@atproto/oauth-provider 0.6.6 → 0.7.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 (465) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/access-token/access-token-mode.d.ts +5 -0
  3. package/dist/access-token/access-token-mode.d.ts.map +1 -0
  4. package/dist/access-token/access-token-mode.js +9 -0
  5. package/dist/access-token/access-token-mode.js.map +1 -0
  6. package/dist/account/account-manager.d.ts +13 -7
  7. package/dist/account/account-manager.d.ts.map +1 -1
  8. package/dist/account/account-manager.js +69 -52
  9. package/dist/account/account-manager.js.map +1 -1
  10. package/dist/account/account-store.d.ts +88 -77
  11. package/dist/account/account-store.d.ts.map +1 -1
  12. package/dist/account/account-store.js +24 -73
  13. package/dist/account/account-store.js.map +1 -1
  14. package/dist/account/sign-in-data.d.ts +4 -13
  15. package/dist/account/sign-in-data.d.ts.map +1 -1
  16. package/dist/account/sign-in-data.js +9 -9
  17. package/dist/account/sign-in-data.js.map +1 -1
  18. package/dist/account/sign-up-input.d.ts +4 -5
  19. package/dist/account/sign-up-input.d.ts.map +1 -1
  20. package/dist/account/sign-up-input.js +13 -3
  21. package/dist/account/sign-up-input.js.map +1 -1
  22. package/dist/client/client-manager.d.ts +4 -1
  23. package/dist/client/client-manager.d.ts.map +1 -1
  24. package/dist/client/client-manager.js +13 -1
  25. package/dist/client/client-manager.js.map +1 -1
  26. package/dist/client/client-store.d.ts +1 -1
  27. package/dist/client/client-store.d.ts.map +1 -1
  28. package/dist/constants.d.ts +5 -1
  29. package/dist/constants.d.ts.map +1 -1
  30. package/dist/constants.js +6 -2
  31. package/dist/constants.js.map +1 -1
  32. package/dist/customization/branding.d.ts +54 -0
  33. package/dist/customization/branding.d.ts.map +1 -0
  34. package/dist/customization/branding.js +13 -0
  35. package/dist/customization/branding.js.map +1 -0
  36. package/dist/customization/build-customization-css.d.ts +3 -0
  37. package/dist/customization/build-customization-css.d.ts.map +1 -0
  38. package/dist/customization/build-customization-css.js +27 -0
  39. package/dist/customization/build-customization-css.js.map +1 -0
  40. package/dist/customization/build-customization-data.d.ts +4 -0
  41. package/dist/customization/build-customization-data.d.ts.map +1 -0
  42. package/dist/customization/build-customization-data.js +18 -0
  43. package/dist/customization/build-customization-data.js.map +1 -0
  44. package/dist/customization/colors.d.ts +7 -0
  45. package/dist/customization/colors.d.ts.map +1 -0
  46. package/dist/customization/colors.js +27 -0
  47. package/dist/customization/colors.js.map +1 -0
  48. package/dist/customization/customization.d.ts +129 -0
  49. package/dist/customization/customization.d.ts.map +1 -0
  50. package/dist/customization/customization.js +26 -0
  51. package/dist/customization/customization.js.map +1 -0
  52. package/dist/customization/links.d.ts +26 -0
  53. package/dist/customization/links.d.ts.map +1 -0
  54. package/dist/customization/links.js +12 -0
  55. package/dist/customization/links.js.map +1 -0
  56. package/dist/device/device-id.d.ts +1 -0
  57. package/dist/device/device-id.d.ts.map +1 -1
  58. package/dist/device/device-id.js +4 -0
  59. package/dist/device/device-id.js.map +1 -1
  60. package/dist/device/device-manager.d.ts +6 -36
  61. package/dist/device/device-manager.d.ts.map +1 -1
  62. package/dist/device/device-manager.js +49 -43
  63. package/dist/device/device-manager.js.map +1 -1
  64. package/dist/device/device-store.d.ts +1 -0
  65. package/dist/device/device-store.d.ts.map +1 -1
  66. package/dist/device/device-store.js.map +1 -1
  67. package/dist/dpop/dpop-manager.d.ts +3 -3
  68. package/dist/dpop/dpop-nonce.d.ts +3 -3
  69. package/dist/dpop/dpop-nonce.d.ts.map +1 -1
  70. package/dist/errors/access-denied-error.d.ts +4 -3
  71. package/dist/errors/access-denied-error.d.ts.map +1 -1
  72. package/dist/errors/access-denied-error.js +5 -6
  73. package/dist/errors/access-denied-error.js.map +1 -1
  74. package/dist/{output/build-error-payload.d.ts → errors/error-parser.d.ts} +1 -1
  75. package/dist/errors/error-parser.d.ts.map +1 -0
  76. package/dist/{output/build-error-payload.js → errors/error-parser.js} +2 -2
  77. package/dist/errors/error-parser.js.map +1 -0
  78. package/dist/errors/invalid-grant-error.d.ts +1 -0
  79. package/dist/errors/invalid-grant-error.d.ts.map +1 -1
  80. package/dist/errors/invalid-grant-error.js +5 -0
  81. package/dist/errors/invalid-grant-error.js.map +1 -1
  82. package/dist/errors/login-required-error.d.ts +1 -0
  83. package/dist/errors/login-required-error.d.ts.map +1 -1
  84. package/dist/errors/login-required-error.js +5 -0
  85. package/dist/errors/login-required-error.js.map +1 -1
  86. package/dist/index.d.ts +1 -0
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +1 -0
  89. package/dist/index.js.map +1 -1
  90. package/dist/lib/html/build-document.d.ts +2 -2
  91. package/dist/lib/html/build-document.d.ts.map +1 -1
  92. package/dist/lib/html/build-document.js +4 -0
  93. package/dist/lib/html/build-document.js.map +1 -1
  94. package/dist/lib/html/hydration-data.d.ts +4 -0
  95. package/dist/lib/html/hydration-data.d.ts.map +1 -0
  96. package/dist/{output/backend-data.js → lib/html/hydration-data.js} +8 -8
  97. package/dist/lib/html/hydration-data.js.map +1 -0
  98. package/dist/lib/html/tags.d.ts +1 -1
  99. package/dist/lib/html/tags.d.ts.map +1 -1
  100. package/dist/lib/html/tags.js +1 -1
  101. package/dist/lib/html/tags.js.map +1 -1
  102. package/dist/lib/http/accept.d.ts +2 -2
  103. package/dist/lib/http/accept.d.ts.map +1 -1
  104. package/dist/lib/http/accept.js +1 -1
  105. package/dist/lib/http/accept.js.map +1 -1
  106. package/dist/lib/http/context.d.ts +2 -4
  107. package/dist/lib/http/context.d.ts.map +1 -1
  108. package/dist/lib/http/context.js +29 -4
  109. package/dist/lib/http/context.js.map +1 -1
  110. package/dist/lib/http/headers.d.ts +3 -0
  111. package/dist/lib/http/headers.d.ts.map +1 -0
  112. package/dist/lib/http/headers.js +14 -0
  113. package/dist/lib/http/headers.js.map +1 -0
  114. package/dist/lib/http/index.d.ts +1 -0
  115. package/dist/lib/http/index.d.ts.map +1 -1
  116. package/dist/lib/http/index.js +1 -0
  117. package/dist/lib/http/index.js.map +1 -1
  118. package/dist/lib/http/middleware.d.ts +1 -1
  119. package/dist/lib/http/middleware.d.ts.map +1 -1
  120. package/dist/lib/http/middleware.js +8 -24
  121. package/dist/lib/http/middleware.js.map +1 -1
  122. package/dist/lib/http/parser.d.ts +3 -3
  123. package/dist/lib/http/parser.d.ts.map +1 -1
  124. package/dist/lib/http/request.d.ts +13 -9
  125. package/dist/lib/http/request.d.ts.map +1 -1
  126. package/dist/lib/http/request.js +27 -49
  127. package/dist/lib/http/request.js.map +1 -1
  128. package/dist/lib/http/response.d.ts +6 -2
  129. package/dist/lib/http/response.d.ts.map +1 -1
  130. package/dist/lib/http/response.js +31 -11
  131. package/dist/lib/http/response.js.map +1 -1
  132. package/dist/lib/http/route.d.ts +3 -3
  133. package/dist/lib/http/route.d.ts.map +1 -1
  134. package/dist/lib/http/route.js +1 -1
  135. package/dist/lib/http/route.js.map +1 -1
  136. package/dist/lib/http/router.d.ts +12 -11
  137. package/dist/lib/http/router.d.ts.map +1 -1
  138. package/dist/lib/http/router.js +26 -34
  139. package/dist/lib/http/router.js.map +1 -1
  140. package/dist/lib/http/security-headers.js +1 -1
  141. package/dist/lib/http/security-headers.js.map +1 -1
  142. package/dist/lib/http/stream.d.ts +3 -3
  143. package/dist/lib/http/stream.d.ts.map +1 -1
  144. package/dist/lib/http/types.d.ts +1 -1
  145. package/dist/lib/http/types.d.ts.map +1 -1
  146. package/dist/lib/send-web-page.d.ts +8 -0
  147. package/dist/lib/send-web-page.d.ts.map +1 -0
  148. package/dist/{output → lib}/send-web-page.js +9 -7
  149. package/dist/lib/send-web-page.js.map +1 -0
  150. package/dist/lib/util/authorization-header.d.ts.map +1 -1
  151. package/dist/lib/util/color.d.ts +32 -0
  152. package/dist/lib/util/color.d.ts.map +1 -0
  153. package/dist/lib/util/color.js +116 -0
  154. package/dist/lib/util/color.js.map +1 -0
  155. package/dist/lib/util/crypto.d.ts +1 -0
  156. package/dist/lib/util/crypto.d.ts.map +1 -1
  157. package/dist/lib/util/crypto.js +8 -3
  158. package/dist/lib/util/crypto.js.map +1 -1
  159. package/dist/lib/util/function.d.ts +1 -0
  160. package/dist/lib/util/function.d.ts.map +1 -1
  161. package/dist/lib/util/function.js +12 -0
  162. package/dist/lib/util/function.js.map +1 -1
  163. package/dist/lib/util/locale.d.ts +20 -0
  164. package/dist/lib/util/locale.d.ts.map +1 -0
  165. package/dist/lib/util/locale.js +14 -0
  166. package/dist/lib/util/locale.js.map +1 -0
  167. package/dist/lib/util/time.d.ts +1 -1
  168. package/dist/lib/util/time.d.ts.map +1 -1
  169. package/dist/lib/util/time.js +1 -1
  170. package/dist/lib/util/time.js.map +1 -1
  171. package/dist/lib/util/type.d.ts +22 -0
  172. package/dist/lib/util/type.d.ts.map +1 -1
  173. package/dist/lib/util/type.js.map +1 -1
  174. package/dist/lib/util/ui8.d.ts +4 -0
  175. package/dist/lib/util/ui8.d.ts.map +1 -0
  176. package/dist/lib/util/ui8.js +17 -0
  177. package/dist/lib/util/ui8.js.map +1 -0
  178. package/dist/lib/util/zod-error.d.ts +2 -0
  179. package/dist/lib/util/zod-error.d.ts.map +1 -0
  180. package/dist/lib/util/zod-error.js +16 -0
  181. package/dist/lib/util/zod-error.js.map +1 -0
  182. package/dist/oauth-errors.d.ts +22 -22
  183. package/dist/oauth-errors.d.ts.map +1 -1
  184. package/dist/oauth-errors.js +37 -45
  185. package/dist/oauth-errors.js.map +1 -1
  186. package/dist/oauth-hooks.d.ts +11 -23
  187. package/dist/oauth-hooks.d.ts.map +1 -1
  188. package/dist/oauth-hooks.js.map +1 -1
  189. package/dist/oauth-middleware.d.ts +12 -0
  190. package/dist/oauth-middleware.d.ts.map +1 -0
  191. package/dist/oauth-middleware.js +32 -0
  192. package/dist/oauth-middleware.js.map +1 -0
  193. package/dist/oauth-provider.d.ts +109 -113
  194. package/dist/oauth-provider.d.ts.map +1 -1
  195. package/dist/oauth-provider.js +124 -542
  196. package/dist/oauth-provider.js.map +1 -1
  197. package/dist/oauth-verifier.d.ts +7 -26
  198. package/dist/oauth-verifier.d.ts.map +1 -1
  199. package/dist/oauth-verifier.js +6 -16
  200. package/dist/oauth-verifier.js.map +1 -1
  201. package/dist/request/code.d.ts.map +1 -1
  202. package/dist/request/request-data.d.ts +2 -4
  203. package/dist/request/request-data.d.ts.map +1 -1
  204. package/dist/request/request-data.js.map +1 -1
  205. package/dist/request/request-manager.d.ts +4 -2
  206. package/dist/request/request-manager.d.ts.map +1 -1
  207. package/dist/request/request-manager.js +9 -8
  208. package/dist/request/request-manager.js.map +1 -1
  209. package/dist/request/request-store.d.ts +6 -0
  210. package/dist/request/request-store.d.ts.map +1 -1
  211. package/dist/request/request-store.js +3 -1
  212. package/dist/request/request-store.js.map +1 -1
  213. package/dist/result/authorization-redirect-parameters.d.ts +18 -0
  214. package/dist/result/authorization-redirect-parameters.d.ts.map +1 -0
  215. package/dist/result/authorization-redirect-parameters.js +3 -0
  216. package/dist/result/authorization-redirect-parameters.js.map +1 -0
  217. package/dist/result/authorization-result-authorize-page.d.ts +13 -0
  218. package/dist/result/authorization-result-authorize-page.d.ts.map +1 -0
  219. package/dist/result/authorization-result-authorize-page.js +3 -0
  220. package/dist/result/authorization-result-authorize-page.js.map +1 -0
  221. package/dist/result/authorization-result-redirect.d.ts +8 -0
  222. package/dist/result/authorization-result-redirect.d.ts.map +1 -0
  223. package/dist/result/authorization-result-redirect.js +3 -0
  224. package/dist/result/authorization-result-redirect.js.map +1 -0
  225. package/dist/router/assets/assets-manifest.d.ts +10 -0
  226. package/dist/router/assets/assets-manifest.d.ts.map +1 -0
  227. package/dist/router/assets/assets-manifest.js +77 -0
  228. package/dist/router/assets/assets-manifest.js.map +1 -0
  229. package/dist/router/assets/assets.d.ts +16 -0
  230. package/dist/router/assets/assets.d.ts.map +1 -0
  231. package/dist/router/assets/assets.js +43 -0
  232. package/dist/router/assets/assets.js.map +1 -0
  233. package/dist/router/assets/csrf.d.ts +4 -0
  234. package/dist/router/assets/csrf.d.ts.map +1 -0
  235. package/dist/router/assets/csrf.js +51 -0
  236. package/dist/router/assets/csrf.js.map +1 -0
  237. package/dist/router/assets/send-account-page.d.ts +7 -0
  238. package/dist/router/assets/send-account-page.d.ts.map +1 -0
  239. package/dist/router/assets/send-account-page.js +34 -0
  240. package/dist/router/assets/send-account-page.js.map +1 -0
  241. package/dist/router/assets/send-authorization-page.d.ts +5 -0
  242. package/dist/router/assets/send-authorization-page.d.ts.map +1 -0
  243. package/dist/router/assets/send-authorization-page.js +49 -0
  244. package/dist/router/assets/send-authorization-page.js.map +1 -0
  245. package/dist/router/assets/send-error-page.d.ts +4 -0
  246. package/dist/router/assets/send-error-page.d.ts.map +1 -0
  247. package/dist/router/assets/send-error-page.js +34 -0
  248. package/dist/router/assets/send-error-page.js.map +1 -0
  249. package/dist/router/create-account-page-middleware.d.ts +6 -0
  250. package/dist/router/create-account-page-middleware.d.ts.map +1 -0
  251. package/dist/router/create-account-page-middleware.js +39 -0
  252. package/dist/router/create-account-page-middleware.js.map +1 -0
  253. package/dist/router/create-api-middleware.d.ts +8 -0
  254. package/dist/router/create-api-middleware.d.ts.map +1 -0
  255. package/dist/router/create-api-middleware.js +501 -0
  256. package/dist/router/create-api-middleware.js.map +1 -0
  257. package/dist/router/create-authorization-page-middleware.d.ts +6 -0
  258. package/dist/router/create-authorization-page-middleware.d.ts.map +1 -0
  259. package/dist/router/create-authorization-page-middleware.js +104 -0
  260. package/dist/router/create-authorization-page-middleware.js.map +1 -0
  261. package/dist/router/create-oauth-middleware.d.ts +6 -0
  262. package/dist/router/create-oauth-middleware.d.ts.map +1 -0
  263. package/dist/router/create-oauth-middleware.js +142 -0
  264. package/dist/router/create-oauth-middleware.js.map +1 -0
  265. package/dist/router/error-handler.d.ts +3 -0
  266. package/dist/router/error-handler.d.ts.map +1 -0
  267. package/dist/{account/account.js → router/error-handler.js} +1 -1
  268. package/dist/router/error-handler.js.map +1 -0
  269. package/dist/router/middleware-options.d.ts +6 -0
  270. package/dist/router/middleware-options.d.ts.map +1 -0
  271. package/dist/router/middleware-options.js +3 -0
  272. package/dist/router/middleware-options.js.map +1 -0
  273. package/dist/router/send-redirect.d.ts +16 -0
  274. package/dist/router/send-redirect.d.ts.map +1 -0
  275. package/dist/{output/send-authorize-redirect.js → router/send-redirect.js} +40 -24
  276. package/dist/router/send-redirect.js.map +1 -0
  277. package/dist/{token/token-claims.d.ts → signer/api-token-payload.d.ts} +237 -232
  278. package/dist/signer/api-token-payload.d.ts.map +1 -0
  279. package/dist/signer/api-token-payload.js +17 -0
  280. package/dist/signer/api-token-payload.js.map +1 -0
  281. package/dist/signer/signed-token-payload.d.ts +164 -159
  282. package/dist/signer/signed-token-payload.d.ts.map +1 -1
  283. package/dist/signer/signed-token-payload.js +10 -16
  284. package/dist/signer/signed-token-payload.js.map +1 -1
  285. package/dist/signer/signer.d.ts +42 -11246
  286. package/dist/signer/signer.d.ts.map +1 -1
  287. package/dist/signer/signer.js +30 -15
  288. package/dist/signer/signer.js.map +1 -1
  289. package/dist/token/refresh-token.d.ts.map +1 -1
  290. package/dist/token/token-data.d.ts +1 -1
  291. package/dist/token/token-data.d.ts.map +1 -1
  292. package/dist/token/token-id.d.ts.map +1 -1
  293. package/dist/token/token-manager.d.ts +28 -26
  294. package/dist/token/token-manager.d.ts.map +1 -1
  295. package/dist/token/token-manager.js +138 -196
  296. package/dist/token/token-manager.js.map +1 -1
  297. package/dist/token/token-store.d.ts +4 -4
  298. package/dist/token/token-store.d.ts.map +1 -1
  299. package/dist/token/token-store.js +1 -0
  300. package/dist/token/token-store.js.map +1 -1
  301. package/dist/token/verify-token-claims.d.ts +3 -3
  302. package/dist/token/verify-token-claims.d.ts.map +1 -1
  303. package/dist/token/verify-token-claims.js +1 -1
  304. package/dist/token/verify-token-claims.js.map +1 -1
  305. package/dist/types/email-otp.d.ts +3 -0
  306. package/dist/types/email-otp.d.ts.map +1 -0
  307. package/dist/types/email-otp.js +6 -0
  308. package/dist/types/email-otp.js.map +1 -0
  309. package/dist/types/email.d.ts +3 -0
  310. package/dist/types/email.d.ts.map +1 -0
  311. package/dist/types/email.js +29 -0
  312. package/dist/types/email.js.map +1 -0
  313. package/dist/types/handle.d.ts +3 -0
  314. package/dist/types/handle.d.ts.map +1 -0
  315. package/dist/types/handle.js +22 -0
  316. package/dist/types/handle.js.map +1 -0
  317. package/dist/types/invite-code.d.ts +4 -0
  318. package/dist/types/invite-code.d.ts.map +1 -0
  319. package/dist/types/invite-code.js +6 -0
  320. package/dist/types/invite-code.js.map +1 -0
  321. package/dist/types/password.d.ts +4 -0
  322. package/dist/types/password.d.ts.map +1 -0
  323. package/dist/types/password.js +7 -0
  324. package/dist/types/password.js.map +1 -0
  325. package/package.json +11 -14
  326. package/src/access-token/access-token-mode.ts +4 -0
  327. package/src/account/account-manager.ts +105 -75
  328. package/src/account/account-store.ts +118 -114
  329. package/src/account/sign-in-data.ts +10 -10
  330. package/src/account/sign-up-input.ts +13 -4
  331. package/src/client/client-manager.ts +34 -2
  332. package/src/client/client-store.ts +1 -1
  333. package/src/constants.ts +6 -1
  334. package/src/customization/branding.ts +12 -0
  335. package/src/customization/build-customization-css.ts +30 -0
  336. package/src/customization/build-customization-data.ts +22 -0
  337. package/src/customization/colors.ts +30 -0
  338. package/src/customization/customization.ts +25 -0
  339. package/src/customization/links.ts +10 -0
  340. package/src/device/device-id.ts +5 -0
  341. package/src/device/device-manager.ts +76 -66
  342. package/src/device/device-store.ts +2 -0
  343. package/src/errors/access-denied-error.ts +24 -17
  344. package/src/{output/build-error-payload.ts → errors/error-parser.ts} +1 -1
  345. package/src/errors/invalid-grant-error.ts +5 -0
  346. package/src/errors/login-required-error.ts +10 -0
  347. package/src/index.ts +1 -0
  348. package/src/lib/html/build-document.ts +6 -4
  349. package/src/{output/backend-data.ts → lib/html/hydration-data.ts} +7 -5
  350. package/src/lib/html/tags.ts +2 -2
  351. package/src/lib/http/accept.ts +3 -3
  352. package/src/lib/http/context.ts +41 -10
  353. package/src/lib/http/headers.ts +15 -0
  354. package/src/lib/http/index.ts +1 -0
  355. package/src/lib/http/middleware.ts +8 -23
  356. package/src/lib/http/request.ts +40 -75
  357. package/src/lib/http/response.ts +39 -15
  358. package/src/lib/http/route.ts +8 -5
  359. package/src/lib/http/router.ts +40 -46
  360. package/src/lib/http/security-headers.ts +1 -1
  361. package/src/lib/http/types.ts +1 -6
  362. package/src/{output → lib}/send-web-page.ts +10 -9
  363. package/src/lib/util/color.ts +132 -0
  364. package/src/lib/util/crypto.ts +9 -4
  365. package/src/lib/util/function.ts +14 -0
  366. package/src/lib/util/locale.ts +18 -0
  367. package/src/lib/util/time.ts +3 -4
  368. package/src/lib/util/type.ts +24 -0
  369. package/src/lib/util/ui8.ts +14 -0
  370. package/src/lib/util/zod-error.ts +14 -0
  371. package/src/oauth-errors.ts +22 -22
  372. package/src/oauth-hooks.ts +11 -24
  373. package/src/oauth-middleware.ts +53 -0
  374. package/src/oauth-provider.ts +290 -1061
  375. package/src/oauth-verifier.ts +9 -55
  376. package/src/request/request-data.ts +5 -4
  377. package/src/request/request-manager.ts +11 -11
  378. package/src/request/request-store.ts +7 -0
  379. package/src/result/authorization-redirect-parameters.ts +24 -0
  380. package/src/result/authorization-result-authorize-page.ts +14 -0
  381. package/src/result/authorization-result-redirect.ts +8 -0
  382. package/src/router/assets/assets-manifest.ts +115 -0
  383. package/src/router/assets/assets.ts +54 -0
  384. package/src/router/assets/csrf.ts +63 -0
  385. package/src/router/assets/send-account-page.ts +43 -0
  386. package/src/router/assets/send-authorization-page.ts +62 -0
  387. package/src/router/assets/send-error-page.ts +42 -0
  388. package/src/router/create-account-page-middleware.ts +69 -0
  389. package/src/router/create-api-middleware.ts +814 -0
  390. package/src/router/create-authorization-page-middleware.ts +173 -0
  391. package/src/router/create-oauth-middleware.ts +247 -0
  392. package/src/router/error-handler.ts +6 -0
  393. package/src/router/middleware-options.ts +9 -0
  394. package/src/router/send-redirect.ts +142 -0
  395. package/src/signer/api-token-payload.ts +18 -0
  396. package/src/signer/signed-token-payload.ts +18 -28
  397. package/src/signer/signer.ts +49 -34
  398. package/src/token/token-data.ts +1 -1
  399. package/src/token/token-manager.ts +190 -239
  400. package/src/token/token-store.ts +6 -4
  401. package/src/token/verify-token-claims.ts +4 -4
  402. package/src/types/email-otp.ts +3 -0
  403. package/src/types/email.ts +26 -0
  404. package/src/types/handle.ts +18 -0
  405. package/src/types/invite-code.ts +4 -0
  406. package/src/types/password.ts +4 -0
  407. package/tsconfig.build.tsbuildinfo +1 -0
  408. package/tsconfig.json +1 -1
  409. package/dist/access-token/access-token-type.d.ts +0 -6
  410. package/dist/access-token/access-token-type.d.ts.map +0 -1
  411. package/dist/access-token/access-token-type.js +0 -10
  412. package/dist/access-token/access-token-type.js.map +0 -1
  413. package/dist/account/account.d.ts +0 -2
  414. package/dist/account/account.d.ts.map +0 -1
  415. package/dist/account/account.js.map +0 -1
  416. package/dist/assets/assets-middleware.d.ts +0 -5
  417. package/dist/assets/assets-middleware.d.ts.map +0 -1
  418. package/dist/assets/assets-middleware.js +0 -41
  419. package/dist/assets/assets-middleware.js.map +0 -1
  420. package/dist/lib/locale.d.ts +0 -15
  421. package/dist/lib/locale.d.ts.map +0 -1
  422. package/dist/lib/locale.js +0 -17
  423. package/dist/lib/locale.js.map +0 -1
  424. package/dist/output/backend-data.d.ts +0 -4
  425. package/dist/output/backend-data.d.ts.map +0 -1
  426. package/dist/output/backend-data.js.map +0 -1
  427. package/dist/output/build-authorize-data.d.ts +0 -29
  428. package/dist/output/build-authorize-data.d.ts.map +0 -1
  429. package/dist/output/build-authorize-data.js +0 -21
  430. package/dist/output/build-authorize-data.js.map +0 -1
  431. package/dist/output/build-customization-data.d.ts +0 -234
  432. package/dist/output/build-customization-data.d.ts.map +0 -1
  433. package/dist/output/build-customization-data.js +0 -174
  434. package/dist/output/build-customization-data.js.map +0 -1
  435. package/dist/output/build-error-data.d.ts +0 -3
  436. package/dist/output/build-error-data.d.ts.map +0 -1
  437. package/dist/output/build-error-data.js +0 -10
  438. package/dist/output/build-error-data.js.map +0 -1
  439. package/dist/output/build-error-payload.d.ts.map +0 -1
  440. package/dist/output/build-error-payload.js.map +0 -1
  441. package/dist/output/output-manager.d.ts +0 -28
  442. package/dist/output/output-manager.d.ts.map +0 -1
  443. package/dist/output/output-manager.js +0 -134
  444. package/dist/output/output-manager.js.map +0 -1
  445. package/dist/output/send-authorize-redirect.d.ts +0 -25
  446. package/dist/output/send-authorize-redirect.d.ts.map +0 -1
  447. package/dist/output/send-authorize-redirect.js.map +0 -1
  448. package/dist/output/send-web-page.d.ts +0 -8
  449. package/dist/output/send-web-page.d.ts.map +0 -1
  450. package/dist/output/send-web-page.js.map +0 -1
  451. package/dist/token/token-claims.d.ts.map +0 -1
  452. package/dist/token/token-claims.js +0 -27
  453. package/dist/token/token-claims.js.map +0 -1
  454. package/src/access-token/access-token-type.ts +0 -5
  455. package/src/account/account.ts +0 -1
  456. package/src/assets/assets-middleware.ts +0 -44
  457. package/src/lib/locale.ts +0 -21
  458. package/src/output/build-authorize-data.ts +0 -53
  459. package/src/output/build-customization-data.ts +0 -217
  460. package/src/output/build-error-data.ts +0 -8
  461. package/src/output/output-manager.ts +0 -188
  462. package/src/output/send-authorize-redirect.ts +0 -137
  463. package/src/token/token-claims.ts +0 -30
  464. package/tsconfig.backend.tsbuildinfo +0 -1
  465. /package/{tsconfig.backend.json → tsconfig.build.json} +0 -0
@@ -1,10 +1,13 @@
1
- import { randomBytes } from 'node:crypto'
2
1
  import type { IncomingMessage, ServerResponse } from 'node:http'
3
2
  import { languages, mediaType } from '@hapi/accept'
4
- import { parse as parseCookie, serialize as serializeCookie } from 'cookie'
3
+ import {
4
+ CookieSerializeOptions,
5
+ parse as parseCookie,
6
+ serialize as serializeCookie,
7
+ } from 'cookie'
5
8
  import forwarded from 'forwarded'
6
9
  import createHttpError from 'http-errors'
7
- import { appendHeader } from './response.js'
10
+ import { appendHeader } from './headers.js'
8
11
  import { UrlReference, urlMatch } from './url.js'
9
12
 
10
13
  export function validateHeaderValue(
@@ -30,7 +33,6 @@ export function validateHeaderValue(
30
33
 
31
34
  export function validateFetchMode(
32
35
  req: IncomingMessage,
33
- res: ServerResponse,
34
36
  expectedMode: readonly (
35
37
  | null
36
38
  | 'navigate'
@@ -44,7 +46,6 @@ export function validateFetchMode(
44
46
 
45
47
  export function validateFetchDest(
46
48
  req: IncomingMessage,
47
- res: ServerResponse,
48
49
  expectedDest: readonly (
49
50
  | null
50
51
  | 'document'
@@ -68,7 +69,6 @@ export function validateFetchDest(
68
69
 
69
70
  export function validateFetchSite(
70
71
  req: IncomingMessage,
71
- res: ServerResponse,
72
72
  expectedSite: readonly (
73
73
  | null
74
74
  | 'same-origin'
@@ -80,103 +80,68 @@ export function validateFetchSite(
80
80
  validateHeaderValue(req, 'sec-fetch-site', expectedSite)
81
81
  }
82
82
 
83
- export function validateReferer(
83
+ export function validateReferrer(
84
84
  req: IncomingMessage,
85
- res: ServerResponse,
86
85
  reference: UrlReference,
87
86
  allowNull: true,
88
87
  ): URL | null
89
- export function validateReferer(
88
+ export function validateReferrer(
90
89
  req: IncomingMessage,
91
- res: ServerResponse,
92
90
  reference: UrlReference,
93
91
  allowNull?: false,
94
92
  ): URL
95
- export function validateReferer(
93
+ export function validateReferrer(
96
94
  req: IncomingMessage,
97
- res: ServerResponse,
98
95
  reference: UrlReference,
99
96
  allowNull = false,
100
97
  ) {
101
- const referer = req.headers['referer']
102
- const refererUrl = referer ? new URL(referer) : null
103
- if (refererUrl ? !urlMatch(refererUrl, reference) : !allowNull) {
104
- throw createHttpError(400, `Invalid referer ${referer}`)
98
+ // @NOTE The header name "referer" is actually a misspelling of the word
99
+ // "referrer". https://en.wikipedia.org/wiki/HTTP_referer
100
+ const referrer = req.headers['referer']
101
+ const referrerUrl = referrer ? new URL(referrer) : null
102
+ if (referrerUrl ? !urlMatch(referrerUrl, reference) : !allowNull) {
103
+ throw createHttpError(400, `Invalid referrer ${referrer}`)
105
104
  }
106
- return refererUrl
105
+ return referrerUrl
107
106
  }
108
107
 
109
- export async function setupCsrfToken(
108
+ export function validateOrigin(
110
109
  req: IncomingMessage,
111
- res: ServerResponse,
112
- cookieName = 'csrf_token',
113
- ) {
114
- const csrfToken = randomBytes(8).toString('hex')
115
- appendHeader(
116
- res,
117
- 'Set-Cookie',
118
- serializeCookie(cookieName, csrfToken, {
119
- secure: true,
120
- httpOnly: false,
121
- sameSite: 'lax',
122
- path: req.url?.split('?', 1)[0] || '/',
123
- }),
124
- )
125
- }
126
-
127
- // CORS ensure not cross origin
128
- export function validateSameOrigin(
129
- req: IncomingMessage,
130
- res: ServerResponse,
131
- origin: string,
132
- allowNull = true,
110
+ expectedOrigin: string,
111
+ optional = true,
133
112
  ) {
134
113
  const reqOrigin = req.headers['origin']
135
- if (reqOrigin ? reqOrigin !== origin : !allowNull) {
114
+ if (reqOrigin ? reqOrigin !== expectedOrigin : !optional) {
136
115
  throw createHttpError(400, `Invalid origin ${reqOrigin}`)
137
116
  }
138
117
  }
139
118
 
140
- export function validateCsrfToken(
141
- req: IncomingMessage,
119
+ export type { CookieSerializeOptions }
120
+
121
+ export function setCookie(
142
122
  res: ServerResponse,
143
- csrfToken: unknown,
144
- cookieName = 'csrf_token',
145
- clearCookie = false,
123
+ cookieName: string,
124
+ value: string,
125
+ options?: CookieSerializeOptions,
146
126
  ) {
147
- const cookies = parseHttpCookies(req)
148
- if (
149
- typeof csrfToken !== 'string' ||
150
- !csrfToken ||
151
- !cookies ||
152
- !cookieName ||
153
- cookies[cookieName] !== csrfToken
154
- ) {
155
- throw createHttpError(400, `Invalid CSRF token`)
156
- }
127
+ appendHeader(res, 'Set-Cookie', serializeCookie(cookieName, value, options))
128
+ }
157
129
 
158
- if (clearCookie) {
159
- appendHeader(
160
- res,
161
- 'Set-Cookie',
162
- serializeCookie(cookieName, '', {
163
- secure: true,
164
- httpOnly: false,
165
- sameSite: 'lax',
166
- maxAge: 0,
167
- }),
168
- )
169
- }
130
+ export function clearCookie(
131
+ res: ServerResponse,
132
+ cookieName: string,
133
+ options?: Omit<CookieSerializeOptions, 'maxAge' | 'expires'>,
134
+ ) {
135
+ setCookie(res, cookieName, '', { ...options, maxAge: 0 })
170
136
  }
171
137
 
172
138
  export function parseHttpCookies(
173
- req: IncomingMessage,
174
- ): null | Record<string, undefined | string> {
175
- return 'cookies' in req && req.cookies // Already parsed by another middleware
176
- ? (req.cookies as any)
177
- : req.headers['cookie']
178
- ? ((req as any).cookies = parseCookie(req.headers['cookie']))
179
- : null
139
+ req: IncomingMessage & { cookies?: any },
140
+ ): Record<string, undefined | string> {
141
+ req.cookies ??= req.headers['cookie']
142
+ ? parseCookie(req.headers['cookie'])
143
+ : Object.create(null)
144
+ return req.cookies
180
145
  }
181
146
 
182
147
  export type ExtractRequestMetadataOptions = {
@@ -1,25 +1,14 @@
1
- import type { ServerResponse } from 'node:http'
1
+ import type { IncomingMessage, ServerResponse } from 'node:http'
2
2
  import { type Readable, pipeline } from 'node:stream'
3
+ import createHttpError from 'http-errors'
4
+ import { Awaitable } from '../util/type.js'
5
+ import { negotiateResponseContent } from './request.js'
3
6
  import {
4
7
  SecurityHeadersOptions,
5
8
  setSecurityHeaders,
6
9
  } from './security-headers.js'
7
10
  import type { Handler, Middleware } from './types.js'
8
11
 
9
- export function appendHeader(
10
- res: ServerResponse,
11
- header: string,
12
- value: string | readonly string[],
13
- ): void {
14
- const existing = res.getHeader(header)
15
- if (existing == null) {
16
- res.setHeader(header, value)
17
- } else {
18
- const arr = Array.isArray(existing) ? existing : [String(existing)]
19
- res.setHeader(header, arr.concat(value))
20
- }
21
- }
22
-
23
12
  export function writeRedirect(
24
13
  res: ServerResponse,
25
14
  url: string,
@@ -111,3 +100,38 @@ export function cacheControlMiddleware(maxAge: number): Middleware<void> {
111
100
  next()
112
101
  }
113
102
  }
103
+
104
+ export function jsonHandler<
105
+ T,
106
+ Req extends IncomingMessage = IncomingMessage,
107
+ Res extends ServerResponse = ServerResponse,
108
+ >(
109
+ buildJson: (
110
+ this: T,
111
+ req: Req,
112
+ res: Res,
113
+ ) => Awaitable<{ payload: unknown; status?: number }>,
114
+ ): Middleware<T, Req, Res> {
115
+ return function (req, res, next) {
116
+ // Ensure we can agree on a content encoding & type before starting to
117
+ // build the JSON response.
118
+ if (negotiateResponseContent(req, ['application/json'])) {
119
+ // A middleware should not be async, so we wrap the async operation in a
120
+ // promise and return it.
121
+ void (async () => {
122
+ try {
123
+ const { payload, status = 200 } = await buildJson.call(this, req, res)
124
+ writeJson(res, payload, { status })
125
+ } catch (cause) {
126
+ const error =
127
+ cause instanceof Error
128
+ ? cause
129
+ : new Error('Failed to build JSON response', { cause })
130
+ next(error satisfies Error)
131
+ }
132
+ })()
133
+ } else {
134
+ next(createHttpError(406, 'Unsupported media type'))
135
+ }
136
+ }
137
+ }
@@ -5,9 +5,12 @@ import { combineMiddlewares } from './middleware.js'
5
5
  import { Params, Path, createPathMatcher } from './path.js'
6
6
  import { Middleware } from './types.js'
7
7
 
8
- export type RouteCtx<T, P extends Params> = SubCtx<T, { params: Readonly<P> }>
8
+ export type RouteCtx<
9
+ T extends object | void,
10
+ P extends Params = Params,
11
+ > = SubCtx<T, { params: Readonly<P> }>
9
12
  export type RouteMiddleware<
10
- T,
13
+ T extends object | void,
11
14
  P extends Params,
12
15
  Req = IncomingMessage,
13
16
  Res = ServerResponse,
@@ -28,8 +31,8 @@ export type RouteMiddleware<
28
31
  * ```
29
32
  */
30
33
  export function createRoute<
31
- P extends Params,
32
- T = void,
34
+ P extends Params = Params,
35
+ T extends object | void = void,
33
36
  Req extends IncomingMessage = IncomingMessage,
34
37
  Res extends ServerResponse = ServerResponse,
35
38
  >(
@@ -47,7 +50,7 @@ export function createRoute<
47
50
  const pathname = req.url?.split('?')[0] ?? '/'
48
51
  const params = paramsMatcher(pathname)
49
52
  if (params) {
50
- const context = subCtx(this, 'params', params)
53
+ const context = subCtx(this, { params })
51
54
  return middleware.call(context, req, res, next)
52
55
  }
53
56
  }
@@ -1,33 +1,37 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http'
2
2
  import { SubCtx, subCtx } from './context.js'
3
3
  import { MethodMatcherInput } from './method.js'
4
- import { asHandler, combineMiddlewares } from './middleware.js'
4
+ import { combineMiddlewares } from './middleware.js'
5
5
  import { Params, Path } from './path.js'
6
6
  import { RouteMiddleware, createRoute } from './route.js'
7
7
  import { Middleware } from './types.js'
8
8
 
9
- export type RouterCtx<T> = SubCtx<T, { url: Readonly<URL> }>
9
+ export type RouterCtx<T extends object | void = void> = SubCtx<
10
+ T,
11
+ { url: Readonly<URL> }
12
+ >
13
+
10
14
  export type RouterMiddleware<
11
- T = void,
15
+ T extends object | void = void,
12
16
  Req = IncomingMessage,
13
17
  Res = ServerResponse,
14
18
  > = Middleware<RouterCtx<T>, Req, Res>
15
19
 
20
+ export type RouterConfig = {
21
+ /** Used to build the origin of the {@link RouterCtx['url']} context property */
22
+ protocol?: string
23
+ /** Used to build the origin of the {@link RouterCtx['url']} context property */
24
+ host?: string
25
+ }
26
+
16
27
  export class Router<
17
- T = void,
28
+ T extends object | void = void,
18
29
  Req extends IncomingMessage = IncomingMessage,
19
30
  Res extends ServerResponse = ServerResponse,
20
31
  > {
21
32
  private readonly middlewares: RouterMiddleware<T, Req, Res>[] = []
22
33
 
23
- constructor(
24
- private readonly url?: {
25
- /** Used to build the origin of the {@link RouterCtx['url']} context property */
26
- protocol?: string
27
- /** Used to build the origin of the {@link RouterCtx['url']} context property */
28
- host?: string
29
- },
30
- ) {}
34
+ constructor(private readonly config?: RouterConfig) {}
31
35
 
32
36
  use(...middlewares: RouterMiddleware<T, Req, Res>[]) {
33
37
  this.middlewares.push(...middlewares)
@@ -73,8 +77,8 @@ export class Router<
73
77
  /**
74
78
  * @returns router middleware which dispatches a route matching the request.
75
79
  */
76
- buildHandler() {
77
- const routerUrl = this.url
80
+ buildMiddleware(): Middleware<T, Req, Res> {
81
+ const { config } = this
78
82
 
79
83
  // Calling next('router') from a middleware will skip all the remaining
80
84
  // middlewares in the stack.
@@ -82,38 +86,28 @@ export class Router<
82
86
  skipKeyword: 'router',
83
87
  })
84
88
 
85
- return asHandler<Middleware<T, Req, Res>>(function (this, req, res, next) {
86
- // Make sure that the context contains a "url". This will allow the add()
87
- // method to match routes based on the pathname and will allow routes to
88
- // access the query params (through this.url.searchParams).
89
- let url: URL
90
-
91
- if (
92
- !routerUrl &&
93
- this != null &&
94
- typeof this === 'object' &&
95
- 'url' in this &&
96
- this.url instanceof URL
97
- ) {
98
- // If the context already contains a "url" (router inside router), let's
99
- // use it.
100
- url = this.url
101
- } else {
102
- // Parse the URL using node's URL parser.
103
- try {
104
- const protocol = routerUrl?.protocol || 'https:'
105
- const host = req.headers.host || routerUrl?.host || 'localhost'
106
- const pathname = req.url || '/'
107
- url = new URL(pathname, `${protocol}//${host}`)
108
- } catch (cause) {
109
- const error =
110
- cause instanceof Error ? cause : new Error('Invalid URL', { cause })
111
- return next(Object.assign(error, { status: 400, statusCode: 400 }))
112
- }
113
- }
114
-
115
- const context = subCtx(this, 'url', url)
89
+ return function (this, req, res, next) {
90
+ // Parse the URL using node's URL parser.
91
+ const url = extractUrl(req, config)
92
+ if (url instanceof Error) return next(url)
93
+
94
+ // Any error thrown here will be uncaught/unhandled (a middleware should
95
+ // never throw)
96
+ const context = subCtx(this, { url })
116
97
  middleware.call(context, req, res, next)
117
- })
98
+ }
99
+ }
100
+ }
101
+
102
+ function extractUrl(req: IncomingMessage, config?: RouterConfig): URL | Error {
103
+ try {
104
+ const protocol = config?.protocol || 'https:'
105
+ const host = config?.host || req.headers.host || 'localhost'
106
+ const pathname = req.url || '/'
107
+ return new URL(pathname, `${protocol}//${host}`)
108
+ } catch (cause) {
109
+ const error =
110
+ cause instanceof Error ? cause : new Error('Invalid URL', { cause })
111
+ return Object.assign(error, { status: 400, statusCode: 400 })
118
112
  }
119
113
  }
@@ -75,7 +75,7 @@ export function setSecurityHeaders(
75
75
  res.setHeader('Strict-Transport-Security', buildHstsValue(hsts))
76
76
  }
77
77
 
78
- // @TODO: make these headers configurable (?)
78
+ // @TODO make these headers configurable (?)
79
79
  res.setHeader('Permissions-Policy', 'otp-credentials=*, document-domain=()')
80
80
  res.setHeader('Referrer-Policy', 'same-origin')
81
81
  res.setHeader('X-Frame-Options', 'DENY')
@@ -6,12 +6,7 @@ export type Middleware<
6
6
  T = void,
7
7
  Req = IncomingMessage,
8
8
  Res = ServerResponse,
9
- > = (
10
- this: T,
11
- req: Req,
12
- res: Res,
13
- next: NextFunction,
14
- ) => void | PromiseLike<void>
9
+ > = (this: T, req: Req, res: Res, next: NextFunction) => void
15
10
 
16
11
  export type Handler<T = void, Req = IncomingMessage, Res = ServerResponse> = (
17
12
  this: T,
@@ -1,13 +1,13 @@
1
1
  import { createHash } from 'node:crypto'
2
2
  import type { ServerResponse } from 'node:http'
3
- import { CspConfig, CspValue, mergeCsp } from '../lib/csp/index.js'
3
+ import { CspConfig, CspValue, mergeCsp } from './csp/index.js'
4
4
  import {
5
5
  AssetRef,
6
6
  BuildDocumentOptions,
7
7
  Html,
8
8
  buildDocument,
9
- } from '../lib/html/index.js'
10
- import { WriteHtmlOptions, writeHtml } from '../lib/http/response.js'
9
+ } from './html/index.js'
10
+ import { WriteHtmlOptions, writeHtml } from './http/response.js'
11
11
 
12
12
  export const DEFAULT_CSP: CspConfig = {
13
13
  'upgrade-insecure-requests': true,
@@ -16,10 +16,10 @@ export const DEFAULT_CSP: CspConfig = {
16
16
 
17
17
  export type SendWebPageOptions = BuildDocumentOptions & WriteHtmlOptions
18
18
 
19
- export async function sendWebPage(
19
+ export function sendWebPage(
20
20
  res: ServerResponse,
21
21
  { csp: inputCsp, ...options }: SendWebPageOptions,
22
- ): Promise<void> {
22
+ ): void {
23
23
  // @NOTE the csp string might be quite long. In that case it might be tempting
24
24
  // to set it through the http-equiv <meta> in the HTML. However, some
25
25
  // directives cannot be enforced by browsers when set through the meta tag
@@ -27,15 +27,16 @@ export async function sendWebPage(
27
27
  // HTTP header.
28
28
  const csp = mergeCsp(DEFAULT_CSP, inputCsp, {
29
29
  'base-uri': options.base?.origin as undefined | `https://${string}`,
30
- 'script-src': options.scripts?.map(assetToCsp),
31
- 'style-src': options.styles?.map(assetToCsp),
30
+ 'script-src': options.scripts?.map(assetToCsp).filter((v) => v != null),
31
+ 'style-src': options.styles?.map(assetToCsp).filter((v) => v != null),
32
32
  })
33
33
 
34
34
  const html = buildDocument(options).toString()
35
- return writeHtml(res, html, { ...options, csp })
35
+ writeHtml(res, html, { ...options, csp })
36
36
  }
37
37
 
38
- function assetToCsp(asset: Html | AssetRef): CspValue {
38
+ function assetToCsp(asset?: Html | AssetRef): undefined | CspValue {
39
+ if (asset == null) return undefined
39
40
  if (asset instanceof Html) {
40
41
  // Inline assets are "allowed" by their hash
41
42
  const hash = createHash('sha256')
@@ -0,0 +1,132 @@
1
+ import { parseUi8Dec, parseUi8Hex } from './ui8.js'
2
+
3
+ export type RgbColor = { r: number; g: number; b: number }
4
+ export type HslColor = { h: number; s: number; l: number }
5
+ export type RgbaColor = { r: number; g: number; b: number; a: number }
6
+ export type HslaColor = { h: number; s: number; l: number; a: number }
7
+
8
+ export function parseColor(color: string): RgbColor | RgbaColor {
9
+ if (color.startsWith('#')) {
10
+ return parseHexColor(color)
11
+ }
12
+
13
+ if (color.startsWith('rgba(')) {
14
+ return parseRgbaColor(color)
15
+ }
16
+
17
+ if (color.startsWith('rgb(')) {
18
+ return parseRgbColor(color)
19
+ }
20
+
21
+ // Should never happen (as long as the input is a validated WebColor)
22
+ throw new TypeError(`Invalid color value: ${color}`)
23
+ }
24
+
25
+ export function parseHexColor(v: string): RgbColor | RgbaColor {
26
+ // parseInt('az', 16) does not return NaN so we need to check the format
27
+ if (!/^#[0-9a-f]+$/i.test(v)) {
28
+ throw new TypeError(`Invalid hex color value: ${v}`)
29
+ }
30
+
31
+ if (v.length === 4 || v.length === 5) {
32
+ const r = parseUi8Hex(v[1].repeat(2))
33
+ const g = parseUi8Hex(v[2].repeat(2))
34
+ const b = parseUi8Hex(v[3].repeat(2))
35
+ const a = v.length > 4 ? parseUi8Hex(v[4].repeat(2)) : undefined
36
+ return a == null ? { r, g, b } : { r, g, b, a }
37
+ }
38
+
39
+ if (v.length === 7 || v.length === 9) {
40
+ const r = parseUi8Hex(v.slice(1, 3))
41
+ const g = parseUi8Hex(v.slice(3, 5))
42
+ const b = parseUi8Hex(v.slice(5, 7))
43
+ const a = v.length > 8 ? parseUi8Hex(v.slice(7, 9)) : undefined
44
+ return a == null ? { r, g, b } : { r, g, b, a }
45
+ }
46
+
47
+ throw new TypeError(`Invalid hex color value: ${v}`)
48
+ }
49
+
50
+ export function parseRgbColor(v: string): RgbColor {
51
+ const matches = v.match(/^\s*rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/)
52
+ if (!matches) throw new TypeError(`Invalid rgb color value: ${v}`)
53
+
54
+ const r = parseUi8Dec(matches[1])
55
+ const g = parseUi8Dec(matches[2])
56
+ const b = parseUi8Dec(matches[3])
57
+ return { r, g, b }
58
+ }
59
+
60
+ export function parseRgbaColor(v: string): RgbaColor {
61
+ const matches = v.match(
62
+ /^\s*rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/,
63
+ )
64
+ if (!matches) throw new TypeError(`Invalid rgba color value: ${v}`)
65
+
66
+ const r = parseUi8Dec(matches[1])
67
+ const g = parseUi8Dec(matches[2])
68
+ const b = parseUi8Dec(matches[3])
69
+ const a = parseUi8Dec(matches[4])
70
+ return { r, g, b, a }
71
+ }
72
+
73
+ /**
74
+ * Return the color that has the best contrast with the reference color.
75
+ */
76
+ export function pickContrastColor(ref: RgbColor, a: RgbColor, b: RgbColor) {
77
+ return computeContrastRatio(ref, a) > computeContrastRatio(ref, b) ? a : b
78
+ }
79
+
80
+ /**
81
+ * @see {@link https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef}
82
+ */
83
+ function relativeLuminance({ r, g, b }: RgbColor) {
84
+ return rgbLum(r) * 0.2126 + rgbLum(g) * 0.7152 + rgbLum(b) * 0.0722
85
+ }
86
+
87
+ function rgbLum(value) {
88
+ const rgb = value / 255
89
+ return rgb < 0.03928 ? rgb / 12.92 : Math.pow((rgb + 0.055) / 1.055, 2.4)
90
+ }
91
+
92
+ /**
93
+ * @see {@link https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef}
94
+ */
95
+ function computeContrastRatio(a: RgbColor, b: RgbColor) {
96
+ const aLum = relativeLuminance(a)
97
+ const bLum = relativeLuminance(b)
98
+ const [lighter, darker] = aLum > bLum ? [aLum, bLum] : [bLum, aLum]
99
+ return (lighter + 0.05) / (darker + 0.05)
100
+ }
101
+
102
+ export function extractHue(input: RgbColor): number {
103
+ const r = input.r / 255
104
+ const g = input.g / 255
105
+ const b = input.b / 255
106
+
107
+ const max = Math.max(r, g, b)
108
+ const min = Math.min(r, g, b)
109
+
110
+ const chroma = max - min
111
+
112
+ switch (max) {
113
+ case min:
114
+ return 0 // Achromatic
115
+ case r: {
116
+ const segment = (g - b) / chroma
117
+ const shift = segment < 0 ? 360 / 60 : 0 / 60
118
+ return 60 * (segment + shift)
119
+ }
120
+ case g: {
121
+ const segment = (b - r) / chroma
122
+ const shift = 120 / 60
123
+ return 60 * (segment + shift)
124
+ }
125
+ // "default" needed for type safety. In practice, should be same as "case b:"
126
+ default: {
127
+ const segment = (r - g) / chroma
128
+ const shift = 240 / 60
129
+ return 60 * (segment + shift)
130
+ }
131
+ }
132
+ }
@@ -1,16 +1,21 @@
1
1
  import { randomBytes } from 'node:crypto'
2
2
 
3
- export async function randomHexId(bytesLength = 16) {
4
- return new Promise<string>((resolve, reject) => {
3
+ export async function randomBuffer(bytesLength = 16) {
4
+ return new Promise<Buffer>((resolve, reject) => {
5
5
  randomBytes(bytesLength, (err, buf) => {
6
6
  if (err) return reject(err)
7
- resolve(buf.toString('hex'))
7
+ resolve(buf)
8
8
  })
9
9
  })
10
10
  }
11
11
 
12
+ export async function randomHexId(bytesLength = 16) {
13
+ const buffer = await randomBuffer(bytesLength)
14
+ return buffer.toString('hex')
15
+ }
16
+
12
17
  // Basically all algorithms supported by "jose"'s jwtVerify().
13
- // @TODO: Is there a way to get this list from the runtime instead of hardcoding it?
18
+ // @TODO Is there a way to get this list from the runtime instead of hardcoding it?
14
19
  export const VERIFY_ALGOS = [
15
20
  'RS256',
16
21
  'RS384',
@@ -19,3 +19,17 @@ export async function callAsync<F extends (...args: any[]) => unknown>(
19
19
  ): Promise<Awaited<ReturnType<F>> | undefined> {
20
20
  return (await fn?.(...args)) as Awaited<ReturnType<F>> | undefined
21
21
  }
22
+
23
+ export function invokeOnce<T extends (this: any, ...a: any[]) => any>(
24
+ fn: T,
25
+ ): T {
26
+ let fnNullable: T | null = fn
27
+ return function (...args) {
28
+ if (fnNullable) {
29
+ const fn = fnNullable
30
+ fnNullable = null
31
+ return fn.call(this, ...args)
32
+ }
33
+ throw new Error('Function called multiple times')
34
+ } as T
35
+ }
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod'
2
+
3
+ export const localeSchema = z
4
+ .string()
5
+ .regex(/^[a-z]{2,3}(-[A-Z]{2})?$/, 'Invalid locale')
6
+ export type Locale = z.infer<typeof localeSchema>
7
+
8
+ export const multiLangStringSchema = z.intersection(
9
+ z.object({ en: z.string() }), // en is required
10
+ z.record(localeSchema, z.string().optional()),
11
+ )
12
+ export type MultiLangString = z.infer<typeof multiLangStringSchema>
13
+
14
+ export const localizedStringSchema = z.union([
15
+ z.string(),
16
+ multiLangStringSchema,
17
+ ])
18
+ export type LocalizedString = z.infer<typeof localizedStringSchema>