@noy-db/hub 0.3.0 → 0.4.0-pre.1

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 (375) hide show
  1. package/dist/aggregate/index.js +4 -4
  2. package/dist/api-IQFZJSQO.js +20 -0
  3. package/dist/as/index.js +11 -11
  4. package/dist/attestation/index.js +18 -18
  5. package/dist/{backup-BJ57IKAO.js → backup-LU2DTZTI.js} +11 -11
  6. package/dist/blobs/index.js +12 -12
  7. package/dist/broker/index.js +12 -12
  8. package/dist/bundle/index.js +26 -74
  9. package/dist/bundle/index.js.map +1 -1
  10. package/dist/by/index.js +2 -2
  11. package/dist/cargo/index.js +41 -22
  12. package/dist/chunk-2IALOXZS.js +24 -0
  13. package/dist/chunk-2IALOXZS.js.map +1 -0
  14. package/dist/{chunk-EVVGJDRQ.js → chunk-2MUZ3LY6.js} +3 -3
  15. package/dist/{chunk-NWSGI5C7.js → chunk-2P7EGNKF.js} +3 -3
  16. package/dist/{chunk-ZAJAVEXY.js → chunk-2PCAVW2I.js} +2 -2
  17. package/dist/{chunk-HTJQYHZP.js → chunk-3ELCWU56.js} +2 -2
  18. package/dist/{chunk-X7QBD67R.js → chunk-3PF6CRDT.js} +136 -114
  19. package/dist/chunk-3PF6CRDT.js.map +1 -0
  20. package/dist/{chunk-J2ZX2XUV.js → chunk-3SV2EPMN.js} +5 -5
  21. package/dist/{chunk-FUG4SFN3.js → chunk-3U3FBR7O.js} +2 -2
  22. package/dist/{chunk-VFUPHI2Z.js → chunk-4BRCIYFG.js} +6 -6
  23. package/dist/{chunk-SNZ6URZZ.js → chunk-5OI6BR4H.js} +7 -7
  24. package/dist/{chunk-B3HJYUOF.js → chunk-5WZXVULX.js} +2 -2
  25. package/dist/{chunk-LASZKVMO.js → chunk-6B4DYGWO.js} +3 -3
  26. package/dist/{chunk-ZJ2AK4V7.js → chunk-6HPG2YNX.js} +2 -2
  27. package/dist/{chunk-5M5DVGV7.js → chunk-6HY2X62D.js} +2 -2
  28. package/dist/{chunk-QRB7NAOJ.js → chunk-6NQMXTYC.js} +2 -2
  29. package/dist/{chunk-RIX4V2TB.js → chunk-6XE5TMPR.js} +3 -3
  30. package/dist/{chunk-BDVFPG6Y.js → chunk-7CI6CP7Z.js} +5 -5
  31. package/dist/{chunk-VLFCJYTL.js → chunk-7O3AYBC7.js} +63 -10
  32. package/dist/chunk-7O3AYBC7.js.map +1 -0
  33. package/dist/{chunk-454JXE57.js → chunk-A5BDDEXK.js} +10 -170
  34. package/dist/chunk-A5BDDEXK.js.map +1 -0
  35. package/dist/{chunk-FHS2HQSH.js → chunk-A7JJLVGH.js} +11 -8
  36. package/dist/chunk-A7JJLVGH.js.map +1 -0
  37. package/dist/{chunk-FM3G3NOY.js → chunk-BGK233EA.js} +83 -6
  38. package/dist/chunk-BGK233EA.js.map +1 -0
  39. package/dist/{chunk-OSDB5WW3.js → chunk-BMQWTQZ4.js} +11 -11
  40. package/dist/{chunk-W22IKJE2.js → chunk-BOKIKJJB.js} +2 -2
  41. package/dist/{chunk-CRI4OUV4.js → chunk-CBSY2N76.js} +2 -2
  42. package/dist/{chunk-2NVOWNI3.js → chunk-CCOP5XDW.js} +4 -4
  43. package/dist/{chunk-CDUQTA3Z.js → chunk-DDATL6PU.js} +7 -7
  44. package/dist/{chunk-RCVJZMFE.js → chunk-DDHM2TLX.js} +4 -4
  45. package/dist/{chunk-PN2XMATY.js → chunk-DRWBEDYO.js} +2 -2
  46. package/dist/{chunk-XDYREZWE.js → chunk-DVUZMT2W.js} +12 -8
  47. package/dist/{chunk-XDYREZWE.js.map → chunk-DVUZMT2W.js.map} +1 -1
  48. package/dist/{chunk-T2FFWNPK.js → chunk-EGCRBCNA.js} +3 -3
  49. package/dist/{chunk-VZRODIM3.js → chunk-F6T6L2UK.js} +4 -4
  50. package/dist/chunk-FH52K7EG.js +50 -0
  51. package/dist/chunk-FH52K7EG.js.map +1 -0
  52. package/dist/{chunk-EXK7QRWF.js → chunk-G6EQYAL6.js} +5 -5
  53. package/dist/{chunk-F64IRYG3.js → chunk-GQNGCEP5.js} +2 -2
  54. package/dist/{chunk-6JTIAGHD.js → chunk-GULTLPFB.js} +3 -3
  55. package/dist/chunk-GULTLPFB.js.map +1 -0
  56. package/dist/{chunk-ZUODRVHY.js → chunk-GVZQ4NAM.js} +2 -2
  57. package/dist/{chunk-3B5VVCNY.js → chunk-GWKAVKHI.js} +2 -2
  58. package/dist/{chunk-SLFHQTTW.js → chunk-GYYCX5K6.js} +5 -5
  59. package/dist/{chunk-CIE2BASH.js → chunk-HOONYZY2.js} +2 -2
  60. package/dist/{chunk-EYZ6IQ6X.js → chunk-HPUWOVR7.js} +5 -5
  61. package/dist/{chunk-IL6POWCW.js → chunk-IM3QVF53.js} +6 -6
  62. package/dist/{chunk-NJ4EOKMB.js → chunk-IRQY7IR5.js} +4 -4
  63. package/dist/{chunk-3QCRBHCY.js → chunk-ISMV2QVG.js} +2 -2
  64. package/dist/{chunk-HB3RFQSC.js → chunk-IY23KFNZ.js} +3 -3
  65. package/dist/chunk-J6Q7UVQE.js +248 -0
  66. package/dist/chunk-J6Q7UVQE.js.map +1 -0
  67. package/dist/{chunk-6CRIV7DE.js → chunk-JBBSYJHP.js} +2 -2
  68. package/dist/{chunk-MEFJPTZ4.js → chunk-JRBNCUNT.js} +3 -3
  69. package/dist/{chunk-4VNQOLML.js → chunk-JVBO4SZL.js} +2 -2
  70. package/dist/{chunk-I6GALP6E.js → chunk-JZWWLLVY.js} +6 -6
  71. package/dist/{chunk-RYCTDFUX.js → chunk-KUIB4B5Q.js} +2 -2
  72. package/dist/{chunk-HDQIIUPH.js → chunk-KUPKLSX5.js} +3 -3
  73. package/dist/{chunk-5O3XF4DV.js → chunk-KWVJAOIN.js} +4 -4
  74. package/dist/{chunk-3LQ2YKO4.js → chunk-LB6OH6K2.js} +2 -2
  75. package/dist/{chunk-AUSTCW2X.js → chunk-LOF2W3JU.js} +8 -8
  76. package/dist/{chunk-JNAI2HN2.js → chunk-LSASLXGC.js} +1 -1
  77. package/dist/chunk-LSASLXGC.js.map +1 -0
  78. package/dist/{chunk-K5IMOGHB.js → chunk-M75ZZHSR.js} +2 -2
  79. package/dist/{chunk-GTGPO5KW.js → chunk-MQ6WOPSY.js} +5 -5
  80. package/dist/{chunk-4CTHCN4J.js → chunk-MTQPRQHB.js} +4 -4
  81. package/dist/{chunk-UNFPPVNI.js → chunk-N55W7KUL.js} +2 -2
  82. package/dist/{chunk-JKJOGPZN.js → chunk-N6WF5PKC.js} +2 -2
  83. package/dist/{chunk-6T2SCQRU.js → chunk-O7WJ47EF.js} +4 -4
  84. package/dist/{chunk-JIF24JPM.js → chunk-OF75ACKS.js} +9 -9
  85. package/dist/{chunk-GKEWXIV5.js → chunk-ONTBCHCP.js} +2 -2
  86. package/dist/{chunk-4JBYV75E.js → chunk-PK6QCH2C.js} +5 -5
  87. package/dist/{chunk-6QTNZJBS.js → chunk-PT37L5DV.js} +3 -3
  88. package/dist/{chunk-EVX47VBM.js → chunk-QDPZWTN2.js} +2 -2
  89. package/dist/chunk-RA5VTXXG.js +173 -0
  90. package/dist/chunk-RA5VTXXG.js.map +1 -0
  91. package/dist/{chunk-QLWMEXCT.js → chunk-RDXW3OBQ.js} +2 -2
  92. package/dist/{chunk-Q5VNKNE5.js → chunk-RM2XV574.js} +3 -3
  93. package/dist/{chunk-ERJ7FU55.js → chunk-RND5ZPNP.js} +3 -3
  94. package/dist/{chunk-JZUV47FZ.js → chunk-RTEK4C4I.js} +2 -2
  95. package/dist/{chunk-KSKBRKGJ.js → chunk-S3BFKGET.js} +84 -6
  96. package/dist/chunk-S3BFKGET.js.map +1 -0
  97. package/dist/{chunk-5YOLBEQ6.js → chunk-S6Q37VGE.js} +2 -2
  98. package/dist/{chunk-HDMEGAQE.js → chunk-SMGEYW6G.js} +127 -8
  99. package/dist/chunk-SMGEYW6G.js.map +1 -0
  100. package/dist/{chunk-E5ZQT5KX.js → chunk-SMIRGMS7.js} +2 -2
  101. package/dist/chunk-T36RFODW.js +180 -0
  102. package/dist/chunk-T36RFODW.js.map +1 -0
  103. package/dist/{chunk-EQO2UVP4.js → chunk-T6GU5PPZ.js} +6 -6
  104. package/dist/{chunk-LXEXBTKL.js → chunk-TAGR72IR.js} +4 -4
  105. package/dist/{chunk-YFXZEAYK.js → chunk-TOPYT5U5.js} +6 -6
  106. package/dist/{chunk-MV43JSJF.js → chunk-UBNCR3YK.js} +4 -4
  107. package/dist/{chunk-ZICD77GM.js → chunk-UC2ETWF3.js} +2 -2
  108. package/dist/{chunk-XWZVFISC.js → chunk-UFEVO57O.js} +6 -6
  109. package/dist/{chunk-NR5LT5RY.js → chunk-UNCOC7QH.js} +7 -7
  110. package/dist/{chunk-R4ROFEYC.js → chunk-VAIS3SOM.js} +2 -2
  111. package/dist/{chunk-VN3DBQAU.js → chunk-VCBHAE4R.js} +2 -2
  112. package/dist/{chunk-M4CV6BRT.js → chunk-VEALDPHW.js} +2 -2
  113. package/dist/{chunk-BIOUI4HL.js → chunk-W2OG6JOL.js} +1 -1
  114. package/dist/{chunk-BIOUI4HL.js.map → chunk-W2OG6JOL.js.map} +1 -1
  115. package/dist/{chunk-7DSTRQIJ.js → chunk-WEYZTLXN.js} +2 -2
  116. package/dist/{chunk-CAF6WOK7.js → chunk-WNGGJWF7.js} +2 -2
  117. package/dist/{chunk-IU6SNYX6.js → chunk-YJV2UZ7H.js} +5 -5
  118. package/dist/{chunk-RZ5SVOYR.js → chunk-YTGDPCPW.js} +2 -2
  119. package/dist/{chunk-DLG74CNB.js → chunk-ZEQWCBCM.js} +7 -19
  120. package/dist/chunk-ZEQWCBCM.js.map +1 -0
  121. package/dist/classified/index.js +5 -5
  122. package/dist/{classified-marker-CZKA6ITE.js → classified-marker-WQVIJAW2.js} +3 -3
  123. package/dist/collection-facade-MJXSC3Q6.js +44 -0
  124. package/dist/{computed-ZIQTYTDI.js → computed-DSMPXG2Y.js} +3 -3
  125. package/dist/consent/index.js +9 -9
  126. package/dist/{public-envelope-B4KNWVUA.js → cover-B5SJDHR3.js} +25 -5
  127. package/dist/{dead-filter-ESOBZZP5.js → dead-filter-2A7IAJ3D.js} +2 -2
  128. package/dist/delegation-A5A75KQO.js +25 -0
  129. package/dist/derivations/index.js +12 -12
  130. package/dist/derive-HJSMH7RA.js +21 -0
  131. package/dist/{enclave-AP3I5W7O.js → enclave-7MW6ZGXW.js} +11 -9
  132. package/dist/executor-AXE4XMLT.js +26 -0
  133. package/dist/executor-KGNLJD6M.js +9 -0
  134. package/dist/executor-XGIS6CR7.js +9 -0
  135. package/dist/export-accessible-VVBJM3MM.js +23 -0
  136. package/dist/extract-partition-LGGBTQET.js +38 -0
  137. package/dist/{fanout-sidecar-A765THMX.js → fanout-sidecar-WDYQ77VB.js} +9 -9
  138. package/dist/find-YS6XWB46.js +11 -0
  139. package/dist/forget/index.js +9 -9
  140. package/dist/guards/index.js +6 -6
  141. package/dist/history/index.js +12 -12
  142. package/dist/i18n/index.js +14 -14
  143. package/dist/index.d.ts +6 -3
  144. package/dist/index.js +140 -117
  145. package/dist/index.js.map +1 -1
  146. package/dist/indexing/index.js +2 -2
  147. package/dist/issue-LSR5PS6H.js +19 -0
  148. package/dist/kernel/enclave/index.d.ts +1 -1
  149. package/dist/kernel/enclave/record-keys/envelope-body.d.ts +8 -0
  150. package/dist/kernel/errors.d.ts +10 -5
  151. package/dist/kernel/noydb.d.ts +21 -11
  152. package/dist/kernel/types.d.ts +53 -6
  153. package/dist/kernel/vault.d.ts +10 -6
  154. package/dist/{ledger-GFBKVPV4.js → ledger-33BC4TR7.js} +11 -11
  155. package/dist/liberate-GBSAYLPJ.js +24 -0
  156. package/dist/{link-set-Z7Q2ASFX.js → link-set-QK22TDEX.js} +11 -11
  157. package/dist/materialized-views/index.js +19 -18
  158. package/dist/noydb-MTUBMWFE.js +70 -0
  159. package/dist/on/index.js +9 -9
  160. package/dist/overlay-views/index.js +4 -4
  161. package/dist/periods/index.js +19 -17
  162. package/dist/pod/index.js +13 -11
  163. package/dist/{policy-Z3RUAIY2.js → policy-NAHB2TUT.js} +5 -5
  164. package/dist/portability/index.js +8 -8
  165. package/dist/{post-register-JTH76BLY.js → post-register-JVTMJ6OW.js} +6 -6
  166. package/dist/query/index.js +10 -8
  167. package/dist/register-X462DNDM.js +23 -0
  168. package/dist/registry-3XMCT7MQ.js +18 -0
  169. package/dist/registry-KHD2I3M2.js +19 -0
  170. package/dist/registry-NPOWJHC4.js +9 -0
  171. package/dist/request-withdrawal-U7K3JTMY.js +31 -0
  172. package/dist/{reveal-DIBHLUZX.js → reveal-LGBOM2BO.js} +6 -6
  173. package/dist/revoke-OCWDSWRQ.js +24 -0
  174. package/dist/satellites/index.js +1 -1
  175. package/dist/sealed-record/index.js +12 -12
  176. package/dist/{seed-Q6M2IZFL.js → seed-PWK45HDF.js} +11 -11
  177. package/dist/session/index.js +9 -9
  178. package/dist/shadow/index.js +2 -2
  179. package/dist/share-link/index.d.ts +19 -0
  180. package/dist/share-link/index.js +201 -0
  181. package/dist/share-link/index.js.map +1 -0
  182. package/dist/share-link/share-link.d.ts +123 -0
  183. package/dist/signer-3YFBLZRN.js +25 -0
  184. package/dist/snapshots/index.js +10 -10
  185. package/dist/{stale-OSQ7J3AA.js → stale-FJED42DM.js} +10 -10
  186. package/dist/storage-W3GURVFU.js +23 -0
  187. package/dist/{store-coordination-provider-64OEZFGS.js → store-coordination-provider-APODTV4W.js} +3 -3
  188. package/dist/sync/index.js +10 -9
  189. package/dist/sync/index.js.map +1 -1
  190. package/dist/team/index.js +16 -15
  191. package/dist/tiers/index.js +10 -10
  192. package/dist/to/index.js +1 -1
  193. package/dist/tx/index.js +3 -3
  194. package/dist/util/index.js +1 -1
  195. package/dist/{verify-ZASTRNPR.js → verify-26Y5GNMK.js} +5 -5
  196. package/dist/via/i18n/policy.d.ts +1 -1
  197. package/dist/{walk-SVNDIZ62.js → walk-XS3PKM6I.js} +11 -11
  198. package/dist/with/index.js +11 -11
  199. package/dist/with-audit/periods/periods.d.ts +1 -16
  200. package/dist/with-audit/periods/window.d.ts +16 -0
  201. package/dist/with-cargo/index.d.ts +6 -0
  202. package/dist/with-formula/materialized-views/dependency-analyzer.d.ts +15 -0
  203. package/dist/with-formula/materialized-views/index.d.ts +1 -1
  204. package/dist/with-formula/materialized-views/registry.d.ts +29 -0
  205. package/dist/with-formula/materialized-views/types.d.ts +81 -0
  206. package/dist/with-formula/materialized-views/with-materialized-view.d.ts +9 -5
  207. package/dist/with-party/directory/cover/custom.d.ts +34 -0
  208. package/dist/with-party/directory/cover/index.d.ts +19 -0
  209. package/dist/with-party/directory/cover/schema.d.ts +39 -0
  210. package/dist/with-party/directory/cover/storage.d.ts +71 -0
  211. package/dist/with-party/directory/cover/types.d.ts +149 -0
  212. package/dist/with-party/team/noydb-facade.d.ts +1 -1
  213. package/dist/with-party/team/sync-period-scope.d.ts +78 -0
  214. package/dist/with-party/team/sync.d.ts +13 -0
  215. package/dist/with-pod/bundle.d.ts +9 -7
  216. package/dist/with-pod/format.d.ts +8 -4
  217. package/dist/with-pod/index.d.ts +2 -1
  218. package/dist/withdraw-accessible-Y3B2535F.js +28 -0
  219. package/package.json +7 -3
  220. package/dist/api-ZGS6LCOF.js +0 -20
  221. package/dist/chunk-454JXE57.js.map +0 -1
  222. package/dist/chunk-4NXKCLLQ.js +0 -97
  223. package/dist/chunk-4NXKCLLQ.js.map +0 -1
  224. package/dist/chunk-6JTIAGHD.js.map +0 -1
  225. package/dist/chunk-DLG74CNB.js.map +0 -1
  226. package/dist/chunk-FHS2HQSH.js.map +0 -1
  227. package/dist/chunk-FM3G3NOY.js.map +0 -1
  228. package/dist/chunk-HDMEGAQE.js.map +0 -1
  229. package/dist/chunk-JNAI2HN2.js.map +0 -1
  230. package/dist/chunk-KSKBRKGJ.js.map +0 -1
  231. package/dist/chunk-VLFCJYTL.js.map +0 -1
  232. package/dist/chunk-WXSYDNBT.js +0 -39
  233. package/dist/chunk-WXSYDNBT.js.map +0 -1
  234. package/dist/chunk-X3YYQWOQ.js +0 -155
  235. package/dist/chunk-X3YYQWOQ.js.map +0 -1
  236. package/dist/chunk-X7QBD67R.js.map +0 -1
  237. package/dist/collection-facade-TLWIPI76.js +0 -44
  238. package/dist/delegation-W7ZV2322.js +0 -25
  239. package/dist/derive-H2BJTLUI.js +0 -21
  240. package/dist/executor-KGWTNEQD.js +0 -9
  241. package/dist/executor-OVEXR6RT.js +0 -9
  242. package/dist/executor-UNEH5ZYH.js +0 -25
  243. package/dist/export-accessible-PCMXSUZS.js +0 -23
  244. package/dist/extract-partition-SWHFSYJO.js +0 -38
  245. package/dist/find-N6345CNT.js +0 -11
  246. package/dist/issue-5OKB6I2R.js +0 -19
  247. package/dist/liberate-3VDOT4KO.js +0 -24
  248. package/dist/noydb-ZCJOYBDI.js +0 -68
  249. package/dist/register-VAHONCID.js +0 -23
  250. package/dist/registry-7MLVD3VO.js +0 -19
  251. package/dist/registry-EPKPD5SM.js +0 -18
  252. package/dist/registry-P5T7PS2U.js +0 -9
  253. package/dist/request-withdrawal-TI3A5WPS.js +0 -31
  254. package/dist/revoke-FDRD6GRW.js +0 -24
  255. package/dist/signer-MUWEIUI6.js +0 -25
  256. package/dist/storage-MVFCGEQE.js +0 -23
  257. package/dist/with-party/directory/public-envelope/index.d.ts +0 -12
  258. package/dist/with-party/directory/public-envelope/schema.d.ts +0 -24
  259. package/dist/with-party/directory/public-envelope/storage.d.ts +0 -59
  260. package/dist/with-party/directory/public-envelope/types.d.ts +0 -80
  261. package/dist/withdraw-accessible-WZIEVWSR.js +0 -28
  262. /package/dist/{api-ZGS6LCOF.js.map → api-IQFZJSQO.js.map} +0 -0
  263. /package/dist/{backup-BJ57IKAO.js.map → backup-LU2DTZTI.js.map} +0 -0
  264. /package/dist/{chunk-EVVGJDRQ.js.map → chunk-2MUZ3LY6.js.map} +0 -0
  265. /package/dist/{chunk-NWSGI5C7.js.map → chunk-2P7EGNKF.js.map} +0 -0
  266. /package/dist/{chunk-ZAJAVEXY.js.map → chunk-2PCAVW2I.js.map} +0 -0
  267. /package/dist/{chunk-HTJQYHZP.js.map → chunk-3ELCWU56.js.map} +0 -0
  268. /package/dist/{chunk-J2ZX2XUV.js.map → chunk-3SV2EPMN.js.map} +0 -0
  269. /package/dist/{chunk-FUG4SFN3.js.map → chunk-3U3FBR7O.js.map} +0 -0
  270. /package/dist/{chunk-VFUPHI2Z.js.map → chunk-4BRCIYFG.js.map} +0 -0
  271. /package/dist/{chunk-SNZ6URZZ.js.map → chunk-5OI6BR4H.js.map} +0 -0
  272. /package/dist/{chunk-B3HJYUOF.js.map → chunk-5WZXVULX.js.map} +0 -0
  273. /package/dist/{chunk-LASZKVMO.js.map → chunk-6B4DYGWO.js.map} +0 -0
  274. /package/dist/{chunk-ZJ2AK4V7.js.map → chunk-6HPG2YNX.js.map} +0 -0
  275. /package/dist/{chunk-5M5DVGV7.js.map → chunk-6HY2X62D.js.map} +0 -0
  276. /package/dist/{chunk-QRB7NAOJ.js.map → chunk-6NQMXTYC.js.map} +0 -0
  277. /package/dist/{chunk-RIX4V2TB.js.map → chunk-6XE5TMPR.js.map} +0 -0
  278. /package/dist/{chunk-BDVFPG6Y.js.map → chunk-7CI6CP7Z.js.map} +0 -0
  279. /package/dist/{chunk-OSDB5WW3.js.map → chunk-BMQWTQZ4.js.map} +0 -0
  280. /package/dist/{chunk-W22IKJE2.js.map → chunk-BOKIKJJB.js.map} +0 -0
  281. /package/dist/{chunk-CRI4OUV4.js.map → chunk-CBSY2N76.js.map} +0 -0
  282. /package/dist/{chunk-2NVOWNI3.js.map → chunk-CCOP5XDW.js.map} +0 -0
  283. /package/dist/{chunk-CDUQTA3Z.js.map → chunk-DDATL6PU.js.map} +0 -0
  284. /package/dist/{chunk-RCVJZMFE.js.map → chunk-DDHM2TLX.js.map} +0 -0
  285. /package/dist/{chunk-PN2XMATY.js.map → chunk-DRWBEDYO.js.map} +0 -0
  286. /package/dist/{chunk-T2FFWNPK.js.map → chunk-EGCRBCNA.js.map} +0 -0
  287. /package/dist/{chunk-VZRODIM3.js.map → chunk-F6T6L2UK.js.map} +0 -0
  288. /package/dist/{chunk-EXK7QRWF.js.map → chunk-G6EQYAL6.js.map} +0 -0
  289. /package/dist/{chunk-F64IRYG3.js.map → chunk-GQNGCEP5.js.map} +0 -0
  290. /package/dist/{chunk-ZUODRVHY.js.map → chunk-GVZQ4NAM.js.map} +0 -0
  291. /package/dist/{chunk-3B5VVCNY.js.map → chunk-GWKAVKHI.js.map} +0 -0
  292. /package/dist/{chunk-SLFHQTTW.js.map → chunk-GYYCX5K6.js.map} +0 -0
  293. /package/dist/{chunk-CIE2BASH.js.map → chunk-HOONYZY2.js.map} +0 -0
  294. /package/dist/{chunk-EYZ6IQ6X.js.map → chunk-HPUWOVR7.js.map} +0 -0
  295. /package/dist/{chunk-IL6POWCW.js.map → chunk-IM3QVF53.js.map} +0 -0
  296. /package/dist/{chunk-NJ4EOKMB.js.map → chunk-IRQY7IR5.js.map} +0 -0
  297. /package/dist/{chunk-3QCRBHCY.js.map → chunk-ISMV2QVG.js.map} +0 -0
  298. /package/dist/{chunk-HB3RFQSC.js.map → chunk-IY23KFNZ.js.map} +0 -0
  299. /package/dist/{chunk-6CRIV7DE.js.map → chunk-JBBSYJHP.js.map} +0 -0
  300. /package/dist/{chunk-MEFJPTZ4.js.map → chunk-JRBNCUNT.js.map} +0 -0
  301. /package/dist/{chunk-4VNQOLML.js.map → chunk-JVBO4SZL.js.map} +0 -0
  302. /package/dist/{chunk-I6GALP6E.js.map → chunk-JZWWLLVY.js.map} +0 -0
  303. /package/dist/{chunk-RYCTDFUX.js.map → chunk-KUIB4B5Q.js.map} +0 -0
  304. /package/dist/{chunk-HDQIIUPH.js.map → chunk-KUPKLSX5.js.map} +0 -0
  305. /package/dist/{chunk-5O3XF4DV.js.map → chunk-KWVJAOIN.js.map} +0 -0
  306. /package/dist/{chunk-3LQ2YKO4.js.map → chunk-LB6OH6K2.js.map} +0 -0
  307. /package/dist/{chunk-AUSTCW2X.js.map → chunk-LOF2W3JU.js.map} +0 -0
  308. /package/dist/{chunk-K5IMOGHB.js.map → chunk-M75ZZHSR.js.map} +0 -0
  309. /package/dist/{chunk-GTGPO5KW.js.map → chunk-MQ6WOPSY.js.map} +0 -0
  310. /package/dist/{chunk-4CTHCN4J.js.map → chunk-MTQPRQHB.js.map} +0 -0
  311. /package/dist/{chunk-UNFPPVNI.js.map → chunk-N55W7KUL.js.map} +0 -0
  312. /package/dist/{chunk-JKJOGPZN.js.map → chunk-N6WF5PKC.js.map} +0 -0
  313. /package/dist/{chunk-6T2SCQRU.js.map → chunk-O7WJ47EF.js.map} +0 -0
  314. /package/dist/{chunk-JIF24JPM.js.map → chunk-OF75ACKS.js.map} +0 -0
  315. /package/dist/{chunk-GKEWXIV5.js.map → chunk-ONTBCHCP.js.map} +0 -0
  316. /package/dist/{chunk-4JBYV75E.js.map → chunk-PK6QCH2C.js.map} +0 -0
  317. /package/dist/{chunk-6QTNZJBS.js.map → chunk-PT37L5DV.js.map} +0 -0
  318. /package/dist/{chunk-EVX47VBM.js.map → chunk-QDPZWTN2.js.map} +0 -0
  319. /package/dist/{chunk-QLWMEXCT.js.map → chunk-RDXW3OBQ.js.map} +0 -0
  320. /package/dist/{chunk-Q5VNKNE5.js.map → chunk-RM2XV574.js.map} +0 -0
  321. /package/dist/{chunk-ERJ7FU55.js.map → chunk-RND5ZPNP.js.map} +0 -0
  322. /package/dist/{chunk-JZUV47FZ.js.map → chunk-RTEK4C4I.js.map} +0 -0
  323. /package/dist/{chunk-5YOLBEQ6.js.map → chunk-S6Q37VGE.js.map} +0 -0
  324. /package/dist/{chunk-E5ZQT5KX.js.map → chunk-SMIRGMS7.js.map} +0 -0
  325. /package/dist/{chunk-EQO2UVP4.js.map → chunk-T6GU5PPZ.js.map} +0 -0
  326. /package/dist/{chunk-LXEXBTKL.js.map → chunk-TAGR72IR.js.map} +0 -0
  327. /package/dist/{chunk-YFXZEAYK.js.map → chunk-TOPYT5U5.js.map} +0 -0
  328. /package/dist/{chunk-MV43JSJF.js.map → chunk-UBNCR3YK.js.map} +0 -0
  329. /package/dist/{chunk-ZICD77GM.js.map → chunk-UC2ETWF3.js.map} +0 -0
  330. /package/dist/{chunk-XWZVFISC.js.map → chunk-UFEVO57O.js.map} +0 -0
  331. /package/dist/{chunk-NR5LT5RY.js.map → chunk-UNCOC7QH.js.map} +0 -0
  332. /package/dist/{chunk-R4ROFEYC.js.map → chunk-VAIS3SOM.js.map} +0 -0
  333. /package/dist/{chunk-VN3DBQAU.js.map → chunk-VCBHAE4R.js.map} +0 -0
  334. /package/dist/{chunk-M4CV6BRT.js.map → chunk-VEALDPHW.js.map} +0 -0
  335. /package/dist/{chunk-7DSTRQIJ.js.map → chunk-WEYZTLXN.js.map} +0 -0
  336. /package/dist/{chunk-CAF6WOK7.js.map → chunk-WNGGJWF7.js.map} +0 -0
  337. /package/dist/{chunk-IU6SNYX6.js.map → chunk-YJV2UZ7H.js.map} +0 -0
  338. /package/dist/{chunk-RZ5SVOYR.js.map → chunk-YTGDPCPW.js.map} +0 -0
  339. /package/dist/{classified-marker-CZKA6ITE.js.map → classified-marker-WQVIJAW2.js.map} +0 -0
  340. /package/dist/{collection-facade-TLWIPI76.js.map → collection-facade-MJXSC3Q6.js.map} +0 -0
  341. /package/dist/{computed-ZIQTYTDI.js.map → computed-DSMPXG2Y.js.map} +0 -0
  342. /package/dist/{delegation-W7ZV2322.js.map → cover-B5SJDHR3.js.map} +0 -0
  343. /package/dist/{dead-filter-ESOBZZP5.js.map → dead-filter-2A7IAJ3D.js.map} +0 -0
  344. /package/dist/{derive-H2BJTLUI.js.map → delegation-A5A75KQO.js.map} +0 -0
  345. /package/dist/{enclave-AP3I5W7O.js.map → derive-HJSMH7RA.js.map} +0 -0
  346. /package/dist/{executor-KGWTNEQD.js.map → enclave-7MW6ZGXW.js.map} +0 -0
  347. /package/dist/{executor-OVEXR6RT.js.map → executor-AXE4XMLT.js.map} +0 -0
  348. /package/dist/{executor-UNEH5ZYH.js.map → executor-KGNLJD6M.js.map} +0 -0
  349. /package/dist/{export-accessible-PCMXSUZS.js.map → executor-XGIS6CR7.js.map} +0 -0
  350. /package/dist/{extract-partition-SWHFSYJO.js.map → export-accessible-VVBJM3MM.js.map} +0 -0
  351. /package/dist/{find-N6345CNT.js.map → extract-partition-LGGBTQET.js.map} +0 -0
  352. /package/dist/{fanout-sidecar-A765THMX.js.map → fanout-sidecar-WDYQ77VB.js.map} +0 -0
  353. /package/dist/{issue-5OKB6I2R.js.map → find-YS6XWB46.js.map} +0 -0
  354. /package/dist/{ledger-GFBKVPV4.js.map → issue-LSR5PS6H.js.map} +0 -0
  355. /package/dist/{liberate-3VDOT4KO.js.map → ledger-33BC4TR7.js.map} +0 -0
  356. /package/dist/{link-set-Z7Q2ASFX.js.map → liberate-GBSAYLPJ.js.map} +0 -0
  357. /package/dist/{noydb-ZCJOYBDI.js.map → link-set-QK22TDEX.js.map} +0 -0
  358. /package/dist/{policy-Z3RUAIY2.js.map → noydb-MTUBMWFE.js.map} +0 -0
  359. /package/dist/{public-envelope-B4KNWVUA.js.map → policy-NAHB2TUT.js.map} +0 -0
  360. /package/dist/{post-register-JTH76BLY.js.map → post-register-JVTMJ6OW.js.map} +0 -0
  361. /package/dist/{register-VAHONCID.js.map → register-X462DNDM.js.map} +0 -0
  362. /package/dist/{registry-7MLVD3VO.js.map → registry-3XMCT7MQ.js.map} +0 -0
  363. /package/dist/{registry-EPKPD5SM.js.map → registry-KHD2I3M2.js.map} +0 -0
  364. /package/dist/{registry-P5T7PS2U.js.map → registry-NPOWJHC4.js.map} +0 -0
  365. /package/dist/{request-withdrawal-TI3A5WPS.js.map → request-withdrawal-U7K3JTMY.js.map} +0 -0
  366. /package/dist/{reveal-DIBHLUZX.js.map → reveal-LGBOM2BO.js.map} +0 -0
  367. /package/dist/{revoke-FDRD6GRW.js.map → revoke-OCWDSWRQ.js.map} +0 -0
  368. /package/dist/{seed-Q6M2IZFL.js.map → seed-PWK45HDF.js.map} +0 -0
  369. /package/dist/{signer-MUWEIUI6.js.map → signer-3YFBLZRN.js.map} +0 -0
  370. /package/dist/{stale-OSQ7J3AA.js.map → stale-FJED42DM.js.map} +0 -0
  371. /package/dist/{storage-MVFCGEQE.js.map → storage-W3GURVFU.js.map} +0 -0
  372. /package/dist/{store-coordination-provider-64OEZFGS.js.map → store-coordination-provider-APODTV4W.js.map} +0 -0
  373. /package/dist/{verify-ZASTRNPR.js.map → verify-26Y5GNMK.js.map} +0 -0
  374. /package/dist/{walk-SVNDIZ62.js.map → walk-XS3PKM6I.js.map} +0 -0
  375. /package/dist/{withdraw-accessible-WZIEVWSR.js.map → withdraw-accessible-Y3B2535F.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-formula/materialized-views/dependency-analyzer.ts","../src/with-formula/materialized-views/query-hash.ts","../src/with-formula/materialized-views/registry.ts"],"sourcesContent":["import type { Query, QueryPlan } from '../../kernel/query/builder.js'\nimport type { JoinContext } from '../../kernel/query/join.js'\nimport type { MaterializedViewStrategy } from './types.js'\n\n/**\n * Walks a `Query<T>` plan and returns the set of source collection\n * names that any source-write should trigger a refresh on.\n *\n * Handles:\n * - root collection (the one the query was built from)\n * - FK join targets (`.join(field, { as })`)\n *\n * Also handles:\n * - cross-join targets (`.crossJoin(target, { as })`) — v3\n *\n * Deferred:\n * - `.wherePredicate(name)` — v2 predicate primitive\n * - Overlay-name expansion to {base, overlay}\n *\n * The set is materialized at MV registration time. The MV registry\n * uses it to (a) dispatch `onSourceWrite` only to MVs that actually\n * care, and (b) contribute edges to the shared cycle-detection graph.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function analyzeDependencies(query: Query<any>): Set<string> {\n const deps = new Set<string>()\n const plan = query._plan()\n const ctx = query._joinContext()\n\n // The root collection is always a dependency.\n if (ctx?.leftCollection) {\n deps.add(ctx.leftCollection)\n }\n\n // FK join targets contribute additional sources.\n for (const leg of plan.joins) {\n deps.add(leg.target)\n }\n\n // Cross-join targets are also dependency sources — writes to either side\n // must trigger MV refresh. Symmetric with FK-join target handling above.\n for (const clause of plan.clauses) {\n if (clause.type === 'crossJoin') {\n deps.add(clause.target)\n }\n }\n\n // Sub-plans inside OR clauses can carry nested joins. Walk them.\n // (Today only top-level `.join()` populates `plan.joins`, but the\n // OR-group machinery permits sub-plans, so we recurse defensively.)\n walkClausesForJoins(plan, deps, ctx)\n\n return deps\n}\n\nfunction walkClausesForJoins(\n plan: QueryPlan,\n deps: Set<string>,\n ctx: JoinContext | undefined,\n): void {\n void ctx\n // Today `plan.joins` carries all join legs at top level. Sub-plans\n // inside OR groups don't currently support nested joins, so the loop\n // below is a no-op safety net for future builder extensions.\n for (const clause of plan.clauses) {\n if (clause.type === 'group') {\n // Group clauses don't (yet) carry their own joins; this is a\n // forward-compat anchor for when OR-groups support nested\n // sources.\n }\n }\n}\n\n/**\n * Convenience: produce a stable string summary of the query plan\n * suitable for `queryHash` derivation. Captures everything the\n * dependency analyzer reads + the where/orderBy/limit/offset\n * structure that affects materialized rows.\n *\n * `joinContext` is intentionally NOT included — the join-resolution\n * function references would defeat hash determinism. The set of join\n * TARGETS (collection names) IS included via the plan.joins legs.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function summarizeQueryPlan(query: Query<any>): string {\n const plan = query._plan()\n const ctx = query._joinContext()\n return JSON.stringify({\n root: ctx?.leftCollection ?? null,\n clauses: plan.clauses.map(c => {\n if (c.type === 'crossJoin') {\n return {\n type: 'crossJoin',\n target: c.target,\n as: c.as,\n // Inline on: callback: use sentinel — drift detection disabled for this MV\n onPredicateName: c.onPredicateName ?? (c.on ? '[inline]' : null),\n maxRows: c.maxRows ?? null,\n }\n }\n return c\n }),\n orderBy: plan.orderBy,\n limit: plan.limit ?? null,\n offset: plan.offset,\n joins: plan.joins.map(j => ({ field: j.field, as: j.as, target: j.target, mode: j.mode })),\n })\n}\n\n/**\n * Canonical string description of a UNION MV's plan, used as input to\n * `computeQueryHash`.\n *\n * Asymmetry note:\n * - Arm collection names are NOT sorted. Declaration order is\n * semantically meaningful for the dedup-only UNION path —\n * `materializeUnionResult` iterates `spec.unionSources` in\n * declaration order and keeps the first-seen row per composite key\n * (tie-break precedence). If we sorted arms here, a consumer who\n * reordered `unionSources` to change precedence would compute the\n * same `queryHash`, refresh would be a no-op, and stale MV rows\n * would persist. Hashing in declaration order makes any reorder\n * trigger a refresh.\n * - `groupBy` fields ARE sorted. Multi-key groupBy buckets are\n * commutative (`canonicalGroupKey` produces the same composite key\n * regardless of field order in the input spec).\n * - `aggregate` keys ARE sorted. Reducer-spec keys are independent\n * of each other — order of declaration doesn't change output.\n *\n * Per-arm `map` functions are NOT fingerprinted; consumers must bump\n * the MV's `name` (or rely on application-level cache busting) when\n * `map` semantics change non-equivalently.\n */\nexport function summarizeUnionPlan<T extends Record<string, unknown>>(\n spec: MaterializedViewStrategy<T>,\n): string {\n const arms = (spec.unionSources ?? [])\n .map(s => {\n // Fold each arm's join legs into the summary so adding or\n // reordering joins (which changes the materialized rows) bumps\n // queryHash. Leg order is declaration-significant (legs chain), so\n // it is NOT sorted — same rationale as arm declaration order.\n const joins = s.join?.length\n ? `[${s.join.map(j => `${j.field}→${j.as}`).join(';')}]`\n : ''\n return `${s.collection}${joins}`\n })\n .join(',')\n const groupBy: string = Array.isArray(spec.groupBy)\n ? [...spec.groupBy].sort().join(',')\n : typeof spec.groupBy === 'string'\n ? spec.groupBy\n : ''\n const aggKeys = spec.aggregate ? Object.keys(spec.aggregate).sort().join(',') : ''\n // `moneyFields` changes reducer semantics (float → exact BigInt), so\n // declaring / removing / re-keying it must bump queryHash. Keys are\n // sorted — they're independent of each other (one descriptor per\n // output field), same rationale as aggregate keys.\n const moneyKeys = spec.moneyFields ? Object.keys(spec.moneyFields).sort().join(',') : ''\n return `union(${arms})|groupBy(${groupBy})|aggregate(${aggKeys})|money(${moneyKeys})`\n}\n","/**\n * Deterministic hash of a materialized view strategy's \"shape\": MV\n * name + canonical query-plan summary + sorted dependency-set.\n *\n * Used to detect strategy drift: a row whose `_materializedFrom.queryHash`\n * doesn't match the current strategy is considered stale.\n *\n * Web Crypto SHA-256 — no extra deps. Mirrors the v1\n * `computeStrategyHash` pattern.\n */\nimport { sha256Hex } from '../../kernel/enclave/index.js'\n\nexport async function computeQueryHash(\n mvName: string,\n /**\n * Source-collection set the query depends on. Sorted before\n * canonicalization so set iteration order doesn't affect the hash.\n */\n dependencies: ReadonlySet<string>,\n /**\n * Stringified query-plan summary. The caller produces this from the\n * `Query<T>` builder — concretely: a JSON serialization of clauses +\n * orderBy + limit + offset + joins. Function bodies inside\n * `wherePredicate` are NOT included here (those carry their own\n * `predicateHash` to be folded in by a later sub-issue).\n */\n queryPlanSummary: string,\n): Promise<string> {\n const canonical = JSON.stringify({\n mvName,\n dependencies: [...dependencies].sort(),\n queryPlanSummary,\n })\n const bytes = new TextEncoder().encode(canonical)\n return sha256Hex(bytes)\n}\n\n/**\n * Canonicalize a query plan for hashing. Walks the plan structure\n * with sorted keys so insertion order doesn't perturb the result.\n * Lives here rather than in `query/builder.ts` to keep that module\n * stable across MV-specific evolutions.\n *\n * @internal exported for testing\n */\nexport function canonicalizeQueryPlan(plan: unknown): string {\n return JSON.stringify(plan, (_key, value) => {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const sorted: Record<string, unknown> = {}\n for (const k of Object.keys(value as Record<string, unknown>).sort()) {\n sorted[k] = (value as Record<string, unknown>)[k]\n }\n return sorted\n }\n return value\n })\n}\n","import { MaterializedViewSourceUnknownError } from '../../kernel/errors.js'\nimport type { ViaGraph, FieldRef, EdgeKind, Grain } from '../../kernel/via/graph.js'\nimport type { Clause, FieldClause } from '../../kernel/query/predicate.js'\nimport type { DeclaredPredicate } from '../../kernel/query/builder.js'\nimport { analyzeDependencies, summarizeQueryPlan, summarizeUnionPlan } from './dependency-analyzer.js'\nimport { computeQueryHash } from './query-hash.js'\nimport type { MaterializedViewStrategy, MVQueryContext } from './types.js'\n\n/**\n * Whole-record artifact-grain field marker (#638 Task 2) — MUST match\n * `derivations/registry.ts`'s and `vault.ts`'s overlay-edge marker so\n * cross-registry edges (a derivation feeding an MV, or vice versa) resolve\n * to the SAME graph node. See that file's `WHOLE_RECORD` doc comment.\n */\nconst WHOLE_RECORD = '*'\n\n/**\n * One registered MV strategy alongside its derived metadata. Stored\n * type-erased on `TRow` so the registry can hold heterogeneous MVs.\n */\nexport interface RegisteredMV {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly spec: MaterializedViewStrategy<any>\n /** Output collection name (`spec.output?.collection ?? spec.name`). */\n readonly outputCollection: string\n /** Set of source collections; populated at registration via the analyzer. */\n readonly dependencies: ReadonlySet<string>\n /** Canonical `queryHash` — `_materializedFrom.queryHash` for every emitted row. */\n readonly queryHash: string\n /**\n * Top-level FieldClauses on the partition field, captured at\n * registration time. Used by the cycle detector to resolve\n * same-collection-as-source edges via the partition-discriminator\n * check. Empty when `spec.output?.partition` is undefined.\n */\n readonly partitionClauses: readonly FieldClause[]\n /**\n * #638 Task 2 — `'record'` for a row-per-source-row Query<T> or\n * `unionSources` MV, `'aggregate'` for a `.groupBy().aggregate()` MV\n * (the shape that requires explicit `sources`, since the dependency\n * analyzer can't walk an aggregate plan). Feeds `edges()`'s grain.\n */\n readonly grain: Grain\n}\n\n/**\n * Vault-internal registry of MV strategies. Owned by `Vault`; not\n * exported. Parallel to v1's `DerivationRegistry`; the two graphs share\n * a single cycle-detection pass at vault open (see `validate`).\n *\n * @internal\n */\nexport class MaterializedViewRegistry {\n /** Keyed by `spec.name`. */\n private readonly _byName = new Map<string, RegisteredMV>()\n /** Keyed by dependency source-collection → MVs that depend on it. */\n private readonly _bySource = new Map<string, RegisteredMV[]>()\n\n /**\n * Register an MV. Invokes `spec.query()` once at registration time to\n * read the plan + join context; the resulting `Query<T>` is discarded\n * after dependency extraction. `vault.collection(...)` must therefore\n * be functional by the time this runs — typically wired from\n * `Vault._initMaterializedViews` after collection bootstrap.\n *\n * Throws `MaterializedViewSourceUnknownError` if the analyzer\n * surfaces a dependency the vault doesn't know about (when a\n * `knownCollections` checker is supplied).\n */\n async register(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n spec: MaterializedViewStrategy<any>,\n db: MVQueryContext,\n options?: { knownCollections?: (name: string) => boolean },\n ): Promise<void> {\n // Build a predicate-aware db wrapper. If `spec.predicates` is\n // declared, the wrapper intercepts `.collection().query()` and\n // attaches the predicates map to the resulting Query<T>. With no\n // predicates declared, the wrapper is the original db unchanged.\n const dbForQuery = spec.predicates ? wrapDbWithPredicates(db, spec.predicates) : db\n\n // Invoke the query callback once to inspect its plan / dependencies.\n // For Query<T> shapes the analyzer extracts deps + plan summary\n // automatically. Aggregation / GroupedAggregation shapes don't\n // expose the underlying Query, so the spec must declare `sources`\n // explicitly. `partitionClauses` are only populated for Query<T>\n // since same-collection-partition is a non-aggregate concern.\n // UNION-form strategies: dependencies and plan summary come\n // straight off the strategy — no `query` callback to introspect.\n // The dependency-analyzer + summarizer are bypassed entirely; the\n // executor handles materialization via `materializeUnionResult`.\n let dependencies: Set<string>\n let queryPlanSummary: string\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let qAny: any = null\n let isQuery = false\n if (spec.unionSources) {\n dependencies = new Set(spec.unionSources.map(s => s.collection))\n // Per-arm joins resolve right-side collections that aren't among\n // the arm `collection`s. The consumer lists those in `sources`;\n // fold them into the dependency set so a write to a join-target\n // collection triggers MV refresh (and contributes a cycle edge).\n if (spec.sources) for (const s of spec.sources) dependencies.add(s)\n queryPlanSummary = summarizeUnionPlan(spec)\n } else {\n const q = spec.query!(dbForQuery)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n qAny = q as any\n isQuery = typeof qAny._plan === 'function'\n if (isQuery) {\n // `q` is the `Query` arm of the union here (runtime-confirmed via\n // `qAny._plan` above); reuse the already-`any` `qAny` the block uses\n // for `_plan()` rather than re-narrowing.\n dependencies = analyzeDependencies(qAny)\n queryPlanSummary = summarizeQueryPlan(qAny)\n // Fold `.wherePredicate(name, ctx)` references into the plan\n // summary so predicate function or ctx changes (signalled by\n // bumping `hash` or supplying a different ctx) propagate into\n // `queryHash` and force refresh on next visit.\n const predicateRefs = extractPredicateRefs(qAny._plan())\n if (predicateRefs.length > 0) {\n queryPlanSummary = JSON.stringify({ plan: queryPlanSummary, predicates: predicateRefs })\n }\n // If `sources` is ALSO declared, take the union (consumer's\n // explicit list extends the auto-analyzed set).\n if (spec.sources) for (const s of spec.sources) dependencies.add(s)\n } else {\n // Aggregate shape: require explicit `sources`.\n if (!spec.sources || spec.sources.length === 0) {\n throw new Error(\n `withMaterializedView \"${spec.name}\": query() returned an aggregate ` +\n `(Aggregation or GroupedAggregation) but no \\`sources\\` field is declared. ` +\n `The dependency analyzer cannot walk through groupBy().aggregate() ` +\n `back to the source — declare sources: [...] explicitly.`,\n )\n }\n dependencies = new Set(spec.sources)\n // Aggregate plans don't carry a chainable query plan for summary\n // purposes; the dep-set + spec.name serve as the queryHash inputs.\n queryPlanSummary = JSON.stringify({ aggregate: true, sources: [...spec.sources].sort() })\n }\n }\n\n // Sanity-check declared dependencies against the vault's known\n // collections. Optional — when the checker isn't supplied (test\n // wiring, in-process composition) the registration succeeds and\n // any typo surfaces at first onSourceWrite as a no-op.\n if (options?.knownCollections) {\n for (const dep of dependencies) {\n if (!options.knownCollections(dep)) {\n throw new MaterializedViewSourceUnknownError(spec.name, dep)\n }\n }\n }\n\n const outputCollection = spec.output?.collection ?? spec.name\n const queryHash = await computeQueryHash(spec.name, dependencies, queryPlanSummary)\n // For same-collection-as-source MVs, capture the where-clauses on\n // the partition field so cycle detection can prove disjointness.\n // Only applicable to Query<T> shapes — aggregate MVs don't carry\n // a chainable plan to inspect (and same-collection aggregation\n // doesn't make sense for same-collection aggregation).\n const partitionClauses: FieldClause[] = []\n const partitionField = spec.output?.partition?.field\n if (partitionField !== undefined && isQuery) {\n const plan = qAny._plan()\n for (const clause of plan.clauses) {\n if (isFieldClauseOnField(clause, partitionField)) partitionClauses.push(clause)\n }\n }\n // #638 Task 2 — 'aggregate' only for the explicit-sources aggregate shape\n // (groupBy().aggregate() with no chainable plan); a row-per-source-row\n // Query<T> or a unionSources MV is 'record'.\n const grain: Grain = spec.unionSources || isQuery ? 'record' : 'aggregate'\n const reg: RegisteredMV = { spec, outputCollection, dependencies, queryHash, partitionClauses, grain }\n\n this._byName.set(spec.name, reg)\n for (const dep of dependencies) {\n const arr = this._bySource.get(dep)\n if (arr) arr.push(reg)\n else this._bySource.set(dep, [reg])\n }\n }\n\n /** All MVs that depend on `source`, in registration order. */\n mvsForSource(source: string): ReadonlyArray<RegisteredMV> {\n return this._bySource.get(source) ?? []\n }\n\n /** Single MV by name, or `undefined`. */\n byName(name: string): RegisteredMV | undefined {\n return this._byName.get(name)\n }\n\n /** Iterate over every registered MV. */\n all(): ReadonlyArray<RegisteredMV> {\n return [...this._byName.values()]\n }\n\n /**\n * Graph edges for #638 Task 2: one `'mv'` edge per registered MV — target\n * = the output collection (a `WHOLE_RECORD` artifact node), sources =\n * every dependency (also `WHOLE_RECORD` nodes). Same-collection edges\n * (`dep === outputCollection`) are skipped IFF the MV declares an\n * `output.partition` discriminator AND the query has a where-clause that\n * provably excludes the partition value (`partitionDisjoint`) — the SAME\n * condition the old local DFS used. Grain comes from the registration-time\n * `RegisteredMV.grain`.\n */\n edges(): ReadonlyArray<{ readonly target: FieldRef; readonly sources: readonly FieldRef[]; readonly kind: EdgeKind; readonly grain: Grain }> {\n const out: Array<{ target: FieldRef; sources: FieldRef[]; kind: EdgeKind; grain: Grain }> = []\n for (const reg of this._byName.values()) {\n const sources: FieldRef[] = []\n for (const dep of reg.dependencies) {\n if (dep === reg.outputCollection && partitionDisjoint(reg)) continue\n sources.push({ collection: dep, field: WHOLE_RECORD })\n }\n if (sources.length === 0) continue\n out.push({ target: { collection: reg.outputCollection, field: WHOLE_RECORD }, sources, kind: 'mv', grain: reg.grain })\n }\n return out\n }\n\n /**\n * Cycle detection, delegated to `ViaGraph.assertAcyclic()` (#638 Task 2 —\n * retires the local DFS). `graph` is the caller's shared per-vault graph,\n * ALREADY carrying this registry's `edges()` AND `DerivationRegistry`'s\n * (registered by the caller — see `Vault._initMaterializedViews`, which\n * runs after `_initDerivations`, so a pure-derivation cycle already threw\n * `DerivationCycleError` there; any cycle surfacing here necessarily\n * touches an MV edge). Throws `MaterializedViewCycleError` — SAME class as\n * before.\n */\n validate(graph: ViaGraph): void {\n graph.assertAcyclic()\n }\n}\n\n/**\n * Type guard: is the clause a top-level `FieldClause` on the given\n * field? Used by the partition-disjoint check.\n *\n * @internal\n */\nfunction isFieldClauseOnField(clause: Clause, field: string): clause is FieldClause {\n return clause.type === 'field' && clause.field === field\n}\n\n/**\n * Wrap an `MVQueryContext` so its `.collection().query()` returns a\n * Query<T> with the MV's declared predicates attached. Bare Queries\n * (outside of any MV) don't gain `.wherePredicate()` — only Queries\n * obtained through this wrapped db do.\n *\n * @internal\n */\nexport function wrapDbWithPredicates(\n db: MVQueryContext,\n predicates: NonNullable<MaterializedViewStrategy<Record<string, unknown>>['predicates']>,\n): MVQueryContext {\n // Build the predicate map once — the fn signature in the MV spec\n // is row-typed but the QueryBuilder casts to unknown, so we widen\n // here for the Map.\n const map = new Map<string, DeclaredPredicate>()\n for (const [name, decl] of Object.entries(predicates)) {\n map.set(name, {\n hash: decl.hash,\n fn: decl.fn as (record: unknown, ctx?: unknown) => boolean,\n })\n }\n return {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n collection<T extends Record<string, unknown>>(name: string): any {\n const c = db.collection<T>(name)\n // Return an object that delegates everything to `c` but\n // overrides `.query()` to attach predicates via the new\n // `Query._withPredicates()` accessor.\n return new Proxy(c, {\n get(target, prop, receiver) {\n if (prop === 'query') {\n return (...args: unknown[]) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const q = (target.query as any)(...args)\n // For non-aggregate Query<T>, attach predicates. For\n // legacy predicate-arg overload that returns T[] (sync\n // filter), pass through unchanged.\n \n if (q && typeof q._withPredicates === 'function') {\n return q._withPredicates(map)\n }\n return q\n }\n }\n return Reflect.get(target, prop, receiver)\n },\n })\n },\n }\n}\n\n/**\n * Walk a QueryPlan's clauses and collect predicate-reference markers\n * for `queryHash` derivation. Returns a sorted array (deterministic\n * order) of `{ name, predicateHash, ctxHash }` tuples — these are the\n * hashable identity of each `.wherePredicate()` call site.\n *\n * @internal\n */\nfunction extractPredicateRefs(\n plan: { clauses: readonly Clause[] },\n): Array<{ name: string; predicateHash: string; ctxHash: string }> {\n const refs: Array<{ name: string; predicateHash: string; ctxHash: string }> = []\n const walk = (clauses: readonly Clause[]): void => {\n for (const c of clauses) {\n if (c.type === 'wherePredicate') {\n refs.push({ name: c.name, predicateHash: c.predicateHash, ctxHash: c.ctxHash })\n } else if (c.type === 'group') {\n walk(c.clauses)\n }\n }\n }\n walk(plan.clauses)\n // Stable-sort by (name, predicateHash, ctxHash) — same predicate\n // appearing twice with different ctx hashes both flow through.\n refs.sort((a, b) => {\n if (a.name !== b.name) return a.name < b.name ? -1 : 1\n if (a.predicateHash !== b.predicateHash) return a.predicateHash < b.predicateHash ? -1 : 1\n return a.ctxHash < b.ctxHash ? -1 : a.ctxHash > b.ctxHash ? 1 : 0\n })\n return refs\n}\n\n/**\n * Provability check for the same-collection partition-discriminator.\n * Returns `true` when\n * the captured partition clauses on the MV's query provably exclude\n * the partition's value — meaning the input filter and the output\n * partition are disjoint and the same-collection edge isn't really a\n * cycle.\n *\n * Supported provability shapes (narrow on purpose — DERIV-PP30-001\n * is the load-bearing case):\n *\n * - `.where(field, '==', X)` where X !== partition.value → disjoint\n * - `.where(field, '!=', partition.value)` → disjoint\n * - `.where(field, 'in', [...])` where partition.value NOT in list → disjoint\n *\n * Anything else (no clause on the partition field, an 'in' list that\n * contains partition.value, unsupported operators) → not disjoint,\n * the cycle detector surfaces `MaterializedViewCycleError`.\n *\n * @internal\n */\nfunction partitionDisjoint(reg: RegisteredMV): boolean {\n const partition = reg.spec.output?.partition\n if (partition === undefined) return false\n const value = partition.value\n // The OR-semantics of multiple where-clauses on the same field\n // would muddy this check. v2 only treats AND-chained clauses;\n // any clause that proves disjoint is sufficient.\n for (const c of reg.partitionClauses) {\n if (c.op === '==' && c.value !== value) return true\n if (c.op === '!=' && c.value === value) return true\n if (c.op === 'in' && Array.isArray(c.value)) {\n const list = c.value as readonly unknown[]\n if (!list.includes(value)) return true\n }\n }\n return false\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-cargo/adopt-partition.ts","../src/with-cargo/decrypt-partition.ts"],"sourcesContent":["/**\n * Partition adoption. Recipient side: verify an extracted bundle,\n * validate the transfer key, import the re-keyed collections into a\n * destination store, and record an `_meta/adoption` marker. The bundle\n * stays UNOWNED after adoption — `createOwnerOnAdoptedPartition`\n * mints the owner; the transfer seal is then destroyed.\n *\n * @module\n */\nimport { base64ToBuffer, wrapKey, type EnclaveKey } from '../kernel/enclave/index.js'\nimport { TransferSealError, AdoptionStateError, ValidationError } from '../kernel/errors.js'\nimport type { NoydbStore, VaultSnapshot, KeyringFile } from '../kernel/types.js'\nimport { createOwnerKeyring } from '../with-party/team/keyring.js'\nimport { resolveManagedSecret } from '../with-party/team/managed-passphrase.js'\nimport type { SealingKeyProvider } from '../with-party/team/managed-passphrase.js'\nimport type { ShamirRecoveryProvider } from '../with-party/team/shamir-recovery-provider.js'\nimport type { RecoveryEnrollmentInput } from '../with-party/team/rotate-recover.js'\nimport { LedgerStore } from '../with-commit/history/ledger/store.js'\nimport { LEDGER_COLLECTION } from '../with-commit/history/ledger/constants.js'\nimport type { TransferSealPayload } from '../with-pod/bundle.js'\nimport { readPodHeader, readPod, parseExtractedPartitionBody } from '../with-pod/bundle.js'\n\n/**\n * Reverse of `sealDeks`. Imports the transfer key, decrypts the\n * sealed `{ collection: base64(rawDEK) }` map (layout iv(12)‖ct‖tag), and\n * re-imports each DEK as an AES-GCM key. Throws `TransferSealError` on a\n * wrong key (AES-GCM auth-tag failure) or malformed payload.\n */\nexport async function unsealDeks(\n seal: TransferSealPayload,\n transferKey: Uint8Array,\n): Promise<Map<string, EnclaveKey>> {\n if (transferKey.byteLength !== 32) {\n throw new TransferSealError(\n `transfer key must be 32 bytes, got ${transferKey.byteLength}.`,\n )\n }\n const key = await crypto.subtle.importKey('raw', transferKey as BufferSource, 'AES-GCM', false, ['decrypt'])\n const raw = base64ToBuffer(seal.payload)\n let plaintext: ArrayBuffer\n try {\n plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: raw.slice(0, 12) as BufferSource },\n key,\n raw.slice(12) as BufferSource,\n )\n } catch {\n throw new TransferSealError(\n 'transfer seal could not be opened — wrong transfer key (AES-GCM authentication failed).',\n )\n }\n let dekMap: Record<string, string>\n try {\n dekMap = JSON.parse(new TextDecoder().decode(plaintext)) as Record<string, string>\n } catch {\n throw new TransferSealError('transfer seal payload is not valid JSON after decryption.')\n }\n const deks = new Map<string, EnclaveKey>()\n for (const [collection, b64] of Object.entries(dekMap)) {\n // Extractable: the recipient must be able to re-wrap these under their\n // own KEK (AES-KW) at owner-creation. Matches generateDEK.\n const dek = await crypto.subtle.importKey('raw', base64ToBuffer(b64) as BufferSource, 'AES-GCM', true, ['encrypt', 'decrypt'])\n deks.set(collection, dek)\n }\n return deks\n}\n\nexport interface AdoptPartitionOptions {\n readonly transferKey: Uint8Array\n readonly destinationStore: NoydbStore\n readonly vaultName: string\n}\n\nexport interface AdoptPartitionResult {\n readonly vaultName: string\n readonly needsOwner: true\n readonly sealId: string\n}\n\nexport async function adoptPartition(\n bundleBytes: Uint8Array,\n opts: AdoptPartitionOptions,\n): Promise<AdoptPartitionResult> {\n const { transferKey, destinationStore, vaultName } = opts\n\n const header = readPodHeader(bundleBytes)\n if (header.bundleKind !== 'extracted-partition' || header.transferSeal === undefined) {\n throw new ValidationError(\n 'adoptPartition requires an extracted-partition bundle with a transfer seal. '\n + 'For ordinary backups use readPod + vault.load.',\n )\n }\n\n const { dumpJson } = await readPod(bundleBytes)\n const { dump, seal } = parseExtractedPartitionBody(dumpJson)\n\n // Validate the transfer key by unsealing in memory; throws\n // TransferSealError on mismatch. DEKs are discarded here — they stay\n // sealed at rest (in _meta/adoption) until owner-creation wraps them under the\n // recipient's KEK.\n await unsealDeks(seal, transferKey)\n\n // Single-occupancy per vaultName: an `_meta/adoption` marker already present\n // means this slot holds a partition (adopted-and-unowned, or already owned).\n // saveAll below would overwrite its data and replace the marker, stranding the\n // prior adoption's transfer seal. Refuse regardless of sealId — re-adopting the\n // SAME bundle is a redundant call, and adopting a DIFFERENT bundle here would\n // clobber the existing partition. Either way, pick a fresh vaultName.\n const existing = await destinationStore.get(vaultName, '_meta', 'adoption')\n if (existing) {\n const prior = JSON.parse(existing._data) as { sealId?: string }\n if (prior.sealId === seal.sealId) {\n throw new AdoptionStateError(\n `partition (sealId ${seal.sealId}) is already adopted into vault \"${vaultName}\".`,\n )\n }\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already holds an adopted partition (sealId ${prior.sealId}); `\n + `adopting a different partition (sealId ${seal.sealId}) here would overwrite it. `\n + `Adopt into a fresh vaultName instead.`,\n )\n }\n\n // The marker-only check above misses a worse case: a vaultName already in use\n // by an ORDINARY vault (createNoydb + openVault) carries no `_meta/adoption`,\n // yet `saveAll` below is destructive on SQL adapters (`DELETE FROM ... WHERE\n // vault = ?` followed by upsert) and would wipe the legitimate keyring +\n // data. Refuse adoption into ANY occupied slot — a fresh vaultName is the\n // documented precondition.\n const existingKeyring = await destinationStore.list(vaultName, '_keyring')\n if (existingKeyring.length > 0) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already holds a keyring (an unrelated owner exists at this slot); `\n + `adoptPartition requires a fresh vaultName to avoid destructive saveAll on SQL adapters.`,\n )\n }\n\n const backup = JSON.parse(dump) as { collections: VaultSnapshot; _internal?: VaultSnapshot }\n await destinationStore.saveAll(vaultName, backup.collections)\n\n // Import carried internal collections (e.g. _schemas from carrySchemas).\n // saveAll only writes data collections; _internal is written per-record.\n if (backup._internal) {\n for (const [collection, records] of Object.entries(backup._internal)) {\n for (const [id, envelope] of Object.entries(records)) {\n await destinationStore.put(vaultName, collection, id, envelope)\n }\n }\n }\n\n const adoptedAt = new Date().toISOString()\n const adoption = { sealId: seal.sealId, adoptedAt, needsOwner: true as const, transferSeal: seal }\n await destinationStore.put(vaultName, '_meta', 'adoption', {\n _noydb: 1, _v: 1, _ts: adoptedAt, _iv: '', _data: JSON.stringify(adoption),\n })\n\n return { vaultName, needsOwner: true, sealId: seal.sealId }\n}\n\nexport interface CreateOwnerResult {\n readonly vaultName: string\n readonly userId: string\n}\n\n/** Standard-mode owner: recipient supplies the passphrase. */\nexport interface CreateOwnerStandardOptions {\n readonly userId: string\n readonly passphrase: string\n readonly transferKey: Uint8Array\n}\n\n/**\n * Managed-mode owner: the passphrase is minted + sealed under\n * a `SealingKeyProvider` (e.g. an `at-*` OS keychain) so the partition\n * auto-unlocks on the recipient's device. Managed mode mandates a strong\n * (Shamir) recovery profile at creation, which needs the\n * `shamirRecovery` provider injected.\n */\nexport interface CreateOwnerManagedOptions {\n readonly userId: string\n readonly passphraseMode: 'managed'\n readonly sealingKey: SealingKeyProvider\n readonly recovery: ReadonlyArray<RecoveryEnrollmentInput>\n readonly shamirRecovery: ShamirRecoveryProvider\n readonly transferKey: Uint8Array\n}\n\nexport type CreateOwnerOptions = CreateOwnerStandardOptions | CreateOwnerManagedOptions\n\nfunction isManaged(o: CreateOwnerOptions): o is CreateOwnerManagedOptions {\n return 'passphraseMode' in o && o.passphraseMode === 'managed'\n}\n\n/**\n * Mint the first owner keyring on an adopted-but-unowned partition,\n * then destroy the transfer seal.\n *\n * Standard mode: the recipient supplies a passphrase. Managed mode: the\n * passphrase is minted + sealed under a `SealingKeyProvider` and a strong\n * (Shamir) recovery profile is enrolled — orchestrated via the existing\n * `openVaultAndEnrollRecovery` ceremony.\n *\n * Either way, reuses `createOwnerKeyring` to derive the KEK + write the base\n * keyring, then wraps the partition's DEKs (recovered from the seal) under that\n * KEK and re-persists the merged keyring file.\n *\n * Idempotent under retry: the seal is destroyed LAST (Stage D), after the\n * keyring (Stage A), the ledger transition (Stage B), and — in managed mode —\n * strong-recovery enrollment (Stage C). A failure in the fallible enrollment\n * step leaves the seal intact, and re-running with the same `userId` +\n * `transferKey` resumes from the first incomplete stage. (Multi-profile recovery\n * arrays may re-enroll an already-enrolled profile on retry; managed mode's\n * mandated single Shamir profile does not.)\n */\nexport async function createOwnerOnAdoptedPartition(\n store: NoydbStore,\n vaultName: string,\n opts: CreateOwnerOptions,\n): Promise<CreateOwnerResult> {\n const { userId, transferKey } = opts\n\n // Managed mode requires a strong (Shamir) recovery profile, validated BEFORE\n // any disk write — same gate as createNoydb.\n if (isManaged(opts) && !opts.recovery.some((r) => r.profile === 'shamir')) {\n throw new AdoptionStateError(\n 'managed-mode adoption requires at least one strong (shamir) recovery profile in '\n + '`recovery` — paper alone is not strong when there is no user passphrase to fall back on.',\n )\n }\n\n // 1. Verify adopted-unowned state.\n const adoptionEnv = await store.get(vaultName, '_meta', 'adoption')\n if (!adoptionEnv) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" is not an adopted partition (no _meta/adoption). `\n + `createOwnerOnAdoptedPartition only applies to vaults created via adoptPartition.`,\n )\n }\n const adoption = JSON.parse(adoptionEnv._data) as {\n sealId: string; adoptedAt: string; needsOwner?: boolean\n consumedAt?: string; transferSeal?: TransferSealPayload\n }\n if (adoption.consumedAt !== undefined || adoption.transferSeal === undefined) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already has an owner (transfer seal consumed at ${adoption.consumedAt}).`,\n )\n }\n\n // 2. Recover the partition DEKs from the seal (throws on wrong key) BEFORE\n // writing any keyring, so a bad transfer key leaves no trace. Always\n // validated, including when resuming a partial prior call.\n const partitionDeks = await unsealDeks(adoption.transferSeal, transferKey)\n\n // The ceremony below is split into stages so a failure in the fallible\n // managed-enrollment step (network/provider outage) leaves the call RETRYABLE\n // — the seal is destroyed only once everything durable is in place. Each stage\n // detects its own prior completion rather than relying on a single resume bit.\n\n // A keyring present for a DIFFERENT user (with the seal still unconsumed) is a\n // genuine second-owner attempt — refuse it. A same-user keyring is a resumed\n // partial call and is handled by the stage checks below.\n const existingKeyring = await store.get(vaultName, '_keyring', userId)\n const otherOwners = (await store.list(vaultName, '_keyring')).filter((u) => u !== userId)\n if (otherOwners.length > 0) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already has a keyring for a different owner; cannot create owner \"${userId}\".`,\n )\n }\n\n // Stage A — mint the owner keyring + merge the partition DEKs. Considered done\n // only when the keyring already holds every partition DEK. createOwnerKeyring\n // overwrites (fresh KEK + fresh _users DEK), so re-running is safe ONLY while\n // no recovery has been enrolled yet — guaranteed here because enrollment\n // (Stage C) runs strictly after Stage A completes.\n const partitionCollections = [...partitionDeks.keys()]\n const priorDeks = existingKeyring ? (JSON.parse(existingKeyring._data) as KeyringFile).deks : {}\n const ownerMinted = existingKeyring !== null && partitionCollections.every((c) => c in priorDeks)\n if (!ownerMinted) {\n // Resolve the owner passphrase. Managed mode mints a random passphrase, seals\n // it under the provider, and persists _meta/sealed-passphrase (so the\n // partition auto-unlocks on the recipient's device); standard mode uses the\n // caller's passphrase. Idempotent under retry — resolveManagedSecret's reopen\n // arm reuses an already-sealed passphrase.\n const passphrase = isManaged(opts)\n ? await resolveManagedSecret(store, vaultName, opts.sealingKey)\n : opts.passphrase\n\n // Mint the owner keyring (KEK + _users DEK + canary, written to disk).\n const unlocked = await createOwnerKeyring(store, vaultName, userId, passphrase)\n\n // Merge the partition DEKs (wrapped under the new KEK) into the keyring.\n const env = await store.get(vaultName, '_keyring', userId)\n if (!env) throw new AdoptionStateError(`keyring write for \"${userId}\" did not persist`)\n const keyringFile = JSON.parse(env._data) as KeyringFile\n const kek = unlocked.kek\n if (!kek) throw new AdoptionStateError(`owner keyring for \"${userId}\" has no KEK to wrap partition DEKs under`)\n const mergedDeks: Record<string, string> = { ...keyringFile.deks }\n for (const [collection, dek] of partitionDeks) {\n mergedDeks[collection] = await wrapKey(dek, kek)\n }\n const mergedFile: KeyringFile = { ...keyringFile, deks: mergedDeks }\n await store.put(vaultName, '_keyring', userId, { ...env, _data: JSON.stringify(mergedFile) })\n }\n\n // Stage B — record the ownership transition on the carried\n // audit chain (carryLedger sealed the _ledger DEK). No-op without that DEK.\n // Idempotent: appended only if the closing `transfer-seal-consumed` entry is\n // absent, so a retry does not duplicate the pair.\n const ledgerDek = partitionDeks.get(LEDGER_COLLECTION)\n if (ledgerDek) {\n const ledger = new LedgerStore({\n adapter: store,\n vault: vaultName,\n encrypted: true,\n getDEK: async () => ledgerDek,\n actor: userId,\n })\n const creationReason = `creation-of-new-owner:${userId}`\n const consumedReason = `transfer-seal-consumed:${adoption.sealId}`\n // Gate each append on its own presence — a crash or store error strictly\n // between the two adjacent puts would otherwise re-append the first one\n // on retry. The pair is the audit record, not a single transaction.\n const recordedReasons = new Set((await ledger.loadAllEntries()).map((e) => e.reason))\n if (!recordedReasons.has(creationReason)) {\n await ledger.append({ op: 'lifecycle', collection: '', id: '', version: 0, actor: '', payloadHash: '', reason: creationReason })\n }\n if (!recordedReasons.has(consumedReason)) {\n await ledger.append({ op: 'lifecycle', collection: '', id: '', version: 0, actor: '', payloadHash: '', reason: consumedReason })\n }\n }\n\n // Stage C — Managed mode: enroll the mandatory strong recovery\n // by orchestrating the existing public ceremony. The partition is\n // now a managed-mode vault on disk (sealed passphrase + keyring), so we\n // open it as a normal client and let openVaultAndEnrollRecovery do the\n // gate-bypass + enroll + re-assert. Dynamic import keeps the Noydb class\n // out of the @noy-db/hub/bundle static graph. Runs BEFORE seal destruction\n // so a failure here leaves the seal intact and the call retryable.\n if (isManaged(opts)) {\n const { createNoydb } = await import('../kernel/noydb.js')\n const db = await createNoydb({\n store,\n user: userId,\n passphraseMode: 'managed',\n sealingKey: opts.sealingKey,\n shamirRecovery: opts.shamirRecovery,\n })\n await db.openVaultAndEnrollRecovery(vaultName, { recovery: opts.recovery })\n }\n\n // Stage D — Destroy the transfer seal LAST — the commit point. Everything\n // above is either idempotent or resumable, so the seal is only consumed\n // once the owner keyring (and, in managed mode, strong recovery) is\n // durably in place. Retain sealId + consumedAt for audit.\n const consumed = { sealId: adoption.sealId, adoptedAt: adoption.adoptedAt, consumedAt: new Date().toISOString() }\n await store.put(vaultName, '_meta', 'adoption', { ...adoptionEnv, _data: JSON.stringify(consumed) })\n\n return { vaultName, userId }\n}\n","/**\n * Read-side counterpart to `extractPartition`: decrypt an\n * extracted-partition bundle's records to plaintext using its transfer\n * key, WITHOUT adopting it into a vault. Used by an outward\n * orchestration layer's reconcile/merge and field-authority flows to\n * compare incoming records against a receiver. The transfer key validates the bundle\n * (wrong key throws).\n * @module\n */\nimport type { EncryptedEnvelope } from '../kernel/types.js'\nimport { openEnvelopeJson } from '../kernel/enclave/index.js'\nimport { readPodHeader, readPod, parseExtractedPartitionBody } from '../with-pod/bundle.js'\nimport { unsealDeks } from './adopt-partition.js'\n\n/** One decrypted record from an extracted-partition compartment. */\nexport interface DecryptedRecord {\n readonly id: string\n readonly record: Record<string, unknown>\n /** Source envelope write timestamp (ISO) — for last-write-wins merges. */\n readonly ts: string\n /** Source envelope version. */\n readonly version: number\n /** Provenance source id (FR-5). Present only when the source collection had provenance:true and a source was supplied on put. */\n readonly source?: string\n /** ISO-8601 timestamp the provenance source was recorded (FR-5). */\n readonly sourceTs?: string\n}\n\n/**\n * Decrypt every record of an extracted-partition bundle to plaintext,\n * grouped by collection. Throws if the bundle isn't an\n * extracted-partition or the transfer key is wrong.\n */\nexport async function decryptExtractedPartition(\n bundleBytes: Uint8Array,\n transferKey: Uint8Array,\n): Promise<Record<string, DecryptedRecord[]>> {\n const header = readPodHeader(bundleBytes)\n if (header.bundleKind !== 'extracted-partition' || header.transferSeal === undefined) {\n throw new Error('decryptExtractedPartition: bundle is not an extracted-partition.')\n }\n const { dumpJson } = await readPod(bundleBytes)\n const { dump, seal } = parseExtractedPartitionBody(dumpJson)\n const deks = await unsealDeks(seal, transferKey) // throws TransferSealError on wrong key\n const backup = JSON.parse(dump) as { collections: Record<string, Record<string, EncryptedEnvelope>> }\n const out: Record<string, DecryptedRecord[]> = {}\n for (const [collection, byId] of Object.entries(backup.collections)) {\n const dek = deks.get(collection)\n if (dek === undefined) continue // no DEK sealed for this collection — skip\n const recs: DecryptedRecord[] = []\n for (const [id, env] of Object.entries(byId)) {\n const plaintext = await openEnvelopeJson(env, dek)\n const body = JSON.parse(plaintext) as Record<string, unknown>\n recs.push({\n id,\n record: { ...body, id },\n ts: env._ts,\n version: env._v,\n ...(env._source !== undefined ? { source: env._source } : {}),\n ...(env._sourceTs !== undefined ? { sourceTs: env._sourceTs } : {}),\n })\n }\n out[collection] = recs\n }\n return out\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,eAAsB,WACpB,MACA,aACkC;AAClC,MAAI,YAAY,eAAe,IAAI;AACjC,UAAM,IAAI;AAAA,MACR,sCAAsC,YAAY,UAAU;AAAA,IAC9D;AAAA,EACF;AACA,QAAM,MAAM,MAAM,OAAO,OAAO,UAAU,OAAO,aAA6B,WAAW,OAAO,CAAC,SAAS,CAAC;AAC3G,QAAM,MAAM,eAAe,KAAK,OAAO;AACvC,MAAI;AACJ,MAAI;AACF,gBAAY,MAAM,OAAO,OAAO;AAAA,MAC9B,EAAE,MAAM,WAAW,IAAI,IAAI,MAAM,GAAG,EAAE,EAAkB;AAAA,MACxD;AAAA,MACA,IAAI,MAAM,EAAE;AAAA,IACd;AAAA,EACF,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI,YAAY,EAAE,OAAO,SAAS,CAAC;AAAA,EACzD,QAAQ;AACN,UAAM,IAAI,kBAAkB,2DAA2D;AAAA,EACzF;AACA,QAAM,OAAO,oBAAI,IAAwB;AACzC,aAAW,CAAC,YAAY,GAAG,KAAK,OAAO,QAAQ,MAAM,GAAG;AAGtD,UAAM,MAAM,MAAM,OAAO,OAAO,UAAU,OAAO,eAAe,GAAG,GAAmB,WAAW,MAAM,CAAC,WAAW,SAAS,CAAC;AAC7H,SAAK,IAAI,YAAY,GAAG;AAAA,EAC1B;AACA,SAAO;AACT;AAcA,eAAsB,eACpB,aACA,MAC+B;AAC/B,QAAM,EAAE,aAAa,kBAAkB,UAAU,IAAI;AAErD,QAAM,SAAS,cAAc,WAAW;AACxC,MAAI,OAAO,eAAe,yBAAyB,OAAO,iBAAiB,QAAW;AACpF,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,QAAM,EAAE,SAAS,IAAI,MAAM,QAAQ,WAAW;AAC9C,QAAM,EAAE,MAAM,KAAK,IAAI,4BAA4B,QAAQ;AAM3D,QAAM,WAAW,MAAM,WAAW;AAQlC,QAAM,WAAW,MAAM,iBAAiB,IAAI,WAAW,SAAS,UAAU;AAC1E,MAAI,UAAU;AACZ,UAAM,QAAQ,KAAK,MAAM,SAAS,KAAK;AACvC,QAAI,MAAM,WAAW,KAAK,QAAQ;AAChC,YAAM,IAAI;AAAA,QACR,qBAAqB,KAAK,MAAM,oCAAoC,SAAS;AAAA,MAC/E;AAAA,IACF;AACA,UAAM,IAAI;AAAA,MACR,UAAU,SAAS,gDAAgD,MAAM,MAAM,6CACnC,KAAK,MAAM;AAAA,IAEzD;AAAA,EACF;AAQA,QAAM,kBAAkB,MAAM,iBAAiB,KAAK,WAAW,UAAU;AACzE,MAAI,gBAAgB,SAAS,GAAG;AAC9B,UAAM,IAAI;AAAA,MACR,UAAU,SAAS;AAAA,IAErB;AAAA,EACF;AAEA,QAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,QAAM,iBAAiB,QAAQ,WAAW,OAAO,WAAW;AAI5D,MAAI,OAAO,WAAW;AACpB,eAAW,CAAC,YAAY,OAAO,KAAK,OAAO,QAAQ,OAAO,SAAS,GAAG;AACpE,iBAAW,CAAC,IAAI,QAAQ,KAAK,OAAO,QAAQ,OAAO,GAAG;AACpD,cAAM,iBAAiB,IAAI,WAAW,YAAY,IAAI,QAAQ;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAM,WAAW,EAAE,QAAQ,KAAK,QAAQ,WAAW,YAAY,MAAe,cAAc,KAAK;AACjG,QAAM,iBAAiB,IAAI,WAAW,SAAS,YAAY;AAAA,IACzD,QAAQ;AAAA,IAAG,IAAI;AAAA,IAAG,KAAK;AAAA,IAAW,KAAK;AAAA,IAAI,OAAO,KAAK,UAAU,QAAQ;AAAA,EAC3E,CAAC;AAED,SAAO,EAAE,WAAW,YAAY,MAAM,QAAQ,KAAK,OAAO;AAC5D;AAgCA,SAAS,UAAU,GAAuD;AACxE,SAAO,oBAAoB,KAAK,EAAE,mBAAmB;AACvD;AAuBA,eAAsB,8BACpB,OACA,WACA,MAC4B;AAC5B,QAAM,EAAE,QAAQ,YAAY,IAAI;AAIhC,MAAI,UAAU,IAAI,KAAK,CAAC,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,YAAY,QAAQ,GAAG;AACzE,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAGA,QAAM,cAAc,MAAM,MAAM,IAAI,WAAW,SAAS,UAAU;AAClE,MAAI,CAAC,aAAa;AAChB,UAAM,IAAI;AAAA,MACR,UAAU,SAAS;AAAA,IAErB;AAAA,EACF;AACA,QAAM,WAAW,KAAK,MAAM,YAAY,KAAK;AAI7C,MAAI,SAAS,eAAe,UAAa,SAAS,iBAAiB,QAAW;AAC5E,UAAM,IAAI;AAAA,MACR,UAAU,SAAS,qDAAqD,SAAS,UAAU;AAAA,IAC7F;AAAA,EACF;AAKA,QAAM,gBAAgB,MAAM,WAAW,SAAS,cAAc,WAAW;AAUzE,QAAM,kBAAkB,MAAM,MAAM,IAAI,WAAW,YAAY,MAAM;AACrE,QAAM,eAAe,MAAM,MAAM,KAAK,WAAW,UAAU,GAAG,OAAO,CAAC,MAAM,MAAM,MAAM;AACxF,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,IAAI;AAAA,MACR,UAAU,SAAS,uEAAuE,MAAM;AAAA,IAClG;AAAA,EACF;AAOA,QAAM,uBAAuB,CAAC,GAAG,cAAc,KAAK,CAAC;AACrD,QAAM,YAAY,kBAAmB,KAAK,MAAM,gBAAgB,KAAK,EAAkB,OAAO,CAAC;AAC/F,QAAM,cAAc,oBAAoB,QAAQ,qBAAqB,MAAM,CAAC,MAAM,KAAK,SAAS;AAChG,MAAI,CAAC,aAAa;AAMhB,UAAM,aAAa,UAAU,IAAI,IAC7B,MAAM,qBAAqB,OAAO,WAAW,KAAK,UAAU,IAC5D,KAAK;AAGT,UAAM,WAAW,MAAM,mBAAmB,OAAO,WAAW,QAAQ,UAAU;AAG9E,UAAM,MAAM,MAAM,MAAM,IAAI,WAAW,YAAY,MAAM;AACzD,QAAI,CAAC,IAAK,OAAM,IAAI,mBAAmB,sBAAsB,MAAM,mBAAmB;AACtF,UAAM,cAAc,KAAK,MAAM,IAAI,KAAK;AACxC,UAAM,MAAM,SAAS;AACrB,QAAI,CAAC,IAAK,OAAM,IAAI,mBAAmB,sBAAsB,MAAM,2CAA2C;AAC9G,UAAM,aAAqC,EAAE,GAAG,YAAY,KAAK;AACjE,eAAW,CAAC,YAAY,GAAG,KAAK,eAAe;AAC7C,iBAAW,UAAU,IAAI,MAAM,QAAQ,KAAK,GAAG;AAAA,IACjD;AACA,UAAM,aAA0B,EAAE,GAAG,aAAa,MAAM,WAAW;AACnE,UAAM,MAAM,IAAI,WAAW,YAAY,QAAQ,EAAE,GAAG,KAAK,OAAO,KAAK,UAAU,UAAU,EAAE,CAAC;AAAA,EAC9F;AAMA,QAAM,YAAY,cAAc,IAAI,iBAAiB;AACrD,MAAI,WAAW;AACb,UAAM,SAAS,IAAI,YAAY;AAAA,MAC7B,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,MACX,QAAQ,YAAY;AAAA,MACpB,OAAO;AAAA,IACT,CAAC;AACD,UAAM,iBAAiB,yBAAyB,MAAM;AACtD,UAAM,iBAAiB,0BAA0B,SAAS,MAAM;AAIhE,UAAM,kBAAkB,IAAI,KAAK,MAAM,OAAO,eAAe,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AACpF,QAAI,CAAC,gBAAgB,IAAI,cAAc,GAAG;AACxC,YAAM,OAAO,OAAO,EAAE,IAAI,aAAa,YAAY,IAAI,IAAI,IAAI,SAAS,GAAG,OAAO,IAAI,aAAa,IAAI,QAAQ,eAAe,CAAC;AAAA,IACjI;AACA,QAAI,CAAC,gBAAgB,IAAI,cAAc,GAAG;AACxC,YAAM,OAAO,OAAO,EAAE,IAAI,aAAa,YAAY,IAAI,IAAI,IAAI,SAAS,GAAG,OAAO,IAAI,aAAa,IAAI,QAAQ,eAAe,CAAC;AAAA,IACjI;AAAA,EACF;AASA,MAAI,UAAU,IAAI,GAAG;AACnB,UAAM,EAAE,YAAY,IAAI,MAAM,OAAO,qBAAoB;AACzD,UAAM,KAAK,MAAM,YAAY;AAAA,MAC3B;AAAA,MACA,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,YAAY,KAAK;AAAA,MACjB,gBAAgB,KAAK;AAAA,IACvB,CAAC;AACD,UAAM,GAAG,2BAA2B,WAAW,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EAC5E;AAMA,QAAM,WAAW,EAAE,QAAQ,SAAS,QAAQ,WAAW,SAAS,WAAW,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE;AAChH,QAAM,MAAM,IAAI,WAAW,SAAS,YAAY,EAAE,GAAG,aAAa,OAAO,KAAK,UAAU,QAAQ,EAAE,CAAC;AAEnG,SAAO,EAAE,WAAW,OAAO;AAC7B;;;ACrUA,eAAsB,0BACpB,aACA,aAC4C;AAC5C,QAAM,SAAS,cAAc,WAAW;AACxC,MAAI,OAAO,eAAe,yBAAyB,OAAO,iBAAiB,QAAW;AACpF,UAAM,IAAI,MAAM,kEAAkE;AAAA,EACpF;AACA,QAAM,EAAE,SAAS,IAAI,MAAM,QAAQ,WAAW;AAC9C,QAAM,EAAE,MAAM,KAAK,IAAI,4BAA4B,QAAQ;AAC3D,QAAM,OAAO,MAAM,WAAW,MAAM,WAAW;AAC/C,QAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,QAAM,MAAyC,CAAC;AAChD,aAAW,CAAC,YAAY,IAAI,KAAK,OAAO,QAAQ,OAAO,WAAW,GAAG;AACnE,UAAM,MAAM,KAAK,IAAI,UAAU;AAC/B,QAAI,QAAQ,OAAW;AACvB,UAAM,OAA0B,CAAC;AACjC,eAAW,CAAC,IAAI,GAAG,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC5C,YAAM,YAAY,MAAM,iBAAiB,KAAK,GAAG;AACjD,YAAM,OAAO,KAAK,MAAM,SAAS;AACjC,WAAK,KAAK;AAAA,QACR;AAAA,QACA,QAAQ,EAAE,GAAG,MAAM,GAAG;AAAA,QACtB,IAAI,IAAI;AAAA,QACR,SAAS,IAAI;AAAA,QACb,GAAI,IAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,QAAQ,IAAI,CAAC;AAAA,QAC3D,GAAI,IAAI,cAAc,SAAY,EAAE,UAAU,IAAI,UAAU,IAAI,CAAC;AAAA,MACnE,CAAC;AAAA,IACH;AACA,QAAI,UAAU,IAAI;AAAA,EACpB;AACA,SAAO;AACT;","names":[]}
@@ -1,39 +0,0 @@
1
- // src/with-party/directory/public-envelope/types.ts
2
- var PUBLIC_ENVELOPE_FIELDS = [
3
- "name",
4
- "description",
5
- "icon",
6
- "createdAt",
7
- "updatedAt",
8
- "defaultLocale"
9
- ];
10
- var DEFAULT_PUBLIC_ENVELOPE_SCHEMA = {
11
- fields: PUBLIC_ENVELOPE_FIELDS,
12
- maxIconBytes: 256 * 1024,
13
- iconMimeTypes: ["image/png", "image/svg+xml"],
14
- maxStringChars: 200
15
- };
16
- function resolveSchema(schema) {
17
- if (!schema) return void 0;
18
- if (schema === true) {
19
- return {
20
- fields: DEFAULT_PUBLIC_ENVELOPE_SCHEMA.fields,
21
- maxIconBytes: DEFAULT_PUBLIC_ENVELOPE_SCHEMA.maxIconBytes,
22
- iconMimeTypes: DEFAULT_PUBLIC_ENVELOPE_SCHEMA.iconMimeTypes,
23
- maxStringChars: DEFAULT_PUBLIC_ENVELOPE_SCHEMA.maxStringChars
24
- };
25
- }
26
- return {
27
- fields: schema.fields ?? DEFAULT_PUBLIC_ENVELOPE_SCHEMA.fields,
28
- maxIconBytes: schema.maxIconBytes ?? DEFAULT_PUBLIC_ENVELOPE_SCHEMA.maxIconBytes,
29
- iconMimeTypes: schema.iconMimeTypes ?? DEFAULT_PUBLIC_ENVELOPE_SCHEMA.iconMimeTypes,
30
- maxStringChars: schema.maxStringChars ?? DEFAULT_PUBLIC_ENVELOPE_SCHEMA.maxStringChars
31
- };
32
- }
33
-
34
- export {
35
- PUBLIC_ENVELOPE_FIELDS,
36
- DEFAULT_PUBLIC_ENVELOPE_SCHEMA,
37
- resolveSchema
38
- };
39
- //# sourceMappingURL=chunk-WXSYDNBT.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-party/directory/public-envelope/types.ts"],"sourcesContent":["/**\n * Public envelope — owner-curated plaintext metadata, readable\n * before vault unlock or bundle decryption.\n *\n * @see https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/public-envelope.md\n *\n * @module\n */\n\n/**\n * Either a single string (used when the developer's app is\n * single-locale) or a locale → string map for i18n. Mirrors the\n * shape `@noy-db/hub/i18n` already uses for record fields, so the\n * existing `resolveI18nText` resolver applies.\n */\nexport type PublicEnvelopeText = string | Record<string, string>\n\n/**\n * Persisted shape — both `_meta/public-envelope` and the bundle\n * header carry this. The version number is monotonic per vault and\n * helps cache invalidators detect change without hashing the JSON.\n */\nexport interface PublicEnvelope {\n readonly _noydb_public: 1\n readonly version: number\n readonly name?: PublicEnvelopeText\n readonly description?: PublicEnvelopeText\n /** Inline `data:` URL (`data:image/png;base64,…` or `data:image/svg+xml;base64,…`). */\n readonly icon?: string\n /** ISO-8601 timestamp; auto-set on first envelope write, immutable thereafter. */\n readonly createdAt?: string\n /** ISO-8601 timestamp; auto-updated on every `setPublicEnvelope` call. */\n readonly updatedAt?: string\n /** BCP-47 fallback locale for renderers when the user's locale isn't covered. */\n readonly defaultLocale?: string\n}\n\n/** Field names the developer can allow in `PublicEnvelopeSchema.fields`. */\nexport const PUBLIC_ENVELOPE_FIELDS = [\n 'name',\n 'description',\n 'icon',\n 'createdAt',\n 'updatedAt',\n 'defaultLocale',\n] as const\n\nexport type PublicEnvelopeField = (typeof PUBLIC_ENVELOPE_FIELDS)[number]\n\n/**\n * Build-time schema. The developer enables the feature and bounds\n * what the owner can set. `true` is shorthand for \"all defaults\" —\n * gives the owner the full field set with the standard caps.\n */\nexport interface PublicEnvelopeSchema {\n /**\n * Allowed field names. Setting `name`/`description`/`icon`/`defaultLocale` is\n * gated on the field being listed here. `createdAt` / `updatedAt` are managed\n * by the hub; including them is a no-op (the owner cannot set them\n * directly). Default: every field above.\n */\n readonly fields?: ReadonlyArray<PublicEnvelopeField>\n /**\n * Maximum icon size — measured as the length of the data-URL\n * string. Default 256 KB.\n */\n readonly maxIconBytes?: number\n /** Allowed icon MIME types. Default ['image/png', 'image/svg+xml']. */\n readonly iconMimeTypes?: ReadonlyArray<string>\n /** Maximum length of `name` / `description` per locale. Default 200. */\n readonly maxStringChars?: number\n}\n\n/** Default schema values; merged onto every developer-supplied schema. */\nexport const DEFAULT_PUBLIC_ENVELOPE_SCHEMA = {\n fields: PUBLIC_ENVELOPE_FIELDS,\n maxIconBytes: 256 * 1024,\n iconMimeTypes: ['image/png', 'image/svg+xml'] as const,\n maxStringChars: 200,\n} as const satisfies Required<PublicEnvelopeSchema>\n\n/** Resolved schema after merging developer override onto defaults. */\nexport interface ResolvedPublicEnvelopeSchema {\n readonly fields: ReadonlyArray<PublicEnvelopeField>\n readonly maxIconBytes: number\n readonly iconMimeTypes: ReadonlyArray<string>\n readonly maxStringChars: number\n}\n\n/**\n * Merge developer schema onto the defaults. The shorthand `true`\n * resolves to the full default schema; an explicit object only\n * overrides the keys it provides.\n */\nexport function resolveSchema(\n schema: true | PublicEnvelopeSchema | undefined,\n): ResolvedPublicEnvelopeSchema | undefined {\n if (!schema) return undefined\n if (schema === true) {\n return {\n fields: DEFAULT_PUBLIC_ENVELOPE_SCHEMA.fields,\n maxIconBytes: DEFAULT_PUBLIC_ENVELOPE_SCHEMA.maxIconBytes,\n iconMimeTypes: DEFAULT_PUBLIC_ENVELOPE_SCHEMA.iconMimeTypes,\n maxStringChars: DEFAULT_PUBLIC_ENVELOPE_SCHEMA.maxStringChars,\n }\n }\n return {\n fields: schema.fields ?? DEFAULT_PUBLIC_ENVELOPE_SCHEMA.fields,\n maxIconBytes: schema.maxIconBytes ?? DEFAULT_PUBLIC_ENVELOPE_SCHEMA.maxIconBytes,\n iconMimeTypes: schema.iconMimeTypes ?? DEFAULT_PUBLIC_ENVELOPE_SCHEMA.iconMimeTypes,\n maxStringChars: schema.maxStringChars ?? DEFAULT_PUBLIC_ENVELOPE_SCHEMA.maxStringChars,\n }\n}\n"],"mappings":";AAsCO,IAAM,yBAAyB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AA6BO,IAAM,iCAAiC;AAAA,EAC5C,QAAQ;AAAA,EACR,cAAc,MAAM;AAAA,EACpB,eAAe,CAAC,aAAa,eAAe;AAAA,EAC5C,gBAAgB;AAClB;AAeO,SAAS,cACd,QAC0C;AAC1C,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,WAAW,MAAM;AACnB,WAAO;AAAA,MACL,QAAQ,+BAA+B;AAAA,MACvC,cAAc,+BAA+B;AAAA,MAC7C,eAAe,+BAA+B;AAAA,MAC9C,gBAAgB,+BAA+B;AAAA,IACjD;AAAA,EACF;AACA,SAAO;AAAA,IACL,QAAQ,OAAO,UAAU,+BAA+B;AAAA,IACxD,cAAc,OAAO,gBAAgB,+BAA+B;AAAA,IACpE,eAAe,OAAO,iBAAiB,+BAA+B;AAAA,IACtE,gBAAgB,OAAO,kBAAkB,+BAA+B;AAAA,EAC1E;AACF;","names":[]}
@@ -1,155 +0,0 @@
1
- import {
2
- NOYDB_FORMAT_VERSION
3
- } from "./chunk-JNAI2HN2.js";
4
- import {
5
- ValidationError
6
- } from "./chunk-BIOUI4HL.js";
7
-
8
- // src/with-party/directory/public-envelope/schema.ts
9
- var DATA_URL_PREFIX = /^data:([a-zA-Z0-9.+-]+\/[a-zA-Z0-9.+-]+);base64,/;
10
- function validatePublicEnvelopeInput(input, schema) {
11
- const allowed = new Set(schema.fields);
12
- for (const key of Object.keys(input)) {
13
- const known = key === "name" || key === "description" || key === "icon" || key === "defaultLocale" ? key : void 0;
14
- if (!known) {
15
- throw new ValidationError(
16
- `setPublicEnvelope: unknown field "${key}". Allowed fields: ${[...allowed].join(", ")}.`
17
- );
18
- }
19
- if (!allowed.has(known)) {
20
- throw new ValidationError(
21
- `setPublicEnvelope: field "${known}" is not enabled in this vault's schema. Allowed fields: ${[...allowed].join(", ")}.`
22
- );
23
- }
24
- }
25
- if (input.name !== void 0) {
26
- validateText(input.name, "name", schema.maxStringChars);
27
- }
28
- if (input.description !== void 0) {
29
- validateText(input.description, "description", schema.maxStringChars);
30
- }
31
- if (input.icon !== void 0) {
32
- validateIcon(input.icon, schema);
33
- }
34
- if (input.defaultLocale !== void 0 && typeof input.defaultLocale !== "string") {
35
- throw new ValidationError(
36
- `setPublicEnvelope: defaultLocale must be a string (BCP-47), got ${typeof input.defaultLocale}.`
37
- );
38
- }
39
- }
40
- function validateText(value, field, maxChars) {
41
- if (typeof value === "string") {
42
- if (value.length > maxChars) {
43
- throw new ValidationError(
44
- `setPublicEnvelope: ${field} exceeds the ${maxChars}-character cap (got ${value.length}).`
45
- );
46
- }
47
- return;
48
- }
49
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
50
- throw new ValidationError(
51
- `setPublicEnvelope: ${field} must be a string or { [locale]: string } map, got ${typeof value}.`
52
- );
53
- }
54
- for (const [locale, str] of Object.entries(value)) {
55
- if (typeof str !== "string") {
56
- throw new ValidationError(
57
- `setPublicEnvelope: ${field}[${locale}] must be a string, got ${typeof str}.`
58
- );
59
- }
60
- if (str.length > maxChars) {
61
- throw new ValidationError(
62
- `setPublicEnvelope: ${field}[${locale}] exceeds the ${maxChars}-character cap (got ${str.length}).`
63
- );
64
- }
65
- }
66
- }
67
- function validateIcon(icon, schema) {
68
- if (typeof icon !== "string") {
69
- throw new ValidationError(
70
- `setPublicEnvelope: icon must be a data: URL string, got ${typeof icon}.`
71
- );
72
- }
73
- if (icon.length > schema.maxIconBytes) {
74
- throw new ValidationError(
75
- `setPublicEnvelope: icon exceeds the ${schema.maxIconBytes}-byte cap (got ${icon.length}).`
76
- );
77
- }
78
- const m = DATA_URL_PREFIX.exec(icon);
79
- if (!m) {
80
- throw new ValidationError(
81
- "setPublicEnvelope: icon must be a base64 data URL (`data:image/png;base64,\u2026` or `data:image/svg+xml;base64,\u2026`). External URLs are not supported in v1."
82
- );
83
- }
84
- const mime = m[1];
85
- if (!schema.iconMimeTypes.includes(mime)) {
86
- throw new ValidationError(
87
- `setPublicEnvelope: icon MIME type "${mime}" is not allowed. Permitted types: ${schema.iconMimeTypes.join(", ")}.`
88
- );
89
- }
90
- }
91
- function isPublicEnvelope(x) {
92
- if (x === null || typeof x !== "object" || Array.isArray(x)) return false;
93
- const obj = x;
94
- return obj["_noydb_public"] === 1 && typeof obj["version"] === "number";
95
- }
96
-
97
- // src/with-party/directory/public-envelope/storage.ts
98
- var PUBLIC_ENVELOPE_RECORD_ID = "public-envelope";
99
- async function loadPublicEnvelope(store, vault) {
100
- const envelope = await store.get(vault, "_meta", PUBLIC_ENVELOPE_RECORD_ID);
101
- if (!envelope) return void 0;
102
- try {
103
- const parsed = JSON.parse(envelope._data);
104
- if (!isPublicEnvelope(parsed)) return void 0;
105
- return parsed;
106
- } catch {
107
- return void 0;
108
- }
109
- }
110
- async function savePublicEnvelope(store, vault, envelope) {
111
- const wireEnvelope = {
112
- _noydb: NOYDB_FORMAT_VERSION,
113
- _v: 1,
114
- _ts: (/* @__PURE__ */ new Date()).toISOString(),
115
- _iv: "",
116
- _data: JSON.stringify(envelope)
117
- };
118
- await store.put(vault, "_meta", PUBLIC_ENVELOPE_RECORD_ID, wireEnvelope);
119
- }
120
- async function readPublicEnvelope(store, vault, opts = {}) {
121
- const raw = await loadPublicEnvelope(store, vault);
122
- if (!raw) return void 0;
123
- if (opts.locale === void 0) return raw;
124
- return resolveLocale(raw, opts.locale);
125
- }
126
- function resolveLocale(envelope, locale) {
127
- return {
128
- ...envelope,
129
- ...envelope.name !== void 0 ? { name: pickLocale(envelope.name, locale, envelope.defaultLocale) } : {},
130
- ...envelope.description !== void 0 ? { description: pickLocale(envelope.description, locale, envelope.defaultLocale) } : {}
131
- };
132
- }
133
- function pickLocale(value, locale, defaultLocale) {
134
- if (typeof value === "string") return value;
135
- if (value[locale] !== void 0 && value[locale] !== "") return value[locale];
136
- if (defaultLocale && value[defaultLocale] !== void 0 && value[defaultLocale] !== "") {
137
- return value[defaultLocale];
138
- }
139
- for (const v of Object.values(value)) {
140
- if (v !== "") return v;
141
- }
142
- return "";
143
- }
144
-
145
- export {
146
- validatePublicEnvelopeInput,
147
- isPublicEnvelope,
148
- PUBLIC_ENVELOPE_RECORD_ID,
149
- loadPublicEnvelope,
150
- savePublicEnvelope,
151
- readPublicEnvelope,
152
- resolveLocale,
153
- pickLocale
154
- };
155
- //# sourceMappingURL=chunk-X3YYQWOQ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/with-party/directory/public-envelope/schema.ts","../src/with-party/directory/public-envelope/storage.ts"],"sourcesContent":["/**\n * Validate-on-write for the public envelope. Runs at every\n * `setPublicEnvelope` call; the developer's schema decides which\n * fields are allowed and the size caps that apply.\n *\n * @module\n */\nimport { ValidationError } from '../../../kernel/errors.js'\nimport type {\n PublicEnvelope,\n PublicEnvelopeText,\n ResolvedPublicEnvelopeSchema,\n PublicEnvelopeField,\n} from './types.js'\n\n/** Owner-supplied input — the subset of {@link PublicEnvelope} the owner can set. */\nexport interface SetPublicEnvelopeInput {\n readonly name?: PublicEnvelopeText\n readonly description?: PublicEnvelopeText\n readonly icon?: string\n readonly defaultLocale?: string\n}\n\nconst DATA_URL_PREFIX = /^data:([a-zA-Z0-9.+-]+\\/[a-zA-Z0-9.+-]+);base64,/\n\n/**\n * Validate an owner-supplied envelope input against the developer's\n * resolved schema. Throws `ValidationError` on the first violation;\n * returns void on success.\n *\n * The validator is deliberately strict: every fail mode is a hard\n * error rather than a silent drop, so the owner finds out immediately\n * which field they oversized rather than discovering a truncated\n * label months later.\n */\nexport function validatePublicEnvelopeInput(\n input: SetPublicEnvelopeInput,\n schema: ResolvedPublicEnvelopeSchema,\n): void {\n const allowed = new Set<PublicEnvelopeField>(schema.fields)\n\n // Reject any key not in the schema's allowed-field list.\n for (const key of Object.keys(input)) {\n const known: PublicEnvelopeField | undefined =\n key === 'name' || key === 'description' || key === 'icon' || key === 'defaultLocale'\n ? key\n : undefined\n if (!known) {\n throw new ValidationError(\n `setPublicEnvelope: unknown field \"${key}\". ` +\n `Allowed fields: ${[...allowed].join(', ')}.`,\n )\n }\n if (!allowed.has(known)) {\n throw new ValidationError(\n `setPublicEnvelope: field \"${known}\" is not enabled in this vault's schema. ` +\n `Allowed fields: ${[...allowed].join(', ')}.`,\n )\n }\n }\n\n if (input.name !== undefined) {\n validateText(input.name, 'name', schema.maxStringChars)\n }\n if (input.description !== undefined) {\n validateText(input.description, 'description', schema.maxStringChars)\n }\n if (input.icon !== undefined) {\n validateIcon(input.icon, schema)\n }\n if (input.defaultLocale !== undefined && typeof input.defaultLocale !== 'string') {\n throw new ValidationError(\n `setPublicEnvelope: defaultLocale must be a string (BCP-47), got ${typeof input.defaultLocale}.`,\n )\n }\n}\n\nfunction validateText(\n value: PublicEnvelopeText,\n field: string,\n maxChars: number,\n): void {\n if (typeof value === 'string') {\n if (value.length > maxChars) {\n throw new ValidationError(\n `setPublicEnvelope: ${field} exceeds the ${maxChars}-character cap (got ${value.length}).`,\n )\n }\n return\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new ValidationError(\n `setPublicEnvelope: ${field} must be a string or { [locale]: string } map, got ${typeof value}.`,\n )\n }\n // Locale map. Each value must be a non-empty string within the cap.\n for (const [locale, str] of Object.entries(value)) {\n if (typeof str !== 'string') {\n throw new ValidationError(\n `setPublicEnvelope: ${field}[${locale}] must be a string, got ${typeof str}.`,\n )\n }\n if (str.length > maxChars) {\n throw new ValidationError(\n `setPublicEnvelope: ${field}[${locale}] exceeds the ${maxChars}-character cap (got ${str.length}).`,\n )\n }\n }\n}\n\nfunction validateIcon(icon: string, schema: ResolvedPublicEnvelopeSchema): void {\n if (typeof icon !== 'string') {\n throw new ValidationError(\n `setPublicEnvelope: icon must be a data: URL string, got ${typeof icon}.`,\n )\n }\n if (icon.length > schema.maxIconBytes) {\n throw new ValidationError(\n `setPublicEnvelope: icon exceeds the ${schema.maxIconBytes}-byte cap (got ${icon.length}).`,\n )\n }\n const m = DATA_URL_PREFIX.exec(icon)\n if (!m) {\n throw new ValidationError(\n 'setPublicEnvelope: icon must be a base64 data URL ' +\n '(`data:image/png;base64,…` or `data:image/svg+xml;base64,…`). ' +\n 'External URLs are not supported in v1.',\n )\n }\n const mime = m[1]!\n if (!schema.iconMimeTypes.includes(mime)) {\n throw new ValidationError(\n `setPublicEnvelope: icon MIME type \"${mime}\" is not allowed. ` +\n `Permitted types: ${schema.iconMimeTypes.join(', ')}.`,\n )\n }\n}\n\n/**\n * Lightweight runtime predicate — used by the bundle header\n * validator to recognise a public envelope without requiring it.\n */\nexport function isPublicEnvelope(x: unknown): x is PublicEnvelope {\n if (x === null || typeof x !== 'object' || Array.isArray(x)) return false\n const obj = x as Record<string, unknown>\n return obj['_noydb_public'] === 1 && typeof obj['version'] === 'number'\n}\n","/**\n * Persistence helpers for `_meta/public-envelope`. Mirrors the\n * bypass-AES pattern used by `_meta/handle` and `_meta/policy` —\n * the document is plaintext JSON, the envelope's `_iv` field is\n * left empty.\n *\n * @module\n */\nimport type { NoydbStore, EncryptedEnvelope } from '../../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../../kernel/types.js'\nimport type { PublicEnvelope } from './types.js'\nimport { isPublicEnvelope } from './schema.js'\n\n/** Reserved id for the vault-level public envelope record. */\nexport const PUBLIC_ENVELOPE_RECORD_ID = 'public-envelope'\n\n/**\n * Read the public envelope from `_meta/public-envelope`. Returns\n * `undefined` when no envelope has been persisted (fresh vault, or\n * a vault written before the feature was enabled). Tolerates\n * corrupted documents — a JSON parse failure surfaces as `undefined`,\n * not a thrown error, mirroring `_meta/handle`'s contract.\n */\nexport async function loadPublicEnvelope(\n store: NoydbStore,\n vault: string,\n): Promise<PublicEnvelope | undefined> {\n const envelope = await store.get(vault, '_meta', PUBLIC_ENVELOPE_RECORD_ID)\n if (!envelope) return undefined\n try {\n const parsed = JSON.parse(envelope._data) as unknown\n if (!isPublicEnvelope(parsed)) return undefined\n return parsed\n } catch {\n return undefined\n }\n}\n\n/** Persist the public envelope. Idempotent — overwrites any prior write. */\nexport async function savePublicEnvelope(\n store: NoydbStore,\n vault: string,\n envelope: PublicEnvelope,\n): Promise<void> {\n const wireEnvelope: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: JSON.stringify(envelope),\n }\n await store.put(vault, '_meta', PUBLIC_ENVELOPE_RECORD_ID, wireEnvelope)\n}\n\n/**\n * Public, no-key reader. Plain function, not a method on `Noydb` —\n * the whole point is it works without an authenticated session, so\n * a UI can show \"Acme 2026 Tax Records\" before unlocking.\n *\n * Resolves any `name` / `description` locale map through the supplied\n * `locale` (when provided). Omitting `locale` returns the raw\n * envelope, which a multilingual picker can render as it pleases.\n */\nexport async function readPublicEnvelope(\n store: NoydbStore,\n vault: string,\n opts: { readonly locale?: string } = {},\n): Promise<PublicEnvelope | undefined> {\n const raw = await loadPublicEnvelope(store, vault)\n if (!raw) return undefined\n if (opts.locale === undefined) return raw\n return resolveLocale(raw, opts.locale)\n}\n\n/**\n * Resolve the locale-map fields (`name`, `description`) of a public\n * envelope to plain strings for the requested locale, falling back\n * through `defaultLocale` and then any available translation.\n *\n * @internal\n */\nexport function resolveLocale(\n envelope: PublicEnvelope,\n locale: string,\n): PublicEnvelope {\n return {\n ...envelope,\n ...(envelope.name !== undefined ? { name: pickLocale(envelope.name, locale, envelope.defaultLocale) } : {}),\n ...(envelope.description !== undefined ? { description: pickLocale(envelope.description, locale, envelope.defaultLocale) } : {}),\n }\n}\n\n/**\n * Resolve a `string | { [locale]: string }` value to a string for a\n * given locale. Exported so the bundle reader can reuse it without\n * duplicating the fallback chain.\n *\n * **Looser than `resolveI18nText`** — the hub's record-field resolver\n * throws `LocaleNotSpecifiedError` when no translation matches, but\n * label semantics deliberately prefer \"show *something*\" over\n * throwing. The fallback chain is:\n * 1. Exact `locale` match.\n * 2. `defaultLocale` (when supplied on the envelope).\n * 3. First non-empty value in the map.\n * 4. Empty string (only if every translation is empty — pathological).\n *\n * This deviation is documented in `https://github.com/vLannaAi/noy-db-docs/blob/main/content/docs/services/public-envelope.md`.\n */\nexport function pickLocale(\n value: string | Record<string, string>,\n locale: string,\n defaultLocale: string | undefined,\n): string {\n if (typeof value === 'string') return value\n if (value[locale] !== undefined && value[locale] !== '') return value[locale]\n if (defaultLocale && value[defaultLocale] !== undefined && value[defaultLocale] !== '') {\n return value[defaultLocale]\n }\n for (const v of Object.values(value)) {\n if (v !== '') return v\n }\n return ''\n}\n"],"mappings":"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","names":[]}