fedipod-server 0.10.0 → 0.12.1

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 (416) hide show
  1. package/README.md +15 -6
  2. package/dist/claims.js +1 -1
  3. package/dist/handler.d.ts +7 -1
  4. package/dist/handler.js +58 -12
  5. package/dist/handler.jsonld +5 -1
  6. package/dist/store-pod.js +18 -4
  7. package/lib/{c2s.mjs → client/c2s.mjs} +8 -3
  8. package/lib/{localapi.mjs → client/localapi.mjs} +2 -2
  9. package/lib/client/masto/accounts.mjs +264 -0
  10. package/lib/client/masto/body.mjs +69 -0
  11. package/lib/client/masto/index.mjs +183 -0
  12. package/lib/client/masto/instance.mjs +104 -0
  13. package/lib/client/masto/media.mjs +133 -0
  14. package/lib/client/masto/oauth.mjs +599 -0
  15. package/lib/client/masto/render.mjs +459 -0
  16. package/lib/client/masto/statuses.mjs +331 -0
  17. package/lib/client/masto/timelines.mjs +316 -0
  18. package/lib/{streaming.mjs → client/streaming.mjs} +1 -1
  19. package/lib/{acctfeed.mjs → connections/acctfeed.mjs} +1 -1
  20. package/lib/{atproto.mjs → connections/atproto.mjs} +15 -16
  21. package/lib/{bskygroup.mjs → connections/bskygroup.mjs} +1 -1
  22. package/lib/{fediacct.mjs → connections/fediacct.mjs} +31 -35
  23. package/lib/{import.mjs → connections/import.mjs} +2 -2
  24. package/lib/{tagfeed.mjs → connections/tagfeed.mjs} +21 -4
  25. package/lib/connections/vault.mjs +114 -0
  26. package/lib/core/as2.mjs +124 -0
  27. package/lib/core/contexts/activitystreams.json +379 -0
  28. package/lib/core/contexts/did-v1.json +57 -0
  29. package/lib/core/contexts/fep-5711.json +36 -0
  30. package/lib/core/contexts/gotosocial.json +86 -0
  31. package/lib/core/contexts/identity-v1.json +152 -0
  32. package/lib/core/contexts/index.mjs +45 -0
  33. package/lib/core/contexts/join-lemmy.json +33 -0
  34. package/lib/core/contexts/joinmastodon.json +28 -0
  35. package/lib/core/contexts/map.json +16 -0
  36. package/lib/core/contexts/miscellany.json +19 -0
  37. package/lib/core/contexts/schemaorg.json +8845 -0
  38. package/lib/core/contexts/security-data-integrity-v1.json +78 -0
  39. package/lib/core/contexts/security-data-integrity-v2.json +81 -0
  40. package/lib/core/contexts/security-multikey-v1.json +35 -0
  41. package/lib/core/contexts/security-v1.json +74 -0
  42. package/lib/core/contexts/webfinger.json +10 -0
  43. package/lib/{deliver.mjs → core/deliver.mjs} +8 -2
  44. package/lib/core/intake/activities.mjs +437 -0
  45. package/lib/core/intake/activity.mjs +240 -0
  46. package/lib/core/intake/channel.mjs +144 -0
  47. package/lib/core/intake/group.mjs +222 -0
  48. package/lib/core/intake/index.mjs +629 -0
  49. package/lib/core/intake/notes.mjs +288 -0
  50. package/lib/core/intake/verify.mjs +141 -0
  51. package/lib/{keys.mjs → core/keys.mjs} +1 -1
  52. package/lib/core/publisher/collections.mjs +229 -0
  53. package/lib/core/publisher/index.mjs +421 -0
  54. package/lib/core/publisher/notes.mjs +188 -0
  55. package/lib/core/publisher/questions.mjs +233 -0
  56. package/lib/core/publisher/restore.mjs +196 -0
  57. package/lib/core/shapes/activitystreams.ttl +129 -0
  58. package/lib/core/shapes/index.mjs +107 -0
  59. package/lib/core/shapes/shapes-text.mjs +13 -0
  60. package/lib/{social.mjs → core/social.mjs} +19 -10
  61. package/lib/{storage.mjs → core/storage.mjs} +2 -2
  62. package/lib/{store.mjs → core/store.mjs} +12 -1
  63. package/lib/{wire.mjs → core/wire.mjs} +48 -67
  64. package/lib/device/admin/index.mjs +13 -0
  65. package/lib/device/admin/origins.mjs +35 -0
  66. package/lib/device/admin/routes/connections.mjs +144 -0
  67. package/lib/device/admin/routes/gateway.mjs +199 -0
  68. package/lib/device/admin/routes/lifecycle.mjs +191 -0
  69. package/lib/device/admin/routes/owner.mjs +322 -0
  70. package/lib/device/admin/routes/setup.mjs +393 -0
  71. package/lib/device/admin/routes/social.mjs +188 -0
  72. package/lib/device/admin/server.mjs +95 -0
  73. package/lib/device/admin/static.mjs +244 -0
  74. package/lib/device/admin/surface.mjs +274 -0
  75. package/lib/device/cli/commands/account.mjs +586 -0
  76. package/lib/device/cli/commands/run.mjs +278 -0
  77. package/lib/device/cli/commands/service.mjs +221 -0
  78. package/lib/device/cli/commands/setup.mjs +410 -0
  79. package/lib/device/cli/commands/state.mjs +559 -0
  80. package/lib/device/cli/context.mjs +288 -0
  81. package/lib/{export-collections.mjs → device/export-collections.mjs} +1 -1
  82. package/lib/{migrate.mjs → device/migrate.mjs} +5 -19
  83. package/lib/device/remote.mjs +54 -0
  84. package/lib/{setup.mjs → device/setup.mjs} +3 -3
  85. package/lib/{update.mjs → device/update.mjs} +1 -1
  86. package/lib/{directory.mjs → gateway/directory.mjs} +1 -1
  87. package/lib/{front-core.mjs → gateway/front-core.mjs} +272 -49
  88. package/lib/{gateway-core.mjs → gateway/gateway-core.mjs} +3 -7
  89. package/lib/{httpsig.mjs → gateway/httpsig.mjs} +34 -3
  90. package/lib/pod/README.md +61 -0
  91. package/lib/pod/actor.mjs +96 -0
  92. package/lib/pod/collection.mjs +77 -0
  93. package/lib/pod/containers.mjs +136 -0
  94. package/lib/pod/discovery.mjs +46 -0
  95. package/lib/pod/featured.mjs +18 -0
  96. package/lib/pod/followers.mjs +24 -0
  97. package/lib/pod/following.mjs +10 -0
  98. package/lib/pod/http.mjs +74 -0
  99. package/lib/pod/inbox.mjs +144 -0
  100. package/lib/{links.mjs → pod/links.mjs} +1 -1
  101. package/lib/pod/media.mjs +27 -0
  102. package/lib/pod/notes.mjs +80 -0
  103. package/lib/pod/notifications.mjs +89 -0
  104. package/lib/pod/outbox.mjs +18 -0
  105. package/lib/pod/package.json +19 -0
  106. package/lib/pod/policy.mjs +58 -0
  107. package/lib/pod/private.mjs +19 -0
  108. package/lib/pod/root.mjs +63 -0
  109. package/lib/pod/state.mjs +39 -0
  110. package/lib/{remote.mjs → pod/transport.mjs} +217 -159
  111. package/lib/pod/urls.mjs +71 -0
  112. package/lib/{embed.mjs → server/embed.mjs} +149 -22
  113. package/lib/shared/links.mjs +9 -0
  114. package/lib/{ua.mjs → shared/ua.mjs} +1 -1
  115. package/package.json +1 -1
  116. package/run-agent.mjs +47 -64
  117. package/web/admin/actors.js +145 -0
  118. package/web/admin/bar.css +9 -13
  119. package/web/admin/bar.js +2 -2
  120. package/web/admin/client/client.js +1 -1
  121. package/web/admin/client/index.html +5 -6
  122. package/web/admin/common.js +23 -0
  123. package/web/admin/connections.js +112 -0
  124. package/web/admin/gateway.js +111 -0
  125. package/web/admin/group.js +258 -0
  126. package/web/admin/index.html +60 -66
  127. package/web/admin/record.js +378 -0
  128. package/web/admin/setup/index.html +3 -1
  129. package/web/admin/setup/setup.js +2 -13
  130. package/web/admin/tokens.css +56 -0
  131. package/web/admin/upkeep.js +170 -0
  132. package/web/app/README.md +77 -0
  133. package/web/app/admin-facade.mjs +549 -0
  134. package/web/app/agent.mjs +490 -0
  135. package/web/app/atproto-browser.mjs +86 -0
  136. package/web/app/boot.mjs +340 -0
  137. package/web/app/deliver-relay.mjs +72 -0
  138. package/web/app/dist/boot.js +34067 -0
  139. package/web/app/dist/boot.js.map +7 -0
  140. package/web/app/dist/sw.js +70014 -0
  141. package/web/app/dist/sw.js.map +7 -0
  142. package/web/app/fediacct-browser.mjs +146 -0
  143. package/web/app/idb-kv.mjs +35 -0
  144. package/web/app/index.html +203 -0
  145. package/web/app/index.html~ +152 -0
  146. package/web/app/keys-browser.mjs +46 -0
  147. package/web/app/keystore.mjs +99 -0
  148. package/web/app/oidc-session.mjs +189 -0
  149. package/web/app/pod-auth.mjs +248 -0
  150. package/web/app/pod-remote.mjs +84 -0
  151. package/web/app/shims/_globals.mjs +2 -0
  152. package/web/app/shims/fedify-sig.mjs +77 -0
  153. package/web/app/shims/node-crypto.mjs +113 -0
  154. package/web/app/shims/node-fs.mjs +17 -0
  155. package/web/app/shims/node-path.mjs +19 -0
  156. package/web/app/shims/node-url.mjs +7 -0
  157. package/web/app/shims/prelude.js +58 -0
  158. package/web/app/shims/safefetch.mjs +58 -0
  159. package/web/app/shims/shapes-text.mjs +8 -0
  160. package/web/app/shims/web-push.mjs +14 -0
  161. package/web/app/signup.mjs +248 -0
  162. package/web/app/site/_headers +9 -0
  163. package/web/app/site/_redirects +21 -0
  164. package/web/app/site/admin/actors.js +145 -0
  165. package/web/app/site/admin/bar.css +60 -0
  166. package/web/app/site/admin/bar.js +61 -0
  167. package/web/app/site/admin/client/client.js +66 -0
  168. package/web/app/site/admin/client/index.html +43 -0
  169. package/web/app/site/admin/common.js +23 -0
  170. package/web/app/site/admin/connections.js +112 -0
  171. package/web/app/site/admin/gateway.js +111 -0
  172. package/web/app/site/admin/group.js +258 -0
  173. package/web/app/site/admin/index.html +562 -0
  174. package/web/app/site/admin/record.js +378 -0
  175. package/web/app/site/admin/setup/index.html +192 -0
  176. package/web/app/site/admin/setup/setup.js +297 -0
  177. package/web/app/site/admin/tokens.css +56 -0
  178. package/web/app/site/admin/upkeep.js +170 -0
  179. package/web/app/site/admin/window.css +55 -0
  180. package/web/app/site/admin/window.js +136 -0
  181. package/web/app/site/app/404.html +40 -0
  182. package/web/app/site/app/_headers +2 -0
  183. package/web/app/site/app/apple-touch-icon.png +0 -0
  184. package/web/app/site/app/assets/ICONS-DIPJeU0C.js +2 -0
  185. package/web/app/site/app/assets/ICONS-DIPJeU0C.js.map +1 -0
  186. package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js +183 -0
  187. package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js.map +1 -0
  188. package/web/app/site/app/assets/Temml-CCjoacWt.woff2 +0 -0
  189. package/web/app/site/app/assets/__vite-browser-external-C7iut881.js +2 -0
  190. package/web/app/site/app/assets/__vite-browser-external-C7iut881.js.map +1 -0
  191. package/web/app/site/app/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  192. package/web/app/site/app/assets/catch-up-O5q5C75Z.png +0 -0
  193. package/web/app/site/app/assets/chunk-aKtaBQYM.js +1 -0
  194. package/web/app/site/app/assets/compose-CFEx7wb4.js +2 -0
  195. package/web/app/site/app/assets/compose-CFEx7wb4.js.map +1 -0
  196. package/web/app/site/app/assets/compose-ZJN5xSb4.js +35 -0
  197. package/web/app/site/app/assets/compose-ZJN5xSb4.js.map +1 -0
  198. package/web/app/site/app/assets/debug.module-YFQgwnIr.js +52 -0
  199. package/web/app/site/app/assets/debug.module-YFQgwnIr.js.map +1 -0
  200. package/web/app/site/app/assets/dom-p1YcJ5Rw.js +2 -0
  201. package/web/app/site/app/assets/dom-p1YcJ5Rw.js.map +1 -0
  202. package/web/app/site/app/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  203. package/web/app/site/app/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  204. package/web/app/site/app/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  205. package/web/app/site/app/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  206. package/web/app/site/app/assets/icons/add-circle-line-WD0mwORD.js +1 -0
  207. package/web/app/site/app/assets/icons/alert-line-CpFUMf9s.js +1 -0
  208. package/web/app/site/app/assets/icons/android-2-line-Bzxnrd3S.js +1 -0
  209. package/web/app/site/app/assets/icons/announcement-line-5nwaJFJm.js +1 -0
  210. package/web/app/site/app/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +1 -0
  211. package/web/app/site/app/assets/icons/arrow-down-line-B8uzXzq6.js +1 -0
  212. package/web/app/site/app/assets/icons/arrow-left-line-CGrGT9C-.js +1 -0
  213. package/web/app/site/app/assets/icons/arrow-right-line-CE-HERfX.js +1 -0
  214. package/web/app/site/app/assets/icons/arrow-to-up-line-CvdQjmcy.js +1 -0
  215. package/web/app/site/app/assets/icons/arrow-up-circle-line-DEparSNF.js +1 -0
  216. package/web/app/site/app/assets/icons/arrow-up-line-C7yBeugc.js +1 -0
  217. package/web/app/site/app/assets/icons/arrows-right-line-BtbXmJGP.js +1 -0
  218. package/web/app/site/app/assets/icons/at-line-CF66Bznk.js +1 -0
  219. package/web/app/site/app/assets/icons/attachment-line-BY8It-l9.js +1 -0
  220. package/web/app/site/app/assets/icons/blockquote-line-BS6pU_hm.js +1 -0
  221. package/web/app/site/app/assets/icons/board-line-C0UWXyA6.js +1 -0
  222. package/web/app/site/app/assets/icons/bookmark-line-1CtX5JLF.js +1 -0
  223. package/web/app/site/app/assets/icons/building-5-line-Dd59MzbO.js +1 -0
  224. package/web/app/site/app/assets/icons/bus-2-line-6_dvA0GG.js +1 -0
  225. package/web/app/site/app/assets/icons/calendar-day-line-Crpl_fmu.js +1 -0
  226. package/web/app/site/app/assets/icons/calendar-month-line-vommts2C.js +1 -0
  227. package/web/app/site/app/assets/icons/calendar-time-add-line-BU_DTlQk.js +1 -0
  228. package/web/app/site/app/assets/icons/camera-line-Cv69MEei.js +1 -0
  229. package/web/app/site/app/assets/icons/celebrate-line-B8DvIoi0.js +1 -0
  230. package/web/app/site/app/assets/icons/chart-bar-line-FK-QOmtF.js +1 -0
  231. package/web/app/site/app/assets/icons/chart-line-line-BGcJAFWw.js +1 -0
  232. package/web/app/site/app/assets/icons/chat-3-line-YFK2-hwz.js +1 -0
  233. package/web/app/site/app/assets/icons/check-circle-line-DsAxFr5-.js +1 -0
  234. package/web/app/site/app/assets/icons/clipboard-line-BBp364gJ.js +1 -0
  235. package/web/app/site/app/assets/icons/close-circle-line-CICdiuGz.js +1 -0
  236. package/web/app/site/app/assets/icons/close-line-DYrFWcjv.js +1 -0
  237. package/web/app/site/app/assets/icons/cloud-line-tiQAbah8.js +1 -0
  238. package/web/app/site/app/assets/icons/code-line-fAl8iiV3.js +1 -0
  239. package/web/app/site/app/assets/icons/comment-2-line-CR3g7WeB.js +1 -0
  240. package/web/app/site/app/assets/icons/copy-2-line-CLW69yF5.js +1 -0
  241. package/web/app/site/app/assets/icons/delete-2-line-B8xmbr4n.js +1 -0
  242. package/web/app/site/app/assets/icons/document-3-line-DBGNhAg2.js +1 -0
  243. package/web/app/site/app/assets/icons/document-line-C7mb9sb7.js +1 -0
  244. package/web/app/site/app/assets/icons/down-line-BGqSSThd.js +1 -0
  245. package/web/app/site/app/assets/icons/edit-4-line-DcgGaEhx.js +1 -0
  246. package/web/app/site/app/assets/icons/emoji-2-line-D8YgnM-y.js +1 -0
  247. package/web/app/site/app/assets/icons/exit-line-Dc07BS-x.js +1 -0
  248. package/web/app/site/app/assets/icons/external-link-line-BvpSkmjO.js +1 -0
  249. package/web/app/site/app/assets/icons/eye-2-line-CiWXx-f_.js +1 -0
  250. package/web/app/site/app/assets/icons/eye-close-line-BgTaSJ4P.js +1 -0
  251. package/web/app/site/app/assets/icons/filter-2-line-B_mR2usy.js +1 -0
  252. package/web/app/site/app/assets/icons/filter-line-C_nm-3cU.js +1 -0
  253. package/web/app/site/app/assets/icons/flag-1-line-C2HB05I0.js +1 -0
  254. package/web/app/site/app/assets/icons/forbid-circle-line-D3LTT9yd.js +1 -0
  255. package/web/app/site/app/assets/icons/formula-line-DLrt2d6j.js +1 -0
  256. package/web/app/site/app/assets/icons/grid-line-CgUmEikf.js +1 -0
  257. package/web/app/site/app/assets/icons/group-line-C5fnX0wU.js +1 -0
  258. package/web/app/site/app/assets/icons/hand-finger-2-line-BGgxbuL9.js +1 -0
  259. package/web/app/site/app/assets/icons/hashtag-line-CyTEEXBb.js +1 -0
  260. package/web/app/site/app/assets/icons/heart-crack-line-BZddbO2C.js +1 -0
  261. package/web/app/site/app/assets/icons/heart-line-CJ7K47J4.js +1 -0
  262. package/web/app/site/app/assets/icons/history-2-line-kWWvFzqv.js +1 -0
  263. package/web/app/site/app/assets/icons/history-line-bGQiwbTH.js +1 -0
  264. package/web/app/site/app/assets/icons/home-3-line-fRU1zeLG.js +1 -0
  265. package/web/app/site/app/assets/icons/information-line-iNk6-sOY.js +1 -0
  266. package/web/app/site/app/assets/icons/keyboard-line-DsCyuQNz.js +1 -0
  267. package/web/app/site/app/assets/icons/layout-4-line-CZkOX52Z.js +1 -0
  268. package/web/app/site/app/assets/icons/layout-5-line-B_znt5wO.js +1 -0
  269. package/web/app/site/app/assets/icons/left-line-eUw0tya4.js +1 -0
  270. package/web/app/site/app/assets/icons/lightning-line-DaKoDdix.js +1 -0
  271. package/web/app/site/app/assets/icons/link-2-line-DCqLmMA7.js +1 -0
  272. package/web/app/site/app/assets/icons/list-check-line-BAxosBh9.js +1 -0
  273. package/web/app/site/app/assets/icons/lock-line-CPoaXeUW.js +1 -0
  274. package/web/app/site/app/assets/icons/mail-line-B0P8aa1L.js +1 -0
  275. package/web/app/site/app/assets/icons/moon-line-CEqNAToP.js +1 -0
  276. package/web/app/site/app/assets/icons/more-1-fill-Dw2e5xab.js +1 -0
  277. package/web/app/site/app/assets/icons/more-3-line-BIiVjYTz.js +1 -0
  278. package/web/app/site/app/assets/icons/notification-line-BZZq2Gtu.js +1 -0
  279. package/web/app/site/app/assets/icons/pencil-line-CKh8-A1v.js +1 -0
  280. package/web/app/site/app/assets/icons/photo-album-line-Bnfg2u40.js +1 -0
  281. package/web/app/site/app/assets/icons/pin-line-DpTolyhs.js +1 -0
  282. package/web/app/site/app/assets/icons/play-fill-B0-G_8lX.js +1 -0
  283. package/web/app/site/app/assets/icons/qrcode-2-line-B6SPCaHn.js +1 -0
  284. package/web/app/site/app/assets/icons/quill-pen-line-B6LUrnCP.js +1 -0
  285. package/web/app/site/app/assets/icons/quote-left-fill-hQZ1cj_6.js +1 -0
  286. package/web/app/site/app/assets/icons/radar-line-CKyroTth.js +1 -0
  287. package/web/app/site/app/assets/icons/react-line-CoIKhhtK.js +1 -0
  288. package/web/app/site/app/assets/icons/refresh-2-line-D0mTF97S.js +1 -0
  289. package/web/app/site/app/assets/icons/right-line-B3KWJaqc.js +1 -0
  290. package/web/app/site/app/assets/icons/rocket-line-4KZl537D.js +1 -0
  291. package/web/app/site/app/assets/icons/round-fill-C69NTrZ0.js +1 -0
  292. package/web/app/site/app/assets/icons/round-line-B9k_1uo-.js +1 -0
  293. package/web/app/site/app/assets/icons/route-line-B28ReHcE.js +1 -0
  294. package/web/app/site/app/assets/icons/rows-4-line-CrBniagM.js +1 -0
  295. package/web/app/site/app/assets/icons/scan-line-DSf74YdP.js +1 -0
  296. package/web/app/site/app/assets/icons/search-2-line-B6geIU1z.js +1 -0
  297. package/web/app/site/app/assets/icons/settings-3-line-azJwK47w.js +1 -0
  298. package/web/app/site/app/assets/icons/settings-6-line-C_5zc5YR.js +1 -0
  299. package/web/app/site/app/assets/icons/share-2-line-D5MxWjrm.js +1 -0
  300. package/web/app/site/app/assets/icons/share-forward-line-Cp61fZzZ.js +1 -0
  301. package/web/app/site/app/assets/icons/sparkles-2-line-DYYyXTaW.js +1 -0
  302. package/web/app/site/app/assets/icons/sparkles-line-C2hY1s6I.js +1 -0
  303. package/web/app/site/app/assets/icons/time-line-BF3eet1v.js +1 -0
  304. package/web/app/site/app/assets/icons/transfer-4-line-oisfvTBw.js +1 -0
  305. package/web/app/site/app/assets/icons/translate-line-BFWol2Ce.js +1 -0
  306. package/web/app/site/app/assets/icons/unlock-line-X_x5vwv3.js +1 -0
  307. package/web/app/site/app/assets/icons/upload-3-line-DgzwUJeW.js +1 -0
  308. package/web/app/site/app/assets/icons/user-4-line-BVSynZCp.js +1 -0
  309. package/web/app/site/app/assets/icons/user-add-2-line-BlGS5Q8q.js +1 -0
  310. package/web/app/site/app/assets/icons/user-add-line-BbXdHG4z.js +1 -0
  311. package/web/app/site/app/assets/icons/user-edit-line-BFAHOV0X.js +1 -0
  312. package/web/app/site/app/assets/icons/user-follow-line-BXux0wSd.js +1 -0
  313. package/web/app/site/app/assets/icons/user-star-line-DdYlpHLU.js +1 -0
  314. package/web/app/site/app/assets/icons/user-warning-line-Cj43j0_E.js +1 -0
  315. package/web/app/site/app/assets/icons/user-x-line-DIDgNlPH.js +1 -0
  316. package/web/app/site/app/assets/icons/volume-line-Bs390KQt.js +1 -0
  317. package/web/app/site/app/assets/icons/volume-mute-line-DzlzIqFZ.js +1 -0
  318. package/web/app/site/app/assets/icons/walk-line-yNXfB-md.js +1 -0
  319. package/web/app/site/app/assets/icons/world-2-line-CenkGIwj.js +1 -0
  320. package/web/app/site/app/assets/icons/zoom-in-line-CaENUphf.js +1 -0
  321. package/web/app/site/app/assets/icons/zoom-out-line-BDNk0EWo.js +1 -0
  322. package/web/app/site/app/assets/instances-BNPptnn-.json +333 -0
  323. package/web/app/site/app/assets/locales/ar-SA-CfheRbCu.js +1 -0
  324. package/web/app/site/app/assets/locales/ca-ES-DHRW86Tz.js +1 -0
  325. package/web/app/site/app/assets/locales/cs-CZ-CKoqEuA1.js +1 -0
  326. package/web/app/site/app/assets/locales/de-DE-ByRPMRFy.js +1 -0
  327. package/web/app/site/app/assets/locales/eo-UY-CPC7wLCS.js +1 -0
  328. package/web/app/site/app/assets/locales/es-ES-dJa0Fd9t.js +1 -0
  329. package/web/app/site/app/assets/locales/eu-ES-C3djlPR2.js +1 -0
  330. package/web/app/site/app/assets/locales/fa-IR-DTT490Ck.js +1 -0
  331. package/web/app/site/app/assets/locales/fi-FI-B23MCPuz.js +1 -0
  332. package/web/app/site/app/assets/locales/fr-FR-BcaQsO5C.js +1 -0
  333. package/web/app/site/app/assets/locales/gl-ES-BR-CVKBN.js +1 -0
  334. package/web/app/site/app/assets/locales/he-IL-CfheRbCu.js +1 -0
  335. package/web/app/site/app/assets/locales/hu-HU-CfheRbCu.js +1 -0
  336. package/web/app/site/app/assets/locales/it-IT-C1136UFV.js +1 -0
  337. package/web/app/site/app/assets/locales/ja-JP-CRZmRAhD.js +1 -0
  338. package/web/app/site/app/assets/locales/kab-Cz8xH_TT.js +1 -0
  339. package/web/app/site/app/assets/locales/ko-KR-CG9UiY4o.js +1 -0
  340. package/web/app/site/app/assets/locales/lt-LT-CJcI6sHZ.js +1 -0
  341. package/web/app/site/app/assets/locales/nb-NO-CfheRbCu.js +1 -0
  342. package/web/app/site/app/assets/locales/nl-NL-DxN4Yc1t.js +1 -0
  343. package/web/app/site/app/assets/locales/oc-FR-CfheRbCu.js +1 -0
  344. package/web/app/site/app/assets/locales/pl-PL-DkwGXf6P.js +1 -0
  345. package/web/app/site/app/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +1 -0
  346. package/web/app/site/app/assets/locales/pt-BR-nGL0HdFS.js +1 -0
  347. package/web/app/site/app/assets/locales/pt-PT-Cp17aj2z.js +1 -0
  348. package/web/app/site/app/assets/locales/ru-RU-0Y8cIoaK.js +1 -0
  349. package/web/app/site/app/assets/locales/th-TH-CfheRbCu.js +1 -0
  350. package/web/app/site/app/assets/locales/tok-CfheRbCu.js +1 -0
  351. package/web/app/site/app/assets/locales/tr-TR-CfheRbCu.js +1 -0
  352. package/web/app/site/app/assets/locales/uk-UA-C5E3dFBf.js +1 -0
  353. package/web/app/site/app/assets/locales/zh-CN-BHJi7_et.js +1 -0
  354. package/web/app/site/app/assets/locales/zh-TW-CfheRbCu.js +1 -0
  355. package/web/app/site/app/assets/main-BdqNbG-a.js +26 -0
  356. package/web/app/site/app/assets/main-BdqNbG-a.js.map +1 -0
  357. package/web/app/site/app/assets/mock-home-DGhR__He.js +2 -0
  358. package/web/app/site/app/assets/mock-home-DGhR__He.js.map +1 -0
  359. package/web/app/site/app/assets/multi-column-ROck0NVt.jpg +0 -0
  360. package/web/app/site/app/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  361. package/web/app/site/app/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  362. package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js +2 -0
  363. package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js.map +1 -0
  364. package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js +20 -0
  365. package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js.map +1 -0
  366. package/web/app/site/app/assets/style-B1iLPTPR.css +2 -0
  367. package/web/app/site/app/assets/temml-DuyIQMei.js +97 -0
  368. package/web/app/site/app/assets/temml-DuyIQMei.js.map +1 -0
  369. package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js +2 -0
  370. package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js.map +1 -0
  371. package/web/app/site/app/assets/year-in-posts-B2lywqdx.png +0 -0
  372. package/web/app/site/app/assets/year-in-posts-DJv_star.js +3 -0
  373. package/web/app/site/app/assets/year-in-posts-DJv_star.js.map +1 -0
  374. package/web/app/site/app/compose/index.html +20 -0
  375. package/web/app/site/app/favicon.ico +0 -0
  376. package/web/app/site/app/index.html +73 -0
  377. package/web/app/site/app/logo-192.png +0 -0
  378. package/web/app/site/app/logo-512.png +0 -0
  379. package/web/app/site/app/logo-badge-72.png +0 -0
  380. package/web/app/site/app/logo-maskable-512.png +0 -0
  381. package/web/app/site/app/logo-monochrome-512.png +0 -0
  382. package/web/app/site/app/logo-monochrome-maskable-512.png +0 -0
  383. package/web/app/site/app/manifest.webmanifest +1 -0
  384. package/web/app/site/app/og-image-2.jpg +0 -0
  385. package/web/app/site/app/og-image.png +0 -0
  386. package/web/app/site/app/robots.txt +2 -0
  387. package/web/app/site/app/version.json +1 -0
  388. package/web/app/site/boot.js +34067 -0
  389. package/web/app/site/index.html +203 -0
  390. package/web/app/site/sw.js +70014 -0
  391. package/web/app/sw-src.mjs +220 -0
  392. package/web/front/#new-account.html# +43 -0
  393. package/web/front/admin.html +1 -81
  394. package/web/front/admin.js +85 -0
  395. package/web/front/new-account.html +1 -9
  396. package/web/front/new-account.js +13 -0
  397. package/web/front/run.html +1 -92
  398. package/web/front/run.js +107 -0
  399. package/lib/admin.mjs +0 -1913
  400. package/lib/intake.mjs +0 -1764
  401. package/lib/mastoapi.mjs +0 -1947
  402. package/lib/podrdf.mjs +0 -132
  403. package/lib/publisher.mjs +0 -1263
  404. package/web/admin/admin.js +0 -1114
  405. /package/lib/{oidc-auth.mjs → client/oidc-auth.mjs} +0 -0
  406. /package/lib/{webpush.mjs → client/webpush.mjs} +0 -0
  407. /package/lib/{bskyfeed.mjs → connections/bskyfeed.mjs} +0 -0
  408. /package/lib/{lease.mjs → core/lease.mjs} +0 -0
  409. /package/lib/{polls.mjs → core/polls.mjs} +0 -0
  410. /package/lib/{proof.mjs → core/proof.mjs} +0 -0
  411. /package/lib/{account.mjs → device/account.mjs} +0 -0
  412. /package/lib/{certs.mjs → device/certs.mjs} +0 -0
  413. /package/lib/{home.mjs → device/home.mjs} +0 -0
  414. /package/lib/{ports.mjs → device/ports.mjs} +0 -0
  415. /package/lib/{guard.mjs → shared/guard.mjs} +0 -0
  416. /package/lib/{safefetch.mjs → shared/safefetch.mjs} +0 -0
@@ -0,0 +1,340 @@
1
+ // boot.mjs — the page side of the browser agent.
2
+ //
3
+ // Login is a Solid-OIDC redirect: the page sends you to your pod's login, and on
4
+ // return the session is kept in IndexedDB (a non-extractable DPoP key + refresh
5
+ // token, see oidc-session.mjs). The agent runs in the service worker, which reads
6
+ // that same session from IndexedDB. So a returning visit restores silently, and
7
+ // a new browser signs in with one redirect.
8
+ //
9
+ // New account: fedipodSignup(answers) — create the account, then redirect.
10
+ // Returning: fedipodSignin({ issuer }) — redirect to the pod's login.
11
+ // On every load: fedipodOnLoad() — finish a redirect, or restore, then boot.
12
+ import { signUp, handleProblem, AP_ROOT } from './signup.mjs';
13
+ import * as podActor from '../../lib/pod/actor.mjs';
14
+ import * as podState from '../../lib/pod/state.mjs';
15
+ import { BrowserRemotePod } from './pod-remote.mjs';
16
+ import { beginLogin, completeLogin, getSession, signOut } from './oidc-session.mjs';
17
+ import { unwrapKeys, isKeyEnvelope } from './keystore.mjs';
18
+ import { kvPut } from './idb-kv.mjs';
19
+ import { keyCacheKey } from './keys-browser.mjs';
20
+
21
+ const REDIRECT = `${location.origin}/`; // the app root doubles as the OIDC callback
22
+
23
+ async function bootWorker({ reset = false } = {}) {
24
+ const reg = await navigator.serviceWorker.register('/sw.js', { type: 'module' });
25
+ await navigator.serviceWorker.ready;
26
+ if (!navigator.serviceWorker.controller) {
27
+ await new Promise((r) => navigator.serviceWorker.addEventListener('controllerchange', r, { once: true }));
28
+ }
29
+ const worker = reg.active || navigator.serviceWorker.controller;
30
+ // A fresh login means a possibly different identity, and the worker may still
31
+ // be holding the last one. Make it let go before it is asked to boot, and
32
+ // wait for the acknowledgement — posting and racing on would let the boot
33
+ // land while the old agent was still installed.
34
+ if (reset) {
35
+ await new Promise((res) => {
36
+ const on = (e) => {
37
+ if (e.data?.type !== 'reset-done') return;
38
+ navigator.serviceWorker.removeEventListener('message', on);
39
+ res();
40
+ };
41
+ navigator.serviceWorker.addEventListener('message', on);
42
+ worker.postMessage({ type: 'reset' });
43
+ setTimeout(res, 2000); // an older worker will not answer; boot anyway
44
+ });
45
+ }
46
+ const booted = new Promise((res, rej) => {
47
+ const on = (e) => {
48
+ if (e.data?.type === 'booted') { navigator.serviceWorker.removeEventListener('message', on); res(); }
49
+ if (e.data?.type === 'boot-error') { navigator.serviceWorker.removeEventListener('message', on); const err = new Error(e.data.error); err.detail = e.data.stack || ''; err.code = e.data.code || null; rej(err); }
50
+ };
51
+ navigator.serviceWorker.addEventListener('message', on);
52
+ });
53
+ worker.postMessage({ type: 'boot', frontOrigin: location.origin });
54
+ await booted;
55
+ }
56
+
57
+ // A browser that has signed in but has never opened this account's signing key.
58
+ //
59
+ // The key on the pod is wrapped under the account password (signup.mjs), which
60
+ // is what keeps the pod's host from being able to sign as you. Opening it needs
61
+ // the password once per browser; after that the opened copy lives in this
62
+ // origin's IndexedDB and the worker boots from it with nothing to ask.
63
+ //
64
+ // The unwrap happens HERE, in the page, and not in the worker: the worker boots
65
+ // itself whenever the browser restarts it, with nobody present to type anything.
66
+ window.fedipodUnlock = async (password) => {
67
+ if (!password) throw new Error('Enter your account password.');
68
+ const session = await getSession();
69
+ if (!session) throw new Error('Sign in first.');
70
+ // The config on the pod says where this account's state lives; the key sits
71
+ // beside it. Both are read with the session, as the owner.
72
+ const podFromWebId = new URL(session.webId).origin + '/';
73
+ const state = `${podFromWebId}${AP_ROOT}ap-state/`;
74
+ // Through the transport rather than the bare session: this is a pod read
75
+ // like any other, and going round it skipped the retry ladder that exists
76
+ // because the pod host throttles bursts.
77
+ const remote = new BrowserRemotePod(session, { webId: session.webId, role: 'signup', log: () => {} });
78
+ const urls = { state };
79
+ const [cfg, doc] = await Promise.all([
80
+ podState.readConfig(remote, urls), podState.readWrappedKeys(remote, urls),
81
+ ]);
82
+ if (!cfg || !doc) throw new Error(`could not read this account's config and key under ${state}`);
83
+ if (!isKeyEnvelope(doc)) throw new Error('this account\'s key is not locked — nothing to unlock');
84
+ const rec = await unwrapKeys(doc, password); // throws 'wrong password'
85
+ const actorUrl = `${cfg.remotePod}${cfg.root || AP_ROOT}ap/actor`;
86
+ await kvPut(keyCacheKey(actorUrl), rec);
87
+ await bootWorker();
88
+ };
89
+
90
+ // New account: create the account, pod, key, config and gateway attach (this
91
+ // needs the password once), then redirect to the pod's login to establish the
92
+ // durable session. The agent boots on return, reading config + key from the pod.
93
+ window.fedipodSignup = async ({ onStep, ...answers }) => {
94
+ await signUp(answers, { onStep, frontOrigin: location.origin });
95
+ const { authorizationUrl } = await beginLogin({ issuer: answers.issuer, redirectUri: REDIRECT });
96
+ location.href = authorizationUrl;
97
+ };
98
+
99
+ // Returning / new browser: sign-in takes the full Fediverse address, @you@yourpod.
100
+ // The host part IS the pod (a FediPod address lives at a host root), so the pod is
101
+ // `https://<host>/`. The pod's actor says where to sign in; we fall back to the
102
+ // host's parent domain when the pod does not answer. Everything else — the account
103
+ // config and the key — is read from the pod on return.
104
+ export function parseAddress(input) {
105
+ let s = String(input || '').trim();
106
+ if (s.startsWith('@')) s = s.slice(1); // @you@yourpod → you@yourpod
107
+ const at = s.indexOf('@');
108
+ if (at < 1) return null; // needs a handle and a host
109
+ const handle = s.slice(0, at).toLowerCase();
110
+ const host = s.slice(at + 1).toLowerCase().replace(/\/+$/, '');
111
+ if (!handle || !host || !host.includes('.')) return null;
112
+ return { handle, host };
113
+ }
114
+ async function issuerForPod(pod) {
115
+ // The pod's actor says where a client signs in (oauthAuthorizationEndpoint's
116
+ // origin); failing that, the account provider is the pod host's parent domain.
117
+ try {
118
+ const authz = await podActor.readIssuer(`${pod}${AP_ROOT}ap/actor`);
119
+ if (authz) return new URL(authz).origin;
120
+ } catch { /* fall through */ }
121
+ const host = new URL(pod).host;
122
+ const parent = host.split('.').slice(1).join('.');
123
+ return `https://${parent || host}`;
124
+ }
125
+ window.fedipodSignin = async ({ address }) => {
126
+ const parsed = parseAddress(address);
127
+ if (!parsed) throw new Error('Enter your address as @you@yourpod (for example @alice@alice.solidcommunity.net).');
128
+ const bad = handleProblem(parsed.handle);
129
+ if (bad) throw new Error(bad);
130
+ const pod = `https://${parsed.host}/`;
131
+ const issuer = await issuerForPod(pod);
132
+ const { authorizationUrl } = await beginLogin({ issuer, redirectUri: REDIRECT });
133
+ location.href = authorizationUrl;
134
+ };
135
+
136
+ // Called on every page load. Returns 'signed-in' | 'restored' | 'anonymous'.
137
+ window.fedipodOnLoad = async () => {
138
+ if (new URLSearchParams(location.search).get('code')) {
139
+ await completeLogin({ currentUrl: location.href });
140
+ history.replaceState({}, '', REDIRECT);
141
+ await bootWorker({ reset: true }); // this may be a different account
142
+ return 'signed-in';
143
+ }
144
+ if (await getSession()) { await bootWorker(); return 'restored'; }
145
+ return 'anonymous';
146
+ };
147
+
148
+ window.fedipodSignOut = async () => {
149
+ await signOut();
150
+ // The client (Phanpy) keeps its own account and token in this origin's
151
+ // localStorage, which outlives the OIDC sign-out and would silently sign you
152
+ // back in as the same identity — so signing out has to clear it too.
153
+ try { localStorage.clear(); sessionStorage.clear(); } catch { /* storage blocked */ }
154
+ // The worker holds a connection to the connected-accounts store, so stop it
155
+ // before deleting that store. Browser-stored Bluesky/fediverse keys are wiped
156
+ // here; pod-stored ones stay on the pod and return when you sign in again.
157
+ const reg = await navigator.serviceWorker.getRegistration(); await reg?.unregister();
158
+ await new Promise((res) => {
159
+ let req; try { req = indexedDB.deleteDatabase('fedipod-accounts'); } catch { res(); return; }
160
+ req.onsuccess = req.onerror = req.onblocked = () => res();
161
+ });
162
+ };
163
+ window.fedipodHandleProblem = handleProblem;
164
+
165
+ // --- page UI wiring (runs only in the page, never the worker) ---
166
+ //
167
+ // Three ways in, matching the front page:
168
+ // signed-in / restored session → straight to the feed (/app/)
169
+ // have an account, new browser → type @you@yourpod, redirect to your pod's login
170
+ // no account → "create an account" reveals the register form
171
+ if (typeof document !== 'undefined') (async () => {
172
+ const $ = (id) => document.getElementById(id);
173
+ const params = new URLSearchParams(location.search);
174
+
175
+ // --- unlock: this browser's first use of an account whose key is locked ---
176
+ //
177
+ // Wired FIRST, before anything that can leave this function early. Showing the
178
+ // pane is one of those early exits, so registering the handler further down
179
+ // meant the pane appeared with a dead button: the one browser that needs it is
180
+ // the only one that never got it.
181
+ const doUnlock = async () => {
182
+ $('unlock-error').textContent = '';
183
+ const btn = $('unlock-go'); btn.disabled = true;
184
+ try {
185
+ await window.fedipodUnlock($('unlock-password').value);
186
+ $('unlock-password').value = '';
187
+ location.href = '/admin/client/';
188
+ } catch (err) {
189
+ // 'wrong password' is what unwrapKeys throws on a failed AES-GCM auth,
190
+ // which is the only way to tell a typo from a real problem.
191
+ $('unlock-error').textContent = err.message || String(err);
192
+ btn.disabled = false;
193
+ $('unlock-password').select();
194
+ }
195
+ };
196
+ $('unlock-go')?.addEventListener('click', doUnlock);
197
+ $('unlock-password')?.addEventListener('keydown', (e) => { if (e.key === 'Enter') doUnlock(); });
198
+
199
+
200
+ // The client shell's bar returns here for two things, and neither continues
201
+ // into the client: `?signout` clears the FediPod session, the client's stored
202
+ // account, and the worker on this browser; `?add` comes back to reach a
203
+ // different pod (signing in anew replaces the session). Both show the sign-in
204
+ // page.
205
+ if (params.has('signout') || params.has('add')) {
206
+ if (params.has('signout')) { try { await window.fedipodSignOut(); } catch { /* nothing to tear down */ } }
207
+ history.replaceState({}, '', '/');
208
+ $('loading').hidden = true;
209
+ $('landing').hidden = false;
210
+ } else {
211
+ let state = 'anonymous';
212
+ try { state = await window.fedipodOnLoad(); }
213
+ catch (e) {
214
+ // A browser that has never opened this account's key is not a failure —
215
+ // it is the ordinary state of a NEW browser, and the answer is a password
216
+ // field rather than a stack trace. Everything else falls through below.
217
+ if (e.code === 'key-password-needed') {
218
+ $('loading').hidden = true; $('hero').hidden = true; $('landing').hidden = true;
219
+ $('brand').hidden = false; $('unlock').hidden = false;
220
+ $('unlock-password').focus();
221
+ return;
222
+ }
223
+ // We logged in (or had a saved session) but the agent could not start.
224
+ // Show exactly why — including where it threw — instead of silently
225
+ // dropping back to the landing, which hides every real failure.
226
+ console.error(e);
227
+ $('loading').hidden = true; $('hero').hidden = true; $('landing').hidden = true;
228
+ $('brand').hidden = false; $('running').hidden = false;
229
+ $('running-title').textContent = 'Signed in, but the agent could not start';
230
+ $('run-error').style.whiteSpace = 'pre-wrap';
231
+ $('run-error').textContent = (e.message || String(e)) + (e.detail ? `\n\n${e.detail}` : '');
232
+ $('run-actions').hidden = false;
233
+ $('run-retry').textContent = 'Reload'; $('run-retry').addEventListener('click', () => location.reload());
234
+ $('run-back').textContent = 'Sign out'; $('run-back').addEventListener('click', async () => { try { await window.fedipodSignOut(); } catch {} location.href = '/'; });
235
+ return;
236
+ }
237
+ // Into the client shell: the FediPod bar over the framed client (the same
238
+ // web/admin surface as local). Its own script signs the framed client in on
239
+ // a fresh session and forwards deep links on a returning one, so both a
240
+ // fresh sign-in and a restored session land in the same place.
241
+ if (state === 'signed-in' || state === 'restored') { location.href = '/admin/client/'; return; }
242
+
243
+ // Anonymous: show the landing (sign-in address + create-account); form stays hidden.
244
+ $('loading').hidden = true;
245
+ $('landing').hidden = false;
246
+ }
247
+
248
+ // Views. The register form stands alone: the marketing hero and the sign-in
249
+ // landing give way to the short brand, so only "FediPod" and the form show.
250
+ const showLanding = () => { $('pane-form').hidden = true; $('running').hidden = true; $('brand').hidden = true; $('hero').hidden = false; $('landing').hidden = false; };
251
+ const showForm = () => { $('hero').hidden = true; $('landing').hidden = true; $('brand').hidden = false; $('pane-form').hidden = false; goStep(1); };
252
+
253
+
254
+ // --- sign in: the full @you@yourpod address → redirect to your pod's login ---
255
+ const doSignin = async () => {
256
+ $('signin-error').textContent = '';
257
+ try { await window.fedipodSignin({ address: $('signin-address').value }); }
258
+ catch (err) { $('signin-error').textContent = err.message; }
259
+ };
260
+ $('signin').addEventListener('click', doSignin);
261
+ $('signin-address').addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); doSignin(); } });
262
+
263
+ // --- register form: two screens, pod first then Fediverse identity ---
264
+ const f = () => $('form').elements;
265
+ // The pod provider is a free-text URL; default and normalise to a scheme.
266
+ const providerUrl = () => { let v = f().provider.value.trim(); if (!v) v = 'https://solidcommunity.net'; if (!/^https?:\/\//i.test(v)) v = 'https://' + v; return v; };
267
+ const providerHost = () => { try { return new URL(providerUrl()).host; } catch { return ''; } };
268
+ // The pod is always a subdomain of the provider, so both new and existing pods
269
+ // resolve to https://<subdomain>.<provider-host>/ and carry a host-root address.
270
+ const podHostOf = () => { const sub = f().podName.value.trim().toLowerCase(); const ph = providerHost(); return (sub && ph) ? `${sub}.${ph}` : ''; };
271
+ const answers = () => {
272
+ const mode = f().mode.value;
273
+ const a = { mode, handle: f().handle.value.trim().toLowerCase(), email: f().email.value.trim(),
274
+ password: f().password.value, issuer: providerUrl() };
275
+ if (mode === 'new') a.podName = f().podName.value.trim().toLowerCase();
276
+ else a.pod = `https://${podHostOf()}/`;
277
+ return a;
278
+ };
279
+ const previewAddr = () => {
280
+ const handle = f().handle.value.trim().toLowerCase(); const ph = podHostOf();
281
+ $('preview').textContent = (handle && ph) ? `@${handle}@${ph}` : '@…@…';
282
+ };
283
+ for (const el of $('form').elements) el.addEventListener('input', previewAddr);
284
+
285
+ // Step machine: one screen at a time, each gated by its own validation.
286
+ const STEP_IDS = ['step-1', 'step-2'];
287
+ const FOCUS = { 1: 'provider', 2: 'handle' };
288
+ const goStep = (n) => {
289
+ STEP_IDS.forEach((id, i) => { $(id).hidden = i !== n - 1; });
290
+ $('err-1').textContent = ''; $('form-error').textContent = '';
291
+ if (n === 2) previewAddr();
292
+ if (FOCUS[n]) $(FOCUS[n]).focus();
293
+ };
294
+ const validateStep1 = () => {
295
+ if (!providerHost()) return 'A valid pod provider URL is required.';
296
+ const sub = f().podName.value.trim().toLowerCase();
297
+ if (!sub) return 'A pod username/subdomain is required.';
298
+ const sp = window.fedipodHandleProblem(sub);
299
+ if (sp) return `Pod username: ${sp}`;
300
+ if (!f().email.value.trim()) return 'A pod email is required.';
301
+ if (!f().password.value) return 'A pod password is required.';
302
+ return null;
303
+ };
304
+ const validateStep2 = () => {
305
+ const hp = window.fedipodHandleProblem(f().handle.value.trim().toLowerCase());
306
+ if (hp) return `Fediverse handle: ${hp}`;
307
+ return null;
308
+ };
309
+ $('create').addEventListener('click', showForm);
310
+ $('cancel').addEventListener('click', () => { showLanding(); goStep(1); });
311
+ $('to-2').addEventListener('click', () => { const e = validateStep1(); if (e) { $('err-1').textContent = e; return; } goStep(2); });
312
+ $('back-1').addEventListener('click', () => goStep(1));
313
+
314
+ // From the setup screen back to the form (a failed run) or the landing.
315
+ const backToForm = (errMsg) => {
316
+ $('running').hidden = true; $('brand').hidden = false; $('pane-form').hidden = false;
317
+ goStep(1);
318
+ if (errMsg) $('err-1').textContent = errMsg; // remind them what failed
319
+ };
320
+ $('run-retry').addEventListener('click', () => backToForm($('run-error').textContent));
321
+ $('run-back').addEventListener('click', () => { showLanding(); goStep(1); });
322
+
323
+ const LABELS = { account: 'Creating your account and pod', credential: 'Preparing this browser', keys: 'Making your signing key', gateway: 'Connecting your mail door' };
324
+ $('form').addEventListener('submit', async (e) => {
325
+ e.preventDefault(); $('form-error').textContent = '';
326
+ const e1 = validateStep1(); if (e1) { $('form-error').textContent = e1; goStep(1); $('err-1').textContent = e1; return; }
327
+ const e2 = validateStep2(); if (e2) { $('form-error').textContent = e2; goStep(2); return; }
328
+ const a = answers();
329
+ $('pane-form').hidden = true; $('running').hidden = false;
330
+ $('running-title').textContent = 'Setting up…'; $('run-error').textContent = ''; $('run-actions').hidden = true;
331
+ const steps = $('steps'); steps.textContent = ''; const mark = {};
332
+ const onStep = (k, st) => { if (!mark[k]) { const li = document.createElement('li'); steps.appendChild(li); mark[k] = li; } mark[k].textContent = (st === 'ok' ? '✓ ' : st === 'running' ? '… ' : '') + (LABELS[k] || k); };
333
+ try { await window.fedipodSignup({ ...a, onStep }); }
334
+ catch (err) {
335
+ $('running-title').textContent = 'Setup did not finish';
336
+ $('run-error').textContent = err.message || String(err);
337
+ $('run-actions').hidden = false; // the way out of the failure screen
338
+ }
339
+ });
340
+ })();
@@ -0,0 +1,72 @@
1
+ // deliver-relay.mjs — delivery for the in-browser agent.
2
+ //
3
+ // The Node deliverer opens a signed connection straight to the remote inbox. A
4
+ // browser cannot: it may not set Date or Host, and it is blocked from posting
5
+ // cross-origin to a fediverse server anyway. So it signs with the agent's key
6
+ // and hands the signed request to the relay (POST /api/relay on the front),
7
+ // which sends it verbatim. Everything else — the retry queue, the cooling
8
+ // hosts, the dead-letter, FEP-8b32 proofs — is the Node Deliverer, reused by
9
+ // overriding only the one network step.
10
+ import { Deliverer } from '../../lib/core/deliver.mjs';
11
+ import { sign } from './shims/fedify-sig.mjs';
12
+
13
+ export class RelayDeliverer extends Deliverer {
14
+ constructor(opts) {
15
+ super(opts);
16
+ this.relayUrl = opts.relayUrl; // <front>/api/relay
17
+ this.handle = opts.handle;
18
+ this.sessionFetch = opts.sessionFetch; // the DPoP session's fetch, to authenticate to the relay
19
+ }
20
+
21
+ // Same contract as Deliverer.signedFetch, DEFAULT INCLUDED: an init with no
22
+ // method is a read. The Node one builds a `Request`, whose default is GET, and
23
+ // every caller that means POST says so (deliver.mjs deliverNow). Defaulting to
24
+ // POST here sent every dereference — an actor, a note, a reply — to the relay
25
+ // as a delivery, and the relay returns a body only for a GET. So the agent
26
+ // never saw the document it asked for: a Follow from anyone new was rejected
27
+ // with "actor fetch failed", and nothing needing a lookup could be ingested.
28
+ async signedFetch(url, init = {}) {
29
+ const body = typeof init.body === 'string' ? init.body : (init.body ? new TextDecoder().decode(init.body) : '');
30
+ const s = await sign({ url, method: init.method || 'GET', headers: init.headers || {}, body }, this.rsaPrivate, this.keyId);
31
+ const relayReq = {
32
+ url: s.url, method: s.method, body,
33
+ headers: {
34
+ date: s.headers.date, digest: s.headers.digest,
35
+ 'content-type': s.headers['content-type'], signature: s.headers.signature,
36
+ },
37
+ };
38
+ let res;
39
+ try {
40
+ res = await this.sessionFetch(this.relayUrl, {
41
+ method: 'POST', headers: { 'content-type': 'application/json' },
42
+ body: JSON.stringify({ handle: this.handle, requests: [relayReq] }),
43
+ });
44
+ } catch (e) { const err = new Error(`relay unreachable: ${e.message}`); err.status = 0; throw err; }
45
+ if (res.status >= 400) { const err = new Error(`relay ${res.status}`); err.status = 502; throw err; }
46
+ const out = await res.json().catch(() => ({}));
47
+ const r0 = (out.results && out.results[0]) || {};
48
+ const status = r0.status || 0;
49
+ if (status === 0) { const err = new Error(r0.error || 'relay could not send'); err.status = 502; throw err; }
50
+ if (status >= 400) {
51
+ const err = new Error(`${init.method || 'POST'} ${url} → ${status}`); err.status = status;
52
+ // The receiving server's own answer to "when should I try again", carried
53
+ // through the relay (lib/front-core.mjs). Spelled `retryAfterMs`, which is
54
+ // what the delivery queue reads (lib/deliver.mjs) — the queue's ladder is
55
+ // the fallback for when there is none, not a replacement for being told.
56
+ if (r0.retryAfter) {
57
+ const secs = Number(r0.retryAfter);
58
+ const ms = Number.isFinite(secs) ? Math.max(secs, 1) * 1000
59
+ : Math.max(Date.parse(r0.retryAfter) - Date.now(), 1000);
60
+ if (Number.isFinite(ms)) err.retryAfterMs = Math.min(ms, 24 * 60 * 60_000);
61
+ }
62
+ throw err;
63
+ }
64
+ // A read (GET) comes back with the fetched body; a delivery (POST) has none.
65
+ // Hand back a real Response either way so the caller can read a remote
66
+ // object — the topical (tag) feed dereferences every note it mirrors, and
67
+ // intake.fetchAP reads that body off this Response.
68
+ const headers = new Headers();
69
+ if (r0.contentType) headers.set('content-type', r0.contentType);
70
+ return new Response(typeof r0.body === 'string' ? r0.body : null, { status, headers });
71
+ }
72
+ }