Compare commits
9 Commits
8e60e9e80b
...
faf12ffd0b
Author | SHA1 | Date |
---|---|---|
|
faf12ffd0b | |
|
928f8cd5b8 | |
|
1765a9a3aa | |
|
247f7705b3 | |
|
8ed99d9d79 | |
|
b9fa57530a | |
|
13d07079df | |
|
7831f2bf26 | |
|
1b7e645c3d |
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@hono/arktype-validator': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Don't return restricted data fields on error responses
|
|
@ -67,7 +67,7 @@ export function ajvValidator<
|
||||||
T,
|
T,
|
||||||
Target extends keyof ValidationTargets,
|
Target extends keyof ValidationTargets,
|
||||||
E extends Env = Env,
|
E extends Env = Env,
|
||||||
P extends string = string
|
P extends string = string,
|
||||||
>(
|
>(
|
||||||
target: Target,
|
target: Target,
|
||||||
schema: JSONSchemaType<T>,
|
schema: JSONSchemaType<T>,
|
||||||
|
|
|
@ -35,6 +35,17 @@ describe('Basic', () => {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'/headers',
|
||||||
|
arktypeValidator(
|
||||||
|
'header',
|
||||||
|
type({
|
||||||
|
'User-Agent': 'string',
|
||||||
|
})
|
||||||
|
),
|
||||||
|
(c) => c.json({ success: true, userAgent: c.header('User-Agent') })
|
||||||
|
)
|
||||||
|
|
||||||
type Actual = ExtractSchema<typeof route>
|
type Actual = ExtractSchema<typeof route>
|
||||||
type Expected = {
|
type Expected = {
|
||||||
'/author': {
|
'/author': {
|
||||||
|
@ -98,6 +109,22 @@ describe('Basic', () => {
|
||||||
const data = (await res.json()) as { success: boolean }
|
const data = (await res.json()) as { success: boolean }
|
||||||
expect(data['success']).toBe(false)
|
expect(data['success']).toBe(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("doesn't return cookies after headers validation", async () => {
|
||||||
|
const req = new Request('http://localhost/headers', {
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'invalid',
|
||||||
|
Cookie: 'SECRET=123',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const res = await app.request(req)
|
||||||
|
expect(res).not.toBeNull()
|
||||||
|
expect(res.status).toBe(400)
|
||||||
|
const data = (await res.json()) as { succcess: false; errors: type.errors }
|
||||||
|
expect(data.errors).toHaveLength(1)
|
||||||
|
expect(data.errors[0].data).not.toHaveProperty('cookie')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('With Hook', () => {
|
describe('With Hook', () => {
|
||||||
|
|
|
@ -10,6 +10,10 @@ export type Hook<T, E extends Env, P extends string, O = {}> = (
|
||||||
|
|
||||||
type HasUndefined<T> = undefined extends T ? true : false
|
type HasUndefined<T> = undefined extends T ? true : false
|
||||||
|
|
||||||
|
const RESTRICTED_DATA_FIELDS = {
|
||||||
|
header: ['cookie'],
|
||||||
|
}
|
||||||
|
|
||||||
export const arktypeValidator = <
|
export const arktypeValidator = <
|
||||||
T extends Type,
|
T extends Type,
|
||||||
Target extends keyof ValidationTargets,
|
Target extends keyof ValidationTargets,
|
||||||
|
@ -23,7 +27,7 @@ export const arktypeValidator = <
|
||||||
} = {
|
} = {
|
||||||
in: HasUndefined<I> extends true ? { [K in Target]?: I } : { [K in Target]: I }
|
in: HasUndefined<I> extends true ? { [K in Target]?: I } : { [K in Target]: I }
|
||||||
out: { [K in Target]: O }
|
out: { [K in Target]: O }
|
||||||
}
|
},
|
||||||
>(
|
>(
|
||||||
target: Target,
|
target: Target,
|
||||||
schema: T,
|
schema: T,
|
||||||
|
@ -54,7 +58,31 @@ export const arktypeValidator = <
|
||||||
return c.json(
|
return c.json(
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
errors: out,
|
errors:
|
||||||
|
target in RESTRICTED_DATA_FIELDS
|
||||||
|
? out.map((error) => {
|
||||||
|
const restrictedFields =
|
||||||
|
RESTRICTED_DATA_FIELDS[target as keyof typeof RESTRICTED_DATA_FIELDS] || []
|
||||||
|
|
||||||
|
if (
|
||||||
|
error &&
|
||||||
|
typeof error === 'object' &&
|
||||||
|
'data' in error &&
|
||||||
|
typeof error.data === 'object' &&
|
||||||
|
error.data !== null &&
|
||||||
|
!Array.isArray(error.data)
|
||||||
|
) {
|
||||||
|
const dataCopy = { ...(error.data as Record<string, unknown>) }
|
||||||
|
for (const field of restrictedFields) {
|
||||||
|
delete dataCopy[field]
|
||||||
|
}
|
||||||
|
|
||||||
|
error.data = dataCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
return error
|
||||||
|
})
|
||||||
|
: out,
|
||||||
},
|
},
|
||||||
400
|
400
|
||||||
)
|
)
|
||||||
|
|
|
@ -225,7 +225,7 @@ export function SessionProvider(props: SessionProviderProps) {
|
||||||
}
|
}
|
||||||
return updatedSession
|
return updatedSession
|
||||||
},
|
},
|
||||||
} as SessionContextValue),
|
}) as SessionContextValue,
|
||||||
[session, loading, setSession]
|
[session, loading, setSession]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ type Hook<
|
||||||
E extends Env,
|
E extends Env,
|
||||||
P extends string,
|
P extends string,
|
||||||
Target extends keyof ValidationTargets = keyof ValidationTargets,
|
Target extends keyof ValidationTargets = keyof ValidationTargets,
|
||||||
O = object
|
O = object,
|
||||||
> = (
|
> = (
|
||||||
result: ({ success: true } | { success: false; errors: ValidationError[] }) & {
|
result: ({ success: true } | { success: false; errors: ValidationError[] }) & {
|
||||||
data: T
|
data: T
|
||||||
|
@ -119,19 +119,19 @@ export const classValidator = <
|
||||||
[K in Target]?: K extends 'json'
|
[K in Target]?: K extends 'json'
|
||||||
? In
|
? In
|
||||||
: HasUndefined<keyof ValidationTargets[K]> extends true
|
: HasUndefined<keyof ValidationTargets[K]> extends true
|
||||||
? { [K2 in keyof In]?: ValidationTargets[K][K2] }
|
? { [K2 in keyof In]?: ValidationTargets[K][K2] }
|
||||||
: { [K2 in keyof In]: ValidationTargets[K][K2] }
|
: { [K2 in keyof In]: ValidationTargets[K][K2] }
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
[K in Target]: K extends 'json'
|
[K in Target]: K extends 'json'
|
||||||
? In
|
? In
|
||||||
: HasUndefined<keyof ValidationTargets[K]> extends true
|
: HasUndefined<keyof ValidationTargets[K]> extends true
|
||||||
? { [K2 in keyof In]?: ValidationTargets[K][K2] }
|
? { [K2 in keyof In]?: ValidationTargets[K][K2] }
|
||||||
: { [K2 in keyof In]: ValidationTargets[K][K2] }
|
: { [K2 in keyof In]: ValidationTargets[K][K2] }
|
||||||
}
|
}
|
||||||
out: { [K in Target]: Output }
|
out: { [K in Target]: Output }
|
||||||
},
|
},
|
||||||
V extends I = I
|
V extends I = I,
|
||||||
>(
|
>(
|
||||||
target: Target,
|
target: Target,
|
||||||
dataType: T,
|
dataType: T,
|
||||||
|
|
|
@ -32,7 +32,7 @@ export const conformValidator = <
|
||||||
form: { [K in keyof In]: FormTargetValue }
|
form: { [K in keyof In]: FormTargetValue }
|
||||||
}
|
}
|
||||||
out: { form: GetSuccessSubmission<Out> }
|
out: { form: GetSuccessSubmission<Out> }
|
||||||
}
|
},
|
||||||
>(
|
>(
|
||||||
parse: F,
|
parse: F,
|
||||||
hook?: Hook<F, E, P>
|
hook?: Hook<F, E, P>
|
||||||
|
|
|
@ -19,18 +19,18 @@ export const effectValidator = <
|
||||||
[K in Target]?: K extends 'json'
|
[K in Target]?: K extends 'json'
|
||||||
? In
|
? In
|
||||||
: HasUndefined<keyof ValidationTargets[K]> extends true
|
: HasUndefined<keyof ValidationTargets[K]> extends true
|
||||||
? { [K2 in keyof In]?: ValidationTargets[K][K2] }
|
? { [K2 in keyof In]?: ValidationTargets[K][K2] }
|
||||||
: { [K2 in keyof In]: ValidationTargets[K][K2] }
|
: { [K2 in keyof In]: ValidationTargets[K][K2] }
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
[K in Target]: K extends 'json'
|
[K in Target]: K extends 'json'
|
||||||
? In
|
? In
|
||||||
: HasUndefined<keyof ValidationTargets[K]> extends true
|
: HasUndefined<keyof ValidationTargets[K]> extends true
|
||||||
? { [K2 in keyof In]?: ValidationTargets[K][K2] }
|
? { [K2 in keyof In]?: ValidationTargets[K][K2] }
|
||||||
: { [K2 in keyof In]: ValidationTargets[K][K2] }
|
: { [K2 in keyof In]: ValidationTargets[K][K2] }
|
||||||
}
|
}
|
||||||
out: { [K in Target]: Out }
|
out: { [K in Target]: Out }
|
||||||
}
|
},
|
||||||
>(
|
>(
|
||||||
target: Target,
|
target: Target,
|
||||||
schema: S.Schema<Type, Encoded, never>
|
schema: S.Schema<Type, Encoded, never>
|
||||||
|
|
|
@ -35,7 +35,7 @@ export interface Emitter<EPMap extends EventPayloadMap> {
|
||||||
export const defineHandler = <
|
export const defineHandler = <
|
||||||
EPMap extends EventPayloadMap,
|
EPMap extends EventPayloadMap,
|
||||||
Key extends keyof EPMap,
|
Key extends keyof EPMap,
|
||||||
E extends Env = Env
|
E extends Env = Env,
|
||||||
>(
|
>(
|
||||||
handler: EventHandler<EPMap[Key], E>
|
handler: EventHandler<EPMap[Key], E>
|
||||||
): EventHandler<EPMap[Key], E> => {
|
): EventHandler<EPMap[Key], E> => {
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# @hono/node-ws
|
# @hono/node-ws
|
||||||
|
|
||||||
|
## 1.1.3
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#1141](https://github.com/honojs/middleware/pull/1141) [`1765a9a3aa1ab6aa59b0efd2d161b7bfaa565ef7`](https://github.com/honojs/middleware/commit/1765a9a3aa1ab6aa59b0efd2d161b7bfaa565ef7) Thanks [@nakasyou](https://github.com/nakasyou)! - Make it uncrashable
|
||||||
|
|
||||||
## 1.1.2
|
## 1.1.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/node-ws",
|
"name": "@hono/node-ws",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "WebSocket helper for Node.js",
|
"description": "WebSocket helper for Node.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|
|
@ -214,6 +214,26 @@ describe('WebSocket helper', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should onError works well', async () => {
|
||||||
|
const mainPromise = new Promise<unknown>((resolve) =>
|
||||||
|
app.get(
|
||||||
|
'/',
|
||||||
|
upgradeWebSocket(
|
||||||
|
() => {
|
||||||
|
throw 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onError(err) {
|
||||||
|
resolve(err)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
const ws = new WebSocket('ws://localhost:3030/')
|
||||||
|
expect(await mainPromise).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
describe('Types', () => {
|
describe('Types', () => {
|
||||||
it('Should not throw a type error with an app with Variables generics', () => {
|
it('Should not throw a type error with an app with Variables generics', () => {
|
||||||
const app = new Hono<{
|
const app = new Hono<{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Hono } from 'hono'
|
import type { Hono } from 'hono'
|
||||||
import type { UpgradeWebSocket, WSContext } from 'hono/ws'
|
import type { UpgradeWebSocket, WSContext, WSEvents } from 'hono/ws'
|
||||||
import type { WebSocket } from 'ws'
|
import type { WebSocket } from 'ws'
|
||||||
import { WebSocketServer } from 'ws'
|
import { WebSocketServer } from 'ws'
|
||||||
import type { IncomingMessage } from 'http'
|
import type { IncomingMessage } from 'http'
|
||||||
|
@ -9,7 +9,12 @@ import type { Duplex } from 'node:stream'
|
||||||
import { CloseEvent } from './events'
|
import { CloseEvent } from './events'
|
||||||
|
|
||||||
export interface NodeWebSocket {
|
export interface NodeWebSocket {
|
||||||
upgradeWebSocket: UpgradeWebSocket<WebSocket>
|
upgradeWebSocket: UpgradeWebSocket<
|
||||||
|
WebSocket,
|
||||||
|
{
|
||||||
|
onError: (err: unknown) => void
|
||||||
|
}
|
||||||
|
>
|
||||||
injectWebSocket(server: Server | Http2Server | Http2SecureServer): void
|
injectWebSocket(server: Server | Http2Server | Http2SecureServer): void
|
||||||
}
|
}
|
||||||
export interface NodeWebSocketInit {
|
export interface NodeWebSocketInit {
|
||||||
|
@ -80,7 +85,7 @@ export const createNodeWebSocket = (init: NodeWebSocketInit): NodeWebSocket => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
upgradeWebSocket: (createEvents) =>
|
upgradeWebSocket: (createEvents, options) =>
|
||||||
async function upgradeWebSocket(c, next) {
|
async function upgradeWebSocket(c, next) {
|
||||||
if (c.req.header('upgrade')?.toLowerCase() !== 'websocket') {
|
if (c.req.header('upgrade')?.toLowerCase() !== 'websocket') {
|
||||||
// Not websocket
|
// Not websocket
|
||||||
|
@ -91,7 +96,14 @@ export const createNodeWebSocket = (init: NodeWebSocketInit): NodeWebSocket => {
|
||||||
const response = new Response()
|
const response = new Response()
|
||||||
;(async () => {
|
;(async () => {
|
||||||
const ws = await nodeUpgradeWebSocket(c.env.incoming, response)
|
const ws = await nodeUpgradeWebSocket(c.env.incoming, response)
|
||||||
const events = await createEvents(c)
|
let events: WSEvents<WebSocket>
|
||||||
|
try {
|
||||||
|
events = await createEvents(c)
|
||||||
|
} catch (e) {
|
||||||
|
;(options?.onError ?? console.error)(e)
|
||||||
|
ws.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const ctx: WSContext<WebSocket> = {
|
const ctx: WSContext<WebSocket> = {
|
||||||
binaryType: 'arraybuffer',
|
binaryType: 'arraybuffer',
|
||||||
|
@ -110,32 +122,48 @@ export const createNodeWebSocket = (init: NodeWebSocketInit): NodeWebSocket => {
|
||||||
},
|
},
|
||||||
url: new URL(c.req.url),
|
url: new URL(c.req.url),
|
||||||
}
|
}
|
||||||
events.onOpen?.(new Event('open'), ctx)
|
try {
|
||||||
|
events?.onOpen?.(new Event('open'), ctx)
|
||||||
|
} catch (e) {
|
||||||
|
;(options?.onError ?? console.error)(e)
|
||||||
|
}
|
||||||
ws.on('message', (data, isBinary) => {
|
ws.on('message', (data, isBinary) => {
|
||||||
const datas = Array.isArray(data) ? data : [data]
|
const datas = Array.isArray(data) ? data : [data]
|
||||||
for (const data of datas) {
|
for (const data of datas) {
|
||||||
events.onMessage?.(
|
try {
|
||||||
new MessageEvent('message', {
|
events?.onMessage?.(
|
||||||
data: isBinary
|
new MessageEvent('message', {
|
||||||
? data instanceof ArrayBuffer
|
data: isBinary
|
||||||
? data
|
? data instanceof ArrayBuffer
|
||||||
: data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
|
? data
|
||||||
: data.toString('utf-8'),
|
: data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
|
||||||
}),
|
: data.toString('utf-8'),
|
||||||
ctx
|
}),
|
||||||
)
|
ctx
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
;(options?.onError ?? console.error)(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
ws.on('close', (code, reason) => {
|
ws.on('close', (code, reason) => {
|
||||||
events.onClose?.(new CloseEvent('close', { code, reason: reason.toString() }), ctx)
|
try {
|
||||||
|
events?.onClose?.(new CloseEvent('close', { code, reason: reason.toString() }), ctx)
|
||||||
|
} catch (e) {
|
||||||
|
;(options?.onError ?? console.error)(e)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
ws.on('error', (error) => {
|
ws.on('error', (error) => {
|
||||||
events.onError?.(
|
try {
|
||||||
new ErrorEvent('error', {
|
events?.onError?.(
|
||||||
error: error,
|
new ErrorEvent('error', {
|
||||||
}),
|
error: error,
|
||||||
ctx
|
}),
|
||||||
)
|
ctx
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
;(options?.onError ?? console.error)(e)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,7 @@ export interface TwitchUserResponse {
|
||||||
view_count: number
|
view_count: number
|
||||||
email: string
|
email: string
|
||||||
created_at: string
|
created_at: string
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ const getMetricConstructor = (type: MetricOptions['type']) =>
|
||||||
({
|
({
|
||||||
counter: Counter,
|
counter: Counter,
|
||||||
histogram: Histogram,
|
histogram: Histogram,
|
||||||
}[type])
|
})[type]
|
||||||
|
|
||||||
export const createStandardMetrics = ({
|
export const createStandardMetrics = ({
|
||||||
registry,
|
registry,
|
||||||
|
|
|
@ -9,9 +9,8 @@ import * as zodSchemas from './__schemas__/zod'
|
||||||
import { sValidator } from '.'
|
import { sValidator } from '.'
|
||||||
|
|
||||||
type ExtractSchema<T> = T extends Hono<infer _, infer S> ? S : never
|
type ExtractSchema<T> = T extends Hono<infer _, infer S> ? S : never
|
||||||
type MergeDiscriminatedUnion<U> = UnionToIntersection<U> extends infer O
|
type MergeDiscriminatedUnion<U> =
|
||||||
? { [K in keyof O]: O[K] }
|
UnionToIntersection<U> extends infer O ? { [K in keyof O]: O[K] } : never
|
||||||
: never
|
|
||||||
|
|
||||||
const libs = ['valibot', 'zod', 'arktype'] as const
|
const libs = ['valibot', 'zod', 'arktype'] as const
|
||||||
const schemasByLibrary = {
|
const schemasByLibrary = {
|
||||||
|
|
|
@ -10,7 +10,7 @@ type Hook<
|
||||||
E extends Env,
|
E extends Env,
|
||||||
P extends string,
|
P extends string,
|
||||||
Target extends keyof ValidationTargets = keyof ValidationTargets,
|
Target extends keyof ValidationTargets = keyof ValidationTargets,
|
||||||
O = {}
|
O = {},
|
||||||
> = (
|
> = (
|
||||||
result: (
|
result: (
|
||||||
| { success: true; data: T }
|
| { success: true; data: T }
|
||||||
|
@ -42,7 +42,7 @@ const sValidator = <
|
||||||
}
|
}
|
||||||
out: { [K in Target]: Out }
|
out: { [K in Target]: Out }
|
||||||
},
|
},
|
||||||
V extends I = I
|
V extends I = I,
|
||||||
>(
|
>(
|
||||||
target: Target,
|
target: Target,
|
||||||
schema: Schema,
|
schema: Schema,
|
||||||
|
|
|
@ -148,7 +148,7 @@ export const renderSwaggerUIOptions = (options: DistSwaggerUIOptions) => {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(item => item !== '')
|
.filter((item) => item !== '')
|
||||||
.join(',')
|
.join(',')
|
||||||
|
|
||||||
return optionsStrings
|
return optionsStrings
|
||||||
|
|
|
@ -68,7 +68,7 @@ export function tbValidator<
|
||||||
V extends {
|
V extends {
|
||||||
in: { [K in Target]: Static<T> }
|
in: { [K in Target]: Static<T> }
|
||||||
out: { [K in Target]: ExcludeResponseType<Static<T>> }
|
out: { [K in Target]: ExcludeResponseType<Static<T>> }
|
||||||
}
|
},
|
||||||
>(
|
>(
|
||||||
target: Target,
|
target: Target,
|
||||||
schema: T,
|
schema: T,
|
||||||
|
|
|
@ -11,54 +11,57 @@ interface IFailure<T> {
|
||||||
type BaseType<T> = T extends string
|
type BaseType<T> = T extends string
|
||||||
? string
|
? string
|
||||||
: T extends number
|
: T extends number
|
||||||
? number
|
? number
|
||||||
: T extends boolean
|
: T extends boolean
|
||||||
? boolean
|
? boolean
|
||||||
: T extends symbol
|
: T extends symbol
|
||||||
? symbol
|
? symbol
|
||||||
: T extends bigint
|
: T extends bigint
|
||||||
? bigint
|
? bigint
|
||||||
: T
|
: T
|
||||||
type Parsed<T> = T extends Record<string | number, any>
|
type Parsed<T> =
|
||||||
? {
|
T extends Record<string | number, any>
|
||||||
[K in keyof T]-?: T[K] extends (infer U)[]
|
? {
|
||||||
? (BaseType<U> | null | undefined)[] | undefined
|
[K in keyof T]-?: T[K] extends (infer U)[]
|
||||||
: BaseType<T[K]> | null | undefined
|
? (BaseType<U> | null | undefined)[] | undefined
|
||||||
}
|
: BaseType<T[K]> | null | undefined
|
||||||
: BaseType<T>
|
}
|
||||||
|
: BaseType<T>
|
||||||
|
|
||||||
export type QueryValidation<O extends Record<string | number, any> = any> = (
|
export type QueryValidation<O extends Record<string | number, any> = any> = (
|
||||||
input: string | URLSearchParams
|
input: string | URLSearchParams
|
||||||
) => IValidation<O>
|
) => IValidation<O>
|
||||||
export type QueryOutputType<T> = T extends QueryValidation<infer O> ? O : never
|
export type QueryOutputType<T> = T extends QueryValidation<infer O> ? O : never
|
||||||
type QueryStringify<T> = T extends Record<string | number, any>
|
type QueryStringify<T> =
|
||||||
? {
|
T extends Record<string | number, any>
|
||||||
// Suppress to split union types
|
? {
|
||||||
[K in keyof T]: [T[K]] extends [bigint | number | boolean]
|
// Suppress to split union types
|
||||||
? `${T[K]}`
|
[K in keyof T]: [T[K]] extends [bigint | number | boolean]
|
||||||
: T[K] extends (infer U)[]
|
? `${T[K]}`
|
||||||
? [U] extends [bigint | number | boolean]
|
: T[K] extends (infer U)[]
|
||||||
? `${U}`[]
|
? [U] extends [bigint | number | boolean]
|
||||||
: T[K]
|
? `${U}`[]
|
||||||
: T[K]
|
: T[K]
|
||||||
}
|
: T[K]
|
||||||
: T
|
}
|
||||||
|
: T
|
||||||
export type HeaderValidation<O extends Record<string | number, any> = any> = (
|
export type HeaderValidation<O extends Record<string | number, any> = any> = (
|
||||||
input: Record<string, string | string[] | undefined>
|
input: Record<string, string | string[] | undefined>
|
||||||
) => IValidation<O>
|
) => IValidation<O>
|
||||||
export type HeaderOutputType<T> = T extends HeaderValidation<infer O> ? O : never
|
export type HeaderOutputType<T> = T extends HeaderValidation<infer O> ? O : never
|
||||||
type HeaderStringify<T> = T extends Record<string | number, any>
|
type HeaderStringify<T> =
|
||||||
? {
|
T extends Record<string | number, any>
|
||||||
// Suppress to split union types
|
? {
|
||||||
[K in keyof T]: [T[K]] extends [bigint | number | boolean]
|
// Suppress to split union types
|
||||||
? `${T[K]}`
|
[K in keyof T]: [T[K]] extends [bigint | number | boolean]
|
||||||
: T[K] extends (infer U)[]
|
? `${T[K]}`
|
||||||
? [U] extends [bigint | number | boolean]
|
: T[K] extends (infer U)[]
|
||||||
? `${U}`
|
? [U] extends [bigint | number | boolean]
|
||||||
: U
|
? `${U}`
|
||||||
: T[K]
|
: U
|
||||||
}
|
: T[K]
|
||||||
: T
|
}
|
||||||
|
: T
|
||||||
|
|
||||||
export type HttpHook<T, E extends Env, P extends string, O = {}> = (
|
export type HttpHook<T, E extends Env, P extends string, O = {}> = (
|
||||||
result: IValidation.ISuccess<T> | IFailure<Parsed<T>>,
|
result: IValidation.ISuccess<T> | IFailure<Parsed<T>>,
|
||||||
|
@ -82,7 +85,7 @@ interface TypiaValidator {
|
||||||
V extends { in: { query: QueryStringify<O> }; out: { query: O } } = {
|
V extends { in: { query: QueryStringify<O> }; out: { query: O } } = {
|
||||||
in: { query: QueryStringify<O> }
|
in: { query: QueryStringify<O> }
|
||||||
out: { query: O }
|
out: { query: O }
|
||||||
}
|
},
|
||||||
>(
|
>(
|
||||||
target: 'query',
|
target: 'query',
|
||||||
validate: T,
|
validate: T,
|
||||||
|
@ -97,7 +100,7 @@ interface TypiaValidator {
|
||||||
V extends { in: { header: HeaderStringify<O> }; out: { header: O } } = {
|
V extends { in: { header: HeaderStringify<O> }; out: { header: O } } = {
|
||||||
in: { header: HeaderStringify<O> }
|
in: { header: HeaderStringify<O> }
|
||||||
out: { header: O }
|
out: { header: O }
|
||||||
}
|
},
|
||||||
>(
|
>(
|
||||||
target: 'header',
|
target: 'header',
|
||||||
validate: T,
|
validate: T,
|
||||||
|
@ -116,7 +119,7 @@ interface TypiaValidator {
|
||||||
} = {
|
} = {
|
||||||
in: { [K in Target]: O }
|
in: { [K in Target]: O }
|
||||||
out: { [K in Target]: O }
|
out: { [K in Target]: O }
|
||||||
}
|
},
|
||||||
>(
|
>(
|
||||||
target: Target,
|
target: Target,
|
||||||
validate: T,
|
validate: T,
|
||||||
|
|
|
@ -23,7 +23,7 @@ export const typiaValidator = <
|
||||||
} = {
|
} = {
|
||||||
in: { [K in Target]: O }
|
in: { [K in Target]: O }
|
||||||
out: { [K in Target]: O }
|
out: { [K in Target]: O }
|
||||||
}
|
},
|
||||||
>(
|
>(
|
||||||
target: Target,
|
target: Target,
|
||||||
validate: T,
|
validate: T,
|
||||||
|
|
|
@ -14,7 +14,7 @@ export type Hook<
|
||||||
E extends Env,
|
E extends Env,
|
||||||
P extends string,
|
P extends string,
|
||||||
Target extends keyof ValidationTargets = keyof ValidationTargets,
|
Target extends keyof ValidationTargets = keyof ValidationTargets,
|
||||||
O = {}
|
O = {},
|
||||||
> = (
|
> = (
|
||||||
result: SafeParseResult<T> & {
|
result: SafeParseResult<T> & {
|
||||||
target: Target
|
target: Target
|
||||||
|
@ -45,7 +45,7 @@ export const vValidator = <
|
||||||
}
|
}
|
||||||
out: { [K in Target]: Out }
|
out: { [K in Target]: Out }
|
||||||
},
|
},
|
||||||
V extends I = I
|
V extends I = I,
|
||||||
>(
|
>(
|
||||||
target: Target,
|
target: Target,
|
||||||
schema: T,
|
schema: T,
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
# @hono/zod-openapi
|
# @hono/zod-openapi
|
||||||
|
|
||||||
|
## 0.19.6
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [[`8ed99d9d791ed6bd8b897c705289b0464947e632`](https://github.com/honojs/middleware/commit/8ed99d9d791ed6bd8b897c705289b0464947e632)]:
|
||||||
|
- @hono/zod-validator@0.5.0
|
||||||
|
|
||||||
## 0.19.5
|
## 0.19.5
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/zod-openapi",
|
"name": "@hono/zod-openapi",
|
||||||
"version": "0.19.5",
|
"version": "0.19.6",
|
||||||
"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",
|
||||||
|
|
|
@ -73,7 +73,7 @@ type IsForm<T> = T extends string
|
||||||
type ReturnJsonOrTextOrResponse<
|
type ReturnJsonOrTextOrResponse<
|
||||||
ContentType,
|
ContentType,
|
||||||
Content,
|
Content,
|
||||||
Status extends keyof StatusCodeRangeDefinitions | StatusCode
|
Status extends keyof StatusCodeRangeDefinitions | StatusCode,
|
||||||
> = ContentType extends string
|
> = ContentType extends string
|
||||||
? ContentType extends `application/${infer Start}json${infer _End}`
|
? ContentType extends `application/${infer Start}json${infer _End}`
|
||||||
? Start extends '' | `${string}+` | `vnd.${string}+`
|
? Start extends '' | `${string}+` | `vnd.${string}+`
|
||||||
|
@ -88,8 +88,8 @@ type ReturnJsonOrTextOrResponse<
|
||||||
>
|
>
|
||||||
: never
|
: never
|
||||||
: ContentType extends `text/plain${infer _Rest}`
|
: ContentType extends `text/plain${infer _Rest}`
|
||||||
? TypedResponse<Content, ExtractStatusCode<Status>, 'text'>
|
? TypedResponse<Content, ExtractStatusCode<Status>, 'text'>
|
||||||
: Response
|
: Response
|
||||||
: never
|
: never
|
||||||
|
|
||||||
type RequestPart<R extends RouteConfig, Part extends string> = Part extends keyof R['request']
|
type RequestPart<R extends RouteConfig, Part extends string> = Part extends keyof R['request']
|
||||||
|
@ -101,7 +101,7 @@ type HasUndefined<T> = undefined extends T ? true : false
|
||||||
type InputTypeBase<
|
type InputTypeBase<
|
||||||
R extends RouteConfig,
|
R extends RouteConfig,
|
||||||
Part extends string,
|
Part extends string,
|
||||||
Type extends keyof ValidationTargets
|
Type extends keyof ValidationTargets,
|
||||||
> = R['request'] extends RequestTypes
|
> = R['request'] extends RequestTypes
|
||||||
? RequestPart<R, Part> extends ZodType
|
? RequestPart<R, Part> extends ZodType
|
||||||
? {
|
? {
|
||||||
|
@ -125,22 +125,22 @@ type InputTypeJson<R extends RouteConfig> = R['request'] extends RequestTypes
|
||||||
? IsJson<keyof R['request']['body']['content']> extends never
|
? IsJson<keyof R['request']['body']['content']> extends never
|
||||||
? {}
|
? {}
|
||||||
: 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>
|
ZodSchema<any>
|
||||||
>
|
>
|
||||||
? {
|
? {
|
||||||
in: {
|
in: {
|
||||||
json: z.input<
|
json: z.input<
|
||||||
R['request']['body']['content'][keyof R['request']['body']['content']]['schema']
|
R['request']['body']['content'][keyof R['request']['body']['content']]['schema']
|
||||||
>
|
>
|
||||||
|
}
|
||||||
|
out: {
|
||||||
|
json: z.output<
|
||||||
|
R['request']['body']['content'][keyof R['request']['body']['content']]['schema']
|
||||||
|
>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
out: {
|
: {}
|
||||||
json: z.output<
|
|
||||||
R['request']['body']['content'][keyof R['request']['body']['content']]['schema']
|
|
||||||
>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
: {}
|
: {}
|
||||||
: {}
|
: {}
|
||||||
: {}
|
: {}
|
||||||
|
@ -151,22 +151,22 @@ type InputTypeForm<R extends RouteConfig> = R['request'] extends RequestTypes
|
||||||
? IsForm<keyof R['request']['body']['content']> extends never
|
? IsForm<keyof R['request']['body']['content']> extends never
|
||||||
? {}
|
? {}
|
||||||
: 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>
|
ZodSchema<any>
|
||||||
>
|
>
|
||||||
? {
|
? {
|
||||||
in: {
|
in: {
|
||||||
form: z.input<
|
form: z.input<
|
||||||
R['request']['body']['content'][keyof R['request']['body']['content']]['schema']
|
R['request']['body']['content'][keyof R['request']['body']['content']]['schema']
|
||||||
>
|
>
|
||||||
|
}
|
||||||
|
out: {
|
||||||
|
form: z.output<
|
||||||
|
R['request']['body']['content'][keyof R['request']['body']['content']]['schema']
|
||||||
|
>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
out: {
|
: {}
|
||||||
form: z.output<
|
|
||||||
R['request']['body']['content'][keyof R['request']['body']['content']]['schema']
|
|
||||||
>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: {}
|
|
||||||
: {}
|
: {}
|
||||||
: {}
|
: {}
|
||||||
: {}
|
: {}
|
||||||
|
@ -249,8 +249,8 @@ type HonoInit<E extends Env> = ConstructorParameters<typeof Hono>[0] & OpenAPIHo
|
||||||
type AsArray<T> = T extends undefined // TODO move to utils?
|
type AsArray<T> = T extends undefined // TODO move to utils?
|
||||||
? []
|
? []
|
||||||
: T extends any[]
|
: T extends any[]
|
||||||
? T
|
? T
|
||||||
: [T]
|
: [T]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Like simplify but recursive
|
* Like simplify but recursive
|
||||||
|
@ -265,17 +265,14 @@ export type DeepSimplify<T> = {
|
||||||
/**
|
/**
|
||||||
* Helper to infer generics from {@link MiddlewareHandler}
|
* Helper to infer generics from {@link MiddlewareHandler}
|
||||||
*/
|
*/
|
||||||
export type OfHandlerType<T extends MiddlewareHandler> = T extends MiddlewareHandler<
|
export type OfHandlerType<T extends MiddlewareHandler> =
|
||||||
infer E,
|
T extends MiddlewareHandler<infer E, infer P, infer I>
|
||||||
infer P,
|
? {
|
||||||
infer I
|
env: E
|
||||||
>
|
path: P
|
||||||
? {
|
input: I
|
||||||
env: E
|
}
|
||||||
path: P
|
: never
|
||||||
input: I
|
|
||||||
}
|
|
||||||
: never
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduce a tuple of middleware handlers into a single
|
* Reduce a tuple of middleware handlers into a single
|
||||||
|
@ -285,7 +282,7 @@ export type OfHandlerType<T extends MiddlewareHandler> = T extends MiddlewareHan
|
||||||
export type MiddlewareToHandlerType<M extends MiddlewareHandler<any, any, any>[]> = M extends [
|
export type MiddlewareToHandlerType<M extends MiddlewareHandler<any, any, any>[]> = M extends [
|
||||||
infer First,
|
infer First,
|
||||||
infer Second,
|
infer Second,
|
||||||
...infer Rest
|
...infer Rest,
|
||||||
]
|
]
|
||||||
? First extends MiddlewareHandler<any, any, any>
|
? First extends MiddlewareHandler<any, any, any>
|
||||||
? Second extends MiddlewareHandler<any, any, any>
|
? Second extends MiddlewareHandler<any, any, any>
|
||||||
|
@ -297,23 +294,22 @@ export type MiddlewareToHandlerType<M extends MiddlewareHandler<any, any, any>[]
|
||||||
OfHandlerType<First>['path'], // Keep path from First
|
OfHandlerType<First>['path'], // Keep path from First
|
||||||
OfHandlerType<First>['input'] // Keep input from First
|
OfHandlerType<First>['input'] // Keep input from First
|
||||||
>,
|
>,
|
||||||
...Rest
|
...Rest,
|
||||||
]
|
]
|
||||||
>
|
>
|
||||||
: never
|
: never
|
||||||
: never
|
: never
|
||||||
: never
|
: never
|
||||||
: M extends [infer Last]
|
: M extends [infer Last]
|
||||||
? Last // Return the last remaining handler in the array
|
? Last // Return the last remaining handler in the array
|
||||||
: MiddlewareHandler<Env>
|
: MiddlewareHandler<Env>
|
||||||
|
|
||||||
type RouteMiddlewareParams<R extends RouteConfig> = OfHandlerType<
|
type RouteMiddlewareParams<R extends RouteConfig> = OfHandlerType<
|
||||||
MiddlewareToHandlerType<AsArray<R['middleware']>>
|
MiddlewareToHandlerType<AsArray<R['middleware']>>
|
||||||
>
|
>
|
||||||
|
|
||||||
export type RouteConfigToEnv<R extends RouteConfig> = RouteMiddlewareParams<R> extends never
|
export type RouteConfigToEnv<R extends RouteConfig> =
|
||||||
? Env
|
RouteMiddlewareParams<R> extends never ? Env : RouteMiddlewareParams<R>['env']
|
||||||
: RouteMiddlewareParams<R>['env']
|
|
||||||
|
|
||||||
export type RouteHandler<
|
export type RouteHandler<
|
||||||
R extends RouteConfig,
|
R extends RouteConfig,
|
||||||
|
@ -324,7 +320,7 @@ export type RouteHandler<
|
||||||
InputTypeCookie<R> &
|
InputTypeCookie<R> &
|
||||||
InputTypeForm<R> &
|
InputTypeForm<R> &
|
||||||
InputTypeJson<R>,
|
InputTypeJson<R>,
|
||||||
P extends string = ConvertPathType<R['path']>
|
P extends string = ConvertPathType<R['path']>,
|
||||||
> = Handler<
|
> = Handler<
|
||||||
E,
|
E,
|
||||||
P,
|
P,
|
||||||
|
@ -352,7 +348,7 @@ export type RouteHook<
|
||||||
InputTypeCookie<R> &
|
InputTypeCookie<R> &
|
||||||
InputTypeForm<R> &
|
InputTypeForm<R> &
|
||||||
InputTypeJson<R>,
|
InputTypeJson<R>,
|
||||||
P extends string = ConvertPathType<R['path']>
|
P extends string = ConvertPathType<R['path']>,
|
||||||
> = Hook<
|
> = Hook<
|
||||||
I,
|
I,
|
||||||
E,
|
E,
|
||||||
|
@ -371,7 +367,7 @@ export type OpenAPIObjectConfigure<E extends Env, P extends string> =
|
||||||
export class OpenAPIHono<
|
export class OpenAPIHono<
|
||||||
E extends Env = Env,
|
E extends Env = Env,
|
||||||
S extends Schema = {},
|
S extends Schema = {},
|
||||||
BasePath extends string = '/'
|
BasePath extends string = '/',
|
||||||
> extends Hono<E, S, BasePath> {
|
> extends Hono<E, S, BasePath> {
|
||||||
openAPIRegistry: OpenAPIRegistry
|
openAPIRegistry: OpenAPIRegistry
|
||||||
defaultHook?: OpenAPIHonoOptions<E>['defaultHook']
|
defaultHook?: OpenAPIHonoOptions<E>['defaultHook']
|
||||||
|
@ -421,7 +417,7 @@ export class OpenAPIHono<
|
||||||
InputTypeCookie<R> &
|
InputTypeCookie<R> &
|
||||||
InputTypeForm<R> &
|
InputTypeForm<R> &
|
||||||
InputTypeJson<R>,
|
InputTypeJson<R>,
|
||||||
P extends string = ConvertPathType<R['path']>
|
P extends string = ConvertPathType<R['path']>,
|
||||||
>(
|
>(
|
||||||
{ middleware: routeMiddleware, hide, ...route }: R,
|
{ middleware: routeMiddleware, hide, ...route }: R,
|
||||||
handler: Handler<
|
handler: Handler<
|
||||||
|
@ -609,7 +605,7 @@ export class OpenAPIHono<
|
||||||
SubPath extends string,
|
SubPath extends string,
|
||||||
SubEnv extends Env,
|
SubEnv extends Env,
|
||||||
SubSchema extends Schema,
|
SubSchema extends Schema,
|
||||||
SubBasePath extends string
|
SubBasePath extends string,
|
||||||
>(
|
>(
|
||||||
path: SubPath,
|
path: SubPath,
|
||||||
app: Hono<SubEnv, SubSchema, SubBasePath>
|
app: Hono<SubEnv, SubSchema, SubBasePath>
|
||||||
|
@ -619,7 +615,7 @@ export class OpenAPIHono<
|
||||||
SubPath extends string,
|
SubPath extends string,
|
||||||
SubEnv extends Env,
|
SubEnv extends Env,
|
||||||
SubSchema extends Schema,
|
SubSchema extends Schema,
|
||||||
SubBasePath extends string
|
SubBasePath extends string,
|
||||||
>(
|
>(
|
||||||
path: SubPath,
|
path: SubPath,
|
||||||
app?: Hono<SubEnv, SubSchema, SubBasePath>
|
app?: Hono<SubEnv, SubSchema, SubBasePath>
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
# @hono/zod-validator
|
# @hono/zod-validator
|
||||||
|
|
||||||
|
## 0.5.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- [#1140](https://github.com/honojs/middleware/pull/1140) [`8ed99d9d791ed6bd8b897c705289b0464947e632`](https://github.com/honojs/middleware/commit/8ed99d9d791ed6bd8b897c705289b0464947e632) Thanks [@yusukebe](https://github.com/yusukebe)! - feat: add `validationFunction` option
|
||||||
|
|
||||||
## 0.4.3
|
## 0.4.3
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -68,6 +68,30 @@ app.post(
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Custom validation function
|
||||||
|
|
||||||
|
By default, this Validator validates values using `.safeParseAsync`.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await schema.safeParseAsync(value)
|
||||||
|
```
|
||||||
|
|
||||||
|
But, if you want to use the [`.passthrough`](https://zod.dev/?id=passthrough), you can specify your own function in `validationFunction`.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
app.post(
|
||||||
|
'/',
|
||||||
|
zValidator('json', schema, undefined, {
|
||||||
|
validationFunction: async (schema, value) => {
|
||||||
|
return await schema.passthrough().safeParseAsync(value)
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
(c) => {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||
|
|
||||||
Yusuke Wada <https://github.com/yusukebe>
|
Yusuke Wada <https://github.com/yusukebe>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@hono/zod-validator",
|
"name": "@hono/zod-validator",
|
||||||
"version": "0.4.3",
|
"version": "0.5.0",
|
||||||
"description": "Validator middleware using Zod",
|
"description": "Validator middleware using Zod",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|
|
@ -378,3 +378,85 @@ describe('Case-Insensitive Headers', () => {
|
||||||
type verify = Expect<Equal<Expected, Actual>>
|
type verify = Expect<Equal<Expected, Actual>>
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('With options + validationFunction', () => {
|
||||||
|
const app = new Hono()
|
||||||
|
const jsonSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
age: z.number(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = app
|
||||||
|
.post('/', zValidator('json', jsonSchema), (c) => {
|
||||||
|
const data = c.req.valid('json')
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.post(
|
||||||
|
'/extended',
|
||||||
|
zValidator('json', jsonSchema, undefined, {
|
||||||
|
validationFunction: async (schema, value) => {
|
||||||
|
return await schema.passthrough().safeParseAsync(value)
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
(c) => {
|
||||||
|
const data = c.req.valid('json')
|
||||||
|
return c.json({
|
||||||
|
success: true,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
it('Should be ok due to passthrough schema', async () => {
|
||||||
|
const req = new Request('http://localhost/extended', {
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: 'Superman',
|
||||||
|
age: 20,
|
||||||
|
length: 170,
|
||||||
|
weight: 55,
|
||||||
|
}),
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const res = await app.request(req)
|
||||||
|
expect(res).not.toBeNull()
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
expect(await res.json()).toEqual({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
name: 'Superman',
|
||||||
|
age: 20,
|
||||||
|
length: 170,
|
||||||
|
weight: 55,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it('Should be ok due to required schema', async () => {
|
||||||
|
const req = new Request('http://localhost', {
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: 'Superman',
|
||||||
|
age: 20,
|
||||||
|
}),
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const res = await app.request(req)
|
||||||
|
|
||||||
|
expect(res).not.toBeNull()
|
||||||
|
expect(res.status).toBe(200)
|
||||||
|
expect(await res.json()).toEqual({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
name: 'Superman',
|
||||||
|
age: 20,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
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 { ZodObject } from 'zod'
|
import { ZodObject } from 'zod'
|
||||||
import type { ZodError, ZodSchema, z } from 'zod'
|
import type { SafeParseReturnType, ZodError, ZodSchema, z } from 'zod'
|
||||||
|
|
||||||
export type Hook<
|
export type Hook<
|
||||||
T,
|
T,
|
||||||
E extends Env,
|
E extends Env,
|
||||||
P extends string,
|
P extends string,
|
||||||
Target extends keyof ValidationTargets = keyof ValidationTargets,
|
Target extends keyof ValidationTargets = keyof ValidationTargets,
|
||||||
O = {}
|
O = {},
|
||||||
> = (
|
> = (
|
||||||
result: ({ success: true; data: T } | { success: false; error: ZodError; data: T }) & {
|
result: ({ success: true; data: T } | { success: false; error: ZodError; data: T }) & {
|
||||||
target: Target
|
target: Target
|
||||||
|
@ -39,11 +39,18 @@ export const zValidator = <
|
||||||
}
|
}
|
||||||
out: { [K in Target]: Out }
|
out: { [K in Target]: Out }
|
||||||
},
|
},
|
||||||
V extends I = I
|
V extends I = I,
|
||||||
>(
|
>(
|
||||||
target: Target,
|
target: Target,
|
||||||
schema: T,
|
schema: T,
|
||||||
hook?: Hook<z.infer<T>, E, P, Target>
|
hook?: Hook<z.infer<T>, E, P, Target>,
|
||||||
|
options?: {
|
||||||
|
validationFunction: (
|
||||||
|
schema: T,
|
||||||
|
value: ValidationTargets[Target]
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
) => SafeParseReturnType<any, any> | Promise<SafeParseReturnType<any, any>>
|
||||||
|
}
|
||||||
): MiddlewareHandler<E, P, V> =>
|
): MiddlewareHandler<E, P, V> =>
|
||||||
// @ts-expect-error not typed well
|
// @ts-expect-error not typed well
|
||||||
validator(target, async (value, c) => {
|
validator(target, async (value, c) => {
|
||||||
|
@ -63,7 +70,10 @@ export const zValidator = <
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await schema.safeParseAsync(validatorValue)
|
const result =
|
||||||
|
options && options.validationFunction
|
||||||
|
? await options.validationFunction(schema, validatorValue)
|
||||||
|
: await schema.safeParseAsync(validatorValue)
|
||||||
|
|
||||||
if (hook) {
|
if (hook) {
|
||||||
const hookResult = await hook({ data: validatorValue, ...result, target }, c)
|
const hookResult = await hook({ data: validatorValue, ...result, target }, c)
|
||||||
|
|
Loading…
Reference in New Issue