diff options
author | Divy Srivastava <dj.srivastava23@gmail.com> | 2022-08-19 15:54:54 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-19 15:54:54 +0530 |
commit | 25a109d9ea27ad3a76fdce14bba283e953af9bce (patch) | |
tree | 68f0280065c9df4be8fa325ba82693879b4b46cd /cli/bench/testdata | |
parent | 9e576dff7c39cfd510c60ba92aa0d1c15fd24a6b (diff) |
chore(bench): add flash router benchmarks (#15495)
Diffstat (limited to 'cli/bench/testdata')
93 files changed, 3325 insertions, 0 deletions
diff --git a/cli/bench/testdata/npm/hono/LICENSE b/cli/bench/testdata/npm/hono/LICENSE new file mode 100644 index 000000000..c9e631261 --- /dev/null +++ b/cli/bench/testdata/npm/hono/LICENSE @@ -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. diff --git a/cli/bench/testdata/npm/hono/README.md b/cli/bench/testdata/npm/hono/README.md new file mode 100644 index 000000000..763e3898a --- /dev/null +++ b/cli/bench/testdata/npm/hono/README.md @@ -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 /> + +[](https://github.com/honojs/hono/actions) +[](https://github.com/honojs/hono/blob/main/LICENSE) +[](https://www.npmjs.com/package/hono) +[](https://www.npmjs.com/package/hono) +[](https://www.npmjs.com/package/hono) +[](https://github.com/honojs/hono/pulse) +[](https://github.com/honojs/hono/commits/main) +[](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. diff --git a/cli/bench/testdata/npm/hono/dist/compose.d.ts b/cli/bench/testdata/npm/hono/dist/compose.d.ts new file mode 100644 index 000000000..495b9bda5 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/compose.d.ts @@ -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>; diff --git a/cli/bench/testdata/npm/hono/dist/compose.js b/cli/bench/testdata/npm/hono/dist/compose.js new file mode 100644 index 000000000..3c7e57722 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/compose.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/context.d.ts b/cli/bench/testdata/npm/hono/dist/context.d.ts new file mode 100644 index 000000000..1f67f6206 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/context.d.ts @@ -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 {}; diff --git a/cli/bench/testdata/npm/hono/dist/context.js b/cli/bench/testdata/npm/hono/dist/context.js new file mode 100644 index 000000000..bfbeb4200 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/context.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/hono.d.ts b/cli/bench/testdata/npm/hono/dist/hono.d.ts new file mode 100644 index 000000000..f234d1675 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/hono.d.ts @@ -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 {}; diff --git a/cli/bench/testdata/npm/hono/dist/hono.js b/cli/bench/testdata/npm/hono/dist/hono.js new file mode 100644 index 000000000..0b6e67b7f --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/hono.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/index.d.ts b/cli/bench/testdata/npm/hono/dist/index.d.ts new file mode 100644 index 000000000..92c9f1881 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/index.d.ts @@ -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 }; diff --git a/cli/bench/testdata/npm/hono/dist/index.js b/cli/bench/testdata/npm/hono/dist/index.js new file mode 100644 index 000000000..a11e3395f --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/index.js @@ -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)); + }); +}; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/basic-auth/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/basic-auth/index.d.ts new file mode 100644 index 000000000..072df608f --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/basic-auth/index.d.ts @@ -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>; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/basic-auth/index.js b/cli/bench/testdata/npm/hono/dist/middleware/basic-auth/index.js new file mode 100644 index 000000000..c58244a3c --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/basic-auth/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/bearer-auth/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/bearer-auth/index.d.ts new file mode 100644 index 000000000..972177fc7 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/bearer-auth/index.d.ts @@ -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>; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/bearer-auth/index.js b/cli/bench/testdata/npm/hono/dist/middleware/bearer-auth/index.js new file mode 100644 index 000000000..da5a78921 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/bearer-auth/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/cache/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/cache/index.d.ts new file mode 100644 index 000000000..c0390c41e --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/cache/index.d.ts @@ -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>; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/cache/index.js b/cli/bench/testdata/npm/hono/dist/middleware/cache/index.js new file mode 100644 index 000000000..890058af9 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/cache/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/compress/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/compress/index.d.ts new file mode 100644 index 000000000..53f39ad86 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/compress/index.d.ts @@ -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 {}; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/compress/index.js b/cli/bench/testdata/npm/hono/dist/middleware/compress/index.js new file mode 100644 index 000000000..fd605cf71 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/compress/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/cors/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/cors/index.d.ts new file mode 100644 index 000000000..f353df808 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/cors/index.d.ts @@ -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 {}; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/cors/index.js b/cli/bench/testdata/npm/hono/dist/middleware/cors/index.js new file mode 100644 index 000000000..441de0928 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/cors/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/etag/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/etag/index.d.ts new file mode 100644 index 000000000..8cb945e14 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/etag/index.d.ts @@ -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 {}; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/etag/index.js b/cli/bench/testdata/npm/hono/dist/middleware/etag/index.js new file mode 100644 index 000000000..60800edf1 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/etag/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/html/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/html/index.d.ts new file mode 100644 index 000000000..976a7d1db --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/html/index.d.ts @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/html/index.js b/cli/bench/testdata/npm/hono/dist/middleware/html/index.js new file mode 100644 index 000000000..598621bf0 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/html/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/jsx/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/jsx/index.d.ts new file mode 100644 index 000000000..3222b2b36 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/jsx/index.d.ts @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/jsx/index.js b/cli/bench/testdata/npm/hono/dist/middleware/jsx/index.js new file mode 100644 index 000000000..f72a7ecb9 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/jsx/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-dev-runtime.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-dev-runtime.d.ts new file mode 100644 index 000000000..d76fc7124 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-dev-runtime.d.ts @@ -0,0 +1,2 @@ +import type { JSXNode } from '.'; +export declare function jsxDEV(tag: string | Function, props: Record<string, any>): JSXNode; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-dev-runtime.js b/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-dev-runtime.js new file mode 100644 index 000000000..6aa4bfa00 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-dev-runtime.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-runtime.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-runtime.d.ts new file mode 100644 index 000000000..4d505cdf8 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-runtime.d.ts @@ -0,0 +1,2 @@ +export { jsxDEV as jsx } from './jsx-dev-runtime'; +export { jsxDEV as jsxs } from './jsx-dev-runtime'; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-runtime.js b/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-runtime.js new file mode 100644 index 000000000..ee8c3fe72 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/jsx/jsx-runtime.js @@ -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; } }); diff --git a/cli/bench/testdata/npm/hono/dist/middleware/jwt/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/jwt/index.d.ts new file mode 100644 index 000000000..5b13b4816 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/jwt/index.d.ts @@ -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>; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/jwt/index.js b/cli/bench/testdata/npm/hono/dist/middleware/jwt/index.js new file mode 100644 index 000000000..6f486560b --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/jwt/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/logger/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/logger/index.d.ts new file mode 100644 index 000000000..232305d2f --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/logger/index.d.ts @@ -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 {}; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/logger/index.js b/cli/bench/testdata/npm/hono/dist/middleware/logger/index.js new file mode 100644 index 000000000..44cf994d0 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/logger/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/powered-by/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/powered-by/index.d.ts new file mode 100644 index 000000000..eb7995b80 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/powered-by/index.d.ts @@ -0,0 +1,3 @@ +import type { Context } from '../../context'; +import type { Next } from '../../hono'; +export declare const poweredBy: () => (c: Context, next: Next) => Promise<void>; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/powered-by/index.js b/cli/bench/testdata/npm/hono/dist/middleware/powered-by/index.js new file mode 100644 index 000000000..c1f73300b --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/powered-by/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/pretty-json/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/pretty-json/index.d.ts new file mode 100644 index 000000000..94736847e --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/pretty-json/index.d.ts @@ -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 {}; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/pretty-json/index.js b/cli/bench/testdata/npm/hono/dist/middleware/pretty-json/index.js new file mode 100644 index 000000000..08af0fb10 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/pretty-json/index.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/serve-static/bun.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/bun.d.ts new file mode 100644 index 000000000..be4515aff --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/bun.d.ts @@ -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>; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/serve-static/bun.js b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/bun.js new file mode 100644 index 000000000..d29a7d936 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/bun.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/serve-static/index.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/index.d.ts new file mode 100644 index 000000000..21bcb4dc3 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/index.d.ts @@ -0,0 +1 @@ +export { serveStatic } from './serve-static'; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/serve-static/index.js b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/index.js new file mode 100644 index 000000000..3748e9530 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/index.js @@ -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; } }); diff --git a/cli/bench/testdata/npm/hono/dist/middleware/serve-static/module.d.mts b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/module.d.mts new file mode 100644 index 000000000..36cd766f3 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/module.d.mts @@ -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 }; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/serve-static/module.mjs b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/module.mjs new file mode 100644 index 000000000..968fd4433 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/module.mjs @@ -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 }; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/serve-static/serve-static.d.ts b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/serve-static.d.ts new file mode 100644 index 000000000..5ff60c4ca --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/serve-static.d.ts @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/middleware/serve-static/serve-static.js b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/serve-static.js new file mode 100644 index 000000000..a647b7015 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/middleware/serve-static/serve-static.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/request.d.ts b/cli/bench/testdata/npm/hono/dist/request.d.ts new file mode 100644 index 000000000..aebc0b855 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/request.d.ts @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/request.js b/cli/bench/testdata/npm/hono/dist/request.js new file mode 100644 index 000000000..fb900d743 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/request.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/router.d.ts b/cli/bench/testdata/npm/hono/dist/router.d.ts new file mode 100644 index 000000000..753bbbc68 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router.d.ts @@ -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>; +} diff --git a/cli/bench/testdata/npm/hono/dist/router.js b/cli/bench/testdata/npm/hono/dist/router.js new file mode 100644 index 000000000..c861fe9ce --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router.js @@ -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'; diff --git a/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/index.d.ts b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/index.d.ts new file mode 100644 index 000000000..2d0f54ba3 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/index.d.ts @@ -0,0 +1 @@ +export { RegExpRouter } from './router'; diff --git a/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/index.js b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/index.js new file mode 100644 index 000000000..782e988d9 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/index.js @@ -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; } }); diff --git a/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/node.d.ts b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/node.d.ts new file mode 100644 index 000000000..ee52660f8 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/node.d.ts @@ -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; +} diff --git a/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/node.js b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/node.js new file mode 100644 index 000000000..c53a23c7b --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/node.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/router.d.ts b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/router.d.ts new file mode 100644 index 000000000..6d06b30bd --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/router.d.ts @@ -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 {}; diff --git a/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/router.js b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/router.js new file mode 100644 index 000000000..782bd8ba4 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/router.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/trie.d.ts b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/trie.d.ts new file mode 100644 index 000000000..3f57ff559 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/trie.d.ts @@ -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]; +} diff --git a/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/trie.js b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/trie.js new file mode 100644 index 000000000..dfcc657f3 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/reg-exp-router/trie.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/router/trie-router/index.d.ts b/cli/bench/testdata/npm/hono/dist/router/trie-router/index.d.ts new file mode 100644 index 000000000..3a38be4f9 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/trie-router/index.d.ts @@ -0,0 +1 @@ +export { TrieRouter } from './router'; diff --git a/cli/bench/testdata/npm/hono/dist/router/trie-router/index.js b/cli/bench/testdata/npm/hono/dist/router/trie-router/index.js new file mode 100644 index 000000000..7e966aa09 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/trie-router/index.js @@ -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; } }); diff --git a/cli/bench/testdata/npm/hono/dist/router/trie-router/node.d.ts b/cli/bench/testdata/npm/hono/dist/router/trie-router/node.d.ts new file mode 100644 index 000000000..a489b17f6 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/trie-router/node.d.ts @@ -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 {}; diff --git a/cli/bench/testdata/npm/hono/dist/router/trie-router/node.js b/cli/bench/testdata/npm/hono/dist/router/trie-router/node.js new file mode 100644 index 000000000..d40f604e7 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/trie-router/node.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/router/trie-router/router.d.ts b/cli/bench/testdata/npm/hono/dist/router/trie-router/router.d.ts new file mode 100644 index 000000000..e8a16efb5 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/trie-router/router.d.ts @@ -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; +} diff --git a/cli/bench/testdata/npm/hono/dist/router/trie-router/router.js b/cli/bench/testdata/npm/hono/dist/router/trie-router/router.js new file mode 100644 index 000000000..2ddad93c3 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/router/trie-router/router.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/body.d.ts b/cli/bench/testdata/npm/hono/dist/utils/body.d.ts new file mode 100644 index 000000000..87407539c --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/body.d.ts @@ -0,0 +1,2 @@ +export declare type Body = string | object | Record<string, string | File> | ArrayBuffer; +export declare const parseBody: (r: Request | Response) => Promise<Body>; diff --git a/cli/bench/testdata/npm/hono/dist/utils/body.js b/cli/bench/testdata/npm/hono/dist/utils/body.js new file mode 100644 index 000000000..5deeca1cd --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/body.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/buffer.d.ts b/cli/bench/testdata/npm/hono/dist/utils/buffer.d.ts new file mode 100644 index 000000000..f30e3f9d4 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/buffer.d.ts @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/buffer.js b/cli/bench/testdata/npm/hono/dist/utils/buffer.js new file mode 100644 index 000000000..58ee6f9ae --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/buffer.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/cloudflare.d.ts b/cli/bench/testdata/npm/hono/dist/utils/cloudflare.d.ts new file mode 100644 index 000000000..abdc25a5b --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/cloudflare.d.ts @@ -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>; diff --git a/cli/bench/testdata/npm/hono/dist/utils/cloudflare.js b/cli/bench/testdata/npm/hono/dist/utils/cloudflare.js new file mode 100644 index 000000000..4ba9a7e52 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/cloudflare.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/cookie.d.ts b/cli/bench/testdata/npm/hono/dist/utils/cookie.d.ts new file mode 100644 index 000000000..d467efde5 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/cookie.d.ts @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/cookie.js b/cli/bench/testdata/npm/hono/dist/utils/cookie.js new file mode 100644 index 000000000..13a18602f --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/cookie.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/crypto.d.ts b/cli/bench/testdata/npm/hono/dist/utils/crypto.d.ts new file mode 100644 index 000000000..540afb399 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/crypto.d.ts @@ -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 {}; diff --git a/cli/bench/testdata/npm/hono/dist/utils/crypto.js b/cli/bench/testdata/npm/hono/dist/utils/crypto.js new file mode 100644 index 000000000..0d259ae1c --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/crypto.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/encode.d.ts b/cli/bench/testdata/npm/hono/dist/utils/encode.d.ts new file mode 100644 index 000000000..e1db501e0 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/encode.d.ts @@ -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>; diff --git a/cli/bench/testdata/npm/hono/dist/utils/encode.js b/cli/bench/testdata/npm/hono/dist/utils/encode.js new file mode 100644 index 000000000..46e61c079 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/encode.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/filepath.d.ts b/cli/bench/testdata/npm/hono/dist/utils/filepath.d.ts new file mode 100644 index 000000000..99453d5d6 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/filepath.d.ts @@ -0,0 +1,7 @@ +declare type FilePathOptions = { + filename: string; + root?: string; + defaultDocument?: string; +}; +export declare const getFilePath: (options: FilePathOptions) => string; +export {}; diff --git a/cli/bench/testdata/npm/hono/dist/utils/filepath.js b/cli/bench/testdata/npm/hono/dist/utils/filepath.js new file mode 100644 index 000000000..43451a9cc --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/filepath.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/html.d.ts b/cli/bench/testdata/npm/hono/dist/utils/html.d.ts new file mode 100644 index 000000000..d8d5cd4e7 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/html.d.ts @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/html.js b/cli/bench/testdata/npm/hono/dist/utils/html.js new file mode 100644 index 000000000..920739092 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/html.js @@ -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 = '"'; + break; + case 38: // & + escape = '&'; + break; + case 60: // < + escape = '<'; + break; + case 62: // > + escape = '>'; + break; + default: + continue; + } + buffer[0] += str.substring(lastIndex, index) + escape; + lastIndex = index + 1; + } + buffer[0] += str.substring(lastIndex, index); +}; +exports.escapeToBuffer = escapeToBuffer; diff --git a/cli/bench/testdata/npm/hono/dist/utils/http-status.d.ts b/cli/bench/testdata/npm/hono/dist/utils/http-status.d.ts new file mode 100644 index 000000000..5002ec0ba --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/http-status.d.ts @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/http-status.js b/cli/bench/testdata/npm/hono/dist/utils/http-status.js new file mode 100644 index 000000000..295d3f1ec --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/http-status.js @@ -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', +}; diff --git a/cli/bench/testdata/npm/hono/dist/utils/jwt/index.d.ts b/cli/bench/testdata/npm/hono/dist/utils/jwt/index.d.ts new file mode 100644 index 000000000..8581a525a --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/jwt/index.d.ts @@ -0,0 +1 @@ +export * as Jwt from './jwt'; diff --git a/cli/bench/testdata/npm/hono/dist/utils/jwt/index.js b/cli/bench/testdata/npm/hono/dist/utils/jwt/index.js new file mode 100644 index 000000000..221dba30c --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/jwt/index.js @@ -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")); diff --git a/cli/bench/testdata/npm/hono/dist/utils/jwt/jwt.d.ts b/cli/bench/testdata/npm/hono/dist/utils/jwt/jwt.d.ts new file mode 100644 index 000000000..46aba49e5 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/jwt/jwt.d.ts @@ -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; +}; diff --git a/cli/bench/testdata/npm/hono/dist/utils/jwt/jwt.js b/cli/bench/testdata/npm/hono/dist/utils/jwt/jwt.js new file mode 100644 index 000000000..73a3f2df4 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/jwt/jwt.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/jwt/types.d.ts b/cli/bench/testdata/npm/hono/dist/utils/jwt/types.d.ts new file mode 100644 index 000000000..b3c8f3f17 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/jwt/types.d.ts @@ -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" +} diff --git a/cli/bench/testdata/npm/hono/dist/utils/jwt/types.js b/cli/bench/testdata/npm/hono/dist/utils/jwt/types.js new file mode 100644 index 000000000..638bdbe6e --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/jwt/types.js @@ -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 = {})); diff --git a/cli/bench/testdata/npm/hono/dist/utils/mime.d.ts b/cli/bench/testdata/npm/hono/dist/utils/mime.d.ts new file mode 100644 index 000000000..83e4db22e --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/mime.d.ts @@ -0,0 +1 @@ +export declare const getMimeType: (filename: string) => string | undefined; diff --git a/cli/bench/testdata/npm/hono/dist/utils/mime.js b/cli/bench/testdata/npm/hono/dist/utils/mime.js new file mode 100644 index 000000000..18aa76d52 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/mime.js @@ -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', +}; diff --git a/cli/bench/testdata/npm/hono/dist/utils/url.d.ts b/cli/bench/testdata/npm/hono/dist/utils/url.d.ts new file mode 100644 index 000000000..16e077f04 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/url.d.ts @@ -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; diff --git a/cli/bench/testdata/npm/hono/dist/utils/url.js b/cli/bench/testdata/npm/hono/dist/utils/url.js new file mode 100644 index 000000000..0e8fc33a3 --- /dev/null +++ b/cli/bench/testdata/npm/hono/dist/utils/url.js @@ -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; diff --git a/cli/bench/testdata/npm/hono/package.json b/cli/bench/testdata/npm/hono/package.json new file mode 100644 index 000000000..0adf8d494 --- /dev/null +++ b/cli/bench/testdata/npm/hono/package.json @@ -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" + } +} |