fedipod-server 0.29.0 → 0.30.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 (440) hide show
  1. package/README.md +11 -6
  2. package/dist/door.d.ts +4 -0
  3. package/dist/door.js +31 -0
  4. package/dist/fedipod.d.ts +16 -0
  5. package/dist/fedipod.js +13 -0
  6. package/dist/front.d.ts +3 -0
  7. package/dist/front.js +94 -0
  8. package/dist/handler.d.ts +36 -101
  9. package/dist/handler.js +85 -645
  10. package/dist/handler.jsonld +2 -30
  11. package/dist/identities.d.ts +62 -0
  12. package/dist/identities.js +236 -0
  13. package/dist/mounts.d.ts +43 -0
  14. package/dist/mounts.js +123 -0
  15. package/dist/opt-in.d.ts +46 -0
  16. package/dist/opt-in.js +176 -0
  17. package/package.json +10 -18
  18. package/lib/client/c2s.mjs +0 -636
  19. package/lib/client/localapi.mjs +0 -87
  20. package/lib/client/masto/accounts.mjs +0 -310
  21. package/lib/client/masto/body.mjs +0 -156
  22. package/lib/client/masto/index.mjs +0 -206
  23. package/lib/client/masto/instance.mjs +0 -112
  24. package/lib/client/masto/media.mjs +0 -79
  25. package/lib/client/masto/oauth.mjs +0 -656
  26. package/lib/client/masto/render.mjs +0 -518
  27. package/lib/client/masto/statuses.mjs +0 -372
  28. package/lib/client/masto/timelines.mjs +0 -438
  29. package/lib/client/oidc-auth.mjs +0 -62
  30. package/lib/client/streaming.mjs +0 -154
  31. package/lib/client/webpush.mjs +0 -98
  32. package/lib/connections/acctfeed.mjs +0 -257
  33. package/lib/connections/atproto.mjs +0 -309
  34. package/lib/connections/bskyfeed.mjs +0 -243
  35. package/lib/connections/bskygroup.mjs +0 -162
  36. package/lib/connections/fediacct.mjs +0 -253
  37. package/lib/connections/import.mjs +0 -357
  38. package/lib/connections/tagfeed.mjs +0 -212
  39. package/lib/connections/vault.mjs +0 -114
  40. package/lib/core/as2.mjs +0 -206
  41. package/lib/core/contexts/activitystreams.json +0 -379
  42. package/lib/core/contexts/anno.json +0 -126
  43. package/lib/core/contexts/did-v1.json +0 -57
  44. package/lib/core/contexts/fep-5711.json +0 -36
  45. package/lib/core/contexts/gotosocial.json +0 -86
  46. package/lib/core/contexts/identity-v1.json +0 -152
  47. package/lib/core/contexts/index.mjs +0 -63
  48. package/lib/core/contexts/join-lemmy.json +0 -33
  49. package/lib/core/contexts/joinmastodon.json +0 -28
  50. package/lib/core/contexts/litepub-0.1.json +0 -60
  51. package/lib/core/contexts/map.json +0 -19
  52. package/lib/core/contexts/miscellany.json +0 -19
  53. package/lib/core/contexts/quotes.json +0 -18
  54. package/lib/core/contexts/schemaorg.json +0 -8845
  55. package/lib/core/contexts/security-data-integrity-v1.json +0 -78
  56. package/lib/core/contexts/security-data-integrity-v2.json +0 -81
  57. package/lib/core/contexts/security-multikey-v1.json +0 -35
  58. package/lib/core/contexts/security-v1.json +0 -74
  59. package/lib/core/contexts/webfinger.json +0 -10
  60. package/lib/core/deliver.mjs +0 -387
  61. package/lib/core/graphview.mjs +0 -327
  62. package/lib/core/intake/activities.mjs +0 -649
  63. package/lib/core/intake/activity.mjs +0 -318
  64. package/lib/core/intake/channel.mjs +0 -144
  65. package/lib/core/intake/group.mjs +0 -258
  66. package/lib/core/intake/index.mjs +0 -732
  67. package/lib/core/intake/notes.mjs +0 -367
  68. package/lib/core/intake/verify.mjs +0 -142
  69. package/lib/core/keys.mjs +0 -153
  70. package/lib/core/lease.mjs +0 -206
  71. package/lib/core/place.mjs +0 -82
  72. package/lib/core/polls.mjs +0 -105
  73. package/lib/core/profile-page.mjs +0 -104
  74. package/lib/core/proof.mjs +0 -79
  75. package/lib/core/publisher/collections.mjs +0 -272
  76. package/lib/core/publisher/index.mjs +0 -522
  77. package/lib/core/publisher/notes.mjs +0 -519
  78. package/lib/core/publisher/own.mjs +0 -143
  79. package/lib/core/publisher/questions.mjs +0 -241
  80. package/lib/core/publisher/restore.mjs +0 -224
  81. package/lib/core/shapes/activitystreams.ttl +0 -157
  82. package/lib/core/shapes/index.mjs +0 -132
  83. package/lib/core/shapes/shapes-text.mjs +0 -13
  84. package/lib/core/social.mjs +0 -561
  85. package/lib/core/storage.mjs +0 -176
  86. package/lib/core/store.mjs +0 -578
  87. package/lib/core/wire-quotes.mjs +0 -71
  88. package/lib/core/wire.mjs +0 -925
  89. package/lib/device/account.mjs +0 -87
  90. package/lib/device/admin/index.mjs +0 -13
  91. package/lib/device/admin/origins.mjs +0 -35
  92. package/lib/device/admin/routes/connections.mjs +0 -144
  93. package/lib/device/admin/routes/gateway.mjs +0 -199
  94. package/lib/device/admin/routes/lifecycle.mjs +0 -200
  95. package/lib/device/admin/routes/owner.mjs +0 -322
  96. package/lib/device/admin/routes/setup.mjs +0 -428
  97. package/lib/device/admin/routes/social.mjs +0 -188
  98. package/lib/device/admin/server.mjs +0 -95
  99. package/lib/device/admin/static.mjs +0 -247
  100. package/lib/device/admin/surface.mjs +0 -315
  101. package/lib/device/certs.mjs +0 -281
  102. package/lib/device/cli/commands/account.mjs +0 -586
  103. package/lib/device/cli/commands/run.mjs +0 -278
  104. package/lib/device/cli/commands/service.mjs +0 -221
  105. package/lib/device/cli/commands/setup.mjs +0 -489
  106. package/lib/device/cli/commands/state.mjs +0 -561
  107. package/lib/device/cli/context.mjs +0 -288
  108. package/lib/device/export-collections.mjs +0 -161
  109. package/lib/device/gateway-move.mjs +0 -58
  110. package/lib/device/home.mjs +0 -161
  111. package/lib/device/migrate.mjs +0 -115
  112. package/lib/device/ports.mjs +0 -27
  113. package/lib/device/remote.mjs +0 -54
  114. package/lib/device/setup.mjs +0 -496
  115. package/lib/device/update.mjs +0 -96
  116. package/lib/gateway/caches.mjs +0 -36
  117. package/lib/gateway/directory.mjs +0 -90
  118. package/lib/gateway/front-core.mjs +0 -966
  119. package/lib/gateway/gateway-core.mjs +0 -244
  120. package/lib/gateway/headers.mjs +0 -49
  121. package/lib/gateway/httpsig.mjs +0 -159
  122. package/lib/gateway/notices.mjs +0 -84
  123. package/lib/gateway/quiet.mjs +0 -189
  124. package/lib/gateway/relay-extras.mjs +0 -89
  125. package/lib/gateway/token-claims.mjs +0 -16
  126. package/lib/pod/README.md +0 -61
  127. package/lib/pod/actor.mjs +0 -96
  128. package/lib/pod/collection.mjs +0 -77
  129. package/lib/pod/containers.mjs +0 -140
  130. package/lib/pod/discovery.mjs +0 -46
  131. package/lib/pod/featured.mjs +0 -18
  132. package/lib/pod/followers.mjs +0 -24
  133. package/lib/pod/following.mjs +0 -10
  134. package/lib/pod/http.mjs +0 -74
  135. package/lib/pod/inbox.mjs +0 -166
  136. package/lib/pod/links.mjs +0 -35
  137. package/lib/pod/location.mjs +0 -52
  138. package/lib/pod/media.mjs +0 -27
  139. package/lib/pod/notes.mjs +0 -78
  140. package/lib/pod/notifications.mjs +0 -89
  141. package/lib/pod/outbox.mjs +0 -18
  142. package/lib/pod/package.json +0 -19
  143. package/lib/pod/policy.mjs +0 -58
  144. package/lib/pod/private.mjs +0 -19
  145. package/lib/pod/root.mjs +0 -104
  146. package/lib/pod/state.mjs +0 -40
  147. package/lib/pod/transport.mjs +0 -674
  148. package/lib/pod/type-index.mjs +0 -101
  149. package/lib/pod/urls.mjs +0 -91
  150. package/lib/server/embed.mjs +0 -424
  151. package/lib/session/README.md +0 -137
  152. package/lib/session/demo.html +0 -94
  153. package/lib/session/fedi-account.mjs +0 -417
  154. package/lib/session/fedi-login.mjs +0 -123
  155. package/lib/session/oidc-session.mjs +0 -233
  156. package/lib/session/package.json +0 -49
  157. package/lib/shared/guard.mjs +0 -213
  158. package/lib/shared/links.mjs +0 -9
  159. package/lib/shared/safefetch.mjs +0 -235
  160. package/lib/shared/ua.mjs +0 -29
  161. package/phanpy/dist/404.html +0 -40
  162. package/phanpy/dist/_headers +0 -2
  163. package/phanpy/dist/apple-touch-icon.png +0 -0
  164. package/phanpy/dist/assets/ICONS-DIPJeU0C.js +0 -2
  165. package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +0 -1
  166. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +0 -183
  167. package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
  168. package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
  169. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +0 -2
  170. package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +0 -1
  171. package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  172. package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
  173. package/phanpy/dist/assets/chunk-aKtaBQYM.js +0 -1
  174. package/phanpy/dist/assets/compose-CFEx7wb4.js +0 -2
  175. package/phanpy/dist/assets/compose-CFEx7wb4.js.map +0 -1
  176. package/phanpy/dist/assets/compose-ZJN5xSb4.js +0 -35
  177. package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +0 -1
  178. package/phanpy/dist/assets/debug.module-YFQgwnIr.js +0 -52
  179. package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +0 -1
  180. package/phanpy/dist/assets/dom-p1YcJ5Rw.js +0 -2
  181. package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +0 -1
  182. package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  183. package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  184. package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  185. package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  186. package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +0 -1
  187. package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +0 -1
  188. package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
  189. package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +0 -1
  190. package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
  191. package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
  192. package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
  193. package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
  194. package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
  195. package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
  196. package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
  197. package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
  198. package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +0 -1
  199. package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +0 -1
  200. package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
  201. package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +0 -1
  202. package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
  203. package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +0 -1
  204. package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
  205. package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
  206. package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +0 -1
  207. package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
  208. package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +0 -1
  209. package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
  210. package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
  211. package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
  212. package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
  213. package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
  214. package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +0 -1
  215. package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +0 -1
  216. package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +0 -1
  217. package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +0 -1
  218. package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +0 -1
  219. package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
  220. package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +0 -1
  221. package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
  222. package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +0 -1
  223. package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +0 -1
  224. package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +0 -1
  225. package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
  226. package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
  227. package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +0 -1
  228. package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +0 -1
  229. package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
  230. package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
  231. package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +0 -1
  232. package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +0 -1
  233. package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +0 -1
  234. package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
  235. package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +0 -1
  236. package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +0 -1
  237. package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +0 -1
  238. package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
  239. package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
  240. package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
  241. package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +0 -1
  242. package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +0 -1
  243. package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +0 -1
  244. package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +0 -1
  245. package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +0 -1
  246. package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
  247. package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
  248. package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +0 -1
  249. package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +0 -1
  250. package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +0 -1
  251. package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +0 -1
  252. package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +0 -1
  253. package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +0 -1
  254. package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +0 -1
  255. package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +0 -1
  256. package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
  257. package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +0 -1
  258. package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +0 -1
  259. package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +0 -1
  260. package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
  261. package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +0 -1
  262. package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +0 -1
  263. package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
  264. package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
  265. package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
  266. package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +0 -1
  267. package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +0 -1
  268. package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
  269. package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +0 -1
  270. package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +0 -1
  271. package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +0 -1
  272. package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +0 -1
  273. package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +0 -1
  274. package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +0 -1
  275. package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +0 -1
  276. package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +0 -1
  277. package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +0 -1
  278. package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
  279. package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +0 -1
  280. package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
  281. package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
  282. package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
  283. package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +0 -1
  284. package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
  285. package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +0 -1
  286. package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +0 -1
  287. package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
  288. package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +0 -1
  289. package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
  290. package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +0 -1
  291. package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
  292. package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +0 -1
  293. package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +0 -1
  294. package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
  295. package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +0 -1
  296. package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +0 -1
  297. package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
  298. package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +0 -1
  299. package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +0 -1
  300. package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +0 -1
  301. package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
  302. package/phanpy/dist/assets/instances-BNPptnn-.json +0 -333
  303. package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +0 -1
  304. package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +0 -1
  305. package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
  306. package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +0 -1
  307. package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +0 -1
  308. package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +0 -1
  309. package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +0 -1
  310. package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +0 -1
  311. package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +0 -1
  312. package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +0 -1
  313. package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +0 -1
  314. package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +0 -1
  315. package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +0 -1
  316. package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +0 -1
  317. package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +0 -1
  318. package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +0 -1
  319. package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +0 -1
  320. package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
  321. package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +0 -1
  322. package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
  323. package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +0 -1
  324. package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +0 -1
  325. package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
  326. package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +0 -1
  327. package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +0 -1
  328. package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
  329. package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +0 -1
  330. package/phanpy/dist/assets/locales/tok-CfheRbCu.js +0 -1
  331. package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +0 -1
  332. package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +0 -1
  333. package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +0 -1
  334. package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +0 -1
  335. package/phanpy/dist/assets/main-BdqNbG-a.js +0 -26
  336. package/phanpy/dist/assets/main-BdqNbG-a.js.map +0 -1
  337. package/phanpy/dist/assets/mock-home-DGhR__He.js +0 -2
  338. package/phanpy/dist/assets/mock-home-DGhR__He.js.map +0 -1
  339. package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
  340. package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  341. package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  342. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +0 -2
  343. package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +0 -1
  344. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +0 -20
  345. package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +0 -1
  346. package/phanpy/dist/assets/style-B1iLPTPR.css +0 -2
  347. package/phanpy/dist/assets/temml-DuyIQMei.js +0 -97
  348. package/phanpy/dist/assets/temml-DuyIQMei.js.map +0 -1
  349. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +0 -2
  350. package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
  351. package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
  352. package/phanpy/dist/assets/year-in-posts-DJv_star.js +0 -3
  353. package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +0 -1
  354. package/phanpy/dist/compose/index.html +0 -20
  355. package/phanpy/dist/favicon.ico +0 -0
  356. package/phanpy/dist/index.html +0 -73
  357. package/phanpy/dist/logo-192.png +0 -0
  358. package/phanpy/dist/logo-512.png +0 -0
  359. package/phanpy/dist/logo-badge-72.png +0 -0
  360. package/phanpy/dist/logo-maskable-512.png +0 -0
  361. package/phanpy/dist/logo-monochrome-512.png +0 -0
  362. package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
  363. package/phanpy/dist/manifest.webmanifest +0 -1
  364. package/phanpy/dist/og-image-2.jpg +0 -0
  365. package/phanpy/dist/og-image.png +0 -0
  366. package/phanpy/dist/robots.txt +0 -2
  367. package/phanpy/dist/sw.js +0 -2
  368. package/phanpy/dist/sw.js.map +0 -1
  369. package/phanpy/dist/version.json +0 -1
  370. package/run-agent.mjs +0 -899
  371. package/vendor/gate.cjs +0 -138
  372. package/vendor/idp-grant.cjs +0 -394
  373. package/vendor/jfetch.cjs +0 -16
  374. package/web/admin/actors.js +0 -147
  375. package/web/admin/bar.css +0 -126
  376. package/web/admin/bar.js +0 -99
  377. package/web/admin/client/client.js +0 -66
  378. package/web/admin/client/index.html +0 -69
  379. package/web/admin/client-pick.js +0 -79
  380. package/web/admin/common.js +0 -23
  381. package/web/admin/connections.js +0 -112
  382. package/web/admin/gateway.js +0 -133
  383. package/web/admin/group.js +0 -258
  384. package/web/admin/index.html +0 -612
  385. package/web/admin/notices-bar.js +0 -90
  386. package/web/admin/oauth-signin.mjs +0 -37
  387. package/web/admin/record.js +0 -393
  388. package/web/admin/setup/index.html +0 -257
  389. package/web/admin/setup/setup.js +0 -339
  390. package/web/admin/tokens.css +0 -68
  391. package/web/admin/upkeep.js +0 -177
  392. package/web/admin/window.css +0 -55
  393. package/web/admin/window.js +0 -136
  394. package/web/app/README.md +0 -81
  395. package/web/app/admin-facade.mjs +0 -588
  396. package/web/app/agent.mjs +0 -672
  397. package/web/app/atproto-browser.mjs +0 -86
  398. package/web/app/authorize_interaction.html +0 -56
  399. package/web/app/authorize_interaction.js +0 -69
  400. package/web/app/boot.mjs +0 -616
  401. package/web/app/deliver-relay.mjs +0 -129
  402. package/web/app/dist/boot.js +0 -34562
  403. package/web/app/dist/boot.js.map +0 -7
  404. package/web/app/dist/sw.js +0 -73456
  405. package/web/app/dist/sw.js.map +0 -7
  406. package/web/app/fediacct-browser.mjs +0 -146
  407. package/web/app/gateway-move.mjs +0 -60
  408. package/web/app/idb-kv.mjs +0 -35
  409. package/web/app/index.html +0 -258
  410. package/web/app/keys-browser.mjs +0 -71
  411. package/web/app/keystore.mjs +0 -82
  412. package/web/app/oidc-session.mjs +0 -6
  413. package/web/app/pod-auth.mjs +0 -248
  414. package/web/app/pod-remote.mjs +0 -84
  415. package/web/app/shims/_globals.mjs +0 -2
  416. package/web/app/shims/fedify-sig.mjs +0 -77
  417. package/web/app/shims/node-crypto.mjs +0 -113
  418. package/web/app/shims/node-fs.mjs +0 -17
  419. package/web/app/shims/node-path.mjs +0 -19
  420. package/web/app/shims/node-url.mjs +0 -7
  421. package/web/app/shims/prelude.js +0 -58
  422. package/web/app/shims/safefetch.mjs +0 -58
  423. package/web/app/shims/shapes-text.mjs +0 -8
  424. package/web/app/shims/web-push.mjs +0 -14
  425. package/web/app/signup.mjs +0 -357
  426. package/web/app/sw-src.mjs +0 -220
  427. package/web/app/update.js +0 -70
  428. package/web/front/admin.html +0 -55
  429. package/web/front/admin.js +0 -89
  430. package/web/front/install.sh +0 -69
  431. package/web/front/new-account.html +0 -35
  432. package/web/front/new-account.js +0 -13
  433. package/web/front/notices.html +0 -69
  434. package/web/front/notices.js +0 -107
  435. package/web/front/run.html +0 -64
  436. package/web/front/run.js +0 -221
  437. package/web/front/solid-oidc-client.js +0 -6
  438. package/web/icons/forum.svg +0 -11
  439. package/web/icons/gateway.svg +0 -14
  440. 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
- }