Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: honojs/hono
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v4.2.3
Choose a base ref
...
head repository: honojs/hono
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v4.2.4
Choose a head ref
  • 8 commits
  • 23 files changed
  • 6 contributors

Commits on Apr 10, 2024

  1. fix(jwt): Make JWT Header typ Field Optional to Enhance Compatibili…

    …ty (#2488)
    
    * chore(utils/jwt): change typ parameter optional
    
    * chore(deno_dist): run denoify
    
    * chore(utils/jwt): add test case
    naporin0624 authored Apr 10, 2024
    Copy the full SHA
    0a92236 View commit details

Commits on Apr 11, 2024

  1. fix(testing): set baseUrl for testClient (#2496)

    * fix(testing): set `baseUrl` for `testClient`
    
    * denoify
    yusukebe authored Apr 11, 2024
    Copy the full SHA
    f5104e8 View commit details

Commits on Apr 12, 2024

  1. fix(validator): Default use to OutputTypeExcludeResponseType when `…

    …InputType` is unknown (#2500)
    
    * fix(validator): Default use to `OutputTypeExcludeResponseType` when `InputType` is unknown
    
    * denoify
    nagasawaryoya authored Apr 12, 2024
    Copy the full SHA
    a33b9b7 View commit details

Commits on Apr 13, 2024

  1. refactor(trie-router): parentPatterns is updated but never queried (#…

    exoego authored Apr 13, 2024
    Copy the full SHA
    7e113f3 View commit details
  2. refactor: Remove redundant initializer (#2502)

    exoego authored Apr 13, 2024
    Copy the full SHA
    fb31c8a View commit details
  3. refactor(cloudflare-workers): Suppress eslint noise (#2504)

    exoego authored Apr 13, 2024
    Copy the full SHA
    c2c76a8 View commit details
  4. fix(jsx): Add catch to async function's promise (#2471)

    * Add catch to async function's promise
    
    The `.then(async function())` returns a new promise, that the above .catch() does not catch, resulting in rapid refreshing of the page to cause a fatal uncatchable error. Adding this second .catch fixes that.
    
    * Revert "Add catch to async function's promise"
    
    This reverts commit b212f21.
    
    * Add error handling callback to renderToReadableStream function
    
    * runs denoify
    
    Co-authored-by: Taku Amano <taku@taaas.jp>
    mwilkins91 and usualoma authored Apr 13, 2024
    Copy the full SHA
    7386a4a View commit details
  5. v4.2.4

    yusukebe committed Apr 13, 2024
    Copy the full SHA
    13c3156 View commit details
3 changes: 1 addition & 2 deletions deno_dist/client/client.ts
Original file line number Diff line number Diff line change
@@ -84,7 +84,6 @@ class ClientRequestImpl {
}

let methodUpperCase = this.method.toUpperCase()
let setBody = !(methodUpperCase === 'GET' || methodUpperCase === 'HEAD')

const headerValues: Record<string, string> = {
...(args?.header ?? {}),
@@ -117,7 +116,7 @@ class ClientRequestImpl {
url = url + '?' + this.queryParams.toString()
}
methodUpperCase = this.method.toUpperCase()
setBody = !(methodUpperCase === 'GET' || methodUpperCase === 'HEAD')
const setBody = !(methodUpperCase === 'GET' || methodUpperCase === 'HEAD')

// Pass URL string to 1st arg for testing with MSW and node-fetch
return (opt?.fetch || fetch)(url, {
2 changes: 1 addition & 1 deletion deno_dist/helper/ssg/ssg.ts
Original file line number Diff line number Diff line change
@@ -271,7 +271,7 @@ export interface ToSSGAdaptorInterface<
* The API might be changed.
*/
export const toSSG: ToSSGInterface = async (app, fs, options) => {
let result: ToSSGResult | undefined = undefined
let result: ToSSGResult | undefined
const getInfoPromises: Promise<unknown>[] = []
const savePromises: Promise<string | undefined>[] = []
try {
2 changes: 1 addition & 1 deletion deno_dist/helper/testing/index.ts
Original file line number Diff line number Diff line change
@@ -15,5 +15,5 @@ export const testClient = <T extends Hono<any, any, any>>(
return app.request(input, init, Env, executionCtx)
}

return hc<typeof app>('', { fetch: customFetch })
return hc<typeof app>('http://localhost', { fetch: customFetch })
}
88 changes: 50 additions & 38 deletions deno_dist/jsx/streaming.ts
Original file line number Diff line number Diff line change
@@ -116,48 +116,60 @@ const textEncoder = new TextEncoder()
* The API might be changed.
*/
export const renderToReadableStream = (
str: HtmlEscapedString | Promise<HtmlEscapedString>
str: HtmlEscapedString | Promise<HtmlEscapedString>,
onError: (e: unknown) => void = console.trace
): ReadableStream<Uint8Array> => {
const reader = new ReadableStream<Uint8Array>({
async start(controller) {
const tmp = str instanceof Promise ? await str : await str.toString()
const context = typeof tmp === 'object' ? tmp : {}
const resolved = await resolveCallback(
tmp,
HtmlEscapedCallbackPhase.BeforeStream,
true,
context
)
controller.enqueue(textEncoder.encode(resolved))

let resolvedCount = 0
const callbacks: Promise<void>[] = []
const then = (promise: Promise<string>) => {
callbacks.push(
promise
.catch((err) => {
console.trace(err)
return ''
})
.then(async (res) => {
res = await resolveCallback(res, HtmlEscapedCallbackPhase.BeforeStream, true, context)
;(res as HtmlEscapedString).callbacks
?.map((c) => c({ phase: HtmlEscapedCallbackPhase.Stream, context }))
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.filter<Promise<string>>(Boolean as any)
.forEach(then)
resolvedCount++
controller.enqueue(textEncoder.encode(res))
})
try {
const tmp = str instanceof Promise ? await str : await str.toString()
const context = typeof tmp === 'object' ? tmp : {}
const resolved = await resolveCallback(
tmp,
HtmlEscapedCallbackPhase.BeforeStream,
true,
context
)
}
;(resolved as HtmlEscapedString).callbacks
?.map((c) => c({ phase: HtmlEscapedCallbackPhase.Stream, context }))
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.filter<Promise<string>>(Boolean as any)
.forEach(then)
while (resolvedCount !== callbacks.length) {
await Promise.all(callbacks)
controller.enqueue(textEncoder.encode(resolved))

let resolvedCount = 0
const callbacks: Promise<void>[] = []
const then = (promise: Promise<string>) => {
callbacks.push(
promise
.catch((err) => {
console.log(err)
onError(err)
return ''
})
.then(async (res) => {
res = await resolveCallback(
res,
HtmlEscapedCallbackPhase.BeforeStream,
true,
context
)
;(res as HtmlEscapedString).callbacks
?.map((c) => c({ phase: HtmlEscapedCallbackPhase.Stream, context }))
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.filter<Promise<string>>(Boolean as any)
.forEach(then)
resolvedCount++
controller.enqueue(textEncoder.encode(res))
})
)
}
;(resolved as HtmlEscapedString).callbacks
?.map((c) => c({ phase: HtmlEscapedCallbackPhase.Stream, context }))
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.filter<Promise<string>>(Boolean as any)
.forEach(then)
while (resolvedCount !== callbacks.length) {
await Promise.all(callbacks)
}
} catch (e) {
// maybe the connection was closed
onError(e)
}

controller.close()
2 changes: 1 addition & 1 deletion deno_dist/middleware/serve-static/index.ts
Original file line number Diff line number Diff line change
@@ -68,7 +68,7 @@ export const serveStatic = <E extends Env = Env>(
}

if (content) {
let mimeType: string | undefined = undefined
let mimeType: string | undefined
if (options.mimes) {
mimeType = getMimeType(path, options.mimes) ?? getMimeType(path)
} else {
4 changes: 0 additions & 4 deletions deno_dist/router/trie-router/node.ts
Original file line number Diff line number Diff line change
@@ -44,13 +44,11 @@ export class Node<T> {
const parts = splitRoutingPath(path)

const possibleKeys: string[] = []
const parentPatterns: Pattern[] = []

for (let i = 0, len = parts.length; i < len; i++) {
const p: string = parts[i]

if (Object.keys(curNode.children).includes(p)) {
parentPatterns.push(...curNode.patterns)
curNode = curNode.children[p]
const pattern = getPattern(p)
if (pattern) {
@@ -64,10 +62,8 @@ export class Node<T> {
const pattern = getPattern(p)
if (pattern) {
curNode.patterns.push(pattern)
parentPatterns.push(...curNode.patterns)
possibleKeys.push(pattern[1])
}
parentPatterns.push(...curNode.patterns)
curNode = curNode.children[p]
}

5 changes: 2 additions & 3 deletions deno_dist/utils/jwt/jwt.ts
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ const decodeJwtPart = (part: string): unknown =>

export interface TokenHeader {
alg: SignatureAlgorithm
typ: 'JWT'
typ?: 'JWT'
}

// eslint-disable-next-line
@@ -32,8 +32,7 @@ export function isTokenHeader(obj: any): obj is TokenHeader {
obj !== null &&
'alg' in obj &&
Object.values(AlgorithmTypes).includes(obj.alg) &&
'typ' in obj &&
obj.typ === 'JWT'
(!('typ' in obj) || obj.typ === 'JWT')
)
}

8 changes: 6 additions & 2 deletions deno_dist/validator/validator.ts
Original file line number Diff line number Diff line change
@@ -34,14 +34,18 @@ export const validator = <
V extends {
in: {
[K in U]: K extends 'json'
? InputType
? unknown extends InputType
? OutputTypeExcludeResponseType
: InputType
: { [K2 in keyof OutputTypeExcludeResponseType]: ValidationTargets[K][K2] }
}
out: { [K in U]: OutputTypeExcludeResponseType }
} = {
in: {
[K in U]: K extends 'json'
? InputType
? unknown extends InputType
? OutputTypeExcludeResponseType
: InputType
: { [K2 in keyof OutputTypeExcludeResponseType]: ValidationTargets[K][K2] }
}
out: { [K in U]: OutputTypeExcludeResponseType }
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hono",
"version": "4.2.3",
"version": "4.2.4",
"description": "Ultrafast web framework for the Edges",
"main": "dist/cjs/index.js",
"type": "module",
2 changes: 1 addition & 1 deletion src/adapter/cloudflare-workers/utils.ts
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ export const getContentFromKVAsset = async (
path: string,
options?: KVAssetOptions
): Promise<ArrayBuffer | null> => {
let ASSET_MANIFEST: Record<string, string> = {}
let ASSET_MANIFEST: Record<string, string>

if (options && options.manifest) {
if (typeof options.manifest === 'string') {
1 change: 1 addition & 0 deletions src/adapter/cloudflare-workers/websocket.test.ts
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ describe('upgradeWebSocket middleware', () => {
app.get(
'/ws',
upgradeWebSocket(() => ({
// eslint-disable-next-line @typescript-eslint/no-unused-vars
onMessage(evt, ws) {
console.log('evt')
resolve(evt.data)
3 changes: 1 addition & 2 deletions src/client/client.ts
Original file line number Diff line number Diff line change
@@ -84,7 +84,6 @@ class ClientRequestImpl {
}

let methodUpperCase = this.method.toUpperCase()
let setBody = !(methodUpperCase === 'GET' || methodUpperCase === 'HEAD')

const headerValues: Record<string, string> = {
...(args?.header ?? {}),
@@ -117,7 +116,7 @@ class ClientRequestImpl {
url = url + '?' + this.queryParams.toString()
}
methodUpperCase = this.method.toUpperCase()
setBody = !(methodUpperCase === 'GET' || methodUpperCase === 'HEAD')
const setBody = !(methodUpperCase === 'GET' || methodUpperCase === 'HEAD')

// Pass URL string to 1st arg for testing with MSW and node-fetch
return (opt?.fetch || fetch)(url, {
2 changes: 1 addition & 1 deletion src/helper/ssg/ssg.ts
Original file line number Diff line number Diff line change
@@ -271,7 +271,7 @@ export interface ToSSGAdaptorInterface<
* The API might be changed.
*/
export const toSSG: ToSSGInterface = async (app, fs, options) => {
let result: ToSSGResult | undefined = undefined
let result: ToSSGResult | undefined
const getInfoPromises: Promise<unknown>[] = []
const savePromises: Promise<string | undefined>[] = []
try {
19 changes: 16 additions & 3 deletions src/helper/testing/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
import { Hono } from '../../hono'
import { testClient } from '.'

describe('hono testClinet', () => {
it('should return the correct search result', async () => {
describe('hono testClient', () => {
it('Should return the correct search result', async () => {
const app = new Hono().get('/search', (c) => c.json({ hello: 'world' }))
const res = await testClient(app).search.$get()
expect(await res.json()).toEqual({ hello: 'world' })
})

it('should return the correct environment variables value', async () => {
it('Should return the correct environment variables value', async () => {
type Bindings = { hello: string }
const app = new Hono<{ Bindings: Bindings }>().get('/search', (c) => {
return c.json({ hello: c.env.hello })
})
const res = await testClient(app, { hello: 'world' }).search.$get()
expect(await res.json()).toEqual({ hello: 'world' })
})

it('Should return a correct URL with out throwing an error', async () => {
const app = new Hono().get('/abc', (c) => c.json(0))
const url = testClient(app).abc.$url()
expect(url.pathname).toBe('/abc')
})

it('Should not throw an error with $ws()', async () => {
vi.stubGlobal('WebSocket', class {})
const app = new Hono().get('/ws', (c) => c.text('Fake response of a WebSocket'))
// @ts-expect-error $ws is not typed correctly
expect(() => testClient(app).ws.$ws()).not.toThrowError()
})
})
2 changes: 1 addition & 1 deletion src/helper/testing/index.ts
Original file line number Diff line number Diff line change
@@ -15,5 +15,5 @@ export const testClient = <T extends Hono<any, any, any>>(
return app.request(input, init, Env, executionCtx)
}

return hc<typeof app>('', { fetch: customFetch })
return hc<typeof app>('http://localhost', { fetch: customFetch })
}
55 changes: 54 additions & 1 deletion src/jsx/streaming.test.tsx
Original file line number Diff line number Diff line change
@@ -390,10 +390,12 @@ d.replaceWith(c.content)
return <p>{content}</p>
}

const onError = vi.fn()
const stream = renderToReadableStream(
<Suspense fallback={<p>Loading...</p>}>
<Content />
</Suspense>
</Suspense>,
onError
)

const chunks = []
@@ -402,6 +404,8 @@ d.replaceWith(c.content)
chunks.push(textDecoder.decode(chunk))
}

expect(onError).toBeCalledTimes(1)

expect(chunks).toEqual([
`<template id="H:${suspenseCounter}"></template><p>Loading...</p><!--/$-->`,
'',
@@ -412,6 +416,55 @@ d.replaceWith(c.content)
)
})

it('closed()', async () => {
const Content = async () => {
await new Promise<void>((resolve) =>
setTimeout(() => {
vi.spyOn(ReadableStreamDefaultController.prototype, 'enqueue').mockImplementation(() => {
throw new Error('closed')
})
resolve()
}, 10)
)
return <p>content</p>
}

const onError = vi.fn()
const stream = renderToReadableStream(
<>
<Suspense fallback={<p>Loading...</p>}>
<Content />
</Suspense>
<Suspense fallback={<p>Loading...</p>}>
<Content />
</Suspense>
</>,
onError
)

const chunks = []
const textDecoder = new TextDecoder()
for await (const chunk of stream as any) {
chunks.push(textDecoder.decode(chunk))
}

expect(onError).toBeCalledTimes(1)

expect(chunks).toEqual([
`<template id="H:${suspenseCounter}"></template><p>Loading...</p><!--/$--><template id="H:${
suspenseCounter + 1
}"></template><p>Loading...</p><!--/$-->`,
])

expect(replacementResult(`<html><body>${chunks.join('')}</body></html>`)).toEqual(
'<p>Loading...</p><!--/$--><p>Loading...</p><!--/$-->'
)

suspenseCounter++
await new Promise((resolve) => setTimeout(resolve, 10))
vi.restoreAllMocks()
})

it('Multiple "await" call', async () => {
const delayedContent = new Promise<HtmlEscapedString>((resolve) =>
setTimeout(() => resolve(<h1>Hello</h1>), 10)
Loading