@noy-db/hub 0.6.0 → 0.7.0-pre.1

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 (442) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +1 -1
  3. package/codemods/0.4.0-pre.json +24 -0
  4. package/codemods/0.6.0-pre.json +18 -0
  5. package/codemods/0.7.0-pre.json +294 -0
  6. package/dist/api-EQYRTZO2.js +20 -0
  7. package/dist/as/index.js +146 -0
  8. package/dist/as/index.js.map +1 -0
  9. package/dist/at/index.js +23 -0
  10. package/dist/attestation/index.js +18 -18
  11. package/dist/{backup-VL4GRIEW.js → backup-Z7ZBTAQ6.js} +12 -12
  12. package/dist/blobs/index.js +13 -13
  13. package/dist/broker/index.js +12 -12
  14. package/dist/by/index.js +11 -0
  15. package/dist/cargo/index.js +35 -31
  16. package/dist/{chunk-JKATP6OE.js → chunk-225O5CXQ.js} +2 -2
  17. package/dist/{chunk-XINT4VJA.js → chunk-2GXEQUEX.js} +2 -2
  18. package/dist/chunk-2GXEQUEX.js.map +1 -0
  19. package/dist/{chunk-CXPOG3ZY.js → chunk-2LPVI4PX.js} +8 -8
  20. package/dist/{chunk-2J5IT37K.js → chunk-3L2MVE36.js} +5 -5
  21. package/dist/{chunk-HOPNIR5R.js → chunk-3QARDAYW.js} +2 -2
  22. package/dist/{chunk-JYPRZWCZ.js → chunk-3SMSERQU.js} +169 -135
  23. package/dist/chunk-3SMSERQU.js.map +1 -0
  24. package/dist/{chunk-HSJBJIB2.js → chunk-467SKJ65.js} +4 -4
  25. package/dist/{chunk-YCGLAANM.js → chunk-4A4TYISP.js} +2 -2
  26. package/dist/{chunk-2CBFYXJX.js → chunk-4G25SDQL.js} +4 -4
  27. package/dist/{chunk-5LZZQMIC.js → chunk-4LPF226H.js} +5 -5
  28. package/dist/{chunk-GANFIM2M.js → chunk-4ZPE6SKS.js} +3 -3
  29. package/dist/{chunk-65JXA63J.js → chunk-5263OEM3.js} +2 -2
  30. package/dist/{chunk-FNYCT27B.js → chunk-57JTHWLQ.js} +4 -4
  31. package/dist/{chunk-5IHGJHWC.js → chunk-5ACAVJUI.js} +2 -2
  32. package/dist/{chunk-3OE2QNSM.js → chunk-5AFMP532.js} +6 -6
  33. package/dist/{chunk-J6I5VR6P.js → chunk-5W7YXFV5.js} +8 -8
  34. package/dist/chunk-5W7YXFV5.js.map +1 -0
  35. package/dist/{chunk-7ZGPQOFV.js → chunk-6EW2UCVF.js} +3 -3
  36. package/dist/{chunk-GCU42IG7.js → chunk-6FOGTKBY.js} +7 -7
  37. package/dist/chunk-6FOGTKBY.js.map +1 -0
  38. package/dist/{chunk-36CEG6CM.js → chunk-7LYUJNU3.js} +2 -2
  39. package/dist/{chunk-D6SXRMZZ.js → chunk-A2YW5EMU.js} +5 -5
  40. package/dist/{chunk-OQMCFHUX.js → chunk-AB5P32RE.js} +2 -2
  41. package/dist/{chunk-JMJSCI7U.js → chunk-ALB64CZM.js} +7 -7
  42. package/dist/{chunk-RPRASGEY.js → chunk-ALUNQEBY.js} +2 -2
  43. package/dist/{chunk-LC6NE4IQ.js → chunk-CBRS2NEK.js} +2 -11
  44. package/dist/chunk-CBRS2NEK.js.map +1 -0
  45. package/dist/{chunk-W3HAIN6U.js → chunk-CN6B5PEN.js} +5 -5
  46. package/dist/{chunk-JIHWBKMQ.js → chunk-DBNYPLWY.js} +3 -3
  47. package/dist/{chunk-ZJUZOJVW.js → chunk-DFAUMLD7.js} +5 -5
  48. package/dist/{chunk-S6PJ6ZKE.js → chunk-DKWPJOYH.js} +2 -2
  49. package/dist/{chunk-MPKP6L5E.js → chunk-E6YA6UAP.js} +2 -2
  50. package/dist/{chunk-LIR2DJWP.js → chunk-F23WQK3G.js} +9 -9
  51. package/dist/chunk-F23WQK3G.js.map +1 -0
  52. package/dist/{chunk-S7PM7Z4K.js → chunk-F7ZFRPGQ.js} +2 -2
  53. package/dist/{chunk-CKKN25P2.js → chunk-FOG7YEJP.js} +3 -3
  54. package/dist/{chunk-AJGGBXWA.js → chunk-GK4DRVUD.js} +6 -6
  55. package/dist/chunk-GK4DRVUD.js.map +1 -0
  56. package/dist/{chunk-4ATF4BLF.js → chunk-H36KSN6X.js} +7 -7
  57. package/dist/{chunk-BMU2DS3S.js → chunk-H3VG6XBC.js} +12 -171
  58. package/dist/chunk-H3VG6XBC.js.map +1 -0
  59. package/dist/{chunk-YTUFV7U7.js → chunk-H4M7ADXY.js} +10 -10
  60. package/dist/chunk-H4M7ADXY.js.map +1 -0
  61. package/dist/{chunk-QQRRKO7W.js → chunk-H7HP56F6.js} +5 -5
  62. package/dist/{chunk-RJZQ2PVD.js → chunk-HM4JSXX2.js} +3 -3
  63. package/dist/{chunk-3YGUHUJA.js → chunk-HTGRQZFB.js} +3 -3
  64. package/dist/{chunk-JXHNUYQO.js → chunk-IESNYJXZ.js} +6 -6
  65. package/dist/{chunk-ZYZJLYNS.js → chunk-ISSU3OH6.js} +7 -7
  66. package/dist/{chunk-OCJ3JA4O.js → chunk-J4MN6XIU.js} +2 -2
  67. package/dist/{chunk-HWI7GHJZ.js → chunk-J4UJ5FRV.js} +4 -4
  68. package/dist/{chunk-Z3BZXPRO.js → chunk-JBIV4FLR.js} +1 -1
  69. package/dist/chunk-JBIV4FLR.js.map +1 -0
  70. package/dist/{chunk-QVXZFJBU.js → chunk-JVLSU7S2.js} +2 -2
  71. package/dist/{chunk-7Q3RHOA5.js → chunk-KDJGJR7F.js} +2 -2
  72. package/dist/{chunk-PJX72ICF.js → chunk-KN4W2OQ4.js} +7 -7
  73. package/dist/chunk-KN4W2OQ4.js.map +1 -0
  74. package/dist/{chunk-M7WBE3OR.js → chunk-L6GTLZRK.js} +5 -5
  75. package/dist/{chunk-2ZT4INZ4.js → chunk-LF6DMX4C.js} +3 -3
  76. package/dist/chunk-LFUID7OW.js +163 -0
  77. package/dist/chunk-LFUID7OW.js.map +1 -0
  78. package/dist/{chunk-4K6BIMLF.js → chunk-LKFKXS5K.js} +2 -2
  79. package/dist/{chunk-POFYIGQ2.js → chunk-LR4Z47QD.js} +3 -3
  80. package/dist/{chunk-LUYPYCHY.js → chunk-MTBXJAS4.js} +4 -4
  81. package/dist/{chunk-FX3UIDCS.js → chunk-N4CNVSUF.js} +2 -2
  82. package/dist/{chunk-FU5VYEYK.js → chunk-NLKLXXTO.js} +2 -2
  83. package/dist/{chunk-YYV53EHO.js → chunk-OPXR4PB2.js} +2 -2
  84. package/dist/{chunk-5F7BZAGV.js → chunk-OWLVBOWO.js} +10 -10
  85. package/dist/{chunk-6NR36UNB.js → chunk-P6ASQOZR.js} +2 -2
  86. package/dist/{chunk-5ATC6UT4.js → chunk-PCQDY6QN.js} +6 -6
  87. package/dist/{chunk-EQOTJL76.js → chunk-PGFKIOR3.js} +3 -3
  88. package/dist/{chunk-D2YFQ7XG.js → chunk-POS53FG6.js} +2 -2
  89. package/dist/{chunk-ZY6P7M52.js → chunk-PQ6Z2TAP.js} +2 -2
  90. package/dist/{chunk-5OT4G5N5.js → chunk-PUJTKLPL.js} +3 -3
  91. package/dist/{chunk-64WHKBMP.js → chunk-QGJ4XIA7.js} +8 -8
  92. package/dist/{chunk-DECNV4QK.js → chunk-QHJKK77B.js} +6 -6
  93. package/dist/{chunk-JHYKSMVX.js → chunk-QI5NWLVW.js} +7 -7
  94. package/dist/{chunk-BZ3WMOYX.js → chunk-RC4BROYR.js} +3 -3
  95. package/dist/{chunk-IWQPYT6S.js → chunk-RDHUZ674.js} +4 -4
  96. package/dist/{chunk-UJ4Z67UQ.js → chunk-RK7N4OQ5.js} +1 -1
  97. package/dist/chunk-RK7N4OQ5.js.map +1 -0
  98. package/dist/{chunk-EHW7NLGD.js → chunk-S2VG3ABP.js} +3 -3
  99. package/dist/chunk-S2VG3ABP.js.map +1 -0
  100. package/dist/{chunk-7OBIBPCV.js → chunk-S35S77YU.js} +10 -10
  101. package/dist/{chunk-22GR3O4J.js → chunk-SHILAQH5.js} +2 -2
  102. package/dist/{chunk-WPZ5FVAS.js → chunk-T3PHCEN4.js} +3 -3
  103. package/dist/{chunk-GKTW3KLV.js → chunk-TOV6VMHF.js} +5 -5
  104. package/dist/chunk-TOV6VMHF.js.map +1 -0
  105. package/dist/{chunk-6RH7PRTL.js → chunk-TVIFAHZR.js} +6 -6
  106. package/dist/chunk-UBSFPGVJ.js +1 -0
  107. package/dist/{chunk-323MN67Z.js → chunk-UCUALOSM.js} +2 -2
  108. package/dist/{chunk-B4CRUZPR.js → chunk-VAG47QHV.js} +2 -2
  109. package/dist/{chunk-QH47SQLD.js → chunk-VFIWDPXZ.js} +3 -3
  110. package/dist/{chunk-K6CPIK3R.js → chunk-VTG7327U.js} +4 -4
  111. package/dist/chunk-VTG7327U.js.map +1 -0
  112. package/dist/{chunk-ONPVHX2Y.js → chunk-W5LHQXZU.js} +2 -2
  113. package/dist/{chunk-ZIMQDXTQ.js → chunk-WK5FSW7T.js} +8 -8
  114. package/dist/{chunk-PFWE7FJG.js → chunk-WLLUM35R.js} +2 -2
  115. package/dist/{chunk-IWKCJCLP.js → chunk-X62MEF2O.js} +5 -5
  116. package/dist/chunk-X62MEF2O.js.map +1 -0
  117. package/dist/{chunk-XPEPMK4Y.js → chunk-X67FCULK.js} +5 -5
  118. package/dist/chunk-X67FCULK.js.map +1 -0
  119. package/dist/{chunk-C3XWJCG7.js → chunk-XDVVBETV.js} +2 -2
  120. package/dist/chunk-XDVVBETV.js.map +1 -0
  121. package/dist/chunk-XJ3FEKEY.js +15 -0
  122. package/dist/chunk-XJ3FEKEY.js.map +1 -0
  123. package/dist/{chunk-EV2TDHQZ.js → chunk-XSN63FUS.js} +2 -2
  124. package/dist/{chunk-OR2FYRYR.js → chunk-XVU7J43V.js} +2 -2
  125. package/dist/{chunk-DNKVKO7K.js → chunk-Y6GZ2P4M.js} +2 -2
  126. package/dist/{chunk-ATY3ZASK.js → chunk-YCSM3GZU.js} +2 -2
  127. package/dist/{chunk-RDB66EKD.js → chunk-YMLQBZFJ.js} +6 -6
  128. package/dist/{chunk-EHRIDNZX.js → chunk-YPSOHDNC.js} +2 -2
  129. package/dist/{chunk-KM6ZXG7U.js → chunk-YXA2TCTZ.js} +4 -4
  130. package/dist/{chunk-TKBB62DN.js → chunk-Z2PWOSCJ.js} +2 -2
  131. package/dist/{chunk-S2I5MYSA.js → chunk-Z2WK53N6.js} +42 -15
  132. package/dist/chunk-Z2WK53N6.js.map +1 -0
  133. package/dist/{chunk-5O5ZSW7N.js → chunk-Z7ZXZL6Y.js} +2 -2
  134. package/dist/{chunk-2ZCWHCLF.js → chunk-ZO252Q5S.js} +6 -6
  135. package/dist/classified/index.js +5 -5
  136. package/dist/{classified-marker-AZDSDG6V.js → classified-marker-BSQHU4JT.js} +3 -3
  137. package/dist/collection-facade-LG3NYZVB.js +45 -0
  138. package/dist/{computed-VG2PCM4L.js → computed-ASBNTLOK.js} +3 -3
  139. package/dist/consent/index.js +9 -9
  140. package/dist/cover/index.js +9 -9
  141. package/dist/custody/index.js +12 -12
  142. package/dist/{dead-filter-JNQNB3TA.js → dead-filter-QKU56XTQ.js} +2 -2
  143. package/dist/debug/index.js +1 -1
  144. package/dist/{delegation-5F36DRDH.js → delegation-XWRPYODW.js} +11 -11
  145. package/dist/derivations/index.js +12 -12
  146. package/dist/derive-CXHCY46K.js +22 -0
  147. package/dist/directory/index.js +10 -10
  148. package/dist/{dispatch-3ZA733EY.js → dispatch-4AP4PUZU.js} +18 -18
  149. package/dist/{dispatch-TNM5CRLZ.js → dispatch-VR4LRXZ7.js} +5 -5
  150. package/dist/{enclave-Y2ALNDRD.js → enclave-NYPT7WA7.js} +9 -9
  151. package/dist/executor-3KFRQETS.js +29 -0
  152. package/dist/executor-AFHQYRAU.js +9 -0
  153. package/dist/executor-YYDW6XQI.js +9 -0
  154. package/dist/export-accessible-BX2ILB4C.js +23 -0
  155. package/dist/extract-partition-UPOVITFI.js +38 -0
  156. package/dist/{fanout-sidecar-3QZU7ZZX.js → fanout-sidecar-IWP3R4RC.js} +9 -9
  157. package/dist/{fence-watcher-DHQ775JB.js → fence-watcher-ZVSMLKQD.js} +1 -1
  158. package/dist/fence-watcher-ZVSMLKQD.js.map +1 -0
  159. package/dist/find-SZBL2HPB.js +11 -0
  160. package/dist/forget/index.js +9 -9
  161. package/dist/guards/index.js +7 -7
  162. package/dist/history/index.js +13 -13
  163. package/dist/i18n/index.js +14 -14
  164. package/dist/index.d.ts +5 -5
  165. package/dist/index.js +134 -131
  166. package/dist/index.js.map +1 -1
  167. package/dist/indexing/index.js +2 -2
  168. package/dist/introspection/index.js +12 -11
  169. package/dist/issue-G53NB6EW.js +19 -0
  170. package/dist/kernel/collection-config.d.ts +9 -9
  171. package/dist/kernel/collection.d.ts +1 -1
  172. package/dist/kernel/errors.d.ts +2 -2
  173. package/dist/kernel/noydb.d.ts +6 -6
  174. package/dist/kernel/secret-mode.d.ts +1 -1
  175. package/dist/kernel/types.d.ts +34 -15
  176. package/dist/kernel/vault.d.ts +12 -3
  177. package/dist/kernel/via/compose.d.ts +3 -3
  178. package/dist/kernel/via/index.d.ts +4 -4
  179. package/dist/kernel/via/pipeline.d.ts +6 -6
  180. package/dist/kernel/via/reconcile.d.ts +2 -2
  181. package/dist/kernel/via/taint-binding.d.ts +3 -3
  182. package/dist/{ledger-MQIIOTZB.js → ledger-T5K4P2D7.js} +11 -11
  183. package/dist/liberate-VEOZRESE.js +25 -0
  184. package/dist/{link-set-5PJBBR63.js → link-set-76JZ6YB4.js} +11 -11
  185. package/dist/{managed-secret-ETDG6AAE.js → managed-secret-6YC32VXG.js} +12 -12
  186. package/dist/materialized-views/index.js +25 -25
  187. package/dist/{migrate-cek-V3VDRV24.js → migrate-cek-LBRKYUZZ.js} +2 -2
  188. package/dist/money/index.js +4 -4
  189. package/dist/noydb-4VV2ZGLZ.js +86 -0
  190. package/dist/on/index.js +1 -0
  191. package/dist/overlay-views/index.js +4 -4
  192. package/dist/periods/index.js +12 -12
  193. package/dist/pod/index.js +58 -57
  194. package/dist/{pod-handle-AZ3CXY5Y.js → pod-handle-P6DPBM5U.js} +9 -9
  195. package/dist/policy/index.js +10 -10
  196. package/dist/port/as/active.d.ts +10 -0
  197. package/dist/port/as/index.d.ts +32 -0
  198. package/dist/port/as/types.d.ts +113 -0
  199. package/dist/port/at/index.d.ts +37 -0
  200. package/dist/port/by/default-provider.d.ts +3 -3
  201. package/dist/port/by/index.d.ts +32 -10
  202. package/dist/port/by/types.d.ts +29 -11
  203. package/dist/port/on/index.d.ts +50 -0
  204. package/dist/port/with/computed-strategy.d.ts +1 -1
  205. package/dist/port/with/device-seal-strategy.d.ts +2 -2
  206. package/dist/port/with/formats-strategy.d.ts +76 -0
  207. package/dist/port/with/strategies.d.ts +11 -5
  208. package/dist/portability/index.js +8 -8
  209. package/dist/{post-register-QCJYRMIK.js → post-register-53O7AJRZ.js} +6 -6
  210. package/dist/query/index.js +3 -3
  211. package/dist/reduce/index.js +4 -4
  212. package/dist/register-6TCBR6XD.js +32 -0
  213. package/dist/registry-DH2L4O2V.js +18 -0
  214. package/dist/registry-IWFMXCHX.js +9 -0
  215. package/dist/registry-Q3FG3OAX.js +9 -0
  216. package/dist/registry-YC3IFG3A.js +19 -0
  217. package/dist/request-withdrawal-NM5VYR6F.js +31 -0
  218. package/dist/{reveal-W7BKZSUP.js → reveal-XBIMHAXR.js} +6 -6
  219. package/dist/revoke-MJNYWO72.js +24 -0
  220. package/dist/satellites/index.js +1 -1
  221. package/dist/schema-update/index.js +2 -2
  222. package/dist/sealed-record/index.js +12 -12
  223. package/dist/search/index.js +4 -4
  224. package/dist/{seed-55FPOMYP.js → seed-NSNIGYXE.js} +12 -12
  225. package/dist/sequence/index.js +9 -9
  226. package/dist/session/index.js +9 -9
  227. package/dist/shadow/index.js +2 -2
  228. package/dist/signer-6XVS2AU3.js +25 -0
  229. package/dist/snapshots/index.js +10 -10
  230. package/dist/{stale-TH7SSVRV.js → stale-MCS46TFO.js} +10 -10
  231. package/dist/stale-MCS46TFO.js.map +1 -0
  232. package/dist/storage-ZXUT476I.js +23 -0
  233. package/dist/storage-ZXUT476I.js.map +1 -0
  234. package/dist/{store-coordination-provider-R27VRD6K.js → store-coordination-provider-6FM26VQM.js} +14 -13
  235. package/dist/store-coordination-provider-6FM26VQM.js.map +1 -0
  236. package/dist/sync/index.js +10 -10
  237. package/dist/team/index.js +17 -17
  238. package/dist/tiers/index.js +11 -11
  239. package/dist/to/index.js +1 -1
  240. package/dist/transactions/index.js +3 -3
  241. package/dist/util/index.js +1 -1
  242. package/dist/vault-diff-FE5J35NJ.js +9 -0
  243. package/dist/vault-diff-FE5J35NJ.js.map +1 -0
  244. package/dist/vault-head/index.js +8 -8
  245. package/dist/{verify-M5LQH43C.js → verify-WYOFLSNA.js} +5 -5
  246. package/dist/via/blob/binding.d.ts +6 -6
  247. package/dist/via/classified/binding.d.ts +9 -9
  248. package/dist/via/computed/binding.d.ts +2 -2
  249. package/dist/via/i18n/binding.d.ts +4 -4
  250. package/dist/via/lookup/binding.d.ts +5 -5
  251. package/dist/via/lookup/index.d.ts +1 -1
  252. package/dist/via/lookup/snapshot.d.ts +1 -1
  253. package/dist/via/money/binding.d.ts +3 -3
  254. package/dist/via/money/normalize.d.ts +1 -1
  255. package/dist/via/money/where.d.ts +3 -3
  256. package/dist/walk-JNEOYCAU.js +21 -0
  257. package/dist/walk-JNEOYCAU.js.map +1 -0
  258. package/dist/with-cargo/adopt-partition.d.ts +6 -6
  259. package/dist/with-cargo/floor.d.ts +1 -1
  260. package/dist/with-cargo/index.d.ts +1 -1
  261. package/dist/with-party/team/deed.d.ts +4 -4
  262. package/dist/with-party/team/device-seal.d.ts +5 -5
  263. package/dist/with-party/team/echo-ceremony.d.ts +2 -2
  264. package/dist/with-party/team/echo-secret.d.ts +4 -4
  265. package/dist/with-party/team/index.d.ts +2 -2
  266. package/dist/with-party/team/keyring.d.ts +2 -2
  267. package/dist/with-party/team/managed-secret.d.ts +21 -11
  268. package/dist/with-party/team/noydb-facade.d.ts +2 -2
  269. package/dist/with-party/team/{shamir-recovery-provider.d.ts → noydb-shamir.d.ts} +1 -1
  270. package/dist/with-party/team/recovery.d.ts +3 -3
  271. package/dist/with-party/team/rotate-recover.d.ts +5 -5
  272. package/dist/with-pod/format.d.ts +1 -1
  273. package/dist/with-pod/pod.d.ts +6 -6
  274. package/dist/with-shape/introspection/describe.d.ts +2 -2
  275. package/dist/with-shape/schema-update/fence-controller.d.ts +4 -4
  276. package/dist/with-shape/schema-update/fence-watcher.d.ts +4 -4
  277. package/dist/with-shape/schema-update/store-coordination-provider.d.ts +7 -6
  278. package/dist/withdraw-accessible-ILTSXTIE.js +28 -0
  279. package/dist/withdraw-accessible-ILTSXTIE.js.map +1 -0
  280. package/package.json +23 -5
  281. package/dist/api-6X2VLZ7J.js +0 -20
  282. package/dist/chunk-AJGGBXWA.js.map +0 -1
  283. package/dist/chunk-BMU2DS3S.js.map +0 -1
  284. package/dist/chunk-C3XWJCG7.js.map +0 -1
  285. package/dist/chunk-EHW7NLGD.js.map +0 -1
  286. package/dist/chunk-GCU42IG7.js.map +0 -1
  287. package/dist/chunk-GKTW3KLV.js.map +0 -1
  288. package/dist/chunk-IWKCJCLP.js.map +0 -1
  289. package/dist/chunk-J6I5VR6P.js.map +0 -1
  290. package/dist/chunk-JYPRZWCZ.js.map +0 -1
  291. package/dist/chunk-K6CPIK3R.js.map +0 -1
  292. package/dist/chunk-LC6NE4IQ.js.map +0 -1
  293. package/dist/chunk-LIR2DJWP.js.map +0 -1
  294. package/dist/chunk-PJX72ICF.js.map +0 -1
  295. package/dist/chunk-S2I5MYSA.js.map +0 -1
  296. package/dist/chunk-UJ4Z67UQ.js.map +0 -1
  297. package/dist/chunk-XINT4VJA.js.map +0 -1
  298. package/dist/chunk-XPEPMK4Y.js.map +0 -1
  299. package/dist/chunk-YTUFV7U7.js.map +0 -1
  300. package/dist/chunk-Z3BZXPRO.js.map +0 -1
  301. package/dist/collection-facade-WSUAYFJ4.js +0 -45
  302. package/dist/derive-MPOGZ4NR.js +0 -22
  303. package/dist/executor-BBEEUFO3.js +0 -9
  304. package/dist/executor-G6G3YEWR.js +0 -9
  305. package/dist/executor-ZUTUMTPA.js +0 -29
  306. package/dist/export-accessible-S3KEB763.js +0 -23
  307. package/dist/extract-partition-KVCTSUVE.js +0 -38
  308. package/dist/fence-watcher-DHQ775JB.js.map +0 -1
  309. package/dist/find-V4FN5ITT.js +0 -11
  310. package/dist/issue-5CFBWBRX.js +0 -19
  311. package/dist/liberate-LW7OKVBH.js +0 -25
  312. package/dist/noydb-LFDRMYZ5.js +0 -85
  313. package/dist/register-JBOEHTVX.js +0 -32
  314. package/dist/registry-5DYHQFJS.js +0 -9
  315. package/dist/registry-EANV44W2.js +0 -9
  316. package/dist/registry-J25H6VB4.js +0 -19
  317. package/dist/registry-R23SQUQH.js +0 -18
  318. package/dist/request-withdrawal-KQ4W4UT5.js +0 -31
  319. package/dist/revoke-H3VSTAIE.js +0 -24
  320. package/dist/signer-53AQ3BU2.js +0 -25
  321. package/dist/storage-WSF7ZKTL.js +0 -23
  322. package/dist/store-coordination-provider-R27VRD6K.js.map +0 -1
  323. package/dist/walk-NABCHDZN.js +0 -21
  324. package/dist/withdraw-accessible-VCINIG6V.js +0 -28
  325. /package/dist/{api-6X2VLZ7J.js.map → api-EQYRTZO2.js.map} +0 -0
  326. /package/dist/{collection-facade-WSUAYFJ4.js.map → at/index.js.map} +0 -0
  327. /package/dist/{backup-VL4GRIEW.js.map → backup-Z7ZBTAQ6.js.map} +0 -0
  328. /package/dist/{computed-VG2PCM4L.js.map → by/index.js.map} +0 -0
  329. /package/dist/{chunk-JKATP6OE.js.map → chunk-225O5CXQ.js.map} +0 -0
  330. /package/dist/{chunk-CXPOG3ZY.js.map → chunk-2LPVI4PX.js.map} +0 -0
  331. /package/dist/{chunk-2J5IT37K.js.map → chunk-3L2MVE36.js.map} +0 -0
  332. /package/dist/{chunk-HOPNIR5R.js.map → chunk-3QARDAYW.js.map} +0 -0
  333. /package/dist/{chunk-HSJBJIB2.js.map → chunk-467SKJ65.js.map} +0 -0
  334. /package/dist/{chunk-YCGLAANM.js.map → chunk-4A4TYISP.js.map} +0 -0
  335. /package/dist/{chunk-2CBFYXJX.js.map → chunk-4G25SDQL.js.map} +0 -0
  336. /package/dist/{chunk-5LZZQMIC.js.map → chunk-4LPF226H.js.map} +0 -0
  337. /package/dist/{chunk-GANFIM2M.js.map → chunk-4ZPE6SKS.js.map} +0 -0
  338. /package/dist/{chunk-65JXA63J.js.map → chunk-5263OEM3.js.map} +0 -0
  339. /package/dist/{chunk-FNYCT27B.js.map → chunk-57JTHWLQ.js.map} +0 -0
  340. /package/dist/{chunk-5IHGJHWC.js.map → chunk-5ACAVJUI.js.map} +0 -0
  341. /package/dist/{chunk-3OE2QNSM.js.map → chunk-5AFMP532.js.map} +0 -0
  342. /package/dist/{chunk-7ZGPQOFV.js.map → chunk-6EW2UCVF.js.map} +0 -0
  343. /package/dist/{chunk-36CEG6CM.js.map → chunk-7LYUJNU3.js.map} +0 -0
  344. /package/dist/{chunk-D6SXRMZZ.js.map → chunk-A2YW5EMU.js.map} +0 -0
  345. /package/dist/{chunk-OQMCFHUX.js.map → chunk-AB5P32RE.js.map} +0 -0
  346. /package/dist/{chunk-JMJSCI7U.js.map → chunk-ALB64CZM.js.map} +0 -0
  347. /package/dist/{chunk-RPRASGEY.js.map → chunk-ALUNQEBY.js.map} +0 -0
  348. /package/dist/{chunk-W3HAIN6U.js.map → chunk-CN6B5PEN.js.map} +0 -0
  349. /package/dist/{chunk-JIHWBKMQ.js.map → chunk-DBNYPLWY.js.map} +0 -0
  350. /package/dist/{chunk-ZJUZOJVW.js.map → chunk-DFAUMLD7.js.map} +0 -0
  351. /package/dist/{chunk-S6PJ6ZKE.js.map → chunk-DKWPJOYH.js.map} +0 -0
  352. /package/dist/{chunk-MPKP6L5E.js.map → chunk-E6YA6UAP.js.map} +0 -0
  353. /package/dist/{chunk-S7PM7Z4K.js.map → chunk-F7ZFRPGQ.js.map} +0 -0
  354. /package/dist/{chunk-CKKN25P2.js.map → chunk-FOG7YEJP.js.map} +0 -0
  355. /package/dist/{chunk-4ATF4BLF.js.map → chunk-H36KSN6X.js.map} +0 -0
  356. /package/dist/{chunk-QQRRKO7W.js.map → chunk-H7HP56F6.js.map} +0 -0
  357. /package/dist/{chunk-RJZQ2PVD.js.map → chunk-HM4JSXX2.js.map} +0 -0
  358. /package/dist/{chunk-3YGUHUJA.js.map → chunk-HTGRQZFB.js.map} +0 -0
  359. /package/dist/{chunk-JXHNUYQO.js.map → chunk-IESNYJXZ.js.map} +0 -0
  360. /package/dist/{chunk-ZYZJLYNS.js.map → chunk-ISSU3OH6.js.map} +0 -0
  361. /package/dist/{chunk-OCJ3JA4O.js.map → chunk-J4MN6XIU.js.map} +0 -0
  362. /package/dist/{chunk-HWI7GHJZ.js.map → chunk-J4UJ5FRV.js.map} +0 -0
  363. /package/dist/{chunk-QVXZFJBU.js.map → chunk-JVLSU7S2.js.map} +0 -0
  364. /package/dist/{chunk-7Q3RHOA5.js.map → chunk-KDJGJR7F.js.map} +0 -0
  365. /package/dist/{chunk-M7WBE3OR.js.map → chunk-L6GTLZRK.js.map} +0 -0
  366. /package/dist/{chunk-2ZT4INZ4.js.map → chunk-LF6DMX4C.js.map} +0 -0
  367. /package/dist/{chunk-4K6BIMLF.js.map → chunk-LKFKXS5K.js.map} +0 -0
  368. /package/dist/{chunk-POFYIGQ2.js.map → chunk-LR4Z47QD.js.map} +0 -0
  369. /package/dist/{chunk-LUYPYCHY.js.map → chunk-MTBXJAS4.js.map} +0 -0
  370. /package/dist/{chunk-FX3UIDCS.js.map → chunk-N4CNVSUF.js.map} +0 -0
  371. /package/dist/{chunk-FU5VYEYK.js.map → chunk-NLKLXXTO.js.map} +0 -0
  372. /package/dist/{chunk-YYV53EHO.js.map → chunk-OPXR4PB2.js.map} +0 -0
  373. /package/dist/{chunk-5F7BZAGV.js.map → chunk-OWLVBOWO.js.map} +0 -0
  374. /package/dist/{chunk-6NR36UNB.js.map → chunk-P6ASQOZR.js.map} +0 -0
  375. /package/dist/{chunk-5ATC6UT4.js.map → chunk-PCQDY6QN.js.map} +0 -0
  376. /package/dist/{chunk-EQOTJL76.js.map → chunk-PGFKIOR3.js.map} +0 -0
  377. /package/dist/{chunk-D2YFQ7XG.js.map → chunk-POS53FG6.js.map} +0 -0
  378. /package/dist/{chunk-ZY6P7M52.js.map → chunk-PQ6Z2TAP.js.map} +0 -0
  379. /package/dist/{chunk-5OT4G5N5.js.map → chunk-PUJTKLPL.js.map} +0 -0
  380. /package/dist/{chunk-64WHKBMP.js.map → chunk-QGJ4XIA7.js.map} +0 -0
  381. /package/dist/{chunk-DECNV4QK.js.map → chunk-QHJKK77B.js.map} +0 -0
  382. /package/dist/{chunk-JHYKSMVX.js.map → chunk-QI5NWLVW.js.map} +0 -0
  383. /package/dist/{chunk-BZ3WMOYX.js.map → chunk-RC4BROYR.js.map} +0 -0
  384. /package/dist/{chunk-IWQPYT6S.js.map → chunk-RDHUZ674.js.map} +0 -0
  385. /package/dist/{chunk-7OBIBPCV.js.map → chunk-S35S77YU.js.map} +0 -0
  386. /package/dist/{chunk-22GR3O4J.js.map → chunk-SHILAQH5.js.map} +0 -0
  387. /package/dist/{chunk-WPZ5FVAS.js.map → chunk-T3PHCEN4.js.map} +0 -0
  388. /package/dist/{chunk-6RH7PRTL.js.map → chunk-TVIFAHZR.js.map} +0 -0
  389. /package/dist/{derive-MPOGZ4NR.js.map → chunk-UBSFPGVJ.js.map} +0 -0
  390. /package/dist/{chunk-323MN67Z.js.map → chunk-UCUALOSM.js.map} +0 -0
  391. /package/dist/{chunk-B4CRUZPR.js.map → chunk-VAG47QHV.js.map} +0 -0
  392. /package/dist/{chunk-QH47SQLD.js.map → chunk-VFIWDPXZ.js.map} +0 -0
  393. /package/dist/{chunk-ONPVHX2Y.js.map → chunk-W5LHQXZU.js.map} +0 -0
  394. /package/dist/{chunk-ZIMQDXTQ.js.map → chunk-WK5FSW7T.js.map} +0 -0
  395. /package/dist/{chunk-PFWE7FJG.js.map → chunk-WLLUM35R.js.map} +0 -0
  396. /package/dist/{chunk-EV2TDHQZ.js.map → chunk-XSN63FUS.js.map} +0 -0
  397. /package/dist/{chunk-OR2FYRYR.js.map → chunk-XVU7J43V.js.map} +0 -0
  398. /package/dist/{chunk-DNKVKO7K.js.map → chunk-Y6GZ2P4M.js.map} +0 -0
  399. /package/dist/{chunk-ATY3ZASK.js.map → chunk-YCSM3GZU.js.map} +0 -0
  400. /package/dist/{chunk-RDB66EKD.js.map → chunk-YMLQBZFJ.js.map} +0 -0
  401. /package/dist/{chunk-EHRIDNZX.js.map → chunk-YPSOHDNC.js.map} +0 -0
  402. /package/dist/{chunk-KM6ZXG7U.js.map → chunk-YXA2TCTZ.js.map} +0 -0
  403. /package/dist/{chunk-TKBB62DN.js.map → chunk-Z2PWOSCJ.js.map} +0 -0
  404. /package/dist/{chunk-5O5ZSW7N.js.map → chunk-Z7ZXZL6Y.js.map} +0 -0
  405. /package/dist/{chunk-2ZCWHCLF.js.map → chunk-ZO252Q5S.js.map} +0 -0
  406. /package/dist/{classified-marker-AZDSDG6V.js.map → classified-marker-BSQHU4JT.js.map} +0 -0
  407. /package/dist/{enclave-Y2ALNDRD.js.map → collection-facade-LG3NYZVB.js.map} +0 -0
  408. /package/dist/{executor-BBEEUFO3.js.map → computed-ASBNTLOK.js.map} +0 -0
  409. /package/dist/{dead-filter-JNQNB3TA.js.map → dead-filter-QKU56XTQ.js.map} +0 -0
  410. /package/dist/{delegation-5F36DRDH.js.map → delegation-XWRPYODW.js.map} +0 -0
  411. /package/dist/{executor-G6G3YEWR.js.map → derive-CXHCY46K.js.map} +0 -0
  412. /package/dist/{dispatch-3ZA733EY.js.map → dispatch-4AP4PUZU.js.map} +0 -0
  413. /package/dist/{dispatch-TNM5CRLZ.js.map → dispatch-VR4LRXZ7.js.map} +0 -0
  414. /package/dist/{executor-ZUTUMTPA.js.map → enclave-NYPT7WA7.js.map} +0 -0
  415. /package/dist/{export-accessible-S3KEB763.js.map → executor-3KFRQETS.js.map} +0 -0
  416. /package/dist/{extract-partition-KVCTSUVE.js.map → executor-AFHQYRAU.js.map} +0 -0
  417. /package/dist/{find-V4FN5ITT.js.map → executor-YYDW6XQI.js.map} +0 -0
  418. /package/dist/{issue-5CFBWBRX.js.map → export-accessible-BX2ILB4C.js.map} +0 -0
  419. /package/dist/{ledger-MQIIOTZB.js.map → extract-partition-UPOVITFI.js.map} +0 -0
  420. /package/dist/{fanout-sidecar-3QZU7ZZX.js.map → fanout-sidecar-IWP3R4RC.js.map} +0 -0
  421. /package/dist/{liberate-LW7OKVBH.js.map → find-SZBL2HPB.js.map} +0 -0
  422. /package/dist/{link-set-5PJBBR63.js.map → issue-G53NB6EW.js.map} +0 -0
  423. /package/dist/{managed-secret-ETDG6AAE.js.map → ledger-T5K4P2D7.js.map} +0 -0
  424. /package/dist/{noydb-LFDRMYZ5.js.map → liberate-VEOZRESE.js.map} +0 -0
  425. /package/dist/{register-JBOEHTVX.js.map → link-set-76JZ6YB4.js.map} +0 -0
  426. /package/dist/{registry-5DYHQFJS.js.map → managed-secret-6YC32VXG.js.map} +0 -0
  427. /package/dist/{migrate-cek-V3VDRV24.js.map → migrate-cek-LBRKYUZZ.js.map} +0 -0
  428. /package/dist/{registry-EANV44W2.js.map → noydb-4VV2ZGLZ.js.map} +0 -0
  429. /package/dist/{registry-J25H6VB4.js.map → on/index.js.map} +0 -0
  430. /package/dist/{pod-handle-AZ3CXY5Y.js.map → pod-handle-P6DPBM5U.js.map} +0 -0
  431. /package/dist/{post-register-QCJYRMIK.js.map → post-register-53O7AJRZ.js.map} +0 -0
  432. /package/dist/{registry-R23SQUQH.js.map → register-6TCBR6XD.js.map} +0 -0
  433. /package/dist/{request-withdrawal-KQ4W4UT5.js.map → registry-DH2L4O2V.js.map} +0 -0
  434. /package/dist/{revoke-H3VSTAIE.js.map → registry-IWFMXCHX.js.map} +0 -0
  435. /package/dist/{signer-53AQ3BU2.js.map → registry-Q3FG3OAX.js.map} +0 -0
  436. /package/dist/{stale-TH7SSVRV.js.map → registry-YC3IFG3A.js.map} +0 -0
  437. /package/dist/{storage-WSF7ZKTL.js.map → request-withdrawal-NM5VYR6F.js.map} +0 -0
  438. /package/dist/{reveal-W7BKZSUP.js.map → reveal-XBIMHAXR.js.map} +0 -0
  439. /package/dist/{walk-NABCHDZN.js.map → revoke-MJNYWO72.js.map} +0 -0
  440. /package/dist/{seed-55FPOMYP.js.map → seed-NSNIGYXE.js.map} +0 -0
  441. /package/dist/{withdraw-accessible-VCINIG6V.js.map → signer-6XVS2AU3.js.map} +0 -0
  442. /package/dist/{verify-M5LQH43C.js.map → verify-WYOFLSNA.js.map} +0 -0
@@ -1,22 +1,22 @@
1
1
  import {
2
2
  BLOB_CHUNKS_COLLECTION,
3
3
  BLOB_INDEX_COLLECTION
4
- } from "./chunk-JMJSCI7U.js";
4
+ } from "./chunk-ALB64CZM.js";
5
5
  import {
6
6
  installViaBinder
7
- } from "./chunk-C3XWJCG7.js";
7
+ } from "./chunk-XDVVBETV.js";
8
8
  import {
9
9
  buildRecordAad
10
- } from "./chunk-CXPOG3ZY.js";
10
+ } from "./chunk-2LPVI4PX.js";
11
11
  import {
12
12
  buildRecordEnvelope
13
- } from "./chunk-FX3UIDCS.js";
13
+ } from "./chunk-N4CNVSUF.js";
14
14
  import {
15
15
  encrypt
16
- } from "./chunk-ONPVHX2Y.js";
16
+ } from "./chunk-W5LHQXZU.js";
17
17
  import {
18
18
  ValidationError
19
- } from "./chunk-Z3BZXPRO.js";
19
+ } from "./chunk-JBIV4FLR.js";
20
20
 
21
21
  // src/via/blob/binding.ts
22
22
  async function eraseBlobs(ctx, cfg) {
@@ -38,7 +38,7 @@ function buildBlobDescribeFragment(fields) {
38
38
  )
39
39
  };
40
40
  }
41
- function blobBinding(cfg) {
41
+ function blobVia(cfg) {
42
42
  return {
43
43
  brand: "blob",
44
44
  // encryptedAtRest: 'envelope' — blob chunks are AEAD-encrypted envelopes
@@ -53,7 +53,7 @@ function blobBinding(cfg) {
53
53
  };
54
54
  }
55
55
  function linkBlobVia() {
56
- installViaBinder("blob", (c) => blobBinding(c));
56
+ installViaBinder("blob", (c) => blobVia(c));
57
57
  }
58
58
 
59
59
  // src/port/with/blob-strategy.ts
@@ -438,4 +438,4 @@ export {
438
438
  BLOB_EVICTION_AUDIT_COLLECTION,
439
439
  runCompaction
440
440
  };
441
- //# sourceMappingURL=chunk-LIR2DJWP.js.map
441
+ //# sourceMappingURL=chunk-F23WQK3G.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/via/blob/binding.ts","../src/port/with/blob-strategy.ts","../src/with-shape/blobs/export-blobs.ts","../src/with-shape/blobs/orphan-report.ts","../src/with-shape/blobs/blob-compaction.ts"],"sourcesContent":["/**\n * The blob `NoydbVia` (#629 Task 7) — wires the `blobFields` declaration\n * into the kernel's generic Via port as a deliberately THIN binding:\n * declaration + posture + `describeFragment` + `erase` glue, and nothing\n * else. Blob content is fully out-of-band (`collection.blob(id)` →\n * `BlobSet` writing `_blob_*` side-collections; it never touches\n * `_putInternal` or the record codec), so this binding declares **no\n * write/read pipeline hooks** — in particular no `encodeAtRest`/\n * `decodeAtRest`: a blobFields collection must not flip the pipeline's\n * `hasAtRestHooks`, or the codec would abandon its inline seal path for a\n * feature that never seals record fields. The routing/compaction/TTL/\n * legal-hold/exportBlobs machinery stays service-side in\n * `with-shape/blobs/` (the i18n-dictionary precedent — that machinery\n * does real chunk AEAD + key-lifecycle work `via-enclave-isolation`\n * forbids under `via/*`).\n *\n * Like classified (see `port/with/classified-strategy.ts`), the link is\n * EAGER — `port/with/blob-strategy.ts` calls {@link linkBlobVia} at module\n * load. `blobFields` policies are plain object literals (there is no\n * `blob.*()` declaration factory to hang a lazy link on), so the binder\n * must be installed before `compileVias` ever sees one.\n *\n * There is no `declare`-time validation: `blobFields` has never had a\n * construction-time refusal matrix (policies are consulted lazily by\n * `vault.compact()` and `BlobSet.put`), and inventing one here would\n * break the behavior lock.\n *\n * `covers` (#629 Task 8) is a passive coverage predicate, not a pipeline\n * hook — it lets `ViaPipeline.postureFor` recognize a `blobFields` slot so\n * `.where()`/`.orderBy()`/`.aggregate()` can refuse it (posture\n * `queryable: 'none'`); it does not participate in clause building or\n * evaluation.\n */\nimport type { NoydbVia, ViaEraseCtx, ViaEraseReport } from '../../kernel/via/index.js'\nimport { installViaBinder } from '../../kernel/via/index.js'\nimport type { BlobFieldsConfig } from '../../with-shape/blobs/blob-compaction.js'\n\n/**\n * Config a blob collection's `blobFields` declaration resolves to — the\n * binding's construction input. `purgeBlobsForRecord` is an OPTIONAL\n * vault/service-provided closure for the `erase` hook, mapping\n * `collection.blob(id).shredAllForRecord()`'s `{shredded, retainedShared,\n * residue}: string[]` accounting onto `ViaEraseReport` — `shredded`/\n * `retainedShared` become counts, each residue eTag becomes a tagged\n * `{kind: 'blob-residue', eTag}` entry.\n *\n * DELIBERATELY left unwired by `compileVias` (#629 Task 10, resolved\n * within parity-first — see task-10-report.md \"blob purge\" section):\n * `per-blob-cek.test.ts`/`forget.test.ts` prove blob-shred-on-forget is\n * gated SOLELY on the vault's `blobsStrategy` being configured, never on a\n * given collection's OWN `blobFields` declaration (`.blob(id)` is callable,\n * and IS exercised by those tests, on collections with no `blobFields` at\n * all). Routing it exclusively through this binding would silently stop\n * blob crypto-shredding for every such collection; `vault.forget()` keeps\n * calling `collection.blob(id).shredAllForRecord()` directly for that\n * reason. The hook stays real/tested (see `blob-binding.test.ts`) for a\n * future caller that wants it.\n */\nexport interface BlobViaConfig {\n readonly fields: BlobFieldsConfig\n readonly collectionName: string\n /** Purge one record's blob slots (crypto-shred participation). Returns the erase report for `forget()`'s summary. */\n readonly purgeBlobsForRecord?: (id: string) => Promise<ViaEraseReport>\n}\n\n/**\n * `forget()`'s per-ref blob-purge participation: delegate to the wired\n * closure, or report a clean zero-shredded, zero-residue no-op — the\n * default in production too (#629 Task 10 deliberately leaves this unwired;\n * see `BlobViaConfig`'s doc comment).\n */\nasync function eraseBlobs(ctx: ViaEraseCtx, cfg: BlobViaConfig): Promise<ViaEraseReport> {\n if (cfg.purgeBlobsForRecord) return cfg.purgeBlobsForRecord(ctx.id)\n return { shredded: 0, residue: [] }\n}\n\n/**\n * One field's blob knobs as they appear on `describeFragment()`'s payload\n * (#657 — the second real `NoydbVia.describeFragment` consumer after\n * `lookup`'s; see `with-shape/introspection/describe.ts`'s `buildDescription`).\n * Declarative scalars (`retainDays`/`external`/`public`/`backlink`) verbatim;\n * predicate knobs (`evictWhen`/`legalHold`/`retainUntil` — functions over the\n * decrypted record) as presence flags, since a predicate has no serializable\n * form — mirrors `buildBlobDescribeFragment`'s per-field shape below.\n */\nexport interface BlobDescribeFragmentEntry {\n readonly retainDays?: number\n readonly evictWhen?: true\n readonly legalHold?: true\n readonly retainUntil?: true\n readonly external?: true\n readonly public?: true\n readonly backlink?: string\n}\n\n/** The `'blob'` binding's `describeFragment()` payload shape. */\nexport interface BlobDescribeFragment {\n readonly blobFields: Record<string, BlobDescribeFragmentEntry>\n}\n\n/**\n * `{ blobFields: { <field>: <knobs> } }` — declarative scalars\n * (`retainDays`/`external`/`public`/`backlink`) verbatim; predicate knobs\n * (`evictWhen`/`legalHold`/`retainUntil` — functions over the decrypted\n * record) as presence flags, since a predicate has no serializable form.\n */\nfunction buildBlobDescribeFragment(fields: BlobFieldsConfig): Record<string, unknown> {\n return {\n blobFields: Object.fromEntries(\n Object.entries(fields).map(([field, p]) => [field, {\n ...(p.retainDays !== undefined ? { retainDays: p.retainDays } : {}),\n ...(p.evictWhen !== undefined ? { evictWhen: true } : {}),\n ...(p.legalHold !== undefined ? { legalHold: true } : {}),\n ...(p.retainUntil !== undefined ? { retainUntil: true } : {}),\n ...(p.external === true ? { external: true } : {}),\n ...(p.public === true ? { public: true } : {}),\n ...(p.backlink !== undefined ? { backlink: p.backlink } : {}),\n }]),\n ),\n }\n}\n\nexport function blobVia(cfg: BlobViaConfig): NoydbVia {\n return {\n brand: 'blob',\n // encryptedAtRest: 'envelope' — blob chunks are AEAD-encrypted envelopes\n // in `_blob_chunks` (service-side), not sealed record fields; queryable:\n // 'none' — nothing indexes blob content; exportable — vault.exportBlobs()\n // is a first-class door; forgettable — shredAllForRecord participates in\n // vault.forget().\n posture: { encryptedAtRest: 'envelope', queryable: 'none', exportable: true, forgettable: true },\n covers: (field) => field in cfg.fields,\n erase: (ctx) => eraseBlobs(ctx, cfg),\n describeFragment: () => buildBlobDescribeFragment(cfg.fields),\n }\n}\n\nexport function linkBlobVia(): void {\n installViaBinder('blob', (c) => blobVia(c as BlobViaConfig))\n}\n","/**\n * Strategy seam between core Collection and the optional blob service\n * (#629 Task 7 — moved out of `with-shape/blobs/strategy.ts`, precedent:\n * `port/with/classified-strategy.ts`/`i18n-strategy.ts`). Lives on the\n * `/with` port (the one seam the kernel spine may import statically) so\n * `Collection`/`Vault` can hold the `NO_BLOBS` default without a\n * spine→service static import.\n *\n * Core imports `BlobsStrategy` as a TYPE-ONLY symbol and `NO_BLOBS` as a\n * minimal runtime stub. Neither pulls in the heavy `BlobSet` / chunk /\n * MIME machinery — those only arrive when the consumer explicitly\n * imports `@noy-db/hub/blobs` (see `via/blob/index.ts` →\n * `withBlobs()` factory).\n *\n * This file is intentionally tiny so the bundler keeps it in the graph\n * without dragging everything else in.\n *\n * @internal\n */\n\nimport type { BlobSet } from '../../with-shape/blobs/blob-set.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { ObjectProjection } from '../../with-shape/blobs/object-projection.js'\nimport type { BlobFieldsConfig } from '../../with-shape/blobs/blob-compaction.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\nimport type { BlobCacheStats } from '../../with-shape/blobs/blob-pinning.js'\nimport type { UnlockedKeyring } from '../../with-party/team/keyring.js'\nimport { linkBlobVia } from '../../via/blob/binding.js'\n\n// Install the blob Via binder EAGERLY (#629 Task 7) — `blobFields` policies\n// are plain object literals (there is no `blob.*()` declaration factory to\n// hang a lazy money/i18n-style link on), so the binder must be installed\n// before `compileVias` ever compiles one. This port module is\n// already unconditionally imported by the kernel spine (for the `NO_BLOBS`\n// strategy default below), so linking here guarantees it — same rationale\n// as `port/with/classified-strategy.ts`'s eager `linkClassifiedVia()`. The\n// binding module is hook-free glue (declare/describe/erase) and imports no\n// machinery, so the eager cost is a few hundred bytes, not `BlobSet`.\nlinkBlobVia()\n\n/**\n * Args forwarded by `Collection.blob(id)` to the active strategy's\n * `openSlot`. The strategy is responsible for returning a live\n * `BlobSet` bound to the given record.\n *\n * @internal\n */\nexport interface BlobsStrategyOpenArgs {\n readonly store: NoydbStore\n readonly vault: string\n readonly collection: string\n readonly recordId: string\n readonly getDEK: (collectionName: string) => Promise<EnclaveKey>\n readonly encrypted: boolean\n readonly userId: string\n /**\n * Collection opts into per-record keys (`perRecordKeys`), so its blobs are\n * erasable: new uploads mint a per-blob content CEK (crypto-shreddable at\n * `refCount → 0`). Off → legacy shared-`_blob`-DEK chunks. See the per-blob\n * CEK design spec.\n */\n readonly erasableBlobs?: boolean\n /**\n * Collection has `tiers` active (#724 I1 completion). Combined with\n * `erasableBlobs` false, a content write is refused —\n * `UnsupportedTierCompositionError` — since a legacy blob has no\n * per-blob `_cek` and can never be tier-isolated on elevate/demote.\n */\n readonly tiersActive?: boolean\n /**\n * Vault is in debug-plaintext mode (`encrypt: false` + `debugPlaintext: true`).\n * Blobs are then written as a single un-gzipped object (one base64 chunk) so\n * the stored object is directly recoverable with native tools (`base64 -d`),\n * matching the directly-inspectable record layout. Dev-only — see the\n * plaintext/debug-store-mode design.\n */\n readonly debugPlaintext?: boolean\n /**\n * Object projection for `external` blob fields (`createNoydb({ objectStore })`).\n * When present, fields declared `external` in `blobFields` route their raw\n * bytes here instead of the encrypted-chunk path.\n */\n readonly objectStore?: ObjectProjection\n /** Per-collection blob field policies — used to resolve `external` / `public`. */\n readonly blobFields?: BlobFieldsConfig\n /**\n * The caller's unlocked keyring (#749). Threaded through so `BlobSet.atTier()`\n * can run `assertTierAccess` — the SAME clearance gate `putAtTier`/`elevate`/\n * `demote` run before any tier `getDEK` call — rather than relying on a bare\n * `getDEK`, which auto-mints a DEK for ANY role and never throws (verified: it\n * is not itself a gate). Omitted only by a construction path that never calls\n * `atTier()`.\n */\n readonly keyring?: UnlockedKeyring\n}\n\n/**\n * The seam interface. `@internal` — do not build public APIs on this\n * shape; it can evolve freely until blobs are extracted into their\n * own package, at which point it will be promoted to public.\n *\n * @internal\n */\nexport interface BlobsStrategy {\n openSlot(args: BlobsStrategyOpenArgs): BlobSet\n /**\n * Snapshot of this device's blob-cache KPI counters: local-read hits,\n * network misses, and total bytes downloaded from the object store (#808).\n *\n * Lives on the seam rather than on the `withBlobs()` return type so the\n * service has exactly one name (#844b) — `NO_BLOBS` answers with zeros,\n * which is the truthful reading of \"blobs were never enabled\".\n */\n cacheStats(): BlobCacheStats\n}\n\n/**\n * Default strategy for collections that did not opt into blob storage.\n * Every operation surfaces an actionable error that points the caller\n * at the opt-in path.\n *\n * @internal\n */\nexport const NO_BLOBS: BlobsStrategy = {\n openSlot() {\n throw new Error(\n 'Blob storage is not enabled on this Noydb instance. ' +\n 'Import `{ withBlobs }` from \"@noy-db/hub/blobs\" and pass `withBlobs()` to `createNoydb({ blobsStrategy: withBlobs() })`.',\n )\n },\n cacheStats() {\n return { hits: 0, misses: 0, bytesDownloaded: 0 }\n },\n}\n","/**\n * `vault.exportBlobs()` — bulk blob extraction primitive.\n *\n * Async-iterable handle over every blob attached to records in a\n * vault, optionally filtered by collection allowlist and per-record\n * predicate. Emits tuples of `{ blobId, recordRef, bytes, meta }` so\n * the consumer can pipe into any sink (zip stream, S3 multipart, USB\n * copy, cold-storage tape) without pulling the whole export into\n * memory.\n *\n * ## Auth + audit\n *\n * - Capability check runs **once** at handle creation via\n * `Vault.assertCanExport('plaintext', 'blob')`. An operator whose\n * keyring lacks that bit fails before a single byte of ciphertext\n * is decrypted.\n * - Audit entry lands in `_export_audit` at handle creation: the\n * actor, start timestamp, target collections, predicate presence,\n * and batch mechanism. **No content hashes** — per the spec\n * non-correlation invariant.\n *\n * ## Abort + resume\n *\n * - `handle.abort()` flips the internal signal; the next iteration\n * boundary throws `AbortError`. Consumers already in `for await`\n * can catch and exit cleanly.\n * - Restart after a partial failure with `{ afterBlobId }` — the\n * iterator skips tuples up to (and including) that blob id before\n * yielding again. Combined with a blob-count ceiling it supports\n * idempotent batch re-runs.\n *\n * @module\n */\n\nimport type { Collection } from '../../kernel/collection.js'\nimport type { SlotInfo } from '../../kernel/types.js'\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\nexport interface ExportBlobsOptions {\n /**\n * Collection allowlist. Omit to export blobs from every collection\n * the caller has read access to.\n */\n readonly collections?: readonly string[]\n /**\n * Per-record predicate. Called on the decrypted record BEFORE any\n * blob bytes are read for that record — returning false skips the\n * record and all its slots without touching their chunks.\n */\n readonly where?: (record: unknown, context: { collection: string; id: string }) => boolean\n /**\n * Resume after a specific blob id. The iterator skips tuples up to\n * and including this id, then yields. Format of the id is the same\n * as `ExportedBlob.blobId` (the HMAC-keyed eTag).\n */\n readonly afterBlobId?: string\n /**\n * External abort signal. When fired, the next iterator tick throws\n * `ExportBlobsAbortedError`. Honored alongside `handle.abort()`.\n */\n readonly signal?: AbortSignal\n}\n\nexport interface ExportedBlob {\n /** Opaque blob identifier — HMAC-keyed eTag, stable across vaults. */\n readonly blobId: string\n /** Where this blob came from in the vault. */\n readonly recordRef: {\n readonly collection: string\n readonly id: string\n readonly slot: string\n }\n /** Decrypted plaintext bytes. */\n readonly bytes: Uint8Array\n /** Best-effort metadata (from the blob slot record). */\n readonly meta: {\n readonly size: number\n /**\n * User-visible filename stored on the slot. Often equal to the\n * slot name; differs when the caller supplied an explicit\n * `filename` to `BlobSet.put()`.\n */\n readonly filename: string\n readonly mimeType?: string\n readonly createdAt?: string\n }\n}\n\nexport interface ExportBlobsHandle extends AsyncIterable<ExportedBlob> {\n /** Abort the export. Safe to call multiple times. */\n abort(): void\n /** True once `abort()` has fired or the external signal aborted. */\n readonly aborted: boolean\n}\n\nexport class ExportBlobsAbortedError extends Error {\n constructor(reason: string) {\n super(`exportBlobs aborted: ${reason}`)\n this.name = 'ExportBlobsAbortedError'\n }\n}\n\n// ─── Audit ──────────────────────────────────────────────────────────────\n\nexport const EXPORT_AUDIT_COLLECTION = '_export_audit'\n\nexport interface ExportBlobsAuditEntry {\n readonly id: string\n readonly mechanism: 'exportBlobs'\n readonly actor: string\n readonly startedAt: string\n readonly collections: readonly string[] | null\n readonly predicate: boolean\n readonly afterBlobId: string | null\n}\n\n// ─── Implementation ─────────────────────────────────────────────────────\n\n/**\n * Build the handle. Factored out of `Vault.exportBlobs` so the\n * implementation can be unit-tested without going through the\n * compartment lifecycle.\n */\nexport function createExportBlobsHandle(\n actor: string,\n listAccessibleCollections: () => Promise<string[]>,\n getCollection: <T>(name: string) => Collection<T>,\n writeAudit: (entry: ExportBlobsAuditEntry) => Promise<void>,\n options: ExportBlobsOptions,\n): ExportBlobsHandle {\n let aborted = false\n\n const abort = (): void => {\n aborted = true\n }\n\n if (options.signal) {\n if (options.signal.aborted) aborted = true\n options.signal.addEventListener('abort', () => { aborted = true })\n }\n\n function assertLive(): void {\n if (aborted) throw new ExportBlobsAbortedError('aborted by caller')\n }\n\n const allowlist = options.collections ? new Set(options.collections) : null\n\n // Write the audit entry BEFORE the first yield so a blocked\n // iteration still leaves an audit trail that the export started.\n let auditPromise: Promise<void> | null = null\n function writeAuditOnce(): Promise<void> {\n if (!auditPromise) {\n auditPromise = writeAudit({\n id: generateBatchId(),\n mechanism: 'exportBlobs',\n actor,\n startedAt: new Date().toISOString(),\n collections: options.collections ?? null,\n predicate: Boolean(options.where),\n afterBlobId: options.afterBlobId ?? null,\n })\n }\n return auditPromise\n }\n\n async function* generate(): AsyncGenerator<ExportedBlob> {\n await writeAuditOnce()\n assertLive()\n\n // Resolve target collections lazily — also keeps the call async.\n const allCollections = await listAccessibleCollections()\n const targets = allCollections.filter(name => {\n if (name.startsWith('_')) return false\n if (allowlist && !allowlist.has(name)) return false\n return true\n })\n\n let resumeCursorHit = options.afterBlobId === undefined\n\n for (const collectionName of targets) {\n if (aborted) return\n\n const coll = getCollection<Record<string, unknown>>(collectionName)\n const records = await coll.list().catch(() => [])\n for (const record of records) {\n if (aborted) return\n assertLive()\n\n const idField = (record as { id?: unknown }).id\n if (typeof idField !== 'string') continue\n\n if (options.where && !options.where(record, { collection: collectionName, id: idField })) continue\n\n const blobSet = coll.blob(idField)\n const slots = await blobSet.list().catch(() => [] as SlotInfo[])\n for (const slot of slots) {\n if (aborted) return\n\n if (!resumeCursorHit) {\n if (slot.eTag === options.afterBlobId) {\n resumeCursorHit = true\n }\n continue\n }\n\n const bytes = await blobSet.get(slot.name)\n if (!bytes) continue\n\n const item: ExportedBlob = {\n blobId: slot.eTag,\n recordRef: { collection: collectionName, id: idField, slot: slot.name },\n bytes,\n meta: {\n size: slot.size,\n filename: slot.filename,\n ...(slot.mimeType !== undefined && { mimeType: slot.mimeType }),\n ...(slot.uploadedAt !== undefined && { createdAt: slot.uploadedAt }),\n },\n }\n yield item\n }\n }\n }\n }\n\n const handle: ExportBlobsHandle = {\n abort,\n get aborted() { return aborted },\n [Symbol.asyncIterator]: () => generate(),\n }\n return handle\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────\n\nfunction generateBatchId(): string {\n // 16 bytes of crypto randomness, URL-safe base64, no padding.\n const raw = globalThis.crypto.getRandomValues(new Uint8Array(16))\n let s = ''\n for (const b of raw) s += b.toString(16).padStart(2, '0')\n return `batch-${Date.now().toString(36)}-${s.slice(0, 12)}`\n}\n","/**\n * Orphan blob-chunk REPORT (#1133) — the read-only half of the residue #1127\n * stopped producing.\n *\n * Before #1127, `releaseRef` deleted a blob's index row BEFORE its chunks, so a\n * crash in between stranded chunk bodies nothing can ever reach: `loadBlobObject`\n * returns null so no reader addresses them, and `rekeyBlobSet` derives its chunk\n * ids from each index entry's `chunkCount` so a rotation never visits them. For a\n * LEGACY blob (bytes under the shared `_blob` DEK rather than a per-blob content\n * CEK) those bodies then stay openable under the retired DEK indefinitely.\n *\n * #1127 reversed the order, which removes the class at the source. It does\n * nothing for orphans created before the fix, and this module does not delete\n * them either. It counts them.\n *\n * ## Why this REPORTS and does not RECLAIM\n *\n * Deciding \"orphaned\" requires `store.list(_blob_index)`, and the store is\n * untrusted — that is the premise of the product, not a caveat on it. A store\n * that withholds a single index row makes a LIVE blob's chunks look orphaned. A\n * reclaim pass acting on that would convert **withholding**, which is reversible\n * the moment the store stops lying, into **permanent destruction** — handing an\n * untrusted store a data-loss primitive it does not currently have, in order to\n * tidy up residue.\n *\n * Reporting inverts the risk to something harmless: a lying store can only\n * inflate this count. Nothing is deleted on its word, so the worst outcome is an\n * operator investigating residue that turns out not to exist.\n *\n * Re-keying is not an alternative either: the chunk AAD is\n * `{eTag}:{chunkIndex}:{chunkCount}`, and `chunkCount` lives only in the index\n * row that is already gone — an orphan's AAD is unreconstructable, so it cannot\n * be decrypted-and-re-encrypted at all.\n */\n\nimport type { NoydbStore } from '../../kernel/types.js'\nimport { BLOB_INDEX_COLLECTION, BLOB_CHUNKS_COLLECTION } from './blob-set.js'\n\n/** How many distinct orphaned eTags to name in the report. */\nconst SAMPLE_LIMIT = 10\n\nexport interface OrphanChunkReport {\n /** Chunk rows whose eTag has no `_blob_index` entry. */\n readonly chunks: number\n /** Distinct eTags those chunks belong to. */\n readonly eTags: number\n /**\n * Up to {@link SAMPLE_LIMIT} of those eTags, so an operator can inspect a real\n * one rather than only a count. Deterministic (store list order), not random.\n */\n readonly sampleETags: readonly string[]\n /**\n * Chunk ids that do not match the `{eTag}_{index}` grammar at all. Counted\n * rather than skipped: an id this module cannot parse is a thing it cannot\n * classify either way, and reporting zero orphans while silently dropping\n * such ids would be the reassuring-but-wrong answer.\n */\n readonly unparseable: number\n}\n\n/**\n * Split a chunk id into `{eTag, index}`, or `null` if it does not fit the\n * grammar `BlobSet` writes (`${eTag}_${i}`).\n *\n * Splits on the LAST underscore and requires a digits-only suffix. eTags are\n * HMAC-SHA-256 hex today and contain no underscore, so a simpler split would\n * work — but it would break silently if that ever changed, and this rule holds\n * either way.\n */\nfunction parseChunkId(id: string): { eTag: string; index: number } | null {\n const cut = id.lastIndexOf('_')\n if (cut <= 0 || cut === id.length - 1) return null\n const suffix = id.slice(cut + 1)\n if (!/^\\d+$/.test(suffix)) return null\n return { eTag: id.slice(0, cut), index: Number(suffix) }\n}\n\n/**\n * Count chunk rows with no surviving index row. Read-only: two `list` calls and\n * no `get`, no `delete`, and no decryption — an orphan cannot be opened anyway.\n *\n * A store that cannot list either collection yields an empty report rather than\n * throwing: this is a diagnostic riding along with a maintenance pass, and it\n * must not be able to fail one.\n */\nexport async function reportOrphanBlobChunks(\n adapter: NoydbStore,\n vault: string,\n): Promise<OrphanChunkReport> {\n const empty: OrphanChunkReport = { chunks: 0, eTags: 0, sampleETags: [], unparseable: 0 }\n let liveETags: Set<string>\n let chunkIds: string[]\n try {\n liveETags = new Set(await adapter.list(vault, BLOB_INDEX_COLLECTION))\n chunkIds = await adapter.list(vault, BLOB_CHUNKS_COLLECTION)\n } catch {\n return empty\n }\n\n let chunks = 0\n let unparseable = 0\n const orphanETags = new Set<string>()\n for (const id of chunkIds) {\n const parsed = parseChunkId(id)\n if (parsed === null) { unparseable += 1; continue }\n if (liveETags.has(parsed.eTag)) continue\n chunks += 1\n orphanETags.add(parsed.eTag)\n }\n\n return {\n chunks,\n eTags: orphanETags.size,\n sampleETags: [...orphanETags].slice(0, SAMPLE_LIMIT),\n unparseable,\n }\n}\n","/**\n * Blob retention + compaction.\n *\n * Declarative per-collection / per-slot eviction policy. Two\n * triggers:\n *\n * - **`retainDays`** — age-based TTL. A slot uploaded more than N\n * days ago is evicted.\n * - **`evictWhen(record)`** — predicate over the **decrypted**\n * record. Lets consumers express \"the image is safe to drop once\n * the structured invoice has been reviewed and confirmed.\"\n *\n * Either trigger (or both) causes the slot to evict. Eviction removes\n * the slot entry from `_blob_slots_{collection}`, decrements the\n * blob's refCount (so unreferenced chunks can be GC'd by the next\n * sweep), and writes one entry to the `_blob_eviction_audit`\n * collection for tamper-evident record-keeping.\n *\n * The audit entry carries the eTag of the evicted blob (opaque HMAC\n * of plaintext under the vault's `_blob` DEK) — no plaintext leakage,\n * per the SPEC non-correlation invariant. Consumers reconstructing\n * \"what used to be attached\" can look up the audit entry by record\n * id.\n *\n * Compaction is **consumer-scheduled** — noy-db never runs a\n * background daemon. Call `vault.compact()` whenever your workflow\n * allows (cron, manual \"tidy\" button, cold-storage export prep, …).\n *\n * @module\n */\n\nimport type { NoydbStore, EncryptedEnvelope, SlotInfo } from '../../kernel/types.js'\nimport { ValidationError } from '../../kernel/errors.js'\nimport { buildRecordAad, buildRecordEnvelope, encrypt, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { reportOrphanBlobChunks, type OrphanChunkReport } from './orphan-report.js'\n\n// ─── Config types ───────────────────────────────────────────────────────\n\nexport interface BlobFieldPolicy<T = unknown> {\n /**\n * Via port brand marker — lets a `BlobFieldPolicy` satisfy the kernel's\n * opaque `ViaDescriptor` (#629 Task 7). Optional (the\n * `ClassifiedFieldSpec._viaBrand` precedent): `blobFields` policies are\n * plain object literals with no declaration factory to stamp it, so a\n * mandatory field would break every existing declaration.\n */\n readonly _viaBrand?: 'blob'\n /**\n * Age-based TTL in days. A slot whose `uploadedAt` is older than\n * `now - retainDays × 86400s` evicts on the next `vault.compact()`.\n * Omit to disable age-based eviction.\n */\n readonly retainDays?: number\n /**\n * Predicate evaluated against the decrypted record. When it returns\n * `true`, every matching slot on that record evicts. Omit to\n * disable predicate-based eviction.\n */\n readonly evictWhen?: (record: T) => boolean\n /**\n * **Legal hold.** When this predicate returns `true`, the slot is\n * never evicted — `retainDays`/`evictWhen` are overridden. Use for a\n * litigation / audit hold on a fiscal document: the blob stays until\n * the predicate returns `false` (the hold is released). Fail-closed:\n * if the predicate throws, the slot is treated as held.\n */\n readonly legalHold?: (record: T) => boolean\n /**\n * **Period-bound retention.** Returns the date (Date / ISO string /\n * epoch ms) until which the slot must be retained — typically derived\n * from the record's fiscal period (e.g. period end + 10 years). While\n * `now < retainUntil`, the slot is never evicted, regardless of\n * `retainDays`. Return `null`/`undefined` to impose no floor.\n * Fail-closed: a throwing function holds the slot.\n */\n readonly retainUntil?: (record: T) => Date | string | number | null | undefined\n /**\n * **External projection.** When `true`, this field's bytes are stored in the\n * vault's `ObjectProjection` (`createNoydb({ objectStore })`) as a single raw,\n * **unencrypted** object — servable directly from S3/CDN and processable by\n * native tooling — instead of the encrypted-chunk path. The encrypted slot\n * record remains the catalog (anchoring invariant). Requires an `objectStore`;\n * **outside the zero-knowledge guarantee** — use only for assets meant to\n * leave the vault. See the as-aws-s3 design spec.\n */\n readonly external?: boolean\n /**\n * For an `external` field: make the object world-readable (CDN origin) rather\n * than presigned-only. Default `false` (presigned). Ignored unless `external`.\n */\n readonly public?: boolean\n /**\n * For an `external` field: how to stamp a **backlink** (this record's\n * vault/collection/id/field) onto the object's metadata — the self-describing\n * \"secondary store\" that powers reconcile / DR / import re-pairing.\n * - `'opaque-token'` (default): a random id; preserves the opaque-bucket\n * property (no names leak); the token is also recorded on the slot.\n * - `'encrypted'`: the reference encrypted under the blob DEK (ZK-preserving;\n * falls back to `'opaque-token'` on a plaintext vault).\n * - `'plain'`: the reference in cleartext metadata — **leaks structure** to\n * bucket readers; only for non-sensitive deployments.\n * - `'none'`: no backlink.\n */\n readonly backlink?: 'opaque-token' | 'encrypted' | 'plain' | 'none'\n}\n\nexport type BlobFieldsConfig<T = unknown> = Record<string, BlobFieldPolicy<T>>\n\n// ─── Audit collection ──────────────────────────────────────────────────\n\nexport const BLOB_EVICTION_AUDIT_COLLECTION = '_blob_eviction_audit'\n\nexport interface BlobEvictionEntry {\n readonly id: string\n readonly collection: string\n readonly recordId: string\n readonly slotName: string\n readonly blobHash: string\n /** `'budget'` — evicted by the #808 cache-budget LRU pass, not a policy. */\n readonly reason: 'ttl' | 'predicate' | 'both' | 'budget'\n readonly evictedAt: string\n readonly actor: string\n}\n\n// ─── Compaction result ──────────────────────────────────────────────────\n\nexport interface CompactionResult {\n /** Number of blob slots evicted by the policy pass across all collections. */\n readonly evicted: number\n /** Number of records touched (iterated + policy checked). */\n readonly records: number\n /** Number of collections with `blobFields` configured. */\n readonly collections: number\n /**\n * Number of audit entries written — one per policy eviction plus one per\n * budget SLOT eviction (dropping a device-local cache copy writes none).\n */\n readonly auditEntries: number\n /**\n * Number of slots that would have evicted (TTL/predicate triggered)\n * but were retained by a `legalHold` or `retainUntil` floor.\n */\n readonly held: number\n /**\n * #808: number of slots whose due policy eviction was skipped because they\n * are pinned for offline on THIS device (pin state is device-local).\n */\n readonly pinned: number\n /**\n * #808: number of evictions performed by the `cacheBudget` LRU pass —\n * internal slots evicted through the standard eviction writer plus\n * device-local external cache copies dropped. 0 when no budget was given.\n */\n readonly budgetEvicted: number\n /** #808: plaintext bytes freed by the `cacheBudget` LRU pass. */\n readonly budgetBytesFreed: number\n /** Per-collection breakdown for diagnostics. */\n readonly byCollection: Record<string, { records: number; evicted: number }>\n /**\n * #1133 — chunk rows in `_blob_chunks` with no surviving `_blob_index` entry.\n * Pre-#1127 residue: unreachable by every reader AND by rotation, so for a\n * legacy blob the bytes stay openable under a retired `_blob` DEK.\n *\n * REPORTED, never reclaimed — deciding \"orphaned\" means trusting the store's\n * `list`, and a store that withholds one index row would have us destroy a\n * LIVE blob's chunks. See `orphan-report.ts` for the full argument. A vault\n * that has run `migrate()` has no legacy chunks to strand, and `chunks: 0`\n * is the expected reading everywhere else.\n */\n readonly orphanBlobChunks: OrphanChunkReport\n}\n\n// ─── Core ──────────────────────────────────────────────────────────────\n\nexport interface CompactRunOptions {\n /** Override \"now\" for deterministic testing. */\n readonly now?: Date\n /**\n * Stop after this many evictions. Useful for capped batches / cron\n * jobs that need to fit in a time window. `undefined` = unbounded.\n */\n readonly maxEvictions?: number\n /**\n * Dry-run — evaluate policies and return the counts, but do NOT\n * delete slots or write audit entries. Lets a consumer preview\n * what would happen.\n */\n readonly dryRun?: boolean\n /**\n * #808 mobile cache budget: cap the locally-cached UNPINNED blob bytes.\n * After the policy pass, a dedicated LRU pass walks every collection's\n * slots (pinned slots and `legalHold`/`retainUntil`-held slots are exempt\n * and uncounted) and evicts oldest-access-first — internal slots through\n * the standard eviction writer (`deleteSlot` + a `'budget'` audit entry),\n * external slots by dropping their device-local encrypted cache copy only\n * (the object-store copy is authoritative and untouched) — until the\n * remaining unpinned bytes fit `maxBytes`. Internal slots count their\n * uncompressed `size`; external slots count `cachedBytes` (0 = nothing\n * local, nothing to evict). LRU order comes from the device-local\n * `lastAccessAt` stamp, falling back to `uploadedAt`.\n */\n readonly cacheBudget?: { readonly maxBytes: number }\n}\n\nexport interface CompactionContext {\n readonly adapter: NoydbStore\n readonly vault: string\n readonly actor: string\n readonly encrypted: boolean\n readonly getDEK: (collection: string) => Promise<EnclaveKey>\n /**\n * Resolve a collection's declared `blobFields` config. Returns an\n * empty map for collections without the config — the walk skips\n * those.\n */\n readonly getBlobFields: <T>(collection: string) => BlobFieldsConfig<T> | null\n /** List collection names in the vault. */\n readonly listCollections: () => Promise<string[]>\n /** List record ids in a collection. */\n readonly listRecords: (collection: string) => Promise<string[]>\n /** Decrypt and return the record. Null when absent. */\n readonly getRecord: <T>(collection: string, id: string) => Promise<T | null>\n /** Return the BlobSet-like handle for a record's slots. */\n readonly listSlots: (collection: string, id: string) => Promise<SlotInfo[]>\n /** Delete a slot and decrement its blob's refCount. */\n readonly deleteSlot: (collection: string, id: string, slotName: string) => Promise<void>\n /**\n * #808: drop a slot's DEVICE-LOCAL cached copy (the encrypted external\n * side-cache) without touching the slot or the object store. Returns the\n * plaintext bytes freed. Optional — absent (or a 0 return) means external\n * cache entries simply cannot be budget-evicted through this context.\n */\n readonly dropLocalCache?: (collection: string, id: string, slotName: string) => Promise<number>\n}\n\nexport async function runCompaction(\n ctx: CompactionContext,\n options: CompactRunOptions = {},\n): Promise<CompactionResult> {\n // #1133 — two `list` calls, no decryption, nothing written. Always on rather\n // than opt-in: residue an operator has to know to ask about is residue nobody\n // finds. The cost is negligible beside this pass's own per-record `getRecord`.\n const orphanBlobChunks = await reportOrphanBlobChunks(ctx.adapter, ctx.vault)\n const now = options.now ?? new Date()\n const maxEvictions = options.maxEvictions ?? Infinity\n const dryRun = options.dryRun === true\n if (options.cacheBudget !== undefined\n && (!Number.isFinite(options.cacheBudget.maxBytes) || options.cacheBudget.maxBytes < 0)) {\n throw new ValidationError('compact(): cacheBudget.maxBytes must be a non-negative finite number (#808)')\n }\n\n const allCollections = await ctx.listCollections()\n const byCollection: Record<string, { records: number; evicted: number }> = {}\n let evicted = 0\n let records = 0\n let auditEntries = 0\n let held = 0\n let pinned = 0\n let collectionsWithPolicy = 0\n\n outer: for (const collectionName of allCollections) {\n if (collectionName.startsWith('_')) continue\n const config = ctx.getBlobFields(collectionName)\n if (!config) continue\n const configuredSlots = Object.keys(config)\n if (configuredSlots.length === 0) continue\n collectionsWithPolicy += 1\n byCollection[collectionName] = { records: 0, evicted: 0 }\n\n const ids = await ctx.listRecords(collectionName)\n for (const recordId of ids) {\n if (evicted >= maxEvictions) break outer\n\n const record = await ctx.getRecord(collectionName, recordId).catch(() => null)\n if (record === null) continue\n records += 1\n byCollection[collectionName].records += 1\n\n const slots = await ctx.listSlots(collectionName, recordId).catch(() => [])\n for (const slot of slots) {\n if (evicted >= maxEvictions) break outer\n const policy = config[slot.name]\n if (!policy) continue\n\n const reason = evaluatePolicy(policy, record, slot, now)\n if (!reason) continue\n\n // #808: device-local offline pin — an exemption inside this existing\n // pass, exactly like a hold: counted, never evicted. `slot.pinned` is\n // the device-local annotation `BlobSet.list()` stamps from the\n // withBlobs() pin registry (never synced state).\n if (slot.pinned === true) {\n pinned += 1\n continue\n }\n\n // Retention floor: a legal hold or period-bound retainUntil\n // blocks an otherwise-due eviction. Counted, never evicted.\n if (isHeld(policy, record, now)) {\n held += 1\n continue\n }\n\n if (!dryRun) {\n await ctx.deleteSlot(collectionName, recordId, slot.name)\n await writeAuditEntry(ctx, {\n id: generateEvictionId(collectionName, recordId, slot.name),\n collection: collectionName,\n recordId,\n slotName: slot.name,\n blobHash: slot.eTag,\n reason,\n evictedAt: now.toISOString(),\n actor: ctx.actor,\n })\n auditEntries += 1\n }\n evicted += 1\n byCollection[collectionName].evicted += 1\n }\n }\n }\n\n // #808: cache-budget LRU pass — runs AFTER the policy pass so a slot that\n // just evicted no longer counts toward the budget. Reuses this module's own\n // eviction writer (deleteSlot + audit), never a parallel one.\n let budgetEvicted = 0\n let budgetBytesFreed = 0\n if (options.cacheBudget !== undefined) {\n const pass = await runBudgetPass(ctx, options.cacheBudget.maxBytes, {\n now, dryRun, remainingEvictions: maxEvictions - evicted,\n })\n budgetEvicted = pass.evicted\n budgetBytesFreed = pass.bytesFreed\n auditEntries += pass.auditEntries\n }\n\n return {\n evicted,\n records,\n collections: collectionsWithPolicy,\n auditEntries,\n held,\n pinned,\n budgetEvicted,\n budgetBytesFreed,\n byCollection,\n orphanBlobChunks,\n }\n}\n\n// ─── #808: cache-budget LRU pass ───────────────────────────────────────\n\ninterface BudgetCandidate {\n readonly collection: string\n readonly recordId: string\n readonly slotName: string\n readonly eTag: string\n readonly bytes: number\n /** `'slot'` — internal blob, evicted via `deleteSlot`; `'cache'` — external device-local copy, dropped via `dropLocalCache`. */\n readonly kind: 'slot' | 'cache'\n readonly lastAccessMs: number\n}\n\n/**\n * Enforce `cacheBudget.maxBytes` over the locally-cached UNPINNED blob bytes\n * (see {@link CompactRunOptions.cacheBudget} for the full contract). Walks\n * EVERY collection — unlike the policy pass, the budget is not gated on a\n * declared `blobFields` config — but still honors a declared policy's\n * `legalHold`/`retainUntil` floor where one exists.\n */\nasync function runBudgetPass(\n ctx: CompactionContext,\n maxBytes: number,\n opts: { now: Date; dryRun: boolean; remainingEvictions: number },\n): Promise<{ evicted: number; bytesFreed: number; auditEntries: number }> {\n const candidates: BudgetCandidate[] = []\n let total = 0\n\n for (const collectionName of await ctx.listCollections()) {\n if (collectionName.startsWith('_')) continue\n const config = ctx.getBlobFields(collectionName)\n for (const recordId of await ctx.listRecords(collectionName)) {\n const slots = await ctx.listSlots(collectionName, recordId).catch(() => [])\n if (slots.length === 0) continue\n // The record is only needed to evaluate a declared policy's retention\n // floor — skip the decrypt entirely for policy-less collections.\n const record = config\n ? await ctx.getRecord<unknown>(collectionName, recordId).catch(() => null)\n : null\n for (const slot of slots) {\n if (slot.pinned === true) continue // pinned: exempt AND uncounted (#808)\n const policy = config?.[slot.name]\n if (policy && record !== null && isHeld(policy, record, opts.now)) continue // floor blocks budget too\n const lastAccessMs = Date.parse(slot.lastAccessAt ?? slot.uploadedAt) || 0\n const isExternal = slot.external !== undefined\n const bytes = isExternal ? slot.cachedBytes ?? 0 : slot.size\n if (bytes <= 0) continue // external with no local copy: nothing cached to evict\n total += bytes\n candidates.push({\n collection: collectionName, recordId, slotName: slot.name, eTag: slot.eTag,\n bytes, kind: isExternal ? 'cache' : 'slot', lastAccessMs,\n })\n }\n }\n }\n\n // Oldest access first; deterministic tiebreak so equal timestamps (common\n // within one ms) never make the eviction order flap between runs.\n candidates.sort((a, b) =>\n a.lastAccessMs - b.lastAccessMs\n || a.collection.localeCompare(b.collection)\n || a.recordId.localeCompare(b.recordId)\n || a.slotName.localeCompare(b.slotName))\n\n let evicted = 0\n let bytesFreed = 0\n let auditEntries = 0\n for (const c of candidates) {\n if (total <= maxBytes || evicted >= opts.remainingEvictions) break\n if (!opts.dryRun) {\n if (c.kind === 'slot') {\n await ctx.deleteSlot(c.collection, c.recordId, c.slotName)\n await writeAuditEntry(ctx, {\n id: generateEvictionId(c.collection, c.recordId, c.slotName),\n collection: c.collection,\n recordId: c.recordId,\n slotName: c.slotName,\n blobHash: c.eTag,\n reason: 'budget',\n evictedAt: opts.now.toISOString(),\n actor: ctx.actor,\n })\n auditEntries += 1\n } else {\n // Device-local cache drop only — the slot (catalog) and the\n // object-store copy are untouched, so no eviction audit entry.\n await ctx.dropLocalCache?.(c.collection, c.recordId, c.slotName)\n }\n }\n evicted += 1\n bytesFreed += c.bytes\n total -= c.bytes\n }\n\n return { evicted, bytesFreed, auditEntries }\n}\n\n/**\n * Whether a retention floor (legal hold or period-bound `retainUntil`)\n * currently blocks eviction of this record's slots. Fail-closed: a\n * throwing predicate holds the slot.\n */\nfunction isHeld<T>(policy: BlobFieldPolicy<T>, record: T, now: Date): boolean {\n if (policy.legalHold) {\n try {\n if (policy.legalHold(record)) return true\n } catch {\n return true\n }\n }\n if (policy.retainUntil) {\n try {\n const until = policy.retainUntil(record)\n if (until !== null && until !== undefined) {\n const t = until instanceof Date ? until.getTime() : typeof until === 'number' ? until : Date.parse(String(until))\n if (!Number.isFinite(t)) return true // fail-closed: unparseable retainUntil holds the slot\n if (t > now.getTime()) return true\n }\n } catch {\n return true\n }\n }\n return false\n}\n\nfunction evaluatePolicy<T>(\n policy: BlobFieldPolicy<T>,\n record: T,\n slot: SlotInfo,\n now: Date,\n): 'ttl' | 'predicate' | 'both' | null {\n let ttlTriggered = false\n let predicateTriggered = false\n\n if (policy.retainDays !== undefined && policy.retainDays > 0) {\n const uploadedAt = Date.parse(slot.uploadedAt)\n if (Number.isFinite(uploadedAt)) {\n const ageMs = now.getTime() - uploadedAt\n const limitMs = policy.retainDays * 86_400_000\n if (ageMs > limitMs) ttlTriggered = true\n }\n }\n\n if (policy.evictWhen) {\n try {\n if (policy.evictWhen(record)) predicateTriggered = true\n } catch {\n // Predicate error → do NOT evict. Fail closed.\n }\n }\n\n if (ttlTriggered && predicateTriggered) return 'both'\n if (ttlTriggered) return 'ttl'\n if (predicateTriggered) return 'predicate'\n return null\n}\n\nfunction generateEvictionId(collection: string, recordId: string, slotName: string): string {\n const rand = globalThis.crypto.getRandomValues(new Uint8Array(8))\n let suffix = ''\n for (const b of rand) suffix += b.toString(16).padStart(2, '0')\n return `${collection}__${recordId}__${slotName}__${suffix}`\n}\n\nasync function writeAuditEntry(ctx: CompactionContext, entry: BlobEvictionEntry): Promise<void> {\n const json = JSON.stringify(entry)\n const identity = { collection: BLOB_EVICTION_AUDIT_COLLECTION, id: entry.id, by: entry.actor, version: 1 }\n const body = { ts: entry.evictedAt }\n let envelope: EncryptedEnvelope\n if (ctx.encrypted) {\n const dek = await ctx.getDEK(BLOB_EVICTION_AUDIT_COLLECTION)\n const { iv, data } = await encrypt(json, dek, buildRecordAad(identity))\n envelope = buildRecordEnvelope(identity, { ...body, iv, data })\n } else {\n envelope = buildRecordEnvelope(identity, { ...body, iv: '', data: json })\n }\n await ctx.adapter.put(ctx.vault, BLOB_EVICTION_AUDIT_COLLECTION, entry.id, envelope)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAuEA,eAAe,WAAW,KAAkB,KAA6C;AACvF,MAAI,IAAI,oBAAqB,QAAO,IAAI,oBAAoB,IAAI,EAAE;AAClE,SAAO,EAAE,UAAU,GAAG,SAAS,CAAC,EAAE;AACpC;AAgCA,SAAS,0BAA0B,QAAmD;AACpF,SAAO;AAAA,IACL,YAAY,OAAO;AAAA,MACjB,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO;AAAA,QACjD,GAAI,EAAE,eAAe,SAAY,EAAE,YAAY,EAAE,WAAW,IAAI,CAAC;AAAA,QACjE,GAAI,EAAE,cAAc,SAAY,EAAE,WAAW,KAAK,IAAI,CAAC;AAAA,QACvD,GAAI,EAAE,cAAc,SAAY,EAAE,WAAW,KAAK,IAAI,CAAC;AAAA,QACvD,GAAI,EAAE,gBAAgB,SAAY,EAAE,aAAa,KAAK,IAAI,CAAC;AAAA,QAC3D,GAAI,EAAE,aAAa,OAAO,EAAE,UAAU,KAAK,IAAI,CAAC;AAAA,QAChD,GAAI,EAAE,WAAW,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;AAAA,QAC5C,GAAI,EAAE,aAAa,SAAY,EAAE,UAAU,EAAE,SAAS,IAAI,CAAC;AAAA,MAC7D,CAAC,CAAC;AAAA,IACJ;AAAA,EACF;AACF;AAEO,SAAS,QAAQ,KAA8B;AACpD,SAAO;AAAA,IACL,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMP,SAAS,EAAE,iBAAiB,YAAY,WAAW,QAAQ,YAAY,MAAM,aAAa,KAAK;AAAA,IAC/F,QAAQ,CAAC,UAAU,SAAS,IAAI;AAAA,IAChC,OAAO,CAAC,QAAQ,WAAW,KAAK,GAAG;AAAA,IACnC,kBAAkB,MAAM,0BAA0B,IAAI,MAAM;AAAA,EAC9D;AACF;AAEO,SAAS,cAAoB;AAClC,mBAAiB,QAAQ,CAAC,MAAM,QAAQ,CAAkB,CAAC;AAC7D;;;ACrGA,YAAY;AAqFL,IAAM,WAA0B;AAAA,EACrC,WAAW;AACT,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAAA,EACA,aAAa;AACX,WAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,iBAAiB,EAAE;AAAA,EAClD;AACF;;;ACrCO,IAAM,0BAAN,cAAsC,MAAM;AAAA,EACjD,YAAY,QAAgB;AAC1B,UAAM,wBAAwB,MAAM,EAAE;AACtC,SAAK,OAAO;AAAA,EACd;AACF;AAIO,IAAM,0BAA0B;AAmBhC,SAAS,wBACd,OACA,2BACA,eACA,YACA,SACmB;AACnB,MAAI,UAAU;AAEd,QAAM,QAAQ,MAAY;AACxB,cAAU;AAAA,EACZ;AAEA,MAAI,QAAQ,QAAQ;AAClB,QAAI,QAAQ,OAAO,QAAS,WAAU;AACtC,YAAQ,OAAO,iBAAiB,SAAS,MAAM;AAAE,gBAAU;AAAA,IAAK,CAAC;AAAA,EACnE;AAEA,WAAS,aAAmB;AAC1B,QAAI,QAAS,OAAM,IAAI,wBAAwB,mBAAmB;AAAA,EACpE;AAEA,QAAM,YAAY,QAAQ,cAAc,IAAI,IAAI,QAAQ,WAAW,IAAI;AAIvE,MAAI,eAAqC;AACzC,WAAS,iBAAgC;AACvC,QAAI,CAAC,cAAc;AACjB,qBAAe,WAAW;AAAA,QACxB,IAAI,gBAAgB;AAAA,QACpB,WAAW;AAAA,QACX;AAAA,QACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC,aAAa,QAAQ,eAAe;AAAA,QACpC,WAAW,QAAQ,QAAQ,KAAK;AAAA,QAChC,aAAa,QAAQ,eAAe;AAAA,MACtC,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAEA,kBAAgB,WAAyC;AACvD,UAAM,eAAe;AACrB,eAAW;AAGX,UAAM,iBAAiB,MAAM,0BAA0B;AACvD,UAAM,UAAU,eAAe,OAAO,UAAQ;AAC5C,UAAI,KAAK,WAAW,GAAG,EAAG,QAAO;AACjC,UAAI,aAAa,CAAC,UAAU,IAAI,IAAI,EAAG,QAAO;AAC9C,aAAO;AAAA,IACT,CAAC;AAED,QAAI,kBAAkB,QAAQ,gBAAgB;AAE9C,eAAW,kBAAkB,SAAS;AACpC,UAAI,QAAS;AAEb,YAAM,OAAO,cAAuC,cAAc;AAClE,YAAM,UAAU,MAAM,KAAK,KAAK,EAAE,MAAM,MAAM,CAAC,CAAC;AAChD,iBAAW,UAAU,SAAS;AAC5B,YAAI,QAAS;AACb,mBAAW;AAEX,cAAM,UAAW,OAA4B;AAC7C,YAAI,OAAO,YAAY,SAAU;AAEjC,YAAI,QAAQ,SAAS,CAAC,QAAQ,MAAM,QAAQ,EAAE,YAAY,gBAAgB,IAAI,QAAQ,CAAC,EAAG;AAE1F,cAAM,UAAU,KAAK,KAAK,OAAO;AACjC,cAAM,QAAQ,MAAM,QAAQ,KAAK,EAAE,MAAM,MAAM,CAAC,CAAe;AAC/D,mBAAW,QAAQ,OAAO;AACxB,cAAI,QAAS;AAEb,cAAI,CAAC,iBAAiB;AACpB,gBAAI,KAAK,SAAS,QAAQ,aAAa;AACrC,gCAAkB;AAAA,YACpB;AACA;AAAA,UACF;AAEA,gBAAM,QAAQ,MAAM,QAAQ,IAAI,KAAK,IAAI;AACzC,cAAI,CAAC,MAAO;AAEZ,gBAAM,OAAqB;AAAA,YACzB,QAAQ,KAAK;AAAA,YACb,WAAW,EAAE,YAAY,gBAAgB,IAAI,SAAS,MAAM,KAAK,KAAK;AAAA,YACtE;AAAA,YACA,MAAM;AAAA,cACJ,MAAM,KAAK;AAAA,cACX,UAAU,KAAK;AAAA,cACf,GAAI,KAAK,aAAa,UAAa,EAAE,UAAU,KAAK,SAAS;AAAA,cAC7D,GAAI,KAAK,eAAe,UAAa,EAAE,WAAW,KAAK,WAAW;AAAA,YACpE;AAAA,UACF;AACA,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAA4B;AAAA,IAChC;AAAA,IACA,IAAI,UAAU;AAAE,aAAO;AAAA,IAAQ;AAAA,IAC/B,CAAC,OAAO,aAAa,GAAG,MAAM,SAAS;AAAA,EACzC;AACA,SAAO;AACT;AAIA,SAAS,kBAA0B;AAEjC,QAAM,MAAM,WAAW,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC;AAChE,MAAI,IAAI;AACR,aAAW,KAAK,IAAK,MAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AACxD,SAAO,SAAS,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC;AAC3D;;;AC3MA,IAAM,eAAe;AA8BrB,SAAS,aAAa,IAAoD;AACxE,QAAM,MAAM,GAAG,YAAY,GAAG;AAC9B,MAAI,OAAO,KAAK,QAAQ,GAAG,SAAS,EAAG,QAAO;AAC9C,QAAM,SAAS,GAAG,MAAM,MAAM,CAAC;AAC/B,MAAI,CAAC,QAAQ,KAAK,MAAM,EAAG,QAAO;AAClC,SAAO,EAAE,MAAM,GAAG,MAAM,GAAG,GAAG,GAAG,OAAO,OAAO,MAAM,EAAE;AACzD;AAUA,eAAsB,uBACpB,SACA,OAC4B;AAC5B,QAAM,QAA2B,EAAE,QAAQ,GAAG,OAAO,GAAG,aAAa,CAAC,GAAG,aAAa,EAAE;AACxF,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,gBAAY,IAAI,IAAI,MAAM,QAAQ,KAAK,OAAO,qBAAqB,CAAC;AACpE,eAAW,MAAM,QAAQ,KAAK,OAAO,sBAAsB;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,MAAI,SAAS;AACb,MAAI,cAAc;AAClB,QAAM,cAAc,oBAAI,IAAY;AACpC,aAAW,MAAM,UAAU;AACzB,UAAM,SAAS,aAAa,EAAE;AAC9B,QAAI,WAAW,MAAM;AAAE,qBAAe;AAAG;AAAA,IAAS;AAClD,QAAI,UAAU,IAAI,OAAO,IAAI,EAAG;AAChC,cAAU;AACV,gBAAY,IAAI,OAAO,IAAI;AAAA,EAC7B;AAEA,SAAO;AAAA,IACL;AAAA,IACA,OAAO,YAAY;AAAA,IACnB,aAAa,CAAC,GAAG,WAAW,EAAE,MAAM,GAAG,YAAY;AAAA,IACnD;AAAA,EACF;AACF;;;ACNO,IAAM,iCAAiC;AA6H9C,eAAsB,cACpB,KACA,UAA6B,CAAC,GACH;AAI3B,QAAM,mBAAmB,MAAM,uBAAuB,IAAI,SAAS,IAAI,KAAK;AAC5E,QAAM,MAAM,QAAQ,OAAO,oBAAI,KAAK;AACpC,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,SAAS,QAAQ,WAAW;AAClC,MAAI,QAAQ,gBAAgB,WACtB,CAAC,OAAO,SAAS,QAAQ,YAAY,QAAQ,KAAK,QAAQ,YAAY,WAAW,IAAI;AACzF,UAAM,IAAI,gBAAgB,6EAA6E;AAAA,EACzG;AAEA,QAAM,iBAAiB,MAAM,IAAI,gBAAgB;AACjD,QAAM,eAAqE,CAAC;AAC5E,MAAI,UAAU;AACd,MAAI,UAAU;AACd,MAAI,eAAe;AACnB,MAAI,OAAO;AACX,MAAI,SAAS;AACb,MAAI,wBAAwB;AAE5B,QAAO,YAAW,kBAAkB,gBAAgB;AAClD,QAAI,eAAe,WAAW,GAAG,EAAG;AACpC,UAAM,SAAS,IAAI,cAAc,cAAc;AAC/C,QAAI,CAAC,OAAQ;AACb,UAAM,kBAAkB,OAAO,KAAK,MAAM;AAC1C,QAAI,gBAAgB,WAAW,EAAG;AAClC,6BAAyB;AACzB,iBAAa,cAAc,IAAI,EAAE,SAAS,GAAG,SAAS,EAAE;AAExD,UAAM,MAAM,MAAM,IAAI,YAAY,cAAc;AAChD,eAAW,YAAY,KAAK;AAC1B,UAAI,WAAW,aAAc,OAAM;AAEnC,YAAM,SAAS,MAAM,IAAI,UAAU,gBAAgB,QAAQ,EAAE,MAAM,MAAM,IAAI;AAC7E,UAAI,WAAW,KAAM;AACrB,iBAAW;AACX,mBAAa,cAAc,EAAE,WAAW;AAExC,YAAM,QAAQ,MAAM,IAAI,UAAU,gBAAgB,QAAQ,EAAE,MAAM,MAAM,CAAC,CAAC;AAC1E,iBAAW,QAAQ,OAAO;AACxB,YAAI,WAAW,aAAc,OAAM;AACnC,cAAM,SAAS,OAAO,KAAK,IAAI;AAC/B,YAAI,CAAC,OAAQ;AAEb,cAAM,SAAS,eAAe,QAAQ,QAAQ,MAAM,GAAG;AACvD,YAAI,CAAC,OAAQ;AAMb,YAAI,KAAK,WAAW,MAAM;AACxB,oBAAU;AACV;AAAA,QACF;AAIA,YAAI,OAAO,QAAQ,QAAQ,GAAG,GAAG;AAC/B,kBAAQ;AACR;AAAA,QACF;AAEA,YAAI,CAAC,QAAQ;AACX,gBAAM,IAAI,WAAW,gBAAgB,UAAU,KAAK,IAAI;AACxD,gBAAM,gBAAgB,KAAK;AAAA,YACzB,IAAI,mBAAmB,gBAAgB,UAAU,KAAK,IAAI;AAAA,YAC1D,YAAY;AAAA,YACZ;AAAA,YACA,UAAU,KAAK;AAAA,YACf,UAAU,KAAK;AAAA,YACf;AAAA,YACA,WAAW,IAAI,YAAY;AAAA,YAC3B,OAAO,IAAI;AAAA,UACb,CAAC;AACD,0BAAgB;AAAA,QAClB;AACA,mBAAW;AACX,qBAAa,cAAc,EAAE,WAAW;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AAKA,MAAI,gBAAgB;AACpB,MAAI,mBAAmB;AACvB,MAAI,QAAQ,gBAAgB,QAAW;AACrC,UAAM,OAAO,MAAM,cAAc,KAAK,QAAQ,YAAY,UAAU;AAAA,MAClE;AAAA,MAAK;AAAA,MAAQ,oBAAoB,eAAe;AAAA,IAClD,CAAC;AACD,oBAAgB,KAAK;AACrB,uBAAmB,KAAK;AACxB,oBAAgB,KAAK;AAAA,EACvB;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAsBA,eAAe,cACb,KACA,UACA,MACwE;AACxE,QAAM,aAAgC,CAAC;AACvC,MAAI,QAAQ;AAEZ,aAAW,kBAAkB,MAAM,IAAI,gBAAgB,GAAG;AACxD,QAAI,eAAe,WAAW,GAAG,EAAG;AACpC,UAAM,SAAS,IAAI,cAAc,cAAc;AAC/C,eAAW,YAAY,MAAM,IAAI,YAAY,cAAc,GAAG;AAC5D,YAAM,QAAQ,MAAM,IAAI,UAAU,gBAAgB,QAAQ,EAAE,MAAM,MAAM,CAAC,CAAC;AAC1E,UAAI,MAAM,WAAW,EAAG;AAGxB,YAAM,SAAS,SACX,MAAM,IAAI,UAAmB,gBAAgB,QAAQ,EAAE,MAAM,MAAM,IAAI,IACvE;AACJ,iBAAW,QAAQ,OAAO;AACxB,YAAI,KAAK,WAAW,KAAM;AAC1B,cAAM,SAAS,SAAS,KAAK,IAAI;AACjC,YAAI,UAAU,WAAW,QAAQ,OAAO,QAAQ,QAAQ,KAAK,GAAG,EAAG;AACnE,cAAM,eAAe,KAAK,MAAM,KAAK,gBAAgB,KAAK,UAAU,KAAK;AACzE,cAAM,aAAa,KAAK,aAAa;AACrC,cAAM,QAAQ,aAAa,KAAK,eAAe,IAAI,KAAK;AACxD,YAAI,SAAS,EAAG;AAChB,iBAAS;AACT,mBAAW,KAAK;AAAA,UACd,YAAY;AAAA,UAAgB;AAAA,UAAU,UAAU,KAAK;AAAA,UAAM,MAAM,KAAK;AAAA,UACtE;AAAA,UAAO,MAAM,aAAa,UAAU;AAAA,UAAQ;AAAA,QAC9C,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAIA,aAAW,KAAK,CAAC,GAAG,MAClB,EAAE,eAAe,EAAE,gBAChB,EAAE,WAAW,cAAc,EAAE,UAAU,KACvC,EAAE,SAAS,cAAc,EAAE,QAAQ,KACnC,EAAE,SAAS,cAAc,EAAE,QAAQ,CAAC;AAEzC,MAAI,UAAU;AACd,MAAI,aAAa;AACjB,MAAI,eAAe;AACnB,aAAW,KAAK,YAAY;AAC1B,QAAI,SAAS,YAAY,WAAW,KAAK,mBAAoB;AAC7D,QAAI,CAAC,KAAK,QAAQ;AAChB,UAAI,EAAE,SAAS,QAAQ;AACrB,cAAM,IAAI,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ;AACzD,cAAM,gBAAgB,KAAK;AAAA,UACzB,IAAI,mBAAmB,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ;AAAA,UAC3D,YAAY,EAAE;AAAA,UACd,UAAU,EAAE;AAAA,UACZ,UAAU,EAAE;AAAA,UACZ,UAAU,EAAE;AAAA,UACZ,QAAQ;AAAA,UACR,WAAW,KAAK,IAAI,YAAY;AAAA,UAChC,OAAO,IAAI;AAAA,QACb,CAAC;AACD,wBAAgB;AAAA,MAClB,OAAO;AAGL,cAAM,IAAI,iBAAiB,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ;AAAA,MACjE;AAAA,IACF;AACA,eAAW;AACX,kBAAc,EAAE;AAChB,aAAS,EAAE;AAAA,EACb;AAEA,SAAO,EAAE,SAAS,YAAY,aAAa;AAC7C;AAOA,SAAS,OAAU,QAA4B,QAAW,KAAoB;AAC5E,MAAI,OAAO,WAAW;AACpB,QAAI;AACF,UAAI,OAAO,UAAU,MAAM,EAAG,QAAO;AAAA,IACvC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,OAAO,aAAa;AACtB,QAAI;AACF,YAAM,QAAQ,OAAO,YAAY,MAAM;AACvC,UAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,cAAM,IAAI,iBAAiB,OAAO,MAAM,QAAQ,IAAI,OAAO,UAAU,WAAW,QAAQ,KAAK,MAAM,OAAO,KAAK,CAAC;AAChH,YAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,YAAI,IAAI,IAAI,QAAQ,EAAG,QAAO;AAAA,MAChC;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,eACP,QACA,QACA,MACA,KACqC;AACrC,MAAI,eAAe;AACnB,MAAI,qBAAqB;AAEzB,MAAI,OAAO,eAAe,UAAa,OAAO,aAAa,GAAG;AAC5D,UAAM,aAAa,KAAK,MAAM,KAAK,UAAU;AAC7C,QAAI,OAAO,SAAS,UAAU,GAAG;AAC/B,YAAM,QAAQ,IAAI,QAAQ,IAAI;AAC9B,YAAM,UAAU,OAAO,aAAa;AACpC,UAAI,QAAQ,QAAS,gBAAe;AAAA,IACtC;AAAA,EACF;AAEA,MAAI,OAAO,WAAW;AACpB,QAAI;AACF,UAAI,OAAO,UAAU,MAAM,EAAG,sBAAqB;AAAA,IACrD,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,MAAI,gBAAgB,mBAAoB,QAAO;AAC/C,MAAI,aAAc,QAAO;AACzB,MAAI,mBAAoB,QAAO;AAC/B,SAAO;AACT;AAEA,SAAS,mBAAmB,YAAoB,UAAkB,UAA0B;AAC1F,QAAM,OAAO,WAAW,OAAO,gBAAgB,IAAI,WAAW,CAAC,CAAC;AAChE,MAAI,SAAS;AACb,aAAW,KAAK,KAAM,WAAU,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC9D,SAAO,GAAG,UAAU,KAAK,QAAQ,KAAK,QAAQ,KAAK,MAAM;AAC3D;AAEA,eAAe,gBAAgB,KAAwB,OAAyC;AAC9F,QAAM,OAAO,KAAK,UAAU,KAAK;AACjC,QAAM,WAAW,EAAE,YAAY,gCAAgC,IAAI,MAAM,IAAI,IAAI,MAAM,OAAO,SAAS,EAAE;AACzG,QAAM,OAAO,EAAE,IAAI,MAAM,UAAU;AACnC,MAAI;AACJ,MAAI,IAAI,WAAW;AACjB,UAAM,MAAM,MAAM,IAAI,OAAO,8BAA8B;AAC3D,UAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,MAAM,KAAK,eAAe,QAAQ,CAAC;AACtE,eAAW,oBAAoB,UAAU,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC;AAAA,EAChE,OAAO;AACL,eAAW,oBAAoB,UAAU,EAAE,GAAG,MAAM,IAAI,IAAI,MAAM,KAAK,CAAC;AAAA,EAC1E;AACA,QAAM,IAAI,QAAQ,IAAI,IAAI,OAAO,gCAAgC,MAAM,IAAI,QAAQ;AACrF;","names":[]}
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  FieldNotQueryableError,
7
7
  IndexRequiredError
8
- } from "./chunk-Z3BZXPRO.js";
8
+ } from "./chunk-JBIV4FLR.js";
9
9
 
10
10
  // src/with-lookup/indexing/strategy.ts
11
11
  var NO_INDEXING = {
@@ -534,4 +534,4 @@ export {
534
534
  PersistedCollectionIndex,
535
535
  LazyQuery
536
536
  };
537
- //# sourceMappingURL=chunk-S7PM7Z4K.js.map
537
+ //# sourceMappingURL=chunk-F7ZFRPGQ.js.map
@@ -2,10 +2,10 @@ import {
2
2
  base64ToBuffer,
3
3
  decryptBytesWithAAD,
4
4
  encryptBytesWithAAD
5
- } from "./chunk-ONPVHX2Y.js";
5
+ } from "./chunk-W5LHQXZU.js";
6
6
  import {
7
7
  TamperedError
8
- } from "./chunk-Z3BZXPRO.js";
8
+ } from "./chunk-JBIV4FLR.js";
9
9
 
10
10
  // src/kernel/enclave/classify/compare.ts
11
11
  var subtle = globalThis.crypto.subtle;
@@ -92,4 +92,4 @@ export {
92
92
  openVdigPayload,
93
93
  evaluateKofN
94
94
  };
95
- //# sourceMappingURL=chunk-CKKN25P2.js.map
95
+ //# sourceMappingURL=chunk-FOG7YEJP.js.map
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  installViaBinder
3
- } from "./chunk-C3XWJCG7.js";
3
+ } from "./chunk-XDVVBETV.js";
4
4
  import {
5
5
  SealedHandle
6
- } from "./chunk-UJ4Z67UQ.js";
6
+ } from "./chunk-RK7N4OQ5.js";
7
7
  import {
8
8
  ClassifiedConfigError,
9
9
  ClassifiedNotEnabledError
10
- } from "./chunk-Z3BZXPRO.js";
10
+ } from "./chunk-JBIV4FLR.js";
11
11
 
12
12
  // src/via/classified/descriptor.ts
13
13
  function isClassifiedFieldSpec(x) {
@@ -209,7 +209,7 @@ function buildClassifiedDescribeFragment(byField) {
209
209
  )
210
210
  };
211
211
  }
212
- function classifiedBinding(cfg) {
212
+ function classifiedVia(cfg) {
213
213
  const resolved = resolveClassifiedFields(cfg.collectionName, cfg.entries);
214
214
  guardClassifiedCompat(cfg.collectionName, resolved.byField, cfg.guardCtx);
215
215
  const byField = resolved.byField;
@@ -229,7 +229,7 @@ function classifiedBinding(cfg) {
229
229
  };
230
230
  }
231
231
  function linkClassifiedVia() {
232
- installViaBinder("classified", (c) => classifiedBinding(c));
232
+ installViaBinder("classified", (c) => classifiedVia(c));
233
233
  }
234
234
 
235
235
  // src/port/with/classified-strategy.ts
@@ -262,4 +262,4 @@ export {
262
262
  enforceClassifiedWrite,
263
263
  NO_CLASSIFIED
264
264
  };
265
- //# sourceMappingURL=chunk-AJGGBXWA.js.map
265
+ //# sourceMappingURL=chunk-GK4DRVUD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/via/classified/descriptor.ts","../src/via/classified/errors.ts","../src/via/classified/resolve.ts","../src/via/classified/write.ts","../src/via/classified/guards.ts","../src/via/classified/binding.ts","../src/port/with/classified-strategy.ts"],"sourcesContent":["/**\n * Classified fields — behavioral sensitive-field descriptors (stage 1).\n * Design: design-history/2026-07-04-classified-fields-design.md\n * Law: open on write, declarative on read — read-side behavior is this\n * closed vocabulary; there are no read-side callbacks.\n * @module\n */\n\nexport type ClassifiedStorage = 'recoverable' | 'never' | 'digest-only'\n\nexport type ClassifiedList =\n | { readonly kind: 'omit' }\n | { readonly kind: 'mask'; readonly pattern: string }\n | { readonly kind: 'rider'; readonly rider: string }\n\nexport type ClassifiedRider = (value: unknown) => unknown\n\nexport interface ClassifiedFieldSpec {\n readonly _noydbClassified: true\n /**\n * Via port brand marker — lets a `ClassifiedFieldSpec` satisfy the\n * kernel's opaque `ViaDescriptor` (#629 Task 5). Optional (unlike\n * `MoneyDescriptor._viaBrand`/`I18nTextDescriptor._viaBrand`, which are\n * required): every `classified.*()` preset factory stamps it, but the\n * pre-existing test suite's hand-rolled `ClassifiedFieldSpec` fixtures\n * (built without going through a preset) predate this field and would\n * fail to compile if it were mandatory.\n */\n readonly _viaBrand?: 'classified'\n readonly preset: string\n readonly storage: ClassifiedStorage\n readonly list: ClassifiedList\n readonly sensitivity: 'pii' | 'secret'\n /** Write-time safe projections; companion field name is `<field>_<rider>`. */\n readonly riders?: Record<string, ClassifiedRider>\n /** Write-time validator: error message, or null when valid. */\n readonly validate?: (value: unknown) => string | null\n /** Digest-only verify policy (stage 2). Mode both sides normalize under. */\n readonly verifyNormalize?: 'password' | 'secret-answer'\n /** Decorate ok:true verdicts with mustRotate after this many days (I1). */\n readonly rotateDays?: number\n /** Refuse reuse of the last N values on rotate (cap 8, spec Q4). */\n readonly notLastN?: number\n /** Member of the collection's matchGroup (secretAnswer preset). */\n readonly verifyGroupMember?: true\n /**\n * Opt into a store-visible blind-index (`_bidx`) tag so equal values become\n * queryable/joinable while staying digest-only. Digest-only knob (R7); gated\n * behind the collection's `acknowledgeEquatableRisk` door (R8). Equal values\n * produce equal tags — a partition leak a DEK holder can offline-dictionary\n * at the PBKDF2 floor. Absent/false ⇒ refused-by-default.\n */\n readonly equatable?: true\n}\n\nexport interface ClassifiedGroup {\n readonly _noydbClassifiedGroup: true\n /** Via port brand marker — see {@link ClassifiedFieldSpec._viaBrand}. */\n readonly _viaBrand?: 'classified'\n readonly preset: string\n /** member record-field name -> spec (differential per-member policy). */\n readonly members: Record<string, ClassifiedFieldSpec>\n}\n\nexport type ClassifiedEntry = ClassifiedFieldSpec | ClassifiedGroup\n\nexport function isClassifiedFieldSpec(x: unknown): x is ClassifiedFieldSpec {\n return typeof x === 'object' && x !== null\n && (x as { _noydbClassified?: unknown })._noydbClassified === true\n}\n\nexport function isClassifiedGroup(x: unknown): x is ClassifiedGroup {\n return typeof x === 'object' && x !== null\n && (x as { _noydbClassifiedGroup?: unknown })._noydbClassifiedGroup === true\n}\n","/**\n * Configuration/write errors for classified fields. @module\n *\n * `ClassifiedConfigError` / `ClassifiedRevealError` moved to `kernel/errors.ts`\n * (stage 2) so `kernel/enclave/classify/*` can throw them without importing\n * with-*. Re-exported here under the same names so existing import paths\n * keep working.\n */\nexport { ClassifiedConfigError, ClassifiedRevealError, ClassifiedVerifyError, ClassifiedRotationError } from '../../kernel/errors.js'\n\nexport class ClassifiedNeverStoredError extends Error {\n constructor(public readonly collection: string, public readonly field: string) {\n super(`Field \"${field}\" in collection \"${collection}\" is classified storage:'never' `\n + `(e.g. a CVC) and must not be persisted. Validate it at capture and drop it before put().`)\n this.name = 'ClassifiedNeverStoredError'\n }\n}\n\nexport class ClassifiedValidationError extends Error {\n constructor(public readonly collection: string, public readonly field: string, detail: string) {\n super(`Classified field \"${field}\" in collection \"${collection}\" failed validation: ${detail}`)\n this.name = 'ClassifiedValidationError'\n }\n}\n","/** Flatten a classifiedFields config into a per-field map + rider computed entries. @module */\n\nimport { isClassifiedGroup, type ClassifiedEntry, type ClassifiedFieldSpec } from './descriptor.js'\nimport { ClassifiedConfigError } from './errors.js'\n\nexport type { ClassifiedEntry, ClassifiedFieldSpec, ClassifiedGroup, ClassifiedList } from './descriptor.js'\nexport { ClassifiedConfigError } from './errors.js'\n\nexport interface ResolvedClassified {\n readonly byField: Record<string, ClassifiedFieldSpec>\n readonly riderComputed: Record<string, (record: Record<string, unknown>) => unknown>\n}\n\nexport function resolveClassifiedFields(\n collection: string,\n config: Record<string, ClassifiedEntry>,\n): ResolvedClassified {\n const byField: Record<string, ClassifiedFieldSpec> = {}\n const claim = (field: string, spec: ClassifiedFieldSpec): void => {\n if (byField[field] !== undefined) {\n throw new ClassifiedConfigError(collection,\n `field \"${field}\" is claimed twice — storage forms are mutually exclusive per field (R5): ` +\n `a field is digest-only OR recoverable OR never, exactly one`)\n }\n byField[field] = spec\n }\n for (const [key, entry] of Object.entries(config)) {\n if (isClassifiedGroup(entry)) {\n for (const [field, spec] of Object.entries(entry.members)) claim(field, spec)\n } else {\n claim(key, entry)\n }\n }\n const riderComputed: Record<string, (record: Record<string, unknown>) => unknown> = {}\n for (const [field, spec] of Object.entries(byField)) {\n for (const [name, rider] of Object.entries(spec.riders ?? {})) {\n const companion = `${field}_${name}`\n if (byField[companion] !== undefined || riderComputed[companion] !== undefined) {\n throw new ClassifiedConfigError(collection, `rider companion \"${companion}\" collides with a declared field`)\n }\n riderComputed[companion] = (record) =>\n record[field] === undefined ? undefined : rider(record[field])\n }\n }\n return { byField, riderComputed }\n}\n","/** Write-path enforcement: storage:'never' rejection + preset validators.\n * Runs BEFORE riders/computed and BEFORE schema validation. Pure. @module */\n\nimport type { ClassifiedFieldSpec } from './descriptor.js'\nimport { ClassifiedNeverStoredError, ClassifiedValidationError } from './errors.js'\n\nexport function enforceClassifiedWrite(\n record: Record<string, unknown>,\n byField: Record<string, ClassifiedFieldSpec>,\n collection: string,\n): void {\n for (const [field, spec] of Object.entries(byField)) {\n const value = record[field]\n if (value === undefined) continue\n if (value === null && spec.storage === 'digest-only') continue // explicit clear (C6 branch 3)\n if (spec.storage === 'never') throw new ClassifiedNeverStoredError(collection, field)\n const problem = spec.validate?.(value) ?? null\n if (problem !== null) throw new ClassifiedValidationError(collection, field, problem)\n }\n}\n","/**\n * Refusal matrix R1-R5 (spec) — ONE guard, run at BOTH doors:\n * `collection()` config resolution AND `_applyClassifiedFields` (the reconcile\n * seam), because crdt/conflictPolicy/perRecordKeys are construction-only\n * while classifiedFields can attach later (C5's lesson). @module\n */\nimport type { ClassifiedFieldSpec } from './descriptor.js'\nimport { ClassifiedConfigError } from '../../kernel/errors.js'\n\nexport interface ClassifiedGuardCtx {\n readonly perRecordKeys: boolean\n readonly crdt: boolean\n readonly hasConflictPolicy: boolean\n /** False on plaintext (`encrypt: false`) collections — no vdig/seal path exists. */\n readonly storeCiphertext: boolean\n readonly deterministicFields: ReadonlySet<string> | null\n readonly indexedFields: ReadonlySet<string>\n readonly textIndexFields: ReadonlySet<string>\n readonly vectorSourceFields: ReadonlySet<string>\n readonly subjectKeyField: string | undefined\n readonly bareSensitiveFields: ReadonlySet<string>\n /** Collection-level gate for the `equatable` knob (R8 double door). */\n readonly acknowledgeEquatableRisk: boolean\n}\n\nexport function guardClassifiedCompat(\n collection: string,\n byField: Record<string, ClassifiedFieldSpec>,\n ctx: ClassifiedGuardCtx,\n): void {\n const digestOnly = Object.entries(byField)\n .filter(([, s]) => s.storage === 'digest-only')\n const protectedForms = Object.values(byField)\n .filter((s) => s.storage === 'digest-only' || s.storage === 'recoverable')\n\n // R2 — merge resolvers bypass the write pipeline entirely (C5): a merge\n // could carry stale/foreign _vdig or resurrect plaintext (the CRDT put\n // branch persists via encryptJsonString with no vdig backstop). Fail-loud.\n if (protectedForms.length > 0 && (ctx.crdt || ctx.hasConflictPolicy)) {\n throw new ClassifiedConfigError(collection,\n 'digest-only/recoverable classified fields cannot combine with a crdt mode or a ' +\n 'conflictPolicy resolver — merge paths bypass write enforcement (R2)')\n }\n // R7 — `equatable` is a digest-only knob: it drives the `_bidx` blind-index\n // slot, which only exists on the digest-only write path. A recoverable/never\n // field carrying it would silently no-op (recoverable equality is `_det`'s\n // job). Refuse rather than accept-and-ignore. Checked over ALL specs, before\n // the digest-only early-return below, precisely to catch the non-digest case.\n for (const [f, spec] of Object.entries(byField)) {\n if (spec.equatable === true && spec.storage !== 'digest-only') {\n throw new ClassifiedConfigError(collection,\n `field \"${f}\" declares equatable but storage is '${spec.storage}' — equatable is a ` +\n `digest-only knob; recoverable equality is _det's job (R7)`)\n }\n }\n\n // R8 — the equatable double door (mirrors deterministicFields ×\n // acknowledgeDeterministicRisk): any field opting into `equatable` requires\n // the collection to acknowledge the partition-leak risk. One-directional —\n // `acknowledgeEquatableRisk: true` with ZERO equatable members is a silent\n // no-op (never throws here), so the ack can sit on a collection config\n // ahead of the field landing.\n const hasEquatable = Object.values(byField).some((s) => s.equatable === true)\n if (hasEquatable && ctx.acknowledgeEquatableRisk !== true) {\n throw new ClassifiedConfigError(collection,\n `classified equatable fields require \\`acknowledgeEquatableRisk: true\\` — equal values ` +\n `produce equal store-visible index tags (a partition leak a DEK holder can offline-` +\n `dictionary at the PBKDF2 floor); the door is the real control for low-entropy fields (R8)`)\n }\n\n if (digestOnly.length === 0) return\n\n // Digest-only on a plaintext collection: the codec's vdig block is gated on\n // storeCiphertext, so the secret would persist as raw plaintext. Refuse.\n if (!ctx.storeCiphertext) {\n throw new ClassifiedConfigError(collection,\n `storage:'digest-only' requires an encrypted collection — with encrypt: false the ` +\n `write path skips the digest slot entirely and the secret would persist as raw plaintext`)\n }\n // R1 — vdig slot keys are CEK-only (I3); without perRecordKeys a digest\n // would survive forget() in backups as offline-crackable material.\n if (!ctx.perRecordKeys) {\n throw new ClassifiedConfigError(collection,\n `storage:'digest-only' requires perRecordKeys: true — vdig keys derive from the ` +\n `per-record CEK so forget() shreds them (R1)`)\n }\n for (const [f, spec] of digestOnly) {\n // notLastN cap: presets clamp this at construction, but a raw spec object\n // bypasses the preset — enforce the same 0..8 cap in the policy projection.\n const n = spec.notLastN ?? 0\n if (!Number.isInteger(n) || n < 0 || n > 8) {\n throw new ClassifiedConfigError(collection,\n `digest-only field \"${f}\" declares notLastN ${n} — must be an integer 0..8 ` +\n `(write cost is n × 600K PBKDF2; ring blast radius is documented)`)\n }\n if (ctx.bareSensitiveFields.has(f)) {\n // R5 — the sealed block runs before the vdig block in the codec, so the\n // overlap would seal the value recoverably (silent downgrade + I4\n // double-slot). Storage forms are mutually exclusive per field.\n throw new ClassifiedConfigError(collection,\n `field \"${f}\" is digest-only AND listed in sensitive[] — storage forms are mutually exclusive per field (R5)`)\n }\n if (ctx.deterministicFields?.has(f)) {\n throw new ClassifiedConfigError(collection,\n `digest-only field \"${f}\" cannot be deterministic — equality-correlatable ciphertext defeats per-write salts (R3)`)\n }\n if (ctx.indexedFields.has(f) || ctx.textIndexFields.has(f)\n || ctx.vectorSourceFields.has(f) || ctx.subjectKeyField === f) {\n throw new ClassifiedConfigError(collection,\n `digest-only field \"${f}\" cannot be indexed, text-indexed, embedded, or a forget-subject key (R4)`)\n }\n }\n}\n","/**\n * The classified `NoydbVia` (#629 Task 5, LIVE since Task 6) — wires\n * classified-fields declaration/write-enforcement/at-rest sealing/erasure\n * into the kernel's generic Via port. Mirrors `via/money/binding.ts`'s\n * inline declaration-time validation and `via/i18n/binding.ts`'s\n * `linkXVia` static-link pattern — except the link itself is EAGER\n * (`port/with/classified-strategy.ts` calls `linkClassifiedVia()` at module\n * load, not lazily from a `classified.*()` preset call): several fixtures\n * build a raw `ClassifiedFieldSpec` literal without ever calling a preset,\n * and the binder must be installed before `compileVias` needs it\n * regardless.\n *\n * `compileVias` (`kernel/collection-config.ts`) compiles this binding\n * in whenever a collection declares `classifiedFields` — money then i18n\n * then classified, order pinned. `kernel/collection.ts`'s `_putInternal`\n * runs `enforceClassifiedWrite`'s effect via the pipeline's `enforceWrite`\n * phase; the codec's `encodeAtRest`/`decodeAtRest` hooks (Task 3's boundary)\n * replace the inline `sensitiveFields` seal path for any collection this\n * binding is compiled into.\n *\n * `declare` — `classifiedVia(cfg)` runs `resolveClassifiedFields` +\n * `guardClassifiedCompat` at CONSTRUCTION time (the same #553 pattern\n * `moneyVia`'s `validateMoneyFieldPaths(moneyFields)` call uses) —\n * mirrors today's \"door 1\" (`collection-config.ts`'s\n * `resolveCollectionConfig`). Both may throw `ClassifiedConfigError`.\n */\nimport type { NoydbVia, ViaWriteCtx, ViaCryptoCtx, SealedSlotRef, ViaEraseCtx, ViaEraseReport } from '../../kernel/via/index.js'\nimport { installViaBinder } from '../../kernel/via/index.js'\nimport { SealedHandle } from '../../kernel/types.js'\nimport { resolveClassifiedFields, type ClassifiedEntry } from './resolve.js'\nimport { guardClassifiedCompat, type ClassifiedGuardCtx } from './guards.js'\nimport { enforceClassifiedWrite } from './write.js'\nimport type { ClassifiedFieldSpec } from './descriptor.js'\n\n/**\n * One classified per-slot shred/residue verdict — mirrors\n * `RecordCodec.SealedShredSlot` (`kernel/enclave/record-keys/record-codec.ts`)\n * byte-for-byte. Duplicated here (not imported) so this binding never\n * reaches into `kernel/enclave/` (`via-enclave-isolation`, #629) — the\n * closure that PRODUCES real values of this shape is codec-owned and\n * injected via {@link ClassifiedViaConfig.classifySealedShred}.\n */\nexport interface ClassifiedShredSlot {\n readonly field: string\n readonly class: 'shreddable' | 'dekResidue' | 'live-shreddable+dekResidue-in-backups'\n}\n\n/**\n * Config a classified collection's declarations resolve to — the binding's\n * construction input. `entries`/`guardCtx` are the raw \"door 1\" inputs\n * (`resolveClassifiedFields`/`guardClassifiedCompat`'s own parameters);\n * `classifySealedShred`/`purgeSealedCekEnvelopes` are OPTIONAL codec/vault-\n * provided closures for the `erase` hook — undefined in this dormant task\n * (no caller wires them yet), wired for real in #629 Task 10 (\"posture\n * enforcement — forget + erase hooks live\").\n */\nexport interface ClassifiedViaConfig {\n readonly entries: Record<string, ClassifiedEntry>\n readonly collectionName: string\n readonly guardCtx: ClassifiedGuardCtx\n /**\n * Codec-provided closure mirroring `RecordCodec.classifySealedShred` —\n * classifies a live envelope's `_sealed` slots for crypto-shred\n * completeness (see that method's doc comment for the shreddable/\n * dekResidue/both-class semantics). `live` is `ViaEraseCtx.live`'s opaque\n * envelope, passed straight through. NOT `readonly` (#629 Task 10):\n * `compileVias` builds this cfg before the owning `Collection`'s\n * `RecordCodec` exists, so `Collection`'s constructor mutates this field\n * in place once `this.codec` is available — the closure `eraseClassified`\n * reads is looked up at CALL time, not at binding-construction time, so\n * the late assignment is picked up correctly.\n */\n classifySealedShred?: (live: unknown) => Promise<{ readonly slots: readonly ClassifiedShredSlot[] }>\n /**\n * Vault-provided closure that purges every `_sealed_cek/<collection>/<id>/*`\n * host-delivery envelope for one record (mirrors `rotateRecordCek`'s own\n * prefix-delete) and returns the count purged. Deliberately left UNWIRED\n * by `compileVias` (#629 Task 10) — `forget-sealed-erasure.test.ts`\n * proves this purge is UNCONDITIONAL on `sealRecordToHost` usage alone,\n * independent of whether `classifiedFields` is declared (a bare\n * `sensitive: [...]` collection exercises it with no classified binding\n * at all) — vault.ts keeps owning it directly so that case stays correct.\n */\n readonly purgeSealedCekEnvelopes?: (id: string) => Promise<number>\n}\n\n/** Fields declared `storage: 'recoverable'` (digest-only stays codec-inline; `'never'` is rejected by `enforceWrite` before either hook runs). */\nfunction recoverableFields(byField: Record<string, ClassifiedFieldSpec>): string[] {\n return Object.entries(byField).filter(([, spec]) => spec.storage === 'recoverable').map(([field]) => field)\n}\n\n/**\n * The full set this binding's `encodeAtRest`/`decodeAtRest` seal: declared\n * recoverable classified fields UNION bare `sensitive[]` fields — mirrors\n * `collection-config.ts`'s pre-cutover \"recoverable classified fields are\n * unioned into sensitiveFields... zero new crypto code\" semantics. Once this\n * binding is compiled into a collection's pipeline, `hasAtRestHooks` is true\n * and the codec's inline `sensitiveFields` seal path (record-codec.ts) is\n * DEAD for that collection — mutually exclusive with the hook path (#629\n * Task 3) — so bare `sensitive[]` fields MUST seal through here too, or\n * they'd silently stop being sealed at all (#629 Task 6 reconciliation).\n * `guardCtx.bareSensitiveFields` is the exact same `Set` the collection's\n * refusal-matrix guard already carries (`ClassifiedGuardCtx`).\n */\nfunction sealFieldNames(byField: Record<string, ClassifiedFieldSpec>, guardCtx: ClassifiedGuardCtx): string[] {\n return [...recoverableFields(byField), ...guardCtx.bareSensitiveFields]\n}\n\n/**\n * Seal every named field present with a defined value into its own sealed\n * slot via `crypto.sealedSlots`, peeling it out of the record.\n */\nasync function encodeClassifiedAtRest(\n record: Record<string, unknown>,\n crypto: ViaCryptoCtx,\n fields: readonly string[],\n): Promise<{ record: Record<string, unknown>; sealed?: Record<string, SealedSlotRef> }> {\n let open = record\n let sealed: Record<string, SealedSlotRef> | undefined\n for (const field of fields) {\n if (!(field in record)) continue\n const value = record[field]\n if (value === undefined) continue\n const ref = await crypto.sealedSlots.seal(field, value)\n if (open === record) open = { ...record }\n delete open[field]\n sealed ??= {}\n sealed[field] = ref\n }\n return sealed ? { record: open, sealed } : { record }\n}\n\n/**\n * Restore every named field's sealed slot back onto the record: an opaque\n * `SealedHandle` under `asHandles` (never materialises plaintext into the\n * cache — `SealedHandle.toJSON()` returns `'[sealed]'`, the export-redaction\n * guarantee `reveal()` alone can lift), the plaintext value otherwise.\n */\nasync function decodeClassifiedAtRest(\n record: Record<string, unknown>,\n sealed: Record<string, SealedSlotRef>,\n crypto: ViaCryptoCtx,\n opts: { asHandles: boolean },\n fields: readonly string[],\n): Promise<Record<string, unknown>> {\n let out = record\n for (const field of fields) {\n const ref = sealed[field]\n if (ref === undefined) continue\n if (out === record) out = { ...record }\n out[field] = opts.asHandles\n ? new SealedHandle(() => crypto.sealedSlots.unseal(field, ref))\n : await crypto.sealedSlots.unseal(field, ref)\n }\n return out\n}\n\n/**\n * `forget()`'s per-ref erasure participation: classify the live envelope's\n * sealed slots (via the codec-provided closure) and mark each shreddable\n * one deleted on this call's sealed-slots capability, then purge the\n * record's sealed-CEK host-delivery envelopes (via the vault-provided\n * closure). Both closures are optional — undefined (this dormant task's\n * only caller, its own unit tests) reports a zero-shredded, zero-residue\n * no-op rather than throwing.\n */\nasync function eraseClassified(\n ctx: ViaEraseCtx,\n cfg: ClassifiedViaConfig,\n): Promise<ViaEraseReport> {\n let shredded = 0\n const residue: unknown[] = []\n\n if (cfg.classifySealedShred) {\n const { slots } = await cfg.classifySealedShred(ctx.live)\n for (const slot of slots) {\n if (slot.class === 'shreddable' || slot.class === 'live-shreddable+dekResidue-in-backups') {\n shredded += 1\n await ctx.crypto.sealedSlots.delete(slot.field)\n }\n if (slot.class === 'dekResidue' || slot.class === 'live-shreddable+dekResidue-in-backups') {\n residue.push({ kind: 'classified-sealed-dek-residue', field: slot.field })\n }\n }\n }\n\n if (cfg.purgeSealedCekEnvelopes) {\n shredded += await cfg.purgeSealedCekEnvelopes(ctx.id)\n }\n\n return { shredded, residue }\n}\n\nfunction buildClassifiedDescribeFragment(byField: Record<string, ClassifiedFieldSpec>): Record<string, unknown> {\n return {\n classifiedFields: Object.fromEntries(\n Object.entries(byField).map(([field, spec]) => [field, { storage: spec.storage, sensitivity: spec.sensitivity }]),\n ),\n }\n}\n\nexport function classifiedVia(cfg: ClassifiedViaConfig): NoydbVia {\n // declare — door 1: resolve + guard, exactly like collection-config.ts's\n // resolveCollectionConfig does today. Throws ClassifiedConfigError on any\n // R1-R8 refusal-matrix violation or field-collision.\n const resolved = resolveClassifiedFields(cfg.collectionName, cfg.entries)\n guardClassifiedCompat(cfg.collectionName, resolved.byField, cfg.guardCtx)\n const byField = resolved.byField\n const sealFields = sealFieldNames(byField, cfg.guardCtx)\n\n return {\n brand: 'classified',\n posture: { encryptedAtRest: 'sealed', queryable: 'det-exact', exportable: false, forgettable: true },\n covers: (field) => field in byField,\n // async (rather than a bare passthrough) so a refusal is always a\n // rejected Promise for direct callers, matching every other async-stack\n // hook's calling convention — `enforceClassifiedWrite` itself stays sync.\n enforceWrite: async (record: Record<string, unknown>, _ctx: ViaWriteCtx) =>\n enforceClassifiedWrite(record, byField, cfg.collectionName),\n encodeAtRest: (record, crypto) => encodeClassifiedAtRest(record, crypto, sealFields),\n decodeAtRest: (record, sealed, crypto, opts) => decodeClassifiedAtRest(record, sealed, crypto, opts, sealFields),\n erase: (ctx) => eraseClassified(ctx, cfg),\n describeFragment: () => buildClassifiedDescribeFragment(byField),\n }\n}\n\nexport function linkClassifiedVia(): void {\n installViaBinder('classified', (c) => classifiedVia(c as ClassifiedViaConfig))\n}\n","/**\n * Classified strategy seam (#629 Task 5 — moved out of\n * `via/classified/strategy.ts`, precedent: `port/with/i18n-strategy.ts`).\n * Lives on the `/with` port (the one seam the kernel spine may import\n * statically) so `Collection`/`Vault` can hold the `NO_CLASSIFIED` default\n * without a spine→service static import.\n *\n * This file (unlike `via/classified/**`) is NOT subject to the\n * `via-enclave-isolation` architecture guard — it may import `EnclaveKey`\n * from the enclave barrel freely, the same way `port/with/i18n-strategy.ts`\n * did before #629 Task 4's DictionaryHandle cutover.\n *\n * The ② capability seam for classified read-egress ops (stage 1: reveal;\n * stage 2: verify).\n *\n * @internal\n */\nimport type { ClassifiedFieldSpec } from '../../via/classified/descriptor.js'\nimport { ClassifiedNotEnabledError } from '../../kernel/errors.js'\nimport type { EncryptedEnvelope } from '../../kernel/types.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\nimport { linkClassifiedVia } from '../../via/classified/binding.js'\n\n// #629 Task 10 — re-export the classified binding's erase-cfg TYPE (not\n// `via/classified/binding.js` directly) so the kernel spine can name\n// it for the post-construction `classifySealedShred` wiring, same rationale\n// as `resolveClassifiedFields`/`guardClassifiedCompat` above.\nexport type { ClassifiedViaConfig } from '../../via/classified/binding.js'\n\nexport type { ClassifiedVerdict } from '../../kernel/types.js'\nimport type { ClassifiedVerdict } from '../../kernel/types.js'\n\n// #629 Task 6 — the classified binding's construction-time resolve/guard,\n// re-exported here (not `via/classified/{resolve,guards}.js` directly)\n// so the kernel spine keeps importing only this one `/with` port seam, same\n// rationale as `NO_CLASSIFIED` below.\nexport { resolveClassifiedFields, type ClassifiedEntry, type ResolvedClassified } from '../../via/classified/resolve.js'\nexport { guardClassifiedCompat, type ClassifiedGuardCtx } from '../../via/classified/guards.js'\nexport type { ClassifiedFieldSpec }\n\n// Install the classified Via binder EAGERLY — unlike money()/i18nText(),\n// several classified fixtures (both hub tests and consumer code) build a raw\n// `ClassifiedFieldSpec` object literal without ever calling a\n// `classified.*()` preset (bypassing `via/classified/presets.ts`\n// entirely — an intentionally supported pattern, see that module's Task 5\n// doc comment). Linking lazily at preset-call time (the money/i18n pattern)\n// would leave `viaBinder('classified')` unlinked for those declarations.\n// This port module is already unconditionally imported by the kernel spine\n// (for the `NO_CLASSIFIED` strategy default below), so linking here\n// guarantees the binder is always installed before `compileVias`\n// ever needs it, regardless of how a collection's classifiedFields were\n// constructed.\nlinkClassifiedVia()\n\nexport interface ClassifiedRevealCtx {\n readonly collection: string\n readonly spec: ClassifiedFieldSpec\n /** True on an encrypted collection — false selects the plaintext-body read. */\n readonly encrypted: boolean\n readonly getEnvelope: (id: string) => Promise<EncryptedEnvelope | null>\n readonly resolveCek: (env: EncryptedEnvelope) => Promise<EnclaveKey | undefined>\n readonly getDEK: () => Promise<EnclaveKey>\n readonly onAccess?: ((op: 'reveal', id: string) => Promise<void>) | undefined\n}\n\nexport interface ClassifiedVerifyCtx {\n readonly collection: string\n readonly spec: ClassifiedFieldSpec\n readonly getEnvelope: (id: string) => Promise<EncryptedEnvelope | null> // raw envelope, NOT a decrypted view\n readonly resolveCek: (env: EncryptedEnvelope) => Promise<EnclaveKey | undefined>\n readonly getDEK: () => Promise<EnclaveKey>\n readonly now: () => number // injected (Q7)\n /** Group members resolved by the collection (matchGroup only). */\n readonly groupMembers?: ReadonlyArray<{ readonly field: string; readonly spec: ClassifiedFieldSpec }>\n readonly onAccess?: ((op: 'verify' | 'find', id: string) => Promise<void>) | undefined\n}\n\nexport interface ClassifiedStrategy {\n reveal(ctx: ClassifiedRevealCtx, id: string, field: string): Promise<unknown>\n verify(ctx: ClassifiedVerifyCtx, id: string, field: string, candidate: string): Promise<ClassifiedVerdict>\n verifyText(ctx: ClassifiedVerifyCtx, id: string, field: string, candidate: string): Promise<ClassifiedVerdict>\n matchGroup(ctx: ClassifiedVerifyCtx, id: string, answers: Record<string, string>,\n opts: { readonly min: number }): Promise<{ readonly passed: boolean }>\n computeTarget(ctx: ClassifiedVerifyCtx, field: string, candidate: string, costByte?: number): Promise<string | null>\n}\n\nexport const NO_CLASSIFIED: ClassifiedStrategy = {\n async reveal() { throw new ClassifiedNotEnabledError() },\n async verify() { throw new ClassifiedNotEnabledError() },\n async verifyText() { throw new ClassifiedNotEnabledError() },\n async matchGroup() { throw new ClassifiedNotEnabledError() },\n async computeTarget() { throw new ClassifiedNotEnabledError() },\n}\n"],"mappings":"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","names":[]}
@@ -7,27 +7,27 @@ import {
7
7
  PERIOD_FREEZES_COLLECTION,
8
8
  PERIOD_TARGET_PURGES_COLLECTION,
9
9
  periodExclusiveUpperBound
10
- } from "./chunk-5O5ZSW7N.js";
10
+ } from "./chunk-Z7ZXZL6Y.js";
11
11
  import {
12
12
  envelopeBodySize
13
- } from "./chunk-CXPOG3ZY.js";
13
+ } from "./chunk-2LPVI4PX.js";
14
14
  import {
15
15
  buildRecordEnvelope,
16
16
  isDeleteMarker,
17
17
  isTombstoneShape
18
- } from "./chunk-FX3UIDCS.js";
18
+ } from "./chunk-N4CNVSUF.js";
19
19
  import {
20
20
  decrypt,
21
21
  derivePresenceKey,
22
22
  encrypt
23
- } from "./chunk-ONPVHX2Y.js";
23
+ } from "./chunk-W5LHQXZU.js";
24
24
  import {
25
25
  NOYDB_SYNC_VERSION
26
- } from "./chunk-UJ4Z67UQ.js";
26
+ } from "./chunk-RK7N4OQ5.js";
27
27
  import {
28
28
  ValidationError,
29
29
  isConflictError
30
- } from "./chunk-Z3BZXPRO.js";
30
+ } from "./chunk-JBIV4FLR.js";
31
31
 
32
32
  // src/with-sync/presence.ts
33
33
  var subtle = globalThis.crypto.subtle;
@@ -1213,4 +1213,4 @@ export {
1213
1213
  SyncEngine,
1214
1214
  SyncTransaction
1215
1215
  };
1216
- //# sourceMappingURL=chunk-4ATF4BLF.js.map
1216
+ //# sourceMappingURL=chunk-H36KSN6X.js.map
@@ -1,204 +1,46 @@
1
1
  import {
2
2
  walkClosure
3
- } from "./chunk-5F7BZAGV.js";
3
+ } from "./chunk-OWLVBOWO.js";
4
4
  import {
5
5
  resolveManagedSecret
6
- } from "./chunk-S2I5MYSA.js";
6
+ } from "./chunk-Z2WK53N6.js";
7
7
  import {
8
8
  parseExtractedPartitionBody,
9
9
  readPod,
10
10
  readPodHeader
11
- } from "./chunk-GCU42IG7.js";
11
+ } from "./chunk-6FOGTKBY.js";
12
12
  import {
13
13
  createOwnerKeyring,
14
14
  mintRosterTag,
15
15
  requireRosterKey
16
- } from "./chunk-J6I5VR6P.js";
17
- import {
18
- diff
19
- } from "./chunk-SVLR4POP.js";
16
+ } from "./chunk-5W7YXFV5.js";
20
17
  import {
21
18
  LedgerStore
22
- } from "./chunk-JXHNUYQO.js";
19
+ } from "./chunk-IESNYJXZ.js";
23
20
  import {
24
21
  LEDGER_COLLECTION
25
22
  } from "./chunk-I7FD46FF.js";
26
23
  import {
27
24
  openEnvelopeJson
28
- } from "./chunk-CXPOG3ZY.js";
25
+ } from "./chunk-2LPVI4PX.js";
29
26
  import {
30
27
  buildRecordEnvelope
31
- } from "./chunk-FX3UIDCS.js";
28
+ } from "./chunk-N4CNVSUF.js";
32
29
  import {
33
30
  base64ToBuffer,
34
31
  wrapKey
35
- } from "./chunk-ONPVHX2Y.js";
32
+ } from "./chunk-W5LHQXZU.js";
36
33
  import {
37
34
  AdoptionStateError,
38
35
  TransferSealError,
39
36
  ValidationError
40
- } from "./chunk-Z3BZXPRO.js";
41
-
42
- // src/with-cargo/vault-diff.ts
43
- async function diffVault(vault, candidate, options = {}) {
44
- const idKey = options.idKey ?? "id";
45
- const filter = options.collections ? new Set(options.collections) : null;
46
- const compareFn = options.compareFn ?? ((a, b) => diff(a, b).length === 0);
47
- const live = /* @__PURE__ */ new Map();
48
- for await (const chunk of vault.exportStream({ granularity: "collection" })) {
49
- if (filter && !filter.has(chunk.collection)) continue;
50
- const collection = live.get(chunk.collection) ?? /* @__PURE__ */ new Map();
51
- for (const record of chunk.records) {
52
- const id = readIdField(record, idKey);
53
- if (!id) continue;
54
- collection.set(id, record);
55
- }
56
- live.set(chunk.collection, collection);
57
- }
58
- const cand = await normaliseCandidate(candidate, idKey, filter);
59
- const added = [];
60
- const modified = [];
61
- const deleted = [];
62
- const unchanged = options.includeUnchanged ? [] : void 0;
63
- const collectionNames = /* @__PURE__ */ new Set([...live.keys(), ...cand.keys()]);
64
- for (const collection of [...collectionNames].sort()) {
65
- const liveColl = live.get(collection) ?? /* @__PURE__ */ new Map();
66
- const candColl = cand.get(collection) ?? /* @__PURE__ */ new Map();
67
- const allIds = /* @__PURE__ */ new Set([...liveColl.keys(), ...candColl.keys()]);
68
- for (const id of [...allIds].sort()) {
69
- const before = liveColl.get(id);
70
- const after = candColl.get(id);
71
- if (before === void 0 && after !== void 0) {
72
- added.push({ collection, id, record: after });
73
- } else if (before !== void 0 && after === void 0) {
74
- const metadata = options.includeMetadata ? await vault.collection(collection).getMetadata(id) ?? void 0 : void 0;
75
- deleted.push({ collection, id, record: before, ...metadata !== void 0 ? { metadata } : {} });
76
- } else if (before !== void 0 && after !== void 0) {
77
- if (compareFn(before, after)) {
78
- unchanged?.push({ collection, id, record: after });
79
- } else {
80
- const metadata = options.includeMetadata ? await vault.collection(collection).getMetadata(id) ?? void 0 : void 0;
81
- const fieldDiffs = diff(before, after);
82
- const fieldsChanged = uniqueTopLevelKeys(fieldDiffs);
83
- modified.push({
84
- collection,
85
- id,
86
- record: after,
87
- before,
88
- fieldsChanged,
89
- fieldDiffs,
90
- ...metadata !== void 0 ? { metadata } : {}
91
- });
92
- }
93
- }
94
- }
95
- }
96
- const summary = {
97
- add: added.length,
98
- modify: modified.length,
99
- delete: deleted.length,
100
- total: added.length + modified.length + deleted.length
101
- };
102
- return {
103
- added,
104
- modified,
105
- deleted,
106
- unchanged,
107
- summary,
108
- format(opts) {
109
- return formatDiff(opts?.detail ?? "full", { added, modified, deleted, summary });
110
- }
111
- };
112
- }
113
- async function normaliseCandidate(candidate, idKey, filter) {
114
- const out = /* @__PURE__ */ new Map();
115
- if (typeof candidate === "object" && candidate !== null && "exportStream" in candidate && typeof candidate.exportStream === "function") {
116
- for await (const chunk of candidate.exportStream({ granularity: "collection" })) {
117
- if (filter && !filter.has(chunk.collection)) continue;
118
- const collection = out.get(chunk.collection) ?? /* @__PURE__ */ new Map();
119
- for (const record of chunk.records) {
120
- const id = readIdField(record, idKey);
121
- if (!id) continue;
122
- collection.set(id, record);
123
- }
124
- out.set(chunk.collection, collection);
125
- }
126
- return out;
127
- }
128
- if (typeof candidate === "string") {
129
- let parsed;
130
- try {
131
- parsed = JSON.parse(candidate);
132
- } catch (err) {
133
- throw new Error(
134
- `diffVault: candidate string is not valid JSON (${err.message})`
135
- );
136
- }
137
- return collectionsFromObject(parsed, idKey, filter);
138
- }
139
- return collectionsFromObject(candidate, idKey, filter);
140
- }
141
- function collectionsFromObject(raw, idKey, filter) {
142
- const out = /* @__PURE__ */ new Map();
143
- if (raw === null || typeof raw !== "object") {
144
- throw new Error("diffVault: candidate must be a Vault, an object, or a JSON string");
145
- }
146
- for (const [key, value] of Object.entries(raw)) {
147
- if (key.startsWith("_")) continue;
148
- if (filter && !filter.has(key)) continue;
149
- if (!Array.isArray(value)) continue;
150
- const collection = /* @__PURE__ */ new Map();
151
- for (const record of value) {
152
- if (record === null || typeof record !== "object") continue;
153
- const id = readIdField(record, idKey);
154
- if (!id) continue;
155
- collection.set(id, record);
156
- }
157
- out.set(key, collection);
158
- }
159
- return out;
160
- }
161
- function uniqueTopLevelKeys(diffs) {
162
- const keys = /* @__PURE__ */ new Set();
163
- for (const d of diffs) {
164
- const m = /^[^.[]+/.exec(d.path);
165
- if (m) keys.add(m[0]);
166
- }
167
- return [...keys];
168
- }
169
- function readIdField(record, idKey) {
170
- if (record === null || typeof record !== "object") return "";
171
- const v = record[idKey];
172
- if (typeof v === "string") return v;
173
- if (typeof v === "number" && Number.isFinite(v)) return String(v);
174
- return "";
175
- }
176
- function formatDiff(detail, b) {
177
- const head = `${b.summary.add} added \xB7 ${b.summary.modify} modified \xB7 ${b.summary.delete} deleted`;
178
- if (detail === "count") return head;
179
- if (b.summary.total === 0) return head + "\n(no changes)";
180
- if (detail === "one-line") return head;
181
- const rows = [head, ""];
182
- for (const e of b.added) rows.push(`${e.collection}/${e.id} added`);
183
- for (const e of b.modified) {
184
- const fields = e.fieldDiffs.map((f) => `${f.path}: ${shortJSON(f.from)} \u2192 ${shortJSON(f.to)}`).join(", ");
185
- rows.push(`${e.collection}/${e.id} modified ${fields}`);
186
- }
187
- for (const e of b.deleted) rows.push(`${e.collection}/${e.id} deleted`);
188
- return rows.join("\n");
189
- }
190
- function shortJSON(value) {
191
- if (value === void 0) return "undefined";
192
- const s = JSON.stringify(value);
193
- if (typeof s !== "string") return "<unrepresentable>";
194
- return s.length > 60 ? s.slice(0, 57) + "..." : s;
195
- }
37
+ } from "./chunk-JBIV4FLR.js";
196
38
 
197
39
  // src/with-cargo/active.ts
198
40
  function withCargo() {
199
41
  return {
200
42
  async extractPartition(vault, opts) {
201
- const { extractPartitionCore } = await import("./extract-partition-KVCTSUVE.js");
43
+ const { extractPartitionCore } = await import("./extract-partition-UPOVITFI.js");
202
44
  return extractPartitionCore(vault, opts);
203
45
  }
204
46
  };
@@ -408,7 +250,7 @@ async function createOwnerOnAdoptedPartition(store, vaultName, opts) {
408
250
  }
409
251
  }
410
252
  if (isManaged(opts)) {
411
- const { createNoydb } = await import("./noydb-LFDRMYZ5.js");
253
+ const { createNoydb } = await import("./noydb-4VV2ZGLZ.js");
412
254
  const db = await createNoydb({
413
255
  store,
414
256
  user: userId,
@@ -460,8 +302,7 @@ export {
460
302
  adoptPartition,
461
303
  createOwnerOnAdoptedPartition,
462
304
  decryptExtractedPartition,
463
- diffVault,
464
305
  withCargo,
465
306
  describeExtraction
466
307
  };
467
- //# sourceMappingURL=chunk-BMU2DS3S.js.map
308
+ //# sourceMappingURL=chunk-H3VG6XBC.js.map