@noy-db/hub 0.2.0-pre.3 → 0.2.0-pre.31

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 (384) hide show
  1. package/README.md +126 -0
  2. package/dist/adapter/index.d.ts +9 -0
  3. package/dist/adapter/index.js +14 -0
  4. package/dist/aggregate/index.d.ts +3 -2
  5. package/dist/aggregate/index.js +10 -8
  6. package/dist/aggregate/index.js.map +1 -1
  7. package/dist/attestation/index.d.ts +7 -5
  8. package/dist/attestation/index.js +7 -6
  9. package/dist/attestation/index.js.map +1 -1
  10. package/dist/blobs/index.d.ts +9 -7
  11. package/dist/blobs/index.js +12 -6
  12. package/dist/blobs/index.js.map +1 -1
  13. package/dist/bundle/index.d.ts +29 -18
  14. package/dist/bundle/index.js +53 -192
  15. package/dist/bundle/index.js.map +1 -1
  16. package/dist/{chunk-75QDHSE4.js → chunk-2QDWRK3B.js} +5 -5
  17. package/dist/{chunk-74JEQFMT.js → chunk-2UT7HJJR.js} +5 -5
  18. package/dist/chunk-2UT7HJJR.js.map +1 -0
  19. package/dist/{chunk-FCDO7UAO.js → chunk-2ZYIN3BL.js} +2 -2
  20. package/dist/{chunk-BFI3RS42.js → chunk-33EQCZXE.js} +2 -2
  21. package/dist/chunk-33EQCZXE.js.map +1 -0
  22. package/dist/{chunk-WRLHNG6H.js → chunk-4K4QKYK4.js} +4 -4
  23. package/dist/chunk-4K4QKYK4.js.map +1 -0
  24. package/dist/{chunk-HGZ7DC5H.js → chunk-4L6N56B4.js} +2 -2
  25. package/dist/chunk-4L6N56B4.js.map +1 -0
  26. package/dist/{chunk-YMYK7US4.js → chunk-4YD7VZH4.js} +2 -2
  27. package/dist/chunk-4YD7VZH4.js.map +1 -0
  28. package/dist/{chunk-ZUMGGHRB.js → chunk-5PTX7PKD.js} +4 -4
  29. package/dist/{chunk-QAU5HM6Q.js → chunk-6EKRQ7QF.js} +3 -3
  30. package/dist/{chunk-ZC2AAE6J.js → chunk-7GPQPLGO.js} +19 -4
  31. package/dist/chunk-7GPQPLGO.js.map +1 -0
  32. package/dist/{chunk-EPK6A3WJ.js → chunk-A3JMGXPG.js} +2 -2
  33. package/dist/chunk-A3JMGXPG.js.map +1 -0
  34. package/dist/{chunk-FXQYZNOW.js → chunk-A7DNZVAV.js} +1 -1
  35. package/dist/chunk-A7DNZVAV.js.map +1 -0
  36. package/dist/{chunk-UMLVJTYV.js → chunk-ADB7GPM3.js} +7 -4
  37. package/dist/chunk-ADB7GPM3.js.map +1 -0
  38. package/dist/{chunk-YL2DR3HY.js → chunk-ASIGWYRA.js} +2 -2
  39. package/dist/chunk-ASIGWYRA.js.map +1 -0
  40. package/dist/{chunk-2EYC3WDT.js → chunk-AWU4JF7C.js} +93 -93
  41. package/dist/chunk-AWU4JF7C.js.map +1 -0
  42. package/dist/chunk-BEZ2EV43.js +129 -0
  43. package/dist/chunk-BEZ2EV43.js.map +1 -0
  44. package/dist/{chunk-IS5HWQO7.js → chunk-CIZDUAHI.js} +30 -4
  45. package/dist/chunk-CIZDUAHI.js.map +1 -0
  46. package/dist/{chunk-4OQWR46B.js → chunk-CUPTCF4R.js} +5 -5
  47. package/dist/chunk-CUPTCF4R.js.map +1 -0
  48. package/dist/{chunk-5ZGZ6HIZ.js → chunk-DEE4CRYN.js} +30 -7
  49. package/dist/chunk-DEE4CRYN.js.map +1 -0
  50. package/dist/{chunk-UOF74WQY.js → chunk-E2MDPPOC.js} +37 -2
  51. package/dist/chunk-E2MDPPOC.js.map +1 -0
  52. package/dist/{chunk-SAVQ6E2O.js → chunk-FFKQ65YX.js} +10 -3
  53. package/dist/chunk-FFKQ65YX.js.map +1 -0
  54. package/dist/{chunk-2XLVPKXG.js → chunk-G7ALNCQH.js} +10 -2
  55. package/dist/chunk-G7ALNCQH.js.map +1 -0
  56. package/dist/chunk-GGXN73NM.js +252 -0
  57. package/dist/chunk-GGXN73NM.js.map +1 -0
  58. package/dist/{chunk-UVPGJXVO.js → chunk-JOJPBZSZ.js} +5 -5
  59. package/dist/{chunk-YDLAFP36.js → chunk-JZ5DS4DP.js} +346 -3
  60. package/dist/chunk-JZ5DS4DP.js.map +1 -0
  61. package/dist/{chunk-KMI2NBBF.js → chunk-K4JID2ZE.js} +44 -8
  62. package/dist/chunk-K4JID2ZE.js.map +1 -0
  63. package/dist/{aggregate/index.cjs → chunk-K6NVSHAH.js} +241 -210
  64. package/dist/chunk-K6NVSHAH.js.map +1 -0
  65. package/dist/chunk-KJ3K5VZM.js +524 -0
  66. package/dist/chunk-KJ3K5VZM.js.map +1 -0
  67. package/dist/chunk-L5HHBOMS.js +232 -0
  68. package/dist/chunk-L5HHBOMS.js.map +1 -0
  69. package/dist/{chunk-YK72A4IT.js → chunk-LBILABKZ.js} +4 -4
  70. package/dist/chunk-LXKBG52H.js +151 -0
  71. package/dist/chunk-LXKBG52H.js.map +1 -0
  72. package/dist/{chunk-NGSPBLLE.js → chunk-MPIZQGFA.js} +50 -20
  73. package/dist/chunk-MPIZQGFA.js.map +1 -0
  74. package/dist/{chunk-4X2S7PBF.js → chunk-MRJPSXPE.js} +84 -70
  75. package/dist/chunk-MRJPSXPE.js.map +1 -0
  76. package/dist/{chunk-6S3LLAQ5.js → chunk-MSMDDDDJ.js} +3 -3
  77. package/dist/{chunk-6S3LLAQ5.js.map → chunk-MSMDDDDJ.js.map} +1 -1
  78. package/dist/{chunk-GAUBWHAF.js → chunk-OCYFFBZS.js} +4 -4
  79. package/dist/{chunk-NCO2JGKK.js → chunk-PDVP3C2I.js} +1 -1
  80. package/dist/chunk-PDVP3C2I.js.map +1 -0
  81. package/dist/chunk-PZ5AY32C.js +10 -0
  82. package/dist/{chunk-P6256WTJ.js → chunk-QBLPY44S.js} +3 -3
  83. package/dist/chunk-QBLPY44S.js.map +1 -0
  84. package/dist/{query/index.cjs → chunk-QGBBQKDS.js} +706 -831
  85. package/dist/chunk-QGBBQKDS.js.map +1 -0
  86. package/dist/{chunk-FS7A4XNF.js → chunk-QWJCNXH2.js} +3 -3
  87. package/dist/chunk-QXWJQ7UU.js +380 -0
  88. package/dist/chunk-QXWJQ7UU.js.map +1 -0
  89. package/dist/{chunk-GDTCGIPX.js → chunk-RCB6GWTC.js} +27 -5
  90. package/dist/chunk-RCB6GWTC.js.map +1 -0
  91. package/dist/{chunk-K5PVGKE4.js → chunk-SAAJYSVV.js} +2 -2
  92. package/dist/{chunk-G7PAZ3TD.js → chunk-SHJ3BRTZ.js} +46 -10
  93. package/dist/chunk-SHJ3BRTZ.js.map +1 -0
  94. package/dist/chunk-SQXTKZL7.js +125 -0
  95. package/dist/chunk-SQXTKZL7.js.map +1 -0
  96. package/dist/chunk-STNPB3UM.js +9 -0
  97. package/dist/chunk-STNPB3UM.js.map +1 -0
  98. package/dist/{chunk-NSLTPGEN.js → chunk-TMUMMAWV.js} +12 -2
  99. package/dist/{chunk-NSLTPGEN.js.map → chunk-TMUMMAWV.js.map} +1 -1
  100. package/dist/{chunk-LS3JLEIB.js → chunk-TZGWI5AX.js} +4 -4
  101. package/dist/{chunk-GD3BGKAR.js → chunk-UBF5JEOJ.js} +44 -5
  102. package/dist/chunk-UBF5JEOJ.js.map +1 -0
  103. package/dist/{chunk-T6HQMVML.js → chunk-W5NVNJDX.js} +5250 -801
  104. package/dist/chunk-W5NVNJDX.js.map +1 -0
  105. package/dist/chunk-XAYDHD2O.js +123 -0
  106. package/dist/chunk-XAYDHD2O.js.map +1 -0
  107. package/dist/{chunk-LOL725S4.js → chunk-XYW2CSCI.js} +31 -3
  108. package/dist/chunk-XYW2CSCI.js.map +1 -0
  109. package/dist/{chunk-TLFUDXVV.js → chunk-ZCMOS4H2.js} +31 -10
  110. package/dist/chunk-ZCMOS4H2.js.map +1 -0
  111. package/dist/{chunk-FBMXWVGP.js → chunk-ZK66B5EY.js} +450 -30
  112. package/dist/chunk-ZK66B5EY.js.map +1 -0
  113. package/dist/{chunk-GVXBHCZ2.js → chunk-ZPJSQPQH.js} +66 -7
  114. package/dist/chunk-ZPJSQPQH.js.map +1 -0
  115. package/dist/consent/index.d.ts +8 -6
  116. package/dist/consent/index.js +4 -3
  117. package/dist/consent/index.js.map +1 -1
  118. package/dist/crdt/index.js +1 -0
  119. package/dist/crdt/index.js.map +1 -1
  120. package/dist/{crypto-H2Y3DDFW.js → crypto-OSIV2IIW.js} +8 -3
  121. package/dist/{ulid-CiM2OAeM.d.ts → decrypt-partition-CXS5KopB.d.ts} +37 -82
  122. package/dist/{delegation-QSC7G5QC.js → delegation-BQTRJATI.js} +6 -5
  123. package/dist/derivations/index.d.ts +11 -9
  124. package/dist/derivations/index.js +9 -6
  125. package/dist/{dev-unlock-Cf2B7Kih.d.ts → dev-unlock-CnHTTVea.d.ts} +1 -1
  126. package/dist/discriminant-BN9REW3o.d.ts +60 -0
  127. package/dist/errors-BAWO5Z5a.d.ts +1500 -0
  128. package/dist/executor-KYSKFJ2R.js +9 -0
  129. package/dist/executor-M7W3NEKR.js +9 -0
  130. package/dist/executor-VDTDGWO6.js +13 -0
  131. package/dist/{fanout-sidecar-NRBWSLRK.js → fanout-sidecar-AZALISHB.js} +3 -2
  132. package/dist/fanout-sidecar-AZALISHB.js.map +1 -0
  133. package/dist/forget/index.d.ts +1 -0
  134. package/dist/forget/index.js +15 -0
  135. package/dist/guards/index.d.ts +16 -8
  136. package/dist/guards/index.js +12 -5
  137. package/dist/{hash-gVn_uKhp.d.ts → hash-oc5paYl0.d.ts} +1 -1
  138. package/dist/history/index.d.ts +9 -7
  139. package/dist/history/index.js +10 -7
  140. package/dist/history/index.js.map +1 -1
  141. package/dist/i18n/index.d.ts +8 -6
  142. package/dist/i18n/index.js +116 -15
  143. package/dist/i18n/index.js.map +1 -1
  144. package/dist/index-BMmajblo.d.ts +362 -0
  145. package/dist/index-BmhGztUi.d.ts +93 -0
  146. package/dist/{types-D9eB0Rvh.d.ts → index-DHn9lEP0.d.ts} +4305 -1523
  147. package/dist/{index-BF1B2HB9.d.ts → index-Ddm8D3Oo.d.ts} +186 -1151
  148. package/dist/index.d.ts +342 -70
  149. package/dist/index.js +420 -112
  150. package/dist/index.js.map +1 -1
  151. package/dist/indexing/index.d.ts +3 -3
  152. package/dist/indexing/index.js +5 -4
  153. package/dist/indexing/index.js.map +1 -1
  154. package/dist/issue-DPHRF2TI.js +13 -0
  155. package/dist/kernel/index.d.ts +11 -0
  156. package/dist/kernel/index.js +50 -0
  157. package/dist/{lazy-builder-Rpd-V3jP.d.ts → lazy-builder-ChSqcF5t.d.ts} +2 -2
  158. package/dist/{ledger-WOEJUYTP.js → ledger-CI7GSMLB.js} +7 -6
  159. package/dist/materialized-views/index.d.ts +23 -20
  160. package/dist/materialized-views/index.js +9 -7
  161. package/dist/mime-magic-JeLKHRng.d.ts +103 -0
  162. package/dist/noydb-NAU2DTFP.js +40 -0
  163. package/dist/overlay-views/index.d.ts +32 -12
  164. package/dist/overlay-views/index.js +7 -6
  165. package/dist/periods/index.d.ts +8 -6
  166. package/dist/periods/index.js +7 -6
  167. package/dist/{predicate-Dnu81tsS.d.cts → predicate-BmhBSPCH.d.ts} +87 -5
  168. package/dist/{public-envelope-OHQ5UZFM.js → public-envelope-MFMBFJLZ.js} +5 -4
  169. package/dist/query/index.d.ts +4 -3
  170. package/dist/query/index.js +14 -6
  171. package/dist/read-only-facade-3OQRZ3VS.js +8 -0
  172. package/dist/registry-IX5WKK4F.js +8 -0
  173. package/dist/registry-LMHO5265.js +11 -0
  174. package/dist/registry-TXCKWH26.js +9 -0
  175. package/dist/registry-VFEDQSYV.js +9 -0
  176. package/dist/revoke-5PUOZPJJ.js +18 -0
  177. package/dist/revoke-5PUOZPJJ.js.map +1 -0
  178. package/dist/sealed-record/index.d.ts +123 -0
  179. package/dist/sealed-record/index.js +43 -0
  180. package/dist/sealed-record/index.js.map +1 -0
  181. package/dist/session/index.d.ts +9 -7
  182. package/dist/session/index.js +4 -3
  183. package/dist/session/index.js.map +1 -1
  184. package/dist/shadow/index.d.ts +8 -6
  185. package/dist/shadow/index.js +3 -2
  186. package/dist/shadow/index.js.map +1 -1
  187. package/dist/{signer-M4K5HBLD.js → signer-UB5TQOZ6.js} +6 -5
  188. package/dist/signer-UB5TQOZ6.js.map +1 -0
  189. package/dist/snapshots/index.d.ts +30 -0
  190. package/dist/snapshots/index.js +153 -0
  191. package/dist/snapshots/index.js.map +1 -0
  192. package/dist/{stale-PAGCS4K5.js → stale-J3TUF5C5.js} +3 -2
  193. package/dist/stale-J3TUF5C5.js.map +1 -0
  194. package/dist/store/index.d.ts +15 -6
  195. package/dist/store/index.js +3 -2
  196. package/dist/{strategy-DSTrsZ8t.d.ts → strategy-8QGs5KQE.d.ts} +475 -7
  197. package/dist/sync/index.d.ts +7 -5
  198. package/dist/sync/index.js +5 -4
  199. package/dist/sync/index.js.map +1 -1
  200. package/dist/team/index.d.ts +8 -6
  201. package/dist/team/index.js +9 -8
  202. package/dist/transition-guard-Dy3NioQr.d.ts +165 -0
  203. package/dist/tx/index.d.ts +26 -8
  204. package/dist/tx/index.js +10 -5
  205. package/dist/tx/index.js.map +1 -1
  206. package/dist/ulid-DRH25k3y.d.ts +66 -0
  207. package/dist/{ulid-COREQ2RQ.js → ulid-KEPZMD2N.js} +2 -1
  208. package/dist/ulid-KEPZMD2N.js.map +1 -0
  209. package/dist/util/index.d.ts +2 -0
  210. package/dist/util/index.js +6 -1
  211. package/dist/util/index.js.map +1 -1
  212. package/dist/{with-materialized-view-qtoJ3xKJ.d.ts → with-materialized-view-DIVeez0W.d.ts} +2 -2
  213. package/dist/{with-overlayed-view-DFaRfgMr.d.ts → with-overlayed-view-DVZrc5Hb.d.ts} +2 -2
  214. package/dist/with-rollup-CbU-O4wP.d.ts +47 -0
  215. package/dist/zod-HAJM7LMS.js +14763 -0
  216. package/dist/zod-HAJM7LMS.js.map +1 -0
  217. package/package.json +73 -191
  218. package/dist/aggregate/index.cjs.map +0 -1
  219. package/dist/aggregate/index.d.cts +0 -38
  220. package/dist/attestation/index.cjs +0 -305
  221. package/dist/attestation/index.cjs.map +0 -1
  222. package/dist/attestation/index.d.cts +0 -52
  223. package/dist/blobs/index.cjs +0 -1480
  224. package/dist/blobs/index.cjs.map +0 -1
  225. package/dist/blobs/index.d.cts +0 -46
  226. package/dist/bundle/index.cjs +0 -18561
  227. package/dist/bundle/index.cjs.map +0 -1
  228. package/dist/bundle/index.d.cts +0 -176
  229. package/dist/chunk-2EYC3WDT.js.map +0 -1
  230. package/dist/chunk-2XLVPKXG.js.map +0 -1
  231. package/dist/chunk-4OQWR46B.js.map +0 -1
  232. package/dist/chunk-4UBOTYP5.js +0 -1367
  233. package/dist/chunk-4UBOTYP5.js.map +0 -1
  234. package/dist/chunk-4X2S7PBF.js.map +0 -1
  235. package/dist/chunk-5YHWBPOT.js +0 -19
  236. package/dist/chunk-5YHWBPOT.js.map +0 -1
  237. package/dist/chunk-5ZGZ6HIZ.js.map +0 -1
  238. package/dist/chunk-74JEQFMT.js.map +0 -1
  239. package/dist/chunk-A6SWRXUQ.js +0 -118
  240. package/dist/chunk-A6SWRXUQ.js.map +0 -1
  241. package/dist/chunk-BFI3RS42.js.map +0 -1
  242. package/dist/chunk-EMEX37ZN.js +0 -51
  243. package/dist/chunk-EMEX37ZN.js.map +0 -1
  244. package/dist/chunk-EPK6A3WJ.js.map +0 -1
  245. package/dist/chunk-FBMXWVGP.js.map +0 -1
  246. package/dist/chunk-FXQYZNOW.js.map +0 -1
  247. package/dist/chunk-G7PAZ3TD.js.map +0 -1
  248. package/dist/chunk-GD3BGKAR.js.map +0 -1
  249. package/dist/chunk-GDTCGIPX.js.map +0 -1
  250. package/dist/chunk-GVXBHCZ2.js.map +0 -1
  251. package/dist/chunk-HGZ7DC5H.js.map +0 -1
  252. package/dist/chunk-IS5HWQO7.js.map +0 -1
  253. package/dist/chunk-KMI2NBBF.js.map +0 -1
  254. package/dist/chunk-KYKMKLJ6.js +0 -340
  255. package/dist/chunk-KYKMKLJ6.js.map +0 -1
  256. package/dist/chunk-LOL725S4.js.map +0 -1
  257. package/dist/chunk-MRIBLZL3.js +0 -86
  258. package/dist/chunk-MRIBLZL3.js.map +0 -1
  259. package/dist/chunk-NCO2JGKK.js.map +0 -1
  260. package/dist/chunk-NGSPBLLE.js.map +0 -1
  261. package/dist/chunk-P6256WTJ.js.map +0 -1
  262. package/dist/chunk-SAVQ6E2O.js.map +0 -1
  263. package/dist/chunk-T6HQMVML.js.map +0 -1
  264. package/dist/chunk-TLFUDXVV.js.map +0 -1
  265. package/dist/chunk-UMLVJTYV.js.map +0 -1
  266. package/dist/chunk-UOF74WQY.js.map +0 -1
  267. package/dist/chunk-WRLHNG6H.js.map +0 -1
  268. package/dist/chunk-YDLAFP36.js.map +0 -1
  269. package/dist/chunk-YL2DR3HY.js.map +0 -1
  270. package/dist/chunk-YMYK7US4.js.map +0 -1
  271. package/dist/chunk-ZC2AAE6J.js.map +0 -1
  272. package/dist/consent/index.cjs +0 -204
  273. package/dist/consent/index.cjs.map +0 -1
  274. package/dist/consent/index.d.cts +0 -25
  275. package/dist/crdt/index.cjs +0 -152
  276. package/dist/crdt/index.cjs.map +0 -1
  277. package/dist/crdt/index.d.cts +0 -30
  278. package/dist/derivations/index.cjs +0 -351
  279. package/dist/derivations/index.cjs.map +0 -1
  280. package/dist/derivations/index.d.cts +0 -72
  281. package/dist/dev-unlock-De3mjQWv.d.cts +0 -263
  282. package/dist/executor-BZKFZVRC.js +0 -8
  283. package/dist/executor-GFZFDQXV.js +0 -8
  284. package/dist/executor-KT2IOZVP.js +0 -11
  285. package/dist/fanout-sidecar-NRBWSLRK.js.map +0 -1
  286. package/dist/guards/index.cjs +0 -322
  287. package/dist/guards/index.cjs.map +0 -1
  288. package/dist/guards/index.d.cts +0 -31
  289. package/dist/hash-vBCB0-Ps.d.cts +0 -63
  290. package/dist/history/index.cjs +0 -1222
  291. package/dist/history/index.cjs.map +0 -1
  292. package/dist/history/index.d.cts +0 -63
  293. package/dist/i18n/index.cjs +0 -840
  294. package/dist/i18n/index.cjs.map +0 -1
  295. package/dist/i18n/index.d.cts +0 -39
  296. package/dist/index-DVkvrgpm.d.cts +0 -2290
  297. package/dist/index.cjs +0 -23590
  298. package/dist/index.cjs.map +0 -1
  299. package/dist/index.d.cts +0 -778
  300. package/dist/indexing/index.cjs +0 -738
  301. package/dist/indexing/index.cjs.map +0 -1
  302. package/dist/indexing/index.d.cts +0 -36
  303. package/dist/issue-BAJ7ZB4S.js +0 -12
  304. package/dist/lazy-builder-C-rPfWG0.d.cts +0 -304
  305. package/dist/materialized-views/index.cjs +0 -837
  306. package/dist/materialized-views/index.cjs.map +0 -1
  307. package/dist/materialized-views/index.d.cts +0 -184
  308. package/dist/mime-magic-CBBSOkjm.d.cts +0 -50
  309. package/dist/mime-magic-CBBSOkjm.d.ts +0 -50
  310. package/dist/noydb-XNQSKXGO.js +0 -34
  311. package/dist/overlay-views/index.cjs +0 -359
  312. package/dist/overlay-views/index.cjs.map +0 -1
  313. package/dist/overlay-views/index.d.cts +0 -82
  314. package/dist/periods/index.cjs +0 -1041
  315. package/dist/periods/index.cjs.map +0 -1
  316. package/dist/periods/index.d.cts +0 -22
  317. package/dist/predicate-Dnu81tsS.d.ts +0 -185
  318. package/dist/query/index.cjs.map +0 -1
  319. package/dist/query/index.d.cts +0 -3
  320. package/dist/read-only-facade-ITU6L7BL.js +0 -7
  321. package/dist/registry-2IEARCGT.js +0 -7
  322. package/dist/registry-CDHASH73.js +0 -10
  323. package/dist/registry-EMGLZGR6.js +0 -8
  324. package/dist/registry-NQALYR77.js +0 -8
  325. package/dist/revoke-7JOVLZFD.js +0 -17
  326. package/dist/session/index.cjs +0 -495
  327. package/dist/session/index.cjs.map +0 -1
  328. package/dist/session/index.d.cts +0 -46
  329. package/dist/shadow/index.cjs +0 -133
  330. package/dist/shadow/index.cjs.map +0 -1
  331. package/dist/shadow/index.d.cts +0 -17
  332. package/dist/store/index.cjs +0 -1083
  333. package/dist/store/index.cjs.map +0 -1
  334. package/dist/store/index.d.cts +0 -492
  335. package/dist/strategy-BSxFXGzb.d.cts +0 -110
  336. package/dist/strategy-DSTrsZ8t.d.cts +0 -601
  337. package/dist/sync/index.cjs +0 -1062
  338. package/dist/sync/index.cjs.map +0 -1
  339. package/dist/sync/index.d.cts +0 -43
  340. package/dist/team/index.cjs +0 -2798
  341. package/dist/team/index.cjs.map +0 -1
  342. package/dist/team/index.d.cts +0 -118
  343. package/dist/tx/index.cjs +0 -544
  344. package/dist/tx/index.cjs.map +0 -1
  345. package/dist/tx/index.d.cts +0 -21
  346. package/dist/types-CSLcfytP.d.cts +0 -12493
  347. package/dist/ulid-CG2YvAbg.d.cts +0 -603
  348. package/dist/util/index.cjs +0 -230
  349. package/dist/util/index.cjs.map +0 -1
  350. package/dist/util/index.d.cts +0 -77
  351. package/dist/with-derivation-Bzpj6UTv.d.ts +0 -13
  352. package/dist/with-derivation-DWajFh4K.d.cts +0 -13
  353. package/dist/with-guard-DF_Ul3DT.d.cts +0 -18
  354. package/dist/with-guard-DR7U-l4v.d.ts +0 -18
  355. package/dist/with-materialized-view-_piodoIz.d.cts +0 -27
  356. package/dist/with-overlayed-view-DwzCKxn2.d.cts +0 -13
  357. /package/dist/{crypto-H2Y3DDFW.js.map → adapter/index.js.map} +0 -0
  358. /package/dist/{chunk-75QDHSE4.js.map → chunk-2QDWRK3B.js.map} +0 -0
  359. /package/dist/{chunk-FCDO7UAO.js.map → chunk-2ZYIN3BL.js.map} +0 -0
  360. /package/dist/{chunk-ZUMGGHRB.js.map → chunk-5PTX7PKD.js.map} +0 -0
  361. /package/dist/{chunk-QAU5HM6Q.js.map → chunk-6EKRQ7QF.js.map} +0 -0
  362. /package/dist/{chunk-UVPGJXVO.js.map → chunk-JOJPBZSZ.js.map} +0 -0
  363. /package/dist/{chunk-YK72A4IT.js.map → chunk-LBILABKZ.js.map} +0 -0
  364. /package/dist/{chunk-GAUBWHAF.js.map → chunk-OCYFFBZS.js.map} +0 -0
  365. /package/dist/{delegation-QSC7G5QC.js.map → chunk-PZ5AY32C.js.map} +0 -0
  366. /package/dist/{chunk-FS7A4XNF.js.map → chunk-QWJCNXH2.js.map} +0 -0
  367. /package/dist/{chunk-K5PVGKE4.js.map → chunk-SAAJYSVV.js.map} +0 -0
  368. /package/dist/{chunk-LS3JLEIB.js.map → chunk-TZGWI5AX.js.map} +0 -0
  369. /package/dist/{executor-BZKFZVRC.js.map → crypto-OSIV2IIW.js.map} +0 -0
  370. /package/dist/{executor-GFZFDQXV.js.map → delegation-BQTRJATI.js.map} +0 -0
  371. /package/dist/{executor-KT2IOZVP.js.map → executor-KYSKFJ2R.js.map} +0 -0
  372. /package/dist/{issue-BAJ7ZB4S.js.map → executor-M7W3NEKR.js.map} +0 -0
  373. /package/dist/{ledger-WOEJUYTP.js.map → executor-VDTDGWO6.js.map} +0 -0
  374. /package/dist/{noydb-XNQSKXGO.js.map → forget/index.js.map} +0 -0
  375. /package/dist/{public-envelope-OHQ5UZFM.js.map → issue-DPHRF2TI.js.map} +0 -0
  376. /package/dist/{read-only-facade-ITU6L7BL.js.map → kernel/index.js.map} +0 -0
  377. /package/dist/{registry-2IEARCGT.js.map → ledger-CI7GSMLB.js.map} +0 -0
  378. /package/dist/{registry-CDHASH73.js.map → noydb-NAU2DTFP.js.map} +0 -0
  379. /package/dist/{registry-EMGLZGR6.js.map → public-envelope-MFMBFJLZ.js.map} +0 -0
  380. /package/dist/{registry-NQALYR77.js.map → read-only-facade-3OQRZ3VS.js.map} +0 -0
  381. /package/dist/{revoke-7JOVLZFD.js.map → registry-IX5WKK4F.js.map} +0 -0
  382. /package/dist/{signer-M4K5HBLD.js.map → registry-LMHO5265.js.map} +0 -0
  383. /package/dist/{stale-PAGCS4K5.js.map → registry-TXCKWH26.js.map} +0 -0
  384. /package/dist/{ulid-COREQ2RQ.js.map → registry-VFEDQSYV.js.map} +0 -0
@@ -1,32 +1,43 @@
1
1
  import {
2
2
  readPath
3
- } from "./chunk-MRIBLZL3.js";
3
+ } from "./chunk-KJ3K5VZM.js";
4
4
 
5
5
  // src/aggregate/reducers.ts
6
6
  function count(opts) {
7
7
  const _seed = opts?.seed;
8
8
  void _seed;
9
9
  return {
10
+ op: "count",
10
11
  init: () => 0,
11
12
  step: (state) => state + 1,
12
13
  remove: (state) => state - 1,
13
- finalize: (state) => state
14
+ finalize: (state) => state,
15
+ merge: (a, b) => a + b
14
16
  };
15
17
  }
16
18
  function sum(field, opts) {
17
19
  const _seed = opts?.seed;
18
20
  void _seed;
19
21
  return {
22
+ op: "sum",
23
+ field,
24
+ // Money-only metadata, read by `wrapMoneyReducers`. No effect on a
25
+ // generic numeric sum.
26
+ ...opts?.convertTo !== void 0 ? { convertTo: opts.convertTo } : {},
27
+ ...opts?.fx !== void 0 ? { fx: opts.fx } : {},
20
28
  init: () => 0,
21
29
  step: (state, record) => state + readNumber(record, field),
22
30
  remove: (state, record) => state - readNumber(record, field),
23
- finalize: (state) => state
31
+ finalize: (state) => state,
32
+ merge: (a, b) => a + b
24
33
  };
25
34
  }
26
35
  function avg(field, opts) {
27
36
  const _seed = opts?.seed;
28
37
  void _seed;
29
38
  return {
39
+ op: "avg",
40
+ field,
30
41
  init: () => ({ sum: 0, count: 0 }),
31
42
  step: (state, record) => ({
32
43
  sum: state.sum + readNumber(record, field),
@@ -36,7 +47,8 @@ function avg(field, opts) {
36
47
  sum: state.sum - readNumber(record, field),
37
48
  count: state.count - 1
38
49
  }),
39
- finalize: (state) => state.count === 0 ? null : state.sum / state.count
50
+ finalize: (state) => state.count === 0 ? null : state.sum / state.count,
51
+ merge: (a, b) => ({ sum: a.sum + b.sum, count: a.count + b.count })
40
52
  };
41
53
  }
42
54
  function pushValue(state, value) {
@@ -53,6 +65,8 @@ function min(field, opts) {
53
65
  const _seed = opts?.seed;
54
66
  void _seed;
55
67
  return {
68
+ op: "min",
69
+ field,
56
70
  init: () => ({ values: [] }),
57
71
  step: (state, record) => pushValue(state, readNumber(record, field)),
58
72
  remove: (state, record) => removeValue(state, readNumber(record, field)),
@@ -64,13 +78,16 @@ function min(field, opts) {
64
78
  if (v < out) out = v;
65
79
  }
66
80
  return out;
67
- }
81
+ },
82
+ merge: (a, b) => ({ values: [...a.values, ...b.values] })
68
83
  };
69
84
  }
70
85
  function max(field, opts) {
71
86
  const _seed = opts?.seed;
72
87
  void _seed;
73
88
  return {
89
+ op: "max",
90
+ field,
74
91
  init: () => ({ values: [] }),
75
92
  step: (state, record) => pushValue(state, readNumber(record, field)),
76
93
  remove: (state, record) => removeValue(state, readNumber(record, field)),
@@ -82,11 +99,17 @@ function max(field, opts) {
82
99
  if (v > out) out = v;
83
100
  }
84
101
  return out;
85
- }
102
+ },
103
+ merge: (a, b) => ({ values: [...a.values, ...b.values] })
86
104
  };
87
105
  }
88
106
  function readNumber(record, field) {
89
107
  const value = readPath(record, field);
108
+ if (typeof value === "object" && value !== null && "amount" in value && "currency" in value) {
109
+ throw new Error(
110
+ `aggregate: field "${field}" holds a money value but was not money-aware \u2014 declare it in the collection's moneyFields so sum/min/max stay exact`
111
+ );
112
+ }
90
113
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
91
114
  }
92
115
 
@@ -97,4 +120,4 @@ export {
97
120
  min,
98
121
  max
99
122
  };
100
- //# sourceMappingURL=chunk-5ZGZ6HIZ.js.map
123
+ //# sourceMappingURL=chunk-DEE4CRYN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/aggregate/reducers.ts"],"sourcesContent":["/**\n * Aggregation reducers for the query DSL.\n *\n * the reducer protocol plus five built-in factories\n * (`count`, `sum`, `avg`, `min`, `max`) consumed by `Query.aggregate()`\n * and, in the future, `Scan.aggregate()`. Every factory accepts\n * an optional `{ seed }` parameter that is plumbed through the\n * protocol but unused by the executor — that's the load-bearing\n * half of constraint #2. When partition-aware aggregation\n * lands, the seed carries the previous partition's running total into\n * the next partition without requiring a protocol change.\n *\n * Reducers are intentionally generic over their internal state type\n * `S` so compound reducers (avg keeps `{sum, count}`, min/max keep a\n * value bag) can model internal bookkeeping without leaking the\n * implementation through the accumulator's public shape. `finalize`\n * collapses `S` back into the user-visible `R`.\n *\n * Reducers are pure data — `init` / `step` / `finalize` / optional\n * `remove` are stateless functions that receive and return `S`. This\n * is the shape that admits O(1) incremental maintenance in a future\n * optimization (delta-aware `LiveAggregation` applies `step` or\n * `remove` per delta), without blocking the simpler \"full re-run on\n * source change\" that ships.\n */\n\nimport { readPath } from '../query/predicate.js'\n\n/**\n * A single reducer: factory-produced, ready to plug into an\n * `.aggregate()` spec.\n *\n * Type parameters:\n * - `R` — user-visible result type (what the aggregation returns\n * for this slot, e.g. `number` for `sum()`)\n * - `S` — internal state type, defaults to `R` for simple reducers\n * that don't need compound bookkeeping\n *\n * A reducer is stateless: every method is pure over `S`. `init()` is\n * called once per aggregation run to build the initial state; `step()`\n * folds a record into the state; `remove()` (optional) un-folds a\n * record, enabling incremental live maintenance; `finalize()` reads\n * the final answer out of the state at the end of the run.\n */\nexport interface Reducer<R, S = R> {\n /** Build the initial state for a fresh aggregation run. */\n init(): S\n /** Fold a record into the state. Returns the new state. */\n step(state: S, record: unknown): S\n /**\n * Un-fold a record from the state. Returns the new state.\n *\n * Optional — reducers without `remove` cannot be maintained\n * incrementally and must be re-run from scratch when the underlying\n * record set changes. `sum`, `count`, `avg` implement `remove` in\n * O(1); `min` and `max` implement it in O(N) worst case (when the\n * extremum itself is removed and the next extremum must be\n * recomputed from the remaining contributing values).\n */\n remove?(state: S, record: unknown): S\n /** Collapse the internal state into the user-visible result. */\n finalize(state: S): R\n /**\n * Combine two independent partial states into one (then `finalize` once).\n * Optional. MUST be associative + commutative with `init()` as identity.\n * Never merge finalized results — only states. Enables parallel /\n * hierarchical aggregation (e.g. cross-shard or advisor→firm rollup).\n */\n merge?(a: S, b: S): S\n /**\n * Identifying operation tag stamped by each built-in factory.\n * Used by `summariseAggregateOp` in the introspection walker to\n * render human-readable aggregate descriptors in `dumpSchema()`.\n * Optional so third-party custom reducers are unaffected.\n */\n readonly op?: 'count' | 'sum' | 'avg' | 'min' | 'max'\n /**\n * Field name for field-based reducers (`sum`, `avg`, `min`, `max`).\n * Absent on `count` which aggregates over record count, not a field.\n */\n readonly field?: string\n /**\n * Money-only: target currency for `sum` over a multi-currency money\n * field. Consumed by `wrapMoneyReducers` to convert per-currency\n * subtotals to one figure. Ignored for non-money fields.\n */\n readonly convertTo?: string\n /**\n * Money-only: FX rate map (`'USD->EUR' → rate`) used with `convertTo`.\n */\n readonly fx?: Record<string, number | string>\n}\n\n/**\n * Common options accepted by every reducer factory.\n *\n * `seed` — optional initial value for the internal state. **Unused by\n * the executor**, plumbed through the protocol for constraint\n * #2 (partition-aware aggregation seam). In, partitioned\n * aggregations will pass the previous partition's carry as `seed` so\n * a long time series can be rolled forward one partition at a time\n * without re-aggregating closed partitions.\n *\n * always uses `init()` with the factory's zero value, regardless\n * of whether `seed` was passed. Do not remove the parameter — that's\n * the whole point of having it exist now.\n */\nexport interface ReducerOptions<TSeed = unknown> {\n /** constraint #2 — seed is plumbed through but unused in. */\n readonly seed?: TSeed\n /**\n * Money-only (honored by `sum` over a multi-currency money field):\n * convert per-currency subtotals to this currency for a single figure.\n */\n readonly convertTo?: string\n /** Money-only: FX rate map (`'USD->EUR' → rate`) used with `convertTo`. */\n readonly fx?: Record<string, number | string>\n}\n\n// ---------------------------------------------------------------------------\n// Factories\n// ---------------------------------------------------------------------------\n\n/**\n * Count the number of records that match the query. Ignores field\n * values entirely — the count is over the number of records, not over\n * the number of non-null field values in any column.\n */\nexport function count(opts?: ReducerOptions<number>): Reducer<number> {\n // Seed captured on the closure but unused at execution time in\n //. The reference in _seed keeps lint happy.\n const _seed = opts?.seed\n void _seed\n return {\n op: 'count',\n init: () => 0,\n step: (state) => state + 1,\n remove: (state) => state - 1,\n finalize: (state) => state,\n merge: (a, b) => a + b,\n }\n}\n\n/**\n * Sum a numeric field across all matching records. Non-number values\n * at the field path are coerced to 0 — consumers who want a different\n * behavior (throw, skip, treat as NaN) should filter upstream via\n * `.where()` or write a custom reducer.\n */\nexport function sum(\n field: string,\n opts?: ReducerOptions<number>,\n): Reducer<number> {\n const _seed = opts?.seed\n void _seed\n return {\n op: 'sum',\n field,\n // Money-only metadata, read by `wrapMoneyReducers`. No effect on a\n // generic numeric sum.\n ...(opts?.convertTo !== undefined ? { convertTo: opts.convertTo } : {}),\n ...(opts?.fx !== undefined ? { fx: opts.fx } : {}),\n init: () => 0,\n step: (state, record) => state + readNumber(record, field),\n remove: (state, record) => state - readNumber(record, field),\n finalize: (state) => state,\n merge: (a, b) => a + b,\n }\n}\n\n/**\n * Arithmetic mean of a numeric field across all matching records.\n *\n * Returns `null` for an empty result set (zero records is not a\n * well-defined denominator — returning NaN would poison downstream\n * arithmetic, and throwing would force every consumer to wrap in\n * try/catch just to handle \"no matches\"). Consumers who want an\n * explicit zero should coalesce with `?? 0`.\n *\n * Internal state is `{sum, count}` so the running average can be\n * maintained incrementally — on each delta, both fields update in\n * O(1) and `finalize` divides. Directly storing `avg` as state would\n * not admit incremental removal without also tracking count.\n */\nexport function avg(\n field: string,\n opts?: ReducerOptions<{ sum: number; count: number }>,\n): Reducer<number | null, { sum: number; count: number }> {\n const _seed = opts?.seed\n void _seed\n return {\n op: 'avg',\n field,\n init: () => ({ sum: 0, count: 0 }),\n step: (state, record) => ({\n sum: state.sum + readNumber(record, field),\n count: state.count + 1,\n }),\n remove: (state, record) => ({\n sum: state.sum - readNumber(record, field),\n count: state.count - 1,\n }),\n finalize: (state) => (state.count === 0 ? null : state.sum / state.count),\n merge: (a, b) => ({ sum: a.sum + b.sum, count: a.count + b.count }),\n }\n}\n\ninterface MinMaxState {\n /**\n * Multiset of contributing field values. Stored as a plain array\n * because we need to support `remove` and a plain array gives us\n * O(1) push + O(N) worst-case removal — which matches the\n * documented min/max removal complexity. A sorted structure would\n * let us drop the O(N) rescan but adds complexity that doesn't\n * need; consumers hitting the O(N) ceiling should file an issue.\n */\n readonly values: number[]\n}\n\nfunction pushValue(state: MinMaxState, value: number): MinMaxState {\n return { values: [...state.values, value] }\n}\n\nfunction removeValue(state: MinMaxState, value: number): MinMaxState {\n // Remove the first matching value — duplicates are fine, we only\n // need to drop one instance per `remove()` call so the multiset\n // count stays consistent with the record count.\n const idx = state.values.indexOf(value)\n if (idx < 0) return state\n const next = state.values.slice()\n next.splice(idx, 1)\n return { values: next }\n}\n\n/**\n * Smallest numeric value of a field across all matching records.\n * Returns `null` for an empty result set. See `avg()` for the\n * reasoning on `null` vs NaN vs throwing.\n *\n * Incremental complexity: O(1) for `step`, O(N) worst case for\n * `remove` when the current minimum is removed (the state holds the\n * full multiset of contributing values and `finalize` scans for the\n * new minimum). Consumers with very large result sets and frequent\n * removals of the current extremum should either accept the cost or\n * wait for a future optimization.\n */\nexport function min(\n field: string,\n opts?: ReducerOptions<number>,\n): Reducer<number | null, MinMaxState> {\n const _seed = opts?.seed\n void _seed\n return {\n op: 'min',\n field,\n init: () => ({ values: [] }),\n step: (state, record) => pushValue(state, readNumber(record, field)),\n remove: (state, record) => removeValue(state, readNumber(record, field)),\n finalize: (state) => {\n if (state.values.length === 0) return null\n let out = state.values[0]!\n for (let i = 1; i < state.values.length; i++) {\n const v = state.values[i]!\n if (v < out) out = v\n }\n return out\n },\n merge: (a, b) => ({ values: [...a.values, ...b.values] }),\n }\n}\n\n/**\n * Largest numeric value of a field across all matching records.\n * Mirror of `min()` — see that doc for semantics, null-on-empty\n * behavior, and the O(N) removal caveat.\n */\nexport function max(\n field: string,\n opts?: ReducerOptions<number>,\n): Reducer<number | null, MinMaxState> {\n const _seed = opts?.seed\n void _seed\n return {\n op: 'max',\n field,\n init: () => ({ values: [] }),\n step: (state, record) => pushValue(state, readNumber(record, field)),\n remove: (state, record) => removeValue(state, readNumber(record, field)),\n finalize: (state) => {\n if (state.values.length === 0) return null\n let out = state.values[0]!\n for (let i = 1; i < state.values.length; i++) {\n const v = state.values[i]!\n if (v > out) out = v\n }\n return out\n },\n merge: (a, b) => ({ values: [...a.values, ...b.values] }),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read a numeric field from a record. Non-number values (null,\n * undefined, strings, objects) coerce to 0 so sum/avg/min/max don't\n * produce NaN on one bad row. Consumers who want strict typing should\n * validate upstream with Standard Schema, which NOYDB already runs on\n * every `put()`.\n */\nfunction readNumber(record: unknown, field: string): number {\n const value = readPath(record, field)\n // Defensive: a `{ amount, currency }` value means a multi-currency\n // money field reached a generic numeric reducer instead of being\n // rewritten by `wrapMoneyReducers` — that would silently produce a\n // wrong total. Surface it loudly rather than coercing to 0.\n if (\n typeof value === 'object' &&\n value !== null &&\n 'amount' in value &&\n 'currency' in value\n ) {\n throw new Error(\n `aggregate: field \"${field}\" holds a money value but was not money-aware — ` +\n `declare it in the collection's moneyFields so sum/min/max stay exact`,\n )\n }\n return typeof value === 'number' && Number.isFinite(value) ? value : 0\n}\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@ import {
2
2
  DecryptionError,
3
3
  InvalidKeyError,
4
4
  TamperedError
5
- } from "./chunk-YDLAFP36.js";
5
+ } from "./chunk-JZ5DS4DP.js";
6
6
 
7
7
  // src/crypto.ts
8
8
  var PBKDF2_ITERATIONS = 6e5;
@@ -58,6 +58,39 @@ async function unwrapKey(wrappedBase64, kek) {
58
58
  throw new InvalidKeyError();
59
59
  }
60
60
  }
61
+ async function asKwKey(dek) {
62
+ const rawDek = await subtle.exportKey("raw", dek);
63
+ const hkdfKey = await subtle.importKey("raw", rawDek, "HKDF", false, ["deriveBits"]);
64
+ const salt = new TextEncoder().encode("noydb-cek-wrap");
65
+ const info = new TextEncoder().encode("v1");
66
+ const bits = await subtle.deriveBits(
67
+ { name: "HKDF", hash: "SHA-256", salt, info },
68
+ hkdfKey,
69
+ KEY_BITS
70
+ );
71
+ return subtle.importKey("raw", bits, "AES-KW", false, ["wrapKey", "unwrapKey"]);
72
+ }
73
+ async function wrapCek(cek, dek) {
74
+ const kw = await asKwKey(dek);
75
+ const wrapped = await subtle.wrapKey("raw", cek, kw, "AES-KW");
76
+ return bufferToBase64(wrapped);
77
+ }
78
+ async function unwrapCek(wrappedBase64, dek) {
79
+ const kw = await asKwKey(dek);
80
+ try {
81
+ return await subtle.unwrapKey(
82
+ "raw",
83
+ base64ToBuffer(wrappedBase64),
84
+ kw,
85
+ "AES-KW",
86
+ { name: "AES-GCM", length: KEY_BITS },
87
+ true,
88
+ ["encrypt", "decrypt"]
89
+ );
90
+ } catch {
91
+ throw new InvalidKeyError();
92
+ }
93
+ }
61
94
  async function encrypt(plaintext, dek) {
62
95
  const iv = generateIV();
63
96
  const encoded = new TextEncoder().encode(plaintext);
@@ -256,6 +289,8 @@ export {
256
289
  generateDEK,
257
290
  wrapKey,
258
291
  unwrapKey,
292
+ wrapCek,
293
+ unwrapCek,
259
294
  encrypt,
260
295
  decrypt,
261
296
  encryptBytes,
@@ -272,4 +307,4 @@ export {
272
307
  bufferToBase64,
273
308
  base64ToBuffer
274
309
  };
275
- //# sourceMappingURL=chunk-UOF74WQY.js.map
310
+ //# sourceMappingURL=chunk-E2MDPPOC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/crypto.ts"],"sourcesContent":["/**\n * Cryptographic primitives — thin wrappers around the Web Crypto API.\n *\n * ## Design principle\n *\n * **Zero npm crypto dependencies.** Every operation uses `globalThis.crypto.subtle`,\n * which is available natively in Node.js ≥ 18, all modern browsers, and\n * Deno/Bun. This avoids supply-chain risk from third-party crypto packages and\n * ensures the library stays auditable.\n *\n * ## Algorithms\n *\n * | Use case | Algorithm | Parameters |\n * |----------|-----------|------------|\n * | Key derivation | PBKDF2-SHA256 | 600,000 iterations, 32-byte salt |\n * | Record encryption | AES-256-GCM | 12-byte random IV per operation |\n * | DEK wrapping | AES-KW (RFC 3394) | 256-bit KEK |\n * | Binary encrypt | AES-256-GCM | same as record encryption |\n * | Integrity | HMAC-SHA256 | for presence channels |\n * | Content hash | SHA-256 | for ledger and bundle integrity |\n *\n * ## Key lifecycle\n *\n * ```\n * passphrase + salt\n * └─► deriveKey() → KEK (CryptoKey, extractable: false)\n * └─► wrapKey() → wrapped DEK bytes [stored in keyring]\n * └─► unwrapKey() → DEK (CryptoKey) [memory only during session]\n * └─► encrypt() / decrypt() → ciphertext / plaintext\n * ```\n *\n * IVs are generated fresh by {@link generateIV} on every encrypt call.\n * Reusing an IV with the same key would break GCM's authentication guarantee —\n * this function should be the only place IVs are produced.\n *\n * @module\n */\n\nimport { DecryptionError, InvalidKeyError, TamperedError } from './errors.js'\n\nconst PBKDF2_ITERATIONS = 600_000\nconst SALT_BYTES = 32\nconst IV_BYTES = 12\nconst KEY_BITS = 256\n\nconst subtle = globalThis.crypto.subtle\n\n// ─── Key Derivation ────────────────────────────────────────────────────\n\n/** Derive a KEK from a passphrase and salt using PBKDF2-SHA256. */\nexport async function deriveKey(\n passphrase: string,\n salt: Uint8Array,\n): Promise<CryptoKey> {\n const keyMaterial = await subtle.importKey(\n 'raw',\n new TextEncoder().encode(passphrase),\n 'PBKDF2',\n false,\n ['deriveKey'],\n )\n\n return subtle.deriveKey(\n {\n name: 'PBKDF2',\n salt: salt as BufferSource,\n iterations: PBKDF2_ITERATIONS,\n hash: 'SHA-256',\n },\n keyMaterial,\n { name: 'AES-KW', length: KEY_BITS },\n false,\n ['wrapKey', 'unwrapKey'],\n )\n}\n\n// ─── DEK Generation ────────────────────────────────────────────────────\n\n/** Generate a random AES-256-GCM data encryption key. */\nexport async function generateDEK(): Promise<CryptoKey> {\n return subtle.generateKey(\n { name: 'AES-GCM', length: KEY_BITS },\n true, // extractable — needed for AES-KW wrapping\n ['encrypt', 'decrypt'],\n )\n}\n\n// ─── Key Wrapping ──────────────────────────────────────────────────────\n\n/** Wrap (encrypt) a DEK with a KEK using AES-KW. Returns base64 string. */\nexport async function wrapKey(dek: CryptoKey, kek: CryptoKey): Promise<string> {\n const wrapped = await subtle.wrapKey('raw', dek, kek, 'AES-KW')\n return bufferToBase64(wrapped)\n}\n\n/** Unwrap (decrypt) a DEK from base64 string using a KEK. */\nexport async function unwrapKey(\n wrappedBase64: string,\n kek: CryptoKey,\n): Promise<CryptoKey> {\n try {\n return await subtle.unwrapKey(\n 'raw',\n base64ToBuffer(wrappedBase64) as BufferSource,\n kek,\n 'AES-KW',\n { name: 'AES-GCM', length: KEY_BITS },\n true,\n ['encrypt', 'decrypt'],\n )\n } catch {\n throw new InvalidKeyError()\n }\n}\n\n// ─── Per-record CEK wrapping ───────────────────────────────────────────\n\n/**\n * Derive a **dedicated** AES-KW wrapping key from a collection/tier DEK via\n * HKDF-SHA256, used to wrap/unwrap a per-record CEK.\n *\n * The collection/tier DEKs are AES-256-**GCM** keys (usages\n * `encrypt`/`decrypt`) and cannot themselves act as an AES-KW KEK. We do NOT\n * re-import the raw DEK bytes directly as an AES-KW key — that would reuse one\n * key across two primitives (AES-GCM for `_det`/presence, AES-KW for CEK\n * wrapping), violating key separation. Instead we HKDF-derive a domain-\n * separated KW key (salt `noydb-cek-wrap`), exactly as `derivePresenceKey`\n * derives a separate presence key. The derivation is deterministic, so\n * wrapping the same CEK under the same DEK always yields identical ciphertext\n * — which is what lets an update reuse a record's stable CEK and produce a\n * byte-identical `_cek`. The KW key is independent of the DEK's GCM key.\n *\n * The DEK must be extractable (it is — `generateDEK`/`unwrapKey` mint\n * extractable keys).\n */\nasync function asKwKey(dek: CryptoKey): Promise<CryptoKey> {\n const rawDek = await subtle.exportKey('raw', dek)\n const hkdfKey = await subtle.importKey('raw', rawDek, 'HKDF', false, ['deriveBits'])\n const salt = new TextEncoder().encode('noydb-cek-wrap')\n const info = new TextEncoder().encode('v1')\n const bits = await subtle.deriveBits(\n { name: 'HKDF', hash: 'SHA-256', salt, info },\n hkdfKey,\n KEY_BITS,\n )\n return subtle.importKey('raw', bits, 'AES-KW', false, ['wrapKey', 'unwrapKey'])\n}\n\n/**\n * AES-KW-wrap a per-record CEK under a collection/tier DEK. Returns base64.\n * Deterministic over `(cek, dek)`.\n */\nexport async function wrapCek(cek: CryptoKey, dek: CryptoKey): Promise<string> {\n const kw = await asKwKey(dek)\n const wrapped = await subtle.wrapKey('raw', cek, kw, 'AES-KW')\n return bufferToBase64(wrapped)\n}\n\n/**\n * Unwrap a per-record CEK from base64 under a collection/tier DEK. Throws\n * `InvalidKeyError` if the wrapped bytes do not authenticate under the DEK.\n */\nexport async function unwrapCek(wrappedBase64: string, dek: CryptoKey): Promise<CryptoKey> {\n const kw = await asKwKey(dek)\n try {\n return await subtle.unwrapKey(\n 'raw',\n base64ToBuffer(wrappedBase64) as BufferSource,\n kw,\n 'AES-KW',\n { name: 'AES-GCM', length: KEY_BITS },\n true,\n ['encrypt', 'decrypt'],\n )\n } catch {\n throw new InvalidKeyError()\n }\n}\n\n// ─── Encrypt / Decrypt ─────────────────────────────────────────────────\n\nexport interface EncryptResult {\n iv: string // base64\n data: string // base64\n}\n\n/** Encrypt plaintext JSON string with AES-256-GCM. Fresh IV per call. */\nexport async function encrypt(\n plaintext: string,\n dek: CryptoKey,\n): Promise<EncryptResult> {\n const iv = generateIV()\n const encoded = new TextEncoder().encode(plaintext)\n\n const ciphertext = await subtle.encrypt(\n { name: 'AES-GCM', iv: iv as BufferSource },\n dek,\n encoded,\n )\n\n return {\n iv: bufferToBase64(iv),\n data: bufferToBase64(ciphertext),\n }\n}\n\n/** Decrypt AES-256-GCM ciphertext. Throws on wrong key or tampered data. */\nexport async function decrypt(\n ivBase64: string,\n dataBase64: string,\n dek: CryptoKey,\n): Promise<string> {\n const iv = base64ToBuffer(ivBase64)\n const ciphertext = base64ToBuffer(dataBase64)\n\n try {\n const plaintext = await subtle.decrypt(\n { name: 'AES-GCM', iv: iv as BufferSource },\n dek,\n ciphertext as BufferSource,\n )\n return new TextDecoder().decode(plaintext)\n } catch (err) {\n if (err instanceof Error && err.name === 'OperationError') {\n throw new TamperedError()\n }\n throw new DecryptionError(\n err instanceof Error ? err.message : 'Decryption failed',\n )\n }\n}\n\n// ─── Binary Encrypt / Decrypt ────────\n\n/**\n * Encrypt raw bytes with AES-256-GCM using a fresh random IV.\n * Used by the attachment store so binary blobs avoid double base64 encoding\n * (the existing `encrypt()` function calls `TextEncoder` on a string — here\n * we pass the `Uint8Array` directly to `subtle.encrypt`).\n */\nexport async function encryptBytes(\n data: Uint8Array,\n dek: CryptoKey,\n): Promise<EncryptResult> {\n const iv = generateIV()\n const ciphertext = await subtle.encrypt(\n { name: 'AES-GCM', iv: iv as BufferSource },\n dek,\n data as unknown as BufferSource,\n )\n return {\n iv: bufferToBase64(iv),\n data: bufferToBase64(ciphertext),\n }\n}\n\n/**\n * Decrypt AES-256-GCM ciphertext back to raw bytes.\n * Counterpart to `encryptBytes`. Throws `TamperedError` on auth-tag failure.\n */\nexport async function decryptBytes(\n ivBase64: string,\n dataBase64: string,\n dek: CryptoKey,\n): Promise<Uint8Array> {\n const iv = base64ToBuffer(ivBase64)\n const ciphertext = base64ToBuffer(dataBase64)\n try {\n const plaintext = await subtle.decrypt(\n { name: 'AES-GCM', iv: iv as BufferSource },\n dek,\n ciphertext as BufferSource,\n )\n return new Uint8Array(plaintext)\n } catch (err) {\n if (err instanceof Error && err.name === 'OperationError') {\n throw new TamperedError()\n }\n throw new DecryptionError(\n err instanceof Error ? err.message : 'Decryption failed',\n )\n }\n}\n\n/**\n * SHA-256 hex digest of raw bytes. Used to derive content-addressed\n * eTags for blob deduplication. Computed on plaintext bytes\n * before compression and encryption so the eTag identifies content, not\n * ciphertext, and survives re-encryption (key rotation, re-upload).\n */\nexport async function sha256Hex(data: Uint8Array): Promise<string> {\n const hash = await subtle.digest('SHA-256', data as unknown as BufferSource)\n return Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n// ─── HMAC-SHA-256 ─────────────────────────────\n\n/**\n * Compute HMAC-SHA-256(key, data) and return hex string.\n *\n * Used to derive content-addressed eTags that are opaque to the store:\n * ```\n * eTag = hmacSha256Hex(blobDEK, plaintext)\n * ```\n *\n * Unlike a plain SHA-256, the HMAC is keyed by the vault-shared `_blob` DEK,\n * so an attacker with store access cannot pre-compute eTags for known files.\n * Deduplication still works within a vault (same key + same content = same eTag).\n */\nexport async function hmacSha256Hex(key: CryptoKey, data: Uint8Array): Promise<string> {\n // Export AES-GCM DEK raw bytes → import as HMAC key\n const rawKey = await subtle.exportKey('raw', key)\n const hmacKey = await subtle.importKey(\n 'raw',\n rawKey,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n const sig = await subtle.sign('HMAC', hmacKey, data as unknown as BufferSource)\n return Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n// ─── AAD-aware Binary Encrypt / Decrypt ──\n\n/**\n * Encrypt raw bytes with AES-256-GCM using Additional Authenticated Data.\n *\n * The AAD binds each chunk to its parent blob and position, preventing\n * chunk reorder, substitution, and truncation attacks:\n * ```\n * AAD = UTF-8(\"{eTag}:{chunkIndex}:{chunkCount}\")\n * ```\n *\n * The AAD is NOT stored — the reader reconstructs it from `BlobObject`\n * metadata and passes it to `decryptBytesWithAAD`.\n */\nexport async function encryptBytesWithAAD(\n data: Uint8Array,\n dek: CryptoKey,\n aad: Uint8Array,\n): Promise<EncryptResult> {\n const iv = generateIV()\n const ciphertext = await subtle.encrypt(\n {\n name: 'AES-GCM',\n iv: iv as BufferSource,\n additionalData: aad as BufferSource,\n },\n dek,\n data as unknown as BufferSource,\n )\n return {\n iv: bufferToBase64(iv),\n data: bufferToBase64(ciphertext),\n }\n}\n\n/**\n * Decrypt AES-256-GCM ciphertext with AAD verification.\n *\n * If the AAD does not match the one used at encryption time (e.g. because\n * a chunk was reordered or substituted from another blob), the GCM auth\n * tag fails and this throws `TamperedError`.\n */\nexport async function decryptBytesWithAAD(\n ivBase64: string,\n dataBase64: string,\n dek: CryptoKey,\n aad: Uint8Array,\n): Promise<Uint8Array> {\n const iv = base64ToBuffer(ivBase64)\n const ciphertext = base64ToBuffer(dataBase64)\n try {\n const plaintext = await subtle.decrypt(\n {\n name: 'AES-GCM',\n iv: iv as BufferSource,\n additionalData: aad as BufferSource,\n },\n dek,\n ciphertext as BufferSource,\n )\n return new Uint8Array(plaintext)\n } catch (err) {\n if (err instanceof Error && err.name === 'OperationError') {\n throw new TamperedError()\n }\n throw new DecryptionError(\n err instanceof Error ? err.message : 'Decryption failed',\n )\n }\n}\n\n// ─── Presence Key Derivation ──────────────────────────────\n\n/**\n * Derive an AES-256-GCM presence key from a collection DEK using HKDF-SHA256.\n *\n * The presence key is domain-separated from the data DEK by the fixed salt\n * `'noydb-presence'` and the `info` = collection name. This means:\n * - The adapter never sees the presence key.\n * - Presence payloads rotate automatically when the collection DEK is rotated.\n * - Revoked users cannot derive the new presence key after a DEK rotation.\n *\n * @param dek The collection's AES-256-GCM DEK (extractable).\n * @param collectionName Used as the HKDF `info` parameter for domain separation.\n * @returns A non-extractable AES-256-GCM key suitable for presence payload encryption.\n */\nexport async function derivePresenceKey(dek: CryptoKey, collectionName: string): Promise<CryptoKey> {\n // Step 1: export DEK raw bytes\n const rawDek = await subtle.exportKey('raw', dek)\n\n // Step 2: import as HKDF key material\n const hkdfKey = await subtle.importKey(\n 'raw',\n rawDek,\n 'HKDF',\n false,\n ['deriveBits'],\n )\n\n // Step 3: derive 256 bits with salt='noydb-presence' and info=collectionName\n const salt = new TextEncoder().encode('noydb-presence')\n const info = new TextEncoder().encode(collectionName)\n const bits = await subtle.deriveBits(\n { name: 'HKDF', hash: 'SHA-256', salt, info },\n hkdfKey,\n KEY_BITS,\n )\n\n // Step 4: import derived bits as AES-GCM key\n return subtle.importKey(\n 'raw',\n bits,\n { name: 'AES-GCM', length: KEY_BITS },\n false,\n ['encrypt', 'decrypt'],\n )\n}\n\n// ─── Deterministic Encryption ────────────────────────────\n\n/**\n * Derive a deterministic 12-byte IV from `{ DEK, context, plaintext }`\n * via HKDF-SHA256. Given the same three inputs, the IV is identical, so\n * `encryptDeterministic` produces the same ciphertext on every call —\n * which is precisely what enables blind equality search on encrypted\n * fields.\n *\n * **The side channel this opens.** Two records whose field value is the\n * same produce the same ciphertext. An observer with store access can\n * therefore tell which records share a value — not *what* the value is,\n * but the equivalence class. This is the well-known trade-off of\n * deterministic encryption and is why the feature is strictly opt-in\n * per field, guarded by `acknowledgeDeterministicRisk: true` at\n * collection creation.\n *\n * The context string MUST include the collection name and field name,\n * so:\n * - The same plaintext in two different fields encrypts differently\n * (no cross-field equality leak).\n * - The same plaintext in two different collections (different DEKs)\n * encrypts differently by virtue of the key, even before HKDF\n * domain separation kicks in.\n */\nasync function deriveDeterministicIV(\n dek: CryptoKey,\n context: string,\n plaintext: string,\n): Promise<Uint8Array> {\n const rawDek = await subtle.exportKey('raw', dek)\n const hkdfKey = await subtle.importKey('raw', rawDek, 'HKDF', false, ['deriveBits'])\n const salt = new TextEncoder().encode('noydb-deterministic-v1')\n const info = new TextEncoder().encode(`${context}\\x00${plaintext}`)\n const bits = await subtle.deriveBits(\n { name: 'HKDF', hash: 'SHA-256', salt, info },\n hkdfKey,\n IV_BYTES * 8,\n )\n return new Uint8Array(bits)\n}\n\n/**\n * Encrypt a plaintext string with AES-256-GCM and a deterministic,\n * HKDF-derived IV.\n *\n * The same `{ dek, context, plaintext }` triple always produces the\n * same `{ iv, data }` — call this twice and you can string-compare the\n * ciphertexts to check equality of the inputs without decrypting them.\n *\n * @param context Domain-separation string — by convention\n * `'<collection>/<field>'`. Different contexts encrypt\n * the same plaintext to different ciphertexts, so\n * `email` in collection `users` does not collide with\n * `email` in collection `customers`.\n */\nexport async function encryptDeterministic(\n plaintext: string,\n dek: CryptoKey,\n context: string,\n): Promise<EncryptResult> {\n const iv = await deriveDeterministicIV(dek, context, plaintext)\n const encoded = new TextEncoder().encode(plaintext)\n const ciphertext = await subtle.encrypt(\n { name: 'AES-GCM', iv: iv as BufferSource },\n dek,\n encoded,\n )\n return {\n iv: bufferToBase64(iv),\n data: bufferToBase64(ciphertext),\n }\n}\n\n/**\n * Counterpart to {@link encryptDeterministic}. The IV is stored\n * alongside the ciphertext (exactly like the randomized path), so\n * decrypt uses the stored IV and verifies the GCM auth tag — a tampered\n * ciphertext throws `TamperedError` just like randomized AES-GCM.\n */\nexport async function decryptDeterministic(\n ivBase64: string,\n dataBase64: string,\n dek: CryptoKey,\n): Promise<string> {\n return decrypt(ivBase64, dataBase64, dek)\n}\n\n// ─── Random Generation ─────────────────────────────────────────────────\n\n/** Generate a random 12-byte IV for AES-GCM. */\nexport function generateIV(): Uint8Array {\n return globalThis.crypto.getRandomValues(new Uint8Array(IV_BYTES))\n}\n\n/** Generate a random 32-byte salt for PBKDF2. */\nexport function generateSalt(): Uint8Array {\n return globalThis.crypto.getRandomValues(new Uint8Array(SALT_BYTES))\n}\n\n// ─── Base64 Helpers ────────────────────────────────────────────────────\n\nexport function bufferToBase64(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer)\n let binary = ''\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]!)\n }\n return btoa(binary)\n}\n\nexport function base64ToBuffer(base64: string): Uint8Array<ArrayBuffer> {\n const binary = atob(base64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n}\n"],"mappings":";;;;;;;AAwCA,IAAM,oBAAoB;AAC1B,IAAM,aAAa;AACnB,IAAM,WAAW;AACjB,IAAM,WAAW;AAEjB,IAAM,SAAS,WAAW,OAAO;AAKjC,eAAsB,UACpB,YACA,MACoB;AACpB,QAAM,cAAc,MAAM,OAAO;AAAA,IAC/B;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,UAAU;AAAA,IACnC;AAAA,IACA;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,SAAO,OAAO;AAAA,IACZ;AAAA,MACE,MAAM;AAAA,MACN;AAAA,MACA,YAAY;AAAA,MACZ,MAAM;AAAA,IACR;AAAA,IACA;AAAA,IACA,EAAE,MAAM,UAAU,QAAQ,SAAS;AAAA,IACnC;AAAA,IACA,CAAC,WAAW,WAAW;AAAA,EACzB;AACF;AAKA,eAAsB,cAAkC;AACtD,SAAO,OAAO;AAAA,IACZ,EAAE,MAAM,WAAW,QAAQ,SAAS;AAAA,IACpC;AAAA;AAAA,IACA,CAAC,WAAW,SAAS;AAAA,EACvB;AACF;AAKA,eAAsB,QAAQ,KAAgB,KAAiC;AAC7E,QAAM,UAAU,MAAM,OAAO,QAAQ,OAAO,KAAK,KAAK,QAAQ;AAC9D,SAAO,eAAe,OAAO;AAC/B;AAGA,eAAsB,UACpB,eACA,KACoB;AACpB,MAAI;AACF,WAAO,MAAM,OAAO;AAAA,MAClB;AAAA,MACA,eAAe,aAAa;AAAA,MAC5B;AAAA,MACA;AAAA,MACA,EAAE,MAAM,WAAW,QAAQ,SAAS;AAAA,MACpC;AAAA,MACA,CAAC,WAAW,SAAS;AAAA,IACvB;AAAA,EACF,QAAQ;AACN,UAAM,IAAI,gBAAgB;AAAA,EAC5B;AACF;AAsBA,eAAe,QAAQ,KAAoC;AACzD,QAAM,SAAS,MAAM,OAAO,UAAU,OAAO,GAAG;AAChD,QAAM,UAAU,MAAM,OAAO,UAAU,OAAO,QAAQ,QAAQ,OAAO,CAAC,YAAY,CAAC;AACnF,QAAM,OAAO,IAAI,YAAY,EAAE,OAAO,gBAAgB;AACtD,QAAM,OAAO,IAAI,YAAY,EAAE,OAAO,IAAI;AAC1C,QAAM,OAAO,MAAM,OAAO;AAAA,IACxB,EAAE,MAAM,QAAQ,MAAM,WAAW,MAAM,KAAK;AAAA,IAC5C;AAAA,IACA;AAAA,EACF;AACA,SAAO,OAAO,UAAU,OAAO,MAAM,UAAU,OAAO,CAAC,WAAW,WAAW,CAAC;AAChF;AAMA,eAAsB,QAAQ,KAAgB,KAAiC;AAC7E,QAAM,KAAK,MAAM,QAAQ,GAAG;AAC5B,QAAM,UAAU,MAAM,OAAO,QAAQ,OAAO,KAAK,IAAI,QAAQ;AAC7D,SAAO,eAAe,OAAO;AAC/B;AAMA,eAAsB,UAAU,eAAuB,KAAoC;AACzF,QAAM,KAAK,MAAM,QAAQ,GAAG;AAC5B,MAAI;AACF,WAAO,MAAM,OAAO;AAAA,MAClB;AAAA,MACA,eAAe,aAAa;AAAA,MAC5B;AAAA,MACA;AAAA,MACA,EAAE,MAAM,WAAW,QAAQ,SAAS;AAAA,MACpC;AAAA,MACA,CAAC,WAAW,SAAS;AAAA,IACvB;AAAA,EACF,QAAQ;AACN,UAAM,IAAI,gBAAgB;AAAA,EAC5B;AACF;AAUA,eAAsB,QACpB,WACA,KACwB;AACxB,QAAM,KAAK,WAAW;AACtB,QAAM,UAAU,IAAI,YAAY,EAAE,OAAO,SAAS;AAElD,QAAM,aAAa,MAAM,OAAO;AAAA,IAC9B,EAAE,MAAM,WAAW,GAAuB;AAAA,IAC1C;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI,eAAe,EAAE;AAAA,IACrB,MAAM,eAAe,UAAU;AAAA,EACjC;AACF;AAGA,eAAsB,QACpB,UACA,YACA,KACiB;AACjB,QAAM,KAAK,eAAe,QAAQ;AAClC,QAAM,aAAa,eAAe,UAAU;AAE5C,MAAI;AACF,UAAM,YAAY,MAAM,OAAO;AAAA,MAC7B,EAAE,MAAM,WAAW,GAAuB;AAAA,MAC1C;AAAA,MACA;AAAA,IACF;AACA,WAAO,IAAI,YAAY,EAAE,OAAO,SAAS;AAAA,EAC3C,SAAS,KAAK;AACZ,QAAI,eAAe,SAAS,IAAI,SAAS,kBAAkB;AACzD,YAAM,IAAI,cAAc;AAAA,IAC1B;AACA,UAAM,IAAI;AAAA,MACR,eAAe,QAAQ,IAAI,UAAU;AAAA,IACvC;AAAA,EACF;AACF;AAUA,eAAsB,aACpB,MACA,KACwB;AACxB,QAAM,KAAK,WAAW;AACtB,QAAM,aAAa,MAAM,OAAO;AAAA,IAC9B,EAAE,MAAM,WAAW,GAAuB;AAAA,IAC1C;AAAA,IACA;AAAA,EACF;AACA,SAAO;AAAA,IACL,IAAI,eAAe,EAAE;AAAA,IACrB,MAAM,eAAe,UAAU;AAAA,EACjC;AACF;AAMA,eAAsB,aACpB,UACA,YACA,KACqB;AACrB,QAAM,KAAK,eAAe,QAAQ;AAClC,QAAM,aAAa,eAAe,UAAU;AAC5C,MAAI;AACF,UAAM,YAAY,MAAM,OAAO;AAAA,MAC7B,EAAE,MAAM,WAAW,GAAuB;AAAA,MAC1C;AAAA,MACA;AAAA,IACF;AACA,WAAO,IAAI,WAAW,SAAS;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,SAAS,IAAI,SAAS,kBAAkB;AACzD,YAAM,IAAI,cAAc;AAAA,IAC1B;AACA,UAAM,IAAI;AAAA,MACR,eAAe,QAAQ,IAAI,UAAU;AAAA,IACvC;AAAA,EACF;AACF;AAQA,eAAsB,UAAU,MAAmC;AACjE,QAAM,OAAO,MAAM,OAAO,OAAO,WAAW,IAA+B;AAC3E,SAAO,MAAM,KAAK,IAAI,WAAW,IAAI,CAAC,EACnC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AACZ;AAgBA,eAAsB,cAAc,KAAgB,MAAmC;AAErF,QAAM,SAAS,MAAM,OAAO,UAAU,OAAO,GAAG;AAChD,QAAM,UAAU,MAAM,OAAO;AAAA,IAC3B;AAAA,IACA;AAAA,IACA,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AACA,QAAM,MAAM,MAAM,OAAO,KAAK,QAAQ,SAAS,IAA+B;AAC9E,SAAO,MAAM,KAAK,IAAI,WAAW,GAAG,CAAC,EAClC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAC1C,KAAK,EAAE;AACZ;AAgBA,eAAsB,oBACpB,MACA,KACA,KACwB;AACxB,QAAM,KAAK,WAAW;AACtB,QAAM,aAAa,MAAM,OAAO;AAAA,IAC9B;AAAA,MACE,MAAM;AAAA,MACN;AAAA,MACA,gBAAgB;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,SAAO;AAAA,IACL,IAAI,eAAe,EAAE;AAAA,IACrB,MAAM,eAAe,UAAU;AAAA,EACjC;AACF;AASA,eAAsB,oBACpB,UACA,YACA,KACA,KACqB;AACrB,QAAM,KAAK,eAAe,QAAQ;AAClC,QAAM,aAAa,eAAe,UAAU;AAC5C,MAAI;AACF,UAAM,YAAY,MAAM,OAAO;AAAA,MAC7B;AAAA,QACE,MAAM;AAAA,QACN;AAAA,QACA,gBAAgB;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,WAAO,IAAI,WAAW,SAAS;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI,eAAe,SAAS,IAAI,SAAS,kBAAkB;AACzD,YAAM,IAAI,cAAc;AAAA,IAC1B;AACA,UAAM,IAAI;AAAA,MACR,eAAe,QAAQ,IAAI,UAAU;AAAA,IACvC;AAAA,EACF;AACF;AAiBA,eAAsB,kBAAkB,KAAgB,gBAA4C;AAElG,QAAM,SAAS,MAAM,OAAO,UAAU,OAAO,GAAG;AAGhD,QAAM,UAAU,MAAM,OAAO;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAGA,QAAM,OAAO,IAAI,YAAY,EAAE,OAAO,gBAAgB;AACtD,QAAM,OAAO,IAAI,YAAY,EAAE,OAAO,cAAc;AACpD,QAAM,OAAO,MAAM,OAAO;AAAA,IACxB,EAAE,MAAM,QAAQ,MAAM,WAAW,MAAM,KAAK;AAAA,IAC5C;AAAA,IACA;AAAA,EACF;AAGA,SAAO,OAAO;AAAA,IACZ;AAAA,IACA;AAAA,IACA,EAAE,MAAM,WAAW,QAAQ,SAAS;AAAA,IACpC;AAAA,IACA,CAAC,WAAW,SAAS;AAAA,EACvB;AACF;AA2BA,eAAe,sBACb,KACA,SACA,WACqB;AACrB,QAAM,SAAS,MAAM,OAAO,UAAU,OAAO,GAAG;AAChD,QAAM,UAAU,MAAM,OAAO,UAAU,OAAO,QAAQ,QAAQ,OAAO,CAAC,YAAY,CAAC;AACnF,QAAM,OAAO,IAAI,YAAY,EAAE,OAAO,wBAAwB;AAC9D,QAAM,OAAO,IAAI,YAAY,EAAE,OAAO,GAAG,OAAO,KAAO,SAAS,EAAE;AAClE,QAAM,OAAO,MAAM,OAAO;AAAA,IACxB,EAAE,MAAM,QAAQ,MAAM,WAAW,MAAM,KAAK;AAAA,IAC5C;AAAA,IACA,WAAW;AAAA,EACb;AACA,SAAO,IAAI,WAAW,IAAI;AAC5B;AAgBA,eAAsB,qBACpB,WACA,KACA,SACwB;AACxB,QAAM,KAAK,MAAM,sBAAsB,KAAK,SAAS,SAAS;AAC9D,QAAM,UAAU,IAAI,YAAY,EAAE,OAAO,SAAS;AAClD,QAAM,aAAa,MAAM,OAAO;AAAA,IAC9B,EAAE,MAAM,WAAW,GAAuB;AAAA,IAC1C;AAAA,IACA;AAAA,EACF;AACA,SAAO;AAAA,IACL,IAAI,eAAe,EAAE;AAAA,IACrB,MAAM,eAAe,UAAU;AAAA,EACjC;AACF;AAQA,eAAsB,qBACpB,UACA,YACA,KACiB;AACjB,SAAO,QAAQ,UAAU,YAAY,GAAG;AAC1C;AAKO,SAAS,aAAyB;AACvC,SAAO,WAAW,OAAO,gBAAgB,IAAI,WAAW,QAAQ,CAAC;AACnE;AAGO,SAAS,eAA2B;AACzC,SAAO,WAAW,OAAO,gBAAgB,IAAI,WAAW,UAAU,CAAC;AACrE;AAIO,SAAS,eAAe,QAA0C;AACvE,QAAM,QAAQ,kBAAkB,aAAa,SAAS,IAAI,WAAW,MAAM;AAC3E,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAU,OAAO,aAAa,MAAM,CAAC,CAAE;AAAA,EACzC;AACA,SAAO,KAAK,MAAM;AACpB;AAEO,SAAS,eAAe,QAAyC;AACtE,QAAM,SAAS,KAAK,MAAM;AAC1B,QAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,EAChC;AACA,SAAO;AACT;","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  FieldFrozenError,
3
3
  InvariantError
4
- } from "./chunk-YDLAFP36.js";
4
+ } from "./chunk-JZ5DS4DP.js";
5
5
 
6
6
  // src/guards/executor.ts
7
7
  var GuardExecutor = {
@@ -9,14 +9,21 @@ var GuardExecutor = {
9
9
  * Compare existing vs incoming for each `frozenFields.fields` entry
10
10
  * when `frozenFields.when(existing)` is true. Throws
11
11
  * `FieldFrozenError` listing every changed frozen field.
12
+ *
13
+ * @param skipFields — field names that are schema-owned computed fields.
14
+ * These are excluded from the comparison because `incoming` carries the
15
+ * raw user input (computed fields not yet evaluated), so comparing
16
+ * `existing[field]` vs `incoming[field]` would always look like a
17
+ * change even when the computed result is unchanged.
12
18
  */
13
- async checkFrozenFields(guard, id, existing, incoming) {
19
+ async checkFrozenFields(guard, id, existing, incoming, skipFields) {
14
20
  const ff = guard.frozenFields;
15
21
  if (!ff) return;
16
22
  if (existing === null) return;
17
23
  if (!ff.when(existing)) return;
18
24
  const changed = [];
19
25
  for (const f of ff.fields) {
26
+ if (skipFields?.has(String(f))) continue;
20
27
  if (existing[f] !== incoming[f]) {
21
28
  if (!deepEqual(existing[f], incoming[f])) changed.push(String(f));
22
29
  }
@@ -70,4 +77,4 @@ function deepEqual(a, b) {
70
77
  export {
71
78
  GuardExecutor
72
79
  };
73
- //# sourceMappingURL=chunk-SAVQ6E2O.js.map
80
+ //# sourceMappingURL=chunk-FFKQ65YX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/guards/executor.ts"],"sourcesContent":["import { FieldFrozenError, InvariantError } from '../errors.js'\nimport type { GuardStrategy, GuardContext, GuardChange } from './types.js'\n\n/**\n * Pure functions that execute the work declared by a `GuardStrategy`.\n * Stateless — `GuardRegistry` decides when to call these.\n *\n * @internal\n */\nexport const GuardExecutor = {\n /**\n * Compare existing vs incoming for each `frozenFields.fields` entry\n * when `frozenFields.when(existing)` is true. Throws\n * `FieldFrozenError` listing every changed frozen field.\n *\n * @param skipFields — field names that are schema-owned computed fields.\n * These are excluded from the comparison because `incoming` carries the\n * raw user input (computed fields not yet evaluated), so comparing\n * `existing[field]` vs `incoming[field]` would always look like a\n * change even when the computed result is unchanged.\n */\n async checkFrozenFields<T extends Record<string, unknown>>(\n guard: GuardStrategy<T>,\n id: string,\n existing: T | null,\n incoming: T,\n skipFields?: ReadonlySet<string>,\n ): Promise<void> {\n const ff = guard.frozenFields\n if (!ff) return\n if (existing === null) return // insert — nothing to freeze\n if (!ff.when(existing)) return\n\n const changed: string[] = []\n for (const f of ff.fields) {\n // Skip computed fields — they are re-evaluated after this gate and\n // their raw-input value is not comparable to the prior computed value.\n if (skipFields?.has(String(f))) continue\n // Strict equality first, then deep-equality fallback for objects.\n if (existing[f] !== incoming[f]) {\n if (!deepEqual(existing[f], incoming[f])) changed.push(String(f))\n }\n }\n if (changed.length > 0) {\n throw new FieldFrozenError(guard.collection, id, changed)\n }\n },\n\n /**\n * Run a single guard's invariant over its slice of the change-set.\n * Any throw is converted to `InvariantError` unless it already is one.\n */\n async runInvariant<T extends Record<string, unknown>>(\n guard: GuardStrategy<T>,\n changes: ReadonlyArray<GuardChange<T>>,\n ctx: GuardContext<T>,\n ): Promise<void> {\n const amendment = guard.amendment\n if (!amendment) return\n try {\n await amendment.invariant(changes, ctx)\n } catch (err) {\n if (err instanceof InvariantError) throw err\n throw new InvariantError(\n err instanceof Error ? err.message : `invariant violated: ${String(err)}`,\n )\n }\n },\n}\n\n/**\n * Minimal deep-equality for guarded field diff. Handles arrays, plain\n * objects, primitives. Not for cyclic structures.\n *\n * @internal\n */\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (a === null || b === null) return a === b\n if (typeof a !== typeof b) return false\n if (typeof a !== 'object') return a === b\n if (Array.isArray(a) !== Array.isArray(b)) return false\n if (Array.isArray(a)) {\n const aa = a as unknown[]\n const bb = b as unknown[]\n if (aa.length !== bb.length) return false\n for (let i = 0; i < aa.length; i++) if (!deepEqual(aa[i], bb[i])) return false\n return true\n }\n const ao = a as Record<string, unknown>\n const bo = b as Record<string, unknown>\n const ak = Object.keys(ao)\n const bk = Object.keys(bo)\n if (ak.length !== bk.length) return false\n for (const k of ak) {\n if (!Object.prototype.hasOwnProperty.call(bo, k)) return false\n if (!deepEqual(ao[k], bo[k])) return false\n }\n return true\n}\n"],"mappings":";;;;;;AASO,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAY3B,MAAM,kBACJ,OACA,IACA,UACA,UACA,YACe;AACf,UAAM,KAAK,MAAM;AACjB,QAAI,CAAC,GAAI;AACT,QAAI,aAAa,KAAM;AACvB,QAAI,CAAC,GAAG,KAAK,QAAQ,EAAG;AAExB,UAAM,UAAoB,CAAC;AAC3B,eAAW,KAAK,GAAG,QAAQ;AAGzB,UAAI,YAAY,IAAI,OAAO,CAAC,CAAC,EAAG;AAEhC,UAAI,SAAS,CAAC,MAAM,SAAS,CAAC,GAAG;AAC/B,YAAI,CAAC,UAAU,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC,EAAG,SAAQ,KAAK,OAAO,CAAC,CAAC;AAAA,MAClE;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,IAAI,iBAAiB,MAAM,YAAY,IAAI,OAAO;AAAA,IAC1D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aACJ,OACA,SACA,KACe;AACf,UAAM,YAAY,MAAM;AACxB,QAAI,CAAC,UAAW;AAChB,QAAI;AACF,YAAM,UAAU,UAAU,SAAS,GAAG;AAAA,IACxC,SAAS,KAAK;AACZ,UAAI,eAAe,eAAgB,OAAM;AACzC,YAAM,IAAI;AAAA,QACR,eAAe,QAAQ,IAAI,UAAU,uBAAuB,OAAO,GAAG,CAAC;AAAA,MACzE;AAAA,IACF;AAAA,EACF;AACF;AAQA,SAAS,UAAU,GAAY,GAAqB;AAClD,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,MAAM,QAAQ,MAAM,KAAM,QAAO,MAAM;AAC3C,MAAI,OAAO,MAAM,OAAO,EAAG,QAAO;AAClC,MAAI,OAAO,MAAM,SAAU,QAAO,MAAM;AACxC,MAAI,MAAM,QAAQ,CAAC,MAAM,MAAM,QAAQ,CAAC,EAAG,QAAO;AAClD,MAAI,MAAM,QAAQ,CAAC,GAAG;AACpB,UAAM,KAAK;AACX,UAAM,KAAK;AACX,QAAI,GAAG,WAAW,GAAG,OAAQ,QAAO;AACpC,aAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,IAAK,KAAI,CAAC,UAAU,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,EAAG,QAAO;AACzE,WAAO;AAAA,EACT;AACA,QAAM,KAAK;AACX,QAAM,KAAK;AACX,QAAM,KAAK,OAAO,KAAK,EAAE;AACzB,QAAM,KAAK,OAAO,KAAK,EAAE;AACzB,MAAI,GAAG,WAAW,GAAG,OAAQ,QAAO;AACpC,aAAW,KAAK,IAAI;AAClB,QAAI,CAAC,OAAO,UAAU,eAAe,KAAK,IAAI,CAAC,EAAG,QAAO;AACzD,QAAI,CAAC,UAAU,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,EAAG,QAAO;AAAA,EACvC;AACA,SAAO;AACT;","names":[]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BundleVersionConflictError,
3
3
  ConflictError
4
- } from "./chunk-YDLAFP36.js";
4
+ } from "./chunk-JZ5DS4DP.js";
5
5
 
6
6
  // src/store/bundle-store.ts
7
7
  var BUNDLE_STORE_VERSION = 1;
@@ -413,6 +413,14 @@ function routeStore(opts) {
413
413
  const q = {};
414
414
  for (const [k, v] of writeQueues) q[k] = v.writes.length;
415
415
  return { overrides: ov, suspended: [...suspended], queued: q };
416
+ },
417
+ resolveBackend(vaultId) {
418
+ if (opts.vaultRoutes) {
419
+ for (const [prefix, s] of Object.entries(opts.vaultRoutes)) {
420
+ if (vaultId.startsWith(prefix)) return s;
421
+ }
422
+ }
423
+ return primary;
416
424
  }
417
425
  };
418
426
  if (anyHas("listVaults")) {
@@ -790,4 +798,4 @@ export {
790
798
  withCache,
791
799
  withHealthCheck
792
800
  };
793
- //# sourceMappingURL=chunk-2XLVPKXG.js.map
801
+ //# sourceMappingURL=chunk-G7ALNCQH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/store/bundle-store.ts","../src/store/route-store.ts","../src/store/store-middleware.ts"],"sourcesContent":["import type { NoydbStore, NoydbBundleStore, VaultSnapshot, EncryptedEnvelope } from '../types.js'\nimport { ConflictError, BundleVersionConflictError } from '../errors.js'\n\n// ─── Bundle format ─────────────────────────────────────────────────────\n\nconst BUNDLE_STORE_VERSION = 1 as const\n\n/**\n * Wire format written by `wrapBundleStore`. A JSON-serialised object that\n * contains the entire `VaultSnapshot` (all encrypted envelopes) plus a small\n * header for integrity checking. The envelopes inside are already AES-GCM\n * encrypted by core — the bundle bytes themselves are not additionally\n * encrypted, but they are safe to store on untrusted blob hosts because\n * every record inside is already ciphertext.\n *\n * @internal\n */\ninterface BundleStoreData {\n readonly _noydb_bundle_store: typeof BUNDLE_STORE_VERSION\n readonly vault: string\n readonly ts: string\n readonly data: VaultSnapshot\n}\n\n// ─── Options ───────────────────────────────────────────────────────────\n\nexport interface WrapBundleStoreOptions {\n /**\n * When `true` (default), every `put()` and `delete()` flushes the full\n * vault snapshot to the bundle backend. Set to `false` for bulk operations\n * and call `store.flush(vaultId)` manually.\n */\n autoFlush?: boolean\n}\n\n// ─── Extended NoydbStore with flush/batch ───────────────────────────────\n\nexport interface WrappedBundleNoydbStore extends NoydbStore {\n /** Manually flush the in-memory snapshot to the bundle backend. */\n flush(vaultId: string): Promise<void>\n /**\n * Run a batch of mutations without flushing until the callback completes.\n * A single flush is performed at the end.\n */\n batch(vaultId: string, fn: () => Promise<void>): Promise<void>\n}\n\n// ─── wrapBundleStore ───────────────────────────────────────────────────\n\nconst MAX_CONFLICT_RETRIES = 3\n\n/**\n * Convert a `NoydbBundleStore` (blob-oriented read/write with OCC) into the\n * standard six-method `NoydbStore` interface expected by `createNoydb({ store })`.\n *\n * Bundle stores operate on the entire vault as a single serialised unit —\n * ideal for backends like Google Drive, WebDAV, or iCloud Drive that work\n * best with whole-file I/O rather than per-record KV operations.\n *\n * ## Optimistic concurrency\n *\n * The wrapper tracks the `version` token from the last `readBundle` and\n * passes it as `expectedVersion` on every flush. On\n * `BundleVersionConflictError`, it re-reads, merges the remote snapshot\n * (last-write-wins per record key), and retries (max 3 attempts).\n *\n * ## Flush modes\n *\n * By default, flushes on every mutation (O(vault size) per write). Options:\n * - `autoFlush: false` + explicit `store.flush(vaultId)` calls\n * - `store.batch(vaultId, async () => { ... })` — defers flush until end\n * - Pair with `syncPolicy: { push: { mode: 'debounce' } }` from \n */\nexport function wrapBundleStore(\n bundle: NoydbBundleStore,\n options?: WrapBundleStoreOptions,\n): WrappedBundleNoydbStore {\n const autoFlush = options?.autoFlush !== false\n\n // Per-vault state\n const snapshots = new Map<string, VaultSnapshot>()\n const versions = new Map<string, string | null>()\n const loaded = new Set<string>()\n\n // Batch mode: when > 0, suppress auto-flush\n let batchDepth = 0\n\n async function load(vault: string): Promise<VaultSnapshot> {\n if (loaded.has(vault)) return snapshots.get(vault)!\n\n const result = await bundle.readBundle(vault)\n if (result) {\n const text = new TextDecoder().decode(result.bytes)\n const format = JSON.parse(text) as BundleStoreData\n snapshots.set(vault, format.data)\n versions.set(vault, result.version)\n } else {\n snapshots.set(vault, {})\n versions.set(vault, null)\n }\n\n loaded.add(vault)\n return snapshots.get(vault)!\n }\n\n async function flush(vault: string): Promise<void> {\n const snapshot = snapshots.get(vault) ?? {}\n const format: BundleStoreData = {\n _noydb_bundle_store: BUNDLE_STORE_VERSION,\n vault,\n ts: new Date().toISOString(),\n data: snapshot,\n }\n const bytes = new TextEncoder().encode(JSON.stringify(format))\n const expectedVersion = versions.get(vault) ?? null\n\n for (let attempt = 0; attempt < MAX_CONFLICT_RETRIES; attempt++) {\n try {\n const { version: newVersion } = await bundle.writeBundle(vault, bytes, expectedVersion)\n versions.set(vault, newVersion)\n return\n } catch (err) {\n if (err instanceof BundleVersionConflictError && attempt < MAX_CONFLICT_RETRIES - 1) {\n // Pull remote, merge (last-write-wins by record key), retry\n const remote = await bundle.readBundle(vault)\n if (remote) {\n const remoteText = new TextDecoder().decode(remote.bytes)\n const remoteFormat = JSON.parse(remoteText) as BundleStoreData\n const localSnap = snapshots.get(vault) ?? {}\n const mergedSnap = mergeSnapshots(remoteFormat.data, localSnap)\n snapshots.set(vault, mergedSnap)\n versions.set(vault, remote.version)\n }\n // Re-encode with merged data for the retry\n continue\n }\n throw err\n }\n }\n }\n\n async function maybeFlush(vault: string): Promise<void> {\n if (autoFlush && batchDepth === 0) {\n await flush(vault)\n }\n }\n\n const store: WrappedBundleNoydbStore = {\n name: bundle.name ?? 'bundle',\n\n async flush(vaultId: string): Promise<void> {\n await flush(vaultId)\n },\n\n async batch(vaultId: string, fn: () => Promise<void>): Promise<void> {\n await load(vaultId) // ensure loaded before batch\n batchDepth++\n try {\n await fn()\n } finally {\n batchDepth--\n }\n await flush(vaultId)\n },\n\n async get(vault: string, collection: string, id: string): Promise<EncryptedEnvelope | null> {\n const snap = await load(vault)\n return snap[collection]?.[id] ?? null\n },\n\n async put(\n vault: string,\n collection: string,\n id: string,\n envelope: EncryptedEnvelope,\n expectedVersion?: number,\n ): Promise<void> {\n const snap = await load(vault)\n\n if (expectedVersion !== undefined) {\n const current = snap[collection]?.[id]\n const currentVersion = current?._v ?? 0\n if (currentVersion !== expectedVersion) {\n throw new ConflictError(\n currentVersion,\n `Expected version ${expectedVersion} but found ${currentVersion} on ${collection}/${id}`,\n )\n }\n }\n\n snap[collection] ??= {}\n snap[collection][id] = envelope\n await maybeFlush(vault)\n },\n\n async delete(vault: string, collection: string, id: string): Promise<void> {\n const snap = await load(vault)\n if (snap[collection]) {\n delete snap[collection][id]\n await maybeFlush(vault)\n }\n },\n\n async list(vault: string, collection: string): Promise<string[]> {\n const snap = await load(vault)\n return Object.keys(snap[collection] ?? {})\n },\n\n async loadAll(vault: string): Promise<VaultSnapshot> {\n return await load(vault)\n },\n\n async saveAll(vault: string, data: VaultSnapshot): Promise<void> {\n snapshots.set(vault, data)\n loaded.add(vault)\n await flush(vault)\n },\n }\n\n return store\n}\n\n// ─── Snapshot merge (last-write-wins per record) ────────────────────────\n\nfunction mergeSnapshots(remote: VaultSnapshot, local: VaultSnapshot): VaultSnapshot {\n const merged: VaultSnapshot = {}\n\n // Start with all remote collections\n for (const [coll, records] of Object.entries(remote)) {\n merged[coll] = { ...records }\n }\n\n // Overlay local collections — LWW by _ts per record\n for (const [coll, records] of Object.entries(local)) {\n if (!merged[coll]) {\n merged[coll] = { ...records }\n continue\n }\n for (const [id, envelope] of Object.entries(records)) {\n const existing = merged[coll][id]\n if (!existing || envelope._ts >= existing._ts) {\n merged[coll][id] = envelope\n }\n }\n }\n\n return merged\n}\n\n// ─── Factory helper ─────────────────────────────────────────────────────\n\n/**\n * Type-safe factory helper for `NoydbBundleStore` implementations,\n * analogous to `createStore` for KV stores.\n */\nexport function createBundleStore<TOptions>(\n factory: (options: TOptions) => NoydbBundleStore,\n): (options: TOptions) => NoydbBundleStore {\n return factory\n}\n","/**\n * Store router / multiplexer.\n *\n * Dispatches `NoydbStore` operations to different backends based on\n * collection type, record size, record age, collection name, or vault name.\n *\n * ```ts\n * const db = await createNoydb({\n * store: routeStore({\n * default: dynamo({ table: 'myapp' }),\n * blobs: s3Store({ bucket: 'myapp-blobs' }),\n * }),\n * })\n * ```\n *\n * @module\n */\n\nimport type {\n NoydbStore,\n EncryptedEnvelope,\n VaultSnapshot,\n} from '../types.js'\n\n// ─── Internal collection prefixes (duplicated to avoid circular import) ──\n\nconst BLOB_CHUNKS = '_blob_chunks'\nconst BLOB_INDEX = '_blob_index'\nconst BLOB_SLOTS = '_blob_slots_'\nconst BLOB_VERSIONS = '_blob_versions_'\n\n// ─── Options ─────────────────────────────────────────────────────────────\n\n/**\n * Size-tiered blob routing configuration.\n *\n * Routes blob chunks to different stores based on byte size. Small blobs\n * (under `threshold`) stay in the primary or `small` store; large blobs\n * go to `large`. This lets you keep DynamoDB as the default while sending\n * large binary objects to S3.\n */\nexport interface BlobStoreRoute {\n /** Store for small blobs (under threshold). Falls back to `default`. */\n readonly small?: NoydbStore\n /** Store for large blobs (over threshold). */\n readonly large: NoydbStore\n /** Size threshold in bytes. Default: `400 * 1024` (DynamoDB item limit). */\n readonly threshold?: number\n}\n\n/**\n * Blob lifecycle management policies evaluated during `compact()`.\n *\n * Controls orphan cleanup, cold-tier archival, and hard deletion of\n * blobs that are no longer referenced by any record.\n */\nexport interface BlobLifecyclePolicy {\n /** Delete orphan blobs (refCount: 0) after this many days. Default: 7. */\n readonly orphanRetentionDays?: number\n /** Move blobs not accessed in this many days to the cold blob store. */\n readonly archiveAfterDays?: number\n /** Store for archived blobs. Required if archiveAfterDays is set. */\n readonly archiveStore?: NoydbStore\n /** Hard-delete archived blobs after this many days. */\n readonly expireAfterDays?: number\n}\n\n/**\n * Age-based hot/cold tiering configuration.\n *\n * Records whose `_ts` timestamp is older than `coldAfterDays` are migrated\n * to the `cold` store during `compact()`. Reads transparently fall through\n * to the cold store when the hot store returns null, so callers don't need\n * to know which tier a record lives in.\n */\nexport interface AgeRoute {\n /** Store for records older than the cutoff. */\n readonly cold: NoydbStore\n /** Days after last modification before a record is cold-eligible. */\n readonly coldAfterDays: number\n /**\n * Collections that participate in age tiering.\n * Empty array or omitted = all user collections (excluding `_` prefixed).\n */\n readonly collections?: string[]\n}\n\n/**\n * Options for `routeStore()` — the store multiplexer.\n *\n * At minimum, provide a `default` store. All other fields are optional\n * extensions for specific routing scenarios (blobs → S3, geographic sharding,\n * age-based tiering, etc.).\n */\nexport interface RouteStoreOptions {\n /** Default store for all unmatched operations. */\n readonly default: NoydbStore\n\n /**\n * Route blob chunk data to a separate store.\n * - Pass a `NoydbStore` for simple prefix routing (all chunks → that store).\n * - Pass `{ small?, large, threshold? }` for size-tiered routing.\n */\n readonly blobs?: NoydbStore | BlobStoreRoute\n\n /** Route all blob metadata (index, slots, versions) to the blobs store too. Default: false. */\n readonly routeBlobMeta?: boolean\n\n /** Route specific user collections to dedicated stores. */\n readonly routes?: Record<string, NoydbStore>\n\n /** Route by vault name (prefix patterns, e.g. `'EU-'`). */\n readonly vaultRoutes?: Record<string, NoydbStore>\n\n /**\n * Age-based tiering: records older than `coldAfterDays` are read from\n * the cold store. A background `compact()` method migrates them.\n */\n readonly age?: AgeRoute\n\n /**\n * Content-aware blob routing.\n * Route blob chunks by MIME type glob pattern. The MIME type is stored\n * in `BlobObject` and matched at read time via `storeHint`.\n */\n readonly blobRoutes?: Record<string, NoydbStore>\n\n /**\n * Blob lifecycle policies.\n * Evaluated during `compact()`.\n */\n readonly blobLifecycle?: BlobLifecyclePolicy\n\n /**\n * Quota-aware overflow.\n * When the default store's usage exceeds the threshold, new writes\n * overflow to the specified store.\n */\n readonly overflow?: NoydbStore\n\n /**\n * Quota threshold (0-1). Default: 0.8 (overflow at 80% usage).\n * Only effective when `overflow` is set.\n */\n readonly quotaThreshold?: number\n}\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\n/**\n * Named route that can be overridden or suspended at runtime.\n *\n * Built-in names: `'default'`, `'blobs'`, `'cold'`.\n * Custom names: any collection name from `routes`, any vault prefix from\n * `vaultRoutes`, or any sync target label.\n */\nexport type OverrideTarget =\n | 'default'\n | 'blobs'\n | 'cold'\n | (string & {}) // named collection route, vault route, or sync target label\n\n/**\n * Options for `RoutedNoydbStore.override()`.\n *\n * Controls whether the new store is pre-populated with data from the\n * original store before the switch takes effect.\n */\nexport interface OverrideOptions {\n /**\n * Hydrate the override store from the original before activating.\n * - `true` — copy all data for all vaults.\n * - `string[]` — copy only named collections.\n * Makes `override()` async — returns a Promise.\n */\n hydrate?: boolean | string[]\n}\n\n/**\n * Options for `RoutedNoydbStore.suspend()`.\n *\n * A suspended route becomes a null store: reads return null/[], writes\n * are dropped (or buffered if `queue: true`). Useful for maintenance\n * windows or restricted-network scenarios.\n */\nexport interface SuspendOptions {\n /**\n * Buffer write operations during suspension. On `resume()`, queued\n * writes are replayed against the restored store.\n */\n queue?: boolean\n /**\n * Maximum queued operations. When exceeded, oldest entries are dropped.\n * Default: 10_000.\n */\n maxQueueSize?: number\n}\n\n/** Queued write operation recorded during suspension. */\ninterface QueuedWrite {\n method: 'put' | 'delete'\n vault: string\n collection: string\n id: string\n envelope?: EncryptedEnvelope\n expectedVersion?: number\n}\n\n/**\n * Snapshot of the current override and suspend state of a `RoutedNoydbStore`.\n * Returned by `routeStatus()` for diagnostics and health dashboards.\n */\nexport interface RouteStatus {\n /** Active overrides: route name → override store name. */\n readonly overrides: Record<string, string>\n /** Currently suspended routes. */\n readonly suspended: string[]\n /** Queued writes per suspended route (only for routes suspended with `queue: true`). */\n readonly queued: Record<string, number>\n}\n\n/**\n * Extended `NoydbStore` returned by `routeStore()`.\n *\n * Satisfies the full `NoydbStore` contract plus adds runtime control\n * methods for overriding, suspending, and inspecting routes.\n */\nexport interface RoutedNoydbStore extends NoydbStore {\n /**\n * Migrate records older than the age cutoff from the hot store to the\n * cold store. Only applies when `age` is configured. Returns the number\n * of records migrated.\n */\n compact(vault: string): Promise<number>\n\n /**\n * Override a named route at runtime.\n *\n * The override persists until `clearOverride()` is called or the\n * instance is closed. In-flight operations complete on the original\n * store; new operations use the override.\n *\n * Options:\n * - `hydrate: true` — async: copies all data from the original store\n * into the override before activating the switch.\n * - `hydrate: ['invoices', 'clients']` — copies only named collections.\n *\n * Use cases:\n * - Shared device: `await store.override('default', memory(), { hydrate: true })`\n * - Restricted network: `store.override('blobs', localFile(...))`\n */\n override(route: OverrideTarget, store: NoydbStore, opts?: OverrideOptions): void | Promise<void>\n\n /** Clear a runtime override, reverting to the original store. */\n clearOverride(route: OverrideTarget): void\n\n /**\n * Suspend a route entirely. Operations to suspended stores become\n * no-ops (puts silently dropped, gets return null, lists return []).\n *\n * Options:\n * - `queue: true` — buffer write operations (put/delete) during\n * suspension. When `resume()` is called, queued writes are replayed\n * against the restored store.\n *\n * Returns a `SuspendHandle` when `queue: true`, for inspecting queue state.\n */\n suspend(route: OverrideTarget, opts?: SuspendOptions): void\n\n /**\n * Resume a previously suspended route.\n * If the route was suspended with `queue: true`, replays queued writes.\n * Returns the number of replayed operations.\n */\n resume(route: OverrideTarget): Promise<number>\n\n /** Snapshot the current override/suspend state for diagnostics. */\n routeStatus(): RouteStatus\n\n /**\n * Resolve the physical backend a vault id maps to via the geographic\n * `vaultRoutes` prefix routing (collection-independent), falling back to\n * the `default` store. Used by the federation data-residency guard (#271)\n * to read the placement backend's `capabilities.region`.\n */\n resolveBackend(vaultId: string): NoydbStore\n}\n\n// ─── Implementation ──────────────────────────────────────────────────────\n\n/**\n * Create a store multiplexer that dispatches operations to different backends\n * based on collection type, record size, record age, vault prefix, or\n * runtime overrides.\n *\n * ```ts\n * const store = routeStore({\n * default: dynamo({ table: 'myapp' }),\n * blobs: s3({ bucket: 'myapp-blobs' }),\n * routes: { auditLog: s3({ bucket: 'myapp-audit' }) },\n * })\n * ```\n *\n * The returned store satisfies `NoydbStore` and can be passed directly to\n * `createNoydb({ store })`. It also exposes additional methods\n * (`override`, `suspend`, `resume`, `routeStatus`, `compact`) for runtime\n * control and maintenance.\n */\nexport function routeStore(opts: RouteStoreOptions): RoutedNoydbStore {\n const primary = opts.default\n\n // Resolve blob store config\n const blobsIsSimple = opts.blobs && 'get' in opts.blobs\n const simpleBlobStore = blobsIsSimple ? opts.blobs : undefined\n const tieredBlobs = !blobsIsSimple ? opts.blobs : undefined\n const blobThreshold = tieredBlobs?.threshold ?? 400 * 1024\n\n // Collect all stores for loadAll/saveAll/listVaults composition\n const allStores = new Set<NoydbStore>([primary])\n if (simpleBlobStore) allStores.add(simpleBlobStore)\n if (tieredBlobs?.large) allStores.add(tieredBlobs.large)\n if (tieredBlobs?.small) allStores.add(tieredBlobs.small)\n if (opts.age?.cold) allStores.add(opts.age.cold)\n if (opts.routes) for (const s of Object.values(opts.routes)) allStores.add(s)\n if (opts.vaultRoutes) for (const s of Object.values(opts.vaultRoutes)) allStores.add(s)\n if (opts.blobRoutes) for (const s of Object.values(opts.blobRoutes)) allStores.add(s)\n if (opts.overflow) allStores.add(opts.overflow)\n if (opts.blobLifecycle?.archiveStore) allStores.add(opts.blobLifecycle.archiveStore)\n\n // ── Runtime override / suspend state ──────────────────\n\n const overrides = new Map<string, NoydbStore>()\n const suspended = new Set<string>()\n const writeQueues = new Map<string, { writes: QueuedWrite[]; maxSize: number }>()\n\n /** Null store: silently absorbs all operations when a route is suspended. */\n const NULL_STORE: NoydbStore = {\n name: 'suspended',\n async get() { return null },\n async put() {},\n async delete() {},\n async list() { return [] },\n async loadAll() { return {} },\n async saveAll() {},\n }\n\n /**\n * Map a resolved route to its canonical name for override/suspend lookup.\n * Vault routes use the prefix, collection routes use the collection name,\n * blob route is 'blobs', cold route is 'cold', everything else is 'default'.\n */\n function routeNameFor(vault: string, collection: string): string {\n if (opts.vaultRoutes) {\n for (const prefix of Object.keys(opts.vaultRoutes)) {\n if (vault.startsWith(prefix)) return prefix\n }\n }\n if (opts.routes && !collection.startsWith('_') && opts.routes[collection]) {\n return collection\n }\n if (isBlobChunks(collection) && (simpleBlobStore || tieredBlobs)) return 'blobs'\n if (opts.routeBlobMeta && isBlobMeta(collection) && (simpleBlobStore || tieredBlobs)) return 'blobs'\n if (opts.age && !collection.startsWith('_')) {\n // We don't name age 'cold' here — cold is a fallback, not a primary route\n }\n return 'default'\n }\n\n // ── Quota-aware overflow (E8) ───────────────────────────────────────\n\n const quotaExceeded = false\n\n /** Resolve the static (non-overridden) store for a given route name. */\n function resolveOriginalStore(route: string): NoydbStore {\n if (route === 'blobs') return simpleBlobStore ?? tieredBlobs?.large ?? primary\n if (route === 'cold') return opts.age?.cold ?? primary\n if (opts.routes?.[route]) return opts.routes[route]\n if (opts.vaultRoutes?.[route]) return opts.vaultRoutes[route]\n return primary\n }\n\n /**\n * Queue a write operation if the route is suspended with queue: true.\n * Returns true if queued (caller should skip the actual write).\n */\n function maybeQueueWrite(\n routeName: string,\n method: 'put' | 'delete',\n vault: string,\n collection: string,\n id: string,\n envelope?: EncryptedEnvelope,\n expectedVersion?: number,\n ): boolean {\n if (!suspended.has(routeName)) return false\n const queue = writeQueues.get(routeName)\n if (!queue) return false // suspended but no queue — NullStore behavior\n\n // Evict oldest if at capacity\n if (queue.writes.length >= queue.maxSize) {\n queue.writes.shift()\n }\n queue.writes.push({\n method, vault, collection, id,\n ...(envelope !== undefined ? { envelope } : {}),\n ...(expectedVersion !== undefined ? { expectedVersion } : {}),\n })\n return true\n }\n\n // ── Routing logic ──────────────────────────────────────────────────\n\n function isBlobChunks(collection: string): boolean {\n return collection === BLOB_CHUNKS\n }\n\n function isBlobMeta(collection: string): boolean {\n return collection === BLOB_INDEX\n || collection.startsWith(BLOB_SLOTS)\n || collection.startsWith(BLOB_VERSIONS)\n }\n\n function isInternal(collection: string): boolean {\n return collection.startsWith('_')\n }\n\n /**\n * Resolve the store for a given vault + collection.\n * Resolution order: overrides/suspend → vaultRoutes → routes → blobs → default\n */\n function storeFor(vault: string, collection: string): NoydbStore {\n const rName = routeNameFor(vault, collection)\n\n // 0. Runtime override / suspend check\n if (suspended.has(rName)) return NULL_STORE\n if (overrides.has(rName)) return overrides.get(rName)!\n\n // 1. Vault-based geographic routing\n if (opts.vaultRoutes) {\n for (const [prefix, store] of Object.entries(opts.vaultRoutes)) {\n if (vault.startsWith(prefix)) return store\n }\n }\n\n // 2. Per-collection routing (user collections only)\n if (opts.routes && !isInternal(collection) && opts.routes[collection]) {\n return opts.routes[collection]\n }\n\n // 3. Blob chunk routing (simple — no size tiering at the store level)\n if (isBlobChunks(collection)) {\n if (simpleBlobStore) return simpleBlobStore\n // Size-tiered: can't determine here without the envelope.\n // Default to large store — BlobSet will use storeHint for reads.\n if (tieredBlobs) return tieredBlobs.large\n }\n\n // 4. Blob metadata routing\n if (opts.routeBlobMeta && isBlobMeta(collection)) {\n if (simpleBlobStore) return simpleBlobStore\n if (tieredBlobs) return tieredBlobs.large\n }\n\n // 5. Quota-aware overflow (E8)\n if (quotaExceeded && opts.overflow) return opts.overflow\n\n // 6. Default\n return primary\n }\n\n /**\n * For size-tiered blob routing: pick store based on envelope data size.\n */\n function blobStoreForSize(dataSize: number): NoydbStore {\n if (!tieredBlobs) return simpleBlobStore ?? primary\n if (dataSize <= blobThreshold) {\n return tieredBlobs.small ?? primary\n }\n return tieredBlobs.large\n }\n\n /**\n * Age routing: check if a record is cold based on `_ts`.\n */\n function isCold(collection: string, envelope: EncryptedEnvelope): boolean {\n if (!opts.age) return false\n if (isInternal(collection)) return false\n if (opts.age.collections && opts.age.collections.length > 0) {\n if (!opts.age.collections.includes(collection)) return false\n }\n const cutoff = Date.now() - opts.age.coldAfterDays * 24 * 60 * 60 * 1000\n const ts = new Date(envelope._ts).getTime()\n return ts < cutoff\n }\n\n // ── Store methods ──────────────────────────────────────────────────\n\n const store: RoutedNoydbStore = {\n name: buildName(),\n\n async get(vault, collection, id) {\n const s = storeFor(vault, collection)\n const result = await s.get(vault, collection, id)\n\n // Age tiering: if hot store returned null, try cold\n if (result === null && opts.age && !isInternal(collection)) {\n if (!opts.age.collections?.length || opts.age.collections.includes(collection)) {\n return opts.age.cold.get(vault, collection, id)\n }\n }\n\n return result\n },\n\n async put(vault, collection, id, envelope, expectedVersion) {\n // Write-behind queue: buffer if suspended with queue option\n const rn = routeNameFor(vault, collection)\n if (maybeQueueWrite(rn, 'put', vault, collection, id, envelope, expectedVersion)) return\n\n // Size-tiered blob routing\n if (isBlobChunks(collection) && tieredBlobs) {\n const dataSize = envelope._data.length\n const s = blobStoreForSize(dataSize)\n return s.put(vault, collection, id, envelope, expectedVersion)\n }\n\n const s = storeFor(vault, collection)\n\n // Age tiering: if a cold record is being updated, it goes to hot.\n if (opts.age && !isInternal(collection)) {\n opts.age.cold.delete(vault, collection, id).catch(() => {})\n }\n\n return s.put(vault, collection, id, envelope, expectedVersion)\n },\n\n async delete(vault, collection, id) {\n // Write-behind queue: buffer if suspended with queue option\n const rn = routeNameFor(vault, collection)\n if (maybeQueueWrite(rn, 'delete', vault, collection, id)) return\n\n const s = storeFor(vault, collection)\n await s.delete(vault, collection, id)\n\n // Also delete from cold store if age-tiered\n if (opts.age && !isInternal(collection)) {\n await opts.age.cold.delete(vault, collection, id).catch(() => {})\n }\n },\n\n async list(vault, collection) {\n const s = storeFor(vault, collection)\n const ids = await s.list(vault, collection)\n\n // Age tiering: merge IDs from cold store, deduplicate\n if (opts.age && !isInternal(collection)) {\n if (!opts.age.collections?.length || opts.age.collections.includes(collection)) {\n const coldIds = await opts.age.cold.list(vault, collection).catch(() => [] as string[])\n if (coldIds.length > 0) {\n const merged = new Set(ids)\n for (const id of coldIds) merged.add(id)\n return [...merged]\n }\n }\n }\n\n return ids\n },\n\n async loadAll(vault) {\n // Query all distinct stores in parallel, merge snapshots\n const stores = getStoresForVault(vault)\n const snapshots = await Promise.all(\n stores.map(s => s.loadAll(vault).catch(() => ({}) as VaultSnapshot)),\n )\n return mergeSnapshots(snapshots)\n },\n\n async saveAll(vault, data) {\n // Partition snapshot by routing rules\n const partitioned = new Map<NoydbStore, VaultSnapshot>()\n\n for (const [collection, records] of Object.entries(data)) {\n const s = storeFor(vault, collection)\n if (!partitioned.has(s)) partitioned.set(s, {})\n partitioned.get(s)![collection] = records\n }\n\n await Promise.all(\n [...partitioned.entries()].map(([s, snap]) => s.saveAll(vault, snap)),\n )\n },\n\n async compact(vault) {\n if (!opts.age) return 0\n let migrated = 0\n const collections = opts.age.collections?.length\n ? opts.age.collections\n : await primary.list(vault, '').catch(() => [] as string[])\n\n // For each age-eligible collection, scan hot store for cold records\n for (const collection of collections) {\n const ids = await primary.list(vault, collection).catch(() => [] as string[])\n for (const id of ids) {\n const envelope = await primary.get(vault, collection, id)\n if (!envelope) continue\n if (isCold(collection, envelope)) {\n // Write to cold, then delete from hot\n await opts.age.cold.put(vault, collection, id, envelope)\n await primary.delete(vault, collection, id)\n migrated++\n }\n }\n }\n\n return migrated\n },\n\n // ── Runtime override / suspend ──────────────────────\n\n override(route: OverrideTarget, overrideStore: NoydbStore, overrideOpts?: OverrideOptions): void | Promise<void> {\n if (overrideOpts?.hydrate) {\n // Async hydration: copy data from current store, then activate override\n return (async () => {\n // Hydration: caller should copy data from the original store to\n // overrideStore before calling override() with { hydrate: true }.\n // The route is activated immediately after.\n overrides.set(route, overrideStore)\n })()\n }\n overrides.set(route, overrideStore)\n },\n\n clearOverride(route: OverrideTarget): void {\n overrides.delete(route)\n },\n\n suspend(route: OverrideTarget, suspendOpts?: SuspendOptions): void {\n suspended.add(route)\n if (suspendOpts?.queue) {\n writeQueues.set(route, {\n writes: [],\n maxSize: suspendOpts.maxQueueSize ?? 10_000,\n })\n }\n },\n\n async resume(route: OverrideTarget): Promise<number> {\n suspended.delete(route)\n const queue = writeQueues.get(route)\n if (!queue || queue.writes.length === 0) {\n writeQueues.delete(route)\n return 0\n }\n\n // Replay queued writes against the now-active store\n let replayed = 0\n const target = overrides.get(route) ?? resolveOriginalStore(route)\n for (const write of queue.writes) {\n try {\n if (write.method === 'put' && write.envelope) {\n await target.put(write.vault, write.collection, write.id, write.envelope, write.expectedVersion)\n } else if (write.method === 'delete') {\n await target.delete(write.vault, write.collection, write.id)\n }\n replayed++\n } catch {\n // Best-effort replay — conflicts are expected after suspension\n }\n }\n\n writeQueues.delete(route)\n return replayed\n },\n\n routeStatus(): RouteStatus {\n const ov: Record<string, string> = {}\n for (const [k, v] of overrides) ov[k] = v.name ?? 'unnamed'\n const q: Record<string, number> = {}\n for (const [k, v] of writeQueues) q[k] = v.writes.length\n return { overrides: ov, suspended: [...suspended], queued: q }\n },\n\n resolveBackend(vaultId: string): NoydbStore {\n // Geographic routing is vault-prefix based + collection-independent;\n // the region-relevant backend is the vaultRoutes match, else default.\n if (opts.vaultRoutes) {\n for (const [prefix, s] of Object.entries(opts.vaultRoutes)) {\n if (vaultId.startsWith(prefix)) return s\n }\n }\n return primary\n },\n }\n\n // ── Optional method forwarding ─────────────────────────────────────\n\n // Forward listVaults from all stores, deduplicated\n if (anyHas('listVaults')) {\n store.listVaults = async () => {\n const results = await Promise.all(\n [...allStores]\n .filter(s => s.listVaults !== undefined)\n .map(s => s.listVaults!().catch(() => [] as string[])),\n )\n return [...new Set(results.flat())]\n }\n }\n\n // Forward ping — succeed if any store responds\n if (anyHas('ping')) {\n store.ping = async () => {\n const results = await Promise.all(\n [...allStores]\n .filter(s => s.ping !== undefined)\n .map(s => s.ping!().catch(() => false)),\n )\n return results.some(Boolean)\n }\n }\n\n return store\n\n // ── Helpers ────────────────────────────────────────────────────────\n\n function buildName(): string {\n const names = [...allStores].map(s => s.name ?? '?').join('+')\n return `route(${names})`\n }\n\n function anyHas(method: string): boolean {\n return [...allStores].some(s => (s as unknown as Record<string, unknown>)[method])\n }\n\n function getStoresForVault(vault: string): NoydbStore[] {\n const stores = new Set<NoydbStore>()\n\n // Check vault routes first\n if (opts.vaultRoutes) {\n for (const [prefix, s] of Object.entries(opts.vaultRoutes)) {\n if (vault.startsWith(prefix)) {\n stores.add(s)\n return [...stores] // vault-routed: only use that store\n }\n }\n }\n\n // Default topology: primary + blob store + cold store\n stores.add(primary)\n if (simpleBlobStore) stores.add(simpleBlobStore)\n if (tieredBlobs?.large) stores.add(tieredBlobs.large)\n if (tieredBlobs?.small && tieredBlobs.small !== primary) stores.add(tieredBlobs.small)\n if (opts.age?.cold) stores.add(opts.age.cold)\n if (opts.routes) {\n for (const s of Object.values(opts.routes)) stores.add(s)\n }\n\n return [...stores]\n }\n}\n\n// ─── Snapshot merge ──────────────────────────────────────────────────────\n\nfunction mergeSnapshots(snapshots: VaultSnapshot[]): VaultSnapshot {\n const merged: VaultSnapshot = {}\n\n for (const snap of snapshots) {\n for (const [collection, records] of Object.entries(snap)) {\n if (!merged[collection]) {\n merged[collection] = { ...records }\n continue\n }\n for (const [id, envelope] of Object.entries(records)) {\n const existing = merged[collection][id]\n // Last-write-wins by _ts\n if (!existing || envelope._ts >= existing._ts) {\n merged[collection][id] = envelope\n }\n }\n }\n }\n\n return merged\n}\n","/**\n * Store middleware — composable interceptors for NoydbStore.\n *\n * ```ts\n * const resilient = wrapStore(\n * dynamo({ table: 'myapp' }),\n * withRetry({ maxRetries: 3 }),\n * withLogging({ level: 'debug' }),\n * withCache({ ttlMs: 60_000 }),\n * )\n * ```\n *\n * Each middleware is `(next: NoydbStore) => NoydbStore`. They compose\n * left-to-right: first middleware is outermost (processes requests first,\n * responses last).\n *\n * @module\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../types.js'\n\n// ─── Core composition ───────────────────────────────────────────────────\n\n/**\n * A store middleware function.\n *\n * Takes the next store in the chain and returns a wrapped store. Middlewares\n * compose left-to-right via `wrapStore()`: the first argument is outermost\n * (first to intercept requests, last to process responses).\n *\n * ```ts\n * const mw: StoreMiddleware = (next) => ({\n * ...next,\n * async get(vault, collection, id) {\n * console.log('get', id)\n * return next.get(vault, collection, id)\n * },\n * })\n * ```\n */\nexport type StoreMiddleware = (next: NoydbStore) => NoydbStore\n\n/**\n * Wrap a store with one or more middlewares. Middlewares compose left-to-right.\n */\nexport function wrapStore(store: NoydbStore, ...middlewares: StoreMiddleware[]): NoydbStore {\n let result = store\n // Apply right-to-left so the first middleware is the outermost wrapper\n for (let i = middlewares.length - 1; i >= 0; i--) {\n result = middlewares[i]!(result)\n }\n return result\n}\n\n// ─── withRetry ──────────────────────────────────────────────────────────\n\n/** Options for `withRetry()`. */\nexport interface RetryOptions {\n /** Maximum retry attempts. Default: 3. */\n maxRetries?: number\n /** Base backoff delay in ms. Default: 500. */\n backoffMs?: number\n /** Jitter factor (0-1). Adds random delay up to `backoffMs * jitter`. Default: 0.3. */\n jitter?: number\n /** Only retry on these error codes. Default: retry all errors. */\n retryOn?: string[]\n}\n\n/**\n * Middleware that retries failed store operations with exponential backoff\n * and optional jitter. Useful for transient network errors on DynamoDB/S3.\n *\n * ```ts\n * wrapStore(dynamo({ table: 'myapp' }), withRetry({ maxRetries: 5, retryOn: ['NETWORK_ERROR'] }))\n * ```\n */\nexport function withRetry(opts: RetryOptions = {}): StoreMiddleware {\n const maxRetries = opts.maxRetries ?? 3\n const backoffMs = opts.backoffMs ?? 500\n const jitter = opts.jitter ?? 0.3\n const retryOn = opts.retryOn ? new Set(opts.retryOn) : null\n\n function shouldRetry(err: unknown): boolean {\n if (!retryOn) return true\n if (err && typeof err === 'object' && 'code' in err) {\n return retryOn.has((err as { code: string }).code)\n }\n return true\n }\n\n async function retryable<T>(fn: () => Promise<T>): Promise<T> {\n let lastError: unknown\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await fn()\n } catch (err) {\n lastError = err\n if (attempt >= maxRetries || !shouldRetry(err)) throw err\n const delay = backoffMs * Math.pow(2, attempt) * (1 + Math.random() * jitter)\n await new Promise(r => setTimeout(r, delay))\n }\n }\n throw lastError\n }\n\n return (next) => ({\n ...next,\n name: next.name ? `retry(${next.name})` : 'retry',\n get: (v, c, id) => retryable(() => next.get(v, c, id)),\n put: (v, c, id, env, ev) => retryable(() => next.put(v, c, id, env, ev)),\n delete: (v, c, id) => retryable(() => next.delete(v, c, id)),\n list: (v, c) => retryable(() => next.list(v, c)),\n loadAll: (v) => retryable(() => next.loadAll(v)),\n saveAll: (v, d) => retryable(() => next.saveAll(v, d)),\n })\n}\n\n// ─── withLogging ────────────────────────────────────────────────────────\n\n/** Log level for `withLogging()`. Maps to standard console method names. */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\n/** Options for `withLogging()`. */\nexport interface LoggingOptions {\n /** Minimum log level. Default: 'info'. */\n level?: LogLevel\n /** Custom logger. Default: console. */\n logger?: {\n debug(msg: string, ...args: unknown[]): void\n info(msg: string, ...args: unknown[]): void\n warn(msg: string, ...args: unknown[]): void\n error(msg: string, ...args: unknown[]): void\n }\n /** Log the data payload (envelope contents). Default: false (privacy). */\n logData?: boolean\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 }\n\n/**\n * Middleware that logs every store operation with its method name, arguments,\n * and elapsed duration. Privacy-safe by default: envelope payloads are not\n * logged unless `logData: true` is set.\n */\nexport function withLogging(opts: LoggingOptions = {}): StoreMiddleware {\n const minLevel = LOG_LEVELS[opts.level ?? 'info']\n const logger = opts.logger ?? console\n const logData = opts.logData ?? false\n\n function log(level: LogLevel, method: string, args: Record<string, unknown>, durationMs?: number) {\n if (LOG_LEVELS[level] < minLevel) return\n const parts = [`[noydb:${method}]`, ...Object.entries(args).map(([k, v]) => `${k}=${String(v)}`)]\n if (durationMs !== undefined) parts.push(`${durationMs}ms`)\n logger[level](parts.join(' '))\n }\n\n function timed<T>(method: string, args: Record<string, unknown>, fn: () => Promise<T>): Promise<T> {\n const start = Date.now()\n return fn().then(\n (result) => {\n log('debug', method, args, Date.now() - start)\n return result\n },\n (err) => {\n log('error', method, { ...args, error: (err as Error).message }, Date.now() - start)\n throw err\n },\n )\n }\n\n return (next) => ({\n ...next,\n name: next.name ? `log(${next.name})` : 'log',\n get: (v, c, id) => timed('get', { vault: v, collection: c, id }, () => next.get(v, c, id)),\n put: (v, c, id, env, ev) => timed('put', {\n vault: v, collection: c, id, version: env._v,\n ...(logData ? { data: env._data.slice(0, 40) + '...' } : {}),\n }, () => next.put(v, c, id, env, ev)),\n delete: (v, c, id) => timed('delete', { vault: v, collection: c, id }, () => next.delete(v, c, id)),\n list: (v, c) => timed('list', { vault: v, collection: c }, () => next.list(v, c)),\n loadAll: (v) => timed('loadAll', { vault: v }, () => next.loadAll(v)),\n saveAll: (v, d) => timed('saveAll', { vault: v }, () => next.saveAll(v, d)),\n })\n}\n\n// ─── withMetrics ────────────────────────────────────────────────────────\n\n/**\n * Data emitted to `MetricsOptions.onOperation` after every store call.\n *\n * Carries method name, vault/collection/id context, elapsed duration,\n * and success/failure status. Wire this into your metrics pipeline\n * (DataDog, Prometheus, CloudWatch) to get per-operation latency histograms.\n */\nexport interface StoreOperation {\n method: 'get' | 'put' | 'delete' | 'list' | 'loadAll' | 'saveAll'\n vault: string\n collection?: string\n id?: string\n durationMs: number\n success: boolean\n error?: Error\n}\n\n/** Options for `withMetrics()`. */\nexport interface MetricsOptions {\n /** Called after every store operation. */\n onOperation: (op: StoreOperation) => void\n}\n\n/**\n * Middleware that calls `onOperation` after every store method with timing\n * and success/failure data. Designed for low-overhead integration with\n * metrics systems — the callback is synchronous and fire-and-forget.\n */\nexport function withMetrics(opts: MetricsOptions): StoreMiddleware {\n function tracked<T>(\n method: StoreOperation['method'],\n vault: string,\n fn: () => Promise<T>,\n collection?: string,\n id?: string,\n ): Promise<T> {\n const start = Date.now()\n return fn().then(\n (result) => {\n opts.onOperation({\n method, vault,\n ...(collection !== undefined ? { collection } : {}),\n ...(id !== undefined ? { id } : {}),\n durationMs: Date.now() - start, success: true,\n })\n return result\n },\n (err) => {\n opts.onOperation({\n method, vault,\n ...(collection !== undefined ? { collection } : {}),\n ...(id !== undefined ? { id } : {}),\n durationMs: Date.now() - start, success: false, error: err as Error,\n })\n throw err\n },\n )\n }\n\n return (next) => ({\n ...next,\n name: next.name ? `metrics(${next.name})` : 'metrics',\n get: (v, c, id) => tracked('get', v, () => next.get(v, c, id), c, id),\n put: (v, c, id, env, ev) => tracked('put', v, () => next.put(v, c, id, env, ev), c, id),\n delete: (v, c, id) => tracked('delete', v, () => next.delete(v, c, id), c, id),\n list: (v, c) => tracked('list', v, () => next.list(v, c), c),\n loadAll: (v) => tracked('loadAll', v, () => next.loadAll(v)),\n saveAll: (v, d) => tracked('saveAll', v, () => next.saveAll(v, d)),\n })\n}\n\n// ─── withCircuitBreaker ─────────────────────────────────────────────────\n\n/**\n * Options for `withCircuitBreaker()`.\n *\n * The circuit breaker moves through three states:\n * - `closed`: normal operation.\n * - `open`: store is failing; all calls return fallback values immediately.\n * - `half-open`: one probe call after `resetTimeoutMs` — success closes, failure re-opens.\n */\nexport interface CircuitBreakerOptions {\n /** Number of consecutive failures before opening the circuit. Default: 5. */\n failureThreshold?: number\n /** Time in ms before attempting to half-open the circuit. Default: 30_000. */\n resetTimeoutMs?: number\n /** Called when the circuit opens (store becomes unavailable). */\n onOpen?: () => void\n /** Called when the circuit closes (store recovers). */\n onClose?: () => void\n}\n\ntype CircuitState = 'closed' | 'open' | 'half-open'\n\n/**\n * Middleware that implements the circuit-breaker pattern.\n *\n * When the wrapped store fails `failureThreshold` consecutive times, the\n * circuit opens: subsequent calls return safe fallback values (`null`, `[]`,\n * `{}`) without hitting the store. After `resetTimeoutMs` the circuit\n * half-opens and allows one probe — success closes the circuit, failure\n * keeps it open. Pair with `withRetry` to handle transient errors before\n * they trip the circuit.\n */\nexport function withCircuitBreaker(opts: CircuitBreakerOptions = {}): StoreMiddleware {\n const threshold = opts.failureThreshold ?? 5\n const resetMs = opts.resetTimeoutMs ?? 30_000\n\n let state: CircuitState = 'closed'\n let failures = 0\n let lastFailureTime = 0\n\n function recordSuccess(): void {\n if (state === 'half-open') {\n state = 'closed'\n failures = 0\n opts.onClose?.()\n }\n failures = 0\n }\n\n function recordFailure(): void {\n failures++\n lastFailureTime = Date.now()\n if (failures >= threshold && state === 'closed') {\n state = 'open'\n opts.onOpen?.()\n }\n }\n\n function canAttempt(): boolean {\n if (state === 'closed') return true\n if (state === 'open') {\n if (Date.now() - lastFailureTime >= resetMs) {\n state = 'half-open'\n return true\n }\n return false\n }\n // half-open: allow one attempt\n return true\n }\n\n async function guarded<T>(fn: () => Promise<T>, fallback: T): Promise<T> {\n if (!canAttempt()) return fallback\n try {\n const result = await fn()\n recordSuccess()\n return result\n } catch (err) {\n recordFailure()\n throw err\n }\n }\n\n return (next) => ({\n ...next,\n name: next.name ? `cb(${next.name})` : 'cb',\n get: (v, c, id) => guarded(() => next.get(v, c, id), null),\n put: (v, c, id, env, ev) => guarded(() => next.put(v, c, id, env, ev), undefined),\n delete: (v, c, id) => guarded(() => next.delete(v, c, id), undefined),\n list: (v, c) => guarded(() => next.list(v, c), []),\n loadAll: (v) => guarded(() => next.loadAll(v), {}),\n saveAll: (v, d) => guarded(() => next.saveAll(v, d), undefined),\n })\n}\n\n// ─── withCache (read-through) ───────────────────────────────────────────\n\n/**\n * Options for `withCache()`.\n *\n * The cache is a read-through LRU that caches individual record fetches\n * (`get`). Writes (`put`, `delete`) invalidate the relevant cache entry\n * immediately. `list`, `loadAll`, and `saveAll` bypass the cache.\n *\n * Named `StoreCacheOptions` to distinguish from `CacheOptions` in\n * `@noy-db/hub/collection`, which controls the in-memory decrypted-record LRU.\n */\nexport interface StoreCacheOptions {\n /** Maximum cached entries. Default: 500. */\n maxEntries?: number\n /** Cache TTL in ms. Default: 60_000 (1 minute). 0 = no expiry. */\n ttlMs?: number\n}\n\ninterface CacheEntry {\n envelope: EncryptedEnvelope | null\n cachedAt: number\n}\n\n/**\n * Middleware that adds a read-through LRU cache for `get()` calls.\n *\n * Reduces latency for frequently-read records (e.g. lookup tables, user\n * profiles) by serving repeat reads from memory. Because NOYDB records are\n * encrypted at rest, caching envelopes is safe — the cache holds ciphertext,\n * not plaintext. For write-heavy workloads, the cache provides little benefit\n * and should be omitted to avoid the invalidation overhead.\n */\nexport function withCache(opts: StoreCacheOptions = {}): StoreMiddleware {\n const maxEntries = opts.maxEntries ?? 500\n const ttlMs = opts.ttlMs ?? 60_000\n\n // LRU cache: Map preserves insertion order, we delete+re-insert on access\n const cache = new Map<string, CacheEntry>()\n\n function cacheKey(vault: string, collection: string, id: string): string {\n return `${vault}\\0${collection}\\0${id}`\n }\n\n function getFromCache(key: string): EncryptedEnvelope | null | undefined {\n const entry = cache.get(key)\n if (!entry) return undefined\n if (ttlMs > 0 && Date.now() - entry.cachedAt > ttlMs) {\n cache.delete(key)\n return undefined\n }\n // LRU: move to end\n cache.delete(key)\n cache.set(key, entry)\n return entry.envelope\n }\n\n function setInCache(key: string, envelope: EncryptedEnvelope | null): void {\n // Evict oldest if at capacity\n if (cache.size >= maxEntries) {\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n cache.set(key, { envelope, cachedAt: Date.now() })\n }\n\n function invalidate(key: string): void {\n cache.delete(key)\n }\n\n return (next) => ({\n ...next,\n name: next.name ? `cache(${next.name})` : 'cache',\n\n async get(vault, collection, id) {\n const key = cacheKey(vault, collection, id)\n const cached = getFromCache(key)\n if (cached !== undefined) return cached\n const result = await next.get(vault, collection, id)\n setInCache(key, result)\n return result\n },\n\n async put(vault, collection, id, env, ev) {\n invalidate(cacheKey(vault, collection, id))\n await next.put(vault, collection, id, env, ev)\n setInCache(cacheKey(vault, collection, id), env)\n },\n\n async delete(vault, collection, id) {\n invalidate(cacheKey(vault, collection, id))\n await next.delete(vault, collection, id)\n },\n\n list: (v, c) => next.list(v, c),\n loadAll: (v) => next.loadAll(v),\n saveAll: (v, d) => next.saveAll(v, d),\n })\n}\n\n// ─── withHealthCheck ────────────────────────────────────────────────────\n\nexport interface HealthCheckOptions {\n /** Ping interval in ms. Default: 30_000. */\n checkIntervalMs?: number\n /** Suspend after N consecutive ping failures. Default: 3. */\n suspendAfterFailures?: number\n /** Resume after N consecutive ping successes. Default: 1. */\n resumeAfterSuccess?: number\n /** Called when the store is auto-suspended. */\n onSuspend?: () => void\n /** Called when the store is auto-resumed. */\n onResume?: () => void\n /**\n * Custom health check. Default: calls `store.ping()` if available,\n * otherwise attempts a `list()` on a sentinel collection.\n */\n check?: () => Promise<boolean>\n}\n\n/**\n * Auto-suspends a store when health checks fail, auto-resumes when they recover.\n *\n * When suspended, `get` returns null, `put`/`delete` are no-ops, `list` returns [].\n * This is identical to the `NullStore` behavior from `routeStore.suspend()`.\n */\nexport function withHealthCheck(opts: HealthCheckOptions = {}): StoreMiddleware {\n const intervalMs = opts.checkIntervalMs ?? 30_000\n const failThreshold = opts.suspendAfterFailures ?? 3\n const successThreshold = opts.resumeAfterSuccess ?? 1\n\n let isSuspended = false\n let consecutiveFailures = 0\n let consecutiveSuccesses = 0\n\n return (next) => {\n const checkFn = opts.check ?? (\n next.ping\n ? () => next.ping!()\n : async () => { await next.list('__health__', '__ping__'); return true }\n )\n\n async function doCheck(): Promise<void> {\n try {\n const ok = await checkFn()\n if (ok) {\n consecutiveFailures = 0\n consecutiveSuccesses++\n if (isSuspended && consecutiveSuccesses >= successThreshold) {\n isSuspended = false\n consecutiveSuccesses = 0\n opts.onResume?.()\n }\n } else {\n throw new Error('Health check returned false')\n }\n } catch {\n consecutiveSuccesses = 0\n consecutiveFailures++\n if (!isSuspended && consecutiveFailures >= failThreshold) {\n isSuspended = true\n consecutiveFailures = 0\n opts.onSuspend?.()\n }\n }\n }\n\n // Start checking\n setInterval(() => { void doCheck() }, intervalMs)\n\n const wrapped: NoydbStore = {\n ...next,\n name: next.name ? `health(${next.name})` : 'health',\n\n async get(v, c, id) { return isSuspended ? null : next.get(v, c, id) },\n async put(v, c, id, env, ev) { if (!isSuspended) await next.put(v, c, id, env, ev) },\n async delete(v, c, id) { if (!isSuspended) await next.delete(v, c, id) },\n async list(v, c) { return isSuspended ? [] : next.list(v, c) },\n async loadAll(v) { return isSuspended ? {} : next.loadAll(v) },\n async saveAll(v, d) { if (!isSuspended) await next.saveAll(v, d) },\n }\n\n return wrapped\n }\n}\n"],"mappings":"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","names":["store","s","mergeSnapshots"]}