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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (812) hide show
  1. package/CHANGELOG.md +147 -14
  2. package/LICENSE +202 -21
  3. package/NOTICE +1 -0
  4. package/README.md +5 -5
  5. package/codemods/0.8.0-pre.json +61 -0
  6. package/dist/api-OS747DS4.js +18 -0
  7. package/dist/as/index.js +11 -12
  8. package/dist/as/index.js.map +1 -1
  9. package/dist/at/index.js +9 -10
  10. package/dist/attestation/index.js +37 -26
  11. package/dist/attestation/index.js.map +1 -1
  12. package/dist/{backup-XKCXVKV3.js → backup-YE2VQ26S.js} +15 -16
  13. package/dist/backup-YE2VQ26S.js.map +1 -0
  14. package/dist/blobs/index.js +13 -14
  15. package/dist/blobs/index.js.map +1 -1
  16. package/dist/broker/index.js +12 -13
  17. package/dist/broker/index.js.map +1 -1
  18. package/dist/by/index.js +2 -2
  19. package/dist/capsule/contract.d.ts +159 -0
  20. package/dist/capsule/contract.js +95 -0
  21. package/dist/capsule/enclave-aes/bound.d.ts +52 -0
  22. package/dist/capsule/enclave-aes/capabilities.d.ts +2 -0
  23. package/dist/capsule/enclave-aes/classify/compare.d.ts +20 -0
  24. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/find.d.ts +1 -1
  25. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/reveal.d.ts +1 -1
  26. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/verify.d.ts +2 -2
  27. package/dist/{kernel/enclave → capsule/enclave-aes}/classify/write.d.ts +1 -1
  28. package/dist/{kernel/enclave → capsule/enclave-aes}/crypto.d.ts +33 -129
  29. package/dist/capsule/enclave-aes/index.d.ts +113 -0
  30. package/dist/capsule/enclave-aes/index.js +335 -0
  31. package/dist/capsule/enclave-aes/index.js.map +1 -0
  32. package/dist/capsule/enclave-aes/primitives.d.ts +29 -0
  33. package/dist/capsule/index.d.ts +27 -0
  34. package/dist/capsule/plumbing/ct-equal.d.ts +11 -0
  35. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/deterministic.d.ts +8 -21
  36. package/dist/capsule/plumbing/digest.d.ts +122 -0
  37. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/envelope-body.d.ts +44 -48
  38. package/dist/capsule/plumbing/index.d.ts +100 -0
  39. package/dist/capsule/plumbing/lifecycle.d.ts +50 -0
  40. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/record-codec.d.ts +80 -20
  41. package/dist/{kernel/enclave → capsule/plumbing}/record-envelope.d.ts +1 -1
  42. package/dist/capsule/plumbing/rekey-blob.d.ts +33 -0
  43. package/dist/capsule/plumbing/rekey.d.ts +44 -0
  44. package/dist/capsule/plumbing/sealed-slot.d.ts +22 -0
  45. package/dist/capsule/plumbing/sealed-slots.d.ts +37 -0
  46. package/dist/capsule/plumbing/sealing.d.ts +29 -0
  47. package/dist/capsule/plumbing/sign.d.ts +9 -0
  48. package/dist/{kernel/enclave/record-keys → capsule/plumbing}/tombstone.d.ts +2 -2
  49. package/dist/capsule-VOLX3QRQ.js +209 -0
  50. package/dist/cargo/index.js +39 -36
  51. package/dist/{chunk-Q2MPFCQ7.js → chunk-23D6M6VM.js} +5 -5
  52. package/dist/chunk-23D6M6VM.js.map +1 -0
  53. package/dist/{chunk-R7BX2NB3.js → chunk-244QBK7Z.js} +4 -4
  54. package/dist/chunk-244QBK7Z.js.map +1 -0
  55. package/dist/chunk-2AFX33YA.js +936 -0
  56. package/dist/chunk-2AFX33YA.js.map +1 -0
  57. package/dist/{chunk-MVMXZYXT.js → chunk-2GHDLTPE.js} +2 -2
  58. package/dist/{chunk-GYGLAVCX.js → chunk-2MG3SW7Q.js} +6 -2
  59. package/dist/chunk-2MG3SW7Q.js.map +1 -0
  60. package/dist/{chunk-XLMEZIAJ.js → chunk-2ODVB6LZ.js} +2 -2
  61. package/dist/{chunk-HJUWN7JN.js → chunk-2PVXF5CS.js} +33 -8
  62. package/dist/chunk-2PVXF5CS.js.map +1 -0
  63. package/dist/{chunk-YJRT5UQM.js → chunk-2RH777HH.js} +21 -20
  64. package/dist/{chunk-YJRT5UQM.js.map → chunk-2RH777HH.js.map} +1 -1
  65. package/dist/{chunk-7O3Y4PPD.js → chunk-37EPTBBR.js} +153 -16
  66. package/dist/chunk-37EPTBBR.js.map +1 -0
  67. package/dist/{chunk-2ENE6445.js → chunk-3A5DUZ56.js} +102 -16
  68. package/dist/chunk-3A5DUZ56.js.map +1 -0
  69. package/dist/chunk-3EWS67C7.js +461 -0
  70. package/dist/chunk-3EWS67C7.js.map +1 -0
  71. package/dist/{chunk-SGZAIJCV.js → chunk-3L2NBKDR.js} +4 -8
  72. package/dist/chunk-3L2NBKDR.js.map +1 -0
  73. package/dist/{chunk-YMUFFJCO.js → chunk-3TDUWVWB.js} +5 -2
  74. package/dist/chunk-3TDUWVWB.js.map +1 -0
  75. package/dist/{chunk-4GVM6QZA.js → chunk-3XJQBBZD.js} +26 -28
  76. package/dist/chunk-3XJQBBZD.js.map +1 -0
  77. package/dist/{chunk-4ZS56W5S.js → chunk-42ALPZ2B.js} +672 -664
  78. package/dist/chunk-42ALPZ2B.js.map +1 -0
  79. package/dist/chunk-4CIQDWRB.js +524 -0
  80. package/dist/chunk-4CIQDWRB.js.map +1 -0
  81. package/dist/{chunk-RMS3NEA3.js → chunk-4JPMZTJY.js} +2 -2
  82. package/dist/{chunk-P3MG6QCN.js → chunk-4T3SE27M.js} +5 -9
  83. package/dist/chunk-4T3SE27M.js.map +1 -0
  84. package/dist/chunk-4V3Q35WG.js +204 -0
  85. package/dist/chunk-4V3Q35WG.js.map +1 -0
  86. package/dist/{chunk-NQKIUNW5.js → chunk-52MGURXO.js} +2 -2
  87. package/dist/{chunk-EQEYOJBM.js → chunk-5A4345TG.js} +4 -4
  88. package/dist/chunk-5A4345TG.js.map +1 -0
  89. package/dist/{chunk-334WSVOW.js → chunk-5FE75J4S.js} +6 -6
  90. package/dist/chunk-5FE75J4S.js.map +1 -0
  91. package/dist/chunk-5KC7QYEC.js +565 -0
  92. package/dist/chunk-5KC7QYEC.js.map +1 -0
  93. package/dist/{chunk-RSGLUIO6.js → chunk-5UMK7GRI.js} +13 -12
  94. package/dist/chunk-5UMK7GRI.js.map +1 -0
  95. package/dist/chunk-5WWDGFOS.js +275 -0
  96. package/dist/chunk-5WWDGFOS.js.map +1 -0
  97. package/dist/{chunk-QPQUVOZ2.js → chunk-6QVKPHBF.js} +3 -5
  98. package/dist/chunk-6QVKPHBF.js.map +1 -0
  99. package/dist/{chunk-ACAQ2DSG.js → chunk-747G6CT3.js} +2 -2
  100. package/dist/chunk-747G6CT3.js.map +1 -0
  101. package/dist/{chunk-QTQUH4DS.js → chunk-7R2Q7CPL.js} +16 -16
  102. package/dist/chunk-7R2Q7CPL.js.map +1 -0
  103. package/dist/{chunk-UGTBXF5U.js → chunk-7XMHMVTS.js} +3 -3
  104. package/dist/{chunk-YJX7EOJD.js → chunk-A4XMIZT3.js} +6 -6
  105. package/dist/{chunk-YJX7EOJD.js.map → chunk-A4XMIZT3.js.map} +1 -1
  106. package/dist/{chunk-A66I4JOA.js → chunk-AEQGFRU4.js} +4 -4
  107. package/dist/chunk-AEQGFRU4.js.map +1 -0
  108. package/dist/chunk-AL6ML4LA.js +1558 -0
  109. package/dist/chunk-AL6ML4LA.js.map +1 -0
  110. package/dist/{chunk-BZ3534YM.js → chunk-AUKKUPVB.js} +2 -2
  111. package/dist/{chunk-CUZLNGD5.js → chunk-B2YY6WDD.js} +1 -1
  112. package/dist/chunk-B2YY6WDD.js.map +1 -0
  113. package/dist/chunk-BKT776R5.js +1 -0
  114. package/dist/chunk-BNWA3WG5.js +22 -0
  115. package/dist/chunk-BNWA3WG5.js.map +1 -0
  116. package/dist/{chunk-VXANTWBJ.js → chunk-BPWYQ25R.js} +92 -6
  117. package/dist/{chunk-VXANTWBJ.js.map → chunk-BPWYQ25R.js.map} +1 -1
  118. package/dist/{chunk-6LKOJBV6.js → chunk-BVCUTXE3.js} +2 -2
  119. package/dist/chunk-CY2CYVWZ.js +41 -0
  120. package/dist/chunk-CY2CYVWZ.js.map +1 -0
  121. package/dist/{chunk-M4YPHYEH.js → chunk-DHHGL42E.js} +70 -25
  122. package/dist/chunk-DHHGL42E.js.map +1 -0
  123. package/dist/{chunk-VTFVLCUH.js → chunk-DIZLQVAA.js} +2 -2
  124. package/dist/{chunk-G73UVHCA.js → chunk-DLD5LGDS.js} +11 -12
  125. package/dist/chunk-DLD5LGDS.js.map +1 -0
  126. package/dist/{chunk-WQBG4SZS.js → chunk-DQESF6RP.js} +60 -49
  127. package/dist/chunk-DQESF6RP.js.map +1 -0
  128. package/dist/{chunk-LWBCUYZW.js → chunk-E2N2T5ZO.js} +2 -2
  129. package/dist/{chunk-3PGS7TYR.js → chunk-EGVJ7TM4.js} +29 -32
  130. package/dist/chunk-EGVJ7TM4.js.map +1 -0
  131. package/dist/{chunk-JVIBCBC7.js → chunk-EJLV62NI.js} +2 -2
  132. package/dist/{chunk-KKEESNPE.js → chunk-EYG2CY2X.js} +126 -19
  133. package/dist/chunk-EYG2CY2X.js.map +1 -0
  134. package/dist/{enclave-55PAM4YO.js → chunk-EZGV6F47.js} +83 -56
  135. package/dist/chunk-EZGV6F47.js.map +1 -0
  136. package/dist/{chunk-CHQCZZWC.js → chunk-EZOAKKTB.js} +36 -27
  137. package/dist/chunk-EZOAKKTB.js.map +1 -0
  138. package/dist/chunk-F6NCB7KT.js +13 -0
  139. package/dist/chunk-F6NCB7KT.js.map +1 -0
  140. package/dist/chunk-FFJFE4T7.js +100 -0
  141. package/dist/chunk-FFJFE4T7.js.map +1 -0
  142. package/dist/{chunk-JMGDHKQF.js → chunk-FLPCETP6.js} +2 -2
  143. package/dist/chunk-FOEDQATO.js +53 -0
  144. package/dist/chunk-FOEDQATO.js.map +1 -0
  145. package/dist/{chunk-DGOZ4UZD.js → chunk-FVH5QYY2.js} +2 -2
  146. package/dist/{chunk-HIWCKBEY.js → chunk-G2MVBDFP.js} +54 -28
  147. package/dist/chunk-G2MVBDFP.js.map +1 -0
  148. package/dist/chunk-GMKGGY7V.js +234 -0
  149. package/dist/chunk-GMKGGY7V.js.map +1 -0
  150. package/dist/{chunk-EIPVYUEP.js → chunk-GQ4MF3CM.js} +3 -3
  151. package/dist/chunk-GQ4MF3CM.js.map +1 -0
  152. package/dist/{chunk-PEOZ34EM.js → chunk-GSAK56VF.js} +1 -1
  153. package/dist/chunk-GSAK56VF.js.map +1 -0
  154. package/dist/{chunk-5VCRVKOZ.js → chunk-GUZET4SG.js} +12 -4
  155. package/dist/chunk-GUZET4SG.js.map +1 -0
  156. package/dist/{chunk-JBDFMYKQ.js → chunk-GZT3NQRA.js} +2 -2
  157. package/dist/{chunk-K23VKTN4.js → chunk-H23XHRTT.js} +19 -34
  158. package/dist/chunk-H23XHRTT.js.map +1 -0
  159. package/dist/{chunk-TFR7DSOV.js → chunk-H427H63K.js} +2 -2
  160. package/dist/{chunk-DFMWUOU5.js → chunk-H7JBY4P7.js} +23 -32
  161. package/dist/chunk-H7JBY4P7.js.map +1 -0
  162. package/dist/{chunk-SKGMN35F.js → chunk-HS554XAF.js} +3 -3
  163. package/dist/chunk-HS554XAF.js.map +1 -0
  164. package/dist/{chunk-HK67PX4J.js → chunk-HW7CMKUG.js} +1 -1
  165. package/dist/chunk-HW7CMKUG.js.map +1 -0
  166. package/dist/{chunk-3BVH633C.js → chunk-IMQKWR46.js} +5 -7
  167. package/dist/chunk-IMQKWR46.js.map +1 -0
  168. package/dist/{chunk-R42NSZI6.js → chunk-IUBMZPHM.js} +7 -7
  169. package/dist/chunk-IUBMZPHM.js.map +1 -0
  170. package/dist/{chunk-CUQPWYRX.js → chunk-IUT3GWP3.js} +75 -386
  171. package/dist/chunk-IUT3GWP3.js.map +1 -0
  172. package/dist/{chunk-RF6D3Y5E.js → chunk-JDEYF3PJ.js} +4 -8
  173. package/dist/{chunk-RF6D3Y5E.js.map → chunk-JDEYF3PJ.js.map} +1 -1
  174. package/dist/{chunk-PPTHY5QO.js → chunk-JIBUPZHA.js} +99 -8
  175. package/dist/chunk-JIBUPZHA.js.map +1 -0
  176. package/dist/{chunk-SMH6SRX7.js → chunk-JOSPH3E5.js} +2 -2
  177. package/dist/chunk-JQYKPKG6.js +193 -0
  178. package/dist/chunk-JQYKPKG6.js.map +1 -0
  179. package/dist/{chunk-7APJ6CO6.js → chunk-JSMTXZR3.js} +45 -13
  180. package/dist/chunk-JSMTXZR3.js.map +1 -0
  181. package/dist/chunk-JWWYRR4Q.js +12 -0
  182. package/dist/chunk-JWWYRR4Q.js.map +1 -0
  183. package/dist/{chunk-BVN6KXL2.js → chunk-KZXDLBY6.js} +14 -9
  184. package/dist/chunk-KZXDLBY6.js.map +1 -0
  185. package/dist/chunk-LGNDFRYC.js +23 -0
  186. package/dist/chunk-LGNDFRYC.js.map +1 -0
  187. package/dist/{chunk-XKWGNJ26.js → chunk-LIOVX5TJ.js} +7 -11
  188. package/dist/chunk-LIOVX5TJ.js.map +1 -0
  189. package/dist/{chunk-AYX7AINO.js → chunk-LNSVGRHO.js} +5 -7
  190. package/dist/chunk-LNSVGRHO.js.map +1 -0
  191. package/dist/chunk-M5B2W6YY.js +293 -0
  192. package/dist/chunk-M5B2W6YY.js.map +1 -0
  193. package/dist/chunk-MBMTND6D.js +456 -0
  194. package/dist/chunk-MBMTND6D.js.map +1 -0
  195. package/dist/{chunk-3BZZJJMQ.js → chunk-MILI22XQ.js} +3 -3
  196. package/dist/{chunk-A2HKRLPV.js → chunk-MXKI4ABF.js} +18 -59
  197. package/dist/chunk-MXKI4ABF.js.map +1 -0
  198. package/dist/{chunk-ZYIYHJ5R.js → chunk-MXOQ5JCG.js} +869 -238
  199. package/dist/chunk-MXOQ5JCG.js.map +1 -0
  200. package/dist/{chunk-V7ZRRCRJ.js → chunk-N7EVQD5S.js} +9 -13
  201. package/dist/chunk-N7EVQD5S.js.map +1 -0
  202. package/dist/chunk-N7UDP5VU.js +757 -0
  203. package/dist/chunk-N7UDP5VU.js.map +1 -0
  204. package/dist/chunk-NBQ3KK6X.js +270 -0
  205. package/dist/chunk-NBQ3KK6X.js.map +1 -0
  206. package/dist/{chunk-ZORTV6NH.js → chunk-NCO25JZO.js} +88 -4
  207. package/dist/chunk-NCO25JZO.js.map +1 -0
  208. package/dist/chunk-NH2UOV3B.js +18 -0
  209. package/dist/chunk-NH2UOV3B.js.map +1 -0
  210. package/dist/chunk-NYMLINRP.js +28 -0
  211. package/dist/chunk-NYMLINRP.js.map +1 -0
  212. package/dist/{chunk-YYP7WSEB.js → chunk-O2IMQSNA.js} +2 -2
  213. package/dist/chunk-O46CHQT7.js +215 -0
  214. package/dist/chunk-O46CHQT7.js.map +1 -0
  215. package/dist/chunk-OIEO4WDL.js +27 -0
  216. package/dist/chunk-OIEO4WDL.js.map +1 -0
  217. package/dist/{chunk-TRGTKX5E.js → chunk-OKSTSITU.js} +27 -4
  218. package/dist/chunk-OKSTSITU.js.map +1 -0
  219. package/dist/chunk-OYW3I5M4.js +31 -0
  220. package/dist/chunk-OYW3I5M4.js.map +1 -0
  221. package/dist/{chunk-Q7HZX3IF.js → chunk-PD64727P.js} +2 -2
  222. package/dist/chunk-PD64727P.js.map +1 -0
  223. package/dist/{chunk-FET5XCAT.js → chunk-PP3K67AJ.js} +34 -6
  224. package/dist/chunk-PP3K67AJ.js.map +1 -0
  225. package/dist/chunk-Q2XRFWOI.js +40 -0
  226. package/dist/chunk-Q2XRFWOI.js.map +1 -0
  227. package/dist/{chunk-QIZPZMUI.js → chunk-Q4UHJ5SN.js} +2 -2
  228. package/dist/{chunk-FAHMLERK.js → chunk-QBLT6POL.js} +2 -2
  229. package/dist/chunk-QMWY2FDS.js +157 -0
  230. package/dist/chunk-QMWY2FDS.js.map +1 -0
  231. package/dist/{chunk-LH5A3ZPG.js → chunk-RB4DOPGZ.js} +150 -22
  232. package/dist/chunk-RB4DOPGZ.js.map +1 -0
  233. package/dist/chunk-RHVMZSUU.js +7 -0
  234. package/dist/chunk-RHVMZSUU.js.map +1 -0
  235. package/dist/{chunk-BUE2D7PX.js → chunk-SMV2HRDU.js} +2 -2
  236. package/dist/chunk-SUZKW6UR.js +25 -0
  237. package/dist/chunk-SUZKW6UR.js.map +1 -0
  238. package/dist/{chunk-7MG7SJT7.js → chunk-SV2PM2OY.js} +1 -1
  239. package/dist/chunk-SV2PM2OY.js.map +1 -0
  240. package/dist/{chunk-UXVWEX5L.js → chunk-SYNY3VB5.js} +6 -2
  241. package/dist/chunk-SYNY3VB5.js.map +1 -0
  242. package/dist/{chunk-4GFX6MPA.js → chunk-TEHHROZL.js} +6 -8
  243. package/dist/chunk-TEHHROZL.js.map +1 -0
  244. package/dist/chunk-TKVUE4JF.js +1289 -0
  245. package/dist/chunk-TKVUE4JF.js.map +1 -0
  246. package/dist/{chunk-SFAWCTEM.js → chunk-TZZBXPZK.js} +2 -2
  247. package/dist/{chunk-F3W6YMDC.js → chunk-U2FZNEWJ.js} +4 -4
  248. package/dist/chunk-U2FZNEWJ.js.map +1 -0
  249. package/dist/{chunk-BLN54W3L.js → chunk-UA372JHH.js} +56 -3
  250. package/dist/chunk-UA372JHH.js.map +1 -0
  251. package/dist/{chunk-6YXCPL4T.js → chunk-UEFOQH74.js} +2 -2
  252. package/dist/chunk-UEFOQH74.js.map +1 -0
  253. package/dist/{chunk-2N5ML6HT.js → chunk-UIYESRD6.js} +9 -11
  254. package/dist/chunk-UIYESRD6.js.map +1 -0
  255. package/dist/{chunk-UHHPLO3I.js → chunk-V3RWOOSO.js} +4 -4
  256. package/dist/{chunk-KTFRCC5C.js → chunk-V7ADQF5O.js} +82 -8
  257. package/dist/chunk-V7ADQF5O.js.map +1 -0
  258. package/dist/{chunk-453F654R.js → chunk-VAORSCSU.js} +3 -3
  259. package/dist/chunk-VAORSCSU.js.map +1 -0
  260. package/dist/{chunk-GPNTB4Y7.js → chunk-VFHCKG5M.js} +6 -10
  261. package/dist/chunk-VFHCKG5M.js.map +1 -0
  262. package/dist/{chunk-2577DKXB.js → chunk-VHBKYN2J.js} +3 -3
  263. package/dist/chunk-VHBKYN2J.js.map +1 -0
  264. package/dist/chunk-VHM7HCKB.js +220 -0
  265. package/dist/chunk-VHM7HCKB.js.map +1 -0
  266. package/dist/{chunk-ZHKOPKE3.js → chunk-VKI2EB3N.js} +3 -3
  267. package/dist/chunk-VKI2EB3N.js.map +1 -0
  268. package/dist/{chunk-UDHWDSRL.js → chunk-VMV5SBE4.js} +16 -17
  269. package/dist/chunk-VMV5SBE4.js.map +1 -0
  270. package/dist/{chunk-SPYWXKJN.js → chunk-VQDZYB2B.js} +3 -3
  271. package/dist/chunk-VQDZYB2B.js.map +1 -0
  272. package/dist/{chunk-Q2PU57HC.js → chunk-VVOLDVIR.js} +2 -2
  273. package/dist/{chunk-3VF3WQ4F.js → chunk-WPOYWFSE.js} +7 -7
  274. package/dist/{chunk-3VF3WQ4F.js.map → chunk-WPOYWFSE.js.map} +1 -1
  275. package/dist/{chunk-OTQZ7ZWU.js → chunk-WXE4W2U6.js} +7 -11
  276. package/dist/chunk-WXE4W2U6.js.map +1 -0
  277. package/dist/chunk-XBKH4FOC.js +195 -0
  278. package/dist/chunk-XBKH4FOC.js.map +1 -0
  279. package/dist/{chunk-TUFM4UZP.js → chunk-XQRFSKYM.js} +2 -2
  280. package/dist/{chunk-3V4KWFX7.js → chunk-XSPFDUDG.js} +58 -53
  281. package/dist/chunk-XSPFDUDG.js.map +1 -0
  282. package/dist/{chunk-GY4WYNNG.js → chunk-Y2RKVFKR.js} +2 -2
  283. package/dist/{chunk-UP4BECPY.js → chunk-Y3LPTBG7.js} +89 -27
  284. package/dist/chunk-Y3LPTBG7.js.map +1 -0
  285. package/dist/{chunk-4OZNXTO4.js → chunk-YRXFFWG4.js} +2 -2
  286. package/dist/classified/index.js +5 -5
  287. package/dist/{classified-marker-BWOFVYAC.js → classified-marker-SIYKUU5L.js} +3 -3
  288. package/dist/classified-marker-SIYKUU5L.js.map +1 -0
  289. package/dist/collection-facade-CS7CA2XO.js +47 -0
  290. package/dist/{computed-VPOKQK4N.js → computed-FAD7SED4.js} +3 -3
  291. package/dist/consent/index.js +10 -11
  292. package/dist/consent/index.js.map +1 -1
  293. package/dist/cover/index.js +10 -11
  294. package/dist/coverage/index.js +306 -0
  295. package/dist/coverage/index.js.map +1 -0
  296. package/dist/custody/index.js +12 -13
  297. package/dist/{dead-filter-PKQDLOT4.js → dead-filter-4F3LWYKH.js} +11 -3
  298. package/dist/dead-filter-4F3LWYKH.js.map +1 -0
  299. package/dist/debug/index.js +11 -2
  300. package/dist/debug/index.js.map +1 -1
  301. package/dist/{delegation-DS5NRILW.js → delegation-NANZDG4N.js} +13 -16
  302. package/dist/delegation-NANZDG4N.js.map +1 -0
  303. package/dist/derivations/index.js +25 -23
  304. package/dist/derive-XLOJVXPU.js +20 -0
  305. package/dist/directory/index.js +10 -11
  306. package/dist/{dispatch-IEPAVQUM.js → dispatch-H4HV32AM.js} +5 -5
  307. package/dist/{dispatch-Q6MWLOJY.js → dispatch-JDZNGEB3.js} +20 -21
  308. package/dist/dispatch-JDZNGEB3.js.map +1 -0
  309. package/dist/executor-FMPVN6DT.js +35 -0
  310. package/dist/executor-LBBBRTN5.js +8 -0
  311. package/dist/executor-OSRZST2U.js +8 -0
  312. package/dist/export-accessible-RAOJAAU5.js +22 -0
  313. package/dist/extract-partition-JD5CUSTG.js +37 -0
  314. package/dist/{fanout-sidecar-S3QL53YX.js → fanout-sidecar-L7NHPIOC.js} +13 -15
  315. package/dist/fanout-sidecar-L7NHPIOC.js.map +1 -0
  316. package/dist/find-JRN4RNQ5.js +10 -0
  317. package/dist/forget/index.js +9 -10
  318. package/dist/forget/index.js.map +1 -1
  319. package/dist/guards/index.js +4 -4
  320. package/dist/history/index.js +13 -14
  321. package/dist/history/index.js.map +1 -1
  322. package/dist/i18n/index.js +14 -15
  323. package/dist/i18n/index.js.map +1 -1
  324. package/dist/index.d.ts +36 -14
  325. package/dist/index.js +970 -163
  326. package/dist/index.js.map +1 -1
  327. package/dist/indexing/index.js +13 -5
  328. package/dist/indexing/index.js.map +1 -1
  329. package/dist/introspection/index.js +11 -12
  330. package/dist/issue-YIVBZUTH.js +19 -0
  331. package/dist/ivf-flat-UZ6DQDLS.js +314 -0
  332. package/dist/ivf-flat-UZ6DQDLS.js.map +1 -0
  333. package/dist/{json-schema-V3M5MUCM.js → json-schema-3SHX72CC.js} +2 -1
  334. package/dist/json-schema-3SHX72CC.js.map +1 -0
  335. package/dist/kernel/attestation-types.d.ts +62 -0
  336. package/dist/kernel/collection-config.d.ts +5 -68
  337. package/dist/kernel/collection.d.ts +30 -9
  338. package/dist/kernel/cutover-transform.d.ts +1 -1
  339. package/dist/kernel/errors.d.ts +257 -38
  340. package/dist/kernel/generation-map.d.ts +48 -0
  341. package/dist/kernel/merge-authority.d.ts +1 -1
  342. package/dist/kernel/noydb.d.ts +6 -1
  343. package/dist/kernel/paths.d.ts +29 -5
  344. package/dist/kernel/prepared-write.d.ts +1 -1
  345. package/dist/kernel/present-cache.d.ts +58 -0
  346. package/dist/kernel/query/all.d.ts +7 -0
  347. package/dist/kernel/query/builder.d.ts +601 -270
  348. package/dist/kernel/query/civil-date.d.ts +64 -0
  349. package/dist/kernel/query/cursor.d.ts +40 -0
  350. package/dist/kernel/query/distinct-key.d.ts +98 -0
  351. package/dist/kernel/query/hydration.d.ts +76 -0
  352. package/dist/kernel/query/index.d.ts +31 -15
  353. package/dist/kernel/query/internal/core.d.ts +30 -0
  354. package/dist/kernel/query/internal/hooks.d.ts +22 -0
  355. package/dist/kernel/query/internal/maintenance.d.ts +43 -0
  356. package/dist/kernel/query/live/incremental.d.ts +314 -0
  357. package/dist/kernel/query/live/index.d.ts +22 -0
  358. package/dist/kernel/query/live/install.d.ts +2 -0
  359. package/dist/kernel/query/{live.d.ts → live/live.d.ts} +39 -9
  360. package/dist/kernel/query/live/methods.d.ts +107 -0
  361. package/dist/kernel/query/predicate.d.ts +119 -1
  362. package/dist/kernel/query/reduce/date-trunc.d.ts +75 -0
  363. package/dist/kernel/query/reduce/index.d.ts +36 -0
  364. package/dist/kernel/query/reduce/install.d.ts +2 -0
  365. package/dist/kernel/query/reduce/methods.d.ts +259 -0
  366. package/dist/kernel/query/reduce/scan-methods.d.ts +121 -0
  367. package/dist/kernel/query/relate/explain.d.ts +199 -0
  368. package/dist/kernel/query/relate/index.d.ts +48 -0
  369. package/dist/kernel/query/relate/install.d.ts +2 -0
  370. package/dist/kernel/query/relate/join-on.d.ts +126 -0
  371. package/dist/kernel/query/relate/join-reduce.d.ts +16 -0
  372. package/dist/kernel/query/relate/join.d.ts +452 -0
  373. package/dist/kernel/query/relate/methods.d.ts +426 -0
  374. package/dist/kernel/query/relate/partition.d.ts +87 -0
  375. package/dist/kernel/query/relate/scan-methods.d.ts +163 -0
  376. package/dist/kernel/query/relate/traverse.d.ts +109 -0
  377. package/dist/kernel/query/scan-builder.d.ts +79 -176
  378. package/dist/kernel/query/sort-key.d.ts +52 -0
  379. package/dist/kernel/secret-mode.d.ts +1 -1
  380. package/dist/kernel/tx-write-gate.d.ts +77 -0
  381. package/dist/kernel/types.d.ts +113 -8
  382. package/dist/kernel/validation.d.ts +27 -10
  383. package/dist/kernel/vault.d.ts +11 -6
  384. package/dist/kernel/via/compose.d.ts +3 -0
  385. package/dist/kernel/via/index.d.ts +56 -0
  386. package/dist/kernel/via/pipeline.d.ts +48 -0
  387. package/dist/kernel/write-queue.d.ts +17 -0
  388. package/dist/{ledger-EDXFFZQL.js → ledger-5ZMGE44K.js} +11 -12
  389. package/dist/liberate-WZSZM2W6.js +24 -0
  390. package/dist/link-set-MFXC24VK.js +29 -0
  391. package/dist/{managed-secret-NCWEMPUQ.js → managed-secret-MFTFT22X.js} +10 -11
  392. package/dist/materialized-views/index.js +30 -24
  393. package/dist/{migrate-cek-5ENH64AA.js → migrate-cek-DZRFFOHW.js} +2 -2
  394. package/dist/money/index.js +5 -5
  395. package/dist/noydb-ITIGNKUF.js +96 -0
  396. package/dist/overlay-views/index.js +4 -4
  397. package/dist/periods/index.js +12 -13
  398. package/dist/pod/index.js +74 -67
  399. package/dist/{pod-handle-ECVCDIXW.js → pod-handle-A3LET7NO.js} +10 -11
  400. package/dist/pod-handle-A3LET7NO.js.map +1 -0
  401. package/dist/policy/index.js +10 -11
  402. package/dist/port/on/index.d.ts +1 -1
  403. package/dist/port/to/index.d.ts +1 -1
  404. package/dist/port/with/blob-strategy.d.ts +10 -1
  405. package/dist/port/with/classified-strategy.d.ts +1 -1
  406. package/dist/port/with/collection-options.d.ts +6 -1
  407. package/dist/port/with/collection-registries.d.ts +1 -1
  408. package/dist/port/with/coverage-strategy.d.ts +83 -0
  409. package/dist/port/with/lookup-strategy.d.ts +2 -2
  410. package/dist/port/with/service-bus.d.ts +46 -0
  411. package/dist/port/with/strategies.d.ts +2 -0
  412. package/dist/portability/index.js +8 -8
  413. package/dist/{post-register-3IPCSG6Q.js → post-register-7ND27CF2.js} +6 -6
  414. package/dist/post-register-7ND27CF2.js.map +1 -0
  415. package/dist/query/all/index.js +31 -0
  416. package/dist/query/index.js +20 -32
  417. package/dist/query/live/index.js +21 -0
  418. package/dist/query/live/index.js.map +1 -0
  419. package/dist/query/reduce/index.js +33 -0
  420. package/dist/query/reduce/index.js.map +1 -0
  421. package/dist/query/relate/index.js +56 -0
  422. package/dist/query/relate/index.js.map +1 -0
  423. package/dist/reduce/index.js +72 -18
  424. package/dist/reduce/index.js.map +1 -1
  425. package/dist/register-DT5HZ2LZ.js +30 -0
  426. package/dist/registry-BHRZGZHF.js +8 -0
  427. package/dist/registry-ET6ZFFWA.js +34 -0
  428. package/dist/registry-HXFGLGTG.js +8 -0
  429. package/dist/registry-THCPDEOD.js +23 -0
  430. package/dist/request-withdrawal-AV2MJJKY.js +30 -0
  431. package/dist/{reveal-QZDHSGD4.js → reveal-B2TLP4JQ.js} +10 -9
  432. package/dist/reveal-B2TLP4JQ.js.map +1 -0
  433. package/dist/revoke-6DWIEOCV.js +24 -0
  434. package/dist/satellites/index.js +1 -1
  435. package/dist/schema-update/index.js +2 -2
  436. package/dist/sealed-record/index.js +16 -18
  437. package/dist/sealed-record/index.js.map +1 -1
  438. package/dist/search/index.js +11 -5
  439. package/dist/{seed-X5CC22CQ.js → seed-XNSE4U3S.js} +15 -18
  440. package/dist/seed-XNSE4U3S.js.map +1 -0
  441. package/dist/sequence/index.js +9 -10
  442. package/dist/session/index.js +9 -10
  443. package/dist/session/index.js.map +1 -1
  444. package/dist/shadow/index.js +2 -2
  445. package/dist/signer-MHGW3OIL.js +23 -0
  446. package/dist/snapshots/index.js +11 -11
  447. package/dist/stale-6GCFMOOR.js +28 -0
  448. package/dist/storage-BYN5LO6Z.js +21 -0
  449. package/dist/storage-BYN5LO6Z.js.map +1 -0
  450. package/dist/store/index.js +1 -1
  451. package/dist/{store-coordination-provider-4WZAIRHC.js → store-coordination-provider-C4BPT5XF.js} +11 -12
  452. package/dist/store-coordination-provider-C4BPT5XF.js.map +1 -0
  453. package/dist/sync/index.js +10 -11
  454. package/dist/sync/index.js.map +1 -1
  455. package/dist/team/index.js +17 -16
  456. package/dist/tiers/index.js +13 -12
  457. package/dist/to/index.js +2 -2
  458. package/dist/transactions/index.js +3 -3
  459. package/dist/util/index.js +1 -1
  460. package/dist/vault-head/index.js +11 -14
  461. package/dist/vault-head/index.js.map +1 -1
  462. package/dist/{verify-6MDTIUEV.js → verify-UKALW7C2.js} +14 -12
  463. package/dist/verify-UKALW7C2.js.map +1 -0
  464. package/dist/via/blob/index.d.ts +1 -0
  465. package/dist/via/classified/binding.d.ts +2 -2
  466. package/dist/via/classified/config-drift.d.ts +2 -2
  467. package/dist/via/classified/errors.d.ts +1 -1
  468. package/dist/via/geo/binding.d.ts +31 -0
  469. package/dist/via/geo/descriptor.d.ts +54 -0
  470. package/dist/via/geo/geohash.d.ts +95 -0
  471. package/dist/via/geo/index.d.ts +13 -0
  472. package/dist/via/geo/where.d.ts +50 -0
  473. package/dist/via/i18n/core.d.ts +1 -1
  474. package/dist/via/lookup/handle.d.ts +4 -4
  475. package/dist/via/lookup/registry.d.ts +1 -1
  476. package/dist/via/lookup/snapshot.d.ts +3 -3
  477. package/dist/walk-4VPK7AS4.js +20 -0
  478. package/dist/walk-4VPK7AS4.js.map +1 -0
  479. package/dist/with-audit/attestation/index.d.ts +2 -1
  480. package/dist/with-audit/attestation/issue.d.ts +2 -2
  481. package/dist/with-audit/attestation/revoke.d.ts +2 -2
  482. package/dist/with-audit/attestation/scheme.d.ts +7 -0
  483. package/dist/with-audit/attestation/signer.d.ts +1 -1
  484. package/dist/with-audit/attestation/strategy.d.ts +2 -2
  485. package/dist/with-audit/attestation/types.d.ts +10 -0
  486. package/dist/with-audit/attestation/vault-facade.d.ts +2 -2
  487. package/dist/with-audit/attestation/verifier.d.ts +6 -0
  488. package/dist/with-audit/consent/consent.d.ts +1 -1
  489. package/dist/with-audit/consent/strategy.d.ts +1 -1
  490. package/dist/with-audit/coverage/accounting.d.ts +112 -0
  491. package/dist/with-audit/coverage/active.d.ts +29 -0
  492. package/dist/with-audit/coverage/index.d.ts +61 -0
  493. package/dist/with-audit/coverage/sketch.d.ts +75 -0
  494. package/dist/with-audit/forget/subject-index.d.ts +1 -1
  495. package/dist/with-audit/periods/periods.d.ts +55 -7
  496. package/dist/with-audit/periods/reserved-collections.d.ts +3 -0
  497. package/dist/with-audit/periods/vault-facade.d.ts +18 -1
  498. package/dist/with-audit/sealed-record/index.d.ts +2 -2
  499. package/dist/with-audit/sealed-record/strategy.d.ts +1 -1
  500. package/dist/with-audit/tiers/index.d.ts +53 -2
  501. package/dist/with-audit/tiers/strategy.d.ts +8 -0
  502. package/dist/with-cargo/adopt-partition.d.ts +1 -1
  503. package/dist/with-cargo/extract-partition.d.ts +1 -1
  504. package/dist/with-cargo/floor.d.ts +3 -3
  505. package/dist/with-commit/history/history.d.ts +2 -2
  506. package/dist/with-commit/history/index.d.ts +1 -1
  507. package/dist/with-commit/history/ledger/store.d.ts +1 -1
  508. package/dist/with-commit/history/strategy.d.ts +1 -1
  509. package/dist/with-commit/history/time-machine.d.ts +1 -1
  510. package/dist/with-commit/numbering/index.d.ts +1 -1
  511. package/dist/with-commit/sequence/index.d.ts +1 -1
  512. package/dist/with-commit/tx/invariants.d.ts +1 -1
  513. package/dist/with-commit/tx/transaction.d.ts +13 -3
  514. package/dist/with-commit/vault-head/head.d.ts +1 -1
  515. package/dist/with-commit/vault-head/index.d.ts +1 -1
  516. package/dist/with-commit/vault-head/strategy.d.ts +1 -1
  517. package/dist/with-commit/vault-head/verify.d.ts +1 -1
  518. package/dist/with-formula/derivations/dispatch.d.ts +1 -1
  519. package/dist/with-formula/derivations/fanout-sidecar.d.ts +1 -1
  520. package/dist/with-formula/materialized-views/dependency-analyzer.d.ts +4 -3
  521. package/dist/with-formula/materialized-views/emit-memo.d.ts +40 -0
  522. package/dist/with-formula/materialized-views/executor.d.ts +8 -0
  523. package/dist/with-formula/materialized-views/index.d.ts +1 -1
  524. package/dist/with-formula/materialized-views/registry.d.ts +15 -0
  525. package/dist/with-formula/materialized-views/types.d.ts +106 -14
  526. package/dist/with-formula/materialized-views/window-stage.d.ts +11 -0
  527. package/dist/with-formula/overlay-views/types.d.ts +1 -1
  528. package/dist/with-formula/overlay-views/virtual-collection.d.ts +13 -4
  529. package/dist/with-lookup/embeddings/chunks.d.ts +79 -0
  530. package/dist/with-lookup/embeddings/descriptor.d.ts +21 -0
  531. package/dist/with-lookup/embeddings/index.d.ts +3 -0
  532. package/dist/with-lookup/embeddings/ivf-flat.d.ts +93 -0
  533. package/dist/with-lookup/embeddings/vector-index.d.ts +73 -0
  534. package/dist/with-lookup/embeddings/vector-set.d.ts +94 -1
  535. package/dist/with-lookup/embeddings/with-vector-index.d.ts +67 -0
  536. package/dist/with-lookup/indexing/collection-facade.d.ts +86 -3
  537. package/dist/with-lookup/indexing/compound-indexes.d.ts +129 -0
  538. package/dist/with-lookup/indexing/eager-indexes.d.ts +170 -1
  539. package/dist/with-lookup/indexing/index-snapshot.d.ts +62 -0
  540. package/dist/with-lookup/indexing/lazy-builder.d.ts +33 -0
  541. package/dist/with-lookup/indexing/persisted-field-indexes.d.ts +104 -0
  542. package/dist/with-lookup/indexing/persisted-indexes.d.ts +12 -0
  543. package/dist/with-lookup/indexing/sorted-indexes.d.ts +117 -0
  544. package/dist/with-lookup/indexing/unique-constraints.d.ts +106 -23
  545. package/dist/with-lookup/reduce/active.d.ts +2 -24
  546. package/dist/with-lookup/reduce/group-core.d.ts +43 -0
  547. package/dist/with-lookup/reduce/groupby.d.ts +195 -36
  548. package/dist/with-lookup/reduce/incremental-group.d.ts +153 -0
  549. package/dist/with-lookup/reduce/index.d.ts +26 -5
  550. package/dist/with-lookup/reduce/reducers.d.ts +255 -1
  551. package/dist/with-lookup/reduce/reduction.d.ts +44 -13
  552. package/dist/with-lookup/reduce/strategy.d.ts +43 -3
  553. package/dist/with-lookup/reduce/window.d.ts +262 -0
  554. package/dist/with-lookup/search/collection-facade.d.ts +45 -9
  555. package/dist/with-lookup/search/index-store.d.ts +5 -3
  556. package/dist/with-lookup/search/index.d.ts +4 -2
  557. package/dist/with-lookup/search/inverted-index.d.ts +38 -2
  558. package/dist/with-lookup/search/persisted-index-store.d.ts +5 -2
  559. package/dist/with-lookup/search/phrase.d.ts +28 -0
  560. package/dist/with-lookup/search/retrieve-types.d.ts +57 -1
  561. package/dist/with-lookup/search/serialize.d.ts +29 -3
  562. package/dist/with-lookup/search/strategy.d.ts +2 -6
  563. package/dist/with-party/broker/index.d.ts +3 -3
  564. package/dist/with-party/directory/cover/index.d.ts +1 -1
  565. package/dist/with-party/directory/cover/storage.d.ts +1 -1
  566. package/dist/with-party/directory/cover/types.d.ts +1 -1
  567. package/dist/with-party/directory/index.d.ts +1 -1
  568. package/dist/with-party/directory/types.d.ts +1 -1
  569. package/dist/with-party/directory/user-envelope/api.d.ts +1 -1
  570. package/dist/with-party/directory/user-envelope/storage.d.ts +1 -1
  571. package/dist/with-party/policy/engine.d.ts +1 -1
  572. package/dist/with-party/policy/index.d.ts +1 -1
  573. package/dist/with-party/policy/presets.d.ts +1 -1
  574. package/dist/with-party/team/authenticators.d.ts +91 -1
  575. package/dist/with-party/team/delegation.d.ts +1 -1
  576. package/dist/with-party/team/echo-secret.d.ts +2 -2
  577. package/dist/with-party/team/index.d.ts +4 -3
  578. package/dist/with-party/team/keyring.d.ts +2 -2
  579. package/dist/with-party/team/magic-link-grant.d.ts +1 -1
  580. package/dist/with-party/team/managed-secret.d.ts +1 -1
  581. package/dist/with-party/team/noydb-facade.d.ts +12 -3
  582. package/dist/with-party/team/recovery.d.ts +1 -1
  583. package/dist/with-party/team/roster-tag.d.ts +1 -1
  584. package/dist/with-party/team/rotate-recover.d.ts +1 -1
  585. package/dist/with-party/team/wrapped-deks.d.ts +1 -1
  586. package/dist/with-pod/format.d.ts +1 -1
  587. package/dist/with-pod/index.d.ts +1 -1
  588. package/dist/with-pod/pod.d.ts +1 -1
  589. package/dist/with-shape/blobs/blob-compaction.d.ts +17 -1
  590. package/dist/with-shape/blobs/blob-intent.d.ts +1 -1
  591. package/dist/with-shape/blobs/blob-set.d.ts +23 -4
  592. package/dist/with-shape/blobs/legacy-sweep.d.ts +35 -0
  593. package/dist/with-shape/introspection/describe.d.ts +7 -1
  594. package/dist/with-shape/introspection/field-meta.d.ts +18 -0
  595. package/dist/with-shape/introspection/types.d.ts +57 -1
  596. package/dist/with-shape/introspection/walk.d.ts +1 -1
  597. package/dist/with-shape/links/lazy-handle.d.ts +1 -1
  598. package/dist/with-shape/links/link-set.d.ts +1 -1
  599. package/dist/with-shape/links/vault-facade.d.ts +1 -1
  600. package/dist/with-shape/manifest/derive.d.ts +1 -1
  601. package/dist/with-shape/manifest/storage.d.ts +1 -1
  602. package/dist/with-shape/persisted-schemas/register.d.ts +1 -1
  603. package/dist/with-shape/persisted-schemas/storage.d.ts +1 -1
  604. package/dist/with-shape/satellites/dead-filter.d.ts +1 -1
  605. package/dist/with-shape/satellites/declare.d.ts +1 -1
  606. package/dist/with-shape/satellites/marker.d.ts +1 -1
  607. package/dist/with-shape/satellites/post-register.d.ts +1 -1
  608. package/dist/with-store/partitioned/index.d.ts +337 -0
  609. package/dist/with-sync/change-broadcast.d.ts +153 -0
  610. package/dist/with-sync/presence.d.ts +5 -10
  611. package/dist/withdraw-accessible-HL7GACUE.js +27 -0
  612. package/dist/withdraw-accessible-HL7GACUE.js.map +1 -0
  613. package/package.json +48 -12
  614. package/dist/api-WYW4YEQG.js +0 -19
  615. package/dist/backup-XKCXVKV3.js.map +0 -1
  616. package/dist/chunk-2577DKXB.js.map +0 -1
  617. package/dist/chunk-2ENE6445.js.map +0 -1
  618. package/dist/chunk-2N5ML6HT.js.map +0 -1
  619. package/dist/chunk-334WSVOW.js.map +0 -1
  620. package/dist/chunk-3BVH633C.js.map +0 -1
  621. package/dist/chunk-3PGS7TYR.js.map +0 -1
  622. package/dist/chunk-3V4KWFX7.js.map +0 -1
  623. package/dist/chunk-453F654R.js.map +0 -1
  624. package/dist/chunk-4GFX6MPA.js.map +0 -1
  625. package/dist/chunk-4GVM6QZA.js.map +0 -1
  626. package/dist/chunk-4ZS56W5S.js.map +0 -1
  627. package/dist/chunk-5VCRVKOZ.js.map +0 -1
  628. package/dist/chunk-6YXCPL4T.js.map +0 -1
  629. package/dist/chunk-7APJ6CO6.js.map +0 -1
  630. package/dist/chunk-7MG2LHO5.js +0 -1616
  631. package/dist/chunk-7MG2LHO5.js.map +0 -1
  632. package/dist/chunk-7MG7SJT7.js.map +0 -1
  633. package/dist/chunk-7O3Y4PPD.js.map +0 -1
  634. package/dist/chunk-A2HKRLPV.js.map +0 -1
  635. package/dist/chunk-A66I4JOA.js.map +0 -1
  636. package/dist/chunk-ACAQ2DSG.js.map +0 -1
  637. package/dist/chunk-ATL3ZVXT.js +0 -151
  638. package/dist/chunk-ATL3ZVXT.js.map +0 -1
  639. package/dist/chunk-AYX7AINO.js.map +0 -1
  640. package/dist/chunk-BLN54W3L.js.map +0 -1
  641. package/dist/chunk-BRLKZUBT.js +0 -95
  642. package/dist/chunk-BRLKZUBT.js.map +0 -1
  643. package/dist/chunk-BTDR2PXY.js +0 -29
  644. package/dist/chunk-BTDR2PXY.js.map +0 -1
  645. package/dist/chunk-BVN6KXL2.js.map +0 -1
  646. package/dist/chunk-CHQCZZWC.js.map +0 -1
  647. package/dist/chunk-CUQPWYRX.js.map +0 -1
  648. package/dist/chunk-CUZLNGD5.js.map +0 -1
  649. package/dist/chunk-DFMWUOU5.js.map +0 -1
  650. package/dist/chunk-EIPVYUEP.js.map +0 -1
  651. package/dist/chunk-EQEYOJBM.js.map +0 -1
  652. package/dist/chunk-F3W6YMDC.js.map +0 -1
  653. package/dist/chunk-FET5XCAT.js.map +0 -1
  654. package/dist/chunk-G73UVHCA.js.map +0 -1
  655. package/dist/chunk-GPNTB4Y7.js.map +0 -1
  656. package/dist/chunk-GYGLAVCX.js.map +0 -1
  657. package/dist/chunk-HIWCKBEY.js.map +0 -1
  658. package/dist/chunk-HJUWN7JN.js.map +0 -1
  659. package/dist/chunk-HK67PX4J.js.map +0 -1
  660. package/dist/chunk-HPVZCW43.js +0 -479
  661. package/dist/chunk-HPVZCW43.js.map +0 -1
  662. package/dist/chunk-I3ISCYT7.js +0 -159
  663. package/dist/chunk-I3ISCYT7.js.map +0 -1
  664. package/dist/chunk-K23VKTN4.js.map +0 -1
  665. package/dist/chunk-KKEESNPE.js.map +0 -1
  666. package/dist/chunk-KTFRCC5C.js.map +0 -1
  667. package/dist/chunk-LH5A3ZPG.js.map +0 -1
  668. package/dist/chunk-LP7BEXCT.js +0 -32
  669. package/dist/chunk-LP7BEXCT.js.map +0 -1
  670. package/dist/chunk-M4YPHYEH.js.map +0 -1
  671. package/dist/chunk-NSITFOCJ.js +0 -99
  672. package/dist/chunk-NSITFOCJ.js.map +0 -1
  673. package/dist/chunk-OTQZ7ZWU.js.map +0 -1
  674. package/dist/chunk-P3MG6QCN.js.map +0 -1
  675. package/dist/chunk-PEOZ34EM.js.map +0 -1
  676. package/dist/chunk-PPTHY5QO.js.map +0 -1
  677. package/dist/chunk-Q2MPFCQ7.js.map +0 -1
  678. package/dist/chunk-Q7HZX3IF.js.map +0 -1
  679. package/dist/chunk-QPQUVOZ2.js.map +0 -1
  680. package/dist/chunk-QTQUH4DS.js.map +0 -1
  681. package/dist/chunk-R42NSZI6.js.map +0 -1
  682. package/dist/chunk-R7BX2NB3.js.map +0 -1
  683. package/dist/chunk-RSGLUIO6.js.map +0 -1
  684. package/dist/chunk-RTBJ5IOO.js +0 -87
  685. package/dist/chunk-RTBJ5IOO.js.map +0 -1
  686. package/dist/chunk-SGZAIJCV.js.map +0 -1
  687. package/dist/chunk-SKGMN35F.js.map +0 -1
  688. package/dist/chunk-SPYWXKJN.js.map +0 -1
  689. package/dist/chunk-SZJ3DLZA.js +0 -66
  690. package/dist/chunk-SZJ3DLZA.js.map +0 -1
  691. package/dist/chunk-TRGTKX5E.js.map +0 -1
  692. package/dist/chunk-UDHWDSRL.js.map +0 -1
  693. package/dist/chunk-UP4BECPY.js.map +0 -1
  694. package/dist/chunk-UXVWEX5L.js.map +0 -1
  695. package/dist/chunk-V7ZRRCRJ.js.map +0 -1
  696. package/dist/chunk-WQBG4SZS.js.map +0 -1
  697. package/dist/chunk-XKWGNJ26.js.map +0 -1
  698. package/dist/chunk-YMUFFJCO.js.map +0 -1
  699. package/dist/chunk-ZHKOPKE3.js.map +0 -1
  700. package/dist/chunk-ZN4FD4PE.js +0 -364
  701. package/dist/chunk-ZN4FD4PE.js.map +0 -1
  702. package/dist/chunk-ZORTV6NH.js.map +0 -1
  703. package/dist/chunk-ZYIYHJ5R.js.map +0 -1
  704. package/dist/classified-marker-BWOFVYAC.js.map +0 -1
  705. package/dist/collection-facade-JSJYIKS4.js +0 -45
  706. package/dist/dead-filter-PKQDLOT4.js.map +0 -1
  707. package/dist/delegation-DS5NRILW.js.map +0 -1
  708. package/dist/derive-E642AOUB.js +0 -21
  709. package/dist/dispatch-Q6MWLOJY.js.map +0 -1
  710. package/dist/executor-5NIMLSKZ.js +0 -8
  711. package/dist/executor-7UQ3BZ4F.js +0 -29
  712. package/dist/executor-AQVXV5GZ.js +0 -8
  713. package/dist/export-accessible-ORMKLA3E.js +0 -22
  714. package/dist/extract-partition-NKYO3WNG.js +0 -37
  715. package/dist/fanout-sidecar-S3QL53YX.js.map +0 -1
  716. package/dist/find-NOGJ25VX.js +0 -10
  717. package/dist/issue-3DTQ7VJJ.js +0 -18
  718. package/dist/json-schema-V3M5MUCM.js.map +0 -1
  719. package/dist/kernel/enclave/classify/compare.d.ts +0 -10
  720. package/dist/kernel/enclave/index.d.ts +0 -87
  721. package/dist/kernel/enclave/record-keys/lifecycle.d.ts +0 -124
  722. package/dist/kernel/enclave/record-keys/rekey-blob.d.ts +0 -88
  723. package/dist/kernel/enclave/record-keys/rekey.d.ts +0 -91
  724. package/dist/kernel/enclave/record-keys/sealed-slot.d.ts +0 -14
  725. package/dist/kernel/enclave/record-keys/sealed-slots.d.ts +0 -123
  726. package/dist/kernel/enclave/record-keys/sealing.d.ts +0 -70
  727. package/dist/kernel/query/join.d.ts +0 -232
  728. package/dist/liberate-V35QUHJP.js +0 -24
  729. package/dist/link-set-2TTM3IZC.js +0 -30
  730. package/dist/noydb-62MX4QUG.js +0 -90
  731. package/dist/pod-handle-ECVCDIXW.js.map +0 -1
  732. package/dist/post-register-3IPCSG6Q.js.map +0 -1
  733. package/dist/register-HHLXEQDN.js +0 -31
  734. package/dist/registry-23WF3TGH.js +0 -8
  735. package/dist/registry-4AYVUNXK.js +0 -8
  736. package/dist/registry-JS3YFQQM.js +0 -18
  737. package/dist/registry-XX2Z7B46.js +0 -32
  738. package/dist/request-withdrawal-CHPATE4G.js +0 -30
  739. package/dist/reveal-QZDHSGD4.js.map +0 -1
  740. package/dist/revoke-7M2VW4ON.js +0 -23
  741. package/dist/seed-X5CC22CQ.js.map +0 -1
  742. package/dist/signer-SJQJVSM6.js +0 -24
  743. package/dist/stale-BZS6H2RY.js +0 -29
  744. package/dist/storage-RW4QBS23.js +0 -22
  745. package/dist/store-coordination-provider-4WZAIRHC.js.map +0 -1
  746. package/dist/verify-6MDTIUEV.js.map +0 -1
  747. package/dist/walk-ZURCH6RZ.js +0 -21
  748. package/dist/withdraw-accessible-PXR4IXDX.js +0 -27
  749. /package/dist/{api-WYW4YEQG.js.map → api-OS747DS4.js.map} +0 -0
  750. /package/dist/{computed-VPOKQK4N.js.map → capsule/contract.js.map} +0 -0
  751. /package/dist/{kernel/enclave → capsule/enclave-aes}/broker/proof.d.ts +0 -0
  752. /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/bidx.d.ts +0 -0
  753. /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/digest.d.ts +0 -0
  754. /package/dist/{kernel/enclave → capsule/enclave-aes}/classify/vdig.d.ts +0 -0
  755. /package/dist/{kernel/enclave/classify → capsule/plumbing}/kofn.d.ts +0 -0
  756. /package/dist/{kernel/enclave/classify → capsule/plumbing}/normalize.d.ts +0 -0
  757. /package/dist/{kernel/enclave → capsule/plumbing}/record-aad.d.ts +0 -0
  758. /package/dist/{collection-facade-JSJYIKS4.js.map → capsule-VOLX3QRQ.js.map} +0 -0
  759. /package/dist/{chunk-MVMXZYXT.js.map → chunk-2GHDLTPE.js.map} +0 -0
  760. /package/dist/{chunk-XLMEZIAJ.js.map → chunk-2ODVB6LZ.js.map} +0 -0
  761. /package/dist/{chunk-RMS3NEA3.js.map → chunk-4JPMZTJY.js.map} +0 -0
  762. /package/dist/{chunk-NQKIUNW5.js.map → chunk-52MGURXO.js.map} +0 -0
  763. /package/dist/{chunk-UGTBXF5U.js.map → chunk-7XMHMVTS.js.map} +0 -0
  764. /package/dist/{chunk-BZ3534YM.js.map → chunk-AUKKUPVB.js.map} +0 -0
  765. /package/dist/{derive-E642AOUB.js.map → chunk-BKT776R5.js.map} +0 -0
  766. /package/dist/{chunk-6LKOJBV6.js.map → chunk-BVCUTXE3.js.map} +0 -0
  767. /package/dist/{chunk-VTFVLCUH.js.map → chunk-DIZLQVAA.js.map} +0 -0
  768. /package/dist/{chunk-LWBCUYZW.js.map → chunk-E2N2T5ZO.js.map} +0 -0
  769. /package/dist/{chunk-JVIBCBC7.js.map → chunk-EJLV62NI.js.map} +0 -0
  770. /package/dist/{chunk-JMGDHKQF.js.map → chunk-FLPCETP6.js.map} +0 -0
  771. /package/dist/{chunk-DGOZ4UZD.js.map → chunk-FVH5QYY2.js.map} +0 -0
  772. /package/dist/{chunk-JBDFMYKQ.js.map → chunk-GZT3NQRA.js.map} +0 -0
  773. /package/dist/{chunk-TFR7DSOV.js.map → chunk-H427H63K.js.map} +0 -0
  774. /package/dist/{chunk-SMH6SRX7.js.map → chunk-JOSPH3E5.js.map} +0 -0
  775. /package/dist/{chunk-3BZZJJMQ.js.map → chunk-MILI22XQ.js.map} +0 -0
  776. /package/dist/{chunk-YYP7WSEB.js.map → chunk-O2IMQSNA.js.map} +0 -0
  777. /package/dist/{chunk-QIZPZMUI.js.map → chunk-Q4UHJ5SN.js.map} +0 -0
  778. /package/dist/{chunk-FAHMLERK.js.map → chunk-QBLT6POL.js.map} +0 -0
  779. /package/dist/{chunk-BUE2D7PX.js.map → chunk-SMV2HRDU.js.map} +0 -0
  780. /package/dist/{chunk-SFAWCTEM.js.map → chunk-TZZBXPZK.js.map} +0 -0
  781. /package/dist/{chunk-UHHPLO3I.js.map → chunk-V3RWOOSO.js.map} +0 -0
  782. /package/dist/{chunk-Q2PU57HC.js.map → chunk-VVOLDVIR.js.map} +0 -0
  783. /package/dist/{chunk-TUFM4UZP.js.map → chunk-XQRFSKYM.js.map} +0 -0
  784. /package/dist/{chunk-GY4WYNNG.js.map → chunk-Y2RKVFKR.js.map} +0 -0
  785. /package/dist/{chunk-4OZNXTO4.js.map → chunk-YRXFFWG4.js.map} +0 -0
  786. /package/dist/{enclave-55PAM4YO.js.map → collection-facade-CS7CA2XO.js.map} +0 -0
  787. /package/dist/{executor-5NIMLSKZ.js.map → computed-FAD7SED4.js.map} +0 -0
  788. /package/dist/{executor-7UQ3BZ4F.js.map → derive-XLOJVXPU.js.map} +0 -0
  789. /package/dist/{dispatch-IEPAVQUM.js.map → dispatch-H4HV32AM.js.map} +0 -0
  790. /package/dist/{executor-AQVXV5GZ.js.map → executor-FMPVN6DT.js.map} +0 -0
  791. /package/dist/{export-accessible-ORMKLA3E.js.map → executor-LBBBRTN5.js.map} +0 -0
  792. /package/dist/{extract-partition-NKYO3WNG.js.map → executor-OSRZST2U.js.map} +0 -0
  793. /package/dist/{find-NOGJ25VX.js.map → export-accessible-RAOJAAU5.js.map} +0 -0
  794. /package/dist/{issue-3DTQ7VJJ.js.map → extract-partition-JD5CUSTG.js.map} +0 -0
  795. /package/dist/{ledger-EDXFFZQL.js.map → find-JRN4RNQ5.js.map} +0 -0
  796. /package/dist/{liberate-V35QUHJP.js.map → issue-YIVBZUTH.js.map} +0 -0
  797. /package/dist/{link-set-2TTM3IZC.js.map → ledger-5ZMGE44K.js.map} +0 -0
  798. /package/dist/{managed-secret-NCWEMPUQ.js.map → liberate-WZSZM2W6.js.map} +0 -0
  799. /package/dist/{noydb-62MX4QUG.js.map → link-set-MFXC24VK.js.map} +0 -0
  800. /package/dist/{register-HHLXEQDN.js.map → managed-secret-MFTFT22X.js.map} +0 -0
  801. /package/dist/{migrate-cek-5ENH64AA.js.map → migrate-cek-DZRFFOHW.js.map} +0 -0
  802. /package/dist/{registry-23WF3TGH.js.map → noydb-ITIGNKUF.js.map} +0 -0
  803. /package/dist/{registry-4AYVUNXK.js.map → query/all/index.js.map} +0 -0
  804. /package/dist/{registry-JS3YFQQM.js.map → register-DT5HZ2LZ.js.map} +0 -0
  805. /package/dist/{registry-XX2Z7B46.js.map → registry-BHRZGZHF.js.map} +0 -0
  806. /package/dist/{request-withdrawal-CHPATE4G.js.map → registry-ET6ZFFWA.js.map} +0 -0
  807. /package/dist/{revoke-7M2VW4ON.js.map → registry-HXFGLGTG.js.map} +0 -0
  808. /package/dist/{signer-SJQJVSM6.js.map → registry-THCPDEOD.js.map} +0 -0
  809. /package/dist/{stale-BZS6H2RY.js.map → request-withdrawal-AV2MJJKY.js.map} +0 -0
  810. /package/dist/{storage-RW4QBS23.js.map → revoke-6DWIEOCV.js.map} +0 -0
  811. /package/dist/{walk-ZURCH6RZ.js.map → signer-MHGW3OIL.js.map} +0 -0
  812. /package/dist/{withdraw-accessible-PXR4IXDX.js.map → stale-6GCFMOOR.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-party/policy/presets.ts","../src/with-party/policy/engine.ts","../src/with-party/policy/noydb-facade.ts"],"sourcesContent":["import type { VaultPolicy } from '../../kernel/types.js'\n\n/**\n * Default policy for personal vaults and SMB deployments — the gates\n * that need an off-device factor get one (TOTP / email-OTP / paper\n * recovery), the rest take a tier-1 unlock alone. Tier-3 (PIN) is the\n * floor only for `rotate-unlock` because that's the\n * \"change my PIN\" flow.\n *\n * The unspecified gates (e.g. `view-user-auth`) inherit the engine\n * default of `{ enabled: false, minTier: 1 }` — they fail closed.\n *\n * @see https://github.com/noy-db/docs/blob/main/content/docs/services/session-tiers.md → Built-in gates\n */\nexport const PERSONAL_POLICY: VaultPolicy = Object.freeze({\n secret: {\n minWords: 6,\n minWordLength: 3,\n rejectRepeatedAdjacent: true,\n },\n gates: {\n 'rotate-secret': {\n minTier: 1,\n // Any second factor satisfies the gate — off-device kinds (TOTP,\n // email-OTP, paper recovery, roaming WebAuthn) are the strongest;\n // platform-bound kinds (platform WebAuthn, password, PIN) are\n // accepted because requiring \"something off-device\" is overkill\n // for personal/SMB threat models. Consumers needing the off-device\n // guarantee should use STRICT_POLICY or override this gate.\n factors: [{\n anyOf: [\n 'totp', 'email-otp', 'recovery',\n 'webauthn-roaming', 'webauthn-platform',\n 'password', 'pin',\n ],\n }],\n },\n 'recover-secret': {\n minTier: 1,\n enabled: true,\n },\n // rotate-recovery: deliberate paper-sheet regeneration\n // when the user remembers their secret. PERSONAL allows tier-1 —\n // knowing the secret is enough.\n 'rotate-recovery': { minTier: 1 },\n 'enroll-authenticator': { minTier: 1 },\n 'remove-authenticator': { minTier: 1 },\n // update-authenticator: meta-only mutation (slot rename, label\n // changes). Symmetric with enroll/remove under PERSONAL — tier-1\n // unlock alone. The structural anti-slot-swap guard inside the\n // implementation enforces wrap-material/id/method immutability\n // regardless of this gate's settings.\n 'update-authenticator': { minTier: 1 },\n 'rotate-unlock': { minTier: 2 },\n 'enroll-user': { minTier: 1 },\n 'revoke-user': { minTier: 1 },\n // Peer-recovery is a high-trust intentional op — co-owners\n // recovering each other should not need an off-device factor in\n // the personal/SMB threat model (the partner is already vetted by\n // virtue of being a co-owner). Tier-1 unlock is the floor; the\n // STRICT preset adds a recovery/email-OTP requirement.\n 'peer-recover-user': { minTier: 1 },\n // update-user: post-grant identity mutation (role/displayName/\n // permissions). PERSONAL_POLICY treats this on par with enroll-user\n // / revoke-user — tier-1 unlock alone. The role-elevation guard\n // inside the implementation is the structural backstop that this\n // gate's settings cannot weaken.\n 'update-user': { minTier: 1 },\n 'export-bundle': { minTier: 1 },\n 'export-plaintext': {\n minTier: 1,\n factors: [{ anyOf: ['totp', 'email-otp'] }],\n },\n 'view-user-auth': {\n minTier: 1,\n enabled: false,\n },\n // ─── User envelope gates ──────────────────────────────────────────\n // edit-own-profile: tier 3 floor — any active session can edit their\n // own profile/preferences. Tightening to require a TOTP for\n // profile changes is a one-line override.\n // view-team-profiles: tier 2 floor — an authenticated session can\n // read teammates' profiles (display names, avatars, locales).\n // Setting `enabled: false` makes vault.user.list() return only\n // self (privacy-strict opt-out).\n 'edit-own-profile': { minTier: 3 },\n 'view-team-profiles': { minTier: 2 },\n // client-unilateral-withdraw: a non-owner's self-service DESTRUCTIVE\n // withdrawal (export-and-delete/freeze). Fail-closed by default —\n // the firm opts in per jurisdiction/contract (e.g. GDPR Art. 17).\n // Listed explicitly (not just relying on the built-in default) so it is\n // discoverable in describeGate / policy dumps.\n 'client-unilateral-withdraw': { minTier: 1, enabled: false },\n // Two-party withdrawal: filing a request is non-destructive\n // (tier-1, enabled so a read-only client can ask); deciding it is the\n // destructive step (tier-2 floor + owner/admin role, enforced in code).\n 'user-request-withdrawal': { minTier: 1 },\n 'approve-user-withdrawal': { minTier: 2 },\n },\n}) as VaultPolicy\n\n/**\n * Strict policy for regulated deployments and shared workstations —\n * raises the phrase floor to 8 words, demands two distinct factors for\n * exports, and blocks export-on-shared-device. Use as a base for\n * `policy: { ...STRICT_POLICY, gates: { ...STRICT_POLICY.gates, ... } }`\n * tweaks.\n */\nexport const STRICT_POLICY: VaultPolicy = Object.freeze({\n secret: {\n minWords: 8,\n minWordLength: 3,\n rejectRepeatedAdjacent: true,\n },\n gates: {\n 'rotate-secret': {\n minTier: 1,\n factors: [{ anyOf: ['totp', 'email-otp', 'recovery'], count: 2 }],\n },\n 'recover-secret': {\n minTier: 1,\n enabled: true,\n },\n // rotate-recovery: STRICT requires an off-device factor —\n // rotating recovery is an off-site-trust event; a stolen unlocked\n // laptop must not be able to silently mint a new sheet for the\n // attacker. Matches the `peer-recover-user` STRICT default.\n 'rotate-recovery': {\n minTier: 1,\n factors: [{ anyOf: ['totp', 'email-otp', 'webauthn-roaming'] }],\n },\n 'enroll-authenticator': {\n minTier: 1,\n factors: [{ anyOf: ['totp', 'email-otp'] }],\n },\n 'remove-authenticator': {\n minTier: 1,\n factors: [{ anyOf: ['totp', 'email-otp'] }],\n },\n // STRICT update-authenticator: same factor floor as enroll/remove.\n // Even though meta changes don't touch wrap material, a malicious\n // rename could mislead the user about which device a slot\n // corresponds to (\"MacBook Touch ID\" → \"iPhone Touch ID\" on a\n // shared workstation). STRICT requires a fresh factor proof.\n 'update-authenticator': {\n minTier: 1,\n factors: [{ anyOf: ['totp', 'email-otp'] }],\n },\n 'rotate-unlock': { minTier: 1 },\n 'enroll-user': {\n minTier: 1,\n factors: [{ anyOf: ['totp', 'email-otp'] }],\n },\n 'revoke-user': {\n minTier: 1,\n factors: [{ anyOf: ['totp', 'email-otp'] }],\n },\n // STRICT peer-recovery: the issuer must present a recovery code\n // OR a fresh off-device second factor at the moment of recovery.\n // This binds the high-trust operation to a verifiable proof\n // (recovery sheet photographed by an attacker won't suffice —\n // they'd also need tier-1 unlock first; this gate is the freshness\n // binding on top). Roaming WebAuthn (YubiKey-class hardware key)\n // accepted; platform-bound kinds (Touch ID, password, PIN)\n // intentionally excluded under STRICT because they don't survive\n // device theft — the off-device requirement is the whole point.\n 'peer-recover-user': {\n minTier: 1,\n factors: [{ anyOf: ['recovery', 'totp', 'email-otp', 'webauthn-roaming'] }],\n },\n // STRICT update-user: matches the enroll-user / revoke-user shape\n // (off-device factor required). Update-user is admin-shaped — it\n // mutates someone else's role/permissions; STRICT requires a fresh\n // off-device factor proof so the operator affirmatively re-asserts\n // identity at the moment of mutation. Platform-bound factors\n // (Touch ID / password / PIN) intentionally excluded: same logic as\n // peer-recover-user — the off-device requirement is the whole\n // point under STRICT.\n 'update-user': {\n minTier: 1,\n factors: [{ anyOf: ['totp', 'email-otp'] }],\n },\n 'export-bundle': {\n minTier: 1,\n factors: [{ anyOf: ['totp', 'email-otp'] }],\n warn: { sharedDevice: 'block' },\n },\n 'export-plaintext': {\n minTier: 1,\n factors: [{ anyOf: ['totp', 'email-otp'], count: 2 }],\n warn: { sharedDevice: 'block' },\n },\n 'view-user-auth': {\n minTier: 1,\n enabled: false,\n },\n // ─── User envelope gates ──────────────────────────────────────────\n // STRICT: profile edits require a TOTP/email-OTP factor (typical\n // shared-workstation hardening — your name/avatar shouldn't change\n // without a fresh second-factor proof).\n 'edit-own-profile': {\n minTier: 2,\n factors: [{ anyOf: ['totp', 'email-otp'] }],\n },\n 'view-team-profiles': { minTier: 2 },\n // STRICT: still fail-closed, but if a regulated firm flips enabled:true\n // they inherit a two-factor proof + shared-device block for the\n // destructive withdrawal (mirrors export-plaintext's hardening).\n 'client-unilateral-withdraw': {\n minTier: 1,\n enabled: false,\n factors: [{ anyOf: ['totp', 'email-otp'], count: 2 }],\n warn: { sharedDevice: 'block' },\n },\n // STRICT: filing stays tier-1; the destructive APPROVE demands an\n // off-device factor + shared-device block (mirrors export-bundle).\n 'user-request-withdrawal': { minTier: 1 },\n 'approve-user-withdrawal': {\n minTier: 2,\n factors: [{ anyOf: ['totp', 'email-otp'] }],\n warn: { sharedDevice: 'block' },\n },\n },\n}) as VaultPolicy\n\n/**\n * Merge a developer override onto a preset. Unspecified gates inherit;\n * specified gates fully replace the preset's entry for that gate.\n *\n * Example:\n *\n * ```ts\n * mergePolicy(PERSONAL_POLICY, {\n * gates: {\n * 'app:approve-large-payment': { minTier: 2, factors: [{ anyOf: ['totp'] }] },\n * },\n * })\n * // → PERSONAL_POLICY plus the new app gate; existing gates intact.\n * ```\n */\nexport function mergePolicy(\n base: VaultPolicy,\n override?: Partial<VaultPolicy>,\n): VaultPolicy {\n if (!override) return base\n const secret = override.secret ?? base.secret\n return {\n ...(secret !== undefined ? { secret } : {}),\n gates: {\n ...base.gates,\n ...(override.gates ?? {}),\n },\n }\n}\n","/**\n * Policy gate engine — the {@link checkGate} entry point.\n *\n * Given a configured {@link VaultPolicy}, an active session tier, and\n * the factor proofs an actor is presenting, decide whether the gate\n * permits the action. On denial, throws {@link PolicyDeniedError} with\n * a stable {@link PolicyDenyReason} so consumers can branch in error\n * UIs.\n *\n * @see https://github.com/noy-db/docs/blob/main/content/docs/services/session-tiers.md → checkGate() API\n *\n * @module\n */\nimport { PolicyDeniedError, type PolicyDenyReason } from '../../kernel/errors.js'\nimport type {\n ActiveTier,\n FactorProof,\n GateName,\n GatePolicy,\n VaultPolicy,\n FactorRequirement,\n} from '../../kernel/types.js'\n\n/** Default freshness window — 5 minutes. */\nexport const DEFAULT_FRESHNESS_MS = 5 * 60 * 1000\n\n/** Caller-supplied context for one `checkGate` invocation. */\nexport interface CheckGateContext {\n /** Tier the active session currently holds. */\n readonly activeTier: ActiveTier\n /** Proofs the actor is presenting for this gate. */\n readonly factors?: ReadonlyArray<FactorProof>\n /**\n * If the host knows the actor is on a shared device, set this to\n * `true` so the engine can apply `warn.sharedDevice` rules. Defaults\n * to `false`.\n */\n readonly sharedDevice?: boolean\n /**\n * Override `now()` for tests. Defaults to `Date.now()`.\n * @internal\n */\n readonly now?: number\n}\n\n/**\n * Decide whether `gate` permits the action under `context`. Throws\n * {@link PolicyDeniedError} on denial; resolves with `void` on success.\n *\n * Lookup rules:\n * - **Built-in gates** without a configured policy fail closed\n * (`enabled: false`).\n * - **App-defined gates** (`app:*`) without a configured policy are\n * treated as no-op (allow). The developer registered the policy if\n * they wanted enforcement; absence means the gate is informational.\n */\nexport async function checkGate(\n policy: VaultPolicy,\n gate: GateName,\n context: CheckGateContext,\n): Promise<void> {\n const configured = policy.gates[gate]\n if (!configured) {\n if (gate.startsWith('app:')) {\n // Custom app gate without a policy — the developer hasn't\n // registered one; engine treats it as an unenforced label.\n return\n }\n // Built-in gate without a policy — fail closed.\n throw deny(gate, 'disabled', { minTier: 1, enabled: false })\n }\n\n if (configured.enabled === false) {\n throw deny(gate, 'disabled', configured)\n }\n\n // Tier check first — cheap and a hard prerequisite.\n if (context.activeTier > configured.minTier) {\n // Higher number is a LOWER tier in this model (1 is most privileged).\n throw deny(gate, 'insufficient-tier', configured)\n }\n\n // Factor checks — every requirement entry must be satisfied.\n if (configured.factors && configured.factors.length > 0) {\n const presented = context.factors ?? []\n const now = context.now ?? Date.now()\n for (const requirement of configured.factors) {\n const matches = countMatchingFactors(presented, requirement, now)\n const need = requirement.count ?? 1\n if (matches.fresh < need) {\n if (matches.totalKindMatches < need) {\n throw deny(gate, 'missing-factor', configured)\n }\n // Some matched the kind list but not the freshness window.\n throw deny(gate, 'stale-proof', configured)\n }\n }\n }\n\n // Soft signals — only `'block'` raises here.\n if (configured.warn?.sharedDevice === 'block' && context.sharedDevice === true) {\n throw deny(gate, 'shared-device-blocked', configured)\n }\n}\n\n/**\n * Same as {@link checkGate} but returns a structured verdict instead\n * of throwing. Useful when an error UI wants to show the user\n * \"you'll need TOTP plus a recovery code to do that\" without first\n * triggering the action.\n */\nexport async function describeGate(\n policy: VaultPolicy,\n gate: GateName,\n context: CheckGateContext,\n): Promise<{ ok: true } | { ok: false; reason: PolicyDenyReason; required: GatePolicy }> {\n try {\n await checkGate(policy, gate, context)\n return { ok: true }\n } catch (err) {\n if (err instanceof PolicyDeniedError) {\n return { ok: false, reason: err.reason, required: err.required }\n }\n throw err\n }\n}\n\nfunction countMatchingFactors(\n presented: ReadonlyArray<FactorProof>,\n requirement: FactorRequirement,\n now: number,\n): { totalKindMatches: number; fresh: number } {\n const freshnessMs = requirement.freshnessMs ?? DEFAULT_FRESHNESS_MS\n let totalKindMatches = 0\n let fresh = 0\n for (const proof of presented) {\n if (!requirement.anyOf.includes(proof.kind)) continue\n totalKindMatches += 1\n const minted = proof.mintedAt ? Date.parse(proof.mintedAt) : now\n if (Number.isFinite(minted) && now - minted <= freshnessMs) {\n fresh += 1\n }\n }\n return { totalKindMatches, fresh }\n}\n\nfunction deny(gate: GateName, reason: PolicyDenyReason, required: GatePolicy): PolicyDeniedError {\n return new PolicyDeniedError(gate, reason, required)\n}\n","/**\n * Noydb-side policy / session-policy facade.\n *\n * Holds the vault-policy read/update/bootstrap logic (`getPolicy`,\n * `updatePolicy`, `bootstrapPolicy`) and the per-vault session-policy\n * enforcer wiring (`touchPolicy`, `checkPolicyOperation`). The session\n * *timer* and the managed-recovery enrolment check stay kernel-resident on\n * `Noydb` (interleaved with the instance lifecycle) and arrive here as the\n * `resetSessionTimer` / `assertRecoveryEnrolled` callbacks. The policy cache\n * and the enforcer map stay `Noydb`-resident too (touched by\n * `openVault`/`close`) and arrive by reference; behaviour is byte-identical\n * to the inline `Noydb` methods it replaced — every other dependency the\n * moving code touched on `this.*` arrives via {@link NoydbPolicyDeps}.\n *\n * Internal service — reached through `noydb.getPolicy(...)` etc. Public\n * contract ({@link NoydbPolicyApi} / {@link NoydbPolicyDeps} /\n * {@link NoydbPolicyFactory}) lives in the kernel spine; `createNoydb()`\n * wires this file's {@link createNoydbPolicy} in via a pre-resolved dynamic\n * import (the S4 gate recipe).\n */\nimport { ValidationError } from '../../kernel/errors.js'\nimport { PERSONAL_POLICY, mergePolicy } from './presets.js'\nimport { loadVaultPolicy, saveVaultPolicy } from './storage.js'\nimport type { VaultPolicy, ReAuthOperation, NoydbPolicyApi, NoydbPolicyDeps, NoydbPolicyFactory } from '../../kernel/types.js'\n\nexport class NoydbPolicy implements NoydbPolicyApi {\n constructor(private readonly deps: NoydbPolicyDeps) {}\n\n /**\n * Touch the policy enforcer for a vault (records activity, resets\n * idle timer). Also touches the legacy session timer. No-op if no enforcer.\n */\n touchPolicy(vault?: string): void {\n this.deps.resetSessionTimer()\n if (vault) {\n this.deps.policyEnforcers.get(vault)?.touch()\n }\n }\n\n /**\n * Check that a policy-guarded operation is permitted.\n * Throws `SessionPolicyError` if re-auth is required.\n */\n checkPolicyOperation(vault: string, op: ReAuthOperation): void {\n this.deps.policyEnforcers.get(vault)?.checkOperation(op)\n }\n\n /**\n * Read the active policy for a vault. Loads from `_meta/policy` on\n * first call; subsequent calls hit the in-memory cache. Throws\n * `ValidationError` if the vault has not been opened.\n */\n async getPolicy(vault: string): Promise<VaultPolicy> {\n if (this.deps.isClosed()) throw new ValidationError('Instance is closed')\n const cached = this.deps.policyCache.get(vault)\n if (cached) return cached\n await this.bootstrapPolicy(vault)\n return this.deps.policyCache.get(vault) ?? PERSONAL_POLICY\n }\n\n /**\n * Replace the policy document at `_meta/policy` and update the\n * in-memory cache. Gated by the `enroll-user` policy (a policy\n * change is fundamentally a privilege-management action).\n */\n async updatePolicy(vault: string, override: Partial<VaultPolicy>): Promise<VaultPolicy> {\n if (this.deps.isClosed()) throw new ValidationError('Instance is closed')\n const current = await this.getPolicy(vault)\n const merged = mergePolicy(current, override)\n if (this.deps.encrypted) {\n await saveVaultPolicy(this.deps.store, vault, merged)\n }\n this.deps.policyCache.set(vault, merged)\n return merged\n }\n\n /** Read or persist the vault policy at `_meta/policy` on first open. */\n async bootstrapPolicy(\n vault: string,\n opts?: { skipManagedCheck?: boolean },\n ): Promise<void> {\n const onDisk = await loadVaultPolicy(this.deps.store, vault)\n if (onDisk) {\n // Honour the on-disk document; developer overrides cannot\n // weaken what the vault committed to at creation time.\n this.deps.policyCache.set(vault, onDisk)\n await this.deps.assertRecoveryEnrolled(vault, onDisk, opts)\n return\n }\n // First time — persist the developer's policy (or default preset).\n const initial = this.deps.policyOption\n ? mergePolicy(PERSONAL_POLICY, this.deps.policyOption)\n : PERSONAL_POLICY\n await saveVaultPolicy(this.deps.store, vault, initial)\n this.deps.policyCache.set(vault, initial)\n await this.deps.assertRecoveryEnrolled(vault, initial, opts)\n }\n}\n\n/**\n * Factory that builds the `NoydbPolicy` service. `createNoydb()`\n * pre-resolves this via a dynamic import (mirrors\n * `with-party/directory/user-envelope/api.js#createUserApi`) and threads\n * it through `NoydbOptions.policyFactory` so `Noydb`'s constructor can\n * build `this.policyManager` synchronously.\n */\nexport const createNoydbPolicy: NoydbPolicyFactory = (deps) => new NoydbPolicy(deps)\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@ import {
2
2
  OverlayBaseIsVirtualError,
3
3
  OverlayCollectionUnavailableError,
4
4
  OverlayNameCollisionError
5
- } from "./chunk-7O3Y4PPD.js";
5
+ } from "./chunk-37EPTBBR.js";
6
6
 
7
7
  // src/with-formula/overlay-views/registry.ts
8
8
  var OverlayedViewRegistry = class {
@@ -68,4 +68,4 @@ var OverlayedViewRegistry = class {
68
68
  export {
69
69
  OverlayedViewRegistry
70
70
  };
71
- //# sourceMappingURL=chunk-Q2PU57HC.js.map
71
+ //# sourceMappingURL=chunk-VVOLDVIR.js.map
@@ -7,28 +7,28 @@ import {
7
7
  deriveSchemaManifest,
8
8
  loadSchemaManifestEntry,
9
9
  saveSchemaManifest
10
- } from "./chunk-4GFX6MPA.js";
10
+ } from "./chunk-TEHHROZL.js";
11
11
  import {
12
12
  MANIFEST_COLLECTION
13
13
  } from "./chunk-EPFF7LOX.js";
14
14
  import {
15
15
  loadFence,
16
16
  saveFence
17
- } from "./chunk-453F654R.js";
17
+ } from "./chunk-VAORSCSU.js";
18
18
  import {
19
19
  derivePersistedSchema
20
- } from "./chunk-ACAQ2DSG.js";
20
+ } from "./chunk-747G6CT3.js";
21
21
  import {
22
22
  loadPersistedSchemaEntry,
23
23
  savePersistedSchema
24
- } from "./chunk-SGZAIJCV.js";
24
+ } from "./chunk-3L2NBKDR.js";
25
25
  import {
26
26
  envelopePayloadHash
27
- } from "./chunk-QPQUVOZ2.js";
27
+ } from "./chunk-6QVKPHBF.js";
28
28
  import {
29
29
  ManifestConflictError,
30
30
  isConflictError
31
- } from "./chunk-7O3Y4PPD.js";
31
+ } from "./chunk-37EPTBBR.js";
32
32
 
33
33
  // src/with-shape/persisted-schemas/field-ids.ts
34
34
  var BASE32URL_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567";
@@ -257,4 +257,4 @@ export {
257
257
  persistClassifiedMarker,
258
258
  persistSatelliteMarker
259
259
  };
260
- //# sourceMappingURL=chunk-3VF3WQ4F.js.map
260
+ //# sourceMappingURL=chunk-WPOYWFSE.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/with-shape/persisted-schemas/field-ids.ts","../src/with-shape/manifest/writer.ts","../src/with-shape/manifest/sync.ts","../src/with-shape/persisted-schemas/register.ts"],"sourcesContent":["/**\n * Stable field-identifier minting (#946).\n *\n * A field's `id` is opaque and permanent — minted once, from randomness\n * only, with no timestamp and no name-derivation. A name-derived id would\n * change when the field is renamed, defeating the whole point (identity\n * must survive a rename). See `PersistedSchemaEnvelope.fieldIds`.\n *\n * @module\n */\n\nconst BASE32URL_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567'\n\n/** Unpadded RFC 4648 §7 base32url encoding of an arbitrary byte string. */\nfunction encodeBase32Url(bytes: Uint8Array): string {\n let bits = 0\n let value = 0\n let out = ''\n for (const byte of bytes) {\n value = (value << 8) | byte\n bits += 8\n while (bits >= 5) {\n out += BASE32URL_ALPHABET[(value >>> (bits - 5)) & 31]\n bits -= 5\n }\n }\n if (bits > 0) {\n out += BASE32URL_ALPHABET[(value << (5 - bits)) & 31]\n }\n return out\n}\n\n/**\n * Mint a fresh opaque field id: 12 random bytes (96 bits) from\n * `crypto.getRandomValues`, encoded as a 20-character base32url token.\n * Not exported — Task 2/3 (#946) may need to export this later; keep it\n * module-internal until an external consumer exists.\n */\nfunction mintFieldId(): string {\n const bytes = new Uint8Array(12)\n crypto.getRandomValues(bytes)\n return encodeBase32Url(bytes)\n}\n\n/** Top-level property names of a Draft 2020-12 object JSON Schema, or `[]` when not an object schema. */\nfunction topLevelFieldNames(jsonSchema: object | null): readonly string[] {\n if (jsonSchema === null) return []\n const props = (jsonSchema as { properties?: unknown }).properties\n if (props === null || typeof props !== 'object') return []\n return Object.keys(props as Record<string, unknown>)\n}\n\n/**\n * Resolve the `fieldIds` map for a freshly-derived schema against the\n * previously-stored envelope's map (if any). Existing ids are preserved by\n * name — a field keeps its id across re-derivation as long as its name is\n * unchanged; a genuinely new field name mints a fresh id. Returns\n * `undefined` when the fresh schema has no derivable top-level field set\n * (e.g. a stub envelope, or a non-object root schema).\n *\n * `renamed` (#946 Task 2) — unambiguous rename pairs from\n * `computeSchemaDelta`'s `SchemaDelta.renamed`. When the fresh name is a\n * rename target (`to`), the id is looked up under its OLD name (`from`)\n * instead of the fresh name itself — the whole point of a stable field id\n * is that it survives exactly this case; without it, a rename would mint a\n * fresh id for the new name and silently drop the old one.\n */\nexport function resolveFieldIds(\n freshJsonSchema: object | null,\n priorFieldIds: Record<string, string> | undefined,\n renamed?: readonly { readonly from: string; readonly to: string }[],\n): Record<string, string> | undefined {\n const names = topLevelFieldNames(freshJsonSchema)\n if (names.length === 0) return undefined\n const priorNameFor = new Map<string, string>()\n if (renamed) for (const { from, to } of renamed) priorNameFor.set(to, from)\n const out: Record<string, string> = {}\n for (const name of names) {\n const priorName = priorNameFor.get(name) ?? name\n out[name] = priorFieldIds?.[priorName] ?? mintFieldId()\n }\n return out\n}\n","/**\n * Strict-CAS schema-manifest writer (#941, AC #1).\n *\n * Every other reserved-collection writer in the hub (`persisted-schemas`'s\n * `savePersistedSchema` callers, the classified/satellite marker writers)\n * retries on a CAS conflict: re-read, re-apply, try again. The schema\n * manifest deliberately does NOT — two concurrent *direct* edits to the\n * manifest must be refused and surfaced, never silently merged or\n * retry-resolved, because a manifest write encodes a specific point-in-time\n * claim about the pod's schema generation/content-hash set. A caller that\n * lost the race must re-derive against the fresh state and decide what to\n * do, not have its stale write quietly re-applied on top of someone else's.\n *\n * Ledger audit: this writer does not have a `LedgerStore` handle at this\n * seam (mirroring `persisted-schemas`, whose `_schemas/<collection>` writes\n * are likewise not ledger-audited today). Wiring `LedgerStore.append({ op:\n * 'migration', ... })` around a manifest write belongs at the call site\n * where a ledger handle is already in scope (the open/write integration —\n * #941 Task 3/4), not here.\n *\n * @module\n */\n\nimport { isConflictError, ManifestConflictError } from '../../kernel/errors.js'\nimport { saveSchemaManifest, type GetManifestDEK } from './storage.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { SchemaManifest } from './types.js'\n\n/**\n * Write the schema manifest as a strict CAS: `expectedVersion` must match\n * the stored envelope's `_v` (0 when no manifest exists yet). On a\n * conflict, throws {@link ManifestConflictError} — this function never\n * retries.\n */\nexport async function writeSchemaManifest(\n store: NoydbStore,\n vault: string,\n manifest: SchemaManifest,\n expectedVersion: number,\n getDEK: GetManifestDEK,\n): Promise<void> {\n try {\n await saveSchemaManifest(store, vault, manifest, expectedVersion, getDEK)\n } catch (err) {\n if (isConflictError(err)) {\n throw new ManifestConflictError(err.version, expectedVersion)\n }\n throw err\n }\n}\n","/**\n * Keep the persisted `_manifest/schema` record in sync with the\n * per-collection `_schemas/<collection>` source of truth (#941 Task 3).\n *\n * Called from `persisted-schemas/register.ts`'s `persistSchemaIfNeeded`\n * right after a `_schemas/<collection>` write succeeds.\n *\n * ## Reconciling strict-CAS-refuse with the derived nature of the manifest\n *\n * `writer.ts`'s `writeSchemaManifest` is deliberately strict-CAS: a\n * concurrent *direct* edit to the manifest is refused, never retried (AC\n * #1). But this call site is not a direct edit — it is a re-derivation\n * from the source of truth, triggered as a side effect of a `_schemas`\n * write that has ALREADY succeeded. When N collections are declared\n * together (one `openVault()` call registering several `persistJsonSchema:\n * true` collections), their `persistSchemaIfNeeded` calls run CONCURRENTLY\n * — each one's own `_schemas/<collection>` write lands independently, and\n * each then calls this function. A single derive-then-write with no\n * feedback loop is NOT enough here: a call whose `deriveSchemaManifest`\n * snapshot ran before a sibling's `_schemas` write landed would persist a\n * manifest that's missing that sibling forever (nothing re-triggers it) —\n * this was a REAL bug (not a test-isolation artifact — reproduced with 0\n * shared state, purely from concurrent `_schemas` writes racing the derive\n * snapshot; see #941 flake fix commit). `syncSchemaManifest` therefore\n * loops (bounded by {@link MAX_SYNC_ATTEMPTS}):\n *\n * 1. Derive fresh, compare to what's persisted. Already converged → done.\n * 2. Write via strict-CAS. `ManifestConflictError` (another writer moved\n * the manifest first) → loop: re-derive against the NEW state and\n * retry, exactly like a normal optimistic-CAS retry.\n * 3. On a successful write, derive ONE more time. If a sibling's\n * `_schemas` write landed while we were deriving/writing, the state\n * has already moved past what we just wrote — loop again to catch it\n * up. If the re-derive matches what we wrote, we're the last writer\n * and the manifest is provably converged: return.\n *\n * This guarantees that whichever collection's `_schemas` write is the LAST\n * to land (in real wall-clock order — there is always exactly one, even\n * though no single caller knows which) ends up running a sync whose own\n * derive sees every sibling's write already committed, and whose\n * recheck-after-write confirms nothing moved further — so it converges the\n * manifest deterministically, regardless of interleaving. If attempts are\n * exhausted under pathological contention, the loop gives up — with a\n * `console.warn` (see cap-exhaustion note below), not silently — leaving\n * the last successful write in place; the manifest is a derivable cache\n * (not a correctness invariant), so a next schema write or `open()`'s\n * re-derive still converges it, matching how `persistSchemaIfNeeded`\n * already treats its own write failures.\n * Any error OTHER than `ManifestConflictError` (store failure, DEK\n * resolution failure, etc.) is NOT swallowed — it propagates to the\n * caller immediately, matching how `persistSchemaIfNeeded` already treats\n * its own write failures as a visible (not silently discarded) fingerprint\n * failure.\n *\n * ## CRITICAL fix (#941 review): a scoped principal must never write a\n * partial manifest\n *\n * `deriveSchemaManifest` returns `undecodableCollections` for any\n * `_schemas/<collection>` entry this principal's `lookupDEK` couldn't\n * resolve — e.g. a member `grant()`-scoped to one collection can decrypt\n * `_manifest` itself (system-prefixed DEKs propagate to every role) but\n * NOT a sibling data collection's `_schemas/<sibling>` entry. Before this\n * fix, `deriveSchemaManifest` silently treated \"can't decrypt\" the same as\n * \"no derivable content\" and omitted the sibling — so a scoped member's\n * OWN schema declare would derive a manifest missing every collection they\n * can't see, load the real (complete) persisted manifest as a valid CAS\n * token, observe a hash mismatch, and OVERWRITE it — permanently dropping\n * the siblings, silently, ledger-audited as a legitimate migration. The\n * converging recheck loop above made this WORSE, not better: re-deriving\n * from the same restricted view just re-confirms the same partial result\n * and declares it \"stable.\"\n *\n * Fix: if `undecodableCollections` is non-empty, `syncSchemaManifest`\n * `SKIP`s entirely — no read-compare, no write, no audit, no loop. It does\n * NOT try to merge-preserve the undecodable entries from what's currently\n * persisted: an `aggregateHash` computed over entries this principal can't\n * verify would be a hash asserting something it never actually checked,\n * which is worse than not writing at all. This is safe because:\n *\n * - The source of truth, `_schemas/<their-collection>`, was ALREADY\n * updated by their declare — nothing about their own change is lost.\n * - The pod-wide `_manifest` cache simply isn't rewritten by a principal\n * who'd corrupt it by omission — it stays whatever a full-visibility\n * principal (owner/admin/custodian) last wrote.\n * - `open()` re-derives from `_schemas` directly (not from the cached\n * manifest) for whoever opens it, so a stale cache is never load-bearing.\n * - The NEXT full-visibility principal's declare (or any subsequent sync\n * by someone with complete access) naturally refreshes the cache,\n * including the scoped member's own just-landed change.\n *\n * ## Ledger audit (AC #5)\n *\n * Schema-generation transitions are not ledger-audited anywhere else today\n * (neither the `_schemas/<collection>` write in `register.ts` nor the fence\n * bump in `schema-update/fence-controller.ts#runCutover`). This is the\n * first ledger-audited signal for a schema change: every manifest write\n * that actually happens (i.e. every time the derived manifest differs from\n * what's stored) appends an `op: 'migration'` entry via the optional\n * `getLedgerOrNull` callback — `null`/absent when the history strategy\n * isn't opted in, matching every other optional-ledger call site\n * (`vault._getLedgerOrNull()?.append(...)`, see `liberate.ts`,\n * `with-audit/periods/vault-facade.ts`). No audit fires on the no-op path\n * (derived manifest unchanged), on a swallowed conflict (the winning\n * writer's own sync already audits its write), or on the scoped-principal\n * SKIP path above (nothing was written).\n *\n * @module\n */\n\nimport type { LedgerStore } from '../../with-commit/history/ledger/store.js'\nimport { envelopePayloadHash } from '../../with-commit/history/ledger/hash.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport { ManifestConflictError } from '../../kernel/errors.js'\nimport { deriveSchemaManifest, type LookupDEK } from './derive.js'\nimport { loadSchemaManifestEntry, type GetManifestDEK } from './storage.js'\nimport { writeSchemaManifest } from './writer.js'\nimport { MANIFEST_COLLECTION } from './reserved-collections.js'\nimport { MANIFEST_SCHEMA_RECORD_ID, type SchemaManifest } from './types.js'\n\nexport interface ManifestSyncDeps {\n /** Resolver for `_manifest`'s OWN DEK — mints if absent (this principal's declare is about to write it). */\n readonly getDEK: GetManifestDEK\n /**\n * NON-MINTING lookup for every OTHER (sibling data) collection's DEK,\n * used by `deriveSchemaManifest` to build the index — see `derive.ts`'s\n * `LookupDEK` doc for why minting here would be actively harmful.\n */\n readonly lookupDEK: LookupDEK\n /** Optional ledger handle — absent when the history strategy isn't opted in (no-op audit). */\n readonly getLedgerOrNull?: () => LedgerStore | null\n}\n\n/**\n * Bound on the derive→write→recheck loop (see module doc). Headroom above\n * what any realistic `openVault()` batch (a handful of collections) needs —\n * a 40-way concurrent-declare stress repro converged within single digits.\n */\nconst MAX_SYNC_ATTEMPTS = 16\n\n/**\n * Re-derive the schema manifest and write it if it changed, looping until\n * the write is provably stable against concurrent sibling `_schemas`\n * writes (see module doc). SKIPS entirely (no write, no audit) when this\n * principal's view is incomplete (`undecodableCollections` non-empty) —\n * see the module doc's CRITICAL fix section. Swallows a\n * {@link ManifestConflictError} by retrying against the fresher state; any\n * other error propagates. Ledger-audits (`op: 'migration'`) once per\n * successful write.\n */\nexport async function syncSchemaManifest(\n store: NoydbStore,\n vault: string,\n deps: ManifestSyncDeps,\n): Promise<void> {\n for (let attempt = 0; attempt < MAX_SYNC_ATTEMPTS; attempt++) {\n const { manifest: derived, undecodableCollections } = await deriveSchemaManifest(store, vault, deps.lookupDEK)\n if (undecodableCollections.length > 0) {\n // Scoped principal — see module doc's CRITICAL fix. Never write a\n // manifest we know is missing collections we couldn't decrypt.\n return\n }\n\n const existing = await loadSchemaManifestEntry(store, vault, deps.getDEK)\n\n if (\n existing &&\n existing.manifest.generation === derived.generation &&\n existing.manifest.aggregateHash === derived.aggregateHash\n ) {\n return // already converged — no write, no audit\n }\n\n const expectedVersion = existing?.version ?? 0\n try {\n await writeSchemaManifest(store, vault, derived, expectedVersion, deps.getDEK)\n } catch (err) {\n if (err instanceof ManifestConflictError) continue // another writer moved it first — re-derive + retry\n throw err\n }\n\n await auditManifestWrite(store, vault, deps, derived, expectedVersion)\n\n // A sibling's `_schemas` write can land while WE were deriving/writing\n // (#941 flake fix — see module doc). Re-derive once more: if the truth\n // has already moved past what we just wrote, loop again to catch it up\n // instead of leaving a permanently-partial manifest. (A principal with\n // full visibility at the top of this iteration cannot lose that\n // visibility mid-call — the keyring doesn't change during one sync —\n // so this recheck is never itself subject to the undecodable-skip; the\n // `for` loop's own guard covers it defensively regardless.)\n const recheck = await deriveSchemaManifest(store, vault, deps.lookupDEK)\n if (recheck.undecodableCollections.length > 0) return // defensive — see comment above\n if (recheck.manifest.generation === derived.generation && recheck.manifest.aggregateHash === derived.aggregateHash) {\n return // stable — we were the last writer\n }\n }\n // Attempts exhausted under pathological contention — see module doc:\n // the manifest is a derivable cache, not a correctness invariant, so a\n // later schema write or open()'s re-derive still converges it. Warn\n // rather than fail silently — a silent give-up on a completeness\n // invariant is exactly what hid the original concurrent-declare race.\n console.warn(\n `[noy-db] schema-manifest sync did not converge after ${MAX_SYNC_ATTEMPTS} attempts; ` +\n 'the persisted manifest may be stale until the next schema write or open()\\'s re-derive.',\n )\n}\n\nasync function auditManifestWrite(\n store: NoydbStore,\n vault: string,\n deps: ManifestSyncDeps,\n derived: SchemaManifest,\n expectedVersion: number,\n): Promise<void> {\n const ledger = deps.getLedgerOrNull?.()\n if (!ledger) return\n // `_manifest/schema` has a real, individually-addressable envelope (unlike\n // a `lifecycle` entry's empty collection/id) — `verifyBackupIntegrity`'s\n // data cross-check (`with-pod/backup.ts`) treats any non-amendment,\n // non-lifecycle entry's `collection`/`id` as \"the latest write to this\n // record\" and recomputes+compares its ciphertext hash, so `payloadHash`\n // must be the real `envelopePayloadHash` of the just-written envelope\n // (ciphertext domain), NOT `derived.aggregateHash` (plaintext domain) —\n // using the wrong domain here trips a false-positive tamper failure on\n // restore.\n const envelope = await store.get(vault, MANIFEST_COLLECTION, MANIFEST_SCHEMA_RECORD_ID)\n await ledger.append({\n op: 'migration',\n collection: MANIFEST_COLLECTION,\n id: MANIFEST_SCHEMA_RECORD_ID,\n version: expectedVersion + 1,\n actor: '',\n payloadHash: await envelopePayloadHash(envelope),\n reason: `schema-manifest-sync:generation=${derived.generation}`,\n })\n}\n","/**\n * Orchestrate the derive → hash → skip-or-write cycle for a collection's\n * persisted JSON Schema. Called by the Vault at collection-registration\n * time when the developer opts in via `collection({ persistJsonSchema:\n * true })`.\n *\n * Skip semantics:\n *\n * - Zod validators: skip when the new hash equals the stored hash.\n * - Non-Zod (stub envelopes have hash=null): skip when the stored\n * envelope's `kind` matches the freshly-detected kind (since there's\n * no body to compare yet — a kind change is the only signal).\n *\n * @module\n */\n\nimport { derivePersistedSchema } from './derive.js'\nimport { loadPersistedSchemaEntry, savePersistedSchema } from './storage.js'\nimport { resolveFieldIds } from './field-ids.js'\nimport { computeSchemaDelta } from '../schema-update/delta.js'\nimport { evaluateStrategies } from '../schema-update/dispatch.js'\nimport { loadFence, saveFence } from '../schema-update/fence.js'\nimport { isConflictError } from '../../kernel/errors.js'\nimport { syncSchemaManifest, type ManifestSyncDeps } from '../manifest/sync.js'\nimport type { SchemaUpdateStrategy, UpdateDecision, SchemaDelta } from '../schema-update/types.js'\nimport type { NoydbStore, ClassifiedMarker } from '../../kernel/types.js'\nimport type { PersistedSchemaEnvelope } from './types.js'\nimport type { PairingMarker } from '../satellites/types.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\n\n/**\n * Max optimistic-CAS retries for the shared `_schemas/<collection>` record\n * (#583). Only two writers ever contend (the JSON-Schema writer and the\n * classified-marker writer), so one retry suffices; the headroom covers a\n * pathological re-order. Exhausting it rethrows the {@link ConflictError}\n * rather than silently losing a write.\n */\nconst MAX_SCHEMA_CAS_RETRIES = 5\n\nexport interface PersistSchemaResult {\n /** True when a fresh envelope was written to storage. */\n readonly written: boolean\n /** True when an existing envelope matched and the write was skipped. */\n readonly skipped: boolean\n /** The envelope that was either written or matched. */\n readonly envelope: PersistedSchemaEnvelope\n /** The update-strategy decision, present when strategies ran. */\n readonly decision?: UpdateDecision\n}\n\nexport async function persistSchemaIfNeeded(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly validator: unknown\n readonly dek: EnclaveKey\n readonly strategies?: readonly SchemaUpdateStrategy[]\n /** #941 Task 3: re-derive + sync the `_manifest/schema` INDEX after a write. Omitted → no sync (e.g. tests that don't care). */\n readonly manifestSync?: ManifestSyncDeps\n}): Promise<PersistSchemaResult> {\n const fresh = await derivePersistedSchema(opts.validator)\n\n // The `_schemas/<collection>` record is shared with the classified-marker\n // writer, so a plain load→save loses whichever writer put first (#583). Read\n // the version at load time and CAS on it; on conflict re-read (picking up the\n // other writer's field), re-evaluate, and retry.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n\n if (stored && isEquivalent(stored, fresh)) {\n return { written: false, skipped: true, envelope: stored, decision: { action: 'allow' } }\n }\n\n // Changed (or first registration). Compute the delta whenever we have a\n // comparable JSON-Schema baseline — independent of whether strategies\n // were registered (#946: the rename pairing it carries feeds the\n // fieldIds id-carry below even on a bare re-declare with no\n // `schemaUpdate` strategies configured). Strategies only run when\n // registered.\n let decision: UpdateDecision = { action: 'allow' }\n const strategies = opts.strategies ?? []\n let delta: SchemaDelta | undefined\n if (\n stored &&\n stored.kind === fresh.kind &&\n isPlainObject(stored.jsonSchema) &&\n isPlainObject(fresh.jsonSchema)\n ) {\n delta = computeSchemaDelta(stored.jsonSchema, fresh.jsonSchema, opts.collectionName)\n if (strategies.length > 0) {\n decision = await evaluateStrategies(delta, strategies, { collection: opts.collectionName })\n }\n }\n\n if (decision.action !== 'allow') {\n // reject (or cutover): do NOT overwrite the baseline — the\n // old schema stays the source of truth until the change is resolved.\n return { written: false, skipped: false, envelope: stored ?? fresh, decision }\n }\n\n // Preserve a previously-persisted classified marker (C-A / R10) and\n // satellite pairing marker (R-S9) — the schema derivation knows nothing\n // about them, so a naive overwrite here would drop the config-drift\n // guards' cross-session signals.\n let toSave: PersistedSchemaEnvelope = fresh\n if (stored?.classified !== undefined) toSave = { ...toSave, classified: stored.classified }\n if (stored?.satellite !== undefined) toSave = { ...toSave, satellite: stored.satellite }\n\n // #946: mint/preserve stable per-field ids (by name, carrying a\n // detected rename's id from its old name — see `delta.renamed`) and\n // stamp the vault-wide schema-fence generation this write happened at —\n // binds \"generation N\" to this envelope's content hash\n // (schemaFenceState() + loadPersistedSchema agree).\n const fieldIds = resolveFieldIds(fresh.jsonSchema, stored?.fieldIds, delta?.renamed)\n if (fieldIds !== undefined) toSave = { ...toSave, fieldIds }\n const fence = await loadFence(opts.store, opts.vault)\n toSave = { ...toSave, generation: fence.currentSchemaVersion }\n\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, toSave, version)\n if (toSave.hash !== null) {\n // This is the FIRST non-barrier writer to `_meta/schema-fence` —\n // previously only SchemaFenceController.#setState wrote it, and only\n // sequentially, under the drain barrier. There is no CAS on the fence\n // doc (out of scope to add here), so re-read it immediately before\n // writing rather than reusing the `fence` snapshot captured above\n // (before the potentially-slow `savePersistedSchema` call): spreading\n // a stale snapshot here would roll back `currentSchemaVersion`/\n // `fenceState` if a concurrent cutover (on a different collection)\n // advanced the fence in between — corrupting the vault-wide gate the\n // whole barrier + MigrationRequiredError check depends on. Re-reading\n // narrows (does not eliminate) that window to just this one overlay.\n const freshFence = await loadFence(opts.store, opts.vault)\n await saveFence(opts.store, opts.vault, { ...freshFence, schemaHash: toSave.hash })\n }\n // #941 Task 3: keep the `_manifest/schema` INDEX in sync with the\n // `_schemas/<collection>` write that just landed. Opt-in via\n // `opts.manifestSync` (threaded from vault.ts, which is the only\n // caller with a general-purpose `getDEK` resolver + ledger handle).\n if (opts.manifestSync) {\n await syncSchemaManifest(opts.store, opts.vault, opts.manifestSync)\n }\n return { written: true, skipped: false, envelope: toSave, decision }\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n/**\n * Persist (or refresh) the C-A / R10 classified marker into the collection's\n * `_schemas/<collection>` record, preserving any existing derived JSON-Schema\n * body. Idempotent — a no-op when an equivalent marker is already stored.\n * Independent of `persistJsonSchema`: called on the first classified write so a\n * later naive handle can detect the drift cross-session.\n */\nexport async function persistClassifiedMarker(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly dek: EnclaveKey\n readonly marker: ClassifiedMarker\n}): Promise<void> {\n // Shares the `_schemas/<collection>` record with the JSON-Schema writer;\n // CAS on the loaded version so a concurrent schema (re)registration can't\n // silently drop the marker (and vice-versa) — see #583.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n if (stored?.classified !== undefined && markersEqual(stored.classified, opts.marker)) return\n const payload: PersistedSchemaEnvelope =\n stored !== undefined\n ? { ...stored, classified: opts.marker }\n : {\n _noydb_schema: 1,\n kind: 'Unknown',\n jsonSchema: null,\n hash: null,\n derivedAt: new Date().toISOString(),\n classified: opts.marker,\n }\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, payload, version)\n return\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n/**\n * Persist (or refresh) the R-S9 satellite pairing marker into the collection's\n * `_schemas/<collection>` record, preserving any existing derived JSON-Schema\n * body. Idempotent — a no-op when an equivalent marker is already stored.\n * Independent of `persistJsonSchema`: called on satellite declaration so a\n * later divergent re-declaration can be detected cross-session.\n */\nexport async function persistSatelliteMarker(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly dek: EnclaveKey\n readonly marker: PairingMarker\n}): Promise<void> {\n // Shares the `_schemas/<collection>` record with the JSON-Schema writer;\n // CAS on the loaded version so a concurrent schema (re)registration can't\n // silently drop the marker (and vice-versa) — see #583.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n if (stored?.satellite !== undefined && satelliteMarkersEqual(stored.satellite, opts.marker)) return\n const payload: PersistedSchemaEnvelope =\n stored !== undefined\n ? { ...stored, satellite: opts.marker }\n : {\n _noydb_schema: 1,\n kind: 'Unknown',\n jsonSchema: null,\n hash: null,\n derivedAt: new Date().toISOString(),\n satellite: opts.marker,\n }\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, payload, version)\n return\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n// #597: both equality checks below deliberately do NOT compare `epoch`. A\n// live collection re-opening itself computes a fresh epoch candidate on\n// every declare (marker.ts / config-drift.ts) that must still be treated as\n// \"the same marker\" so the no-op fast path fires and the already-persisted\n// epoch is left untouched — that's what makes epoch stable across re-opens.\n// Comparing epoch here would break that fast path today, with no\n// corresponding benefit: there is no delete-collection API yet, so a\n// mismatched epoch can't (yet) mean \"this name was reused for a genuinely\n// new collection.\" Wiring an epoch-mismatch REJECTION is a deferred\n// follow-up for whenever collection deletion ships.\n\nfunction satelliteMarkersEqual(a: PairingMarker, b: PairingMarker): boolean {\n return a.base === b.base && a.fieldsHash === b.fieldsHash && (a.joined ?? null) === (b.joined ?? null)\n}\n\nfunction markersEqual(a: ClassifiedMarker, b: ClassifiedMarker): boolean {\n return sameSet(a.digestOnly, b.digestOnly) && sameSet(a.equatable, b.equatable)\n}\n\nfunction sameSet(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false\n const s = new Set(a)\n return b.every((x) => s.has(x))\n}\n\nfunction isPlainObject(v: unknown): v is object {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\nfunction isEquivalent(a: PersistedSchemaEnvelope, b: PersistedSchemaEnvelope): boolean {\n if (a.kind !== b.kind) return false\n // Zod path: real hashes — compare directly.\n if (a.hash && b.hash) return a.hash === b.hash\n // Stub path: both have hash=null. Kind equality is the only signal we have.\n if (a.hash === null && b.hash === null) return true\n // Mixed (one has a hash, the other doesn't) — treat as changed.\n return false\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/with-shape/persisted-schemas/field-ids.ts","../src/with-shape/manifest/writer.ts","../src/with-shape/manifest/sync.ts","../src/with-shape/persisted-schemas/register.ts"],"sourcesContent":["/**\n * Stable field-identifier minting (#946).\n *\n * A field's `id` is opaque and permanent — minted once, from randomness\n * only, with no timestamp and no name-derivation. A name-derived id would\n * change when the field is renamed, defeating the whole point (identity\n * must survive a rename). See `PersistedSchemaEnvelope.fieldIds`.\n *\n * @module\n */\n\nconst BASE32URL_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567'\n\n/** Unpadded RFC 4648 §7 base32url encoding of an arbitrary byte string. */\nfunction encodeBase32Url(bytes: Uint8Array): string {\n let bits = 0\n let value = 0\n let out = ''\n for (const byte of bytes) {\n value = (value << 8) | byte\n bits += 8\n while (bits >= 5) {\n out += BASE32URL_ALPHABET[(value >>> (bits - 5)) & 31]\n bits -= 5\n }\n }\n if (bits > 0) {\n out += BASE32URL_ALPHABET[(value << (5 - bits)) & 31]\n }\n return out\n}\n\n/**\n * Mint a fresh opaque field id: 12 random bytes (96 bits) from\n * `crypto.getRandomValues`, encoded as a 20-character base32url token.\n * Not exported — Task 2/3 (#946) may need to export this later; keep it\n * module-internal until an external consumer exists.\n */\nfunction mintFieldId(): string {\n const bytes = new Uint8Array(12)\n crypto.getRandomValues(bytes)\n return encodeBase32Url(bytes)\n}\n\n/** Top-level property names of a Draft 2020-12 object JSON Schema, or `[]` when not an object schema. */\nfunction topLevelFieldNames(jsonSchema: object | null): readonly string[] {\n if (jsonSchema === null) return []\n const props = (jsonSchema as { properties?: unknown }).properties\n if (props === null || typeof props !== 'object') return []\n return Object.keys(props as Record<string, unknown>)\n}\n\n/**\n * Resolve the `fieldIds` map for a freshly-derived schema against the\n * previously-stored envelope's map (if any). Existing ids are preserved by\n * name — a field keeps its id across re-derivation as long as its name is\n * unchanged; a genuinely new field name mints a fresh id. Returns\n * `undefined` when the fresh schema has no derivable top-level field set\n * (e.g. a stub envelope, or a non-object root schema).\n *\n * `renamed` (#946 Task 2) — unambiguous rename pairs from\n * `computeSchemaDelta`'s `SchemaDelta.renamed`. When the fresh name is a\n * rename target (`to`), the id is looked up under its OLD name (`from`)\n * instead of the fresh name itself — the whole point of a stable field id\n * is that it survives exactly this case; without it, a rename would mint a\n * fresh id for the new name and silently drop the old one.\n */\nexport function resolveFieldIds(\n freshJsonSchema: object | null,\n priorFieldIds: Record<string, string> | undefined,\n renamed?: readonly { readonly from: string; readonly to: string }[],\n): Record<string, string> | undefined {\n const names = topLevelFieldNames(freshJsonSchema)\n if (names.length === 0) return undefined\n const priorNameFor = new Map<string, string>()\n if (renamed) for (const { from, to } of renamed) priorNameFor.set(to, from)\n const out: Record<string, string> = {}\n for (const name of names) {\n const priorName = priorNameFor.get(name) ?? name\n out[name] = priorFieldIds?.[priorName] ?? mintFieldId()\n }\n return out\n}\n","/**\n * Strict-CAS schema-manifest writer (#941, AC #1).\n *\n * Every other reserved-collection writer in the hub (`persisted-schemas`'s\n * `savePersistedSchema` callers, the classified/satellite marker writers)\n * retries on a CAS conflict: re-read, re-apply, try again. The schema\n * manifest deliberately does NOT — two concurrent *direct* edits to the\n * manifest must be refused and surfaced, never silently merged or\n * retry-resolved, because a manifest write encodes a specific point-in-time\n * claim about the pod's schema generation/content-hash set. A caller that\n * lost the race must re-derive against the fresh state and decide what to\n * do, not have its stale write quietly re-applied on top of someone else's.\n *\n * Ledger audit: this writer does not have a `LedgerStore` handle at this\n * seam (mirroring `persisted-schemas`, whose `_schemas/<collection>` writes\n * are likewise not ledger-audited today). Wiring `LedgerStore.append({ op:\n * 'migration', ... })` around a manifest write belongs at the call site\n * where a ledger handle is already in scope (the open/write integration —\n * #941 Task 3/4), not here.\n *\n * @module\n */\n\nimport { isConflictError, ManifestConflictError } from '../../kernel/errors.js'\nimport { saveSchemaManifest, type GetManifestDEK } from './storage.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { SchemaManifest } from './types.js'\n\n/**\n * Write the schema manifest as a strict CAS: `expectedVersion` must match\n * the stored envelope's `_v` (0 when no manifest exists yet). On a\n * conflict, throws {@link ManifestConflictError} — this function never\n * retries.\n */\nexport async function writeSchemaManifest(\n store: NoydbStore,\n vault: string,\n manifest: SchemaManifest,\n expectedVersion: number,\n getDEK: GetManifestDEK,\n): Promise<void> {\n try {\n await saveSchemaManifest(store, vault, manifest, expectedVersion, getDEK)\n } catch (err) {\n if (isConflictError(err)) {\n throw new ManifestConflictError(err.version, expectedVersion)\n }\n throw err\n }\n}\n","/**\n * Keep the persisted `_manifest/schema` record in sync with the\n * per-collection `_schemas/<collection>` source of truth (#941 Task 3).\n *\n * Called from `persisted-schemas/register.ts`'s `persistSchemaIfNeeded`\n * right after a `_schemas/<collection>` write succeeds.\n *\n * ## Reconciling strict-CAS-refuse with the derived nature of the manifest\n *\n * `writer.ts`'s `writeSchemaManifest` is deliberately strict-CAS: a\n * concurrent *direct* edit to the manifest is refused, never retried (AC\n * #1). But this call site is not a direct edit — it is a re-derivation\n * from the source of truth, triggered as a side effect of a `_schemas`\n * write that has ALREADY succeeded. When N collections are declared\n * together (one `openVault()` call registering several `persistJsonSchema:\n * true` collections), their `persistSchemaIfNeeded` calls run CONCURRENTLY\n * — each one's own `_schemas/<collection>` write lands independently, and\n * each then calls this function. A single derive-then-write with no\n * feedback loop is NOT enough here: a call whose `deriveSchemaManifest`\n * snapshot ran before a sibling's `_schemas` write landed would persist a\n * manifest that's missing that sibling forever (nothing re-triggers it) —\n * this was a REAL bug (not a test-isolation artifact — reproduced with 0\n * shared state, purely from concurrent `_schemas` writes racing the derive\n * snapshot; see #941 flake fix commit). `syncSchemaManifest` therefore\n * loops (bounded by {@link MAX_SYNC_ATTEMPTS}):\n *\n * 1. Derive fresh, compare to what's persisted. Already converged → done.\n * 2. Write via strict-CAS. `ManifestConflictError` (another writer moved\n * the manifest first) → loop: re-derive against the NEW state and\n * retry, exactly like a normal optimistic-CAS retry.\n * 3. On a successful write, derive ONE more time. If a sibling's\n * `_schemas` write landed while we were deriving/writing, the state\n * has already moved past what we just wrote — loop again to catch it\n * up. If the re-derive matches what we wrote, we're the last writer\n * and the manifest is provably converged: return.\n *\n * This guarantees that whichever collection's `_schemas` write is the LAST\n * to land (in real wall-clock order — there is always exactly one, even\n * though no single caller knows which) ends up running a sync whose own\n * derive sees every sibling's write already committed, and whose\n * recheck-after-write confirms nothing moved further — so it converges the\n * manifest deterministically, regardless of interleaving. If attempts are\n * exhausted under pathological contention, the loop gives up — with a\n * `console.warn` (see cap-exhaustion note below), not silently — leaving\n * the last successful write in place; the manifest is a derivable cache\n * (not a correctness invariant), so a next schema write or `open()`'s\n * re-derive still converges it, matching how `persistSchemaIfNeeded`\n * already treats its own write failures.\n * Any error OTHER than `ManifestConflictError` (store failure, DEK\n * resolution failure, etc.) is NOT swallowed — it propagates to the\n * caller immediately, matching how `persistSchemaIfNeeded` already treats\n * its own write failures as a visible (not silently discarded) fingerprint\n * failure.\n *\n * ## CRITICAL fix (#941 review): a scoped principal must never write a\n * partial manifest\n *\n * `deriveSchemaManifest` returns `undecodableCollections` for any\n * `_schemas/<collection>` entry this principal's `lookupDEK` couldn't\n * resolve — e.g. a member `grant()`-scoped to one collection can decrypt\n * `_manifest` itself (system-prefixed DEKs propagate to every role) but\n * NOT a sibling data collection's `_schemas/<sibling>` entry. Before this\n * fix, `deriveSchemaManifest` silently treated \"can't decrypt\" the same as\n * \"no derivable content\" and omitted the sibling — so a scoped member's\n * OWN schema declare would derive a manifest missing every collection they\n * can't see, load the real (complete) persisted manifest as a valid CAS\n * token, observe a hash mismatch, and OVERWRITE it — permanently dropping\n * the siblings, silently, ledger-audited as a legitimate migration. The\n * converging recheck loop above made this WORSE, not better: re-deriving\n * from the same restricted view just re-confirms the same partial result\n * and declares it \"stable.\"\n *\n * Fix: if `undecodableCollections` is non-empty, `syncSchemaManifest`\n * `SKIP`s entirely — no read-compare, no write, no audit, no loop. It does\n * NOT try to merge-preserve the undecodable entries from what's currently\n * persisted: an `aggregateHash` computed over entries this principal can't\n * verify would be a hash asserting something it never actually checked,\n * which is worse than not writing at all. This is safe because:\n *\n * - The source of truth, `_schemas/<their-collection>`, was ALREADY\n * updated by their declare — nothing about their own change is lost.\n * - The pod-wide `_manifest` cache simply isn't rewritten by a principal\n * who'd corrupt it by omission — it stays whatever a full-visibility\n * principal (owner/admin/custodian) last wrote.\n * - `open()` re-derives from `_schemas` directly (not from the cached\n * manifest) for whoever opens it, so a stale cache is never load-bearing.\n * - The NEXT full-visibility principal's declare (or any subsequent sync\n * by someone with complete access) naturally refreshes the cache,\n * including the scoped member's own just-landed change.\n *\n * ## Ledger audit (AC #5)\n *\n * Schema-generation transitions are not ledger-audited anywhere else today\n * (neither the `_schemas/<collection>` write in `register.ts` nor the fence\n * bump in `schema-update/fence-controller.ts#runCutover`). This is the\n * first ledger-audited signal for a schema change: every manifest write\n * that actually happens (i.e. every time the derived manifest differs from\n * what's stored) appends an `op: 'migration'` entry via the optional\n * `getLedgerOrNull` callback — `null`/absent when the history strategy\n * isn't opted in, matching every other optional-ledger call site\n * (`vault._getLedgerOrNull()?.append(...)`, see `liberate.ts`,\n * `with-audit/periods/vault-facade.ts`). No audit fires on the no-op path\n * (derived manifest unchanged), on a swallowed conflict (the winning\n * writer's own sync already audits its write), or on the scoped-principal\n * SKIP path above (nothing was written).\n *\n * @module\n */\n\nimport type { LedgerStore } from '../../with-commit/history/ledger/store.js'\nimport { envelopePayloadHash } from '../../with-commit/history/ledger/hash.js'\nimport type { NoydbStore } from '../../kernel/types.js'\nimport { ManifestConflictError } from '../../kernel/errors.js'\nimport { deriveSchemaManifest, type LookupDEK } from './derive.js'\nimport { loadSchemaManifestEntry, type GetManifestDEK } from './storage.js'\nimport { writeSchemaManifest } from './writer.js'\nimport { MANIFEST_COLLECTION } from './reserved-collections.js'\nimport { MANIFEST_SCHEMA_RECORD_ID, type SchemaManifest } from './types.js'\n\nexport interface ManifestSyncDeps {\n /** Resolver for `_manifest`'s OWN DEK — mints if absent (this principal's declare is about to write it). */\n readonly getDEK: GetManifestDEK\n /**\n * NON-MINTING lookup for every OTHER (sibling data) collection's DEK,\n * used by `deriveSchemaManifest` to build the index — see `derive.ts`'s\n * `LookupDEK` doc for why minting here would be actively harmful.\n */\n readonly lookupDEK: LookupDEK\n /** Optional ledger handle — absent when the history strategy isn't opted in (no-op audit). */\n readonly getLedgerOrNull?: () => LedgerStore | null\n}\n\n/**\n * Bound on the derive→write→recheck loop (see module doc). Headroom above\n * what any realistic `openVault()` batch (a handful of collections) needs —\n * a 40-way concurrent-declare stress repro converged within single digits.\n */\nconst MAX_SYNC_ATTEMPTS = 16\n\n/**\n * Re-derive the schema manifest and write it if it changed, looping until\n * the write is provably stable against concurrent sibling `_schemas`\n * writes (see module doc). SKIPS entirely (no write, no audit) when this\n * principal's view is incomplete (`undecodableCollections` non-empty) —\n * see the module doc's CRITICAL fix section. Swallows a\n * {@link ManifestConflictError} by retrying against the fresher state; any\n * other error propagates. Ledger-audits (`op: 'migration'`) once per\n * successful write.\n */\nexport async function syncSchemaManifest(\n store: NoydbStore,\n vault: string,\n deps: ManifestSyncDeps,\n): Promise<void> {\n for (let attempt = 0; attempt < MAX_SYNC_ATTEMPTS; attempt++) {\n const { manifest: derived, undecodableCollections } = await deriveSchemaManifest(store, vault, deps.lookupDEK)\n if (undecodableCollections.length > 0) {\n // Scoped principal — see module doc's CRITICAL fix. Never write a\n // manifest we know is missing collections we couldn't decrypt.\n return\n }\n\n const existing = await loadSchemaManifestEntry(store, vault, deps.getDEK)\n\n if (\n existing &&\n existing.manifest.generation === derived.generation &&\n existing.manifest.aggregateHash === derived.aggregateHash\n ) {\n return // already converged — no write, no audit\n }\n\n const expectedVersion = existing?.version ?? 0\n try {\n await writeSchemaManifest(store, vault, derived, expectedVersion, deps.getDEK)\n } catch (err) {\n if (err instanceof ManifestConflictError) continue // another writer moved it first — re-derive + retry\n throw err\n }\n\n await auditManifestWrite(store, vault, deps, derived, expectedVersion)\n\n // A sibling's `_schemas` write can land while WE were deriving/writing\n // (#941 flake fix — see module doc). Re-derive once more: if the truth\n // has already moved past what we just wrote, loop again to catch it up\n // instead of leaving a permanently-partial manifest. (A principal with\n // full visibility at the top of this iteration cannot lose that\n // visibility mid-call — the keyring doesn't change during one sync —\n // so this recheck is never itself subject to the undecodable-skip; the\n // `for` loop's own guard covers it defensively regardless.)\n const recheck = await deriveSchemaManifest(store, vault, deps.lookupDEK)\n if (recheck.undecodableCollections.length > 0) return // defensive — see comment above\n if (recheck.manifest.generation === derived.generation && recheck.manifest.aggregateHash === derived.aggregateHash) {\n return // stable — we were the last writer\n }\n }\n // Attempts exhausted under pathological contention — see module doc:\n // the manifest is a derivable cache, not a correctness invariant, so a\n // later schema write or open()'s re-derive still converges it. Warn\n // rather than fail silently — a silent give-up on a completeness\n // invariant is exactly what hid the original concurrent-declare race.\n console.warn(\n `[noy-db] schema-manifest sync did not converge after ${MAX_SYNC_ATTEMPTS} attempts; ` +\n 'the persisted manifest may be stale until the next schema write or open()\\'s re-derive.',\n )\n}\n\nasync function auditManifestWrite(\n store: NoydbStore,\n vault: string,\n deps: ManifestSyncDeps,\n derived: SchemaManifest,\n expectedVersion: number,\n): Promise<void> {\n const ledger = deps.getLedgerOrNull?.()\n if (!ledger) return\n // `_manifest/schema` has a real, individually-addressable envelope (unlike\n // a `lifecycle` entry's empty collection/id) — `verifyBackupIntegrity`'s\n // data cross-check (`with-pod/backup.ts`) treats any non-amendment,\n // non-lifecycle entry's `collection`/`id` as \"the latest write to this\n // record\" and recomputes+compares its ciphertext hash, so `payloadHash`\n // must be the real `envelopePayloadHash` of the just-written envelope\n // (ciphertext domain), NOT `derived.aggregateHash` (plaintext domain) —\n // using the wrong domain here trips a false-positive tamper failure on\n // restore.\n const envelope = await store.get(vault, MANIFEST_COLLECTION, MANIFEST_SCHEMA_RECORD_ID)\n await ledger.append({\n op: 'migration',\n collection: MANIFEST_COLLECTION,\n id: MANIFEST_SCHEMA_RECORD_ID,\n version: expectedVersion + 1,\n actor: '',\n payloadHash: await envelopePayloadHash(envelope),\n reason: `schema-manifest-sync:generation=${derived.generation}`,\n })\n}\n","/**\n * Orchestrate the derive → hash → skip-or-write cycle for a collection's\n * persisted JSON Schema. Called by the Vault at collection-registration\n * time when the developer opts in via `collection({ persistJsonSchema:\n * true })`.\n *\n * Skip semantics:\n *\n * - Zod validators: skip when the new hash equals the stored hash.\n * - Non-Zod (stub envelopes have hash=null): skip when the stored\n * envelope's `kind` matches the freshly-detected kind (since there's\n * no body to compare yet — a kind change is the only signal).\n *\n * @module\n */\n\nimport { derivePersistedSchema } from './derive.js'\nimport { loadPersistedSchemaEntry, savePersistedSchema } from './storage.js'\nimport { resolveFieldIds } from './field-ids.js'\nimport { computeSchemaDelta } from '../schema-update/delta.js'\nimport { evaluateStrategies } from '../schema-update/dispatch.js'\nimport { loadFence, saveFence } from '../schema-update/fence.js'\nimport { isConflictError } from '../../kernel/errors.js'\nimport { syncSchemaManifest, type ManifestSyncDeps } from '../manifest/sync.js'\nimport type { SchemaUpdateStrategy, UpdateDecision, SchemaDelta } from '../schema-update/types.js'\nimport type { NoydbStore, ClassifiedMarker } from '../../kernel/types.js'\nimport type { PersistedSchemaEnvelope } from './types.js'\nimport type { PairingMarker } from '../satellites/types.js'\nimport type { EnclaveKey } from '../../capsule/index.js'\n\n/**\n * Max optimistic-CAS retries for the shared `_schemas/<collection>` record\n * (#583). Only two writers ever contend (the JSON-Schema writer and the\n * classified-marker writer), so one retry suffices; the headroom covers a\n * pathological re-order. Exhausting it rethrows the {@link ConflictError}\n * rather than silently losing a write.\n */\nconst MAX_SCHEMA_CAS_RETRIES = 5\n\nexport interface PersistSchemaResult {\n /** True when a fresh envelope was written to storage. */\n readonly written: boolean\n /** True when an existing envelope matched and the write was skipped. */\n readonly skipped: boolean\n /** The envelope that was either written or matched. */\n readonly envelope: PersistedSchemaEnvelope\n /** The update-strategy decision, present when strategies ran. */\n readonly decision?: UpdateDecision\n}\n\nexport async function persistSchemaIfNeeded(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly validator: unknown\n readonly dek: EnclaveKey\n readonly strategies?: readonly SchemaUpdateStrategy[]\n /** #941 Task 3: re-derive + sync the `_manifest/schema` INDEX after a write. Omitted → no sync (e.g. tests that don't care). */\n readonly manifestSync?: ManifestSyncDeps\n}): Promise<PersistSchemaResult> {\n const fresh = await derivePersistedSchema(opts.validator)\n\n // The `_schemas/<collection>` record is shared with the classified-marker\n // writer, so a plain load→save loses whichever writer put first (#583). Read\n // the version at load time and CAS on it; on conflict re-read (picking up the\n // other writer's field), re-evaluate, and retry.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n\n if (stored && isEquivalent(stored, fresh)) {\n return { written: false, skipped: true, envelope: stored, decision: { action: 'allow' } }\n }\n\n // Changed (or first registration). Compute the delta whenever we have a\n // comparable JSON-Schema baseline — independent of whether strategies\n // were registered (#946: the rename pairing it carries feeds the\n // fieldIds id-carry below even on a bare re-declare with no\n // `schemaUpdate` strategies configured). Strategies only run when\n // registered.\n let decision: UpdateDecision = { action: 'allow' }\n const strategies = opts.strategies ?? []\n let delta: SchemaDelta | undefined\n if (\n stored &&\n stored.kind === fresh.kind &&\n isPlainObject(stored.jsonSchema) &&\n isPlainObject(fresh.jsonSchema)\n ) {\n delta = computeSchemaDelta(stored.jsonSchema, fresh.jsonSchema, opts.collectionName)\n if (strategies.length > 0) {\n decision = await evaluateStrategies(delta, strategies, { collection: opts.collectionName })\n }\n }\n\n if (decision.action !== 'allow') {\n // reject (or cutover): do NOT overwrite the baseline — the\n // old schema stays the source of truth until the change is resolved.\n return { written: false, skipped: false, envelope: stored ?? fresh, decision }\n }\n\n // Preserve a previously-persisted classified marker (C-A / R10) and\n // satellite pairing marker (R-S9) — the schema derivation knows nothing\n // about them, so a naive overwrite here would drop the config-drift\n // guards' cross-session signals.\n let toSave: PersistedSchemaEnvelope = fresh\n if (stored?.classified !== undefined) toSave = { ...toSave, classified: stored.classified }\n if (stored?.satellite !== undefined) toSave = { ...toSave, satellite: stored.satellite }\n\n // #946: mint/preserve stable per-field ids (by name, carrying a\n // detected rename's id from its old name — see `delta.renamed`) and\n // stamp the vault-wide schema-fence generation this write happened at —\n // binds \"generation N\" to this envelope's content hash\n // (schemaFenceState() + loadPersistedSchema agree).\n const fieldIds = resolveFieldIds(fresh.jsonSchema, stored?.fieldIds, delta?.renamed)\n if (fieldIds !== undefined) toSave = { ...toSave, fieldIds }\n const fence = await loadFence(opts.store, opts.vault)\n toSave = { ...toSave, generation: fence.currentSchemaVersion }\n\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, toSave, version)\n if (toSave.hash !== null) {\n // This is the FIRST non-barrier writer to `_meta/schema-fence` —\n // previously only SchemaFenceController.#setState wrote it, and only\n // sequentially, under the drain barrier. There is no CAS on the fence\n // doc (out of scope to add here), so re-read it immediately before\n // writing rather than reusing the `fence` snapshot captured above\n // (before the potentially-slow `savePersistedSchema` call): spreading\n // a stale snapshot here would roll back `currentSchemaVersion`/\n // `fenceState` if a concurrent cutover (on a different collection)\n // advanced the fence in between — corrupting the vault-wide gate the\n // whole barrier + MigrationRequiredError check depends on. Re-reading\n // narrows (does not eliminate) that window to just this one overlay.\n const freshFence = await loadFence(opts.store, opts.vault)\n await saveFence(opts.store, opts.vault, { ...freshFence, schemaHash: toSave.hash })\n }\n // #941 Task 3: keep the `_manifest/schema` INDEX in sync with the\n // `_schemas/<collection>` write that just landed. Opt-in via\n // `opts.manifestSync` (threaded from vault.ts, which is the only\n // caller with a general-purpose `getDEK` resolver + ledger handle).\n if (opts.manifestSync) {\n await syncSchemaManifest(opts.store, opts.vault, opts.manifestSync)\n }\n return { written: true, skipped: false, envelope: toSave, decision }\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n/**\n * Persist (or refresh) the C-A / R10 classified marker into the collection's\n * `_schemas/<collection>` record, preserving any existing derived JSON-Schema\n * body. Idempotent — a no-op when an equivalent marker is already stored.\n * Independent of `persistJsonSchema`: called on the first classified write so a\n * later naive handle can detect the drift cross-session.\n */\nexport async function persistClassifiedMarker(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly dek: EnclaveKey\n readonly marker: ClassifiedMarker\n}): Promise<void> {\n // Shares the `_schemas/<collection>` record with the JSON-Schema writer;\n // CAS on the loaded version so a concurrent schema (re)registration can't\n // silently drop the marker (and vice-versa) — see #583.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n if (stored?.classified !== undefined && markersEqual(stored.classified, opts.marker)) return\n const payload: PersistedSchemaEnvelope =\n stored !== undefined\n ? { ...stored, classified: opts.marker }\n : {\n _noydb_schema: 1,\n kind: 'Unknown',\n jsonSchema: null,\n hash: null,\n derivedAt: new Date().toISOString(),\n classified: opts.marker,\n }\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, payload, version)\n return\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n/**\n * Persist (or refresh) the R-S9 satellite pairing marker into the collection's\n * `_schemas/<collection>` record, preserving any existing derived JSON-Schema\n * body. Idempotent — a no-op when an equivalent marker is already stored.\n * Independent of `persistJsonSchema`: called on satellite declaration so a\n * later divergent re-declaration can be detected cross-session.\n */\nexport async function persistSatelliteMarker(opts: {\n readonly store: NoydbStore\n readonly vault: string\n readonly collectionName: string\n readonly dek: EnclaveKey\n readonly marker: PairingMarker\n}): Promise<void> {\n // Shares the `_schemas/<collection>` record with the JSON-Schema writer;\n // CAS on the loaded version so a concurrent schema (re)registration can't\n // silently drop the marker (and vice-versa) — see #583.\n for (let attempt = 0; ; attempt++) {\n const { version, payload: stored } = await loadPersistedSchemaEntry(\n opts.store, opts.vault, opts.collectionName, opts.dek,\n )\n if (stored?.satellite !== undefined && satelliteMarkersEqual(stored.satellite, opts.marker)) return\n const payload: PersistedSchemaEnvelope =\n stored !== undefined\n ? { ...stored, satellite: opts.marker }\n : {\n _noydb_schema: 1,\n kind: 'Unknown',\n jsonSchema: null,\n hash: null,\n derivedAt: new Date().toISOString(),\n satellite: opts.marker,\n }\n try {\n await savePersistedSchema(opts.store, opts.vault, opts.collectionName, opts.dek, payload, version)\n return\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_SCHEMA_CAS_RETRIES) continue\n throw err\n }\n }\n}\n\n// #597: both equality checks below deliberately do NOT compare `epoch`. A\n// live collection re-opening itself computes a fresh epoch candidate on\n// every declare (marker.ts / config-drift.ts) that must still be treated as\n// \"the same marker\" so the no-op fast path fires and the already-persisted\n// epoch is left untouched — that's what makes epoch stable across re-opens.\n// Comparing epoch here would break that fast path today, with no\n// corresponding benefit: there is no delete-collection API yet, so a\n// mismatched epoch can't (yet) mean \"this name was reused for a genuinely\n// new collection.\" Wiring an epoch-mismatch REJECTION is a deferred\n// follow-up for whenever collection deletion ships.\n\nfunction satelliteMarkersEqual(a: PairingMarker, b: PairingMarker): boolean {\n return a.base === b.base && a.fieldsHash === b.fieldsHash && (a.joined ?? null) === (b.joined ?? null)\n}\n\nfunction markersEqual(a: ClassifiedMarker, b: ClassifiedMarker): boolean {\n return sameSet(a.digestOnly, b.digestOnly) && sameSet(a.equatable, b.equatable)\n}\n\nfunction sameSet(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false\n const s = new Set(a)\n return b.every((x) => s.has(x))\n}\n\nfunction isPlainObject(v: unknown): v is object {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\nfunction isEquivalent(a: PersistedSchemaEnvelope, b: PersistedSchemaEnvelope): boolean {\n if (a.kind !== b.kind) return false\n // Zod path: real hashes — compare directly.\n if (a.hash && b.hash) return a.hash === b.hash\n // Stub path: both have hash=null. Kind equality is the only signal we have.\n if (a.hash === null && b.hash === null) return true\n // Mixed (one has a hash, the other doesn't) — treat as changed.\n return false\n}\n"],"mappings":"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","names":[]}
@@ -1,20 +1,16 @@
1
1
  import {
2
2
  linkCollectionName,
3
3
  linkRowKey
4
- } from "./chunk-QIZPZMUI.js";
4
+ } from "./chunk-Q4UHJ5SN.js";
5
5
  import {
6
6
  buildRecordAad,
7
+ buildRecordEnvelope,
8
+ encrypt,
7
9
  openEnvelopeJson
8
- } from "./chunk-4ZS56W5S.js";
9
- import {
10
- buildRecordEnvelope
11
- } from "./chunk-SZJ3DLZA.js";
12
- import {
13
- encrypt
14
- } from "./chunk-HPVZCW43.js";
10
+ } from "./chunk-EZGV6F47.js";
15
11
  import {
16
12
  NoydbError
17
- } from "./chunk-7O3Y4PPD.js";
13
+ } from "./chunk-37EPTBBR.js";
18
14
 
19
15
  // src/with-shape/links/link-set.ts
20
16
  var LinkSet = class {
@@ -55,7 +51,7 @@ var LinkSet = class {
55
51
  return buildRecordEnvelope(identity, { iv, data });
56
52
  }
57
53
  async decryptEntry(key, env) {
58
- const json = this.encrypted ? await openEnvelopeJson({ collection: this.collName, id: key }, env, await this.dek()) : env._data;
54
+ const json = this.encrypted ? await openEnvelopeJson({ collection: this.collName, id: key }, env, await this.dek()) : env._data ?? "";
59
55
  return JSON.parse(json);
60
56
  }
61
57
  async connect(aId, bId, meta) {
@@ -130,4 +126,4 @@ export {
130
126
  LinkSet,
131
127
  LinkEndpointError
132
128
  };
133
- //# sourceMappingURL=chunk-OTQZ7ZWU.js.map
129
+ //# sourceMappingURL=chunk-WXE4W2U6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/with-shape/links/link-set.ts"],"sourcesContent":["/**\n * `_links_*` reserved collections — managed bidirectional many-to-many\n * junctions (design B).\n *\n * Where `refArray` (design A) stores an id-array on one owning record,\n * `vault.link()` creates a first-class junction: a dedicated encrypted\n * `_links_<name>` collection whose rows are `{ a, b, meta? }` link tuples.\n * It is queryable from BOTH sides (`of(id)`), carries optional per-link\n * metadata, and cascades on endpoint delete.\n *\n * Each link is slot-typed: `a` is an id in the `a` endpoint collection,\n * `b` an id in the `b` endpoint collection (they may be the same\n * collection for self-links). A link's identity is the ordered pair\n * `(a, b)`; `of(id)` matches either slot.\n *\n * Rows are encrypted under a dedicated `_links_<name>` DEK — same\n * zero-knowledge stack as every other collection (the store sees only\n * ciphertext). Backup/restore rides the normal `loadAll` path.\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport type { NoydbEventEmitter } from '../../kernel/events.js'\nimport { buildRecordAad, buildRecordEnvelope, encrypt, openEnvelopeJson, type EnclaveKey } from '../../capsule/index.js'\nimport { NoydbError } from '../../kernel/errors.js'\n\n// Naming helpers, declaration types, and LinkIntegrityError live in\n// `names.ts` (the always-loadable slice — #553); re-exported here so\n// existing import paths and the root barrel are unchanged.\nexport {\n LINK_COLLECTION_PREFIX,\n linkCollectionName,\n isLinkCollectionName,\n linkRowKey,\n LinkIntegrityError,\n} from './names.js'\nexport type { LinkOnDelete, LinkSpec, LinkRow, LinkSetHandle } from './names.js'\nimport { linkCollectionName, linkRowKey } from './names.js'\nimport type { LinkSpec, LinkRow, LinkSetHandle } from './names.js'\n\n/** Stored form (also the row key derivation source). */\ninterface LinkEntry {\n a: string\n b: string\n meta?: Record<string, unknown>\n}\n\n/**\n * @internal — the concrete handle. The Vault owns construction (one per\n * link name) and the cascade hooks; consumers use the {@link LinkSetHandle}\n * surface via `vault.links(name)`.\n */\nexport class LinkSet implements LinkSetHandle {\n private readonly collName: string\n private dekPromise: Promise<EnclaveKey> | null = null\n\n constructor(\n private readonly adapter: NoydbStore,\n private readonly vault: string,\n private readonly name: string,\n private readonly spec: LinkSpec,\n private readonly encrypted: boolean,\n private readonly getDEK: (collectionName: string) => Promise<EnclaveKey>,\n private readonly actor: string,\n private readonly emitter: NoydbEventEmitter,\n /** Vault-provided existence check for endpoint validation on connect(). */\n private readonly endpointExists: (collection: string, id: string) => Promise<boolean>,\n ) {\n this.collName = linkCollectionName(name)\n }\n\n private dek(): Promise<EnclaveKey> {\n if (!this.dekPromise) this.dekPromise = this.getDEK(this.collName)\n return this.dekPromise\n }\n\n private async encryptEntry(entry: LinkEntry, version: number, key: string): Promise<EncryptedEnvelope> {\n const json = JSON.stringify(entry)\n const identity = { collection: this.collName, id: key, by: this.actor, version }\n if (!this.encrypted) {\n return buildRecordEnvelope(identity, { iv: '', data: json})\n }\n const { iv, data } = await encrypt(json, await this.dek(), buildRecordAad(identity))\n return buildRecordEnvelope(identity, { iv, data})\n }\n\n private async decryptEntry(key: string, env: EncryptedEnvelope): Promise<LinkEntry> {\n const json = this.encrypted ? await openEnvelopeJson({ collection: this.collName, id: key }, env, await this.dek()) : (env._data ?? '')\n return JSON.parse(json) as LinkEntry\n }\n\n async connect(aId: string, bId: string, meta?: Record<string, unknown>): Promise<void> {\n if (!(await this.endpointExists(this.spec.a, aId))) {\n throw new LinkEndpointError(this.name, this.spec.a, aId)\n }\n if (!(await this.endpointExists(this.spec.b, bId))) {\n throw new LinkEndpointError(this.name, this.spec.b, bId)\n }\n const key = linkRowKey(aId, bId)\n const entry: LinkEntry = meta !== undefined ? { a: aId, b: bId, meta } : { a: aId, b: bId }\n const existing = await this.adapter.get(this.vault, this.collName, key)\n const env = await this.encryptEntry(entry, (existing?._v ?? 0) + 1, key)\n await this.adapter.put(this.vault, this.collName, key, env, existing?._v)\n this.emitter.emit('change', { vault: this.vault, collection: this.collName, id: key, action: 'put' })\n }\n\n async disconnect(aId: string, bId: string): Promise<void> {\n const key = linkRowKey(aId, bId)\n const existing = await this.adapter.get(this.vault, this.collName, key)\n if (!existing) return\n await this.adapter.delete(this.vault, this.collName, key)\n this.emitter.emit('change', { vault: this.vault, collection: this.collName, id: key, action: 'delete' })\n }\n\n async has(aId: string, bId: string): Promise<boolean> {\n return (await this.adapter.get(this.vault, this.collName, linkRowKey(aId, bId))) !== null\n }\n\n async of(id: string): Promise<LinkRow[]> {\n const rows = await this.list()\n return rows.filter((r) => r.a === id || r.b === id)\n }\n\n async list(): Promise<LinkRow[]> {\n const keys = await this.adapter.list(this.vault, this.collName)\n const out: LinkRow[] = []\n for (const key of keys) {\n const env = await this.adapter.get(this.vault, this.collName, key)\n if (!env) continue\n const e = await this.decryptEntry(key, env)\n out.push(e.meta !== undefined ? { a: e.a, b: e.b, meta: e.meta } : { a: e.a, b: e.b })\n }\n return out\n }\n\n // ── Vault-internal cascade helpers ──────────────────────────────────\n\n /** @internal — rows where the deleted endpoint id matches the relevant slot. */\n async _rowsTouchingEndpoint(collection: string, id: string): Promise<LinkRow[]> {\n const rows = await this.list()\n return rows.filter(\n (r) => (this.spec.a === collection && r.a === id) || (this.spec.b === collection && r.b === id),\n )\n }\n\n /** @internal — the storage collection name (for tx pre-image capture). */\n get _collectionName(): string {\n return this.collName\n }\n}\n\n/** Thrown by `connect()` when an endpoint record does not exist. */\nexport class LinkEndpointError extends NoydbError {\n readonly link: string\n readonly endpoint: string\n readonly missingId: string\n constructor(link: string, endpoint: string, missingId: string) {\n super(\n 'LINK_ENDPOINT',\n `link(\"${link}\").connect: endpoint \"${endpoint}\" has no record \"${missingId}\".`,\n )\n this.name = 'LinkEndpointError'\n this.link = link\n this.endpoint = endpoint\n this.missingId = missingId\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,195 @@
1
+ // src/kernel/query/civil-date.ts
2
+ var CIVIL = /^(\d{4})(?:-(\d{1,2})(?:-(\d{1,2})(?:[T ]\d{1,2}:\d{2}(?::\d{2}(?:\.\d+)?)?)?)?)?$/;
3
+ function daysInMonth(y, m) {
4
+ if (m === 2) return y % 4 === 0 && y % 100 !== 0 || y % 400 === 0 ? 29 : 28;
5
+ return m === 4 || m === 6 || m === 9 || m === 11 ? 30 : 31;
6
+ }
7
+ function civilDateOf(value) {
8
+ if (typeof value !== "string") return void 0;
9
+ const match = CIVIL.exec(value);
10
+ if (match === null) return void 0;
11
+ const y = Number(match[1]);
12
+ const m = match[2] === void 0 ? 1 : Number(match[2]);
13
+ const d = match[3] === void 0 ? 1 : Number(match[3]);
14
+ if (m < 1 || m > 12 || d < 1 || d > daysInMonth(y, m)) return "invalid";
15
+ return { y, m, d };
16
+ }
17
+
18
+ // src/kernel/query/reduce/date-trunc.ts
19
+ var UNITS = ["day", "week", "month", "quarter", "year"];
20
+ var WEEK_STARTS = ["monday", "sunday"];
21
+ function dateTrunc(field, unit, options) {
22
+ if (typeof field !== "string" || field.length === 0) {
23
+ throw new Error("dateTrunc(): field must be a non-empty field name.");
24
+ }
25
+ if (!UNITS.includes(unit)) {
26
+ throw new Error(
27
+ `dateTrunc("${field}"): unknown unit "${String(unit)}". Expected one of ${UNITS.join(", ")}.`
28
+ );
29
+ }
30
+ const timeZone = options?.timeZone;
31
+ if (typeof timeZone !== "string" || timeZone.length === 0) {
32
+ throw new Error(
33
+ `dateTrunc("${field}", "${unit}"): timeZone is required. An accounting period is a local-calendar fact, so there is no safe host-local default \u2014 pass an IANA zone (e.g. { timeZone: 'Europe/Berlin' }), or { timeZone: 'UTC' } to bucket in UTC.`
34
+ );
35
+ }
36
+ try {
37
+ formatterFor(timeZone);
38
+ } catch {
39
+ throw new Error(
40
+ `dateTrunc("${field}", "${unit}"): unknown timeZone "${timeZone}". Expected an IANA zone name.`
41
+ );
42
+ }
43
+ const weekStartsOn = options.weekStartsOn ?? "monday";
44
+ if (!WEEK_STARTS.includes(weekStartsOn)) {
45
+ throw new Error(
46
+ `dateTrunc("${field}", "${unit}"): weekStartsOn must be ${WEEK_STARTS.join(" or ")}.`
47
+ );
48
+ }
49
+ return {
50
+ __dateTrunc: true,
51
+ field,
52
+ unit,
53
+ timeZone,
54
+ weekStartsOn,
55
+ as: options.as ?? `${field.replaceAll(".", "_")}_${unit}`
56
+ };
57
+ }
58
+ function isDateTruncKey(value) {
59
+ return typeof value === "object" && value !== null && value.__dateTrunc === true;
60
+ }
61
+ function groupKeyName(key) {
62
+ return isDateTruncKey(key) ? key.as : key;
63
+ }
64
+ function groupKeySourceField(key) {
65
+ return isDateTruncKey(key) ? key.field : key;
66
+ }
67
+ function describeGroupKey(key) {
68
+ if (!isDateTruncKey(key)) return key;
69
+ return `dateTrunc(${key.field}|${key.unit}|${key.timeZone}|${key.weekStartsOn}|${key.as})`;
70
+ }
71
+ function truncateDate(value, key) {
72
+ if (value === null || value === void 0) return value;
73
+ const { y, m, d } = resolveCivilDate(value, key);
74
+ switch (key.unit) {
75
+ case "day":
76
+ return ymd(y, m, d);
77
+ case "month":
78
+ return ymd(y, m, 1);
79
+ case "quarter":
80
+ return ymd(y, m - (m - 1) % 3, 1);
81
+ case "year":
82
+ return ymd(y, 1, 1);
83
+ case "week": {
84
+ const days = daysFromCivil(y, m, d);
85
+ const sundayIndex = ((days % 7 + 7) % 7 + 4) % 7;
86
+ const back = key.weekStartsOn === "monday" ? (sundayIndex + 6) % 7 : sundayIndex;
87
+ const start = civilFromDays(days - back);
88
+ return ymd(start.y, start.m, start.d);
89
+ }
90
+ }
91
+ }
92
+ function projectDateTruncKeys(rows, keys) {
93
+ return rows.map((row) => {
94
+ const next = { ...row };
95
+ for (const key of keys) {
96
+ next[key.as] = truncateDate(readFieldPath(next, key.field), key);
97
+ }
98
+ return next;
99
+ });
100
+ }
101
+ function readFieldPath(row, path) {
102
+ if (!path.includes(".")) return row[path];
103
+ let cur = row;
104
+ for (const seg of path.split(".")) {
105
+ if (cur === null || cur === void 0 || typeof cur !== "object") return void 0;
106
+ cur = cur[seg];
107
+ }
108
+ return cur;
109
+ }
110
+ function resolveCivilDate(value, key) {
111
+ const civil = civilDateOf(value);
112
+ if (civil === "invalid") {
113
+ throw new Error(
114
+ `dateTrunc("${key.field}", "${key.unit}"): value ${JSON.stringify(value)} is a calendar date shape but names no real day.`
115
+ );
116
+ }
117
+ if (civil !== void 0) return civil;
118
+ const ms = toEpochMs(value);
119
+ if (ms === void 0) {
120
+ throw new Error(
121
+ `dateTrunc("${key.field}", "${key.unit}"): value ${JSON.stringify(value) ?? typeof value} is not a timestamp. Expected a Date, epoch milliseconds, or a parseable date string.`
122
+ );
123
+ }
124
+ return localCivilDate(ms, key.timeZone);
125
+ }
126
+ function toEpochMs(value) {
127
+ if (value instanceof Date) return Number.isNaN(value.getTime()) ? void 0 : value.getTime();
128
+ if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
129
+ if (typeof value === "string") {
130
+ const parsed = Date.parse(value);
131
+ return Number.isNaN(parsed) ? void 0 : parsed;
132
+ }
133
+ return void 0;
134
+ }
135
+ var formatters = /* @__PURE__ */ new Map();
136
+ function formatterFor(timeZone) {
137
+ let f = formatters.get(timeZone);
138
+ if (!f) {
139
+ f = new Intl.DateTimeFormat("en-US", {
140
+ timeZone,
141
+ year: "numeric",
142
+ month: "2-digit",
143
+ day: "2-digit"
144
+ });
145
+ formatters.set(timeZone, f);
146
+ }
147
+ return f;
148
+ }
149
+ function localCivilDate(ms, timeZone) {
150
+ let y = 0;
151
+ let m = 0;
152
+ let d = 0;
153
+ for (const part of formatterFor(timeZone).formatToParts(new Date(ms))) {
154
+ if (part.type === "year") y = Number(part.value);
155
+ else if (part.type === "month") m = Number(part.value);
156
+ else if (part.type === "day") d = Number(part.value);
157
+ }
158
+ return { y, m, d };
159
+ }
160
+ function ymd(y, m, d) {
161
+ return `${String(y).padStart(4, "0")}-${String(m).padStart(2, "0")}-${String(d).padStart(2, "0")}`;
162
+ }
163
+ function daysFromCivil(y, m, d) {
164
+ const yy = m <= 2 ? y - 1 : y;
165
+ const era = Math.floor(yy / 400);
166
+ const yoe = yy - era * 400;
167
+ const doy = Math.floor((153 * (m + (m > 2 ? -3 : 9)) + 2) / 5) + d - 1;
168
+ const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
169
+ return era * 146097 + doe - 719468;
170
+ }
171
+ function civilFromDays(z) {
172
+ const shifted = z + 719468;
173
+ const era = Math.floor(shifted / 146097);
174
+ const doe = shifted - era * 146097;
175
+ const yoe = Math.floor(
176
+ (doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365
177
+ );
178
+ const y = yoe + era * 400;
179
+ const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));
180
+ const mp = Math.floor((5 * doy + 2) / 153);
181
+ const d = doy - Math.floor((153 * mp + 2) / 5) + 1;
182
+ const m = mp + (mp < 10 ? 3 : -9);
183
+ return { y: m <= 2 ? y + 1 : y, m, d };
184
+ }
185
+
186
+ export {
187
+ dateTrunc,
188
+ isDateTruncKey,
189
+ groupKeyName,
190
+ groupKeySourceField,
191
+ describeGroupKey,
192
+ truncateDate,
193
+ projectDateTruncKeys
194
+ };
195
+ //# sourceMappingURL=chunk-XBKH4FOC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/kernel/query/civil-date.ts","../src/kernel/query/reduce/date-trunc.ts"],"sourcesContent":["/**\n * Civil-date recognition for `dateTrunc()` (#1410).\n *\n * ## Why this module exists\n *\n * `dateTrunc()` buckets a value by resolving it to an instant and reading the\n * civil calendar date that instant falls on in the declared `timeZone`. That is\n * exactly right for a *timestamp*. It is a **category error** for a value that\n * names no instant.\n *\n * `'2026-01-01'` — the shape `z.string().date()` produces — is a calendar date.\n * ECMA-262 nonetheless resolves the ISO date-only form to **UTC midnight**, and\n * converting that instant into any zone west of UTC moves it back a day. So\n * `dateTrunc(..., 'month', { timeZone: 'America/New_York' })` silently bucketed\n * `'2026-01-01'` into `2025-12` — a wrong year, no error, no warning. Correct at\n * `+07` and UTC purely by accident of sign, which is what made it latent.\n *\n * ## The rule\n *\n * **A string carrying no timezone designator denotes CIVIL time and is used as\n * written. Everything else is an instant and is converted through the zone.**\n *\n * That covers three shapes, all of which are zoneless:\n *\n * | input | denotes | why |\n * |---|---|---|\n * | `'2026-01-01'`, `'2026-01'`, `'2026'` | civil | ISO calendar forms; no time, no zone |\n * | `'2026-01-01T08:30'`, `'2026-01-01 08:30:00.5'` | civil | offsetless date-time |\n * | `'2026-01-01T00:00:00Z'`, `'…+07:00'`, `Date`, epoch ms | instant | carries, or *is*, a fixed point in time |\n *\n * The offsetless date-time is the subtle one and it is included **deliberately**.\n * ECMA-262 parses it as *host-local*, which is the same defect one layer down:\n * the bucket would depend on the machine the query ran on, and this module's\n * whole premise (see `date-trunc.ts`) is that a host-local guess is never\n * acceptable. Reading it as civil makes the result host-independent. The\n * time-of-day is then simply discarded — every `dateTrunc` unit is a day or\n * coarser, so no unit can observe it.\n *\n * ⚠️ **Loose, non-ISO strings are NOT recognised here** — `'01/01/2026'`,\n * RFC-2822, and anything else `Date.parse` accepts by implementation extension\n * stay on the instant path. Their parsing is implementation-defined, so there\n * is no shape this module could claim to recognise correctly; a consumer who\n * wants civil treatment should pass an ISO calendar date.\n *\n * ⚠️ **An out-of-range field is REFUSED, not rolled over.** V8's `Date.parse`\n * accepts `'2026-02-30'` and yields 2 March; bucketing a typo under a\n * wrong-but-plausible key is the failure mode this whole module exists to\n * prevent, so a recognised-shape-but-impossible date throws instead.\n */\n\n/** A calendar date with no instant and no zone. `m` is 1-12. */\nexport interface CivilDate {\n readonly y: number\n readonly m: number\n readonly d: number\n}\n\n/**\n * `YYYY[-M[-D[(T| )HH:MM[:SS[.fff]]]]]` and nothing else — in particular no\n * trailing `Z` and no `±HH:MM`, which are precisely the designators that turn\n * the string into an instant.\n */\nconst CIVIL =\n /^(\\d{4})(?:-(\\d{1,2})(?:-(\\d{1,2})(?:[T ]\\d{1,2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?)?)?)?$/\n\n/** Days in `m` of `y`, proleptic Gregorian. */\nfunction daysInMonth(y: number, m: number): number {\n if (m === 2) return (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0 ? 29 : 28\n return m === 4 || m === 6 || m === 9 || m === 11 ? 30 : 31\n}\n\n/**\n * What a value denotes:\n * - a {@link CivilDate} — a zoneless calendar shape, use it as written;\n * - `'invalid'` — a recognised calendar shape naming no real day (see the\n * out-of-range note above); the caller refuses it with its own context;\n * - `undefined` — an instant, or an unrecognised shape: convert through the\n * declared zone exactly as before.\n */\nexport function civilDateOf(value: unknown): CivilDate | 'invalid' | undefined {\n if (typeof value !== 'string') return undefined\n const match = CIVIL.exec(value)\n if (match === null) return undefined\n const y = Number(match[1])\n const m = match[2] === undefined ? 1 : Number(match[2])\n const d = match[3] === undefined ? 1 : Number(match[3])\n if (m < 1 || m > 12 || d < 1 || d > daysInMonth(y, m)) return 'invalid'\n return { y, m, d }\n}\n","/**\n * Calendar bucketing for group keys — `dateTrunc()` (#1350).\n *\n * ```ts\n * invoices.query()\n * .groupBy(dateTrunc('issuedAt', 'month', { timeZone: 'Europe/Berlin' }))\n * .aggregate({ total: sum('amount') })\n * .run()\n * // → [ { issuedAt_month: '2026-09-01', total: 5250 }, … ]\n * ```\n *\n * ## The timezone is a REQUIRED parameter, never a host-local guess\n *\n * An accounting period is a local-calendar fact: \"September\" means the\n * September of some jurisdiction's calendar, and the same instant belongs to\n * different months depending on which. A host-local default would make the\n * same query produce different rollups on a laptop in Berlin and a CI runner\n * in UTC — silently, with no error and a plausible-looking number. So\n * `timeZone` has no default: omit it and the descriptor is refused at\n * construction, which is also where an unknown zone is caught.\n *\n * ## The bucket value is the local calendar date of the bucket START\n *\n * Every unit produces `'YYYY-MM-DD'`: `'2026-09-01'` for September 2026,\n * `'2026-07-01'` for Q3, `'2026-01-01'` for the year, `'2026-08-31'` for the\n * ISO week containing 3 September. One shape for all five units, lexically\n * sortable, and free of the week-numbering ambiguity that `'2026-W36'` would\n * drag in (ISO week-years disagree with calendar years at the boundary, and\n * ISO week numbers are undefined for a sunday-start week).\n *\n * ## Week start\n *\n * `weekStartsOn` defaults to `'monday'` (ISO-8601). `'sunday'` is the other\n * accepted value. It is only consulted for `unit: 'week'`, but it is part of\n * the descriptor's identity either way, so it folds into the queryHash.\n *\n * ## DST\n *\n * No arithmetic is ever done on a timestamp. The instant is resolved to a\n * local civil date through `Intl.DateTimeFormat` (the platform's own tz\n * database), and every truncation from there is pure civil-calendar\n * arithmetic. A 23-hour or 25-hour local day therefore needs no special case:\n * both ends of it format to the same local date.\n *\n * ## A ZONELESS value is a civil date, and the timeZone does not apply to it\n *\n * `'2026-01-01'` — what `z.string().date()` produces — names a calendar day,\n * not an instant. It is bucketed as written, in every zone. Applying a zone to\n * it is a category error, and an expensive one: ECMA-262 resolves the ISO\n * date-only form to UTC midnight, so before #1410 that value bucketed into\n * **2025-12** for `America/New_York` — a wrong year, silently. The same holds\n * for an offsetless date-time, which ECMA-262 would otherwise read as\n * *host*-local. A `Date`, epoch millis, or a string carrying `Z` or an offset\n * IS an instant and still converts through `timeZone`. See `civil-date.ts`.\n *\n * Portability: `Intl` is a language global, so this file stays inside hub's\n * no-Node-built-ins boundary and adds no dependency.\n */\nimport { civilDateOf } from '../civil-date.js'\n\n/** Calendar units `dateTrunc()` can bucket into. */\nexport type DateTruncUnit = 'day' | 'week' | 'month' | 'quarter' | 'year'\n\n/** Accepted week-start conventions. Default is `'monday'` (ISO-8601). */\nexport type WeekStart = 'monday' | 'sunday'\n\nconst UNITS: readonly DateTruncUnit[] = ['day', 'week', 'month', 'quarter', 'year']\nconst WEEK_STARTS: readonly WeekStart[] = ['monday', 'sunday']\n\n/**\n * A derived group key: \"bucket `field` to the start of its `unit`, in\n * `timeZone`\". Opaque to consumers — build it with {@link dateTrunc}.\n */\nexport interface DateTruncKey {\n readonly __dateTrunc: true\n /** Source field (a `readPath` path — dots address nested values). */\n readonly field: string\n readonly unit: DateTruncUnit\n /** IANA zone name. Always explicit; validated at construction. */\n readonly timeZone: string\n readonly weekStartsOn: WeekStart\n /** Output key the bucket is stamped under on each result row. */\n readonly as: string\n}\n\n/** Either a plain field name or a derived calendar key. */\nexport type GroupKey = string | DateTruncKey\n\nexport interface DateTruncOptions {\n /**\n * IANA timezone the calendar is read in — REQUIRED. See the module note:\n * there is no safe default, so there is no default.\n *\n * It applies to values that ARE instants. A zoneless calendar string\n * (`'2026-01-01'`, `'2026-01-01T08:30'`) has no instant to convert and is\n * bucketed as written regardless of what is passed here (#1410).\n */\n timeZone: string\n /** Week-start convention for `unit: 'week'`. Default `'monday'`. */\n weekStartsOn?: WeekStart\n /**\n * Output key. Defaults to `<field>_<unit>` with dots flattened to\n * underscores, so a nested source path cannot produce an output key that\n * `readPath` would then re-interpret as a path.\n */\n as?: string\n}\n\n/**\n * Declare a calendar-bucketed group key. Composable with plain field names in\n * a multi-key `.groupBy(...)`.\n */\nexport function dateTrunc(\n field: string,\n unit: DateTruncUnit,\n options: DateTruncOptions,\n): DateTruncKey {\n if (typeof field !== 'string' || field.length === 0) {\n throw new Error('dateTrunc(): field must be a non-empty field name.')\n }\n if (!UNITS.includes(unit)) {\n throw new Error(\n `dateTrunc(\"${field}\"): unknown unit \"${String(unit)}\". Expected one of ${UNITS.join(', ')}.`,\n )\n }\n const timeZone = options?.timeZone\n if (typeof timeZone !== 'string' || timeZone.length === 0) {\n throw new Error(\n `dateTrunc(\"${field}\", \"${unit}\"): timeZone is required. An accounting period is a ` +\n `local-calendar fact, so there is no safe host-local default — pass an IANA zone ` +\n `(e.g. { timeZone: 'Europe/Berlin' }), or { timeZone: 'UTC' } to bucket in UTC.`,\n )\n }\n try {\n formatterFor(timeZone)\n } catch {\n throw new Error(\n `dateTrunc(\"${field}\", \"${unit}\"): unknown timeZone \"${timeZone}\". Expected an IANA zone name.`,\n )\n }\n const weekStartsOn = options.weekStartsOn ?? 'monday'\n if (!WEEK_STARTS.includes(weekStartsOn)) {\n throw new Error(\n `dateTrunc(\"${field}\", \"${unit}\"): weekStartsOn must be ${WEEK_STARTS.join(' or ')}.`,\n )\n }\n return {\n __dateTrunc: true,\n field,\n unit,\n timeZone,\n weekStartsOn,\n as: options.as ?? `${field.replaceAll('.', '_')}_${unit}`,\n }\n}\n\n/** Runtime discriminator — a `DateTruncKey` vs a plain field name. */\nexport function isDateTruncKey(value: unknown): value is DateTruncKey {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __dateTrunc?: unknown }).__dateTrunc === true\n )\n}\n\n/** Output key a group key stamps on the result row. */\nexport function groupKeyName(key: GroupKey): string {\n return isDateTruncKey(key) ? key.as : key\n}\n\n/**\n * Source field a group key READS. Differs from {@link groupKeyName} only for a\n * derived key — guards that ask \"is this field stored?\" want this one.\n */\nexport function groupKeySourceField(key: GroupKey): string {\n return isDateTruncKey(key) ? key.field : key\n}\n\n/**\n * Canonical, stable description of a group key for hashing. Every parameter\n * that changes which bucket a row lands in appears here, so declaring,\n * removing, or re-parameterising a derived key bumps an MV's `queryHash`.\n * Pipe-separated so a description can never be confused with the\n * comma-separated field list it sits inside.\n */\nexport function describeGroupKey(key: GroupKey): string {\n if (!isDateTruncKey(key)) return key\n return `dateTrunc(${key.field}|${key.unit}|${key.timeZone}|${key.weekStartsOn}|${key.as})`\n}\n\n/**\n * Truncate one value to its bucket. `null` / `undefined` pass through\n * unchanged so the existing null-vs-undefined bucket semantics of\n * `groupAndReduce` still apply. Anything else that is not a timestamp is\n * REFUSED rather than bucketed under a wrong-but-plausible key.\n */\nexport function truncateDate(value: unknown, key: DateTruncKey): string | null | undefined {\n if (value === null || value === undefined) return value\n const { y, m, d } = resolveCivilDate(value, key)\n switch (key.unit) {\n case 'day':\n return ymd(y, m, d)\n case 'month':\n return ymd(y, m, 1)\n case 'quarter':\n return ymd(y, m - ((m - 1) % 3), 1)\n case 'year':\n return ymd(y, 1, 1)\n case 'week': {\n const days = daysFromCivil(y, m, d)\n // 1970-01-01 (day 0) was a Thursday; +4 shifts to a sunday-indexed week.\n const sundayIndex = (((days % 7) + 7) % 7 + 4) % 7\n const back = key.weekStartsOn === 'monday' ? (sundayIndex + 6) % 7 : sundayIndex\n const start = civilFromDays(days - back)\n return ymd(start.y, start.m, start.d)\n }\n }\n}\n\n/**\n * Stamp each derived key's bucket onto a shallow copy of every row, so the\n * downstream grouping pipeline sees an ordinary stored field. Input rows are\n * never mutated.\n */\nexport function projectDateTruncKeys<R>(\n rows: readonly R[],\n keys: readonly DateTruncKey[],\n): R[] {\n return rows.map((row) => {\n const next = { ...(row as Record<string, unknown>) }\n for (const key of keys) {\n next[key.as] = truncateDate(readFieldPath(next, key.field), key)\n }\n return next as R\n })\n}\n\n// ─── internals ──────────────────────────────────────────────────────────────\n\n/**\n * Local `readPath`. Duplicating six lines from `predicate.ts` keeps this\n * module importable from the MV executor without dragging the query\n * predicate evaluator in behind it.\n */\nfunction readFieldPath(row: Record<string, unknown>, path: string): unknown {\n if (!path.includes('.')) return row[path]\n let cur: unknown = row\n for (const seg of path.split('.')) {\n if (cur === null || cur === undefined || typeof cur !== 'object') return undefined\n cur = (cur as Record<string, unknown>)[seg]\n }\n return cur\n}\n\n/**\n * The civil date a value buckets on. Two paths, and which one a value takes is\n * a property of the VALUE, never of the descriptor (#1410):\n *\n * - a zoneless calendar string names no instant, so there is nothing to\n * convert — it is its own civil date, and `key.timeZone` is not applicable\n * to it. This is what stops `'2026-01-01'` bucketing into 2025 west of UTC;\n * - anything else IS an instant, and the calendar it falls on is read in\n * `key.timeZone`, exactly as before.\n *\n * See `civil-date.ts` for which shapes are zoneless and why.\n */\nfunction resolveCivilDate(value: unknown, key: DateTruncKey): { y: number; m: number; d: number } {\n const civil = civilDateOf(value)\n if (civil === 'invalid') {\n throw new Error(\n `dateTrunc(\"${key.field}\", \"${key.unit}\"): value ${JSON.stringify(value)} is a calendar ` +\n `date shape but names no real day.`,\n )\n }\n if (civil !== undefined) return civil\n const ms = toEpochMs(value)\n if (ms === undefined) {\n throw new Error(\n `dateTrunc(\"${key.field}\", \"${key.unit}\"): value ${JSON.stringify(value) ?? typeof value} ` +\n `is not a timestamp. Expected a Date, epoch milliseconds, or a parseable date string.`,\n )\n }\n return localCivilDate(ms, key.timeZone)\n}\n\nfunction toEpochMs(value: unknown): number | undefined {\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? undefined : value.getTime()\n if (typeof value === 'number') return Number.isFinite(value) ? value : undefined\n if (typeof value === 'string') {\n const parsed = Date.parse(value)\n return Number.isNaN(parsed) ? undefined : parsed\n }\n return undefined\n}\n\nconst formatters = new Map<string, Intl.DateTimeFormat>()\nfunction formatterFor(timeZone: string): Intl.DateTimeFormat {\n let f = formatters.get(timeZone)\n if (!f) {\n f = new Intl.DateTimeFormat('en-US', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n })\n formatters.set(timeZone, f)\n }\n return f\n}\n\n/** The civil (wall-clock) calendar date an instant falls on, in `timeZone`. */\nfunction localCivilDate(ms: number, timeZone: string): { y: number; m: number; d: number } {\n let y = 0\n let m = 0\n let d = 0\n for (const part of formatterFor(timeZone).formatToParts(new Date(ms))) {\n if (part.type === 'year') y = Number(part.value)\n else if (part.type === 'month') m = Number(part.value)\n else if (part.type === 'day') d = Number(part.value)\n }\n return { y, m, d }\n}\n\nfunction ymd(y: number, m: number, d: number): string {\n return `${String(y).padStart(4, '0')}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`\n}\n\n/** Days since 1970-01-01 for a civil date (Hinnant's algorithm; `m` is 1-12). */\nfunction daysFromCivil(y: number, m: number, d: number): number {\n const yy = m <= 2 ? y - 1 : y\n const era = Math.floor(yy / 400)\n const yoe = yy - era * 400\n const doy = Math.floor((153 * (m + (m > 2 ? -3 : 9)) + 2) / 5) + d - 1\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy\n return era * 146097 + doe - 719468\n}\n\n/** Inverse of {@link daysFromCivil}. */\nfunction civilFromDays(z: number): { y: number; m: number; d: number } {\n const shifted = z + 719468\n const era = Math.floor(shifted / 146097)\n const doe = shifted - era * 146097\n const yoe = Math.floor(\n (doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365,\n )\n const y = yoe + era * 400\n const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100))\n const mp = Math.floor((5 * doy + 2) / 153)\n const d = doy - Math.floor((153 * mp + 2) / 5) + 1\n const m = mp + (mp < 10 ? 3 : -9)\n return { y: m <= 2 ? y + 1 : y, m, d }\n}\n"],"mappings":";AA8DA,IAAM,QACJ;AAGF,SAAS,YAAY,GAAW,GAAmB;AACjD,MAAI,MAAM,EAAG,QAAQ,IAAI,MAAM,KAAK,IAAI,QAAQ,KAAM,IAAI,QAAQ,IAAI,KAAK;AAC3E,SAAO,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,MAAM,KAAK,KAAK;AAC1D;AAUO,SAAS,YAAY,OAAmD;AAC7E,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,QAAQ,MAAM,KAAK,KAAK;AAC9B,MAAI,UAAU,KAAM,QAAO;AAC3B,QAAM,IAAI,OAAO,MAAM,CAAC,CAAC;AACzB,QAAM,IAAI,MAAM,CAAC,MAAM,SAAY,IAAI,OAAO,MAAM,CAAC,CAAC;AACtD,QAAM,IAAI,MAAM,CAAC,MAAM,SAAY,IAAI,OAAO,MAAM,CAAC,CAAC;AACtD,MAAI,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,YAAY,GAAG,CAAC,EAAG,QAAO;AAC9D,SAAO,EAAE,GAAG,GAAG,EAAE;AACnB;;;ACtBA,IAAM,QAAkC,CAAC,OAAO,QAAQ,SAAS,WAAW,MAAM;AAClF,IAAM,cAAoC,CAAC,UAAU,QAAQ;AA6CtD,SAAS,UACd,OACA,MACA,SACc;AACd,MAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG;AACnD,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AACA,MAAI,CAAC,MAAM,SAAS,IAAI,GAAG;AACzB,UAAM,IAAI;AAAA,MACR,cAAc,KAAK,qBAAqB,OAAO,IAAI,CAAC,sBAAsB,MAAM,KAAK,IAAI,CAAC;AAAA,IAC5F;AAAA,EACF;AACA,QAAM,WAAW,SAAS;AAC1B,MAAI,OAAO,aAAa,YAAY,SAAS,WAAW,GAAG;AACzD,UAAM,IAAI;AAAA,MACR,cAAc,KAAK,OAAO,IAAI;AAAA,IAGhC;AAAA,EACF;AACA,MAAI;AACF,iBAAa,QAAQ;AAAA,EACvB,QAAQ;AACN,UAAM,IAAI;AAAA,MACR,cAAc,KAAK,OAAO,IAAI,yBAAyB,QAAQ;AAAA,IACjE;AAAA,EACF;AACA,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,MAAI,CAAC,YAAY,SAAS,YAAY,GAAG;AACvC,UAAM,IAAI;AAAA,MACR,cAAc,KAAK,OAAO,IAAI,4BAA4B,YAAY,KAAK,MAAM,CAAC;AAAA,IACpF;AAAA,EACF;AACA,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,IAAI,QAAQ,MAAM,GAAG,MAAM,WAAW,KAAK,GAAG,CAAC,IAAI,IAAI;AAAA,EACzD;AACF;AAGO,SAAS,eAAe,OAAuC;AACpE,SACE,OAAO,UAAU,YACjB,UAAU,QACT,MAAoC,gBAAgB;AAEzD;AAGO,SAAS,aAAa,KAAuB;AAClD,SAAO,eAAe,GAAG,IAAI,IAAI,KAAK;AACxC;AAMO,SAAS,oBAAoB,KAAuB;AACzD,SAAO,eAAe,GAAG,IAAI,IAAI,QAAQ;AAC3C;AASO,SAAS,iBAAiB,KAAuB;AACtD,MAAI,CAAC,eAAe,GAAG,EAAG,QAAO;AACjC,SAAO,aAAa,IAAI,KAAK,IAAI,IAAI,IAAI,IAAI,IAAI,QAAQ,IAAI,IAAI,YAAY,IAAI,IAAI,EAAE;AACzF;AAQO,SAAS,aAAa,OAAgB,KAA8C;AACzF,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,QAAM,EAAE,GAAG,GAAG,EAAE,IAAI,iBAAiB,OAAO,GAAG;AAC/C,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,aAAO,IAAI,GAAG,GAAG,CAAC;AAAA,IACpB,KAAK;AACH,aAAO,IAAI,GAAG,GAAG,CAAC;AAAA,IACpB,KAAK;AACH,aAAO,IAAI,GAAG,KAAM,IAAI,KAAK,GAAI,CAAC;AAAA,IACpC,KAAK;AACH,aAAO,IAAI,GAAG,GAAG,CAAC;AAAA,IACpB,KAAK,QAAQ;AACX,YAAM,OAAO,cAAc,GAAG,GAAG,CAAC;AAElC,YAAM,gBAAiB,OAAO,IAAK,KAAK,IAAI,KAAK;AACjD,YAAM,OAAO,IAAI,iBAAiB,YAAY,cAAc,KAAK,IAAI;AACrE,YAAM,QAAQ,cAAc,OAAO,IAAI;AACvC,aAAO,IAAI,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAAA,IACtC;AAAA,EACF;AACF;AAOO,SAAS,qBACd,MACA,MACK;AACL,SAAO,KAAK,IAAI,CAAC,QAAQ;AACvB,UAAM,OAAO,EAAE,GAAI,IAAgC;AACnD,eAAW,OAAO,MAAM;AACtB,WAAK,IAAI,EAAE,IAAI,aAAa,cAAc,MAAM,IAAI,KAAK,GAAG,GAAG;AAAA,IACjE;AACA,WAAO;AAAA,EACT,CAAC;AACH;AASA,SAAS,cAAc,KAA8B,MAAuB;AAC1E,MAAI,CAAC,KAAK,SAAS,GAAG,EAAG,QAAO,IAAI,IAAI;AACxC,MAAI,MAAe;AACnB,aAAW,OAAO,KAAK,MAAM,GAAG,GAAG;AACjC,QAAI,QAAQ,QAAQ,QAAQ,UAAa,OAAO,QAAQ,SAAU,QAAO;AACzE,UAAO,IAAgC,GAAG;AAAA,EAC5C;AACA,SAAO;AACT;AAcA,SAAS,iBAAiB,OAAgB,KAAwD;AAChG,QAAM,QAAQ,YAAY,KAAK;AAC/B,MAAI,UAAU,WAAW;AACvB,UAAM,IAAI;AAAA,MACR,cAAc,IAAI,KAAK,OAAO,IAAI,IAAI,aAAa,KAAK,UAAU,KAAK,CAAC;AAAA,IAE1E;AAAA,EACF;AACA,MAAI,UAAU,OAAW,QAAO;AAChC,QAAM,KAAK,UAAU,KAAK;AAC1B,MAAI,OAAO,QAAW;AACpB,UAAM,IAAI;AAAA,MACR,cAAc,IAAI,KAAK,OAAO,IAAI,IAAI,aAAa,KAAK,UAAU,KAAK,KAAK,OAAO,KAAK;AAAA,IAE1F;AAAA,EACF;AACA,SAAO,eAAe,IAAI,IAAI,QAAQ;AACxC;AAEA,SAAS,UAAU,OAAoC;AACrD,MAAI,iBAAiB,KAAM,QAAO,OAAO,MAAM,MAAM,QAAQ,CAAC,IAAI,SAAY,MAAM,QAAQ;AAC5F,MAAI,OAAO,UAAU,SAAU,QAAO,OAAO,SAAS,KAAK,IAAI,QAAQ;AACvE,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,WAAO,OAAO,MAAM,MAAM,IAAI,SAAY;AAAA,EAC5C;AACA,SAAO;AACT;AAEA,IAAM,aAAa,oBAAI,IAAiC;AACxD,SAAS,aAAa,UAAuC;AAC3D,MAAI,IAAI,WAAW,IAAI,QAAQ;AAC/B,MAAI,CAAC,GAAG;AACN,QAAI,IAAI,KAAK,eAAe,SAAS;AAAA,MACnC;AAAA,MACA,MAAM;AAAA,MACN,OAAO;AAAA,MACP,KAAK;AAAA,IACP,CAAC;AACD,eAAW,IAAI,UAAU,CAAC;AAAA,EAC5B;AACA,SAAO;AACT;AAGA,SAAS,eAAe,IAAY,UAAuD;AACzF,MAAI,IAAI;AACR,MAAI,IAAI;AACR,MAAI,IAAI;AACR,aAAW,QAAQ,aAAa,QAAQ,EAAE,cAAc,IAAI,KAAK,EAAE,CAAC,GAAG;AACrE,QAAI,KAAK,SAAS,OAAQ,KAAI,OAAO,KAAK,KAAK;AAAA,aACtC,KAAK,SAAS,QAAS,KAAI,OAAO,KAAK,KAAK;AAAA,aAC5C,KAAK,SAAS,MAAO,KAAI,OAAO,KAAK,KAAK;AAAA,EACrD;AACA,SAAO,EAAE,GAAG,GAAG,EAAE;AACnB;AAEA,SAAS,IAAI,GAAW,GAAW,GAAmB;AACpD,SAAO,GAAG,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAClG;AAGA,SAAS,cAAc,GAAW,GAAW,GAAmB;AAC9D,QAAM,KAAK,KAAK,IAAI,IAAI,IAAI;AAC5B,QAAM,MAAM,KAAK,MAAM,KAAK,GAAG;AAC/B,QAAM,MAAM,KAAK,MAAM;AACvB,QAAM,MAAM,KAAK,OAAO,OAAO,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,CAAC,IAAI,IAAI;AACrE,QAAM,MAAM,MAAM,MAAM,KAAK,MAAM,MAAM,CAAC,IAAI,KAAK,MAAM,MAAM,GAAG,IAAI;AACtE,SAAO,MAAM,SAAS,MAAM;AAC9B;AAGA,SAAS,cAAc,GAAgD;AACrE,QAAM,UAAU,IAAI;AACpB,QAAM,MAAM,KAAK,MAAM,UAAU,MAAM;AACvC,QAAM,MAAM,UAAU,MAAM;AAC5B,QAAM,MAAM,KAAK;AAAA,KACd,MAAM,KAAK,MAAM,MAAM,IAAI,IAAI,KAAK,MAAM,MAAM,KAAK,IAAI,KAAK,MAAM,MAAM,MAAM,KAAK;AAAA,EACxF;AACA,QAAM,IAAI,MAAM,MAAM;AACtB,QAAM,MAAM,OAAO,MAAM,MAAM,KAAK,MAAM,MAAM,CAAC,IAAI,KAAK,MAAM,MAAM,GAAG;AACzE,QAAM,KAAK,KAAK,OAAO,IAAI,MAAM,KAAK,GAAG;AACzC,QAAM,IAAI,MAAM,KAAK,OAAO,MAAM,KAAK,KAAK,CAAC,IAAI;AACjD,QAAM,IAAI,MAAM,KAAK,KAAK,IAAI;AAC9B,SAAO,EAAE,GAAG,KAAK,IAAI,IAAI,IAAI,GAAG,GAAG,EAAE;AACvC;","names":[]}