From a5be555b3163236909f5699f783bbd03e28ea226 Mon Sep 17 00:00:00 2001 From: Taku Amano Date: Mon, 26 Jan 2026 23:36:06 +0900 Subject: [PATCH 1/5] refactor: use `unique symbol` for more accurate typing. (#4651) --- src/request/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/request/constants.ts b/src/request/constants.ts index 94b4bb932..c95a35c85 100644 --- a/src/request/constants.ts +++ b/src/request/constants.ts @@ -1 +1 @@ -export const GET_MATCH_RESULT: symbol = Symbol() +export const GET_MATCH_RESULT: unique symbol = Symbol() From 8078bbf7591ec133ac8a4f7cfc4f2666a50909a9 Mon Sep 17 00:00:00 2001 From: Sano Suguru <43309177+sano-suguru@users.noreply.github.com> Date: Mon, 26 Jan 2026 23:40:05 +0900 Subject: [PATCH 2/5] docs: align CODE_OF_CONDUCT.md wording with Contributor Covenant (#4630) Co-authored-by: sanosuguru --- docs/CODE_OF_CONDUCT.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md index 0f4f4da02..ffa3d9416 100644 --- a/docs/CODE_OF_CONDUCT.md +++ b/docs/CODE_OF_CONDUCT.md @@ -14,8 +14,8 @@ diverse, inclusive, and healthy community. ## Our Standards -Examples of behavior that contribute to a positive environment for our -community includes: +Examples of behavior that contributes to a positive environment for our +community include: - Demonstrating empathy and kindness toward other people - Being respectful of differing opinions, viewpoints, and experiences From 2a9cd953b6e94ecef45894654ec5e765e1e9e411 Mon Sep 17 00:00:00 2001 From: AprilNEA Date: Mon, 26 Jan 2026 22:42:07 +0800 Subject: [PATCH 3/5] fix(sse): handle `\r` and `\r\n` line endings in writeSSE (#4644) According to the SSE specification, lines can end with `\r`, `\n`, or `\r\n`. --- src/helper/streaming/sse.test.tsx | 77 +++++++++++++++++++++++++++++++ src/helper/streaming/sse.ts | 2 +- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/helper/streaming/sse.test.tsx b/src/helper/streaming/sse.test.tsx index d51cc3eec..c5d927961 100644 --- a/src/helper/streaming/sse.test.tsx +++ b/src/helper/streaming/sse.test.tsx @@ -238,4 +238,81 @@ describe('SSE Streaming helper', () => { const decodedValue = decoder.decode(value) expect(decodedValue).toBe('data:
Error
\n\n') }) + + it('Check streamSSE handles \\r (CR) line ending correctly', async () => { + const res = streamSSE(c, async (stream) => { + await stream.writeSSE({ + data: 'Line1\rLine2', + event: 'test-cr', + }) + }) + + if (!res.body) { + throw new Error('Body is null') + } + const reader = res.body.getReader() + const decoder = new TextDecoder() + const { value } = await reader.read() + const decodedValue = decoder.decode(value) + + expect(decodedValue).toBe('event: test-cr\ndata: Line1\ndata: Line2\n\n') + }) + + it('Check streamSSE handles \\r\\n (CRLF) line ending correctly', async () => { + const res = streamSSE(c, async (stream) => { + await stream.writeSSE({ + data: 'Line1\r\nLine2', + event: 'test-crlf', + }) + }) + + if (!res.body) { + throw new Error('Body is null') + } + const reader = res.body.getReader() + const decoder = new TextDecoder() + const { value } = await reader.read() + const decodedValue = decoder.decode(value) + + expect(decodedValue).toBe('event: test-crlf\ndata: Line1\ndata: Line2\n\n') + }) + + it('Check streamSSE handles mixed line endings correctly', async () => { + const res = streamSSE(c, async (stream) => { + await stream.writeSSE({ + data: 'A\nB\rC\r\nD', + event: 'test-mixed', + }) + }) + + if (!res.body) { + throw new Error('Body is null') + } + const reader = res.body.getReader() + const decoder = new TextDecoder() + const { value } = await reader.read() + const decodedValue = decoder.decode(value) + + expect(decodedValue).toBe('event: test-mixed\ndata: A\ndata: B\ndata: C\ndata: D\n\n') + }) + + it('Check streamSSE handles consecutive \\r correctly', async () => { + const res = streamSSE(c, async (stream) => { + await stream.writeSSE({ + data: 'Left\r\rRight', + event: 'test-double-cr', + }) + }) + + if (!res.body) { + throw new Error('Body is null') + } + const reader = res.body.getReader() + const decoder = new TextDecoder() + const { value } = await reader.read() + const decodedValue = decoder.decode(value) + + // Two \r should produce an empty line in between + expect(decodedValue).toBe('event: test-double-cr\ndata: Left\ndata: \ndata: Right\n\n') + }) }) diff --git a/src/helper/streaming/sse.ts b/src/helper/streaming/sse.ts index 8c10eae51..ef9368261 100644 --- a/src/helper/streaming/sse.ts +++ b/src/helper/streaming/sse.ts @@ -18,7 +18,7 @@ export class SSEStreamingApi extends StreamingApi { async writeSSE(message: SSEMessage) { const data = await resolveCallback(message.data, HtmlEscapedCallbackPhase.Stringify, false, {}) const dataLines = (data as string) - .split('\n') + .split(/\r\n|\r|\n/) .map((line) => { return `data: ${line}` }) From b6e5a97cd44b3572320b2f14763ff83ff826aeb8 Mon Sep 17 00:00:00 2001 From: Artem Tamoian Date: Mon, 26 Jan 2026 15:44:09 +0100 Subject: [PATCH 4/5] feat(bun): export getBunServer (#4626) * feat(bun): export getBunServer * fix: add generic to jsdoc --- src/adapter/bun/conninfo.ts | 8 +++++++- src/adapter/bun/index.ts | 1 + src/adapter/bun/server.test.ts | 3 +-- src/adapter/bun/server.ts | 22 +++------------------- src/adapter/bun/websocket.ts | 10 +++++++++- 5 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/adapter/bun/conninfo.ts b/src/adapter/bun/conninfo.ts index b069ec841..c3d150d28 100644 --- a/src/adapter/bun/conninfo.ts +++ b/src/adapter/bun/conninfo.ts @@ -8,7 +8,13 @@ import { getBunServer } from './server' * @returns ConnInfo */ export const getConnInfo: GetConnInfo = (c: Context) => { - const server = getBunServer(c) + const server = getBunServer<{ + requestIP?: (req: Request) => { + address: string + family: string + port: number + } | null + }>(c) if (!server) { throw new TypeError('env has to include the 2nd argument of fetch.') diff --git a/src/adapter/bun/index.ts b/src/adapter/bun/index.ts index 1e1656b4e..a18d55f35 100644 --- a/src/adapter/bun/index.ts +++ b/src/adapter/bun/index.ts @@ -8,3 +8,4 @@ export { bunFileSystemModule, toSSG } from './ssg' export { createBunWebSocket, upgradeWebSocket, websocket } from './websocket' export type { BunWebSocketData, BunWebSocketHandler } from './websocket' export { getConnInfo } from './conninfo' +export { getBunServer } from './server' diff --git a/src/adapter/bun/server.test.ts b/src/adapter/bun/server.test.ts index 13a791055..f145ee1f8 100644 --- a/src/adapter/bun/server.test.ts +++ b/src/adapter/bun/server.test.ts @@ -1,10 +1,9 @@ import { Context } from '../../context' import { getBunServer } from './server' -import type { BunServer } from './server' describe('getBunServer', () => { it('Should success to pick Server', () => { - const server = {} as BunServer + const server = {} expect(getBunServer(new Context(new Request('http://localhost/'), { env: server }))).toBe( server diff --git a/src/adapter/bun/server.ts b/src/adapter/bun/server.ts index 3e88494e7..b042f0d2a 100644 --- a/src/adapter/bun/server.ts +++ b/src/adapter/bun/server.ts @@ -4,27 +4,11 @@ */ import type { Context } from '../../context' -/** - * Bun Server Object - */ -export interface BunServer { - requestIP?: (req: Request) => { - address: string - family: string - port: number - } | null - upgrade( - req: Request, - options?: { - data: T - } - ): boolean -} - /** * Get Bun Server Object from Context + * @template T - The type of Bun Server * @param c Context * @returns Bun Server */ -export const getBunServer = (c: Context): BunServer | undefined => - ('server' in c.env ? c.env.server : c.env) as BunServer | undefined +export const getBunServer = (c: Context): T | undefined => + ('server' in c.env ? c.env.server : c.env) as T | undefined diff --git a/src/adapter/bun/websocket.ts b/src/adapter/bun/websocket.ts index 73631dda4..af5db0645 100644 --- a/src/adapter/bun/websocket.ts +++ b/src/adapter/bun/websocket.ts @@ -46,7 +46,15 @@ export const createWSContext = (ws: BunServerWebSocket): WSCon } export const upgradeWebSocket: UpgradeWebSocket = defineWebSocketHelper((c, events) => { - const server = getBunServer(c) + const server = getBunServer<{ + upgrade( + req: Request, + options?: { + data: T + } + ): boolean + }>(c) + if (!server) { throw new TypeError('env has to include the 2nd argument of fetch.') } From 7343487e620631d30bf3da54650546fd2e6a9bee Mon Sep 17 00:00:00 2001 From: Yusuke Wada Date: Mon, 26 Jan 2026 23:47:13 +0900 Subject: [PATCH 5/5] 4.11.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d464324f8..2fd55aced 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hono", - "version": "4.11.5", + "version": "4.11.6", "description": "Web framework built on Web Standards", "main": "dist/cjs/index.js", "type": "module",