Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

hono

Package Overview
Dependencies
Maintainers
1
Versions
344
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hono - npm Package Compare versions

Comparing version 1.1.1 to 1.2.0

4

dist/compose.d.ts

@@ -1,2 +0,2 @@

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

@@ -6,17 +6,27 @@ "use strict";

// Based on the code in the MIT licensed `koa-compose` package.
const compose = (middleware, onError) => {
return function (context) {
const compose = (middleware, onError, onNotFound) => {
return async (context, next) => {
let index = -1;
return dispatch(0);
async function dispatch(i) {
if (i === middleware.length) {
return context;
}
if (i <= index) {
return Promise.reject(new Error('next() called multiple times'));
}
const handler = middleware[i];
let handler = middleware[i];
index = i;
if (i === middleware.length)
handler = next;
if (handler === undefined) {
if (context instanceof context_1.Context && context.res === undefined) {
context.res = onNotFound(context);
}
return Promise.resolve(context);
}
return Promise.resolve(handler(context, dispatch.bind(null, i + 1)))
.then(() => {
.then(async (res) => {
// If handler return Response like `return c.text('foo')`
if (res && context instanceof context_1.Context) {
context.res = res;
dispatch(i + 1); // <--- Call next()
}
return context;

@@ -26,3 +36,5 @@ })

if (onError && context instanceof context_1.Context) {
context.res = onError(err, context);
if (err instanceof Error) {
context.res = onError(err, context);
}
return context;

@@ -29,0 +41,0 @@ }

@@ -7,3 +7,2 @@ /// <reference types="@cloudflare/workers-types" />

export declare class Context<RequestParamKeyType = string, E = Env> {
#private;
req: Request<RequestParamKeyType>;

@@ -13,2 +12,7 @@ res: Response;

event: FetchEvent;
private _headers;
private _status;
private _statusText;
private _pretty;
private _prettySpace;
render: (template: string, params?: object, options?: object) => Promise<Response>;

@@ -15,0 +19,0 @@ notFound: () => Response | Promise<Response>;

"use strict";
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _Context_headers, _Context_status, _Context_statusText, _Context_pretty, _Context_prettySpace;
Object.defineProperty(exports, "__esModule", { value: true });

@@ -20,10 +8,10 @@ exports.Context = void 0;

constructor(req, opts) {
_Context_headers.set(this, void 0);
_Context_status.set(this, void 0);
_Context_statusText.set(this, void 0);
_Context_pretty.set(this, void 0);
_Context_prettySpace.set(this, 2);
this.res = undefined;
this._headers = {};
this._status = 200;
this._statusText = '';
this._pretty = false;
this._prettySpace = 2;
this.req = this.initRequest(req);
Object.assign(this, opts);
__classPrivateFieldSet(this, _Context_headers, {}, "f");
}

@@ -44,24 +32,20 @@ initRequest(req) {

}
__classPrivateFieldGet(this, _Context_headers, "f")[name] = value;
this._headers[name] = value;
}
status(status) {
if (this.res) {
console.warn('c.res.status is already set.');
return;
}
__classPrivateFieldSet(this, _Context_status, status, "f");
__classPrivateFieldSet(this, _Context_statusText, (0, http_status_1.getStatusText)(status), "f");
this._status = status;
this._statusText = (0, http_status_1.getStatusText)(status);
}
pretty(prettyJSON, space = 2) {
__classPrivateFieldSet(this, _Context_pretty, prettyJSON, "f");
__classPrivateFieldSet(this, _Context_prettySpace, space, "f");
this._pretty = prettyJSON;
this._prettySpace = space;
}
newResponse(data, init = {}) {
init.status = init.status || __classPrivateFieldGet(this, _Context_status, "f") || 200;
init.status = init.status || this._status || 200;
init.statusText =
init.statusText || __classPrivateFieldGet(this, _Context_statusText, "f") || (0, http_status_1.getStatusText)(init.status);
init.headers = Object.assign(Object.assign({}, __classPrivateFieldGet(this, _Context_headers, "f")), init.headers);
init.statusText || this._statusText || (0, http_status_1.getStatusText)(init.status);
init.headers = Object.assign(Object.assign({}, this._headers), init.headers);
return new Response(data, init);
}
body(data, status = __classPrivateFieldGet(this, _Context_status, "f"), headers = __classPrivateFieldGet(this, _Context_headers, "f")) {
body(data, status = this._status, headers = this._headers) {
return this.newResponse(data, {

@@ -72,3 +56,3 @@ status: status,

}
text(text, status = __classPrivateFieldGet(this, _Context_status, "f"), headers = {}) {
text(text, status = this._status, headers = {}) {
if (typeof text !== 'string') {

@@ -80,8 +64,8 @@ throw new TypeError('text method arg must be a string!');

}
json(object, status = __classPrivateFieldGet(this, _Context_status, "f"), headers = {}) {
json(object, status = this._status, headers = {}) {
if (typeof object !== 'object') {
throw new TypeError('json method arg must be an object!');
}
const body = __classPrivateFieldGet(this, _Context_pretty, "f")
? JSON.stringify(object, null, __classPrivateFieldGet(this, _Context_prettySpace, "f"))
const body = this._pretty
? JSON.stringify(object, null, this._prettySpace)
: JSON.stringify(object);

@@ -91,3 +75,3 @@ headers['Content-Type'] || (headers['Content-Type'] = 'application/json; charset=UTF-8');

}
html(html, status = __classPrivateFieldGet(this, _Context_status, "f"), headers = {}) {
html(html, status = this._status, headers = {}) {
if (typeof html !== 'string') {

@@ -117,2 +101,1 @@ throw new TypeError('html method arg must be a string!');

exports.Context = Context;
_Context_headers = new WeakMap(), _Context_status = new WeakMap(), _Context_statusText = new WeakMap(), _Context_pretty = new WeakMap(), _Context_prettySpace = new WeakMap();

@@ -5,2 +5,3 @@ /// <reference types="@cloudflare/workers-types" />

import type { Router } from './router';
import { METHOD_NAME_ALL_LOWERCASE } from './router';
declare global {

@@ -13,5 +14,4 @@ interface Request<ParamKeyType = string> {

}
export declare type Handler<RequestParamKeyType = string, E = Env> = (c: Context<RequestParamKeyType, E>) => Response | Promise<Response>;
export declare type MiddlewareHandler<E = Env> = (c: Context<string, E>, next: Next) => Promise<void>;
export declare type NotFoundHandler<E = Env> = (c: Context<string, E>) => Response | Promise<Response>;
export declare type Handler<RequestParamKeyType = string, E = Env> = (c: Context<RequestParamKeyType, E>, next?: Next) => Response | Promise<Response> | void | Promise<void>;
export declare type NotFoundHandler<E = Env> = (c: Context<string, E>) => Response;
export declare type ErrorHandler<E = Env> = (err: Error, c: Context<string, E>) => Response;

@@ -22,20 +22,43 @@ export declare type Next = () => Promise<void>;

declare type ParamKeys<Path> = Path extends `${infer Component}/${infer Rest}` ? ParamKey<Component> | ParamKeys<Rest> : ParamKey<Path>;
interface HandlerInterface<T extends string, E = Env> {
<Path extends string>(path: Path, handler: Handler<ParamKeys<Path>, E>): Hono<E, Path>;
(path: string, handler: Handler<string, E>): Hono<E, T>;
<Path extends T>(handler: Handler<ParamKeys<T>, E>): Hono<E, Path>;
(handler: Handler<string, E>): Hono<E, T>;
interface HandlerInterface<T extends string, E = 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;
}
declare const Hono_base: new <E_1 extends Env, T extends string>() => {
delete: HandlerInterface<T, E_1>;
get: HandlerInterface<T, E_1>;
post: HandlerInterface<T, E_1>;
put: HandlerInterface<T, E_1>;
head: HandlerInterface<T, E_1>;
options: HandlerInterface<T, E_1>;
patch: HandlerInterface<T, E_1>;
all: HandlerInterface<T, E_1>;
declare const methods: readonly ["get", "post", "put", "delete", "head", "options", "patch"];
declare type Methods = typeof methods[number] | typeof METHOD_NAME_ALL_LOWERCASE;
interface Routing<E extends Env> {
path: string;
method: Methods;
handler: Handler<string, E>;
}
declare const Route_base: new <E_1 extends Env, T extends string, U>() => {
delete: HandlerInterface<T, E_1, U>;
get: HandlerInterface<T, E_1, U>;
all: HandlerInterface<T, E_1, U>;
post: HandlerInterface<T, E_1, U>;
put: 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 = Env, P extends string = ''> extends Hono_base<E, P> {
export declare class Route<E = Env, P extends string = ''> extends Route_base<E, P, Route<E, P>> {
#private;
routes: Routing<E>[];
constructor();
private add;
}
declare const Hono_base: new <E_1 extends Env, T extends string, U>() => {
delete: HandlerInterface<T, E_1, U>;
get: HandlerInterface<T, E_1, U>;
all: HandlerInterface<T, E_1, U>;
post: HandlerInterface<T, E_1, U>;
put: 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 = Env, P extends string = ''> extends Hono_base<E, P, Hono<E, P>> {
#private;
readonly routerClass: {

@@ -49,5 +72,5 @@ new (): Router<any>;

private errorHandler;
route(path: string): Hono<E, P>;
use(path: string, middleware: MiddlewareHandler<E>): Hono<E, P>;
use(middleware: MiddlewareHandler<E>): Hono<E, P>;
route(path: string, route?: Route): 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>;

@@ -54,0 +77,0 @@ notFound(handler: NotFoundHandler<E>): Hono<E, P>;

@@ -13,11 +13,12 @@ "use strict";

};
var _Hono_router, _Hono_middlewareRouters, _Hono_tempPath;
var _Route_path, _Hono_router, _Hono_tempPath;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Hono = void 0;
exports.Hono = exports.Route = void 0;
const compose_1 = require("./compose");
const context_1 = require("./context");
const router_1 = require("./router");
const router_2 = 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', 'all'];
const methods = ['get', 'post', 'put', 'delete', 'head', 'options', 'patch'];
function defineDynamicClass() {

@@ -27,2 +28,33 @@ return class {

}
class Route extends defineDynamicClass() {
constructor() {
super();
this.routes = [];
_Route_path.set(this, '');
const allMethods = [...methods, router_2.METHOD_NAME_ALL_LOWERCASE];
allMethods.map((method) => {
this[method] = (args1, ...args) => {
if (typeof args1 === 'string') {
__classPrivateFieldSet(this, _Route_path, args1, "f");
}
else {
this.add(method, __classPrivateFieldGet(this, _Route_path, "f"), args1);
}
args.map((handler) => {
if (typeof handler !== 'string') {
this.add(method, __classPrivateFieldGet(this, _Route_path, "f"), handler);
}
});
return this;
};
});
}
add(method, path, handler) {
const r = { path: path, method: method, handler: handler };
this.routes.push(r);
return this;
}
}
exports.Route = Route;
_Route_path = new WeakMap();
class Hono extends defineDynamicClass() {

@@ -34,3 +66,2 @@ constructor(init = {}) {

_Hono_router.set(this, void 0);
_Hono_middlewareRouters.set(this, void 0);
_Hono_tempPath.set(this, void 0);

@@ -46,9 +77,17 @@ this.notFoundHandler = (c) => {

};
methods.map((method) => {
this[method] = (arg1, arg2) => {
if (typeof arg1 === 'string') {
this.path = arg1;
return this.addRoute(method, this.path, arg2);
const allMethods = [...methods, router_2.METHOD_NAME_ALL_LOWERCASE];
allMethods.map((method) => {
this[method] = (args1, ...args) => {
if (typeof args1 === 'string') {
this.path = args1;
}
return this.addRoute(method, this.path, arg1);
else {
this.addRoute(method, this.path, args1);
}
args.map((handler) => {
if (typeof handler !== 'string') {
this.addRoute(method, this.path, handler);
}
});
return this;
};

@@ -58,26 +97,25 @@ });

__classPrivateFieldSet(this, _Hono_router, new this.routerClass(), "f");
__classPrivateFieldSet(this, _Hono_middlewareRouters, [], "f");
__classPrivateFieldSet(this, _Hono_tempPath, null, "f");
}
route(path) {
route(path, route) {
const newHono = new Hono();
__classPrivateFieldSet(newHono, _Hono_tempPath, path, "f");
__classPrivateFieldSet(newHono, _Hono_router, __classPrivateFieldGet(this, _Hono_router, "f"), "f");
if (route) {
route.routes.map((r) => {
newHono.addRoute(r.method, r.path, r.handler);
});
}
return newHono;
}
use(arg1, arg2) {
let handler;
use(arg1, ...handlers) {
if (typeof arg1 === 'string') {
this.path = arg1;
handler = arg2;
}
else {
handler = arg1;
handlers.unshift(arg1);
}
if (handler.constructor.name !== 'AsyncFunction') {
throw new TypeError('middleware must be a async function!');
}
const router = new this.routerClass();
router.add(router_1.METHOD_NAME_OF_ALL, this.path, handler);
__classPrivateFieldGet(this, _Hono_middlewareRouters, "f").push(router);
handlers.map((handler) => {
this.addRoute(router_1.METHOD_NAME_ALL, this.path, handler);
});
return this;

@@ -99,3 +137,2 @@ }

__classPrivateFieldGet(this, _Hono_router, "f").add(method, path, handler);
return this;
}

@@ -109,3 +146,2 @@ async matchRoute(method, path) {

const result = await this.matchRoute(method, path);
// Methods for Request object
request.param = (key) => {

@@ -115,22 +151,17 @@ if (result)

};
const handler = result ? result.handler : this.notFoundHandler;
const middleware = [];
for (const mr of __classPrivateFieldGet(this, _Hono_middlewareRouters, "f")) {
const mwResult = mr.match(router_1.METHOD_NAME_OF_ALL, path);
if (mwResult)
middleware.push(mwResult.handler);
const handlers = result ? result.handlers : [this.notFoundHandler];
const c = new context_1.Context(request, { env: env, event: event, res: undefined });
c.notFound = () => this.notFoundHandler(c);
const composed = (0, compose_1.compose)(handlers, this.errorHandler, this.notFoundHandler);
let context;
try {
context = await composed(c);
}
const wrappedHandler = async (context, next) => {
const res = await handler(context);
if (!(res instanceof Response)) {
throw new TypeError('response must be a instance of Response');
catch (err) {
if (err instanceof Error) {
return this.errorHandler(err, c);
}
context.res = res;
await next();
};
middleware.push(wrappedHandler);
const composed = (0, compose_1.compose)(middleware, this.errorHandler);
const c = new context_1.Context(request, { env: env, event: event, res: null });
c.notFound = () => this.notFoundHandler(c);
const context = await composed(c);
}
if (!context.res)
return context.notFound();
return context.res;

@@ -155,2 +186,2 @@ }

exports.Hono = Hono;
_Hono_router = new WeakMap(), _Hono_middlewareRouters = new WeakMap(), _Hono_tempPath = new WeakMap();
_Hono_router = new WeakMap(), _Hono_tempPath = new WeakMap();

@@ -1,4 +0,4 @@

export { Hono } from './hono';
export type { Handler, MiddlewareHandler, Next } from './hono';
export { Hono, Route } from './hono';
export type { Handler, Next } from './hono';
export { Context } from './context';
export type { Env } from './context';
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Context = exports.Hono = void 0;
exports.Context = exports.Route = exports.Hono = void 0;
var hono_1 = require("./hono");
Object.defineProperty(exports, "Hono", { enumerable: true, get: function () { return hono_1.Hono; } });
Object.defineProperty(exports, "Route", { enumerable: true, get: function () { return hono_1.Route; } });
var context_1 = require("./context");
Object.defineProperty(exports, "Context", { enumerable: true, get: function () { return context_1.Context; } });

@@ -44,15 +44,16 @@ "use strict";

// Authorized OK
return next();
await next();
}
}
}
ctx.res = new Response('Unauthorized', {
status: 401,
headers: {
'WWW-Authenticate': 'Basic realm="' + options.realm.replace(/"/g, '\\"') + '"',
},
});
return;
else {
ctx.res = new Response('Unauthorized', {
status: 401,
headers: {
'WWW-Authenticate': 'Basic realm="' + options.realm.replace(/"/g, '\\"') + '"',
},
});
}
};
};
exports.basicAuth = basicAuth;

@@ -11,2 +11,3 @@ "use strict";

const credentials = ctx.req.headers.get('Authorization');
await next();
if (!credentials) {

@@ -29,3 +30,2 @@ ctx.res = new Response('Unauthorized', {

});
return;
}

@@ -40,15 +40,13 @@ let authorized = false;

}
if (authorized) {
return next();
if (!authorized) {
ctx.res = new Response('Unauthorized', {
status: 401,
statusText: msg,
headers: {
'WWW-Authenticate': 'Bearer ${options.secret}',
},
});
}
ctx.res = new Response('Unauthorized', {
status: 401,
statusText: msg,
headers: {
'WWW-Authenticate': 'Bearer ${options.secret}',
},
});
return;
};
};
exports.jwt = jwt;

@@ -1,2 +0,3 @@

export declare const METHOD_NAME_OF_ALL = "ALL";
export declare const METHOD_NAME_ALL: "ALL";
export declare const METHOD_NAME_ALL_LOWERCASE: "all";
export declare abstract class Router<T> {

@@ -7,5 +8,5 @@ abstract add(method: string, path: string, handler: T): void;

export declare class Result<T> {
handler: T;
handlers: T[];
params: Record<string, string>;
constructor(handler: T, params: Record<string, string>);
constructor(handlers: T[], params: Record<string, string>);
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Result = exports.Router = exports.METHOD_NAME_OF_ALL = void 0;
exports.METHOD_NAME_OF_ALL = 'ALL';
exports.Result = exports.Router = exports.METHOD_NAME_ALL_LOWERCASE = exports.METHOD_NAME_ALL = void 0;
exports.METHOD_NAME_ALL = 'ALL';
exports.METHOD_NAME_ALL_LOWERCASE = 'all';
class Router {

@@ -9,4 +10,4 @@ }

class Result {
constructor(handler, params) {
this.handler = handler;
constructor(handlers, params) {
this.handlers = handlers;
this.params = params;

@@ -13,0 +14,0 @@ }

@@ -9,4 +9,7 @@ export declare type ParamMap = Array<[string, 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;
}

@@ -38,5 +38,9 @@ "use strict";

class Node {
constructor() {
constructor({ reverse } = { reverse: false }) {
this.children = {};
this.reverse = reverse;
}
newChildNode() {
return new Node({ reverse: this.reverse });
}
insert(tokens, index, paramMap, context) {

@@ -62,3 +66,3 @@ var _a;

if (!node) {
node = this.children[regexpStr] = new Node();
node = this.children[regexpStr] = this.newChildNode();
if (name !== '') {

@@ -73,3 +77,3 @@ node.varIndex = context.varIndex++;

else {
node = (_a = this.children)[token] || (_a[token] = new Node());
node = (_a = this.children)[token] || (_a[token] = this.newChildNode());
}

@@ -79,5 +83,7 @@ node.insert(restTokens, index, paramMap, context);

buildRegExpStr() {
const strList = Object.keys(this.children)
.sort(compareKey)
.map((k) => {
let childKeys = Object.keys(this.children).sort(compareKey);
if (this.reverse) {
childKeys = childKeys.reverse();
}
const strList = childKeys.map((k) => {
const c = this.children[k];

@@ -87,3 +93,3 @@ return (typeof c.varIndex === 'number' ? `(${k})@${c.varIndex}` : k) + c.buildRegExpStr();

if (typeof this.index === 'number') {
strList.push(`#${this.index}`);
strList.unshift(`#${this.index}`);
}

@@ -90,0 +96,0 @@ if (strList.length === 0) {

import { Router, Result } from '../../router';
declare type Route<T> = [string, T];
interface Hint {
components: string[];
regExpComponents: Array<true | string>;
componentsLength: number;
endWithWildcard: boolean;
paramIndexList: number[];
maybeHandler: boolean;
namedParams: [number, string, string][];
}
interface Route<T> {
method: string;
path: string;
hint: Hint;
handlers: T[];
middleware: T[];
paramAliasMap: Record<string, string[]>;
}
export declare class RegExpRouter<T> extends Router<T> {
routes?: Record<string, Route<T>[]>;
routeData?: {
routes: Route<T>[];
methods: Set<string>;
};
add(method: string, path: string, handler: T): void;

@@ -6,0 +25,0 @@ match(method: string, path: string): Result<T> | null;

@@ -6,110 +6,330 @@ "use strict";

const trie_1 = require("../../router/reg-exp-router/trie");
const regExpMatchAll = new RegExp('');
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 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,
];
}
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 extends router_1.Router {
constructor() {
super(...arguments);
this.routes = {};
this.routeData = { routes: [], methods: new Set() };
}
add(method, path, handler) {
var _a;
if (!this.routes) {
if (!this.routeData) {
throw new Error('Can not add a route since the matcher is already built.');
}
(_a = this.routes)[method] || (_a[method] = []);
this.routes[method].push([path, handler]);
const { routes, methods } = this.routeData;
if (path === '/*') {
path = '*';
}
for (let i = 0, len = routes.length; i < len; i++) {
if (routes[i].method === method && routes[i].path === path) {
routes[i].handlers.push(handler);
return;
}
}
methods.add(method);
routes.push({
method,
path,
handlers: [handler],
hint: initHint(path),
middleware: [],
paramAliasMap: {},
});
}
match(method, path) {
const matchers = this.buildAllMatchers();
let match;
// Optimization for middleware
const methods = Object.keys(matchers);
if (methods.length === 1 && methods[0] === router_1.METHOD_NAME_OF_ALL) {
const [regexp, handlers] = matchers[router_1.METHOD_NAME_OF_ALL];
if (handlers.length === 1) {
const result = new router_1.Result(handlers[0][0], emptyParam);
if (regexp === regExpMatchAll) {
match = () => result;
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;
}
else if (handlers.length === 1 && !handlers[0][1]) {
match = (_, path) => (regexp.test(path) ? result : 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 new router_1.Result([...handlers.values()], params);
}
}
match || (match = (method, path) => {
const matcher = matchers[method] || matchers[router_1.METHOD_NAME_OF_ALL];
if (!matcher) {
return null;
: (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 [handler, paramMap] = matcher[1][index];
if (!paramMap) {
return new router_1.Result(handler, emptyParam);
}
const params = {};
for (let i = 0, len = paramMap.length; i < len; i++) {
params[paramMap[i][0]] = match[paramMap[i][1]];
}
return new router_1.Result(handler, params);
};
return this.match(method, path);
}
buildAllMatchers() {
this.routeData.routes.sort(({ hint: a }, { hint: b }) => {
if (a.componentsLength !== b.componentsLength) {
return a.componentsLength - b.componentsLength;
}
const match = path.match(matcher[0]);
if (!match) {
return null;
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];
}
}
}
const index = match.indexOf('', 1);
const [handler, paramMap] = matcher[1][index];
if (!paramMap) {
return new router_1.Result(handler, emptyParam);
if (a.endWithWildcard !== b.endWithWildcard) {
return a.endWithWildcard ? -1 : 1;
}
const params = {};
for (let i = 0; i < paramMap.length; i++) {
params[paramMap[i][0]] = match[paramMap[i][1]];
}
return new router_1.Result(handler, params);
return 0;
});
this.match = match;
return this.match(method, path);
}
buildAllMatchers() {
const matchers = {};
Object.keys(this.routes).forEach((method) => {
matchers[method] = this.buildMatcher(method);
const primaryMatchers = {};
const secondaryMatchers = {};
let hasAmbiguous = false;
this.routeData.methods.forEach((method) => {
let _hasAmbiguous;
[primaryMatchers[method], secondaryMatchers[method], _hasAmbiguous] =
this.buildMatcher(method);
hasAmbiguous = hasAmbiguous || _hasAmbiguous;
});
delete this.routes; // to reduce memory usage
return matchers;
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) {
const trie = new trie_1.Trie();
const handlers = [];
const targetMethods = [method];
if (method !== router_1.METHOD_NAME_OF_ALL) {
targetMethods.unshift(router_1.METHOD_NAME_OF_ALL);
var _a, _b;
let hasAmbiguous = false;
const targetMethods = new Set([method, router_1.METHOD_NAME_ALL]);
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 = {};
}
const routes = targetMethods.flatMap((method) => this.routes[method] || []);
if (routes.length === 0) {
return null;
}
if (method === router_1.METHOD_NAME_OF_ALL) {
if (routes.length === 1 && routes[0][0] === '*') {
return [regExpMatchAll, [[routes[0][1], null]]];
// 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('');
}
}
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 (routes.length === 1 && !routes[0][0].match(/:/)) {
// there is only one route and no capture
const tmp = routes[0][0].endsWith('*')
? routes[0][0].replace(/\/\*$/, '(?:$|/)') // /path/to/* => /path/to(?:$|/)
: `${routes[0][0]}$`; // /path/to/action => /path/to/action$
const regExpStr = `^${tmp.replace(/\*/g, '[^/]+')}`; // /prefix/*/path/to => /prefix/[^/]+/path/to
return [new RegExp(regExpStr), [[routes[0][1], null]]];
if (!verifyDuplicateParam([routes[i]])) {
throw new Error('Duplicate param name');
}
}
for (let i = 0; i < routes.length; i++) {
const paramMap = trie.insert(routes[i][0], i);
handlers[i] = [routes[i][1], Object.keys(paramMap).length !== 0 ? paramMap : null];
if (hasAmbiguous) {
return [buildMatcherFromPreprocessedRoutes(routes, hasAmbiguous), [], hasAmbiguous];
}
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
for (let i = 0; i < handlers.length; i++) {
const paramMap = handlers[i][1];
if (paramMap) {
for (let i = 0; i < paramMap.length; i++) {
paramMap[i][1] = paramReplacementMap[paramMap[i][1]];
}
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]);
}
}
const handlerMap = [];
// using `in` because indexReplacementMap is a sparse array
for (const i in indexReplacementMap) {
handlerMap[i] = handlers[indexReplacementMap[i]];
}
return [regexp, handlerMap];
return [
buildMatcherFromPreprocessedRoutes(primaryRoutes, hasAmbiguous),
[buildMatcherFromPreprocessedRoutes(secondaryRoutes, hasAmbiguous)],
hasAmbiguous,
];
}
}
exports.RegExpRouter = RegExpRouter;

@@ -5,7 +5,11 @@ import type { ParamMap, Context } from '../../router/reg-exp-router/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];
}

@@ -6,5 +6,5 @@ "use strict";

class Trie {
constructor() {
constructor({ reverse } = { reverse: false }) {
this.context = { varIndex: 0 };
this.root = new node_1.Node();
this.root = new node_1.Node({ reverse });
}

@@ -11,0 +11,0 @@ insert(path, index) {

import { Result } from '../../router';
import type { Pattern } from '../../utils/url';
export declare class Node<T> {
method: Record<string, T>;
handler: T;
methods: Record<string, T>[];
handlers: T[];
children: Record<string, Node<T>>;
middlewares: [];
patterns: Pattern[];
constructor(method?: string, handler?: T, children?: Record<string, Node<T>>);
insert(method: string, path: string, handler: T): Node<T>;
private getHandlers;
private next;
search(method: string, path: string): Result<T>;
}

@@ -6,13 +6,25 @@ "use strict";

const url_1 = require("../../utils/url");
const noRoute = () => {
return null;
};
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.children = children || {};
this.method = {};
this.methods = [];
if (method && handler) {
this.method[method] = handler;
const m = {};
m[method] = handler;
this.methods = [m];
}
this.middlewares = [];
this.patterns = [];

@@ -24,5 +36,10 @@ }

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];

@@ -34,68 +51,119 @@ continue;

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];
}
curNode.method[method] = handler;
if (!curNode.methods.length) {
curNode.methods = [];
}
const m = {};
m[method] = handler;
curNode.methods.push(m);
return curNode;
}
getHandlers(node, method) {
const handlers = [];
node.methods.map((m) => {
let handler = m[method];
if (handler !== undefined) {
handlers.push(handler);
return;
}
handler = m[router_1.METHOD_NAME_ALL];
if (handler !== undefined) {
handlers.push(handler);
return;
}
});
return handlers;
}
next(node, part, method, isLast) {
const handlers = [];
const nextNodes = [];
const params = {};
for (let j = 0, len = node.patterns.length; j < len; j++) {
const pattern = node.patterns[j];
// Wildcard
// '/hello/*/foo' => match /hello/bar/foo
if (pattern === '*') {
const astNode = node.children['*'];
if (astNode) {
handlers.push(...this.getHandlers(astNode, method));
nextNodes.push(astNode);
}
}
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) {
handlers.push(...this.getHandlers(node.children[key], method));
}
nextNodes.push(node.children[key]);
}
if (typeof name === 'string') {
params[name] = part;
}
}
}
const nextNode = node.children[part];
if (nextNode) {
if (isLast === true) {
// '/hello/*' => match '/hello'
if (nextNode.children['*'] !== undefined) {
handlers.push(...this.getHandlers(nextNode.children['*'], method));
}
handlers.push(...this.getHandlers(nextNode, method));
}
nextNodes.push(nextNode);
}
const next = {
nodes: nextNodes,
handlers: handlers,
params: params,
};
return next;
}
search(method, path) {
const handlers = [];
let params = {};
// eslint-disable-next-line @typescript-eslint/no-this-alias
let curNode = this;
const params = {};
const curNode = this;
let curNodes = [curNode];
const parts = (0, url_1.splitPath)(path);
for (let i = 0, len = parts.length; i < len; i++) {
const p = parts[i];
// '*' => match any path
// /api/* => default wildcard match
if (curNode.children['*'] && !curNode.children[p]) {
const astNode = curNode.children['*'];
if (Object.keys(astNode.children).length === 0) {
curNode = astNode;
break;
}
}
const nextNode = curNode.children[p];
if (nextNode) {
curNode = nextNode;
// '/hello/*' => match '/hello'
if (!(i == len - 1 && nextNode.children['*'])) {
const isLast = i === len - 1;
const tempNodes = [];
for (let j = 0, len2 = curNodes.length; j < len2; j++) {
const res = this.next(curNodes[j], p, method, isLast);
if (res.nodes.length === 0) {
continue;
}
handlers.push(...res.handlers);
params = Object.assign(params, res.params);
tempNodes.push(...res.nodes);
}
let isWildcard = false;
let isParamMatch = false;
for (let j = 0, len = curNode.patterns.length; j < len; j++) {
const pattern = curNode.patterns[j];
// Wildcard
// '/hello/*/foo' => match /hello/bar/foo
if (pattern === '*') {
curNode = curNode.children['*'];
isWildcard = true;
break;
}
// Named match
const [key, name, matcher] = pattern;
if (p !== '' && (matcher === true || matcher.test(p))) {
params[name] = p;
curNode = curNode.children[key];
isParamMatch = true;
break;
}
return noRoute();
}
if (isWildcard && i === len - 1) {
break;
}
if (isWildcard === false && isParamMatch === false) {
return noRoute();
}
curNodes = tempNodes;
}
const handler = curNode.method[router_1.METHOD_NAME_OF_ALL] || curNode.method[method];
if (!handler) {
return noRoute();
}
return new router_1.Result(handler, params);
if (handlers.length <= 0)
return null;
return new router_1.Result(handlers, params);
}
}
exports.Node = Node;

@@ -8,3 +8,3 @@ import { Router } from '../../router';

add(method: string, path: string, handler: T): void;
match(method: string, path: string): Result<T> | null;
match(method: string, path: string): Result<T>;
}

@@ -10,6 +10,6 @@ "use strict";

else if (contentType.includes('application/text')) {
return r.text();
return await r.text();
}
else if (contentType.startsWith('text')) {
return r.text();
return await r.text();
}

@@ -16,0 +16,0 @@ else if (contentType.includes('form')) {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergePath = exports.isAbsoluteURL = exports.getPathFromURL = exports.getPattern = exports.splitPath = void 0;
const URL_REGEXP = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
const URL_REGEXP = /^https?:\/\/[a-zA-Z0-9\-\.:]+(\/?[^?#]*)/;
const splitPath = (path) => {

@@ -40,3 +40,3 @@ const paths = path.split(/\//); // faster than path.split('/')

// default is true
if (!params.strict && url.endsWith('/')) {
if (params.strict === false && url.endsWith('/')) {
url = url.slice(0, -1);

@@ -46,3 +46,3 @@ }

if (match) {
return match[5];
return match[1];
}

@@ -54,3 +54,3 @@ return '';

const match = url.match(URL_REGEXP);
if (match && match[1]) {
if (match) {
return true;

@@ -57,0 +57,0 @@ }

{
"name": "hono",
"version": "1.1.1",
"version": "1.2.0",
"description": "Ultrafast web framework for Cloudflare Workers.",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -9,8 +9,2 @@ <div align="center">

<p>
<a href="https://github.com/honojs/hono/blob/master/README.md">English</a>
&#x000B7;
<a href="https://github.com/honojs/hono/blob/master/docs/README.ja.md">日本語</a>
</p>
[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/honojs/hono/ci)](https://github.com/honojs/hono/actions)

@@ -24,5 +18,5 @@ [![GitHub](https://img.shields.io/github/license/honojs/hono)](https://github.com/honojs/hono/blob/master/LICENSE)

Hono - _**[炎] means flame🔥 in Japanese**_ - is a small, simple, and ultrafast web framework for Cloudflare Workers and Service Worker based serverless such as Fastly Compute@Edge.
Hono - _**[炎] means flame🔥 in Japanese**_ - is a small, simple, and ultrafast web framework for Cloudflare Workers or Service Worker based serverless such as Fastly Compute@Edge.
```js
```ts
import { Hono } from 'hono'

@@ -39,6 +33,6 @@ const app = new Hono()

- **Ultrafast** - the router does not use linear loops.
- **Zero-dependencies** - using only Service Worker and Web standard API.
- **Zero-dependencies** - using only Service Worker and Web Standard API.
- **Middleware** - built-in middleware and ability to extend with your own middleware.
- **TypeScript** - first-class TypeScript support.
- **Optimized** - for Cloudflare Workers and Fastly Compute@Edge.
- **Optimized** - for Cloudflare Workers.

@@ -50,10 +44,18 @@ ## Benchmark

```plain
hono x 809,503 ops/sec ±6.94% (73 runs sampled)
itty-router x 157,310 ops/sec ±4.31% (87 runs sampled)
sunder x 328,350 ops/sec ±2.30% (95 runs sampled)
worktop x 209,758 ops/sec ±4.28% (83 runs sampled)
Fastest is hono
✨ Done in 60.66s.
hono - trie-router(default) x 737,602 ops/sec ±3.65% (67 runs sampled)
hono - regexp-router x 1,188,203 ops/sec ±6.42% (60 runs sampled)
itty-router x 163,970 ops/sec ±3.05% (91 runs sampled)
sunder x 344,468 ops/sec ±0.87% (97 runs sampled)
worktop x 222,044 ops/sec ±2.13% (85 runs sampled)
Fastest is hono - regexp-router
✨ Done in 84.04s.
```
## Why so fast?
Routers used in Hono are really smart.
- **TrieRouter**(default) - Implemented with Trie tree structure.
- **RegExpRouter** - Match routes with one big Regex made before dispatching at once.
## Hono in 1 minute

@@ -84,16 +86,43 @@

You can enable logger and CORS middleware with just this code.
To enable logger and Etag middleware with just this code.
```js
```ts
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { etag } from 'hono/etag'
import { logger } from 'hono/logger'
const app = new Hono()
app.use('*', cors()).use(logger())
app.use('*', etag(), (logger())
```
And, the routing of Hono is so flexible. It's easy to construct large web applications.
```ts
import { Hono, Route } from 'hono'
import { cors } from 'hono/cors'
const app = new Hono()
const v1 = new Route()
v1.get('/posts', (c) => {
return c.text('list pots')
})
.post('/posts', cors(), (c) => {
return c.text('created!', 201)
})
.get('/posts/:id', (c) => {
const id = c.req.param('id')
return c.text(`your id is ${id}`)
})
app.route('/v1', v1)
```
### Web Standard
Request and Response object used in Hono are extensions of the Web Standard [Fetch API](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API). If you are familiar with that, you don't need to know more than that.
### Developer Experience
And Hono provides fine _"**Developer Experience**"_. Easy access to Request/Response thanks to the `Context` object.
Hono provides fine _"**Developer Experience**"_. Easy access to Request/Response thanks to the `Context` object.
Above all, Hono is written in TypeScript. So, Hono has _"**Types**"_!

@@ -110,8 +139,2 @@

```sh
yarn add hono
```
or
```sh
npm install hono

@@ -124,5 +147,5 @@ ```

- app.**HTTP_METHOD**(\[path,\] handler)
- app.**all**(\[path,\] handler)
- app.**route**(path)
- app.**HTTP_METHOD**(\[path,\] handler|middleware...)
- app.**all**(\[path,\] handler|middleware...)
- app.**route**(path, \[Route\])
- app.**use**(\[path,\] middleware)

@@ -133,3 +156,3 @@ - app.**notFound**(handler)

- app.**fetch**(request, env, event)
- app.**request**(path, option)
- app.**request**(path, options)

@@ -140,3 +163,3 @@ ## Routing

```js
```ts
// HTTP Methods

@@ -157,3 +180,3 @@ app.get('/', (c) => c.text('GET /'))

```js
```ts
app.get('/user/:name', (c) => {

@@ -167,3 +190,3 @@ const name = c.req.param('name')

```js
```ts
app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {

@@ -178,3 +201,3 @@ const date = c.req.param('date')

```js
```ts
app

@@ -192,15 +215,2 @@ .get('/endpoint', (c) => {

### Nested route
```js
const book = app.route('/book')
book.get('/', (c) => c.text('List Books')) // GET /book
book.get('/:id', (c) => {
// GET /book/:id
const id = c.req.param('id')
return c.text('Get Book: ' + id)
})
book.post('/', (c) => c.text('Create Book')) // POST /book
```
### no strict

@@ -210,3 +220,3 @@

```js
```ts
const app = new Hono({ strict: false }) // Default is true

@@ -226,4 +236,29 @@

## Route
`Route` object enables Nested route.
```ts
const book = new Route()
book.get('/', (c) => c.text('List Books')) // GET /book
book.get('/:id', (c) => {
// GET /book/:id
const id = c.req.param('id')
return c.text('Get Book: ' + id)
})
book.post('/', (c) => c.text('Create Book')) // POST /book
app.route('/book', book)
```
## Middleware
Middleware operate after/before executing Handler. We can get `Response` before dispatching or manipulate `Response` after dispatching.
### Definition of Middleware
- Handler - should return `Response` object.
- Middleware - should return nothing, do `await next()`
### Built-in Middleware

@@ -233,3 +268,3 @@

```js
```ts
import { Hono } from 'hono'

@@ -244,4 +279,2 @@ import { poweredBy } from 'hono/powered-by'

app.use('*', logger())
// Or you can write:
// app.use('*', poweredBy()).use(logger())

@@ -263,3 +296,3 @@ app.use(

```js
```ts
// Custom logger

@@ -303,7 +336,7 @@ app.use('*', async (c, next) => {

To handle Request and Reponse, you can use `Context` object.
To handle Request and Response, you can use `Context` object.
### c.req
```js
```ts
// Get Request object

@@ -336,3 +369,3 @@ app.get('/hello', (c) => {

```js
```ts
app.get('/welcome', (c) => {

@@ -342,4 +375,6 @@ // Set headers

c.header('Content-Type', 'text/plain')
// Set HTTP status code
c.status(201)
// Return the response body

@@ -352,3 +387,3 @@ return c.body('Thank you for comming')

```js
```ts
new Response('Thank you for comming', {

@@ -360,3 +395,2 @@ status: 201,

'Content-Type': 'text/plain',
'Content-Length': '22',
},

@@ -370,3 +404,3 @@ })

```js
```ts
app.get('/say', (c) => {

@@ -381,3 +415,3 @@ return c.text('Hello!')

```js
```ts
app.get('/api', (c) => {

@@ -392,3 +426,3 @@ return c.json({ message: 'Hello!' })

```js
```ts
app.get('/', (c) => {

@@ -403,3 +437,3 @@ return c.html('<h1>Hello! Hono!</h1>')

```js
```ts
app.get('/notfound', (c) => {

@@ -414,3 +448,3 @@ return c.notFound()

```js
```ts
app.get('/redirect', (c) => c.redirect('/'))

@@ -422,3 +456,3 @@ app.get('/redirect-permanently', (c) => c.redirect('/', 301))

```js
```ts
// Response object

@@ -433,3 +467,3 @@ app.use('/', (c, next) => {

```js
```ts
// FetchEvent object

@@ -446,3 +480,3 @@ app.use('*', async (c, next) => {

```js
```ts
// Environment object for Cloudflare Workers

@@ -459,3 +493,3 @@ app.get('*', async c => {

```js
```ts
addEventListener('fetch', (event) => {

@@ -470,3 +504,3 @@ event.respondWith(this.handleEvent(event))

```js
```ts
export default {

@@ -477,7 +511,8 @@ fetch(request: Request, env: Env, event: FetchEvent) {

}
```
/*
or just do:
```ts
export default app
*/
```

@@ -498,57 +533,31 @@

Using [Wrangler](https://developers.cloudflare.com/workers/cli-wrangler/) or [Miniflare](https://miniflare.dev), you can develop the application locally and publish it with few commands.
Using [Wrangler](https://developers.cloudflare.com/workers/cli-wrangler/), you can develop the application locally and publish it with few commands.
Let's write your first code for Cloudflare Workers with Hono.
---
### 1. `wrangler init`
### Caution
Initialize as a wrangler project.
**Wrangler 1.x** does not support importing middleware. We recommend two ways:
1. Use [Wragler 2.0 Beta](https://github.com/cloudflare/wrangler2).
2. Build without webpack 4.x. For example, you can use esbuild. See [the starter template](https://github.com/honojs/hono-minimal).
---
### 1. `npm init`
Make a npm skeleton directory.
```sh
```
mkdir hono-example
cd hono-example
npm init -y
npx wrangler init -y
```
### 2. `wrangler init`
### 2. `npm install hono`
Initialize as a wrangler project.
Install `hono` from the npm registry.
```sh
npx wrangler@beta init
```
Answer the questions. If you want, you can answer `y`.
```
Would you like to install wrangler into your package.json? (y/n) <--- n
Would you like to use TypeScript? (y/n) <--- n
Would you like to create a Worker at src/index.js? (y/n) <--- n
```
### 3. `npm install hono`
Install `hono` from the npm registry.
```sh
npm init -y
npm i hono
```
### 4. Write your app
### 3. Write your app
Only 4 lines!!
Edit `src/index.ts`. Only 4 lines!!
```js
// index.js
```ts
// src/index.ts
import { Hono } from 'hono'

@@ -562,17 +571,17 @@ const app = new Hono()

### 5. Run
### 4. Run
Run the development server locally. Then, access `http://127.0.0.1:8787/` in your Web browser.
```sh
npx wrangler@beta dev index.js
```
npx wrangler dev
```
### 6. Publish
### 5. Publish
Deploy to Cloudflare. That's all!
```sh
npx wrangler@beta publish index.js
```
npx wrangler publish index.ts
```

@@ -585,5 +594,5 @@ ## Starter template

```sh
wrangler generate my-app https://github.com/honojs/hono-minimal
```
npx create-cloudflare my-app https://github.com/honojs/hono-minimal
```

@@ -618,3 +627,3 @@ ## Examples

Thanks to [all contributors](https://github.com/honojs/hono/graphs/contributors)!
Thanks to [all contributors](https://github.com/honojs/hono/graphs/contributors)! Especially, [@metrue](https://github.com/metrue) and [@usualoma](https://github.com/usualoma)!

@@ -621,0 +630,0 @@ ## Author

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc