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,112 +0,0 @@
1
- // connections.js — the accounts held elsewhere: the rail, the connect forms
2
- // for Bluesky and for a fediverse account, and the migration aliases.
3
-
4
- // The connect form lives in the floating window, like every other form here.
5
- // The inbound transfer panel: the aliases live here now, not in the facts.
6
- $('do-transfer-in').addEventListener('click', () => {
7
- solWindow.show('transfer-in-form', 'Transfer an account here');
8
- });
9
-
10
- // The rail: one click hides the actions; on a phone it starts hidden.
11
- function setRail(closed) {
12
- $('rail').classList.toggle('closed', closed);
13
- const t = $('rail-toggle');
14
- t.setAttribute('aria-expanded', String(!closed));
15
- t.querySelector('.rail-arrow').textContent = closed ? '»' : '«';
16
- t.title = closed ? ' Show the action panel' : ' Hide the action panel';
17
- }
18
- $('rail-toggle').addEventListener('click', () => setRail(!$('rail').classList.contains('closed')));
19
- if (matchMedia('(max-width: 47rem)').matches) setRail(true);
20
-
21
- // One way in for both networks, because from the record they are the same
22
- // thing: an account elsewhere whose timeline joins the one you read here.
23
- $('ident-open').addEventListener('click', () => {
24
- closePanels();
25
- solWindow.show('ident-form', 'Connect an identity');
26
- });
27
- $('ident-pick-fedi').addEventListener('click', () => {
28
- solWindow.show('fediacct-form', 'Connect a Fediverse account');
29
- });
30
- $('ident-pick-bsky').addEventListener('click', () => {
31
- solWindow.show('bsky-form', 'Connect a Bluesky account');
32
- });
33
-
34
- let bskyBusy = false;
35
- $('bsky-form').addEventListener('submit', async (ev) => {
36
- ev.preventDefault();
37
- if (bskyBusy) return;
38
- bskyBusy = true;
39
- try {
40
- const r = await write('/atproto/connect', {
41
- service: $('bsky-service').value.trim(),
42
- identifier: $('bsky-identifier').value.trim(),
43
- appPassword: $('bsky-password').value,
44
- }, 'bluesky account connected');
45
- if (r) {
46
- $('bsky-password').value = '';
47
- closePanels();
48
- await load();
49
- }
50
- } finally { bskyBusy = false; }
51
- });
52
- // The sign-in happens at the other server, so this hands the browser over
53
- // rather than taking a password. What comes back lands on /fediacct/callback,
54
- // which this machine answers and nowhere else does.
55
- let fediBusy = false;
56
- $('fediacct-form').addEventListener('submit', async (ev) => {
57
- ev.preventDefault();
58
- if (fediBusy) return;
59
- fediBusy = true;
60
- try {
61
- const r = await write('/fediacct/connect', { host: $('fediacct-host').value.trim() }, '');
62
- if (r?.authorize) location.href = r.authorize;
63
- } finally { fediBusy = false; }
64
- });
65
-
66
- // Disconnecting Bluesky, pausing its feed and choosing its storage all live in
67
- // the account's Options menu now (see optionsMenu). Connecting is still here.
68
- // Adding resolves the old account on the agent side, so what lands in
69
- // alsoKnownAs is its canonical id, not the string typed here.
70
- // The accounts elsewhere this one may receive a Move from — the chips in the
71
- // Transfer-an-account-here panel. Each entry is removable, behind a second
72
- // click: servers still retrying a Move check the list, so removal is not a
73
- // tidy-up.
74
- function renderAliases() {
75
- const box = $('alias-chips');
76
- box.textContent = '';
77
- for (const alias of config.aliases || []) {
78
- let armed = false;
79
- const rm = document.createElement('button');
80
- rm.type = 'button';
81
- rm.className = 'inline danger';
82
- rm.textContent = '✕';
83
- rm.title = ' Remove this alias';
84
- rm.addEventListener('click', async () => {
85
- if (!armed) {
86
- armed = true;
87
- rm.textContent = 'confirm ✕';
88
- rm.title = ' Servers still retrying the Move check this alias — click again to remove it anyway';
89
- return;
90
- }
91
- const r = await write('/alias', { remove: alias, confirm: true }, 'alias removed and the actor republished');
92
- if (r) { config.aliases = r.aliases; render(); }
93
- });
94
- const chip = document.createElement('span');
95
- chip.append(alias, ' ', rm);
96
- box.append(chip, ' ');
97
- }
98
- }
99
-
100
- let aliasBusy = false;
101
- $('alias-add').addEventListener('click', async () => {
102
- if (aliasBusy) return;
103
- const v = $('alias-input').value.trim();
104
- if (!v) { say('enter the old account first — @you@old.server', 'err'); return; }
105
- aliasBusy = true;
106
- try {
107
- const r = await write('/alias', { add: v }, 'alias added and the actor republished');
108
- if (r) { $('alias-input').value = ''; config.aliases = r.aliases; render(); }
109
- } finally { aliasBusy = false; }
110
- });
111
-
112
-
@@ -1,133 +0,0 @@
1
- // gateway.js — the gateway row and its attach and detach forms, the inbox
2
- // prune actions, and the load that starts the page.
3
-
4
- const GATEWAY_CTL = $('gateway-ctl'); // held: it rides into a generated row
5
- const GATEWAY_WORD = $('gateway-word');
6
- const GW_OPEN_ATTACH = $('gateway-open-attach');
7
- const GW_OPEN_DETACH = $('gateway-open-detach');
8
- let gwState = null;
9
- async function refreshGateway() {
10
- const { status, json: g } = await api('/gateway');
11
- if (status !== 200 || !g) return;
12
- gwState = g;
13
- GATEWAY_CTL.hidden = false;
14
- if (g.configured) {
15
- let host = g.url;
16
- try { host = new URL(g.url).host; } catch { /* show it as-is */ }
17
- const frontName = g.frontActor?.match(/\/u\/([^/]+)\/ap\/actor\/?$/)?.[1];
18
- GATEWAY_WORD.textContent = frontName ? `${host} — publishing as @${frontName}@${host}` : host;
19
- GW_OPEN_ATTACH.hidden = true;
20
- GW_OPEN_DETACH.hidden = false;
21
- // The account's standing at the gateway — paused, closed. The browser
22
- // build's agent reports it; a DeviceAgent's does not, and the pause and
23
- // close controls stay hidden.
24
- const st = g.standing;
25
- const known = !!st && (st.status === 200 || st.status === 410);
26
- $('gateway-pause').hidden = !known || !!st.closed || !!st.paused;
27
- $('gateway-resume').hidden = !known || !!st.closed || !st.paused;
28
- $('gateway-close').hidden = !known || !!st.closed;
29
- if (known && st.closed) GATEWAY_WORD.textContent += ' — this address is closed';
30
- else if (known && st.paused) GATEWAY_WORD.textContent += st.pausedBy === 'owner' ? ' — paused by you' : ' — paused';
31
- } else {
32
- GATEWAY_WORD.textContent = '';
33
- GW_OPEN_ATTACH.hidden = false;
34
- GW_OPEN_DETACH.hidden = true;
35
- for (const id of ['gateway-pause', 'gateway-resume', 'gateway-close']) $(id).hidden = true;
36
- }
37
- }
38
- const setPausedAtGateway = async (paused) => {
39
- const r = await write('/gateway/pause', { paused },
40
- paused ? 'paused — posts sent to you are not kept until you resume; follows still arrive'
41
- : 'resumed — posts sent to you are kept again');
42
- if (r) refreshGateway();
43
- };
44
- $('gateway-pause').addEventListener('click', () => setPausedAtGateway(true));
45
- $('gateway-resume').addEventListener('click', () => setPausedAtGateway(false));
46
- const gwShape = () => document.querySelector('input[name=gwShape]:checked')?.value || 'pod';
47
- function gwPreviews() {
48
- $('gw-pod-preview').textContent = config?.address || `@${config?.handle || 'you'}@your.pod`;
49
- try { $('gw-front-host').textContent = new URL($('gw-front').value.trim()).host; }
50
- catch { /* not a URL yet — the placeholder host stands */ }
51
- }
52
- function gwShapeChanged() { gwPreviews(); gwCheck(); }
53
- for (const r of document.querySelectorAll('input[name=gwShape]')) r.addEventListener('change', gwShapeChanged);
54
- // Typing in the blank IS choosing that shape.
55
- $('gw-name').addEventListener('focus', () => {
56
- const r = document.querySelector('input[name=gwShape][value=front]');
57
- if (!r.checked) { r.checked = true; gwShapeChanged(); }
58
- });
59
- GW_OPEN_ATTACH.addEventListener('click', () => {
60
- gwShapeChanged();
61
- solWindow.show('gateway-attach-form', 'Attach to a gateway');
62
- });
63
- GW_OPEN_DETACH.addEventListener('click', () => {
64
- $('gw-detach-fronted').hidden = !gwState?.frontActor;
65
- // The move-in hint exists only in the browser build (see index.html).
66
- const moveHint = $('gw-move-hint-browser');
67
- if (moveHint) moveHint.hidden = !gwState?.frontActor;
68
- $('gw-detach-plain').hidden = !!gwState?.frontActor;
69
- solWindow.show('gateway-detach-form', 'Detach from gateway');
70
- });
71
- // Live availability, asked through the agent (the front answers it without CORS).
72
- let gwTimer = null;
73
- function gwCheck() {
74
- clearTimeout(gwTimer);
75
- const front = $('gw-front').value.trim().replace(/\/+$/, '');
76
- const name = $('gw-name').value.trim().toLowerCase();
77
- $('gw-name-msg').textContent = ''; $('gw-name-msg').className = 'hint';
78
- if (gwShape() !== 'front' || !front || !name) return;
79
- gwTimer = setTimeout(async () => {
80
- const { status, json } = await postJson('/gateway', { action: 'check', front, handle: name });
81
- if (status !== 200 || !json) return;
82
- $('gw-name-msg').textContent = json.available
83
- ? `${name} is free at ${front.replace(/^https?:\/\//, '')}`
84
- : (json.reason || 'that name is taken');
85
- $('gw-name-msg').className = json.available ? 'hint' : 'warn';
86
- }, 300);
87
- }
88
- $('gw-front').addEventListener('input', () => { gwPreviews(); gwCheck(); });
89
- $('gw-name').addEventListener('input', () => { gwPreviews(); gwCheck(); });
90
- $('gw-attach').addEventListener('click', async () => {
91
- const front = $('gw-front').value.trim().replace(/\/+$/, '');
92
- const fronted = gwShape() === 'front';
93
- const name = $('gw-name').value.trim().toLowerCase();
94
- if (fronted && !name) { say('give the handle you want at the gateway', 'err'); return; }
95
- $('gw-attach').disabled = true;
96
- // Pod-based sends no name: the door's label is the agent's business, not
97
- // the user's, and the agent picks a free variant by itself.
98
- const r = await write('/gateway',
99
- { action: 'attach', front, ...(fronted ? { handle: name, fronted: true } : {}) },
100
- fronted ? 'attached — the agent is restarting to publish under the gateway handle; reload in a moment'
101
- : 'attached — your mail now arrives through the gateway, filtered');
102
- $('gw-attach').disabled = false;
103
- if (r) { closePanels(); refreshGateway(); }
104
- });
105
- $('gw-detach').addEventListener('click', async () => {
106
- const fronted = !!gwState?.frontActor;
107
- const r = await write('/gateway', { action: 'forget' },
108
- fronted ? 'detached — the agent is restarting under your pod\'s own name; reload in a moment'
109
- : 'detached — the actor was republished advertising your pod\'s own inbox');
110
- if (r) { closePanels(); refreshGateway(); }
111
- });
112
-
113
- $('inbox-keep').addEventListener('click', () => {
114
- dismissed = true;
115
- $('pane-inbox').hidden = true;
116
- say('leaving it to the agent — it drains oldest first');
117
- });
118
-
119
- $('inbox-prune').addEventListener('click', async () => {
120
- const days = Number($('inbox-before').value);
121
- const before = new Date(Date.now() - days * 86400_000).toISOString();
122
- $('inbox-prune').disabled = true;
123
- say(`discarding content older than ${days} days — this takes a while`);
124
- const r = await write('/inbox/prune', { before }, 'done');
125
- $('inbox-prune').disabled = false;
126
- if (r) {
127
- say(`applied ${r.applied} follow/unfollow/delete, discarded ${r.dropped + r.discarded} posts`
128
- + (r.failed ? `, ${r.failed} failed` : ''));
129
- renderInbox();
130
- }
131
- });
132
-
133
- load();
@@ -1,258 +0,0 @@
1
- // group.js — the group operator's console and the status, joins, review and
2
- // follower controls shared with a person: the pickers with their keyboard
3
- // guard, the request rows, and the refresh of every list.
4
-
5
- // Each control shows the setting it would change, so what it displays IS the
6
- // current state — no separate sentence reporting it.
7
- const MOD = { on: 'moderated', off: 'unmoderated' };
8
- function renderGroupToggles() {
9
- $('joins-mod').value = config.approveJoins ? MOD.on : MOD.off;
10
- $('review-mod').value = config.review ? MOD.on : MOD.off;
11
- }
12
- function renderStatus() {
13
- STATUS_PICK.value = config.quiescedAt ? 'parked' : 'active';
14
- }
15
-
16
- // Parking is reversible — the follow graph is saved first and going back to
17
- // active re-sends a Follow to everyone in it — so the word applies directly,
18
- // like the moderation controls. The result line says what actually happened.
19
- let statusBusy = false;
20
- const setStatus = async (parked) => {
21
- if (statusBusy || parked === !!config.quiescedAt) return;
22
- statusBusy = true;
23
- try {
24
- const r = await write(parked ? '/park' : '/revive', {},
25
- parked ? 'parking — unfollowing everyone and closing the inbox' : 'reviving — re-following the saved graph');
26
- if (r) {
27
- say(parked
28
- ? `parked ${r.quiescedAt}: unfollowed ${r.unfollowed}/${r.following}, inbox closed`
29
- : `revived: inbox open, ${r.refollowed}/${r.of} follow(s) re-sent`);
30
- await load();
31
- }
32
- renderStatus();
33
- } finally { statusBusy = false; }
34
- };
35
-
36
- // One write at a time, because committing an open picker with Enter fires our
37
- // keydown AND the browser's change — the second call must find the first still
38
- // holding the flag, or config, not yet updated by the awaited write, lets a
39
- // duplicate POST through.
40
- let modBusy = false;
41
- const setJoins = async (approve) => {
42
- // Picking the value already in force is not a change: no write, and no
43
- // republish of the actor for a setting that did not move.
44
- if (modBusy || approve === !!config.approveJoins) return;
45
- modBusy = true;
46
- try {
47
- if (await write('/joins', { approve }, approve ? 'join requests will wait' : 'anyone may join')) {
48
- config.approveJoins = approve;
49
- refreshGroup();
50
- }
51
- // Both ways: refused, the control must not keep the value it did not get;
52
- // granted, a blur meanwhile may have repainted it to the old state.
53
- renderGroupToggles();
54
- } finally { modBusy = false; }
55
- };
56
- const setReview = async (on) => {
57
- if (modBusy || on === !!config.review) return;
58
- modBusy = true;
59
- try {
60
- if (await write('/review', { on }, on ? 'posts will be held' : 'posts will be carried at once')) {
61
- config.review = on;
62
- refreshGroup();
63
- }
64
- renderGroupToggles();
65
- } finally { modBusy = false; }
66
- };
67
-
68
- // Arrowing a closed select fires `change` on every keypress, so a keyboard user
69
- // running down the list would apply each value they passed — here that silently
70
- // opens or closes the group. An arrow arms a flag and the change it causes is
71
- // ignored; Enter commits. A mouse never sets the flag, so clicking is unchanged.
72
- // Same guard as the actor picker above, for the same reason.
73
- function onPick(el, apply, repaint = renderGroupToggles) {
74
- let arrowing = false;
75
- el.addEventListener('keydown', (ev) => {
76
- if (['ArrowUp', 'ArrowDown', 'Home', 'End', 'PageUp', 'PageDown'].includes(ev.key)) arrowing = true;
77
- if (ev.key === 'Enter') { arrowing = false; apply(el.value); }
78
- // Escape abandons the armed value; what shows must return to what is.
79
- if (ev.key === 'Escape') { arrowing = false; repaint(); }
80
- });
81
- // Leaving abandons too. Unconditional, because the swallow above already
82
- // consumed the flag — the select may show a state that was never applied,
83
- // and repainting from config costs nothing when it does match.
84
- el.addEventListener('blur', () => { arrowing = false; repaint(); });
85
- el.addEventListener('pointerdown', () => { arrowing = false; });
86
- el.addEventListener('change', () => {
87
- if (!arrowing) apply(el.value);
88
- arrowing = false;
89
- });
90
- }
91
-
92
- onPick($('joins-mod'), (v) => setJoins(v === MOD.on));
93
- onPick($('review-mod'), (v) => setReview(v === MOD.on));
94
- onPick(STATUS_PICK, (v) => setStatus(v === 'parked'), renderStatus);
95
-
96
- // A person's follower gate. Automatic is what a migration wave needs: every
97
- // follower's server re-follows at once, and the waiting queue caps at 500.
98
- let followsBusy = false;
99
- const setAutoAccept = async (auto) => {
100
- if (followsBusy || auto === !!config.autoAcceptFollows) return;
101
- followsBusy = true;
102
- try {
103
- if (await write('/config', { autoAcceptFollows: auto },
104
- auto ? 'new followers are accepted automatically' : 'new followers will wait for you')) {
105
- config.autoAcceptFollows = auto;
106
- }
107
- FOLLOWS_PICK.value = config.autoAcceptFollows ? 'auto' : 'approve';
108
- } finally { followsBusy = false; }
109
- };
110
- onPick(FOLLOWS_PICK, (v) => setAutoAccept(v === 'auto'),
111
- () => { FOLLOWS_PICK.value = config?.autoAcceptFollows ? 'auto' : 'approve'; });
112
-
113
- // One row: what it is, then what can be done to it.
114
- function row(text, sub, actions) {
115
- const li = document.createElement('li');
116
- const span = document.createElement('span');
117
- span.textContent = text;
118
- if (sub) {
119
- const small = document.createElement('div');
120
- small.className = 'muted';
121
- small.textContent = sub;
122
- span.appendChild(small);
123
- }
124
- li.appendChild(span);
125
- for (const [label, run, hint] of actions) {
126
- const b = document.createElement('button');
127
- b.className = 'inline'; // sized like the page's other buttons
128
- b.textContent = label;
129
- // A list of fifteen buttons all reading "Mute" tells a screen-reader user
130
- // browsing by button nothing; name each one for the row it acts on.
131
- b.setAttribute('aria-label', `${label} ${text}`);
132
- // Three leading spaces: the tooltip appears under the pointer, and without
133
- // them the first word sits behind the cursor.
134
- if (hint) b.title = ` ${hint}`;
135
- // Where focus should land afterwards. `b.disabled = true` blurs it to
136
- // <body> at once, and refreshGroup() then wipes and rebuilds the list, so
137
- // without this every mute, eject, admit or refuse dropped a keyboard user
138
- // back to the top of the page.
139
- b.addEventListener('click', async () => {
140
- const list = li.parentNode;
141
- focusAfterRefresh = { listId: list?.id, index: [...(list?.children || [])].indexOf(li), label };
142
- b.disabled = true;
143
- await run();
144
- refreshGroup();
145
- });
146
- li.appendChild(b);
147
- }
148
- return li;
149
- }
150
-
151
- // Set by a row action, consumed by the fill() that replaces that row.
152
- let focusAfterRefresh = null;
153
-
154
- function fill(listId, countId, items, make) {
155
- const ul = $(listId);
156
- ul.textContent = '';
157
- $(countId).textContent = items.length ? `(${items.length})` : '(none)';
158
- for (const it of items) ul.appendChild(make(it));
159
- if (!focusAfterRefresh || focusAfterRefresh.listId !== listId) return;
160
- const { index, label } = focusAfterRefresh;
161
- focusAfterRefresh = null;
162
- // The same action on the row that took this one's place, or the row before it
163
- // when the list just got shorter. Nothing left to land on is the one case
164
- // where the heading is the honest answer.
165
- const rows = [...ul.children];
166
- const li = rows[Math.min(index, rows.length - 1)];
167
- const same = li && [...li.querySelectorAll('button')].find(x => x.textContent === label);
168
- const heading = $(countId).closest('h3');
169
- // A bare <h3> is not focusable, so the "nothing left" fallback would silently
170
- // drop focus to <body> — give it a programmatic-only tab stop first.
171
- if (heading && !heading.hasAttribute('tabindex')) heading.setAttribute('tabindex', '-1');
172
- (same || li?.querySelector('button') || heading)?.focus?.();
173
- }
174
-
175
- // The request rows, shared: a group calls them joins, a person calls them
176
- // follows, and the two do exactly the same thing to exactly the same queue.
177
- function fillRequests(list) {
178
- fill('requests', 'requests-count', list, (r) => row(r.actor, r.at, [
179
- ['Accept', () => write('/admit', { actor: r.actor }, 'accepted'), 'Let them follow you'],
180
- ['Refuse', () => write('/refuse', { actor: r.actor }, 'refused'), 'Turn this down; they may ask again'],
181
- ]));
182
- }
183
-
184
- // The whole queue in one action — the shape a migration wave arrives in.
185
- $('admit-all').addEventListener('click', async () => {
186
- if (await write('/admit', { all: true }, 'accepted everyone waiting')) {
187
- if (config.kind === 'group') refreshGroup(); else refreshRequests();
188
- }
189
- });
190
-
191
- // A person's whole group pane is this one block, and only when it has something
192
- // in it — an empty heading promising a list is what the group console avoids too.
193
- async function refreshRequests() {
194
- const { json } = await api('/requests');
195
- const list = json?.requests || [];
196
- $('pane-group').hidden = !list.length;
197
- $('block-requests').hidden = !list.length;
198
- if (list.length) fillRequests(list);
199
- }
200
-
201
- // What a queued moderation would do, in the words the buttons beside it use.
202
- function describeModeration(e) {
203
- const o = e.activity?.object;
204
- const id = typeof o === 'string' ? o : o?.id || '';
205
- if (e.type === 'Block') return `block ${id}`;
206
- if (e.type === 'Remove') return `eject ${id}`;
207
- if (e.type === 'Delete') return `take down ${id}`;
208
- if (e.type === 'Add') return `add ${id}`;
209
- return id || 'no target named';
210
- }
211
-
212
- async function refreshGroup() {
213
- const [members, requests, pending, announced, modqueue] = await Promise.all(
214
- ['/members', '/requests', '/pending', '/announced', '/modqueue']
215
- .map(p => api(p).then(r => r.json || {})));
216
-
217
- fill('requests', 'requests-count', requests.requests || [], (r) => row(r.actor, r.at, [
218
- ['Admit', () => write('/admit', { actor: r.actor }, 'admitted'), 'Let them in — they become a member'],
219
- ['Refuse', () => write('/refuse', { actor: r.actor }, 'refused'), 'Turn this request down; they may ask again'],
220
- ]));
221
-
222
- fill('pending', 'pending-count', pending.pending || [], (p) => row(p.noteId, `${p.actor} · ${p.at}`, [
223
- ['Carry it', () => write('/approve', { noteId: p.noteId }, 'carried'), 'Announce this post to every member'],
224
- ['Decline', () => write('/decline', { noteId: p.noteId }, 'declined'), 'Do not carry it — the post stays up on its author\u2019s pod'],
225
- ]));
226
-
227
- // Moderation another server asked for: apply it, or turn it down. The route
228
- // answers with the list itself, not an object wrapping one.
229
- const modq = Array.isArray(modqueue) ? modqueue : (modqueue.queue || []);
230
- fill('modqueue', 'modqueue-count', modq, (e) => row(
231
- `${e.type} — ${describeModeration(e)}`, `asked by ${e.moderator} · ${e.at}`, [
232
- ['Carry it out', () => write('/modqueue', { id: e.id, action: 'apply' }, 'applied'),
233
- 'Do what the moderator asked, as if you had asked it'],
234
- ['Turn it down', () => write('/modqueue', { id: e.id, action: 'dismiss' }, 'dismissed'),
235
- 'Leave things as they are and drop the request'],
236
- ]));
237
-
238
- // A queue nothing can arrive in is not an empty list, it is a list that does
239
- // not apply — so the setting has to be on before the heading appears at all.
240
- // This one has no setting: it fills only when a moderator has asked.
241
- $('block-requests').hidden = !(config.approveJoins && (requests.requests || []).length);
242
- $('block-pending').hidden = !(config.review && (pending.pending || []).length);
243
- $('block-modqueue').hidden = !modq.length;
244
-
245
- fill('members', 'members-count', members.members || [], (m) => row(
246
- m.handle || m.actor, m.muted ? 'muted — their posts are not carried' : null,
247
- [
248
- m.muted
249
- ? ['Unmute', () => write('/unmute', { actor: m.actor }, 'unmuted'), 'Carry their posts again']
250
- : ['Mute', () => write('/mute', { actor: m.actor }, 'muted'), 'Stop carrying their posts; they stay a member'],
251
- ['Eject', () => write('/eject', { actor: m.actor }, 'ejected'), 'Remove them and tell their server; also mutes'],
252
- ]));
253
-
254
- fill('announced', 'announced-count', announced.announced || [], (a) => row(
255
- a.noteId, `${a.actor} · ${a.announcedAt}`,
256
- [['Retract', () => write('/retract', { noteId: a.noteId }, 'retracted'), 'Un-say this announcement to everyone it reached']]));
257
- }
258
-