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,206 +0,0 @@
1
- // lease.mjs — single-active-agent coordination. The inbox drain is a
2
- // destructive read and the state store is write-through-cached, so exactly
3
- // one agent may ACT on a pod at a time; later arrivals run as read-only
4
- // viewers. The lease is one JSON doc in ap-state, always read and written
5
- // with FRESH fetches (never through the cached store), renewed while held.
6
- // Best-effort by design: a lost renewal just means the other agent takes
7
- // over at the next expiry.
8
-
9
- import crypto from 'node:crypto';
10
-
11
- // Renewal is the one thing that writes even when nothing is happening: at 30s
12
- // it was 120 PUTs an hour per agent, each taking a write lock on the pod; at
13
- // 90s, 40. Five minutes against a 15-minute TTL leaves two missed renewals of
14
- // headroom and makes it 12. The cost is automatic promotion after a crash
15
- // waiting up to the TTL — a user acting on a second device does not wait,
16
- // since takeover() claims the lease outright.
17
- const TTL_MS = 900_000;
18
- const RENEW_MS = 300_000;
19
- const JITTER = () => 0.85 + Math.random() * 0.3;
20
- // Distinct from null: null is "nobody holds it", this is "we could not ask".
21
- const UNREADABLE = Symbol('lease-unreadable');
22
-
23
- export class Lease {
24
- // `id` names the HOLDER, and acquire() lets a holder reclaim its own lease
25
- // without waiting for the TTL — so whether it is stable decides whether a
26
- // restart is the same agent coming back or a second one arriving. A process
27
- // that is gone for good wants a new one each time; a browser does not, since
28
- // its agent lives in a service worker the browser is free to kill and restart
29
- // whenever it likes. Left to the default, every one of those restarts looked
30
- // like another device and asked the owner to take over their own account —
31
- // which is what switching between the two clients did, because that is a
32
- // navigation and a navigation can take the worker with it. The browser build
33
- // passes one it keeps.
34
- constructor({ url, fetchImpl, log = console.log, id = null }) {
35
- this.url = url;
36
- this.fetchImpl = fetchImpl;
37
- this.log = log;
38
- this.id = id || crypto.randomUUID();
39
- this.stopped = false;
40
- this.timer = null;
41
- this.heldUntil = 0; // wall-clock end of the lease we last wrote
42
- this.denied = null; // why the last acquire() said no: 'unreadable' | 'held'
43
- }
44
-
45
- // null means the lease document is NOT THERE — nobody holds it. UNREADABLE
46
- // means we could not ask, which is a different answer and must not be read as
47
- // "free": acquire() treated both as free, so a pod hiccup during acquire
48
- // could hand a second agent a lease the first still holds, and both would
49
- // drain the same inbox destructively.
50
- async readFresh() {
51
- let res, body;
52
- try {
53
- res = await this.fetchImpl(this.url, { headers: { accept: 'application/json' } });
54
- if (res.status === 404 || res.status === 410) return null;
55
- if (res.status >= 400) return UNREADABLE;
56
- body = await res.text();
57
- } catch { return UNREADABLE; }
58
- this.etag = res.headers?.get?.('etag') || null;
59
- // A 200 carrying garbage — a half-written PUT, a proxy error page served
60
- // as 200 — is not a lease anybody holds, and overwriting it is the repair.
61
- // Calling that UNREADABLE would leave the agent a viewer forever, because
62
- // acquire() refuses to write over what it could not read.
63
- try { return JSON.parse(body); }
64
- catch { this.log('lease document is unparsable — treating it as unheld'); return null; }
65
- }
66
-
67
- // Returns false only for a refused If-Match — that is not an error, it is the
68
- // answer. Anything else throws.
69
- async write(doc, { ifMatch = null } = {}) {
70
- const res = await this.fetchImpl(this.url, {
71
- method: 'PUT',
72
- headers: { 'content-type': 'application/json', ...(ifMatch ? { 'if-match': ifMatch } : {}) },
73
- body: JSON.stringify(doc),
74
- });
75
- if (ifMatch && res.status === 412) return false;
76
- if (res.status >= 400) throw new Error(`lease PUT → ${res.status}`);
77
- this.etag = res.headers?.get?.('etag') || null;
78
- return true;
79
- }
80
-
81
- // True when this agent now holds the lease. Write-then-confirm narrows the
82
- // last-writer-wins race to a small window — acceptable for a human-scale
83
- // "laptop vs phone" situation.
84
- async acquire() {
85
- const cur = await this.readFresh();
86
- // Cannot read it: stay a viewer. A viewer that should have been active is
87
- // an inconvenience; two agents both draining is the destructive one.
88
- if (cur === UNREADABLE) {
89
- this.denied = 'unreadable';
90
- this.log('lease unreadable — staying a viewer'); return false;
91
- }
92
- if (cur && cur.holder !== this.id && Date.now() < cur.expiresAt) { this.denied = 'held'; return false; }
93
- const doc = { holder: this.id, expiresAt: Date.now() + TTL_MS };
94
- if (!await this.write(doc)) { this.denied = 'held'; return false; }
95
- const confirm = await this.readFresh();
96
- if (confirm === UNREADABLE) { this.denied = 'unreadable'; return false; }
97
- if (confirm?.holder !== this.id) { this.denied = 'held'; return false; }
98
- // From the document we WROTE, and only once the confirm agrees. Recomputing
99
- // it here would claim a lease that outlives what the pod's copy grants, by
100
- // however long the PUT and the confirming GET took.
101
- this.heldUntil = doc.expiresAt;
102
- this.denied = null;
103
- return true;
104
- }
105
-
106
- // Renewal checks ownership first: a takeover by another agent must win,
107
- // not be clobbered by a blind rewrite. Losing the lease fires onLost once
108
- // and stops renewing.
109
- async renewOnce() {
110
- // The lease we hold has a wall-clock end. If renewals keep failing — the
111
- // pod unreachable, refusing, or slow — the TTL passes, another agent is
112
- // entitled to take over, and this one went on draining as though nothing
113
- // had happened. Two agents on one destructive inbox read is exactly what
114
- // the lease exists to prevent, so an expiry we could not extend is a loss.
115
- if (this.heldUntil && Date.now() > this.heldUntil) {
116
- this.stopRenewal();
117
- this.etag = null;
118
- this.log('lease expired and could not be renewed — standing down');
119
- this.onLost?.();
120
- return false;
121
- }
122
- const doc = { holder: this.id, expiresAt: Date.now() + TTL_MS };
123
- // The steady path is a single conditional PUT. A 412 means somebody else
124
- // wrote the lease — the only thing the preceding GET was ever there to
125
- // detect — so the read is paid for exactly when it matters instead of every
126
- // 90 seconds forever. No etag (a server that does not return one) falls
127
- // back to read-then-write.
128
- if (this.etag && await this.write(doc, { ifMatch: this.etag })) { this.heldUntil = doc.expiresAt; return true; }
129
- const cur = await this.readFresh();
130
- // Before the ownership test, or the sentinel answers it: a Symbol is truthy
131
- // and its .holder is undefined, so "could not ask" read as "somebody else
132
- // holds it" and stood a perfectly healthy agent down on one 500.
133
- if (cur === UNREADABLE) return true; // keep it; the next tick retries
134
- if (cur && cur.holder !== this.id) {
135
- // clearTimeout alone was a no-op here — the timer being cleared is the one
136
- // whose own callback is running, and the chain re-armed in `finally`
137
- // regardless. Stopping has to outlive this call, so it is a flag.
138
- this.stopRenewal();
139
- // readFresh just cached the NEW holder's ETag. Keeping it would let the
140
- // next conditional PUT succeed and take the lease back, silently, from an
141
- // agent that has already stood down.
142
- this.etag = null;
143
- this.log('lease taken over by another agent');
144
- this.onLost?.();
145
- return false;
146
- }
147
- await this.write(doc);
148
- this.heldUntil = doc.expiresAt;
149
- return true;
150
- }
151
-
152
- // Self-scheduling rather than setInterval, so each agent's renewals drift
153
- // apart instead of several beating in lockstep against one pod.
154
- // Idempotent, like Deliverer.startQueue: startActive reaches this twice on the
155
- // parked path, and two chains against one pod is the doubling this whole file
156
- // is written to avoid.
157
- startRenewal() {
158
- if (this.timer) return;
159
- this.stopped = false; // restartable across demote/takeover
160
- const tick = () => {
161
- if (this.stopped) return;
162
- this.timer = setTimeout(() => {
163
- this.renewOnce()
164
- .catch(e => this.log(`lease renewal: ${e.message}`))
165
- .finally(() => { if (!this.stopped) tick(); });
166
- }, Math.round(RENEW_MS * JITTER()));
167
- this.timer.unref?.();
168
- };
169
- tick();
170
- }
171
-
172
- // Stop renewing WITHOUT giving the lease up — a demoted agent stands down, it
173
- // does not release. Unstopped, the chain outlives demotion for the life of the
174
- // process: ~80 requests an hour from an agent that is no longer acting.
175
- stopRenewal() {
176
- this.stopped = true;
177
- clearTimeout(this.timer);
178
- this.timer = null;
179
- }
180
-
181
- // Forcibly claim the lease (a user acting on a viewer device outranks the
182
- // idle active agent). Write-then-confirm with one retry against the small
183
- // window where the current holder's renewal lands in between.
184
- async takeover() {
185
- for (let attempt = 0; attempt < 2; attempt++) {
186
- const doc = { holder: this.id, expiresAt: Date.now() + TTL_MS };
187
- await this.write(doc);
188
- const confirm = await this.readFresh();
189
- if (confirm !== UNREADABLE && confirm?.holder === this.id) {
190
- this.heldUntil = doc.expiresAt;
191
- return true;
192
- }
193
- }
194
- return false;
195
- }
196
-
197
- // Asked by the destructive path before it acts. The stand-down otherwise
198
- // waits for the next renewal tick, which is up to ~117s away, and the whole
199
- // point of the TTL is that after it another agent may start draining.
200
- stillHeld() { return !this.stopped && (!this.heldUntil || Date.now() < this.heldUntil); }
201
-
202
- async release() {
203
- this.stopRenewal();
204
- await this.write({ holder: this.id, expiresAt: 0 }).catch(() => {});
205
- }
206
- }
@@ -1,82 +0,0 @@
1
- // place.mjs — where on a pod a FediPod account lives, and recording it.
2
- //
3
- // The account's container is always named `fedipod`, inside a container its
4
- // owner chose. The choice is recorded in their public type index as an
5
- // instance of as:Actor (lib/pod/type-index.mjs), and that record is how the
6
- // account is found again. An account made before the choice existed sits at
7
- // `<pod>fedipod/` with no record, and is found there.
8
-
9
- import * as typeIndex from '../pod/type-index.mjs';
10
- import { PodTransport } from '../pod/transport.mjs';
11
- import { rootFromContainer, rootOfActor } from '../pod/location.mjs';
12
- import { DEFAULT_ROOT } from './wire.mjs';
13
-
14
- export { podRootPath } from '../pod/location.mjs';
15
-
16
- /** The container someone typed, as the account's root — or why it cannot be. */
17
- export const chosenRoot = (podBase, typed) => rootFromContainer(podBase, typed, DEFAULT_ROOT);
18
-
19
- /**
20
- * The roots of the accounts this pod's owner has recorded, then the place an
21
- * older account sits without a record. `pod` is a transport acting as the owner.
22
- */
23
- export async function candidateRoots(pod, podBase) {
24
- const recorded = await typeIndex.registeredActors(pod, podBase).catch(() => []);
25
- const roots = recorded.map(a => rootOfActor(podBase, a)).filter(Boolean);
26
- return [...new Set([...roots, DEFAULT_ROOT])];
27
- }
28
-
29
- /**
30
- * The account on this pod: the first candidate whose config `read(root)` finds
31
- * and `accept(config)` takes. Null when there is none.
32
- */
33
- export async function findAccount(pod, podBase, read, accept = () => true) {
34
- for (const root of await candidateRoots(pod, podBase)) {
35
- const config = await read(root).catch(() => null);
36
- if (config && accept(config)) return { root, config };
37
- }
38
- return null;
39
- }
40
-
41
- /**
42
- * Record the account's place in the owner's public type index. With no index,
43
- * one is made only when `create` is true — the person's own yes. Returns what
44
- * was done: 'registered', 'already', or 'no-index'.
45
- */
46
- export async function recordPlace(pod, podBase, actorAtPod, { create = false } = {}) {
47
- let index = await typeIndex.findPublicIndex(pod, podBase);
48
- if (!index) {
49
- if (!create) return 'no-index';
50
- index = await typeIndex.createPublicIndex(pod, podBase);
51
- }
52
- return (await typeIndex.register(pod, index, actorAtPod)) ? 'registered' : 'already';
53
- }
54
-
55
- /** Whether this person's profile names a public type index. */
56
- export async function hasPublicIndex(pod, podBase) {
57
- return !!(await typeIndex.findPublicIndex(pod, podBase).catch(() => null));
58
- }
59
-
60
- /**
61
- * Whether a pod's owner has a public type index, asked as a stranger before any
62
- * credential exists: true, false, or null when the profile could not be read.
63
- */
64
- export async function publicIndexKnown(fetchImpl, podBase, webId = `${podBase}profile/card#me`) {
65
- const pod = new PodTransport({ fetch: fetchImpl }, { webId });
66
- try { return !!(await typeIndex.findPublicIndex(pod, podBase)); } catch { return null; }
67
- }
68
-
69
- /**
70
- * The root for a container typed before the pod's own path was known — a new
71
- * pod: the answer is taken as inside the pod's root, whatever it turns out to be.
72
- */
73
- export function chosenRootInNewPod(podBase, typed) {
74
- const inner = String(typed ?? '').trim().replace(/^\/+/, '');
75
- return chosenRoot(podBase, new URL(podBase).pathname + inner);
76
- }
77
-
78
- /** The error that stops a setup until its person has said yes to a new type index. */
79
- export function needsIndex(detail) {
80
- return Object.assign(new Error(`${detail} FediPod records where your account lives in your public type index, `
81
- + 'so it needs one. Say yes to creating it, or nothing is set up.'), { code: 'needs-index' });
82
- }
@@ -1,105 +0,0 @@
1
- // polls.mjs — the count behind a Question.
2
- //
3
- // A poll travels as a Question whose options live in `oneOf` (pick one) or
4
- // `anyOf` (pick several). A vote is not a special activity: it arrives as an
5
- // ordinary reply carrying the option's `name` and no content. So the fediverse
6
- // carries the votes and the author's server keeps the count — which means when
7
- // the poll is ours, the roster of who chose what is ours to hold.
8
- //
9
- // Everything here is pure. The roster is a plain object the caller stores and
10
- // hands back; the tallies are always DERIVED from it rather than kept beside
11
- // it, so a count cannot drift away from the votes it is meant to summarize.
12
-
13
- import crypto from 'node:crypto';
14
-
15
- /** Where the roster lives in agent state. */
16
- export const VOTES_DOC = 'poll-votes.json';
17
-
18
- // What a poll may be. These are Mastodon's own limits, and they are the ones
19
- // clients read out of the instance document to draw their poll composer, so a
20
- // number we chose differently would be a number the composer then lets someone
21
- // exceed. They live here rather than beside the client API because every way
22
- // in has to honour them: an option is matched BY NAME when the vote comes
23
- // back, so a long title is one something else in the network may truncate and
24
- // hand back unrecognizable.
25
- export const MAX_OPTIONS = 4;
26
- export const MAX_OPTION_CHARS = 50;
27
- export const MIN_SECONDS = 5 * 60;
28
- export const MAX_SECONDS = 2629746; // a month, as Mastodon counts one
29
-
30
- // A voter is recorded as a hash of their actor id rather than the id itself.
31
- // The roster is rewritten on every vote inside a document we serialize whole,
32
- // and a poll that travels would otherwise grow a list of everyone who took
33
- // part. The hash is stable, which is all a duplicate check needs.
34
- export function voterKey(actor) {
35
- return crypto.createHash('sha256').update(String(actor)).digest('hex').slice(0, 16);
36
- }
37
-
38
- /**
39
- * Does this note have the shape of a vote? A name, and no content: that is the
40
- * whole convention. Anything carrying prose is a reply somebody wrote, and
41
- * swallowing it into a tally would lose it.
42
- */
43
- export function isVoteShape(note) {
44
- if (!note?.name || typeof note.name !== 'string') return false;
45
- const text = String(note.content ?? '').replace(/<[^>]*>/gu, '').trim();
46
- return text === '';
47
- }
48
-
49
- /** Which option is this the name of? -1 when it names none of them. */
50
- export function optionIndex(poll, name) {
51
- const opts = poll?.options || [];
52
- return opts.findIndex(o => o.title === String(name));
53
- }
54
-
55
- /** Closed outright, or past its end time. */
56
- export function pollClosed(poll, now = Date.now()) {
57
- if (!poll) return false;
58
- if (poll.closed) return true;
59
- return !!poll.expiresAt && Date.parse(poll.expiresAt) <= now;
60
- }
61
-
62
- /**
63
- * Record one choice. Returns the roster to store and whether anything changed
64
- * — an unchanged roster must not cost a rewrite of the Question, which is the
65
- * one expense a busy poll can run up on the pod.
66
- *
67
- * A single-choice poll takes the first answer and ignores the rest, which is
68
- * how a voter changing their mind is refused rather than counted twice. A
69
- * multiple-choice poll unions the answers, because each choice arrives as its
70
- * own reply.
71
- */
72
- export function addVote(roster, key, index, { multiple = false } = {}) {
73
- const had = roster[key] || [];
74
- if (had.length && !multiple) return { roster, changed: false };
75
- if (had.includes(index)) return { roster, changed: false };
76
- return { roster: { ...roster, [key]: [...had, index] }, changed: true };
77
- }
78
-
79
- /**
80
- * The counts, derived. `votes` is answers given, `voters` is people who gave
81
- * them — the same number until a poll takes several answers each.
82
- */
83
- export function tallyOf(roster, optionCount) {
84
- const counts = new Array(optionCount).fill(0);
85
- let voters = 0;
86
- for (const picks of Object.values(roster || {})) {
87
- let counted = false;
88
- for (const i of picks) {
89
- if (i >= 0 && i < optionCount) { counts[i]++; counted = true; }
90
- }
91
- if (counted) voters++;
92
- }
93
- return { counts, voters, votes: counts.reduce((n, c) => n + c, 0) };
94
- }
95
-
96
- /** The poll record with its counts brought up to date from the roster. */
97
- export function withTally(poll, roster) {
98
- const opts = poll?.options || [];
99
- const { counts, voters } = tallyOf(roster, opts.length);
100
- return {
101
- ...poll,
102
- options: opts.map((o, i) => ({ ...o, votes: counts[i] })),
103
- votersCount: voters,
104
- };
105
- }
@@ -1,104 +0,0 @@
1
- // profile-page.mjs — the human page a browser opens for an actor: who the
2
- // account is, its fields, when it joined, what it pinned, and a remote-follow
3
- // control. The actor document is for servers; this is the address you hand
4
- // to a person. The agent writes it beside the actor (publisher
5
- // publishProfilePage) only when its content changed.
6
-
7
- const HTML_ESCAPES = { '&': '&amp;', '<': '&lt;', '>': '&gt;' };
8
-
9
- // `fields` are the profile's name/value rows, `joined` an ISO date, `pinned`
10
- // the pinned posts as { content (sanitized HTML), published, url }.
11
- export function profilePageHtml({ name, address, summary = null, icon = null, image = null,
12
- fields = [], joined = null, pinned = [], kind = 'person' }) {
13
- const esc = (s) => String(s ?? '').replace(/[&<>"]/g, c => ({ ...HTML_ESCAPES, '"': '&quot;' }[c]));
14
- const what = kind === 'group' ? 'a group' : 'an account';
15
- const month = (iso) => {
16
- const d = new Date(iso);
17
- return Number.isNaN(d.getTime()) ? null
18
- : d.toLocaleDateString('en', { month: 'long', year: 'numeric', timeZone: 'UTC' });
19
- };
20
- const day = (iso) => {
21
- const d = new Date(iso);
22
- return Number.isNaN(d.getTime()) ? '' : d.toISOString().slice(0, 10);
23
- };
24
- const fieldRows = fields.filter(f => f?.name && f?.value !== undefined && f?.value !== null && String(f.value) !== '');
25
- const joinedText = joined ? month(joined) : null;
26
- const pins = pinned.filter(x => x && typeof x.content === 'string');
27
- return `<!doctype html>
28
- <html lang="en">
29
- <head>
30
- <meta charset="utf-8">
31
- <meta name="viewport" content="width=device-width, initial-scale=1">
32
- <title>${esc(name)} (${esc(address)})</title>
33
- <style>
34
- :root { color-scheme: light dark; }
35
- body { font: 112.5%/1.5 system-ui, sans-serif; max-width: 34rem; margin: 3rem auto; padding: 0 1rem; }
36
- img.header { width: 100%; max-height: 12rem; object-fit: cover; border-radius: 1rem; }
37
- img.avatar { width: 6rem; height: 6rem; border-radius: 1rem; object-fit: cover; margin-top: 1rem; }
38
- h1 { margin: .5rem 0 0; }
39
- .address { font-size: 1.1rem; user-select: all; }
40
- dl.fields { display: grid; grid-template-columns: max-content 1fr; gap: .4rem 1rem; margin: 1.5rem 0; }
41
- dl.fields dt { font-weight: 600; }
42
- dl.fields dd { margin: 0; overflow-wrap: anywhere; }
43
- .joined { margin: 1rem 0; }
44
- section.pinned h2 { font-size: 1.1rem; margin: 2rem 0 .5rem; }
45
- article.pin { border: 1px solid #8884; border-radius: 1rem; padding: 1rem 1.2rem; margin: 1rem 0; }
46
- article.pin p:last-child { margin-bottom: 0; }
47
- article.pin .when { font-size: 1rem; }
48
- form { margin-top: 2rem; }
49
- label { display: block; margin-bottom: .3rem; }
50
- input { font: inherit; padding: .5rem; width: 14rem; max-width: 100%; }
51
- button { font: inherit; padding: .5rem 1rem; }
52
- .hint { color: #666; font-size: 1rem; }
53
- .err { color: #b00020; }
54
- .err:empty { display: none; }
55
- @media (prefers-color-scheme: dark) {
56
- .hint { color: #aaa; }
57
- .err { color: #ff8a80; }
58
- }
59
- </style>
60
- </head>
61
- <body>
62
- <main>
63
- ${image ? `<img class="header" src="${esc(image)}" alt="">` : ''}
64
- ${icon ? `<img class="avatar" src="${esc(icon)}" alt="">` : ''}
65
- <h1>${esc(name)}</h1>
66
- <p class="address">${esc(address)}</p>
67
- ${summary ? `<div>${summary}</div>` : ''}
68
- ${fieldRows.length ? `<dl class="fields">
69
- ${fieldRows.map(f => `<dt>${esc(f.name)}</dt><dd>${esc(f.value)}</dd>`).join('\n')}
70
- </dl>` : ''}
71
- ${joinedText ? `<p class="joined">Joined ${esc(joinedText)}</p>` : ''}
72
- ${pins.length ? `<section class="pinned">
73
- <h2>Pinned</h2>
74
- ${pins.map(x => `<article class="pin">
75
- ${x.content}
76
- <p class="when">${x.url ? `<a href="${esc(x.url)}">${esc(day(x.published))}</a>` : esc(day(x.published))}</p>
77
- </article>`).join('\n')}
78
- </section>` : ''}
79
- <p>This is ${what} on the Fediverse. To follow it, paste the address above
80
- into the search box of Mastodon or any Fediverse app — or use the form.</p>
81
- <form id="follow">
82
- <label for="server">your server</label>
83
- <input id="server" type="text" placeholder="mastodon.social" autocomplete="off"
84
- required aria-describedby="follow-err">
85
- <button type="submit">Follow</button>
86
- <p class="err" id="follow-err" role="alert"></p>
87
- </form>
88
- <p class="hint">The form sends you to your own server's follow screen.</p>
89
- </main>
90
- <script>
91
- document.getElementById('follow').addEventListener('submit', (ev) => {
92
- ev.preventDefault();
93
- const s = document.getElementById('server').value.trim().replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '');
94
- const err = document.getElementById('follow-err');
95
- const addr = document.querySelector('.address').textContent.trim();
96
- if (!s) { err.textContent = 'Enter your server, e.g. mastodon.social'; return; }
97
- err.textContent = '';
98
- location.href = 'https://' + s + '/authorize_interaction?uri=' + encodeURIComponent(addr);
99
- });
100
- </script>
101
- </body>
102
- </html>
103
- `;
104
- }
@@ -1,79 +0,0 @@
1
- // proof.mjs — FEP-8b32 object integrity proofs (Data Integrity, eddsa-jcs-2022).
2
- //
3
- // An HTTP signature covers one delivery and says nothing about who wrote what
4
- // was inside it. A proof travels with the activity, so a server that receives
5
- // one of ours second-hand — carried by a group, or forwarded by a follower's
6
- // server — can tell it is ours without asking us.
7
- //
8
- // The RSA `#main-key` and its HTTP signatures are untouched; this is a second
9
- // key alongside, which is what the Data Integrity suites require.
10
-
11
- import crypto from 'node:crypto';
12
- import serialize from 'json-canon';
13
-
14
- // multicodec: an Ed25519 public key is its 32 raw bytes behind 0xed 0x01.
15
- const ED25519_PREFIX = Buffer.from([0xed, 0x01]);
16
- const B58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
17
-
18
- export function base58btc(bytes) {
19
- const b = Buffer.from(bytes);
20
- let n = 0n;
21
- for (const byte of b) n = (n << 8n) + BigInt(byte);
22
- let out = '';
23
- while (n > 0n) { out = B58[Number(n % 58n)] + out; n /= 58n; }
24
- for (const byte of b) { if (byte !== 0) break; out = B58[0] + out; }
25
- return out;
26
- }
27
-
28
- /** The `publicKeyMultibase` form of an Ed25519 public key in PEM. */
29
- export function multibaseEd25519(publicPem) {
30
- const raw = Buffer.from(crypto.createPublicKey(publicPem).export({ format: 'jwk' }).x, 'base64url');
31
- return 'z' + base58btc(Buffer.concat([ED25519_PREFIX, raw]));
32
- }
33
-
34
- /** The signing key, as WebCrypto wants it. */
35
- export function edPrivateKey(privatePem) {
36
- const jwk = crypto.createPrivateKey(privatePem).export({ format: 'jwk' });
37
- return crypto.subtle.importKey('jwk', { ...jwk, key_ops: ['sign'] }, { name: 'Ed25519' }, true, ['sign']);
38
- }
39
-
40
- const sha256 = (s) => crypto.createHash('sha256').update(s).digest();
41
-
42
- // The proof's own terms have to be declared in the document that carries it,
43
- // or a receiver that processes JSON-LD expands the proof away before it ever
44
- // looks at it. Declared BEFORE signing, because the proof covers the document
45
- // exactly as sent, `@context` included.
46
- export const DI_CTX = 'https://w3id.org/security/data-integrity/v1';
47
-
48
- function withProofContext(doc) {
49
- const ctx = doc['@context'];
50
- const list = Array.isArray(ctx) ? ctx : ctx ? [ctx] : [];
51
- if (list.includes(DI_CTX)) return doc;
52
- return { ...doc, '@context': [...list, DI_CTX] };
53
- }
54
-
55
- /**
56
- * Return the activity with a `proof` attached. The proof covers the activity
57
- * exactly as it goes on the wire, minus the proof itself — so it must be the
58
- * last thing added, and nothing may edit the activity afterwards.
59
- *
60
- * Whole seconds on `created`: a verifier re-serializes the timestamp it was
61
- * given, and a fractional one has more than one spelling.
62
- */
63
- export async function attachProof(activity, { privateKey, verificationMethod, created = new Date() } = {}) {
64
- if (!privateKey || !verificationMethod || !activity || typeof activity !== 'object') return activity;
65
- const { proof: _existing, ...bare } = activity;
66
- const doc = withProofContext(bare);
67
- const config = {
68
- '@context': doc['@context'],
69
- type: 'DataIntegrityProof',
70
- cryptosuite: 'eddsa-jcs-2022',
71
- verificationMethod,
72
- proofPurpose: 'assertionMethod',
73
- created: new Date(created).toISOString().replace(/\.\d+Z$/, 'Z'),
74
- };
75
- const digest = Buffer.concat([sha256(serialize(config)), sha256(serialize(doc))]);
76
- const sig = await crypto.subtle.sign('Ed25519', privateKey, digest);
77
- const { '@context': _ctx, ...emitted } = config;
78
- return { ...doc, proof: { ...emitted, proofValue: 'z' + base58btc(Buffer.from(sig)) } };
79
- }