Compare commits

...

27 Commits

Author SHA1 Message Date
github-actions[bot] 0dc6a1f5e6
Version Packages (#1325)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-21 15:04:24 +09:00
James Talmage d89fed7eec
feat(auth-js): Support async ConfigHandler (#1324)
* feat(auth-js): Support async ConfigHandler

* add changeset

* format
2025-07-21 14:55:46 +09:00
github-actions[bot] b24925168a
Version Packages (#1323)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-20 08:36:17 +09:00
Andrei 2a46bfbba0
fix(standard-validator): arktype leaking headers (#1282)
* fix(standard-validator): arktype strip headers from error output

* chore(standard-validator): fix formatting

* feat(changeset): add changeset

* fix: change header schema to lowercase and cleanup tests

* fix: strip out sensitive fields for valibot

* chore: refactor, cleanup and add jsdoc
2025-07-20 08:30:09 +09:00
github-actions[bot] 28601b311b
Version Packages (#1321)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-19 18:53:02 +09:00
Yusuke Wada 5a3cbc4cc6
chore: fix the changeset (#1322) 2025-07-19 18:47:26 +09:00
Yusuke Wada e835cf6183
chore: add changeset (#1320) 2025-07-19 18:26:04 +09:00
Yusuke Wada 2339eefa1c
fix(zod-openapi): correct importing `zod` (#1319) 2025-07-19 18:03:34 +09:00
github-actions[bot] 7d82875634
Version Packages (#1317)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-19 10:26:47 +09:00
Yusuke Wada c6a16ab7aa
fix(zod-validator): support transform (#1315)
* fix(zod-validator): support transform

* polish
2025-07-19 10:20:40 +09:00
github-actions[bot] 1a564d5fb6
Version Packages (#1313)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-18 17:00:12 +09:00
Yusuke Wada 9216d2a666
ci: specify the deno version `2.3.7` for dry-run (#1314) 2025-07-18 16:55:45 +09:00
Yusuke Wada 845e336ff4
feat(zod-openapi): support Zod v4 (#1223)
* feat(zod-openapi): support Zod v4

* update `peerDependencies`

* add changeset

* fixed type error

* update dependencies
2025-07-18 16:40:21 +09:00
github-actions[bot] 2a589b0728
Version Packages (#1310)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-16 12:53:35 +09:00
github-actions[bot] 47cb4c8569
chore(ua-blocker): update robots.json from upstream (#1309)
Co-authored-by: yusukebe <10682+yusukebe@users.noreply.github.com>
2025-07-16 12:16:43 +09:00
github-actions[bot] 3705fe6560
Version Packages (#1305)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-13 20:10:52 +09:00
kiki-kanri 9f6278f51c
chore(zod-validator): upgrade zod to v4 and update import style in README.md (#1302)
* chore(zod-validator): upgrade zod to v4

* docs(zod-validator): update Zod import style to be more tree-shakeable in README.md

* chore(zod-validator): commit .changeset

* fix(zod-validator): correctly set the zod version to follow the official website doc

* chore(zod-validator): change `.changeset` to patch cause it does not include a new feature
2025-07-13 19:57:55 +09:00
github-actions[bot] 55e4b19a50
Version Packages (#1304)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-13 17:16:43 +09:00
Yusuke Wada 68cbb31af4
feat(session): enable accessing sid via `c.var.session.id` (#1272)
* feat(session): enable accessing sid via `c.var.session.id`

* changeset
2025-07-13 17:12:05 +09:00
github-actions[bot] b5186da328
Version Packages (#1303)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-11 18:27:58 +09:00
Jonathan Haines 037fd94b7b
fix(session): types for SessionOptions cookie methods (#1301) 2025-07-11 18:14:09 +09:00
github-actions[bot] 2498ccab03
Version Packages (#1299)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-11 08:28:49 +09:00
Jonathan Haines 6e53f6e8f3
test: vitest include (#1297) 2025-07-11 08:28:28 +09:00
github-actions[bot] 18dec85b78
chore(ua-blocker): update robots.json from upstream (#1298)
Co-authored-by: yusukebe <10682+yusukebe@users.noreply.github.com>
2025-07-11 08:19:58 +09:00
github-actions[bot] 4e5f592359
Version Packages (#1296)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-10 07:27:38 +09:00
Bryce Kalow 0f472345c7
chore(clerk-auth): Update clerk dependencies (#1295) 2025-07-10 07:07:29 +09:00
Jonathan Haines 0a82c1faed
test: add `config.test.dir` (#1289)
* test: add `config.test.dir`

* test: fix unplugin-typia tsconfig
2025-07-07 17:59:56 +09:00
79 changed files with 650 additions and 82 deletions

View File

@ -54,7 +54,7 @@ jobs:
- uses: denoland/setup-deno@v2
with:
cache: true
deno-version: v2.x
deno-version: v2.3.7
- run: deno install --no-lock
- run: deno publish --dry-run

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -1,5 +1,11 @@
# @hono/auth-js
## 1.1.0
### Minor Changes
- [#1324](https://github.com/honojs/middleware/pull/1324) [`d89fed7eecfa151c18b0a8cf95dae1dfe83dfec2`](https://github.com/honojs/middleware/commit/d89fed7eecfa151c18b0a8cf95dae1dfe83dfec2) Thanks [@jamestalmage](https://github.com/jamestalmage)! - Allow async authjs Config
## 1.0.17
### Patch Changes

View File

@ -1,6 +1,6 @@
{
"name": "@hono/auth-js",
"version": "1.0.17",
"version": "1.1.0",
"license": "MIT",
"exports": {
".": "./src/index.ts",

View File

@ -1,6 +1,6 @@
{
"name": "@hono/auth-js",
"version": "1.0.17",
"version": "1.1.0",
"description": "A third-party Auth js middleware for Hono",
"main": "dist/index.js",
"type": "module",

View File

@ -47,6 +47,27 @@ describe('Config', () => {
expect(res.status).toBe(200)
})
it('Should allow async ConfigHandler', async () => {
globalThis.process.env = { AUTH_SECRET: 'secret' }
const app = new Hono()
app.use(
'/*',
initAuthConfig(async () => {
await new Promise((resolve) => setTimeout(resolve, 1))
return {
basePath: '/api/auth',
providers: [],
}
})
)
app.use('/api/auth/*', authHandler())
const req = new Request('http://localhost/api/auth/signin')
const res = await app.request(req)
expect(res.status).toBe(200)
})
it('Should return 401 is if auth cookie is invalid or missing', async () => {
const app = new Hono()

View File

@ -29,7 +29,7 @@ export type AuthUser = {
export interface AuthConfig extends Omit<AuthConfigCore, 'raw'> {}
export type ConfigHandler = (c: Context) => AuthConfig
export type ConfigHandler = (c: Context) => AuthConfig | Promise<AuthConfig>
export function setEnvDefaults(env: AuthEnv, config: AuthConfig): void {
config.secret ??= env.AUTH_SECRET
@ -118,7 +118,7 @@ export function verifyAuth(): MiddlewareHandler {
export function initAuthConfig(cb: ConfigHandler): MiddlewareHandler {
return async (c, next) => {
const config = cb(c)
const config = await cb(c)
c.set('authConfig', config)
await next()
}

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,6 +3,7 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
// See https://github.com/vitest-dev/vitest/issues/5277
pool: 'forks',
setupFiles: ['./vitest.setup.ts'],

View File

@ -3,6 +3,7 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
typecheck: {
tsconfig: './tsconfig.json',
enabled: true,

View File

@ -1,5 +1,11 @@
# @hono/clerk-auth
## 3.0.2
### Patch Changes
- [#1295](https://github.com/honojs/middleware/pull/1295) [`0f472345c737ed054bf26c2e0ab13d71dc0fcbcd`](https://github.com/honojs/middleware/commit/0f472345c737ed054bf26c2e0ab13d71dc0fcbcd) Thanks [@brkalow](https://github.com/brkalow)! - Upgrade clerk dependencies.
## 3.0.1
### Patch Changes

View File

@ -1,6 +1,6 @@
{
"name": "@hono/clerk-auth",
"version": "3.0.1",
"version": "3.0.2",
"license": "MIT",
"exports": {
".": "./src/index.ts"

View File

@ -1,6 +1,6 @@
{
"name": "@hono/clerk-auth",
"version": "3.0.1",
"version": "3.0.2",
"description": "A third-party Clerk auth middleware for Hono",
"type": "module",
"main": "dist/index.cjs",
@ -41,8 +41,8 @@
},
"homepage": "https://github.com/honojs/middleware",
"dependencies": {
"@clerk/backend": "^2.2.0",
"@clerk/types": "^4.61.0"
"@clerk/backend": "^2.4.1",
"@clerk/types": "^4.64.0"
},
"peerDependencies": {
"hono": ">=3.0.0"

View File

@ -3,6 +3,7 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
restoreMocks: true,
unstubEnvs: true,
},

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -0,0 +1,8 @@
import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -29,8 +29,5 @@
"eslint-plugin-import-x": "^4.1.1",
"eslint-plugin-n": "^17.10.2",
"typescript-eslint": "^8.27.0"
},
"devDependencies": {
"hono": "^4.8.4"
}
}

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -23,6 +23,7 @@ const firebasePlugin = {
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
plugins: [firebasePlugin],

View File

@ -1,9 +1,9 @@
import { defaultExclude, defineProject } from 'vitest/config'
import { defineProject } from 'vitest/config'
export default defineProject({
test: {
exclude: [...defaultExclude, 'bun_test'],
globals: true,
include: ['src/**/*.test.ts'],
restoreMocks: true,
unstubEnvs: true,
},

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,6 +3,7 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
restoreMocks: true,
unstubEnvs: true,
},

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,6 +3,7 @@ import { defineWorkersProject } from '@cloudflare/vitest-pool-workers/config'
export default defineWorkersProject({
test: {
globals: true,
include: ['src/**/*.test.ts?(x)'],
poolOptions: {
workers: {
miniflare: {

View File

@ -3,6 +3,7 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
restoreMocks: true,
unstubEnvs: true,
},

View File

@ -1,5 +1,17 @@
# @hono/session
## 0.2.0
### Minor Changes
- [#1272](https://github.com/honojs/middleware/pull/1272) [`68cbb31af43fd5916173f9f6b07c8cedb3dbe706`](https://github.com/honojs/middleware/commit/68cbb31af43fd5916173f9f6b07c8cedb3dbe706) Thanks [@yusukebe](https://github.com/yusukebe)! - feat: enable accessing sid via `c.var.session.id`
## 0.1.1
### Patch Changes
- [#1301](https://github.com/honojs/middleware/pull/1301) [`037fd94b7b0e17a049c979fa8750bd6673fdae19`](https://github.com/honojs/middleware/commit/037fd94b7b0e17a049c979fa8750bd6673fdae19) Thanks [@BarryThePenguin](https://github.com/BarryThePenguin)! - Fix types for `SessionOptions` cookie methods
## 0.1.0
### Minor Changes

View File

@ -0,0 +1,18 @@
{
"name": "@hono/session",
"version": "0.1.0",
"license": "MIT",
"exports": {
".": "./src/index.ts",
"./cookies": "./src/cookies.ts",
"./testing": "./src/helper/testing/index.ts"
},
"imports": {
"hono": "jsr:@hono/hono@^4.8.3",
"jose": "jsr:@panva/jose@^6.0.11"
},
"publish": {
"include": ["deno.json", "README.md", "src/**/*.ts"],
"exclude": ["src/**/*.test.ts"]
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@hono/session",
"version": "0.1.0",
"version": "0.2.0",
"description": "Session middleware for Hono",
"type": "module",
"module": "dist/index.js",

View File

@ -939,3 +939,85 @@ describe('session events', () => {
expect(onUpdate).toHaveBeenCalledWith({ sub })
})
})
describe('session.id', () => {
const app = new Hono()
.get('/session-id', useSession<TestData>({ secret }), async (c) => {
await c.var.session.get()
return c.json({ id: c.var.session.id })
})
.put('/session-id/:sub', useSession<TestData>({ secret }), async (c) => {
const sub = c.req.param('sub')
await c.var.session.update({ sub })
return c.json({ id: c.var.session.id })
})
it('throws an error when session is not initialised', async () => {
const app = new Hono().get('/session-id', useSession<TestData>({ secret }), (c) => {
return c.json({ id: c.var.session.id })
})
const res = await app.request('/session-id')
expect(res.status).toBe(500)
})
it('returns session id after get()', async () => {
const res = await app.request('/session-id')
const data = await res.json()
expect(res.status).toBe(200)
expect(data.id).toBeTypeOf('string')
expect(data.id.length).toBeGreaterThan(0)
})
it('returns session id after update()', async () => {
const res = await app.request('/session-id/test-user', { method: 'PUT' })
const data = await res.json()
expect(res.status).toBe(200)
expect(data.id).toBeTypeOf('string')
expect(data.id.length).toBeGreaterThan(0)
})
it('returns same session id for existing session', async () => {
const cookie = await encrypt(session.payload)
const res = await app.request('/session-id', {
headers: { cookie: `sid=${cookie}` },
})
const data = await res.json()
expect(res.status).toBe(200)
expect(data.id).toBe(sid) // 'some-session-id'
})
it('generates different session ids for new sessions', async () => {
const res1 = await app.request('/session-id')
const res2 = await app.request('/session-id')
const data1 = await res1.json()
const data2 = await res2.json()
expect(res1.status).toBe(200)
expect(res2.status).toBe(200)
expect(data1.id).not.toBe(data2.id)
expect(data1.id).toBeTypeOf('string')
expect(data2.id).toBeTypeOf('string')
})
it('preserves session id across requests with same cookie', async () => {
// First request - create session
const firstRes = await app.request('/session-id/user1', { method: 'PUT' })
const sessionCookie = await getSetCookie(firstRes, 'sid', decrypt)
const firstData = await firstRes.json()
// Second request - use same cookie
const secondRes = await app.request('/session-id', {
headers: { cookie: `sid=${sessionCookie?.value}` },
})
const secondData = await secondRes.json()
expect(firstRes.status).toBe(200)
expect(secondRes.status).toBe(200)
expect(firstData.id).toBe(secondData.id)
expect(firstData.id).toBe(sessionCookie?.payload.sid)
})
})

View File

@ -37,9 +37,9 @@ export interface SessionOptions<Data> extends SessionEvents<Data> {
*/
secret?: string | EncryptionKey
deleteCookie?: typeof cookie.deleteCookie
getCookie?: typeof cookie.getCookie
setCookie?: typeof cookie.setCookie
deleteCookie?: (c: Context, name: string, opt?: CookieOptions) => void
getCookie?: (c: Context, name: string) => string | undefined
setCookie?: (c: Context, name: string, value: string, opt?: CookieOptions) => void
}
export type SessionEnv<Data = SessionData> = Env & {
@ -104,6 +104,9 @@ export const useSession = <Data extends SessionData>(
get data() {
return session.data
},
get id() {
return session.id
},
delete() {
session.delete()
},

View File

@ -26,6 +26,11 @@ export interface Session<Data> {
*/
readonly data: Data | null
/**
* Current session ID.
*/
readonly id: string | null
/**
* Delete the current session, removing the session cookie and data from storage.
*/
@ -122,6 +127,17 @@ export function getSession<Data extends SessionData>({
return session.data ?? null
},
get id() {
if (!session) {
throw new Error('Session not initialised. Call get() or update() first.')
}
if (session.action === 'destroy') {
throw new Error('Session has been destroyed.')
}
return session.payload?.sid ?? null
},
delete() {
if (session) {
onDelete?.(session.data)

View File

@ -3,6 +3,7 @@ import { defineWorkersProject } from '@cloudflare/vitest-pool-workers/config'
export default defineWorkersProject({
test: {
globals: true,
include: ['examples/**/*.test.ts', 'src/**/*.test.ts'],
restoreMocks: true,
poolOptions: {
workers: {

View File

@ -1,5 +1,11 @@
# @hono/standard-validator
## 0.1.4
### Patch Changes
- [#1282](https://github.com/honojs/middleware/pull/1282) [`2a46bfbba0695ae713b58211fd5e83a08b829ad6`](https://github.com/honojs/middleware/commit/2a46bfbba0695ae713b58211fd5e83a08b829ad6) Thanks [@MonsterDeveloper](https://github.com/MonsterDeveloper)! - Fix cookies output for arktype in standard schema validator
## 0.1.3
### Patch Changes

View File

@ -26,7 +26,12 @@ const querySortSchema = type({
order: "'asc'|'desc'",
})
const headerSchema = type({
'user-agent': 'string',
})
export {
headerSchema,
idJSONSchema,
personJSONSchema,
postJSONSchema,

View File

@ -28,7 +28,12 @@ const querySortSchema = object({
order: picklist(['asc', 'desc']),
})
const headerSchema = object({
'user-agent': string(),
})
export {
headerSchema,
idJSONSchema,
personJSONSchema,
postJSONSchema,

View File

@ -28,7 +28,12 @@ const querySortSchema = z.object({
order: z.enum(['asc', 'desc']),
})
const headerSchema = z.object({
'user-agent': z.string(),
})
export {
headerSchema,
idJSONSchema,
personJSONSchema,
postJSONSchema,

View File

@ -1,6 +1,6 @@
{
"name": "@hono/standard-validator",
"version": "0.1.3",
"version": "0.1.4",
"license": "MIT",
"exports": {
".": "./src/index.ts"

View File

@ -1,6 +1,6 @@
{
"name": "@hono/standard-validator",
"version": "0.1.3",
"version": "0.1.4",
"description": "Validator middleware using Standard Schema",
"type": "module",
"main": "dist/index.cjs",

View File

@ -352,6 +352,47 @@ describe('Standard Schema Validation', () => {
>
})
})
describe('Sensitive Data Removal', () => {
it("doesn't return cookies after headers validation", async () => {
const app = new Hono()
const schema = schemas.headerSchema
app.get('/headers', sValidator('header', schema), (c) =>
c.json({ success: true, userAgent: c.req.header('User-Agent') })
)
const req = new Request('http://localhost/headers', {
headers: {
// Not passing the User-Agent header to trigger the validation error
Cookie: 'SECRET=123',
},
})
const res = await app.request(req)
expect(res.status).toBe(400)
const data = (await res.json()) as { success: false; error: unknown[] }
expect(data.success).toBe(false)
expect(data.error).toBeDefined()
if (lib === 'arktype') {
expect(
(data.error as { data: Record<string, unknown> }[]).some(
(error) => error.data && error.data.cookie
)
).toBe(false)
}
if (lib === 'valibot') {
expect(
(data.error as { path: { input: Record<string, unknown> }[] }[]).some((error) =>
error.path.some((path) => path.input.cookie)
)
).toBe(false)
}
})
})
})
})
})

View File

@ -1,6 +1,7 @@
import type { StandardSchemaV1 } from '@standard-schema/spec'
import type { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono'
import { validator } from 'hono/validator'
import { sanitizeIssues } from './sanitize-issues'
type HasUndefined<T> = undefined extends T ? true : false
type TOrPromiseOfT<T> = T | Promise<T>
@ -21,6 +22,67 @@ type Hook<
c: Context<E, P>
) => TOrPromiseOfT<Response | void | TypedResponse<O>>
/**
* Validation middleware for libraries that support [Standard Schema](https://standardschema.dev/) specification.
*
* This middleware validates incoming request data against a provided schema
* that conforms to the Standard Schema specification. It supports validation
* of JSON bodies, headers, queries, forms, and other request targets.
*
* @param target - The request target to validate ('json', 'header', 'query', 'form', etc.)
* @param schema - A schema object conforming to Standard Schema specification
* @param hook - Optional hook function called with validation results for custom error handling
* @returns A Hono middleware handler that validates requests and makes validated data available via `c.req.valid()`
*
* @example Basic JSON validation
* ```ts
* import { z } from 'zod'
* import { sValidator } from '@hono/standard-validator'
*
* const schema = z.object({
* name: z.string(),
* age: z.number(),
* })
*
* app.post('/author', sValidator('json', schema), (c) => {
* const data = c.req.valid('json')
* return c.json({
* success: true,
* message: `${data.name} is ${data.age}`,
* })
* })
* ```
*
* @example With custom error handling hook
* ```ts
* app.post(
* '/post',
* sValidator('json', schema, (result, c) => {
* if (!result.success) {
* return c.text('Invalid!', 400)
* }
* }),
* (c) => {
* // Handler code
* }
* )
* ```
*
* @example Header validation
* ```ts
* import { object, string } from 'valibot'
*
* const schema = object({
* 'content-type': string(),
* 'user-agent': string(),
* })
*
* app.post('/author', sValidator('header', schema), (c) => {
* const headers = c.req.valid('header')
* // do something with headers
* })
* ```
*/
const sValidator = <
Schema extends StandardSchemaV1,
Target extends keyof ValidationTargets,
@ -71,7 +133,9 @@ const sValidator = <
}
if (result.issues) {
return c.json({ data: value, error: result.issues, success: false }, 400)
const processedIssues = sanitizeIssues(result.issues, schema['~standard'].vendor, target)
return c.json({ data: value, error: processedIssues, success: false }, 400)
}
return result.value as StandardSchemaV1.InferOutput<Schema>

View File

@ -0,0 +1,95 @@
import type { StandardSchemaV1 } from '@standard-schema/spec'
import type { ValidationTargets } from 'hono'
const RESTRICTED_DATA_FIELDS = {
header: ['cookie'],
}
/**
* Sanitizes validation issues by removing sensitive data fields from error messages.
*
* This function removes potentially sensitive information (like cookies) from validation
* error messages before they are returned to the client. It handles different validation
* library formats based on the vendor string.
*
* @param issues - Array of validation issues from Standard Schema validation
* @param vendor - The validation library vendor identifier (e.g., 'arktype', 'valibot')
* @param target - The validation target being processed ('header', 'json', etc.)
* @returns Sanitized array of validation issues with sensitive data removed
*
* @example
* ```ts
* const issues = [{ message: 'Invalid header', data: { cookie: 'secret' } }]
* const sanitized = sanitizeIssues(issues, 'arktype', 'header')
* // Returns issues with cookie field removed from data
* ```
*/
export function sanitizeIssues(
issues: readonly StandardSchemaV1.Issue[],
vendor: string,
target: keyof ValidationTargets
): readonly StandardSchemaV1.Issue[] {
if (!(target in RESTRICTED_DATA_FIELDS)) {
return issues
}
const restrictedFields =
RESTRICTED_DATA_FIELDS[target as keyof typeof RESTRICTED_DATA_FIELDS] || []
if (vendor === 'arktype') {
return sanitizeArktypeIssues(issues, restrictedFields)
}
if (vendor === 'valibot') {
return sanitizeValibotIssues(issues, restrictedFields)
}
return issues
}
function sanitizeArktypeIssues(
issues: readonly StandardSchemaV1.Issue[],
restrictedFields: string[]
): readonly StandardSchemaV1.Issue[] {
return issues.map((issue) => {
if (
issue &&
typeof issue === 'object' &&
'data' in issue &&
typeof issue.data === 'object' &&
issue.data !== null &&
!Array.isArray(issue.data)
) {
const dataCopy = { ...(issue.data as Record<string, unknown>) }
for (const field of restrictedFields) {
delete dataCopy[field]
}
return { ...issue, data: dataCopy }
}
return issue
}) as readonly StandardSchemaV1.Issue[]
}
function sanitizeValibotIssues(
issues: readonly StandardSchemaV1.Issue[],
restrictedFields: string[]
): readonly StandardSchemaV1.Issue[] {
return issues.map((issue) => {
if (issue && typeof issue === 'object' && 'path' in issue && Array.isArray(issue.path)) {
for (const path of issue.path) {
if (
typeof path === 'object' &&
'input' in path &&
typeof path.input === 'object' &&
path.input !== null &&
!Array.isArray(path.input)
) {
for (const field of restrictedFields) {
delete path.input[field]
}
}
}
}
return issue
}) as readonly StandardSchemaV1.Issue[]
}

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,6 +3,7 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
restoreMocks: true,
unstubEnvs: true,
},

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -4,10 +4,11 @@ import { defineProject } from 'vitest/config'
export default defineProject({
plugins: [
UnpluginTypia({
tsconfig: './tsconfig.json',
tsconfig: './tsconfig.build.json',
}),
],
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -1,5 +1,17 @@
# @hono/ua-blocker
## 0.1.6
### Patch Changes
- [#1309](https://github.com/honojs/middleware/pull/1309) [`47cb4c85693ebe77d3b8b55f6e1e59778f671a15`](https://github.com/honojs/middleware/commit/47cb4c85693ebe77d3b8b55f6e1e59778f671a15) Thanks [@github-actions](https://github.com/apps/github-actions)! - chore(ua-blocker): sync `robots.json` with upstream
## 0.1.5
### Patch Changes
- [#1298](https://github.com/honojs/middleware/pull/1298) [`18dec85b7833d1b3e80b5e1443f636f8c34adbc4`](https://github.com/honojs/middleware/commit/18dec85b7833d1b3e80b5e1443f636f8c34adbc4) Thanks [@github-actions](https://github.com/apps/github-actions)! - chore(ua-blocker): sync `robots.json` with upstream
## 0.1.4
### Patch Changes

View File

@ -1,6 +1,6 @@
{
"name": "@hono/ua-blocker",
"version": "0.1.4",
"version": "0.1.6",
"description": "User agent-based blocker for Hono",
"type": "module",
"module": "dist/index.js",

View File

@ -251,6 +251,13 @@
"frequency": "No information.",
"description": "Used to train Gemini and Vertex AI generative APIs. Does not impact a site's inclusion or ranking in Google Search."
},
"GoogleAgent-Mariner": {
"operator": "Unclear at this time.",
"respect": "Unclear at this time.",
"function": "AI Agents",
"frequency": "Unclear at this time.",
"description": "GoogleAgent-Mariner is an AI agent created by Google that can use a web browser. It can intelligently navigate and interact with websites to complete multi-step tasks on behalf of a human user. More info can be found at https://darkvisitors.com/agents/agents/googleagent-mariner"
},
"GoogleOther": {
"description": "\"Used by various product teams for fetching publicly accessible content from sites. For example, it may be used for one-off crawls for internal research and development.\"",
"frequency": "No information.",
@ -524,6 +531,20 @@
"operator": "[Sidetrade](https://www.sidetrade.com)",
"respect": "Unclear at this time."
},
"SummalyBot": {
"description": "AI input summarization.",
"frequency": "No information.",
"function": "Extracts data to provide summaries for user; additional usages unclear.",
"operator": "Unclear at this time.",
"respect": "Unclear at this time."
},
"Thinkbot": {
"operator": "[Thinkbot](https://www.thinkbot.agency)",
"respect": "No",
"function": "Insights on AI integration and automation.",
"frequency": "Unclear at this time.",
"description": "Collects data for analysis on AI usage and automation."
},
"TikTokSpider": {
"operator": "ByteDance",
"respect": "Unclear at this time.",

View File

@ -35,6 +35,7 @@ User-agent: FriendlyCrawler
User-agent: Gemini-Deep-Research
User-agent: Google-CloudVertexBot
User-agent: Google-Extended
User-agent: GoogleAgent-Mariner
User-agent: GoogleOther
User-agent: GoogleOther-Image
User-agent: GoogleOther-Video
@ -74,6 +75,8 @@ User-agent: Scrapy
User-agent: SemrushBot-OCOB
User-agent: SemrushBot-SWA
User-agent: Sidetrade indexer bot
User-agent: SummalyBot
User-agent: Thinkbot
User-agent: TikTokSpider
User-agent: Timpibot
User-agent: VelenPublicWebCrawler
@ -85,7 +88,7 @@ User-agent: YandexAdditionalBot
User-agent: YouBot
Disallow: /
`;
export const ALL_BOTS = ["AI2Bot", "Ai2Bot-Dolma", "aiHitBot", "Amazonbot", "Andibot", "anthropic-ai", "Applebot", "Applebot-Extended", "Awario", "bedrockbot", "Brightbot 1.0", "Bytespider", "CCBot", "ChatGPT-User", "Claude-SearchBot", "Claude-User", "Claude-Web", "ClaudeBot", "cohere-ai", "cohere-training-data-crawler", "Cotoyogi", "Crawlspace", "Datenbank Crawler", "Devin", "Diffbot", "DuckAssistBot", "Echobot Bot", "EchoboxBot", "FacebookBot", "facebookexternalhit", "Factset_spyderbot", "FirecrawlAgent", "FriendlyCrawler", "Gemini-Deep-Research", "Google-CloudVertexBot", "Google-Extended", "GoogleOther", "GoogleOther-Image", "GoogleOther-Video", "GPTBot", "iaskspider/2.0", "ICC-Crawler", "ImagesiftBot", "img2dataset", "ISSCyberRiskCrawler", "Kangaroo Bot", "meta-externalagent", "Meta-ExternalAgent", "meta-externalfetcher", "Meta-ExternalFetcher", "MistralAI-User", "MistralAI-User/1.0", "MyCentralAIScraperBot", "netEstate Imprint Crawler", "NovaAct", "OAI-SearchBot", "omgili", "omgilibot", "Operator", "PanguBot", "Panscient", "panscient.com", "Perplexity-User", "PerplexityBot", "PetalBot", "PhindBot", "Poseidon Research Crawler", "QualifiedBot", "QuillBot", "quillbot.com", "SBIntuitionsBot", "Scrapy", "SemrushBot-OCOB", "SemrushBot-SWA", "Sidetrade indexer bot", "TikTokSpider", "Timpibot", "VelenPublicWebCrawler", "WARDBot", "Webzio-Extended", "wpbot", "YandexAdditional", "YandexAdditionalBot", "YouBot"];
export const NON_RESPECTING_BOTS = ["Andibot", "anthropic-ai", "Applebot", "Awario", "Brightbot 1.0", "Bytespider", "Claude-Web", "cohere-ai", "cohere-training-data-crawler", "Datenbank Crawler", "Devin", "Diffbot", "DuckAssistBot", "Echobot Bot", "EchoboxBot", "facebookexternalhit", "Factset_spyderbot", "Gemini-Deep-Research", "iaskspider/2.0", "img2dataset", "ISSCyberRiskCrawler", "Kangaroo Bot", "Meta-ExternalAgent", "meta-externalfetcher", "Meta-ExternalFetcher", "MistralAI-User", "MyCentralAIScraperBot", "netEstate Imprint Crawler", "NovaAct", "Operator", "PanguBot", "Perplexity-User", "PhindBot", "Poseidon Research Crawler", "QualifiedBot", "QuillBot", "quillbot.com", "Scrapy", "Sidetrade indexer bot", "TikTokSpider", "Timpibot", "WARDBot", "Webzio-Extended", "wpbot"];
export const ALL_BOTS_REGEX = /(AI2BOT|AI2BOT-DOLMA|AIHITBOT|AMAZONBOT|ANDIBOT|ANTHROPIC-AI|APPLEBOT|APPLEBOT-EXTENDED|AWARIO|BEDROCKBOT|BRIGHTBOT 1.0|BYTESPIDER|CCBOT|CHATGPT-USER|CLAUDE-SEARCHBOT|CLAUDE-USER|CLAUDE-WEB|CLAUDEBOT|COHERE-AI|COHERE-TRAINING-DATA-CRAWLER|COTOYOGI|CRAWLSPACE|DATENBANK CRAWLER|DEVIN|DIFFBOT|DUCKASSISTBOT|ECHOBOT BOT|ECHOBOXBOT|FACEBOOKBOT|FACEBOOKEXTERNALHIT|FACTSET_SPYDERBOT|FIRECRAWLAGENT|FRIENDLYCRAWLER|GEMINI-DEEP-RESEARCH|GOOGLE-CLOUDVERTEXBOT|GOOGLE-EXTENDED|GOOGLEOTHER|GOOGLEOTHER-IMAGE|GOOGLEOTHER-VIDEO|GPTBOT|IASKSPIDER\/2.0|ICC-CRAWLER|IMAGESIFTBOT|IMG2DATASET|ISSCYBERRISKCRAWLER|KANGAROO BOT|META-EXTERNALAGENT|META-EXTERNALAGENT|META-EXTERNALFETCHER|META-EXTERNALFETCHER|MISTRALAI-USER|MISTRALAI-USER\/1.0|MYCENTRALAISCRAPERBOT|NETESTATE IMPRINT CRAWLER|NOVAACT|OAI-SEARCHBOT|OMGILI|OMGILIBOT|OPERATOR|PANGUBOT|PANSCIENT|PANSCIENT.COM|PERPLEXITY-USER|PERPLEXITYBOT|PETALBOT|PHINDBOT|POSEIDON RESEARCH CRAWLER|QUALIFIEDBOT|QUILLBOT|QUILLBOT.COM|SBINTUITIONSBOT|SCRAPY|SEMRUSHBOT-OCOB|SEMRUSHBOT-SWA|SIDETRADE INDEXER BOT|TIKTOKSPIDER|TIMPIBOT|VELENPUBLICWEBCRAWLER|WARDBOT|WEBZIO-EXTENDED|WPBOT|YANDEXADDITIONAL|YANDEXADDITIONALBOT|YOUBOT)/;
export const NON_RESPECTING_BOTS_REGEX = /(ANDIBOT|ANTHROPIC-AI|APPLEBOT|AWARIO|BRIGHTBOT 1.0|BYTESPIDER|CLAUDE-WEB|COHERE-AI|COHERE-TRAINING-DATA-CRAWLER|DATENBANK CRAWLER|DEVIN|DIFFBOT|DUCKASSISTBOT|ECHOBOT BOT|ECHOBOXBOT|FACEBOOKEXTERNALHIT|FACTSET_SPYDERBOT|GEMINI-DEEP-RESEARCH|IASKSPIDER\/2.0|IMG2DATASET|ISSCYBERRISKCRAWLER|KANGAROO BOT|META-EXTERNALAGENT|META-EXTERNALFETCHER|META-EXTERNALFETCHER|MISTRALAI-USER|MYCENTRALAISCRAPERBOT|NETESTATE IMPRINT CRAWLER|NOVAACT|OPERATOR|PANGUBOT|PERPLEXITY-USER|PHINDBOT|POSEIDON RESEARCH CRAWLER|QUALIFIEDBOT|QUILLBOT|QUILLBOT.COM|SCRAPY|SIDETRADE INDEXER BOT|TIKTOKSPIDER|TIMPIBOT|WARDBOT|WEBZIO-EXTENDED|WPBOT)/;
export const ALL_BOTS = ["AI2Bot", "Ai2Bot-Dolma", "aiHitBot", "Amazonbot", "Andibot", "anthropic-ai", "Applebot", "Applebot-Extended", "Awario", "bedrockbot", "Brightbot 1.0", "Bytespider", "CCBot", "ChatGPT-User", "Claude-SearchBot", "Claude-User", "Claude-Web", "ClaudeBot", "cohere-ai", "cohere-training-data-crawler", "Cotoyogi", "Crawlspace", "Datenbank Crawler", "Devin", "Diffbot", "DuckAssistBot", "Echobot Bot", "EchoboxBot", "FacebookBot", "facebookexternalhit", "Factset_spyderbot", "FirecrawlAgent", "FriendlyCrawler", "Gemini-Deep-Research", "Google-CloudVertexBot", "Google-Extended", "GoogleAgent-Mariner", "GoogleOther", "GoogleOther-Image", "GoogleOther-Video", "GPTBot", "iaskspider/2.0", "ICC-Crawler", "ImagesiftBot", "img2dataset", "ISSCyberRiskCrawler", "Kangaroo Bot", "meta-externalagent", "Meta-ExternalAgent", "meta-externalfetcher", "Meta-ExternalFetcher", "MistralAI-User", "MistralAI-User/1.0", "MyCentralAIScraperBot", "netEstate Imprint Crawler", "NovaAct", "OAI-SearchBot", "omgili", "omgilibot", "Operator", "PanguBot", "Panscient", "panscient.com", "Perplexity-User", "PerplexityBot", "PetalBot", "PhindBot", "Poseidon Research Crawler", "QualifiedBot", "QuillBot", "quillbot.com", "SBIntuitionsBot", "Scrapy", "SemrushBot-OCOB", "SemrushBot-SWA", "Sidetrade indexer bot", "SummalyBot", "Thinkbot", "TikTokSpider", "Timpibot", "VelenPublicWebCrawler", "WARDBot", "Webzio-Extended", "wpbot", "YandexAdditional", "YandexAdditionalBot", "YouBot"];
export const NON_RESPECTING_BOTS = ["Andibot", "anthropic-ai", "Applebot", "Awario", "Brightbot 1.0", "Bytespider", "Claude-Web", "cohere-ai", "cohere-training-data-crawler", "Datenbank Crawler", "Devin", "Diffbot", "DuckAssistBot", "Echobot Bot", "EchoboxBot", "facebookexternalhit", "Factset_spyderbot", "Gemini-Deep-Research", "GoogleAgent-Mariner", "iaskspider/2.0", "img2dataset", "ISSCyberRiskCrawler", "Kangaroo Bot", "Meta-ExternalAgent", "meta-externalfetcher", "Meta-ExternalFetcher", "MistralAI-User", "MyCentralAIScraperBot", "netEstate Imprint Crawler", "NovaAct", "Operator", "PanguBot", "Perplexity-User", "PhindBot", "Poseidon Research Crawler", "QualifiedBot", "QuillBot", "quillbot.com", "Scrapy", "Sidetrade indexer bot", "SummalyBot", "Thinkbot", "TikTokSpider", "Timpibot", "WARDBot", "Webzio-Extended", "wpbot"];
export const ALL_BOTS_REGEX = /(AI2BOT|AI2BOT-DOLMA|AIHITBOT|AMAZONBOT|ANDIBOT|ANTHROPIC-AI|APPLEBOT|APPLEBOT-EXTENDED|AWARIO|BEDROCKBOT|BRIGHTBOT 1.0|BYTESPIDER|CCBOT|CHATGPT-USER|CLAUDE-SEARCHBOT|CLAUDE-USER|CLAUDE-WEB|CLAUDEBOT|COHERE-AI|COHERE-TRAINING-DATA-CRAWLER|COTOYOGI|CRAWLSPACE|DATENBANK CRAWLER|DEVIN|DIFFBOT|DUCKASSISTBOT|ECHOBOT BOT|ECHOBOXBOT|FACEBOOKBOT|FACEBOOKEXTERNALHIT|FACTSET_SPYDERBOT|FIRECRAWLAGENT|FRIENDLYCRAWLER|GEMINI-DEEP-RESEARCH|GOOGLE-CLOUDVERTEXBOT|GOOGLE-EXTENDED|GOOGLEAGENT-MARINER|GOOGLEOTHER|GOOGLEOTHER-IMAGE|GOOGLEOTHER-VIDEO|GPTBOT|IASKSPIDER\/2.0|ICC-CRAWLER|IMAGESIFTBOT|IMG2DATASET|ISSCYBERRISKCRAWLER|KANGAROO BOT|META-EXTERNALAGENT|META-EXTERNALAGENT|META-EXTERNALFETCHER|META-EXTERNALFETCHER|MISTRALAI-USER|MISTRALAI-USER\/1.0|MYCENTRALAISCRAPERBOT|NETESTATE IMPRINT CRAWLER|NOVAACT|OAI-SEARCHBOT|OMGILI|OMGILIBOT|OPERATOR|PANGUBOT|PANSCIENT|PANSCIENT.COM|PERPLEXITY-USER|PERPLEXITYBOT|PETALBOT|PHINDBOT|POSEIDON RESEARCH CRAWLER|QUALIFIEDBOT|QUILLBOT|QUILLBOT.COM|SBINTUITIONSBOT|SCRAPY|SEMRUSHBOT-OCOB|SEMRUSHBOT-SWA|SIDETRADE INDEXER BOT|SUMMALYBOT|THINKBOT|TIKTOKSPIDER|TIMPIBOT|VELENPUBLICWEBCRAWLER|WARDBOT|WEBZIO-EXTENDED|WPBOT|YANDEXADDITIONAL|YANDEXADDITIONALBOT|YOUBOT)/;
export const NON_RESPECTING_BOTS_REGEX = /(ANDIBOT|ANTHROPIC-AI|APPLEBOT|AWARIO|BRIGHTBOT 1.0|BYTESPIDER|CLAUDE-WEB|COHERE-AI|COHERE-TRAINING-DATA-CRAWLER|DATENBANK CRAWLER|DEVIN|DIFFBOT|DUCKASSISTBOT|ECHOBOT BOT|ECHOBOXBOT|FACEBOOKEXTERNALHIT|FACTSET_SPYDERBOT|GEMINI-DEEP-RESEARCH|GOOGLEAGENT-MARINER|IASKSPIDER\/2.0|IMG2DATASET|ISSCYBERRISKCRAWLER|KANGAROO BOT|META-EXTERNALAGENT|META-EXTERNALFETCHER|META-EXTERNALFETCHER|MISTRALAI-USER|MYCENTRALAISCRAPERBOT|NETESTATE IMPRINT CRAWLER|NOVAACT|OPERATOR|PANGUBOT|PERPLEXITY-USER|PHINDBOT|POSEIDON RESEARCH CRAWLER|QUALIFIEDBOT|QUILLBOT|QUILLBOT.COM|SCRAPY|SIDETRADE INDEXER BOT|SUMMALYBOT|THINKBOT|TIKTOKSPIDER|TIMPIBOT|WARDBOT|WEBZIO-EXTENDED|WPBOT)/;

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -1,5 +1,35 @@
# @hono/zod-openapi
## 1.0.2
### Patch Changes
- [#1320](https://github.com/honojs/middleware/pull/1320) [`e835cf6183fb4717cfdbe156d9b3d423625a0e15`](https://github.com/honojs/middleware/commit/e835cf6183fb4717cfdbe156d9b3d423625a0e15) Thanks [@yusukebe](https://github.com/yusukebe)! - fix: correct importing `zod`
## 1.0.1
### Patch Changes
- Updated dependencies [[`c6a16ab7aa8fba2403d4294e7673f96796020c65`](https://github.com/honojs/middleware/commit/c6a16ab7aa8fba2403d4294e7673f96796020c65)]:
- @hono/zod-validator@0.7.2
## 1.0.0
### Major Changes
- [#1223](https://github.com/honojs/middleware/pull/1223) [`845e336ff41d29fa74ec3cf84afc16f5ac0c2c77`](https://github.com/honojs/middleware/commit/845e336ff41d29fa74ec3cf84afc16f5ac0c2c77) Thanks [@yusukebe](https://github.com/yusukebe)! - feat: support Zod v4
Zod OpenAPI has been migrated the Zod version from v3 to v4. As a result, the `zod` in `peerDependencies` has been updated to 4.0.0 or higher.
Although this is not a breaking change, it is a major change, so it is considered a major version upgrade.
## 0.19.10
### Patch Changes
- Updated dependencies [[`9f6278f51c846a171a9baa6335fb8fbd9b42cb1c`](https://github.com/honojs/middleware/commit/9f6278f51c846a171a9baa6335fb8fbd9b42cb1c), [`9f6278f51c846a171a9baa6335fb8fbd9b42cb1c`](https://github.com/honojs/middleware/commit/9f6278f51c846a171a9baa6335fb8fbd9b42cb1c)]:
- @hono/zod-validator@0.7.1
## 0.19.9
### Patch Changes

View File

@ -1,6 +1,6 @@
{
"name": "@hono/zod-openapi",
"version": "0.19.9",
"version": "1.0.2",
"license": "MIT",
"exports": {
".": "./src/index.ts"

View File

@ -1,6 +1,6 @@
{
"name": "@hono/zod-openapi",
"version": "0.19.9",
"version": "1.0.2",
"description": "A wrapper class of Hono which supports OpenAPI.",
"type": "module",
"module": "dist/index.js",
@ -41,7 +41,7 @@
"homepage": "https://github.com/honojs/middleware",
"peerDependencies": {
"hono": ">=4.3.6",
"zod": ">=3.0.0"
"zod": "^4.0.0"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.4",
@ -51,10 +51,10 @@
"typescript": "^5.8.2",
"vitest": "^3.2.4",
"yaml": "^2.4.3",
"zod": "^3.22.1"
"zod": "^4.0.5"
},
"dependencies": {
"@asteasolutions/zod-to-openapi": "^7.3.0",
"@asteasolutions/zod-to-openapi": "^8.0.0",
"@hono/zod-validator": "workspace:^",
"openapi3-ts": "^4.5.0"
},

View File

@ -240,7 +240,7 @@ describe('coerce', () => {
type Actual = ExtractSchema<typeof routes>['/api/users/:id']['$get']['input']
type Expected = {
param: {
id: number
id: unknown
}
}
type verify = Expect<Equal<Expected, Actual>>

View File

@ -11,6 +11,7 @@ import {
OpenApiGeneratorV3,
OpenApiGeneratorV31,
extendZodWithOpenApi,
getOpenApiMetadata,
} from '@asteasolutions/zod-to-openapi'
import { zValidator } from '@hono/zod-validator'
import { Hono } from 'hono'
@ -38,8 +39,8 @@ import type { JSONParsed, JSONValue, RemoveBlankRecord, SimplifyDeepArray } from
import { mergePath } from 'hono/utils/url'
import type { OpenAPIObject } from 'openapi3-ts/oas30'
import type { OpenAPIObject as OpenAPIV31bject } from 'openapi3-ts/oas31'
import type { ZodError, ZodSchema } from 'zod'
import { ZodType, z } from 'zod'
import type { ZodError } from 'zod'
type MaybePromise<T> = Promise<T> | T
@ -128,7 +129,7 @@ type InputTypeJson<R extends RouteConfig> = R['request'] extends RequestTypes
? {}
: R['request']['body']['content'][keyof R['request']['body']['content']] extends Record<
'schema',
ZodSchema<any>
ZodType<any>
>
? {
in: {
@ -154,7 +155,7 @@ type InputTypeForm<R extends RouteConfig> = R['request'] extends RequestTypes
? {}
: R['request']['body']['content'][keyof R['request']['body']['content']] extends Record<
'schema',
ZodSchema<any>
ZodType<any>
>
? {
in: {
@ -181,7 +182,7 @@ type InputTypeCookie<R extends RouteConfig> = InputTypeBase<R, 'cookies', 'cooki
type ExtractContent<T> = T extends {
[K in keyof T]: infer A
}
? A extends Record<'schema', ZodSchema>
? A extends Record<'schema', ZodType>
? z.infer<A['schema']>
: never
: never
@ -659,11 +660,14 @@ export class OpenAPIHono<
}
case 'schema':
return this.openAPIRegistry.register(def.schema._def.openapi._internal.refId, def.schema)
return this.openAPIRegistry.register(
getOpenApiMetadata(def.schema)._internal?.refId,
def.schema
)
case 'parameter':
return this.openAPIRegistry.registerParameter(
def.schema._def.openapi._internal.refId,
getOpenApiMetadata(def.schema)._internal?.refId,
def.schema
)

View File

@ -3,6 +3,7 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
typecheck: {
tsconfig: './tsconfig.json',
enabled: true,

View File

@ -1,5 +1,19 @@
# @hono/zod-validator
## 0.7.2
### Patch Changes
- [#1315](https://github.com/honojs/middleware/pull/1315) [`c6a16ab7aa8fba2403d4294e7673f96796020c65`](https://github.com/honojs/middleware/commit/c6a16ab7aa8fba2403d4294e7673f96796020c65) Thanks [@yusukebe](https://github.com/yusukebe)! - fix: support transform
## 0.7.1
### Patch Changes
- [#1302](https://github.com/honojs/middleware/pull/1302) [`9f6278f51c846a171a9baa6335fb8fbd9b42cb1c`](https://github.com/honojs/middleware/commit/9f6278f51c846a171a9baa6335fb8fbd9b42cb1c) Thanks [@kiki-kanri](https://github.com/kiki-kanri)! - correctly set the zod version to follow the official website doc
- [#1302](https://github.com/honojs/middleware/pull/1302) [`9f6278f51c846a171a9baa6335fb8fbd9b42cb1c`](https://github.com/honojs/middleware/commit/9f6278f51c846a171a9baa6335fb8fbd9b42cb1c) Thanks [@kiki-kanri](https://github.com/kiki-kanri)! - upgrade zod to v4 and import style to be more tree-shakeable in README.md
## 0.7.0
### Minor Changes

View File

@ -7,7 +7,7 @@ The validator middleware using [Zod](https://zod.dev) for [Hono](https://honojs.
## Usage
```ts
import { z } from 'zod'
import * as z from 'zod'
import { zValidator } from '@hono/zod-validator'
const schema = z.object({

View File

@ -1,6 +1,6 @@
{
"name": "@hono/zod-validator",
"version": "0.7.0",
"version": "0.7.2",
"license": "MIT",
"exports": {
".": "./src/index.ts"

View File

@ -1,6 +1,6 @@
{
"name": "@hono/zod-validator",
"version": "0.7.0",
"version": "0.7.2",
"description": "Validator middleware using Zod",
"type": "module",
"main": "dist/index.js",
@ -42,7 +42,7 @@
"homepage": "https://github.com/honojs/middleware",
"peerDependencies": {
"hono": ">=3.9.0",
"zod": "^3.25.0"
"zod": "^3.25.0 || ^4.0.0"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.4",
@ -51,6 +51,6 @@
"tsup": "^8.4.0",
"typescript": "^5.8.2",
"vitest": "^3.2.4",
"zod": "~3.25.6"
"zod": "^4.0.5"
}
}

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import type { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono'
import { validator } from 'hono/validator'
import type * as v3 from 'zod'
import type * as v3 from 'zod/v3'
import type { ZodSafeParseResult as v4ZodSafeParseResult } from 'zod/v4'
import type * as v4 from 'zod/v4/core'
@ -73,7 +73,7 @@ export const zValidator = <
if ((target === 'header' && '_def' in schema) || (target === 'header' && '_zod' in schema)) {
// create an object that maps lowercase schema keys to lowercase
// @ts-expect-error the schema is a Zod Schema
const schemaKeys = Object.keys(schema.shape)
const schemaKeys = Object.keys('in' in schema ? schema.in.shape : schema.shape)
const caseInsensitiveKeymap = Object.fromEntries(
schemaKeys.map((key) => [key.toLowerCase(), key])
)

View File

@ -464,3 +464,39 @@ describe('With options + validationFunction', () => {
})
})
})
describe('Transform', () => {
const schema = z
.object({
'user-agent': z.string(),
})
.transform((data) => ({
userAgent: data['user-agent'],
}))
const zValidatorHeader = zValidator('header', schema)
const app = new Hono()
app.post('/test', zValidatorHeader, async (c) => {
const header = c.req.valid('header')
return c.json(header)
})
it('Should return 400 response', async () => {
const res = await app.request('/test', {
method: 'POST',
})
expect(res.status).toBe(400)
})
it('Should return 200 response', async () => {
const res = await app.request('/test', {
method: 'POST',
headers: {
'user-agent': 'my-agent',
},
})
expect(res.status).toBe(200)
})
})

View File

@ -3,5 +3,6 @@ import { defineProject } from 'vitest/config'
export default defineProject({
test: {
globals: true,
include: ['src/**/*.test.ts'],
},
})

View File

@ -1,15 +1,14 @@
import { defineConfig, coverageConfigDefaults } from 'vitest/config'
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
coverage: {
exclude: ['**/dist/**', ...coverageConfigDefaults.exclude],
// TODO: use v8 - https://github.com/vitest-dev/vitest/issues/5783
provider: 'istanbul',
thresholds: {
autoUpdate: true,
},
},
projects: ['packages/*', '!packages/bun-transpiler'],
projects: ['packages/*/vitest.config.ts'],
},
})

View File

@ -99,14 +99,14 @@ __metadata:
languageName: node
linkType: hard
"@asteasolutions/zod-to-openapi@npm:^7.3.0":
version: 7.3.0
resolution: "@asteasolutions/zod-to-openapi@npm:7.3.0"
"@asteasolutions/zod-to-openapi@npm:^8.0.0":
version: 8.0.0
resolution: "@asteasolutions/zod-to-openapi@npm:8.0.0"
dependencies:
openapi3-ts: "npm:^4.1.2"
peerDependencies:
zod: ^3.20.2
checksum: 10c0/f0a68a89929cdeaa3e21d2027489689f982824d676a9332c680e119f60881dd39b571324b24ad4837fda49bf6fe7c3e2af2199268b281bf1aec923d7a7cbfc40
zod: ^4.0.0
checksum: 10c0/b522d074832fb137dca724c8bd4bb134c7b4d4cad12c247ed3c864f993923b3475fc06580e6e1cbc4fd8641cd361679bbe1dd87c9bb42e142bc056d96d59fbc8
languageName: node
linkType: hard
@ -694,24 +694,25 @@ __metadata:
languageName: node
linkType: hard
"@clerk/backend@npm:^2.2.0":
version: 2.2.0
resolution: "@clerk/backend@npm:2.2.0"
"@clerk/backend@npm:^2.4.1":
version: 2.4.1
resolution: "@clerk/backend@npm:2.4.1"
dependencies:
"@clerk/shared": "npm:^3.9.8"
"@clerk/types": "npm:^4.61.0"
"@clerk/shared": "npm:^3.11.0"
"@clerk/types": "npm:^4.64.0"
cookie: "npm:1.0.2"
snakecase-keys: "npm:8.0.1"
standardwebhooks: "npm:^1.0.0"
tslib: "npm:2.8.1"
checksum: 10c0/deaf688e085ec2a28b8e98453b3ce2c293f19adf440acd8b295c71c7d765ad1c98841e8a5e9ee5d254e91d18884533cc11f6880c34ec99f4d3b57b330b47fe70
checksum: 10c0/1b6324a44c908c3623e60a2b25f814074e0beab78bf2dfc3ff2401215b5c8e5d8757a1df33af537f6a0a4540ac933d0583dab5aaba5664c0a719ce5d70b4cea0
languageName: node
linkType: hard
"@clerk/shared@npm:^3.9.8":
version: 3.9.8
resolution: "@clerk/shared@npm:3.9.8"
"@clerk/shared@npm:^3.11.0":
version: 3.11.0
resolution: "@clerk/shared@npm:3.11.0"
dependencies:
"@clerk/types": "npm:^4.61.0"
"@clerk/types": "npm:^4.64.0"
dequal: "npm:2.0.3"
glob-to-regexp: "npm:0.4.1"
js-cookie: "npm:3.0.5"
@ -725,16 +726,16 @@ __metadata:
optional: true
react-dom:
optional: true
checksum: 10c0/64f21c38900dd41229c94920ce7fa52dba67b1bb3fdca4468d99a7bca450b1f1d1cfebecd1867657554bf915b26c24cb46d0962746ec9b92dad6d435571f05d2
checksum: 10c0/f5b6099f506bfdbf8a03fb615597500adddab7350d2d1e39ee0ff0f5775d10b712e6fcbf8a551ea6c17d96d242a59fccaed4e536122b8306137a942582ecf2b7
languageName: node
linkType: hard
"@clerk/types@npm:^4.61.0":
version: 4.61.0
resolution: "@clerk/types@npm:4.61.0"
"@clerk/types@npm:^4.64.0":
version: 4.64.0
resolution: "@clerk/types@npm:4.64.0"
dependencies:
csstype: "npm:3.1.3"
checksum: 10c0/4f05bbdc6562c3e8f114ccfe378bfb7c622ce49338336ec962c7bf7f69bdd7eab98484c69e43dd69ffc8f7d2fea43609c1f8a37a650b475361cf8dea541b9ee3
checksum: 10c0/83ad0a436deedfe2148f99af905dd54f945acb04a2eb5a822d119e071abc3d4123b72d2132276dac5f0c0c4dba5bd6b2e56d4b3789721919b1badde760288ee0
languageName: node
linkType: hard
@ -2171,8 +2172,8 @@ __metadata:
resolution: "@hono/clerk-auth@workspace:packages/clerk-auth"
dependencies:
"@arethetypeswrong/cli": "npm:^0.17.4"
"@clerk/backend": "npm:^2.2.0"
"@clerk/types": "npm:^4.61.0"
"@clerk/backend": "npm:^2.4.1"
"@clerk/types": "npm:^4.64.0"
"@types/react": "npm:^18"
hono: "npm:^4.8.4"
publint: "npm:^0.3.9"
@ -2266,7 +2267,6 @@ __metadata:
eslint-import-resolver-typescript: "npm:^4.2.2"
eslint-plugin-import-x: "npm:^4.1.1"
eslint-plugin-n: "npm:^17.10.2"
hono: "npm:^4.8.4"
typescript-eslint: "npm:^8.27.0"
peerDependencies:
eslint: ^9.0.0
@ -2734,7 +2734,7 @@ __metadata:
resolution: "@hono/zod-openapi@workspace:packages/zod-openapi"
dependencies:
"@arethetypeswrong/cli": "npm:^0.17.4"
"@asteasolutions/zod-to-openapi": "npm:^7.3.0"
"@asteasolutions/zod-to-openapi": "npm:^8.0.0"
"@hono/zod-validator": "workspace:^"
hono: "npm:^4.8.4"
openapi3-ts: "npm:^4.5.0"
@ -2743,10 +2743,10 @@ __metadata:
typescript: "npm:^5.8.2"
vitest: "npm:^3.2.4"
yaml: "npm:^2.4.3"
zod: "npm:^3.22.1"
zod: "npm:^4.0.5"
peerDependencies:
hono: ">=4.3.6"
zod: ">=3.0.0"
zod: ^4.0.0
languageName: unknown
linkType: soft
@ -2760,10 +2760,10 @@ __metadata:
tsup: "npm:^8.4.0"
typescript: "npm:^5.8.2"
vitest: "npm:^3.2.4"
zod: "npm:~3.25.6"
zod: "npm:^4.0.5"
peerDependencies:
hono: ">=3.9.0"
zod: ^3.25.0
zod: ^3.25.0 || ^4.0.0
languageName: unknown
linkType: soft
@ -4410,6 +4410,13 @@ __metadata:
languageName: node
linkType: hard
"@stablelib/base64@npm:^1.0.0":
version: 1.0.1
resolution: "@stablelib/base64@npm:1.0.1"
checksum: 10c0/6330720f021819d19cecfe274111b79a256caa81df478d6b0ae7effc8842b96915b6aeed85926ff05b4d48ec1fc78ad043d928b730ee4e6cc6e8cba6aa097bed
languageName: node
linkType: hard
"@standard-schema/spec@npm:1.0.0":
version: 1.0.0
resolution: "@standard-schema/spec@npm:1.0.0"
@ -8380,6 +8387,13 @@ __metadata:
languageName: node
linkType: hard
"fast-sha256@npm:^1.3.0":
version: 1.3.0
resolution: "fast-sha256@npm:1.3.0"
checksum: 10c0/87f9e4baa7639576cf60a2b6235c9f436e1a1c52323abbd8a705b5bea8355500acf176f2aed0c14f2ecd6d6007e26151461bab2f27b8953bcca8d9d6b76a86e4
languageName: node
linkType: hard
"fast-uri@npm:^3.0.1":
version: 3.0.6
resolution: "fast-uri@npm:3.0.6"
@ -14505,6 +14519,16 @@ __metadata:
languageName: node
linkType: hard
"standardwebhooks@npm:^1.0.0":
version: 1.0.0
resolution: "standardwebhooks@npm:1.0.0"
dependencies:
"@stablelib/base64": "npm:^1.0.0"
fast-sha256: "npm:^1.3.0"
checksum: 10c0/aee097d0f3c05172c19b80df1ed9596a2ce92f8956957650d0bbe47c2ca6d36515796b51d523333cb4a48c889b2ab130d789e7879e14975c4381bc7a61274327
languageName: node
linkType: hard
"statuses@npm:2.0.1, statuses@npm:^2.0.1":
version: 2.0.1
resolution: "statuses@npm:2.0.1"
@ -16806,7 +16830,7 @@ __metadata:
languageName: node
linkType: hard
"zod@npm:^3.20.2, zod@npm:^3.22.1, zod@npm:^3.22.3":
"zod@npm:^3.20.2, zod@npm:^3.22.3":
version: 3.24.2
resolution: "zod@npm:3.24.2"
checksum: 10c0/c638c7220150847f13ad90635b3e7d0321b36cce36f3fc6050ed960689594c949c326dfe2c6fa87c14b126ee5d370ccdebd6efb304f41ef5557a4aaca2824565
@ -16841,10 +16865,10 @@ __metadata:
languageName: node
linkType: hard
"zod@npm:~3.25.6":
version: 3.25.36
resolution: "zod@npm:3.25.36"
checksum: 10c0/9ad8ca759d1a5d19c86d6e49a1bda817b3c0566ea0a8726d45994b9fcaac8d569db45a65218a7b4067ca18b07c5fc636a587a94cd05f76793174ace8b81742e3
"zod@npm:^4.0.5":
version: 4.0.5
resolution: "zod@npm:4.0.5"
checksum: 10c0/59449d731ca63849b6bcb14300aa6e2f042d440b3ed294b45c248519aec78780f85a5d1939a62c2ce82e9dc60afca77c8005e0a98d7517b0c2586d6c76940424
languageName: node
linkType: hard