diff --git a/src/context.ts b/src/context.ts index 81e39207..fe615750 100644 --- a/src/context.ts +++ b/src/context.ts @@ -25,7 +25,7 @@ export class Context { req: Request, opts?: { res: Response; env: Env; event: FetchEvent } ) { - this.req = req + this.req = this.initRequest(req) if (opts) { this.res = opts.res this.env = opts.env @@ -34,14 +34,18 @@ export class Context { this._headers = {} } + private initRequest(req: Request): Request { + req.header = (name: string): string => { + return req.headers.get(name) + } + req.query = (key: string): string => { + const url = new URL(req.url) + return url.searchParams.get(key) + } + return req + } + header(name: string, value: string): void { - /* - XXX: - app.use('*', (c, next) => { - next() - c.header('foo', 'bar') // => c.res.headers.set(...) - }) - */ if (this.res) { this.res.headers.set(name, value) } @@ -101,7 +105,7 @@ export class Context { json(object: object, status: StatusCode = this._status, headers: Headers = {}): Response { if (typeof object !== 'object') { - throw new TypeError('json method arg must be a object!') + throw new TypeError('json method arg must be an object!') } const body = this._pretty ? JSON.stringify(object, null, this._prettySpace) diff --git a/src/hono.ts b/src/hono.ts index e7dafa82..fdb05510 100644 --- a/src/hono.ts +++ b/src/hono.ts @@ -11,7 +11,6 @@ declare global { param: (key: ParamKeyType) => string query: (key: string) => string header: (name: string) => string - // TODO: do not use `any` parsedBody: any } } @@ -64,11 +63,11 @@ export class Hono extends defineDynamicClass( 'patch', 'all' ) { - routerClass: { new (): Router } = TrieRouter - strict: boolean = true // strict routing - default is true - router: Router - middlewareRouters: Router[] - tempPath: string + readonly routerClass: { new (): Router } = TrieRouter + readonly strict: boolean = true // strict routing - default is true + private router: Router + private middlewareRouters: Router[] + private tempPath: string constructor(init: Partial> = {}) { super() @@ -137,7 +136,7 @@ export class Hono extends defineDynamicClass( return this.router.match(method, path) } - async dispatch(request: Request, env?: Env, event?: FetchEvent): Promise { + private async dispatch(request: Request, env?: Env, event?: FetchEvent): Promise { const path = getPathFromURL(request.url, { strict: this.strict }) const method = request.method @@ -147,13 +146,6 @@ export class Hono extends defineDynamicClass( request.param = (key: string): string => { if (result) return result.params[key] } - request.header = (name: string): string => { - return request.headers.get(name) - } - request.query = (key: string): string => { - const url = new URL(c.req.url) - return url.searchParams.get(key) - } const handler = result ? result.handler : this.notFoundHandler @@ -192,12 +184,12 @@ export class Hono extends defineDynamicClass( return this.dispatch(request, env, event) } - request(input: RequestInfo, requestInit?: RequestInit) { - const req = new Request(input, requestInit) + request(input: RequestInfo, requestInit?: RequestInit): Promise { + const req = input instanceof Request ? input : new Request(input, requestInit) return this.dispatch(req) } - fire() { + fire(): void { addEventListener('fetch', (event: FetchEvent): void => { event.respondWith(this.handleEvent(event)) }) diff --git a/src/middleware/basic-auth/index.test.ts b/src/middleware/basic-auth/index.test.ts index c68520af..f58a0165 100644 --- a/src/middleware/basic-auth/index.test.ts +++ b/src/middleware/basic-auth/index.test.ts @@ -67,42 +67,42 @@ describe('Basic Auth by Middleware', () => { app.get('/auth-multi/*', () => new Response('auth')) app.get('/auth-override-func/*', () => new Response('auth')) - it('Unauthorized', async () => { + it('Should not authorize', async () => { const req = new Request('http://localhost/auth/a') - const res = await app.dispatch(req) + const res = await app.request(req) expect(res).not.toBeNull() expect(res.status).toBe(401) expect(await res.text()).toBe('Unauthorized') }) - it('Authorizated', async () => { + it('Should authorize', async () => { const credential = Buffer.from(username + ':' + password).toString('base64') const req = new Request('http://localhost/auth/a') req.headers.set('Authorization', `Basic ${credential}`) - const res = await app.dispatch(req) + const res = await app.request(req) expect(res).not.toBeNull() expect(res.status).toBe(200) expect(await res.text()).toBe('auth') }) - it('Authorizated Unicode', async () => { + it('Should authorize Unicode', async () => { const credential = Buffer.from(username + ':' + unicodePassword).toString('base64') const req = new Request('http://localhost/auth-unicode/a') req.headers.set('Authorization', `Basic ${credential}`) - const res = await app.dispatch(req) + const res = await app.request(req) expect(res).not.toBeNull() expect(res.status).toBe(200) expect(await res.text()).toBe('auth') }) - it('Authorizated multiple users', async () => { + it('Should authorize multiple users', async () => { let credential = Buffer.from(usernameB + ':' + passwordB).toString('base64') let req = new Request('http://localhost/auth-multi/b') req.headers.set('Authorization', `Basic ${credential}`) - let res = await app.dispatch(req) + let res = await app.request(req) expect(res).not.toBeNull() expect(res.status).toBe(200) expect(await res.text()).toBe('auth') @@ -110,18 +110,18 @@ describe('Basic Auth by Middleware', () => { credential = Buffer.from(usernameC + ':' + passwordC).toString('base64') req = new Request('http://localhost/auth-multi/c') req.headers.set('Authorization', `Basic ${credential}`) - res = await app.dispatch(req) + res = await app.request(req) expect(res).not.toBeNull() expect(res.status).toBe(200) expect(await res.text()).toBe('auth') }) - it('should authorize with sha256 function override', async () => { + it('Should authorize with sha256 function override', async () => { const credential = Buffer.from(username + ':' + password).toString('base64') const req = new Request('http://localhost/auth-override-func/a') req.headers.set('Authorization', `Basic ${credential}`) - const res = await app.dispatch(req) + const res = await app.request(req) expect(res).not.toBeNull() expect(res.status).toBe(200) expect(await res.text()).toBe('auth') diff --git a/src/middleware/body-parse/index.test.ts b/src/middleware/body-parse/index.test.ts index cbd4b2ce..267fff24 100644 --- a/src/middleware/body-parse/index.test.ts +++ b/src/middleware/body-parse/index.test.ts @@ -22,7 +22,7 @@ describe('Parse Body Middleware', () => { body: JSON.stringify(payload), headers: new Headers({ 'Content-Type': 'application/json' }), }) - const res = await app.dispatch(req) + const res = await app.request(req) expect(res).not.toBeNull() expect(res.status).toBe(200) expect(req.parsedBody).toEqual(payload) @@ -36,7 +36,7 @@ describe('Parse Body Middleware', () => { body: 'hello', headers: new Headers({ 'Content-Type': 'application/text' }), }) - const res = await app.dispatch(req) + const res = await app.request(req) expect(res).not.toBeNull() expect(res.status).toBe(200) expect(req.parsedBody).toEqual(payload) @@ -53,7 +53,7 @@ describe('Parse Body Middleware', () => { 'Content-Type': 'application/x-www-form-urlencoded', }, }) - const res = await app.dispatch(req) + const res = await app.request(req) expect(res).not.toBeNull() expect(res.status).toBe(200) expect(req.parsedBody).toEqual({ message: 'hello' }) diff --git a/src/middleware/cookie/index.test.ts b/src/middleware/cookie/index.test.ts index 2748599a..e842692f 100644 --- a/src/middleware/cookie/index.test.ts +++ b/src/middleware/cookie/index.test.ts @@ -18,7 +18,7 @@ describe('Cookie Middleware', () => { const req = new Request('http://localhost/cookie') const cookieString = 'yummy_cookie=choco; tasty_cookie = strawberry ' req.headers.set('Cookie', cookieString) - const res = await app.dispatch(req) + const res = await app.request(req) expect(res.headers.get('Yummy-Cookie')).toBe('choco') expect(res.headers.get('Tasty-Cookie')).toBe('strawberry') @@ -32,8 +32,7 @@ describe('Cookie Middleware', () => { }) it('Set cookie on c.cookie', async () => { - const req = new Request('http://localhost/set-cookie') - const res = await app.dispatch(req) + const res = await app.request('http://localhost/set-cookie') expect(res.status).toBe(200) const header = res.headers.get('Set-Cookie') expect(header).toBe('delicious_cookie=macha') @@ -55,8 +54,7 @@ describe('Cookie Middleware', () => { }) it('Complex pattern', async () => { - const req = new Request('http://localhost/set-cookie-complex') - const res = await app.dispatch(req) + const res = await app.request('http://localhost/set-cookie-complex') expect(res.status).toBe(200) const header = res.headers.get('Set-Cookie') expect(header).toBe( diff --git a/src/middleware/cors/index.test.ts b/src/middleware/cors/index.test.ts index 50e4695e..e21ceed7 100644 --- a/src/middleware/cors/index.test.ts +++ b/src/middleware/cors/index.test.ts @@ -25,8 +25,7 @@ describe('CORS by Middleware', () => { }) it('GET default', async () => { - const req = new Request('http://localhost/api/abc') - const res = await app.dispatch(req) + const res = await app.request('http://localhost/api/abc') expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*') expect(res.headers.get('Vary')).toBeNull() @@ -35,7 +34,7 @@ describe('CORS by Middleware', () => { it('Preflight default', async () => { const req = new Request('https://localhost/api/abc', { method: 'OPTIONS' }) req.headers.append('Access-Control-Request-Headers', 'X-PINGOTHER, Content-Type') - const res = await app.dispatch(req) + const res = await app.request(req) expect(res.status).toBe(204) expect(res.headers.get('Access-Control-Allow-Methods').split(',')[0]).toBe('GET') @@ -47,7 +46,7 @@ describe('CORS by Middleware', () => { it('Preflight with options', async () => { const req = new Request('https://localhost/api2/abc', { method: 'OPTIONS' }) - const res = await app.dispatch(req) + const res = await app.request(req) expect(res.headers.get('Access-Control-Allow-Origin')).toBe('http://example.com') expect(res.headers.get('Vary').split(/\s*,\s*/)).toEqual(expect.arrayContaining(['Origin'])) diff --git a/src/middleware/etag/index.test.ts b/src/middleware/etag/index.test.ts index b4921a89..45b302a4 100644 --- a/src/middleware/etag/index.test.ts +++ b/src/middleware/etag/index.test.ts @@ -17,43 +17,34 @@ describe('Etag Middleware', () => { return c.text('Hono is cool') }) - it('should return etag header', async () => { - let req = new Request('http://localhost/etag/abc') - let res = await app.dispatch(req) - + it('Should return etag header', async () => { + let res = await app.request('http://localhost/etag/abc') expect(res.headers.get('ETag')).not.toBeFalsy() expect(res.headers.get('ETag')).toBe('"4e32298b1cb4edc595237405e5b696e105c2399a"') - req = new Request('http://localhost/etag/def') - res = await app.dispatch(req) - + res = await app.request('http://localhost/etag/def') expect(res.headers.get('ETag')).not.toBeFalsy() expect(res.headers.get('ETag')).toBe('"c1d44ff03aff1372856c281854f454e2e1d15b7c"') }) - it('should return etag header - weak', async () => { - const req = new Request('http://localhost/etag-weak/abc') - const res = await app.dispatch(req) - + it('Should return etag header - weak', async () => { + const res = await app.request('http://localhost/etag-weak/abc') expect(res.headers.get('ETag')).not.toBeFalsy() expect(res.headers.get('ETag')).toBe('W/"4e32298b1cb4edc595237405e5b696e105c2399a"') }) - it('should return 304 response', async () => { - let req = new Request('http://localhost/etag/abc') - let res = await app.dispatch(req) - + it('Should return 304 response', async () => { + let res = await app.request('http://localhost/etag/abc') expect(res.status).toBe(200) expect(res.headers.get('ETag')).not.toBeFalsy() const etag = res.headers.get('Etag') - req = new Request('http://localhost/etag/abc', { + const req = new Request('http://localhost/etag/abc', { headers: { 'If-None-Match': etag, }, }) - res = await app.dispatch(req) - + res = await app.request(req) expect(res.status).toBe(304) expect(await res.text()).toBe('') }) diff --git a/src/middleware/logger/index.test.ts b/src/middleware/logger/index.test.ts index 6dab1198..d13521cb 100644 --- a/src/middleware/logger/index.test.ts +++ b/src/middleware/logger/index.test.ts @@ -18,8 +18,7 @@ describe('Logger by Middleware', () => { app.get('/empty', (c) => c.text('')) it('Log status 200 with empty body', async () => { - const req = new Request('http://localhost/empty') - const res = await app.dispatch(req) + const res = await app.request('http://localhost/empty') expect(res).not.toBeNull() expect(res.status).toBe(200) expect(log.startsWith(' --> GET /empty \x1b[32m200\x1b[0m')).toBe(true) @@ -27,8 +26,7 @@ describe('Logger by Middleware', () => { }) it('Log status 200 with small body', async () => { - const req = new Request('http://localhost/short') - const res = await app.dispatch(req) + const res = await app.request('http://localhost/short') expect(res).not.toBeNull() expect(res.status).toBe(200) expect(log.startsWith(' --> GET /short \x1b[32m200\x1b[0m')).toBe(true) @@ -36,8 +34,7 @@ describe('Logger by Middleware', () => { }) it('Log status 200 with big body', async () => { - const req = new Request('http://localhost/long') - const res = await app.dispatch(req) + const res = await app.request('http://localhost/long') expect(res).not.toBeNull() expect(res.status).toBe(200) expect(log.startsWith(' --> GET /long \x1b[32m200\x1b[0m')).toBe(true) @@ -49,9 +46,7 @@ describe('Logger by Middleware', () => { app.all('*', (c) => { return c.text(msg, 404) }) - - const req = new Request('http://localhost/notfound') - const res = await app.dispatch(req) + const res = await app.request('http://localhost/notfound') expect(res).not.toBeNull() expect(res.status).toBe(404) expect(log.startsWith(' --> GET /notfound \x1b[33m404\x1b[0m')).toBe(true) diff --git a/src/middleware/mustache/index.test.ts b/src/middleware/mustache/index.test.ts index 8e8f4b28..790d50d3 100644 --- a/src/middleware/mustache/index.test.ts +++ b/src/middleware/mustache/index.test.ts @@ -45,15 +45,13 @@ describe('Mustache by Middleware', () => { }) it('Mustache template rendering', async () => { - const req = new Request('http://localhost/foo') - const res = await app.dispatch(req) + const res = await app.request('http://localhost/foo') expect(res.status).toBe(200) expect(await res.text()).toBe('Title: Hono!') }) it('Mustache template rendering with root', async () => { - const req = new Request('http://localhost/bar') - const res = await app.dispatch(req) + const res = await app.request('http://localhost/bar') expect(res.status).toBe(200) expect(await res.text()).toBe('

With Root

') }) diff --git a/src/middleware/powered-by/index.test.ts b/src/middleware/powered-by/index.test.ts index 29529ae6..8be7874f 100644 --- a/src/middleware/powered-by/index.test.ts +++ b/src/middleware/powered-by/index.test.ts @@ -5,11 +5,10 @@ describe('Powered by Middleware', () => { const app = new Hono() app.use('*', poweredBy()) - app.get('/', () => new Response('root')) + app.get('/', (c) => c.text('root')) - it('Response headers include X-Powered-By', async () => { - const req = new Request('http://localhost/') - const res = await app.dispatch(req) + it('Should return with X-Powered-By header', async () => { + const res = await app.request('http://localhost/') expect(res).not.toBeNull() expect(res.status).toBe(200) expect(res.headers.get('X-Powered-By')).toBe('Hono') diff --git a/src/middleware/pretty-json/index.test.ts b/src/middleware/pretty-json/index.test.ts index 1f279a91..42a2f4cb 100644 --- a/src/middleware/pretty-json/index.test.ts +++ b/src/middleware/pretty-json/index.test.ts @@ -10,8 +10,7 @@ describe('JSON pretty by Middleware', () => { it('Should return pretty JSON output', async () => { app.use('*', prettyJSON()) - const req = new Request('http://localhost/?pretty') - const res = await app.dispatch(req) + const res = await app.request('http://localhost/?pretty') expect(res).not.toBeNull() expect(res.status).toBe(200) expect(await res.text()).toBe(`{ @@ -21,8 +20,7 @@ describe('JSON pretty by Middleware', () => { it('Should return pretty JSON output with 4 spaces', async () => { app.use('*', prettyJSON({ space: 4 })) - const req = new Request('http://localhost/?pretty') - const res = await app.dispatch(req) + const res = await app.request('http://localhost/?pretty') expect(res).not.toBeNull() expect(res.status).toBe(200) expect(await res.text()).toBe(`{ diff --git a/src/middleware/serve-static/index.test.ts b/src/middleware/serve-static/index.test.ts index 6ad3c441..f0240eef 100644 --- a/src/middleware/serve-static/index.test.ts +++ b/src/middleware/serve-static/index.test.ts @@ -26,40 +26,40 @@ Object.assign(global, { describe('ServeStatic Middleware', () => { const app = new Hono() - app.use('/static/*', serveStatic({ root: './assets' })) app.use('/static-no-root/*', serveStatic()) - it('Serve static files', async () => { - let req = new Request('http://localhost/static/plain.txt') - let res = await app.dispatch(req) + it('Should return plain.txt', async () => { + const res = await app.request('http://localhost/static/plain.txt') expect(res.status).toBe(200) expect(await res.text()).toBe('This is plain.txt') expect(res.headers.get('Content-Type')).toBe('text/plain; charset=utf-8') expect(res.headers.get('Content-Length')).toBe('17') + }) - req = new Request('http://localhost/static/hono.html') - res = await app.dispatch(req) + it('Should return hono.html', async () => { + const res = await app.request('http://localhost/static/hono.html') expect(res.status).toBe(200) expect(await res.text()).toBe('

Hono!

') expect(res.headers.get('Content-Type')).toBe('text/html; charset=utf-8') expect(res.headers.get('Content-Length')).toBe('14') + }) - req = new Request('http://localhost/static/not-found.html') - res = await app.dispatch(req) + it('Should return 404 response', async () => { + const res = await app.request('http://localhost/static/not-found.html') expect(res.status).toBe(404) + }) - req = new Request('http://localhost/static-no-root/plain.txt') - res = await app.dispatch(req) + it('Should return plan.txt', async () => { + const res = await app.request('http://localhost/static-no-root/plain.txt') expect(res.status).toBe(200) expect(await res.text()).toBe('That is plain.txt') expect(res.headers.get('Content-Type')).toBe('text/plain; charset=utf-8') expect(res.headers.get('Content-Length')).toBe('17') }) - it('Serve index.html', async () => { - const req = new Request('http://localhost/static/top') - const res = await app.dispatch(req) + it('Should return index.html', async () => { + const res = await app.request('http://localhost/static/top') expect(res.status).toBe(200) expect(await res.text()).toBe('

Top

') expect(res.headers.get('Content-Type')).toBe('text/html; charset=utf-8')