@noy-db/hub 0.6.0-pre.20 → 0.6.0-pre.21

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 (363) hide show
  1. package/CHANGELOG.md +5234 -0
  2. package/dist/api-EYNT6KGS.js +20 -0
  3. package/dist/attestation/index.js +18 -18
  4. package/dist/{backup-K62BFMYK.js → backup-FSC36VWC.js} +12 -12
  5. package/dist/blobs/index.js +13 -13
  6. package/dist/broker/index.js +12 -12
  7. package/dist/cargo/index.js +29 -29
  8. package/dist/{chunk-GT72D4FO.js → chunk-2LFOMUEA.js} +3 -3
  9. package/dist/{chunk-W6NFONOV.js → chunk-32B7VCKU.js} +2 -2
  10. package/dist/{chunk-PG6PFMDC.js → chunk-3B6S6RSI.js} +2 -2
  11. package/dist/{chunk-DKYEVHZH.js → chunk-3ENNPCIW.js} +2 -2
  12. package/dist/{chunk-5J5TTSGW.js → chunk-3O72ZD2E.js} +3 -3
  13. package/dist/{chunk-WKD5ESK4.js → chunk-3PMFMUFX.js} +2 -2
  14. package/dist/{chunk-VEX2BQVX.js → chunk-45YHJ3GV.js} +11 -3
  15. package/dist/{chunk-VEX2BQVX.js.map → chunk-45YHJ3GV.js.map} +1 -1
  16. package/dist/{chunk-MNUTPZRQ.js → chunk-4A34KWOU.js} +6 -6
  17. package/dist/{chunk-32O2N2CH.js → chunk-4N5Q23KV.js} +7 -7
  18. package/dist/{chunk-MQXYR65M.js → chunk-4OQZNXQG.js} +6 -6
  19. package/dist/{chunk-S55DBWRC.js → chunk-4RAS6HN6.js} +2 -2
  20. package/dist/{chunk-6S466CMQ.js → chunk-4YL2LMXK.js} +8 -8
  21. package/dist/{chunk-TC6MBEGH.js → chunk-5Q7AJ7DN.js} +5 -5
  22. package/dist/{chunk-GDGTIWB4.js → chunk-6JPGVL7X.js} +2 -2
  23. package/dist/{chunk-GYUCRDAU.js → chunk-6KTVJ5ZZ.js} +4 -4
  24. package/dist/{chunk-YVTADFTC.js → chunk-6WO62AJW.js} +4 -4
  25. package/dist/{chunk-RORE2GX2.js → chunk-6YD5SYQS.js} +2 -2
  26. package/dist/{chunk-JIF3IBRK.js → chunk-7KAE7CQ2.js} +2 -2
  27. package/dist/{chunk-4HVTRHSA.js → chunk-7LNE2N2V.js} +6 -6
  28. package/dist/{chunk-M4IKEOJS.js → chunk-7PJ3BKHS.js} +6 -6
  29. package/dist/{chunk-HTHRCGWP.js → chunk-A4TOGAUW.js} +2 -2
  30. package/dist/{chunk-VJ4B3ZXY.js → chunk-AU3AUJIZ.js} +2 -2
  31. package/dist/{chunk-XGQGWZ6D.js → chunk-AUPSEJ7O.js} +4 -4
  32. package/dist/{chunk-A4K5GLE6.js → chunk-B6IYATME.js} +4 -4
  33. package/dist/{chunk-CV7WXCZD.js → chunk-BAHOV5I5.js} +10 -10
  34. package/dist/{chunk-E6VDPBE3.js → chunk-BEZDLIDG.js} +6 -6
  35. package/dist/{chunk-RKDH6L53.js → chunk-CAGI4LLJ.js} +303 -49
  36. package/dist/chunk-CAGI4LLJ.js.map +1 -0
  37. package/dist/{chunk-BTNKPDZM.js → chunk-CLAHBPOI.js} +3 -3
  38. package/dist/{chunk-P7L3OQLL.js → chunk-CWM4GQD2.js} +15 -1
  39. package/dist/chunk-CWM4GQD2.js.map +1 -0
  40. package/dist/{chunk-C7WJNVJG.js → chunk-D3R7Q4QK.js} +2 -2
  41. package/dist/{chunk-ODVIFBAQ.js → chunk-DIZ6HM7Q.js} +6 -6
  42. package/dist/{chunk-ICCRAPWD.js → chunk-DVRZYAGT.js} +4 -4
  43. package/dist/{chunk-SG3OQCRG.js → chunk-DXKLLKME.js} +3 -3
  44. package/dist/{chunk-RQY5UWEL.js → chunk-E5SC6ENM.js} +12 -12
  45. package/dist/{chunk-OIIAXFKM.js → chunk-EES37S2V.js} +3 -3
  46. package/dist/{chunk-REL4OLWW.js → chunk-EKEIEJL6.js} +2 -2
  47. package/dist/{chunk-ZBDWKPBW.js → chunk-ESKIAXO2.js} +5 -5
  48. package/dist/{chunk-HJRTFXAA.js → chunk-FFSQFH6D.js} +2 -2
  49. package/dist/{chunk-A3NTKHDB.js → chunk-FS7NTGCJ.js} +2 -2
  50. package/dist/{chunk-5BS6IQZZ.js → chunk-FW7NGYLT.js} +5 -5
  51. package/dist/{chunk-ZPMXYPBR.js → chunk-GBBDFE6C.js} +2 -2
  52. package/dist/{chunk-OQLYMBJI.js → chunk-GQXFOAYZ.js} +3 -3
  53. package/dist/{chunk-5JT4Y5UE.js → chunk-GTHVPVAR.js} +10 -10
  54. package/dist/{chunk-NH2MST6J.js → chunk-GWO2IDAB.js} +2 -2
  55. package/dist/{chunk-MNMYHZVU.js → chunk-H3G4I3PN.js} +2 -2
  56. package/dist/{chunk-PNAIDIP7.js → chunk-HQSPF5IY.js} +16 -9
  57. package/dist/{chunk-PNAIDIP7.js.map → chunk-HQSPF5IY.js.map} +1 -1
  58. package/dist/{chunk-ZWKF3CD3.js → chunk-HR5R5JOM.js} +2 -2
  59. package/dist/{chunk-2E2MKBMI.js → chunk-HYYVT2KZ.js} +2 -2
  60. package/dist/{chunk-HGJC7I5F.js → chunk-I6QKB7MT.js} +2 -2
  61. package/dist/{chunk-IPDS2TYW.js → chunk-ICDHUUMQ.js} +3 -3
  62. package/dist/{chunk-3Y4OFWQJ.js → chunk-IGNJOJEP.js} +1 -1
  63. package/dist/chunk-IGNJOJEP.js.map +1 -0
  64. package/dist/{chunk-BP4I7IBK.js → chunk-IJ46VCWD.js} +2 -2
  65. package/dist/{chunk-AQF7JSQR.js → chunk-INMHEMXX.js} +2 -2
  66. package/dist/{chunk-IOURTXTM.js → chunk-JLYQQNY4.js} +7 -7
  67. package/dist/{chunk-IOURTXTM.js.map → chunk-JLYQQNY4.js.map} +1 -1
  68. package/dist/{chunk-KDX6VEYV.js → chunk-JNMTN5D5.js} +2 -2
  69. package/dist/{chunk-ZXW3X3DQ.js → chunk-JWMBG5S3.js} +7 -7
  70. package/dist/{chunk-L2XQ776M.js → chunk-KRVS7YWY.js} +2 -2
  71. package/dist/{chunk-DJ23QZIN.js → chunk-KV6JM2IA.js} +2 -2
  72. package/dist/{chunk-EJIVFOOQ.js → chunk-KX57HODD.js} +6 -6
  73. package/dist/{chunk-6TXUVKPG.js → chunk-LGFTOU27.js} +115 -90
  74. package/dist/chunk-LGFTOU27.js.map +1 -0
  75. package/dist/{chunk-YOT7D63X.js → chunk-LN7P6MZB.js} +3 -3
  76. package/dist/{chunk-JHO6FGZX.js → chunk-LX46B56Q.js} +5 -5
  77. package/dist/{chunk-APV42WPR.js → chunk-LXECF2ZR.js} +2 -2
  78. package/dist/{chunk-VH2W6BOG.js → chunk-MSLPDFK2.js} +7 -7
  79. package/dist/{chunk-NYSD43OE.js → chunk-MZTUTEHP.js} +4 -4
  80. package/dist/{chunk-F5AJZC3U.js → chunk-NCCEDL2K.js} +30 -11
  81. package/dist/chunk-NCCEDL2K.js.map +1 -0
  82. package/dist/{chunk-KBOPY6W2.js → chunk-NR2SUJ3H.js} +2 -2
  83. package/dist/{chunk-HTMQBBF3.js → chunk-NW4TY6VC.js} +3 -3
  84. package/dist/{chunk-OWGEHDT2.js → chunk-O742QBZZ.js} +5 -5
  85. package/dist/{chunk-D5UDZANO.js → chunk-OHOKBMTM.js} +96 -8
  86. package/dist/chunk-OHOKBMTM.js.map +1 -0
  87. package/dist/{chunk-KM6YR5XF.js → chunk-OIBEJN5H.js} +2 -2
  88. package/dist/{chunk-N2HKBESK.js → chunk-OMYCSTXL.js} +4 -4
  89. package/dist/{chunk-N7YAQKAO.js → chunk-ONVD6GBY.js} +2 -2
  90. package/dist/{chunk-NRNTXBHT.js → chunk-OTHHO3KI.js} +2 -2
  91. package/dist/{chunk-KUXTWVAR.js → chunk-P2MR5UNE.js} +3 -3
  92. package/dist/{chunk-ZVAFJQZB.js → chunk-PENPHA45.js} +2 -2
  93. package/dist/{chunk-47ZFULEZ.js → chunk-PPCOKVFA.js} +2 -2
  94. package/dist/{chunk-2IK6AC6A.js → chunk-QSIEL6TL.js} +5 -5
  95. package/dist/{chunk-VHVMJWM6.js → chunk-RFO4UXKQ.js} +6 -6
  96. package/dist/{chunk-CVYXIN5B.js → chunk-RKCRPPNB.js} +3 -3
  97. package/dist/{chunk-RSFG7XHM.js → chunk-RSXQNFGO.js} +2 -2
  98. package/dist/{chunk-DYASJSKI.js → chunk-S56VYR4Q.js} +3 -3
  99. package/dist/{chunk-7RCZ436O.js → chunk-SYS46BL7.js} +2 -2
  100. package/dist/{chunk-EG6AQBYW.js → chunk-T7JGWTES.js} +8 -8
  101. package/dist/{chunk-SA77BQ7F.js → chunk-TDSN2O2V.js} +5 -5
  102. package/dist/{chunk-SFD2LL7G.js → chunk-TMBSPLUG.js} +2 -2
  103. package/dist/{chunk-5EJQNA26.js → chunk-TQPASLY4.js} +2 -2
  104. package/dist/{chunk-QPIUFRUC.js → chunk-TX5MKAHA.js} +3 -3
  105. package/dist/{chunk-TENLCGYD.js → chunk-U27Y4DJZ.js} +4 -4
  106. package/dist/{chunk-L6FSZFG7.js → chunk-VVSOPRUE.js} +2 -2
  107. package/dist/{chunk-437FDIHO.js → chunk-WVE6YKMX.js} +3 -3
  108. package/dist/{chunk-PZSS7MUM.js → chunk-YALD3SST.js} +5 -5
  109. package/dist/{chunk-WBNXZU42.js → chunk-YKXZHUOW.js} +4 -4
  110. package/dist/{chunk-RNSC2ARR.js → chunk-ZLHHE5XZ.js} +3 -3
  111. package/dist/{chunk-GILI76SU.js → chunk-ZZXLB6AS.js} +2 -2
  112. package/dist/classified/index.js +5 -5
  113. package/dist/{classified-marker-PMDG3O36.js → classified-marker-NA6HUV33.js} +3 -3
  114. package/dist/collection-facade-VBM35WAH.js +45 -0
  115. package/dist/{computed-UHCBRILW.js → computed-GSTLWSY5.js} +3 -3
  116. package/dist/consent/index.js +9 -9
  117. package/dist/cover/index.js +9 -9
  118. package/dist/custody/index.js +11 -11
  119. package/dist/{dead-filter-777VWDER.js → dead-filter-UALP5DJX.js} +2 -2
  120. package/dist/debug/index.js +1 -1
  121. package/dist/{delegation-4R5DU23X.js → delegation-JLNXM7KH.js} +11 -11
  122. package/dist/derivations/index.js +12 -12
  123. package/dist/derive-YUO3TQYN.js +22 -0
  124. package/dist/directory/index.js +10 -10
  125. package/dist/{dispatch-ASL257KG.js → dispatch-4ZHLSFZQ.js} +5 -5
  126. package/dist/{dispatch-3EN6YCCS.js → dispatch-77CB75KB.js} +18 -18
  127. package/dist/{enclave-ZJS5R5RH.js → enclave-O4NOROCJ.js} +11 -9
  128. package/dist/executor-CZDXBXKE.js +29 -0
  129. package/dist/executor-DVJAWN3B.js +9 -0
  130. package/dist/executor-VRLG4TQJ.js +9 -0
  131. package/dist/export-accessible-UXSMG7AB.js +23 -0
  132. package/dist/extract-partition-TVY7AMKH.js +38 -0
  133. package/dist/{fanout-sidecar-TFPWDOME.js → fanout-sidecar-NLQ7QAYW.js} +9 -9
  134. package/dist/find-WSARMJBW.js +11 -0
  135. package/dist/forget/index.js +9 -9
  136. package/dist/guards/index.js +7 -7
  137. package/dist/history/index.js +13 -13
  138. package/dist/i18n/index.js +14 -14
  139. package/dist/index.d.ts +2 -1
  140. package/dist/index.js +95 -91
  141. package/dist/index.js.map +1 -1
  142. package/dist/indexing/index.js +2 -2
  143. package/dist/introspection/index.js +11 -11
  144. package/dist/issue-3JNJNPT3.js +19 -0
  145. package/dist/kernel/constants.d.ts +18 -0
  146. package/dist/kernel/enclave/index.d.ts +1 -0
  147. package/dist/kernel/enclave/record-keys/rekey-blob.d.ts +88 -0
  148. package/dist/kernel/errors.d.ts +48 -1
  149. package/dist/kernel/noydb.d.ts +18 -1
  150. package/dist/kernel/types.d.ts +20 -6
  151. package/dist/kernel/vault.d.ts +1 -1
  152. package/dist/{ledger-62CEPRPX.js → ledger-MVPNG5OS.js} +11 -11
  153. package/dist/liberate-4LOJOLDT.js +24 -0
  154. package/dist/{link-set-ZRYNUHN3.js → link-set-3POMQR3I.js} +11 -11
  155. package/dist/{managed-secret-T2WB4O47.js → managed-secret-IARIJJ7Q.js} +10 -10
  156. package/dist/materialized-views/index.js +20 -20
  157. package/dist/migrate-cek-XM2ETTZW.js +19 -0
  158. package/dist/migrate-cek-XM2ETTZW.js.map +1 -0
  159. package/dist/money/index.js +4 -4
  160. package/dist/noydb-IRILUDED.js +84 -0
  161. package/dist/overlay-views/index.js +4 -4
  162. package/dist/periods/index.js +12 -12
  163. package/dist/pod/index.js +55 -55
  164. package/dist/{pod-handle-UPLXOGIK.js → pod-handle-MELERCKU.js} +9 -9
  165. package/dist/policy/index.js +10 -10
  166. package/dist/port/with/team-strategy.d.ts +5 -1
  167. package/dist/portability/index.js +8 -8
  168. package/dist/{post-register-J4DEVNXK.js → post-register-Z5T7FY45.js} +6 -6
  169. package/dist/query/index.js +3 -3
  170. package/dist/reduce/index.js +4 -4
  171. package/dist/register-UZ37QPDB.js +32 -0
  172. package/dist/registry-ETJNA7PO.js +9 -0
  173. package/dist/registry-F7U6KSLX.js +9 -0
  174. package/dist/registry-L4ELKX6C.js +18 -0
  175. package/dist/registry-SDT5LIGO.js +19 -0
  176. package/dist/request-withdrawal-PVFEYX57.js +31 -0
  177. package/dist/{reveal-525XWQU2.js → reveal-DQ2E3QAZ.js} +6 -6
  178. package/dist/revoke-YXY6SHUH.js +24 -0
  179. package/dist/satellites/index.js +1 -1
  180. package/dist/schema-update/index.js +2 -2
  181. package/dist/sealed-record/index.js +12 -12
  182. package/dist/search/index.js +4 -4
  183. package/dist/{seed-SGIF3EN4.js → seed-DZ4ORU5N.js} +11 -11
  184. package/dist/sequence/index.js +9 -9
  185. package/dist/session/index.js +9 -9
  186. package/dist/shadow/index.js +2 -2
  187. package/dist/signer-Q7XQBMPA.js +25 -0
  188. package/dist/snapshots/index.js +10 -10
  189. package/dist/{stale-PQYPVCER.js → stale-NI7SAXAZ.js} +10 -10
  190. package/dist/storage-3VNGSHXS.js +23 -0
  191. package/dist/{store-coordination-provider-TFHZLKHG.js → store-coordination-provider-TYKNR3OU.js} +10 -10
  192. package/dist/sync/index.js +10 -10
  193. package/dist/team/index.js +15 -15
  194. package/dist/tiers/index.js +11 -11
  195. package/dist/to/index.js +1 -1
  196. package/dist/transactions/index.js +3 -3
  197. package/dist/util/index.js +1 -1
  198. package/dist/vault-head/index.js +8 -8
  199. package/dist/{verify-J6DEOXP3.js → verify-6J2YLKSO.js} +5 -5
  200. package/dist/walk-WRA7RAPY.js +21 -0
  201. package/dist/with-party/team/keyring.d.ts +132 -0
  202. package/dist/with-party/team/noydb-facade.d.ts +5 -1
  203. package/dist/with-party/team/roster-tag.d.ts +101 -0
  204. package/dist/with-shape/blobs/blob-set.d.ts +2 -2
  205. package/dist/withdraw-accessible-4JTDFRFZ.js +28 -0
  206. package/package.json +4 -3
  207. package/dist/api-Z5VHH24A.js +0 -20
  208. package/dist/chunk-3Y4OFWQJ.js.map +0 -1
  209. package/dist/chunk-6TXUVKPG.js.map +0 -1
  210. package/dist/chunk-D5UDZANO.js.map +0 -1
  211. package/dist/chunk-F5AJZC3U.js.map +0 -1
  212. package/dist/chunk-P7L3OQLL.js.map +0 -1
  213. package/dist/chunk-RKDH6L53.js.map +0 -1
  214. package/dist/collection-facade-ZJK5EB33.js +0 -45
  215. package/dist/derive-3WAS2G5T.js +0 -22
  216. package/dist/executor-GPM6DNWV.js +0 -9
  217. package/dist/executor-LJSMZ4IB.js +0 -9
  218. package/dist/executor-SHUVRZUJ.js +0 -29
  219. package/dist/export-accessible-GNY6DC6U.js +0 -23
  220. package/dist/extract-partition-BG3AF4NZ.js +0 -38
  221. package/dist/find-KX6OKHDP.js +0 -11
  222. package/dist/issue-PAUM7NCX.js +0 -19
  223. package/dist/liberate-I6AOLWXW.js +0 -24
  224. package/dist/noydb-SS6EN7TK.js +0 -84
  225. package/dist/register-UPD4GKBC.js +0 -32
  226. package/dist/registry-7KX2HWFS.js +0 -18
  227. package/dist/registry-PKYYEHEW.js +0 -9
  228. package/dist/registry-XHCYMD34.js +0 -19
  229. package/dist/registry-XIRTZHPC.js +0 -9
  230. package/dist/request-withdrawal-XZSESWZV.js +0 -31
  231. package/dist/revoke-QI7YHQ2B.js +0 -24
  232. package/dist/signer-BZWDPGGN.js +0 -25
  233. package/dist/storage-MJHKNQRO.js +0 -23
  234. package/dist/walk-BX4NSB5L.js +0 -21
  235. package/dist/withdraw-accessible-K4CPMH4C.js +0 -28
  236. /package/dist/{api-Z5VHH24A.js.map → api-EYNT6KGS.js.map} +0 -0
  237. /package/dist/{backup-K62BFMYK.js.map → backup-FSC36VWC.js.map} +0 -0
  238. /package/dist/{chunk-GT72D4FO.js.map → chunk-2LFOMUEA.js.map} +0 -0
  239. /package/dist/{chunk-W6NFONOV.js.map → chunk-32B7VCKU.js.map} +0 -0
  240. /package/dist/{chunk-PG6PFMDC.js.map → chunk-3B6S6RSI.js.map} +0 -0
  241. /package/dist/{chunk-DKYEVHZH.js.map → chunk-3ENNPCIW.js.map} +0 -0
  242. /package/dist/{chunk-5J5TTSGW.js.map → chunk-3O72ZD2E.js.map} +0 -0
  243. /package/dist/{chunk-WKD5ESK4.js.map → chunk-3PMFMUFX.js.map} +0 -0
  244. /package/dist/{chunk-MNUTPZRQ.js.map → chunk-4A34KWOU.js.map} +0 -0
  245. /package/dist/{chunk-32O2N2CH.js.map → chunk-4N5Q23KV.js.map} +0 -0
  246. /package/dist/{chunk-MQXYR65M.js.map → chunk-4OQZNXQG.js.map} +0 -0
  247. /package/dist/{chunk-S55DBWRC.js.map → chunk-4RAS6HN6.js.map} +0 -0
  248. /package/dist/{chunk-6S466CMQ.js.map → chunk-4YL2LMXK.js.map} +0 -0
  249. /package/dist/{chunk-TC6MBEGH.js.map → chunk-5Q7AJ7DN.js.map} +0 -0
  250. /package/dist/{chunk-GDGTIWB4.js.map → chunk-6JPGVL7X.js.map} +0 -0
  251. /package/dist/{chunk-GYUCRDAU.js.map → chunk-6KTVJ5ZZ.js.map} +0 -0
  252. /package/dist/{chunk-YVTADFTC.js.map → chunk-6WO62AJW.js.map} +0 -0
  253. /package/dist/{chunk-RORE2GX2.js.map → chunk-6YD5SYQS.js.map} +0 -0
  254. /package/dist/{chunk-JIF3IBRK.js.map → chunk-7KAE7CQ2.js.map} +0 -0
  255. /package/dist/{chunk-4HVTRHSA.js.map → chunk-7LNE2N2V.js.map} +0 -0
  256. /package/dist/{chunk-M4IKEOJS.js.map → chunk-7PJ3BKHS.js.map} +0 -0
  257. /package/dist/{chunk-HTHRCGWP.js.map → chunk-A4TOGAUW.js.map} +0 -0
  258. /package/dist/{chunk-VJ4B3ZXY.js.map → chunk-AU3AUJIZ.js.map} +0 -0
  259. /package/dist/{chunk-XGQGWZ6D.js.map → chunk-AUPSEJ7O.js.map} +0 -0
  260. /package/dist/{chunk-A4K5GLE6.js.map → chunk-B6IYATME.js.map} +0 -0
  261. /package/dist/{chunk-CV7WXCZD.js.map → chunk-BAHOV5I5.js.map} +0 -0
  262. /package/dist/{chunk-E6VDPBE3.js.map → chunk-BEZDLIDG.js.map} +0 -0
  263. /package/dist/{chunk-BTNKPDZM.js.map → chunk-CLAHBPOI.js.map} +0 -0
  264. /package/dist/{chunk-C7WJNVJG.js.map → chunk-D3R7Q4QK.js.map} +0 -0
  265. /package/dist/{chunk-ODVIFBAQ.js.map → chunk-DIZ6HM7Q.js.map} +0 -0
  266. /package/dist/{chunk-ICCRAPWD.js.map → chunk-DVRZYAGT.js.map} +0 -0
  267. /package/dist/{chunk-SG3OQCRG.js.map → chunk-DXKLLKME.js.map} +0 -0
  268. /package/dist/{chunk-RQY5UWEL.js.map → chunk-E5SC6ENM.js.map} +0 -0
  269. /package/dist/{chunk-OIIAXFKM.js.map → chunk-EES37S2V.js.map} +0 -0
  270. /package/dist/{chunk-REL4OLWW.js.map → chunk-EKEIEJL6.js.map} +0 -0
  271. /package/dist/{chunk-ZBDWKPBW.js.map → chunk-ESKIAXO2.js.map} +0 -0
  272. /package/dist/{chunk-HJRTFXAA.js.map → chunk-FFSQFH6D.js.map} +0 -0
  273. /package/dist/{chunk-A3NTKHDB.js.map → chunk-FS7NTGCJ.js.map} +0 -0
  274. /package/dist/{chunk-5BS6IQZZ.js.map → chunk-FW7NGYLT.js.map} +0 -0
  275. /package/dist/{chunk-ZPMXYPBR.js.map → chunk-GBBDFE6C.js.map} +0 -0
  276. /package/dist/{chunk-OQLYMBJI.js.map → chunk-GQXFOAYZ.js.map} +0 -0
  277. /package/dist/{chunk-5JT4Y5UE.js.map → chunk-GTHVPVAR.js.map} +0 -0
  278. /package/dist/{chunk-NH2MST6J.js.map → chunk-GWO2IDAB.js.map} +0 -0
  279. /package/dist/{chunk-MNMYHZVU.js.map → chunk-H3G4I3PN.js.map} +0 -0
  280. /package/dist/{chunk-ZWKF3CD3.js.map → chunk-HR5R5JOM.js.map} +0 -0
  281. /package/dist/{chunk-2E2MKBMI.js.map → chunk-HYYVT2KZ.js.map} +0 -0
  282. /package/dist/{chunk-HGJC7I5F.js.map → chunk-I6QKB7MT.js.map} +0 -0
  283. /package/dist/{chunk-IPDS2TYW.js.map → chunk-ICDHUUMQ.js.map} +0 -0
  284. /package/dist/{chunk-BP4I7IBK.js.map → chunk-IJ46VCWD.js.map} +0 -0
  285. /package/dist/{chunk-AQF7JSQR.js.map → chunk-INMHEMXX.js.map} +0 -0
  286. /package/dist/{chunk-KDX6VEYV.js.map → chunk-JNMTN5D5.js.map} +0 -0
  287. /package/dist/{chunk-ZXW3X3DQ.js.map → chunk-JWMBG5S3.js.map} +0 -0
  288. /package/dist/{chunk-L2XQ776M.js.map → chunk-KRVS7YWY.js.map} +0 -0
  289. /package/dist/{chunk-DJ23QZIN.js.map → chunk-KV6JM2IA.js.map} +0 -0
  290. /package/dist/{chunk-EJIVFOOQ.js.map → chunk-KX57HODD.js.map} +0 -0
  291. /package/dist/{chunk-YOT7D63X.js.map → chunk-LN7P6MZB.js.map} +0 -0
  292. /package/dist/{chunk-JHO6FGZX.js.map → chunk-LX46B56Q.js.map} +0 -0
  293. /package/dist/{chunk-APV42WPR.js.map → chunk-LXECF2ZR.js.map} +0 -0
  294. /package/dist/{chunk-VH2W6BOG.js.map → chunk-MSLPDFK2.js.map} +0 -0
  295. /package/dist/{chunk-NYSD43OE.js.map → chunk-MZTUTEHP.js.map} +0 -0
  296. /package/dist/{chunk-KBOPY6W2.js.map → chunk-NR2SUJ3H.js.map} +0 -0
  297. /package/dist/{chunk-HTMQBBF3.js.map → chunk-NW4TY6VC.js.map} +0 -0
  298. /package/dist/{chunk-OWGEHDT2.js.map → chunk-O742QBZZ.js.map} +0 -0
  299. /package/dist/{chunk-KM6YR5XF.js.map → chunk-OIBEJN5H.js.map} +0 -0
  300. /package/dist/{chunk-N2HKBESK.js.map → chunk-OMYCSTXL.js.map} +0 -0
  301. /package/dist/{chunk-N7YAQKAO.js.map → chunk-ONVD6GBY.js.map} +0 -0
  302. /package/dist/{chunk-NRNTXBHT.js.map → chunk-OTHHO3KI.js.map} +0 -0
  303. /package/dist/{chunk-KUXTWVAR.js.map → chunk-P2MR5UNE.js.map} +0 -0
  304. /package/dist/{chunk-ZVAFJQZB.js.map → chunk-PENPHA45.js.map} +0 -0
  305. /package/dist/{chunk-47ZFULEZ.js.map → chunk-PPCOKVFA.js.map} +0 -0
  306. /package/dist/{chunk-2IK6AC6A.js.map → chunk-QSIEL6TL.js.map} +0 -0
  307. /package/dist/{chunk-VHVMJWM6.js.map → chunk-RFO4UXKQ.js.map} +0 -0
  308. /package/dist/{chunk-CVYXIN5B.js.map → chunk-RKCRPPNB.js.map} +0 -0
  309. /package/dist/{chunk-RSFG7XHM.js.map → chunk-RSXQNFGO.js.map} +0 -0
  310. /package/dist/{chunk-DYASJSKI.js.map → chunk-S56VYR4Q.js.map} +0 -0
  311. /package/dist/{chunk-7RCZ436O.js.map → chunk-SYS46BL7.js.map} +0 -0
  312. /package/dist/{chunk-EG6AQBYW.js.map → chunk-T7JGWTES.js.map} +0 -0
  313. /package/dist/{chunk-SA77BQ7F.js.map → chunk-TDSN2O2V.js.map} +0 -0
  314. /package/dist/{chunk-SFD2LL7G.js.map → chunk-TMBSPLUG.js.map} +0 -0
  315. /package/dist/{chunk-5EJQNA26.js.map → chunk-TQPASLY4.js.map} +0 -0
  316. /package/dist/{chunk-QPIUFRUC.js.map → chunk-TX5MKAHA.js.map} +0 -0
  317. /package/dist/{chunk-TENLCGYD.js.map → chunk-U27Y4DJZ.js.map} +0 -0
  318. /package/dist/{chunk-L6FSZFG7.js.map → chunk-VVSOPRUE.js.map} +0 -0
  319. /package/dist/{chunk-437FDIHO.js.map → chunk-WVE6YKMX.js.map} +0 -0
  320. /package/dist/{chunk-PZSS7MUM.js.map → chunk-YALD3SST.js.map} +0 -0
  321. /package/dist/{chunk-WBNXZU42.js.map → chunk-YKXZHUOW.js.map} +0 -0
  322. /package/dist/{chunk-RNSC2ARR.js.map → chunk-ZLHHE5XZ.js.map} +0 -0
  323. /package/dist/{chunk-GILI76SU.js.map → chunk-ZZXLB6AS.js.map} +0 -0
  324. /package/dist/{classified-marker-PMDG3O36.js.map → classified-marker-NA6HUV33.js.map} +0 -0
  325. /package/dist/{collection-facade-ZJK5EB33.js.map → collection-facade-VBM35WAH.js.map} +0 -0
  326. /package/dist/{computed-UHCBRILW.js.map → computed-GSTLWSY5.js.map} +0 -0
  327. /package/dist/{dead-filter-777VWDER.js.map → dead-filter-UALP5DJX.js.map} +0 -0
  328. /package/dist/{delegation-4R5DU23X.js.map → delegation-JLNXM7KH.js.map} +0 -0
  329. /package/dist/{derive-3WAS2G5T.js.map → derive-YUO3TQYN.js.map} +0 -0
  330. /package/dist/{dispatch-ASL257KG.js.map → dispatch-4ZHLSFZQ.js.map} +0 -0
  331. /package/dist/{dispatch-3EN6YCCS.js.map → dispatch-77CB75KB.js.map} +0 -0
  332. /package/dist/{enclave-ZJS5R5RH.js.map → enclave-O4NOROCJ.js.map} +0 -0
  333. /package/dist/{executor-GPM6DNWV.js.map → executor-CZDXBXKE.js.map} +0 -0
  334. /package/dist/{executor-LJSMZ4IB.js.map → executor-DVJAWN3B.js.map} +0 -0
  335. /package/dist/{executor-SHUVRZUJ.js.map → executor-VRLG4TQJ.js.map} +0 -0
  336. /package/dist/{export-accessible-GNY6DC6U.js.map → export-accessible-UXSMG7AB.js.map} +0 -0
  337. /package/dist/{extract-partition-BG3AF4NZ.js.map → extract-partition-TVY7AMKH.js.map} +0 -0
  338. /package/dist/{fanout-sidecar-TFPWDOME.js.map → fanout-sidecar-NLQ7QAYW.js.map} +0 -0
  339. /package/dist/{find-KX6OKHDP.js.map → find-WSARMJBW.js.map} +0 -0
  340. /package/dist/{issue-PAUM7NCX.js.map → issue-3JNJNPT3.js.map} +0 -0
  341. /package/dist/{ledger-62CEPRPX.js.map → ledger-MVPNG5OS.js.map} +0 -0
  342. /package/dist/{liberate-I6AOLWXW.js.map → liberate-4LOJOLDT.js.map} +0 -0
  343. /package/dist/{link-set-ZRYNUHN3.js.map → link-set-3POMQR3I.js.map} +0 -0
  344. /package/dist/{managed-secret-T2WB4O47.js.map → managed-secret-IARIJJ7Q.js.map} +0 -0
  345. /package/dist/{noydb-SS6EN7TK.js.map → noydb-IRILUDED.js.map} +0 -0
  346. /package/dist/{pod-handle-UPLXOGIK.js.map → pod-handle-MELERCKU.js.map} +0 -0
  347. /package/dist/{post-register-J4DEVNXK.js.map → post-register-Z5T7FY45.js.map} +0 -0
  348. /package/dist/{register-UPD4GKBC.js.map → register-UZ37QPDB.js.map} +0 -0
  349. /package/dist/{registry-7KX2HWFS.js.map → registry-ETJNA7PO.js.map} +0 -0
  350. /package/dist/{registry-PKYYEHEW.js.map → registry-F7U6KSLX.js.map} +0 -0
  351. /package/dist/{registry-XHCYMD34.js.map → registry-L4ELKX6C.js.map} +0 -0
  352. /package/dist/{registry-XIRTZHPC.js.map → registry-SDT5LIGO.js.map} +0 -0
  353. /package/dist/{request-withdrawal-XZSESWZV.js.map → request-withdrawal-PVFEYX57.js.map} +0 -0
  354. /package/dist/{reveal-525XWQU2.js.map → reveal-DQ2E3QAZ.js.map} +0 -0
  355. /package/dist/{revoke-QI7YHQ2B.js.map → revoke-YXY6SHUH.js.map} +0 -0
  356. /package/dist/{seed-SGIF3EN4.js.map → seed-DZ4ORU5N.js.map} +0 -0
  357. /package/dist/{signer-BZWDPGGN.js.map → signer-Q7XQBMPA.js.map} +0 -0
  358. /package/dist/{stale-PQYPVCER.js.map → stale-NI7SAXAZ.js.map} +0 -0
  359. /package/dist/{storage-MJHKNQRO.js.map → storage-3VNGSHXS.js.map} +0 -0
  360. /package/dist/{store-coordination-provider-TFHZLKHG.js.map → store-coordination-provider-TYKNR3OU.js.map} +0 -0
  361. /package/dist/{verify-J6DEOXP3.js.map → verify-6J2YLKSO.js.map} +0 -0
  362. /package/dist/{walk-BX4NSB5L.js.map → walk-WRA7RAPY.js.map} +0 -0
  363. /package/dist/{withdraw-accessible-K4CPMH4C.js.map → withdraw-accessible-4JTDFRFZ.js.map} +0 -0
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/with-commit/numbering/descriptor.ts","../src/via/computed/binding.ts","../src/via/computed/descriptor.ts","../src/via/lookup/binding.ts","../src/via/lookup/descriptor.ts","../src/with-sync/credentials.ts"],"sourcesContent":["/**\n * @category capability\n * Deferred-numbering config descriptor. See\n * design-history/2026-06-08-sealed-numbering-and-store-clock-design.md.\n */\n\n/** A registered deferred-numbering series. */\nexport interface DeferredNumberingConfig {\n /** Series name — the key passed to `vault.sequence(series)`. */\n readonly series: string\n /** Collection holding the records to number. */\n readonly collection: string\n /** Field on each record where the assigned serial is written. */\n readonly field: string\n /**\n * Minimum wall-clock age (ms) before an entry is eligible at a pass, in\n * addition to the interval commit-wait. Default 0 — the store-clock\n * interval (`storeLatest ≤ now.earliest`) is the correctness mechanism.\n */\n readonly settleWindowMs: number\n}\n\n/**\n * Options for {@link withDeferredNumbering} (#844b — was an inline literal, so\n * unnameable). Same shape as {@link DeferredNumberingConfig} except\n * `settleWindowMs` is optional; the factory defaults it to 0.\n */\nexport interface WithDeferredNumberingOptions {\n /** Series name — the key passed to `vault.sequence(series)`. */\n readonly series: string\n /** Collection holding the records to number. */\n readonly collection: string\n /** Field on each record where the assigned serial is written. */\n readonly field: string\n /** See {@link DeferredNumberingConfig.settleWindowMs}. Default 0. */\n readonly settleWindowMs?: number\n}\n\n/** Declare a deferred-numbering series. Pass the result in `createNoydb({ numbering: [...] })`. */\nexport function withDeferredNumbering(opts: WithDeferredNumberingOptions): DeferredNumberingConfig {\n return {\n series: opts.series,\n collection: opts.collection,\n field: opts.field,\n settleWindowMs: opts.settleWindowMs ?? 0,\n }\n}\n","/**\n * The `computed` via-binding (#638 Task 7) — covers ONLY `mode: 'virtual'`\n * fields. Materialized entries never reach here: they compile into\n * `mergedComputed` (`kernel/collection-config.ts`) and run through the\n * EXISTING stage-5 `evalComputedFields` write path, byte-for-byte unchanged\n * (the behavior lock).\n *\n * A virtual field is computed on READ, inside `present()` — the money-\n * `Formatted`/i18n-`Label` precedent (seam map Part 4) generalized to a\n * user-declared function. It is NEVER stored (no `encodeAtRest`/\n * `decodeAtRest` — the field never appears in `_data` or any `_sealed`\n * slot), and its posture is fixed `queryable: 'none'` — there is no stored/\n * indexed form to query against, regardless of what its sources would\n * otherwise permit (`ViaGraph`'s grain-'virtual' clamp, `kernel/via/graph.ts`,\n * is the belt; this binding's own static posture is the suspenders for a\n * depsless virtual field, which registers no graph edge at all).\n *\n * Export/read redaction for a TAINTED virtual field (sourced from a\n * classified/sealed field) is NOT this binding's job — it stays ignorant of\n * taint, exactly like `evalComputedFields` stays ignorant of it for\n * materialized fields. `kernel/via/taint-binding.ts#taintBinding` (appended\n * to the pipeline by `via/graph-wiring.ts#applyTaintOverlay`, AFTER this\n * binding) overwrites a tainted virtual field's value with the same\n * `EXPORT_REDACTION_MARKER` on every present() — the one enforcement seam\n * every via feature's taint already routes through.\n */\nimport type { ViaBinding, ViaPosture } from '../../kernel/via/index.js'\nimport { installViaBinder } from '../../kernel/via/index.js'\nimport type { ComputedDescriptor } from './descriptor.js'\n\nexport interface ComputedViaConfig {\n /** field name -> its virtual-mode descriptor. Materialized entries are never present here. */\n readonly virtualFields: ReadonlyMap<string, ComputedDescriptor>\n}\n\nconst VIRTUAL_POSTURE: ViaPosture = { encryptedAtRest: 'envelope', queryable: 'none', exportable: true, forgettable: false }\n\nexport function computedBinding(cfg: ComputedViaConfig): ViaBinding {\n const fields = cfg.virtualFields\n return {\n brand: 'computed',\n posture: VIRTUAL_POSTURE,\n covers: (field) => fields.has(field),\n present: (record) => {\n let r = record\n for (const [field, desc] of fields) {\n if (r === record) r = { ...record }\n r[field] = desc.fn(r)\n }\n return r\n },\n }\n}\n\nexport function linkComputedVia(): void {\n installViaBinder('computed', (cfg) => computedBinding(cfg as ComputedViaConfig))\n}\n","/**\n * computed() — the declaration factory for a field whose value is DERIVED\n * from other fields on the same record (#638 Task 7, spec §6). Composes\n * with `via()` (the locked grammar since phase A: `via(computed(fn, { deps,\n * mode }), money('EUR'))`), grouped by `_viaBrand` like every other via\n * feature (`kernel/via/compose.ts#mergeViaFields`).\n *\n * `mode` picks where the function runs:\n * - `'materialized'` (default) — TODAY's stage-5 write-time eager compute\n * (`with-formula/computed/index.ts#evalComputedFields`), stored like any\n * other field. Byte-for-byte the existing `computed: { field: fn }` sugar.\n * - `'virtual'` — rides the `present` read phase (the money-`Formatted`/\n * i18n-`Label` precedent, seam map Part 4): computed fresh on every\n * read, NEVER stored, `queryable: 'none'`, excluded from export unless\n * its declared `deps` permit (identical taint rule to materialized —\n * see `via/computed/binding.ts`).\n *\n * `deps` names the OTHER fields `fn` reads — feeds `ViaGraph` (Task 1/2) so\n * a source's taint (e.g. a classified field) propagates to this derived\n * field. A depsless entry is fine UNLESS the collection also declares\n * classified fields (`kernel/collection-config.ts#resolveComputedEdges`\n * refuses it — closes the #636 opaque-function leak).\n */\nimport type { ViaDescriptor } from '../../kernel/via/index.js'\nimport { linkComputedVia } from './binding.js'\n\nexport interface ComputedDescriptor extends ViaDescriptor {\n readonly _viaBrand: 'computed'\n readonly fn: (record: Record<string, unknown>) => unknown\n readonly deps?: readonly string[]\n readonly mode: 'materialized' | 'virtual'\n}\n\nexport function computed(\n fn: (record: Record<string, unknown>) => unknown,\n opts?: { readonly deps?: readonly string[]; readonly mode?: 'materialized' | 'virtual' },\n): ComputedDescriptor {\n // Self-link, exactly as `money()` and `lookup()` do (#813). Until this call\n // existed, `computed` was the only via feature whose binder was installed by a\n // DIFFERENT module — `port/with/computed-strategy.ts` — rather than by its own\n // declaration factory. That works whenever the kernel spine and the consumer's\n // `computed` import resolve to one module instance, and fails when they do not:\n // under vitest's `server.deps.inline`, a consumer got the descriptor from one\n // transformed instance while the binder registry consulted at bind time lived in\n // another, producing `VIA_NOT_LINKED` for a descriptor `isComputedDescriptor()`\n // accepted. money/i18n/lookup were immune precisely because they self-link.\n //\n // Constructing a descriptor is the binding's opt-in unit, so linking here makes\n // the guarantee local: whatever instance produced the descriptor also has the\n // binder. `installViaBinder` is idempotent + first-wins, so the eager call in\n // `port/with/computed-strategy.ts` stays harmless.\n linkComputedVia()\n return {\n _viaBrand: 'computed',\n fn,\n ...(opts?.deps !== undefined ? { deps: opts.deps } : {}),\n mode: opts?.mode ?? 'materialized',\n }\n}\n\nexport function isComputedDescriptor(x: unknown): x is ComputedDescriptor {\n return typeof x === 'object' && x !== null && (x as { _viaBrand?: unknown })._viaBrand === 'computed'\n}\n","/**\n * The `'lookup'` `ViaBinding` — wires the lookup engine (present-time label\n * dressing across all three backing tiers) into the kernel's generic Via\n * port. Mirrors `via/i18n/binding.ts`'s #553 static-link pattern; the\n * present-time label-dressing algorithm below is adapted from\n * `via-i18n/binding.ts:253-337` (the same wildcard/array/scalar handling,\n * the same `onMissing`/`substitute` policy engine), generalized to branch on\n * `backing` instead of a static-vs-dynamic descriptor-shape check. For the\n * `'static'` and `'reserved'` tiers this delegates to `cfg.lookupLabelResolver`\n * — the SAME vault-built closure the i18n binding's `dictLabelResolver` uses\n * (static table first, else the `vault.dictionary()` handle) — so a native\n * `dict()`/`lookup(static)` field resolves through the identical label data\n * as its `dictKey()`/`staticDict()` alias (the byte-equivalence lock).\n *\n * `lookup()`/`enumOf()`/`dict()` each call {@link linkLookupVia} first — the\n * same #553 pattern `money()`/`dictKey()` use.\n *\n * `buildClause` (label-predicate queries) is still undeclared — out of scope\n * for #650. `compareForOrder` (#650 Task 6, spec §5; matrix tier added Task\n * 7) resolves a `sortBy`-declared field's ordering via `cfg.snapshotFor`'s\n * sync snapshot; the hook signature is UNCHANGED (`via/index.ts:128-129` — no\n * locale param), so it closes over each descriptor's own `displayLocale`\n * (the same locale-less-hinge default `runLookupPresent`'s\n * `hasStaticDisplay` branch already uses). `resolveOrderLabel` (#650 Task\n * 7) is the PER-CALL-locale sibling `orderBy(..., {by:'label'})` needs —\n * see its own doc comment below. `describeFragment` (#650 Task 7) is the\n * first-ever consumed `ViaBinding.describeFragment` implementation — see\n * `with-shape/introspection/describe.ts`'s `buildDescription`.\n */\nimport type { ViaBinding, ViaReadCtx } from '../../kernel/via/index.js'\nimport { installViaBinder } from '../../kernel/via/index.js'\nimport type { LookupDescriptor, LookupBacking, Vocabulary, OnDelete } from './descriptor.js'\nimport { resolvePolicy, type Layer } from '../i18n/policy.js'\nimport { LocaleNotSpecifiedError, UnknownLookupKeyError, ValidationError } from '../../kernel/errors.js'\nimport { getAtPath, setAtPathInPlace } from '../../kernel/paths.js'\nimport type { MaterializedBacking } from './registry.js'\nimport { buildLookupSnapshot } from './snapshot.js'\n\n/**\n * Config a collection's lookup declarations resolve to — the binding's\n * construction input. `lookupLabelResolver`/`getLookupBacking`/`membership`/\n * `snapshotFor` are vault-built closures (never a `Collection` handle,\n * keyring, or DEK/CEK — the zero-knowledge boundary).\n */\nexport interface LookupViaConfig {\n readonly lookupFields: Record<string, LookupDescriptor>\n readonly lookupLabelResolver?: (dimension: string, key: string, locale: string, fallback?: unknown) => Promise<string | undefined>\n /**\n * The matrix (collection) tier's present-time backing-row source, keyed by the full\n * descriptor (not a bare dimension name) so the closure can resolve by `descriptor.key`,\n * not the backing row's PUT-id (#651 Task 3 — the same descriptor-gaining dispatch Task 7\n * already applied to `snapshotFor`).\n */\n readonly getLookupBacking?: (descriptor: LookupDescriptor) => (key: string) => Promise<Record<string, unknown> | undefined>\n /** Closed-vocabulary write-time membership test (#650 Task 3) — `(field, key) => known?`. */\n readonly membership?: (field: string, key: string) => boolean | Promise<boolean>\n /** Sync per-descriptor altKey index — `ingest`'s normalization source (#650 Task 3). */\n readonly getAltIndex?: (desc: LookupDescriptor) => MaterializedBacking | undefined\n /**\n * Sync materialized `key -> row` rows for a lookup descriptor (#650 Task\n * 6, spec §5; matrix-tier routing added #650 Task 7) — `compareForOrder`\n * and `resolveOrderLabel` below, and `snapshot.ts`'s `presentForJoin`\n * builder, all read this same vault-built closure. Reserved AND\n * collection (matrix) tier route here (the vault wires\n * `dimension -> LookupHandle.snapshotEntries()` / `dimension ->\n * collection.querySourceForJoin().snapshot()` respectively, keyed by\n * `descriptor.key` for the matrix case — see `registry.ts`'s\n * `buildLookupSnapshotRows`); static tier is resolved locally from\n * `descriptor.table` without calling this.\n */\n readonly snapshotFor?: (descriptor: LookupDescriptor) => ReadonlyMap<string, Record<string, unknown>> | undefined\n readonly collectionName: string\n}\n\n/** Enum tier (`backing:'static'`, no `table`) has no label source at all — never dressed. */\nfunction hasLabelSource(desc: LookupDescriptor): boolean {\n return !(desc.backing === 'static' && desc.table === undefined)\n}\n\n/**\n * Resolve one key's label. `'static'`/`'reserved'` both go through\n * `cfg.lookupLabelResolver` (mirrors `dictLabelResolver`'s own static-table-\n * first-else-reserved-handle branching — the SAME closure is reused, see\n * `kernel/vault.ts`). `'collection'` (matrix tier) reads the declared\n * `present.label` off the backing row via `cfg.getLookupBacking`; when\n * `present.by` is set, that field's value is a `{ locale -> label }` map\n * indexed by the effective locale.\n */\nasync function fetchLookupLabel(\n desc: LookupDescriptor,\n key: string,\n effLocale: string,\n fieldFallback: string | readonly string[] | undefined,\n cfg: LookupViaConfig,\n): Promise<string | undefined> {\n if (desc.backing === 'collection') {\n const getRow = cfg.getLookupBacking?.(desc)\n const row = getRow ? await getRow(key) : undefined\n const labelField = desc.present?.label\n if (!row || labelField === undefined) return undefined\n const raw = row[labelField]\n if (desc.present?.by !== undefined) {\n return raw && typeof raw === 'object' ? (raw as Record<string, unknown>)[effLocale] as string | undefined : undefined\n }\n return typeof raw === 'string' ? raw : undefined\n }\n return cfg.lookupLabelResolver?.(desc.dimension, key, effLocale, fieldFallback)\n}\n\n/** `present` — resolve `<field>Label` for every declared lookup field. Adapted from `via-i18n/binding.ts:253-337`. */\nasync function runLookupPresent(\n record: Record<string, unknown>,\n ctx: ViaReadCtx,\n cfg: LookupViaConfig,\n): Promise<Record<string, unknown>> {\n const fields = Object.entries(cfg.lookupFields).filter(([, d]) => hasLabelSource(d))\n if (fields.length === 0) return record\n\n const locale = typeof ctx.locale === 'string' ? ctx.locale : undefined\n const fallback = ctx.fallback as string | readonly string[] | undefined\n const layer = ctx.layer as Layer\n\n // `{ locale: 'raw' }` wants the untouched record — mirrors\n // `via-i18n/binding.ts`'s `locale !== 'raw'` dict-label gate exactly (no\n // synthetic `<field>Label` derivative on a raw read).\n if (locale === 'raw') return record\n\n // Static-display hybrid hinge: a `backing:'static'` field with a declared\n // `displayLocale` resolves its `<field>Label` even under a locale-less\n // read (mirrors staticDict's `hasStaticDisplay` gate).\n const hasStaticDisplay = fields.some(([, d]) => d.backing === 'static' && d.displayLocale !== undefined)\n if (!locale && !hasStaticDisplay) return record\n\n let result = record\n const withLabels = { ...result }\n\n for (const [field, desc] of fields) {\n const policy = desc.onMissing ? resolvePolicy(desc.onMissing, layer) : 'null'\n const fieldFallback = policy === 'substitute' ? (fallback ?? desc.substitute) : fallback\n const effLocale = locale ?? (desc.backing === 'static' ? desc.displayLocale : undefined)\n\n const resolveKey = async (key: string): Promise<string | null> => {\n if (!effLocale) {\n if (policy === 'throw') {\n throw new LocaleNotSpecifiedError(field, `lookup \"${field}\": no locale active to resolve key \"${key}\".`)\n }\n return null\n }\n const label = await fetchLookupLabel(desc, key, effLocale, fieldFallback, cfg)\n if (label === undefined) {\n if (policy === 'throw') {\n throw new LocaleNotSpecifiedError(field, `lookup \"${field}\": no label for key \"${key}\" in locale \"${effLocale}\".`)\n }\n return null\n }\n return label\n }\n\n if (field.includes('[].')) {\n const parts = field.split('[].')\n const arrayKey = parts[0]!\n const leaf = parts[1]\n if (!leaf || leaf.includes('.')) continue\n const arr = (withLabels as Record<string, unknown>)[arrayKey]\n if (!Array.isArray(arr)) continue\n const labelKey = `${leaf}Label`\n ;(withLabels as Record<string, unknown>)[arrayKey] = await Promise.all(\n arr.map(async (el) => {\n if (!el || typeof el !== 'object' || Array.isArray(el)) return el\n const k = (el as Record<string, unknown>)[leaf]\n if (typeof k !== 'string') return el\n return { ...(el as Record<string, unknown>), [labelKey]: await resolveKey(k) }\n }),\n )\n continue\n }\n\n const val = result[field]\n if (Array.isArray(val)) {\n withLabels[`${field}Label`] = await Promise.all(\n val.map(async (k) => ({ key: k, label: typeof k === 'string' ? await resolveKey(k) : null })),\n )\n } else if (typeof val === 'string') {\n const label = await resolveKey(val)\n if (label !== null) withLabels[`${field}Label`] = label\n }\n }\n\n result = withLabels\n return result\n}\n\n/**\n * One field's `lookup` descriptor as it appears on a `ViaBinding.\n * describeFragment()` payload (#650 Task 7 — the first real consumer,\n * `with-shape/introspection/describe.ts`'s `buildDescription`, imports this\n * type directly; `describe.ts` is NOT under `kernel/**`, so it's free to\n * import concrete via/ types the way it already does for\n * `LookupDescriptor`/`MoneyDescriptor`/etc.). `dimension` is OMITTED (not\n * emitted as `''`) for a bare `enumOf()` descriptor — the #650 Task 2\n * `dimension:''` sentinel resolved: no dimension name means no `dimension`\n * key, not a meaningless empty string (T2 carry, #650 Task 7).\n */\nexport interface LookupDescribeFragmentEntry {\n readonly dimension?: string\n readonly backing: LookupBacking\n readonly vocabulary: Vocabulary\n readonly key: string\n readonly altKeys?: readonly string[]\n readonly present?: { readonly label: string; readonly by?: string }\n readonly sortBy?: string\n readonly onDelete: OnDelete\n /** Statically-known closed-vocabulary key set (declared `keys`, or a static table's own keys). Omitted when membership lives only in the backing collection/dictionary (open vocabulary, or closed with no declared `keys`). */\n readonly keys?: readonly string[]\n}\n\n/** The `'lookup'` binding's `describeFragment()` payload shape. */\nexport interface LookupDescribeFragment {\n readonly lookupFields: Record<string, LookupDescribeFragmentEntry>\n}\n\nfunction buildLookupDescribeFragment(cfg: LookupViaConfig): Record<string, unknown> {\n const lookupFields: Record<string, LookupDescribeFragmentEntry> = {}\n for (const [field, desc] of Object.entries(cfg.lookupFields)) {\n lookupFields[field] = {\n ...(desc.dimension !== '' ? { dimension: desc.dimension } : {}),\n backing: desc.backing,\n vocabulary: desc.vocabulary,\n key: desc.key,\n onDelete: desc.onDelete,\n ...(desc.altKeys !== undefined ? { altKeys: desc.altKeys } : {}),\n ...(desc.present !== undefined ? { present: desc.present } : {}),\n ...(desc.sortBy !== undefined ? { sortBy: desc.sortBy } : {}),\n // #657 — static tier: when no `keys` was explicitly declared but a\n // `table` was (the staticDict()-equivalent `lookup(dim, {backing:\n // 'static', table})` shape), the table's own key set IS the\n // statically-known closed-vocabulary set the DescribedField.lookup\n // docblock promises (\"declared `keys`, OR a static table's own\n // keys\"). Reserved/matrix tiers never carry `table`, so this branch\n // never fires for them — their `keys` emission is unchanged.\n ...(desc.keys !== undefined\n ? { keys: desc.keys }\n : desc.backing === 'static' && desc.table !== undefined ? { keys: Object.keys(desc.table) } : {}),\n }\n }\n return { lookupFields }\n}\n\n/**\n * `cfg.getAltIndex(desc)` (matrix tier) reads the backing collection's cache\n * via `querySourceForJoin()` (`buildLookupAltIndex`, registry.ts) — which\n * throws a `.join()`-branded message when that collection was opened\n * `{prefetch:false}` (lazy mode is unsupported for altKey normalization).\n * Normalization must never silently not happen (the banned silent-no-op\n * class, #650 Task 3 review, Important 2) — detect that specific failure\n * and surface a CLEAR, lookup-branded `ValidationError` at the point of\n * failure instead of letting the confusing join-branded one leak onto an\n * unrelated `put()`. Any OTHER error (e.g. `materializeBackingTable`'s own\n * altKey-collision `ValidationError`) propagates unchanged.\n */\nfunction getAltIndexOrThrow(field: string, desc: LookupDescriptor, cfg: LookupViaConfig): MaterializedBacking | undefined {\n try {\n return cfg.getAltIndex?.(desc)\n } catch (err) {\n if (err instanceof Error && err.message.includes('lazy-mode')) {\n throw new ValidationError(\n `lookup: altKeys on \"${field}\" require the backing collection \"${desc.dimension}\" to be ` +\n `prefetch-enabled (lazy mode unsupported); open it without {prefetch:false} or drop altKeys.`,\n )\n }\n throw err\n }\n}\n\n/**\n * `ingest` — altKey candidate values normalize to the canonical key (#650\n * Task 3, spec §3). Pure, sync, idempotent (a canonical key maps to\n * itself); no store read — consults the pre-materialized\n * `cfg.getAltIndex(desc)`. The money `canonicalizeIncomingMoney` precedent\n * (`via/index.ts:108`).\n *\n * A `[].`-wildcard multi-value path (`getAtPath` resolves >1 entries — an\n * array of nested objects, e.g. `'lines[].country'`, the same wildcard\n * convention `runLookupPresent` above already handles) normalizes EVERY\n * element's leaf value, not just a lone scalar (#652 fix — this used to bail\n * out entirely here while `runLookupEnforceWrite` below validated every\n * value unnormalized, so a legitimate altKey in an array position was\n * wrongly refused by closed-vocabulary enforcement). `setAtPathInPlace`\n * can't write into a `[].`-wildcard path, so the array is reconstructed\n * immutably instead, mirroring `runLookupPresent`'s own `field.includes\n * ('[].')` branch. A plain field whose OWN value is a bare top-level array\n * (not a `[].`-wildcard path) gets the SAME element-wise normalization\n * against `backing.altIndex` (#661 fix — `getAtPath` resolves it to one\n * opaque value, so the array itself is `values[0]`; a non-string element is\n * left untouched, mirroring the scalar branch's own `typeof !== 'string'`\n * skip — no parallel coercion invented for this shape).\n */\nfunction runLookupIngest(record: Record<string, unknown>, cfg: LookupViaConfig): Record<string, unknown> {\n const withAltKeys = Object.entries(cfg.lookupFields).filter(([, d]) => (d.altKeys?.length ?? 0) > 0)\n if (withAltKeys.length === 0) return record\n\n let result = record\n for (const [field, desc] of withAltKeys) {\n const backing = getAltIndexOrThrow(field, desc, cfg)\n if (!backing || backing.altIndex.size === 0) continue\n\n if (field.includes('[].')) {\n const [arrayKey, leaf] = field.split('[].')\n if (!leaf || leaf.includes('.')) continue\n const arr = record[arrayKey!]\n if (!Array.isArray(arr)) continue\n let changed = false\n const normalized = arr.map((item) => {\n if (!item || typeof item !== 'object' || Array.isArray(item)) return item\n const value = (item as Record<string, unknown>)[leaf]\n if (typeof value !== 'string') return item\n const canonical = backing.altIndex.get(value)\n if (canonical === undefined || canonical === value) return item\n changed = true\n return { ...(item as Record<string, unknown>), [leaf]: canonical }\n })\n if (!changed) continue\n if (result === record) result = { ...record }\n result[arrayKey!] = normalized\n continue\n }\n\n const values = getAtPath(record, field)\n if (values.length !== 1) continue\n const value = values[0]\n\n // `getAtPath`/`setAtPathInPlace` resolve `field` generically, dotted\n // paths included (e.g. 'meta.tags') — do not rewrite this branch to a\n // direct `record[field]` bracket access, which would silently stop\n // normalizing/enforcing a dotted-non-wildcard bare-array field (#661).\n if (Array.isArray(value)) {\n if (value.length === 0) continue\n let changed = false\n const normalized = value.map((el) => {\n if (typeof el !== 'string') return el\n const canonical = backing.altIndex.get(el)\n if (canonical === undefined || canonical === el) return el\n changed = true\n return canonical\n })\n if (!changed) continue\n if (result === record) result = { ...record }\n setAtPathInPlace(result, field, normalized)\n continue\n }\n\n if (typeof value !== 'string') continue\n const canonical = backing.altIndex.get(value)\n if (canonical === undefined || canonical === value) continue\n if (result === record) result = { ...record }\n setAtPathInPlace(result, field, canonical)\n }\n return result\n}\n\n/**\n * `enforceWrite` — closed-vocabulary write refusal (#650 Task 3, spec §3).\n * Runs only for `vocabulary:'closed'` fields; `'open'` (the dictKey/dict\n * default) skips the check entirely, so #649's fix is additive — existing\n * dictKey/staticDict collections are unaffected. `ctx` carries no\n * cross-collection door (`id`/`vault`/`prior`/`emit` only, unchanged) —\n * membership is a vault-built closure on `cfg`, per spec.\n */\nasync function runLookupEnforceWrite(record: Record<string, unknown>, cfg: LookupViaConfig): Promise<void> {\n for (const [field, desc] of Object.entries(cfg.lookupFields)) {\n if (desc.vocabulary !== 'closed') continue\n // Checks every value `getAtPath` returns — for a `[].`-wildcard\n // multi-value path, that's every element's leaf value.\n // `runLookupIngest` above now normalizes each of those elements first\n // (#652), so what lands here for a `[].`-wildcard field is already\n // canonical; this loop's job stays membership, not normalization.\n for (const value of getAtPath(record, field)) {\n // A plain field whose OWN value is a bare top-level array (#661) —\n // `runLookupIngest` above has already normalized its elements'\n // altKeys, so membership-check each element through the SAME\n // `cfg.membership` closure the scalar branch below uses, refusing on\n // the first unknown one. A non-string element is skipped, mirroring\n // the scalar branch's own skip.\n if (Array.isArray(value)) {\n for (const el of value) {\n if (typeof el !== 'string') continue\n const known = cfg.membership ? await cfg.membership(field, el) : true\n if (!known) throw new UnknownLookupKeyError(desc.dimension, field, el)\n }\n continue\n }\n if (typeof value !== 'string') continue\n const known = cfg.membership ? await cfg.membership(field, value) : true\n if (!known) throw new UnknownLookupKeyError(desc.dimension, field, value)\n }\n }\n}\n\n/**\n * `compareForOrder` — exact ordering for a `sortBy`-declared lookup field\n * against the sync snapshot (#650 Task 6, spec §5, conflict resolution 4;\n * matrix-tier coverage added #650 Task 7). Opt-in: undeclared `sortBy`\n * (every dictKey/staticDict alias and every lookup field declared before\n * Task 6) returns `undefined` — falls through to the generic stored-value\n * comparator, byte-identical to today. Static tier reads `descriptor.table`\n * directly; reserved AND matrix (collection) tier both read `cfg.snapshotFor`\n * (the SAME live cache `presentForJoin`'s lookup half reads — see\n * `snapshot.ts`'s file header). The hook has no locale parameter\n * (`via/index.ts:128-129`, unchanged) — closes over the descriptor's own\n * `displayLocale` (the same locale-less-hinge default `runLookupPresent`\n * already uses); a `sortBy` field whose value isn't locale-keyed\n * (`present.by` undefined) never needs one. A `by`-keyed `sortBy` field\n * with NO declared `displayLocale` degrades to comparing the raw canonical\n * keys (silent — `LookupSnapshot.compareKeys` never throws; declare-time\n * warning at `descriptor.ts`'s `lookup()` factory; use\n * `orderBy(field, dir, {by:'label'})` — `resolveOrderLabel` below — for a\n * PER-CALL locale instead).\n */\nfunction compareLookupOrder(field: string, a: unknown, b: unknown, cfg: LookupViaConfig): number | undefined {\n if (typeof a !== 'string' || typeof b !== 'string') return undefined\n const desc = cfg.lookupFields[field]\n if (!desc || desc.sortBy === undefined) return undefined\n const rows = desc.backing === 'static'\n ? (desc.table ? new Map(Object.entries(desc.table)) : undefined)\n : cfg.snapshotFor?.(desc)\n if (!rows) return undefined\n return buildLookupSnapshot(desc.dimension, rows, desc).compareKeys(a, b, desc.displayLocale ?? '')\n}\n\n/**\n * `resolveOrderLabel` — per-key, PER-CALL-locale label resolution for\n * `orderBy(field, dir, { by: 'label' })` (#650 Task 7, spec §6 / seam map\n * Part 10 surprise 6's option (b)) — the channel `compareForOrder` above\n * structurally cannot serve, since `ViaBinding.compareForOrder` carries no\n * locale parameter. Consumed by `kernel/query/builder.ts`'s\n * `buildOrderLabelMaps` as the fallback for lookup fields the legacy dict\n * registries don't bridge (matrix tier; reserved/static tier already\n * resolves via that bridge — `JoinContext.resolveDictSource` — tried\n * FIRST by the caller, see `registry.ts`'s `collectLookupDictCompat` doc\n * comment). Reuses the exact same `cfg.snapshotFor`/`buildLookupSnapshot`\n * machinery as `compareLookupOrder` above, just with the per-call `locale`\n * in place of the descriptor's own `displayLocale` — falling back to\n * `displayLocale` only when the call itself is locale-less, the same\n * hinge order `runLookupPresent` already uses.\n */\nfunction resolveLookupOrderLabel(field: string, key: string, locale: string | undefined, cfg: LookupViaConfig): string | undefined {\n const desc = cfg.lookupFields[field]\n if (!desc) return undefined\n const rows = desc.backing === 'static'\n ? (desc.table ? new Map(Object.entries(desc.table)) : undefined)\n : cfg.snapshotFor?.(desc)\n if (!rows) return undefined\n return buildLookupSnapshot(desc.dimension, rows, desc).label(key, locale ?? desc.displayLocale ?? '')\n}\n\nexport function lookupBinding(cfg: LookupViaConfig): ViaBinding {\n return {\n brand: 'lookup',\n posture: { encryptedAtRest: 'envelope', queryable: 'full', exportable: true, forgettable: false },\n reservedPrefixes: ['_dict_', '_lookup_'],\n covers: (field) => field in cfg.lookupFields,\n ingest: (record) => runLookupIngest(record, cfg),\n enforceWrite: (record) => runLookupEnforceWrite(record, cfg),\n present: async (record, ctx) => runLookupPresent(record, ctx, cfg),\n compareForOrder: (field, a, b) => compareLookupOrder(field, a, b, cfg),\n resolveOrderLabel: (field, key, locale) => resolveLookupOrderLabel(field, key, locale, cfg),\n describeFragment: () => buildLookupDescribeFragment(cfg),\n }\n}\n\nexport function linkLookupVia(): void {\n installViaBinder('lookup', (c) => lookupBinding(c as LookupViaConfig))\n}\n","/**\n * `lookup()` / `enumOf()` / `dict()` descriptors — the three declaration\n * surfaces for the `'lookup'` via binding (#650 Task 2, phase D of the Via\n * port). One `LookupDescriptor` shape; the tiers are `backing` details:\n *\n * - `lookup(dimension, opts?)` — matrix tier: first-class collection backing\n * (default `backing:'collection'`) — a reference-collection dimension\n * (e.g. `countries`), open vocabulary by default.\n * - `enumOf(keys)` — enum tier: static in-config table, CLOSED\n * vocabulary, no backing store at all (no dimension name — pure inline\n * keys). Exported as `enumOf`; the barrel (`index.ts`) re-exports it as\n * `enum` (`enum` is a reserved word, so it can't be the function's own\n * name).\n * - `dict(dimension, opts?)` — dict tier: reserved `_dict_<dimension>`\n * micro-collection backing (the `vault.dictionary(name)` engine), open\n * vocabulary by default.\n *\n * Each factory calls {@link linkLookupVia} first — the declaration IS the\n * via binding's opt-in unit (#553), same pattern as `money()`/`i18nText()`/\n * `dictKey()`.\n */\n\nimport type { OnMissingPolicy } from '../i18n/policy.js'\nimport { linkLookupVia } from './binding.js'\n\n/** `'closed'` = enum semantics (membership enforced, Task 3); `'open'` permits unknown keys. */\nexport type Vocabulary = 'open' | 'closed'\n\n/** Where a dimension's rows live: static in-config table / reserved micro-collection / first-class collection. */\nexport type LookupBacking = 'static' | 'reserved' | 'collection'\n\n/** Delete-time referential policy for the backing dimension (default `'restrict'`, enforced in Task 5). */\nexport type OnDelete = 'restrict' | 'cascade' | 'nullify'\n\n/** The one descriptor shape every tier compiles to — tiers differ only by `backing`. */\nexport interface LookupDescriptor<Keys extends string = string> {\n readonly _viaBrand: 'lookup'\n /** Dimension/dictionary/target name. Empty for a bare `enumOf()` (no backing store, no name). */\n readonly dimension: string\n /** Canonical key field on the row (default `'id'`). */\n readonly key: string\n /** Candidate keys normalized to `key` on ingest (Task 3). */\n readonly altKeys?: readonly string[]\n readonly vocabulary: Vocabulary\n /** Dressing dimension: which backing-row field supplies `<field>Label`, optionally keyed `by` a sub-field (e.g. locale). */\n readonly present?: { readonly label: string; readonly by?: string }\n /** Field to sort by against the snapshot (Task 6). */\n readonly sortBy?: string\n readonly backing: LookupBacking\n readonly onDelete: OnDelete\n /** Static/enum inline key set. */\n readonly keys?: readonly Keys[]\n /** Static tier only: in-code `key -> { locale -> label }` table. */\n readonly table?: Readonly<Record<string, Readonly<Record<string, string>>>>\n /** Static hybrid hinge (the `staticDict` alias) — locale-less `<field>Label`. */\n readonly displayLocale?: string\n readonly onMissing?: OnMissingPolicy\n readonly substitute?: readonly string[]\n /** Inline display labels (value -> label), the dict-tier sync fallback (mirrors `dictKey`'s `labels`). */\n readonly labels?: Record<string, string>\n}\n\n/**\n * `sortBy`/`present.by` coupling warning (#650 Task 7, T6 minor — task-6-\n * report.md Concern #4). A `by`-keyed `present` (locale-map) field used as\n * `sortBy` needs a declared `displayLocale` for the locale-less\n * `compareForOrder` sort hook (a plain `orderBy(field)`, no `{by:'label'}`)\n * to resolve anything — without one it silently degrades to comparing the\n * raw canonical keys (never throws — `LookupSnapshot.compareKeys`'s\n * contract). `orderBy(field, dir, {by:'label'})` doesn't need this (it\n * carries its own per-call locale via `ViaBinding.resolveOrderLabel`,\n * #650 Task 7) — only a sortBy-driven PLAIN orderBy does. Warn once at\n * declare time rather than degrade silently at query time.\n */\nfunction warnIfSortByNeedsDisplayLocale(\n dimension: string,\n opts?: { sortBy?: string; present?: { by?: string }; displayLocale?: string },\n): void {\n if (opts?.sortBy !== undefined && opts.present?.by !== undefined && opts.displayLocale === undefined) {\n console.warn(\n `[noy-db] lookup(\"${dimension}\"): sortBy \"${opts.sortBy}\" is locale-keyed (present.by is set) but no ` +\n `displayLocale is declared — a locale-less orderBy() will silently sort by the raw stored key instead ` +\n `of the resolved label. Declare displayLocale, or sort via orderBy(field, dir, { by: 'label' }), which ` +\n `resolves at the query's own per-call locale.`,\n )\n }\n}\n\n/**\n * Matrix tier — first-class collection backing (default `backing:'collection'`).\n * `dimension` names the backing collection (e.g. `'countries'`).\n *\n * `backing` may be overridden to construct any tier through this one\n * factory — e.g. `lookup(name, { backing:'static', table, displayLocale })`\n * is the table-bearing static tier `staticDict()` compiles onto. The\n * `table`/`displayLocale`/`keys`/`onMissing`/`substitute`/`labels` options\n * are a superset of the brief's literal opts list: `LookupDescriptor`\n * already carries these fields for exactly this purpose, and without them\n * `backing:'static'` would be unreachable stand-alone through `lookup()`\n * (see task-2-report.md's \"Design decisions\").\n *\n * **`altKeys` caveat (matrix tier only)**: normalizing an altKey candidate\n * to its canonical `key` requires the backing `dimension` collection to be\n * open in EAGER mode (the default; `{ prefetch: false }` — lazy mode — is\n * unsupported). A `put()` on a field with `altKeys` whose backing collection\n * is lazy throws a `ValidationError` naming the field and dimension; open\n * the backing collection without `{ prefetch: false }`, or drop `altKeys`.\n */\nexport function lookup<Keys extends string>(\n dimension: string,\n opts?: {\n key?: string\n altKeys?: readonly string[]\n vocabulary?: Vocabulary\n present?: { label: string; by?: string }\n sortBy?: string\n backing?: LookupBacking\n onDelete?: OnDelete\n keys?: readonly Keys[]\n table?: Readonly<Record<string, Readonly<Record<string, string>>>>\n displayLocale?: string\n onMissing?: OnMissingPolicy\n substitute?: readonly string[]\n labels?: Record<string, string>\n },\n): LookupDescriptor<Keys> {\n linkLookupVia()\n warnIfSortByNeedsDisplayLocale(dimension, opts)\n return {\n _viaBrand: 'lookup',\n dimension,\n key: opts?.key ?? 'id',\n vocabulary: opts?.vocabulary ?? 'open',\n backing: opts?.backing ?? 'collection',\n onDelete: opts?.onDelete ?? 'restrict',\n ...(opts?.altKeys !== undefined ? { altKeys: opts.altKeys } : {}),\n ...(opts?.present !== undefined ? { present: opts.present } : {}),\n ...(opts?.sortBy !== undefined ? { sortBy: opts.sortBy } : {}),\n ...(opts?.keys !== undefined ? { keys: opts.keys } : {}),\n ...(opts?.table !== undefined ? { table: opts.table } : {}),\n ...(opts?.displayLocale !== undefined ? { displayLocale: opts.displayLocale } : {}),\n ...(opts?.onMissing !== undefined ? { onMissing: opts.onMissing } : {}),\n ...(opts?.substitute !== undefined ? { substitute: opts.substitute } : {}),\n ...(opts?.labels !== undefined ? { labels: opts.labels } : {}),\n }\n}\n\n/**\n * Enum tier — static in-config table, CLOSED vocabulary, no backing store.\n * No dimension name (pure inline keys) — `dimension` is `''`.\n */\nexport function enumOf<const Keys extends readonly string[]>(keys: Keys): LookupDescriptor<Keys[number]> {\n linkLookupVia()\n return {\n _viaBrand: 'lookup',\n dimension: '',\n key: 'id',\n vocabulary: 'closed',\n backing: 'static',\n onDelete: 'restrict',\n keys,\n }\n}\n\n/**\n * Dict tier — reserved `_dict_<dimension>` micro-collection backing (the\n * `vault.dictionary(dimension)` engine), open vocabulary by default. The\n * native equivalent of `dictKey()`.\n *\n * Unlike `lookup()`'s matrix tier, `dict()` has no `altKeys` option — its\n * `_dict_<dimension>` backing is the always-synchronous `LookupHandle`\n * write-through cache, never a `vault.collection()` that can be opened\n * `{ prefetch: false }`, so the matrix tier's lazy-mode altKeys restriction\n * does not apply here.\n */\nexport function dict<Keys extends string>(\n dimension: string,\n opts?: {\n keys?: readonly Keys[]\n vocabulary?: Vocabulary\n present?: { label: string; by?: string }\n onDelete?: OnDelete\n onMissing?: OnMissingPolicy\n substitute?: readonly string[]\n },\n): LookupDescriptor<Keys> {\n linkLookupVia()\n return {\n _viaBrand: 'lookup',\n dimension,\n key: 'id',\n vocabulary: opts?.vocabulary ?? 'open',\n backing: 'reserved',\n onDelete: opts?.onDelete ?? 'restrict',\n ...(opts?.keys !== undefined ? { keys: opts.keys } : {}),\n ...(opts?.present !== undefined ? { present: opts.present } : {}),\n ...(opts?.onMissing !== undefined ? { onMissing: opts.onMissing } : {}),\n ...(opts?.substitute !== undefined ? { substitute: opts.substitute } : {}),\n }\n}\n","/**\n * _sync_credentials reserved collection —\n *\n * Stores per-adapter OAuth tokens (and any other long-lived sync secrets) as\n * encrypted records inside the vault itself. Tokens are wrapped with the\n * compartment's own DEK, live on disk as ciphertext like any other record, and\n * are accessed only through the dedicated API in this module — never via\n * `vault.collection('_sync_credentials')`.\n *\n * Design decisions\n * ────────────────\n *\n * **Why a reserved collection, not a separate store?**\n * The compartment's existing encryption stack (AES-256-GCM + collection DEK)\n * is exactly the right primitive for protecting OAuth tokens at rest. Using a\n * separate store would require a new encryption surface, new adapter calls,\n * and a new backup/restore path — all of which already exist for collections.\n *\n * **Why not exposed as a regular collection?**\n * The same reason `_keyring` and `_ledger` aren't: they have invariants that\n * must be enforced (naming scheme, no cross-user leakage, no schema\n * validation, no history/ledger writes for privacy). Routing through a\n * dedicated API enforces those invariants.\n *\n * **Token lifecycle:**\n * - `putCredential(vault, adapterId, token)` — store or overwrite\n * - `getCredential(vault, adapterId)` — load and decrypt\n * - `deleteCredential(vault, adapterId)` — remove\n * - `listCredentials(vault)` — enumerate adapter IDs (not tokens)\n *\n * The `adapterId` is the record ID within the `_sync_credentials` collection.\n * It should be a stable, human-readable identifier for the adapter instance\n * (e.g. `'google-drive'`, `'dropbox'`, `'s3-prod'`).\n *\n * **ACL:** only `owner` and `admin` roles can read/write sync credentials.\n * Operators, viewers, and clients cannot call this API. The check is made\n * against the caller's keyring role at call time.\n */\n\nimport { buildRecordAad, buildRecordEnvelope } from '../kernel/enclave/index.js'\nimport type { NoydbStore, EncryptedEnvelope } from '../kernel/types.js'\nimport type { UnlockedKeyring } from '../with-party/team/keyring.js'\nimport { encrypt, openEnvelopeJson } from '../kernel/enclave/index.js'\nimport { ensureCollectionDEK } from '../with-party/team/keyring.js'\nimport { PermissionDeniedError } from '../kernel/errors.js'\n\n/**\n * The reserved collection name. Never collides with user collections.\n * Canonical definition lives in `reserved-secret-collections.ts` (shared with\n * the `vault.collection()` guard and grant DEK-propagation); re-exported here\n * for existing consumers.\n */\nexport { SYNC_CREDENTIALS_COLLECTION } from '../with-party/team/reserved-secret-collections.js'\nimport { SYNC_CREDENTIALS_COLLECTION } from '../with-party/team/reserved-secret-collections.js'\n\n// ─── Token types ──────────────────────────────────────────────────────\n\n/**\n * An OAuth/auth token stored in `_sync_credentials`.\n *\n * Fields mirror the OAuth2 token response shape. `customData` is an escape\n * hatch for adapter-specific secrets (API keys, connection strings, etc.)\n * that don't fit the OAuth2 shape.\n */\nexport interface SyncCredential {\n /** Stable identifier for the adapter instance (e.g. 'google-drive'). */\n readonly adapterId: string\n /** OAuth token type, usually 'Bearer'. */\n readonly tokenType: string\n /** The access token. Expires at `expiresAt` if set. */\n readonly accessToken: string\n /** Long-lived refresh token for renewing the access token. */\n readonly refreshToken?: string\n /** ISO timestamp when `accessToken` expires. Absent means \"no expiry\". */\n readonly expiresAt?: string\n /** Space-separated OAuth scopes. */\n readonly scopes?: string\n /** Adapter-specific opaque data (API keys, endpoints, etc.). */\n readonly customData?: Record<string, string>\n}\n\n// ─── Access check ─────────────────────────────────────────────────────\n\nfunction requireAdminAccess(keyring: UnlockedKeyring): void {\n // FR-6: custodian is INTENTIONALLY excluded. Sync credentials are the\n // firm's hosting/infrastructure secrets (OAuth tokens, connection strings) —\n // not the custodian's operational scope. A custodian operates the DATA but\n // must never mint or read transport credentials that could be used to\n // re-home or impersonate the firm's vault. Do NOT add 'custodian' here.\n if (keyring.role !== 'owner' && keyring.role !== 'admin') {\n throw new PermissionDeniedError(\n `Sync credentials require owner or admin role. Current role: \"${keyring.role}\"`,\n )\n }\n}\n\n// ─── Public API ────────────────────────────────────────────────────────\n\n/**\n * Store or overwrite a sync credential for the given adapter.\n *\n * The credential is encrypted with the `_sync_credentials` collection DEK\n * (auto-generated on first use). The record ID is the `adapterId`.\n *\n * Requires owner or admin role.\n */\nexport async function putCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n credential: SyncCredential,\n): Promise<void> {\n requireAdminAccess(keyring)\n\n const getDek = await ensureCollectionDEK(adapter, vault, keyring)\n const dek = await getDek(SYNC_CREDENTIALS_COLLECTION)\n\n // The version is resolved BEFORE the identity, because it is now part of it:\n // sealing under one version and stamping another produces a record no reader\n // can open (#1093).\n const existing = await adapter.get(vault, SYNC_CREDENTIALS_COLLECTION, credential.adapterId)\n const version = existing ? existing._v + 1 : 1\n\n const identity = { collection: SYNC_CREDENTIALS_COLLECTION, id: credential.adapterId, version }\n const { iv, data } = await encrypt(JSON.stringify(credential), dek, buildRecordAad(identity))\n\n const envelope: EncryptedEnvelope = buildRecordEnvelope(identity, { iv, data })\n\n await adapter.put(\n vault,\n SYNC_CREDENTIALS_COLLECTION,\n credential.adapterId,\n envelope,\n existing ? existing._v : undefined,\n )\n}\n\n/**\n * Load and decrypt a sync credential for the given adapter ID.\n *\n * Returns `null` if no credential exists for this adapter.\n * Requires owner or admin role.\n */\nexport async function getCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<SyncCredential | null> {\n requireAdminAccess(keyring)\n\n const getDek = await ensureCollectionDEK(adapter, vault, keyring)\n const dek = await getDek(SYNC_CREDENTIALS_COLLECTION)\n\n const envelope = await adapter.get(vault, SYNC_CREDENTIALS_COLLECTION, adapterId)\n if (!envelope) return null\n\n const plaintext = await openEnvelopeJson({ collection: SYNC_CREDENTIALS_COLLECTION, id: adapterId }, envelope, dek)\n return JSON.parse(plaintext) as SyncCredential\n}\n\n/**\n * Delete a sync credential by adapter ID.\n *\n * No-op if the credential doesn't exist. Requires owner or admin role.\n */\nexport async function deleteCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<void> {\n requireAdminAccess(keyring)\n await adapter.delete(vault, SYNC_CREDENTIALS_COLLECTION, adapterId)\n}\n\n/**\n * List all adapter IDs that have stored credentials.\n *\n * Returns only the IDs, never the credential payloads. Useful for\n * displaying \"connected adapters\" in UI without decrypting tokens.\n * Requires owner or admin role.\n */\nexport async function listCredentials(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n): Promise<string[]> {\n requireAdminAccess(keyring)\n return adapter.list(vault, SYNC_CREDENTIALS_COLLECTION)\n}\n\n/**\n * Check whether a credential exists and whether its access token has expired.\n *\n * Returns `{ exists: false }` if no credential is stored, or\n * `{ exists: true, expired: boolean }` based on the `expiresAt` field.\n * Requires owner or admin role.\n */\nexport async function credentialStatus(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<{ exists: false } | { exists: true; expired: boolean }> {\n const credential = await getCredential(adapter, vault, keyring, adapterId)\n if (!credential) return { exists: false }\n\n const expired = credential.expiresAt\n ? Date.now() > new Date(credential.expiresAt).getTime()\n : false\n\n return { exists: true, expired }\n}\n"],"mappings":"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","names":["value","canonical","known"]}
1
+ {"version":3,"sources":["../src/with-commit/numbering/descriptor.ts","../src/via/computed/binding.ts","../src/via/computed/descriptor.ts","../src/via/lookup/binding.ts","../src/via/lookup/descriptor.ts","../src/with-sync/credentials.ts"],"sourcesContent":["/**\n * @category capability\n * Deferred-numbering config descriptor. See\n * design-history/2026-06-08-sealed-numbering-and-store-clock-design.md.\n */\n\n/** A registered deferred-numbering series. */\nexport interface DeferredNumberingConfig {\n /** Series name — the key passed to `vault.sequence(series)`. */\n readonly series: string\n /** Collection holding the records to number. */\n readonly collection: string\n /** Field on each record where the assigned serial is written. */\n readonly field: string\n /**\n * Minimum wall-clock age (ms) before an entry is eligible at a pass, in\n * addition to the interval commit-wait. Default 0 — the store-clock\n * interval (`storeLatest ≤ now.earliest`) is the correctness mechanism.\n */\n readonly settleWindowMs: number\n}\n\n/**\n * Options for {@link withDeferredNumbering} (#844b — was an inline literal, so\n * unnameable). Same shape as {@link DeferredNumberingConfig} except\n * `settleWindowMs` is optional; the factory defaults it to 0.\n */\nexport interface WithDeferredNumberingOptions {\n /** Series name — the key passed to `vault.sequence(series)`. */\n readonly series: string\n /** Collection holding the records to number. */\n readonly collection: string\n /** Field on each record where the assigned serial is written. */\n readonly field: string\n /** See {@link DeferredNumberingConfig.settleWindowMs}. Default 0. */\n readonly settleWindowMs?: number\n}\n\n/** Declare a deferred-numbering series. Pass the result in `createNoydb({ numbering: [...] })`. */\nexport function withDeferredNumbering(opts: WithDeferredNumberingOptions): DeferredNumberingConfig {\n return {\n series: opts.series,\n collection: opts.collection,\n field: opts.field,\n settleWindowMs: opts.settleWindowMs ?? 0,\n }\n}\n","/**\n * The `computed` via-binding (#638 Task 7) — covers ONLY `mode: 'virtual'`\n * fields. Materialized entries never reach here: they compile into\n * `mergedComputed` (`kernel/collection-config.ts`) and run through the\n * EXISTING stage-5 `evalComputedFields` write path, byte-for-byte unchanged\n * (the behavior lock).\n *\n * A virtual field is computed on READ, inside `present()` — the money-\n * `Formatted`/i18n-`Label` precedent (seam map Part 4) generalized to a\n * user-declared function. It is NEVER stored (no `encodeAtRest`/\n * `decodeAtRest` — the field never appears in `_data` or any `_sealed`\n * slot), and its posture is fixed `queryable: 'none'` — there is no stored/\n * indexed form to query against, regardless of what its sources would\n * otherwise permit (`ViaGraph`'s grain-'virtual' clamp, `kernel/via/graph.ts`,\n * is the belt; this binding's own static posture is the suspenders for a\n * depsless virtual field, which registers no graph edge at all).\n *\n * Export/read redaction for a TAINTED virtual field (sourced from a\n * classified/sealed field) is NOT this binding's job — it stays ignorant of\n * taint, exactly like `evalComputedFields` stays ignorant of it for\n * materialized fields. `kernel/via/taint-binding.ts#taintBinding` (appended\n * to the pipeline by `via/graph-wiring.ts#applyTaintOverlay`, AFTER this\n * binding) overwrites a tainted virtual field's value with the same\n * `EXPORT_REDACTION_MARKER` on every present() — the one enforcement seam\n * every via feature's taint already routes through.\n */\nimport type { ViaBinding, ViaPosture } from '../../kernel/via/index.js'\nimport { installViaBinder } from '../../kernel/via/index.js'\nimport type { ComputedDescriptor } from './descriptor.js'\n\nexport interface ComputedViaConfig {\n /** field name -> its virtual-mode descriptor. Materialized entries are never present here. */\n readonly virtualFields: ReadonlyMap<string, ComputedDescriptor>\n}\n\nconst VIRTUAL_POSTURE: ViaPosture = { encryptedAtRest: 'envelope', queryable: 'none', exportable: true, forgettable: false }\n\nexport function computedBinding(cfg: ComputedViaConfig): ViaBinding {\n const fields = cfg.virtualFields\n return {\n brand: 'computed',\n posture: VIRTUAL_POSTURE,\n covers: (field) => fields.has(field),\n present: (record) => {\n let r = record\n for (const [field, desc] of fields) {\n if (r === record) r = { ...record }\n r[field] = desc.fn(r)\n }\n return r\n },\n }\n}\n\nexport function linkComputedVia(): void {\n installViaBinder('computed', (cfg) => computedBinding(cfg as ComputedViaConfig))\n}\n","/**\n * computed() — the declaration factory for a field whose value is DERIVED\n * from other fields on the same record (#638 Task 7, spec §6). Composes\n * with `via()` (the locked grammar since phase A: `via(computed(fn, { deps,\n * mode }), money('EUR'))`), grouped by `_viaBrand` like every other via\n * feature (`kernel/via/compose.ts#mergeViaFields`).\n *\n * `mode` picks where the function runs:\n * - `'materialized'` (default) — TODAY's stage-5 write-time eager compute\n * (`with-formula/computed/index.ts#evalComputedFields`), stored like any\n * other field. Byte-for-byte the existing `computed: { field: fn }` sugar.\n * - `'virtual'` — rides the `present` read phase (the money-`Formatted`/\n * i18n-`Label` precedent, seam map Part 4): computed fresh on every\n * read, NEVER stored, `queryable: 'none'`, excluded from export unless\n * its declared `deps` permit (identical taint rule to materialized —\n * see `via/computed/binding.ts`).\n *\n * `deps` names the OTHER fields `fn` reads — feeds `ViaGraph` (Task 1/2) so\n * a source's taint (e.g. a classified field) propagates to this derived\n * field. A depsless entry is fine UNLESS the collection also declares\n * classified fields (`kernel/collection-config.ts#resolveComputedEdges`\n * refuses it — closes the #636 opaque-function leak).\n */\nimport type { ViaDescriptor } from '../../kernel/via/index.js'\nimport { linkComputedVia } from './binding.js'\n\nexport interface ComputedDescriptor extends ViaDescriptor {\n readonly _viaBrand: 'computed'\n readonly fn: (record: Record<string, unknown>) => unknown\n readonly deps?: readonly string[]\n readonly mode: 'materialized' | 'virtual'\n}\n\nexport function computed(\n fn: (record: Record<string, unknown>) => unknown,\n opts?: { readonly deps?: readonly string[]; readonly mode?: 'materialized' | 'virtual' },\n): ComputedDescriptor {\n // Self-link, exactly as `money()` and `lookup()` do (#813). Until this call\n // existed, `computed` was the only via feature whose binder was installed by a\n // DIFFERENT module — `port/with/computed-strategy.ts` — rather than by its own\n // declaration factory. That works whenever the kernel spine and the consumer's\n // `computed` import resolve to one module instance, and fails when they do not:\n // under vitest's `server.deps.inline`, a consumer got the descriptor from one\n // transformed instance while the binder registry consulted at bind time lived in\n // another, producing `VIA_NOT_LINKED` for a descriptor `isComputedDescriptor()`\n // accepted. money/i18n/lookup were immune precisely because they self-link.\n //\n // Constructing a descriptor is the binding's opt-in unit, so linking here makes\n // the guarantee local: whatever instance produced the descriptor also has the\n // binder. `installViaBinder` is idempotent + first-wins, so the eager call in\n // `port/with/computed-strategy.ts` stays harmless.\n linkComputedVia()\n return {\n _viaBrand: 'computed',\n fn,\n ...(opts?.deps !== undefined ? { deps: opts.deps } : {}),\n mode: opts?.mode ?? 'materialized',\n }\n}\n\nexport function isComputedDescriptor(x: unknown): x is ComputedDescriptor {\n return typeof x === 'object' && x !== null && (x as { _viaBrand?: unknown })._viaBrand === 'computed'\n}\n","/**\n * The `'lookup'` `ViaBinding` — wires the lookup engine (present-time label\n * dressing across all three backing tiers) into the kernel's generic Via\n * port. Mirrors `via/i18n/binding.ts`'s #553 static-link pattern; the\n * present-time label-dressing algorithm below is adapted from\n * `via-i18n/binding.ts:253-337` (the same wildcard/array/scalar handling,\n * the same `onMissing`/`substitute` policy engine), generalized to branch on\n * `backing` instead of a static-vs-dynamic descriptor-shape check. For the\n * `'static'` and `'reserved'` tiers this delegates to `cfg.lookupLabelResolver`\n * — the SAME vault-built closure the i18n binding's `dictLabelResolver` uses\n * (static table first, else the `vault.dictionary()` handle) — so a native\n * `dict()`/`lookup(static)` field resolves through the identical label data\n * as its `dictKey()`/`staticDict()` alias (the byte-equivalence lock).\n *\n * `lookup()`/`enumOf()`/`dict()` each call {@link linkLookupVia} first — the\n * same #553 pattern `money()`/`dictKey()` use.\n *\n * `buildClause` (label-predicate queries) is still undeclared — out of scope\n * for #650. `compareForOrder` (#650 Task 6, spec §5; matrix tier added Task\n * 7) resolves a `sortBy`-declared field's ordering via `cfg.snapshotFor`'s\n * sync snapshot; the hook signature is UNCHANGED (`via/index.ts:128-129` — no\n * locale param), so it closes over each descriptor's own `displayLocale`\n * (the same locale-less-hinge default `runLookupPresent`'s\n * `hasStaticDisplay` branch already uses). `resolveOrderLabel` (#650 Task\n * 7) is the PER-CALL-locale sibling `orderBy(..., {by:'label'})` needs —\n * see its own doc comment below. `describeFragment` (#650 Task 7) is the\n * first-ever consumed `ViaBinding.describeFragment` implementation — see\n * `with-shape/introspection/describe.ts`'s `buildDescription`.\n */\nimport type { ViaBinding, ViaReadCtx } from '../../kernel/via/index.js'\nimport { installViaBinder } from '../../kernel/via/index.js'\nimport type { LookupDescriptor, LookupBacking, Vocabulary, OnDelete } from './descriptor.js'\nimport { resolvePolicy, type Layer } from '../i18n/policy.js'\nimport { LocaleNotSpecifiedError, UnknownLookupKeyError, ValidationError } from '../../kernel/errors.js'\nimport { getAtPath, setAtPathInPlace } from '../../kernel/paths.js'\nimport type { MaterializedBacking } from './registry.js'\nimport { buildLookupSnapshot } from './snapshot.js'\n\n/**\n * Config a collection's lookup declarations resolve to — the binding's\n * construction input. `lookupLabelResolver`/`getLookupBacking`/`membership`/\n * `snapshotFor` are vault-built closures (never a `Collection` handle,\n * keyring, or DEK/CEK — the zero-knowledge boundary).\n */\nexport interface LookupViaConfig {\n readonly lookupFields: Record<string, LookupDescriptor>\n readonly lookupLabelResolver?: (dimension: string, key: string, locale: string, fallback?: unknown) => Promise<string | undefined>\n /**\n * The matrix (collection) tier's present-time backing-row source, keyed by the full\n * descriptor (not a bare dimension name) so the closure can resolve by `descriptor.key`,\n * not the backing row's PUT-id (#651 Task 3 — the same descriptor-gaining dispatch Task 7\n * already applied to `snapshotFor`).\n */\n readonly getLookupBacking?: (descriptor: LookupDescriptor) => (key: string) => Promise<Record<string, unknown> | undefined>\n /** Closed-vocabulary write-time membership test (#650 Task 3) — `(field, key) => known?`. */\n readonly membership?: (field: string, key: string) => boolean | Promise<boolean>\n /** Sync per-descriptor altKey index — `ingest`'s normalization source (#650 Task 3). */\n readonly getAltIndex?: (desc: LookupDescriptor) => MaterializedBacking | undefined\n /**\n * Sync materialized `key -> row` rows for a lookup descriptor (#650 Task\n * 6, spec §5; matrix-tier routing added #650 Task 7) — `compareForOrder`\n * and `resolveOrderLabel` below, and `snapshot.ts`'s `presentForJoin`\n * builder, all read this same vault-built closure. Reserved AND\n * collection (matrix) tier route here (the vault wires\n * `dimension -> LookupHandle.snapshotEntries()` / `dimension ->\n * collection.querySourceForJoin().snapshot()` respectively, keyed by\n * `descriptor.key` for the matrix case — see `registry.ts`'s\n * `buildLookupSnapshotRows`); static tier is resolved locally from\n * `descriptor.table` without calling this.\n */\n readonly snapshotFor?: (descriptor: LookupDescriptor) => ReadonlyMap<string, Record<string, unknown>> | undefined\n readonly collectionName: string\n}\n\n/** Enum tier (`backing:'static'`, no `table`) has no label source at all — never dressed. */\nfunction hasLabelSource(desc: LookupDescriptor): boolean {\n return !(desc.backing === 'static' && desc.table === undefined)\n}\n\n/**\n * Resolve one key's label. `'static'`/`'reserved'` both go through\n * `cfg.lookupLabelResolver` (mirrors `dictLabelResolver`'s own static-table-\n * first-else-reserved-handle branching — the SAME closure is reused, see\n * `kernel/vault.ts`). `'collection'` (matrix tier) reads the declared\n * `present.label` off the backing row via `cfg.getLookupBacking`; when\n * `present.by` is set, that field's value is a `{ locale -> label }` map\n * indexed by the effective locale.\n */\nasync function fetchLookupLabel(\n desc: LookupDescriptor,\n key: string,\n effLocale: string,\n fieldFallback: string | readonly string[] | undefined,\n cfg: LookupViaConfig,\n): Promise<string | undefined> {\n if (desc.backing === 'collection') {\n const getRow = cfg.getLookupBacking?.(desc)\n const row = getRow ? await getRow(key) : undefined\n const labelField = desc.present?.label\n if (!row || labelField === undefined) return undefined\n const raw = row[labelField]\n if (desc.present?.by !== undefined) {\n return raw && typeof raw === 'object' ? (raw as Record<string, unknown>)[effLocale] as string | undefined : undefined\n }\n return typeof raw === 'string' ? raw : undefined\n }\n return cfg.lookupLabelResolver?.(desc.dimension, key, effLocale, fieldFallback)\n}\n\n/** `present` — resolve `<field>Label` for every declared lookup field. Adapted from `via-i18n/binding.ts:253-337`. */\nasync function runLookupPresent(\n record: Record<string, unknown>,\n ctx: ViaReadCtx,\n cfg: LookupViaConfig,\n): Promise<Record<string, unknown>> {\n const fields = Object.entries(cfg.lookupFields).filter(([, d]) => hasLabelSource(d))\n if (fields.length === 0) return record\n\n const locale = typeof ctx.locale === 'string' ? ctx.locale : undefined\n const fallback = ctx.fallback as string | readonly string[] | undefined\n const layer = ctx.layer as Layer\n\n // `{ locale: 'raw' }` wants the untouched record — mirrors\n // `via-i18n/binding.ts`'s `locale !== 'raw'` dict-label gate exactly (no\n // synthetic `<field>Label` derivative on a raw read).\n if (locale === 'raw') return record\n\n // Static-display hybrid hinge: a `backing:'static'` field with a declared\n // `displayLocale` resolves its `<field>Label` even under a locale-less\n // read (mirrors staticDict's `hasStaticDisplay` gate).\n const hasStaticDisplay = fields.some(([, d]) => d.backing === 'static' && d.displayLocale !== undefined)\n if (!locale && !hasStaticDisplay) return record\n\n let result = record\n const withLabels = { ...result }\n\n for (const [field, desc] of fields) {\n const policy = desc.onMissing ? resolvePolicy(desc.onMissing, layer) : 'null'\n const fieldFallback = policy === 'substitute' ? (fallback ?? desc.substitute) : fallback\n const effLocale = locale ?? (desc.backing === 'static' ? desc.displayLocale : undefined)\n\n const resolveKey = async (key: string): Promise<string | null> => {\n if (!effLocale) {\n if (policy === 'throw') {\n throw new LocaleNotSpecifiedError(field, `lookup \"${field}\": no locale active to resolve key \"${key}\".`)\n }\n return null\n }\n const label = await fetchLookupLabel(desc, key, effLocale, fieldFallback, cfg)\n if (label === undefined) {\n if (policy === 'throw') {\n throw new LocaleNotSpecifiedError(field, `lookup \"${field}\": no label for key \"${key}\" in locale \"${effLocale}\".`)\n }\n return null\n }\n return label\n }\n\n if (field.includes('[].')) {\n const parts = field.split('[].')\n const arrayKey = parts[0]!\n const leaf = parts[1]\n if (!leaf || leaf.includes('.')) continue\n const arr = (withLabels as Record<string, unknown>)[arrayKey]\n if (!Array.isArray(arr)) continue\n const labelKey = `${leaf}Label`\n ;(withLabels as Record<string, unknown>)[arrayKey] = await Promise.all(\n arr.map(async (el) => {\n if (!el || typeof el !== 'object' || Array.isArray(el)) return el\n const k = (el as Record<string, unknown>)[leaf]\n if (typeof k !== 'string') return el\n return { ...(el as Record<string, unknown>), [labelKey]: await resolveKey(k) }\n }),\n )\n continue\n }\n\n const val = result[field]\n if (Array.isArray(val)) {\n withLabels[`${field}Label`] = await Promise.all(\n val.map(async (k) => ({ key: k, label: typeof k === 'string' ? await resolveKey(k) : null })),\n )\n } else if (typeof val === 'string') {\n const label = await resolveKey(val)\n if (label !== null) withLabels[`${field}Label`] = label\n }\n }\n\n result = withLabels\n return result\n}\n\n/**\n * One field's `lookup` descriptor as it appears on a `ViaBinding.\n * describeFragment()` payload (#650 Task 7 — the first real consumer,\n * `with-shape/introspection/describe.ts`'s `buildDescription`, imports this\n * type directly; `describe.ts` is NOT under `kernel/**`, so it's free to\n * import concrete via/ types the way it already does for\n * `LookupDescriptor`/`MoneyDescriptor`/etc.). `dimension` is OMITTED (not\n * emitted as `''`) for a bare `enumOf()` descriptor — the #650 Task 2\n * `dimension:''` sentinel resolved: no dimension name means no `dimension`\n * key, not a meaningless empty string (T2 carry, #650 Task 7).\n */\nexport interface LookupDescribeFragmentEntry {\n readonly dimension?: string\n readonly backing: LookupBacking\n readonly vocabulary: Vocabulary\n readonly key: string\n readonly altKeys?: readonly string[]\n readonly present?: { readonly label: string; readonly by?: string }\n readonly sortBy?: string\n readonly onDelete: OnDelete\n /** Statically-known closed-vocabulary key set (declared `keys`, or a static table's own keys). Omitted when membership lives only in the backing collection/dictionary (open vocabulary, or closed with no declared `keys`). */\n readonly keys?: readonly string[]\n}\n\n/** The `'lookup'` binding's `describeFragment()` payload shape. */\nexport interface LookupDescribeFragment {\n readonly lookupFields: Record<string, LookupDescribeFragmentEntry>\n}\n\nfunction buildLookupDescribeFragment(cfg: LookupViaConfig): Record<string, unknown> {\n const lookupFields: Record<string, LookupDescribeFragmentEntry> = {}\n for (const [field, desc] of Object.entries(cfg.lookupFields)) {\n lookupFields[field] = {\n ...(desc.dimension !== '' ? { dimension: desc.dimension } : {}),\n backing: desc.backing,\n vocabulary: desc.vocabulary,\n key: desc.key,\n onDelete: desc.onDelete,\n ...(desc.altKeys !== undefined ? { altKeys: desc.altKeys } : {}),\n ...(desc.present !== undefined ? { present: desc.present } : {}),\n ...(desc.sortBy !== undefined ? { sortBy: desc.sortBy } : {}),\n // #657 — static tier: when no `keys` was explicitly declared but a\n // `table` was (the staticDict()-equivalent `lookup(dim, {backing:\n // 'static', table})` shape), the table's own key set IS the\n // statically-known closed-vocabulary set the DescribedField.lookup\n // docblock promises (\"declared `keys`, OR a static table's own\n // keys\"). Reserved/matrix tiers never carry `table`, so this branch\n // never fires for them — their `keys` emission is unchanged.\n ...(desc.keys !== undefined\n ? { keys: desc.keys }\n : desc.backing === 'static' && desc.table !== undefined ? { keys: Object.keys(desc.table) } : {}),\n }\n }\n return { lookupFields }\n}\n\n/**\n * `cfg.getAltIndex(desc)` (matrix tier) reads the backing collection's cache\n * via `querySourceForJoin()` (`buildLookupAltIndex`, registry.ts) — which\n * throws a `.join()`-branded message when that collection was opened\n * `{prefetch:false}` (lazy mode is unsupported for altKey normalization).\n * Normalization must never silently not happen (the banned silent-no-op\n * class, #650 Task 3 review, Important 2) — detect that specific failure\n * and surface a CLEAR, lookup-branded `ValidationError` at the point of\n * failure instead of letting the confusing join-branded one leak onto an\n * unrelated `put()`. Any OTHER error (e.g. `materializeBackingTable`'s own\n * altKey-collision `ValidationError`) propagates unchanged.\n */\nfunction getAltIndexOrThrow(field: string, desc: LookupDescriptor, cfg: LookupViaConfig): MaterializedBacking | undefined {\n try {\n return cfg.getAltIndex?.(desc)\n } catch (err) {\n if (err instanceof Error && err.message.includes('lazy-mode')) {\n throw new ValidationError(\n `lookup: altKeys on \"${field}\" require the backing collection \"${desc.dimension}\" to be ` +\n `prefetch-enabled (lazy mode unsupported); open it without {prefetch:false} or drop altKeys.`,\n )\n }\n throw err\n }\n}\n\n/**\n * `ingest` — altKey candidate values normalize to the canonical key (#650\n * Task 3, spec §3). Pure, sync, idempotent (a canonical key maps to\n * itself); no store read — consults the pre-materialized\n * `cfg.getAltIndex(desc)`. The money `canonicalizeIncomingMoney` precedent\n * (`via/index.ts:108`).\n *\n * A `[].`-wildcard multi-value path (`getAtPath` resolves >1 entries — an\n * array of nested objects, e.g. `'lines[].country'`, the same wildcard\n * convention `runLookupPresent` above already handles) normalizes EVERY\n * element's leaf value, not just a lone scalar (#652 fix — this used to bail\n * out entirely here while `runLookupEnforceWrite` below validated every\n * value unnormalized, so a legitimate altKey in an array position was\n * wrongly refused by closed-vocabulary enforcement). `setAtPathInPlace`\n * can't write into a `[].`-wildcard path, so the array is reconstructed\n * immutably instead, mirroring `runLookupPresent`'s own `field.includes\n * ('[].')` branch. A plain field whose OWN value is a bare top-level array\n * (not a `[].`-wildcard path) gets the SAME element-wise normalization\n * against `backing.altIndex` (#661 fix — `getAtPath` resolves it to one\n * opaque value, so the array itself is `values[0]`; a non-string element is\n * left untouched, mirroring the scalar branch's own `typeof !== 'string'`\n * skip — no parallel coercion invented for this shape).\n */\nfunction runLookupIngest(record: Record<string, unknown>, cfg: LookupViaConfig): Record<string, unknown> {\n const withAltKeys = Object.entries(cfg.lookupFields).filter(([, d]) => (d.altKeys?.length ?? 0) > 0)\n if (withAltKeys.length === 0) return record\n\n let result = record\n for (const [field, desc] of withAltKeys) {\n const backing = getAltIndexOrThrow(field, desc, cfg)\n if (!backing || backing.altIndex.size === 0) continue\n\n if (field.includes('[].')) {\n const [arrayKey, leaf] = field.split('[].')\n if (!leaf || leaf.includes('.')) continue\n const arr = record[arrayKey!]\n if (!Array.isArray(arr)) continue\n let changed = false\n const normalized = arr.map((item) => {\n if (!item || typeof item !== 'object' || Array.isArray(item)) return item\n const value = (item as Record<string, unknown>)[leaf]\n if (typeof value !== 'string') return item\n const canonical = backing.altIndex.get(value)\n if (canonical === undefined || canonical === value) return item\n changed = true\n return { ...(item as Record<string, unknown>), [leaf]: canonical }\n })\n if (!changed) continue\n if (result === record) result = { ...record }\n result[arrayKey!] = normalized\n continue\n }\n\n const values = getAtPath(record, field)\n if (values.length !== 1) continue\n const value = values[0]\n\n // `getAtPath`/`setAtPathInPlace` resolve `field` generically, dotted\n // paths included (e.g. 'meta.tags') — do not rewrite this branch to a\n // direct `record[field]` bracket access, which would silently stop\n // normalizing/enforcing a dotted-non-wildcard bare-array field (#661).\n if (Array.isArray(value)) {\n if (value.length === 0) continue\n let changed = false\n const normalized = value.map((el) => {\n if (typeof el !== 'string') return el\n const canonical = backing.altIndex.get(el)\n if (canonical === undefined || canonical === el) return el\n changed = true\n return canonical\n })\n if (!changed) continue\n if (result === record) result = { ...record }\n setAtPathInPlace(result, field, normalized)\n continue\n }\n\n if (typeof value !== 'string') continue\n const canonical = backing.altIndex.get(value)\n if (canonical === undefined || canonical === value) continue\n if (result === record) result = { ...record }\n setAtPathInPlace(result, field, canonical)\n }\n return result\n}\n\n/**\n * `enforceWrite` — closed-vocabulary write refusal (#650 Task 3, spec §3).\n * Runs only for `vocabulary:'closed'` fields; `'open'` (the dictKey/dict\n * default) skips the check entirely, so #649's fix is additive — existing\n * dictKey/staticDict collections are unaffected. `ctx` carries no\n * cross-collection door (`id`/`vault`/`prior`/`emit` only, unchanged) —\n * membership is a vault-built closure on `cfg`, per spec.\n */\nasync function runLookupEnforceWrite(record: Record<string, unknown>, cfg: LookupViaConfig): Promise<void> {\n for (const [field, desc] of Object.entries(cfg.lookupFields)) {\n if (desc.vocabulary !== 'closed') continue\n // Checks every value `getAtPath` returns — for a `[].`-wildcard\n // multi-value path, that's every element's leaf value.\n // `runLookupIngest` above now normalizes each of those elements first\n // (#652), so what lands here for a `[].`-wildcard field is already\n // canonical; this loop's job stays membership, not normalization.\n for (const value of getAtPath(record, field)) {\n // A plain field whose OWN value is a bare top-level array (#661) —\n // `runLookupIngest` above has already normalized its elements'\n // altKeys, so membership-check each element through the SAME\n // `cfg.membership` closure the scalar branch below uses, refusing on\n // the first unknown one. A non-string element is skipped, mirroring\n // the scalar branch's own skip.\n if (Array.isArray(value)) {\n for (const el of value) {\n if (typeof el !== 'string') continue\n const known = cfg.membership ? await cfg.membership(field, el) : true\n if (!known) throw new UnknownLookupKeyError(desc.dimension, field, el)\n }\n continue\n }\n if (typeof value !== 'string') continue\n const known = cfg.membership ? await cfg.membership(field, value) : true\n if (!known) throw new UnknownLookupKeyError(desc.dimension, field, value)\n }\n }\n}\n\n/**\n * `compareForOrder` — exact ordering for a `sortBy`-declared lookup field\n * against the sync snapshot (#650 Task 6, spec §5, conflict resolution 4;\n * matrix-tier coverage added #650 Task 7). Opt-in: undeclared `sortBy`\n * (every dictKey/staticDict alias and every lookup field declared before\n * Task 6) returns `undefined` — falls through to the generic stored-value\n * comparator, byte-identical to today. Static tier reads `descriptor.table`\n * directly; reserved AND matrix (collection) tier both read `cfg.snapshotFor`\n * (the SAME live cache `presentForJoin`'s lookup half reads — see\n * `snapshot.ts`'s file header). The hook has no locale parameter\n * (`via/index.ts:128-129`, unchanged) — closes over the descriptor's own\n * `displayLocale` (the same locale-less-hinge default `runLookupPresent`\n * already uses); a `sortBy` field whose value isn't locale-keyed\n * (`present.by` undefined) never needs one. A `by`-keyed `sortBy` field\n * with NO declared `displayLocale` degrades to comparing the raw canonical\n * keys (silent — `LookupSnapshot.compareKeys` never throws; declare-time\n * warning at `descriptor.ts`'s `lookup()` factory; use\n * `orderBy(field, dir, {by:'label'})` — `resolveOrderLabel` below — for a\n * PER-CALL locale instead).\n */\nfunction compareLookupOrder(field: string, a: unknown, b: unknown, cfg: LookupViaConfig): number | undefined {\n if (typeof a !== 'string' || typeof b !== 'string') return undefined\n const desc = cfg.lookupFields[field]\n if (!desc || desc.sortBy === undefined) return undefined\n const rows = desc.backing === 'static'\n ? (desc.table ? new Map(Object.entries(desc.table)) : undefined)\n : cfg.snapshotFor?.(desc)\n if (!rows) return undefined\n return buildLookupSnapshot(desc.dimension, rows, desc).compareKeys(a, b, desc.displayLocale ?? '')\n}\n\n/**\n * `resolveOrderLabel` — per-key, PER-CALL-locale label resolution for\n * `orderBy(field, dir, { by: 'label' })` (#650 Task 7, spec §6 / seam map\n * Part 10 surprise 6's option (b)) — the channel `compareForOrder` above\n * structurally cannot serve, since `ViaBinding.compareForOrder` carries no\n * locale parameter. Consumed by `kernel/query/builder.ts`'s\n * `buildOrderLabelMaps` as the fallback for lookup fields the legacy dict\n * registries don't bridge (matrix tier; reserved/static tier already\n * resolves via that bridge — `JoinContext.resolveDictSource` — tried\n * FIRST by the caller, see `registry.ts`'s `collectLookupDictCompat` doc\n * comment). Reuses the exact same `cfg.snapshotFor`/`buildLookupSnapshot`\n * machinery as `compareLookupOrder` above, just with the per-call `locale`\n * in place of the descriptor's own `displayLocale` — falling back to\n * `displayLocale` only when the call itself is locale-less, the same\n * hinge order `runLookupPresent` already uses.\n */\nfunction resolveLookupOrderLabel(field: string, key: string, locale: string | undefined, cfg: LookupViaConfig): string | undefined {\n const desc = cfg.lookupFields[field]\n if (!desc) return undefined\n const rows = desc.backing === 'static'\n ? (desc.table ? new Map(Object.entries(desc.table)) : undefined)\n : cfg.snapshotFor?.(desc)\n if (!rows) return undefined\n return buildLookupSnapshot(desc.dimension, rows, desc).label(key, locale ?? desc.displayLocale ?? '')\n}\n\nexport function lookupBinding(cfg: LookupViaConfig): ViaBinding {\n return {\n brand: 'lookup',\n posture: { encryptedAtRest: 'envelope', queryable: 'full', exportable: true, forgettable: false },\n reservedPrefixes: ['_dict_', '_lookup_'],\n covers: (field) => field in cfg.lookupFields,\n ingest: (record) => runLookupIngest(record, cfg),\n enforceWrite: (record) => runLookupEnforceWrite(record, cfg),\n present: async (record, ctx) => runLookupPresent(record, ctx, cfg),\n compareForOrder: (field, a, b) => compareLookupOrder(field, a, b, cfg),\n resolveOrderLabel: (field, key, locale) => resolveLookupOrderLabel(field, key, locale, cfg),\n describeFragment: () => buildLookupDescribeFragment(cfg),\n }\n}\n\nexport function linkLookupVia(): void {\n installViaBinder('lookup', (c) => lookupBinding(c as LookupViaConfig))\n}\n","/**\n * `lookup()` / `enumOf()` / `dict()` descriptors — the three declaration\n * surfaces for the `'lookup'` via binding (#650 Task 2, phase D of the Via\n * port). One `LookupDescriptor` shape; the tiers are `backing` details:\n *\n * - `lookup(dimension, opts?)` — matrix tier: first-class collection backing\n * (default `backing:'collection'`) — a reference-collection dimension\n * (e.g. `countries`), open vocabulary by default.\n * - `enumOf(keys)` — enum tier: static in-config table, CLOSED\n * vocabulary, no backing store at all (no dimension name — pure inline\n * keys). Exported as `enumOf`; the barrel (`index.ts`) re-exports it as\n * `enum` (`enum` is a reserved word, so it can't be the function's own\n * name).\n * - `dict(dimension, opts?)` — dict tier: reserved `_dict_<dimension>`\n * micro-collection backing (the `vault.dictionary(name)` engine), open\n * vocabulary by default.\n *\n * Each factory calls {@link linkLookupVia} first — the declaration IS the\n * via binding's opt-in unit (#553), same pattern as `money()`/`i18nText()`/\n * `dictKey()`.\n */\n\nimport type { OnMissingPolicy } from '../i18n/policy.js'\nimport { linkLookupVia } from './binding.js'\n\n/** `'closed'` = enum semantics (membership enforced, Task 3); `'open'` permits unknown keys. */\nexport type Vocabulary = 'open' | 'closed'\n\n/** Where a dimension's rows live: static in-config table / reserved micro-collection / first-class collection. */\nexport type LookupBacking = 'static' | 'reserved' | 'collection'\n\n/** Delete-time referential policy for the backing dimension (default `'restrict'`, enforced in Task 5). */\nexport type OnDelete = 'restrict' | 'cascade' | 'nullify'\n\n/** The one descriptor shape every tier compiles to — tiers differ only by `backing`. */\nexport interface LookupDescriptor<Keys extends string = string> {\n readonly _viaBrand: 'lookup'\n /** Dimension/dictionary/target name. Empty for a bare `enumOf()` (no backing store, no name). */\n readonly dimension: string\n /** Canonical key field on the row (default `'id'`). */\n readonly key: string\n /** Candidate keys normalized to `key` on ingest (Task 3). */\n readonly altKeys?: readonly string[]\n readonly vocabulary: Vocabulary\n /** Dressing dimension: which backing-row field supplies `<field>Label`, optionally keyed `by` a sub-field (e.g. locale). */\n readonly present?: { readonly label: string; readonly by?: string }\n /** Field to sort by against the snapshot (Task 6). */\n readonly sortBy?: string\n readonly backing: LookupBacking\n readonly onDelete: OnDelete\n /** Static/enum inline key set. */\n readonly keys?: readonly Keys[]\n /** Static tier only: in-code `key -> { locale -> label }` table. */\n readonly table?: Readonly<Record<string, Readonly<Record<string, string>>>>\n /** Static hybrid hinge (the `staticDict` alias) — locale-less `<field>Label`. */\n readonly displayLocale?: string\n readonly onMissing?: OnMissingPolicy\n readonly substitute?: readonly string[]\n /** Inline display labels (value -> label), the dict-tier sync fallback (mirrors `dictKey`'s `labels`). */\n readonly labels?: Record<string, string>\n}\n\n/**\n * `sortBy`/`present.by` coupling warning (#650 Task 7, T6 minor — task-6-\n * report.md Concern #4). A `by`-keyed `present` (locale-map) field used as\n * `sortBy` needs a declared `displayLocale` for the locale-less\n * `compareForOrder` sort hook (a plain `orderBy(field)`, no `{by:'label'}`)\n * to resolve anything — without one it silently degrades to comparing the\n * raw canonical keys (never throws — `LookupSnapshot.compareKeys`'s\n * contract). `orderBy(field, dir, {by:'label'})` doesn't need this (it\n * carries its own per-call locale via `ViaBinding.resolveOrderLabel`,\n * #650 Task 7) — only a sortBy-driven PLAIN orderBy does. Warn once at\n * declare time rather than degrade silently at query time.\n */\nfunction warnIfSortByNeedsDisplayLocale(\n dimension: string,\n opts?: { sortBy?: string; present?: { by?: string }; displayLocale?: string },\n): void {\n if (opts?.sortBy !== undefined && opts.present?.by !== undefined && opts.displayLocale === undefined) {\n console.warn(\n `[noy-db] lookup(\"${dimension}\"): sortBy \"${opts.sortBy}\" is locale-keyed (present.by is set) but no ` +\n `displayLocale is declared — a locale-less orderBy() will silently sort by the raw stored key instead ` +\n `of the resolved label. Declare displayLocale, or sort via orderBy(field, dir, { by: 'label' }), which ` +\n `resolves at the query's own per-call locale.`,\n )\n }\n}\n\n/**\n * Matrix tier — first-class collection backing (default `backing:'collection'`).\n * `dimension` names the backing collection (e.g. `'countries'`).\n *\n * `backing` may be overridden to construct any tier through this one\n * factory — e.g. `lookup(name, { backing:'static', table, displayLocale })`\n * is the table-bearing static tier `staticDict()` compiles onto. The\n * `table`/`displayLocale`/`keys`/`onMissing`/`substitute`/`labels` options\n * are a superset of the brief's literal opts list: `LookupDescriptor`\n * already carries these fields for exactly this purpose, and without them\n * `backing:'static'` would be unreachable stand-alone through `lookup()`\n * (see task-2-report.md's \"Design decisions\").\n *\n * **`altKeys` caveat (matrix tier only)**: normalizing an altKey candidate\n * to its canonical `key` requires the backing `dimension` collection to be\n * open in EAGER mode (the default; `{ prefetch: false }` — lazy mode — is\n * unsupported). A `put()` on a field with `altKeys` whose backing collection\n * is lazy throws a `ValidationError` naming the field and dimension; open\n * the backing collection without `{ prefetch: false }`, or drop `altKeys`.\n */\nexport function lookup<Keys extends string>(\n dimension: string,\n opts?: {\n key?: string\n altKeys?: readonly string[]\n vocabulary?: Vocabulary\n present?: { label: string; by?: string }\n sortBy?: string\n backing?: LookupBacking\n onDelete?: OnDelete\n keys?: readonly Keys[]\n table?: Readonly<Record<string, Readonly<Record<string, string>>>>\n displayLocale?: string\n onMissing?: OnMissingPolicy\n substitute?: readonly string[]\n labels?: Record<string, string>\n },\n): LookupDescriptor<Keys> {\n linkLookupVia()\n warnIfSortByNeedsDisplayLocale(dimension, opts)\n return {\n _viaBrand: 'lookup',\n dimension,\n key: opts?.key ?? 'id',\n vocabulary: opts?.vocabulary ?? 'open',\n backing: opts?.backing ?? 'collection',\n onDelete: opts?.onDelete ?? 'restrict',\n ...(opts?.altKeys !== undefined ? { altKeys: opts.altKeys } : {}),\n ...(opts?.present !== undefined ? { present: opts.present } : {}),\n ...(opts?.sortBy !== undefined ? { sortBy: opts.sortBy } : {}),\n ...(opts?.keys !== undefined ? { keys: opts.keys } : {}),\n ...(opts?.table !== undefined ? { table: opts.table } : {}),\n ...(opts?.displayLocale !== undefined ? { displayLocale: opts.displayLocale } : {}),\n ...(opts?.onMissing !== undefined ? { onMissing: opts.onMissing } : {}),\n ...(opts?.substitute !== undefined ? { substitute: opts.substitute } : {}),\n ...(opts?.labels !== undefined ? { labels: opts.labels } : {}),\n }\n}\n\n/**\n * Enum tier — static in-config table, CLOSED vocabulary, no backing store.\n * No dimension name (pure inline keys) — `dimension` is `''`.\n */\nexport function enumOf<const Keys extends readonly string[]>(keys: Keys): LookupDescriptor<Keys[number]> {\n linkLookupVia()\n return {\n _viaBrand: 'lookup',\n dimension: '',\n key: 'id',\n vocabulary: 'closed',\n backing: 'static',\n onDelete: 'restrict',\n keys,\n }\n}\n\n/**\n * Dict tier — reserved `_dict_<dimension>` micro-collection backing (the\n * `vault.dictionary(dimension)` engine), open vocabulary by default. The\n * native equivalent of `dictKey()`.\n *\n * Unlike `lookup()`'s matrix tier, `dict()` has no `altKeys` option — its\n * `_dict_<dimension>` backing is the always-synchronous `LookupHandle`\n * write-through cache, never a `vault.collection()` that can be opened\n * `{ prefetch: false }`, so the matrix tier's lazy-mode altKeys restriction\n * does not apply here.\n */\nexport function dict<Keys extends string>(\n dimension: string,\n opts?: {\n keys?: readonly Keys[]\n vocabulary?: Vocabulary\n present?: { label: string; by?: string }\n onDelete?: OnDelete\n onMissing?: OnMissingPolicy\n substitute?: readonly string[]\n },\n): LookupDescriptor<Keys> {\n linkLookupVia()\n return {\n _viaBrand: 'lookup',\n dimension,\n key: 'id',\n vocabulary: opts?.vocabulary ?? 'open',\n backing: 'reserved',\n onDelete: opts?.onDelete ?? 'restrict',\n ...(opts?.keys !== undefined ? { keys: opts.keys } : {}),\n ...(opts?.present !== undefined ? { present: opts.present } : {}),\n ...(opts?.onMissing !== undefined ? { onMissing: opts.onMissing } : {}),\n ...(opts?.substitute !== undefined ? { substitute: opts.substitute } : {}),\n }\n}\n","/**\n * _sync_credentials reserved collection —\n *\n * Stores per-adapter OAuth tokens (and any other long-lived sync secrets) as\n * encrypted records inside the vault itself. Tokens are wrapped with the\n * compartment's own DEK, live on disk as ciphertext like any other record, and\n * are accessed only through the dedicated API in this module — never via\n * `vault.collection('_sync_credentials')`.\n *\n * Design decisions\n * ────────────────\n *\n * **Why a reserved collection, not a separate store?**\n * The compartment's existing encryption stack (AES-256-GCM + collection DEK)\n * is exactly the right primitive for protecting OAuth tokens at rest. Using a\n * separate store would require a new encryption surface, new adapter calls,\n * and a new backup/restore path — all of which already exist for collections.\n *\n * **Why not exposed as a regular collection?**\n * The same reason `_keyring` and `_ledger` aren't: they have invariants that\n * must be enforced (naming scheme, no cross-user leakage, no schema\n * validation, no history/ledger writes for privacy). Routing through a\n * dedicated API enforces those invariants.\n *\n * **Token lifecycle:**\n * - `putCredential(vault, adapterId, token)` — store or overwrite\n * - `getCredential(vault, adapterId)` — load and decrypt\n * - `deleteCredential(vault, adapterId)` — remove\n * - `listCredentials(vault)` — enumerate adapter IDs (not tokens)\n *\n * The `adapterId` is the record ID within the `_sync_credentials` collection.\n * It should be a stable, human-readable identifier for the adapter instance\n * (e.g. `'google-drive'`, `'dropbox'`, `'s3-prod'`).\n *\n * **ACL:** only `owner` and `admin` roles can read/write sync credentials.\n * Operators, viewers, and clients cannot call this API. The check is made\n * against the caller's keyring role at call time.\n */\n\nimport { buildRecordAad, buildRecordEnvelope } from '../kernel/enclave/index.js'\nimport type { NoydbStore, EncryptedEnvelope } from '../kernel/types.js'\nimport type { UnlockedKeyring } from '../with-party/team/keyring.js'\nimport { encrypt, openEnvelopeJson } from '../kernel/enclave/index.js'\nimport { ensureCollectionDEK } from '../with-party/team/keyring.js'\nimport { PermissionDeniedError } from '../kernel/errors.js'\n\n/**\n * The reserved collection name. Never collides with user collections.\n * Canonical definition lives in `reserved-secret-collections.ts` (shared with\n * the `vault.collection()` guard and grant DEK-propagation); re-exported here\n * for existing consumers.\n */\nexport { SYNC_CREDENTIALS_COLLECTION } from '../with-party/team/reserved-secret-collections.js'\nimport { SYNC_CREDENTIALS_COLLECTION } from '../with-party/team/reserved-secret-collections.js'\n\n// ─── Token types ──────────────────────────────────────────────────────\n\n/**\n * An OAuth/auth token stored in `_sync_credentials`.\n *\n * Fields mirror the OAuth2 token response shape. `customData` is an escape\n * hatch for adapter-specific secrets (API keys, connection strings, etc.)\n * that don't fit the OAuth2 shape.\n */\nexport interface SyncCredential {\n /** Stable identifier for the adapter instance (e.g. 'google-drive'). */\n readonly adapterId: string\n /** OAuth token type, usually 'Bearer'. */\n readonly tokenType: string\n /** The access token. Expires at `expiresAt` if set. */\n readonly accessToken: string\n /** Long-lived refresh token for renewing the access token. */\n readonly refreshToken?: string\n /** ISO timestamp when `accessToken` expires. Absent means \"no expiry\". */\n readonly expiresAt?: string\n /** Space-separated OAuth scopes. */\n readonly scopes?: string\n /** Adapter-specific opaque data (API keys, endpoints, etc.). */\n readonly customData?: Record<string, string>\n}\n\n// ─── Access check ─────────────────────────────────────────────────────\n\nfunction requireAdminAccess(keyring: UnlockedKeyring): void {\n // FR-6: custodian is INTENTIONALLY excluded. Sync credentials are the\n // firm's hosting/infrastructure secrets (OAuth tokens, connection strings) —\n // not the custodian's operational scope. A custodian operates the DATA but\n // must never mint or read transport credentials that could be used to\n // re-home or impersonate the firm's vault. Do NOT add 'custodian' here.\n if (keyring.role !== 'owner' && keyring.role !== 'admin') {\n throw new PermissionDeniedError(\n `Sync credentials require owner or admin role. Current role: \"${keyring.role}\"`,\n )\n }\n}\n\n// ─── Public API ────────────────────────────────────────────────────────\n\n/**\n * Store or overwrite a sync credential for the given adapter.\n *\n * The credential is encrypted with the `_sync_credentials` collection DEK\n * (auto-generated on first use). The record ID is the `adapterId`.\n *\n * Requires owner or admin role.\n */\nexport async function putCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n credential: SyncCredential,\n): Promise<void> {\n requireAdminAccess(keyring)\n\n const getDek = await ensureCollectionDEK(adapter, vault, keyring)\n const dek = await getDek(SYNC_CREDENTIALS_COLLECTION)\n\n // The version is resolved BEFORE the identity, because it is now part of it:\n // sealing under one version and stamping another produces a record no reader\n // can open (#1093).\n const existing = await adapter.get(vault, SYNC_CREDENTIALS_COLLECTION, credential.adapterId)\n const version = existing ? existing._v + 1 : 1\n\n const identity = { collection: SYNC_CREDENTIALS_COLLECTION, id: credential.adapterId, version }\n const { iv, data } = await encrypt(JSON.stringify(credential), dek, buildRecordAad(identity))\n\n const envelope: EncryptedEnvelope = buildRecordEnvelope(identity, { iv, data })\n\n await adapter.put(\n vault,\n SYNC_CREDENTIALS_COLLECTION,\n credential.adapterId,\n envelope,\n existing ? existing._v : undefined,\n )\n}\n\n/**\n * Load and decrypt a sync credential for the given adapter ID.\n *\n * Returns `null` if no credential exists for this adapter.\n * Requires owner or admin role.\n */\nexport async function getCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<SyncCredential | null> {\n requireAdminAccess(keyring)\n\n const getDek = await ensureCollectionDEK(adapter, vault, keyring)\n const dek = await getDek(SYNC_CREDENTIALS_COLLECTION)\n\n const envelope = await adapter.get(vault, SYNC_CREDENTIALS_COLLECTION, adapterId)\n if (!envelope) return null\n\n const plaintext = await openEnvelopeJson({ collection: SYNC_CREDENTIALS_COLLECTION, id: adapterId }, envelope, dek)\n return JSON.parse(plaintext) as SyncCredential\n}\n\n/**\n * Delete a sync credential by adapter ID.\n *\n * No-op if the credential doesn't exist. Requires owner or admin role.\n */\nexport async function deleteCredential(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<void> {\n requireAdminAccess(keyring)\n await adapter.delete(vault, SYNC_CREDENTIALS_COLLECTION, adapterId)\n}\n\n/**\n * List all adapter IDs that have stored credentials.\n *\n * Returns only the IDs, never the credential payloads. Useful for\n * displaying \"connected adapters\" in UI without decrypting tokens.\n * Requires owner or admin role.\n */\nexport async function listCredentials(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n): Promise<string[]> {\n requireAdminAccess(keyring)\n return adapter.list(vault, SYNC_CREDENTIALS_COLLECTION)\n}\n\n/**\n * Check whether a credential exists and whether its access token has expired.\n *\n * Returns `{ exists: false }` if no credential is stored, or\n * `{ exists: true, expired: boolean }` based on the `expiresAt` field.\n * Requires owner or admin role.\n */\nexport async function credentialStatus(\n adapter: NoydbStore,\n vault: string,\n keyring: UnlockedKeyring,\n adapterId: string,\n): Promise<{ exists: false } | { exists: true; expired: boolean }> {\n const credential = await getCredential(adapter, vault, keyring, adapterId)\n if (!credential) return { exists: false }\n\n const expired = credential.expiresAt\n ? Date.now() > new Date(credential.expiresAt).getTime()\n : false\n\n return { exists: true, expired }\n}\n"],"mappings":"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","names":["value","canonical","known"]}
@@ -8,12 +8,12 @@ import {
8
8
  decodeIdxId,
9
9
  encodeIdxId,
10
10
  isIdxId
11
- } from "../chunk-REL4OLWW.js";
11
+ } from "../chunk-EKEIEJL6.js";
12
12
  import {
13
13
  CollectionIndexes
14
14
  } from "../chunk-GQ3BX5EA.js";
15
15
  import "../chunk-IGJCYUT5.js";
16
- import "../chunk-P7L3OQLL.js";
16
+ import "../chunk-CWM4GQD2.js";
17
17
  import "../chunk-PZ5AY32C.js";
18
18
 
19
19
  // src/with-lookup/indexing/active.ts
@@ -1,24 +1,24 @@
1
1
  import {
2
2
  dumpVaultSchema
3
- } from "../chunk-SG3OQCRG.js";
3
+ } from "../chunk-DXKLLKME.js";
4
4
  import {
5
5
  applyListProjection
6
6
  } from "../chunk-VG2ZSFYD.js";
7
7
  import {
8
8
  jsonSchemaToFields
9
9
  } from "../chunk-KHKKD62N.js";
10
- import "../chunk-N7YAQKAO.js";
10
+ import "../chunk-ONVD6GBY.js";
11
11
  import "../chunk-Y2NBBT6K.js";
12
- import "../chunk-YVTADFTC.js";
13
- import "../chunk-D5UDZANO.js";
14
- import "../chunk-ZVAFJQZB.js";
15
- import "../chunk-OQLYMBJI.js";
16
- import "../chunk-DJ23QZIN.js";
17
- import "../chunk-SFD2LL7G.js";
12
+ import "../chunk-6WO62AJW.js";
13
+ import "../chunk-OHOKBMTM.js";
14
+ import "../chunk-PENPHA45.js";
15
+ import "../chunk-GQXFOAYZ.js";
16
+ import "../chunk-KV6JM2IA.js";
17
+ import "../chunk-TMBSPLUG.js";
18
18
  import "../chunk-LP7BEXCT.js";
19
- import "../chunk-VJ4B3ZXY.js";
20
- import "../chunk-3Y4OFWQJ.js";
21
- import "../chunk-P7L3OQLL.js";
19
+ import "../chunk-AU3AUJIZ.js";
20
+ import "../chunk-IGNJOJEP.js";
21
+ import "../chunk-CWM4GQD2.js";
22
22
  import "../chunk-PZ5AY32C.js";
23
23
  export {
24
24
  applyListProjection,
@@ -0,0 +1,19 @@
1
+ import {
2
+ issueAttestationCore
3
+ } from "./chunk-7LNE2N2V.js";
4
+ import "./chunk-ESKIAXO2.js";
5
+ import "./chunk-4AOERLTV.js";
6
+ import "./chunk-OHOKBMTM.js";
7
+ import "./chunk-PENPHA45.js";
8
+ import "./chunk-GQXFOAYZ.js";
9
+ import "./chunk-KV6JM2IA.js";
10
+ import "./chunk-TMBSPLUG.js";
11
+ import "./chunk-LP7BEXCT.js";
12
+ import "./chunk-AU3AUJIZ.js";
13
+ import "./chunk-IGNJOJEP.js";
14
+ import "./chunk-CWM4GQD2.js";
15
+ import "./chunk-PZ5AY32C.js";
16
+ export {
17
+ issueAttestationCore
18
+ };
19
+ //# sourceMappingURL=issue-3JNJNPT3.js.map
@@ -16,3 +16,21 @@ export declare const USER_ENVELOPE_MAX_BYTES: number;
16
16
  * existing system-collection DEK propagation path in `team/keyring.ts`.
17
17
  */
18
18
  export declare const USER_ENVELOPE_COLLECTION = "_users";
19
+ /**
20
+ * #1096 — reserved DEK-map key holding the vault-wide ROSTER KEY.
21
+ *
22
+ * Not a collection: no records are ever stored under this name. It rides the
23
+ * `deks` map purely so the roster key reaches every member through the
24
+ * channels a DEK already travels — grant's `_`-prefix propagation,
25
+ * `persistKeyring`, the wrapped-DEKs recovery blob, `peer-recover`, pod
26
+ * recipient slots, session tokens — with no new plumbing and no satellite
27
+ * changes.
28
+ *
29
+ * The key authenticates each keyring file's plaintext AUTHORITY half via
30
+ * `roster_tag` (see `team/roster-tag.ts`). It is deliberately NOT
31
+ * secret-bearing: every role must be able to verify the roster it is
32
+ * subject to, so it propagates like `_ledger`, not like `_sync_credentials`.
33
+ *
34
+ * `rotateKeys` refuses to rotate it — see the guard there for why.
35
+ */
36
+ export declare const ROSTER_KEY_ID = "_roster";
@@ -63,6 +63,7 @@ export type { EchoSecretParts } from './crypto.js';
63
63
  export { resolveStableCek, rewrapBodyToDek, rewrapEnvelope, applyRewrappedBody, isRewrappedUnder } from './record-keys/lifecycle.js';
64
64
  export type { RewrappedBody } from './record-keys/lifecycle.js';
65
65
  export { rekeyEnvelopeToDek, rekeyEnvelopeIfNeeded } from './record-keys/rekey.js';
66
+ export { rekeyBlobSet } from './record-keys/rekey-blob.js';
66
67
  export { buildRecordEnvelope, buildSealedRecordEnvelope } from './record-envelope.js';
67
68
  export type { RecordEnvelopeBody } from './record-envelope.js';
68
69
  export { buildRecordAad, recordAadFor } from './record-aad.js';
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Re-key a vault's blob set from an old `_blob` DEK to a new one (#1122).
3
+ *
4
+ * ## The defect this closes
5
+ *
6
+ * `rotateKeys` re-keys `store.list(vault, <slot>)` plus the derived refs
7
+ * `derivedRefsFor` declares. That assumes a DEK slot named `X` protects data
8
+ * filed under collection `X`. For `_blob` it is false: the slot is `_blob`, but
9
+ * every byte it protects lives in `_blob_index` and `_blob_chunks`. So rotating
10
+ * `_blob` minted a fresh DEK, re-encrypted **nothing**, and made every blob in
11
+ * the vault permanently unreadable — reported to the user as `TamperedError`,
12
+ * i.e. as an attack, when it was self-inflicted. An ordinary `revoke` of a
13
+ * whole-vault grantee reaches it, because such a grantee's DEK map contains
14
+ * `_blob`.
15
+ *
16
+ * ## Why this cannot be done by `rekeyEnvelopeIfNeeded` alone
17
+ *
18
+ * Two of the three surfaces do not have the shape that helper assumes:
19
+ *
20
+ * - a **chunk** envelope is sealed over raw bytes under a bespoke AAD
21
+ * (`{eTag}:{index}:{count}`), not under the record AAD, so the generic
22
+ * helper cannot open it;
23
+ * - an **index** envelope's body is JSON that may itself carry a wrapped
24
+ * per-blob content CEK (`_cek` / `_cekPending`). Re-encrypting the body under
25
+ * the new DEK without re-wrapping that CEK leaves a blob whose metadata opens
26
+ * and whose bytes never will.
27
+ *
28
+ * ## Why it lives in the enclave
29
+ *
30
+ * It reads and writes protected body slots and it unwraps/re-wraps keys — the
31
+ * two things `enclave-body-only` reserves to `kernel/enclave/**`, and the same
32
+ * reason `rekeyEnvelopeToDek` moved here in #1074. `with-party` orchestrates
33
+ * the rotation and must not touch a body to do it.
34
+ *
35
+ * The blob layout below is duplicated from its owner rather than imported:
36
+ * `with-shape/blobs/blob-set.ts` already imports `with-party/team/tiers.js`, so
37
+ * a `with-party → with-shape` import would close a cycle, and the kernel may
38
+ * not depend on a service. `BlobObject` itself is a kernel type, so only the
39
+ * three collection/id string layouts are copied, with their owner named.
40
+ *
41
+ * @packageDocumentation
42
+ */
43
+ import { type EnclaveKey } from '../crypto.js';
44
+ import type { NoydbStore } from '../../types.js';
45
+ /**
46
+ * What a rotation did to the blob set.
47
+ *
48
+ * Deliberately NOT on the enclave barrel: no caller outside this module binds
49
+ * it, and that surface is the fork-swap trust boundary, so exporting a type
50
+ * nobody consumes is pure widening.
51
+ *
52
+ * It is a return value rather than a log line because `foreign` and `blobs`
53
+ * are the only way to tell "left alone, correctly" from "left alone, because
54
+ * the caller passed a wrong `otherDeks` set". Both re-key nothing and both
55
+ * return successfully. `rotate-preserves-blobs.test.ts` asserts these counts
56
+ * against a direct call for exactly that reason.
57
+ */
58
+ export interface BlobRekeyReport {
59
+ /** Index envelopes moved onto the new DEK. */
60
+ readonly blobs: number;
61
+ /** Chunk envelopes re-encrypted under the new DEK. */
62
+ readonly chunks: number;
63
+ /**
64
+ * Index envelopes left alone because they open under a DEK the caller holds
65
+ * for a DIFFERENT slot — an elevated blob, sealed under `_blob#<tier>`. Those
66
+ * are that slot's rotation to do, not this one's.
67
+ */
68
+ readonly foreign: number;
69
+ /** Index envelopes already under the new DEK — a resumed rotation. */
70
+ readonly alreadyMoved: number;
71
+ }
72
+ /**
73
+ * Move every blob sealed under `oldDek` onto `newDek`.
74
+ *
75
+ * Chunks are re-encrypted BEFORE their index entry is re-sealed, and that order
76
+ * is the resume property: an index entry still readable under `oldDek` means
77
+ * its chunks may be half-moved (each one is probed individually), and an index
78
+ * entry readable under `newDek` means its chunks are already done. An
79
+ * interrupted rotation is finished by re-running it with the same `newDek`.
80
+ *
81
+ * `otherDeks` is what keeps a tier-elevated blob from being mistaken for a
82
+ * damaged one. Membership in `_blob_index` is by DEK, not by collection name:
83
+ * a record elevated to tier N has its blob metadata sealed under `_blob#N`. An
84
+ * envelope that opens under NONE of the keys the caller holds is genuinely
85
+ * unreadable and throws, exactly as `rekeyEnvelopeIfNeeded` does — silently
86
+ * walking past it would turn a loud failure into permanent quiet loss.
87
+ */
88
+ export declare function rekeyBlobSet(adapter: NoydbStore, vault: string, oldDek: EnclaveKey, newDek: EnclaveKey, otherDeks?: readonly EnclaveKey[]): Promise<BlobRekeyReport>;
@@ -10,7 +10,9 @@
10
10
  * ├─ Crypto errors
11
11
  * │ ├─ DecryptionError — AES-GCM tag failure
12
12
  * │ ├─ TamperedError — ciphertext modified after write
13
- * │ └─ InvalidKeyError — wrong secret / corrupt keyring
13
+ * │ ├─ InvalidKeyError — wrong secret / corrupt keyring
14
+ * │ ├─ KeyringCorruptError — partial DEK corruption, KEK correct
15
+ * │ └─ KeyringTamperedError — keyring roster failed authentication (#1096)
14
16
  * ├─ Access errors
15
17
  * │ ├─ NoAccessError — no DEK for this collection
16
18
  * │ ├─ ReadOnlyError — ro permission, write attempted
@@ -193,6 +195,51 @@ export declare class KeyringCorruptError extends NoydbError {
193
195
  message?: string;
194
196
  });
195
197
  }
198
+ /**
199
+ * #1096 — the keyring's plaintext AUTHORITY half failed authentication.
200
+ *
201
+ * Thrown by `loadKeyring` when the canary is absent, when the reserved
202
+ * roster-key DEK entry is absent, or when the keys check out (KEK proven
203
+ * correct) but `roster_tag` is missing or does not match the file.
204
+ *
205
+ * Distinct from {@link KeyringCorruptError}: the KEYS are fine; the ROSTER —
206
+ * `role`, `permissions`, `granted_by`, the capability grants — is what cannot
207
+ * be trusted. A `_keyring` file is stored plaintext so admins can edit a
208
+ * member's authority without holding that member's credential, which left
209
+ * those fields authenticated by nothing; this error is what a hostile store
210
+ * editing them now produces.
211
+ *
212
+ * Deliberately raised only AFTER the key-unwrap epilogue, so a plain
213
+ * wrong-secret keeps reporting as `InvalidKeyError` and is never
214
+ * misannounced to the user as an attack.
215
+ */
216
+ /**
217
+ * Why a `_keyring` file failed roster authentication (#1096).
218
+ *
219
+ * Named rather than inlined because a third consumer now reports it without
220
+ * throwing: `RotateResult.unverified` (#1114) lists the members a rotation
221
+ * skipped, and a union duplicated per call site drifts.
222
+ */
223
+ export type KeyringTamperedReason = 'canary-missing' | 'roster-key-missing' | 'roster-tag-missing' | 'roster-tag-mismatch'
224
+ /**
225
+ * The file did not parse at all (#1121). Never thrown by
226
+ * `assertRosterAuthenticated` — which cannot reach a file it could not read —
227
+ * but reported by `verifyRoster` and accepted by `quarantineKeyring`, because
228
+ * truncation and bitrot produce the most literally unauthenticatable file
229
+ * there is and the tools for unauthenticatable files must not be the ones
230
+ * that choke on it.
231
+ */
232
+ | 'unparseable';
233
+ export declare class KeyringTamperedError extends NoydbError {
234
+ readonly details: {
235
+ readonly userId: string;
236
+ readonly reason: KeyringTamperedReason;
237
+ };
238
+ constructor(details: {
239
+ readonly userId: string;
240
+ readonly reason: KeyringTamperedReason;
241
+ });
242
+ }
196
243
  /**
197
244
  * Thrown when the authenticated user does not have a DEK for the requested
198
245
  * collection — i.e. the collection is not in their keyring at all.
@@ -1,4 +1,4 @@
1
- import type { RotateResult } from '../with-party/team/keyring.js';
1
+ import type { RotateResult, RosterVerifyResult, QuarantineResult } from '../with-party/team/keyring.js';
2
2
  import type { NoydbOptions, NoydbEventMap, GrantOptions, RevokeOptions, UpdateUserOptions, UserInfo, PushResult, PullResult, PushOptions, PullOptions, SyncStatus, SyncTargetInfo, SyncTargetStatus, NoydbStore, AccessibleVault, ListAccessibleVaultsOptions, QueryAcrossOptions, QueryAcrossResult, TranslatorAuditEntry, WriteConflict, UserApiFactory, ActiveTier, FactorProofBundle, GateName, VaultPolicy } from './types.js';
3
3
  import type { SecretPolicy } from './validation.js';
4
4
  import { type CoordinationProvider } from '../port/by/default-provider.js';
@@ -292,6 +292,23 @@ export declare class Noydb {
292
292
  * Opt-in (#267): throws {@link TeamNotEnabledError} without `withTeam()`.
293
293
  */
294
294
  rotate(vault: string, collections: string[]): Promise<RotateResult>;
295
+ /**
296
+ * Verify every `_keyring` file in the vault. Read-only (#1121).
297
+ *
298
+ * Names each failing file and why, so the bad one need not be found by trial.
299
+ * Reports `checked` too: "nothing unverified" is equally true of a sweep that
300
+ * examined nothing.
301
+ */
302
+ verifyRoster(vault: string): Promise<RosterVerifyResult>;
303
+ /**
304
+ * Remove a `_keyring` file that cannot be authenticated, and re-key behind it
305
+ * (#1121). Owner-only; refuses a file that verifies (use {@link revoke}).
306
+ *
307
+ * `revoke` consults the target's own `role`, so it cannot act on a file it
308
+ * will not trust. This ignores every claim the file makes — which is exactly
309
+ * why it is restricted to files that already fail authentication.
310
+ */
311
+ quarantineKeyring(vault: string, userId: string, factors?: FactorProofBundle): Promise<QuarantineResult>;
295
312
  /** List all users with access to a vault. */
296
313
  listUsers(vault: string): Promise<UserInfo[]>;
297
314
  /**