@noy-db/hub 0.7.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 (616) hide show
  1. package/CHANGELOG.md +105 -14
  2. package/LICENSE +202 -21
  3. package/NOTICE +1 -0
  4. package/README.md +27 -10
  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-IJTB4S4M.js → backup-7TFRVFLH.js} +21 -15
  11. package/dist/backup-7TFRVFLH.js.map +1 -0
  12. package/dist/blobs/index.js +13 -13
  13. package/dist/broker/index.js +12 -12
  14. package/dist/by/index.js +2 -2
  15. package/dist/cargo/index.js +39 -36
  16. package/dist/{chunk-7O3Y4PPD.js → chunk-244VXYPG.js} +140 -16
  17. package/dist/chunk-244VXYPG.js.map +1 -0
  18. package/dist/{chunk-BUE2D7PX.js → chunk-32YV3TJT.js} +2 -2
  19. package/dist/{chunk-3RCJJBNR.js → chunk-3ELRMVJK.js} +12 -12
  20. package/dist/{chunk-6GMZ7HEE.js → chunk-3F7GLNAE.js} +6 -6
  21. package/dist/{chunk-2Y4IMKA6.js → chunk-3H5KLDZN.js} +4 -4
  22. package/dist/{chunk-VTFVLCUH.js → chunk-3JWHAR6E.js} +2 -2
  23. package/dist/{chunk-YMUFFJCO.js → chunk-3TDUWVWB.js} +5 -2
  24. package/dist/chunk-3TDUWVWB.js.map +1 -0
  25. package/dist/{chunk-JBDFMYKQ.js → chunk-4AF3TUMP.js} +2 -2
  26. package/dist/{chunk-ZORTV6NH.js → chunk-4I522ERM.js} +88 -4
  27. package/dist/chunk-4I522ERM.js.map +1 -0
  28. package/dist/{chunk-MUM7IANK.js → chunk-4MXI3ODD.js} +4 -4
  29. package/dist/chunk-4MXI3ODD.js.map +1 -0
  30. package/dist/{chunk-BLN54W3L.js → chunk-5FDLC6G7.js} +56 -3
  31. package/dist/chunk-5FDLC6G7.js.map +1 -0
  32. package/dist/chunk-5O7D6ZTW.js +53 -0
  33. package/dist/chunk-5O7D6ZTW.js.map +1 -0
  34. package/dist/{chunk-J6FXAEUG.js → chunk-6B3VNR4P.js} +12 -12
  35. package/dist/{chunk-TUFM4UZP.js → chunk-6K7DPM7D.js} +2 -2
  36. package/dist/{chunk-K6LHRFBL.js → chunk-6LMQ5R26.js} +7 -7
  37. package/dist/{chunk-SPXXX5E4.js → chunk-6LNL42F5.js} +94 -32
  38. package/dist/chunk-6LNL42F5.js.map +1 -0
  39. package/dist/{chunk-XLMEZIAJ.js → chunk-6OWXYZK2.js} +2 -2
  40. package/dist/{chunk-BOWNCWZT.js → chunk-6QSQQLSE.js} +3 -3
  41. package/dist/chunk-7JE7BDYJ.js +1558 -0
  42. package/dist/chunk-7JE7BDYJ.js.map +1 -0
  43. package/dist/{chunk-KG7TZKZG.js → chunk-7VCXZDA4.js} +5 -5
  44. package/dist/chunk-7VCXZDA4.js.map +1 -0
  45. package/dist/{chunk-NQKIUNW5.js → chunk-A6IJU4TK.js} +2 -2
  46. package/dist/{chunk-BM5TWITZ.js → chunk-ANWPSTZU.js} +38 -9
  47. package/dist/chunk-ANWPSTZU.js.map +1 -0
  48. package/dist/{chunk-QIZPZMUI.js → chunk-AOWGXU6D.js} +2 -2
  49. package/dist/{chunk-CUZLNGD5.js → chunk-B2YY6WDD.js} +1 -1
  50. package/dist/chunk-B2YY6WDD.js.map +1 -0
  51. package/dist/{chunk-SFAWCTEM.js → chunk-B57AFBY2.js} +2 -2
  52. package/dist/{chunk-GPEQUXHL.js → chunk-B6SJIBQK.js} +4 -4
  53. package/dist/chunk-BJ2IXQ5Q.js +100 -0
  54. package/dist/chunk-BJ2IXQ5Q.js.map +1 -0
  55. package/dist/{chunk-Q2PU57HC.js → chunk-BK2HHQGN.js} +2 -2
  56. package/dist/chunk-BKT776R5.js +1 -0
  57. package/dist/{chunk-KUX3OB6D.js → chunk-BP6DARYA.js} +33 -5
  58. package/dist/chunk-BP6DARYA.js.map +1 -0
  59. package/dist/{chunk-FXBIP2WD.js → chunk-BUDAGWL5.js} +6 -6
  60. package/dist/chunk-CF5UU4S7.js +757 -0
  61. package/dist/chunk-CF5UU4S7.js.map +1 -0
  62. package/dist/{chunk-BOET4NA4.js → chunk-CKNGS5MH.js} +3 -3
  63. package/dist/chunk-CY2CYVWZ.js +41 -0
  64. package/dist/chunk-CY2CYVWZ.js.map +1 -0
  65. package/dist/{chunk-2577DKXB.js → chunk-DC5QVPV7.js} +2 -2
  66. package/dist/chunk-DC5QVPV7.js.map +1 -0
  67. package/dist/{chunk-YSRUYAVU.js → chunk-DE4DH2RZ.js} +2 -2
  68. package/dist/{chunk-6I2RQCET.js → chunk-DF73H4UN.js} +4 -4
  69. package/dist/{chunk-4GVM6QZA.js → chunk-ENAG3IY7.js} +2 -2
  70. package/dist/chunk-ENAG3IY7.js.map +1 -0
  71. package/dist/{chunk-K7NTTA6P.js → chunk-EOVMT6TB.js} +2 -2
  72. package/dist/chunk-F24ZZTHQ.js +193 -0
  73. package/dist/chunk-F24ZZTHQ.js.map +1 -0
  74. package/dist/chunk-F2TDU7MQ.js +456 -0
  75. package/dist/chunk-F2TDU7MQ.js.map +1 -0
  76. package/dist/chunk-F6NCB7KT.js +13 -0
  77. package/dist/chunk-F6NCB7KT.js.map +1 -0
  78. package/dist/{chunk-SPYWXKJN.js → chunk-F7XGHL5O.js} +3 -3
  79. package/dist/chunk-F7XGHL5O.js.map +1 -0
  80. package/dist/{chunk-XTXOCYOJ.js → chunk-FCKWQE2H.js} +5 -5
  81. package/dist/{chunk-U4FOQDY4.js → chunk-FH2L2E34.js} +4 -4
  82. package/dist/chunk-FH2L2E34.js.map +1 -0
  83. package/dist/{chunk-4OZNXTO4.js → chunk-FN5EBVTQ.js} +2 -2
  84. package/dist/chunk-FQ54DWUQ.js +270 -0
  85. package/dist/chunk-FQ54DWUQ.js.map +1 -0
  86. package/dist/{chunk-NKHRDYOR.js → chunk-FQ5VRHIB.js} +4 -4
  87. package/dist/{chunk-FAHMLERK.js → chunk-FW22ZCXT.js} +2 -2
  88. package/dist/chunk-FXQBGFOX.js +524 -0
  89. package/dist/chunk-FXQBGFOX.js.map +1 -0
  90. package/dist/{chunk-AO42KJO6.js → chunk-GF5BZ7D4.js} +45 -9
  91. package/dist/chunk-GF5BZ7D4.js.map +1 -0
  92. package/dist/{chunk-TRGTKX5E.js → chunk-GHLTDZRS.js} +5 -3
  93. package/dist/{chunk-TRGTKX5E.js.map → chunk-GHLTDZRS.js.map} +1 -1
  94. package/dist/{chunk-5WDXHNSW.js → chunk-HBPPGWUM.js} +5 -5
  95. package/dist/chunk-HBPPGWUM.js.map +1 -0
  96. package/dist/{chunk-KTFRCC5C.js → chunk-HBRLNOHL.js} +82 -8
  97. package/dist/chunk-HBRLNOHL.js.map +1 -0
  98. package/dist/chunk-HLEK5VAN.js +275 -0
  99. package/dist/chunk-HLEK5VAN.js.map +1 -0
  100. package/dist/{chunk-RMS3NEA3.js → chunk-HT2AHGQI.js} +2 -2
  101. package/dist/{chunk-CHJBLCOG.js → chunk-HVSCEIYL.js} +7 -7
  102. package/dist/{chunk-UXWB3FPF.js → chunk-I3JDRCMW.js} +150 -22
  103. package/dist/chunk-I3JDRCMW.js.map +1 -0
  104. package/dist/{chunk-GY4WYNNG.js → chunk-ID6Z6E66.js} +2 -2
  105. package/dist/{chunk-EYBQEIUY.js → chunk-IEX7NK2K.js} +3 -3
  106. package/dist/{chunk-KUJDV5QH.js → chunk-ISZQJBML.js} +5 -5
  107. package/dist/{chunk-LWBCUYZW.js → chunk-IT22PWKV.js} +2 -2
  108. package/dist/{chunk-EQEYOJBM.js → chunk-IUWCVHQ7.js} +3 -3
  109. package/dist/chunk-IUWCVHQ7.js.map +1 -0
  110. package/dist/{chunk-7V4TP3AF.js → chunk-J6UOHJY5.js} +4 -4
  111. package/dist/{chunk-JMGDHKQF.js → chunk-JCN44NCY.js} +2 -2
  112. package/dist/{chunk-CARABLSQ.js → chunk-K6W7BVZ6.js} +3 -3
  113. package/dist/{chunk-77K4PG6P.js → chunk-KJFW62P4.js} +29 -11
  114. package/dist/chunk-KJFW62P4.js.map +1 -0
  115. package/dist/{chunk-453F654R.js → chunk-L7DOS3TE.js} +2 -2
  116. package/dist/chunk-LGNDFRYC.js +23 -0
  117. package/dist/chunk-LGNDFRYC.js.map +1 -0
  118. package/dist/{chunk-WEQEYLCS.js → chunk-LGRLME4M.js} +7 -7
  119. package/dist/{chunk-RMF67KCG.js → chunk-LQ4ZHOKP.js} +2 -2
  120. package/dist/{chunk-ZVO4QVPD.js → chunk-M522UYZ2.js} +5 -5
  121. package/dist/{chunk-G56KBWFY.js → chunk-M7NI3IWE.js} +45 -12
  122. package/dist/chunk-M7NI3IWE.js.map +1 -0
  123. package/dist/chunk-MDWKM2XC.js +293 -0
  124. package/dist/chunk-MDWKM2XC.js.map +1 -0
  125. package/dist/chunk-MKXXMNJH.js +204 -0
  126. package/dist/chunk-MKXXMNJH.js.map +1 -0
  127. package/dist/{chunk-SKGMN35F.js → chunk-MLPHLJIQ.js} +3 -3
  128. package/dist/chunk-MLPHLJIQ.js.map +1 -0
  129. package/dist/{chunk-2N4GY74U.js → chunk-MTL7MXGR.js} +6 -6
  130. package/dist/{chunk-TFR7DSOV.js → chunk-MVZ2TPVP.js} +2 -2
  131. package/dist/{chunk-CPWQB3N5.js → chunk-N5DNADLQ.js} +7 -7
  132. package/dist/{chunk-ELNXJMGB.js → chunk-NNARWO72.js} +6 -6
  133. package/dist/{chunk-PT43Y56J.js → chunk-NRT6QZQB.js} +102 -16
  134. package/dist/chunk-NRT6QZQB.js.map +1 -0
  135. package/dist/{chunk-QIBVIOXO.js → chunk-NWHBJQAL.js} +99 -8
  136. package/dist/chunk-NWHBJQAL.js.map +1 -0
  137. package/dist/chunk-NYMLINRP.js +28 -0
  138. package/dist/chunk-NYMLINRP.js.map +1 -0
  139. package/dist/{chunk-7MG7SJT7.js → chunk-ODVPTDPK.js} +1 -1
  140. package/dist/chunk-ODVPTDPK.js.map +1 -0
  141. package/dist/chunk-ORDURZBU.js +461 -0
  142. package/dist/chunk-ORDURZBU.js.map +1 -0
  143. package/dist/{chunk-6YXCPL4T.js → chunk-PDCG6PR5.js} +2 -2
  144. package/dist/{chunk-REDOOY2Q.js → chunk-PSIV7CJV.js} +2 -2
  145. package/dist/{chunk-NA2NIJZI.js → chunk-QBBUZ3I3.js} +126 -19
  146. package/dist/chunk-QBBUZ3I3.js.map +1 -0
  147. package/dist/{chunk-5VCRVKOZ.js → chunk-QOOUUR4A.js} +6 -4
  148. package/dist/{chunk-5VCRVKOZ.js.map → chunk-QOOUUR4A.js.map} +1 -1
  149. package/dist/{chunk-ANATMFIO.js → chunk-QWFHIOD2.js} +5 -5
  150. package/dist/{chunk-X2DLENDQ.js → chunk-QWGU66MP.js} +3 -3
  151. package/dist/chunk-RHVMZSUU.js +7 -0
  152. package/dist/chunk-RHVMZSUU.js.map +1 -0
  153. package/dist/{chunk-BSFNRLK2.js → chunk-S57HTZ6X.js} +5 -5
  154. package/dist/{chunk-SZJ3DLZA.js → chunk-SEE332EV.js} +2 -2
  155. package/dist/{chunk-YYP7WSEB.js → chunk-SI3V7D4G.js} +2 -2
  156. package/dist/{chunk-UGTBXF5U.js → chunk-SOIQ4MM4.js} +3 -3
  157. package/dist/chunk-SUZKW6UR.js +25 -0
  158. package/dist/chunk-SUZKW6UR.js.map +1 -0
  159. package/dist/{chunk-VXGSWL6L.js → chunk-SYVZW77S.js} +6 -6
  160. package/dist/{chunk-6GOE5PGN.js → chunk-T2LBQ5DV.js} +883 -227
  161. package/dist/chunk-T2LBQ5DV.js.map +1 -0
  162. package/dist/chunk-T52GX4SA.js +215 -0
  163. package/dist/chunk-T52GX4SA.js.map +1 -0
  164. package/dist/{chunk-MVMXZYXT.js → chunk-TKUOEU2F.js} +2 -2
  165. package/dist/chunk-TOPYQ6GA.js +31 -0
  166. package/dist/chunk-TOPYQ6GA.js.map +1 -0
  167. package/dist/{chunk-SMH6SRX7.js → chunk-TROR3ALR.js} +2 -2
  168. package/dist/{chunk-2C3AQB46.js → chunk-UD5EVGZ7.js} +42 -389
  169. package/dist/chunk-UD5EVGZ7.js.map +1 -0
  170. package/dist/{chunk-KJIMGIWO.js → chunk-UPD5NWI5.js} +65 -17
  171. package/dist/chunk-UPD5NWI5.js.map +1 -0
  172. package/dist/{chunk-SL3JAAKN.js → chunk-UYB234ZR.js} +30 -2
  173. package/dist/chunk-UYB234ZR.js.map +1 -0
  174. package/dist/chunk-VHM7HCKB.js +220 -0
  175. package/dist/chunk-VHM7HCKB.js.map +1 -0
  176. package/dist/{chunk-DGOZ4UZD.js → chunk-VNNRUGMM.js} +2 -2
  177. package/dist/{chunk-HDOU6I2Y.js → chunk-VNPEWFPD.js} +4 -4
  178. package/dist/{chunk-L7CIOOBW.js → chunk-WNJ4AVGE.js} +2 -2
  179. package/dist/{chunk-FMZY5Q6L.js → chunk-WX5ADGTZ.js} +3 -3
  180. package/dist/{chunk-VXANTWBJ.js → chunk-WY2XXTET.js} +92 -6
  181. package/dist/{chunk-VXANTWBJ.js.map → chunk-WY2XXTET.js.map} +1 -1
  182. package/dist/{chunk-6X6SEMEE.js → chunk-X25NWUZB.js} +33 -8
  183. package/dist/chunk-X25NWUZB.js.map +1 -0
  184. package/dist/{chunk-Q7HZX3IF.js → chunk-X4773RBR.js} +2 -2
  185. package/dist/chunk-XBKH4FOC.js +195 -0
  186. package/dist/chunk-XBKH4FOC.js.map +1 -0
  187. package/dist/{chunk-ANSIZYEG.js → chunk-YNIUIERI.js} +3 -3
  188. package/dist/chunk-YSK2CU2Q.js +936 -0
  189. package/dist/chunk-YSK2CU2Q.js.map +1 -0
  190. package/dist/{chunk-UXVWEX5L.js → chunk-Z5HTYMMQ.js} +6 -2
  191. package/dist/chunk-Z5HTYMMQ.js.map +1 -0
  192. package/dist/{chunk-RRNRNANG.js → chunk-ZE4CHIED.js} +66 -75
  193. package/dist/chunk-ZE4CHIED.js.map +1 -0
  194. package/dist/chunk-ZSX4S5XB.js +1289 -0
  195. package/dist/chunk-ZSX4S5XB.js.map +1 -0
  196. package/dist/{chunk-BZ3534YM.js → chunk-ZUVBC32X.js} +2 -2
  197. package/dist/{chunk-L742JPKZ.js → chunk-ZXABPVM6.js} +6 -6
  198. package/dist/classified/index.js +5 -5
  199. package/dist/{classified-marker-LI2SF2E4.js → classified-marker-W3USLL5M.js} +3 -3
  200. package/dist/collection-facade-QU6IJBN3.js +48 -0
  201. package/dist/{computed-VPOKQK4N.js → computed-TPFBX7U4.js} +3 -3
  202. package/dist/consent/index.js +9 -9
  203. package/dist/cover/index.js +10 -10
  204. package/dist/coverage/index.js +306 -0
  205. package/dist/coverage/index.js.map +1 -0
  206. package/dist/custody/index.js +12 -12
  207. package/dist/{dead-filter-TSWZQRR5.js → dead-filter-U2UYKSZL.js} +2 -2
  208. package/dist/debug/index.js +1 -1
  209. package/dist/{delegation-OVIFFPG4.js → delegation-S3AEPW2T.js} +11 -11
  210. package/dist/derivations/index.js +25 -22
  211. package/dist/derive-OX7F6TGG.js +21 -0
  212. package/dist/directory/index.js +10 -10
  213. package/dist/{dispatch-WC6X2M6C.js → dispatch-3CQDJV5Q.js} +5 -5
  214. package/dist/{dispatch-S73TK3IY.js → dispatch-RH4LHKBH.js} +21 -21
  215. package/dist/{enclave-KJOTOH7X.js → enclave-27SMF6GF.js} +15 -9
  216. package/dist/executor-B6SRO2QS.js +36 -0
  217. package/dist/executor-BI5CHWRY.js +8 -0
  218. package/dist/executor-RBKM5W5K.js +8 -0
  219. package/dist/export-accessible-VMA3YM5V.js +22 -0
  220. package/dist/extract-partition-NF7PXGPP.js +37 -0
  221. package/dist/{fanout-sidecar-WUFXNYP4.js → fanout-sidecar-OT6RYLOL.js} +9 -9
  222. package/dist/find-LZMP26KF.js +10 -0
  223. package/dist/forget/index.js +9 -9
  224. package/dist/guards/index.js +4 -4
  225. package/dist/history/index.js +13 -13
  226. package/dist/i18n/index.js +14 -14
  227. package/dist/index.d.ts +29 -6
  228. package/dist/index.js +999 -191
  229. package/dist/index.js.map +1 -1
  230. package/dist/indexing/index.js +13 -5
  231. package/dist/indexing/index.js.map +1 -1
  232. package/dist/introspection/index.js +11 -11
  233. package/dist/issue-IHARXWW5.js +18 -0
  234. package/dist/ivf-flat-UZ6DQDLS.js +314 -0
  235. package/dist/ivf-flat-UZ6DQDLS.js.map +1 -0
  236. package/dist/{json-schema-V3M5MUCM.js → json-schema-3SHX72CC.js} +2 -1
  237. package/dist/json-schema-3SHX72CC.js.map +1 -0
  238. package/dist/kernel/collection-config.d.ts +4 -20
  239. package/dist/kernel/collection.d.ts +29 -8
  240. package/dist/kernel/enclave/crypto.d.ts +16 -0
  241. package/dist/kernel/enclave/index.d.ts +1 -1
  242. package/dist/kernel/enclave/record-keys/record-codec.d.ts +25 -0
  243. package/dist/kernel/errors.d.ts +227 -36
  244. package/dist/kernel/generation-map.d.ts +48 -0
  245. package/dist/kernel/memory-store.d.ts +35 -7
  246. package/dist/kernel/noydb.d.ts +6 -1
  247. package/dist/kernel/present-cache.d.ts +58 -0
  248. package/dist/kernel/query/all.d.ts +7 -0
  249. package/dist/kernel/query/builder.d.ts +601 -270
  250. package/dist/kernel/query/civil-date.d.ts +64 -0
  251. package/dist/kernel/query/cursor.d.ts +40 -0
  252. package/dist/kernel/query/distinct-key.d.ts +98 -0
  253. package/dist/kernel/query/hydration.d.ts +76 -0
  254. package/dist/kernel/query/index.d.ts +31 -15
  255. package/dist/kernel/query/internal/core.d.ts +30 -0
  256. package/dist/kernel/query/internal/hooks.d.ts +22 -0
  257. package/dist/kernel/query/internal/maintenance.d.ts +43 -0
  258. package/dist/kernel/query/live/incremental.d.ts +314 -0
  259. package/dist/kernel/query/live/index.d.ts +22 -0
  260. package/dist/kernel/query/live/install.d.ts +2 -0
  261. package/dist/kernel/query/{live.d.ts → live/live.d.ts} +39 -9
  262. package/dist/kernel/query/live/methods.d.ts +107 -0
  263. package/dist/kernel/query/predicate.d.ts +119 -1
  264. package/dist/kernel/query/reduce/date-trunc.d.ts +75 -0
  265. package/dist/kernel/query/reduce/index.d.ts +36 -0
  266. package/dist/kernel/query/reduce/install.d.ts +2 -0
  267. package/dist/kernel/query/reduce/methods.d.ts +259 -0
  268. package/dist/kernel/query/reduce/scan-methods.d.ts +121 -0
  269. package/dist/kernel/query/relate/explain.d.ts +199 -0
  270. package/dist/kernel/query/relate/index.d.ts +48 -0
  271. package/dist/kernel/query/relate/install.d.ts +2 -0
  272. package/dist/kernel/query/relate/join-on.d.ts +126 -0
  273. package/dist/kernel/query/relate/join-reduce.d.ts +16 -0
  274. package/dist/kernel/query/relate/join.d.ts +452 -0
  275. package/dist/kernel/query/relate/methods.d.ts +426 -0
  276. package/dist/kernel/query/relate/partition.d.ts +87 -0
  277. package/dist/kernel/query/relate/scan-methods.d.ts +163 -0
  278. package/dist/kernel/query/relate/traverse.d.ts +109 -0
  279. package/dist/kernel/query/scan-builder.d.ts +79 -176
  280. package/dist/kernel/query/sort-key.d.ts +52 -0
  281. package/dist/kernel/tx-write-gate.d.ts +77 -0
  282. package/dist/kernel/types.d.ts +71 -3
  283. package/dist/kernel/validation.d.ts +27 -10
  284. package/dist/kernel/vault.d.ts +9 -4
  285. package/dist/kernel/via/compose.d.ts +3 -0
  286. package/dist/kernel/via/index.d.ts +56 -0
  287. package/dist/kernel/via/pipeline.d.ts +48 -0
  288. package/dist/{ledger-F33ZVMTK.js → ledger-KZPCMVZY.js} +11 -11
  289. package/dist/liberate-GRLCZMW4.js +24 -0
  290. package/dist/link-set-JCMJ7ULP.js +30 -0
  291. package/dist/{managed-secret-PQYKSR6T.js → managed-secret-S3MQ6SH6.js} +10 -10
  292. package/dist/materialized-views/index.js +30 -23
  293. package/dist/{migrate-cek-5ENH64AA.js → migrate-cek-V7GKJZ7O.js} +2 -2
  294. package/dist/money/index.js +5 -5
  295. package/dist/noydb-SWQJ3MZS.js +97 -0
  296. package/dist/overlay-views/index.js +4 -4
  297. package/dist/periods/index.js +12 -12
  298. package/dist/pod/index.js +75 -68
  299. package/dist/{pod-handle-Q736KYON.js → pod-handle-WGDFB4C2.js} +9 -9
  300. package/dist/policy/index.js +10 -10
  301. package/dist/port/with/blob-strategy.d.ts +9 -0
  302. package/dist/port/with/collection-options.d.ts +5 -0
  303. package/dist/port/with/coverage-strategy.d.ts +83 -0
  304. package/dist/port/with/service-bus.d.ts +46 -0
  305. package/dist/port/with/strategies.d.ts +2 -0
  306. package/dist/portability/index.js +8 -8
  307. package/dist/{post-register-LNE2FE2A.js → post-register-SLSXIPBV.js} +6 -6
  308. package/dist/query/all/index.js +31 -0
  309. package/dist/query/index.js +20 -32
  310. package/dist/query/live/index.js +21 -0
  311. package/dist/query/live/index.js.map +1 -0
  312. package/dist/query/reduce/index.js +33 -0
  313. package/dist/query/reduce/index.js.map +1 -0
  314. package/dist/query/relate/index.js +56 -0
  315. package/dist/query/relate/index.js.map +1 -0
  316. package/dist/reduce/index.js +72 -18
  317. package/dist/reduce/index.js.map +1 -1
  318. package/dist/register-F3OYE7MJ.js +31 -0
  319. package/dist/registry-CILQFXFK.js +8 -0
  320. package/dist/registry-XNNYQVKV.js +8 -0
  321. package/dist/registry-YCDZV3VW.js +35 -0
  322. package/dist/registry-ZGYS33TE.js +24 -0
  323. package/dist/request-withdrawal-Z5NC6TXT.js +30 -0
  324. package/dist/{reveal-67RDQRIN.js → reveal-4BCKPEC2.js} +6 -6
  325. package/dist/revoke-ZL62WC2D.js +23 -0
  326. package/dist/satellites/index.js +1 -1
  327. package/dist/schema-update/index.js +2 -2
  328. package/dist/sealed-record/index.js +12 -12
  329. package/dist/search/index.js +11 -5
  330. package/dist/{seed-G42JYN56.js → seed-4JS2RJ4A.js} +12 -12
  331. package/dist/sequence/index.js +9 -9
  332. package/dist/session/index.js +9 -9
  333. package/dist/shadow/index.js +2 -2
  334. package/dist/signer-MBZZMXTC.js +24 -0
  335. package/dist/snapshots/index.js +10 -10
  336. package/dist/{stale-DDL7WQEZ.js → stale-TJ3DYZMQ.js} +10 -10
  337. package/dist/storage-SVMKRGCW.js +22 -0
  338. package/dist/{store-coordination-provider-MEQ2MJQG.js → store-coordination-provider-2O2VUHKW.js} +10 -10
  339. package/dist/sync/index.js +10 -10
  340. package/dist/team/index.js +17 -15
  341. package/dist/tiers/index.js +13 -11
  342. package/dist/to/index.js +2 -2
  343. package/dist/transactions/index.js +3 -3
  344. package/dist/util/index.js +1 -1
  345. package/dist/vault-head/index.js +8 -8
  346. package/dist/{verify-KOV2G5D6.js → verify-RVT67IMH.js} +5 -5
  347. package/dist/via/blob/index.d.ts +1 -0
  348. package/dist/via/geo/binding.d.ts +31 -0
  349. package/dist/via/geo/descriptor.d.ts +54 -0
  350. package/dist/via/geo/geohash.d.ts +95 -0
  351. package/dist/via/geo/index.d.ts +13 -0
  352. package/dist/via/geo/where.d.ts +50 -0
  353. package/dist/via/i18n/core.d.ts +1 -1
  354. package/dist/via/lookup/registry.d.ts +1 -1
  355. package/dist/via/lookup/snapshot.d.ts +3 -3
  356. package/dist/walk-HJ42EISE.js +21 -0
  357. package/dist/walk-HJ42EISE.js.map +1 -0
  358. package/dist/with-audit/coverage/accounting.d.ts +112 -0
  359. package/dist/with-audit/coverage/active.d.ts +29 -0
  360. package/dist/with-audit/coverage/index.d.ts +61 -0
  361. package/dist/with-audit/coverage/sketch.d.ts +75 -0
  362. package/dist/with-audit/periods/periods.d.ts +55 -7
  363. package/dist/with-audit/periods/reserved-collections.d.ts +3 -0
  364. package/dist/with-audit/periods/vault-facade.d.ts +17 -0
  365. package/dist/with-audit/tiers/index.d.ts +51 -0
  366. package/dist/with-audit/tiers/strategy.d.ts +8 -0
  367. package/dist/with-cargo/floor.d.ts +2 -2
  368. package/dist/with-commit/tx/invariants.d.ts +1 -1
  369. package/dist/with-commit/tx/transaction.d.ts +13 -3
  370. package/dist/with-formula/materialized-views/dependency-analyzer.d.ts +4 -3
  371. package/dist/with-formula/materialized-views/emit-memo.d.ts +40 -0
  372. package/dist/with-formula/materialized-views/executor.d.ts +8 -0
  373. package/dist/with-formula/materialized-views/index.d.ts +1 -1
  374. package/dist/with-formula/materialized-views/registry.d.ts +15 -0
  375. package/dist/with-formula/materialized-views/types.d.ts +106 -14
  376. package/dist/with-formula/materialized-views/window-stage.d.ts +11 -0
  377. package/dist/with-formula/overlay-views/types.d.ts +1 -1
  378. package/dist/with-formula/overlay-views/virtual-collection.d.ts +13 -4
  379. package/dist/with-lookup/embeddings/chunks.d.ts +79 -0
  380. package/dist/with-lookup/embeddings/descriptor.d.ts +21 -0
  381. package/dist/with-lookup/embeddings/index.d.ts +3 -0
  382. package/dist/with-lookup/embeddings/ivf-flat.d.ts +93 -0
  383. package/dist/with-lookup/embeddings/vector-index.d.ts +73 -0
  384. package/dist/with-lookup/embeddings/vector-set.d.ts +94 -1
  385. package/dist/with-lookup/embeddings/with-vector-index.d.ts +67 -0
  386. package/dist/with-lookup/indexing/collection-facade.d.ts +85 -2
  387. package/dist/with-lookup/indexing/compound-indexes.d.ts +129 -0
  388. package/dist/with-lookup/indexing/eager-indexes.d.ts +170 -1
  389. package/dist/with-lookup/indexing/index-snapshot.d.ts +62 -0
  390. package/dist/with-lookup/indexing/lazy-builder.d.ts +33 -0
  391. package/dist/with-lookup/indexing/persisted-field-indexes.d.ts +104 -0
  392. package/dist/with-lookup/indexing/persisted-indexes.d.ts +12 -0
  393. package/dist/with-lookup/indexing/sorted-indexes.d.ts +117 -0
  394. package/dist/with-lookup/indexing/unique-constraints.d.ts +106 -23
  395. package/dist/with-lookup/reduce/active.d.ts +2 -24
  396. package/dist/with-lookup/reduce/group-core.d.ts +43 -0
  397. package/dist/with-lookup/reduce/groupby.d.ts +195 -36
  398. package/dist/with-lookup/reduce/incremental-group.d.ts +153 -0
  399. package/dist/with-lookup/reduce/index.d.ts +26 -5
  400. package/dist/with-lookup/reduce/reducers.d.ts +255 -1
  401. package/dist/with-lookup/reduce/reduction.d.ts +44 -13
  402. package/dist/with-lookup/reduce/strategy.d.ts +43 -3
  403. package/dist/with-lookup/reduce/window.d.ts +262 -0
  404. package/dist/with-lookup/search/collection-facade.d.ts +44 -8
  405. package/dist/with-lookup/search/index-store.d.ts +5 -3
  406. package/dist/with-lookup/search/index.d.ts +4 -2
  407. package/dist/with-lookup/search/inverted-index.d.ts +38 -2
  408. package/dist/with-lookup/search/persisted-index-store.d.ts +5 -2
  409. package/dist/with-lookup/search/phrase.d.ts +28 -0
  410. package/dist/with-lookup/search/retrieve-types.d.ts +57 -1
  411. package/dist/with-lookup/search/serialize.d.ts +29 -3
  412. package/dist/with-lookup/search/strategy.d.ts +2 -6
  413. package/dist/with-party/directory/cover/index.d.ts +1 -1
  414. package/dist/with-party/directory/cover/storage.d.ts +1 -1
  415. package/dist/with-party/directory/cover/types.d.ts +1 -1
  416. package/dist/with-party/directory/index.d.ts +1 -1
  417. package/dist/with-party/directory/types.d.ts +1 -1
  418. package/dist/with-party/directory/user-envelope/api.d.ts +1 -1
  419. package/dist/with-party/policy/engine.d.ts +1 -1
  420. package/dist/with-party/policy/index.d.ts +1 -1
  421. package/dist/with-party/policy/presets.d.ts +1 -1
  422. package/dist/with-party/team/authenticators.d.ts +91 -1
  423. package/dist/with-party/team/index.d.ts +3 -2
  424. package/dist/with-party/team/keyring.d.ts +13 -6
  425. package/dist/with-party/team/managed-secret.d.ts +1 -1
  426. package/dist/with-party/team/noydb-facade.d.ts +12 -3
  427. package/dist/with-party/team/wrapped-deks.d.ts +4 -4
  428. package/dist/with-pod/format.d.ts +1 -1
  429. package/dist/with-pod/pod.d.ts +5 -5
  430. package/dist/with-shape/blobs/blob-compaction.d.ts +16 -0
  431. package/dist/with-shape/blobs/blob-set.d.ts +21 -2
  432. package/dist/with-shape/blobs/legacy-sweep.d.ts +35 -0
  433. package/dist/with-shape/introspection/describe.d.ts +6 -0
  434. package/dist/with-shape/introspection/field-meta.d.ts +18 -0
  435. package/dist/with-shape/introspection/types.d.ts +57 -1
  436. package/dist/with-shape/links/vault-facade.d.ts +1 -1
  437. package/dist/with-store/partitioned/index.d.ts +337 -0
  438. package/dist/with-sync/change-broadcast.d.ts +153 -0
  439. package/dist/withdraw-accessible-5NZUPAVA.js +27 -0
  440. package/dist/withdraw-accessible-5NZUPAVA.js.map +1 -0
  441. package/package.json +37 -10
  442. package/dist/api-TXFOVOXX.js +0 -19
  443. package/dist/backup-IJTB4S4M.js.map +0 -1
  444. package/dist/chunk-2577DKXB.js.map +0 -1
  445. package/dist/chunk-2C3AQB46.js.map +0 -1
  446. package/dist/chunk-4GVM6QZA.js.map +0 -1
  447. package/dist/chunk-5WDXHNSW.js.map +0 -1
  448. package/dist/chunk-6GOE5PGN.js.map +0 -1
  449. package/dist/chunk-6X6SEMEE.js.map +0 -1
  450. package/dist/chunk-77K4PG6P.js.map +0 -1
  451. package/dist/chunk-7MG2LHO5.js +0 -1616
  452. package/dist/chunk-7MG2LHO5.js.map +0 -1
  453. package/dist/chunk-7MG7SJT7.js.map +0 -1
  454. package/dist/chunk-7O3Y4PPD.js.map +0 -1
  455. package/dist/chunk-AO42KJO6.js.map +0 -1
  456. package/dist/chunk-ATL3ZVXT.js +0 -151
  457. package/dist/chunk-ATL3ZVXT.js.map +0 -1
  458. package/dist/chunk-BLN54W3L.js.map +0 -1
  459. package/dist/chunk-BM5TWITZ.js.map +0 -1
  460. package/dist/chunk-CUZLNGD5.js.map +0 -1
  461. package/dist/chunk-EQEYOJBM.js.map +0 -1
  462. package/dist/chunk-G56KBWFY.js.map +0 -1
  463. package/dist/chunk-I3ISCYT7.js +0 -159
  464. package/dist/chunk-I3ISCYT7.js.map +0 -1
  465. package/dist/chunk-KG7TZKZG.js.map +0 -1
  466. package/dist/chunk-KJIMGIWO.js.map +0 -1
  467. package/dist/chunk-KTFRCC5C.js.map +0 -1
  468. package/dist/chunk-KUX3OB6D.js.map +0 -1
  469. package/dist/chunk-MUM7IANK.js.map +0 -1
  470. package/dist/chunk-NA2NIJZI.js.map +0 -1
  471. package/dist/chunk-NSITFOCJ.js +0 -99
  472. package/dist/chunk-NSITFOCJ.js.map +0 -1
  473. package/dist/chunk-PT43Y56J.js.map +0 -1
  474. package/dist/chunk-QIBVIOXO.js.map +0 -1
  475. package/dist/chunk-RRNRNANG.js.map +0 -1
  476. package/dist/chunk-RTBJ5IOO.js +0 -87
  477. package/dist/chunk-RTBJ5IOO.js.map +0 -1
  478. package/dist/chunk-SKGMN35F.js.map +0 -1
  479. package/dist/chunk-SL3JAAKN.js.map +0 -1
  480. package/dist/chunk-SPXXX5E4.js.map +0 -1
  481. package/dist/chunk-SPYWXKJN.js.map +0 -1
  482. package/dist/chunk-U4FOQDY4.js.map +0 -1
  483. package/dist/chunk-UXVWEX5L.js.map +0 -1
  484. package/dist/chunk-UXWB3FPF.js.map +0 -1
  485. package/dist/chunk-YMUFFJCO.js.map +0 -1
  486. package/dist/chunk-ZN4FD4PE.js +0 -364
  487. package/dist/chunk-ZN4FD4PE.js.map +0 -1
  488. package/dist/chunk-ZORTV6NH.js.map +0 -1
  489. package/dist/collection-facade-4GRREZ6C.js +0 -45
  490. package/dist/derive-6O5SWQKN.js +0 -21
  491. package/dist/executor-5NIMLSKZ.js +0 -8
  492. package/dist/executor-AQVXV5GZ.js +0 -8
  493. package/dist/executor-PUAO6AKJ.js +0 -29
  494. package/dist/export-accessible-72EQNBMR.js +0 -22
  495. package/dist/extract-partition-PUYNU3UF.js +0 -37
  496. package/dist/find-2ABFCZX4.js +0 -10
  497. package/dist/issue-SIDORTSZ.js +0 -18
  498. package/dist/json-schema-V3M5MUCM.js.map +0 -1
  499. package/dist/kernel/query/join.d.ts +0 -232
  500. package/dist/liberate-LCDPNAG2.js +0 -24
  501. package/dist/link-set-FKNWAYKS.js +0 -30
  502. package/dist/noydb-DUXZPKSN.js +0 -90
  503. package/dist/register-I2DZXGAR.js +0 -31
  504. package/dist/registry-23WF3TGH.js +0 -8
  505. package/dist/registry-4AYVUNXK.js +0 -8
  506. package/dist/registry-VSSQPIGF.js +0 -18
  507. package/dist/registry-YLEWMGAC.js +0 -32
  508. package/dist/request-withdrawal-KFQHEZ25.js +0 -30
  509. package/dist/revoke-DQDZAZZM.js +0 -23
  510. package/dist/signer-WAMDZHEN.js +0 -24
  511. package/dist/storage-FLR73HHF.js +0 -22
  512. package/dist/walk-3UQBRMDV.js +0 -21
  513. package/dist/withdraw-accessible-EL5GXZBS.js +0 -27
  514. /package/dist/{api-TXFOVOXX.js.map → api-GVYEKSJE.js.map} +0 -0
  515. /package/dist/{chunk-BUE2D7PX.js.map → chunk-32YV3TJT.js.map} +0 -0
  516. /package/dist/{chunk-3RCJJBNR.js.map → chunk-3ELRMVJK.js.map} +0 -0
  517. /package/dist/{chunk-6GMZ7HEE.js.map → chunk-3F7GLNAE.js.map} +0 -0
  518. /package/dist/{chunk-2Y4IMKA6.js.map → chunk-3H5KLDZN.js.map} +0 -0
  519. /package/dist/{chunk-VTFVLCUH.js.map → chunk-3JWHAR6E.js.map} +0 -0
  520. /package/dist/{chunk-JBDFMYKQ.js.map → chunk-4AF3TUMP.js.map} +0 -0
  521. /package/dist/{chunk-J6FXAEUG.js.map → chunk-6B3VNR4P.js.map} +0 -0
  522. /package/dist/{chunk-TUFM4UZP.js.map → chunk-6K7DPM7D.js.map} +0 -0
  523. /package/dist/{chunk-K6LHRFBL.js.map → chunk-6LMQ5R26.js.map} +0 -0
  524. /package/dist/{chunk-XLMEZIAJ.js.map → chunk-6OWXYZK2.js.map} +0 -0
  525. /package/dist/{chunk-BOWNCWZT.js.map → chunk-6QSQQLSE.js.map} +0 -0
  526. /package/dist/{chunk-NQKIUNW5.js.map → chunk-A6IJU4TK.js.map} +0 -0
  527. /package/dist/{chunk-QIZPZMUI.js.map → chunk-AOWGXU6D.js.map} +0 -0
  528. /package/dist/{chunk-SFAWCTEM.js.map → chunk-B57AFBY2.js.map} +0 -0
  529. /package/dist/{chunk-GPEQUXHL.js.map → chunk-B6SJIBQK.js.map} +0 -0
  530. /package/dist/{chunk-Q2PU57HC.js.map → chunk-BK2HHQGN.js.map} +0 -0
  531. /package/dist/{collection-facade-4GRREZ6C.js.map → chunk-BKT776R5.js.map} +0 -0
  532. /package/dist/{chunk-FXBIP2WD.js.map → chunk-BUDAGWL5.js.map} +0 -0
  533. /package/dist/{chunk-BOET4NA4.js.map → chunk-CKNGS5MH.js.map} +0 -0
  534. /package/dist/{chunk-YSRUYAVU.js.map → chunk-DE4DH2RZ.js.map} +0 -0
  535. /package/dist/{chunk-6I2RQCET.js.map → chunk-DF73H4UN.js.map} +0 -0
  536. /package/dist/{chunk-K7NTTA6P.js.map → chunk-EOVMT6TB.js.map} +0 -0
  537. /package/dist/{chunk-XTXOCYOJ.js.map → chunk-FCKWQE2H.js.map} +0 -0
  538. /package/dist/{chunk-4OZNXTO4.js.map → chunk-FN5EBVTQ.js.map} +0 -0
  539. /package/dist/{chunk-NKHRDYOR.js.map → chunk-FQ5VRHIB.js.map} +0 -0
  540. /package/dist/{chunk-FAHMLERK.js.map → chunk-FW22ZCXT.js.map} +0 -0
  541. /package/dist/{chunk-RMS3NEA3.js.map → chunk-HT2AHGQI.js.map} +0 -0
  542. /package/dist/{chunk-CHJBLCOG.js.map → chunk-HVSCEIYL.js.map} +0 -0
  543. /package/dist/{chunk-GY4WYNNG.js.map → chunk-ID6Z6E66.js.map} +0 -0
  544. /package/dist/{chunk-EYBQEIUY.js.map → chunk-IEX7NK2K.js.map} +0 -0
  545. /package/dist/{chunk-KUJDV5QH.js.map → chunk-ISZQJBML.js.map} +0 -0
  546. /package/dist/{chunk-LWBCUYZW.js.map → chunk-IT22PWKV.js.map} +0 -0
  547. /package/dist/{chunk-7V4TP3AF.js.map → chunk-J6UOHJY5.js.map} +0 -0
  548. /package/dist/{chunk-JMGDHKQF.js.map → chunk-JCN44NCY.js.map} +0 -0
  549. /package/dist/{chunk-CARABLSQ.js.map → chunk-K6W7BVZ6.js.map} +0 -0
  550. /package/dist/{chunk-453F654R.js.map → chunk-L7DOS3TE.js.map} +0 -0
  551. /package/dist/{chunk-WEQEYLCS.js.map → chunk-LGRLME4M.js.map} +0 -0
  552. /package/dist/{chunk-RMF67KCG.js.map → chunk-LQ4ZHOKP.js.map} +0 -0
  553. /package/dist/{chunk-ZVO4QVPD.js.map → chunk-M522UYZ2.js.map} +0 -0
  554. /package/dist/{chunk-2N4GY74U.js.map → chunk-MTL7MXGR.js.map} +0 -0
  555. /package/dist/{chunk-TFR7DSOV.js.map → chunk-MVZ2TPVP.js.map} +0 -0
  556. /package/dist/{chunk-CPWQB3N5.js.map → chunk-N5DNADLQ.js.map} +0 -0
  557. /package/dist/{chunk-ELNXJMGB.js.map → chunk-NNARWO72.js.map} +0 -0
  558. /package/dist/{chunk-6YXCPL4T.js.map → chunk-PDCG6PR5.js.map} +0 -0
  559. /package/dist/{chunk-REDOOY2Q.js.map → chunk-PSIV7CJV.js.map} +0 -0
  560. /package/dist/{chunk-ANATMFIO.js.map → chunk-QWFHIOD2.js.map} +0 -0
  561. /package/dist/{chunk-X2DLENDQ.js.map → chunk-QWGU66MP.js.map} +0 -0
  562. /package/dist/{chunk-BSFNRLK2.js.map → chunk-S57HTZ6X.js.map} +0 -0
  563. /package/dist/{chunk-SZJ3DLZA.js.map → chunk-SEE332EV.js.map} +0 -0
  564. /package/dist/{chunk-YYP7WSEB.js.map → chunk-SI3V7D4G.js.map} +0 -0
  565. /package/dist/{chunk-UGTBXF5U.js.map → chunk-SOIQ4MM4.js.map} +0 -0
  566. /package/dist/{chunk-VXGSWL6L.js.map → chunk-SYVZW77S.js.map} +0 -0
  567. /package/dist/{chunk-MVMXZYXT.js.map → chunk-TKUOEU2F.js.map} +0 -0
  568. /package/dist/{chunk-SMH6SRX7.js.map → chunk-TROR3ALR.js.map} +0 -0
  569. /package/dist/{chunk-DGOZ4UZD.js.map → chunk-VNNRUGMM.js.map} +0 -0
  570. /package/dist/{chunk-HDOU6I2Y.js.map → chunk-VNPEWFPD.js.map} +0 -0
  571. /package/dist/{chunk-L7CIOOBW.js.map → chunk-WNJ4AVGE.js.map} +0 -0
  572. /package/dist/{chunk-FMZY5Q6L.js.map → chunk-WX5ADGTZ.js.map} +0 -0
  573. /package/dist/{chunk-Q7HZX3IF.js.map → chunk-X4773RBR.js.map} +0 -0
  574. /package/dist/{chunk-ANSIZYEG.js.map → chunk-YNIUIERI.js.map} +0 -0
  575. /package/dist/{chunk-BZ3534YM.js.map → chunk-ZUVBC32X.js.map} +0 -0
  576. /package/dist/{chunk-L742JPKZ.js.map → chunk-ZXABPVM6.js.map} +0 -0
  577. /package/dist/{classified-marker-LI2SF2E4.js.map → classified-marker-W3USLL5M.js.map} +0 -0
  578. /package/dist/{computed-VPOKQK4N.js.map → collection-facade-QU6IJBN3.js.map} +0 -0
  579. /package/dist/{derive-6O5SWQKN.js.map → computed-TPFBX7U4.js.map} +0 -0
  580. /package/dist/{dead-filter-TSWZQRR5.js.map → dead-filter-U2UYKSZL.js.map} +0 -0
  581. /package/dist/{delegation-OVIFFPG4.js.map → delegation-S3AEPW2T.js.map} +0 -0
  582. /package/dist/{enclave-KJOTOH7X.js.map → derive-OX7F6TGG.js.map} +0 -0
  583. /package/dist/{dispatch-WC6X2M6C.js.map → dispatch-3CQDJV5Q.js.map} +0 -0
  584. /package/dist/{dispatch-S73TK3IY.js.map → dispatch-RH4LHKBH.js.map} +0 -0
  585. /package/dist/{executor-5NIMLSKZ.js.map → enclave-27SMF6GF.js.map} +0 -0
  586. /package/dist/{executor-AQVXV5GZ.js.map → executor-B6SRO2QS.js.map} +0 -0
  587. /package/dist/{executor-PUAO6AKJ.js.map → executor-BI5CHWRY.js.map} +0 -0
  588. /package/dist/{export-accessible-72EQNBMR.js.map → executor-RBKM5W5K.js.map} +0 -0
  589. /package/dist/{extract-partition-PUYNU3UF.js.map → export-accessible-VMA3YM5V.js.map} +0 -0
  590. /package/dist/{find-2ABFCZX4.js.map → extract-partition-NF7PXGPP.js.map} +0 -0
  591. /package/dist/{fanout-sidecar-WUFXNYP4.js.map → fanout-sidecar-OT6RYLOL.js.map} +0 -0
  592. /package/dist/{issue-SIDORTSZ.js.map → find-LZMP26KF.js.map} +0 -0
  593. /package/dist/{ledger-F33ZVMTK.js.map → issue-IHARXWW5.js.map} +0 -0
  594. /package/dist/{liberate-LCDPNAG2.js.map → ledger-KZPCMVZY.js.map} +0 -0
  595. /package/dist/{link-set-FKNWAYKS.js.map → liberate-GRLCZMW4.js.map} +0 -0
  596. /package/dist/{managed-secret-PQYKSR6T.js.map → link-set-JCMJ7ULP.js.map} +0 -0
  597. /package/dist/{noydb-DUXZPKSN.js.map → managed-secret-S3MQ6SH6.js.map} +0 -0
  598. /package/dist/{migrate-cek-5ENH64AA.js.map → migrate-cek-V7GKJZ7O.js.map} +0 -0
  599. /package/dist/{register-I2DZXGAR.js.map → noydb-SWQJ3MZS.js.map} +0 -0
  600. /package/dist/{pod-handle-Q736KYON.js.map → pod-handle-WGDFB4C2.js.map} +0 -0
  601. /package/dist/{post-register-LNE2FE2A.js.map → post-register-SLSXIPBV.js.map} +0 -0
  602. /package/dist/{registry-23WF3TGH.js.map → query/all/index.js.map} +0 -0
  603. /package/dist/{registry-4AYVUNXK.js.map → register-F3OYE7MJ.js.map} +0 -0
  604. /package/dist/{registry-VSSQPIGF.js.map → registry-CILQFXFK.js.map} +0 -0
  605. /package/dist/{registry-YLEWMGAC.js.map → registry-XNNYQVKV.js.map} +0 -0
  606. /package/dist/{request-withdrawal-KFQHEZ25.js.map → registry-YCDZV3VW.js.map} +0 -0
  607. /package/dist/{revoke-DQDZAZZM.js.map → registry-ZGYS33TE.js.map} +0 -0
  608. /package/dist/{signer-WAMDZHEN.js.map → request-withdrawal-Z5NC6TXT.js.map} +0 -0
  609. /package/dist/{reveal-67RDQRIN.js.map → reveal-4BCKPEC2.js.map} +0 -0
  610. /package/dist/{stale-DDL7WQEZ.js.map → revoke-ZL62WC2D.js.map} +0 -0
  611. /package/dist/{seed-G42JYN56.js.map → seed-4JS2RJ4A.js.map} +0 -0
  612. /package/dist/{storage-FLR73HHF.js.map → signer-MBZZMXTC.js.map} +0 -0
  613. /package/dist/{walk-3UQBRMDV.js.map → stale-TJ3DYZMQ.js.map} +0 -0
  614. /package/dist/{withdraw-accessible-EL5GXZBS.js.map → storage-SVMKRGCW.js.map} +0 -0
  615. /package/dist/{store-coordination-provider-MEQ2MJQG.js.map → store-coordination-provider-2O2VUHKW.js.map} +0 -0
  616. /package/dist/{verify-KOV2G5D6.js.map → verify-RVT67IMH.js.map} +0 -0
@@ -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-party/directory/storage.ts","../src/with-party/directory/visibility.ts","../src/with-party/directory/user-envelope/storage.ts"],"sourcesContent":["/**\n * Persistence helpers for the vault-level user-directory toggle\n * (`_meta/directory`). Mirrors the bypass-AES pattern used by\n * `_meta/policy` — the directory document is plain JSON, the\n * envelope's `_iv` field is left empty.\n *\n * @see https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/user-envelope.md → Directory visibility\n * @see https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/plaintext-bypass.md — every `_iv: ''` write site\n *\n * @module\n */\nimport { buildRecordEnvelope } from '../../kernel/enclave/index.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { DirectoryConfig } from './types.js'\n\n/** Reserved collection name for vault-level metadata documents. */\nexport const META_COLLECTION = '_meta'\n/** Reserved id for the vault-level directory document. */\nexport const DIRECTORY_RECORD_ID = 'directory'\n\n/**\n * Read the directory toggle from `_meta/directory`. Returns `undefined`\n * when no document has been persisted — callers treat that as the\n * default-on case (`{ enabled: true }`).\n *\n * Tolerates corrupted documents the same way `_meta/policy` does: a\n * JSON parse failure surfaces as `undefined`, not a thrown error, so a\n * bad write never permanently breaks team enumeration.\n */\nexport async function readDirectoryConfig(\n store: NoydbStore,\n vault: string,\n): Promise<DirectoryConfig | undefined> {\n const envelope = await store.get(vault, META_COLLECTION, DIRECTORY_RECORD_ID)\n if (!envelope) return undefined\n try {\n const parsed = JSON.parse(envelope._data) as unknown\n if (!isDirectoryConfig(parsed)) return undefined\n return parsed\n } catch {\n return undefined\n }\n}\n\n/**\n * Persist the directory toggle at `_meta/directory`. Idempotent — call\n * on every `db.setDirectoryEnabled()` invocation. Owner-only at the\n * caller site; this primitive does not check roles.\n */\nexport async function persistDirectoryConfig(\n store: NoydbStore,\n vault: string,\n config: DirectoryConfig,\n): Promise<void> {\n const envelope = buildRecordEnvelope(\n { collection: META_COLLECTION, id: DIRECTORY_RECORD_ID, version: 1 },\n { iv: '', data: JSON.stringify({ enabled: config.enabled }) },\n )\n await store.put(vault, META_COLLECTION, DIRECTORY_RECORD_ID, envelope)\n}\n\nfunction isDirectoryConfig(x: unknown): x is DirectoryConfig {\n if (x === null || typeof x !== 'object') return false\n if (!('enabled' in x)) return false\n return typeof (x as { enabled: unknown }).enabled === 'boolean'\n}\n","/**\n * Persistence helpers for the per-user visibility flag\n * (`_meta/visibility/<keyringId>`). Mirrors the bypass-AES pattern used\n * by `_meta/policy` — the visibility document is plain JSON, the\n * envelope's `_iv` field is left empty.\n *\n * Stored alongside the keyring file rather than inside the encrypted\n * user envelope (`_users/<keyringId>`) because:\n *\n * - `UserEnvelope<T>.data` is opaque-to-hub by contract — hub does not\n * introspect or reserve any keys inside it. Adding `hidden` there\n * would violate that contract.\n * - `listUsersWithEnvelopes` filters by the flag, and the filter must\n * work even when decryption fails (legacy keyrings predating the\n * envelope feature, or a corrupted envelope).\n *\n * @see https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/user-envelope.md → Directory visibility\n * @see https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/plaintext-bypass.md — every `_iv: ''` write site\n *\n * @module\n */\nimport { buildRecordEnvelope } from '../../kernel/enclave/index.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { UserVisibility } from './types.js'\nimport { META_COLLECTION } from './storage.js'\n\n/** Prefix for per-user visibility records inside `_meta`. */\nexport const VISIBILITY_RECORD_PREFIX = 'visibility/'\n\n/** Compose the `_meta` record id for a keyring's visibility doc. */\nexport function visibilityRecordId(keyringId: string): string {\n return VISIBILITY_RECORD_PREFIX + keyringId\n}\n\n/**\n * Read the visibility flag for `keyringId`. Returns `undefined` when no\n * document has been persisted — callers treat that as the default-visible\n * case (`{ hidden: false }`).\n */\nexport async function readUserVisibility(\n store: NoydbStore,\n vault: string,\n keyringId: string,\n): Promise<UserVisibility | undefined> {\n const envelope = await store.get(vault, META_COLLECTION, visibilityRecordId(keyringId))\n if (!envelope) return undefined\n try {\n const parsed = JSON.parse(envelope._data) as unknown\n if (!isUserVisibility(parsed)) return undefined\n return parsed\n } catch {\n return undefined\n }\n}\n\n/**\n * Persist the visibility flag for `keyringId` at\n * `_meta/visibility/<keyringId>`. Idempotent — call on every\n * `vault.user.setMyVisibility()` invocation. Own-only at the caller\n * site; this primitive does not enforce keyring ownership.\n */\nexport async function persistUserVisibility(\n store: NoydbStore,\n vault: string,\n keyringId: string,\n visibility: UserVisibility,\n): Promise<void> {\n const recordId = visibilityRecordId(keyringId)\n const envelope = buildRecordEnvelope(\n { collection: META_COLLECTION, id: recordId, version: 1 },\n { iv: '', data: JSON.stringify({ hidden: visibility.hidden }) },\n )\n await store.put(vault, META_COLLECTION, recordId, envelope)\n}\n\n/**\n * Delete the visibility flag for `keyringId`. Called from `revoke()`\n * alongside `deleteUserEnvelope` so the sidecar does not leak to a\n * re-granted principal with the same `userId`. Idempotent — the store's\n * `delete()` is already a no-op when the record is absent.\n */\nexport async function deleteUserVisibility(\n store: NoydbStore,\n vault: string,\n keyringId: string,\n): Promise<void> {\n await store.delete(vault, META_COLLECTION, visibilityRecordId(keyringId))\n}\n\nfunction isUserVisibility(x: unknown): x is UserVisibility {\n if (x === null || typeof x !== 'object') return false\n if (!('hidden' in x)) return false\n return typeof (x as { hidden: unknown }).hidden === 'boolean'\n}\n","/**\n * Persistence helpers for per-principal user envelopes stored at\n * `_users/<keyringId>` (logically: `_meta/user/<keyringId>`).\n *\n * Unlike `_meta/policy` and `_meta/handle` which are plaintext, user\n * envelopes carry user data and are encrypted with a dedicated\n * {@link USER_ENVELOPE_COLLECTION} DEK (provisioned at vault open and\n * propagated to every keyring via the system-collection DEK path in\n * `team/keyring.ts`).\n *\n * This module is the **storage primitive** layer. The public API\n * (`vault.user.*`) sits on top of this; permission gates, own-only\n * write enforcement, and presence-channel propagation live there.\n *\n * @see design-history/2026-05-05-user-envelope-design.md\n *\n * @module\n */\nimport type { NoydbStore, UserEnvelope } from '../../../kernel/types.js'\nimport { buildRecordAad, buildRecordEnvelope, encrypt, openEnvelopeJson, type EnclaveKey } from '../../../kernel/enclave/index.js'\nimport { ConflictError, UserEnvelopeOversizedError } from '../../../kernel/errors.js'\nimport {\n USER_ENVELOPE_COLLECTION,\n USER_ENVELOPE_MAX_BYTES,\n} from '../../../kernel/constants.js'\n\n/**\n * Read and decrypt the user envelope for `keyringId`. Returns `null`\n * when no envelope has been persisted (either the principal has never\n * called `updateMe`, or the keyring predates this feature).\n *\n * Decryption errors propagate — a tampered or wrong-keyed envelope\n * surfaces as the underlying crypto error rather than masquerading as\n * \"not found\".\n */\nexport async function loadUserEnvelope<T = unknown>(\n store: NoydbStore,\n vault: string,\n keyringId: string,\n dek: EnclaveKey,\n): Promise<UserEnvelope<T> | null> {\n const envelope = await store.get(vault, USER_ENVELOPE_COLLECTION, keyringId)\n if (!envelope) return null\n const plaintext = await openEnvelopeJson({ collection: USER_ENVELOPE_COLLECTION, id: keyringId }, envelope, dek)\n const data = JSON.parse(plaintext) as T\n return {\n keyringId,\n data,\n _v: envelope._v,\n _ts: envelope._ts,\n }\n}\n\n/**\n * Encrypt and persist the user envelope for `keyringId`. The new\n * version is `(prior._v ?? 0) + 1`. Pass `expectedVersion` to enable\n * optimistic-concurrency checks: a mismatch with the stored version\n * throws {@link ConflictError} with the actual stored version.\n *\n * `expectedVersion: 0` means \"expect no prior envelope\"; the write\n * succeeds only if no envelope exists yet.\n *\n * Soft-caps the JSON-serialized payload at {@link USER_ENVELOPE_MAX_BYTES};\n * larger payloads throw {@link UserEnvelopeOversizedError}.\n */\nexport async function saveUserEnvelope<T>(\n store: NoydbStore,\n vault: string,\n keyringId: string,\n payload: T,\n dek: EnclaveKey,\n expectedVersion?: number,\n): Promise<UserEnvelope<T>> {\n const json = JSON.stringify(payload)\n // TextEncoder counts bytes correctly for multi-byte UTF-8 (Thai text,\n // emoji, etc.) — JSON.stringify().length would undercount.\n const bytes = new TextEncoder().encode(json).byteLength\n if (bytes > USER_ENVELOPE_MAX_BYTES) {\n throw new UserEnvelopeOversizedError(bytes)\n }\n\n const prior = await store.get(vault, USER_ENVELOPE_COLLECTION, keyringId)\n if (expectedVersion !== undefined) {\n const priorVersion = prior?._v ?? 0\n if (priorVersion !== expectedVersion) {\n throw new ConflictError(\n priorVersion,\n `User envelope for \"${keyringId}\" expected version ${expectedVersion}, ` +\n `actual ${priorVersion}`,\n )\n }\n }\n\n const nextVersion = (prior?._v ?? 0) + 1\n const ts = new Date().toISOString()\n const identity = { collection: USER_ENVELOPE_COLLECTION, id: keyringId, version: nextVersion }\n const { iv, data } = await encrypt(json, dek, buildRecordAad(identity))\n\n const envelope = buildRecordEnvelope(\n identity,\n { ts, iv, data },\n )\n await store.put(vault, USER_ENVELOPE_COLLECTION, keyringId, envelope)\n\n return {\n keyringId,\n data: payload,\n _v: nextVersion,\n _ts: ts,\n }\n}\n\n/**\n * Delete the user envelope for `keyringId`. Idempotent — no error if\n * the envelope is already absent. Called from the keyring revoke path\n * (cascade-delete) and is a no-op for keyrings that never wrote.\n */\nexport async function deleteUserEnvelope(\n store: NoydbStore,\n vault: string,\n keyringId: string,\n): Promise<void> {\n await store.delete(vault, USER_ENVELOPE_COLLECTION, keyringId)\n}\n\n/**\n * List the keyring ids that have a user envelope persisted in `vault`.\n * Order is store-defined — callers that need a stable order should sort.\n */\nexport async function listUserEnvelopeIds(\n store: NoydbStore,\n vault: string,\n): Promise<string[]> {\n return store.list(vault, USER_ENVELOPE_COLLECTION)\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-audit/tiers/active.ts","../src/with-audit/tiers/strategy.ts","../src/with-audit/tiers/index.ts"],"sourcesContent":["/**\n * Enable the hierarchical-tiers capability.\n * Pass to `createNoydb({ tiersStrategy: withTiers() })` to make a collection's\n * `putAtTier` / `getAtTier` / `listAtTier` / `elevate` / `demote` methods live.\n * The tier read/write/re-key engine is dynamically imported here, so it stays\n * out of the floor bundle until opted in.\n */\nimport type { TiersStrategy } from './strategy.js'\n\nexport function withTiers(): TiersStrategy {\n return {\n async putAtTier(ctx, id, record, tier, opts) {\n const { putAtTier } = await import('./index.js')\n return putAtTier(ctx, id, record, tier, opts)\n },\n async getAtTier(ctx, id) {\n const { getAtTier } = await import('./index.js')\n return getAtTier(ctx, id)\n },\n async listAtTier(ctx) {\n const { listAtTier } = await import('./index.js')\n return listAtTier(ctx)\n },\n async elevate(ctx, id, toTier) {\n const { elevate } = await import('./index.js')\n return elevate(ctx, id, toTier)\n },\n async demote(ctx, id, toTier) {\n const { demote } = await import('./index.js')\n return demote(ctx, id, toTier)\n },\n }\n}\n","/**\n * Tiers capability strategy — the five on-demand collection-level tier\n * operations the `Collection` routes through. The active engine\n * ({@link withTiers}) dynamically imports the tier cores (keeping them out of\n * the floor bundle); {@link NO_TIERS} throws. Collection always builds its\n * per-call {@link TiersContext} and delegates here, so an un-opted-in caller\n * hits `NO_TIERS`'s throw.\n * @internal\n */\nimport type { GhostRecord } from '../../kernel/types.js'\nimport type { TiersContext, TierMoveResult } from './index.js'\nimport { TiersNotEnabledError } from '../../kernel/errors.js'\n\n// #779 — re-exported so kernel-spine callers (e.g. `Vault._elevatedPut`) can reference the\n// result shape via this grandfathered strategy.js seam instead of statically importing\n// tiers/index.js directly (the port-layering check only grandfathers this file).\nexport type { TierMoveResult }\n\nexport interface TiersStrategy {\n putAtTier<T>(\n ctx: TiersContext<T>,\n id: string,\n record: T,\n tier: number,\n opts?: { elevation?: { reason: string; fromTier: number }; source?: string; sourceTs?: string },\n ): Promise<TierMoveResult>\n getAtTier<T>(ctx: TiersContext<T>, id: string): Promise<T | GhostRecord | null>\n listAtTier<T>(ctx: TiersContext<T>): Promise<Array<{ id: string; tier: number; readable: boolean }>>\n elevate<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<TierMoveResult>\n demote<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<TierMoveResult>\n}\n\n/**\n * No-op stub — the floor default. Every capability method throws\n * {@link TiersNotEnabledError}; opt in with `tiersStrategy: withTiers()` in\n * createNoydb. @internal\n */\nexport const NO_TIERS: TiersStrategy = {\n async putAtTier() { throw new TiersNotEnabledError() },\n async getAtTier() { throw new TiersNotEnabledError() },\n async listAtTier() { throw new TiersNotEnabledError() },\n async elevate() { throw new TiersNotEnabledError() },\n async demote() { throw new TiersNotEnabledError() },\n}\n","/**\n * Hierarchical access — the collection-level tier operations\n * (`putAtTier` / `getAtTier` / `listAtTier` / `elevate` / `demote`).\n *\n * A collection opted into `{ tiers: [...] }` stamps `_tier: N` on each envelope\n * and encrypts its body under the tier-N DEK. These helpers are the read/write\n * surface for that scheme: write at a tier, read with tier-aware visibility\n * (invisibility vs. ghost), and move a record up (`elevate`) or down (`demote`)\n * the tier ladder by re-wrapping its body key.\n *\n * Each function takes a small {@link TiersContext} (the exact `this.*` the\n * moving methods touched) instead of `this`, mirroring the `record-keys/`\n * siblings. Behaviour is byte-identical to the inline code it replaced.\n *\n * The crux is `cekCache`: `elevate`/`demote`/`getAtTier` write the SAME `Lru`\n * reference `Collection` owns (never a copy) so a per-record CEK re-wrap stays\n * synchronous with the cache the kernel's write/read path also mutates. The\n * cross-tier event sink stays collection-resident and is reached via the\n * `emitCrossTierEvent` callback.\n *\n * Internal service — not exported as a `@noy-db/hub/*` subpath.\n */\nexport { withTiers } from './active.js'\nexport { NO_TIERS, type TiersStrategy } from './strategy.js'\nexport { TiersNotEnabledError } from '../../kernel/errors.js'\nimport { recordAadFor, type RecordIdentity, buildRecordAad, buildRecordEnvelope, encrypt, decrypt, unwrapCek, rewrapBodyToDek, applyRewrappedBody, isDeleteMarker, isTombstoneShape, type RecordCodec, type EnclaveKey, type SealedShredSlot } from '../../kernel/enclave/index.js'\nimport { TierDemoteDeniedError, UnsupportedTierCompositionError, PersistedIndexCompensationError } from '../../kernel/errors.js'\nimport { dekKey, assertTierAccess } from '../../with-party/team/tiers.js'\nimport type { UnlockedKeyring } from '../../with-party/team/keyring.js'\nimport type { Lru } from '../../kernel/cache/index.js'\nimport type { BlobFieldsConfig } from '../../with-shape/blobs/blob-compaction.js'\nimport {\n NOYDB_FORMAT_VERSION,\n type NoydbStore,\n type EncryptedEnvelope,\n type GhostRecord,\n type TierMode,\n type CrossTierAccessEvent,\n} from '../../kernel/types.js'\n\n/** Everything the moving tier methods touched on `this.*`, as a flat context. */\nexport interface TiersContext<T> {\n /** Collection name — the crypto AAD scope and tier-DEK key prefix. */\n readonly name: string\n /** Vault namespace the records live under. */\n readonly vault: string\n /** The ciphertext store. */\n readonly adapter: NoydbStore\n /** The caller's unlocked keyring (tier-DEK holdings + role + userId). */\n readonly keyring: UnlockedKeyring\n /** The record codec — decrypts a tier-0 envelope to T. */\n readonly codec: RecordCodec<T>\n /**\n * The collection's per-record CEK cache (SHARED reference, not a copy).\n * `elevate`/`demote`/`getAtTier` re-wrap a record's CEK and `set` it here so\n * the move stays synchronous with the cache the kernel's read/write path\n * owns. `null` → no caching.\n */\n readonly cekCache: Lru<string, EnclaveKey> | null\n /**\n * Sync the collection's decoded-record cache after a tier move rewraps the\n * envelope (#691). The lazy LRU is evicted on EVERY call — it is never\n * re-seeded, since a lazy `#getRaw` miss refetches and decodes the fresh\n * envelope via its adapter fallback (a stale LRU entry after a tier-0\n * `putAtTier` overwrite was the #702 lazy-mode gap). `null` → also evict\n * the eager cache entry; an `entry` → set the eager cache to the given\n * decoded record/version (used by `demote()` landing back at tier 0 and\n * `putAtTier`'s tier-0 write, #702 — the record is tier-0, so it must stay\n * plain-`get()`-readable in-session).\n */\n syncCache(id: string, entry: { record: T; version: number } | null): void\n /**\n * Sync the collection's indexes after a tier move rewraps the envelope\n * (#709). Persisted `_idx/<field>/<recordId>` side-cars hold the indexed\n * field's PLAINTEXT value and are always encrypted under the tier-0 DEK,\n * whatever the record's own tier — the same leak class `forget()` fixed\n * via `purgePersistedIndexes` (\"forget() crypto-shreds the body but keeps\n * the collection DEK, under which these side-cars are encrypted — so\n * without this they leave the indexed field VALUES readable\"). `null` →\n * the record just left tier 0: purge its persisted side-cars and drop its\n * in-memory index entries. A record → it is tier-0 again: (re)build its\n * entries from that record. Call this BEFORE {@link syncCache} — the\n * implementation reads the pre-write cached value as \"previous\" to clean\n * up stale index buckets, so it must run while that entry is still live.\n * `version` stamps a rebuilt side-car's own envelope version — every\n * caller has one in scope (the envelope it just wrote), including the\n * purge (`null`) branches, which pass it along even though it's ignored\n * there (#720: keeps the parameter honestly required, no dead default).\n * `priorEnvelope` (#720) — the RAW envelope read BEFORE this call's own\n * overwrite (already in scope as `existing`/`envelope` at every record-\n * branch call site), so a lazy same-tier value change (a `putAtTier(0)`\n * dropping an indexed field, a `demote(0)` off an elevated record) can\n * still be tier-gate-decoded as \"previous\" once the live envelope itself\n * has already moved past it. The `null`-record branches never pass one —\n * see the implementation's doc comment for why they don't need to.\n */\n syncIndexes(id: string, record: T | null, version: number, priorEnvelope?: EncryptedEnvelope): Promise<void>\n /** Sync the collection's SEARCH artifacts after a tier move (#721). Both the\n * lexical `_ftindex` blob and the `_vec/<id>` embedding are encrypted under\n * the tier-0 DEK and hold the record's derived plaintext (full field text /\n * a text-invertible vector), so leaving them means elevation never hid what\n * the record was searchable by — the `forget()` precedent, unapplied to\n * elevate. `null` → the record left tier 0: purge its `_vec`, and\n * invalidate the `_ftindex` blob (the cache-driven rebuild then excludes\n * it). A record → it is tier-0 again: re-embed it, and invalidate\n * `_ftindex` (rebuild includes it). No-op fast when the collection has no\n * search. */\n syncSearch(id: string, record: T | null, version?: number): Promise<void>\n /**\n * Sync a record's `_history` snapshots after a tier move rewraps the live\n * envelope (#712, at-rest hardening). Each snapshot carries its own key\n * material (a direct DEK wrap, or a per-record `_cek` itself wrapped under\n * the tier DEK) — `elevate()`'s live-body rewrap moves only the live\n * envelope, leaving prior versions decryptable at rest under the tier the\n * record has moved away from (the same leak class `forget()`'s\n * `tombstoneHistory` closed for erasure). `fromDek`/`toDek` are the SAME\n * DEKs the caller already resolved for the live rewrap — reuse them, don't\n * recompute. No-op when the strategy is `NO_HISTORY`.\n */\n syncHistory(id: string, fromDek: EnclaveKey, toDek: EnclaveKey): Promise<void>\n /**\n * Save a raw pre-move envelope as a `_history` snapshot (#728). Tier moves\n * (`putAtTier`/`elevate`/`demote`) bump `_v` and overwrite the live\n * envelope without ever snapshotting the version that existed just before\n * the move — so `history()` silently lost it. The caller (`tiers/index.ts`)\n * builds `envelope` by reusing the SAME `rewrapBodyToDek(envelope, fromDek,\n * toDek)` result it already computed for the live write, so the snapshot\n * lands wrapped under the DESTINATION tier's DEK — never `ctx.codec.\n * encryptRecord`, which always resolves the tier-0 DEK and would leak the\n * pre-move body whenever `fromTier > 0`. No-op when history is disabled\n * (folds the `historyConfig.enabled` gate so `tiers/index.ts` stays simple).\n */\n /**\n * Persist a `_history` snapshot of `version`, sealed against the address it\n * will be STORED at (#1041).\n *\n * Takes a **sealer**, not a finished envelope, because the destination\n * identity is not knowable here: only the history strategy owns the\n * `_history` layout. It hands the address in; the caller seals to it.\n *\n * ⚠️ The identity passed to `rewrapBodyToDek` must be what a READER will\n * recompute — `recordAadFor(address, finalEnvelope)`. A tier-move snapshot\n * strips `_tier`/`_elevatedBy` but KEEPS `_by`, so the destination is\n * `{...address, by: envelope._by}` with no tier. Sealing against the bare\n * address would produce history nothing can open, and the failure would\n * surface only when someone reads it.\n */\n saveHistorySnapshot(\n id: string,\n version: number,\n seal: (address: RecordIdentity) => Promise<EncryptedEnvelope>,\n ): Promise<void>\n /**\n * `true` iff a real history strategy is wired (not `NO_HISTORY`) AND not\n * explicitly disabled via `historyConfig.enabled`. `putAtTier` checks this\n * BEFORE decrypting `existing` to build a snapshot (#728, #737 regression\n * fix) — unlike elevate/demote, which reuse a `body` rewrap their live\n * write needs unconditionally, putAtTier's live write never decrypts the\n * prior envelope, so building a snapshot is the ONLY reason it would\n * touch (and potentially throw decrypting) `existing`'s ciphertext. A\n * derivation-free, history-disabled collection must not pay that decode —\n * or risk it, on a corrupted/inaccessible prior body — same law\n * `hasDerivedOutputs` already enforces for the derived-outputs decode.\n */\n readonly historyEnabled: boolean\n /**\n * Rehome a record's blob attachments after a tier move (#724 Arc 10 Task\n * 2, at-rest hardening). A blob's home tier is its owning record's tier —\n * the `_blob` DEK is tier-scoped (`dekKey('_blob', tier)`), so a solo-\n * owned blob's content CEK must move with the record or it stays\n * tier-0-unwrappable at rest even once the live body has moved (the same\n * leak class `syncHistory` closes for `_history` snapshots). Delegates to\n * `BlobSet.rehomeForTier`; no-op fast when the collection has no blob\n * fields. Order-independent with the other sync hooks — touches only\n * blob side structures, not this collection's own cache/indexes.\n */\n syncBlobs(id: string, fromTier: number, toTier: number): Promise<void>\n /**\n * Purge a record's tier-0-era plaintext ledger deltas after a tier move\n * lands it above tier 0 (#729). The audit ledger is a flat, vault-wide,\n * hash-chained log — a record's reverse-JSON-Patch deltas in\n * `_ledger_deltas` are encrypted under one collection-wide `_ledger` DEK\n * (not the record's own tier DEK), so `elevate()`/`putAtTier()`'s live-body\n * rewrap never moves them: they stay readable at rest to any tier-0\n * caller, the same leak class `syncHistory` closed for `_history`\n * snapshots. Chain-safe: `verify()` reads only `_ledger` entry fields\n * (including `deltaHash`, which lives on the entry, not the deleted delta\n * row), never `_ledger_deltas`, so a purge cannot break it.\n * **Irreversible** — the deleted plaintext cannot be restored by\n * `demote()`. **Metadata-retained** — the `_ledger` entries (that the\n * record was mutated, at which version/timestamp/actor) are untouched;\n * only the delta *content* is purged. No-op when the collection has no\n * ledger (`withHistory()` not enabled).\n */\n syncLedger(id: string): Promise<void>\n /**\n * Sync a record's derived outputs (materialized-view rows, rollup\n * contributions, `withDerivation` outputs) after a tier move (#722).\n * Those outputs are computed from this record and written to OTHER\n * (output) collections via a plain `put` at tier 0 — `elevate()`'s live-\n * body rewrap moves only the source envelope, so the output rows keep\n * holding the source's tier-0-era plaintext, the same leak class\n * `syncSearch`/`syncIndexes` closed for THIS collection's own artifacts.\n * `elevated` (landing tier > 0) reuses the SAME onDelete fanout\n * `forgetDerivedFanout` drives for `forget()` (`kernel/via/dispatch.ts`)\n * — minus its `'ref'` cascade edge (elevate/demote never erase a\n * *different* record) — to recompute this record's derived outputs as\n * REMOVED. Recompute is tier-safe: the fanout's own source scan reads\n * the elevated-excluding cache (#701/#709/#712), so it naturally omits\n * the now-elevated record instead of re-embedding its plaintext. Landing\n * back at tier 0 (`elevated === false`) is the reverse (Task 2, #722):\n * `syncDerivedOutputs` instead runs the ordinary local-write add-\n * dispatchers (`dispatchDerivations`/`dispatchMaterializedViews`, the\n * same ones a plain `put()` fires) to restore the contribution —\n * reversible, since the source's plaintext survives the elevate/demote\n * rewrap round-trip. `record` is the decoded source record (PRE-move on\n * the remove side, POST-move on the add side); only the rollup edge\n * consumes it on remove, both add-dispatchers require it on add; `null`\n * (tombstone/delete-marker) skips both. `version` stamps the add-\n * dispatchers' `_derivedFrom.sourceVersion` metadata — reuses the version\n * the tier op already has, no re-read; unused on the remove side. No-op\n * fast when the collection has no MV/derivation source — each dispatcher\n * below checks its own source before doing any work.\n */\n syncDerived(id: string, record: T | null, elevated: boolean, version?: number): Promise<void>\n /**\n * `true` iff the collection has a materialized-view or derivation source\n * attached. Gates the {@link syncDerived} pre-move decode on the remove\n * direction (`elevate()`, `putAtTier(tier>0)`, `demote()`'s intermediate-\n * tier branch): those sites decode the record SOLELY to feed\n * `syncDerived(..., true)`, whose dispatchers no-op immediately when the\n * collection has no MV/derivation source — so a no-derivation collection\n * would otherwise pay a full record-body decrypt on every tier move for\n * nothing (#722 perf regression). `false` short-circuits the decode to\n * `null`, which `syncDerivedOutputs` already treats safely.\n */\n readonly hasDerivedOutputs: boolean\n /** Emit `_source`/`_sourceTs` provenance fields when a source is supplied. */\n readonly provenance: boolean\n /** Declared tiers, or null when the feature is off. */\n readonly tiers: ReadonlySet<number> | null\n /** Above-tier read visibility mode (`'invisibility'` | `'ghost'`). */\n readonly tierMode: TierMode\n /** Resolve a tier DEK by its `dekKey(name, tier)`. */\n getDEK(key: string): Promise<EnclaveKey>\n /** Fire a cross-tier access event (sink stays collection-resident). */\n emitCrossTierEvent(event: CrossTierAccessEvent): void\n /**\n * Register this record's ref in the encrypted `_subject_index` (#766).\n * `putAtTier`'s raw `ctx.adapter.put` bypasses `Collection.put()`'s write-\n * hook pipeline — the `onAfterWrite` hook that normally does this — so a\n * record whose FIRST persistence is `putAtTier` (sensitive from birth, a\n * documented legitimate use) would otherwise never enter the index and\n * stay unreachable by `vault.forget()`. Idempotent (safe on every call,\n * including a repeat write of an already-registered record) and a no-op\n * when the collection declares no forget-subject field.\n */\n addSubjectRef(id: string, record: T): Promise<void>\n}\n\nexport function assertTiersEnabled<T>(ctx: TiersContext<T>): void {\n if (!ctx.tiers) {\n throw new Error(\n `Collection \"${ctx.name}\": hierarchical tiers are not enabled. ` +\n `Pass { tiers: [0, 1, 2, …] } to vault.collection() to opt in.`,\n )\n }\n}\n\nexport function assertDeclaredTier<T>(ctx: TiersContext<T>, tier: number): void {\n if (tier < 0 || !Number.isInteger(tier)) {\n throw new Error(`Collection \"${ctx.name}\": tier must be a non-negative integer, got ${tier}`)\n }\n if (tier === 0) return\n if (!ctx.tiers || !ctx.tiers.has(tier)) {\n throw new Error(\n `Collection \"${ctx.name}\": tier ${tier} is not declared in { tiers: [...] }`,\n )\n }\n}\n\n/**\n * Tier-composition guard (#724 / Arc 7 of the tier-invisibility campaign).\n *\n * Refuses `tiers` declared together with a derived-artifact feature whose\n * crypto has not yet been made tier-aware — i.e. a feature `elevate()` /\n * `demote()` does not re-key when a record moves tiers, so an elevated\n * record's data for that feature would stay readable at tier 0.\n *\n * **Status:** the original call site (`Collection` constructor, beside\n * `buildUniqueConstraintSet`) was removed by Arc 10 Task 1 (#724) — the\n * `tiers + blobFields` refusal below was superseded by a runtime read gate\n * on `collection.blob(id)` (`with-shape/blobs/blob-set.ts`), so this\n * function currently has no caller. Relocated here from\n * `with-lookup/indexing/unique-constraints.ts` (#733) so the tier-domain\n * guard lives in the tier domain, rather than an indexing file it only\n * borrowed for a grandfathered import specifier.\n *\n * ## #724 verified: `tiers + blobFields` leaks\n *\n * `collection.blob(id)` (`Collection.blob()`) never checks the live\n * record's tier before returning a `BlobSet` handle, and `BlobSet`'s crypto\n * is entirely orthogonal to the tier ladder:\n * - the slot map (`_blob_slots_{collection}/{id}`) is encrypted under the\n * collection's TIER-0 DEK (`getDEK(name)` ≡ `dekKey(name, 0)` — see\n * `dekKey` in `with-party/team/tiers.ts`), never a tier-N DEK;\n * - chunk content (`_blob_index` / `_blob_chunks`) is encrypted under the\n * vault-shared `_blob` DEK (`BLOB_COLLECTION`), which has no tier\n * dimension at all.\n * `elevate()`/`demote()` (this file) rewrap only the live record body,\n * `_history` snapshots (#712), and index side-cars — blob slots and chunks\n * are untouched. A caller whose keyring never held the record's elevated\n * tier DEK can still open `collection.blob(id).get(slot)` and read full\n * plaintext, even though `collection.get(id)` correctly reports the same\n * record as invisible. See `__tests__/tier-composition-guard.test.ts` for\n * the reproduction.\n *\n * ## Safe today — no check needed here\n *\n * Field indexes, search (`textIndexes`/`embeddings`), `withHistory`\n * (snapshot keys are rewrapped by `elevate()`/`demote()`, #712), and the\n * decoded-record cache (evicted on tier moves) are already tier-safe. They\n * are not enumerated below; only known-unsafe features are refused, so any\n * combination not named here passes silently by default.\n *\n * ## Deliberately NOT handled here\n *\n * - `ledger` — `withHistory` threads a ledger writer onto every tiered\n * collection by default in shipped usage; refusing `tiers + ledger` would\n * break that default. A ledger-specific tier-rekey handler (rewrap ledger\n * entries, or scope ledger visibility to tier) is a separate arc.\n * - materialized-view (MV) output — an MV's fanout spec lives on the\n * SOURCE collection, not on the MV collection's own config, so it is not\n * visible at `vault.collection()` registration time and this guard cannot\n * detect it structurally.\n */\nexport function assertTierComposition(\n collectionName: string,\n cfg: { readonly tiers: boolean; readonly blobFields: BlobFieldsConfig | undefined },\n): void {\n if (!cfg.tiers) return\n if (cfg.blobFields !== undefined && Object.keys(cfg.blobFields).length > 0) {\n throw new UnsupportedTierCompositionError(\n 'blobs',\n `Collection \"${collectionName}\": blobFields are not supported together with tiers (#724) — ` +\n `blob chunk content is encrypted under a vault-shared DEK that elevate()/demote() do not ` +\n `re-key, so an elevated record's blob attachments would remain readable at tier 0. Use a ` +\n `non-tiered collection for blob-bearing records until a blob tier-rekey handler ships.`,\n )\n }\n}\n\nfunction isElevatorOrOwner<T>(ctx: TiersContext<T>): boolean {\n return ctx.keyring.role === 'owner' || ctx.keyring.role === 'admin'\n}\n\n/**\n * Structural surface {@link syncDerivedOutputs} needs from the owning\n * collection — the SAME onDelete dispatchers `forgetDerivedFanout` already\n * drives (`kernel/via/dispatch.ts`), reused as-is (#722). Narrower than\n * `Collection<T>` (avoids an import cycle: collection.ts constructs\n * `TiersContext` FROM `this`, which already satisfies this shape\n * structurally — no cast needed at the `tiersContext()` call site).\n */\nexport interface DerivedOutputsHost<T> {\n dispatchMaterializedViewsOnDelete(id: string): Promise<{ deleted: number; residueUndecodable: string[]; residueDeclined: string[] }>\n dispatchArrayDerivationsOnDelete(id: string, eraseRecordShapeToo?: boolean): Promise<number>\n dispatchRollupsOnDelete(id: string, deleted: T): Promise<unknown>\n /** Task 2 (#722) add-direction: the same local-write dispatchers an ordinary `put()` fires. */\n dispatchMaterializedViews(id: string, record: T): Promise<void>\n dispatchDerivations(id: string, record: T, version: number): Promise<void>\n}\n\n/**\n * The {@link TiersContext.syncDerived} implementation collection.ts's\n * `tiersContext()` binds `this` into (#722). See that field's doc comment\n * for the design; `host` is `this` — every dispatcher below is a public\n * Collection method, already self-guarding (no-op) when the collection has\n * no materialized-view/derivation source, so no separate fast-path check is\n * needed here.\n */\nexport async function syncDerivedOutputs<T>(\n host: DerivedOutputsHost<T>,\n id: string,\n record: T | null,\n elevated: boolean,\n version?: number,\n): Promise<void> {\n if (elevated) {\n await host.dispatchMaterializedViewsOnDelete(id)\n await host.dispatchArrayDerivationsOnDelete(id, true)\n if (record !== null) await host.dispatchRollupsOnDelete(id, record)\n return\n }\n // Task 2 (#722) recompute-as-add: the record rejoined tier 0 (demote(→0) /\n // putAtTier(0)) — restore its contribution via the SAME local-write\n // dispatchers an ordinary `put()` fires (`collection.ts`'s\n // `_onRecordMutated('local-write')`), same order. `record === null` only\n // when a caller demotes a tombstone/delete-marker to 0 — nothing to add.\n if (record === null) return\n await host.dispatchDerivations(id, record, version ?? 0)\n await host.dispatchMaterializedViews(id, record)\n}\n\n/**\n * tier-aware put. Encrypts the record with the collection's tier-N DEK and\n * stamps `_tier: N` on the envelope. The caller's keyring must hold the tier-N\n * DEK (directly, by delegation, or by virtue of being the grantor); otherwise\n * throws `TierNotGrantedError`.\n *\n * accepts an optional `elevation` context. When present, the emitted cross-tier\n * event is stamped with `authorization: 'elevation'`, the elevation's reason,\n * and the caller's pre-elevation tier. `vault.elevate(...).collection().put`\n * threads this through; direct `putAtTier` calls leave it undefined and fall\n * back to the inherent-write event shape.\n */\nexport async function putAtTier<T>(\n ctx: TiersContext<T>,\n id: string,\n record: T,\n tier: number,\n opts?: { elevation?: { reason: string; fromTier: number }; source?: string; sourceTs?: string },\n): Promise<TierMoveResult> {\n assertTiersEnabled(ctx)\n assertDeclaredTier(ctx, tier)\n assertTierAccess(ctx.keyring, ctx.name, tier)\n\n const key = dekKey(ctx.name, tier)\n const dek = await ctx.getDEK(key)\n\n const existing = await ctx.adapter.get(ctx.vault, ctx.name, id)\n // #712/whole-branch-fix-1: putAtTier moves the record OFF its existing\n // tier, same as elevate/demote — so the caller must also be cleared for\n // that existing tier, not just the target. Without this, a member holding\n // only the target tier's DEK could putAtTier over a record parked at a\n // tier they've never been granted; the from-tier `getDEK` below would then\n // silently MINT a fresh DEK for that tier into their keyring (a\n // non-cleared caller creating tier key material inside the trust\n // boundary — see `assertTierAccess`'s doc comment). Gate here, BEFORE any\n // from-tier getDEK/syncHistory call, so the mint never happens; owner/\n // admin/custodian still bypass (they may mint, same as elevate/demote).\n assertTierAccess(ctx.keyring, ctx.name, existing?._tier ?? 0)\n const version = existing ? existing._v + 1 : 1\n\n // #728 review-fix-2: resolve the from-tier DEK and snapshot the PRE-move\n // version BEFORE the live write below, mirroring elevate()/demote() —\n // originally this ran much later (after syncIndexes/syncLedger/syncDerived/\n // syncSearchResilient), so a throw in any of those left the live record\n // already moved with `existing` — its sole pre-move copy — gone, silently\n // losing the version #728 exists to preserve. `fromKey`/`fromDek` are\n // resolved ONCE here and reused below for the trailing syncHistory rewrap.\n const fromKey = dekKey(ctx.name, existing?._tier ?? 0)\n const fromDek = await ctx.getDEK(fromKey)\n if (existing && ctx.historyEnabled) {\n // Snapshot the version this write is about to overwrite — same law as\n // Collection.put()'s history save, extended to putAtTier (#728). Rewrap\n // under the DESTINATION `dek` via `applyRewrappedBody` (never\n // `ctx.codec.encryptRecord`, which always resolves the tier-0 DEK and\n // would leak this body at rest whenever the prior tier was > 0).\n // Untagged (`_tier`/`_elevatedBy` stripped), same as elevate()/\n // demote()'s snapshots. `_v` stays the PRE-move version (unbumped), as\n // `preCarried` already carries it.\n const preFrom: RecordIdentity = {\n // The PRE-move version — this identity opens what is already stored.\n collection: ctx.name, id, version: existing._v,\n ...(existing._tier !== undefined ? { tier: existing._tier } : {}),\n ...(existing._by !== undefined ? { by: existing._by } : {}),\n }\n await ctx.saveHistorySnapshot(id, existing._v, async (address) => {\n // The snapshot is UNTAGGED (`_tier` stripped) and keeps `_by`, so that is\n // the identity a reader will recompute from it (#1041).\n const to: RecordIdentity = { ...address, ...(existing._by !== undefined ? { by: existing._by } : {}) }\n const body = await rewrapBodyToDek(preFrom, to, existing, fromDek, dek)\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- excluded from ...preCarried\n const { _tier: _preTier, _elevatedBy: _preElevatedBy, ...preCarried } = existing\n return applyRewrappedBody(preCarried, body)\n })\n }\n\n const json = JSON.stringify(record)\n const identity = { collection: ctx.name, id, version, by: ctx.keyring.userId, ...(tier > 0 ? { tier } : {}) }\n const { iv, data } = await encrypt(json, dek, buildRecordAad(identity))\n const envelope = buildRecordEnvelope(\n identity,\n {\n iv,\n data,\n // `_tier` is stamped from `identity.tier` above — the single source.\n ...(ctx.provenance && opts?.source !== undefined\n ? { provenance: { source: opts.source, sourceTs: opts.sourceTs ?? new Date().toISOString() } }\n : {}),\n },\n )\n\n await ctx.adapter.put(ctx.vault, ctx.name, id, envelope)\n\n // #766: putAtTier bypasses Collection.put()'s onAfterWrite hook — register\n // the subject ref directly so a sensitive-from-birth first write stays\n // reachable by vault.forget(). No-op when no forget-subject field is declared.\n await ctx.addSubjectRef(id, record)\n\n // #702/#709: keep the record cache AND indexes coherent with the raw\n // write — same law as elevate/demote (#691): tier > 0 → invisible on\n // tier-0 surfaces (evict + purge); tier 0 → an ordinary write (re-seed +\n // reindex). syncIndexes runs first — it needs the pre-write cache entry\n // as \"previous\", which syncCache is about to overwrite/evict.\n if (tier > 0) {\n await ctx.syncIndexes(id, null, version)\n ctx.syncCache(id, null)\n // #729: the record lands above tier 0 — purge its tier-0-era plaintext\n // ledger deltas (irreversible; a tier-0 putAtTier(0) has nothing to purge).\n await ctx.syncLedger(id)\n // #722: recompute this record's derived outputs now that it left tier 0\n // — same law as elevate() below. `existing` is the PRE-write envelope;\n // decode it only here (the rollup edge is the only consumer). Gated by\n // `hasDerivedOutputs` — no-derivation collections skip this decrypt\n // entirely (perf regression review finding, Arc 9 #722). Whole-branch\n // review: `existing` may sit at ANY prior tier (including a\n // `putAtTier`-origin tier>0 record with no `_cek` ever minted) — decode\n // it under ITS OWN tier's DEK via `codec.decryptRecordAtDek`, not\n // `ctx.codec.decryptRecord`'s tier-unaware default, which threw\n // `TamperedError` here.\n await ctx.syncDerived(\n id,\n ctx.hasDerivedOutputs && existing\n ? await ctx.codec.decryptRecordAtDek({ collection: ctx.name, id }, existing, await ctx.getDEK(dekKey(ctx.name, existing._tier ?? 0)))\n : null,\n true,\n )\n } else {\n const rec = await ctx.codec.decryptRecord({ collection: ctx.name, id }, envelope, { sealedAsHandles: true })\n await ctx.syncIndexes(id, rec, envelope._v, existing ?? undefined)\n ctx.syncCache(id, rec !== null ? { record: rec, version: envelope._v } : null)\n // #722 Task 2: the record is written at tier 0 — restore its\n // contribution to every derived output (reuse the decode above, no\n // double-decrypt).\n await ctx.syncDerived(id, rec, false, envelope._v)\n }\n // #721: search artifacts follow the same tier > 0 → purge / tier 0 →\n // re-embed law as syncIndexes above. No ordering dependency on syncCache —\n // the _ftindex rebuild is deferred to the next retrieve() and _vec re-embed\n // reads `record` directly, not the cache. #774: a permanently stuck\n // compensation must not abort the write — the record put above already\n // landed — so it is caught and surfaced as residue instead, same as\n // elevate()/demote() (#764).\n const searchResidue = await syncSearchResilient(ctx, id, tier > 0 ? null : record, envelope._v)\n\n if (tier > 0) {\n ctx.emitCrossTierEvent({\n actor: ctx.keyring.userId,\n collection: ctx.name,\n id,\n tier,\n authorization: opts?.elevation ? 'elevation' : 'inherent',\n op: 'put',\n ts: envelope._ts,\n ...(opts?.elevation && {\n reason: opts.elevation.reason,\n elevatedFrom: opts.elevation.fromTier,\n }),\n })\n }\n\n // #712/whole-branch-fix-2: syncHistory runs LAST — after syncIndexes/\n // syncCache/syncSearch (and the cross-tier emit) have already landed. Its\n // own throw (adapter I/O, the Fix-1 assertion, the rewrap) then strands\n // only the `_history` artifact instead of leaving indexes/cache/search\n // unsynced behind an already-moved live record (would conditionally\n // reopen #709/#721/#723 on the error path). A direct putAtTier also moves\n // the record's tier — its history snapshots (if any exist from an earlier\n // tier-0 put()) must follow, same as elevate/demote. Skip when the record\n // was already at this tier (no move, nothing to rewrap). `fromKey`/\n // `fromDek` were already resolved above (review-fix-2), before the live\n // write, for the pre-move snapshot — reused here rather than recomputed.\n if (fromKey !== key) {\n await ctx.syncHistory(id, fromDek, dek)\n // #724: this putAtTier moved the record's tier — rehome its blobs too.\n await ctx.syncBlobs(id, existing?._tier ?? 0, tier)\n }\n return { searchResidue }\n}\n\n/**\n * tier-aware get. When the stored record is at a tier the caller cannot\n * decrypt:\n * - `'invisibility'` mode (default) → returns `null`.\n * - `'ghost'` mode → returns a `GhostRecord` placeholder with the tier and\n * the record id (the record exists but contents are withheld).\n *\n * Fully-cleared reads return the plaintext record and fire a cross-tier event\n * when `_tier > 0`.\n */\nexport async function getAtTier<T>(ctx: TiersContext<T>, id: string): Promise<T | GhostRecord | null> {\n assertTiersEnabled(ctx)\n const envelope = await ctx.adapter.get(ctx.vault, ctx.name, id)\n if (!envelope) return null\n const tier = envelope._tier ?? 0\n if (tier === 0) {\n return ctx.codec.decryptRecord({ collection: ctx.name, id }, envelope)\n }\n\n const key = dekKey(ctx.name, tier)\n if (!ctx.keyring.deks.has(key)) {\n if (ctx.tierMode === 'ghost') {\n return { _ghost: true, _tier: tier } as GhostRecord\n }\n return null\n }\n\n const dek = await ctx.getDEK(key)\n // A tiered record may carry a per-record CEK (e.g. a CEK record\n // elevated via `elevate()`): the CEK is wrapped under the TIER DEK, so\n // unwrap under the tier DEK then decrypt the body under the CEK. Legacy\n // tiered records decrypt directly under the tier DEK.\n // This leg decrypts manually rather than through `decryptRecord` (#635), so\n // it must build the AAD itself — from the address it fetched from plus the\n // envelope's own `_tier`/`_by`, exactly as `recordAadFor` does everywhere\n // else (#1041).\n const aad = recordAadFor({ collection: ctx.name, id }, envelope)\n let plaintext: string\n let cek: EnclaveKey | undefined\n if (envelope._cek !== undefined) {\n cek = await unwrapCek(envelope._cek, dek)\n ctx.cekCache?.set(id, cek, 1)\n plaintext = await decrypt(envelope._iv, envelope._data, cek, aad)\n } else {\n plaintext = await decrypt(envelope._iv, envelope._data, dek, aad)\n }\n let record = JSON.parse(plaintext) as T\n\n // #635: this manual-decrypt leg never went through `decryptRecord`, so it\n // never processed `_sealed` slots — an elevated classified record read\n // back through here would return the body WITHOUT its sealed fields\n // (silent omission). `applySealedSlots` is the same post-processing\n // `decryptRecord`'s tier-0 branch above gets for free, extracted so it can\n // take OUR already-unwrapped `cek` (unwrapped under the TIER dek above)\n // instead of resolving one itself under the collection DEK the way\n // `decryptRecord`/`resolveEnvelopeCek` would — which would be the wrong\n // key for a `_cek` wrapped under a tier DEK. `sealedAsHandles` is omitted\n // (default false) to match this function's OWN tier-0 branch above\n // (`ctx.codec.decryptRecord({ collection: ctx.name, id }, envelope)`, no `sealedAsHandles`) —\n // both tiers return sealed fields inline-decrypted, not as handles.\n if (envelope._sealed !== undefined) {\n record = await ctx.codec.applySealedSlots(record, envelope._sealed, cek, { id })\n }\n\n ctx.emitCrossTierEvent({\n actor: ctx.keyring.userId,\n collection: ctx.name,\n id,\n tier,\n authorization: isElevatorOrOwner(ctx) ? 'inherent' : 'delegation',\n op: 'get',\n ts: new Date().toISOString(),\n })\n\n return record\n}\n\n/**\n * list ids grouped by the caller's readability. Returns only ids whose tier the\n * caller can read. Above-tier ids are omitted in `'invisibility'` mode and\n * included (with tier metadata) in `'ghost'` mode.\n */\nexport async function listAtTier<T>(ctx: TiersContext<T>): Promise<Array<{ id: string; tier: number; readable: boolean }>> {\n assertTiersEnabled(ctx)\n const ids = await ctx.adapter.list(ctx.vault, ctx.name)\n const out: Array<{ id: string; tier: number; readable: boolean }> = []\n for (const id of ids) {\n const env = await ctx.adapter.get(ctx.vault, ctx.name, id)\n if (!env) continue\n const tier = env._tier ?? 0\n const readable = tier === 0 || ctx.keyring.deks.has(dekKey(ctx.name, tier))\n if (!readable && ctx.tierMode === 'invisibility') continue\n out.push({ id, tier, readable })\n }\n return out\n}\n\n/**\n * Result of a completed `elevate()`/`demote()` (#764). The record move\n * (put + indexes/cache/ledger/derived/history/blobs sync) always completes —\n * `searchResidue: true` means only the search-index side (`_vec`/`_ftindex`)\n * was left in a needs-retry state (a stuck `PersistedIndexCompensationError`),\n * mirroring `forget()`'s `indexResidue` posture rather than aborting the move.\n */\nexport interface TierMoveResult {\n readonly searchResidue: boolean\n}\n\n/**\n * Run {@link TiersContext.syncSearch} with #764 resilience: a permanently\n * stuck compensation (`PersistedIndexCompensationError`, thrown by\n * `PersistedIndexStore` when its compensating `remove()` of a stale\n * `_ftindex` blob keeps failing) must not abort `elevate()`/`demote()` — the\n * record's tier-move `put` has already landed, and aborting here would leave\n * `syncLedger`/`syncDerived`/`syncHistory`/`syncBlobs` unsynced behind it (the\n * partial-completion hazard #764 names). Same posture `forget()` takes for\n * `_purgeSearchIndex` (`vault.ts`'s `indexResidue`) — everything else (a\n * genuinely unexpected search-hook error) still aborts, undisturbed.\n */\nasync function syncSearchResilient<T>(ctx: TiersContext<T>, id: string, record: T | null, version?: number): Promise<boolean> {\n try {\n await ctx.syncSearch(id, record, version)\n return false\n } catch (e) {\n if (e instanceof PersistedIndexCompensationError) return true\n throw e\n }\n}\n\n/**\n * elevate a record to a higher tier. Re-encrypts with the target tier's DEK.\n * The caller must hold DEKs for both the current tier (to decrypt) and the\n * target tier (to re-encrypt). Stamps `_elevatedBy` with the caller id so\n * `demote()` can check the reverse operation.\n */\nexport async function elevate<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<TierMoveResult> {\n assertTiersEnabled(ctx)\n assertDeclaredTier(ctx, toTier)\n assertTierAccess(ctx.keyring, ctx.name, toTier)\n\n const envelope = await ctx.adapter.get(ctx.vault, ctx.name, id)\n if (!envelope || isDeleteMarker(envelope) || isTombstoneShape(envelope)) {\n throw new Error(`Record \"${id}\" not found in collection \"${ctx.name}\"`)\n }\n const fromTier = envelope._tier ?? 0\n if (toTier === fromTier) return { searchResidue: false }\n if (toTier < fromTier) {\n throw new Error(`Use demote() to lower the tier of \"${id}\" from ${fromTier} to ${toTier}`)\n }\n // Caller must have access at the existing tier to decrypt.\n if (fromTier > 0) assertTierAccess(ctx.keyring, ctx.name, fromTier)\n\n const fromKey = dekKey(ctx.name, fromTier)\n const toKey = dekKey(ctx.name, toTier)\n const fromDek = await ctx.getDEK(fromKey)\n const toDek = await ctx.getDEK(toKey)\n\n // Per-record CEK composes with tiers: the body key is unchanged (history\n // chain identity preserved); only the wrapping key moves with the tier.\n // Legacy (no `_cek`) records take the direct-DEK path unchanged.\n const now = new Date().toISOString()\n const moveFrom: RecordIdentity = {\n // Opens at the version currently stored; the live write below re-seals at\n // `_v + 1`, so the two ends of a move are NOT the same identity (#1093).\n collection: ctx.name, id, version: envelope._v,\n ...(fromTier > 0 ? { tier: fromTier } : {}),\n ...(envelope._by !== undefined ? { by: envelope._by } : {}),\n }\n const moveTo: RecordIdentity = {\n collection: ctx.name, id, version: envelope._v + 1,\n ...(toTier > 0 ? { tier: toTier } : {}),\n by: ctx.keyring.userId,\n }\n const body = await rewrapBodyToDek(moveFrom, moveTo, envelope, fromDek, toDek)\n if (body.cek) ctx.cekCache?.set(id, body.cek, 1)\n // #728: snapshot the PRE-move version into `_history` before it's\n // overwritten below — reuses `body` (already fromDek→toDek) via\n // `applyRewrappedBody` (never `ctx.codec.encryptRecord`, which always\n // resolves the tier-0 DEK and would leak this body at rest whenever\n // `fromTier > 0`). Untagged (`_tier`/`_elevatedBy` stripped) so the\n // history read-gate doesn't hide it PERMANENTLY once the record demotes\n // back — at-rest protection comes from the ciphertext being under\n // `toDek`, not from the tag. `_v` stays the PRE-move version (unbumped),\n // exactly as `envelope` already carries it.\n //\n // A SECOND rewrap, not a reuse of `body` above. The live write and the\n // snapshot now land at different identities, so they need different AAD and\n // therefore different ciphertext — sharing one rewrap would produce bytes\n // that open at one address and not the other (#1041).\n await ctx.saveHistorySnapshot(id, envelope._v, async (address) => {\n const to: RecordIdentity = { ...address, ...(envelope._by !== undefined ? { by: envelope._by } : {}) }\n const histBody = await rewrapBodyToDek(moveFrom, to, envelope, fromDek, toDek)\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- excluded from ...snapshot\n const { _tier: _preTier, _elevatedBy: _preElevatedBy, ...snapshot } = applyRewrappedBody(envelope, histBody)\n return snapshot\n })\n // #662: spread every slot the source carries (_sealed/_det/_vdig/_bidx/\n // _source/_sourceTs/_debug) through UNCHANGED, then override only\n // the fields a tier move manages. rewrapBodyToDek preserves the CEK, so no\n // passenger slot is re-keyed by the move. The old field-literal dropped every\n // unlisted slot.\n // NOT a `buildRecordEnvelope` site, deliberately (#1051). This is an envelope\n // TRANSFORM, not a construction: the spread is load-bearing (#662) — it carries\n // every passenger slot (`_sealed`/`_det`/`_vdig`/`_bidx`/`_source`/`_debug`)\n // through unchanged, which a fresh literal would drop. That drop is exactly the\n // defect #1074 found in `rotateKeys`. Its body already goes through the enclave\n // (`rewrapBodyToDek`), which is where a tier move will supply AAD.\n const next: EncryptedEnvelope = {\n ...envelope,\n _noydb: NOYDB_FORMAT_VERSION,\n _v: envelope._v + 1,\n _ts: now,\n _iv: body._iv,\n _data: body._data,\n _by: ctx.keyring.userId,\n _tier: toTier,\n _elevatedBy: ctx.keyring.userId,\n ...(body._cek !== undefined ? { _cek: body._cek } : {}),\n }\n await ctx.adapter.put(ctx.vault, ctx.name, id, next)\n // Evict/purge only once the write landed — a throwing put must not blind\n // the eager cache or leave a readable index behind for a still-valid\n // tier-0 record (same ordering as demote). #709: syncIndexes runs first —\n // it needs the pre-elevation cache entry as \"previous\" to clean the\n // eager index bucket; the persisted side-car purge is content-free.\n await ctx.syncIndexes(id, null, next._v)\n ctx.syncCache(id, null)\n // #721: same purge law as syncIndexes above — the record left tier 0, so\n // its _vec sidecar is purged and _ftindex is invalidated. #764: a\n // permanently stuck compensation must not abort the move — the record put\n // above already landed — so it is caught and surfaced as residue instead.\n const searchResidue = await syncSearchResilient(ctx, id, null)\n // #729: elevate always lands the record at tier > 0 — purge its\n // tier-0-era plaintext ledger deltas (irreversible; entry metadata stays).\n await ctx.syncLedger(id)\n // #722: recompute this record's derived outputs now that it left tier 0 —\n // same law as putAtTier(tier>0) above. `envelope` is the PRE-move envelope\n // (captured before the rewrap), decoded only here (the rollup edge is the\n // only consumer of the record content). Gated by `hasDerivedOutputs` — no-\n // derivation collections skip this decrypt entirely (perf regression\n // review finding, Arc 9 #722). Whole-branch review: decode under `fromDek`\n // via `codec.decryptRecordAtDek` — `fromTier` may be > 0 here (a prior\n // elevate), and `ctx.codec.decryptRecord`'s tier-unaware CEK resolution\n // threw `TamperedError` whenever this op's own rewrap hadn't just primed\n // the cekCache (non-`perRecordKeys` collections; `_cek`-absent bodies).\n await ctx.syncDerived(id, ctx.hasDerivedOutputs ? await ctx.codec.decryptRecordAtDek({ collection: ctx.name, id }, envelope, fromDek) : null, true)\n\n ctx.emitCrossTierEvent({\n actor: ctx.keyring.userId,\n collection: ctx.name,\n id,\n tier: toTier,\n authorization: 'elevation',\n op: 'elevate',\n ts: now,\n })\n\n // #712/whole-branch-fix-2: syncHistory runs LAST — after syncIndexes/\n // syncCache/syncSearch/the cross-tier emit have already landed, so its own\n // throw (adapter I/O, the rewrap) strands only the `_history` artifact\n // instead of leaving indexes/cache/search unsynced behind an already-moved\n // live record (would conditionally reopen #709/#721/#723 on the error\n // path). Rewraps prior-version history snapshots to the SAME toTier DEK —\n // otherwise they stay decryptable at rest under fromDek forever.\n await ctx.syncHistory(id, fromDek, toDek)\n // #724: rehome any solo-owned blob attachments to the target tier's\n // `_blob` DEK — same at-rest law as syncHistory above.\n await ctx.syncBlobs(id, fromTier, toTier)\n return { searchResidue }\n}\n\n/**\n * demote a record to a lower tier. Allowed only for the user who performed the\n * last elevation or an owner.\n */\nexport async function demote<T>(ctx: TiersContext<T>, id: string, toTier: number): Promise<TierMoveResult> {\n assertTiersEnabled(ctx)\n if (toTier < 0) throw new Error(`Cannot demote to negative tier ${toTier}`)\n\n const envelope = await ctx.adapter.get(ctx.vault, ctx.name, id)\n if (!envelope || isDeleteMarker(envelope) || isTombstoneShape(envelope)) {\n throw new Error(`Record \"${id}\" not found in collection \"${ctx.name}\"`)\n }\n const fromTier = envelope._tier ?? 0\n if (toTier === fromTier) return { searchResidue: false }\n if (toTier > fromTier) {\n throw new Error(`Use elevate() to raise the tier of \"${id}\" from ${fromTier} to ${toTier}`)\n }\n const isOwner = ctx.keyring.role === 'owner'\n const isOriginalElevator = envelope._elevatedBy === ctx.keyring.userId\n if (!isOwner && !isOriginalElevator) {\n throw new TierDemoteDeniedError(id, fromTier)\n }\n // Caller must still hold the DEK of the current tier to decrypt.\n assertTierAccess(ctx.keyring, ctx.name, fromTier)\n if (toTier > 0) assertDeclaredTier(ctx, toTier)\n\n const fromDek = await ctx.getDEK(dekKey(ctx.name, fromTier))\n const toDek = await ctx.getDEK(dekKey(ctx.name, toTier))\n\n // CEK re-wrap on demote — same body key, moved from the source tier\n // DEK to the target tier DEK. Legacy records take the direct-DEK path.\n const now = new Date().toISOString()\n const moveFrom: RecordIdentity = {\n // Opens at the version currently stored; the live write below re-seals at\n // `_v + 1`, so the two ends of a move are NOT the same identity (#1093).\n collection: ctx.name, id, version: envelope._v,\n ...(fromTier > 0 ? { tier: fromTier } : {}),\n ...(envelope._by !== undefined ? { by: envelope._by } : {}),\n }\n const moveTo: RecordIdentity = {\n collection: ctx.name, id, version: envelope._v + 1,\n ...(toTier > 0 ? { tier: toTier } : {}),\n by: ctx.keyring.userId,\n }\n const body = await rewrapBodyToDek(moveFrom, moveTo, envelope, fromDek, toDek)\n if (body.cek) ctx.cekCache?.set(id, body.cek, 1)\n // #662: same passenger carry-through as elevate(). demote additionally CLEARS\n // `_elevatedBy` (the demote right is consumed) and OMITS `_tier` at tier 0, so\n // both are destructured out of the spread rather than carried.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- excluded from ...carried\n const { _elevatedBy, _tier: _priorTier, ...carried } = envelope\n // #728: snapshot the PRE-move version into `_history` — reuses `body`\n // (already fromDek→toDek) and `carried` (envelope minus `_tier`/\n // `_elevatedBy`, computed above for the live write) via\n // `applyRewrappedBody`, so a tier-0-landing demote's snapshot is untagged\n // just like an ordinary put() entry, same law as elevate()'s snapshot\n // above. `_v` stays the PRE-move version (unbumped), as `carried` already\n // carries it.\n // A SECOND rewrap — see elevate()'s note. The snapshot's destination\n // identity differs from the live write's, so the bytes must differ too.\n await ctx.saveHistorySnapshot(id, envelope._v, async (address) => {\n const to: RecordIdentity = { ...address, ...(envelope._by !== undefined ? { by: envelope._by } : {}) }\n const histBody = await rewrapBodyToDek(moveFrom, to, envelope, fromDek, toDek)\n return applyRewrappedBody(carried, histBody)\n })\n // Transform, not construction — see the note on elevate()'s `next` above.\n const next: EncryptedEnvelope = {\n ...carried,\n _noydb: NOYDB_FORMAT_VERSION,\n _v: envelope._v + 1,\n _ts: now,\n _iv: body._iv,\n _data: body._data,\n _by: ctx.keyring.userId,\n ...(toTier > 0 ? { _tier: toTier } : {}),\n ...(body._cek !== undefined ? { _cek: body._cek } : {}),\n }\n await ctx.adapter.put(ctx.vault, ctx.name, id, next)\n\n // #691/#709: landing back at tier 0 makes this a plain tier-0 record\n // again — it must stay plain-get()-readable AND indexed in the same\n // session, so re-seed the eager cache and rebuild its index entries from\n // the just-written (now tier-0-keyed) envelope through the canonical\n // codec path, the same decode collection.ts's own eager hydration / lazy\n // cache-miss paths use — reused for both, no double-decrypt. A demote\n // that lands on an intermediate elevated tier (toTier > 0) still evicts\n // + purges: the record stays above tier 0 and must remain invisible on\n // tier-0 surfaces, unindexed. syncIndexes runs first in both branches —\n // it needs the pre-demote cache entry as \"previous\".\n // #764: a permanently stuck search-index compensation must not abort the\n // move — the record put above already landed — so it is caught in both\n // branches and surfaced as residue on the return value instead.\n let searchResidue: boolean\n if (toTier === 0) {\n const rec = await ctx.codec.decryptRecord({ collection: ctx.name, id }, next, { sealedAsHandles: true })\n await ctx.syncIndexes(id, rec, next._v, envelope)\n ctx.syncCache(id, rec !== null ? { record: rec, version: next._v } : null)\n // #721: reuse the decode above — no double-decrypt. The record is tier-0\n // again, so re-embed its _vec and invalidate _ftindex to include it.\n searchResidue = await syncSearchResilient(ctx, id, rec, next._v)\n // #722 Task 2: the record rejoined tier 0 — restore its contribution to\n // every derived output (reuse the decode above, no double-decrypt).\n await ctx.syncDerived(id, rec, false, next._v)\n } else {\n await ctx.syncIndexes(id, null, next._v)\n ctx.syncCache(id, null)\n // #721: still above tier 0 — purge _vec, invalidate _ftindex.\n searchResidue = await syncSearchResilient(ctx, id, null)\n // #722 Task 2: still elevated (an intermediate tier) — the source's\n // derived outputs stay recompute-as-removed, same law as elevate()\n // above. `envelope` is the PRE-move envelope captured at function\n // entry; decoded only here (the rollup edge is the only consumer).\n // Gated by `hasDerivedOutputs` — no-derivation collections skip this\n // decrypt entirely (perf regression review finding, Arc 9 #722).\n // Whole-branch review: decode under `fromDek` via\n // `codec.decryptRecordAtDek` — this branch's `fromTier` is always > 0 (a\n // demote FROM tier `fromTier` TO an intermediate `toTier` > 0), so\n // `ctx.codec.decryptRecord`'s tier-unaware CEK resolution threw\n // `TamperedError` here exactly as it did in elevate() above\n // (code-identical bug, same fix).\n await ctx.syncDerived(id, ctx.hasDerivedOutputs ? await ctx.codec.decryptRecordAtDek({ collection: ctx.name, id }, envelope, fromDek) : null, true)\n }\n\n ctx.emitCrossTierEvent({\n actor: ctx.keyring.userId,\n collection: ctx.name,\n id,\n tier: fromTier,\n authorization: 'elevation',\n op: 'demote',\n ts: now,\n })\n\n // #712/whole-branch-fix-2: syncHistory runs LAST — after syncIndexes/\n // syncCache/syncSearch/the cross-tier emit have already landed, so its own\n // throw (adapter I/O, the rewrap) strands only the `_history` artifact\n // instead of leaving indexes/cache/search unsynced behind an already-moved\n // live record (would conditionally reopen #709/#721/#723 on the error\n // path). Rewraps history snapshots the same direction as the live body —\n // demote restores tier-0 (or intermediate-tier) readability at rest.\n await ctx.syncHistory(id, fromDek, toDek)\n // #724: rehome any solo-owned blob attachments the same direction as the\n // live body — restores tier-0 (or intermediate-tier) readability at rest.\n await ctx.syncBlobs(id, fromTier, toTier)\n return { searchResidue }\n}\n\n/**\n * Classify a live envelope's `_sealed` slots for crypto-shred completeness\n * (#M-1). Thin delegate to {@link RecordCodec.classifySealedShred}; surfaced\n * here because `vault.ts` forget() reaches in via `collection._classifySealedShred`.\n */\nexport function classifySealedShred<T>(\n ctx: TiersContext<T>,\n live: EncryptedEnvelope,\n): Promise<{ readonly slots: readonly SealedShredSlot[] }> {\n return ctx.codec.classifySealedShred(live)\n}\n\n// #837 — classifySealedShred's slot type must be nameable from this entry.\nexport type { SealedShredSlot } from '../../kernel/enclave/index.js'\n"],"mappings":"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","names":["putAtTier","getAtTier","listAtTier","elevate","demote"]}
@@ -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-shape/introspection/walk.ts"],"sourcesContent":["/**\n * Orchestrate the structural walk of a Vault, producing a\n * {@link VaultSchemaSnapshot}. Called from `Vault.dumpSchema()`.\n *\n * @module\n */\n\nimport { derivePersistedSchema } from '../persisted-schemas/derive.js'\nimport { loadPersistedSchema } from '../persisted-schemas/storage.js'\nimport { fallbackDerivationName } from './derivation-key.js'\nimport { jsonSchemaToFields } from './fields.js'\nimport type {\n CollectionConfig,\n CollectionDescriptor,\n CollectionStats,\n DumpSchemaOptions,\n FieldDescriptor,\n InternalCollectionStats,\n MaterializedViewDescriptor,\n OverlayViewDescriptor,\n DerivationDescriptor,\n VaultSchemaSnapshot,\n} from './types.js'\nimport type { Collection } from '../../kernel/collection.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from '../../with-party/team/keyring.js'\nimport type { RefRegistry } from '../../kernel/refs.js'\nimport type { VaultMeta } from './meta.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\n\n/**\n * The minimal slice of Vault internal state the walker needs.\n * Exposed via `vault._introspectState()` to keep the public Vault\n * surface narrow.\n *\n * @internal\n */\nexport interface VaultIntrospectState {\n readonly name: string\n readonly adapter: NoydbStore\n readonly collectionCache: Map<string, Collection<unknown>>\n readonly refRegistry: RefRegistry\n readonly getDEK: (collectionName: string) => Promise<EnclaveKey>\n /** The active unlocked keyring — role/permissions/userId for access-scoped ops. */\n readonly keyring: UnlockedKeyring\n readonly subsystems: Record<string, boolean>\n /** Vault-level descriptive metadata, when set via `openVault({meta})`. */\n readonly vaultMeta?: VaultMeta\n // Typed loosely on purpose — these are private service registries\n // accessed only for \"is anything registered\" enumeration.\n readonly mvRegistry: unknown\n readonly overlayRegistry: unknown\n readonly derivationRegistry: unknown\n /**\n * Returns the registered schema-update strategy names for a collection,\n * or `undefined` when none are registered. Populated from\n * `vault.#schemaUpdateNames` in `_introspectState()`.\n */\n readonly getCollectionSchemaUpdateNames?: (col: string) => readonly string[] | undefined\n /**\n * Returns `true` when the collection has an archive policy registered\n * in `vault.archiveRegistry`, `false` otherwise. Populated from\n * `vault.archiveRegistry` in `_introspectState()`.\n */\n readonly hasCollectionArchive?: (col: string) => boolean\n}\n\nconst INTERNAL_PREFIX = '_'\n\n/** Reserved internal collections always present in any real vault. */\nconst KNOWN_INTERNAL_NAMES = ['_keyring', '_ledger', '_meta', '_schemas', '_deltas']\n\nexport async function dumpVaultSchema(\n vault: { _introspectState(): VaultIntrospectState },\n opts: DumpSchemaOptions,\n): Promise<VaultSchemaSnapshot> {\n const state = vault._introspectState()\n const sampleSize = opts.sampleSize ?? 50\n const withStats = opts.withStats === true\n\n // 1. User-facing collections — alphabetical for diff stability\n const cacheNames = [...state.collectionCache.keys()]\n const storageNames = (await safeListAllCollections(state.adapter, state.name))\n .filter((n) => !n.startsWith(INTERNAL_PREFIX))\n const allNames = Array.from(new Set([...cacheNames, ...storageNames])).sort()\n\n const collections: Record<string, CollectionDescriptor> = {}\n for (const name of allNames) {\n collections[name] = await describeCollection(state, name, sampleSize, withStats)\n }\n\n // 2. Materialized views (placeholder — real walk in MV slice 2 follow-up)\n const materializedViews = describeMVs(state.mvRegistry)\n // 3. Overlay views\n const overlayViews = describeOverlays(state.overlayRegistry)\n // 4. Derivations\n const derivations = describeDerivations(state.derivationRegistry)\n\n // 5. Internal collections — only with stats\n let internal: Record<string, InternalCollectionStats> | undefined\n if (withStats) {\n internal = {}\n for (const name of KNOWN_INTERNAL_NAMES) {\n const stats = await statsForCollection(state.adapter, state.name, name)\n if (stats.records > 0) {\n internal[name] = { records: stats.records, bytes: stats.bytes }\n }\n }\n }\n\n const snap: VaultSchemaSnapshot = {\n _noydb_snapshot: 1,\n vault: state.name,\n emittedAt: new Date().toISOString(),\n subsystems: state.subsystems,\n ...(state.vaultMeta !== undefined ? { meta: state.vaultMeta } : {}),\n collections,\n materializedViews,\n overlayViews,\n derivations,\n ...(internal !== undefined ? { internal } : {}),\n }\n return snap\n}\n\nasync function safeListAllCollections(adapter: NoydbStore, vault: string): Promise<string[]> {\n try {\n const snap = await adapter.loadAll(vault)\n return Object.keys(snap)\n } catch {\n return []\n }\n}\n\nasync function describeCollection(\n state: VaultIntrospectState,\n collectionName: string,\n sampleSize: number,\n withStats: boolean,\n): Promise<CollectionDescriptor> {\n let fields: Record<string, FieldDescriptor> = {}\n let validator: CollectionDescriptor['validator']\n\n const refsRaw = state.refRegistry.getOutbound(collectionName)\n const refs: CollectionDescriptor['refs'] = {}\n for (const [name, desc] of Object.entries(refsRaw)) {\n refs[name] = { target: desc.target, mode: desc.mode, ...(desc.isArray ? { isArray: true } : {}) }\n }\n\n // 1. Try the persisted Route B envelope first.\n try {\n const dek = await state.getDEK(collectionName)\n const persisted = await loadPersistedSchema(state.adapter, state.name, collectionName, dek)\n if (persisted) {\n validator = { kind: persisted.kind, source: 'persisted' }\n if (persisted.jsonSchema) {\n fields = jsonSchemaToFields(persisted.jsonSchema, 'persisted', refsRaw)\n }\n }\n } catch {\n // No DEK or decrypt failure — fall through to live-validator\n }\n\n const liveColl = state.collectionCache.get(collectionName)\n\n // 2. Try the live in-process validator (when no persisted envelope).\n if (!validator) {\n const schema = liveColl?.getSchema()\n if (schema) {\n try {\n const derived = await derivePersistedSchema(schema)\n validator = { kind: derived.kind, source: 'live-validator' }\n if (derived.jsonSchema) {\n fields = jsonSchemaToFields(derived.jsonSchema, 'live-validator', refsRaw)\n }\n } catch {\n // Derivation failed (e.g. missing peer-dep) — silently leave fields empty\n }\n }\n }\n\n // 3. Sampling fallback — deferred to a follow-up. For now: empty when no schema.\n if (Object.keys(fields).length === 0 && sampleSize > 0) {\n // Sampling path not implemented in baseline slice 2; reserved for follow-up.\n }\n\n // Populate collection-level meta and config from the live collection when available.\n const collMeta = liveColl?.getMeta()\n const collConfig = liveColl?.getConfig()\n\n // Thread vault-level config (archive + schemaUpdate) that the Collection cannot see.\n const archivePresent = state.hasCollectionArchive?.(collectionName) === true\n const schemaUpdateNames = state.getCollectionSchemaUpdateNames?.(collectionName)\n const hasSchemaUpdate = schemaUpdateNames !== undefined && schemaUpdateNames.length > 0\n\n // Merge Collection-level config with vault-level fields. Omit config entirely\n // when all sources are empty.\n let mergedConfig: CollectionConfig | undefined\n if (collConfig !== undefined || archivePresent || hasSchemaUpdate) {\n mergedConfig = {\n ...(collConfig ?? {}),\n ...(archivePresent ? { archive: true as const } : {}),\n ...(hasSchemaUpdate ? { schemaUpdate: schemaUpdateNames } : {}),\n }\n }\n\n const descriptor: CollectionDescriptor = {\n fields,\n indexes: liveColl?.getDeclaredIndexes() ?? [],\n refs,\n ...(validator ? { validator } : {}),\n ...(collMeta !== undefined ? { meta: collMeta } : {}),\n ...(mergedConfig !== undefined ? { config: mergedConfig } : {}),\n }\n if (withStats) {\n const stats = await statsForCollection(state.adapter, state.name, collectionName)\n ;(descriptor as { stats?: CollectionStats }).stats = stats\n }\n return descriptor\n}\n\nasync function statsForCollection(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n): Promise<CollectionStats> {\n const ids = await adapter.list(vault, collection)\n if (ids.length === 0) {\n return { records: 0, bytes: 0, bytesAvg: 0, bytesMin: 0, bytesMax: 0, oldest: '', newest: '' }\n }\n let total = 0\n let min = Number.POSITIVE_INFINITY\n let max = 0\n let oldest = '￿'\n let newest = ''\n for (const id of ids) {\n const env = await adapter.get(vault, collection, id)\n if (!env) continue\n const size = env._data.length\n total += size\n if (size < min) min = size\n if (size > max) max = size\n if (env._ts < oldest) oldest = env._ts\n if (env._ts > newest) newest = env._ts\n }\n return {\n records: ids.length,\n bytes: total,\n bytesAvg: Math.round(total / ids.length),\n bytesMin: min === Number.POSITIVE_INFINITY ? 0 : min,\n bytesMax: max,\n oldest: oldest === '￿' ? '' : oldest,\n newest,\n }\n}\n\nfunction describeMVs(registry: unknown): Record<string, MaterializedViewDescriptor> {\n if (!registry || typeof registry !== 'object') return {}\n const items = listFromRegistry(registry as Record<string, unknown>)\n const out: Record<string, MaterializedViewDescriptor> = {}\n // Each item is RegisteredMV { spec, dependencies, outputCollection, ... }\n for (const item of items) {\n const reg = item as {\n spec?: {\n name?: string\n unionSources?: ReadonlyArray<{ collection: string }>\n groupBy?: string | ReadonlyArray<string>\n aggregate?: Record<string, unknown>\n refresh?: string\n }\n dependencies?: ReadonlySet<string>\n }\n const spec = reg.spec\n if (!spec?.name) continue\n const sources = spec.unionSources\n ? spec.unionSources.map((u) => u.collection)\n : (reg.dependencies ? [...reg.dependencies].sort() : [])\n const groupBy = spec.groupBy\n ? Array.isArray(spec.groupBy) ? [...spec.groupBy] : [spec.groupBy]\n : undefined\n const aggregate = spec.aggregate ? Object.fromEntries(\n Object.entries(spec.aggregate).map(([k, v]) => [k, summariseAggregateOp(v)]),\n ) : undefined\n out[spec.name] = {\n sources,\n ...(groupBy ? { groupBy } : {}),\n ...(aggregate ? { aggregate } : {}),\n refresh: spec.refresh ?? 'eager',\n }\n }\n return out\n}\n\nfunction describeOverlays(registry: unknown): Record<string, OverlayViewDescriptor> {\n if (!registry || typeof registry !== 'object') return {}\n const specs = listFromRegistry(registry as Record<string, unknown>)\n const out: Record<string, OverlayViewDescriptor> = {}\n for (const spec of specs) {\n const s = spec as { name?: string; base?: string; overlay?: string }\n if (!s.name || !s.base || !s.overlay) continue\n out[s.name] = { base: s.base, overlay: s.overlay }\n }\n return out\n}\n\nfunction describeDerivations(registry: unknown): Record<string, DerivationDescriptor> {\n if (!registry || typeof registry !== 'object') return {}\n const items = listFromRegistry(registry as Record<string, unknown>)\n const out: Record<string, DerivationDescriptor> = {}\n const used = new Set<string>()\n // Pre-seed with every explicit name BEFORE keying any derivation, so an\n // unnamed derivation's fallback key never collides with (and clobbers) a\n // named one, regardless of registration order (#973).\n for (const item of items) {\n const reg = item as { spec?: { name?: string }; name?: string }\n const explicitName = (reg.spec ?? reg).name\n if (explicitName !== undefined) used.add(explicitName)\n }\n for (const item of items) {\n // `all()` on DerivationRegistry returns RegisteredStrategy objects:\n // { spec: DerivationSpec, strategyHash }. Read `spec` and fall\n // back to the item itself for forward-compat with other registries.\n const reg = item as { spec?: { name?: string; source?: string; outputs?: Record<string, { collection: string }> }; name?: string; source?: string; outputs?: Record<string, { collection: string }> }\n const s = reg.spec ?? reg\n if (!s.source) continue\n const outputCollections = s.outputs\n ? Object.values(s.outputs).map((o) => (o as { collection: string }).collection)\n : []\n // Named derivations key by their (vault-unique) `name`. Unnamed ones key\n // by the shared collision-safe fallback (sorted output collections, else\n // source, with a `#occurrence` suffix on collision) — the SAME algorithm\n // `listBehaviors` uses (`./derivation-key.ts`) so both surfaces key an\n // unnamed derivation identically.\n const key = s.name ?? fallbackDerivationName(outputCollections, s.source, used)\n if (s.name === undefined) used.add(key)\n out[key] = {\n source: s.source,\n outputs: outputCollections,\n ...(s.name !== undefined ? { name: s.name } : {}),\n }\n }\n return out\n}\n\nfunction listFromRegistry(reg: Record<string, unknown>): readonly unknown[] {\n // Registries expose different accessor methods; try the common ones.\n for (const method of ['all', 'list', 'specs', 'values']) {\n const fn = reg[method]\n if (typeof fn === 'function') {\n try {\n const out = (fn as () => unknown).call(reg)\n if (Array.isArray(out)) return out\n if (out && typeof (out as { values?: () => unknown }).values === 'function') {\n return [...(out as { values: () => Iterable<unknown> }).values()]\n }\n } catch {\n continue\n }\n }\n }\n return []\n}\n\nfunction summariseAggregateOp(value: unknown): string {\n if (value && typeof value === 'object') {\n const op = (value as { op?: string; kind?: string; field?: string }).op\n ?? (value as { kind?: string }).kind\n const field = (value as { field?: string }).field\n if (op && field) return `${op}(${field})`\n if (op) return op\n }\n return String(value)\n}\n"],"mappings":"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","names":[]}