@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
@@ -57,88 +57,156 @@ __export(query_exports, {
57
57
  });
58
58
  module.exports = __toCommonJS(query_exports);
59
59
 
60
- // src/query/predicate.ts
61
- function readPath(record, path) {
62
- if (record === null || record === void 0) return void 0;
63
- if (!path.includes(".")) {
64
- return record[path];
60
+ // src/money/fixed-point.ts
61
+ function expandExponent(s) {
62
+ const m = /^([+-]?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
63
+ if (!m) return s;
64
+ const sign = m[1] === "-" ? "-" : "";
65
+ const intp = m[2];
66
+ const frac = m[3] ?? "";
67
+ const exp = Number(m[4]);
68
+ const digits = intp + frac;
69
+ const pointPos = intp.length + exp;
70
+ let body;
71
+ if (pointPos <= 0) {
72
+ body = "0." + "0".repeat(-pointPos) + digits;
73
+ } else if (pointPos >= digits.length) {
74
+ body = digits + "0".repeat(pointPos - digits.length);
75
+ } else {
76
+ body = digits.slice(0, pointPos) + "." + digits.slice(pointPos);
65
77
  }
66
- const segments = path.split(".");
67
- let cursor = record;
68
- for (const segment of segments) {
69
- if (cursor === null || cursor === void 0) return void 0;
70
- cursor = cursor[segment];
78
+ return sign + body;
79
+ }
80
+ function toCanonicalDecimalString(input) {
81
+ let s;
82
+ if (typeof input === "number") {
83
+ if (!Number.isFinite(input)) return null;
84
+ s = String(input);
85
+ } else {
86
+ s = input.trim();
71
87
  }
72
- return cursor;
88
+ s = expandExponent(s);
89
+ if (s.startsWith("+")) s = s.slice(1);
90
+ if (!/^-?(\d+(\.\d*)?|\.\d+)$/.test(s)) return null;
91
+ return s;
73
92
  }
74
- function evaluateFieldClause(record, clause) {
75
- const actual = readPath(record, clause.field);
76
- const { op, value } = clause;
77
- switch (op) {
78
- case "==":
79
- return actual === value;
80
- case "!=":
81
- return actual !== value;
82
- case "<":
83
- return isComparable(actual, value) && actual < value;
84
- case "<=":
85
- return isComparable(actual, value) && actual <= value;
86
- case ">":
87
- return isComparable(actual, value) && actual > value;
88
- case ">=":
89
- return isComparable(actual, value) && actual >= value;
90
- case "in":
91
- return Array.isArray(value) && value.includes(actual);
92
- case "contains":
93
- if (typeof actual === "string") return typeof value === "string" && actual.includes(value);
94
- if (Array.isArray(actual)) return actual.includes(value);
95
- return false;
96
- case "startsWith":
97
- return typeof actual === "string" && typeof value === "string" && actual.startsWith(value);
98
- case "between": {
99
- if (!Array.isArray(value) || value.length !== 2) return false;
100
- const [lo, hi] = value;
101
- if (!isComparable(actual, lo) || !isComparable(actual, hi)) return false;
102
- return actual >= lo && actual <= hi;
103
- }
104
- default: {
105
- const _exhaustive = op;
106
- void _exhaustive;
93
+ function shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, mode) {
94
+ switch (mode) {
95
+ case "up":
96
+ return true;
97
+ case "down":
107
98
  return false;
108
- }
99
+ case "ceil":
100
+ return !negative;
101
+ case "floor":
102
+ return negative;
103
+ case "half-up":
104
+ return firstDiscarded >= 5;
105
+ case "half-down":
106
+ return firstDiscarded > 5 || firstDiscarded === 5 && hasMoreNonZeroAfterFirst;
107
+ case "half-even":
108
+ if (firstDiscarded > 5) return true;
109
+ if (firstDiscarded < 5) return false;
110
+ return hasMoreNonZeroAfterFirst || lastKeptDigit % 2 === 1;
109
111
  }
110
112
  }
111
- function isComparable(a, b) {
112
- if (typeof a === "number" && typeof b === "number") return true;
113
- if (typeof a === "string" && typeof b === "string") return true;
114
- if (a instanceof Date && b instanceof Date) return true;
115
- return false;
113
+ function parseToScaledInt(input, scale, rounding) {
114
+ const canonical = toCanonicalDecimalString(input);
115
+ if (canonical === null) return { ok: false, reason: "nonfinite" };
116
+ const negative = canonical.startsWith("-");
117
+ const unsigned = negative ? canonical.slice(1) : canonical;
118
+ const dot = unsigned.indexOf(".");
119
+ const intPart = dot === -1 ? unsigned : unsigned.slice(0, dot);
120
+ const fracPart = dot === -1 ? "" : unsigned.slice(dot + 1);
121
+ const intDigits = intPart === "" ? "0" : intPart;
122
+ if (fracPart.length <= scale) {
123
+ const keep2 = fracPart.padEnd(scale, "0");
124
+ const magnitude2 = BigInt(intDigits + keep2);
125
+ return { ok: true, value: negative && magnitude2 !== 0n ? -magnitude2 : magnitude2 };
126
+ }
127
+ const keep = fracPart.slice(0, scale);
128
+ const tail = fracPart.slice(scale);
129
+ const magnitudeDigits = intDigits + keep;
130
+ let magnitude = BigInt(magnitudeDigits);
131
+ if (/^0+$/.test(tail)) {
132
+ return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
133
+ }
134
+ if (rounding === void 0) return { ok: false, reason: "precision" };
135
+ const lastKeptDigit = Number(magnitudeDigits[magnitudeDigits.length - 1]);
136
+ const firstDiscarded = Number(tail[0]);
137
+ const hasMoreNonZeroAfterFirst = /[1-9]/.test(tail.slice(1));
138
+ if (shouldRoundUp(negative, lastKeptDigit, firstDiscarded, hasMoreNonZeroAfterFirst, rounding)) {
139
+ magnitude += 1n;
140
+ }
141
+ return { ok: true, value: negative && magnitude !== 0n ? -magnitude : magnitude };
116
142
  }
117
- function evaluateClause(record, clause) {
118
- switch (clause.type) {
119
- case "field":
120
- return evaluateFieldClause(record, clause);
121
- case "filter":
122
- return clause.fn(record);
123
- case "wherePredicate":
124
- return clause.fn(record, clause.ctx);
125
- case "crossJoin":
126
- throw new Error(
127
- `evaluateClause: 'crossJoin' clauses are expansion primitives and are not evaluated per-record. This is a query planner routing error \u2014 crossJoin clauses must be extracted from the clause list before calling evaluateClause or filterRecords.`
128
- );
129
- case "group":
130
- if (clause.op === "and") {
131
- for (const child of clause.clauses) {
132
- if (!evaluateClause(record, child)) return false;
133
- }
134
- return true;
135
- } else {
136
- for (const child of clause.clauses) {
137
- if (evaluateClause(record, child)) return true;
138
- }
139
- return false;
140
- }
141
- }
143
+ function formatScaledInt(value, scale) {
144
+ const negative = value < 0n;
145
+ const abs = (negative ? -value : value).toString();
146
+ if (scale === 0) return (negative ? "-" : "") + abs;
147
+ const padded = abs.padStart(scale + 1, "0");
148
+ const cut = padded.length - scale;
149
+ const intPart = padded.slice(0, cut);
150
+ const fracPart = padded.slice(cut);
151
+ return (negative ? "-" : "") + intPart + "." + fracPart;
152
+ }
153
+
154
+ // src/money/iso4217.ts
155
+ var MINOR_UNITS = {
156
+ // 2-decimal majors
157
+ EUR: 2,
158
+ USD: 2,
159
+ GBP: 2,
160
+ CHF: 2,
161
+ CAD: 2,
162
+ AUD: 2,
163
+ NZD: 2,
164
+ SGD: 2,
165
+ HKD: 2,
166
+ CNY: 2,
167
+ INR: 2,
168
+ BRL: 2,
169
+ MXN: 2,
170
+ ZAR: 2,
171
+ RUB: 2,
172
+ TRY: 2,
173
+ PLN: 2,
174
+ SEK: 2,
175
+ NOK: 2,
176
+ DKK: 2,
177
+ CZK: 2,
178
+ HUF: 2,
179
+ RON: 2,
180
+ ILS: 2,
181
+ THB: 2,
182
+ PHP: 2,
183
+ MYR: 2,
184
+ IDR: 2,
185
+ AED: 2,
186
+ SAR: 2,
187
+ QAR: 2,
188
+ EGP: 2,
189
+ // 0-decimal
190
+ JPY: 0,
191
+ KRW: 0,
192
+ ISK: 0,
193
+ CLP: 0,
194
+ VND: 0,
195
+ XOF: 0,
196
+ XAF: 0,
197
+ PYG: 0,
198
+ // 3-decimal
199
+ BHD: 3,
200
+ KWD: 3,
201
+ OMR: 3,
202
+ TND: 3,
203
+ JOD: 3,
204
+ IQD: 3,
205
+ LYD: 3
206
+ };
207
+ function scaleForCurrency(code) {
208
+ const v = MINOR_UNITS[code];
209
+ return v === void 0 ? null : v;
142
210
  }
143
211
 
144
212
  // src/errors.ts
@@ -151,6 +219,12 @@ var NoydbError = class extends Error {
151
219
  this.code = code;
152
220
  }
153
221
  };
222
+ var ValidationError = class extends NoydbError {
223
+ constructor(message = "Validation error") {
224
+ super("VALIDATION_ERROR", message);
225
+ this.name = "ValidationError";
226
+ }
227
+ };
154
228
  var GroupCardinalityError = class extends NoydbError {
155
229
  /** The field being grouped on. */
156
230
  field;
@@ -256,6 +330,244 @@ var DanglingReferenceError = class extends NoydbError {
256
330
  }
257
331
  };
258
332
 
333
+ // src/money/descriptor.ts
334
+ var MoneyUnsupportedError = class extends NoydbError {
335
+ constructor(field, message) {
336
+ super(
337
+ "MONEY_UNSUPPORTED",
338
+ message ?? `money: operation is not supported on field "${field}" \u2014 use sum() and count() and divide at the boundary`
339
+ );
340
+ this.field = field;
341
+ this.name = "MoneyUnsupportedError";
342
+ }
343
+ field;
344
+ };
345
+
346
+ // src/money/where.ts
347
+ function isMoneyValueObject(v) {
348
+ return typeof v === "object" && v !== null && "currency" in v;
349
+ }
350
+ function parseOperand(field, raw, desc) {
351
+ let amount;
352
+ let currency;
353
+ if (desc.mode === "fixed") {
354
+ currency = desc.fixedCurrency;
355
+ amount = raw;
356
+ } else if (isMoneyValueObject(raw)) {
357
+ currency = String(raw.currency);
358
+ amount = raw.amount;
359
+ } else {
360
+ const sole = desc.soleCurrency();
361
+ if (sole === void 0) {
362
+ throw new MoneyUnsupportedError(
363
+ `where("${field}"): field is multi-currency \u2014 compare against { amount, currency }, not a bare amount`
364
+ );
365
+ }
366
+ currency = sole;
367
+ amount = raw;
368
+ }
369
+ if (typeof amount !== "number" && typeof amount !== "string") {
370
+ throw new MoneyUnsupportedError(
371
+ `where("${field}"): operand ${JSON.stringify(raw)} is not a money amount`
372
+ );
373
+ }
374
+ const r = parseToScaledInt(amount, desc.scaleFor(currency), desc.rounding);
375
+ if (!r.ok) {
376
+ throw new MoneyUnsupportedError(
377
+ `where("${field}"): operand ${JSON.stringify(amount)} is not a finite decimal`
378
+ );
379
+ }
380
+ return { scaled: r.value.toString(), currency };
381
+ }
382
+ function moneyFieldClause(field, op, value, desc) {
383
+ switch (op) {
384
+ case "==":
385
+ case "!=":
386
+ case "<":
387
+ case "<=":
388
+ case ">":
389
+ case ">=": {
390
+ const e = parseOperand(field, value, desc);
391
+ return withMoney(field, op, value, desc, [e]);
392
+ }
393
+ case "between": {
394
+ if (!Array.isArray(value) || value.length !== 2) {
395
+ throw new MoneyUnsupportedError(`where("${field}"): 'between' needs a [lo, hi] tuple`);
396
+ }
397
+ const lo = parseOperand(field, value[0], desc);
398
+ const hi = parseOperand(field, value[1], desc);
399
+ if (lo.currency !== hi.currency) {
400
+ throw new MoneyUnsupportedError(
401
+ `where("${field}"): 'between' bounds mix currencies (${lo.currency} vs ${hi.currency})`
402
+ );
403
+ }
404
+ return withMoney(field, op, value, desc, [lo, hi]);
405
+ }
406
+ case "in": {
407
+ if (!Array.isArray(value)) {
408
+ throw new MoneyUnsupportedError(`where("${field}"): 'in' needs an array of amounts`);
409
+ }
410
+ return withMoney(field, op, value, desc, value.map((v) => parseOperand(field, v, desc)));
411
+ }
412
+ default:
413
+ throw new MoneyUnsupportedError(
414
+ `where("${field}"): operator '${op}' is not supported on a money field`
415
+ );
416
+ }
417
+ }
418
+ function withMoney(field, op, originalValue, desc, entries) {
419
+ const money = { mode: desc.mode, entries };
420
+ const value = desc.mode !== "fixed" ? originalValue : entries.length === 1 && op !== "in" && op !== "between" ? entries[0].scaled : entries.map((e) => e.scaled);
421
+ return { type: "field", field, op, value, money };
422
+ }
423
+ function readStored(actual, operand) {
424
+ let amount;
425
+ let currency;
426
+ if (operand.mode === "fixed") {
427
+ if (typeof actual !== "string" && typeof actual !== "number") return null;
428
+ amount = actual;
429
+ currency = operand.entries[0]?.currency ?? "";
430
+ } else {
431
+ if (!isMoneyValueObject(actual)) return null;
432
+ if (typeof actual.currency !== "string") return null;
433
+ amount = actual.amount;
434
+ currency = actual.currency;
435
+ }
436
+ if (typeof amount !== "string" && typeof amount !== "number") return null;
437
+ try {
438
+ return { scaled: BigInt(amount).toString(), currency };
439
+ } catch {
440
+ return null;
441
+ }
442
+ }
443
+ function evaluateMoneyClause(actual, op, operand) {
444
+ const stored = readStored(actual, operand);
445
+ if (stored === null) return op === "!=";
446
+ const a = BigInt(stored.scaled);
447
+ if (op === "in") {
448
+ return operand.entries.some(
449
+ (e2) => e2.currency === stored.currency && BigInt(e2.scaled) === a
450
+ );
451
+ }
452
+ if (op === "between") {
453
+ const [lo, hi] = operand.entries;
454
+ if (!lo || !hi || lo.currency !== stored.currency) return false;
455
+ return a >= BigInt(lo.scaled) && a <= BigInt(hi.scaled);
456
+ }
457
+ const e = operand.entries[0];
458
+ if (!e) return false;
459
+ if (e.currency !== stored.currency) return op === "!=";
460
+ const b = BigInt(e.scaled);
461
+ switch (op) {
462
+ case "==":
463
+ return a === b;
464
+ case "!=":
465
+ return a !== b;
466
+ case "<":
467
+ return a < b;
468
+ case "<=":
469
+ return a <= b;
470
+ case ">":
471
+ return a > b;
472
+ case ">=":
473
+ return a >= b;
474
+ default:
475
+ return false;
476
+ }
477
+ }
478
+
479
+ // src/query/predicate.ts
480
+ function readPath(record, path) {
481
+ if (record === null || record === void 0) return void 0;
482
+ if (!path.includes(".")) {
483
+ return record[path];
484
+ }
485
+ const segments = path.split(".");
486
+ let cursor = record;
487
+ for (const segment of segments) {
488
+ if (cursor === null || cursor === void 0) return void 0;
489
+ cursor = cursor[segment];
490
+ }
491
+ return cursor;
492
+ }
493
+ function evaluateFieldClause(record, clause) {
494
+ const actual = readPath(record, clause.field);
495
+ const { op, value } = clause;
496
+ if (clause.money) return evaluateMoneyClause(actual, op, clause.money);
497
+ switch (op) {
498
+ case "==":
499
+ return actual === value;
500
+ case "!=":
501
+ return actual !== value;
502
+ case "<":
503
+ return isComparable(actual, value) && actual < value;
504
+ case "<=":
505
+ return isComparable(actual, value) && actual <= value;
506
+ case ">":
507
+ return isComparable(actual, value) && actual > value;
508
+ case ">=":
509
+ return isComparable(actual, value) && actual >= value;
510
+ case "in":
511
+ return Array.isArray(value) && value.includes(actual);
512
+ case "contains":
513
+ if (typeof actual === "string") return typeof value === "string" && actual.includes(value);
514
+ if (Array.isArray(actual)) return actual.includes(value);
515
+ return false;
516
+ case "startsWith":
517
+ return typeof actual === "string" && typeof value === "string" && actual.startsWith(value);
518
+ case "between": {
519
+ if (!Array.isArray(value) || value.length !== 2) return false;
520
+ const [lo, hi] = value;
521
+ if (!isComparable(actual, lo) || !isComparable(actual, hi)) return false;
522
+ return actual >= lo && actual <= hi;
523
+ }
524
+ default: {
525
+ const _exhaustive = op;
526
+ void _exhaustive;
527
+ return false;
528
+ }
529
+ }
530
+ }
531
+ function isComparable(a, b) {
532
+ if (typeof a === "number" && typeof b === "number") return true;
533
+ if (typeof a === "string" && typeof b === "string") return true;
534
+ if (a instanceof Date && b instanceof Date) return true;
535
+ return false;
536
+ }
537
+ function evaluateClause(record, clause, fnRecord) {
538
+ switch (clause.type) {
539
+ case "field":
540
+ return evaluateFieldClause(record, clause);
541
+ case "filter":
542
+ return clause.fn(fnRecord !== void 0 ? fnRecord : record);
543
+ case "wherePredicate":
544
+ return clause.fn(fnRecord !== void 0 ? fnRecord : record, clause.ctx);
545
+ case "crossJoin":
546
+ throw new Error(
547
+ `evaluateClause: 'crossJoin' clauses are expansion primitives and are not evaluated per-record. This is a query planner routing error \u2014 crossJoin clauses must be extracted from the clause list before calling evaluateClause or filterRecords.`
548
+ );
549
+ case "group":
550
+ if (clause.op === "and") {
551
+ for (const child of clause.clauses) {
552
+ if (!evaluateClause(record, child, fnRecord)) return false;
553
+ }
554
+ return true;
555
+ } else {
556
+ for (const child of clause.clauses) {
557
+ if (evaluateClause(record, child, fnRecord)) return true;
558
+ }
559
+ return false;
560
+ }
561
+ }
562
+ }
563
+ function hasFnClause(clauses) {
564
+ for (const c of clauses) {
565
+ if (c.type === "filter" || c.type === "wherePredicate") return true;
566
+ if (c.type === "group" && hasFnClause(c.clauses)) return true;
567
+ }
568
+ return false;
569
+ }
570
+
259
571
  // src/query/join.ts
260
572
  var DEFAULT_JOIN_MAX_ROWS = 5e4;
261
573
  var JOIN_WARN_FRACTION = 0.8;
@@ -501,89 +813,6 @@ var NO_AGGREGATE = {
501
813
  }
502
814
  };
503
815
 
504
- // src/money/fixed-point.ts
505
- function formatScaledInt(value, scale) {
506
- const negative = value < 0n;
507
- const abs = (negative ? -value : value).toString();
508
- if (scale === 0) return (negative ? "-" : "") + abs;
509
- const padded = abs.padStart(scale + 1, "0");
510
- const cut = padded.length - scale;
511
- const intPart = padded.slice(0, cut);
512
- const fracPart = padded.slice(cut);
513
- return (negative ? "-" : "") + intPart + "." + fracPart;
514
- }
515
-
516
- // src/money/iso4217.ts
517
- var MINOR_UNITS = {
518
- // 2-decimal majors
519
- EUR: 2,
520
- USD: 2,
521
- GBP: 2,
522
- CHF: 2,
523
- CAD: 2,
524
- AUD: 2,
525
- NZD: 2,
526
- SGD: 2,
527
- HKD: 2,
528
- CNY: 2,
529
- INR: 2,
530
- BRL: 2,
531
- MXN: 2,
532
- ZAR: 2,
533
- RUB: 2,
534
- TRY: 2,
535
- PLN: 2,
536
- SEK: 2,
537
- NOK: 2,
538
- DKK: 2,
539
- CZK: 2,
540
- HUF: 2,
541
- RON: 2,
542
- ILS: 2,
543
- THB: 2,
544
- PHP: 2,
545
- MYR: 2,
546
- IDR: 2,
547
- AED: 2,
548
- SAR: 2,
549
- QAR: 2,
550
- EGP: 2,
551
- // 0-decimal
552
- JPY: 0,
553
- KRW: 0,
554
- ISK: 0,
555
- CLP: 0,
556
- VND: 0,
557
- XOF: 0,
558
- XAF: 0,
559
- PYG: 0,
560
- // 3-decimal
561
- BHD: 3,
562
- KWD: 3,
563
- OMR: 3,
564
- TND: 3,
565
- JOD: 3,
566
- IQD: 3,
567
- LYD: 3
568
- };
569
- function scaleForCurrency(code) {
570
- const v = MINOR_UNITS[code];
571
- return v === void 0 ? null : v;
572
- }
573
-
574
- // src/money/descriptor.ts
575
- var MoneyUnsupportedError = class extends NoydbError {
576
- constructor(field, message) {
577
- super(
578
- "MONEY_UNSUPPORTED",
579
- message ?? `money: operation is not supported on field "${field}" \u2014 use sum() and count() and divide at the boundary`
580
- );
581
- this.field = field;
582
- this.name = "MoneyUnsupportedError";
583
- }
584
- field;
585
- };
586
-
587
816
  // src/money/money-reducer.ts
588
817
  function toScaledInt(v) {
589
818
  if (typeof v === "string" || typeof v === "number" || typeof v === "bigint") {
@@ -758,8 +987,107 @@ function wrapMoneyReducers(spec, moneyFields) {
758
987
  return changed ? out : spec;
759
988
  }
760
989
 
990
+ // src/money/paths.ts
991
+ var SEGMENT_RE = /^(\*|[^.[\]*]+)(\[\])?$/;
992
+ var parseCache = /* @__PURE__ */ new Map();
993
+ function parseMoneyPath(path) {
994
+ const cached = parseCache.get(path);
995
+ if (cached) return cached;
996
+ if (typeof path !== "string" || path.length === 0) {
997
+ throw new ValidationError("moneyFields: path must be a non-empty string");
998
+ }
999
+ const segments = [];
1000
+ for (const part of path.split(".")) {
1001
+ const m = SEGMENT_RE.exec(part);
1002
+ if (!m) {
1003
+ throw new ValidationError(
1004
+ `moneyFields: invalid path "${path}" \u2014 segment "${part}" must be a key, "key[]", "*", or "*[]"`
1005
+ );
1006
+ }
1007
+ const array = m[2] === "[]";
1008
+ segments.push(
1009
+ m[1] === "*" ? { kind: "wildcard", array } : { kind: "key", key: m[1], array }
1010
+ );
1011
+ }
1012
+ parseCache.set(path, segments);
1013
+ return segments;
1014
+ }
1015
+ function isSimpleMoneyPath(path) {
1016
+ return !path.includes(".") && !path.includes("[") && !path.includes("*");
1017
+ }
1018
+ function transformAtMoneyPath(node, path, segments, index, visit, lenient) {
1019
+ if (node === null || node === void 0) return node;
1020
+ const seg = segments[index];
1021
+ const last = index === segments.length - 1;
1022
+ if (seg.kind === "key") {
1023
+ if (typeof node !== "object" || Array.isArray(node)) {
1024
+ if (lenient) return node;
1025
+ throw new ValidationError(
1026
+ `moneyFields: path "${path}" expected an object at segment "${seg.key}", got ${Array.isArray(node) ? "an array" : typeof node}`
1027
+ );
1028
+ }
1029
+ const obj2 = node;
1030
+ if (!(seg.key in obj2) || obj2[seg.key] === null || obj2[seg.key] === void 0) return node;
1031
+ if (seg.array) {
1032
+ const arr = obj2[seg.key];
1033
+ if (!Array.isArray(arr)) {
1034
+ if (lenient) return node;
1035
+ throw new ValidationError(
1036
+ `moneyFields: path "${path}" declares "${seg.key}[]" but the value is not an array`
1037
+ );
1038
+ }
1039
+ const cloned = [...arr];
1040
+ if (last) {
1041
+ for (let i = 0; i < cloned.length; i++) visit(cloned, i);
1042
+ } else {
1043
+ for (let i = 0; i < cloned.length; i++) {
1044
+ cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
1045
+ }
1046
+ }
1047
+ return { ...obj2, [seg.key]: cloned };
1048
+ }
1049
+ const clone2 = { ...obj2 };
1050
+ if (last) {
1051
+ visit(clone2, seg.key);
1052
+ } else {
1053
+ clone2[seg.key] = transformAtMoneyPath(clone2[seg.key], path, segments, index + 1, visit, lenient);
1054
+ }
1055
+ return clone2;
1056
+ }
1057
+ if (seg.array) {
1058
+ if (!Array.isArray(node)) {
1059
+ if (lenient) return node;
1060
+ throw new ValidationError(`moneyFields: path "${path}" declares "*[]" but the value is not an array`);
1061
+ }
1062
+ const cloned = [...node];
1063
+ if (last) {
1064
+ for (let i = 0; i < cloned.length; i++) visit(cloned, i);
1065
+ } else {
1066
+ for (let i = 0; i < cloned.length; i++) {
1067
+ cloned[i] = transformAtMoneyPath(cloned[i], path, segments, index + 1, visit, lenient);
1068
+ }
1069
+ }
1070
+ return cloned;
1071
+ }
1072
+ if (typeof node !== "object" || Array.isArray(node)) {
1073
+ if (lenient) return node;
1074
+ throw new ValidationError(
1075
+ `moneyFields: path "${path}" applies "*" to a non-object (${Array.isArray(node) ? 'array \u2014 use "*[]"' : typeof node})`
1076
+ );
1077
+ }
1078
+ const obj = node;
1079
+ const clone = { ...obj };
1080
+ for (const key of Object.keys(obj)) {
1081
+ const v = clone[key];
1082
+ if (v === null || v === void 0) continue;
1083
+ if (last) visit(clone, key);
1084
+ else clone[key] = transformAtMoneyPath(v, path, segments, index + 1, visit, lenient);
1085
+ }
1086
+ return clone;
1087
+ }
1088
+
761
1089
  // src/money/normalize.ts
762
- function isMoneyValueObject(v) {
1090
+ function isMoneyValueObject2(v) {
763
1091
  return typeof v === "object" && v !== null && "currency" in v;
764
1092
  }
765
1093
  function formatCurrency(decimal, currency, scale, locale) {
@@ -771,33 +1099,70 @@ function formatCurrency(decimal, currency, scale, locale) {
771
1099
  });
772
1100
  return fmt.format(decimal);
773
1101
  }
1102
+ function decodeValue(stored, desc) {
1103
+ let currency;
1104
+ let scaledIntString;
1105
+ if (desc.mode === "fixed") {
1106
+ if (typeof stored !== "string" && typeof stored !== "number") return null;
1107
+ currency = desc.fixedCurrency;
1108
+ scaledIntString = String(stored);
1109
+ } else {
1110
+ if (!isMoneyValueObject2(stored)) return null;
1111
+ const amount = stored.amount;
1112
+ if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") return null;
1113
+ currency = stored.currency;
1114
+ scaledIntString = String(amount);
1115
+ }
1116
+ const scale = desc.scaleFor(currency);
1117
+ let decimal;
1118
+ try {
1119
+ decimal = formatScaledInt(BigInt(scaledIntString), scale);
1120
+ } catch {
1121
+ return null;
1122
+ }
1123
+ return {
1124
+ decoded: desc.mode === "fixed" ? decimal : { amount: decimal, currency },
1125
+ decimal,
1126
+ currency,
1127
+ scale
1128
+ };
1129
+ }
774
1130
  function decodeMoneyFields(record, moneyFields, locale) {
775
- const out = { ...record };
1131
+ let out = { ...record };
776
1132
  const format = locale !== "raw";
777
1133
  const fmtLocale = typeof locale === "string" && locale !== "raw" ? locale : "en-US";
778
- for (const [field, desc] of Object.entries(moneyFields)) {
779
- const stored = out[field];
780
- if (stored === null || stored === void 0) continue;
781
- let currency;
782
- let scaledIntString;
783
- if (desc.mode === "fixed") {
784
- if (typeof stored !== "string" && typeof stored !== "number") continue;
785
- currency = desc.fixedCurrency;
786
- scaledIntString = String(stored);
787
- } else {
788
- if (!isMoneyValueObject(stored)) continue;
789
- const amount = stored.amount;
790
- if (typeof stored.currency !== "string" || typeof amount !== "string" && typeof amount !== "number") continue;
791
- currency = stored.currency;
792
- scaledIntString = String(amount);
793
- }
794
- const scale = desc.scaleFor(currency);
795
- const decimal = formatScaledInt(BigInt(scaledIntString), scale);
796
- out[field] = desc.mode === "fixed" ? decimal : { amount: decimal, currency };
797
- if (format) {
798
- out[`${field}Formatted`] = formatCurrency(decimal, currency, scale, fmtLocale);
799
- out[`${field}Number`] = Number(decimal);
800
- }
1134
+ for (const [path, desc] of Object.entries(moneyFields)) {
1135
+ if (isSimpleMoneyPath(path)) {
1136
+ const stored = out[path];
1137
+ if (stored === null || stored === void 0) continue;
1138
+ const r = decodeValue(stored, desc);
1139
+ if (r === null) continue;
1140
+ out[path] = r.decoded;
1141
+ if (format) {
1142
+ out[`${path}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
1143
+ out[`${path}Number`] = Number(r.decimal);
1144
+ }
1145
+ continue;
1146
+ }
1147
+ out = transformAtMoneyPath(
1148
+ out,
1149
+ path,
1150
+ parseMoneyPath(path),
1151
+ 0,
1152
+ (container, key) => {
1153
+ const stored = container[key];
1154
+ if (stored === null || stored === void 0) return;
1155
+ const r = decodeValue(stored, desc);
1156
+ if (r === null) return;
1157
+ container[key] = r.decoded;
1158
+ if (format && typeof key === "string" && !Array.isArray(container)) {
1159
+ container[`${key}Formatted`] = formatCurrency(r.decimal, r.currency, r.scale, fmtLocale);
1160
+ container[`${key}Number`] = Number(r.decimal);
1161
+ }
1162
+ },
1163
+ /* lenient */
1164
+ true
1165
+ );
801
1166
  }
802
1167
  return out;
803
1168
  }
@@ -894,9 +1259,18 @@ var Query = class _Query {
894
1259
  this.predicates
895
1260
  );
896
1261
  }
897
- /** Add a field comparison. Multiple where() calls are AND-combined. */
1262
+ /**
1263
+ * Add a field comparison. Multiple where() calls are AND-combined.
1264
+ *
1265
+ * A declared money field compares in MAJOR units (#336): the operand
1266
+ * (`10000`, `'10000.00'`, or `{ amount, currency }` in multi mode) is
1267
+ * quantized into stored scaled-int space at build time and evaluated
1268
+ * BigInt-exact per record. A malformed operand or a string operator
1269
+ * (`contains`/`startsWith`) throws here, at the call site.
1270
+ */
898
1271
  where(field, op, value) {
899
- const clause = { type: "field", field, op, value };
1272
+ const desc = this.source.moneyFields?.[field];
1273
+ const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
900
1274
  return new _Query(
901
1275
  this.source,
902
1276
  { ...this.plan, clauses: [...this.plan.clauses, clause] },
@@ -1222,7 +1596,7 @@ var Query = class _Query {
1222
1596
  }
1223
1597
  const { candidates, remainingClauses } = candidateRecords(this.source, this.plan.clauses);
1224
1598
  if (remainingClauses.length === 0) return candidates.length;
1225
- return filterRecords(candidates, remainingClauses).length;
1599
+ return filterRecords(candidates, remainingClauses, fnViewDecoder(this.source)).length;
1226
1600
  }
1227
1601
  /**
1228
1602
  * Reduce the matching records through a named set of reducers.
@@ -1279,7 +1653,7 @@ var Query = class _Query {
1279
1653
  return executeClausePipeline(source, clauses, joinCtx);
1280
1654
  }
1281
1655
  const { candidates, remainingClauses } = candidateRecords(source, clauses);
1282
- return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
1656
+ return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
1283
1657
  };
1284
1658
  const upstreams = [];
1285
1659
  if (source.subscribe) {
@@ -1302,7 +1676,7 @@ var Query = class _Query {
1302
1676
  return executeClausePipeline(source, clauses, joinCtx);
1303
1677
  }
1304
1678
  const { candidates, remainingClauses } = candidateRecords(source, clauses);
1305
- return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses);
1679
+ return remainingClauses.length === 0 ? candidates : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
1306
1680
  };
1307
1681
  const upstreams = [];
1308
1682
  if (source.subscribe) {
@@ -1454,7 +1828,7 @@ function executePlanWithSource(source, plan, joinContext) {
1454
1828
  result = executeClausePipeline(source, plan.clauses, joinContext);
1455
1829
  } else {
1456
1830
  const { candidates, remainingClauses } = candidateRecords(source, plan.clauses);
1457
- result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses);
1831
+ result = remainingClauses.length === 0 ? [...candidates] : filterRecords(candidates, remainingClauses, fnViewDecoder(source));
1458
1832
  }
1459
1833
  if (plan.orderBy.length > 0) {
1460
1834
  result = sortRecords(result, plan.orderBy);
@@ -1477,6 +1851,7 @@ function candidateRecords(source, clauses) {
1477
1851
  const clause = clauses[i];
1478
1852
  if (clause.type !== "field") continue;
1479
1853
  if (!indexes.has(clause.field)) continue;
1854
+ if (clause.money?.mode === "multi") continue;
1480
1855
  let ids = null;
1481
1856
  if (clause.op === "==") {
1482
1857
  ids = indexes.lookupEqual(clause.field, clause.value);
@@ -1522,13 +1897,20 @@ function executePlan(records, plan) {
1522
1897
  }
1523
1898
  return result;
1524
1899
  }
1525
- function filterRecords(records, clauses) {
1900
+ function fnViewDecoder(source) {
1901
+ const mf = source.moneyFields;
1902
+ if (!mf || Object.keys(mf).length === 0) return void 0;
1903
+ return (r) => decodeMoneyFields(r, mf, "raw");
1904
+ }
1905
+ function filterRecords(records, clauses, decodeForFns) {
1526
1906
  if (clauses.length === 0) return [...records];
1907
+ const needsFnView = decodeForFns !== void 0 && hasFnClause(clauses);
1527
1908
  const out = [];
1528
1909
  for (const r of records) {
1910
+ const fnView = needsFnView ? decodeForFns(r) : void 0;
1529
1911
  let matches = true;
1530
1912
  for (const clause of clauses) {
1531
- if (!evaluateClause(r, clause)) {
1913
+ if (!evaluateClause(r, clause, fnView)) {
1532
1914
  matches = false;
1533
1915
  break;
1534
1916
  }
@@ -1540,10 +1922,11 @@ function filterRecords(records, clauses) {
1540
1922
  function executeClausePipeline(source, clauses, joinContext) {
1541
1923
  let rel = [...source.snapshot()];
1542
1924
  let filterBatch = [];
1925
+ const decodeForFns = fnViewDecoder(source);
1543
1926
  for (const clause of clauses) {
1544
1927
  if (clause.type === "crossJoin") {
1545
1928
  if (filterBatch.length > 0) {
1546
- rel = filterRecords(rel, filterBatch);
1929
+ rel = filterRecords(rel, filterBatch, decodeForFns);
1547
1930
  filterBatch = [];
1548
1931
  }
1549
1932
  const rightSource = joinContext.resolveSource(clause.target);
@@ -1556,7 +1939,7 @@ function executeClausePipeline(source, clauses, joinContext) {
1556
1939
  }
1557
1940
  }
1558
1941
  if (filterBatch.length > 0) {
1559
- rel = filterRecords(rel, filterBatch);
1942
+ rel = filterRecords(rel, filterBatch, decodeForFns);
1560
1943
  }
1561
1944
  return rel;
1562
1945
  }
@@ -2351,7 +2734,8 @@ var ScanBuilder = class _ScanBuilder {
2351
2734
  * evaluates clauses per record in O(1) per clause.
2352
2735
  */
2353
2736
  where(field, op, value) {
2354
- const clause = { type: "field", field, op, value };
2737
+ const desc = this.moneyFields?.[field];
2738
+ const clause = desc ? moneyFieldClause(field, op, value, desc) : { type: "field", field, op, value };
2355
2739
  return new _ScanBuilder(
2356
2740
  this.pageProvider,
2357
2741
  this.pageSize,
@@ -2697,8 +3081,9 @@ var ScanBuilder = class _ScanBuilder {
2697
3081
  */
2698
3082
  recordMatches(record) {
2699
3083
  if (this.clauses.length === 0) return true;
3084
+ const fnView = this.moneyFields && Object.keys(this.moneyFields).length > 0 && hasFnClause(this.clauses) ? this.decodeMoney(record) : void 0;
2700
3085
  for (const clause of this.clauses) {
2701
- if (!evaluateClause(record, clause)) return false;
3086
+ if (!evaluateClause(record, clause, fnView)) return false;
2702
3087
  }
2703
3088
  return true;
2704
3089
  }