hono 1.4.0 → 1.4.3
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 +9 -9
- package/dist/compose.d.ts +1 -1
- package/dist/compose.js +5 -8
- package/dist/context.d.ts +11 -11
- package/dist/context.js +38 -78
- package/dist/hono.d.ts +5 -29
- package/dist/hono.js +11 -27
- package/dist/middleware/basic-auth/index.js +2 -2
- package/dist/middleware/cookie/index.js +1 -1
- package/dist/middleware/cors/index.d.ts +1 -1
- package/dist/middleware/cors/index.js +8 -5
- package/dist/middleware/etag/index.js +3 -2
- package/dist/middleware/graphql-server/index.d.ts +2 -2
- package/dist/middleware/graphql-server/index.js +24 -31
- package/dist/middleware/jwt/index.js +5 -5
- package/dist/middleware/serve-static/serve-static.d.ts +2 -1
- package/dist/middleware/serve-static/serve-static.js +2 -1
- package/dist/request.d.ts +22 -0
- package/dist/request.js +59 -0
- package/dist/router/reg-exp-router/router.js +5 -2
- package/dist/router/reg-exp-router/trie.js +2 -0
- package/dist/router/trie-router/node.d.ts +2 -2
- package/dist/router/trie-router/node.js +58 -74
- package/dist/router/trie-router/router.d.ts +1 -1
- package/dist/utils/body.js +1 -0
- package/dist/utils/buffer.d.ts +1 -1
- package/dist/utils/cloudflare.d.ts +1 -1
- package/dist/utils/cloudflare.js +1 -1
- package/dist/utils/crypto.d.ts +3 -3
- package/dist/utils/crypto.js +1 -0
- package/dist/utils/encode.js +3 -2
- package/dist/utils/mime.d.ts +1 -1
- package/dist/utils/url.d.ts +1 -5
- package/dist/utils/url.js +6 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -38,13 +38,13 @@ app.fire()
|
|
|
38
38
|
**Hono is fastest**, compared to other routers for Cloudflare Workers.
|
|
39
39
|
|
|
40
40
|
```plain
|
|
41
|
-
hono - trie-router(default) x
|
|
42
|
-
hono - regexp-router x
|
|
43
|
-
itty-router x
|
|
44
|
-
sunder x
|
|
45
|
-
worktop x
|
|
46
|
-
Fastest is hono - regexp-router
|
|
47
|
-
✨ Done in
|
|
41
|
+
hono - trie-router(default) x 389,510 ops/sec ±3.16% (85 runs sampled)
|
|
42
|
+
hono - regexp-router x 452,290 ops/sec ±2.64% (84 runs sampled)
|
|
43
|
+
itty-router x 206,013 ops/sec ±3.39% (90 runs sampled)
|
|
44
|
+
sunder x 323,131 ops/sec ±0.75% (97 runs sampled)
|
|
45
|
+
worktop x 191,218 ops/sec ±2.70% (91 runs sampled)
|
|
46
|
+
Fastest is hono - regexp-router
|
|
47
|
+
✨ Done in 43.56s.
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
## Why so fast?
|
|
@@ -98,7 +98,7 @@ import { basicAuth } from 'hono/basic-auth'
|
|
|
98
98
|
|
|
99
99
|
const v1 = new Hono()
|
|
100
100
|
v1.get('/posts', (c) => {
|
|
101
|
-
return c.text('list
|
|
101
|
+
return c.text('list posts')
|
|
102
102
|
})
|
|
103
103
|
.post(basicAuth({ username, password }), (c) => {
|
|
104
104
|
return c.text('created!', 201)
|
|
@@ -644,7 +644,7 @@ npx wrangler publish ./src/index.ts
|
|
|
644
644
|
|
|
645
645
|
You can start making your Cloudflare Workers application with [the starter template](https://github.com/honojs/hono-minimal). It is really minimal using TypeScript, esbuild, Miniflare, and Jest.
|
|
646
646
|
|
|
647
|
-
To generate a project
|
|
647
|
+
To generate a project skeleton, run this command.
|
|
648
648
|
|
|
649
649
|
```
|
|
650
650
|
npx create-cloudflare my-app https://github.com/honojs/hono-minimal
|
package/dist/compose.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ErrorHandler, NotFoundHandler } from './hono';
|
|
2
|
-
export declare const compose: <C>(middleware: Function[], onError?: ErrorHandler, onNotFound?: NotFoundHandler) => (context: C, next?: Function) => Promise<C>;
|
|
2
|
+
export declare const compose: <C>(middleware: Function[], onError?: ErrorHandler<import("./context").Env> | undefined, onNotFound?: NotFoundHandler<import("./context").Env> | undefined) => (context: C, next?: Function | undefined) => Promise<C>;
|
package/dist/compose.js
CHANGED
|
@@ -13,29 +13,26 @@ const compose = (middleware, onError, onNotFound) => {
|
|
|
13
13
|
}
|
|
14
14
|
let handler = middleware[i];
|
|
15
15
|
index = i;
|
|
16
|
-
if (i === middleware.length)
|
|
16
|
+
if (i === middleware.length && next)
|
|
17
17
|
handler = next;
|
|
18
|
-
if (handler
|
|
19
|
-
if (context instanceof context_1.Context && context.
|
|
18
|
+
if (!handler) {
|
|
19
|
+
if (context instanceof context_1.Context && context.finalized === false && onNotFound) {
|
|
20
20
|
context.res = onNotFound(context);
|
|
21
|
-
context.res.finalized = true;
|
|
22
21
|
}
|
|
23
22
|
return Promise.resolve(context);
|
|
24
23
|
}
|
|
25
|
-
return Promise.resolve(handler(context, dispatch
|
|
24
|
+
return Promise.resolve(handler(context, () => dispatch(i + 1)))
|
|
26
25
|
.then(async (res) => {
|
|
27
26
|
// If handler return Response like `return c.text('foo')`
|
|
28
27
|
if (res && context instanceof context_1.Context) {
|
|
29
28
|
context.res = res;
|
|
30
|
-
context.res.finalized = true;
|
|
31
29
|
}
|
|
32
30
|
return context;
|
|
33
31
|
})
|
|
34
32
|
.catch((err) => {
|
|
35
|
-
if (
|
|
33
|
+
if (context instanceof context_1.Context && onError) {
|
|
36
34
|
if (err instanceof Error) {
|
|
37
35
|
context.res = onError(err, context);
|
|
38
|
-
context.res.finalized = true;
|
|
39
36
|
}
|
|
40
37
|
return context;
|
|
41
38
|
}
|
package/dist/context.d.ts
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
/// <reference types="@cloudflare/workers-types" />
|
|
2
2
|
import type { StatusCode } from './utils/http-status';
|
|
3
3
|
declare type Headers = Record<string, string>;
|
|
4
|
-
declare type Data = string | ArrayBuffer | ReadableStream;
|
|
4
|
+
export declare type Data = string | ArrayBuffer | ReadableStream;
|
|
5
5
|
export declare type Env = Record<string, any>;
|
|
6
6
|
export declare class Context<RequestParamKeyType extends string = string, E = Env> {
|
|
7
7
|
req: Request<RequestParamKeyType>;
|
|
8
|
-
res: Response;
|
|
9
8
|
env: E;
|
|
10
|
-
event: FetchEvent;
|
|
9
|
+
event: FetchEvent | undefined;
|
|
10
|
+
finalized: boolean;
|
|
11
11
|
private _status;
|
|
12
12
|
private _pretty;
|
|
13
13
|
private _prettySpace;
|
|
14
14
|
private _map;
|
|
15
|
+
private _headers;
|
|
16
|
+
private _res;
|
|
17
|
+
private notFoundHandler;
|
|
15
18
|
render: (template: string, params?: object, options?: object) => Promise<Response>;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
event: FetchEvent;
|
|
20
|
-
res?: Response;
|
|
21
|
-
});
|
|
22
|
-
private initRequest;
|
|
19
|
+
constructor(req: Request<RequestParamKeyType>, env?: E | undefined, event?: FetchEvent | undefined, notFoundHandler?: (c: Context<string, E>) => Response);
|
|
20
|
+
get res(): Response;
|
|
21
|
+
set res(_res: Response);
|
|
23
22
|
header(name: string, value: string): void;
|
|
24
23
|
status(status: StatusCode): void;
|
|
25
24
|
set(key: string, value: any): void;
|
|
26
25
|
get(key: string): any;
|
|
27
26
|
pretty(prettyJSON: boolean, space?: number): void;
|
|
28
|
-
newResponse(data: Data,
|
|
27
|
+
newResponse(data: Data | null, status: StatusCode, headers?: Headers): Response;
|
|
29
28
|
body(data: Data | null, status?: StatusCode, headers?: Headers): Response;
|
|
30
29
|
text(text: string, status?: StatusCode, headers?: Headers): Response;
|
|
31
30
|
json(object: object, status?: StatusCode, headers?: Headers): Response;
|
|
32
31
|
html(html: string, status?: StatusCode, headers?: Headers): Response;
|
|
33
32
|
redirect(location: string, status?: StatusCode): Response;
|
|
33
|
+
notFound(): Response | Promise<Response>;
|
|
34
34
|
}
|
|
35
35
|
export {};
|
package/dist/context.js
CHANGED
|
@@ -3,103 +3,69 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Context = void 0;
|
|
4
4
|
const url_1 = require("./utils/url");
|
|
5
5
|
class Context {
|
|
6
|
-
constructor(req,
|
|
6
|
+
constructor(req, env = undefined, event = undefined, notFoundHandler = () => new Response()) {
|
|
7
7
|
this._status = 200;
|
|
8
8
|
this._pretty = false;
|
|
9
9
|
this._prettySpace = 2;
|
|
10
|
-
this.req =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (!this.res) {
|
|
14
|
-
const res = new Response(null, { status: 404 });
|
|
15
|
-
res.finalized = false;
|
|
16
|
-
this.res = res;
|
|
10
|
+
this.req = req;
|
|
11
|
+
if (env) {
|
|
12
|
+
this.env = env;
|
|
17
13
|
}
|
|
14
|
+
this.event = event;
|
|
15
|
+
this.notFoundHandler = notFoundHandler;
|
|
16
|
+
this.finalized = false;
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const result = {};
|
|
26
|
-
for (const [key, value] of req.headers) {
|
|
27
|
-
result[key] = value;
|
|
28
|
-
}
|
|
29
|
-
return result;
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
req.query = ((key) => {
|
|
33
|
-
const url = new URL(req.url);
|
|
34
|
-
if (key) {
|
|
35
|
-
return url.searchParams.get(key);
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
const result = {};
|
|
39
|
-
for (const key of url.searchParams.keys()) {
|
|
40
|
-
result[key] = url.searchParams.get(key);
|
|
41
|
-
}
|
|
42
|
-
return result;
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
req.queries = ((key) => {
|
|
46
|
-
const url = new URL(req.url);
|
|
47
|
-
if (key) {
|
|
48
|
-
return url.searchParams.getAll(key);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
const result = {};
|
|
52
|
-
for (const key of url.searchParams.keys()) {
|
|
53
|
-
result[key] = url.searchParams.getAll(key);
|
|
54
|
-
}
|
|
55
|
-
return result;
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
return req;
|
|
18
|
+
get res() {
|
|
19
|
+
return (this._res || (this._res = new Response()));
|
|
20
|
+
}
|
|
21
|
+
set res(_res) {
|
|
22
|
+
this._res = _res;
|
|
23
|
+
this.finalized = true;
|
|
59
24
|
}
|
|
60
25
|
header(name, value) {
|
|
61
|
-
this.
|
|
26
|
+
this._headers || (this._headers = {});
|
|
27
|
+
this._headers[name] = value;
|
|
28
|
+
if (this.finalized) {
|
|
29
|
+
this.res.headers.set(name, value);
|
|
30
|
+
}
|
|
62
31
|
}
|
|
63
32
|
status(status) {
|
|
64
33
|
this._status = status;
|
|
65
34
|
}
|
|
66
35
|
set(key, value) {
|
|
36
|
+
this._map || (this._map = {});
|
|
67
37
|
this._map[key] = value;
|
|
68
38
|
}
|
|
69
39
|
get(key) {
|
|
40
|
+
if (!this._map) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
70
43
|
return this._map[key];
|
|
71
44
|
}
|
|
72
45
|
pretty(prettyJSON, space = 2) {
|
|
73
46
|
this._pretty = prettyJSON;
|
|
74
47
|
this._prettySpace = space;
|
|
75
48
|
}
|
|
76
|
-
newResponse(data,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
49
|
+
newResponse(data, status, headers = {}) {
|
|
50
|
+
const _headers = { ...this._headers, ...headers };
|
|
51
|
+
if (this._res) {
|
|
52
|
+
this._res.headers.forEach((v, k) => {
|
|
53
|
+
_headers[k] = v;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return new Response(data, {
|
|
57
|
+
status: status || this._status || 200,
|
|
58
|
+
headers: _headers,
|
|
81
59
|
});
|
|
82
|
-
init.headers = Object.assign(headers, init.headers);
|
|
83
|
-
headers = {};
|
|
84
|
-
return new Response(data, init);
|
|
85
60
|
}
|
|
86
61
|
body(data, status = this._status, headers = {}) {
|
|
87
|
-
return this.newResponse(data,
|
|
88
|
-
status: status,
|
|
89
|
-
headers: headers,
|
|
90
|
-
});
|
|
62
|
+
return this.newResponse(data, status, headers);
|
|
91
63
|
}
|
|
92
64
|
text(text, status = this._status, headers = {}) {
|
|
93
|
-
if (typeof text !== 'string') {
|
|
94
|
-
throw new TypeError('text method arg must be a string!');
|
|
95
|
-
}
|
|
96
65
|
headers['Content-Type'] || (headers['Content-Type'] = 'text/plain; charset=UTF-8');
|
|
97
66
|
return this.body(text, status, headers);
|
|
98
67
|
}
|
|
99
68
|
json(object, status = this._status, headers = {}) {
|
|
100
|
-
if (typeof object !== 'object') {
|
|
101
|
-
throw new TypeError('json method arg must be an object!');
|
|
102
|
-
}
|
|
103
69
|
const body = this._pretty
|
|
104
70
|
? JSON.stringify(object, null, this._prettySpace)
|
|
105
71
|
: JSON.stringify(object);
|
|
@@ -107,27 +73,21 @@ class Context {
|
|
|
107
73
|
return this.body(body, status, headers);
|
|
108
74
|
}
|
|
109
75
|
html(html, status = this._status, headers = {}) {
|
|
110
|
-
if (typeof html !== 'string') {
|
|
111
|
-
throw new TypeError('html method arg must be a string!');
|
|
112
|
-
}
|
|
113
76
|
headers['Content-Type'] || (headers['Content-Type'] = 'text/html; charset=UTF-8');
|
|
114
77
|
return this.body(html, status, headers);
|
|
115
78
|
}
|
|
116
79
|
redirect(location, status = 302) {
|
|
117
|
-
if (typeof location !== 'string') {
|
|
118
|
-
throw new TypeError('location must be a string!');
|
|
119
|
-
}
|
|
120
80
|
if (!(0, url_1.isAbsoluteURL)(location)) {
|
|
121
81
|
const url = new URL(this.req.url);
|
|
122
82
|
url.pathname = location;
|
|
123
83
|
location = url.toString();
|
|
124
84
|
}
|
|
125
|
-
return this.newResponse(null, {
|
|
126
|
-
|
|
127
|
-
headers: {
|
|
128
|
-
Location: location,
|
|
129
|
-
},
|
|
85
|
+
return this.newResponse(null, status, {
|
|
86
|
+
Location: location,
|
|
130
87
|
});
|
|
131
88
|
}
|
|
89
|
+
notFound() {
|
|
90
|
+
return this.notFoundHandler(this);
|
|
91
|
+
}
|
|
132
92
|
}
|
|
133
93
|
exports.Context = Context;
|
package/dist/hono.d.ts
CHANGED
|
@@ -2,37 +2,14 @@
|
|
|
2
2
|
import { Context } from './context';
|
|
3
3
|
import type { Env } from './context';
|
|
4
4
|
import type { Router } from './router';
|
|
5
|
-
declare
|
|
6
|
-
interface Request<ParamKeyType extends string = string> {
|
|
7
|
-
param: {
|
|
8
|
-
(key: ParamKeyType): string;
|
|
9
|
-
(): Record<ParamKeyType, string>;
|
|
10
|
-
};
|
|
11
|
-
query: {
|
|
12
|
-
(key: string): string;
|
|
13
|
-
(): Record<string, string>;
|
|
14
|
-
};
|
|
15
|
-
queries: {
|
|
16
|
-
(key: string): string[];
|
|
17
|
-
(): Record<string, string[]>;
|
|
18
|
-
};
|
|
19
|
-
header: {
|
|
20
|
-
(name: string): string;
|
|
21
|
-
(): Record<string, string>;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
interface Response {
|
|
25
|
-
finalized: boolean;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
export declare type Handler<RequestParamKeyType extends string = string, E = Env> = (c: Context<RequestParamKeyType, E>, next: Next) => Response | Promise<Response> | void | Promise<void>;
|
|
5
|
+
export declare type Handler<RequestParamKeyType extends string = string, E = Env> = (c: Context<RequestParamKeyType, E>, next: Next) => Response | Promise<Response> | Promise<void> | Promise<Response | undefined>;
|
|
29
6
|
export declare type NotFoundHandler<E = Env> = (c: Context<string, E>) => Response;
|
|
30
7
|
export declare type ErrorHandler<E = Env> = (err: Error, c: Context<string, E>) => Response;
|
|
31
8
|
export declare type Next = () => Promise<void>;
|
|
32
9
|
declare type ParamKeyName<NameWithPattern> = NameWithPattern extends `${infer Name}{${infer _Pattern}` ? Name : NameWithPattern;
|
|
33
10
|
declare type ParamKey<Component> = Component extends `:${infer NameWithPattern}` ? ParamKeyName<NameWithPattern> : never;
|
|
34
11
|
declare type ParamKeys<Path> = Path extends `${infer Component}/${infer Rest}` ? ParamKey<Component> | ParamKeys<Rest> : ParamKey<Path>;
|
|
35
|
-
interface HandlerInterface<T extends string, E = Env, U = Hono<E, T>> {
|
|
12
|
+
interface HandlerInterface<T extends string, E extends Env = Env, U = Hono<E, T>> {
|
|
36
13
|
<Path extends string>(path: Path, ...handlers: Handler<ParamKeys<Path> extends never ? string : ParamKeys<Path>, E>[]): U;
|
|
37
14
|
(path: string, ...handlers: Handler<string, E>[]): U;
|
|
38
15
|
<Path extends string>(...handlers: Handler<ParamKeys<Path> extends never ? string : ParamKeys<Path>, E>[]): U;
|
|
@@ -44,21 +21,20 @@ interface Route<E extends Env> {
|
|
|
44
21
|
handler: Handler<string, E>;
|
|
45
22
|
}
|
|
46
23
|
declare const Hono_base: new <E_1 extends Env, T extends string, U>() => {
|
|
47
|
-
delete: HandlerInterface<T, E_1, U>;
|
|
48
|
-
get: HandlerInterface<T, E_1, U>;
|
|
49
24
|
all: HandlerInterface<T, E_1, U>;
|
|
25
|
+
get: HandlerInterface<T, E_1, U>;
|
|
50
26
|
post: HandlerInterface<T, E_1, U>;
|
|
51
27
|
put: HandlerInterface<T, E_1, U>;
|
|
28
|
+
delete: HandlerInterface<T, E_1, U>;
|
|
52
29
|
head: HandlerInterface<T, E_1, U>;
|
|
53
30
|
options: HandlerInterface<T, E_1, U>;
|
|
54
31
|
patch: HandlerInterface<T, E_1, U>;
|
|
55
32
|
};
|
|
56
|
-
export declare class Hono<E = Env, P extends string = '/'> extends Hono_base<E, P, Hono<E, P>> {
|
|
33
|
+
export declare class Hono<E extends Env = Env, P extends string = '/'> extends Hono_base<E, P, Hono<E, P>> {
|
|
57
34
|
readonly router: Router<Handler<string, E>>;
|
|
58
35
|
readonly strict: boolean;
|
|
59
36
|
private _tempPath;
|
|
60
37
|
private path;
|
|
61
|
-
private _cachedResponse;
|
|
62
38
|
routes: Route<E>[];
|
|
63
39
|
constructor(init?: Partial<Pick<Hono, 'router' | 'strict'>>);
|
|
64
40
|
private notFoundHandler;
|
package/dist/hono.js
CHANGED
|
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Hono = void 0;
|
|
4
4
|
const compose_1 = require("./compose");
|
|
5
5
|
const context_1 = require("./context");
|
|
6
|
+
const request_1 = require("./request");
|
|
6
7
|
const router_1 = require("./router");
|
|
7
|
-
const router_2 = require("./router");
|
|
8
8
|
const trie_router_1 = require("./router/trie-router"); // Default Router
|
|
9
9
|
const url_1 = require("./utils/url");
|
|
10
10
|
const methods = ['get', 'post', 'put', 'delete', 'head', 'options', 'patch'];
|
|
@@ -17,6 +17,7 @@ class Hono extends defineDynamicClass() {
|
|
|
17
17
|
super();
|
|
18
18
|
this.router = new trie_router_1.TrieRouter();
|
|
19
19
|
this.strict = true; // strict routing - default is true
|
|
20
|
+
this._tempPath = '';
|
|
20
21
|
this.path = '/';
|
|
21
22
|
this.routes = [];
|
|
22
23
|
this.notFoundHandler = (c) => {
|
|
@@ -28,7 +29,8 @@ class Hono extends defineDynamicClass() {
|
|
|
28
29
|
const message = 'Internal Server Error';
|
|
29
30
|
return c.text(message, 500);
|
|
30
31
|
};
|
|
31
|
-
|
|
32
|
+
(0, request_1.extendRequestPrototype)(); // FIXME: should be executed at a better timing
|
|
33
|
+
const allMethods = [...methods, router_1.METHOD_NAME_ALL_LOWERCASE];
|
|
32
34
|
allMethods.map((method) => {
|
|
33
35
|
this[method] = (args1, ...args) => {
|
|
34
36
|
if (typeof args1 === 'string') {
|
|
@@ -46,9 +48,6 @@ class Hono extends defineDynamicClass() {
|
|
|
46
48
|
};
|
|
47
49
|
});
|
|
48
50
|
Object.assign(this, init);
|
|
49
|
-
this._tempPath = null;
|
|
50
|
-
this._cachedResponse = new Response(null, { status: 404 });
|
|
51
|
-
this._cachedResponse.finalized = false;
|
|
52
51
|
}
|
|
53
52
|
route(path, app) {
|
|
54
53
|
this._tempPath = path;
|
|
@@ -56,7 +55,7 @@ class Hono extends defineDynamicClass() {
|
|
|
56
55
|
app.routes.map((r) => {
|
|
57
56
|
this.addRoute(r.method, r.path, r.handler);
|
|
58
57
|
});
|
|
59
|
-
this._tempPath =
|
|
58
|
+
this._tempPath = '';
|
|
60
59
|
}
|
|
61
60
|
return this;
|
|
62
61
|
}
|
|
@@ -89,30 +88,16 @@ class Hono extends defineDynamicClass() {
|
|
|
89
88
|
const r = { path: path, method: method, handler: handler };
|
|
90
89
|
this.routes.push(r);
|
|
91
90
|
}
|
|
92
|
-
|
|
91
|
+
matchRoute(method, path) {
|
|
93
92
|
return this.router.match(method, path);
|
|
94
93
|
}
|
|
95
94
|
async dispatch(request, event, env) {
|
|
96
|
-
const path = (0, url_1.getPathFromURL)(request.url,
|
|
95
|
+
const path = (0, url_1.getPathFromURL)(request.url, this.strict);
|
|
97
96
|
const method = request.method;
|
|
98
|
-
const result =
|
|
99
|
-
request.
|
|
100
|
-
if (result) {
|
|
101
|
-
if (key) {
|
|
102
|
-
return result.params[key];
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
return result.params;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
});
|
|
97
|
+
const result = this.matchRoute(method, path);
|
|
98
|
+
request.paramData = result?.params;
|
|
109
99
|
const handlers = result ? result.handlers : [this.notFoundHandler];
|
|
110
|
-
const c = new context_1.Context(request,
|
|
111
|
-
env: env,
|
|
112
|
-
event: event,
|
|
113
|
-
res: this._cachedResponse,
|
|
114
|
-
});
|
|
115
|
-
c.notFound = () => this.notFoundHandler(c);
|
|
100
|
+
const c = new context_1.Context(request, env, event, this.notFoundHandler);
|
|
116
101
|
const composed = (0, compose_1.compose)(handlers, this.errorHandler, this.notFoundHandler);
|
|
117
102
|
let context;
|
|
118
103
|
try {
|
|
@@ -122,9 +107,8 @@ class Hono extends defineDynamicClass() {
|
|
|
122
107
|
if (err instanceof Error) {
|
|
123
108
|
return this.errorHandler(err, c);
|
|
124
109
|
}
|
|
110
|
+
throw err;
|
|
125
111
|
}
|
|
126
|
-
if (!context.res)
|
|
127
|
-
return context.notFound();
|
|
128
112
|
return context.res;
|
|
129
113
|
}
|
|
130
114
|
async handleEvent(event) {
|
|
@@ -15,7 +15,7 @@ const auth = (req) => {
|
|
|
15
15
|
if (!req.headers || typeof req.headers !== 'object') {
|
|
16
16
|
throw new TypeError('argument req is required to have headers property');
|
|
17
17
|
}
|
|
18
|
-
const match = CREDENTIALS_REGEXP.exec(req.headers.get('Authorization'));
|
|
18
|
+
const match = CREDENTIALS_REGEXP.exec(req.headers.get('Authorization') || '');
|
|
19
19
|
if (!match) {
|
|
20
20
|
return undefined;
|
|
21
21
|
}
|
|
@@ -49,7 +49,7 @@ const basicAuth = (options, ...users) => {
|
|
|
49
49
|
ctx.res = new Response('Unauthorized', {
|
|
50
50
|
status: 401,
|
|
51
51
|
headers: {
|
|
52
|
-
'WWW-Authenticate': 'Basic realm="' + options.realm
|
|
52
|
+
'WWW-Authenticate': 'Basic realm="' + options.realm?.replace(/"/g, '\\"') + '"',
|
|
53
53
|
},
|
|
54
54
|
});
|
|
55
55
|
};
|
|
@@ -4,7 +4,7 @@ exports.cookie = void 0;
|
|
|
4
4
|
const cookie = () => {
|
|
5
5
|
return async (c, next) => {
|
|
6
6
|
c.req.cookie = ((name) => {
|
|
7
|
-
const cookie = c.req.headers.get('Cookie');
|
|
7
|
+
const cookie = c.req.headers.get('Cookie') || '';
|
|
8
8
|
const obj = parse(cookie);
|
|
9
9
|
if (name) {
|
|
10
10
|
const value = obj[name];
|
|
@@ -8,5 +8,5 @@ declare type CORSOptions = {
|
|
|
8
8
|
credentials?: boolean;
|
|
9
9
|
exposeHeaders?: string[];
|
|
10
10
|
};
|
|
11
|
-
export declare const cors: (options?: CORSOptions) => (c: Context, next: Next) => Promise<void>;
|
|
11
|
+
export declare const cors: (options?: CORSOptions | undefined) => (c: Context, next: Next) => Promise<void>;
|
|
12
12
|
export {};
|
|
@@ -8,7 +8,10 @@ const cors = (options) => {
|
|
|
8
8
|
allowHeaders: [],
|
|
9
9
|
exposeHeaders: [],
|
|
10
10
|
};
|
|
11
|
-
const opts =
|
|
11
|
+
const opts = {
|
|
12
|
+
...defaults,
|
|
13
|
+
...options,
|
|
14
|
+
};
|
|
12
15
|
return async (c, next) => {
|
|
13
16
|
await next();
|
|
14
17
|
function set(key, value) {
|
|
@@ -23,7 +26,7 @@ const cors = (options) => {
|
|
|
23
26
|
if (opts.credentials) {
|
|
24
27
|
set('Access-Control-Allow-Credentials', 'true');
|
|
25
28
|
}
|
|
26
|
-
if (opts.exposeHeaders
|
|
29
|
+
if (opts.exposeHeaders?.length) {
|
|
27
30
|
set('Access-Control-Expose-Headers', opts.exposeHeaders.join(','));
|
|
28
31
|
}
|
|
29
32
|
if (c.req.method === 'OPTIONS') {
|
|
@@ -31,17 +34,17 @@ const cors = (options) => {
|
|
|
31
34
|
if (opts.maxAge != null) {
|
|
32
35
|
set('Access-Control-Max-Age', opts.maxAge.toString());
|
|
33
36
|
}
|
|
34
|
-
if (opts.allowMethods
|
|
37
|
+
if (opts.allowMethods?.length) {
|
|
35
38
|
set('Access-Control-Allow-Methods', opts.allowMethods.join(','));
|
|
36
39
|
}
|
|
37
40
|
let headers = opts.allowHeaders;
|
|
38
|
-
if (!headers
|
|
41
|
+
if (!headers?.length) {
|
|
39
42
|
const requestHeaders = c.req.headers.get('Access-Control-Request-Headers');
|
|
40
43
|
if (requestHeaders) {
|
|
41
44
|
headers = requestHeaders.split(/\s*,\s*/);
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
|
-
if (headers
|
|
47
|
+
if (headers?.length) {
|
|
45
48
|
set('Access-Control-Allow-Headers', headers.join(','));
|
|
46
49
|
set('Vary', 'Access-Control-Request-Headers');
|
|
47
50
|
}
|
|
@@ -7,8 +7,9 @@ const etag = (options = { weak: false }) => {
|
|
|
7
7
|
return async (c, next) => {
|
|
8
8
|
const ifNoneMatch = c.req.header('If-None-Match') || c.req.header('if-none-match');
|
|
9
9
|
await next();
|
|
10
|
-
const
|
|
11
|
-
const
|
|
10
|
+
const res = c.res;
|
|
11
|
+
const clone = res.clone();
|
|
12
|
+
const body = await (0, body_1.parseBody)(res);
|
|
12
13
|
const hash = await (0, crypto_1.sha1)(body);
|
|
13
14
|
const etag = options.weak ? `W/"${hash}"` : `"${hash}"`;
|
|
14
15
|
if (ifNoneMatch && ifNoneMatch === etag) {
|
|
@@ -8,7 +8,7 @@ declare type Options = {
|
|
|
8
8
|
pretty?: boolean;
|
|
9
9
|
validationRules?: ReadonlyArray<ValidationRule>;
|
|
10
10
|
};
|
|
11
|
-
export declare const graphqlServer: (options: Options) => (c: Context, next: Next) => Promise<
|
|
11
|
+
export declare const graphqlServer: (options: Options) => (c: Context, next: Next) => Promise<Response>;
|
|
12
12
|
export interface GraphQLParams {
|
|
13
13
|
query: string | null;
|
|
14
14
|
variables: {
|
|
@@ -18,7 +18,7 @@ export interface GraphQLParams {
|
|
|
18
18
|
raw: boolean;
|
|
19
19
|
}
|
|
20
20
|
export declare const getGraphQLParams: (request: Request) => Promise<GraphQLParams>;
|
|
21
|
-
export declare const errorMessages: (messages: string[], graphqlErrors?: readonly GraphQLError[] | readonly GraphQLFormattedError[]) => {
|
|
21
|
+
export declare const errorMessages: (messages: string[], graphqlErrors?: readonly GraphQLError[] | readonly GraphQLFormattedError[] | undefined) => {
|
|
22
22
|
errors: readonly GraphQLError[] | readonly GraphQLFormattedError[];
|
|
23
23
|
} | {
|
|
24
24
|
errors: {
|
|
@@ -6,20 +6,17 @@ exports.errorMessages = exports.getGraphQLParams = exports.graphqlServer = void
|
|
|
6
6
|
const graphql_1 = require("graphql");
|
|
7
7
|
const parse_body_1 = require("./parse-body");
|
|
8
8
|
const graphqlServer = (options) => {
|
|
9
|
-
var _a, _b;
|
|
10
9
|
const schema = options.schema;
|
|
11
10
|
const rootValue = options.rootValue;
|
|
12
|
-
const pretty =
|
|
13
|
-
const validationRules =
|
|
11
|
+
const pretty = options.pretty ?? false;
|
|
12
|
+
const validationRules = options.validationRules ?? [];
|
|
14
13
|
// const showGraphiQL = options.graphiql ?? false
|
|
15
14
|
return async (c, next) => {
|
|
16
|
-
await next();
|
|
17
15
|
// GraphQL HTTP only supports GET and POST methods.
|
|
18
16
|
if (c.req.method !== 'GET' && c.req.method !== 'POST') {
|
|
19
|
-
|
|
17
|
+
return c.json((0, exports.errorMessages)(['GraphQL only supports GET and POST requests.']), 405, {
|
|
20
18
|
Allow: 'GET, POST',
|
|
21
19
|
});
|
|
22
|
-
return;
|
|
23
20
|
}
|
|
24
21
|
let params;
|
|
25
22
|
try {
|
|
@@ -28,20 +25,18 @@ const graphqlServer = (options) => {
|
|
|
28
25
|
catch (e) {
|
|
29
26
|
if (e instanceof Error) {
|
|
30
27
|
console.error(`${e.stack || e.message}`);
|
|
31
|
-
|
|
28
|
+
return c.json((0, exports.errorMessages)([e.message], [e]), 400);
|
|
32
29
|
}
|
|
33
|
-
|
|
30
|
+
throw e;
|
|
34
31
|
}
|
|
35
32
|
const { query, variables, operationName } = params;
|
|
36
33
|
if (query == null) {
|
|
37
|
-
|
|
38
|
-
return;
|
|
34
|
+
return c.json((0, exports.errorMessages)(['Must provide query string.']), 400);
|
|
39
35
|
}
|
|
40
36
|
const schemaValidationErrors = (0, graphql_1.validateSchema)(schema);
|
|
41
37
|
if (schemaValidationErrors.length > 0) {
|
|
42
38
|
// Return 500: Internal Server Error if invalid schema.
|
|
43
|
-
|
|
44
|
-
return;
|
|
39
|
+
return c.json((0, exports.errorMessages)(['GraphQL schema validation error.'], schemaValidationErrors), 500);
|
|
45
40
|
}
|
|
46
41
|
let documentAST;
|
|
47
42
|
try {
|
|
@@ -54,16 +49,15 @@ const graphqlServer = (options) => {
|
|
|
54
49
|
const e = new graphql_1.GraphQLError(syntaxError.message, {
|
|
55
50
|
originalError: syntaxError,
|
|
56
51
|
});
|
|
57
|
-
|
|
52
|
+
return c.json((0, exports.errorMessages)(['GraphQL syntax error.'], [e]), 400);
|
|
58
53
|
}
|
|
59
|
-
|
|
54
|
+
throw syntaxError;
|
|
60
55
|
}
|
|
61
56
|
// Validate AST, reporting any errors.
|
|
62
57
|
const validationErrors = (0, graphql_1.validate)(schema, documentAST, [...graphql_1.specifiedRules, ...validationRules]);
|
|
63
58
|
if (validationErrors.length > 0) {
|
|
64
59
|
// Return 400: Bad Request if any validation errors exist.
|
|
65
|
-
|
|
66
|
-
return;
|
|
60
|
+
return c.json((0, exports.errorMessages)(['GraphQL validation error.'], validationErrors), 400);
|
|
67
61
|
}
|
|
68
62
|
if (c.req.method === 'GET') {
|
|
69
63
|
// Determine if this GET request will perform a non-query.
|
|
@@ -76,10 +70,9 @@ const graphqlServer = (options) => {
|
|
|
76
70
|
}
|
|
77
71
|
*/
|
|
78
72
|
// Otherwise, report a 405: Method Not Allowed error.
|
|
79
|
-
|
|
73
|
+
return c.json((0, exports.errorMessages)([
|
|
80
74
|
`Can only perform a ${operationAST.operation} operation from a POST request.`,
|
|
81
75
|
]), 405, { Allow: 'POST' });
|
|
82
|
-
return;
|
|
83
76
|
}
|
|
84
77
|
}
|
|
85
78
|
let result;
|
|
@@ -100,13 +93,14 @@ const graphqlServer = (options) => {
|
|
|
100
93
|
nodes: documentAST,
|
|
101
94
|
});
|
|
102
95
|
// Return 400: Bad Request if any execution context errors exist.
|
|
103
|
-
|
|
96
|
+
return c.json((0, exports.errorMessages)(['GraphQL execution context error.'], [e]), 400);
|
|
104
97
|
}
|
|
105
|
-
|
|
98
|
+
throw contextError;
|
|
106
99
|
}
|
|
107
|
-
if (result.data
|
|
108
|
-
|
|
109
|
-
|
|
100
|
+
if (!result.data) {
|
|
101
|
+
if (result.errors) {
|
|
102
|
+
return c.json((0, exports.errorMessages)([result.errors.toString()], result.errors), 500);
|
|
103
|
+
}
|
|
110
104
|
}
|
|
111
105
|
/*
|
|
112
106
|
Now, does not support GraphiQL
|
|
@@ -115,33 +109,32 @@ const graphqlServer = (options) => {
|
|
|
115
109
|
*/
|
|
116
110
|
if (pretty) {
|
|
117
111
|
const payload = JSON.stringify(result, null, pretty ? 2 : 0);
|
|
118
|
-
|
|
112
|
+
return c.text(payload, 200, {
|
|
119
113
|
'Content-Type': 'application/json',
|
|
120
114
|
});
|
|
121
115
|
}
|
|
122
116
|
else {
|
|
123
|
-
|
|
117
|
+
return c.json(result);
|
|
124
118
|
}
|
|
125
|
-
|
|
119
|
+
await next(); // XXX
|
|
126
120
|
};
|
|
127
121
|
};
|
|
128
122
|
exports.graphqlServer = graphqlServer;
|
|
129
123
|
const getGraphQLParams = async (request) => {
|
|
130
|
-
var _a, _b, _c;
|
|
131
124
|
const urlData = new URLSearchParams(request.url.split('?')[1]);
|
|
132
125
|
const bodyData = await (0, parse_body_1.parseBody)(request);
|
|
133
126
|
// GraphQL Query string.
|
|
134
|
-
let query =
|
|
127
|
+
let query = urlData.get('query') ?? bodyData.query;
|
|
135
128
|
if (typeof query !== 'string') {
|
|
136
129
|
query = null;
|
|
137
130
|
}
|
|
138
131
|
// Parse the variables if needed.
|
|
139
|
-
let variables = (
|
|
132
|
+
let variables = (urlData.get('variables') ?? bodyData.variables);
|
|
140
133
|
if (typeof variables === 'string') {
|
|
141
134
|
try {
|
|
142
135
|
variables = JSON.parse(variables);
|
|
143
136
|
}
|
|
144
|
-
catch
|
|
137
|
+
catch {
|
|
145
138
|
throw Error('Variables are invalid JSON.');
|
|
146
139
|
}
|
|
147
140
|
}
|
|
@@ -149,7 +142,7 @@ const getGraphQLParams = async (request) => {
|
|
|
149
142
|
variables = null;
|
|
150
143
|
}
|
|
151
144
|
// Name of GraphQL operation to execute.
|
|
152
|
-
let operationName =
|
|
145
|
+
let operationName = urlData.get('operationName') ?? bodyData.operationName;
|
|
153
146
|
if (typeof operationName !== 'string') {
|
|
154
147
|
operationName = null;
|
|
155
148
|
}
|
|
@@ -10,9 +10,9 @@ const jwt = (options) => {
|
|
|
10
10
|
const credentials = ctx.req.headers.get('Authorization');
|
|
11
11
|
if (!credentials) {
|
|
12
12
|
ctx.res = new Response('Unauthorized', {
|
|
13
|
-
status:
|
|
13
|
+
status: 400,
|
|
14
14
|
headers: {
|
|
15
|
-
'WWW-Authenticate':
|
|
15
|
+
'WWW-Authenticate': `Bearer realm="${ctx.req.url}",error="invalid_request",error_description="no authorization included in request"`,
|
|
16
16
|
},
|
|
17
17
|
});
|
|
18
18
|
return;
|
|
@@ -20,9 +20,9 @@ const jwt = (options) => {
|
|
|
20
20
|
const parts = credentials.split(/\s+/);
|
|
21
21
|
if (parts.length !== 2) {
|
|
22
22
|
ctx.res = new Response('Unauthorized', {
|
|
23
|
-
status:
|
|
23
|
+
status: 400,
|
|
24
24
|
headers: {
|
|
25
|
-
'WWW-Authenticate':
|
|
25
|
+
'WWW-Authenticate': `Bearer realm="${ctx.req.url}",error="invalid_request",error_description="no authorization included in request"`,
|
|
26
26
|
},
|
|
27
27
|
});
|
|
28
28
|
return;
|
|
@@ -40,7 +40,7 @@ const jwt = (options) => {
|
|
|
40
40
|
status: 401,
|
|
41
41
|
statusText: msg,
|
|
42
42
|
headers: {
|
|
43
|
-
'WWW-Authenticate':
|
|
43
|
+
'WWW-Authenticate': `Bearer realm="${ctx.req.url}",error="invalid_token",error_description="token verification failure"`,
|
|
44
44
|
},
|
|
45
45
|
});
|
|
46
46
|
return;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
+
import type { Env } from '../../context';
|
|
2
3
|
import type { Handler } from '../../hono';
|
|
3
4
|
export declare type ServeStaticOptions = {
|
|
4
5
|
root: string;
|
|
5
6
|
manifest?: object | string;
|
|
6
7
|
namespace?: KVNamespace;
|
|
7
8
|
};
|
|
8
|
-
export declare const serveStatic: (options?: ServeStaticOptions) => Handler
|
|
9
|
+
export declare const serveStatic: (options?: ServeStaticOptions) => Handler<string, Env>;
|
|
@@ -8,7 +8,7 @@ const DEFAULT_DOCUMENT = 'index.html';
|
|
|
8
8
|
const serveStatic = (options = { root: '' }) => {
|
|
9
9
|
return async (c, next) => {
|
|
10
10
|
// Do nothing if Response is already set
|
|
11
|
-
if (c.res && c.
|
|
11
|
+
if (c.res && c.finalized) {
|
|
12
12
|
await next();
|
|
13
13
|
}
|
|
14
14
|
const url = new URL(c.req.url);
|
|
@@ -33,6 +33,7 @@ const serveStatic = (options = { root: '' }) => {
|
|
|
33
33
|
console.warn(`Static file: ${path} is not found`);
|
|
34
34
|
await next();
|
|
35
35
|
}
|
|
36
|
+
return;
|
|
36
37
|
};
|
|
37
38
|
};
|
|
38
39
|
exports.serveStatic = serveStatic;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
declare global {
|
|
2
|
+
interface Request<ParamKeyType extends string = string> {
|
|
3
|
+
param: {
|
|
4
|
+
(key: ParamKeyType): string;
|
|
5
|
+
(): Record<ParamKeyType, string>;
|
|
6
|
+
};
|
|
7
|
+
paramData?: Record<ParamKeyType, string>;
|
|
8
|
+
query: {
|
|
9
|
+
(key: string): string;
|
|
10
|
+
(): Record<string, string>;
|
|
11
|
+
};
|
|
12
|
+
queries: {
|
|
13
|
+
(key: string): string[];
|
|
14
|
+
(): Record<string, string[]>;
|
|
15
|
+
};
|
|
16
|
+
header: {
|
|
17
|
+
(name: string): string;
|
|
18
|
+
(): Record<string, string>;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export declare function extendRequestPrototype(): void;
|
package/dist/request.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extendRequestPrototype = void 0;
|
|
4
|
+
function extendRequestPrototype() {
|
|
5
|
+
if (!!Request.prototype.param) {
|
|
6
|
+
// already extended
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
Request.prototype.param = function (key) {
|
|
10
|
+
if (this.paramData) {
|
|
11
|
+
if (key) {
|
|
12
|
+
return this.paramData[key];
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
return this.paramData;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
};
|
|
20
|
+
Request.prototype.header = function (name) {
|
|
21
|
+
if (name) {
|
|
22
|
+
return this.headers.get(name);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
const result = {};
|
|
26
|
+
for (const [key, value] of this.headers) {
|
|
27
|
+
result[key] = value;
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
Request.prototype.query = function (key) {
|
|
33
|
+
const url = new URL(this.url);
|
|
34
|
+
if (key) {
|
|
35
|
+
return url.searchParams.get(key);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const result = {};
|
|
39
|
+
for (const key of url.searchParams.keys()) {
|
|
40
|
+
result[key] = url.searchParams.get(key) || '';
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
Request.prototype.queries = function (key) {
|
|
46
|
+
const url = new URL(this.url);
|
|
47
|
+
if (key) {
|
|
48
|
+
return url.searchParams.getAll(key);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const result = {};
|
|
52
|
+
for (const key of url.searchParams.keys()) {
|
|
53
|
+
result[key] = url.searchParams.getAll(key);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
exports.extendRequestPrototype = extendRequestPrototype;
|
|
@@ -158,7 +158,7 @@ class RegExpRouter {
|
|
|
158
158
|
const handlerWithSortIndex = {
|
|
159
159
|
index,
|
|
160
160
|
handler,
|
|
161
|
-
componentsLength: hint.components.length,
|
|
161
|
+
componentsLength: hint.components.length || 1,
|
|
162
162
|
};
|
|
163
163
|
for (let i = 0, len = routes.length; i < len; i++) {
|
|
164
164
|
if (routes[i].method === method && routes[i].path === path) {
|
|
@@ -240,6 +240,7 @@ class RegExpRouter {
|
|
|
240
240
|
return this.match(method, path);
|
|
241
241
|
}
|
|
242
242
|
buildAllMatchers() {
|
|
243
|
+
// @ts-ignore
|
|
243
244
|
this.routeData.routes.sort(({ hint: a }, { hint: b }) => {
|
|
244
245
|
if (a.componentsLength !== b.componentsLength) {
|
|
245
246
|
return a.componentsLength - b.componentsLength;
|
|
@@ -265,6 +266,7 @@ class RegExpRouter {
|
|
|
265
266
|
const primaryMatchers = {};
|
|
266
267
|
const secondaryMatchers = {};
|
|
267
268
|
let hasAmbiguous = false;
|
|
269
|
+
// @ts-ignore
|
|
268
270
|
this.routeData.methods.forEach((method) => {
|
|
269
271
|
let _hasAmbiguous;
|
|
270
272
|
[primaryMatchers[method], secondaryMatchers[method], _hasAmbiguous] =
|
|
@@ -280,6 +282,7 @@ class RegExpRouter {
|
|
|
280
282
|
var _a, _b;
|
|
281
283
|
let hasAmbiguous = false;
|
|
282
284
|
const targetMethods = new Set([method, router_1.METHOD_NAME_ALL]);
|
|
285
|
+
// @ts-ignore
|
|
283
286
|
const routes = this.routeData.routes.filter(({ method }) => targetMethods.has(method));
|
|
284
287
|
// Reset temporary data per method
|
|
285
288
|
for (let i = 0, len = routes.length; i < len; i++) {
|
|
@@ -315,7 +318,7 @@ class RegExpRouter {
|
|
|
315
318
|
}
|
|
316
319
|
}
|
|
317
320
|
if (routes[j].hint.components.length < routes[i].hint.components.length) {
|
|
318
|
-
const componentsLength = routes[j].hint.components.length;
|
|
321
|
+
const componentsLength = routes[j].hint.components.length || 1;
|
|
319
322
|
routes[j].middleware.push(...routes[i].handlers.map((h) => ({
|
|
320
323
|
componentsLength,
|
|
321
324
|
index: h.index,
|
|
@@ -15,6 +15,8 @@ class Trie {
|
|
|
15
15
|
* - character
|
|
16
16
|
*/
|
|
17
17
|
const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g);
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
19
|
+
// @ts-ignore
|
|
18
20
|
this.root.insert(tokens, index, paramMap, this.context);
|
|
19
21
|
return paramMap;
|
|
20
22
|
}
|
|
@@ -11,10 +11,10 @@ export declare class Node<T> {
|
|
|
11
11
|
patterns: Pattern[];
|
|
12
12
|
order: number;
|
|
13
13
|
name: string;
|
|
14
|
+
handlerSetCache: Record<string, HandlerSet<T>[]>;
|
|
14
15
|
constructor(method?: string, handler?: T, children?: Record<string, Node<T>>);
|
|
15
16
|
insert(method: string, path: string, handler: T): Node<T>;
|
|
16
17
|
private getHandlerSets;
|
|
17
|
-
|
|
18
|
-
search(method: string, path: string): Result<T>;
|
|
18
|
+
search(method: string, path: string): Result<T> | null;
|
|
19
19
|
}
|
|
20
20
|
export {};
|
|
@@ -28,6 +28,7 @@ class Node {
|
|
|
28
28
|
this.methods = [m];
|
|
29
29
|
}
|
|
30
30
|
this.patterns = [];
|
|
31
|
+
this.handlerSetCache = {};
|
|
31
32
|
}
|
|
32
33
|
insert(method, path, handler) {
|
|
33
34
|
this.name = `${method} ${path}`;
|
|
@@ -70,7 +71,7 @@ class Node {
|
|
|
70
71
|
score = score + this.order * 0.01;
|
|
71
72
|
}
|
|
72
73
|
else {
|
|
73
|
-
score = parts.length +
|
|
74
|
+
score = parts.length + this.order * 0.01;
|
|
74
75
|
}
|
|
75
76
|
if (!curNode.methods.length) {
|
|
76
77
|
curNode.methods = [];
|
|
@@ -82,93 +83,76 @@ class Node {
|
|
|
82
83
|
return curNode;
|
|
83
84
|
}
|
|
84
85
|
getHandlerSets(node, method, wildcard) {
|
|
85
|
-
|
|
86
|
-
node.
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
if (
|
|
91
|
-
hs
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
return handlerSets;
|
|
98
|
-
}
|
|
99
|
-
next(node, part, method, isLast) {
|
|
100
|
-
const handlerSets = [];
|
|
101
|
-
const nextNodes = [];
|
|
102
|
-
const params = {};
|
|
103
|
-
for (let j = 0, len = node.patterns.length; j < len; j++) {
|
|
104
|
-
const pattern = node.patterns[j];
|
|
105
|
-
// Wildcard
|
|
106
|
-
// '/hello/*/foo' => match /hello/bar/foo
|
|
107
|
-
if (pattern === '*') {
|
|
108
|
-
const astNode = node.children['*'];
|
|
109
|
-
if (astNode) {
|
|
110
|
-
let wildcard = false;
|
|
111
|
-
if (!Object.keys(astNode.children).length) {
|
|
112
|
-
wildcard = true;
|
|
86
|
+
var _a, _b;
|
|
87
|
+
return ((_a = node.handlerSetCache)[_b = `${method}:${wildcard ? '1' : '0'}`] || (_a[_b] = (() => {
|
|
88
|
+
const handlerSets = [];
|
|
89
|
+
node.methods.map((m) => {
|
|
90
|
+
const handlerSet = m[method] || m[router_1.METHOD_NAME_ALL];
|
|
91
|
+
if (handlerSet !== undefined) {
|
|
92
|
+
const hs = { ...handlerSet };
|
|
93
|
+
if (wildcard) {
|
|
94
|
+
hs.score = handlerSet.score - 1;
|
|
113
95
|
}
|
|
114
|
-
handlerSets.push(
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
if (part === '')
|
|
119
|
-
continue;
|
|
120
|
-
// Named match
|
|
121
|
-
// `/posts/:id` => match /posts/123
|
|
122
|
-
const [key, name, matcher] = pattern;
|
|
123
|
-
if (matcher === true || (matcher instanceof RegExp && matcher.test(part))) {
|
|
124
|
-
if (typeof key === 'string') {
|
|
125
|
-
if (isLast === true) {
|
|
126
|
-
handlerSets.push(...this.getHandlerSets(node.children[key], method));
|
|
127
|
-
}
|
|
128
|
-
nextNodes.push(node.children[key]);
|
|
129
|
-
}
|
|
130
|
-
if (typeof name === 'string') {
|
|
131
|
-
params[name] = part;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
const nextNode = node.children[part];
|
|
136
|
-
if (nextNode) {
|
|
137
|
-
if (isLast === true) {
|
|
138
|
-
// '/hello/*' => match '/hello'
|
|
139
|
-
if (nextNode.children['*']) {
|
|
140
|
-
handlerSets.push(...this.getHandlerSets(nextNode.children['*'], method, true));
|
|
96
|
+
handlerSets.push(hs);
|
|
97
|
+
return;
|
|
141
98
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
const next = {
|
|
147
|
-
nodes: nextNodes,
|
|
148
|
-
handlerSets: handlerSets,
|
|
149
|
-
params: params,
|
|
150
|
-
};
|
|
151
|
-
return next;
|
|
99
|
+
});
|
|
100
|
+
return handlerSets;
|
|
101
|
+
})()));
|
|
152
102
|
}
|
|
153
103
|
search(method, path) {
|
|
154
104
|
const handlerSets = [];
|
|
155
|
-
|
|
105
|
+
const params = {};
|
|
156
106
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
157
107
|
const curNode = this;
|
|
158
108
|
let curNodes = [curNode];
|
|
159
109
|
const parts = (0, url_1.splitPath)(path);
|
|
160
110
|
for (let i = 0, len = parts.length; i < len; i++) {
|
|
161
|
-
const
|
|
111
|
+
const part = parts[i];
|
|
162
112
|
const isLast = i === len - 1;
|
|
163
113
|
const tempNodes = [];
|
|
164
114
|
for (let j = 0, len2 = curNodes.length; j < len2; j++) {
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
115
|
+
const node = curNodes[j];
|
|
116
|
+
for (let k = 0, len3 = node.patterns.length; k < len3; k++) {
|
|
117
|
+
const pattern = node.patterns[k];
|
|
118
|
+
// Wildcard
|
|
119
|
+
// '/hello/*/foo' => match /hello/bar/foo
|
|
120
|
+
if (pattern === '*') {
|
|
121
|
+
const astNode = node.children['*'];
|
|
122
|
+
if (astNode) {
|
|
123
|
+
handlerSets.push(...this.getHandlerSets(astNode, method));
|
|
124
|
+
tempNodes.push(astNode);
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (part === '')
|
|
129
|
+
continue;
|
|
130
|
+
// Named match
|
|
131
|
+
// `/posts/:id` => match /posts/123
|
|
132
|
+
const [key, name, matcher] = pattern;
|
|
133
|
+
if (matcher === true || (matcher instanceof RegExp && matcher.test(part))) {
|
|
134
|
+
if (typeof key === 'string') {
|
|
135
|
+
if (isLast === true) {
|
|
136
|
+
handlerSets.push(...this.getHandlerSets(node.children[key], method));
|
|
137
|
+
}
|
|
138
|
+
tempNodes.push(node.children[key]);
|
|
139
|
+
}
|
|
140
|
+
if (typeof name === 'string') {
|
|
141
|
+
params[name] = part;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const nextNode = node.children[part];
|
|
146
|
+
if (nextNode) {
|
|
147
|
+
if (isLast === true) {
|
|
148
|
+
// '/hello/*' => match '/hello'
|
|
149
|
+
if (nextNode.children['*']) {
|
|
150
|
+
handlerSets.push(...this.getHandlerSets(nextNode.children['*'], method, true));
|
|
151
|
+
}
|
|
152
|
+
handlerSets.push(...this.getHandlerSets(nextNode, method));
|
|
153
|
+
}
|
|
154
|
+
tempNodes.push(nextNode);
|
|
168
155
|
}
|
|
169
|
-
handlerSets.push(...res.handlerSets);
|
|
170
|
-
params = Object.assign(params, res.params);
|
|
171
|
-
tempNodes.push(...res.nodes);
|
|
172
156
|
}
|
|
173
157
|
curNodes = tempNodes;
|
|
174
158
|
}
|
package/dist/utils/body.js
CHANGED
package/dist/utils/buffer.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export declare const equal: (a: ArrayBuffer, b: ArrayBuffer) => boolean;
|
|
2
|
-
export declare const timingSafeEqual: (a: string | object | boolean, b: string | object | boolean, hashFunction?: Function) => Promise<boolean>;
|
|
2
|
+
export declare const timingSafeEqual: (a: string | object | boolean, b: string | object | boolean, hashFunction?: Function | undefined) => Promise<boolean>;
|
|
3
3
|
export declare const bufferToString: (buffer: ArrayBuffer) => string;
|
|
@@ -3,7 +3,7 @@ export declare type KVAssetOptions = {
|
|
|
3
3
|
manifest?: object | string;
|
|
4
4
|
namespace?: KVNamespace;
|
|
5
5
|
};
|
|
6
|
-
export declare const getContentFromKVAsset: (path: string, options?: KVAssetOptions) => Promise<ArrayBuffer>;
|
|
6
|
+
export declare const getContentFromKVAsset: (path: string, options?: KVAssetOptions | undefined) => Promise<ArrayBuffer | null>;
|
|
7
7
|
declare type FilePathOptions = {
|
|
8
8
|
filename: string;
|
|
9
9
|
root?: string;
|
package/dist/utils/cloudflare.js
CHANGED
package/dist/utils/crypto.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ declare type Algorithm = {
|
|
|
3
3
|
alias: string;
|
|
4
4
|
};
|
|
5
5
|
declare type Data = string | object | boolean;
|
|
6
|
-
export declare const sha256: (data: Data) => Promise<string>;
|
|
7
|
-
export declare const sha1: (data: Data) => Promise<string>;
|
|
8
|
-
export declare const createHash: (data: Data, algorithm: Algorithm) => Promise<string>;
|
|
6
|
+
export declare const sha256: (data: Data) => Promise<string | null>;
|
|
7
|
+
export declare const sha1: (data: Data) => Promise<string | null>;
|
|
8
|
+
export declare const createHash: (data: Data, algorithm: Algorithm) => Promise<string | null>;
|
|
9
9
|
export {};
|
package/dist/utils/crypto.js
CHANGED
package/dist/utils/encode.js
CHANGED
|
@@ -10,7 +10,7 @@ const encodeBase64 = (str) => {
|
|
|
10
10
|
const bytes = encoder.encode(str);
|
|
11
11
|
return btoa(String.fromCharCode(...bytes));
|
|
12
12
|
}
|
|
13
|
-
catch
|
|
13
|
+
catch { }
|
|
14
14
|
try {
|
|
15
15
|
return Buffer.from(str).toString('base64');
|
|
16
16
|
}
|
|
@@ -30,7 +30,7 @@ const decodeBase64 = (str) => {
|
|
|
30
30
|
const decoder = new TextDecoder();
|
|
31
31
|
return decoder.decode(bytes);
|
|
32
32
|
}
|
|
33
|
-
catch
|
|
33
|
+
catch { }
|
|
34
34
|
try {
|
|
35
35
|
return Buffer.from(str, 'base64').toString();
|
|
36
36
|
}
|
|
@@ -71,6 +71,7 @@ const arrayBufferToBase64 = async (buf) => {
|
|
|
71
71
|
return Buffer.from(String.fromCharCode(...new Uint8Array(buf))).toString('base64');
|
|
72
72
|
}
|
|
73
73
|
catch (e) { }
|
|
74
|
+
return '';
|
|
74
75
|
};
|
|
75
76
|
exports.arrayBufferToBase64 = arrayBufferToBase64;
|
|
76
77
|
const arrayBufferToBase64URL = async (buf) => {
|
package/dist/utils/mime.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const getMimeType: (filename: string) => string;
|
|
1
|
+
export declare const getMimeType: (filename: string) => string | undefined;
|
package/dist/utils/url.d.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
export declare type Pattern = readonly [string, string, RegExp | true] | '*';
|
|
2
2
|
export declare const splitPath: (path: string) => string[];
|
|
3
3
|
export declare const getPattern: (label: string) => Pattern | null;
|
|
4
|
-
declare
|
|
5
|
-
strict: boolean;
|
|
6
|
-
};
|
|
7
|
-
export declare const getPathFromURL: (url: string, params?: Params) => string;
|
|
4
|
+
export declare const getPathFromURL: (url: string, strict?: boolean) => string;
|
|
8
5
|
export declare const isAbsoluteURL: (url: string) => boolean;
|
|
9
6
|
export declare const mergePath: (...paths: string[]) => string;
|
|
10
|
-
export {};
|
package/dist/utils/url.js
CHANGED
|
@@ -34,17 +34,15 @@ const getPattern = (label) => {
|
|
|
34
34
|
return null;
|
|
35
35
|
};
|
|
36
36
|
exports.getPattern = getPattern;
|
|
37
|
-
const getPathFromURL = (url,
|
|
37
|
+
const getPathFromURL = (url, strict = true) => {
|
|
38
|
+
const queryIndex = url.indexOf('?');
|
|
39
|
+
const result = url.substring(url.indexOf('/', 8), queryIndex === -1 ? url.length : queryIndex);
|
|
38
40
|
// if strict routing is false => `/hello/hey/` and `/hello/hey` are treated the same
|
|
39
41
|
// default is true
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
+
if (strict === false && result.endsWith('/')) {
|
|
43
|
+
return result.slice(0, -1);
|
|
42
44
|
}
|
|
43
|
-
|
|
44
|
-
if (match) {
|
|
45
|
-
return match[1];
|
|
46
|
-
}
|
|
47
|
-
return '';
|
|
45
|
+
return result;
|
|
48
46
|
};
|
|
49
47
|
exports.getPathFromURL = getPathFromURL;
|
|
50
48
|
const isAbsoluteURL = (url) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hono",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
4
4
|
"description": "Ultrafast web framework for Cloudflare Workers.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
"form-data": "^4.0.0",
|
|
134
134
|
"graphql": "^16.4.0",
|
|
135
135
|
"jest": "27.5.1",
|
|
136
|
-
"jest-environment-miniflare": "^2.
|
|
136
|
+
"jest-environment-miniflare": "^2.5.0",
|
|
137
137
|
"mustache": "^4.2.0",
|
|
138
138
|
"prettier": "^2.6.2",
|
|
139
139
|
"prettier-plugin-md-nocjsp": "^1.2.0",
|