Compare commits
15 Commits
@hono/zod-
...
main
Author | SHA1 | Date |
---|---|---|
|
0dc6a1f5e6 | |
|
d89fed7eec | |
|
b24925168a | |
|
2a46bfbba0 | |
|
28601b311b | |
|
5a3cbc4cc6 | |
|
e835cf6183 | |
|
2339eefa1c | |
|
7d82875634 | |
|
c6a16ab7aa | |
|
1a564d5fb6 | |
|
9216d2a666 | |
|
845e336ff4 | |
|
2a589b0728 | |
|
47cb4c8569 |
|
@ -54,7 +54,7 @@ jobs:
|
|||
- uses: denoland/setup-deno@v2
|
||||
with:
|
||||
cache: true
|
||||
deno-version: v2.x
|
||||
deno-version: v2.3.7
|
||||
- run: deno install --no-lock
|
||||
- run: deno publish --dry-run
|
||||
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
# @hono/auth-js
|
||||
|
||||
## 1.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#1324](https://github.com/honojs/middleware/pull/1324) [`d89fed7eecfa151c18b0a8cf95dae1dfe83dfec2`](https://github.com/honojs/middleware/commit/d89fed7eecfa151c18b0a8cf95dae1dfe83dfec2) Thanks [@jamestalmage](https://github.com/jamestalmage)! - Allow async authjs Config
|
||||
|
||||
## 1.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@hono/auth-js",
|
||||
"version": "1.0.17",
|
||||
"version": "1.1.0",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@hono/auth-js",
|
||||
"version": "1.0.17",
|
||||
"version": "1.1.0",
|
||||
"description": "A third-party Auth js middleware for Hono",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
|
|
|
@ -47,6 +47,27 @@ describe('Config', () => {
|
|||
expect(res.status).toBe(200)
|
||||
})
|
||||
|
||||
it('Should allow async ConfigHandler', async () => {
|
||||
globalThis.process.env = { AUTH_SECRET: 'secret' }
|
||||
const app = new Hono()
|
||||
|
||||
app.use(
|
||||
'/*',
|
||||
initAuthConfig(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1))
|
||||
return {
|
||||
basePath: '/api/auth',
|
||||
providers: [],
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
app.use('/api/auth/*', authHandler())
|
||||
const req = new Request('http://localhost/api/auth/signin')
|
||||
const res = await app.request(req)
|
||||
expect(res.status).toBe(200)
|
||||
})
|
||||
|
||||
it('Should return 401 is if auth cookie is invalid or missing', async () => {
|
||||
const app = new Hono()
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ export type AuthUser = {
|
|||
|
||||
export interface AuthConfig extends Omit<AuthConfigCore, 'raw'> {}
|
||||
|
||||
export type ConfigHandler = (c: Context) => AuthConfig
|
||||
export type ConfigHandler = (c: Context) => AuthConfig | Promise<AuthConfig>
|
||||
|
||||
export function setEnvDefaults(env: AuthEnv, config: AuthConfig): void {
|
||||
config.secret ??= env.AUTH_SECRET
|
||||
|
@ -118,7 +118,7 @@ export function verifyAuth(): MiddlewareHandler {
|
|||
|
||||
export function initAuthConfig(cb: ConfigHandler): MiddlewareHandler {
|
||||
return async (c, next) => {
|
||||
const config = cb(c)
|
||||
const config = await cb(c)
|
||||
c.set('authConfig', config)
|
||||
await next()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
# @hono/standard-validator
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1282](https://github.com/honojs/middleware/pull/1282) [`2a46bfbba0695ae713b58211fd5e83a08b829ad6`](https://github.com/honojs/middleware/commit/2a46bfbba0695ae713b58211fd5e83a08b829ad6) Thanks [@MonsterDeveloper](https://github.com/MonsterDeveloper)! - Fix cookies output for arktype in standard schema validator
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
|
|
@ -26,7 +26,12 @@ const querySortSchema = type({
|
|||
order: "'asc'|'desc'",
|
||||
})
|
||||
|
||||
const headerSchema = type({
|
||||
'user-agent': 'string',
|
||||
})
|
||||
|
||||
export {
|
||||
headerSchema,
|
||||
idJSONSchema,
|
||||
personJSONSchema,
|
||||
postJSONSchema,
|
||||
|
|
|
@ -28,7 +28,12 @@ const querySortSchema = object({
|
|||
order: picklist(['asc', 'desc']),
|
||||
})
|
||||
|
||||
const headerSchema = object({
|
||||
'user-agent': string(),
|
||||
})
|
||||
|
||||
export {
|
||||
headerSchema,
|
||||
idJSONSchema,
|
||||
personJSONSchema,
|
||||
postJSONSchema,
|
||||
|
|
|
@ -28,7 +28,12 @@ const querySortSchema = z.object({
|
|||
order: z.enum(['asc', 'desc']),
|
||||
})
|
||||
|
||||
const headerSchema = z.object({
|
||||
'user-agent': z.string(),
|
||||
})
|
||||
|
||||
export {
|
||||
headerSchema,
|
||||
idJSONSchema,
|
||||
personJSONSchema,
|
||||
postJSONSchema,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@hono/standard-validator",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@hono/standard-validator",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"description": "Validator middleware using Standard Schema",
|
||||
"type": "module",
|
||||
"main": "dist/index.cjs",
|
||||
|
|
|
@ -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 { Context, Env, Input, MiddlewareHandler, TypedResponse, ValidationTargets } from 'hono'
|
||||
import { validator } from 'hono/validator'
|
||||
import { sanitizeIssues } from './sanitize-issues'
|
||||
|
||||
type HasUndefined<T> = undefined extends T ? true : false
|
||||
type TOrPromiseOfT<T> = T | Promise<T>
|
||||
|
@ -21,6 +22,67 @@ type Hook<
|
|||
c: Context<E, P>
|
||||
) => TOrPromiseOfT<Response | void | TypedResponse<O>>
|
||||
|
||||
/**
|
||||
* Validation middleware for libraries that support [Standard Schema](https://standardschema.dev/) specification.
|
||||
*
|
||||
* This middleware validates incoming request data against a provided schema
|
||||
* that conforms to the Standard Schema specification. It supports validation
|
||||
* of JSON bodies, headers, queries, forms, and other request targets.
|
||||
*
|
||||
* @param target - The request target to validate ('json', 'header', 'query', 'form', etc.)
|
||||
* @param schema - A schema object conforming to Standard Schema specification
|
||||
* @param hook - Optional hook function called with validation results for custom error handling
|
||||
* @returns A Hono middleware handler that validates requests and makes validated data available via `c.req.valid()`
|
||||
*
|
||||
* @example Basic JSON validation
|
||||
* ```ts
|
||||
* import { z } from 'zod'
|
||||
* import { sValidator } from '@hono/standard-validator'
|
||||
*
|
||||
* const schema = z.object({
|
||||
* name: z.string(),
|
||||
* age: z.number(),
|
||||
* })
|
||||
*
|
||||
* app.post('/author', sValidator('json', schema), (c) => {
|
||||
* const data = c.req.valid('json')
|
||||
* return c.json({
|
||||
* success: true,
|
||||
* message: `${data.name} is ${data.age}`,
|
||||
* })
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* @example With custom error handling hook
|
||||
* ```ts
|
||||
* app.post(
|
||||
* '/post',
|
||||
* sValidator('json', schema, (result, c) => {
|
||||
* if (!result.success) {
|
||||
* return c.text('Invalid!', 400)
|
||||
* }
|
||||
* }),
|
||||
* (c) => {
|
||||
* // Handler code
|
||||
* }
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* @example Header validation
|
||||
* ```ts
|
||||
* import { object, string } from 'valibot'
|
||||
*
|
||||
* const schema = object({
|
||||
* 'content-type': string(),
|
||||
* 'user-agent': string(),
|
||||
* })
|
||||
*
|
||||
* app.post('/author', sValidator('header', schema), (c) => {
|
||||
* const headers = c.req.valid('header')
|
||||
* // do something with headers
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
const sValidator = <
|
||||
Schema extends StandardSchemaV1,
|
||||
Target extends keyof ValidationTargets,
|
||||
|
@ -71,7 +133,9 @@ const sValidator = <
|
|||
}
|
||||
|
||||
if (result.issues) {
|
||||
return c.json({ data: value, error: result.issues, success: false }, 400)
|
||||
const processedIssues = sanitizeIssues(result.issues, schema['~standard'].vendor, target)
|
||||
|
||||
return c.json({ data: value, error: processedIssues, success: false }, 400)
|
||||
}
|
||||
|
||||
return result.value as StandardSchemaV1.InferOutput<Schema>
|
||||
|
|
|
@ -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
|
||||
|
||||
## 0.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1309](https://github.com/honojs/middleware/pull/1309) [`47cb4c85693ebe77d3b8b55f6e1e59778f671a15`](https://github.com/honojs/middleware/commit/47cb4c85693ebe77d3b8b55f6e1e59778f671a15) Thanks [@github-actions](https://github.com/apps/github-actions)! - chore(ua-blocker): sync `robots.json` with upstream
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@hono/ua-blocker",
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.6",
|
||||
"description": "User agent-based blocker for Hono",
|
||||
"type": "module",
|
||||
"module": "dist/index.js",
|
||||
|
|
|
@ -251,6 +251,13 @@
|
|||
"frequency": "No information.",
|
||||
"description": "Used to train Gemini and Vertex AI generative APIs. Does not impact a site's inclusion or ranking in Google Search."
|
||||
},
|
||||
"GoogleAgent-Mariner": {
|
||||
"operator": "Unclear at this time.",
|
||||
"respect": "Unclear at this time.",
|
||||
"function": "AI Agents",
|
||||
"frequency": "Unclear at this time.",
|
||||
"description": "GoogleAgent-Mariner is an AI agent created by Google that can use a web browser. It can intelligently navigate and interact with websites to complete multi-step tasks on behalf of a human user. More info can be found at https://darkvisitors.com/agents/agents/googleagent-mariner"
|
||||
},
|
||||
"GoogleOther": {
|
||||
"description": "\"Used by various product teams for fetching publicly accessible content from sites. For example, it may be used for one-off crawls for internal research and development.\"",
|
||||
"frequency": "No information.",
|
||||
|
|
|
@ -35,6 +35,7 @@ User-agent: FriendlyCrawler
|
|||
User-agent: Gemini-Deep-Research
|
||||
User-agent: Google-CloudVertexBot
|
||||
User-agent: Google-Extended
|
||||
User-agent: GoogleAgent-Mariner
|
||||
User-agent: GoogleOther
|
||||
User-agent: GoogleOther-Image
|
||||
User-agent: GoogleOther-Video
|
||||
|
@ -87,7 +88,7 @@ User-agent: YandexAdditionalBot
|
|||
User-agent: YouBot
|
||||
Disallow: /
|
||||
`;
|
||||
export const ALL_BOTS = ["AI2Bot", "Ai2Bot-Dolma", "aiHitBot", "Amazonbot", "Andibot", "anthropic-ai", "Applebot", "Applebot-Extended", "Awario", "bedrockbot", "Brightbot 1.0", "Bytespider", "CCBot", "ChatGPT-User", "Claude-SearchBot", "Claude-User", "Claude-Web", "ClaudeBot", "cohere-ai", "cohere-training-data-crawler", "Cotoyogi", "Crawlspace", "Datenbank Crawler", "Devin", "Diffbot", "DuckAssistBot", "Echobot Bot", "EchoboxBot", "FacebookBot", "facebookexternalhit", "Factset_spyderbot", "FirecrawlAgent", "FriendlyCrawler", "Gemini-Deep-Research", "Google-CloudVertexBot", "Google-Extended", "GoogleOther", "GoogleOther-Image", "GoogleOther-Video", "GPTBot", "iaskspider/2.0", "ICC-Crawler", "ImagesiftBot", "img2dataset", "ISSCyberRiskCrawler", "Kangaroo Bot", "meta-externalagent", "Meta-ExternalAgent", "meta-externalfetcher", "Meta-ExternalFetcher", "MistralAI-User", "MistralAI-User/1.0", "MyCentralAIScraperBot", "netEstate Imprint Crawler", "NovaAct", "OAI-SearchBot", "omgili", "omgilibot", "Operator", "PanguBot", "Panscient", "panscient.com", "Perplexity-User", "PerplexityBot", "PetalBot", "PhindBot", "Poseidon Research Crawler", "QualifiedBot", "QuillBot", "quillbot.com", "SBIntuitionsBot", "Scrapy", "SemrushBot-OCOB", "SemrushBot-SWA", "Sidetrade indexer bot", "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 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 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 ALL_BOTS = ["AI2Bot", "Ai2Bot-Dolma", "aiHitBot", "Amazonbot", "Andibot", "anthropic-ai", "Applebot", "Applebot-Extended", "Awario", "bedrockbot", "Brightbot 1.0", "Bytespider", "CCBot", "ChatGPT-User", "Claude-SearchBot", "Claude-User", "Claude-Web", "ClaudeBot", "cohere-ai", "cohere-training-data-crawler", "Cotoyogi", "Crawlspace", "Datenbank Crawler", "Devin", "Diffbot", "DuckAssistBot", "Echobot Bot", "EchoboxBot", "FacebookBot", "facebookexternalhit", "Factset_spyderbot", "FirecrawlAgent", "FriendlyCrawler", "Gemini-Deep-Research", "Google-CloudVertexBot", "Google-Extended", "GoogleAgent-Mariner", "GoogleOther", "GoogleOther-Image", "GoogleOther-Video", "GPTBot", "iaskspider/2.0", "ICC-Crawler", "ImagesiftBot", "img2dataset", "ISSCyberRiskCrawler", "Kangaroo Bot", "meta-externalagent", "Meta-ExternalAgent", "meta-externalfetcher", "Meta-ExternalFetcher", "MistralAI-User", "MistralAI-User/1.0", "MyCentralAIScraperBot", "netEstate Imprint Crawler", "NovaAct", "OAI-SearchBot", "omgili", "omgilibot", "Operator", "PanguBot", "Panscient", "panscient.com", "Perplexity-User", "PerplexityBot", "PetalBot", "PhindBot", "Poseidon Research Crawler", "QualifiedBot", "QuillBot", "quillbot.com", "SBIntuitionsBot", "Scrapy", "SemrushBot-OCOB", "SemrushBot-SWA", "Sidetrade indexer bot", "SummalyBot", "Thinkbot", "TikTokSpider", "Timpibot", "VelenPublicWebCrawler", "WARDBot", "Webzio-Extended", "wpbot", "YandexAdditional", "YandexAdditionalBot", "YouBot"];
|
||||
export const NON_RESPECTING_BOTS = ["Andibot", "anthropic-ai", "Applebot", "Awario", "Brightbot 1.0", "Bytespider", "Claude-Web", "cohere-ai", "cohere-training-data-crawler", "Datenbank Crawler", "Devin", "Diffbot", "DuckAssistBot", "Echobot Bot", "EchoboxBot", "facebookexternalhit", "Factset_spyderbot", "Gemini-Deep-Research", "GoogleAgent-Mariner", "iaskspider/2.0", "img2dataset", "ISSCyberRiskCrawler", "Kangaroo Bot", "Meta-ExternalAgent", "meta-externalfetcher", "Meta-ExternalFetcher", "MistralAI-User", "MyCentralAIScraperBot", "netEstate Imprint Crawler", "NovaAct", "Operator", "PanguBot", "Perplexity-User", "PhindBot", "Poseidon Research Crawler", "QualifiedBot", "QuillBot", "quillbot.com", "Scrapy", "Sidetrade indexer bot", "SummalyBot", "Thinkbot", "TikTokSpider", "Timpibot", "WARDBot", "Webzio-Extended", "wpbot"];
|
||||
export const ALL_BOTS_REGEX = /(AI2BOT|AI2BOT-DOLMA|AIHITBOT|AMAZONBOT|ANDIBOT|ANTHROPIC-AI|APPLEBOT|APPLEBOT-EXTENDED|AWARIO|BEDROCKBOT|BRIGHTBOT 1.0|BYTESPIDER|CCBOT|CHATGPT-USER|CLAUDE-SEARCHBOT|CLAUDE-USER|CLAUDE-WEB|CLAUDEBOT|COHERE-AI|COHERE-TRAINING-DATA-CRAWLER|COTOYOGI|CRAWLSPACE|DATENBANK CRAWLER|DEVIN|DIFFBOT|DUCKASSISTBOT|ECHOBOT BOT|ECHOBOXBOT|FACEBOOKBOT|FACEBOOKEXTERNALHIT|FACTSET_SPYDERBOT|FIRECRAWLAGENT|FRIENDLYCRAWLER|GEMINI-DEEP-RESEARCH|GOOGLE-CLOUDVERTEXBOT|GOOGLE-EXTENDED|GOOGLEAGENT-MARINER|GOOGLEOTHER|GOOGLEOTHER-IMAGE|GOOGLEOTHER-VIDEO|GPTBOT|IASKSPIDER\/2.0|ICC-CRAWLER|IMAGESIFTBOT|IMG2DATASET|ISSCYBERRISKCRAWLER|KANGAROO BOT|META-EXTERNALAGENT|META-EXTERNALAGENT|META-EXTERNALFETCHER|META-EXTERNALFETCHER|MISTRALAI-USER|MISTRALAI-USER\/1.0|MYCENTRALAISCRAPERBOT|NETESTATE IMPRINT CRAWLER|NOVAACT|OAI-SEARCHBOT|OMGILI|OMGILIBOT|OPERATOR|PANGUBOT|PANSCIENT|PANSCIENT.COM|PERPLEXITY-USER|PERPLEXITYBOT|PETALBOT|PHINDBOT|POSEIDON RESEARCH CRAWLER|QUALIFIEDBOT|QUILLBOT|QUILLBOT.COM|SBINTUITIONSBOT|SCRAPY|SEMRUSHBOT-OCOB|SEMRUSHBOT-SWA|SIDETRADE INDEXER BOT|SUMMALYBOT|THINKBOT|TIKTOKSPIDER|TIMPIBOT|VELENPUBLICWEBCRAWLER|WARDBOT|WEBZIO-EXTENDED|WPBOT|YANDEXADDITIONAL|YANDEXADDITIONALBOT|YOUBOT)/;
|
||||
export const NON_RESPECTING_BOTS_REGEX = /(ANDIBOT|ANTHROPIC-AI|APPLEBOT|AWARIO|BRIGHTBOT 1.0|BYTESPIDER|CLAUDE-WEB|COHERE-AI|COHERE-TRAINING-DATA-CRAWLER|DATENBANK CRAWLER|DEVIN|DIFFBOT|DUCKASSISTBOT|ECHOBOT BOT|ECHOBOXBOT|FACEBOOKEXTERNALHIT|FACTSET_SPYDERBOT|GEMINI-DEEP-RESEARCH|GOOGLEAGENT-MARINER|IASKSPIDER\/2.0|IMG2DATASET|ISSCYBERRISKCRAWLER|KANGAROO BOT|META-EXTERNALAGENT|META-EXTERNALFETCHER|META-EXTERNALFETCHER|MISTRALAI-USER|MYCENTRALAISCRAPERBOT|NETESTATE IMPRINT CRAWLER|NOVAACT|OPERATOR|PANGUBOT|PERPLEXITY-USER|PHINDBOT|POSEIDON RESEARCH CRAWLER|QUALIFIEDBOT|QUILLBOT|QUILLBOT.COM|SCRAPY|SIDETRADE INDEXER BOT|SUMMALYBOT|THINKBOT|TIKTOKSPIDER|TIMPIBOT|WARDBOT|WEBZIO-EXTENDED|WPBOT)/;
|
||||
|
|
|
@ -1,5 +1,28 @@
|
|||
# @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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@hono/zod-openapi",
|
||||
"version": "0.19.10",
|
||||
"version": "1.0.2",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@hono/zod-openapi",
|
||||
"version": "0.19.10",
|
||||
"version": "1.0.2",
|
||||
"description": "A wrapper class of Hono which supports OpenAPI.",
|
||||
"type": "module",
|
||||
"module": "dist/index.js",
|
||||
|
@ -41,7 +41,7 @@
|
|||
"homepage": "https://github.com/honojs/middleware",
|
||||
"peerDependencies": {
|
||||
"hono": ">=4.3.6",
|
||||
"zod": ">=3.0.0"
|
||||
"zod": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@arethetypeswrong/cli": "^0.17.4",
|
||||
|
@ -51,10 +51,10 @@
|
|||
"typescript": "^5.8.2",
|
||||
"vitest": "^3.2.4",
|
||||
"yaml": "^2.4.3",
|
||||
"zod": "^3.22.1"
|
||||
"zod": "^4.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@asteasolutions/zod-to-openapi": "^7.3.0",
|
||||
"@asteasolutions/zod-to-openapi": "^8.0.0",
|
||||
"@hono/zod-validator": "workspace:^",
|
||||
"openapi3-ts": "^4.5.0"
|
||||
},
|
||||
|
|
|
@ -240,7 +240,7 @@ describe('coerce', () => {
|
|||
type Actual = ExtractSchema<typeof routes>['/api/users/:id']['$get']['input']
|
||||
type Expected = {
|
||||
param: {
|
||||
id: number
|
||||
id: unknown
|
||||
}
|
||||
}
|
||||
type verify = Expect<Equal<Expected, Actual>>
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
OpenApiGeneratorV3,
|
||||
OpenApiGeneratorV31,
|
||||
extendZodWithOpenApi,
|
||||
getOpenApiMetadata,
|
||||
} from '@asteasolutions/zod-to-openapi'
|
||||
import { zValidator } from '@hono/zod-validator'
|
||||
import { Hono } from 'hono'
|
||||
|
@ -38,8 +39,8 @@ import type { JSONParsed, JSONValue, RemoveBlankRecord, SimplifyDeepArray } from
|
|||
import { mergePath } from 'hono/utils/url'
|
||||
import type { OpenAPIObject } from 'openapi3-ts/oas30'
|
||||
import type { OpenAPIObject as OpenAPIV31bject } from 'openapi3-ts/oas31'
|
||||
import type { ZodError, ZodSchema } from 'zod'
|
||||
import { ZodType, z } from 'zod'
|
||||
import type { ZodError } from 'zod'
|
||||
|
||||
type MaybePromise<T> = Promise<T> | T
|
||||
|
||||
|
@ -128,7 +129,7 @@ type InputTypeJson<R extends RouteConfig> = R['request'] extends RequestTypes
|
|||
? {}
|
||||
: R['request']['body']['content'][keyof R['request']['body']['content']] extends Record<
|
||||
'schema',
|
||||
ZodSchema<any>
|
||||
ZodType<any>
|
||||
>
|
||||
? {
|
||||
in: {
|
||||
|
@ -154,7 +155,7 @@ type InputTypeForm<R extends RouteConfig> = R['request'] extends RequestTypes
|
|||
? {}
|
||||
: R['request']['body']['content'][keyof R['request']['body']['content']] extends Record<
|
||||
'schema',
|
||||
ZodSchema<any>
|
||||
ZodType<any>
|
||||
>
|
||||
? {
|
||||
in: {
|
||||
|
@ -181,7 +182,7 @@ type InputTypeCookie<R extends RouteConfig> = InputTypeBase<R, 'cookies', 'cooki
|
|||
type ExtractContent<T> = T extends {
|
||||
[K in keyof T]: infer A
|
||||
}
|
||||
? A extends Record<'schema', ZodSchema>
|
||||
? A extends Record<'schema', ZodType>
|
||||
? z.infer<A['schema']>
|
||||
: never
|
||||
: never
|
||||
|
@ -659,11 +660,14 @@ export class OpenAPIHono<
|
|||
}
|
||||
|
||||
case 'schema':
|
||||
return this.openAPIRegistry.register(def.schema._def.openapi._internal.refId, def.schema)
|
||||
return this.openAPIRegistry.register(
|
||||
getOpenApiMetadata(def.schema)._internal?.refId,
|
||||
def.schema
|
||||
)
|
||||
|
||||
case 'parameter':
|
||||
return this.openAPIRegistry.registerParameter(
|
||||
def.schema._def.openapi._internal.refId,
|
||||
getOpenApiMetadata(def.schema)._internal?.refId,
|
||||
def.schema
|
||||
)
|
||||
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
# @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
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@hono/zod-validator",
|
||||
"version": "0.7.1",
|
||||
"version": "0.7.2",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@hono/zod-validator",
|
||||
"version": "0.7.1",
|
||||
"version": "0.7.2",
|
||||
"description": "Validator middleware using Zod",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
|
|
@ -73,7 +73,7 @@ export const zValidator = <
|
|||
if ((target === 'header' && '_def' in schema) || (target === 'header' && '_zod' in schema)) {
|
||||
// create an object that maps lowercase schema keys to lowercase
|
||||
// @ts-expect-error the schema is a Zod Schema
|
||||
const schemaKeys = Object.keys(schema.shape)
|
||||
const schemaKeys = Object.keys('in' in schema ? schema.in.shape : schema.shape)
|
||||
const caseInsensitiveKeymap = Object.fromEntries(
|
||||
schemaKeys.map((key) => [key.toLowerCase(), key])
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -99,14 +99,14 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@asteasolutions/zod-to-openapi@npm:^7.3.0":
|
||||
version: 7.3.0
|
||||
resolution: "@asteasolutions/zod-to-openapi@npm:7.3.0"
|
||||
"@asteasolutions/zod-to-openapi@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "@asteasolutions/zod-to-openapi@npm:8.0.0"
|
||||
dependencies:
|
||||
openapi3-ts: "npm:^4.1.2"
|
||||
peerDependencies:
|
||||
zod: ^3.20.2
|
||||
checksum: 10c0/f0a68a89929cdeaa3e21d2027489689f982824d676a9332c680e119f60881dd39b571324b24ad4837fda49bf6fe7c3e2af2199268b281bf1aec923d7a7cbfc40
|
||||
zod: ^4.0.0
|
||||
checksum: 10c0/b522d074832fb137dca724c8bd4bb134c7b4d4cad12c247ed3c864f993923b3475fc06580e6e1cbc4fd8641cd361679bbe1dd87c9bb42e142bc056d96d59fbc8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -2734,7 +2734,7 @@ __metadata:
|
|||
resolution: "@hono/zod-openapi@workspace:packages/zod-openapi"
|
||||
dependencies:
|
||||
"@arethetypeswrong/cli": "npm:^0.17.4"
|
||||
"@asteasolutions/zod-to-openapi": "npm:^7.3.0"
|
||||
"@asteasolutions/zod-to-openapi": "npm:^8.0.0"
|
||||
"@hono/zod-validator": "workspace:^"
|
||||
hono: "npm:^4.8.4"
|
||||
openapi3-ts: "npm:^4.5.0"
|
||||
|
@ -2743,10 +2743,10 @@ __metadata:
|
|||
typescript: "npm:^5.8.2"
|
||||
vitest: "npm:^3.2.4"
|
||||
yaml: "npm:^2.4.3"
|
||||
zod: "npm:^3.22.1"
|
||||
zod: "npm:^4.0.5"
|
||||
peerDependencies:
|
||||
hono: ">=4.3.6"
|
||||
zod: ">=3.0.0"
|
||||
zod: ^4.0.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
|
@ -16830,7 +16830,7 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^3.20.2, zod@npm:^3.22.1, zod@npm:^3.22.3":
|
||||
"zod@npm:^3.20.2, zod@npm:^3.22.3":
|
||||
version: 3.24.2
|
||||
resolution: "zod@npm:3.24.2"
|
||||
checksum: 10c0/c638c7220150847f13ad90635b3e7d0321b36cce36f3fc6050ed960689594c949c326dfe2c6fa87c14b126ee5d370ccdebd6efb304f41ef5557a4aaca2824565
|
||||
|
|
Loading…
Reference in New Issue