@atproto/oauth-provider 0.4.0 → 0.5.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 (402) hide show
  1. package/.linguirc +57 -0
  2. package/CHANGELOG.md +29 -0
  3. package/dist/account/account-manager.d.ts +17 -3
  4. package/dist/account/account-manager.d.ts.map +1 -1
  5. package/dist/account/account-manager.js +102 -8
  6. package/dist/account/account-manager.js.map +1 -1
  7. package/dist/account/account-store.d.ts +81 -15
  8. package/dist/account/account-store.d.ts.map +1 -1
  9. package/dist/account/account-store.js +70 -19
  10. package/dist/account/account-store.js.map +1 -1
  11. package/dist/account/sign-in-data.d.ts +28 -0
  12. package/dist/account/sign-in-data.d.ts.map +1 -0
  13. package/dist/account/sign-in-data.js +16 -0
  14. package/dist/account/sign-in-data.js.map +1 -0
  15. package/dist/account/sign-up-data.d.ts +26 -0
  16. package/dist/account/sign-up-data.d.ts.map +1 -0
  17. package/dist/account/sign-up-data.js +11 -0
  18. package/dist/account/sign-up-data.js.map +1 -0
  19. package/dist/assets/app/bundle-manifest.json +598 -6
  20. package/dist/assets/app/index-ItwwtJ8r.js +36 -0
  21. package/dist/assets/app/index-ItwwtJ8r.js.map +1 -0
  22. package/dist/assets/app/main-B_dNxQo_.js +4 -0
  23. package/dist/assets/app/main-B_dNxQo_.js.map +1 -0
  24. package/dist/assets/app/main-CSatvmRR.css +3 -0
  25. package/dist/assets/app/main-CSatvmRR.js +306 -0
  26. package/dist/assets/app/main-CSatvmRR.js.map +1 -0
  27. package/dist/assets/app/messages-BQeltXSF.js +4 -0
  28. package/dist/assets/app/messages-BQeltXSF.js.map +1 -0
  29. package/dist/assets/app/messages-BQkEhfjg.js +4 -0
  30. package/dist/assets/app/messages-BQkEhfjg.js.map +1 -0
  31. package/dist/assets/app/messages-BUjKj_UJ.js +4 -0
  32. package/dist/assets/app/messages-BUjKj_UJ.js.map +1 -0
  33. package/dist/assets/app/messages-BWIQa8fO.js +4 -0
  34. package/dist/assets/app/messages-BWIQa8fO.js.map +1 -0
  35. package/dist/assets/app/messages-BaNVb0bp.js +4 -0
  36. package/dist/assets/app/messages-BaNVb0bp.js.map +1 -0
  37. package/dist/assets/app/messages-BaizVXcF.js +4 -0
  38. package/dist/assets/app/messages-BaizVXcF.js.map +1 -0
  39. package/dist/assets/app/messages-BfoClA1Y.js +4 -0
  40. package/dist/assets/app/messages-BfoClA1Y.js.map +1 -0
  41. package/dist/assets/app/messages-BsKGDZnC.js +4 -0
  42. package/dist/assets/app/messages-BsKGDZnC.js.map +1 -0
  43. package/dist/assets/app/messages-Bu-TJhml.js +4 -0
  44. package/dist/assets/app/messages-Bu-TJhml.js.map +1 -0
  45. package/dist/assets/app/messages-BvOKnBQk.js +4 -0
  46. package/dist/assets/app/messages-BvOKnBQk.js.map +1 -0
  47. package/dist/assets/app/messages-BxDzCiWz.js +4 -0
  48. package/dist/assets/app/messages-BxDzCiWz.js.map +1 -0
  49. package/dist/assets/app/messages-CDgFOy4S.js +4 -0
  50. package/dist/assets/app/messages-CDgFOy4S.js.map +1 -0
  51. package/dist/assets/app/messages-CLbTz0o9.js +4 -0
  52. package/dist/assets/app/messages-CLbTz0o9.js.map +1 -0
  53. package/dist/assets/app/messages-CNwSh0t7.js +4 -0
  54. package/dist/assets/app/messages-CNwSh0t7.js.map +1 -0
  55. package/dist/assets/app/messages-CSMNJ6P8.js +4 -0
  56. package/dist/assets/app/messages-CSMNJ6P8.js.map +1 -0
  57. package/dist/assets/app/messages-CZQUw3mp.js +4 -0
  58. package/dist/assets/app/messages-CZQUw3mp.js.map +1 -0
  59. package/dist/assets/app/messages-CZT41oVp.js +4 -0
  60. package/dist/assets/app/messages-CZT41oVp.js.map +1 -0
  61. package/dist/assets/app/messages-C_b-d3t8.js +4 -0
  62. package/dist/assets/app/messages-C_b-d3t8.js.map +1 -0
  63. package/dist/assets/app/messages-C_u3MTc2.js +4 -0
  64. package/dist/assets/app/messages-C_u3MTc2.js.map +1 -0
  65. package/dist/assets/app/messages-Cn8nHZic.js +4 -0
  66. package/dist/assets/app/messages-Cn8nHZic.js.map +1 -0
  67. package/dist/assets/app/messages-CtDywJUm.js +4 -0
  68. package/dist/assets/app/messages-CtDywJUm.js.map +1 -0
  69. package/dist/assets/app/messages-CurtIjBF.js +4 -0
  70. package/dist/assets/app/messages-CurtIjBF.js.map +1 -0
  71. package/dist/assets/app/messages-Cv6zIbaP.js +4 -0
  72. package/dist/assets/app/messages-Cv6zIbaP.js.map +1 -0
  73. package/dist/assets/app/messages-D1eLQuPE.js +4 -0
  74. package/dist/assets/app/messages-D1eLQuPE.js.map +1 -0
  75. package/dist/assets/app/messages-D8vHEaYW.js +4 -0
  76. package/dist/assets/app/messages-D8vHEaYW.js.map +1 -0
  77. package/dist/assets/app/messages-DJ1Q4GeC.js +4 -0
  78. package/dist/assets/app/messages-DJ1Q4GeC.js.map +1 -0
  79. package/dist/assets/app/messages-DRL3exqd.js +4 -0
  80. package/dist/assets/app/messages-DRL3exqd.js.map +1 -0
  81. package/dist/assets/app/messages-DWLPQRTp.js +4 -0
  82. package/dist/assets/app/messages-DWLPQRTp.js.map +1 -0
  83. package/dist/assets/app/messages-DjVaE9YE.js +4 -0
  84. package/dist/assets/app/messages-DjVaE9YE.js.map +1 -0
  85. package/dist/assets/app/messages-DqpMfFJR.js +4 -0
  86. package/dist/assets/app/messages-DqpMfFJR.js.map +1 -0
  87. package/dist/assets/app/messages-ETjhJBEN.js +4 -0
  88. package/dist/assets/app/messages-ETjhJBEN.js.map +1 -0
  89. package/dist/assets/app/messages-EUKrgrGn.js +4 -0
  90. package/dist/assets/app/messages-EUKrgrGn.js.map +1 -0
  91. package/dist/assets/app/messages-QQrOUcPW.js +4 -0
  92. package/dist/assets/app/messages-QQrOUcPW.js.map +1 -0
  93. package/dist/assets/app/messages-e2QGqFL6.js +4 -0
  94. package/dist/assets/app/messages-e2QGqFL6.js.map +1 -0
  95. package/dist/assets/app/messages-p61py7gD.js +4 -0
  96. package/dist/assets/app/messages-p61py7gD.js.map +1 -0
  97. package/dist/assets/asset.d.ts +1 -0
  98. package/dist/assets/asset.d.ts.map +1 -1
  99. package/dist/assets/assets-middleware.d.ts.map +1 -1
  100. package/dist/assets/assets-middleware.js +12 -7
  101. package/dist/assets/assets-middleware.js.map +1 -1
  102. package/dist/assets/index.d.ts +3 -2
  103. package/dist/assets/index.d.ts.map +1 -1
  104. package/dist/assets/index.js +13 -1
  105. package/dist/assets/index.js.map +1 -1
  106. package/dist/client/client-store.d.ts +3 -3
  107. package/dist/client/client-store.d.ts.map +1 -1
  108. package/dist/client/client-store.js +6 -5
  109. package/dist/client/client-store.js.map +1 -1
  110. package/dist/device/device-manager.d.ts +9 -8
  111. package/dist/device/device-manager.d.ts.map +1 -1
  112. package/dist/device/device-manager.js.map +1 -1
  113. package/dist/device/device-store.d.ts +3 -3
  114. package/dist/device/device-store.d.ts.map +1 -1
  115. package/dist/device/device-store.js +10 -9
  116. package/dist/device/device-store.js.map +1 -1
  117. package/dist/dpop/dpop-manager.d.ts +15 -7
  118. package/dist/dpop/dpop-manager.d.ts.map +1 -1
  119. package/dist/dpop/dpop-manager.js +17 -3
  120. package/dist/dpop/dpop-manager.js.map +1 -1
  121. package/dist/dpop/dpop-nonce.d.ts +11 -5
  122. package/dist/dpop/dpop-nonce.d.ts.map +1 -1
  123. package/dist/dpop/dpop-nonce.js +47 -38
  124. package/dist/dpop/dpop-nonce.js.map +1 -1
  125. package/dist/errors/handle-unavailable-error.d.ts +11 -0
  126. package/dist/errors/handle-unavailable-error.d.ts.map +1 -0
  127. package/dist/errors/handle-unavailable-error.js +19 -0
  128. package/dist/errors/handle-unavailable-error.js.map +1 -0
  129. package/dist/errors/invalid-request-error.d.ts +6 -8
  130. package/dist/errors/invalid-request-error.d.ts.map +1 -1
  131. package/dist/errors/invalid-request-error.js +10 -8
  132. package/dist/errors/invalid-request-error.js.map +1 -1
  133. package/dist/lib/csp/index.d.ts +18 -0
  134. package/dist/lib/csp/index.d.ts.map +1 -0
  135. package/dist/lib/csp/index.js +72 -0
  136. package/dist/lib/csp/index.js.map +1 -0
  137. package/dist/lib/hcaptcha.d.ts +177 -0
  138. package/dist/lib/hcaptcha.d.ts.map +1 -0
  139. package/dist/lib/hcaptcha.js +155 -0
  140. package/dist/lib/hcaptcha.js.map +1 -0
  141. package/dist/lib/html/build-document.d.ts +11 -3
  142. package/dist/lib/html/build-document.d.ts.map +1 -1
  143. package/dist/lib/html/build-document.js +51 -15
  144. package/dist/lib/html/build-document.js.map +1 -1
  145. package/dist/lib/http/middleware.d.ts.map +1 -1
  146. package/dist/lib/http/middleware.js +4 -1
  147. package/dist/lib/http/middleware.js.map +1 -1
  148. package/dist/lib/http/request.d.ts +5 -2
  149. package/dist/lib/http/request.d.ts.map +1 -1
  150. package/dist/lib/http/request.js +16 -1
  151. package/dist/lib/http/request.js.map +1 -1
  152. package/dist/lib/http/response.d.ts +4 -2
  153. package/dist/lib/http/response.d.ts.map +1 -1
  154. package/dist/lib/http/response.js +23 -5
  155. package/dist/lib/http/response.js.map +1 -1
  156. package/dist/lib/locale.d.ts +15 -0
  157. package/dist/lib/locale.d.ts.map +1 -0
  158. package/dist/lib/locale.js +17 -0
  159. package/dist/lib/locale.js.map +1 -0
  160. package/dist/lib/util/function.d.ts +2 -2
  161. package/dist/lib/util/function.d.ts.map +1 -1
  162. package/dist/lib/util/function.js.map +1 -1
  163. package/dist/lib/util/type.d.ts +88 -1
  164. package/dist/lib/util/type.d.ts.map +1 -1
  165. package/dist/lib/util/type.js +41 -0
  166. package/dist/lib/util/type.js.map +1 -1
  167. package/dist/metadata/build-metadata.d.ts +2 -2
  168. package/dist/metadata/build-metadata.d.ts.map +1 -1
  169. package/dist/metadata/build-metadata.js.map +1 -1
  170. package/dist/oauth-errors.d.ts +1 -0
  171. package/dist/oauth-errors.d.ts.map +1 -1
  172. package/dist/oauth-errors.js +3 -1
  173. package/dist/oauth-errors.js.map +1 -1
  174. package/dist/oauth-hooks.d.ts +60 -3
  175. package/dist/oauth-hooks.d.ts.map +1 -1
  176. package/dist/oauth-hooks.js +3 -3
  177. package/dist/oauth-hooks.js.map +1 -1
  178. package/dist/oauth-provider.d.ts +23 -18
  179. package/dist/oauth-provider.d.ts.map +1 -1
  180. package/dist/oauth-provider.js +207 -204
  181. package/dist/oauth-provider.js.map +1 -1
  182. package/dist/oauth-verifier.d.ts +1 -1
  183. package/dist/oauth-verifier.d.ts.map +1 -1
  184. package/dist/oauth-verifier.js +2 -1
  185. package/dist/oauth-verifier.js.map +1 -1
  186. package/dist/output/build-authorize-data.d.ts +0 -1
  187. package/dist/output/build-authorize-data.d.ts.map +1 -1
  188. package/dist/output/build-authorize-data.js +0 -1
  189. package/dist/output/build-authorize-data.js.map +1 -1
  190. package/dist/output/build-customization-data.d.ts +241 -0
  191. package/dist/output/build-customization-data.d.ts.map +1 -0
  192. package/dist/output/build-customization-data.js +174 -0
  193. package/dist/output/build-customization-data.js.map +1 -0
  194. package/dist/output/output-manager.d.ts +16 -9
  195. package/dist/output/output-manager.d.ts.map +1 -1
  196. package/dist/output/output-manager.js +78 -42
  197. package/dist/output/output-manager.js.map +1 -1
  198. package/dist/output/send-authorize-redirect.d.ts +9 -6
  199. package/dist/output/send-authorize-redirect.d.ts.map +1 -1
  200. package/dist/output/send-authorize-redirect.js +20 -14
  201. package/dist/output/send-authorize-redirect.js.map +1 -1
  202. package/dist/output/send-web-page.d.ts +7 -2
  203. package/dist/output/send-web-page.d.ts.map +1 -1
  204. package/dist/output/send-web-page.js +37 -21
  205. package/dist/output/send-web-page.js.map +1 -1
  206. package/dist/request/request-manager.d.ts +1 -1
  207. package/dist/request/request-manager.d.ts.map +1 -1
  208. package/dist/request/request-manager.js +4 -4
  209. package/dist/request/request-manager.js.map +1 -1
  210. package/dist/request/request-store.d.ts +3 -3
  211. package/dist/request/request-store.d.ts.map +1 -1
  212. package/dist/request/request-store.js +11 -10
  213. package/dist/request/request-store.js.map +1 -1
  214. package/dist/token/token-store.d.ts +4 -4
  215. package/dist/token/token-store.d.ts.map +1 -1
  216. package/dist/token/token-store.js +13 -12
  217. package/dist/token/token-store.js.map +1 -1
  218. package/package.json +43 -20
  219. package/rollup.config.js +61 -17
  220. package/src/account/account-manager.ts +159 -8
  221. package/src/account/account-store.ts +127 -32
  222. package/src/account/sign-in-data.ts +15 -0
  223. package/src/account/sign-up-data.ts +11 -0
  224. package/src/assets/app/app.tsx +31 -16
  225. package/src/assets/app/backend-data.ts +15 -60
  226. package/src/assets/app/backend-types.ts +66 -0
  227. package/src/assets/app/components/forms/button-toggle-visibility.tsx +43 -0
  228. package/src/assets/app/components/forms/button.tsx +60 -0
  229. package/src/assets/app/components/forms/fieldset.tsx +55 -0
  230. package/src/assets/app/components/forms/form-card-async.tsx +103 -0
  231. package/src/assets/app/components/forms/form-card.tsx +49 -0
  232. package/src/assets/app/components/forms/input-checkbox.tsx +73 -0
  233. package/src/assets/app/components/forms/input-container.tsx +107 -0
  234. package/src/assets/app/components/forms/input-email-address.tsx +66 -0
  235. package/src/assets/app/components/forms/input-new-password.tsx +62 -0
  236. package/src/assets/app/components/forms/input-password.tsx +88 -0
  237. package/src/assets/app/components/forms/input-text.tsx +76 -0
  238. package/src/assets/app/components/forms/input-token.tsx +94 -0
  239. package/src/assets/app/components/forms/wizard-card.tsx +116 -0
  240. package/src/assets/app/components/layouts/layout-title-page.tsx +77 -0
  241. package/src/assets/app/components/layouts/layout-welcome.tsx +73 -0
  242. package/src/assets/app/components/utils/account-identifier.tsx +23 -0
  243. package/src/assets/app/components/utils/account-image.tsx +33 -0
  244. package/src/assets/app/components/utils/admonition.tsx +52 -0
  245. package/src/assets/app/components/utils/client-name.tsx +45 -0
  246. package/src/assets/app/components/utils/error-card.tsx +93 -0
  247. package/src/assets/app/components/utils/error-message.tsx +62 -0
  248. package/src/assets/app/components/utils/help-card.tsx +46 -0
  249. package/src/assets/app/components/utils/icons.tsx +88 -0
  250. package/src/assets/app/components/utils/link-anchor.tsx +28 -0
  251. package/src/assets/app/components/utils/link-title.tsx +26 -0
  252. package/src/assets/app/components/utils/multi-lang-string.tsx +56 -0
  253. package/src/assets/app/components/utils/password-strength-label.tsx +37 -0
  254. package/src/assets/app/components/utils/password-strength-meter.tsx +58 -0
  255. package/src/assets/app/components/{url-viewer.tsx → utils/url-viewer.tsx} +9 -6
  256. package/src/assets/app/hooks/use-api.ts +128 -55
  257. package/src/assets/app/hooks/use-async-action.ts +120 -0
  258. package/src/assets/app/hooks/use-browser-color-scheme.ts +31 -0
  259. package/src/assets/app/hooks/use-csrf-token.ts +1 -1
  260. package/src/assets/app/hooks/use-random-string.ts +37 -0
  261. package/src/assets/app/hooks/use-stepper.ts +87 -0
  262. package/src/assets/app/index.html +182 -0
  263. package/src/assets/app/lib/api.ts +248 -79
  264. package/src/assets/app/lib/clsx.ts +5 -8
  265. package/src/assets/app/lib/json-client.ts +94 -0
  266. package/src/assets/app/lib/password.ts +98 -0
  267. package/src/assets/app/lib/ref.ts +17 -0
  268. package/src/assets/app/locales/an/messages.po +492 -0
  269. package/src/assets/app/locales/ast/messages.po +492 -0
  270. package/src/assets/app/locales/ca/messages.po +492 -0
  271. package/src/assets/app/locales/da/messages.po +492 -0
  272. package/src/assets/app/locales/de/messages.po +492 -0
  273. package/src/assets/app/locales/el/messages.po +492 -0
  274. package/src/assets/app/locales/en/messages.po +492 -0
  275. package/src/assets/app/locales/en-GB/messages.po +492 -0
  276. package/src/assets/app/locales/es/messages.po +492 -0
  277. package/src/assets/app/locales/eu/messages.po +492 -0
  278. package/src/assets/app/locales/fi/messages.po +492 -0
  279. package/src/assets/app/locales/fr/messages.po +492 -0
  280. package/src/assets/app/locales/ga/messages.po +492 -0
  281. package/src/assets/app/locales/gl/messages.po +492 -0
  282. package/src/assets/app/locales/hi/messages.po +492 -0
  283. package/src/assets/app/locales/hu/messages.po +492 -0
  284. package/src/assets/app/locales/ia/messages.po +492 -0
  285. package/src/assets/app/locales/id/messages.po +492 -0
  286. package/src/assets/app/locales/it/messages.po +492 -0
  287. package/src/assets/app/locales/ja/messages.po +492 -0
  288. package/src/assets/app/locales/km/messages.po +492 -0
  289. package/src/assets/app/locales/ko/messages.po +492 -0
  290. package/src/assets/app/locales/load.ts +8 -0
  291. package/src/assets/app/locales/locale-context.ts +19 -0
  292. package/src/assets/app/locales/locale-provider.tsx +112 -0
  293. package/src/assets/app/locales/locale-selector.tsx +58 -0
  294. package/src/assets/app/locales/locales.ts +168 -0
  295. package/src/assets/app/locales/ne/messages.po +492 -0
  296. package/src/assets/app/locales/nl/messages.po +492 -0
  297. package/src/assets/app/locales/pl/messages.po +492 -0
  298. package/src/assets/app/locales/pt-BR/messages.po +492 -0
  299. package/src/assets/app/locales/ro/messages.po +492 -0
  300. package/src/assets/app/locales/ru/messages.po +492 -0
  301. package/src/assets/app/locales/sv/messages.po +492 -0
  302. package/src/assets/app/locales/th/messages.po +492 -0
  303. package/src/assets/app/locales/tr/messages.po +492 -0
  304. package/src/assets/app/locales/uk/messages.po +492 -0
  305. package/src/assets/app/locales/vi/messages.po +492 -0
  306. package/src/assets/app/locales/zh-CN/messages.po +492 -0
  307. package/src/assets/app/locales/zh-HK/messages.po +492 -0
  308. package/src/assets/app/locales/zh-TW/messages.po +492 -0
  309. package/src/assets/app/main.css +23 -2
  310. package/src/assets/app/main.tsx +24 -8
  311. package/src/assets/app/views/authorize/accept/accept-form.tsx +150 -0
  312. package/src/assets/app/views/authorize/accept/accept-view.tsx +70 -0
  313. package/src/assets/app/views/authorize/authorize-view.tsx +180 -0
  314. package/src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx +88 -0
  315. package/src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx +80 -0
  316. package/src/assets/app/views/authorize/reset-password/reset-password-view.tsx +127 -0
  317. package/src/assets/app/views/authorize/sign-in/sign-in-form.tsx +244 -0
  318. package/src/assets/app/views/authorize/sign-in/sign-in-picker.tsx +116 -0
  319. package/src/assets/app/views/authorize/sign-in/sign-in-view.tsx +145 -0
  320. package/src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx +140 -0
  321. package/src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx +51 -0
  322. package/src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx +289 -0
  323. package/src/assets/app/views/authorize/sign-up/sign-up-hcaptcha-form.tsx +108 -0
  324. package/src/assets/app/views/authorize/sign-up/sign-up-view.tsx +158 -0
  325. package/src/assets/app/views/authorize/welcome/welcome-view.tsx +56 -0
  326. package/src/assets/app/views/error/error-view.tsx +31 -0
  327. package/src/assets/asset.ts +1 -0
  328. package/src/assets/assets-middleware.ts +13 -8
  329. package/src/assets/index.ts +15 -2
  330. package/src/client/client-store.ts +10 -12
  331. package/src/device/device-manager.ts +8 -12
  332. package/src/device/device-store.ts +9 -15
  333. package/src/dpop/dpop-manager.ts +20 -8
  334. package/src/dpop/dpop-nonce.ts +58 -40
  335. package/src/errors/handle-unavailable-error.ts +18 -0
  336. package/src/errors/invalid-request-error.ts +10 -8
  337. package/src/lib/csp/index.ts +98 -0
  338. package/src/lib/hcaptcha.ts +182 -0
  339. package/src/lib/html/build-document.ts +60 -16
  340. package/src/lib/http/middleware.ts +4 -3
  341. package/src/lib/http/request.ts +31 -1
  342. package/src/lib/http/response.ts +22 -9
  343. package/src/lib/locale.ts +21 -0
  344. package/src/lib/util/function.ts +0 -3
  345. package/src/lib/util/type.ts +130 -1
  346. package/src/metadata/build-metadata.ts +2 -1
  347. package/src/oauth-errors.ts +1 -0
  348. package/src/oauth-hooks.ts +69 -3
  349. package/src/oauth-provider.ts +403 -307
  350. package/src/oauth-verifier.ts +3 -1
  351. package/src/output/build-authorize-data.ts +1 -3
  352. package/src/output/build-customization-data.ts +228 -0
  353. package/src/output/output-manager.ts +111 -48
  354. package/src/output/send-authorize-redirect.ts +43 -36
  355. package/src/output/send-web-page.ts +40 -26
  356. package/src/request/request-manager.ts +4 -4
  357. package/src/request/request-store.ts +12 -16
  358. package/src/token/token-store.ts +14 -18
  359. package/tailwind.config.js +5 -0
  360. package/tsconfig.backend.tsbuildinfo +1 -1
  361. package/tsconfig.frontend.tsbuildinfo +1 -1
  362. package/tsconfig.tools.tsbuildinfo +1 -1
  363. package/vite.config.mjs +16 -0
  364. package/.postcssrc.yml +0 -3
  365. package/dist/assets/app/main.css +0 -3
  366. package/dist/assets/app/main.js +0 -20
  367. package/dist/assets/app/main.js.map +0 -1
  368. package/dist/output/customization.d.ts +0 -27
  369. package/dist/output/customization.d.ts.map +0 -1
  370. package/dist/output/customization.js +0 -88
  371. package/dist/output/customization.js.map +0 -1
  372. package/src/assets/app/components/accept-form.tsx +0 -137
  373. package/src/assets/app/components/account-identifier.tsx +0 -18
  374. package/src/assets/app/components/account-picker.tsx +0 -127
  375. package/src/assets/app/components/button.tsx +0 -34
  376. package/src/assets/app/components/client-name.tsx +0 -37
  377. package/src/assets/app/components/fieldset.tsx +0 -26
  378. package/src/assets/app/components/form-card.tsx +0 -47
  379. package/src/assets/app/components/help-card.tsx +0 -42
  380. package/src/assets/app/components/icons/alert-icon.tsx +0 -5
  381. package/src/assets/app/components/icons/at-symbol-icon.tsx +0 -5
  382. package/src/assets/app/components/icons/caret-right-icon.tsx +0 -5
  383. package/src/assets/app/components/icons/lock-icon.tsx +0 -5
  384. package/src/assets/app/components/icons/token-icon.tsx +0 -5
  385. package/src/assets/app/components/icons/util.tsx +0 -17
  386. package/src/assets/app/components/info-card.tsx +0 -45
  387. package/src/assets/app/components/input-checkbox.tsx +0 -47
  388. package/src/assets/app/components/input-container.tsx +0 -37
  389. package/src/assets/app/components/input-layout.tsx +0 -47
  390. package/src/assets/app/components/input-text.tsx +0 -69
  391. package/src/assets/app/components/layout-title-page.tsx +0 -60
  392. package/src/assets/app/components/layout-welcome.tsx +0 -74
  393. package/src/assets/app/components/sign-in-form.tsx +0 -337
  394. package/src/assets/app/components/sign-up-account-form.tsx +0 -194
  395. package/src/assets/app/components/sign-up-disclaimer.tsx +0 -44
  396. package/src/assets/app/views/accept-view.tsx +0 -55
  397. package/src/assets/app/views/authorize-view.tsx +0 -106
  398. package/src/assets/app/views/error-view.tsx +0 -36
  399. package/src/assets/app/views/sign-in-view.tsx +0 -111
  400. package/src/assets/app/views/sign-up-view.tsx +0 -86
  401. package/src/assets/app/views/welcome-view.tsx +0 -54
  402. package/src/output/customization.ts +0 -118
@@ -0,0 +1,62 @@
1
+ import { Trans } from '@lingui/react/macro'
2
+ import { ReactNode, memo } from 'react'
3
+ import {
4
+ EmailTakenError,
5
+ HandleUnavailableError,
6
+ InvalidCredentialsError,
7
+ RequestExpiredError,
8
+ SecondAuthenticationFactorRequiredError,
9
+ UnknownRequestUriError,
10
+ } from '../../lib/api.ts'
11
+ import { JsonErrorResponse } from '../../lib/json-client.ts'
12
+
13
+ export type ApiErrorMessageProps = {
14
+ error: unknown
15
+ }
16
+
17
+ export const ErrorMessage = memo(function ErrorMessage({
18
+ error,
19
+ }: ApiErrorMessageProps): ReactNode {
20
+ if (error instanceof InvalidCredentialsError) {
21
+ return <Trans>Wrong identifier or password</Trans>
22
+ }
23
+
24
+ if (error instanceof EmailTakenError) {
25
+ return <Trans>This email is already used</Trans>
26
+ }
27
+
28
+ if (error instanceof HandleUnavailableError) {
29
+ switch (error.reason) {
30
+ case 'syntax':
31
+ return <Trans>The handle is invalid</Trans>
32
+ case 'domain':
33
+ return <Trans>The domain name is not allowed</Trans>
34
+ case 'slur':
35
+ return <Trans>The handle contains inappropriate language</Trans>
36
+ case 'taken':
37
+ if (error.description === 'Reserved handle') {
38
+ return <Trans>This handle is reserved</Trans>
39
+ }
40
+ return <Trans>The handle is already in use</Trans>
41
+ default:
42
+ return <Trans>That handle cannot be used</Trans>
43
+ }
44
+ }
45
+
46
+ if (error instanceof SecondAuthenticationFactorRequiredError) {
47
+ return <Trans>A second authentication factor is required</Trans>
48
+ }
49
+
50
+ if (
51
+ error instanceof UnknownRequestUriError ||
52
+ error instanceof RequestExpiredError
53
+ ) {
54
+ return <Trans>This sign-in session has expired</Trans>
55
+ }
56
+
57
+ if (error instanceof JsonErrorResponse) {
58
+ return <Trans>Unexpected server response</Trans>
59
+ }
60
+
61
+ return <Trans>An unknown error occurred</Trans>
62
+ })
@@ -0,0 +1,46 @@
1
+ import { Trans } from '@lingui/react/macro'
2
+ import { JSX } from 'react'
3
+ import { LinkDefinition } from '../../backend-types.ts'
4
+ import { clsx } from '../../lib/clsx.ts'
5
+ import { Override } from '../../lib/util.ts'
6
+
7
+ export type HelpCardProps = Override<
8
+ Omit<JSX.IntrinsicElements['p'], 'children'>,
9
+ {
10
+ links?: readonly LinkDefinition[]
11
+ }
12
+ >
13
+
14
+ export function HelpCard({
15
+ links,
16
+
17
+ className,
18
+ ...props
19
+ }: HelpCardProps) {
20
+ const helpLink = links?.find((l) => l.rel === 'help')
21
+
22
+ if (!helpLink) return null
23
+
24
+ return (
25
+ <p
26
+ {...props}
27
+ className={clsx(
28
+ 'text-sm rounded-md bg-slate-100 text-slate-800 dark:bg-slate-800 dark:text-slate-400 p-3',
29
+ className,
30
+ )}
31
+ >
32
+ <Trans>
33
+ Having trouble?{' '}
34
+ <a
35
+ role="link"
36
+ href={helpLink.href}
37
+ rel={helpLink.rel}
38
+ target="_blank"
39
+ className="text-brand"
40
+ >
41
+ <Trans>Contact support</Trans>
42
+ </a>
43
+ </Trans>
44
+ </p>
45
+ )
46
+ }
@@ -0,0 +1,88 @@
1
+ import type { FunctionComponent, JSX } from 'react'
2
+ import { Override } from '../../lib/util.ts'
3
+
4
+ export type IconProps = Override<
5
+ Omit<JSX.IntrinsicElements['svg'], 'viewBox' | 'children' | 'xmlns'>,
6
+ {
7
+ /**
8
+ * The title of the icon, used for accessibility.
9
+ */
10
+ title?: string
11
+ }
12
+ >
13
+
14
+ const makeSvgComponent = (path: string, displayName: string) => {
15
+ const SvgComponent: FunctionComponent<IconProps> = ({ title, ...props }) => (
16
+ <svg
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ viewBox="0 0 24 24"
19
+ {...props}
20
+ aria-hidden={!title}
21
+ >
22
+ {title && <title>{title}</title>}
23
+ <path
24
+ fill="currentColor"
25
+ fillRule="evenodd"
26
+ clipRule="evenodd"
27
+ d={path}
28
+ ></path>
29
+ </svg>
30
+ )
31
+ SvgComponent.displayName = displayName
32
+ return SvgComponent
33
+ }
34
+
35
+ export const AccountIcon = makeSvgComponent(
36
+ 'M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z',
37
+ 'AccountIcon',
38
+ )
39
+
40
+ export const AlertIcon = makeSvgComponent(
41
+ 'M11.14 4.494a.995.995 0 0 1 1.72 0l7.001 12.008a.996.996 0 0 1-.86 1.498H4.999a.996.996 0 0 1-.86-1.498L11.14 4.494Zm3.447-1.007c-1.155-1.983-4.019-1.983-5.174 0L2.41 15.494C1.247 17.491 2.686 20 4.998 20h14.004c2.312 0 3.751-2.509 2.587-4.506L14.587 3.487ZM13 9.019a1 1 0 1 0-2 0v2.994a1 1 0 1 0 2 0V9.02Zm-1 4.731a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5Z',
42
+ 'AlertIcon',
43
+ )
44
+
45
+ export const AtSymbolIcon = makeSvgComponent(
46
+ 'M12 4a8 8 0 1 0 4.21 14.804 1 1 0 0 1 1.054 1.7A9.958 9.958 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 1.104-.27 2.31-.949 3.243-.716.984-1.849 1.6-3.331 1.465a4.207 4.207 0 0 1-2.93-1.585c-.94 1.21-2.388 1.94-3.985 1.715-2.53-.356-4.04-2.91-3.682-5.458.358-2.547 2.514-4.586 5.044-4.23.905.127 1.68.536 2.286 1.126a1 1 0 0 1 1.964.368l-.515 3.545v.002a2.222 2.222 0 0 0 1.999 2.526c.75.068 1.212-.21 1.533-.65.358-.493.566-1.245.566-2.067a8 8 0 0 0-8-8Zm-.112 5.13c-1.195-.168-2.544.819-2.784 2.529-.24 1.71.784 3.03 1.98 3.198 1.195.168 2.543-.819 2.784-2.529.24-1.71-.784-3.03-1.98-3.198Z',
47
+ 'AtSymbolIcon',
48
+ )
49
+
50
+ export const CaretRightIcon = makeSvgComponent(
51
+ 'M8.293 3.293a1 1 0 0 1 1.414 0l8 8a1 1 0 0 1 0 1.414l-8 8a1 1 0 0 1-1.414-1.414L15.586 12 8.293 4.707a1 1 0 0 1 0-1.414Z',
52
+ 'CaretRightIcon',
53
+ )
54
+
55
+ export const CheckMarkIcon = makeSvgComponent(
56
+ 'M21.59 3.193a1 1 0 0 1 .217 1.397l-11.706 16a1 1 0 0 1-1.429.193l-6.294-5a1 1 0 1 1 1.244-1.566l5.48 4.353 11.09-15.16a1 1 0 0 1 1.398-.217Z',
57
+ 'CheckMarkIcon',
58
+ )
59
+
60
+ export const EmailIcon = makeSvgComponent(
61
+ 'M4.568 4h14.864c.252 0 .498 0 .706.017.229.019.499.063.77.201a2 2 0 0 1 .874.874c.138.271.182.541.201.77.017.208.017.454.017.706v10.864c0 .252 0 .498-.017.706a2.022 2.022 0 0 1-.201.77 2 2 0 0 1-.874.874 2.022 2.022 0 0 1-.77.201c-.208.017-.454.017-.706.017H4.568c-.252 0-.498 0-.706-.017a2.022 2.022 0 0 1-.77-.201 2 2 0 0 1-.874-.874 2.022 2.022 0 0 1-.201-.77C2 17.93 2 17.684 2 17.432V6.568c0-.252 0-.498.017-.706.019-.229.063-.499.201-.77a2 2 0 0 1 .874-.874c.271-.138.541-.182.77-.201C4.07 4 4.316 4 4.568 4Zm.456 2L12 11.708 18.976 6H5.024ZM20 7.747l-6.733 5.509a2 2 0 0 1-2.534 0L4 7.746V17.4a8.187 8.187 0 0 0 .011.589h.014c.116.01.278.011.575.011h14.8a8.207 8.207 0 0 0 .589-.012v-.013c.01-.116.011-.279.011-.575V7.747Z',
62
+ 'EmailIcon',
63
+ )
64
+
65
+ export const EyeIcon = makeSvgComponent(
66
+ 'M12 6.5c3.79 0 7.17 2.13 8.82 5.5-1.65 3.37-5.02 5.5-8.82 5.5S4.83 15.37 3.18 12C4.83 8.63 8.21 6.5 12 6.5m0-2C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5m0 5c1.38 0 2.5 1.12 2.5 2.5s-1.12 2.5-2.5 2.5-2.5-1.12-2.5-2.5 1.12-2.5 2.5-2.5m0-2c-2.48 0-4.5 2.02-4.5 4.5s2.02 4.5 4.5 4.5 4.5-2.02 4.5-4.5-2.02-4.5-4.5-4.5',
67
+ 'EyeIcon',
68
+ )
69
+
70
+ export const EyeSlashIcon = makeSvgComponent(
71
+ 'M12 6c3.79 0 7.17 2.13 8.82 5.5-.59 1.22-1.42 2.27-2.41 3.12l1.41 1.41c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l1.65 1.65C10.66 6.09 11.32 6 12 6m-1.07 1.14L13 9.21c.57.25 1.03.71 1.28 1.28l2.07 2.07c.08-.34.14-.7.14-1.07C16.5 9.01 14.48 7 12 7c-.37 0-.72.05-1.07.14M2.01 3.87l2.68 2.68C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.98-.29 4.32-.82l3.42 3.42 1.41-1.41L3.42 2.45zm7.5 7.5 2.61 2.61c-.04.01-.08.02-.12.02-1.38 0-2.5-1.12-2.5-2.5 0-.05.01-.08.01-.13m-3.4-3.4 1.75 1.75c-.23.55-.36 1.15-.36 1.78 0 2.48 2.02 4.5 4.5 4.5.63 0 1.23-.13 1.77-.36l.98.98c-.88.24-1.8.38-2.75.38-3.79 0-7.17-2.13-8.82-5.5.7-1.43 1.72-2.61 2.93-3.53',
72
+ 'EyeSlashIcon',
73
+ )
74
+
75
+ export const LockIcon = makeSvgComponent(
76
+ 'M7 7a5 5 0 0 1 10 0v2h1a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2h1V7Zm-1 4v9h12v-9H6Zm9-2H9V7a3 3 0 1 1 6 0v2Zm-3 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0v-3a1 1 0 0 1 1-1Z',
77
+ 'LockIcon',
78
+ )
79
+
80
+ export const TokenIcon = makeSvgComponent(
81
+ 'M4 5.5a.5.5 0 0 0-.5.5v2.535a.5.5 0 0 0 .25.433A3.498 3.498 0 0 1 5.5 12a3.498 3.498 0 0 1-1.75 3.032.5.5 0 0 0-.25.433V18a.5.5 0 0 0 .5.5h16a.5.5 0 0 0 .5-.5v-2.535a.5.5 0 0 0-.25-.433A3.498 3.498 0 0 1 18.5 12a3.5 3.5 0 0 1 1.75-3.032.5.5 0 0 0 .25-.433V6a.5.5 0 0 0-.5-.5H4ZM2.5 6A1.5 1.5 0 0 1 4 4.5h16A1.5 1.5 0 0 1 21.5 6v3.17a.5.5 0 0 1-.333.472 2.501 2.501 0 0 0 0 4.716.5.5 0 0 1 .333.471V18a1.5 1.5 0 0 1-1.5 1.5H4A1.5 1.5 0 0 1 2.5 18v-3.17a.5.5 0 0 1 .333-.472 2.501 2.501 0 0 0 0-4.716.5.5 0 0 1-.333-.471V6Zm12 2a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Z',
82
+ 'TokenIcon',
83
+ )
84
+
85
+ export const XMarkIcon = makeSvgComponent(
86
+ 'M4.293 4.293a1 1 0 0 1 1.414 0L12 10.586l6.293-6.293a1 1 0 1 1 1.414 1.414L13.414 12l6.293 6.293a1 1 0 0 1-1.414 1.414L12 13.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L10.586 12 4.293 5.707a1 1 0 0 1 0-1.414Z',
87
+ 'XMarkIcon',
88
+ )
@@ -0,0 +1,28 @@
1
+ import { JSX } from 'react'
2
+ import { LinkDefinition } from '../../backend-types.ts'
3
+ import { Override } from '../../lib/util.ts'
4
+ import { LinkTitle } from './link-title.tsx'
5
+
6
+ export type LinkAnchorProps = Override<
7
+ JSX.IntrinsicElements['a'],
8
+ {
9
+ link: LinkDefinition
10
+ }
11
+ >
12
+ export function LinkAnchor({
13
+ link,
14
+
15
+ // a
16
+ children = <LinkTitle link={link} />,
17
+ role = 'link',
18
+ target = '_blank',
19
+ href = link.href,
20
+ rel = link.rel,
21
+ ...props
22
+ }: LinkAnchorProps) {
23
+ return (
24
+ <a {...props} role={role} target={target} href={href} rel={rel}>
25
+ {children}
26
+ </a>
27
+ )
28
+ }
@@ -0,0 +1,26 @@
1
+ import { Trans } from '@lingui/react/macro'
2
+ import { LinkDefinition } from '../../backend-types.ts'
3
+ import { MultiLangString } from './multi-lang-string.tsx'
4
+
5
+ export type LinkNameProps = {
6
+ link: LinkDefinition
7
+ }
8
+
9
+ export function LinkTitle({ link }: LinkNameProps) {
10
+ return (
11
+ <MultiLangString
12
+ value={link.title}
13
+ fallback={
14
+ link.rel === 'canonical' ? (
15
+ <Trans>Home</Trans>
16
+ ) : link.rel === 'privacy-policy' ? (
17
+ <Trans>Privacy Policy</Trans>
18
+ ) : link.rel === 'terms-of-service' ? (
19
+ <Trans>Terms of Service</Trans>
20
+ ) : link.rel === 'help' ? (
21
+ <Trans>Support</Trans>
22
+ ) : undefined
23
+ }
24
+ />
25
+ )
26
+ }
@@ -0,0 +1,56 @@
1
+ import { useLingui } from '@lingui/react/macro'
2
+ import { ReactNode } from 'react'
3
+ import type { LocalizedString } from '../../backend-types.ts'
4
+
5
+ export type MultiLangStringProps = {
6
+ value: LocalizedString
7
+ fallback?: ReactNode
8
+ }
9
+
10
+ export function MultiLangString({
11
+ value,
12
+ fallback,
13
+ }: MultiLangStringProps): ReactNode {
14
+ const { i18n } = useLingui()
15
+ return (
16
+ findMatchingString(value, i18n.locale) ??
17
+ fallback ??
18
+ (typeof value === 'string' ? value : value.en)
19
+ )
20
+ }
21
+
22
+ /**
23
+ * Only returns a string if it matches the desired locale.
24
+ */
25
+ function findMatchingString(
26
+ value: LocalizedString,
27
+ locale: string,
28
+ ): string | undefined {
29
+ switch (typeof value) {
30
+ case 'string':
31
+ // By convention, string values are in english ("en")
32
+ if (locale.startsWith('en')) return value
33
+ break
34
+
35
+ case 'object': {
36
+ // Exact match
37
+ const localeMatch = value[locale]
38
+ if (typeof localeMatch === 'string') return localeMatch
39
+
40
+ // Fallback to language match
41
+ const lang = locale.split('-')[0]
42
+ const langMatch = value[lang]
43
+ if (typeof langMatch === 'string') return langMatch
44
+
45
+ // Fallback to any locale from same language (e.g. "pt-PT" -> "pt-BR")
46
+ for (const k in value) {
47
+ if (k.startsWith(`${lang}-`)) {
48
+ const fallbackMatch = value[k]
49
+ if (typeof fallbackMatch === 'string') return fallbackMatch
50
+ }
51
+ }
52
+ }
53
+ }
54
+
55
+ return undefined
56
+ }
@@ -0,0 +1,37 @@
1
+ import { Trans, useLingui } from '@lingui/react/macro'
2
+ import { JSX } from 'react'
3
+ import { PasswordStrength, getPasswordStrength } from '../../lib/password.ts'
4
+ import { Override } from '../../lib/util.ts'
5
+
6
+ export type PasswordStrengthLabelProps = Override<
7
+ Omit<JSX.IntrinsicElements['span'], 'children' | 'aria-label'>,
8
+ {
9
+ password: string
10
+ }
11
+ >
12
+
13
+ export function PasswordStrengthLabel({
14
+ password,
15
+
16
+ // span
17
+ ...props
18
+ }: PasswordStrengthLabelProps) {
19
+ const { t } = useLingui()
20
+ const strength = getPasswordStrength(password)
21
+
22
+ return (
23
+ <span {...props} aria-label={t`Password strength`}>
24
+ {strength === PasswordStrength.extra ? (
25
+ <Trans>Extra</Trans>
26
+ ) : strength === PasswordStrength.strong ? (
27
+ <Trans>Strong</Trans>
28
+ ) : strength === PasswordStrength.moderate ? (
29
+ <Trans>Moderate</Trans>
30
+ ) : password ? (
31
+ <Trans>Weak</Trans>
32
+ ) : (
33
+ <Trans>Missing</Trans>
34
+ )}
35
+ </span>
36
+ )
37
+ }
@@ -0,0 +1,58 @@
1
+ import { useLingui } from '@lingui/react/macro'
2
+ import { JSX } from 'react'
3
+ import { clsx } from '../../lib/clsx.ts'
4
+ import { PasswordStrength, getPasswordStrength } from '../../lib/password.ts'
5
+ import { Override } from '../../lib/util.ts'
6
+
7
+ export type PasswordStrengthMeterProps = Override<
8
+ Omit<
9
+ JSX.IntrinsicElements['div'],
10
+ | 'children'
11
+ | 'role'
12
+ | 'aria-label'
13
+ | 'aria-valuemin'
14
+ | 'aria-valuemax'
15
+ | 'aria-valuenow'
16
+ >,
17
+ {
18
+ password: string
19
+ }
20
+ >
21
+
22
+ export function PasswordStrengthMeter({
23
+ password,
24
+
25
+ // div
26
+ className,
27
+ ...props
28
+ }: PasswordStrengthMeterProps) {
29
+ const { t } = useLingui()
30
+ const strength = password ? getPasswordStrength(password) : 0
31
+
32
+ const colorBg = 'bg-gray-300 dark:bg-slate-500'
33
+ const color =
34
+ strength === PasswordStrength.extra || strength === PasswordStrength.strong
35
+ ? 'bg-success'
36
+ : strength === PasswordStrength.moderate
37
+ ? 'bg-warning'
38
+ : 'bg-error'
39
+
40
+ return (
41
+ <div
42
+ {...props}
43
+ className={clsx('w-full h-1 flex space-x-2', className)}
44
+ role="meter"
45
+ aria-label={t`Password strength indicator`}
46
+ aria-valuemin={0}
47
+ aria-valuemax={PasswordStrength.extra}
48
+ aria-valuenow={strength}
49
+ >
50
+ {Array.from({ length: 4 }, (_, i) => (
51
+ <div
52
+ key={i}
53
+ className={`rounded h-1 w-1/4 ${strength > i ? color : colorBg}`}
54
+ />
55
+ ))}
56
+ </div>
57
+ )
58
+ }
@@ -1,4 +1,5 @@
1
- import { HTMLAttributes, useMemo } from 'react'
1
+ import { JSX, useMemo } from 'react'
2
+ import { Override } from '../../lib/util.ts'
2
3
 
3
4
  export type UrlPartRenderingOptions = {
4
5
  faded?: boolean
@@ -12,10 +13,10 @@ export type UrlRendererProps = {
12
13
  path?: boolean | UrlPartRenderingOptions
13
14
  query?: boolean | UrlPartRenderingOptions
14
15
  hash?: boolean | UrlPartRenderingOptions
15
- as?: keyof JSX.IntrinsicElements
16
+ as?: string
16
17
  }
17
18
 
18
- export function UrlViewer({
19
+ export function UrlViewer<As extends keyof JSX.IntrinsicElements = 'span'>({
19
20
  url,
20
21
  proto = false,
21
22
  host = true,
@@ -23,12 +24,14 @@ export function UrlViewer({
23
24
  query = false,
24
25
  hash = false,
25
26
  as: As = 'span',
26
- ...attrs
27
- }: UrlRendererProps & HTMLAttributes<Element>) {
27
+
28
+ // Element
29
+ ...props
30
+ }: Override<JSX.IntrinsicElements[As], UrlRendererProps>) {
28
31
  const urlObj = useMemo(() => new URL(url), [url])
29
32
 
30
33
  return (
31
- <As {...attrs}>
34
+ <As {...props}>
32
35
  {proto && (
33
36
  <UrlPartViewer
34
37
  value={`${urlObj.protocol}//`}
@@ -1,41 +1,68 @@
1
+ import { useLingui } from '@lingui/react/macro'
1
2
  import { useCallback, useMemo, useState } from 'react'
2
- import { Account, AuthorizeData, Session } from '../backend-data'
3
- import { Api } from '../lib/api'
4
- import { upsert } from '../lib/util'
5
- import { useCsrfToken } from './use-csrf-token'
3
+ import { useErrorBoundary } from 'react-error-boundary'
4
+ import { Session } from '../backend-types.ts'
5
+ import {
6
+ AcceptData,
7
+ Api,
8
+ ConfirmResetPasswordData,
9
+ InitiatePasswordResetData,
10
+ SessionResponse,
11
+ SignInData,
12
+ SignUpData,
13
+ UnknownRequestUriError,
14
+ VerifyHandleAvailabilityData,
15
+ } from '../lib/api.ts'
16
+ import { upsert } from '../lib/util.ts'
17
+ import { useCsrfToken } from './use-csrf-token.ts'
6
18
 
7
- export type SignInCredentials = {
8
- username: string
9
- password: string
10
- emailOtp?: string
19
+ /**
20
+ * Any function wrapped with this helper will automatically show the error
21
+ * boundary when an `UnknownRequestUriError` is thrown. This typically happens
22
+ * in development, or if the user left its browser session open for a (very)
23
+ * long time.
24
+ *
25
+ * @note Requires an error boundary to be present in the component tree.
26
+ */
27
+ function useSafeCallback<F extends (...a: any) => any>(fn: F, deps: unknown[]) {
28
+ const { showBoundary } = useErrorBoundary<UnknownRequestUriError>()
11
29
 
12
- remember?: boolean
30
+ return useCallback(
31
+ async (...args: Parameters<F>): Promise<Awaited<ReturnType<F>>> => {
32
+ try {
33
+ return await fn(...args)
34
+ } catch (error) {
35
+ if (error instanceof UnknownRequestUriError) showBoundary(error)
36
+ throw error
37
+ }
38
+ },
39
+ deps.concat(showBoundary),
40
+ )
13
41
  }
14
42
 
15
- export type SignUpData = {
16
- username: string
17
- password: string
18
- extra?: Record<string, string>
43
+ export type UseApiOptions = {
44
+ requestUri: string
45
+ sessions?: readonly Session[]
46
+ newSessionsRequireConsent?: boolean
47
+ onRedirected?: () => void
19
48
  }
20
49
 
21
- export function useApi(
22
- {
23
- clientId,
24
- requestUri,
25
- csrfCookie,
26
- sessions: initialSessions,
27
- newSessionsRequireConsent,
28
- }: AuthorizeData,
29
- {
30
- onRedirected,
31
- }: {
32
- onRedirected?: () => void
33
- } = {},
34
- ) {
35
- const csrfToken = useCsrfToken(csrfCookie) ?? '<csrf-token-missing>' // Invalid value
36
- const [sessions, setSessions] = useState<readonly Session[]>(initialSessions)
37
-
38
- const setSession = useCallback(
50
+ export function useApi({
51
+ requestUri,
52
+ sessions: sessionsInit = [],
53
+ newSessionsRequireConsent = true,
54
+ onRedirected,
55
+ }: UseApiOptions) {
56
+ const csrfToken = useCsrfToken(`csrf-${requestUri}`)
57
+ if (!csrfToken) throw new Error('CSRF token is missing')
58
+
59
+ const api = useMemo(() => new Api(csrfToken), [csrfToken])
60
+ const [sessions, setSessions] = useState(sessionsInit)
61
+
62
+ const { i18n } = useLingui()
63
+ const { locale } = i18n
64
+
65
+ const selectSub = useCallback(
39
66
  (sub: string | null) => {
40
67
  setSessions((sessions) =>
41
68
  sub === (sessions.find((s) => s.selected)?.account.sub || null)
@@ -46,9 +73,23 @@ export function useApi(
46
73
  [setSessions],
47
74
  )
48
75
 
49
- const api = useMemo(
50
- () => new Api(requestUri, clientId, csrfToken, newSessionsRequireConsent),
51
- [requestUri, clientId, csrfToken, newSessionsRequireConsent],
76
+ const upsertSession = useCallback(
77
+ ({ account, consentRequired }: SessionResponse) => {
78
+ const session: Session = {
79
+ account,
80
+ selected: true,
81
+ loginRequired: false,
82
+ consentRequired: newSessionsRequireConsent || consentRequired,
83
+ }
84
+
85
+ setSessions((sessions) =>
86
+ upsert(sessions, session, (s) => s.account.sub === account.sub).map(
87
+ // Make sure to de-select any other selected session
88
+ (s) => (s === session || !s.selected ? s : { ...s, selected: false }),
89
+ ),
90
+ )
91
+ },
92
+ [setSessions, newSessionsRequireConsent],
52
93
  )
53
94
 
54
95
  const performRedirect = useCallback(
@@ -59,45 +100,77 @@ export function useApi(
59
100
  [onRedirected],
60
101
  )
61
102
 
62
- const doSignIn = useCallback(
63
- async (credentials: SignInCredentials): Promise<void> => {
64
- const session = await api.signIn(credentials)
65
- const { sub } = session.account
103
+ const doSignIn = useSafeCallback(
104
+ async (data: Omit<SignInData, 'locale'>, signal?: AbortSignal) => {
105
+ const response = await api.fetch(
106
+ '/sign-in',
107
+ { ...data, locale },
108
+ { signal },
109
+ )
110
+ upsertSession(response)
111
+ },
112
+ [api, locale, upsertSession],
113
+ )
66
114
 
67
- setSessions((sessions) => {
68
- return upsert(sessions, session, (s) => s.account.sub === sub).map(
69
- // Make sure to de-select any other selected session
70
- (s) => (s === session || !s.selected ? s : { ...s, selected: false }),
71
- )
72
- })
115
+ const doInitiatePasswordReset = useSafeCallback(
116
+ async (
117
+ data: Omit<InitiatePasswordResetData, 'locale'>,
118
+ signal?: AbortSignal,
119
+ ) => {
120
+ await api.fetch(
121
+ '/reset-password-request',
122
+ { ...data, locale },
123
+ { signal },
124
+ )
125
+ },
126
+ [api, locale],
127
+ )
128
+
129
+ const doConfirmResetPassword = useSafeCallback(
130
+ async (data: ConfirmResetPasswordData, signal?: AbortSignal) => {
131
+ await api.fetch('/reset-password-confirm', data, { signal })
73
132
  },
74
- [api, performRedirect, clientId, setSessions],
133
+ [api],
75
134
  )
76
135
 
77
- const doSignUp = useCallback(
78
- (_data: SignUpData) => {
79
- //
80
- throw new Error('Not implemented')
136
+ const doValidateNewHandle = useSafeCallback(
137
+ async (data: VerifyHandleAvailabilityData, signal?: AbortSignal) => {
138
+ await api.fetch('/verify-handle-availability', data, { signal })
81
139
  },
82
140
  [api],
83
141
  )
84
142
 
85
- const doAccept = useCallback(
86
- async (account: Account) => {
87
- performRedirect(await api.accept(account))
143
+ const doSignUp = useSafeCallback(
144
+ async (data: Omit<SignUpData, 'locale'>, signal?: AbortSignal) => {
145
+ const response = await api.fetch(
146
+ '/sign-up',
147
+ { ...data, locale },
148
+ { signal },
149
+ )
150
+ upsertSession(response)
151
+ },
152
+ [api, locale, upsertSession],
153
+ )
154
+
155
+ const doAccept = useSafeCallback(
156
+ async (data: AcceptData) => {
157
+ performRedirect(api.buildAcceptUrl(data))
88
158
  },
89
159
  [api, performRedirect],
90
160
  )
91
161
 
92
- const doReject = useCallback(async () => {
93
- performRedirect(await api.reject())
162
+ const doReject = useSafeCallback(async () => {
163
+ performRedirect(api.buildRejectUrl())
94
164
  }, [api, performRedirect])
95
165
 
96
166
  return {
97
167
  sessions,
98
- setSession,
168
+ selectSub,
99
169
 
100
170
  doSignIn,
171
+ doInitiatePasswordReset,
172
+ doConfirmResetPassword,
173
+ doValidateNewHandle,
101
174
  doSignUp,
102
175
  doAccept,
103
176
  doReject,