@noy-db/hub 0.2.0-pre.2 → 0.2.0-pre.21

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 (368) hide show
  1. package/README.md +126 -0
  2. package/dist/aggregate/index.cjs +643 -37
  3. package/dist/aggregate/index.cjs.map +1 -1
  4. package/dist/aggregate/index.d.cts +3 -2
  5. package/dist/aggregate/index.d.ts +3 -2
  6. package/dist/aggregate/index.js +9 -8
  7. package/dist/aggregate/index.js.map +1 -1
  8. package/dist/attestation/index.cjs.map +1 -1
  9. package/dist/attestation/index.d.cts +7 -5
  10. package/dist/attestation/index.d.ts +7 -5
  11. package/dist/attestation/index.js +6 -6
  12. package/dist/blobs/index.cjs +509 -22
  13. package/dist/blobs/index.cjs.map +1 -1
  14. package/dist/blobs/index.d.cts +9 -7
  15. package/dist/blobs/index.d.ts +9 -7
  16. package/dist/blobs/index.js +11 -6
  17. package/dist/blobs/index.js.map +1 -1
  18. package/dist/bundle/index.cjs +7886 -841
  19. package/dist/bundle/index.cjs.map +1 -1
  20. package/dist/bundle/index.d.cts +20 -18
  21. package/dist/bundle/index.d.ts +20 -18
  22. package/dist/bundle/index.js +24 -13
  23. package/dist/bundle/index.js.map +1 -1
  24. package/dist/{chunk-PFSNOPBQ.js → chunk-2XA2ZML4.js} +31 -3
  25. package/dist/chunk-2XA2ZML4.js.map +1 -0
  26. package/dist/{chunk-2PAQNPE3.js → chunk-37VGJM3T.js} +37 -2
  27. package/dist/chunk-37VGJM3T.js.map +1 -0
  28. package/dist/{chunk-7BRE6EUA.js → chunk-3HNKR65T.js} +4 -4
  29. package/dist/chunk-3HNKR65T.js.map +1 -0
  30. package/dist/{chunk-Y2RKOPNC.js → chunk-5YTXYPES.js} +46 -10
  31. package/dist/chunk-5YTXYPES.js.map +1 -0
  32. package/dist/{chunk-OVZDFEOR.js → chunk-6QAZ5O6X.js} +2 -2
  33. package/dist/chunk-6QAZ5O6X.js.map +1 -0
  34. package/dist/{chunk-RTZVQAJ7.js → chunk-6QE4DUYC.js} +19 -4
  35. package/dist/chunk-6QE4DUYC.js.map +1 -0
  36. package/dist/{chunk-7Q5PLD5C.js → chunk-7MRT7EPB.js} +3 -3
  37. package/dist/{chunk-E535SAN4.js → chunk-7PH4OPBZ.js} +4258 -520
  38. package/dist/chunk-7PH4OPBZ.js.map +1 -0
  39. package/dist/{chunk-PEULZC6M.js → chunk-A3JMGXPG.js} +8 -1
  40. package/dist/chunk-A3JMGXPG.js.map +1 -0
  41. package/dist/{chunk-UMLVJTYV.js → chunk-ADB7GPM3.js} +7 -4
  42. package/dist/chunk-ADB7GPM3.js.map +1 -0
  43. package/dist/{chunk-G6FRSBKK.js → chunk-AI4USDRI.js} +4 -4
  44. package/dist/chunk-BZW5IL43.js +151 -0
  45. package/dist/chunk-BZW5IL43.js.map +1 -0
  46. package/dist/chunk-C2RJVZZL.js +123 -0
  47. package/dist/chunk-C2RJVZZL.js.map +1 -0
  48. package/dist/{chunk-UND4XIB6.js → chunk-C6W5KVDV.js} +52 -38
  49. package/dist/chunk-C6W5KVDV.js.map +1 -0
  50. package/dist/chunk-CQYEDODS.js +125 -0
  51. package/dist/chunk-CQYEDODS.js.map +1 -0
  52. package/dist/{chunk-NWZ3I6R6.js → chunk-EYK72OTL.js} +5 -5
  53. package/dist/{chunk-7BUTTVMR.js → chunk-F5GWNSE2.js} +2 -2
  54. package/dist/{chunk-AHPFONIL.js → chunk-F5ILTHMU.js} +5 -5
  55. package/dist/{chunk-Q6W2CMEJ.js → chunk-FRRJIUSI.js} +18 -5
  56. package/dist/chunk-FRRJIUSI.js.map +1 -0
  57. package/dist/{chunk-YMYK7US4.js → chunk-GJTKMME7.js} +2 -2
  58. package/dist/chunk-GJTKMME7.js.map +1 -0
  59. package/dist/{chunk-EUYOGYGV.js → chunk-HYJMAV53.js} +6 -6
  60. package/dist/chunk-HYJMAV53.js.map +1 -0
  61. package/dist/{chunk-QPEXPHJR.js → chunk-I3IYTUUI.js} +4 -4
  62. package/dist/{chunk-3QAKZ37R.js → chunk-IVZWHIEK.js} +5 -5
  63. package/dist/{chunk-PLI5TV7N.js → chunk-IW4L4X65.js} +2 -2
  64. package/dist/chunk-IW4L4X65.js.map +1 -0
  65. package/dist/{chunk-3Z2TPHC4.js → chunk-IY24WS2P.js} +69 -5
  66. package/dist/chunk-IY24WS2P.js.map +1 -0
  67. package/dist/{chunk-HXJXPZRE.js → chunk-J6RGRZOY.js} +10 -3
  68. package/dist/chunk-J6RGRZOY.js.map +1 -0
  69. package/dist/{chunk-3S4BJX25.js → chunk-JBBWALNI.js} +2 -2
  70. package/dist/chunk-JBBWALNI.js.map +1 -0
  71. package/dist/{chunk-7Z23ZFLV.js → chunk-JDCPRJVS.js} +5 -5
  72. package/dist/chunk-JDCPRJVS.js.map +1 -0
  73. package/dist/{chunk-243PNUA6.js → chunk-JOK73NDT.js} +3 -3
  74. package/dist/chunk-JTI57WRT.js +164 -0
  75. package/dist/chunk-JTI57WRT.js.map +1 -0
  76. package/dist/{chunk-VRBCTEKQ.js → chunk-JYNH4FIM.js} +233 -11
  77. package/dist/chunk-JYNH4FIM.js.map +1 -0
  78. package/dist/{chunk-TBKOGSYR.js → chunk-KOAJ3TZM.js} +27 -5
  79. package/dist/chunk-KOAJ3TZM.js.map +1 -0
  80. package/dist/{chunk-YTXSFG3C.js → chunk-MBXKRHSS.js} +50 -20
  81. package/dist/chunk-MBXKRHSS.js.map +1 -0
  82. package/dist/{chunk-MUWOSVEP.js → chunk-NSXNXLYM.js} +10 -2
  83. package/dist/chunk-NSXNXLYM.js.map +1 -0
  84. package/dist/{chunk-J4KLMEUL.js → chunk-NV4IHBZS.js} +664 -51
  85. package/dist/chunk-NV4IHBZS.js.map +1 -0
  86. package/dist/{chunk-LRAZDV5X.js → chunk-O5XKZCUD.js} +31 -8
  87. package/dist/chunk-O5XKZCUD.js.map +1 -0
  88. package/dist/{chunk-W3XXT26A.js → chunk-OTWT6BAJ.js} +358 -3
  89. package/dist/chunk-OTWT6BAJ.js.map +1 -0
  90. package/dist/{chunk-XG3PTSCD.js → chunk-PDVP3C2I.js} +1 -1
  91. package/dist/chunk-PDVP3C2I.js.map +1 -0
  92. package/dist/{chunk-GIV6DWBG.js → chunk-S45MDEEF.js} +44 -5
  93. package/dist/chunk-S45MDEEF.js.map +1 -0
  94. package/dist/{chunk-VK5EER6C.js → chunk-SQKAECUL.js} +2 -2
  95. package/dist/{chunk-FAQVNJD4.js → chunk-SQOK5UM6.js} +12 -2
  96. package/dist/{chunk-FAQVNJD4.js.map → chunk-SQOK5UM6.js.map} +1 -1
  97. package/dist/chunk-STNPB3UM.js +9 -0
  98. package/dist/chunk-STNPB3UM.js.map +1 -0
  99. package/dist/{chunk-YS3POABP.js → chunk-TA6HPKWQ.js} +1 -1
  100. package/dist/chunk-TA6HPKWQ.js.map +1 -0
  101. package/dist/{chunk-4HIL6AHQ.js → chunk-TAMRU7A2.js} +4 -4
  102. package/dist/{chunk-QXQRKXCU.js → chunk-TGIJTNM3.js} +2 -2
  103. package/dist/chunk-TNH5SLCD.js +361 -0
  104. package/dist/chunk-TNH5SLCD.js.map +1 -0
  105. package/dist/{chunk-VPSUZLOJ.js → chunk-TYMDCIQM.js} +31 -5
  106. package/dist/chunk-TYMDCIQM.js.map +1 -0
  107. package/dist/chunk-U2XSUCDF.js +524 -0
  108. package/dist/chunk-U2XSUCDF.js.map +1 -0
  109. package/dist/{chunk-3Y53S2SA.js → chunk-UU6M64HI.js} +4 -4
  110. package/dist/{chunk-VCGTOS2A.js → chunk-WE2BUQD2.js} +3 -3
  111. package/dist/chunk-WE2BUQD2.js.map +1 -0
  112. package/dist/{chunk-JYQTXEIO.js → chunk-WWVJXBOT.js} +449 -29
  113. package/dist/chunk-WWVJXBOT.js.map +1 -0
  114. package/dist/chunk-YPIOFSN3.js +129 -0
  115. package/dist/chunk-YPIOFSN3.js.map +1 -0
  116. package/dist/chunk-ZC7J6ZYV.js +7 -0
  117. package/dist/chunk-ZC7J6ZYV.js.map +1 -0
  118. package/dist/{chunk-5ZGZ6HIZ.js → chunk-ZONKSLF2.js} +30 -7
  119. package/dist/chunk-ZONKSLF2.js.map +1 -0
  120. package/dist/consent/index.cjs.map +1 -1
  121. package/dist/consent/index.d.cts +8 -6
  122. package/dist/consent/index.d.ts +8 -6
  123. package/dist/consent/index.js +3 -3
  124. package/dist/{crypto-5ZDIY3NG.js → crypto-456N7UVX.js} +7 -3
  125. package/dist/{delegation-QYXZW25W.js → delegation-DP4COTXB.js} +5 -5
  126. package/dist/derivations/index.cjs +124 -6
  127. package/dist/derivations/index.cjs.map +1 -1
  128. package/dist/derivations/index.d.cts +11 -9
  129. package/dist/derivations/index.d.ts +11 -9
  130. package/dist/derivations/index.js +8 -6
  131. package/dist/{dev-unlock-DQCNDfFp.d.cts → dev-unlock-CY0HIZA0.d.cts} +1 -1
  132. package/dist/{dev-unlock-utkybTKb.d.ts → dev-unlock-CpKSkl2c.d.ts} +1 -1
  133. package/dist/discriminant-BN9REW3o.d.cts +60 -0
  134. package/dist/discriminant-BN9REW3o.d.ts +60 -0
  135. package/dist/errors-Dkc_fi-S.d.cts +1467 -0
  136. package/dist/errors-Dkc_fi-S.d.ts +1467 -0
  137. package/dist/executor-4IEW4KG5.js +8 -0
  138. package/dist/executor-KYJCJCIN.js +12 -0
  139. package/dist/executor-W7VIBOBZ.js +8 -0
  140. package/dist/{fanout-sidecar-VJ52RIEY.js → fanout-sidecar-YXNAEZ33.js} +2 -2
  141. package/dist/fanout-sidecar-YXNAEZ33.js.map +1 -0
  142. package/dist/forget/index.cjs +43 -0
  143. package/dist/forget/index.cjs.map +1 -0
  144. package/dist/forget/index.d.cts +1 -0
  145. package/dist/forget/index.d.ts +1 -0
  146. package/dist/forget/index.js +14 -0
  147. package/dist/guards/index.cjs +144 -4
  148. package/dist/guards/index.cjs.map +1 -1
  149. package/dist/guards/index.d.cts +16 -8
  150. package/dist/guards/index.d.ts +16 -8
  151. package/dist/guards/index.js +13 -7
  152. package/dist/{hash-jDowCrK2.d.cts → hash-BSd0-_L8.d.cts} +1 -1
  153. package/dist/{hash-DcoYWfJ_.d.ts → hash-BnBQx39y.d.ts} +1 -1
  154. package/dist/history/index.cjs +28 -5
  155. package/dist/history/index.cjs.map +1 -1
  156. package/dist/history/index.d.cts +9 -7
  157. package/dist/history/index.d.ts +9 -7
  158. package/dist/history/index.js +9 -7
  159. package/dist/history/index.js.map +1 -1
  160. package/dist/i18n/index.cjs +356 -26
  161. package/dist/i18n/index.cjs.map +1 -1
  162. package/dist/i18n/index.d.cts +8 -6
  163. package/dist/i18n/index.d.ts +8 -6
  164. package/dist/i18n/index.js +36 -15
  165. package/dist/i18n/index.js.map +1 -1
  166. package/dist/index-BMmajblo.d.cts +362 -0
  167. package/dist/index-BMmajblo.d.ts +362 -0
  168. package/dist/{index-BCKdioeh.d.ts → index-Bm9hIY7t.d.ts} +169 -1127
  169. package/dist/{index-BMjrzNZr.d.cts → index-tZqVB9g5.d.cts} +169 -1127
  170. package/dist/index.cjs +10286 -2168
  171. package/dist/index.cjs.map +1 -1
  172. package/dist/index.d.cts +258 -23
  173. package/dist/index.d.ts +258 -23
  174. package/dist/index.js +443 -110
  175. package/dist/index.js.map +1 -1
  176. package/dist/indexing/index.cjs +97 -32
  177. package/dist/indexing/index.cjs.map +1 -1
  178. package/dist/indexing/index.d.cts +3 -3
  179. package/dist/indexing/index.d.ts +3 -3
  180. package/dist/indexing/index.js +4 -4
  181. package/dist/issue-JXC6T2QR.js +12 -0
  182. package/dist/{lazy-builder-Rpd-V3jP.d.ts → lazy-builder-ChSqcF5t.d.ts} +2 -2
  183. package/dist/{lazy-builder-C-rPfWG0.d.cts → lazy-builder-eYZzLEL1.d.cts} +2 -2
  184. package/dist/{ledger-3IU5GMXA.js → ledger-I7JUYP4L.js} +6 -6
  185. package/dist/materialized-views/index.cjs +687 -13
  186. package/dist/materialized-views/index.cjs.map +1 -1
  187. package/dist/materialized-views/index.d.cts +23 -20
  188. package/dist/materialized-views/index.d.ts +23 -20
  189. package/dist/materialized-views/index.js +8 -7
  190. package/dist/mime-magic-BnJCGJzB.d.cts +103 -0
  191. package/dist/mime-magic-CjSyakO4.d.ts +103 -0
  192. package/dist/noydb-ZZCRF6TE.js +38 -0
  193. package/dist/overlay-views/index.cjs +58 -18
  194. package/dist/overlay-views/index.cjs.map +1 -1
  195. package/dist/overlay-views/index.d.cts +32 -12
  196. package/dist/overlay-views/index.d.ts +32 -12
  197. package/dist/overlay-views/index.js +6 -6
  198. package/dist/periods/index.cjs.map +1 -1
  199. package/dist/periods/index.d.cts +8 -6
  200. package/dist/periods/index.d.ts +8 -6
  201. package/dist/periods/index.js +6 -6
  202. package/dist/{predicate-Dnu81tsS.d.cts → predicate-BmhBSPCH.d.cts} +87 -5
  203. package/dist/{predicate-Dnu81tsS.d.ts → predicate-BmhBSPCH.d.ts} +87 -5
  204. package/dist/{public-envelope-U3CMEOMV.js → public-envelope-5XRTUNKF.js} +4 -4
  205. package/dist/query/index.cjs +1438 -130
  206. package/dist/query/index.cjs.map +1 -1
  207. package/dist/query/index.d.cts +4 -3
  208. package/dist/query/index.d.ts +4 -3
  209. package/dist/query/index.js +13 -6
  210. package/dist/read-only-facade-EX6WZZBP.js +7 -0
  211. package/dist/registry-ATRHOG5B.js +8 -0
  212. package/dist/registry-DKEXOJVO.js +7 -0
  213. package/dist/registry-LEHB26TY.js +8 -0
  214. package/dist/{registry-3ALP62P6.js → registry-NWHOLD5M.js} +3 -3
  215. package/dist/{revoke-KY2GB4KP.js → revoke-5IEK22KT.js} +6 -6
  216. package/dist/sealed-record/index.cjs +139 -0
  217. package/dist/sealed-record/index.cjs.map +1 -0
  218. package/dist/sealed-record/index.d.cts +123 -0
  219. package/dist/sealed-record/index.d.ts +123 -0
  220. package/dist/sealed-record/index.js +42 -0
  221. package/dist/sealed-record/index.js.map +1 -0
  222. package/dist/session/index.cjs.map +1 -1
  223. package/dist/session/index.d.cts +9 -7
  224. package/dist/session/index.d.ts +9 -7
  225. package/dist/session/index.js +3 -3
  226. package/dist/shadow/index.cjs.map +1 -1
  227. package/dist/shadow/index.d.cts +8 -6
  228. package/dist/shadow/index.d.ts +8 -6
  229. package/dist/shadow/index.js +2 -2
  230. package/dist/{signer-GRI5TZKH.js → signer-I6YARZQA.js} +5 -5
  231. package/dist/snapshots/index.cjs +937 -0
  232. package/dist/snapshots/index.cjs.map +1 -0
  233. package/dist/snapshots/index.d.cts +30 -0
  234. package/dist/snapshots/index.d.ts +30 -0
  235. package/dist/snapshots/index.js +152 -0
  236. package/dist/snapshots/index.js.map +1 -0
  237. package/dist/{stale-OTOF3FH7.js → stale-CPESGAPL.js} +2 -2
  238. package/dist/stale-CPESGAPL.js.map +1 -0
  239. package/dist/state-vault-JR3CFGNP.js +14 -0
  240. package/dist/state-vault-JR3CFGNP.js.map +1 -0
  241. package/dist/store/index.cjs +8 -0
  242. package/dist/store/index.cjs.map +1 -1
  243. package/dist/store/index.d.cts +15 -6
  244. package/dist/store/index.d.ts +15 -6
  245. package/dist/store/index.js +2 -2
  246. package/dist/{strategy-DSTrsZ8t.d.ts → strategy-54eIwox5.d.ts} +456 -7
  247. package/dist/{strategy-DSTrsZ8t.d.cts → strategy-WtB-jXYv.d.cts} +456 -7
  248. package/dist/sync/index.cjs.map +1 -1
  249. package/dist/sync/index.d.cts +7 -5
  250. package/dist/sync/index.d.ts +7 -5
  251. package/dist/sync/index.js +4 -4
  252. package/dist/team/index.cjs +1 -1
  253. package/dist/team/index.cjs.map +1 -1
  254. package/dist/team/index.d.cts +8 -6
  255. package/dist/team/index.d.ts +8 -6
  256. package/dist/team/index.js +8 -8
  257. package/dist/transition-guard-D4bfIAiW.d.ts +165 -0
  258. package/dist/transition-guard-Dmpqzg-_.d.cts +165 -0
  259. package/dist/tx/index.cjs +155 -5
  260. package/dist/tx/index.cjs.map +1 -1
  261. package/dist/tx/index.d.cts +27 -9
  262. package/dist/tx/index.d.ts +27 -9
  263. package/dist/tx/index.js +61 -4
  264. package/dist/tx/index.js.map +1 -1
  265. package/dist/{types-BoFFiskX.d.ts → types-DLfWFr6U.d.ts} +3997 -1262
  266. package/dist/{types-DJG8HG6F.d.cts → types-DyOI6XZ_.d.cts} +3997 -1262
  267. package/dist/{ulid-BmBgooGm.d.ts → ulid-B2L_aqVA.d.ts} +19 -19
  268. package/dist/{ulid-C7ms9oli.d.cts → ulid-LaxfH2tK.d.cts} +19 -19
  269. package/dist/util/index.cjs +7 -0
  270. package/dist/util/index.cjs.map +1 -1
  271. package/dist/util/index.d.cts +2 -0
  272. package/dist/util/index.d.ts +2 -0
  273. package/dist/util/index.js +5 -1
  274. package/dist/util/index.js.map +1 -1
  275. package/dist/vault-group-BB246VIM.js +804 -0
  276. package/dist/vault-group-BB246VIM.js.map +1 -0
  277. package/dist/{with-materialized-view-CqnRwI2S.d.ts → with-materialized-view-CeZYGJVf.d.cts} +2 -2
  278. package/dist/{with-materialized-view-BbEPFIIJ.d.cts → with-materialized-view-DNULSxoP.d.ts} +2 -2
  279. package/dist/{with-overlayed-view-Ct1fSJt-.d.ts → with-overlayed-view-C9joG7UZ.d.ts} +2 -2
  280. package/dist/{with-overlayed-view-bwlmmFjx.d.cts → with-overlayed-view-kdcPGHih.d.cts} +2 -2
  281. package/dist/with-rollup-DJDbrxjf.d.ts +47 -0
  282. package/dist/with-rollup-s58XAeWO.d.cts +47 -0
  283. package/package.json +35 -4
  284. package/dist/chunk-2PAQNPE3.js.map +0 -1
  285. package/dist/chunk-3S4BJX25.js.map +0 -1
  286. package/dist/chunk-3XHOCQK4.js +0 -118
  287. package/dist/chunk-3XHOCQK4.js.map +0 -1
  288. package/dist/chunk-3Z2TPHC4.js.map +0 -1
  289. package/dist/chunk-5ZGZ6HIZ.js.map +0 -1
  290. package/dist/chunk-7BRE6EUA.js.map +0 -1
  291. package/dist/chunk-7Z23ZFLV.js.map +0 -1
  292. package/dist/chunk-CXSCDO5T.js +0 -51
  293. package/dist/chunk-CXSCDO5T.js.map +0 -1
  294. package/dist/chunk-E535SAN4.js.map +0 -1
  295. package/dist/chunk-EUYOGYGV.js.map +0 -1
  296. package/dist/chunk-GIV6DWBG.js.map +0 -1
  297. package/dist/chunk-HXJXPZRE.js.map +0 -1
  298. package/dist/chunk-J4KLMEUL.js.map +0 -1
  299. package/dist/chunk-JYQTXEIO.js.map +0 -1
  300. package/dist/chunk-LRAZDV5X.js.map +0 -1
  301. package/dist/chunk-MRIBLZL3.js +0 -86
  302. package/dist/chunk-MRIBLZL3.js.map +0 -1
  303. package/dist/chunk-MUWOSVEP.js.map +0 -1
  304. package/dist/chunk-OVZDFEOR.js.map +0 -1
  305. package/dist/chunk-PEULZC6M.js.map +0 -1
  306. package/dist/chunk-PFSNOPBQ.js.map +0 -1
  307. package/dist/chunk-PLI5TV7N.js.map +0 -1
  308. package/dist/chunk-Q6W2CMEJ.js.map +0 -1
  309. package/dist/chunk-RTZVQAJ7.js.map +0 -1
  310. package/dist/chunk-TBKOGSYR.js.map +0 -1
  311. package/dist/chunk-UMLVJTYV.js.map +0 -1
  312. package/dist/chunk-UND4XIB6.js.map +0 -1
  313. package/dist/chunk-VCGTOS2A.js.map +0 -1
  314. package/dist/chunk-VE6YVP32.js +0 -19
  315. package/dist/chunk-VE6YVP32.js.map +0 -1
  316. package/dist/chunk-VPSUZLOJ.js.map +0 -1
  317. package/dist/chunk-VRBCTEKQ.js.map +0 -1
  318. package/dist/chunk-W3XXT26A.js.map +0 -1
  319. package/dist/chunk-XG3PTSCD.js.map +0 -1
  320. package/dist/chunk-Y2RKOPNC.js.map +0 -1
  321. package/dist/chunk-YMYK7US4.js.map +0 -1
  322. package/dist/chunk-YS3POABP.js.map +0 -1
  323. package/dist/chunk-YTXSFG3C.js.map +0 -1
  324. package/dist/executor-AS2IDHKZ.js +0 -11
  325. package/dist/executor-HLXFXNFM.js +0 -8
  326. package/dist/executor-HN6YBHZ5.js +0 -8
  327. package/dist/fanout-sidecar-VJ52RIEY.js.map +0 -1
  328. package/dist/issue-ORP37MVW.js +0 -12
  329. package/dist/mime-magic-CBBSOkjm.d.cts +0 -50
  330. package/dist/mime-magic-CBBSOkjm.d.ts +0 -50
  331. package/dist/noydb-5H3C24GG.js +0 -34
  332. package/dist/read-only-facade-ITU6L7BL.js +0 -7
  333. package/dist/registry-7HE6VJGC.js +0 -8
  334. package/dist/registry-PSIPG2QR.js +0 -8
  335. package/dist/registry-RFGGMVNJ.js +0 -7
  336. package/dist/with-derivation-BKXXa8Vt.d.ts +0 -13
  337. package/dist/with-derivation-BjQ7q4NE.d.cts +0 -13
  338. package/dist/with-guard-C25yNjzd.d.ts +0 -18
  339. package/dist/with-guard-DQme5DKE.d.cts +0 -18
  340. /package/dist/{chunk-7Q5PLD5C.js.map → chunk-7MRT7EPB.js.map} +0 -0
  341. /package/dist/{chunk-G6FRSBKK.js.map → chunk-AI4USDRI.js.map} +0 -0
  342. /package/dist/{chunk-NWZ3I6R6.js.map → chunk-EYK72OTL.js.map} +0 -0
  343. /package/dist/{chunk-7BUTTVMR.js.map → chunk-F5GWNSE2.js.map} +0 -0
  344. /package/dist/{chunk-AHPFONIL.js.map → chunk-F5ILTHMU.js.map} +0 -0
  345. /package/dist/{chunk-QPEXPHJR.js.map → chunk-I3IYTUUI.js.map} +0 -0
  346. /package/dist/{chunk-3QAKZ37R.js.map → chunk-IVZWHIEK.js.map} +0 -0
  347. /package/dist/{chunk-243PNUA6.js.map → chunk-JOK73NDT.js.map} +0 -0
  348. /package/dist/{chunk-VK5EER6C.js.map → chunk-SQKAECUL.js.map} +0 -0
  349. /package/dist/{chunk-4HIL6AHQ.js.map → chunk-TAMRU7A2.js.map} +0 -0
  350. /package/dist/{chunk-QXQRKXCU.js.map → chunk-TGIJTNM3.js.map} +0 -0
  351. /package/dist/{chunk-3Y53S2SA.js.map → chunk-UU6M64HI.js.map} +0 -0
  352. /package/dist/{crypto-5ZDIY3NG.js.map → crypto-456N7UVX.js.map} +0 -0
  353. /package/dist/{delegation-QYXZW25W.js.map → delegation-DP4COTXB.js.map} +0 -0
  354. /package/dist/{executor-AS2IDHKZ.js.map → executor-4IEW4KG5.js.map} +0 -0
  355. /package/dist/{executor-HLXFXNFM.js.map → executor-KYJCJCIN.js.map} +0 -0
  356. /package/dist/{executor-HN6YBHZ5.js.map → executor-W7VIBOBZ.js.map} +0 -0
  357. /package/dist/{issue-ORP37MVW.js.map → forget/index.js.map} +0 -0
  358. /package/dist/{ledger-3IU5GMXA.js.map → issue-JXC6T2QR.js.map} +0 -0
  359. /package/dist/{noydb-5H3C24GG.js.map → ledger-I7JUYP4L.js.map} +0 -0
  360. /package/dist/{public-envelope-U3CMEOMV.js.map → noydb-ZZCRF6TE.js.map} +0 -0
  361. /package/dist/{read-only-facade-ITU6L7BL.js.map → public-envelope-5XRTUNKF.js.map} +0 -0
  362. /package/dist/{registry-3ALP62P6.js.map → read-only-facade-EX6WZZBP.js.map} +0 -0
  363. /package/dist/{registry-7HE6VJGC.js.map → registry-ATRHOG5B.js.map} +0 -0
  364. /package/dist/{registry-PSIPG2QR.js.map → registry-DKEXOJVO.js.map} +0 -0
  365. /package/dist/{registry-RFGGMVNJ.js.map → registry-LEHB26TY.js.map} +0 -0
  366. /package/dist/{revoke-KY2GB4KP.js.map → registry-NWHOLD5M.js.map} +0 -0
  367. /package/dist/{signer-GRI5TZKH.js.map → revoke-5IEK22KT.js.map} +0 -0
  368. /package/dist/{stale-OTOF3FH7.js.map → signer-I6YARZQA.js.map} +0 -0
@@ -90,7 +90,9 @@ __export(crypto_exports, {
90
90
  generateSalt: () => generateSalt,
91
91
  hmacSha256Hex: () => hmacSha256Hex,
92
92
  sha256Hex: () => sha256Hex,
93
+ unwrapCek: () => unwrapCek,
93
94
  unwrapKey: () => unwrapKey,
95
+ wrapCek: () => wrapCek,
94
96
  wrapKey: () => wrapKey
95
97
  });
96
98
  async function deriveKey(passphrase, salt) {
@@ -141,6 +143,39 @@ async function unwrapKey(wrappedBase64, kek) {
141
143
  throw new InvalidKeyError();
142
144
  }
143
145
  }
146
+ async function asKwKey(dek) {
147
+ const rawDek = await subtle.exportKey("raw", dek);
148
+ const hkdfKey = await subtle.importKey("raw", rawDek, "HKDF", false, ["deriveBits"]);
149
+ const salt = new TextEncoder().encode("noydb-cek-wrap");
150
+ const info = new TextEncoder().encode("v1");
151
+ const bits = await subtle.deriveBits(
152
+ { name: "HKDF", hash: "SHA-256", salt, info },
153
+ hkdfKey,
154
+ KEY_BITS
155
+ );
156
+ return subtle.importKey("raw", bits, "AES-KW", false, ["wrapKey", "unwrapKey"]);
157
+ }
158
+ async function wrapCek(cek, dek) {
159
+ const kw = await asKwKey(dek);
160
+ const wrapped = await subtle.wrapKey("raw", cek, kw, "AES-KW");
161
+ return bufferToBase64(wrapped);
162
+ }
163
+ async function unwrapCek(wrappedBase64, dek) {
164
+ const kw = await asKwKey(dek);
165
+ try {
166
+ return await subtle.unwrapKey(
167
+ "raw",
168
+ base64ToBuffer(wrappedBase64),
169
+ kw,
170
+ "AES-KW",
171
+ { name: "AES-GCM", length: KEY_BITS },
172
+ true,
173
+ ["encrypt", "decrypt"]
174
+ );
175
+ } catch {
176
+ throw new InvalidKeyError();
177
+ }
178
+ }
144
179
  async function encrypt(plaintext, dek) {
145
180
  const iv = generateIV();
146
181
  const encoded = new TextEncoder().encode(plaintext);
@@ -362,7 +397,9 @@ __export(blobs_exports, {
362
397
  createExportBlobsHandle: () => createExportBlobsHandle,
363
398
  detectMagic: () => detectMagic,
364
399
  detectMimeType: () => detectMimeType,
400
+ importExternalObjects: () => importExternalObjects,
365
401
  isPreCompressed: () => isPreCompressed,
402
+ memoryObjectProjection: () => memoryObjectProjection,
366
403
  runCompaction: () => runCompaction,
367
404
  withBlobs: () => withBlobs
368
405
  });
@@ -373,6 +410,11 @@ var NOYDB_FORMAT_VERSION = 1;
373
410
 
374
411
  // src/blobs/blob-set.ts
375
412
  init_crypto();
413
+
414
+ // src/record-keys/index.ts
415
+ init_crypto();
416
+
417
+ // src/blobs/blob-set.ts
376
418
  init_errors();
377
419
 
378
420
  // src/blobs/mime-magic.ts
@@ -600,11 +642,8 @@ async function compressBytes(data) {
600
642
  if (typeof CompressionStream === "undefined") {
601
643
  return { bytes: data, algorithm: "none" };
602
644
  }
603
- const cs = new CompressionStream("gzip");
604
- const writer = cs.writable.getWriter();
605
- await writer.write(data);
606
- await writer.close();
607
- const buf = await new Response(cs.readable).arrayBuffer();
645
+ const piped = new Response(data).body.pipeThrough(new CompressionStream("gzip"));
646
+ const buf = await new Response(piped).arrayBuffer();
608
647
  return { bytes: new Uint8Array(buf), algorithm: "gzip" };
609
648
  }
610
649
  async function decompressBytes(data) {
@@ -613,11 +652,8 @@ async function decompressBytes(data) {
613
652
  "[noy-db] DecompressionStream not available \u2014 cannot decompress blob chunk"
614
653
  );
615
654
  }
616
- const ds = new DecompressionStream("gzip");
617
- const writer = ds.writable.getWriter();
618
- await writer.write(data);
619
- await writer.close();
620
- const buf = await new Response(ds.readable).arrayBuffer();
655
+ const piped = new Response(data).body.pipeThrough(new DecompressionStream("gzip"));
656
+ const buf = await new Response(piped).arrayBuffer();
621
657
  return new Uint8Array(buf);
622
658
  }
623
659
  function concatChunks(chunks) {
@@ -642,6 +678,10 @@ var BlobSet = class {
642
678
  encrypted;
643
679
  userId;
644
680
  maxBlobBytes;
681
+ erasableBlobs;
682
+ debugPlaintext;
683
+ objectStore;
684
+ blobFields;
645
685
  constructor(opts) {
646
686
  this.store = opts.store;
647
687
  this.vault = opts.vault;
@@ -651,6 +691,24 @@ var BlobSet = class {
651
691
  this.encrypted = opts.encrypted;
652
692
  this.userId = opts.userId;
653
693
  this.maxBlobBytes = opts.maxBlobBytes;
694
+ this.erasableBlobs = opts.erasableBlobs === true;
695
+ this.debugPlaintext = opts.debugPlaintext === true;
696
+ this.objectStore = opts.objectStore;
697
+ this.blobFields = opts.blobFields;
698
+ }
699
+ /**
700
+ * Resolve the key the blob's CHUNKS are encrypted under.
701
+ *
702
+ * - `_cek` present (erasable blob) → unwrap the per-blob content CEK under
703
+ * the `_blob` DEK. Deleting the BlobObject (at `refCount → 0`) makes this
704
+ * key unrecoverable → the chunks are crypto-shredded.
705
+ * - `_cek` absent (legacy) → the `_blob` DEK encrypts chunks directly.
706
+ * - unencrypted vault → `null` (chunks stored as plaintext base64).
707
+ */
708
+ async resolveChunkKey(blob) {
709
+ if (!this.encrypted) return null;
710
+ const blobDEK = await this.getDEK(BLOB_COLLECTION);
711
+ return blob._cek !== void 0 ? await unwrapCek(blob._cek, blobDEK) : blobDEK;
654
712
  }
655
713
  /** The internal collection that holds slot metadata for this collection's blobs. */
656
714
  get slotsCollection() {
@@ -768,12 +826,163 @@ var BlobSet = class {
768
826
  const updated = { ...blob, refCount: blob.refCount + delta };
769
827
  try {
770
828
  await this.writeBlobObject(updated, version);
829
+ return updated.refCount;
830
+ } catch (err) {
831
+ if (err instanceof ConflictError && attempt < MAX_CAS_RETRIES - 1) continue;
832
+ throw err;
833
+ }
834
+ }
835
+ throw new ConflictError(-1);
836
+ }
837
+ /**
838
+ * Release `n` references to a blob and reclaim it at refCount 0 (#365 slice 4).
839
+ *
840
+ * The single reclaim choke point for every reference-drop path — slot
841
+ * delete/overwrite, published-version delete, and `forget()` shred — so the
842
+ * refCount-0 policy is uniform:
843
+ * - **erasable blob** (`_cek` present) → delete the `BlobObject` (the SOLE
844
+ * copy of the wrapped content CEK → chunks permanently undecryptable) and
845
+ * reclaim the chunks. The crypto-shred is EAGER on every path: GDPR erasure
846
+ * must not wait on orphan retention.
847
+ * - **legacy blob** (no `_cek`) → reclaimed only when `reclaimLegacy` (the
848
+ * `forget()` erasure path); otherwise left for deferred GC so the existing
849
+ * `BlobLifecyclePolicy.orphanRetentionDays` semantics are preserved.
850
+ *
851
+ * @returns `'shredded'` (erasable, refCount 0, chunks dead) · `'retainedShared'`
852
+ * (erasable, still referenced) · `'residue'` (legacy — not a crypto-shred).
853
+ */
854
+ async releaseRef(eTag, n, reclaimLegacy) {
855
+ const loaded = await this.loadBlobObject(eTag);
856
+ if (!loaded) return "shredded";
857
+ const erasable = loaded.blob._cek !== void 0;
858
+ const remaining = await this.casUpdateRefCount(eTag, -n);
859
+ if (remaining > 0) return erasable ? "retainedShared" : "residue";
860
+ if (erasable || reclaimLegacy) {
861
+ await this.store.delete(this.vault, BLOB_INDEX_COLLECTION, eTag);
862
+ for (let i = 0; i < loaded.blob.chunkCount; i++) {
863
+ await this.store.delete(this.vault, BLOB_CHUNKS_COLLECTION, `${eTag}_${i}`);
864
+ }
865
+ }
866
+ return erasable ? "shredded" : "residue";
867
+ }
868
+ /**
869
+ * Crypto-shred this record's blob attachments (#365 slice 2) — called by
870
+ * `vault.forget()`.
871
+ *
872
+ * For each distinct eTag the record references (a record may attach the same
873
+ * content under several slot names → several refCount holds): decrement the
874
+ * blob's refCount by that many. When it reaches 0:
875
+ * - **erasable blob** (`_cek` present) → delete the `BlobObject` (the SOLE
876
+ * recoverable copy of the wrapped content CEK → chunks permanently
877
+ * undecryptable) and reclaim the chunk bytes. This is the crypto-shred.
878
+ * - **legacy blob** (no `_cek`) → chunks are under the shared `_blob` DEK and
879
+ * stay decryptable until byte-deleted; we delete the orphaned chunks +
880
+ * index but report it as residue, not a cryptographic erasure.
881
+ * When refCount stays > 0 the content legitimately persists for its other
882
+ * owner — reported as `retainedShared` (or `residue` if legacy).
883
+ *
884
+ * Finally drops the record's slot map, severing the subject's link.
885
+ */
886
+ async shredAllForRecord() {
887
+ const { slots } = await this.loadSlots();
888
+ const slotNames = Object.keys(slots);
889
+ const shredded = [];
890
+ const retainedShared = [];
891
+ const residue = [];
892
+ if (slotNames.length === 0) return { shredded, retainedShared, residue };
893
+ const holds = /* @__PURE__ */ new Map();
894
+ for (const name of slotNames) {
895
+ const eTag = slots[name].eTag;
896
+ holds.set(eTag, (holds.get(eTag) ?? 0) + 1);
897
+ }
898
+ for (const [eTag, n] of holds) {
899
+ const outcome = await this.releaseRef(eTag, n, true);
900
+ if (outcome === "shredded") shredded.push(eTag);
901
+ else if (outcome === "retainedShared") retainedShared.push(eTag);
902
+ else residue.push(eTag);
903
+ }
904
+ await this.store.delete(this.vault, this.slotsCollection, this.recordId);
905
+ return { shredded, retainedShared, residue };
906
+ }
907
+ /** CAS retry loop for an arbitrary BlobObject mutation. */
908
+ async casUpdateBlobObject(eTag, mutate) {
909
+ for (let attempt = 0; attempt < MAX_CAS_RETRIES; attempt++) {
910
+ const result = await this.loadBlobObject(eTag);
911
+ if (!result) throw new NotFoundError(`BlobObject ${eTag} not found`);
912
+ try {
913
+ await this.writeBlobObject(mutate(result.blob), result.version);
771
914
  return;
772
915
  } catch (err) {
773
916
  if (err instanceof ConflictError && attempt < MAX_CAS_RETRIES - 1) continue;
774
917
  throw err;
775
918
  }
776
919
  }
920
+ throw new ConflictError(-1);
921
+ }
922
+ /**
923
+ * Migrate this record's LEGACY blobs (no `_cek`, chunks under the shared
924
+ * `_blob` DEK) to per-blob content CEKs so they become crypto-shreddable
925
+ * (#365 slice 3). Returns the eTags migrated vs. already-erasable.
926
+ *
927
+ * **Explicit maintenance pass** (mirrors the record-CEK migration posture):
928
+ * re-encrypts the existing compressed chunks IN PLACE under a fresh content
929
+ * CEK — preserving the eTag, chunkCount, chunkSize, and compression — then
930
+ * flips the `_cek` discriminant. Crash-safe + idempotent via `_cekPending`:
931
+ * 1. persist the wrapped content CEK in `_cekPending` (readers ignore it →
932
+ * the blob stays readable under the `_blob` DEK; the key survives a crash);
933
+ * 2. re-encrypt each chunk under the content CEK (a resume reads an
934
+ * already-migrated chunk under the content CEK, else under the `_blob` DEK);
935
+ * 3. promote `_cekPending` → `_cek` (atomic flip). Reads now use the CEK.
936
+ * A re-run after a crash resumes from whichever phase was reached.
937
+ *
938
+ * Dedup-safe: migrating a shared blob (refCount > 1) re-keys it for every
939
+ * referencer at once; a non-erasable collection still reads it (it unwraps
940
+ * `_cek` under the `_blob` DEK it holds).
941
+ */
942
+ async migrate() {
943
+ const migrated = [];
944
+ const alreadyErasable = [];
945
+ if (!this.encrypted) return { migrated, alreadyErasable };
946
+ const blobDEK = await this.getDEK(BLOB_COLLECTION);
947
+ const { slots } = await this.loadSlots();
948
+ const eTags = new Set(Object.values(slots).map((s) => s.eTag));
949
+ for (const eTag of eTags) {
950
+ const loaded = await this.loadBlobObject(eTag);
951
+ if (!loaded) continue;
952
+ const blob = loaded.blob;
953
+ if (blob._cek !== void 0) {
954
+ alreadyErasable.push(eTag);
955
+ continue;
956
+ }
957
+ let contentCek;
958
+ if (blob._cekPending !== void 0) {
959
+ contentCek = await unwrapCek(blob._cekPending, blobDEK);
960
+ } else {
961
+ contentCek = await generateDEK();
962
+ const wrapped = await wrapCek(contentCek, blobDEK);
963
+ await this.casUpdateBlobObject(eTag, (b) => ({ ...b, _cekPending: wrapped }));
964
+ }
965
+ for (let i = 0; i < blob.chunkCount; i++) {
966
+ let raw;
967
+ try {
968
+ raw = await this.readChunk(eTag, i, blob.chunkCount, blobDEK);
969
+ } catch {
970
+ raw = await this.readChunk(eTag, i, blob.chunkCount, contentCek);
971
+ }
972
+ if (!raw) {
973
+ throw new NotFoundError(
974
+ `Blob chunk ${i}/${blob.chunkCount} missing for eTag "${eTag}" during migration`
975
+ );
976
+ }
977
+ await this.writeChunk(eTag, i, blob.chunkCount, raw, contentCek);
978
+ }
979
+ await this.casUpdateBlobObject(eTag, (b) => {
980
+ const { _cekPending, ...rest } = b;
981
+ return _cekPending === void 0 ? b : { ...rest, _cek: _cekPending };
982
+ });
983
+ migrated.push(eTag);
984
+ }
985
+ return { migrated, alreadyErasable };
777
986
  }
778
987
  // ─── Chunk I/O (with AAD binding) ─────────────────────────────────
779
988
  async writeChunk(eTag, index, chunkCount, chunk, dek) {
@@ -845,10 +1054,10 @@ var BlobSet = class {
845
1054
  }
846
1055
  // ─── Fetch all chunks for a blob ──────────────────────────────────
847
1056
  async fetchAllChunks(blob) {
848
- const blobDEK = this.encrypted ? await this.getDEK(BLOB_COLLECTION) : null;
1057
+ const chunkKey = await this.resolveChunkKey(blob);
849
1058
  const chunks = [];
850
1059
  for (let i = 0; i < blob.chunkCount; i++) {
851
- const chunk = await this.readChunk(blob.eTag, i, blob.chunkCount, blobDEK);
1060
+ const chunk = await this.readChunk(blob.eTag, i, blob.chunkCount, chunkKey);
852
1061
  if (!chunk) {
853
1062
  throw new NotFoundError(
854
1063
  `Blob chunk ${i}/${blob.chunkCount} missing for eTag "${blob.eTag}" on record "${this.recordId}"`
@@ -873,6 +1082,49 @@ var BlobSet = class {
873
1082
  * If overwriting an existing slot, decrements the old eTag's refCount.
874
1083
  */
875
1084
  async put(slotName, data, opts) {
1085
+ if (this.objectStore && this.blobFields?.[slotName]?.external) {
1086
+ const policy = this.blobFields[slotName];
1087
+ let contentType = opts?.mimeType;
1088
+ if (!contentType) {
1089
+ const detected = detectMagic(data.subarray(0, 16));
1090
+ contentType = detected?.mime ?? "application/octet-stream";
1091
+ }
1092
+ const key = `${this.collection}/${this.recordId}/${slotName}`;
1093
+ const isPublic = policy.public === true;
1094
+ const backlink = await this.buildBacklink(slotName, policy.backlink ?? "opaque-token");
1095
+ await this.objectStore.putObject(key, data, {
1096
+ contentType,
1097
+ public: isPublic,
1098
+ ...backlink.userMeta ? { userMeta: backlink.userMeta } : {}
1099
+ });
1100
+ const uploaderUserId2 = opts?.uploadedBy ?? this.userId;
1101
+ let oldETag;
1102
+ await this.casUpdateSlots((slots) => {
1103
+ oldETag = slots[slotName]?.eTag || void 0;
1104
+ const prevMeta = slots[slotName]?.external?.meta;
1105
+ slots[slotName] = {
1106
+ eTag: "",
1107
+ external: {
1108
+ key,
1109
+ contentType,
1110
+ ...isPublic ? { public: true } : {},
1111
+ ...backlink.token ? { backlink: backlink.token } : {},
1112
+ ...prevMeta ? { meta: prevMeta } : {}
1113
+ },
1114
+ filename: slotName,
1115
+ size: data.byteLength,
1116
+ mimeType: contentType,
1117
+ uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
1118
+ ...uploaderUserId2 !== void 0 ? { uploadedBy: uploaderUserId2 } : {}
1119
+ };
1120
+ return slots;
1121
+ });
1122
+ if (oldETag) {
1123
+ await this.releaseRef(oldETag, 1, false).catch(() => {
1124
+ });
1125
+ }
1126
+ return;
1127
+ }
876
1128
  const blobDEK = this.encrypted ? await this.getDEK(BLOB_COLLECTION) : null;
877
1129
  const eTag = blobDEK ? await hmacSha256Hex(blobDEK, data) : await plainSha256Hex(data);
878
1130
  let mimeType = opts?.mimeType;
@@ -888,13 +1140,21 @@ var BlobSet = class {
888
1140
  } else {
889
1141
  shouldCompress = true;
890
1142
  }
1143
+ if (this.debugPlaintext) shouldCompress = false;
891
1144
  const existingBlob = await this.loadBlobObject(eTag);
892
1145
  if (existingBlob) {
893
1146
  await this.casUpdateRefCount(eTag, 1);
894
1147
  } else {
895
1148
  const { bytes: compressed, algorithm } = shouldCompress ? await compressBytes(data) : { bytes: data, algorithm: "none" };
896
- const chunkSize = this.effectiveChunkSize(opts);
1149
+ const chunkSize = this.debugPlaintext ? Math.max(compressed.byteLength, 1) : this.effectiveChunkSize(opts);
897
1150
  const chunkCount = Math.max(1, Math.ceil(compressed.byteLength / chunkSize));
1151
+ let chunkKey = blobDEK;
1152
+ let wrappedCek;
1153
+ if (blobDEK && this.erasableBlobs) {
1154
+ const contentCek = await generateDEK();
1155
+ wrappedCek = await wrapCek(contentCek, blobDEK);
1156
+ chunkKey = contentCek;
1157
+ }
898
1158
  for (let i = 0; i < chunkCount; i++) {
899
1159
  const start = i * chunkSize;
900
1160
  await this.writeChunk(
@@ -902,7 +1162,7 @@ var BlobSet = class {
902
1162
  i,
903
1163
  chunkCount,
904
1164
  compressed.subarray(start, start + chunkSize),
905
- blobDEK
1165
+ chunkKey
906
1166
  );
907
1167
  }
908
1168
  await this.writeBlobObject({
@@ -914,7 +1174,8 @@ var BlobSet = class {
914
1174
  chunkCount,
915
1175
  ...mimeType !== void 0 ? { mimeType } : {},
916
1176
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
917
- refCount: 1
1177
+ refCount: 1,
1178
+ ...wrappedCek !== void 0 ? { _cek: wrappedCek } : {}
918
1179
  });
919
1180
  }
920
1181
  const uploaderUserId = opts?.uploadedBy ?? this.userId;
@@ -936,7 +1197,7 @@ var BlobSet = class {
936
1197
  if (this._deferredRefDecrement) {
937
1198
  const oldETag = this._deferredRefDecrement;
938
1199
  this._deferredRefDecrement = void 0;
939
- await this.casUpdateRefCount(oldETag, -1).catch(() => {
1200
+ await this.releaseRef(oldETag, 1, false).catch(() => {
940
1201
  });
941
1202
  }
942
1203
  }
@@ -950,10 +1211,114 @@ var BlobSet = class {
950
1211
  const { slots } = await this.loadSlots();
951
1212
  const slot = slots[slotName];
952
1213
  if (!slot) return null;
1214
+ if (slot.external) {
1215
+ if (!this.objectStore) {
1216
+ throw new NotFoundError(`Blob slot "${slotName}" is external but no objectStore is configured`);
1217
+ }
1218
+ return this.objectStore.getObject(slot.external.key);
1219
+ }
953
1220
  const result = await this.loadBlobObject(slot.eTag);
954
1221
  if (!result) return null;
955
1222
  return this.fetchAllChunks(result.blob);
956
1223
  }
1224
+ /**
1225
+ * A URL to fetch an `external` slot's object directly — presigned
1226
+ * (time-limited) or public, per the projection. Returns `null` if the slot
1227
+ * does not exist. Throws for a non-external slot (use `get()`/`response()`).
1228
+ */
1229
+ async url(slotName, opts) {
1230
+ const { slots } = await this.loadSlots();
1231
+ const slot = slots[slotName];
1232
+ if (!slot) return null;
1233
+ if (!slot.external) {
1234
+ throw new NotFoundError(`Blob slot "${slotName}" is not external \u2014 url() is only for external fields`);
1235
+ }
1236
+ if (!this.objectStore) {
1237
+ throw new NotFoundError(`Blob slot "${slotName}" is external but no objectStore is configured`);
1238
+ }
1239
+ return this.objectStore.objectUrl(slot.external.key, opts);
1240
+ }
1241
+ /**
1242
+ * Build the backlink stamped onto an external object's metadata — the
1243
+ * self-describing "secondary store" reference back to this record. See
1244
+ * {@link BlobFieldPolicy.backlink}. Returns the `userMeta` to attach and, for
1245
+ * `opaque-token`, the `token` to record on the slot.
1246
+ */
1247
+ async buildBacklink(slotName, mode) {
1248
+ if (mode === "none") return {};
1249
+ const ref = { vault: this.vault, collection: this.collection, record: this.recordId, field: slotName };
1250
+ if (mode === "plain") {
1251
+ return {
1252
+ userMeta: {
1253
+ "noydb-vault": ref.vault,
1254
+ "noydb-collection": ref.collection,
1255
+ "noydb-record": ref.record,
1256
+ "noydb-field": ref.field
1257
+ }
1258
+ };
1259
+ }
1260
+ if (mode === "encrypted" && this.encrypted) {
1261
+ const dek = await this.getDEK(BLOB_COLLECTION);
1262
+ const { iv, data } = await encrypt(JSON.stringify(ref), dek);
1263
+ return { userMeta: { "noydb-backlink-enc": `${iv}.${data}` } };
1264
+ }
1265
+ const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16));
1266
+ const token = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
1267
+ return { userMeta: { "noydb-backlink": token }, token };
1268
+ }
1269
+ /**
1270
+ * Adopt an EXISTING object in the projection into this slot **without
1271
+ * re-uploading** — used by import/bootstrap to anchor objects already in the
1272
+ * bucket. Writes the external slot record (the catalog entry).
1273
+ */
1274
+ async adoptExternal(slotName, ref) {
1275
+ const uploaderUserId = this.userId;
1276
+ await this.casUpdateSlots((slots) => {
1277
+ slots[slotName] = {
1278
+ eTag: "",
1279
+ external: {
1280
+ key: ref.key,
1281
+ ...ref.contentType ? { contentType: ref.contentType } : {},
1282
+ ...ref.public ? { public: true } : {},
1283
+ ...ref.backlink ? { backlink: ref.backlink } : {},
1284
+ ...ref.meta ? { meta: ref.meta } : {}
1285
+ },
1286
+ filename: slotName,
1287
+ size: ref.size ?? 0,
1288
+ ...ref.contentType ? { mimeType: ref.contentType } : {},
1289
+ uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
1290
+ ...uploaderUserId !== void 0 ? { uploadedBy: uploaderUserId } : {}
1291
+ };
1292
+ return slots;
1293
+ });
1294
+ }
1295
+ /**
1296
+ * Merge derived metadata (video `duration`, image `width`/`height`, arbitrary
1297
+ * metatags) into an external slot's secondary metadata store. Typically called
1298
+ * from an AWS-side processing callback (MediaConvert / ffprobe / Rekognition)
1299
+ * once it has probed the object. No-op for a missing or non-external slot.
1300
+ */
1301
+ async setExternalMeta(slotName, meta) {
1302
+ await this.casUpdateSlots((slots) => {
1303
+ const slot = slots[slotName];
1304
+ if (!slot?.external) return null;
1305
+ slots[slotName] = {
1306
+ ...slot,
1307
+ external: { ...slot.external, meta: { ...slot.external.meta, ...meta } }
1308
+ };
1309
+ return slots;
1310
+ });
1311
+ }
1312
+ /**
1313
+ * Read an external slot's synced derived metadata (the secondary store).
1314
+ * Returns `null` for a missing or non-external slot, `{}` if none synced yet.
1315
+ */
1316
+ async externalMeta(slotName) {
1317
+ const { slots } = await this.loadSlots();
1318
+ const slot = slots[slotName];
1319
+ if (!slot?.external) return null;
1320
+ return slot.external.meta ?? {};
1321
+ }
957
1322
  /**
958
1323
  * List all slot entries for this record.
959
1324
  * Returns metadata only — no chunk data is loaded.
@@ -967,15 +1332,22 @@ var BlobSet = class {
967
1332
  * Decrements refCount on the blob. Chunks are GC'd by `vault.blobGC()`.
968
1333
  */
969
1334
  async delete(slotName) {
970
- let eTagToDecrement;
1335
+ let eTagToRelease;
1336
+ let externalKeyToDelete;
971
1337
  await this.casUpdateSlots((slots) => {
972
1338
  if (!(slotName in slots)) return null;
973
- eTagToDecrement = slots[slotName].eTag;
1339
+ const slot = slots[slotName];
1340
+ if (slot.external) externalKeyToDelete = slot.external.key;
1341
+ else eTagToRelease = slot.eTag;
974
1342
  delete slots[slotName];
975
1343
  return slots;
976
1344
  });
977
- if (eTagToDecrement) {
978
- await this.casUpdateRefCount(eTagToDecrement, -1).catch(() => {
1345
+ if (externalKeyToDelete && this.objectStore) {
1346
+ await this.objectStore.deleteObject(externalKeyToDelete).catch(() => {
1347
+ });
1348
+ }
1349
+ if (eTagToRelease) {
1350
+ await this.releaseRef(eTagToRelease, 1, false).catch(() => {
979
1351
  });
980
1352
  }
981
1353
  }
@@ -1058,7 +1430,7 @@ var BlobSet = class {
1058
1430
  await this.writeVersionRecord(slotName, record);
1059
1431
  await this.casUpdateRefCount(slot.eTag, 1);
1060
1432
  if (existing && existing.eTag !== slot.eTag) {
1061
- await this.casUpdateRefCount(existing.eTag, -1).catch(() => {
1433
+ await this.releaseRef(existing.eTag, 1, false).catch(() => {
1062
1434
  });
1063
1435
  }
1064
1436
  }
@@ -1101,7 +1473,7 @@ var BlobSet = class {
1101
1473
  const record = await this.loadVersionRecord(slotName, label);
1102
1474
  if (!record) return;
1103
1475
  await this.deleteVersionRecord(slotName, label);
1104
- await this.casUpdateRefCount(record.eTag, -1).catch(() => {
1476
+ await this.releaseRef(record.eTag, 1, false).catch(() => {
1105
1477
  });
1106
1478
  }
1107
1479
  /**
@@ -1240,6 +1612,91 @@ function withBlobs() {
1240
1612
  };
1241
1613
  }
1242
1614
 
1615
+ // src/blobs/object-projection.ts
1616
+ function memoryObjectProjection(opts = {}) {
1617
+ const base = opts.baseUrl ?? "memory://objects";
1618
+ const store = /* @__PURE__ */ new Map();
1619
+ return {
1620
+ name: "memory",
1621
+ async putObject(key, bytes, o) {
1622
+ store.set(key, {
1623
+ bytes,
1624
+ contentType: o.contentType,
1625
+ public: o.public === true,
1626
+ ...o.userMeta ? { userMeta: o.userMeta } : {}
1627
+ });
1628
+ },
1629
+ async getObject(key) {
1630
+ return store.get(key)?.bytes ?? null;
1631
+ },
1632
+ async deleteObject(key) {
1633
+ store.delete(key);
1634
+ },
1635
+ async headObject(key) {
1636
+ const e = store.get(key);
1637
+ if (!e) return null;
1638
+ return {
1639
+ size: e.bytes.byteLength,
1640
+ contentType: e.contentType,
1641
+ ...e.userMeta ? { userMeta: e.userMeta } : {}
1642
+ };
1643
+ },
1644
+ async objectUrl(key, o) {
1645
+ const e = store.get(key);
1646
+ if (e?.public) return `${base}/${key}`;
1647
+ return `${base}/${key}?sig=memory&expires=${o?.expiresInSeconds ?? 900}`;
1648
+ },
1649
+ async putUrl(key, o) {
1650
+ return `${base}/${key}?upload=memory&ct=${encodeURIComponent(o.contentType)}`;
1651
+ },
1652
+ async listPrefix(prefix) {
1653
+ const out = [];
1654
+ for (const [key, e] of store) {
1655
+ if (!key.startsWith(prefix)) continue;
1656
+ out.push({
1657
+ key,
1658
+ meta: { size: e.bytes.byteLength, contentType: e.contentType, ...e.userMeta ? { userMeta: e.userMeta } : {} }
1659
+ });
1660
+ }
1661
+ return out;
1662
+ }
1663
+ };
1664
+ }
1665
+
1666
+ // src/blobs/import-external.ts
1667
+ function defaultDeriveRecordId(key) {
1668
+ const parts = key.split("/");
1669
+ return parts.length >= 2 ? parts[parts.length - 2] ?? null : null;
1670
+ }
1671
+ async function importExternalObjects(args) {
1672
+ const { collection, objectStore, field } = args;
1673
+ const opts = args.options ?? {};
1674
+ const derive = opts.deriveRecordId ?? defaultDeriveRecordId;
1675
+ const makeRecord = opts.makeRecord ?? ((id) => ({ id }));
1676
+ const objects = await objectStore.listPrefix(opts.prefix ?? "");
1677
+ const recordIds = [];
1678
+ let imported = 0;
1679
+ let skipped = 0;
1680
+ for (const { key, meta } of objects) {
1681
+ const recordId = derive(key);
1682
+ if (!recordId) {
1683
+ skipped++;
1684
+ continue;
1685
+ }
1686
+ if (await collection.get(recordId) == null) {
1687
+ await collection.put(recordId, makeRecord(recordId));
1688
+ }
1689
+ await collection.blob(recordId).adoptExternal(field, {
1690
+ key,
1691
+ ...meta.size !== void 0 ? { size: meta.size } : {},
1692
+ ...meta.contentType ? { contentType: meta.contentType } : {}
1693
+ });
1694
+ recordIds.push(recordId);
1695
+ imported++;
1696
+ }
1697
+ return { imported, skipped, recordIds };
1698
+ }
1699
+
1243
1700
  // src/blobs/blob-compaction.ts
1244
1701
  init_crypto();
1245
1702
  var BLOB_EVICTION_AUDIT_COLLECTION = "_blob_eviction_audit";
@@ -1252,6 +1709,7 @@ async function runCompaction(ctx, options = {}) {
1252
1709
  let evicted = 0;
1253
1710
  let records = 0;
1254
1711
  let auditEntries = 0;
1712
+ let held = 0;
1255
1713
  let collectionsWithPolicy = 0;
1256
1714
  outer: for (const collectionName of allCollections) {
1257
1715
  if (collectionName.startsWith("_")) continue;
@@ -1275,6 +1733,10 @@ async function runCompaction(ctx, options = {}) {
1275
1733
  if (!policy) continue;
1276
1734
  const reason = evaluatePolicy(policy, record, slot, now);
1277
1735
  if (!reason) continue;
1736
+ if (isHeld(policy, record, now)) {
1737
+ held += 1;
1738
+ continue;
1739
+ }
1278
1740
  if (!dryRun) {
1279
1741
  await ctx.deleteSlot(collectionName, recordId, slot.name);
1280
1742
  await writeAuditEntry(ctx, {
@@ -1299,9 +1761,32 @@ async function runCompaction(ctx, options = {}) {
1299
1761
  records,
1300
1762
  collections: collectionsWithPolicy,
1301
1763
  auditEntries,
1764
+ held,
1302
1765
  byCollection
1303
1766
  };
1304
1767
  }
1768
+ function isHeld(policy, record, now) {
1769
+ if (policy.legalHold) {
1770
+ try {
1771
+ if (policy.legalHold(record)) return true;
1772
+ } catch {
1773
+ return true;
1774
+ }
1775
+ }
1776
+ if (policy.retainUntil) {
1777
+ try {
1778
+ const until = policy.retainUntil(record);
1779
+ if (until !== null && until !== void 0) {
1780
+ const t = until instanceof Date ? until.getTime() : typeof until === "number" ? until : Date.parse(String(until));
1781
+ if (!Number.isFinite(t)) return true;
1782
+ if (t > now.getTime()) return true;
1783
+ }
1784
+ } catch {
1785
+ return true;
1786
+ }
1787
+ }
1788
+ return false;
1789
+ }
1305
1790
  function evaluatePolicy(policy, record, slot, now) {
1306
1791
  let ttlTriggered = false;
1307
1792
  let predicateTriggered = false;
@@ -1473,7 +1958,9 @@ function generateBatchId() {
1473
1958
  createExportBlobsHandle,
1474
1959
  detectMagic,
1475
1960
  detectMimeType,
1961
+ importExternalObjects,
1476
1962
  isPreCompressed,
1963
+ memoryObjectProjection,
1477
1964
  runCompaction,
1478
1965
  withBlobs
1479
1966
  });