mirror of
https://github.com/honojs/hono.git
synced 2024-12-01 10:51:01 +00:00
feat: update visibility (#164)
This commit is contained in:
parent
1a30e37dfa
commit
3d064d0559
@ -25,7 +25,7 @@ export class Context<RequestParamKeyType = string> {
|
||||
req: Request<RequestParamKeyType>,
|
||||
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<RequestParamKeyType = string> {
|
||||
this._headers = {}
|
||||
}
|
||||
|
||||
private initRequest<T>(req: Request<T>): Request<T> {
|
||||
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<RequestParamKeyType = string> {
|
||||
|
||||
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)
|
||||
|
26
src/hono.ts
26
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<any> } = TrieRouter
|
||||
strict: boolean = true // strict routing - default is true
|
||||
router: Router<Handler>
|
||||
middlewareRouters: Router<MiddlewareHandler>[]
|
||||
tempPath: string
|
||||
readonly routerClass: { new (): Router<any> } = TrieRouter
|
||||
readonly strict: boolean = true // strict routing - default is true
|
||||
private router: Router<Handler>
|
||||
private middlewareRouters: Router<MiddlewareHandler>[]
|
||||
private tempPath: string
|
||||
|
||||
constructor(init: Partial<Pick<Hono, 'routerClass' | 'strict'>> = {}) {
|
||||
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<Response> {
|
||||
private async dispatch(request: Request, env?: Env, event?: FetchEvent): Promise<Response> {
|
||||
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<Response> {
|
||||
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))
|
||||
})
|
||||
|
@ -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')
|
||||
|
@ -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' })
|
||||
|
@ -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(
|
||||
|
@ -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']))
|
||||
|
@ -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('')
|
||||
})
|
||||
|
@ -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)
|
||||
|
@ -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('<html><body>Title: Hono!</body></html>')
|
||||
})
|
||||
|
||||
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('<h1>With Root</h1>')
|
||||
})
|
||||
|
@ -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')
|
||||
|
@ -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(`{
|
||||
|
@ -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('<h1>Hono!</h1>')
|
||||
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('<h1>Top</h1>')
|
||||
expect(res.headers.get('Content-Type')).toBe('text/html; charset=utf-8')
|
||||
|
Loading…
Reference in New Issue
Block a user