h3 0.7.20 → 0.8.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.
- package/README.md +37 -53
- package/dist/index.cjs +147 -143
- package/dist/index.d.ts +80 -103
- package/dist/index.mjs +142 -131
- package/package.json +17 -16
package/README.md
CHANGED
|
@@ -13,9 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
✔️ **Portable:** Works perfectly in Serverless, Workers, and Node.js
|
|
15
15
|
|
|
16
|
-
✔️ **
|
|
17
|
-
|
|
18
|
-
✔️ **Minimal:** Small, tree-shakable and zero-dependency
|
|
16
|
+
✔️ **Minimal:** Small and tree-shakable
|
|
19
17
|
|
|
20
18
|
✔️ **Modern:** Native promise support
|
|
21
19
|
|
|
@@ -23,6 +21,8 @@
|
|
|
23
21
|
|
|
24
22
|
✔️ **Router:** Super fast route matching using [unjs/radix3](https://github.com/unjs/radix3)
|
|
25
23
|
|
|
24
|
+
✔️ **Compatible:** Compatibility layer with node/connect/express middleware
|
|
25
|
+
|
|
26
26
|
## Install
|
|
27
27
|
|
|
28
28
|
```bash
|
|
@@ -40,25 +40,25 @@ pnpm add h3
|
|
|
40
40
|
|
|
41
41
|
```ts
|
|
42
42
|
import { createServer } from 'http'
|
|
43
|
-
import { createApp } from 'h3'
|
|
43
|
+
import { createApp, eventHandler } from 'h3'
|
|
44
44
|
|
|
45
45
|
const app = createApp()
|
|
46
|
-
app.use('/', () => 'Hello world!')
|
|
46
|
+
app.use('/', eventHandler(() => 'Hello world!'))
|
|
47
47
|
|
|
48
|
-
createServer(app).listen(process.env.PORT || 3000)
|
|
48
|
+
createServer(toNodeListener(app)).listen(process.env.PORT || 3000)
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
<details>
|
|
52
52
|
<summary>Example using <a href="https://github.com/unjs/listhen">listhen</a> for an elegant listener.</summary>
|
|
53
53
|
|
|
54
54
|
```ts
|
|
55
|
-
import { createApp } from 'h3'
|
|
55
|
+
import { createApp, toNodeListener } from 'h3'
|
|
56
56
|
import { listen } from 'listhen'
|
|
57
57
|
|
|
58
58
|
const app = createApp()
|
|
59
|
-
app.use('/', () => 'Hello world!')
|
|
59
|
+
app.use('/', eventHandler(() => 'Hello world!'))
|
|
60
60
|
|
|
61
|
-
listen(app)
|
|
61
|
+
listen(toNodeListener(app))
|
|
62
62
|
```
|
|
63
63
|
</details>
|
|
64
64
|
|
|
@@ -69,13 +69,13 @@ The `app` instance created by `h3` uses a middleware stack (see [how it works](#
|
|
|
69
69
|
To opt-in using a more advanced and convenient routing system, we can create a router instance and register it to app instance.
|
|
70
70
|
|
|
71
71
|
```ts
|
|
72
|
-
import { createApp, createRouter } from 'h3'
|
|
72
|
+
import { createApp, eventHandler, createRouter } from 'h3'
|
|
73
73
|
|
|
74
74
|
const app = createApp()
|
|
75
75
|
|
|
76
76
|
const router = createRouter()
|
|
77
|
-
.get('/', () => 'Hello World!')
|
|
78
|
-
.get('/hello/:name',
|
|
77
|
+
.get('/', eventHandler(() => 'Hello World!'))
|
|
78
|
+
.get('/hello/:name', eventHandler(event => `Hello ${event.context.params.name}!`))
|
|
79
79
|
|
|
80
80
|
app.use(router)
|
|
81
81
|
```
|
|
@@ -84,61 +84,58 @@ app.use(router)
|
|
|
84
84
|
|
|
85
85
|
Routes are internally stored in a [Radix Tree](https://en.wikipedia.org/wiki/Radix_tree) and matched using [unjs/radix3](https://github.com/unjs/radix3).
|
|
86
86
|
|
|
87
|
-
## More usage examples
|
|
87
|
+
## More app usage examples
|
|
88
88
|
|
|
89
89
|
```js
|
|
90
90
|
// Handle can directly return object or Promise<object> for JSON response
|
|
91
|
-
app.use('/api', (
|
|
91
|
+
app.use('/api', eventHandler((event) => ({ url: event.req.url }))
|
|
92
92
|
|
|
93
93
|
// We can have better matching other than quick prefix match
|
|
94
|
-
app.use('/odd', () => 'Is odd!', { match: url => url.substr(1) % 2 })
|
|
94
|
+
app.use('/odd', eventHandler(() => 'Is odd!'), { match: url => url.substr(1) % 2 })
|
|
95
95
|
|
|
96
96
|
// Handle can directly return string for HTML response
|
|
97
|
-
app.use(() => '<h1>Hello world!</h1>')
|
|
97
|
+
app.use(eventHandler(() => '<h1>Hello world!</h1>'))
|
|
98
98
|
|
|
99
99
|
// We can chain calls to .use()
|
|
100
|
-
app.use('/1', () => '<h1>Hello world!</h1>')
|
|
101
|
-
.use('/2', () => '<h1>Goodbye!</h1>')
|
|
100
|
+
app.use('/1', eventHandler(() => '<h1>Hello world!</h1>'))
|
|
101
|
+
.use('/2', eventHandler(() => '<h1>Goodbye!</h1>'))
|
|
102
102
|
|
|
103
103
|
// Legacy middleware with 3rd argument are automatically promisified
|
|
104
|
-
app.use((req, res, next) => { req.setHeader('X-Foo', 'bar'); next() })
|
|
105
|
-
|
|
106
|
-
// Force promisify a legacy middleware
|
|
107
|
-
// app.use(someMiddleware, { promisify: true })
|
|
104
|
+
app.use(fromNodeMiddleware((req, res, next) => { req.setHeader('X-Foo', 'bar'); next() }))
|
|
108
105
|
|
|
109
106
|
// Lazy loaded routes using { lazy: true }
|
|
110
|
-
|
|
107
|
+
app.use('/big', () => import('./big-handler'), { lazy: true })
|
|
111
108
|
```
|
|
112
109
|
|
|
113
110
|
## Utilities
|
|
114
111
|
|
|
115
|
-
|
|
112
|
+
H3 has concept of compasable utilities that accept `event` (from `eventHandler((event) => {})`) as their first argument. This has several performance benefits over injecting them to `event` or `app` instances and global middleware commonly used in Node.js frameworks such as Express, which Only required code is evaluated and bundled and rest of utils can be tree-shaken when not used.
|
|
116
113
|
|
|
117
|
-
|
|
114
|
+
### Built-in
|
|
118
115
|
|
|
119
|
-
- `useRawBody(
|
|
120
|
-
- `useBody(
|
|
121
|
-
- `useCookies(
|
|
122
|
-
- `useCookie(
|
|
123
|
-
- `setCookie(
|
|
124
|
-
- `deleteCookie(
|
|
125
|
-
- `useQuery(
|
|
116
|
+
- `useRawBody(event, encoding?)`
|
|
117
|
+
- `useBody(event)`
|
|
118
|
+
- `useCookies(event)`
|
|
119
|
+
- `useCookie(event, name)`
|
|
120
|
+
- `setCookie(event, name, value, opts?)`
|
|
121
|
+
- `deleteCookie(event, name, opts?)`
|
|
122
|
+
- `useQuery(event)`
|
|
126
123
|
- `getRouterParams(event)`
|
|
127
|
-
- `send(
|
|
128
|
-
- `sendRedirect(
|
|
124
|
+
- `send(event, data, type?)`
|
|
125
|
+
- `sendRedirect(event, location, code=302)`
|
|
129
126
|
- `getRequestHeaders(event, headers)` (alias: `getHeaders`)
|
|
130
127
|
- `getRequestHeader(event, name)` (alias: `getHeader`)
|
|
131
128
|
- `setResponseHeaders(event, headers)` (alias: `setHeaders`)
|
|
132
129
|
- `setResponseHeader(event, name, value)` (alias: `setHeader`)
|
|
133
130
|
- `appendResponseHeaders(event, headers)` (alias: `appendHeaders`)
|
|
134
131
|
- `appendResponseHeader(event, name, value)` (alias: `appendHeader`)
|
|
132
|
+
- `writeEarlyHints(event, links, callback)`
|
|
133
|
+
- `sendStream(event, data)`
|
|
134
|
+
- `sendError(event, error, debug?)`
|
|
135
|
+
- `useMethod(event, default?)`
|
|
136
|
+
- `isMethod(event, expected, allowHead?)`
|
|
137
|
+
- `assertMethod(event, expected, allowHead?)`
|
|
135
138
|
- `createError({ statusCode, statusMessage, data? })`
|
|
136
|
-
- `sendError(res, error, debug?)`
|
|
137
|
-
- `defineHandle(handle)`
|
|
138
|
-
- `defineMiddleware(middlware)`
|
|
139
|
-
- `useMethod(req, default?)`
|
|
140
|
-
- `isMethod(req, expected, allowHead?)`
|
|
141
|
-
- `assertMethod(req, expected, allowHead?)`
|
|
142
139
|
|
|
143
140
|
👉 You can learn more about usage in [JSDocs Documentation](https://www.jsdocs.io/package/h3#package-functions).
|
|
144
141
|
|
|
@@ -149,19 +146,6 @@ More composable utilities can be found in community packages.
|
|
|
149
146
|
- `validateBody(event, schema)` from [h3-typebox](https://github.com/kevinmarrec/h3-typebox)
|
|
150
147
|
- `validateQuery(event, schema)` from [h3-typebox](https://github.com/kevinmarrec/h3-typebox)
|
|
151
148
|
|
|
152
|
-
## How it works?
|
|
153
|
-
|
|
154
|
-
Using `createApp`, it returns a standard `(req, res)` handler function and internally an array called middleware stack. using`use()` method we can add an item to this internal stack.
|
|
155
|
-
|
|
156
|
-
When a request comes, each stack item that matches the route will be called and resolved until [`res.writableEnded`](https://nodejs.org/api/http.html#http_response_writableended) flag is set, which means the response is sent. If `writableEnded` is not set after all middleware, a `404` error will be thrown. And if one of the stack items resolves to a value, it will be serialized and sent as response as a shorthand method to sending responses.
|
|
157
|
-
|
|
158
|
-
For maximum compatibility with connect/express middleware (`req, res, next?` signature), h3 converts classic middleware into a promisified version ready to use with stack runner:
|
|
159
|
-
|
|
160
|
-
- If middleware has 3rd next/callback param, the promise will `resolve/reject` when called
|
|
161
|
-
- If middleware returns a promise, it will be **chained** to the main promise
|
|
162
|
-
- If calling middleware throws an immediate error, the promise will be rejected
|
|
163
|
-
- On `close` and `error` events of res, the promise will `resolve/reject` (to ensure if middleware simply calls `res.end`)
|
|
164
|
-
|
|
165
149
|
## License
|
|
166
150
|
|
|
167
151
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
const ufo = require('ufo');
|
|
4
|
+
const radix3 = require('radix3');
|
|
6
5
|
const destr = require('destr');
|
|
7
6
|
const cookieEs = require('cookie-es');
|
|
8
|
-
const radix3 = require('radix3');
|
|
9
7
|
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
function useBase(base, handler) {
|
|
9
|
+
base = ufo.withoutTrailingSlash(base);
|
|
10
|
+
if (!base) {
|
|
11
|
+
return handler;
|
|
12
|
+
}
|
|
13
|
+
return eventHandler((event) => {
|
|
14
|
+
event.req.originalUrl = event.req.originalUrl || event.req.url || "/";
|
|
15
|
+
event.req.url = ufo.withoutBase(event.req.url || "/", base);
|
|
16
|
+
return handler(event);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
13
19
|
|
|
14
20
|
class H3Error extends Error {
|
|
15
21
|
constructor() {
|
|
@@ -17,7 +23,7 @@ class H3Error extends Error {
|
|
|
17
23
|
this.statusCode = 500;
|
|
18
24
|
this.fatal = false;
|
|
19
25
|
this.unhandled = false;
|
|
20
|
-
this.statusMessage =
|
|
26
|
+
this.statusMessage = void 0;
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
29
|
H3Error.__h3_error__ = true;
|
|
@@ -75,8 +81,13 @@ function sendError(event, error, debug) {
|
|
|
75
81
|
if (event.res.writableEnded) {
|
|
76
82
|
return;
|
|
77
83
|
}
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
const _code = parseInt(h3Error.statusCode);
|
|
85
|
+
if (_code) {
|
|
86
|
+
event.res.statusCode = _code;
|
|
87
|
+
}
|
|
88
|
+
if (h3Error.statusMessage) {
|
|
89
|
+
event.res.statusMessage = h3Error.statusMessage;
|
|
90
|
+
}
|
|
80
91
|
event.res.setHeader("Content-Type", MIMES.json);
|
|
81
92
|
event.res.end(JSON.stringify(responseBody, null, 2));
|
|
82
93
|
}
|
|
@@ -132,8 +143,8 @@ function getRequestHeader(event, name) {
|
|
|
132
143
|
}
|
|
133
144
|
const getHeader = getRequestHeader;
|
|
134
145
|
|
|
135
|
-
const RawBodySymbol = Symbol("h3RawBody");
|
|
136
|
-
const ParsedBodySymbol = Symbol("
|
|
146
|
+
const RawBodySymbol = Symbol.for("h3RawBody");
|
|
147
|
+
const ParsedBodySymbol = Symbol.for("h3ParsedBody");
|
|
137
148
|
const PayloadMethods = ["PATCH", "POST", "PUT", "DELETE"];
|
|
138
149
|
function readRawBody(event, encoding = "utf-8") {
|
|
139
150
|
assertMethod(event, PayloadMethods);
|
|
@@ -166,7 +177,7 @@ async function readBody(event) {
|
|
|
166
177
|
const parsedForm = Object.fromEntries(new URLSearchParams(body));
|
|
167
178
|
return parsedForm;
|
|
168
179
|
}
|
|
169
|
-
const json =
|
|
180
|
+
const json = destr(body);
|
|
170
181
|
event.req[ParsedBodySymbol] = json;
|
|
171
182
|
return json;
|
|
172
183
|
}
|
|
@@ -227,14 +238,10 @@ function defaultContentType(event, type) {
|
|
|
227
238
|
}
|
|
228
239
|
}
|
|
229
240
|
function sendRedirect(event, location, code = 302) {
|
|
230
|
-
const encodedLoc = encodeURI(decodeURI(location));
|
|
231
241
|
event.res.statusCode = code;
|
|
232
|
-
event.res.setHeader("Location",
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
<head><meta http-equiv="refresh" content="0; url=${encodedLoc}"></head>
|
|
236
|
-
<body>Redirecting to <a href=${JSON.stringify(encodedLoc)}>${encodedLoc}</a></body>
|
|
237
|
-
</html>`;
|
|
242
|
+
event.res.setHeader("Location", location);
|
|
243
|
+
const encodedLoc = location.replace(/"/g, "%22");
|
|
244
|
+
const html = `<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=${encodedLoc}"></head></html>`;
|
|
238
245
|
return send(event, html, MIMES.html);
|
|
239
246
|
}
|
|
240
247
|
function getResponseHeaders(event) {
|
|
@@ -277,6 +284,22 @@ function sendStream(event, data) {
|
|
|
277
284
|
data.on("error", (error) => reject(createError(error)));
|
|
278
285
|
});
|
|
279
286
|
}
|
|
287
|
+
function writeEarlyHints(event, links, callback) {
|
|
288
|
+
if (!event.res.socket && !("writeEarlyHints" in event.res)) {
|
|
289
|
+
if (callback) {
|
|
290
|
+
callback();
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if ("writeEarlyHints" in event.res) {
|
|
295
|
+
return event.res.writeEarlyHints(links, callback);
|
|
296
|
+
}
|
|
297
|
+
const _links = Array.isArray(links) ? links : [links];
|
|
298
|
+
event.res.socket.write(`HTTP/1.1 103 Early Hints\r
|
|
299
|
+
Link: ${_links.join("\r\n")}\r
|
|
300
|
+
\r
|
|
301
|
+
`, "utf-8", callback);
|
|
302
|
+
}
|
|
280
303
|
|
|
281
304
|
function parseCookies(event) {
|
|
282
305
|
return cookieEs.parse(event.req.headers.cookie || "");
|
|
@@ -376,16 +399,6 @@ class H3Event {
|
|
|
376
399
|
this.context = {};
|
|
377
400
|
this.req = req;
|
|
378
401
|
this.res = res;
|
|
379
|
-
this.event = this;
|
|
380
|
-
req.event = this;
|
|
381
|
-
req.context = this.context;
|
|
382
|
-
req.req = req;
|
|
383
|
-
req.res = res;
|
|
384
|
-
res.event = this;
|
|
385
|
-
res.res = res;
|
|
386
|
-
res.req = res.req || {};
|
|
387
|
-
res.req.res = res;
|
|
388
|
-
res.req.req = req;
|
|
389
402
|
}
|
|
390
403
|
respondWith(r) {
|
|
391
404
|
Promise.resolve(r).then((_response) => {
|
|
@@ -425,63 +438,6 @@ function createEvent(req, res) {
|
|
|
425
438
|
return new H3Event(req, res);
|
|
426
439
|
}
|
|
427
440
|
|
|
428
|
-
const defineHandler = (handler) => handler;
|
|
429
|
-
const defineHandle = defineHandler;
|
|
430
|
-
const defineMiddleware = (middleware) => middleware;
|
|
431
|
-
function promisifyHandler(handler) {
|
|
432
|
-
return function(req, res) {
|
|
433
|
-
return callHandler(handler, req, res);
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
const promisifyHandle = promisifyHandler;
|
|
437
|
-
function callHandler(handler, req, res) {
|
|
438
|
-
const isMiddleware = handler.length > 2;
|
|
439
|
-
return new Promise((resolve, reject) => {
|
|
440
|
-
const next = (err) => {
|
|
441
|
-
if (isMiddleware) {
|
|
442
|
-
res.off("close", next);
|
|
443
|
-
res.off("error", next);
|
|
444
|
-
}
|
|
445
|
-
return err ? reject(createError(err)) : resolve(void 0);
|
|
446
|
-
};
|
|
447
|
-
try {
|
|
448
|
-
const returned = handler(req, res, next);
|
|
449
|
-
if (isMiddleware && returned === void 0) {
|
|
450
|
-
res.once("close", next);
|
|
451
|
-
res.once("error", next);
|
|
452
|
-
} else {
|
|
453
|
-
resolve(returned);
|
|
454
|
-
}
|
|
455
|
-
} catch (err) {
|
|
456
|
-
next(err);
|
|
457
|
-
}
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
function defineLazyHandler(handler, promisify) {
|
|
461
|
-
let _promise;
|
|
462
|
-
const resolve = () => {
|
|
463
|
-
if (!_promise) {
|
|
464
|
-
_promise = Promise.resolve(handler()).then((r) => promisify ? promisifyHandler(r.default || r) : r.default || r);
|
|
465
|
-
}
|
|
466
|
-
return _promise;
|
|
467
|
-
};
|
|
468
|
-
return function(req, res) {
|
|
469
|
-
return resolve().then((h) => h(req, res));
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
const lazyHandle = defineLazyHandler;
|
|
473
|
-
function useBase(base, handler) {
|
|
474
|
-
base = ufo.withoutTrailingSlash(base);
|
|
475
|
-
if (!base) {
|
|
476
|
-
return handler;
|
|
477
|
-
}
|
|
478
|
-
return function(req, res) {
|
|
479
|
-
req.originalUrl = req.originalUrl || req.url || "/";
|
|
480
|
-
req.url = ufo.withoutBase(req.url || "/", base);
|
|
481
|
-
return handler(req, res);
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
|
|
485
441
|
function defineEventHandler(handler) {
|
|
486
442
|
handler.__is_handler__ = true;
|
|
487
443
|
return handler;
|
|
@@ -490,16 +446,17 @@ const eventHandler = defineEventHandler;
|
|
|
490
446
|
function isEventHandler(input) {
|
|
491
447
|
return "__is_handler__" in input;
|
|
492
448
|
}
|
|
493
|
-
function toEventHandler(
|
|
494
|
-
if (isEventHandler(
|
|
495
|
-
|
|
449
|
+
function toEventHandler(input, _, _route) {
|
|
450
|
+
if (!isEventHandler(input)) {
|
|
451
|
+
console.warn(
|
|
452
|
+
"[h3] Implicit event handler conversion is deprecated. Use `eventHandler()` or `fromNodeMiddleware()` to define event handlers.",
|
|
453
|
+
_route && _route !== "/" ? `
|
|
454
|
+
Route: ${_route}` : "",
|
|
455
|
+
`
|
|
456
|
+
Handler: ${input}`
|
|
457
|
+
);
|
|
496
458
|
}
|
|
497
|
-
|
|
498
|
-
throw new TypeError("Invalid handler. It should be a function:", handler);
|
|
499
|
-
}
|
|
500
|
-
return eventHandler((event) => {
|
|
501
|
-
return callHandler(handler, event.req, event.res);
|
|
502
|
-
});
|
|
459
|
+
return input;
|
|
503
460
|
}
|
|
504
461
|
function dynamicEventHandler(initial) {
|
|
505
462
|
let current = initial;
|
|
@@ -544,30 +501,12 @@ const lazyEventHandler = defineLazyEventHandler;
|
|
|
544
501
|
function createApp(options = {}) {
|
|
545
502
|
const stack = [];
|
|
546
503
|
const handler = createAppEventHandler(stack, options);
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
const error = createError(_error);
|
|
553
|
-
if (!isError(_error)) {
|
|
554
|
-
error.unhandled = true;
|
|
555
|
-
}
|
|
556
|
-
if (options.onError) {
|
|
557
|
-
await options.onError(error, event);
|
|
558
|
-
} else {
|
|
559
|
-
if (error.unhandled || error.fatal) {
|
|
560
|
-
console.error("[h3]", error.fatal ? "[fatal]" : "[unhandled]", error);
|
|
561
|
-
}
|
|
562
|
-
await sendError(event, error, !!options.debug);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
504
|
+
const app = {
|
|
505
|
+
use: (arg1, arg2, arg3) => use(app, arg1, arg2, arg3),
|
|
506
|
+
handler,
|
|
507
|
+
stack,
|
|
508
|
+
options
|
|
565
509
|
};
|
|
566
|
-
const app = nodeHandler;
|
|
567
|
-
app.nodeHandler = nodeHandler;
|
|
568
|
-
app.stack = stack;
|
|
569
|
-
app.handler = handler;
|
|
570
|
-
app.use = (arg1, arg2, arg3) => use(app, arg1, arg2, arg3);
|
|
571
510
|
return app;
|
|
572
511
|
}
|
|
573
512
|
function use(app, arg1, arg2, arg3) {
|
|
@@ -624,19 +563,22 @@ function createAppEventHandler(stack, options) {
|
|
|
624
563
|
}
|
|
625
564
|
}
|
|
626
565
|
if (!event.res.writableEnded) {
|
|
627
|
-
throw createError({
|
|
566
|
+
throw createError({
|
|
567
|
+
statusCode: 404,
|
|
568
|
+
statusMessage: `Cannot find any route matching ${event.req.url || "/"}.`
|
|
569
|
+
});
|
|
628
570
|
}
|
|
629
571
|
});
|
|
630
572
|
}
|
|
631
573
|
function normalizeLayer(input) {
|
|
632
|
-
let handler = input.handler
|
|
574
|
+
let handler = input.handler;
|
|
633
575
|
if (handler.handler) {
|
|
634
576
|
handler = handler.handler;
|
|
635
577
|
}
|
|
636
578
|
if (input.lazy) {
|
|
637
579
|
handler = lazyEventHandler(handler);
|
|
638
580
|
} else if (!isEventHandler(handler)) {
|
|
639
|
-
handler = toEventHandler(handler);
|
|
581
|
+
handler = toEventHandler(handler, null, input.route);
|
|
640
582
|
}
|
|
641
583
|
return {
|
|
642
584
|
route: ufo.withoutTrailingSlash(input.route),
|
|
@@ -645,8 +587,72 @@ function normalizeLayer(input) {
|
|
|
645
587
|
};
|
|
646
588
|
}
|
|
647
589
|
|
|
590
|
+
const defineNodeListener = (handler) => handler;
|
|
591
|
+
const defineNodeMiddleware = (middleware) => middleware;
|
|
592
|
+
function fromNodeMiddleware(handler) {
|
|
593
|
+
if (isEventHandler(handler)) {
|
|
594
|
+
return handler;
|
|
595
|
+
}
|
|
596
|
+
if (typeof handler !== "function") {
|
|
597
|
+
throw new TypeError("Invalid handler. It should be a function:", handler);
|
|
598
|
+
}
|
|
599
|
+
return eventHandler((event) => {
|
|
600
|
+
return callNodeListener(handler, event.req, event.res);
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
function toNodeListener(app) {
|
|
604
|
+
const toNodeHandle = async function(req, res) {
|
|
605
|
+
const event = createEvent(req, res);
|
|
606
|
+
try {
|
|
607
|
+
await app.handler(event);
|
|
608
|
+
} catch (_error) {
|
|
609
|
+
const error = createError(_error);
|
|
610
|
+
if (!isError(_error)) {
|
|
611
|
+
error.unhandled = true;
|
|
612
|
+
}
|
|
613
|
+
if (app.options.onError) {
|
|
614
|
+
await app.options.onError(error, event);
|
|
615
|
+
} else {
|
|
616
|
+
if (error.unhandled || error.fatal) {
|
|
617
|
+
console.error("[h3]", error.fatal ? "[fatal]" : "[unhandled]", error);
|
|
618
|
+
}
|
|
619
|
+
await sendError(event, error, !!app.options.debug);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
return toNodeHandle;
|
|
624
|
+
}
|
|
625
|
+
function promisifyNodeListener(handler) {
|
|
626
|
+
return function(req, res) {
|
|
627
|
+
return callNodeListener(handler, req, res);
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function callNodeListener(handler, req, res) {
|
|
631
|
+
const isMiddleware = handler.length > 2;
|
|
632
|
+
return new Promise((resolve, reject) => {
|
|
633
|
+
const next = (err) => {
|
|
634
|
+
if (isMiddleware) {
|
|
635
|
+
res.off("close", next);
|
|
636
|
+
res.off("error", next);
|
|
637
|
+
}
|
|
638
|
+
return err ? reject(createError(err)) : resolve(void 0);
|
|
639
|
+
};
|
|
640
|
+
try {
|
|
641
|
+
const returned = handler(req, res, next);
|
|
642
|
+
if (isMiddleware && returned === void 0) {
|
|
643
|
+
res.once("close", next);
|
|
644
|
+
res.once("error", next);
|
|
645
|
+
} else {
|
|
646
|
+
resolve(returned);
|
|
647
|
+
}
|
|
648
|
+
} catch (err) {
|
|
649
|
+
next(err);
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
|
|
648
654
|
const RouterMethods = ["connect", "delete", "get", "head", "options", "post", "put", "trace", "patch"];
|
|
649
|
-
function createRouter() {
|
|
655
|
+
function createRouter(opts = {}) {
|
|
650
656
|
const _router = radix3.createRouter({});
|
|
651
657
|
const routes = {};
|
|
652
658
|
const router = {};
|
|
@@ -659,7 +665,7 @@ function createRouter() {
|
|
|
659
665
|
if (Array.isArray(method)) {
|
|
660
666
|
method.forEach((m) => addRoute(path, handler, m));
|
|
661
667
|
} else {
|
|
662
|
-
route.handlers[method] = toEventHandler(handler);
|
|
668
|
+
route.handlers[method] = toEventHandler(handler, null, path);
|
|
663
669
|
}
|
|
664
670
|
return router;
|
|
665
671
|
};
|
|
@@ -668,18 +674,18 @@ function createRouter() {
|
|
|
668
674
|
router[method] = (path, handle) => router.add(path, handle, method);
|
|
669
675
|
}
|
|
670
676
|
router.handler = eventHandler((event) => {
|
|
671
|
-
|
|
672
|
-
const queryUrlIndex = path.lastIndexOf("?");
|
|
673
|
-
if (queryUrlIndex > -1) {
|
|
674
|
-
path = path.substring(0, queryUrlIndex);
|
|
675
|
-
}
|
|
677
|
+
const path = new URL(event.req.url || "/", "http://localhost").pathname;
|
|
676
678
|
const matched = _router.lookup(path);
|
|
677
679
|
if (!matched) {
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
680
|
+
if (opts.preemtive) {
|
|
681
|
+
throw createError({
|
|
682
|
+
statusCode: 404,
|
|
683
|
+
name: "Not Found",
|
|
684
|
+
statusMessage: `Cannot find any route matching ${event.req.url || "/"}.`
|
|
685
|
+
});
|
|
686
|
+
} else {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
683
689
|
}
|
|
684
690
|
const method = (event.req.method || "get").toLowerCase();
|
|
685
691
|
const handler = matched.handlers[method] || matched.handlers.all;
|
|
@@ -691,8 +697,7 @@ function createRouter() {
|
|
|
691
697
|
});
|
|
692
698
|
}
|
|
693
699
|
const params = matched.params || {};
|
|
694
|
-
event.
|
|
695
|
-
event.req.context.params = params;
|
|
700
|
+
event.context.params = params;
|
|
696
701
|
return handler(event);
|
|
697
702
|
});
|
|
698
703
|
return router;
|
|
@@ -708,7 +713,7 @@ exports.appendHeaders = appendHeaders;
|
|
|
708
713
|
exports.appendResponseHeader = appendResponseHeader;
|
|
709
714
|
exports.appendResponseHeaders = appendResponseHeaders;
|
|
710
715
|
exports.assertMethod = assertMethod;
|
|
711
|
-
exports.
|
|
716
|
+
exports.callNodeListener = callNodeListener;
|
|
712
717
|
exports.createApp = createApp;
|
|
713
718
|
exports.createAppEventHandler = createAppEventHandler;
|
|
714
719
|
exports.createError = createError;
|
|
@@ -716,14 +721,13 @@ exports.createEvent = createEvent;
|
|
|
716
721
|
exports.createRouter = createRouter;
|
|
717
722
|
exports.defaultContentType = defaultContentType;
|
|
718
723
|
exports.defineEventHandler = defineEventHandler;
|
|
719
|
-
exports.defineHandle = defineHandle;
|
|
720
|
-
exports.defineHandler = defineHandler;
|
|
721
724
|
exports.defineLazyEventHandler = defineLazyEventHandler;
|
|
722
|
-
exports.
|
|
723
|
-
exports.
|
|
725
|
+
exports.defineNodeListener = defineNodeListener;
|
|
726
|
+
exports.defineNodeMiddleware = defineNodeMiddleware;
|
|
724
727
|
exports.deleteCookie = deleteCookie;
|
|
725
728
|
exports.dynamicEventHandler = dynamicEventHandler;
|
|
726
729
|
exports.eventHandler = eventHandler;
|
|
730
|
+
exports.fromNodeMiddleware = fromNodeMiddleware;
|
|
727
731
|
exports.getCookie = getCookie;
|
|
728
732
|
exports.getHeader = getHeader;
|
|
729
733
|
exports.getHeaders = getHeaders;
|
|
@@ -742,10 +746,8 @@ exports.isEventHandler = isEventHandler;
|
|
|
742
746
|
exports.isMethod = isMethod;
|
|
743
747
|
exports.isStream = isStream;
|
|
744
748
|
exports.lazyEventHandler = lazyEventHandler;
|
|
745
|
-
exports.lazyHandle = lazyHandle;
|
|
746
749
|
exports.parseCookies = parseCookies;
|
|
747
|
-
exports.
|
|
748
|
-
exports.promisifyHandler = promisifyHandler;
|
|
750
|
+
exports.promisifyNodeListener = promisifyNodeListener;
|
|
749
751
|
exports.readBody = readBody;
|
|
750
752
|
exports.readRawBody = readRawBody;
|
|
751
753
|
exports.send = send;
|
|
@@ -758,6 +760,7 @@ exports.setHeaders = setHeaders;
|
|
|
758
760
|
exports.setResponseHeader = setResponseHeader;
|
|
759
761
|
exports.setResponseHeaders = setResponseHeaders;
|
|
760
762
|
exports.toEventHandler = toEventHandler;
|
|
763
|
+
exports.toNodeListener = toNodeListener;
|
|
761
764
|
exports.use = use;
|
|
762
765
|
exports.useBase = useBase;
|
|
763
766
|
exports.useBody = useBody;
|
|
@@ -766,3 +769,4 @@ exports.useCookies = useCookies;
|
|
|
766
769
|
exports.useMethod = useMethod;
|
|
767
770
|
exports.useQuery = useQuery;
|
|
768
771
|
exports.useRawBody = useRawBody;
|
|
772
|
+
exports.writeEarlyHints = writeEarlyHints;
|