hono 0.0.10 → 0.0.14
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 +212 -75
- package/dist/compose.d.ts +1 -0
- package/dist/compose.js +27 -0
- package/dist/context.d.ts +23 -0
- package/dist/context.js +65 -0
- package/dist/hono.d.ts +44 -0
- package/dist/hono.js +147 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +9 -0
- package/dist/middleware/basic-auth/basic-auth.d.ts +6 -0
- package/dist/middleware/basic-auth/basic-auth.js +50 -0
- package/dist/middleware/body-parse/body-parse.d.ts +2 -0
- package/dist/middleware/body-parse/body-parse.js +27 -0
- package/dist/middleware/default.d.ts +2 -0
- package/dist/middleware/default.js +16 -0
- package/dist/middleware/logger/logger.d.ts +6 -0
- package/dist/middleware/logger/logger.js +58 -0
- package/dist/middleware/powered-by/powered-by.d.ts +2 -0
- package/dist/middleware/powered-by/powered-by.js +11 -0
- package/dist/middleware.d.ts +15 -0
- package/dist/middleware.js +16 -0
- package/dist/node.d.ts +24 -0
- package/dist/node.js +104 -0
- package/dist/util.d.ts +5 -0
- package/dist/util.js +72 -0
- package/package.json +34 -5
- package/CODE_OF_CONDUCT.md +0 -128
- package/src/compose.js +0 -21
- package/src/compose.test.js +0 -42
- package/src/hono.d.ts +0 -67
- package/src/hono.js +0 -141
- package/src/hono.test.js +0 -158
- package/src/methods.js +0 -30
- package/src/middleware/defaultFilter.js +0 -19
- package/src/middleware/poweredBy.js +0 -6
- package/src/middleware/poweredBy.test.js +0 -17
- package/src/middleware.js +0 -9
- package/src/middleware.test.js +0 -17
- package/src/node.js +0 -97
- package/src/node.test.js +0 -135
- package/src/router.test.js +0 -88
- package/src/util.js +0 -33
- package/src/util.test.js +0 -44
package/dist/hono.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Hono = exports.Router = void 0;
|
|
4
|
+
const node_1 = require("./node");
|
|
5
|
+
const compose_1 = require("./compose");
|
|
6
|
+
const util_1 = require("./util");
|
|
7
|
+
const middleware_1 = require("./middleware");
|
|
8
|
+
const context_1 = require("./context");
|
|
9
|
+
const METHOD_NAME_OF_ALL = 'ALL';
|
|
10
|
+
class Router {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.node = new node_1.Node();
|
|
13
|
+
}
|
|
14
|
+
add(method, path, handler) {
|
|
15
|
+
this.node.insert(method, path, handler);
|
|
16
|
+
}
|
|
17
|
+
match(method, path) {
|
|
18
|
+
return this.node.search(method, path);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.Router = Router;
|
|
22
|
+
class Hono {
|
|
23
|
+
constructor() {
|
|
24
|
+
this.router = new Router();
|
|
25
|
+
this.middlewareRouters = [];
|
|
26
|
+
this.tempPath = '/';
|
|
27
|
+
}
|
|
28
|
+
/* HTTP METHODS */
|
|
29
|
+
get(arg, ...args) {
|
|
30
|
+
return this.addRoute('get', arg, ...args);
|
|
31
|
+
}
|
|
32
|
+
post(arg, ...args) {
|
|
33
|
+
return this.addRoute('post', arg, ...args);
|
|
34
|
+
}
|
|
35
|
+
put(arg, ...args) {
|
|
36
|
+
return this.addRoute('put', arg, ...args);
|
|
37
|
+
}
|
|
38
|
+
head(arg, ...args) {
|
|
39
|
+
return this.addRoute('head', arg, ...args);
|
|
40
|
+
}
|
|
41
|
+
delete(arg, ...args) {
|
|
42
|
+
return this.addRoute('delete', arg, ...args);
|
|
43
|
+
}
|
|
44
|
+
options(arg, ...args) {
|
|
45
|
+
return this.addRoute('options', arg, ...args);
|
|
46
|
+
}
|
|
47
|
+
patch(arg, ...args) {
|
|
48
|
+
return this.addRoute('patch', arg, ...args);
|
|
49
|
+
}
|
|
50
|
+
/*
|
|
51
|
+
trace
|
|
52
|
+
copy
|
|
53
|
+
lock
|
|
54
|
+
purge
|
|
55
|
+
unlock
|
|
56
|
+
report
|
|
57
|
+
checkout
|
|
58
|
+
merge
|
|
59
|
+
notify
|
|
60
|
+
subscribe
|
|
61
|
+
unsubscribe
|
|
62
|
+
search
|
|
63
|
+
connect
|
|
64
|
+
*/
|
|
65
|
+
all(arg, ...args) {
|
|
66
|
+
return this.addRoute('all', arg, ...args);
|
|
67
|
+
}
|
|
68
|
+
route(path) {
|
|
69
|
+
this.tempPath = path;
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
use(path, middleware) {
|
|
73
|
+
if (middleware.constructor.name !== 'AsyncFunction') {
|
|
74
|
+
throw new TypeError('middleware must be a async function!');
|
|
75
|
+
}
|
|
76
|
+
const router = new Router();
|
|
77
|
+
router.add(METHOD_NAME_OF_ALL, path, middleware);
|
|
78
|
+
this.middlewareRouters.push(router);
|
|
79
|
+
}
|
|
80
|
+
// addRoute('get', '/', handler)
|
|
81
|
+
addRoute(method, arg, ...args) {
|
|
82
|
+
method = method.toUpperCase();
|
|
83
|
+
if (typeof arg === 'string') {
|
|
84
|
+
this.tempPath = arg;
|
|
85
|
+
this.router.add(method, arg, args);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
args.unshift(arg);
|
|
89
|
+
this.router.add(method, this.tempPath, args);
|
|
90
|
+
}
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
async matchRoute(method, path) {
|
|
94
|
+
return this.router.match(method, path);
|
|
95
|
+
}
|
|
96
|
+
async dispatch(request, env, event) {
|
|
97
|
+
const [method, path] = [request.method, (0, util_1.getPathFromURL)(request.url)];
|
|
98
|
+
const result = await this.matchRoute(method, path);
|
|
99
|
+
request.params = (key) => {
|
|
100
|
+
if (result) {
|
|
101
|
+
return result.params[key];
|
|
102
|
+
}
|
|
103
|
+
return '';
|
|
104
|
+
};
|
|
105
|
+
const handler = result ? result.handler[0] : this.notFound; // XXX
|
|
106
|
+
const middleware = [];
|
|
107
|
+
for (const mr of this.middlewareRouters) {
|
|
108
|
+
const mwResult = mr.match(METHOD_NAME_OF_ALL, path);
|
|
109
|
+
if (mwResult) {
|
|
110
|
+
middleware.push(mwResult.handler);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const wrappedHandler = async (context, next) => {
|
|
114
|
+
context.res = await handler(context);
|
|
115
|
+
await next();
|
|
116
|
+
};
|
|
117
|
+
middleware.push(middleware_1.Middleware.default);
|
|
118
|
+
middleware.push(wrappedHandler);
|
|
119
|
+
const composed = (0, compose_1.compose)(middleware);
|
|
120
|
+
const c = new context_1.Context(request, { env: env, event: event, res: null });
|
|
121
|
+
await composed(c);
|
|
122
|
+
return c.res;
|
|
123
|
+
}
|
|
124
|
+
async handleEvent(event) {
|
|
125
|
+
return this.dispatch(event.request, {}, event).catch((err) => {
|
|
126
|
+
return this.onError(err);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
async fetch(request, env, event) {
|
|
130
|
+
return this.dispatch(request, env, event).catch((err) => {
|
|
131
|
+
return this.onError(err);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
fire() {
|
|
135
|
+
addEventListener('fetch', (event) => {
|
|
136
|
+
event.respondWith(this.handleEvent(event));
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
onError(err) {
|
|
140
|
+
console.error(err);
|
|
141
|
+
return new Response('Internal Server Error', { status: 500 });
|
|
142
|
+
}
|
|
143
|
+
notFound() {
|
|
144
|
+
return new Response('Not Found', { status: 404 });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
exports.Hono = Hono;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Context = exports.Middleware = exports.Hono = void 0;
|
|
4
|
+
var hono_1 = require("./hono");
|
|
5
|
+
Object.defineProperty(exports, "Hono", { enumerable: true, get: function () { return hono_1.Hono; } });
|
|
6
|
+
var middleware_1 = require("./middleware");
|
|
7
|
+
Object.defineProperty(exports, "Middleware", { enumerable: true, get: function () { return middleware_1.Middleware; } });
|
|
8
|
+
var context_1 = require("./context");
|
|
9
|
+
Object.defineProperty(exports, "Context", { enumerable: true, get: function () { return context_1.Context; } });
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.basicAuth = void 0;
|
|
4
|
+
const util_1 = require("../../util");
|
|
5
|
+
const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/;
|
|
6
|
+
const USER_PASS_REGEXP = /^([^:]*):(.*)$/;
|
|
7
|
+
const auth = (req) => {
|
|
8
|
+
if (!req) {
|
|
9
|
+
throw new TypeError('argument req is required');
|
|
10
|
+
}
|
|
11
|
+
if (typeof req !== 'object') {
|
|
12
|
+
throw new TypeError('argument req is required to be an object');
|
|
13
|
+
}
|
|
14
|
+
if (!req.headers || typeof req.headers !== 'object') {
|
|
15
|
+
throw new TypeError('argument req is required to have headers property');
|
|
16
|
+
}
|
|
17
|
+
const match = CREDENTIALS_REGEXP.exec(req.headers.get('Authorization'));
|
|
18
|
+
if (!match) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
const userPass = USER_PASS_REGEXP.exec(decodeBase64(match[1]));
|
|
22
|
+
if (!userPass) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
return { username: userPass[1], password: userPass[2] };
|
|
26
|
+
};
|
|
27
|
+
function decodeBase64(str) {
|
|
28
|
+
return Buffer.from(str, 'base64').toString();
|
|
29
|
+
}
|
|
30
|
+
const basicAuth = (options) => {
|
|
31
|
+
if (!options.realm) {
|
|
32
|
+
options.realm = 'Secure Area';
|
|
33
|
+
}
|
|
34
|
+
return async (ctx, next) => {
|
|
35
|
+
const user = auth(ctx.req);
|
|
36
|
+
const usernameEqual = user && await (0, util_1.timingSafeEqual)(options.username, user.username);
|
|
37
|
+
const passwordEqual = user && await (0, util_1.timingSafeEqual)(options.password, user.password);
|
|
38
|
+
if (!user || !usernameEqual || !passwordEqual) {
|
|
39
|
+
ctx.res = new Response('Unauthorized', {
|
|
40
|
+
status: 401,
|
|
41
|
+
headers: {
|
|
42
|
+
'WWW-Authenticate': 'Basic realm="' + options.realm.replace(/"/g, '\\"') + '"',
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
exports.basicAuth = basicAuth;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bodyParse = void 0;
|
|
4
|
+
const bodyParse = () => {
|
|
5
|
+
return async (ctx, next) => {
|
|
6
|
+
const contentType = ctx.req.headers.get('Content-Type') || '';
|
|
7
|
+
if (contentType.includes('application/json')) {
|
|
8
|
+
ctx.req.parsedBody = await ctx.req.json();
|
|
9
|
+
}
|
|
10
|
+
else if (contentType.includes('application/text')) {
|
|
11
|
+
ctx.req.parsedBody = await ctx.req.text();
|
|
12
|
+
}
|
|
13
|
+
else if (contentType.includes('text/html')) {
|
|
14
|
+
ctx.req.parsedBody = await ctx.req.text();
|
|
15
|
+
}
|
|
16
|
+
else if (contentType.includes('form')) {
|
|
17
|
+
const form = {};
|
|
18
|
+
const data = [...(await ctx.req.formData())].reduce((acc, cur) => {
|
|
19
|
+
acc[cur[0]] = cur[1];
|
|
20
|
+
return acc;
|
|
21
|
+
}, form);
|
|
22
|
+
ctx.req.parsedBody = data;
|
|
23
|
+
}
|
|
24
|
+
await next();
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
exports.bodyParse = bodyParse;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultMiddleware = void 0;
|
|
4
|
+
const defaultMiddleware = async (c, next) => {
|
|
5
|
+
c.req.query = (key) => {
|
|
6
|
+
// eslint-disable-next-line
|
|
7
|
+
const url = new URL(c.req.url);
|
|
8
|
+
return url.searchParams.get(key);
|
|
9
|
+
};
|
|
10
|
+
await next();
|
|
11
|
+
if (c.res.body) {
|
|
12
|
+
const buff = await c.res.clone().arrayBuffer();
|
|
13
|
+
c.res.headers.append('Content-Length', buff.byteLength.toString());
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
exports.defaultMiddleware = defaultMiddleware;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logger = void 0;
|
|
4
|
+
const util_1 = require("../../util");
|
|
5
|
+
const humanize = (n, opts) => {
|
|
6
|
+
const options = opts || {};
|
|
7
|
+
const d = options.delimiter || ',';
|
|
8
|
+
const s = options.separator || '.';
|
|
9
|
+
n = n.toString().split('.');
|
|
10
|
+
n[0] = n[0].replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + d);
|
|
11
|
+
return n.join(s);
|
|
12
|
+
};
|
|
13
|
+
const time = (start) => {
|
|
14
|
+
const delta = Date.now() - start;
|
|
15
|
+
return humanize([
|
|
16
|
+
delta < 10000 ? delta + 'ms' : Math.round(delta / 1000) + 's',
|
|
17
|
+
]);
|
|
18
|
+
};
|
|
19
|
+
const LogPrefix = {
|
|
20
|
+
Outgoing: '-->',
|
|
21
|
+
Incoming: '<--',
|
|
22
|
+
Error: 'xxx',
|
|
23
|
+
};
|
|
24
|
+
const colorStatus = (status = 0) => {
|
|
25
|
+
const out = {
|
|
26
|
+
7: `\x1b[35m${status}\x1b[0m`,
|
|
27
|
+
5: `\x1b[31m${status}\x1b[0m`,
|
|
28
|
+
4: `\x1b[33m${status}\x1b[0m`,
|
|
29
|
+
3: `\x1b[36m${status}\x1b[0m`,
|
|
30
|
+
2: `\x1b[32m${status}\x1b[0m`,
|
|
31
|
+
1: `\x1b[32m${status}\x1b[0m`,
|
|
32
|
+
0: `\x1b[33m${status}\x1b[0m`,
|
|
33
|
+
};
|
|
34
|
+
return out[(status / 100) | 0];
|
|
35
|
+
};
|
|
36
|
+
function log(fn, prefix, method, path, status, elasped) {
|
|
37
|
+
const out = prefix === LogPrefix.Incoming
|
|
38
|
+
? ` ${prefix} ${method} ${path}`
|
|
39
|
+
: ` ${prefix} ${method} ${path} ${colorStatus(status)} ${elasped}`;
|
|
40
|
+
fn(out);
|
|
41
|
+
}
|
|
42
|
+
const logger = (fn = console.log) => {
|
|
43
|
+
return async (c, next) => {
|
|
44
|
+
const { method } = c.req;
|
|
45
|
+
const path = (0, util_1.getPathFromURL)(c.req.url);
|
|
46
|
+
log(fn, LogPrefix.Incoming, method, path);
|
|
47
|
+
const start = Date.now();
|
|
48
|
+
try {
|
|
49
|
+
await next();
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
log(fn, LogPrefix.Error, method, path, c.res.status || 500, time(start));
|
|
53
|
+
throw e;
|
|
54
|
+
}
|
|
55
|
+
log(fn, LogPrefix.Outgoing, method, path, c.res.status, time(start));
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
exports.logger = logger;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.poweredBy = void 0;
|
|
4
|
+
const poweredBy = () => {
|
|
5
|
+
return async (c, next) => {
|
|
6
|
+
await next();
|
|
7
|
+
// await c.res.headers.append('X-Powered-By', 'Hono')
|
|
8
|
+
c.res.headers.append('X-Powered-By', 'Hono');
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
exports.poweredBy = poweredBy;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare class Middleware {
|
|
2
|
+
static default: (c: import("./context").Context, next: Function) => Promise<void>;
|
|
3
|
+
static poweredBy: () => (c: import("./context").Context, next: Function) => Promise<void>;
|
|
4
|
+
static logger: (fn?: {
|
|
5
|
+
(...data: any[]): void;
|
|
6
|
+
(...data: any[]): void;
|
|
7
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
8
|
+
}) => (c: import("./context").Context, next: Function) => Promise<void>;
|
|
9
|
+
static basicAuth: (options: {
|
|
10
|
+
username: string;
|
|
11
|
+
password: string;
|
|
12
|
+
realm?: string;
|
|
13
|
+
}) => (ctx: import("./context").Context, next: Function) => Promise<any>;
|
|
14
|
+
static bodyParse: () => (ctx: import("./context").Context, next: Function) => Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Middleware = void 0;
|
|
4
|
+
const default_1 = require("./middleware/default");
|
|
5
|
+
const powered_by_1 = require("./middleware/powered-by/powered-by");
|
|
6
|
+
const logger_1 = require("./middleware/logger/logger");
|
|
7
|
+
const basic_auth_1 = require("./middleware/basic-auth/basic-auth");
|
|
8
|
+
const body_parse_1 = require("./middleware/body-parse/body-parse");
|
|
9
|
+
class Middleware {
|
|
10
|
+
}
|
|
11
|
+
exports.Middleware = Middleware;
|
|
12
|
+
Middleware.default = default_1.defaultMiddleware;
|
|
13
|
+
Middleware.poweredBy = powered_by_1.poweredBy;
|
|
14
|
+
Middleware.logger = logger_1.logger;
|
|
15
|
+
Middleware.basicAuth = basic_auth_1.basicAuth;
|
|
16
|
+
Middleware.bodyParse = body_parse_1.bodyParse;
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare class Result<T> {
|
|
2
|
+
handler: T;
|
|
3
|
+
params: {
|
|
4
|
+
[key: string]: string;
|
|
5
|
+
};
|
|
6
|
+
constructor(handler: T, params: {
|
|
7
|
+
[key: string]: string;
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
export declare class Node<T> {
|
|
11
|
+
method: {
|
|
12
|
+
[key: string]: T;
|
|
13
|
+
};
|
|
14
|
+
handler: T;
|
|
15
|
+
children: {
|
|
16
|
+
[key: string]: Node<T>;
|
|
17
|
+
};
|
|
18
|
+
middlewares: [];
|
|
19
|
+
constructor(method?: string, handler?: any, children?: {
|
|
20
|
+
[key: string]: Node<T>;
|
|
21
|
+
});
|
|
22
|
+
insert(method: string, path: string, handler: T): Node<T>;
|
|
23
|
+
search(method: string, path: string): Result<T>;
|
|
24
|
+
}
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Node = exports.Result = void 0;
|
|
4
|
+
const util_1 = require("./util");
|
|
5
|
+
const METHOD_NAME_OF_ALL = 'ALL';
|
|
6
|
+
class Result {
|
|
7
|
+
constructor(handler, params) {
|
|
8
|
+
this.handler = handler;
|
|
9
|
+
this.params = params;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.Result = Result;
|
|
13
|
+
const noRoute = () => {
|
|
14
|
+
return null;
|
|
15
|
+
};
|
|
16
|
+
class Node {
|
|
17
|
+
constructor(method, handler, children) {
|
|
18
|
+
this.children = children || {};
|
|
19
|
+
this.method = {};
|
|
20
|
+
if (method && handler) {
|
|
21
|
+
this.method[method] = handler;
|
|
22
|
+
}
|
|
23
|
+
this.middlewares = [];
|
|
24
|
+
}
|
|
25
|
+
insert(method, path, handler) {
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
27
|
+
let curNode = this;
|
|
28
|
+
const parts = (0, util_1.splitPath)(path);
|
|
29
|
+
for (let i = 0, len = parts.length; i < len; i++) {
|
|
30
|
+
const p = parts[i];
|
|
31
|
+
if (Object.keys(curNode.children).includes(p)) {
|
|
32
|
+
curNode = curNode.children[p];
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
curNode.children[p] = new Node();
|
|
36
|
+
curNode = curNode.children[p];
|
|
37
|
+
}
|
|
38
|
+
curNode.method[method] = handler;
|
|
39
|
+
return curNode;
|
|
40
|
+
}
|
|
41
|
+
search(method, path) {
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
43
|
+
let curNode = this;
|
|
44
|
+
const params = {};
|
|
45
|
+
const parts = (0, util_1.splitPath)(path);
|
|
46
|
+
for (let i = 0, len = parts.length; i < len; i++) {
|
|
47
|
+
const p = parts[i];
|
|
48
|
+
// '*' => match any path
|
|
49
|
+
if (curNode.children['*']) {
|
|
50
|
+
const astNode = curNode.children['*'];
|
|
51
|
+
if (Object.keys(astNode.children).length === 0) {
|
|
52
|
+
curNode = astNode;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const nextNode = curNode.children[p];
|
|
57
|
+
if (nextNode) {
|
|
58
|
+
curNode = nextNode;
|
|
59
|
+
// '/hello/*' => match '/hello'
|
|
60
|
+
if (!(i == len - 1 && nextNode.children['*'])) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
let isWildcard = false;
|
|
65
|
+
let isParamMatch = false;
|
|
66
|
+
const keys = Object.keys(curNode.children);
|
|
67
|
+
for (let j = 0, len = keys.length; j < len; j++) {
|
|
68
|
+
const key = keys[j];
|
|
69
|
+
// Wildcard
|
|
70
|
+
// '/hello/*/foo' => match /hello/bar/foo
|
|
71
|
+
if (key === '*') {
|
|
72
|
+
curNode = curNode.children['*'];
|
|
73
|
+
isWildcard = true;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
const pattern = (0, util_1.getPattern)(key);
|
|
77
|
+
// Named match
|
|
78
|
+
if (pattern) {
|
|
79
|
+
const match = p.match(new RegExp(pattern[1]));
|
|
80
|
+
if (match) {
|
|
81
|
+
const k = pattern[0];
|
|
82
|
+
params[k] = match[1];
|
|
83
|
+
curNode = curNode.children[key];
|
|
84
|
+
isParamMatch = true;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
return noRoute();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (isWildcard && i === len - 1) {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
if (isWildcard === false && isParamMatch === false) {
|
|
94
|
+
return noRoute();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const handler = curNode.method[METHOD_NAME_OF_ALL] || curNode.method[method];
|
|
98
|
+
if (!handler) {
|
|
99
|
+
return noRoute();
|
|
100
|
+
}
|
|
101
|
+
return new Result(handler, params);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.Node = Node;
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const splitPath: (path: string) => string[];
|
|
2
|
+
export declare const getPattern: (label: string) => string[] | null;
|
|
3
|
+
export declare const getPathFromURL: (url: string) => string;
|
|
4
|
+
export declare const isAbsoluteURL: (url: string) => boolean;
|
|
5
|
+
export declare const timingSafeEqual: (a: any, b: any) => Promise<boolean>;
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.timingSafeEqual = exports.isAbsoluteURL = exports.getPathFromURL = exports.getPattern = exports.splitPath = void 0;
|
|
4
|
+
const URL_REGEXP = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
|
|
5
|
+
const splitPath = (path) => {
|
|
6
|
+
const paths = path.split(/\//); // faster than path.split('/')
|
|
7
|
+
if (paths[0] === '') {
|
|
8
|
+
paths.shift();
|
|
9
|
+
}
|
|
10
|
+
return paths;
|
|
11
|
+
};
|
|
12
|
+
exports.splitPath = splitPath;
|
|
13
|
+
const getPattern = (label) => {
|
|
14
|
+
// :id{[0-9]+} => ([0-9]+)
|
|
15
|
+
// :id => (.+)
|
|
16
|
+
//const name = ''
|
|
17
|
+
const match = label.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
|
|
18
|
+
if (match) {
|
|
19
|
+
if (match[2]) {
|
|
20
|
+
return [match[1], '(' + match[2] + ')'];
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return [match[1], '(.+)'];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
exports.getPattern = getPattern;
|
|
29
|
+
const getPathFromURL = (url) => {
|
|
30
|
+
// XXX
|
|
31
|
+
const match = url.match(URL_REGEXP);
|
|
32
|
+
if (match) {
|
|
33
|
+
return match[5];
|
|
34
|
+
}
|
|
35
|
+
return '';
|
|
36
|
+
};
|
|
37
|
+
exports.getPathFromURL = getPathFromURL;
|
|
38
|
+
const isAbsoluteURL = (url) => {
|
|
39
|
+
const match = url.match(URL_REGEXP);
|
|
40
|
+
if (match && match[1]) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
};
|
|
45
|
+
exports.isAbsoluteURL = isAbsoluteURL;
|
|
46
|
+
const bufferEqual = (a, b) => {
|
|
47
|
+
if (a === b) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
if (a.byteLength !== b.byteLength) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const va = new DataView(a);
|
|
54
|
+
const vb = new DataView(b);
|
|
55
|
+
let i = va.byteLength;
|
|
56
|
+
while (i--) {
|
|
57
|
+
if (va.getUint8(i) !== vb.getUint8(i)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
};
|
|
63
|
+
const timingSafeEqual = async (a, b) => {
|
|
64
|
+
const sa = await crypto.subtle.digest({
|
|
65
|
+
name: 'SHA-256',
|
|
66
|
+
}, new TextEncoder().encode(String(a)));
|
|
67
|
+
const sb = await crypto.subtle.digest({
|
|
68
|
+
name: 'SHA-256',
|
|
69
|
+
}, new TextEncoder().encode(String(b)));
|
|
70
|
+
return bufferEqual(sa, sb) && a === b;
|
|
71
|
+
};
|
|
72
|
+
exports.timingSafeEqual = timingSafeEqual;
|
package/package.json
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hono",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "
|
|
3
|
+
"version": "0.0.14",
|
|
4
|
+
"description": "[炎] Ultrafast web framework for Cloudflare Workers.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
6
10
|
"scripts": {
|
|
7
|
-
"test": "jest"
|
|
11
|
+
"test": "jest",
|
|
12
|
+
"lint": "eslint --ext js,ts src .eslintrc.js test",
|
|
13
|
+
"build": "rimraf dist && tsc",
|
|
14
|
+
"watch": "tsc -w",
|
|
15
|
+
"prepublishOnly": "yarn build"
|
|
8
16
|
},
|
|
9
17
|
"author": "Yusuke Wada <yusuke@kamawada.com> (https://github.com/yusukebe)",
|
|
10
18
|
"license": "MIT",
|
|
@@ -26,7 +34,28 @@
|
|
|
26
34
|
"compute@edge"
|
|
27
35
|
],
|
|
28
36
|
"devDependencies": {
|
|
37
|
+
"@cloudflare/workers-types": "^3.3.0",
|
|
38
|
+
"@types/jest": "^27.4.0",
|
|
39
|
+
"@types/node": "^17.0.8",
|
|
40
|
+
"@typescript-eslint/eslint-plugin": "^5.9.0",
|
|
41
|
+
"@typescript-eslint/parser": "^5.9.0",
|
|
42
|
+
"eslint": "^7.26.0",
|
|
43
|
+
"eslint-config-prettier": "^8.1.0",
|
|
44
|
+
"eslint-define-config": "^1.2.1",
|
|
45
|
+
"eslint-import-resolver-typescript": "^2.0.0",
|
|
46
|
+
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
47
|
+
"eslint-plugin-flowtype": "^5.7.2",
|
|
48
|
+
"eslint-plugin-import": "^2.20.2",
|
|
49
|
+
"eslint-plugin-node": "^11.1.0",
|
|
50
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
51
|
+
"form-data": "^4.0.0",
|
|
29
52
|
"jest": "^27.4.5",
|
|
30
|
-
"
|
|
53
|
+
"jest-environment-miniflare": "^2.0.0",
|
|
54
|
+
"rimraf": "^3.0.2",
|
|
55
|
+
"ts-jest": "^27.1.2",
|
|
56
|
+
"typescript": "^4.5.4"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=11.0.0"
|
|
31
60
|
}
|
|
32
61
|
}
|