Compare commits
19 Commits
@hono/sess
...
main
Author | SHA1 | Date |
---|---|---|
|
0dc6a1f5e6 | |
|
d89fed7eec | |
|
b24925168a | |
|
2a46bfbba0 | |
|
28601b311b | |
|
5a3cbc4cc6 | |
|
e835cf6183 | |
|
2339eefa1c | |
|
7d82875634 | |
|
c6a16ab7aa | |
|
1a564d5fb6 | |
|
9216d2a666 | |
|
845e336ff4 | |
|
2a589b0728 | |
|
47cb4c8569 | |
|
3705fe6560 | |
|
9f6278f51c | |
|
55e4b19a50 | |
|
68cbb31af4 |
|
@ -54,7 +54,7 @@ jobs:
|
||||||
- uses: denoland/setup-deno@v2
|
- uses: denoland/setup-deno@v2
|
||||||
with:
|
with:
|
||||||
cache: true
|
cache: true
|
||||||
deno-version: v2.x
|
deno-version: v2.3.7
|
||||||
- run: deno install --no-lock
|
- run: deno install --no-lock
|
||||||
- run: deno publish --dry-run
|
- run: deno publish --dry-run
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# @hono/auth-js
|
# @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
|
## 1.0.17
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/auth-js",
|
"name": "@hono/auth-js",
|
||||||
"version": "1.0.17",
|
"version": "1.1.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": "./src/index.ts",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/auth-js",
|
"name": "@hono/auth-js",
|
||||||
"version": "1.0.17",
|
"version": "1.1.0",
|
||||||
"description": "A third-party Auth js middleware for Hono",
|
"description": "A third-party Auth js middleware for Hono",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
@ -47,6 +47,27 @@ describe('Config', () => {
|
||||||
expect(res.status).toBe(200)
|
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 () => {
|
it('Should return 401 is if auth cookie is invalid or missing', async () => {
|
||||||
const app = new Hono()
|
const app = new Hono()
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ export type AuthUser = {
|
||||||
|
|
||||||
export interface AuthConfig extends Omit<AuthConfigCore, 'raw'> {}
|
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 {
|
export function setEnvDefaults(env: AuthEnv, config: AuthConfig): void {
|
||||||
config.secret ??= env.AUTH_SECRET
|
config.secret ??= env.AUTH_SECRET
|
||||||
|
@ -118,7 +118,7 @@ export function verifyAuth(): MiddlewareHandler {
|
||||||
|
|
||||||
export function initAuthConfig(cb: ConfigHandler): MiddlewareHandler {
|
export function initAuthConfig(cb: ConfigHandler): MiddlewareHandler {
|
||||||
return async (c, next) => {
|
return async (c, next) => {
|
||||||
const config = cb(c)
|
const config = await cb(c)
|
||||||
c.set('authConfig', config)
|
c.set('authConfig', config)
|
||||||
await next()
|
await next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# @hono/session
|
# @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
|
## 0.1.1
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/session",
|
"name": "@hono/session",
|
||||||
"version": "0.1.1",
|
"version": "0.2.0",
|
||||||
"description": "Session middleware for Hono",
|
"description": "Session middleware for Hono",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "dist/index.js",
|
"module": "dist/index.js",
|
||||||
|
|
|
@ -939,3 +939,85 @@ describe('session events', () => {
|
||||||
expect(onUpdate).toHaveBeenCalledWith({ sub })
|
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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -104,6 +104,9 @@ export const useSession = <Data extends SessionData>(
|
||||||
get data() {
|
get data() {
|
||||||
return session.data
|
return session.data
|
||||||
},
|
},
|
||||||
|
get id() {
|
||||||
|
return session.id
|
||||||
|
},
|
||||||
delete() {
|
delete() {
|
||||||
session.delete()
|
session.delete()
|
||||||
},
|
},
|
||||||
|
|
|
@ -26,6 +26,11 @@ export interface Session<Data> {
|
||||||
*/
|
*/
|
||||||
readonly data: Data | null
|
readonly data: Data | null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current session ID.
|
||||||
|
*/
|
||||||
|
readonly id: string | null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the current session, removing the session cookie and data from storage.
|
* 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
|
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() {
|
delete() {
|
||||||
if (session) {
|
if (session) {
|
||||||
onDelete?.(session.data)
|
onDelete?.(session.data)
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# @hono/standard-validator
|
# @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
|
## 0.1.3
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -26,7 +26,12 @@ const querySortSchema = type({
|
||||||
order: "'asc'|'desc'",
|
order: "'asc'|'desc'",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const headerSchema = type({
|
||||||
|
'user-agent': 'string',
|
||||||
|
})
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
headerSchema,
|
||||||
idJSONSchema,
|
idJSONSchema,
|
||||||
personJSONSchema,
|
personJSONSchema,
|
||||||
postJSONSchema,
|
postJSONSchema,
|
||||||
|
|
|
@ -28,7 +28,12 @@ const querySortSchema = object({
|
||||||
order: picklist(['asc', 'desc']),
|
order: picklist(['asc', 'desc']),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const headerSchema = object({
|
||||||
|
'user-agent': string(),
|
||||||
|
})
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
headerSchema,
|
||||||
idJSONSchema,
|
idJSONSchema,
|
||||||
personJSONSchema,
|
personJSONSchema,
|
||||||
postJSONSchema,
|
postJSONSchema,
|
||||||
|
|
|
@ -28,7 +28,12 @@ const querySortSchema = z.object({
|
||||||
order: z.enum(['asc', 'desc']),
|
order: z.enum(['asc', 'desc']),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const headerSchema = z.object({
|
||||||
|
'user-agent': z.string(),
|
||||||
|
})
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
headerSchema,
|
||||||
idJSONSchema,
|
idJSONSchema,
|
||||||
personJSONSchema,
|
personJSONSchema,
|
||||||
postJSONSchema,
|
postJSONSchema,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/standard-validator",
|
"name": "@hono/standard-validator",
|
||||||
"version": "0.1.3",
|
"version": "0.1.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/standard-validator",
|
"name": "@hono/standard-validator",
|
||||||
"version": "0.1.3",
|
"version": "0.1.4",
|
||||||
"description": "Validator middleware using Standard Schema",
|
"description": "Validator middleware using Standard Schema",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.cjs",
|
"main": "dist/index.cjs",
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import type { StandardSchemaV1 } from '@standard-schema/spec'
|
import type { StandardSchemaV1 } from '@standard-schema/spec'
|
||||||
import type { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono'
|
import type { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono'
|
||||||
import { validator } from 'hono/validator'
|
import { validator } from 'hono/validator'
|
||||||
|
import { sanitizeIssues } from './sanitize-issues'
|
||||||
|
|
||||||
type HasUndefined<T> = undefined extends T ? true : false
|
type HasUndefined<T> = undefined extends T ? true : false
|
||||||
type TOrPromiseOfT<T> = T | Promise<T>
|
type TOrPromiseOfT<T> = T | Promise<T>
|
||||||
|
@ -21,6 +22,67 @@ type Hook<
|
||||||
c: Context<E, P>
|
c: Context<E, P>
|
||||||
) => TOrPromiseOfT<Response | void | TypedResponse<O>>
|
) => 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 = <
|
const sValidator = <
|
||||||
Schema extends StandardSchemaV1,
|
Schema extends StandardSchemaV1,
|
||||||
Target extends keyof ValidationTargets,
|
Target extends keyof ValidationTargets,
|
||||||
|
@ -71,7 +133,9 @@ const sValidator = <
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.issues) {
|
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>
|
return result.value as StandardSchemaV1.InferOutput<Schema>
|
||||||
|
|
|
@ -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[]
|
||||||
|
}
|
|
@ -1,5 +1,11 @@
|
||||||
# @hono/ua-blocker
|
# @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
|
## 0.1.5
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/ua-blocker",
|
"name": "@hono/ua-blocker",
|
||||||
"version": "0.1.5",
|
"version": "0.1.6",
|
||||||
"description": "User agent-based blocker for Hono",
|
"description": "User agent-based blocker for Hono",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "dist/index.js",
|
"module": "dist/index.js",
|
||||||
|
|
|
@ -251,6 +251,13 @@
|
||||||
"frequency": "No information.",
|
"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."
|
"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": {
|
"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.\"",
|
"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.",
|
"frequency": "No information.",
|
||||||
|
|
|
@ -35,6 +35,7 @@ User-agent: FriendlyCrawler
|
||||||
User-agent: Gemini-Deep-Research
|
User-agent: Gemini-Deep-Research
|
||||||
User-agent: Google-CloudVertexBot
|
User-agent: Google-CloudVertexBot
|
||||||
User-agent: Google-Extended
|
User-agent: Google-Extended
|
||||||
|
User-agent: GoogleAgent-Mariner
|
||||||
User-agent: GoogleOther
|
User-agent: GoogleOther
|
||||||
User-agent: GoogleOther-Image
|
User-agent: GoogleOther-Image
|
||||||
User-agent: GoogleOther-Video
|
User-agent: GoogleOther-Video
|
||||||
|
@ -87,7 +88,7 @@ User-agent: YandexAdditionalBot
|
||||||
User-agent: YouBot
|
User-agent: YouBot
|
||||||
Disallow: /
|
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", "SummalyBot", "Thinkbot", "TikTokSpider", "Timpibot", "VelenPublicWebCrawler", "WARDBot", "Webzio-Extended", "wpbot", "YandexAdditional", "YandexAdditionalBot", "YouBot"];
|
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", "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 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|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 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|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 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)/;
|
||||||
|
|
|
@ -1,5 +1,35 @@
|
||||||
# @hono/zod-openapi
|
# @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
|
## 0.19.9
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/zod-openapi",
|
"name": "@hono/zod-openapi",
|
||||||
"version": "0.19.9",
|
"version": "1.0.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/zod-openapi",
|
"name": "@hono/zod-openapi",
|
||||||
"version": "0.19.9",
|
"version": "1.0.2",
|
||||||
"description": "A wrapper class of Hono which supports OpenAPI.",
|
"description": "A wrapper class of Hono which supports OpenAPI.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "dist/index.js",
|
"module": "dist/index.js",
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
"homepage": "https://github.com/honojs/middleware",
|
"homepage": "https://github.com/honojs/middleware",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"hono": ">=4.3.6",
|
"hono": ">=4.3.6",
|
||||||
"zod": ">=3.0.0"
|
"zod": "^4.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@arethetypeswrong/cli": "^0.17.4",
|
"@arethetypeswrong/cli": "^0.17.4",
|
||||||
|
@ -51,10 +51,10 @@
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.2.4",
|
"vitest": "^3.2.4",
|
||||||
"yaml": "^2.4.3",
|
"yaml": "^2.4.3",
|
||||||
"zod": "^3.22.1"
|
"zod": "^4.0.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@asteasolutions/zod-to-openapi": "^7.3.0",
|
"@asteasolutions/zod-to-openapi": "^8.0.0",
|
||||||
"@hono/zod-validator": "workspace:^",
|
"@hono/zod-validator": "workspace:^",
|
||||||
"openapi3-ts": "^4.5.0"
|
"openapi3-ts": "^4.5.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -240,7 +240,7 @@ describe('coerce', () => {
|
||||||
type Actual = ExtractSchema<typeof routes>['/api/users/:id']['$get']['input']
|
type Actual = ExtractSchema<typeof routes>['/api/users/:id']['$get']['input']
|
||||||
type Expected = {
|
type Expected = {
|
||||||
param: {
|
param: {
|
||||||
id: number
|
id: unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type verify = Expect<Equal<Expected, Actual>>
|
type verify = Expect<Equal<Expected, Actual>>
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
OpenApiGeneratorV3,
|
OpenApiGeneratorV3,
|
||||||
OpenApiGeneratorV31,
|
OpenApiGeneratorV31,
|
||||||
extendZodWithOpenApi,
|
extendZodWithOpenApi,
|
||||||
|
getOpenApiMetadata,
|
||||||
} from '@asteasolutions/zod-to-openapi'
|
} from '@asteasolutions/zod-to-openapi'
|
||||||
import { zValidator } from '@hono/zod-validator'
|
import { zValidator } from '@hono/zod-validator'
|
||||||
import { Hono } from 'hono'
|
import { Hono } from 'hono'
|
||||||
|
@ -38,8 +39,8 @@ import type { JSONParsed, JSONValue, RemoveBlankRecord, SimplifyDeepArray } from
|
||||||
import { mergePath } from 'hono/utils/url'
|
import { mergePath } from 'hono/utils/url'
|
||||||
import type { OpenAPIObject } from 'openapi3-ts/oas30'
|
import type { OpenAPIObject } from 'openapi3-ts/oas30'
|
||||||
import type { OpenAPIObject as OpenAPIV31bject } from 'openapi3-ts/oas31'
|
import type { OpenAPIObject as OpenAPIV31bject } from 'openapi3-ts/oas31'
|
||||||
import type { ZodError, ZodSchema } from 'zod'
|
|
||||||
import { ZodType, z } from 'zod'
|
import { ZodType, z } from 'zod'
|
||||||
|
import type { ZodError } from 'zod'
|
||||||
|
|
||||||
type MaybePromise<T> = Promise<T> | T
|
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<
|
: R['request']['body']['content'][keyof R['request']['body']['content']] extends Record<
|
||||||
'schema',
|
'schema',
|
||||||
ZodSchema<any>
|
ZodType<any>
|
||||||
>
|
>
|
||||||
? {
|
? {
|
||||||
in: {
|
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<
|
: R['request']['body']['content'][keyof R['request']['body']['content']] extends Record<
|
||||||
'schema',
|
'schema',
|
||||||
ZodSchema<any>
|
ZodType<any>
|
||||||
>
|
>
|
||||||
? {
|
? {
|
||||||
in: {
|
in: {
|
||||||
|
@ -181,7 +182,7 @@ type InputTypeCookie<R extends RouteConfig> = InputTypeBase<R, 'cookies', 'cooki
|
||||||
type ExtractContent<T> = T extends {
|
type ExtractContent<T> = T extends {
|
||||||
[K in keyof T]: infer A
|
[K in keyof T]: infer A
|
||||||
}
|
}
|
||||||
? A extends Record<'schema', ZodSchema>
|
? A extends Record<'schema', ZodType>
|
||||||
? z.infer<A['schema']>
|
? z.infer<A['schema']>
|
||||||
: never
|
: never
|
||||||
: never
|
: never
|
||||||
|
@ -659,11 +660,14 @@ export class OpenAPIHono<
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'schema':
|
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':
|
case 'parameter':
|
||||||
return this.openAPIRegistry.registerParameter(
|
return this.openAPIRegistry.registerParameter(
|
||||||
def.schema._def.openapi._internal.refId,
|
getOpenApiMetadata(def.schema)._internal?.refId,
|
||||||
def.schema
|
def.schema
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,19 @@
|
||||||
# @hono/zod-validator
|
# @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
|
## 0.7.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|
|
@ -7,7 +7,7 @@ The validator middleware using [Zod](https://zod.dev) for [Hono](https://honojs.
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { z } from 'zod'
|
import * as z from 'zod'
|
||||||
import { zValidator } from '@hono/zod-validator'
|
import { zValidator } from '@hono/zod-validator'
|
||||||
|
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/zod-validator",
|
"name": "@hono/zod-validator",
|
||||||
"version": "0.7.0",
|
"version": "0.7.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts"
|
".": "./src/index.ts"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/zod-validator",
|
"name": "@hono/zod-validator",
|
||||||
"version": "0.7.0",
|
"version": "0.7.2",
|
||||||
"description": "Validator middleware using Zod",
|
"description": "Validator middleware using Zod",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
"homepage": "https://github.com/honojs/middleware",
|
"homepage": "https://github.com/honojs/middleware",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"hono": ">=3.9.0",
|
"hono": ">=3.9.0",
|
||||||
"zod": "^3.25.0"
|
"zod": "^3.25.0 || ^4.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@arethetypeswrong/cli": "^0.17.4",
|
"@arethetypeswrong/cli": "^0.17.4",
|
||||||
|
@ -51,6 +51,6 @@
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typescript": "^5.8.2",
|
"typescript": "^5.8.2",
|
||||||
"vitest": "^3.2.4",
|
"vitest": "^3.2.4",
|
||||||
"zod": "~3.25.6"
|
"zod": "^4.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import type { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono'
|
import type { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono'
|
||||||
import { validator } from 'hono/validator'
|
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 { ZodSafeParseResult as v4ZodSafeParseResult } from 'zod/v4'
|
||||||
import type * as v4 from 'zod/v4/core'
|
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)) {
|
if ((target === 'header' && '_def' in schema) || (target === 'header' && '_zod' in schema)) {
|
||||||
// create an object that maps lowercase schema keys to lowercase
|
// create an object that maps lowercase schema keys to lowercase
|
||||||
// @ts-expect-error the schema is a Zod Schema
|
// @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(
|
const caseInsensitiveKeymap = Object.fromEntries(
|
||||||
schemaKeys.map((key) => [key.toLowerCase(), key])
|
schemaKeys.map((key) => [key.toLowerCase(), key])
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
30
yarn.lock
30
yarn.lock
|
@ -99,14 +99,14 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@asteasolutions/zod-to-openapi@npm:^7.3.0":
|
"@asteasolutions/zod-to-openapi@npm:^8.0.0":
|
||||||
version: 7.3.0
|
version: 8.0.0
|
||||||
resolution: "@asteasolutions/zod-to-openapi@npm:7.3.0"
|
resolution: "@asteasolutions/zod-to-openapi@npm:8.0.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
openapi3-ts: "npm:^4.1.2"
|
openapi3-ts: "npm:^4.1.2"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
zod: ^3.20.2
|
zod: ^4.0.0
|
||||||
checksum: 10c0/f0a68a89929cdeaa3e21d2027489689f982824d676a9332c680e119f60881dd39b571324b24ad4837fda49bf6fe7c3e2af2199268b281bf1aec923d7a7cbfc40
|
checksum: 10c0/b522d074832fb137dca724c8bd4bb134c7b4d4cad12c247ed3c864f993923b3475fc06580e6e1cbc4fd8641cd361679bbe1dd87c9bb42e142bc056d96d59fbc8
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -2734,7 +2734,7 @@ __metadata:
|
||||||
resolution: "@hono/zod-openapi@workspace:packages/zod-openapi"
|
resolution: "@hono/zod-openapi@workspace:packages/zod-openapi"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@arethetypeswrong/cli": "npm:^0.17.4"
|
"@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/zod-validator": "workspace:^"
|
||||||
hono: "npm:^4.8.4"
|
hono: "npm:^4.8.4"
|
||||||
openapi3-ts: "npm:^4.5.0"
|
openapi3-ts: "npm:^4.5.0"
|
||||||
|
@ -2743,10 +2743,10 @@ __metadata:
|
||||||
typescript: "npm:^5.8.2"
|
typescript: "npm:^5.8.2"
|
||||||
vitest: "npm:^3.2.4"
|
vitest: "npm:^3.2.4"
|
||||||
yaml: "npm:^2.4.3"
|
yaml: "npm:^2.4.3"
|
||||||
zod: "npm:^3.22.1"
|
zod: "npm:^4.0.5"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
hono: ">=4.3.6"
|
hono: ">=4.3.6"
|
||||||
zod: ">=3.0.0"
|
zod: ^4.0.0
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
@ -2760,10 +2760,10 @@ __metadata:
|
||||||
tsup: "npm:^8.4.0"
|
tsup: "npm:^8.4.0"
|
||||||
typescript: "npm:^5.8.2"
|
typescript: "npm:^5.8.2"
|
||||||
vitest: "npm:^3.2.4"
|
vitest: "npm:^3.2.4"
|
||||||
zod: "npm:~3.25.6"
|
zod: "npm:^4.0.5"
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
hono: ">=3.9.0"
|
hono: ">=3.9.0"
|
||||||
zod: ^3.25.0
|
zod: ^3.25.0 || ^4.0.0
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
@ -16830,7 +16830,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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
|
version: 3.24.2
|
||||||
resolution: "zod@npm:3.24.2"
|
resolution: "zod@npm:3.24.2"
|
||||||
checksum: 10c0/c638c7220150847f13ad90635b3e7d0321b36cce36f3fc6050ed960689594c949c326dfe2c6fa87c14b126ee5d370ccdebd6efb304f41ef5557a4aaca2824565
|
checksum: 10c0/c638c7220150847f13ad90635b3e7d0321b36cce36f3fc6050ed960689594c949c326dfe2c6fa87c14b126ee5d370ccdebd6efb304f41ef5557a4aaca2824565
|
||||||
|
@ -16865,10 +16865,10 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"zod@npm:~3.25.6":
|
"zod@npm:^4.0.5":
|
||||||
version: 3.25.36
|
version: 4.0.5
|
||||||
resolution: "zod@npm:3.25.36"
|
resolution: "zod@npm:4.0.5"
|
||||||
checksum: 10c0/9ad8ca759d1a5d19c86d6e49a1bda817b3c0566ea0a8726d45994b9fcaac8d569db45a65218a7b4067ca18b07c5fc636a587a94cd05f76793174ace8b81742e3
|
checksum: 10c0/59449d731ca63849b6bcb14300aa6e2f042d440b3ed294b45c248519aec78780f85a5d1939a62c2ce82e9dc60afca77c8005e0a98d7517b0c2586d6c76940424
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue