fedipod-server 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (444) hide show
  1. package/README.md +16 -6
  2. package/dist/claims.d.ts +2 -0
  3. package/dist/claims.js +13 -4
  4. package/dist/door.d.ts +4 -0
  5. package/dist/door.js +31 -0
  6. package/dist/fedipod.d.ts +15 -0
  7. package/dist/fedipod.js +13 -0
  8. package/dist/front.d.ts +3 -0
  9. package/dist/front.js +92 -0
  10. package/dist/handler.d.ts +36 -101
  11. package/dist/handler.js +93 -644
  12. package/dist/handler.jsonld +2 -30
  13. package/dist/identities.d.ts +71 -0
  14. package/dist/identities.js +236 -0
  15. package/dist/mounts.d.ts +43 -0
  16. package/dist/mounts.js +123 -0
  17. package/dist/opt-in.d.ts +46 -0
  18. package/dist/opt-in.js +176 -0
  19. package/dist/private-read.d.ts +4 -0
  20. package/dist/private-read.js +34 -0
  21. package/package.json +12 -20
  22. package/lib/client/c2s.mjs +0 -636
  23. package/lib/client/localapi.mjs +0 -87
  24. package/lib/client/masto/accounts.mjs +0 -310
  25. package/lib/client/masto/body.mjs +0 -156
  26. package/lib/client/masto/index.mjs +0 -206
  27. package/lib/client/masto/instance.mjs +0 -112
  28. package/lib/client/masto/media.mjs +0 -79
  29. package/lib/client/masto/oauth.mjs +0 -656
  30. package/lib/client/masto/render.mjs +0 -518
  31. package/lib/client/masto/statuses.mjs +0 -372
  32. package/lib/client/masto/timelines.mjs +0 -438
  33. package/lib/client/oidc-auth.mjs +0 -62
  34. package/lib/client/streaming.mjs +0 -154
  35. package/lib/client/webpush.mjs +0 -98
  36. package/lib/connections/acctfeed.mjs +0 -257
  37. package/lib/connections/atproto.mjs +0 -309
  38. package/lib/connections/bskyfeed.mjs +0 -243
  39. package/lib/connections/bskygroup.mjs +0 -162
  40. package/lib/connections/fediacct.mjs +0 -253
  41. package/lib/connections/import.mjs +0 -357
  42. package/lib/connections/tagfeed.mjs +0 -212
  43. package/lib/connections/vault.mjs +0 -114
  44. package/lib/core/as2.mjs +0 -206
  45. package/lib/core/contexts/activitystreams.json +0 -379
  46. package/lib/core/contexts/anno.json +0 -126
  47. package/lib/core/contexts/did-v1.json +0 -57
  48. package/lib/core/contexts/fep-5711.json +0 -36
  49. package/lib/core/contexts/gotosocial.json +0 -86
  50. package/lib/core/contexts/identity-v1.json +0 -152
  51. package/lib/core/contexts/index.mjs +0 -63
  52. package/lib/core/contexts/join-lemmy.json +0 -33
  53. package/lib/core/contexts/joinmastodon.json +0 -28
  54. package/lib/core/contexts/litepub-0.1.json +0 -60
  55. package/lib/core/contexts/map.json +0 -19
  56. package/lib/core/contexts/miscellany.json +0 -19
  57. package/lib/core/contexts/quotes.json +0 -18
  58. package/lib/core/contexts/schemaorg.json +0 -8845
  59. package/lib/core/contexts/security-data-integrity-v1.json +0 -78
  60. package/lib/core/contexts/security-data-integrity-v2.json +0 -81
  61. package/lib/core/contexts/security-multikey-v1.json +0 -35
  62. package/lib/core/contexts/security-v1.json +0 -74
  63. package/lib/core/contexts/webfinger.json +0 -10
  64. package/lib/core/deliver.mjs +0 -387
  65. package/lib/core/graphview.mjs +0 -327
  66. package/lib/core/intake/activities.mjs +0 -649
  67. package/lib/core/intake/activity.mjs +0 -318
  68. package/lib/core/intake/channel.mjs +0 -144
  69. package/lib/core/intake/group.mjs +0 -258
  70. package/lib/core/intake/index.mjs +0 -732
  71. package/lib/core/intake/notes.mjs +0 -367
  72. package/lib/core/intake/verify.mjs +0 -142
  73. package/lib/core/keys.mjs +0 -153
  74. package/lib/core/lease.mjs +0 -206
  75. package/lib/core/place.mjs +0 -82
  76. package/lib/core/polls.mjs +0 -105
  77. package/lib/core/profile-page.mjs +0 -104
  78. package/lib/core/proof.mjs +0 -79
  79. package/lib/core/publisher/collections.mjs +0 -272
  80. package/lib/core/publisher/index.mjs +0 -522
  81. package/lib/core/publisher/notes.mjs +0 -519
  82. package/lib/core/publisher/own.mjs +0 -143
  83. package/lib/core/publisher/questions.mjs +0 -241
  84. package/lib/core/publisher/restore.mjs +0 -224
  85. package/lib/core/shapes/activitystreams.ttl +0 -157
  86. package/lib/core/shapes/index.mjs +0 -132
  87. package/lib/core/shapes/shapes-text.mjs +0 -13
  88. package/lib/core/social.mjs +0 -561
  89. package/lib/core/storage.mjs +0 -176
  90. package/lib/core/store.mjs +0 -578
  91. package/lib/core/wire-quotes.mjs +0 -71
  92. package/lib/core/wire.mjs +0 -925
  93. package/lib/device/account.mjs +0 -87
  94. package/lib/device/admin/index.mjs +0 -13
  95. package/lib/device/admin/origins.mjs +0 -35
  96. package/lib/device/admin/routes/connections.mjs +0 -144
  97. package/lib/device/admin/routes/gateway.mjs +0 -199
  98. package/lib/device/admin/routes/lifecycle.mjs +0 -200
  99. package/lib/device/admin/routes/owner.mjs +0 -322
  100. package/lib/device/admin/routes/setup.mjs +0 -428
  101. package/lib/device/admin/routes/social.mjs +0 -188
  102. package/lib/device/admin/server.mjs +0 -95
  103. package/lib/device/admin/static.mjs +0 -247
  104. package/lib/device/admin/surface.mjs +0 -315
  105. package/lib/device/certs.mjs +0 -281
  106. package/lib/device/cli/commands/account.mjs +0 -586
  107. package/lib/device/cli/commands/run.mjs +0 -278
  108. package/lib/device/cli/commands/service.mjs +0 -221
  109. package/lib/device/cli/commands/setup.mjs +0 -489
  110. package/lib/device/cli/commands/state.mjs +0 -561
  111. package/lib/device/cli/context.mjs +0 -288
  112. package/lib/device/export-collections.mjs +0 -161
  113. package/lib/device/gateway-move.mjs +0 -58
  114. package/lib/device/home.mjs +0 -161
  115. package/lib/device/migrate.mjs +0 -115
  116. package/lib/device/ports.mjs +0 -27
  117. package/lib/device/remote.mjs +0 -54
  118. package/lib/device/setup.mjs +0 -496
  119. package/lib/device/update.mjs +0 -96
  120. package/lib/gateway/caches.mjs +0 -36
  121. package/lib/gateway/directory.mjs +0 -90
  122. package/lib/gateway/front-core.mjs +0 -966
  123. package/lib/gateway/gateway-core.mjs +0 -244
  124. package/lib/gateway/headers.mjs +0 -49
  125. package/lib/gateway/httpsig.mjs +0 -159
  126. package/lib/gateway/notices.mjs +0 -84
  127. package/lib/gateway/quiet.mjs +0 -189
  128. package/lib/gateway/relay-extras.mjs +0 -89
  129. package/lib/gateway/token-claims.mjs +0 -16
  130. package/lib/pod/README.md +0 -61
  131. package/lib/pod/actor.mjs +0 -96
  132. package/lib/pod/collection.mjs +0 -77
  133. package/lib/pod/containers.mjs +0 -140
  134. package/lib/pod/discovery.mjs +0 -46
  135. package/lib/pod/featured.mjs +0 -18
  136. package/lib/pod/followers.mjs +0 -24
  137. package/lib/pod/following.mjs +0 -10
  138. package/lib/pod/http.mjs +0 -74
  139. package/lib/pod/inbox.mjs +0 -166
  140. package/lib/pod/links.mjs +0 -35
  141. package/lib/pod/location.mjs +0 -52
  142. package/lib/pod/media.mjs +0 -27
  143. package/lib/pod/notes.mjs +0 -78
  144. package/lib/pod/notifications.mjs +0 -89
  145. package/lib/pod/outbox.mjs +0 -18
  146. package/lib/pod/package.json +0 -19
  147. package/lib/pod/policy.mjs +0 -58
  148. package/lib/pod/private.mjs +0 -19
  149. package/lib/pod/root.mjs +0 -104
  150. package/lib/pod/state.mjs +0 -40
  151. package/lib/pod/transport.mjs +0 -674
  152. package/lib/pod/type-index.mjs +0 -101
  153. package/lib/pod/urls.mjs +0 -91
  154. package/lib/server/embed.mjs +0 -424
  155. package/lib/session/README.md +0 -137
  156. package/lib/session/demo.html +0 -94
  157. package/lib/session/fedi-account.mjs +0 -417
  158. package/lib/session/fedi-login.mjs +0 -123
  159. package/lib/session/oidc-session.mjs +0 -233
  160. package/lib/session/package.json +0 -49
  161. package/lib/shared/guard.mjs +0 -213
  162. package/lib/shared/links.mjs +0 -9
  163. package/lib/shared/safefetch.mjs +0 -235
  164. package/lib/shared/ua.mjs +0 -29
  165. package/phanpy/dist/404.html +0 -40
  166. package/phanpy/dist/_headers +0 -2
  167. package/phanpy/dist/apple-touch-icon.png +0 -0
  168. package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
  169. package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
  170. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
  171. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
  172. package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
  173. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
  174. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
  175. package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  176. package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
  177. package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
  178. package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
  179. package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
  180. package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
  181. package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
  182. package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
  183. package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
  184. package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
  185. package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
  186. package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  187. package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  188. package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  189. package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  190. package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
  191. package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
  192. package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
  193. package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
  194. package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
  195. package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
  196. package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
  197. package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
  198. package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
  199. package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
  200. package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
  201. package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
  202. package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
  203. package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
  204. package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
  205. package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
  206. package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
  207. package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
  208. package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
  209. package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
  210. package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
  211. package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
  212. package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
  213. package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
  214. package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
  215. package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
  216. package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
  217. package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
  218. package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
  219. package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
  220. package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
  221. package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
  222. package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
  223. package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
  224. package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
  225. package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
  226. package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
  227. package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
  228. package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
  229. package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
  230. package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
  231. package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
  232. package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
  233. package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
  234. package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
  235. package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
  236. package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
  237. package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
  238. package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
  239. package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
  240. package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
  241. package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
  242. package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
  243. package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
  244. package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
  245. package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
  246. package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
  247. package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
  248. package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
  249. package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
  250. package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
  251. package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
  252. package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
  253. package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
  254. package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
  255. package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
  256. package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
  257. package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
  258. package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
  259. package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
  260. package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
  261. package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
  262. package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
  263. package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
  264. package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
  265. package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
  266. package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
  267. package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
  268. package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
  269. package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
  270. package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
  271. package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
  272. package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
  273. package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
  274. package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
  275. package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
  276. package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
  277. package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
  278. package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
  279. package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
  280. package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
  281. package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
  282. package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
  283. package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
  284. package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
  285. package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
  286. package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
  287. package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
  288. package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
  289. package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
  290. package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
  291. package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
  292. package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
  293. package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
  294. package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
  295. package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
  296. package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
  297. package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
  298. package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
  299. package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
  300. package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
  301. package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
  302. package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
  303. package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
  304. package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
  305. package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
  306. package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
  307. package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
  308. package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
  309. package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
  310. package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
  311. package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
  312. package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
  313. package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
  314. package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
  315. package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
  316. package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
  317. package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
  318. package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
  319. package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
  320. package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
  321. package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
  322. package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
  323. package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
  324. package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
  325. package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
  326. package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
  327. package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
  328. package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
  329. package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
  330. package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
  331. package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
  332. package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
  333. package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
  334. package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
  335. package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
  336. package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
  337. package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
  338. package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
  339. package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
  340. package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
  341. package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
  342. package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
  343. package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
  344. package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  345. package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  346. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
  347. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
  348. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
  349. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
  350. package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
  351. package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
  352. package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
  353. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
  354. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
  355. package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
  356. package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
  357. package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
  358. package/phanpy/dist/compose/index.html +0 -20
  359. package/phanpy/dist/favicon.ico +0 -0
  360. package/phanpy/dist/index.html +0 -73
  361. package/phanpy/dist/logo-192.png +0 -0
  362. package/phanpy/dist/logo-512.png +0 -0
  363. package/phanpy/dist/logo-badge-72.png +0 -0
  364. package/phanpy/dist/logo-maskable-512.png +0 -0
  365. package/phanpy/dist/logo-monochrome-512.png +0 -0
  366. package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
  367. package/phanpy/dist/manifest.webmanifest +0 -1
  368. package/phanpy/dist/og-image-2.jpg +0 -0
  369. package/phanpy/dist/og-image.png +0 -0
  370. package/phanpy/dist/robots.txt +0 -2
  371. package/phanpy/dist/sw.js +0 -2
  372. package/phanpy/dist/sw.js.map +0 -1
  373. package/phanpy/dist/version.json +0 -1
  374. package/run-agent.mjs +0 -899
  375. package/vendor/gate.cjs +0 -138
  376. package/vendor/idp-grant.cjs +0 -394
  377. package/vendor/jfetch.cjs +0 -16
  378. package/web/admin/actors.js +0 -147
  379. package/web/admin/bar.css +0 -126
  380. package/web/admin/bar.js +0 -99
  381. package/web/admin/client/client.js +0 -66
  382. package/web/admin/client/index.html +0 -69
  383. package/web/admin/client-pick.js +0 -79
  384. package/web/admin/common.js +0 -23
  385. package/web/admin/connections.js +0 -112
  386. package/web/admin/gateway.js +0 -133
  387. package/web/admin/group.js +0 -258
  388. package/web/admin/index.html +0 -612
  389. package/web/admin/notices-bar.js +0 -90
  390. package/web/admin/oauth-signin.mjs +0 -37
  391. package/web/admin/record.js +0 -393
  392. package/web/admin/setup/index.html +0 -257
  393. package/web/admin/setup/setup.js +0 -339
  394. package/web/admin/tokens.css +0 -68
  395. package/web/admin/upkeep.js +0 -177
  396. package/web/admin/window.css +0 -55
  397. package/web/admin/window.js +0 -136
  398. package/web/app/README.md +0 -81
  399. package/web/app/admin-facade.mjs +0 -588
  400. package/web/app/agent.mjs +0 -672
  401. package/web/app/atproto-browser.mjs +0 -86
  402. package/web/app/authorize_interaction.html +0 -56
  403. package/web/app/authorize_interaction.js +0 -69
  404. package/web/app/boot.mjs +0 -616
  405. package/web/app/deliver-relay.mjs +0 -129
  406. package/web/app/dist/boot.js +0 -34562
  407. package/web/app/dist/boot.js.map +0 -7
  408. package/web/app/dist/sw.js +0 -73456
  409. package/web/app/dist/sw.js.map +0 -7
  410. package/web/app/fediacct-browser.mjs +0 -146
  411. package/web/app/gateway-move.mjs +0 -60
  412. package/web/app/idb-kv.mjs +0 -35
  413. package/web/app/index.html +0 -258
  414. package/web/app/keys-browser.mjs +0 -71
  415. package/web/app/keystore.mjs +0 -82
  416. package/web/app/oidc-session.mjs +0 -6
  417. package/web/app/pod-auth.mjs +0 -248
  418. package/web/app/pod-remote.mjs +0 -84
  419. package/web/app/shims/_globals.mjs +0 -2
  420. package/web/app/shims/fedify-sig.mjs +0 -77
  421. package/web/app/shims/node-crypto.mjs +0 -113
  422. package/web/app/shims/node-fs.mjs +0 -17
  423. package/web/app/shims/node-path.mjs +0 -19
  424. package/web/app/shims/node-url.mjs +0 -7
  425. package/web/app/shims/prelude.js +0 -58
  426. package/web/app/shims/safefetch.mjs +0 -58
  427. package/web/app/shims/shapes-text.mjs +0 -8
  428. package/web/app/shims/web-push.mjs +0 -14
  429. package/web/app/signup.mjs +0 -357
  430. package/web/app/sw-src.mjs +0 -220
  431. package/web/app/update.js +0 -70
  432. package/web/front/admin.html +0 -55
  433. package/web/front/admin.js +0 -89
  434. package/web/front/install.sh +0 -69
  435. package/web/front/new-account.html +0 -35
  436. package/web/front/new-account.js +0 -13
  437. package/web/front/notices.html +0 -69
  438. package/web/front/notices.js +0 -107
  439. package/web/front/run.html +0 -64
  440. package/web/front/run.js +0 -221
  441. package/web/front/solid-oidc-client.js +0 -6
  442. package/web/icons/forum.svg +0 -11
  443. package/web/icons/gateway.svg +0 -14
  444. package/web/icons/local.svg +0 -11
@@ -1,241 +0,0 @@
1
- // questions.mjs — polls.
2
- //
3
- // A poll is published as a Question and answered by ordinary replies naming
4
- // an option, so the count is ours to keep and ours to republish. The roster
5
- // of who chose what lives in agent state (polls.VOTES_DOC); the tallies on
6
- // the status row and in the pod document are always derived from it.
7
-
8
- import crypto from 'node:crypto';
9
- import * as wire from '../wire.mjs';
10
- import * as polls from '../polls.mjs';
11
- import * as podNotes from '../../pod/notes.mjs';
12
- import { assertDirectAddressed, replyTarget, rowTags, slugFor } from './notes.mjs';
13
-
14
- // How long a poll gathers votes before its Question is rewritten. Every vote
15
- // changes a number other servers re-read, and rewriting per vote would make a
16
- // busy poll a steady write stream against the pod. A burst costs one rewrite
17
- // and one Update instead.
18
- const POLL_REWRITE_MS = 10_000;
19
-
20
- /**
21
- * Publish a poll. `options` is a list of choice titles, `multiple` lets a
22
- * voter pick more than one, and `expiresAt` is when voting stops.
23
- */
24
- export async function publishQuestion(publisher, content, { options = [], multiple = false, expiresAt = null,
25
- inReplyTo = undefined, visibility = 'public', spoilerText = null, sensitive = false, slug: wanted = null } = {}) {
26
- const { urls } = publisher;
27
- const priv = visibility === 'private' || visibility === 'direct';
28
- if (priv) {
29
- const ready = await publisher.privateReady();
30
- if (ready !== true) throw new Error(ready);
31
- }
32
- const titles = [].concat(options).map(o => String(o ?? '').trim()).filter(Boolean);
33
- if (titles.length < 2) throw new Error('a poll needs at least two options');
34
- // Options are matched BY NAME when a vote arrives — that is the whole of
35
- // the convention — so two options reading the same are one option that
36
- // cannot be told apart.
37
- if (new Set(titles).size !== titles.length) throw new Error('a poll’s options must differ from one another');
38
- // Bounded here as well as at the client API, because the outbox is a
39
- // second way in and an unbounded poll is a document a stranger sizes.
40
- if (titles.length > polls.MAX_OPTIONS) throw new Error(`a poll takes at most ${polls.MAX_OPTIONS} options`);
41
- if (titles.some(t => t.length > polls.MAX_OPTION_CHARS)) {
42
- throw new Error(`a poll option is at most ${polls.MAX_OPTION_CHARS} characters`);
43
- }
44
- if (expiresAt) {
45
- const ends = Date.parse(expiresAt);
46
- if (!Number.isFinite(ends)) throw new Error('the closing time is not a date');
47
- const seconds = (ends - Date.now()) / 1000;
48
- if (seconds < polls.MIN_SECONDS || seconds > polls.MAX_SECONDS) {
49
- throw new Error(`a poll runs between ${polls.MIN_SECONDS} and ${polls.MAX_SECONDS} seconds`);
50
- }
51
- }
52
-
53
- const published = new Date().toISOString();
54
- // The name the client asked for, when it is plain — the outbox door has
55
- // already told the client the address it names.
56
- const slug = await slugFor(publisher, priv ? urls.privateNotes : urls.notes, wanted, published);
57
- const mentions = await publisher._mentionsFor(content, inReplyTo);
58
- if (visibility === 'direct') assertDirectAddressed(content, mentions);
59
- const reply = await replyTarget(publisher, inReplyTo, mentions, visibility);
60
- const poll = {
61
- multiple: !!multiple,
62
- expiresAt: expiresAt || null,
63
- closed: null,
64
- options: titles.map(title => ({ title, votes: 0 })),
65
- votersCount: 0,
66
- // Resolved once, here: a tally rewrite must not cost a webfinger lookup
67
- // per vote for people the poll named.
68
- mentionInboxes: [...new Set([...mentions.map(m => m.inbox), reply?.inbox].filter(Boolean))],
69
- };
70
- const question = wire.questionDoc({
71
- urls, slug, content, published, inReplyTo, attachments: [], mentions,
72
- visibility, summary: spoilerText, sensitive, also: reply ? [reply.actor] : [],
73
- container: priv ? urls.privateNotes : urls.notes,
74
- options: poll.options, multiple: poll.multiple, endTime: poll.expiresAt, votersCount: 0,
75
- });
76
-
77
- await podNotes.write(publisher.remote, question.id, question);
78
- // Empty, but present: a dangling `replies` that 404s is worse than none.
79
- await podNotes.writeEmptyReplies(publisher.remote, wire.repliesId(question.id),
80
- wire.collection(wire.repliesId(question.id), []));
81
- if (!priv) await publisher.recordOutbox(question.id);
82
- publisher.store.addStatus({
83
- noteId: question.id, actor: urls.actor, content: question.content, published,
84
- kind: 'post', slug, text: content, visibility, poll, inReplyTo,
85
- ...(reply ? { replyActor: reply.actor } : {}),
86
- ...(spoilerText ? { spoiler: spoilerText } : {}),
87
- ...(question.sensitive ? { sensitive: true } : {}),
88
- ...rowTags(question),
89
- });
90
- if (await publisher.store.commit?.() === false) publisher.log(`poll published but its timeline row was refused: ${question.id}`);
91
-
92
- const create = publisher._pollActivity('Create', question, wire.createActivityId(question.id));
93
- await podNotes.writeCreate(publisher.remote, create.id, create);
94
- const contacts = publisher.store.getContacts();
95
- const inboxes = [...new Set([
96
- ...(visibility === 'direct' ? [] : contacts.followers.map(f => f.sharedInbox || f.inbox)),
97
- ...poll.mentionInboxes,
98
- ].filter(Boolean))];
99
- await publisher.deliverer.deliverToAll(inboxes, create);
100
- publisher.log(`poll published: ${question.id} (${titles.length} options) → ${inboxes.length} inbox(es)`);
101
- return question;
102
- }
103
-
104
- // The Create or Update carrying a Question. The context is hoisted onto the
105
- // activity and the embedded object keeps none: a nested @context is legal
106
- // JSON-LD but not every server reads one, and votersCount is declared there.
107
- export function pollActivity(publisher, type, question, id) {
108
- const { '@context': ctx, ...object } = question;
109
- return {
110
- '@context': ctx, id, type,
111
- actor: publisher.urls.actor,
112
- published: question.published,
113
- to: question.to, cc: question.cc,
114
- object,
115
- };
116
- }
117
-
118
- /**
119
- * One vote on one of OUR polls, named by the option's title. Returns true
120
- * when it counted — a second answer to a single-choice poll, an option we do
121
- * not offer, or a poll that has closed all count for nothing.
122
- */
123
- export async function recordVote(publisher, questionId, actor, optionName) {
124
- const s = publisher.store.getStatuses().find(x => x.noteId === questionId);
125
- if (!s?.poll || s.kind !== 'post' || s.actor !== publisher.urls.actor) return false;
126
- if (polls.pollClosed(s.poll)) return false;
127
- const index = polls.optionIndex(s.poll, optionName);
128
- if (index < 0) return false;
129
- const all = publisher.store.read(polls.VOTES_DOC, {});
130
- const { roster, changed } = polls.addVote(all[questionId] || {}, polls.voterKey(actor), index,
131
- { multiple: !!s.poll.multiple });
132
- if (!changed) return false;
133
- publisher.store.write(polls.VOTES_DOC, { ...all, [questionId]: roster });
134
- publisher.store.updateStatus(questionId, { poll: polls.withTally(s.poll, roster) });
135
- publisher._pollDirty(questionId);
136
- return true;
137
- }
138
-
139
- // Open the rewrite window for a poll whose count moved. Already open is
140
- // already enough: the whole point is that a burst costs one rewrite.
141
- export function pollDirty(publisher, questionId) {
142
- if (publisher.pollTimers.has(questionId)) return;
143
- const t = setTimeout(() => {
144
- publisher.pollTimers.delete(questionId);
145
- publisher.republishPoll(questionId).catch(e => publisher.log(`poll rewrite: ${e.message}`));
146
- }, POLL_REWRITE_MS);
147
- t.unref?.();
148
- publisher.pollTimers.set(questionId, t);
149
- }
150
-
151
- /**
152
- * Write the poll's current count back to the pod and tell everyone who has
153
- * it. `closing` stamps it shut, which is a one-way door.
154
- */
155
- export async function republishPoll(publisher, questionId, { closing = null } = {}) {
156
- const s = publisher.store.getStatuses().find(x => x.noteId === questionId);
157
- if (!s?.poll) return null;
158
- const { urls } = publisher;
159
- const roster = publisher.store.read(polls.VOTES_DOC, {})[questionId] || {};
160
- // A shut poll whose roster has been retired keeps the counts on its row:
161
- // deriving them from an empty roster would publish a poll nobody voted in.
162
- const shut = closing || s.poll.closed;
163
- const counted = shut && !Object.keys(roster).length ? s.poll : polls.withTally(s.poll, roster);
164
- const poll = { ...counted, ...(closing ? { closed: closing } : {}) };
165
- const container = String(s.noteId).startsWith(urls.privateNotes) ? urls.privateNotes : urls.notes;
166
- const slug = s.slug || String(s.noteId).slice(container.length);
167
- const mentions = (s.mentions || []).map(m => ({
168
- handle: String(m.name || '').replace(/^@/, ''), actor: m.href, page: null, inbox: null,
169
- }));
170
- const question = wire.questionDoc({
171
- urls, slug, content: s.text ?? '', published: s.published, inReplyTo: s.inReplyTo,
172
- attachments: [], mentions, also: s.replyActor ? [s.replyActor] : [],
173
- visibility: s.visibility || 'public', summary: s.spoiler || null, sensitive: !!s.sensitive, container,
174
- options: poll.options, multiple: !!poll.multiple, endTime: poll.expiresAt,
175
- closed: poll.closed, votersCount: poll.votersCount || 0,
176
- });
177
- await podNotes.write(publisher.remote, question.id, question);
178
- // The Create is overwritten too, so a group's Announce resolves to the
179
- // current count rather than to the one the poll opened with.
180
- await podNotes.writeCreate(publisher.remote, wire.createActivityId(question.id),
181
- publisher._pollActivity('Create', question, wire.createActivityId(question.id)));
182
- publisher.store.updateStatus(questionId, { poll });
183
-
184
- // Not `updated`: a changed count is not an edit, and stamping one would
185
- // have every client show the poll as edited each time somebody voted.
186
- //
187
- // The Update is named after the STATE it carries rather than the moment it
188
- // was sent. A clock only tells two rewrites apart when they fall in
189
- // different milliseconds, and a receiving server that has seen an activity
190
- // id drops the next one wearing it — which would quietly freeze the count.
191
- // Naming the state means an id changes exactly when there is something new
192
- // to say, and two sends of the same numbers are the duplicate they look
193
- // like.
194
- const stamp = crypto.createHash('sha256').update(JSON.stringify([
195
- poll.options.map(o => o.votes || 0), poll.votersCount || 0, poll.closed || '',
196
- ])).digest('hex').slice(0, 12);
197
- const update = publisher._pollActivity('Update', question, `${question.id}#poll-${stamp}`);
198
- const contacts = publisher.store.getContacts();
199
- const inboxes = [...new Set([
200
- ...(s.visibility === 'direct' ? [] : contacts.followers.map(f => f.sharedInbox || f.inbox)),
201
- ...(s.poll.mentionInboxes || []),
202
- ].filter(Boolean))];
203
- await publisher.deliverer.deliverToAll(inboxes, update);
204
- publisher.log(`poll ${closing ? 'closed' : 'count published'}: ${question.id} → ${inboxes.length} inbox(es)`);
205
- return poll;
206
- }
207
-
208
- /**
209
- * Shut any poll whose time is up. Called from the agent's sweep. Closing is
210
- * recorded BEFORE the republish, so a failed republish cannot leave a poll
211
- * open and collecting votes it has already refused.
212
- */
213
- export async function closeDuePolls(publisher, now = Date.now()) {
214
- const due = publisher.store.getStatuses().filter(s => s.kind === 'post' && s.poll
215
- && !s.poll.closed && s.poll.expiresAt && Date.parse(s.poll.expiresAt) <= now);
216
- for (const s of due) {
217
- const closed = new Date(Math.min(now, Date.parse(s.poll.expiresAt) || now)).toISOString();
218
- clearTimeout(publisher.pollTimers.get(s.noteId));
219
- publisher.pollTimers.delete(s.noteId);
220
- publisher.store.updateStatus(s.noteId, { poll: { ...s.poll, closed } });
221
- const done = await publisher.republishPoll(s.noteId, { closing: closed })
222
- .catch(e => { publisher.log(`poll close ${s.noteId}: ${e.message}`); return null; });
223
- // The roster only ever answered one question — has this person already
224
- // voted — and a shut poll has stopped asking it. Dropping it keeps a
225
- // document we serialize whole from carrying every poll's voters forever.
226
- if (done) {
227
- const all = publisher.store.read(polls.VOTES_DOC, {});
228
- if (all[s.noteId]) {
229
- delete all[s.noteId];
230
- publisher.store.write(polls.VOTES_DOC, all);
231
- }
232
- }
233
- }
234
- return due.length;
235
- }
236
-
237
- /** Stop the pending rewrite windows. Called at shutdown. */
238
- export function stopPolls(publisher) {
239
- for (const t of publisher.pollTimers.values()) clearTimeout(t);
240
- publisher.pollTimers.clear();
241
- }
@@ -1,224 +0,0 @@
1
- // restore.mjs — a machine that is behind the pod catching up: followers and
2
- // outbox entries the pod carries and this machine never heard of, and the
3
- // actor's own posts rebuilt from the published outbox.
4
-
5
- import * as wire from '../wire.mjs';
6
- import * as podNotes from '../../pod/notes.mjs';
7
- import { readLenient } from '../as2.mjs';
8
- import { rowContent } from './notes.mjs';
9
-
10
- const ACCEPT_AP = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
11
- const REBUILD_MAX_PER_RUN = 200;
12
-
13
- // Anyone the pod says follows us that we have no record of, and never
14
- // deliberately removed. In the steady state there is nobody: the pod's list is
15
- // written from this one. They appear when the local half is BEHIND the pod —
16
- // a restored backup, a home copied off a dead machine — and republishing
17
- // blindly would delete them from the wire and, worse, stop delivering to them.
18
- //
19
- // Removals are what makes this safe to do unconditionally: `dropFollower`
20
- // records every unfollow, ejection and account deletion, so a returning name
21
- // is either genuinely still a follower or genuinely a mistake we made.
22
- export async function reconcileFollowers(publisher, contacts) {
23
- let published = [];
24
- try {
25
- published = (await publisher.readPublishedFollowers()) || []; // walks pages
26
- } catch { return 0; } // no list to reconcile against
27
-
28
- const known = new Set(contacts.followers.map(f => f.actor));
29
- const removed = new Set((contacts.removedFollowers || []).map(r => r.actor));
30
- const missing = published.filter(a => typeof a === 'string' && !known.has(a) && !removed.has(a));
31
- if (!missing.length) return 0;
32
-
33
- // An inbox is what delivery needs, and only the actor document has it — so
34
- // recovering a follower costs one fetch each. Capped: a list this wrong is
35
- // a restore, and the rest catch up on the next publish.
36
- let recovered = 0;
37
- for (const actor of missing.slice(0, 200)) {
38
- try {
39
- const res = await publisher.deliverer.signedFetch(actor, { headers: { accept: ACCEPT_AP } });
40
- if (!res.ok) continue;
41
- // The graph, not the compacted copy: `inbox` is `ldp:inbox` rather than
42
- // an `as:` term, and what is wanted here is the statement, not a key.
43
- const { doc, view, degraded } = await readLenient(await res.json());
44
- const actorDoc = view ?? doc;
45
- if (degraded) publisher.log?.(`actor ${actor} grounded to read: ${degraded}`);
46
- if (!actorDoc?.inbox) continue;
47
- contacts.followers.push({
48
- actor, inbox: actorDoc.inbox, sharedInbox: actorDoc.endpoints?.sharedInbox || null, recovered: true,
49
- // Said explicitly, because onUndo reads it: the pod publishes WHO
50
- // follows, never the id of the Follow that did it, so a recovered
51
- // record has nothing an Undo can be matched against and must not be
52
- // evictable by one naming anything at all.
53
- followId: null,
54
- });
55
- recovered++;
56
- } catch { /* unreachable now; it will be there next time */ }
57
- }
58
- if (recovered) {
59
- publisher.store.setContacts(contacts);
60
- publisher.log(`reconciled ${recovered} follower(s) the pod knew about and this machine did not `
61
- + '— a restored or copied state was behind');
62
- }
63
- return recovered;
64
- }
65
-
66
- // The same argument as reconcileFollowers, for the other published list. It
67
- // matters more: the outbox is the INDEX a statuses rebuild reads, so a
68
- // republish from a restored-and-behind machine would destroy the record of
69
- // everything this actor ever posted — and destroy it before anyone noticed
70
- // there was anything to recover.
71
- //
72
- // Safe for the same reason: `unrecordOutbox` leaves a tombstone, so an entry
73
- // the pod still carries is one this machine has not heard of, never one it
74
- // deliberately took back.
75
- export async function reconcileOutbox(publisher, outbox) {
76
- let published = [];
77
- try {
78
- published = await publisher.readPublishedOutbox() || [];
79
- } catch { return 0; }
80
- if (!Array.isArray(published) || !published.length) return 0;
81
-
82
- const idOf = (i) => (typeof i === 'string' ? i : i?.id || null);
83
- const known = new Set(outbox.map(idOf).filter(Boolean));
84
- const removed = new Set((publisher.store.read('outbox-removed.json', [])).map(r => r.id));
85
- const missing = published.filter((i) => {
86
- const id = idOf(i);
87
- return id && !known.has(id) && !removed.has(id);
88
- });
89
- if (!missing.length) return 0;
90
- // Newest first, like recordOutbox leaves it; the pod's copy is already in
91
- // that order, so appending the tail is enough to keep both sorted.
92
- outbox.push(...missing);
93
- publisher.store.write('outbox.json', outbox);
94
- publisher.log(`reconciled ${missing.length} outbox entr(ies) the pod carried and this machine did not`);
95
- return missing.length;
96
- }
97
-
98
- // Recover this actor's own posts from the pod's public face. The private half
99
- // lives on this machine now, so a restored backup or a replaced machine loses
100
- // statuses.json while the pod still serves every note. Followers already come
101
- // back; this is the other half of the same gap.
102
- //
103
- // The INDEX is ap/outbox, not the ap/notes/ listing, and that difference is
104
- // the safety argument. Deleting a post rewrites the outbox in one PUT, so an
105
- // entry still there is a post that still stands. The note DOCUMENT can outlive
106
- // its own deletion — deleteNote's `remote.delete` is a request that can fail —
107
- // so walking the container can bring back something its author took down.
108
- // `fromNotes` is for when you would rather have that than lose the post; it is
109
- // not the default, and it says so where it is offered.
110
- //
111
- // MERGE ONLY. A status this machine already holds is left exactly as it is:
112
- // it carries local facts — favourited, reblogged, the activities an Undo has
113
- // to name — that the pod knows nothing about. That is also what makes the
114
- // failure modes harmless: a listing that fails returns nothing, and nothing
115
- // is what an empty listing recovers.
116
- export async function rebuildStatuses(publisher, { fromNotes = false } = {}) {
117
- const { urls } = publisher;
118
- const ids = new Set();
119
- const boosts = [];
120
- let indexed = false;
121
-
122
- const published = await publisher.readPublishedOutbox().catch(() => null);
123
- if (published) {
124
- indexed = true;
125
- for (const item of published) {
126
- if (typeof item === 'string') { if (item.startsWith(urls.notes)) ids.add(item); }
127
- else if (item?.type === 'Announce') boosts.push(item);
128
- }
129
- }
130
- if (fromNotes) {
131
- // Three documents are published per post — the note, `-create` and
132
- // `-replies` — plus a `.keep`. Which is which is settled by reading the
133
- // document below, not by its name: a slug is a date and eight hex
134
- // characters and says nothing about what it holds.
135
- for (const child of await podNotes.list(publisher.remote, urls).catch(() => [])) {
136
- ids.add(child.url);
137
- }
138
- indexed = true;
139
- }
140
- if (!indexed) return { indexed: 0, recovered: 0, reblogs: 0, landed: false, why: 'the pod would not answer for its outbox' };
141
-
142
- const statuses = publisher.store.getStatuses();
143
- const have = new Set(statuses.map(s => s.noteId));
144
- const removed = new Set(publisher.store.read('outbox-removed.json', []).map(r => r.id));
145
- const recovered = [];
146
- // Capped per run: a long-lived actor's rebuild is otherwise one pod request
147
- // per post it has ever made, in one burst. What is left is picked up by
148
- // running it again — the merge is idempotent, so that is safe to repeat.
149
- let budget = REBUILD_MAX_PER_RUN;
150
- for (const id of ids) {
151
- if (budget <= 0) { publisher.log(`rebuild: stopping at ${REBUILD_MAX_PER_RUN} this run — run it again for the rest`); break; }
152
- if (have.has(id) || removed.has(id)) continue;
153
- budget--;
154
- const note = await podNotes.read(publisher.remote, id).catch(() => null);
155
- // Any object of ours: a Note, a poll, or whatever a client posted.
156
- if (!note?.type || note.id !== id || note.attributedTo !== urls.actor) continue;
157
- const attachments = wire.attachmentsOf(note);
158
- const mentions = (Array.isArray(note.tag) ? note.tag : [])
159
- .filter(t => t?.type === 'Mention' && t.href)
160
- .map(t => ({ href: t.href, name: t.name }));
161
- recovered.push({
162
- noteId: note.id, actor: urls.actor, content: rowContent(note),
163
- published: note.published || null,
164
- ...(note.inReplyTo ? { inReplyTo: note.inReplyTo } : {}),
165
- kind: 'post', slug: note.id.slice(urls.notes.length),
166
- ...(attachments.length ? { attachments } : {}),
167
- ...(mentions.length ? { mentions } : {}),
168
- recovered: true,
169
- });
170
- }
171
-
172
- // A boost is only recoverable for a post we can name — the Announce carries
173
- // the activity a later Undo needs, but not the boosted post's text, which
174
- // belongs to whoever wrote it.
175
- const merged = [...statuses, ...recovered];
176
- let reblogs = 0;
177
- for (const act of boosts) {
178
- const object = typeof act.object === 'string' ? act.object : act.object?.id;
179
- const s = object && merged.find(x => x.noteId === object);
180
- if (!s || s.reblogged) continue;
181
- s.reblogged = true;
182
- s.announceActivity = act;
183
- reblogs++;
184
- }
185
- if (!recovered.length && !reblogs) return { indexed: ids.size, recovered: 0, reblogs: 0, landed: true };
186
-
187
- // Written whole and sorted: addStatus unshifts and fires the streaming
188
- // event, so a loop of it would arrive backwards and push every recovered
189
- // post at connected clients as new.
190
- merged.sort((a, b) => String(b.published || '').localeCompare(String(a.published || '')));
191
- const kept = merged.slice(0, 1000);
192
- publisher.store.write('statuses.json', kept);
193
- const landed = await publisher.store.commit();
194
-
195
- publisher.log(`rebuilt ${recovered.length} post(s) and ${reblogs} boost(s) from the pod`
196
- + `${landed ? '' : ' — THE STATE WRITE DID NOT LAND'}`);
197
- return {
198
- indexed: ids.size, recovered: recovered.length, reblogs, landed,
199
- dropped: Math.max(0, merged.length - kept.length),
200
- };
201
- }
202
-
203
- // Own posts the outbox records and the statuses index does not.
204
- //
205
- // The index row is the last thing a post writes, and it goes by a debounced
206
- // PUT: a worker killed or a write refused after the post was answered leaves a
207
- // post that stands on the pod and reached followers but never shows on its
208
- // author's own timeline. Run when an agent becomes active. Cheap when nothing
209
- // is wrong — two cached documents compared, no request — and the capped,
210
- // merge-only rebuild when something is.
211
- export async function healStatuses(publisher) {
212
- const { urls, store } = publisher;
213
- const own = store.read('outbox.json', [])
214
- .map(i => (typeof i === 'string' ? i : null))
215
- .filter(id => id && id.startsWith(urls.notes));
216
- if (!own.length) return { missing: 0, recovered: 0 };
217
- const have = new Set(store.getStatuses().map(s => s.noteId));
218
- const removed = new Set(store.read('outbox-removed.json', []).map(r => r.id));
219
- const missing = own.filter(id => !have.has(id) && !removed.has(id));
220
- if (!missing.length) return { missing: 0, recovered: 0 };
221
- const r = await rebuildStatuses(publisher);
222
- publisher.log(`timeline index healed: ${missing.length} own post(s) were missing, ${r.recovered} recovered`);
223
- return { missing: missing.length, recovered: r.recovered };
224
- }
@@ -1,157 +0,0 @@
1
- # activitystreams.ttl — shapes for the ActivityStreams documents an identity
2
- # reads from strangers.
3
- #
4
- # Shapes are identified by fragments of this document, so nothing here claims a
5
- # namespace of its own.
6
- #
7
- # These are deliberately thin. They state what the ActivityPub specification
8
- # requires and nothing more, because every fediverse implementation writes
9
- # slightly different documents and a constraint beyond the spec is a constraint
10
- # that fires on somebody's real traffic. Nothing here rejects anything: a
11
- # failure is recorded and the activity is handled. A shape earns its way to
12
- # being enforced by not firing.
13
-
14
- @prefix sh: <http://www.w3.org/ns/shacl#> .
15
- @prefix as: <https://www.w3.org/ns/activitystreams#> .
16
- @prefix ldp: <http://www.w3.org/ns/ldp#> .
17
- @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
18
-
19
- # --- the actor document ------------------------------------------------------
20
-
21
- <#Actor>
22
- a sh:NodeShape ;
23
- sh:targetClass as:Person, as:Service, as:Group, as:Organization, as:Application ;
24
- sh:name "Actor" ;
25
- sh:description "Someone or something that acts: it has an address activities are sent to, and a name it is addressed by." ;
26
- sh:property [
27
- sh:path ldp:inbox ;
28
- sh:name "inbox" ;
29
- sh:description "The collection an actor receives activities in." ;
30
- sh:nodeKind sh:IRI ;
31
- sh:minCount 1 ;
32
- sh:maxCount 1 ;
33
- ] ;
34
- sh:property [
35
- sh:path as:preferredUsername ;
36
- sh:name "preferred username" ;
37
- sh:description "The short name an actor is addressed by, the local part of its @handle@host address." ;
38
- sh:datatype xsd:string ;
39
- sh:maxCount 1 ;
40
- ] .
41
-
42
- # --- the activities an identity acts on --------------------------------------
43
- #
44
- # Each states the two things ActivityPub requires of an activity: who performed
45
- # it, and what it is about.
46
-
47
- <#Create>
48
- a sh:NodeShape ;
49
- sh:targetClass as:Create ;
50
- sh:name "Create" ;
51
- sh:description "The bringing into existence of the object it names." ;
52
- sh:property [
53
- sh:path as:actor ;
54
- sh:name "actor" ;
55
- sh:description "The one that performed the activity." ;
56
- sh:nodeKind sh:BlankNodeOrIRI ;
57
- sh:minCount 1 ;
58
- sh:maxCount 1 ;
59
- ] ;
60
- sh:property [
61
- sh:path as:object ;
62
- sh:name "object" ;
63
- sh:description "The thing the activity is about." ;
64
- sh:minCount 1 ;
65
- sh:maxCount 1 ;
66
- ] .
67
-
68
- <#Follow>
69
- a sh:NodeShape ;
70
- sh:targetClass as:Follow ;
71
- sh:name "Follow" ;
72
- sh:description "A request to receive what the object it names publishes." ;
73
- sh:property [
74
- sh:path as:actor ;
75
- sh:name "actor" ;
76
- sh:description "The one that performed the activity." ;
77
- sh:nodeKind sh:BlankNodeOrIRI ;
78
- sh:minCount 1 ;
79
- sh:maxCount 1 ;
80
- ] ;
81
- sh:property [
82
- sh:path as:object ;
83
- sh:name "object" ;
84
- sh:description "The thing the activity is about." ;
85
- sh:minCount 1 ;
86
- sh:maxCount 1 ;
87
- ] .
88
-
89
- <#Accept>
90
- a sh:NodeShape ;
91
- sh:targetClass as:Accept ;
92
- sh:name "Accept" ;
93
- sh:description "Assent to the activity it names." ;
94
- sh:property [
95
- sh:path as:actor ;
96
- sh:name "actor" ;
97
- sh:description "The one that performed the activity." ;
98
- sh:nodeKind sh:BlankNodeOrIRI ;
99
- sh:minCount 1 ;
100
- sh:maxCount 1 ;
101
- ] ;
102
- sh:property [
103
- sh:path as:object ;
104
- sh:name "object" ;
105
- sh:description "The thing the activity is about." ;
106
- sh:minCount 1 ;
107
- sh:maxCount 1 ;
108
- ] .
109
-
110
- <#Undo>
111
- a sh:NodeShape ;
112
- sh:targetClass as:Undo ;
113
- sh:name "Undo" ;
114
- sh:description "The retraction of the activity it names, by the one that performed it." ;
115
- sh:property [
116
- sh:path as:actor ;
117
- sh:name "actor" ;
118
- sh:description "The one that performed the activity." ;
119
- sh:nodeKind sh:BlankNodeOrIRI ;
120
- sh:minCount 1 ;
121
- sh:maxCount 1 ;
122
- ] ;
123
- sh:property [
124
- sh:path as:object ;
125
- sh:name "object" ;
126
- sh:description "The thing the activity is about." ;
127
- sh:minCount 1 ;
128
- sh:maxCount 1 ;
129
- ] .
130
-
131
- <#Update>
132
- a sh:NodeShape ;
133
- sh:targetClass as:Update ;
134
- sh:name "Update" ;
135
- sh:description "A change to the object it names, carrying the parts that are now different." ;
136
- sh:property [
137
- sh:path as:actor ;
138
- sh:name "actor" ;
139
- sh:description "The one that performed the activity." ;
140
- sh:nodeKind sh:BlankNodeOrIRI ;
141
- sh:minCount 1 ;
142
- sh:maxCount 1 ;
143
- ] ;
144
- sh:property [
145
- sh:path as:object ;
146
- sh:name "object" ;
147
- sh:description "The thing the activity is about." ;
148
- sh:minCount 1 ;
149
- sh:maxCount 1 ;
150
- ] ;
151
- sh:property [
152
- sh:path ( as:object as:closed ) ;
153
- sh:name "closed" ;
154
- sh:description "The time the object stopped taking anything more." ;
155
- sh:datatype xsd:dateTime ;
156
- sh:maxCount 1 ;
157
- ] .