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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (384) hide show
  1. package/README.md +126 -0
  2. package/dist/adapter/index.d.ts +9 -0
  3. package/dist/adapter/index.js +14 -0
  4. package/dist/aggregate/index.d.ts +3 -2
  5. package/dist/aggregate/index.js +10 -8
  6. package/dist/aggregate/index.js.map +1 -1
  7. package/dist/attestation/index.d.ts +7 -5
  8. package/dist/attestation/index.js +7 -6
  9. package/dist/attestation/index.js.map +1 -1
  10. package/dist/blobs/index.d.ts +9 -7
  11. package/dist/blobs/index.js +12 -6
  12. package/dist/blobs/index.js.map +1 -1
  13. package/dist/bundle/index.d.ts +29 -18
  14. package/dist/bundle/index.js +53 -192
  15. package/dist/bundle/index.js.map +1 -1
  16. package/dist/{chunk-75QDHSE4.js → chunk-2QDWRK3B.js} +5 -5
  17. package/dist/{chunk-74JEQFMT.js → chunk-2UT7HJJR.js} +5 -5
  18. package/dist/chunk-2UT7HJJR.js.map +1 -0
  19. package/dist/{chunk-FCDO7UAO.js → chunk-2ZYIN3BL.js} +2 -2
  20. package/dist/{chunk-BFI3RS42.js → chunk-33EQCZXE.js} +2 -2
  21. package/dist/chunk-33EQCZXE.js.map +1 -0
  22. package/dist/{chunk-WRLHNG6H.js → chunk-4K4QKYK4.js} +4 -4
  23. package/dist/chunk-4K4QKYK4.js.map +1 -0
  24. package/dist/{chunk-HGZ7DC5H.js → chunk-4L6N56B4.js} +2 -2
  25. package/dist/chunk-4L6N56B4.js.map +1 -0
  26. package/dist/{chunk-YMYK7US4.js → chunk-4YD7VZH4.js} +2 -2
  27. package/dist/chunk-4YD7VZH4.js.map +1 -0
  28. package/dist/{chunk-ZUMGGHRB.js → chunk-5PTX7PKD.js} +4 -4
  29. package/dist/{chunk-QAU5HM6Q.js → chunk-6EKRQ7QF.js} +3 -3
  30. package/dist/{chunk-ZC2AAE6J.js → chunk-7GPQPLGO.js} +19 -4
  31. package/dist/chunk-7GPQPLGO.js.map +1 -0
  32. package/dist/{chunk-EPK6A3WJ.js → chunk-A3JMGXPG.js} +2 -2
  33. package/dist/chunk-A3JMGXPG.js.map +1 -0
  34. package/dist/{chunk-FXQYZNOW.js → chunk-A7DNZVAV.js} +1 -1
  35. package/dist/chunk-A7DNZVAV.js.map +1 -0
  36. package/dist/{chunk-UMLVJTYV.js → chunk-ADB7GPM3.js} +7 -4
  37. package/dist/chunk-ADB7GPM3.js.map +1 -0
  38. package/dist/{chunk-YL2DR3HY.js → chunk-ASIGWYRA.js} +2 -2
  39. package/dist/chunk-ASIGWYRA.js.map +1 -0
  40. package/dist/{chunk-2EYC3WDT.js → chunk-AWU4JF7C.js} +93 -93
  41. package/dist/chunk-AWU4JF7C.js.map +1 -0
  42. package/dist/chunk-BEZ2EV43.js +129 -0
  43. package/dist/chunk-BEZ2EV43.js.map +1 -0
  44. package/dist/{chunk-IS5HWQO7.js → chunk-CIZDUAHI.js} +30 -4
  45. package/dist/chunk-CIZDUAHI.js.map +1 -0
  46. package/dist/{chunk-4OQWR46B.js → chunk-CUPTCF4R.js} +5 -5
  47. package/dist/chunk-CUPTCF4R.js.map +1 -0
  48. package/dist/{chunk-5ZGZ6HIZ.js → chunk-DEE4CRYN.js} +30 -7
  49. package/dist/chunk-DEE4CRYN.js.map +1 -0
  50. package/dist/{chunk-UOF74WQY.js → chunk-E2MDPPOC.js} +37 -2
  51. package/dist/chunk-E2MDPPOC.js.map +1 -0
  52. package/dist/{chunk-SAVQ6E2O.js → chunk-FFKQ65YX.js} +10 -3
  53. package/dist/chunk-FFKQ65YX.js.map +1 -0
  54. package/dist/{chunk-2XLVPKXG.js → chunk-G7ALNCQH.js} +10 -2
  55. package/dist/chunk-G7ALNCQH.js.map +1 -0
  56. package/dist/chunk-GGXN73NM.js +252 -0
  57. package/dist/chunk-GGXN73NM.js.map +1 -0
  58. package/dist/{chunk-UVPGJXVO.js → chunk-JOJPBZSZ.js} +5 -5
  59. package/dist/{chunk-YDLAFP36.js → chunk-JZ5DS4DP.js} +346 -3
  60. package/dist/chunk-JZ5DS4DP.js.map +1 -0
  61. package/dist/{chunk-KMI2NBBF.js → chunk-K4JID2ZE.js} +44 -8
  62. package/dist/chunk-K4JID2ZE.js.map +1 -0
  63. package/dist/{aggregate/index.cjs → chunk-K6NVSHAH.js} +241 -210
  64. package/dist/chunk-K6NVSHAH.js.map +1 -0
  65. package/dist/chunk-KJ3K5VZM.js +524 -0
  66. package/dist/chunk-KJ3K5VZM.js.map +1 -0
  67. package/dist/chunk-L5HHBOMS.js +232 -0
  68. package/dist/chunk-L5HHBOMS.js.map +1 -0
  69. package/dist/{chunk-YK72A4IT.js → chunk-LBILABKZ.js} +4 -4
  70. package/dist/chunk-LXKBG52H.js +151 -0
  71. package/dist/chunk-LXKBG52H.js.map +1 -0
  72. package/dist/{chunk-NGSPBLLE.js → chunk-MPIZQGFA.js} +50 -20
  73. package/dist/chunk-MPIZQGFA.js.map +1 -0
  74. package/dist/{chunk-4X2S7PBF.js → chunk-MRJPSXPE.js} +84 -70
  75. package/dist/chunk-MRJPSXPE.js.map +1 -0
  76. package/dist/{chunk-6S3LLAQ5.js → chunk-MSMDDDDJ.js} +3 -3
  77. package/dist/{chunk-6S3LLAQ5.js.map → chunk-MSMDDDDJ.js.map} +1 -1
  78. package/dist/{chunk-GAUBWHAF.js → chunk-OCYFFBZS.js} +4 -4
  79. package/dist/{chunk-NCO2JGKK.js → chunk-PDVP3C2I.js} +1 -1
  80. package/dist/chunk-PDVP3C2I.js.map +1 -0
  81. package/dist/chunk-PZ5AY32C.js +10 -0
  82. package/dist/{chunk-P6256WTJ.js → chunk-QBLPY44S.js} +3 -3
  83. package/dist/chunk-QBLPY44S.js.map +1 -0
  84. package/dist/{query/index.cjs → chunk-QGBBQKDS.js} +706 -831
  85. package/dist/chunk-QGBBQKDS.js.map +1 -0
  86. package/dist/{chunk-FS7A4XNF.js → chunk-QWJCNXH2.js} +3 -3
  87. package/dist/chunk-QXWJQ7UU.js +380 -0
  88. package/dist/chunk-QXWJQ7UU.js.map +1 -0
  89. package/dist/{chunk-GDTCGIPX.js → chunk-RCB6GWTC.js} +27 -5
  90. package/dist/chunk-RCB6GWTC.js.map +1 -0
  91. package/dist/{chunk-K5PVGKE4.js → chunk-SAAJYSVV.js} +2 -2
  92. package/dist/{chunk-G7PAZ3TD.js → chunk-SHJ3BRTZ.js} +46 -10
  93. package/dist/chunk-SHJ3BRTZ.js.map +1 -0
  94. package/dist/chunk-SQXTKZL7.js +125 -0
  95. package/dist/chunk-SQXTKZL7.js.map +1 -0
  96. package/dist/chunk-STNPB3UM.js +9 -0
  97. package/dist/chunk-STNPB3UM.js.map +1 -0
  98. package/dist/{chunk-NSLTPGEN.js → chunk-TMUMMAWV.js} +12 -2
  99. package/dist/{chunk-NSLTPGEN.js.map → chunk-TMUMMAWV.js.map} +1 -1
  100. package/dist/{chunk-LS3JLEIB.js → chunk-TZGWI5AX.js} +4 -4
  101. package/dist/{chunk-GD3BGKAR.js → chunk-UBF5JEOJ.js} +44 -5
  102. package/dist/chunk-UBF5JEOJ.js.map +1 -0
  103. package/dist/{chunk-T6HQMVML.js → chunk-W5NVNJDX.js} +5250 -801
  104. package/dist/chunk-W5NVNJDX.js.map +1 -0
  105. package/dist/chunk-XAYDHD2O.js +123 -0
  106. package/dist/chunk-XAYDHD2O.js.map +1 -0
  107. package/dist/{chunk-LOL725S4.js → chunk-XYW2CSCI.js} +31 -3
  108. package/dist/chunk-XYW2CSCI.js.map +1 -0
  109. package/dist/{chunk-TLFUDXVV.js → chunk-ZCMOS4H2.js} +31 -10
  110. package/dist/chunk-ZCMOS4H2.js.map +1 -0
  111. package/dist/{chunk-FBMXWVGP.js → chunk-ZK66B5EY.js} +450 -30
  112. package/dist/chunk-ZK66B5EY.js.map +1 -0
  113. package/dist/{chunk-GVXBHCZ2.js → chunk-ZPJSQPQH.js} +66 -7
  114. package/dist/chunk-ZPJSQPQH.js.map +1 -0
  115. package/dist/consent/index.d.ts +8 -6
  116. package/dist/consent/index.js +4 -3
  117. package/dist/consent/index.js.map +1 -1
  118. package/dist/crdt/index.js +1 -0
  119. package/dist/crdt/index.js.map +1 -1
  120. package/dist/{crypto-H2Y3DDFW.js → crypto-OSIV2IIW.js} +8 -3
  121. package/dist/{ulid-CiM2OAeM.d.ts → decrypt-partition-CXS5KopB.d.ts} +37 -82
  122. package/dist/{delegation-QSC7G5QC.js → delegation-BQTRJATI.js} +6 -5
  123. package/dist/derivations/index.d.ts +11 -9
  124. package/dist/derivations/index.js +9 -6
  125. package/dist/{dev-unlock-Cf2B7Kih.d.ts → dev-unlock-CnHTTVea.d.ts} +1 -1
  126. package/dist/discriminant-BN9REW3o.d.ts +60 -0
  127. package/dist/errors-BAWO5Z5a.d.ts +1500 -0
  128. package/dist/executor-KYSKFJ2R.js +9 -0
  129. package/dist/executor-M7W3NEKR.js +9 -0
  130. package/dist/executor-VDTDGWO6.js +13 -0
  131. package/dist/{fanout-sidecar-NRBWSLRK.js → fanout-sidecar-AZALISHB.js} +3 -2
  132. package/dist/fanout-sidecar-AZALISHB.js.map +1 -0
  133. package/dist/forget/index.d.ts +1 -0
  134. package/dist/forget/index.js +15 -0
  135. package/dist/guards/index.d.ts +16 -8
  136. package/dist/guards/index.js +12 -5
  137. package/dist/{hash-gVn_uKhp.d.ts → hash-oc5paYl0.d.ts} +1 -1
  138. package/dist/history/index.d.ts +9 -7
  139. package/dist/history/index.js +10 -7
  140. package/dist/history/index.js.map +1 -1
  141. package/dist/i18n/index.d.ts +8 -6
  142. package/dist/i18n/index.js +116 -15
  143. package/dist/i18n/index.js.map +1 -1
  144. package/dist/index-BMmajblo.d.ts +362 -0
  145. package/dist/index-BmhGztUi.d.ts +93 -0
  146. package/dist/{types-D9eB0Rvh.d.ts → index-DHn9lEP0.d.ts} +4305 -1523
  147. package/dist/{index-BF1B2HB9.d.ts → index-Ddm8D3Oo.d.ts} +186 -1151
  148. package/dist/index.d.ts +342 -70
  149. package/dist/index.js +420 -112
  150. package/dist/index.js.map +1 -1
  151. package/dist/indexing/index.d.ts +3 -3
  152. package/dist/indexing/index.js +5 -4
  153. package/dist/indexing/index.js.map +1 -1
  154. package/dist/issue-DPHRF2TI.js +13 -0
  155. package/dist/kernel/index.d.ts +11 -0
  156. package/dist/kernel/index.js +50 -0
  157. package/dist/{lazy-builder-Rpd-V3jP.d.ts → lazy-builder-ChSqcF5t.d.ts} +2 -2
  158. package/dist/{ledger-WOEJUYTP.js → ledger-CI7GSMLB.js} +7 -6
  159. package/dist/materialized-views/index.d.ts +23 -20
  160. package/dist/materialized-views/index.js +9 -7
  161. package/dist/mime-magic-JeLKHRng.d.ts +103 -0
  162. package/dist/noydb-NAU2DTFP.js +40 -0
  163. package/dist/overlay-views/index.d.ts +32 -12
  164. package/dist/overlay-views/index.js +7 -6
  165. package/dist/periods/index.d.ts +8 -6
  166. package/dist/periods/index.js +7 -6
  167. package/dist/{predicate-Dnu81tsS.d.cts → predicate-BmhBSPCH.d.ts} +87 -5
  168. package/dist/{public-envelope-OHQ5UZFM.js → public-envelope-MFMBFJLZ.js} +5 -4
  169. package/dist/query/index.d.ts +4 -3
  170. package/dist/query/index.js +14 -6
  171. package/dist/read-only-facade-3OQRZ3VS.js +8 -0
  172. package/dist/registry-IX5WKK4F.js +8 -0
  173. package/dist/registry-LMHO5265.js +11 -0
  174. package/dist/registry-TXCKWH26.js +9 -0
  175. package/dist/registry-VFEDQSYV.js +9 -0
  176. package/dist/revoke-5PUOZPJJ.js +18 -0
  177. package/dist/revoke-5PUOZPJJ.js.map +1 -0
  178. package/dist/sealed-record/index.d.ts +123 -0
  179. package/dist/sealed-record/index.js +43 -0
  180. package/dist/sealed-record/index.js.map +1 -0
  181. package/dist/session/index.d.ts +9 -7
  182. package/dist/session/index.js +4 -3
  183. package/dist/session/index.js.map +1 -1
  184. package/dist/shadow/index.d.ts +8 -6
  185. package/dist/shadow/index.js +3 -2
  186. package/dist/shadow/index.js.map +1 -1
  187. package/dist/{signer-M4K5HBLD.js → signer-UB5TQOZ6.js} +6 -5
  188. package/dist/signer-UB5TQOZ6.js.map +1 -0
  189. package/dist/snapshots/index.d.ts +30 -0
  190. package/dist/snapshots/index.js +153 -0
  191. package/dist/snapshots/index.js.map +1 -0
  192. package/dist/{stale-PAGCS4K5.js → stale-J3TUF5C5.js} +3 -2
  193. package/dist/stale-J3TUF5C5.js.map +1 -0
  194. package/dist/store/index.d.ts +15 -6
  195. package/dist/store/index.js +3 -2
  196. package/dist/{strategy-DSTrsZ8t.d.ts → strategy-8QGs5KQE.d.ts} +475 -7
  197. package/dist/sync/index.d.ts +7 -5
  198. package/dist/sync/index.js +5 -4
  199. package/dist/sync/index.js.map +1 -1
  200. package/dist/team/index.d.ts +8 -6
  201. package/dist/team/index.js +9 -8
  202. package/dist/transition-guard-Dy3NioQr.d.ts +165 -0
  203. package/dist/tx/index.d.ts +26 -8
  204. package/dist/tx/index.js +10 -5
  205. package/dist/tx/index.js.map +1 -1
  206. package/dist/ulid-DRH25k3y.d.ts +66 -0
  207. package/dist/{ulid-COREQ2RQ.js → ulid-KEPZMD2N.js} +2 -1
  208. package/dist/ulid-KEPZMD2N.js.map +1 -0
  209. package/dist/util/index.d.ts +2 -0
  210. package/dist/util/index.js +6 -1
  211. package/dist/util/index.js.map +1 -1
  212. package/dist/{with-materialized-view-qtoJ3xKJ.d.ts → with-materialized-view-DIVeez0W.d.ts} +2 -2
  213. package/dist/{with-overlayed-view-DFaRfgMr.d.ts → with-overlayed-view-DVZrc5Hb.d.ts} +2 -2
  214. package/dist/with-rollup-CbU-O4wP.d.ts +47 -0
  215. package/dist/zod-HAJM7LMS.js +14763 -0
  216. package/dist/zod-HAJM7LMS.js.map +1 -0
  217. package/package.json +73 -191
  218. package/dist/aggregate/index.cjs.map +0 -1
  219. package/dist/aggregate/index.d.cts +0 -38
  220. package/dist/attestation/index.cjs +0 -305
  221. package/dist/attestation/index.cjs.map +0 -1
  222. package/dist/attestation/index.d.cts +0 -52
  223. package/dist/blobs/index.cjs +0 -1480
  224. package/dist/blobs/index.cjs.map +0 -1
  225. package/dist/blobs/index.d.cts +0 -46
  226. package/dist/bundle/index.cjs +0 -18561
  227. package/dist/bundle/index.cjs.map +0 -1
  228. package/dist/bundle/index.d.cts +0 -176
  229. package/dist/chunk-2EYC3WDT.js.map +0 -1
  230. package/dist/chunk-2XLVPKXG.js.map +0 -1
  231. package/dist/chunk-4OQWR46B.js.map +0 -1
  232. package/dist/chunk-4UBOTYP5.js +0 -1367
  233. package/dist/chunk-4UBOTYP5.js.map +0 -1
  234. package/dist/chunk-4X2S7PBF.js.map +0 -1
  235. package/dist/chunk-5YHWBPOT.js +0 -19
  236. package/dist/chunk-5YHWBPOT.js.map +0 -1
  237. package/dist/chunk-5ZGZ6HIZ.js.map +0 -1
  238. package/dist/chunk-74JEQFMT.js.map +0 -1
  239. package/dist/chunk-A6SWRXUQ.js +0 -118
  240. package/dist/chunk-A6SWRXUQ.js.map +0 -1
  241. package/dist/chunk-BFI3RS42.js.map +0 -1
  242. package/dist/chunk-EMEX37ZN.js +0 -51
  243. package/dist/chunk-EMEX37ZN.js.map +0 -1
  244. package/dist/chunk-EPK6A3WJ.js.map +0 -1
  245. package/dist/chunk-FBMXWVGP.js.map +0 -1
  246. package/dist/chunk-FXQYZNOW.js.map +0 -1
  247. package/dist/chunk-G7PAZ3TD.js.map +0 -1
  248. package/dist/chunk-GD3BGKAR.js.map +0 -1
  249. package/dist/chunk-GDTCGIPX.js.map +0 -1
  250. package/dist/chunk-GVXBHCZ2.js.map +0 -1
  251. package/dist/chunk-HGZ7DC5H.js.map +0 -1
  252. package/dist/chunk-IS5HWQO7.js.map +0 -1
  253. package/dist/chunk-KMI2NBBF.js.map +0 -1
  254. package/dist/chunk-KYKMKLJ6.js +0 -340
  255. package/dist/chunk-KYKMKLJ6.js.map +0 -1
  256. package/dist/chunk-LOL725S4.js.map +0 -1
  257. package/dist/chunk-MRIBLZL3.js +0 -86
  258. package/dist/chunk-MRIBLZL3.js.map +0 -1
  259. package/dist/chunk-NCO2JGKK.js.map +0 -1
  260. package/dist/chunk-NGSPBLLE.js.map +0 -1
  261. package/dist/chunk-P6256WTJ.js.map +0 -1
  262. package/dist/chunk-SAVQ6E2O.js.map +0 -1
  263. package/dist/chunk-T6HQMVML.js.map +0 -1
  264. package/dist/chunk-TLFUDXVV.js.map +0 -1
  265. package/dist/chunk-UMLVJTYV.js.map +0 -1
  266. package/dist/chunk-UOF74WQY.js.map +0 -1
  267. package/dist/chunk-WRLHNG6H.js.map +0 -1
  268. package/dist/chunk-YDLAFP36.js.map +0 -1
  269. package/dist/chunk-YL2DR3HY.js.map +0 -1
  270. package/dist/chunk-YMYK7US4.js.map +0 -1
  271. package/dist/chunk-ZC2AAE6J.js.map +0 -1
  272. package/dist/consent/index.cjs +0 -204
  273. package/dist/consent/index.cjs.map +0 -1
  274. package/dist/consent/index.d.cts +0 -25
  275. package/dist/crdt/index.cjs +0 -152
  276. package/dist/crdt/index.cjs.map +0 -1
  277. package/dist/crdt/index.d.cts +0 -30
  278. package/dist/derivations/index.cjs +0 -351
  279. package/dist/derivations/index.cjs.map +0 -1
  280. package/dist/derivations/index.d.cts +0 -72
  281. package/dist/dev-unlock-De3mjQWv.d.cts +0 -263
  282. package/dist/executor-BZKFZVRC.js +0 -8
  283. package/dist/executor-GFZFDQXV.js +0 -8
  284. package/dist/executor-KT2IOZVP.js +0 -11
  285. package/dist/fanout-sidecar-NRBWSLRK.js.map +0 -1
  286. package/dist/guards/index.cjs +0 -322
  287. package/dist/guards/index.cjs.map +0 -1
  288. package/dist/guards/index.d.cts +0 -31
  289. package/dist/hash-vBCB0-Ps.d.cts +0 -63
  290. package/dist/history/index.cjs +0 -1222
  291. package/dist/history/index.cjs.map +0 -1
  292. package/dist/history/index.d.cts +0 -63
  293. package/dist/i18n/index.cjs +0 -840
  294. package/dist/i18n/index.cjs.map +0 -1
  295. package/dist/i18n/index.d.cts +0 -39
  296. package/dist/index-DVkvrgpm.d.cts +0 -2290
  297. package/dist/index.cjs +0 -23590
  298. package/dist/index.cjs.map +0 -1
  299. package/dist/index.d.cts +0 -778
  300. package/dist/indexing/index.cjs +0 -738
  301. package/dist/indexing/index.cjs.map +0 -1
  302. package/dist/indexing/index.d.cts +0 -36
  303. package/dist/issue-BAJ7ZB4S.js +0 -12
  304. package/dist/lazy-builder-C-rPfWG0.d.cts +0 -304
  305. package/dist/materialized-views/index.cjs +0 -837
  306. package/dist/materialized-views/index.cjs.map +0 -1
  307. package/dist/materialized-views/index.d.cts +0 -184
  308. package/dist/mime-magic-CBBSOkjm.d.cts +0 -50
  309. package/dist/mime-magic-CBBSOkjm.d.ts +0 -50
  310. package/dist/noydb-XNQSKXGO.js +0 -34
  311. package/dist/overlay-views/index.cjs +0 -359
  312. package/dist/overlay-views/index.cjs.map +0 -1
  313. package/dist/overlay-views/index.d.cts +0 -82
  314. package/dist/periods/index.cjs +0 -1041
  315. package/dist/periods/index.cjs.map +0 -1
  316. package/dist/periods/index.d.cts +0 -22
  317. package/dist/predicate-Dnu81tsS.d.ts +0 -185
  318. package/dist/query/index.cjs.map +0 -1
  319. package/dist/query/index.d.cts +0 -3
  320. package/dist/read-only-facade-ITU6L7BL.js +0 -7
  321. package/dist/registry-2IEARCGT.js +0 -7
  322. package/dist/registry-CDHASH73.js +0 -10
  323. package/dist/registry-EMGLZGR6.js +0 -8
  324. package/dist/registry-NQALYR77.js +0 -8
  325. package/dist/revoke-7JOVLZFD.js +0 -17
  326. package/dist/session/index.cjs +0 -495
  327. package/dist/session/index.cjs.map +0 -1
  328. package/dist/session/index.d.cts +0 -46
  329. package/dist/shadow/index.cjs +0 -133
  330. package/dist/shadow/index.cjs.map +0 -1
  331. package/dist/shadow/index.d.cts +0 -17
  332. package/dist/store/index.cjs +0 -1083
  333. package/dist/store/index.cjs.map +0 -1
  334. package/dist/store/index.d.cts +0 -492
  335. package/dist/strategy-BSxFXGzb.d.cts +0 -110
  336. package/dist/strategy-DSTrsZ8t.d.cts +0 -601
  337. package/dist/sync/index.cjs +0 -1062
  338. package/dist/sync/index.cjs.map +0 -1
  339. package/dist/sync/index.d.cts +0 -43
  340. package/dist/team/index.cjs +0 -2798
  341. package/dist/team/index.cjs.map +0 -1
  342. package/dist/team/index.d.cts +0 -118
  343. package/dist/tx/index.cjs +0 -544
  344. package/dist/tx/index.cjs.map +0 -1
  345. package/dist/tx/index.d.cts +0 -21
  346. package/dist/types-CSLcfytP.d.cts +0 -12493
  347. package/dist/ulid-CG2YvAbg.d.cts +0 -603
  348. package/dist/util/index.cjs +0 -230
  349. package/dist/util/index.cjs.map +0 -1
  350. package/dist/util/index.d.cts +0 -77
  351. package/dist/with-derivation-Bzpj6UTv.d.ts +0 -13
  352. package/dist/with-derivation-DWajFh4K.d.cts +0 -13
  353. package/dist/with-guard-DF_Ul3DT.d.cts +0 -18
  354. package/dist/with-guard-DR7U-l4v.d.ts +0 -18
  355. package/dist/with-materialized-view-_piodoIz.d.cts +0 -27
  356. package/dist/with-overlayed-view-DwzCKxn2.d.cts +0 -13
  357. /package/dist/{crypto-H2Y3DDFW.js.map → adapter/index.js.map} +0 -0
  358. /package/dist/{chunk-75QDHSE4.js.map → chunk-2QDWRK3B.js.map} +0 -0
  359. /package/dist/{chunk-FCDO7UAO.js.map → chunk-2ZYIN3BL.js.map} +0 -0
  360. /package/dist/{chunk-ZUMGGHRB.js.map → chunk-5PTX7PKD.js.map} +0 -0
  361. /package/dist/{chunk-QAU5HM6Q.js.map → chunk-6EKRQ7QF.js.map} +0 -0
  362. /package/dist/{chunk-UVPGJXVO.js.map → chunk-JOJPBZSZ.js.map} +0 -0
  363. /package/dist/{chunk-YK72A4IT.js.map → chunk-LBILABKZ.js.map} +0 -0
  364. /package/dist/{chunk-GAUBWHAF.js.map → chunk-OCYFFBZS.js.map} +0 -0
  365. /package/dist/{delegation-QSC7G5QC.js.map → chunk-PZ5AY32C.js.map} +0 -0
  366. /package/dist/{chunk-FS7A4XNF.js.map → chunk-QWJCNXH2.js.map} +0 -0
  367. /package/dist/{chunk-K5PVGKE4.js.map → chunk-SAAJYSVV.js.map} +0 -0
  368. /package/dist/{chunk-LS3JLEIB.js.map → chunk-TZGWI5AX.js.map} +0 -0
  369. /package/dist/{executor-BZKFZVRC.js.map → crypto-OSIV2IIW.js.map} +0 -0
  370. /package/dist/{executor-GFZFDQXV.js.map → delegation-BQTRJATI.js.map} +0 -0
  371. /package/dist/{executor-KT2IOZVP.js.map → executor-KYSKFJ2R.js.map} +0 -0
  372. /package/dist/{issue-BAJ7ZB4S.js.map → executor-M7W3NEKR.js.map} +0 -0
  373. /package/dist/{ledger-WOEJUYTP.js.map → executor-VDTDGWO6.js.map} +0 -0
  374. /package/dist/{noydb-XNQSKXGO.js.map → forget/index.js.map} +0 -0
  375. /package/dist/{public-envelope-OHQ5UZFM.js.map → issue-DPHRF2TI.js.map} +0 -0
  376. /package/dist/{read-only-facade-ITU6L7BL.js.map → kernel/index.js.map} +0 -0
  377. /package/dist/{registry-2IEARCGT.js.map → ledger-CI7GSMLB.js.map} +0 -0
  378. /package/dist/{registry-CDHASH73.js.map → noydb-NAU2DTFP.js.map} +0 -0
  379. /package/dist/{registry-EMGLZGR6.js.map → public-envelope-MFMBFJLZ.js.map} +0 -0
  380. /package/dist/{registry-NQALYR77.js.map → read-only-facade-3OQRZ3VS.js.map} +0 -0
  381. /package/dist/{revoke-7JOVLZFD.js.map → registry-IX5WKK4F.js.map} +0 -0
  382. /package/dist/{signer-M4K5HBLD.js.map → registry-LMHO5265.js.map} +0 -0
  383. /package/dist/{stale-PAGCS4K5.js.map → registry-TXCKWH26.js.map} +0 -0
  384. /package/dist/{ulid-COREQ2RQ.js.map → registry-VFEDQSYV.js.map} +0 -0
@@ -1,225 +1,25 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/query/index.ts
21
- var query_exports = {};
22
- __export(query_exports, {
23
- Aggregation: () => Aggregation,
24
- CollectionIndexes: () => CollectionIndexes,
25
- DEFAULT_JOIN_MAX_ROWS: () => DEFAULT_JOIN_MAX_ROWS,
26
- DanglingReferenceError: () => DanglingReferenceError,
27
- GROUPBY_MAX_CARDINALITY: () => GROUPBY_MAX_CARDINALITY,
28
- GROUPBY_WARN_CARDINALITY: () => GROUPBY_WARN_CARDINALITY,
29
- GroupCardinalityError: () => GroupCardinalityError,
30
- GroupedAggregation: () => GroupedAggregation,
31
- GroupedQuery: () => GroupedQuery,
32
- GroupedQueryN: () => GroupedQueryN,
33
- IndexRequiredError: () => IndexRequiredError,
34
- IndexWriteFailureError: () => IndexWriteFailureError,
35
- JoinTooLargeError: () => JoinTooLargeError,
36
- Query: () => Query,
37
- ScanBuilder: () => ScanBuilder,
38
- applyJoins: () => applyJoins,
39
- avg: () => avg,
40
- buildLiveAggregation: () => buildLiveAggregation,
41
- buildLiveQuery: () => buildLiveQuery,
42
- count: () => count,
43
- evaluateClause: () => evaluateClause,
44
- evaluateFieldClause: () => evaluateFieldClause,
45
- executePlan: () => executePlan,
46
- groupAndReduce: () => groupAndReduce,
47
- max: () => max,
48
- min: () => min,
49
- readPath: () => readPath,
50
- reduceRecords: () => reduceRecords,
51
- resetGroupByWarnings: () => resetGroupByWarnings,
52
- resetJoinWarnings: () => resetJoinWarnings,
53
- sum: () => sum
54
- });
55
- module.exports = __toCommonJS(query_exports);
56
-
57
- // src/query/predicate.ts
58
- function readPath(record, path) {
59
- if (record === null || record === void 0) return void 0;
60
- if (!path.includes(".")) {
61
- return record[path];
62
- }
63
- const segments = path.split(".");
64
- let cursor = record;
65
- for (const segment of segments) {
66
- if (cursor === null || cursor === void 0) return void 0;
67
- cursor = cursor[segment];
68
- }
69
- return cursor;
70
- }
71
- function evaluateFieldClause(record, clause) {
72
- const actual = readPath(record, clause.field);
73
- const { op, value } = clause;
74
- switch (op) {
75
- case "==":
76
- return actual === value;
77
- case "!=":
78
- return actual !== value;
79
- case "<":
80
- return isComparable(actual, value) && actual < value;
81
- case "<=":
82
- return isComparable(actual, value) && actual <= value;
83
- case ">":
84
- return isComparable(actual, value) && actual > value;
85
- case ">=":
86
- return isComparable(actual, value) && actual >= value;
87
- case "in":
88
- return Array.isArray(value) && value.includes(actual);
89
- case "contains":
90
- if (typeof actual === "string") return typeof value === "string" && actual.includes(value);
91
- if (Array.isArray(actual)) return actual.includes(value);
92
- return false;
93
- case "startsWith":
94
- return typeof actual === "string" && typeof value === "string" && actual.startsWith(value);
95
- case "between": {
96
- if (!Array.isArray(value) || value.length !== 2) return false;
97
- const [lo, hi] = value;
98
- if (!isComparable(actual, lo) || !isComparable(actual, hi)) return false;
99
- return actual >= lo && actual <= hi;
100
- }
101
- default: {
102
- const _exhaustive = op;
103
- void _exhaustive;
104
- return false;
105
- }
106
- }
107
- }
108
- function isComparable(a, b) {
109
- if (typeof a === "number" && typeof b === "number") return true;
110
- if (typeof a === "string" && typeof b === "string") return true;
111
- if (a instanceof Date && b instanceof Date) return true;
112
- return false;
113
- }
114
- function evaluateClause(record, clause) {
115
- switch (clause.type) {
116
- case "field":
117
- return evaluateFieldClause(record, clause);
118
- case "filter":
119
- return clause.fn(record);
120
- case "wherePredicate":
121
- return clause.fn(record, clause.ctx);
122
- case "group":
123
- if (clause.op === "and") {
124
- for (const child of clause.clauses) {
125
- if (!evaluateClause(record, child)) return false;
126
- }
127
- return true;
128
- } else {
129
- for (const child of clause.clauses) {
130
- if (evaluateClause(record, child)) return true;
131
- }
132
- return false;
133
- }
134
- }
135
- }
136
-
137
- // src/errors.ts
138
- var NoydbError = class extends Error {
139
- /** Machine-readable error code. Stable across library versions. */
140
- code;
141
- constructor(code, message) {
142
- super(message);
143
- this.name = "NoydbError";
144
- this.code = code;
145
- }
146
- };
147
- var GroupCardinalityError = class extends NoydbError {
148
- /** The field being grouped on. */
149
- field;
150
- /** Observed number of distinct groups at the moment the cap tripped. */
151
- cardinality;
152
- /** The cap that was exceeded. */
153
- maxGroups;
154
- constructor(field, cardinality, maxGroups) {
155
- super(
156
- "GROUP_CARDINALITY",
157
- `.groupBy("${field}") produced ${cardinality} distinct groups, exceeding the ${maxGroups}-group ceiling. This is almost always a query mistake \u2014 grouping on a high-uniqueness field like "id" or "createdAt" produces one bucket per record. Narrow the query with .where() before grouping, or group on a lower-cardinality field (status, category, clientId). If you genuinely need high-cardinality grouping, file an issue with your use case.`
158
- );
159
- this.name = "GroupCardinalityError";
160
- this.field = field;
161
- this.cardinality = cardinality;
162
- this.maxGroups = maxGroups;
163
- }
164
- };
165
- var IndexRequiredError = class extends NoydbError {
166
- collection;
167
- touchedFields;
168
- missingFields;
169
- constructor(args) {
170
- super(
171
- "INDEX_REQUIRED",
172
- `Collection "${args.collection}": query references unindexed fields in lazy mode (missing: ${args.missingFields.join(", ")}). Declare an index on each field, or use collection.scan() for non-indexed iteration.`
173
- );
174
- this.name = "IndexRequiredError";
175
- this.collection = args.collection;
176
- this.touchedFields = [...args.touchedFields];
177
- this.missingFields = [...args.missingFields];
178
- }
179
- };
180
- var IndexWriteFailureError = class extends NoydbError {
181
- recordId;
182
- field;
183
- op;
184
- cause;
185
- constructor(args) {
186
- super(
187
- "INDEX_WRITE_FAILURE",
188
- `Index side-car ${args.op} failed for field "${args.field}" on record "${args.recordId}"`
189
- );
190
- this.name = "IndexWriteFailureError";
191
- this.recordId = args.recordId;
192
- this.field = args.field;
193
- this.op = args.op;
194
- this.cause = args.cause;
195
- }
196
- };
197
- var JoinTooLargeError = class extends NoydbError {
198
- leftRows;
199
- rightRows;
200
- maxRows;
201
- side;
202
- constructor(opts) {
203
- super("JOIN_TOO_LARGE", opts.message);
204
- this.name = "JoinTooLargeError";
205
- this.leftRows = opts.leftRows;
206
- this.rightRows = opts.rightRows;
207
- this.maxRows = opts.maxRows;
208
- this.side = opts.side;
209
- }
210
- };
211
- var DanglingReferenceError = class extends NoydbError {
212
- field;
213
- target;
214
- refId;
215
- constructor(opts) {
216
- super("DANGLING_REFERENCE", opts.message);
217
- this.name = "DanglingReferenceError";
218
- this.field = opts.field;
219
- this.target = opts.target;
220
- this.refId = opts.refId;
221
- }
222
- };
1
+ import {
2
+ wrapMoneyReducers
3
+ } from "./chunk-K6NVSHAH.js";
4
+ import {
5
+ MoneyPrecisionError,
6
+ evaluateClause,
7
+ formatScaledInt,
8
+ hasFnClause,
9
+ moneyFieldClause,
10
+ parseToScaledInt,
11
+ readPath
12
+ } from "./chunk-KJ3K5VZM.js";
13
+ import {
14
+ applyI18nLocale
15
+ } from "./chunk-QXWJQ7UU.js";
16
+ import {
17
+ CrossJoinSourceUnknownError,
18
+ CrossJoinTooLargeError,
19
+ DanglingReferenceError,
20
+ JoinTooLargeError,
21
+ ValidationError
22
+ } from "./chunk-JZ5DS4DP.js";
223
23
 
224
24
  // src/query/join.ts
225
25
  var DEFAULT_JOIN_MAX_ROWS = 5e4;
@@ -249,15 +49,15 @@ function warnCeilingApproaching(target, side, rows, maxRows) {
249
49
  `[noy-db] .join() ${side} side is at ${pct}% of the ${maxRows}-row ceiling for target "${target}" (${rows} rows). Streaming joins over scan() are not yet supported for collections that need to exceed this.`
250
50
  );
251
51
  }
252
- function applyJoins(rows, joins, context) {
52
+ function applyJoins(rows, joins, context, locale) {
253
53
  if (joins.length === 0) return [...rows];
254
54
  let result = [...rows];
255
55
  for (const leg of joins) {
256
- result = applyOneJoin(result, leg, context);
56
+ result = applyOneJoin(result, leg, context, locale);
257
57
  }
258
58
  return result;
259
59
  }
260
- function applyOneJoin(leftRows, leg, context) {
60
+ function applyOneJoin(leftRows, leg, context, locale) {
261
61
  if (leg.isDictJoin) {
262
62
  const dictSource = context.resolveDictSource?.(leg.field);
263
63
  if (!dictSource) {
@@ -312,24 +112,27 @@ function applyOneJoin(leftRows, leg, context) {
312
112
  if (rightSnapshot.length > maxRows * JOIN_WARN_FRACTION) {
313
113
  warnCeilingApproaching(leg.target, "right", rightSnapshot.length, maxRows);
314
114
  }
115
+ const effLocale = locale ?? context.defaultLocale;
116
+ const i18nResolve = effLocale !== void 0 && source.i18nFields !== void 0 ? (right) => right !== null && typeof right === "object" ? applyI18nLocale(right, source.i18nFields, effLocale, void 0, "join") : right : void 0;
315
117
  const strategy = leg.strategy ?? (source.lookupById ? "nested" : "hash");
316
118
  if (strategy === "nested" && source.lookupById) {
317
119
  const lookup = (id) => source.lookupById?.(id);
318
- return nestedLoopJoin(leftRows, leg, lookup);
120
+ return nestedLoopJoin(leftRows, leg, lookup, i18nResolve);
319
121
  }
320
- return hashJoin(leftRows, leg, rightSnapshot);
122
+ return hashJoin(leftRows, leg, rightSnapshot, i18nResolve);
321
123
  }
322
- function nestedLoopJoin(leftRows, leg, lookupById) {
124
+ function nestedLoopJoin(leftRows, leg, lookupById, i18nResolve) {
323
125
  const out = [];
324
126
  for (const left of leftRows) {
325
127
  const rawId = readPath(left, leg.field);
326
128
  const key = coerceRefKey(rawId);
327
- const right = key === null ? void 0 : lookupById(key);
129
+ let right = key === null ? void 0 : lookupById(key);
130
+ if (i18nResolve && right !== void 0) right = i18nResolve(right);
328
131
  out.push(attachJoin(left, leg, right, rawId));
329
132
  }
330
133
  return out;
331
134
  }
332
- function hashJoin(leftRows, leg, rightSnapshot) {
135
+ function hashJoin(leftRows, leg, rightSnapshot, i18nResolve) {
333
136
  const rightMap = /* @__PURE__ */ new Map();
334
137
  for (const record of rightSnapshot) {
335
138
  const rawId = readPath(record, "id");
@@ -342,7 +145,8 @@ function hashJoin(leftRows, leg, rightSnapshot) {
342
145
  for (const left of leftRows) {
343
146
  const rawId = readPath(left, leg.field);
344
147
  const key = coerceRefKey(rawId);
345
- const right = key === null ? void 0 : rightMap.get(key);
148
+ let right = key === null ? void 0 : rightMap.get(key);
149
+ if (i18nResolve && right !== void 0) right = i18nResolve(right);
346
150
  out.push(attachJoin(left, leg, right, rawId));
347
151
  }
348
152
  return out;
@@ -466,6 +270,277 @@ var NO_AGGREGATE = {
466
270
  }
467
271
  };
468
272
 
273
+ // src/money/paths.ts
274
+ var SEGMENT_RE = /^(\*|[^.[\]*]+)(\[\])?$/;
275
+ var parseCache = /* @__PURE__ */ new Map();
276
+ function parseMoneyPath(path) {
277
+ const cached = parseCache.get(path);
278
+ if (cached) return cached;
279
+ if (typeof path !== "string" || path.length === 0) {
280
+ throw new ValidationError("moneyFields: path must be a non-empty string");
281
+ }
282
+ const segments = [];
283
+ for (const part of path.split(".")) {
284
+ const m = SEGMENT_RE.exec(part);
285
+ if (!m) {
286
+ throw new ValidationError(
287
+ `moneyFields: invalid path "${path}" \u2014 segment "${part}" must be a key, "key[]", "*", or "*[]"`
288
+ );
289
+ }
290
+ const array = m[2] === "[]";
291
+ segments.push(
292
+ m[1] === "*" ? { kind: "wildcard", array } : { kind: "key", key: m[1], array }
293
+ );
294
+ }
295
+ parseCache.set(path, segments);
296
+ return segments;
297
+ }
298
+ function isSimpleMoneyPath(path) {
299
+ return !path.includes(".") && !path.includes("[") && !path.includes("*");
300
+ }
301
+ function validateMoneyFieldPaths(moneyFields) {
302
+ for (const path of Object.keys(moneyFields)) parseMoneyPath(path);
303
+ }
304
+ function transformAtMoneyPath(node, path, segments, index, visit, lenient) {
305
+ if (node === null || node === void 0) return node;
306
+ const seg = segments[index];
307
+ const last = index === segments.length - 1;
308
+ if (seg.kind === "key") {
309
+ if (typeof node !== "object" || Array.isArray(node)) {
310
+ if (lenient) return node;
311
+ throw new ValidationError(
312
+ `moneyFields: path "${path}" expected an object at segment "${seg.key}", got ${Array.isArray(node) ? "an array" : typeof node}`
313
+ );
314
+ }
315
+ const obj2 = node;
316
+ if (!(seg.key in obj2) || obj2[seg.key] === null || obj2[seg.key] === void 0) return node;
317
+ if (seg.array) {
318
+ const arr = obj2[seg.key];
319
+ if (!Array.isArray(arr)) {
320
+ if (lenient) return node;
321
+ throw new ValidationError(
322
+ `moneyFields: path "${path}" declares "${seg.key}[]" but the value is not an array`
323
+ );
324
+ }
325
+ const cloned = [...arr];
326
+ if (last) {
327
+ for (let i = 0; i < cloned.length; i++) visit(cloned, i);
328
+ } else {
329
+ for (let i = 0; i < cloned.length; i++) {
330
+ cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
331
+ }
332
+ }
333
+ return { ...obj2, [seg.key]: cloned };
334
+ }
335
+ const clone2 = { ...obj2 };
336
+ if (last) {
337
+ visit(clone2, seg.key);
338
+ } else {
339
+ clone2[seg.key] = transformAtMoneyPath(clone2[seg.key], path, segments, index + 1, visit, lenient);
340
+ }
341
+ return clone2;
342
+ }
343
+ if (seg.array) {
344
+ if (!Array.isArray(node)) {
345
+ if (lenient) return node;
346
+ throw new ValidationError(`moneyFields: path "${path}" declares "*[]" but the value is not an array`);
347
+ }
348
+ const cloned = [...node];
349
+ if (last) {
350
+ for (let i = 0; i < cloned.length; i++) visit(cloned, i);
351
+ } else {
352
+ for (let i = 0; i < cloned.length; i++) {
353
+ cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
354
+ }
355
+ }
356
+ return cloned;
357
+ }
358
+ if (typeof node !== "object" || Array.isArray(node)) {
359
+ if (lenient) return node;
360
+ throw new ValidationError(
361
+ `moneyFields: path "${path}" applies "*" to a non-object (${Array.isArray(node) ? 'array \u2014 use "*[]"' : typeof node})`
362
+ );
363
+ }
364
+ const obj = node;
365
+ const clone = { ...obj };
366
+ for (const key of Object.keys(obj)) {
367
+ const v = clone[key];
368
+ if (v === null || v === void 0) continue;
369
+ if (last) visit(clone, key);
370
+ else clone[key] = transformAtMoneyPath(v, path, segments, index + 1, visit, lenient);
371
+ }
372
+ return clone;
373
+ }
374
+
375
+ // src/money/normalize.ts
376
+ function isMoneyValueObject(v) {
377
+ return typeof v === "object" && v !== null && "currency" in v;
378
+ }
379
+ function quantizeAmount(field, input, scale, rounding) {
380
+ const r = parseToScaledInt(input, scale, rounding);
381
+ if (!r.ok) {
382
+ if (r.reason === "precision") throw new MoneyPrecisionError(field, input, scale);
383
+ throw new TypeError(`money: field "${field}" value ${JSON.stringify(input)} is not a finite decimal`);
384
+ }
385
+ return r.value.toString();
386
+ }
387
+ function canonicalizeStoredMoney(record, moneyFields) {
388
+ if (record === null || record === void 0) return record;
389
+ if (!moneyFields || Object.keys(moneyFields).length === 0) return record;
390
+ return decodeMoneyFields(record, moneyFields, "raw");
391
+ }
392
+ function canonicalizeIncomingMoney(record, moneyFields) {
393
+ if (!moneyFields || Object.keys(moneyFields).length === 0) return record;
394
+ try {
395
+ return decodeMoneyFields(
396
+ quantizeMoneyFields(record, moneyFields),
397
+ moneyFields,
398
+ "raw"
399
+ );
400
+ } catch {
401
+ return record;
402
+ }
403
+ }
404
+ function quantizeValue(field, raw, desc) {
405
+ if (desc.mode === "fixed") {
406
+ const currency2 = desc.fixedCurrency;
407
+ return quantizeAmount(field, raw, desc.scaleFor(currency2), desc.rounding);
408
+ }
409
+ let amount;
410
+ let currency;
411
+ if (isMoneyValueObject(raw)) {
412
+ currency = String(raw.currency);
413
+ amount = raw.amount;
414
+ } else {
415
+ const sole = desc.soleCurrency();
416
+ if (sole === void 0) {
417
+ throw new TypeError(
418
+ `money: field "${field}" is multi-currency \u2014 write { amount, currency }, not a bare amount`
419
+ );
420
+ }
421
+ currency = sole;
422
+ amount = raw;
423
+ }
424
+ const scale = desc.scaleFor(currency);
425
+ return { amount: quantizeAmount(field, amount, scale, desc.rounding), currency };
426
+ }
427
+ function quantizeMoneyFields(record, moneyFields) {
428
+ let out = { ...record };
429
+ for (const [path, desc] of Object.entries(moneyFields)) {
430
+ if (isSimpleMoneyPath(path)) {
431
+ const raw = out[path];
432
+ if (raw === null || raw === void 0) continue;
433
+ out[path] = quantizeValue(path, raw, desc);
434
+ continue;
435
+ }
436
+ out = transformAtMoneyPath(
437
+ out,
438
+ path,
439
+ parseMoneyPath(path),
440
+ 0,
441
+ (container, key) => {
442
+ const raw = container[key];
443
+ if (raw === null || raw === void 0) return;
444
+ container[key] = quantizeValue(path, raw, desc);
445
+ },
446
+ /* lenient */
447
+ false
448
+ );
449
+ }
450
+ return out;
451
+ }
452
+ function formatCurrency(decimal, currency, scale, locale) {
453
+ const fmt = new Intl.NumberFormat(locale, {
454
+ style: "currency",
455
+ currency,
456
+ minimumFractionDigits: scale,
457
+ maximumFractionDigits: scale
458
+ });
459
+ return fmt.format(decimal);
460
+ }
461
+ function moneyScaledValue(stored, desc) {
462
+ let raw;
463
+ if (desc.mode === "fixed") {
464
+ raw = stored;
465
+ } else {
466
+ if (!isMoneyValueObject(stored)) return null;
467
+ raw = stored.amount;
468
+ }
469
+ if (typeof raw !== "string" && typeof raw !== "number") return null;
470
+ try {
471
+ return BigInt(String(raw));
472
+ } catch {
473
+ return null;
474
+ }
475
+ }
476
+ function decodeValue(stored, desc) {
477
+ let currency;
478
+ let scaledIntString;
479
+ if (desc.mode === "fixed") {
480
+ if (typeof stored !== "string" && typeof stored !== "number") return null;
481
+ currency = desc.fixedCurrency;
482
+ scaledIntString = String(stored);
483
+ } else {
484
+ if (!isMoneyValueObject(stored)) return null;
485
+ const amount = stored.amount;
486
+ if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") return null;
487
+ currency = stored.currency;
488
+ scaledIntString = String(amount);
489
+ }
490
+ const scale = desc.scaleFor(currency);
491
+ let decimal;
492
+ try {
493
+ decimal = formatScaledInt(BigInt(scaledIntString), scale);
494
+ } catch {
495
+ return null;
496
+ }
497
+ return {
498
+ decoded: desc.mode === "fixed" ? decimal : { amount: decimal, currency },
499
+ decimal,
500
+ currency,
501
+ scale
502
+ };
503
+ }
504
+ function decodeMoneyFields(record, moneyFields, locale) {
505
+ let out = { ...record };
506
+ const format = locale !== "raw";
507
+ const fmtLocale = typeof locale === "string" && locale !== "raw" ? locale : "en-US";
508
+ for (const [path, desc] of Object.entries(moneyFields)) {
509
+ if (isSimpleMoneyPath(path)) {
510
+ const stored = out[path];
511
+ if (stored === null || stored === void 0) continue;
512
+ const r = decodeValue(stored, desc);
513
+ if (r === null) continue;
514
+ out[path] = r.decoded;
515
+ if (format) {
516
+ out[`${path}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
517
+ out[`${path}Number`] = Number(r.decimal);
518
+ }
519
+ continue;
520
+ }
521
+ out = transformAtMoneyPath(
522
+ out,
523
+ path,
524
+ parseMoneyPath(path),
525
+ 0,
526
+ (container, key) => {
527
+ const stored = container[key];
528
+ if (stored === null || stored === void 0) return;
529
+ const r = decodeValue(stored, desc);
530
+ if (r === null) return;
531
+ container[key] = r.decoded;
532
+ if (format && typeof key === "string" && !Array.isArray(container)) {
533
+ container[`${key}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
534
+ container[`${key}Number`] = Number(r.decimal);
535
+ }
536
+ },
537
+ /* lenient */
538
+ true
539
+ );
540
+ }
541
+ return out;
542
+ }
543
+
469
544
  // src/query/builder.ts
470
545
  var EMPTY_PLAN = {
471
546
  clauses: [],
@@ -474,6 +549,7 @@ var EMPTY_PLAN = {
474
549
  offset: 0,
475
550
  joins: []
476
551
  };
552
+ var DEFAULT_CROSS_JOIN_MAX_ROWS = 5e4;
477
553
  var Query = class _Query {
478
554
  source;
479
555
  plan;
@@ -506,7 +582,7 @@ var Query = class _Query {
506
582
  /**
507
583
  * @internal — clone this Query with a declared-predicate map
508
584
  * attached. Used by the materialized-view registry to enable
509
- * `.wherePredicate(name, ctx?)` for the MV's query callback (#153).
585
+ * `.wherePredicate(name, ctx?)` for the MV's query callback.
510
586
  * Consumers don't call this directly.
511
587
  */
512
588
  _withPredicates(predicates) {
@@ -519,7 +595,37 @@ var Query = class _Query {
519
595
  );
520
596
  }
521
597
  /**
522
- * Filter by a registered deterministic predicate (#153). Requires
598
+ * @internal #308 L3. The ids of records matching this query's plan,
599
+ * recovered by reference identity: `executePlanWithSource` returns the
600
+ * ORIGINAL snapshot record references (money-decode and joins are applied
601
+ * later, in `toArray`), so each matched record is found in the id-paired
602
+ * `snapshotEntries()` map. Used by `collection.retrieve({ within })`.
603
+ * Throws if the source is not collection-backed (no `snapshotEntries`).
604
+ */
605
+ _idArray() {
606
+ const entries = this.source.snapshotEntries?.();
607
+ if (entries === void 0) {
608
+ throw new Error(
609
+ "Query._idArray(): the query source has no snapshotEntries(); retrieve({ within }) requires a collection-backed query (collection.query())."
610
+ );
611
+ }
612
+ if (this.plan.clauses.some((c) => c.type === "crossJoin")) {
613
+ throw new Error(
614
+ "Query._idArray(): retrieve({ within }) does not support crossJoin queries (cross-join produces new row objects, breaking id recovery). Use where/filter/and/or, or a projection .join()."
615
+ );
616
+ }
617
+ const refToId = /* @__PURE__ */ new Map();
618
+ for (const { id, record } of entries) refToId.set(record, id);
619
+ const matched = executePlanWithSource(this.source, this.plan, this.joinContext);
620
+ const ids = [];
621
+ for (const r of matched) {
622
+ const id = refToId.get(r);
623
+ if (id !== void 0) ids.push(id);
624
+ }
625
+ return ids;
626
+ }
627
+ /**
628
+ * Filter by a registered deterministic predicate. Requires
523
629
  * the Query to have been augmented with a predicates map (typically
524
630
  * via the materialized-view registry — bare Queries constructed
525
631
  * outside an MV throw on `.wherePredicate()`).
@@ -557,9 +663,18 @@ var Query = class _Query {
557
663
  this.predicates
558
664
  );
559
665
  }
560
- /** Add a field comparison. Multiple where() calls are AND-combined. */
666
+ /**
667
+ * Add a field comparison. Multiple where() calls are AND-combined.
668
+ *
669
+ * A declared money field compares in MAJOR units (#336): the operand
670
+ * (`10000`, `'10000.00'`, or `{ amount, currency }` in multi mode) is
671
+ * quantized into stored scaled-int space at build time and evaluated
672
+ * BigInt-exact per record. A malformed operand or a string operator
673
+ * (`contains`/`startsWith`) throws here, at the call site.
674
+ */
561
675
  where(field, op, value) {
562
- const clause = { type: "field", field, op, value };
676
+ const desc = this.source.moneyFields?.[field];
677
+ const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
563
678
  return new _Query(
564
679
  this.source,
565
680
  { ...this.plan, clauses: [...this.plan.clauses, clause] },
@@ -625,11 +740,16 @@ var Query = class _Query {
625
740
  this.predicates
626
741
  );
627
742
  }
628
- /** Sort by a field. Subsequent calls are tie-breakers. */
629
- orderBy(field, direction = "asc") {
743
+ /**
744
+ * Sort by a field. Subsequent calls are tie-breakers. Pass
745
+ * `{ by: 'label' }` to sort a `dictKey`/`staticDict` field by its resolved
746
+ * label at the query locale instead of the stored code (#285).
747
+ */
748
+ orderBy(field, direction = "asc", opts) {
749
+ const entry = opts?.by === "label" ? { field, direction, by: "label" } : { field, direction };
630
750
  return new _Query(
631
751
  this.source,
632
- { ...this.plan, orderBy: [...this.plan.orderBy, { field, direction }] },
752
+ { ...this.plan, orderBy: [...this.plan.orderBy, entry] },
633
753
  this.joinContext,
634
754
  this.aggregateStrategy,
635
755
  this.predicates
@@ -755,25 +875,119 @@ var Query = class _Query {
755
875
  this.predicates
756
876
  );
757
877
  }
878
+ /**
879
+ * Cartesian-product cross-join against `target` collection. Each result row
880
+ * carries the original `T` fields plus `result[as]` populated from every
881
+ * right-side row (or the filtered subset when `on:` is supplied).
882
+ *
883
+ * **Order matters:** `.where().crossJoin()` filters BEFORE expanding (cheaper);
884
+ * `.crossJoin().where('alias.field', ...)` filters AFTER (required when the
885
+ * where clause references the aliased fields).
886
+ *
887
+ * **Cost ceiling:** `CrossJoinTooLargeError` fires before allocation when
888
+ * `leftRows × rightRows` (or the cumulative lateral count) exceeds the limit.
889
+ * Default: 50,000 rows. Override per-clause with `{ maxRows: N }`.
890
+ *
891
+ * **`on:` shapes:**
892
+ * - `on: (left) => TTarget[]` — subset form (most efficient)
893
+ * - `on: (left) => (right) => boolean` — predicate form
894
+ * - `on: { predicate: 'name' }` — MV-safe, hash-tracked form
895
+ * (requires the Query to have been augmented via `_withPredicates`)
896
+ *
897
+ * Requires a JoinContext (constructed via `collection.query()`).
898
+ */
899
+ crossJoin(target, opts) {
900
+ if (!this.joinContext) {
901
+ throw new Error(
902
+ `Query.crossJoin("${target}"): requires a join context. Use collection.query() to construct a cross-join-capable Query instead of the Query constructor directly.`
903
+ );
904
+ }
905
+ let onFn;
906
+ let onPredicateName;
907
+ if (opts.on !== void 0) {
908
+ if (typeof opts.on === "function") {
909
+ onFn = opts.on;
910
+ if (this.predicates) {
911
+ console.warn(
912
+ `Query.crossJoin("${target}", { on: callback }): inline on: callback inside a withMaterializedView query() disables queryHash drift detection for this cross-join. Use on: { predicate: '<name>' } to enable it.`
913
+ );
914
+ }
915
+ } else {
916
+ const predName = opts.on.predicate;
917
+ if (!this.predicates) {
918
+ throw new Error(
919
+ `Query.crossJoin("${target}", { on: { predicate: "${predName}" } }): the { predicate } form requires a predicates map. Use this form inside a withMaterializedView query() callback that declares predicates: { ${predName}: { hash, fn } }.`
920
+ );
921
+ }
922
+ const decl = this.predicates.get(predName);
923
+ if (!decl) {
924
+ throw new Error(
925
+ `Query.crossJoin("${target}"): predicate "${predName}" not registered. Available: ${[...this.predicates.keys()].join(", ") || "(none)"}.`
926
+ );
927
+ }
928
+ const as = opts.as;
929
+ const predicateFn = decl.fn;
930
+ onFn = (_left) => (right) => predicateFn({ ..._left, [as]: right });
931
+ onPredicateName = predName;
932
+ }
933
+ }
934
+ const clause = {
935
+ type: "crossJoin",
936
+ target,
937
+ as: opts.as,
938
+ ...onFn !== void 0 && { on: onFn },
939
+ ...onPredicateName !== void 0 && { onPredicateName },
940
+ ...opts.maxRows !== void 0 && { maxRows: opts.maxRows }
941
+ };
942
+ return new _Query(
943
+ this.source,
944
+ { ...this.plan, clauses: [...this.plan.clauses, clause] },
945
+ this.joinContext,
946
+ this.aggregateStrategy,
947
+ this.predicates
948
+ );
949
+ }
758
950
  /**
759
951
  * Execute the plan and return the matching records. When the plan
760
952
  * carries any join legs, they are applied after `where` / `orderBy`
761
953
  * / `limit` / `offset` narrow the left set. See the `.join()` doc
762
954
  * for the ordering rationale.
955
+ *
956
+ * `opts.locale` (#285 §3) resolves JOINED right-side i18n fields at the
957
+ * `join` layer to that locale; without it, the owning collection's default
958
+ * locale applies, and a locale-less query leaves joined i18n fields raw.
959
+ * (Left/base i18n fields are resolved by `get`/`list`, not here.)
763
960
  */
764
- toArray() {
765
- const base = executePlanWithSource(this.source, this.plan);
961
+ toArray(opts) {
962
+ const base = this.decodeMoney(executePlanWithSource(this.source, this.plan, this.joinContext, opts?.locale));
766
963
  if (this.plan.joins.length === 0) return base;
767
964
  if (!this.joinContext) {
768
965
  throw new Error(
769
966
  `Query.toArray(): plan carries ${this.plan.joins.length} join leg(s) but no JoinContext is attached. This usually means the Query was constructed via the raw Query constructor with a plan that had joins pre-populated. Use collection.query().join(...) instead.`
770
967
  );
771
968
  }
772
- return applyJoins(base, this.plan.joins, this.joinContext);
969
+ return applyJoins(base, this.plan.joins, this.joinContext, opts?.locale);
773
970
  }
774
- /** Return the first matching record, or null. Joins are applied. */
775
- first() {
776
- const arr = this.limit(1).toArray();
971
+ /**
972
+ * Decode this source's money fields on read (stored scaled-int → canonical
973
+ * decimal), so `query().toArray()` agrees with `get()`/`sum()` on the value.
974
+ * No-op when the source declares no money fields.
975
+ *
976
+ * The query layer carries no locale context, so we decode with `'raw'` —
977
+ * canonical decimal, WITHOUT fabricating locale-formatted `<field>Formatted`
978
+ * / `<field>Number` virtuals. Producing a guessed-locale string here would
979
+ * just reintroduce #322's "two read paths disagree" failure on the virtual
980
+ * field (e.g. it-IT via `get()` vs en-US here). Consumers who need formatted
981
+ * money read through `get()`/`list()` with a locale.
982
+ */
983
+ decodeMoney(records) {
984
+ const moneyFields = this.source.moneyFields;
985
+ if (!moneyFields || Object.keys(moneyFields).length === 0) return records;
986
+ return records.map((r) => decodeMoneyFields(r, moneyFields, "raw"));
987
+ }
988
+ /** Return the first matching record, or null. Joins are applied. `opts.locale` resolves joined i18n fields (#285 §3). */
989
+ first(opts) {
990
+ const arr = this.limit(1).toArray(opts);
777
991
  return arr[0] ?? null;
778
992
  }
779
993
  /**
@@ -786,9 +1000,17 @@ var Query = class _Query {
786
1000
  * intent is purely to count.
787
1001
  */
788
1002
  count() {
1003
+ if (this.plan.clauses.some((c) => c.type === "crossJoin")) {
1004
+ if (!this.joinContext) {
1005
+ throw new Error(
1006
+ `Query.count(): plan contains crossJoin clauses but no JoinContext is attached.`
1007
+ );
1008
+ }
1009
+ return executeClausePipeline(this.source, this.plan.clauses, this.joinContext).length;
1010
+ }
789
1011
  const { candidates, remainingClauses } = candidateRecords(this.source, this.plan.clauses);
790
1012
  if (remainingClauses.length === 0) return candidates.length;
791
- return filterRecords(candidates, remainingClauses).length;
1013
+ return filterRecords(candidates, remainingClauses, fnViewDecoder(this.source)).length;
792
1014
  }
793
1015
  /**
794
1016
  * Reduce the matching records through a named set of reducers.
@@ -831,11 +1053,21 @@ var Query = class _Query {
831
1053
  * partition boundaries without an API break.
832
1054
  */
833
1055
  aggregate(spec) {
1056
+ const moneyFields = this.source.moneyFields;
1057
+ if (moneyFields) {
1058
+ spec = wrapMoneyReducers(spec, moneyFields);
1059
+ }
834
1060
  const source = this.source;
835
1061
  const clauses = this.plan.clauses;
1062
+ const joinCtx = this.joinContext;
1063
+ const hasCrossJoins = clauses.some((c) => c.type === "crossJoin");
836
1064
  const executeRecords = () => {
1065
+ if (hasCrossJoins) {
1066
+ if (!joinCtx) throw new Error("Query.aggregate(): crossJoin requires a join context");
1067
+ return executeClausePipeline(source, clauses, joinCtx);
1068
+ }
837
1069
  const { candidates, remainingClauses } = candidateRecords(source, clauses);
838
- return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
1070
+ return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
839
1071
  };
840
1072
  const upstreams = [];
841
1073
  if (source.subscribe) {
@@ -850,9 +1082,15 @@ var Query = class _Query {
850
1082
  }
851
1083
  const source = this.source;
852
1084
  const clauses = this.plan.clauses;
1085
+ const joinCtx = this.joinContext;
1086
+ const hasCrossJoins = clauses.some((c) => c.type === "crossJoin");
853
1087
  const executeRecords = () => {
1088
+ if (hasCrossJoins) {
1089
+ if (!joinCtx) throw new Error("Query.groupBy(): crossJoin requires a join context");
1090
+ return executeClausePipeline(source, clauses, joinCtx);
1091
+ }
854
1092
  const { candidates, remainingClauses } = candidateRecords(source, clauses);
855
- return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
1093
+ return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
856
1094
  };
857
1095
  const upstreams = [];
858
1096
  if (source.subscribe) {
@@ -866,13 +1104,15 @@ var Query = class _Query {
866
1104
  executeRecords,
867
1105
  field,
868
1106
  upstreams,
869
- dictLabelResolver
1107
+ dictLabelResolver,
1108
+ this.source.moneyFields
870
1109
  );
871
1110
  }
872
1111
  return this.aggregateStrategy.groupByN(
873
1112
  executeRecords,
874
1113
  fields,
875
- upstreams
1114
+ upstreams,
1115
+ this.source.moneyFields
876
1116
  );
877
1117
  }
878
1118
  /**
@@ -964,6 +1204,21 @@ var Query = class _Query {
964
1204
  }
965
1205
  }
966
1206
  }
1207
+ if (this.joinContext) {
1208
+ const subscribedCross = /* @__PURE__ */ new Set();
1209
+ for (const clause of this.plan.clauses) {
1210
+ if (clause.type !== "crossJoin") continue;
1211
+ if (subscribedCross.has(clause.target)) continue;
1212
+ subscribedCross.add(clause.target);
1213
+ const rightSource = this.joinContext.resolveSource(clause.target);
1214
+ if (rightSource?.subscribe) {
1215
+ const rightSubscribe = rightSource.subscribe.bind(rightSource);
1216
+ upstreams.push({
1217
+ subscribe: (cb) => rightSubscribe(cb)
1218
+ });
1219
+ }
1220
+ }
1221
+ }
967
1222
  return buildLiveQuery(() => this.toArray(), upstreams);
968
1223
  }
969
1224
  /**
@@ -975,11 +1230,23 @@ var Query = class _Query {
975
1230
  return serializePlan(this.plan);
976
1231
  }
977
1232
  };
978
- function executePlanWithSource(source, plan) {
979
- const { candidates, remainingClauses } = candidateRecords(source, plan.clauses);
980
- let result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses);
1233
+ function executePlanWithSource(source, plan, joinContext, locale) {
1234
+ const hasCrossJoins = plan.clauses.some((c) => c.type === "crossJoin");
1235
+ let result;
1236
+ if (hasCrossJoins) {
1237
+ if (!joinContext) {
1238
+ throw new Error(
1239
+ `Query.toArray(): plan contains crossJoin clauses but no JoinContext is attached. Use collection.query() instead of new Query() for cross-join support.`
1240
+ );
1241
+ }
1242
+ result = executeClausePipeline(source, plan.clauses, joinContext);
1243
+ } else {
1244
+ const { candidates, remainingClauses } = candidateRecords(source, plan.clauses);
1245
+ result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
1246
+ }
981
1247
  if (plan.orderBy.length > 0) {
982
- result = sortRecords(result, plan.orderBy);
1248
+ const labelMaps = buildOrderLabelMaps(plan.orderBy, joinContext, locale);
1249
+ result = sortRecords(result, plan.orderBy, source.moneyFields, labelMaps);
983
1250
  }
984
1251
  if (plan.offset > 0) {
985
1252
  result = result.slice(plan.offset);
@@ -999,6 +1266,7 @@ function candidateRecords(source, clauses) {
999
1266
  const clause = clauses[i];
1000
1267
  if (clause.type !== "field") continue;
1001
1268
  if (!indexes.has(clause.field)) continue;
1269
+ if (clause.money?.mode === "multi") continue;
1002
1270
  let ids = null;
1003
1271
  if (clause.op === "==") {
1004
1272
  ids = indexes.lookupEqual(clause.field, clause.value);
@@ -1027,6 +1295,11 @@ function materializeIds(ids, lookupById) {
1027
1295
  return out;
1028
1296
  }
1029
1297
  function executePlan(records, plan) {
1298
+ if (plan.clauses.some((c) => c.type === "crossJoin")) {
1299
+ throw new Error(
1300
+ `executePlan(): does not support crossJoin clauses. executePlan is a stateless pure function \u2014 it cannot resolve cross-join right-side collections. Use Query.toArray() (via collection.query()) instead.`
1301
+ );
1302
+ }
1030
1303
  let result = filterRecords(records, plan.clauses);
1031
1304
  if (plan.orderBy.length > 0) {
1032
1305
  result = sortRecords(result, plan.orderBy);
@@ -1039,13 +1312,20 @@ function executePlan(records, plan) {
1039
1312
  }
1040
1313
  return result;
1041
1314
  }
1042
- function filterRecords(records, clauses) {
1315
+ function fnViewDecoder(source) {
1316
+ const mf = source.moneyFields;
1317
+ if (!mf || Object.keys(mf).length === 0) return void 0;
1318
+ return (r) => decodeMoneyFields(r, mf, "raw");
1319
+ }
1320
+ function filterRecords(records, clauses, decodeForFns) {
1043
1321
  if (clauses.length === 0) return [...records];
1322
+ const needsFnView = decodeForFns !== void 0 && hasFnClause(clauses);
1044
1323
  const out = [];
1045
1324
  for (const r of records) {
1325
+ const fnView = needsFnView ? decodeForFns(r) : void 0;
1046
1326
  let matches = true;
1047
1327
  for (const clause of clauses) {
1048
- if (!evaluateClause(r, clause)) {
1328
+ if (!evaluateClause(r, clause, fnView)) {
1049
1329
  matches = false;
1050
1330
  break;
1051
1331
  }
@@ -1054,17 +1334,124 @@ function filterRecords(records, clauses) {
1054
1334
  }
1055
1335
  return out;
1056
1336
  }
1057
- function sortRecords(records, orderBy) {
1337
+ function executeClausePipeline(source, clauses, joinContext) {
1338
+ let rel = [...source.snapshot()];
1339
+ let filterBatch = [];
1340
+ const decodeForFns = fnViewDecoder(source);
1341
+ for (const clause of clauses) {
1342
+ if (clause.type === "crossJoin") {
1343
+ if (filterBatch.length > 0) {
1344
+ rel = filterRecords(rel, filterBatch, decodeForFns);
1345
+ filterBatch = [];
1346
+ }
1347
+ const rightSource = joinContext.resolveSource(clause.target);
1348
+ if (!rightSource) {
1349
+ throw new CrossJoinSourceUnknownError(clause.target, joinContext.leftCollection);
1350
+ }
1351
+ rel = applyCrossJoin(rel, clause, rightSource);
1352
+ } else {
1353
+ filterBatch.push(clause);
1354
+ }
1355
+ }
1356
+ if (filterBatch.length > 0) {
1357
+ rel = filterRecords(rel, filterBatch, decodeForFns);
1358
+ }
1359
+ return rel;
1360
+ }
1361
+ function applyCrossJoin(leftRel, clause, rightSource) {
1362
+ const rightRows = rightSource.snapshot();
1363
+ const maxRows = clause.maxRows ?? DEFAULT_CROSS_JOIN_MAX_ROWS;
1364
+ const { as } = clause;
1365
+ if (!clause.on) {
1366
+ const product = leftRel.length * rightRows.length;
1367
+ if (product > maxRows) {
1368
+ throw new CrossJoinTooLargeError({ target: clause.target, expected: product, limit: maxRows });
1369
+ }
1370
+ const expanded2 = [];
1371
+ for (const left of leftRel) {
1372
+ const leftObj = left;
1373
+ for (const right of rightRows) {
1374
+ expanded2.push({ ...leftObj, [as]: right });
1375
+ }
1376
+ }
1377
+ return expanded2;
1378
+ }
1379
+ const expanded = [];
1380
+ let cumulative = 0;
1381
+ for (const left of leftRel) {
1382
+ const callbackResult = clause.on(left);
1383
+ let filteredRight;
1384
+ if (Array.isArray(callbackResult)) {
1385
+ filteredRight = callbackResult;
1386
+ } else {
1387
+ filteredRight = rightRows.filter(
1388
+ callbackResult
1389
+ );
1390
+ }
1391
+ cumulative += filteredRight.length;
1392
+ if (cumulative > maxRows) {
1393
+ throw new CrossJoinTooLargeError({
1394
+ target: clause.target,
1395
+ expected: cumulative,
1396
+ limit: maxRows
1397
+ });
1398
+ }
1399
+ const leftObj = left;
1400
+ for (const right of filteredRight) {
1401
+ expanded.push({ ...leftObj, [as]: right });
1402
+ }
1403
+ }
1404
+ return expanded;
1405
+ }
1406
+ function sortRecords(records, orderBy, moneyFields, labelMaps) {
1058
1407
  return [...records].sort((a, b) => {
1059
- for (const { field, direction } of orderBy) {
1060
- const av = readField(a, field);
1061
- const bv = readField(b, field);
1062
- const cmp = compareValues(av, bv);
1408
+ for (const { field, direction, by } of orderBy) {
1409
+ let av = readField(a, field);
1410
+ let bv = readField(b, field);
1411
+ const labelMap = by === "label" ? labelMaps?.get(field) : void 0;
1412
+ if (labelMap) {
1413
+ av = (typeof av === "string" ? labelMap.get(av) : void 0) ?? av;
1414
+ bv = (typeof bv === "string" ? labelMap.get(bv) : void 0) ?? bv;
1415
+ const cmp2 = compareValues(av, bv);
1416
+ if (cmp2 !== 0) return direction === "asc" ? cmp2 : -cmp2;
1417
+ continue;
1418
+ }
1419
+ const desc = moneyFields?.[field];
1420
+ const cmp = desc ? compareMoney(av, bv, desc) : compareValues(av, bv);
1063
1421
  if (cmp !== 0) return direction === "asc" ? cmp : -cmp;
1064
1422
  }
1065
1423
  return 0;
1066
1424
  });
1067
1425
  }
1426
+ function buildOrderLabelMaps(orderBy, joinContext, locale) {
1427
+ if (!joinContext?.resolveDictSource) return void 0;
1428
+ const resolveDict = joinContext.resolveDictSource.bind(joinContext);
1429
+ let maps;
1430
+ for (const { field, by } of orderBy) {
1431
+ if (by !== "label") continue;
1432
+ const dictSource = resolveDict(field);
1433
+ if (!dictSource) continue;
1434
+ const loc = locale ?? dictSource.displayLocale;
1435
+ if (loc === void 0) continue;
1436
+ const codeToLabel = /* @__PURE__ */ new Map();
1437
+ for (const entry of dictSource.snapshot()) {
1438
+ const k = entry["key"];
1439
+ const labels = entry["labels"];
1440
+ const label = labels?.[loc];
1441
+ if (typeof k === "string" && typeof label === "string") codeToLabel.set(k, label);
1442
+ }
1443
+ ;
1444
+ (maps ??= /* @__PURE__ */ new Map()).set(field, codeToLabel);
1445
+ }
1446
+ return maps;
1447
+ }
1448
+ function compareMoney(a, b, desc) {
1449
+ const av = moneyScaledValue(a, desc);
1450
+ const bv = moneyScaledValue(b, desc);
1451
+ if (av === null) return bv === null ? 0 : 1;
1452
+ if (bv === null) return -1;
1453
+ return av < bv ? -1 : av > bv ? 1 : 0;
1454
+ }
1068
1455
  function readField(record, field) {
1069
1456
  if (record === null || record === void 0) return void 0;
1070
1457
  if (!field.includes(".")) {
@@ -1116,6 +1503,16 @@ function serializeClause(clause) {
1116
1503
  clauses: clause.clauses.map(serializeClause)
1117
1504
  };
1118
1505
  }
1506
+ if (clause.type === "crossJoin") {
1507
+ return {
1508
+ type: "crossJoin",
1509
+ target: clause.target,
1510
+ as: clause.as,
1511
+ on: clause.on ? "[function]" : void 0,
1512
+ onPredicateName: clause.onPredicateName,
1513
+ maxRows: clause.maxRows
1514
+ };
1515
+ }
1119
1516
  return clause;
1120
1517
  }
1121
1518
  function canonicalCtxHash(ctx) {
@@ -1141,6 +1538,7 @@ function buildDictLabelResolver(joinCtx, field) {
1141
1538
  const dictSource = joinCtx.resolveDictSource(field);
1142
1539
  if (!dictSource) return void 0;
1143
1540
  const snapshot = dictSource.snapshot();
1541
+ const displayLocale = dictSource.displayLocale;
1144
1542
  const dictMap = /* @__PURE__ */ new Map();
1145
1543
  for (const entry of snapshot) {
1146
1544
  const k = entry["key"];
@@ -1150,9 +1548,11 @@ function buildDictLabelResolver(joinCtx, field) {
1150
1548
  }
1151
1549
  }
1152
1550
  return async (key, locale, fallback) => {
1551
+ const effLocale = locale || displayLocale;
1552
+ if (!effLocale) return void 0;
1153
1553
  const labels = dictMap.get(key);
1154
1554
  if (!labels) return void 0;
1155
- if (labels[locale] !== void 0) return labels[locale];
1555
+ if (labels[effLocale] !== void 0) return labels[effLocale];
1156
1556
  const chain = Array.isArray(fallback) ? fallback : fallback ? [fallback] : [];
1157
1557
  for (const fb of chain) {
1158
1558
  if (fb === "any") {
@@ -1166,537 +1566,6 @@ function buildDictLabelResolver(joinCtx, field) {
1166
1566
  };
1167
1567
  }
1168
1568
 
1169
- // src/indexing/eager-indexes.ts
1170
- var CollectionIndexes = class {
1171
- indexes = /* @__PURE__ */ new Map();
1172
- /**
1173
- * Declare an index. Subsequent record additions are tracked under it.
1174
- * Calling this twice for the same field is a no-op (idempotent).
1175
- */
1176
- declare(field) {
1177
- if (this.indexes.has(field)) return;
1178
- this.indexes.set(field, { field, buckets: /* @__PURE__ */ new Map() });
1179
- }
1180
- /** True if the given field has a declared index. */
1181
- has(field) {
1182
- return this.indexes.has(field);
1183
- }
1184
- /** All declared field names, in declaration order. */
1185
- fields() {
1186
- return [...this.indexes.keys()];
1187
- }
1188
- /**
1189
- * Build all declared indexes from a snapshot of records.
1190
- * Called once per hydration. O(N × indexes.size).
1191
- */
1192
- build(records) {
1193
- for (const idx of this.indexes.values()) {
1194
- idx.buckets.clear();
1195
- for (const { id, record } of records) {
1196
- addToIndex(idx, id, record);
1197
- }
1198
- }
1199
- }
1200
- /**
1201
- * Insert or update a single record across all indexes.
1202
- * Called by `Collection.put()` after the encrypted write succeeds.
1203
- *
1204
- * If `previousRecord` is provided, the record is removed from any old
1205
- * buckets first — this is the update path. Pass `null` for fresh adds.
1206
- */
1207
- upsert(id, newRecord, previousRecord) {
1208
- if (this.indexes.size === 0) return;
1209
- if (previousRecord !== null) {
1210
- this.remove(id, previousRecord);
1211
- }
1212
- for (const idx of this.indexes.values()) {
1213
- addToIndex(idx, id, newRecord);
1214
- }
1215
- }
1216
- /**
1217
- * Remove a record from all indexes. Called by `Collection.delete()`
1218
- * (and as the first half of `upsert` for the update path).
1219
- */
1220
- remove(id, record) {
1221
- if (this.indexes.size === 0) return;
1222
- for (const idx of this.indexes.values()) {
1223
- removeFromIndex(idx, id, record);
1224
- }
1225
- }
1226
- /** Drop all index data. Called when the collection is invalidated. */
1227
- clear() {
1228
- for (const idx of this.indexes.values()) {
1229
- idx.buckets.clear();
1230
- }
1231
- }
1232
- /**
1233
- * Equality lookup: return the set of record ids whose `field` matches
1234
- * the given value. Returns `null` if no index covers the field — the
1235
- * caller should fall back to a linear scan.
1236
- *
1237
- * The returned Set is a reference to the index's internal storage —
1238
- * callers must NOT mutate it.
1239
- */
1240
- lookupEqual(field, value) {
1241
- const idx = this.indexes.get(field);
1242
- if (!idx) return null;
1243
- const key = stringifyKey(value);
1244
- return idx.buckets.get(key) ?? EMPTY_SET;
1245
- }
1246
- /**
1247
- * Set lookup: return the union of record ids whose `field` matches any
1248
- * of the given values. Returns `null` if no index covers the field.
1249
- */
1250
- lookupIn(field, values) {
1251
- const idx = this.indexes.get(field);
1252
- if (!idx) return null;
1253
- const out = /* @__PURE__ */ new Set();
1254
- for (const value of values) {
1255
- const key = stringifyKey(value);
1256
- const bucket = idx.buckets.get(key);
1257
- if (bucket) {
1258
- for (const id of bucket) out.add(id);
1259
- }
1260
- }
1261
- return out;
1262
- }
1263
- };
1264
- var EMPTY_SET = /* @__PURE__ */ new Set();
1265
- function stringifyKey(value) {
1266
- if (value === null || value === void 0) return "\0NULL\0";
1267
- if (typeof value === "string") return value;
1268
- if (typeof value === "number" || typeof value === "boolean") return String(value);
1269
- if (value instanceof Date) return value.toISOString();
1270
- return "\0OBJECT\0";
1271
- }
1272
- function addToIndex(idx, id, record) {
1273
- const value = readPath(record, idx.field);
1274
- if (value === null || value === void 0) return;
1275
- const key = stringifyKey(value);
1276
- let bucket = idx.buckets.get(key);
1277
- if (!bucket) {
1278
- bucket = /* @__PURE__ */ new Set();
1279
- idx.buckets.set(key, bucket);
1280
- }
1281
- bucket.add(id);
1282
- }
1283
- function removeFromIndex(idx, id, record) {
1284
- const value = readPath(record, idx.field);
1285
- if (value === null || value === void 0) return;
1286
- const key = stringifyKey(value);
1287
- const bucket = idx.buckets.get(key);
1288
- if (!bucket) return;
1289
- bucket.delete(id);
1290
- if (bucket.size === 0) idx.buckets.delete(key);
1291
- }
1292
-
1293
- // src/aggregate/reducers.ts
1294
- function count(opts) {
1295
- const _seed = opts?.seed;
1296
- void _seed;
1297
- return {
1298
- init: () => 0,
1299
- step: (state) => state + 1,
1300
- remove: (state) => state - 1,
1301
- finalize: (state) => state
1302
- };
1303
- }
1304
- function sum(field, opts) {
1305
- const _seed = opts?.seed;
1306
- void _seed;
1307
- return {
1308
- init: () => 0,
1309
- step: (state, record) => state + readNumber(record, field),
1310
- remove: (state, record) => state - readNumber(record, field),
1311
- finalize: (state) => state
1312
- };
1313
- }
1314
- function avg(field, opts) {
1315
- const _seed = opts?.seed;
1316
- void _seed;
1317
- return {
1318
- init: () => ({ sum: 0, count: 0 }),
1319
- step: (state, record) => ({
1320
- sum: state.sum + readNumber(record, field),
1321
- count: state.count + 1
1322
- }),
1323
- remove: (state, record) => ({
1324
- sum: state.sum - readNumber(record, field),
1325
- count: state.count - 1
1326
- }),
1327
- finalize: (state) => state.count === 0 ? null : state.sum / state.count
1328
- };
1329
- }
1330
- function pushValue(state, value) {
1331
- return { values: [...state.values, value] };
1332
- }
1333
- function removeValue(state, value) {
1334
- const idx = state.values.indexOf(value);
1335
- if (idx < 0) return state;
1336
- const next = state.values.slice();
1337
- next.splice(idx, 1);
1338
- return { values: next };
1339
- }
1340
- function min(field, opts) {
1341
- const _seed = opts?.seed;
1342
- void _seed;
1343
- return {
1344
- init: () => ({ values: [] }),
1345
- step: (state, record) => pushValue(state, readNumber(record, field)),
1346
- remove: (state, record) => removeValue(state, readNumber(record, field)),
1347
- finalize: (state) => {
1348
- if (state.values.length === 0) return null;
1349
- let out = state.values[0];
1350
- for (let i = 1; i < state.values.length; i++) {
1351
- const v = state.values[i];
1352
- if (v < out) out = v;
1353
- }
1354
- return out;
1355
- }
1356
- };
1357
- }
1358
- function max(field, opts) {
1359
- const _seed = opts?.seed;
1360
- void _seed;
1361
- return {
1362
- init: () => ({ values: [] }),
1363
- step: (state, record) => pushValue(state, readNumber(record, field)),
1364
- remove: (state, record) => removeValue(state, readNumber(record, field)),
1365
- finalize: (state) => {
1366
- if (state.values.length === 0) return null;
1367
- let out = state.values[0];
1368
- for (let i = 1; i < state.values.length; i++) {
1369
- const v = state.values[i];
1370
- if (v > out) out = v;
1371
- }
1372
- return out;
1373
- }
1374
- };
1375
- }
1376
- function readNumber(record, field) {
1377
- const value = readPath(record, field);
1378
- return typeof value === "number" && Number.isFinite(value) ? value : 0;
1379
- }
1380
-
1381
- // src/aggregate/aggregation.ts
1382
- function reduceRecords(records, spec) {
1383
- const state = {};
1384
- for (const key of Object.keys(spec)) {
1385
- state[key] = spec[key].init();
1386
- }
1387
- for (const record of records) {
1388
- for (const key of Object.keys(spec)) {
1389
- state[key] = spec[key].step(state[key], record);
1390
- }
1391
- }
1392
- const result = {};
1393
- for (const key of Object.keys(spec)) {
1394
- result[key] = spec[key].finalize(state[key]);
1395
- }
1396
- return result;
1397
- }
1398
- var LiveAggregationImpl = class {
1399
- constructor(recompute, upstreams) {
1400
- this.recompute = recompute;
1401
- try {
1402
- this.value = recompute();
1403
- this.error = void 0;
1404
- } catch (err) {
1405
- this.value = void 0;
1406
- this.error = err;
1407
- }
1408
- for (const upstream of upstreams) {
1409
- const unsub = upstream.subscribe(() => this.refresh());
1410
- this.unsubscribes.push(unsub);
1411
- }
1412
- }
1413
- recompute;
1414
- value;
1415
- error;
1416
- listeners = /* @__PURE__ */ new Set();
1417
- unsubscribes = [];
1418
- stopped = false;
1419
- refresh() {
1420
- if (this.stopped) return;
1421
- try {
1422
- this.value = this.recompute();
1423
- this.error = void 0;
1424
- } catch (err) {
1425
- this.error = err;
1426
- }
1427
- for (const listener of this.listeners) {
1428
- try {
1429
- listener();
1430
- } catch (err) {
1431
- console.warn("[noy-db] LiveAggregation listener threw:", err);
1432
- }
1433
- }
1434
- }
1435
- subscribe(cb) {
1436
- if (this.stopped) {
1437
- return () => {
1438
- };
1439
- }
1440
- this.listeners.add(cb);
1441
- return () => {
1442
- this.listeners.delete(cb);
1443
- };
1444
- }
1445
- stop() {
1446
- if (this.stopped) return;
1447
- this.stopped = true;
1448
- for (const unsub of this.unsubscribes) {
1449
- try {
1450
- unsub();
1451
- } catch (err) {
1452
- console.warn("[noy-db] LiveAggregation upstream unsubscribe threw:", err);
1453
- }
1454
- }
1455
- this.unsubscribes.length = 0;
1456
- this.listeners.clear();
1457
- }
1458
- };
1459
- var Aggregation = class {
1460
- constructor(executeRecords, spec, upstreams) {
1461
- this.executeRecords = executeRecords;
1462
- this.spec = spec;
1463
- this.upstreams = upstreams;
1464
- }
1465
- executeRecords;
1466
- spec;
1467
- upstreams;
1468
- /**
1469
- * Execute the query and reduce the results synchronously.
1470
- * Returns the reduced shape matching the spec — e.g. a spec of
1471
- * `{ total: sum('amount'), n: count() }` returns
1472
- * `{ total: number, n: number }`.
1473
- */
1474
- run() {
1475
- return reduceRecords(this.executeRecords(), this.spec);
1476
- }
1477
- /**
1478
- * Build a reactive `LiveAggregation<R>` that re-runs the reduction
1479
- * whenever any upstream source notifies of a change. The initial
1480
- * value is computed eagerly in the constructor, so consumers can
1481
- * read `live.value` immediately after calling `.live()`.
1482
- *
1483
- * Always call `live.stop()` when finished — it tears down the
1484
- * upstream subscriptions. Vue's `onUnmounted` is the canonical
1485
- * place.
1486
- *
1487
- * **Implementation note:** every upstream change triggers a full
1488
- * re-reduction. Incremental maintenance (O(1) per delta for
1489
- * sum/count/avg via the reducer protocol's `remove()` method) is a
1490
- * planned follow-up optimization — the protocol already supports
1491
- * it, but the executor doesn't drive it yet. Consumers get
1492
- * correct, reactive values today; future PRs can switch to
1493
- * delta-based maintenance without changing this API.
1494
- */
1495
- live() {
1496
- const recompute = () => reduceRecords(this.executeRecords(), this.spec);
1497
- return new LiveAggregationImpl(recompute, this.upstreams);
1498
- }
1499
- };
1500
- function buildLiveAggregation(recompute, upstreams) {
1501
- return new LiveAggregationImpl(recompute, upstreams);
1502
- }
1503
-
1504
- // src/aggregate/canonical-key.ts
1505
- function canonicalGroupKey(fields, row) {
1506
- const sorted = [...fields].sort();
1507
- const parts = [];
1508
- for (const name of sorted) {
1509
- const v = row[name];
1510
- const serialised = v === void 0 ? "undefined" : JSON.stringify(v);
1511
- parts.push(`${name}=${serialised}`);
1512
- }
1513
- return parts.join("|");
1514
- }
1515
-
1516
- // src/aggregate/groupby.ts
1517
- var GROUPBY_WARN_CARDINALITY = 1e4;
1518
- var GROUPBY_MAX_CARDINALITY = 1e5;
1519
- var warnedCardinalityFields = /* @__PURE__ */ new Set();
1520
- function warnCardinalityApproaching(fields, observed) {
1521
- const key = JSON.stringify([...fields].sort());
1522
- if (warnedCardinalityFields.has(key)) return;
1523
- warnedCardinalityFields.add(key);
1524
- const label = `[${fields.join(", ")}]`;
1525
- console.warn(
1526
- `[noy-db] .groupBy(${label}) produced ${observed} distinct groups, ${Math.round(observed / GROUPBY_MAX_CARDINALITY * 100)}% of the ${GROUPBY_MAX_CARDINALITY}-group ceiling. Narrow the query with .where() before grouping, or switch to a lower-cardinality field.`
1527
- );
1528
- }
1529
- function resetGroupByWarnings() {
1530
- warnedCardinalityFields.clear();
1531
- }
1532
- var GroupedQueryBase = class {
1533
- constructor(executeRecords, fieldOrFields, upstreams, dictLabelResolver) {
1534
- this.executeRecords = executeRecords;
1535
- this.upstreams = upstreams;
1536
- this.dictLabelResolver = dictLabelResolver;
1537
- this.fields = typeof fieldOrFields === "string" ? [fieldOrFields] : [...fieldOrFields];
1538
- }
1539
- executeRecords;
1540
- upstreams;
1541
- dictLabelResolver;
1542
- /**
1543
- * Field set this grouped query buckets on. Stored in declaration
1544
- * order — the same order is preserved on every result row by
1545
- * `groupAndReduce`. For the single-field constructor, this is
1546
- * `[field]`.
1547
- */
1548
- fields;
1549
- };
1550
- var GroupedQuery = class extends GroupedQueryBase {
1551
- /**
1552
- * Build a grouped aggregation. Returns a `GroupedAggregation`
1553
- * with `.run()`, `.runAsync()`, and `.live()` terminals — same shape
1554
- * as the non-grouped `.aggregate()` wrapper, just with an array
1555
- * result (one row per bucket) instead of a single reduced object.
1556
- */
1557
- aggregate(spec) {
1558
- return new GroupedAggregation(
1559
- this.executeRecords,
1560
- this.fields,
1561
- spec,
1562
- this.upstreams,
1563
- this.dictLabelResolver
1564
- );
1565
- }
1566
- };
1567
- var GroupedQueryN = class extends GroupedQueryBase {
1568
- aggregate(spec) {
1569
- return new GroupedAggregation(
1570
- this.executeRecords,
1571
- this.fields,
1572
- spec,
1573
- this.upstreams,
1574
- this.dictLabelResolver
1575
- );
1576
- }
1577
- };
1578
- function groupAndReduce(records, fieldOrFields, spec) {
1579
- const fields = typeof fieldOrFields === "string" ? [fieldOrFields] : fieldOrFields;
1580
- if (fields.length === 0) {
1581
- throw new Error(".groupBy() requires at least one field");
1582
- }
1583
- const buckets = /* @__PURE__ */ new Map();
1584
- const fieldLabel = fields.length === 1 ? fields[0] : `[${fields.join(", ")}]`;
1585
- for (const record of records) {
1586
- const keyValues = {};
1587
- for (const f of fields) {
1588
- keyValues[f] = readPath(record, f);
1589
- }
1590
- const dedupKey = canonicalGroupKey(fields, keyValues);
1591
- let bucket = buckets.get(dedupKey);
1592
- if (bucket === void 0) {
1593
- if (buckets.size >= GROUPBY_MAX_CARDINALITY) {
1594
- throw new GroupCardinalityError(
1595
- fieldLabel,
1596
- buckets.size + 1,
1597
- GROUPBY_MAX_CARDINALITY
1598
- );
1599
- }
1600
- bucket = { keyValues, records: [] };
1601
- buckets.set(dedupKey, bucket);
1602
- }
1603
- bucket.records.push(record);
1604
- }
1605
- if (buckets.size >= GROUPBY_WARN_CARDINALITY) {
1606
- warnCardinalityApproaching(fields, buckets.size);
1607
- }
1608
- const reducerKeys = Object.keys(spec);
1609
- const out = [];
1610
- for (const bucket of buckets.values()) {
1611
- const state = {};
1612
- for (const rk of reducerKeys) {
1613
- state[rk] = spec[rk].init();
1614
- }
1615
- for (const record of bucket.records) {
1616
- for (const rk of reducerKeys) {
1617
- state[rk] = spec[rk].step(state[rk], record);
1618
- }
1619
- }
1620
- const row = {};
1621
- for (const f of fields) {
1622
- row[f] = bucket.keyValues[f];
1623
- }
1624
- for (const rk of reducerKeys) {
1625
- row[rk] = spec[rk].finalize(state[rk]);
1626
- }
1627
- out.push(row);
1628
- }
1629
- return out;
1630
- }
1631
- var GroupedAggregation = class {
1632
- constructor(executeRecords, fields, spec, upstreams, dictLabelResolver) {
1633
- this.executeRecords = executeRecords;
1634
- this.spec = spec;
1635
- this.upstreams = upstreams;
1636
- this.dictLabelResolver = dictLabelResolver;
1637
- this.fields = typeof fields === "string" ? [fields] : [...fields];
1638
- }
1639
- executeRecords;
1640
- spec;
1641
- upstreams;
1642
- dictLabelResolver;
1643
- fields;
1644
- /** Execute the query, group, reduce, and return an array of rows. */
1645
- run() {
1646
- return groupAndReduce(this.executeRecords(), this.fields, this.spec);
1647
- }
1648
- /**
1649
- * Execute the query, group, reduce, and resolve `<field>Label` for
1650
- * each result row when the grouping field is a `dictKey` and a
1651
- * `locale` is provided. Returns `R[]` synchronously when
1652
- * no locale is specified (identical to `.run()`).
1653
- *
1654
- * The `<field>Label` field is appended to each row. Rows whose group
1655
- * key has no dictionary entry get `<field>Label: undefined`.
1656
- *
1657
- * Dict-label resolution is single-field only — multi-key groupings
1658
- * do not produce a `<field>Label`. The resolver is only attached
1659
- * by the builder when `fields.length === 1`.
1660
- */
1661
- async runAsync(opts) {
1662
- const rows = groupAndReduce(this.executeRecords(), this.fields, this.spec);
1663
- if (!opts?.locale || !this.dictLabelResolver || this.fields.length !== 1) return rows;
1664
- const resolve = this.dictLabelResolver;
1665
- const locale = opts.locale;
1666
- const fallback = opts.fallback;
1667
- const field = this.fields[0];
1668
- const labelKey = `${field}Label`;
1669
- return Promise.all(
1670
- rows.map(async (row) => {
1671
- const key = row[field];
1672
- if (typeof key !== "string") return row;
1673
- const label = await resolve(key, locale, fallback);
1674
- return { ...row, [labelKey]: label };
1675
- })
1676
- );
1677
- }
1678
- /**
1679
- * Build a reactive `LiveAggregation<R[]>` that re-runs the full
1680
- * group-and-reduce pipeline whenever any upstream source notifies
1681
- * of a change. Same error-isolation and idempotent-stop contract
1682
- * as `Aggregation.live()` — the implementation delegates to the
1683
- * same `LiveAggregationImpl` class by threading a fresh
1684
- * recompute closure through the existing constructor.
1685
- *
1686
- * uses naive full re-run on every change. Incremental
1687
- * per-bucket maintenance (apply `step` on inserted records,
1688
- * `remove` on deleted records, route by bucket key) is a future
1689
- * optimization — the reducer protocol admits it, but wiring
1690
- * delta-aware source subscriptions is a separate PR.
1691
- *
1692
- * Always call `live.stop()` when finished.
1693
- */
1694
- live() {
1695
- const recompute = () => groupAndReduce(this.executeRecords(), this.fields, this.spec);
1696
- return buildLiveAggregation(recompute, this.upstreams);
1697
- }
1698
- };
1699
-
1700
1569
  // src/query/scan-builder.ts
1701
1570
  var DEFAULT_SCAN_PAGE_SIZE = 100;
1702
1571
  var ScanBuilder = class _ScanBuilder {
@@ -1723,12 +1592,29 @@ var ScanBuilder = class _ScanBuilder {
1723
1592
  * context throws with an actionable error.
1724
1593
  */
1725
1594
  joinContext;
1726
- constructor(pageProvider, pageSize = DEFAULT_SCAN_PAGE_SIZE, clauses = [], joins = [], joinContext) {
1595
+ /**
1596
+ * Money field descriptors for the backing collection. When present, yielded
1597
+ * records are decoded (stored scaled-int → canonical decimal) so `scan()`
1598
+ * agrees with `get()`/`list()`/`query().toArray()` — #322. Decoded with
1599
+ * `'raw'` (canonical decimal, no locale-formatted virtuals) since the scan
1600
+ * stream carries no locale context, mirroring `Query.toArray()`.
1601
+ */
1602
+ moneyFields;
1603
+ constructor(pageProvider, pageSize = DEFAULT_SCAN_PAGE_SIZE, clauses = [], joins = [], joinContext, moneyFields) {
1727
1604
  this.pageProvider = pageProvider;
1728
1605
  this.pageSize = pageSize;
1729
1606
  this.clauses = clauses;
1730
1607
  this.joins = joins;
1731
1608
  this.joinContext = joinContext;
1609
+ this.moneyFields = moneyFields;
1610
+ }
1611
+ /**
1612
+ * Decode this scan's money fields on a record (stored scaled-int → canonical
1613
+ * decimal). No-op when no money fields are declared. See {@link moneyFields}.
1614
+ */
1615
+ decodeMoney(record) {
1616
+ if (!this.moneyFields || Object.keys(this.moneyFields).length === 0) return record;
1617
+ return decodeMoneyFields(record, this.moneyFields, "raw");
1732
1618
  }
1733
1619
  /**
1734
1620
  * Add a field comparison. Runs per record as the scan stream
@@ -1744,13 +1630,15 @@ var ScanBuilder = class _ScanBuilder {
1744
1630
  * evaluates clauses per record in O(1) per clause.
1745
1631
  */
1746
1632
  where(field, op, value) {
1747
- const clause = { type: "field", field, op, value };
1633
+ const desc = this.moneyFields?.[field];
1634
+ const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
1748
1635
  return new _ScanBuilder(
1749
1636
  this.pageProvider,
1750
1637
  this.pageSize,
1751
1638
  [...this.clauses, clause],
1752
1639
  this.joins,
1753
- this.joinContext
1640
+ this.joinContext,
1641
+ this.moneyFields
1754
1642
  );
1755
1643
  }
1756
1644
  /**
@@ -1769,7 +1657,8 @@ var ScanBuilder = class _ScanBuilder {
1769
1657
  this.pageSize,
1770
1658
  [...this.clauses, clause],
1771
1659
  this.joins,
1772
- this.joinContext
1660
+ this.joinContext,
1661
+ this.moneyFields
1773
1662
  );
1774
1663
  }
1775
1664
  /**
@@ -1880,7 +1769,8 @@ var ScanBuilder = class _ScanBuilder {
1880
1769
  this.pageSize,
1881
1770
  this.clauses,
1882
1771
  [...this.joins, leg],
1883
- this.joinContext
1772
+ this.joinContext,
1773
+ this.moneyFields
1884
1774
  );
1885
1775
  }
1886
1776
  /**
@@ -1897,10 +1787,11 @@ var ScanBuilder = class _ScanBuilder {
1897
1787
  while (true) {
1898
1788
  for (const record of page.items) {
1899
1789
  if (!this.recordMatches(record)) continue;
1790
+ const decoded = this.decodeMoney(record);
1900
1791
  if (joinResolvers === null) {
1901
- yield record;
1792
+ yield decoded;
1902
1793
  } else {
1903
- let attached = record;
1794
+ let attached = decoded;
1904
1795
  for (const resolver of joinResolvers) {
1905
1796
  attached = this.applyOneJoinStreaming(attached, resolver);
1906
1797
  }
@@ -2086,8 +1977,9 @@ var ScanBuilder = class _ScanBuilder {
2086
1977
  */
2087
1978
  recordMatches(record) {
2088
1979
  if (this.clauses.length === 0) return true;
1980
+ const fnView = this.moneyFields && Object.keys(this.moneyFields).length > 0 && hasFnClause(this.clauses) ? this.decodeMoney(record) : void 0;
2089
1981
  for (const clause of this.clauses) {
2090
- if (!evaluateClause(record, clause)) return false;
1982
+ if (!evaluateClause(record, clause, fnView)) return false;
2091
1983
  }
2092
1984
  return true;
2093
1985
  }
@@ -2098,38 +1990,21 @@ function coerceRefKey2(value) {
2098
1990
  if (typeof value === "number" || typeof value === "bigint") return String(value);
2099
1991
  return null;
2100
1992
  }
2101
- // Annotate the CommonJS export names for ESM import in node:
2102
- 0 && (module.exports = {
2103
- Aggregation,
2104
- CollectionIndexes,
1993
+
1994
+ export {
1995
+ validateMoneyFieldPaths,
1996
+ canonicalizeStoredMoney,
1997
+ canonicalizeIncomingMoney,
1998
+ quantizeMoneyFields,
1999
+ decodeMoneyFields,
2105
2000
  DEFAULT_JOIN_MAX_ROWS,
2106
- DanglingReferenceError,
2107
- GROUPBY_MAX_CARDINALITY,
2108
- GROUPBY_WARN_CARDINALITY,
2109
- GroupCardinalityError,
2110
- GroupedAggregation,
2111
- GroupedQuery,
2112
- GroupedQueryN,
2113
- IndexRequiredError,
2114
- IndexWriteFailureError,
2115
- JoinTooLargeError,
2116
- Query,
2117
- ScanBuilder,
2118
2001
  applyJoins,
2119
- avg,
2120
- buildLiveAggregation,
2002
+ resetJoinWarnings,
2121
2003
  buildLiveQuery,
2122
- count,
2123
- evaluateClause,
2124
- evaluateFieldClause,
2004
+ NO_AGGREGATE,
2005
+ DEFAULT_CROSS_JOIN_MAX_ROWS,
2006
+ Query,
2125
2007
  executePlan,
2126
- groupAndReduce,
2127
- max,
2128
- min,
2129
- readPath,
2130
- reduceRecords,
2131
- resetGroupByWarnings,
2132
- resetJoinWarnings,
2133
- sum
2134
- });
2135
- //# sourceMappingURL=index.cjs.map
2008
+ ScanBuilder
2009
+ };
2010
+ //# sourceMappingURL=chunk-QGBBQKDS.js.map