@noy-db/hub 0.9.0-pre.0 → 0.9.0-pre.2

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 (465) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/api-H4B7ZQXP.js +18 -0
  3. package/dist/as/index.js +10 -10
  4. package/dist/at/index.js +8 -8
  5. package/dist/attestation/index.js +18 -18
  6. package/dist/{backup-7RKNZMQO.js → backup-E3GBMNDA.js} +92 -31
  7. package/dist/backup-E3GBMNDA.js.map +1 -0
  8. package/dist/blobs/index.js +12 -12
  9. package/dist/broker/index.js +13 -13
  10. package/dist/by/index.js +2 -2
  11. package/dist/capsule/contract.js +6 -6
  12. package/dist/capsule/enclave-aes/crypto.d.ts +18 -0
  13. package/dist/capsule/enclave-aes/index.d.ts +2 -2
  14. package/dist/capsule/enclave-aes/index.js +14 -8
  15. package/dist/capsule/enclave-aes/index.js.map +1 -1
  16. package/dist/capsule/index.d.ts +1 -1
  17. package/dist/{capsule-57BDK7LG.js → capsule-YQKQZPH7.js} +14 -8
  18. package/dist/cargo/index.js +35 -35
  19. package/dist/{chunk-AJFPAHMW.js → chunk-27NCEPKM.js} +3 -3
  20. package/dist/{chunk-PGC35KXE.js → chunk-2IASHJPS.js} +2 -2
  21. package/dist/{chunk-NYJBYVBG.js → chunk-2ODEFG6K.js} +4 -4
  22. package/dist/{chunk-7BMOS4JQ.js → chunk-2QH3I7QC.js} +11 -11
  23. package/dist/{chunk-VSUQI5Q6.js → chunk-2RH5SEY7.js} +16 -2
  24. package/dist/chunk-2RH5SEY7.js.map +1 -0
  25. package/dist/{chunk-IVMFIKNA.js → chunk-2T2TGRWE.js} +6 -6
  26. package/dist/{chunk-G66YXUTY.js → chunk-35CNOVQE.js} +3 -3
  27. package/dist/{chunk-OJU67C23.js → chunk-3EXQXOQ2.js} +4 -4
  28. package/dist/{chunk-BJH77UY2.js → chunk-4KYKRYRT.js} +2 -2
  29. package/dist/{chunk-RDOEOPOI.js → chunk-4N4OIVK3.js} +5 -5
  30. package/dist/{chunk-WHOKGHJ3.js → chunk-4WLI37WK.js} +7 -7
  31. package/dist/{chunk-RNACRT5H.js → chunk-52ZFNK46.js} +2 -2
  32. package/dist/{chunk-CB4467G3.js → chunk-5A3HOTP5.js} +33 -7
  33. package/dist/chunk-5A3HOTP5.js.map +1 -0
  34. package/dist/{chunk-QDHHHI5Q.js → chunk-5SMCBPJ2.js} +5 -5
  35. package/dist/{chunk-EMAWOGVH.js → chunk-654RE5YC.js} +3 -3
  36. package/dist/{chunk-FZXRLNJO.js → chunk-6BEBWHNL.js} +3 -3
  37. package/dist/{chunk-EUD3HQM4.js → chunk-6WEPFEMJ.js} +4 -4
  38. package/dist/{chunk-YFFMPZPY.js → chunk-6WIFF46H.js} +2 -2
  39. package/dist/{chunk-ZVSKSUGI.js → chunk-7C7OF26T.js} +5 -5
  40. package/dist/{chunk-QXJXG7DN.js → chunk-7IV7GVOS.js} +5 -5
  41. package/dist/{chunk-MQ7YWBQ5.js → chunk-7JRLCP2J.js} +2 -2
  42. package/dist/{chunk-QSHATAXJ.js → chunk-7ONMS7QI.js} +2 -2
  43. package/dist/{chunk-JBMMWYOH.js → chunk-7UHLO4ZN.js} +35 -2
  44. package/dist/chunk-7UHLO4ZN.js.map +1 -0
  45. package/dist/{chunk-6WS35E6X.js → chunk-A52BK5AY.js} +2 -2
  46. package/dist/{chunk-GOJQ5DZA.js → chunk-AI35LNWQ.js} +4 -4
  47. package/dist/chunk-AI35LNWQ.js.map +1 -0
  48. package/dist/{chunk-XXQ5XIYY.js → chunk-AMSSAOOB.js} +2 -2
  49. package/dist/{chunk-3V6LZHUO.js → chunk-AYOQZJDB.js} +2 -2
  50. package/dist/{chunk-DL6BZKP3.js → chunk-B3B6HV5S.js} +2 -2
  51. package/dist/{chunk-5XONHAFD.js → chunk-B6EWMGFX.js} +2 -2
  52. package/dist/{chunk-6Y5E4SJU.js → chunk-BMAXBTYH.js} +5 -5
  53. package/dist/{chunk-LGGIFKJP.js → chunk-BNYDOF2Q.js} +4 -4
  54. package/dist/{chunk-XXNWU5QZ.js → chunk-BQJ5ORU5.js} +2 -2
  55. package/dist/{chunk-Z3ZXVB2Y.js → chunk-BSEPI3HD.js} +12 -12
  56. package/dist/chunk-BSEPI3HD.js.map +1 -0
  57. package/dist/{chunk-BYHCKX73.js → chunk-CLOSXHR4.js} +3 -3
  58. package/dist/{chunk-5HNW4ZTA.js → chunk-CSAN22QG.js} +6 -6
  59. package/dist/{chunk-TZYTMZZY.js → chunk-DL4GCSTS.js} +10 -10
  60. package/dist/{chunk-EA4VDI5D.js → chunk-DYIR6RC3.js} +4 -4
  61. package/dist/{chunk-PUBI455O.js → chunk-EPPAEGH5.js} +2 -2
  62. package/dist/{chunk-2A56DSRI.js → chunk-EQ6C2ZPT.js} +17 -51
  63. package/dist/chunk-EQ6C2ZPT.js.map +1 -0
  64. package/dist/{chunk-BNFNQNEP.js → chunk-F7X7MPK4.js} +2 -2
  65. package/dist/{chunk-SNM7LSYG.js → chunk-FUHUGKDN.js} +4 -4
  66. package/dist/{chunk-72XBVN4J.js → chunk-FVKQ5QUC.js} +2 -2
  67. package/dist/chunk-FVKQ5QUC.js.map +1 -0
  68. package/dist/{chunk-D4V5JJAK.js → chunk-GHMINSER.js} +4 -4
  69. package/dist/{chunk-APFFJN5T.js → chunk-GLSCL74M.js} +31 -34
  70. package/dist/chunk-GLSCL74M.js.map +1 -0
  71. package/dist/{chunk-XKBTJGFP.js → chunk-GU6QXHPZ.js} +3 -3
  72. package/dist/chunk-GU6QXHPZ.js.map +1 -0
  73. package/dist/{chunk-QGFEBNHO.js → chunk-GZISWBCJ.js} +4 -4
  74. package/dist/{chunk-OM3SEDI4.js → chunk-HTRLQ4ZZ.js} +4 -4
  75. package/dist/{chunk-SWXLZ4HX.js → chunk-ICHHNI5E.js} +8 -8
  76. package/dist/{chunk-K673OO6B.js → chunk-ID4ZBC3X.js} +4 -4
  77. package/dist/{chunk-H3YM66EI.js → chunk-IF2M43G4.js} +2 -2
  78. package/dist/{chunk-QGSNJD3B.js → chunk-IFUYLIEY.js} +2 -2
  79. package/dist/{chunk-D5A3GCZ4.js → chunk-IPF5ORDI.js} +3 -3
  80. package/dist/{delegation-ONLM4JFI.js → chunk-JQMEXCUZ.js} +59 -16
  81. package/dist/chunk-JQMEXCUZ.js.map +1 -0
  82. package/dist/{chunk-TFWFPZRU.js → chunk-K5JNK63R.js} +11 -11
  83. package/dist/{chunk-TFWFPZRU.js.map → chunk-K5JNK63R.js.map} +1 -1
  84. package/dist/{chunk-BVF3SXD7.js → chunk-KBIS64X4.js} +5 -5
  85. package/dist/chunk-L4UL4IAR.js +55 -0
  86. package/dist/chunk-L4UL4IAR.js.map +1 -0
  87. package/dist/{chunk-RIZTZJX6.js → chunk-LJ744WXU.js} +2 -2
  88. package/dist/{chunk-XITHENBI.js → chunk-LMC5L3MM.js} +7 -7
  89. package/dist/{chunk-XITHENBI.js.map → chunk-LMC5L3MM.js.map} +1 -1
  90. package/dist/{chunk-L5A3KR7R.js → chunk-LUQBG637.js} +3 -3
  91. package/dist/{chunk-6QLCS7ET.js → chunk-LZN6DTFI.js} +2 -2
  92. package/dist/{chunk-NIXRB5BP.js → chunk-MMWX5OT5.js} +2 -2
  93. package/dist/{chunk-JA25QG5M.js → chunk-N6EWQ5VB.js} +622 -119
  94. package/dist/chunk-N6EWQ5VB.js.map +1 -0
  95. package/dist/{chunk-UOZOWN73.js → chunk-ND6S2DKC.js} +2 -2
  96. package/dist/{chunk-WFWFXDNS.js → chunk-NHEKQWFI.js} +2 -2
  97. package/dist/{chunk-2ZFD2UJC.js → chunk-NIUHKKMM.js} +9 -9
  98. package/dist/{chunk-XA34THLF.js → chunk-NK4LGBGQ.js} +4 -4
  99. package/dist/{chunk-JSWKVZCR.js → chunk-NUZ7ROUZ.js} +2 -2
  100. package/dist/{chunk-DHU2AQ6T.js → chunk-O3NGOHXQ.js} +2 -2
  101. package/dist/{chunk-ZBUAX4EF.js → chunk-O6VOR4NH.js} +2 -2
  102. package/dist/{chunk-JZ4EDEIP.js → chunk-ODBOHPZU.js} +3 -3
  103. package/dist/{chunk-5UUJE4FP.js → chunk-OK44MD3C.js} +2 -2
  104. package/dist/{chunk-ECGXA4LT.js → chunk-OR3OHKU6.js} +2 -2
  105. package/dist/{chunk-ZWF3EWZA.js → chunk-P6FATPXQ.js} +2 -2
  106. package/dist/{chunk-PCYPXLEX.js → chunk-PBEHTFUG.js} +6 -6
  107. package/dist/{chunk-7CT6SX4M.js → chunk-PFHDQY5L.js} +3 -3
  108. package/dist/{chunk-PTJY5R3O.js → chunk-PIO6NW7O.js} +2 -2
  109. package/dist/{chunk-TMIJAR2X.js → chunk-PREYKC76.js} +3 -3
  110. package/dist/{chunk-ZHQJAV6D.js → chunk-PU5ZEV65.js} +2 -2
  111. package/dist/{chunk-UNSQZSZP.js → chunk-PWAMX5KU.js} +2 -2
  112. package/dist/{chunk-GETUTTZU.js → chunk-PZN76MMV.js} +4 -4
  113. package/dist/{chunk-ABU6A4Z5.js → chunk-Q4DBU5RM.js} +2 -2
  114. package/dist/{chunk-2WJKJZSO.js → chunk-QHG7GQKM.js} +2 -2
  115. package/dist/{chunk-UAKY6E4N.js → chunk-QNOWNCZS.js} +2 -2
  116. package/dist/{chunk-JPSL5GIZ.js → chunk-QQLXKDGC.js} +3 -3
  117. package/dist/{chunk-45VHWE4C.js → chunk-QVUMKG3Z.js} +271 -69
  118. package/dist/chunk-QVUMKG3Z.js.map +1 -0
  119. package/dist/{chunk-OK57EATL.js → chunk-R4GLRJ55.js} +2 -2
  120. package/dist/{chunk-B65CC6Z6.js → chunk-RF5WDM25.js} +2 -2
  121. package/dist/{chunk-G46JSSVX.js → chunk-RKBCUP5D.js} +7 -7
  122. package/dist/{chunk-GCUMOKTN.js → chunk-S2ORTQGS.js} +3 -3
  123. package/dist/{chunk-MRJDLXI3.js → chunk-SVXFCCSX.js} +8 -8
  124. package/dist/{chunk-BUE7R3B5.js → chunk-SXPLIRQH.js} +2 -2
  125. package/dist/{chunk-5LMCVBMY.js → chunk-T5EXREMW.js} +5 -5
  126. package/dist/{chunk-PT2YCQVK.js → chunk-TBVDI3LY.js} +4 -4
  127. package/dist/{chunk-BMHQHH7Q.js → chunk-TLIFNRU5.js} +2 -2
  128. package/dist/{chunk-YKGJ44Z7.js → chunk-TU6ZJTYT.js} +3 -3
  129. package/dist/{chunk-FSQNGPHH.js → chunk-U277K5GR.js} +612 -175
  130. package/dist/chunk-U277K5GR.js.map +1 -0
  131. package/dist/{chunk-55P6OBB7.js → chunk-U43RSPPX.js} +2 -2
  132. package/dist/{chunk-K7RW7FI4.js → chunk-UADW5NOU.js} +5 -5
  133. package/dist/{chunk-GRS2HIV6.js → chunk-UMV5GBTJ.js} +2 -2
  134. package/dist/{chunk-4UIP3QNW.js → chunk-UNAMWK2Q.js} +2 -2
  135. package/dist/{chunk-J4KAPKBJ.js → chunk-UORWAG7K.js} +2 -2
  136. package/dist/{chunk-V55JASXO.js → chunk-UQRKAM2R.js} +3 -3
  137. package/dist/{chunk-2ELOCEXE.js → chunk-URGHKZPL.js} +1 -1
  138. package/dist/chunk-URGHKZPL.js.map +1 -0
  139. package/dist/{chunk-ZMVSKYTJ.js → chunk-VLRY2UH4.js} +10 -10
  140. package/dist/{chunk-SPPOUJLN.js → chunk-VZFTON37.js} +2 -2
  141. package/dist/{chunk-CAD7N6HQ.js → chunk-WIUH6EJ7.js} +10 -10
  142. package/dist/chunk-WIUH6EJ7.js.map +1 -0
  143. package/dist/{chunk-YQFBH5ZJ.js → chunk-WQUIJPN4.js} +3 -3
  144. package/dist/{chunk-4J3MNVVX.js → chunk-WRLWEYHY.js} +7 -1
  145. package/dist/{chunk-4J3MNVVX.js.map → chunk-WRLWEYHY.js.map} +1 -1
  146. package/dist/{chunk-NZULV7LK.js → chunk-WVQDY3UM.js} +4 -4
  147. package/dist/{chunk-3Y5JLSXS.js → chunk-X5RGG3ZW.js} +2 -2
  148. package/dist/{chunk-PFCTSXHU.js → chunk-X6Z2SIRF.js} +2 -2
  149. package/dist/{chunk-UHFGK6YA.js → chunk-XQGKS4OW.js} +3 -3
  150. package/dist/{chunk-L2F3CFCH.js → chunk-Y22JWHCP.js} +4 -4
  151. package/dist/{chunk-C53ER7AG.js → chunk-YVA6UPRN.js} +2 -2
  152. package/dist/{chunk-226V6XXJ.js → chunk-Z2KP6PNF.js} +6 -6
  153. package/dist/{chunk-G5EDYVSE.js → chunk-ZK2WT532.js} +5 -5
  154. package/dist/{chunk-SFCD6OU7.js → chunk-ZNVBCKMV.js} +2 -2
  155. package/dist/{chunk-BU2ABHFW.js → chunk-ZOFNZLAA.js} +4 -4
  156. package/dist/{chunk-X2RP4TO5.js → chunk-ZVJKHGE5.js} +7 -7
  157. package/dist/classified/index.js +5 -5
  158. package/dist/{classified-marker-KHDOYIJB.js → classified-marker-7HXDMYHI.js} +3 -3
  159. package/dist/collection-facade-V63BEBGU.js +41 -0
  160. package/dist/{computed-XMFTHAXV.js → computed-LZLSYOPG.js} +3 -3
  161. package/dist/consent/index.js +8 -8
  162. package/dist/cover/index.js +8 -8
  163. package/dist/custody/index.js +12 -12
  164. package/dist/{dead-filter-CXV5NS67.js → dead-filter-PZIJ4F4G.js} +10 -10
  165. package/dist/debug/index.js +7 -7
  166. package/dist/delegation-7SSGIETH.js +26 -0
  167. package/dist/derivations/index.js +22 -22
  168. package/dist/derive-IMI3BMDS.js +20 -0
  169. package/dist/directory/index.js +9 -9
  170. package/dist/{dispatch-2MQFMEGY.js → dispatch-4BUYPWMJ.js} +19 -19
  171. package/dist/{dispatch-OFDA4WQV.js → dispatch-KOPDDPEQ.js} +5 -5
  172. package/dist/executor-7SY3M6TZ.js +8 -0
  173. package/dist/executor-SNHW4ELF.js +35 -0
  174. package/dist/executor-Z3IKK7G3.js +8 -0
  175. package/dist/export-accessible-O7DIVFUO.js +22 -0
  176. package/dist/extract-partition-ELZEJKXM.js +35 -0
  177. package/dist/{fanout-sidecar-BO67PCPO.js → fanout-sidecar-FYDRL7F3.js} +8 -8
  178. package/dist/find-KWB2RYWJ.js +10 -0
  179. package/dist/forget/index.js +8 -8
  180. package/dist/guards/index.js +4 -4
  181. package/dist/history/index.js +15 -13
  182. package/dist/history/index.js.map +1 -1
  183. package/dist/i18n/index.js +13 -13
  184. package/dist/index.d.ts +3 -2
  185. package/dist/index.js +111 -107
  186. package/dist/index.js.map +1 -1
  187. package/dist/indexing/index.js +5 -5
  188. package/dist/introspection/index.js +10 -10
  189. package/dist/issue-5ZHXK4HJ.js +19 -0
  190. package/dist/kernel/collection.d.ts +85 -6
  191. package/dist/kernel/constants.d.ts +7 -0
  192. package/dist/kernel/errors.d.ts +9 -0
  193. package/dist/kernel/merge-authority.d.ts +5 -1
  194. package/dist/kernel/noydb.d.ts +116 -21
  195. package/dist/kernel/rejection-seal.d.ts +13 -0
  196. package/dist/kernel/sync-wiring.d.ts +55 -0
  197. package/dist/kernel/types.d.ts +366 -8
  198. package/dist/kernel/vault.d.ts +39 -5
  199. package/dist/{ledger-MSJGWKGW.js → ledger-UFJCB6T5.js} +10 -10
  200. package/dist/liberate-OEOSNNFH.js +25 -0
  201. package/dist/{link-set-QXXSPPHL.js → link-set-KMUBFN2L.js} +10 -10
  202. package/dist/{managed-secret-VSK7ANQI.js → managed-secret-H5WYSJ46.js} +9 -9
  203. package/dist/materialized-views/index.js +25 -25
  204. package/dist/{migrate-cek-GEBFYBDC.js → migrate-cek-SPESQAAO.js} +2 -2
  205. package/dist/money/index.js +5 -5
  206. package/dist/noydb-QDZ4QIKO.js +100 -0
  207. package/dist/on/index.js +12 -12
  208. package/dist/overlay-views/index.js +4 -4
  209. package/dist/periods/index.js +11 -11
  210. package/dist/pod/index.js +66 -64
  211. package/dist/{pod-handle-YE7CT66T.js → pod-handle-GJLCOUGK.js} +8 -8
  212. package/dist/policy/index.js +9 -9
  213. package/dist/port/at/index.d.ts +1 -1
  214. package/dist/port/by/index.d.ts +1 -1
  215. package/dist/port/on/index.d.ts +1 -1
  216. package/dist/port/with/admission-authority.d.ts +34 -0
  217. package/dist/port/with/broker-strategy.d.ts +10 -0
  218. package/dist/port/with/rejection-courier.d.ts +37 -0
  219. package/dist/port/with/restore-host.d.ts +33 -0
  220. package/dist/port/with/service-bus.d.ts +18 -0
  221. package/dist/port/with/team-strategy.d.ts +2 -2
  222. package/dist/port/with/write-hooks.d.ts +2 -0
  223. package/dist/portability/index.js +8 -8
  224. package/dist/{post-register-QC5TEAW3.js → post-register-2AC2STHC.js} +6 -6
  225. package/dist/query/all/index.js +15 -15
  226. package/dist/query/index.js +7 -7
  227. package/dist/query/live/index.js +7 -7
  228. package/dist/query/reduce/index.js +10 -10
  229. package/dist/query/relate/index.js +9 -9
  230. package/dist/reduce/index.js +8 -8
  231. package/dist/register-PIORIMMR.js +30 -0
  232. package/dist/registry-BIMICFUH.js +23 -0
  233. package/dist/registry-JGWLW53D.js +35 -0
  234. package/dist/registry-SELCBYEQ.js +8 -0
  235. package/dist/registry-W327UNYH.js +8 -0
  236. package/dist/request-withdrawal-K3SXXODI.js +28 -0
  237. package/dist/restore-to-OOPKLAPU.js +66 -0
  238. package/dist/restore-to-OOPKLAPU.js.map +1 -0
  239. package/dist/{reveal-YQTRNQFH.js → reveal-UAPBG743.js} +7 -7
  240. package/dist/revoke-6AFVXTWF.js +24 -0
  241. package/dist/satellites/index.js +1 -1
  242. package/dist/schema-update/index.js +2 -2
  243. package/dist/sealed-record/index.js +11 -11
  244. package/dist/search/index.js +4 -4
  245. package/dist/{seed-JMQTRKRE.js → seed-BVYDPVIW.js} +33 -18
  246. package/dist/seed-BVYDPVIW.js.map +1 -0
  247. package/dist/sequence/index.js +8 -8
  248. package/dist/session/index.js +8 -8
  249. package/dist/shadow/index.js +2 -2
  250. package/dist/signer-TW5MAQJP.js +23 -0
  251. package/dist/snapshots/index.js +9 -9
  252. package/dist/{stale-SKV33PBU.js → stale-CROGJBIJ.js} +9 -9
  253. package/dist/storage-SO2L2Z5J.js +21 -0
  254. package/dist/{store-coordination-provider-Z6HKPOGM.js → store-coordination-provider-2YD2PTTP.js} +9 -9
  255. package/dist/sync/index.js +16 -16
  256. package/dist/sync/index.js.map +1 -1
  257. package/dist/team/index.js +15 -15
  258. package/dist/tiers/index.js +10 -10
  259. package/dist/to/index.js +2 -2
  260. package/dist/transactions/index.js +3 -3
  261. package/dist/util/index.js +1 -1
  262. package/dist/vault-head/index.js +7 -7
  263. package/dist/{verify-KWQIWLGH.js → verify-QWO5UKD6.js} +7 -7
  264. package/dist/walk-3KEOL7BY.js +20 -0
  265. package/dist/with-audit/guards/types.d.ts +2 -0
  266. package/dist/with-audit/tiers/index.d.ts +5 -5
  267. package/dist/with-commit/history/history.d.ts +16 -0
  268. package/dist/with-commit/history/restore-to.d.ts +40 -0
  269. package/dist/with-party/broker/seed.d.ts +4 -0
  270. package/dist/with-party/custody/strategy.d.ts +2 -2
  271. package/dist/with-party/team/delegation.d.ts +41 -11
  272. package/dist/with-party/team/index.d.ts +2 -0
  273. package/dist/with-party/team/keyring.d.ts +133 -3
  274. package/dist/with-party/team/managed-secret.d.ts +1 -1
  275. package/dist/with-party/team/noydb-facade.d.ts +14 -2
  276. package/dist/with-party/team/roster-tag.d.ts +1 -1
  277. package/dist/with-pod/backup.d.ts +9 -17
  278. package/dist/with-sync/active.d.ts +28 -1
  279. package/dist/with-sync/engine.d.ts +59 -2
  280. package/dist/with-sync/index.d.ts +1 -0
  281. package/dist/with-sync/reserved-mirror.d.ts +27 -4
  282. package/dist/with-sync/strategy.d.ts +2 -0
  283. package/dist/withdraw-accessible-OZU525ND.js +25 -0
  284. package/dist/withdraw-accessible-OZU525ND.js.map +1 -0
  285. package/package.json +3 -3
  286. package/dist/api-MSJXVYQG.js +0 -18
  287. package/dist/backup-7RKNZMQO.js.map +0 -1
  288. package/dist/chunk-2A56DSRI.js.map +0 -1
  289. package/dist/chunk-2ELOCEXE.js.map +0 -1
  290. package/dist/chunk-45VHWE4C.js.map +0 -1
  291. package/dist/chunk-72XBVN4J.js.map +0 -1
  292. package/dist/chunk-APFFJN5T.js.map +0 -1
  293. package/dist/chunk-CAD7N6HQ.js.map +0 -1
  294. package/dist/chunk-CB4467G3.js.map +0 -1
  295. package/dist/chunk-FSQNGPHH.js.map +0 -1
  296. package/dist/chunk-GOJQ5DZA.js.map +0 -1
  297. package/dist/chunk-JA25QG5M.js.map +0 -1
  298. package/dist/chunk-JBMMWYOH.js.map +0 -1
  299. package/dist/chunk-VSUQI5Q6.js.map +0 -1
  300. package/dist/chunk-XKBTJGFP.js.map +0 -1
  301. package/dist/chunk-Z3ZXVB2Y.js.map +0 -1
  302. package/dist/collection-facade-LZIF7GWD.js +0 -41
  303. package/dist/delegation-ONLM4JFI.js.map +0 -1
  304. package/dist/derive-3YSIS36W.js +0 -20
  305. package/dist/executor-E6YDJ7YA.js +0 -8
  306. package/dist/executor-NJX3YQQ4.js +0 -35
  307. package/dist/executor-WWM3DD4P.js +0 -8
  308. package/dist/export-accessible-RLHU5G4W.js +0 -22
  309. package/dist/extract-partition-QII2EDCS.js +0 -35
  310. package/dist/find-6M7GYDUZ.js +0 -10
  311. package/dist/issue-LPEEEMPP.js +0 -19
  312. package/dist/liberate-LEKDYKTC.js +0 -25
  313. package/dist/noydb-F2AUF6KL.js +0 -98
  314. package/dist/register-SN72QALV.js +0 -30
  315. package/dist/registry-253FF7C6.js +0 -35
  316. package/dist/registry-3AZWQTDT.js +0 -23
  317. package/dist/registry-7UEBTHWG.js +0 -8
  318. package/dist/registry-IVJ67NFQ.js +0 -8
  319. package/dist/request-withdrawal-UKVJZKX2.js +0 -28
  320. package/dist/revoke-G2KXJQA5.js +0 -24
  321. package/dist/seed-JMQTRKRE.js.map +0 -1
  322. package/dist/signer-IUIYR3CX.js +0 -23
  323. package/dist/storage-NPBYP2SM.js +0 -21
  324. package/dist/walk-F4OLXI5H.js +0 -20
  325. package/dist/withdraw-accessible-ZKPLZ66E.js +0 -25
  326. /package/dist/{api-MSJXVYQG.js.map → api-H4B7ZQXP.js.map} +0 -0
  327. /package/dist/{capsule-57BDK7LG.js.map → capsule-YQKQZPH7.js.map} +0 -0
  328. /package/dist/{chunk-AJFPAHMW.js.map → chunk-27NCEPKM.js.map} +0 -0
  329. /package/dist/{chunk-PGC35KXE.js.map → chunk-2IASHJPS.js.map} +0 -0
  330. /package/dist/{chunk-NYJBYVBG.js.map → chunk-2ODEFG6K.js.map} +0 -0
  331. /package/dist/{chunk-7BMOS4JQ.js.map → chunk-2QH3I7QC.js.map} +0 -0
  332. /package/dist/{chunk-IVMFIKNA.js.map → chunk-2T2TGRWE.js.map} +0 -0
  333. /package/dist/{chunk-G66YXUTY.js.map → chunk-35CNOVQE.js.map} +0 -0
  334. /package/dist/{chunk-OJU67C23.js.map → chunk-3EXQXOQ2.js.map} +0 -0
  335. /package/dist/{chunk-BJH77UY2.js.map → chunk-4KYKRYRT.js.map} +0 -0
  336. /package/dist/{chunk-RDOEOPOI.js.map → chunk-4N4OIVK3.js.map} +0 -0
  337. /package/dist/{chunk-WHOKGHJ3.js.map → chunk-4WLI37WK.js.map} +0 -0
  338. /package/dist/{chunk-RNACRT5H.js.map → chunk-52ZFNK46.js.map} +0 -0
  339. /package/dist/{chunk-QDHHHI5Q.js.map → chunk-5SMCBPJ2.js.map} +0 -0
  340. /package/dist/{chunk-EMAWOGVH.js.map → chunk-654RE5YC.js.map} +0 -0
  341. /package/dist/{chunk-FZXRLNJO.js.map → chunk-6BEBWHNL.js.map} +0 -0
  342. /package/dist/{chunk-EUD3HQM4.js.map → chunk-6WEPFEMJ.js.map} +0 -0
  343. /package/dist/{chunk-YFFMPZPY.js.map → chunk-6WIFF46H.js.map} +0 -0
  344. /package/dist/{chunk-ZVSKSUGI.js.map → chunk-7C7OF26T.js.map} +0 -0
  345. /package/dist/{chunk-QXJXG7DN.js.map → chunk-7IV7GVOS.js.map} +0 -0
  346. /package/dist/{chunk-MQ7YWBQ5.js.map → chunk-7JRLCP2J.js.map} +0 -0
  347. /package/dist/{chunk-QSHATAXJ.js.map → chunk-7ONMS7QI.js.map} +0 -0
  348. /package/dist/{chunk-6WS35E6X.js.map → chunk-A52BK5AY.js.map} +0 -0
  349. /package/dist/{chunk-XXQ5XIYY.js.map → chunk-AMSSAOOB.js.map} +0 -0
  350. /package/dist/{chunk-3V6LZHUO.js.map → chunk-AYOQZJDB.js.map} +0 -0
  351. /package/dist/{chunk-DL6BZKP3.js.map → chunk-B3B6HV5S.js.map} +0 -0
  352. /package/dist/{chunk-5XONHAFD.js.map → chunk-B6EWMGFX.js.map} +0 -0
  353. /package/dist/{chunk-6Y5E4SJU.js.map → chunk-BMAXBTYH.js.map} +0 -0
  354. /package/dist/{chunk-LGGIFKJP.js.map → chunk-BNYDOF2Q.js.map} +0 -0
  355. /package/dist/{chunk-XXNWU5QZ.js.map → chunk-BQJ5ORU5.js.map} +0 -0
  356. /package/dist/{chunk-BYHCKX73.js.map → chunk-CLOSXHR4.js.map} +0 -0
  357. /package/dist/{chunk-5HNW4ZTA.js.map → chunk-CSAN22QG.js.map} +0 -0
  358. /package/dist/{chunk-TZYTMZZY.js.map → chunk-DL4GCSTS.js.map} +0 -0
  359. /package/dist/{chunk-EA4VDI5D.js.map → chunk-DYIR6RC3.js.map} +0 -0
  360. /package/dist/{chunk-PUBI455O.js.map → chunk-EPPAEGH5.js.map} +0 -0
  361. /package/dist/{chunk-BNFNQNEP.js.map → chunk-F7X7MPK4.js.map} +0 -0
  362. /package/dist/{chunk-SNM7LSYG.js.map → chunk-FUHUGKDN.js.map} +0 -0
  363. /package/dist/{chunk-D4V5JJAK.js.map → chunk-GHMINSER.js.map} +0 -0
  364. /package/dist/{chunk-QGFEBNHO.js.map → chunk-GZISWBCJ.js.map} +0 -0
  365. /package/dist/{chunk-OM3SEDI4.js.map → chunk-HTRLQ4ZZ.js.map} +0 -0
  366. /package/dist/{chunk-SWXLZ4HX.js.map → chunk-ICHHNI5E.js.map} +0 -0
  367. /package/dist/{chunk-K673OO6B.js.map → chunk-ID4ZBC3X.js.map} +0 -0
  368. /package/dist/{chunk-H3YM66EI.js.map → chunk-IF2M43G4.js.map} +0 -0
  369. /package/dist/{chunk-QGSNJD3B.js.map → chunk-IFUYLIEY.js.map} +0 -0
  370. /package/dist/{chunk-D5A3GCZ4.js.map → chunk-IPF5ORDI.js.map} +0 -0
  371. /package/dist/{chunk-BVF3SXD7.js.map → chunk-KBIS64X4.js.map} +0 -0
  372. /package/dist/{chunk-RIZTZJX6.js.map → chunk-LJ744WXU.js.map} +0 -0
  373. /package/dist/{chunk-L5A3KR7R.js.map → chunk-LUQBG637.js.map} +0 -0
  374. /package/dist/{chunk-6QLCS7ET.js.map → chunk-LZN6DTFI.js.map} +0 -0
  375. /package/dist/{chunk-NIXRB5BP.js.map → chunk-MMWX5OT5.js.map} +0 -0
  376. /package/dist/{chunk-UOZOWN73.js.map → chunk-ND6S2DKC.js.map} +0 -0
  377. /package/dist/{chunk-WFWFXDNS.js.map → chunk-NHEKQWFI.js.map} +0 -0
  378. /package/dist/{chunk-2ZFD2UJC.js.map → chunk-NIUHKKMM.js.map} +0 -0
  379. /package/dist/{chunk-XA34THLF.js.map → chunk-NK4LGBGQ.js.map} +0 -0
  380. /package/dist/{chunk-JSWKVZCR.js.map → chunk-NUZ7ROUZ.js.map} +0 -0
  381. /package/dist/{chunk-DHU2AQ6T.js.map → chunk-O3NGOHXQ.js.map} +0 -0
  382. /package/dist/{chunk-ZBUAX4EF.js.map → chunk-O6VOR4NH.js.map} +0 -0
  383. /package/dist/{chunk-JZ4EDEIP.js.map → chunk-ODBOHPZU.js.map} +0 -0
  384. /package/dist/{chunk-5UUJE4FP.js.map → chunk-OK44MD3C.js.map} +0 -0
  385. /package/dist/{chunk-ECGXA4LT.js.map → chunk-OR3OHKU6.js.map} +0 -0
  386. /package/dist/{chunk-ZWF3EWZA.js.map → chunk-P6FATPXQ.js.map} +0 -0
  387. /package/dist/{chunk-PCYPXLEX.js.map → chunk-PBEHTFUG.js.map} +0 -0
  388. /package/dist/{chunk-7CT6SX4M.js.map → chunk-PFHDQY5L.js.map} +0 -0
  389. /package/dist/{chunk-PTJY5R3O.js.map → chunk-PIO6NW7O.js.map} +0 -0
  390. /package/dist/{chunk-TMIJAR2X.js.map → chunk-PREYKC76.js.map} +0 -0
  391. /package/dist/{chunk-ZHQJAV6D.js.map → chunk-PU5ZEV65.js.map} +0 -0
  392. /package/dist/{chunk-UNSQZSZP.js.map → chunk-PWAMX5KU.js.map} +0 -0
  393. /package/dist/{chunk-GETUTTZU.js.map → chunk-PZN76MMV.js.map} +0 -0
  394. /package/dist/{chunk-ABU6A4Z5.js.map → chunk-Q4DBU5RM.js.map} +0 -0
  395. /package/dist/{chunk-2WJKJZSO.js.map → chunk-QHG7GQKM.js.map} +0 -0
  396. /package/dist/{chunk-UAKY6E4N.js.map → chunk-QNOWNCZS.js.map} +0 -0
  397. /package/dist/{chunk-JPSL5GIZ.js.map → chunk-QQLXKDGC.js.map} +0 -0
  398. /package/dist/{chunk-OK57EATL.js.map → chunk-R4GLRJ55.js.map} +0 -0
  399. /package/dist/{chunk-B65CC6Z6.js.map → chunk-RF5WDM25.js.map} +0 -0
  400. /package/dist/{chunk-G46JSSVX.js.map → chunk-RKBCUP5D.js.map} +0 -0
  401. /package/dist/{chunk-GCUMOKTN.js.map → chunk-S2ORTQGS.js.map} +0 -0
  402. /package/dist/{chunk-MRJDLXI3.js.map → chunk-SVXFCCSX.js.map} +0 -0
  403. /package/dist/{chunk-BUE7R3B5.js.map → chunk-SXPLIRQH.js.map} +0 -0
  404. /package/dist/{chunk-5LMCVBMY.js.map → chunk-T5EXREMW.js.map} +0 -0
  405. /package/dist/{chunk-PT2YCQVK.js.map → chunk-TBVDI3LY.js.map} +0 -0
  406. /package/dist/{chunk-BMHQHH7Q.js.map → chunk-TLIFNRU5.js.map} +0 -0
  407. /package/dist/{chunk-YKGJ44Z7.js.map → chunk-TU6ZJTYT.js.map} +0 -0
  408. /package/dist/{chunk-55P6OBB7.js.map → chunk-U43RSPPX.js.map} +0 -0
  409. /package/dist/{chunk-K7RW7FI4.js.map → chunk-UADW5NOU.js.map} +0 -0
  410. /package/dist/{chunk-GRS2HIV6.js.map → chunk-UMV5GBTJ.js.map} +0 -0
  411. /package/dist/{chunk-4UIP3QNW.js.map → chunk-UNAMWK2Q.js.map} +0 -0
  412. /package/dist/{chunk-J4KAPKBJ.js.map → chunk-UORWAG7K.js.map} +0 -0
  413. /package/dist/{chunk-V55JASXO.js.map → chunk-UQRKAM2R.js.map} +0 -0
  414. /package/dist/{chunk-ZMVSKYTJ.js.map → chunk-VLRY2UH4.js.map} +0 -0
  415. /package/dist/{chunk-SPPOUJLN.js.map → chunk-VZFTON37.js.map} +0 -0
  416. /package/dist/{chunk-YQFBH5ZJ.js.map → chunk-WQUIJPN4.js.map} +0 -0
  417. /package/dist/{chunk-NZULV7LK.js.map → chunk-WVQDY3UM.js.map} +0 -0
  418. /package/dist/{chunk-3Y5JLSXS.js.map → chunk-X5RGG3ZW.js.map} +0 -0
  419. /package/dist/{chunk-PFCTSXHU.js.map → chunk-X6Z2SIRF.js.map} +0 -0
  420. /package/dist/{chunk-UHFGK6YA.js.map → chunk-XQGKS4OW.js.map} +0 -0
  421. /package/dist/{chunk-L2F3CFCH.js.map → chunk-Y22JWHCP.js.map} +0 -0
  422. /package/dist/{chunk-C53ER7AG.js.map → chunk-YVA6UPRN.js.map} +0 -0
  423. /package/dist/{chunk-226V6XXJ.js.map → chunk-Z2KP6PNF.js.map} +0 -0
  424. /package/dist/{chunk-G5EDYVSE.js.map → chunk-ZK2WT532.js.map} +0 -0
  425. /package/dist/{chunk-SFCD6OU7.js.map → chunk-ZNVBCKMV.js.map} +0 -0
  426. /package/dist/{chunk-BU2ABHFW.js.map → chunk-ZOFNZLAA.js.map} +0 -0
  427. /package/dist/{chunk-X2RP4TO5.js.map → chunk-ZVJKHGE5.js.map} +0 -0
  428. /package/dist/{classified-marker-KHDOYIJB.js.map → classified-marker-7HXDMYHI.js.map} +0 -0
  429. /package/dist/{collection-facade-LZIF7GWD.js.map → collection-facade-V63BEBGU.js.map} +0 -0
  430. /package/dist/{computed-XMFTHAXV.js.map → computed-LZLSYOPG.js.map} +0 -0
  431. /package/dist/{dead-filter-CXV5NS67.js.map → dead-filter-PZIJ4F4G.js.map} +0 -0
  432. /package/dist/{derive-3YSIS36W.js.map → delegation-7SSGIETH.js.map} +0 -0
  433. /package/dist/{executor-E6YDJ7YA.js.map → derive-IMI3BMDS.js.map} +0 -0
  434. /package/dist/{dispatch-2MQFMEGY.js.map → dispatch-4BUYPWMJ.js.map} +0 -0
  435. /package/dist/{dispatch-OFDA4WQV.js.map → dispatch-KOPDDPEQ.js.map} +0 -0
  436. /package/dist/{executor-NJX3YQQ4.js.map → executor-7SY3M6TZ.js.map} +0 -0
  437. /package/dist/{executor-WWM3DD4P.js.map → executor-SNHW4ELF.js.map} +0 -0
  438. /package/dist/{export-accessible-RLHU5G4W.js.map → executor-Z3IKK7G3.js.map} +0 -0
  439. /package/dist/{extract-partition-QII2EDCS.js.map → export-accessible-O7DIVFUO.js.map} +0 -0
  440. /package/dist/{find-6M7GYDUZ.js.map → extract-partition-ELZEJKXM.js.map} +0 -0
  441. /package/dist/{fanout-sidecar-BO67PCPO.js.map → fanout-sidecar-FYDRL7F3.js.map} +0 -0
  442. /package/dist/{issue-LPEEEMPP.js.map → find-KWB2RYWJ.js.map} +0 -0
  443. /package/dist/{ledger-MSJGWKGW.js.map → issue-5ZHXK4HJ.js.map} +0 -0
  444. /package/dist/{liberate-LEKDYKTC.js.map → ledger-UFJCB6T5.js.map} +0 -0
  445. /package/dist/{link-set-QXXSPPHL.js.map → liberate-OEOSNNFH.js.map} +0 -0
  446. /package/dist/{managed-secret-VSK7ANQI.js.map → link-set-KMUBFN2L.js.map} +0 -0
  447. /package/dist/{noydb-F2AUF6KL.js.map → managed-secret-H5WYSJ46.js.map} +0 -0
  448. /package/dist/{migrate-cek-GEBFYBDC.js.map → migrate-cek-SPESQAAO.js.map} +0 -0
  449. /package/dist/{register-SN72QALV.js.map → noydb-QDZ4QIKO.js.map} +0 -0
  450. /package/dist/{pod-handle-YE7CT66T.js.map → pod-handle-GJLCOUGK.js.map} +0 -0
  451. /package/dist/{post-register-QC5TEAW3.js.map → post-register-2AC2STHC.js.map} +0 -0
  452. /package/dist/{registry-253FF7C6.js.map → register-PIORIMMR.js.map} +0 -0
  453. /package/dist/{registry-3AZWQTDT.js.map → registry-BIMICFUH.js.map} +0 -0
  454. /package/dist/{registry-7UEBTHWG.js.map → registry-JGWLW53D.js.map} +0 -0
  455. /package/dist/{registry-IVJ67NFQ.js.map → registry-SELCBYEQ.js.map} +0 -0
  456. /package/dist/{request-withdrawal-UKVJZKX2.js.map → registry-W327UNYH.js.map} +0 -0
  457. /package/dist/{revoke-G2KXJQA5.js.map → request-withdrawal-K3SXXODI.js.map} +0 -0
  458. /package/dist/{reveal-YQTRNQFH.js.map → reveal-UAPBG743.js.map} +0 -0
  459. /package/dist/{signer-IUIYR3CX.js.map → revoke-6AFVXTWF.js.map} +0 -0
  460. /package/dist/{stale-SKV33PBU.js.map → signer-TW5MAQJP.js.map} +0 -0
  461. /package/dist/{storage-NPBYP2SM.js.map → stale-CROGJBIJ.js.map} +0 -0
  462. /package/dist/{walk-F4OLXI5H.js.map → storage-SO2L2Z5J.js.map} +0 -0
  463. /package/dist/{store-coordination-provider-Z6HKPOGM.js.map → store-coordination-provider-2YD2PTTP.js.map} +0 -0
  464. /package/dist/{verify-KWQIWLGH.js.map → verify-QWO5UKD6.js.map} +0 -0
  465. /package/dist/{withdraw-accessible-ZKPLZ66E.js.map → walk-3KEOL7BY.js.map} +0 -0
@@ -1,34 +1,38 @@
1
1
  import {
2
- SyncScheduler
3
- } from "./chunk-JBMMWYOH.js";
2
+ REJECTIONS_COLLECTION,
3
+ SyncScheduler,
4
+ rejectionKey
5
+ } from "./chunk-7UHLO4ZN.js";
4
6
  import {
5
7
  PERIODS_COLLECTION,
6
8
  PERIOD_ARCHIVES_COLLECTION,
7
9
  PERIOD_FREEZES_COLLECTION,
8
10
  PERIOD_TARGET_PURGES_COLLECTION,
9
11
  periodExclusiveUpperBound
10
- } from "./chunk-6WS35E6X.js";
12
+ } from "./chunk-A52BK5AY.js";
11
13
  import {
12
14
  parseKeyringEnvelope
13
- } from "./chunk-45VHWE4C.js";
15
+ } from "./chunk-QVUMKG3Z.js";
14
16
  import {
17
+ buildDeleteMarker,
15
18
  buildRecordEnvelope,
16
19
  decrypt,
17
20
  derivePresenceKey,
18
21
  derivePresenceTagKey,
19
22
  encrypt,
23
+ envelopeBodyForHash,
20
24
  envelopeBodySize,
21
25
  hmacSignHex,
22
26
  isDeleteMarker,
23
27
  isTombstoneShape
24
- } from "./chunk-4J3MNVVX.js";
28
+ } from "./chunk-WRLWEYHY.js";
25
29
  import {
26
30
  NOYDB_SYNC_VERSION
27
- } from "./chunk-2ELOCEXE.js";
31
+ } from "./chunk-URGHKZPL.js";
28
32
  import {
29
33
  ValidationError,
30
34
  isConflictError
31
- } from "./chunk-VSUQI5Q6.js";
35
+ } from "./chunk-2RH5SEY7.js";
32
36
 
33
37
  // src/with-sync/presence.ts
34
38
  var PresenceHandle = class {
@@ -287,6 +291,10 @@ function keyringEpoch(envelope) {
287
291
  return void 0;
288
292
  }
289
293
  }
294
+ function keyringIdentity(envelope) {
295
+ const file = parseKeyringEnvelope(envelope);
296
+ return `${file.roster_epoch ?? ""}:${file.roster_tag.data}:${file.display_name}`;
297
+ }
290
298
  function keyringSupersedes(candidate, current) {
291
299
  if (!current) return true;
292
300
  const c = keyringEpoch(candidate);
@@ -301,9 +309,26 @@ function versionSupersedes(candidate, current) {
301
309
  }
302
310
  var RESERVED_REPLICATION = [
303
311
  { collection: KEYRING_COLLECTION, supersedes: keyringSupersedes, propagateDeletes: true },
312
+ // core#91 — the broker seeds. `_broker/<brokerId>` is sealed under the
313
+ // admin `_broker` DEK (owner/admin only hold it), `_broker_member/<userId>`
314
+ // under that grantee's own DEK: both are ciphertext to the mover and to every
315
+ // other device, and both are exactly what a fresh device needs to mint its
316
+ // first credential. Revocation is a delete that rides the dirty log.
317
+ // core#97 — SECOND, right behind the roster: on a fresh device every target
318
+ // request before the seed has landed cannot be served by the device's own
319
+ // mint, so the seed travels before anything else a first pull carries.
320
+ { collection: "_broker_member", supersedes: versionSupersedes, propagateDeletes: true },
321
+ { collection: "_broker", supersedes: versionSupersedes, propagateDeletes: true },
304
322
  { collection: "_meta", idPrefix: "invite-audit-", supersedes: versionSupersedes, propagateDeletes: false },
305
323
  { collection: "_users", supersedes: versionSupersedes, propagateDeletes: true },
306
- { collection: "_delegations", supersedes: versionSupersedes, propagateDeletes: true }
324
+ { collection: "_delegations", supersedes: versionSupersedes, propagateDeletes: true },
325
+ // core#107 — the arbiter's admission refusals, so the WRITER learns its
326
+ // offline record was rejected instead of the verdict staying on the device
327
+ // that made it. Sealed under the refused record's OWN collection DEK, so
328
+ // this mover still reads nothing (`_v` is on the envelope) and a member
329
+ // without that collection receives ciphertext they quietly skip.
330
+ // `propagateDeletes: true` — clearing a refusal is how it stops being shown.
331
+ { collection: REJECTIONS_COLLECTION, supersedes: versionSupersedes, propagateDeletes: true }
307
332
  ];
308
333
  function inScope(rule, id) {
309
334
  return rule.idPrefix === void 0 || id.startsWith(rule.idPrefix);
@@ -324,12 +349,25 @@ async function mirrorRule(rule, from, to, vault, fromIds) {
324
349
  async function pushReserved(local, remote, vault) {
325
350
  let copied = 0;
326
351
  let keyringsCopied = [];
352
+ const staleKeyrings = [];
327
353
  for (const rule of RESERVED_REPLICATION) {
328
354
  const ids = await mirrorRule(rule, local, remote, vault);
329
355
  copied += ids.length;
330
- if (rule.collection === KEYRING_COLLECTION) keyringsCopied = ids;
356
+ if (rule.collection === KEYRING_COLLECTION) {
357
+ keyringsCopied = ids;
358
+ for (const id of await local.list(vault, KEYRING_COLLECTION)) {
359
+ if (ids.includes(id)) continue;
360
+ const [mine, theirs] = await Promise.all([local.get(vault, KEYRING_COLLECTION, id), remote.get(vault, KEYRING_COLLECTION, id)]);
361
+ if (mine && theirs && keyringIdentity(mine) !== keyringIdentity(theirs)) staleKeyrings.push({ id, local: mine, remote: theirs });
362
+ }
363
+ }
331
364
  }
332
- return { copied, deleted: 0, keyringsCopied };
365
+ return { copied, deleted: 0, keyringsCopied, ...staleKeyrings.length > 0 && { staleKeyrings } };
366
+ }
367
+ async function pullKeyringFile(remote, local, vault, userId) {
368
+ const rule = RESERVED_REPLICATION[0];
369
+ const ids = await mirrorRule(rule, remote, local, vault, [userId]);
370
+ return ids.length > 0;
333
371
  }
334
372
  async function pullReserved(remote, local, vault, protectedIds) {
335
373
  const localKeyrings = await local.list(vault, KEYRING_COLLECTION);
@@ -337,12 +375,14 @@ async function pullReserved(remote, local, vault, protectedIds) {
337
375
  let copied = 0;
338
376
  let deleted = 0;
339
377
  let keyringsCopied = [];
378
+ let remoteInitialised = false;
340
379
  for (const rule of RESERVED_REPLICATION) {
341
380
  const remoteIds = (await remote.list(vault, rule.collection)).filter((id) => inScope(rule, id));
381
+ if (rule.collection === KEYRING_COLLECTION) remoteInitialised = remoteIds.length > 0;
342
382
  const ids = await mirrorRule(rule, remote, local, vault, remoteIds);
343
383
  copied += ids.length;
344
384
  if (rule.collection === KEYRING_COLLECTION) keyringsCopied = ids;
345
- if (!rule.propagateDeletes || remoteIds.length === 0) continue;
385
+ if (!rule.propagateDeletes || !remoteInitialised) continue;
346
386
  const remoteSet = new Set(remoteIds);
347
387
  const protectedSet = protectedIds.get(rule.collection);
348
388
  const localIds = rule.collection === KEYRING_COLLECTION ? localKeyrings : await local.list(vault, rule.collection);
@@ -406,8 +446,22 @@ function buildPeriodScope(option, records) {
406
446
  }
407
447
 
408
448
  // src/with-sync/engine.ts
449
+ var REJECTED_COLLECTION = "_sync_rejected";
450
+ function parseParked(env) {
451
+ return JSON.parse(envelopeBodyForHash(env));
452
+ }
409
453
  var SyncEngine = class {
410
454
  mergeAuthority;
455
+ /** core#74 — the vault's admission gate for incoming records; wired at open like the other vault seams. */
456
+ admission;
457
+ #applied = [];
458
+ #rejected = [];
459
+ /** core#108 — this device's own records, refused by its own gates on the way OUT. */
460
+ #pushRejected = [];
461
+ /** core#107 — the vault's sealing capability for replicated refusals; wired at open like `admission`. */
462
+ courier;
463
+ /** core#107 — this device's refusals replicate. Configuration, not an authority proof — see `withSync`. */
464
+ arbiter;
411
465
  local;
412
466
  remote;
413
467
  strategy;
@@ -423,6 +477,8 @@ var SyncEngine = class {
423
477
  /** #1036 — last SUCCESSFUL push/pull. Never advanced by a failed attempt. */
424
478
  lastPush = null;
425
479
  lastPull = null;
480
+ /** core#72 — the target's restore epoch this device adopted; null before any. */
481
+ epoch = null;
426
482
  /**
427
483
  * #1036 — the failure that ended the most recent attempt, cleared by the next
428
484
  * success. Live state only: deliberately NOT persisted in `_sync/meta`, since a
@@ -490,9 +546,188 @@ var SyncEngine = class {
490
546
  setPeriodPullSource(source) {
491
547
  this.periodPullSource = source;
492
548
  }
549
+ /** core#81 — the collections this keyring names; the walk set for a paged pull. Wired by the vault at open. */
550
+ collectionNames;
551
+ /** Wire the paged-pull walk set (core#81). Same injection pattern as `setCacheInvalidator`. */
552
+ setCollectionNames(fn) {
553
+ this.collectionNames = fn;
554
+ }
555
+ /**
556
+ * core#81 — one chunk per page of one collection, for `pull({ paged: true })`.
557
+ * `listPage` when the store offers it (200 per page), else `list` + `get` in
558
+ * batches of the same size. Never more than one page in memory.
559
+ */
560
+ async *pagedChunks(names) {
561
+ const PAGE = 200;
562
+ const remote = this.remote;
563
+ for (const coll of names) {
564
+ if (typeof remote.listPage === "function") {
565
+ let cursor;
566
+ do {
567
+ const page = await remote.listPage(this.vault, coll, cursor, PAGE);
568
+ const records = {};
569
+ for (const { id, envelope } of page.items) records[id] = envelope;
570
+ if (page.items.length > 0) yield [coll, records];
571
+ cursor = page.nextCursor ?? void 0;
572
+ } while (cursor !== void 0);
573
+ } else {
574
+ const ids = await remote.list(this.vault, coll);
575
+ for (let i = 0; i < ids.length; i += PAGE) {
576
+ const records = {};
577
+ for (const id of ids.slice(i, i + PAGE)) {
578
+ const env = await remote.get(this.vault, coll, id);
579
+ if (env) records[id] = env;
580
+ }
581
+ if (Object.keys(records).length > 0) yield [coll, records];
582
+ }
583
+ }
584
+ }
585
+ }
493
586
  /** core#82 — when a pull replaces the caller's OWN keyring file, the vault reloads it in place. */
494
587
  rosterReload;
495
588
  /** Wire the roster-reload seam (core#82). Same injection pattern as `setCacheInvalidator`. */
589
+ /** core#107 — wire the vault's rejection sealing capability. */
590
+ setRejectionCourier(c) {
591
+ this.courier = c;
592
+ }
593
+ /** core#74 — wire the vault's admission gate. */
594
+ setAdmission(a) {
595
+ this.admission = a;
596
+ }
597
+ /** core#71 — after `vault.load()`: a new base. Drops the dirty log, the watermarks and the adopted epoch. */
598
+ async resetAfterRestore() {
599
+ await this.ensureLoaded();
600
+ this.dirty = [];
601
+ this.lastPush = null;
602
+ this.lastPull = null;
603
+ this.epoch = null;
604
+ await this.persistMeta();
605
+ }
606
+ /** core#72 — the target's epoch record, or null when the target was never replaced. */
607
+ async #remoteEpoch() {
608
+ const env = await this.remote.get(this.vault, "_sync", "epoch");
609
+ return env ? JSON.parse(envelopeBodyForHash(env)) : null;
610
+ }
611
+ /**
612
+ * core#72 — make the LOCAL vault authoritative on this target. Every local
613
+ * record goes up unconditionally, re-sealed above the target's version where
614
+ * the target moved on (so every peer's copy is superseded); every id the
615
+ * target holds that the local vault lacks gets a delete marker at
616
+ * `remote._v + 1` (so every peer removes it); the reserved collections are
617
+ * mirrored; then a new epoch is written at `_sync/epoch`. The dirty log is
618
+ * cleared: the target IS the local state now.
619
+ */
620
+ async replaceRemote() {
621
+ await this.ensureLoaded();
622
+ const actor = this.rosterReload?.userId ?? "sync";
623
+ const [localSnapshot, remoteSnapshot] = await Promise.all([this.local.loadAll(this.vault), this.remote.loadAll(this.vault)]);
624
+ let replaced = 0;
625
+ let tombstoned = 0;
626
+ for (const [collection, records] of Object.entries(localSnapshot)) {
627
+ if (collection === REJECTED_COLLECTION) continue;
628
+ for (const [id, envelope] of Object.entries(records)) {
629
+ const current = remoteSnapshot[collection]?.[id];
630
+ let out = envelope;
631
+ if (current && current._v >= envelope._v) {
632
+ out = this.mergeAuthority ? await this.mergeAuthority.advance(collection, id, envelope, current._v + 1) : { ...envelope, _v: current._v + 1 };
633
+ await this.local.put(this.vault, collection, id, out);
634
+ }
635
+ await this.remote.put(this.vault, collection, id, out);
636
+ replaced++;
637
+ }
638
+ }
639
+ for (const [collection, records] of Object.entries(remoteSnapshot)) {
640
+ if (collection === REJECTED_COLLECTION) continue;
641
+ for (const [id, envelope] of Object.entries(records)) {
642
+ if (localSnapshot[collection]?.[id] !== void 0) continue;
643
+ if (isTombstoneShape(envelope) || isDeleteMarker(envelope)) continue;
644
+ const marker = buildDeleteMarker({ collection, id }, envelope._v + 1, actor);
645
+ await this.remote.put(this.vault, collection, id, marker);
646
+ await this.local.put(this.vault, collection, id, marker);
647
+ tombstoned++;
648
+ }
649
+ }
650
+ const mirrored = await pushReserved(this.local, this.remote, this.vault);
651
+ const previous = await this.#remoteEpoch();
652
+ const epoch = (previous?.epoch ?? 0) + 1;
653
+ const record = { epoch, at: (/* @__PURE__ */ new Date()).toISOString(), by: actor, replaced, tombstoned };
654
+ await this.remote.put(this.vault, "_sync", "epoch", buildRecordEnvelope({ collection: "_sync", id: "epoch", version: epoch }, { iv: "", data: JSON.stringify(record) }));
655
+ this.dirty = [];
656
+ this.epoch = epoch;
657
+ this.lastPush = (/* @__PURE__ */ new Date()).toISOString();
658
+ await this.persistMeta();
659
+ const result = { epoch, replaced, tombstoned, reserved: mirrored.copied };
660
+ this.emitter.emit("sync:replace", { vault: this.vault, ...result });
661
+ return result;
662
+ }
663
+ /**
664
+ * core#72 — at the start of a pull: has the target been replaced since this
665
+ * device last synced? If so, park every unpushed local edit (same fate as an
666
+ * admission refusal, reason `restore-epoch`), drop the dirty log, adopt the
667
+ * epoch. The pull that follows then adopts the target: the restorer lifted
668
+ * its records above every peer's version and tombstoned what it removed.
669
+ */
670
+ async #crossEpoch() {
671
+ const remote = await this.#remoteEpoch();
672
+ if (!remote || remote.epoch === this.epoch) return { epoch: remote?.epoch ?? 0, resynced: false, parked: 0 };
673
+ const from = this.epoch;
674
+ let parked = 0;
675
+ for (const entry of this.dirty) {
676
+ if (entry.action === "delete") continue;
677
+ const env = await this.local.get(this.vault, entry.collection, entry.id);
678
+ if (!env) continue;
679
+ const rejection = {
680
+ vault: this.vault,
681
+ collection: entry.collection,
682
+ id: entry.id,
683
+ version: env._v,
684
+ reason: `restore-epoch: the target was replaced (epoch ${remote.epoch}) after this edit; readmit to keep it locally, then put again to push`,
685
+ ...env._by !== void 0 ? { by: env._by } : {},
686
+ at: (/* @__PURE__ */ new Date()).toISOString()
687
+ };
688
+ const parkedRecord = { rejection, envelope: env };
689
+ await this.local.put(
690
+ this.vault,
691
+ REJECTED_COLLECTION,
692
+ `${entry.collection}::${entry.id}`,
693
+ buildRecordEnvelope({ collection: REJECTED_COLLECTION, id: `${entry.collection}::${entry.id}`, version: 1 }, { iv: "", data: JSON.stringify(parkedRecord) })
694
+ );
695
+ this.#rejected.push(rejection);
696
+ this.emitter.emit("sync:rejected", rejection);
697
+ await this.local.delete(this.vault, entry.collection, entry.id);
698
+ await this.cacheInvalidator?.(entry.collection, entry.id, "delete");
699
+ parked++;
700
+ }
701
+ this.dirty = [];
702
+ this.epoch = remote.epoch;
703
+ this.emitter.emit("sync:epoch", { vault: this.vault, from, to: remote.epoch, parked });
704
+ return { epoch: remote.epoch, resynced: true, parked };
705
+ }
706
+ /** core#74 — the parked refusals on this device, and their two fates. */
707
+ rejected() {
708
+ const key = (collection, id) => `${collection}::${id}`;
709
+ return {
710
+ list: async () => {
711
+ const out = [];
712
+ for (const id of await this.local.list(this.vault, REJECTED_COLLECTION)) {
713
+ const env = await this.local.get(this.vault, REJECTED_COLLECTION, id);
714
+ if (!env) continue;
715
+ out.push(parseParked(env).rejection);
716
+ }
717
+ return out;
718
+ },
719
+ readmit: async (collection, id) => {
720
+ const env = await this.local.get(this.vault, REJECTED_COLLECTION, key(collection, id));
721
+ if (!env) throw new ValidationError(`sync: no rejected record parked for "${collection}/${id}".`);
722
+ await this.local.put(this.vault, collection, id, parseParked(env).envelope);
723
+ await this.local.delete(this.vault, REJECTED_COLLECTION, key(collection, id));
724
+ await this.cacheInvalidator?.(collection, id, "put");
725
+ },
726
+ discard: async (collection, id) => {
727
+ await this.local.delete(this.vault, REJECTED_COLLECTION, key(collection, id));
728
+ }
729
+ };
730
+ }
496
731
  setRosterReload(seam) {
497
732
  this.rosterReload = seam;
498
733
  }
@@ -531,6 +766,7 @@ var SyncEngine = class {
531
766
  this.label = opts.label;
532
767
  this.policy = opts.syncPolicy;
533
768
  this.mergeAuthority = opts.mergeAuthority;
769
+ this.arbiter = opts.arbiter === true;
534
770
  const policy = opts.syncPolicy;
535
771
  if (policy && (policy.push.mode !== "manual" || policy.pull.mode !== "manual")) {
536
772
  this.scheduler = new SyncScheduler(policy, {
@@ -623,11 +859,250 @@ var SyncEngine = class {
623
859
  this.dirty = this.dirty.filter((d) => !(d.collection === collection && d.id === id));
624
860
  if (this.dirty.length !== before) await this.persistMeta();
625
861
  }
862
+ /**
863
+ * core#74/#108 — park a refused envelope under `_sync_rejected` and announce
864
+ * it. One helper for both fates: a record that ARRIVED and was turned away,
865
+ * and (core#108) this device's own record refused on the way out. The
866
+ * parking key is `collection::id`, so re-refusing the same record overwrites
867
+ * rather than accumulating.
868
+ */
869
+ async #park(collection, id, envelope, reason, origin) {
870
+ const rejection = {
871
+ vault: this.vault,
872
+ collection,
873
+ id,
874
+ reason,
875
+ version: envelope._v,
876
+ ...envelope._by !== void 0 ? { by: envelope._by } : {},
877
+ at: (/* @__PURE__ */ new Date()).toISOString(),
878
+ ...envelope._ts !== void 0 ? { recordAt: envelope._ts } : {},
879
+ origin
880
+ };
881
+ await this.#parkBuilt(collection, id, envelope, rejection);
882
+ if (origin === "sync-apply") await this.#publishRejection(collection, id, rejection);
883
+ return rejection;
884
+ }
885
+ /** Store half of `#park`, also used for a refusal that arrived already made (core#107). */
886
+ async #parkBuilt(collection, id, envelope, rejection) {
887
+ const parked = { rejection, envelope };
888
+ await this.local.put(
889
+ this.vault,
890
+ REJECTED_COLLECTION,
891
+ `${collection}::${id}`,
892
+ buildRecordEnvelope({ collection: REJECTED_COLLECTION, id: `${collection}::${id}`, version: 1 }, { iv: "", data: JSON.stringify(parked) })
893
+ );
894
+ this.emitter.emit("sync:rejected", rejection);
895
+ }
896
+ /**
897
+ * core#107 — turn the arbiter's replicated refusals into THIS device's
898
+ * notification. Runs after the reserved phase has brought `_sync_rejections`.
899
+ *
900
+ * Three filters, each load-bearing:
901
+ * - `courier.open` returns `null` for a member who holds no DEK for that
902
+ * collection. They received the ciphertext like everyone else and skip it.
903
+ * - a refusal names a VERSION. If this device's copy has moved on, the
904
+ * refusal is about a record that no longer exists here and is ignored —
905
+ * otherwise a stale verdict would re-fire against an edit that may well
906
+ * pass now.
907
+ * - an existing parking at the same version means this was already
908
+ * reported; the parking record IS the dedupe, so no extra state is kept
909
+ * and it survives a restart.
910
+ *
911
+ * ⛔ REPORT ONLY (ruled): the local record is never deleted, tombstoned or
912
+ * hidden. The refusal is information; what to do about it is the writer's.
913
+ */
914
+ async #scanReplicatedRejections() {
915
+ if (!this.courier) return 0;
916
+ let keys;
917
+ try {
918
+ keys = await this.local.list(this.vault, REJECTIONS_COLLECTION);
919
+ } catch {
920
+ return 0;
921
+ }
922
+ let seen = 0;
923
+ for (const parkKey of keys) {
924
+ const sep = parkKey.indexOf("::");
925
+ if (sep < 0) continue;
926
+ const collection = parkKey.slice(0, sep), id = parkKey.slice(sep + 2);
927
+ const env = await this.local.get(this.vault, REJECTIONS_COLLECTION, parkKey);
928
+ if (!env) continue;
929
+ const rejection = await this.courier.open(collection, id, env);
930
+ if (!rejection) continue;
931
+ const mine = await this.local.get(this.vault, collection, id);
932
+ if (!mine || mine._v !== rejection.version) continue;
933
+ const existing = await this.local.get(this.vault, REJECTED_COLLECTION, parkKey);
934
+ if (existing && parseParked(existing).rejection.version === rejection.version) continue;
935
+ const replicated = { ...rejection, origin: "arbiter" };
936
+ await this.#parkBuilt(collection, id, mine, replicated);
937
+ this.#rejected.push(replicated);
938
+ seen++;
939
+ }
940
+ return seen;
941
+ }
942
+ /**
943
+ * core#107 — the ARBITER's refusals replicate, so the writer finds out.
944
+ *
945
+ * Only `sync-apply` refusals: a `push-recheck` refusal (core#108) never left
946
+ * this device, so there is no other device to tell, and a `restore-epoch`
947
+ * parking is this device adopting a new base rather than a judgement about
948
+ * the record.
949
+ *
950
+ * ⛔ Best-effort by construction: a failure to publish must not fail the
951
+ * pull that produced it. The local parking, the `PullResult.rejected` row
952
+ * and the event have already happened and are this device's real answer;
953
+ * replication is the courtesy on top. So a courier that cannot seal (no DEK
954
+ * for that collection) or a store that refuses the write leaves the refusal
955
+ * exactly as it was before core#107.
956
+ */
957
+ async #publishRejection(collection, id, rejection) {
958
+ if (!this.arbiter || !this.courier) return;
959
+ try {
960
+ const key = rejectionKey(collection, id);
961
+ const current = await this.local.get(this.vault, REJECTIONS_COLLECTION, key);
962
+ const envelope = await this.courier.seal(collection, id, rejection, (current?._v ?? 0) + 1);
963
+ if (envelope) await this.local.put(this.vault, REJECTIONS_COLLECTION, key, envelope);
964
+ } catch {
965
+ }
966
+ }
967
+ /**
968
+ * core#108 — re-run this device's OWN gates against a dirty record before it
969
+ * is pushed. A record written offline may no longer pass the rules this
970
+ * device now holds (a period closed while it was away); today it pushes and
971
+ * every OTHER device refuses it at admission, so the writer is the last to
972
+ * know. Judging it here means the writer's own device catches it the moment
973
+ * it reconnects, before anyone else sees the record.
974
+ *
975
+ * Returns `true` when the entry was WITHHELD. The entry stays dirty and the
976
+ * local copy is untouched, so nothing is lost and a later push carries it
977
+ * unchanged once the rule passes again — which is why a refusal here needs
978
+ * no `readmit()` and no dirty-log surgery.
979
+ *
980
+ * ⛔ `rekey` is never re-checked: core#100 rewrites the SAME content under a
981
+ * new key, so judging it would refuse a record for its content having been
982
+ * legal at write time — the same wrong answer core#74 fixed with
983
+ * `admit: false` for a device's own re-versioned records.
984
+ */
985
+ async #withheldByRecheck(entry, envelope) {
986
+ if (!this.admission || entry.action === "rekey") return false;
987
+ if (isTombstoneShape(envelope) || isDeleteMarker(envelope)) return false;
988
+ const verdict = await this.admission.admit(entry.collection, entry.id, envelope, "push-recheck");
989
+ if (verdict.admitted) return false;
990
+ this.#pushRejected.push(await this.#park(entry.collection, entry.id, envelope, verdict.reason, "push-recheck"));
991
+ return true;
992
+ }
993
+ /** One dirty entry of `push()`: the CAS put, the tombstone assertion, the delete, and every conflict branch. Extracted so `push({ concurrency })` can run entries in a bounded pool (core#93). */
994
+ async #pushOne(i, entry, acc) {
995
+ try {
996
+ if (entry.action === "delete") {
997
+ await this.remote.delete(this.vault, entry.collection, entry.id);
998
+ acc.completed.push(i);
999
+ acc.pushed++;
1000
+ } else {
1001
+ const envelope = await this.local.get(this.vault, entry.collection, entry.id);
1002
+ if (!envelope) {
1003
+ acc.completed.push(i);
1004
+ return;
1005
+ }
1006
+ if (isTombstoneShape(envelope)) {
1007
+ await this.remote.put(this.vault, entry.collection, entry.id, envelope);
1008
+ acc.completed.push(i);
1009
+ acc.pushed++;
1010
+ return;
1011
+ }
1012
+ if (await this.#withheldByRecheck(entry, envelope)) return;
1013
+ try {
1014
+ await this.remote.put(
1015
+ this.vault,
1016
+ entry.collection,
1017
+ entry.id,
1018
+ envelope,
1019
+ // core#100 — a `rekey` re-encrypted the record in place: same `_v`,
1020
+ // new bytes; the target still at that version takes it.
1021
+ entry.action === "rekey" ? entry.version : entry.version - 1
1022
+ );
1023
+ acc.completed.push(i);
1024
+ acc.pushed++;
1025
+ acc.bytes += envelopeBodySize(envelope);
1026
+ } catch (err) {
1027
+ if (isConflictError(err)) {
1028
+ const remoteEnvelope = await this.remote.get(this.vault, entry.collection, entry.id);
1029
+ if (remoteEnvelope) {
1030
+ if (remoteEnvelope._v === envelope._v && envelopeBodyForHash(remoteEnvelope) === envelopeBodyForHash(envelope)) {
1031
+ acc.completed.push(i);
1032
+ } else if (isTombstoneShape(remoteEnvelope)) {
1033
+ await this.applyRemote(entry.collection, entry.id, remoteEnvelope);
1034
+ acc.erasures.push(this.reportErasure(entry.collection, entry.id, remoteEnvelope, envelope, "push"));
1035
+ acc.completed.push(i);
1036
+ } else if (remoteEnvelope._v === envelope._v && isDeleteMarker(remoteEnvelope) !== isDeleteMarker(envelope) && !this.conflictResolvers.get(entry.collection)) {
1037
+ if (isDeleteMarker(remoteEnvelope)) {
1038
+ await this.applyRemote(entry.collection, entry.id, remoteEnvelope);
1039
+ acc.completed.push(i);
1040
+ } else {
1041
+ await this.remote.put(this.vault, entry.collection, entry.id, envelope);
1042
+ acc.completed.push(i);
1043
+ acc.pushed++;
1044
+ }
1045
+ } else {
1046
+ const { handled, conflict } = await this.handleConflict(
1047
+ entry.collection,
1048
+ entry.id,
1049
+ envelope,
1050
+ remoteEnvelope,
1051
+ "push"
1052
+ );
1053
+ acc.conflicts.push(conflict);
1054
+ if (handled === "local") {
1055
+ const winner = await this.advancePastRemote(conflict.local, entry.collection, entry.id, remoteEnvelope);
1056
+ await this.remote.put(this.vault, entry.collection, entry.id, winner);
1057
+ if (winner !== conflict.local) await this.applyRemote(entry.collection, entry.id, winner, { admit: false });
1058
+ acc.completed.push(i);
1059
+ acc.pushed++;
1060
+ } else if (handled === "remote") {
1061
+ await this.applyRemote(entry.collection, entry.id, conflict.remote);
1062
+ acc.completed.push(i);
1063
+ } else if (handled === "merged" && conflict.local !== envelope) {
1064
+ const merged = conflict.local;
1065
+ await this.remote.put(this.vault, entry.collection, entry.id, merged);
1066
+ await this.applyRemote(entry.collection, entry.id, merged);
1067
+ acc.completed.push(i);
1068
+ acc.pushed++;
1069
+ }
1070
+ }
1071
+ }
1072
+ } else {
1073
+ throw err;
1074
+ }
1075
+ }
1076
+ }
1077
+ } catch (err) {
1078
+ acc.errors.push(err instanceof Error ? err : new Error(String(err)));
1079
+ }
1080
+ }
1081
+ /**
1082
+ * core#92 — mark every user-collection record in the local store dirty at its
1083
+ * current version, so the next push sends it (CAS against `_v - 1`, so a
1084
+ * remote that already holds a newer copy resolves through the conflict path
1085
+ * instead of being overwritten). Records already dirty keep their entry.
1086
+ */
1087
+ async markAllDirty() {
1088
+ await this.ensureLoaded();
1089
+ const snapshot = await this.local.loadAll(this.vault);
1090
+ for (const [collection, records] of Object.entries(snapshot)) {
1091
+ if (collection === REJECTED_COLLECTION) continue;
1092
+ for (const [id, envelope] of Object.entries(records)) {
1093
+ if (this.dirty.some((d) => d.collection === collection && d.id === id)) continue;
1094
+ this.dirty.push({ vault: this.vault, collection, id, action: "put", version: envelope._v, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
1095
+ }
1096
+ }
1097
+ await this.persistMeta();
1098
+ }
626
1099
  /** Push dirty records to remote adapter. Accepts optional `PushOptions` for partial sync. */
627
1100
  async push(options) {
628
1101
  await this.ensureLoaded();
1102
+ if (options?.full) await this.markAllDirty();
629
1103
  this.graphBatchController?.begin();
630
- let pushed = 0;
1104
+ let reserved = 0;
1105
+ this.#pushRejected = [];
631
1106
  const conflicts = [];
632
1107
  const erasures = [];
633
1108
  const errors = [];
@@ -635,101 +1110,39 @@ var SyncEngine = class {
635
1110
  const expanded = options?.collections ? this.pairExpander?.(options.collections) ?? options.collections : null;
636
1111
  const filter = expanded ? /* @__PURE__ */ new Set([...expanded, ...this.reservedDictExpander?.(expanded) ?? []]) : null;
637
1112
  const pushTotal = filter ? this.dirty.filter((d) => filter.has(d.collection)).length : this.dirty.length;
638
- let pushBytes = 0;
639
- for (let i = 0; i < this.dirty.length; i++) {
640
- const entry = this.dirty[i];
641
- if (filter && !filter.has(entry.collection)) {
642
- continue;
643
- }
644
- this.progress({ direction: "push", phase: "records", records: pushed, bytes: pushBytes, total: { records: pushTotal } });
645
- try {
646
- if (entry.action === "delete") {
647
- await this.remote.delete(this.vault, entry.collection, entry.id);
648
- completed.push(i);
649
- pushed++;
650
- } else {
651
- const envelope = await this.local.get(this.vault, entry.collection, entry.id);
652
- if (!envelope) {
653
- completed.push(i);
654
- continue;
655
- }
656
- if (isTombstoneShape(envelope)) {
657
- await this.remote.put(this.vault, entry.collection, entry.id, envelope);
658
- completed.push(i);
659
- pushed++;
660
- continue;
661
- }
662
- try {
663
- await this.remote.put(
664
- this.vault,
665
- entry.collection,
666
- entry.id,
667
- envelope,
668
- entry.version - 1
669
- );
670
- completed.push(i);
671
- pushed++;
672
- pushBytes += envelopeBodySize(envelope);
673
- } catch (err) {
674
- if (isConflictError(err)) {
675
- const remoteEnvelope = await this.remote.get(this.vault, entry.collection, entry.id);
676
- if (remoteEnvelope) {
677
- if (isTombstoneShape(remoteEnvelope)) {
678
- await this.applyRemote(entry.collection, entry.id, remoteEnvelope);
679
- erasures.push(this.reportErasure(entry.collection, entry.id, remoteEnvelope, envelope, "push"));
680
- completed.push(i);
681
- } else if (remoteEnvelope._v === envelope._v && isDeleteMarker(remoteEnvelope) !== isDeleteMarker(envelope) && !this.conflictResolvers.get(entry.collection)) {
682
- if (isDeleteMarker(remoteEnvelope)) {
683
- await this.applyRemote(entry.collection, entry.id, remoteEnvelope);
684
- completed.push(i);
685
- } else {
686
- await this.remote.put(this.vault, entry.collection, entry.id, envelope);
687
- completed.push(i);
688
- pushed++;
689
- }
690
- } else {
691
- const { handled, conflict } = await this.handleConflict(
692
- entry.collection,
693
- entry.id,
694
- envelope,
695
- remoteEnvelope,
696
- "push"
697
- );
698
- conflicts.push(conflict);
699
- if (handled === "local") {
700
- const winner = await this.advancePastRemote(conflict.local, entry.collection, entry.id, remoteEnvelope);
701
- await this.remote.put(this.vault, entry.collection, entry.id, winner);
702
- if (winner !== conflict.local) await this.applyRemote(entry.collection, entry.id, winner);
703
- completed.push(i);
704
- pushed++;
705
- } else if (handled === "remote") {
706
- await this.applyRemote(entry.collection, entry.id, conflict.remote);
707
- completed.push(i);
708
- } else if (handled === "merged" && conflict.local !== envelope) {
709
- const merged = conflict.local;
710
- await this.remote.put(this.vault, entry.collection, entry.id, merged);
711
- await this.applyRemote(entry.collection, entry.id, merged);
712
- completed.push(i);
713
- pushed++;
714
- }
715
- }
716
- }
717
- } else {
718
- throw err;
719
- }
720
- }
721
- }
722
- } catch (err) {
723
- errors.push(err instanceof Error ? err : new Error(String(err)));
1113
+ const acc = { pushed: 0, bytes: 0, completed, conflicts, erasures, errors };
1114
+ const eligible = [...this.dirty.keys()].filter((i) => !filter || filter.has(this.dirty[i].collection));
1115
+ const width = Math.max(1, Math.floor(options?.concurrency ?? 1));
1116
+ let next = 0;
1117
+ const worker = async () => {
1118
+ while (next < eligible.length) {
1119
+ const i = eligible[next++];
1120
+ this.progress({ direction: "push", phase: "records", records: acc.pushed, bytes: acc.bytes, total: { records: pushTotal } });
1121
+ await this.#pushOne(i, this.dirty[i], acc);
724
1122
  }
725
- }
1123
+ };
1124
+ await Promise.all(Array.from({ length: Math.min(width, eligible.length) }, () => worker()));
1125
+ const pushed = acc.pushed;
1126
+ const pushBytes = acc.bytes;
726
1127
  for (const i of completed.sort((a, b) => b - a)) {
727
1128
  this.dirty.splice(i, 1);
728
1129
  }
729
1130
  if (!filter) {
730
1131
  this.progress({ direction: "push", phase: "reserved", records: pushed, bytes: pushBytes, total: { records: pushTotal } });
731
1132
  try {
732
- await pushReserved(this.local, this.remote, this.vault);
1133
+ const mirrored = await pushReserved(this.local, this.remote, this.vault);
1134
+ reserved = mirrored.copied;
1135
+ for (const stale of mirrored.staleKeyrings ?? []) {
1136
+ conflicts.push({
1137
+ vault: this.vault,
1138
+ collection: "_keyring",
1139
+ id: stale.id,
1140
+ local: stale.local,
1141
+ remote: stale.remote,
1142
+ localVersion: stale.local._v,
1143
+ remoteVersion: stale.remote._v
1144
+ });
1145
+ }
733
1146
  } catch (err) {
734
1147
  errors.push(err instanceof Error ? err : new Error(String(err)));
735
1148
  }
@@ -742,7 +1155,9 @@ var SyncEngine = class {
742
1155
  } finally {
743
1156
  await this.graphBatchController?.flush();
744
1157
  }
745
- const result = { pushed, conflicts, errors, erasures };
1158
+ const pushRejected = this.#pushRejected;
1159
+ this.#pushRejected = [];
1160
+ const result = { pushed, conflicts, errors, erasures, ...reserved > 0 && { reserved }, ...pushRejected.length > 0 && { rejected: pushRejected } };
746
1161
  this.emitter.emit("sync:push", result);
747
1162
  return result;
748
1163
  }
@@ -750,15 +1165,19 @@ var SyncEngine = class {
750
1165
  async pull(options) {
751
1166
  await this.ensureLoaded();
752
1167
  let pulled = 0;
1168
+ let reserved = 0;
1169
+ this.#applied = [];
1170
+ this.#rejected = [];
753
1171
  const conflicts = [];
754
- const erasures = [];
755
1172
  const errors = [];
1173
+ let crossed = { epoch: 0, resynced: false, parked: 0 };
1174
+ const erasures = [];
756
1175
  if (!options?.collections) {
757
1176
  this.progress({ direction: "pull", phase: "reserved", records: 0, bytes: 0 });
758
1177
  try {
759
1178
  const protectedIds = /* @__PURE__ */ new Map();
760
1179
  for (const d of this.dirty) {
761
- if (d.action !== "put") continue;
1180
+ if (d.action === "delete") continue;
762
1181
  let set = protectedIds.get(d.collection);
763
1182
  if (!set) {
764
1183
  set = /* @__PURE__ */ new Set();
@@ -766,12 +1185,23 @@ var SyncEngine = class {
766
1185
  }
767
1186
  set.add(d.id);
768
1187
  }
769
- const { keyringsCopied } = await pullReserved(this.remote, this.local, this.vault, protectedIds);
770
- if (this.rosterReload && keyringsCopied.includes(this.rosterReload.userId)) await this.rosterReload.reload();
1188
+ const mirrored = await pullReserved(this.remote, this.local, this.vault, protectedIds);
1189
+ reserved = mirrored.copied + mirrored.deleted;
1190
+ if (this.rosterReload && mirrored.keyringsCopied.includes(this.rosterReload.userId)) await this.rosterReload.reload();
1191
+ } catch (err) {
1192
+ errors.push(err instanceof Error ? err : new Error(String(err)));
1193
+ }
1194
+ try {
1195
+ await this.#scanReplicatedRejections();
771
1196
  } catch (err) {
772
1197
  errors.push(err instanceof Error ? err : new Error(String(err)));
773
1198
  }
774
1199
  }
1200
+ try {
1201
+ crossed = await this.#crossEpoch();
1202
+ } catch (err) {
1203
+ errors.push(err instanceof Error ? err : new Error(String(err)));
1204
+ }
775
1205
  let periodScope = null;
776
1206
  let phases = null;
777
1207
  if (options?.periods !== void 0) {
@@ -792,7 +1222,7 @@ var SyncEngine = class {
792
1222
  const remoteEnvelope = await this.remote.get(this.vault, collName, id);
793
1223
  if (!remoteEnvelope) continue;
794
1224
  const localEnvelope = await this.local.get(this.vault, collName, id);
795
- if (!localEnvelope || remoteEnvelope._v > localEnvelope._v) {
1225
+ if (!localEnvelope || this.#remoteSupersedes(collName, id, remoteEnvelope, localEnvelope)) {
796
1226
  await this.applyRemote(collName, id, remoteEnvelope);
797
1227
  pulled++;
798
1228
  }
@@ -817,11 +1247,21 @@ var SyncEngine = class {
817
1247
  const expanded = options?.collections ? this.pairExpander?.(options.collections) ?? options.collections : null;
818
1248
  const filter = expanded ? /* @__PURE__ */ new Set([...expanded, ...this.reservedDictExpander?.(expanded) ?? []]) : null;
819
1249
  try {
820
- const remoteSnapshot = await this.remote.loadAll(this.vault);
821
1250
  let pullTotal = 0;
822
- for (const [collName, records] of Object.entries(remoteSnapshot)) {
823
- if (filter && !filter.has(collName)) continue;
824
- pullTotal += Object.keys(records).length;
1251
+ let chunks;
1252
+ if (options?.paged) {
1253
+ const known = new Set(this.collectionNames?.() ?? []);
1254
+ for (const name of filter ?? []) if (!name.startsWith("_")) known.add(name);
1255
+ const names = [...known].filter((n) => !n.startsWith("_") && (!filter || filter.has(n)));
1256
+ for (const name of names) pullTotal += (await this.remote.list(this.vault, name)).length;
1257
+ chunks = this.pagedChunks(names);
1258
+ } else {
1259
+ const remoteSnapshot = await this.remote.loadAll(this.vault);
1260
+ for (const [collName, records] of Object.entries(remoteSnapshot)) {
1261
+ if (filter && !filter.has(collName)) continue;
1262
+ pullTotal += Object.keys(records).length;
1263
+ }
1264
+ chunks = Object.entries(remoteSnapshot);
825
1265
  }
826
1266
  const pullSample = () => ({
827
1267
  direction: "pull",
@@ -830,7 +1270,7 @@ var SyncEngine = class {
830
1270
  bytes: this.pullBytes,
831
1271
  total: { records: pullTotal }
832
1272
  });
833
- for (const [collName, records] of Object.entries(remoteSnapshot)) {
1273
+ for await (const [collName, records] of chunks) {
834
1274
  if (filter && !filter.has(collName)) {
835
1275
  continue;
836
1276
  }
@@ -877,7 +1317,7 @@ var SyncEngine = class {
877
1317
  this.dirty = this.dirty.filter((d) => !(d.collection === collName && d.id === id));
878
1318
  pulled++;
879
1319
  }
880
- } else if (remoteEnvelope._v > localEnvelope._v) {
1320
+ } else if (this.#remoteSupersedes(collName, id, remoteEnvelope, localEnvelope)) {
881
1321
  const isDirty = this.dirty.some((d) => d.collection === collName && d.id === id);
882
1322
  if (isDirty) {
883
1323
  const { handled, conflict } = await this.handleConflict(
@@ -927,7 +1367,7 @@ var SyncEngine = class {
927
1367
  this.dirty = this.dirty.filter((d) => !(d.collection === collName && d.id === id));
928
1368
  pulled++;
929
1369
  }
930
- } else if (remoteEnvelope._v > localEnvelope._v) {
1370
+ } else if (this.#remoteSupersedes(collName, id, remoteEnvelope, localEnvelope)) {
931
1371
  await this.applyRemote(collName, id, remoteEnvelope);
932
1372
  this.dirty = this.dirty.filter((d) => !(d.collection === collName && d.id === id));
933
1373
  pulled++;
@@ -950,7 +1390,22 @@ var SyncEngine = class {
950
1390
  this.pullByteSink = null;
951
1391
  await this.graphBatchController?.flush();
952
1392
  }
953
- const result = { pulled, conflicts, errors, erasures, ...phases !== null ? { phases } : {} };
1393
+ pulled = Math.max(0, pulled - this.#rejected.length);
1394
+ const applied = this.#applied, rejected = this.#rejected;
1395
+ this.#applied = [];
1396
+ this.#rejected = [];
1397
+ const result = {
1398
+ pulled,
1399
+ conflicts,
1400
+ errors,
1401
+ erasures,
1402
+ ...reserved > 0 && { reserved },
1403
+ ...phases !== null ? { phases } : {},
1404
+ ...applied.length > 0 && { applied },
1405
+ ...rejected.length > 0 && { rejected },
1406
+ ...crossed.epoch > 0 && { epoch: crossed.epoch },
1407
+ ...crossed.resynced && { resynced: true }
1408
+ };
954
1409
  this.emitter.emit("sync:pull", result);
955
1410
  return result;
956
1411
  }
@@ -968,6 +1423,7 @@ var SyncEngine = class {
968
1423
  async pushFiltered(predicate) {
969
1424
  await this.ensureLoaded();
970
1425
  let pushed = 0;
1426
+ this.#pushRejected = [];
971
1427
  const conflicts = [];
972
1428
  const erasures = [];
973
1429
  const errors = [];
@@ -992,6 +1448,7 @@ var SyncEngine = class {
992
1448
  pushed++;
993
1449
  continue;
994
1450
  }
1451
+ if (await this.#withheldByRecheck(entry, envelope)) continue;
995
1452
  try {
996
1453
  await this.remote.put(
997
1454
  this.vault,
@@ -1031,7 +1488,7 @@ var SyncEngine = class {
1031
1488
  if (handled === "local") {
1032
1489
  const winner = await this.advancePastRemote(conflict.local, entry.collection, entry.id, remoteEnvelope);
1033
1490
  await this.remote.put(this.vault, entry.collection, entry.id, winner);
1034
- if (winner !== conflict.local) await this.applyRemote(entry.collection, entry.id, winner);
1491
+ if (winner !== conflict.local) await this.applyRemote(entry.collection, entry.id, winner, { admit: false });
1035
1492
  completed.push(i);
1036
1493
  pushed++;
1037
1494
  } else if (handled === "remote") {
@@ -1060,7 +1517,9 @@ var SyncEngine = class {
1060
1517
  }
1061
1518
  this.recordOutcome("push", errors);
1062
1519
  await this.persistMeta();
1063
- const result = { pushed, conflicts, errors, erasures };
1520
+ const pushRejected = this.#pushRejected;
1521
+ this.#pushRejected = [];
1522
+ const result = { pushed, conflicts, errors, erasures, ...pushRejected.length > 0 && { rejected: pushRejected } };
1064
1523
  this.emitter.emit("sync:push", result);
1065
1524
  return result;
1066
1525
  }
@@ -1098,6 +1557,18 @@ var SyncEngine = class {
1098
1557
  * what the caller can read. Requires a `MergeAuthority` (a vault-attached
1099
1558
  * engine on an encrypted vault).
1100
1559
  */
1560
+ /**
1561
+ * core#96 (pilot-1, finding B) — bring one member's keyring file down from
1562
+ * this target if the target's copy supersedes ours. Called by the kernel
1563
+ * before an authority edit; reloads the caller's own keyring in place when
1564
+ * it is the one refreshed (core#82).
1565
+ */
1566
+ async refreshKeyring(userId) {
1567
+ await this.ensureLoaded();
1568
+ const copied = await pullKeyringFile(this.remote, this.local, this.vault, userId);
1569
+ if (copied && this.rosterReload && userId === this.rosterReload.userId) await this.rosterReload.reload();
1570
+ return copied;
1571
+ }
1101
1572
  async realign() {
1102
1573
  await this.ensureLoaded();
1103
1574
  if (!this.mergeAuthority) throw new ValidationError("realign: requires a vault-attached engine on an encrypted vault (no MergeAuthority).");
@@ -1132,6 +1603,8 @@ var SyncEngine = class {
1132
1603
  dirty: this.dirty.length,
1133
1604
  lastPush: this.lastPush,
1134
1605
  lastPull: this.lastPull,
1606
+ ...this.epoch !== null ? { epoch: this.epoch } : {},
1607
+ // core#72
1135
1608
  ...this.inFlight ? { inFlight: this.inFlight } : {},
1136
1609
  online: this.isOnline,
1137
1610
  ...this.lastError ? { lastError: this.lastError } : {},
@@ -1215,13 +1688,41 @@ var SyncEngine = class {
1215
1688
  if (!this.mergeAuthority) return { ...winner, _v: toVersion };
1216
1689
  return this.mergeAuthority.advance(collection, id, winner, toVersion);
1217
1690
  }
1218
- async applyRemote(collection, id, envelope) {
1691
+ /**
1692
+ * core#100 — does the remote copy supersede ours? A higher `_v`, as always;
1693
+ * or the SAME `_v` with different bytes while we hold no pending write for
1694
+ * it: a DEK rotation re-encrypts records in place, and a replica that kept
1695
+ * the old ciphertext would read `TamperedError` under the delivered key.
1696
+ * `applyRemote` still authenticates it (`MergeAuthority`) before it lands.
1697
+ */
1698
+ #remoteSupersedes(collection, id, remote, local) {
1699
+ if (remote._v > local._v) return true;
1700
+ if (remote._v !== local._v) return false;
1701
+ if (this.dirty.some((d) => d.collection === collection && d.id === id)) return false;
1702
+ return envelopeBodyForHash(remote) !== envelopeBodyForHash(local);
1703
+ }
1704
+ async applyRemote(collection, id, envelope, opts) {
1219
1705
  if (this.mergeAuthority && !await this.mergeAuthority.verify(collection, id, envelope)) {
1220
1706
  throw new ValidationError(
1221
1707
  `sync: refusing remote envelope for "${collection}/${id}" \u2014 it does not authenticate at the identity and version it claims. The local copy is unchanged.`
1222
1708
  );
1223
1709
  }
1710
+ const isErasure = isTombstoneShape(envelope) || isDeleteMarker(envelope);
1711
+ if (this.admission && !isErasure && opts?.admit !== false) {
1712
+ const verdict = await this.admission.admit(collection, id, envelope);
1713
+ if (!verdict.admitted) {
1714
+ this.#rejected.push(await this.#park(collection, id, envelope, verdict.reason, "sync-apply"));
1715
+ return;
1716
+ }
1717
+ }
1718
+ const prior = await this.local.get(this.vault, collection, id);
1224
1719
  await this.local.put(this.vault, collection, id, envelope);
1720
+ this.#applied.push({
1721
+ collection,
1722
+ id,
1723
+ action: isErasure ? "delete" : "put",
1724
+ ...prior && prior._v < envelope._v ? { replaced: { version: prior._v, ...prior._by !== void 0 ? { by: prior._by } : {} } } : {}
1725
+ });
1225
1726
  this.pullBytes += envelopeBodySize(envelope);
1226
1727
  if (this.pullByteSink !== null) {
1227
1728
  this.pullByteSink.records++;
@@ -1321,6 +1822,7 @@ var SyncEngine = class {
1321
1822
  this.dirty = [...meta.dirty];
1322
1823
  this.lastPush = meta.last_push;
1323
1824
  this.lastPull = meta.last_pull;
1825
+ this.epoch = meta.epoch ?? null;
1324
1826
  }
1325
1827
  this.loaded = true;
1326
1828
  }
@@ -1329,7 +1831,8 @@ var SyncEngine = class {
1329
1831
  _noydb_sync: NOYDB_SYNC_VERSION,
1330
1832
  last_push: this.lastPush,
1331
1833
  last_pull: this.lastPull,
1332
- dirty: this.dirty
1834
+ dirty: this.dirty,
1835
+ ...this.epoch !== null ? { epoch: this.epoch } : {}
1333
1836
  };
1334
1837
  const envelope = buildRecordEnvelope(
1335
1838
  { collection: "_sync", id: "meta", version: 1 },
@@ -1403,4 +1906,4 @@ export {
1403
1906
  SyncEngine,
1404
1907
  SyncTransaction
1405
1908
  };
1406
- //# sourceMappingURL=chunk-JA25QG5M.js.map
1909
+ //# sourceMappingURL=chunk-N6EWQ5VB.js.map