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

v4.6 #482

Merged
merged 9 commits into from
Sep 11, 2024
1 change: 1 addition & 0 deletions .vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ const sidebars = (): DefaultTheme.SidebarItem[] => [
{ text: 'Cache', link: '/docs/middleware/builtin/cache' },
{ text: 'Combine', link: '/docs/middleware/builtin/combine' },
{ text: 'Compress', link: '/docs/middleware/builtin/compress' },
{ text: 'Context Storage', link: '/docs/middleware/builtin/context-storage' },
{ text: 'CORS', link: '/docs/middleware/builtin/cors' },
{
text: 'CSRF Protection',
Expand Down
29 changes: 29 additions & 0 deletions docs/getting-started/bun.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,22 @@ app.get(
)
```

### `onFound`

You can specify handling when the requested file is found with `onFound`:

```ts
app.get(
'/static/*',
serveStatic({
// ...
onFound: (_path, c) => {
c.header('Cache-Control', `public, immutable, max-age=31536000`)
},
})
)
```

### `onNotFound`

You can specify handling when the requested file is not found with `onNotFound`:
Expand All @@ -150,6 +166,19 @@ app.get(
)
```

### `precompressed`

The `precompressed` option checks if files with extensions like `.br` or `.gz` are available and serves them based on the `Accept-Encoding` header. It prioritizes Brotli, then Zstd, and Gzip. If none are available, it serves the original file.

```ts
app.get(
'/static/*',
serveStatic({
precompressed: true,
})
)
```

## Testing

You can use `bun:test` for testing on Bun.
Expand Down
42 changes: 42 additions & 0 deletions docs/getting-started/cloudflare-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,45 @@ export const onRequest = [
handleMiddleware(middleware3),
]
```

### Accessing `EventContext`

You can access [`EventContext`](https://developers.cloudflare.com/pages/functions/api-reference/#eventcontext) object via `c.env` in `handleMiddleware`.

```ts
// functions/_middleware.ts
import { handleMiddleware } from 'hono/cloudflare-pages'

export const onRequest = [
handleMiddleware(async (c, next) => {
c.env.eventContext.data.user = 'Joe'
await next()
}),
]
```

Then, you can access the data value in via `c.env.eventContext` in the handler:

```ts
// functions/api/[[route]].ts
import type { EventContext } from 'hono/cloudflare-pages'
import { handle } from 'hono/cloudflare-pages'

// ...

type Env = {
Bindings: {
eventContext: EventContext
}
}

const app = new Hono<Env>()

app.get('/hello', (c) => {
return c.json({
message: `Hello, ${c.env.eventContext.data.user}!`, // 'Joe'
})
})

export const onRequest = handle(app)
```
29 changes: 29 additions & 0 deletions docs/getting-started/deno.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,22 @@ app.get(
)
```

### `onFound`

You can specify handling when the requested file is found with `onFound`:

```ts
app.get(
'/static/*',
serveStatic({
// ...
onFound: (_path, c) => {
c.header('Cache-Control', `public, immutable, max-age=31536000`)
},
})
)
```

### `onNotFound`

You can specify handling when the requested file is not found with `onNotFound`:
Expand All @@ -137,6 +153,19 @@ app.get(
)
```

### `precompressed`

The `precompressed` option checks if files with extensions like `.br` or `.gz` are available and serves them based on the `Accept-Encoding` header. It prioritizes Brotli, then Zstd, and Gzip. If none are available, it serves the original file.

```ts
app.get(
'/static/*',
serveStatic({
precompressed: true,
})
)
```

## Deno Deploy

Deno Deploy is an edge runtime platform for Deno.
Expand Down
4 changes: 3 additions & 1 deletion docs/helpers/websocket.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import { upgradeWebSocket } from 'hono/deno'
```ts [Bun]
import { Hono } from 'hono'
import { createBunWebSocket } from 'hono/bun'
import type { ServerWebSocket } from 'bun'

const { upgradeWebSocket, websocket } = createBunWebSocket()
const { upgradeWebSocket, websocket } =
createBunWebSocket<ServerWebSocket>()

// ...

Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ Out of the box, Hono provides middleware and helpers for:
- [Body Limit](/docs/middleware/builtin/body-limit)
- [Cache](/docs/middleware/builtin/cache)
- [Compress](/docs/middleware/builtin/compress)
- [Context Storage](/docs/middleware/builtin/context-storage)
- [Cookie](/docs/helpers/cookie)
- [CORS](/docs/middleware/builtin/cors)
- [ETag](/docs/middleware/builtin/etag)
Expand Down
4 changes: 4 additions & 0 deletions docs/middleware/builtin/basic-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ A function to handle hashing for safe comparison of passwords.

The function to verify the user.

### <Badge type="info" text="optional" /> invalidUserMessage: `string | object | MessageFunction`

`MessageFunction` is `(c: Context) => string | object | Promise<string | object>`. The custom message if the user is invalid.

## More Options

### <Badge type="info" text="optional" /> ...users: `{ username: string, password: string }[]`
Expand Down
12 changes: 12 additions & 0 deletions docs/middleware/builtin/bearer-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,15 @@ A function to handle hashing for safe comparison of authentication tokens.
### <Badge type="info" text="optional" /> verifyToken: `(token: string, c: Context) => boolean | Promise<boolean>`

The function to verify the token.

### <Badge type="info" text="optional" /> noAuthenticationHeaderMessage: `string | object | MessageFunction`

`MessageFunction` is `(c: Context) => string | object | Promise<string | object>`. The custom message if it does not have an authentication header.

### <Badge type="info" text="optional" /> invalidAuthenticationHeaderMessage: `string | object | MessageFunction`

The custom message if the authentication header is invalid.

### <Badge type="info" text="optional" /> invalidTokenMessage: `string | object | MessageFunction`

The custom message if the token is invalid.
64 changes: 64 additions & 0 deletions docs/middleware/builtin/context-storage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Context Storage Middleware

The Context Storage Middleware stores the Hono `Context` in the `AsyncLocalStorage`, to make it globally accessible.

::: info
**Note** This middleware uses `AsyncLocalStorage`. The runtime should support it.

**Cloudflare Workers**: To enable `AsyncLocalStorage`, add the [`nodejs_compat` or `nodejs_als` flag](https://developers.cloudflare.com/workers/configuration/compatibility-dates/#nodejs-compatibility-flag) to your `wrangler.toml` file.
:::

## Import

```ts
import { Hono } from 'hono'
import { contextStorage, getContext } from 'hono/context-storage'
```

## Usage

The `getContext()` will return the current Context object if the `contextStorage()` is applied as a middleware.

```ts
type Env = {
Variables: {
message: string
}
}

const app = new Hono<Env>()

app.use(contextStorage())

app.use(async (c, next) => {
c.set('message', 'Hello!')
await next()
})

// You can access the variable outside the handler.
const getMessage = () => {
return getContext<Env>().var.message
}

app.get('/', (c) => {
return c.text(getMessage())
})
```

On Cloudflare Workers, you can access the bindings outside the handler.

```ts
type Env = {
Bindings: {
KV: KVNamespace
}
}

const app = new Hono<Env>()

app.use(contextStorage())

const setKV = (value: string) => {
return getContext<Env>().env.KV.put('key', value)
}
```
3 changes: 2 additions & 1 deletion docs/middleware/builtin/jsx-renderer.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ If `true` is set, the following headers are added:
```ts
{
'Transfer-Encoding': 'chunked',
'Content-Type': 'text/html; charset=UTF-8'
'Content-Type': 'text/html; charset=UTF-8',
'Content-Encoding': 'Identity'
}
```

Expand Down
30 changes: 30 additions & 0 deletions docs/middleware/builtin/secure-headers.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Each option corresponds to the following Header Key-Value pairs.
| xFrameOptions | [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) | SAMEORIGIN | True |
| xPermittedCrossDomainPolicies | [X-Permitted-Cross-Domain-Policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Permitted-Cross-Domain-Policies) | none | True |
| xXssProtection | [X-XSS-Protection](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection) | 0 | True |
| permissionPolicy | [Permissions-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy) | Usage: [Setting Permission-Policy](#setting-permission-policy) | No Setting |

## Middleware Conflict

Expand Down Expand Up @@ -211,3 +212,32 @@ app.get('/', (c) => {
)
})
```

## Setting Permission-Policy

The Permission-Policy header allows you to control which features and APIs can be used in the browser. Here's an example of how to set it:

```ts
const app = new Hono()
app.use(
'*',
secureHeaders({
permissionsPolicy: {
fullscreen: ['self'], // fullscreen=(self)
bluetooth: ['none'], // bluetooth=(none)
payment: ['self', 'https://example.com'], // payment=(self "https://example.com")
syncXhr: [], // sync-xhr=()
camera: false, // camera=none
microphone: true, // microphone=*
geolocation: ['*'], // geolocation=*
usb: ['self', 'https://a.example.com', 'https://b.example.com'], // usb=(self "https://a.example.com" "https://b.example.com")
accelerometer: ['https://*.example.com'], // accelerometer=("https://*.example.com")
gyroscope: ['src'], // gyroscope=(src)
magnetometer: [
'https://a.example.com',
'https://b.example.com',
], // magnetometer=("https://a.example.com" "https://b.example.com")
},
})
)
```