fedipod-server 0.21.0 → 0.28.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 (332) hide show
  1. package/README.md +5 -4
  2. package/dist/claims.d.ts +3 -1
  3. package/dist/claims.js +20 -7
  4. package/dist/components/context.jsonld +6 -0
  5. package/dist/handler.d.ts +29 -1
  6. package/dist/handler.js +89 -4
  7. package/dist/handler.jsonld +36 -1
  8. package/lib/client/c2s.mjs +100 -3
  9. package/lib/client/masto/accounts.mjs +44 -4
  10. package/lib/client/masto/body.mjs +90 -3
  11. package/lib/client/masto/index.mjs +5 -1
  12. package/lib/client/masto/instance.mjs +11 -3
  13. package/lib/client/masto/media.mjs +3 -57
  14. package/lib/client/masto/oauth.mjs +66 -9
  15. package/lib/client/masto/render.mjs +62 -5
  16. package/lib/client/masto/statuses.mjs +25 -1
  17. package/lib/client/masto/timelines.mjs +128 -6
  18. package/lib/connections/tagfeed.mjs +42 -5
  19. package/lib/core/as2.mjs +36 -5
  20. package/lib/core/contexts/index.mjs +14 -0
  21. package/lib/core/contexts/litepub-0.1.json +60 -0
  22. package/lib/core/contexts/map.json +3 -1
  23. package/lib/core/contexts/quotes.json +18 -0
  24. package/lib/core/graphview.mjs +41 -3
  25. package/lib/core/intake/activities.mjs +136 -2
  26. package/lib/core/intake/activity.mjs +79 -1
  27. package/lib/core/intake/group.mjs +37 -3
  28. package/lib/core/intake/index.mjs +32 -2
  29. package/lib/core/intake/notes.mjs +60 -3
  30. package/lib/core/lease.mjs +12 -2
  31. package/lib/core/profile-page.mjs +104 -0
  32. package/lib/core/publisher/collections.mjs +6 -1
  33. package/lib/core/publisher/index.mjs +35 -17
  34. package/lib/core/publisher/notes.mjs +104 -3
  35. package/lib/core/shapes/activitystreams.ttl +28 -0
  36. package/lib/core/shapes/index.mjs +26 -1
  37. package/lib/core/wire-quotes.mjs +71 -0
  38. package/lib/core/wire.mjs +59 -103
  39. package/lib/device/admin/routes/lifecycle.mjs +9 -0
  40. package/lib/device/admin/static.mjs +4 -1
  41. package/lib/device/admin/surface.mjs +26 -2
  42. package/lib/device/certs.mjs +18 -2
  43. package/lib/device/cli/commands/setup.mjs +6 -2
  44. package/lib/device/cli/commands/state.mjs +3 -1
  45. package/lib/device/gateway-move.mjs +58 -0
  46. package/lib/device/migrate.mjs +3 -1
  47. package/lib/device/setup.mjs +67 -7
  48. package/lib/gateway/front-core.mjs +307 -36
  49. package/lib/gateway/gateway-core.mjs +15 -6
  50. package/lib/gateway/notices.mjs +84 -0
  51. package/lib/gateway/quiet.mjs +184 -0
  52. package/lib/pod/root.mjs +6 -3
  53. package/lib/pod/state.mjs +8 -7
  54. package/lib/pod/transport.mjs +5 -1
  55. package/lib/session/README.md +123 -0
  56. package/lib/session/demo.html +94 -0
  57. package/lib/session/fedi-account.mjs +346 -0
  58. package/lib/session/fedi-login.mjs +123 -0
  59. package/lib/session/oidc-session.mjs +233 -0
  60. package/lib/session/package.json +41 -0
  61. package/package.json +1 -1
  62. package/run-agent.mjs +28 -0
  63. package/vendor/gate.cjs +6 -3
  64. package/web/admin/bar.css +66 -0
  65. package/web/admin/bar.js +38 -0
  66. package/web/admin/client/index.html +31 -3
  67. package/web/admin/client-pick.js +79 -0
  68. package/web/admin/gateway.js +22 -0
  69. package/web/admin/index.html +54 -13
  70. package/web/admin/notices-bar.js +90 -0
  71. package/web/admin/oauth-signin.mjs +37 -0
  72. package/web/admin/record.js +13 -1
  73. package/web/admin/setup/index.html +31 -3
  74. package/web/admin/tokens.css +15 -3
  75. package/web/admin/upkeep.js +4 -5
  76. package/web/app/README.md +14 -10
  77. package/web/app/admin-facade.mjs +51 -12
  78. package/web/app/agent.mjs +157 -19
  79. package/web/app/authorize_interaction.html +56 -0
  80. package/web/app/authorize_interaction.js +69 -0
  81. package/web/app/boot.mjs +233 -95
  82. package/web/app/dist/boot.js +477 -569
  83. package/web/app/dist/boot.js.map +4 -4
  84. package/web/app/dist/sw.js +1861 -532
  85. package/web/app/dist/sw.js.map +4 -4
  86. package/web/app/gateway-move.mjs +60 -0
  87. package/web/app/index.html +40 -49
  88. package/web/app/keys-browser.mjs +12 -15
  89. package/web/app/keystore.mjs +19 -36
  90. package/web/app/oidc-session.mjs +5 -188
  91. package/web/app/signup.mjs +172 -132
  92. package/web/app/sw-src.mjs +1 -1
  93. package/web/app/update.js +66 -0
  94. package/web/front/admin.html +5 -3
  95. package/web/front/new-account.html +2 -2
  96. package/web/front/notices.html +69 -0
  97. package/web/front/notices.js +107 -0
  98. package/web/front/run.html +15 -8
  99. package/web/front/run.js +124 -36
  100. package/web/icons/forum.svg +11 -0
  101. package/web/icons/gateway.svg +14 -0
  102. package/web/icons/local.svg +11 -0
  103. package/web/app/index.html~ +0 -152
  104. package/web/app/site/_headers +0 -9
  105. package/web/app/site/_redirects +0 -22
  106. package/web/app/site/admin/actors.js +0 -145
  107. package/web/app/site/admin/bar.css +0 -60
  108. package/web/app/site/admin/bar.js +0 -61
  109. package/web/app/site/admin/client/client.js +0 -66
  110. package/web/app/site/admin/client/index.html +0 -43
  111. package/web/app/site/admin/common.js +0 -23
  112. package/web/app/site/admin/connections.js +0 -112
  113. package/web/app/site/admin/gateway.js +0 -111
  114. package/web/app/site/admin/group.js +0 -258
  115. package/web/app/site/admin/index.html +0 -564
  116. package/web/app/site/admin/record.js +0 -378
  117. package/web/app/site/admin/setup/index.html +0 -215
  118. package/web/app/site/admin/setup/setup.js +0 -326
  119. package/web/app/site/admin/tokens.css +0 -56
  120. package/web/app/site/admin/upkeep.js +0 -178
  121. package/web/app/site/admin/window.css +0 -55
  122. package/web/app/site/admin/window.js +0 -136
  123. package/web/app/site/app/404.html +0 -40
  124. package/web/app/site/app/_headers +0 -2
  125. package/web/app/site/app/apple-touch-icon.png +0 -0
  126. package/web/app/site/app/assets/ICONS-DIPJeU0C.js +0 -2
  127. package/web/app/site/app/assets/ICONS-DIPJeU0C.js.map +0 -1
  128. package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js +0 -183
  129. package/web/app/site/app/assets/ScheduledAtField-D-Abfo4J.js.map +0 -1
  130. package/web/app/site/app/assets/Temml-CCjoacWt.woff2 +0 -0
  131. package/web/app/site/app/assets/__vite-browser-external-C7iut881.js +0 -2
  132. package/web/app/site/app/assets/__vite-browser-external-C7iut881.js.map +0 -1
  133. package/web/app/site/app/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
  134. package/web/app/site/app/assets/catch-up-O5q5C75Z.png +0 -0
  135. package/web/app/site/app/assets/chunk-aKtaBQYM.js +0 -1
  136. package/web/app/site/app/assets/compose-CFEx7wb4.js +0 -2
  137. package/web/app/site/app/assets/compose-CFEx7wb4.js.map +0 -1
  138. package/web/app/site/app/assets/compose-ZJN5xSb4.js +0 -35
  139. package/web/app/site/app/assets/compose-ZJN5xSb4.js.map +0 -1
  140. package/web/app/site/app/assets/debug.module-YFQgwnIr.js +0 -52
  141. package/web/app/site/app/assets/debug.module-YFQgwnIr.js.map +0 -1
  142. package/web/app/site/app/assets/dom-p1YcJ5Rw.js +0 -2
  143. package/web/app/site/app/assets/dom-p1YcJ5Rw.js.map +0 -1
  144. package/web/app/site/app/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
  145. package/web/app/site/app/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
  146. package/web/app/site/app/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
  147. package/web/app/site/app/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
  148. package/web/app/site/app/assets/icons/add-circle-line-WD0mwORD.js +0 -1
  149. package/web/app/site/app/assets/icons/alert-line-CpFUMf9s.js +0 -1
  150. package/web/app/site/app/assets/icons/android-2-line-Bzxnrd3S.js +0 -1
  151. package/web/app/site/app/assets/icons/announcement-line-5nwaJFJm.js +0 -1
  152. package/web/app/site/app/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +0 -1
  153. package/web/app/site/app/assets/icons/arrow-down-line-B8uzXzq6.js +0 -1
  154. package/web/app/site/app/assets/icons/arrow-left-line-CGrGT9C-.js +0 -1
  155. package/web/app/site/app/assets/icons/arrow-right-line-CE-HERfX.js +0 -1
  156. package/web/app/site/app/assets/icons/arrow-to-up-line-CvdQjmcy.js +0 -1
  157. package/web/app/site/app/assets/icons/arrow-up-circle-line-DEparSNF.js +0 -1
  158. package/web/app/site/app/assets/icons/arrow-up-line-C7yBeugc.js +0 -1
  159. package/web/app/site/app/assets/icons/arrows-right-line-BtbXmJGP.js +0 -1
  160. package/web/app/site/app/assets/icons/at-line-CF66Bznk.js +0 -1
  161. package/web/app/site/app/assets/icons/attachment-line-BY8It-l9.js +0 -1
  162. package/web/app/site/app/assets/icons/blockquote-line-BS6pU_hm.js +0 -1
  163. package/web/app/site/app/assets/icons/board-line-C0UWXyA6.js +0 -1
  164. package/web/app/site/app/assets/icons/bookmark-line-1CtX5JLF.js +0 -1
  165. package/web/app/site/app/assets/icons/building-5-line-Dd59MzbO.js +0 -1
  166. package/web/app/site/app/assets/icons/bus-2-line-6_dvA0GG.js +0 -1
  167. package/web/app/site/app/assets/icons/calendar-day-line-Crpl_fmu.js +0 -1
  168. package/web/app/site/app/assets/icons/calendar-month-line-vommts2C.js +0 -1
  169. package/web/app/site/app/assets/icons/calendar-time-add-line-BU_DTlQk.js +0 -1
  170. package/web/app/site/app/assets/icons/camera-line-Cv69MEei.js +0 -1
  171. package/web/app/site/app/assets/icons/celebrate-line-B8DvIoi0.js +0 -1
  172. package/web/app/site/app/assets/icons/chart-bar-line-FK-QOmtF.js +0 -1
  173. package/web/app/site/app/assets/icons/chart-line-line-BGcJAFWw.js +0 -1
  174. package/web/app/site/app/assets/icons/chat-3-line-YFK2-hwz.js +0 -1
  175. package/web/app/site/app/assets/icons/check-circle-line-DsAxFr5-.js +0 -1
  176. package/web/app/site/app/assets/icons/clipboard-line-BBp364gJ.js +0 -1
  177. package/web/app/site/app/assets/icons/close-circle-line-CICdiuGz.js +0 -1
  178. package/web/app/site/app/assets/icons/close-line-DYrFWcjv.js +0 -1
  179. package/web/app/site/app/assets/icons/cloud-line-tiQAbah8.js +0 -1
  180. package/web/app/site/app/assets/icons/code-line-fAl8iiV3.js +0 -1
  181. package/web/app/site/app/assets/icons/comment-2-line-CR3g7WeB.js +0 -1
  182. package/web/app/site/app/assets/icons/copy-2-line-CLW69yF5.js +0 -1
  183. package/web/app/site/app/assets/icons/delete-2-line-B8xmbr4n.js +0 -1
  184. package/web/app/site/app/assets/icons/document-3-line-DBGNhAg2.js +0 -1
  185. package/web/app/site/app/assets/icons/document-line-C7mb9sb7.js +0 -1
  186. package/web/app/site/app/assets/icons/down-line-BGqSSThd.js +0 -1
  187. package/web/app/site/app/assets/icons/edit-4-line-DcgGaEhx.js +0 -1
  188. package/web/app/site/app/assets/icons/emoji-2-line-D8YgnM-y.js +0 -1
  189. package/web/app/site/app/assets/icons/exit-line-Dc07BS-x.js +0 -1
  190. package/web/app/site/app/assets/icons/external-link-line-BvpSkmjO.js +0 -1
  191. package/web/app/site/app/assets/icons/eye-2-line-CiWXx-f_.js +0 -1
  192. package/web/app/site/app/assets/icons/eye-close-line-BgTaSJ4P.js +0 -1
  193. package/web/app/site/app/assets/icons/filter-2-line-B_mR2usy.js +0 -1
  194. package/web/app/site/app/assets/icons/filter-line-C_nm-3cU.js +0 -1
  195. package/web/app/site/app/assets/icons/flag-1-line-C2HB05I0.js +0 -1
  196. package/web/app/site/app/assets/icons/forbid-circle-line-D3LTT9yd.js +0 -1
  197. package/web/app/site/app/assets/icons/formula-line-DLrt2d6j.js +0 -1
  198. package/web/app/site/app/assets/icons/grid-line-CgUmEikf.js +0 -1
  199. package/web/app/site/app/assets/icons/group-line-C5fnX0wU.js +0 -1
  200. package/web/app/site/app/assets/icons/hand-finger-2-line-BGgxbuL9.js +0 -1
  201. package/web/app/site/app/assets/icons/hashtag-line-CyTEEXBb.js +0 -1
  202. package/web/app/site/app/assets/icons/heart-crack-line-BZddbO2C.js +0 -1
  203. package/web/app/site/app/assets/icons/heart-line-CJ7K47J4.js +0 -1
  204. package/web/app/site/app/assets/icons/history-2-line-kWWvFzqv.js +0 -1
  205. package/web/app/site/app/assets/icons/history-line-bGQiwbTH.js +0 -1
  206. package/web/app/site/app/assets/icons/home-3-line-fRU1zeLG.js +0 -1
  207. package/web/app/site/app/assets/icons/information-line-iNk6-sOY.js +0 -1
  208. package/web/app/site/app/assets/icons/keyboard-line-DsCyuQNz.js +0 -1
  209. package/web/app/site/app/assets/icons/layout-4-line-CZkOX52Z.js +0 -1
  210. package/web/app/site/app/assets/icons/layout-5-line-B_znt5wO.js +0 -1
  211. package/web/app/site/app/assets/icons/left-line-eUw0tya4.js +0 -1
  212. package/web/app/site/app/assets/icons/lightning-line-DaKoDdix.js +0 -1
  213. package/web/app/site/app/assets/icons/link-2-line-DCqLmMA7.js +0 -1
  214. package/web/app/site/app/assets/icons/list-check-line-BAxosBh9.js +0 -1
  215. package/web/app/site/app/assets/icons/lock-line-CPoaXeUW.js +0 -1
  216. package/web/app/site/app/assets/icons/mail-line-B0P8aa1L.js +0 -1
  217. package/web/app/site/app/assets/icons/moon-line-CEqNAToP.js +0 -1
  218. package/web/app/site/app/assets/icons/more-1-fill-Dw2e5xab.js +0 -1
  219. package/web/app/site/app/assets/icons/more-3-line-BIiVjYTz.js +0 -1
  220. package/web/app/site/app/assets/icons/notification-line-BZZq2Gtu.js +0 -1
  221. package/web/app/site/app/assets/icons/pencil-line-CKh8-A1v.js +0 -1
  222. package/web/app/site/app/assets/icons/photo-album-line-Bnfg2u40.js +0 -1
  223. package/web/app/site/app/assets/icons/pin-line-DpTolyhs.js +0 -1
  224. package/web/app/site/app/assets/icons/play-fill-B0-G_8lX.js +0 -1
  225. package/web/app/site/app/assets/icons/qrcode-2-line-B6SPCaHn.js +0 -1
  226. package/web/app/site/app/assets/icons/quill-pen-line-B6LUrnCP.js +0 -1
  227. package/web/app/site/app/assets/icons/quote-left-fill-hQZ1cj_6.js +0 -1
  228. package/web/app/site/app/assets/icons/radar-line-CKyroTth.js +0 -1
  229. package/web/app/site/app/assets/icons/react-line-CoIKhhtK.js +0 -1
  230. package/web/app/site/app/assets/icons/refresh-2-line-D0mTF97S.js +0 -1
  231. package/web/app/site/app/assets/icons/right-line-B3KWJaqc.js +0 -1
  232. package/web/app/site/app/assets/icons/rocket-line-4KZl537D.js +0 -1
  233. package/web/app/site/app/assets/icons/round-fill-C69NTrZ0.js +0 -1
  234. package/web/app/site/app/assets/icons/round-line-B9k_1uo-.js +0 -1
  235. package/web/app/site/app/assets/icons/route-line-B28ReHcE.js +0 -1
  236. package/web/app/site/app/assets/icons/rows-4-line-CrBniagM.js +0 -1
  237. package/web/app/site/app/assets/icons/scan-line-DSf74YdP.js +0 -1
  238. package/web/app/site/app/assets/icons/search-2-line-B6geIU1z.js +0 -1
  239. package/web/app/site/app/assets/icons/settings-3-line-azJwK47w.js +0 -1
  240. package/web/app/site/app/assets/icons/settings-6-line-C_5zc5YR.js +0 -1
  241. package/web/app/site/app/assets/icons/share-2-line-D5MxWjrm.js +0 -1
  242. package/web/app/site/app/assets/icons/share-forward-line-Cp61fZzZ.js +0 -1
  243. package/web/app/site/app/assets/icons/sparkles-2-line-DYYyXTaW.js +0 -1
  244. package/web/app/site/app/assets/icons/sparkles-line-C2hY1s6I.js +0 -1
  245. package/web/app/site/app/assets/icons/time-line-BF3eet1v.js +0 -1
  246. package/web/app/site/app/assets/icons/transfer-4-line-oisfvTBw.js +0 -1
  247. package/web/app/site/app/assets/icons/translate-line-BFWol2Ce.js +0 -1
  248. package/web/app/site/app/assets/icons/unlock-line-X_x5vwv3.js +0 -1
  249. package/web/app/site/app/assets/icons/upload-3-line-DgzwUJeW.js +0 -1
  250. package/web/app/site/app/assets/icons/user-4-line-BVSynZCp.js +0 -1
  251. package/web/app/site/app/assets/icons/user-add-2-line-BlGS5Q8q.js +0 -1
  252. package/web/app/site/app/assets/icons/user-add-line-BbXdHG4z.js +0 -1
  253. package/web/app/site/app/assets/icons/user-edit-line-BFAHOV0X.js +0 -1
  254. package/web/app/site/app/assets/icons/user-follow-line-BXux0wSd.js +0 -1
  255. package/web/app/site/app/assets/icons/user-star-line-DdYlpHLU.js +0 -1
  256. package/web/app/site/app/assets/icons/user-warning-line-Cj43j0_E.js +0 -1
  257. package/web/app/site/app/assets/icons/user-x-line-DIDgNlPH.js +0 -1
  258. package/web/app/site/app/assets/icons/volume-line-Bs390KQt.js +0 -1
  259. package/web/app/site/app/assets/icons/volume-mute-line-DzlzIqFZ.js +0 -1
  260. package/web/app/site/app/assets/icons/walk-line-yNXfB-md.js +0 -1
  261. package/web/app/site/app/assets/icons/world-2-line-CenkGIwj.js +0 -1
  262. package/web/app/site/app/assets/icons/zoom-in-line-CaENUphf.js +0 -1
  263. package/web/app/site/app/assets/icons/zoom-out-line-BDNk0EWo.js +0 -1
  264. package/web/app/site/app/assets/instances-BNPptnn-.json +0 -333
  265. package/web/app/site/app/assets/locales/ar-SA-CfheRbCu.js +0 -1
  266. package/web/app/site/app/assets/locales/ca-ES-DHRW86Tz.js +0 -1
  267. package/web/app/site/app/assets/locales/cs-CZ-CKoqEuA1.js +0 -1
  268. package/web/app/site/app/assets/locales/de-DE-ByRPMRFy.js +0 -1
  269. package/web/app/site/app/assets/locales/eo-UY-CPC7wLCS.js +0 -1
  270. package/web/app/site/app/assets/locales/es-ES-dJa0Fd9t.js +0 -1
  271. package/web/app/site/app/assets/locales/eu-ES-C3djlPR2.js +0 -1
  272. package/web/app/site/app/assets/locales/fa-IR-DTT490Ck.js +0 -1
  273. package/web/app/site/app/assets/locales/fi-FI-B23MCPuz.js +0 -1
  274. package/web/app/site/app/assets/locales/fr-FR-BcaQsO5C.js +0 -1
  275. package/web/app/site/app/assets/locales/gl-ES-BR-CVKBN.js +0 -1
  276. package/web/app/site/app/assets/locales/he-IL-CfheRbCu.js +0 -1
  277. package/web/app/site/app/assets/locales/hu-HU-CfheRbCu.js +0 -1
  278. package/web/app/site/app/assets/locales/it-IT-C1136UFV.js +0 -1
  279. package/web/app/site/app/assets/locales/ja-JP-CRZmRAhD.js +0 -1
  280. package/web/app/site/app/assets/locales/kab-Cz8xH_TT.js +0 -1
  281. package/web/app/site/app/assets/locales/ko-KR-CG9UiY4o.js +0 -1
  282. package/web/app/site/app/assets/locales/lt-LT-CJcI6sHZ.js +0 -1
  283. package/web/app/site/app/assets/locales/nb-NO-CfheRbCu.js +0 -1
  284. package/web/app/site/app/assets/locales/nl-NL-DxN4Yc1t.js +0 -1
  285. package/web/app/site/app/assets/locales/oc-FR-CfheRbCu.js +0 -1
  286. package/web/app/site/app/assets/locales/pl-PL-DkwGXf6P.js +0 -1
  287. package/web/app/site/app/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +0 -1
  288. package/web/app/site/app/assets/locales/pt-BR-nGL0HdFS.js +0 -1
  289. package/web/app/site/app/assets/locales/pt-PT-Cp17aj2z.js +0 -1
  290. package/web/app/site/app/assets/locales/ru-RU-0Y8cIoaK.js +0 -1
  291. package/web/app/site/app/assets/locales/th-TH-CfheRbCu.js +0 -1
  292. package/web/app/site/app/assets/locales/tok-CfheRbCu.js +0 -1
  293. package/web/app/site/app/assets/locales/tr-TR-CfheRbCu.js +0 -1
  294. package/web/app/site/app/assets/locales/uk-UA-C5E3dFBf.js +0 -1
  295. package/web/app/site/app/assets/locales/zh-CN-BHJi7_et.js +0 -1
  296. package/web/app/site/app/assets/locales/zh-TW-CfheRbCu.js +0 -1
  297. package/web/app/site/app/assets/main-BdqNbG-a.js +0 -26
  298. package/web/app/site/app/assets/main-BdqNbG-a.js.map +0 -1
  299. package/web/app/site/app/assets/mock-home-DGhR__He.js +0 -2
  300. package/web/app/site/app/assets/mock-home-DGhR__He.js.map +0 -1
  301. package/web/app/site/app/assets/multi-column-ROck0NVt.jpg +0 -0
  302. package/web/app/site/app/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
  303. package/web/app/site/app/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
  304. package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js +0 -2
  305. package/web/app/site/app/assets/polyfill-force-H8sUkLGp.js.map +0 -1
  306. package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js +0 -20
  307. package/web/app/site/app/assets/pwa-viewport-ogKbF51K.js.map +0 -1
  308. package/web/app/site/app/assets/style-B1iLPTPR.css +0 -2
  309. package/web/app/site/app/assets/temml-DuyIQMei.js +0 -97
  310. package/web/app/site/app/assets/temml-DuyIQMei.js.map +0 -1
  311. package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js +0 -2
  312. package/web/app/site/app/assets/tinyld.light.node-DtbBhLYC.js.map +0 -1
  313. package/web/app/site/app/assets/year-in-posts-B2lywqdx.png +0 -0
  314. package/web/app/site/app/assets/year-in-posts-DJv_star.js +0 -3
  315. package/web/app/site/app/assets/year-in-posts-DJv_star.js.map +0 -1
  316. package/web/app/site/app/compose/index.html +0 -20
  317. package/web/app/site/app/favicon.ico +0 -0
  318. package/web/app/site/app/index.html +0 -73
  319. package/web/app/site/app/logo-192.png +0 -0
  320. package/web/app/site/app/logo-512.png +0 -0
  321. package/web/app/site/app/logo-badge-72.png +0 -0
  322. package/web/app/site/app/logo-maskable-512.png +0 -0
  323. package/web/app/site/app/logo-monochrome-512.png +0 -0
  324. package/web/app/site/app/logo-monochrome-maskable-512.png +0 -0
  325. package/web/app/site/app/manifest.webmanifest +0 -1
  326. package/web/app/site/app/og-image-2.jpg +0 -0
  327. package/web/app/site/app/og-image.png +0 -0
  328. package/web/app/site/app/robots.txt +0 -2
  329. package/web/app/site/app/version.json +0 -1
  330. package/web/app/site/boot.js +0 -34277
  331. package/web/app/site/index.html +0 -251
  332. package/web/app/site/sw.js +0 -71331
@@ -22024,196 +22024,6 @@ var require_browser_ponyfill = __commonJS({
22024
22024
  }
22025
22025
  });
22026
22026
 
22027
- // web/app/pod-auth.mjs
22028
- var b64u = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
22029
- var enc = (o) => b64u(new TextEncoder().encode(JSON.stringify(o)));
22030
- var sha256 = async (str) => crypto.subtle.digest("SHA-256", new TextEncoder().encode(str));
22031
- async function accountFetch(url, { method = "GET", body, token } = {}) {
22032
- const headers = { accept: "application/json" };
22033
- if (body !== void 0) headers["content-type"] = "application/json";
22034
- if (token) headers.authorization = `CSS-Account-Token ${token}`;
22035
- const res = await fetch(url, { method, headers, body: body === void 0 ? void 0 : JSON.stringify(body) });
22036
- let json = null;
22037
- try {
22038
- json = await res.json();
22039
- } catch {
22040
- }
22041
- return { status: res.status, json };
22042
- }
22043
- async function createAccountWithPod({ issuer, email, password, podName }) {
22044
- const origin = issuer.replace(/\/+$/, "");
22045
- const accountRoot = `${origin}/.account/`;
22046
- let token;
22047
- const login = await accountFetch(`${accountRoot}login/password/`, { method: "POST", body: { email, password } });
22048
- if (login.status < 400 && login.json?.authorization) {
22049
- token = login.json.authorization;
22050
- } else {
22051
- const create = await accountFetch(`${accountRoot}account/`, { method: "POST" });
22052
- if (!create.json?.authorization) {
22053
- throw new Error(`could not create an account at ${origin} (HTTP ${create.status}) \u2014 is sign-up open there? ${create.json?.message || ""}`);
22054
- }
22055
- token = create.json.authorization;
22056
- const pwCreate = (await accountFetch(accountRoot, { token })).json?.controls?.password?.create;
22057
- if (!pwCreate) throw new Error("this server is not a CSS v7 account API (no password control)");
22058
- const pw = await accountFetch(pwCreate, { method: "POST", token, body: { email, password } });
22059
- if (pw.status >= 400) throw new Error(`could not set the password (HTTP ${pw.status}): ${pw.json?.message || ""}`);
22060
- }
22061
- const podCreate = (await accountFetch(accountRoot, { token })).json?.controls?.account?.pod;
22062
- if (!podCreate) throw new Error("this server does not offer pod creation through its account API");
22063
- const findOwn = async () => {
22064
- const pods = (await accountFetch(podCreate, { token })).json?.pods || {};
22065
- return Object.entries(pods).find(([url]) => {
22066
- try {
22067
- const u = new URL(url);
22068
- return u.hostname === podName || u.hostname.startsWith(`${podName}.`) || u.pathname.split("/").filter(Boolean).includes(podName);
22069
- } catch {
22070
- return false;
22071
- }
22072
- }) || null;
22073
- };
22074
- const owned = Object.keys((await accountFetch(podCreate, { token })).json?.pods || {});
22075
- if (owned.length && !await findOwn()) {
22076
- throw new Error(`${email} already has a pod on ${new URL(origin).host} \u2014 one account, one pod. A second identity needs its own account. Nothing was created.`);
22077
- }
22078
- const made = await accountFetch(podCreate, { method: "POST", token, body: { name: podName } });
22079
- let pod = made.json?.pod || made.json?.podBaseUrl || null;
22080
- let webId = made.json?.webId || null;
22081
- if (made.status >= 400 || !pod) {
22082
- const own = await findOwn();
22083
- if (own) {
22084
- pod = own[0];
22085
- webId = webId || own[1]?.webId || null;
22086
- } else if (made.status >= 400) throw new Error(`pod creation failed (HTTP ${made.status}): ${made.json?.message || ""}`);
22087
- }
22088
- if (!pod) throw new Error("the server did not report a pod URL");
22089
- return { pod: pod.endsWith("/") ? pod : pod + "/", webId, accountToken: token };
22090
- }
22091
- async function mintCredential({ issuer, email, password, webId, podUrl, accountToken, name = "fedipod" }) {
22092
- const origin = issuer.replace(/\/+$/, "");
22093
- const accountRoot = `${origin}/.account/`;
22094
- let token = accountToken;
22095
- if (!token) {
22096
- const tryLogin = async (id) => {
22097
- const r = await accountFetch(`${accountRoot}login/password/`, { method: "POST", body: { email: id, password } });
22098
- return r.status < 400 && r.json?.authorization ? r.json.authorization : null;
22099
- };
22100
- const ownsPod = async (tok) => {
22101
- if (!podUrl) return true;
22102
- try {
22103
- const ctl = (await accountFetch(accountRoot, { token: tok })).json?.controls?.account?.webId;
22104
- const links = ctl ? (await accountFetch(ctl, { token: tok })).json?.webIdLinks : null;
22105
- return Object.keys(links || {}).some((w) => new URL(w).origin === new URL(podUrl).origin);
22106
- } catch {
22107
- return false;
22108
- }
22109
- };
22110
- token = await tryLogin(email);
22111
- let sub = null;
22112
- try {
22113
- sub = podUrl ? new URL(podUrl).host.split(".")[0] : null;
22114
- } catch {
22115
- }
22116
- if (sub && sub !== email && (!token || !await ownsPod(token))) {
22117
- const alt = await tryLogin(sub);
22118
- if (alt) token = alt;
22119
- }
22120
- if (!token) throw new Error("account login failed \u2014 check the email or username and the password");
22121
- }
22122
- const controls = (await accountFetch(accountRoot, { token })).json?.controls;
22123
- const ccUrl = controls?.account?.clientCredentials;
22124
- if (!ccUrl) throw new Error("this server is not a CSS account API (no clientCredentials control)");
22125
- let wid = webId;
22126
- if (!wid) {
22127
- const linkCtl = controls?.account?.webId;
22128
- const links = linkCtl ? (await accountFetch(linkCtl, { token })).json?.webIdLinks : null;
22129
- const all = links ? Object.keys(links) : [];
22130
- if (!all.length) throw new Error("no WebID is linked to this account");
22131
- if (podUrl) {
22132
- let origins = [];
22133
- try {
22134
- origins = all.filter((w) => new URL(w).origin === new URL(podUrl).origin);
22135
- } catch {
22136
- }
22137
- if (!origins.length) {
22138
- throw new Error(`the account you signed in with does not own ${new URL(podUrl).origin}. It is linked to ${all.length} WebID${all.length > 1 ? "s" : ""} \u2014 on ${all.map((w) => new URL(w).host).join(", ")} \u2014 none of them this pod. Sign in with the account that owns this pod, or let FediPod make you a new one.`);
22139
- }
22140
- wid = origins[0];
22141
- } else {
22142
- wid = all[0];
22143
- }
22144
- }
22145
- const made = await accountFetch(ccUrl, { method: "POST", token, body: { name, webId: wid } });
22146
- if (made.status >= 400 || !made.json?.secret) throw new Error(`mint failed (HTTP ${made.status}): ${made.json?.message || ""}`);
22147
- const tokenEndpoint = await discoverTokenEndpoint(origin);
22148
- return {
22149
- clientId: made.json.id,
22150
- secret: made.json.secret,
22151
- webId: wid,
22152
- tokenEndpoint,
22153
- resource: made.json.resource,
22154
- issuerOrigin: origin,
22155
- // Carried so sign-up can hand it straight back to revokeCredential. It is a
22156
- // session token for the ACCOUNT — never stored, never written to the pod,
22157
- // and gone with the tab.
22158
- accountToken: token
22159
- };
22160
- }
22161
- async function revokeCredential({ resource, accountToken }) {
22162
- if (!resource || !accountToken) return false;
22163
- try {
22164
- const r = await accountFetch(resource, { method: "DELETE", token: accountToken });
22165
- return r.status < 400;
22166
- } catch {
22167
- return false;
22168
- }
22169
- }
22170
- async function discoverTokenEndpoint(issuer) {
22171
- const origin = issuer.replace(/\/+$/, "");
22172
- try {
22173
- const res = await fetch(`${origin}/.well-known/openid-configuration`, { headers: { accept: "application/json" } });
22174
- if (res.ok) {
22175
- const doc = await res.json();
22176
- if (doc.token_endpoint) return doc.token_endpoint;
22177
- }
22178
- } catch {
22179
- }
22180
- return `${origin}/.oidc/token`;
22181
- }
22182
- async function makeDpopSession({ clientId, secret, tokenEndpoint }) {
22183
- const proofKey = await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
22184
- const jwk = await crypto.subtle.exportKey("jwk", proofKey.publicKey);
22185
- const publicJwk2 = { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y };
22186
- const proof = async (htm, htu, ath) => {
22187
- const header = { typ: "dpop+jwt", alg: "ES256", jwk: publicJwk2 };
22188
- const payload = { htm, htu: htu.split("#")[0], jti: crypto.randomUUID(), iat: Math.floor(Date.now() / 1e3), ...ath ? { ath } : {} };
22189
- const data = `${enc(header)}.${enc(payload)}`;
22190
- const sig = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, proofKey.privateKey, new TextEncoder().encode(data));
22191
- return `${data}.${b64u(sig)}`;
22192
- };
22193
- let token = null;
22194
- let expiresAt = 0;
22195
- const refresh = async () => {
22196
- const basic = btoa(`${encodeURIComponent(clientId)}:${encodeURIComponent(secret)}`);
22197
- const res = await fetch(tokenEndpoint, {
22198
- method: "POST",
22199
- headers: { authorization: `Basic ${basic}`, "content-type": "application/x-www-form-urlencoded", dpop: await proof("POST", tokenEndpoint) },
22200
- body: "grant_type=client_credentials&scope=webid"
22201
- });
22202
- const tok = await res.json().catch(() => ({}));
22203
- if (!tok.access_token) throw new Error(`token request failed (HTTP ${res.status}): ${tok.error || ""}`);
22204
- token = tok.access_token;
22205
- expiresAt = Date.now() + Math.max(30, (tok.expires_in || 300) - 30) * 1e3;
22206
- return token;
22207
- };
22208
- const authFetch = async (url, init = {}) => {
22209
- if (!token || Date.now() > expiresAt) await refresh();
22210
- const ath = b64u(await sha256(token));
22211
- const headers = { ...init.headers || {}, authorization: `DPoP ${token}`, dpop: await proof(init.method || "GET", url, ath) };
22212
- return fetch(url, { ...init, headers });
22213
- };
22214
- return { fetch: authFetch, refresh };
22215
- }
22216
-
22217
22027
  // web/app/keystore.mjs
22218
22028
  var PEM = (der, label) => {
22219
22029
  const b64 = btoa(String.fromCharCode(...new Uint8Array(der)));
@@ -22245,7 +22055,6 @@ async function generateKeys() {
22245
22055
  }
22246
22056
  return rec;
22247
22057
  }
22248
- var PBKDF2_ITERATIONS = 31e4;
22249
22058
  async function deriveAesKey(password, salt, iterations) {
22250
22059
  const base = await crypto.subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveKey"]);
22251
22060
  return crypto.subtle.deriveKey(
@@ -22256,26 +22065,10 @@ async function deriveAesKey(password, salt, iterations) {
22256
22065
  ["encrypt", "decrypt"]
22257
22066
  );
22258
22067
  }
22259
- var toB64 = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf)));
22260
22068
  var fromB64 = (s) => Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
22261
22069
  function isKeyEnvelope(doc) {
22262
22070
  return !!doc && doc.v === 1 && typeof doc.ct === "string" && typeof doc.salt === "string";
22263
22071
  }
22264
- async function wrapKeys(keysRecord, password) {
22265
- const salt = crypto.getRandomValues(new Uint8Array(16));
22266
- const iv = crypto.getRandomValues(new Uint8Array(12));
22267
- const aes = await deriveAesKey(password, salt, PBKDF2_ITERATIONS);
22268
- const plaintext = new TextEncoder().encode(JSON.stringify(keysRecord));
22269
- const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, aes, plaintext);
22270
- return {
22271
- v: 1,
22272
- kdf: "PBKDF2-SHA256",
22273
- iterations: PBKDF2_ITERATIONS,
22274
- salt: toB64(salt),
22275
- iv: toB64(iv),
22276
- ct: toB64(ct)
22277
- };
22278
- }
22279
22072
  async function unwrapKeys(envelope, password) {
22280
22073
  if (!envelope || envelope.v !== 1) throw new Error("not a key envelope");
22281
22074
  const aes = await deriveAesKey(password, fromB64(envelope.salt), envelope.iterations);
@@ -33000,7 +32793,7 @@ var PodTransport = class {
33000
32793
  // leaves the private trees world-readable. $rdf.sym() also throws on an
33001
32794
  // illegal IRI, so a pod URL with something odd in it fails here rather than
33002
32795
  // silently producing a document that means something else.
33003
- aclDoc(targetUrl, publicModes, { appendAgents = [], aclUrl = null } = {}) {
32796
+ aclDoc(targetUrl, publicModes, { appendAgents = [], readAgents = [], aclUrl = null } = {}) {
33004
32797
  const url = aclUrl || targetUrl + ".acl";
33005
32798
  const doc = namedNode2(url);
33006
32799
  const target = namedNode2(targetUrl);
@@ -33016,6 +32809,7 @@ var PodTransport = class {
33016
32809
  authorize(namedNode2(url + "#public"), ACL("agentClass"), FOAF("Agent"), publicModes);
33017
32810
  }
33018
32811
  appendAgents.forEach((webId, i) => authorize(namedNode2(url + `#gw${i}`), ACL("agent"), namedNode2(webId), ["Append"]));
32812
+ readAgents.forEach((webId, i) => authorize(namedNode2(url + `#r${i}`), ACL("agent"), namedNode2(webId), ["Read"]));
33019
32813
  authorize(
33020
32814
  namedNode2(url + "#owner"),
33021
32815
  ACL("agent"),
@@ -33206,44 +33000,18 @@ var BrowserRemotePod = class extends PodTransport {
33206
33000
  };
33207
33001
 
33208
33002
  // lib/pod/state.mjs
33209
- var readWrappedKeys = (pod, urls) => pod.getJson(urls.state + "keys.json");
33003
+ var readKeys = (pod, urls) => pod.getJson(urls.state + "keys.json");
33210
33004
  var readConfig = (pod, urls) => pod.getJson(urls.state + "config.json");
33211
- var writeWrappedKeys = (pod, urls, envelope) => pod.putJson(urls.state + "keys.json", envelope, "application/json");
33005
+ var writeKeys = (pod, urls, keys) => pod.putJson(urls.state + "keys.json", keys, "application/json");
33212
33006
  var writeConfig = (pod, urls, config) => pod.putJson(urls.state + "config.json", config, "application/json");
33213
- async function provisionKey(pod, { stateUrl, keysUrl, envelope }) {
33007
+ async function provisionKey(pod, { stateUrl, keysUrl, keys }) {
33214
33008
  await pod.setAcl(stateUrl, []);
33215
- await pod.putJson(keysUrl, envelope, "application/json");
33009
+ await pod.putJson(keysUrl, keys, "application/json");
33216
33010
  }
33217
33011
 
33218
33012
  // lib/pod/root.mjs
33219
33013
  var OWNER_LOOKUP_MS = 5e3;
33220
33014
  var PUBLIC_DOC_MAX_BYTES = 1024 * 1024;
33221
- async function podLayout(fetchImpl, providerOrigin, { timeoutMs = OWNER_LOOKUP_MS } = {}) {
33222
- let origin;
33223
- try {
33224
- origin = new URL(providerOrigin).origin;
33225
- } catch {
33226
- return null;
33227
- }
33228
- let res;
33229
- try {
33230
- res = await fetchImpl(
33231
- `${origin}/.well-known/solid`,
33232
- { headers: { accept: "text/turtle" }, signal: AbortSignal.timeout(timeoutMs) }
33233
- );
33234
- } catch {
33235
- return null;
33236
- }
33237
- if (res.status === 501) return "host";
33238
- if (res.status !== 200) return null;
33239
- let body = "";
33240
- try {
33241
- body = await readCapped(res, 64 * 1024);
33242
- } catch {
33243
- return null;
33244
- }
33245
- return /ns\/pim\/space#Storage|pim:Storage/u.test(body) ? "path" : null;
33246
- }
33247
33015
  async function resourceExists(fetchImpl, url, { timeoutMs = OWNER_LOOKUP_MS } = {}) {
33248
33016
  try {
33249
33017
  const res = await fetchImpl(
@@ -33256,6 +33024,15 @@ async function resourceExists(fetchImpl, url, { timeoutMs = OWNER_LOOKUP_MS } =
33256
33024
  }
33257
33025
  }
33258
33026
 
33027
+ // lib/pod/urls.mjs
33028
+ function podBaseOfWebId(webId) {
33029
+ const u = new URL(webId);
33030
+ u.hash = "";
33031
+ u.search = "";
33032
+ const dir = u.pathname.replace(/profile\/card$/u, "").replace(/[^/]*$/u, "");
33033
+ return `${u.origin}${dir.endsWith("/") ? dir : dir + "/"}`;
33034
+ }
33035
+
33259
33036
  // web/app/idb-kv.mjs
33260
33037
  var DB = "fedipod-accounts";
33261
33038
  function open() {
@@ -33314,7 +33091,7 @@ function handleProblem(handle) {
33314
33091
  return null;
33315
33092
  }
33316
33093
  var PROGRESS = /* @__PURE__ */ new Map();
33317
- var progressKey = (a) => [a.issuer, a.mode, a.handle, a.mode === "new" ? a.podName || a.handle : a.pod].join("|");
33094
+ var progressKey = (webId, a) => [webId, a.handle, a.shape || "pod"].join("|");
33318
33095
  async function assertFrontNameFree(frontOrigin, handle) {
33319
33096
  const res = await fetch(
33320
33097
  `${frontOrigin.replace(/\/$/, "")}/api/handle?handle=${encodeURIComponent(handle)}`,
@@ -33324,18 +33101,18 @@ async function assertFrontNameFree(frontOrigin, handle) {
33324
33101
  if (!d) throw new Error(`${new URL(frontOrigin).host} did not answer whether @${handle} is free`);
33325
33102
  if (!d.available) throw new Error(d.reason || `the name @${handle}@${new URL(frontOrigin).host} is taken \u2014 choose another handle`);
33326
33103
  }
33327
- async function signUp(answers, { onStep = () => {
33104
+ async function signUp(answers, { session, onStep = () => {
33328
33105
  }, frontOrigin = null } = {}) {
33329
- const { mode, issuer, email, password, handle } = answers;
33106
+ const { handle } = answers;
33330
33107
  const bad = handleProblem(handle);
33331
33108
  if (bad) throw new Error(bad);
33332
- if (!email) throw new Error("an email is required");
33333
- if (!password) throw new Error("a password is required");
33334
- if (mode === "existing" && !answers.pod) throw new Error("a pod address is required");
33109
+ if (!session?.webId || typeof session.fetch !== "function") throw new Error("sign in at your pod first");
33335
33110
  const wantsFront = answers.shape === "front";
33336
33111
  if (wantsFront && !frontOrigin) throw new Error("an address at the gateway needs a gateway, and this page has none");
33337
33112
  if (wantsFront) await assertFrontNameFree(frontOrigin, handle);
33338
- const key = progressKey(answers);
33113
+ const webId = session.webId;
33114
+ const pod = podBaseOfWebId(webId);
33115
+ const key = progressKey(webId, answers);
33339
33116
  const prog = PROGRESS.get(key) || {};
33340
33117
  PROGRESS.set(key, prog);
33341
33118
  const step = (key2) => ({
@@ -33343,30 +33120,15 @@ async function signUp(answers, { onStep = () => {
33343
33120
  ok: (note) => onStep(key2, "ok", note),
33344
33121
  skip: (note) => onStep(key2, "skipped", note)
33345
33122
  });
33346
- let accountToken = null;
33347
- const acct = step("account");
33123
+ const podStep = step("pod");
33348
33124
  if (!prog.pod) {
33349
- if (mode === "new") {
33350
- acct.running("creating the account and pod");
33351
- const made = await createAccountWithPod({ issuer, email, password, podName: answers.podName || handle });
33352
- prog.pod = made.pod;
33353
- prog.webId = made.webId;
33354
- accountToken = made.accountToken;
33355
- acct.ok(made.pod);
33356
- } else {
33357
- const brought = answers.pod.endsWith("/") ? answers.pod : answers.pod + "/";
33358
- acct.running("checking your pod");
33359
- const head = await fetch(brought, { method: "HEAD" }).catch(() => null);
33360
- if (!head || head.status >= 400) throw new Error(`the pod at ${brought} did not answer (HTTP ${head?.status || "no response"})`);
33361
- if (await resourceExists(fetch, actorUrlFor(brought))) throw new Error("The pod already hosts a FediPod account. If you want a second account, put it on a different pod.");
33362
- prog.pod = brought;
33363
- acct.skip("using the pod you brought");
33364
- }
33125
+ podStep.running("checking your pod");
33126
+ if (await resourceExists(session.fetch, actorUrlFor(pod))) throw new Error("The pod already hosts a FediPod account. If you want a second account, put it on a different pod.");
33127
+ prog.pod = pod;
33128
+ podStep.ok(pod);
33365
33129
  } else {
33366
- acct.ok(prog.pod);
33130
+ podStep.ok(prog.pod);
33367
33131
  }
33368
- const pod = prog.pod;
33369
- const webId = prog.webId || null;
33370
33132
  const pathPod = new URL(pod).pathname !== "/";
33371
33133
  const fronted = pathPod || wantsFront;
33372
33134
  if (fronted && !frontOrigin) {
@@ -33375,20 +33137,6 @@ async function signUp(answers, { onStep = () => {
33375
33137
  if (pathPod && !wantsFront) await assertFrontNameFree(frontOrigin, handle);
33376
33138
  const actorUrl = actorUrlFor(pod);
33377
33139
  const frontActor = fronted ? `${frontOrigin.replace(/\/$/, "")}/u/${handle}/ap/actor` : null;
33378
- const cred = step("credential");
33379
- let credential;
33380
- if (!prog.credential) {
33381
- cred.running("minting a credential for this browser");
33382
- credential = await mintCredential({ issuer, email, password, webId, podUrl: pod, accountToken });
33383
- credential.remotePod = pod;
33384
- credential.root = AP_ROOT;
33385
- prog.credential = credential;
33386
- cred.ok();
33387
- } else {
33388
- credential = prog.credential;
33389
- cred.ok();
33390
- }
33391
- const session = await makeDpopSession(credential);
33392
33140
  let gateway = answers.gateway || prog.gateway || null;
33393
33141
  if (frontOrigin && !gateway) {
33394
33142
  const gw = step("gateway");
@@ -33413,22 +33161,22 @@ async function signUp(answers, { onStep = () => {
33413
33161
  } else if (frontOrigin && gateway) {
33414
33162
  step("gateway").ok();
33415
33163
  }
33164
+ const remote = new BrowserRemotePod(session, { webId, role: "signup", log: () => {
33165
+ } });
33416
33166
  const keysStep = step("keys");
33417
33167
  let keys;
33418
33168
  if (!prog.keysStored) {
33419
- keysStep.running("making your signing key and locking it under your password");
33169
+ keysStep.running("making your signing key and storing it on your pod");
33420
33170
  keys = await generateKeys();
33421
33171
  keys.mintedFor = gateway?.frontActor || actorUrl;
33422
- const remote = new BrowserRemotePod(session, { webId: credential.webId, role: "signup", log: () => {
33423
- } });
33424
33172
  try {
33425
33173
  await provisionKey(remote, {
33426
33174
  stateUrl: `${pod}${AP_ROOT}ap-state/`,
33427
33175
  keysUrl: keysDocFor(pod),
33428
- envelope: await wrapKeys(keys, password)
33176
+ keys
33429
33177
  });
33430
33178
  } catch (e) {
33431
- throw new Error(`could not store the signing key on the pod (${e.message}). The credential is for ${credential.webId} \u2014 that WebID must own ${pod} and its ${AP_ROOT} must be writable by it.`);
33179
+ throw new Error(`could not store the signing key on the pod (${e.message}). You are signed in as ${webId} \u2014 that WebID must own ${pod} and its ${AP_ROOT} must be writable by it.`);
33432
33180
  }
33433
33181
  await cacheOpenedKeys(actorUrl, keys);
33434
33182
  prog.keys = keys;
@@ -33443,40 +33191,108 @@ async function signUp(answers, { onStep = () => {
33443
33191
  root: AP_ROOT,
33444
33192
  handle,
33445
33193
  name: handle,
33446
- issuer: credential.issuerOrigin,
33194
+ issuer: String(session.issuer || "").replace(/\/+$/, ""),
33447
33195
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
33448
33196
  ...gateway ? { gateway } : {}
33449
33197
  };
33450
- const cfgRemote = new BrowserRemotePod(session, { webId: credential.webId, role: "signup", log: () => {
33451
- } });
33452
33198
  try {
33453
- await writeConfig(cfgRemote, { state: `${pod}${AP_ROOT}ap-state/` }, config);
33199
+ await writeConfig(remote, { state: `${pod}${AP_ROOT}ap-state/` }, config);
33454
33200
  } catch (e) {
33455
33201
  throw new Error(`could not store the config on the pod (${e.message})`);
33456
33202
  }
33457
- const revoked = await revokeCredential({
33458
- resource: credential.resource,
33459
- accountToken: credential.accountToken
33460
- });
33461
- delete credential.accountToken;
33462
- if (!revoked && credential.resource) {
33463
- onStep("credential", "ok", "this browser is ready (the setup credential could not be revoked automatically \u2014 you can remove it from your pod's account page)");
33464
- }
33465
33203
  PROGRESS.delete(key);
33466
33204
  const host = gateway?.frontActor ? new URL(gateway.frontActor).host : new URL(pod).host;
33467
33205
  return {
33468
- credential,
33469
33206
  config,
33470
33207
  actorUrl,
33208
+ pod,
33471
33209
  address: `@${handle}@${host}`,
33472
33210
  // The opened keys, for booting the agent in THIS browser session right away.
33473
- // The durable copy on the pod is wrapped under the account password; this
33474
- // browser also holds an opened one in IndexedDB (above), which is what the
33475
- // worker reads. A fresh browser asks for the password once and makes its own.
33476
33211
  keys,
33477
33212
  keysPublic: { rsa: keys.rsa.publicPem, ed25519: keys.ed25519?.publicPem || null }
33478
33213
  };
33479
33214
  }
33215
+ async function readAccount(session) {
33216
+ const pod = podBaseOfWebId(session.webId);
33217
+ const remote = new BrowserRemotePod(session, { webId: session.webId, role: "signup", log: () => {
33218
+ } });
33219
+ const config = await readConfig(remote, { state: `${pod}${AP_ROOT}ap-state/` }).catch(() => null);
33220
+ if (!config) return null;
33221
+ const frontActor = config.gateway?.frontActor || null;
33222
+ let frontHost = null;
33223
+ try {
33224
+ frontHost = frontActor ? new URL(frontActor).host : null;
33225
+ } catch {
33226
+ frontHost = null;
33227
+ }
33228
+ let podHost = "";
33229
+ try {
33230
+ podHost = new URL(config.remotePod || pod).host;
33231
+ } catch {
33232
+ podHost = "";
33233
+ }
33234
+ return { pod, config, frontActor, frontHost, address: `@${config.handle}@${frontHost || podHost}` };
33235
+ }
33236
+ async function moveIn(answers, { session, onStep = () => {
33237
+ }, frontOrigin = null } = {}) {
33238
+ const { handle } = answers;
33239
+ const bad = handleProblem(handle);
33240
+ if (bad) throw new Error(bad);
33241
+ if (!session?.webId || typeof session.fetch !== "function") throw new Error("sign in at your pod first");
33242
+ if (!frontOrigin) throw new Error("a move needs a gateway to move to, and this page has none");
33243
+ const origin = frontOrigin.replace(/\/$/, "");
33244
+ const step = (key) => ({ running: (note) => onStep(key, "running", note), ok: (note) => onStep(key, "ok", note) });
33245
+ const podStep = step("pod");
33246
+ podStep.running("reading your account on your pod");
33247
+ const here = await readAccount(session);
33248
+ if (!here?.frontActor) throw new Error("this pod holds no account at another gateway to move here");
33249
+ if (here.frontHost === new URL(origin).host) throw new Error("this account already lives at this gateway");
33250
+ const { pod, config: old } = here;
33251
+ const remote = new BrowserRemotePod(session, { webId: session.webId, role: "signup", log: () => {
33252
+ } });
33253
+ const urls = { state: `${pod}${AP_ROOT}ap-state/` };
33254
+ const keys = await readKeys(remote, urls);
33255
+ if (!keys?.rsa) throw new Error("could not read this account's signing key on the pod");
33256
+ if (isKeyEnvelope(keys)) throw new Error(`this account's key is still under a password from an earlier version \u2014 sign in once at ${here.frontHost} first, then come back`);
33257
+ podStep.ok(here.address);
33258
+ const gw = step("gateway");
33259
+ gw.running(`taking your address at ${new URL(origin).host}`);
33260
+ await assertFrontNameFree(origin, handle);
33261
+ const res = await session.fetch(`${origin}/api/attach`, {
33262
+ method: "POST",
33263
+ headers: { "content-type": "application/json" },
33264
+ body: JSON.stringify({ handle, podHome: `${pod}${AP_ROOT}`, actorUrl: actorUrlFor(pod), kind: old.kind || "person", fronted: true })
33265
+ });
33266
+ const d = await res.json().catch(() => ({}));
33267
+ if (res.status !== 201 || !d.hmacSecret) throw new Error(`could not attach to this gateway (HTTP ${res.status}): ${d.error || ""}`);
33268
+ const gateway = {
33269
+ url: `${origin}/u/${handle}/ap/inbox/`,
33270
+ frontActor: String(d.frontActor || `${origin}/u/${handle}/ap/actor`),
33271
+ hmacSecret: d.hmacSecret,
33272
+ mode: "trust"
33273
+ };
33274
+ gw.ok();
33275
+ const keysStep = step("keys");
33276
+ keysStep.running("moving your key and account record to the new address");
33277
+ const aliases = [.../* @__PURE__ */ new Set([...old.aliases || [], here.frontActor])];
33278
+ const config = {
33279
+ ...old,
33280
+ handle,
33281
+ gateway,
33282
+ aliases,
33283
+ movedFrom: { actor: here.frontActor, gateway: new URL(here.frontActor).origin, handle: old.handle, at: (/* @__PURE__ */ new Date()).toISOString() }
33284
+ };
33285
+ keys.mintedFor = gateway.frontActor;
33286
+ try {
33287
+ await writeKeys(remote, urls, keys);
33288
+ await writeConfig(remote, urls, config);
33289
+ } catch (e) {
33290
+ throw new Error(`could not update your account on the pod (${e.message})`);
33291
+ }
33292
+ await cacheOpenedKeys(actorUrlFor(pod), keys);
33293
+ keysStep.ok();
33294
+ return { config, address: `@${handle}@${new URL(origin).host}`, movedFrom: config.movedFrom };
33295
+ }
33480
33296
 
33481
33297
  // lib/pod/actor.mjs
33482
33298
  var ACCEPT_AP = 'application/activity+json, application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
@@ -33491,54 +33307,12 @@ async function readIssuer(actorUrl, fetchImpl = fetch) {
33491
33307
  }
33492
33308
  }
33493
33309
 
33494
- // lib/pod/urls.mjs
33495
- function podBaseOfWebId(webId) {
33496
- const u = new URL(webId);
33497
- u.hash = "";
33498
- u.search = "";
33499
- const dir = u.pathname.replace(/profile\/card$/u, "").replace(/[^/]*$/u, "");
33500
- return `${u.origin}${dir.endsWith("/") ? dir : dir + "/"}`;
33501
- }
33502
-
33503
- // web/app/oidc-session.mjs
33504
- var DB2 = "fedipod-oidc";
33310
+ // lib/session/oidc-session.mjs
33505
33311
  var STORE = "session";
33506
- var b64u2 = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
33507
- var enc2 = (o) => b64u2(new TextEncoder().encode(JSON.stringify(o)));
33508
- var sha2562 = (s) => crypto.subtle.digest("SHA-256", new TextEncoder().encode(s));
33509
- var rand = (n = 32) => b64u2(crypto.getRandomValues(new Uint8Array(n)));
33510
- function idb() {
33511
- return new Promise((res, rej) => {
33512
- const r = indexedDB.open(DB2, 1);
33513
- r.onupgradeneeded = () => r.result.createObjectStore(STORE);
33514
- r.onsuccess = () => res(r.result);
33515
- r.onerror = () => rej(r.error);
33516
- });
33517
- }
33518
- async function idbGet(key) {
33519
- const db = await idb();
33520
- return new Promise((res, rej) => {
33521
- const t = db.transaction(STORE).objectStore(STORE).get(key);
33522
- t.onsuccess = () => res(t.result);
33523
- t.onerror = () => rej(t.error);
33524
- });
33525
- }
33526
- async function idbPut(key, val) {
33527
- const db = await idb();
33528
- return new Promise((res, rej) => {
33529
- const t = db.transaction(STORE, "readwrite").objectStore(STORE).put(val, key);
33530
- t.onsuccess = () => res();
33531
- t.onerror = () => rej(t.error);
33532
- });
33533
- }
33534
- async function idbDel(key) {
33535
- const db = await idb();
33536
- return new Promise((res, rej) => {
33537
- const t = db.transaction(STORE, "readwrite").objectStore(STORE).delete(key);
33538
- t.onsuccess = () => res();
33539
- t.onerror = () => rej(t.error);
33540
- });
33541
- }
33312
+ var b64u = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
33313
+ var enc = (o) => b64u(new TextEncoder().encode(JSON.stringify(o)));
33314
+ var sha256 = (s) => crypto.subtle.digest("SHA-256", new TextEncoder().encode(s));
33315
+ var rand = (n = 32) => b64u(crypto.getRandomValues(new Uint8Array(n)));
33542
33316
  async function dpopKey() {
33543
33317
  return crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
33544
33318
  }
@@ -33549,9 +33323,9 @@ async function publicJwk(pair) {
33549
33323
  async function dpopProof(pair, jwk, htm, htu, ath) {
33550
33324
  const header = { typ: "dpop+jwt", alg: "ES256", jwk };
33551
33325
  const payload = { htm, htu: htu.split("#")[0], jti: crypto.randomUUID(), iat: Math.floor(Date.now() / 1e3), ...ath ? { ath } : {} };
33552
- const data = `${enc2(header)}.${enc2(payload)}`;
33326
+ const data = `${enc(header)}.${enc(payload)}`;
33553
33327
  const sig = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, pair.privateKey, new TextEncoder().encode(data));
33554
- return `${data}.${b64u2(sig)}`;
33328
+ return `${data}.${b64u(sig)}`;
33555
33329
  }
33556
33330
  var jwtPayload = (jwt) => {
33557
33331
  try {
@@ -33582,129 +33356,188 @@ async function registerClient(cfg, redirectUri, clientName) {
33582
33356
  if (!res.ok) throw new Error(`client registration failed (HTTP ${res.status})`);
33583
33357
  return (await res.json()).client_id;
33584
33358
  }
33585
- async function beginLogin({ issuer, redirectUri, clientName = "FediPod" }) {
33586
- const cfg = await discover(issuer);
33587
- const client_id = await registerClient(cfg, redirectUri, clientName);
33588
- const pair = await dpopKey();
33589
- const verifier = rand(48);
33590
- const challenge = b64u2(await sha2562(verifier));
33591
- const state = rand(16);
33592
- await idbPut("pending", {
33593
- issuer,
33594
- client_id,
33595
- redirectUri,
33596
- verifier,
33597
- state,
33598
- pair,
33599
- tokenEndpoint: cfg.token_endpoint,
33600
- authorizationEndpoint: cfg.authorization_endpoint,
33601
- revocationEndpoint: cfg.revocation_endpoint || null
33602
- });
33603
- const url = new URL(cfg.authorization_endpoint);
33604
- for (const [k, v] of Object.entries({
33605
- client_id,
33606
- redirect_uri: redirectUri,
33607
- response_type: "code",
33608
- scope: "openid webid offline_access",
33609
- code_challenge: challenge,
33610
- code_challenge_method: "S256",
33611
- state,
33612
- prompt: "consent"
33613
- })) url.searchParams.set(k, v);
33614
- return { authorizationUrl: url.href };
33615
- }
33616
- async function completeLogin({ currentUrl }) {
33617
- const u = new URL(currentUrl);
33618
- const code = u.searchParams.get("code");
33619
- const state = u.searchParams.get("state");
33620
- if (!code) return null;
33621
- const p = await idbGet("pending");
33622
- if (!p || p.state !== state) throw new Error("login state mismatch");
33623
- const jwk = await publicJwk(p.pair);
33624
- const res = await fetch(p.tokenEndpoint, {
33625
- method: "POST",
33626
- headers: { "content-type": "application/x-www-form-urlencoded", dpop: await dpopProof(p.pair, jwk, "POST", p.tokenEndpoint) },
33627
- body: new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri: p.redirectUri, client_id: p.client_id, code_verifier: p.verifier })
33628
- });
33629
- const tok = await res.json().catch(() => ({}));
33630
- if (!res.ok || !tok.access_token) throw new Error(`token exchange failed (HTTP ${res.status}): ${tok.error || ""}`);
33631
- const webId = jwtPayload(tok.access_token).webid || jwtPayload(tok.id_token || "").webid || null;
33632
- const session = {
33633
- issuer: p.issuer,
33634
- client_id: p.client_id,
33635
- tokenEndpoint: p.tokenEndpoint,
33636
- pair: p.pair,
33637
- // Where to hand the refresh token back at sign-out. Kept on the session
33638
- // because discovery is a network round trip and sign-out should not need
33639
- // one — see signOut().
33640
- revocationEndpoint: p.revocationEndpoint || null,
33641
- refreshToken: tok.refresh_token || null,
33642
- accessToken: tok.access_token,
33643
- expiresAt: Date.now() + Math.max(30, tok.expires_in || 300) * 1e3,
33644
- webId
33645
- };
33646
- await idbPut("session", session);
33647
- await idbDel("pending");
33648
- return sessionHandle(session);
33649
- }
33650
- async function getSession() {
33651
- const s = await idbGet("session");
33652
- return s ? sessionHandle(s) : null;
33653
- }
33654
- async function signOut() {
33655
- const s = await idbGet("session").catch(() => null);
33656
- if (s?.revocationEndpoint && s.refreshToken) {
33657
- try {
33658
- await fetch(s.revocationEndpoint, {
33659
- method: "POST",
33660
- headers: { "content-type": "application/x-www-form-urlencoded" },
33661
- body: new URLSearchParams({
33662
- token: s.refreshToken,
33663
- token_type_hint: "refresh_token",
33664
- client_id: s.client_id
33665
- })
33666
- });
33667
- } catch {
33668
- }
33359
+ function solidOidcSession({ dbName = "solid-oidc-session", clientName = "Solid app" } = {}) {
33360
+ function idb() {
33361
+ return new Promise((res, rej) => {
33362
+ const r = indexedDB.open(dbName, 1);
33363
+ r.onupgradeneeded = () => r.result.createObjectStore(STORE);
33364
+ r.onsuccess = () => res(r.result);
33365
+ r.onerror = () => rej(r.error);
33366
+ });
33669
33367
  }
33670
- await idbDel("session");
33671
- await idbDel("pending");
33672
- }
33673
- function sessionHandle(s) {
33674
- let { accessToken, expiresAt, refreshToken } = s;
33675
- const jwkP = publicJwk(s.pair);
33676
- const refresh = async () => {
33677
- if (!refreshToken) throw new Error("session expired and there is no refresh token \u2014 sign in again");
33678
- const jwk = await jwkP;
33679
- const res = await fetch(s.tokenEndpoint, {
33368
+ async function idbGet(key) {
33369
+ const db = await idb();
33370
+ return new Promise((res, rej) => {
33371
+ const t = db.transaction(STORE).objectStore(STORE).get(key);
33372
+ t.onsuccess = () => res(t.result);
33373
+ t.onerror = () => rej(t.error);
33374
+ });
33375
+ }
33376
+ async function idbPut(key, val) {
33377
+ const db = await idb();
33378
+ return new Promise((res, rej) => {
33379
+ const t = db.transaction(STORE, "readwrite").objectStore(STORE).put(val, key);
33380
+ t.onsuccess = () => res();
33381
+ t.onerror = () => rej(t.error);
33382
+ });
33383
+ }
33384
+ async function idbDel(key) {
33385
+ const db = await idb();
33386
+ return new Promise((res, rej) => {
33387
+ const t = db.transaction(STORE, "readwrite").objectStore(STORE).delete(key);
33388
+ t.onsuccess = () => res();
33389
+ t.onerror = () => rej(t.error);
33390
+ });
33391
+ }
33392
+ async function beginLogin2({ issuer, redirectUri, clientName: name = clientName, returnTo = null }) {
33393
+ const cfg = await discover(issuer);
33394
+ const client_id = await registerClient(cfg, redirectUri, name);
33395
+ const pair = await dpopKey();
33396
+ const verifier = rand(48);
33397
+ const challenge = b64u(await sha256(verifier));
33398
+ const state = rand(16);
33399
+ await idbPut("pending", {
33400
+ issuer,
33401
+ client_id,
33402
+ redirectUri,
33403
+ verifier,
33404
+ state,
33405
+ pair,
33406
+ returnTo,
33407
+ tokenEndpoint: cfg.token_endpoint,
33408
+ authorizationEndpoint: cfg.authorization_endpoint,
33409
+ revocationEndpoint: cfg.revocation_endpoint || null
33410
+ });
33411
+ const url = new URL(cfg.authorization_endpoint);
33412
+ for (const [k, v] of Object.entries({
33413
+ client_id,
33414
+ redirect_uri: redirectUri,
33415
+ response_type: "code",
33416
+ scope: "openid webid offline_access",
33417
+ code_challenge: challenge,
33418
+ code_challenge_method: "S256",
33419
+ state,
33420
+ prompt: "consent"
33421
+ })) url.searchParams.set(k, v);
33422
+ return { authorizationUrl: url.href };
33423
+ }
33424
+ async function completeLogin2({ currentUrl }) {
33425
+ const u = new URL(currentUrl);
33426
+ const code = u.searchParams.get("code");
33427
+ const state = u.searchParams.get("state");
33428
+ if (!code) return null;
33429
+ const p = await idbGet("pending");
33430
+ if (!p || p.state !== state) throw new Error("login state mismatch");
33431
+ const jwk = await publicJwk(p.pair);
33432
+ const res = await fetch(p.tokenEndpoint, {
33680
33433
  method: "POST",
33681
- headers: { "content-type": "application/x-www-form-urlencoded", dpop: await dpopProof(s.pair, jwk, "POST", s.tokenEndpoint) },
33682
- body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: s.client_id, scope: "openid webid offline_access" })
33434
+ headers: { "content-type": "application/x-www-form-urlencoded", dpop: await dpopProof(p.pair, jwk, "POST", p.tokenEndpoint) },
33435
+ body: new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri: p.redirectUri, client_id: p.client_id, code_verifier: p.verifier })
33683
33436
  });
33684
33437
  const tok = await res.json().catch(() => ({}));
33685
- if (!res.ok || !tok.access_token) {
33686
- await idbDel("session");
33687
- throw new Error("refresh failed \u2014 sign in again");
33688
- }
33689
- accessToken = tok.access_token;
33690
- expiresAt = Date.now() + Math.max(30, tok.expires_in || 300) * 1e3;
33691
- if (tok.refresh_token) refreshToken = tok.refresh_token;
33692
- const now = await idbGet("session");
33693
- if (now && now.webId !== s.webId) {
33694
- throw new Error("signed in as somebody else while this session was refreshing");
33695
- }
33696
- await idbPut("session", { ...s, accessToken, expiresAt, refreshToken });
33697
- };
33698
- const authFetch = async (url, init = {}) => {
33699
- if (Date.now() > expiresAt - 3e4) await refresh();
33700
- const jwk = await jwkP;
33701
- const ath = b64u2(await sha2562(accessToken));
33702
- const headers = { ...init.headers || {}, authorization: `DPoP ${accessToken}`, dpop: await dpopProof(s.pair, jwk, init.method || "GET", url, ath) };
33703
- return fetch(url, { ...init, headers });
33704
- };
33705
- return { webId: s.webId, fetch: authFetch, refresh, signOut };
33438
+ if (!res.ok || !tok.access_token) throw new Error(`token exchange failed (HTTP ${res.status}): ${tok.error || ""}`);
33439
+ const webId = jwtPayload(tok.access_token).webid || jwtPayload(tok.id_token || "").webid || null;
33440
+ const session = {
33441
+ issuer: p.issuer,
33442
+ client_id: p.client_id,
33443
+ tokenEndpoint: p.tokenEndpoint,
33444
+ pair: p.pair,
33445
+ // Where to hand the refresh token back at sign-out. Kept on the session
33446
+ // because discovery is a network round trip and sign-out should not need
33447
+ // one — see signOut().
33448
+ revocationEndpoint: p.revocationEndpoint || null,
33449
+ refreshToken: tok.refresh_token || null,
33450
+ accessToken: tok.access_token,
33451
+ expiresAt: Date.now() + Math.max(30, tok.expires_in || 300) * 1e3,
33452
+ webId
33453
+ };
33454
+ await idbPut("session", session);
33455
+ await idbDel("pending");
33456
+ return Object.assign(sessionHandle(session), p.returnTo ? { returnTo: p.returnTo } : {});
33457
+ }
33458
+ async function getSession2() {
33459
+ const s = await idbGet("session");
33460
+ return s ? sessionHandle(s) : null;
33461
+ }
33462
+ async function signOut2() {
33463
+ const s = await idbGet("session").catch(() => null);
33464
+ if (s?.revocationEndpoint && s.refreshToken) {
33465
+ try {
33466
+ await fetch(s.revocationEndpoint, {
33467
+ method: "POST",
33468
+ headers: { "content-type": "application/x-www-form-urlencoded" },
33469
+ body: new URLSearchParams({
33470
+ token: s.refreshToken,
33471
+ token_type_hint: "refresh_token",
33472
+ client_id: s.client_id
33473
+ })
33474
+ });
33475
+ } catch {
33476
+ }
33477
+ }
33478
+ await idbDel("session");
33479
+ await idbDel("pending");
33480
+ }
33481
+ function sessionHandle(s) {
33482
+ let { accessToken, expiresAt, refreshToken } = s;
33483
+ const jwkP = publicJwk(s.pair);
33484
+ const refresh = async () => {
33485
+ if (!refreshToken) throw new Error("session expired and there is no refresh token \u2014 sign in again");
33486
+ const jwk = await jwkP;
33487
+ let res;
33488
+ try {
33489
+ res = await fetch(s.tokenEndpoint, {
33490
+ method: "POST",
33491
+ headers: { "content-type": "application/x-www-form-urlencoded", dpop: await dpopProof(s.pair, jwk, "POST", s.tokenEndpoint) },
33492
+ body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: s.client_id, scope: "openid webid offline_access" })
33493
+ });
33494
+ } catch (e) {
33495
+ throw new Error(`${e.message} \u2014 renewing your sign-in at ${new URL(s.tokenEndpoint).host}`);
33496
+ }
33497
+ const tok = await res.json().catch(() => ({}));
33498
+ if (res.status === 429) {
33499
+ throw new Error(`${new URL(s.tokenEndpoint).host} is asking us to slow down \u2014 your sign-in could not be renewed just now`);
33500
+ }
33501
+ if (!res.ok || !tok.access_token) {
33502
+ await idbDel("session");
33503
+ throw new Error("refresh failed \u2014 sign in again");
33504
+ }
33505
+ accessToken = tok.access_token;
33506
+ expiresAt = Date.now() + Math.max(30, tok.expires_in || 300) * 1e3;
33507
+ if (tok.refresh_token) refreshToken = tok.refresh_token;
33508
+ const now = await idbGet("session");
33509
+ if (now && now.webId !== s.webId) {
33510
+ throw new Error("signed in as somebody else while this session was refreshing");
33511
+ }
33512
+ await idbPut("session", { ...s, accessToken, expiresAt, refreshToken });
33513
+ };
33514
+ const send = async (url, init) => {
33515
+ const jwk = await jwkP;
33516
+ const ath = b64u(await sha256(accessToken));
33517
+ const headers = { ...init.headers || {}, authorization: `DPoP ${accessToken}`, dpop: await dpopProof(s.pair, jwk, init.method || "GET", url, ath) };
33518
+ return fetch(url, { ...init, headers });
33519
+ };
33520
+ const authFetch = async (url, init = {}) => {
33521
+ if (Date.now() > expiresAt - 3e4) await refresh();
33522
+ const res = await send(url, init);
33523
+ if (res.status === 401 && refreshToken) {
33524
+ try {
33525
+ await refresh();
33526
+ } catch {
33527
+ return res;
33528
+ }
33529
+ return send(url, init);
33530
+ }
33531
+ return res;
33532
+ };
33533
+ return { webId: s.webId, issuer: s.issuer, fetch: authFetch, refresh, signOut: signOut2 };
33534
+ }
33535
+ return { beginLogin: beginLogin2, completeLogin: completeLogin2, getSession: getSession2, signOut: signOut2 };
33706
33536
  }
33707
33537
 
33538
+ // web/app/oidc-session.mjs
33539
+ var { beginLogin, completeLogin, getSession, signOut } = solidOidcSession({ dbName: "fedipod-oidc", clientName: "FediPod" });
33540
+
33708
33541
  // web/app/boot.mjs
33709
33542
  var REDIRECT = `${location.origin}/`;
33710
33543
  async function bootWorker({ reset = false } = {}) {
@@ -33755,7 +33588,7 @@ async function readAccountState() {
33755
33588
  const urls = { state };
33756
33589
  const [cfg, doc] = await Promise.all([
33757
33590
  readConfig(remote, urls),
33758
- readWrappedKeys(remote, urls)
33591
+ readKeys(remote, urls)
33759
33592
  ]);
33760
33593
  if (!cfg) throw new Error(`could not read this account's config under ${state}`);
33761
33594
  const actorUrl = `${cfg.remotePod}${cfg.root || AP_ROOT}ap/actor`;
@@ -33763,27 +33596,46 @@ async function readAccountState() {
33763
33596
  }
33764
33597
  window.fedipodUnlock = async (password) => {
33765
33598
  if (!password) throw new Error("Enter your password.");
33766
- const { doc, actorUrl } = await readAccountState();
33599
+ const { remote, urls, doc, actorUrl } = await readAccountState();
33767
33600
  if (!doc) throw new Error("could not read this account's key on the pod");
33768
- if (!isKeyEnvelope(doc)) throw new Error("this account's key is not locked \u2014 nothing to unlock");
33601
+ if (!isKeyEnvelope(doc)) throw new Error("this account's key is not under a password \u2014 nothing to unlock");
33769
33602
  const rec = await unwrapKeys(doc, password);
33603
+ await writeKeys(remote, urls, rec);
33770
33604
  await cacheOpenedKeys(actorUrl, rec);
33771
33605
  await bootWorker();
33772
33606
  };
33773
- window.fedipodNewKey = async (password) => {
33774
- if (!password) throw new Error("Enter the password you use for your pod now.");
33607
+ window.fedipodNewKey = async () => {
33775
33608
  const { remote, urls, cfg, actorUrl } = await readAccountState();
33776
33609
  const keys = await generateKeys();
33777
33610
  keys.mintedFor = cfg.gateway?.frontActor || actorUrl;
33778
- await writeWrappedKeys(remote, urls, await wrapKeys(keys, password));
33611
+ await writeKeys(remote, urls, keys);
33779
33612
  await cacheOpenedKeys(actorUrl, keys);
33780
33613
  await bootWorker();
33781
33614
  };
33782
- window.fedipodSignup = async ({ onStep, ...answers }) => {
33783
- await signUp(answers, { onStep, frontOrigin: location.origin });
33784
- const { authorizationUrl } = await beginLogin({ issuer: answers.issuer, redirectUri: REDIRECT });
33615
+ var SIGNUP_RETURN = "signup";
33616
+ var issuerOf = (v) => {
33617
+ let s = String(v || "").trim();
33618
+ if (!s) throw new Error("A pod provider is required.");
33619
+ if (!/^https?:\/\//i.test(s)) s = "https://" + s;
33620
+ return new URL(s).origin;
33621
+ };
33622
+ window.fedipodPodLogin = async ({ issuer }) => {
33623
+ const { authorizationUrl } = await beginLogin({ issuer: issuerOf(issuer), redirectUri: REDIRECT, returnTo: SIGNUP_RETURN });
33785
33624
  location.href = authorizationUrl;
33786
33625
  };
33626
+ window.fedipodSignup = async ({ onStep, ...answers }) => {
33627
+ const session = await getSession();
33628
+ if (!session) throw new Error("Sign in at your pod first.");
33629
+ await signUp(answers, { session, onStep, frontOrigin: location.origin });
33630
+ await bootWorker({ reset: true });
33631
+ };
33632
+ window.fedipodMoveIn = async ({ onStep, ...answers }) => {
33633
+ const session = await getSession();
33634
+ if (!session) throw new Error("Sign in at your pod first.");
33635
+ await moveIn(answers, { session, onStep, frontOrigin: location.origin });
33636
+ await bootWorker({ reset: true });
33637
+ };
33638
+ var moveFrom = null;
33787
33639
  function parseAddress(input) {
33788
33640
  let s = String(input || "").trim();
33789
33641
  if (s.startsWith("@")) s = s.slice(1);
@@ -33810,6 +33662,7 @@ async function podForFrontedAddress(handle) {
33810
33662
  `/.well-known/webfinger?resource=${encodeURIComponent(`acct:${handle}@${location.host}`)}`,
33811
33663
  { headers: { accept: "application/jrd+json, application/json" } }
33812
33664
  ).catch(() => null);
33665
+ if (res?.status === 410) throw new Error(`@${handle}@${location.host} is closed: nothing on the pod behind it was touched, but the address is gone for good.`);
33813
33666
  if (!res || res.status >= 400) throw new Error(`nobody at this site is called @${handle}@${location.host}`);
33814
33667
  const doc = await res.json().catch(() => ({}));
33815
33668
  const podActorId = (doc.aliases || []).find((a) => /\/ap\/actor$/u.test(String(a)));
@@ -33830,8 +33683,16 @@ window.fedipodSignin = async ({ address }) => {
33830
33683
  };
33831
33684
  window.fedipodOnLoad = async () => {
33832
33685
  if (new URLSearchParams(location.search).get("code")) {
33833
- await completeLogin({ currentUrl: location.href });
33686
+ const done = await completeLogin({ currentUrl: location.href });
33834
33687
  history.replaceState({}, "", REDIRECT);
33688
+ if (done?.returnTo === SIGNUP_RETURN) {
33689
+ const here = await readAccount(done).catch(() => null);
33690
+ if (!here) return "signup";
33691
+ if (here.frontHost && here.frontHost !== location.host) {
33692
+ moveFrom = here;
33693
+ return "move-in";
33694
+ }
33695
+ }
33835
33696
  await bootWorker({ reset: true });
33836
33697
  return "signed-in";
33837
33698
  }
@@ -33865,6 +33726,7 @@ window.fedipodHandleProblem = handleProblem;
33865
33726
  if (typeof document !== "undefined") (async () => {
33866
33727
  const $ = (id) => document.getElementById(id);
33867
33728
  const params = new URLSearchParams(location.search);
33729
+ let pendingIdentity = false;
33868
33730
  const doUnlock = async () => {
33869
33731
  $("unlock-error").textContent = "";
33870
33732
  const btn = $("unlock-go");
@@ -33892,13 +33754,12 @@ if (typeof document !== "undefined") (async () => {
33892
33754
  const btn = $("unlock-newkey-go");
33893
33755
  btn.disabled = true;
33894
33756
  try {
33895
- await window.fedipodNewKey($("unlock-password").value);
33757
+ await window.fedipodNewKey();
33896
33758
  $("unlock-password").value = "";
33897
33759
  location.href = "/admin/client/";
33898
33760
  } catch (err) {
33899
33761
  $("unlock-error").textContent = err.message || String(err);
33900
33762
  btn.disabled = false;
33901
- $("unlock-password").select();
33902
33763
  }
33903
33764
  };
33904
33765
  $("unlock-newkey-go")?.addEventListener("click", doNewKey);
@@ -33926,20 +33787,86 @@ if (typeof document !== "undefined") (async () => {
33926
33787
  $("unlock-password").focus();
33927
33788
  return;
33928
33789
  }
33790
+ const once = (key) => {
33791
+ try {
33792
+ if (sessionStorage.getItem(key)) return false;
33793
+ sessionStorage.setItem(key, "1");
33794
+ return true;
33795
+ } catch {
33796
+ return false;
33797
+ }
33798
+ };
33799
+ const reload = () => location.reload();
33800
+ const signIn = () => {
33801
+ location.href = "/?add";
33802
+ };
33803
+ const known = {
33804
+ "sign-in-refused": { title: "Your pod refused this sign-in", retry: "Sign in again", go: signIn },
33805
+ "pod-busy": { title: "Your pod is asking for a pause", retry: "Reload", go: reload, wait: 45 },
33806
+ "pod-error": { title: "Your pod had an error", retry: "Reload", go: reload, wait: 20 },
33807
+ "pod-unreachable": { title: "Your pod could not be reached", retry: "Reload", go: reload, wait: 20 },
33808
+ // A session with no account behind it is a sign-up that has not
33809
+ // happened yet — the person who closed the tab between the pod's
33810
+ // login and the identity screen lands here.
33811
+ "no-account-here": { title: "No FediPod account in that pod", retry: "Create one on this pod", go: () => showIdentity() },
33812
+ "no-account": { title: "No FediPod account in that pod", retry: "Create one on this pod", go: () => showIdentity() },
33813
+ "device-account": { title: "This account is run from a device", retry: "Use another pod", go: signIn },
33814
+ "address-closed": { title: "This address is closed", retry: "Sign in with another account", go: signIn }
33815
+ }[e.code];
33816
+ if (e.code === "sign-in-refused" && once("fedipod-renewing")) {
33817
+ $("loading").hidden = true;
33818
+ $("hero").hidden = true;
33819
+ $("landing").hidden = true;
33820
+ $("brand").hidden = false;
33821
+ $("running").hidden = false;
33822
+ $("running-title").textContent = "Renewing your sign-in";
33823
+ $("run-error").style.color = "var(--sub)";
33824
+ $("run-error").style.borderLeftColor = "var(--line)";
33825
+ $("run-error").textContent = "Your pod would not take the sign-in this browser is holding. Renewing it now.";
33826
+ $("run-actions").hidden = true;
33827
+ const session = await getSession().catch(() => null);
33828
+ if (session) {
33829
+ try {
33830
+ await session.refresh();
33831
+ location.reload();
33832
+ return;
33833
+ } catch {
33834
+ }
33835
+ try {
33836
+ const { authorizationUrl } = await beginLogin({ issuer: session.issuer, redirectUri: REDIRECT });
33837
+ location.href = authorizationUrl;
33838
+ return;
33839
+ } catch {
33840
+ }
33841
+ }
33842
+ }
33929
33843
  console.error(e);
33930
33844
  $("loading").hidden = true;
33931
33845
  $("hero").hidden = true;
33932
33846
  $("landing").hidden = true;
33933
33847
  $("brand").hidden = false;
33934
33848
  $("running").hidden = false;
33935
- $("running-title").textContent = "Signed in, but the agent could not start";
33849
+ $("running-title").textContent = known ? known.title : "Signed in, but the agent could not start";
33936
33850
  $("run-error").style.whiteSpace = "pre-wrap";
33937
- $("run-error").textContent = (e.message || String(e)) + (e.detail ? `
33851
+ $("run-error").textContent = (e.message || String(e)) + (!known && e.detail ? `
33938
33852
 
33939
33853
  ${e.detail}` : "");
33940
33854
  $("run-actions").hidden = false;
33941
- $("run-retry").textContent = "Reload";
33942
- $("run-retry").addEventListener("click", () => location.reload());
33855
+ $("run-actions").prepend($("run-retry"));
33856
+ $("run-retry").textContent = known ? known.retry : "Reload";
33857
+ $("run-retry").addEventListener("click", known ? known.go : reload);
33858
+ if (known?.wait && once(`fedipod-waited-${e.code}`)) {
33859
+ let left = known.wait;
33860
+ const tick = () => {
33861
+ $("run-retry").textContent = left > 0 ? `Trying again in ${left}s` : "Trying again\u2026";
33862
+ if (left-- <= 0) {
33863
+ clearInterval(timer);
33864
+ location.reload();
33865
+ }
33866
+ };
33867
+ const timer = setInterval(tick, 1e3);
33868
+ tick();
33869
+ }
33943
33870
  $("run-back").textContent = "Sign out";
33944
33871
  $("run-back").addEventListener("click", async () => {
33945
33872
  try {
@@ -33954,8 +33881,11 @@ ${e.detail}` : "");
33954
33881
  location.href = "/admin/client/";
33955
33882
  return;
33956
33883
  }
33957
- $("loading").hidden = true;
33958
- $("landing").hidden = false;
33884
+ if (state === "signup" || state === "move-in") pendingIdentity = true;
33885
+ else {
33886
+ $("loading").hidden = true;
33887
+ $("landing").hidden = false;
33888
+ }
33959
33889
  }
33960
33890
  const showLanding = () => {
33961
33891
  $("pane-form").hidden = true;
@@ -33971,6 +33901,29 @@ ${e.detail}` : "");
33971
33901
  $("pane-form").hidden = false;
33972
33902
  goStep(1);
33973
33903
  };
33904
+ let signedPod = "";
33905
+ async function showIdentity() {
33906
+ const session = await getSession();
33907
+ if (!session) {
33908
+ showLanding();
33909
+ return;
33910
+ }
33911
+ signedPod = podBaseOfWebId(session.webId);
33912
+ $("loading").hidden = true;
33913
+ $("hero").hidden = true;
33914
+ $("landing").hidden = true;
33915
+ $("running").hidden = true;
33916
+ $("brand").hidden = false;
33917
+ $("pane-form").hidden = false;
33918
+ $("signed-pod").textContent = signedPod;
33919
+ $("movein-note").hidden = !moveFrom;
33920
+ if (moveFrom) {
33921
+ $("movein-from").textContent = moveFrom.address;
33922
+ if (!f().handle.value) f().handle.value = moveFrom.config.handle;
33923
+ $("submit").textContent = "Move your account here";
33924
+ }
33925
+ goStep(2);
33926
+ }
33974
33927
  const doSignin = async () => {
33975
33928
  $("signin-error").textContent = "";
33976
33929
  try {
@@ -34000,91 +33953,52 @@ ${e.detail}` : "");
34000
33953
  return "";
34001
33954
  }
34002
33955
  };
34003
- const podHostOf = () => {
34004
- const sub = f().podName.value.trim().toLowerCase();
34005
- const ph = providerHost();
34006
- return sub && ph ? `${sub}.${ph}` : "";
33956
+ const registerUrl = () => {
33957
+ if (!providerHost()) return "";
33958
+ const origin = new URL(providerUrl()).origin;
33959
+ return f().provider.value ? `${origin}/.account/login/password/register/` : `${origin}/`;
34007
33960
  };
34008
- const podUrl = () => {
34009
- let v = f().pod.value.trim();
34010
- if (!v) return "";
34011
- if (!/^https?:\/\//i.test(v)) v = "https://" + v;
34012
- if (!v.endsWith("/")) v += "/";
33961
+ const pathPod = () => {
34013
33962
  try {
34014
- return new URL(v).href;
34015
- } catch {
34016
- return "";
34017
- }
34018
- };
34019
- const isPathPod = (u) => {
34020
- try {
34021
- return new URL(u).pathname !== "/";
33963
+ return new URL(signedPod).pathname !== "/";
34022
33964
  } catch {
34023
33965
  return false;
34024
33966
  }
34025
33967
  };
34026
- const layouts = /* @__PURE__ */ new Map();
34027
- let layout = null;
34028
- const learnLayout = async () => {
34029
- const origin = providerHost() ? new URL(providerUrl()).origin : "";
34030
- if (!origin) {
34031
- layout = null;
34032
- return;
34033
- }
34034
- if (!layouts.has(origin)) layouts.set(origin, podLayout(fetch, origin).catch(() => null));
34035
- const known = await layouts.get(origin);
34036
- if (providerHost() && new URL(providerUrl()).origin === origin) {
34037
- layout = known;
34038
- applyShape();
34039
- previewAddr();
34040
- }
34041
- };
34042
- const pathPod = () => f().mode.value === "existing" ? isPathPod(podUrl()) : layout === "path";
34043
- const shape = () => pathPod() ? "front" : f().shape.value;
34044
- const answers = () => {
34045
- const mode = f().mode.value;
34046
- const a = {
34047
- mode,
34048
- handle: f().handle.value.trim().toLowerCase(),
34049
- email: f().email.value.trim(),
34050
- password: f().password.value,
34051
- issuer: providerUrl(),
34052
- shape: shape()
34053
- };
34054
- if (mode === "new") a.podName = f().podName.value.trim().toLowerCase();
34055
- else a.pod = podUrl();
34056
- return a;
34057
- };
33968
+ const shape = () => pathPod() || moveFrom ? "front" : f().shape.value;
33969
+ const answers = () => ({ handle: f().handle.value.trim().toLowerCase(), shape: shape() });
34058
33970
  const previewAddr = () => {
34059
33971
  const handle = f().handle.value.trim().toLowerCase();
34060
- const host = shape() === "front" ? location.host : f().mode.value === "existing" ? podUrl() ? new URL(podUrl()).host : "" : podHostOf();
33972
+ let host = "";
33973
+ try {
33974
+ host = shape() === "front" ? location.host : new URL(signedPod).host;
33975
+ } catch {
33976
+ host = "";
33977
+ }
34061
33978
  $("preview").textContent = handle && host ? `@${handle}@${host}` : "@\u2026@\u2026";
34062
33979
  };
34063
33980
  const applyShape = () => {
34064
- const fixed = pathPod();
33981
+ const fixed = pathPod() || !!moveFrom;
34065
33982
  for (const r of f().shape) {
34066
33983
  if (fixed) r.checked = r.value === "front";
34067
33984
  }
34068
33985
  $("shape-group").hidden = fixed;
34069
- $("shape-hint").hidden = !fixed;
33986
+ $("shape-hint").hidden = !fixed || !!moveFrom;
34070
33987
  };
34071
33988
  const applyMode = () => {
34072
33989
  const existing = f().mode.value === "existing";
34073
- $("pod-field").hidden = !existing;
34074
- $("podname-field").hidden = existing;
33990
+ $("newpod-field").hidden = existing;
34075
33991
  $("provider-other-field").hidden = f().provider.value !== "";
33992
+ const url = registerUrl();
33993
+ $("register-link").href = url || "#";
33994
+ $("register-link").textContent = providerHost() ? `Create your pod at ${providerHost()}` : "Create your pod at your provider";
34076
33995
  };
34077
33996
  for (const el of $("form").elements) for (const evt of ["input", "change"]) el.addEventListener(evt, () => {
34078
33997
  applyMode();
34079
33998
  applyShape();
34080
33999
  previewAddr();
34081
34000
  });
34082
- for (const evt of ["input", "change"]) {
34083
- $("provider").addEventListener(evt, learnLayout);
34084
- $("providerOther").addEventListener(evt, learnLayout);
34085
- }
34086
34001
  applyMode();
34087
- learnLayout();
34088
34002
  const STEP_IDS = ["step-1", "step-2"];
34089
34003
  const FOCUS = { 1: "provider", 2: "handle" };
34090
34004
  const goStep = (n) => {
@@ -34096,22 +34010,11 @@ ${e.detail}` : "");
34096
34010
  if (n === 2) {
34097
34011
  applyShape();
34098
34012
  previewAddr();
34099
- learnLayout();
34100
34013
  }
34101
34014
  if (FOCUS[n]) $(FOCUS[n]).focus();
34102
34015
  };
34103
34016
  const validateStep1 = () => {
34104
34017
  if (!providerHost()) return f().provider.value === "" ? "A pod provider address is required under Other\u2026." : "A valid pod provider URL is required.";
34105
- if (f().mode.value === "existing") {
34106
- if (!podUrl()) return "A pod address is required, like https://alice.solidcommunity.net/ or https://server.example/alice/.";
34107
- } else {
34108
- const sub = f().podName.value.trim().toLowerCase();
34109
- if (!sub) return "A pod username/subdomain is required.";
34110
- const sp = window.fedipodHandleProblem(sub);
34111
- if (sp) return `Pod username: ${sp}`;
34112
- }
34113
- if (!f().email.value.trim()) return "A pod email is required.";
34114
- if (!f().password.value) return "A pod password is required.";
34115
34018
  return null;
34116
34019
  };
34117
34020
  const validateStep2 = () => {
@@ -34124,38 +34027,39 @@ ${e.detail}` : "");
34124
34027
  showLanding();
34125
34028
  goStep(1);
34126
34029
  });
34127
- $("to-2").addEventListener("click", () => {
34030
+ $("to-2").addEventListener("click", async () => {
34128
34031
  const e = validateStep1();
34129
34032
  if (e) {
34130
34033
  $("err-1").textContent = e;
34131
34034
  return;
34132
34035
  }
34133
- goStep(2);
34036
+ $("err-1").textContent = "";
34037
+ $("to-2").disabled = true;
34038
+ try {
34039
+ await window.fedipodPodLogin({ issuer: providerUrl() });
34040
+ } catch (err) {
34041
+ $("err-1").textContent = err.message || String(err);
34042
+ $("to-2").disabled = false;
34043
+ }
34134
34044
  });
34135
34045
  $("back-1").addEventListener("click", () => goStep(1));
34136
34046
  const backToForm = (errMsg) => {
34137
34047
  $("running").hidden = true;
34138
34048
  $("brand").hidden = false;
34139
34049
  $("pane-form").hidden = false;
34140
- goStep(1);
34141
- if (errMsg) $("err-1").textContent = errMsg;
34050
+ goStep(2);
34051
+ if (errMsg) $("form-error").textContent = errMsg;
34142
34052
  };
34143
34053
  $("run-retry").addEventListener("click", () => backToForm($("run-error").textContent));
34144
34054
  $("run-back").addEventListener("click", () => {
34145
34055
  showLanding();
34146
34056
  goStep(1);
34147
34057
  });
34148
- const LABELS = { account: "Creating your account and pod", credential: "Preparing this browser", keys: "Making your signing key", gateway: "Connecting your mail door" };
34058
+ const LABELS = { pod: "Checking your pod", keys: "Making your signing key", gateway: "Connecting your mail door" };
34059
+ const MOVE_LABELS = { pod: "Reading your account on your pod", gateway: "Taking your address here", keys: "Moving your key and account record" };
34149
34060
  $("form").addEventListener("submit", async (e) => {
34150
34061
  e.preventDefault();
34151
34062
  $("form-error").textContent = "";
34152
- const e1 = validateStep1();
34153
- if (e1) {
34154
- $("form-error").textContent = e1;
34155
- goStep(1);
34156
- $("err-1").textContent = e1;
34157
- return;
34158
- }
34159
34063
  const e2 = validateStep2();
34160
34064
  if (e2) {
34161
34065
  $("form-error").textContent = e2;
@@ -34171,22 +34075,26 @@ ${e.detail}` : "");
34171
34075
  const steps = $("steps");
34172
34076
  steps.textContent = "";
34173
34077
  const mark = {};
34078
+ const labels = moveFrom ? MOVE_LABELS : LABELS;
34174
34079
  const onStep = (k, st2) => {
34175
34080
  if (!mark[k]) {
34176
34081
  const li = document.createElement("li");
34177
34082
  steps.appendChild(li);
34178
34083
  mark[k] = li;
34179
34084
  }
34180
- mark[k].textContent = (st2 === "ok" ? "\u2713 " : st2 === "running" ? "\u2026 " : "") + (LABELS[k] || k);
34085
+ mark[k].textContent = (st2 === "ok" ? "\u2713 " : st2 === "running" ? "\u2026 " : "") + (labels[k] || k);
34181
34086
  };
34182
34087
  try {
34183
- await window.fedipodSignup({ ...a, onStep });
34088
+ if (moveFrom) await window.fedipodMoveIn({ handle: a.handle, onStep });
34089
+ else await window.fedipodSignup({ ...a, onStep });
34090
+ location.href = "/admin/client/";
34184
34091
  } catch (err) {
34185
34092
  $("running-title").textContent = "Setup did not finish";
34186
34093
  $("run-error").textContent = err.message || String(err);
34187
34094
  $("run-actions").hidden = false;
34188
34095
  }
34189
34096
  });
34097
+ if (pendingIdentity) await showIdentity();
34190
34098
  })();
34191
34099
  export {
34192
34100
  parseAddress