@atproto/oauth-provider 0.5.1 → 0.6.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 (325) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/account/account-manager.d.ts +7 -5
  3. package/dist/account/account-manager.d.ts.map +1 -1
  4. package/dist/account/account-manager.js +34 -25
  5. package/dist/account/account-manager.js.map +1 -1
  6. package/dist/account/account-store.d.ts +13 -5
  7. package/dist/account/account-store.d.ts.map +1 -1
  8. package/dist/account/account-store.js +24 -8
  9. package/dist/account/account-store.js.map +1 -1
  10. package/dist/account/account.d.ts +1 -11
  11. package/dist/account/account.d.ts.map +1 -1
  12. package/dist/account/{sign-up-data.d.ts → sign-up-input.d.ts} +5 -5
  13. package/dist/account/sign-up-input.d.ts.map +1 -0
  14. package/dist/account/{sign-up-data.js → sign-up-input.js} +3 -3
  15. package/dist/account/sign-up-input.js.map +1 -0
  16. package/dist/assets/assets-middleware.d.ts +2 -0
  17. package/dist/assets/assets-middleware.d.ts.map +1 -1
  18. package/dist/assets/assets-middleware.js +12 -14
  19. package/dist/assets/assets-middleware.js.map +1 -1
  20. package/dist/errors/invalid-invite-code-error.d.ts +5 -0
  21. package/dist/errors/invalid-invite-code-error.d.ts.map +1 -0
  22. package/dist/errors/invalid-invite-code-error.js +11 -0
  23. package/dist/errors/invalid-invite-code-error.js.map +1 -0
  24. package/dist/errors/oauth-error.d.ts +2 -2
  25. package/dist/errors/oauth-error.js.map +1 -1
  26. package/dist/lib/csp/index.d.ts +5 -6
  27. package/dist/lib/csp/index.d.ts.map +1 -1
  28. package/dist/lib/csp/index.js +14 -11
  29. package/dist/lib/csp/index.js.map +1 -1
  30. package/dist/lib/hcaptcha.d.ts +5 -3
  31. package/dist/lib/hcaptcha.d.ts.map +1 -1
  32. package/dist/lib/hcaptcha.js +7 -4
  33. package/dist/lib/hcaptcha.js.map +1 -1
  34. package/dist/lib/html/build-document.d.ts +2 -2
  35. package/dist/lib/html/build-document.d.ts.map +1 -1
  36. package/dist/lib/html/build-document.js +11 -7
  37. package/dist/lib/html/build-document.js.map +1 -1
  38. package/dist/lib/html/html.d.ts.map +1 -1
  39. package/dist/lib/html/html.js +10 -13
  40. package/dist/lib/html/html.js.map +1 -1
  41. package/dist/lib/html/util.d.ts +0 -1
  42. package/dist/lib/html/util.d.ts.map +1 -1
  43. package/dist/lib/html/util.js +0 -4
  44. package/dist/lib/html/util.js.map +1 -1
  45. package/dist/lib/http/response.d.ts +3 -1
  46. package/dist/lib/http/response.d.ts.map +1 -1
  47. package/dist/lib/http/response.js +3 -0
  48. package/dist/lib/http/response.js.map +1 -1
  49. package/dist/lib/http/security-headers.d.ts +48 -0
  50. package/dist/lib/http/security-headers.d.ts.map +1 -0
  51. package/dist/lib/http/security-headers.js +62 -0
  52. package/dist/lib/http/security-headers.js.map +1 -0
  53. package/dist/lib/util/type.d.ts +8 -0
  54. package/dist/lib/util/type.d.ts.map +1 -1
  55. package/dist/lib/util/type.js.map +1 -1
  56. package/dist/oauth-errors.d.ts +1 -0
  57. package/dist/oauth-errors.d.ts.map +1 -1
  58. package/dist/oauth-errors.js +3 -1
  59. package/dist/oauth-errors.js.map +1 -1
  60. package/dist/oauth-hooks.d.ts +4 -25
  61. package/dist/oauth-hooks.d.ts.map +1 -1
  62. package/dist/oauth-provider.d.ts.map +1 -1
  63. package/dist/oauth-provider.js +26 -25
  64. package/dist/oauth-provider.js.map +1 -1
  65. package/dist/output/backend-data.d.ts +4 -0
  66. package/dist/output/backend-data.d.ts.map +1 -0
  67. package/dist/output/backend-data.js +19 -0
  68. package/dist/output/backend-data.js.map +1 -0
  69. package/dist/output/build-authorize-data.d.ts +3 -19
  70. package/dist/output/build-authorize-data.d.ts.map +1 -1
  71. package/dist/output/build-authorize-data.js.map +1 -1
  72. package/dist/output/build-customization-data.d.ts +11 -18
  73. package/dist/output/build-customization-data.d.ts.map +1 -1
  74. package/dist/output/build-customization-data.js +1 -1
  75. package/dist/output/build-customization-data.js.map +1 -1
  76. package/dist/output/build-error-data.d.ts +3 -0
  77. package/dist/output/build-error-data.d.ts.map +1 -0
  78. package/dist/output/build-error-data.js +10 -0
  79. package/dist/output/build-error-data.js.map +1 -0
  80. package/dist/output/build-error-payload.d.ts +2 -1
  81. package/dist/output/build-error-payload.d.ts.map +1 -1
  82. package/dist/output/build-error-payload.js.map +1 -1
  83. package/dist/output/output-manager.d.ts +10 -4
  84. package/dist/output/output-manager.d.ts.map +1 -1
  85. package/dist/output/output-manager.js +68 -39
  86. package/dist/output/output-manager.js.map +1 -1
  87. package/dist/output/send-web-page.d.ts +6 -10
  88. package/dist/output/send-web-page.d.ts.map +1 -1
  89. package/dist/output/send-web-page.js +27 -47
  90. package/dist/output/send-web-page.js.map +1 -1
  91. package/dist/signer/signed-token-payload.d.ts +3 -3
  92. package/dist/signer/signer.d.ts +2 -2
  93. package/package.json +7 -39
  94. package/src/account/account-manager.ts +55 -34
  95. package/src/account/account-store.ts +29 -6
  96. package/src/account/account.ts +1 -14
  97. package/src/account/{sign-up-data.ts → sign-up-input.ts} +2 -2
  98. package/src/assets/assets-middleware.ts +11 -17
  99. package/src/errors/invalid-invite-code-error.ts +10 -0
  100. package/src/errors/oauth-error.ts +1 -1
  101. package/src/lib/csp/index.ts +16 -13
  102. package/src/lib/hcaptcha.ts +10 -7
  103. package/src/lib/html/build-document.ts +15 -8
  104. package/src/lib/html/html.ts +11 -18
  105. package/src/lib/html/util.ts +0 -4
  106. package/src/lib/http/response.ts +9 -1
  107. package/src/lib/http/security-headers.ts +91 -0
  108. package/src/lib/util/type.ts +18 -0
  109. package/src/oauth-errors.ts +1 -0
  110. package/src/oauth-hooks.ts +4 -25
  111. package/src/oauth-provider.ts +40 -34
  112. package/src/output/backend-data.ts +18 -0
  113. package/src/output/build-authorize-data.ts +3 -26
  114. package/src/output/build-customization-data.ts +2 -13
  115. package/src/output/build-error-data.ts +8 -0
  116. package/src/output/build-error-payload.ts +4 -2
  117. package/src/output/output-manager.ts +86 -47
  118. package/src/output/send-web-page.ts +29 -58
  119. package/tsconfig.backend.json +1 -2
  120. package/tsconfig.backend.tsbuildinfo +1 -1
  121. package/tsconfig.json +1 -5
  122. package/.linguirc +0 -57
  123. package/dist/account/sign-up-data.d.ts.map +0 -1
  124. package/dist/account/sign-up-data.js.map +0 -1
  125. package/dist/assets/app/bundle-manifest.json +0 -614
  126. package/dist/assets/app/index-ItwwtJ8r.js +0 -36
  127. package/dist/assets/app/index-ItwwtJ8r.js.map +0 -1
  128. package/dist/assets/app/main-B_dNxQo_.js +0 -4
  129. package/dist/assets/app/main-B_dNxQo_.js.map +0 -1
  130. package/dist/assets/app/main-CSatvmRR.css +0 -3
  131. package/dist/assets/app/main-CSatvmRR.js +0 -306
  132. package/dist/assets/app/main-CSatvmRR.js.map +0 -1
  133. package/dist/assets/app/messages-BQeltXSF.js +0 -4
  134. package/dist/assets/app/messages-BQeltXSF.js.map +0 -1
  135. package/dist/assets/app/messages-BQkEhfjg.js +0 -4
  136. package/dist/assets/app/messages-BQkEhfjg.js.map +0 -1
  137. package/dist/assets/app/messages-BUjKj_UJ.js +0 -4
  138. package/dist/assets/app/messages-BUjKj_UJ.js.map +0 -1
  139. package/dist/assets/app/messages-BWIQa8fO.js +0 -4
  140. package/dist/assets/app/messages-BWIQa8fO.js.map +0 -1
  141. package/dist/assets/app/messages-BaNVb0bp.js +0 -4
  142. package/dist/assets/app/messages-BaNVb0bp.js.map +0 -1
  143. package/dist/assets/app/messages-BaizVXcF.js +0 -4
  144. package/dist/assets/app/messages-BaizVXcF.js.map +0 -1
  145. package/dist/assets/app/messages-BfoClA1Y.js +0 -4
  146. package/dist/assets/app/messages-BfoClA1Y.js.map +0 -1
  147. package/dist/assets/app/messages-BsKGDZnC.js +0 -4
  148. package/dist/assets/app/messages-BsKGDZnC.js.map +0 -1
  149. package/dist/assets/app/messages-Bu-TJhml.js +0 -4
  150. package/dist/assets/app/messages-Bu-TJhml.js.map +0 -1
  151. package/dist/assets/app/messages-BvOKnBQk.js +0 -4
  152. package/dist/assets/app/messages-BvOKnBQk.js.map +0 -1
  153. package/dist/assets/app/messages-BxDzCiWz.js +0 -4
  154. package/dist/assets/app/messages-BxDzCiWz.js.map +0 -1
  155. package/dist/assets/app/messages-CDgFOy4S.js +0 -4
  156. package/dist/assets/app/messages-CDgFOy4S.js.map +0 -1
  157. package/dist/assets/app/messages-CLbTz0o9.js +0 -4
  158. package/dist/assets/app/messages-CLbTz0o9.js.map +0 -1
  159. package/dist/assets/app/messages-CNwSh0t7.js +0 -4
  160. package/dist/assets/app/messages-CNwSh0t7.js.map +0 -1
  161. package/dist/assets/app/messages-CSMNJ6P8.js +0 -4
  162. package/dist/assets/app/messages-CSMNJ6P8.js.map +0 -1
  163. package/dist/assets/app/messages-CZQUw3mp.js +0 -4
  164. package/dist/assets/app/messages-CZQUw3mp.js.map +0 -1
  165. package/dist/assets/app/messages-CZT41oVp.js +0 -4
  166. package/dist/assets/app/messages-CZT41oVp.js.map +0 -1
  167. package/dist/assets/app/messages-C_b-d3t8.js +0 -4
  168. package/dist/assets/app/messages-C_b-d3t8.js.map +0 -1
  169. package/dist/assets/app/messages-C_u3MTc2.js +0 -4
  170. package/dist/assets/app/messages-C_u3MTc2.js.map +0 -1
  171. package/dist/assets/app/messages-Cn8nHZic.js +0 -4
  172. package/dist/assets/app/messages-Cn8nHZic.js.map +0 -1
  173. package/dist/assets/app/messages-CtDywJUm.js +0 -4
  174. package/dist/assets/app/messages-CtDywJUm.js.map +0 -1
  175. package/dist/assets/app/messages-CurtIjBF.js +0 -4
  176. package/dist/assets/app/messages-CurtIjBF.js.map +0 -1
  177. package/dist/assets/app/messages-Cv6zIbaP.js +0 -4
  178. package/dist/assets/app/messages-Cv6zIbaP.js.map +0 -1
  179. package/dist/assets/app/messages-D1eLQuPE.js +0 -4
  180. package/dist/assets/app/messages-D1eLQuPE.js.map +0 -1
  181. package/dist/assets/app/messages-D8vHEaYW.js +0 -4
  182. package/dist/assets/app/messages-D8vHEaYW.js.map +0 -1
  183. package/dist/assets/app/messages-DJ1Q4GeC.js +0 -4
  184. package/dist/assets/app/messages-DJ1Q4GeC.js.map +0 -1
  185. package/dist/assets/app/messages-DRL3exqd.js +0 -4
  186. package/dist/assets/app/messages-DRL3exqd.js.map +0 -1
  187. package/dist/assets/app/messages-DWLPQRTp.js +0 -4
  188. package/dist/assets/app/messages-DWLPQRTp.js.map +0 -1
  189. package/dist/assets/app/messages-DjVaE9YE.js +0 -4
  190. package/dist/assets/app/messages-DjVaE9YE.js.map +0 -1
  191. package/dist/assets/app/messages-DqpMfFJR.js +0 -4
  192. package/dist/assets/app/messages-DqpMfFJR.js.map +0 -1
  193. package/dist/assets/app/messages-ETjhJBEN.js +0 -4
  194. package/dist/assets/app/messages-ETjhJBEN.js.map +0 -1
  195. package/dist/assets/app/messages-EUKrgrGn.js +0 -4
  196. package/dist/assets/app/messages-EUKrgrGn.js.map +0 -1
  197. package/dist/assets/app/messages-QQrOUcPW.js +0 -4
  198. package/dist/assets/app/messages-QQrOUcPW.js.map +0 -1
  199. package/dist/assets/app/messages-e2QGqFL6.js +0 -4
  200. package/dist/assets/app/messages-e2QGqFL6.js.map +0 -1
  201. package/dist/assets/app/messages-p61py7gD.js +0 -4
  202. package/dist/assets/app/messages-p61py7gD.js.map +0 -1
  203. package/dist/assets/asset.d.ts +0 -9
  204. package/dist/assets/asset.d.ts.map +0 -1
  205. package/dist/assets/asset.js +0 -3
  206. package/dist/assets/asset.js.map +0 -1
  207. package/dist/assets/index.d.ts +0 -5
  208. package/dist/assets/index.d.ts.map +0 -1
  209. package/dist/assets/index.js +0 -78
  210. package/dist/assets/index.js.map +0 -1
  211. package/rollup.config.js +0 -98
  212. package/src/assets/app/app.tsx +0 -43
  213. package/src/assets/app/backend-data.ts +0 -27
  214. package/src/assets/app/backend-types.ts +0 -66
  215. package/src/assets/app/components/forms/button-toggle-visibility.tsx +0 -43
  216. package/src/assets/app/components/forms/button.tsx +0 -60
  217. package/src/assets/app/components/forms/fieldset.tsx +0 -55
  218. package/src/assets/app/components/forms/form-card-async.tsx +0 -103
  219. package/src/assets/app/components/forms/form-card.tsx +0 -49
  220. package/src/assets/app/components/forms/input-checkbox.tsx +0 -73
  221. package/src/assets/app/components/forms/input-container.tsx +0 -107
  222. package/src/assets/app/components/forms/input-email-address.tsx +0 -66
  223. package/src/assets/app/components/forms/input-new-password.tsx +0 -62
  224. package/src/assets/app/components/forms/input-password.tsx +0 -88
  225. package/src/assets/app/components/forms/input-text.tsx +0 -76
  226. package/src/assets/app/components/forms/input-token.tsx +0 -94
  227. package/src/assets/app/components/forms/wizard-card.tsx +0 -116
  228. package/src/assets/app/components/layouts/layout-title-page.tsx +0 -77
  229. package/src/assets/app/components/layouts/layout-welcome.tsx +0 -73
  230. package/src/assets/app/components/utils/account-identifier.tsx +0 -23
  231. package/src/assets/app/components/utils/account-image.tsx +0 -33
  232. package/src/assets/app/components/utils/admonition.tsx +0 -52
  233. package/src/assets/app/components/utils/client-name.tsx +0 -45
  234. package/src/assets/app/components/utils/error-card.tsx +0 -93
  235. package/src/assets/app/components/utils/error-message.tsx +0 -62
  236. package/src/assets/app/components/utils/help-card.tsx +0 -46
  237. package/src/assets/app/components/utils/icons.tsx +0 -88
  238. package/src/assets/app/components/utils/link-anchor.tsx +0 -28
  239. package/src/assets/app/components/utils/link-title.tsx +0 -26
  240. package/src/assets/app/components/utils/multi-lang-string.tsx +0 -56
  241. package/src/assets/app/components/utils/password-strength-label.tsx +0 -37
  242. package/src/assets/app/components/utils/password-strength-meter.tsx +0 -58
  243. package/src/assets/app/components/utils/url-viewer.tsx +0 -73
  244. package/src/assets/app/cookies.ts +0 -11
  245. package/src/assets/app/hooks/use-api.ts +0 -178
  246. package/src/assets/app/hooks/use-async-action.ts +0 -120
  247. package/src/assets/app/hooks/use-bound-dispatch.ts +0 -5
  248. package/src/assets/app/hooks/use-browser-color-scheme.ts +0 -31
  249. package/src/assets/app/hooks/use-csrf-token.ts +0 -5
  250. package/src/assets/app/hooks/use-random-string.ts +0 -37
  251. package/src/assets/app/hooks/use-stepper.ts +0 -87
  252. package/src/assets/app/index.html +0 -182
  253. package/src/assets/app/lib/api.ts +0 -267
  254. package/src/assets/app/lib/clsx.ts +0 -6
  255. package/src/assets/app/lib/json-client.ts +0 -94
  256. package/src/assets/app/lib/password.ts +0 -98
  257. package/src/assets/app/lib/ref.ts +0 -17
  258. package/src/assets/app/lib/util.ts +0 -13
  259. package/src/assets/app/locales/an/messages.po +0 -492
  260. package/src/assets/app/locales/ast/messages.po +0 -492
  261. package/src/assets/app/locales/ca/messages.po +0 -492
  262. package/src/assets/app/locales/da/messages.po +0 -492
  263. package/src/assets/app/locales/de/messages.po +0 -492
  264. package/src/assets/app/locales/el/messages.po +0 -492
  265. package/src/assets/app/locales/en/messages.po +0 -492
  266. package/src/assets/app/locales/en-GB/messages.po +0 -492
  267. package/src/assets/app/locales/es/messages.po +0 -492
  268. package/src/assets/app/locales/eu/messages.po +0 -492
  269. package/src/assets/app/locales/fi/messages.po +0 -492
  270. package/src/assets/app/locales/fr/messages.po +0 -492
  271. package/src/assets/app/locales/ga/messages.po +0 -492
  272. package/src/assets/app/locales/gl/messages.po +0 -492
  273. package/src/assets/app/locales/hi/messages.po +0 -492
  274. package/src/assets/app/locales/hu/messages.po +0 -492
  275. package/src/assets/app/locales/ia/messages.po +0 -492
  276. package/src/assets/app/locales/id/messages.po +0 -492
  277. package/src/assets/app/locales/it/messages.po +0 -492
  278. package/src/assets/app/locales/ja/messages.po +0 -492
  279. package/src/assets/app/locales/km/messages.po +0 -492
  280. package/src/assets/app/locales/ko/messages.po +0 -492
  281. package/src/assets/app/locales/load.ts +0 -8
  282. package/src/assets/app/locales/locale-context.ts +0 -19
  283. package/src/assets/app/locales/locale-provider.tsx +0 -112
  284. package/src/assets/app/locales/locale-selector.tsx +0 -58
  285. package/src/assets/app/locales/locales.ts +0 -168
  286. package/src/assets/app/locales/ne/messages.po +0 -492
  287. package/src/assets/app/locales/nl/messages.po +0 -492
  288. package/src/assets/app/locales/pl/messages.po +0 -492
  289. package/src/assets/app/locales/pt-BR/messages.po +0 -492
  290. package/src/assets/app/locales/ro/messages.po +0 -492
  291. package/src/assets/app/locales/ru/messages.po +0 -492
  292. package/src/assets/app/locales/sv/messages.po +0 -492
  293. package/src/assets/app/locales/th/messages.po +0 -492
  294. package/src/assets/app/locales/tr/messages.po +0 -492
  295. package/src/assets/app/locales/uk/messages.po +0 -492
  296. package/src/assets/app/locales/vi/messages.po +0 -492
  297. package/src/assets/app/locales/zh-CN/messages.po +0 -492
  298. package/src/assets/app/locales/zh-HK/messages.po +0 -492
  299. package/src/assets/app/locales/zh-TW/messages.po +0 -492
  300. package/src/assets/app/main.css +0 -33
  301. package/src/assets/app/main.tsx +0 -44
  302. package/src/assets/app/views/authorize/accept/accept-form.tsx +0 -150
  303. package/src/assets/app/views/authorize/accept/accept-view.tsx +0 -70
  304. package/src/assets/app/views/authorize/authorize-view.tsx +0 -180
  305. package/src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx +0 -88
  306. package/src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx +0 -80
  307. package/src/assets/app/views/authorize/reset-password/reset-password-view.tsx +0 -127
  308. package/src/assets/app/views/authorize/sign-in/sign-in-form.tsx +0 -244
  309. package/src/assets/app/views/authorize/sign-in/sign-in-picker.tsx +0 -116
  310. package/src/assets/app/views/authorize/sign-in/sign-in-view.tsx +0 -145
  311. package/src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx +0 -140
  312. package/src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx +0 -51
  313. package/src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx +0 -289
  314. package/src/assets/app/views/authorize/sign-up/sign-up-hcaptcha-form.tsx +0 -108
  315. package/src/assets/app/views/authorize/sign-up/sign-up-view.tsx +0 -158
  316. package/src/assets/app/views/authorize/welcome/welcome-view.tsx +0 -56
  317. package/src/assets/app/views/error/error-view.tsx +0 -31
  318. package/src/assets/asset.ts +0 -9
  319. package/src/assets/index.ts +0 -86
  320. package/tailwind.config.js +0 -31
  321. package/tsconfig.frontend.json +0 -11
  322. package/tsconfig.frontend.tsbuildinfo +0 -1
  323. package/tsconfig.tools.json +0 -8
  324. package/tsconfig.tools.tsbuildinfo +0 -1
  325. package/vite.config.mjs +0 -16
@@ -1,58 +0,0 @@
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,73 +0,0 @@
1
- import { JSX, useMemo } from 'react'
2
- import { Override } from '../../lib/util.ts'
3
-
4
- export type UrlPartRenderingOptions = {
5
- faded?: boolean
6
- bold?: boolean
7
- }
8
-
9
- export type UrlRendererProps = {
10
- url: string | URL
11
- proto?: boolean | UrlPartRenderingOptions
12
- host?: boolean | UrlPartRenderingOptions
13
- path?: boolean | UrlPartRenderingOptions
14
- query?: boolean | UrlPartRenderingOptions
15
- hash?: boolean | UrlPartRenderingOptions
16
- as?: string
17
- }
18
-
19
- export function UrlViewer<As extends keyof JSX.IntrinsicElements = 'span'>({
20
- url,
21
- proto = false,
22
- host = true,
23
- path = false,
24
- query = false,
25
- hash = false,
26
- as: As = 'span',
27
-
28
- // Element
29
- ...props
30
- }: Override<JSX.IntrinsicElements[As], UrlRendererProps>) {
31
- const urlObj = useMemo(() => new URL(url), [url])
32
-
33
- return (
34
- <As {...props}>
35
- {proto && (
36
- <UrlPartViewer
37
- value={`${urlObj.protocol}//`}
38
- {...(proto === true ? null : proto)}
39
- />
40
- )}
41
- {host && (
42
- <UrlPartViewer
43
- value={urlObj.host}
44
- {...(host === true ? { faded: false, bold: true } : host)}
45
- />
46
- )}
47
- {path && (
48
- <UrlPartViewer
49
- value={urlObj.pathname}
50
- {...(path === true ? null : path)}
51
- />
52
- )}
53
- {query && (
54
- <UrlPartViewer
55
- value={urlObj.search}
56
- {...(query === true ? null : query)}
57
- />
58
- )}
59
- {hash && (
60
- <UrlPartViewer value={urlObj.hash} {...(hash === true ? null : hash)} />
61
- )}
62
- </As>
63
- )
64
- }
65
-
66
- function UrlPartViewer({
67
- value,
68
- faded = true,
69
- bold = false,
70
- }: { value: string } & UrlPartRenderingOptions) {
71
- const Comp = bold ? 'b' : 'span'
72
- return <Comp className={faded ? 'opacity-50' : ''}>{value}</Comp>
73
- }
@@ -1,11 +0,0 @@
1
- export const parseCookieString = (
2
- cookie: string,
3
- ): Record<string, string | undefined> =>
4
- Object.fromEntries(
5
- cookie
6
- .split(';')
7
- .filter(Boolean)
8
- .map((str) => str.split('=', 2).map((s) => decodeURIComponent(s.trim()))),
9
- )
10
-
11
- export const cookies = parseCookieString(document.cookie)
@@ -1,178 +0,0 @@
1
- import { useLingui } from '@lingui/react/macro'
2
- import { useCallback, useMemo, useState } from 'react'
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'
18
-
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>()
29
-
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
- )
41
- }
42
-
43
- export type UseApiOptions = {
44
- requestUri: string
45
- sessions?: readonly Session[]
46
- newSessionsRequireConsent?: boolean
47
- onRedirected?: () => void
48
- }
49
-
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(
66
- (sub: string | null) => {
67
- setSessions((sessions) =>
68
- sub === (sessions.find((s) => s.selected)?.account.sub || null)
69
- ? sessions
70
- : sessions.map((s) => ({ ...s, selected: s.account.sub === sub })),
71
- )
72
- },
73
- [setSessions],
74
- )
75
-
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],
93
- )
94
-
95
- const performRedirect = useCallback(
96
- (url: URL) => {
97
- window.location.href = String(url)
98
- if (onRedirected) setTimeout(onRedirected)
99
- },
100
- [onRedirected],
101
- )
102
-
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
- )
114
-
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 })
132
- },
133
- [api],
134
- )
135
-
136
- const doValidateNewHandle = useSafeCallback(
137
- async (data: VerifyHandleAvailabilityData, signal?: AbortSignal) => {
138
- await api.fetch('/verify-handle-availability', data, { signal })
139
- },
140
- [api],
141
- )
142
-
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))
158
- },
159
- [api, performRedirect],
160
- )
161
-
162
- const doReject = useSafeCallback(async () => {
163
- performRedirect(api.buildRejectUrl())
164
- }, [api, performRedirect])
165
-
166
- return {
167
- sessions,
168
- selectSub,
169
-
170
- doSignIn,
171
- doInitiatePasswordReset,
172
- doConfirmResetPassword,
173
- doValidateNewHandle,
174
- doSignUp,
175
- doAccept,
176
- doReject,
177
- }
178
- }
@@ -1,120 +0,0 @@
1
- import {
2
- ForwardedRef,
3
- useCallback,
4
- useEffect,
5
- useImperativeHandle,
6
- useRef,
7
- useState,
8
- } from 'react'
9
-
10
- export type AsyncActionController = {
11
- reset: () => void
12
- }
13
-
14
- export type UseAsyncActionOptions = {
15
- ref?: ForwardedRef<AsyncActionController>
16
- onLoading?: (loading: boolean) => void
17
- onError?: (error: Error | undefined) => void
18
- }
19
-
20
- export function useAsyncAction(
21
- fn: (signal: AbortSignal) => void | PromiseLike<void>,
22
- { ref, onLoading, onError }: UseAsyncActionOptions = {},
23
- ) {
24
- const [loading, setLoading] = useState(false)
25
- const [error, setError] = useState<Error | undefined>()
26
-
27
- const doSetError = useCallback(
28
- (error: Error | undefined) => {
29
- setError(error)
30
- onError?.(error)
31
- },
32
- [onError],
33
- )
34
-
35
- const doSetLoading = useCallback(
36
- (loading: boolean) => {
37
- setLoading(loading)
38
- onLoading?.(loading)
39
- },
40
- [onLoading],
41
- )
42
-
43
- const controllerRef = useRef<AbortController>(null)
44
-
45
- const resetRef = useRef<() => void>(null)
46
- useEffect(() => {
47
- resetRef.current = () => {
48
- controllerRef.current?.abort()
49
- controllerRef.current = null
50
- doSetError(undefined)
51
- doSetLoading(false)
52
- }
53
- return () => {
54
- resetRef.current = null
55
- }
56
- }, [doSetError, doSetLoading])
57
-
58
- useImperativeHandle(
59
- ref,
60
- (): AsyncActionController => ({
61
- reset: () => resetRef.current?.(),
62
- }),
63
- [],
64
- )
65
-
66
- // Cancel pending action when unmounted
67
- useEffect(() => {
68
- return () => {
69
- controllerRef.current?.abort()
70
- controllerRef.current = null
71
- }
72
- }, [])
73
-
74
- const run = useCallback(async (): Promise<void> => {
75
- // Cancel previous run
76
- controllerRef.current?.abort()
77
-
78
- doSetLoading(true)
79
- doSetError(undefined)
80
-
81
- const controller = new AbortController()
82
- const { signal } = controller
83
-
84
- controllerRef.current = controller
85
-
86
- try {
87
- await fn(signal)
88
- } catch (err) {
89
- if (controller === controllerRef.current) {
90
- doSetError(err instanceof Error ? err : new Error(String(err)))
91
- } else {
92
- if (!isAbortReason(signal, err)) {
93
- console.warn('Async action error after abort', err)
94
- }
95
- }
96
- } finally {
97
- if (controller === controllerRef.current) {
98
- controllerRef.current = null
99
- doSetLoading(false)
100
- }
101
-
102
- controller.abort()
103
- }
104
- }, [fn, doSetLoading, doSetError])
105
-
106
- return {
107
- loading,
108
- error,
109
- run,
110
- }
111
- }
112
-
113
- function isAbortReason(signal: AbortSignal, err: unknown): boolean {
114
- return (
115
- signal.aborted &&
116
- (signal.reason === err ||
117
- signal.reason === err?.['cause'] ||
118
- (err instanceof DOMException && err.name === 'AbortError'))
119
- )
120
- }
@@ -1,5 +0,0 @@
1
- import { Dispatch, useCallback } from 'react'
2
-
3
- export function useBoundDispatch<A>(dispatch: Dispatch<A>, value: A) {
4
- return useCallback(() => dispatch(value), [dispatch, value])
5
- }
@@ -1,31 +0,0 @@
1
- import { useEffect, useState } from 'react'
2
-
3
- const query =
4
- typeof window === 'undefined'
5
- ? null
6
- : window.matchMedia('(prefers-color-scheme: dark)')
7
-
8
- export function useBrowserColorScheme() {
9
- const [theme, setTheme] = useState<'light' | 'dark'>(
10
- query?.matches ? 'dark' : 'light',
11
- )
12
-
13
- useEffect(() => {
14
- if (!query) return
15
-
16
- const listener = () => {
17
- setTheme(query.matches ? 'dark' : 'light')
18
- }
19
-
20
- query.addEventListener('change', listener)
21
-
22
- return () => {
23
- query.removeEventListener('change', listener)
24
- }
25
-
26
- // @NOTE "query" is a global constant and does not need to be part of the
27
- // array bellow:
28
- }, [])
29
-
30
- return theme
31
- }
@@ -1,5 +0,0 @@
1
- import { cookies } from '../cookies.ts'
2
-
3
- export function useCsrfToken(cookieName: string) {
4
- return cookies[cookieName]
5
- }
@@ -1,37 +0,0 @@
1
- import { useEffect, useState } from 'react'
2
-
3
- export const UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
4
- export const LOWER = UPPER.toLowerCase() as Lowercase<typeof UPPER>
5
- export const DIGITS = '0123456789'
6
-
7
- export const ALPHANUMERIC = `${UPPER}${LOWER}${DIGITS}` as const
8
-
9
- export type UseRandomStringOptions = BuildRandomStringOptions & {
10
- prefix?: string
11
- suffix?: string
12
- }
13
-
14
- export function useRandomString(options?: UseRandomStringOptions) {
15
- const [state, setState] = useState(() => buildRandomString(options))
16
- useEffect(() => {
17
- setState(buildRandomString(options))
18
- }, [options?.length, options?.alphabet])
19
-
20
- return `${options?.prefix ?? ''}${state}${options?.suffix ?? ''}`
21
- }
22
-
23
- type BuildRandomStringOptions = {
24
- length?: number
25
- alphabet?: string
26
- }
27
-
28
- function buildRandomString({
29
- length = 16,
30
- alphabet = ALPHANUMERIC,
31
- }: BuildRandomStringOptions = {}) {
32
- return Array.from({ length }, () => getRandomCharFrom(alphabet)).join('')
33
- }
34
-
35
- function getRandomCharFrom(alphabet: string) {
36
- return alphabet.charAt((Math.random() * alphabet.length) | 0)
37
- }
@@ -1,87 +0,0 @@
1
- import { useCallback, useEffect, useState } from 'react'
2
-
3
- export type DisabledStep = false | null | undefined
4
- export type Step = {
5
- invalid: boolean
6
- }
7
-
8
- const isEnabled = <S extends Step | DisabledStep>(
9
- s: S,
10
- ): s is S extends DisabledStep ? never : S => s != null && s !== false
11
- const isRequired = <S extends Step | DisabledStep>(
12
- s: S,
13
- ): s is S extends DisabledStep ? never : S & { invalid: true } =>
14
- isEnabled(s) && s.invalid === true
15
- const isCompleted = <S extends Step | DisabledStep>(
16
- s: S,
17
- ): s is S extends DisabledStep ? S : S & { invalid: false } =>
18
- !isEnabled(s) || s.invalid === false
19
-
20
- export function useStepper<const S extends Step>(
21
- steps: readonly (S | DisabledStep)[],
22
- ) {
23
- const firstIdx = steps.findIndex(isEnabled)
24
- const lastIdx = steps.findLastIndex(isEnabled)
25
- const requiredIdx = steps.findIndex(isRequired)
26
-
27
- const [currentIdx, setCurrentIdx] = useState<number>(firstIdx)
28
-
29
- const to = useCallback(
30
- (idx: number) => {
31
- if (idx !== -1 && steps[idx]) {
32
- setCurrentIdx(idx)
33
- return true
34
- } else {
35
- return false
36
- }
37
- },
38
- [steps.map(isEnabled).join()],
39
- )
40
-
41
- const prevIdx = steps.findLastIndex((s, i) => isEnabled(s) && i < currentIdx)
42
- const nextIdx = steps.findIndex((s, i) => isEnabled(s) && i > currentIdx)
43
-
44
- const toFirst = useCallback(() => to(firstIdx), [to, firstIdx])
45
- const toLast = useCallback(() => to(lastIdx), [to, lastIdx])
46
- const toPrev = useCallback(() => to(prevIdx), [to, prevIdx])
47
- const toNext = useCallback(() => to(nextIdx), [to, nextIdx])
48
- const toRequired = useCallback(() => to(requiredIdx), [to, requiredIdx])
49
-
50
- // Step number in user friendly terms (accounting for disabled steps)
51
- const currentPosition =
52
- currentIdx +
53
- // use "1 indexed position" (for user friendliness):
54
- 1 +
55
- // Adjust the position by counting the number of disabled steps before the
56
- // current step (if any):
57
- steps.reduce(
58
- (acc, s, i) => (i >= currentIdx || isEnabled(s) ? acc : acc - 1),
59
- 0,
60
- )
61
-
62
- const count = steps.filter(isEnabled).length
63
- const completed = steps.every(isCompleted)
64
-
65
- const current =
66
- currentIdx === -1 || !steps[currentIdx] ? undefined : steps[currentIdx]
67
-
68
- // Fool-proof (reset current step in case the current step becomes disabled)
69
- const broken = currentIdx === -1
70
- useEffect(() => {
71
- if (broken) toFirst()
72
- }, [broken])
73
-
74
- return {
75
- current,
76
- currentPosition,
77
- count,
78
- completed,
79
- atFirst: currentPosition === 1,
80
- atLast: currentPosition === count,
81
- toFirst,
82
- toLast,
83
- toPrev,
84
- toNext,
85
- toRequired,
86
- }
87
- }