Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 91 additions & 87 deletions lib/server-response-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,108 +16,112 @@ export function createServerResponseAdapter(
fn: (re: ServerResponse) => Promise<void> | void,
): Promise<Response> {
let writeHeadResolver: (v: WriteheadArgs) => void
const writeHeadPromise = new Promise<WriteheadArgs>(
async (resolve, reject) => {
writeHeadResolver = resolve
},
)

return new Promise(async (resolve, reject) => {
let controller: ReadableStreamController<Uint8Array> | undefined
let shouldClose = false
let wroteHead = false

const writeHead = (
statusCode: number,
headers?: Record<string, string>,
) => {
if (typeof headers === 'string') {
throw new Error('Status message of writeHead not supported')
}
wroteHead = true
writeHeadResolver({
statusCode,
headers,
})
return fakeServerResponse
}
const writeHeadPromise = new Promise<WriteheadArgs>((resolve) => {
writeHeadResolver = resolve
})

const bufferedData: Uint8Array[] = []
return new Promise((resolve, reject) => {
;(async () => {
let controller: ReadableStreamController<Uint8Array> | undefined
let shouldClose = false
let wroteHead = false

const write = (
chunk: Buffer | string,
encoding?: BufferEncoding,
): boolean => {
if (encoding) {
throw new Error('Encoding not supported')
}
if (chunk instanceof Buffer) {
throw new Error('Buffer not supported')
}
if (!wroteHead) {
writeHead(200)
}
if (!controller) {
bufferedData.push(new TextEncoder().encode(chunk as string))
return true
const writeHead = (
statusCode: number,
headers?: Record<string, string>,
) => {
if (typeof headers === 'string') {
throw new Error('Status message of writeHead not supported')
}
wroteHead = true
writeHeadResolver({
statusCode,
headers,
})
return fakeServerResponse
}
controller.enqueue(new TextEncoder().encode(chunk as string))
return true
}

const eventEmitter = new EventEmitter()
const bufferedData: Uint8Array[] = []

const fakeServerResponse = {
writeHead,
write,
end: (data?: Buffer | string) => {
if (data) {
write(data)
const write = (
chunk: Buffer | string,
encoding?: BufferEncoding,
): boolean => {
if (encoding) {
throw new Error('Encoding not supported')
}

if (!controller) {
shouldClose = true
return fakeServerResponse
if (chunk instanceof Buffer) {
throw new Error('Buffer not supported')
}
try {
controller.close()
} catch {
/* May be closed on tcp layer */
if (!wroteHead) {
writeHead(200)
}
return fakeServerResponse
},
on: (event: string, listener: (...args: any[]) => void) => {
eventEmitter.on(event, listener)
return fakeServerResponse
},
}

signal.addEventListener('abort', () => {
eventEmitter.emit('close')
})
if (!controller) {
bufferedData.push(new TextEncoder().encode(chunk as string))
return true
}
controller.enqueue(new TextEncoder().encode(chunk as string))
return true
}

fn(fakeServerResponse as ServerResponse)
const eventEmitter = new EventEmitter()

const head = await writeHeadPromise
const fakeServerResponse = {
writeHead,
write,
end: (data?: Buffer | string) => {
if (data) {
write(data)
}

const response = new Response(
new ReadableStream({
start(c) {
controller = c
for (const chunk of bufferedData) {
controller.enqueue(chunk)
if (!controller) {
shouldClose = true
return fakeServerResponse
}
if (shouldClose) {
try {
controller.close()
} catch {
/* May be closed on tcp layer */
}
return fakeServerResponse
},
on: (event: string, listener: (...args: unknown[]) => void) => {
eventEmitter.on(event, listener)
return fakeServerResponse
},
}),
{
status: head.statusCode,
headers: head.headers,
},
)
}

signal.addEventListener('abort', () => {
eventEmitter.emit('close')
})

resolve(response)
fn(fakeServerResponse as ServerResponse)

try {
const head = await writeHeadPromise

const response = new Response(
new ReadableStream({
start(c) {
controller = c
for (const chunk of bufferedData) {
controller.enqueue(chunk)
}
if (shouldClose) {
controller.close()
}
},
}),
{
status: head.statusCode,
headers: head.headers,
},
)

resolve(response)
} catch (error) {
reject(error)
}
})()
})
}
2 changes: 1 addition & 1 deletion lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { clsx, type ClassValue } from 'clsx'
import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'

export function cn(...inputs: ClassValue[]) {
Expand Down