h3 0.7.21 → 0.8.1
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 +142 -134
- package/dist/index.d.ts +80 -103
- package/dist/index.mjs +137 -122
- 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
|
}
|
|
@@ -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
|
}
|
|
@@ -273,6 +284,22 @@ function sendStream(event, data) {
|
|
|
273
284
|
data.on("error", (error) => reject(createError(error)));
|
|
274
285
|
});
|
|
275
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
|
+
}
|
|
276
303
|
|
|
277
304
|
function parseCookies(event) {
|
|
278
305
|
return cookieEs.parse(event.req.headers.cookie || "");
|
|
@@ -372,16 +399,6 @@ class H3Event {
|
|
|
372
399
|
this.context = {};
|
|
373
400
|
this.req = req;
|
|
374
401
|
this.res = res;
|
|
375
|
-
this.event = this;
|
|
376
|
-
req.event = this;
|
|
377
|
-
req.context = this.context;
|
|
378
|
-
req.req = req;
|
|
379
|
-
req.res = res;
|
|
380
|
-
res.event = this;
|
|
381
|
-
res.res = res;
|
|
382
|
-
res.req = res.req || {};
|
|
383
|
-
res.req.res = res;
|
|
384
|
-
res.req.req = req;
|
|
385
402
|
}
|
|
386
403
|
respondWith(r) {
|
|
387
404
|
Promise.resolve(r).then((_response) => {
|
|
@@ -421,63 +438,6 @@ function createEvent(req, res) {
|
|
|
421
438
|
return new H3Event(req, res);
|
|
422
439
|
}
|
|
423
440
|
|
|
424
|
-
const defineHandler = (handler) => handler;
|
|
425
|
-
const defineHandle = defineHandler;
|
|
426
|
-
const defineMiddleware = (middleware) => middleware;
|
|
427
|
-
function promisifyHandler(handler) {
|
|
428
|
-
return function(req, res) {
|
|
429
|
-
return callHandler(handler, req, res);
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
const promisifyHandle = promisifyHandler;
|
|
433
|
-
function callHandler(handler, req, res) {
|
|
434
|
-
const isMiddleware = handler.length > 2;
|
|
435
|
-
return new Promise((resolve, reject) => {
|
|
436
|
-
const next = (err) => {
|
|
437
|
-
if (isMiddleware) {
|
|
438
|
-
res.off("close", next);
|
|
439
|
-
res.off("error", next);
|
|
440
|
-
}
|
|
441
|
-
return err ? reject(createError(err)) : resolve(void 0);
|
|
442
|
-
};
|
|
443
|
-
try {
|
|
444
|
-
const returned = handler(req, res, next);
|
|
445
|
-
if (isMiddleware && returned === void 0) {
|
|
446
|
-
res.once("close", next);
|
|
447
|
-
res.once("error", next);
|
|
448
|
-
} else {
|
|
449
|
-
resolve(returned);
|
|
450
|
-
}
|
|
451
|
-
} catch (err) {
|
|
452
|
-
next(err);
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
function defineLazyHandler(handler, promisify) {
|
|
457
|
-
let _promise;
|
|
458
|
-
const resolve = () => {
|
|
459
|
-
if (!_promise) {
|
|
460
|
-
_promise = Promise.resolve(handler()).then((r) => promisify ? promisifyHandler(r.default || r) : r.default || r);
|
|
461
|
-
}
|
|
462
|
-
return _promise;
|
|
463
|
-
};
|
|
464
|
-
return function(req, res) {
|
|
465
|
-
return resolve().then((h) => h(req, res));
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
const lazyHandle = defineLazyHandler;
|
|
469
|
-
function useBase(base, handler) {
|
|
470
|
-
base = ufo.withoutTrailingSlash(base);
|
|
471
|
-
if (!base) {
|
|
472
|
-
return handler;
|
|
473
|
-
}
|
|
474
|
-
return function(req, res) {
|
|
475
|
-
req.originalUrl = req.originalUrl || req.url || "/";
|
|
476
|
-
req.url = ufo.withoutBase(req.url || "/", base);
|
|
477
|
-
return handler(req, res);
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
|
|
481
441
|
function defineEventHandler(handler) {
|
|
482
442
|
handler.__is_handler__ = true;
|
|
483
443
|
return handler;
|
|
@@ -486,16 +446,17 @@ const eventHandler = defineEventHandler;
|
|
|
486
446
|
function isEventHandler(input) {
|
|
487
447
|
return "__is_handler__" in input;
|
|
488
448
|
}
|
|
489
|
-
function toEventHandler(
|
|
490
|
-
if (isEventHandler(
|
|
491
|
-
|
|
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
|
+
);
|
|
492
458
|
}
|
|
493
|
-
|
|
494
|
-
throw new TypeError("Invalid handler. It should be a function:", handler);
|
|
495
|
-
}
|
|
496
|
-
return eventHandler((event) => {
|
|
497
|
-
return callHandler(handler, event.req, event.res);
|
|
498
|
-
});
|
|
459
|
+
return input;
|
|
499
460
|
}
|
|
500
461
|
function dynamicEventHandler(initial) {
|
|
501
462
|
let current = initial;
|
|
@@ -540,30 +501,12 @@ const lazyEventHandler = defineLazyEventHandler;
|
|
|
540
501
|
function createApp(options = {}) {
|
|
541
502
|
const stack = [];
|
|
542
503
|
const handler = createAppEventHandler(stack, options);
|
|
543
|
-
const
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
const error = createError(_error);
|
|
549
|
-
if (!isError(_error)) {
|
|
550
|
-
error.unhandled = true;
|
|
551
|
-
}
|
|
552
|
-
if (options.onError) {
|
|
553
|
-
await options.onError(error, event);
|
|
554
|
-
} else {
|
|
555
|
-
if (error.unhandled || error.fatal) {
|
|
556
|
-
console.error("[h3]", error.fatal ? "[fatal]" : "[unhandled]", error);
|
|
557
|
-
}
|
|
558
|
-
await sendError(event, error, !!options.debug);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
504
|
+
const app = {
|
|
505
|
+
use: (arg1, arg2, arg3) => use(app, arg1, arg2, arg3),
|
|
506
|
+
handler,
|
|
507
|
+
stack,
|
|
508
|
+
options
|
|
561
509
|
};
|
|
562
|
-
const app = nodeHandler;
|
|
563
|
-
app.nodeHandler = nodeHandler;
|
|
564
|
-
app.stack = stack;
|
|
565
|
-
app.handler = handler;
|
|
566
|
-
app.use = (arg1, arg2, arg3) => use(app, arg1, arg2, arg3);
|
|
567
510
|
return app;
|
|
568
511
|
}
|
|
569
512
|
function use(app, arg1, arg2, arg3) {
|
|
@@ -620,19 +563,22 @@ function createAppEventHandler(stack, options) {
|
|
|
620
563
|
}
|
|
621
564
|
}
|
|
622
565
|
if (!event.res.writableEnded) {
|
|
623
|
-
throw createError({
|
|
566
|
+
throw createError({
|
|
567
|
+
statusCode: 404,
|
|
568
|
+
statusMessage: `Cannot find any route matching ${event.req.url || "/"}.`
|
|
569
|
+
});
|
|
624
570
|
}
|
|
625
571
|
});
|
|
626
572
|
}
|
|
627
573
|
function normalizeLayer(input) {
|
|
628
|
-
let handler = input.handler
|
|
574
|
+
let handler = input.handler;
|
|
629
575
|
if (handler.handler) {
|
|
630
576
|
handler = handler.handler;
|
|
631
577
|
}
|
|
632
578
|
if (input.lazy) {
|
|
633
579
|
handler = lazyEventHandler(handler);
|
|
634
580
|
} else if (!isEventHandler(handler)) {
|
|
635
|
-
handler = toEventHandler(handler);
|
|
581
|
+
handler = toEventHandler(handler, null, input.route);
|
|
636
582
|
}
|
|
637
583
|
return {
|
|
638
584
|
route: ufo.withoutTrailingSlash(input.route),
|
|
@@ -641,8 +587,72 @@ function normalizeLayer(input) {
|
|
|
641
587
|
};
|
|
642
588
|
}
|
|
643
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
|
+
|
|
644
654
|
const RouterMethods = ["connect", "delete", "get", "head", "options", "post", "put", "trace", "patch"];
|
|
645
|
-
function createRouter() {
|
|
655
|
+
function createRouter(opts = {}) {
|
|
646
656
|
const _router = radix3.createRouter({});
|
|
647
657
|
const routes = {};
|
|
648
658
|
const router = {};
|
|
@@ -655,7 +665,7 @@ function createRouter() {
|
|
|
655
665
|
if (Array.isArray(method)) {
|
|
656
666
|
method.forEach((m) => addRoute(path, handler, m));
|
|
657
667
|
} else {
|
|
658
|
-
route.handlers[method] = toEventHandler(handler);
|
|
668
|
+
route.handlers[method] = toEventHandler(handler, null, path);
|
|
659
669
|
}
|
|
660
670
|
return router;
|
|
661
671
|
};
|
|
@@ -664,18 +674,18 @@ function createRouter() {
|
|
|
664
674
|
router[method] = (path, handle) => router.add(path, handle, method);
|
|
665
675
|
}
|
|
666
676
|
router.handler = eventHandler((event) => {
|
|
667
|
-
|
|
668
|
-
const queryUrlIndex = path.lastIndexOf("?");
|
|
669
|
-
if (queryUrlIndex > -1) {
|
|
670
|
-
path = path.substring(0, queryUrlIndex);
|
|
671
|
-
}
|
|
677
|
+
const path = new URL(event.req.url || "/", "http://localhost").pathname;
|
|
672
678
|
const matched = _router.lookup(path);
|
|
673
679
|
if (!matched) {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
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
|
+
}
|
|
679
689
|
}
|
|
680
690
|
const method = (event.req.method || "get").toLowerCase();
|
|
681
691
|
const handler = matched.handlers[method] || matched.handlers.all;
|
|
@@ -687,8 +697,7 @@ function createRouter() {
|
|
|
687
697
|
});
|
|
688
698
|
}
|
|
689
699
|
const params = matched.params || {};
|
|
690
|
-
event.
|
|
691
|
-
event.req.context.params = params;
|
|
700
|
+
event.context.params = params;
|
|
692
701
|
return handler(event);
|
|
693
702
|
});
|
|
694
703
|
return router;
|
|
@@ -704,7 +713,7 @@ exports.appendHeaders = appendHeaders;
|
|
|
704
713
|
exports.appendResponseHeader = appendResponseHeader;
|
|
705
714
|
exports.appendResponseHeaders = appendResponseHeaders;
|
|
706
715
|
exports.assertMethod = assertMethod;
|
|
707
|
-
exports.
|
|
716
|
+
exports.callNodeListener = callNodeListener;
|
|
708
717
|
exports.createApp = createApp;
|
|
709
718
|
exports.createAppEventHandler = createAppEventHandler;
|
|
710
719
|
exports.createError = createError;
|
|
@@ -712,14 +721,13 @@ exports.createEvent = createEvent;
|
|
|
712
721
|
exports.createRouter = createRouter;
|
|
713
722
|
exports.defaultContentType = defaultContentType;
|
|
714
723
|
exports.defineEventHandler = defineEventHandler;
|
|
715
|
-
exports.defineHandle = defineHandle;
|
|
716
|
-
exports.defineHandler = defineHandler;
|
|
717
724
|
exports.defineLazyEventHandler = defineLazyEventHandler;
|
|
718
|
-
exports.
|
|
719
|
-
exports.
|
|
725
|
+
exports.defineNodeListener = defineNodeListener;
|
|
726
|
+
exports.defineNodeMiddleware = defineNodeMiddleware;
|
|
720
727
|
exports.deleteCookie = deleteCookie;
|
|
721
728
|
exports.dynamicEventHandler = dynamicEventHandler;
|
|
722
729
|
exports.eventHandler = eventHandler;
|
|
730
|
+
exports.fromNodeMiddleware = fromNodeMiddleware;
|
|
723
731
|
exports.getCookie = getCookie;
|
|
724
732
|
exports.getHeader = getHeader;
|
|
725
733
|
exports.getHeaders = getHeaders;
|
|
@@ -738,10 +746,8 @@ exports.isEventHandler = isEventHandler;
|
|
|
738
746
|
exports.isMethod = isMethod;
|
|
739
747
|
exports.isStream = isStream;
|
|
740
748
|
exports.lazyEventHandler = lazyEventHandler;
|
|
741
|
-
exports.lazyHandle = lazyHandle;
|
|
742
749
|
exports.parseCookies = parseCookies;
|
|
743
|
-
exports.
|
|
744
|
-
exports.promisifyHandler = promisifyHandler;
|
|
750
|
+
exports.promisifyNodeListener = promisifyNodeListener;
|
|
745
751
|
exports.readBody = readBody;
|
|
746
752
|
exports.readRawBody = readRawBody;
|
|
747
753
|
exports.send = send;
|
|
@@ -754,6 +760,7 @@ exports.setHeaders = setHeaders;
|
|
|
754
760
|
exports.setResponseHeader = setResponseHeader;
|
|
755
761
|
exports.setResponseHeaders = setResponseHeaders;
|
|
756
762
|
exports.toEventHandler = toEventHandler;
|
|
763
|
+
exports.toNodeListener = toNodeListener;
|
|
757
764
|
exports.use = use;
|
|
758
765
|
exports.useBase = useBase;
|
|
759
766
|
exports.useBody = useBody;
|
|
@@ -762,3 +769,4 @@ exports.useCookies = useCookies;
|
|
|
762
769
|
exports.useMethod = useMethod;
|
|
763
770
|
exports.useQuery = useQuery;
|
|
764
771
|
exports.useRawBody = useRawBody;
|
|
772
|
+
exports.writeEarlyHints = writeEarlyHints;
|