Skip to content

Commit

Permalink
fix: Nested constructors should not cause an error even if ReadableSt…
Browse files Browse the repository at this point in the history
…ream is specified (#109)
  • Loading branch information
usualoma authored Dec 13, 2023
1 parent 0e0ef48 commit 63ec862
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 14 deletions.
29 changes: 16 additions & 13 deletions src/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,33 @@ import type { OutgoingHttpHeaders } from 'node:http'
import { buildOutgoingHttpHeaders } from './utils'

const responseCache = Symbol('responseCache')
const newGlobalResponseKey = Symbol('newGlobalResponse')
export const cacheKey = Symbol('cache')

export const GlobalResponse = global.Response
export class Response {
#body?: BodyInit | null
#init?: ResponseInit;
#init?: ResponseInit

[newGlobalResponseKey](): typeof GlobalResponse {
return new GlobalResponse(
this.#body,
this.#init instanceof Response ? this.#init[newGlobalResponseKey]() : (this.#init as any)
) as any
}

// @ts-ignore
private get cache(): typeof GlobalResponse {
delete (this as any)[cacheKey]
return ((this as any)[responseCache] ||= this[newGlobalResponseKey]())
return ((this as any)[responseCache] ||= new GlobalResponse(this.#body, this.#init))
}

constructor(body?: BodyInit | null, init?: ResponseInit) {
this.#body = body
this.#init = init
if (init instanceof Response) {
const cachedGlobalResponse = (init as any)[responseCache]
if (cachedGlobalResponse) {
this.#init = cachedGlobalResponse
// instantiate GlobalResponse cache and this object always returns value from global.Response
this.cache
return
} else {
this.#init = init.#init
}
} else {
this.#init = init
}

if (typeof body === 'string' || body instanceof ReadableStream) {
let headers = (init?.headers || { 'content-type': 'text/plain;charset=UTF-8' }) as
Expand All @@ -38,7 +41,7 @@ export class Response {
headers = buildOutgoingHttpHeaders(headers)
}

(this as any)[cacheKey] = [init?.status || 200, body, headers]
;(this as any)[cacheKey] = [init?.status || 200, body, headers]
}
}
}
Expand Down
27 changes: 26 additions & 1 deletion test/response.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ import { GlobalResponse } from '../src/response'

class NextResponse extends Response {}

class UpperCaseStream extends TransformStream {
constructor() {
super({
transform(chunk, controller) {
controller.enqueue(
new TextEncoder().encode(new TextDecoder().decode(chunk).toString().toUpperCase())
)
},
})
}
}

describe('Response', () => {
let server: Server
let port: number
Expand Down Expand Up @@ -54,14 +66,27 @@ describe('Response', () => {

// support other class to extends from Response
expect(new NextResponse()).toBeInstanceOf(Response)
})

it('Should not lose header data', async () => {
const parentResponse = new Response('OK', {
headers: {
'content-type': 'application/json',
}
},
})
const childResponse = new Response('OK', parentResponse)
parentResponse.headers.delete('content-type')
expect(childResponse.headers.get('content-type')).toEqual('application/json')
})

it('Nested constructors should not cause an error even if ReadableStream is specified', async () => {
const stream = new Response('hono').body
const parentResponse = new Response(stream)
const upperCaseStream = new UpperCaseStream()
const childResponse = new Response(
parentResponse.body!.pipeThrough(upperCaseStream),
parentResponse
)
expect(await childResponse.text()).toEqual('HONO')
})
})

0 comments on commit 63ec862

Please sign in to comment.