@depup/h3 2.0.1-depup.0

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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/bin/h3.mjs +36 -0
  4. package/changes.json +5 -0
  5. package/dist/THIRD-PARTY-LICENSES.md +70 -0
  6. package/dist/_entries/bun.d.mts +6 -0
  7. package/dist/_entries/bun.mjs +16 -0
  8. package/dist/_entries/cloudflare.d.mts +6 -0
  9. package/dist/_entries/cloudflare.mjs +16 -0
  10. package/dist/_entries/deno.d.mts +6 -0
  11. package/dist/_entries/deno.mjs +16 -0
  12. package/dist/_entries/generic.d.mts +6 -0
  13. package/dist/_entries/generic.mjs +16 -0
  14. package/dist/_entries/node.d.mts +10 -0
  15. package/dist/_entries/node.mjs +19 -0
  16. package/dist/_entries/service-worker.d.mts +6 -0
  17. package/dist/_entries/service-worker.mjs +16 -0
  18. package/dist/_utils.mjs +240 -0
  19. package/dist/cache.mjs +599 -0
  20. package/dist/cache2.mjs +50 -0
  21. package/dist/cors.mjs +292 -0
  22. package/dist/docs/0.guide/0.index/index.md +117 -0
  23. package/dist/docs/0.guide/1.basics/0.lifecycle.md +68 -0
  24. package/dist/docs/0.guide/1.basics/1.routing.md +167 -0
  25. package/dist/docs/0.guide/1.basics/2.middleware.md +97 -0
  26. package/dist/docs/0.guide/1.basics/3.handler.md +165 -0
  27. package/dist/docs/0.guide/1.basics/4.response.md +171 -0
  28. package/dist/docs/0.guide/1.basics/5.error.md +117 -0
  29. package/dist/docs/0.guide/1.basics/6.nested-apps.md +57 -0
  30. package/dist/docs/0.guide/2.rules.md +698 -0
  31. package/dist/docs/0.guide/3.api/0.h3.md +144 -0
  32. package/dist/docs/0.guide/3.api/1.h3event.md +160 -0
  33. package/dist/docs/0.guide/4.advanced/0.plugins.md +50 -0
  34. package/dist/docs/0.guide/4.advanced/1.websocket.md +176 -0
  35. package/dist/docs/0.guide/4.advanced/2.nightly.md +13 -0
  36. package/dist/docs/1.utils/0.index/index.md +46 -0
  37. package/dist/docs/1.utils/1.request.md +447 -0
  38. package/dist/docs/1.utils/2.response.md +172 -0
  39. package/dist/docs/1.utils/3.cookie.md +33 -0
  40. package/dist/docs/1.utils/4.security.md +175 -0
  41. package/dist/docs/1.utils/5.proxy.md +57 -0
  42. package/dist/docs/1.utils/6.mcp.md +75 -0
  43. package/dist/docs/1.utils/7.more.md +117 -0
  44. package/dist/docs/1.utils/8.community.md +48 -0
  45. package/dist/docs/2.examples/0.index/index.md +17 -0
  46. package/dist/docs/2.examples/1.handle-cookie.md +67 -0
  47. package/dist/docs/2.examples/2.handle-query.md +76 -0
  48. package/dist/docs/2.examples/3.handle-session.md +210 -0
  49. package/dist/docs/2.examples/4.serve-static-assets.md +66 -0
  50. package/dist/docs/2.examples/5.stream-response.md +76 -0
  51. package/dist/docs/2.examples/6.validate-data.md +193 -0
  52. package/dist/docs/3.migration/0.index/index.md +204 -0
  53. package/dist/docs/README.md +37 -0
  54. package/dist/h3.d.mts +1669 -0
  55. package/dist/h3.mjs +1809 -0
  56. package/dist/index.d.mts +1634 -0
  57. package/dist/match.d.mts +123 -0
  58. package/dist/middleware.mjs +123 -0
  59. package/dist/normalize.mjs +645 -0
  60. package/dist/path.mjs +42 -0
  61. package/dist/proxy.mjs +254 -0
  62. package/dist/response.mjs +465 -0
  63. package/dist/rules/cache.d.mts +29 -0
  64. package/dist/rules/cache.mjs +163 -0
  65. package/dist/rules/compiler.d.mts +94 -0
  66. package/dist/rules/compiler.mjs +173 -0
  67. package/dist/rules/index.d.mts +77 -0
  68. package/dist/rules/index.mjs +34 -0
  69. package/dist/rules/proxy.d.mts +3 -0
  70. package/dist/rules/proxy.mjs +14 -0
  71. package/dist/tracing.d.mts +33 -0
  72. package/dist/tracing.mjs +89 -0
  73. package/package.json +148 -0
@@ -0,0 +1,144 @@
1
+ # H3
2
+
3
+ > H3 class is the core of server.
4
+
5
+ You can create a new H3 app instance using `new H3()`:
6
+
7
+ ```js
8
+ import { H3 } from "h3";
9
+
10
+ const app = new H3({/* optional config */});
11
+ ```
12
+
13
+ ## `H3` Methods
14
+
15
+ ### `H3.request`
16
+
17
+ A [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)-compatible function allowing to fetch app routes.
18
+
19
+ - Input can be a relative path, [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL), or [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request).
20
+ - Returned value is a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) promise.
21
+
22
+ ```ts
23
+ const response = await app.request("/");
24
+ console.log(response, await response.text());
25
+ ```
26
+
27
+ ### `H3.fetch`
28
+ Similar to `H3.request` but only accepts one `(req: Request)` argument for cross runtime compatibility.
29
+
30
+ ### `H3.on`
31
+
32
+ Register route handler for specific HTTP method.
33
+
34
+ ```js
35
+ const app = new H3().on("GET", "/", () => "OK");
36
+ ```
37
+
38
+ <read-more></read-more>
39
+
40
+ ### `H3.[method]`
41
+
42
+ Register route handler for specific HTTP method (shortcut for `app.on(method, ...)`).
43
+
44
+ ```js
45
+ const app = new H3().get("/", () => "OK");
46
+ ```
47
+
48
+ ### `H3.all`
49
+
50
+ Register route handler for all HTTP methods.
51
+
52
+ ```js
53
+ const app = new H3().all("/", () => "OK");
54
+ ```
55
+
56
+ ### `H3.use`
57
+
58
+ Register a global [middleware](/guide/basics/middleware).
59
+
60
+ ```js
61
+ const app = new H3()
62
+ .use((event) => {
63
+ console.log(`request: ${event.req.url}`);
64
+ })
65
+ .all("/", () => "OK");
66
+ ```
67
+
68
+ <read-more></read-more>
69
+
70
+ ### `H3.register`
71
+
72
+ Register a H3 plugin to extend app.
73
+
74
+ <read-more></read-more>
75
+
76
+ ### `H3.handler`
77
+
78
+ An H3 [event handler](/guide/basics/handler) useful to compose multiple H3 app instances.
79
+
80
+ **Example:** Nested apps.
81
+
82
+ ```js
83
+ import { H3, serve, redirect, withBase } from "h3";
84
+
85
+ const nestedApp = new H3().get("/test", () => "/test (sub app)");
86
+
87
+ const app = new H3()
88
+ .get("/", (event) => redirect(event, "/api/test"))
89
+ .all("/api/**", withBase("/api", nestedApp.handler));
90
+
91
+ serve(app);
92
+ ```
93
+
94
+ ### `H3.mount`
95
+
96
+ Using `.mount` method, you can register a sub-app with prefix.
97
+
98
+ <read-more></read-more>
99
+
100
+ ## `H3` Options
101
+
102
+ You can pass global app configuration when initializing an app.
103
+
104
+ Supported options:
105
+
106
+ - `debug`: Displays debugging stack traces in HTTP responses (potentially dangerous for production!).
107
+ - `silent`: When enabled, console errors for unhandled exceptions will not be displayed.
108
+ - `allowMalformedURL`: When enabled, requests with a malformed percent-encoded URL path (e.g. `/foo%`, `/%ZZ`) are allowed through with the raw pathname instead of being rejected with a `400 Bad Request` (the default).
109
+ - `plugins`: (see [plugins](/guide/advanced/plugins) for more information)
110
+
111
+ > [!IMPORTANT]
112
+ Enabling `debug` option, sends important stuff like stack traces in error responses. Only enable during development.
113
+
114
+ ### Global Hooks
115
+
116
+ When initializing an H3 app, you can register global hooks:
117
+
118
+ - `onError`
119
+ - `onRequest`
120
+ - `onResponse`
121
+ These hooks are called for every request and can be used to add global logic to your app such as logging, error handling, etc.
122
+
123
+ ```js
124
+ const app = new H3({
125
+ onRequest: (event) => {
126
+ console.log("Request:", event.req.url);
127
+ },
128
+ onResponse: (response, event) => {
129
+ console.log("Response:", event.url.pathname, response.status);
130
+ },
131
+ onError: (error, event) => {
132
+ console.error(error);
133
+ },
134
+ });
135
+ ```
136
+
137
+ > [!IMPORTANT]
138
+ > Global hooks only run from main H3 app and **not** sub-apps. Use [middleware](/guide/basics/middleware) for more flexibility.
139
+
140
+ ## `H3` Properties
141
+
142
+ ### `H3.config`
143
+
144
+ Global H3 instance config.
@@ -0,0 +1,160 @@
1
+ # H3Event
2
+
3
+ > H3Event, carries incoming request, prepared response and context.
4
+
5
+ With each HTTP request, H3 internally creates an `H3Event` object and passes it though event handlers until sending the response.
6
+
7
+ <read-more></read-more>
8
+
9
+ An event is passed through all the lifecycle hooks and composable utils to use it as context.
10
+
11
+ **Example:**
12
+
13
+ ```js
14
+ app.get("/", async (event) => {
15
+ // Log HTTP request
16
+ console.log(`[${event.req.method}] ${event.req.url}`);
17
+
18
+ // Parsed URL and query params
19
+ const searchParams = event.url.searchParams;
20
+
21
+ // Try to read request JSON body
22
+ const jsonBody = await event.req.json().catch(() => {});
23
+
24
+ return "OK";
25
+ });
26
+ ```
27
+
28
+ ## `H3Event` Methods
29
+
30
+ ### `H3Event.waitUntil`
31
+
32
+ Tell the runtime about an ongoing operation that shouldn't close until the promise resolves.
33
+
34
+ ```js [app.mjs]
35
+ import { logRequest } from "./tracing.mjs";
36
+
37
+ app.get("/", (event) => {
38
+ request.waitUntil(logRequest(request));
39
+ return "OK";
40
+ });
41
+ ```
42
+
43
+ ```js [tracing.mjs]
44
+ export async function logRequest(request) {
45
+ await fetch("https://telemetry.example.com", {
46
+ method: "POST",
47
+ body: JSON.stringify({
48
+ method: request.method,
49
+ url: request.url,
50
+ ip: request.ip,
51
+ }),
52
+ });
53
+ }
54
+ ```
55
+
56
+ > [!TIP]
57
+ > To release per-request resources (timers, upstream connections, file handles) once the event is fully over — on every runtime — use the [`onDispose(event, cb)`](/utils/response#ondisposeevent-cb) utility.
58
+
59
+ ## `H3Event` Properties
60
+
61
+ ### `H3Event.app?`
62
+
63
+ Access to the H3 [application instance](/guide/api/h3).
64
+
65
+ ### `H3Event.context`
66
+
67
+ The context is an object that contains arbitrary information about the request.
68
+
69
+ You can store your custom properties inside `event.context` to share across utils.
70
+
71
+ **Known context keys:**
72
+
73
+ - `context.params`: Matched router parameters.
74
+ - `middlewareParams`: Matched middleware parameters
75
+ - `matchedRoute`: Matched router route object.
76
+ - `sessions`: Cached session data.
77
+ - `basicAuth`: Basic authentication data.
78
+
79
+ ### `H3Event.req`
80
+ Incoming HTTP request info based on native [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) with additional runtime addons (see [srvx docs](https://srvx.h3.dev/guide/handler#extended-request-context)).
81
+
82
+ ```ts
83
+ app.get("/", async (event) => {
84
+ const url = event.req.url;
85
+ const method = event.req.method;
86
+ const headers = event.req.headers;
87
+
88
+ // (note: you can consume body only once with either of this)
89
+ const bodyStream = await event.req.body;
90
+ const textBody = await event.req.text();
91
+ const jsonBody = await event.req.json();
92
+ const formDataBody = await event.req.formData();
93
+
94
+ return "OK";
95
+ });
96
+ ```
97
+
98
+ ### `H3Event.url`
99
+
100
+ Access to the full parsed request [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL).
101
+
102
+ ```ts
103
+ app.get("/", (event) => {
104
+ const { pathname, search, searchParams } = event.url;
105
+
106
+ return "OK";
107
+ });
108
+ ```
109
+
110
+ #### Pathname encoding
111
+
112
+ `event.url.pathname` is the path in its wire encoding, with one exception: an escape that is <u>needlessly</u> there is dropped.
113
+
114
+ An escape is needless when every decoding consumer — a proxy, a filesystem lookup, a handler calling `decodeURIComponent` — reads it as its literal, while H3's matchers compare the two as different strings. Left alone, that gap lets `/%61dmin` slip past an `/admin` guard and still reach the `/admin` route, or reach a catch-all handler that decodes it downstream. So H3 decodes exactly those escapes, once, before routing. Nothing else is touched:
115
+
116
+ | Request | `event.url.pathname` | Why |
117
+ | --- | --- | --- |
118
+ | `/%61dmin` | `/admin` | Unreserved escape, needlessly encoded |
119
+ | `/a%2eb` | `/a.b` | Unreserved escape, needlessly encoded |
120
+ | `/a%21b` | `/a!b` | Decodes downstream, needlessly encoded |
121
+ | `/%40handle` | `/@handle` | Decodes downstream, needlessly encoded |
122
+ | `/a/%2e%2e/b` | `/b` | The URL parser resolved the dot segment first |
123
+ | `/x%2fy` | `/x%2fy` | Separator, must stay encoded |
124
+ | `/x%5cy` | `/x%5cy` | Separator, must stay encoded |
125
+ | `/100%25` | `/100%25` | Decoding it would expose a nested escape |
126
+ | `/a%20b` | `/a%20b` | The URL serializer re-encodes a space anyway |
127
+ | `/caf%C3%A9` | `/caf%C3%A9` | The URL serializer re-encodes non-ASCII anyway |
128
+
129
+ The decoded set is every escape whose character survives WHATWG path serialization unchanged, minus `%2f` and `%25`: the [RFC 3986 §2.3](https://www.rfc-editor.org/rfc/rfc3986#section-2.3) unreserved set (`ALPHA` / `DIGIT` / `-` / `.` / `_` / `~`), which is equivalent to its literal per [§6.2.2.2](https://www.rfc-editor.org/rfc/rfc3986#section-6.2.2.2), plus `!`, `$`, `&`, `'`, `(`, `)`, `*`, `+`, `,`, `:`, `;`, `=`, `@`, `[`, `]` and `|`. Anything else is left alone because decoding it cannot survive the round trip (`%20`, `%5E`, `%7B`, non-ASCII), would change how many segments the path has (`%2f`, `%5c`), would delete the character outright (`%09` and the other C0 controls), or would expose a nested escape (`%25`).
130
+
131
+ That is wider than [`decodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURI), which preserves all of RFC 3986's reserved set (`; / ? : @ & = + $ ,`) — of which only `/` is structural in an already-parsed path. Routes like `/@handle` or `/resource:action` are ordinary, so a guard protecting one must not be walkable past by its escaped spelling (`/%40handle`).
132
+
133
+ Route matching, `use()` matchers and your own `event.url.pathname` checks therefore all compare one and the same string. `event.req.url` always keeps the original wire encoding, so for a non-canonical path the two disagree: read the path from `event.url`, and never re-derive it from `event.req.url` — slicing one by an offset taken from the other is how mount prefixes and proxy targets desync.
134
+
135
+ Canonicalization happens in the `H3Event` constructor, so it covers every event, including one built by `mockEvent()` or a standalone `handler.fetch()`.
136
+
137
+ Every escape that survives is **opaque** — treat it that way:
138
+
139
+ > [!WARNING]
140
+ > Never decode `event.url.pathname` yourself. Decoding can reintroduce a `/` or `..` that routing and middleware never saw, which is a path traversal vector when the value reaches a filesystem or an upstream URL. To read a route param in decoded form, use [`getRouterParams(event, { decode: true })`](/utils/request#getrouterparamsevent-opts-decode), which decodes everything else but keeps encoded separators encoded. To canonicalize a path for a scope check, use [`resolveDotSegments`](/utils/security#resolvedotsegments).
141
+
142
+ A route param therefore can never contain a path separator the router did not match on: `%2f` and `%5c` stay encoded, so `/a%2fb` and `/a%5cb` are one segment (matching the route `/:id`, not `/a/:id`).
143
+
144
+ Requests with malformed percent-encoding (such as `/foo%` or `/%ZZ`) have no canonical form to decode to and are rejected with a `400 Bad Request` before any handler runs. Set the [`allowMalformedURL`](/guide/api/h3#h3-options) app option to receive the raw pathname instead.
145
+
146
+ ### `H3Event.res`
147
+
148
+ Prepared HTTP response status and headers.
149
+
150
+ ```ts
151
+ app.get("/", (event) => {
152
+ event.res.status = 200;
153
+ event.res.statusText = "OK";
154
+ event.res.headers.set("x-test", "works");
155
+
156
+ return "OK";
157
+ });
158
+ ```
159
+
160
+ <read-more></read-more>
@@ -0,0 +1,50 @@
1
+ # Plugins
2
+
3
+ > H3 plugins allow you to extend an H3 app instance with reusable logic.
4
+
5
+ ## Register Plugins
6
+
7
+ Plugins can be registered either when creating a new [H3 instance](/guide/api/h3) or by using [H3.register](/guide/api/h3#h3register).
8
+
9
+ ```js
10
+ import { H3 } from "h3";
11
+ import { logger } from "./logger.mjs";
12
+
13
+ // Using instance config
14
+ const app = new H3({
15
+ plugins: [logger()],
16
+ });
17
+
18
+ // Or register later
19
+ app.register(logger());
20
+
21
+ // ... rest of the code..
22
+ app.get("/**", () => "Hello, World!");
23
+ ```
24
+
25
+ > [!NOTE]
26
+ > Plugins are always registered immediately. Therefore, the order in which they are used might be important depending on the plugin's functionality.
27
+
28
+ ## Creating Plugins
29
+
30
+ H3 plugins are simply functions that accept an [H3 instance](/guide/api/h3) as the first argument and immediately apply logic to extend it.
31
+
32
+ ```js
33
+ app.register((app) => {
34
+ app.use(...)
35
+ })
36
+ ```
37
+
38
+ For convenience, H3 provides a built-in `definePlugin` utility, which creates a typed factory function with optional plugin-specific options.
39
+
40
+ ```js
41
+ import { definePlugin } from "h3";
42
+
43
+ const logger = definePlugin((h3, _options) => {
44
+ if (h3.config.debug) {
45
+ h3.use((req) => {
46
+ console.log(`[${req.method}] ${req.url}`);
47
+ });
48
+ }
49
+ });
50
+ ```
@@ -0,0 +1,176 @@
1
+ # WebSockets
2
+
3
+ > H3 has built-in utilities for cross platform WebSocket and Server-Sent Events.
4
+
5
+ You can add cross platform WebSocket support to H3 servers using [🔌 CrossWS](https://crossws.h3.dev/).
6
+
7
+ ## Usage
8
+
9
+ WebSocket handlers can be defined using the `defineWebSocketHandler()` utility and registered to any route like event handlers.
10
+
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.
12
+
13
+ ```js
14
+ import { H3, serve, defineWebSocketHandler } from "h3";
15
+
16
+ import { plugin as ws } from "crossws/server";
17
+
18
+ const app = new H3();
19
+
20
+ app.get("/_ws", defineWebSocketHandler({ message: console.log }));
21
+
22
+ serve(app, {
23
+ plugins: [ws()],
24
+ });
25
+ ```
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
+ `defineWebSocketHandler()` attaches the hooks to the **request** (CrossWS reads them back with `getWebSocketHooks(request)`, keyed by `Symbol.for("crossws.hooks")`), and answers the upgrade request with `426 Upgrade Required` for anything that is not a WebSocket client.
31
+
32
+ They are attached to the request rather than to that response because a `Response` gets rebuilt on its way out of an app — merging a header staged by a [route rule](/guide/rules) or CORS middleware, or any middleware doing `new Response(res.body, res)` — and a rebuilt response carries none of the original's own properties. The response also exposes the hooks as `res.crossws` for convenience, but only when nothing rebuilt it; if you write a custom `resolve`, read the request instead:
33
+
34
+ ```js
35
+ import { getWebSocketHooks } from "crossws";
36
+
37
+ serve(app, {
38
+ plugins: [ws({ resolve: (req) => app.fetch(req).then(() => getWebSocketHooks(req)) })],
39
+ });
40
+ ```
41
+
42
+ **Full example:**
43
+
44
+ ```js [websocket.mjs]
45
+ import { H3, serve, html, defineWebSocketHandler } from "h3";
46
+ import { plugin as ws } from "crossws/server";
47
+
48
+ export const app = new H3();
49
+
50
+ // A minimal self-contained WebSocket playground served for plain HTTP requests.
51
+ const playground = html`<!doctype html>
52
+ <title>H3 WebSocket Playground</title>
53
+ <h1>H3 WebSocket Playground</h1>
54
+ <form id="form">
55
+ <input id="input" placeholder="Type a message..." autocomplete="off" autofocus />
56
+ <button type="submit">Send</button>
57
+ </form>
58
+ <div id="log"></div>
59
+ <script type="module">
60
+ const log = (msg) => {
61
+ const line = document.createElement("div");
62
+ line.textContent = msg;
63
+ document.getElementById("log").append(line);
64
+ };
65
+ const url = location.href.replace(/^http/, "ws");
66
+ const ws = new WebSocket(url);
67
+ ws.addEventListener("open", () => log("[open] connected to " + url));
68
+ ws.addEventListener("message", (e) => log("[message] " + e.data));
69
+ ws.addEventListener("close", () => log("[close] disconnected"));
70
+ document.getElementById("form").addEventListener("submit", (e) => {
71
+ e.preventDefault();
72
+ const input = document.getElementById("input");
73
+ ws.send(input.value);
74
+ input.value = "";
75
+ });
76
+ </script>`;
77
+
78
+ // A single route serves both the playground page (plain HTTP) and the
79
+ // WebSocket endpoint (upgrade requests). The page connects back to itself.
80
+ app.get(
81
+ "/",
82
+ defineWebSocketHandler(
83
+ {
84
+ open(peer) {
85
+ console.log("[open]", peer);
86
+
87
+ // Send welcome to the new client
88
+ peer.send("Welcome to the server!");
89
+
90
+ // Join new client to the "chat" channel
91
+ peer.subscribe("chat");
92
+
93
+ // Notify every other connected client
94
+ peer.publish("chat", `[system] ${peer} joined!`);
95
+ },
96
+
97
+ message(peer, message) {
98
+ console.log("[message]", peer);
99
+
100
+ if (message.text() === "ping") {
101
+ // Reply to the client with a ping response
102
+ peer.send("pong");
103
+ return;
104
+ }
105
+
106
+ // The server re-broadcasts incoming messages to everyone
107
+ peer.publish("chat", `[${peer}] ${message}`);
108
+
109
+ // Echo the message back to the sender
110
+ peer.send(message);
111
+ },
112
+
113
+ close(peer) {
114
+ console.log("[close]", peer);
115
+ peer.publish("chat", `[system] ${peer} has left the chat!`);
116
+ peer.unsubscribe("chat");
117
+ },
118
+ },
119
+ // Non-upgrade requests get the playground page.
120
+ () => playground,
121
+ ),
122
+ );
123
+
124
+ serve(app, {
125
+ plugins: [ws()],
126
+ });
127
+ ```
128
+
129
+ ### Handling HTTP requests
130
+
131
+ By default, a WebSocket route responds with `426 Upgrade Required` to any request that is not a WebSocket upgrade.
132
+
133
+ 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.
134
+
135
+ ```js
136
+ app.get(
137
+ "/_ws",
138
+ defineWebSocketHandler(
139
+ { message: (peer, message) => peer.send(message.text()) },
140
+ () => "Send a WebSocket upgrade request to connect.",
141
+ ),
142
+ );
143
+ ```
144
+
145
+ ## Server-Sent Events (SSE)
146
+
147
+ As an alternative to WebSockets, you can use [Server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
148
+
149
+ H3 has a built-in `EventStream` class to create server-sent events. Construct it directly with `new EventStream(event)` and return it from a handler.
150
+
151
+ ### Example
152
+
153
+ ```js [server-sent-events.mjs]
154
+ import { H3, serve, EventStream } from "h3";
155
+
156
+ export const app = new H3();
157
+
158
+ app.get("/", (event) => {
159
+ const eventStream = new EventStream(event);
160
+
161
+ // Send a message every second
162
+ const interval = setInterval(async () => {
163
+ await eventStream.push("Hello world");
164
+ }, 1000);
165
+
166
+ // cleanup the interval when the connection is terminated or the writer is closed
167
+ eventStream.onClosed(() => {
168
+ console.log("Connection closed");
169
+ clearInterval(interval);
170
+ });
171
+
172
+ return eventStream;
173
+ });
174
+
175
+ serve(app);
176
+ ```
@@ -0,0 +1,13 @@
1
+ # Nightly Builds
2
+
3
+ You can opt-in to early test latest H3 changes using automated nightly release channel.
4
+
5
+ If you are directly using `h3` as a dependency in your project:
6
+
7
+ ```json
8
+ {
9
+ "dependencies": {
10
+ "h3": "npm:h3-nightly@latest"
11
+ }
12
+ }
13
+ ```
@@ -0,0 +1,46 @@
1
+ # H3 Utils
2
+
3
+ H3 is a composable framework. Instead of providing a big core, you start with a lightweight [H3](/guide/api/h3) instance and for every functionality, there is either a built-in utility or you can make yours.
4
+
5
+ <card-group>
6
+
7
+ <card>
8
+
9
+ Utilities for incoming request.
10
+ </card>
11
+
12
+ <card>
13
+
14
+ Utilities for preparing and sending response.
15
+ </card>
16
+
17
+ <card>
18
+
19
+ Cookie utilities.
20
+ </card>
21
+
22
+ <card>
23
+
24
+ Security utilities.
25
+ </card>
26
+
27
+ <card>
28
+
29
+ Proxy utilities.
30
+ </card>
31
+
32
+ <card>
33
+
34
+ MCP related utilities.
35
+ </card>
36
+
37
+ <card>
38
+
39
+ More Utilities.
40
+ </card>
41
+
42
+ <card>
43
+
44
+ Community made utilities.
45
+ </card>
46
+ </card-group>