@noy-db/hub 0.3.0-pre.7 → 0.3.0-pre.9

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 (401) hide show
  1. package/dist/adapter/index.d.ts +2 -3
  2. package/dist/adapter/index.js +1 -1
  3. package/dist/aggregate/index.d.ts +3 -4
  4. package/dist/aggregate/index.js +9 -8
  5. package/dist/aggregate/index.js.map +1 -1
  6. package/dist/api-HV4PDUWF.js +20 -0
  7. package/dist/as/index.d.ts +4 -5
  8. package/dist/as/index.js +11 -11
  9. package/dist/at/index.d.ts +2 -3
  10. package/dist/attestation/index.d.ts +3 -4
  11. package/dist/attestation/index.js +18 -18
  12. package/dist/{backup-7PX6NDKT.js → backup-ODXO7OGA.js} +12 -12
  13. package/dist/blobs/index.d.ts +4 -5
  14. package/dist/blobs/index.js +11 -11
  15. package/dist/blobs/index.js.map +1 -1
  16. package/dist/bundle/index.d.ts +5 -6
  17. package/dist/bundle/index.js +20 -20
  18. package/dist/{bundle-BN_VSq5L.d.ts → bundle-DlSxo31R.d.ts} +1 -1
  19. package/dist/by/index.js +2 -2
  20. package/dist/cargo/index.d.ts +4 -5
  21. package/dist/cargo/index.js +23 -22
  22. package/dist/{chunk-QYNBPDGO.js → chunk-2644KEGW.js} +18 -10
  23. package/dist/chunk-2644KEGW.js.map +1 -0
  24. package/dist/{chunk-XW5LZYV4.js → chunk-2HVUKRNE.js} +2 -2
  25. package/dist/{chunk-VFQOE76I.js → chunk-2KOSJTMU.js} +2 -2
  26. package/dist/{chunk-7AERMJP6.js → chunk-2UQ4RMHV.js} +4 -4
  27. package/dist/{chunk-5GQE5IDB.js → chunk-2XE6LIXT.js} +13 -11
  28. package/dist/chunk-2XE6LIXT.js.map +1 -0
  29. package/dist/{chunk-VIWOGJ2O.js → chunk-34RRRIC3.js} +2 -2
  30. package/dist/{chunk-BASCPHNM.js → chunk-3C5CKXFI.js} +289 -48
  31. package/dist/chunk-3C5CKXFI.js.map +1 -0
  32. package/dist/{chunk-LFGJG4XZ.js → chunk-3I4KYWNP.js} +2 -2
  33. package/dist/{chunk-RUWEHFRB.js → chunk-42KVY3OC.js} +4 -4
  34. package/dist/{chunk-5K7KZMXG.js → chunk-4C4WBS4V.js} +2048 -948
  35. package/dist/chunk-4C4WBS4V.js.map +1 -0
  36. package/dist/{chunk-J54DC5IC.js → chunk-4G3IT7J7.js} +7 -7
  37. package/dist/{chunk-WJEMUO32.js → chunk-4RQ62VSR.js} +2 -2
  38. package/dist/{chunk-QAVFI23O.js → chunk-4ZSGSRBI.js} +8 -8
  39. package/dist/{chunk-C4NOLA5X.js → chunk-5GAX2QOH.js} +2 -2
  40. package/dist/{chunk-IEKK2XI4.js → chunk-5SAZMBW7.js} +7 -7
  41. package/dist/{chunk-II6DLDZM.js → chunk-5TZZUL5X.js} +2 -2
  42. package/dist/{chunk-GMZFHDSG.js → chunk-6CU3FN74.js} +8 -8
  43. package/dist/chunk-6G2SCQYS.js +29 -0
  44. package/dist/chunk-6G2SCQYS.js.map +1 -0
  45. package/dist/{chunk-UWZ7O4NR.js → chunk-6MWUANRF.js} +2 -2
  46. package/dist/{chunk-U7LV2DUN.js → chunk-6ULCYCMU.js} +2 -2
  47. package/dist/{chunk-6Z435B6N.js → chunk-7HAPHVRJ.js} +131 -92
  48. package/dist/chunk-7HAPHVRJ.js.map +1 -0
  49. package/dist/{chunk-VFG5YXWM.js → chunk-7IZQTCTR.js} +3 -3
  50. package/dist/chunk-7ZANJ6JZ.js +265 -0
  51. package/dist/chunk-7ZANJ6JZ.js.map +1 -0
  52. package/dist/{chunk-VMRXI3IF.js → chunk-A2APWS32.js} +6 -6
  53. package/dist/chunk-A2APWS32.js.map +1 -0
  54. package/dist/{chunk-U5IJHUHZ.js → chunk-A4MAD6PO.js} +5 -5
  55. package/dist/chunk-ANDBBA2P.js +36 -0
  56. package/dist/chunk-ANDBBA2P.js.map +1 -0
  57. package/dist/{chunk-5HZULLHE.js → chunk-AUR7MRHA.js} +2 -2
  58. package/dist/{chunk-YSJG4L4A.js → chunk-AYGZMQP5.js} +3 -3
  59. package/dist/chunk-AYGZMQP5.js.map +1 -0
  60. package/dist/{chunk-H7WQMGXH.js → chunk-CJEMA2Z6.js} +2 -2
  61. package/dist/{chunk-WRLPV6SI.js → chunk-CTSFKDGA.js} +2 -2
  62. package/dist/{chunk-OJ64XMPJ.js → chunk-CU62BBRB.js} +32 -1
  63. package/dist/chunk-CU62BBRB.js.map +1 -0
  64. package/dist/{chunk-OFYY5Q4R.js → chunk-CWCCYH34.js} +2 -2
  65. package/dist/{chunk-WGH3YV6Y.js → chunk-DEYAO3N6.js} +10 -10
  66. package/dist/{chunk-7L6EH6S7.js → chunk-DFIBOSOQ.js} +8 -8
  67. package/dist/{chunk-3CHNKEFI.js → chunk-E5AHO65E.js} +7 -7
  68. package/dist/{chunk-AG2PLGQ2.js → chunk-FC3XK5JT.js} +8 -8
  69. package/dist/{chunk-CP3GUIAN.js → chunk-FGOGS7CE.js} +10 -12
  70. package/dist/chunk-FGOGS7CE.js.map +1 -0
  71. package/dist/{chunk-DJ4TNF4N.js → chunk-GX2BYNCT.js} +7 -7
  72. package/dist/{chunk-YTCCC6NG.js → chunk-GZ2MOPVX.js} +34 -63
  73. package/dist/chunk-GZ2MOPVX.js.map +1 -0
  74. package/dist/chunk-H57P2SJB.js +126 -0
  75. package/dist/chunk-H57P2SJB.js.map +1 -0
  76. package/dist/{chunk-55LSDABW.js → chunk-HBTT7YQX.js} +2 -2
  77. package/dist/{chunk-PYIUDUGS.js → chunk-HOCEGVGQ.js} +2 -2
  78. package/dist/{chunk-5JQMX2EH.js → chunk-ICV32FPK.js} +2 -2
  79. package/dist/{chunk-WFBZRXS5.js → chunk-IIP5WFVT.js} +138 -92
  80. package/dist/chunk-IIP5WFVT.js.map +1 -0
  81. package/dist/{chunk-YSU2E4GZ.js → chunk-IUKGIB2Q.js} +19 -6
  82. package/dist/chunk-IUKGIB2Q.js.map +1 -0
  83. package/dist/{chunk-OWGAXPRE.js → chunk-IWTYFS3D.js} +2 -2
  84. package/dist/{chunk-FCOZ7DEC.js → chunk-JC5R7P44.js} +3 -3
  85. package/dist/{chunk-K6WHZBCO.js → chunk-KBBWY46Y.js} +2 -2
  86. package/dist/{chunk-RNYDHIQL.js → chunk-KBF5GSGX.js} +3 -3
  87. package/dist/{chunk-6P5AJ2ZS.js → chunk-KCG3BHFG.js} +2 -23
  88. package/dist/chunk-KCG3BHFG.js.map +1 -0
  89. package/dist/{chunk-T4NQMPBN.js → chunk-KNXN47MO.js} +3 -3
  90. package/dist/{chunk-VK6OC22R.js → chunk-KWLD2D75.js} +4 -3
  91. package/dist/chunk-KWLD2D75.js.map +1 -0
  92. package/dist/{chunk-7F2P2NHX.js → chunk-KXKLKJW4.js} +6 -6
  93. package/dist/{chunk-PAJCRV7O.js → chunk-KYC4LIEU.js} +2 -2
  94. package/dist/{chunk-AZPGB4OD.js → chunk-LEOXZY36.js} +8 -8
  95. package/dist/{chunk-2L7PPF2C.js → chunk-M5I354EE.js} +9 -9
  96. package/dist/{chunk-26FSVNBL.js → chunk-MK2BSS44.js} +2 -2
  97. package/dist/{chunk-G5CFMITA.js → chunk-MSVY7ULZ.js} +3 -3
  98. package/dist/{chunk-PE25C4QL.js → chunk-NNWK3PSY.js} +9 -9
  99. package/dist/{chunk-JT4A6OAJ.js → chunk-NSZ6J5DF.js} +7 -7
  100. package/dist/{chunk-RHB7BOEO.js → chunk-NUEFXLZU.js} +7 -7
  101. package/dist/{chunk-I6Q7YECW.js → chunk-NYVQCZ54.js} +5 -5
  102. package/dist/chunk-NYVQCZ54.js.map +1 -0
  103. package/dist/{chunk-DKGZIX6S.js → chunk-OQAODTYR.js} +2 -2
  104. package/dist/{chunk-HV3VNAYI.js → chunk-OV4CMJGR.js} +5 -5
  105. package/dist/{chunk-V76PHWLE.js → chunk-PEDZFFKW.js} +2 -2
  106. package/dist/chunk-PF3FQCCF.js +164 -0
  107. package/dist/chunk-PF3FQCCF.js.map +1 -0
  108. package/dist/{chunk-CND5WQLO.js → chunk-Q2KVKTC2.js} +5 -5
  109. package/dist/{chunk-RUIMKQTA.js → chunk-QJR3W3MF.js} +1 -1
  110. package/dist/chunk-QJR3W3MF.js.map +1 -0
  111. package/dist/{chunk-HBVEGUON.js → chunk-QTXQ6RNZ.js} +3 -3
  112. package/dist/{chunk-J5YDMJHG.js → chunk-RJ5522XX.js} +12 -12
  113. package/dist/{chunk-H3MPSYCH.js → chunk-RMUXPHIH.js} +56 -7
  114. package/dist/chunk-RMUXPHIH.js.map +1 -0
  115. package/dist/{chunk-CHK5VVMI.js → chunk-S6AQXDN4.js} +2 -2
  116. package/dist/{chunk-E3LURVGQ.js → chunk-SSFC3IYA.js} +9 -9
  117. package/dist/chunk-SSFC3IYA.js.map +1 -0
  118. package/dist/{chunk-MOQ5M46U.js → chunk-SZCHEDML.js} +2 -2
  119. package/dist/{chunk-UB3XHGFG.js → chunk-TDPEJINP.js} +14 -7
  120. package/dist/chunk-TDPEJINP.js.map +1 -0
  121. package/dist/{chunk-HCK2DFEK.js → chunk-UNJU345Q.js} +1 -1
  122. package/dist/chunk-UNJU345Q.js.map +1 -0
  123. package/dist/chunk-V7RNIND6.js +265 -0
  124. package/dist/chunk-V7RNIND6.js.map +1 -0
  125. package/dist/{chunk-VUHXLL32.js → chunk-VJ3NQK4K.js} +5 -5
  126. package/dist/{chunk-62TTFKU4.js → chunk-WNA6T3MA.js} +8 -8
  127. package/dist/{chunk-7CZ6YXMO.js → chunk-WVCQ75OK.js} +2 -2
  128. package/dist/{chunk-NO53W6O5.js → chunk-X66MXMR5.js} +2 -2
  129. package/dist/{chunk-JIW3Q5JN.js → chunk-XHD3L3JO.js} +199 -35
  130. package/dist/chunk-XHD3L3JO.js.map +1 -0
  131. package/dist/{chunk-6PALJTC2.js → chunk-XJK2WIYN.js} +3 -3
  132. package/dist/{chunk-5UOPIPZC.js → chunk-XQ7UYPVN.js} +3 -3
  133. package/dist/{chunk-HU55NPHZ.js → chunk-Y2OCKFZ3.js} +2 -2
  134. package/dist/{chunk-UJJJPKJO.js → chunk-YBFOBRTO.js} +3 -3
  135. package/dist/{chunk-I6JJ6VJS.js → chunk-YLN33ADH.js} +2 -2
  136. package/dist/{chunk-HGQ6UNHE.js → chunk-Z5YFRMG5.js} +2 -2
  137. package/dist/{chunk-EU7HDMT3.js → chunk-ZWQFOXBR.js} +2 -2
  138. package/dist/classified/index.d.ts +4 -5
  139. package/dist/classified/index.js +5 -3
  140. package/dist/{config-drift-LRKCC3VZ.js → classified-marker-IO5EBW7Q.js} +4 -4
  141. package/dist/classified-marker-IO5EBW7Q.js.map +1 -0
  142. package/dist/collection-facade-3RAXG2OU.js +38 -0
  143. package/dist/{computed-6FOFXQPE.js → computed-R62HGCO4.js} +3 -3
  144. package/dist/consent/index.d.ts +3 -4
  145. package/dist/consent/index.js +9 -9
  146. package/dist/crdt/index.d.ts +3 -4
  147. package/dist/{dead-filter-NZORV45S.js → dead-filter-VNA4M3GP.js} +2 -2
  148. package/dist/delegation-ZKII5UBI.js +25 -0
  149. package/dist/derivations/index.d.ts +4 -5
  150. package/dist/derivations/index.js +12 -11
  151. package/dist/derive-5D2C3WG4.js +21 -0
  152. package/dist/describe/index.d.ts +4 -1
  153. package/dist/{dev-unlock-CsOqX7O2.d.ts → dev-unlock-DDTafVO6.d.ts} +1 -1
  154. package/dist/{enclave-7X5BUAMS.js → enclave-EUBEJGPG.js} +11 -9
  155. package/dist/executor-IL7SRNUZ.js +23 -0
  156. package/dist/executor-OUA2Y6YC.js +9 -0
  157. package/dist/executor-WAEI6PDV.js +9 -0
  158. package/dist/export-accessible-O55RKAGC.js +23 -0
  159. package/dist/extract-partition-VBGCBA6Q.js +36 -0
  160. package/dist/{fanout-sidecar-BDKLP5GG.js → fanout-sidecar-CI5BCMQM.js} +11 -11
  161. package/dist/find-FISGUWSK.js +11 -0
  162. package/dist/forget/index.d.ts +2 -2
  163. package/dist/forget/index.js +9 -9
  164. package/dist/guards/index.d.ts +4 -5
  165. package/dist/guards/index.js +4 -4
  166. package/dist/{hash-BPzm02Za.d.ts → hash-DBkZfIBW.d.ts} +1 -1
  167. package/dist/history/index.d.ts +4 -5
  168. package/dist/history/index.js +11 -11
  169. package/dist/i18n/index.d.ts +24 -5
  170. package/dist/i18n/index.js +45 -34
  171. package/dist/i18n/index.js.map +1 -1
  172. package/dist/in/index.d.ts +2 -3
  173. package/dist/{index-CLucFLeN.d.ts → index-CyclmyDr.d.ts} +2 -3
  174. package/dist/{index-BJ3L-XcV.d.ts → index-DzhNI2vE.d.ts} +6378 -5022
  175. package/dist/index.d.ts +64 -23
  176. package/dist/index.js +411 -132
  177. package/dist/index.js.map +1 -1
  178. package/dist/indexing/index.d.ts +3 -4
  179. package/dist/indexing/index.js +4 -4
  180. package/dist/issue-EQAWIYFQ.js +19 -0
  181. package/dist/kernel/index.d.ts +3 -4
  182. package/dist/kernel/index.js +13 -12
  183. package/dist/lazy/index.d.ts +3 -4
  184. package/dist/{ledger-EMDHRJJN.js → ledger-AJA2SR3C.js} +11 -11
  185. package/dist/liberate-SEGR4JLY.js +24 -0
  186. package/dist/{link-set-62Y36TOG.js → link-set-YBAC2DQT.js} +11 -11
  187. package/dist/materialized-views/index.d.ts +14 -5
  188. package/dist/materialized-views/index.js +17 -15
  189. package/dist/{mime-magic-BlE9fzn2.d.ts → mime-magic-CbV1quyp.d.ts} +1 -1
  190. package/dist/noydb-AMEOU456.js +65 -0
  191. package/dist/on/index.d.ts +2 -3
  192. package/dist/on/index.js +9 -9
  193. package/dist/overlay-views/index.d.ts +4 -5
  194. package/dist/overlay-views/index.js +4 -4
  195. package/dist/periods/index.d.ts +3 -4
  196. package/dist/periods/index.js +20 -12
  197. package/dist/pod/index.d.ts +3 -4
  198. package/dist/pod/index.js +11 -11
  199. package/dist/{policy-SXPLTQS4.js → policy-SDGRWVIL.js} +5 -5
  200. package/dist/portability/index.d.ts +3 -4
  201. package/dist/portability/index.js +8 -8
  202. package/dist/{post-register-DCJ2SRVG.js → post-register-MROIUA5O.js} +6 -6
  203. package/dist/{public-envelope-CBRIJORQ.js → public-envelope-J5PLQOW2.js} +4 -4
  204. package/dist/query/index.d.ts +2 -3
  205. package/dist/query/index.js +9 -6
  206. package/dist/{read-only-facade-5ADRJVTM.js → read-only-facade-UCYNRE56.js} +2 -2
  207. package/dist/register-OVJKW7BS.js +23 -0
  208. package/dist/registry-5RJSK5AJ.js +9 -0
  209. package/dist/registry-DQRNTWRL.js +18 -0
  210. package/dist/registry-LTGLERS7.js +19 -0
  211. package/dist/request-withdrawal-27O77D2V.js +31 -0
  212. package/dist/{reveal-R5N7JKA5.js → reveal-LT3I2K4P.js} +6 -6
  213. package/dist/revoke-4N45G7D2.js +24 -0
  214. package/dist/satellites/index.d.ts +2 -3
  215. package/dist/satellites/index.js +1 -1
  216. package/dist/sealed-record/index.d.ts +3 -4
  217. package/dist/sealed-record/index.js +12 -12
  218. package/dist/session/index.d.ts +4 -5
  219. package/dist/session/index.js +9 -9
  220. package/dist/shadow/index.d.ts +3 -4
  221. package/dist/shadow/index.js +2 -2
  222. package/dist/signer-XJCOV7JD.js +25 -0
  223. package/dist/snapshots/index.d.ts +3 -4
  224. package/dist/snapshots/index.js +10 -10
  225. package/dist/{stale-DP66HFT3.js → stale-IYYXEH3U.js} +2 -2
  226. package/dist/storage-Y44CAPE6.js +23 -0
  227. package/dist/{store-coordination-provider-F5N5ASDF.js → store-coordination-provider-WKGAE2WI.js} +3 -3
  228. package/dist/{subject-index-O75wKshW.d.ts → subject-index-CyOgGMJL.d.ts} +18 -0
  229. package/dist/sync/index.d.ts +2 -3
  230. package/dist/sync/index.js +9 -9
  231. package/dist/team/index.d.ts +3 -4
  232. package/dist/team/index.js +15 -15
  233. package/dist/tiers/index.d.ts +2 -3
  234. package/dist/tiers/index.js +10 -10
  235. package/dist/to/index.d.ts +2 -3
  236. package/dist/to/index.js +1 -1
  237. package/dist/{transition-guard-BzsAQEd-.d.ts → transition-guard-BOUkzQU4.d.ts} +1 -1
  238. package/dist/tx/index.d.ts +3 -4
  239. package/dist/tx/index.js +3 -3
  240. package/dist/ui/index.d.ts +4 -1
  241. package/dist/util/index.js +1 -1
  242. package/dist/{validators-DGmYFP51.d.ts → validators-B3SalxlJ.d.ts} +1 -1
  243. package/dist/{vault-diff-DmBBatkx.d.ts → vault-diff-Dt9NkLzw.d.ts} +1 -1
  244. package/dist/{verify-CAYGXYNO.js → verify-GH6CWVSW.js} +5 -5
  245. package/dist/{walk-3SB25HF2.js → walk-76UCFXYV.js} +11 -11
  246. package/dist/with/index.d.ts +3 -4
  247. package/dist/with/index.js +11 -11
  248. package/dist/{with-materialized-view-DPEVoGoB.d.ts → with-materialized-view-BP33v1Z6.d.ts} +1 -1
  249. package/dist/{with-overlayed-view-DGpKFSv1.d.ts → with-overlayed-view-DntFpOKN.d.ts} +1 -1
  250. package/dist/{with-rollup-B2RwnTq0.d.ts → with-rollup-ClCHO6-4.d.ts} +1 -1
  251. package/dist/withdraw-accessible-6J2UH3IN.js +28 -0
  252. package/package.json +3 -3
  253. package/dist/api-Z66JMGOU.js +0 -20
  254. package/dist/chunk-47C4CI7R.js +0 -108
  255. package/dist/chunk-47C4CI7R.js.map +0 -1
  256. package/dist/chunk-5GQE5IDB.js.map +0 -1
  257. package/dist/chunk-5K7KZMXG.js.map +0 -1
  258. package/dist/chunk-6P5AJ2ZS.js.map +0 -1
  259. package/dist/chunk-6Z435B6N.js.map +0 -1
  260. package/dist/chunk-7KTL4RHZ.js +0 -120
  261. package/dist/chunk-7KTL4RHZ.js.map +0 -1
  262. package/dist/chunk-BASCPHNM.js.map +0 -1
  263. package/dist/chunk-CP3GUIAN.js.map +0 -1
  264. package/dist/chunk-E3LURVGQ.js.map +0 -1
  265. package/dist/chunk-H3MPSYCH.js.map +0 -1
  266. package/dist/chunk-HCK2DFEK.js.map +0 -1
  267. package/dist/chunk-I6Q7YECW.js.map +0 -1
  268. package/dist/chunk-JIW3Q5JN.js.map +0 -1
  269. package/dist/chunk-OJ64XMPJ.js.map +0 -1
  270. package/dist/chunk-QYNBPDGO.js.map +0 -1
  271. package/dist/chunk-RUIMKQTA.js.map +0 -1
  272. package/dist/chunk-UB3XHGFG.js.map +0 -1
  273. package/dist/chunk-VK6OC22R.js.map +0 -1
  274. package/dist/chunk-VMRXI3IF.js.map +0 -1
  275. package/dist/chunk-WFBZRXS5.js.map +0 -1
  276. package/dist/chunk-YSJG4L4A.js.map +0 -1
  277. package/dist/chunk-YSU2E4GZ.js.map +0 -1
  278. package/dist/chunk-YTCCC6NG.js.map +0 -1
  279. package/dist/collection-facade-JP6N7USE.js +0 -39
  280. package/dist/config-drift-LRKCC3VZ.js.map +0 -1
  281. package/dist/delegation-FDOXXHNG.js +0 -25
  282. package/dist/derive-JR2C5JFG.js +0 -21
  283. package/dist/describe-C6iHNlqJ.d.ts +0 -162
  284. package/dist/executor-6LHOV7MN.js +0 -21
  285. package/dist/executor-EUPYHCLU.js +0 -9
  286. package/dist/executor-KZ4B3PRT.js +0 -9
  287. package/dist/export-accessible-7XLSZBGJ.js +0 -23
  288. package/dist/extract-partition-RM6EROS5.js +0 -36
  289. package/dist/find-OH4BEULF.js +0 -11
  290. package/dist/issue-FJE2IHUI.js +0 -19
  291. package/dist/liberate-HFIPHJGR.js +0 -24
  292. package/dist/noydb-4ZASGII5.js +0 -62
  293. package/dist/register-5NRE7HBZ.js +0 -23
  294. package/dist/registry-5QLETK3Q.js +0 -17
  295. package/dist/registry-DYTNLOJC.js +0 -19
  296. package/dist/registry-HSNQ4NBP.js +0 -9
  297. package/dist/request-withdrawal-5APB7RKN.js +0 -31
  298. package/dist/revoke-VELFCK7S.js +0 -24
  299. package/dist/signer-IVU3B6DP.js +0 -25
  300. package/dist/storage-RH5VBLLL.js +0 -23
  301. package/dist/withdraw-accessible-LN4AOJDD.js +0 -28
  302. /package/dist/{api-Z66JMGOU.js.map → api-HV4PDUWF.js.map} +0 -0
  303. /package/dist/{backup-7PX6NDKT.js.map → backup-ODXO7OGA.js.map} +0 -0
  304. /package/dist/{chunk-XW5LZYV4.js.map → chunk-2HVUKRNE.js.map} +0 -0
  305. /package/dist/{chunk-VFQOE76I.js.map → chunk-2KOSJTMU.js.map} +0 -0
  306. /package/dist/{chunk-7AERMJP6.js.map → chunk-2UQ4RMHV.js.map} +0 -0
  307. /package/dist/{chunk-VIWOGJ2O.js.map → chunk-34RRRIC3.js.map} +0 -0
  308. /package/dist/{chunk-LFGJG4XZ.js.map → chunk-3I4KYWNP.js.map} +0 -0
  309. /package/dist/{chunk-RUWEHFRB.js.map → chunk-42KVY3OC.js.map} +0 -0
  310. /package/dist/{chunk-J54DC5IC.js.map → chunk-4G3IT7J7.js.map} +0 -0
  311. /package/dist/{chunk-WJEMUO32.js.map → chunk-4RQ62VSR.js.map} +0 -0
  312. /package/dist/{chunk-QAVFI23O.js.map → chunk-4ZSGSRBI.js.map} +0 -0
  313. /package/dist/{chunk-C4NOLA5X.js.map → chunk-5GAX2QOH.js.map} +0 -0
  314. /package/dist/{chunk-IEKK2XI4.js.map → chunk-5SAZMBW7.js.map} +0 -0
  315. /package/dist/{chunk-II6DLDZM.js.map → chunk-5TZZUL5X.js.map} +0 -0
  316. /package/dist/{chunk-GMZFHDSG.js.map → chunk-6CU3FN74.js.map} +0 -0
  317. /package/dist/{chunk-UWZ7O4NR.js.map → chunk-6MWUANRF.js.map} +0 -0
  318. /package/dist/{chunk-U7LV2DUN.js.map → chunk-6ULCYCMU.js.map} +0 -0
  319. /package/dist/{chunk-VFG5YXWM.js.map → chunk-7IZQTCTR.js.map} +0 -0
  320. /package/dist/{chunk-U5IJHUHZ.js.map → chunk-A4MAD6PO.js.map} +0 -0
  321. /package/dist/{chunk-5HZULLHE.js.map → chunk-AUR7MRHA.js.map} +0 -0
  322. /package/dist/{chunk-H7WQMGXH.js.map → chunk-CJEMA2Z6.js.map} +0 -0
  323. /package/dist/{chunk-WRLPV6SI.js.map → chunk-CTSFKDGA.js.map} +0 -0
  324. /package/dist/{chunk-OFYY5Q4R.js.map → chunk-CWCCYH34.js.map} +0 -0
  325. /package/dist/{chunk-WGH3YV6Y.js.map → chunk-DEYAO3N6.js.map} +0 -0
  326. /package/dist/{chunk-7L6EH6S7.js.map → chunk-DFIBOSOQ.js.map} +0 -0
  327. /package/dist/{chunk-3CHNKEFI.js.map → chunk-E5AHO65E.js.map} +0 -0
  328. /package/dist/{chunk-AG2PLGQ2.js.map → chunk-FC3XK5JT.js.map} +0 -0
  329. /package/dist/{chunk-DJ4TNF4N.js.map → chunk-GX2BYNCT.js.map} +0 -0
  330. /package/dist/{chunk-55LSDABW.js.map → chunk-HBTT7YQX.js.map} +0 -0
  331. /package/dist/{chunk-PYIUDUGS.js.map → chunk-HOCEGVGQ.js.map} +0 -0
  332. /package/dist/{chunk-5JQMX2EH.js.map → chunk-ICV32FPK.js.map} +0 -0
  333. /package/dist/{chunk-OWGAXPRE.js.map → chunk-IWTYFS3D.js.map} +0 -0
  334. /package/dist/{chunk-FCOZ7DEC.js.map → chunk-JC5R7P44.js.map} +0 -0
  335. /package/dist/{chunk-K6WHZBCO.js.map → chunk-KBBWY46Y.js.map} +0 -0
  336. /package/dist/{chunk-RNYDHIQL.js.map → chunk-KBF5GSGX.js.map} +0 -0
  337. /package/dist/{chunk-T4NQMPBN.js.map → chunk-KNXN47MO.js.map} +0 -0
  338. /package/dist/{chunk-7F2P2NHX.js.map → chunk-KXKLKJW4.js.map} +0 -0
  339. /package/dist/{chunk-PAJCRV7O.js.map → chunk-KYC4LIEU.js.map} +0 -0
  340. /package/dist/{chunk-AZPGB4OD.js.map → chunk-LEOXZY36.js.map} +0 -0
  341. /package/dist/{chunk-2L7PPF2C.js.map → chunk-M5I354EE.js.map} +0 -0
  342. /package/dist/{chunk-26FSVNBL.js.map → chunk-MK2BSS44.js.map} +0 -0
  343. /package/dist/{chunk-G5CFMITA.js.map → chunk-MSVY7ULZ.js.map} +0 -0
  344. /package/dist/{chunk-PE25C4QL.js.map → chunk-NNWK3PSY.js.map} +0 -0
  345. /package/dist/{chunk-JT4A6OAJ.js.map → chunk-NSZ6J5DF.js.map} +0 -0
  346. /package/dist/{chunk-RHB7BOEO.js.map → chunk-NUEFXLZU.js.map} +0 -0
  347. /package/dist/{chunk-DKGZIX6S.js.map → chunk-OQAODTYR.js.map} +0 -0
  348. /package/dist/{chunk-HV3VNAYI.js.map → chunk-OV4CMJGR.js.map} +0 -0
  349. /package/dist/{chunk-V76PHWLE.js.map → chunk-PEDZFFKW.js.map} +0 -0
  350. /package/dist/{chunk-CND5WQLO.js.map → chunk-Q2KVKTC2.js.map} +0 -0
  351. /package/dist/{chunk-HBVEGUON.js.map → chunk-QTXQ6RNZ.js.map} +0 -0
  352. /package/dist/{chunk-J5YDMJHG.js.map → chunk-RJ5522XX.js.map} +0 -0
  353. /package/dist/{chunk-CHK5VVMI.js.map → chunk-S6AQXDN4.js.map} +0 -0
  354. /package/dist/{chunk-MOQ5M46U.js.map → chunk-SZCHEDML.js.map} +0 -0
  355. /package/dist/{chunk-VUHXLL32.js.map → chunk-VJ3NQK4K.js.map} +0 -0
  356. /package/dist/{chunk-62TTFKU4.js.map → chunk-WNA6T3MA.js.map} +0 -0
  357. /package/dist/{chunk-7CZ6YXMO.js.map → chunk-WVCQ75OK.js.map} +0 -0
  358. /package/dist/{chunk-NO53W6O5.js.map → chunk-X66MXMR5.js.map} +0 -0
  359. /package/dist/{chunk-6PALJTC2.js.map → chunk-XJK2WIYN.js.map} +0 -0
  360. /package/dist/{chunk-5UOPIPZC.js.map → chunk-XQ7UYPVN.js.map} +0 -0
  361. /package/dist/{chunk-HU55NPHZ.js.map → chunk-Y2OCKFZ3.js.map} +0 -0
  362. /package/dist/{chunk-UJJJPKJO.js.map → chunk-YBFOBRTO.js.map} +0 -0
  363. /package/dist/{chunk-I6JJ6VJS.js.map → chunk-YLN33ADH.js.map} +0 -0
  364. /package/dist/{chunk-HGQ6UNHE.js.map → chunk-Z5YFRMG5.js.map} +0 -0
  365. /package/dist/{chunk-EU7HDMT3.js.map → chunk-ZWQFOXBR.js.map} +0 -0
  366. /package/dist/{collection-facade-JP6N7USE.js.map → collection-facade-3RAXG2OU.js.map} +0 -0
  367. /package/dist/{computed-6FOFXQPE.js.map → computed-R62HGCO4.js.map} +0 -0
  368. /package/dist/{dead-filter-NZORV45S.js.map → dead-filter-VNA4M3GP.js.map} +0 -0
  369. /package/dist/{delegation-FDOXXHNG.js.map → delegation-ZKII5UBI.js.map} +0 -0
  370. /package/dist/{derive-JR2C5JFG.js.map → derive-5D2C3WG4.js.map} +0 -0
  371. /package/dist/{enclave-7X5BUAMS.js.map → enclave-EUBEJGPG.js.map} +0 -0
  372. /package/dist/{executor-6LHOV7MN.js.map → executor-IL7SRNUZ.js.map} +0 -0
  373. /package/dist/{executor-EUPYHCLU.js.map → executor-OUA2Y6YC.js.map} +0 -0
  374. /package/dist/{executor-KZ4B3PRT.js.map → executor-WAEI6PDV.js.map} +0 -0
  375. /package/dist/{export-accessible-7XLSZBGJ.js.map → export-accessible-O55RKAGC.js.map} +0 -0
  376. /package/dist/{extract-partition-RM6EROS5.js.map → extract-partition-VBGCBA6Q.js.map} +0 -0
  377. /package/dist/{fanout-sidecar-BDKLP5GG.js.map → fanout-sidecar-CI5BCMQM.js.map} +0 -0
  378. /package/dist/{find-OH4BEULF.js.map → find-FISGUWSK.js.map} +0 -0
  379. /package/dist/{issue-FJE2IHUI.js.map → issue-EQAWIYFQ.js.map} +0 -0
  380. /package/dist/{ledger-EMDHRJJN.js.map → ledger-AJA2SR3C.js.map} +0 -0
  381. /package/dist/{liberate-HFIPHJGR.js.map → liberate-SEGR4JLY.js.map} +0 -0
  382. /package/dist/{link-set-62Y36TOG.js.map → link-set-YBAC2DQT.js.map} +0 -0
  383. /package/dist/{noydb-4ZASGII5.js.map → noydb-AMEOU456.js.map} +0 -0
  384. /package/dist/{policy-SXPLTQS4.js.map → policy-SDGRWVIL.js.map} +0 -0
  385. /package/dist/{post-register-DCJ2SRVG.js.map → post-register-MROIUA5O.js.map} +0 -0
  386. /package/dist/{public-envelope-CBRIJORQ.js.map → public-envelope-J5PLQOW2.js.map} +0 -0
  387. /package/dist/{read-only-facade-5ADRJVTM.js.map → read-only-facade-UCYNRE56.js.map} +0 -0
  388. /package/dist/{register-5NRE7HBZ.js.map → register-OVJKW7BS.js.map} +0 -0
  389. /package/dist/{registry-5QLETK3Q.js.map → registry-5RJSK5AJ.js.map} +0 -0
  390. /package/dist/{registry-DYTNLOJC.js.map → registry-DQRNTWRL.js.map} +0 -0
  391. /package/dist/{registry-HSNQ4NBP.js.map → registry-LTGLERS7.js.map} +0 -0
  392. /package/dist/{request-withdrawal-5APB7RKN.js.map → request-withdrawal-27O77D2V.js.map} +0 -0
  393. /package/dist/{reveal-R5N7JKA5.js.map → reveal-LT3I2K4P.js.map} +0 -0
  394. /package/dist/{revoke-VELFCK7S.js.map → revoke-4N45G7D2.js.map} +0 -0
  395. /package/dist/{signer-IVU3B6DP.js.map → signer-XJCOV7JD.js.map} +0 -0
  396. /package/dist/{stale-DP66HFT3.js.map → stale-IYYXEH3U.js.map} +0 -0
  397. /package/dist/{storage-RH5VBLLL.js.map → storage-Y44CAPE6.js.map} +0 -0
  398. /package/dist/{store-coordination-provider-F5N5ASDF.js.map → store-coordination-provider-WKGAE2WI.js.map} +0 -0
  399. /package/dist/{verify-CAYGXYNO.js.map → verify-GH6CWVSW.js.map} +0 -0
  400. /package/dist/{walk-3SB25HF2.js.map → walk-76UCFXYV.js.map} +0 -0
  401. /package/dist/{withdraw-accessible-LN4AOJDD.js.map → withdraw-accessible-6J2UH3IN.js.map} +0 -0
@@ -1,95 +1,57 @@
1
- import {
2
- ensureCollectionDEK
3
- } from "./chunk-GMZFHDSG.js";
4
1
  import {
5
2
  envelopePayloadHash
6
- } from "./chunk-G5CFMITA.js";
7
- import {
8
- openEnvelopeJson
9
- } from "./chunk-JIW3Q5JN.js";
3
+ } from "./chunk-MSVY7ULZ.js";
10
4
  import {
11
5
  NOYDB_FORMAT_VERSION
12
- } from "./chunk-HCK2DFEK.js";
6
+ } from "./chunk-UNJU345Q.js";
13
7
  import {
14
- encrypt
15
- } from "./chunk-5HZULLHE.js";
8
+ linkI18nVia
9
+ } from "./chunk-3C5CKXFI.js";
16
10
  import {
17
11
  DictKeyMissingError,
18
12
  PermissionDeniedError
19
- } from "./chunk-OJ64XMPJ.js";
13
+ } from "./chunk-CU62BBRB.js";
20
14
 
21
- // src/with-shape/i18n/dictionary.ts
15
+ // src/shape/via-lookup/handle.ts
16
+ function isDeleteMarker(envelope) {
17
+ return envelope._del === true;
18
+ }
22
19
  var DICT_COLLECTION_PREFIX = "_dict_";
23
20
  function dictCollectionName(dictionaryName) {
24
21
  return `${DICT_COLLECTION_PREFIX}${dictionaryName}`;
25
22
  }
26
- function isDictCollectionName(name) {
27
- return name.startsWith(DICT_COLLECTION_PREFIX);
28
- }
29
- function dictKey(name, keysOrMap, opts) {
30
- let keys;
31
- let labels;
32
- if (Array.isArray(keysOrMap)) {
33
- keys = keysOrMap;
34
- labels = opts?.labels;
35
- } else if (keysOrMap && typeof keysOrMap === "object") {
36
- keys = Object.keys(keysOrMap);
37
- labels = keysOrMap;
38
- } else {
39
- keys = void 0;
40
- labels = opts?.labels;
41
- }
42
- return {
43
- _noydbDictKey: true,
44
- name,
45
- keys,
46
- ...opts?.onMissing !== void 0 ? { onMissing: opts.onMissing } : {},
47
- ...opts?.substitute !== void 0 ? { substitute: opts.substitute } : {},
48
- ...labels !== void 0 ? { labels } : {}
49
- };
50
- }
51
- function isDictKeyDescriptor(x) {
52
- return typeof x === "object" && x !== null && x._noydbDictKey === true;
53
- }
54
- function staticDict(name, table, opts) {
55
- return {
56
- _noydbStaticDict: true,
57
- name,
58
- table,
59
- keys: Object.keys(table),
60
- ...opts?.displayLocale !== void 0 ? { displayLocale: opts.displayLocale } : {},
61
- ...opts?.onMissing !== void 0 ? { onMissing: opts.onMissing } : {},
62
- ...opts?.substitute !== void 0 ? { substitute: opts.substitute } : {},
63
- ...opts?.validateCodes !== void 0 ? { validateCodes: opts.validateCodes } : {}
64
- };
65
- }
66
- function isStaticDictDescriptor(x) {
67
- return typeof x === "object" && x !== null && x._noydbStaticDict === true;
68
- }
69
- var DictionaryHandle = class {
70
- constructor(adapter, compartmentName, dictionaryName, keyring, getDEK, encrypted, ledger, options, findAndUpdateReferences, emitter) {
23
+ var LookupHandle = class {
24
+ constructor(adapter, compartmentName, dictionaryName, keyring, reservedEnvelopes, encrypted, ledger, options, findAndUpdateReferences, emitter, buildDeleteMarker, onDirty, onRecordMutated, checkReferencesOnDelete) {
71
25
  this.adapter = adapter;
72
26
  this.compartmentName = compartmentName;
73
27
  this.dictionaryName = dictionaryName;
74
28
  this.keyring = keyring;
75
- this.getDEK = getDEK;
29
+ this.reservedEnvelopes = reservedEnvelopes;
76
30
  this.encrypted = encrypted;
77
31
  this.ledger = ledger;
78
32
  this.options = options;
79
33
  this.findAndUpdateReferences = findAndUpdateReferences;
80
34
  this.emitter = emitter;
35
+ this.buildDeleteMarker = buildDeleteMarker;
36
+ this.onDirty = onDirty;
37
+ this.onRecordMutated = onRecordMutated;
38
+ this.checkReferencesOnDelete = checkReferencesOnDelete;
81
39
  this.collName = dictCollectionName(dictionaryName);
82
40
  }
83
41
  adapter;
84
42
  compartmentName;
85
43
  dictionaryName;
86
44
  keyring;
87
- getDEK;
45
+ reservedEnvelopes;
88
46
  encrypted;
89
47
  ledger;
90
48
  options;
91
49
  findAndUpdateReferences;
92
50
  emitter;
51
+ buildDeleteMarker;
52
+ onDirty;
53
+ onRecordMutated;
54
+ checkReferencesOnDelete;
93
55
  collName;
94
56
  /**
95
57
  * Synchronous write-through cache for dict-join support.
@@ -132,14 +94,6 @@ var DictionaryHandle = class {
132
94
  }
133
95
  }
134
96
  // ─── Internal helpers ─────────────────────────────────────────────
135
- async getDekForDict() {
136
- const resolve = await ensureCollectionDEK(
137
- this.adapter,
138
- this.compartmentName,
139
- this.keyring
140
- );
141
- return resolve(this.collName);
142
- }
143
97
  async encryptEntry(entry, version) {
144
98
  if (!this.encrypted) {
145
99
  return {
@@ -151,25 +105,29 @@ var DictionaryHandle = class {
151
105
  _by: this.keyring.userId
152
106
  };
153
107
  }
154
- const dek = await this.getDekForDict();
155
- const { iv, data } = await encrypt(JSON.stringify(entry), dek);
156
- return {
157
- _noydb: NOYDB_FORMAT_VERSION,
158
- _v: version,
159
- _ts: (/* @__PURE__ */ new Date()).toISOString(),
160
- _iv: iv,
161
- _data: data,
162
- _by: this.keyring.userId
163
- };
108
+ const envelope = await this.reservedEnvelopes.encrypt(this.collName, JSON.stringify(entry), version);
109
+ return { ...envelope, _by: this.keyring.userId };
164
110
  }
165
111
  async decryptEntry(envelope) {
166
112
  if (!this.encrypted) {
167
113
  return JSON.parse(envelope._data);
168
114
  }
169
- const dek = await this.getDekForDict();
170
- const json = await openEnvelopeJson(envelope, dek);
115
+ const json = await this.reservedEnvelopes.decrypt(this.collName, envelope);
171
116
  return JSON.parse(json);
172
117
  }
118
+ /** @internal #650 Task 4 — re-read `key`'s CURRENT adapter truth into `_syncCache` (delete the
119
+ * cache entry when the key is now gone). Called by `Vault._invalidateSyncApplied` after a
120
+ * reserved-lookup row applies via sync, so an already-warmed snapshot/membership/altIndex read
121
+ * never sees a stale verdict for a pulled vocabulary edit — the same invalidation a local
122
+ * put()/delete()/rename() already triggers via `_syncCache.set`/`delete` above. */
123
+ async _refreshSyncCache(key) {
124
+ const envelope = await this.adapter.get(this.compartmentName, this.collName, key);
125
+ if (!envelope || isDeleteMarker(envelope)) {
126
+ this._syncCache.delete(key);
127
+ return;
128
+ }
129
+ this._syncCache.set(key, await this.decryptEntry(envelope));
130
+ }
173
131
  // ─── Public API ───────────────────────────────────────────────────
174
132
  /**
175
133
  * Add or overwrite a single dictionary entry.
@@ -195,12 +153,14 @@ var DictionaryHandle = class {
195
153
  existing ? existing._v : void 0
196
154
  );
197
155
  this._syncCache.set(key, entry);
156
+ await this.onDirty?.(this.collName, key, "put", version);
198
157
  this.emitter.emit("change", {
199
158
  vault: this.compartmentName,
200
159
  collection: this.collName,
201
160
  id: key,
202
161
  action: "put"
203
162
  });
163
+ await this.onRecordMutated?.(this.collName, key, "put", version);
204
164
  if (this.ledger) {
205
165
  await this.ledger.append({
206
166
  op: "put",
@@ -236,7 +196,7 @@ var DictionaryHandle = class {
236
196
  this.collName,
237
197
  key
238
198
  );
239
- if (!envelope) return null;
199
+ if (!envelope || isDeleteMarker(envelope)) return null;
240
200
  const entry = await this.decryptEntry(envelope);
241
201
  return entry.labels;
242
202
  }
@@ -246,6 +206,10 @@ var DictionaryHandle = class {
246
206
  * Default mode is `'strict'` — throws `DictKeyInUseError` if any
247
207
  * registered collection has a record referencing this key. Pass
248
208
  * `{ mode: 'warn' }` to skip the check (dev-mode cleanup only).
209
+ *
210
+ * Under sync, deletion writes a version-ordered delete-marker row rather than removing the
211
+ * adapter key outright (#647 fix wave 1) — markers accumulate over time; GC is future work,
212
+ * acceptable for vocabulary-scale data.
249
213
  */
250
214
  async delete(key, opts = {}) {
251
215
  this.requireWriteAccess();
@@ -254,20 +218,28 @@ var DictionaryHandle = class {
254
218
  this.collName,
255
219
  key
256
220
  );
257
- if (!existing) {
221
+ if (!existing || isDeleteMarker(existing)) {
258
222
  throw new DictKeyMissingError(this.dictionaryName, key);
259
223
  }
260
224
  const mode = opts.mode ?? "strict";
261
- if (mode === "strict" && this.findAndUpdateReferences) {
225
+ if (mode === "strict") {
226
+ await this.checkReferencesOnDelete?.(key);
227
+ }
228
+ const nextVersion = existing._v + 1;
229
+ if (this.onDirty) {
230
+ await this.adapter.put(this.compartmentName, this.collName, key, this.buildDeleteMarker(nextVersion, this.keyring.userId));
231
+ } else {
232
+ await this.adapter.delete(this.compartmentName, this.collName, key);
262
233
  }
263
- await this.adapter.delete(this.compartmentName, this.collName, key);
264
234
  this._syncCache.delete(key);
235
+ await this.onDirty?.(this.collName, key, "put", nextVersion);
265
236
  this.emitter.emit("change", {
266
237
  vault: this.compartmentName,
267
238
  collection: this.collName,
268
239
  id: key,
269
240
  action: "delete"
270
241
  });
242
+ await this.onRecordMutated?.(this.collName, key, "delete", nextVersion);
271
243
  if (this.ledger) {
272
244
  await this.ledger.append({
273
245
  op: "delete",
@@ -308,7 +280,7 @@ var DictionaryHandle = class {
308
280
  this.collName,
309
281
  oldKey
310
282
  );
311
- if (!existing) {
283
+ if (!existing || isDeleteMarker(existing)) {
312
284
  throw new DictKeyMissingError(this.dictionaryName, oldKey);
313
285
  }
314
286
  const oldEntry = await this.decryptEntry(existing);
@@ -323,21 +295,30 @@ var DictionaryHandle = class {
323
295
  if (this.findAndUpdateReferences) {
324
296
  await this.findAndUpdateReferences(this.dictionaryName, oldKey, newKey);
325
297
  }
326
- await this.adapter.delete(this.compartmentName, this.collName, oldKey);
298
+ const oldKeyNextVersion = existing._v + 1;
299
+ if (this.onDirty) {
300
+ await this.adapter.put(this.compartmentName, this.collName, oldKey, this.buildDeleteMarker(oldKeyNextVersion, this.keyring.userId));
301
+ } else {
302
+ await this.adapter.delete(this.compartmentName, this.collName, oldKey);
303
+ }
327
304
  this._syncCache.delete(oldKey);
328
305
  this._syncCache.set(newKey, newEntry);
306
+ await this.onDirty?.(this.collName, oldKey, "put", oldKeyNextVersion);
329
307
  this.emitter.emit("change", {
330
308
  vault: this.compartmentName,
331
309
  collection: this.collName,
332
310
  id: oldKey,
333
311
  action: "delete"
334
312
  });
313
+ await this.onRecordMutated?.(this.collName, oldKey, "delete", oldKeyNextVersion);
314
+ await this.onDirty?.(this.collName, newKey, "put", 1);
335
315
  this.emitter.emit("change", {
336
316
  vault: this.compartmentName,
337
317
  collection: this.collName,
338
318
  id: newKey,
339
319
  action: "put"
340
320
  });
321
+ await this.onRecordMutated?.(this.collName, newKey, "put", 1);
341
322
  if (this.ledger) {
342
323
  await this.ledger.append({
343
324
  op: "delete",
@@ -371,7 +352,10 @@ var DictionaryHandle = class {
371
352
  this.collName,
372
353
  key
373
354
  );
374
- if (!envelope) continue;
355
+ if (!envelope || isDeleteMarker(envelope)) {
356
+ this._syncCache.delete(key);
357
+ continue;
358
+ }
375
359
  const entry = await this.decryptEntry(envelope);
376
360
  entries.push(entry);
377
361
  this._syncCache.set(key, entry);
@@ -403,14 +387,76 @@ var DictionaryHandle = class {
403
387
  }
404
388
  };
405
389
 
390
+ // src/shape/via-i18n/dictionary.ts
391
+ function dictKey(name, keysOrMap, opts) {
392
+ linkI18nVia();
393
+ let keys;
394
+ let labels;
395
+ if (Array.isArray(keysOrMap)) {
396
+ keys = keysOrMap;
397
+ labels = opts?.labels;
398
+ } else if (keysOrMap && typeof keysOrMap === "object") {
399
+ keys = Object.keys(keysOrMap);
400
+ labels = keysOrMap;
401
+ } else {
402
+ keys = void 0;
403
+ labels = opts?.labels;
404
+ }
405
+ const equiv = {
406
+ _viaBrand: "lookup",
407
+ dimension: name,
408
+ key: "id",
409
+ vocabulary: "open",
410
+ backing: "reserved",
411
+ onDelete: "restrict",
412
+ ...keys !== void 0 ? { keys } : {},
413
+ ...opts?.onMissing !== void 0 ? { onMissing: opts.onMissing } : {},
414
+ ...opts?.substitute !== void 0 ? { substitute: opts.substitute } : {},
415
+ ...labels !== void 0 ? { labels } : {}
416
+ };
417
+ return {
418
+ _noydbDictKey: true,
419
+ _viaBrand: "i18n",
420
+ name: equiv.dimension,
421
+ keys: equiv.keys,
422
+ ...equiv.onMissing !== void 0 ? { onMissing: equiv.onMissing } : {},
423
+ ...equiv.substitute !== void 0 ? { substitute: equiv.substitute } : {},
424
+ ...equiv.labels !== void 0 ? { labels: equiv.labels } : {}
425
+ };
426
+ }
427
+ function staticDict(name, table, opts) {
428
+ linkI18nVia();
429
+ const equiv = {
430
+ _viaBrand: "lookup",
431
+ dimension: name,
432
+ key: "id",
433
+ vocabulary: "closed",
434
+ backing: "static",
435
+ onDelete: "restrict",
436
+ table,
437
+ keys: Object.keys(table),
438
+ ...opts?.displayLocale !== void 0 ? { displayLocale: opts.displayLocale } : {},
439
+ ...opts?.onMissing !== void 0 ? { onMissing: opts.onMissing } : {},
440
+ ...opts?.substitute !== void 0 ? { substitute: opts.substitute } : {}
441
+ };
442
+ return {
443
+ _noydbStaticDict: true,
444
+ _viaBrand: "i18n",
445
+ name: equiv.dimension,
446
+ table: equiv.table,
447
+ keys: equiv.keys,
448
+ ...equiv.displayLocale !== void 0 ? { displayLocale: equiv.displayLocale } : {},
449
+ ...equiv.onMissing !== void 0 ? { onMissing: equiv.onMissing } : {},
450
+ ...equiv.substitute !== void 0 ? { substitute: equiv.substitute } : {},
451
+ ...opts?.validateCodes !== void 0 ? { validateCodes: opts.validateCodes } : {}
452
+ };
453
+ }
454
+
406
455
  export {
407
456
  DICT_COLLECTION_PREFIX,
408
457
  dictCollectionName,
409
- isDictCollectionName,
458
+ LookupHandle,
410
459
  dictKey,
411
- isDictKeyDescriptor,
412
- staticDict,
413
- isStaticDictDescriptor,
414
- DictionaryHandle
460
+ staticDict
415
461
  };
416
- //# sourceMappingURL=chunk-WFBZRXS5.js.map
462
+ //# sourceMappingURL=chunk-IIP5WFVT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shape/via-lookup/handle.ts","../src/shape/via-i18n/dictionary.ts"],"sourcesContent":["/**\n * `_dict_*` reserved-collection storage engine — `LookupHandle`.\n *\n * Verbatim move of `DictionaryHandle` out of `shape/via-i18n/dictionary.ts`\n * (#650 Task 1 — via-lookup extraction, phase D). The class is renamed to\n * `LookupHandle` (the dict tier is one backing kind the eventual lookup\n * layer supports); `shape/via-i18n/dictionary.ts` re-exports it as\n * `DictionaryHandle` for one release so existing importers compile\n * unchanged. `DictEntry` / `DictionaryOptions` / `DICT_COLLECTION_PREFIX` /\n * `dictCollectionName` move with it — they were exclusively consumed by\n * this class's constructor/methods.\n *\n * No behavior change from the pre-move class: same encryption door\n * (`reservedEnvelopes('_dict_')`, #629 Task 4), same ACL, same ledger\n * shape, same sync-cache/snapshot contract for dict joins.\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport type { NoydbEventEmitter } from '../../kernel/events.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from '../../with-party/team/keyring.js'\nimport type { ViaCryptoCtx } from '../../kernel/via.js'\nimport type { LedgerStore } from '../../with-commit/history/ledger/store.js'\nimport { envelopePayloadHash } from '../../with-commit/history/ledger/hash.js'\nimport {\n PermissionDeniedError,\n DictKeyMissingError,\n} from '../../kernel/errors.js'\n\n/**\n * Delete-marker predicate, duplicated (not imported) from\n * `kernel/enclave/record-keys/tombstone.ts`'s `isDeleteMarker` (#647 fix wave 1).\n * `shape/via-lookup/**` may not statically import `kernel/enclave/` — not even the barrel\n * (Check 15, `via-enclave-isolation`) — and this predicate carries zero crypto/protected-body\n * surface (just the `_del` protocol-header field, not `_iv`/`_data`), so duplicating it here\n * mirrors the same call `port/with/i18n-strategy.ts` already makes for `isDictCollectionName`\n * (see its doc comment). Keep the two in sync if the marker shape ever changes.\n *\n * `buildDeleteMarker` (which DOES construct the protected `_iv`/`_data` body fields — Check 11,\n * `enclave-body-only`) can't be duplicated the same way; it's injected via the constructor\n * instead, built from the real `kernel/enclave` function at the Vault call site.\n */\nfunction isDeleteMarker(envelope: EncryptedEnvelope): boolean {\n return envelope._del === true\n}\n\n/** Reserved collection name prefix. Never collides with user collections. */\nexport const DICT_COLLECTION_PREFIX = '_dict_'\n\n/** Return the adapter collection name for a named dictionary. */\nexport function dictCollectionName(dictionaryName: string): string {\n return `${DICT_COLLECTION_PREFIX}${dictionaryName}`\n}\n\n// ─── Dictionary entry shape ────────────────────────────────────────────\n\n/**\n * One entry in a `_dict_*` collection. The record `id` (adapter-side\n * key) IS the stable dictionary key (e.g. `'paid'`). The `labels`\n * record maps locale codes to display strings.\n */\nexport interface DictEntry {\n /** Stable key — same as the record id in the adapter. */\n readonly key: string\n /** Locale → label map, e.g. `{ en: 'Paid', th: 'ชำระแล้ว' }`. */\n readonly labels: Record<string, string>\n}\n\n// ─── Per-dictionary options ────────────────────────────────────────────\n\n/**\n * Options for `vault.dictionary(name, options?)`.\n *\n * `writableBy` controls the minimum role for write operations (put,\n * putAll, delete, rename). Defaults to `'admin'` to match the standard\n * \"dictionary contents are owned by admins\" convention; set to\n * `'operator'` for user-editable dictionaries like custom tags.\n */\nexport interface DictionaryOptions {\n /** Minimum role allowed to write dictionary entries. Default: `'admin'`. */\n readonly writableBy?: 'owner' | 'admin' | 'operator'\n}\n\n// ─── LookupHandle ───────────────────────────────────────────────────────\n\n/**\n * Handle to a named dictionary within a vault.\n *\n * Obtained via `vault.dictionary(name)`. Provides strongly-typed\n * CRUD for dictionary entries, plus the `rename()` operation that is the\n * only sanctioned mass-mutation path for dictKey fields.\n *\n * All writes are encrypted under the compartment's DEK for the\n * `_dict_<name>` collection. Adapters never see plaintext.\n */\nexport class LookupHandle<Keys extends string = string> {\n private readonly collName: string\n\n /**\n * Synchronous write-through cache for dict-join support.\n * Populated on every `put()`, `delete()`, and `rename()`. The snapshot\n * is built from this cache by `snapshotEntries()` — the query executor\n * calls this synchronously inside `.toArray()`.\n *\n * `null` means \"not yet initialized\" — callers should use `list()`\n * to warm the cache before using dict joins on pre-existing data.\n */\n private readonly _syncCache = new Map<string, DictEntry>()\n\n /**\n * Return all cached entries as `{ key, labels, ...labels }` records —\n * usable synchronously by the join executor's `snapshot()` call.\n * Returns an empty array when the cache has never been populated.\n */\n snapshotEntries(): readonly Record<string, unknown>[] {\n return Array.from(this._syncCache.values()).map((e) => ({\n key: e.key,\n labels: e.labels,\n ...e.labels,\n }))\n }\n\n constructor(\n private readonly adapter: NoydbStore,\n private readonly compartmentName: string,\n private readonly dictionaryName: string,\n private readonly keyring: UnlockedKeyring,\n /**\n * The `reservedEnvelopes('_dict_')` capability — this handle's sanctioned\n * crypto door onto its `_dict_<name>` collection (#629 Task 4). Bound by\n * the Vault; never a keyring, raw DEK, or enclave import.\n */\n private readonly reservedEnvelopes: ReturnType<ViaCryptoCtx['reservedEnvelopes']>,\n private readonly encrypted: boolean,\n private readonly ledger: LedgerStore | undefined,\n private readonly options: DictionaryOptions,\n /**\n * Callback provided by the Vault to find and rewrite records\n * in any registered collection that has a dictKeyField pointing at\n * this dictionary, used by `rename()`.\n */\n private readonly findAndUpdateReferences:\n | ((\n dictionaryName: string,\n oldKey: string,\n newKey: string,\n ) => Promise<void>)\n | undefined,\n private readonly emitter: NoydbEventEmitter,\n /**\n * #647 fix wave 1 — mints a version-ordered delete-marker envelope (the reserved-tier\n * mirror of #589's ordinary-collection delete marker). Injected rather than imported:\n * `buildDeleteMarker` constructs the envelope's protected-body fields (`_iv`/`_data`), and\n * `shape/via-lookup/**` may not reach `kernel/enclave/` directly (Check 11\n * `enclave-body-only` / Check 15 `via-enclave-isolation`) — the Vault binds the real\n * `kernel/enclave` function at construction time, same pattern as `reservedEnvelopes` above.\n */\n private readonly buildDeleteMarker: (version: number, actor: string) => EncryptedEnvelope,\n /** #650 Task 4 (#647) — dirty-log participation hook (origin `local-write`), threaded from `BuildLookupHandleOptions`. */\n private readonly onDirty?: (collection: string, id: string, action: 'put' | 'delete', version: number) => Promise<void>,\n /** #650 Task 4 — thin `graphDispatch.collect`-equivalent; opens/collects/flushes a one-shot wave for this touch. */\n private readonly onRecordMutated?: (collection: string, id: string, action: 'put' | 'delete', version: number) => Promise<void>,\n /** #650 Task 5 (#648) — the real reference check `delete()`'s strict branch calls: restrict\n * throws `DictKeyInUseError`, cascade/nullify apply their propagation. Vault-bound; a no-op\n * when the dimension has no declared lookup-referencing edges. */\n private readonly checkReferencesOnDelete?: (key: string) => Promise<unknown>,\n ) {\n this.collName = dictCollectionName(dictionaryName)\n }\n\n // ─── Access checks ────────────────────────────────────────────────\n\n private requireWriteAccess(): void {\n const minRole = this.options.writableBy ?? 'admin'\n const roleRank: Record<string, number> = {\n client: 1,\n viewer: 2,\n operator: 3,\n admin: 4,\n owner: 5,\n }\n const callerRank = roleRank[this.keyring.role] ?? 0\n const requiredRank = roleRank[minRole] ?? 4\n if (callerRank < requiredRank) {\n throw new PermissionDeniedError(\n `Dictionary \"${this.dictionaryName}\" writes require \"${minRole}\" role or above. ` +\n `Current role: \"${this.keyring.role}\".`,\n )\n }\n }\n\n // ─── Internal helpers ─────────────────────────────────────────────\n\n private async encryptEntry(entry: DictEntry, version: number): Promise<EncryptedEnvelope> {\n if (!this.encrypted) {\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: version,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: JSON.stringify(entry),\n _by: this.keyring.userId,\n }\n }\n const envelope = await this.reservedEnvelopes.encrypt(this.collName, JSON.stringify(entry), version)\n return { ...envelope, _by: this.keyring.userId }\n }\n\n private async decryptEntry(envelope: EncryptedEnvelope): Promise<DictEntry> {\n if (!this.encrypted) {\n return JSON.parse(envelope._data) as DictEntry\n }\n const json = await this.reservedEnvelopes.decrypt(this.collName, envelope)\n return JSON.parse(json) as DictEntry\n }\n\n /** @internal #650 Task 4 — re-read `key`'s CURRENT adapter truth into `_syncCache` (delete the\n * cache entry when the key is now gone). Called by `Vault._invalidateSyncApplied` after a\n * reserved-lookup row applies via sync, so an already-warmed snapshot/membership/altIndex read\n * never sees a stale verdict for a pulled vocabulary edit — the same invalidation a local\n * put()/delete()/rename() already triggers via `_syncCache.set`/`delete` above. */\n async _refreshSyncCache(key: string): Promise<void> {\n const envelope = await this.adapter.get(this.compartmentName, this.collName, key)\n if (!envelope || isDeleteMarker(envelope)) { this._syncCache.delete(key); return }\n this._syncCache.set(key, await this.decryptEntry(envelope))\n }\n\n // ─── Public API ───────────────────────────────────────────────────\n\n /**\n * Add or overwrite a single dictionary entry.\n *\n * @param key The stable key to store (e.g. `'paid'`).\n * @param labels Locale → label map (e.g. `{ en: 'Paid', th: 'ชำระแล้ว' }`).\n */\n async put(key: Keys, labels: Record<string, string>): Promise<void> {\n this.requireWriteAccess()\n\n const entry: DictEntry = { key, labels }\n const existing = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n const version = existing ? existing._v + 1 : 1\n const envelope = await this.encryptEntry(entry, version)\n\n await this.adapter.put(\n this.compartmentName,\n this.collName,\n key,\n envelope,\n existing ? existing._v : undefined,\n )\n\n // Maintain synchronous cache for dict-join snapshot\n this._syncCache.set(key, entry)\n\n // #650 Task 4 (#647) — choke-point participation: dirty-log tracking BEFORE the emit (mirrors\n // Collection._onRecordMutated's local-write order), then the wave-open hook AFTER.\n await this.onDirty?.(this.collName, key, 'put', version)\n\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: key,\n action: 'put',\n })\n\n await this.onRecordMutated?.(this.collName, key, 'put', version)\n\n if (this.ledger) {\n await this.ledger.append({\n op: 'put',\n collection: this.collName,\n id: key,\n version,\n actor: this.keyring.userId,\n // — must be the real envelope hash so\n // vault.verifyBackupIntegrity()'s data-cross-check matches.\n payloadHash: await envelopePayloadHash(envelope),\n })\n }\n }\n\n /**\n * Batch-add or overwrite multiple dictionary entries in one call.\n *\n * @param entries `{ key: { locale: label } }` map.\n */\n async putAll(entries: Record<Keys, Record<string, string>>): Promise<void> {\n this.requireWriteAccess()\n for (const [key, labels] of Object.entries(entries) as [Keys, Record<string, string>][]) {\n await this.put(key, labels)\n }\n }\n\n /**\n * Load the label map for a single key.\n *\n * @returns The label map, or `null` if the key doesn't exist.\n */\n async get(key: Keys): Promise<Record<string, string> | null> {\n const envelope = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n if (!envelope || isDeleteMarker(envelope)) return null\n const entry = await this.decryptEntry(envelope)\n return entry.labels\n }\n\n /**\n * Delete a dictionary key.\n *\n * Default mode is `'strict'` — throws `DictKeyInUseError` if any\n * registered collection has a record referencing this key. Pass\n * `{ mode: 'warn' }` to skip the check (dev-mode cleanup only).\n *\n * Under sync, deletion writes a version-ordered delete-marker row rather than removing the\n * adapter key outright (#647 fix wave 1) — markers accumulate over time; GC is future work,\n * acceptable for vocabulary-scale data.\n */\n async delete(key: Keys, opts: { mode?: 'strict' | 'warn' } = {}): Promise<void> {\n this.requireWriteAccess()\n\n const existing = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n if (!existing || isDeleteMarker(existing)) {\n throw new DictKeyMissingError(this.dictionaryName, key)\n }\n\n // #650 Task 5 (#648) — the real reference check: restrict throws `DictKeyInUseError` naming\n // the referencing collection, cascade/nullify apply their propagation. `mode: 'warn'` skips\n // this entirely (today's dev-mode-cleanup escape hatch, unconditional delete). A dimension\n // with no declared lookup-referencing edges is a no-op (undeclared refs keep dangling).\n const mode = opts.mode ?? 'strict'\n if (mode === 'strict') {\n await this.checkReferencesOnDelete?.(key)\n }\n\n // #647 fix wave 1 — mirrors Collection._doDelete's identical `this.onDirty`-gated branch\n // (#589): under sync, a raw adapter.delete() is invisible to a peer's next pull (the key\n // simply stops appearing in remote.list()) — a deleted key never propagates and can be\n // silently resurrected by a later edit. Write a version-ordered DELETE-MARKER instead, so\n // the removal round-trips like any other row. No-op (plain adapter.delete) when sync isn't wired.\n const nextVersion = existing._v + 1\n if (this.onDirty) {\n await this.adapter.put(this.compartmentName, this.collName, key, this.buildDeleteMarker(nextVersion, this.keyring.userId))\n } else {\n await this.adapter.delete(this.compartmentName, this.collName, key)\n }\n\n // Maintain synchronous cache for dict-join snapshot\n this._syncCache.delete(key)\n\n // #650 Task 4 (#647) — same choke-point ordering as put() above.\n // #647 fix wave 1: the dirty entry's ACTION is 'put' (not 'delete') — mirrors\n // Collection._onRecordMutated's 'local-delete' case exactly: the marker rides push's\n // CAS-put path, not push's raw remote.delete() path (which would strip the marker off the\n // remote too and reopen this same hole). The version is the MARKER's version, so push's CAS\n // (expectedVersion = entry.version - 1) matches the pre-delete remote copy.\n await this.onDirty?.(this.collName, key, 'put', nextVersion)\n\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: key,\n action: 'delete',\n })\n\n await this.onRecordMutated?.(this.collName, key, 'delete', nextVersion)\n\n if (this.ledger) {\n await this.ledger.append({\n op: 'delete',\n collection: this.collName,\n id: key,\n version: existing._v,\n actor: this.keyring.userId,\n // — for delete the prior envelope is what was just\n // removed; we hash it so the chain captures intent. The\n // verifyBackupIntegrity data-cross-check skips delete\n // entries entirely (the live record is gone), but the\n // chain still benefits from a stable non-empty hash.\n payloadHash: await envelopePayloadHash(existing),\n })\n }\n }\n\n /**\n * Rename a dictionary key — the only sanctioned mass-mutation path.\n *\n * Atomically:\n * 1. Adds the new key with the same labels as the old key.\n * 2. Updates every registered record that stores the old key to\n * store the new key instead.\n * 3. Deletes the old key.\n * 4. Appends a single ledger entry recording the rename.\n *\n * Respects ACL: throws `PermissionDeniedError` before any mutation\n * if the caller can't write. The cascade is best-effort atomic\n * within this call — no two-phase commit across adapter calls.\n *\n * Cascade-on-delete is NOT supported. Use `rename()` when you need\n * to change a key that records reference.\n */\n async rename(oldKey: Keys, newKey: string): Promise<void> {\n this.requireWriteAccess()\n\n // 1. Load old entry\n const existing = await this.adapter.get(\n this.compartmentName,\n this.collName,\n oldKey,\n )\n if (!existing || isDeleteMarker(existing)) {\n throw new DictKeyMissingError(this.dictionaryName, oldKey)\n }\n const oldEntry = await this.decryptEntry(existing)\n\n // 2. Write new key\n const newEntry: DictEntry = { key: newKey, labels: oldEntry.labels }\n const newEnvelope = await this.encryptEntry(newEntry, 1)\n await this.adapter.put(\n this.compartmentName,\n this.collName,\n newKey,\n newEnvelope,\n )\n\n // 3. Update all referencing records in registered collections\n if (this.findAndUpdateReferences) {\n await this.findAndUpdateReferences(this.dictionaryName, oldKey, newKey)\n }\n\n // 4. Remove old key — #647 fix wave 1: same delete-marker convention as delete() above, so\n // the old key's removal propagates on sync instead of leaving a phantom stale row on a peer\n // that only observes the new key arriving.\n const oldKeyNextVersion = existing._v + 1\n if (this.onDirty) {\n await this.adapter.put(this.compartmentName, this.collName, oldKey, this.buildDeleteMarker(oldKeyNextVersion, this.keyring.userId))\n } else {\n await this.adapter.delete(this.compartmentName, this.collName, oldKey)\n }\n\n // Maintain synchronous cache for dict-join snapshot\n this._syncCache.delete(oldKey)\n this._syncCache.set(newKey, newEntry)\n\n // #650 Task 4 (#647) — same choke-point ordering as put()/delete() above, for BOTH touches.\n // #647 fix wave 1: oldKey's dirty action is 'put' (not 'delete') — see delete()'s comment\n // above for why the marker must ride push's CAS-put path, at the marker's version.\n await this.onDirty?.(this.collName, oldKey, 'put', oldKeyNextVersion)\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: oldKey,\n action: 'delete',\n })\n await this.onRecordMutated?.(this.collName, oldKey, 'delete', oldKeyNextVersion)\n\n await this.onDirty?.(this.collName, newKey, 'put', 1)\n this.emitter.emit('change', {\n vault: this.compartmentName,\n collection: this.collName,\n id: newKey,\n action: 'put',\n })\n await this.onRecordMutated?.(this.collName, newKey, 'put', 1)\n\n // 5. Ledger — record the rename as delete(oldKey) + put(newKey)\n // so verifyBackupIntegrity()'s data-cross-check matches reality\n // (the oldKey envelope is gone; the newKey envelope is what was\n // just written). Two entries instead of one — the chain still\n // captures the rename intent via the matching ts + actor.\n if (this.ledger) {\n await this.ledger.append({\n op: 'delete',\n collection: this.collName,\n id: oldKey,\n version: existing._v,\n actor: this.keyring.userId,\n payloadHash: await envelopePayloadHash(existing),\n })\n await this.ledger.append({\n op: 'put',\n collection: this.collName,\n id: newKey,\n version: 1,\n actor: this.keyring.userId,\n payloadHash: await envelopePayloadHash(newEnvelope),\n })\n }\n }\n\n /**\n * List all entries in this dictionary.\n *\n * @returns Array of `{ key, labels }` objects.\n */\n async list(): Promise<DictEntry[]> {\n const keys = await this.adapter.list(this.compartmentName, this.collName)\n const entries: DictEntry[] = []\n for (const key of keys) {\n const envelope = await this.adapter.get(\n this.compartmentName,\n this.collName,\n key,\n )\n // #647 fix wave 1 — a delete-marker row is a removed key, not a live entry: exclude it\n // from the listing and drop any stale cache entry a prior warm may have left under it.\n if (!envelope || isDeleteMarker(envelope)) { this._syncCache.delete(key); continue }\n const entry = await this.decryptEntry(envelope)\n entries.push(entry)\n // Warm the synchronous cache\n this._syncCache.set(key, entry)\n }\n return entries\n }\n\n /**\n * Resolve a key to its label for the given locale.\n *\n * Used by the collection's locale-aware read path to populate\n * `<field>Label` virtual fields. Returns `undefined` when the\n * key doesn't exist or has no label for the requested locale\n * (after exhausting the fallback chain).\n */\n async resolveLabel(\n key: string,\n locale: string,\n fallback?: string | readonly string[],\n ): Promise<string | undefined> {\n const labels = await this.get(key as Keys)\n if (!labels) return undefined\n\n // Try primary locale\n if (labels[locale] !== undefined) return labels[locale]\n\n // Try fallback chain\n const chain = Array.isArray(fallback) ? (fallback as readonly string[]) : fallback ? [fallback as string] : []\n for (const fb of chain) {\n if (fb === 'any') {\n // Return any available label\n const any = Object.values(labels)[0]\n if (any !== undefined) return any\n } else if (labels[fb] !== undefined) {\n return labels[fb]\n }\n }\n\n return undefined\n }\n}\n\n// Compat alias — existing importers of `DictionaryHandle` (the pre-#650\n// name) keep compiling unchanged. `shape/via-i18n/dictionary.ts` re-exports\n// this same binding for one release.\nexport { LookupHandle as DictionaryHandle }\n","/**\n * _dict_* reserved collections + dictKey schema descriptor —\n *\n * Stores bounded enum-like field dictionaries as reserved encrypted\n * collections (`_dict_<name>/`) within a vault. Each dictionary\n * entry maps a stable key (e.g. `'paid'`) to a locale → label record\n * (e.g. `{ en: 'Paid', th: 'ชำระแล้ว' }`).\n *\n * Design decisions\n * ────────────────\n *\n * **Why reserved collections, not a separate store?**\n * Same answer as `_sync_credentials`: the compartment's existing\n * encryption stack is exactly right. Dictionaries are encrypted under the\n * same vault DEK, inherit ACL, ledger, and backup/restore for free.\n *\n * **One collection per dictionary, not one collection with namespaces.**\n * Each `_dict_<name>/` collection holds entries `{ id: key, labels: {...} }`.\n * This composes with `ref()` naturally (a dictKey IS a ref to the dict\n * collection), and means the query DSL works over dictionary entries\n * without any special-casing.\n *\n * **dictKey() is a descriptor, not a Zod type.**\n * The descriptor pattern matches `ref()`: declare NOYDB-specific metadata\n * in the collection options alongside `refs`. TypeScript inference comes\n * from the descriptor's generic parameter, not from Zod internals.\n *\n * API:\n * `dictKey(name, keys?)` — returns a DictKeyDescriptor\n * `vault.dictionary(name)` — returns a DictionaryHandle\n * `DictionaryHandle.put/putAll/get/delete/rename/list` — CRUD\n *\n * The storage engine (`DictionaryHandle`, `DictEntry`, `DictionaryOptions`,\n * `DICT_COLLECTION_PREFIX`, `dictCollectionName`) moved to\n * `shape/via-lookup/handle.ts` (#650 Task 1 — via-lookup extraction, phase\n * D of the Via port) and is renamed `LookupHandle` there; re-exported here\n * under the original names for one release so existing importers of this\n * module compile unchanged. This file keeps only the descriptor factories\n * (`dictKey`/`staticDict`), which are locale/i18n-coupled and stay put.\n */\n\nimport type { OnMissingPolicy } from './policy.js'\nimport { linkI18nVia } from './binding.js'\n// #650 Task 2 — type-only: dictKey()/staticDict() construct the equivalent\n// LookupDescriptor shape internally (the reserved/static tiers via-lookup's\n// dict()/lookup(static) also produce) before adapting it to their own\n// legacy public return shape. No runtime dependency on shape/via-lookup.\nimport type { LookupDescriptor } from '../via-lookup/descriptor.js'\n\n// `isDictCollectionName` now lives on the kernel port (#623 Task 7,\n// port/with/i18n-strategy.ts) — re-exported here for compat. Its `'_dict_'`\n// check is a duplicate of DICT_COLLECTION_PREFIX (now on\n// shape/via-lookup/handle.ts, not an import, to avoid a port→feature value\n// dependency); keep the two in sync.\nexport { isDictCollectionName } from '../../port/with/i18n-strategy.js'\n\n// Storage-engine re-exports (#650 Task 1 — moved to shape/via-lookup/handle.ts).\nexport {\n DictionaryHandle,\n DICT_COLLECTION_PREFIX,\n dictCollectionName,\n type DictEntry,\n type DictionaryOptions,\n} from '../via-lookup/handle.js'\n\n// ─── DictKey descriptor ────────────────────────────────────────────────\n\n/**\n * Descriptor returned by `dictKey()`. Attach to the collection's\n * `dictKeyFields` option to declare which fields are dictionary-backed:\n *\n * ```ts\n * const invoices = company.collection<Invoice>('invoices', {\n * dictKeyFields: {\n * status: dictKey('status', ['draft', 'open', 'paid'] as const),\n * },\n * })\n * ```\n *\n * The generic parameter `Keys` narrows the TypeScript type of the field\n * to a literal union; the runtime value of `keys` is used by `put()`\n * validation to reject unknown keys when a key set is declared.\n */\nexport interface DictKeyDescriptor<Keys extends string = string> {\n readonly _noydbDictKey: true\n /** Via port brand marker (#623 Task 9) — dictKey shares the i18n binding brand. */\n readonly _viaBrand: 'i18n'\n /** Which dictionary this field references. */\n readonly name: string\n /** Declared valid keys. When set, `put()` rejects keys not in this set. */\n readonly keys: readonly Keys[] | undefined\n /**\n * What to do when a label is missing for the resolved locale. Mirrors\n * `i18nText`'s `onMissing`. Default behavior (when unset) preserves the\n * legacy contract: a missing label is omitted (scalar) or `null`\n * (array element) — i.e. `'null'`. Set `'substitute'` to walk the\n * declared `substitute` chain, or `'throw'` to raise.\n */\n readonly onMissing?: OnMissingPolicy\n /** Ordered preferred-substitute locales for label resolution. */\n readonly substitute?: readonly string[]\n /**\n * Optional inline display labels (value → label map). A SYNC display\n * fallback for `describe()` when async `dictLabels` (from `_dict_`) are\n * not resolved. The async path still wins when available.\n */\n readonly labels?: Record<string, string>\n}\n\n/**\n * Create a `DictKeyDescriptor` for a dictionary-backed enum field.\n *\n * @param name The dictionary name (corresponds to `_dict_<name>` collection).\n * @param keysOrMap Either:\n * - An `as const` array of valid key literals — narrows the TypeScript type\n * to a literal union and enables put-time validation.\n * - A **value→label map** (`{ draft: 'Draft', paid: 'Paid' }`) — keys are\n * inferred from the map, and the labels are stored as inline display\n * defaults surfaced by `describe()` without a store read.\n * @param opts `{ labels?, onMissing?, substitute? }` when the array form\n * is used; `opts.labels` provides inline display defaults for\n * specific keys (same semantics as the map form, but\n * explicit key order from the array is preserved).\n *\n * @example\n * ```ts\n * // array form (original):\n * dictKey('status', ['draft', 'open', 'paid'] as const)\n *\n * // value→label map form (keys inferred, inline labels for describe()):\n * dictKey('status', { draft: 'Draft', open: 'Open', paid: 'Paid' })\n *\n * // array + opts.labels (preserves explicit order, partial labels OK):\n * dictKey('status', ['draft', 'open', 'paid'] as const, { labels: { paid: 'Paid' } })\n * ```\n */\nexport function dictKey<Keys extends string>(\n name: string,\n keysOrMap?: readonly Keys[] | Record<Keys, string>,\n opts?: { onMissing?: OnMissingPolicy; substitute?: readonly string[]; labels?: Record<string, string> },\n): DictKeyDescriptor<Keys> {\n // The declaration is the Via binding's opt-in unit (#553) — same pattern\n // as i18nText()/money().\n linkI18nVia()\n let keys: readonly Keys[] | undefined\n let labels: Record<string, string> | undefined\n if (Array.isArray(keysOrMap)) {\n keys = keysOrMap as readonly Keys[]\n labels = opts?.labels\n } else if (keysOrMap && typeof keysOrMap === 'object') {\n keys = Object.keys(keysOrMap) as Keys[]\n labels = keysOrMap as Record<string, string>\n } else {\n keys = undefined\n labels = opts?.labels\n }\n // #650 Task 2 — dictKey is the reserved-backing tier of via-lookup\n // (dict()'s alias); construct the equivalent LookupDescriptor shape so\n // the two tiers' field semantics stay in lockstep, then adapt it to\n // dictKey's legacy public return shape. `_viaBrand` stays `'i18n'` for\n // one release (see task-2-report.md's alias-brand decision) —\n // mergeViaFields/compileViaBindings's existing i18nFields+dictKeyFields\n // path is untouched.\n const equiv: LookupDescriptor<Keys> = {\n _viaBrand: 'lookup',\n dimension: name,\n key: 'id',\n vocabulary: 'open',\n backing: 'reserved',\n onDelete: 'restrict',\n ...(keys !== undefined ? { keys } : {}),\n ...(opts?.onMissing !== undefined ? { onMissing: opts.onMissing } : {}),\n ...(opts?.substitute !== undefined ? { substitute: opts.substitute } : {}),\n ...(labels !== undefined ? { labels } : {}),\n }\n return {\n _noydbDictKey: true,\n _viaBrand: 'i18n',\n name: equiv.dimension,\n keys: equiv.keys,\n ...(equiv.onMissing !== undefined ? { onMissing: equiv.onMissing } : {}),\n ...(equiv.substitute !== undefined ? { substitute: equiv.substitute } : {}),\n ...(equiv.labels !== undefined ? { labels: equiv.labels } : {}),\n }\n}\n\n// `isDictKeyDescriptor` now lives on the kernel port (#623 Task 11,\n// port/with/i18n-strategy.ts, alongside `isStaticDictDescriptor`) —\n// re-exported here for compat with existing importers of this module.\nexport { isDictKeyDescriptor } from '../../port/with/i18n-strategy.js'\n\n// ─── StaticDict descriptor (code-provided dictionary) ──────────────────\n\n/**\n * Descriptor returned by `staticDict()`. A sibling to {@link DictKeyDescriptor}\n * for **closed, defined-in-code, identical-across-vaults** enums (honorific,\n * civil-status, gender, religion, ContactTitle, status…).\n *\n * Unlike `dictKey`, the labels are supplied **at registration in code** and\n * resolved through the *same* label machinery, but with **no `_dict_*`\n * per-vault encrypted copy** and **no `rename()`**. The record stores only\n * the **code**; a static dict has no mutation surface.\n *\n * **Hybrid resolution.** Because a static dict is pure code with no\n * vault-locale dependency, it can — via a configured `displayLocale` — emit a\n * `<field>Label` even under a **locale-less read** (the property a locale-less\n * consumer needs and `dictKey` cannot provide). When a locale *is* active it\n * behaves exactly like `dictKey` (honoring `onMissing`/`substitute`).\n *\n * ```ts\n * const workers = vault.collection<Worker>('workers', {\n * dictKeyFields: {\n * civilStatus: staticDict('civilStatus', {\n * adultMale: { th: 'นาย', en: 'Mr' },\n * adultFemale: { th: 'นาง', en: 'Mrs' },\n * }, { displayLocale: 'th' }),\n * },\n * })\n * ```\n */\nexport interface StaticDictDescriptor<Keys extends string = string> {\n readonly _noydbStaticDict: true\n /** Via port brand marker (#623 Task 9) — staticDict shares the i18n binding brand. */\n readonly _viaBrand: 'i18n'\n /** Which dictionary this field references (the registry name). */\n readonly name: string\n /** The in-code label table: key → { locale → label }. */\n readonly table: Readonly<Record<Keys, Readonly<Record<string, string>>>>\n /** Declared valid keys — derived from `Object.keys(table)`. */\n readonly keys: readonly Keys[]\n /**\n * Locale used to emit `<field>Label` under a **locale-less** read — the\n * hybrid hinge. When unset, a static dict behaves like `dictKey` under a\n * locale-less read (no label); a locale-less consumer almost always wants\n * it set.\n */\n readonly displayLocale?: string\n /** Same `onMissing` policy engine as `dictKey`. Default `'null'`. */\n readonly onMissing?: OnMissingPolicy\n /** Ordered preferred-substitute locales for label resolution. */\n readonly substitute?: readonly string[]\n /**\n * Validate the stored code against `keys` on every `put()`. Default `true`\n * — codes are closed by construction, so an unknown code is a bug. Set\n * `false` to allow open codes (skips the `UnknownDictCodeError` guard).\n */\n readonly validateCodes?: boolean\n}\n\n/**\n * Create a `StaticDictDescriptor` for a code-provided enum field — a sibling\n * to {@link dictKey} for closed, code-defined, identical-across-vaults enums.\n *\n * The labels live in `table` (no `_dict_*` collection, no `rename()`); the\n * record stores only the stable code. Pass `displayLocale` so `<field>Label`\n * resolves even under a locale-less read.\n *\n * @param name The dictionary name (used for the readonly-guard registry and\n * the query label seam — never creates a `_dict_<name>` key).\n * @param table `{ key: { locale: label } }` map.\n * @param opts `displayLocale` (locale-less label), `onMissing`, `substitute`.\n *\n * @example\n * ```ts\n * staticDict('civilStatus', {\n * adultMale: { th: 'นาย', en: 'Mr' },\n * adultFemale: { th: 'นาง', en: 'Mrs' },\n * }, { displayLocale: 'th' })\n * ```\n */\nexport function staticDict<const T extends Record<string, Record<string, string>>>(\n name: string,\n table: T,\n opts?: {\n displayLocale?: string\n onMissing?: OnMissingPolicy\n substitute?: readonly string[]\n validateCodes?: boolean\n },\n): StaticDictDescriptor<Extract<keyof T, string>> {\n // The declaration is the Via binding's opt-in unit (#553) — same pattern\n // as i18nText()/dictKey()/money().\n linkI18nVia()\n type K = Extract<keyof T, string>\n // #650 Task 2 — staticDict is the static-backing tier of via-lookup\n // (lookup(dimension, {backing:'static', table, ...})'s alias); construct\n // the equivalent LookupDescriptor shape, then adapt it to staticDict's\n // legacy public return shape. `validateCodes` has no LookupDescriptor\n // equivalent (a dictKey-family-only put-time knob) — applied directly\n // below. `_viaBrand` stays `'i18n'` for one release — see\n // task-2-report.md's alias-brand decision.\n const equiv: LookupDescriptor<K> = {\n _viaBrand: 'lookup',\n dimension: name,\n key: 'id',\n vocabulary: 'closed',\n backing: 'static',\n onDelete: 'restrict',\n table: table as Readonly<Record<string, Readonly<Record<string, string>>>>,\n keys: Object.keys(table) as K[],\n ...(opts?.displayLocale !== undefined ? { displayLocale: opts.displayLocale } : {}),\n ...(opts?.onMissing !== undefined ? { onMissing: opts.onMissing } : {}),\n ...(opts?.substitute !== undefined ? { substitute: opts.substitute } : {}),\n }\n return {\n _noydbStaticDict: true,\n _viaBrand: 'i18n',\n name: equiv.dimension,\n table: equiv.table as Readonly<Record<K, Readonly<Record<string, string>>>>,\n keys: equiv.keys as readonly K[],\n ...(equiv.displayLocale !== undefined ? { displayLocale: equiv.displayLocale } : {}),\n ...(equiv.onMissing !== undefined ? { onMissing: equiv.onMissing } : {}),\n ...(equiv.substitute !== undefined ? { substitute: equiv.substitute } : {}),\n ...(opts?.validateCodes !== undefined ? { validateCodes: opts.validateCodes } : {}),\n }\n}\n\n// `isStaticDictDescriptor` now lives on the kernel port (#623 Task 7,\n// port/with/i18n-strategy.ts) — re-exported here for compat.\nexport { isStaticDictDescriptor } from '../../port/with/i18n-strategy.js'\n\n"],"mappings":"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","names":[]}
@@ -1,14 +1,23 @@
1
1
  import {
2
2
  canonicalJson,
3
3
  sha256Hex
4
- } from "./chunk-G5CFMITA.js";
4
+ } from "./chunk-MSVY7ULZ.js";
5
5
  import {
6
6
  PeriodClosedError,
7
7
  ValidationError
8
- } from "./chunk-OJ64XMPJ.js";
8
+ } from "./chunk-CU62BBRB.js";
9
9
 
10
10
  // src/with-audit/periods/periods.ts
11
11
  var PERIODS_COLLECTION = "_periods";
12
+ var PERIOD_FREEZES_COLLECTION = "_period_freezes";
13
+ var PERIOD_ARCHIVES_COLLECTION = "_period_archives";
14
+ var PERIOD_TARGET_PURGES_COLLECTION = "_period_target_purges";
15
+ function periodExclusiveUpperBound(endDate) {
16
+ const ms = Date.parse(endDate);
17
+ if (Number.isNaN(ms)) throw new ValidationError(`freezePeriod: unparseable period endDate "${endDate}".`);
18
+ const dateOnly = /^\d{4}-\d{2}-\d{2}$/.test(endDate);
19
+ return new Date(ms + (dateOnly ? 864e5 : 1)).toISOString();
20
+ }
12
21
  async function loadPeriods(adapter, vault, decrypt) {
13
22
  const ids = await adapter.list(vault, PERIODS_COLLECTION);
14
23
  const records = [];
@@ -54,12 +63,12 @@ function validatePeriodName(name, existing) {
54
63
  throw new ValidationError(`Period "${name}" already exists.`);
55
64
  }
56
65
  }
57
- async function appendPeriodLedgerEntry(ledger, actor, envelope, name) {
66
+ async function appendPeriodLedgerEntry(ledger, actor, envelope, name, collection = PERIODS_COLLECTION) {
58
67
  if (!ledger) return;
59
- const { envelopePayloadHash } = await import("./ledger-EMDHRJJN.js");
68
+ const { envelopePayloadHash } = await import("./ledger-AJA2SR3C.js");
60
69
  await ledger.append({
61
70
  op: "put",
62
- collection: PERIODS_COLLECTION,
71
+ collection,
63
72
  id: name,
64
73
  version: envelope._v,
65
74
  actor,
@@ -69,10 +78,14 @@ async function appendPeriodLedgerEntry(ledger, actor, envelope, name) {
69
78
 
70
79
  export {
71
80
  PERIODS_COLLECTION,
81
+ PERIOD_FREEZES_COLLECTION,
82
+ PERIOD_ARCHIVES_COLLECTION,
83
+ PERIOD_TARGET_PURGES_COLLECTION,
84
+ periodExclusiveUpperBound,
72
85
  loadPeriods,
73
86
  chainAnchor,
74
87
  assertTsWritable,
75
88
  validatePeriodName,
76
89
  appendPeriodLedgerEntry
77
90
  };
78
- //# sourceMappingURL=chunk-YSU2E4GZ.js.map
91
+ //# sourceMappingURL=chunk-IUKGIB2Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-audit/periods/periods.ts"],"sourcesContent":["/**\n * Accounting-period closure + opening.\n *\n * A closed period seals every record whose envelope `_ts` is at or\n * before the period's `endDate`: further writes (`put` / `delete`)\n * against such records throw {@link PeriodClosedError}. The period\n * itself is stored as a record in the reserved `_periods` collection\n * and written through the normal ledger-instrumented path, so every\n * closure appends a tamper-evident entry to the vault's hash chain.\n *\n * ## Closure model\n *\n * ```\n * vault.closePeriod({ name: 'FY2026-Q1', endDate: '2026-03-31' })\n * └─► PeriodRecord written to _periods/<name>\n * ├─ priorPeriodName / priorPeriodHash — chain to last close\n * ├─ closedAt / closedBy — provenance\n * └─ normal ledger append fires (LedgerStore.append)\n * ```\n *\n * Enforcement (`assertTsWritable`) is vault-local: the Vault caches\n * the list of closed periods on first read and consults that cache in\n * the `Collection.put` / `.delete` path via the `periodGuard` hook.\n *\n * ## Opening model\n *\n * ```\n * vault.openPeriod({\n * name: 'FY2026-Q2',\n * startDate: '2026-04-01',\n * fromPeriod: 'FY2026-Q1',\n * carryForward: async (priorView) => Record<string, Record<string, unknown>>,\n * })\n * ```\n *\n * `carryForward` receives a read-only `VaultInstant` anchored at the\n * prior period's `endDate` (built via `vault.at(endDate)`) so the\n * callback can compute closing aggregates from the sealed state. The\n * returned `{ [collectionName]: { [id]: record } }` map is written\n * before the new `PeriodRecord` lands — opening balances materialise\n * as normal records with fresh timestamps that fall outside every\n * closed period.\n *\n * ## Freeze\n *\n * ```\n * vault.freezePeriod('FY2026-Q1')\n * └─► physically purges delete markers whose write-time falls inside\n * the closed period's window (via the #589 `_purgeDeleteMarkers`\n * seam), then records the fact:\n * ├─ PeriodFreezeRecord written to _period_freezes/<name>\n * └─ normal ledger append fires (LedgerStore.append)\n * ```\n *\n * The chained `_periods/<name>` record is never mutated — `frozenAt` /\n * `frozenBy` / `purgedMarkerCount` are merged onto the returned\n * `PeriodRecord` at read time from the companion, so a tamper with the\n * freeze can never break the inter-period hash chain. Freezing is\n * terminal (a closed period, once frozen, stays frozen) and idempotent\n * (a second call is a no-op that returns the same merged record without\n * re-purging or re-appending a ledger entry). Freeze does NOT purge\n * forget-tombstones (GDPR crypto-shred erasure evidence), `_history`\n * versions, or live records — the delete-markers-only seam leaves all\n * three untouched by construction.\n *\n * Freeze purges the LOCAL adapter only. On a synced vault, markers already\n * pushed to sync targets survive there, and a later pull re-imports them\n * (benign — they still read deleted, but the space isn't reclaimed). A\n * re-imported marker keeps its original `_ts` (inside the already-frozen\n * period's window), so — like any late-booked delete — it is reclaimed by\n * the NEXT period's freeze, whose window covers it; freeze stays terminal\n * and does NOT re-purge an already-frozen period (#611). Sweeping the sync\n * targets themselves is a cross-target-purge concern deferred to the\n * cold-archival spec. Purging re-opens the #589 resurrection window for a\n * peer offline since before the cutoff, which is why the closed period is\n * the operator-asserted safe-point that gates the call.\n *\n * A period whose purge window has not fully elapsed cannot be frozen —\n * `freezePeriod` throws rather than purge markers for deletes that may not\n * have converged yet (#610).\n *\n * ## Archive\n *\n * ```\n * vault.archivePeriod('FY2026-Q1')\n * └─► relocates the closed period's in-window records (those with\n * `_ts < periodExclusiveUpperBound(endDate)`) from the hot store to\n * the configured cold tier (routeStore's `cold` route), then records:\n * ├─ PeriodArchiveRecord written to _period_archives/<name>\n * └─ a ledger entry attributed to _period_archives\n * ```\n *\n * Archival is NON-DESTRUCTIVE: routeStore reads fall through to the cold\n * tier on a hot miss, so an archived record still reads normally. It is\n * therefore gated only on `closed` (not `frozen`) — it does not re-open the\n * #589 resurrection window and needs no convergence safe-point. Freeze\n * (purge markers) and archive (relocate records) are independent and compose\n * in either order. Like freeze, archival keeps the chained `_periods/<name>`\n * record byte-immutable (state lives in the companion) and is idempotent.\n *\n * Bounds by write-time `_ts`, NOT business date: the store tier sees only\n * encrypted envelopes. A record with an in-period business date but a later\n * `_ts` (late-booked) archives at the NEXT period's archive — the same rule\n * freeze uses for late-booked delete markers. Requires a `routeStore` with a\n * cold route (`age: { cold }`); throws otherwise.\n *\n * Read cost: with `withLazy()` (per-id reads) archived records are truly\n * cold — fetched from cold only on access. In the default hydrated mode,\n * `loadAll` merges the cold store, so archived records still load into RAM\n * on vault open (hot-tier STORAGE is reclaimed; RAM is not). Summaries\n * (`_`-prefixed) always stay hot.\n *\n * ## Target-purge\n *\n * ```\n * vault.purgePeriodTargets('FY2026-Q1')\n * └─► sweeps delete markers (`_ts < periodExclusiveUpperBound(endDate)`) off\n * the vault's PUSH-ONLY sync targets (backup/archive roles), then records:\n * ├─ PeriodTargetPurgeRecord written to _period_target_purges/<name>\n * └─ a ledger entry attributed to _period_target_purges\n * ```\n *\n * Extends freeze's local marker purge to the vault's own remote sinks.\n * `sync-peer` (bidirectional) targets are SKIPPED: purging a marker there\n * re-opens the #589 resurrection window for a client offline before the\n * cutoff, an assertion no single vault can verify. Backup/archive targets are\n * push-only — never pulled from into convergence — so sweeping their markers\n * is safe. Requires the period be frozen first (closed → frozen →\n * target-purged) so the local safe-point is already established. Idempotent\n * once run; a vault with no push-only targets writes no companion and is\n * re-runnable (so a target added later is still swept). Single-vault only —\n * fleet-wide purge across sovereign vaults is klum's concern over\n * `@noy-db/hub/cargo`.\n *\n * ## Not covered\n *\n * - Partial re-opening of a closed period. If an auditor needs to\n * make a correction inside a sealed period, the sanctioned path is\n * a compensating entry in the NEW period, not an unlock of the\n * old one.\n * - Automatic period rollover. `closePeriod` / `openPeriod` are\n * deliberately explicit operator calls so the caller decides when\n * the boundary lands.\n *\n * @module\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport type { LedgerStore } from '../../with-commit/history/ledger/index.js'\nimport { sha256Hex, canonicalJson } from '../../with-commit/history/ledger/index.js'\nimport { PeriodClosedError, ValidationError } from '../../kernel/errors.js'\n\n/** The reserved collection name holding closed-period metadata. */\nexport const PERIODS_COLLECTION = '_periods'\n\n/** Sibling of {@link PERIODS_COLLECTION} holding freeze companions (#604). */\nexport const PERIOD_FREEZES_COLLECTION = '_period_freezes'\n\n/** Sibling of {@link PERIODS_COLLECTION} holding archive companions (#613). */\nexport const PERIOD_ARCHIVES_COLLECTION = '_period_archives'\n\n/** Sibling of {@link PERIODS_COLLECTION} holding target-purge companions (#615). */\nexport const PERIOD_TARGET_PURGES_COLLECTION = '_period_target_purges'\n\n/**\n * Companion record recording that a closed period was frozen (its delete\n * markers physically purged). Stored in {@link PERIOD_FREEZES_COLLECTION},\n * keyed by period name — kept OFF the hash-chained `_periods/<name>` record so\n * freeze never alters the inter-period chain.\n */\nexport interface PeriodFreezeRecord {\n readonly period: string\n readonly frozenAt: string\n readonly frozenBy: string\n readonly purgedMarkerCount: number\n}\n\n/**\n * Companion record noting that a closed period was cold-archived (its\n * in-window records physically relocated hot → cold). Stored in\n * {@link PERIOD_ARCHIVES_COLLECTION}, keyed by period name — kept OFF the\n * hash-chained `_periods/<name>` record so archive never alters the chain.\n */\nexport interface PeriodArchiveRecord {\n readonly period: string\n readonly archivedAt: string\n readonly archivedBy: string\n readonly archivedRecordCount: number\n}\n\n/** Per-target count of delete markers purged off one push-only sync target (#615). */\nexport interface TargetPurgeCount {\n readonly label?: string\n readonly role: 'backup' | 'archive'\n readonly purgedCount: number\n}\n\n/**\n * Companion record noting that a closed+frozen period's delete markers were\n * swept off the vault's push-only sync targets (#615). Stored in\n * {@link PERIOD_TARGET_PURGES_COLLECTION}, keyed by period name — kept OFF the\n * hash-chained `_periods/<name>` record so target-purge never alters the chain.\n */\nexport interface PeriodTargetPurgeRecord {\n readonly period: string\n readonly purgedAt: string\n readonly purgedBy: string\n readonly targets: readonly TargetPurgeCount[]\n}\n\n/**\n * Exclusive upper bound for a period's delete-marker purge window (#604).\n * Markers carry no business date (empty body), only write-time `_ts`, so freeze\n * purges markers with `_ts < bound`, `bound` being the instant just after the\n * period's inclusive `endDate`: a date-only `endDate` seals through end-of-day\n * → next midnight; a full-timestamp `endDate` seals through that instant → +1ms.\n */\nexport function periodExclusiveUpperBound(endDate: string): string {\n const ms = Date.parse(endDate)\n if (Number.isNaN(ms)) throw new ValidationError(`freezePeriod: unparseable period endDate \"${endDate}\".`)\n const dateOnly = /^\\d{4}-\\d{2}-\\d{2}$/.test(endDate)\n return new Date(ms + (dateOnly ? 86_400_000 : 1)).toISOString()\n}\n\n/**\n * Stored record for one closed or opened accounting period. One entry\n * per period, keyed by `name` in the reserved `_periods` collection.\n *\n * The hash chain between periods is computed at read time by\n * `loadPeriods()` — each record carries the name + hash of its\n * predecessor so a tamper with any period's record breaks the chain\n * into the next one, the same way the ledger's `prevHash` works.\n */\nexport interface PeriodRecord {\n /** Human-readable name (e.g., `'FY2026-Q1'`). Unique per vault. */\n readonly name: string\n /**\n * Role discriminator. A period is `'closed'` from the moment its\n * `closedAt` is recorded; `'opened'` marks a period whose opening\n * entries have been carried forward via {@link openPeriod}. Many\n * workflows will produce one opened period per closed period (the\n * opened one is the SUCCESSOR — its `startDate` equals the prior\n * `endDate + 1 day`).\n */\n readonly kind: 'closed' | 'opened'\n /** ISO date — inclusive upper bound for records belonging to this period. */\n readonly endDate: string\n /** ISO date — lower bound (present on opened periods only). */\n readonly startDate?: string\n /**\n * Record field carrying the business date (e.g. `'date'` on an\n * invoice, `'paidAt'` on a payment). The guard compares\n * `record[dateField]` against `endDate` — NOT the envelope `_ts`.\n * Accounting entries booked late (business date `2026-01-15`,\n * write-time `2026-04-22`) still get sealed when Q1 closes at\n * `2026-03-31` because the comparison uses the business date.\n *\n * Optional for backwards compat. When absent, the guard falls back\n * to envelope `_ts` — that's a write-time seal, appropriate for\n * content that doesn't carry a logical business date (e.g. system\n * settings) but almost never right for accounting ledgers.\n */\n readonly dateField?: string\n /** ISO timestamp recorded at `closePeriod()` / `openPeriod()` call time. */\n readonly closedAt: string\n /** userId of the keyring that invoked the close/open. */\n readonly closedBy: string\n /** Name of the prior period this one chains to, if any. */\n readonly priorPeriodName?: string\n /** sha256(canonicalJson(priorPeriod)) — empty for the first period. */\n readonly priorPeriodHash: string\n /**\n * Opened periods only — the names of the collections whose\n * carry-forward aggregates were written by {@link openPeriod}.\n * Recorded for auditability so a future `verifyPeriodChain()` can\n * cross-check the opening balances against the closing snapshot.\n */\n readonly openingCollections?: readonly string[]\n /** #604 return-only — merged from the `_period_freezes/<name>` companion on\n * read; NEVER written into the stored `_periods/<name>` record (would break\n * the hash chain). Absent = not yet frozen. */\n readonly frozenAt?: string\n readonly frozenBy?: string\n readonly purgedMarkerCount?: number\n /** #613 return-only — merged from the `_period_archives/<name>` companion on\n * read; NEVER written into the stored `_periods/<name>` record. Absent = not\n * yet archived. */\n readonly archivedAt?: string\n readonly archivedBy?: string\n readonly archivedRecordCount?: number\n /** #615 return-only — merged from the `_period_target_purges/<name>` companion\n * on read; NEVER written into the stored `_periods/<name>` record. Absent =\n * target-purge not yet run (or the vault has no push-only targets). */\n readonly targetsPurgedAt?: string\n readonly targetsPurgedBy?: string\n readonly targetsPurged?: readonly TargetPurgeCount[]\n}\n\n/** Options for `vault.closePeriod()`. */\nexport interface ClosePeriodOptions {\n /** Human-readable name. Must not collide with an existing period. */\n readonly name: string\n /**\n * Inclusive upper cutoff. A record is sealed when its\n * `record[dateField]` (or, if absent, the envelope `_ts`) is at or\n * before this ISO timestamp.\n */\n readonly endDate: string\n /**\n * Record field carrying the business date used for period\n * membership. Recommended for accounting workflows — e.g. an\n * invoice booked late (write-time after close) is still sealed\n * when its `invoice.date` falls inside the closed period.\n *\n * Omit to use envelope `_ts` (write-time seal). This fallback\n * rarely matches real-world accounting semantics; prefer passing\n * an explicit `dateField`.\n */\n readonly dateField?: string\n}\n\n/** Options for `vault.openPeriod()`. */\nexport interface OpenPeriodOptions<TCollections = Record<string, Record<string, unknown>>> {\n /** Human-readable name for the new period. Must be unique. */\n readonly name: string\n /** ISO lower bound of the new period (usually prior `endDate + 1 day`). */\n readonly startDate: string\n /**\n * Name of the prior CLOSED period this one chains from. The prior\n * period's record is verified to exist and to be `kind: 'closed'`;\n * its `endDate` is made available to the `carryForward` callback.\n */\n readonly fromPeriod: string\n /**\n * Receives a read-only facade over the vault's CURRENT state,\n * plus the prior period's `endDate`. Accounting semantics: after\n * a period closes, records with `record[dateField] <= endDate`\n * are frozen — current state equals closing state, so a caller\n * can compute closing balances by querying the live collection\n * with a `where('date', '<=', priorEndDate)` filter.\n *\n * Returns opening-balance records keyed by collection name.\n * Example:\n *\n * ```ts\n * carryForward: async (ctx) => {\n * const closing = await ctx.collection<Journal>('journal')\n * .query().where('date', '<=', ctx.priorEndDate).toArray()\n * const opening: Record<string, Journal> = {}\n * for (const entry of closing) {\n * opening[`OB-${entry.id}`] = { ...entry, date: '2026-04-01' }\n * }\n * return { journal: opening }\n * }\n * ```\n */\n readonly carryForward: (\n ctx: CarryForwardContext,\n ) => Promise<TCollections> | TCollections\n}\n\n/**\n * Context passed to `OpenPeriodOptions.carryForward`. Exposes a\n * read-only subset of the live vault (`collection(name).get/list`)\n * plus the prior period's `endDate` so business-date filters can\n * be built by the caller.\n *\n * Writes go via the return value, not via the facade — the\n * `collection()` here is deliberately restricted to reads.\n */\nexport interface CarryForwardContext {\n /** The prior period's `endDate` — the boundary of the closing snapshot. */\n readonly priorEndDate: string\n /** Read-only collection facade over current vault state. */\n collection<T = unknown>(name: string): ReadOnlyCollection<T>\n}\n\n/** Minimum read surface exposed to `carryForward`. */\nexport interface ReadOnlyCollection<T> {\n get(id: string): Promise<T | null>\n list(): Promise<T[]>\n}\n\n/**\n * Load every period record currently stored on the adapter.\n * Decrypting is the caller's responsibility (we return plain records\n * so the vault can use its own `_periods` DEK).\n *\n * @internal — called by Vault methods that need the closed-period\n * cache. Not part of the public API surface.\n */\nexport async function loadPeriods(\n adapter: NoydbStore,\n vault: string,\n decrypt: (envelope: EncryptedEnvelope) => Promise<PeriodRecord>,\n): Promise<PeriodRecord[]> {\n const ids = await adapter.list(vault, PERIODS_COLLECTION)\n const records: PeriodRecord[] = []\n for (const id of ids) {\n const env = await adapter.get(vault, PERIODS_COLLECTION, id)\n if (env) records.push(await decrypt(env))\n }\n // Stable order by closedAt so chain verification is reproducible.\n records.sort((a, b) => a.closedAt.localeCompare(b.closedAt))\n return records\n}\n\n/**\n * Given the current ordered period list, pick the last entry that\n * belongs to the hash chain — used as the `priorPeriodHash` anchor\n * for the next closure/opening.\n *\n * @internal\n */\nexport async function chainAnchor(\n records: readonly PeriodRecord[],\n): Promise<{ priorPeriodName?: string; priorPeriodHash: string }> {\n const last = records[records.length - 1]\n if (!last) return { priorPeriodHash: '' }\n const hash = await sha256Hex(canonicalJson(last as unknown as Record<string, unknown>))\n return { priorPeriodName: last.name, priorPeriodHash: hash }\n}\n\n/**\n * Throw `PeriodClosedError` if the record being touched falls within\n * any closed period.\n *\n * Three signals, evaluated per period:\n *\n * 1. If the period declares a `dateField`, the guard reads\n * `record[dateField]` on BOTH the existing (prior) record AND the\n * incoming (new) record. Either comparing `<= endDate` triggers\n * the error — callers cannot slide a record into a closed period\n * by editing its date field.\n * 2. If the period has no `dateField`, the guard falls back to the\n * envelope `_ts` of the existing record. Fresh inserts (no\n * existing envelope) pass.\n * 3. For a delete, only the existing side is checked.\n *\n * @internal\n */\nexport function assertTsWritable(\n existing: { ts: string | null; record: Record<string, unknown> | null } | null,\n incomingRecord: Record<string, unknown> | null,\n closedPeriods: readonly PeriodRecord[],\n): void {\n for (const p of closedPeriods) {\n if (p.kind !== 'closed') continue\n if (p.dateField) {\n const checkRecord = (label: string, r: Record<string, unknown> | null): void => {\n if (!r) return\n const v = r[p.dateField!]\n if (typeof v === 'string' && v <= p.endDate) {\n throw new PeriodClosedError(p.name, p.endDate, `${label}[${p.dateField}]=${v}`)\n }\n }\n checkRecord('existing', existing?.record ?? null)\n checkRecord('incoming', incomingRecord)\n continue\n }\n // Fallback: write-time seal via envelope _ts.\n const existingTs = existing?.ts ?? null\n if (existingTs !== null && existingTs <= p.endDate) {\n throw new PeriodClosedError(p.name, p.endDate, existingTs)\n }\n }\n}\n\n/**\n * Sanity-check a proposed period name + endDate against existing\n * records. Shared by closePeriod / openPeriod so the two pathways\n * produce identical diagnostics.\n *\n * @internal\n */\nexport function validatePeriodName(\n name: string,\n existing: readonly PeriodRecord[],\n): void {\n if (name.length === 0) {\n throw new ValidationError('Period name cannot be empty.')\n }\n if (existing.some((p) => p.name === name)) {\n throw new ValidationError(`Period \"${name}\" already exists.`)\n }\n}\n\n/**\n * Wire a reserved-collection ledger append for a period record. The\n * period itself is stored via the adapter as an encrypted envelope;\n * the ledger entry is a normal `put` with the period's payloadHash,\n * so period closures inherit the chain's tamper-evidence.\n *\n * @internal\n */\nexport async function appendPeriodLedgerEntry(\n ledger: LedgerStore | null,\n actor: string,\n envelope: EncryptedEnvelope,\n name: string,\n collection: string = PERIODS_COLLECTION,\n): Promise<void> {\n if (!ledger) return\n const { envelopePayloadHash } = await import('../../with-commit/history/ledger/index.js')\n await ledger.append({\n op: 'put',\n collection,\n id: name,\n version: envelope._v,\n actor,\n payloadHash: await envelopePayloadHash(envelope),\n })\n}\n"],"mappings":";;;;;;;;;;AAyJO,IAAM,qBAAqB;AAG3B,IAAM,4BAA4B;AAGlC,IAAM,6BAA6B;AAGnC,IAAM,kCAAkC;AAuDxC,SAAS,0BAA0B,SAAyB;AACjE,QAAM,KAAK,KAAK,MAAM,OAAO;AAC7B,MAAI,OAAO,MAAM,EAAE,EAAG,OAAM,IAAI,gBAAgB,6CAA6C,OAAO,IAAI;AACxG,QAAM,WAAW,sBAAsB,KAAK,OAAO;AACnD,SAAO,IAAI,KAAK,MAAM,WAAW,QAAa,EAAE,EAAE,YAAY;AAChE;AAyKA,eAAsB,YACpB,SACA,OACA,SACyB;AACzB,QAAM,MAAM,MAAM,QAAQ,KAAK,OAAO,kBAAkB;AACxD,QAAM,UAA0B,CAAC;AACjC,aAAW,MAAM,KAAK;AACpB,UAAM,MAAM,MAAM,QAAQ,IAAI,OAAO,oBAAoB,EAAE;AAC3D,QAAI,IAAK,SAAQ,KAAK,MAAM,QAAQ,GAAG,CAAC;AAAA,EAC1C;AAEA,UAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,cAAc,EAAE,QAAQ,CAAC;AAC3D,SAAO;AACT;AASA,eAAsB,YACpB,SACgE;AAChE,QAAM,OAAO,QAAQ,QAAQ,SAAS,CAAC;AACvC,MAAI,CAAC,KAAM,QAAO,EAAE,iBAAiB,GAAG;AACxC,QAAM,OAAO,MAAM,UAAU,cAAc,IAA0C,CAAC;AACtF,SAAO,EAAE,iBAAiB,KAAK,MAAM,iBAAiB,KAAK;AAC7D;AAoBO,SAAS,iBACd,UACA,gBACA,eACM;AACN,aAAW,KAAK,eAAe;AAC7B,QAAI,EAAE,SAAS,SAAU;AACzB,QAAI,EAAE,WAAW;AACf,YAAM,cAAc,CAAC,OAAe,MAA4C;AAC9E,YAAI,CAAC,EAAG;AACR,cAAM,IAAI,EAAE,EAAE,SAAU;AACxB,YAAI,OAAO,MAAM,YAAY,KAAK,EAAE,SAAS;AAC3C,gBAAM,IAAI,kBAAkB,EAAE,MAAM,EAAE,SAAS,GAAG,KAAK,IAAI,EAAE,SAAS,KAAK,CAAC,EAAE;AAAA,QAChF;AAAA,MACF;AACA,kBAAY,YAAY,UAAU,UAAU,IAAI;AAChD,kBAAY,YAAY,cAAc;AACtC;AAAA,IACF;AAEA,UAAM,aAAa,UAAU,MAAM;AACnC,QAAI,eAAe,QAAQ,cAAc,EAAE,SAAS;AAClD,YAAM,IAAI,kBAAkB,EAAE,MAAM,EAAE,SAAS,UAAU;AAAA,IAC3D;AAAA,EACF;AACF;AASO,SAAS,mBACd,MACA,UACM;AACN,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,IAAI,gBAAgB,8BAA8B;AAAA,EAC1D;AACA,MAAI,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,GAAG;AACzC,UAAM,IAAI,gBAAgB,WAAW,IAAI,mBAAmB;AAAA,EAC9D;AACF;AAUA,eAAsB,wBACpB,QACA,OACA,UACA,MACA,aAAqB,oBACN;AACf,MAAI,CAAC,OAAQ;AACb,QAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,sBAA2C;AACxF,QAAM,OAAO,OAAO;AAAA,IAClB,IAAI;AAAA,IACJ;AAAA,IACA,IAAI;AAAA,IACJ,SAAS,SAAS;AAAA,IAClB;AAAA,IACA,aAAa,MAAM,oBAAoB,QAAQ;AAAA,EACjD,CAAC;AACH;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ValidationError
3
- } from "./chunk-OJ64XMPJ.js";
3
+ } from "./chunk-CU62BBRB.js";
4
4
 
5
5
  // src/with-formula/derivations/with-derivation.ts
6
6
  function withDerivation(spec) {
@@ -126,4 +126,4 @@ export {
126
126
  withDerivation,
127
127
  withRollup
128
128
  };
129
- //# sourceMappingURL=chunk-OWGAXPRE.js.map
129
+ //# sourceMappingURL=chunk-IWTYFS3D.js.map
@@ -6,7 +6,7 @@ import {
6
6
  ConflictError,
7
7
  InvariantError,
8
8
  ValidationError
9
- } from "./chunk-OJ64XMPJ.js";
9
+ } from "./chunk-CU62BBRB.js";
10
10
 
11
11
  // src/with-commit/tx/transaction.ts
12
12
  var TxContext = class {
@@ -216,7 +216,7 @@ async function runTransaction(db, fn, options, txInvariants) {
216
216
  db._clearActiveTxContext(ctx);
217
217
  }
218
218
  if (ctx._amendment) {
219
- const { GuardExecutor } = await import("./executor-KZ4B3PRT.js");
219
+ const { GuardExecutor } = await import("./executor-OUA2Y6YC.js");
220
220
  try {
221
221
  for (const [vaultName, v] of ctx._amendmentVaults) {
222
222
  const registry = v._getGuardRegistry();
@@ -352,4 +352,4 @@ export {
352
352
  runTransaction,
353
353
  revertExecuted
354
354
  };
355
- //# sourceMappingURL=chunk-FCOZ7DEC.js.map
355
+ //# sourceMappingURL=chunk-JC5R7P44.js.map