@atproto/oauth-provider 0.19.6 → 0.19.8

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 (165) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/errors/error-parser.js +5 -5
  3. package/dist/errors/error-parser.js.map +1 -1
  4. package/package.json +22 -18
  5. package/src/access-token/access-token-mode.ts +0 -4
  6. package/src/account/account-manager.ts +0 -591
  7. package/src/account/account-store.ts +0 -305
  8. package/src/account/sign-in-data.ts +0 -15
  9. package/src/account/sign-up-input.ts +0 -20
  10. package/src/client/client-auth.ts +0 -64
  11. package/src/client/client-data.ts +0 -9
  12. package/src/client/client-id.ts +0 -4
  13. package/src/client/client-info.ts +0 -13
  14. package/src/client/client-manager.ts +0 -772
  15. package/src/client/client-store.ts +0 -35
  16. package/src/client/client-utils.ts +0 -37
  17. package/src/client/client.ts +0 -394
  18. package/src/constants.ts +0 -80
  19. package/src/customization/branding.ts +0 -12
  20. package/src/customization/build-customization-css.ts +0 -55
  21. package/src/customization/build-customization-data.ts +0 -24
  22. package/src/customization/colors.ts +0 -48
  23. package/src/customization/customization.ts +0 -29
  24. package/src/customization/links.ts +0 -10
  25. package/src/device/device-data.ts +0 -11
  26. package/src/device/device-id.ts +0 -27
  27. package/src/device/device-manager.ts +0 -292
  28. package/src/device/device-store.ts +0 -31
  29. package/src/device/session-id.ts +0 -21
  30. package/src/dpop/dpop-manager.ts +0 -214
  31. package/src/dpop/dpop-nonce.ts +0 -121
  32. package/src/dpop/dpop-proof.ts +0 -6
  33. package/src/errors/access-denied-error.ts +0 -12
  34. package/src/errors/account-selection-required-error.ts +0 -12
  35. package/src/errors/authorization-error.ts +0 -45
  36. package/src/errors/consent-required-error.ts +0 -12
  37. package/src/errors/error-parser.ts +0 -147
  38. package/src/errors/handle-unavailable-error.ts +0 -23
  39. package/src/errors/invalid-authorization-details-error.ts +0 -27
  40. package/src/errors/invalid-client-error.ts +0 -20
  41. package/src/errors/invalid-client-id-error.ts +0 -31
  42. package/src/errors/invalid-client-metadata-error.ts +0 -68
  43. package/src/errors/invalid-credentials-error.ts +0 -29
  44. package/src/errors/invalid-dpop-key-binding-error.ts +0 -21
  45. package/src/errors/invalid-dpop-proof-error.ts +0 -13
  46. package/src/errors/invalid-grant-error.ts +0 -21
  47. package/src/errors/invalid-invite-code-error.ts +0 -10
  48. package/src/errors/invalid-redirect-uri-error.ts +0 -17
  49. package/src/errors/invalid-request-error.ts +0 -32
  50. package/src/errors/invalid-scope-error.ts +0 -15
  51. package/src/errors/invalid-token-error.ts +0 -60
  52. package/src/errors/login-required-error.ts +0 -12
  53. package/src/errors/oauth-error.ts +0 -28
  54. package/src/errors/second-authentication-factor-required-error.ts +0 -25
  55. package/src/errors/unauthorized-client-error.ts +0 -20
  56. package/src/errors/use-dpop-nonce-error.ts +0 -32
  57. package/src/errors/www-authenticate-error.ts +0 -64
  58. package/src/index.ts +0 -16
  59. package/src/lexicon/lexicon-data.ts +0 -11
  60. package/src/lexicon/lexicon-getter.ts +0 -55
  61. package/src/lexicon/lexicon-manager.ts +0 -116
  62. package/src/lexicon/lexicon-store.ts +0 -35
  63. package/src/lib/csp/index.ts +0 -101
  64. package/src/lib/hcaptcha.ts +0 -228
  65. package/src/lib/html/README.md +0 -9
  66. package/src/lib/html/build-document.ts +0 -151
  67. package/src/lib/html/escapers.ts +0 -66
  68. package/src/lib/html/html.ts +0 -56
  69. package/src/lib/html/hydration-data.ts +0 -19
  70. package/src/lib/html/index.ts +0 -5
  71. package/src/lib/html/tags.ts +0 -67
  72. package/src/lib/html/util.ts +0 -17
  73. package/src/lib/http/README.md +0 -11
  74. package/src/lib/http/accept.ts +0 -90
  75. package/src/lib/http/context.ts +0 -42
  76. package/src/lib/http/headers.ts +0 -15
  77. package/src/lib/http/index.ts +0 -10
  78. package/src/lib/http/method.ts +0 -18
  79. package/src/lib/http/middleware.ts +0 -169
  80. package/src/lib/http/parser.ts +0 -101
  81. package/src/lib/http/path.ts +0 -82
  82. package/src/lib/http/request.ts +0 -256
  83. package/src/lib/http/response.ts +0 -122
  84. package/src/lib/http/route.ts +0 -60
  85. package/src/lib/http/router.ts +0 -117
  86. package/src/lib/http/security-headers.ts +0 -100
  87. package/src/lib/http/stream.ts +0 -57
  88. package/src/lib/http/types.ts +0 -16
  89. package/src/lib/http/url.ts +0 -23
  90. package/src/lib/nsid.ts +0 -10
  91. package/src/lib/redis.ts +0 -25
  92. package/src/lib/util/authorization-header.ts +0 -28
  93. package/src/lib/util/cast.ts +0 -18
  94. package/src/lib/util/color.ts +0 -168
  95. package/src/lib/util/crypto.ts +0 -32
  96. package/src/lib/util/date.ts +0 -7
  97. package/src/lib/util/error.ts +0 -7
  98. package/src/lib/util/function.ts +0 -39
  99. package/src/lib/util/locale.ts +0 -12
  100. package/src/lib/util/object.ts +0 -23
  101. package/src/lib/util/redirect-uri.ts +0 -46
  102. package/src/lib/util/time.ts +0 -49
  103. package/src/lib/util/type.ts +0 -182
  104. package/src/lib/util/ui8.ts +0 -14
  105. package/src/lib/util/well-known.ts +0 -8
  106. package/src/lib/util/zod-error.ts +0 -26
  107. package/src/lib/write-form-redirect.ts +0 -58
  108. package/src/lib/write-html.ts +0 -70
  109. package/src/metadata/build-metadata.ts +0 -141
  110. package/src/oauth-client.ts +0 -3
  111. package/src/oauth-dpop.ts +0 -2
  112. package/src/oauth-errors.ts +0 -26
  113. package/src/oauth-hooks.ts +0 -519
  114. package/src/oauth-middleware.ts +0 -53
  115. package/src/oauth-provider.ts +0 -1110
  116. package/src/oauth-store.ts +0 -12
  117. package/src/oauth-verifier.ts +0 -260
  118. package/src/replay/replay-manager.ts +0 -51
  119. package/src/replay/replay-store-memory.ts +0 -36
  120. package/src/replay/replay-store-redis.ts +0 -30
  121. package/src/replay/replay-store.ts +0 -44
  122. package/src/request/code.ts +0 -23
  123. package/src/request/request-data.ts +0 -36
  124. package/src/request/request-id.ts +0 -22
  125. package/src/request/request-manager.ts +0 -521
  126. package/src/request/request-store.ts +0 -60
  127. package/src/request/request-uri.ts +0 -42
  128. package/src/result/authorization-redirect-parameters.ts +0 -24
  129. package/src/result/authorization-result-authorize-page.ts +0 -16
  130. package/src/result/authorization-result-redirect.ts +0 -8
  131. package/src/router/assets/assets-manifest.ts +0 -117
  132. package/src/router/assets/assets.ts +0 -124
  133. package/src/router/assets/csrf.ts +0 -79
  134. package/src/router/assets/send-account-page.ts +0 -23
  135. package/src/router/assets/send-authorization-page.ts +0 -42
  136. package/src/router/assets/send-cookie-error-page.ts +0 -21
  137. package/src/router/assets/send-error-page.ts +0 -25
  138. package/src/router/assets/send-redirect.ts +0 -140
  139. package/src/router/create-account-page-middleware.ts +0 -88
  140. package/src/router/create-api-middleware.ts +0 -1051
  141. package/src/router/create-authorization-page-middleware.ts +0 -281
  142. package/src/router/create-oauth-middleware.ts +0 -257
  143. package/src/router/error-handler.ts +0 -6
  144. package/src/router/middleware-options.ts +0 -9
  145. package/src/signer/access-token-payload.ts +0 -25
  146. package/src/signer/api-token-payload.ts +0 -18
  147. package/src/signer/signer.ts +0 -121
  148. package/src/token/refresh-token.ts +0 -30
  149. package/src/token/token-claims.ts +0 -22
  150. package/src/token/token-data.ts +0 -40
  151. package/src/token/token-id.ts +0 -25
  152. package/src/token/token-manager.ts +0 -427
  153. package/src/token/token-store.ts +0 -88
  154. package/src/types/authorization-response-error.ts +0 -27
  155. package/src/types/color-hue.ts +0 -3
  156. package/src/types/email-otp.ts +0 -3
  157. package/src/types/email.ts +0 -26
  158. package/src/types/handle.ts +0 -23
  159. package/src/types/invite-code.ts +0 -4
  160. package/src/types/par-response-error.ts +0 -25
  161. package/src/types/password.ts +0 -4
  162. package/src/types/rgb-color.ts +0 -21
  163. package/tsconfig.build.json +0 -8
  164. package/tsconfig.build.tsbuildinfo +0 -1
  165. package/tsconfig.json +0 -4
@@ -1,90 +0,0 @@
1
- import type { IncomingMessage, ServerResponse } from 'node:http'
2
- // eslint-disable-next-line import/default, import/no-named-as-default-member
3
- import accept from '@hapi/accept'
4
- // eslint-disable-next-line import/no-named-as-default-member
5
- const { mediaType } = accept
6
- import { SubCtx, subCtx } from './context.js'
7
- import { Middleware, NextFunction } from './types.js'
8
-
9
- type View<
10
- T extends object | void,
11
- D,
12
- Req extends IncomingMessage = IncomingMessage,
13
- Res extends ServerResponse = ServerResponse,
14
- > = (
15
- this: SubCtx<T, { data: D }>,
16
- req: Req,
17
- res: Res,
18
- next: NextFunction,
19
- ) => void | PromiseLike<void>
20
-
21
- /**
22
- * @example
23
- * ```ts
24
- * app.use(
25
- * acceptMiddleware(
26
- * async function (req, res) {
27
- * return { hello: 'world' }
28
- * },
29
- * {
30
- * '': 'application/json', // Fallback to JSON
31
- * 'text/plain': function (req, res) {
32
- * res.writeHead(200).end(this.data.hello)
33
- * },
34
- * 'application/json': function (req, res) {
35
- * res.writeHead(200).end(JSON.stringify(this.data))
36
- * }
37
- * }
38
- * )
39
- * )
40
- * ```
41
- */
42
- export function acceptMiddleware<
43
- D,
44
- T extends object | void = void,
45
- Req extends IncomingMessage = IncomingMessage,
46
- Res extends ServerResponse = ServerResponse,
47
- >(
48
- controller: (this: T, req: Req, res: Res) => D | PromiseLike<D>,
49
- views: Record<string, string | View<T, D, Req, Res>>,
50
- fallback: Middleware<T> = (req, res, _next) => void res.writeHead(406).end(),
51
- ): (this: T, req: Req, res: Res, next: NextFunction) => Promise<void> {
52
- const viewsMap = new Map(Object.entries(views))
53
- const preferences = Array.from(viewsMap.keys()).filter(Boolean)
54
-
55
- // Make sure that every view is either a function or a string that points to a
56
- // function.
57
- for (const type of viewsMap.keys()) {
58
- const view = viewsMap.get(type)
59
- if (typeof view === 'string' && typeof viewsMap.get(view) !== 'function') {
60
- throw new Error(`Invalid view "${view}" for media type "${type}"`)
61
- }
62
- }
63
-
64
- return async function (req, res, next) {
65
- try {
66
- const type = req.headers['accept']
67
- ? mediaType(req.headers['accept'], preferences) || undefined
68
- : '' // indicate that the client accepts anything
69
-
70
- let view = type != null ? viewsMap.get(type) : undefined
71
-
72
- if (typeof view === 'string') view = viewsMap.get(view)
73
- if (typeof view === 'string') throw new Error('Invalid view') // should not happen
74
-
75
- if (view) {
76
- const data = await controller.call(this, req, res)
77
- const ctx = subCtx(this, { data })
78
- if (type) res.setHeader('Content-Type', type)
79
-
80
- await view.call(ctx, req, res, next)
81
- } else {
82
- // media negotiation failed
83
- await fallback.call(this, req, res, next)
84
- }
85
- } catch (err) {
86
- if (!res.headersSent) res.removeHeader('Content-Type')
87
- next(err)
88
- }
89
- }
90
- }
@@ -1,42 +0,0 @@
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 }
42
- }
@@ -1,15 +0,0 @@
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,10 +0,0 @@
1
- export * from './accept.js'
2
- export * from './context.js'
3
- export * from './headers.js'
4
- export * from './middleware.js'
5
- export * from './parser.js'
6
- export * from './request.js'
7
- export * from './response.js'
8
- export * from './router.js'
9
- export * from './stream.js'
10
- export * from './types.js'
@@ -1,18 +0,0 @@
1
- import type { IncomingMessage } from 'node:http'
2
-
3
- export type MethodMatcherInput = string | Iterable<string> | MethodMatcher
4
- export type MethodMatcher = (req: IncomingMessage) => boolean
5
-
6
- export function createMethodMatcher(method: MethodMatcherInput): MethodMatcher {
7
- if (method === '*') return () => true
8
- if (typeof method === 'function') return method
9
-
10
- if (typeof method === 'string') {
11
- method = method.toUpperCase()
12
- return (req) => req.method === method
13
- }
14
-
15
- const set = new Set(Array.from(method, (m) => m.toUpperCase()))
16
- if (set.size === 0) return () => false
17
- return (req) => req.method != null && set.has(req.method)
18
- }
@@ -1,169 +0,0 @@
1
- import type { IncomingMessage, ServerResponse } from 'node:http'
2
- import { invokeOnce } from '../util/function.js'
3
- import { writeJson } from './response.js'
4
- import { Handler, Middleware, NextFunction } from './types.js'
5
-
6
- const isNonNullable = <X>(x: X): x is NonNullable<X> => x != null
7
-
8
- export function combineMiddlewares<M extends Middleware<any, any, any>>(
9
- middlewares: Iterable<null | undefined | M>,
10
- options?: { skipKeyword?: string },
11
- ): M
12
-
13
- /**
14
- * Combine express/connect like middlewares (that can be async) into a single
15
- * middleware.
16
- */
17
- export function combineMiddlewares(
18
- middlewares: Iterable<null | undefined | Middleware<unknown>>,
19
- { skipKeyword }: { skipKeyword?: string } = {},
20
- ): Middleware<unknown> {
21
- const middlewaresArray = Array.from(middlewares).filter(isNonNullable)
22
-
23
- // Optimization: if there are no middlewares, return a noop middleware.
24
- if (middlewaresArray.length === 0) return (req, res, next) => void next()
25
- if (middlewaresArray.length === 1) return middlewaresArray[0]
26
-
27
- return function (req, res, next) {
28
- let i = 0
29
- const nextMiddleware = (err?: unknown) => {
30
- if (err) {
31
- if (skipKeyword && err === skipKeyword) next()
32
- else next(err)
33
- } else if (i >= middlewaresArray.length) {
34
- next()
35
- } else {
36
- const currentMiddleware = middlewaresArray[i++]!
37
- const currentNext = invokeOnce(nextMiddleware)
38
- currentMiddleware.call(this, req, res, currentNext)
39
- }
40
- }
41
- nextMiddleware()
42
- }
43
- }
44
-
45
- export type AsHandler<M extends Middleware<any, any, any>> =
46
- M extends Middleware<infer T, infer Req, infer Res>
47
- ? Handler<T, Req, Res>
48
- : never
49
-
50
- /**
51
- * Convert a middleware in a function that can be used as both a middleware and
52
- * and handler.
53
- */
54
- export function asHandler<M extends Middleware<any, any, any>>(
55
- middleware: M,
56
- options?: FinalHandlerOptions,
57
- ) {
58
- return function (
59
- this,
60
- req,
61
- res,
62
- next = invokeOnce(createFinalHandler(req, res, options)),
63
- ) {
64
- return middleware.call(this, req, res, next)
65
- } as AsHandler<M>
66
- }
67
-
68
- export const DEV_MODE = process.env['NODE_ENV'] === 'development'
69
-
70
- export type FinalHandlerOptions = {
71
- debug?: boolean
72
- }
73
-
74
- export function createFinalHandler(
75
- req: IncomingMessage,
76
- res: ServerResponse,
77
- options?: FinalHandlerOptions,
78
- ): NextFunction {
79
- return (err) => {
80
- if (err != null && (options?.debug ?? DEV_MODE)) {
81
- console.error(err)
82
- }
83
-
84
- if (res.headersSent) {
85
- // If an error occurred, and headers were sent, we can't know that the
86
- // whole response body was sent. So we can't safely reuse the socket.
87
- if (err) req.socket.destroy()
88
-
89
- return
90
- }
91
-
92
- const { status, ...payload } = buildFallbackPayload(req, err)
93
-
94
- res.setHeader('Content-Security-Policy', "default-src 'none'")
95
- res.setHeader('X-Content-Type-Options', 'nosniff')
96
-
97
- writeJson(res, payload, { status })
98
- }
99
- }
100
-
101
- function buildFallbackPayload(
102
- req: IncomingMessage,
103
- err: unknown,
104
- ): {
105
- status: number
106
- error: string
107
- error_description: string
108
- stack?: undefined | string
109
- } {
110
- const status = err ? getErrorStatusCode(err) : 404
111
- const expose = getProp(err, 'expose', 'boolean') ?? status < 500
112
-
113
- return {
114
- status,
115
- error: err
116
- ? expose
117
- ? getProp(err, 'code', 'string') ??
118
- getProp(err, 'error', 'string') ??
119
- 'unknown_error'
120
- : 'system_error'
121
- : 'not_found',
122
- error_description:
123
- err instanceof Error
124
- ? expose
125
- ? getProp(err, 'error_description', 'string') ||
126
- String(err.message) ||
127
- 'Unknown error'
128
- : 'System error'
129
- : `Cannot ${req.method} ${req.url}`,
130
- stack: DEV_MODE && err instanceof Error ? err.stack : undefined,
131
- }
132
- }
133
-
134
- function getErrorStatusCode(err: NonNullable<unknown>): number {
135
- const status =
136
- getProp(err, 'status', 'number') ?? getProp(err, 'statusCode', 'number')
137
- return status != null && status >= 400 && status < 600 ? status : 500
138
- }
139
-
140
- // eslint-disable-next-line
141
- function getProp(obj: unknown, key: string, t: 'function'): Function | undefined
142
- function getProp(obj: unknown, key: string, t: 'string'): string | undefined
143
- function getProp(obj: unknown, key: string, t: 'number'): number | undefined
144
- function getProp(obj: unknown, key: string, t: 'boolean'): boolean | undefined
145
- function getProp(obj: unknown, key: string, t: 'object'): object | undefined
146
- function getProp(obj: unknown, key: string, t: 'symbol'): symbol | undefined
147
- function getProp(obj: unknown, key: string, t: 'bigint'): bigint | undefined
148
- function getProp(obj: unknown, key: string, t: 'undefined'): undefined
149
- function getProp(
150
- obj: unknown,
151
- key: string,
152
- type:
153
- | 'string'
154
- | 'number'
155
- | 'boolean'
156
- | 'object'
157
- | 'function'
158
- | 'symbol'
159
- | 'bigint'
160
- | 'undefined',
161
- ): unknown
162
-
163
- function getProp(obj: unknown, key: string, type: string): unknown {
164
- if (obj != null && typeof obj === 'object' && key in obj) {
165
- const value = (obj as Record<string, unknown>)[key]
166
- if (typeof value === type) return value
167
- }
168
- return undefined
169
- }
@@ -1,101 +0,0 @@
1
- import { parse as parseJson } from '@hapi/bourne'
2
- import { type as hapiContentType } from '@hapi/content'
3
- import createHttpError from 'http-errors'
4
-
5
- export type JsonScalar = string | number | boolean | null
6
- export type Json = JsonScalar | Json[] | { [_ in string]?: Json }
7
-
8
- /**
9
- * Parse a content-type string into its components.
10
- *
11
- * @throws {TypeError} If the content-type is invalid.
12
- */
13
- export function parseContentType(type: unknown): ContentType {
14
- if (typeof type !== 'string') {
15
- throw createHttpError(
16
- 415,
17
- `Invalid content-type: ${type == null ? String(type) : typeof type}`,
18
- )
19
- }
20
-
21
- try {
22
- return hapiContentType(type)
23
- } catch (err) {
24
- // De-boomify the error
25
- throw createHttpError(
26
- 415,
27
- err instanceof Error ? err.message : 'Invalid content-type',
28
- )
29
- }
30
- }
31
-
32
- export type ContentType = {
33
- mime: string
34
- charset?: string
35
- boundary?: string
36
- }
37
-
38
- export type Parser<T extends string = string, R = unknown> = {
39
- readonly name: string
40
- readonly test: (mime: string) => mime is T
41
- readonly parse: (buffer: Buffer, type: ContentType) => R
42
- }
43
-
44
- export type ParserName<P extends Parser> = P extends { readonly name: infer N }
45
- ? N
46
- : never
47
- export type ParserType<P extends Parser> = P extends Parser<infer T> ? T : never
48
- export type ParserResult<P extends Parser> = ReturnType<P['parse']>
49
-
50
- export type ParserForType<P extends Parser, T> =
51
- P extends Parser<infer U> ? (U extends T ? P : never) : never
52
-
53
- export const parsers = [
54
- {
55
- name: 'json',
56
- test: (mime): mime is `application/json` | `application/${string}+json` => {
57
- return /^application\/(?:.+\+)?json$/.test(mime)
58
- },
59
- parse: (buffer, { charset }): Json => {
60
- if (charset != null && !/^utf-?8$/i.test(charset)) {
61
- throw createHttpError(415, 'Unsupported charset')
62
- }
63
- try {
64
- return parseJson(buffer.toString())
65
- } catch (err) {
66
- throw createHttpError(400, 'Invalid JSON', { cause: err })
67
- }
68
- },
69
- },
70
- {
71
- name: 'urlencoded',
72
- test: (mime): mime is 'application/x-www-form-urlencoded' => {
73
- return mime === 'application/x-www-form-urlencoded'
74
- },
75
- parse: (buffer, { charset }): { [_ in string]?: string } => {
76
- if (charset != null && !/^utf-?8$/i.test(charset)) {
77
- throw createHttpError(415, 'Unsupported charset')
78
- }
79
- try {
80
- if (!buffer.length) return {}
81
- const params = new URLSearchParams(buffer.toString())
82
- if (params.has('__proto__')) throw new TypeError('Invalid key')
83
- return Object.fromEntries(params)
84
- } catch (err) {
85
- throw createHttpError(400, 'Invalid URL-encoded data', { cause: err })
86
- }
87
- },
88
- },
89
- {
90
- name: 'bytes',
91
- test: (mime): mime is 'application/octet-stream' => {
92
- return mime === 'application/octet-stream'
93
- },
94
- parse: (buffer): Buffer => buffer,
95
- },
96
- ] as const satisfies Parser[]
97
-
98
- export type KnownParser = (typeof parsers)[number]
99
-
100
- export type KnownNames = KnownParser['name']
101
- export type KnownTypes = ParserType<KnownParser>
@@ -1,82 +0,0 @@
1
- export type PathMatcher<P extends Params> = (pathname: string) => P | undefined
2
-
3
- type StringPath<P extends Params> = string extends keyof P
4
- ? `/${string}`
5
- : keyof P extends never
6
- ? `/${string}` | ``
7
- : {
8
- [K in keyof P]: K extends string
9
- ?
10
- | `${`/:${K}` | `/${string}/:${K}`}${StringPath<Omit<P, K>>}`
11
- | `${StringPath<Omit<P, K>>}${`/:${K}` | `/:${K}/${string}`}`
12
- : never
13
- }[keyof P]
14
-
15
- export type Path<P extends Params> =
16
- | string
17
- | StringPath<P>
18
- | RegExp
19
- | PathMatcher<P>
20
- export type Params = Record<string, undefined | string>
21
-
22
- export function createPathMatcher<P extends Params = Params>(
23
- refPath: Path<P>,
24
- ): PathMatcher<P> {
25
- if (typeof refPath === 'string') {
26
- // Create a path matcher for a path with parameters (like /foo/:fooId/bar/:barId).
27
- if (refPath.includes('/:')) {
28
- const refParts = refPath
29
- .slice(1)
30
- .split('/')
31
- .map((part, i) => [part, i] as const)
32
- const refPartsLength = refParts.length
33
-
34
- const staticParts = refParts.filter(([p]) => !p.startsWith(':'))
35
- const paramParts = refParts
36
- // Extract parameters, ignoring those with no name (like /foo/:/bar).
37
- .filter(([p]) => p.startsWith(':') && p.length > 1)
38
- .map(([p, i]) => [p.slice(1), i] as const)
39
-
40
- return (reqPath: string) => {
41
- const reqParts = reqPath.slice(1).split('/')
42
-
43
- if (reqParts.length !== refPartsLength) return undefined
44
-
45
- // Make sure all static parts match.
46
- for (let i = 0; i < staticParts.length; i++) {
47
- const value = staticParts[i]![0]
48
- const idx = staticParts[i]![1]
49
-
50
- if (value !== reqParts[idx]) return undefined
51
- }
52
-
53
- // Then extract the parameters.
54
- const params: Record<string, string> = {}
55
- for (let i = 0; i < paramParts.length; i++) {
56
- const name = paramParts[i]![0]
57
- const idx = paramParts[i]![1]
58
-
59
- const value = reqParts[idx]
60
-
61
- // Empty parameter values are not allowed.
62
- if (!value) return undefined
63
-
64
- params[name] = value
65
- }
66
-
67
- return params as P
68
- }
69
- }
70
-
71
- return (reqPath: string) => (reqPath === refPath ? ({} as P) : undefined)
72
- }
73
-
74
- if (refPath instanceof RegExp) {
75
- return (reqPath: string) => {
76
- const match = reqPath.match(refPath)
77
- return match ? ((match.groups || {}) as P) : undefined
78
- }
79
- }
80
-
81
- return refPath
82
- }