better-call 1.0.13 → 1.0.14

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.
package/dist/node.cjs CHANGED
@@ -104,11 +104,26 @@ function getRequest({
104
104
  base,
105
105
  bodySizeLimit
106
106
  }) {
107
- return new Request(base + request.url, {
107
+ const baseUrl = request?.baseUrl;
108
+ const fullPath = baseUrl ? baseUrl + request.url : request.url;
109
+ const maybeConsumedReq = request;
110
+ let body = void 0;
111
+ if (maybeConsumedReq.body !== void 0) {
112
+ const bodyContent = typeof maybeConsumedReq.body === "string" ? maybeConsumedReq.body : JSON.stringify(maybeConsumedReq.body);
113
+ body = new ReadableStream({
114
+ start(controller) {
115
+ controller.enqueue(new TextEncoder().encode(bodyContent));
116
+ controller.close();
117
+ }
118
+ });
119
+ } else {
120
+ body = get_raw_body(request, bodySizeLimit);
121
+ }
122
+ return new Request(base + fullPath, {
108
123
  // @ts-expect-error
109
124
  duplex: "half",
110
125
  method: request.method,
111
- body: get_raw_body(request, bodySizeLimit),
126
+ body,
112
127
  headers: request.headers
113
128
  });
114
129
  }
package/dist/node.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapters/node/index.ts","../src/adapters/node/request.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\n\nimport { getRequest, setResponse } from \"./request\";\nimport type { Router } from \"../../router.js\";\n\nexport function toNodeHandler(handler: Router[\"handler\"]) {\n\treturn async (req: IncomingMessage, res: ServerResponse) => {\n\t\tconst protocol =\n\t\t\treq.headers[\"x-forwarded-proto\"] || ((req.socket as any).encrypted ? \"https\" : \"http\");\n\t\tconst base = `${protocol}://${req.headers[\":authority\"] || req.headers.host}`;\n\t\tconst response = await handler(getRequest({ base, request: req }));\n\t\treturn setResponse(res, response);\n\t};\n}\n\nexport { getRequest, setResponse };\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport * as set_cookie_parser from \"set-cookie-parser\";\n\nfunction get_raw_body(req: IncomingMessage, body_size_limit?: number) {\n\tconst h = req.headers;\n\n\tif (!h[\"content-type\"]) return null;\n\n\tconst content_length = Number(h[\"content-length\"]);\n\n\t// check if no request body\n\tif (\n\t\t(req.httpVersionMajor === 1 && isNaN(content_length) && h[\"transfer-encoding\"] == null) ||\n\t\tcontent_length === 0\n\t) {\n\t\treturn null;\n\t}\n\n\tlet length = content_length;\n\n\tif (body_size_limit) {\n\t\tif (!length) {\n\t\t\tlength = body_size_limit;\n\t\t} else if (length > body_size_limit) {\n\t\t\tthrow Error(\n\t\t\t\t`Received content-length of ${length}, but only accept up to ${body_size_limit} bytes.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (req.destroyed) {\n\t\tconst readable = new ReadableStream();\n\t\treadable.cancel();\n\t\treturn readable;\n\t}\n\n\tlet size = 0;\n\tlet cancelled = false;\n\n\treturn new ReadableStream({\n\t\tstart(controller) {\n\t\t\treq.on(\"error\", (error) => {\n\t\t\t\tcancelled = true;\n\t\t\t\tcontroller.error(error);\n\t\t\t});\n\n\t\t\treq.on(\"end\", () => {\n\t\t\t\tif (cancelled) return;\n\t\t\t\tcontroller.close();\n\t\t\t});\n\n\t\t\treq.on(\"data\", (chunk) => {\n\t\t\t\tif (cancelled) return;\n\n\t\t\t\tsize += chunk.length;\n\n\t\t\t\tif (size > length) {\n\t\t\t\t\tcancelled = true;\n\n\t\t\t\t\tcontroller.error(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`request body size exceeded ${\n\t\t\t\t\t\t\t\tcontent_length ? \"'content-length'\" : \"BODY_SIZE_LIMIT\"\n\t\t\t\t\t\t\t} of ${length}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcontroller.enqueue(chunk);\n\n\t\t\t\tif (controller.desiredSize === null || controller.desiredSize <= 0) {\n\t\t\t\t\treq.pause();\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tpull() {\n\t\t\treq.resume();\n\t\t},\n\n\t\tcancel(reason) {\n\t\t\tcancelled = true;\n\t\t\treq.destroy(reason);\n\t\t},\n\t});\n}\n\nexport function getRequest({\n\trequest,\n\tbase,\n\tbodySizeLimit,\n}: {\n\tbase: string;\n\tbodySizeLimit?: number;\n\trequest: IncomingMessage;\n}) {\n\treturn new Request(base + request.url, {\n\t\t// @ts-expect-error\n\t\tduplex: \"half\",\n\t\tmethod: request.method,\n\t\tbody: get_raw_body(request, bodySizeLimit),\n\t\theaders: request.headers as Record<string, string>,\n\t});\n}\n\nexport async function setResponse(res: ServerResponse, response: Response) {\n\tfor (const [key, value] of response.headers as any) {\n\t\ttry {\n\t\t\tres.setHeader(\n\t\t\t\tkey,\n\t\t\t\tkey === \"set-cookie\"\n\t\t\t\t\t? set_cookie_parser.splitCookiesString(response.headers.get(key) as string)\n\t\t\t\t\t: value,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tres.getHeaderNames().forEach((name) => res.removeHeader(name));\n\t\t\tres.writeHead(500).end(String(error));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tres.writeHead(response.status);\n\n\tif (!response.body) {\n\t\tres.end();\n\t\treturn;\n\t}\n\n\tif (response.body.locked) {\n\t\tres.end(\n\t\t\t\"Fatal error: Response body is locked. \" +\n\t\t\t\t\"This can happen when the response was already read (for example through 'response.json()' or 'response.text()').\",\n\t\t);\n\t\treturn;\n\t}\n\n\tconst reader = response.body.getReader();\n\n\tif (res.destroyed) {\n\t\treader.cancel();\n\t\treturn;\n\t}\n\n\tconst cancel = (error?: Error) => {\n\t\tres.off(\"close\", cancel);\n\t\tres.off(\"error\", cancel);\n\n\t\t// If the reader has already been interrupted with an error earlier,\n\t\t// then it will appear here, it is useless, but it needs to be catch.\n\t\treader.cancel(error).catch(() => {});\n\t\tif (error) res.destroy(error);\n\t};\n\n\tres.on(\"close\", cancel);\n\tres.on(\"error\", cancel);\n\n\tnext();\n\tasync function next() {\n\t\ttry {\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read();\n\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (!res.write(value)) {\n\t\t\t\t\tres.once(\"drain\", next);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tres.end();\n\t\t} catch (error) {\n\t\t\tcancel(error instanceof Error ? error : new Error(String(error)));\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/adapters/node/index.ts","../src/adapters/node/request.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\n\nimport { getRequest, setResponse } from \"./request\";\nimport type { Router } from \"../../router.js\";\n\nexport function toNodeHandler(handler: Router[\"handler\"]) {\n\treturn async (req: IncomingMessage, res: ServerResponse) => {\n\t\tconst protocol =\n\t\t\treq.headers[\"x-forwarded-proto\"] || ((req.socket as any).encrypted ? \"https\" : \"http\");\n\t\tconst base = `${protocol}://${req.headers[\":authority\"] || req.headers.host}`;\n\t\tconst response = await handler(getRequest({ base, request: req }));\n\t\treturn setResponse(res, response);\n\t};\n}\n\nexport { getRequest, setResponse };\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport * as set_cookie_parser from \"set-cookie-parser\";\n\nfunction get_raw_body(req: IncomingMessage, body_size_limit?: number) {\n\tconst h = req.headers;\n\n\tif (!h[\"content-type\"]) return null;\n\n\tconst content_length = Number(h[\"content-length\"]);\n\n\t// check if no request body\n\tif (\n\t\t(req.httpVersionMajor === 1 && isNaN(content_length) && h[\"transfer-encoding\"] == null) ||\n\t\tcontent_length === 0\n\t) {\n\t\treturn null;\n\t}\n\n\tlet length = content_length;\n\n\tif (body_size_limit) {\n\t\tif (!length) {\n\t\t\tlength = body_size_limit;\n\t\t} else if (length > body_size_limit) {\n\t\t\tthrow Error(\n\t\t\t\t`Received content-length of ${length}, but only accept up to ${body_size_limit} bytes.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (req.destroyed) {\n\t\tconst readable = new ReadableStream();\n\t\treadable.cancel();\n\t\treturn readable;\n\t}\n\n\tlet size = 0;\n\tlet cancelled = false;\n\n\treturn new ReadableStream({\n\t\tstart(controller) {\n\t\t\treq.on(\"error\", (error) => {\n\t\t\t\tcancelled = true;\n\t\t\t\tcontroller.error(error);\n\t\t\t});\n\n\t\t\treq.on(\"end\", () => {\n\t\t\t\tif (cancelled) return;\n\t\t\t\tcontroller.close();\n\t\t\t});\n\n\t\t\treq.on(\"data\", (chunk) => {\n\t\t\t\tif (cancelled) return;\n\n\t\t\t\tsize += chunk.length;\n\n\t\t\t\tif (size > length) {\n\t\t\t\t\tcancelled = true;\n\n\t\t\t\t\tcontroller.error(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`request body size exceeded ${\n\t\t\t\t\t\t\t\tcontent_length ? \"'content-length'\" : \"BODY_SIZE_LIMIT\"\n\t\t\t\t\t\t\t} of ${length}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcontroller.enqueue(chunk);\n\n\t\t\t\tif (controller.desiredSize === null || controller.desiredSize <= 0) {\n\t\t\t\t\treq.pause();\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tpull() {\n\t\t\treq.resume();\n\t\t},\n\n\t\tcancel(reason) {\n\t\t\tcancelled = true;\n\t\t\treq.destroy(reason);\n\t\t},\n\t});\n}\n\nexport function getRequest({\n\trequest,\n\tbase,\n\tbodySizeLimit,\n}: {\n\tbase: string;\n\tbodySizeLimit?: number;\n\trequest: IncomingMessage;\n}) {\n\t// In Express subrouters, `request.url` is relative to the mount path (e.g., '/auth/xxx'),\n\t// and `request.baseUrl` holds the mount path (e.g., '/api').\n\t// Build the full path as baseUrl + url when available to preserve the full route.\n\tconst baseUrl = (request as any)?.baseUrl as string | undefined;\n\tconst fullPath = baseUrl ? baseUrl + request.url : request.url;\n\n\t// Check if body has already been parsed by Express middleware\n\tconst maybeConsumedReq = request as any;\n\tlet body = undefined;\n\n\t// If body was already parsed by Express body-parser middleware\n\tif (maybeConsumedReq.body !== undefined) {\n\t\t// Convert parsed body back to a ReadableStream\n\t\tconst bodyContent =\n\t\t\ttypeof maybeConsumedReq.body === \"string\"\n\t\t\t\t? maybeConsumedReq.body\n\t\t\t\t: JSON.stringify(maybeConsumedReq.body);\n\n\t\tbody = new ReadableStream({\n\t\t\tstart(controller) {\n\t\t\t\tcontroller.enqueue(new TextEncoder().encode(bodyContent));\n\t\t\t\tcontroller.close();\n\t\t\t},\n\t\t});\n\t} else {\n\t\t// Otherwise, get the raw body stream\n\t\tbody = get_raw_body(request, bodySizeLimit);\n\t}\n\n\treturn new Request(base + fullPath, {\n\t\t// @ts-expect-error\n\t\tduplex: \"half\",\n\t\tmethod: request.method,\n\t\tbody,\n\t\theaders: request.headers as Record<string, string>,\n\t});\n}\n\nexport async function setResponse(res: ServerResponse, response: Response) {\n\tfor (const [key, value] of response.headers as any) {\n\t\ttry {\n\t\t\tres.setHeader(\n\t\t\t\tkey,\n\t\t\t\tkey === \"set-cookie\"\n\t\t\t\t\t? set_cookie_parser.splitCookiesString(response.headers.get(key) as string)\n\t\t\t\t\t: value,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tres.getHeaderNames().forEach((name) => res.removeHeader(name));\n\t\t\tres.writeHead(500).end(String(error));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tres.writeHead(response.status);\n\n\tif (!response.body) {\n\t\tres.end();\n\t\treturn;\n\t}\n\n\tif (response.body.locked) {\n\t\tres.end(\n\t\t\t\"Fatal error: Response body is locked. \" +\n\t\t\t\t\"This can happen when the response was already read (for example through 'response.json()' or 'response.text()').\",\n\t\t);\n\t\treturn;\n\t}\n\n\tconst reader = response.body.getReader();\n\n\tif (res.destroyed) {\n\t\treader.cancel();\n\t\treturn;\n\t}\n\n\tconst cancel = (error?: Error) => {\n\t\tres.off(\"close\", cancel);\n\t\tres.off(\"error\", cancel);\n\n\t\t// If the reader has already been interrupted with an error earlier,\n\t\t// then it will appear here, it is useless, but it needs to be catch.\n\t\treader.cancel(error).catch(() => {});\n\t\tif (error) res.destroy(error);\n\t};\n\n\tres.on(\"close\", cancel);\n\tres.on(\"error\", cancel);\n\n\tnext();\n\tasync function next() {\n\t\ttry {\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read();\n\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (!res.write(value)) {\n\t\t\t\t\tres.once(\"drain\", next);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tres.end();\n\t\t} catch (error) {\n\t\t\tcancel(error instanceof Error ? error : new Error(String(error)));\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
package/dist/node.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { IncomingMessage, ServerResponse } from 'node:http';
2
- import { j as Router } from './router-BEp4ze3Q.cjs';
2
+ import { j as Router } from './router-BcwNrqXF.cjs';
3
3
 
4
4
  declare function getRequest({ request, base, bodySizeLimit, }: {
5
5
  base: string;
package/dist/node.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { IncomingMessage, ServerResponse } from 'node:http';
2
- import { j as Router } from './router-BEp4ze3Q.js';
2
+ import { j as Router } from './router-BcwNrqXF.js';
3
3
 
4
4
  declare function getRequest({ request, base, bodySizeLimit, }: {
5
5
  base: string;
package/dist/node.js CHANGED
@@ -66,11 +66,26 @@ function getRequest({
66
66
  base,
67
67
  bodySizeLimit
68
68
  }) {
69
- return new Request(base + request.url, {
69
+ const baseUrl = request?.baseUrl;
70
+ const fullPath = baseUrl ? baseUrl + request.url : request.url;
71
+ const maybeConsumedReq = request;
72
+ let body = void 0;
73
+ if (maybeConsumedReq.body !== void 0) {
74
+ const bodyContent = typeof maybeConsumedReq.body === "string" ? maybeConsumedReq.body : JSON.stringify(maybeConsumedReq.body);
75
+ body = new ReadableStream({
76
+ start(controller) {
77
+ controller.enqueue(new TextEncoder().encode(bodyContent));
78
+ controller.close();
79
+ }
80
+ });
81
+ } else {
82
+ body = get_raw_body(request, bodySizeLimit);
83
+ }
84
+ return new Request(base + fullPath, {
70
85
  // @ts-expect-error
71
86
  duplex: "half",
72
87
  method: request.method,
73
- body: get_raw_body(request, bodySizeLimit),
88
+ body,
74
89
  headers: request.headers
75
90
  });
76
91
  }
package/dist/node.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapters/node/request.ts","../src/adapters/node/index.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport * as set_cookie_parser from \"set-cookie-parser\";\n\nfunction get_raw_body(req: IncomingMessage, body_size_limit?: number) {\n\tconst h = req.headers;\n\n\tif (!h[\"content-type\"]) return null;\n\n\tconst content_length = Number(h[\"content-length\"]);\n\n\t// check if no request body\n\tif (\n\t\t(req.httpVersionMajor === 1 && isNaN(content_length) && h[\"transfer-encoding\"] == null) ||\n\t\tcontent_length === 0\n\t) {\n\t\treturn null;\n\t}\n\n\tlet length = content_length;\n\n\tif (body_size_limit) {\n\t\tif (!length) {\n\t\t\tlength = body_size_limit;\n\t\t} else if (length > body_size_limit) {\n\t\t\tthrow Error(\n\t\t\t\t`Received content-length of ${length}, but only accept up to ${body_size_limit} bytes.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (req.destroyed) {\n\t\tconst readable = new ReadableStream();\n\t\treadable.cancel();\n\t\treturn readable;\n\t}\n\n\tlet size = 0;\n\tlet cancelled = false;\n\n\treturn new ReadableStream({\n\t\tstart(controller) {\n\t\t\treq.on(\"error\", (error) => {\n\t\t\t\tcancelled = true;\n\t\t\t\tcontroller.error(error);\n\t\t\t});\n\n\t\t\treq.on(\"end\", () => {\n\t\t\t\tif (cancelled) return;\n\t\t\t\tcontroller.close();\n\t\t\t});\n\n\t\t\treq.on(\"data\", (chunk) => {\n\t\t\t\tif (cancelled) return;\n\n\t\t\t\tsize += chunk.length;\n\n\t\t\t\tif (size > length) {\n\t\t\t\t\tcancelled = true;\n\n\t\t\t\t\tcontroller.error(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`request body size exceeded ${\n\t\t\t\t\t\t\t\tcontent_length ? \"'content-length'\" : \"BODY_SIZE_LIMIT\"\n\t\t\t\t\t\t\t} of ${length}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcontroller.enqueue(chunk);\n\n\t\t\t\tif (controller.desiredSize === null || controller.desiredSize <= 0) {\n\t\t\t\t\treq.pause();\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tpull() {\n\t\t\treq.resume();\n\t\t},\n\n\t\tcancel(reason) {\n\t\t\tcancelled = true;\n\t\t\treq.destroy(reason);\n\t\t},\n\t});\n}\n\nexport function getRequest({\n\trequest,\n\tbase,\n\tbodySizeLimit,\n}: {\n\tbase: string;\n\tbodySizeLimit?: number;\n\trequest: IncomingMessage;\n}) {\n\treturn new Request(base + request.url, {\n\t\t// @ts-expect-error\n\t\tduplex: \"half\",\n\t\tmethod: request.method,\n\t\tbody: get_raw_body(request, bodySizeLimit),\n\t\theaders: request.headers as Record<string, string>,\n\t});\n}\n\nexport async function setResponse(res: ServerResponse, response: Response) {\n\tfor (const [key, value] of response.headers as any) {\n\t\ttry {\n\t\t\tres.setHeader(\n\t\t\t\tkey,\n\t\t\t\tkey === \"set-cookie\"\n\t\t\t\t\t? set_cookie_parser.splitCookiesString(response.headers.get(key) as string)\n\t\t\t\t\t: value,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tres.getHeaderNames().forEach((name) => res.removeHeader(name));\n\t\t\tres.writeHead(500).end(String(error));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tres.writeHead(response.status);\n\n\tif (!response.body) {\n\t\tres.end();\n\t\treturn;\n\t}\n\n\tif (response.body.locked) {\n\t\tres.end(\n\t\t\t\"Fatal error: Response body is locked. \" +\n\t\t\t\t\"This can happen when the response was already read (for example through 'response.json()' or 'response.text()').\",\n\t\t);\n\t\treturn;\n\t}\n\n\tconst reader = response.body.getReader();\n\n\tif (res.destroyed) {\n\t\treader.cancel();\n\t\treturn;\n\t}\n\n\tconst cancel = (error?: Error) => {\n\t\tres.off(\"close\", cancel);\n\t\tres.off(\"error\", cancel);\n\n\t\t// If the reader has already been interrupted with an error earlier,\n\t\t// then it will appear here, it is useless, but it needs to be catch.\n\t\treader.cancel(error).catch(() => {});\n\t\tif (error) res.destroy(error);\n\t};\n\n\tres.on(\"close\", cancel);\n\tres.on(\"error\", cancel);\n\n\tnext();\n\tasync function next() {\n\t\ttry {\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read();\n\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (!res.write(value)) {\n\t\t\t\t\tres.once(\"drain\", next);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tres.end();\n\t\t} catch (error) {\n\t\t\tcancel(error instanceof Error ? error : new Error(String(error)));\n\t\t}\n\t}\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\n\nimport { getRequest, setResponse } from \"./request\";\nimport type { Router } from \"../../router.js\";\n\nexport function toNodeHandler(handler: Router[\"handler\"]) {\n\treturn async (req: IncomingMessage, res: ServerResponse) => {\n\t\tconst protocol =\n\t\t\treq.headers[\"x-forwarded-proto\"] || ((req.socket as any).encrypted ? \"https\" : \"http\");\n\t\tconst base = `${protocol}://${req.headers[\":authority\"] || req.headers.host}`;\n\t\tconst response = await handler(getRequest({ base, request: req }));\n\t\treturn setResponse(res, response);\n\t};\n}\n\nexport { getRequest, setResponse };\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/adapters/node/request.ts","../src/adapters/node/index.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport * as set_cookie_parser from \"set-cookie-parser\";\n\nfunction get_raw_body(req: IncomingMessage, body_size_limit?: number) {\n\tconst h = req.headers;\n\n\tif (!h[\"content-type\"]) return null;\n\n\tconst content_length = Number(h[\"content-length\"]);\n\n\t// check if no request body\n\tif (\n\t\t(req.httpVersionMajor === 1 && isNaN(content_length) && h[\"transfer-encoding\"] == null) ||\n\t\tcontent_length === 0\n\t) {\n\t\treturn null;\n\t}\n\n\tlet length = content_length;\n\n\tif (body_size_limit) {\n\t\tif (!length) {\n\t\t\tlength = body_size_limit;\n\t\t} else if (length > body_size_limit) {\n\t\t\tthrow Error(\n\t\t\t\t`Received content-length of ${length}, but only accept up to ${body_size_limit} bytes.`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (req.destroyed) {\n\t\tconst readable = new ReadableStream();\n\t\treadable.cancel();\n\t\treturn readable;\n\t}\n\n\tlet size = 0;\n\tlet cancelled = false;\n\n\treturn new ReadableStream({\n\t\tstart(controller) {\n\t\t\treq.on(\"error\", (error) => {\n\t\t\t\tcancelled = true;\n\t\t\t\tcontroller.error(error);\n\t\t\t});\n\n\t\t\treq.on(\"end\", () => {\n\t\t\t\tif (cancelled) return;\n\t\t\t\tcontroller.close();\n\t\t\t});\n\n\t\t\treq.on(\"data\", (chunk) => {\n\t\t\t\tif (cancelled) return;\n\n\t\t\t\tsize += chunk.length;\n\n\t\t\t\tif (size > length) {\n\t\t\t\t\tcancelled = true;\n\n\t\t\t\t\tcontroller.error(\n\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t`request body size exceeded ${\n\t\t\t\t\t\t\t\tcontent_length ? \"'content-length'\" : \"BODY_SIZE_LIMIT\"\n\t\t\t\t\t\t\t} of ${length}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcontroller.enqueue(chunk);\n\n\t\t\t\tif (controller.desiredSize === null || controller.desiredSize <= 0) {\n\t\t\t\t\treq.pause();\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\n\t\tpull() {\n\t\t\treq.resume();\n\t\t},\n\n\t\tcancel(reason) {\n\t\t\tcancelled = true;\n\t\t\treq.destroy(reason);\n\t\t},\n\t});\n}\n\nexport function getRequest({\n\trequest,\n\tbase,\n\tbodySizeLimit,\n}: {\n\tbase: string;\n\tbodySizeLimit?: number;\n\trequest: IncomingMessage;\n}) {\n\t// In Express subrouters, `request.url` is relative to the mount path (e.g., '/auth/xxx'),\n\t// and `request.baseUrl` holds the mount path (e.g., '/api').\n\t// Build the full path as baseUrl + url when available to preserve the full route.\n\tconst baseUrl = (request as any)?.baseUrl as string | undefined;\n\tconst fullPath = baseUrl ? baseUrl + request.url : request.url;\n\n\t// Check if body has already been parsed by Express middleware\n\tconst maybeConsumedReq = request as any;\n\tlet body = undefined;\n\n\t// If body was already parsed by Express body-parser middleware\n\tif (maybeConsumedReq.body !== undefined) {\n\t\t// Convert parsed body back to a ReadableStream\n\t\tconst bodyContent =\n\t\t\ttypeof maybeConsumedReq.body === \"string\"\n\t\t\t\t? maybeConsumedReq.body\n\t\t\t\t: JSON.stringify(maybeConsumedReq.body);\n\n\t\tbody = new ReadableStream({\n\t\t\tstart(controller) {\n\t\t\t\tcontroller.enqueue(new TextEncoder().encode(bodyContent));\n\t\t\t\tcontroller.close();\n\t\t\t},\n\t\t});\n\t} else {\n\t\t// Otherwise, get the raw body stream\n\t\tbody = get_raw_body(request, bodySizeLimit);\n\t}\n\n\treturn new Request(base + fullPath, {\n\t\t// @ts-expect-error\n\t\tduplex: \"half\",\n\t\tmethod: request.method,\n\t\tbody,\n\t\theaders: request.headers as Record<string, string>,\n\t});\n}\n\nexport async function setResponse(res: ServerResponse, response: Response) {\n\tfor (const [key, value] of response.headers as any) {\n\t\ttry {\n\t\t\tres.setHeader(\n\t\t\t\tkey,\n\t\t\t\tkey === \"set-cookie\"\n\t\t\t\t\t? set_cookie_parser.splitCookiesString(response.headers.get(key) as string)\n\t\t\t\t\t: value,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tres.getHeaderNames().forEach((name) => res.removeHeader(name));\n\t\t\tres.writeHead(500).end(String(error));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tres.writeHead(response.status);\n\n\tif (!response.body) {\n\t\tres.end();\n\t\treturn;\n\t}\n\n\tif (response.body.locked) {\n\t\tres.end(\n\t\t\t\"Fatal error: Response body is locked. \" +\n\t\t\t\t\"This can happen when the response was already read (for example through 'response.json()' or 'response.text()').\",\n\t\t);\n\t\treturn;\n\t}\n\n\tconst reader = response.body.getReader();\n\n\tif (res.destroyed) {\n\t\treader.cancel();\n\t\treturn;\n\t}\n\n\tconst cancel = (error?: Error) => {\n\t\tres.off(\"close\", cancel);\n\t\tres.off(\"error\", cancel);\n\n\t\t// If the reader has already been interrupted with an error earlier,\n\t\t// then it will appear here, it is useless, but it needs to be catch.\n\t\treader.cancel(error).catch(() => {});\n\t\tif (error) res.destroy(error);\n\t};\n\n\tres.on(\"close\", cancel);\n\tres.on(\"error\", cancel);\n\n\tnext();\n\tasync function next() {\n\t\ttry {\n\t\t\tfor (;;) {\n\t\t\t\tconst { done, value } = await reader.read();\n\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (!res.write(value)) {\n\t\t\t\t\tres.once(\"drain\", next);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tres.end();\n\t\t} catch (error) {\n\t\t\tcancel(error instanceof Error ? error : new Error(String(error)));\n\t\t}\n\t}\n}\n","import type { IncomingMessage, ServerResponse } from \"node:http\";\n\nimport { getRequest, setResponse } from \"./request\";\nimport type { Router } from \"../../router.js\";\n\nexport function toNodeHandler(handler: Router[\"handler\"]) {\n\treturn async (req: IncomingMessage, res: ServerResponse) => {\n\t\tconst protocol =\n\t\t\treq.headers[\"x-forwarded-proto\"] || ((req.socket as any).encrypted ? \"https\" : \"http\");\n\t\tconst base = `${protocol}://${req.headers[\":authority\"] || req.headers.host}`;\n\t\tconst response = await handler(getRequest({ base, request: req }));\n\t\treturn setResponse(res, response);\n\t};\n}\n\nexport { getRequest, setResponse };\n"],"mappings":";AACA,YAAY,uBAAuB;AAEnC,SAAS,aAAa,KAAsB,iBAA0B;AACrE,QAAM,IAAI,IAAI;AAEd,MAAI,CAAC,EAAE,cAAc,EAAG,QAAO;AAE/B,QAAM,iBAAiB,OAAO,EAAE,gBAAgB,CAAC;AAGjD,MACE,IAAI,qBAAqB,KAAK,MAAM,cAAc,KAAK,EAAE,mBAAmB,KAAK,QAClF,mBAAmB,GAClB;AACD,WAAO;AAAA,EACR;AAEA,MAAI,SAAS;AAEb,MAAI,iBAAiB;AACpB,QAAI,CAAC,QAAQ;AACZ,eAAS;AAAA,IACV,WAAW,SAAS,iBAAiB;AACpC,YAAM;AAAA,QACL,8BAA8B,MAAM,2BAA2B,eAAe;AAAA,MAC/E;AAAA,IACD;AAAA,EACD;AAEA,MAAI,IAAI,WAAW;AAClB,UAAM,WAAW,IAAI,eAAe;AACpC,aAAS,OAAO;AAChB,WAAO;AAAA,EACR;AAEA,MAAI,OAAO;AACX,MAAI,YAAY;AAEhB,SAAO,IAAI,eAAe;AAAA,IACzB,MAAM,YAAY;AACjB,UAAI,GAAG,SAAS,CAAC,UAAU;AAC1B,oBAAY;AACZ,mBAAW,MAAM,KAAK;AAAA,MACvB,CAAC;AAED,UAAI,GAAG,OAAO,MAAM;AACnB,YAAI,UAAW;AACf,mBAAW,MAAM;AAAA,MAClB,CAAC;AAED,UAAI,GAAG,QAAQ,CAAC,UAAU;AACzB,YAAI,UAAW;AAEf,gBAAQ,MAAM;AAEd,YAAI,OAAO,QAAQ;AAClB,sBAAY;AAEZ,qBAAW;AAAA,YACV,IAAI;AAAA,cACH,8BACC,iBAAiB,qBAAqB,iBACvC,OAAO,MAAM;AAAA,YACd;AAAA,UACD;AACA;AAAA,QACD;AAEA,mBAAW,QAAQ,KAAK;AAExB,YAAI,WAAW,gBAAgB,QAAQ,WAAW,eAAe,GAAG;AACnE,cAAI,MAAM;AAAA,QACX;AAAA,MACD,CAAC;AAAA,IACF;AAAA,IAEA,OAAO;AACN,UAAI,OAAO;AAAA,IACZ;AAAA,IAEA,OAAO,QAAQ;AACd,kBAAY;AACZ,UAAI,QAAQ,MAAM;AAAA,IACnB;AAAA,EACD,CAAC;AACF;AAEO,SAAS,WAAW;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACD,GAIG;AAIF,QAAM,UAAW,SAAiB;AAClC,QAAM,WAAW,UAAU,UAAU,QAAQ,MAAM,QAAQ;AAG3D,QAAM,mBAAmB;AACzB,MAAI,OAAO;AAGX,MAAI,iBAAiB,SAAS,QAAW;AAExC,UAAM,cACL,OAAO,iBAAiB,SAAS,WAC9B,iBAAiB,OACjB,KAAK,UAAU,iBAAiB,IAAI;AAExC,WAAO,IAAI,eAAe;AAAA,MACzB,MAAM,YAAY;AACjB,mBAAW,QAAQ,IAAI,YAAY,EAAE,OAAO,WAAW,CAAC;AACxD,mBAAW,MAAM;AAAA,MAClB;AAAA,IACD,CAAC;AAAA,EACF,OAAO;AAEN,WAAO,aAAa,SAAS,aAAa;AAAA,EAC3C;AAEA,SAAO,IAAI,QAAQ,OAAO,UAAU;AAAA;AAAA,IAEnC,QAAQ;AAAA,IACR,QAAQ,QAAQ;AAAA,IAChB;AAAA,IACA,SAAS,QAAQ;AAAA,EAClB,CAAC;AACF;AAEA,eAAsB,YAAY,KAAqB,UAAoB;AAC1E,aAAW,CAAC,KAAK,KAAK,KAAK,SAAS,SAAgB;AACnD,QAAI;AACH,UAAI;AAAA,QACH;AAAA,QACA,QAAQ,eACa,qCAAmB,SAAS,QAAQ,IAAI,GAAG,CAAW,IACxE;AAAA,MACJ;AAAA,IACD,SAAS,OAAO;AACf,UAAI,eAAe,EAAE,QAAQ,CAAC,SAAS,IAAI,aAAa,IAAI,CAAC;AAC7D,UAAI,UAAU,GAAG,EAAE,IAAI,OAAO,KAAK,CAAC;AACpC;AAAA,IACD;AAAA,EACD;AAEA,MAAI,UAAU,SAAS,MAAM;AAE7B,MAAI,CAAC,SAAS,MAAM;AACnB,QAAI,IAAI;AACR;AAAA,EACD;AAEA,MAAI,SAAS,KAAK,QAAQ;AACzB,QAAI;AAAA,MACH;AAAA,IAED;AACA;AAAA,EACD;AAEA,QAAM,SAAS,SAAS,KAAK,UAAU;AAEvC,MAAI,IAAI,WAAW;AAClB,WAAO,OAAO;AACd;AAAA,EACD;AAEA,QAAM,SAAS,CAAC,UAAkB;AACjC,QAAI,IAAI,SAAS,MAAM;AACvB,QAAI,IAAI,SAAS,MAAM;AAIvB,WAAO,OAAO,KAAK,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AACnC,QAAI,MAAO,KAAI,QAAQ,KAAK;AAAA,EAC7B;AAEA,MAAI,GAAG,SAAS,MAAM;AACtB,MAAI,GAAG,SAAS,MAAM;AAEtB,OAAK;AACL,iBAAe,OAAO;AACrB,QAAI;AACH,iBAAS;AACR,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAE1C,YAAI,KAAM;AAEV,YAAI,CAAC,IAAI,MAAM,KAAK,GAAG;AACtB,cAAI,KAAK,SAAS,IAAI;AACtB;AAAA,QACD;AAAA,MACD;AACA,UAAI,IAAI;AAAA,IACT,SAAS,OAAO;AACf,aAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACjE;AAAA,EACD;AACD;;;ACvMO,SAAS,cAAc,SAA4B;AACzD,SAAO,OAAO,KAAsB,QAAwB;AAC3D,UAAM,WACL,IAAI,QAAQ,mBAAmB,MAAO,IAAI,OAAe,YAAY,UAAU;AAChF,UAAM,OAAO,GAAG,QAAQ,MAAM,IAAI,QAAQ,YAAY,KAAK,IAAI,QAAQ,IAAI;AAC3E,UAAM,WAAW,MAAM,QAAQ,WAAW,EAAE,MAAM,SAAS,IAAI,CAAC,CAAC;AACjE,WAAO,YAAY,KAAK,QAAQ;AAAA,EACjC;AACD;","names":[]}
@@ -1,3 +1,6 @@
1
+ declare class ErrorWithStack extends Error {
2
+ constructor();
3
+ }
1
4
  declare const _statusCode: {
2
5
  OK: number;
3
6
  CREATED: number;
@@ -51,19 +54,29 @@ declare const _statusCode: {
51
54
  NETWORK_AUTHENTICATION_REQUIRED: number;
52
55
  };
53
56
  type Status = 100 | 101 | 102 | 103 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511;
54
- declare class APIError extends Error {
57
+ declare class InternalAPIError extends Error {
55
58
  status: keyof typeof _statusCode | Status;
56
59
  body: ({
57
60
  message?: string;
58
61
  code?: string;
62
+ cause?: unknown;
59
63
  } & Record<string, any>) | undefined;
60
64
  headers: HeadersInit;
61
65
  statusCode: number;
62
66
  constructor(status?: keyof typeof _statusCode | Status, body?: ({
63
67
  message?: string;
64
68
  code?: string;
69
+ cause?: unknown;
65
70
  } & Record<string, any>) | undefined, headers?: HeadersInit, statusCode?: number);
66
71
  }
72
+ type APIError = InstanceType<typeof InternalAPIError>;
73
+ declare const APIError: new (status?: Status | "OK" | "CREATED" | "ACCEPTED" | "NO_CONTENT" | "MULTIPLE_CHOICES" | "MOVED_PERMANENTLY" | "FOUND" | "SEE_OTHER" | "NOT_MODIFIED" | "TEMPORARY_REDIRECT" | "BAD_REQUEST" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "NOT_FOUND" | "METHOD_NOT_ALLOWED" | "NOT_ACCEPTABLE" | "PROXY_AUTHENTICATION_REQUIRED" | "REQUEST_TIMEOUT" | "CONFLICT" | "GONE" | "LENGTH_REQUIRED" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "URI_TOO_LONG" | "UNSUPPORTED_MEDIA_TYPE" | "RANGE_NOT_SATISFIABLE" | "EXPECTATION_FAILED" | "I'M_A_TEAPOT" | "MISDIRECTED_REQUEST" | "UNPROCESSABLE_ENTITY" | "LOCKED" | "FAILED_DEPENDENCY" | "TOO_EARLY" | "UPGRADE_REQUIRED" | "PRECONDITION_REQUIRED" | "TOO_MANY_REQUESTS" | "REQUEST_HEADER_FIELDS_TOO_LARGE" | "UNAVAILABLE_FOR_LEGAL_REASONS" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT" | "HTTP_VERSION_NOT_SUPPORTED" | "VARIANT_ALSO_NEGOTIATES" | "INSUFFICIENT_STORAGE" | "LOOP_DETECTED" | "NOT_EXTENDED" | "NETWORK_AUTHENTICATION_REQUIRED" | undefined, body?: ({
74
+ message?: string;
75
+ code?: string;
76
+ cause?: unknown;
77
+ } & Record<string, any>) | undefined, headers?: HeadersInit | undefined, statusCode?: number | undefined) => InternalAPIError & {
78
+ errorWithStack: ErrorWithStack;
79
+ };
67
80
 
68
81
  type RequiredKeysOf<BaseType extends object> = Exclude<{
69
82
  [Key in keyof BaseType]: BaseType extends Record<Key, BaseType[Key]> ? Key : never;
@@ -441,11 +454,51 @@ declare const createInternalContext: (context: InputContext<any, any>, { options
441
454
  getSignedCookie: (key: string, secret: string, prefix?: CookiePrefixOptions) => Promise<string | false | null>;
442
455
  setCookie: (key: string, value: string, options?: CookieOptions) => string;
443
456
  setSignedCookie: (key: string, value: string, secret: string, options?: CookieOptions) => Promise<string>;
444
- redirect: (url: string) => APIError;
457
+ redirect: (url: string) => {
458
+ status: keyof typeof _statusCode | Status;
459
+ body: ({
460
+ message?: string;
461
+ code?: string;
462
+ cause?: unknown;
463
+ } & Record<string, any>) | undefined;
464
+ headers: HeadersInit;
465
+ statusCode: number;
466
+ name: string;
467
+ message: string;
468
+ stack?: string;
469
+ cause?: unknown;
470
+ } & {
471
+ errorWithStack: {
472
+ name: string;
473
+ message: string;
474
+ stack?: string;
475
+ cause?: unknown;
476
+ };
477
+ };
445
478
  error: (status: keyof typeof _statusCode | Status, body?: {
446
479
  message?: string;
447
480
  code?: string;
448
- } | undefined, headers?: HeadersInit) => APIError;
481
+ } | undefined, headers?: HeadersInit) => {
482
+ status: keyof typeof _statusCode | Status;
483
+ body: ({
484
+ message?: string;
485
+ code?: string;
486
+ cause?: unknown;
487
+ } & Record<string, any>) | undefined;
488
+ headers: HeadersInit;
489
+ statusCode: number;
490
+ name: string;
491
+ message: string;
492
+ stack?: string;
493
+ cause?: unknown;
494
+ } & {
495
+ errorWithStack: {
496
+ name: string;
497
+ message: string;
498
+ stack?: string;
499
+ cause?: unknown;
500
+ };
501
+ };
449
502
  json: (json: Record<string, any>, routerResponse?: {
450
503
  status?: number;
451
504
  headers?: Record<string, string>;
@@ -472,11 +525,51 @@ declare const createInternalContext: (context: InputContext<any, any>, { options
472
525
  getSignedCookie: (key: string, secret: string, prefix?: CookiePrefixOptions) => Promise<string | false | null>;
473
526
  setCookie: (key: string, value: string, options?: CookieOptions) => string;
474
527
  setSignedCookie: (key: string, value: string, secret: string, options?: CookieOptions) => Promise<string>;
475
- redirect: (url: string) => APIError;
528
+ redirect: (url: string) => {
529
+ status: keyof typeof _statusCode | Status;
530
+ body: ({
531
+ message?: string;
532
+ code?: string;
533
+ cause?: unknown;
534
+ } & Record<string, any>) | undefined;
535
+ headers: HeadersInit;
536
+ statusCode: number;
537
+ name: string;
538
+ message: string;
539
+ stack?: string;
540
+ cause?: unknown;
541
+ } & {
542
+ errorWithStack: {
543
+ name: string;
544
+ message: string;
545
+ stack?: string;
546
+ cause?: unknown;
547
+ };
548
+ };
476
549
  error: (status: keyof typeof _statusCode | Status, body?: {
477
550
  message?: string;
478
551
  code?: string;
479
- } | undefined, headers?: HeadersInit) => APIError;
552
+ } | undefined, headers?: HeadersInit) => {
553
+ status: keyof typeof _statusCode | Status;
554
+ body: ({
555
+ message?: string;
556
+ code?: string;
557
+ cause?: unknown;
558
+ } & Record<string, any>) | undefined;
559
+ headers: HeadersInit;
560
+ statusCode: number;
561
+ name: string;
562
+ message: string;
563
+ stack?: string;
564
+ cause?: unknown;
565
+ } & {
566
+ errorWithStack: {
567
+ name: string;
568
+ message: string;
569
+ stack?: string;
570
+ cause?: unknown;
571
+ };
572
+ };
480
573
  json: (json: Record<string, any>, routerResponse?: {
481
574
  status?: number;
482
575
  headers?: Record<string, string>;
@@ -503,11 +596,51 @@ declare const createInternalContext: (context: InputContext<any, any>, { options
503
596
  getSignedCookie: (key: string, secret: string, prefix?: CookiePrefixOptions) => Promise<string | false | null>;
504
597
  setCookie: (key: string, value: string, options?: CookieOptions) => string;
505
598
  setSignedCookie: (key: string, value: string, secret: string, options?: CookieOptions) => Promise<string>;
506
- redirect: (url: string) => APIError;
599
+ redirect: (url: string) => {
600
+ status: keyof typeof _statusCode | Status;
601
+ body: ({
602
+ message?: string;
603
+ code?: string;
604
+ cause?: unknown;
605
+ } & Record<string, any>) | undefined;
606
+ headers: HeadersInit;
607
+ statusCode: number;
608
+ name: string;
609
+ message: string;
610
+ stack?: string;
611
+ cause?: unknown;
612
+ } & {
613
+ errorWithStack: {
614
+ name: string;
615
+ message: string;
616
+ stack?: string;
617
+ cause?: unknown;
618
+ };
619
+ };
507
620
  error: (status: keyof typeof _statusCode | Status, body?: {
508
621
  message?: string;
509
622
  code?: string;
510
- } | undefined, headers?: HeadersInit) => APIError;
623
+ } | undefined, headers?: HeadersInit) => {
624
+ status: keyof typeof _statusCode | Status;
625
+ body: ({
626
+ message?: string;
627
+ code?: string;
628
+ cause?: unknown;
629
+ } & Record<string, any>) | undefined;
630
+ headers: HeadersInit;
631
+ statusCode: number;
632
+ name: string;
633
+ message: string;
634
+ stack?: string;
635
+ cause?: unknown;
636
+ } & {
637
+ errorWithStack: {
638
+ name: string;
639
+ message: string;
640
+ stack?: string;
641
+ cause?: unknown;
642
+ };
643
+ };
511
644
  json: (json: Record<string, any>, routerResponse?: {
512
645
  status?: number;
513
646
  headers?: Record<string, string>;
@@ -534,11 +667,51 @@ declare const createInternalContext: (context: InputContext<any, any>, { options
534
667
  getSignedCookie: (key: string, secret: string, prefix?: CookiePrefixOptions) => Promise<string | false | null>;
535
668
  setCookie: (key: string, value: string, options?: CookieOptions) => string;
536
669
  setSignedCookie: (key: string, value: string, secret: string, options?: CookieOptions) => Promise<string>;
537
- redirect: (url: string) => APIError;
670
+ redirect: (url: string) => {
671
+ status: keyof typeof _statusCode | Status;
672
+ body: ({
673
+ message?: string;
674
+ code?: string;
675
+ cause?: unknown;
676
+ } & Record<string, any>) | undefined;
677
+ headers: HeadersInit;
678
+ statusCode: number;
679
+ name: string;
680
+ message: string;
681
+ stack?: string;
682
+ cause?: unknown;
683
+ } & {
684
+ errorWithStack: {
685
+ name: string;
686
+ message: string;
687
+ stack?: string;
688
+ cause?: unknown;
689
+ };
690
+ };
538
691
  error: (status: keyof typeof _statusCode | Status, body?: {
539
692
  message?: string;
540
693
  code?: string;
541
- } | undefined, headers?: HeadersInit) => APIError;
694
+ } | undefined, headers?: HeadersInit) => {
695
+ status: keyof typeof _statusCode | Status;
696
+ body: ({
697
+ message?: string;
698
+ code?: string;
699
+ cause?: unknown;
700
+ } & Record<string, any>) | undefined;
701
+ headers: HeadersInit;
702
+ statusCode: number;
703
+ name: string;
704
+ message: string;
705
+ stack?: string;
706
+ cause?: unknown;
707
+ } & {
708
+ errorWithStack: {
709
+ name: string;
710
+ message: string;
711
+ stack?: string;
712
+ cause?: unknown;
713
+ };
714
+ };
542
715
  json: (json: Record<string, any>, routerResponse?: {
543
716
  status?: number;
544
717
  headers?: Record<string, string>;
@@ -676,6 +849,10 @@ interface EndpointOptions {
676
849
  * Query Schema
677
850
  */
678
851
  query?: StandardSchemaV1;
852
+ /**
853
+ * Error Schema
854
+ */
855
+ error?: StandardSchemaV1;
679
856
  /**
680
857
  * If true headers will be required to be passed in the context
681
858
  */