1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-24 15:19:26 -05:00

chore(bench): add flash router benchmarks (#15495)

This commit is contained in:
Divy Srivastava 2022-08-19 15:54:54 +05:30 committed by GitHub
parent 9e576dff7c
commit 25a109d9ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
97 changed files with 3350 additions and 0 deletions

View file

@ -20,6 +20,7 @@
".cargo_home", ".cargo_home",
".git", ".git",
"cli/bench/testdata/express-router.js", "cli/bench/testdata/express-router.js",
"cli/bench/testdata/npm/*",
"cli/dts/lib.d.ts", "cli/dts/lib.d.ts",
"cli/dts/lib.dom*", "cli/dts/lib.dom*",
"cli/dts/lib.es*", "cli/dts/lib.es*",

View file

@ -0,0 +1,13 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
const port = Bun.argv[2] || "4545";
const { Hono } = require("../testdata/npm/hono/dist/index.js");
const app = new Hono();
app.get("/", (c) => c.text("Hello, World!"));
Bun.serve({
fetch(r) {
return app.fetch(r);
},
port: Number(port),
});

View file

@ -0,0 +1,10 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
import { Hono } from "https://deno.land/x/hono@v2.0.9/mod.ts";
const addr = Deno.args[0] || "127.0.0.1:4500";
const [hostname, port] = addr.split(":");
const app = new Hono();
app.get("/", (c) => c.text("Hello, World!"));
Deno.serve(app.fetch, { port: Number(port), hostname });

21
cli/bench/testdata/npm/hono/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Yusuke Wada
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

88
cli/bench/testdata/npm/hono/README.md vendored Normal file
View file

@ -0,0 +1,88 @@
<div align="center">
<a href="https://honojs.dev">
<img src="https://raw.githubusercontent.com/honojs/hono/main/docs/images/hono-title.png" width="500" height="auto" alt="Hono"/>
</a>
</div>
<hr />
<p align="center">
<a href="https://honojs.dev"><b>Documentation :point_right: honojs.dev</b></a><br />
<i>v2.x has been released!</i> <a href="docs/MIGRATION.md">Migration guide</b>
</p>
<hr />
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/honojs/hono/ci)](https://github.com/honojs/hono/actions)
[![GitHub](https://img.shields.io/github/license/honojs/hono)](https://github.com/honojs/hono/blob/main/LICENSE)
[![npm](https://img.shields.io/npm/v/hono)](https://www.npmjs.com/package/hono)
[![npm](https://img.shields.io/npm/dm/hono)](https://www.npmjs.com/package/hono)
[![npm type definitions](https://img.shields.io/npm/types/hono)](https://www.npmjs.com/package/hono)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/honojs/hono)](https://github.com/honojs/hono/pulse)
[![GitHub last commit](https://img.shields.io/github/last-commit/honojs/hono)](https://github.com/honojs/hono/commits/main)
[![Deno badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fdeno-visualizer.danopia.net%2Fshields%2Flatest-version%2Fx%2Fhono%2Fmod.ts)](https://doc.deno.land/https/deno.land/x/hono/mod.ts)
Hono - _**[炎] means flame🔥 in Japanese**_ - is a small, simple, and ultrafast web framework for Cloudflare Workers, Deno, Bun, and others.
```ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hono!!'))
export default app
```
## Features
- **Ultrafast** - the router does not use linear loops.
- **Zero-dependencies** - using only Service Worker and Web Standard API.
- **Middleware** - built-in middleware, custom middleware, and third-party middleware.
- **TypeScript** - first-class TypeScript support.
- **Multi-platform** - works on Cloudflare Workers, Fastly Compute@Edge, Deno, or Bun.
## Benchmarks
**Hono is fastest**, compared to other routers for Cloudflare Workers.
```plain
hono - trie-router(default) x 424,449 ops/sec ±4.98% (77 runs sampled)
hono - regexp-router x 516,228 ops/sec ±4.79% (81 runs sampled)
itty-router x 206,641 ops/sec ±3.59% (87 runs sampled)
sunder x 319,500 ops/sec ±1.33% (93 runs sampled)
worktop x 187,280 ops/sec ±3.09% (87 runs sampled)
Fastest is hono - regexp-router
✨ Done in 38.32s.
```
## Documentation
The documentation is available on [honojs.dev](https://honojs.dev).
## Migration
Migration guide is available on [docs/MIGRATION.md](docs/MIGRATION.md).
## Contributing
Contributions Welcome! You can contribute in the following ways.
- Create an Issue - Propose a new feature. Report a bug.
- Pull Request - Fix a bug and typo. Refactor the code.
- Create third-party middleware - Instruct below.
- Share - Share your thoughts on the Blog, Twitter, and others.
- Make your application - Please try to use Hono.
For more details, see [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md).
## Contributors
Thanks to [all contributors](https://github.com/honojs/hono/graphs/contributors)! Especially, [@metrue](https://github.com/metrue) and [@usualoma](https://github.com/usualoma)!
## Author
Yusuke Wada <https://github.com/yusukebe>
## License
Distributed under the MIT License. See [LICENSE](LICENSE) for more information.

View file

@ -0,0 +1,2 @@
import type { ErrorHandler, NotFoundHandler } from './hono';
export declare const compose: <C>(middleware: Function[], onError?: ErrorHandler, onNotFound?: NotFoundHandler) => (context: C, next?: Function) => Promise<C>;

View file

@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.compose = void 0;
const context_1 = require("./context");
// Based on the code in the MIT licensed `koa-compose` package.
const compose = (middleware, onError, onNotFound) => {
const middlewareLength = middleware.length;
return (context, next) => {
let index = -1;
return dispatch(0);
async function dispatch(i) {
if (i <= index) {
throw new Error('next() called multiple times');
}
let handler = middleware[i];
index = i;
if (i === middlewareLength && next)
handler = next;
if (!handler) {
if (context instanceof context_1.HonoContext && context.finalized === false && onNotFound) {
context.res = await onNotFound(context);
}
return context;
}
let res;
let isError = false;
try {
const tmp = handler(context, () => dispatch(i + 1));
res = tmp instanceof Promise ? await tmp : tmp;
}
catch (err) {
if (context instanceof context_1.HonoContext && onError) {
if (err instanceof Error) {
isError = true;
res = onError(err, context);
}
}
if (!res) {
throw err;
}
}
if (res && context instanceof context_1.HonoContext && (!context.finalized || isError)) {
context.res = res;
}
return context;
}
};
};
exports.compose = compose;

View file

@ -0,0 +1,69 @@
/// <reference types="@cloudflare/workers-types" />
import type { ContextVariableMap, NotFoundHandler } from './hono';
import type { CookieOptions } from './utils/cookie';
import type { StatusCode } from './utils/http-status';
declare type Headers = Record<string, string>;
export declare type Data = string | ArrayBuffer | ReadableStream;
declare type Env = Record<string, any>;
export interface Context<RequestParamKeyType extends string = string, E = Env> {
req: Request<RequestParamKeyType>;
env: E;
event: FetchEvent;
executionCtx: ExecutionContext;
finalized: boolean;
get res(): Response;
set res(_res: Response);
header: (name: string, value: string) => void;
status: (status: StatusCode) => void;
set: {
<Key extends keyof ContextVariableMap>(key: Key, value: ContextVariableMap[Key]): void;
(key: string, value: any): void;
};
get: {
<Key extends keyof ContextVariableMap>(key: Key): ContextVariableMap[Key];
<T = any>(key: string): T;
};
pretty: (prettyJSON: boolean, space?: number) => void;
newResponse: (data: Data | null, status: StatusCode, headers: Headers) => Response;
body: (data: Data | null, status?: StatusCode, headers?: Headers) => Response;
text: (text: string, status?: StatusCode, headers?: Headers) => Response;
json: <T>(object: T, status?: StatusCode, headers?: Headers) => Response;
html: (html: string, status?: StatusCode, headers?: Headers) => Response;
redirect: (location: string, status?: StatusCode) => Response;
cookie: (name: string, value: string, options?: CookieOptions) => void;
notFound: () => Response | Promise<Response>;
}
export declare class HonoContext<RequestParamKeyType extends string = string, E = Env> implements Context<RequestParamKeyType, E> {
req: Request<RequestParamKeyType>;
env: E;
finalized: boolean;
_status: StatusCode;
private _executionCtx;
private _pretty;
private _prettySpace;
private _map;
private _headers;
private _res;
private notFoundHandler;
constructor(req: Request, env?: E | undefined, executionCtx?: FetchEvent | ExecutionContext | undefined, notFoundHandler?: NotFoundHandler);
get event(): FetchEvent;
get executionCtx(): ExecutionContext;
get res(): Response;
set res(_res: Response);
header(name: string, value: string): void;
status(status: StatusCode): void;
set<Key extends keyof ContextVariableMap>(key: Key, value: ContextVariableMap[Key]): void;
set(key: string, value: any): void;
get<Key extends keyof ContextVariableMap>(key: Key): ContextVariableMap[Key];
get<T = any>(key: string): T;
pretty(prettyJSON: boolean, space?: number): void;
newResponse(data: Data | null, status: StatusCode, headers?: Headers): Response;
body(data: Data | null, status?: StatusCode, headers?: Headers): Response;
text(text: string, status?: StatusCode, headers?: Headers): Response;
json<T>(object: T, status?: StatusCode, headers?: Headers): Response;
html(html: string, status?: StatusCode, headers?: Headers): Response;
redirect(location: string, status?: StatusCode): Response;
cookie(name: string, value: string, opt?: CookieOptions): void;
notFound(): Response | Promise<Response>;
}
export {};

View file

@ -0,0 +1,112 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HonoContext = void 0;
const cookie_1 = require("./utils/cookie");
const url_1 = require("./utils/url");
class HonoContext {
constructor(req, env = undefined, executionCtx = undefined, notFoundHandler = () => new Response()) {
this._status = 200;
this._pretty = false;
this._prettySpace = 2;
this._executionCtx = executionCtx;
this.req = req;
this.env = env ? env : {};
this.notFoundHandler = notFoundHandler;
this.finalized = false;
}
get event() {
if (this._executionCtx instanceof FetchEvent) {
return this._executionCtx;
}
else {
throw Error('This context has no FetchEvent');
}
}
get executionCtx() {
if (this._executionCtx) {
return this._executionCtx;
}
else {
throw Error('This context has no ExecutionContext');
}
}
get res() {
return (this._res || (this._res = new Response()));
}
set res(_res) {
this._res = _res;
this.finalized = true;
}
header(name, value) {
this._headers || (this._headers = {});
this._headers[name.toLowerCase()] = value;
if (this.finalized) {
this.res.headers.set(name, value);
}
}
status(status) {
this._status = status;
}
set(key, value) {
this._map || (this._map = {});
this._map[key] = value;
}
get(key) {
if (!this._map) {
return undefined;
}
return this._map[key];
}
pretty(prettyJSON, space = 2) {
this._pretty = prettyJSON;
this._prettySpace = space;
}
newResponse(data, status, headers = {}) {
const _headers = { ...this._headers };
if (this._res) {
this._res.headers.forEach((v, k) => {
_headers[k] = v;
});
}
return new Response(data, {
status: status || this._status || 200,
headers: { ..._headers, ...headers },
});
}
body(data, status = this._status, headers = {}) {
return this.newResponse(data, status, headers);
}
text(text, status = this._status, headers = {}) {
headers['content-type'] = 'text/plain; charset=UTF-8';
return this.body(text, status, headers);
}
json(object, status = this._status, headers = {}) {
const body = this._pretty
? JSON.stringify(object, null, this._prettySpace)
: JSON.stringify(object);
headers['content-type'] = 'application/json; charset=UTF-8';
return this.body(body, status, headers);
}
html(html, status = this._status, headers = {}) {
headers['content-type'] = 'text/html; charset=UTF-8';
return this.body(html, status, headers);
}
redirect(location, status = 302) {
if (!(0, url_1.isAbsoluteURL)(location)) {
const url = new URL(this.req.url);
url.pathname = location;
location = url.toString();
}
return this.newResponse(null, status, {
Location: location,
});
}
cookie(name, value, opt) {
const cookie = (0, cookie_1.serialize)(name, value, opt);
this.header('set-cookie', cookie);
}
notFound() {
return this.notFoundHandler(this);
}
}
exports.HonoContext = HonoContext;

View file

@ -0,0 +1,56 @@
/// <reference types="@cloudflare/workers-types" />
import type { Context } from './context';
import type { Router } from './router';
export interface ContextVariableMap {
}
declare type Env = Record<string, any>;
export declare type Handler<RequestParamKeyType extends string = string, E = Env> = (c: Context<RequestParamKeyType, E>, next: Next) => Response | Promise<Response> | Promise<void> | Promise<Response | undefined>;
export declare type NotFoundHandler<E = Env> = (c: Context<string, E>) => Response | Promise<Response>;
export declare type ErrorHandler<E = Env> = (err: Error, c: Context<string, E>) => Response;
export declare type Next = () => Promise<void>;
declare type ParamKeyName<NameWithPattern> = NameWithPattern extends `${infer Name}{${infer _Pattern}` ? Name : NameWithPattern;
declare type ParamKey<Component> = Component extends `:${infer NameWithPattern}` ? ParamKeyName<NameWithPattern> : never;
declare type ParamKeys<Path> = Path extends `${infer Component}/${infer Rest}` ? ParamKey<Component> | ParamKeys<Rest> : ParamKey<Path>;
interface HandlerInterface<T extends string, E extends Env = Env, U = Hono<E, T>> {
<Path extends string>(path: Path, ...handlers: Handler<ParamKeys<Path> extends never ? string : ParamKeys<Path>, E>[]): U;
(path: string, ...handlers: Handler<string, E>[]): U;
<Path extends string>(...handlers: Handler<ParamKeys<Path> extends never ? string : ParamKeys<Path>, E>[]): U;
(...handlers: Handler<string, E>[]): U;
}
interface Route<E extends Env> {
path: string;
method: string;
handler: Handler<string, E>;
}
declare const Hono_base: new <E_1 extends Env, T extends string, U>() => {
all: HandlerInterface<T, E_1, U>;
get: HandlerInterface<T, E_1, U>;
post: HandlerInterface<T, E_1, U>;
put: HandlerInterface<T, E_1, U>;
delete: HandlerInterface<T, E_1, U>;
head: HandlerInterface<T, E_1, U>;
options: HandlerInterface<T, E_1, U>;
patch: HandlerInterface<T, E_1, U>;
};
export declare class Hono<E extends Env = Env, P extends string = '/'> extends Hono_base<E, P, Hono<E, P>> {
readonly router: Router<Handler<string, E>>;
readonly strict: boolean;
private _tempPath;
private path;
routes: Route<E>[];
constructor(init?: Partial<Pick<Hono, 'router' | 'strict'>>);
private notFoundHandler;
private errorHandler;
route(path: string, app?: Hono<any>): Hono<E, P>;
use(path: string, ...middleware: Handler<string, E>[]): Hono<E, P>;
use(...middleware: Handler<string, E>[]): Hono<E, P>;
onError(handler: ErrorHandler<E>): Hono<E, P>;
notFound(handler: NotFoundHandler<E>): Hono<E, P>;
private addRoute;
private matchRoute;
private dispatch;
handleEvent(event: FetchEvent): Promise<Response>;
fetch: (request: Request, env?: E, executionCtx?: ExecutionContext) => Promise<Response>;
request(input: RequestInfo, requestInit?: RequestInit): Promise<Response>;
}
export {};

128
cli/bench/testdata/npm/hono/dist/hono.js vendored Normal file
View file

@ -0,0 +1,128 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Hono = void 0;
const compose_1 = require("./compose");
const context_1 = require("./context");
const request_1 = require("./request");
const router_1 = require("./router");
const trie_router_1 = require("./router/trie-router"); // Default Router
const url_1 = require("./utils/url");
const methods = ['get', 'post', 'put', 'delete', 'head', 'options', 'patch'];
function defineDynamicClass() {
return class {
};
}
class Hono extends defineDynamicClass() {
constructor(init = {}) {
super();
this.router = new trie_router_1.TrieRouter();
this.strict = true; // strict routing - default is true
this._tempPath = '';
this.path = '/';
this.routes = [];
this.notFoundHandler = (c) => {
const message = '404 Not Found';
return c.text(message, 404);
};
this.errorHandler = (err, c) => {
console.error(`${err.stack || err.message}`);
const message = 'Internal Server Error';
return c.text(message, 500);
};
this.fetch = (request, env, executionCtx) => {
return this.dispatch(request, executionCtx, env);
};
(0, request_1.extendRequestPrototype)();
const allMethods = [...methods, router_1.METHOD_NAME_ALL_LOWERCASE];
allMethods.map((method) => {
this[method] = (args1, ...args) => {
if (typeof args1 === 'string') {
this.path = args1;
}
else {
this.addRoute(method, this.path, args1);
}
args.map((handler) => {
if (typeof handler !== 'string') {
this.addRoute(method, this.path, handler);
}
});
return this;
};
});
Object.assign(this, init);
}
route(path, app) {
this._tempPath = path;
if (app) {
app.routes.map((r) => {
this.addRoute(r.method, r.path, r.handler);
});
this._tempPath = '';
}
return this;
}
use(arg1, ...handlers) {
if (typeof arg1 === 'string') {
this.path = arg1;
}
else {
handlers.unshift(arg1);
}
handlers.map((handler) => {
this.addRoute(router_1.METHOD_NAME_ALL, this.path, handler);
});
return this;
}
onError(handler) {
this.errorHandler = handler;
return this;
}
notFound(handler) {
this.notFoundHandler = handler;
return this;
}
addRoute(method, path, handler) {
method = method.toUpperCase();
if (this._tempPath) {
path = (0, url_1.mergePath)(this._tempPath, path);
}
this.router.add(method, path, handler);
const r = { path: path, method: method, handler: handler };
this.routes.push(r);
}
matchRoute(method, path) {
return this.router.match(method, path);
}
async dispatch(request, eventOrExecutionCtx, env) {
const path = (0, url_1.getPathFromURL)(request.url, this.strict);
const method = request.method;
const result = this.matchRoute(method, path);
request.paramData = result?.params;
const handlers = result ? result.handlers : [this.notFoundHandler];
const c = new context_1.HonoContext(request, env, eventOrExecutionCtx, this.notFoundHandler);
const composed = (0, compose_1.compose)(handlers, this.errorHandler, this.notFoundHandler);
let context;
try {
context = await composed(c);
if (!context.finalized) {
throw new Error('Context is not finalized. You may forget returning Response object or `await next()`');
}
}
catch (err) {
if (err instanceof Error) {
return this.errorHandler(err, c);
}
throw err;
}
return context.res;
}
handleEvent(event) {
return this.dispatch(event.request, event);
}
request(input, requestInit) {
const req = input instanceof Request ? input : new Request(input, requestInit);
return this.dispatch(req);
}
}
exports.Hono = Hono;

View file

@ -0,0 +1,10 @@
/// <reference path="request.d.ts" />
import { Hono } from './hono';
export type { Handler, Next } from './hono';
export type { Context } from './context';
declare module './hono' {
interface Hono {
fire(): void;
}
}
export { Hono };

View file

@ -0,0 +1,13 @@
"use strict";
// @denoify-ignore
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="./request.ts" /> Import "declare global" for the Request interface.
Object.defineProperty(exports, "__esModule", { value: true });
exports.Hono = void 0;
const hono_1 = require("./hono");
Object.defineProperty(exports, "Hono", { enumerable: true, get: function () { return hono_1.Hono; } });
hono_1.Hono.prototype.fire = function () {
addEventListener('fetch', (event) => {
void event.respondWith(this.handleEvent(event));
});
};

View file

@ -0,0 +1,11 @@
import type { Context } from '../../context';
import type { Next } from '../../hono';
export declare const basicAuth: (options: {
username: string;
password: string;
realm?: string;
hashFunction?: Function;
}, ...users: {
username: string;
password: string;
}[]) => (ctx: Context, next: Next) => Promise<void>;

View file

@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.basicAuth = void 0;
const buffer_1 = require("../../utils/buffer");
const encode_1 = require("../../utils/encode");
const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/;
const USER_PASS_REGEXP = /^([^:]*):(.*)$/;
const auth = (req) => {
const match = CREDENTIALS_REGEXP.exec(req.headers.get('Authorization') || '');
if (!match) {
return undefined;
}
const userPass = USER_PASS_REGEXP.exec((0, encode_1.decodeBase64)(match[1]));
if (!userPass) {
return undefined;
}
return { username: userPass[1], password: userPass[2] };
};
const basicAuth = (options, ...users) => {
if (!options) {
throw new Error('basic auth middleware requires options for "username and password"');
}
if (!options.realm) {
options.realm = 'Secure Area';
}
users.unshift({ username: options.username, password: options.password });
return async (ctx, next) => {
const requestUser = auth(ctx.req);
if (requestUser) {
for (const user of users) {
const usernameEqual = await (0, buffer_1.timingSafeEqual)(user.username, requestUser.username, options.hashFunction);
const passwordEqual = await (0, buffer_1.timingSafeEqual)(user.password, requestUser.password, options.hashFunction);
if (usernameEqual && passwordEqual) {
// Authorized OK
await next();
return;
}
}
}
ctx.res = new Response('Unauthorized', {
status: 401,
headers: {
'WWW-Authenticate': 'Basic realm="' + options.realm?.replace(/"/g, '\\"') + '"',
},
});
};
};
exports.basicAuth = basicAuth;

View file

@ -0,0 +1,8 @@
import type { Context } from '../../context';
import type { Next } from '../../hono';
export declare const bearerAuth: (options: {
token: string;
realm?: string;
prefix?: string;
hashFunction?: Function;
}) => (c: Context, next: Next) => Promise<void>;

View file

@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.bearerAuth = void 0;
const buffer_1 = require("../../utils/buffer");
const TOKEN_STRINGS = '[A-Za-z0-9._~+/-]+=*';
const PREFIX = 'Bearer';
const bearerAuth = (options) => {
if (!options.token) {
throw new Error('bearer auth middleware requires options for "token"');
}
if (!options.realm) {
options.realm = '';
}
if (!options.prefix) {
options.prefix = PREFIX;
}
const realm = options.realm?.replace(/"/g, '\\"');
return async (c, next) => {
const headerToken = c.req.headers.get('Authorization');
if (!headerToken) {
// No Authorization header
c.res = new Response('Unauthorized', {
status: 401,
headers: {
'WWW-Authenticate': `${options.prefix} realm="` + realm + '"',
},
});
}
else {
const regexp = new RegExp('^' + options.prefix + ' +(' + TOKEN_STRINGS + ') *$');
const match = regexp.exec(headerToken);
if (!match) {
// Invalid Request
c.res = new Response('Bad Request', {
status: 400,
headers: {
'WWW-Authenticate': `${options.prefix} error="invalid_request"`,
},
});
}
else {
const equal = await (0, buffer_1.timingSafeEqual)(options.token, match[1], options.hashFunction);
if (!equal) {
// Invalid Token
c.res = new Response('Unauthorized', {
status: 401,
headers: {
'WWW-Authenticate': `${options.prefix} error="invalid_token"`,
},
});
}
else {
// Authorize OK
await next();
return;
}
}
}
};
};
exports.bearerAuth = bearerAuth;

View file

@ -0,0 +1,7 @@
import type { Context } from '../../context';
import type { Next } from '../../hono';
export declare const cache: (options: {
cacheName: string;
wait?: boolean;
cacheControl?: string;
}) => (c: Context, next: Next) => Promise<Response | undefined>;

View file

@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cache = void 0;
const cache = (options) => {
if (options.wait === undefined) {
options.wait = false;
}
const addHeader = (response) => {
if (options.cacheControl)
response.headers.append('Cache-Control', options.cacheControl);
};
return async (c, next) => {
const key = c.req;
const cache = await caches.open(options.cacheName);
const response = await cache.match(key);
if (!response) {
await next();
addHeader(c.res);
const response = c.res.clone();
if (options.wait) {
await cache.put(key, response);
}
else {
c.executionCtx.waitUntil(cache.put(key, response));
}
}
else {
return response;
}
};
};
exports.cache = cache;

View file

@ -0,0 +1,8 @@
import type { Context } from '../../context';
import type { Next } from '../../hono';
declare type EncodingType = 'gzip' | 'deflate';
interface CompressionOptions {
encoding?: EncodingType;
}
export declare const compress: (options?: CompressionOptions) => (ctx: Context, next: Next) => Promise<void>;
export {};

View file

@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.compress = void 0;
const compress = (options) => {
return async (ctx, next) => {
await next();
const accepted = ctx.req.headers.get('Accept-Encoding');
const pattern = options?.encoding ?? /gzip|deflate/;
const match = accepted?.match(pattern);
if (!accepted || !match || !ctx.res.body) {
return;
}
const encoding = match[0];
const stream = new CompressionStream(encoding);
ctx.res = new Response(ctx.res.body.pipeThrough(stream), ctx.res.clone());
ctx.res.headers.set('Content-Encoding', encoding);
};
};
exports.compress = compress;

View file

@ -0,0 +1,12 @@
import type { Context } from '../../context';
import type { Next } from '../../hono';
declare type CORSOptions = {
origin: string;
allowMethods?: string[];
allowHeaders?: string[];
maxAge?: number;
credentials?: boolean;
exposeHeaders?: string[];
};
export declare const cors: (options?: CORSOptions) => (c: Context, next: Next) => Promise<void>;
export {};

View file

@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cors = void 0;
const cors = (options) => {
const defaults = {
origin: '*',
allowMethods: ['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH'],
allowHeaders: [],
exposeHeaders: [],
};
const opts = {
...defaults,
...options,
};
return async (c, next) => {
await next();
function set(key, value) {
c.res.headers.append(key, value);
}
set('Access-Control-Allow-Origin', opts.origin);
// Suppose the server sends a response with an Access-Control-Allow-Origin value with an explicit origin (rather than the "*" wildcard).
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
if (opts.origin !== '*') {
set('Vary', 'Origin');
}
if (opts.credentials) {
set('Access-Control-Allow-Credentials', 'true');
}
if (opts.exposeHeaders?.length) {
set('Access-Control-Expose-Headers', opts.exposeHeaders.join(','));
}
if (c.req.method === 'OPTIONS') {
// Preflight
if (opts.maxAge != null) {
set('Access-Control-Max-Age', opts.maxAge.toString());
}
if (opts.allowMethods?.length) {
set('Access-Control-Allow-Methods', opts.allowMethods.join(','));
}
let headers = opts.allowHeaders;
if (!headers?.length) {
const requestHeaders = c.req.headers.get('Access-Control-Request-Headers');
if (requestHeaders) {
headers = requestHeaders.split(/\s*,\s*/);
}
}
if (headers?.length) {
set('Access-Control-Allow-Headers', headers.join(','));
set('Vary', 'Access-Control-Request-Headers');
}
c.res.headers.delete('Content-Length');
c.res.headers.delete('Content-Type');
c.res = new Response(null, {
headers: c.res.headers,
status: 204,
statusText: c.res.statusText,
});
}
};
};
exports.cors = cors;

View file

@ -0,0 +1,7 @@
import type { Context } from '../../context';
import type { Next } from '../../hono';
declare type ETagOptions = {
weak: boolean;
};
export declare const etag: (options?: ETagOptions) => (c: Context, next: Next) => Promise<void>;
export {};

View file

@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.etag = void 0;
const crypto_1 = require("../../utils/crypto");
const etag = (options = { weak: false }) => {
return async (c, next) => {
const ifNoneMatch = c.req.header('If-None-Match') || c.req.header('if-none-match');
await next();
const res = c.res;
const clone = res.clone();
const hash = await (0, crypto_1.sha1)(res.body || '');
const etag = options.weak ? `W/"${hash}"` : `"${hash}"`;
if (ifNoneMatch && ifNoneMatch === etag) {
await clone.blob(); // Force using body
c.res = new Response(null, {
status: 304,
statusText: 'Not Modified',
});
c.res.headers.delete('Content-Length');
}
else {
c.res = new Response(clone.body, clone);
c.res.headers.append('ETag', etag);
}
};
};
exports.etag = etag;

View file

@ -0,0 +1,3 @@
import type { HtmlEscapedString } from '../../utils/html';
export declare const raw: (value: any) => HtmlEscapedString;
export declare const html: (strings: TemplateStringsArray, ...values: any[]) => HtmlEscapedString;

View file

@ -0,0 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.html = exports.raw = void 0;
const html_1 = require("../../utils/html");
const raw = (value) => {
const escapedString = new String(value);
escapedString.isEscaped = true;
return escapedString;
};
exports.raw = raw;
const html = (strings, ...values) => {
const buffer = [''];
for (let i = 0, len = strings.length - 1; i < len; i++) {
buffer[0] += strings[i];
const children = values[i] instanceof Array ? values[i].flat(Infinity) : [values[i]];
for (let i = 0, len = children.length; i < len; i++) {
const child = children[i];
if (typeof child === 'string') {
(0, html_1.escapeToBuffer)(child, buffer);
}
else if (typeof child === 'boolean' || child === null || child === undefined) {
continue;
}
else if ((typeof child === 'object' && child.isEscaped) ||
typeof child === 'number') {
buffer[0] += child;
}
else {
(0, html_1.escapeToBuffer)(child.toString(), buffer);
}
}
}
buffer[0] += strings[strings.length - 1];
return (0, exports.raw)(buffer[0]);
};
exports.html = html;

View file

@ -0,0 +1,26 @@
import type { StringBuffer, HtmlEscaped, HtmlEscapedString } from '../../utils/html';
declare global {
namespace jsx.JSX {
interface IntrinsicElements {
[tagName: string]: Record<string, any>;
}
}
}
declare type Child = string | number | JSXNode | Child[];
export declare class JSXNode implements HtmlEscaped {
tag: string | Function;
props: Record<string, any>;
children: Child[];
isEscaped: true;
constructor(tag: string | Function, props: Record<string, any>, children: Child[]);
toString(): string;
toStringToBuffer(buffer: StringBuffer): void;
}
export { jsxFn as jsx };
declare const jsxFn: (tag: string | Function, props: Record<string, any>, ...children: (string | HtmlEscapedString)[]) => JSXNode;
declare type FC<T = Record<string, any>> = (props: T) => HtmlEscapedString;
export declare const memo: <T>(component: FC<T>, propsAreEqual?: (prevProps: Readonly<T>, nextProps: Readonly<T>) => boolean) => FC<T>;
export declare const Fragment: (props: {
key?: string;
children?: any;
}) => JSXNode;

View file

@ -0,0 +1,193 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Fragment = exports.memo = exports.jsx = exports.JSXNode = void 0;
const html_1 = require("../../utils/html");
const emptyTags = [
'area',
'base',
'br',
'col',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'meta',
'param',
'source',
'track',
'wbr',
];
const booleanAttributes = [
'allowfullscreen',
'async',
'autofocus',
'autoplay',
'checked',
'controls',
'default',
'defer',
'disabled',
'formnovalidate',
'hidden',
'inert',
'ismap',
'itemscope',
'loop',
'multiple',
'muted',
'nomodule',
'novalidate',
'open',
'playsinline',
'readonly',
'required',
'reversed',
'selected',
];
const childrenToStringToBuffer = (children, buffer) => {
for (let i = 0, len = children.length; i < len; i++) {
const child = children[i];
if (typeof child === 'string') {
(0, html_1.escapeToBuffer)(child, buffer);
}
else if (typeof child === 'boolean' || child === null || child === undefined) {
continue;
}
else if (child instanceof JSXNode) {
child.toStringToBuffer(buffer);
}
else if (typeof child === 'number' || child.isEscaped) {
buffer[0] += child;
}
else {
// `child` type is `Child[]`, so stringify recursively
childrenToStringToBuffer(child, buffer);
}
}
};
class JSXNode {
constructor(tag, props, children) {
this.isEscaped = true;
this.tag = tag;
this.props = props;
this.children = children;
}
toString() {
const buffer = [''];
this.toStringToBuffer(buffer);
return buffer[0];
}
toStringToBuffer(buffer) {
const tag = this.tag;
const props = this.props;
let { children } = this;
buffer[0] += `<${tag}`;
const propsKeys = Object.keys(props || {});
for (let i = 0, len = propsKeys.length; i < len; i++) {
const v = props[propsKeys[i]];
if (typeof v === 'string') {
buffer[0] += ` ${propsKeys[i]}="`;
(0, html_1.escapeToBuffer)(v, buffer);
buffer[0] += '"';
}
else if (typeof v === 'number') {
buffer[0] += ` ${propsKeys[i]}="${v}"`;
}
else if (v === null || v === undefined) {
// Do nothing
}
else if (typeof v === 'boolean' && booleanAttributes.includes(propsKeys[i])) {
if (v) {
buffer[0] += ` ${propsKeys[i]}=""`;
}
}
else if (propsKeys[i] === 'dangerouslySetInnerHTML') {
if (children.length > 0) {
throw 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.';
}
const escapedString = new String(v.__html);
escapedString.isEscaped = true;
children = [escapedString];
}
else {
buffer[0] += ` ${propsKeys[i]}="`;
(0, html_1.escapeToBuffer)(v.toString(), buffer);
buffer[0] += '"';
}
}
if (emptyTags.includes(tag)) {
buffer[0] += '/>';
return;
}
buffer[0] += '>';
childrenToStringToBuffer(children, buffer);
buffer[0] += `</${tag}>`;
}
}
exports.JSXNode = JSXNode;
class JSXFunctionNode extends JSXNode {
toStringToBuffer(buffer) {
const { children } = this;
const res = this.tag.call(null, {
...this.props,
children: children.length <= 1 ? children[0] : children,
});
if (res instanceof JSXNode) {
res.toStringToBuffer(buffer);
}
else if (typeof res === 'number' || res.isEscaped) {
buffer[0] += res;
}
else {
(0, html_1.escapeToBuffer)(res, buffer);
}
}
}
class JSXFragmentNode extends JSXNode {
toStringToBuffer(buffer) {
childrenToStringToBuffer(this.children, buffer);
}
}
const jsxFn = (tag, props, ...children) => {
if (typeof tag === 'function') {
return new JSXFunctionNode(tag, props, children);
}
else {
return new JSXNode(tag, props, children);
}
};
exports.jsx = jsxFn;
const shallowEqual = (a, b) => {
if (a === b) {
return true;
}
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
if (aKeys.length !== bKeys.length) {
return false;
}
for (let i = 0, len = aKeys.length; i < len; i++) {
if (a[aKeys[i]] !== b[aKeys[i]]) {
return false;
}
}
return true;
};
const memo = (component, propsAreEqual = shallowEqual) => {
let computed = undefined;
let prevProps = undefined;
return ((props) => {
if (prevProps && !propsAreEqual(prevProps, props)) {
computed = undefined;
}
prevProps = props;
return (computed || (computed = component(props)));
});
};
exports.memo = memo;
const Fragment = (props) => {
return new JSXFragmentNode('', {}, props.children || []);
};
exports.Fragment = Fragment;

View file

@ -0,0 +1,2 @@
import type { JSXNode } from '.';
export declare function jsxDEV(tag: string | Function, props: Record<string, any>): JSXNode;

View file

@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.jsxDEV = void 0;
const _1 = require(".");
function jsxDEV(tag, props) {
const children = props.children ?? [];
delete props['children'];
return (0, _1.jsx)(tag, props, children);
}
exports.jsxDEV = jsxDEV;

View file

@ -0,0 +1,2 @@
export { jsxDEV as jsx } from './jsx-dev-runtime';
export { jsxDEV as jsxs } from './jsx-dev-runtime';

View file

@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.jsxs = exports.jsx = void 0;
var jsx_dev_runtime_1 = require("./jsx-dev-runtime");
Object.defineProperty(exports, "jsx", { enumerable: true, get: function () { return jsx_dev_runtime_1.jsxDEV; } });
var jsx_dev_runtime_2 = require("./jsx-dev-runtime");
Object.defineProperty(exports, "jsxs", { enumerable: true, get: function () { return jsx_dev_runtime_2.jsxDEV; } });

View file

@ -0,0 +1,7 @@
import type { Context } from '../../context';
import type { Next } from '../../hono';
export declare const jwt: (options: {
secret: string;
cookie?: string;
alg?: string;
}) => (ctx: Context, next: Next) => Promise<void>;

View file

@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.jwt = void 0;
const jwt_1 = require("../../utils/jwt");
const jwt = (options) => {
if (!options) {
throw new Error('JWT auth middleware requires options for "secret');
}
if (!crypto.subtle || !crypto.subtle.importKey) {
throw new Error('`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.');
}
return async (ctx, next) => {
const credentials = ctx.req.headers.get('Authorization');
let token;
if (credentials) {
const parts = credentials.split(/\s+/);
if (parts.length !== 2) {
ctx.res = new Response('Unauthorized', {
status: 401,
headers: {
'WWW-Authenticate': `Bearer realm="${ctx.req.url}",error="invalid_request",error_description="invalid credentials structure"`,
},
});
return;
}
else {
token = parts[1];
}
}
else if (options.cookie) {
token = ctx.req.cookie(options.cookie);
}
if (!token) {
ctx.res = new Response('Unauthorized', {
status: 401,
headers: {
'WWW-Authenticate': `Bearer realm="${ctx.req.url}",error="invalid_request",error_description="no authorization included in request"`,
},
});
return;
}
let authorized = false;
let msg = '';
try {
authorized = await jwt_1.Jwt.verify(token, options.secret, options.alg);
}
catch (e) {
msg = `${e}`;
}
if (!authorized) {
ctx.res = new Response('Unauthorized', {
status: 401,
statusText: msg,
headers: {
'WWW-Authenticate': `Bearer realm="${ctx.req.url}",error="invalid_token",error_description="token verification failure"`,
},
});
return;
}
await next();
};
};
exports.jwt = jwt;

View file

@ -0,0 +1,5 @@
import type { Context } from '../../context';
import type { Next } from '../../hono';
declare type PrintFunc = (str: string, ...rest: string[]) => void;
export declare const logger: (fn?: PrintFunc) => (c: Context, next: Next) => Promise<void>;
export {};

View file

@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.logger = void 0;
const url_1 = require("../../utils/url");
var LogPrefix;
(function (LogPrefix) {
LogPrefix["Outgoing"] = "-->";
LogPrefix["Incoming"] = "<--";
LogPrefix["Error"] = "xxx";
})(LogPrefix || (LogPrefix = {}));
const humanize = (times) => {
const [delimiter, separator] = [',', '.'];
const orderTimes = times.map((v) => v.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1' + delimiter));
return orderTimes.join(separator);
};
const time = (start) => {
const delta = Date.now() - start;
return humanize([delta < 1000 ? delta + 'ms' : Math.round(delta / 1000) + 's']);
};
const colorStatus = (status) => {
const out = {
7: `\x1b[35m${status}\x1b[0m`,
5: `\x1b[31m${status}\x1b[0m`,
4: `\x1b[33m${status}\x1b[0m`,
3: `\x1b[36m${status}\x1b[0m`,
2: `\x1b[32m${status}\x1b[0m`,
1: `\x1b[32m${status}\x1b[0m`,
0: `\x1b[33m${status}\x1b[0m`,
};
const calculateStatus = (status / 100) | 0;
return out[calculateStatus];
};
function log(fn, prefix, method, path, status = 0, elapsed) {
const out = prefix === LogPrefix.Incoming
? ` ${prefix} ${method} ${path}`
: ` ${prefix} ${method} ${path} ${colorStatus(status)} ${elapsed}`;
fn(out);
}
const logger = (fn = console.log) => {
return async (c, next) => {
const { method } = c.req;
const path = (0, url_1.getPathFromURL)(c.req.url);
log(fn, LogPrefix.Incoming, method, path);
const start = Date.now();
await next();
log(fn, LogPrefix.Outgoing, method, path, c.res.status, time(start));
};
};
exports.logger = logger;

View file

@ -0,0 +1,3 @@
import type { Context } from '../../context';
import type { Next } from '../../hono';
export declare const poweredBy: () => (c: Context, next: Next) => Promise<void>;

View file

@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.poweredBy = void 0;
const poweredBy = () => {
return async (c, next) => {
await next();
c.res.headers.append('X-Powered-By', 'Hono');
};
};
exports.poweredBy = poweredBy;

View file

@ -0,0 +1,7 @@
import type { Context } from '../../context';
import type { Next } from '../../hono';
declare type prettyOptions = {
space: number;
};
export declare const prettyJSON: (options?: prettyOptions) => (c: Context, next: Next) => Promise<void>;
export {};

View file

@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.prettyJSON = void 0;
const prettyJSON = (options = { space: 2 }) => {
return async (c, next) => {
const pretty = c.req.query('pretty') || c.req.query('pretty') === '' ? true : false;
c.pretty(pretty, options.space);
await next();
};
};
exports.prettyJSON = prettyJSON;

View file

@ -0,0 +1,7 @@
import type { Context } from '../../context';
import type { Next } from '../../hono';
export declare type ServeStaticOptions = {
root?: string;
path?: string;
};
export declare const serveStatic: (options?: ServeStaticOptions) => (c: Context, next: Next) => Promise<Response | undefined>;

View file

@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serveStatic = void 0;
const filepath_1 = require("../../utils/filepath");
const mime_1 = require("../../utils/mime");
// @ts-ignore
const { file } = Bun;
const DEFAULT_DOCUMENT = 'index.html';
const serveStatic = (options = { root: '' }) => {
return async (c, next) => {
// Do nothing if Response is already set
if (c.res && c.finalized) {
await next();
}
const url = new URL(c.req.url);
let path = (0, filepath_1.getFilePath)({
filename: options.path ?? url.pathname,
root: options.root,
defaultDocument: DEFAULT_DOCUMENT,
});
path = `./${path}`;
const content = file(path);
if (content) {
const mimeType = (0, mime_1.getMimeType)(path);
if (mimeType) {
c.header('Content-Type', mimeType);
}
// Return Response object
return c.body(content);
}
else {
console.warn(`Static file: ${path} is not found`);
await next();
}
return;
};
};
exports.serveStatic = serveStatic;

View file

@ -0,0 +1 @@
export { serveStatic } from './serve-static';

View file

@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serveStatic = void 0;
var serve_static_1 = require("./serve-static");
Object.defineProperty(exports, "serveStatic", { enumerable: true, get: function () { return serve_static_1.serveStatic; } });

View file

@ -0,0 +1,5 @@
import type { ServeStaticOptions } from './serve-static';
declare const module: (options?: ServeStaticOptions) => import("../../hono").Handler<string, {
[x: string]: any;
}>;
export { module as serveStatic };

View file

@ -0,0 +1,13 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// For ES module mode
import manifest from '__STATIC_CONTENT_MANIFEST';
import { serveStatic } from './serve-static';
const module = (options = { root: '' }) => {
return serveStatic({
root: options.root,
path: options.path,
manifest: options.manifest ? options.manifest : manifest,
});
};
export { module as serveStatic };

View file

@ -0,0 +1,9 @@
/// <reference types="@cloudflare/workers-types" />
import type { Handler } from '../../hono';
export declare type ServeStaticOptions = {
root?: string;
path?: string;
manifest?: object | string;
namespace?: KVNamespace;
};
export declare const serveStatic: (options?: ServeStaticOptions) => Handler;

View file

@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serveStatic = void 0;
const cloudflare_1 = require("../../utils/cloudflare");
const filepath_1 = require("../../utils/filepath");
const mime_1 = require("../../utils/mime");
const DEFAULT_DOCUMENT = 'index.html';
// This middleware is available only on Cloudflare Workers.
const serveStatic = (options = { root: '' }) => {
return async (c, next) => {
// Do nothing if Response is already set
if (c.res && c.finalized) {
await next();
}
const url = new URL(c.req.url);
const path = (0, filepath_1.getFilePath)({
filename: options.path ?? url.pathname,
root: options.root,
defaultDocument: DEFAULT_DOCUMENT,
});
const content = await (0, cloudflare_1.getContentFromKVAsset)(path, {
manifest: options.manifest,
namespace: options.namespace ? options.namespace : c.env ? c.env.__STATIC_CONTENT : undefined,
});
if (content) {
const mimeType = (0, mime_1.getMimeType)(path);
if (mimeType) {
c.header('Content-Type', mimeType);
}
// Return Response object
return c.body(content);
}
else {
console.warn(`Static file: ${path} is not found`);
await next();
}
return;
};
};
exports.serveStatic = serveStatic;

View file

@ -0,0 +1,32 @@
import type { Body } from './utils/body';
import type { Cookie } from './utils/cookie';
declare global {
interface Request<ParamKeyType extends string = string> {
param: {
(key: ParamKeyType): string;
(): Record<ParamKeyType, string>;
};
paramData?: Record<ParamKeyType, string>;
query: {
(key: string): string;
(): Record<string, string>;
};
queries: {
(key: string): string[];
(): Record<string, string[]>;
};
header: {
(name: string): string;
(): Record<string, string>;
};
cookie: {
(name: string): string;
(): Cookie;
};
parsedBody?: Promise<Body>;
parseBody: {
(): Promise<Body>;
};
}
}
export declare function extendRequestPrototype(): void;

View file

@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extendRequestPrototype = void 0;
const body_1 = require("./utils/body");
const cookie_1 = require("./utils/cookie");
function extendRequestPrototype() {
if (!!Request.prototype.param) {
// already extended
return;
}
Request.prototype.param = function (key) {
if (this.paramData) {
if (key) {
return this.paramData[key];
}
else {
return this.paramData;
}
}
return null;
};
Request.prototype.header = function (name) {
if (name) {
return this.headers.get(name);
}
else {
const result = {};
for (const [key, value] of this.headers) {
result[key] = value;
}
return result;
}
};
Request.prototype.query = function (key) {
const url = new URL(this.url);
if (key) {
return url.searchParams.get(key);
}
else {
const result = {};
for (const key of url.searchParams.keys()) {
result[key] = url.searchParams.get(key) || '';
}
return result;
}
};
Request.prototype.queries = function (key) {
const url = new URL(this.url);
if (key) {
return url.searchParams.getAll(key);
}
else {
const result = {};
for (const key of url.searchParams.keys()) {
result[key] = url.searchParams.getAll(key);
}
return result;
}
};
Request.prototype.cookie = function (key) {
const cookie = this.headers.get('Cookie') || '';
const obj = (0, cookie_1.parse)(cookie);
if (key) {
const value = obj[key];
return value;
}
else {
return obj;
}
};
Request.prototype.parseBody = function () {
if (!this.parsedBody) {
this.parsedBody = (0, body_1.parseBody)(this);
}
return this.parsedBody;
};
}
exports.extendRequestPrototype = extendRequestPrototype;

View file

@ -0,0 +1,10 @@
export declare const METHOD_NAME_ALL: "ALL";
export declare const METHOD_NAME_ALL_LOWERCASE: "all";
export interface Router<T> {
add(method: string, path: string, handler: T): void;
match(method: string, path: string): Result<T> | null;
}
export interface Result<T> {
handlers: T[];
params: Record<string, string>;
}

View file

@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.METHOD_NAME_ALL_LOWERCASE = exports.METHOD_NAME_ALL = void 0;
exports.METHOD_NAME_ALL = 'ALL';
exports.METHOD_NAME_ALL_LOWERCASE = 'all';

View file

@ -0,0 +1 @@
export { RegExpRouter } from './router';

View file

@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegExpRouter = void 0;
var router_1 = require("./router");
Object.defineProperty(exports, "RegExpRouter", { enumerable: true, get: function () { return router_1.RegExpRouter; } });

View file

@ -0,0 +1,14 @@
export declare type ParamMap = Array<[string, number]>;
export interface Context {
varIndex: number;
}
export declare class Node {
index?: number;
varIndex?: number;
children: Record<string, Node>;
reverse: boolean;
constructor({ reverse }?: Partial<Node>);
newChildNode(): Node;
insert(tokens: readonly string[], index: number, paramMap: ParamMap, context: Context): void;
buildRegExpStr(): string;
}

View file

@ -0,0 +1,100 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Node = void 0;
const LABEL_REG_EXP_STR = '[^/]+';
const ONLY_WILDCARD_REG_EXP_STR = '.*';
const TAIL_WILDCARD_REG_EXP_STR = '(?:|/.*)';
/**
* Sort order:
* 1. literal
* 2. special pattern (e.g. :label{[0-9]+})
* 3. common label pattern (e.g. :label)
* 4. wildcard
*/
function compareKey(a, b) {
if (a.length === 1) {
return b.length === 1 ? (a < b ? -1 : 1) : -1;
}
if (b.length === 1) {
return 1;
}
// wildcard
if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
return 1;
}
else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
return -1;
}
// label
if (a === LABEL_REG_EXP_STR) {
return 1;
}
else if (b === LABEL_REG_EXP_STR) {
return -1;
}
return a.length === b.length ? (a < b ? -1 : 1) : b.length - a.length;
}
class Node {
constructor({ reverse } = { reverse: false }) {
this.children = {};
this.reverse = reverse;
}
newChildNode() {
return new Node({ reverse: this.reverse });
}
insert(tokens, index, paramMap, context) {
var _a;
if (tokens.length === 0) {
this.index = index;
return;
}
const [token, ...restTokens] = tokens;
const pattern = token === '*'
? restTokens.length === 0
? ['', '', ONLY_WILDCARD_REG_EXP_STR] // '*' matches to all the trailing paths
: ['', '', LABEL_REG_EXP_STR]
: token === '/*'
? ['', '', TAIL_WILDCARD_REG_EXP_STR] // '/path/to/*' is /\/path\/to(?:|/.*)$
: token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
let node;
if (pattern) {
const name = pattern[1];
const regexpStr = pattern[2] || LABEL_REG_EXP_STR;
node = this.children[regexpStr];
if (!node) {
node = this.children[regexpStr] = this.newChildNode();
if (name !== '') {
node.varIndex = context.varIndex++;
}
}
if (name !== '') {
paramMap.push([name, node.varIndex]);
}
}
else {
node = (_a = this.children)[token] || (_a[token] = this.newChildNode());
}
node.insert(restTokens, index, paramMap, context);
}
buildRegExpStr() {
let childKeys = Object.keys(this.children).sort(compareKey);
if (this.reverse) {
childKeys = childKeys.reverse();
}
const strList = childKeys.map((k) => {
const c = this.children[k];
return (typeof c.varIndex === 'number' ? `(${k})@${c.varIndex}` : k) + c.buildRegExpStr();
});
if (typeof this.index === 'number') {
strList.unshift(`#${this.index}`);
}
if (strList.length === 0) {
return '';
}
if (strList.length === 1) {
return strList[0];
}
return '(?:' + strList.join('|') + ')';
}
}
exports.Node = Node;

View file

@ -0,0 +1,34 @@
import type { Router, Result } from '../../router';
interface Hint {
components: string[];
regExpComponents: Array<true | string>;
componentsLength: number;
endWithWildcard: boolean;
paramIndexList: number[];
maybeHandler: boolean;
namedParams: [number, string, string][];
}
interface HandlerWithSortIndex<T> {
handler: T;
index: number;
}
interface Route<T> {
method: string;
path: string;
hint: Hint;
handlers: HandlerWithSortIndex<T>[];
middleware: HandlerWithSortIndex<T>[];
paramAliasMap: Record<string, string[]>;
}
export declare class RegExpRouter<T> implements Router<T> {
routeData?: {
index: number;
routes: Route<T>[];
methods: Set<string>;
};
add(method: string, path: string, handler: T): void;
match(method: string, path: string): Result<T> | null;
private buildAllMatchers;
private buildMatcher;
}
export {};

View file

@ -0,0 +1,360 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegExpRouter = void 0;
const router_1 = require("../../router");
const trie_1 = require("./trie");
const emptyParam = {};
const nullMatcher = [/^$/, []];
function initHint(path) {
const components = path.match(/\/(?::\w+{[^}]+}|[^\/]*)/g) || [];
let componentsLength = components.length;
const paramIndexList = [];
const regExpComponents = [];
const namedParams = [];
for (let i = 0, len = components.length; i < len; i++) {
if (i === len - 1 && components[i] === '/*') {
componentsLength--;
break;
}
const m = components[i].match(/^\/:(\w+)({[^}]+})?/);
if (m) {
namedParams.push([i, m[1], m[2] || '[^/]+']);
regExpComponents[i] = m[2] || true;
}
else if (components[i] === '/*') {
regExpComponents[i] = true;
}
else {
regExpComponents[i] = components[i];
}
if (/\/(?::|\*)/.test(components[i])) {
paramIndexList.push(i);
}
}
return {
components,
regExpComponents,
componentsLength,
endWithWildcard: path.endsWith('*'),
paramIndexList,
namedParams,
maybeHandler: true,
};
}
function compareRoute(a, b) {
if (a.path === '*') {
return 1;
}
let i = 0;
const len = a.hint.regExpComponents.length;
for (; i < len; i++) {
if (a.hint.regExpComponents[i] !== b.hint.regExpComponents[i]) {
if (a.hint.regExpComponents[i] === true) {
break;
}
return 0;
}
}
// may be ambiguous
for (; i < len; i++) {
if (a.hint.regExpComponents[i] !== true &&
a.hint.regExpComponents[i] !== b.hint.regExpComponents[i]) {
return 2;
}
}
return i === b.hint.regExpComponents.length || a.hint.endWithWildcard ? 1 : 0;
}
function compareHandler(a, b) {
return a.index - b.index;
}
function getSortedHandlers(handlers) {
return [...handlers].sort(compareHandler).map((h) => h.handler);
}
function buildMatcherFromPreprocessedRoutes(routes, hasAmbiguous = false) {
const trie = new trie_1.Trie({ reverse: hasAmbiguous });
const handlers = [];
if (routes.length === 0) {
return nullMatcher;
}
for (let i = 0, len = routes.length; i < len; i++) {
const paramMap = trie.insert(routes[i].path, i);
handlers[i] = [
[...routes[i].middleware, ...routes[i].handlers],
Object.keys(paramMap).length !== 0 ? paramMap : null,
];
if (!hasAmbiguous) {
handlers[i][0] = getSortedHandlers(handlers[i][0]);
}
}
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
for (let i = 0, len = handlers.length; i < len; i++) {
const paramMap = handlers[i][1];
if (paramMap) {
for (let j = 0, len = paramMap.length; j < len; j++) {
paramMap[j][1] = paramReplacementMap[paramMap[j][1]];
const aliasTo = routes[i].paramAliasMap[paramMap[j][0]];
if (aliasTo) {
for (let k = 0, len = aliasTo.length; k < len; k++) {
paramMap.push([aliasTo[k], paramMap[j][1]]);
}
}
}
}
}
const handlerMap = [];
// using `in` because indexReplacementMap is a sparse array
for (const i in indexReplacementMap) {
handlerMap[i] = handlers[indexReplacementMap[i]];
}
return [regexp, handlerMap];
}
function verifyDuplicateParam(routes) {
const nameMap = {};
for (let i = 0, len = routes.length; i < len; i++) {
const route = routes[i];
for (let k = 0, len = route.hint.namedParams.length; k < len; k++) {
const [index, name] = route.hint.namedParams[k];
if (name in nameMap && index !== nameMap[name]) {
return false;
}
else {
nameMap[name] = index;
}
}
const paramAliasMap = route.paramAliasMap;
const paramAliasMapKeys = Object.keys(paramAliasMap);
for (let k = 0, len = paramAliasMapKeys.length; k < len; k++) {
const aliasFrom = paramAliasMapKeys[k];
for (let l = 0, len = paramAliasMap[aliasFrom].length; l < len; l++) {
const aliasTo = paramAliasMap[aliasFrom][l];
const index = nameMap[aliasFrom];
if (aliasTo in nameMap && index !== nameMap[aliasTo]) {
return false;
}
else {
nameMap[aliasTo] = index;
}
}
}
}
return true;
}
class RegExpRouter {
constructor() {
this.routeData = { index: 0, routes: [], methods: new Set() };
}
add(method, path, handler) {
if (!this.routeData) {
throw new Error('Can not add a route since the matcher is already built.');
}
this.routeData.index++;
const { index, routes, methods } = this.routeData;
if (path === '/*') {
path = '*';
}
const hint = initHint(path);
const handlerWithSortIndex = {
index,
handler,
};
for (let i = 0, len = routes.length; i < len; i++) {
if (routes[i].method === method && routes[i].path === path) {
routes[i].handlers.push(handlerWithSortIndex);
return;
}
}
methods.add(method);
routes.push({
method,
path,
hint,
handlers: [handlerWithSortIndex],
middleware: [],
paramAliasMap: {},
});
}
match(method, path) {
const [primaryMatchers, secondaryMatchers, hasAmbiguous] = this.buildAllMatchers();
this.match = hasAmbiguous
? (method, path) => {
const matcher = (primaryMatchers[method] ||
primaryMatchers[router_1.METHOD_NAME_ALL]);
let match = path.match(matcher[0]);
if (!match) {
// do not support secondary matchers here.
return null;
}
const params = {};
const handlers = new Set();
let regExpSrc = matcher[0].source;
while (match) {
let index = match.indexOf('', 1);
for (;;) {
const [handler, paramMap] = matcher[1][index];
if (paramMap) {
for (let i = 0, len = paramMap.length; i < len; i++) {
params[paramMap[i][0]] = match[paramMap[i][1]];
}
}
for (let i = 0, len = handler.length; i < len; i++) {
handlers.add(handler[i]);
}
const newIndex = match.indexOf('', index + 1);
if (newIndex === -1) {
break;
}
index = newIndex;
}
regExpSrc = regExpSrc.replace(new RegExp(`((?:(?:\\(\\?:|.)*?\\([^)]*\\)){${index - 1}}.*?)\\(\\)`), '$1(^)');
match = path.match(new RegExp(regExpSrc));
}
return { handlers: getSortedHandlers(handlers.values()), params };
}
: (method, path) => {
let matcher = (primaryMatchers[method] || primaryMatchers[router_1.METHOD_NAME_ALL]);
let match = path.match(matcher[0]);
if (!match) {
const matchers = secondaryMatchers[method] || secondaryMatchers[router_1.METHOD_NAME_ALL];
for (let i = 0, len = matchers.length; i < len && !match; i++) {
matcher = matchers[i];
match = path.match(matcher[0]);
}
if (!match) {
return null;
}
}
const index = match.indexOf('', 1);
const [handlers, paramMap] = matcher[1][index];
if (!paramMap) {
return { handlers, params: emptyParam };
}
const params = {};
for (let i = 0, len = paramMap.length; i < len; i++) {
params[paramMap[i][0]] = match[paramMap[i][1]];
}
return { handlers, params };
};
return this.match(method, path);
}
buildAllMatchers() {
// @ts-ignore
this.routeData.routes.sort(({ hint: a }, { hint: b }) => {
if (a.componentsLength !== b.componentsLength) {
return a.componentsLength - b.componentsLength;
}
for (let i = 0, len = Math.min(a.paramIndexList.length, b.paramIndexList.length) + 1; i < len; i++) {
if (a.paramIndexList[i] !== b.paramIndexList[i]) {
if (a.paramIndexList[i] === undefined) {
return -1;
}
else if (b.paramIndexList[i] === undefined) {
return 1;
}
else {
return a.paramIndexList[i] - b.paramIndexList[i];
}
}
}
if (a.endWithWildcard !== b.endWithWildcard) {
return a.endWithWildcard ? -1 : 1;
}
return 0;
});
const primaryMatchers = {};
const secondaryMatchers = {};
let hasAmbiguous = false;
// @ts-ignore
this.routeData.methods.forEach((method) => {
let _hasAmbiguous;
[primaryMatchers[method], secondaryMatchers[method], _hasAmbiguous] =
this.buildMatcher(method);
hasAmbiguous = hasAmbiguous || _hasAmbiguous;
});
primaryMatchers[router_1.METHOD_NAME_ALL] || (primaryMatchers[router_1.METHOD_NAME_ALL] = nullMatcher);
secondaryMatchers[router_1.METHOD_NAME_ALL] || (secondaryMatchers[router_1.METHOD_NAME_ALL] = []);
delete this.routeData; // to reduce memory usage
return [primaryMatchers, secondaryMatchers, hasAmbiguous];
}
buildMatcher(method) {
var _a, _b;
let hasAmbiguous = false;
const targetMethods = new Set([method, router_1.METHOD_NAME_ALL]);
// @ts-ignore
const routes = this.routeData.routes.filter(({ method }) => targetMethods.has(method));
// Reset temporary data per method
for (let i = 0, len = routes.length; i < len; i++) {
routes[i].middleware = [];
routes[i].paramAliasMap = {};
}
// preprocess routes
for (let i = 0, len = routes.length; i < len; i++) {
for (let j = i + 1; j < len; j++) {
const compareResult = compareRoute(routes[i], routes[j]);
// i includes j
if (compareResult === 1) {
const components = routes[j].hint.components;
const namedParams = routes[i].hint.namedParams;
for (let k = 0, len = namedParams.length; k < len; k++) {
const c = components[namedParams[k][0]];
const m = c.match(/^\/:(\w+)({[^}]+})?/);
if (m && namedParams[k][1] === m[1]) {
continue;
}
if (m) {
(_a = routes[j].paramAliasMap)[_b = m[1]] || (_a[_b] = []);
routes[j].paramAliasMap[m[1]].push(namedParams[k][1]);
}
else {
components[namedParams[k][0]] = `/:${namedParams[k][1]}{${c.substring(1)}}`;
routes[j].hint.namedParams.push([
namedParams[k][0],
namedParams[k][1],
c.substring(1),
]);
routes[j].path = components.join('');
}
}
if (routes[j].hint.components.length < routes[i].hint.components.length) {
routes[j].middleware.push(...routes[i].handlers.map((h) => ({
index: h.index,
handler: h.handler,
})));
}
else {
routes[j].middleware.push(...routes[i].handlers);
}
routes[i].hint.maybeHandler = false;
}
else if (compareResult === 2) {
// ambiguous
hasAmbiguous = true;
if (!verifyDuplicateParam([routes[i], routes[j]])) {
throw new Error('Duplicate param name');
}
}
}
if (!verifyDuplicateParam([routes[i]])) {
throw new Error('Duplicate param name');
}
}
if (hasAmbiguous) {
return [buildMatcherFromPreprocessedRoutes(routes, hasAmbiguous), [], hasAmbiguous];
}
const primaryRoutes = [];
const secondaryRoutes = [];
for (let i = 0, len = routes.length; i < len; i++) {
if (routes[i].hint.maybeHandler || !routes[i].hint.endWithWildcard) {
primaryRoutes.push(routes[i]);
}
else {
secondaryRoutes.push(routes[i]);
}
}
return [
buildMatcherFromPreprocessedRoutes(primaryRoutes, hasAmbiguous),
[buildMatcherFromPreprocessedRoutes(secondaryRoutes, hasAmbiguous)],
hasAmbiguous,
];
}
}
exports.RegExpRouter = RegExpRouter;

View file

@ -0,0 +1,14 @@
import type { ParamMap, Context } from './node';
import { Node } from './node';
export type { ParamMap } from './node';
export declare type ReplacementMap = number[];
interface InitOptions {
reverse: boolean;
}
export declare class Trie {
context: Context;
root: Node;
constructor({ reverse }?: InitOptions);
insert(path: string, index: number): ParamMap;
buildRegExp(): [RegExp, ReplacementMap, ReplacementMap];
}

View file

@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Trie = void 0;
const node_1 = require("./node");
class Trie {
constructor({ reverse } = { reverse: false }) {
this.context = { varIndex: 0 };
this.root = new node_1.Node({ reverse });
}
insert(path, index) {
const paramMap = [];
/**
* - pattern (:label, :label{0-9]+}, ...)
* - /* wildcard
* - character
*/
const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.root.insert(tokens, index, paramMap, this.context);
return paramMap;
}
buildRegExp() {
let regexp = this.root.buildRegExpStr();
let captureIndex = 0;
const indexReplacementMap = [];
const paramReplacementMap = [];
regexp = regexp.replace(/#(\d+)|@(\d+)|\.\*\$/g, (_, handlerIndex, paramIndex) => {
if (typeof handlerIndex !== 'undefined') {
indexReplacementMap[++captureIndex] = Number(handlerIndex);
return '$()';
}
if (typeof paramIndex !== 'undefined') {
paramReplacementMap[Number(paramIndex)] = ++captureIndex;
return '';
}
return '';
});
return [new RegExp(`^${regexp}`), indexReplacementMap, paramReplacementMap];
}
}
exports.Trie = Trie;

View file

@ -0,0 +1 @@
export { TrieRouter } from './router';

View file

@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TrieRouter = void 0;
var router_1 = require("./router");
Object.defineProperty(exports, "TrieRouter", { enumerable: true, get: function () { return router_1.TrieRouter; } });

View file

@ -0,0 +1,20 @@
import type { Result } from '../../router';
import type { Pattern } from '../../utils/url';
declare type HandlerSet<T> = {
handler: T;
score: number;
name: string;
};
export declare class Node<T> {
methods: Record<string, HandlerSet<T>>[];
children: Record<string, Node<T>>;
patterns: Pattern[];
order: number;
name: string;
handlerSetCache: Record<string, HandlerSet<T>[]>;
constructor(method?: string, handler?: T, children?: Record<string, Node<T>>);
insert(method: string, path: string, handler: T): Node<T>;
private getHandlerSets;
search(method: string, path: string): Result<T> | null;
}
export {};

View file

@ -0,0 +1,168 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Node = void 0;
const router_1 = require("../../router");
const url_1 = require("../../utils/url");
function findParam(node, name) {
for (let i = 0, len = node.patterns.length; i < len; i++) {
if (typeof node.patterns[i] === 'object' && node.patterns[i][1] === name) {
return true;
}
}
const nodes = Object.values(node.children);
for (let i = 0, len = nodes.length; i < len; i++) {
if (findParam(nodes[i], name)) {
return true;
}
}
return false;
}
class Node {
constructor(method, handler, children) {
this.order = 0;
this.children = children || {};
this.methods = [];
this.name = '';
if (method && handler) {
const m = {};
m[method] = { handler: handler, score: 0, name: this.name };
this.methods = [m];
}
this.patterns = [];
this.handlerSetCache = {};
}
insert(method, path, handler) {
this.name = `${method} ${path}`;
this.order = ++this.order;
// eslint-disable-next-line @typescript-eslint/no-this-alias
let curNode = this;
const parts = (0, url_1.splitPath)(path);
const parentPatterns = [];
const errorMessage = (name) => {
return `Duplicate param name, use another name instead of '${name}' - ${method} ${path} <--- '${name}'`;
};
for (let i = 0, len = parts.length; i < len; i++) {
const p = parts[i];
if (Object.keys(curNode.children).includes(p)) {
parentPatterns.push(...curNode.patterns);
curNode = curNode.children[p];
continue;
}
curNode.children[p] = new Node();
const pattern = (0, url_1.getPattern)(p);
if (pattern) {
if (typeof pattern === 'object') {
for (let j = 0, len = parentPatterns.length; j < len; j++) {
if (typeof parentPatterns[j] === 'object' && parentPatterns[j][1] === pattern[1]) {
throw new Error(errorMessage(pattern[1]));
}
}
if (Object.values(curNode.children).some((n) => findParam(n, pattern[1]))) {
throw new Error(errorMessage(pattern[1]));
}
}
curNode.patterns.push(pattern);
parentPatterns.push(...curNode.patterns);
}
parentPatterns.push(...curNode.patterns);
curNode = curNode.children[p];
}
if (!curNode.methods.length) {
curNode.methods = [];
}
const m = {};
const handlerSet = { handler: handler, name: this.name, score: this.order };
m[method] = handlerSet;
curNode.methods.push(m);
return curNode;
}
getHandlerSets(node, method, wildcard) {
var _a, _b;
return ((_a = node.handlerSetCache)[_b = `${method}:${wildcard ? '1' : '0'}`] || (_a[_b] = (() => {
const handlerSets = [];
node.methods.map((m) => {
const handlerSet = m[method] || m[router_1.METHOD_NAME_ALL];
if (handlerSet !== undefined) {
const hs = { ...handlerSet };
handlerSets.push(hs);
return;
}
});
return handlerSets;
})()));
}
search(method, path) {
const handlerSets = [];
const params = {};
// eslint-disable-next-line @typescript-eslint/no-this-alias
const curNode = this;
let curNodes = [curNode];
const parts = (0, url_1.splitPath)(path);
for (let i = 0, len = parts.length; i < len; i++) {
const part = parts[i];
const isLast = i === len - 1;
const tempNodes = [];
let matched = false;
for (let j = 0, len2 = curNodes.length; j < len2; j++) {
const node = curNodes[j];
const nextNode = node.children[part];
if (nextNode) {
if (isLast === true) {
// '/hello/*' => match '/hello'
if (nextNode.children['*']) {
handlerSets.push(...this.getHandlerSets(nextNode.children['*'], method, true));
}
handlerSets.push(...this.getHandlerSets(nextNode, method));
matched = true;
}
tempNodes.push(nextNode);
}
for (let k = 0, len3 = node.patterns.length; k < len3; k++) {
const pattern = node.patterns[k];
// Wildcard
// '/hello/*/foo' => match /hello/bar/foo
if (pattern === '*') {
const astNode = node.children['*'];
if (astNode) {
handlerSets.push(...this.getHandlerSets(astNode, method));
tempNodes.push(astNode);
}
continue;
}
if (part === '')
continue;
// Named match
// `/posts/:id` => match /posts/123
const [key, name, matcher] = pattern;
if (matcher === true || (matcher instanceof RegExp && matcher.test(part))) {
if (typeof key === 'string') {
if (isLast === true) {
handlerSets.push(...this.getHandlerSets(node.children[key], method));
}
tempNodes.push(node.children[key]);
}
// '/book/a' => not-slug
// '/book/:slug' => slug
// GET /book/a ~> no-slug, param['slug'] => undefined
// GET /book/foo ~> slug, param['slug'] => foo
if (typeof name === 'string' && !matched) {
params[name] = part;
}
}
}
}
curNodes = tempNodes;
}
if (handlerSets.length <= 0)
return null;
const handlers = handlerSets
.sort((a, b) => {
return a.score - b.score;
})
.map((s) => {
return s.handler;
});
return { handlers, params };
}
}
exports.Node = Node;

View file

@ -0,0 +1,8 @@
import type { Result, Router } from '../../router';
import { Node } from './node';
export declare class TrieRouter<T> implements Router<T> {
node: Node<T>;
constructor();
add(method: string, path: string, handler: T): void;
match(method: string, path: string): Result<T> | null;
}

View file

@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TrieRouter = void 0;
const node_1 = require("./node");
class TrieRouter {
constructor() {
this.node = new node_1.Node();
}
add(method, path, handler) {
this.node.insert(method, path, handler);
}
match(method, path) {
return this.node.search(method, path);
}
}
exports.TrieRouter = TrieRouter;

View file

@ -0,0 +1,2 @@
export declare type Body = string | object | Record<string, string | File> | ArrayBuffer;
export declare const parseBody: (r: Request | Response) => Promise<Body>;

View file

@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseBody = void 0;
const parseBody = async (r) => {
const contentType = r.headers.get('Content-Type') || '';
if (contentType.includes('application/json')) {
let body = {};
try {
body = await r.json();
}
catch { } // Do nothing
return body;
}
else if (contentType.includes('application/text')) {
return await r.text();
}
else if (contentType.startsWith('text')) {
return await r.text();
}
else if (contentType.includes('form')) {
const form = {};
const data = [...(await r.formData())].reduce((acc, cur) => {
acc[cur[0]] = cur[1];
return acc;
}, form);
return data;
}
const arrayBuffer = await r.arrayBuffer();
return arrayBuffer;
};
exports.parseBody = parseBody;

View file

@ -0,0 +1,3 @@
export declare const equal: (a: ArrayBuffer, b: ArrayBuffer) => boolean;
export declare const timingSafeEqual: (a: string | object | boolean, b: string | object | boolean, hashFunction?: Function) => Promise<boolean>;
export declare const bufferToString: (buffer: ArrayBuffer) => string;

View file

@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.bufferToString = exports.timingSafeEqual = exports.equal = void 0;
const crypto_1 = require("./crypto");
const equal = (a, b) => {
if (a === b) {
return true;
}
if (a.byteLength !== b.byteLength) {
return false;
}
const va = new DataView(a);
const vb = new DataView(b);
let i = va.byteLength;
while (i--) {
if (va.getUint8(i) !== vb.getUint8(i)) {
return false;
}
}
return true;
};
exports.equal = equal;
const timingSafeEqual = async (a, b, hashFunction) => {
if (!hashFunction) {
hashFunction = crypto_1.sha256;
}
const sa = await hashFunction(a);
const sb = await hashFunction(b);
return sa === sb && a === b;
};
exports.timingSafeEqual = timingSafeEqual;
const bufferToString = (buffer) => {
if (buffer instanceof ArrayBuffer) {
const enc = new TextDecoder('utf-8');
return enc.decode(buffer);
}
return buffer;
};
exports.bufferToString = bufferToString;

View file

@ -0,0 +1,6 @@
/// <reference types="@cloudflare/workers-types" />
export declare type KVAssetOptions = {
manifest?: object | string;
namespace?: KVNamespace;
};
export declare const getContentFromKVAsset: (path: string, options?: KVAssetOptions) => Promise<ArrayBuffer | null>;

View file

@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getContentFromKVAsset = void 0;
const getContentFromKVAsset = async (path, options) => {
let ASSET_MANIFEST = {};
if (options && options.manifest) {
if (typeof options.manifest === 'string') {
ASSET_MANIFEST = JSON.parse(options.manifest);
}
else {
ASSET_MANIFEST = options.manifest;
}
}
else {
if (typeof __STATIC_CONTENT_MANIFEST === 'string') {
ASSET_MANIFEST = JSON.parse(__STATIC_CONTENT_MANIFEST);
}
else {
ASSET_MANIFEST = __STATIC_CONTENT_MANIFEST;
}
}
let ASSET_NAMESPACE;
if (options && options.namespace) {
ASSET_NAMESPACE = options.namespace;
}
else {
ASSET_NAMESPACE = __STATIC_CONTENT;
}
const key = ASSET_MANIFEST[path] || path;
if (!key) {
return null;
}
let content = await ASSET_NAMESPACE.get(key, { type: 'arrayBuffer' });
if (content) {
content = content;
}
return content;
};
exports.getContentFromKVAsset = getContentFromKVAsset;

View file

@ -0,0 +1,13 @@
export declare type Cookie = Record<string, string>;
export declare type CookieOptions = {
domain?: string;
expires?: Date;
httpOnly?: boolean;
maxAge?: number;
path?: string;
secure?: boolean;
signed?: boolean;
sameSite?: 'Strict' | 'Lax' | 'None';
};
export declare const parse: (cookie: string) => Cookie;
export declare const serialize: (name: string, value: string, opt?: CookieOptions) => string;

View file

@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serialize = exports.parse = void 0;
const parse = (cookie) => {
const pairs = cookie.split(/;\s*/g);
const parsedCookie = {};
for (let i = 0, len = pairs.length; i < len; i++) {
const pair = pairs[i].split(/\s*=\s*([^\s]+)/);
parsedCookie[pair[0]] = decodeURIComponent(pair[1]);
}
return parsedCookie;
};
exports.parse = parse;
const serialize = (name, value, opt = {}) => {
value = encodeURIComponent(value);
let cookie = `${name}=${value}`;
if (opt.maxAge) {
cookie += `; Max-Age=${Math.floor(opt.maxAge)}`;
}
if (opt.domain) {
cookie += '; Domain=' + opt.domain;
}
if (opt.path) {
cookie += '; Path=' + opt.path;
}
if (opt.expires) {
cookie += '; Expires=' + opt.expires.toUTCString();
}
if (opt.httpOnly) {
cookie += '; HttpOnly';
}
if (opt.secure) {
cookie += '; Secure';
}
if (opt.sameSite) {
cookie += `; SameSite=${opt.sameSite}`;
}
return cookie;
};
exports.serialize = serialize;

View file

@ -0,0 +1,10 @@
declare type Algorithm = {
name: string;
alias: string;
};
declare type Data = string | boolean | number | object | ArrayBufferView | ArrayBuffer | ReadableStream;
export declare const sha256: (data: Data) => Promise<string | null>;
export declare const sha1: (data: Data) => Promise<string | null>;
export declare const md5: (data: Data) => Promise<string | null>;
export declare const createHash: (data: Data, algorithm: Algorithm) => Promise<string | null>;
export {};

View file

@ -0,0 +1,53 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createHash = exports.md5 = exports.sha1 = exports.sha256 = void 0;
const sha256 = async (data) => {
const algorithm = { name: 'SHA-256', alias: 'sha256' };
const hash = await (0, exports.createHash)(data, algorithm);
return hash;
};
exports.sha256 = sha256;
const sha1 = async (data) => {
const algorithm = { name: 'SHA-1', alias: 'sha1' };
const hash = await (0, exports.createHash)(data, algorithm);
return hash;
};
exports.sha1 = sha1;
const md5 = async (data) => {
const algorithm = { name: 'MD5', alias: 'md5' };
const hash = await (0, exports.createHash)(data, algorithm);
return hash;
};
exports.md5 = md5;
const createHash = async (data, algorithm) => {
let sourceBuffer;
if (data instanceof ReadableStream) {
let body = '';
const reader = data.getReader();
await reader?.read().then(async (chuck) => {
const value = await (0, exports.createHash)(chuck.value || '', algorithm);
body += value;
});
return body;
}
if (ArrayBuffer.isView(data) || data instanceof ArrayBuffer) {
sourceBuffer = data;
}
else {
if (typeof data === 'object') {
data = JSON.stringify(data);
}
sourceBuffer = new TextEncoder().encode(String(data));
}
if (crypto && crypto.subtle) {
const buffer = await crypto.subtle.digest({
name: algorithm.name,
}, sourceBuffer);
const hash = Array.prototype.map
.call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2))
.join('');
return hash;
}
return null;
};
exports.createHash = createHash;

View file

@ -0,0 +1,7 @@
export declare const encodeBase64: (str: string) => string;
export declare const decodeBase64: (str: string) => string;
export declare const encodeBase64URL: (str: string) => string;
export declare const decodeBase64URL: (str: string) => string;
export declare const utf8ToUint8Array: (str: string) => Uint8Array;
export declare const arrayBufferToBase64: (buf: ArrayBuffer) => Promise<string>;
export declare const arrayBufferToBase64URL: (buf: ArrayBuffer) => Promise<string>;

View file

@ -0,0 +1,80 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.arrayBufferToBase64URL = exports.arrayBufferToBase64 = exports.utf8ToUint8Array = exports.decodeBase64URL = exports.encodeBase64URL = exports.decodeBase64 = exports.encodeBase64 = void 0;
const encodeBase64 = (str) => {
if (str === null) {
throw new TypeError('1st argument of "encodeBase64" should not be null.');
}
try {
const encoder = new TextEncoder();
const bytes = encoder.encode(str);
return btoa(String.fromCharCode(...bytes));
}
catch { }
try {
return Buffer.from(str).toString('base64');
}
catch (e) {
console.error('If you want to do "encodeBase64", polyfill "buffer" module.');
throw e;
}
};
exports.encodeBase64 = encodeBase64;
const decodeBase64 = (str) => {
if (str === null) {
throw new TypeError('1st argument of "decodeBase64" should not be null.');
}
try {
const text = atob(str);
const bytes = new Uint8Array(text.split('').map((c) => c.charCodeAt(0)));
const decoder = new TextDecoder();
return decoder.decode(bytes);
}
catch { }
try {
return Buffer.from(str, 'base64').toString();
}
catch (e) {
console.error('If you want to do "decodeBase64", polyfill "buffer" module.');
throw e;
}
};
exports.decodeBase64 = decodeBase64;
const encodeBase64URL = (str) => {
return (0, exports.encodeBase64)(str).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
};
exports.encodeBase64URL = encodeBase64URL;
const decodeBase64URL = (str) => {
const pad = (s) => {
const diff = s.length % 4;
if (diff === 2) {
return `${s}==`;
}
if (diff === 3) {
return `${s}=`;
}
return s;
};
return (0, exports.decodeBase64)(pad(str).replace(/-/g, '+').replace('_', '/'));
};
exports.decodeBase64URL = decodeBase64URL;
const utf8ToUint8Array = (str) => {
const encoder = new TextEncoder();
return encoder.encode(str);
};
exports.utf8ToUint8Array = utf8ToUint8Array;
const arrayBufferToBase64 = async (buf) => {
if (typeof btoa === 'function') {
return btoa(String.fromCharCode(...new Uint8Array(buf)));
}
try {
return Buffer.from(String.fromCharCode(...new Uint8Array(buf))).toString('base64');
}
catch (e) { }
return '';
};
exports.arrayBufferToBase64 = arrayBufferToBase64;
const arrayBufferToBase64URL = async (buf) => {
return (await (0, exports.arrayBufferToBase64)(buf)).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
};
exports.arrayBufferToBase64URL = arrayBufferToBase64URL;

View file

@ -0,0 +1,7 @@
declare type FilePathOptions = {
filename: string;
root?: string;
defaultDocument?: string;
};
export declare const getFilePath: (options: FilePathOptions) => string;
export {};

View file

@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFilePath = void 0;
const getFilePath = (options) => {
let filename = options.filename;
let root = options.root || '';
const defaultDocument = options.defaultDocument || 'index.html';
if (filename.endsWith('/')) {
// /top/ => /top/index.html
filename = filename.concat(defaultDocument);
}
else if (!filename.match(/\.[a-zA-Z0-9]+$/)) {
// /top => /top/index.html
filename = filename.concat('/' + defaultDocument);
}
// /foo.html => foo.html
filename = filename.replace(/^\.?\//, '');
// assets/ => assets
root = root.replace(/\/$/, '');
// ./assets/foo.html => assets/foo.html
let path = root ? root + '/' + filename : filename;
path = path.replace(/^\.?\//, '');
return path;
};
exports.getFilePath = getFilePath;

View file

@ -0,0 +1,6 @@
export declare type HtmlEscaped = {
isEscaped: true;
};
export declare type HtmlEscapedString = string & HtmlEscaped;
export declare type StringBuffer = [string];
export declare const escapeToBuffer: (str: string, buffer: StringBuffer) => void;

View file

@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.escapeToBuffer = void 0;
// The `escapeToBuffer` implementation is based on code from the MIT licensed `react-dom` package.
// https://github.com/facebook/react/blob/main/packages/react-dom/src/server/escapeTextForBrowser.js
const escapeRe = /[&<>"]/;
const escapeToBuffer = (str, buffer) => {
const match = str.search(escapeRe);
if (match === -1) {
buffer[0] += str;
return;
}
let escape;
let index;
let lastIndex = 0;
for (index = match; index < str.length; index++) {
switch (str.charCodeAt(index)) {
case 34: // "
escape = '&quot;';
break;
case 38: // &
escape = '&amp;';
break;
case 60: // <
escape = '&lt;';
break;
case 62: // >
escape = '&gt;';
break;
default:
continue;
}
buffer[0] += str.substring(lastIndex, index) + escape;
lastIndex = index + 1;
}
buffer[0] += str.substring(lastIndex, index);
};
exports.escapeToBuffer = escapeToBuffer;

View file

@ -0,0 +1,2 @@
export declare const getStatusText: (statusCode: StatusCode) => string;
export declare type StatusCode = 100 | 101 | 102 | 103 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 444 | 449 | 450 | 451 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 598 | 599;

View file

@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getStatusText = void 0;
const getStatusText = (statusCode) => {
const text = statuses[statusCode];
return text;
};
exports.getStatusText = getStatusText;
const statuses = {
100: 'Continue',
101: 'Switching Protocols',
102: 'Processing',
103: 'Early Hints',
200: 'OK',
201: 'Created',
202: 'Accepted',
204: 'No Content',
206: 'Partial Content',
301: 'Moved Permanently',
302: 'Moved Temporarily',
303: 'See Other',
304: 'Not Modified',
307: 'Temporary Redirect',
308: 'Permanent Redirect',
400: 'Bad Request',
401: 'Unauthorized',
402: 'Payment Required',
403: 'Forbidden',
404: 'Not Found',
405: 'Not Allowed',
406: 'Not Acceptable',
408: 'Request Time-out',
409: 'Conflict',
410: 'Gone',
411: 'Length Required',
412: 'Precondition Failed',
413: 'Request Entity Too Large',
414: 'Request-URI Too Large',
415: 'Unsupported Media Type',
416: 'Requested Range Not Satisfiable',
421: 'Misdirected Request',
429: 'Too Many Requests',
500: 'Internal Server Error',
501: 'Not Implemented',
502: 'Bad Gateway',
503: 'Service Temporarily Unavailable',
504: 'Gateway Time-out',
505: 'HTTP Version Not Supported',
507: 'Insufficient Storage',
};

View file

@ -0,0 +1 @@
export * as Jwt from './jwt';

View file

@ -0,0 +1,27 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Jwt = void 0;
exports.Jwt = __importStar(require("./jwt"));

View file

@ -0,0 +1,7 @@
import { AlgorithmTypes } from './types';
export declare const sign: (payload: unknown, secret: string, alg?: AlgorithmTypes) => Promise<string>;
export declare const verify: (token: string, secret: string, alg?: AlgorithmTypes) => Promise<boolean>;
export declare const decode: (token: string) => {
header: any;
payload: any;
};

View file

@ -0,0 +1,101 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.decode = exports.verify = exports.sign = void 0;
const encode_1 = require("../../utils/encode");
const types_1 = require("./types");
const types_2 = require("./types");
var CryptoKeyFormat;
(function (CryptoKeyFormat) {
CryptoKeyFormat["RAW"] = "raw";
CryptoKeyFormat["PKCS8"] = "pkcs8";
CryptoKeyFormat["SPKI"] = "spki";
CryptoKeyFormat["JWK"] = "jwk";
})(CryptoKeyFormat || (CryptoKeyFormat = {}));
var CryptoKeyUsage;
(function (CryptoKeyUsage) {
CryptoKeyUsage["Ecrypt"] = "encrypt";
CryptoKeyUsage["Decrypt"] = "decrypt";
CryptoKeyUsage["Sign"] = "sign";
CryptoKeyUsage["Verify"] = "verify";
CryptoKeyUsage["Deriverkey"] = "deriveKey";
CryptoKeyUsage["DeriveBits"] = "deriveBits";
CryptoKeyUsage["WrapKey"] = "wrapKey";
CryptoKeyUsage["UnwrapKey"] = "unwrapKey";
})(CryptoKeyUsage || (CryptoKeyUsage = {}));
const param = (name) => {
switch (name.toUpperCase()) {
case 'HS256':
return {
name: 'HMAC',
hash: {
name: 'SHA-256',
},
};
case 'HS384':
return {
name: 'HMAC',
hash: {
name: 'SHA-384',
},
};
case 'HS512':
return {
name: 'HMAC',
hash: {
name: 'SHA-512',
},
};
default:
throw new types_2.JwtAlgorithmNotImplemented(name);
}
};
const signing = async (data, secret, alg = types_1.AlgorithmTypes.HS256) => {
if (!crypto.subtle || !crypto.subtle.importKey) {
throw new Error('`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.');
}
const cryptoKey = await crypto.subtle.importKey(CryptoKeyFormat.RAW, (0, encode_1.utf8ToUint8Array)(secret), param(alg), false, [CryptoKeyUsage.Sign]);
return await crypto.subtle.sign(param(alg), cryptoKey, (0, encode_1.utf8ToUint8Array)(data));
};
const sign = async (payload, secret, alg = types_1.AlgorithmTypes.HS256) => {
const encodedPayload = await (0, encode_1.encodeBase64URL)(JSON.stringify(payload));
const encodedHeader = await (0, encode_1.encodeBase64URL)(JSON.stringify({ alg, typ: 'JWT' }));
const partialToken = `${encodedHeader}.${encodedPayload}`;
const signature = await (0, encode_1.arrayBufferToBase64URL)(await signing(partialToken, secret, alg));
return `${partialToken}.${signature}`;
};
exports.sign = sign;
const verify = async (token, secret, alg = types_1.AlgorithmTypes.HS256) => {
const tokenParts = token.split('.');
if (tokenParts.length !== 3) {
throw new types_2.JwtTokenInvalid(token);
}
const { payload } = (0, exports.decode)(token);
if (payload.nbf && payload.nbf > Math.floor(Date.now() / 1000)) {
throw new types_2.JwtTokenNotBefore(token);
}
if (payload.exp && payload.exp <= Math.floor(Date.now() / 1000)) {
throw new types_2.JwtTokenExpired(token);
}
const signature = await (0, encode_1.arrayBufferToBase64URL)(await signing(tokenParts.slice(0, 2).join('.'), secret, alg));
if (signature !== tokenParts[2]) {
throw new types_2.JwtTokenSignatureMismatched(token);
}
return true;
};
exports.verify = verify;
// eslint-disable-next-line
const decode = (token) => {
try {
const [h, p] = token.split('.');
const header = JSON.parse((0, encode_1.decodeBase64URL)(h));
const payload = JSON.parse((0, encode_1.decodeBase64URL)(p));
return {
header,
payload,
};
}
catch (e) {
throw new types_2.JwtTokenInvalid(token);
}
};
exports.decode = decode;

View file

@ -0,0 +1,25 @@
export declare class JwtAlgorithmNotImplemented extends Error {
constructor(token: string);
}
/**
* Export for backward compatibility
* @deprecated Use JwtAlgorithmNotImplemented instead
**/
export declare const JwtAlorithmNotImplemented: typeof JwtAlgorithmNotImplemented;
export declare class JwtTokenInvalid extends Error {
constructor(token: string);
}
export declare class JwtTokenNotBefore extends Error {
constructor(token: string);
}
export declare class JwtTokenExpired extends Error {
constructor(token: string);
}
export declare class JwtTokenSignatureMismatched extends Error {
constructor(token: string);
}
export declare enum AlgorithmTypes {
HS256 = "HS256",
HS384 = "HS384",
HS512 = "HS512"
}

View file

@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AlgorithmTypes = exports.JwtTokenSignatureMismatched = exports.JwtTokenExpired = exports.JwtTokenNotBefore = exports.JwtTokenInvalid = exports.JwtAlorithmNotImplemented = exports.JwtAlgorithmNotImplemented = void 0;
class JwtAlgorithmNotImplemented extends Error {
constructor(token) {
super(`invalid JWT token: ${token}`);
this.name = 'JwtAlgorithmNotImplemented';
}
}
exports.JwtAlgorithmNotImplemented = JwtAlgorithmNotImplemented;
/**
* Export for backward compatibility
* @deprecated Use JwtAlgorithmNotImplemented instead
**/
exports.JwtAlorithmNotImplemented = JwtAlgorithmNotImplemented;
class JwtTokenInvalid extends Error {
constructor(token) {
super(`invalid JWT token: ${token}`);
this.name = 'JwtTokenInvalid';
}
}
exports.JwtTokenInvalid = JwtTokenInvalid;
class JwtTokenNotBefore extends Error {
constructor(token) {
super(`token (${token}) is being used before it's valid`);
this.name = 'JwtTokenNotBefore';
}
}
exports.JwtTokenNotBefore = JwtTokenNotBefore;
class JwtTokenExpired extends Error {
constructor(token) {
super(`token (${token}) expired`);
this.name = 'JwtTokenExpired';
}
}
exports.JwtTokenExpired = JwtTokenExpired;
class JwtTokenSignatureMismatched extends Error {
constructor(token) {
super(`token(${token}) signature mismatched`);
this.name = 'JwtTokenSignatureMismatched';
}
}
exports.JwtTokenSignatureMismatched = JwtTokenSignatureMismatched;
var AlgorithmTypes;
(function (AlgorithmTypes) {
AlgorithmTypes["HS256"] = "HS256";
AlgorithmTypes["HS384"] = "HS384";
AlgorithmTypes["HS512"] = "HS512";
})(AlgorithmTypes = exports.AlgorithmTypes || (exports.AlgorithmTypes = {}));

View file

@ -0,0 +1 @@
export declare const getMimeType: (filename: string) => string | undefined;

View file

@ -0,0 +1,92 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMimeType = void 0;
const getMimeType = (filename) => {
const regexp = /\.([a-zA-Z0-9]+?)$/;
const match = filename.match(regexp);
if (!match)
return;
let mimeType = mimes[match[1]];
if ((mimeType && mimeType.startsWith('text')) || mimeType === 'application/json') {
mimeType += '; charset=utf-8';
}
return mimeType;
};
exports.getMimeType = getMimeType;
const mimes = {
aac: 'audio/aac',
abw: 'application/x-abiword',
arc: 'application/x-freearc',
avi: 'video/x-msvideo',
azw: 'application/vnd.amazon.ebook',
bin: 'application/octet-stream',
bmp: 'image/bmp',
bz: 'application/x-bzip',
bz2: 'application/x-bzip2',
csh: 'application/x-csh',
css: 'text/css',
csv: 'text/csv',
doc: 'application/msword',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
eot: 'application/vnd.ms-fontobject',
epub: 'application/epub+zip',
gz: 'application/gzip',
gif: 'image/gif',
htm: 'text/html',
html: 'text/html',
ico: 'image/x-icon',
ics: 'text/calendar',
jar: 'application/java-archive',
jpeg: 'image/jpeg',
jpg: 'image/jpeg',
js: 'text/javascript',
json: 'application/json',
jsonld: 'application/ld+json',
map: 'application/json',
mid: 'audio/x-midi',
midi: 'audio/x-midi',
mjs: 'text/javascript',
mp3: 'audio/mpeg',
mpeg: 'video/mpeg',
mpkg: 'application/vnd.apple.installer+xml',
odp: 'application/vnd.oasis.opendocument.presentation',
ods: 'application/vnd.oasis.opendocument.spreadsheet',
odt: 'application/vnd.oasis.opendocument.text',
oga: 'audio/ogg',
ogv: 'video/ogg',
ogx: 'application/ogg',
opus: 'audio/opus',
otf: 'font/otf',
png: 'image/png',
pdf: 'application/pdf',
php: 'application/php',
ppt: 'application/vnd.ms-powerpoint',
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
rar: 'application/vnd.rar',
rtf: 'application/rtf',
sh: 'application/x-sh',
svg: 'image/svg+xml',
swf: 'application/x-shockwave-flash',
tar: 'application/x-tar',
tif: 'image/tiff',
tiff: 'image/tiff',
ts: 'video/mp2t',
ttf: 'font/ttf',
txt: 'text/plain',
vsd: 'application/vnd.visio',
wav: 'audio/wav',
weba: 'audio/webm',
webm: 'video/webm',
webp: 'image/webp',
woff: 'font/woff',
woff2: 'font/woff2',
xhtml: 'application/xhtml+xml',
xls: 'application/vnd.ms-excel',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
xml: 'application/xml',
xul: 'application/vnd.mozilla.xul+xml',
zip: 'application/zip',
'3gp': 'video/3gpp',
'3g2': 'video/3gpp2',
'7z': 'application/x-7z-compressed',
};

View file

@ -0,0 +1,6 @@
export declare type Pattern = readonly [string, string, RegExp | true] | '*';
export declare const splitPath: (path: string) => string[];
export declare const getPattern: (label: string) => Pattern | null;
export declare const getPathFromURL: (url: string, strict?: boolean) => string;
export declare const isAbsoluteURL: (url: string) => boolean;
export declare const mergePath: (...paths: string[]) => string;

View file

@ -0,0 +1,83 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergePath = exports.isAbsoluteURL = exports.getPathFromURL = exports.getPattern = exports.splitPath = void 0;
const URL_REGEXP = /^https?:\/\/[a-zA-Z0-9\-\.:]+(\/?[^?#]*)/;
const splitPath = (path) => {
const paths = path.split(/\//); // faster than path.split('/')
if (paths[0] === '') {
paths.shift();
}
return paths;
};
exports.splitPath = splitPath;
const patternCache = {};
const getPattern = (label) => {
// * => wildcard
// :id{[0-9]+} => ([0-9]+)
// :id => (.+)
//const name = ''
if (label === '*') {
return '*';
}
const match = label.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
if (match) {
if (!patternCache[label]) {
if (match[2]) {
patternCache[label] = [label, match[1], new RegExp('^' + match[2] + '$')];
}
else {
patternCache[label] = [label, match[1], true];
}
}
return patternCache[label];
}
return null;
};
exports.getPattern = getPattern;
const getPathFromURL = (url, strict = true) => {
const queryIndex = url.indexOf('?');
const result = url.substring(url.indexOf('/', 8), queryIndex === -1 ? url.length : queryIndex);
// if strict routing is false => `/hello/hey/` and `/hello/hey` are treated the same
// default is true
if (strict === false && result.endsWith('/')) {
return result.slice(0, -1);
}
return result;
};
exports.getPathFromURL = getPathFromURL;
const isAbsoluteURL = (url) => {
const match = url.match(URL_REGEXP);
if (match) {
return true;
}
return false;
};
exports.isAbsoluteURL = isAbsoluteURL;
const mergePath = (...paths) => {
let p = '';
let endsWithSlash = false;
for (let path of paths) {
/* ['/hey/','/say'] => ['/hey', '/say'] */
if (p.endsWith('/')) {
p = p.slice(0, -1);
endsWithSlash = true;
}
/* ['/hey','say'] => ['/hey', '/say'] */
if (!path.startsWith('/')) {
path = `/${path}`;
}
/* ['/hey/', '/'] => `/hey/` */
if (path === '/' && endsWithSlash) {
p = `${p}/`;
}
else if (path !== '/') {
p = `${p}${path}`;
}
/* ['/', '/'] => `/` */
if (path === '/' && p === '') {
p = '/';
}
}
return p;
};
exports.mergePath = mergePath;

168
cli/bench/testdata/npm/hono/package.json vendored Normal file
View file

@ -0,0 +1,168 @@
{
"name": "hono",
"version": "2.0.9",
"description": "Ultrafast web framework for Cloudflare Workers.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"test": "jest",
"test:deno": "deno test --allow-read deno_test",
"test:bun": "bun wiptest --jsx-import-source ../src/middleware/jsx/jsx-dev-runtime bun_test/index.test.tsx",
"test:all": "yarn test && yarn test:deno && yarn test:bun",
"lint": "eslint --ext js,ts src .eslintrc.js",
"lint:fix": "eslint --ext js,ts src .eslintrc.js --fix",
"denoify": "rimraf deno_dist && denoify && rimraf 'deno_dist/**/*.test.ts'",
"build": "rimraf dist && tsc --project tsconfig.build.esm.json && tsc --project tsconfig.build.json",
"watch": "tsc --project tsconfig.build.json -w",
"prerelease": "yarn denoify && yarn test:deno && yarn build",
"release": "np"
},
"exports": {
".": "./dist/index.js",
"./basic-auth": "./dist/middleware/basic-auth/index.js",
"./bearer-auth": "./dist/middleware/bearer-auth/index.js",
"./cache": "./dist/middleware/cache/index.js",
"./compress": "./dist/middleware/compress/index.js",
"./cors": "./dist/middleware/cors/index.js",
"./etag": "./dist/middleware/etag/index.js",
"./html": "./dist/middleware/html/index.js",
"./jsx": "./dist/middleware/jsx/index.js",
"./jsx/jsx-dev-runtime": "./dist/middleware/jsx/jsx-dev-runtime.js",
"./jsx/jsx-runtime": "./dist/middleware/jsx/jsx-runtime.js",
"./jwt": "./dist/middleware/jwt/index.js",
"./logger": "./dist/middleware/logger/index.js",
"./powered-by": "./dist/middleware/powered-by/index.js",
"./pretty-json": "./dist/middleware/pretty-json/index.js",
"./serve-static": "./dist/middleware/serve-static/index.js",
"./serve-static.bun": "./dist/middleware/serve-static/bun.js",
"./serve-static.module": "./dist/middleware/serve-static/module.mjs",
"./router/trie-router": "./dist/router/trie-router/index.js",
"./router/reg-exp-router": "./dist/router/reg-exp-router/index.js",
"./utils/jwt": "./dist/utils/jwt/index.js",
"./utils/*": "./dist/utils/*.js"
},
"typesVersions": {
"*": {
"basic-auth": [
"./dist/middleware/basic-auth"
],
"bearer-auth": [
"./dist/middleware/bearer-auth"
],
"cache": [
"./dist/middleware/cache"
],
"compress": [
"./dist/middleware/compress"
],
"cors": [
"./dist/middleware/cors"
],
"etag": [
"./dist/middleware/etag"
],
"html": [
"./dist/middleware/html"
],
"jsx": [
"./dist/middleware/jsx"
],
"jsx/jsx-runtime": [
"./dist/middleware/jsx/jsx-runtime.d.ts"
],
"jsx/jsx-dev-runtime": [
"./dist/middleware/jsx/jsx-dev-runtime.d.ts"
],
"jwt": [
"./dist/middleware/jwt"
],
"logger": [
"./dist/middleware/logger"
],
"powered-by": [
"./dist/middleware/powered-by"
],
"pretty-json": [
"./dist/middleware/pretty-json"
],
"serve-static": [
"./dist/middleware/serve-static/index.d.ts"
],
"serve-static.bun": [
"./dist/middleware/serve-static/bun.d.ts"
],
"serve-static.module": [
"./dist/middleware/serve-static/module.d.mts"
],
"router/trie-router": [
"./dist/router/trie-router/router.d.ts"
],
"router/reg-exp-router": [
"./dist/router/reg-exp-router/router.d.ts"
],
"utils/jwt": [
"./dist/utils/jwt/index.d.ts"
],
"utils/*": [
"./dist/utils/*"
]
}
},
"author": "Yusuke Wada <yusuke@kamawada.com> (https://github.com/yusukebe)",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/honojs/hono.git"
},
"publishConfig": {
"registry": "https://registry.npmjs.org"
},
"homepage": "https://github.com/honojs/hono",
"keywords": [
"hono",
"web",
"app",
"http",
"application",
"framework",
"router",
"cloudflare",
"workers",
"fastly",
"compute@edge",
"deno",
"bun"
],
"devDependencies": {
"@cloudflare/workers-types": "^3.7.1",
"@types/crypto-js": "^4.1.1",
"@types/jest": "^27.4.1",
"@types/node": "^17.0.29",
"@typescript-eslint/eslint-plugin": "^5.21.0",
"@typescript-eslint/parser": "^5.21.0",
"crypto-js": "^4.1.1",
"denoify": "^0.11.1",
"eslint": "^8.14.0",
"eslint-config-prettier": "^8.5.0",
"eslint-define-config": "^1.4.0",
"eslint-import-resolver-typescript": "^2.7.1",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-node": "^11.1.0",
"form-data": "^4.0.0",
"jest": "27.5.1",
"jest-environment-miniflare": "^2.6.0",
"np": "^7.6.2",
"prettier": "^2.6.2",
"rimraf": "^3.0.2",
"ts-jest": "^27.1.4",
"typescript": "^4.6.3"
},
"engines": {
"node": ">=11.0.0"
}
}

View file

@ -21,6 +21,7 @@ async function dlint() {
":!:cli/tests/testdata/038_checkjs.js", ":!:cli/tests/testdata/038_checkjs.js",
":!:cli/tests/testdata/error_008_checkjs.js", ":!:cli/tests/testdata/error_008_checkjs.js",
":!:cli/bench/http/node*.js", ":!:cli/bench/http/node*.js",
":!:cli/bench/testdata/npm/*",
":!:cli/bench/testdata/express-router.js", ":!:cli/bench/testdata/express-router.js",
":!:cli/bench/testdata/react-dom.js", ":!:cli/bench/testdata/react-dom.js",
":!:cli/compilers/wasm_wrap.js", ":!:cli/compilers/wasm_wrap.js",