Skip to content

Commit

Permalink
chore: denoify
Browse files Browse the repository at this point in the history
  • Loading branch information
sor4chi committed Dec 23, 2023
1 parent e0864b9 commit 69f30b6
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 57 deletions.
10 changes: 8 additions & 2 deletions deno_dist/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { StatusCode } from './utils/http-status.ts'
import { StreamingApi } from './utils/stream.ts'
import type { JSONValue, InterfaceToType, JSONParsed } from './utils/types.ts'

type HeaderRecord = Record<string, string | string[]>
export type HeaderRecord = Record<string, string | string[]>
type Data = string | ArrayBuffer | ReadableStream

export interface ExecutionContext {
Expand Down Expand Up @@ -83,7 +83,7 @@ type ContextOptions<E extends Env> = {
notFoundHandler?: NotFoundHandler<E>
}

const TEXT_PLAIN = 'text/plain; charset=UTF-8'
export const TEXT_PLAIN = 'text/plain; charset=UTF-8'

export class Context<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -375,6 +375,9 @@ export class Context<
return this.newResponse(null, status)
}

/** @deprecated
* Use `streamText()` in `hono/helper/streaming` instead of `c.streamText()`. The `c.streamText()` will be removed in v4.
*/
streamText = (
cb: (stream: StreamingApi) => Promise<void>,
arg?: StatusCode | ResponseInit,
Expand All @@ -387,6 +390,9 @@ export class Context<
return this.stream(cb, arg, headers)
}

/** @deprecated
* Use `stream()` in `hono/helper/streaming` instead of `c.stream()`. The `c.stream()` will be removed in v4.
*/
stream = (
cb: (stream: StreamingApi) => Promise<void>,
arg?: StatusCode | ResponseInit,
Expand Down
72 changes: 17 additions & 55 deletions deno_dist/helper/streaming/index.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,21 @@
import type { Context } from '../../context.ts'
import { Context, HeaderRecord, TEXT_PLAIN } from '../../context.ts'
import { StatusCode } from '../../utils/http-status.ts'
import { StreamingApi } from '../../utils/stream.ts'

interface SSEMessage {
data: string
event?: string
id?: string
export const stream = (
c: Context,
cb: (stream: StreamingApi) => Promise<void>,
arg?: StatusCode | ResponseInit,
headers?: HeaderRecord
): Response => {
const { readable, writable } = new TransformStream()
const stream = new StreamingApi(writable)
cb(stream).finally(() => stream.close())

return typeof arg === 'number'
? c.newResponse(readable, arg, headers)
: c.newResponse(readable, arg)
}

class SSEStreamingApi extends StreamingApi {
constructor(writable: WritableStream) {
super(writable)
}

async writeSSE(message: SSEMessage) {
const data = message.data
.split('\n')
.map((line) => {
return `data: ${line}`
})
.join('\n')

const sseData =
[message.event && `event: ${message.event}`, data, message.id && `id: ${message.id}`]
.filter(Boolean)
.join('\n') + '\n\n'

await this.write(sseData)
}
}

const setSSEHeaders = (context: Context) => {
context.header('Transfer-Encoding', 'chunked')
context.header('Content-Type', 'text/event-stream')
context.header('Cache-Control', 'no-cache')
context.header('Connection', 'keep-alive')
}

export const streamSSE = (c: Context, cb: (stream: SSEStreamingApi) => Promise<void>) => {
return c.stream(async (originalStream: StreamingApi) => {
const { readable, writable } = new TransformStream()
const stream = new SSEStreamingApi(writable)

originalStream.pipe(readable).catch((err) => {
console.error('Error in stream piping: ', err)
stream.close()
})

setSSEHeaders(c)

try {
await cb(stream)
} catch (err) {
console.error('Error during streaming: ', err)
} finally {
await stream.close()
}
})
}
export { streamSSE } from './sse.ts'
export { streamText } from './text.ts'
60 changes: 60 additions & 0 deletions deno_dist/helper/streaming/sse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { stream } from './index.ts'
import type { Context } from '../../context.ts'
import { StreamingApi } from '../../utils/stream.ts'

interface SSEMessage {
data: string
event?: string
id?: string
}

class SSEStreamingApi extends StreamingApi {
constructor(writable: WritableStream) {
super(writable)
}

async writeSSE(message: SSEMessage) {
const data = message.data
.split('\n')
.map((line) => {
return `data: ${line}`
})
.join('\n')

const sseData =
[message.event && `event: ${message.event}`, data, message.id && `id: ${message.id}`]
.filter(Boolean)
.join('\n') + '\n\n'

await this.write(sseData)
}
}

const setSSEHeaders = (context: Context) => {
context.header('Transfer-Encoding', 'chunked')
context.header('Content-Type', 'text/event-stream')
context.header('Cache-Control', 'no-cache')
context.header('Connection', 'keep-alive')
}

export const streamSSE = (c: Context, cb: (stream: SSEStreamingApi) => Promise<void>) => {
return stream(c, async (originalStream: StreamingApi) => {
const { readable, writable } = new TransformStream()
const stream = new SSEStreamingApi(writable)

originalStream.pipe(readable).catch((err) => {
console.error('Error in stream piping: ', err)
stream.close()
})

setSSEHeaders(c)

try {
await cb(stream)
} catch (err) {
console.error('Error during streaming: ', err)
} finally {
await stream.close()
}
})
}
17 changes: 17 additions & 0 deletions deno_dist/helper/streaming/text.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { stream } from './index.ts'
import { Context, HeaderRecord, TEXT_PLAIN } from '../../context.ts'
import { StatusCode } from '../../utils/http-status.ts'
import { StreamingApi } from '../../utils/stream.ts'

export const streamText = (
c: Context,
cb: (stream: StreamingApi) => Promise<void>,
arg?: StatusCode | ResponseInit,
headers?: HeaderRecord
): Response => {
headers ??= {}
c.header('content-type', TEXT_PLAIN)
c.header('x-content-type-options', 'nosniff')
c.header('transfer-encoding', 'chunked')
return stream(c, cb, arg, headers)
}

0 comments on commit 69f30b6

Please sign in to comment.