Skip to content

Commit fb66924

Browse files
feat: improve typed headers (#625)
Co-authored-by: Pooya Parsa <pooya@pi0.io>
1 parent 2dcaf06 commit fb66924

File tree

9 files changed

+297
-148
lines changed

9 files changed

+297
-148
lines changed

docs/2.utils/2.response.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ export default defineEventHandler((event) => {
254254
});
255255
```
256256

257-
### `setHeaders(event)`
257+
### `setHeaders(event, headers)`
258258

259259
Set the response headers.
260260

@@ -281,7 +281,7 @@ export default defineEventHandler((event) => {
281281
});
282282
```
283283

284-
### `setResponseHeaders(event)`
284+
### `setResponseHeaders(event, headers)`
285285

286286
Set the response headers.
287287

src/types.ts

Lines changed: 0 additions & 119 deletions
This file was deleted.

src/types/_headers.ts

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import type { HTTPMethod } from ".";
2+
import type { MimeType } from "./_mimes";
3+
import type { AnyString, AnyNumber } from "./_utils";
4+
5+
export type RequestHeaders = Partial<
6+
Record<HTTPHeaderName, string | undefined>
7+
>;
8+
9+
// prettier-ignore
10+
type _HTTPHeaderName = "WWW-Authenticate" | "Authorization" | "Proxy-Authenticate" | "Proxy-Authorization" | "Age" | "Cache-Control" | "Clear-Site-Data" | "Expires" | "Pragma" | "Accept-CH" | "Critical-CH" | "Sec-CH-UA" | "Sec-CH-UA-Arch" | "Sec-CH-UA-Bitness" | "Sec-CH-UA-Full-Version-List" | "Sec-CH-UA-Mobile" | "Sec-CH-UA-Model" | "Sec-CH-UA-Platform" | "Sec-CH-UA-Platform-Version" | "Sec-CH-UA-Prefers-Color-Scheme" | "Sec-CH-UA-Prefers-Reduced-Motion" | "Downlink" | "ECT" | "RTT" | "Save-Data" | "Last-Modified" | "ETag" | "If-Match" | "If-None-Match" | "If-Modified-Since" | "If-Unmodified-Since" | "Vary" | "Connection" | "Keep-Alive" | "Accept" | "Accept-Encoding" | "Accept-Language" | "Expect" | "Max-Forwards" | "Cookie" | "Set-Cookie" | "Access-Control-Allow-Origin" | "Access-Control-Allow-Credentials" | "Access-Control-Allow-Headers" | "Access-Control-Allow-Methods" | "Access-Control-Expose-Headers" | "Access-Control-Max-Age" | "Access-Control-Request-Headers" | "Access-Control-Request-Method" | "Origin" | "Timing-Allow-Origin" | "Content-Disposition" | "Content-Length" | "Content-Type" | "Content-Encoding" | "Content-Language" | "Content-Location" | "Forwarded" | "X-Forwarded-For" | "X-Forwarded-Host" | "X-Forwarded-Proto" | "Via" | "Location" | "Refresh" | "From" | "Host" | "Referer" | "Referrer-Policy" | "User-Agent" | "Allow" | "Server" | "Accept-Ranges" | "Range" | "If-Range" | "Content-Range" | "Cross-Origin-Embedder-Policy" | "Cross-Origin-Opener-Policy" | "Cross-Origin-Resource-Policy" | "Content-Security-Policy" | "Content-Security-Policy-Report-Only" | "Expect-CT" | "Origin-Isolation" | "Permissions-Policy" | "Strict-Transport-Security" | "Upgrade-Insecure-Requests" | "X-Content-Type-Options" | "X-Frame-Options" | "X-Permitted-Cross-Domain-Policies" | "X-Powered-By" | "X-XSS-Protection" | "Sec-Fetch-Site" | "Sec-Fetch-Mode" | "Sec-Fetch-User" | "Sec-Fetch-Dest" | "Sec-Purpose" | "Service-Worker-Navigation-Preload" | "Last-Event-ID" | "NEL" | "Ping-From" | "Ping-To" | "Report-To" | "Transfer-Encoding" | "TE" | "Trailer" | "Sec-WebSocket-Key" | "Sec-WebSocket-Extensions" | "Sec-WebSocket-Accept" | "Sec-WebSocket-Protocol" | "Sec-WebSocket-Version" | "Accept-Push-Policy" | "Accept-Signature" | "Alt-Svc" | "Alt-Used" | "Date" | "Early-Data" | "Link" | "Push-Policy" | "Retry-After" | "Signature" | "Signed-Headers" | "Server-Timing" | "Service-Worker-Allowed" | "SourceMap" | "Upgrade" | "X-DNS-Prefetch-Control" | "X-Pingback" | "X-Requested-With" | "X-Robots-Tag";
11+
12+
export type HTTPHeaderName =
13+
| _HTTPHeaderName
14+
| Lowercase<_HTTPHeaderName>
15+
| (string & {}); // eslint-disable-line @typescript-eslint/ban-types
16+
17+
// prettier-ignore
18+
type ClientHint = "Sec-CH-UA" | "Sec-CH-UA-Arch" | "Sec-CH-UA-Bitness" | "Sec-CH-UA-Full-Version-List" | "Sec-CH-UA-Full-Version" | "Sec-CH-UA-Mobile" | "Sec-CH-UA-Model" | "Sec-CH-UA-Platform" | "Sec-CH-UA-Platform-Version" | "Sec-CH-Prefers-Reduced-Motion" | "Sec-CH-Prefers-Color-Scheme" | "Device-Memory" | "Width" | "Viewport-Width" | "Save-Data" | "Downlink" | "ECT" | "RTT" | AnyString;
19+
20+
export type TypedHeaders = Partial<Record<HTTPHeaderName, unknown>> &
21+
Partial<{
22+
host: string;
23+
24+
location: string;
25+
26+
referrer: string;
27+
28+
origin: "null" | AnyString;
29+
30+
from: string;
31+
32+
"alt-used": string;
33+
34+
"content-location": string;
35+
36+
sourcemap: string;
37+
38+
"content-length": number;
39+
40+
"access-control-max-age": number;
41+
42+
"retry-after": number;
43+
44+
rtt: number;
45+
46+
age: number;
47+
48+
"max-forwards": number;
49+
50+
downlink: number;
51+
52+
"device-memory": 0.25 | 0.5 | 1 | 2 | 4 | 8 | AnyNumber;
53+
54+
accept: MimeType | MimeType[] | `${MimeType};q=${number}`[];
55+
56+
"content-type": MimeType;
57+
58+
"accept-ch": ClientHint | ClientHint[];
59+
60+
"keep-alive": `timeout=${number}, max=${number}` | AnyString;
61+
62+
"access-control-allow-credentials": "true" | AnyString;
63+
64+
"access-control-allow-headers": "*" | HTTPHeaderName[] | AnyString;
65+
66+
"access-control-allow-methods": "*" | HTTPMethod[] | AnyString;
67+
68+
"access-control-allow-origin": "*" | "null" | AnyString;
69+
70+
"access-control-expose-headers": "*" | HTTPHeaderName[] | AnyString;
71+
72+
"access-control-request-headers": HTTPHeaderName[] | AnyString;
73+
74+
"access-control-request-method": HTTPMethod | AnyString;
75+
76+
"early-data": 1;
77+
78+
"upgrade-insecure-requests": 1;
79+
80+
// prettier-ignore
81+
"accept-ranges": "bytes" | "none" | AnyString;
82+
83+
// prettier-ignore
84+
connection: "keep-alive" | "close" | "upgrade" | AnyString;
85+
86+
// prettier-ignore
87+
ect: "slow-2g" | "2g" | "3g" | "4g" | AnyString;
88+
89+
// prettier-ignore
90+
expect: "100-continue" | AnyString;
91+
92+
// prettier-ignore
93+
"save-data": `on` | `off` | AnyString;
94+
95+
// prettier-ignore
96+
"sec-ch-prefers-reduced-motion": "no-preference" | "reduce" | AnyString;
97+
98+
// prettier-ignore
99+
"sec-ch-prefers-reduced-transparency": "no-preference" | "reduce" | AnyString;
100+
101+
// prettier-ignore
102+
"sec-ch-ua-mobile": `?1` | `?0` | AnyString;
103+
104+
// prettier-ignore
105+
"origin-agent-cluster": `?1` | `?0` | AnyString;
106+
107+
// prettier-ignore
108+
"sec-fetch-user": "?1" | AnyString;
109+
110+
// prettier-ignore
111+
"sec-purpose": "prefetch" | AnyString;
112+
113+
// prettier-ignore
114+
"x-content-type-options": "nosniff" | AnyString;
115+
116+
// prettier-ignore
117+
"x-dns-prefetch-control": "on" | "off" | AnyString;
118+
119+
// prettier-ignore
120+
"x-frame-options": "DENY" | "SAMEORIGIN" | AnyString;
121+
122+
// prettier-ignore
123+
"sec-ch-ua-arch": "x86" | "ARM" | "[arm64-v8a, armeabi-v7a, armeabi]" | AnyString;
124+
125+
// prettier-ignore
126+
"sec-fetch-site": "cross-site" | "same-origin" | "same-site" | "none" | AnyString;
127+
128+
// prettier-ignore
129+
"sec-ch-prefers-color-scheme": "dark" | "light" | AnyString;
130+
131+
// prettier-ignore
132+
"sec-ch-ua-bitness": "64" | "32" | AnyString;
133+
134+
// prettier-ignore
135+
"sec-fetch-mode": "cors" | "navigate" | "no-cors" | "same-origin" | "websocket" | AnyString;
136+
137+
// prettier-ignore
138+
"cross-origin-embedder-policy": "unsafe-none" | "require-corp" | "credentialless" | AnyString;
139+
140+
// prettier-ignore
141+
"cross-origin-opener-policy": "unsafe-none" | "same-origin-allow-popups" | "same-origin" | AnyString;
142+
143+
// prettier-ignore
144+
"cross-origin-resource-policy": "same-site" | "same-origin" | "cross-origin" | AnyString;
145+
146+
// prettier-ignore
147+
"sec-ch-ua-platform": "Android" | "Chrome OS" | "Chromium OS" | "iOS" | "Linux" | "macOS" | "Windows" | "Unknown" | AnyString;
148+
149+
// prettier-ignore
150+
"referrer-policy": "no-referrer" | "no-referrer-when-downgrade" | "origin" | "origin-when-cross-origin" | "same-origin" | "strict-origin" | "strict-origin-when-cross-origin" | "unsafe-url" | AnyString;
151+
152+
// prettier-ignore
153+
"sec-fetch-dest": "audio" | "audioworklet" | "document" | "embed" | "empty" | "font" | "frame" | "iframe" | "image" | "manifest" | "object" | "paintworklet" | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track" | "video" | "worker" | "xslt" | AnyString;
154+
}>;

src/types/_mimes.ts

Lines changed: 4 additions & 0 deletions
Large diffs are not rendered by default.

src/types/_utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export type AnyString = string & {}; // eslint-disable-line @typescript-eslint/ban-types
2+
export type AnyNumber = number & {}; // eslint-disable-line @typescript-eslint/ban-types

src/types/index.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import type { QueryObject } from "ufo";
2+
import type { Hooks as WSHooks } from "crossws";
3+
import type { H3Event } from "../event";
4+
import type { Session } from "../utils/session";
5+
import type { RouteNode } from "../router";
6+
import type { AnyNumber } from "./_utils";
7+
8+
export type {
9+
ValidateFunction,
10+
ValidateResult,
11+
} from "../utils/internal/validate";
12+
13+
// https://www.rfc-editor.org/rfc/rfc7231#section-4.1
14+
// prettier-ignore
15+
export type HTTPMethod = "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | "DELETE" | "CONNECT" | "OPTIONS" | "TRACE";
16+
17+
// prettier-ignore
18+
// eslint-disable-next-line unicorn/text-encoding-identifier-case
19+
export type Encoding = false | "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex";
20+
21+
// prettier-ignore
22+
export type StatusCode = 100 | 101 | 102 | 103 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 304 | 305 | 307 | 308 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 444 | 450 | 451 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 506 | 507 | 508 | 509 | 510 | 511 | 521 | 522 | 523 | 525 | 530 | 599 | AnyNumber;
23+
24+
export interface H3EventContext extends Record<string, any> {
25+
/* Matched router parameters */
26+
params?: Record<string, string>;
27+
/**
28+
* Matched router Node
29+
*
30+
* @experimental The object structure may change in non-major version.
31+
*/
32+
matchedRoute?: RouteNode;
33+
/* Cached session data */
34+
sessions?: Record<string, Session>;
35+
/* Trusted IP Address of client */
36+
clientAddress?: string;
37+
}
38+
39+
export type EventHandlerResponse<T = any> = T | Promise<T>;
40+
41+
export interface EventHandlerRequest {
42+
body?: any; // TODO: Default to unknown in next major version
43+
query?: QueryObject;
44+
routerParams?: Record<string, string>;
45+
}
46+
47+
export type InferEventInput<
48+
Key extends keyof EventHandlerRequest,
49+
Event extends H3Event,
50+
T,
51+
> = void extends T ? (Event extends H3Event<infer E> ? E[Key] : never) : T;
52+
53+
type MaybePromise<T> = T | Promise<T>;
54+
55+
export type EventHandlerResolver = (
56+
path: string,
57+
) => MaybePromise<undefined | { route?: string; handler: EventHandler }>;
58+
59+
export interface EventHandler<
60+
Request extends EventHandlerRequest = EventHandlerRequest,
61+
Response extends EventHandlerResponse = EventHandlerResponse,
62+
> {
63+
__is_handler__?: true;
64+
__resolve__?: EventHandlerResolver;
65+
__websocket__?: Partial<WSHooks>;
66+
(event: H3Event<Request>): Response;
67+
}
68+
69+
export type _RequestMiddleware<
70+
Request extends EventHandlerRequest = EventHandlerRequest,
71+
> = (event: H3Event<Request>) => void | Promise<void>;
72+
73+
export type _ResponseMiddleware<
74+
Request extends EventHandlerRequest = EventHandlerRequest,
75+
Response extends EventHandlerResponse = EventHandlerResponse,
76+
> = (
77+
event: H3Event<Request>,
78+
response: { body?: Awaited<Response> },
79+
) => void | Promise<void>;
80+
81+
export type EventHandlerObject<
82+
Request extends EventHandlerRequest = EventHandlerRequest,
83+
Response extends EventHandlerResponse = EventHandlerResponse,
84+
> = {
85+
onRequest?: _RequestMiddleware<Request> | _RequestMiddleware<Request>[];
86+
onBeforeResponse?:
87+
| _ResponseMiddleware<Request, Response>
88+
| _ResponseMiddleware<Request, Response>[];
89+
/** @experimental */
90+
websocket?: Partial<WSHooks>;
91+
handler: EventHandler<Request, Response>;
92+
};
93+
94+
export type LazyEventHandler = () => EventHandler | Promise<EventHandler>;
95+
96+
export type { MimeType } from "./_mimes";
97+
export type { TypedHeaders, RequestHeaders, HTTPHeaderName } from "./_headers";

src/utils/consts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export const MIMES = {
22
html: "text/html",
33
json: "application/json",
4-
};
4+
} as const;

0 commit comments

Comments
 (0)