@noy-db/hub 0.7.1-pre.0 → 0.8.0-pre.0

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 (610) hide show
  1. package/CHANGELOG.md +97 -14
  2. package/LICENSE +202 -21
  3. package/NOTICE +1 -0
  4. package/README.md +5 -5
  5. package/codemods/0.8.0-pre.json +61 -0
  6. package/dist/api-GVYEKSJE.js +19 -0
  7. package/dist/as/index.js +11 -11
  8. package/dist/at/index.js +9 -9
  9. package/dist/attestation/index.js +20 -20
  10. package/dist/{backup-XKCXVKV3.js → backup-7TFRVFLH.js} +12 -12
  11. package/dist/blobs/index.js +13 -13
  12. package/dist/broker/index.js +12 -12
  13. package/dist/by/index.js +2 -2
  14. package/dist/cargo/index.js +39 -36
  15. package/dist/{chunk-7O3Y4PPD.js → chunk-244VXYPG.js} +140 -16
  16. package/dist/chunk-244VXYPG.js.map +1 -0
  17. package/dist/{chunk-BUE2D7PX.js → chunk-32YV3TJT.js} +2 -2
  18. package/dist/{chunk-3PGS7TYR.js → chunk-3ELRMVJK.js} +12 -12
  19. package/dist/{chunk-OTQZ7ZWU.js → chunk-3F7GLNAE.js} +6 -6
  20. package/dist/{chunk-SGZAIJCV.js → chunk-3H5KLDZN.js} +4 -4
  21. package/dist/{chunk-VTFVLCUH.js → chunk-3JWHAR6E.js} +2 -2
  22. package/dist/{chunk-YMUFFJCO.js → chunk-3TDUWVWB.js} +5 -2
  23. package/dist/chunk-3TDUWVWB.js.map +1 -0
  24. package/dist/{chunk-JBDFMYKQ.js → chunk-4AF3TUMP.js} +2 -2
  25. package/dist/{chunk-ZORTV6NH.js → chunk-4I522ERM.js} +88 -4
  26. package/dist/chunk-4I522ERM.js.map +1 -0
  27. package/dist/{chunk-A66I4JOA.js → chunk-4MXI3ODD.js} +4 -4
  28. package/dist/chunk-4MXI3ODD.js.map +1 -0
  29. package/dist/{chunk-BLN54W3L.js → chunk-5FDLC6G7.js} +56 -3
  30. package/dist/chunk-5FDLC6G7.js.map +1 -0
  31. package/dist/chunk-5O7D6ZTW.js +53 -0
  32. package/dist/chunk-5O7D6ZTW.js.map +1 -0
  33. package/dist/{chunk-DFMWUOU5.js → chunk-6B3VNR4P.js} +12 -12
  34. package/dist/{chunk-TUFM4UZP.js → chunk-6K7DPM7D.js} +2 -2
  35. package/dist/{chunk-3VF3WQ4F.js → chunk-6LMQ5R26.js} +7 -7
  36. package/dist/{chunk-UP4BECPY.js → chunk-6LNL42F5.js} +94 -32
  37. package/dist/chunk-6LNL42F5.js.map +1 -0
  38. package/dist/{chunk-XLMEZIAJ.js → chunk-6OWXYZK2.js} +2 -2
  39. package/dist/{chunk-BRLKZUBT.js → chunk-6QSQQLSE.js} +3 -3
  40. package/dist/chunk-7JE7BDYJ.js +1558 -0
  41. package/dist/chunk-7JE7BDYJ.js.map +1 -0
  42. package/dist/{chunk-XKWGNJ26.js → chunk-7VCXZDA4.js} +5 -5
  43. package/dist/chunk-7VCXZDA4.js.map +1 -0
  44. package/dist/{chunk-NQKIUNW5.js → chunk-A6IJU4TK.js} +2 -2
  45. package/dist/{chunk-HIWCKBEY.js → chunk-ANWPSTZU.js} +38 -9
  46. package/dist/chunk-ANWPSTZU.js.map +1 -0
  47. package/dist/{chunk-QIZPZMUI.js → chunk-AOWGXU6D.js} +2 -2
  48. package/dist/{chunk-CUZLNGD5.js → chunk-B2YY6WDD.js} +1 -1
  49. package/dist/chunk-B2YY6WDD.js.map +1 -0
  50. package/dist/{chunk-SFAWCTEM.js → chunk-B57AFBY2.js} +2 -2
  51. package/dist/{chunk-UHHPLO3I.js → chunk-B6SJIBQK.js} +4 -4
  52. package/dist/chunk-BJ2IXQ5Q.js +100 -0
  53. package/dist/chunk-BJ2IXQ5Q.js.map +1 -0
  54. package/dist/{chunk-Q2PU57HC.js → chunk-BK2HHQGN.js} +2 -2
  55. package/dist/chunk-BKT776R5.js +1 -0
  56. package/dist/{chunk-FET5XCAT.js → chunk-BP6DARYA.js} +33 -5
  57. package/dist/chunk-BP6DARYA.js.map +1 -0
  58. package/dist/{chunk-YJX7EOJD.js → chunk-BUDAGWL5.js} +6 -6
  59. package/dist/chunk-CF5UU4S7.js +757 -0
  60. package/dist/chunk-CF5UU4S7.js.map +1 -0
  61. package/dist/{chunk-QPQUVOZ2.js → chunk-CKNGS5MH.js} +3 -3
  62. package/dist/chunk-CY2CYVWZ.js +41 -0
  63. package/dist/chunk-CY2CYVWZ.js.map +1 -0
  64. package/dist/{chunk-2577DKXB.js → chunk-DC5QVPV7.js} +2 -2
  65. package/dist/chunk-DC5QVPV7.js.map +1 -0
  66. package/dist/{chunk-BTDR2PXY.js → chunk-DE4DH2RZ.js} +2 -2
  67. package/dist/{chunk-Q2MPFCQ7.js → chunk-DF73H4UN.js} +4 -4
  68. package/dist/{chunk-4GVM6QZA.js → chunk-ENAG3IY7.js} +2 -2
  69. package/dist/chunk-ENAG3IY7.js.map +1 -0
  70. package/dist/{chunk-6LKOJBV6.js → chunk-EOVMT6TB.js} +2 -2
  71. package/dist/chunk-F24ZZTHQ.js +193 -0
  72. package/dist/chunk-F24ZZTHQ.js.map +1 -0
  73. package/dist/chunk-F2TDU7MQ.js +456 -0
  74. package/dist/chunk-F2TDU7MQ.js.map +1 -0
  75. package/dist/chunk-F6NCB7KT.js +13 -0
  76. package/dist/chunk-F6NCB7KT.js.map +1 -0
  77. package/dist/{chunk-SPYWXKJN.js → chunk-F7XGHL5O.js} +3 -3
  78. package/dist/chunk-F7XGHL5O.js.map +1 -0
  79. package/dist/{chunk-R42NSZI6.js → chunk-FCKWQE2H.js} +5 -5
  80. package/dist/{chunk-R7BX2NB3.js → chunk-FH2L2E34.js} +4 -4
  81. package/dist/chunk-FH2L2E34.js.map +1 -0
  82. package/dist/{chunk-4OZNXTO4.js → chunk-FN5EBVTQ.js} +2 -2
  83. package/dist/chunk-FQ54DWUQ.js +270 -0
  84. package/dist/chunk-FQ54DWUQ.js.map +1 -0
  85. package/dist/{chunk-P3MG6QCN.js → chunk-FQ5VRHIB.js} +4 -4
  86. package/dist/{chunk-FAHMLERK.js → chunk-FW22ZCXT.js} +2 -2
  87. package/dist/chunk-FXQBGFOX.js +524 -0
  88. package/dist/chunk-FXQBGFOX.js.map +1 -0
  89. package/dist/{chunk-7APJ6CO6.js → chunk-GF5BZ7D4.js} +45 -9
  90. package/dist/chunk-GF5BZ7D4.js.map +1 -0
  91. package/dist/{chunk-TRGTKX5E.js → chunk-GHLTDZRS.js} +5 -3
  92. package/dist/{chunk-TRGTKX5E.js.map → chunk-GHLTDZRS.js.map} +1 -1
  93. package/dist/{chunk-BVN6KXL2.js → chunk-HBPPGWUM.js} +5 -5
  94. package/dist/chunk-HBPPGWUM.js.map +1 -0
  95. package/dist/{chunk-KTFRCC5C.js → chunk-HBRLNOHL.js} +82 -8
  96. package/dist/chunk-HBRLNOHL.js.map +1 -0
  97. package/dist/chunk-HLEK5VAN.js +275 -0
  98. package/dist/chunk-HLEK5VAN.js.map +1 -0
  99. package/dist/{chunk-RMS3NEA3.js → chunk-HT2AHGQI.js} +2 -2
  100. package/dist/{chunk-2N5ML6HT.js → chunk-HVSCEIYL.js} +7 -7
  101. package/dist/{chunk-LH5A3ZPG.js → chunk-I3JDRCMW.js} +150 -22
  102. package/dist/chunk-I3JDRCMW.js.map +1 -0
  103. package/dist/{chunk-GY4WYNNG.js → chunk-ID6Z6E66.js} +2 -2
  104. package/dist/{chunk-YJRT5UQM.js → chunk-IEX7NK2K.js} +3 -3
  105. package/dist/{chunk-334WSVOW.js → chunk-ISZQJBML.js} +5 -5
  106. package/dist/{chunk-LWBCUYZW.js → chunk-IT22PWKV.js} +2 -2
  107. package/dist/{chunk-EQEYOJBM.js → chunk-IUWCVHQ7.js} +3 -3
  108. package/dist/chunk-IUWCVHQ7.js.map +1 -0
  109. package/dist/{chunk-RF6D3Y5E.js → chunk-J6UOHJY5.js} +4 -4
  110. package/dist/{chunk-JMGDHKQF.js → chunk-JCN44NCY.js} +2 -2
  111. package/dist/{chunk-3BZZJJMQ.js → chunk-K6W7BVZ6.js} +3 -3
  112. package/dist/{chunk-4ZS56W5S.js → chunk-KJFW62P4.js} +29 -11
  113. package/dist/chunk-KJFW62P4.js.map +1 -0
  114. package/dist/{chunk-453F654R.js → chunk-L7DOS3TE.js} +2 -2
  115. package/dist/chunk-LGNDFRYC.js +23 -0
  116. package/dist/chunk-LGNDFRYC.js.map +1 -0
  117. package/dist/{chunk-QTQUH4DS.js → chunk-LGRLME4M.js} +7 -7
  118. package/dist/{chunk-CHQCZZWC.js → chunk-LQ4ZHOKP.js} +2 -2
  119. package/dist/{chunk-GPNTB4Y7.js → chunk-M522UYZ2.js} +5 -5
  120. package/dist/{chunk-3V4KWFX7.js → chunk-M7NI3IWE.js} +45 -12
  121. package/dist/chunk-M7NI3IWE.js.map +1 -0
  122. package/dist/chunk-MDWKM2XC.js +293 -0
  123. package/dist/chunk-MDWKM2XC.js.map +1 -0
  124. package/dist/chunk-MKXXMNJH.js +204 -0
  125. package/dist/chunk-MKXXMNJH.js.map +1 -0
  126. package/dist/{chunk-SKGMN35F.js → chunk-MLPHLJIQ.js} +3 -3
  127. package/dist/chunk-MLPHLJIQ.js.map +1 -0
  128. package/dist/{chunk-UDHWDSRL.js → chunk-MTL7MXGR.js} +6 -6
  129. package/dist/{chunk-TFR7DSOV.js → chunk-MVZ2TPVP.js} +2 -2
  130. package/dist/{chunk-K23VKTN4.js → chunk-N5DNADLQ.js} +7 -7
  131. package/dist/{chunk-RSGLUIO6.js → chunk-NNARWO72.js} +6 -6
  132. package/dist/{chunk-2ENE6445.js → chunk-NRT6QZQB.js} +102 -16
  133. package/dist/chunk-NRT6QZQB.js.map +1 -0
  134. package/dist/{chunk-PPTHY5QO.js → chunk-NWHBJQAL.js} +99 -8
  135. package/dist/chunk-NWHBJQAL.js.map +1 -0
  136. package/dist/chunk-NYMLINRP.js +28 -0
  137. package/dist/chunk-NYMLINRP.js.map +1 -0
  138. package/dist/{chunk-7MG7SJT7.js → chunk-ODVPTDPK.js} +1 -1
  139. package/dist/chunk-ODVPTDPK.js.map +1 -0
  140. package/dist/chunk-ORDURZBU.js +461 -0
  141. package/dist/chunk-ORDURZBU.js.map +1 -0
  142. package/dist/{chunk-6YXCPL4T.js → chunk-PDCG6PR5.js} +2 -2
  143. package/dist/{chunk-ACAQ2DSG.js → chunk-PSIV7CJV.js} +2 -2
  144. package/dist/{chunk-KKEESNPE.js → chunk-QBBUZ3I3.js} +126 -19
  145. package/dist/chunk-QBBUZ3I3.js.map +1 -0
  146. package/dist/{chunk-5VCRVKOZ.js → chunk-QOOUUR4A.js} +6 -4
  147. package/dist/{chunk-5VCRVKOZ.js.map → chunk-QOOUUR4A.js.map} +1 -1
  148. package/dist/{chunk-G73UVHCA.js → chunk-QWFHIOD2.js} +5 -5
  149. package/dist/{chunk-AYX7AINO.js → chunk-QWGU66MP.js} +3 -3
  150. package/dist/chunk-RHVMZSUU.js +7 -0
  151. package/dist/chunk-RHVMZSUU.js.map +1 -0
  152. package/dist/{chunk-3BVH633C.js → chunk-S57HTZ6X.js} +5 -5
  153. package/dist/{chunk-SZJ3DLZA.js → chunk-SEE332EV.js} +2 -2
  154. package/dist/{chunk-YYP7WSEB.js → chunk-SI3V7D4G.js} +2 -2
  155. package/dist/{chunk-UGTBXF5U.js → chunk-SOIQ4MM4.js} +3 -3
  156. package/dist/chunk-SUZKW6UR.js +25 -0
  157. package/dist/chunk-SUZKW6UR.js.map +1 -0
  158. package/dist/{chunk-4GFX6MPA.js → chunk-SYVZW77S.js} +6 -6
  159. package/dist/{chunk-ZYIYHJ5R.js → chunk-T2LBQ5DV.js} +815 -222
  160. package/dist/chunk-T2LBQ5DV.js.map +1 -0
  161. package/dist/chunk-T52GX4SA.js +215 -0
  162. package/dist/chunk-T52GX4SA.js.map +1 -0
  163. package/dist/{chunk-MVMXZYXT.js → chunk-TKUOEU2F.js} +2 -2
  164. package/dist/chunk-TOPYQ6GA.js +31 -0
  165. package/dist/chunk-TOPYQ6GA.js.map +1 -0
  166. package/dist/{chunk-SMH6SRX7.js → chunk-TROR3ALR.js} +2 -2
  167. package/dist/{chunk-CUQPWYRX.js → chunk-UD5EVGZ7.js} +42 -389
  168. package/dist/chunk-UD5EVGZ7.js.map +1 -0
  169. package/dist/{chunk-M4YPHYEH.js → chunk-UPD5NWI5.js} +65 -17
  170. package/dist/chunk-UPD5NWI5.js.map +1 -0
  171. package/dist/{chunk-HPVZCW43.js → chunk-UYB234ZR.js} +2 -2
  172. package/dist/chunk-VHM7HCKB.js +220 -0
  173. package/dist/chunk-VHM7HCKB.js.map +1 -0
  174. package/dist/{chunk-DGOZ4UZD.js → chunk-VNNRUGMM.js} +2 -2
  175. package/dist/{chunk-F3W6YMDC.js → chunk-VNPEWFPD.js} +4 -4
  176. package/dist/{chunk-JVIBCBC7.js → chunk-WNJ4AVGE.js} +2 -2
  177. package/dist/{chunk-ZHKOPKE3.js → chunk-WX5ADGTZ.js} +3 -3
  178. package/dist/{chunk-VXANTWBJ.js → chunk-WY2XXTET.js} +92 -6
  179. package/dist/{chunk-VXANTWBJ.js.map → chunk-WY2XXTET.js.map} +1 -1
  180. package/dist/{chunk-HJUWN7JN.js → chunk-X25NWUZB.js} +33 -8
  181. package/dist/chunk-X25NWUZB.js.map +1 -0
  182. package/dist/{chunk-Q7HZX3IF.js → chunk-X4773RBR.js} +2 -2
  183. package/dist/chunk-XBKH4FOC.js +195 -0
  184. package/dist/chunk-XBKH4FOC.js.map +1 -0
  185. package/dist/{chunk-A2HKRLPV.js → chunk-YNIUIERI.js} +3 -3
  186. package/dist/chunk-YSK2CU2Q.js +936 -0
  187. package/dist/chunk-YSK2CU2Q.js.map +1 -0
  188. package/dist/{chunk-UXVWEX5L.js → chunk-Z5HTYMMQ.js} +6 -2
  189. package/dist/chunk-Z5HTYMMQ.js.map +1 -0
  190. package/dist/{chunk-WQBG4SZS.js → chunk-ZE4CHIED.js} +56 -34
  191. package/dist/chunk-ZE4CHIED.js.map +1 -0
  192. package/dist/chunk-ZSX4S5XB.js +1289 -0
  193. package/dist/chunk-ZSX4S5XB.js.map +1 -0
  194. package/dist/{chunk-BZ3534YM.js → chunk-ZUVBC32X.js} +2 -2
  195. package/dist/{chunk-V7ZRRCRJ.js → chunk-ZXABPVM6.js} +6 -6
  196. package/dist/classified/index.js +5 -5
  197. package/dist/{classified-marker-BWOFVYAC.js → classified-marker-W3USLL5M.js} +3 -3
  198. package/dist/collection-facade-QU6IJBN3.js +48 -0
  199. package/dist/{computed-VPOKQK4N.js → computed-TPFBX7U4.js} +3 -3
  200. package/dist/consent/index.js +9 -9
  201. package/dist/cover/index.js +10 -10
  202. package/dist/coverage/index.js +306 -0
  203. package/dist/coverage/index.js.map +1 -0
  204. package/dist/custody/index.js +12 -12
  205. package/dist/{dead-filter-PKQDLOT4.js → dead-filter-U2UYKSZL.js} +2 -2
  206. package/dist/debug/index.js +1 -1
  207. package/dist/{delegation-DS5NRILW.js → delegation-S3AEPW2T.js} +11 -11
  208. package/dist/derivations/index.js +25 -22
  209. package/dist/derive-OX7F6TGG.js +21 -0
  210. package/dist/directory/index.js +10 -10
  211. package/dist/{dispatch-IEPAVQUM.js → dispatch-3CQDJV5Q.js} +5 -5
  212. package/dist/{dispatch-Q6MWLOJY.js → dispatch-RH4LHKBH.js} +21 -21
  213. package/dist/{enclave-55PAM4YO.js → enclave-27SMF6GF.js} +9 -9
  214. package/dist/executor-B6SRO2QS.js +36 -0
  215. package/dist/executor-BI5CHWRY.js +8 -0
  216. package/dist/executor-RBKM5W5K.js +8 -0
  217. package/dist/export-accessible-VMA3YM5V.js +22 -0
  218. package/dist/extract-partition-NF7PXGPP.js +37 -0
  219. package/dist/{fanout-sidecar-S3QL53YX.js → fanout-sidecar-OT6RYLOL.js} +9 -9
  220. package/dist/find-LZMP26KF.js +10 -0
  221. package/dist/forget/index.js +9 -9
  222. package/dist/guards/index.js +4 -4
  223. package/dist/history/index.js +13 -13
  224. package/dist/i18n/index.js +14 -14
  225. package/dist/index.d.ts +28 -6
  226. package/dist/index.js +999 -191
  227. package/dist/index.js.map +1 -1
  228. package/dist/indexing/index.js +13 -5
  229. package/dist/indexing/index.js.map +1 -1
  230. package/dist/introspection/index.js +11 -11
  231. package/dist/issue-IHARXWW5.js +18 -0
  232. package/dist/ivf-flat-UZ6DQDLS.js +314 -0
  233. package/dist/ivf-flat-UZ6DQDLS.js.map +1 -0
  234. package/dist/{json-schema-V3M5MUCM.js → json-schema-3SHX72CC.js} +2 -1
  235. package/dist/json-schema-3SHX72CC.js.map +1 -0
  236. package/dist/kernel/collection-config.d.ts +4 -20
  237. package/dist/kernel/collection.d.ts +29 -8
  238. package/dist/kernel/enclave/record-keys/record-codec.d.ts +25 -0
  239. package/dist/kernel/errors.d.ts +227 -36
  240. package/dist/kernel/generation-map.d.ts +48 -0
  241. package/dist/kernel/noydb.d.ts +6 -1
  242. package/dist/kernel/present-cache.d.ts +58 -0
  243. package/dist/kernel/query/all.d.ts +7 -0
  244. package/dist/kernel/query/builder.d.ts +601 -270
  245. package/dist/kernel/query/civil-date.d.ts +64 -0
  246. package/dist/kernel/query/cursor.d.ts +40 -0
  247. package/dist/kernel/query/distinct-key.d.ts +98 -0
  248. package/dist/kernel/query/hydration.d.ts +76 -0
  249. package/dist/kernel/query/index.d.ts +31 -15
  250. package/dist/kernel/query/internal/core.d.ts +30 -0
  251. package/dist/kernel/query/internal/hooks.d.ts +22 -0
  252. package/dist/kernel/query/internal/maintenance.d.ts +43 -0
  253. package/dist/kernel/query/live/incremental.d.ts +314 -0
  254. package/dist/kernel/query/live/index.d.ts +22 -0
  255. package/dist/kernel/query/live/install.d.ts +2 -0
  256. package/dist/kernel/query/{live.d.ts → live/live.d.ts} +39 -9
  257. package/dist/kernel/query/live/methods.d.ts +107 -0
  258. package/dist/kernel/query/predicate.d.ts +119 -1
  259. package/dist/kernel/query/reduce/date-trunc.d.ts +75 -0
  260. package/dist/kernel/query/reduce/index.d.ts +36 -0
  261. package/dist/kernel/query/reduce/install.d.ts +2 -0
  262. package/dist/kernel/query/reduce/methods.d.ts +259 -0
  263. package/dist/kernel/query/reduce/scan-methods.d.ts +121 -0
  264. package/dist/kernel/query/relate/explain.d.ts +199 -0
  265. package/dist/kernel/query/relate/index.d.ts +48 -0
  266. package/dist/kernel/query/relate/install.d.ts +2 -0
  267. package/dist/kernel/query/relate/join-on.d.ts +126 -0
  268. package/dist/kernel/query/relate/join-reduce.d.ts +16 -0
  269. package/dist/kernel/query/relate/join.d.ts +452 -0
  270. package/dist/kernel/query/relate/methods.d.ts +426 -0
  271. package/dist/kernel/query/relate/partition.d.ts +87 -0
  272. package/dist/kernel/query/relate/scan-methods.d.ts +163 -0
  273. package/dist/kernel/query/relate/traverse.d.ts +109 -0
  274. package/dist/kernel/query/scan-builder.d.ts +79 -176
  275. package/dist/kernel/query/sort-key.d.ts +52 -0
  276. package/dist/kernel/tx-write-gate.d.ts +77 -0
  277. package/dist/kernel/types.d.ts +71 -3
  278. package/dist/kernel/validation.d.ts +27 -10
  279. package/dist/kernel/vault.d.ts +9 -4
  280. package/dist/kernel/via/compose.d.ts +3 -0
  281. package/dist/kernel/via/index.d.ts +56 -0
  282. package/dist/kernel/via/pipeline.d.ts +48 -0
  283. package/dist/{ledger-EDXFFZQL.js → ledger-KZPCMVZY.js} +11 -11
  284. package/dist/liberate-GRLCZMW4.js +24 -0
  285. package/dist/link-set-JCMJ7ULP.js +30 -0
  286. package/dist/{managed-secret-NCWEMPUQ.js → managed-secret-S3MQ6SH6.js} +10 -10
  287. package/dist/materialized-views/index.js +30 -23
  288. package/dist/{migrate-cek-5ENH64AA.js → migrate-cek-V7GKJZ7O.js} +2 -2
  289. package/dist/money/index.js +5 -5
  290. package/dist/noydb-SWQJ3MZS.js +97 -0
  291. package/dist/overlay-views/index.js +4 -4
  292. package/dist/periods/index.js +12 -12
  293. package/dist/pod/index.js +75 -68
  294. package/dist/{pod-handle-ECVCDIXW.js → pod-handle-WGDFB4C2.js} +9 -9
  295. package/dist/policy/index.js +10 -10
  296. package/dist/port/with/blob-strategy.d.ts +9 -0
  297. package/dist/port/with/collection-options.d.ts +5 -0
  298. package/dist/port/with/coverage-strategy.d.ts +83 -0
  299. package/dist/port/with/service-bus.d.ts +46 -0
  300. package/dist/port/with/strategies.d.ts +2 -0
  301. package/dist/portability/index.js +8 -8
  302. package/dist/{post-register-3IPCSG6Q.js → post-register-SLSXIPBV.js} +6 -6
  303. package/dist/query/all/index.js +31 -0
  304. package/dist/query/index.js +20 -32
  305. package/dist/query/live/index.js +21 -0
  306. package/dist/query/live/index.js.map +1 -0
  307. package/dist/query/reduce/index.js +33 -0
  308. package/dist/query/reduce/index.js.map +1 -0
  309. package/dist/query/relate/index.js +56 -0
  310. package/dist/query/relate/index.js.map +1 -0
  311. package/dist/reduce/index.js +72 -18
  312. package/dist/reduce/index.js.map +1 -1
  313. package/dist/register-F3OYE7MJ.js +31 -0
  314. package/dist/registry-CILQFXFK.js +8 -0
  315. package/dist/registry-XNNYQVKV.js +8 -0
  316. package/dist/registry-YCDZV3VW.js +35 -0
  317. package/dist/registry-ZGYS33TE.js +24 -0
  318. package/dist/request-withdrawal-Z5NC6TXT.js +30 -0
  319. package/dist/{reveal-QZDHSGD4.js → reveal-4BCKPEC2.js} +6 -6
  320. package/dist/revoke-ZL62WC2D.js +23 -0
  321. package/dist/satellites/index.js +1 -1
  322. package/dist/schema-update/index.js +2 -2
  323. package/dist/sealed-record/index.js +12 -12
  324. package/dist/search/index.js +11 -5
  325. package/dist/{seed-X5CC22CQ.js → seed-4JS2RJ4A.js} +12 -12
  326. package/dist/sequence/index.js +9 -9
  327. package/dist/session/index.js +9 -9
  328. package/dist/shadow/index.js +2 -2
  329. package/dist/signer-MBZZMXTC.js +24 -0
  330. package/dist/snapshots/index.js +10 -10
  331. package/dist/{stale-BZS6H2RY.js → stale-TJ3DYZMQ.js} +10 -10
  332. package/dist/storage-SVMKRGCW.js +22 -0
  333. package/dist/{store-coordination-provider-4WZAIRHC.js → store-coordination-provider-2O2VUHKW.js} +10 -10
  334. package/dist/sync/index.js +10 -10
  335. package/dist/team/index.js +17 -15
  336. package/dist/tiers/index.js +13 -11
  337. package/dist/to/index.js +2 -2
  338. package/dist/transactions/index.js +3 -3
  339. package/dist/util/index.js +1 -1
  340. package/dist/vault-head/index.js +8 -8
  341. package/dist/{verify-6MDTIUEV.js → verify-RVT67IMH.js} +5 -5
  342. package/dist/via/blob/index.d.ts +1 -0
  343. package/dist/via/geo/binding.d.ts +31 -0
  344. package/dist/via/geo/descriptor.d.ts +54 -0
  345. package/dist/via/geo/geohash.d.ts +95 -0
  346. package/dist/via/geo/index.d.ts +13 -0
  347. package/dist/via/geo/where.d.ts +50 -0
  348. package/dist/via/i18n/core.d.ts +1 -1
  349. package/dist/via/lookup/registry.d.ts +1 -1
  350. package/dist/via/lookup/snapshot.d.ts +3 -3
  351. package/dist/walk-HJ42EISE.js +21 -0
  352. package/dist/walk-HJ42EISE.js.map +1 -0
  353. package/dist/with-audit/coverage/accounting.d.ts +112 -0
  354. package/dist/with-audit/coverage/active.d.ts +29 -0
  355. package/dist/with-audit/coverage/index.d.ts +61 -0
  356. package/dist/with-audit/coverage/sketch.d.ts +75 -0
  357. package/dist/with-audit/periods/periods.d.ts +55 -7
  358. package/dist/with-audit/periods/reserved-collections.d.ts +3 -0
  359. package/dist/with-audit/periods/vault-facade.d.ts +17 -0
  360. package/dist/with-audit/tiers/index.d.ts +51 -0
  361. package/dist/with-audit/tiers/strategy.d.ts +8 -0
  362. package/dist/with-cargo/floor.d.ts +2 -2
  363. package/dist/with-commit/tx/invariants.d.ts +1 -1
  364. package/dist/with-commit/tx/transaction.d.ts +13 -3
  365. package/dist/with-formula/materialized-views/dependency-analyzer.d.ts +4 -3
  366. package/dist/with-formula/materialized-views/emit-memo.d.ts +40 -0
  367. package/dist/with-formula/materialized-views/executor.d.ts +8 -0
  368. package/dist/with-formula/materialized-views/index.d.ts +1 -1
  369. package/dist/with-formula/materialized-views/registry.d.ts +15 -0
  370. package/dist/with-formula/materialized-views/types.d.ts +106 -14
  371. package/dist/with-formula/materialized-views/window-stage.d.ts +11 -0
  372. package/dist/with-formula/overlay-views/types.d.ts +1 -1
  373. package/dist/with-formula/overlay-views/virtual-collection.d.ts +13 -4
  374. package/dist/with-lookup/embeddings/chunks.d.ts +79 -0
  375. package/dist/with-lookup/embeddings/descriptor.d.ts +21 -0
  376. package/dist/with-lookup/embeddings/index.d.ts +3 -0
  377. package/dist/with-lookup/embeddings/ivf-flat.d.ts +93 -0
  378. package/dist/with-lookup/embeddings/vector-index.d.ts +73 -0
  379. package/dist/with-lookup/embeddings/vector-set.d.ts +94 -1
  380. package/dist/with-lookup/embeddings/with-vector-index.d.ts +67 -0
  381. package/dist/with-lookup/indexing/collection-facade.d.ts +85 -2
  382. package/dist/with-lookup/indexing/compound-indexes.d.ts +129 -0
  383. package/dist/with-lookup/indexing/eager-indexes.d.ts +170 -1
  384. package/dist/with-lookup/indexing/index-snapshot.d.ts +62 -0
  385. package/dist/with-lookup/indexing/lazy-builder.d.ts +33 -0
  386. package/dist/with-lookup/indexing/persisted-field-indexes.d.ts +104 -0
  387. package/dist/with-lookup/indexing/persisted-indexes.d.ts +12 -0
  388. package/dist/with-lookup/indexing/sorted-indexes.d.ts +117 -0
  389. package/dist/with-lookup/indexing/unique-constraints.d.ts +106 -23
  390. package/dist/with-lookup/reduce/active.d.ts +2 -24
  391. package/dist/with-lookup/reduce/group-core.d.ts +43 -0
  392. package/dist/with-lookup/reduce/groupby.d.ts +195 -36
  393. package/dist/with-lookup/reduce/incremental-group.d.ts +153 -0
  394. package/dist/with-lookup/reduce/index.d.ts +26 -5
  395. package/dist/with-lookup/reduce/reducers.d.ts +255 -1
  396. package/dist/with-lookup/reduce/reduction.d.ts +44 -13
  397. package/dist/with-lookup/reduce/strategy.d.ts +43 -3
  398. package/dist/with-lookup/reduce/window.d.ts +262 -0
  399. package/dist/with-lookup/search/collection-facade.d.ts +44 -8
  400. package/dist/with-lookup/search/index-store.d.ts +5 -3
  401. package/dist/with-lookup/search/index.d.ts +4 -2
  402. package/dist/with-lookup/search/inverted-index.d.ts +38 -2
  403. package/dist/with-lookup/search/persisted-index-store.d.ts +5 -2
  404. package/dist/with-lookup/search/phrase.d.ts +28 -0
  405. package/dist/with-lookup/search/retrieve-types.d.ts +57 -1
  406. package/dist/with-lookup/search/serialize.d.ts +29 -3
  407. package/dist/with-lookup/search/strategy.d.ts +2 -6
  408. package/dist/with-party/directory/cover/index.d.ts +1 -1
  409. package/dist/with-party/directory/cover/storage.d.ts +1 -1
  410. package/dist/with-party/directory/cover/types.d.ts +1 -1
  411. package/dist/with-party/directory/index.d.ts +1 -1
  412. package/dist/with-party/directory/types.d.ts +1 -1
  413. package/dist/with-party/directory/user-envelope/api.d.ts +1 -1
  414. package/dist/with-party/policy/engine.d.ts +1 -1
  415. package/dist/with-party/policy/index.d.ts +1 -1
  416. package/dist/with-party/policy/presets.d.ts +1 -1
  417. package/dist/with-party/team/authenticators.d.ts +91 -1
  418. package/dist/with-party/team/index.d.ts +3 -2
  419. package/dist/with-party/team/keyring.d.ts +1 -1
  420. package/dist/with-party/team/managed-secret.d.ts +1 -1
  421. package/dist/with-party/team/noydb-facade.d.ts +12 -3
  422. package/dist/with-pod/format.d.ts +1 -1
  423. package/dist/with-pod/pod.d.ts +1 -1
  424. package/dist/with-shape/blobs/blob-compaction.d.ts +16 -0
  425. package/dist/with-shape/blobs/blob-set.d.ts +21 -2
  426. package/dist/with-shape/blobs/legacy-sweep.d.ts +35 -0
  427. package/dist/with-shape/introspection/describe.d.ts +6 -0
  428. package/dist/with-shape/introspection/field-meta.d.ts +18 -0
  429. package/dist/with-shape/introspection/types.d.ts +57 -1
  430. package/dist/with-shape/links/vault-facade.d.ts +1 -1
  431. package/dist/with-store/partitioned/index.d.ts +337 -0
  432. package/dist/with-sync/change-broadcast.d.ts +153 -0
  433. package/dist/withdraw-accessible-5NZUPAVA.js +27 -0
  434. package/dist/withdraw-accessible-5NZUPAVA.js.map +1 -0
  435. package/package.json +37 -10
  436. package/dist/api-WYW4YEQG.js +0 -19
  437. package/dist/chunk-2577DKXB.js.map +0 -1
  438. package/dist/chunk-2ENE6445.js.map +0 -1
  439. package/dist/chunk-3V4KWFX7.js.map +0 -1
  440. package/dist/chunk-4GVM6QZA.js.map +0 -1
  441. package/dist/chunk-4ZS56W5S.js.map +0 -1
  442. package/dist/chunk-7APJ6CO6.js.map +0 -1
  443. package/dist/chunk-7MG2LHO5.js +0 -1616
  444. package/dist/chunk-7MG2LHO5.js.map +0 -1
  445. package/dist/chunk-7MG7SJT7.js.map +0 -1
  446. package/dist/chunk-7O3Y4PPD.js.map +0 -1
  447. package/dist/chunk-A66I4JOA.js.map +0 -1
  448. package/dist/chunk-ATL3ZVXT.js +0 -151
  449. package/dist/chunk-ATL3ZVXT.js.map +0 -1
  450. package/dist/chunk-BLN54W3L.js.map +0 -1
  451. package/dist/chunk-BVN6KXL2.js.map +0 -1
  452. package/dist/chunk-CUQPWYRX.js.map +0 -1
  453. package/dist/chunk-CUZLNGD5.js.map +0 -1
  454. package/dist/chunk-EQEYOJBM.js.map +0 -1
  455. package/dist/chunk-FET5XCAT.js.map +0 -1
  456. package/dist/chunk-HIWCKBEY.js.map +0 -1
  457. package/dist/chunk-HJUWN7JN.js.map +0 -1
  458. package/dist/chunk-I3ISCYT7.js +0 -159
  459. package/dist/chunk-I3ISCYT7.js.map +0 -1
  460. package/dist/chunk-KKEESNPE.js.map +0 -1
  461. package/dist/chunk-KTFRCC5C.js.map +0 -1
  462. package/dist/chunk-LH5A3ZPG.js.map +0 -1
  463. package/dist/chunk-M4YPHYEH.js.map +0 -1
  464. package/dist/chunk-NSITFOCJ.js +0 -99
  465. package/dist/chunk-NSITFOCJ.js.map +0 -1
  466. package/dist/chunk-PPTHY5QO.js.map +0 -1
  467. package/dist/chunk-R7BX2NB3.js.map +0 -1
  468. package/dist/chunk-RTBJ5IOO.js +0 -87
  469. package/dist/chunk-RTBJ5IOO.js.map +0 -1
  470. package/dist/chunk-SKGMN35F.js.map +0 -1
  471. package/dist/chunk-SPYWXKJN.js.map +0 -1
  472. package/dist/chunk-UP4BECPY.js.map +0 -1
  473. package/dist/chunk-UXVWEX5L.js.map +0 -1
  474. package/dist/chunk-WQBG4SZS.js.map +0 -1
  475. package/dist/chunk-XKWGNJ26.js.map +0 -1
  476. package/dist/chunk-YMUFFJCO.js.map +0 -1
  477. package/dist/chunk-ZN4FD4PE.js +0 -364
  478. package/dist/chunk-ZN4FD4PE.js.map +0 -1
  479. package/dist/chunk-ZORTV6NH.js.map +0 -1
  480. package/dist/chunk-ZYIYHJ5R.js.map +0 -1
  481. package/dist/collection-facade-JSJYIKS4.js +0 -45
  482. package/dist/derive-E642AOUB.js +0 -21
  483. package/dist/executor-5NIMLSKZ.js +0 -8
  484. package/dist/executor-7UQ3BZ4F.js +0 -29
  485. package/dist/executor-AQVXV5GZ.js +0 -8
  486. package/dist/export-accessible-ORMKLA3E.js +0 -22
  487. package/dist/extract-partition-NKYO3WNG.js +0 -37
  488. package/dist/find-NOGJ25VX.js +0 -10
  489. package/dist/issue-3DTQ7VJJ.js +0 -18
  490. package/dist/json-schema-V3M5MUCM.js.map +0 -1
  491. package/dist/kernel/query/join.d.ts +0 -232
  492. package/dist/liberate-V35QUHJP.js +0 -24
  493. package/dist/link-set-2TTM3IZC.js +0 -30
  494. package/dist/noydb-62MX4QUG.js +0 -90
  495. package/dist/register-HHLXEQDN.js +0 -31
  496. package/dist/registry-23WF3TGH.js +0 -8
  497. package/dist/registry-4AYVUNXK.js +0 -8
  498. package/dist/registry-JS3YFQQM.js +0 -18
  499. package/dist/registry-XX2Z7B46.js +0 -32
  500. package/dist/request-withdrawal-CHPATE4G.js +0 -30
  501. package/dist/revoke-7M2VW4ON.js +0 -23
  502. package/dist/signer-SJQJVSM6.js +0 -24
  503. package/dist/storage-RW4QBS23.js +0 -22
  504. package/dist/walk-ZURCH6RZ.js +0 -21
  505. package/dist/withdraw-accessible-PXR4IXDX.js +0 -27
  506. /package/dist/{api-WYW4YEQG.js.map → api-GVYEKSJE.js.map} +0 -0
  507. /package/dist/{backup-XKCXVKV3.js.map → backup-7TFRVFLH.js.map} +0 -0
  508. /package/dist/{chunk-BUE2D7PX.js.map → chunk-32YV3TJT.js.map} +0 -0
  509. /package/dist/{chunk-3PGS7TYR.js.map → chunk-3ELRMVJK.js.map} +0 -0
  510. /package/dist/{chunk-OTQZ7ZWU.js.map → chunk-3F7GLNAE.js.map} +0 -0
  511. /package/dist/{chunk-SGZAIJCV.js.map → chunk-3H5KLDZN.js.map} +0 -0
  512. /package/dist/{chunk-VTFVLCUH.js.map → chunk-3JWHAR6E.js.map} +0 -0
  513. /package/dist/{chunk-JBDFMYKQ.js.map → chunk-4AF3TUMP.js.map} +0 -0
  514. /package/dist/{chunk-DFMWUOU5.js.map → chunk-6B3VNR4P.js.map} +0 -0
  515. /package/dist/{chunk-TUFM4UZP.js.map → chunk-6K7DPM7D.js.map} +0 -0
  516. /package/dist/{chunk-3VF3WQ4F.js.map → chunk-6LMQ5R26.js.map} +0 -0
  517. /package/dist/{chunk-XLMEZIAJ.js.map → chunk-6OWXYZK2.js.map} +0 -0
  518. /package/dist/{chunk-BRLKZUBT.js.map → chunk-6QSQQLSE.js.map} +0 -0
  519. /package/dist/{chunk-NQKIUNW5.js.map → chunk-A6IJU4TK.js.map} +0 -0
  520. /package/dist/{chunk-QIZPZMUI.js.map → chunk-AOWGXU6D.js.map} +0 -0
  521. /package/dist/{chunk-SFAWCTEM.js.map → chunk-B57AFBY2.js.map} +0 -0
  522. /package/dist/{chunk-UHHPLO3I.js.map → chunk-B6SJIBQK.js.map} +0 -0
  523. /package/dist/{chunk-Q2PU57HC.js.map → chunk-BK2HHQGN.js.map} +0 -0
  524. /package/dist/{collection-facade-JSJYIKS4.js.map → chunk-BKT776R5.js.map} +0 -0
  525. /package/dist/{chunk-YJX7EOJD.js.map → chunk-BUDAGWL5.js.map} +0 -0
  526. /package/dist/{chunk-QPQUVOZ2.js.map → chunk-CKNGS5MH.js.map} +0 -0
  527. /package/dist/{chunk-BTDR2PXY.js.map → chunk-DE4DH2RZ.js.map} +0 -0
  528. /package/dist/{chunk-Q2MPFCQ7.js.map → chunk-DF73H4UN.js.map} +0 -0
  529. /package/dist/{chunk-6LKOJBV6.js.map → chunk-EOVMT6TB.js.map} +0 -0
  530. /package/dist/{chunk-R42NSZI6.js.map → chunk-FCKWQE2H.js.map} +0 -0
  531. /package/dist/{chunk-4OZNXTO4.js.map → chunk-FN5EBVTQ.js.map} +0 -0
  532. /package/dist/{chunk-P3MG6QCN.js.map → chunk-FQ5VRHIB.js.map} +0 -0
  533. /package/dist/{chunk-FAHMLERK.js.map → chunk-FW22ZCXT.js.map} +0 -0
  534. /package/dist/{chunk-RMS3NEA3.js.map → chunk-HT2AHGQI.js.map} +0 -0
  535. /package/dist/{chunk-2N5ML6HT.js.map → chunk-HVSCEIYL.js.map} +0 -0
  536. /package/dist/{chunk-GY4WYNNG.js.map → chunk-ID6Z6E66.js.map} +0 -0
  537. /package/dist/{chunk-YJRT5UQM.js.map → chunk-IEX7NK2K.js.map} +0 -0
  538. /package/dist/{chunk-334WSVOW.js.map → chunk-ISZQJBML.js.map} +0 -0
  539. /package/dist/{chunk-LWBCUYZW.js.map → chunk-IT22PWKV.js.map} +0 -0
  540. /package/dist/{chunk-RF6D3Y5E.js.map → chunk-J6UOHJY5.js.map} +0 -0
  541. /package/dist/{chunk-JMGDHKQF.js.map → chunk-JCN44NCY.js.map} +0 -0
  542. /package/dist/{chunk-3BZZJJMQ.js.map → chunk-K6W7BVZ6.js.map} +0 -0
  543. /package/dist/{chunk-453F654R.js.map → chunk-L7DOS3TE.js.map} +0 -0
  544. /package/dist/{chunk-QTQUH4DS.js.map → chunk-LGRLME4M.js.map} +0 -0
  545. /package/dist/{chunk-CHQCZZWC.js.map → chunk-LQ4ZHOKP.js.map} +0 -0
  546. /package/dist/{chunk-GPNTB4Y7.js.map → chunk-M522UYZ2.js.map} +0 -0
  547. /package/dist/{chunk-UDHWDSRL.js.map → chunk-MTL7MXGR.js.map} +0 -0
  548. /package/dist/{chunk-TFR7DSOV.js.map → chunk-MVZ2TPVP.js.map} +0 -0
  549. /package/dist/{chunk-K23VKTN4.js.map → chunk-N5DNADLQ.js.map} +0 -0
  550. /package/dist/{chunk-RSGLUIO6.js.map → chunk-NNARWO72.js.map} +0 -0
  551. /package/dist/{chunk-6YXCPL4T.js.map → chunk-PDCG6PR5.js.map} +0 -0
  552. /package/dist/{chunk-ACAQ2DSG.js.map → chunk-PSIV7CJV.js.map} +0 -0
  553. /package/dist/{chunk-G73UVHCA.js.map → chunk-QWFHIOD2.js.map} +0 -0
  554. /package/dist/{chunk-AYX7AINO.js.map → chunk-QWGU66MP.js.map} +0 -0
  555. /package/dist/{chunk-3BVH633C.js.map → chunk-S57HTZ6X.js.map} +0 -0
  556. /package/dist/{chunk-SZJ3DLZA.js.map → chunk-SEE332EV.js.map} +0 -0
  557. /package/dist/{chunk-YYP7WSEB.js.map → chunk-SI3V7D4G.js.map} +0 -0
  558. /package/dist/{chunk-UGTBXF5U.js.map → chunk-SOIQ4MM4.js.map} +0 -0
  559. /package/dist/{chunk-4GFX6MPA.js.map → chunk-SYVZW77S.js.map} +0 -0
  560. /package/dist/{chunk-MVMXZYXT.js.map → chunk-TKUOEU2F.js.map} +0 -0
  561. /package/dist/{chunk-SMH6SRX7.js.map → chunk-TROR3ALR.js.map} +0 -0
  562. /package/dist/{chunk-HPVZCW43.js.map → chunk-UYB234ZR.js.map} +0 -0
  563. /package/dist/{chunk-DGOZ4UZD.js.map → chunk-VNNRUGMM.js.map} +0 -0
  564. /package/dist/{chunk-F3W6YMDC.js.map → chunk-VNPEWFPD.js.map} +0 -0
  565. /package/dist/{chunk-JVIBCBC7.js.map → chunk-WNJ4AVGE.js.map} +0 -0
  566. /package/dist/{chunk-ZHKOPKE3.js.map → chunk-WX5ADGTZ.js.map} +0 -0
  567. /package/dist/{chunk-Q7HZX3IF.js.map → chunk-X4773RBR.js.map} +0 -0
  568. /package/dist/{chunk-A2HKRLPV.js.map → chunk-YNIUIERI.js.map} +0 -0
  569. /package/dist/{chunk-BZ3534YM.js.map → chunk-ZUVBC32X.js.map} +0 -0
  570. /package/dist/{chunk-V7ZRRCRJ.js.map → chunk-ZXABPVM6.js.map} +0 -0
  571. /package/dist/{classified-marker-BWOFVYAC.js.map → classified-marker-W3USLL5M.js.map} +0 -0
  572. /package/dist/{computed-VPOKQK4N.js.map → collection-facade-QU6IJBN3.js.map} +0 -0
  573. /package/dist/{derive-E642AOUB.js.map → computed-TPFBX7U4.js.map} +0 -0
  574. /package/dist/{dead-filter-PKQDLOT4.js.map → dead-filter-U2UYKSZL.js.map} +0 -0
  575. /package/dist/{delegation-DS5NRILW.js.map → delegation-S3AEPW2T.js.map} +0 -0
  576. /package/dist/{enclave-55PAM4YO.js.map → derive-OX7F6TGG.js.map} +0 -0
  577. /package/dist/{dispatch-IEPAVQUM.js.map → dispatch-3CQDJV5Q.js.map} +0 -0
  578. /package/dist/{dispatch-Q6MWLOJY.js.map → dispatch-RH4LHKBH.js.map} +0 -0
  579. /package/dist/{executor-5NIMLSKZ.js.map → enclave-27SMF6GF.js.map} +0 -0
  580. /package/dist/{executor-7UQ3BZ4F.js.map → executor-B6SRO2QS.js.map} +0 -0
  581. /package/dist/{executor-AQVXV5GZ.js.map → executor-BI5CHWRY.js.map} +0 -0
  582. /package/dist/{export-accessible-ORMKLA3E.js.map → executor-RBKM5W5K.js.map} +0 -0
  583. /package/dist/{extract-partition-NKYO3WNG.js.map → export-accessible-VMA3YM5V.js.map} +0 -0
  584. /package/dist/{find-NOGJ25VX.js.map → extract-partition-NF7PXGPP.js.map} +0 -0
  585. /package/dist/{fanout-sidecar-S3QL53YX.js.map → fanout-sidecar-OT6RYLOL.js.map} +0 -0
  586. /package/dist/{issue-3DTQ7VJJ.js.map → find-LZMP26KF.js.map} +0 -0
  587. /package/dist/{ledger-EDXFFZQL.js.map → issue-IHARXWW5.js.map} +0 -0
  588. /package/dist/{liberate-V35QUHJP.js.map → ledger-KZPCMVZY.js.map} +0 -0
  589. /package/dist/{link-set-2TTM3IZC.js.map → liberate-GRLCZMW4.js.map} +0 -0
  590. /package/dist/{managed-secret-NCWEMPUQ.js.map → link-set-JCMJ7ULP.js.map} +0 -0
  591. /package/dist/{noydb-62MX4QUG.js.map → managed-secret-S3MQ6SH6.js.map} +0 -0
  592. /package/dist/{migrate-cek-5ENH64AA.js.map → migrate-cek-V7GKJZ7O.js.map} +0 -0
  593. /package/dist/{register-HHLXEQDN.js.map → noydb-SWQJ3MZS.js.map} +0 -0
  594. /package/dist/{pod-handle-ECVCDIXW.js.map → pod-handle-WGDFB4C2.js.map} +0 -0
  595. /package/dist/{post-register-3IPCSG6Q.js.map → post-register-SLSXIPBV.js.map} +0 -0
  596. /package/dist/{registry-23WF3TGH.js.map → query/all/index.js.map} +0 -0
  597. /package/dist/{registry-4AYVUNXK.js.map → register-F3OYE7MJ.js.map} +0 -0
  598. /package/dist/{registry-JS3YFQQM.js.map → registry-CILQFXFK.js.map} +0 -0
  599. /package/dist/{registry-XX2Z7B46.js.map → registry-XNNYQVKV.js.map} +0 -0
  600. /package/dist/{request-withdrawal-CHPATE4G.js.map → registry-YCDZV3VW.js.map} +0 -0
  601. /package/dist/{revoke-7M2VW4ON.js.map → registry-ZGYS33TE.js.map} +0 -0
  602. /package/dist/{signer-SJQJVSM6.js.map → request-withdrawal-Z5NC6TXT.js.map} +0 -0
  603. /package/dist/{reveal-QZDHSGD4.js.map → reveal-4BCKPEC2.js.map} +0 -0
  604. /package/dist/{stale-BZS6H2RY.js.map → revoke-ZL62WC2D.js.map} +0 -0
  605. /package/dist/{seed-X5CC22CQ.js.map → seed-4JS2RJ4A.js.map} +0 -0
  606. /package/dist/{storage-RW4QBS23.js.map → signer-MBZZMXTC.js.map} +0 -0
  607. /package/dist/{walk-ZURCH6RZ.js.map → stale-TJ3DYZMQ.js.map} +0 -0
  608. /package/dist/{withdraw-accessible-PXR4IXDX.js.map → storage-SVMKRGCW.js.map} +0 -0
  609. /package/dist/{store-coordination-provider-4WZAIRHC.js.map → store-coordination-provider-2O2VUHKW.js.map} +0 -0
  610. /package/dist/{verify-6MDTIUEV.js.map → verify-RVT67IMH.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-formula/materialized-views/with-materialized-view.ts"],"sourcesContent":["import { MaterializedViewConfigError, ValidationError } from '../../kernel/errors.js'\nimport type { MaterializedViewSpec, MaterializedViewStrategy } from './types.js'\nimport { validateProjectionLegs } from './projection-legs.js' // #1140\n\n/**\n * Register a materialized view: a declared query whose result is\n * persisted as a queryable collection and kept fresh as sources\n * change. Writes go through the standard `Collection.put` pipeline;\n * refresh-driven deletes route through `Collection._internalDelete` so\n * user `onDelete` guards on the output collection aren't tripped by\n * housekeeping.\n *\n * Three registration modes:\n * - **single-source** — declare `query: (db) => Query<TRow>`; the\n * dependency analyzer derives source collections from the plan.\n * - **UNION** — declare `unionSources: [{ collection, map }, ...]`\n * plus optional `groupBy` + `aggregate`; the executor reads each\n * arm, maps to the unified row shape, concatenates, then groups\n * and aggregates.\n * - **projection** (#810) — declare `projection: { source, joins,\n * map }`; one output row per primary record, enriched with\n * forward FK legs and reverse \"collect\" legs before `map` runs.\n *\n * The three modes are mutually exclusive — exactly one of `query` /\n * `unionSources` / `projection` must be set at registration time.\n *\n * See design-history/2026-05-20-dim14-mv-v2-design.md (single-source v2),\n * design-history/2026-05-21-dim14-mv-multikey-and-union.md (UNION),\n * and design-history/2026-07-25-join-projection-mv-design.md (projection).\n */\nexport function withMaterializedView<TRow extends Record<string, unknown>>(\n spec: MaterializedViewSpec<TRow>,\n): MaterializedViewStrategy {\n if (!spec.name || spec.name.length === 0) {\n throw new ValidationError('withMaterializedView: name is required')\n }\n // Mutual exclusion: exactly one of the three forms must be declared.\n const declaredForms = [spec.query, spec.unionSources, spec.projection]\n .filter((f) => f !== undefined).length\n if (declaredForms > 1) {\n throw new MaterializedViewConfigError(\n 'query, unionSources, and projection are mutually exclusive — pick one',\n )\n }\n if (declaredForms === 0) {\n throw new MaterializedViewConfigError(\n 'strategy must declare exactly one of query, unionSources, or projection',\n )\n }\n if (spec.query !== undefined && typeof spec.query !== 'function') {\n throw new ValidationError('withMaterializedView: query must be a function returning a Query<T>')\n }\n // UNION-form invariants.\n if (spec.unionSources) {\n // A single arm is a deliberate shape: map→group→aggregate over\n // ONE collection with a COMPUTED bucket key (e.g. month sliced from a\n // date field) — something the query form's stored-field groupBy cannot\n // express. The executor and dependency analyzer are arm-count-agnostic.\n if (spec.unionSources.length < 1) {\n throw new MaterializedViewConfigError(\n 'unionSources requires at least 1 source collection',\n )\n }\n const seen = new Set<string>()\n for (const s of spec.unionSources) {\n if (typeof s?.collection !== 'string' || s.collection.length === 0) {\n throw new MaterializedViewConfigError(\n 'each unionSources entry must declare a non-empty `collection` string',\n )\n }\n if (typeof s.map !== 'function') {\n throw new MaterializedViewConfigError(\n `unionSources entry for \"${s.collection}\" is missing a \\`map\\` function`,\n )\n }\n if (seen.has(s.collection)) {\n throw new MaterializedViewConfigError(\n `unionSources must reference distinct collections (duplicate: \"${s.collection}\")`,\n )\n }\n seen.add(s.collection)\n }\n if (Array.isArray(spec.groupBy) && spec.groupBy.length === 0) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": groupBy must not be an empty array — omit it or provide at least one field name`,\n )\n }\n if (spec.aggregate && !spec.groupBy) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": UNION strategy with aggregate requires groupBy — `\n + `use groupBy to declare the bucketing keys, or remove aggregate for a pure dedup MV`,\n )\n }\n // `moneyFields` only has meaning when there's an aggregate to\n // money-rewrite — it keys reducer outputs, so declaring it without\n // `aggregate` is a no-op config mistake.\n if (spec.moneyFields && !spec.aggregate) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": moneyFields requires aggregate — `\n + `moneyFields rewrites sum/min/max reducers over money output fields, `\n + `so it is meaningless without an aggregate spec`,\n )\n }\n // Per-arm joins resolve right-side collections that the union\n // dependency set (built from arm `collection`s alone) does NOT\n // include. The consumer must list those right-side collections in\n // `sources` so writes to them trigger MV refresh.\n if (spec.unionSources.some(s => s.join && s.join.length > 0) && (!spec.sources || spec.sources.length === 0)) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": a unionSources arm declares join(s) but `\n + `no \\`sources\\` are listed — declare sources: [...] with the right-side `\n + `(join-target) collection names so writes to them trigger MV refresh`,\n )\n }\n if (spec.predicates) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": predicates are not supported on UNION strategies — `\n + `UNION mode does not use a Query<T> chain, so .wherePredicate() cannot fire. `\n + `Use the query() form, or open an issue if per-arm predicates are needed`,\n )\n }\n }\n // Projection-form invariants (#810). Shape checks only — the\n // semantic ref() check on collect legs (the `on` field must carry a\n // ref() targeting the projection source) runs at first\n // materialization, parity with join-time ref errors.\n if (spec.projection) {\n const projection = spec.projection\n if (typeof projection.source !== 'string' || projection.source.length === 0) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": projection.source must be a non-empty collection name`,\n )\n }\n if (typeof projection.map !== 'function') {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": projection is missing a \\`map\\` function`,\n )\n }\n if (!Array.isArray(projection.joins) || projection.joins.length < 1) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": projection.joins must declare at least 1 leg — `\n + `a leg-less single-source MV is the query() form's job`,\n )\n }\n const seenAs = new Set<string>()\n for (const leg of projection.joins) {\n const l = leg as { field?: unknown; collect?: unknown; on?: unknown; as?: unknown }\n const isForward = l.field !== undefined\n const isCollect = l.collect !== undefined\n if (isForward && isCollect) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": a projection leg cannot declare both \\`field\\` `\n + `(forward) and \\`collect\\` (reverse) — split it into two legs`,\n )\n }\n if (!isForward && !isCollect) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": each projection leg must declare either \\`field\\` `\n + `(forward FK) or \\`collect\\` (reverse one-to-many)`,\n )\n }\n if (isForward && (typeof l.field !== 'string' || l.field.length === 0)) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": a forward projection leg must declare a non-empty \\`field\\``,\n )\n }\n if (isCollect) {\n if (typeof l.collect !== 'string' || l.collect.length === 0) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": a collect projection leg must declare a non-empty \\`collect\\` collection name`,\n )\n }\n if (typeof l.on !== 'string' || l.on.length === 0) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": collect leg for \"${l.collect}\" must declare a non-empty \\`on\\` FK field`,\n )\n }\n }\n if (typeof l.as !== 'string' || l.as.length === 0) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": each projection leg must declare a non-empty \\`as\\` alias`,\n )\n }\n if (seenAs.has(l.as)) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": projection legs must attach under distinct \\`as\\` aliases (duplicate: \"${l.as}\")`,\n )\n }\n seenAs.add(l.as)\n }\n // #1140 — `from` wiring, checked here with the rest of the leg shape rather\n // than at vault open: it is pure configuration, so the earliest failure is\n // the best one. Owned by `projection-legs.ts` because the executor and the\n // registry both walk the same graph.\n validateProjectionLegs(spec.name, spec.projection)\n // Post-map grouping invariants — same rules as UNION (the mapped\n // stream feeds the same shared groupAndReduce tail).\n if (Array.isArray(spec.groupBy) && spec.groupBy.length === 0) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": groupBy must not be an empty array — omit it or provide at least one field name`,\n )\n }\n if (spec.aggregate && !spec.groupBy) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": projection strategy with aggregate requires groupBy — `\n + `use groupBy to declare the bucketing keys, or remove aggregate for a row-per-primary-record MV`,\n )\n }\n if (spec.moneyFields && !spec.aggregate) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": moneyFields requires aggregate — `\n + `moneyFields rewrites sum/min/max reducers over money output fields, `\n + `so it is meaningless without an aggregate spec`,\n )\n }\n if (spec.predicates) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": predicates are not supported on projection strategies — `\n + `projection mode does not use a Query<T> chain, so .wherePredicate() cannot fire. `\n + `Filter inside projection.map (return null to omit) instead`,\n )\n }\n }\n // i18nLocale + i18nFields drive compute-time i18n resolution of group-key\n // i18nText fields before bucketing — UNION mode (resolved on the unified rows)\n // AND query mode (resolved in GroupedReduction.run before groupAndReduce).\n // i18nLocale without i18nFields cannot resolve anything, so reject it early.\n if (spec.i18nLocale !== undefined && spec.i18nFields === undefined) {\n throw new MaterializedViewConfigError(\n `withMaterializedView \"${spec.name}\": i18nLocale requires i18nFields — `\n + `declare the i18nText descriptors of the group-key fields so they can be `\n + `resolved at the mv layer before bucketing.`,\n )\n }\n if (typeof spec.rowKey !== 'function') {\n // Two states, two messages. The original text said \"required\" for BOTH,\n // so passing a field NAME — the shape every other option here takes —\n // reported the key as ABSENT. That collapse cost this author three\n // attempts and produced a probe that measured nothing.\n throw new ValidationError(\n spec.rowKey === undefined\n ? 'withMaterializedView: rowKey is required (no default; see spec § Type surface)'\n : `withMaterializedView: rowKey must be a FUNCTION (row) => string, not a ${typeof spec.rowKey}`\n + ` — it computes each output row's id from the grouped row, so a field NAME is not enough.`\n + ` Pass \\`rowKey: (r) => String(r.${typeof spec.rowKey === 'string' ? String(spec.rowKey) : 'someField'})\\`.`)\n }\n if (spec.refresh !== 'eager' && spec.refresh !== 'lazy' && spec.refresh !== 'manual') {\n throw new ValidationError(\n `withMaterializedView: refresh must be 'eager' | 'lazy' | 'manual', got \"${String(spec.refresh)}\"`,\n )\n }\n return {\n __noydb_strategy: 'materialized-view',\n spec,\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,159 +0,0 @@
1
- import {
2
- readPath
3
- } from "./chunk-NSITFOCJ.js";
4
-
5
- // src/with-lookup/indexing/eager-indexes.ts
6
- function normalizeIndexDefs(defs) {
7
- return defs.map((def) => {
8
- if (typeof def === "string") return { fields: [def] };
9
- if (Array.isArray(def)) return { fields: def };
10
- return def;
11
- });
12
- }
13
- var CollectionIndexes = class {
14
- indexes = /* @__PURE__ */ new Map();
15
- /**
16
- * Per-field bucket-key canonicalizer (#672 review C1), registered ONCE
17
- * via {@link setCanonicalizer} when the collection wires indexing up
18
- * (money-aware via `ViaPipeline.canonicalizeIndexKey`). Consulted by
19
- * EVERY bucket-mutation site — `build`/`upsert`/`remove` — so bucket
20
- * membership is symmetric by construction: a value can never be added
21
- * under a canonical key and later removed under the raw one (or vice
22
- * versa), which is what stranded ids on `put`/`delete` before this fix.
23
- */
24
- canonicalize;
25
- /**
26
- * Register the bucket-key canonicalizer. Called once, where the
27
- * collection constructs its indexing state — safe to call again (the
28
- * closure is simply replaced) but there is normally only one caller.
29
- */
30
- setCanonicalizer(fn) {
31
- this.canonicalize = fn;
32
- }
33
- /**
34
- * Declare an index. Subsequent record additions are tracked under it.
35
- * Calling this twice for the same field is a no-op (idempotent).
36
- */
37
- declare(field) {
38
- if (this.indexes.has(field)) return;
39
- this.indexes.set(field, { field, buckets: /* @__PURE__ */ new Map() });
40
- }
41
- /** True if the given field has a declared index. */
42
- has(field) {
43
- return this.indexes.has(field);
44
- }
45
- /** All declared field names, in declaration order. */
46
- fields() {
47
- return [...this.indexes.keys()];
48
- }
49
- /**
50
- * Build all declared indexes from a snapshot of records.
51
- * Called once per hydration. O(N × indexes.size). Buckets through the
52
- * registered {@link setCanonicalizer} closure, same as `upsert`/`remove`.
53
- */
54
- build(records) {
55
- for (const idx of this.indexes.values()) {
56
- idx.buckets.clear();
57
- for (const { id, record } of records) {
58
- addToIndex(idx, id, record, this.canonicalize);
59
- }
60
- }
61
- }
62
- /**
63
- * Insert or update a single record across all indexes.
64
- * Called by `Collection.put()` after the encrypted write succeeds.
65
- *
66
- * If `previousRecord` is provided, the record is removed from any old
67
- * buckets first — this is the update path. Pass `null` for fresh adds.
68
- */
69
- upsert(id, newRecord, previousRecord) {
70
- if (this.indexes.size === 0) return;
71
- if (previousRecord !== null) {
72
- this.remove(id, previousRecord);
73
- }
74
- for (const idx of this.indexes.values()) {
75
- addToIndex(idx, id, newRecord, this.canonicalize);
76
- }
77
- }
78
- /**
79
- * Remove a record from all indexes. Called by `Collection.delete()`
80
- * (and as the first half of `upsert` for the update path).
81
- */
82
- remove(id, record) {
83
- if (this.indexes.size === 0) return;
84
- for (const idx of this.indexes.values()) {
85
- removeFromIndex(idx, id, record, this.canonicalize);
86
- }
87
- }
88
- /** Drop all index data. Called when the collection is invalidated. */
89
- clear() {
90
- for (const idx of this.indexes.values()) {
91
- idx.buckets.clear();
92
- }
93
- }
94
- /**
95
- * Equality lookup: return the set of record ids whose `field` matches
96
- * the given value. Returns `null` if no index covers the field — the
97
- * caller should fall back to a linear scan.
98
- *
99
- * The returned Set is a reference to the index's internal storage —
100
- * callers must NOT mutate it.
101
- */
102
- lookupEqual(field, value) {
103
- const idx = this.indexes.get(field);
104
- if (!idx) return null;
105
- const key = stringifyKey(value);
106
- return idx.buckets.get(key) ?? EMPTY_SET;
107
- }
108
- /**
109
- * Set lookup: return the union of record ids whose `field` matches any
110
- * of the given values. Returns `null` if no index covers the field.
111
- */
112
- lookupIn(field, values) {
113
- const idx = this.indexes.get(field);
114
- if (!idx) return null;
115
- const out = /* @__PURE__ */ new Set();
116
- for (const value of values) {
117
- const key = stringifyKey(value);
118
- const bucket = idx.buckets.get(key);
119
- if (bucket) {
120
- for (const id of bucket) out.add(id);
121
- }
122
- }
123
- return out;
124
- }
125
- };
126
- var EMPTY_SET = /* @__PURE__ */ new Set();
127
- function stringifyKey(value) {
128
- if (value === null || value === void 0) return "\0NULL\0";
129
- if (typeof value === "string") return value;
130
- if (typeof value === "number" || typeof value === "boolean") return String(value);
131
- if (value instanceof Date) return value.toISOString();
132
- return "\0OBJECT\0";
133
- }
134
- function addToIndex(idx, id, record, canonicalize) {
135
- const value = readPath(record, idx.field);
136
- if (value === null || value === void 0) return;
137
- const key = canonicalize?.(idx.field, value) ?? stringifyKey(value);
138
- let bucket = idx.buckets.get(key);
139
- if (!bucket) {
140
- bucket = /* @__PURE__ */ new Set();
141
- idx.buckets.set(key, bucket);
142
- }
143
- bucket.add(id);
144
- }
145
- function removeFromIndex(idx, id, record, canonicalize) {
146
- const value = readPath(record, idx.field);
147
- if (value === null || value === void 0) return;
148
- const key = canonicalize?.(idx.field, value) ?? stringifyKey(value);
149
- const bucket = idx.buckets.get(key);
150
- if (!bucket) return;
151
- bucket.delete(id);
152
- if (bucket.size === 0) idx.buckets.delete(key);
153
- }
154
-
155
- export {
156
- normalizeIndexDefs,
157
- CollectionIndexes
158
- };
159
- //# sourceMappingURL=chunk-I3ISCYT7.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-lookup/indexing/eager-indexes.ts"],"sourcesContent":["/**\n * Secondary indexes for the query DSL.\n *\n * ships **in-memory hash indexes**:\n * - Built during `Collection.ensureHydrated()` from the decrypted cache\n * - Maintained incrementally on `put` and `delete`\n * - Consulted by the query executor for `==` and `in` operators on\n * indexed fields, falling back to a linear scan otherwise\n * - Live entirely in memory — no adapter writes for the index itself\n *\n * Persistent encrypted index blobs (the spec's \"store as a separate\n * AES-256-GCM blob\" note) are deferred to a follow-up issue. The reasons\n * are documented in the PR body — short version: at the target\n * scale of 1K–50K records, building the index during hydrate is free,\n * so persistence buys nothing measurable.\n */\n\nimport { readPath } from '../../kernel/query/predicate.js'\n\n/**\n * Index declaration accepted by `Collection`'s constructor.\n *\n * Accepts:\n * - `string` — a single-field hash index (`'clientId'`)\n * - `{ fields: [...] }` or `readonly string[]` — a composite index\n * over an ordered field tuple. Only lazy-mode\n * collections consume composite declarations today; eager mode\n * silently treats a composite as equivalent to declaring each\n * component field as its own single-field index.\n *\n * Additive variants (unique constraints, partial indexes) will land as\n * further union members without breaking existing declarations.\n */\nexport type IndexDef =\n | string\n | { readonly fields: readonly string[]; readonly unique?: boolean }\n | readonly string[]\n\n/**\n * Normalize a declared `IndexDef[]` into a uniform `{ fields, unique? }`\n * shape: `string` → `{fields:[s]}`, `string[]` → `{fields}`, object →\n * passthrough. Pure — used by `Collection`'s sensitive-field leak check\n * and by `getDeclaredIndexes()` (introspection).\n */\nexport function normalizeIndexDefs(\n defs: readonly IndexDef[],\n): ReadonlyArray<{ readonly fields: readonly string[]; readonly unique?: boolean }> {\n return defs.map((def) => {\n if (typeof def === 'string') return { fields: [def] }\n if (Array.isArray(def)) return { fields: def }\n return def as { readonly fields: readonly string[]; readonly unique?: boolean }\n })\n}\n\n/**\n * Internal representation of a built hash index.\n *\n * Maps stringified field values to the set of record ids whose value\n * for that field matches. Stringification keeps the index simple and\n * works uniformly for primitives (`'open'`, `'42'`, `'true'`).\n *\n * Records whose indexed field is `undefined` or `null` are NOT inserted\n * — `query().where('field', '==', undefined)` falls back to a linear\n * scan, which is the conservative behavior.\n */\nexport interface HashIndex {\n readonly field: string\n readonly buckets: Map<string, Set<string>>\n}\n\n/**\n * Container for all indexes on a single collection.\n *\n * Methods are pure with respect to the in-memory `buckets` Map — they\n * never touch the adapter or the keyring. The Collection class owns\n * lifecycle (build on hydrate, maintain on put/delete).\n */\nexport class CollectionIndexes {\n private readonly indexes = new Map<string, HashIndex>()\n\n /**\n * Per-field bucket-key canonicalizer (#672 review C1), registered ONCE\n * via {@link setCanonicalizer} when the collection wires indexing up\n * (money-aware via `ViaPipeline.canonicalizeIndexKey`). Consulted by\n * EVERY bucket-mutation site — `build`/`upsert`/`remove` — so bucket\n * membership is symmetric by construction: a value can never be added\n * under a canonical key and later removed under the raw one (or vice\n * versa), which is what stranded ids on `put`/`delete` before this fix.\n */\n private canonicalize?: (field: string, value: unknown) => string | undefined\n\n /**\n * Register the bucket-key canonicalizer. Called once, where the\n * collection constructs its indexing state — safe to call again (the\n * closure is simply replaced) but there is normally only one caller.\n */\n setCanonicalizer(fn: (field: string, value: unknown) => string | undefined): void {\n this.canonicalize = fn\n }\n\n /**\n * Declare an index. Subsequent record additions are tracked under it.\n * Calling this twice for the same field is a no-op (idempotent).\n */\n declare(field: string): void {\n if (this.indexes.has(field)) return\n this.indexes.set(field, { field, buckets: new Map() })\n }\n\n /** True if the given field has a declared index. */\n has(field: string): boolean {\n return this.indexes.has(field)\n }\n\n /** All declared field names, in declaration order. */\n fields(): string[] {\n return [...this.indexes.keys()]\n }\n\n /**\n * Build all declared indexes from a snapshot of records.\n * Called once per hydration. O(N × indexes.size). Buckets through the\n * registered {@link setCanonicalizer} closure, same as `upsert`/`remove`.\n */\n build<T>(records: ReadonlyArray<{ id: string; record: T }>): void {\n for (const idx of this.indexes.values()) {\n idx.buckets.clear()\n for (const { id, record } of records) {\n addToIndex(idx, id, record, this.canonicalize)\n }\n }\n }\n\n /**\n * Insert or update a single record across all indexes.\n * Called by `Collection.put()` after the encrypted write succeeds.\n *\n * If `previousRecord` is provided, the record is removed from any old\n * buckets first — this is the update path. Pass `null` for fresh adds.\n */\n upsert<T>(id: string, newRecord: T, previousRecord: T | null): void {\n if (this.indexes.size === 0) return\n if (previousRecord !== null) {\n this.remove(id, previousRecord)\n }\n for (const idx of this.indexes.values()) {\n addToIndex(idx, id, newRecord, this.canonicalize)\n }\n }\n\n /**\n * Remove a record from all indexes. Called by `Collection.delete()`\n * (and as the first half of `upsert` for the update path).\n */\n remove<T>(id: string, record: T): void {\n if (this.indexes.size === 0) return\n for (const idx of this.indexes.values()) {\n removeFromIndex(idx, id, record, this.canonicalize)\n }\n }\n\n /** Drop all index data. Called when the collection is invalidated. */\n clear(): void {\n for (const idx of this.indexes.values()) {\n idx.buckets.clear()\n }\n }\n\n /**\n * Equality lookup: return the set of record ids whose `field` matches\n * the given value. Returns `null` if no index covers the field — the\n * caller should fall back to a linear scan.\n *\n * The returned Set is a reference to the index's internal storage —\n * callers must NOT mutate it.\n */\n lookupEqual(field: string, value: unknown): ReadonlySet<string> | null {\n const idx = this.indexes.get(field)\n if (!idx) return null\n const key = stringifyKey(value)\n return idx.buckets.get(key) ?? EMPTY_SET\n }\n\n /**\n * Set lookup: return the union of record ids whose `field` matches any\n * of the given values. Returns `null` if no index covers the field.\n */\n lookupIn(field: string, values: readonly unknown[]): ReadonlySet<string> | null {\n const idx = this.indexes.get(field)\n if (!idx) return null\n const out = new Set<string>()\n for (const value of values) {\n const key = stringifyKey(value)\n const bucket = idx.buckets.get(key)\n if (bucket) {\n for (const id of bucket) out.add(id)\n }\n }\n return out\n }\n}\n\nconst EMPTY_SET: ReadonlySet<string> = new Set()\n\n/**\n * Stringify a value into a stable bucket key.\n *\n * `null`/`undefined` produce a sentinel that records will never match\n * (so we never index nullish values — `where('x', '==', null)` falls back\n * to a linear scan). Numbers, booleans, strings, and Date objects are\n * coerced via `String()`. Objects produce a sentinel that no real record\n * will match — querying with object values is a code smell.\n */\nfunction stringifyKey(value: unknown): string {\n if (value === null || value === undefined) return '\\0NULL\\0'\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n if (value instanceof Date) return value.toISOString()\n return '\\0OBJECT\\0'\n}\n\nfunction addToIndex<T>(\n idx: HashIndex,\n id: string,\n record: T,\n canonicalize?: (field: string, value: unknown) => string | undefined,\n): void {\n const value = readPath(record, idx.field)\n if (value === null || value === undefined) return\n const key = canonicalize?.(idx.field, value) ?? stringifyKey(value)\n let bucket = idx.buckets.get(key)\n if (!bucket) {\n bucket = new Set()\n idx.buckets.set(key, bucket)\n }\n bucket.add(id)\n}\n\nfunction removeFromIndex<T>(\n idx: HashIndex,\n id: string,\n record: T,\n canonicalize?: (field: string, value: unknown) => string | undefined,\n): void {\n const value = readPath(record, idx.field)\n if (value === null || value === undefined) return\n const key = canonicalize?.(idx.field, value) ?? stringifyKey(value)\n const bucket = idx.buckets.get(key)\n if (!bucket) return\n bucket.delete(id)\n // Clean up empty buckets so the Map doesn't accumulate dead keys.\n if (bucket.size === 0) idx.buckets.delete(key)\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-formula/materialized-views/executor.ts"],"sourcesContent":["import type { Collection } from '../../kernel/collection.js'\nimport type { TxContext } from '../../with-commit/tx/transaction.js'\nimport type { EncryptedEnvelope } from '../../kernel/types.js'\nimport { MaterializedViewTooLargeError, MaterializedViewConfigError, LocaleNotSpecifiedError, JoinTooLargeError } from '../../kernel/errors.js'\nimport { DEFAULT_JOIN_MAX_ROWS } from '../../kernel/query/join.js'\nimport type { MaterializedFromMeta, MVQueryContext, MaterializedViewSpec, ProjectionJoinLeg } from './types.js'\nimport type { RegisteredMV } from './registry.js'\nimport { wrapDbWithPredicates } from './registry.js'\nimport { groupAndReduce } from '../../with-lookup/reduce/groupby.js'\nimport { canonicalGroupKey } from '../../with-lookup/reduce/canonical-key.js'\nimport { applyI18nLocale, type I18nTextDescriptor } from '../../via/i18n/core.js'\nimport { canonicalizeMoneyFieldsAsDecimal, decodeMoneyFields } from '../../via/money/normalize.js'\nimport { exactMath } from '../../via/money/exact.js'\nimport { putDerivedOutput, type PutDerivedOutputCtx } from '../../kernel/via/dispatch.js'\nimport { isCollectLeg, resolveLegOwner, type RefLookup } from './projection-legs.js' // #1140\n\n/**\n * Accessor shape passed in from the owning Vault. Mirrors v1's\n * `DerivationStaleAccessor` — provides the per-collection resolver\n * and the active TxContext so refresh writes/tombstones register on\n * `_executed` for rollback symmetry.\n */\nexport interface MVExecutorAccessor {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n getCollection(name: string): Collection<any>\n getActiveTxContext(): TxContext | null\n /**\n * Vault-shaped accessor passed to the MV's `query()` callback at\n * each refresh. Same instance the registry used at registration\n * time; threading through the executor lets the refresh path\n * re-evaluate the closure against the live vault state.\n */\n getQueryContext(): MVQueryContext\n /**\n * #638 Task 5 — ctx for `putDerivedOutput`'s frozen-period skip+audit. #641: the lazy\n * resolve-on-read caller (`stale.ts#resolveStaleMVOnRead`) now supplies one too — built at\n * the `Collection.get()`/`list()` entry point with a `'resolve-on-read'` sentinel id (no\n * real \"reacting write\" for a read-triggered materialize, mirroring `refreshView()`'s\n * `'refreshView'` sentinel). Still declared optional here since `MVExecutorAccessor` is a\n * general shape and a future caller could reasonably omit it.\n */\n dispatchCtx?: PutDerivedOutputCtx\n}\n\nexport interface RefreshResult {\n /** Rows newly written / overwritten. */\n written: number\n /** Rows tombstoned via `_internalDelete` (only when `onEmpty: 'delete'`). */\n deleted: number\n /** Failed row writes (non-strict mode). */\n failed: number\n /** #782/#785 — `outputCollection:id` entries from the tombstone pass whose ownership stamp\n * couldn't be decoded under the collection's default DEK (undecodable, mirroring\n * `invalidateMVAtRest`'s #776 posture) — ownership UNCONFIRMED. Only populated when\n * `onEmpty: 'delete'`. */\n residueUndecodable: string[]\n /** #782/#785 — `outputCollection:id` entries that decoded, stamp-matched this MV, but\n * `_internalDelete` declined (the #718 tier-elevation gate) — ownership CONFIRMED, a real\n * silent survival, surfaced rather than dropped. Only populated when `onEmpty: 'delete'`. */\n residueDeclined: string[]\n}\n\n/** Default cost ceiling — overridable per-MV via `spec.maxRows`. */\nconst DEFAULT_MAX_ROWS = 100_000\n\n/**\n * Materialize a query terminal that may be a `Query<T>` (call\n * `.toArray()`), an `Reduction<R>` (call `.run()` returning a\n * single object — wrap as a one-row array), or a `GroupedReduction<R>`\n * (call `.run()` returning an array of grouped rows). Branches on\n * available terminal at runtime — no type-discrimination at registration.\n */\nasync function materializeQueryResult(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n q: any,\n mvName: string,\n i18nLocale?: string,\n i18nFields?: Record<string, I18nTextDescriptor>,\n): Promise<ReadonlyArray<Record<string, unknown>>> {\n if (typeof q?.toArray === 'function') {\n // Query<T> — non-aggregate path. `.toArray()` returns Promise<T[]>.\n return await q.toArray()\n }\n if (typeof q?.run === 'function') {\n // Reduction<R> or GroupedReduction<R>. `.run()` is synchronous\n // and returns either a single object (Reduction) or an array of\n // rows (GroupedReduction). Promise.resolve() normalizes both\n // sync and async (future) variants.\n // Query-form MV grouping: when the MV declares i18nLocale, pass it +\n // i18nFields so a GroupedReduction resolves i18n group keys before\n // bucketing (the Reduction path ignores the extra arg).\n const runOpts = i18nLocale !== undefined ? { locale: i18nLocale, i18nFields } : undefined\n const result: unknown = await Promise.resolve(q.run(runOpts))\n if (Array.isArray(result)) {\n return result as ReadonlyArray<Record<string, unknown>>\n }\n // Single-aggregate result — wrap as one-row array. The consumer's\n // `rowKey()` should return a stable identity (often a literal\n // constant like `'total'`) since there's only one row.\n return [result as Record<string, unknown>]\n }\n throw new Error(\n `MV \"${mvName}\": query() must return a Query<T>, Reduction, or GroupedReduction. ` +\n `Got something without a .toArray() or .run() terminal.`,\n )\n}\n\n/**\n * Materialize a UNION-form MV: read every arm's source\n * collection, apply each arm's `map` to project rows into the unified\n * MV row shape, concatenate the mapped streams, then optionally run\n * `groupBy` + `aggregate` over the result.\n *\n * Modes (driven by `spec.groupBy` / `spec.aggregate`):\n *\n * - No `groupBy` → return the concatenated mapped rows unchanged.\n * - `groupBy` without `aggregate` → dedupe by composite group key,\n * keep the first row seen per key (later arms don't overwrite\n * earlier arms — Map insertion order rules).\n * - `groupBy` + `aggregate` → delegate to the shared `groupAndReduce`\n * pipeline used by `Query.groupBy().aggregate()`.\n *\n * Per-arm `map` is the schema-unification boundary; the strategy's\n * `TRow` type parameter enforces that every arm projects into the\n * same shape at compile time.\n *\n * @internal\n */\nasync function materializeUnionResult<TRow extends Record<string, unknown>>(\n spec: MaterializedViewSpec<TRow>,\n db: MVQueryContext,\n): Promise<ReadonlyArray<Record<string, unknown>>> {\n const unified: TRow[] = []\n for (const arm of spec.unionSources!) {\n const coll = db.collection<Record<string, unknown>>(arm.collection)\n // Optional per-arm FK joins: chain `.join(field, { as, ... })` for\n // each declared leg before terminating. The aliased right-side\n // record lands at `sourceRow[leg.as]`, where the arm's `map` reads\n // it. Cast to `any` because the chained join widens the row type but\n // the executor treats every row as `Record<string, unknown>` — same\n // pattern the query-form join path uses.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let q: any = coll.query()\n if (arm.join?.length) {\n for (const leg of arm.join) {\n q = q.join(leg.field, { as: leg.as, maxRows: leg.maxRows, strategy: leg.strategy })\n }\n }\n const sourceRows = q.toArray() as ReadonlyArray<Record<string, unknown>>\n for (const r of sourceRows) {\n const mapped = arm.map(r)\n // null / undefined means \"omit this source row\" — skip without\n // pushing so groupBy/aggregate never see a null entry.\n if (mapped == null) continue\n unified.push(mapped)\n }\n }\n\n return finalizeMappedRows(spec, unified)\n}\n\n/**\n * Shared post-map tail for the UNION and projection (#810) forms:\n * optional `groupBy` (+ `aggregate`) over the mapped-row stream.\n * Extracted verbatim from `materializeUnionResult` so both forms feed\n * the identical grouping pipeline — i18n group-key resolution, the\n * object-group-key guard, dedup-without-aggregate, and the shared\n * `groupAndReduce` delegate.\n *\n * @internal\n */\nfunction finalizeMappedRows<TRow extends Record<string, unknown>>(\n spec: MaterializedViewSpec<TRow>,\n unified: TRow[],\n): ReadonlyArray<Record<string, unknown>> {\n if (!spec.groupBy) return unified\n\n const groupFields: readonly string[] =\n typeof spec.groupBy === 'string' ? [spec.groupBy] : spec.groupBy\n\n // i18n-aware group keys. An `i18nText` group field carries a raw\n // `{ locale: string }` map, an unstable object key. When `i18nLocale` is\n // declared (with `i18nFields` describing those fields), resolve the declared\n // group-key i18n fields to it at the `mv` layer FIRST — the same unified-rows\n // boundary where money is threaded — so buckets are stable strings and the\n // `mv`-layer `onMissing` policy fires here.\n if (spec.i18nLocale !== undefined && spec.i18nFields !== undefined) {\n const groupI18n: Record<string, I18nTextDescriptor> = {}\n for (const f of groupFields) {\n const d = spec.i18nFields[f]\n if (d !== undefined) groupI18n[f] = d\n }\n if (Object.keys(groupI18n).length > 0) {\n for (let i = 0; i < unified.length; i++) {\n unified[i] = applyI18nLocale(unified[i] as Record<string, unknown>, groupI18n, spec.i18nLocale, undefined, 'mv') as TRow\n }\n }\n }\n // Guard (always): a remaining object-valued group key — an undeclared i18n\n // field or a locale-less MV — would bucket on a map. Refuse, don't bucket wrong.\n for (const f of groupFields) {\n for (const row of unified) {\n const v = (row as Record<string, unknown>)[f]\n if (v !== null && typeof v === 'object') {\n throw new LocaleNotSpecifiedError(\n f,\n `Materialized view \"${spec.name}\" groups by \"${f}\", whose value is a raw i18n locale map — ` +\n `an unstable object group key. Declare { i18nLocale, i18nFields } on the MV to resolve it at ` +\n `the 'mv' layer, or group by a dictKey/staticDict code (the stable key) and resolve the label at read time.`,\n )\n }\n }\n }\n\n // groupBy without aggregate — dedupe by composite key, keep first\n // seen row per key. Useful for cross-arm uniqueness (e.g. unify two\n // sibling collections, keeping one row per natural key).\n if (!spec.aggregate) {\n const seen = new Map<string, TRow>()\n for (const row of unified) {\n const k = canonicalGroupKey(groupFields, row as Record<string, unknown>)\n if (!seen.has(k)) seen.set(k, row)\n }\n return [...seen.values()]\n }\n\n // groupBy + aggregate — delegate to the shared pipeline used by\n // `Query.groupBy().aggregate()`. Result rows carry each grouped\n // field in declaration order followed by the spec's reducer outputs.\n return groupAndReduce<Record<string, unknown>>(unified, groupFields, spec.aggregate, spec.moneyFields)\n}\n\n/**\n * Apply the spec's `derive` to each finished row (#1007).\n *\n * Three rules, all of them about keeping the hook narrow enough that the\n * engine never has to reason about the function:\n *\n * - a `null` / `undefined` return leaves the row untouched;\n * - a returned key that collides with a `groupBy` field is refused — group\n * keys are the row's identity and feed `rowKey`, so letting `derive`\n * rewrite one would silently re-home the row into a different bucket than\n * the one it was aggregated for;\n * - a derived field declared in `moneyFields` is quantised through its\n * descriptor, so the stored value is exact at the declared scale instead of\n * whatever the user's arithmetic produced.\n *\n * @internal\n */\nfunction applyDerive<TRow extends Record<string, unknown>>(\n spec: MaterializedViewSpec<TRow>,\n rows: ReadonlyArray<Record<string, unknown>>,\n): ReadonlyArray<Record<string, unknown>> {\n const groupFields = new Set<string>(\n spec.groupBy === undefined ? [] : typeof spec.groupBy === 'string' ? [spec.groupBy] : spec.groupBy,\n )\n return rows.map((row) => {\n // `derive` sees the row the way a READER would: money decoded to its\n // canonical decimal form, not the scaled integer the reducer left behind.\n // Handing over `1005` where the schema says `10.05` would make every\n // derived money expression quietly wrong by a factor of the scale.\n // `'raw'` keeps it at the exact decimal rather than a locale-formatted\n // string, which is what `exact.*` consumes.\n const view = spec.moneyFields ? decodeMoneyFields(row, spec.moneyFields, 'raw') : row\n const patch = spec.derive!(view as TRow, exactMath)\n if (patch === null || patch === undefined) return row\n for (const key of Object.keys(patch)) {\n if (groupFields.has(key)) {\n throw new MaterializedViewConfigError(\n `Materialized view \"${spec.name}\": derive() returned the group key \"${key}\". ` +\n 'A group key is the row\\'s identity and feeds rowKey — rewriting it would re-home the ' +\n 'row into a bucket it was not aggregated for. Emit a differently-named field instead.',\n )\n }\n }\n // Canonicalize the PATCH only, into the DECIMAL form the money-aware\n // reducers emit — not the scaled-integer storage form a collection uses.\n // An MV row's money fields are decimal strings, so a derived field\n // quantised into storage form reads back as the scaled integer beside\n // correctly-decoded siblings: `\"1000000\"` next to `\"10000.00\"` (#1018).\n // Precision is still enforced; only the output shape differs.\n const canonical = spec.moneyFields\n ? canonicalizeMoneyFieldsAsDecimal(patch, spec.moneyFields)\n : patch\n return { ...row, ...canonical }\n })\n}\n\n/**\n * Materialize a projection-form MV (#810): hydrate the primary source\n * rows, resolve forward FK legs through the same `.join()` machinery\n * the UNION arms use, attach reverse \"collect\" legs via one\n * hash-grouped snapshot pass per leg, then run the projection `map`\n * (null / undefined omits the primary row) and the shared post-map\n * grouping tail.\n *\n * @internal\n */\nasync function materializeProjectionResult<TRow extends Record<string, unknown>>(\n spec: MaterializedViewSpec<TRow>,\n db: MVQueryContext,\n): Promise<ReadonlyArray<Record<string, unknown>>> {\n const projection = spec.projection!\n const coll = db.collection<Record<string, unknown>>(projection.source)\n // ROOT forward legs chain through the query builder exactly like UNION arm\n // joins — ref() resolution, dangling-mode semantics, presentation dressing,\n // and ceilings all ride the existing `.join()` path. Cast to `any` for the\n // same reason `materializeUnionResult` does.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let q: any = coll.query()\n for (const leg of projection.joins) {\n if (isCollectLeg(leg) || leg.from !== undefined) continue\n q = q.join(leg.field, { as: leg.as, maxRows: leg.maxRows, strategy: leg.strategy })\n }\n let rows = q.toArray() as Array<Record<string, unknown>>\n // Remaining legs in DECLARATION order — which is what makes `from` work: a leg\n // may only name an alias declared earlier, so by the time it runs, the rows it\n // attaches to are already on the row. Root collect legs are in this loop too;\n // they were never part of the query chain.\n for (const leg of projection.joins) {\n if (isCollectLeg(leg)) {\n rows = applyCollectLeg(rows, leg, spec.name, projection.source, projection.joins, db)\n } else if (leg.from !== undefined) {\n rows = applyLegRelativeForwardLeg(rows, leg, spec.name, projection.source, projection.joins, db)\n }\n }\n const mapped: TRow[] = []\n for (const r of rows) {\n const m = projection.map(r)\n // null / undefined means \"omit this primary row\" — same contract\n // as the UNION arm `map`.\n if (m == null) continue\n mapped.push(m)\n }\n return finalizeMappedRows(spec, mapped)\n}\n\n/**\n * A `RefLookup` backed by the live join contexts — the executor's answer to the\n * same question the registry answers from the ref registry. Reads through, so a\n * ref declared after `openVault()` (the only place refs CAN be declared) is\n * visible here.\n *\n * @internal\n */\nfunction legOwnerLookup(db: MVQueryContext): RefLookup {\n return (collection, field) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const q = db.collection<Record<string, unknown>>(collection).query() as any\n return (q._joinContext?.()?.resolveRef(field) as { target: string } | null | undefined) ?? null\n }\n}\n\n/**\n * Attach one FORWARD leg that hangs off another leg's alias (#1140).\n *\n * Root forward legs ride `Query.join()` and are resolved before this runs; a\n * leg-relative one cannot, because the FK it follows lives on an already-attached\n * record rather than on a column of the primary query. It is therefore resolved\n * the same way a collect leg is — one snapshot of the target collection, indexed\n * by id — which keeps it O(N+M) rather than one `get()` per row.\n *\n * Attaches `null` when the `from` alias is null (a dangling ref upstream), when\n * the FK is nullish, or when the target row is missing. That is deliberately\n * outer-by-nature and matches the collect leg's empty array: a projection MV\n * must not drop a primary row because something two hops away is absent.\n *\n * @internal\n */\nfunction applyLegRelativeForwardLeg(\n primaryRows: ReadonlyArray<Record<string, unknown>>,\n leg: Extract<ProjectionJoinLeg, { field: string }>,\n mvName: string,\n source: string,\n joins: ReadonlyArray<ProjectionJoinLeg>,\n db: MVQueryContext,\n): Array<Record<string, unknown>> {\n const lookup = legOwnerLookup(db)\n const owner = resolveLegOwner(leg, joins, source, lookup)\n const refDesc = owner === null ? null : lookup(owner, leg.field)\n if (owner === null || refDesc === null) {\n throw new MaterializedViewConfigError(\n `\"${mvName}\": projection leg \"${leg.as}\" requires a ref() on field \"${leg.field}\" of `\n + `collection \"${owner ?? `(behind from: \"${leg.from}\")`}\" — declare it on that collection, `\n + `then retry`,\n )\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const targetQ = db.collection<Record<string, unknown>>(refDesc.target).query() as any\n const byId = new Map<string, Record<string, unknown>>()\n for (const row of targetQ.toArray() as Array<Record<string, unknown>>) {\n const id = coerceCollectKey(row.id)\n if (id !== null) byId.set(id, row)\n }\n return primaryRows.map((row) => {\n const anchor = leg.from === undefined\n ? row\n : (row[leg.from] as Record<string, unknown> | null | undefined)\n const key = anchor == null ? null : coerceCollectKey(anchor[leg.field])\n return { ...row, [leg.as]: key === null ? null : byId.get(key) ?? null }\n })\n}\n\n/**\n * Attach one reverse \"collect\" leg (#810): every row of `leg.collect`\n * whose `leg.on` field references the ATTACH POINT's id lands in a\n * possibly-empty ARRAY under `leg.as` on that primary row. One\n * snapshot pass over the collect collection, hash-grouped by the `on`\n * FK — O(N+M), mirroring the forward hash-join fallback.\n *\n * The attach point is the primary record, or — with `from` (#1140) — the record\n * already attached under that alias. Matching then keys off `row[from].id`\n * instead of `row.id`, and `leg.on` must ref THAT leg's collection. A row whose\n * `from` alias is null (a dangling forward ref) gets `[]`, which is the same\n * outer-by-nature semantics a root collect leg already has for a primary record\n * with no children.\n *\n * Semantic check (first materialization, not factory time — parity\n * with join-time ref errors): `leg.on` must carry a `ref()` declared\n * on the collect collection targeting the attach point's collection.\n *\n * @internal\n */\nfunction applyCollectLeg(\n primaryRows: ReadonlyArray<Record<string, unknown>>,\n leg: { readonly collect: string; readonly on: string; readonly as: string; readonly from?: string; readonly maxRows?: number },\n mvName: string,\n source: string,\n joins: ReadonlyArray<ProjectionJoinLeg>,\n db: MVQueryContext,\n): Array<Record<string, unknown>> {\n const childColl = db.collection<Record<string, unknown>>(leg.collect)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const childQ = childColl.query() as any\n const refDesc = childQ._joinContext?.()?.resolveRef(leg.on) as { target: string } | null | undefined\n // The collection this leg matches AGAINST: the projection source for a root\n // leg, the `from` leg's own target otherwise (#1140).\n const expected = resolveLegOwner(leg, joins, source, legOwnerLookup(db))\n const expectedName = expected ?? source\n if (refDesc == null) {\n throw new MaterializedViewConfigError(\n `\"${mvName}\": projection collect leg \"${leg.as}\" requires a ref() on field \"${leg.on}\" of `\n + `collection \"${leg.collect}\" targeting \"${expectedName}\" — declare `\n + `refs: { ${leg.on}: ref('${expectedName}') } on collection \"${leg.collect}\", then retry`,\n )\n }\n if (refDesc.target !== expectedName) {\n throw new MaterializedViewConfigError(\n `\"${mvName}\": projection collect leg \"${leg.as}\" expects field \"${leg.on}\" of collection `\n + `\"${leg.collect}\" to reference ${leg.from === undefined\n ? `the projection source \"${expectedName}\"`\n : `\"${expectedName}\" (the collection behind from: \"${leg.from}\")`}`\n + `, but its ref() targets \"${refDesc.target}\"`,\n )\n }\n const maxRows = leg.maxRows ?? DEFAULT_JOIN_MAX_ROWS\n const groups = new Map<string, Array<Record<string, unknown>>>()\n for (const child of childQ.toArray() as Array<Record<string, unknown>>) {\n const key = coerceCollectKey(child[leg.on])\n // Nullish / non-scalar FK values mean \"no reference\" — same\n // narrowing as the forward join path's key coercion.\n if (key === null) continue\n const bucket = groups.get(key)\n if (bucket) bucket.push(child)\n else groups.set(key, [child])\n }\n const out: Array<Record<string, unknown>> = []\n for (const row of primaryRows) {\n // `from` matches against the attached record's id, not the primary row's.\n const anchor = leg.from === undefined\n ? row\n : (row[leg.from] as Record<string, unknown> | null | undefined)\n const key = anchor == null ? null : coerceCollectKey(anchor.id)\n const children = key === null ? [] : groups.get(key) ?? []\n if (children.length > maxRows) {\n throw new JoinTooLargeError({\n leftRows: primaryRows.length,\n rightRows: children.length,\n maxRows,\n side: 'right',\n message:\n `projection MV \"${mvName}\": collect leg \"${leg.as}\" gathered ${children.length} ` +\n `\"${leg.collect}\" rows for one \"${source}\" record, exceeding the ${maxRows}-row ` +\n `per-primary-row ceiling. Raise the ceiling via { maxRows } on the leg if the ` +\n `fan-out genuinely fits in memory, or restructure the child collection.`,\n })\n }\n out.push({ ...row, [leg.as]: children })\n }\n return out\n}\n\n/**\n * Coerce an unknown FK value into a collect-grouping key. Same\n * narrowing as the join path's `coerceRefKey` (not exported from\n * there): strings and numbers are legitimate ref values; anything\n * else is \"no reference\" and returns `null`.\n *\n * @internal\n */\nfunction coerceCollectKey(value: unknown): string | null {\n if (value === null || value === undefined) return null\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'bigint') return String(value)\n return null\n}\n\n/**\n * Run an MV's `query()` and write the result rows to the output\n * collection. Same-DEK encryption: routes through the standard\n * `Collection.put` pipeline, so the output collection's DEK is what\n * gets used (matches the v2 spec's \"same DEK as the left-most source\"\n * invariant — `Collection.put` looks up the DEK by collection name,\n * and the output collection IS the MV's owned collection).\n *\n * Stamps `_materializedFrom` onto every emitted row.\n *\n * **Tombstoning:** when `spec.onEmpty: 'delete'` (default), rows\n * that existed in a prior refresh but no longer appear in the new\n * materialized result are deleted via `Collection._internalDelete` —\n * the housekeeping bypass primitive prevents user\n * `onDelete` guards on the output collection from firing on these\n * system-internal deletes. `onEmpty: 'keep'` opts out (rows from\n * prior refreshes linger even when the new result lacks them).\n *\n * **Cost ceiling:** if the materialized row count exceeds\n * `spec.maxRows` (default 100k), throws `MaterializedViewTooLargeError`\n * before any writes hit the store — so strict-mode rollback is\n * clean.\n *\n * **Strict mode:** `spec.strict === true` re-throws on any\n * row-write failure; the active TxContext registration means the\n * source-write rolls back atomically via `revertExecuted`.\n *\n * @internal\n */\nexport const MaterializedViewExecutor = {\n async refresh(\n reg: RegisteredMV,\n accessor: MVExecutorAccessor,\n ): Promise<RefreshResult> {\n const spec = reg.spec\n const outputColl = accessor.getCollection(reg.outputCollection)\n const maxRows = spec.maxRows ?? DEFAULT_MAX_ROWS\n const onEmpty = spec.onEmpty ?? 'delete'\n const strict = spec.strict ?? false\n\n // 1. Materialize the query (branches on terminal shape). If the\n // MV declared predicates, wrap the query context the same way\n // the registry did at registration time so `.wherePredicate()`\n // calls resolve to the registered functions.\n const baseCtx = accessor.getQueryContext()\n const ctxForQuery: MVQueryContext = spec.predicates\n ? wrapDbWithPredicates(baseCtx, spec.predicates)\n : baseCtx\n // UNION-form strategies: read every arm, map to the unified\n // row shape, concatenate, then optionally groupBy + aggregate.\n // Projection-form strategies (#810): hydrate the primary source,\n // attach forward + collect legs, map, then the same optional\n // grouping tail. The single-source `query()` path is untouched.\n let rows: ReadonlyArray<Record<string, unknown>>\n if (spec.unionSources) {\n rows = await materializeUnionResult(spec, ctxForQuery)\n } else if (spec.projection) {\n rows = await materializeProjectionResult(spec, ctxForQuery)\n } else {\n const q = spec.query!(ctxForQuery)\n rows = await materializeQueryResult(q, spec.name, spec.i18nLocale, spec.i18nFields)\n }\n\n // #1007 — the post-aggregate projection. Applied here, after every form has\n // produced its finished rows, so union / projection / query all get the\n // same single definition of \"last step before materialisation\".\n if (spec.derive) rows = applyDerive(spec, rows)\n\n // #777 — exclude this MV's OWN previously-stamped output rows from the\n // input scan. A same-collection Query-form MV copies the whole source row\n // verbatim into its output, so a stale output row can itself satisfy the\n // MV's own input filter (when the filter is on a field disjoint from the\n // partition field) and get re-selected as if it were live source — the\n // row self-perpetuates after its true source is gone. Prior output is\n // never this MV's own source.\n rows = rows.filter((row) => {\n const stamp = row._materializedFrom as { mvName?: string } | undefined\n return stamp?.mvName !== spec.name\n })\n\n // 2. Cost ceiling check BEFORE any writes — keeps the rollback\n // clean if the source-write is wrapped in a transaction.\n if (rows.length > maxRows) {\n throw new MaterializedViewTooLargeError(spec.name, rows.length, maxRows)\n }\n\n const txCtx = accessor.getActiveTxContext()\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const adapter = (outputColl as any).adapter as {\n get(v: string, c: string, i: string): Promise<EncryptedEnvelope | null>\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const vaultName = (outputColl as any).vault as string\n\n // 3. Compute the post-refresh id set so we can diff against the\n // prior-emitted id set for tombstoning (when onEmpty === 'delete').\n const newIds = new Set<string>()\n const enrichedRows: Array<{ id: string; record: Record<string, unknown> }> = []\n for (const row of rows) {\n const id = spec.rowKey(row)\n newIds.add(id)\n const meta: MaterializedFromMeta = {\n mvName: spec.name,\n queryHash: reg.queryHash,\n sourceVersions: {},\n materializedAt: new Date().toISOString(),\n }\n enrichedRows.push({ id, record: { ...row, _materializedFrom: meta } })\n }\n\n // 4. Write the new rows.\n let written = 0\n let failed = 0\n for (const { id, record } of enrichedRows) {\n try {\n if (txCtx !== null) {\n const prior = await adapter.get(vaultName, reg.outputCollection, id)\n txCtx._executed.push({\n op: { type: 'put', vaultName, collectionName: reg.outputCollection, id },\n priorEnvelope: prior,\n })\n }\n if (accessor.dispatchCtx) {\n if (await putDerivedOutput(outputColl, id, record, accessor.dispatchCtx) === 'written') written++\n } else {\n await outputColl.put(id, record)\n written++\n }\n } catch (err) {\n failed++\n if (strict) throw err\n \n console.warn(`[mv] \"${spec.name}\" row write failed:`, err)\n }\n }\n\n // 5. Tombstone rows that existed before but don't appear now.\n // `onEmpty: 'keep'` skips this pass entirely. Uses\n // `_internalDelete` so a user-registered `onDelete` on the\n // output collection does NOT fire on housekeeping (composition fix).\n let deleted = 0\n const residueUndecodable: string[] = []\n const residueDeclined: string[] = []\n if (onEmpty === 'delete') {\n const priorIds = await listOutputIds(outputColl)\n for (const priorId of priorIds) {\n if (newIds.has(priorId)) continue\n // #762 — a same-collection partition MV (`output: { collection: <source>, partition }`,\n // the DERIV-PP30-001 shape) writes INTO its own source collection, so `listOutputIds`\n // also returns untouched USER source rows here. Decode each candidate and only\n // tombstone rows THIS MV stamped via `_materializedFrom.mvName` — the exact discipline\n // `invalidateMVAtRest` uses (stale.ts:151-162). An unstamped row, or one stamped by a\n // different MV, is never this MV's to delete.\n const priorEnvelope = await adapter.get(vaultName, reg.outputCollection, priorId)\n if (priorEnvelope === null) continue\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const priorDecoded = await (outputColl as any)._decodeEnvelope(priorEnvelope, priorId)\n if (priorDecoded === null) {\n // #782 part a — ownership unknown (undecodable, e.g. elevated above tier 0 on a\n // tiered output collection): can't rule out this being THIS MV's own stamped row.\n // Surface it rather than silently skip (mirrors invalidateMVAtRest's #776 posture —\n // previously this leg had NO residue channel at all).\n residueUndecodable.push(`${reg.outputCollection}:${priorId}`)\n continue\n }\n const priorStampedBy =\n typeof priorDecoded === 'object'\n ? (priorDecoded as Record<string, unknown>)._materializedFrom as { mvName?: string } | undefined\n : undefined\n if (priorStampedBy?.mvName !== spec.name) continue\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const outAny = outputColl as any\n if (typeof outAny._internalDelete === 'function') {\n // #776 part b — gate on the boolean, matching invalidateMVAtRest's discipline\n // (stale.ts): `_internalDelete` returns `false` for a #718 elevated-skip (no\n // erasure happened), and that must not inflate the tombstone count.\n if (await outAny._internalDelete(priorId, txCtx)) {\n deleted++\n } else {\n // #782 part b — decoded AND stamp-owned, but erasure was declined (#718\n // tier-elevation gate). Ownership IS confirmed here — a real silent survival,\n // not a legit stamp-mismatch skip. Surface it too.\n residueDeclined.push(`${reg.outputCollection}:${priorId}`)\n }\n } else {\n // Defensive fallback — should never hit in real flow since\n // every Collection has `_internalDelete`.\n await outputColl.delete(priorId)\n deleted++\n }\n } catch (err) {\n failed++\n if (strict) throw err\n\n console.warn(`[mv] \"${spec.name}\" tombstone failed for id=\"${priorId}\":`, err)\n }\n }\n }\n\n return { written, deleted, failed, residueUndecodable, residueDeclined }\n },\n}\n\n/**\n * List ids currently present in the MV's output collection via the\n * adapter directly (avoids triggering the lazy resolve-on-read path\n * we're INSIDE). Returns an empty array if the collection doesn't\n * exist or the adapter doesn't surface a list method.\n *\n * @internal\n */\nasync function listOutputIds(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n outputColl: Collection<any>,\n): Promise<string[]> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const cAny = outputColl as any\n const adapter = cAny.adapter as { list?: (v: string, c: string) => Promise<readonly string[]> }\n const vault = cAny.vault as string\n const name = cAny.name as string\n if (typeof adapter?.list !== 'function') return []\n try {\n const ids = await adapter.list(vault, name)\n return [...ids]\n } catch {\n return []\n }\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-lookup/indexing/strategy.ts","../src/with-lookup/indexing/persisted-indexes.ts","../src/with-lookup/indexing/lazy-builder.ts"],"sourcesContent":["/**\n * Strategy seam between core Collection and the optional indexing\n * service. Core imports `IndexingStrategy` and `IndexState` as\n * TYPE-ONLY symbols and `NO_INDEXING` as a tiny runtime stub.\n *\n * The heavy classes — `CollectionIndexes`, `PersistedCollectionIndex`,\n * `LazyQuery` — are only instantiated inside the `withIndexing()`\n * factory under `./active.ts`, which in turn is only reachable through\n * the `@noy-db/hub/indexing` subpath export. A consumer that never\n * imports the subpath ships none of those classes in their bundle\n * (ESM tree-shaking + hub's `\"sideEffects\": false`).\n *\n * @internal\n */\n\nimport type { CollectionIndexes, IndexDef } from './eager-indexes.js'\nimport type { PersistedCollectionIndex } from './persisted-indexes.js'\n\n/**\n * Per-collection container for whatever mirrors the active strategy\n * decided to materialize. Both accessors may return `null` — they do\n * for `NO_INDEXING`, and `getEagerIndexes` returns null in a\n * lazy-mode collection even when indexing is active (lazy uses the\n * persisted mirror instead).\n *\n * `isEnabled` is a cheap guard so collection code can short-circuit\n * the full indexing path without inspecting either mirror.\n *\n * @internal\n */\nexport interface IndexState {\n readonly isEnabled: boolean\n getEagerIndexes(): CollectionIndexes | null\n getPersistedIndexes(): PersistedCollectionIndex | null\n}\n\n/**\n * Factory that builds one `IndexState` per Collection. Called exactly\n * once inside each Collection constructor with the declared\n * `IndexDef[]` and the lazy-mode flag (so lazy collections get the\n * persisted mirror and eager collections get the in-memory one).\n *\n * @internal\n */\nexport interface IndexingStrategy {\n createState(args: {\n readonly defs: readonly IndexDef[]\n readonly lazy: boolean\n }): IndexState\n}\n\n/**\n * No-indexing stub. Every Collection defaults to this; it returns a\n * cheap `IndexState` whose mirrors are both `null`. Collection code\n * null-checks both accessors and short-circuits, so no indexing code\n * path runs and the heavy classes never arrive in the bundle.\n *\n * @internal\n */\nexport const NO_INDEXING: IndexingStrategy = {\n createState() {\n return DISABLED_STATE\n },\n}\n\nconst DISABLED_STATE: IndexState = {\n isEnabled: false,\n getEagerIndexes: () => null,\n getPersistedIndexes: () => null,\n}\n","/**\n * Persistent, encrypted secondary indexes for lazy-mode collections.\n *\n * Parallel to the in-memory `CollectionIndexes` used by eager mode (see\n * `packages/hub/src/query/indexes.ts`): same logical surface, but entries\n * are materialised as encrypted side-car records (`_idx/<field>/<recordId>`)\n * and bulk-loaded into an in-memory mirror on first query.\n *\n * This module only owns the id-namespace convention, the in-memory mirror,\n * and the typed errors. Write-path integration (PR 2 / ), query-planner\n * dispatch (PR 3 / , PR 4 / ), and the rebuild/reconcile utilities\n * (PR 5 / ) live in other files.\n *\n * See the design spec for the full architecture + threat model.\n */\n\n/**\n * Reserved id prefix for encrypted index side-car records.\n * Matches the existing `_keyring`, `_ledger_deltas/…`, `_meta/handle`\n * conventions inside a collection's id namespace.\n */\nexport const IDX_PREFIX = '_idx/' as const\n\n/**\n * Encode the side-car record id for a (field, recordId) pair.\n *\n * Format: `_idx/<field>/<recordId>` — no escaping. Field names may contain\n * dots (for dotted-path access consistent with eager-mode `readPath`);\n * record ids may contain slashes. The first two slash-separated segments\n * are `_idx` and the field; everything after the *second* slash is the\n * record id verbatim.\n */\nexport function encodeIdxId(field: string, recordId: string): string {\n return `${IDX_PREFIX}${field}/${recordId}`\n}\n\n/**\n * Decode a side-car id back into `{ field, recordId }`, or `null` if the\n * input is not a well-formed idx id. A well-formed id is:\n * - prefixed with `_idx/`\n * - contains a field segment (non-empty, no slashes)\n * - contains a record-id segment (non-empty, may contain slashes)\n */\nexport function decodeIdxId(id: string): { field: string; recordId: string } | null {\n if (!id.startsWith(IDX_PREFIX)) return null\n const rest = id.slice(IDX_PREFIX.length)\n const firstSlash = rest.indexOf('/')\n if (firstSlash <= 0) return null\n const field = rest.slice(0, firstSlash)\n const recordId = rest.slice(firstSlash + 1)\n if (recordId.length === 0) return null\n return { field, recordId }\n}\n\n/**\n * Fast-path predicate for discriminating side-car ids from regular record\n * ids and other reserved namespaces. Used by the hub to filter `list()`\n * results during bulk-load of the in-memory mirror.\n */\nexport function isIdxId(id: string): boolean {\n return decodeIdxId(id) !== null\n}\n\n/**\n * Sorted-value entry returned by `orderedBy()`. Mirrors the body shape\n * used by the write path — but `orderedBy` emits them already sorted by\n * `value` in the requested direction. Consumers (PR 4 / ) treat the\n * array as immutable and paginate via a numeric offset.\n *\n * **Note on `value`:** as of, this is the ORIGINAL TYPED\n * value (number, Date, boolean, etc.), not the stringified bucket key.\n * That's what lets range predicates and `orderedBy` compare numerically\n * instead of stumbling into `'10' < '2'` on `String(n)`.\n */\nexport interface OrderedEntry {\n readonly recordId: string\n readonly value: unknown\n}\n\n/**\n * Bulk-load row shape accepted by `ingest()`. The `value` field is the\n * decrypted index body's `value` field verbatim.\n */\nexport interface IngestRow {\n readonly recordId: string\n readonly value: unknown\n}\n\n/**\n * In-memory mirror of the persisted index side-car records for a single\n * collection. Populated by bulk-loading `_idx/<field>/*` ids on first\n * query and maintained incrementally by `Collection.put()` / `.delete()`\n * via `upsert()` / `remove()`.\n *\n * API surface is deliberately parallel to `CollectionIndexes` (eager mode)\n * so the query planner in PR 3/4 can dispatch to either polymorphically.\n *\n * Lifecycle:\n * - `declare(field)` — accept the field as indexable (idempotent)\n * - `ingest(field, rows[])` — bulk-load from decrypted index bodies\n * - `upsert(recordId, field, newValue, previousValue)` — incremental update\n * - `remove(recordId, field, value)` — incremental remove\n * - `lookupEqual(field, value)` / `lookupIn(field, values)` — equality reads\n * - `orderedBy(field, dir)` — sorted iteration for orderBy\n * - `clear()` — drop all buckets (invalidation / rotation)\n */\n/**\n * Per-field storage: the equality bucket map AND a parallel table of typed\n * values keyed by recordId. The typed table exists so range predicates\n * and `orderedBy` can compare on the original typed value rather\n * than the stringified bucket key — String(10) < String(2) is the classic\n * landmine `stringifyKey` introduces for numeric fields.\n */\ninterface PersistedFieldState {\n readonly buckets: Map<string, Set<string>>\n readonly values: Map<string, unknown>\n}\n\n/**\n * Structured index definition. Single-field indexes carry just a field\n * name; composite indexes carry the ordered list of fields and\n * the synthetic `key` (= fields joined by `COMPOSITE_DELIMITER`) used\n * as the bucket-map key and side-car envelope id segment.\n */\nexport type PersistedIndexDef =\n | { readonly kind: 'single'; readonly field: string; readonly key: string }\n | { readonly kind: 'composite'; readonly fields: readonly string[]; readonly key: string }\n\n/**\n * Delimiter used to synthesize a composite-index key from an ordered\n * field list. Intentionally a character that is extremely unusual in\n * JavaScript object keys (`|`) so collision with a literal field name\n * is vanishingly rare in practice. Composite declarations whose field\n * names contain `|` are rejected at declare-time with an explicit\n * error.\n */\nexport const COMPOSITE_DELIMITER = '|'\n\nexport function compositeKey(fields: readonly string[]): string {\n return fields.join(COMPOSITE_DELIMITER)\n}\n\nexport class PersistedCollectionIndex {\n private readonly indexes = new Map<string, PersistedFieldState>()\n private readonly defs = new Map<string, PersistedIndexDef>()\n\n /**\n * Per-field bucket-key canonicalizer (#677 — lazy twin of #672's\n * `CollectionIndexes.setCanonicalizer`), registered ONCE via\n * {@link setCanonicalizer} when the collection wires indexing up\n * (money-aware via `ViaPipeline.canonicalizeIndexKey`). Consulted by\n * EVERY bucket-mutation site — `ingest`/`upsert`/`remove` — so bucket\n * membership is symmetric: a legacy non-canonical stored value (e.g.\n * `'0100'`) and a canonical one (`'100'`) land in the SAME bucket,\n * mirroring `eager-indexes.ts`'s `addToIndex`/`removeFromIndex`.\n */\n private canonicalize?: (field: string, value: unknown) => string | undefined\n\n /**\n * Register the bucket-key canonicalizer. Called once, where the\n * collection constructs its indexing state — safe to call again (the\n * closure is simply replaced) but there is normally only one caller.\n */\n setCanonicalizer(fn: (field: string, value: unknown) => string | undefined): void {\n this.canonicalize = fn\n }\n\n /**\n * Declare a single-field index. Subsequent `upsert` / `ingest` calls\n * populate the in-memory mirror; calls before `declare` are no-ops\n * (tolerant bulk-load ordering). Idempotent.\n */\n declare(field: string): void {\n if (this.indexes.has(field)) return\n this.indexes.set(field, { buckets: new Map(), values: new Map() })\n this.defs.set(field, { kind: 'single', field, key: field })\n }\n\n /**\n * Declare a composite (multi-field) index. The synthetic\n * key is `fields.join('|')`; it doubles as the in-memory map key and\n * the `_idx/<key>/<recordId>` side-car field segment. Callers upsert\n * and lookup via the same `key` as single-field indexes, just with a\n * tuple value (JSON-stringified for bucketing).\n */\n declareComposite(fields: readonly string[]): void {\n if (fields.length === 0) {\n throw new Error('declareComposite: fields array must be non-empty')\n }\n for (const f of fields) {\n if (f.includes(COMPOSITE_DELIMITER)) {\n throw new Error(\n `declareComposite: field \"${f}\" contains the composite delimiter ` +\n `\"${COMPOSITE_DELIMITER}\" — pick a different field name or open an ` +\n `issue to add hash-based composite keys.`,\n )\n }\n }\n const key = compositeKey(fields)\n if (this.indexes.has(key)) return\n this.indexes.set(key, { buckets: new Map(), values: new Map() })\n this.defs.set(key, { kind: 'composite', fields: [...fields], key })\n }\n\n /**\n * Every declared index's structured definition. Collection walks this\n * when materialising side-cars on put/delete so it can extract a\n * single-field value or a composite tuple appropriately.\n */\n definitions(): PersistedIndexDef[] {\n return [...this.defs.values()]\n }\n\n /** True if `field` has been declared as indexable on this mirror. */\n has(field: string): boolean {\n return this.indexes.has(field)\n }\n\n /** All declared field names, in declaration order. */\n fields(): string[] {\n return [...this.indexes.keys()]\n }\n\n /**\n * Bulk-load the mirror from decrypted index bodies. Intended to be\n * called once per field after reading the collection's `_idx/<field>/*`\n * side-cars. Safe to call twice with the same rows — bucket Sets\n * deduplicate recordIds. If `field` is not declared, this is a no-op\n * (tolerates the case where bulk-load runs before `declare()` lands).\n */\n ingest(field: string, rows: readonly IngestRow[]): void {\n const state = this.indexes.get(field)\n if (!state) return\n for (const row of rows) {\n addToState(state, row.recordId, row.value, field, this.canonicalize)\n }\n }\n\n /**\n * Incrementally update a record's index entry for one field. Called by\n * `Collection.put()` after the main write succeeds. If\n * `previousValue` is non-null, the record is removed from the old\n * bucket first — this is the update path. Pass `null` for fresh adds.\n * No-op if the field is not declared.\n */\n upsert(recordId: string, field: string, newValue: unknown, previousValue: unknown): void {\n const state = this.indexes.get(field)\n if (!state) return\n if (previousValue !== null && previousValue !== undefined) {\n removeFromState(state, recordId, previousValue, field, this.canonicalize)\n }\n addToState(state, recordId, newValue, field, this.canonicalize)\n }\n\n /**\n * Remove a record from the index for one field. Called by\n * `Collection.delete()`. No-op if the field is not declared or\n * the record isn't in the bucket. Empty buckets are dropped to keep\n * the Map clean.\n */\n remove(recordId: string, field: string, value: unknown): void {\n const state = this.indexes.get(field)\n if (!state) return\n removeFromState(state, recordId, value, field, this.canonicalize)\n }\n\n /**\n * Drop all bucket data while preserving field declarations. Called on\n * invalidation (incoming sync changes, keyring rotation) — the next\n * query re-populates via `ingest`.\n */\n clear(): void {\n for (const state of this.indexes.values()) {\n state.buckets.clear()\n state.values.clear()\n }\n }\n\n /**\n * Equality lookup — return the set of record ids whose `field` matches\n * `value`. Returns `null` if the field is not declared (caller falls\n * back to scan or throws `IndexRequiredError`). Returns a shared empty\n * set if the field is declared but no record matches — that set MUST\n * NOT be mutated by the caller.\n *\n * `value` is bucketed via the plain {@link stringifyKey} — it is NOT\n * canonicalized here. Callers on a Via-covered field (money) canonicalize\n * `value` themselves BEFORE calling (see `lazy-builder.ts`'s\n * `resolveCandidateIds()`, #677) — the SAME caller-canonicalizes split\n * `eager-indexes.ts`'s `lookupEqual`/`lookupIn` use (see `kernel/query/\n * builder.ts`'s `candidateRecords()`), so probing here twice never\n * double-canonicalizes.\n */\n lookupEqual(field: string, value: unknown): ReadonlySet<string> | null {\n const state = this.indexes.get(field)\n if (!state) return null\n const key = stringifyKey(value)\n return state.buckets.get(key) ?? EMPTY_SET\n }\n\n /**\n * Set lookup — return the union of record ids whose `field` matches any\n * of `values`. Returns `null` if the field is not declared. Returns a\n * fresh (non-shared) Set — safe for the caller to mutate. Same\n * caller-canonicalizes contract as {@link lookupEqual} (#677).\n */\n lookupIn(field: string, values: readonly unknown[]): ReadonlySet<string> | null {\n const state = this.indexes.get(field)\n if (!state) return null\n const out = new Set<string>()\n for (const value of values) {\n const bucket = state.buckets.get(stringifyKey(value))\n if (bucket) for (const id of bucket) out.add(id)\n }\n return out\n }\n\n /**\n * Range lookup. Return record ids whose indexed value\n * satisfies the predicate. Comparison happens on the ORIGINAL TYPED\n * value carried in `state.values` — so numeric `<` sorts numerically,\n * not lexicographically on `String(n)`. Returns `null` if the field\n * is not declared.\n *\n * Supported ops: `'<'`, `'<='`, `'>'`, `'>='`, `'between'`. For\n * `'between'`, `value` is `[lo, hi]` and both bounds are inclusive\n * (matches the eager-mode operator contract in `predicate.ts`).\n *\n * NOT canonicalized (#677): this method compares `value` against the\n * ORIGINAL TYPED `state.values`, not a stringified bucket key, so a\n * `canonicalizeIndexKey`-shaped (`string | undefined`) result would not\n * even type-check as a bound here. This does NOT mirror eager mode the\n * way `lookupEqual`/`lookupIn` do: `moneyIndexProbe` (`via/money/\n * where.ts`) never emits a range probe, so EAGER mode's `candidateRecords`\n * never calls an index for a range clause and always scans.\n *\n * LAZY mode's `resolveCandidateIds()` (`lazy-builder.ts`) no longer\n * dispatches a Via-covered (e.g. money) range clause through THIS method\n * at all (#684) — it enumerates the field's indexed ids via `orderedBy()`\n * instead and leaves the raw/typed comparison here for the (now\n * Via-aware) post-filter to redo correctly in scaled-int space. A\n * NON-via range clause still dispatches through this method exactly as\n * before — the raw/typed comparison here is correct and sufficient for\n * fields with no decode-transforming Via binding.\n */\n lookupRange(\n field: string,\n op: '<' | '<=' | '>' | '>=' | 'between',\n value: unknown,\n ): ReadonlySet<string> | null {\n const state = this.indexes.get(field)\n if (!state) return null\n const out = new Set<string>()\n for (const [recordId, live] of state.values) {\n if (live === undefined || live === null) continue\n if (matchesRange(live, op, value)) out.add(recordId)\n }\n return out\n }\n\n /**\n * Sorted iteration — return every entry on `field` as an\n * `OrderedEntry[]`, sorted by the ORIGINAL TYPED value (no more\n * `'10' < '2'` surprises on numeric fields). Consumers paginate with\n * a numeric offset. `OrderedEntry.value` is the typed value.\n */\n orderedBy(field: string, dir: 'asc' | 'desc'): readonly OrderedEntry[] | null {\n const state = this.indexes.get(field)\n if (!state) return null\n const entries: OrderedEntry[] = []\n for (const [recordId, value] of state.values) {\n entries.push({ recordId, value })\n }\n entries.sort((a, b) => compareTyped(a.value, b.value))\n if (dir === 'desc') entries.reverse()\n return entries\n }\n}\n\nconst EMPTY_SET: ReadonlySet<string> = new Set()\n\n/**\n * Canonicalize a value into a bucket key. Deliberately identical to the\n * eager-mode `stringifyKey` in `query/indexes.ts` so semantics match. When\n * `query/indexes.ts` changes its coercion rules, update this in lockstep.\n *\n * null / undefined values are NOT indexed — callers who pass them to\n * `upsert` / `remove` short-circuit before reaching this function; the\n * sentinel here exists only to make `lookupEqual(field, null)` return\n * an empty bucket (rather than matching some arbitrary record).\n */\nfunction stringifyKey(value: unknown): string {\n if (value === null || value === undefined) return '\\0NULL\\0'\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean') return String(value)\n if (value instanceof Date) return value.toISOString()\n // composite index values are tuple arrays. JSON.stringify\n // gives a delimiter-safe, order-preserving canonical form so buckets\n // for `['c-A', '2026-Q1']` and `['c-A', '2026-Q2']` never collide.\n if (Array.isArray(value)) {\n const parts: string[] = []\n for (const el of value) parts.push(stringifyKey(el))\n return JSON.stringify(parts)\n }\n return '\\0OBJECT\\0'\n}\n\nfunction addToState(\n state: PersistedFieldState,\n recordId: string,\n value: unknown,\n field: string,\n canonicalize?: (field: string, value: unknown) => string | undefined,\n): void {\n if (value === null || value === undefined) return\n const key = canonicalize?.(field, value) ?? stringifyKey(value)\n let bucket = state.buckets.get(key)\n if (!bucket) {\n bucket = new Set()\n state.buckets.set(key, bucket)\n }\n bucket.add(recordId)\n state.values.set(recordId, value)\n}\n\nfunction removeFromState(\n state: PersistedFieldState,\n recordId: string,\n value: unknown,\n field: string,\n canonicalize?: (field: string, value: unknown) => string | undefined,\n): void {\n if (value === null || value === undefined) return\n const key = canonicalize?.(field, value) ?? stringifyKey(value)\n const bucket = state.buckets.get(key)\n if (bucket) {\n bucket.delete(recordId)\n if (bucket.size === 0) state.buckets.delete(key)\n }\n state.values.delete(recordId)\n}\n\n/**\n * Range-predicate comparator. Runs on the ORIGINAL TYPED value so numeric\n * fields sort numerically (not lexicographically on `String(n)`). ISO-8601\n * date strings already sort correctly lexicographically; Date instances\n * compare via `getTime()` before the string branch to keep the contract\n * honest regardless of which form survived serialization.\n */\nfunction matchesRange(\n live: unknown,\n op: '<' | '<=' | '>' | '>=' | 'between',\n bound: unknown,\n): boolean {\n if (op === 'between') {\n if (!Array.isArray(bound) || bound.length !== 2) return false\n return compareTyped(live, bound[0]) >= 0 && compareTyped(live, bound[1]) <= 0\n }\n const cmp = compareTyped(live, bound)\n switch (op) {\n case '<': return cmp < 0\n case '<=': return cmp <= 0\n case '>': return cmp > 0\n case '>=': return cmp >= 0\n }\n}\n\nfunction compareTyped(a: unknown, b: unknown): number {\n if (a === undefined || a === null) return b === undefined || b === null ? 0 : 1\n if (b === undefined || b === null) return -1\n if (typeof a === 'number' && typeof b === 'number') return a - b\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime()\n if (typeof a === 'string' && typeof b === 'string') return a < b ? -1 : a > b ? 1 : 0\n if (typeof a === 'boolean' && typeof b === 'boolean') {\n return a === b ? 0 : a ? 1 : -1\n }\n // Mixed/unsupported types: deliberately treat as equal so sort stays\n // stable. Matches the eager-mode `compareValues` contract in\n // builder.ts — we don't silently coerce arbitrary objects to strings\n // (which would be meaningless) nor throw (which would be hostile).\n return 0\n}\n","/**\n * Lazy-mode query builder.\n *\n * Companion to `Query<T>` in `builder.ts`, but built for collections in lazy\n * mode where `snapshot()` is unavailable — records live in the adapter and\n * are pulled on demand. Dispatches through `PersistedCollectionIndex` to\n * resolve a candidate record-id set, then decrypts only those records.\n *\n * Scope:\n * - `.where(field, '==' | 'in', value)` — dispatched through the index\n * - `.where(field, other-op, value)` — evaluated against the decrypted\n * candidate set (non-indexed ops still require the field to be indexed\n * — we need SOMETHING to scope the candidate set)\n * - `.orderBy(field, dir?)` — dispatched through `orderedBy` when no\n * `==`/`in` clause is present; otherwise applied as an in-memory sort\n * over the candidate set\n * - `.limit(n)` / `.offset(n)` — page slice after filtering\n * - `.toArray()` / `.first()` / `.count()` — terminals\n *\n * Every field referenced by a where or orderBy clause MUST be indexed;\n * otherwise `toArray()` throws `IndexRequiredError`. This is deliberate:\n * silent scan-fallback would hide the very performance cliff that lazy-mode\n * indexes exist to prevent (see `docs/architecture.md` §indexes).\n */\n\nimport type { Clause, FieldClause, Operator } from '../../kernel/query/predicate.js'\nimport { evaluateClause, readPath } from '../../kernel/query/predicate.js'\nimport type { PersistedCollectionIndex } from './persisted-indexes.js'\nimport { IndexRequiredError, FieldNotQueryableError } from '../../kernel/errors.js'\nimport type { QueryField } from '../../kernel/types.js'\nimport type { ViaPipeline } from '../../kernel/via/pipeline.js'\n\nexport interface LazyOrderBy {\n readonly field: string\n readonly direction: 'asc' | 'desc'\n}\n\n/**\n * Source abstraction the LazyQuery runs against. Collection implements it.\n * Kept minimal so the builder stays test-friendly.\n */\nexport interface LazyQuerySource<T> {\n readonly collectionName: string\n readonly persistedIndexes: PersistedCollectionIndex\n /**\n * Per-field bucket-key canonicalizer (#677 — lazy twin of the eager\n * `candidateRecords()` probe-resolution in `kernel/query/builder.ts`).\n * Consulted in `resolveCandidateIds()` for `==`/`in` clause values\n * BEFORE calling `lookupEqual`/`lookupIn`, so a mixed-era stored value\n * (money) probes the SAME canonical bucket the write path now\n * populates. A narrow closure — not the whole `ViaPipeline` — keeps\n * the with-lookup/kernel seam thin. `undefined` (no via, or no binding\n * covers the field) is the common case: falls back to the raw value,\n * unchanged from today.\n */\n canonicalizeIndexKey?(field: string, value: unknown): string | undefined\n /** Ensure `_idx/<field>/*` side-cars have been bulk-loaded into the mirror. */\n ensurePersistedIndexesLoaded(): Promise<void>\n /**\n * Decrypt one record by id in RAW (pre-`present()`, stored-form) shape,\n * or return null if it's gone (#684 — the raw-fetch seam). Used by the\n * post-filter (`toArray()`) so a `clause.via.evaluate` (e.g. money) sees\n * the same operand/actual-value space as eager's `filterRecords` — the\n * DECODED form (e.g. money's canonical decimal) is only materialized\n * for survivors, via {@link decodeRecord}.\n */\n getRawRecord(id: string): Promise<unknown>\n /**\n * Apply this collection's locale/virtual-field decode (`present()`) to a\n * RAW record already known to match — the output-side twin of\n * {@link getRawRecord}. Mirrors eager's `Query.decodeVia`.\n */\n decodeRecord(record: unknown): Promise<T>\n /**\n * This collection's compiled Via pipeline (money, i18n, …), read lazily\n * (a method, not a captured value) so a late `_setVia` (#666) is honored\n * — same closure discipline as {@link canonicalizeIndexKey}. `undefined`\n * when the collection has no Via-covered fields.\n */\n via(): ViaPipeline | undefined\n}\n\ninterface LazyPlan {\n readonly clauses: readonly FieldClause[]\n readonly orderBy: readonly LazyOrderBy[]\n readonly limit: number | undefined\n readonly offset: number\n}\n\nconst EMPTY_PLAN: LazyPlan = {\n clauses: [],\n orderBy: [],\n limit: undefined,\n offset: 0,\n}\n\nexport class LazyQuery<T, S extends keyof T = never, Q extends keyof T & string = never> {\n private readonly source: LazyQuerySource<T>\n private readonly plan: LazyPlan\n\n constructor(source: LazyQuerySource<T>, plan: LazyPlan = EMPTY_PLAN) {\n this.source = source\n this.plan = plan\n }\n\n where<V>(field: QueryField<T, S, Q>, op: Operator, value: V): LazyQuery<T, S, Q> {\n // A Via-covered field (e.g. money) compares in major units, BigInt-exact\n // in scaled space — same build-time operand rewrite + `clause.via`\n // attachment as `Query.where()` (`kernel/query/builder.ts`) and\n // `ScanBuilder.where()` (`kernel/query/scan-builder.ts`). Before #684\n // this method built a bare clause with no `clause.via` at all, so the\n // post-filter fell through to the generic (non-Via-aware) comparison —\n // see `toArray()` below for the raw-record post-filter this feeds.\n const via = this.source.via()\n // Consults the Via pipeline's posture BEFORE building a clause — same\n // gate as `Query.where()` (builder.ts) / `ScanBuilder.where()`\n // (scan-builder.ts): a field whose posture is `queryable: 'none'`\n // (e.g. a `blobFields` slot) throws `FieldNotQueryableError` here, at\n // the call site, instead of building a bare clause that later surfaces\n // as a deferred `IndexRequiredError` from `toArray()`.\n if (via?.postureFor(field)?.queryable === 'none') throw new FieldNotQueryableError(field)\n const viaClause = via?.buildClause(field, op, value)\n const clause: FieldClause = viaClause\n ? {\n type: 'field',\n field,\n op,\n value,\n via: {\n brand: viaClause.brand,\n payload: viaClause.payload,\n evaluate: (actual: unknown, evalOp: string) => via!.evaluateClause(viaClause, actual, evalOp),\n indexValue: via!.indexProbe(viaClause, op),\n },\n }\n : { type: 'field', field, op, value }\n return new LazyQuery<T, S, Q>(this.source, {\n ...this.plan,\n clauses: [...this.plan.clauses, clause],\n })\n }\n\n orderBy(field: QueryField<T, S>, direction: 'asc' | 'desc' = 'asc'): LazyQuery<T, S, Q> {\n return new LazyQuery<T, S, Q>(this.source, {\n ...this.plan,\n orderBy: [...this.plan.orderBy, { field, direction }],\n })\n }\n\n limit(n: number): LazyQuery<T, S, Q> {\n return new LazyQuery<T, S, Q>(this.source, { ...this.plan, limit: n })\n }\n\n offset(n: number): LazyQuery<T, S, Q> {\n return new LazyQuery<T, S, Q>(this.source, { ...this.plan, offset: n })\n }\n\n async toArray(): Promise<T[]> {\n await this.source.ensurePersistedIndexesLoaded()\n\n const touchedFields = collectTouchedFields(this.plan)\n const missingFields = touchedFields.filter(f => !isFieldIndexed(f, this.source.persistedIndexes))\n if (missingFields.length > 0) {\n throw new IndexRequiredError({\n collection: this.source.collectionName,\n touchedFields,\n missingFields,\n })\n }\n\n const candidateIds = this.resolveCandidateIds()\n if (candidateIds === null) {\n // No usable driver — every touched field is indexed but no clause\n // pins the candidate set. This happens when a query only uses\n // operators other than `==`/`in` and no `orderBy` clause is\n // present — we refuse to enumerate the whole index, because that\n // defeats the purpose of lazy mode.\n throw new IndexRequiredError({\n collection: this.source.collectionName,\n touchedFields,\n missingFields: touchedFields,\n })\n }\n\n // #684: post-filter the RAW record (mirrors eager's filterRecords on the\n // raw snapshot) so `clause.via.evaluate` sees the same stored-form\n // operand/actual-value space `buildClause` quantized into at where()\n // time. Survivors stay RAW here — #695 sorts them via-aware in that\n // same stored-form space (mirroring eager's `sortRecords(result, ...,\n // source.via, ...)` in `kernel/query/builder.ts`) before anything is\n // decoded (`present()`).\n const survivors: T[] = []\n for (const id of candidateIds) {\n const raw = await this.source.getRawRecord(id)\n if (raw === null) continue\n if (!matchesAll(raw, this.plan.clauses)) continue\n survivors.push(raw as T)\n }\n\n // #695: sort the RAW survivors via-aware — a money field's stored form\n // (scaled-integer string) sorts lexicographically wrong under the\n // generic comparator ('10.00' < '2.00' once decoded); `via.compareForOrder`\n // compares in the correct (BigInt-exact scaled) space, same as eager.\n const sorted = this.plan.orderBy.length > 0\n ? sortRecords(survivors, this.plan.orderBy, this.source.via())\n : survivors\n\n const offset = this.plan.offset > 0 ? this.plan.offset : 0\n const page = this.plan.limit === undefined\n ? sorted.slice(offset)\n : sorted.slice(offset, offset + this.plan.limit)\n\n // Decode only the final page — fewer decodes than the pre-#695 code,\n // which decoded every survivor before sorting/slicing.\n return Promise.all(page.map(r => this.source.decodeRecord(r)))\n }\n\n async first(): Promise<T | null> {\n const out = await this.limit(1).toArray()\n return out.length > 0 ? out[0]! : null\n }\n\n async count(): Promise<number> {\n const out = await this.toArray()\n return out.length\n }\n\n /**\n * Resolve the candidate record-id set to decrypt. Returns null when the\n * query has no usable driver — no `==`/`in` clause and no `orderBy`\n * clause that can scope the scan. Callers interpret null as\n * IndexRequiredError (see `toArray`).\n */\n private resolveCandidateIds(): readonly string[] | null {\n const idx = this.source.persistedIndexes\n\n // prefer a composite index when the query's `==`\n // clauses cover every field of one declared composite. The\n // composite mirror lookup is O(matches) vs single-field +\n // post-filter on the decrypted candidate set.\n const eqMap = new Map<string, unknown>()\n const viaFields = new Set<string>()\n for (const clause of this.plan.clauses) {\n if (clause.op === '==') {\n eqMap.set(clause.field, clause.value)\n // #696: the composite mirror buckets on `stringifyKey(tuple)` built\n // from the RAW clause values — the per-field money canonicalizer\n // (`canonicalizeIndexKey`) only ever keys off a single field name,\n // never the joined composite key, so a Via-covered field's tuple\n // slot never lands in the same bucket a canonical write produced.\n // Track which `==` fields are Via-covered so the composite branch\n // below can skip them and fall through to the single-field\n // Via-aware path (already correct for money) instead.\n if (clause.via) viaFields.add(clause.field)\n }\n }\n if (eqMap.size >= 2) {\n for (const def of idx.definitions()) {\n if (def.kind !== 'composite') continue\n if (def.fields.some(f => viaFields.has(f))) continue\n if (def.fields.every(f => eqMap.has(f))) {\n const tuple = def.fields.map(f => eqMap.get(f))\n const ids = idx.lookupEqual(def.key, tuple)\n if (ids) return [...ids]\n }\n }\n }\n\n for (const clause of this.plan.clauses) {\n if (clause.op === '==') {\n if (clause.via) {\n // #684: prefer `clause.via.indexValue` — the same STORED-form\n // probe operand eager's `candidateRecords()` resolves first\n // (`kernel/query/builder.ts`). `undefined` means the binding\n // declined to probe this op/payload (e.g. money multi-mode) — no\n // sound equality bucket exists. Eager's `candidateRecords()`\n // handles this by skipping the index-eligibility check for the\n // clause and falling back to a full scan (`builder.ts:1176`); the\n // lazy equivalent (#684 review-fix 2) is the same move the RANGE branch\n // below already makes — enumerate the field's full indexed id\n // set via `orderedBy` and let the (now via-aware) post-filter in\n // `toArray()` decide, instead of throwing `IndexRequiredError`.\n if (clause.via.indexValue === undefined) {\n const entries = idx.orderedBy(clause.field, 'asc')\n if (entries) return entries.map(e => e.recordId)\n continue\n }\n const ids = idx.lookupEqual(clause.field, clause.via.indexValue)\n if (ids) return [...ids]\n continue\n }\n // #677: canonicalize the probe value BEFORE the lookup — same\n // \"resolve before lookupEqual\" shape as eager's `candidateRecords()`,\n // but through the narrower `canonicalizeIndexKey` seam, used only on\n // this NON-via path. `undefined` (no via coverage) falls back to the\n // raw clause value, unchanged from today.\n const probe = this.source.canonicalizeIndexKey?.(clause.field, clause.value) ?? clause.value\n const ids = idx.lookupEqual(clause.field, probe)\n if (ids) return [...ids]\n } else if (clause.op === 'in' && Array.isArray(clause.value)) {\n if (clause.via) {\n // Same #684 review-fix 2 fallback as the `==` branch above — a via\n // payload that declines to probe (e.g. money multi-mode) still\n // has a sound candidate superset via `orderedBy`.\n if (clause.via.indexValue === undefined) {\n const entries = idx.orderedBy(clause.field, 'asc')\n if (entries) return entries.map(e => e.recordId)\n continue\n }\n if (!Array.isArray(clause.via.indexValue)) continue\n const ids = idx.lookupIn(clause.field, clause.via.indexValue)\n if (ids) return [...ids]\n continue\n }\n const probes = clause.value.map(v => this.source.canonicalizeIndexKey?.(clause.field, v) ?? v)\n const ids = idx.lookupIn(clause.field, probes)\n if (ids) return [...ids]\n } else if (isRangeOp(clause.op)) {\n if (clause.via) {\n // #684: a Via-covered range clause (e.g. money) cannot trust\n // `lookupRange` — it compares the ORIGINAL TYPED stored value,\n // wrong space for a binding whose stored form isn't directly\n // orderable that way (money: BigInt-exact scaled-int compare).\n // No binding emits a range `indexProbe` either (see\n // `moneyIndexProbe`), so there's no sound bucket lookup at all.\n // Enumerate the field's full indexed id set via `orderedBy` — the\n // same superset a scan would consider, just scoped to indexed ids\n // — and let the (now via-aware) post-filter in `toArray()` apply\n // the correct scaled-space comparison. Replaces the old\n // \"always trust lookupRange, no scan fallback\" gap.\n const entries = idx.orderedBy(clause.field, 'asc')\n if (entries) return entries.map(e => e.recordId)\n continue\n }\n // range predicates on a NON-via indexed field dispatch through\n // `lookupRange`, which compares on the original typed value (no\n // numeric-lexicographic landmines).\n const ids = idx.lookupRange(clause.field, clause.op, clause.value)\n if (ids) return [...ids]\n }\n }\n\n // No equality/range driver — try to scope via orderBy.\n if (this.plan.orderBy.length > 0) {\n const primary = this.plan.orderBy[0]!\n const entries = idx.orderedBy(primary.field, primary.direction)\n if (entries) return entries.map(e => e.recordId)\n }\n\n return null\n }\n}\n\n/**\n * True if the given field name is covered by either a single-field\n * index or appears as a component of a declared composite index.\n * Composite coverage is sufficient for the missing-field check because\n * composite writes also maintain the in-memory mirror — the range /\n * orderBy / single-equality lookup paths fall through to decrypted\n * candidates that still get post-filtered by the composite clause.\n */\nfunction isFieldIndexed(field: string, idx: PersistedCollectionIndex): boolean {\n if (idx.has(field)) return true\n for (const def of idx.definitions()) {\n if (def.kind === 'composite' && def.fields.includes(field)) return true\n }\n return false\n}\n\nfunction isRangeOp(op: Operator): op is '<' | '<=' | '>' | '>=' | 'between' {\n return op === '<' || op === '<=' || op === '>' || op === '>=' || op === 'between'\n}\n\nfunction collectTouchedFields(plan: LazyPlan): string[] {\n const seen = new Set<string>()\n for (const c of plan.clauses) seen.add(c.field)\n for (const o of plan.orderBy) seen.add(o.field)\n return [...seen]\n}\n\nfunction matchesAll(record: unknown, clauses: readonly Clause[]): boolean {\n for (const c of clauses) {\n if (!evaluateClause(record, c)) return false\n }\n return true\n}\n\nfunction sortRecords<T>(records: T[], orderBy: readonly LazyOrderBy[], via?: ViaPipeline): T[] {\n return [...records].sort((a, b) => {\n for (const { field, direction } of orderBy) {\n const av = readPath(a, field)\n const bv = readPath(b, field)\n // #695: a Via-covered field (e.g. money) may store a representation\n // the generic comparator would order wrong — ask the pipeline for an\n // exact ordering first, same as eager's `sortRecords` in\n // `kernel/query/builder.ts`. `undefined` (no via, or the field isn't\n // covered) falls back to the generic comparator, unchanged from today.\n const viaCmp = via?.compareForOrder(field, av, bv)\n const cmp = viaCmp !== undefined ? viaCmp : compareValues(av, bv)\n if (cmp !== 0) return direction === 'asc' ? cmp : -cmp\n }\n return 0\n })\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a === undefined || a === null) return b === undefined || b === null ? 0 : 1\n if (b === undefined || b === null) return -1\n if (typeof a === 'number' && typeof b === 'number') return a - b\n if (typeof a === 'string' && typeof b === 'string') return a < b ? -1 : a > b ? 1 : 0\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime()\n return 0\n}\n"],"mappings":"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","names":["ids"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-lookup/search/tokenize.ts","../src/with-lookup/search/scan.ts","../src/with-lookup/search/segment.ts","../src/with-lookup/search/inverted-index.ts","../src/with-lookup/search/snippet.ts","../src/with-lookup/search/index-store.ts","../src/with-lookup/search/build-docs.ts","../src/with-lookup/search/active.ts","../src/with-lookup/search/strategy.ts"],"sourcesContent":["/**\n * Default text tokenizer for scan-mode search.\n *\n * NFKC-normalize → lowercase → split on Unicode letter/number runs. This is a\n * word-boundary tokenizer: it does **not** segment scripts written without\n * inter-word spaces (Thai, Lao, Khmer, CJK) — those collapse to one token per\n * run. For such data, a dictionary/ICU segmenter is the right `tokenizer`\n * (the engine takes one as a parameter); substring/`contains` matching is the\n * pragmatic fallback until then.\n */\nexport type Tokenizer = (text: string) => string[]\n\nconst WORD = /[\\p{L}\\p{N}]+/gu\n\nexport const tokenize: Tokenizer = (text: string): string[] => {\n if (!text) return []\n return text.normalize('NFKC').toLowerCase().match(WORD) ?? []\n}\n","/**\n * Scan-mode full-text search — ranks **already-decrypted** records by\n * BM25 against a tokenized query. Pure client-side: nothing is written to the\n * store, so this adds **zero** leakage (unlike a store-usable blind index,\n * which is a separate, gated opt-in).\n *\n * O(n) over the collection; intended for small/medium collections (the pilot's\n * scale) where moving the existing userland scan into the DB is the win.\n */\nimport { tokenize, type Tokenizer } from './tokenize.js'\n\nexport interface SearchOptions {\n /** Top-N by score (default: all matches). */\n readonly limit?: number\n /** `'any'` (default) = OR of query terms; `'all'` = every term must match. */\n readonly match?: 'any' | 'all'\n /** Treat the LAST query term as a prefix (typeahead). */\n readonly prefix?: boolean\n}\n\nexport interface SearchResult<T> {\n readonly id: string\n readonly score: number\n readonly record: T\n}\n\nexport interface SearchEntry<T> {\n readonly id: string\n readonly record: T\n}\n\nconst K1 = 1.2\nconst B = 0.75\n\n/** Coerce a field value to searchable text (numbers/booleans stringified). */\nfunction fieldText(record: unknown, field: string): string {\n const v = (record as Record<string, unknown>)[field]\n if (typeof v === 'string') return v\n if (v === null || v === undefined) return ''\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n return ''\n}\n\nexport function searchScan<T>(\n entries: ReadonlyArray<SearchEntry<T>>,\n field: string,\n query: string,\n opts: SearchOptions = {},\n tokenizer: Tokenizer = tokenize,\n): SearchResult<T>[] {\n const queryTerms = tokenizer(query)\n if (queryTerms.length === 0) return []\n\n const match = opts.match ?? 'any'\n const usePrefix = opts.prefix ?? false\n const exactTerms = usePrefix ? queryTerms.slice(0, -1) : queryTerms\n const prefixTerm = usePrefix ? queryTerms[queryTerms.length - 1] : undefined\n\n // One tokenize pass per doc; collect corpus stats (df, avg length).\n const docs = entries.map((e) => ({ id: e.id, record: e.record, terms: tokenizer(fieldText(e.record, field)) }))\n const N = docs.length || 1\n const df = new Map<string, number>()\n let totalLen = 0\n for (const d of docs) {\n totalLen += d.terms.length\n for (const t of new Set(d.terms)) df.set(t, (df.get(t) ?? 0) + 1)\n }\n const avgdl = totalLen / N || 1\n\n // Document frequency for the prefix term: docs containing ANY term with it.\n let prefixDf = 0\n if (prefixTerm !== undefined) {\n for (const d of docs) {\n if (d.terms.some((t) => t.startsWith(prefixTerm))) prefixDf++\n }\n }\n\n const requiredCount = exactTerms.length + (prefixTerm !== undefined ? 1 : 0)\n const results: SearchResult<T>[] = []\n\n for (const d of docs) {\n const tf = new Map<string, number>()\n for (const t of d.terms) tf.set(t, (tf.get(t) ?? 0) + 1)\n\n const matched: { tf: number; df: number }[] = []\n for (const qt of exactTerms) {\n const c = tf.get(qt) ?? 0\n if (c > 0) matched.push({ tf: c, df: df.get(qt) ?? 0 })\n }\n if (prefixTerm !== undefined) {\n let ptf = 0\n for (const [t, c] of tf) if (t.startsWith(prefixTerm)) ptf += c\n if (ptf > 0) matched.push({ tf: ptf, df: prefixDf })\n }\n\n if (matched.length === 0) continue\n if (match === 'all' && matched.length < requiredCount) continue\n\n let score = 0\n for (const m of matched) {\n const idf = Math.log(1 + (N - m.df + 0.5) / (m.df + 0.5))\n const denom = m.tf + K1 * (1 - B + B * (d.terms.length / avgdl))\n score += idf * ((m.tf * (K1 + 1)) / (denom || 1))\n }\n results.push({ id: d.id, score, record: d.record })\n }\n\n results.sort((a, b) => b.score - a.score)\n return opts.limit !== undefined ? results.slice(0, opts.limit) : results\n}\n","/**\n * i18n word tokenizer for the L1 lexical index. Uses Intl.Segmenter\n * (standard ECMAScript — hub-portable) to dictionary-segment Thai/Lao/Khmer/CJK,\n * which the word-run `tokenize` cannot. Terms are matched in NFKC-lowercased form;\n * offsets index the ORIGINAL text so snippets slice the user's text.\n */\nimport type { Tokenizer } from './tokenize.js'\n\nconst SEGMENTER = new Intl.Segmenter(undefined, { granularity: 'word' })\n\nexport interface Token {\n readonly term: string\n readonly offset: number\n}\n\n/** Word-like tokens with NFKC-lowercased terms + original-text char offsets. */\nexport const segmentTokens = (text: string): Token[] => {\n const out: Token[] = []\n if (!text) return out\n for (const s of SEGMENTER.segment(text)) {\n if (s.isWordLike) out.push({ term: s.segment.normalize('NFKC').toLowerCase(), offset: s.index })\n }\n return out\n}\n\n/** Term-only tokenizer (the public `Tokenizer` shape) — for queries. */\nexport const segmentTokenizer: Tokenizer = (text: string): string[] =>\n segmentTokens(text).map((t) => t.term)\n","/**\n * In-memory inverted index for the L1 lexical retrieval layer. Built\n * client-side from already-decrypted records; nothing touches the store. BM25\n * mirrors src/search/scan.ts; multi-field with max-field combination so a record\n * ranks by its strongest field, which also supplies the snippet location.\n */\nimport { segmentTokens, segmentTokenizer } from './segment.js'\n\nconst K1 = 1.2\nconst B = 0.75\n\nexport interface IndexDoc {\n readonly id: string\n readonly fields: ReadonlyArray<{ readonly field: string; readonly locale?: string; readonly text: string }>\n}\n\nexport interface IndexHit {\n readonly id: string\n readonly score: number\n readonly field: string\n readonly locale?: string\n readonly text: string\n readonly offset: number\n}\n\nexport interface QueryOptions {\n readonly limit?: number\n readonly match?: 'any' | 'all'\n readonly prefix?: boolean\n readonly fields?: readonly string[]\n}\n\nexport interface IndexSnapshot {\n readonly v: 1\n readonly fieldStats: ReadonlyArray<[string, { df: [string, number][]; n: number; totalLen: number }]>\n readonly docs: ReadonlyArray<{\n id: string; field: string; locale?: string; text: string; len: number\n tf: [string, number][]; firstOffset: [string, number][]\n }>\n}\n\ninterface Doc {\n id: string\n field: string\n locale?: string\n text: string\n len: number\n tf: Map<string, number>\n firstOffset: Map<string, number>\n}\n\nexport class InvertedIndex {\n // per field: df (term -> #docs), N (#docs), totalLen\n private readonly fieldStats = new Map<string, { df: Map<string, number>; n: number; totalLen: number }>()\n private readonly docs: Doc[] = []\n\n static build(docs: ReadonlyArray<IndexDoc>): InvertedIndex {\n const idx = new InvertedIndex()\n for (const d of docs) {\n for (const f of d.fields) {\n const tokens = segmentTokens(f.text)\n const tf = new Map<string, number>()\n const firstOffset = new Map<string, number>()\n for (const t of tokens) {\n tf.set(t.term, (tf.get(t.term) ?? 0) + 1)\n if (!firstOffset.has(t.term)) firstOffset.set(t.term, t.offset)\n }\n const doc: Doc = { id: d.id, field: f.field, ...(f.locale !== undefined ? { locale: f.locale } : {}), text: f.text, len: tokens.length, tf, firstOffset }\n idx.docs.push(doc)\n let s = idx.fieldStats.get(f.field)\n if (!s) { s = { df: new Map(), n: 0, totalLen: 0 }; idx.fieldStats.set(f.field, s) }\n s.n += 1\n s.totalLen += doc.len\n for (const term of tf.keys()) s.df.set(term, (s.df.get(term) ?? 0) + 1)\n }\n }\n return idx\n }\n\n query(query: string, opts: QueryOptions = {}): IndexHit[] {\n const terms = segmentTokenizer(query)\n if (terms.length === 0) return []\n const usePrefix = opts.prefix ?? false\n const exact = usePrefix ? terms.slice(0, -1) : terms\n const prefix = usePrefix ? terms[terms.length - 1] : undefined\n const match = opts.match ?? 'any'\n const required = exact.length + (prefix !== undefined ? 1 : 0)\n\n // best (max-score) doc per record\n const best = new Map<string, IndexHit>()\n for (const doc of this.docs) {\n if (opts.fields !== undefined && !opts.fields.includes(doc.field)) continue\n const stats = this.fieldStats.get(doc.field)!\n const avgdl = stats.totalLen / stats.n || 1\n let score = 0\n let matchedCount = 0\n let snippetOffset = -1\n\n const scoreTerm = (tf: number, df: number, offset: number): void => {\n if (tf <= 0) return\n matchedCount += 1\n if (snippetOffset < 0 && offset >= 0) snippetOffset = offset\n const idf = Math.log(1 + (stats.n - df + 0.5) / (df + 0.5))\n const denom = tf + K1 * (1 - B + B * (doc.len / avgdl))\n score += idf * ((tf * (K1 + 1)) / (denom || 1))\n }\n\n for (const qt of exact) scoreTerm(doc.tf.get(qt) ?? 0, stats.df.get(qt) ?? 0, doc.firstOffset.get(qt) ?? -1)\n\n if (prefix !== undefined) {\n // Build a set of exact query terms so we don't double-count a term that\n // is both an exact match and starts with the prefix (defect 2 fix).\n const exactSet = new Set(exact)\n let ptf = 0\n let poff = -1\n for (const [term, c] of doc.tf) {\n if (term.startsWith(prefix) && !exactSet.has(term)) {\n ptf += c\n if (poff < 0) poff = doc.firstOffset.get(term) ?? -1\n }\n }\n if (ptf > 0) {\n // df for the prefix: count DISTINCT docs in this field that have any\n // prefix-matching term — mirrors scan.ts semantics (defect 1 fix).\n let pdf = 0\n for (const d of this.docs) {\n if (d.field === doc.field && [...d.tf.keys()].some((t) => t.startsWith(prefix))) pdf++\n }\n scoreTerm(ptf, pdf || 1, poff)\n }\n }\n\n if (matchedCount === 0) continue\n if (match === 'all' && matchedCount < required) continue\n\n const hit: IndexHit = {\n id: doc.id, score, field: doc.field, text: doc.text,\n offset: snippetOffset < 0 ? 0 : snippetOffset,\n ...(doc.locale !== undefined ? { locale: doc.locale } : {}),\n }\n const prev = best.get(doc.id)\n if (!prev || hit.score > prev.score) best.set(doc.id, hit)\n }\n\n const results = [...best.values()].sort((a, b) => b.score - a.score)\n return opts.limit !== undefined ? results.slice(0, opts.limit) : results\n }\n\n toSnapshot(): IndexSnapshot {\n return {\n v: 1,\n fieldStats: [...this.fieldStats].map(([f, s]) => [f, { df: [...s.df], n: s.n, totalLen: s.totalLen }]),\n docs: this.docs.map((d) => ({\n id: d.id, field: d.field, text: d.text, len: d.len,\n tf: [...d.tf], firstOffset: [...d.firstOffset],\n ...(d.locale !== undefined ? { locale: d.locale } : {}),\n })),\n }\n }\n\n static fromSnapshot(s: IndexSnapshot): InvertedIndex {\n const idx = new InvertedIndex()\n for (const [f, st] of s.fieldStats) idx.fieldStats.set(f, { df: new Map(st.df), n: st.n, totalLen: st.totalLen })\n for (const d of s.docs) {\n idx.docs.push({\n id: d.id, field: d.field, text: d.text, len: d.len,\n tf: new Map(d.tf), firstOffset: new Map(d.firstOffset),\n ...(d.locale !== undefined ? { locale: d.locale } : {}),\n })\n }\n return idx\n }\n}\n","/**\n * Minimal-disclosure snippet: a char-window around a match offset (L1).\n * Unicode-safe (operates on code points). `…` marks each truncated end.\n */\nexport function extractSnippet(text: string, offset: number, window = 80): string {\n const chars = Array.from(text)\n if (chars.length <= window) return text\n // offset is a UTF-16 index into `text`; map to a code-point index.\n const cpOffset = Array.from(text.slice(0, Math.max(0, offset))).length\n const half = Math.floor(window / 2)\n let start = Math.max(0, cpOffset - half)\n const end = Math.min(chars.length, start + window)\n start = Math.max(0, end - window)\n const body = chars.slice(start, end).join('')\n return `${start > 0 ? '…' : ''}${body}${end < chars.length ? '…' : ''}`\n}\n","/**\n * The index persistence seam (L1.5). MemoryIndexStore is session-scoped and\n * lazy; an opaque-blob backend implements the same interface so the\n * collection call-site is unchanged.\n */\nimport { InvertedIndex, type IndexDoc } from './inverted-index.js'\n\nexport interface IndexStore {\n ensureBuilt(build: () => ReadonlyArray<IndexDoc>): Promise<InvertedIndex>\n markDirty(): void\n flush(): Promise<void>\n readonly built: boolean\n}\n\nexport class MemoryIndexStore implements IndexStore {\n private index: InvertedIndex | undefined\n\n get built(): boolean { return this.index !== undefined }\n\n async ensureBuilt(build: () => ReadonlyArray<IndexDoc>): Promise<InvertedIndex> {\n if (this.index === undefined) this.index = InvertedIndex.build(build())\n return this.index\n }\n\n markDirty(): void { this.index = undefined }\n\n async flush(): Promise<void> { /* in-memory: nothing to persist */ }\n}\n","/**\n * Map a decrypted record's configured text fields → IndexDoc field entries\n * (L1). String, i18nText, dictKey, and blob-filename expansion. Uses\n * getAtPath for nested/[]-wildcard paths. Blob filenames are NOT inline on the\n * record — the collection pre-resolves them (one listSlots() per record, the\n * heaviest source) and feeds them to {@link buildBlobFieldEntries}.\n */\nimport { getAtPath } from '../../via/i18n/core.js'\nimport type { I18nTextDescriptor } from '../../via/i18n/core.js'\nimport type { DictKeyDescriptor, StaticDictDescriptor } from '../../via/i18n/dictionary.js'\nimport type { IndexDoc } from './inverted-index.js'\n\n/**\n * One entry in an {@link IndexDoc}'s `fields` array — the return shape of the\n * `build*FieldEntries` helpers. Exported so `@noy-db/hub/search` can name the\n * return type of its own public functions (#843 C1: the subpath's first\n * type-reachability gap).\n */\nexport type FieldEntry = IndexDoc['fields'][number]\n\nexport function buildStringFieldEntries(\n record: Record<string, unknown>,\n textIndexes: readonly string[],\n only?: readonly string[],\n): FieldEntry[] {\n const fields = only ? textIndexes.filter((f) => only.includes(f)) : textIndexes\n const out: FieldEntry[] = []\n for (const field of fields) {\n for (const leaf of getAtPath(record, field)) {\n if (typeof leaf === 'string' && leaf !== '') out.push({ field, text: leaf })\n }\n }\n return out\n}\n\nexport function buildI18nFieldEntries(\n record: Record<string, unknown>,\n i18nFields: Record<string, I18nTextDescriptor>,\n textIndexes: readonly string[],\n only?: readonly string[],\n): FieldEntry[] {\n const fields = (only ? textIndexes.filter((f) => only.includes(f)) : textIndexes).filter((f) => f in i18nFields)\n const out: FieldEntry[] = []\n for (const field of fields) {\n for (const leaf of getAtPath(record, field)) {\n if (!leaf || typeof leaf !== 'object' || Array.isArray(leaf)) continue\n for (const [locale, val] of Object.entries(leaf as Record<string, unknown>)) {\n if (typeof val === 'string' && val !== '') out.push({ field, locale, text: val })\n }\n }\n }\n return out\n}\n\n/** label map: field -> (key -> { locale -> label }). Precomputed in the collection (async for dynamic dicts). */\nexport function buildDictKeyFieldEntries(\n record: Record<string, unknown>,\n dictKeyFields: Record<string, DictKeyDescriptor | StaticDictDescriptor>,\n labelMaps: Map<string, Map<string, Record<string, string>>>,\n textIndexes: readonly string[],\n only?: readonly string[],\n): FieldEntry[] {\n const fields = (only ? textIndexes.filter((f) => only.includes(f)) : textIndexes).filter((f) => f in dictKeyFields)\n const out: FieldEntry[] = []\n for (const field of fields) {\n const map = labelMaps.get(field)\n if (!map) continue\n for (const leaf of getAtPath(record, field)) {\n const keys = typeof leaf === 'string' ? [leaf] : Array.isArray(leaf) ? leaf.filter((k): k is string => typeof k === 'string') : []\n for (const key of keys) {\n const labels = map.get(key)\n if (!labels) continue\n for (const [locale, label] of Object.entries(labels)) {\n if (label !== '') out.push({ field, locale, text: label })\n }\n }\n }\n }\n return out\n}\n\n/**\n * Blob filenames precomputed in the collection (`field -> filenames[]`, from one\n * async `listSlots()` per record — the heaviest source, see {@link buildBlobFieldEntries}'s\n * caller). Bytes are never tokenized; only the slot `filename` is indexed.\n */\nexport function buildBlobFieldEntries(filenamesByField: Map<string, string[]>): FieldEntry[] {\n const out: FieldEntry[] = []\n for (const [field, names] of filenamesByField) {\n for (const name of names) if (name !== '') out.push({ field, text: name })\n }\n return out\n}\n","/**\n * Enable the search / retrieval capability.\n * Pass to `createNoydb({ searchStrategy: withSearch() })` to make a collection's\n * `search` / `retrieve` / `similarTo` / `warmIndex` / `flushIndex` methods live,\n * and to enable the put()-time embedding-vector compute for collections that\n * declare an `embeddings` config. The search/retrieval engine is dynamically\n * imported here, so it stays out of the floor bundle until opted in.\n */\nimport type { SearchStrategy } from './strategy.js'\n\nexport function withSearch(): SearchStrategy {\n return {\n async search(ctx, field, query, opts) {\n const { search } = await import('./collection-facade.js')\n return search(ctx, field, query, opts)\n },\n async retrieve(ctx, query, opts) {\n const { retrieve } = await import('./collection-facade.js')\n return retrieve(ctx, query, opts)\n },\n async similarTo(ctx, vector, opts) {\n const { similarTo } = await import('./collection-facade.js')\n return similarTo(ctx, vector, opts)\n },\n async warmIndex(ctx) {\n const { warmIndex } = await import('./collection-facade.js')\n return warmIndex(ctx)\n },\n async flushIndex(ctx) {\n const { flushIndex } = await import('./collection-facade.js')\n return flushIndex(ctx)\n },\n async embedOnWrite(ctx, id, record, version) {\n const { embedOnWrite } = await import('./collection-facade.js')\n return embedOnWrite(ctx, id, record, version)\n },\n async rebuildEmbeddings(ctx) {\n const { rebuildEmbeddings } = await import('./collection-facade.js')\n return rebuildEmbeddings(ctx)\n },\n }\n}\n","/**\n * Search capability strategy — the on-demand collection-level retrieval\n * operations the `Collection` routes through: scan-mode full-text `search`,\n * unified `retrieve` (lexical | semantic | hybrid), raw-vector `similarTo`, and\n * the lexical-index lifecycle (`warmIndex` / `flushIndex`). The active engine\n * ({@link withSearch}) dynamically imports the search/retrieval facade (keeping\n * it out of the floor bundle); {@link NO_SEARCH} throws.\n *\n * The embedding write-hook (`embedOnWrite`) is paired here too: opting into\n * `withSearch()` enables both the query surface AND the put()-time vector\n * compute — a vector that no gated `similarTo` / semantic `retrieve` could read\n * is dead weight, so embedding compute is not separately gatable. `Collection`\n * always assembles the per-call {@link SearchContext} and delegates here, so an\n * un-opted-in caller hits `NO_SEARCH`'s throw.\n * @internal\n */\nimport type { SearchContext } from './collection-facade.js'\nimport type { SearchOptions, SearchResult } from './index.js'\nimport type { RetrieveOptions, RetrieveHit } from './retrieve-types.js'\nimport { SearchNotEnabledError } from '../../kernel/errors.js'\n\nexport interface SearchStrategy {\n search<T>(ctx: SearchContext<T>, field: string, query: string, opts?: SearchOptions): Promise<SearchResult<T>[]>\n retrieve<T>(ctx: SearchContext<T>, query: string, opts?: RetrieveOptions<T>): Promise<RetrieveHit<T>[]>\n similarTo<T>(ctx: SearchContext<T>, vector: Float32Array, opts?: { k?: number; minScore?: number; includeRecord?: boolean }): Promise<RetrieveHit<T>[]>\n warmIndex<T>(ctx: SearchContext<T>): Promise<void>\n flushIndex<T>(ctx: SearchContext<T>): Promise<void>\n embedOnWrite<T>(ctx: SearchContext<T>, id: string, record: T, version: number): Promise<void>\n rebuildEmbeddings<T>(ctx: SearchContext<T>): Promise<{ rebuilt: number; skipped: number }>\n}\n\n/**\n * No-op stub — the floor default. Every capability method (and the embedding\n * write-hook) throws {@link SearchNotEnabledError}; opt in with\n * `searchStrategy: withSearch()` in createNoydb. @internal\n */\nexport const NO_SEARCH: SearchStrategy = {\n async search() { throw new SearchNotEnabledError() },\n async retrieve() { throw new SearchNotEnabledError() },\n async similarTo() { throw new SearchNotEnabledError() },\n async warmIndex() { throw new SearchNotEnabledError() },\n async flushIndex() { throw new SearchNotEnabledError() },\n async embedOnWrite() { throw new SearchNotEnabledError() },\n async rebuildEmbeddings() { throw new SearchNotEnabledError() },\n}\n"],"mappings":"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","names":["K1","B"]}