@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,11 +1,19 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http'
2
- import { serialize as serializeCookie } from 'cookie'
3
2
  import { z } from 'zod'
4
3
  import { SESSION_FIXATION_MAX_AGE } from '../constants.js'
5
- import { appendHeader, parseHttpCookies } from '../lib/http/index.js'
6
- import { RequestMetadata, extractRequestMetadata } from '../lib/http/request.js'
4
+ import { parseHttpCookies } from '../lib/http/index.js'
5
+ import {
6
+ RequestMetadata,
7
+ extractRequestMetadata,
8
+ setCookie,
9
+ } from '../lib/http/request.js'
7
10
  import { DeviceData } from './device-data.js'
8
- import { DeviceId, deviceIdSchema, generateDeviceId } from './device-id.js'
11
+ import {
12
+ DeviceId,
13
+ deviceIdSchema,
14
+ generateDeviceId,
15
+ isDeviceId,
16
+ } from './device-id.js'
9
17
  import { DeviceStore } from './device-store.js'
10
18
  import { generateSessionId, sessionIdSchema } from './session-id.js'
11
19
 
@@ -41,24 +49,6 @@ export const deviceManagerOptionsSchema = z.object({
41
49
  cookie: z
42
50
  .object({
43
51
  keys: keygripSchema.optional(),
44
- /**
45
- * Name of the cookie used to identify the device
46
- *
47
- * @default 'session-id'
48
- */
49
- device: z.string().default('device-id'),
50
- /**
51
- * Name of the cookie used to identify the session
52
- *
53
- * @default 'session-id'
54
- */
55
- session: z.string().default('session-id'),
56
- /**
57
- * Url path for the cookie
58
- *
59
- * @default '/oauth/authorize'
60
- */
61
- path: z.string().default('/oauth/authorize'),
62
52
  /**
63
53
  * Amount of time (in ms) after which the session cookie will expire.
64
54
  * If set to `null`, the cookie will be a session cookie (deleted when the
@@ -88,8 +78,10 @@ export const deviceManagerOptionsSchema = z.object({
88
78
 
89
79
  export type DeviceManagerOptions = z.input<typeof deviceManagerOptionsSchema>
90
80
 
91
- const cookieValueSchema = z.tuple([deviceIdSchema, sessionIdSchema])
92
- type CookieValue = z.infer<typeof cookieValueSchema>
81
+ type CookieValue = {
82
+ deviceId: DeviceId
83
+ sessionId: string
84
+ }
93
85
 
94
86
  export type DeviceInfo = {
95
87
  deviceId: DeviceId
@@ -116,7 +108,7 @@ export class DeviceManager {
116
108
  res: ServerResponse,
117
109
  forceRotate = false,
118
110
  ): Promise<DeviceInfo> {
119
- const cookie = await this.getCookie(req)
111
+ const cookie = await this.getCookies(req, res)
120
112
  if (cookie) {
121
113
  return this.refresh(
122
114
  req,
@@ -147,7 +139,7 @@ export class DeviceManager {
147
139
  ipAddress: deviceMetadata.ipAddress,
148
140
  })
149
141
 
150
- this.setCookie(res, [deviceId, sessionId])
142
+ await this.setCookies(req, res, { deviceId, sessionId })
151
143
 
152
144
  return { deviceId, deviceMetadata }
153
145
  }
@@ -155,7 +147,7 @@ export class DeviceManager {
155
147
  private async refresh(
156
148
  req: IncomingMessage,
157
149
  res: ServerResponse,
158
- [deviceId, sessionId]: CookieValue,
150
+ { deviceId, sessionId }: CookieValue,
159
151
  forceRotate = false,
160
152
  ): Promise<DeviceInfo> {
161
153
  const data = await this.store.readDevice(deviceId)
@@ -207,36 +199,56 @@ export class DeviceManager {
207
199
  lastSeenAt: new Date(),
208
200
  })
209
201
 
210
- this.setCookie(res, [deviceId, sessionId])
202
+ await this.setCookies(req, res, { deviceId, sessionId })
211
203
  }
212
204
 
213
- private async getCookie(
205
+ private async getCookies(
214
206
  req: IncomingMessage,
207
+ res: ServerResponse,
215
208
  ): Promise<{ value: CookieValue; mustRotate: boolean } | null> {
216
209
  const cookies = parseHttpCookies(req)
217
- if (!cookies) return null
218
-
219
- const device = this.parseCookie(
220
- cookies,
221
- this.options.cookie.device,
222
- deviceIdSchema,
223
- )
224
- const session = this.parseCookie(
225
- cookies,
226
- this.options.cookie.session,
227
- sessionIdSchema,
228
- )
210
+
211
+ // Old cookies were set for the "/oauth/authorize" path while new cookies
212
+ // need to be set for the "/" path (in order to be valid on the api,
213
+ // authorization page and account page). This means that if a user has both
214
+ // cookies set, the browser would use the old cookie for the
215
+ // "/oauth/authorize" path and the new cookie for all other paths. Because
216
+ // of this, different "phantom" sessions would be created for the same
217
+ // device. To avoid this, we needed to change the cookie name. We can still
218
+ // attempt to read the old cookie in order to carry over the session from
219
+ // the "/oauth/authorize" path to the "/" path. This will only work if the
220
+ // user visits the "/oauth/authorize" path first.
221
+
222
+ const device =
223
+ this.parseCookie(cookies, `dev-id`, deviceIdSchema) ||
224
+ this.parseCookie(cookies, 'device-id', deviceIdSchema)
225
+ const session =
226
+ this.parseCookie(cookies, `ses-id`, sessionIdSchema) ||
227
+ this.parseCookie(cookies, 'session-id', sessionIdSchema)
228
+
229
+ const deviceId = device?.value
230
+ const sessionId = session?.value
231
+
232
+ // Clear the legacy cookies, if they are set.
233
+ if (isDeviceId(cookies['device-id']) && cookies['device-id'] !== deviceId) {
234
+ await this.store.deleteDevice(cookies['device-id'])
235
+ }
236
+ if (cookies['device-id'] || cookies['session-id']) {
237
+ const options = { path: '/oauth/authorize', maxAge: 0 } as const
238
+ setCookie(res, 'device-id', '', options)
239
+ setCookie(res, 'session-id', '', options)
240
+ }
229
241
 
230
242
  // Silently ignore invalid cookies
231
- if (!device || !session) {
243
+ if (!deviceId || !sessionId) {
232
244
  // If the device cookie is valid, let's cleanup the DB
233
- if (device) await this.store.deleteDevice(device.value)
245
+ if (deviceId) await this.store.deleteDevice(deviceId)
234
246
 
235
247
  return null
236
248
  }
237
249
 
238
250
  return {
239
- value: [device.value, session.value],
251
+ value: { deviceId, sessionId },
240
252
  mustRotate: device.mustRotate || session.mustRotate,
241
253
  }
242
254
  }
@@ -246,16 +258,21 @@ export class DeviceManager {
246
258
  name: string,
247
259
  schema: z.ZodType<T> | z.ZodEffects<z.ZodTypeAny, T, string>,
248
260
  ): null | { value: T; mustRotate: boolean } {
249
- const result = schema.safeParse(cookies[name], { path: ['cookie', name] })
261
+ const rawValue = Object.hasOwn(cookies, name) ? cookies[name] : null
262
+ if (!rawValue) return null
263
+
264
+ const result = schema.safeParse(rawValue, { path: ['cookie', name] })
250
265
  if (!result.success) return null
251
266
 
252
267
  const value = result.data
253
268
 
254
269
  if (this.options.cookie.keys) {
255
- const hash = cookies[`${name}:hash`]
270
+ const hashName = `${name}:hash`
271
+
272
+ const hash = Object.hasOwn(cookies, hashName) ? cookies[hashName] : null
256
273
  if (!hash) return null
257
274
 
258
- const idx = this.options.cookie.keys.index(value, hash)
275
+ const idx = this.options.cookie.keys.index(rawValue, hash)
259
276
  if (idx < 0) return null
260
277
 
261
278
  return { value, mustRotate: idx !== 0 }
@@ -264,9 +281,13 @@ export class DeviceManager {
264
281
  return { value, mustRotate: false }
265
282
  }
266
283
 
267
- private setCookie(res: ServerResponse, cookieValue: null | CookieValue) {
268
- this.writeCookie(res, this.options.cookie.device, cookieValue?.[0])
269
- this.writeCookie(res, this.options.cookie.session, cookieValue?.[1])
284
+ private async setCookies(
285
+ req: IncomingMessage,
286
+ res: ServerResponse,
287
+ { deviceId, sessionId }: CookieValue,
288
+ ) {
289
+ this.writeCookie(res, `dev-id`, deviceId)
290
+ this.writeCookie(res, `ses-id`, sessionId)
270
291
  }
271
292
 
272
293
  private writeCookie(res: ServerResponse, name: string, value?: string) {
@@ -277,27 +298,16 @@ export class DeviceManager {
277
298
  : this.options.cookie.age / 1000
278
299
  : 0,
279
300
  httpOnly: true,
280
- path: this.options.cookie.path,
301
+ path: '/',
281
302
  secure: this.options.cookie.secure !== false,
282
- sameSite: this.options.cookie.sameSite === 'lax' ? 'lax' : 'strict',
303
+ sameSite: this.options.cookie.sameSite,
283
304
  } as const
284
305
 
285
- appendHeader(
286
- res,
287
- 'Set-Cookie',
288
- serializeCookie(name, value || '', cookieOptions),
289
- )
306
+ setCookie(res, name, value || '', cookieOptions)
290
307
 
291
308
  if (this.options.cookie.keys) {
292
- appendHeader(
293
- res,
294
- 'Set-Cookie',
295
- serializeCookie(
296
- `${name}:hash`,
297
- value ? this.options.cookie.keys.sign(value) : '',
298
- cookieOptions,
299
- ),
300
- )
309
+ const hash = value ? this.options.cookie.keys.sign(value) : ''
310
+ setCookie(res, `${name}:hash`, hash, cookieOptions)
301
311
  }
302
312
  }
303
313
 
@@ -7,6 +7,8 @@ export * from './device-data.js'
7
7
  export * from './device-id.js'
8
8
  export * from './session-id.js'
9
9
 
10
+ export type { Awaitable }
11
+
10
12
  export interface DeviceStore {
11
13
  createDevice(deviceId: DeviceId, data: DeviceData): Awaitable<void>
12
14
  readDevice(deviceId: DeviceId): Awaitable<DeviceData | null>
@@ -1,12 +1,27 @@
1
- import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
2
- import { buildErrorPayload } from '../output/build-error-payload.js'
1
+ import {
2
+ OAuthAuthenticationErrorResponse,
3
+ OAuthAuthorizationRequestParameters,
4
+ OidcAuthenticationErrorResponse,
5
+ } from '@atproto/oauth-types'
6
+ import { buildErrorPayload } from './error-parser.js'
3
7
  import { OAuthError } from './oauth-error.js'
4
8
 
9
+ export type AuthenticationErrorResponse =
10
+ | OAuthAuthenticationErrorResponse
11
+ // OIDC authentication error response are not part of the ATproto flavoured
12
+ // OAuth but we allow them because they provide better feedback to the client
13
+ // (in particular when SSO is used).
14
+ | OidcAuthenticationErrorResponse
15
+ // This error is defined by rfc9396 (not part of the OAuth 2.1 or OIDC). But
16
+ // since, in ATproto flavoured OAuth, client registration is a dynamic part of
17
+ // the authorization process, we allow it.
18
+ | 'invalid_authorization_details'
19
+
5
20
  export class AccessDeniedError extends OAuthError {
6
21
  constructor(
7
22
  public readonly parameters: OAuthAuthorizationRequestParameters,
8
23
  error_description: string,
9
- error = 'access_denied',
24
+ error: AuthenticationErrorResponse = 'access_denied',
10
25
  cause?: unknown,
11
26
  ) {
12
27
  super(error, error_description, 400, cause)
@@ -14,19 +29,11 @@ export class AccessDeniedError extends OAuthError {
14
29
 
15
30
  static from(
16
31
  parameters: OAuthAuthorizationRequestParameters,
17
- cause?: unknown,
18
- fallbackError?: string,
19
- ) {
20
- if (cause && cause instanceof AccessDeniedError) {
21
- return cause
22
- }
23
-
24
- const { error, error_description } = buildErrorPayload(cause)
25
- return new AccessDeniedError(
26
- parameters,
27
- error_description,
28
- fallbackError ?? error,
29
- cause,
30
- )
32
+ cause: unknown,
33
+ error: AuthenticationErrorResponse,
34
+ ): AccessDeniedError {
35
+ if (cause instanceof AccessDeniedError) return cause
36
+ const { error_description } = buildErrorPayload(cause)
37
+ return new AccessDeniedError(parameters, error_description, error, cause)
31
38
  }
32
39
  }
@@ -1,7 +1,7 @@
1
1
  import { errors } from 'jose'
2
2
  import { ZodError } from 'zod'
3
3
  import { JwtVerifyError } from '@atproto/jwk'
4
- import { OAuthError } from '../errors/oauth-error.js'
4
+ import { OAuthError } from './oauth-error.js'
5
5
 
6
6
  const { JOSEError } = errors
7
7
 
@@ -13,4 +13,9 @@ export class InvalidGrantError extends OAuthError {
13
13
  constructor(error_description: string, cause?: unknown) {
14
14
  super('invalid_grant', error_description, 400, cause)
15
15
  }
16
+
17
+ static from(err: unknown, error_description: string): InvalidGrantError {
18
+ if (err instanceof InvalidGrantError) return err
19
+ return new InvalidGrantError(error_description, err)
20
+ }
16
21
  }
@@ -9,4 +9,14 @@ export class LoginRequiredError extends AccessDeniedError {
9
9
  ) {
10
10
  super(parameters, error_description, 'login_required', cause)
11
11
  }
12
+
13
+ static from(
14
+ parameters: OAuthAuthorizationRequestParameters,
15
+ cause?: unknown,
16
+ fallbackError?: string,
17
+ ): LoginRequiredError {
18
+ if (cause instanceof LoginRequiredError) return cause
19
+
20
+ return new LoginRequiredError(parameters, fallbackError, cause)
21
+ }
12
22
  }
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export * from './oauth-client.js'
10
10
  export * from './oauth-dpop.js'
11
11
  export * from './oauth-errors.js'
12
12
  export * from './oauth-hooks.js'
13
+ export * from './oauth-middleware.js'
13
14
  export * from './oauth-provider.js'
14
15
  export * from './oauth-store.js'
15
16
  export * from './oauth-verifier.js'
@@ -61,8 +61,8 @@ export type BuildDocumentOptions = {
61
61
  preloads?: readonly AssetRef[]
62
62
  head?: HtmlValue
63
63
  title?: HtmlValue
64
- scripts?: readonly (Html | AssetRef)[]
65
- styles?: readonly (Html | AssetRef)[]
64
+ scripts?: readonly (Html | AssetRef | undefined)[]
65
+ styles?: readonly (Html | AssetRef | undefined)[]
66
66
  body?: HtmlValue
67
67
  bodyAttrs?: Attrs
68
68
  }
@@ -134,14 +134,16 @@ function linkPreload(asset: AssetRef) {
134
134
  return undefined
135
135
  }
136
136
 
137
- function scriptToHtml(script: Html | AssetRef) {
137
+ function scriptToHtml(script?: Html | AssetRef): Html | undefined {
138
+ if (script == null) return undefined
138
139
  return script instanceof Html
139
140
  ? // prettier-ignore
140
141
  html`<script>${script}</script>` // hash validity requires no space around the content
141
142
  : html`<script type="module" src="${script.url}"></script>`
142
143
  }
143
144
 
144
- function styleToHtml(style: Html | AssetRef) {
145
+ function styleToHtml(style?: Html | AssetRef): Html | undefined {
146
+ if (style == null) return undefined
145
147
  return style instanceof Html
146
148
  ? // prettier-ignore
147
149
  html`<style>${style}</style>` // hash validity requires no space around the content
@@ -1,14 +1,16 @@
1
- import { Html, js } from '../lib/html/index.js'
1
+ import { Html, js } from './index.js'
2
2
 
3
- export function declareBackendData(values: Record<string, unknown>): Html {
4
- return Html.dangerouslyCreate(backendDataGenerator(values))
3
+ export function declareHydrationData<T extends Record<string, unknown>>(
4
+ values: T,
5
+ ): Html {
6
+ return Html.dangerouslyCreate(hydrationDataGenerator(values))
5
7
  }
6
8
 
7
- export function* backendDataGenerator(
9
+ export function* hydrationDataGenerator(
8
10
  values: Record<string, unknown>,
9
11
  ): Generator<Html> {
10
12
  for (const [key, val] of Object.entries(values)) {
11
- yield js`window[${key}]=${val};`
13
+ yield js`window[${key}]=JSON.parse(${JSON.stringify(val)});`
12
14
  }
13
15
  // The script tag is removed after the data is assigned to the global
14
16
  // variables to prevent other scripts from reading the values. The "app"
@@ -54,5 +54,5 @@ export const jsonCode = (value: unknown) =>
54
54
  /**
55
55
  * Escapes a value to be uses as CSS styles inside a `<style>` tag.
56
56
  */
57
- export const cssCode = (code: string) =>
58
- Html.dangerouslyCreate(cssEscaper(code))
57
+ export const cssCode = (code?: string) =>
58
+ code ? Html.dangerouslyCreate(cssEscaper(code)) : undefined
@@ -4,7 +4,7 @@ import { SubCtx, subCtx } from './context.js'
4
4
  import { Middleware, NextFunction } from './types.js'
5
5
 
6
6
  type View<
7
- T,
7
+ T extends object | void,
8
8
  D,
9
9
  Req extends IncomingMessage = IncomingMessage,
10
10
  Res extends ServerResponse = ServerResponse,
@@ -38,7 +38,7 @@ type View<
38
38
  */
39
39
  export function acceptMiddleware<
40
40
  D,
41
- T = void,
41
+ T extends object | void = void,
42
42
  Req extends IncomingMessage = IncomingMessage,
43
43
  Res extends ServerResponse = ServerResponse,
44
44
  >(
@@ -71,7 +71,7 @@ export function acceptMiddleware<
71
71
 
72
72
  if (view) {
73
73
  const data = await controller.call(this, req, res)
74
- const ctx = subCtx(this, 'data', data)
74
+ const ctx = subCtx(this, { data })
75
75
  if (type) res.setHeader('Content-Type', type)
76
76
 
77
77
  await view.call(ctx, req, res, next)
@@ -1,11 +1,42 @@
1
- export type SubCtx<Parent, Child> = Child & Omit<Parent, keyof Child>
2
-
3
- export function subCtx<T, K extends string, V>(
4
- ctx: T,
5
- key: K,
6
- value: V,
7
- ): SubCtx<T, { [_ in K]: V }> {
8
- return Object.create(typeof ctx === 'object' ? ctx : null, {
9
- [key]: { value, enumerable: true, writable: false },
10
- })
1
+ export type SubCtx<Parent extends object | void, Child extends object> = Child &
2
+ Omit<Parent, keyof Child>
3
+
4
+ export function subCtx<Parent extends object | void, Child extends object>(
5
+ parent: Parent,
6
+ child: Child,
7
+ ): SubCtx<Parent, Child> {
8
+ const proto = typeof parent === 'object' ? parent : null
9
+ const entries = Object.entries(child)
10
+
11
+ // Optimization for small objects
12
+ switch (entries.length) {
13
+ case 0:
14
+ return Object.create(proto)
15
+ case 1: {
16
+ const e0 = entries[0]
17
+ return Object.create(proto, {
18
+ [e0[0]]: valueDescriptor(e0[1]),
19
+ })
20
+ }
21
+ case 2: {
22
+ const e0 = entries[0]
23
+ const e1 = entries[1]
24
+ return Object.create(proto, {
25
+ [e0[0]]: valueDescriptor(e0[1]),
26
+ [e1[0]]: valueDescriptor(e1[1]),
27
+ })
28
+ }
29
+ }
30
+
31
+ return Object.create(proto, Object.fromEntries(entries.map(entryToEntryDesc)))
32
+ }
33
+
34
+ function entryToEntryDesc(
35
+ entry: [string, unknown],
36
+ ): [string, PropertyDescriptor] {
37
+ return [entry[0], valueDescriptor(entry[1])]
38
+ }
39
+
40
+ function valueDescriptor(value: unknown): PropertyDescriptor {
41
+ return { value, enumerable: true, writable: false }
11
42
  }
@@ -0,0 +1,15 @@
1
+ import type { ServerResponse } from 'node:http'
2
+
3
+ export function appendHeader(
4
+ res: ServerResponse,
5
+ header: string,
6
+ value: string | readonly string[],
7
+ ): void {
8
+ const existing = res.getHeader(header)
9
+ if (existing == null) {
10
+ res.setHeader(header, value)
11
+ } else {
12
+ const arr = Array.isArray(existing) ? existing : [String(existing)]
13
+ res.setHeader(header, arr.concat(value))
14
+ }
15
+ }
@@ -1,5 +1,6 @@
1
1
  export * from './accept.js'
2
2
  export * from './context.js'
3
+ export * from './headers.js'
3
4
  export * from './middleware.js'
4
5
  export * from './parser.js'
5
6
  export * from './request.js'
@@ -1,4 +1,5 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http'
2
+ import { invokeOnce } from '../util/function.js'
2
3
  import { writeJson } from './response.js'
3
4
  import { Handler, Middleware, NextFunction } from './types.js'
4
5
 
@@ -33,13 +34,8 @@ export function combineMiddlewares(
33
34
  next()
34
35
  } else {
35
36
  const currentMiddleware = middlewaresArray[i++]!
36
- const currentNext = once(nextMiddleware)
37
- try {
38
- const result = currentMiddleware.call(this, req, res, currentNext)
39
- Promise.resolve(result).catch(currentNext)
40
- } catch (err) {
41
- currentNext(err)
42
- }
37
+ const currentNext = invokeOnce(nextMiddleware)
38
+ currentMiddleware.call(this, req, res, currentNext)
43
39
  }
44
40
  }
45
41
  nextMiddleware()
@@ -63,12 +59,14 @@ export function asHandler<M extends Middleware<any, any, any>>(
63
59
  this,
64
60
  req,
65
61
  res,
66
- next = once(createFinalHandler(req, res, options)),
62
+ next = invokeOnce(createFinalHandler(req, res, options)),
67
63
  ) {
68
64
  return middleware.call(this, req, res, next)
69
65
  } as AsHandler<M>
70
66
  }
71
67
 
68
+ export const DEV_MODE = process.env['NODE_ENV'] === 'development'
69
+
72
70
  export type FinalHandlerOptions = {
73
71
  debug?: boolean
74
72
  }
@@ -79,7 +77,7 @@ export function createFinalHandler(
79
77
  options?: FinalHandlerOptions,
80
78
  ): NextFunction {
81
79
  return (err) => {
82
- if (err && (options?.debug ?? process.env['NODE_ENV'] === 'development')) {
80
+ if (err != null && (options?.debug ?? DEV_MODE)) {
83
81
  console.error(err)
84
82
  }
85
83
 
@@ -129,10 +127,7 @@ function buildFallbackPayload(
129
127
  'Unknown error'
130
128
  : 'System error'
131
129
  : `Cannot ${req.method} ${req.url}`,
132
- stack:
133
- err instanceof Error && process.env['NODE_ENV'] === 'development'
134
- ? err.stack
135
- : undefined,
130
+ stack: DEV_MODE && err instanceof Error ? err.stack : undefined,
136
131
  }
137
132
  }
138
133
 
@@ -142,16 +137,6 @@ function getErrorStatusCode(err: NonNullable<unknown>): number {
142
137
  return status != null && status >= 400 && status < 600 ? status : 500
143
138
  }
144
139
 
145
- export function once<T extends NextFunction>(next: T): T {
146
- let nextNullable: T | null = next
147
- return function (err) {
148
- if (!nextNullable) throw new Error('next() called multiple times')
149
- const next = nextNullable
150
- nextNullable = null
151
- return next(err)
152
- } as T
153
- }
154
-
155
140
  // eslint-disable-next-line
156
141
  function getProp(obj: unknown, key: string, t: 'function'): Function | undefined
157
142
  function getProp(obj: unknown, key: string, t: 'string'): string | undefined