h3 2.0.1-rc.21 → 2.0.1-rc.23
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/_entries/bun.d.mts +3 -3
- package/dist/_entries/bun.mjs +2 -3
- package/dist/_entries/cloudflare.d.mts +3 -3
- package/dist/_entries/cloudflare.mjs +2 -3
- package/dist/_entries/deno.d.mts +3 -3
- package/dist/_entries/deno.mjs +2 -3
- package/dist/_entries/generic.d.mts +3 -3
- package/dist/_entries/generic.mjs +2 -3
- package/dist/_entries/node.d.mts +3 -3
- package/dist/_entries/node.mjs +2 -3
- package/dist/_entries/service-worker.d.mts +3 -3
- package/dist/_entries/service-worker.mjs +2 -3
- package/dist/docs/0.guide/2.api/0.h3.md +1 -0
- package/dist/docs/0.guide/3.advanced/1.websocket.md +91 -51
- package/dist/docs/1.utils/1.request.md +15 -5
- package/dist/docs/1.utils/4.security.md +12 -0
- package/dist/docs/1.utils/5.proxy.md +12 -0
- package/dist/docs/1.utils/7.more.md +40 -1
- package/dist/docs/1.utils/8.community.md +6 -0
- package/dist/docs/2.examples/2.handle-session.md +34 -1
- package/dist/{h3-D76FUMrE.d.mts → h3.d.mts} +12 -3
- package/dist/{h3-CRCltuUf.mjs → h3.mjs} +190 -82
- package/dist/{h3-DiSMXP1G.d.mts → index.d.mts} +136 -13
- package/dist/tracing.d.mts +12 -2
- package/dist/tracing.mjs +15 -2
- package/package.json +28 -28
- package/dist/h3-DagAgogP.mjs +0 -4
|
@@ -4,14 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
You can add cross platform WebSocket support to H3 servers using [🔌 CrossWS](https://crossws.h3.dev/).
|
|
6
6
|
|
|
7
|
-
> [!IMPORTANT]
|
|
8
|
-
> Built-in support of WebSockets in h3 version is WIP.
|
|
9
|
-
|
|
10
7
|
## Usage
|
|
11
8
|
|
|
12
9
|
WebSocket handlers can be defined using the `defineWebSocketHandler()` utility and registered to any route like event handlers.
|
|
13
10
|
|
|
14
|
-
You need to register CrossWS as a server plugin in the `serve` function
|
|
11
|
+
You need to register CrossWS as a server plugin in the `serve` function. The plugin resolves the correct hooks from your matched route automatically.
|
|
15
12
|
|
|
16
13
|
```js
|
|
17
14
|
import { H3, serve, defineWebSocketHandler } from "h3";
|
|
@@ -23,71 +20,114 @@ const app = new H3();
|
|
|
23
20
|
app.get("/_ws", defineWebSocketHandler({ message: console.log }));
|
|
24
21
|
|
|
25
22
|
serve(app, {
|
|
26
|
-
plugins: [ws(
|
|
23
|
+
plugins: [ws()],
|
|
27
24
|
});
|
|
28
25
|
```
|
|
29
26
|
|
|
27
|
+
> [!NOTE]
|
|
28
|
+
> Passing a custom `resolve` to `ws()` is only needed to resolve hooks yourself (for example without invoking the app). By default, CrossWS calls the app's `fetch` handler and reads the hooks attached by `defineWebSocketHandler()`.
|
|
29
|
+
|
|
30
30
|
**Full example:**
|
|
31
31
|
|
|
32
32
|
```js [websocket.mjs]
|
|
33
|
-
import { H3, serve, defineWebSocketHandler } from "h3";
|
|
33
|
+
import { H3, serve, html, defineWebSocketHandler } from "h3";
|
|
34
34
|
import { plugin as ws } from "crossws/server";
|
|
35
35
|
|
|
36
36
|
export const app = new H3();
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
// A minimal self-contained WebSocket playground served for plain HTTP requests.
|
|
39
|
+
const playground = html`<!doctype html>
|
|
40
|
+
<title>H3 WebSocket Playground</title>
|
|
41
|
+
<h1>H3 WebSocket Playground</h1>
|
|
42
|
+
<form id="form">
|
|
43
|
+
<input id="input" placeholder="Type a message..." autocomplete="off" autofocus />
|
|
44
|
+
<button type="submit">Send</button>
|
|
45
|
+
</form>
|
|
46
|
+
<div id="log"></div>
|
|
47
|
+
<script type="module">
|
|
48
|
+
const log = (msg) => {
|
|
49
|
+
const line = document.createElement("div");
|
|
50
|
+
line.textContent = msg;
|
|
51
|
+
document.getElementById("log").append(line);
|
|
52
|
+
};
|
|
53
|
+
const url = location.href.replace(/^http/, "ws");
|
|
54
|
+
const ws = new WebSocket(url);
|
|
55
|
+
ws.addEventListener("open", () => log("[open] connected to " + url));
|
|
56
|
+
ws.addEventListener("message", (e) => log("[message] " + e.data));
|
|
57
|
+
ws.addEventListener("close", () => log("[close] disconnected"));
|
|
58
|
+
document.getElementById("form").addEventListener("submit", (e) => {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
const input = document.getElementById("input");
|
|
61
|
+
ws.send(input.value);
|
|
62
|
+
input.value = "";
|
|
63
|
+
});
|
|
64
|
+
</script>`;
|
|
65
|
+
|
|
66
|
+
// A single route serves both the playground page (plain HTTP) and the
|
|
67
|
+
// WebSocket endpoint (upgrade requests). The page connects back to itself.
|
|
47
68
|
app.get(
|
|
48
|
-
"/
|
|
49
|
-
defineWebSocketHandler(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
"/",
|
|
70
|
+
defineWebSocketHandler(
|
|
71
|
+
{
|
|
72
|
+
open(peer) {
|
|
73
|
+
console.log("[open]", peer);
|
|
74
|
+
|
|
75
|
+
// Send welcome to the new client
|
|
76
|
+
peer.send("Welcome to the server!");
|
|
77
|
+
|
|
78
|
+
// Join new client to the "chat" channel
|
|
79
|
+
peer.subscribe("chat");
|
|
80
|
+
|
|
81
|
+
// Notify every other connected client
|
|
82
|
+
peer.publish("chat", `[system] ${peer} joined!`);
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
message(peer, message) {
|
|
86
|
+
console.log("[message]", peer);
|
|
87
|
+
|
|
88
|
+
if (message.text() === "ping") {
|
|
89
|
+
// Reply to the client with a ping response
|
|
90
|
+
peer.send("pong");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// The server re-broadcasts incoming messages to everyone
|
|
95
|
+
peer.publish("chat", `[${peer}] ${message}`);
|
|
96
|
+
|
|
97
|
+
// Echo the message back to the sender
|
|
98
|
+
peer.send(message);
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
close(peer) {
|
|
102
|
+
console.log("[close]", peer);
|
|
103
|
+
peer.publish("chat", `[system] ${peer} has left the chat!`);
|
|
104
|
+
peer.unsubscribe("chat");
|
|
105
|
+
},
|
|
62
106
|
},
|
|
107
|
+
// Non-upgrade requests get the playground page.
|
|
108
|
+
() => playground,
|
|
109
|
+
),
|
|
110
|
+
);
|
|
63
111
|
|
|
64
|
-
|
|
65
|
-
|
|
112
|
+
serve(app, {
|
|
113
|
+
plugins: [ws()],
|
|
114
|
+
});
|
|
115
|
+
```
|
|
66
116
|
|
|
67
|
-
|
|
68
|
-
// Reply to the client with a ping response
|
|
69
|
-
peer.send("pong");
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
117
|
+
### Handling HTTP requests
|
|
72
118
|
|
|
73
|
-
|
|
74
|
-
peer.publish("chat", `[${peer}] ${message}`);
|
|
119
|
+
By default, a WebSocket route responds with `426 Upgrade Required` to any request that is not a WebSocket upgrade.
|
|
75
120
|
|
|
76
|
-
|
|
77
|
-
peer.send(message);
|
|
78
|
-
},
|
|
121
|
+
You can pass an optional HTTP handler as the second argument to `defineWebSocketHandler()` to serve regular (non-upgrade) requests on the same route. WebSocket upgrade requests still go to the hooks.
|
|
79
122
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
|
|
123
|
+
```js
|
|
124
|
+
app.get(
|
|
125
|
+
"/_ws",
|
|
126
|
+
defineWebSocketHandler(
|
|
127
|
+
{ message: (peer, message) => peer.send(message.text()) },
|
|
128
|
+
() => "Send a WebSocket upgrade request to connect.",
|
|
129
|
+
),
|
|
86
130
|
);
|
|
87
|
-
|
|
88
|
-
serve(app, {
|
|
89
|
-
plugins: [ws({ resolve: async (req) => (await app.fetch(req)).crossws })],
|
|
90
|
-
});
|
|
91
131
|
```
|
|
92
132
|
|
|
93
133
|
## Server-Sent Events (SSE)
|
|
@@ -19,18 +19,28 @@ app.get("/", async (event) => {
|
|
|
19
19
|
});
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
### `readBody(event)`
|
|
22
|
+
### `readBody(event, options?)`
|
|
23
23
|
|
|
24
24
|
Reads request body and tries to parse using JSON.parse or URLSearchParams.
|
|
25
25
|
|
|
26
|
+
By default the body is parsed as JSON (falling back to URL-encoded parsing when the `Content-Type` is `application/x-www-form-urlencoded`). Other body types, such as `multipart/form-data`, must be opted into explicitly via `options.type` and are never auto-detected from the request headers.
|
|
27
|
+
|
|
26
28
|
**Example:**
|
|
27
29
|
|
|
28
30
|
```ts
|
|
29
|
-
app.
|
|
31
|
+
app.post("/", async (event) => {
|
|
30
32
|
const body = await readBody(event);
|
|
31
33
|
});
|
|
32
34
|
```
|
|
33
35
|
|
|
36
|
+
**Example:**
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
app.post("/upload", async (event) => {
|
|
40
|
+
const body = await readBody(event, { type: "formData" });
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
34
44
|
### `readValidatedBody(event, validate)`
|
|
35
45
|
|
|
36
46
|
Tries to read the request body via `readBody`, then uses the provided validation schema or function and either throws a validation error or returns the result.
|
|
@@ -155,7 +165,7 @@ app.get("/", (event) => {
|
|
|
155
165
|
|
|
156
166
|
Get the request protocol.
|
|
157
167
|
|
|
158
|
-
If `x-forwarded-proto` header is set to "https", it will return "https". You can disable this behavior by setting `xForwardedProto` to `false`.
|
|
168
|
+
If `x-forwarded-proto` header is set to "https", it will return "https". If the header contains a comma-separated list of protocols, the first entry is used. You can disable this behavior by setting `xForwardedProto` to `false`.
|
|
159
169
|
|
|
160
170
|
If protocol cannot be determined, it will default to "http".
|
|
161
171
|
|
|
@@ -187,7 +197,7 @@ app.get("/", (event) => {
|
|
|
187
197
|
|
|
188
198
|
Get a matched route param by name.
|
|
189
199
|
|
|
190
|
-
If `decode` option is `true`, it will decode the matched route param using `
|
|
200
|
+
If `decode` option is `true`, it will decode the matched route param using `decodeURIComponent`.
|
|
191
201
|
|
|
192
202
|
**Example:**
|
|
193
203
|
|
|
@@ -265,7 +275,7 @@ app.get("/", async (event) => {
|
|
|
265
275
|
|
|
266
276
|
Get matched route params and validate with validate function.
|
|
267
277
|
|
|
268
|
-
If `decode` option is `true`, it will decode the matched route params using `
|
|
278
|
+
If `decode` option is `true`, it will decode the matched route params using `decodeURIComponent`.
|
|
269
279
|
|
|
270
280
|
You can use a simple function to validate the params object or use a Standard-Schema compatible library like `zod` to define a schema.
|
|
271
281
|
|
|
@@ -107,3 +107,15 @@ Check if the origin is allowed.
|
|
|
107
107
|
### `isPreflightRequest(event)`
|
|
108
108
|
|
|
109
109
|
Check if the incoming request is a CORS preflight request.
|
|
110
|
+
|
|
111
|
+
## Path
|
|
112
|
+
|
|
113
|
+
### `resolveDotSegments(path, opts?)`
|
|
114
|
+
|
|
115
|
+
Resolve `.` and `..` segments in a path, without ever escaping above the root `/`. The result is always an absolute path with a single leading `/`, so it can never be protocol-relative (`//host`).
|
|
116
|
+
|
|
117
|
+
Also decodes percent-encoded dot segments at any `%25`-nesting depth (`%2e`, `%252e`, ...) and normalizes `\` to `/`, so encoded or backslash-based traversal (e.g. `%2e%2e/`, `..\..\`) is caught the same way as a literal `../`.
|
|
118
|
+
|
|
119
|
+
`%2f`/`%5c` (encoded path separators) are left untouched by default — see {@link ResolveDotSegmentsOptions.decodeSlashes}.
|
|
120
|
+
|
|
121
|
+
Only `.`/`..` resolution and the decodes above alter the string; every other percent-encoding (`%20`, non-ASCII, `%3A`, and any `%2e` not forming a whole segment) is left intact, so the result stays in the same representation as an un-decoded `event.url.pathname` and matches routes/rules consistently. Interior empty segments are preserved (`/a//b` stays `/a//b`, per WHATWG URL normalization) — only the leading slash is guaranteed single, so a consumer doing exact prefix matching should normalize its allowlist the same way.
|
|
@@ -28,4 +28,16 @@ Proxy the incoming request to a target URL.
|
|
|
28
28
|
|
|
29
29
|
If the `target` starts with `/`, the request is handled internally by the app router via `event.app.fetch()` instead of making an external HTTP request.
|
|
30
30
|
|
|
31
|
+
The request body is streamed to the target without buffering. Per the Fetch standard, a request body can only be consumed once, so reading it beforehand (e.g. via `readBody()`, `readFormData()`, or body-reading middleware) locks the stream and proxying fails. If you need to inspect the body and still proxy it, read from a clone and leave the original event untouched.
|
|
32
|
+
|
|
31
33
|
**Security:** Never pass unsanitized user input as the `target`. Callers are responsible for validating and restricting the target URL (e.g. allowlisting hosts, blocking internal paths, enforcing protocol). Consider using `bodyLimit()` middleware to prevent large request bodies from consuming excessive resources when proxying untrusted input.
|
|
34
|
+
|
|
35
|
+
**Example:**
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
app.all("/proxy", async (event) => {
|
|
39
|
+
const body = await event.req.clone().json(); // read from the clone
|
|
40
|
+
// ...inspect body...
|
|
41
|
+
return proxyRequest(event, "/target"); // original stream still intact
|
|
42
|
+
});
|
|
43
|
+
```
|
|
@@ -63,10 +63,49 @@ You can return a new Response from the handler to replace the original response.
|
|
|
63
63
|
|
|
64
64
|
Define WebSocket hooks.
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
**Example:**
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const hooks = defineWebSocket({
|
|
70
|
+
open: (peer) => peer.send("Welcome!"),
|
|
71
|
+
message: (peer, message) => peer.send(message.text()),
|
|
72
|
+
close: (peer) => console.log("closed", peer),
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `defineWebSocketHandler(http?)`
|
|
67
77
|
|
|
68
78
|
Define WebSocket event handler.
|
|
69
79
|
|
|
80
|
+
By default, non-upgrade (plain HTTP) requests receive a `426 Upgrade Required` response. Pass an `http` handler to serve those requests instead, allowing the same route to handle both WebSocket upgrades and regular HTTP requests. WebSocket upgrade requests always go to `hooks`.
|
|
81
|
+
|
|
82
|
+
Note: the `http` handler only handles non-upgrade requests. To reject or customize the upgrade handshake itself, use the crossws `upgrade` hook instead.
|
|
83
|
+
|
|
84
|
+
**Example:**
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// WebSocket-only route (non-upgrade requests get `426 Upgrade Required`)
|
|
88
|
+
app.get(
|
|
89
|
+
"/_ws",
|
|
90
|
+
defineWebSocketHandler({
|
|
91
|
+
message: (peer, message) => peer.send(message.text()),
|
|
92
|
+
}),
|
|
93
|
+
);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Example:**
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
// Handle both WebSocket upgrades and plain HTTP on the same route
|
|
100
|
+
app.get(
|
|
101
|
+
"/_ws",
|
|
102
|
+
defineWebSocketHandler(
|
|
103
|
+
{ message: (peer, message) => peer.send(message.text()) },
|
|
104
|
+
() => "Send a WebSocket upgrade request to connect.",
|
|
105
|
+
),
|
|
106
|
+
);
|
|
107
|
+
```
|
|
108
|
+
|
|
70
109
|
## Adapters
|
|
71
110
|
|
|
72
111
|
### `defineNodeHandler(handler)`
|
|
@@ -40,3 +40,9 @@ Laravel-style routing system for H3 and Express.js. Clean route definitions, mid
|
|
|
40
40
|
`unjwt` is a collection of low-level JWT utilities (JWS, JWE, JWK) built on the Web Crypto API, with zero runtime dependencies. It includes a dedicated H3 v2 adapter for header and cookie-based session management with support for encrypted (JWE) and signed (JWS) tokens.
|
|
41
41
|
|
|
42
42
|
<read-more></read-more>
|
|
43
|
+
|
|
44
|
+
## `Arkstack`
|
|
45
|
+
|
|
46
|
+
[Arkstack](https://arkstack.toneflix.net) is a runtime-agnostic TypeScript backend framework for building structured, production-ready server applications with first-class support for H3.
|
|
47
|
+
|
|
48
|
+
<read-more></read-more>
|
|
@@ -29,7 +29,7 @@ app.use(async (event) => {
|
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
> [!WARNING]
|
|
32
|
-
> You must provide a password to encrypt the session.
|
|
32
|
+
> You must provide a password to encrypt the session. The examples below use a hardcoded value so they stay readable, but in a real app load it from an environment variable such as `process.env.SESSION_PASSWORD`, keep it a strong secret of at least 32 characters, and never commit it to source control.
|
|
33
33
|
|
|
34
34
|
This will initialize a session and return an header `Set-Cookie` with a cookie named `h3` and an encrypted content.
|
|
35
35
|
|
|
@@ -128,3 +128,36 @@ app.use(async (event) => {
|
|
|
128
128
|
return session.data;
|
|
129
129
|
});
|
|
130
130
|
```
|
|
131
|
+
|
|
132
|
+
Every option is optional except `password`. The `name` option is worth calling out: it sets the cookie used to store the session and defaults to `h3`. H3 also reads the session from a request header derived from `name`, which it normalizes to lowercase as `x-${name.toLowerCase()}-session`, so the default name `h3` produces the `x-h3-session` header seen earlier. A mixed-case `name` like `MyApp` still resolves to a lowercase `x-myapp-session` header, while the cookie keeps the original casing. That default is why the earlier examples set a cookie named `h3`.
|
|
133
|
+
|
|
134
|
+
> [!NOTE]
|
|
135
|
+
> The `secure: true` option tells the browser to only store and send the cookie over HTTPS. When developing locally over plain HTTP, compliant browsers (notably Safari and iOS, and Chrome on some local domains) silently drop the cookie, so the session will not persist. Set `cookie: { secure: false }` during local development to work around this.
|
|
136
|
+
|
|
137
|
+
## Use Multiple Sessions
|
|
138
|
+
|
|
139
|
+
Because each session is stored under its own `name`, you can run several independent sessions on the same request. They live in separate cookies and never overwrite each other, which is useful for keeping unrelated concerns apart, such as a long-lived auth session and a short-lived flash message:
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
import { useSession } from "h3";
|
|
143
|
+
|
|
144
|
+
app.use(async (event) => {
|
|
145
|
+
const auth = await useSession(event, {
|
|
146
|
+
name: "auth",
|
|
147
|
+
password: "80d42cfb-1cd2-462c-8f17-e3237d9027e9",
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const flash = await useSession(event, {
|
|
151
|
+
name: "flash",
|
|
152
|
+
password: "80d42cfb-1cd2-462c-8f17-e3237d9027e9",
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
await flash.update({ message: "Saved!" });
|
|
156
|
+
|
|
157
|
+
// `auth` and `flash` are backed by different cookies, so they stay separate
|
|
158
|
+
return { user: auth.data.user, flash: flash.data.message };
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
> [!NOTE]
|
|
163
|
+
> Give each session a distinct `name`. Two sessions that share a name share the same cookie, so the last write wins.
|
|
@@ -582,6 +582,8 @@ declare class HTTPError<DataT = unknown> extends Error implements ErrorBody<Data
|
|
|
582
582
|
get statusMessage(): string | undefined;
|
|
583
583
|
toJSON(): Omit<ErrorBody, "body"> & ErrorBody["body"];
|
|
584
584
|
}
|
|
585
|
+
type H3Plugin = (h3: H3) => void;
|
|
586
|
+
declare function definePlugin<T = unknown>(def: (h3: H3, options: T) => void): undefined extends T ? (options?: T) => H3Plugin : (options: T) => H3Plugin;
|
|
585
587
|
interface RouterContext {
|
|
586
588
|
root: any;
|
|
587
589
|
static: Record<string, any>;
|
|
@@ -600,6 +602,14 @@ interface H3Config {
|
|
|
600
602
|
* When enabled, H3 console errors for unhandled exceptions will not be displayed.
|
|
601
603
|
*/
|
|
602
604
|
silent?: boolean;
|
|
605
|
+
/**
|
|
606
|
+
* By default H3 rejects requests with a malformed percent-encoded URL path
|
|
607
|
+
* (e.g. `/foo%`, `/%ZZ`) with a `400 Bad Request` before routing.
|
|
608
|
+
*
|
|
609
|
+
* When enabled, such requests are allowed through with the raw, undecoded
|
|
610
|
+
* pathname instead. Your handlers are then responsible for handling it safely.
|
|
611
|
+
*/
|
|
612
|
+
allowMalformedURL?: boolean;
|
|
603
613
|
plugins?: H3Plugin[];
|
|
604
614
|
onRequest?: (event: H3Event) => MaybePromise<void>;
|
|
605
615
|
onResponse?: (response: Response, event: H3Event) => MaybePromise<void>;
|
|
@@ -619,8 +629,6 @@ interface H3Route {
|
|
|
619
629
|
meta?: H3RouteMeta;
|
|
620
630
|
handler: EventHandler;
|
|
621
631
|
}
|
|
622
|
-
type H3Plugin = (h3: H3$1) => void;
|
|
623
|
-
declare function definePlugin<T = unknown>(def: (h3: H3$1, options: T) => void): undefined extends T ? (options?: T) => H3Plugin : (options: T) => H3Plugin;
|
|
624
632
|
type RouteOptions = {
|
|
625
633
|
middleware?: Middleware[];
|
|
626
634
|
meta?: H3RouteMeta;
|
|
@@ -815,6 +823,7 @@ declare class H3EventResponse {
|
|
|
815
823
|
get errHeaders(): Headers;
|
|
816
824
|
}
|
|
817
825
|
declare class H3Core implements H3Core$1 {
|
|
826
|
+
static "~h3": boolean;
|
|
818
827
|
readonly config: H3CoreConfig;
|
|
819
828
|
"~middleware": Middleware[];
|
|
820
829
|
"~routes": H3Route[];
|
|
@@ -830,4 +839,4 @@ declare const H3: {
|
|
|
830
839
|
new (config?: H3Config): H3$1;
|
|
831
840
|
};
|
|
832
841
|
type H3 = H3$1;
|
|
833
|
-
export {
|
|
842
|
+
export { type CookieSerializeOptions, DynamicEventHandler, ErrorBody, ErrorDetails, ErrorInput, EventHandler, EventHandlerFetch, EventHandlerObject, EventHandlerRequest, EventHandlerResponse, EventHandlerWithFetch, FetchHandler$1 as FetchHandler, FetchableObject, H3, H3$1, H3Config, H3Core, H3CoreConfig, H3Event, H3EventContext, H3Plugin, H3Route, H3RouteMeta, HTTPError, HTTPEvent, HTTPHandler, HTTPMethod, InferEventInput, LazyEventHandler, MatchedRoute, MaybePromise, Middleware, MiddlewareOptions, PreparedResponse, RouteOptions, RouterContext, Session, SessionConfig, SessionData, SessionManager, type TypedRequest, TypedServerRequest, clearSession, definePlugin, getSession, sealSession, unsealSession, updateSession, useSession };
|