h3 0.3.8 → 0.4.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 +27 -2
- package/dist/index.cjs +65 -351
- package/dist/index.d.ts +29 -6
- package/dist/index.mjs +58 -350
- package/package.json +16 -14
package/README.md
CHANGED
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
|
|
22
22
|
✔️ **Extendable:** Ships with a set of composable utilities but can be extended
|
|
23
23
|
|
|
24
|
+
✔️ **Router:** Super fast route matching using [unjs/radix3](https://github.com/unjs/radix3)
|
|
25
|
+
|
|
24
26
|
## Install
|
|
25
27
|
|
|
26
28
|
```bash
|
|
@@ -57,7 +59,29 @@ listen(app)
|
|
|
57
59
|
```
|
|
58
60
|
</details>
|
|
59
61
|
|
|
60
|
-
##
|
|
62
|
+
## Router
|
|
63
|
+
|
|
64
|
+
The `app` instance created by `h3` uses a middleware stack (see [how it works](#how-it-works)) with the ability to match route prefix and apply matched middleware.
|
|
65
|
+
|
|
66
|
+
To opt-in using a more advanced and convenient routing system, we can create a router instance and register it to app instance.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { createApp, createRouter } from 'h3'
|
|
70
|
+
|
|
71
|
+
const app = createApp()
|
|
72
|
+
|
|
73
|
+
const router = createRouter()
|
|
74
|
+
.get('/', () => 'Hello World!')
|
|
75
|
+
.get('/hello/:name', req => `Hello ${req.params.name}!`)
|
|
76
|
+
|
|
77
|
+
app.use(router)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Tip:** We can register same route more than once with different methods.
|
|
81
|
+
|
|
82
|
+
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).
|
|
83
|
+
|
|
84
|
+
## More usage examples
|
|
61
85
|
|
|
62
86
|
```js
|
|
63
87
|
// Handle can directly return object or Promise<object> for JSON response
|
|
@@ -92,11 +116,12 @@ Instead of adding helpers to `req` and `res`, h3 exposes them as composable util
|
|
|
92
116
|
- `useCookies(req)`
|
|
93
117
|
- `useCookie(req, name)`
|
|
94
118
|
- `setCookie(res, name, value, opts?)`
|
|
119
|
+
- `deleteCookie(res, name, opts?)`
|
|
95
120
|
- `useQuery(req)`
|
|
96
121
|
- `send(res, data, type?)`
|
|
97
122
|
- `sendRedirect(res, location, code=302)`
|
|
98
123
|
- `appendHeader(res, name, value)`
|
|
99
|
-
- `createError({ statusCode, statusMessage, data? }`
|
|
124
|
+
- `createError({ statusCode, statusMessage, data? })`
|
|
100
125
|
- `sendError(res, error, debug?)`
|
|
101
126
|
- `defineHandle(handle)`
|
|
102
127
|
- `defineMiddleware(middlware)`
|
package/dist/index.cjs
CHANGED
|
@@ -2,106 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} catch (_err) {
|
|
10
|
-
return "" + text;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
function decodeQueryValue(text) {
|
|
14
|
-
return decode$1(text.replace(PLUS_RE, " "));
|
|
15
|
-
}
|
|
5
|
+
const ufo = require('ufo');
|
|
6
|
+
const radix3 = require('radix3');
|
|
7
|
+
const destr = require('destr');
|
|
8
|
+
const cookieEs = require('cookie-es');
|
|
16
9
|
|
|
17
|
-
function
|
|
18
|
-
const obj = {};
|
|
19
|
-
if (paramsStr[0] === "?") {
|
|
20
|
-
paramsStr = paramsStr.substr(1);
|
|
21
|
-
}
|
|
22
|
-
for (const param of paramsStr.split("&")) {
|
|
23
|
-
const s = param.match(/([^=]+)=?(.*)/) || [];
|
|
24
|
-
if (s.length < 2) {
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
const key = decode$1(s[1]);
|
|
28
|
-
if (key === "__proto__" || key === "constructor") {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
const value = decodeQueryValue(s[2] || "");
|
|
32
|
-
if (obj[key]) {
|
|
33
|
-
if (Array.isArray(obj[key])) {
|
|
34
|
-
obj[key].push(value);
|
|
35
|
-
} else {
|
|
36
|
-
obj[key] = [obj[key], value];
|
|
37
|
-
}
|
|
38
|
-
} else {
|
|
39
|
-
obj[key] = value;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return obj;
|
|
43
|
-
}
|
|
44
|
-
function hasProtocol(inputStr, acceptProtocolRelative = false) {
|
|
45
|
-
return /^\w+:\/\/.+/.test(inputStr) || acceptProtocolRelative && /^\/\/[^/]+/.test(inputStr);
|
|
46
|
-
}
|
|
47
|
-
const TRAILING_SLASH_RE = /\/$|\/\?/;
|
|
48
|
-
function hasTrailingSlash(input = "", queryParams = false) {
|
|
49
|
-
if (!queryParams) {
|
|
50
|
-
return input.endsWith("/");
|
|
51
|
-
}
|
|
52
|
-
return TRAILING_SLASH_RE.test(input);
|
|
53
|
-
}
|
|
54
|
-
function withoutTrailingSlash(input = "", queryParams = false) {
|
|
55
|
-
if (!queryParams) {
|
|
56
|
-
return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || "/";
|
|
57
|
-
}
|
|
58
|
-
if (!hasTrailingSlash(input, true)) {
|
|
59
|
-
return input || "/";
|
|
60
|
-
}
|
|
61
|
-
const [s0, ...s] = input.split("?");
|
|
62
|
-
return (s0.slice(0, -1) || "/") + (s.length ? `?${s.join("?")}` : "");
|
|
63
|
-
}
|
|
64
|
-
function withoutBase(input, base) {
|
|
65
|
-
if (isEmptyURL(base)) {
|
|
66
|
-
return input;
|
|
67
|
-
}
|
|
68
|
-
const _base = withoutTrailingSlash(base);
|
|
69
|
-
if (input.startsWith(_base)) {
|
|
70
|
-
return input.substr(_base.length) || "/";
|
|
71
|
-
}
|
|
72
|
-
return input;
|
|
73
|
-
}
|
|
74
|
-
function getQuery(input) {
|
|
75
|
-
return parseQuery(parseURL(input).search);
|
|
76
|
-
}
|
|
77
|
-
function isEmptyURL(url) {
|
|
78
|
-
return !url || url === "/";
|
|
79
|
-
}
|
|
10
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e["default"] : e; }
|
|
80
11
|
|
|
81
|
-
|
|
82
|
-
if (!hasProtocol(input, true)) {
|
|
83
|
-
return defaultProto ? parseURL(defaultProto + input) : parsePath(input);
|
|
84
|
-
}
|
|
85
|
-
const [protocol = "", auth, hostAndPath] = (input.match(/([^:/]+:)?\/\/([^/@]+@)?(.*)/) || []).splice(1);
|
|
86
|
-
const [host = "", path = ""] = (hostAndPath.match(/([^/?]*)(.*)?/) || []).splice(1);
|
|
87
|
-
const { pathname, search, hash } = parsePath(path);
|
|
88
|
-
return {
|
|
89
|
-
protocol,
|
|
90
|
-
auth: auth ? auth.substr(0, auth.length - 1) : "",
|
|
91
|
-
host,
|
|
92
|
-
pathname,
|
|
93
|
-
search,
|
|
94
|
-
hash
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
function parsePath(input = "") {
|
|
98
|
-
const [pathname = "", search = "", hash = ""] = (input.match(/([^#?]*)(\?[^#]*)?(#.*)?/) || []).splice(1);
|
|
99
|
-
return {
|
|
100
|
-
pathname,
|
|
101
|
-
search,
|
|
102
|
-
hash
|
|
103
|
-
};
|
|
104
|
-
}
|
|
12
|
+
const destr__default = /*#__PURE__*/_interopDefaultLegacy(destr);
|
|
105
13
|
|
|
106
14
|
const defineHandle = (handler) => handler;
|
|
107
15
|
const defineMiddleware = (middleware) => middleware;
|
|
@@ -139,64 +47,19 @@ function lazyHandle(handle, promisify) {
|
|
|
139
47
|
};
|
|
140
48
|
}
|
|
141
49
|
function useBase(base, handle) {
|
|
142
|
-
base = withoutTrailingSlash(base);
|
|
50
|
+
base = ufo.withoutTrailingSlash(base);
|
|
143
51
|
if (!base) {
|
|
144
52
|
return handle;
|
|
145
53
|
}
|
|
146
54
|
return function(req, res) {
|
|
147
55
|
req.originalUrl = req.originalUrl || req.url || "/";
|
|
148
|
-
req.url = withoutBase(req.url || "/", base);
|
|
56
|
+
req.url = ufo.withoutBase(req.url || "/", base);
|
|
149
57
|
return handle(req, res);
|
|
150
58
|
};
|
|
151
59
|
}
|
|
152
60
|
|
|
153
|
-
const suspectProtoRx = /"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*:/;
|
|
154
|
-
const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
|
|
155
|
-
const JsonSigRx = /^["{[]|^-?[0-9][0-9.]{0,14}$/;
|
|
156
|
-
function jsonParseTransform(key, value) {
|
|
157
|
-
if (key === "__proto__" || key === "constructor") {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
return value;
|
|
161
|
-
}
|
|
162
|
-
function destr(val) {
|
|
163
|
-
if (typeof val !== "string") {
|
|
164
|
-
return val;
|
|
165
|
-
}
|
|
166
|
-
const _lval = val.toLowerCase();
|
|
167
|
-
if (_lval === "true") {
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
170
|
-
if (_lval === "false") {
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
if (_lval === "null") {
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
if (_lval === "nan") {
|
|
177
|
-
return NaN;
|
|
178
|
-
}
|
|
179
|
-
if (_lval === "infinity") {
|
|
180
|
-
return Infinity;
|
|
181
|
-
}
|
|
182
|
-
if (_lval === "undefined") {
|
|
183
|
-
return void 0;
|
|
184
|
-
}
|
|
185
|
-
if (!JsonSigRx.test(val)) {
|
|
186
|
-
return val;
|
|
187
|
-
}
|
|
188
|
-
try {
|
|
189
|
-
if (suspectProtoRx.test(val) || suspectConstructorRx.test(val)) {
|
|
190
|
-
return JSON.parse(val, jsonParseTransform);
|
|
191
|
-
}
|
|
192
|
-
return JSON.parse(val);
|
|
193
|
-
} catch (_e) {
|
|
194
|
-
return val;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
61
|
function useQuery(req) {
|
|
199
|
-
return getQuery(req.url || "");
|
|
62
|
+
return ufo.getQuery(req.url || "");
|
|
200
63
|
}
|
|
201
64
|
function useMethod(req, defaultMethod = "GET") {
|
|
202
65
|
return (req.method || defaultMethod).toUpperCase();
|
|
@@ -253,7 +116,7 @@ async function useBody(req) {
|
|
|
253
116
|
return req[ParsedBodySymbol];
|
|
254
117
|
}
|
|
255
118
|
const body = await useRawBody(req);
|
|
256
|
-
const json =
|
|
119
|
+
const json = destr__default(body);
|
|
257
120
|
req[ParsedBodySymbol] = json;
|
|
258
121
|
return json;
|
|
259
122
|
}
|
|
@@ -263,203 +126,6 @@ const MIMES = {
|
|
|
263
126
|
json: "application/json"
|
|
264
127
|
};
|
|
265
128
|
|
|
266
|
-
/*!
|
|
267
|
-
* cookie
|
|
268
|
-
* Copyright(c) 2012-2014 Roman Shtylman
|
|
269
|
-
* Copyright(c) 2015 Douglas Christopher Wilson
|
|
270
|
-
* MIT Licensed
|
|
271
|
-
*/
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Module exports.
|
|
275
|
-
* @public
|
|
276
|
-
*/
|
|
277
|
-
|
|
278
|
-
var parse_1 = parse;
|
|
279
|
-
var serialize_1 = serialize;
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Module variables.
|
|
283
|
-
* @private
|
|
284
|
-
*/
|
|
285
|
-
|
|
286
|
-
var decode = decodeURIComponent;
|
|
287
|
-
var encode = encodeURIComponent;
|
|
288
|
-
var pairSplitRegExp = /; */;
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* RegExp to match field-content in RFC 7230 sec 3.2
|
|
292
|
-
*
|
|
293
|
-
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
|
294
|
-
* field-vchar = VCHAR / obs-text
|
|
295
|
-
* obs-text = %x80-FF
|
|
296
|
-
*/
|
|
297
|
-
|
|
298
|
-
var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Parse a cookie header.
|
|
302
|
-
*
|
|
303
|
-
* Parse the given cookie header string into an object
|
|
304
|
-
* The object has the various cookies as keys(names) => values
|
|
305
|
-
*
|
|
306
|
-
* @param {string} str
|
|
307
|
-
* @param {object} [options]
|
|
308
|
-
* @return {object}
|
|
309
|
-
* @public
|
|
310
|
-
*/
|
|
311
|
-
|
|
312
|
-
function parse(str, options) {
|
|
313
|
-
if (typeof str !== 'string') {
|
|
314
|
-
throw new TypeError('argument str must be a string');
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
var obj = {};
|
|
318
|
-
var opt = options || {};
|
|
319
|
-
var pairs = str.split(pairSplitRegExp);
|
|
320
|
-
var dec = opt.decode || decode;
|
|
321
|
-
|
|
322
|
-
for (var i = 0; i < pairs.length; i++) {
|
|
323
|
-
var pair = pairs[i];
|
|
324
|
-
var eq_idx = pair.indexOf('=');
|
|
325
|
-
|
|
326
|
-
// skip things that don't look like key=value
|
|
327
|
-
if (eq_idx < 0) {
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
var key = pair.substr(0, eq_idx).trim();
|
|
332
|
-
var val = pair.substr(++eq_idx, pair.length).trim();
|
|
333
|
-
|
|
334
|
-
// quoted values
|
|
335
|
-
if ('"' == val[0]) {
|
|
336
|
-
val = val.slice(1, -1);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// only assign once
|
|
340
|
-
if (undefined == obj[key]) {
|
|
341
|
-
obj[key] = tryDecode(val, dec);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
return obj;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Serialize data into a cookie header.
|
|
350
|
-
*
|
|
351
|
-
* Serialize the a name value pair into a cookie string suitable for
|
|
352
|
-
* http headers. An optional options object specified cookie parameters.
|
|
353
|
-
*
|
|
354
|
-
* serialize('foo', 'bar', { httpOnly: true })
|
|
355
|
-
* => "foo=bar; httpOnly"
|
|
356
|
-
*
|
|
357
|
-
* @param {string} name
|
|
358
|
-
* @param {string} val
|
|
359
|
-
* @param {object} [options]
|
|
360
|
-
* @return {string}
|
|
361
|
-
* @public
|
|
362
|
-
*/
|
|
363
|
-
|
|
364
|
-
function serialize(name, val, options) {
|
|
365
|
-
var opt = options || {};
|
|
366
|
-
var enc = opt.encode || encode;
|
|
367
|
-
|
|
368
|
-
if (typeof enc !== 'function') {
|
|
369
|
-
throw new TypeError('option encode is invalid');
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
if (!fieldContentRegExp.test(name)) {
|
|
373
|
-
throw new TypeError('argument name is invalid');
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
var value = enc(val);
|
|
377
|
-
|
|
378
|
-
if (value && !fieldContentRegExp.test(value)) {
|
|
379
|
-
throw new TypeError('argument val is invalid');
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
var str = name + '=' + value;
|
|
383
|
-
|
|
384
|
-
if (null != opt.maxAge) {
|
|
385
|
-
var maxAge = opt.maxAge - 0;
|
|
386
|
-
if (isNaN(maxAge)) throw new Error('maxAge should be a Number');
|
|
387
|
-
str += '; Max-Age=' + Math.floor(maxAge);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (opt.domain) {
|
|
391
|
-
if (!fieldContentRegExp.test(opt.domain)) {
|
|
392
|
-
throw new TypeError('option domain is invalid');
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
str += '; Domain=' + opt.domain;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (opt.path) {
|
|
399
|
-
if (!fieldContentRegExp.test(opt.path)) {
|
|
400
|
-
throw new TypeError('option path is invalid');
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
str += '; Path=' + opt.path;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
if (opt.expires) {
|
|
407
|
-
if (typeof opt.expires.toUTCString !== 'function') {
|
|
408
|
-
throw new TypeError('option expires is invalid');
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
str += '; Expires=' + opt.expires.toUTCString();
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
if (opt.httpOnly) {
|
|
415
|
-
str += '; HttpOnly';
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
if (opt.secure) {
|
|
419
|
-
str += '; Secure';
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (opt.sameSite) {
|
|
423
|
-
var sameSite = typeof opt.sameSite === 'string'
|
|
424
|
-
? opt.sameSite.toLowerCase() : opt.sameSite;
|
|
425
|
-
|
|
426
|
-
switch (sameSite) {
|
|
427
|
-
case true:
|
|
428
|
-
str += '; SameSite=Strict';
|
|
429
|
-
break;
|
|
430
|
-
case 'lax':
|
|
431
|
-
str += '; SameSite=Lax';
|
|
432
|
-
break;
|
|
433
|
-
case 'strict':
|
|
434
|
-
str += '; SameSite=Strict';
|
|
435
|
-
break;
|
|
436
|
-
case 'none':
|
|
437
|
-
str += '; SameSite=None';
|
|
438
|
-
break;
|
|
439
|
-
default:
|
|
440
|
-
throw new TypeError('option sameSite is invalid');
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
return str;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Try decoding a string using a decoding function.
|
|
449
|
-
*
|
|
450
|
-
* @param {string} str
|
|
451
|
-
* @param {function} decode
|
|
452
|
-
* @private
|
|
453
|
-
*/
|
|
454
|
-
|
|
455
|
-
function tryDecode(str, decode) {
|
|
456
|
-
try {
|
|
457
|
-
return decode(str);
|
|
458
|
-
} catch (e) {
|
|
459
|
-
return str;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
129
|
const defer = typeof setImmediate !== "undefined" ? setImmediate : (fn) => fn();
|
|
464
130
|
function send(res, data, type) {
|
|
465
131
|
if (type) {
|
|
@@ -495,15 +161,21 @@ function appendHeader(res, name, value) {
|
|
|
495
161
|
}
|
|
496
162
|
|
|
497
163
|
function useCookies(req) {
|
|
498
|
-
return
|
|
164
|
+
return cookieEs.parse(req.headers.cookie || "");
|
|
499
165
|
}
|
|
500
166
|
function useCookie(req, name) {
|
|
501
167
|
return useCookies(req)[name];
|
|
502
168
|
}
|
|
503
169
|
function setCookie(res, name, value, serializeOptions) {
|
|
504
|
-
const cookieStr =
|
|
170
|
+
const cookieStr = cookieEs.serialize(name, value, serializeOptions);
|
|
505
171
|
appendHeader(res, "Set-Cookie", cookieStr);
|
|
506
172
|
}
|
|
173
|
+
function deleteCookie(res, name, serializeOptions) {
|
|
174
|
+
setCookie(res, name, "", {
|
|
175
|
+
...serializeOptions,
|
|
176
|
+
maxAge: 0
|
|
177
|
+
});
|
|
178
|
+
}
|
|
507
179
|
|
|
508
180
|
class H3Error extends Error {
|
|
509
181
|
constructor() {
|
|
@@ -513,11 +185,10 @@ class H3Error extends Error {
|
|
|
513
185
|
}
|
|
514
186
|
}
|
|
515
187
|
function createError(input) {
|
|
516
|
-
var _a;
|
|
517
188
|
if (input instanceof H3Error) {
|
|
518
189
|
return input;
|
|
519
190
|
}
|
|
520
|
-
const err = new H3Error(
|
|
191
|
+
const err = new H3Error(input.message ?? input.statusMessage);
|
|
521
192
|
if (input.statusCode) {
|
|
522
193
|
err.statusCode = input.statusCode;
|
|
523
194
|
}
|
|
@@ -595,7 +266,7 @@ function createHandle(stack, options) {
|
|
|
595
266
|
if (!reqUrl.startsWith(layer.route)) {
|
|
596
267
|
continue;
|
|
597
268
|
}
|
|
598
|
-
req.url = reqUrl.
|
|
269
|
+
req.url = reqUrl.slice(layer.route.length) || "/";
|
|
599
270
|
} else {
|
|
600
271
|
req.url = reqUrl;
|
|
601
272
|
}
|
|
@@ -609,7 +280,7 @@ function createHandle(stack, options) {
|
|
|
609
280
|
const type = typeof val;
|
|
610
281
|
if (type === "string") {
|
|
611
282
|
return send(res, val, MIMES.html);
|
|
612
|
-
} else if (type === "object"
|
|
283
|
+
} else if (type === "object" || type === "boolean" || type === "number") {
|
|
613
284
|
if (val && val.buffer) {
|
|
614
285
|
return send(res, val);
|
|
615
286
|
} else if (val instanceof Error) {
|
|
@@ -629,12 +300,53 @@ function normalizeLayer(layer) {
|
|
|
629
300
|
layer.promisify = layer.handle.length > 2;
|
|
630
301
|
}
|
|
631
302
|
return {
|
|
632
|
-
route: withoutTrailingSlash(layer.route)
|
|
303
|
+
route: ufo.withoutTrailingSlash(layer.route),
|
|
633
304
|
match: layer.match,
|
|
634
305
|
handle: layer.lazy ? lazyHandle(layer.handle, layer.promisify) : layer.promisify ? promisifyHandle(layer.handle) : layer.handle
|
|
635
306
|
};
|
|
636
307
|
}
|
|
637
308
|
|
|
309
|
+
const RouterMethods = ["connect", "delete", "get", "head", "options", "post", "put", "trace"];
|
|
310
|
+
function createRouter() {
|
|
311
|
+
const _router = radix3.createRouter({});
|
|
312
|
+
const routes = {};
|
|
313
|
+
const router = {};
|
|
314
|
+
router.add = (path, handle, method = "all") => {
|
|
315
|
+
let route = routes[path];
|
|
316
|
+
if (!route) {
|
|
317
|
+
routes[path] = route = { handlers: {} };
|
|
318
|
+
_router.insert(path, route);
|
|
319
|
+
}
|
|
320
|
+
route.handlers[method] = handle;
|
|
321
|
+
return router;
|
|
322
|
+
};
|
|
323
|
+
for (const method of RouterMethods) {
|
|
324
|
+
router[method] = (path, handle) => router.add(path, handle, method);
|
|
325
|
+
}
|
|
326
|
+
router.handle = (req, res) => {
|
|
327
|
+
const matched = _router.lookup(req.url || "/");
|
|
328
|
+
if (!matched) {
|
|
329
|
+
throw createError({
|
|
330
|
+
statusCode: 404,
|
|
331
|
+
name: "Not Found",
|
|
332
|
+
statusMessage: `Cannot find any route matching ${req.url || "/"}.`
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
const method = (req.method || "get").toLowerCase();
|
|
336
|
+
const handler = matched.handlers[method] || matched.handlers.all;
|
|
337
|
+
if (!handler) {
|
|
338
|
+
throw createError({
|
|
339
|
+
statusCode: 405,
|
|
340
|
+
name: "Method Not Allowed",
|
|
341
|
+
statusMessage: `Method ${method} is not allowed on this route.`
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
req.params = matched.params || {};
|
|
345
|
+
return handler(req, res);
|
|
346
|
+
};
|
|
347
|
+
return router;
|
|
348
|
+
}
|
|
349
|
+
|
|
638
350
|
exports.H3Error = H3Error;
|
|
639
351
|
exports.MIMES = MIMES;
|
|
640
352
|
exports.appendHeader = appendHeader;
|
|
@@ -643,9 +355,11 @@ exports.callHandle = callHandle;
|
|
|
643
355
|
exports.createApp = createApp;
|
|
644
356
|
exports.createError = createError;
|
|
645
357
|
exports.createHandle = createHandle;
|
|
358
|
+
exports.createRouter = createRouter;
|
|
646
359
|
exports.defaultContentType = defaultContentType;
|
|
647
360
|
exports.defineHandle = defineHandle;
|
|
648
361
|
exports.defineMiddleware = defineMiddleware;
|
|
362
|
+
exports.deleteCookie = deleteCookie;
|
|
649
363
|
exports.isMethod = isMethod;
|
|
650
364
|
exports.lazyHandle = lazyHandle;
|
|
651
365
|
exports.promisifyHandle = promisifyHandle;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,11 +3,11 @@ import * as ufo from 'ufo';
|
|
|
3
3
|
|
|
4
4
|
declare type Encoding = false | 'ascii' | 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2' | 'base64' | 'latin1' | 'binary' | 'hex';
|
|
5
5
|
|
|
6
|
-
declare type Handle<T = any> = (req: IncomingMessage, res: ServerResponse) => T;
|
|
6
|
+
declare type Handle<T = any, ReqT = {}> = (req: IncomingMessage & ReqT, res: ServerResponse) => T;
|
|
7
7
|
declare type PHandle = Handle<Promise<any>>;
|
|
8
8
|
declare type Middleware = (req: IncomingMessage, res: ServerResponse, next: (err?: Error) => any) => any;
|
|
9
9
|
declare type LazyHandle = () => Handle | Promise<Handle>;
|
|
10
|
-
declare const defineHandle: <T>(handler: Handle<T>) => Handle<T>;
|
|
10
|
+
declare const defineHandle: <T>(handler: Handle<T, {}>) => Handle<T, {}>;
|
|
11
11
|
declare const defineMiddleware: (middleware: Middleware) => Middleware;
|
|
12
12
|
declare function promisifyHandle(handle: Handle | Middleware): PHandle;
|
|
13
13
|
declare function callHandle(handle: Middleware, req: IncomingMessage, res: ServerResponse): Promise<unknown>;
|
|
@@ -73,8 +73,8 @@ declare class H3Error extends Error {
|
|
|
73
73
|
*/
|
|
74
74
|
declare function createError(input: Partial<H3Error>): H3Error;
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
77
|
-
* H3 internally uses this
|
|
76
|
+
* Receive an error and return the corresponding response.<br>
|
|
77
|
+
* H3 internally uses this function to handle unhandled errors.<br>
|
|
78
78
|
* Note that calling this function will close the connection and no other data will be sent to client afterwards.
|
|
79
79
|
*
|
|
80
80
|
* @param res {ServerResponse} The ServerResponse object is passed as the second parameter in the handler function
|
|
@@ -232,9 +232,20 @@ declare function useCookie(req: IncomingMessage, name: string): string | undefin
|
|
|
232
232
|
* ```
|
|
233
233
|
*/
|
|
234
234
|
declare function setCookie(res: ServerResponse, name: string, value: string, serializeOptions?: CookieSerializeOptions): void;
|
|
235
|
+
/**
|
|
236
|
+
* Set a cookie value by name.
|
|
237
|
+
* @param res {ServerResponse} A ServerResponse object created by [http.Server](https://nodejs.org/api/http.html#http_class_http_server)
|
|
238
|
+
* @param name Name of the cookie to delete
|
|
239
|
+
* @param serializeOptions {CookieSerializeOptions} Cookie options
|
|
240
|
+
* ```ts
|
|
241
|
+
* deleteCookie(res, 'SessionId')
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
declare function deleteCookie(res: ServerResponse, name: string, serializeOptions?: CookieSerializeOptions): void;
|
|
235
245
|
|
|
236
|
-
declare function useQuery(req: IncomingMessage): ufo.QueryObject;
|
|
237
246
|
declare type HTTPMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE';
|
|
247
|
+
|
|
248
|
+
declare function useQuery(req: IncomingMessage): ufo.QueryObject;
|
|
238
249
|
declare function useMethod(req: IncomingMessage, defaultMethod?: HTTPMethod): HTTPMethod;
|
|
239
250
|
declare function isMethod(req: IncomingMessage, expected: HTTPMethod | HTTPMethod[], allowHead?: boolean): boolean;
|
|
240
251
|
declare function assertMethod(req: IncomingMessage, expected: HTTPMethod | HTTPMethod[], allowHead?: boolean): void;
|
|
@@ -244,4 +255,16 @@ declare function defaultContentType(res: ServerResponse, type?: string): void;
|
|
|
244
255
|
declare function sendRedirect(res: ServerResponse, location: string, code?: number): Promise<unknown>;
|
|
245
256
|
declare function appendHeader(res: ServerResponse, name: string, value: string): void;
|
|
246
257
|
|
|
247
|
-
|
|
258
|
+
declare type RouterMethod = Lowercase<HTTPMethod>;
|
|
259
|
+
declare type HandleWithParams = Handle<any, {
|
|
260
|
+
params: Record<string, string>;
|
|
261
|
+
}>;
|
|
262
|
+
declare type AddWithMethod = (path: string, handle: HandleWithParams) => Router;
|
|
263
|
+
declare type AddRouteShortcuts = Record<Lowercase<HTTPMethod>, AddWithMethod>;
|
|
264
|
+
interface Router extends AddRouteShortcuts {
|
|
265
|
+
add: (path: string, handle: HandleWithParams, method?: RouterMethod | 'all') => Router;
|
|
266
|
+
handle: Handle;
|
|
267
|
+
}
|
|
268
|
+
declare function createRouter(): Router;
|
|
269
|
+
|
|
270
|
+
export { AddRouteShortcuts, AddWithMethod, App, AppOptions, AppUse, H3Error, Handle, HandleWithParams, InputLayer, InputStack, Layer, LazyHandle, MIMES, Matcher, Middleware, PHandle, Router, RouterMethod, Stack, appendHeader, assertMethod, callHandle, createApp, createError, createHandle, createRouter, defaultContentType, defineHandle, defineMiddleware, deleteCookie, isMethod, lazyHandle, promisifyHandle, send, sendError, sendRedirect, setCookie, use, useBase, useBody, useCookie, useCookies, useMethod, useQuery, useRawBody };
|
package/dist/index.mjs
CHANGED
|
@@ -1,103 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} catch (_err) {
|
|
6
|
-
return "" + text;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
function decodeQueryValue(text) {
|
|
10
|
-
return decode$1(text.replace(PLUS_RE, " "));
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function parseQuery(paramsStr = "") {
|
|
14
|
-
const obj = {};
|
|
15
|
-
if (paramsStr[0] === "?") {
|
|
16
|
-
paramsStr = paramsStr.substr(1);
|
|
17
|
-
}
|
|
18
|
-
for (const param of paramsStr.split("&")) {
|
|
19
|
-
const s = param.match(/([^=]+)=?(.*)/) || [];
|
|
20
|
-
if (s.length < 2) {
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
const key = decode$1(s[1]);
|
|
24
|
-
if (key === "__proto__" || key === "constructor") {
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
const value = decodeQueryValue(s[2] || "");
|
|
28
|
-
if (obj[key]) {
|
|
29
|
-
if (Array.isArray(obj[key])) {
|
|
30
|
-
obj[key].push(value);
|
|
31
|
-
} else {
|
|
32
|
-
obj[key] = [obj[key], value];
|
|
33
|
-
}
|
|
34
|
-
} else {
|
|
35
|
-
obj[key] = value;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return obj;
|
|
39
|
-
}
|
|
40
|
-
function hasProtocol(inputStr, acceptProtocolRelative = false) {
|
|
41
|
-
return /^\w+:\/\/.+/.test(inputStr) || acceptProtocolRelative && /^\/\/[^/]+/.test(inputStr);
|
|
42
|
-
}
|
|
43
|
-
const TRAILING_SLASH_RE = /\/$|\/\?/;
|
|
44
|
-
function hasTrailingSlash(input = "", queryParams = false) {
|
|
45
|
-
if (!queryParams) {
|
|
46
|
-
return input.endsWith("/");
|
|
47
|
-
}
|
|
48
|
-
return TRAILING_SLASH_RE.test(input);
|
|
49
|
-
}
|
|
50
|
-
function withoutTrailingSlash(input = "", queryParams = false) {
|
|
51
|
-
if (!queryParams) {
|
|
52
|
-
return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || "/";
|
|
53
|
-
}
|
|
54
|
-
if (!hasTrailingSlash(input, true)) {
|
|
55
|
-
return input || "/";
|
|
56
|
-
}
|
|
57
|
-
const [s0, ...s] = input.split("?");
|
|
58
|
-
return (s0.slice(0, -1) || "/") + (s.length ? `?${s.join("?")}` : "");
|
|
59
|
-
}
|
|
60
|
-
function withoutBase(input, base) {
|
|
61
|
-
if (isEmptyURL(base)) {
|
|
62
|
-
return input;
|
|
63
|
-
}
|
|
64
|
-
const _base = withoutTrailingSlash(base);
|
|
65
|
-
if (input.startsWith(_base)) {
|
|
66
|
-
return input.substr(_base.length) || "/";
|
|
67
|
-
}
|
|
68
|
-
return input;
|
|
69
|
-
}
|
|
70
|
-
function getQuery(input) {
|
|
71
|
-
return parseQuery(parseURL(input).search);
|
|
72
|
-
}
|
|
73
|
-
function isEmptyURL(url) {
|
|
74
|
-
return !url || url === "/";
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function parseURL(input = "", defaultProto) {
|
|
78
|
-
if (!hasProtocol(input, true)) {
|
|
79
|
-
return defaultProto ? parseURL(defaultProto + input) : parsePath(input);
|
|
80
|
-
}
|
|
81
|
-
const [protocol = "", auth, hostAndPath] = (input.match(/([^:/]+:)?\/\/([^/@]+@)?(.*)/) || []).splice(1);
|
|
82
|
-
const [host = "", path = ""] = (hostAndPath.match(/([^/?]*)(.*)?/) || []).splice(1);
|
|
83
|
-
const { pathname, search, hash } = parsePath(path);
|
|
84
|
-
return {
|
|
85
|
-
protocol,
|
|
86
|
-
auth: auth ? auth.substr(0, auth.length - 1) : "",
|
|
87
|
-
host,
|
|
88
|
-
pathname,
|
|
89
|
-
search,
|
|
90
|
-
hash
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
function parsePath(input = "") {
|
|
94
|
-
const [pathname = "", search = "", hash = ""] = (input.match(/([^#?]*)(\?[^#]*)?(#.*)?/) || []).splice(1);
|
|
95
|
-
return {
|
|
96
|
-
pathname,
|
|
97
|
-
search,
|
|
98
|
-
hash
|
|
99
|
-
};
|
|
100
|
-
}
|
|
1
|
+
import { withoutTrailingSlash, withoutBase, getQuery } from 'ufo';
|
|
2
|
+
import { createRouter as createRouter$1 } from 'radix3';
|
|
3
|
+
import destr from 'destr';
|
|
4
|
+
import { parse, serialize } from 'cookie-es';
|
|
101
5
|
|
|
102
6
|
const defineHandle = (handler) => handler;
|
|
103
7
|
const defineMiddleware = (middleware) => middleware;
|
|
@@ -146,51 +50,6 @@ function useBase(base, handle) {
|
|
|
146
50
|
};
|
|
147
51
|
}
|
|
148
52
|
|
|
149
|
-
const suspectProtoRx = /"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*:/;
|
|
150
|
-
const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
|
|
151
|
-
const JsonSigRx = /^["{[]|^-?[0-9][0-9.]{0,14}$/;
|
|
152
|
-
function jsonParseTransform(key, value) {
|
|
153
|
-
if (key === "__proto__" || key === "constructor") {
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
return value;
|
|
157
|
-
}
|
|
158
|
-
function destr(val) {
|
|
159
|
-
if (typeof val !== "string") {
|
|
160
|
-
return val;
|
|
161
|
-
}
|
|
162
|
-
const _lval = val.toLowerCase();
|
|
163
|
-
if (_lval === "true") {
|
|
164
|
-
return true;
|
|
165
|
-
}
|
|
166
|
-
if (_lval === "false") {
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
if (_lval === "null") {
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
if (_lval === "nan") {
|
|
173
|
-
return NaN;
|
|
174
|
-
}
|
|
175
|
-
if (_lval === "infinity") {
|
|
176
|
-
return Infinity;
|
|
177
|
-
}
|
|
178
|
-
if (_lval === "undefined") {
|
|
179
|
-
return void 0;
|
|
180
|
-
}
|
|
181
|
-
if (!JsonSigRx.test(val)) {
|
|
182
|
-
return val;
|
|
183
|
-
}
|
|
184
|
-
try {
|
|
185
|
-
if (suspectProtoRx.test(val) || suspectConstructorRx.test(val)) {
|
|
186
|
-
return JSON.parse(val, jsonParseTransform);
|
|
187
|
-
}
|
|
188
|
-
return JSON.parse(val);
|
|
189
|
-
} catch (_e) {
|
|
190
|
-
return val;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
53
|
function useQuery(req) {
|
|
195
54
|
return getQuery(req.url || "");
|
|
196
55
|
}
|
|
@@ -259,203 +118,6 @@ const MIMES = {
|
|
|
259
118
|
json: "application/json"
|
|
260
119
|
};
|
|
261
120
|
|
|
262
|
-
/*!
|
|
263
|
-
* cookie
|
|
264
|
-
* Copyright(c) 2012-2014 Roman Shtylman
|
|
265
|
-
* Copyright(c) 2015 Douglas Christopher Wilson
|
|
266
|
-
* MIT Licensed
|
|
267
|
-
*/
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Module exports.
|
|
271
|
-
* @public
|
|
272
|
-
*/
|
|
273
|
-
|
|
274
|
-
var parse_1 = parse;
|
|
275
|
-
var serialize_1 = serialize;
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Module variables.
|
|
279
|
-
* @private
|
|
280
|
-
*/
|
|
281
|
-
|
|
282
|
-
var decode = decodeURIComponent;
|
|
283
|
-
var encode = encodeURIComponent;
|
|
284
|
-
var pairSplitRegExp = /; */;
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* RegExp to match field-content in RFC 7230 sec 3.2
|
|
288
|
-
*
|
|
289
|
-
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
|
290
|
-
* field-vchar = VCHAR / obs-text
|
|
291
|
-
* obs-text = %x80-FF
|
|
292
|
-
*/
|
|
293
|
-
|
|
294
|
-
var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Parse a cookie header.
|
|
298
|
-
*
|
|
299
|
-
* Parse the given cookie header string into an object
|
|
300
|
-
* The object has the various cookies as keys(names) => values
|
|
301
|
-
*
|
|
302
|
-
* @param {string} str
|
|
303
|
-
* @param {object} [options]
|
|
304
|
-
* @return {object}
|
|
305
|
-
* @public
|
|
306
|
-
*/
|
|
307
|
-
|
|
308
|
-
function parse(str, options) {
|
|
309
|
-
if (typeof str !== 'string') {
|
|
310
|
-
throw new TypeError('argument str must be a string');
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
var obj = {};
|
|
314
|
-
var opt = options || {};
|
|
315
|
-
var pairs = str.split(pairSplitRegExp);
|
|
316
|
-
var dec = opt.decode || decode;
|
|
317
|
-
|
|
318
|
-
for (var i = 0; i < pairs.length; i++) {
|
|
319
|
-
var pair = pairs[i];
|
|
320
|
-
var eq_idx = pair.indexOf('=');
|
|
321
|
-
|
|
322
|
-
// skip things that don't look like key=value
|
|
323
|
-
if (eq_idx < 0) {
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
var key = pair.substr(0, eq_idx).trim();
|
|
328
|
-
var val = pair.substr(++eq_idx, pair.length).trim();
|
|
329
|
-
|
|
330
|
-
// quoted values
|
|
331
|
-
if ('"' == val[0]) {
|
|
332
|
-
val = val.slice(1, -1);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// only assign once
|
|
336
|
-
if (undefined == obj[key]) {
|
|
337
|
-
obj[key] = tryDecode(val, dec);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return obj;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Serialize data into a cookie header.
|
|
346
|
-
*
|
|
347
|
-
* Serialize the a name value pair into a cookie string suitable for
|
|
348
|
-
* http headers. An optional options object specified cookie parameters.
|
|
349
|
-
*
|
|
350
|
-
* serialize('foo', 'bar', { httpOnly: true })
|
|
351
|
-
* => "foo=bar; httpOnly"
|
|
352
|
-
*
|
|
353
|
-
* @param {string} name
|
|
354
|
-
* @param {string} val
|
|
355
|
-
* @param {object} [options]
|
|
356
|
-
* @return {string}
|
|
357
|
-
* @public
|
|
358
|
-
*/
|
|
359
|
-
|
|
360
|
-
function serialize(name, val, options) {
|
|
361
|
-
var opt = options || {};
|
|
362
|
-
var enc = opt.encode || encode;
|
|
363
|
-
|
|
364
|
-
if (typeof enc !== 'function') {
|
|
365
|
-
throw new TypeError('option encode is invalid');
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (!fieldContentRegExp.test(name)) {
|
|
369
|
-
throw new TypeError('argument name is invalid');
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
var value = enc(val);
|
|
373
|
-
|
|
374
|
-
if (value && !fieldContentRegExp.test(value)) {
|
|
375
|
-
throw new TypeError('argument val is invalid');
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
var str = name + '=' + value;
|
|
379
|
-
|
|
380
|
-
if (null != opt.maxAge) {
|
|
381
|
-
var maxAge = opt.maxAge - 0;
|
|
382
|
-
if (isNaN(maxAge)) throw new Error('maxAge should be a Number');
|
|
383
|
-
str += '; Max-Age=' + Math.floor(maxAge);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
if (opt.domain) {
|
|
387
|
-
if (!fieldContentRegExp.test(opt.domain)) {
|
|
388
|
-
throw new TypeError('option domain is invalid');
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
str += '; Domain=' + opt.domain;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
if (opt.path) {
|
|
395
|
-
if (!fieldContentRegExp.test(opt.path)) {
|
|
396
|
-
throw new TypeError('option path is invalid');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
str += '; Path=' + opt.path;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (opt.expires) {
|
|
403
|
-
if (typeof opt.expires.toUTCString !== 'function') {
|
|
404
|
-
throw new TypeError('option expires is invalid');
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
str += '; Expires=' + opt.expires.toUTCString();
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if (opt.httpOnly) {
|
|
411
|
-
str += '; HttpOnly';
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
if (opt.secure) {
|
|
415
|
-
str += '; Secure';
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
if (opt.sameSite) {
|
|
419
|
-
var sameSite = typeof opt.sameSite === 'string'
|
|
420
|
-
? opt.sameSite.toLowerCase() : opt.sameSite;
|
|
421
|
-
|
|
422
|
-
switch (sameSite) {
|
|
423
|
-
case true:
|
|
424
|
-
str += '; SameSite=Strict';
|
|
425
|
-
break;
|
|
426
|
-
case 'lax':
|
|
427
|
-
str += '; SameSite=Lax';
|
|
428
|
-
break;
|
|
429
|
-
case 'strict':
|
|
430
|
-
str += '; SameSite=Strict';
|
|
431
|
-
break;
|
|
432
|
-
case 'none':
|
|
433
|
-
str += '; SameSite=None';
|
|
434
|
-
break;
|
|
435
|
-
default:
|
|
436
|
-
throw new TypeError('option sameSite is invalid');
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return str;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* Try decoding a string using a decoding function.
|
|
445
|
-
*
|
|
446
|
-
* @param {string} str
|
|
447
|
-
* @param {function} decode
|
|
448
|
-
* @private
|
|
449
|
-
*/
|
|
450
|
-
|
|
451
|
-
function tryDecode(str, decode) {
|
|
452
|
-
try {
|
|
453
|
-
return decode(str);
|
|
454
|
-
} catch (e) {
|
|
455
|
-
return str;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
121
|
const defer = typeof setImmediate !== "undefined" ? setImmediate : (fn) => fn();
|
|
460
122
|
function send(res, data, type) {
|
|
461
123
|
if (type) {
|
|
@@ -491,15 +153,21 @@ function appendHeader(res, name, value) {
|
|
|
491
153
|
}
|
|
492
154
|
|
|
493
155
|
function useCookies(req) {
|
|
494
|
-
return
|
|
156
|
+
return parse(req.headers.cookie || "");
|
|
495
157
|
}
|
|
496
158
|
function useCookie(req, name) {
|
|
497
159
|
return useCookies(req)[name];
|
|
498
160
|
}
|
|
499
161
|
function setCookie(res, name, value, serializeOptions) {
|
|
500
|
-
const cookieStr =
|
|
162
|
+
const cookieStr = serialize(name, value, serializeOptions);
|
|
501
163
|
appendHeader(res, "Set-Cookie", cookieStr);
|
|
502
164
|
}
|
|
165
|
+
function deleteCookie(res, name, serializeOptions) {
|
|
166
|
+
setCookie(res, name, "", {
|
|
167
|
+
...serializeOptions,
|
|
168
|
+
maxAge: 0
|
|
169
|
+
});
|
|
170
|
+
}
|
|
503
171
|
|
|
504
172
|
class H3Error extends Error {
|
|
505
173
|
constructor() {
|
|
@@ -509,11 +177,10 @@ class H3Error extends Error {
|
|
|
509
177
|
}
|
|
510
178
|
}
|
|
511
179
|
function createError(input) {
|
|
512
|
-
var _a;
|
|
513
180
|
if (input instanceof H3Error) {
|
|
514
181
|
return input;
|
|
515
182
|
}
|
|
516
|
-
const err = new H3Error(
|
|
183
|
+
const err = new H3Error(input.message ?? input.statusMessage);
|
|
517
184
|
if (input.statusCode) {
|
|
518
185
|
err.statusCode = input.statusCode;
|
|
519
186
|
}
|
|
@@ -591,7 +258,7 @@ function createHandle(stack, options) {
|
|
|
591
258
|
if (!reqUrl.startsWith(layer.route)) {
|
|
592
259
|
continue;
|
|
593
260
|
}
|
|
594
|
-
req.url = reqUrl.
|
|
261
|
+
req.url = reqUrl.slice(layer.route.length) || "/";
|
|
595
262
|
} else {
|
|
596
263
|
req.url = reqUrl;
|
|
597
264
|
}
|
|
@@ -605,7 +272,7 @@ function createHandle(stack, options) {
|
|
|
605
272
|
const type = typeof val;
|
|
606
273
|
if (type === "string") {
|
|
607
274
|
return send(res, val, MIMES.html);
|
|
608
|
-
} else if (type === "object"
|
|
275
|
+
} else if (type === "object" || type === "boolean" || type === "number") {
|
|
609
276
|
if (val && val.buffer) {
|
|
610
277
|
return send(res, val);
|
|
611
278
|
} else if (val instanceof Error) {
|
|
@@ -625,10 +292,51 @@ function normalizeLayer(layer) {
|
|
|
625
292
|
layer.promisify = layer.handle.length > 2;
|
|
626
293
|
}
|
|
627
294
|
return {
|
|
628
|
-
route: withoutTrailingSlash(layer.route)
|
|
295
|
+
route: withoutTrailingSlash(layer.route),
|
|
629
296
|
match: layer.match,
|
|
630
297
|
handle: layer.lazy ? lazyHandle(layer.handle, layer.promisify) : layer.promisify ? promisifyHandle(layer.handle) : layer.handle
|
|
631
298
|
};
|
|
632
299
|
}
|
|
633
300
|
|
|
634
|
-
|
|
301
|
+
const RouterMethods = ["connect", "delete", "get", "head", "options", "post", "put", "trace"];
|
|
302
|
+
function createRouter() {
|
|
303
|
+
const _router = createRouter$1({});
|
|
304
|
+
const routes = {};
|
|
305
|
+
const router = {};
|
|
306
|
+
router.add = (path, handle, method = "all") => {
|
|
307
|
+
let route = routes[path];
|
|
308
|
+
if (!route) {
|
|
309
|
+
routes[path] = route = { handlers: {} };
|
|
310
|
+
_router.insert(path, route);
|
|
311
|
+
}
|
|
312
|
+
route.handlers[method] = handle;
|
|
313
|
+
return router;
|
|
314
|
+
};
|
|
315
|
+
for (const method of RouterMethods) {
|
|
316
|
+
router[method] = (path, handle) => router.add(path, handle, method);
|
|
317
|
+
}
|
|
318
|
+
router.handle = (req, res) => {
|
|
319
|
+
const matched = _router.lookup(req.url || "/");
|
|
320
|
+
if (!matched) {
|
|
321
|
+
throw createError({
|
|
322
|
+
statusCode: 404,
|
|
323
|
+
name: "Not Found",
|
|
324
|
+
statusMessage: `Cannot find any route matching ${req.url || "/"}.`
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
const method = (req.method || "get").toLowerCase();
|
|
328
|
+
const handler = matched.handlers[method] || matched.handlers.all;
|
|
329
|
+
if (!handler) {
|
|
330
|
+
throw createError({
|
|
331
|
+
statusCode: 405,
|
|
332
|
+
name: "Method Not Allowed",
|
|
333
|
+
statusMessage: `Method ${method} is not allowed on this route.`
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
req.params = matched.params || {};
|
|
337
|
+
return handler(req, res);
|
|
338
|
+
};
|
|
339
|
+
return router;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export { H3Error, MIMES, appendHeader, assertMethod, callHandle, createApp, createError, createHandle, createRouter, defaultContentType, defineHandle, defineMiddleware, deleteCookie, isMethod, lazyHandle, promisifyHandle, send, sendError, sendRedirect, setCookie, use, useBase, useBody, useCookie, useCookies, useMethod, useQuery, useRawBody };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "h3",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Tiny JavaScript Server",
|
|
5
5
|
"repository": "unjs/h3",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,36 +18,38 @@
|
|
|
18
18
|
"dist"
|
|
19
19
|
],
|
|
20
20
|
"scripts": {
|
|
21
|
-
"build": "
|
|
22
|
-
"dev": "jiti
|
|
23
|
-
"lint": "eslint --ext ts .",
|
|
24
|
-
"profile": "0x -o -D .profile -P 'autocannon -c 100 -p 10 -d 40 http://localhost:$PORT' ./
|
|
25
|
-
"release": "yarn test && yarn build && standard-version && npm publish && git push --follow-tags",
|
|
26
|
-
"test": "
|
|
21
|
+
"build": "unbuild",
|
|
22
|
+
"dev": "jiti ./playground/index.ts",
|
|
23
|
+
"lint": "eslint --ext ts,mjs,cjs .",
|
|
24
|
+
"profile": "0x -o -D .profile -P 'autocannon -c 100 -p 10 -d 40 http://localhost:$PORT' ./playground/server.cjs",
|
|
25
|
+
"release": "yarn lint && yarn test && yarn build && standard-version && npm publish && git push --follow-tags",
|
|
26
|
+
"test": "vitest run"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"cookie-es": "^0.5.0",
|
|
30
|
+
"destr": "^1.1.0",
|
|
31
|
+
"radix3": "^0.1.1",
|
|
32
|
+
"ufo": "^0.7.11"
|
|
27
33
|
},
|
|
28
34
|
"devDependencies": {
|
|
29
35
|
"0x": "latest",
|
|
30
36
|
"@nuxtjs/eslint-config-typescript": "latest",
|
|
31
37
|
"@types/cookie": "latest",
|
|
32
38
|
"@types/express": "latest",
|
|
33
|
-
"@types/jest": "latest",
|
|
34
39
|
"@types/node": "latest",
|
|
35
40
|
"@types/supertest": "latest",
|
|
36
41
|
"autocannon": "latest",
|
|
42
|
+
"c8": "latest",
|
|
37
43
|
"connect": "latest",
|
|
38
|
-
"cookie-es": "latest",
|
|
39
|
-
"destr": "latest",
|
|
40
44
|
"eslint": "latest",
|
|
41
45
|
"express": "latest",
|
|
42
46
|
"get-port": "^5.0.0",
|
|
43
|
-
"jest": "latest",
|
|
44
47
|
"jiti": "latest",
|
|
45
48
|
"listhen": "latest",
|
|
46
|
-
"siroc": "latest",
|
|
47
49
|
"standard-version": "latest",
|
|
48
50
|
"supertest": "latest",
|
|
49
|
-
"ts-jest": "latest",
|
|
50
51
|
"typescript": "latest",
|
|
51
|
-
"
|
|
52
|
+
"unbuild": "latest",
|
|
53
|
+
"vitest": "latest"
|
|
52
54
|
}
|
|
53
55
|
}
|