@noy-db/hub 0.2.0-pre.13 → 0.2.0-pre.15

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 (260) hide show
  1. package/dist/aggregate/index.cjs +56 -56
  2. package/dist/aggregate/index.cjs.map +1 -1
  3. package/dist/aggregate/index.d.cts +2 -2
  4. package/dist/aggregate/index.d.ts +2 -2
  5. package/dist/aggregate/index.js +4 -4
  6. package/dist/attestation/index.cjs.map +1 -1
  7. package/dist/attestation/index.d.cts +5 -5
  8. package/dist/attestation/index.d.ts +5 -5
  9. package/dist/attestation/index.js +5 -5
  10. package/dist/blobs/index.cjs.map +1 -1
  11. package/dist/blobs/index.d.cts +6 -6
  12. package/dist/blobs/index.d.ts +6 -6
  13. package/dist/blobs/index.js +4 -4
  14. package/dist/bundle/index.cjs +559 -76
  15. package/dist/bundle/index.cjs.map +1 -1
  16. package/dist/bundle/index.d.cts +7 -7
  17. package/dist/bundle/index.d.ts +7 -7
  18. package/dist/bundle/index.js +8 -8
  19. package/dist/{chunk-YWBHS25M.js → chunk-3EWXMOK3.js} +8 -267
  20. package/dist/chunk-3EWXMOK3.js.map +1 -0
  21. package/dist/{chunk-SJ24GHID.js → chunk-4TBBMHVC.js} +2 -2
  22. package/dist/{chunk-NKGY3C53.js → chunk-535SSHBS.js} +8 -1
  23. package/dist/{chunk-NKGY3C53.js.map → chunk-535SSHBS.js.map} +1 -1
  24. package/dist/{chunk-HQXOEWLZ.js → chunk-56DJ7JVK.js} +3 -3
  25. package/dist/{chunk-KKB42D3Q.js → chunk-5LQG6ZO2.js} +2 -2
  26. package/dist/{chunk-TDECYU4Y.js → chunk-6AJBSQU4.js} +2 -2
  27. package/dist/{chunk-O2JW656W.js → chunk-6RR3MNMG.js} +3 -3
  28. package/dist/{chunk-P5MW7BG2.js → chunk-7EFFHEN5.js} +57 -50
  29. package/dist/chunk-7EFFHEN5.js.map +1 -0
  30. package/dist/{chunk-7TEI2K2A.js → chunk-7HT2MEZ5.js} +4 -4
  31. package/dist/{chunk-WQKZIQIL.js → chunk-7PS7EOCF.js} +3 -3
  32. package/dist/{chunk-C3WRKABE.js → chunk-A5ZOOZFB.js} +3 -3
  33. package/dist/{chunk-5NISHSBO.js → chunk-AAVWKNZW.js} +2 -2
  34. package/dist/{chunk-SOU42FGB.js → chunk-BIYRQQV6.js} +4 -4
  35. package/dist/{chunk-ILWQGTNH.js → chunk-BQ65SS5A.js} +2 -2
  36. package/dist/{chunk-CWFQTAD4.js → chunk-C5T5AFWN.js} +4 -4
  37. package/dist/chunk-CJORTUJ2.js +524 -0
  38. package/dist/chunk-CJORTUJ2.js.map +1 -0
  39. package/dist/{chunk-SYSKC237.js → chunk-COFPAMX6.js} +5 -5
  40. package/dist/{chunk-D5Y3HIC6.js → chunk-CZI2A4MQ.js} +3 -3
  41. package/dist/{chunk-KJF7EPUE.js → chunk-DKO2QFSA.js} +2 -2
  42. package/dist/{chunk-QFYVGJLI.js → chunk-DQU36Q7I.js} +2 -2
  43. package/dist/{chunk-NIUXQDWD.js → chunk-EGD5DXFT.js} +2 -2
  44. package/dist/{chunk-NJMKHRQI.js → chunk-EYVQHAGH.js} +267 -67
  45. package/dist/chunk-EYVQHAGH.js.map +1 -0
  46. package/dist/{chunk-KHQ3N5AB.js → chunk-F4OJZIWQ.js} +4 -4
  47. package/dist/{chunk-3OUCWHV6.js → chunk-FWPKCXTN.js} +2 -2
  48. package/dist/{chunk-QIVFGU2M.js → chunk-HOR4R722.js} +3 -3
  49. package/dist/{chunk-NP6EZT44.js → chunk-IQLVUT37.js} +2 -2
  50. package/dist/{chunk-4VCQH32J.js → chunk-JD3OZAI4.js} +2 -2
  51. package/dist/{chunk-J67BP5EP.js → chunk-KI6HAJWL.js} +3 -3
  52. package/dist/{chunk-AYNF7PVX.js → chunk-KIP6JLTF.js} +2 -2
  53. package/dist/{chunk-PW26DAXS.js → chunk-L2FE64BU.js} +3 -3
  54. package/dist/{chunk-QAWCVWCX.js → chunk-LX3CB26H.js} +4 -4
  55. package/dist/{chunk-VAK6NQAK.js → chunk-NSCVNK5K.js} +4 -4
  56. package/dist/{chunk-4YDZ7JPZ.js → chunk-NU6Q3FOR.js} +4 -4
  57. package/dist/chunk-NU6Q3FOR.js.map +1 -0
  58. package/dist/{chunk-3XZRRBFW.js → chunk-OHVFWCJP.js} +2 -2
  59. package/dist/{chunk-TEQGXA7L.js → chunk-PE4AQGFH.js} +4 -4
  60. package/dist/{chunk-UNQEWORI.js → chunk-TS26M2SB.js} +2 -2
  61. package/dist/{chunk-FNVFT4HZ.js → chunk-VU7SWWT5.js} +2 -2
  62. package/dist/{chunk-GZJ5JBED.js → chunk-WBAYSNUQ.js} +3 -3
  63. package/dist/{chunk-HHZ77DHM.js → chunk-WGHU7BLI.js} +2 -2
  64. package/dist/{chunk-GL3Z7LH7.js → chunk-X73VS74Y.js} +2 -2
  65. package/dist/{chunk-WIBHRONM.js → chunk-XWH4MXIU.js} +2 -2
  66. package/dist/{chunk-M6KXHRIA.js → chunk-YHPM5D7Y.js} +3 -3
  67. package/dist/{chunk-E3DIBDKA.js → chunk-YULZKK4F.js} +2 -2
  68. package/dist/{chunk-JWFNOD2T.js → chunk-Z4DO7YSI.js} +2 -2
  69. package/dist/{chunk-JPOQMXGT.js → chunk-ZNQYHJXX.js} +2 -2
  70. package/dist/consent/index.cjs.map +1 -1
  71. package/dist/consent/index.d.cts +6 -6
  72. package/dist/consent/index.d.ts +6 -6
  73. package/dist/consent/index.js +3 -3
  74. package/dist/{crypto-YXH6SAOW.js → crypto-QXQOHMHF.js} +3 -3
  75. package/dist/{delegation-K5ERUH6A.js → delegation-NIQ43IPU.js} +5 -5
  76. package/dist/derivations/index.cjs.map +1 -1
  77. package/dist/derivations/index.d.cts +7 -7
  78. package/dist/derivations/index.d.ts +7 -7
  79. package/dist/derivations/index.js +4 -4
  80. package/dist/{dev-unlock-BW0GNBEV.d.ts → dev-unlock-iAS8z9jc.d.ts} +1 -1
  81. package/dist/{dev-unlock-a7SOtaV0.d.cts → dev-unlock-nVkuRLLe.d.cts} +1 -1
  82. package/dist/executor-6ZDSDZ6V.js +8 -0
  83. package/dist/executor-HSSRXDOB.js +11 -0
  84. package/dist/executor-IDZDAFNH.js +8 -0
  85. package/dist/guards/index.cjs.map +1 -1
  86. package/dist/guards/index.d.cts +7 -7
  87. package/dist/guards/index.d.ts +7 -7
  88. package/dist/guards/index.js +3 -3
  89. package/dist/{hash-B0cLQcq_.d.cts → hash-Cv6byZs7.d.cts} +1 -1
  90. package/dist/{hash-uMNIAAW8.d.ts → hash-DHOnRarj.d.ts} +1 -1
  91. package/dist/history/index.cjs.map +1 -1
  92. package/dist/history/index.d.cts +7 -7
  93. package/dist/history/index.d.ts +7 -7
  94. package/dist/history/index.js +4 -4
  95. package/dist/i18n/index.cjs.map +1 -1
  96. package/dist/i18n/index.d.cts +6 -6
  97. package/dist/i18n/index.d.ts +6 -6
  98. package/dist/i18n/index.js +5 -5
  99. package/dist/{immutable-guard-B0h-ipLz.d.ts → immutable-guard-BehB1YGB.d.ts} +1 -1
  100. package/dist/{immutable-guard-BZIcYhYX.d.cts → immutable-guard-yBEOYmif.d.cts} +1 -1
  101. package/dist/{index-CUVOMtgg.d.cts → index-D95VK1Qy.d.cts} +11 -3
  102. package/dist/{index-Cqzp4tt9.d.ts → index-XNB2r6bX.d.ts} +11 -3
  103. package/dist/index.cjs +700 -78
  104. package/dist/index.cjs.map +1 -1
  105. package/dist/index.d.cts +145 -15
  106. package/dist/index.d.ts +145 -15
  107. package/dist/index.js +164 -48
  108. package/dist/index.js.map +1 -1
  109. package/dist/indexing/index.cjs +92 -31
  110. package/dist/indexing/index.cjs.map +1 -1
  111. package/dist/indexing/index.d.cts +3 -3
  112. package/dist/indexing/index.d.ts +3 -3
  113. package/dist/indexing/index.js +4 -4
  114. package/dist/issue-ADVS4OVP.js +12 -0
  115. package/dist/{lazy-builder-D5GU14TS.d.ts → lazy-builder-ChSqcF5t.d.ts} +1 -1
  116. package/dist/{lazy-builder-Ci5_YG73.d.cts → lazy-builder-eYZzLEL1.d.cts} +1 -1
  117. package/dist/{ledger-64TTQMRS.js → ledger-CWSE3BLF.js} +4 -4
  118. package/dist/materialized-views/index.cjs +2 -2
  119. package/dist/materialized-views/index.cjs.map +1 -1
  120. package/dist/materialized-views/index.d.cts +7 -7
  121. package/dist/materialized-views/index.d.ts +7 -7
  122. package/dist/materialized-views/index.js +7 -7
  123. package/dist/noydb-GZGFBA4E.js +35 -0
  124. package/dist/overlay-views/index.cjs.map +1 -1
  125. package/dist/overlay-views/index.d.cts +7 -7
  126. package/dist/overlay-views/index.d.ts +7 -7
  127. package/dist/overlay-views/index.js +4 -4
  128. package/dist/periods/index.cjs.map +1 -1
  129. package/dist/periods/index.d.cts +6 -6
  130. package/dist/periods/index.d.ts +6 -6
  131. package/dist/periods/index.js +4 -4
  132. package/dist/{predicate-Bt5ft-9c.d.cts → predicate-BmhBSPCH.d.cts} +59 -2
  133. package/dist/{predicate-Bt5ft-9c.d.ts → predicate-BmhBSPCH.d.ts} +59 -2
  134. package/dist/{public-envelope-MHG6YVXW.js → public-envelope-SYHEYQ3X.js} +3 -3
  135. package/dist/query/index.cjs +580 -195
  136. package/dist/query/index.cjs.map +1 -1
  137. package/dist/query/index.d.cts +3 -3
  138. package/dist/query/index.d.ts +3 -3
  139. package/dist/query/index.js +6 -6
  140. package/dist/{registry-PV4G3OPA.js → registry-DK5YWAAA.js} +3 -3
  141. package/dist/registry-IUZQVVBB.js +8 -0
  142. package/dist/registry-XGLNADIE.js +8 -0
  143. package/dist/{revoke-5BOLVJ3N.js → revoke-ZDFKMR5E.js} +5 -5
  144. package/dist/session/index.cjs.map +1 -1
  145. package/dist/session/index.d.cts +7 -7
  146. package/dist/session/index.d.ts +7 -7
  147. package/dist/session/index.js +3 -3
  148. package/dist/shadow/index.cjs.map +1 -1
  149. package/dist/shadow/index.d.cts +6 -6
  150. package/dist/shadow/index.d.ts +6 -6
  151. package/dist/shadow/index.js +2 -2
  152. package/dist/{signer-GRIYBA22.js → signer-P5D7Y72U.js} +4 -4
  153. package/dist/snapshots/index.cjs.map +1 -1
  154. package/dist/snapshots/index.d.cts +6 -6
  155. package/dist/snapshots/index.d.ts +6 -6
  156. package/dist/snapshots/index.js +3 -3
  157. package/dist/{stale-LZYMMDDS.js → stale-JH67FU57.js} +2 -2
  158. package/dist/{state-vault-QFJWU23A.js → state-vault-TMXZRTY5.js} +3 -3
  159. package/dist/store/index.cjs.map +1 -1
  160. package/dist/store/index.d.cts +6 -6
  161. package/dist/store/index.d.ts +6 -6
  162. package/dist/store/index.js +2 -2
  163. package/dist/{strategy-CrS7PnbE.d.ts → strategy-CbneC7bS.d.cts} +1 -1
  164. package/dist/{strategy-CrS7PnbE.d.cts → strategy-CbneC7bS.d.ts} +1 -1
  165. package/dist/sync/index.cjs.map +1 -1
  166. package/dist/sync/index.d.cts +5 -5
  167. package/dist/sync/index.d.ts +5 -5
  168. package/dist/sync/index.js +3 -3
  169. package/dist/team/index.cjs.map +1 -1
  170. package/dist/team/index.d.cts +6 -6
  171. package/dist/team/index.d.ts +6 -6
  172. package/dist/team/index.js +7 -7
  173. package/dist/tx/index.cjs.map +1 -1
  174. package/dist/tx/index.d.cts +6 -6
  175. package/dist/tx/index.d.ts +6 -6
  176. package/dist/tx/index.js +3 -3
  177. package/dist/{types-pax34sec.d.ts → types-4t1-tWS4.d.ts} +77 -9
  178. package/dist/{types-CDwSSXiI.d.cts → types-BpPV5uyy.d.cts} +77 -9
  179. package/dist/{ulid-7bCSgIgb.d.cts → ulid-CiPrpGqm.d.cts} +1 -1
  180. package/dist/{ulid-C_t4hL3d.d.ts → ulid-DAfenvFd.d.ts} +1 -1
  181. package/dist/util/index.cjs.map +1 -1
  182. package/dist/util/index.js +1 -1
  183. package/dist/{vault-group-UO4YUZOG.js → vault-group-KOM7QRJG.js} +125 -11
  184. package/dist/vault-group-KOM7QRJG.js.map +1 -0
  185. package/dist/{with-derivation-D8wFlb6V.d.cts → with-derivation-DBqJB3dQ.d.cts} +1 -1
  186. package/dist/{with-derivation-BjdOxUBn.d.ts → with-derivation-OK9M2sJE.d.ts} +1 -1
  187. package/dist/{with-materialized-view-DJb-HO65.d.ts → with-materialized-view-Dt-ufPWQ.d.ts} +1 -1
  188. package/dist/{with-materialized-view-5QMF1rS_.d.cts → with-materialized-view-NzuxYPDF.d.cts} +1 -1
  189. package/dist/{with-overlayed-view-DDNflPvC.d.cts → with-overlayed-view-CC0_ocy-.d.cts} +1 -1
  190. package/dist/{with-overlayed-view-CkqTefbz.d.ts → with-overlayed-view-eDvMs6LO.d.ts} +1 -1
  191. package/package.json +3 -3
  192. package/dist/chunk-4YDZ7JPZ.js.map +0 -1
  193. package/dist/chunk-NJMKHRQI.js.map +0 -1
  194. package/dist/chunk-P5MW7BG2.js.map +0 -1
  195. package/dist/chunk-TV3YZ35S.js +0 -90
  196. package/dist/chunk-TV3YZ35S.js.map +0 -1
  197. package/dist/chunk-YWBHS25M.js.map +0 -1
  198. package/dist/executor-AVJ7UEWA.js +0 -8
  199. package/dist/executor-IQO3KGXQ.js +0 -11
  200. package/dist/executor-VT7TKGE4.js +0 -8
  201. package/dist/issue-ZH27C23Y.js +0 -12
  202. package/dist/noydb-O76SKBST.js +0 -35
  203. package/dist/registry-2PKBQDCH.js +0 -8
  204. package/dist/registry-4VXFKCBJ.js +0 -8
  205. package/dist/vault-group-UO4YUZOG.js.map +0 -1
  206. /package/dist/{chunk-SJ24GHID.js.map → chunk-4TBBMHVC.js.map} +0 -0
  207. /package/dist/{chunk-HQXOEWLZ.js.map → chunk-56DJ7JVK.js.map} +0 -0
  208. /package/dist/{chunk-KKB42D3Q.js.map → chunk-5LQG6ZO2.js.map} +0 -0
  209. /package/dist/{chunk-TDECYU4Y.js.map → chunk-6AJBSQU4.js.map} +0 -0
  210. /package/dist/{chunk-O2JW656W.js.map → chunk-6RR3MNMG.js.map} +0 -0
  211. /package/dist/{chunk-7TEI2K2A.js.map → chunk-7HT2MEZ5.js.map} +0 -0
  212. /package/dist/{chunk-WQKZIQIL.js.map → chunk-7PS7EOCF.js.map} +0 -0
  213. /package/dist/{chunk-C3WRKABE.js.map → chunk-A5ZOOZFB.js.map} +0 -0
  214. /package/dist/{chunk-5NISHSBO.js.map → chunk-AAVWKNZW.js.map} +0 -0
  215. /package/dist/{chunk-SOU42FGB.js.map → chunk-BIYRQQV6.js.map} +0 -0
  216. /package/dist/{chunk-ILWQGTNH.js.map → chunk-BQ65SS5A.js.map} +0 -0
  217. /package/dist/{chunk-CWFQTAD4.js.map → chunk-C5T5AFWN.js.map} +0 -0
  218. /package/dist/{chunk-SYSKC237.js.map → chunk-COFPAMX6.js.map} +0 -0
  219. /package/dist/{chunk-D5Y3HIC6.js.map → chunk-CZI2A4MQ.js.map} +0 -0
  220. /package/dist/{chunk-KJF7EPUE.js.map → chunk-DKO2QFSA.js.map} +0 -0
  221. /package/dist/{chunk-QFYVGJLI.js.map → chunk-DQU36Q7I.js.map} +0 -0
  222. /package/dist/{chunk-NIUXQDWD.js.map → chunk-EGD5DXFT.js.map} +0 -0
  223. /package/dist/{chunk-KHQ3N5AB.js.map → chunk-F4OJZIWQ.js.map} +0 -0
  224. /package/dist/{chunk-3OUCWHV6.js.map → chunk-FWPKCXTN.js.map} +0 -0
  225. /package/dist/{chunk-QIVFGU2M.js.map → chunk-HOR4R722.js.map} +0 -0
  226. /package/dist/{chunk-NP6EZT44.js.map → chunk-IQLVUT37.js.map} +0 -0
  227. /package/dist/{chunk-4VCQH32J.js.map → chunk-JD3OZAI4.js.map} +0 -0
  228. /package/dist/{chunk-J67BP5EP.js.map → chunk-KI6HAJWL.js.map} +0 -0
  229. /package/dist/{chunk-AYNF7PVX.js.map → chunk-KIP6JLTF.js.map} +0 -0
  230. /package/dist/{chunk-PW26DAXS.js.map → chunk-L2FE64BU.js.map} +0 -0
  231. /package/dist/{chunk-QAWCVWCX.js.map → chunk-LX3CB26H.js.map} +0 -0
  232. /package/dist/{chunk-VAK6NQAK.js.map → chunk-NSCVNK5K.js.map} +0 -0
  233. /package/dist/{chunk-3XZRRBFW.js.map → chunk-OHVFWCJP.js.map} +0 -0
  234. /package/dist/{chunk-TEQGXA7L.js.map → chunk-PE4AQGFH.js.map} +0 -0
  235. /package/dist/{chunk-UNQEWORI.js.map → chunk-TS26M2SB.js.map} +0 -0
  236. /package/dist/{chunk-FNVFT4HZ.js.map → chunk-VU7SWWT5.js.map} +0 -0
  237. /package/dist/{chunk-GZJ5JBED.js.map → chunk-WBAYSNUQ.js.map} +0 -0
  238. /package/dist/{chunk-HHZ77DHM.js.map → chunk-WGHU7BLI.js.map} +0 -0
  239. /package/dist/{chunk-GL3Z7LH7.js.map → chunk-X73VS74Y.js.map} +0 -0
  240. /package/dist/{chunk-WIBHRONM.js.map → chunk-XWH4MXIU.js.map} +0 -0
  241. /package/dist/{chunk-M6KXHRIA.js.map → chunk-YHPM5D7Y.js.map} +0 -0
  242. /package/dist/{chunk-E3DIBDKA.js.map → chunk-YULZKK4F.js.map} +0 -0
  243. /package/dist/{chunk-JWFNOD2T.js.map → chunk-Z4DO7YSI.js.map} +0 -0
  244. /package/dist/{chunk-JPOQMXGT.js.map → chunk-ZNQYHJXX.js.map} +0 -0
  245. /package/dist/{crypto-YXH6SAOW.js.map → crypto-QXQOHMHF.js.map} +0 -0
  246. /package/dist/{delegation-K5ERUH6A.js.map → delegation-NIQ43IPU.js.map} +0 -0
  247. /package/dist/{executor-AVJ7UEWA.js.map → executor-6ZDSDZ6V.js.map} +0 -0
  248. /package/dist/{executor-IQO3KGXQ.js.map → executor-HSSRXDOB.js.map} +0 -0
  249. /package/dist/{executor-VT7TKGE4.js.map → executor-IDZDAFNH.js.map} +0 -0
  250. /package/dist/{issue-ZH27C23Y.js.map → issue-ADVS4OVP.js.map} +0 -0
  251. /package/dist/{ledger-64TTQMRS.js.map → ledger-CWSE3BLF.js.map} +0 -0
  252. /package/dist/{noydb-O76SKBST.js.map → noydb-GZGFBA4E.js.map} +0 -0
  253. /package/dist/{public-envelope-MHG6YVXW.js.map → public-envelope-SYHEYQ3X.js.map} +0 -0
  254. /package/dist/{registry-2PKBQDCH.js.map → registry-DK5YWAAA.js.map} +0 -0
  255. /package/dist/{registry-4VXFKCBJ.js.map → registry-IUZQVVBB.js.map} +0 -0
  256. /package/dist/{registry-PV4G3OPA.js.map → registry-XGLNADIE.js.map} +0 -0
  257. /package/dist/{revoke-5BOLVJ3N.js.map → revoke-ZDFKMR5E.js.map} +0 -0
  258. /package/dist/{signer-GRIYBA22.js.map → signer-P5D7Y72U.js.map} +0 -0
  259. /package/dist/{stale-LZYMMDDS.js.map → stale-JH67FU57.js.map} +0 -0
  260. /package/dist/{state-vault-QFJWU23A.js.map → state-vault-TMXZRTY5.js.map} +0 -0
@@ -4,21 +4,65 @@ import {
4
4
  import {
5
5
  groupAndReduce,
6
6
  reduceRecords
7
- } from "./chunk-YWBHS25M.js";
8
- import "./chunk-TV3YZ35S.js";
7
+ } from "./chunk-3EWXMOK3.js";
9
8
  import {
9
+ readPath
10
+ } from "./chunk-CJORTUJ2.js";
11
+ import {
12
+ CrossShardJoinError,
10
13
  NoAccessError,
11
14
  ReservedVaultNameError,
12
15
  ShardProvisioningError,
13
16
  UnknownShardError,
14
17
  ValidationError
15
- } from "./chunk-NKGY3C53.js";
18
+ } from "./chunk-535SSHBS.js";
16
19
 
17
20
  // src/federation/classify-skip.ts
18
21
  function classifyShardSkip(err) {
19
22
  return err instanceof NoAccessError ? "no-grant" : "error";
20
23
  }
21
24
 
25
+ // src/federation/cross-shard-join.ts
26
+ function coerceKey(value) {
27
+ if (value === null || value === void 0) return null;
28
+ if (typeof value === "string") return value;
29
+ if (typeof value === "number" || typeof value === "bigint") return String(value);
30
+ return null;
31
+ }
32
+ var warnedBroadcastKeys = /* @__PURE__ */ new Set();
33
+ function warnOnceBroadcastMiss(field, as, key) {
34
+ const dedup = `${field}\u2192${as}:${key}`;
35
+ if (warnedBroadcastKeys.has(dedup)) return;
36
+ warnedBroadcastKeys.add(dedup);
37
+ console.warn(
38
+ `[noy-db] broadcastJoin: no "${as}" dimension row for ${field}="${key}". Attaching null. Use mode: 'cascade' to silence.`
39
+ );
40
+ }
41
+ async function applyBroadcastLegs(rows, legs) {
42
+ if (legs.length === 0) return [...rows];
43
+ const indexes = [];
44
+ for (const leg of legs) {
45
+ const map = /* @__PURE__ */ new Map();
46
+ for (const rec of await leg.from.list()) {
47
+ const k = coerceKey(readPath(rec, leg.on));
48
+ if (k !== null && !map.has(k)) map.set(k, rec);
49
+ }
50
+ indexes.push({ leg, map });
51
+ }
52
+ return rows.map((row) => {
53
+ const out = { ...row };
54
+ for (const { leg, map } of indexes) {
55
+ const key = coerceKey(readPath(row, leg.field));
56
+ const match = key === null ? null : map.get(key) ?? null;
57
+ if (match === null && leg.mode === "warn") {
58
+ warnOnceBroadcastMiss(leg.field, leg.as, key ?? "<null>");
59
+ }
60
+ out[leg.as] = match;
61
+ }
62
+ return out;
63
+ });
64
+ }
65
+
22
66
  // src/federation/cross-vault-live.ts
23
67
  var CrossVaultLive = class {
24
68
  snapshot;
@@ -379,31 +423,89 @@ var ShardedCollection = class {
379
423
  }
380
424
  };
381
425
  var ShardedQuery = class _ShardedQuery {
382
- constructor(group, collectionName, clauses) {
426
+ constructor(group, collectionName, clauses, coPartitionedLegs = [], broadcastLegs = []) {
383
427
  this.group = group;
384
428
  this.collectionName = collectionName;
385
429
  this.clauses = clauses;
430
+ this.coPartitionedLegs = coPartitionedLegs;
431
+ this.broadcastLegs = broadcastLegs;
386
432
  }
387
433
  group;
388
434
  collectionName;
389
435
  clauses;
436
+ coPartitionedLegs;
437
+ broadcastLegs;
390
438
  where(field, op, value) {
391
- return new _ShardedQuery(this.group, this.collectionName, [
392
- ...this.clauses,
393
- { field, op, value }
394
- ]);
439
+ return new _ShardedQuery(
440
+ this.group,
441
+ this.collectionName,
442
+ [...this.clauses, { field, op, value }],
443
+ this.coPartitionedLegs,
444
+ this.broadcastLegs
445
+ );
446
+ }
447
+ /** Co-partitioned join: each shard joins its own same-vault right collection (resolved via ref()), then union. */
448
+ crossShardJoin(field, opts) {
449
+ const leg = { field, as: opts.as, maxRows: opts.maxRows, strategy: opts.strategy };
450
+ return new _ShardedQuery(
451
+ this.group,
452
+ this.collectionName,
453
+ this.clauses,
454
+ [...this.coPartitionedLegs, leg],
455
+ this.broadcastLegs
456
+ );
457
+ }
458
+ /** Broadcast dimension join: enrich every merged row from a single shared collection. */
459
+ broadcastJoin(field, opts) {
460
+ const leg = {
461
+ field,
462
+ as: opts.as,
463
+ from: opts.from,
464
+ on: opts.on ?? "id",
465
+ mode: opts.mode ?? "warn"
466
+ };
467
+ return new _ShardedQuery(
468
+ this.group,
469
+ this.collectionName,
470
+ this.clauses,
471
+ this.coPartitionedLegs,
472
+ [...this.broadcastLegs, leg]
473
+ );
395
474
  }
396
475
  /** @internal — fan out the where-filtered records across eligible shards. */
397
476
  async fanoutRecords(options = {}) {
398
477
  const { eligible, skipped } = await this.group.resolveEligible(options);
478
+ const probeRow = eligible[0];
479
+ if (this.coPartitionedLegs.length > 0 && probeRow) {
480
+ const probe = await this.group.openShard(probeRow.partitionKey);
481
+ this.group.template.configure(probe);
482
+ for (const leg of this.coPartitionedLegs) {
483
+ if (!probe.resolveRef(this.collectionName, leg.field)) {
484
+ throw new CrossShardJoinError(
485
+ `crossShardJoin("${leg.field}"): no ref() declared for "${leg.field}" on collection "${this.collectionName}" in template "${this.group.sharding.vaultTemplate}". Add refs: { ${leg.field}: ref('<target>') } to the template's collection options.`
486
+ );
487
+ }
488
+ }
489
+ }
399
490
  const across = await this.group.db.queryAcross(
400
491
  eligible.map((r) => r.vaultId),
401
492
  async (vault) => {
402
493
  this.group.template.configure(vault);
403
494
  const coll = vault.collection(this.collectionName);
404
495
  await coll.list();
496
+ for (const leg of this.coPartitionedLegs) {
497
+ const desc = vault.resolveRef(this.collectionName, leg.field);
498
+ if (desc) await vault.collection(desc.target).list();
499
+ }
405
500
  let q = coll.query();
406
501
  for (const c of this.clauses) q = q.where(c.field, c.op, c.value);
502
+ for (const leg of this.coPartitionedLegs) {
503
+ q = q.join(leg.field, {
504
+ as: leg.as,
505
+ ...leg.maxRows !== void 0 ? { maxRows: leg.maxRows } : {},
506
+ ...leg.strategy ? { strategy: leg.strategy } : {}
507
+ });
508
+ }
407
509
  return q.toArray();
408
510
  },
409
511
  { concurrency: options.concurrency ?? 1, create: false }
@@ -415,10 +517,11 @@ var ShardedQuery = class _ShardedQuery {
415
517
  }
416
518
  return { records: results, skippedVaults: skipped };
417
519
  }
418
- /** Fan out across eligible shards and merge results. */
520
+ /** Fan out across eligible shards, merge, then apply any broadcast dimension legs. */
419
521
  async toArray(options = {}) {
420
522
  const { records, skippedVaults } = await this.fanoutRecords(options);
421
- return { results: records, skippedVaults };
523
+ const results = await applyBroadcastLegs(records, this.broadcastLegs);
524
+ return { results, skippedVaults };
422
525
  }
423
526
  /** @internal — build the change-subscription + relevance binding for this query's group+collection. */
424
527
  liveBinding() {
@@ -432,8 +535,17 @@ var ShardedQuery = class _ShardedQuery {
432
535
  isRelevant: (e) => e.collection === collectionName && e.vault.startsWith(`${group.name}--`)
433
536
  };
434
537
  }
538
+ /** @internal — joined queries don't support reactive/aggregate surfaces in v1. */
539
+ assertNoJoinLegs(surface) {
540
+ if (this.coPartitionedLegs.length || this.broadcastLegs.length) {
541
+ throw new CrossShardJoinError(
542
+ `${surface}() is not supported on a ShardedQuery with crossShardJoin/broadcastJoin legs in v1. Use toArray() for joined cross-shard queries.`
543
+ );
544
+ }
545
+ }
435
546
  /** Returns a reactive cross-shard live query — a facade over CrossVaultLive. */
436
547
  live(options = {}) {
548
+ this.assertNoJoinLegs("live");
437
549
  const bind = this.liveBinding();
438
550
  const core = new CrossVaultLive({
439
551
  ...bind,
@@ -461,10 +573,12 @@ var ShardedQuery = class _ShardedQuery {
461
573
  }
462
574
  /** One-shot distributed aggregate — central reduce over all shard records. */
463
575
  aggregate(spec) {
576
+ this.assertNoJoinLegs("aggregate");
464
577
  return new CrossVaultAggregation(this, spec, this.liveBinding());
465
578
  }
466
579
  /** Begin a grouped cross-shard aggregate. */
467
580
  groupBy(field) {
581
+ this.assertNoJoinLegs("groupBy");
468
582
  return new ShardedGroupedQuery(this, field);
469
583
  }
470
584
  };
@@ -490,4 +604,4 @@ export {
490
604
  ShardedQuery,
491
605
  VaultGroup
492
606
  };
493
- //# sourceMappingURL=vault-group-UO4YUZOG.js.map
607
+ //# sourceMappingURL=vault-group-KOM7QRJG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/federation/classify-skip.ts","../src/federation/cross-shard-join.ts","../src/federation/cross-vault-live.ts","../src/federation/aggregate-across.ts","../src/federation/vault-group.ts"],"sourcesContent":["import { NoAccessError } from '../errors.js'\nimport type { SkippedVault } from './types.js'\n\n/**\n * Classify a per-shard fan-out failure. `NoAccessError` (no keyring envelope for\n * the calling identity) is the unambiguous not-granted signal → `'no-grant'`\n * (expected under scoped access, not a fault). Everything else → `'error'` —\n * `InvalidKeyError`/`DecryptionError`/`KeyringCorruptError` can mean \"wrong KEK\n * OR whole-file corruption\" per loadKeyring, so they must not hide as no-grant.\n */\nexport function classifyShardSkip(err: Error): Exclude<SkippedVault['reason'], 'schema-drift'> {\n return err instanceof NoAccessError ? 'no-grant' : 'error'\n}\n","/**\n * @category capability\n * crossShardJoin — co-partitioned + broadcast dimension join for\n * ShardedQuery. Spec:\n * docs/superpowers/specs/2026-06-09-cross-shard-join-design.md.\n *\n * This module owns the BROADCAST half (central, post-merge map-attach)\n * and the leg type definitions. The CO-PARTITIONED half is threaded\n * into the existing intra-vault `.join()` from vault-group.ts — see\n * ShardedQuery.fanoutRecords. join.ts is deliberately untouched.\n */\nimport { readPath } from '../query/predicate.js'\nimport type { JoinStrategy } from '../query/join.js'\n\n/** Public options for `ShardedQuery.crossShardJoin`. */\nexport interface CrossShardJoinOptions {\n /** Alias key under which the joined same-shard record attaches. */\n readonly as: string\n /** Per-shard row ceiling override (default DEFAULT_JOIN_MAX_ROWS). */\n readonly maxRows?: number\n /** Planner strategy override, passed through to intra-vault `.join()`. */\n readonly strategy?: JoinStrategy\n}\n\n/**\n * Minimal structural shape of a broadcast dimension source. A\n * `Collection` satisfies this natively: `list()` hydrates and returns\n * the decoded records. Kept as a one-method interface so plain test\n * sources are trivial to construct.\n */\nexport interface BroadcastSource {\n list(): Promise<readonly unknown[]>\n}\n\n/** Public options for `ShardedQuery.broadcastJoin`. */\nexport interface BroadcastJoinOptions {\n /** Alias key under which the dimension record attaches. */\n readonly as: string\n /** The shared dimension collection (an opened handle in another vault). */\n readonly from: BroadcastSource\n /** Right-side key to match `field` against. Default 'id'. */\n readonly on?: string\n /** Miss behavior. 'warn' (default) attaches null + one-shot warning; 'cascade' is silent. */\n readonly mode?: 'warn' | 'cascade'\n}\n\n/** Internal co-partitioned leg carried on ShardedQuery. */\nexport interface CoPartitionedLeg {\n readonly field: string\n readonly as: string\n readonly maxRows: number | undefined\n readonly strategy: JoinStrategy | undefined\n}\n\n/** Internal broadcast leg carried on ShardedQuery. */\nexport interface BroadcastLeg {\n readonly field: string\n readonly as: string\n readonly from: BroadcastSource\n readonly on: string\n readonly mode: 'warn' | 'cascade'\n}\n\n/**\n * Coerce an unknown key value into a lookup string. Mirrors join.ts's\n * private `coerceRefKey` (string → string; number/bigint → String;\n * else null) — re-implemented locally to keep join.ts literally\n * untouched.\n */\nfunction coerceKey(value: unknown): string | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'bigint') return String(value)\n return null\n}\n\n/** One-shot warn dedup for broadcast misses, keyed by `field→as`. */\nconst warnedBroadcastKeys = new Set<string>()\nfunction warnOnceBroadcastMiss(field: string, as: string, key: string): void {\n const dedup = `${field}→${as}:${key}`\n if (warnedBroadcastKeys.has(dedup)) return\n warnedBroadcastKeys.add(dedup)\n console.warn(\n `[noy-db] broadcastJoin: no \"${as}\" dimension row for ${field}=\"${key}\". ` +\n `Attaching null. Use mode: 'cascade' to silence.`,\n )\n}\n\n/** Test-only reset for the broadcast warn dedup set. */\nexport function resetBroadcastWarnings(): void {\n warnedBroadcastKeys.clear()\n}\n\n/**\n * Apply every broadcast leg to a merged row set, centrally. Each leg's\n * source is snapshotted ONCE, indexed by its `on` key, then every row\n * gets `{ [as]: match ?? null }`. Returns fresh top-level objects.\n */\nexport async function applyBroadcastLegs(\n rows: readonly unknown[],\n legs: readonly BroadcastLeg[],\n): Promise<unknown[]> {\n if (legs.length === 0) return [...rows]\n\n // Build one index per leg (list() once per source).\n const indexes: { leg: BroadcastLeg; map: Map<string, unknown> }[] = []\n for (const leg of legs) {\n const map = new Map<string, unknown>()\n for (const rec of await leg.from.list()) {\n const k = coerceKey(readPath(rec, leg.on))\n if (k !== null && !map.has(k)) map.set(k, rec)\n }\n indexes.push({ leg, map })\n }\n\n return rows.map((row) => {\n const out = { ...(row as Record<string, unknown>) }\n for (const { leg, map } of indexes) {\n const key = coerceKey(readPath(row, leg.field))\n const match = key === null ? null : map.get(key) ?? null\n if (match === null && leg.mode === 'warn') {\n warnOnceBroadcastMiss(leg.field, leg.as, key ?? '<null>')\n }\n out[leg.as] = match\n }\n return out\n })\n}\n","/**\n * @category capability\n * Reactive core for cross-vault live queries/aggregations. Generic over a\n * snapshot S. Single-flight + microtask-coalesced recompute on relevant\n * change. Mirrors the LiveQuery/LiveAggregation contracts via facades.\n * Spec: docs/superpowers/specs/2026-06-07-cross-vault-live-and-aggregate-design.md.\n */\nimport type { ChangeEvent } from '../types.js'\n\nexport interface CrossVaultLiveOptions<S> {\n readonly subscribeToChanges: (handler: (e: ChangeEvent) => void) => () => void\n readonly isRelevant: (e: ChangeEvent) => boolean\n readonly compute: () => Promise<S>\n readonly initialSnapshot: S\n readonly debounceMs?: number\n}\n\nexport class CrossVaultLive<S> {\n snapshot: S\n error: Error | null = null\n readonly ready: Promise<void>\n\n private readonly subs = new Set<() => void>()\n private readonly unsubChange: () => void\n private readonly opts: CrossVaultLiveOptions<S>\n private stopped = false\n private computing = false\n private dirty = false\n private scheduled = false\n private timer: ReturnType<typeof setTimeout> | null = null\n private resolveReady!: () => void\n private settledOnce = false\n\n constructor(opts: CrossVaultLiveOptions<S>) {\n this.opts = opts\n this.snapshot = opts.initialSnapshot\n this.ready = new Promise<void>((res) => { this.resolveReady = res })\n this.unsubChange = opts.subscribeToChanges((e) => {\n if (this.stopped || !opts.isRelevant(e)) return\n this.schedule()\n })\n this.schedule() // initial compute\n }\n\n subscribe(cb: () => void): () => void {\n if (this.stopped) return () => {}\n this.subs.add(cb)\n return () => this.subs.delete(cb)\n }\n\n stop(): void {\n if (this.stopped) return\n this.stopped = true\n this.unsubChange()\n if (this.timer !== null) clearTimeout(this.timer)\n this.subs.clear()\n if (!this.settledOnce) this.resolveReady() // never leave ready dangling\n }\n\n private schedule(): void {\n if (this.stopped) return\n if (this.computing) { this.dirty = true; return }\n if (this.scheduled) return\n this.scheduled = true\n const run = () => { this.scheduled = false; void this.runCompute() }\n const ms = this.opts.debounceMs ?? 0\n if (ms > 0) this.timer = setTimeout(run, ms)\n // queueMicrotask is non-cancellable; the `if (this.stopped) return` guard at the top of runCompute makes a post-stop fire a no-op.\n else queueMicrotask(run)\n }\n\n private async runCompute(): Promise<void> {\n if (this.stopped) return\n this.computing = true\n this.dirty = false\n try {\n const next = await this.opts.compute()\n if (this.stopped) return\n this.snapshot = next\n this.error = null\n } catch (err) {\n if (this.stopped) return\n this.error = err instanceof Error ? err : new Error(String(err))\n } finally {\n this.computing = false\n if (!this.stopped) {\n if (!this.settledOnce) { this.settledOnce = true; this.resolveReady() }\n for (const cb of this.subs) cb()\n if (this.dirty) this.schedule()\n }\n }\n }\n}\n","/**\n * @category capability\n * One-shot distributed aggregate wrappers for cross-vault fan-out.\n * Central-reduce: all shard records are concatenated and reduced in one pass\n * so avg/mean values are computed over the full union, not as avg-of-avgs.\n * Spec: docs/superpowers/specs/2026-06-07-cross-vault-live-and-aggregate-design.md.\n */\nimport { reduceRecords } from '../aggregate/aggregation.js'\nimport { groupAndReduce } from '../aggregate/groupby.js'\nimport type { AggregateResult, AggregateSpec } from '../aggregate/aggregation.js'\nimport type {\n FanoutQueryOptions,\n SkippedVault,\n GroupedRow,\n LiveQueryOptions,\n CrossVaultLiveAggregation,\n CrossVaultLiveQuery,\n} from './types.js'\nimport { CrossVaultLive } from './cross-vault-live.js'\nimport type { ChangeEvent } from '../types.js'\n\n/** A source that can fan out records across shards. Satisfied by ShardedQuery. */\nexport interface FanoutRecordSource<R> {\n fanoutRecords(options: FanoutQueryOptions): Promise<{ records: R[]; skippedVaults: SkippedVault[] }>\n}\n\n/** Live-binding hooks (change subscription + relevance) threaded from ShardedQuery. */\nexport interface LiveBinding {\n subscribeToChanges: (handler: (e: ChangeEvent) => void) => () => void\n isRelevant: (e: ChangeEvent) => boolean\n}\n\n/**\n * One-shot cross-vault aggregate. Concatenates all shard records and runs a\n * single central reduce, ensuring correct avg/mean values.\n */\nexport class CrossVaultAggregation<R, Spec extends AggregateSpec> {\n constructor(\n private readonly src: FanoutRecordSource<R>,\n private readonly spec: Spec,\n private readonly bind?: LiveBinding,\n ) {}\n\n async run(options: FanoutQueryOptions = {}): Promise<{\n result: AggregateResult<Spec>\n skippedVaults: SkippedVault[]\n }> {\n const { records, skippedVaults } = await this.src.fanoutRecords(options)\n return { result: reduceRecords(records, this.spec), skippedVaults }\n }\n\n live(options: LiveQueryOptions = {}): CrossVaultLiveAggregation<AggregateResult<Spec>> {\n if (!this.bind) throw new Error('CrossVaultAggregation: live() requires a LiveBinding — use ShardedQuery.aggregate()')\n const spec = this.spec\n const src = this.src\n const core = new CrossVaultLive<{ value: AggregateResult<Spec> | undefined; skipped: SkippedVault[] }>({\n subscribeToChanges: this.bind.subscribeToChanges,\n isRelevant: this.bind.isRelevant,\n compute: async () => {\n const { records, skippedVaults } = await src.fanoutRecords(options)\n return { value: reduceRecords(records, spec), skipped: skippedVaults }\n },\n initialSnapshot: { value: undefined, skipped: [] },\n ...(options.debounceMs !== undefined ? { debounceMs: options.debounceMs } : {}),\n })\n return {\n get value() { return core.snapshot.value },\n get skippedVaults() { return core.snapshot.skipped as readonly SkippedVault[] },\n get error() { return core.error },\n ready: core.ready,\n subscribe: (cb) => core.subscribe(cb),\n stop: () => core.stop(),\n }\n }\n}\n\n/**\n * One-shot cross-vault grouped aggregate. Concatenates all shard records and\n * runs a single central group-and-reduce, emitting one row per bucket.\n */\nexport class CrossVaultGroupedAggregation<R, F extends string, Spec extends AggregateSpec> {\n constructor(\n private readonly src: FanoutRecordSource<R>,\n private readonly field: F,\n private readonly spec: Spec,\n private readonly bind?: LiveBinding,\n ) {}\n\n async run(options: FanoutQueryOptions = {}): Promise<{\n results: GroupedRow<F, Spec>[]\n skippedVaults: SkippedVault[]\n }> {\n const { records, skippedVaults } = await this.src.fanoutRecords(options)\n return {\n results: groupAndReduce<GroupedRow<F, Spec>>(records, this.field, this.spec),\n skippedVaults,\n }\n }\n\n live(options: LiveQueryOptions = {}): CrossVaultLiveQuery<GroupedRow<F, Spec>> {\n if (!this.bind) throw new Error('CrossVaultGroupedAggregation: live() requires a LiveBinding — use ShardedQuery.groupBy().aggregate()')\n const field = this.field\n const spec = this.spec\n const src = this.src\n const core = new CrossVaultLive<{ records: GroupedRow<F, Spec>[]; skipped: SkippedVault[] }>({\n subscribeToChanges: this.bind.subscribeToChanges,\n isRelevant: this.bind.isRelevant,\n compute: async () => {\n const { records, skippedVaults } = await src.fanoutRecords(options)\n return {\n records: groupAndReduce<GroupedRow<F, Spec>>(records, field, spec),\n skipped: skippedVaults,\n }\n },\n initialSnapshot: { records: [], skipped: [] },\n ...(options.debounceMs !== undefined ? { debounceMs: options.debounceMs } : {}),\n })\n return {\n get value() { return core.snapshot.records as readonly GroupedRow<F, Spec>[] },\n get skippedVaults() { return core.snapshot.skipped as readonly SkippedVault[] },\n get error() { return core.error },\n ready: core.ready,\n subscribe: (cb) => core.subscribe(cb),\n stop: () => core.stop(),\n }\n }\n}\n","/**\n * @category capability\n * Multi-vault partition federation — VaultGroup transparent shard\n * routing. Spec:\n * docs/superpowers/specs/2026-06-07-mvf-vaultgroup-routing-mvp-design.md.\n */\nimport type { Noydb } from '../noydb.js'\nimport type { Vault } from '../vault.js'\nimport type { Collection } from '../collection.js'\nimport type { StateManagementVault } from './state-vault.js'\nimport { CrossShardJoinError, ReservedVaultNameError, ShardProvisioningError, UnknownShardError, ValidationError } from '../errors.js'\nimport { STATE_VAULT_NAME } from './constants.js'\nimport { classifyShardSkip } from './classify-skip.js'\nimport { applyBroadcastLegs } from './cross-shard-join.js'\nimport type { CoPartitionedLeg, BroadcastLeg, CrossShardJoinOptions, BroadcastJoinOptions } from './cross-shard-join.js'\nimport { CrossVaultLive } from './cross-vault-live.js'\nimport { CrossVaultAggregation, CrossVaultGroupedAggregation } from './aggregate-across.js'\nimport type { FanoutRecordSource, LiveBinding } from './aggregate-across.js'\nimport type { AggregateSpec } from '../aggregate/aggregation.js'\nimport type {\n ShardingConfig,\n VaultRegistryRow,\n VaultTemplate,\n FanoutQueryOptions,\n FanoutResult,\n SkippedVault,\n WhereClause,\n LiveQueryOptions,\n CrossVaultLiveQuery,\n} from './types.js'\n\n/** Reserved separator between group name and partition key in a shard vault id. */\nconst SHARD_SEPARATOR = '--'\n/** Store-safe partition-key charset (single hyphens OK; '--' is the reserved separator). */\nconst SAFE_PARTITION_KEY = /^[A-Za-z0-9._-]+$/\n\nfunction assertSafePartitionKey(partitionKey: string): void {\n if (partitionKey.length === 0) {\n throw new ValidationError('partitionKey must be a non-empty string')\n }\n if (partitionKey === STATE_VAULT_NAME) {\n throw new ReservedVaultNameError(partitionKey)\n }\n if (!SAFE_PARTITION_KEY.test(partitionKey)) {\n throw new ValidationError(\n `partitionKey \"${partitionKey}\" contains characters outside [A-Za-z0-9._-]. ` +\n `Map your records to a store-safe key in sharding.keyOf.`,\n )\n }\n if (partitionKey.includes(SHARD_SEPARATOR)) {\n throw new ValidationError(\n `partitionKey \"${partitionKey}\" must not contain \"--\" — it is reserved as the ` +\n `shard vault-id separator and would risk shard-id collisions.`,\n )\n }\n}\n\nexport class VaultGroup<T> {\n constructor(\n /** @internal */ readonly db: Noydb,\n /** @internal */ readonly name: string,\n /** @internal */ readonly registry: Collection<VaultRegistryRow>,\n /** @internal */ readonly sharding: ShardingConfig<T>,\n /** @internal */ readonly template: VaultTemplate,\n ) {\n if (name.includes(SHARD_SEPARATOR)) {\n throw new ValidationError(\n `VaultGroup name \"${name}\" must not contain \"--\" (reserved shard vault-id separator).`,\n )\n }\n }\n\n /** @internal — set when the group is managed (no explicit registry). */\n private stateVault: StateManagementVault | undefined\n\n /** @internal */\n _attachStateVault(sv: StateManagementVault): void {\n this.stateVault = sv\n }\n\n /** Deterministic vault name for a partition key, namespaced by the group. */\n shardVaultId(partitionKey: string): string {\n assertSafePartitionKey(partitionKey)\n return `${this.name}${SHARD_SEPARATOR}${partitionKey}`\n }\n\n /**\n * @internal — group-qualified registry record key (avoids cross-group key\n * collisions). Identical to the shard vault id by design — the registry row\n * for a shard is keyed by that shard's vault id — so it delegates to\n * `shardVaultId`, reusing its partition-key validation.\n */\n registryId(partitionKey: string): string {\n return this.shardVaultId(partitionKey)\n }\n\n /**\n * Registry rows for THIS group (hydrates the registry collection first).\n * The registry may be shared across groups (the auto-wired StateManagement\n * vault holds one `vaultRegistry` for the whole instance), so rows are\n * filtered by `group` — without this, a group's fan-out reads would leak\n * across into other groups' shards. Mirrors the `${group}--` scoping that\n * `liveBinding().isRelevant` already applies to the reactive path.\n */\n async allRows(): Promise<VaultRegistryRow[]> {\n await this.registry.list()\n const rows = this.registry.query().toArray() // toArray() is synchronous\n return rows.filter((r) => r.group === this.name)\n }\n\n /** Open an existing shard and apply the template. */\n async openShard(partitionKey: string): Promise<Vault> {\n const vault = await this.db.openVault(this.shardVaultId(partitionKey), { create: false })\n this.template.configure(vault)\n return vault\n }\n\n /**\n * Idempotently provision a shard for `partitionKey`. Returns the\n * configured vault handle.\n *\n * - row + vault present → no-op, return handle\n * - row present, vault gone → ShardProvisioningError\n * - row absent (vault present or not) → open-or-create, configure, write row\n */\n async createShard(partitionKey: string): Promise<Vault> {\n const vaultId = this.shardVaultId(partitionKey)\n const row = await this.registry.get(this.registryId(partitionKey))\n const provisioned = await this.db._shardVaultProvisioned(vaultId)\n\n if (row && !provisioned) throw new ShardProvisioningError(vaultId, partitionKey)\n if (row && provisioned) return this.openShard(partitionKey)\n\n // Row absent → create (or reconcile a provisioned-but-unregistered vault).\n const vault = await this.db.openVault(vaultId)\n this.template.configure(vault)\n await this.registry.put(this.registryId(partitionKey), {\n vaultId,\n partitionKey,\n templateName: this.sharding.vaultTemplate,\n schemaVersion: this.template.version,\n createdAt: Date.now(),\n group: this.name,\n })\n if (this.stateVault) {\n try {\n await this.stateVault.appendEvent({\n type: 'shard-created',\n group: this.name,\n vaultId,\n templateName: this.sharding.vaultTemplate,\n version: this.template.version,\n })\n } catch {\n /* best-effort: event logging never fails the shard write */\n }\n }\n return vault\n }\n\n /**\n * Drill down to a single shard's full Collection API. Throws if the shard is unknown.\n * Also throws ShardProvisioningError if the registry row exists but the vault has been deleted\n * (registry/store divergence).\n */\n async shard(partitionKey: string): Promise<Vault> {\n const vaultId = this.shardVaultId(partitionKey)\n const row = await this.registry.get(this.registryId(partitionKey))\n if (!row) throw new UnknownShardError(partitionKey, this.name)\n const provisioned = await this.db._shardVaultProvisioned(vaultId)\n if (!provisioned) throw new ShardProvisioningError(vaultId, partitionKey)\n return this.openShard(partitionKey)\n }\n\n /** A sharded view over one logical collection across all shards. */\n collection<R = T>(collectionName: string): ShardedCollection<T, R> {\n return new ShardedCollection<T, R>(this, collectionName)\n }\n\n /** @internal — eligible (openable-candidate) rows + drift/divergence skips. */\n async resolveEligible(options: { minVersion?: number } = {}): Promise<{\n eligible: VaultRegistryRow[]\n skipped: SkippedVault[]\n }> {\n const rows = await this.allRows()\n const skipped: SkippedVault[] = []\n const versionOk: VaultRegistryRow[] = []\n for (const row of rows) {\n if (options.minVersion !== undefined && row.schemaVersion < options.minVersion) {\n skipped.push({ vaultId: row.vaultId, reason: 'schema-drift' })\n } else versionOk.push(row)\n }\n const provisioned = await Promise.all(versionOk.map((r) => this.db._shardVaultProvisioned(r.vaultId)))\n const eligible: VaultRegistryRow[] = []\n versionOk.forEach((row, i) => {\n if (provisioned[i]) eligible.push(row)\n else skipped.push({ vaultId: row.vaultId, reason: 'error', error: new ShardProvisioningError(row.vaultId, row.partitionKey) })\n })\n return { eligible, skipped }\n }\n}\n\nexport class ShardedCollection<T, R = T> {\n constructor(\n private readonly group: VaultGroup<T>,\n private readonly collectionName: string,\n ) {}\n\n /** Route a write to the shard owning `keyOf(record)`. */\n async put(id: string, record: T): Promise<void> {\n const key = this.group.sharding.keyOf(record)\n const row = await this.group.registry.get(this.group.registryId(key))\n let vault: Vault\n if (!row) {\n if (this.group.sharding.autoCreate === false) {\n throw new UnknownShardError(key, this.group.name)\n }\n vault = await this.group.createShard(key)\n } else {\n vault = await this.group.openShard(key)\n }\n await vault.collection<T>(this.collectionName).put(id, record)\n }\n\n /** Begin a cross-shard fan-out query. */\n query(): ShardedQuery<T, R> {\n return new ShardedQuery<T, R>(this.group, this.collectionName, [])\n }\n}\n\nexport class ShardedQuery<T, R = T> {\n constructor(\n private readonly group: VaultGroup<T>,\n private readonly collectionName: string,\n private readonly clauses: readonly WhereClause[],\n private readonly coPartitionedLegs: readonly CoPartitionedLeg[] = [],\n private readonly broadcastLegs: readonly BroadcastLeg[] = [],\n ) {}\n\n where(field: string, op: WhereClause['op'], value: unknown): ShardedQuery<T, R> {\n return new ShardedQuery<T, R>(\n this.group,\n this.collectionName,\n [...this.clauses, { field, op, value }],\n this.coPartitionedLegs,\n this.broadcastLegs,\n )\n }\n\n /** Co-partitioned join: each shard joins its own same-vault right collection (resolved via ref()), then union. */\n crossShardJoin(field: string, opts: CrossShardJoinOptions): ShardedQuery<T, R> {\n const leg: CoPartitionedLeg = { field, as: opts.as, maxRows: opts.maxRows, strategy: opts.strategy }\n return new ShardedQuery<T, R>(\n this.group,\n this.collectionName,\n this.clauses,\n [...this.coPartitionedLegs, leg],\n this.broadcastLegs,\n )\n }\n\n /** Broadcast dimension join: enrich every merged row from a single shared collection. */\n broadcastJoin(field: string, opts: BroadcastJoinOptions): ShardedQuery<T, R> {\n const leg: BroadcastLeg = {\n field,\n as: opts.as,\n from: opts.from,\n on: opts.on ?? 'id',\n mode: opts.mode ?? 'warn',\n }\n return new ShardedQuery<T, R>(\n this.group,\n this.collectionName,\n this.clauses,\n this.coPartitionedLegs,\n [...this.broadcastLegs, leg],\n )\n }\n\n /** @internal — fan out the where-filtered records across eligible shards. */\n async fanoutRecords(options: FanoutQueryOptions = {}): Promise<{ records: R[]; skippedVaults: SkippedVault[] }> {\n const { eligible, skipped } = await this.group.resolveEligible(options)\n // Deterministic pre-check: an undeclared co-partitioned join ref fails\n // identically on every shard, so surface it as ONE CrossShardJoinError\n // rather than N identical skips. Probe the first eligible shard.\n const probeRow = eligible[0]\n if (this.coPartitionedLegs.length > 0 && probeRow) {\n const probe = await this.group.openShard(probeRow.partitionKey)\n this.group.template.configure(probe)\n for (const leg of this.coPartitionedLegs) {\n if (!probe.resolveRef(this.collectionName, leg.field)) {\n throw new CrossShardJoinError(\n `crossShardJoin(\"${leg.field}\"): no ref() declared for \"${leg.field}\" on ` +\n `collection \"${this.collectionName}\" in template \"${this.group.sharding.vaultTemplate}\". ` +\n `Add refs: { ${leg.field}: ref('<target>') } to the template's collection options.`,\n )\n }\n }\n }\n const across = await this.group.db.queryAcross<R[]>(\n eligible.map((r) => r.vaultId),\n async (vault) => {\n this.group.template.configure(vault)\n const coll = vault.collection<R>(this.collectionName)\n await coll.list() // hydrate the in-memory cache before the sync query\n // Hydrate each co-partitioned join target — resolveSource reads the\n // in-memory cache, so an unopened right collection would join to an\n // empty snapshot (every row → null).\n for (const leg of this.coPartitionedLegs) {\n const desc = vault.resolveRef(this.collectionName, leg.field)\n if (desc) await vault.collection(desc.target).list()\n }\n let q = coll.query()\n for (const c of this.clauses) q = q.where(c.field, c.op, c.value)\n for (const leg of this.coPartitionedLegs) {\n q = q.join(leg.field, {\n as: leg.as,\n ...(leg.maxRows !== undefined ? { maxRows: leg.maxRows } : {}),\n ...(leg.strategy ? { strategy: leg.strategy } : {}),\n })\n }\n return q.toArray()\n },\n { concurrency: options.concurrency ?? 1, create: false },\n )\n const results: R[] = []\n for (const r of across) {\n if (r.error) skipped.push({ vaultId: r.vault, reason: classifyShardSkip(r.error), error: r.error })\n else for (const item of r.result) results.push(item)\n }\n return { records: results, skippedVaults: skipped }\n }\n\n /** Fan out across eligible shards, merge, then apply any broadcast dimension legs. */\n async toArray(options: FanoutQueryOptions = {}): Promise<FanoutResult<R>> {\n const { records, skippedVaults } = await this.fanoutRecords(options)\n const results = (await applyBroadcastLegs(records, this.broadcastLegs)) as R[]\n return { results, skippedVaults }\n }\n\n /** @internal — build the change-subscription + relevance binding for this query's group+collection. */\n liveBinding(): LiveBinding {\n const group = this.group\n const collectionName = this.collectionName\n return {\n subscribeToChanges: (h) => { group.db.on('change', h); return () => group.db.off('change', h) },\n isRelevant: (e) => e.collection === collectionName && e.vault.startsWith(`${group.name}--`),\n }\n }\n\n /** @internal — joined queries don't support reactive/aggregate surfaces in v1. */\n private assertNoJoinLegs(surface: string): void {\n if (this.coPartitionedLegs.length || this.broadcastLegs.length) {\n throw new CrossShardJoinError(\n `${surface}() is not supported on a ShardedQuery with crossShardJoin/broadcastJoin ` +\n `legs in v1. Use toArray() for joined cross-shard queries.`,\n )\n }\n }\n\n /** Returns a reactive cross-shard live query — a facade over CrossVaultLive. */\n live(options: LiveQueryOptions = {}): CrossVaultLiveQuery<R> {\n this.assertNoJoinLegs('live')\n const bind = this.liveBinding()\n const core = new CrossVaultLive<{ records: R[]; skipped: SkippedVault[] }>({\n ...bind,\n compute: async () => {\n const { records, skippedVaults } = await this.fanoutRecords(options)\n return { records, skipped: skippedVaults }\n },\n initialSnapshot: { records: [], skipped: [] },\n ...(options.debounceMs !== undefined ? { debounceMs: options.debounceMs } : {}),\n })\n return {\n get value() { return core.snapshot.records as readonly R[] },\n get skippedVaults() { return core.snapshot.skipped as readonly SkippedVault[] },\n get error() { return core.error },\n ready: core.ready,\n subscribe: (cb) => core.subscribe(cb),\n stop: () => core.stop(),\n }\n }\n\n /** One-shot distributed aggregate — central reduce over all shard records. */\n aggregate<Spec extends AggregateSpec>(spec: Spec): CrossVaultAggregation<R, Spec> {\n this.assertNoJoinLegs('aggregate')\n return new CrossVaultAggregation<R, Spec>(this, spec, this.liveBinding())\n }\n\n /** Begin a grouped cross-shard aggregate. */\n groupBy<F extends string>(field: F): ShardedGroupedQuery<T, R, F> {\n this.assertNoJoinLegs('groupBy')\n return new ShardedGroupedQuery<T, R, F>(this, field)\n }\n}\n\n/** Grouped cross-shard query — intermediate after `.groupBy(field)`, terminates with `.aggregate(spec)`. */\nexport class ShardedGroupedQuery<T, R, F extends string> {\n constructor(\n private readonly query: ShardedQuery<T, R>,\n private readonly field: F,\n ) {}\n\n aggregate<Spec extends AggregateSpec>(spec: Spec): CrossVaultGroupedAggregation<R, F, Spec> {\n return new CrossVaultGroupedAggregation<R, F, Spec>(\n { fanoutRecords: (o) => this.query.fanoutRecords(o) } satisfies FanoutRecordSource<R>,\n this.field,\n spec,\n this.query.liveBinding(),\n )\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,4 +1,4 @@
1
- import { aN as DerivationStrategy, aR as DerivationStrategyHandle } from './types-CDwSSXiI.cjs';
1
+ import { aN as DerivationStrategy, aR as DerivationStrategyHandle } from './types-BpPV5uyy.cjs';
2
2
 
3
3
  /**
4
4
  * Register a deterministic derivation: one source collection → one or
@@ -1,4 +1,4 @@
1
- import { aN as DerivationStrategy, aR as DerivationStrategyHandle } from './types-pax34sec.js';
1
+ import { aN as DerivationStrategy, aR as DerivationStrategyHandle } from './types-4t1-tWS4.js';
2
2
 
3
3
  /**
4
4
  * Register a deterministic derivation: one source collection → one or
@@ -1,4 +1,4 @@
1
- import { aV as MaterializedViewStrategy, aW as MaterializedViewStrategyHandle } from './types-pax34sec.js';
1
+ import { aV as MaterializedViewStrategy, aW as MaterializedViewStrategyHandle } from './types-4t1-tWS4.js';
2
2
 
3
3
  /**
4
4
  * Register a materialized view: a declared query whose result is
@@ -1,4 +1,4 @@
1
- import { aV as MaterializedViewStrategy, aW as MaterializedViewStrategyHandle } from './types-CDwSSXiI.cjs';
1
+ import { aV as MaterializedViewStrategy, aW as MaterializedViewStrategyHandle } from './types-BpPV5uyy.cjs';
2
2
 
3
3
  /**
4
4
  * Register a materialized view: a declared query whose result is
@@ -1,4 +1,4 @@
1
- import { aX as OverlayedViewStrategy, a_ as OverlayedViewStrategyHandle } from './types-CDwSSXiI.cjs';
1
+ import { aX as OverlayedViewStrategy, a_ as OverlayedViewStrategyHandle } from './types-BpPV5uyy.cjs';
2
2
 
3
3
  /**
4
4
  * Register a read-shadow overlay: bind an MV-owned base collection to
@@ -1,4 +1,4 @@
1
- import { aX as OverlayedViewStrategy, a_ as OverlayedViewStrategyHandle } from './types-pax34sec.js';
1
+ import { aX as OverlayedViewStrategy, a_ as OverlayedViewStrategyHandle } from './types-4t1-tWS4.js';
2
2
 
3
3
  /**
4
4
  * Register a read-shadow overlay: bind an MV-owned base collection to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noy-db/hub",
3
- "version": "0.2.0-pre.13",
3
+ "version": "0.2.0-pre.15",
4
4
  "description": "Zero-knowledge, offline-first, encrypted document store — core library with AES-256-GCM, PBKDF2, multi-user keyring, and sync engine",
5
5
  "license": "MIT",
6
6
  "author": "vLannaAi <vicio@lanna.ai>",
@@ -269,14 +269,14 @@
269
269
  "node": ">=18.0.0"
270
270
  },
271
271
  "dependencies": {
272
- "@noy-db/attestation": "0.2.0-pre.13"
272
+ "@noy-db/attestation": "0.2.0-pre.15"
273
273
  },
274
274
  "devDependencies": {
275
275
  "@types/node": "^22.0.0",
276
276
  "esbuild": "^0.25.0",
277
277
  "zod": "^3.23.0",
278
278
  "zod-to-json-schema": "^3.25.2",
279
- "@noy-db/on-shamir": "0.2.0-pre.13"
279
+ "@noy-db/on-shamir": "0.2.0-pre.15"
280
280
  },
281
281
  "peerDependencies": {
282
282
  "zod-to-json-schema": "^3.25.0"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/materialized-views/with-materialized-view.ts"],"sourcesContent":["import { MaterializedViewConfigError, ValidationError } from '../errors.js'\nimport type { MaterializedViewStrategy, MaterializedViewStrategyHandle } from './types.js'\n\n/**\n * Register a materialized view: a declared query whose result is\n * persisted as a queryable collection and kept fresh as sources\n * change. Writes go through the standard `Collection.put` pipeline;\n * refresh-driven deletes route through `Collection._internalDelete` so\n * user `onDelete` guards on the output collection aren't tripped by\n * housekeeping.\n *\n * Two registration modes:\n * - **single-source** — declare `query: (db) => Query<TRow>`; the\n * dependency analyzer derives source collections from the plan.\n * - **UNION** — declare `unionSources: [{ collection, map }, ...]`\n * plus optional `groupBy` + `aggregate`; the executor reads each\n * arm, maps to the unified row shape, concatenates, then groups\n * and aggregates.\n *\n * The two modes are mutually exclusive — exactly one of `query` /\n * `unionSources` must be set at registration time.\n *\n * See docs/superpowers/specs/2026-05-20-dim14-mv-v2-design.md (single-source v2)\n * and docs/superpowers/specs/2026-05-21-dim14-mv-multikey-and-union.md (UNION).\n */\nexport function withMaterializedView<TRow extends Record<string, unknown>>(\n spec: MaterializedViewStrategy<TRow>,\n): MaterializedViewStrategyHandle {\n if (!spec.name || spec.name.length === 0) {\n throw new ValidationError('withMaterializedView: name is required')\n }\n // Mutual exclusion: query and unionSources cannot coexist.\n if (spec.query && spec.unionSources) {\n throw new MaterializedViewConfigError(\n 'query and unionSources are mutually exclusive — pick one',\n )\n }\n // Strategy must declare one of the two.\n if (!spec.query && !spec.unionSources) {\n throw new MaterializedViewConfigError(\n 'strategy must declare either query or unionSources',\n )\n }\n if (spec.query !== undefined && typeof spec.query !== 'function') {\n throw new ValidationError('withMaterializedView: query must be a function returning a Query<T>')\n }\n // UNION-form invariants.\n if (spec.unionSources) {\n if (spec.unionSources.length < 2) {\n throw new MaterializedViewConfigError(\n 'unionSources requires at least 2 source collections',\n )\n }\n const seen = new Set<string>()\n for (const s of spec.unionSources) {\n if (typeof s?.collection !== 'string' || s.collection.length === 0) {\n throw new MaterializedViewConfigError(\n 'each unionSources entry must declare a non-empty `collection` string',\n )\n }\n if (typeof s.map !== 'function') {\n throw new MaterializedViewConfigError(\n `unionSources entry for \"${s.collection}\" is missing a \\`map\\` function`,\n )\n }\n if (seen.has(s.collection)) {\n throw new MaterializedViewConfigError(\n `unionSources must reference distinct collections (duplicate: \"${s.collection}\")`,\n )\n }\n seen.add(s.collection)\n }\n if (Array.isArray(spec.groupBy) && spec.groupBy.length === 0) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": groupBy must not be an empty array — omit it or provide at least one field name`,\n )\n }\n if (spec.aggregate && !spec.groupBy) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": UNION strategy with aggregate requires groupBy — `\n + `use groupBy to declare the bucketing keys, or remove aggregate for a pure dedup MV`,\n )\n }\n if (spec.predicates) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": predicates are not supported on UNION strategies — `\n + `UNION mode does not use a Query<T> chain, so .wherePredicate() cannot fire. `\n + `Use the query() form, or open an issue if per-arm predicates are needed`,\n )\n }\n }\n if (typeof spec.rowKey !== 'function') {\n throw new ValidationError('withMaterializedView: rowKey is required (no default; see spec § Type surface)')\n }\n if (spec.refresh !== 'eager' && spec.refresh !== 'lazy' && spec.refresh !== 'manual') {\n throw new ValidationError(\n `withMaterializedView: refresh must be 'eager' | 'lazy' | 'manual', got \"${String(spec.refresh)}\"`,\n )\n }\n return {\n __noydb_strategy: 'materialized-view',\n spec,\n }\n}\n"],"mappings":";;;;;;AAyBO,SAAS,qBACd,MACgC;AAChC,MAAI,CAAC,KAAK,QAAQ,KAAK,KAAK,WAAW,GAAG;AACxC,UAAM,IAAI,gBAAgB,wCAAwC;AAAA,EACpE;AAEA,MAAI,KAAK,SAAS,KAAK,cAAc;AACnC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,KAAK,SAAS,CAAC,KAAK,cAAc;AACrC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,KAAK,UAAU,UAAa,OAAO,KAAK,UAAU,YAAY;AAChE,UAAM,IAAI,gBAAgB,qEAAqE;AAAA,EACjG;AAEA,MAAI,KAAK,cAAc;AACrB,QAAI,KAAK,aAAa,SAAS,GAAG;AAChC,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,KAAK,KAAK,cAAc;AACjC,UAAI,OAAO,GAAG,eAAe,YAAY,EAAE,WAAW,WAAW,GAAG;AAClE,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,UAAI,OAAO,EAAE,QAAQ,YAAY;AAC/B,cAAM,IAAI;AAAA,UACR,2BAA2B,EAAE,UAAU;AAAA,QACzC;AAAA,MACF;AACA,UAAI,KAAK,IAAI,EAAE,UAAU,GAAG;AAC1B,cAAM,IAAI;AAAA,UACR,iEAAiE,EAAE,UAAU;AAAA,QAC/E;AAAA,MACF;AACA,WAAK,IAAI,EAAE,UAAU;AAAA,IACvB;AACA,QAAI,MAAM,QAAQ,KAAK,OAAO,KAAK,KAAK,QAAQ,WAAW,GAAG;AAC5D,YAAM,IAAI;AAAA,QACR,yBAAyB,KAAK,IAAI;AAAA,MACpC;AAAA,IACF;AACA,QAAI,KAAK,aAAa,CAAC,KAAK,SAAS;AACnC,YAAM,IAAI;AAAA,QACR,yBAAyB,KAAK,IAAI;AAAA,MAEpC;AAAA,IACF;AACA,QAAI,KAAK,YAAY;AACnB,YAAM,IAAI;AAAA,QACR,yBAAyB,KAAK,IAAI;AAAA,MAGpC;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,KAAK,WAAW,YAAY;AACrC,UAAM,IAAI,gBAAgB,mFAAgF;AAAA,EAC5G;AACA,MAAI,KAAK,YAAY,WAAW,KAAK,YAAY,UAAU,KAAK,YAAY,UAAU;AACpF,UAAM,IAAI;AAAA,MACR,2EAA2E,OAAO,KAAK,OAAO,CAAC;AAAA,IACjG;AAAA,EACF;AACA,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB;AAAA,EACF;AACF;","names":[]}