Comparing version 0.11.1 to 0.11.2
{ | ||
"name": "bunshine", | ||
"version": "0.11.1", | ||
"version": "0.11.2", | ||
"module": "server/server.ts", | ||
@@ -5,0 +5,0 @@ "type": "module", |
@@ -1,7 +0,7 @@ | ||
<img alt="Bunshine Logo" src="https://github.com/kensnyder/bunshine/raw/main/assets/bunshine-logo.png?v=0.11.1" width="200" height="187" /> | ||
<img alt="Bunshine Logo" src="https://github.com/kensnyder/bunshine/raw/main/assets/bunshine-logo.png?v=0.11.2" width="200" height="187" /> | ||
[](https://npmjs.com/package/bunshine) | ||
[](https://www.npmjs.com/package/bunshine?activeTab=dependencies) | ||
 | ||
[](https://opensource.org/licenses/ISC) | ||
[](https://npmjs.com/package/bunshine) | ||
[](https://www.npmjs.com/package/bunshine?activeTab=dependencies) | ||
 | ||
[](https://opensource.org/licenses/ISC) | ||
@@ -8,0 +8,0 @@ # Bunshine |
@@ -27,2 +27,4 @@ import type { Server } from 'bun'; | ||
expect(c.app).toBe(app); | ||
expect(c.date).toBeInstanceOf(Date); | ||
expect(c.now).toBeNumber(); | ||
expect(c.url).toBeInstanceOf(URL); | ||
@@ -96,3 +98,3 @@ expect(c.url.pathname).toBe('/home'); | ||
expect(await resp.text()).toBe('Hi'); | ||
expect(resp.headers.get('Content-type')).toBe('text/plain'); | ||
expect(resp.headers.get('Content-type')).toStartWith('text/plain'); | ||
}); | ||
@@ -102,3 +104,3 @@ it('should include js()', async () => { | ||
expect(await resp.text()).toBe('alert(42)'); | ||
expect(resp.headers.get('Content-type')).toBe('text/javascript'); | ||
expect(resp.headers.get('Content-type')).toStartWith('text/javascript'); | ||
}); | ||
@@ -108,3 +110,3 @@ it('should include html()', async () => { | ||
expect(await resp.text()).toBe('<h1>Hi</h1>'); | ||
expect(resp.headers.get('Content-type')).toBe('text/html'); | ||
expect(resp.headers.get('Content-type')).toStartWith('text/html'); | ||
}); | ||
@@ -114,9 +116,8 @@ it('should include xml()', async () => { | ||
expect(await resp.text()).toBe('<greeting>Hi</greeting>'); | ||
expect(resp.headers.get('Content-type')).toBe('text/xml'); | ||
expect(resp.headers.get('Content-type')).toStartWith('text/xml'); | ||
}); | ||
it('should include json(data)', async () => { | ||
const resp = json({ hello: 'world' }); | ||
// @ts-expect-error | ||
expect(await resp.json()).toEqual({ hello: 'world' }); | ||
expect(resp.headers.get('Content-type')).toBe('application/json'); | ||
expect(resp.headers.get('Content-type')).toStartWith('application/json'); | ||
}); | ||
@@ -132,5 +133,4 @@ it('should include json(data, init)', async () => { | ||
); | ||
// @ts-expect-error | ||
expect(await resp.json()).toEqual({ hello: 'world' }); | ||
expect(resp.headers.get('Content-type')).toBe('application/json'); | ||
expect(resp.headers.get('Content-type')).toStartWith('application/json'); | ||
expect(resp.headers.get('X-Hello')).toBe('World'); | ||
@@ -137,0 +137,0 @@ }); |
@@ -33,2 +33,7 @@ import type { BunFile, Server } from 'bun'; | ||
url: URL; | ||
/** The date the request was received */ | ||
date: Date; | ||
/** The milliseconds between server start and this request, as float (from performance.now()) */ | ||
now: number; | ||
// construct this Context object | ||
constructor(request: Request, server: Server, app: HttpRouter) { | ||
@@ -39,6 +44,9 @@ this.request = request; | ||
this.url = new URL(request.url); | ||
this.date = new Date(); | ||
this.now = performance.now(); | ||
} | ||
getIp = () => { | ||
/** Get the IP address info of the client */ | ||
get ip(): { address: string; family: string; port: number } | null { | ||
return this.server.requestIP(this.request); | ||
}; | ||
} | ||
/** A shorthand for `new Response(text, { headers: { 'Content-type': 'text/plain' } })` */ | ||
@@ -59,13 +67,8 @@ text = text; | ||
filenameOrBunFile: string | BunFile, | ||
fileOptions: FileResponseOptions = {}, | ||
responseInit: ResponseInit = {} | ||
fileOptions: FileResponseOptions = {} | ||
) => { | ||
return file( | ||
filenameOrBunFile, | ||
{ | ||
range: this.request.headers.get('Range') || undefined, | ||
...fileOptions, | ||
}, | ||
responseInit | ||
); | ||
return file(filenameOrBunFile, { | ||
range: this.request.headers.get('Range') || undefined, | ||
...fileOptions, | ||
}); | ||
}; | ||
@@ -72,0 +75,0 @@ /** A shorthand for `new Response({ headers: { 'Content-type': 'text/event-stream' } })` */ |
@@ -186,3 +186,2 @@ import type { Server } from 'bun'; | ||
expect(resp.status).toBe(200); | ||
// @ts-expect-error | ||
expect(await resp.json()).toEqual({ | ||
@@ -281,2 +280,15 @@ pathname: '/user/123/account', | ||
}); | ||
it('should get client ip info', async () => { | ||
app.get('/', c => c.json(c.ip)); | ||
server = app.listen(); | ||
const resp = await fetch(`http://localhost:${server.port}/`); | ||
const info = (await resp.json()) as { | ||
address: string; | ||
family: string; | ||
port: number; | ||
}; | ||
expect(info.address).toBe('::1'); | ||
expect(info.family).toBe('IPv6'); | ||
expect(info.port).toBeGreaterThan(0); | ||
}); | ||
it('should handle all', async () => { | ||
@@ -283,0 +295,0 @@ app.all('/', () => new Response('Hi')); |
@@ -98,3 +98,3 @@ import type { ServeOptions, Server } from 'bun'; | ||
throw new Error( | ||
'Cannot emit URL before server has been started. Call .listen() first.' | ||
'Cannot emit URL before server has been started. Use .listen() to start the server first.' | ||
); | ||
@@ -101,0 +101,0 @@ } |
import { BunFile } from 'bun'; | ||
export const text = getResponseFactory('text/plain'); | ||
export const js = getResponseFactory('text/javascript'); | ||
export const html = getResponseFactory('text/html'); | ||
export const xml = getResponseFactory('text/xml'); | ||
export const json = (data: Record<string, any>, init: ResponseInit = {}) => { | ||
export const text = getResponseFactory('text/plain; charset=utf-8'); | ||
export const js = getResponseFactory('text/javascript; charset=utf-8'); | ||
export const html = getResponseFactory('text/html; charset=utf-8'); | ||
export const xml = getResponseFactory('text/xml; charset=utf-8'); | ||
export const json = (data: any, init: ResponseInit = {}) => { | ||
return new Response(JSON.stringify(data), { | ||
...init, | ||
// @ts-expect-error | ||
headers: { | ||
...(init.headers || {}), | ||
'Content-Type': 'application/json', | ||
'Content-Type': 'application/json; charset=utf-8', | ||
}, | ||
@@ -132,5 +133,7 @@ }); | ||
headers.has('Content-Type') && | ||
headers.get('Content-Type') !== 'text/event-stream' | ||
!/^text\/event-stream/.test(headers.get('Content-Type')!) | ||
) { | ||
console.warn('Overriding Content-Type header to `text/event-stream`'); | ||
console.warn( | ||
'Overriding Content-Type header to `text/event-stream; charset=utf-8`' | ||
); | ||
} | ||
@@ -146,3 +149,3 @@ if ( | ||
} | ||
headers.set('Content-Type', 'text/event-stream'); | ||
headers.set('Content-Type', 'text/event-stream; charset=utf-8'); | ||
headers.set('Cache-Control', 'no-cache'); | ||
@@ -173,2 +176,3 @@ headers.set('Connection', 'keep-alive'); | ||
method, | ||
responseInit, | ||
}: { | ||
@@ -180,2 +184,3 @@ file: BunFile; | ||
method: string; | ||
responseInit?: ResponseInit; | ||
}) { | ||
@@ -182,0 +187,0 @@ let response: Response; |
import type { Middleware } from '../../HttpRouter/HttpRouter'; | ||
export function performanceHeader(headerName: string = 'X-Took'): Middleware { | ||
return async (_, next) => { | ||
const start = performance.now(); | ||
return async (c, next) => { | ||
const resp = await next(); | ||
const ms = (performance.now() - start).toFixed(3); | ||
const ms = (performance.now() - c.now).toFixed(3); | ||
resp.headers.set(headerName, ms); | ||
@@ -9,0 +8,0 @@ return resp; |
204896
3570