@noy-db/hub 0.2.0-pre.16 → 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 (286) hide show
  1. package/dist/aggregate/index.cjs.map +1 -1
  2. package/dist/aggregate/index.d.cts +3 -2
  3. package/dist/aggregate/index.d.ts +3 -2
  4. package/dist/aggregate/index.js +4 -4
  5. package/dist/attestation/index.cjs.map +1 -1
  6. package/dist/attestation/index.d.cts +5 -3
  7. package/dist/attestation/index.d.ts +5 -3
  8. package/dist/attestation/index.js +6 -6
  9. package/dist/blobs/index.cjs +226 -11
  10. package/dist/blobs/index.cjs.map +1 -1
  11. package/dist/blobs/index.d.cts +6 -4
  12. package/dist/blobs/index.d.ts +6 -4
  13. package/dist/blobs/index.js +6 -5
  14. package/dist/blobs/index.js.map +1 -1
  15. package/dist/bundle/index.cjs +1087 -95
  16. package/dist/bundle/index.cjs.map +1 -1
  17. package/dist/bundle/index.d.cts +7 -5
  18. package/dist/bundle/index.d.ts +7 -5
  19. package/dist/bundle/index.js +21 -10
  20. package/dist/bundle/index.js.map +1 -1
  21. package/dist/{chunk-G4SCICH5.js → chunk-2FU2FTXD.js} +2 -2
  22. package/dist/{chunk-JD3OZAI4.js → chunk-3G3W65EQ.js} +2 -2
  23. package/dist/{chunk-XWH4MXIU.js → chunk-5AXTH4QZ.js} +2 -2
  24. package/dist/{chunk-4TBBMHVC.js → chunk-5LIROIDM.js} +2 -2
  25. package/dist/{chunk-L2BNJ6HM.js → chunk-7H2GEJ3O.js} +3 -3
  26. package/dist/{chunk-GNI5STXQ.js → chunk-AEIKD3PP.js} +52 -38
  27. package/dist/chunk-AEIKD3PP.js.map +1 -0
  28. package/dist/{chunk-QSUK7YWK.js → chunk-BH3X5L6A.js} +4 -4
  29. package/dist/{chunk-BQ65SS5A.js → chunk-BJSLBUJ7.js} +2 -2
  30. package/dist/{chunk-FFXM3ZIF.js → chunk-BL5GYANC.js} +3 -3
  31. package/dist/{chunk-6H2ZUNR7.js → chunk-BSZOCSDZ.js} +4 -4
  32. package/dist/{chunk-ZNQYHJXX.js → chunk-C3HYQPV4.js} +2 -2
  33. package/dist/{chunk-E2CDVKMH.js → chunk-CD2AVTEM.js} +5 -5
  34. package/dist/{chunk-667MB6AH.js → chunk-D77ZQSQQ.js} +769 -131
  35. package/dist/chunk-D77ZQSQQ.js.map +1 -0
  36. package/dist/{chunk-BR3AMFGS.js → chunk-DWEBTE2W.js} +5 -5
  37. package/dist/{chunk-Z4DO7YSI.js → chunk-DYYYUW5D.js} +2 -2
  38. package/dist/{chunk-SCJPI4Z5.js → chunk-E77UKJYL.js} +5 -5
  39. package/dist/{chunk-OMAMZKKD.js → chunk-F4G63NTZ.js} +2 -2
  40. package/dist/{chunk-TKIY625R.js → chunk-FEJDVE3Z.js} +2 -2
  41. package/dist/{chunk-7Z7KSVA5.js → chunk-GP3SDSH2.js} +2 -2
  42. package/dist/{chunk-IQLVUT37.js → chunk-H2MRGONI.js} +2 -2
  43. package/dist/{chunk-DUREQF5W.js → chunk-HGVSHKZW.js} +8 -5
  44. package/dist/chunk-HGVSHKZW.js.map +1 -0
  45. package/dist/chunk-I5IUYN7B.js +125 -0
  46. package/dist/chunk-I5IUYN7B.js.map +1 -0
  47. package/dist/{chunk-CJORTUJ2.js → chunk-J7RWBXFY.js} +2 -2
  48. package/dist/{chunk-AAVWKNZW.js → chunk-JDWE6JMX.js} +2 -2
  49. package/dist/{chunk-XL35NSEN.js → chunk-KCEHMDZF.js} +3 -3
  50. package/dist/{chunk-TS26M2SB.js → chunk-M476FOQ7.js} +2 -2
  51. package/dist/{chunk-F4OJZIWQ.js → chunk-NBBMMJ2H.js} +4 -4
  52. package/dist/{chunk-CZI2A4MQ.js → chunk-NYSYPFXJ.js} +3 -3
  53. package/dist/{chunk-OQSRJG6A.js → chunk-PDULVIBY.js} +2 -2
  54. package/dist/{chunk-Z6FNBOTC.js → chunk-PDVP3C2I.js} +1 -1
  55. package/dist/{chunk-Z6FNBOTC.js.map → chunk-PDVP3C2I.js.map} +1 -1
  56. package/dist/{chunk-DLZ2ONOD.js → chunk-QHM6XEAH.js} +6 -6
  57. package/dist/{chunk-HBXJ37ZY.js → chunk-QO6RGLLD.js} +4 -4
  58. package/dist/{chunk-7BQ4QWYX.js → chunk-ROPJVUG3.js} +23 -6
  59. package/dist/chunk-ROPJVUG3.js.map +1 -0
  60. package/dist/{chunk-42FEUPZQ.js → chunk-ROVO6NPJ.js} +2 -2
  61. package/dist/{chunk-6RR3MNMG.js → chunk-SHX5QBCI.js} +3 -3
  62. package/dist/{chunk-F3BPIPLS.js → chunk-SISBMAPO.js} +1 -1
  63. package/dist/chunk-SISBMAPO.js.map +1 -0
  64. package/dist/{chunk-3YWP3WBP.js → chunk-SNMJ7SB3.js} +5 -5
  65. package/dist/{chunk-IXBIFDEW.js → chunk-TIDXB5DF.js} +4 -4
  66. package/dist/chunk-U5QCMH3W.js +151 -0
  67. package/dist/chunk-U5QCMH3W.js.map +1 -0
  68. package/dist/{chunk-YULZKK4F.js → chunk-UNTGHX5A.js} +37 -2
  69. package/dist/chunk-UNTGHX5A.js.map +1 -0
  70. package/dist/{chunk-FWPKCXTN.js → chunk-WIAOUFFB.js} +2 -2
  71. package/dist/{chunk-KABJXG2F.js → chunk-WV7WV6JO.js} +195 -17
  72. package/dist/chunk-WV7WV6JO.js.map +1 -0
  73. package/dist/{chunk-X73VS74Y.js → chunk-XJV6OB4D.js} +2 -2
  74. package/dist/{chunk-VLMPU56Q.js → chunk-XMHUK5PN.js} +2 -2
  75. package/dist/{chunk-BI6ETQPF.js → chunk-XMVHEWF6.js} +4 -4
  76. package/dist/{chunk-HOR4R722.js → chunk-XPIHJ34I.js} +30 -4
  77. package/dist/chunk-XPIHJ34I.js.map +1 -0
  78. package/dist/{chunk-OB2ZJQ2D.js → chunk-YYVZYTWW.js} +3 -3
  79. package/dist/{chunk-535SSHBS.js → chunk-ZEGSDPB7.js} +81 -2
  80. package/dist/chunk-ZEGSDPB7.js.map +1 -0
  81. package/dist/{chunk-QVIEAYTP.js → chunk-ZNGPEV5J.js} +3 -3
  82. package/dist/consent/index.cjs.map +1 -1
  83. package/dist/consent/index.d.cts +6 -4
  84. package/dist/consent/index.d.ts +6 -4
  85. package/dist/consent/index.js +3 -3
  86. package/dist/{crypto-QXQOHMHF.js → crypto-7BN2HDWG.js} +7 -3
  87. package/dist/{delegation-NIQ43IPU.js → delegation-MGH5SODX.js} +5 -5
  88. package/dist/derivations/index.cjs.map +1 -1
  89. package/dist/derivations/index.d.cts +7 -5
  90. package/dist/derivations/index.d.ts +7 -5
  91. package/dist/derivations/index.js +4 -4
  92. package/dist/{dev-unlock-DR3upLd1.d.ts → dev-unlock-CI1ijTML.d.ts} +1 -1
  93. package/dist/{dev-unlock-8XzcD2Z4.d.cts → dev-unlock-iXbYFAWl.d.cts} +1 -1
  94. package/dist/{strategy-BtW8fAjz.d.ts → errors-Dz64FA65.d.cts} +98 -727
  95. package/dist/{strategy-BtW8fAjz.d.cts → errors-Dz64FA65.d.ts} +98 -727
  96. package/dist/executor-3W63Y44O.js +11 -0
  97. package/dist/executor-CFFWPWBJ.js +8 -0
  98. package/dist/executor-VDQQOR4F.js +8 -0
  99. package/dist/{fanout-sidecar-67CMI3UT.js → fanout-sidecar-FIJJ46YG.js} +2 -2
  100. package/dist/forget/index.cjs +43 -0
  101. package/dist/forget/index.cjs.map +1 -0
  102. package/dist/forget/index.d.cts +1 -0
  103. package/dist/forget/index.d.ts +1 -0
  104. package/dist/forget/index.js +14 -0
  105. package/dist/guards/index.cjs.map +1 -1
  106. package/dist/guards/index.d.cts +7 -5
  107. package/dist/guards/index.d.ts +7 -5
  108. package/dist/guards/index.js +6 -6
  109. package/dist/{hash-CDjye9KV.d.ts → hash-blk7Bkes.d.ts} +1 -1
  110. package/dist/{hash-DuQ88_5W.d.cts → hash-tEcM5fnv.d.cts} +1 -1
  111. package/dist/history/index.cjs +27 -4
  112. package/dist/history/index.cjs.map +1 -1
  113. package/dist/history/index.d.cts +7 -5
  114. package/dist/history/index.d.ts +7 -5
  115. package/dist/history/index.js +9 -7
  116. package/dist/history/index.js.map +1 -1
  117. package/dist/i18n/index.cjs +53 -0
  118. package/dist/i18n/index.cjs.map +1 -1
  119. package/dist/i18n/index.d.cts +6 -4
  120. package/dist/i18n/index.d.ts +6 -4
  121. package/dist/i18n/index.js +16 -8
  122. package/dist/i18n/index.js.map +1 -1
  123. package/dist/{immutable-guard-Dov3WvwF.d.ts → immutable-guard-B5M95nbq.d.ts} +1 -1
  124. package/dist/{immutable-guard-CRPvu24K.d.cts → immutable-guard-qN3zF8o1.d.cts} +1 -1
  125. package/dist/index-C-SSRIxP.d.cts +348 -0
  126. package/dist/index-C-SSRIxP.d.ts +348 -0
  127. package/dist/{index-nP99bXLg.d.ts → index-DpU6KWof.d.ts} +9 -1
  128. package/dist/{index-C8Bk3-VF.d.cts → index-u-kWzSrL.d.cts} +9 -1
  129. package/dist/index.cjs +7271 -6079
  130. package/dist/index.cjs.map +1 -1
  131. package/dist/index.d.cts +15 -12
  132. package/dist/index.d.ts +15 -12
  133. package/dist/index.js +130 -106
  134. package/dist/index.js.map +1 -1
  135. package/dist/indexing/index.cjs.map +1 -1
  136. package/dist/indexing/index.js +4 -4
  137. package/dist/issue-TTMGHQ2J.js +12 -0
  138. package/dist/{ledger-A3LL253R.js → ledger-LFVLHE5H.js} +6 -6
  139. package/dist/materialized-views/index.cjs.map +1 -1
  140. package/dist/materialized-views/index.d.cts +7 -5
  141. package/dist/materialized-views/index.d.ts +7 -5
  142. package/dist/materialized-views/index.js +12 -12
  143. package/dist/noydb-36S6GQNC.js +37 -0
  144. package/dist/overlay-views/index.cjs.map +1 -1
  145. package/dist/overlay-views/index.d.cts +7 -5
  146. package/dist/overlay-views/index.d.ts +7 -5
  147. package/dist/overlay-views/index.js +4 -4
  148. package/dist/periods/index.cjs.map +1 -1
  149. package/dist/periods/index.d.cts +6 -4
  150. package/dist/periods/index.d.ts +6 -4
  151. package/dist/periods/index.js +6 -6
  152. package/dist/{public-envelope-YP2UWMLG.js → public-envelope-RXZNP3V6.js} +4 -4
  153. package/dist/query/index.cjs +4 -1
  154. package/dist/query/index.cjs.map +1 -1
  155. package/dist/query/index.d.cts +3 -2
  156. package/dist/query/index.d.ts +3 -2
  157. package/dist/query/index.js +6 -6
  158. package/dist/registry-3YFLZ7WD.js +8 -0
  159. package/dist/{registry-UTA4CLQS.js → registry-SECUWSGY.js} +3 -3
  160. package/dist/registry-TGZISEWC.js +8 -0
  161. package/dist/{revoke-HNMQZSCL.js → revoke-B54H2S2W.js} +6 -6
  162. package/dist/sealed-record/index.cjs +139 -0
  163. package/dist/sealed-record/index.cjs.map +1 -0
  164. package/dist/sealed-record/index.d.cts +123 -0
  165. package/dist/sealed-record/index.d.ts +123 -0
  166. package/dist/sealed-record/index.js +42 -0
  167. package/dist/sealed-record/index.js.map +1 -0
  168. package/dist/session/index.cjs.map +1 -1
  169. package/dist/session/index.d.cts +7 -5
  170. package/dist/session/index.d.ts +7 -5
  171. package/dist/session/index.js +3 -3
  172. package/dist/shadow/index.cjs.map +1 -1
  173. package/dist/shadow/index.d.cts +6 -4
  174. package/dist/shadow/index.d.ts +6 -4
  175. package/dist/shadow/index.js +2 -2
  176. package/dist/{signer-DCMNKXSF.js → signer-YSXZT574.js} +5 -5
  177. package/dist/snapshots/index.cjs.map +1 -1
  178. package/dist/snapshots/index.d.cts +6 -4
  179. package/dist/snapshots/index.d.ts +6 -4
  180. package/dist/snapshots/index.js +4 -4
  181. package/dist/{stale-W5PQTRYH.js → stale-TOA36SRK.js} +2 -2
  182. package/dist/stale-TOA36SRK.js.map +1 -0
  183. package/dist/{state-vault-TMXZRTY5.js → state-vault-W2OEABNO.js} +3 -3
  184. package/dist/store/index.cjs.map +1 -1
  185. package/dist/store/index.d.cts +6 -4
  186. package/dist/store/index.d.ts +6 -4
  187. package/dist/store/index.js +2 -2
  188. package/dist/strategy-4M9jo172.d.ts +739 -0
  189. package/dist/strategy-CLC1j79g.d.cts +739 -0
  190. package/dist/sync/index.cjs.map +1 -1
  191. package/dist/sync/index.d.cts +5 -3
  192. package/dist/sync/index.d.ts +5 -3
  193. package/dist/sync/index.js +4 -4
  194. package/dist/team/index.cjs.map +1 -1
  195. package/dist/team/index.d.cts +6 -4
  196. package/dist/team/index.d.ts +6 -4
  197. package/dist/team/index.js +8 -8
  198. package/dist/tx/index.cjs.map +1 -1
  199. package/dist/tx/index.d.cts +6 -4
  200. package/dist/tx/index.d.ts +6 -4
  201. package/dist/tx/index.js +3 -3
  202. package/dist/{types-DrmBTscX.d.ts → types-CljIHm_J.d.ts} +789 -500
  203. package/dist/{types-Bze6vkwm.d.cts → types-CrSpRDuG.d.cts} +789 -500
  204. package/dist/{ulid-DbBVrNSt.d.ts → ulid-CWfL2Vfv.d.ts} +1 -1
  205. package/dist/{ulid-DfZlAh0u.d.cts → ulid-CrI7PPbA.d.cts} +1 -1
  206. package/dist/util/index.cjs.map +1 -1
  207. package/dist/util/index.js +1 -1
  208. package/dist/{vault-group-DX2HFQMX.js → vault-group-DHAHFX2A.js} +4 -4
  209. package/dist/{with-derivation-_lySGdlm.d.ts → with-derivation-BZ2y4bzF.d.ts} +1 -1
  210. package/dist/{with-derivation-CCqAchD5.d.cts → with-derivation-Bozs8DmD.d.cts} +1 -1
  211. package/dist/{with-materialized-view-QT1Tp7NO.d.ts → with-materialized-view-B892zYZV.d.ts} +1 -1
  212. package/dist/{with-materialized-view--4PsvMDu.d.cts → with-materialized-view-NzF71cG_.d.cts} +1 -1
  213. package/dist/{with-overlayed-view-BEXfpzSb.d.ts → with-overlayed-view-CR6m7CHe.d.ts} +1 -1
  214. package/dist/{with-overlayed-view-DlH5qmeB.d.cts → with-overlayed-view-UI8qSGL4.d.cts} +1 -1
  215. package/package.json +23 -3
  216. package/dist/chunk-535SSHBS.js.map +0 -1
  217. package/dist/chunk-667MB6AH.js.map +0 -1
  218. package/dist/chunk-7BQ4QWYX.js.map +0 -1
  219. package/dist/chunk-DUREQF5W.js.map +0 -1
  220. package/dist/chunk-F3BPIPLS.js.map +0 -1
  221. package/dist/chunk-GNI5STXQ.js.map +0 -1
  222. package/dist/chunk-HOR4R722.js.map +0 -1
  223. package/dist/chunk-KABJXG2F.js.map +0 -1
  224. package/dist/chunk-YULZKK4F.js.map +0 -1
  225. package/dist/executor-6ZDSDZ6V.js +0 -8
  226. package/dist/executor-AZLS3KBK.js +0 -11
  227. package/dist/executor-IDZDAFNH.js +0 -8
  228. package/dist/issue-RZP3VI6O.js +0 -12
  229. package/dist/noydb-WCMY2ZOW.js +0 -35
  230. package/dist/registry-EB6SISTA.js +0 -8
  231. package/dist/registry-IUZQVVBB.js +0 -8
  232. /package/dist/{chunk-G4SCICH5.js.map → chunk-2FU2FTXD.js.map} +0 -0
  233. /package/dist/{chunk-JD3OZAI4.js.map → chunk-3G3W65EQ.js.map} +0 -0
  234. /package/dist/{chunk-XWH4MXIU.js.map → chunk-5AXTH4QZ.js.map} +0 -0
  235. /package/dist/{chunk-4TBBMHVC.js.map → chunk-5LIROIDM.js.map} +0 -0
  236. /package/dist/{chunk-L2BNJ6HM.js.map → chunk-7H2GEJ3O.js.map} +0 -0
  237. /package/dist/{chunk-QSUK7YWK.js.map → chunk-BH3X5L6A.js.map} +0 -0
  238. /package/dist/{chunk-BQ65SS5A.js.map → chunk-BJSLBUJ7.js.map} +0 -0
  239. /package/dist/{chunk-FFXM3ZIF.js.map → chunk-BL5GYANC.js.map} +0 -0
  240. /package/dist/{chunk-6H2ZUNR7.js.map → chunk-BSZOCSDZ.js.map} +0 -0
  241. /package/dist/{chunk-ZNQYHJXX.js.map → chunk-C3HYQPV4.js.map} +0 -0
  242. /package/dist/{chunk-E2CDVKMH.js.map → chunk-CD2AVTEM.js.map} +0 -0
  243. /package/dist/{chunk-BR3AMFGS.js.map → chunk-DWEBTE2W.js.map} +0 -0
  244. /package/dist/{chunk-Z4DO7YSI.js.map → chunk-DYYYUW5D.js.map} +0 -0
  245. /package/dist/{chunk-SCJPI4Z5.js.map → chunk-E77UKJYL.js.map} +0 -0
  246. /package/dist/{chunk-OMAMZKKD.js.map → chunk-F4G63NTZ.js.map} +0 -0
  247. /package/dist/{chunk-TKIY625R.js.map → chunk-FEJDVE3Z.js.map} +0 -0
  248. /package/dist/{chunk-7Z7KSVA5.js.map → chunk-GP3SDSH2.js.map} +0 -0
  249. /package/dist/{chunk-IQLVUT37.js.map → chunk-H2MRGONI.js.map} +0 -0
  250. /package/dist/{chunk-CJORTUJ2.js.map → chunk-J7RWBXFY.js.map} +0 -0
  251. /package/dist/{chunk-AAVWKNZW.js.map → chunk-JDWE6JMX.js.map} +0 -0
  252. /package/dist/{chunk-XL35NSEN.js.map → chunk-KCEHMDZF.js.map} +0 -0
  253. /package/dist/{chunk-TS26M2SB.js.map → chunk-M476FOQ7.js.map} +0 -0
  254. /package/dist/{chunk-F4OJZIWQ.js.map → chunk-NBBMMJ2H.js.map} +0 -0
  255. /package/dist/{chunk-CZI2A4MQ.js.map → chunk-NYSYPFXJ.js.map} +0 -0
  256. /package/dist/{chunk-OQSRJG6A.js.map → chunk-PDULVIBY.js.map} +0 -0
  257. /package/dist/{chunk-DLZ2ONOD.js.map → chunk-QHM6XEAH.js.map} +0 -0
  258. /package/dist/{chunk-HBXJ37ZY.js.map → chunk-QO6RGLLD.js.map} +0 -0
  259. /package/dist/{chunk-42FEUPZQ.js.map → chunk-ROVO6NPJ.js.map} +0 -0
  260. /package/dist/{chunk-6RR3MNMG.js.map → chunk-SHX5QBCI.js.map} +0 -0
  261. /package/dist/{chunk-3YWP3WBP.js.map → chunk-SNMJ7SB3.js.map} +0 -0
  262. /package/dist/{chunk-IXBIFDEW.js.map → chunk-TIDXB5DF.js.map} +0 -0
  263. /package/dist/{chunk-FWPKCXTN.js.map → chunk-WIAOUFFB.js.map} +0 -0
  264. /package/dist/{chunk-X73VS74Y.js.map → chunk-XJV6OB4D.js.map} +0 -0
  265. /package/dist/{chunk-VLMPU56Q.js.map → chunk-XMHUK5PN.js.map} +0 -0
  266. /package/dist/{chunk-BI6ETQPF.js.map → chunk-XMVHEWF6.js.map} +0 -0
  267. /package/dist/{chunk-OB2ZJQ2D.js.map → chunk-YYVZYTWW.js.map} +0 -0
  268. /package/dist/{chunk-QVIEAYTP.js.map → chunk-ZNGPEV5J.js.map} +0 -0
  269. /package/dist/{crypto-QXQOHMHF.js.map → crypto-7BN2HDWG.js.map} +0 -0
  270. /package/dist/{delegation-NIQ43IPU.js.map → delegation-MGH5SODX.js.map} +0 -0
  271. /package/dist/{executor-6ZDSDZ6V.js.map → executor-3W63Y44O.js.map} +0 -0
  272. /package/dist/{executor-AZLS3KBK.js.map → executor-CFFWPWBJ.js.map} +0 -0
  273. /package/dist/{executor-IDZDAFNH.js.map → executor-VDQQOR4F.js.map} +0 -0
  274. /package/dist/{fanout-sidecar-67CMI3UT.js.map → fanout-sidecar-FIJJ46YG.js.map} +0 -0
  275. /package/dist/{issue-RZP3VI6O.js.map → forget/index.js.map} +0 -0
  276. /package/dist/{ledger-A3LL253R.js.map → issue-TTMGHQ2J.js.map} +0 -0
  277. /package/dist/{noydb-WCMY2ZOW.js.map → ledger-LFVLHE5H.js.map} +0 -0
  278. /package/dist/{public-envelope-YP2UWMLG.js.map → noydb-36S6GQNC.js.map} +0 -0
  279. /package/dist/{registry-EB6SISTA.js.map → public-envelope-RXZNP3V6.js.map} +0 -0
  280. /package/dist/{registry-IUZQVVBB.js.map → registry-3YFLZ7WD.js.map} +0 -0
  281. /package/dist/{registry-UTA4CLQS.js.map → registry-SECUWSGY.js.map} +0 -0
  282. /package/dist/{revoke-HNMQZSCL.js.map → registry-TGZISEWC.js.map} +0 -0
  283. /package/dist/{signer-DCMNKXSF.js.map → revoke-B54H2S2W.js.map} +0 -0
  284. /package/dist/{stale-W5PQTRYH.js.map → signer-YSXZT574.js.map} +0 -0
  285. /package/dist/{state-vault-TMXZRTY5.js.map → state-vault-W2OEABNO.js.map} +0 -0
  286. /package/dist/{vault-group-DX2HFQMX.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-QVIEAYTP.js";
10
+ } from "./chunk-ZNGPEV5J.js";
11
11
  import {
12
12
  OverlayedCollection
13
- } from "./chunk-VLMPU56Q.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-GNI5STXQ.js";
46
+ } from "./chunk-AEIKD3PP.js";
26
47
  import {
27
48
  loadPublicEnvelope,
28
49
  readPublicEnvelope,
29
50
  savePublicEnvelope,
30
51
  validatePublicEnvelopeInput
31
- } from "./chunk-OB2ZJQ2D.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-QSUK7YWK.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-7BQ4QWYX.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-DLZ2ONOD.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-6H2ZUNR7.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-BR3AMFGS.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-DUREQF5W.js";
115
- import {
116
- canonicalGroupKey
117
- } from "./chunk-L2BNJ6HM.js";
135
+ } from "./chunk-PDVP3C2I.js";
118
136
  import {
119
- readPath
120
- } from "./chunk-CJORTUJ2.js";
121
- import {
122
- EXPORT_AUDIT_COLLECTION,
123
- createExportBlobsHandle,
124
- runCompaction
125
- } from "./chunk-XL35NSEN.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-F3BPIPLS.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-W5PQTRYH.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-AZLS3KBK.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-W5PQTRYH.js");
1795
+ staleHelpers = await import("./stale-TOA36SRK.js");
1730
1796
  }
1731
1797
  staleHelpers.markMVStale(registry, reg.spec.name);
1732
1798
  }
@@ -1755,7 +1821,7 @@ 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"));
1759
1825
  }
1760
1826
  let sourceWithId;
1761
1827
  let sourceVersion = version;
@@ -1783,7 +1849,7 @@ var Collection = class {
1783
1849
  const outputCollection = this.derivationSource.getCollection(outSpec.collection);
1784
1850
  const txCtx = this.derivationSource.getActiveTxContext();
1785
1851
  if (out.kind === "array") {
1786
- const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-67CMI3UT.js");
1852
+ const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-FIJJ46YG.js");
1787
1853
  const prior = await loadFanoutSidecar(
1788
1854
  this.adapter,
1789
1855
  this.vault,
@@ -1890,11 +1956,14 @@ var Collection = class {
1890
1956
  let count = 0;
1891
1957
  for (const id of ids) {
1892
1958
  const env = await this.adapter.get(this.vault, this.name, id);
1893
- if (!env) continue;
1894
- 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;
1895
1963
  const next = transform(record);
1896
1964
  const nextVersion = (env._v ?? 0) + 1;
1897
- 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);
1898
1967
  await this.adapter.put(this.vault, this.name, id, newEnv);
1899
1968
  await this._invalidateCacheEntry(id);
1900
1969
  if (this.ledger) {
@@ -2006,14 +2075,17 @@ var Collection = class {
2006
2075
  const previousEnvelope2 = await this.adapter.get(this.vault, this.name, id);
2007
2076
  if (previousEnvelope2) {
2008
2077
  const previousRecord = await this.decryptRecord(previousEnvelope2);
2009
- existing = { record: previousRecord, version: previousEnvelope2._v };
2078
+ if (previousRecord !== null) {
2079
+ existing = { record: previousRecord, version: previousEnvelope2._v };
2080
+ }
2010
2081
  }
2011
2082
  }
2012
2083
  } else {
2013
2084
  existing = this.cache.get(id);
2014
2085
  }
2015
2086
  if (existing && this.historyConfig.enabled !== false) {
2016
- 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);
2017
2089
  await this.historyStrategy.saveHistory(this.adapter, this.vault, this.name, id, historyEnvelope);
2018
2090
  }
2019
2091
  const previousEnvelope = await this.adapter.get(this.vault, this.name, id);
@@ -2054,6 +2126,50 @@ var Collection = class {
2054
2126
  await this.dispatchArrayDerivationsOnDelete(id);
2055
2127
  }
2056
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
+ }
2057
2173
  /**
2058
2174
  * Cascade deletes of array-shape derived rows when a source row is
2059
2175
  * deleted. Reads each registered strategy's fanout sidecar
@@ -2076,7 +2192,7 @@ var Collection = class {
2076
2192
  for (const [outputKey, outSpec] of Object.entries(spec.outputs)) {
2077
2193
  if (outSpec.shape !== "array") continue;
2078
2194
  if (helpers === null) {
2079
- helpers = await import("./fanout-sidecar-67CMI3UT.js");
2195
+ helpers = await import("./fanout-sidecar-FIJJ46YG.js");
2080
2196
  }
2081
2197
  const sidecar = await helpers.loadFanoutSidecar(
2082
2198
  this.adapter,
@@ -2116,7 +2232,7 @@ var Collection = class {
2116
2232
  if (mode === "eager") {
2117
2233
  if (executor === null) {
2118
2234
  ;
2119
- ({ MaterializedViewExecutor: executor } = await import("./executor-AZLS3KBK.js"));
2235
+ ({ MaterializedViewExecutor: executor } = await import("./executor-3W63Y44O.js"));
2120
2236
  }
2121
2237
  await executor.refresh(reg, {
2122
2238
  getCollection: (name) => this.materializedViewSource.getCollection(name),
@@ -2125,7 +2241,7 @@ var Collection = class {
2125
2241
  });
2126
2242
  } else if (mode === "lazy") {
2127
2243
  if (staleHelpers === null) {
2128
- staleHelpers = await import("./stale-W5PQTRYH.js");
2244
+ staleHelpers = await import("./stale-TOA36SRK.js");
2129
2245
  }
2130
2246
  staleHelpers.markMVStale(registry, reg.spec.name);
2131
2247
  }
@@ -2148,7 +2264,7 @@ var Collection = class {
2148
2264
  );
2149
2265
  }
2150
2266
  if (this.materializedViewSource !== void 0) {
2151
- const { resolveStaleMVOnRead } = await import("./stale-W5PQTRYH.js");
2267
+ const { resolveStaleMVOnRead } = await import("./stale-TOA36SRK.js");
2152
2268
  await resolveStaleMVOnRead(this.materializedViewSource, this.name);
2153
2269
  }
2154
2270
  await this.ensureHydrated();
@@ -2466,6 +2582,7 @@ var Collection = class {
2466
2582
  const entries = [];
2467
2583
  for (const env of envelopes) {
2468
2584
  const record = await this.decryptRecord(env, { skipValidation: true });
2585
+ if (record === null) continue;
2469
2586
  entries.push({
2470
2587
  version: env._v,
2471
2588
  timestamp: env._ts,
@@ -2602,6 +2719,7 @@ var Collection = class {
2602
2719
  const envelope = await this.adapter.get(this.vault, this.name, id);
2603
2720
  if (envelope) {
2604
2721
  const record = await this.decryptRecord(envelope);
2722
+ if (record === null) continue;
2605
2723
  items.push(record);
2606
2724
  if (!this.lazy && !this.cache.has(id)) {
2607
2725
  this.cache.set(id, { record, version: envelope._v });
@@ -2678,6 +2796,7 @@ var Collection = class {
2678
2796
  const out = [];
2679
2797
  for (const { id, envelope } of items) {
2680
2798
  const record = await this.decryptRecord(envelope);
2799
+ if (record === null) continue;
2681
2800
  out.push({ id, record, version: envelope._v });
2682
2801
  }
2683
2802
  return out;
@@ -2699,6 +2818,18 @@ var Collection = class {
2699
2818
  * the cache entry (record still present) or deletes it (record was
2700
2819
  * gone before the tx and the revert deleted it again).
2701
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
+ }
2702
2833
  async _invalidateCacheEntry(id) {
2703
2834
  if (this.lazy && this.lru) {
2704
2835
  this.lru.remove(id);
@@ -2716,6 +2847,14 @@ var Collection = class {
2716
2847
  return;
2717
2848
  }
2718
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
+ }
2719
2858
  this.cache.set(id, { record, version: envelope._v });
2720
2859
  this.indexes?.upsert(id, record, previous ? previous.record : null);
2721
2860
  this.uniqueConstraints?.upsert(id, record, previous?.record);
@@ -2740,8 +2879,9 @@ var Collection = class {
2740
2879
  const ids = await this.adapter.list(this.vault, this.name);
2741
2880
  for (const id of ids) {
2742
2881
  const envelope = await this.adapter.get(this.vault, this.name, id);
2743
- if (envelope) {
2744
- 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;
2745
2885
  this.cache.set(id, { record, version: envelope._v });
2746
2886
  }
2747
2887
  }
@@ -2752,7 +2892,9 @@ var Collection = class {
2752
2892
  /** Hydrate from a pre-loaded snapshot (used by Vault). */
2753
2893
  async hydrateFromSnapshot(records) {
2754
2894
  for (const [id, envelope] of Object.entries(records)) {
2755
- 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;
2756
2898
  this.cache.set(id, { record, version: envelope._v });
2757
2899
  }
2758
2900
  this.hydrated = true;
@@ -2840,6 +2982,7 @@ var Collection = class {
2840
2982
  const envelope = await this.adapter.get(this.vault, this.name, recordId);
2841
2983
  if (!envelope) continue;
2842
2984
  const record = await this.decryptRecord(envelope, { skipValidation: true });
2985
+ if (record === null) continue;
2843
2986
  await this.maintainPersistedIndexesOnPut(recordId, record, null, envelope._v);
2844
2987
  }
2845
2988
  this.persistedIndexesLoaded = true;
@@ -2890,8 +3033,13 @@ var Collection = class {
2890
3033
  const env = await this.adapter.get(this.vault, this.name, id);
2891
3034
  if (!env) continue;
2892
3035
  try {
2893
- const body = JSON.parse(await this.decryptJsonString(env));
2894
- 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
+ }
2895
3043
  } catch {
2896
3044
  sidecar.set(decoded.recordId, void 0);
2897
3045
  }
@@ -2905,6 +3053,7 @@ var Collection = class {
2905
3053
  const env = await this.adapter.get(this.vault, this.name, id);
2906
3054
  if (!env) continue;
2907
3055
  const record = await this.decryptRecord(env, { skipValidation: true });
3056
+ if (record === null) continue;
2908
3057
  const live = readPersistedValue(record, field);
2909
3058
  const stored = sidecar.get(id);
2910
3059
  const hasSidecar = sidecarIds.has(id);
@@ -2987,7 +3136,8 @@ var Collection = class {
2987
3136
  recordId: id,
2988
3137
  getDEK: this.getDEK,
2989
3138
  encrypted: this.encrypted,
2990
- userId: this.keyring.userId
3139
+ userId: this.keyring.userId,
3140
+ erasableBlobs: this.perRecordCek
2991
3141
  });
2992
3142
  }
2993
3143
  /** Get all records as encrypted envelopes (for dump). */
@@ -2995,7 +3145,8 @@ var Collection = class {
2995
3145
  await this.ensureHydrated();
2996
3146
  const result = {};
2997
3147
  for (const [id, entry] of this.cache) {
2998
- 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);
2999
3150
  }
3000
3151
  return result;
3001
3152
  }
@@ -3023,8 +3174,11 @@ var Collection = class {
3023
3174
  if (hasMoney && this.moneyFields) {
3024
3175
  result = decodeMoneyFields(result, this.moneyFields, typeof locale === "string" ? locale : void 0);
3025
3176
  }
3026
- if (!locale) return result;
3027
- 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) {
3028
3182
  result = this.i18nStrategy.applyI18nLocale(result, this.i18nFields, locale, localeOpts?.fallback);
3029
3183
  }
3030
3184
  if (hasDict && this.dictKeyFields && this.dictLabelResolver && locale !== "raw") {
@@ -3033,13 +3187,23 @@ var Collection = class {
3033
3187
  for (const [field, desc] of Object.entries(this.dictKeyFields)) {
3034
3188
  const policy = desc.onMissing ? resolvePolicy(desc.onMissing, "read") : "null";
3035
3189
  const fallback = policy === "substitute" ? localeOpts?.fallback ?? desc.substitute : localeOpts?.fallback;
3190
+ const effLocale = locale ?? (isStaticDictDescriptor(desc) ? desc.displayLocale : void 0);
3036
3191
  const resolveKey = async (key) => {
3037
- 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);
3038
3202
  if (label === void 0) {
3039
3203
  if (policy === "throw") {
3040
3204
  throw new LocaleNotSpecifiedError(
3041
3205
  field,
3042
- `dictKey "${field}": no label for key "${key}" in locale "${locale}".`
3206
+ `dictKey "${field}": no label for key "${key}" in locale "${effLocale}".`
3043
3207
  );
3044
3208
  }
3045
3209
  return null;
@@ -3196,6 +3360,7 @@ var Collection = class {
3196
3360
  if (!envelope) continue;
3197
3361
  try {
3198
3362
  const json = await this.decryptJsonString(envelope);
3363
+ if (json === null) continue;
3199
3364
  const body = JSON.parse(json);
3200
3365
  if (typeof body.recordId !== "string") continue;
3201
3366
  const rows = byField.get(decoded.field) ?? [];
@@ -3305,7 +3470,31 @@ var Collection = class {
3305
3470
  };
3306
3471
  return new LazyQuery(source);
3307
3472
  }
3308
- 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) {
3309
3498
  const by = this.keyring.userId;
3310
3499
  if (!this.encrypted) {
3311
3500
  return {
@@ -3318,6 +3507,19 @@ var Collection = class {
3318
3507
  };
3319
3508
  }
3320
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
+ }
3321
3523
  const { iv, data } = await encrypt(json, dek);
3322
3524
  return {
3323
3525
  _noydb: NOYDB_FORMAT_VERSION,
@@ -3328,8 +3530,8 @@ var Collection = class {
3328
3530
  _by: by
3329
3531
  };
3330
3532
  }
3331
- async encryptRecord(record, version) {
3332
- 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);
3333
3535
  if (!this.deterministicFields || !this.encrypted) return base;
3334
3536
  const dek = await this.getDEK(this.name);
3335
3537
  const rec = record;
@@ -3407,7 +3609,8 @@ var Collection = class {
3407
3609
  const env = await this.adapter.get(this.vault, this.name, id);
3408
3610
  if (!env || !env._det) continue;
3409
3611
  if (env._det[field] === target) {
3410
- matches.push(await this.decryptRecord(env));
3612
+ const rec = await this.decryptRecord(env);
3613
+ if (rec !== null) matches.push(rec);
3411
3614
  }
3412
3615
  }
3413
3616
  return matches;
@@ -3508,7 +3711,14 @@ var Collection = class {
3508
3711
  return null;
3509
3712
  }
3510
3713
  const dek = await this.getDEK(key);
3511
- 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
+ }
3512
3722
  const record = JSON.parse(plaintext);
3513
3723
  this.emitCrossTierEvent({
3514
3724
  actor: this.keyring.userId,
@@ -3564,18 +3774,19 @@ var Collection = class {
3564
3774
  const toKey = dekKey(this.name, toTier);
3565
3775
  const fromDek = await this.getDEK(fromKey);
3566
3776
  const toDek = await this.getDEK(toKey);
3567
- const plaintext = await decrypt(envelope._iv, envelope._data, fromDek);
3568
- const { iv, data } = await encrypt(plaintext, toDek);
3569
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);
3570
3780
  const next = {
3571
3781
  _noydb: NOYDB_FORMAT_VERSION,
3572
3782
  _v: envelope._v + 1,
3573
3783
  _ts: now,
3574
- _iv: iv,
3575
- _data: data,
3784
+ _iv: body._iv,
3785
+ _data: body._data,
3576
3786
  _by: this.keyring.userId,
3577
3787
  _tier: toTier,
3578
- _elevatedBy: this.keyring.userId
3788
+ _elevatedBy: this.keyring.userId,
3789
+ ...body._cek !== void 0 ? { _cek: body._cek } : {}
3579
3790
  };
3580
3791
  await this.adapter.put(this.vault, this.name, id, next);
3581
3792
  this.emitCrossTierEvent({
@@ -3611,17 +3822,18 @@ var Collection = class {
3611
3822
  if (toTier > 0) this.assertDeclaredTier(toTier);
3612
3823
  const fromDek = await this.getDEK(dekKey(this.name, fromTier));
3613
3824
  const toDek = await this.getDEK(dekKey(this.name, toTier));
3614
- const plaintext = await decrypt(envelope._iv, envelope._data, fromDek);
3615
- const { iv, data } = await encrypt(plaintext, toDek);
3616
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);
3617
3828
  const next = {
3618
3829
  _noydb: NOYDB_FORMAT_VERSION,
3619
3830
  _v: envelope._v + 1,
3620
3831
  _ts: now,
3621
- _iv: iv,
3622
- _data: data,
3832
+ _iv: body._iv,
3833
+ _data: body._data,
3623
3834
  _by: this.keyring.userId,
3624
- ...toTier > 0 && { _tier: toTier }
3835
+ ...toTier > 0 && { _tier: toTier },
3836
+ ...body._cek !== void 0 ? { _cek: body._cek } : {}
3625
3837
  };
3626
3838
  await this.adapter.put(this.vault, this.name, id, next);
3627
3839
  this.emitCrossTierEvent({
@@ -3643,10 +3855,30 @@ var Collection = class {
3643
3855
  } catch {
3644
3856
  }
3645
3857
  }
3646
- /** Low-level: decrypt an envelope and return the raw JSON string. */
3647
- 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;
3648
3874
  if (!this.encrypted) return envelope._data;
3649
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
+ }
3650
3882
  return decrypt(envelope._iv, envelope._data, dek);
3651
3883
  }
3652
3884
  /**
@@ -3665,7 +3897,8 @@ var Collection = class {
3665
3897
  * false positive. Every non-history read leaves this flag `false`.
3666
3898
  */
3667
3899
  async decryptRecord(envelope, opts = {}) {
3668
- const json = await this.decryptJsonString(envelope);
3900
+ const json = await this.decryptJsonString(envelope, opts.id);
3901
+ if (json === null) return null;
3669
3902
  let parsed = JSON.parse(json);
3670
3903
  if (this.crdtMode && parsed !== null && typeof parsed === "object" && "_crdt" in parsed) {
3671
3904
  parsed = this.crdtStrategy.resolveCrdtSnapshot(parsed);
@@ -5149,6 +5382,19 @@ function summariseAggregateOp(value) {
5149
5382
  }
5150
5383
 
5151
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
+ }
5152
5398
  var Vault = class {
5153
5399
  adapter;
5154
5400
  /** The vault's name as passed to `openVault()`. Stable for the instance lifetime. */
@@ -5192,6 +5438,7 @@ var Vault = class {
5192
5438
  periodsStrategy;
5193
5439
  shadowStrategy;
5194
5440
  historyStrategy;
5441
+ forgetStrategy;
5195
5442
  i18nStrategy;
5196
5443
  syncStrategy;
5197
5444
  /**
@@ -5358,6 +5605,27 @@ var Vault = class {
5358
5605
  * Populated by `collection()` when the `dictKeyFields` option is passed.
5359
5606
  */
5360
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();
5361
5629
  /**
5362
5630
  * Registry of i18nText fields declared across all collections. Keyed
5363
5631
  * by collection name → field name → I18nTextDescriptor. Used by
@@ -5404,6 +5672,7 @@ var Vault = class {
5404
5672
  this.periodsStrategy = opts.periodsStrategy ?? NO_PERIODS;
5405
5673
  this.shadowStrategy = opts.shadowStrategy ?? NO_SHADOW;
5406
5674
  this.historyStrategy = opts.historyStrategy ?? NO_HISTORY;
5675
+ this.forgetStrategy = opts.forgetStrategy ?? NO_FORGET;
5407
5676
  this.i18nStrategy = opts.i18nStrategy ?? NO_I18N;
5408
5677
  this.syncStrategy = opts.syncStrategy ?? NO_SYNC;
5409
5678
  void opts.guardStrategies;
@@ -5508,10 +5777,22 @@ var Vault = class {
5508
5777
  }
5509
5778
  if (options?.dictKeyFields) {
5510
5779
  const dictFieldMap = {};
5780
+ const staticFieldMap = {};
5511
5781
  for (const [field, desc] of Object.entries(options.dictKeyFields)) {
5512
- 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);
5513
5795
  }
5514
- this.dictKeyFieldRegistry.set(collectionName, dictFieldMap);
5515
5796
  }
5516
5797
  if ((options?.schemaUpdate?.length ?? 0) > 0) {
5517
5798
  this.#schemaUpdateNames.set(collectionName, (options.schemaUpdate ?? []).map((s) => s.name));
@@ -5613,6 +5894,17 @@ var Vault = class {
5613
5894
  if (options?.acknowledgeDeterministicRisk !== void 0) {
5614
5895
  collOpts.acknowledgeDeterministicRisk = options.acknowledgeDeterministicRisk;
5615
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
+ }
5616
5908
  if (options?.tiers !== void 0) collOpts.tiers = options.tiers;
5617
5909
  if (options?.tierMode !== void 0) collOpts.tierMode = options.tierMode;
5618
5910
  collOpts.onCrossTierAccess = (event) => this.emitCrossTier(event);
@@ -5622,6 +5914,11 @@ var Vault = class {
5622
5914
  if (options?.computed !== void 0) collOpts.computed = options.computed;
5623
5915
  if (options?.dictKeyFields !== void 0) {
5624
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
+ }
5625
5922
  const handle = this.dictionary(dictName);
5626
5923
  return handle.resolveLabel(key, locale, fallback);
5627
5924
  };
@@ -5630,6 +5927,7 @@ var Vault = class {
5630
5927
  if (options?.i18nFields !== void 0 || options?.dictKeyFields !== void 0) {
5631
5928
  collOpts.i18nPutValidator = (record) => {
5632
5929
  this.enforceI18nOnPut(collectionName, record);
5930
+ this.enforceStaticDictOnPut(collectionName, record);
5633
5931
  };
5634
5932
  }
5635
5933
  if (options?.i18nFields !== void 0 && this.translateText) {
@@ -5769,6 +6067,34 @@ var Vault = class {
5769
6067
  }
5770
6068
  }
5771
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
+ }
5772
6098
  /**
5773
6099
  * Apply locale resolution to a record for the given collection.
5774
6100
  *
@@ -5777,14 +6103,18 @@ var Vault = class {
5777
6103
  */
5778
6104
  async applyLocale(collectionName, record, localeOpts) {
5779
6105
  const locale = localeOpts.locale ?? this.locale;
5780
- 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;
5781
6109
  let result = record;
5782
- const i18nFields = this.i18nFieldRegistry.get(collectionName);
5783
- if (i18nFields && Object.keys(i18nFields).length > 0) {
5784
- 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
+ }
5785
6115
  }
5786
6116
  const dictFields = this.dictKeyFieldRegistry.get(collectionName);
5787
- if (dictFields && Object.keys(dictFields).length > 0 && locale !== "raw") {
6117
+ if (locale && dictFields && Object.keys(dictFields).length > 0 && locale !== "raw") {
5788
6118
  const withLabels = { ...result };
5789
6119
  for (const [field, dictName] of Object.entries(dictFields)) {
5790
6120
  const key = result[field];
@@ -5797,6 +6127,22 @@ var Vault = class {
5797
6127
  }
5798
6128
  result = withLabels;
5799
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
+ }
5800
6146
  return result;
5801
6147
  }
5802
6148
  /**
@@ -5818,6 +6164,9 @@ var Vault = class {
5818
6164
  * ```
5819
6165
  */
5820
6166
  dictionary(name, options = {}) {
6167
+ if (this.staticDictNames.has(name)) {
6168
+ throw new StaticDictReadonlyError(name);
6169
+ }
5821
6170
  let handle = this.dictionaryCache.get(name);
5822
6171
  if (!handle) {
5823
6172
  handle = this.i18nStrategy.buildDictionaryHandle({
@@ -5887,6 +6236,26 @@ var Vault = class {
5887
6236
  * Returns `null` when `field` is not a dictKey in `leftCollection`.
5888
6237
  */
5889
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
+ }
5890
6259
  const dictFields = this.dictKeyFieldRegistry.get(leftCollection);
5891
6260
  if (!dictFields || !(field in dictFields)) return null;
5892
6261
  const dictName = dictFields[field];
@@ -6197,12 +6566,12 @@ var Vault = class {
6197
6566
  if (!fieldSchema) {
6198
6567
  throw new AttestationError(`issueAttestation: collection '${collectionName}' has no attestation field-schema. Declare it via vault.collection('${collectionName}', { attestation: { fields: [...] } }).`);
6199
6568
  }
6200
- const { issueAttestationCore } = await import("./issue-RZP3VI6O.js");
6569
+ const { issueAttestationCore } = await import("./issue-TTMGHQ2J.js");
6201
6570
  const out = await issueAttestationCore(this.makeIssueContext(), { collection: collectionName, id, fieldSchema });
6202
6571
  return { docId: out.docId, qr: out.qr, keyId: out.keyId, publicKeyB64: out.publicKeyB64 };
6203
6572
  }
6204
6573
  async getDocumentSigningPublicKey() {
6205
- const { loadSigner, loadOrCreateSigner } = await import("./signer-DCMNKXSF.js");
6574
+ const { loadSigner, loadOrCreateSigner } = await import("./signer-YSXZT574.js");
6206
6575
  const existing = await loadSigner(this.adapter, this.name, this.getDEK);
6207
6576
  if (existing) return { keyId: existing.keyId, publicKeyB64: existing.publicKeyB64 };
6208
6577
  if (this.keyring.role !== "owner") {
@@ -6228,19 +6597,19 @@ var Vault = class {
6228
6597
  };
6229
6598
  }
6230
6599
  async revokeAttestation(docId) {
6231
- const { revokeDocCore } = await import("./revoke-HNMQZSCL.js");
6600
+ const { revokeDocCore } = await import("./revoke-B54H2S2W.js");
6232
6601
  await revokeDocCore(this.makeRevokeContext(), docId);
6233
6602
  }
6234
6603
  async unrevokeAttestation(docId) {
6235
- const { unrevokeDocCore } = await import("./revoke-HNMQZSCL.js");
6604
+ const { unrevokeDocCore } = await import("./revoke-B54H2S2W.js");
6236
6605
  await unrevokeDocCore(this.makeRevokeContext(), docId);
6237
6606
  }
6238
6607
  async getRevokedDocIds() {
6239
- const { getRevokedDocIdsCore } = await import("./revoke-HNMQZSCL.js");
6608
+ const { getRevokedDocIdsCore } = await import("./revoke-B54H2S2W.js");
6240
6609
  return getRevokedDocIdsCore(this.makeRevokeContext());
6241
6610
  }
6242
6611
  async publishRevocationList() {
6243
- const { publishRevocationListCore } = await import("./revoke-HNMQZSCL.js");
6612
+ const { publishRevocationListCore } = await import("./revoke-B54H2S2W.js");
6244
6613
  return publishRevocationListCore(this.makeRevokeContext());
6245
6614
  }
6246
6615
  makeRevokeContext() {
@@ -6530,6 +6899,218 @@ var Vault = class {
6530
6899
  }
6531
6900
  return this.ledgerStore;
6532
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
+ }
6533
7114
  /**
6534
7115
  * @internal — called by `Noydb.openVault` after construction.
6535
7116
  * Dynamic-imports `GuardRegistry` + `ReadOnlyVaultFacade` and seeds
@@ -6572,7 +7153,7 @@ var Vault = class {
6572
7153
  async _initDerivations(handles) {
6573
7154
  if (handles.length === 0) return;
6574
7155
  const [{ DerivationRegistry }, { ReadOnlyVaultFacade }] = await Promise.all([
6575
- import("./registry-EB6SISTA.js"),
7156
+ import("./registry-TGZISEWC.js"),
6576
7157
  import("./read-only-facade-ITU6L7BL.js")
6577
7158
  ]);
6578
7159
  const registry = new DerivationRegistry();
@@ -6603,7 +7184,7 @@ var Vault = class {
6603
7184
  */
6604
7185
  async _initMaterializedViews(handles) {
6605
7186
  if (handles.length === 0) return;
6606
- const { MaterializedViewRegistry } = await import("./registry-UTA4CLQS.js");
7187
+ const { MaterializedViewRegistry } = await import("./registry-SECUWSGY.js");
6607
7188
  const registry = new MaterializedViewRegistry();
6608
7189
  this.materializedViewRegistry = registry;
6609
7190
  const db = this;
@@ -6627,7 +7208,7 @@ var Vault = class {
6627
7208
  */
6628
7209
  async _initOverlayedViews(handles) {
6629
7210
  if (handles.length === 0) return;
6630
- const { OverlayedViewRegistry } = await import("./registry-IUZQVVBB.js");
7211
+ const { OverlayedViewRegistry } = await import("./registry-3YFLZ7WD.js");
6631
7212
  const registry = new OverlayedViewRegistry();
6632
7213
  const mvRegistry = this.materializedViewRegistry;
6633
7214
  const overlayNames = /* @__PURE__ */ new Set();
@@ -6674,13 +7255,13 @@ var Vault = class {
6674
7255
  if (!reg) {
6675
7256
  throw new Error(`refreshView: no MV registered with name "${name}"`);
6676
7257
  }
6677
- const { MaterializedViewExecutor } = await import("./executor-AZLS3KBK.js");
7258
+ const { MaterializedViewExecutor } = await import("./executor-3W63Y44O.js");
6678
7259
  const result = await MaterializedViewExecutor.refresh(reg, {
6679
7260
  getCollection: (n) => this.collection(n),
6680
7261
  getActiveTxContext: () => this.noydb._activeTxContextOrNull,
6681
7262
  getQueryContext: () => this
6682
7263
  });
6683
- const { clearMVStale } = await import("./stale-W5PQTRYH.js");
7264
+ const { clearMVStale } = await import("./stale-TOA36SRK.js");
6684
7265
  clearMVStale(registry, name);
6685
7266
  return result;
6686
7267
  }
@@ -6696,7 +7277,7 @@ var Vault = class {
6696
7277
  if (registry === null) return { derived: 0, failed: 0 };
6697
7278
  const strategies = registry.strategiesForSource(sourceCollection);
6698
7279
  if (strategies.length === 0) return { derived: 0, failed: 0 };
6699
- const { DerivationExecutor } = await import("./executor-6ZDSDZ6V.js");
7280
+ const { DerivationExecutor } = await import("./executor-CFFWPWBJ.js");
6700
7281
  const sourceColl = this.collection(sourceCollection);
6701
7282
  const records = await sourceColl.list();
6702
7283
  const ctx = { vault: this.readOnlyFacade ?? new (await import("./read-only-facade-ITU6L7BL.js")).ReadOnlyVaultFacade(this) };
@@ -6721,7 +7302,7 @@ var Vault = class {
6721
7302
  if (!outSpec) continue;
6722
7303
  const outputColl = this.collection(outSpec.collection);
6723
7304
  if (out.kind === "array") {
6724
- const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-67CMI3UT.js");
7305
+ const { loadFanoutSidecar, saveFanoutSidecar } = await import("./fanout-sidecar-FIJJ46YG.js");
6725
7306
  const prior = await loadFanoutSidecar(this.adapter, this.name, spec.source, id, key);
6726
7307
  const prevKeys = new Set(prior?.keys ?? []);
6727
7308
  const newKeysList = out.entries.map((e) => e.key);
@@ -6942,7 +7523,7 @@ var Vault = class {
6942
7523
  * collection.
6943
7524
  */
6944
7525
  async delegate(opts) {
6945
- const { issueDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-NIQ43IPU.js");
7526
+ const { issueDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-MGH5SODX.js");
6946
7527
  if (!this.keyring.kek) {
6947
7528
  throw new ValidationError(
6948
7529
  "issueDelegation: keyring.kek is null \u2014 issuing a delegation requires a tier-1 unlock. Re-authenticate at tier 1 (passphrase) first."
@@ -6964,7 +7545,7 @@ var Vault = class {
6964
7545
  * if the id does not exist.
6965
7546
  */
6966
7547
  async revokeDelegation(id) {
6967
- const { revokeDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-NIQ43IPU.js");
7548
+ const { revokeDelegation, DELEGATIONS_COLLECTION } = await import("./delegation-MGH5SODX.js");
6968
7549
  await revokeDelegation(this.adapter, this.name, id);
6969
7550
  void DELEGATIONS_COLLECTION;
6970
7551
  }
@@ -7433,7 +8014,7 @@ var Vault = class {
7433
8014
  * @see docs/subsystems/public-envelope.md
7434
8015
  */
7435
8016
  async getPublicEnvelope(opts = {}) {
7436
- const { readPublicEnvelope: readPublicEnvelope2 } = await import("./public-envelope-YP2UWMLG.js");
8017
+ const { readPublicEnvelope: readPublicEnvelope2 } = await import("./public-envelope-RXZNP3V6.js");
7437
8018
  return readPublicEnvelope2(this.adapter, this.name, opts);
7438
8019
  }
7439
8020
  /**
@@ -8977,6 +9558,7 @@ var Noydb = class {
8977
9558
  policyEnforcers = /* @__PURE__ */ new Map();
8978
9559
  vaultTemplates = /* @__PURE__ */ new Map();
8979
9560
  txStrategy;
9561
+ forgetStrategy;
8980
9562
  sessionStrategy;
8981
9563
  syncStrategy;
8982
9564
  snapshotStrategy;
@@ -9004,6 +9586,7 @@ var Noydb = class {
9004
9586
  constructor(options) {
9005
9587
  this.options = options;
9006
9588
  this.txStrategy = options.txStrategy ?? NO_TX;
9589
+ this.forgetStrategy = options.forgetStrategy ?? NO_FORGET;
9007
9590
  this.sessionStrategy = options.sessionStrategy ?? NO_SESSION;
9008
9591
  this.syncStrategy = options.syncStrategy ?? NO_SYNC;
9009
9592
  this.snapshotStrategy = options.snapshotStrategy ?? NO_SNAPSHOTS;
@@ -9014,8 +9597,61 @@ var Noydb = class {
9014
9597
  }
9015
9598
  this.#registerGuardGate();
9016
9599
  this.#registerPeriodGate();
9600
+ this.#registerForgetHooks();
9017
9601
  this.resetSessionTimer();
9018
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
+ }
9019
9655
  // Track A — guards migration. Registers record-lock / field-freeze / onDelete
9020
9656
  // / amendment-collect as gate-bus handlers (only when guards are opted in, so
9021
9657
  // the write path is zero-cost otherwise). Resolves the live vault's
@@ -9043,7 +9679,7 @@ var Noydb = class {
9043
9679
  if (!facade) return;
9044
9680
  const ctx = { existing, vault: facade, userId: e.userId, role: e.role };
9045
9681
  await registry.runChecks(e.collection, incoming, ctx);
9046
- const { GuardExecutor } = await import("./executor-IDZDAFNH.js");
9682
+ const { GuardExecutor } = await import("./executor-VDQQOR4F.js");
9047
9683
  for (const g of guards) {
9048
9684
  await GuardExecutor.checkFrozenFields(g, e.docId, existing, incoming, e.computedFieldNames);
9049
9685
  }
@@ -9236,6 +9872,7 @@ var Noydb = class {
9236
9872
  ...this.options.syncStrategy !== void 0 ? { syncStrategy: this.options.syncStrategy } : {},
9237
9873
  ...this.options.guardStrategies !== void 0 ? { guardStrategies: this.options.guardStrategies } : {},
9238
9874
  ...this.options.numbering !== void 0 ? { numberingConfigs: this.options.numbering } : {},
9875
+ forgetStrategy: this.forgetStrategy,
9239
9876
  locale: opts?.locale,
9240
9877
  // Thread the translator hook so Collection.put() can invoke it
9241
9878
  plaintextTranslator: this.options.plaintextTranslator ? (text, from, to, field, collection) => this.invokeTranslator(text, from, to, field, collection) : void 0,
@@ -9290,7 +9927,8 @@ var Noydb = class {
9290
9927
  ...this.options.i18nStrategy !== void 0 ? { i18nStrategy: this.options.i18nStrategy } : {},
9291
9928
  ...this.options.syncStrategy !== void 0 ? { syncStrategy: this.options.syncStrategy } : {},
9292
9929
  ...this.options.guardStrategies !== void 0 ? { guardStrategies: this.options.guardStrategies } : {},
9293
- ...this.options.numbering !== void 0 ? { numberingConfigs: this.options.numbering } : {}
9930
+ ...this.options.numbering !== void 0 ? { numberingConfigs: this.options.numbering } : {},
9931
+ forgetStrategy: this.forgetStrategy
9294
9932
  });
9295
9933
  this.vaultCache.set(name, comp2);
9296
9934
  return comp2;
@@ -9642,8 +10280,8 @@ var Noydb = class {
9642
10280
  if (name === STATE_VAULT_NAME) throw new ReservedVaultNameError(name);
9643
10281
  const template = this.vaultTemplates.get(opts.sharding.vaultTemplate);
9644
10282
  if (!template) throw new VaultTemplateNotFoundError(opts.sharding.vaultTemplate);
9645
- const { VaultGroup } = await import("./vault-group-DX2HFQMX.js");
9646
- 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");
9647
10285
  const stateVault = opts.registry ? void 0 : await StateManagementVault.open(this);
9648
10286
  const registry = opts.registry ?? stateVault.registry;
9649
10287
  const group = new VaultGroup(this, name, registry, opts.sharding, template);
@@ -9670,7 +10308,7 @@ var Noydb = class {
9670
10308
  */
9671
10309
  async openStateManagementVault() {
9672
10310
  if (this.closed) throw new ValidationError("Instance is closed");
9673
- const { StateManagementVault } = await import("./state-vault-TMXZRTY5.js");
10311
+ const { StateManagementVault } = await import("./state-vault-W2OEABNO.js");
9674
10312
  return StateManagementVault.open(this);
9675
10313
  }
9676
10314
  /**
@@ -11211,4 +11849,4 @@ export {
11211
11849
  Noydb,
11212
11850
  createNoydb
11213
11851
  };
11214
- //# sourceMappingURL=chunk-667MB6AH.js.map
11852
+ //# sourceMappingURL=chunk-D77ZQSQQ.js.map