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,636 +0,0 @@
1
- // c2s.mjs — ActivityPub client-to-server (§6): the spec's own write API,
2
- // beside the Mastodon facade. A client POSTs an activity — or a bare object,
3
- // which is wrapped in a Create (§6.2.1) — to this actor's outbox; the agent
4
- // assigns ids, applies the side-effects and delivers. Every verb lands on the
5
- // SAME helper the facade and admin surfaces use — this module re-implements
6
- // no persistence and no delivery, so one write path stays one.
7
- //
8
- // GETs on the actor and outbox are redirects to the pod's documents wherever
9
- // those are reachable, which is the Server. Standalone they are not: the actor
10
- // published there names no address a local client could write to, and on a
11
- // fronted account the published outbox address is the Gateway's write door,
12
- // which does not answer a read. So on that build both are served here, to the
13
- // client already talking to this agent.
14
- //
15
- // `dispatch` takes an activity and answers with { status, body, headers }; it
16
- // touches no request or response. The HTTP handler below is one caller. The
17
- // other is the inbox drain, handing over an activity the Gateway took at the
18
- // outbox door on the owner's behalf and stamped as theirs.
19
- //
20
- // The inbox is the exception, and has to be. Deliveries land in a container on
21
- // the pod which the drain empties as it handles each item, so reading that
22
- // container tells the owner only what has not been dealt with yet. What was
23
- // actually received is whole only in the archive, so §5.2's "the owner can
24
- // read their own inbox" is served from there, by this agent, to the owner
25
- // alone.
26
-
27
- import * as social from '../core/social.mjs';
28
- import * as wire from '../core/wire.mjs';
29
- import { readLenient } from '../core/as2.mjs';
30
- import { safeSlug } from '../core/publisher/notes.mjs';
31
- import * as podNotes from '../pod/notes.mjs';
32
-
33
- const MAX_BODY = 512 * 1024; // same ceiling the inbox drain enforces
34
-
35
- // How many archived items one page of the inbox will read. A page is one
36
- // month, and a month with more than this is served short rather than costing
37
- // the pod an unbounded read; the log says when that happened.
38
- const MAX_INBOX_PAGE = 500;
39
-
40
- // How many recorded activities one page of the locally-served outbox carries.
41
- const MAX_OUTBOX_PAGE = 50;
42
-
43
- // §6 names activities; anything else with a type is an object to wrap.
44
- const ACTIVITY_TYPES = new Set([
45
- 'Create', 'Update', 'Delete', 'Follow', 'Like', 'Announce', 'Undo',
46
- 'Block', 'Add', 'Remove', 'Accept', 'Reject', 'Move',
47
- ]);
48
-
49
- const arr = (v) => (v === undefined || v === null ? [] : Array.isArray(v) ? v : [v]);
50
- const idOf = (v) => (typeof v === 'string' ? v : v?.id || null);
51
-
52
- function readBody(req) {
53
- return new Promise((resolve, reject) => {
54
- let size = 0;
55
- const chunks = [];
56
- req.on('data', (c) => {
57
- size += c.length;
58
- if (size > MAX_BODY) { reject(new Error('body too large')); req.destroy(); return; }
59
- chunks.push(c);
60
- });
61
- req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
62
- req.on('error', reject);
63
- });
64
- }
65
-
66
- export class C2S {
67
- constructor({ agent, log = console.log, auth, scheme = null, mount = '' }) {
68
- this.agent = agent;
69
- this.log = log;
70
- this.auth = auth;
71
- // What this agent calls itself, for the actor it serves a local client.
72
- // The same two values oidc-auth rebuilds the DPoP `htu` from, so the two
73
- // surfaces agree on the address a client just used.
74
- this.scheme = scheme;
75
- this.mount = mount;
76
- }
77
-
78
- get store() { return this.agent.store; }
79
- get urls() { return this.agent.publisher?.urls; }
80
-
81
- send(res, status, obj, headers = {}) {
82
- res.writeHead(status, {
83
- 'content-type': 'application/json; charset=utf-8',
84
- 'cache-control': 'no-store',
85
- ...headers,
86
- });
87
- res.end(JSON.stringify(obj));
88
- return true;
89
- }
90
-
91
- byIri(iri) {
92
- return iri ? this.store.getStatuses().find((s) => s.noteId === iri) : null;
93
- }
94
-
95
- /** The months the archive holds, newest first. One container listing. */
96
- async archiveMonths() {
97
- const archive = this.agent.intake?.archive;
98
- if (!archive) return [];
99
- const { names } = await archive.list('');
100
- return (names || [])
101
- .map((n) => n.replace(/\/$/u, ''))
102
- .filter((n) => /^\d{4}-\d{2}$/u.test(n))
103
- .sort()
104
- .reverse();
105
- }
106
-
107
- /**
108
- * The owner's own inbox, §5.2. Paged by month because that is how the
109
- * archive is stored, so a page costs one listing and a read per item and no
110
- * page is dearer for another month being large.
111
- */
112
- async sendInbox(res, url) {
113
- const id = `${this.urls.base}ap/inbox`;
114
- const page = url?.searchParams?.get('page') || null;
115
- let months;
116
- try {
117
- months = await this.archiveMonths();
118
- } catch (e) {
119
- return this.send(res, 502, { error: `the archive could not be read: ${e.message}` });
120
- }
121
-
122
- if (!page) {
123
- if (!months.length && this.store.getConfig()?.archiveInbox === false) {
124
- this.log('inbox read: nothing to show — this identity does not keep what it receives');
125
- }
126
- return this.send(res, 200, {
127
- '@context': wire.AS_CTX, id, type: 'OrderedCollection',
128
- ...(months.length ? { first: `${id}?page=${months[0]}` } : { orderedItems: [] }),
129
- });
130
- }
131
- if (!/^\d{4}-\d{2}$/u.test(page)) {
132
- return this.send(res, 400, { error: 'page names a month, written 2026-09' });
133
- }
134
-
135
- const archive = this.agent.intake?.archive;
136
- let names = [];
137
- try {
138
- // The trailing slash matters: without it this names a document, not the
139
- // container, and a pod answers about the wrong thing.
140
- ({ names } = await archive.list(`${page}/`));
141
- } catch (e) {
142
- return this.send(res, 502, { error: `the archive could not be read: ${e.message}` });
143
- }
144
- const files = (names || []).filter((n) => n.endsWith('.json')).sort();
145
- if (files.length > MAX_INBOX_PAGE) {
146
- this.log(`inbox read: ${page} holds ${files.length} items; serving the first ${MAX_INBOX_PAGE}`);
147
- }
148
- const kept = [];
149
- for (const file of files.slice(0, MAX_INBOX_PAGE)) {
150
- // Read as written: these records are JSON-LD, and the default read asks
151
- // turtle-first, which a server is free to answer with turtle.
152
- const read = await archive.read(`${page}/${file}`, { accept: '*/*' });
153
- if (!read?.ok || !read.body) continue;
154
- try {
155
- const record = JSON.parse(read.body);
156
- // The record wraps the bytes as they arrived; the activity is those
157
- // bytes, not a retelling of them.
158
- kept.push({ at: record.receivedAt || '', activity: JSON.parse(record.raw) });
159
- } catch { /* a record that will not parse is not one that can be served */ }
160
- }
161
- kept.sort((a, b) => String(b.at).localeCompare(String(a.at)));
162
- const older = months.filter((m) => m < page)[0] || null;
163
- return this.send(res, 200, {
164
- '@context': wire.AS_CTX,
165
- id: `${id}?page=${page}`,
166
- type: 'OrderedCollectionPage',
167
- partOf: id,
168
- ...(older ? { next: `${id}?page=${older}` } : {}),
169
- orderedItems: kept.map((k) => k.activity),
170
- });
171
- }
172
-
173
- // The actor a client-to-server client on this machine reads. The same
174
- // document the pod holds, but for the fields naming where a write goes —
175
- // the outbox and the two OAuth endpoints — and for `id`, which names this
176
- // address so the client files its credential here and comes back. The pod's
177
- // id stays on as an alias; the two are one identity.
178
- // What this agent calls itself, to the client that just asked. The Host
179
- // passed the Authorities firewall before any route ran, so whichever alias
180
- // the client used is one this agent answers on. The scheme is read off the
181
- // connection unless the caller named one: only the Server passes a scheme,
182
- // and standalone this listener is its own TLS.
183
- localOrigin(req) {
184
- const host = req.headers.host;
185
- if (!host) return null;
186
- const scheme = this.scheme
187
- || (req.socket?.encrypted || req.headers['x-forwarded-proto'] === 'https' ? 'https' : 'http');
188
- return `${scheme}://${host}${this.mount}/`;
189
- }
190
-
191
- /**
192
- * The owner's own outbox, from what this agent recorded. The pod holds the
193
- * published copy and it stays the canonical one; this is served only where
194
- * that copy cannot be read by the client asking.
195
- */
196
- sendLocalOutbox(res, url, origin, { owner = false } = {}) {
197
- const id = `${origin}ap/outbox`;
198
- // newest first; the owner's view is every message, the public one what a
199
- // stranger may see
200
- const outbox = this.store.read(owner ? 'outbox-own.json' : 'outbox.json', []);
201
- const page = url?.searchParams?.get('page') || null;
202
- const ct = { 'content-type': 'application/activity+json; charset=utf-8' };
203
- if (!page) {
204
- return this.send(res, 200, {
205
- '@context': wire.AS_CTX, id, type: 'OrderedCollection', totalItems: outbox.length,
206
- ...(outbox.length ? { first: `${id}?page=1` } : { orderedItems: [] }),
207
- }, ct);
208
- }
209
- const n = Number(page);
210
- if (!Number.isInteger(n) || n < 1) {
211
- return this.send(res, 400, { error: 'page is a whole number from 1' });
212
- }
213
- const start = (n - 1) * MAX_OUTBOX_PAGE;
214
- const items = outbox.slice(start, start + MAX_OUTBOX_PAGE);
215
- return this.send(res, 200, {
216
- '@context': wire.AS_CTX,
217
- id: `${id}?page=${n}`,
218
- type: 'OrderedCollectionPage',
219
- partOf: id,
220
- ...(start + items.length < outbox.length ? { next: `${id}?page=${n + 1}` } : {}),
221
- // A post is recorded by its object id and published as the Create beside
222
- // it — the same mapping the pod's copy uses, from the same helper.
223
- orderedItems: items.map(wire.outboxWireItem),
224
- }, ct);
225
- }
226
-
227
- sendLocalActor(req, res, origin) {
228
- const pub = this.agent.publisher;
229
- const cfg = pub.config || {};
230
- const gw = cfg.gateway;
231
- const gwActive = gw && gw.url && gw.mode && gw.mode !== 'off';
232
- const doc = pub.actorDocFor({
233
- inbox: gwActive ? gw.url : (cfg.inboxUrl || null),
234
- clientOrigin: origin,
235
- });
236
- // A client files its credential under the id it reads here and comes back
237
- // to that address for everything after, so this document has to name
238
- // itself. The pod's id stays on as an alias rather than being dropped —
239
- // the two are one identity. Nothing published ever carries this id: every
240
- // activity the dispatcher builds names the pod's actor.
241
- const canonical = doc.id;
242
- doc.id = `${origin}ap/actor`;
243
- doc.alsoKnownAs = [...new Set([...(doc.alsoKnownAs || []), canonical])];
244
- const body = JSON.stringify(doc);
245
- res.writeHead(200, {
246
- 'content-type': 'application/activity+json; charset=utf-8',
247
- 'cache-control': 'no-store',
248
- 'content-length': Buffer.byteLength(body),
249
- });
250
- res.end(req.method === 'HEAD' ? undefined : body);
251
- return true;
252
- }
253
-
254
- async handle(req, res, pathname, url) { // eslint-disable-line no-unused-vars
255
- if (pathname !== '/ap/outbox' && pathname !== '/ap/actor' && pathname !== '/ap/inbox') return false;
256
- if (req.method === 'OPTIONS') {
257
- // Accept-Post is what a client such as dokieli reads to choose a format;
258
- // naming JSON only is what makes it send JSON-LD rather than HTML.
259
- res.writeHead(204, pathname === '/ap/inbox' ? { allow: 'GET, OPTIONS' }
260
- : { allow: 'GET, POST, OPTIONS', 'accept-post': 'application/ld+json, application/activity+json' });
261
- res.end(); return true;
262
- }
263
- if (!this.agent.configured() || !this.urls) {
264
- return this.send(res, 409, { error: 'agent not configured' });
265
- }
266
- if (pathname === '/ap/inbox') {
267
- if (req.method !== 'GET' && req.method !== 'HEAD') {
268
- return this.send(res, 405, { error: "deliveries go to this actor's inbox on the pod, which the "
269
- + 'actor document names; this address is the owner reading their own' });
270
- }
271
- const reader = await this.auth(req, pathname);
272
- if (!reader.ok) return this.send(res, reader.status, { error: reader.error }, reader.headers || {});
273
- return this.sendInbox(res, url);
274
- }
275
- if (req.method === 'GET' || req.method === 'HEAD') {
276
- // The owner, signed in, reads every message the actor produced (§5.1:
277
- // the outbox is filtered by who asks). Anyone else reads the public one.
278
- const owner = pathname === '/ap/outbox' && req.headers.authorization
279
- ? (await this.auth(req, pathname)).ok : false;
280
- // Where the client surface answers on an address a stranger can reach,
281
- // the pod's copies already name it and are the documents. Standalone
282
- // they are not reachable by the client asking, so it is answered here.
283
- const local = this.agent.publisher?.clientOrigin ? null : this.localOrigin(req);
284
- if (local && pathname === '/ap/actor') return this.sendLocalActor(req, res, local);
285
- if (local && pathname === '/ap/outbox') return this.sendLocalOutbox(res, url, local, { owner });
286
- // The owner is answered here, not sent on: a redirect loses the
287
- // credential (a browser drops it across origins, and a DPoP proof names
288
- // the address it was made for), so the pod would refuse them.
289
- if (owner && pathname === '/ap/outbox') {
290
- return this.sendLocalOutbox(res, url, this.agent.publisher.clientOrigin, { owner });
291
- }
292
- // The pod's copy is the document; send the reader there.
293
- const target = pathname === '/ap/actor' ? this.urls.actor : this.urls.outbox;
294
- res.writeHead(303, { location: target, 'cache-control': 'no-store' });
295
- res.end();
296
- return true;
297
- }
298
- if (pathname !== '/ap/outbox' || req.method !== 'POST') {
299
- return this.send(res, 405, { error: 'POST the outbox; GET redirects to the pod' });
300
- }
301
-
302
- const who = await this.auth(req, pathname);
303
- if (!who.ok) return this.send(res, who.status, { error: who.error }, who.headers || {});
304
-
305
- // A viewer (another agent holds the lease) may not act — but the user
306
- // acting HERE outranks an idle active agent elsewhere, exactly as on the
307
- // facade: a write attempt claims the lease, and only a failed claim 503s.
308
- if (this.agent.viewer) {
309
- const took = await this.agent.requestTakeover?.();
310
- if (!took) return this.send(res, 503, { error: 'another agent is active for this pod — takeover failed, try again' });
311
- }
312
-
313
- // Read as JSON-LD, so a client may send its activity with whatever context
314
- // it likes and still be understood. What is read is the GRAPH: `dispatch`
315
- // takes decisions from it and the publisher builds the document that is
316
- // actually posted, so nothing a client sent is republished verbatim and a
317
- // term it aliased still means what it says.
318
- // `raw` is the document as the client wrote it. The graph is what
319
- // decisions are taken from; the bytes are what an object that is stored
320
- // as sent (publishObject) is stored from — a read against our contexts
321
- // rewrites a stranger's terms to full IRIs, which is right for reading and
322
- // wrong for keeping.
323
- let activity; let raw = null;
324
- try {
325
- const body = await readBody(req);
326
- try { raw = JSON.parse(body); } catch { raw = null; }
327
- const read = await readLenient(body);
328
- activity = read.view ?? read.doc;
329
- } catch (e) {
330
- return this.send(res, 400, { error: `unreadable body: ${e.message}` });
331
- }
332
- if (!activity || typeof activity !== 'object' || Array.isArray(activity) || !activity.type) {
333
- return this.send(res, 400, { error: 'a typed ActivityStreams object is required' });
334
- }
335
- // The name the client asks for its new document (LDP's Slug), taken when
336
- // it is plain and free — it is what lets the client know the address of
337
- // what it made before anything answers.
338
- const slug = safeSlug(req.headers.slug) || null;
339
- const r = await this.dispatch(activity, { slug, raw });
340
- return this.send(res, r.status, r.body, r.headers);
341
- }
342
-
343
- // Addressing → the facade's four visibilities, inverting the table the
344
- // composer writes (wire.addressing). Nothing stated is a public post — what
345
- // every client means by a post with no audience chosen, and what a client
346
- // that never addresses (dokieli's annotations) needs.
347
- visibilityOf(activity, object) {
348
- const to = arr(activity.to ?? object?.to).map(idOf);
349
- const cc = arr(activity.cc ?? object?.cc).map(idOf);
350
- // Blind copies alone are still an audience: a post for them and nobody
351
- // else is direct, never public.
352
- const blind = arr(activity.bto ?? object?.bto).length + arr(activity.bcc ?? object?.bcc).length;
353
- if (!to.length && !cc.length) return blind ? 'direct' : 'public';
354
- if (to.includes(wire.PUBLIC)) return 'public';
355
- if (cc.includes(wire.PUBLIC)) return 'unlisted';
356
- // Followers named anywhere — "to alice, cc my followers" included — is a
357
- // followers-only post that also reaches whoever else is named.
358
- if (to.includes(this.urls.followers) || cc.includes(this.urls.followers)) return 'private';
359
- return 'direct';
360
- }
361
-
362
- // `serial` and `at` name what this activity will make: the outbox door
363
- // chose them and told the client, so they are used as given. A client
364
- // posting here directly gets fresh ones.
365
- async dispatch(activity, { slug = null, raw = null, serial = Date.now(), at = new Date().toISOString() } = {}) {
366
- const reply = (status, body, headers = {}) => ({ status, body, headers });
367
- if (!activity || typeof activity !== 'object' || Array.isArray(activity) || !activity.type) {
368
- return reply(400, { error: 'a typed ActivityStreams object is required' });
369
- }
370
- // A bare object arrives without an activity around it; the server supplies
371
- // the Create (§6.2.1), carrying the object's own addressing up onto it.
372
- if (!ACTIVITY_TYPES.has(activity.type)) {
373
- // Every audience field is copied up onto the Create (§6.2.1).
374
- const { to, cc, bto, bcc, audience } = activity;
375
- activity = { type: 'Create', object: activity, to, cc, bto, bcc, audience };
376
- }
377
- try {
378
- return await this._dispatch(activity, { slug, raw, reply, serial, at });
379
- } catch (e) {
380
- this.log(`c2s ${activity?.type}: ${e.message}`);
381
- return reply(422, { error: e.message || String(e) });
382
- }
383
- }
384
-
385
- // The actors a client addressed by id, beyond the audience words: the ones
386
- // in to/cc are listed and delivered to; the ones in bto/bcc are delivered
387
- // to and never listed (§6).
388
- addressedActors(activity, object) {
389
- const pick = (...fields) => [...new Set(fields.flatMap((f) => arr(activity[f] ?? object?.[f]).map(idOf)))]
390
- .filter((a) => typeof a === 'string' && /^https?:\/\//u.test(a)
391
- && a !== wire.PUBLIC && a !== this.urls.followers && a !== this.urls.actor);
392
- // `audience` is who the post is for, a group most often: delivered to and
393
- // named like to and cc (§7.1).
394
- return { also: pick('to', 'cc', 'audience'), deliverTo: pick('bto', 'bcc') };
395
- }
396
-
397
- async _dispatch(activity, { slug, raw, reply, serial, at }) {
398
- const agent = this.agent;
399
- // §6: every post to the outbox answers 201 with the new activity's id.
400
- const made = (id, body = {}) => reply(201, { id, ...body }, { location: id });
401
- // What is never sent anywhere — a person's block, a pin — is still a
402
- // message this actor produced, so the owner's outbox records it here.
403
- const kept = (type, extra) => {
404
- const id = type === 'Undo' ? wire.undoActivityId(this.urls, serial) : `${this.urls.actor}#${type.toLowerCase()}-${serial}`;
405
- const act = { id, type, actor: this.urls.actor, published: at, ...extra };
406
- agent.publisher.recordOwn?.(act);
407
- return act.id;
408
- };
409
- const object = typeof activity.object === 'object' && activity.object !== null
410
- ? activity.object : null;
411
- const objectId = idOf(activity.object);
412
-
413
- switch (activity.type) {
414
- case 'Create': {
415
- const makes = object?.type || 'Note';
416
- if (!object) return reply(422, { error: 'a Create carries the object it creates' });
417
- const visibility = this.visibilityOf(activity, object);
418
- const { also, deliverTo } = this.addressedActors(activity, object);
419
- // Not a Note and not a poll: stored as sent, under this actor, and
420
- // the Create around it delivered — a Web Annotation, for one.
421
- if (makes !== 'Note' && makes !== 'Question') {
422
- const asSent = raw && typeof raw === 'object' && !Array.isArray(raw)
423
- ? (ACTIVITY_TYPES.has(raw.type) ? (raw.object && typeof raw.object === 'object' ? raw.object : null) : raw)
424
- : null;
425
- const made = await agent.publisher.publishObject(asSent || object, { visibility, slug, also, deliverTo });
426
- return reply(201, { id: made.createId, object: made.id }, { location: made.createId });
427
- }
428
- // `source.content` is the client's plain text when it sends one; bare
429
- // `content` is TREATED as plain text and escaped — markup survives as
430
- // visible characters rather than as markup. Documented v1 limit.
431
- const text = String(object.source?.content ?? object.content ?? '');
432
- if (!text.trim()) return reply(422, { error: 'the note has no content' });
433
-
434
- // A Question is a poll: the choices are in oneOf (pick one) or anyOf
435
- // (pick several), each naming itself, and endTime is when it shuts.
436
- if (makes === 'Question') {
437
- const one = arr(object.oneOf);
438
- const many = arr(object.anyOf);
439
- const titles = (one.length ? one : many).map((c) => String(c?.name ?? '').trim()).filter(Boolean);
440
- try {
441
- const question = await agent.publisher.publishQuestion(text, {
442
- options: titles,
443
- multiple: !one.length && many.length > 0,
444
- expiresAt: object.endTime || null,
445
- inReplyTo: idOf(object.inReplyTo) || undefined,
446
- visibility,
447
- spoilerText: object.summary || null,
448
- sensitive: object.sensitive === true,
449
- slug,
450
- });
451
- return reply(201,
452
- { id: wire.createActivityId(question.id), object: question.id },
453
- { location: wire.createActivityId(question.id) });
454
- } catch (e) {
455
- return reply(422, { error: e.message });
456
- }
457
- }
458
-
459
- const attachments = arr(object.attachment).map((a) => ({
460
- url: a?.url, mediaType: a?.mediaType,
461
- ...(a?.name ? { description: a.name } : {}),
462
- })).filter((a) => a.url);
463
- const note = await agent.publisher.publishNote(text, {
464
- inReplyTo: idOf(object.inReplyTo) || undefined,
465
- attachments,
466
- visibility,
467
- spoilerText: object.summary || null,
468
- sensitive: object.sensitive === true,
469
- slug, also, deliverTo,
470
- });
471
- return reply(201, { id: wire.createActivityId(note.id), object: note.id },
472
- { location: wire.createActivityId(note.id) });
473
- }
474
-
475
- case 'Update': {
476
- if (objectId === this.urls.actor) {
477
- return reply(422, { error: 'edit the profile on the admin surface; actor updates are not taken here' });
478
- }
479
- const s = this.byIri(objectId);
480
- if (!s) return reply(404, { error: 'no such note here' });
481
- if (s.actor !== this.urls.actor || s.kind !== 'post') {
482
- return reply(403, { error: 'not your note' });
483
- }
484
- // §6.3: a client's Update changes only what it sends, and a field sent
485
- // as null is removed. What was sent is read from the bytes, because a
486
- // JSON-LD reading drops the nulls that mean "remove this".
487
- const sent = raw?.type === 'Update' && raw.object && typeof raw.object === 'object' ? raw.object : (object || {});
488
- const has = (k) => Object.prototype.hasOwnProperty.call(sent, k);
489
- const stored = await Promise.resolve().then(() => podNotes.read(agent.remote, s.noteId)).catch(() => null);
490
- if (stored?.type && stored.type !== 'Note') {
491
- await agent.publisher.updateObject(s, sent, { updated: at });
492
- return made(wire.updateActivityId(s.noteId, at), { object: s.noteId });
493
- }
494
- const text = has('content') || sent.source?.content !== undefined
495
- ? String(object?.source?.content ?? object?.content ?? '') : String(s.text ?? '');
496
- if (!text.trim()) return reply(422, { error: 'the edit leaves the note with no content' });
497
- const attachments = has('attachment')
498
- ? arr(object.attachment).map((a) => ({
499
- url: a?.url, mediaType: a?.mediaType,
500
- ...(a?.name ? { description: a.name } : {}),
501
- })).filter((a) => a.url)
502
- : null;
503
- await agent.publisher.updateNote(s, {
504
- content: text,
505
- spoilerText: has('summary') ? (object?.summary || null) : (s.spoiler || null),
506
- sensitive: has('sensitive') ? object?.sensitive === true : null, attachments,
507
- updated: at,
508
- });
509
- return made(wire.updateActivityId(s.noteId, at), { object: s.noteId });
510
- }
511
-
512
- case 'Delete': {
513
- if (objectId === this.urls.actor) {
514
- return reply(422, { error: 'retiring the actor is done on the admin surface, where it asks twice' });
515
- }
516
- const s = this.byIri(objectId);
517
- if (!s) return reply(404, { error: 'no such note here' });
518
- if (s.actor !== this.urls.actor || s.kind !== 'post') {
519
- return reply(403, { error: 'not your note' });
520
- }
521
- const r = await social.deleteNote(agent, s);
522
- if (!r.ok) return reply(502, { error: r.error, stillPublished: r.stillPublished });
523
- return made(wire.deleteActivityId(s.noteId), { object: s.noteId });
524
- }
525
-
526
- case 'Follow': {
527
- if (!objectId) return reply(400, { error: 'whom? object must name an actor' });
528
- // An acct: form or bare handle resolves through WebFinger; an https
529
- // IRI is fetched directly.
530
- if (/^acct:|^@|^[^/@]+@[^/@]+$/.test(objectId) && !/^https?:/.test(objectId)) {
531
- const r = await social.followHandle(agent, objectId.replace(/^acct:/, ''), { serial });
532
- const rec = this.store.getContacts().following.find((f) => f.actor === r.actor);
533
- return made(rec?.followActivity?.id, { object: r.actor });
534
- }
535
- const doc = await social.followActor(agent, objectId, { serial });
536
- const rec = this.store.getContacts().following.find((f) => f.actor === doc.id);
537
- return made(rec?.followActivity?.id, { object: doc.id });
538
- }
539
-
540
- case 'Like': {
541
- const s = this.byIri(objectId);
542
- if (!s) return reply(422, { error: 'that note is not held here — like what the timeline holds' });
543
- const updated = await social.favourite(agent, s, { serial });
544
- return made(updated.likeActivity?.id, { object: s.noteId });
545
- }
546
-
547
- case 'Announce': {
548
- const s = this.byIri(objectId);
549
- if (!s) return reply(422, { error: 'that note is not held here — boost what the timeline holds' });
550
- const updated = await social.reblog(agent, s, { serial });
551
- return made(updated.announceActivity?.id, { object: s.noteId });
552
- }
553
-
554
- case 'Undo': {
555
- // What is being unsaid — by the inner activity's id when the client
556
- // sends one, by its type+object when it re-states it instead.
557
- const inner = object;
558
- const innerId = idOf(activity.object);
559
- const undone = wire.undoActivityId(this.urls, serial);
560
- if (inner?.type === 'Block') {
561
- const target = idOf(inner.object);
562
- if (!target) return reply(400, { error: 'unblock whom?' });
563
- await social.unblockActor(agent, target, { serial });
564
- // A group's unban was sent, and recorded as it went; a person's is not.
565
- if (this.store.getConfig()?.kind !== 'group') {
566
- kept('Undo', { object: { type: 'Block', actor: this.urls.actor, object: target } });
567
- }
568
- return made(undone, { object: target });
569
- }
570
- const statuses = this.store.getStatuses();
571
- let s = innerId ? statuses.find((x) => x.likeActivity?.id === innerId) : null;
572
- if (!s && inner?.type === 'Like') s = this.byIri(idOf(inner.object));
573
- if (s?.favourited) {
574
- const updated = await social.unfavourite(agent, s, { serial });
575
- return made(undone, { object: updated.noteId });
576
- }
577
- s = innerId ? statuses.find((x) => x.announceActivity?.id === innerId) : null;
578
- if (!s && inner?.type === 'Announce') s = this.byIri(idOf(inner.object));
579
- if (s?.reblogged) {
580
- const updated = await social.unreblog(agent, s, { serial });
581
- return made(undone, { object: updated.noteId });
582
- }
583
- const following = this.store.getContacts().following;
584
- const rec = following.find((f) => f.followActivity?.id === innerId)
585
- || (inner?.type === 'Follow' ? following.find((f) => f.actor === idOf(inner.object)) : null);
586
- if (rec) {
587
- await social.unfollowActor(agent, rec.actor, { serial });
588
- return made(undone, { object: rec.actor });
589
- }
590
- return reply(422, { error: 'nothing here matches what that Undo names' });
591
- }
592
-
593
- case 'Block': {
594
- if (!objectId) return reply(400, { error: 'block whom? object must name an actor' });
595
- await social.blockActor(agent, objectId, { serial });
596
- // A group's ban was sent to its members; a person's never goes anywhere.
597
- const id = this.store.getConfig()?.kind === 'group'
598
- ? `${this.urls.actor}#block-${serial}` : kept('Block', { object: objectId });
599
- return made(id, { object: objectId });
600
- }
601
-
602
- case 'Add':
603
- case 'Remove': {
604
- // The one collection a client may edit is the pins (§7.6/§7.9 in the
605
- // other direction): target must be the featured collection.
606
- if (idOf(activity.target) !== this.urls.featured) {
607
- return reply(422, { error: 'the featured collection is the one Add/Remove edits here' });
608
- }
609
- const s = this.byIri(objectId);
610
- if (!s) return reply(404, { error: 'no such note here' });
611
- const updated = await social.pinStatus(agent, s, activity.type === 'Add');
612
- return made(kept(activity.type, { object: updated.noteId, target: this.urls.featured }),
613
- { object: updated.noteId, pinned: !!updated.pinned });
614
- }
615
-
616
- case 'Accept':
617
- case 'Reject': {
618
- // Answering a held follow request: the object is the Follow (or the
619
- // requester). Which request is meant comes from the Follow's actor.
620
- const requester = object?.actor ? idOf(object.actor) : objectId;
621
- if (!requester) return reply(400, { error: 'whose request? object must name the Follow or its actor' });
622
- const r = activity.type === 'Accept'
623
- ? await social.admitRequest(agent, requester, { serial }).catch((e) => ({ error: e.message }))
624
- : await social.refuseRequest(agent, requester, { serial }).catch((e) => ({ error: e.message }));
625
- if (r.error) return reply(404, { error: r.error });
626
- return made(`${this.urls.actor}#${activity.type.toLowerCase()}-${serial}`, { object: requester });
627
- }
628
-
629
- case 'Move':
630
- return reply(422, { error: 'moving the account is done on the admin surface, where it asks twice' });
631
-
632
- default:
633
- return reply(422, { error: `no handler for ${activity.type}` });
634
- }
635
- }
636
- }