Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(jsx): Add catch to async function's promise #2471

Merged
merged 4 commits into from
Apr 13, 2024

Conversation

mwilkins91
Copy link
Contributor

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.

The problem:

If you're streaming a response, via renderToReadableStream from hono/jsx/streaming, and you rapidly refresh your page, you can trigger an error:

TypeError: Invalid state: Controller is already closed
    at new NodeError (node:internal/errors:405:5)
    at ReadableStreamDefaultController.enqueue (node:internal/webstreams/readablestream:1038:13)
    at /Users/markwilkins/projects/wilkins-software-nx-v2/node_modules/hono/dist/cjs/jsx/streaming.js:137:24
    at processTicksAndRejections (node:internal/process/task_queues:95:5)

which is uncatchable as far as I can tell.

Apr-05-2024 21-53-51

The fix:

Since the catch block above is applied to the promise variable, but not to the promise returned by the async function in the .then(), the call to enqueue on line 150 can fail due to a closed connection.

Fixing it is as simple as adding another .catch, as funny as it looks at first glance.

Apr-05-2024 21-56-58

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.
@usualoma
Copy link
Member

usualoma commented Apr 6, 2024

Hi @mwilkins91

Thanks for making the pull request. There are indeed some issues you point out, which I think will be fixed in this PR. Please wait a bit so I can look at the whole thing and comment.

@mwilkins91
Copy link
Contributor Author

@usualoma Thanks! Let me know if I can help.

@usualoma
Copy link
Member

usualoma commented Apr 7, 2024

Hi @mwilkins91 !

I consider the following

Given these assumptions, I would suggest the following patches to this matter. If you have no objection, I would be happy to have this change incorporated into the PR.

diff --git a/src/jsx/streaming.test.tsx b/src/jsx/streaming.test.tsx
index 0c9ba6e1..c5cd5579 100644
--- a/src/jsx/streaming.test.tsx
+++ b/src/jsx/streaming.test.tsx
@@ -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)
diff --git a/src/jsx/streaming.ts b/src/jsx/streaming.ts
index fac77193..5df7a3e4 100644
--- a/src/jsx/streaming.ts
+++ b/src/jsx/streaming.ts
@@ -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()

@mwilkins91
Copy link
Contributor Author

That seems to make sense to me @usualoma! When you say If you have no objection, I would be happy to have this change incorporated into the PR. do you mean that you'll make that change? or would you like me to make it?

@usualoma
Copy link
Member

Hi @mwilkins91!

Thanks for the confirmation.
I am thinking that you can have the content of this PR changed to the diff I made above and add it as your commits.
We can have @yusukebe review it and merge it into the main branch.

@yusukebe
Copy link
Member

Yes.

@mwilkins91, can you apply the diff to this PR? If it's done, I'll merge it into main with @usualoma as a co-author.

@mwilkins91
Copy link
Contributor Author

Sounds perfect, I'll do that tonight!

@mwilkins91
Copy link
Contributor Author

@yusukebe done!

@yusukebe yusukebe changed the title Add catch to async function's promise fix(jsx): Add catch to async function's promise Apr 13, 2024
@yusukebe
Copy link
Member

@mwilkins91 Thanks!

One thing. Can you run bun run denoify or yarn denoify and git add&commit&push the generated file.

@mwilkins91
Copy link
Contributor Author

@yusukebe like so? 3ec79b3

@yusukebe
Copy link
Member

@mwilkins91 Yup. That's right!

I'll merge this now. Thanks!

@yusukebe yusukebe merged commit 7386a4a into honojs:main Apr 13, 2024
10 checks passed
nicolewhite pushed a commit to autoblocksai/cli that referenced this pull request Apr 24, 2024
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [hono](https://hono.dev/) ([source](https://togithub.com/honojs/hono))
| [`4.1.4` ->
`4.2.7`](https://renovatebot.com/diffs/npm/hono/4.1.4/4.2.7) |
[![age](https://developer.mend.io/api/mc/badges/age/npm/hono/4.2.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/hono/4.2.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/hono/4.1.4/4.2.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/hono/4.1.4/4.2.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

### GitHub Vulnerability Alerts

####
[CVE-2024-32869](https://togithub.com/honojs/hono/security/advisories/GHSA-3mpf-rcc7-5347)

### Summary

When using serveStatic with deno, it is possible to directory traverse
where main.ts is located.

My environment is configured as per this tutorial
https://hono.dev/getting-started/deno

### PoC

```bash
$ tree
.
├── deno.json
├── deno.lock
├── main.ts
├── README.md
└── static
    └── a.txt
```

source

```jsx
import { Hono } from 'https://deno.land/x/hono@v4.2.6/mod.ts'
import { serveStatic } from 'https://deno.land/x/hono@v4.2.6/middleware.ts'

const app = new Hono()
app.use('/static/*', serveStatic({ root: './' }))

Deno.serve(app.fetch)
```

request

```bash
curl localhost:8000/static/%2e%2e/main.ts
```

response is content of main.ts

### Impact

Unexpected files are retrieved.

---

### Release Notes

<details>
<summary>honojs/hono (hono)</summary>

### [`v4.2.7`](https://togithub.com/honojs/hono/releases/tag/v4.2.7)

[Compare
Source](https://togithub.com/honojs/hono/compare/v4.2.6...v4.2.7)

This release fixes "[Restricted Directory Traversal in serveStatic with
deno](https://togithub.com/honojs/hono/security/advisories/GHSA-3mpf-rcc7-5347)".

**Full Changelog**:
honojs/hono@v4.2.6...v4.2.7

### [`v4.2.6`](https://togithub.com/honojs/hono/releases/tag/v4.2.6)

[Compare
Source](https://togithub.com/honojs/hono/compare/v4.2.5...v4.2.6)

#### What's Changed

- refactor(adapter/aws): Optimize multiple call of same conditions with
polymorphism by [@&#8203;exoego](https://togithub.com/exoego) in
[honojs/hono#2521
- fix(sse): close sse stream on end by
[@&#8203;domeccleston](https://togithub.com/domeccleston) in
[honojs/hono#2529
- fix(client): Don't show `$ws` when not used WebSockets by
[@&#8203;nakasyou](https://togithub.com/nakasyou) in
[honojs/hono#2532
- refactor(ssg): update utils.ts by
[@&#8203;eltociear](https://togithub.com/eltociear) in
[honojs/hono#2519

#### New Contributors

- [@&#8203;domeccleston](https://togithub.com/domeccleston) made their
first contribution in
[honojs/hono#2529
- [@&#8203;eltociear](https://togithub.com/eltociear) made their first
contribution in
[honojs/hono#2519

**Full Changelog**:
honojs/hono@v4.2.5...v4.2.6

### [`v4.2.5`](https://togithub.com/honojs/hono/releases/tag/v4.2.5)

[Compare
Source](https://togithub.com/honojs/hono/compare/v4.2.4...v4.2.5)

#### What's Changed

- fix(client): Allow calling toString and valueOf on the proxy object by
[@&#8203;ibash](https://togithub.com/ibash) in
[honojs/hono#2510
- fix(adapter): handle multi value headers in AWS Lambda by
[@&#8203;exoego](https://togithub.com/exoego) in
[honojs/hono#2494
- fix(client): shuold not remove tailing slash from top-level URL by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2523
- fix(jsx/dom): remove lookbehind assertion in event regexp by
[@&#8203;usualoma](https://togithub.com/usualoma) in
[honojs/hono#2524

#### New Contributors

- [@&#8203;ibash](https://togithub.com/ibash) made their first
contribution in
[honojs/hono#2510

**Full Changelog**:
honojs/hono@v4.2.4...v4.2.5

### [`v4.2.4`](https://togithub.com/honojs/hono/releases/tag/v4.2.4)

[Compare
Source](https://togithub.com/honojs/hono/compare/v4.2.3...v4.2.4)

##### What's Changed

- fix(jwt): Make JWT Header `typ` Field Optional to Enhance
Compatibility by [@&#8203;naporin0624](https://togithub.com/naporin0624)
in
[honojs/hono#2488
- fix(testing): set `baseUrl` for `testClient` by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2496
- fix(validator): Default use to `OutputTypeExcludeResponseType` when
`InputType` is unknown by
[@&#8203;nagasawaryoya](https://togithub.com/nagasawaryoya) in
[honojs/hono#2500
- refactor(trie-router): parentPatterns is updated but never queried by
[@&#8203;exoego](https://togithub.com/exoego) in
[honojs/hono#2503
- refactor: Remove redundant initializer by
[@&#8203;exoego](https://togithub.com/exoego) in
[honojs/hono#2502
- refactor(cloudflare-workers): Suppress eslint noise by
[@&#8203;exoego](https://togithub.com/exoego) in
[honojs/hono#2504
- fix(jsx): Add catch to async function's promise by
[@&#8203;mwilkins91](https://togithub.com/mwilkins91) in
[honojs/hono#2471

##### New Contributors

- [@&#8203;nagasawaryoya](https://togithub.com/nagasawaryoya) made their
first contribution in
[honojs/hono#2500
- [@&#8203;exoego](https://togithub.com/exoego) made their first
contribution in
[honojs/hono#2503
- [@&#8203;mwilkins91](https://togithub.com/mwilkins91) made their first
contribution in
[honojs/hono#2471

**Full Changelog**:
honojs/hono@v4.2.3...v4.2.4

### [`v4.2.3`](https://togithub.com/honojs/hono/releases/tag/v4.2.3)

[Compare
Source](https://togithub.com/honojs/hono/compare/v4.2.2...v4.2.3)

#### What's Changed

- fix(ssg): use response header to mark as disabled routes for SSG by
[@&#8203;usualoma](https://togithub.com/usualoma) in
[honojs/hono#2477
- fix(trailing-slash): export types in `package.json` correctly by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2483
- fix(client): fix websocket client protocol by
[@&#8203;naporin0624](https://togithub.com/naporin0624) in
[honojs/hono#2479

**Full Changelog**:
honojs/hono@v4.2.2...v4.2.3

### [`v4.2.2`](https://togithub.com/honojs/hono/releases/tag/v4.2.2)

[Compare
Source](https://togithub.com/honojs/hono/compare/v4.2.1...v4.2.2)

#### What's Changed

- feat(jsx-renderer): pass the context as 2nd arg by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2459
- feat(client): accept a function that provides dynamic headers to hc by
[@&#8203;niko-gardenanet](https://togithub.com/niko-gardenanet) in
[honojs/hono#2461
- fix(client): infer `null` correctly by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2469

#### New Contributors

- [@&#8203;niko-gardenanet](https://togithub.com/niko-gardenanet) made
their first contribution in
[honojs/hono#2461

**Full Changelog**:
honojs/hono@v4.2.1...v4.2.2

### [`v4.2.1`](https://togithub.com/honojs/hono/releases/tag/v4.2.1)

[Compare
Source](https://togithub.com/honojs/hono/compare/v4.2.0...v4.2.1)

#### What's Changed

- fix(jws): Only import necessary helper (not all helpers) by
[@&#8203;nicksrandall](https://togithub.com/nicksrandall) in
[honojs/hono#2458

#### New Contributors

- [@&#8203;nicksrandall](https://togithub.com/nicksrandall) made their
first contribution in
[honojs/hono#2458

**Full Changelog**:
honojs/hono@v4.2.0...v4.2.1

### [`v4.2.0`](https://togithub.com/honojs/hono/releases/tag/v4.2.0)

[Compare
Source](https://togithub.com/honojs/hono/compare/v4.1.7...v4.2.0)

Hono v4.2.0 is now available! Let's take a look at the new features.

#### Added more algorithms for JWT

The number of algorithms that JWT util can handle has increased from
only 3 to 13! This means that JWT util now implements many of the
algorithms supported by JWT.

-   HS256
-   HS384
-   HS512
-   RS256
-   RS384
-   RS512
-   PS256
-   PS384
-   PS512
-   ES256
-   ES384
-   ES512
-   EdDSA

You can use these algorithms from the JWT middleware or JWT helpers.
Thanks [@&#8203;Code-Hex](https://togithub.com/Code-Hex)!

#### Method Override Middleware

[Method Override
Middleware](https://hono.dev/middleware/builtin/method-override) has
been added. This middleware override the method of the real request with
the specified method.

HTML `form` does not allow you to send a DELETE method request. Instead,
by sending an input with `name` as `_method` and a value of `DELETE`,
you can call the handler registered in `app.delete()`.

```ts
const app = new Hono()

// If no options are specified, the value of `_method` in the form,
// e.g. DELETE, is used as the method.
app.use('/posts', methodOverride({ app }))

app.delete('/posts', (c) => {
  // ....
})
```

#### Trailing Slash Middleware

[Trailing Slash
Middleware](https://hono.dev/middleware/builtin/trailing-slash) resolves
the handling of Trailing Slashes in GET requests. You can use
`appendTrailingSlash` and `trimTrailingSlash` functions.

For example, it redirects a GET request to `/about/me` to `/about/me/`.

```ts
import { Hono } from 'hono'
import { appendTrailingSlash } from 'hono/trailing-slash'

const app = new Hono({ strict: true })

app.use(appendTrailingSlash())
app.get('/about/me/', (c) => c.text('With Trailing Slash'))
```

Thanks [@&#8203;rnmeow](https://togithub.com/rnmeow)!

#### Other features

- SSG Helper - Support `extensionMap`
[honojs/hono#2382
- JSX/DOM - Add `userId` hook
[honojs/hono#2389
- JWT Middleware - Improve error handling
[honojs/hono#2406
- Request - Cache the body for re-using
[honojs/hono#2416
- JWT Util - Add type helper to `payload`
[honojs/hono#2424
- CORS Middleware - Pass context to `options.origin` function
[honojs/hono#2436
- Cache Middleware - Support for the `vary` header option
[honojs/hono#2426
- HTTP Exception - Add `cause` option
[honojs/hono#2224
- Logger - Support `NO_COLOR`
[honojs/hono#2228
- JWT Middleware - Add `JwtTokenInvalid` object as `cause` when JWT is
invalid
[honojs/hono#2448
- Bearer Auth Middleware - Add `verifyToken` option
[honojs/hono#2449
- Basic Auth Middleware - Add `verifyUser` option
[honojs/hono#2450

#### All Updates

- feat(jwt): supported RS256, RS384, RS512 algorithm for JWT by
[@&#8203;Code-Hex](https://togithub.com/Code-Hex) in
[honojs/hono#2339
- added remain algorithm for JWT by
[@&#8203;Code-Hex](https://togithub.com/Code-Hex) in
[honojs/hono#2352
- acceptable CryptoKey in JWT sign and verify by
[@&#8203;Code-Hex](https://togithub.com/Code-Hex) in
[honojs/hono#2373
- feat(ssg): Support `extentionMap` by
[@&#8203;watany-dev](https://togithub.com/watany-dev) in
[honojs/hono#2382
- feat(jwt): support remaining algorithms by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2368
- feat(jsx): add useId hook by
[@&#8203;usualoma](https://togithub.com/usualoma) in
[honojs/hono#2389
- feat(middleware/jwt): improve error handling by
[@&#8203;tfkhdyt](https://togithub.com/tfkhdyt) in
[honojs/hono#2406
- feat(request): cache body for reusing by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2416
- feat(jwt): Add type helper to `payload` by
[@&#8203;nakasyou](https://togithub.com/nakasyou) in
[honojs/hono#2424
- feat: introduce Method Override Middleware by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2420
- feat(middleware/cors): pass context to options.origin function by
[@&#8203;okmr-d](https://togithub.com/okmr-d) in
[honojs/hono#2436
- feat: support for `vary` header in cache middleware by
[@&#8203;naporin0624](https://togithub.com/naporin0624) in
[honojs/hono#2426
- feat: add middlewares resolve trailing slashes on GET request by
[@&#8203;rnmeow](https://togithub.com/rnmeow) in
[honojs/hono#2408
- test: stub `crypto` if not exist by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2445
- feat(jwt): literal typed `alg` option value by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2446
- test(ssg): add test for content-type includes `;` by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2447
- feat(jwt): add `JwtTokenInvalid` object as `cause` when JWT is invalid
by [@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2448
- feat(bearer-auth): add `verifyToken` option by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2449
- feat(basic-auth): add `verifyUser` option by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2450
- Next by [@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2454

#### New Contributors

- [@&#8203;tfkhdyt](https://togithub.com/tfkhdyt) made their first
contribution in
[honojs/hono#2406
- [@&#8203;okmr-d](https://togithub.com/okmr-d) made their first
contribution in
[honojs/hono#2436
- [@&#8203;naporin0624](https://togithub.com/naporin0624) made their
first contribution in
[honojs/hono#2426
- [@&#8203;rnmeow](https://togithub.com/rnmeow) made their first
contribution in
[honojs/hono#2408

**Full Changelog**:
honojs/hono@v4.1.7...v4.2.0

### [`v4.1.7`](https://togithub.com/honojs/hono/releases/tag/v4.1.7)

[Compare
Source](https://togithub.com/honojs/hono/compare/v4.1.6...v4.1.7)

#### What's Changed

- fix(cache): check `globalThis.caches` by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2444

**Full Changelog**:
honojs/hono@v4.1.6...v4.1.7

### [`v4.1.6`](https://togithub.com/honojs/hono/releases/tag/v4.1.6)

[Compare
Source](https://togithub.com/honojs/hono/compare/v4.1.5...v4.1.6)

#### What's Changed

- chore(benchmark): add "loop" script by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2431
- fix(cache): not enabled if `caches` is not defined by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2443

**Full Changelog**:
honojs/hono@v4.1.5...v4.1.6

### [`v4.1.5`](https://togithub.com/honojs/hono/releases/tag/v4.1.5)

[Compare
Source](https://togithub.com/honojs/hono/compare/v4.1.4...v4.1.5)

#### What's Changed

- perf: Don't use `Arrap.prototype.map` if it is not needed return value
by [@&#8203;nakasyou](https://togithub.com/nakasyou) in
[honojs/hono#2419
- fix(aws-lambda): handle response without body
([#&#8203;2401](https://togithub.com/honojs/hono/issues/2401)) by
[@&#8203;KnisterPeter](https://togithub.com/KnisterPeter) in
[honojs/hono#2413
- fix(validator): `await` cached contents by
[@&#8203;yusukebe](https://togithub.com/yusukebe) in
[honojs/hono#2430

#### New Contributors

- [@&#8203;KnisterPeter](https://togithub.com/KnisterPeter) made their
first contribution in
[honojs/hono#2413

**Full Changelog**:
honojs/hono@v4.1.4...v4.1.5

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "" in timezone America/Chicago,
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/autoblocksai/cli).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zMTMuMSIsInVwZGF0ZWRJblZlciI6IjM3LjMxMy4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants