@noy-db/hub 0.2.0-pre.15 → 0.2.0-pre.17

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 (301) hide show
  1. package/dist/aggregate/index.cjs +106 -10
  2. package/dist/aggregate/index.cjs.map +1 -1
  3. package/dist/aggregate/index.d.cts +3 -2
  4. package/dist/aggregate/index.d.ts +3 -2
  5. package/dist/aggregate/index.js +4 -4
  6. package/dist/attestation/index.cjs.map +1 -1
  7. package/dist/attestation/index.d.cts +5 -3
  8. package/dist/attestation/index.d.ts +5 -3
  9. package/dist/attestation/index.js +6 -6
  10. package/dist/blobs/index.cjs +226 -11
  11. package/dist/blobs/index.cjs.map +1 -1
  12. package/dist/blobs/index.d.cts +6 -4
  13. package/dist/blobs/index.d.ts +6 -4
  14. package/dist/blobs/index.js +6 -5
  15. package/dist/blobs/index.js.map +1 -1
  16. package/dist/bundle/index.cjs +1268 -141
  17. package/dist/bundle/index.cjs.map +1 -1
  18. package/dist/bundle/index.d.cts +7 -5
  19. package/dist/bundle/index.d.ts +7 -5
  20. package/dist/bundle/index.js +21 -10
  21. package/dist/bundle/index.js.map +1 -1
  22. package/dist/{chunk-5LQG6ZO2.js → chunk-2FU2FTXD.js} +9 -4
  23. package/dist/chunk-2FU2FTXD.js.map +1 -0
  24. package/dist/{chunk-JD3OZAI4.js → chunk-3G3W65EQ.js} +2 -2
  25. package/dist/{chunk-XWH4MXIU.js → chunk-5AXTH4QZ.js} +2 -2
  26. package/dist/{chunk-4TBBMHVC.js → chunk-5LIROIDM.js} +2 -2
  27. package/dist/{chunk-3EWXMOK3.js → chunk-7H2GEJ3O.js} +28 -13
  28. package/dist/chunk-7H2GEJ3O.js.map +1 -0
  29. package/dist/{chunk-WGHU7BLI.js → chunk-AEIKD3PP.js} +52 -38
  30. package/dist/chunk-AEIKD3PP.js.map +1 -0
  31. package/dist/{chunk-KI6HAJWL.js → chunk-BH3X5L6A.js} +4 -4
  32. package/dist/{chunk-BQ65SS5A.js → chunk-BJSLBUJ7.js} +2 -2
  33. package/dist/{chunk-L2FE64BU.js → chunk-BL5GYANC.js} +3 -3
  34. package/dist/{chunk-A5ZOOZFB.js → chunk-BSZOCSDZ.js} +4 -4
  35. package/dist/{chunk-ZNQYHJXX.js → chunk-C3HYQPV4.js} +2 -2
  36. package/dist/{chunk-PE4AQGFH.js → chunk-CD2AVTEM.js} +5 -5
  37. package/dist/{chunk-7EFFHEN5.js → chunk-D77ZQSQQ.js} +852 -143
  38. package/dist/chunk-D77ZQSQQ.js.map +1 -0
  39. package/dist/{chunk-56DJ7JVK.js → chunk-DWEBTE2W.js} +5 -5
  40. package/dist/{chunk-Z4DO7YSI.js → chunk-DYYYUW5D.js} +2 -2
  41. package/dist/{chunk-NSCVNK5K.js → chunk-E77UKJYL.js} +5 -5
  42. package/dist/{chunk-KIP6JLTF.js → chunk-F4G63NTZ.js} +2 -2
  43. package/dist/{chunk-NU6Q3FOR.js → chunk-FEJDVE3Z.js} +12 -2
  44. package/dist/{chunk-NU6Q3FOR.js.map → chunk-FEJDVE3Z.js.map} +1 -1
  45. package/dist/{chunk-DQU36Q7I.js → chunk-GP3SDSH2.js} +14 -5
  46. package/dist/chunk-GP3SDSH2.js.map +1 -0
  47. package/dist/{chunk-IQLVUT37.js → chunk-H2MRGONI.js} +2 -2
  48. package/dist/{chunk-EYVQHAGH.js → chunk-HGVSHKZW.js} +8 -5
  49. package/dist/chunk-HGVSHKZW.js.map +1 -0
  50. package/dist/chunk-I5IUYN7B.js +125 -0
  51. package/dist/chunk-I5IUYN7B.js.map +1 -0
  52. package/dist/{chunk-CJORTUJ2.js → chunk-J7RWBXFY.js} +2 -2
  53. package/dist/{chunk-AAVWKNZW.js → chunk-JDWE6JMX.js} +2 -2
  54. package/dist/{chunk-6AJBSQU4.js → chunk-KCEHMDZF.js} +3 -3
  55. package/dist/{chunk-TS26M2SB.js → chunk-M476FOQ7.js} +2 -2
  56. package/dist/{chunk-F4OJZIWQ.js → chunk-NBBMMJ2H.js} +4 -4
  57. package/dist/{chunk-CZI2A4MQ.js → chunk-NYSYPFXJ.js} +3 -3
  58. package/dist/{chunk-EGD5DXFT.js → chunk-PDULVIBY.js} +14 -2
  59. package/dist/chunk-PDULVIBY.js.map +1 -0
  60. package/dist/{chunk-Z6FNBOTC.js → chunk-PDVP3C2I.js} +1 -1
  61. package/dist/{chunk-Z6FNBOTC.js.map → chunk-PDVP3C2I.js.map} +1 -1
  62. package/dist/{chunk-COFPAMX6.js → chunk-QHM6XEAH.js} +6 -6
  63. package/dist/{chunk-C5T5AFWN.js → chunk-QO6RGLLD.js} +12 -6
  64. package/dist/chunk-QO6RGLLD.js.map +1 -0
  65. package/dist/{chunk-7HT2MEZ5.js → chunk-ROPJVUG3.js} +23 -6
  66. package/dist/chunk-ROPJVUG3.js.map +1 -0
  67. package/dist/{chunk-VU7SWWT5.js → chunk-ROVO6NPJ.js} +11 -7
  68. package/dist/chunk-ROVO6NPJ.js.map +1 -0
  69. package/dist/{chunk-6RR3MNMG.js → chunk-SHX5QBCI.js} +3 -3
  70. package/dist/{chunk-GC4V7RU7.js → chunk-SISBMAPO.js} +1 -1
  71. package/dist/chunk-SISBMAPO.js.map +1 -0
  72. package/dist/{chunk-BIYRQQV6.js → chunk-SNMJ7SB3.js} +5 -5
  73. package/dist/{chunk-7PS7EOCF.js → chunk-TIDXB5DF.js} +4 -4
  74. package/dist/chunk-U5QCMH3W.js +151 -0
  75. package/dist/chunk-U5QCMH3W.js.map +1 -0
  76. package/dist/{chunk-YULZKK4F.js → chunk-UNTGHX5A.js} +37 -2
  77. package/dist/chunk-UNTGHX5A.js.map +1 -0
  78. package/dist/{chunk-FWPKCXTN.js → chunk-WIAOUFFB.js} +2 -2
  79. package/dist/{chunk-LX3CB26H.js → chunk-WV7WV6JO.js} +195 -17
  80. package/dist/chunk-WV7WV6JO.js.map +1 -0
  81. package/dist/{chunk-X73VS74Y.js → chunk-XJV6OB4D.js} +2 -2
  82. package/dist/{chunk-OHVFWCJP.js → chunk-XMHUK5PN.js} +49 -19
  83. package/dist/chunk-XMHUK5PN.js.map +1 -0
  84. package/dist/{chunk-WBAYSNUQ.js → chunk-XMVHEWF6.js} +4 -4
  85. package/dist/{chunk-HOR4R722.js → chunk-XPIHJ34I.js} +30 -4
  86. package/dist/chunk-XPIHJ34I.js.map +1 -0
  87. package/dist/{chunk-DKO2QFSA.js → chunk-YYVZYTWW.js} +3 -3
  88. package/dist/{chunk-535SSHBS.js → chunk-ZEGSDPB7.js} +81 -2
  89. package/dist/chunk-ZEGSDPB7.js.map +1 -0
  90. package/dist/{chunk-YHPM5D7Y.js → chunk-ZNGPEV5J.js} +63 -4
  91. package/dist/chunk-ZNGPEV5J.js.map +1 -0
  92. package/dist/consent/index.cjs.map +1 -1
  93. package/dist/consent/index.d.cts +6 -4
  94. package/dist/consent/index.d.ts +6 -4
  95. package/dist/consent/index.js +3 -3
  96. package/dist/{crypto-QXQOHMHF.js → crypto-7BN2HDWG.js} +7 -3
  97. package/dist/{delegation-NIQ43IPU.js → delegation-MGH5SODX.js} +5 -5
  98. package/dist/derivations/index.cjs +24 -3
  99. package/dist/derivations/index.cjs.map +1 -1
  100. package/dist/derivations/index.d.cts +7 -5
  101. package/dist/derivations/index.d.ts +7 -5
  102. package/dist/derivations/index.js +4 -4
  103. package/dist/{dev-unlock-iAS8z9jc.d.ts → dev-unlock-CI1ijTML.d.ts} +1 -1
  104. package/dist/{dev-unlock-nVkuRLLe.d.cts → dev-unlock-iXbYFAWl.d.cts} +1 -1
  105. package/dist/{strategy-CbneC7bS.d.ts → errors-Dz64FA65.d.cts} +98 -727
  106. package/dist/{strategy-CbneC7bS.d.cts → errors-Dz64FA65.d.ts} +98 -727
  107. package/dist/executor-3W63Y44O.js +11 -0
  108. package/dist/executor-CFFWPWBJ.js +8 -0
  109. package/dist/executor-VDQQOR4F.js +8 -0
  110. package/dist/{fanout-sidecar-N6OJX6QR.js → fanout-sidecar-FIJJ46YG.js} +2 -2
  111. package/dist/forget/index.cjs +43 -0
  112. package/dist/forget/index.cjs.map +1 -0
  113. package/dist/forget/index.d.cts +1 -0
  114. package/dist/forget/index.d.ts +1 -0
  115. package/dist/forget/index.js +14 -0
  116. package/dist/guards/index.cjs +9 -5
  117. package/dist/guards/index.cjs.map +1 -1
  118. package/dist/guards/index.d.cts +7 -5
  119. package/dist/guards/index.d.ts +7 -5
  120. package/dist/guards/index.js +6 -6
  121. package/dist/{hash-DHOnRarj.d.ts → hash-blk7Bkes.d.ts} +1 -1
  122. package/dist/{hash-Cv6byZs7.d.cts → hash-tEcM5fnv.d.cts} +1 -1
  123. package/dist/history/index.cjs +27 -4
  124. package/dist/history/index.cjs.map +1 -1
  125. package/dist/history/index.d.cts +7 -5
  126. package/dist/history/index.d.ts +7 -5
  127. package/dist/history/index.js +9 -7
  128. package/dist/history/index.js.map +1 -1
  129. package/dist/i18n/index.cjs +53 -0
  130. package/dist/i18n/index.cjs.map +1 -1
  131. package/dist/i18n/index.d.cts +6 -4
  132. package/dist/i18n/index.d.ts +6 -4
  133. package/dist/i18n/index.js +16 -8
  134. package/dist/i18n/index.js.map +1 -1
  135. package/dist/{immutable-guard-BehB1YGB.d.ts → immutable-guard-B5M95nbq.d.ts} +16 -1
  136. package/dist/{immutable-guard-yBEOYmif.d.cts → immutable-guard-qN3zF8o1.d.cts} +16 -1
  137. package/dist/index-C-SSRIxP.d.cts +348 -0
  138. package/dist/index-C-SSRIxP.d.ts +348 -0
  139. package/dist/{index-XNB2r6bX.d.ts → index-DpU6KWof.d.ts} +9 -1
  140. package/dist/{index-D95VK1Qy.d.cts → index-u-kWzSrL.d.cts} +9 -1
  141. package/dist/index.cjs +2715 -1302
  142. package/dist/index.cjs.map +1 -1
  143. package/dist/index.d.cts +16 -12
  144. package/dist/index.d.ts +16 -12
  145. package/dist/index.js +132 -106
  146. package/dist/index.js.map +1 -1
  147. package/dist/indexing/index.cjs.map +1 -1
  148. package/dist/indexing/index.js +4 -4
  149. package/dist/issue-TTMGHQ2J.js +12 -0
  150. package/dist/{ledger-CWSE3BLF.js → ledger-LFVLHE5H.js} +6 -6
  151. package/dist/materialized-views/index.cjs +407 -5
  152. package/dist/materialized-views/index.cjs.map +1 -1
  153. package/dist/materialized-views/index.d.cts +7 -5
  154. package/dist/materialized-views/index.d.ts +7 -5
  155. package/dist/materialized-views/index.js +12 -12
  156. package/dist/noydb-36S6GQNC.js +37 -0
  157. package/dist/overlay-views/index.cjs +47 -17
  158. package/dist/overlay-views/index.cjs.map +1 -1
  159. package/dist/overlay-views/index.d.cts +28 -8
  160. package/dist/overlay-views/index.d.ts +28 -8
  161. package/dist/overlay-views/index.js +4 -4
  162. package/dist/periods/index.cjs.map +1 -1
  163. package/dist/periods/index.d.cts +6 -4
  164. package/dist/periods/index.d.ts +6 -4
  165. package/dist/periods/index.js +6 -6
  166. package/dist/{public-envelope-SYHEYQ3X.js → public-envelope-RXZNP3V6.js} +4 -4
  167. package/dist/query/index.cjs +28 -11
  168. package/dist/query/index.cjs.map +1 -1
  169. package/dist/query/index.d.cts +3 -2
  170. package/dist/query/index.d.ts +3 -2
  171. package/dist/query/index.js +6 -6
  172. package/dist/registry-3YFLZ7WD.js +8 -0
  173. package/dist/{registry-DK5YWAAA.js → registry-SECUWSGY.js} +3 -3
  174. package/dist/registry-TGZISEWC.js +8 -0
  175. package/dist/{revoke-ZDFKMR5E.js → revoke-B54H2S2W.js} +6 -6
  176. package/dist/sealed-record/index.cjs +139 -0
  177. package/dist/sealed-record/index.cjs.map +1 -0
  178. package/dist/sealed-record/index.d.cts +123 -0
  179. package/dist/sealed-record/index.d.ts +123 -0
  180. package/dist/sealed-record/index.js +42 -0
  181. package/dist/sealed-record/index.js.map +1 -0
  182. package/dist/session/index.cjs.map +1 -1
  183. package/dist/session/index.d.cts +7 -5
  184. package/dist/session/index.d.ts +7 -5
  185. package/dist/session/index.js +3 -3
  186. package/dist/shadow/index.cjs.map +1 -1
  187. package/dist/shadow/index.d.cts +6 -4
  188. package/dist/shadow/index.d.ts +6 -4
  189. package/dist/shadow/index.js +2 -2
  190. package/dist/{signer-P5D7Y72U.js → signer-YSXZT574.js} +5 -5
  191. package/dist/snapshots/index.cjs.map +1 -1
  192. package/dist/snapshots/index.d.cts +6 -4
  193. package/dist/snapshots/index.d.ts +6 -4
  194. package/dist/snapshots/index.js +4 -4
  195. package/dist/{stale-JH67FU57.js → stale-TOA36SRK.js} +2 -2
  196. package/dist/stale-TOA36SRK.js.map +1 -0
  197. package/dist/{state-vault-TMXZRTY5.js → state-vault-W2OEABNO.js} +3 -3
  198. package/dist/store/index.cjs.map +1 -1
  199. package/dist/store/index.d.cts +6 -4
  200. package/dist/store/index.d.ts +6 -4
  201. package/dist/store/index.js +2 -2
  202. package/dist/strategy-4M9jo172.d.ts +739 -0
  203. package/dist/strategy-CLC1j79g.d.cts +739 -0
  204. package/dist/sync/index.cjs.map +1 -1
  205. package/dist/sync/index.d.cts +5 -3
  206. package/dist/sync/index.d.ts +5 -3
  207. package/dist/sync/index.js +4 -4
  208. package/dist/team/index.cjs.map +1 -1
  209. package/dist/team/index.d.cts +6 -4
  210. package/dist/team/index.d.ts +6 -4
  211. package/dist/team/index.js +8 -8
  212. package/dist/tx/index.cjs +66 -3
  213. package/dist/tx/index.cjs.map +1 -1
  214. package/dist/tx/index.d.cts +24 -6
  215. package/dist/tx/index.d.ts +24 -6
  216. package/dist/tx/index.js +9 -5
  217. package/dist/tx/index.js.map +1 -1
  218. package/dist/{types-4t1-tWS4.d.ts → types-CljIHm_J.d.ts} +1127 -606
  219. package/dist/{types-BpPV5uyy.d.cts → types-CrSpRDuG.d.cts} +1127 -606
  220. package/dist/{ulid-DAfenvFd.d.ts → ulid-CWfL2Vfv.d.ts} +1 -1
  221. package/dist/{ulid-CiPrpGqm.d.cts → ulid-CrI7PPbA.d.cts} +1 -1
  222. package/dist/util/index.cjs.map +1 -1
  223. package/dist/util/index.js +1 -1
  224. package/dist/{vault-group-KOM7QRJG.js → vault-group-DHAHFX2A.js} +4 -4
  225. package/dist/{with-derivation-OK9M2sJE.d.ts → with-derivation-BZ2y4bzF.d.ts} +1 -1
  226. package/dist/{with-derivation-DBqJB3dQ.d.cts → with-derivation-Bozs8DmD.d.cts} +1 -1
  227. package/dist/{with-materialized-view-Dt-ufPWQ.d.ts → with-materialized-view-B892zYZV.d.ts} +1 -1
  228. package/dist/{with-materialized-view-NzuxYPDF.d.cts → with-materialized-view-NzF71cG_.d.cts} +1 -1
  229. package/dist/{with-overlayed-view-eDvMs6LO.d.ts → with-overlayed-view-CR6m7CHe.d.ts} +1 -1
  230. package/dist/{with-overlayed-view-CC0_ocy-.d.cts → with-overlayed-view-UI8qSGL4.d.cts} +1 -1
  231. package/package.json +23 -3
  232. package/dist/chunk-3EWXMOK3.js.map +0 -1
  233. package/dist/chunk-535SSHBS.js.map +0 -1
  234. package/dist/chunk-5LQG6ZO2.js.map +0 -1
  235. package/dist/chunk-7EFFHEN5.js.map +0 -1
  236. package/dist/chunk-7HT2MEZ5.js.map +0 -1
  237. package/dist/chunk-C5T5AFWN.js.map +0 -1
  238. package/dist/chunk-DQU36Q7I.js.map +0 -1
  239. package/dist/chunk-EGD5DXFT.js.map +0 -1
  240. package/dist/chunk-EYVQHAGH.js.map +0 -1
  241. package/dist/chunk-GC4V7RU7.js.map +0 -1
  242. package/dist/chunk-HOR4R722.js.map +0 -1
  243. package/dist/chunk-LX3CB26H.js.map +0 -1
  244. package/dist/chunk-OHVFWCJP.js.map +0 -1
  245. package/dist/chunk-VU7SWWT5.js.map +0 -1
  246. package/dist/chunk-WGHU7BLI.js.map +0 -1
  247. package/dist/chunk-YHPM5D7Y.js.map +0 -1
  248. package/dist/chunk-YULZKK4F.js.map +0 -1
  249. package/dist/executor-6ZDSDZ6V.js +0 -8
  250. package/dist/executor-HSSRXDOB.js +0 -11
  251. package/dist/executor-IDZDAFNH.js +0 -8
  252. package/dist/issue-ADVS4OVP.js +0 -12
  253. package/dist/noydb-GZGFBA4E.js +0 -35
  254. package/dist/registry-IUZQVVBB.js +0 -8
  255. package/dist/registry-XGLNADIE.js +0 -8
  256. /package/dist/{chunk-JD3OZAI4.js.map → chunk-3G3W65EQ.js.map} +0 -0
  257. /package/dist/{chunk-XWH4MXIU.js.map → chunk-5AXTH4QZ.js.map} +0 -0
  258. /package/dist/{chunk-4TBBMHVC.js.map → chunk-5LIROIDM.js.map} +0 -0
  259. /package/dist/{chunk-KI6HAJWL.js.map → chunk-BH3X5L6A.js.map} +0 -0
  260. /package/dist/{chunk-BQ65SS5A.js.map → chunk-BJSLBUJ7.js.map} +0 -0
  261. /package/dist/{chunk-L2FE64BU.js.map → chunk-BL5GYANC.js.map} +0 -0
  262. /package/dist/{chunk-A5ZOOZFB.js.map → chunk-BSZOCSDZ.js.map} +0 -0
  263. /package/dist/{chunk-ZNQYHJXX.js.map → chunk-C3HYQPV4.js.map} +0 -0
  264. /package/dist/{chunk-PE4AQGFH.js.map → chunk-CD2AVTEM.js.map} +0 -0
  265. /package/dist/{chunk-56DJ7JVK.js.map → chunk-DWEBTE2W.js.map} +0 -0
  266. /package/dist/{chunk-Z4DO7YSI.js.map → chunk-DYYYUW5D.js.map} +0 -0
  267. /package/dist/{chunk-NSCVNK5K.js.map → chunk-E77UKJYL.js.map} +0 -0
  268. /package/dist/{chunk-KIP6JLTF.js.map → chunk-F4G63NTZ.js.map} +0 -0
  269. /package/dist/{chunk-IQLVUT37.js.map → chunk-H2MRGONI.js.map} +0 -0
  270. /package/dist/{chunk-CJORTUJ2.js.map → chunk-J7RWBXFY.js.map} +0 -0
  271. /package/dist/{chunk-AAVWKNZW.js.map → chunk-JDWE6JMX.js.map} +0 -0
  272. /package/dist/{chunk-6AJBSQU4.js.map → chunk-KCEHMDZF.js.map} +0 -0
  273. /package/dist/{chunk-TS26M2SB.js.map → chunk-M476FOQ7.js.map} +0 -0
  274. /package/dist/{chunk-F4OJZIWQ.js.map → chunk-NBBMMJ2H.js.map} +0 -0
  275. /package/dist/{chunk-CZI2A4MQ.js.map → chunk-NYSYPFXJ.js.map} +0 -0
  276. /package/dist/{chunk-COFPAMX6.js.map → chunk-QHM6XEAH.js.map} +0 -0
  277. /package/dist/{chunk-6RR3MNMG.js.map → chunk-SHX5QBCI.js.map} +0 -0
  278. /package/dist/{chunk-BIYRQQV6.js.map → chunk-SNMJ7SB3.js.map} +0 -0
  279. /package/dist/{chunk-7PS7EOCF.js.map → chunk-TIDXB5DF.js.map} +0 -0
  280. /package/dist/{chunk-FWPKCXTN.js.map → chunk-WIAOUFFB.js.map} +0 -0
  281. /package/dist/{chunk-X73VS74Y.js.map → chunk-XJV6OB4D.js.map} +0 -0
  282. /package/dist/{chunk-WBAYSNUQ.js.map → chunk-XMVHEWF6.js.map} +0 -0
  283. /package/dist/{chunk-DKO2QFSA.js.map → chunk-YYVZYTWW.js.map} +0 -0
  284. /package/dist/{crypto-QXQOHMHF.js.map → crypto-7BN2HDWG.js.map} +0 -0
  285. /package/dist/{delegation-NIQ43IPU.js.map → delegation-MGH5SODX.js.map} +0 -0
  286. /package/dist/{executor-6ZDSDZ6V.js.map → executor-3W63Y44O.js.map} +0 -0
  287. /package/dist/{executor-HSSRXDOB.js.map → executor-CFFWPWBJ.js.map} +0 -0
  288. /package/dist/{executor-IDZDAFNH.js.map → executor-VDQQOR4F.js.map} +0 -0
  289. /package/dist/{fanout-sidecar-N6OJX6QR.js.map → fanout-sidecar-FIJJ46YG.js.map} +0 -0
  290. /package/dist/{issue-ADVS4OVP.js.map → forget/index.js.map} +0 -0
  291. /package/dist/{ledger-CWSE3BLF.js.map → issue-TTMGHQ2J.js.map} +0 -0
  292. /package/dist/{noydb-GZGFBA4E.js.map → ledger-LFVLHE5H.js.map} +0 -0
  293. /package/dist/{public-envelope-SYHEYQ3X.js.map → noydb-36S6GQNC.js.map} +0 -0
  294. /package/dist/{registry-DK5YWAAA.js.map → public-envelope-RXZNP3V6.js.map} +0 -0
  295. /package/dist/{registry-IUZQVVBB.js.map → registry-3YFLZ7WD.js.map} +0 -0
  296. /package/dist/{registry-XGLNADIE.js.map → registry-SECUWSGY.js.map} +0 -0
  297. /package/dist/{revoke-ZDFKMR5E.js.map → registry-TGZISEWC.js.map} +0 -0
  298. /package/dist/{signer-P5D7Y72U.js.map → revoke-B54H2S2W.js.map} +0 -0
  299. /package/dist/{stale-JH67FU57.js.map → signer-YSXZT574.js.map} +0 -0
  300. /package/dist/{state-vault-TMXZRTY5.js.map → state-vault-W2OEABNO.js.map} +0 -0
  301. /package/dist/{vault-group-KOM7QRJG.js.map → vault-group-DHAHFX2A.js.map} +0 -0
@@ -7,37 +7,68 @@ import {
7
7
  import {
8
8
  TxContext,
9
9
  revertExecuted
10
- } from "./chunk-YHPM5D7Y.js";
10
+ } from "./chunk-ZNGPEV5J.js";
11
11
  import {
12
12
  OverlayedCollection
13
- } from "./chunk-OHVFWCJP.js";
13
+ } from "./chunk-XMHUK5PN.js";
14
+ import {
15
+ NO_AGGREGATE,
16
+ Query,
17
+ ScanBuilder,
18
+ canonicalizeIncomingMoney,
19
+ canonicalizeStoredMoney,
20
+ decodeMoneyFields,
21
+ quantizeMoneyFields,
22
+ validateMoneyFieldPaths
23
+ } from "./chunk-HGVSHKZW.js";
24
+ import {
25
+ EXPORT_AUDIT_COLLECTION,
26
+ createExportBlobsHandle,
27
+ runCompaction
28
+ } from "./chunk-KCEHMDZF.js";
14
29
  import {
15
30
  LazyQuery,
16
31
  decodeIdxId,
17
32
  encodeIdxId
18
- } from "./chunk-CZI2A4MQ.js";
33
+ } from "./chunk-NYSYPFXJ.js";
34
+ import {
35
+ canonicalGroupKey
36
+ } from "./chunk-7H2GEJ3O.js";
37
+ import {
38
+ readPath
39
+ } from "./chunk-J7RWBXFY.js";
19
40
  import {
20
41
  SCHEMAS_COLLECTION,
21
42
  loadPersistedSchema,
22
43
  resolveManagedSecret,
23
44
  savePersistedSchema,
24
45
  saveSealedPassphrase
25
- } from "./chunk-WGHU7BLI.js";
46
+ } from "./chunk-AEIKD3PP.js";
26
47
  import {
27
48
  loadPublicEnvelope,
28
49
  readPublicEnvelope,
29
50
  savePublicEnvelope,
30
51
  validatePublicEnvelopeInput
31
- } from "./chunk-DKO2QFSA.js";
52
+ } from "./chunk-YYVZYTWW.js";
53
+ import {
54
+ buildTombstone,
55
+ isTombstone,
56
+ resolveStableCek,
57
+ revokeSealedRecord,
58
+ rewrapBodyToDek,
59
+ rotateRecordCek,
60
+ sealRecordToHost
61
+ } from "./chunk-U5QCMH3W.js";
32
62
  import {
33
63
  PERIODS_COLLECTION
34
- } from "./chunk-KI6HAJWL.js";
64
+ } from "./chunk-BH3X5L6A.js";
35
65
  import {
36
66
  getAtPath,
37
67
  isDictCollectionName,
68
+ isStaticDictDescriptor,
38
69
  resolvePolicy,
39
70
  setAtPathInPlace
40
- } from "./chunk-7HT2MEZ5.js";
71
+ } from "./chunk-ROPJVUG3.js";
41
72
  import {
42
73
  ManagedRecoveryNotEnrolledError,
43
74
  PolicyDeniedError,
@@ -59,11 +90,11 @@ import {
59
90
  saveShamirRecoveryEntries,
60
91
  updateAuthenticator,
61
92
  writeMagicLinkGrant
62
- } from "./chunk-COFPAMX6.js";
93
+ } from "./chunk-QHM6XEAH.js";
63
94
  import {
64
95
  assertTierAccess,
65
96
  dekKey
66
- } from "./chunk-4TBBMHVC.js";
97
+ } from "./chunk-5LIROIDM.js";
67
98
  import {
68
99
  USER_ENVELOPE_COLLECTION,
69
100
  assertKeyringOpenAllowed,
@@ -88,7 +119,7 @@ import {
88
119
  rotateKeys,
89
120
  saveUserEnvelope,
90
121
  updateKeyringIdentity
91
- } from "./chunk-A5ZOOZFB.js";
122
+ } from "./chunk-BSZOCSDZ.js";
92
123
  import {
93
124
  INDEXED_STORE_POLICY
94
125
  } from "./chunk-2QR2PQTT.js";
@@ -98,41 +129,31 @@ import {
98
129
  import {
99
130
  LEDGER_COLLECTION,
100
131
  LEDGER_DELTAS_COLLECTION
101
- } from "./chunk-56DJ7JVK.js";
132
+ } from "./chunk-DWEBTE2W.js";
102
133
  import {
103
134
  sha256Hex as sha256Hex2
104
- } from "./chunk-Z6FNBOTC.js";
105
- import {
106
- NO_AGGREGATE,
107
- Query,
108
- ScanBuilder,
109
- canonicalizeIncomingMoney,
110
- canonicalizeStoredMoney,
111
- decodeMoneyFields,
112
- quantizeMoneyFields,
113
- validateMoneyFieldPaths
114
- } from "./chunk-EYVQHAGH.js";
115
- import {
116
- canonicalGroupKey
117
- } from "./chunk-3EWXMOK3.js";
118
- import {
119
- readPath
120
- } from "./chunk-CJORTUJ2.js";
135
+ } from "./chunk-PDVP3C2I.js";
121
136
  import {
122
- EXPORT_AUDIT_COLLECTION,
123
- createExportBlobsHandle,
124
- runCompaction
125
- } from "./chunk-6AJBSQU4.js";
137
+ NO_FORGET,
138
+ addSubjectRef,
139
+ coerceSubjectId,
140
+ lookupSubject,
141
+ readDottedPath,
142
+ rebuildSubjectIndex,
143
+ removeSubjectRef
144
+ } from "./chunk-I5IUYN7B.js";
126
145
  import {
127
146
  NOYDB_BACKUP_VERSION,
128
147
  NOYDB_FORMAT_VERSION
129
- } from "./chunk-GC4V7RU7.js";
148
+ } from "./chunk-SISBMAPO.js";
130
149
  import {
131
150
  decrypt,
132
151
  encrypt,
133
152
  encryptDeterministic,
134
- sha256Hex
135
- } from "./chunk-YULZKK4F.js";
153
+ sha256Hex,
154
+ unwrapCek,
155
+ wrapCek
156
+ } from "./chunk-UNTGHX5A.js";
136
157
  import {
137
158
  AlreadyElevatedError,
138
159
  AttestationError,
@@ -141,6 +162,7 @@ import {
141
162
  ConflictError,
142
163
  ElevationExpiredError,
143
164
  ExportCapabilityError,
165
+ ForgetStrategyNotConfiguredError,
144
166
  ImportCapabilityError,
145
167
  IndexWriteFailureError,
146
168
  InvalidKeyError,
@@ -159,15 +181,17 @@ import {
159
181
  SchemaValidationError,
160
182
  SequenceContentionError,
161
183
  SequenceOfflineError,
184
+ StaticDictReadonlyError,
162
185
  StoreCapabilityError,
163
186
  TierDemoteDeniedError,
164
187
  TierNotGrantedError,
165
188
  TranslatorNotConfiguredError,
166
189
  UniqueConstraintError,
190
+ UnknownDictCodeError,
167
191
  UnsupportedIndexOptionError,
168
192
  ValidationError,
169
193
  VaultTemplateNotFoundError
170
- } from "./chunk-535SSHBS.js";
194
+ } from "./chunk-ZEGSDPB7.js";
171
195
 
172
196
  // src/policy/storage.ts
173
197
  var META_COLLECTION = "_meta";
@@ -450,6 +474,9 @@ var NO_HISTORY = {
450
474
  async clearHistory() {
451
475
  return 0;
452
476
  },
477
+ async tombstoneHistory() {
478
+ return 0;
479
+ },
453
480
  async envelopePayloadHash() {
454
481
  return "";
455
482
  },
@@ -810,7 +837,7 @@ async function resolveStaleOnRead(accessor, outputCollection, id) {
810
837
  }
811
838
  const sourceWithId = { ...source, id };
812
839
  if (DerivationExecutor === null) {
813
- ({ DerivationExecutor } = await import("./executor-6ZDSDZ6V.js"));
840
+ ({ DerivationExecutor } = await import("./executor-CFFWPWBJ.js"));
814
841
  }
815
842
  const ctx = { vault: accessor.getReadOnlyFacade() };
816
843
  const result = await DerivationExecutor.run(spec, sourceWithId, 0, strategyHash, ctx);
@@ -1035,6 +1062,25 @@ var Collection = class {
1035
1062
  * is inactive for this collection; a frozen `Set` otherwise.
1036
1063
  */
1037
1064
  deterministicFields;
1065
+ /**
1066
+ * Per-record CEK opt-in (`perRecordKeys: true`). When set, writes mint /
1067
+ * reuse a per-record content-encryption key and stamp `_cek` on the
1068
+ * envelope (see {@link EncryptedEnvelope._cek}). OFF by default — a
1069
+ * non-adopting collection takes the byte-identical legacy path. The READ
1070
+ * path does not consult this flag: `_cek` presence on the envelope is the
1071
+ * format discriminant, so a mixed vault (and a recipient that never set the
1072
+ * flag) still decrypts CEK records.
1073
+ */
1074
+ perRecordCek;
1075
+ /**
1076
+ * Session-scoped `(id) → CEK` cache for this collection. Lets updates
1077
+ * reuse a record's stable CEK and lets repeated reads skip the AES-KW
1078
+ * unwrap. Bounded by LRU; never persisted. Dropped when the owning
1079
+ * collection instance is discarded — `vault.load()` clears the
1080
+ * collectionCache, so a keyring refresh drops every CEK alongside the
1081
+ * DEK cache. `null` unless `perRecordCek` is set.
1082
+ */
1083
+ cekCache;
1038
1084
  /**
1039
1085
  * declared tiers for this collection. `null` when
1040
1086
  * tier-aware methods are disabled. Tier 0 is implicit and never
@@ -1181,19 +1227,24 @@ var Collection = class {
1181
1227
  } else {
1182
1228
  this.deterministicFields = null;
1183
1229
  }
1230
+ this.perRecordCek = opts.perRecordKeys === true;
1231
+ this.cekCache = this.perRecordCek ? new Lru({ maxRecords: 4096 }) : null;
1184
1232
  if (opts.crdt && opts.onRegisterConflictResolver) {
1185
1233
  const crdtMode = opts.crdt;
1186
- const crdtResolver = async (_id, local, remote) => {
1234
+ const crdtResolver = async (id, local, remote) => {
1187
1235
  if (crdtMode === "yjs") {
1188
1236
  return local._v >= remote._v ? local : remote;
1189
1237
  }
1190
- const localJson = await this.decryptJsonString(local);
1191
- const remoteJson = await this.decryptJsonString(remote);
1238
+ const localJson = await this.decryptJsonString(local, id);
1239
+ const remoteJson = await this.decryptJsonString(remote, id);
1240
+ if (localJson === null) return local;
1241
+ if (remoteJson === null) return remote;
1192
1242
  const localState = JSON.parse(localJson);
1193
1243
  const remoteState = JSON.parse(remoteJson);
1194
1244
  const merged = this.crdtStrategy.mergeCrdtStates(localState, remoteState);
1195
1245
  const mergedVersion = Math.max(local._v, remote._v) + 1;
1196
- return this.encryptJsonString(JSON.stringify(merged), mergedVersion);
1246
+ const cek = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
1247
+ return this.encryptJsonString(JSON.stringify(merged), mergedVersion, cek);
1197
1248
  };
1198
1249
  opts.onRegisterConflictResolver(this.name, crdtResolver);
1199
1250
  }
@@ -1233,12 +1284,15 @@ var Collection = class {
1233
1284
  });
1234
1285
  } else {
1235
1286
  const mergeFn = policy;
1236
- resolver = async (_id, local, remote) => {
1237
- const localRecord = await this.decryptRecord(local, { skipValidation: true });
1238
- const remoteRecord = await this.decryptRecord(remote, { skipValidation: true });
1287
+ resolver = async (id, local, remote) => {
1288
+ const localRecord = await this.decryptRecord(local, { skipValidation: true, id });
1289
+ const remoteRecord = await this.decryptRecord(remote, { skipValidation: true, id });
1290
+ if (localRecord === null) return local;
1291
+ if (remoteRecord === null) return remote;
1239
1292
  const merged = mergeFn(localRecord, remoteRecord);
1240
1293
  const mergedVersion = Math.max(local._v, remote._v) + 1;
1241
- return this.encryptRecord(merged, mergedVersion);
1294
+ const cek = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
1295
+ return this.encryptRecord(merged, mergedVersion, cek);
1242
1296
  };
1243
1297
  }
1244
1298
  opts.onRegisterConflictResolver(collectionName, resolver);
@@ -1319,7 +1373,7 @@ var Collection = class {
1319
1373
  }
1320
1374
  }
1321
1375
  if (this.materializedViewSource !== void 0) {
1322
- const { resolveStaleMVOnRead } = await import("./stale-JH67FU57.js");
1376
+ const { resolveStaleMVOnRead } = await import("./stale-TOA36SRK.js");
1323
1377
  await resolveStaleMVOnRead(this.materializedViewSource, this.name);
1324
1378
  }
1325
1379
  let record;
@@ -1330,7 +1384,9 @@ var Collection = class {
1330
1384
  } else {
1331
1385
  const envelope = await this.adapter.get(this.vault, this.name, id);
1332
1386
  if (!envelope) return null;
1333
- record = await this.decryptRecord(envelope);
1387
+ if (isTombstone(envelope, this.encrypted)) return null;
1388
+ record = await this.decryptRecord(envelope, { id });
1389
+ if (record === null) return null;
1334
1390
  this.lru.set(id, { record, version: envelope._v }, estimateRecordBytes(record));
1335
1391
  }
1336
1392
  } else {
@@ -1357,6 +1413,7 @@ var Collection = class {
1357
1413
  const envelope = await this.adapter.get(this.vault, this.name, id);
1358
1414
  if (!envelope) return null;
1359
1415
  const json = await this.decryptJsonString(envelope);
1416
+ if (json === null) return null;
1360
1417
  return JSON.parse(json);
1361
1418
  }
1362
1419
  /**
@@ -1445,7 +1502,7 @@ var Collection = class {
1445
1502
  if (cached2) return { record: cached2.record, version: cached2.version };
1446
1503
  const env = await this.adapter.get(this.vault, this.name, id);
1447
1504
  if (!env) return { record: null, version: 0 };
1448
- return { record: await this.decryptRecord(env, { skipValidation: true }), version: env._v };
1505
+ return { record: await this.decryptRecord(env, { skipValidation: true }) ?? null, version: env._v };
1449
1506
  }
1450
1507
  await this.ensureHydrated();
1451
1508
  const cached = this.cache.get(id);
@@ -1558,9 +1615,11 @@ var Collection = class {
1558
1615
  let existingState;
1559
1616
  if (existingEnvelope) {
1560
1617
  const prevJson = await this.decryptJsonString(existingEnvelope);
1561
- const prevParsed = JSON.parse(prevJson);
1562
- if (prevParsed !== null && typeof prevParsed === "object" && "_crdt" in prevParsed) {
1563
- existingState = prevParsed;
1618
+ if (prevJson !== null) {
1619
+ const prevParsed = JSON.parse(prevJson);
1620
+ if (prevParsed !== null && typeof prevParsed === "object" && "_crdt" in prevParsed) {
1621
+ existingState = prevParsed;
1622
+ }
1564
1623
  }
1565
1624
  }
1566
1625
  crdtState = this.crdtStrategy.buildLwwMapState(record, existingState, now);
@@ -1568,9 +1627,11 @@ var Collection = class {
1568
1627
  let existingState;
1569
1628
  if (existingEnvelope) {
1570
1629
  const prevJson = await this.decryptJsonString(existingEnvelope);
1571
- const prevParsed = JSON.parse(prevJson);
1572
- if (prevParsed !== null && typeof prevParsed === "object" && "_crdt" in prevParsed) {
1573
- existingState = prevParsed;
1630
+ if (prevJson !== null) {
1631
+ const prevParsed = JSON.parse(prevJson);
1632
+ if (prevParsed !== null && typeof prevParsed === "object" && "_crdt" in prevParsed) {
1633
+ existingState = prevParsed;
1634
+ }
1574
1635
  }
1575
1636
  }
1576
1637
  const arr = Array.isArray(record) ? record : [record];
@@ -1579,12 +1640,14 @@ var Collection = class {
1579
1640
  crdtState = { _crdt: "yjs", update: record };
1580
1641
  }
1581
1642
  const version2 = existingVersion + 1;
1582
- const envelope2 = await this.encryptJsonString(JSON.stringify(crdtState), version2);
1643
+ const cek2 = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
1644
+ const envelope2 = await this.encryptJsonString(JSON.stringify(crdtState), version2, cek2);
1583
1645
  await this.adapter.put(this.vault, this.name, id, envelope2);
1584
1646
  const resolvedRecord = this.crdtStrategy.resolveCrdtSnapshot(crdtState);
1585
- const existingResolved = existingEnvelope ? { record: await this.decryptRecord(existingEnvelope, { skipValidation: true }), version: existingVersion } : void 0;
1647
+ const existingResolvedRecord = existingEnvelope ? await this.decryptRecord(existingEnvelope, { skipValidation: true }) : null;
1648
+ const existingResolved = existingResolvedRecord !== null ? { record: existingResolvedRecord, version: existingVersion } : void 0;
1586
1649
  if (existingResolved && this.historyConfig.enabled !== false) {
1587
- const histEnvelope = await this.encryptRecord(existingResolved.record, existingResolved.version);
1650
+ const histEnvelope = await this.encryptRecord(existingResolved.record, existingResolved.version, cek2);
1588
1651
  await this.historyStrategy.saveHistory(this.adapter, this.vault, this.name, id, histEnvelope);
1589
1652
  this.emitter.emit("history:save", { vault: this.vault, collection: this.name, id, version: existingResolved.version });
1590
1653
  if (this.historyConfig.maxVersions) {
@@ -1630,7 +1693,9 @@ var Collection = class {
1630
1693
  const previousEnvelope = await this.adapter.get(this.vault, this.name, id);
1631
1694
  if (previousEnvelope) {
1632
1695
  const previousRecord = await this.decryptRecord(previousEnvelope);
1633
- existing = { record: previousRecord, version: previousEnvelope._v };
1696
+ if (previousRecord !== null) {
1697
+ existing = { record: previousRecord, version: previousEnvelope._v };
1698
+ }
1634
1699
  }
1635
1700
  }
1636
1701
  } else {
@@ -1639,8 +1704,9 @@ var Collection = class {
1639
1704
  }
1640
1705
  const version = existing ? existing.version + 1 : 1;
1641
1706
  this.uniqueConstraints?.check(id, record);
1707
+ const cek = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
1642
1708
  if (existing && this.historyConfig.enabled !== false) {
1643
- const historyEnvelope = await this.encryptRecord(existing.record, existing.version);
1709
+ const historyEnvelope = await this.encryptRecord(existing.record, existing.version, cek);
1644
1710
  await this.historyStrategy.saveHistory(this.adapter, this.vault, this.name, id, historyEnvelope);
1645
1711
  this.emitter.emit("history:save", {
1646
1712
  vault: this.vault,
@@ -1654,7 +1720,7 @@ var Collection = class {
1654
1720
  });
1655
1721
  }
1656
1722
  }
1657
- const envelope = await this.encryptRecord(record, version);
1723
+ const envelope = await this.encryptRecord(record, version, cek);
1658
1724
  await this.adapter.put(this.vault, this.name, id, envelope);
1659
1725
  if (this.ledger) {
1660
1726
  const appendInput = {
@@ -1717,7 +1783,7 @@ var Collection = class {
1717
1783
  if (mode === "eager") {
1718
1784
  if (executor === null) {
1719
1785
  ;
1720
- ({ MaterializedViewExecutor: executor } = await import("./executor-HSSRXDOB.js"));
1786
+ ({ MaterializedViewExecutor: executor } = await import("./executor-3W63Y44O.js"));
1721
1787
  }
1722
1788
  await executor.refresh(reg, {
1723
1789
  getCollection: (name) => this.materializedViewSource.getCollection(name),
@@ -1726,7 +1792,7 @@ var Collection = class {
1726
1792
  });
1727
1793
  } else if (mode === "lazy") {
1728
1794
  if (staleHelpers === null) {
1729
- staleHelpers = await import("./stale-JH67FU57.js");
1795
+ staleHelpers = await import("./stale-TOA36SRK.js");
1730
1796
  }
1731
1797
  staleHelpers.markMVStale(registry, reg.spec.name);
1732
1798
  }
@@ -1755,11 +1821,20 @@ var Collection = class {
1755
1821
  const mode = typeof spec.lifecycle === "string" ? spec.lifecycle : spec.lifecycle.mode;
1756
1822
  if (mode === "eager") {
1757
1823
  if (DerivationExecutor === null) {
1758
- ({ DerivationExecutor } = await import("./executor-6ZDSDZ6V.js"));
1824
+ ({ DerivationExecutor } = await import("./executor-CFFWPWBJ.js"));
1825
+ }
1826
+ let sourceWithId;
1827
+ let sourceVersion = version;
1828
+ if (spec.source === this.name) {
1829
+ sourceWithId = { ...incoming, id };
1830
+ } else {
1831
+ const primary = await this.derivationSource.getCollection(spec.source).get(id);
1832
+ if (primary === null || primary === void 0) continue;
1833
+ sourceWithId = { ...primary, id };
1834
+ sourceVersion = 0;
1759
1835
  }
1760
- const sourceWithId = { ...incoming, id };
1761
1836
  const ctx = { vault: this.derivationSource.getReadOnlyFacade() };
1762
- const result = await DerivationExecutor.run(spec, sourceWithId, version, strategyHash, ctx);
1837
+ const result = await DerivationExecutor.run(spec, sourceWithId, sourceVersion, strategyHash, ctx);
1763
1838
  for (const key of Object.keys(spec.outputs)) {
1764
1839
  const out = result.outputs[key];
1765
1840
  if (!out) continue;
@@ -1774,7 +1849,7 @@ var Collection = class {
1774
1849
  const outputCollection = this.derivationSource.getCollection(outSpec.collection);
1775
1850
  const txCtx = this.derivationSource.getActiveTxContext();
1776
1851
  if (out.kind === "array") {
1777
- const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-N6OJX6QR.js");
1852
+ const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-FIJJ46YG.js");
1778
1853
  const prior = await loadFanoutSidecar(
1779
1854
  this.adapter,
1780
1855
  this.vault,
@@ -1881,11 +1956,14 @@ var Collection = class {
1881
1956
  let count = 0;
1882
1957
  for (const id of ids) {
1883
1958
  const env = await this.adapter.get(this.vault, this.name, id);
1884
- if (!env) continue;
1885
- const record = await this.decryptRecord(env, { skipValidation: true });
1959
+ if (!env || isTombstone(env, this.encrypted)) continue;
1960
+ const decoded = await this.decryptRecord(env, { skipValidation: true, id });
1961
+ if (decoded === null) continue;
1962
+ const record = decoded;
1886
1963
  const next = transform(record);
1887
1964
  const nextVersion = (env._v ?? 0) + 1;
1888
- const newEnv = await this.encryptRecord(next, nextVersion);
1965
+ const cek = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
1966
+ const newEnv = await this.encryptRecord(next, nextVersion, cek);
1889
1967
  await this.adapter.put(this.vault, this.name, id, newEnv);
1890
1968
  await this._invalidateCacheEntry(id);
1891
1969
  if (this.ledger) {
@@ -1997,14 +2075,17 @@ var Collection = class {
1997
2075
  const previousEnvelope2 = await this.adapter.get(this.vault, this.name, id);
1998
2076
  if (previousEnvelope2) {
1999
2077
  const previousRecord = await this.decryptRecord(previousEnvelope2);
2000
- existing = { record: previousRecord, version: previousEnvelope2._v };
2078
+ if (previousRecord !== null) {
2079
+ existing = { record: previousRecord, version: previousEnvelope2._v };
2080
+ }
2001
2081
  }
2002
2082
  }
2003
2083
  } else {
2004
2084
  existing = this.cache.get(id);
2005
2085
  }
2006
2086
  if (existing && this.historyConfig.enabled !== false) {
2007
- const historyEnvelope = await this.encryptRecord(existing.record, existing.version);
2087
+ const cek = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
2088
+ const historyEnvelope = await this.encryptRecord(existing.record, existing.version, cek);
2008
2089
  await this.historyStrategy.saveHistory(this.adapter, this.vault, this.name, id, historyEnvelope);
2009
2090
  }
2010
2091
  const previousEnvelope = await this.adapter.get(this.vault, this.name, id);
@@ -2045,6 +2126,50 @@ var Collection = class {
2045
2126
  await this.dispatchArrayDerivationsOnDelete(id);
2046
2127
  }
2047
2128
  }
2129
+ /**
2130
+ * @internal — GDPR crypto-shred a LIVE record to a tombstone (#304).
2131
+ *
2132
+ * Rewrites the on-disk envelope to `{ _noydb, _v, _ts, _by, _iv:'', _data:'' }`,
2133
+ * dropping `_iv`/`_data`/`_cek`/`_det`. The wrapped per-record CEK is gone, so
2134
+ * the body — and (via {@link tombstoneHistory}) every history version under
2135
+ * the same CEK — is permanently undecryptable; the collection DEK and every
2136
+ * other record are untouched. `_det` is stripped too, so `findByDet` no
2137
+ * longer matches the shredded record (avoiding a post-shred TamperedError).
2138
+ *
2139
+ * Unlike `delete()`/`_internalDelete`, this:
2140
+ * - does NOT fire onDelete guards / MV / derivation dispatch (a shred is an
2141
+ * erasure, not a domain delete — re-running those would be wrong),
2142
+ * - does NOT append a per-record ledger entry (`vault.forget()` appends a
2143
+ * single `op:'forget'` summary for the whole subject),
2144
+ * - keeps the record KEY present (it's an overwrite, not an adapter delete)
2145
+ * so the version counter + "record existed" survive for audit.
2146
+ *
2147
+ * Idempotent: returns `null` when the record is absent or already a tombstone.
2148
+ * Otherwise returns `{ previousVersion }`. Invalidates the eager cache, the
2149
+ * lazy LRU, and the per-record CEK cache for this id.
2150
+ */
2151
+ /**
2152
+ * @internal — decrypt an envelope to a plain record for subject-index
2153
+ * rebuild (#304). Returns `null` for a tombstone or unreadable envelope.
2154
+ * Skips schema validation — the rebuild only reads the subject field.
2155
+ */
2156
+ async _decodeEnvelope(envelope, id) {
2157
+ try {
2158
+ const rec = await this.decryptRecord(envelope, { skipValidation: true, id });
2159
+ return rec === null ? null : rec;
2160
+ } catch {
2161
+ return null;
2162
+ }
2163
+ }
2164
+ async _writeTombstone(id, actor) {
2165
+ const live = await this.adapter.get(this.vault, this.name, id);
2166
+ if (!live || isTombstone(live, this.encrypted)) return null;
2167
+ await this.adapter.put(this.vault, this.name, id, buildTombstone(live._v, actor));
2168
+ this.cache.delete(id);
2169
+ this.lru?.remove(id);
2170
+ this.cekCache?.remove(id);
2171
+ return { previousVersion: live._v };
2172
+ }
2048
2173
  /**
2049
2174
  * Cascade deletes of array-shape derived rows when a source row is
2050
2175
  * deleted. Reads each registered strategy's fanout sidecar
@@ -2067,7 +2192,7 @@ var Collection = class {
2067
2192
  for (const [outputKey, outSpec] of Object.entries(spec.outputs)) {
2068
2193
  if (outSpec.shape !== "array") continue;
2069
2194
  if (helpers === null) {
2070
- helpers = await import("./fanout-sidecar-N6OJX6QR.js");
2195
+ helpers = await import("./fanout-sidecar-FIJJ46YG.js");
2071
2196
  }
2072
2197
  const sidecar = await helpers.loadFanoutSidecar(
2073
2198
  this.adapter,
@@ -2107,7 +2232,7 @@ var Collection = class {
2107
2232
  if (mode === "eager") {
2108
2233
  if (executor === null) {
2109
2234
  ;
2110
- ({ MaterializedViewExecutor: executor } = await import("./executor-HSSRXDOB.js"));
2235
+ ({ MaterializedViewExecutor: executor } = await import("./executor-3W63Y44O.js"));
2111
2236
  }
2112
2237
  await executor.refresh(reg, {
2113
2238
  getCollection: (name) => this.materializedViewSource.getCollection(name),
@@ -2116,7 +2241,7 @@ var Collection = class {
2116
2241
  });
2117
2242
  } else if (mode === "lazy") {
2118
2243
  if (staleHelpers === null) {
2119
- staleHelpers = await import("./stale-JH67FU57.js");
2244
+ staleHelpers = await import("./stale-TOA36SRK.js");
2120
2245
  }
2121
2246
  staleHelpers.markMVStale(registry, reg.spec.name);
2122
2247
  }
@@ -2139,7 +2264,7 @@ var Collection = class {
2139
2264
  );
2140
2265
  }
2141
2266
  if (this.materializedViewSource !== void 0) {
2142
- const { resolveStaleMVOnRead } = await import("./stale-JH67FU57.js");
2267
+ const { resolveStaleMVOnRead } = await import("./stale-TOA36SRK.js");
2143
2268
  await resolveStaleMVOnRead(this.materializedViewSource, this.name);
2144
2269
  }
2145
2270
  await this.ensureHydrated();
@@ -2457,6 +2582,7 @@ var Collection = class {
2457
2582
  const entries = [];
2458
2583
  for (const env of envelopes) {
2459
2584
  const record = await this.decryptRecord(env, { skipValidation: true });
2585
+ if (record === null) continue;
2460
2586
  entries.push({
2461
2587
  version: env._v,
2462
2588
  timestamp: env._ts,
@@ -2593,6 +2719,7 @@ var Collection = class {
2593
2719
  const envelope = await this.adapter.get(this.vault, this.name, id);
2594
2720
  if (envelope) {
2595
2721
  const record = await this.decryptRecord(envelope);
2722
+ if (record === null) continue;
2596
2723
  items.push(record);
2597
2724
  if (!this.lazy && !this.cache.has(id)) {
2598
2725
  this.cache.set(id, { record, version: envelope._v });
@@ -2669,6 +2796,7 @@ var Collection = class {
2669
2796
  const out = [];
2670
2797
  for (const { id, envelope } of items) {
2671
2798
  const record = await this.decryptRecord(envelope);
2799
+ if (record === null) continue;
2672
2800
  out.push({ id, record, version: envelope._v });
2673
2801
  }
2674
2802
  return out;
@@ -2690,6 +2818,18 @@ var Collection = class {
2690
2818
  * the cache entry (record still present) or deletes it (record was
2691
2819
  * gone before the tx and the revert deleted it again).
2692
2820
  */
2821
+ /**
2822
+ * @internal — evict ONLY the per-record CEK cache entry for `id`. Used by
2823
+ * `vault.rotateRecordCek()`: after a hard CEK rotation the cached unwrapped
2824
+ * CEK is stale (it would decrypt the pre-rotation body and fail GCM auth on
2825
+ * the post-rotation body). Eviction must be synchronous with the live-envelope
2826
+ * rewrite so no concurrent read observes the old CEK. Paired with
2827
+ * {@link _invalidateCacheEntry} (which refreshes the decrypted-record cache).
2828
+ * No-op when the collection is not `perRecordKeys`.
2829
+ */
2830
+ _invalidateCekCacheEntry(id) {
2831
+ this.cekCache?.remove(id);
2832
+ }
2693
2833
  async _invalidateCacheEntry(id) {
2694
2834
  if (this.lazy && this.lru) {
2695
2835
  this.lru.remove(id);
@@ -2707,6 +2847,14 @@ var Collection = class {
2707
2847
  return;
2708
2848
  }
2709
2849
  const record = await this.decryptRecord(envelope);
2850
+ if (record === null) {
2851
+ this.cache.delete(id);
2852
+ if (previous) {
2853
+ this.indexes?.remove(id, previous.record);
2854
+ this.uniqueConstraints?.remove(id, previous.record);
2855
+ }
2856
+ return;
2857
+ }
2710
2858
  this.cache.set(id, { record, version: envelope._v });
2711
2859
  this.indexes?.upsert(id, record, previous ? previous.record : null);
2712
2860
  this.uniqueConstraints?.upsert(id, record, previous?.record);
@@ -2731,8 +2879,9 @@ var Collection = class {
2731
2879
  const ids = await this.adapter.list(this.vault, this.name);
2732
2880
  for (const id of ids) {
2733
2881
  const envelope = await this.adapter.get(this.vault, this.name, id);
2734
- if (envelope) {
2735
- const record = await this.decryptRecord(envelope);
2882
+ if (envelope && !isTombstone(envelope, this.encrypted)) {
2883
+ const record = await this.decryptRecord(envelope, { id });
2884
+ if (record === null) continue;
2736
2885
  this.cache.set(id, { record, version: envelope._v });
2737
2886
  }
2738
2887
  }
@@ -2743,7 +2892,9 @@ var Collection = class {
2743
2892
  /** Hydrate from a pre-loaded snapshot (used by Vault). */
2744
2893
  async hydrateFromSnapshot(records) {
2745
2894
  for (const [id, envelope] of Object.entries(records)) {
2746
- const record = await this.decryptRecord(envelope);
2895
+ if (isTombstone(envelope, this.encrypted)) continue;
2896
+ const record = await this.decryptRecord(envelope, { id });
2897
+ if (record === null) continue;
2747
2898
  this.cache.set(id, { record, version: envelope._v });
2748
2899
  }
2749
2900
  this.hydrated = true;
@@ -2831,6 +2982,7 @@ var Collection = class {
2831
2982
  const envelope = await this.adapter.get(this.vault, this.name, recordId);
2832
2983
  if (!envelope) continue;
2833
2984
  const record = await this.decryptRecord(envelope, { skipValidation: true });
2985
+ if (record === null) continue;
2834
2986
  await this.maintainPersistedIndexesOnPut(recordId, record, null, envelope._v);
2835
2987
  }
2836
2988
  this.persistedIndexesLoaded = true;
@@ -2881,8 +3033,13 @@ var Collection = class {
2881
3033
  const env = await this.adapter.get(this.vault, this.name, id);
2882
3034
  if (!env) continue;
2883
3035
  try {
2884
- const body = JSON.parse(await this.decryptJsonString(env));
2885
- sidecar.set(decoded.recordId, body.value);
3036
+ const sidecarJson = await this.decryptJsonString(env);
3037
+ if (sidecarJson === null) {
3038
+ sidecar.set(decoded.recordId, void 0);
3039
+ } else {
3040
+ const body = JSON.parse(sidecarJson);
3041
+ sidecar.set(decoded.recordId, body.value);
3042
+ }
2886
3043
  } catch {
2887
3044
  sidecar.set(decoded.recordId, void 0);
2888
3045
  }
@@ -2896,6 +3053,7 @@ var Collection = class {
2896
3053
  const env = await this.adapter.get(this.vault, this.name, id);
2897
3054
  if (!env) continue;
2898
3055
  const record = await this.decryptRecord(env, { skipValidation: true });
3056
+ if (record === null) continue;
2899
3057
  const live = readPersistedValue(record, field);
2900
3058
  const stored = sidecar.get(id);
2901
3059
  const hasSidecar = sidecarIds.has(id);
@@ -2978,7 +3136,8 @@ var Collection = class {
2978
3136
  recordId: id,
2979
3137
  getDEK: this.getDEK,
2980
3138
  encrypted: this.encrypted,
2981
- userId: this.keyring.userId
3139
+ userId: this.keyring.userId,
3140
+ erasableBlobs: this.perRecordCek
2982
3141
  });
2983
3142
  }
2984
3143
  /** Get all records as encrypted envelopes (for dump). */
@@ -2986,7 +3145,8 @@ var Collection = class {
2986
3145
  await this.ensureHydrated();
2987
3146
  const result = {};
2988
3147
  for (const [id, entry] of this.cache) {
2989
- result[id] = await this.encryptRecord(entry.record, entry.version);
3148
+ const cek = this.perRecordCek ? await this.resolveRecordCek(id) : void 0;
3149
+ result[id] = await this.encryptRecord(entry.record, entry.version, cek);
2990
3150
  }
2991
3151
  return result;
2992
3152
  }
@@ -3014,8 +3174,11 @@ var Collection = class {
3014
3174
  if (hasMoney && this.moneyFields) {
3015
3175
  result = decodeMoneyFields(result, this.moneyFields, typeof locale === "string" ? locale : void 0);
3016
3176
  }
3017
- if (!locale) return result;
3018
- if (hasI18n && this.i18nFields) {
3177
+ const hasStaticDisplay = hasDict && this.dictKeyFields !== void 0 && Object.values(this.dictKeyFields).some(
3178
+ (d) => isStaticDictDescriptor(d) && d.displayLocale !== void 0
3179
+ );
3180
+ if (!locale && !hasStaticDisplay) return result;
3181
+ if (locale && hasI18n && this.i18nFields) {
3019
3182
  result = this.i18nStrategy.applyI18nLocale(result, this.i18nFields, locale, localeOpts?.fallback);
3020
3183
  }
3021
3184
  if (hasDict && this.dictKeyFields && this.dictLabelResolver && locale !== "raw") {
@@ -3024,13 +3187,23 @@ var Collection = class {
3024
3187
  for (const [field, desc] of Object.entries(this.dictKeyFields)) {
3025
3188
  const policy = desc.onMissing ? resolvePolicy(desc.onMissing, "read") : "null";
3026
3189
  const fallback = policy === "substitute" ? localeOpts?.fallback ?? desc.substitute : localeOpts?.fallback;
3190
+ const effLocale = locale ?? (isStaticDictDescriptor(desc) ? desc.displayLocale : void 0);
3027
3191
  const resolveKey = async (key) => {
3028
- const label = await resolver(desc.name, key, locale, fallback);
3192
+ if (!effLocale) {
3193
+ if (policy === "throw") {
3194
+ throw new LocaleNotSpecifiedError(
3195
+ field,
3196
+ `dictKey "${field}": no locale active to resolve key "${key}".`
3197
+ );
3198
+ }
3199
+ return null;
3200
+ }
3201
+ const label = await resolver(desc.name, key, effLocale, fallback);
3029
3202
  if (label === void 0) {
3030
3203
  if (policy === "throw") {
3031
3204
  throw new LocaleNotSpecifiedError(
3032
3205
  field,
3033
- `dictKey "${field}": no label for key "${key}" in locale "${locale}".`
3206
+ `dictKey "${field}": no label for key "${key}" in locale "${effLocale}".`
3034
3207
  );
3035
3208
  }
3036
3209
  return null;
@@ -3187,6 +3360,7 @@ var Collection = class {
3187
3360
  if (!envelope) continue;
3188
3361
  try {
3189
3362
  const json = await this.decryptJsonString(envelope);
3363
+ if (json === null) continue;
3190
3364
  const body = JSON.parse(json);
3191
3365
  if (typeof body.recordId !== "string") continue;
3192
3366
  const rows = byField.get(decoded.field) ?? [];
@@ -3296,7 +3470,31 @@ var Collection = class {
3296
3470
  };
3297
3471
  return new LazyQuery(source);
3298
3472
  }
3299
- async encryptJsonString(json, version) {
3473
+ /**
3474
+ * Resolve the stable CEK for a record on the WRITE path — see
3475
+ * {@link resolveStableCek}. Thin delegate that supplies the collection's
3476
+ * CEK cache, live-envelope reader, and DEK resolver.
3477
+ */
3478
+ resolveRecordCek(id) {
3479
+ return resolveStableCek(
3480
+ {
3481
+ cache: this.cekCache,
3482
+ getLive: (rid) => this.adapter.get(this.vault, this.name, rid),
3483
+ getDEK: () => this.getDEK(this.name)
3484
+ },
3485
+ id
3486
+ );
3487
+ }
3488
+ /**
3489
+ * Encrypt a JSON body into an envelope.
3490
+ *
3491
+ * When `cek` is supplied (per-record CEK collections), the body is
3492
+ * encrypted under the CEK and the CEK is AES-KW-wrapped under the
3493
+ * collection DEK and stamped on `_cek`. When `cek` is omitted, the legacy
3494
+ * path encrypts the body directly under the collection DEK — byte-identical
3495
+ * to pre-CEK behaviour, so non-adopting collections pay nothing.
3496
+ */
3497
+ async encryptJsonString(json, version, cek) {
3300
3498
  const by = this.keyring.userId;
3301
3499
  if (!this.encrypted) {
3302
3500
  return {
@@ -3309,6 +3507,19 @@ var Collection = class {
3309
3507
  };
3310
3508
  }
3311
3509
  const dek = await this.getDEK(this.name);
3510
+ if (cek !== void 0) {
3511
+ const { iv: iv2, data: data2 } = await encrypt(json, cek);
3512
+ const wrapped = await wrapCek(cek, dek);
3513
+ return {
3514
+ _noydb: NOYDB_FORMAT_VERSION,
3515
+ _v: version,
3516
+ _ts: (/* @__PURE__ */ new Date()).toISOString(),
3517
+ _iv: iv2,
3518
+ _data: data2,
3519
+ _by: by,
3520
+ _cek: wrapped
3521
+ };
3522
+ }
3312
3523
  const { iv, data } = await encrypt(json, dek);
3313
3524
  return {
3314
3525
  _noydb: NOYDB_FORMAT_VERSION,
@@ -3319,8 +3530,8 @@ var Collection = class {
3319
3530
  _by: by
3320
3531
  };
3321
3532
  }
3322
- async encryptRecord(record, version) {
3323
- const base = await this.encryptJsonString(JSON.stringify(record), version);
3533
+ async encryptRecord(record, version, cek) {
3534
+ const base = await this.encryptJsonString(JSON.stringify(record), version, cek);
3324
3535
  if (!this.deterministicFields || !this.encrypted) return base;
3325
3536
  const dek = await this.getDEK(this.name);
3326
3537
  const rec = record;
@@ -3398,7 +3609,8 @@ var Collection = class {
3398
3609
  const env = await this.adapter.get(this.vault, this.name, id);
3399
3610
  if (!env || !env._det) continue;
3400
3611
  if (env._det[field] === target) {
3401
- matches.push(await this.decryptRecord(env));
3612
+ const rec = await this.decryptRecord(env);
3613
+ if (rec !== null) matches.push(rec);
3402
3614
  }
3403
3615
  }
3404
3616
  return matches;
@@ -3499,7 +3711,14 @@ var Collection = class {
3499
3711
  return null;
3500
3712
  }
3501
3713
  const dek = await this.getDEK(key);
3502
- const plaintext = await decrypt(envelope._iv, envelope._data, dek);
3714
+ let plaintext;
3715
+ if (envelope._cek !== void 0) {
3716
+ const cek = await unwrapCek(envelope._cek, dek);
3717
+ this.cekCache?.set(id, cek, 1);
3718
+ plaintext = await decrypt(envelope._iv, envelope._data, cek);
3719
+ } else {
3720
+ plaintext = await decrypt(envelope._iv, envelope._data, dek);
3721
+ }
3503
3722
  const record = JSON.parse(plaintext);
3504
3723
  this.emitCrossTierEvent({
3505
3724
  actor: this.keyring.userId,
@@ -3555,18 +3774,19 @@ var Collection = class {
3555
3774
  const toKey = dekKey(this.name, toTier);
3556
3775
  const fromDek = await this.getDEK(fromKey);
3557
3776
  const toDek = await this.getDEK(toKey);
3558
- const plaintext = await decrypt(envelope._iv, envelope._data, fromDek);
3559
- const { iv, data } = await encrypt(plaintext, toDek);
3560
3777
  const now = (/* @__PURE__ */ new Date()).toISOString();
3778
+ const body = await rewrapBodyToDek(envelope, fromDek, toDek);
3779
+ if (body.cek) this.cekCache?.set(id, body.cek, 1);
3561
3780
  const next = {
3562
3781
  _noydb: NOYDB_FORMAT_VERSION,
3563
3782
  _v: envelope._v + 1,
3564
3783
  _ts: now,
3565
- _iv: iv,
3566
- _data: data,
3784
+ _iv: body._iv,
3785
+ _data: body._data,
3567
3786
  _by: this.keyring.userId,
3568
3787
  _tier: toTier,
3569
- _elevatedBy: this.keyring.userId
3788
+ _elevatedBy: this.keyring.userId,
3789
+ ...body._cek !== void 0 ? { _cek: body._cek } : {}
3570
3790
  };
3571
3791
  await this.adapter.put(this.vault, this.name, id, next);
3572
3792
  this.emitCrossTierEvent({
@@ -3602,17 +3822,18 @@ var Collection = class {
3602
3822
  if (toTier > 0) this.assertDeclaredTier(toTier);
3603
3823
  const fromDek = await this.getDEK(dekKey(this.name, fromTier));
3604
3824
  const toDek = await this.getDEK(dekKey(this.name, toTier));
3605
- const plaintext = await decrypt(envelope._iv, envelope._data, fromDek);
3606
- const { iv, data } = await encrypt(plaintext, toDek);
3607
3825
  const now = (/* @__PURE__ */ new Date()).toISOString();
3826
+ const body = await rewrapBodyToDek(envelope, fromDek, toDek);
3827
+ if (body.cek) this.cekCache?.set(id, body.cek, 1);
3608
3828
  const next = {
3609
3829
  _noydb: NOYDB_FORMAT_VERSION,
3610
3830
  _v: envelope._v + 1,
3611
3831
  _ts: now,
3612
- _iv: iv,
3613
- _data: data,
3832
+ _iv: body._iv,
3833
+ _data: body._data,
3614
3834
  _by: this.keyring.userId,
3615
- ...toTier > 0 && { _tier: toTier }
3835
+ ...toTier > 0 && { _tier: toTier },
3836
+ ...body._cek !== void 0 ? { _cek: body._cek } : {}
3616
3837
  };
3617
3838
  await this.adapter.put(this.vault, this.name, id, next);
3618
3839
  this.emitCrossTierEvent({
@@ -3634,10 +3855,30 @@ var Collection = class {
3634
3855
  } catch {
3635
3856
  }
3636
3857
  }
3637
- /** Low-level: decrypt an envelope and return the raw JSON string. */
3638
- async decryptJsonString(envelope) {
3858
+ /**
3859
+ * Low-level: decrypt an envelope and return the raw JSON string.
3860
+ *
3861
+ * `_cek` presence is the format discriminant (NOT `this.perRecordCek`),
3862
+ * so a mixed vault — and a recipient that never opted into
3863
+ * `perRecordKeys` — decrypts both legacy and CEK records:
3864
+ * - `_cek` present → unwrap the CEK under the collection DEK, decrypt the
3865
+ * body under the CEK (cache the unwrapped CEK so repeated reads skip it).
3866
+ * - `_cek` absent → legacy path, body decrypts directly under the
3867
+ * collection DEK.
3868
+ *
3869
+ * The optional `id` lets reads populate the CEK cache; it is omitted by
3870
+ * callers (history, conflict merge) that have only the envelope.
3871
+ */
3872
+ async decryptJsonString(envelope, id) {
3873
+ if (isTombstone(envelope, this.encrypted)) return null;
3639
3874
  if (!this.encrypted) return envelope._data;
3640
3875
  const dek = await this.getDEK(this.name);
3876
+ if (envelope._cek !== void 0) {
3877
+ const cached = id !== void 0 ? this.cekCache?.get(id) : void 0;
3878
+ const cek = cached ?? await unwrapCek(envelope._cek, dek);
3879
+ if (cached === void 0 && id !== void 0) this.cekCache?.set(id, cek, 1);
3880
+ return decrypt(envelope._iv, envelope._data, cek);
3881
+ }
3641
3882
  return decrypt(envelope._iv, envelope._data, dek);
3642
3883
  }
3643
3884
  /**
@@ -3656,7 +3897,8 @@ var Collection = class {
3656
3897
  * false positive. Every non-history read leaves this flag `false`.
3657
3898
  */
3658
3899
  async decryptRecord(envelope, opts = {}) {
3659
- const json = await this.decryptJsonString(envelope);
3900
+ const json = await this.decryptJsonString(envelope, opts.id);
3901
+ if (json === null) return null;
3660
3902
  let parsed = JSON.parse(json);
3661
3903
  if (this.crdtMode && parsed !== null && typeof parsed === "object" && "_crdt" in parsed) {
3662
3904
  parsed = this.crdtStrategy.resolveCrdtSnapshot(parsed);
@@ -3784,6 +4026,21 @@ function withArchive(opts) {
3784
4026
  // src/sequence/index.ts
3785
4027
  var SEQUENCE_COLLECTION = "_sequences";
3786
4028
  var MAX_NEXT_ATTEMPTS = 16;
4029
+ function resolveSequenceKey(series, opts) {
4030
+ const partition = opts?.partition;
4031
+ if (!partition || partition.length === 0) return series;
4032
+ const parts = partition.map((p) => {
4033
+ if (typeof p === "number" && !Number.isFinite(p)) {
4034
+ throw new ValidationError(`sequence partition component must be a finite number, got ${p}`);
4035
+ }
4036
+ const s = String(p);
4037
+ if (s === "") {
4038
+ throw new ValidationError("sequence partition component must not be empty");
4039
+ }
4040
+ return encodeURIComponent(s);
4041
+ });
4042
+ return `${series}\0${parts.join("/")}`;
4043
+ }
3787
4044
  async function sleepBackoff(attempt) {
3788
4045
  const ceil = Math.min(2 ** attempt, 32);
3789
4046
  const ms = Math.floor(Math.random() * ceil);
@@ -3814,7 +4071,8 @@ var SequenceStore = class {
3814
4071
  handle(name) {
3815
4072
  return {
3816
4073
  next: () => this.next(name),
3817
- peek: () => this.peek(name)
4074
+ peek: () => this.peek(name),
4075
+ seedTo: (n) => this.seedTo(name, n)
3818
4076
  };
3819
4077
  }
3820
4078
  assertOnline() {
@@ -3867,6 +4125,30 @@ var SequenceStore = class {
3867
4125
  void lastConflict;
3868
4126
  throw new SequenceContentionError(name, MAX_NEXT_ATTEMPTS);
3869
4127
  }
4128
+ async seedTo(name, n) {
4129
+ this.assertOnline();
4130
+ if (n <= 0) return;
4131
+ let lastConflict;
4132
+ for (let attempt = 0; attempt < MAX_NEXT_ATTEMPTS; attempt++) {
4133
+ const { env, value } = await this.read(name);
4134
+ if (value >= n) return;
4135
+ const expectedVersion = env?._v ?? 0;
4136
+ const envelope = await this.encryptState({ value: n }, expectedVersion + 1);
4137
+ try {
4138
+ await this.adapter.put(this.vault, SEQUENCE_COLLECTION, name, envelope, expectedVersion);
4139
+ return;
4140
+ } catch (err) {
4141
+ if (err instanceof ConflictError) {
4142
+ lastConflict = err;
4143
+ if (attempt < MAX_NEXT_ATTEMPTS - 1) await sleepBackoff(attempt);
4144
+ continue;
4145
+ }
4146
+ throw err;
4147
+ }
4148
+ }
4149
+ void lastConflict;
4150
+ throw new SequenceContentionError(name, MAX_NEXT_ATTEMPTS);
4151
+ }
3870
4152
  };
3871
4153
 
3872
4154
  // src/numbering/index.ts
@@ -5100,6 +5382,19 @@ function summariseAggregateOp(value) {
5100
5382
  }
5101
5383
 
5102
5384
  // src/vault.ts
5385
+ function resolveLabelFromMap(labels, locale, fallback) {
5386
+ if (labels[locale] !== void 0) return labels[locale];
5387
+ const chain = Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];
5388
+ for (const fb of chain) {
5389
+ if (fb === "any") {
5390
+ const any = Object.values(labels)[0];
5391
+ if (any !== void 0) return any;
5392
+ } else if (labels[fb] !== void 0) {
5393
+ return labels[fb];
5394
+ }
5395
+ }
5396
+ return void 0;
5397
+ }
5103
5398
  var Vault = class {
5104
5399
  adapter;
5105
5400
  /** The vault's name as passed to `openVault()`. Stable for the instance lifetime. */
@@ -5143,6 +5438,7 @@ var Vault = class {
5143
5438
  periodsStrategy;
5144
5439
  shadowStrategy;
5145
5440
  historyStrategy;
5441
+ forgetStrategy;
5146
5442
  i18nStrategy;
5147
5443
  syncStrategy;
5148
5444
  /**
@@ -5309,6 +5605,27 @@ var Vault = class {
5309
5605
  * Populated by `collection()` when the `dictKeyFields` option is passed.
5310
5606
  */
5311
5607
  dictKeyFieldRegistry = /* @__PURE__ */ new Map();
5608
+ /**
5609
+ * Names of dictionaries backed by a `staticDict()` descriptor (#291).
5610
+ * A static dict skips the `dictKeyFieldRegistry` rename machinery, but the
5611
+ * vault must still *know* a name is static so `vault.dictionary(name)` can
5612
+ * refuse mutation (`StaticDictReadonlyError`). Populated at `collection()`
5613
+ * config time whenever a `StaticDictDescriptor` is seen.
5614
+ */
5615
+ staticDictNames = /* @__PURE__ */ new Set();
5616
+ /**
5617
+ * Static-dict descriptors keyed by dictionary name (#291). Backs the
5618
+ * read-path label resolver (resolve from the in-memory table) and the
5619
+ * query-seam `resolveDictSource` snapshot. Last writer wins when the same
5620
+ * name is registered by multiple collections (identical-across-vaults by
5621
+ * construction, so the tables match).
5622
+ */
5623
+ staticByName = /* @__PURE__ */ new Map();
5624
+ /**
5625
+ * Per-collection map of field name → StaticDictDescriptor (#291). Used by
5626
+ * `enforceStaticDictOnPut` to validate stored codes against `desc.keys`.
5627
+ */
5628
+ staticDescriptorByField = /* @__PURE__ */ new Map();
5312
5629
  /**
5313
5630
  * Registry of i18nText fields declared across all collections. Keyed
5314
5631
  * by collection name → field name → I18nTextDescriptor. Used by
@@ -5355,6 +5672,7 @@ var Vault = class {
5355
5672
  this.periodsStrategy = opts.periodsStrategy ?? NO_PERIODS;
5356
5673
  this.shadowStrategy = opts.shadowStrategy ?? NO_SHADOW;
5357
5674
  this.historyStrategy = opts.historyStrategy ?? NO_HISTORY;
5675
+ this.forgetStrategy = opts.forgetStrategy ?? NO_FORGET;
5358
5676
  this.i18nStrategy = opts.i18nStrategy ?? NO_I18N;
5359
5677
  this.syncStrategy = opts.syncStrategy ?? NO_SYNC;
5360
5678
  void opts.guardStrategies;
@@ -5459,10 +5777,22 @@ var Vault = class {
5459
5777
  }
5460
5778
  if (options?.dictKeyFields) {
5461
5779
  const dictFieldMap = {};
5780
+ const staticFieldMap = {};
5462
5781
  for (const [field, desc] of Object.entries(options.dictKeyFields)) {
5463
- dictFieldMap[field] = desc.name;
5782
+ if (isStaticDictDescriptor(desc)) {
5783
+ staticFieldMap[field] = desc;
5784
+ this.staticDictNames.add(desc.name);
5785
+ this.staticByName.set(desc.name, desc);
5786
+ } else {
5787
+ dictFieldMap[field] = desc.name;
5788
+ }
5789
+ }
5790
+ if (Object.keys(dictFieldMap).length > 0) {
5791
+ this.dictKeyFieldRegistry.set(collectionName, dictFieldMap);
5792
+ }
5793
+ if (Object.keys(staticFieldMap).length > 0) {
5794
+ this.staticDescriptorByField.set(collectionName, staticFieldMap);
5464
5795
  }
5465
- this.dictKeyFieldRegistry.set(collectionName, dictFieldMap);
5466
5796
  }
5467
5797
  if ((options?.schemaUpdate?.length ?? 0) > 0) {
5468
5798
  this.#schemaUpdateNames.set(collectionName, (options.schemaUpdate ?? []).map((s) => s.name));
@@ -5564,6 +5894,17 @@ var Vault = class {
5564
5894
  if (options?.acknowledgeDeterministicRisk !== void 0) {
5565
5895
  collOpts.acknowledgeDeterministicRisk = options.acknowledgeDeterministicRisk;
5566
5896
  }
5897
+ if (options?.perRecordKeys !== void 0) {
5898
+ collOpts.perRecordKeys = options.perRecordKeys;
5899
+ }
5900
+ if (this.forgetStrategy.subjects[collectionName] !== void 0) {
5901
+ if (options?.perRecordKeys === false) {
5902
+ console.warn(
5903
+ `[noy-db] Collection "${collectionName}" is declared in withForgetCascade but opened with perRecordKeys: false. Forcing perRecordKeys: true \u2014 GDPR crypto-shred requires per-record CEKs.`
5904
+ );
5905
+ }
5906
+ collOpts.perRecordKeys = true;
5907
+ }
5567
5908
  if (options?.tiers !== void 0) collOpts.tiers = options.tiers;
5568
5909
  if (options?.tierMode !== void 0) collOpts.tierMode = options.tierMode;
5569
5910
  collOpts.onCrossTierAccess = (event) => this.emitCrossTier(event);
@@ -5573,6 +5914,11 @@ var Vault = class {
5573
5914
  if (options?.computed !== void 0) collOpts.computed = options.computed;
5574
5915
  if (options?.dictKeyFields !== void 0) {
5575
5916
  collOpts.dictLabelResolver = async (dictName, key, locale, fallback) => {
5917
+ const stat = this.staticByName.get(dictName);
5918
+ if (stat) {
5919
+ const labels = stat.table[key];
5920
+ return labels ? resolveLabelFromMap(labels, locale, fallback) : void 0;
5921
+ }
5576
5922
  const handle = this.dictionary(dictName);
5577
5923
  return handle.resolveLabel(key, locale, fallback);
5578
5924
  };
@@ -5581,6 +5927,7 @@ var Vault = class {
5581
5927
  if (options?.i18nFields !== void 0 || options?.dictKeyFields !== void 0) {
5582
5928
  collOpts.i18nPutValidator = (record) => {
5583
5929
  this.enforceI18nOnPut(collectionName, record);
5930
+ this.enforceStaticDictOnPut(collectionName, record);
5584
5931
  };
5585
5932
  }
5586
5933
  if (options?.i18nFields !== void 0 && this.translateText) {
@@ -5720,6 +6067,34 @@ var Vault = class {
5720
6067
  }
5721
6068
  }
5722
6069
  }
6070
+ /**
6071
+ * Validate staticDict codes on a `put()` (#291). For each `staticDict()`
6072
+ * field, every stored code must be a declared key of the descriptor's
6073
+ * table, else `UnknownDictCodeError`. Opt out per descriptor with
6074
+ * `{ validateCodes: false }`. Supports scalar, dotted, and `[].`-wildcard
6075
+ * field paths via `getAtPath` (same path support as i18n validation).
6076
+ */
6077
+ enforceStaticDictOnPut(collectionName, record) {
6078
+ const staticFields = this.staticDescriptorByField.get(collectionName);
6079
+ if (!staticFields || Object.keys(staticFields).length === 0) return;
6080
+ if (!record || typeof record !== "object") return;
6081
+ const obj = record;
6082
+ for (const [field, desc] of Object.entries(staticFields)) {
6083
+ if (desc.validateCodes === false) continue;
6084
+ const known = new Set(desc.keys);
6085
+ const values = getAtPath(obj, field);
6086
+ for (const value of values) {
6087
+ if (value === void 0 || value === null) continue;
6088
+ const codes = Array.isArray(value) ? value : [value];
6089
+ for (const code of codes) {
6090
+ if (typeof code !== "string") continue;
6091
+ if (!known.has(code)) {
6092
+ throw new UnknownDictCodeError(desc.name, field, code);
6093
+ }
6094
+ }
6095
+ }
6096
+ }
6097
+ }
5723
6098
  /**
5724
6099
  * Apply locale resolution to a record for the given collection.
5725
6100
  *
@@ -5728,14 +6103,18 @@ var Vault = class {
5728
6103
  */
5729
6104
  async applyLocale(collectionName, record, localeOpts) {
5730
6105
  const locale = localeOpts.locale ?? this.locale;
5731
- if (!locale) return record;
6106
+ const staticFields = this.staticDescriptorByField.get(collectionName);
6107
+ const hasStaticDisplay = staticFields !== void 0 && Object.values(staticFields).some((d) => d.displayLocale !== void 0);
6108
+ if (!locale && !hasStaticDisplay) return record;
5732
6109
  let result = record;
5733
- const i18nFields = this.i18nFieldRegistry.get(collectionName);
5734
- if (i18nFields && Object.keys(i18nFields).length > 0) {
5735
- result = this.i18nStrategy.applyI18nLocale(result, i18nFields, locale, localeOpts.fallback);
6110
+ if (locale) {
6111
+ const i18nFields = this.i18nFieldRegistry.get(collectionName);
6112
+ if (i18nFields && Object.keys(i18nFields).length > 0) {
6113
+ result = this.i18nStrategy.applyI18nLocale(result, i18nFields, locale, localeOpts.fallback);
6114
+ }
5736
6115
  }
5737
6116
  const dictFields = this.dictKeyFieldRegistry.get(collectionName);
5738
- if (dictFields && Object.keys(dictFields).length > 0 && locale !== "raw") {
6117
+ if (locale && dictFields && Object.keys(dictFields).length > 0 && locale !== "raw") {
5739
6118
  const withLabels = { ...result };
5740
6119
  for (const [field, dictName] of Object.entries(dictFields)) {
5741
6120
  const key = result[field];
@@ -5748,6 +6127,22 @@ var Vault = class {
5748
6127
  }
5749
6128
  result = withLabels;
5750
6129
  }
6130
+ if (staticFields && Object.keys(staticFields).length > 0 && locale !== "raw") {
6131
+ const withLabels = { ...result };
6132
+ for (const [field, desc] of Object.entries(staticFields)) {
6133
+ const effLocale = locale ?? desc.displayLocale;
6134
+ if (!effLocale) continue;
6135
+ const key = result[field];
6136
+ if (typeof key !== "string") continue;
6137
+ const labels = desc.table[key];
6138
+ if (!labels) continue;
6139
+ const label = resolveLabelFromMap(labels, effLocale, localeOpts.fallback ?? desc.substitute);
6140
+ if (label !== void 0) {
6141
+ withLabels[`${field}Label`] = label;
6142
+ }
6143
+ }
6144
+ result = withLabels;
6145
+ }
5751
6146
  return result;
5752
6147
  }
5753
6148
  /**
@@ -5769,6 +6164,9 @@ var Vault = class {
5769
6164
  * ```
5770
6165
  */
5771
6166
  dictionary(name, options = {}) {
6167
+ if (this.staticDictNames.has(name)) {
6168
+ throw new StaticDictReadonlyError(name);
6169
+ }
5772
6170
  let handle = this.dictionaryCache.get(name);
5773
6171
  if (!handle) {
5774
6172
  handle = this.i18nStrategy.buildDictionaryHandle({
@@ -5838,6 +6236,26 @@ var Vault = class {
5838
6236
  * Returns `null` when `field` is not a dictKey in `leftCollection`.
5839
6237
  */
5840
6238
  resolveDictSource(leftCollection, field) {
6239
+ const staticFields = this.staticDescriptorByField.get(leftCollection);
6240
+ if (staticFields && field in staticFields) {
6241
+ const desc = staticFields[field];
6242
+ const rows = Object.entries(desc.table).map(
6243
+ ([key, labels]) => ({ key, labels, ...labels })
6244
+ );
6245
+ const source = {
6246
+ snapshot() {
6247
+ return rows;
6248
+ },
6249
+ lookupById(id) {
6250
+ return rows.find((e) => e["key"] === id);
6251
+ }
6252
+ };
6253
+ if (desc.displayLocale !== void 0) {
6254
+ ;
6255
+ source.displayLocale = desc.displayLocale;
6256
+ }
6257
+ return source;
6258
+ }
5841
6259
  const dictFields = this.dictKeyFieldRegistry.get(leftCollection);
5842
6260
  if (!dictFields || !(field in dictFields)) return null;
5843
6261
  const dictName = dictFields[field];
@@ -6005,17 +6423,23 @@ var Vault = class {
6005
6423
  * const cur = await vault.sequence('invoice-2026').peek() // current value, no allocation
6006
6424
  * ```
6007
6425
  */
6008
- sequence(name) {
6009
- if (this.numberingConfigs.has(name)) {
6426
+ sequence(series, opts) {
6427
+ if (series.includes("\0")) {
6428
+ throw new ValidationError(`sequence("${series}"): series name must not contain a null byte (\\x00).`);
6429
+ }
6430
+ if (this.numberingConfigs.has(series)) {
6010
6431
  const eng = this.deferred();
6011
6432
  return {
6012
- next: async (opts) => {
6013
- if (!opts?.for) {
6014
- throw new ValidationError(`sequence("${name}") is a deferred-numbering series; call next({ for: recordId }).`);
6433
+ next: async (nextOpts) => {
6434
+ if (!nextOpts?.for) {
6435
+ throw new ValidationError(`sequence("${series}") is a deferred-numbering series; call next({ for: recordId }).`);
6015
6436
  }
6016
- return (await eng.enqueue(name, opts.for)).assigned;
6437
+ return (await eng.enqueue(series, nextOpts.for)).assigned;
6017
6438
  },
6018
- peek: () => eng.peek(name)
6439
+ peek: () => eng.peek(series),
6440
+ seedTo: () => {
6441
+ throw new ValidationError(`sequence("${series}") is a deferred-numbering series; seedTo is CAS-only.`);
6442
+ }
6019
6443
  };
6020
6444
  }
6021
6445
  if (!this.sequenceStore) {
@@ -6027,7 +6451,7 @@ var Vault = class {
6027
6451
  actor: this.keyring.userId
6028
6452
  });
6029
6453
  }
6030
- return this.sequenceStore.handle(name);
6454
+ return this.sequenceStore.handle(resolveSequenceKey(series, opts));
6031
6455
  }
6032
6456
  /** @internal — lazily build the deferred-numbering engine with a cache-coherent stamp. */
6033
6457
  deferred() {
@@ -6142,12 +6566,12 @@ var Vault = class {
6142
6566
  if (!fieldSchema) {
6143
6567
  throw new AttestationError(`issueAttestation: collection '${collectionName}' has no attestation field-schema. Declare it via vault.collection('${collectionName}', { attestation: { fields: [...] } }).`);
6144
6568
  }
6145
- const { issueAttestationCore } = await import("./issue-ADVS4OVP.js");
6569
+ const { issueAttestationCore } = await import("./issue-TTMGHQ2J.js");
6146
6570
  const out = await issueAttestationCore(this.makeIssueContext(), { collection: collectionName, id, fieldSchema });
6147
6571
  return { docId: out.docId, qr: out.qr, keyId: out.keyId, publicKeyB64: out.publicKeyB64 };
6148
6572
  }
6149
6573
  async getDocumentSigningPublicKey() {
6150
- const { loadSigner, loadOrCreateSigner } = await import("./signer-P5D7Y72U.js");
6574
+ const { loadSigner, loadOrCreateSigner } = await import("./signer-YSXZT574.js");
6151
6575
  const existing = await loadSigner(this.adapter, this.name, this.getDEK);
6152
6576
  if (existing) return { keyId: existing.keyId, publicKeyB64: existing.publicKeyB64 };
6153
6577
  if (this.keyring.role !== "owner") {
@@ -6173,19 +6597,19 @@ var Vault = class {
6173
6597
  };
6174
6598
  }
6175
6599
  async revokeAttestation(docId) {
6176
- const { revokeDocCore } = await import("./revoke-ZDFKMR5E.js");
6600
+ const { revokeDocCore } = await import("./revoke-B54H2S2W.js");
6177
6601
  await revokeDocCore(this.makeRevokeContext(), docId);
6178
6602
  }
6179
6603
  async unrevokeAttestation(docId) {
6180
- const { unrevokeDocCore } = await import("./revoke-ZDFKMR5E.js");
6604
+ const { unrevokeDocCore } = await import("./revoke-B54H2S2W.js");
6181
6605
  await unrevokeDocCore(this.makeRevokeContext(), docId);
6182
6606
  }
6183
6607
  async getRevokedDocIds() {
6184
- const { getRevokedDocIdsCore } = await import("./revoke-ZDFKMR5E.js");
6608
+ const { getRevokedDocIdsCore } = await import("./revoke-B54H2S2W.js");
6185
6609
  return getRevokedDocIdsCore(this.makeRevokeContext());
6186
6610
  }
6187
6611
  async publishRevocationList() {
6188
- const { publishRevocationListCore } = await import("./revoke-ZDFKMR5E.js");
6612
+ const { publishRevocationListCore } = await import("./revoke-B54H2S2W.js");
6189
6613
  return publishRevocationListCore(this.makeRevokeContext());
6190
6614
  }
6191
6615
  makeRevokeContext() {
@@ -6319,9 +6743,24 @@ var Vault = class {
6319
6743
  });
6320
6744
  }
6321
6745
  if (rule.mode === "cascade") {
6746
+ const txCtx = this.noydb._activeTxContextOrNull;
6322
6747
  for (const match of matches) {
6323
6748
  const matchId = match["id"] ?? null;
6324
6749
  if (matchId === null) continue;
6750
+ if (txCtx !== null) {
6751
+ const prior = await this.adapter.get(this.name, rule.collection, matchId);
6752
+ if (prior !== null) {
6753
+ txCtx._executed.push({
6754
+ op: {
6755
+ type: "delete",
6756
+ vaultName: this.name,
6757
+ collectionName: rule.collection,
6758
+ id: matchId
6759
+ },
6760
+ priorEnvelope: prior
6761
+ });
6762
+ }
6763
+ }
6325
6764
  await fromCollection.delete(matchId);
6326
6765
  }
6327
6766
  }
@@ -6460,6 +6899,218 @@ var Vault = class {
6460
6899
  }
6461
6900
  return this.ledgerStore;
6462
6901
  }
6902
+ // ─── GDPR right-to-erasure (#304) ────────────────────────────────
6903
+ /** @internal — add a subject→record ref to the encrypted subject index. */
6904
+ async _addSubjectRef(subjectId, ref2) {
6905
+ await addSubjectRef(this.adapter, this.name, this.getDEK, this.encrypted, subjectId, ref2);
6906
+ }
6907
+ /** @internal — drop a subject→record ref from the encrypted subject index. */
6908
+ async _removeSubjectRef(subjectId, ref2) {
6909
+ await removeSubjectRef(this.adapter, this.name, this.getDEK, this.encrypted, subjectId, ref2);
6910
+ }
6911
+ /**
6912
+ * Rebuild the encrypted subject index from canonical records. The recovery
6913
+ * path for the documented read-modify-write race (RISK #3). Returns the
6914
+ * number of distinct subjects re-indexed.
6915
+ */
6916
+ async rebuildSubjectIndex() {
6917
+ if (Object.keys(this.forgetStrategy.subjects).length === 0) {
6918
+ throw new ForgetStrategyNotConfiguredError();
6919
+ }
6920
+ return rebuildSubjectIndex(
6921
+ this.adapter,
6922
+ this.name,
6923
+ this.getDEK,
6924
+ this.encrypted,
6925
+ this.forgetStrategy.subjects,
6926
+ async (collectionName, id, env) => {
6927
+ const coll = this.collection(collectionName);
6928
+ return coll._decodeEnvelope(env, id);
6929
+ }
6930
+ );
6931
+ }
6932
+ /**
6933
+ * GDPR crypto-shred of a data subject (#304). Consults the encrypted subject
6934
+ * index and, per matching record:
6935
+ * - rewrites the LIVE envelope to a tombstone (drops `_iv`/`_data`/`_cek`/`_det`),
6936
+ * - tombstones every `_history` version of the record,
6937
+ * so the body and all prior versions become permanently undecryptable while
6938
+ * the collection DEK and every OTHER record stay intact. Then appends ONE
6939
+ * `op:'forget'` ledger entry whose `payloadHash` is `sha256Hex(subjectId)` —
6940
+ * the chain still `verify()`s, PROVING the subject existed and was erased
6941
+ * without retaining any plaintext.
6942
+ *
6943
+ * Reports — but does not silently swallow — two completeness gaps:
6944
+ * - `unmigratedRecords`: a record whose body was NOT yet migrated to a
6945
+ * per-record CEK (legacy body still under the shared collection DEK). It
6946
+ * is still tombstoned, but its pre-shred ciphertext (if leaked to a
6947
+ * backup before migration) stays decryptable. Migrate, then re-forget.
6948
+ * - `blobResidueCollections`: a shredded record still has blob attachments,
6949
+ * which are keyed off a separate `_blob` DEK and are out of scope here.
6950
+ *
6951
+ * @throws ForgetStrategyNotConfiguredError when no `withForgetCascade` was set.
6952
+ */
6953
+ async forget(subjectId) {
6954
+ if (Object.keys(this.forgetStrategy.subjects).length === 0) {
6955
+ throw new ForgetStrategyNotConfiguredError();
6956
+ }
6957
+ const refs = await lookupSubject(this.adapter, this.name, this.getDEK, this.encrypted, subjectId);
6958
+ let recordsShredded = 0;
6959
+ let historyVersionsShredded = 0;
6960
+ const collections = /* @__PURE__ */ new Set();
6961
+ const unmigratedRecords = [];
6962
+ const blobResidueCollections = /* @__PURE__ */ new Set();
6963
+ let blobsShredded = 0;
6964
+ let blobsRetainedShared = 0;
6965
+ const blobsEnabled = this.blobStrategy !== void 0;
6966
+ const actor = this.keyring.userId;
6967
+ for (const ref2 of refs) {
6968
+ const coll = this.collection(ref2.collection);
6969
+ const perRecordKeys = this.forgetStrategy.subjects[ref2.collection] !== void 0;
6970
+ const live = await this.adapter.get(this.name, ref2.collection, ref2.id);
6971
+ if (perRecordKeys && live && live._data && live._cek === void 0) {
6972
+ unmigratedRecords.push(`${ref2.collection}:${ref2.id}`);
6973
+ }
6974
+ const shred = await coll._writeTombstone(ref2.id, actor);
6975
+ if (shred !== null) {
6976
+ recordsShredded++;
6977
+ collections.add(ref2.collection);
6978
+ }
6979
+ historyVersionsShredded += await this.historyStrategy.tombstoneHistory(
6980
+ this.adapter,
6981
+ this.name,
6982
+ ref2.collection,
6983
+ ref2.id,
6984
+ actor
6985
+ );
6986
+ if (blobsEnabled) {
6987
+ const r = await this.collection(ref2.collection).blob(ref2.id).shredAllForRecord();
6988
+ blobsShredded += r.shredded.length;
6989
+ blobsRetainedShared += r.retainedShared.length;
6990
+ if (r.residue.length > 0) blobResidueCollections.add(ref2.collection);
6991
+ } else {
6992
+ try {
6993
+ const slotIds = await this.adapter.list(this.name, `_blob_slots_${ref2.collection}`);
6994
+ if (slotIds.includes(ref2.id)) blobResidueCollections.add(ref2.collection);
6995
+ } catch {
6996
+ }
6997
+ }
6998
+ await this._removeSubjectRef(subjectId, ref2);
6999
+ }
7000
+ const subjectHash = await sha256Hex2(subjectId);
7001
+ const ledger = this.getLedgerOrNull();
7002
+ if (!ledger) {
7003
+ throw new Error(
7004
+ 'vault.forget() requires the history strategy for the erasure-proof ledger entry. Pass `historyStrategy: withHistory()` from "@noy-db/hub/history" to createNoydb().'
7005
+ );
7006
+ }
7007
+ const ledgerEntry = await ledger.append({
7008
+ op: "forget",
7009
+ collection: "",
7010
+ id: "",
7011
+ version: 0,
7012
+ actor,
7013
+ payloadHash: subjectHash,
7014
+ reason: JSON.stringify({
7015
+ recordsShredded,
7016
+ historyVersionsShredded,
7017
+ collections: [...collections],
7018
+ unmigratedCount: unmigratedRecords.length,
7019
+ blobsShredded,
7020
+ blobsRetainedShared,
7021
+ blobResidueCollections: [...blobResidueCollections]
7022
+ })
7023
+ });
7024
+ return {
7025
+ subject: subjectId,
7026
+ recordsShredded,
7027
+ historyVersionsShredded,
7028
+ collections: [...collections],
7029
+ unmigratedRecords,
7030
+ blobsShredded,
7031
+ blobsRetainedShared,
7032
+ blobResidueCollections: [...blobResidueCollections],
7033
+ ledgerEntry
7034
+ };
7035
+ }
7036
+ // ─── Record-scoped CEK sealing (#306 slices 2-3) ──────────────────────
7037
+ /**
7038
+ * Seal ONE record's content-encryption key (CEK) to an `at-*` host so that
7039
+ * host — and only that host — can decrypt exactly that record, with no
7040
+ * access to the vault DEK and no ability to read any other record.
7041
+ *
7042
+ * The grantor (this caller, who holds the collection DEK) reads the record's
7043
+ * live `_cek`, unwraps it under the collection DEK, exports the raw CEK
7044
+ * bytes, builds a {@link SealedCekBinding} `{collection, id, cek, expiresAt}`,
7045
+ * seals that binding for the recipient host via the host's published hint,
7046
+ * and persists a thin {@link SealedCekDeliveryEnvelope} at
7047
+ * `_sealed_cek/<collection>/<id>/<pid>`. The binding (not the delivery
7048
+ * envelope) is the security boundary: the host re-verifies `{collection, id}`
7049
+ * and `expiresAt` from inside the sealed payload.
7050
+ *
7051
+ * Only works on a `perRecordKeys` record — a legacy record has no `_cek` to
7052
+ * seal (its body is under the shared collection DEK, which is never exposed
7053
+ * by sealing) → {@link RecordCekNotFoundError}.
7054
+ *
7055
+ * @param collection Collection holding the record.
7056
+ * @param id Record id.
7057
+ * @param hostSealer The recipient host's {@link RecipientSealer}.
7058
+ * @param opts.expiresAt REQUIRED authoritative expiry (ISO 8601), sealed into
7059
+ * the binding the host verifies.
7060
+ * @returns `{ pid, envelopeKey }` — the host provider id and the
7061
+ * `<collection>/<id>/<pid>` key the delivery envelope was written under.
7062
+ */
7063
+ async sealRecordToHost(collection, id, hostSealer, opts) {
7064
+ return sealRecordToHost(this.sealingContext(), collection, id, hostSealer, opts);
7065
+ }
7066
+ /**
7067
+ * Revoke a single sealed-CEK delivery envelope by deleting it from the store.
7068
+ * A soft revocation: it removes the host's copy of the sealed CEK, but a host
7069
+ * that already fetched the envelope keeps whatever it cached. For a hard
7070
+ * revocation that makes the live record undecryptable to every prior grant,
7071
+ * use {@link rotateRecordCek}.
7072
+ */
7073
+ async revokeSealedRecord(collection, id, pid) {
7074
+ return revokeSealedRecord(this.sealingContext(), collection, id, pid);
7075
+ }
7076
+ /**
7077
+ * HARD-rotate a record's CEK: decrypt the live body under the old CEK,
7078
+ * re-encrypt it under a freshly-minted CEK, write the new live envelope, evict
7079
+ * the in-memory caches, and delete EVERY sealed-CEK delivery envelope for the
7080
+ * record. After this, any host holding a previously-sealed CEK can still
7081
+ * decrypt PRE-rotation history versions (they keep their old `_cek`) but NOT
7082
+ * the rotated live record (its body is under the new CEK → the old CEK fails
7083
+ * the AES-GCM auth tag → `TamperedError`). That asymmetry IS the revocation:
7084
+ * old grants lose the live record.
7085
+ *
7086
+ * Administrative path — bypasses `Collection.put` deliberately (no guards, no
7087
+ * history snapshot, no materialized-view refresh): rotation is a key-rotation
7088
+ * operation, not a business write, and must not version-bump history (which
7089
+ * would re-encrypt the prior version under the NEW CEK and defeat the point).
7090
+ *
7091
+ * @throws {@link RecordCekNotFoundError} if the record is missing or has no `_cek`.
7092
+ */
7093
+ async rotateRecordCek(collection, id) {
7094
+ return rotateRecordCek(this.sealingContext(), collection, id);
7095
+ }
7096
+ /**
7097
+ * Build the {@link SealingContext} the record-keys grantor functions need:
7098
+ * the vault-bound adapter, DEK resolver, actor, and the dual-cache eviction
7099
+ * `rotateRecordCek` performs (per-record CEK cache + decrypted-record cache).
7100
+ */
7101
+ sealingContext() {
7102
+ return {
7103
+ adapter: this.adapter,
7104
+ vault: this.name,
7105
+ getDEK: (collection) => this.getDEK(collection),
7106
+ actor: this.keyring.userId,
7107
+ invalidateRecordCaches: async (collection, id) => {
7108
+ const coll = this.collection(collection);
7109
+ coll._invalidateCekCacheEntry(id);
7110
+ await coll._invalidateCacheEntry(id);
7111
+ }
7112
+ };
7113
+ }
6463
7114
  /**
6464
7115
  * @internal — called by `Noydb.openVault` after construction.
6465
7116
  * Dynamic-imports `GuardRegistry` + `ReadOnlyVaultFacade` and seeds
@@ -6502,7 +7153,7 @@ var Vault = class {
6502
7153
  async _initDerivations(handles) {
6503
7154
  if (handles.length === 0) return;
6504
7155
  const [{ DerivationRegistry }, { ReadOnlyVaultFacade }] = await Promise.all([
6505
- import("./registry-XGLNADIE.js"),
7156
+ import("./registry-TGZISEWC.js"),
6506
7157
  import("./read-only-facade-ITU6L7BL.js")
6507
7158
  ]);
6508
7159
  const registry = new DerivationRegistry();
@@ -6533,7 +7184,7 @@ var Vault = class {
6533
7184
  */
6534
7185
  async _initMaterializedViews(handles) {
6535
7186
  if (handles.length === 0) return;
6536
- const { MaterializedViewRegistry } = await import("./registry-DK5YWAAA.js");
7187
+ const { MaterializedViewRegistry } = await import("./registry-SECUWSGY.js");
6537
7188
  const registry = new MaterializedViewRegistry();
6538
7189
  this.materializedViewRegistry = registry;
6539
7190
  const db = this;
@@ -6557,7 +7208,7 @@ var Vault = class {
6557
7208
  */
6558
7209
  async _initOverlayedViews(handles) {
6559
7210
  if (handles.length === 0) return;
6560
- const { OverlayedViewRegistry } = await import("./registry-IUZQVVBB.js");
7211
+ const { OverlayedViewRegistry } = await import("./registry-3YFLZ7WD.js");
6561
7212
  const registry = new OverlayedViewRegistry();
6562
7213
  const mvRegistry = this.materializedViewRegistry;
6563
7214
  const overlayNames = /* @__PURE__ */ new Set();
@@ -6604,13 +7255,13 @@ var Vault = class {
6604
7255
  if (!reg) {
6605
7256
  throw new Error(`refreshView: no MV registered with name "${name}"`);
6606
7257
  }
6607
- const { MaterializedViewExecutor } = await import("./executor-HSSRXDOB.js");
7258
+ const { MaterializedViewExecutor } = await import("./executor-3W63Y44O.js");
6608
7259
  const result = await MaterializedViewExecutor.refresh(reg, {
6609
7260
  getCollection: (n) => this.collection(n),
6610
7261
  getActiveTxContext: () => this.noydb._activeTxContextOrNull,
6611
7262
  getQueryContext: () => this
6612
7263
  });
6613
- const { clearMVStale } = await import("./stale-JH67FU57.js");
7264
+ const { clearMVStale } = await import("./stale-TOA36SRK.js");
6614
7265
  clearMVStale(registry, name);
6615
7266
  return result;
6616
7267
  }
@@ -6626,7 +7277,7 @@ var Vault = class {
6626
7277
  if (registry === null) return { derived: 0, failed: 0 };
6627
7278
  const strategies = registry.strategiesForSource(sourceCollection);
6628
7279
  if (strategies.length === 0) return { derived: 0, failed: 0 };
6629
- const { DerivationExecutor } = await import("./executor-6ZDSDZ6V.js");
7280
+ const { DerivationExecutor } = await import("./executor-CFFWPWBJ.js");
6630
7281
  const sourceColl = this.collection(sourceCollection);
6631
7282
  const records = await sourceColl.list();
6632
7283
  const ctx = { vault: this.readOnlyFacade ?? new (await import("./read-only-facade-ITU6L7BL.js")).ReadOnlyVaultFacade(this) };
@@ -6651,7 +7302,7 @@ var Vault = class {
6651
7302
  if (!outSpec) continue;
6652
7303
  const outputColl = this.collection(outSpec.collection);
6653
7304
  if (out.kind === "array") {
6654
- const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-N6OJX6QR.js");
7305
+ const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-FIJJ46YG.js");
6655
7306
  const prior = await loadFanoutSidecar(this.adapter, this.name, spec.source, id, key);
6656
7307
  const prevKeys = new Set(prior?.keys ?? []);
6657
7308
  const newKeysList = out.entries.map((e) => e.key);
@@ -6872,7 +7523,7 @@ var Vault = class {
6872
7523
  * collection.
6873
7524
  */
6874
7525
  async delegate(opts) {
6875
- const { issueDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-NIQ43IPU.js");
7526
+ const { issueDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-MGH5SODX.js");
6876
7527
  if (!this.keyring.kek) {
6877
7528
  throw new ValidationError(
6878
7529
  "issueDelegation: keyring.kek is null \u2014 issuing a delegation requires a tier-1 unlock. Re-authenticate at tier 1 (passphrase) first."
@@ -6894,7 +7545,7 @@ var Vault = class {
6894
7545
  * if the id does not exist.
6895
7546
  */
6896
7547
  async revokeDelegation(id) {
6897
- const { revokeDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-NIQ43IPU.js");
7548
+ const { revokeDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-MGH5SODX.js");
6898
7549
  await revokeDelegation(this.adapter, this.name, id);
6899
7550
  void DELEGATIONS_COLLECTION;
6900
7551
  }
@@ -7363,7 +8014,7 @@ var Vault = class {
7363
8014
  * @see docs/subsystems/public-envelope.md
7364
8015
  */
7365
8016
  async getPublicEnvelope(opts = {}) {
7366
- const { readPublicEnvelope: readPublicEnvelope2 } = await import("./public-envelope-SYHEYQ3X.js");
8017
+ const { readPublicEnvelope: readPublicEnvelope2 } = await import("./public-envelope-RXZNP3V6.js");
7367
8018
  return readPublicEnvelope2(this.adapter, this.name, opts);
7368
8019
  }
7369
8020
  /**
@@ -8561,7 +9212,7 @@ var NOT_ENABLED5 = new Error(
8561
9212
  'Multi-record transactions require the tx strategy. Import `{ withTransactions }` from "@noy-db/hub/tx" and pass it to `createNoydb({ txStrategy: withTransactions() })`.'
8562
9213
  );
8563
9214
  var NO_TX = {
8564
- async runTransaction() {
9215
+ async runTransaction(_db, _fn, _options, _txInvariants) {
8565
9216
  throw NOT_ENABLED5;
8566
9217
  },
8567
9218
  async runDryRun() {
@@ -8907,6 +9558,7 @@ var Noydb = class {
8907
9558
  policyEnforcers = /* @__PURE__ */ new Map();
8908
9559
  vaultTemplates = /* @__PURE__ */ new Map();
8909
9560
  txStrategy;
9561
+ forgetStrategy;
8910
9562
  sessionStrategy;
8911
9563
  syncStrategy;
8912
9564
  snapshotStrategy;
@@ -8934,6 +9586,7 @@ var Noydb = class {
8934
9586
  constructor(options) {
8935
9587
  this.options = options;
8936
9588
  this.txStrategy = options.txStrategy ?? NO_TX;
9589
+ this.forgetStrategy = options.forgetStrategy ?? NO_FORGET;
8937
9590
  this.sessionStrategy = options.sessionStrategy ?? NO_SESSION;
8938
9591
  this.syncStrategy = options.syncStrategy ?? NO_SYNC;
8939
9592
  this.snapshotStrategy = options.snapshotStrategy ?? NO_SNAPSHOTS;
@@ -8944,8 +9597,61 @@ var Noydb = class {
8944
9597
  }
8945
9598
  this.#registerGuardGate();
8946
9599
  this.#registerPeriodGate();
9600
+ this.#registerForgetHooks();
8947
9601
  this.resetSessionTimer();
8948
9602
  }
9603
+ /** @internal — resolved forget strategy (NO_FORGET when not configured). */
9604
+ get _forgetStrategy() {
9605
+ return this.forgetStrategy;
9606
+ }
9607
+ // #304 — GDPR subject-index maintenance. When `withForgetCascade` declares
9608
+ // any subject fields, keep the encrypted `_subject_index` in lock-step with
9609
+ // writes so `vault.forget(subjectId)` can find every record for a subject.
9610
+ //
9611
+ // Two consumers are required because they cover disjoint events:
9612
+ // - onAfterWrite fires on create/update (NOT delete) — add the new ref;
9613
+ // on an update that changed the subject value, drop the stale ref.
9614
+ // - the subsystemBus `afterDelete` observer fires on delete (onAfterWrite
9615
+ // does NOT) — drop the ref so a deleted record never lingers in the
9616
+ // index (RISK #2). Without it, forget() would try to shred a ghost.
9617
+ #registerForgetHooks() {
9618
+ const subjects = this.forgetStrategy.subjects;
9619
+ if (Object.keys(subjects).length === 0) return;
9620
+ const subjectFieldFor = (collection) => subjects[collection];
9621
+ this.writeHooks.onAfterWrite(async (event) => {
9622
+ const field = subjectFieldFor(event.collection);
9623
+ if (field === void 0) return;
9624
+ const vault = this.vaultCache.get(event.vault);
9625
+ if (!vault) return;
9626
+ if (event.after !== null && typeof event.after === "object") {
9627
+ const subjectValue = readDottedPath(event.after, field);
9628
+ if (subjectValue !== void 0 && subjectValue !== null) {
9629
+ await vault._addSubjectRef(coerceSubjectId(subjectValue), { collection: event.collection, id: event.docId });
9630
+ }
9631
+ }
9632
+ if (event.op === "update" && event.before !== null && typeof event.before === "object") {
9633
+ const beforeValue = readDottedPath(event.before, field);
9634
+ const afterValue = event.after !== null && typeof event.after === "object" ? readDottedPath(event.after, field) : void 0;
9635
+ const beforeId = beforeValue === void 0 || beforeValue === null ? void 0 : coerceSubjectId(beforeValue);
9636
+ const afterId = afterValue === void 0 || afterValue === null ? void 0 : coerceSubjectId(afterValue);
9637
+ if (beforeId !== void 0 && beforeId !== afterId) {
9638
+ await vault._removeSubjectRef(beforeId, { collection: event.collection, id: event.docId });
9639
+ }
9640
+ }
9641
+ });
9642
+ this.subsystemBus.register("afterDelete", async (event) => {
9643
+ const field = subjectFieldFor(event.collection);
9644
+ if (field === void 0) return;
9645
+ const vault = this.vaultCache.get(event.vault);
9646
+ if (!vault) return;
9647
+ if (event.before !== null && typeof event.before === "object") {
9648
+ const subjectValue = readDottedPath(event.before, field);
9649
+ if (subjectValue !== void 0 && subjectValue !== null) {
9650
+ await vault._removeSubjectRef(coerceSubjectId(subjectValue), { collection: event.collection, id: event.docId });
9651
+ }
9652
+ }
9653
+ });
9654
+ }
8949
9655
  // Track A — guards migration. Registers record-lock / field-freeze / onDelete
8950
9656
  // / amendment-collect as gate-bus handlers (only when guards are opted in, so
8951
9657
  // the write path is zero-cost otherwise). Resolves the live vault's
@@ -8973,7 +9679,7 @@ var Noydb = class {
8973
9679
  if (!facade) return;
8974
9680
  const ctx = { existing, vault: facade, userId: e.userId, role: e.role };
8975
9681
  await registry.runChecks(e.collection, incoming, ctx);
8976
- const { GuardExecutor } = await import("./executor-IDZDAFNH.js");
9682
+ const { GuardExecutor } = await import("./executor-VDQQOR4F.js");
8977
9683
  for (const g of guards) {
8978
9684
  await GuardExecutor.checkFrozenFields(g, e.docId, existing, incoming, e.computedFieldNames);
8979
9685
  }
@@ -9166,6 +9872,7 @@ var Noydb = class {
9166
9872
  ...this.options.syncStrategy !== void 0 ? { syncStrategy: this.options.syncStrategy } : {},
9167
9873
  ...this.options.guardStrategies !== void 0 ? { guardStrategies: this.options.guardStrategies } : {},
9168
9874
  ...this.options.numbering !== void 0 ? { numberingConfigs: this.options.numbering } : {},
9875
+ forgetStrategy: this.forgetStrategy,
9169
9876
  locale: opts?.locale,
9170
9877
  // Thread the translator hook so Collection.put() can invoke it
9171
9878
  plaintextTranslator: this.options.plaintextTranslator ? (text, from, to, field, collection) => this.invokeTranslator(text, from, to, field, collection) : void 0,
@@ -9220,7 +9927,8 @@ var Noydb = class {
9220
9927
  ...this.options.i18nStrategy !== void 0 ? { i18nStrategy: this.options.i18nStrategy } : {},
9221
9928
  ...this.options.syncStrategy !== void 0 ? { syncStrategy: this.options.syncStrategy } : {},
9222
9929
  ...this.options.guardStrategies !== void 0 ? { guardStrategies: this.options.guardStrategies } : {},
9223
- ...this.options.numbering !== void 0 ? { numberingConfigs: this.options.numbering } : {}
9930
+ ...this.options.numbering !== void 0 ? { numberingConfigs: this.options.numbering } : {},
9931
+ forgetStrategy: this.forgetStrategy
9224
9932
  });
9225
9933
  this.vaultCache.set(name, comp2);
9226
9934
  return comp2;
@@ -9572,8 +10280,8 @@ var Noydb = class {
9572
10280
  if (name === STATE_VAULT_NAME) throw new ReservedVaultNameError(name);
9573
10281
  const template = this.vaultTemplates.get(opts.sharding.vaultTemplate);
9574
10282
  if (!template) throw new VaultTemplateNotFoundError(opts.sharding.vaultTemplate);
9575
- const { VaultGroup } = await import("./vault-group-KOM7QRJG.js");
9576
- const { StateManagementVault } = await import("./state-vault-TMXZRTY5.js");
10283
+ const { VaultGroup } = await import("./vault-group-DHAHFX2A.js");
10284
+ const { StateManagementVault } = await import("./state-vault-W2OEABNO.js");
9577
10285
  const stateVault = opts.registry ? void 0 : await StateManagementVault.open(this);
9578
10286
  const registry = opts.registry ?? stateVault.registry;
9579
10287
  const group = new VaultGroup(this, name, registry, opts.sharding, template);
@@ -9600,7 +10308,7 @@ var Noydb = class {
9600
10308
  */
9601
10309
  async openStateManagementVault() {
9602
10310
  if (this.closed) throw new ValidationError("Instance is closed");
9603
- const { StateManagementVault } = await import("./state-vault-TMXZRTY5.js");
10311
+ const { StateManagementVault } = await import("./state-vault-W2OEABNO.js");
9604
10312
  return StateManagementVault.open(this);
9605
10313
  }
9606
10314
  /**
@@ -11102,6 +11810,7 @@ function normalizeSyncTargets(sync) {
11102
11810
 
11103
11811
  export {
11104
11812
  withArchive,
11813
+ resolveSequenceKey,
11105
11814
  SequenceStore,
11106
11815
  validateSchemaInput,
11107
11816
  validateSchemaOutput,
@@ -11140,4 +11849,4 @@ export {
11140
11849
  Noydb,
11141
11850
  createNoydb
11142
11851
  };
11143
- //# sourceMappingURL=chunk-7EFFHEN5.js.map
11852
+ //# sourceMappingURL=chunk-D77ZQSQQ.js.map