From 681300cd815e8dadedb5ba39b28bd59bebfbdc70 Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Fri, 19 Jul 2024 19:07:07 +0000 Subject: [PATCH 01/17] feat: add `@hono/conform-validator` to packages --- package.json | 1 + packages/conform-validator/README.md | 12 + packages/conform-validator/package.json | 45 ++++ packages/conform-validator/src/index.ts | 40 +++ packages/conform-validator/src/utils.ts | 21 ++ .../conform-validator/test/valibot.test.ts | 116 +++++++++ packages/conform-validator/test/yup.test.ts | 112 ++++++++ packages/conform-validator/test/zod.test.ts | 112 ++++++++ packages/conform-validator/tsconfig.cjs.json | 8 + packages/conform-validator/tsconfig.esm.json | 8 + packages/conform-validator/tsconfig.json | 9 + packages/conform-validator/vitest.config.ts | 8 + yarn.lock | 239 ++++++++++++++++++ 13 files changed, 731 insertions(+) create mode 100644 packages/conform-validator/README.md create mode 100644 packages/conform-validator/package.json create mode 100644 packages/conform-validator/src/index.ts create mode 100644 packages/conform-validator/src/utils.ts create mode 100644 packages/conform-validator/test/valibot.test.ts create mode 100644 packages/conform-validator/test/yup.test.ts create mode 100644 packages/conform-validator/test/zod.test.ts create mode 100644 packages/conform-validator/tsconfig.cjs.json create mode 100644 packages/conform-validator/tsconfig.esm.json create mode 100644 packages/conform-validator/tsconfig.json create mode 100644 packages/conform-validator/vitest.config.ts diff --git a/package.json b/package.json index 26fc86585..d266d6fce 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "build:oidc-auth": "yarn workspace @hono/oidc-auth build", "build:node-ws": "yarn workspace @hono/node-ws build", "build:react-compat": "yarn workspace @hono/react-compat build", + "build:conform-validator": "yarn workspace @hono/conform-validator build", "build": "run-p 'build:*'", "lint": "eslint 'packages/**/*.{ts,tsx}'", "lint:fix": "eslint --fix 'packages/**/*.{ts,tsx}'", diff --git a/packages/conform-validator/README.md b/packages/conform-validator/README.md new file mode 100644 index 000000000..f1b821f00 --- /dev/null +++ b/packages/conform-validator/README.md @@ -0,0 +1,12 @@ +# Conform validator middleware for Hono + + +## Usage + + +## Author + + +## License + +MIT diff --git a/packages/conform-validator/package.json b/packages/conform-validator/package.json new file mode 100644 index 000000000..27842987c --- /dev/null +++ b/packages/conform-validator/package.json @@ -0,0 +1,45 @@ +{ + "name": "@hono/conform-validator", + "version": "0.0.1", + "description": "Validator middleware using Conform", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", + "types": "dist/esm/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "test": "vitest --run", + "build:cjs": "tsc -p tsconfig.cjs.json", + "build:esm": "tsc -p tsconfig.esm.json", + "build": "rimraf dist && yarn build:cjs && yarn build:esm", + "prerelease": "yarn build && yarn test", + "release": "yarn publish" + }, + "license": "MIT", + "publishConfig": { + "registry": "https://registry.npmjs.org", + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/honojs/middleware.git" + }, + "homepage": "https://github.com/honojs/middleware", + "peerDependencies": { + "hono": ">=3.9.0", + "@conform-to/dom":">=1.1.5" + }, + "devDependencies": { + "@conform-to/dom": "^1.1.5", + "@conform-to/yup": "^1.1.5", + "@conform-to/zod": "^1.1.5", + "conform-to-valibot": "^1.10.0", + "hono": "^4.0.10", + "rimraf": "^5.0.5", + "valibot": "^0.36.0", + "vitest": "^2.0.3", + "yup": "^1.4.0", + "zod": "^3.23.8" + } +} diff --git a/packages/conform-validator/src/index.ts b/packages/conform-validator/src/index.ts new file mode 100644 index 000000000..d7fbc0d00 --- /dev/null +++ b/packages/conform-validator/src/index.ts @@ -0,0 +1,40 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import type { Env, Input as HonoInput, MiddlewareHandler, ValidationTargets } from 'hono' +import type { Submission } from '@conform-to/dom' +import { getFormDataFromContext } from './utils' + +type FormTargetValue = ValidationTargets['form']['string'] + +type GetInput = T extends (_: any) => infer S + ? Awaited extends Submission + ? V + : never + : never + +type ParseFn = (formData: FormData) => Submission | Promise> + +export const conformValidator = < + F extends ParseFn, + E extends Env, + P extends string, + In = GetInput, + Out = Awaited>, + I extends HonoInput = { + in: { + form: { [K in keyof In]: FormTargetValue } + } + out: { form: Out } + } +>( + parse: F +): MiddlewareHandler => { + return async (ctx, next) => { + const formData = await getFormDataFromContext(ctx) + const submission = parse(formData) + + ctx.req.addValidatedData('form', submission) + + await next() + } +} diff --git a/packages/conform-validator/src/utils.ts b/packages/conform-validator/src/utils.ts new file mode 100644 index 000000000..b0f76de51 --- /dev/null +++ b/packages/conform-validator/src/utils.ts @@ -0,0 +1,21 @@ +import type { Context } from 'hono' +import { bufferToFormData } from 'hono/utils/buffer' + +export const getFormDataFromContext = async (ctx: Context): Promise => { + const contentType = ctx.req.header('Content-Type') + if (!contentType) { + return new FormData() + } + + const cache = ctx.req.bodyCache.formData + if (cache) { + return cache + } + + const arrayBuffer = await ctx.req.arrayBuffer() + const formData = await bufferToFormData(arrayBuffer, contentType) + + ctx.req.bodyCache.formData = formData + + return formData +} diff --git a/packages/conform-validator/test/valibot.test.ts b/packages/conform-validator/test/valibot.test.ts new file mode 100644 index 000000000..dd79cb55c --- /dev/null +++ b/packages/conform-validator/test/valibot.test.ts @@ -0,0 +1,116 @@ +import * as v from 'valibot' +import { Hono } from 'hono' +import { hc } from 'hono/client' +import type { ExtractSchema } from 'hono/types' +import type { Equal, Expect } from 'hono/utils/types' +import { parseWithValibot } from 'conform-to-valibot' +import { conformValidator } from '../src' + +describe('Validate requests using a Valibot schema', () => { + const app = new Hono() + + const schema = v.object({ + name: v.string(), + age: v.pipe( + v.string(), + v.transform((v) => Number(v)), + v.integer() + ), + nickname: v.optional(v.string()), + }) + + const route = app.post( + '/author', + conformValidator((formData) => parseWithValibot(formData, { schema })), + (c) => { + const submission = c.req.valid('form') + + if (submission.status === 'success') { + const value = submission.value + + return c.json({ + success: true, + message: `${value.name} is ${value.age}, nickname is ${ + value?.nickname || 'nothing yet :<' + }`, + }) + } + + return c.json( + { + success: false, + message: 'Bad Request', + }, + 400 + ) + } + ) + + it.skip('check the route object types', () => { + type Actual = ExtractSchema + type Expected = { + '/author': { + $post: { + input: { + form: { + name: string | File + age: string | File + nickname?: string | File | undefined + } + } + output: { + success: boolean + message: string + } + } + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type verify = Expect> + }) + + it('Should return 200 response', async () => { + const client = hc('http://localhost', { + fetch: (req, init) => { + return app.request(req, init) + }, + }) + + const res = await client.author.$post({ + form: { + name: 'Space Cat', + age: '20', + nickname: 'meow', + }, + }) + + expect(res).not.toBeNull() + expect(res.status).toBe(200) + + const json = await res.json() + expect(json).toEqual({ + success: true, + message: 'Space Cat is 20, nickname is meow', + }) + }) + + it('Should return 400 response', async () => { + const formData = new FormData() + + const req = new Request('http://localhost/author', { + body: formData, + method: 'POST', + }) + + const res = await app.request(req) + expect(res).not.toBeNull() + expect(res.status).toBe(400) + + const json = await res.json() + expect(json).toEqual({ + success: false, + message: 'Bad Request', + }) + }) +}) diff --git a/packages/conform-validator/test/yup.test.ts b/packages/conform-validator/test/yup.test.ts new file mode 100644 index 000000000..005b3270d --- /dev/null +++ b/packages/conform-validator/test/yup.test.ts @@ -0,0 +1,112 @@ +import * as y from 'yup' +import { Hono } from 'hono' +import { hc } from 'hono/client' +import type { ExtractSchema } from 'hono/types' +import type { Equal, Expect } from 'hono/utils/types' +import { parseWithYup } from '@conform-to/yup' +import { conformValidator } from '../src' + +describe('Validate requests using a Valibot schema', () => { + const app = new Hono() + + const schema = y.object({ + name: y.string().required(), + age: y.number().required(), + nickname: y.string().optional(), + }) + + const route = app.post( + '/author', + conformValidator((formData) => parseWithYup(formData, { schema })), + (c) => { + const submission = c.req.valid('form') + + if (submission.status === 'success') { + const value = submission.value + + return c.json({ + success: true, + message: `${value.name} is ${value.age}, nickname is ${ + value?.nickname || 'nothing yet :3' + }`, + }) + } + + return c.json( + { + success: false, + message: 'Bad Request', + }, + 400 + ) + } + ) + + it.skip('check the route object types', () => { + type Actual = ExtractSchema + type Expected = { + '/author': { + $post: { + input: { + form: { + name: string | File + age: string | File + nickname?: string | File | undefined + } + } + output: { + success: boolean + message: string + } + } + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type verify = Expect> + }) + + it('Should return 200 response', async () => { + const client = hc('http://localhost', { + fetch: (req, init) => { + return app.request(req, init) + }, + }) + + const res = await client.author.$post({ + form: { + name: 'Space Cat', + age: '20', + nickname: 'meow', + }, + }) + + expect(res).not.toBeNull() + expect(res.status).toBe(200) + + const json = await res.json() + expect(json).toEqual({ + success: true, + message: 'Space Cat is 20, nickname is meow', + }) + }) + + it('Should return 400 response', async () => { + const formData = new FormData() + + const req = new Request('http://localhost/author', { + body: formData, + method: 'POST', + }) + + const res = await app.request(req) + expect(res).not.toBeNull() + expect(res.status).toBe(400) + + const json = await res.json() + expect(json).toEqual({ + success: false, + message: 'Bad Request', + }) + }) +}) diff --git a/packages/conform-validator/test/zod.test.ts b/packages/conform-validator/test/zod.test.ts new file mode 100644 index 000000000..0b0df94c1 --- /dev/null +++ b/packages/conform-validator/test/zod.test.ts @@ -0,0 +1,112 @@ +import * as z from 'zod' +import { Hono } from 'hono' +import { hc } from 'hono/client' +import type { ExtractSchema } from 'hono/types' +import type { Equal, Expect } from 'hono/utils/types' +import { parseWithZod } from '@conform-to/zod' +import { conformValidator } from '../src' + +describe('Validate requests using a Valibot schema', () => { + const app = new Hono() + + const schema = z.object({ + name: z.string(), + age: z.string().transform((str) => Number(str)), + nickname: z.string().optional(), + }) + + const route = app.post( + '/author', + conformValidator((formData) => parseWithZod(formData, { schema })), + (c) => { + const submission = c.req.valid('form') + + if (submission.status === 'success') { + const value = submission.value + + return c.json({ + success: true, + message: `${value.name} is ${value.age}, nickname is ${ + value?.nickname || 'nothing yet :<' + }`, + }) + } + + return c.json( + { + success: false, + message: 'Bad Request', + }, + 400 + ) + } + ) + + it.skip('check the route object types', () => { + type Actual = ExtractSchema + type Expected = { + '/author': { + $post: { + input: { + form: { + name: string | File + age: string | File + nickname?: string | File | undefined + } + } + output: { + success: boolean + message: string + } + } + } + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + type verify = Expect> + }) + + it('Should return 200 response', async () => { + const client = hc('http://localhost', { + fetch: (req, init) => { + return app.request(req, init) + }, + }) + + const res = await client.author.$post({ + form: { + name: 'Space Cat', + age: '20', + nickname: 'meow', + }, + }) + + expect(res).not.toBeNull() + expect(res.status).toBe(200) + + const json = await res.json() + expect(json).toEqual({ + success: true, + message: 'Space Cat is 20, nickname is meow', + }) + }) + + it('Should return 400 response', async () => { + const formData = new FormData() + + const req = new Request('http://localhost/author', { + body: formData, + method: 'POST', + }) + + const res = await app.request(req) + expect(res).not.toBeNull() + expect(res.status).toBe(400) + + const json = await res.json() + expect(json).toEqual({ + success: false, + message: 'Bad Request', + }) + }) +}) diff --git a/packages/conform-validator/tsconfig.cjs.json b/packages/conform-validator/tsconfig.cjs.json new file mode 100644 index 000000000..b8bf50ee9 --- /dev/null +++ b/packages/conform-validator/tsconfig.cjs.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "declaration": false, + "outDir": "./dist/cjs" + } +} \ No newline at end of file diff --git a/packages/conform-validator/tsconfig.esm.json b/packages/conform-validator/tsconfig.esm.json new file mode 100644 index 000000000..8130f1a53 --- /dev/null +++ b/packages/conform-validator/tsconfig.esm.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "declaration": true, + "outDir": "./dist/esm" + } +} \ No newline at end of file diff --git a/packages/conform-validator/tsconfig.json b/packages/conform-validator/tsconfig.json new file mode 100644 index 000000000..6c1a39902 --- /dev/null +++ b/packages/conform-validator/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + }, + "include": [ + "src/**/*.ts" + ], +} \ No newline at end of file diff --git a/packages/conform-validator/vitest.config.ts b/packages/conform-validator/vitest.config.ts new file mode 100644 index 000000000..17b54e485 --- /dev/null +++ b/packages/conform-validator/vitest.config.ts @@ -0,0 +1,8 @@ +/// +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + globals: true, + }, +}) diff --git a/yarn.lock b/yarn.lock index 0931cbbb7..adf6d76ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -992,6 +992,35 @@ __metadata: languageName: node linkType: hard +"@conform-to/dom@npm:1.1.5, @conform-to/dom@npm:^1.1.5": + version: 1.1.5 + resolution: "@conform-to/dom@npm:1.1.5" + checksum: 8297852ec422630a01a401842a994a0e19f8585df820ab26df3bbb4687fd97812acbb6ebce7ddc38c6b9197ee1d45ddf136f42a3f33703613c52710de4cf026a + languageName: node + linkType: hard + +"@conform-to/yup@npm:^1.1.5": + version: 1.1.5 + resolution: "@conform-to/yup@npm:1.1.5" + dependencies: + "@conform-to/dom": "npm:1.1.5" + peerDependencies: + yup: ">=0.32.0" + checksum: 31849faaf1b9c4e0368547b0174aadd9fd0c2c4e9d38c63b7455806c721ccf8f56e3826f2b979bb795f55b793492de7ff3f445ac45302acd1f82c74100a24dbf + languageName: node + linkType: hard + +"@conform-to/zod@npm:^1.1.5": + version: 1.1.5 + resolution: "@conform-to/zod@npm:1.1.5" + dependencies: + "@conform-to/dom": "npm:1.1.5" + peerDependencies: + zod: ^3.21.0 + checksum: 047ddb45be8d156f0b0cddf4b6db7a3612e04f7c4b194502f29230a6c720d2e841058afe13838e2daac0804db1bb236ed8ba673fc4737c46589b90ec73935e4d + languageName: node + linkType: hard + "@cspotcode/source-map-support@npm:0.8.1": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -2028,6 +2057,25 @@ __metadata: languageName: unknown linkType: soft +"@hono/conform-validator@workspace:packages/conform-validator": + version: 0.0.0-use.local + resolution: "@hono/conform-validator@workspace:packages/conform-validator" + dependencies: + "@conform-to/dom": "npm:^1.1.5" + "@conform-to/yup": "npm:^1.1.5" + "@conform-to/zod": "npm:^1.1.5" + conform-to-valibot: "npm:^1.10.0" + hono: "npm:^4.0.10" + rimraf: "npm:^5.0.5" + valibot: "npm:^0.36.0" + vitest: "npm:^2.0.3" + yup: "npm:^1.4.0" + zod: "npm:^3.23.8" + peerDependencies: + hono: ">=3.9.0" + languageName: unknown + linkType: soft + "@hono/effect-validator@workspace:packages/effect-validator": version: 0.0.0-use.local resolution: "@hono/effect-validator@workspace:packages/effect-validator" @@ -4797,6 +4845,27 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:2.0.3": + version: 2.0.3 + resolution: "@vitest/expect@npm:2.0.3" + dependencies: + "@vitest/spy": "npm:2.0.3" + "@vitest/utils": "npm:2.0.3" + chai: "npm:^5.1.1" + tinyrainbow: "npm:^1.2.0" + checksum: bc8dead850a8aeb84a0d5d8620e1437752cbfe10908c2d5ec9f80fc6d9c387d70c964abfd2d6caf76da2882022c0dd05b0fa09b7c2a44d65abdde2b6c73517fe + languageName: node + linkType: hard + +"@vitest/pretty-format@npm:2.0.3, @vitest/pretty-format@npm:^2.0.3": + version: 2.0.3 + resolution: "@vitest/pretty-format@npm:2.0.3" + dependencies: + tinyrainbow: "npm:^1.2.0" + checksum: 217fd176fa4d1e64e04bc6a187d146381e99921f46007f98f7132d0e31e2c14b9c6d050a150331b3368ee8004bbeab5b1b7d477522a4e4d71ad822d046debc16 + languageName: node + linkType: hard + "@vitest/runner@npm:0.34.6": version: 0.34.6 resolution: "@vitest/runner@npm:0.34.6" @@ -4873,6 +4942,16 @@ __metadata: languageName: node linkType: hard +"@vitest/runner@npm:2.0.3": + version: 2.0.3 + resolution: "@vitest/runner@npm:2.0.3" + dependencies: + "@vitest/utils": "npm:2.0.3" + pathe: "npm:^1.1.2" + checksum: efbf646457c29268f0d370985d8cbfcfc7d181693dfc2e061dd05ce911f43592957f2c866cde1b5b2e3078ae5d74b94dc28453e1c70b80e8467440223431e863 + languageName: node + linkType: hard + "@vitest/snapshot@npm:0.34.6": version: 0.34.6 resolution: "@vitest/snapshot@npm:0.34.6" @@ -4950,6 +5029,17 @@ __metadata: languageName: node linkType: hard +"@vitest/snapshot@npm:2.0.3": + version: 2.0.3 + resolution: "@vitest/snapshot@npm:2.0.3" + dependencies: + "@vitest/pretty-format": "npm:2.0.3" + magic-string: "npm:^0.30.10" + pathe: "npm:^1.1.2" + checksum: dc7e2e8f60d40c308c487effe2cd94c42bffa795c2d8c740c30b880b451637763891609a052afe29f0c9872e71141d439cb03118595e4a461fe6b4877ae99878 + languageName: node + linkType: hard + "@vitest/spy@npm:0.34.6": version: 0.34.6 resolution: "@vitest/spy@npm:0.34.6" @@ -5013,6 +5103,15 @@ __metadata: languageName: node linkType: hard +"@vitest/spy@npm:2.0.3": + version: 2.0.3 + resolution: "@vitest/spy@npm:2.0.3" + dependencies: + tinyspy: "npm:^3.0.0" + checksum: 4780aeed692c52756d70735b633ad58f201b2b8729b9e46c4cf968b8e4174e2c2cddd099de669019771bcd8e1ca32d0b9fa42d962e431fdf473b62393b9d2a0a + languageName: node + linkType: hard + "@vitest/utils@npm:0.34.6": version: 0.34.6 resolution: "@vitest/utils@npm:0.34.6" @@ -5095,6 +5194,18 @@ __metadata: languageName: node linkType: hard +"@vitest/utils@npm:2.0.3": + version: 2.0.3 + resolution: "@vitest/utils@npm:2.0.3" + dependencies: + "@vitest/pretty-format": "npm:2.0.3" + estree-walker: "npm:^3.0.3" + loupe: "npm:^3.1.1" + tinyrainbow: "npm:^1.2.0" + checksum: 41b64c07814e7d576ebe7d11d277eb104a2aafb986497855a59f641b45fa53a30a2bfea525cd913e91b695f444a7a48b1f1e5909c27d5a989b0aea68f2242bd9 + languageName: node + linkType: hard + "@yarnpkg/lockfile@npm:^1.1.0": version: 1.1.0 resolution: "@yarnpkg/lockfile@npm:1.1.0" @@ -6831,6 +6942,16 @@ __metadata: languageName: node linkType: hard +"conform-to-valibot@npm:^1.10.0": + version: 1.10.0 + resolution: "conform-to-valibot@npm:1.10.0" + peerDependencies: + "@conform-to/dom": ^1.0.0 + valibot: ">= 0.32.0" + checksum: e11f07c00d7b443e80a6d0bcca79c1842bdc8f6f4d966c79ab8ee12fadfb0bd31c04a593f2c880f162c8ffb431b0845696f17ecddaf6ae9b4e3843c8ad168ad0 + languageName: node + linkType: hard + "connect@npm:^3.7.0": version: 3.7.0 resolution: "connect@npm:3.7.0" @@ -15559,6 +15680,13 @@ __metadata: languageName: node linkType: hard +"property-expr@npm:^2.0.5": + version: 2.0.6 + resolution: "property-expr@npm:2.0.6" + checksum: 69b7da15038a1146d6447c69c445306f66a33c425271235bb20507f1846dbf9577a8f9dfafe8acbfcb66f924b270157f155248308f026a68758f35fc72265b3c + languageName: node + linkType: hard + "property-information@npm:^6.0.0": version: 6.4.0 resolution: "property-information@npm:6.4.0" @@ -17648,6 +17776,13 @@ __metadata: languageName: node linkType: hard +"tiny-case@npm:^1.0.3": + version: 1.0.3 + resolution: "tiny-case@npm:1.0.3" + checksum: c0cbed35884a322265e2cd61ff435168d1ea523f88bf3864ce14a238ae9169e732649776964283a66e4eb882e655992081d4daf8c865042e2233425866111b35 + languageName: node + linkType: hard + "tinybench@npm:^2.5.0, tinybench@npm:^2.5.1": version: 2.5.1 resolution: "tinybench@npm:2.5.1" @@ -17697,6 +17832,13 @@ __metadata: languageName: node linkType: hard +"tinyrainbow@npm:^1.2.0": + version: 1.2.0 + resolution: "tinyrainbow@npm:1.2.0" + checksum: 7f78a4b997e5ba0f5ecb75e7ed786f30bab9063716e7dff24dd84013fb338802e43d176cb21ed12480561f5649a82184cf31efb296601a29d38145b1cdb4c192 + languageName: node + linkType: hard + "tinyspy@npm:^2.1.1, tinyspy@npm:^2.2.0": version: 2.2.0 resolution: "tinyspy@npm:2.2.0" @@ -17773,6 +17915,13 @@ __metadata: languageName: node linkType: hard +"toposort@npm:^2.0.2": + version: 2.0.2 + resolution: "toposort@npm:2.0.2" + checksum: ab9ca91fce4b972ccae9e2f539d755bf799a0c7eb60da07fd985fce0f14c159ed1e92305ff55697693b5bc13e300f5417db90e2593b127d421c9f6c440950222 + languageName: node + linkType: hard + "toucan-js@npm:^4.0.0": version: 4.0.0 resolution: "toucan-js@npm:4.0.0" @@ -18794,6 +18943,13 @@ __metadata: languageName: node linkType: hard +"valibot@npm:^0.36.0": + version: 0.36.0 + resolution: "valibot@npm:0.36.0" + checksum: deff84cdcdc324d5010c2087e553cd26b07752ac18912c9152eccd241b507a49a9ad77fed57501d45bcbef9bec6a7a6707b17d9bef8d35e681d45f098a70e466 + languageName: node + linkType: hard + "valid-url@npm:^1": version: 1.0.9 resolution: "valid-url@npm:1.0.9" @@ -18995,6 +19151,21 @@ __metadata: languageName: node linkType: hard +"vite-node@npm:2.0.3": + version: 2.0.3 + resolution: "vite-node@npm:2.0.3" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.3.5" + pathe: "npm:^1.1.2" + tinyrainbow: "npm:^1.2.0" + vite: "npm:^5.0.0" + bin: + vite-node: vite-node.mjs + checksum: a1bcc110aeb49e79a50ae0df41ca692d39e0d992702f7c5b095c969f622eb72636543bed79efb7131fdedaa4c44a6c9c19daf6fca909240acc1f27f79b978c11 + languageName: node + linkType: hard + "vite@npm:^3.0.0 || ^4.0.0 || ^5.0.0-0, vite@npm:^3.1.0 || ^4.0.0 || ^5.0.0-0, vite@npm:^5.0.0": version: 5.0.10 resolution: "vite@npm:5.0.10" @@ -19434,6 +19605,55 @@ __metadata: languageName: node linkType: hard +"vitest@npm:^2.0.3": + version: 2.0.3 + resolution: "vitest@npm:2.0.3" + dependencies: + "@ampproject/remapping": "npm:^2.3.0" + "@vitest/expect": "npm:2.0.3" + "@vitest/pretty-format": "npm:^2.0.3" + "@vitest/runner": "npm:2.0.3" + "@vitest/snapshot": "npm:2.0.3" + "@vitest/spy": "npm:2.0.3" + "@vitest/utils": "npm:2.0.3" + chai: "npm:^5.1.1" + debug: "npm:^4.3.5" + execa: "npm:^8.0.1" + magic-string: "npm:^0.30.10" + pathe: "npm:^1.1.2" + std-env: "npm:^3.7.0" + tinybench: "npm:^2.8.0" + tinypool: "npm:^1.0.0" + tinyrainbow: "npm:^1.2.0" + vite: "npm:^5.0.0" + vite-node: "npm:2.0.3" + why-is-node-running: "npm:^2.2.2" + peerDependencies: + "@edge-runtime/vm": "*" + "@types/node": ^18.0.0 || >=20.0.0 + "@vitest/browser": 2.0.3 + "@vitest/ui": 2.0.3 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: 1801ec31eb144063d14a03d054ff573869732dcaf69abd4fefdabe011d183599a7493e49d8e180b29808675309814421c4a12271fb140c708e7c9f68c4a37a3c + languageName: node + linkType: hard + "walker@npm:^1.0.8": version: 1.0.8 resolution: "walker@npm:1.0.8" @@ -19993,6 +20213,18 @@ __metadata: languageName: node linkType: hard +"yup@npm:^1.4.0": + version: 1.4.0 + resolution: "yup@npm:1.4.0" + dependencies: + property-expr: "npm:^2.0.5" + tiny-case: "npm:^1.0.3" + toposort: "npm:^2.0.2" + type-fest: "npm:^2.19.0" + checksum: fe142141365eed0f78fb2e18bdd2f10bf101385dae12a5f9de14884448067bdca16a54b547fc0bffec04a098dd70b4519ff366422f3da006fd11a0717a7863ac + languageName: node + linkType: hard + "zip-stream@npm:^4.1.0": version: 4.1.1 resolution: "zip-stream@npm:4.1.1" @@ -20011,6 +20243,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^3.23.8": + version: 3.23.8 + resolution: "zod@npm:3.23.8" + checksum: 8f14c87d6b1b53c944c25ce7a28616896319d95bc46a9660fe441adc0ed0a81253b02b5abdaeffedbeb23bdd25a0bf1c29d2c12dd919aef6447652dd295e3e69 + languageName: node + linkType: hard + "zwitch@npm:^2.0.0": version: 2.0.4 resolution: "zwitch@npm:2.0.4" From b8ca015028e98981e863ce1c959df265df599036 Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Mon, 22 Jul 2024 16:19:51 +0000 Subject: [PATCH 02/17] docs: Add the conform-validtor middleware usage to README.md --- packages/conform-validator/README.md | 102 +++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/packages/conform-validator/README.md b/packages/conform-validator/README.md index f1b821f00..e1649c89d 100644 --- a/packages/conform-validator/README.md +++ b/packages/conform-validator/README.md @@ -1,11 +1,113 @@ # Conform validator middleware for Hono +The validator middleware using [conform](https://conform.guide) for [Hono](https://honojs.dev) applications. This middleware allows you to validate submitted FormValue and making better use of [Hono RPC](https://hono.dev/docs/guides/rpc). + ## Usage +Zod: + +```ts +import { z } from 'zod' +import { parseWithZod } from '@conform-to/zod' +import { conformValidator } from '@hono/conform-validator' + +const schema = z.object({ + name: z.string(), + age: z.string() +}) + +app.post( + '/author', + conformValidator((formData) => parseWithZod(formData, { schema })), + (c) => { + const submission = c.req.valid('form') + + if(submission.status === 'success') { + const data = submission.value; + + return c.json({ + success: true, + message: `${data.name} is ${data.age}`, + }) + } + + throw c.json({ success: false, message: `Bad Request` }, 400) + } +) +``` + + +Yup: + +```ts +import { object, string } from 'yup' +import { parseWithYup } from '@conform-to/yup' +import { conformValidator } from '@hono/conform-validator' + +const schema = object({ + name: string(), + age: string() +}) + +app.post( + '/author', + conformValidator((formData) => parseWithYup(formData, { schema })), + (c) => { + const submission = c.req.valid('form') + + if(submission.status === 'success') { + const data = submission.value; + + return c.json({ + success: true, + message: `${data.name} is ${data.age}`, + }) + } + + throw c.json({ success: false, message: `Bad Request` }, 400) + } +) +``` + + +Valibot: + +```ts +import { object, string } from 'valibot' +import { parseWithValibot } from 'conform-to-valibot' +import { conformValidator } from '@hono/conform-validator' + +const schema = object({ + name: string(), + age: string() +}) + +app.post( + '/author', + conformValidator((formData) => parseWithYup(formData, { schema })), + (c) => { + const submission = c.req.valid('form') + + if(submission.status === 'success') { + const data = submission.value; + + return c.json({ + success: true, + message: `${data.name} is ${data.age}`, + }) + } + + throw c.json({ success: false, message: `Bad Request` }, 400) + } +) +``` + ## Author +uttk + ## License From d03939944a9a113d0d4f5f925dedb8e3ef61021b Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Tue, 23 Jul 2024 02:56:40 +0000 Subject: [PATCH 03/17] docs: fix README --- packages/conform-validator/README.md | 43 ++++++++++++++-------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/packages/conform-validator/README.md b/packages/conform-validator/README.md index e1649c89d..50a268ae9 100644 --- a/packages/conform-validator/README.md +++ b/packages/conform-validator/README.md @@ -2,29 +2,29 @@ The validator middleware using [conform](https://conform.guide) for [Hono](https://honojs.dev) applications. This middleware allows you to validate submitted FormValue and making better use of [Hono RPC](https://hono.dev/docs/guides/rpc). - ## Usage -Zod: +Zod: ```ts import { z } from 'zod' import { parseWithZod } from '@conform-to/zod' import { conformValidator } from '@hono/conform-validator' +import { HTTPException } from 'hono/http-exception' const schema = z.object({ name: z.string(), - age: z.string() + age: z.string(), }) app.post( - '/author', + '/author', conformValidator((formData) => parseWithZod(formData, { schema })), (c) => { const submission = c.req.valid('form') - if(submission.status === 'success') { - const data = submission.value; + if (submission.status === 'success') { + const data = submission.value return c.json({ success: true, @@ -32,32 +32,33 @@ app.post( }) } - throw c.json({ success: false, message: `Bad Request` }, 400) + const res = c.json({ success: false, message: `Bad Request` }) + return HTTPException(400, { res }) } ) ``` - Yup: ```ts import { object, string } from 'yup' import { parseWithYup } from '@conform-to/yup' import { conformValidator } from '@hono/conform-validator' +import { HTTPException } from 'hono/http-exception' const schema = object({ name: string(), - age: string() + age: string(), }) app.post( - '/author', + '/author', conformValidator((formData) => parseWithYup(formData, { schema })), (c) => { const submission = c.req.valid('form') - if(submission.status === 'success') { - const data = submission.value; + if (submission.status === 'success') { + const data = submission.value return c.json({ success: true, @@ -65,32 +66,33 @@ app.post( }) } - throw c.json({ success: false, message: `Bad Request` }, 400) + const res = c.json({ success: false, message: `Bad Request` }) + return HTTPException(400, { res }) } ) ``` - Valibot: ```ts import { object, string } from 'valibot' import { parseWithValibot } from 'conform-to-valibot' import { conformValidator } from '@hono/conform-validator' +import { HTTPException } from 'hono/http-exception' const schema = object({ name: string(), - age: string() + age: string(), }) app.post( - '/author', + '/author', conformValidator((formData) => parseWithYup(formData, { schema })), (c) => { const submission = c.req.valid('form') - if(submission.status === 'success') { - const data = submission.value; + if (submission.status === 'success') { + const data = submission.value return c.json({ success: true, @@ -98,17 +100,16 @@ app.post( }) } - throw c.json({ success: false, message: `Bad Request` }, 400) + const res = c.json({ success: false, message: `Bad Request` }) + return HTTPException(400, { res }) } ) ``` - ## Author uttk - ## License MIT From 51eb87ea4a358f092c550f5b6de50310ee26b87b Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Tue, 23 Jul 2024 04:04:36 +0000 Subject: [PATCH 04/17] refactor: Fix tests to use HTTPException --- packages/conform-validator/README.md | 12 ++++++------ packages/conform-validator/test/valibot.test.ts | 14 +++++--------- packages/conform-validator/test/yup.test.ts | 14 +++++--------- packages/conform-validator/test/zod.test.ts | 14 +++++--------- 4 files changed, 21 insertions(+), 33 deletions(-) diff --git a/packages/conform-validator/README.md b/packages/conform-validator/README.md index 50a268ae9..db5ff5f71 100644 --- a/packages/conform-validator/README.md +++ b/packages/conform-validator/README.md @@ -32,8 +32,8 @@ app.post( }) } - const res = c.json({ success: false, message: `Bad Request` }) - return HTTPException(400, { res }) + const res = c.json({ success: false, message: `Bad Request` }, 400) + throw HTTPException(400, { res }) } ) ``` @@ -66,8 +66,8 @@ app.post( }) } - const res = c.json({ success: false, message: `Bad Request` }) - return HTTPException(400, { res }) + const res = c.json({ success: false, message: `Bad Request` }, 400) + throw HTTPException(400, { res }) } ) ``` @@ -100,8 +100,8 @@ app.post( }) } - const res = c.json({ success: false, message: `Bad Request` }) - return HTTPException(400, { res }) + const res = c.json({ success: false, message: `Bad Request` }, 400) + throw HTTPException(400, { res }) } ) ``` diff --git a/packages/conform-validator/test/valibot.test.ts b/packages/conform-validator/test/valibot.test.ts index dd79cb55c..204ffb095 100644 --- a/packages/conform-validator/test/valibot.test.ts +++ b/packages/conform-validator/test/valibot.test.ts @@ -1,8 +1,9 @@ +import type { ExtractSchema } from 'hono/types' +import type { Equal, Expect } from 'hono/utils/types' import * as v from 'valibot' import { Hono } from 'hono' import { hc } from 'hono/client' -import type { ExtractSchema } from 'hono/types' -import type { Equal, Expect } from 'hono/utils/types' +import { HTTPException } from 'hono/http-exception' import { parseWithValibot } from 'conform-to-valibot' import { conformValidator } from '../src' @@ -36,13 +37,8 @@ describe('Validate requests using a Valibot schema', () => { }) } - return c.json( - { - success: false, - message: 'Bad Request', - }, - 400 - ) + const res = c.json({ success: false, message: 'Bad Request' }, 400) + throw new HTTPException(400, { res }) } ) diff --git a/packages/conform-validator/test/yup.test.ts b/packages/conform-validator/test/yup.test.ts index 005b3270d..e57fb277b 100644 --- a/packages/conform-validator/test/yup.test.ts +++ b/packages/conform-validator/test/yup.test.ts @@ -1,8 +1,9 @@ +import type { ExtractSchema } from 'hono/types' +import type { Equal, Expect } from 'hono/utils/types' import * as y from 'yup' import { Hono } from 'hono' import { hc } from 'hono/client' -import type { ExtractSchema } from 'hono/types' -import type { Equal, Expect } from 'hono/utils/types' +import { HTTPException } from 'hono/http-exception' import { parseWithYup } from '@conform-to/yup' import { conformValidator } from '../src' @@ -32,13 +33,8 @@ describe('Validate requests using a Valibot schema', () => { }) } - return c.json( - { - success: false, - message: 'Bad Request', - }, - 400 - ) + const res = c.json({ success: false, message: 'Bad Request' }, 400) + throw new HTTPException(400, { res }) } ) diff --git a/packages/conform-validator/test/zod.test.ts b/packages/conform-validator/test/zod.test.ts index 0b0df94c1..d010fdcb1 100644 --- a/packages/conform-validator/test/zod.test.ts +++ b/packages/conform-validator/test/zod.test.ts @@ -1,8 +1,9 @@ +import type { ExtractSchema } from 'hono/types' +import type { Equal, Expect } from 'hono/utils/types' import * as z from 'zod' import { Hono } from 'hono' import { hc } from 'hono/client' -import type { ExtractSchema } from 'hono/types' -import type { Equal, Expect } from 'hono/utils/types' +import { HTTPException } from 'hono/http-exception' import { parseWithZod } from '@conform-to/zod' import { conformValidator } from '../src' @@ -32,13 +33,8 @@ describe('Validate requests using a Valibot schema', () => { }) } - return c.json( - { - success: false, - message: 'Bad Request', - }, - 400 - ) + const res = c.json({ success: false, message: 'Bad Request' }, 400) + throw new HTTPException(400, { res }) } ) From 74fe00022d5c85772935ef146c7a8137bc39cf4c Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Tue, 23 Jul 2024 04:40:17 +0000 Subject: [PATCH 05/17] fix: update devDependencies in conform-validator --- packages/conform-validator/package.json | 10 +- .../conform-validator/test/valibot.test.ts | 13 +- packages/conform-validator/test/yup.test.ts | 13 +- packages/conform-validator/test/zod.test.ts | 13 +- yarn.lock | 133 +++++++++++------- 5 files changed, 111 insertions(+), 71 deletions(-) diff --git a/packages/conform-validator/package.json b/packages/conform-validator/package.json index 27842987c..035dfb87a 100644 --- a/packages/conform-validator/package.json +++ b/packages/conform-validator/package.json @@ -27,18 +27,18 @@ }, "homepage": "https://github.com/honojs/middleware", "peerDependencies": { - "hono": ">=3.9.0", - "@conform-to/dom":">=1.1.5" + "@conform-to/dom": ">=1.1.5", + "hono": ">=4.5.1" }, "devDependencies": { "@conform-to/dom": "^1.1.5", "@conform-to/yup": "^1.1.5", "@conform-to/zod": "^1.1.5", "conform-to-valibot": "^1.10.0", - "hono": "^4.0.10", - "rimraf": "^5.0.5", + "hono": "^4.5.1", + "rimraf": "^5.0.9", "valibot": "^0.36.0", - "vitest": "^2.0.3", + "vitest": "^2.0.4", "yup": "^1.4.0", "zod": "^3.23.8" } diff --git a/packages/conform-validator/test/valibot.test.ts b/packages/conform-validator/test/valibot.test.ts index 204ffb095..64c6f19fa 100644 --- a/packages/conform-validator/test/valibot.test.ts +++ b/packages/conform-validator/test/valibot.test.ts @@ -1,5 +1,6 @@ -import type { ExtractSchema } from 'hono/types' +import type { ExtractSchema, ParsedFormValue } from 'hono/types' import type { Equal, Expect } from 'hono/utils/types' +import type { StatusCode } from 'hono/utils/http-status' import * as v from 'valibot' import { Hono } from 'hono' import { hc } from 'hono/client' @@ -37,7 +38,7 @@ describe('Validate requests using a Valibot schema', () => { }) } - const res = c.json({ success: false, message: 'Bad Request' }, 400) + const res = c.json({ success: false, message: 'Bad Request' }) throw new HTTPException(400, { res }) } ) @@ -49,15 +50,17 @@ describe('Validate requests using a Valibot schema', () => { $post: { input: { form: { - name: string | File - age: string | File - nickname?: string | File | undefined + name: ParsedFormValue | ParsedFormValue[] + age: ParsedFormValue | ParsedFormValue[] + nickname?: ParsedFormValue | ParsedFormValue[] | undefined } } output: { success: boolean message: string } + outputFormat: 'json' + status: StatusCode } } } diff --git a/packages/conform-validator/test/yup.test.ts b/packages/conform-validator/test/yup.test.ts index e57fb277b..8b7a68e1d 100644 --- a/packages/conform-validator/test/yup.test.ts +++ b/packages/conform-validator/test/yup.test.ts @@ -1,5 +1,6 @@ -import type { ExtractSchema } from 'hono/types' +import type { ExtractSchema, ParsedFormValue } from 'hono/types' import type { Equal, Expect } from 'hono/utils/types' +import type { StatusCode } from 'hono/utils/http-status' import * as y from 'yup' import { Hono } from 'hono' import { hc } from 'hono/client' @@ -33,7 +34,7 @@ describe('Validate requests using a Valibot schema', () => { }) } - const res = c.json({ success: false, message: 'Bad Request' }, 400) + const res = c.json({ success: false, message: 'Bad Request' }) throw new HTTPException(400, { res }) } ) @@ -45,15 +46,17 @@ describe('Validate requests using a Valibot schema', () => { $post: { input: { form: { - name: string | File - age: string | File - nickname?: string | File | undefined + name: ParsedFormValue | ParsedFormValue[] + age: ParsedFormValue | ParsedFormValue[] + nickname?: ParsedFormValue | ParsedFormValue[] | undefined } } output: { success: boolean message: string } + outputFormat: 'json' + status: StatusCode } } } diff --git a/packages/conform-validator/test/zod.test.ts b/packages/conform-validator/test/zod.test.ts index d010fdcb1..5eef72b08 100644 --- a/packages/conform-validator/test/zod.test.ts +++ b/packages/conform-validator/test/zod.test.ts @@ -1,5 +1,6 @@ -import type { ExtractSchema } from 'hono/types' +import type { ExtractSchema, ParsedFormValue } from 'hono/types' import type { Equal, Expect } from 'hono/utils/types' +import type { StatusCode } from 'hono/utils/http-status' import * as z from 'zod' import { Hono } from 'hono' import { hc } from 'hono/client' @@ -33,7 +34,7 @@ describe('Validate requests using a Valibot schema', () => { }) } - const res = c.json({ success: false, message: 'Bad Request' }, 400) + const res = c.json({ success: false, message: 'Bad Request' }) throw new HTTPException(400, { res }) } ) @@ -45,15 +46,17 @@ describe('Validate requests using a Valibot schema', () => { $post: { input: { form: { - name: string | File - age: string | File - nickname?: string | File | undefined + name: ParsedFormValue | ParsedFormValue[] + age: ParsedFormValue | ParsedFormValue[] + nickname?: ParsedFormValue | ParsedFormValue[] | undefined } } output: { success: boolean message: string } + outputFormat: 'json' + status: StatusCode } } } diff --git a/yarn.lock b/yarn.lock index adf6d76ef..c5e58c7fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2065,14 +2065,15 @@ __metadata: "@conform-to/yup": "npm:^1.1.5" "@conform-to/zod": "npm:^1.1.5" conform-to-valibot: "npm:^1.10.0" - hono: "npm:^4.0.10" - rimraf: "npm:^5.0.5" + hono: "npm:^4.5.1" + rimraf: "npm:^5.0.9" valibot: "npm:^0.36.0" - vitest: "npm:^2.0.3" + vitest: "npm:^2.0.4" yup: "npm:^1.4.0" zod: "npm:^3.23.8" peerDependencies: - hono: ">=3.9.0" + "@conform-to/dom": ">=1.1.5" + hono: ">=4.5.1" languageName: unknown linkType: soft @@ -4845,24 +4846,24 @@ __metadata: languageName: node linkType: hard -"@vitest/expect@npm:2.0.3": - version: 2.0.3 - resolution: "@vitest/expect@npm:2.0.3" +"@vitest/expect@npm:2.0.4": + version: 2.0.4 + resolution: "@vitest/expect@npm:2.0.4" dependencies: - "@vitest/spy": "npm:2.0.3" - "@vitest/utils": "npm:2.0.3" + "@vitest/spy": "npm:2.0.4" + "@vitest/utils": "npm:2.0.4" chai: "npm:^5.1.1" tinyrainbow: "npm:^1.2.0" - checksum: bc8dead850a8aeb84a0d5d8620e1437752cbfe10908c2d5ec9f80fc6d9c387d70c964abfd2d6caf76da2882022c0dd05b0fa09b7c2a44d65abdde2b6c73517fe + checksum: 18acdd6b1f5001830722fab7d41b0bd754e37572dded74d1549c5e8f40e58d9e4bbbb6a8ce6be1200b04653237329ba1aeeb3330c2a41f1024450016464d491e languageName: node linkType: hard -"@vitest/pretty-format@npm:2.0.3, @vitest/pretty-format@npm:^2.0.3": - version: 2.0.3 - resolution: "@vitest/pretty-format@npm:2.0.3" +"@vitest/pretty-format@npm:2.0.4, @vitest/pretty-format@npm:^2.0.4": + version: 2.0.4 + resolution: "@vitest/pretty-format@npm:2.0.4" dependencies: tinyrainbow: "npm:^1.2.0" - checksum: 217fd176fa4d1e64e04bc6a187d146381e99921f46007f98f7132d0e31e2c14b9c6d050a150331b3368ee8004bbeab5b1b7d477522a4e4d71ad822d046debc16 + checksum: c2ac3ca302b93ad53ea2977209ee4eb31a313c18690034a09f8ec5528d7e82715c233c4927ecf8b364203c5e5475231d9b737b3fb7680eea71882e1eae11e473 languageName: node linkType: hard @@ -4942,13 +4943,13 @@ __metadata: languageName: node linkType: hard -"@vitest/runner@npm:2.0.3": - version: 2.0.3 - resolution: "@vitest/runner@npm:2.0.3" +"@vitest/runner@npm:2.0.4": + version: 2.0.4 + resolution: "@vitest/runner@npm:2.0.4" dependencies: - "@vitest/utils": "npm:2.0.3" + "@vitest/utils": "npm:2.0.4" pathe: "npm:^1.1.2" - checksum: efbf646457c29268f0d370985d8cbfcfc7d181693dfc2e061dd05ce911f43592957f2c866cde1b5b2e3078ae5d74b94dc28453e1c70b80e8467440223431e863 + checksum: b550372ce5e2c6a3f08dbd584ea669723fc0d789ebaa4224b703f12e908813fb76b963ea9ac2265aa751cab0309f637dc1fa7ce3fb3e67e08e52e241d33237ee languageName: node linkType: hard @@ -5029,14 +5030,14 @@ __metadata: languageName: node linkType: hard -"@vitest/snapshot@npm:2.0.3": - version: 2.0.3 - resolution: "@vitest/snapshot@npm:2.0.3" +"@vitest/snapshot@npm:2.0.4": + version: 2.0.4 + resolution: "@vitest/snapshot@npm:2.0.4" dependencies: - "@vitest/pretty-format": "npm:2.0.3" + "@vitest/pretty-format": "npm:2.0.4" magic-string: "npm:^0.30.10" pathe: "npm:^1.1.2" - checksum: dc7e2e8f60d40c308c487effe2cd94c42bffa795c2d8c740c30b880b451637763891609a052afe29f0c9872e71141d439cb03118595e4a461fe6b4877ae99878 + checksum: 67608c5b1e2f8b02ebc95286cd644c31ea29344c81d67151375b6eebf088a0eea242756eefb509aac626b8f7f091044fdcbc80d137d811ead1117a4a524e2d74 languageName: node linkType: hard @@ -5103,12 +5104,12 @@ __metadata: languageName: node linkType: hard -"@vitest/spy@npm:2.0.3": - version: 2.0.3 - resolution: "@vitest/spy@npm:2.0.3" +"@vitest/spy@npm:2.0.4": + version: 2.0.4 + resolution: "@vitest/spy@npm:2.0.4" dependencies: tinyspy: "npm:^3.0.0" - checksum: 4780aeed692c52756d70735b633ad58f201b2b8729b9e46c4cf968b8e4174e2c2cddd099de669019771bcd8e1ca32d0b9fa42d962e431fdf473b62393b9d2a0a + checksum: ef0d0c5e36bb6dfa3ef7561368b39c92cd89bb52d112ec13345dfc99981796a9af98bafd35ce6952322a6a7534eaad144485fe7764628d94d77edeba5fa773b6 languageName: node linkType: hard @@ -5194,15 +5195,15 @@ __metadata: languageName: node linkType: hard -"@vitest/utils@npm:2.0.3": - version: 2.0.3 - resolution: "@vitest/utils@npm:2.0.3" +"@vitest/utils@npm:2.0.4": + version: 2.0.4 + resolution: "@vitest/utils@npm:2.0.4" dependencies: - "@vitest/pretty-format": "npm:2.0.3" + "@vitest/pretty-format": "npm:2.0.4" estree-walker: "npm:^3.0.3" loupe: "npm:^3.1.1" tinyrainbow: "npm:^1.2.0" - checksum: 41b64c07814e7d576ebe7d11d277eb104a2aafb986497855a59f641b45fa53a30a2bfea525cd913e91b695f444a7a48b1f1e5909c27d5a989b0aea68f2242bd9 + checksum: 48e0bad3aa463d147b125e355b6bc6c5b4a5eab600132ebafac8379800273b2f47df17dbf76fe179b1500cc6b5866ead2d375a39a9114a03f705eb8850b93afa languageName: node linkType: hard @@ -10287,6 +10288,13 @@ __metadata: languageName: node linkType: hard +"hono@npm:^4.5.1": + version: 4.5.1 + resolution: "hono@npm:4.5.1" + checksum: 71228cefd3808b4bb42c10de23d67f135678eb0ab7fdfd1728d934ec2ee1241be9b1260f279b81acd2a6a57346d06d047a2650ff9bddd48fda6581751a690d80 + languageName: node + linkType: hard + "hosted-git-info@npm:^2.1.4": version: 2.8.9 resolution: "hosted-git-info@npm:2.8.9" @@ -16422,6 +16430,17 @@ __metadata: languageName: node linkType: hard +"rimraf@npm:^5.0.9": + version: 5.0.9 + resolution: "rimraf@npm:5.0.9" + dependencies: + glob: "npm:^10.3.7" + bin: + rimraf: dist/esm/bin.mjs + checksum: 87374682492b9e64de9c6fcbf2c8f209c7a2cd0e9749b3732eef8a62c6f859a9ed996d46f662d9ad5dd38c2c469f8e88de56b6c509026070ee3f06369cac1bc8 + languageName: node + linkType: hard + "rollup-plugin-inject@npm:^3.0.0": version: 3.0.2 resolution: "rollup-plugin-inject@npm:3.0.2" @@ -19151,9 +19170,9 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:2.0.3": - version: 2.0.3 - resolution: "vite-node@npm:2.0.3" +"vite-node@npm:2.0.4": + version: 2.0.4 + resolution: "vite-node@npm:2.0.4" dependencies: cac: "npm:^6.7.14" debug: "npm:^4.3.5" @@ -19162,7 +19181,7 @@ __metadata: vite: "npm:^5.0.0" bin: vite-node: vite-node.mjs - checksum: a1bcc110aeb49e79a50ae0df41ca692d39e0d992702f7c5b095c969f622eb72636543bed79efb7131fdedaa4c44a6c9c19daf6fca909240acc1f27f79b978c11 + checksum: 2689b05b391b59cf3d15e1e80884e9b054f2ca90b2150cc7a08b0f234e79e6750a28cc8d107a57f005185e759c3bc020030f687065317fc37fe169ce17f4cdb7 languageName: node linkType: hard @@ -19605,17 +19624,17 @@ __metadata: languageName: node linkType: hard -"vitest@npm:^2.0.3": - version: 2.0.3 - resolution: "vitest@npm:2.0.3" +"vitest@npm:^2.0.4": + version: 2.0.4 + resolution: "vitest@npm:2.0.4" dependencies: "@ampproject/remapping": "npm:^2.3.0" - "@vitest/expect": "npm:2.0.3" - "@vitest/pretty-format": "npm:^2.0.3" - "@vitest/runner": "npm:2.0.3" - "@vitest/snapshot": "npm:2.0.3" - "@vitest/spy": "npm:2.0.3" - "@vitest/utils": "npm:2.0.3" + "@vitest/expect": "npm:2.0.4" + "@vitest/pretty-format": "npm:^2.0.4" + "@vitest/runner": "npm:2.0.4" + "@vitest/snapshot": "npm:2.0.4" + "@vitest/spy": "npm:2.0.4" + "@vitest/utils": "npm:2.0.4" chai: "npm:^5.1.1" debug: "npm:^4.3.5" execa: "npm:^8.0.1" @@ -19626,13 +19645,13 @@ __metadata: tinypool: "npm:^1.0.0" tinyrainbow: "npm:^1.2.0" vite: "npm:^5.0.0" - vite-node: "npm:2.0.3" - why-is-node-running: "npm:^2.2.2" + vite-node: "npm:2.0.4" + why-is-node-running: "npm:^2.3.0" peerDependencies: "@edge-runtime/vm": "*" "@types/node": ^18.0.0 || >=20.0.0 - "@vitest/browser": 2.0.3 - "@vitest/ui": 2.0.3 + "@vitest/browser": 2.0.4 + "@vitest/ui": 2.0.4 happy-dom: "*" jsdom: "*" peerDependenciesMeta: @@ -19650,7 +19669,7 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 1801ec31eb144063d14a03d054ff573869732dcaf69abd4fefdabe011d183599a7493e49d8e180b29808675309814421c4a12271fb140c708e7c9f68c4a37a3c + checksum: 139200d0bda3270fd00641e4bd5524f78a2b1fe9a3d4a0d5ba2b6ed08bbcf6f1e711cc4bfd8b0d823628a2fcab00f822bb210bd5bf3c6a9260fd6115ea085a3d languageName: node linkType: hard @@ -19802,6 +19821,18 @@ __metadata: languageName: node linkType: hard +"why-is-node-running@npm:^2.3.0": + version: 2.3.0 + resolution: "why-is-node-running@npm:2.3.0" + dependencies: + siginfo: "npm:^2.0.0" + stackback: "npm:0.0.2" + bin: + why-is-node-running: cli.js + checksum: 1cde0b01b827d2cf4cb11db962f3958b9175d5d9e7ac7361d1a7b0e2dc6069a263e69118bd974c4f6d0a890ef4eedfe34cf3d5167ec14203dbc9a18620537054 + languageName: node + linkType: hard + "widest-line@npm:^3.1.0": version: 3.1.0 resolution: "widest-line@npm:3.1.0" From 355a64e53a07e773183512e30fbbce7b397889b0 Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:42:26 +0000 Subject: [PATCH 06/17] chore: add github workflows for conform-validator --- .github/workflows/ci-conform-validator.yml | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/ci-conform-validator.yml diff --git a/.github/workflows/ci-conform-validator.yml b/.github/workflows/ci-conform-validator.yml new file mode 100644 index 000000000..6937b58d2 --- /dev/null +++ b/.github/workflows/ci-conform-validator.yml @@ -0,0 +1,25 @@ +name: ci-conform-validator +on: + push: + branches: [main] + paths: + - 'packages/conform-validator/**' + pull_request: + branches: ['*'] + paths: + - 'packages/conform-validator/**' + +jobs: + ci: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./packages/conform-validator + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 18.x + - run: yarn install --frozen-lockfile + - run: yarn build + - run: yarn test From 10b271740297517ece00eb8b4c513dad5b9cb3b7 Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:44:09 +0000 Subject: [PATCH 07/17] feat: add changesets --- .changeset/pretty-eels-prove.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/pretty-eels-prove.md diff --git a/.changeset/pretty-eels-prove.md b/.changeset/pretty-eels-prove.md new file mode 100644 index 000000000..6f8415acd --- /dev/null +++ b/.changeset/pretty-eels-prove.md @@ -0,0 +1,5 @@ +--- +'@hono/conform-validator': patch +--- + +Create Conform validator middleware From 29a96c73497337ddb515072459ba33892c9953f3 Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:46:36 +0000 Subject: [PATCH 08/17] fix: Init conform-validator version to 0.0.0 for changesets --- packages/conform-validator/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/conform-validator/package.json b/packages/conform-validator/package.json index 035dfb87a..9796c4d45 100644 --- a/packages/conform-validator/package.json +++ b/packages/conform-validator/package.json @@ -1,6 +1,6 @@ { "name": "@hono/conform-validator", - "version": "0.0.1", + "version": "0.0.0", "description": "Validator middleware using Conform", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", From df322f53a78106dcbe3db7a7f009fc067fee987d Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Thu, 25 Jul 2024 09:34:16 +0000 Subject: [PATCH 09/17] feat: Add a hook option to `conformValidator()` --- packages/conform-validator/src/index.ts | 25 ++++++-- packages/conform-validator/test/hook.test.ts | 65 ++++++++++++++++++++ 2 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 packages/conform-validator/test/hook.test.ts diff --git a/packages/conform-validator/src/index.ts b/packages/conform-validator/src/index.ts index d7fbc0d00..336397124 100644 --- a/packages/conform-validator/src/index.ts +++ b/packages/conform-validator/src/index.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import type { Env, Input as HonoInput, MiddlewareHandler, ValidationTargets } from 'hono' +import type { Context, Env, Input as HonoInput, MiddlewareHandler, ValidationTargets } from 'hono' import type { Submission } from '@conform-to/dom' import { getFormDataFromContext } from './utils' @@ -14,6 +14,11 @@ type GetInput = T extends (_: any) => infer S type ParseFn = (formData: FormData) => Submission | Promise> +type Hook = ( + submission: Awaited>, + c: Context +) => Response | Promise | void | Promise + export const conformValidator = < F extends ParseFn, E extends Env, @@ -27,13 +32,21 @@ export const conformValidator = < out: { form: Out } } >( - parse: F + parse: F, + hook?: Hook ): MiddlewareHandler => { - return async (ctx, next) => { - const formData = await getFormDataFromContext(ctx) - const submission = parse(formData) + return async (c, next) => { + const formData = await getFormDataFromContext(c) + const submission = await parse(formData) + + if (hook) { + const hookResult = hook(submission as any, c) + if (hookResult instanceof Response || hookResult instanceof Promise) { + return hookResult + } + } - ctx.req.addValidatedData('form', submission) + c.req.addValidatedData('form', submission) await next() } diff --git a/packages/conform-validator/test/hook.test.ts b/packages/conform-validator/test/hook.test.ts new file mode 100644 index 000000000..e20a9ecee --- /dev/null +++ b/packages/conform-validator/test/hook.test.ts @@ -0,0 +1,65 @@ +import * as z from 'zod' +import { Hono } from 'hono' +import { hc } from 'hono/client' +import { HTTPException } from 'hono/http-exception' +import { parseWithZod } from '@conform-to/zod' +import { conformValidator } from '../src' +import { vi } from 'vitest' + +describe('Validate requests using a Valibot schema', () => { + const app = new Hono() + const schema = z.object({ name: z.string() }) + const hookMockFn = vi.fn((submission, c) => { + if (submission.status !== 'success') { + return c.json({ success: false, message: 'Bad Request' }, 400) + } + }) + const handlerMockFn = vi.fn((c) => { + const submission = c.req.valid('form') + if (submission.status !== 'success') { + throw new HTTPException() + } + + const value = submission.value + return c.json({ success: true, message: `name is ${value.name}` }) + }) + const route = app.post( + '/author', + conformValidator((formData) => parseWithZod(formData, { schema }), hookMockFn), + handlerMockFn + ) + const client = hc('http://localhost', { + fetch: (req, init) => { + return app.request(req, init) + }, + }) + + afterEach(() => { + hookMockFn.mockClear() + handlerMockFn.mockClear() + }) + + it('Should called hook function', async () => { + await client.author.$post({ form: { name: 'Space Cat' } }) + expect(hookMockFn).toHaveBeenCalledTimes(1) + }) + + describe('When the hook return Response', () => { + it('Should return response that the hook returned', async () => { + const req = new Request('http://localhost/author', { body: new FormData(), method: 'POST' }) + const res = await app.request(req) + const hookRes = hookMockFn.mock.results[0].value + expect(hookMockFn).toHaveReturnedWith(expect.any(Response)) + expect(res.status).toBe(hookRes.status) + }) + }) + + describe('When the hook not return Response', () => { + it('Should return response that the handler function returned', async () => { + const res = await client.author.$post({ form: { name: 'Space Cat' } }) + const handlerRes = handlerMockFn.mock.results[0].value + expect(hookMockFn).not.toHaveReturnedWith(expect.any(Response)) + expect(res.status).toBe(handlerRes.status) + }) + }) +}) From 07dc7510dfea0ecb0f734835e2134d9d8b7684b6 Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:57:19 +0000 Subject: [PATCH 10/17] feat: Fixed the conformValidator to return an error response when a validation error occurs --- packages/conform-validator/README.md | 61 +++++++++---------- packages/conform-validator/src/index.ts | 8 ++- packages/conform-validator/test/hook.test.ts | 15 ++--- .../conform-validator/test/valibot.test.ts | 32 +++++----- packages/conform-validator/test/yup.test.ts | 32 +++++----- packages/conform-validator/test/zod.test.ts | 32 +++++----- 6 files changed, 84 insertions(+), 96 deletions(-) diff --git a/packages/conform-validator/README.md b/packages/conform-validator/README.md index db5ff5f71..a3fbc8b8a 100644 --- a/packages/conform-validator/README.md +++ b/packages/conform-validator/README.md @@ -22,18 +22,9 @@ app.post( conformValidator((formData) => parseWithZod(formData, { schema })), (c) => { const submission = c.req.valid('form') + const data = submission.value - if (submission.status === 'success') { - const data = submission.value - - return c.json({ - success: true, - message: `${data.name} is ${data.age}`, - }) - } - - const res = c.json({ success: false, message: `Bad Request` }, 400) - throw HTTPException(400, { res }) + return c.json({ success: true, message: `${data.name} is ${data.age}` }) } ) ``` @@ -56,18 +47,8 @@ app.post( conformValidator((formData) => parseWithYup(formData, { schema })), (c) => { const submission = c.req.valid('form') - - if (submission.status === 'success') { - const data = submission.value - - return c.json({ - success: true, - message: `${data.name} is ${data.age}`, - }) - } - - const res = c.json({ success: false, message: `Bad Request` }, 400) - throw HTTPException(400, { res }) + const data = submission.value + return c.json({ success: true, message: `${data.name} is ${data.age}` }) } ) ``` @@ -90,22 +71,38 @@ app.post( conformValidator((formData) => parseWithYup(formData, { schema })), (c) => { const submission = c.req.valid('form') + const data = submission.value + return c.json({ success: true, message: `${data.name} is ${data.age}` }) + } +) +``` - if (submission.status === 'success') { - const data = submission.value +## Custom Hook Option - return c.json({ - success: true, - message: `${data.name} is ${data.age}`, - }) - } +By default, `conformValidator()` returns a [`SubmissionResult`](https://github.com/edmundhung/conform/blob/6b98c077d757edd4846321678dfb6de283c177b1/packages/conform-dom/submission.ts#L40-L47) when a validation error occurs. If you wish to change this behavior, or if you wish to perform common processing, you can modify the response by passing a function as the second argument. - const res = c.json({ success: false, message: `Bad Request` }, 400) - throw HTTPException(400, { res }) +```ts +app.post( + '/author', + conformValidator( + (formData) => parseWithYup(formData, { schema }) + (submission, c) => { + if(submission.status !== 'success') { + return c.json({ success: false, message: 'Bad Request' }, 400) + } + } + ), + (c) => { + const submission = c.req.valid('form') + const data = submission.value + return c.json({ success: true, message: `${data.name} is ${data.age}` }) } ) ``` +> [!NOTE] +> if a response is returned by the Hook function, subsequent middleware or handler functions will not be executed. [see more](https://hono.dev/docs/concepts/middleware). + ## Author uttk diff --git a/packages/conform-validator/src/index.ts b/packages/conform-validator/src/index.ts index 336397124..57d1ccbf3 100644 --- a/packages/conform-validator/src/index.ts +++ b/packages/conform-validator/src/index.ts @@ -12,6 +12,8 @@ type GetInput = T extends (_: any) => infer S : never : never +type GetSuccessSubmission = S extends { status: 'success' } ? S : never + type ParseFn = (formData: FormData) => Submission | Promise> type Hook = ( @@ -29,7 +31,7 @@ export const conformValidator = < in: { form: { [K in keyof In]: FormTargetValue } } - out: { form: Out } + out: { form: GetSuccessSubmission } } >( parse: F, @@ -46,6 +48,10 @@ export const conformValidator = < } } + if (submission.status !== 'success') { + return c.json(submission.reply(), 400) + } + c.req.addValidatedData('form', submission) await next() diff --git a/packages/conform-validator/test/hook.test.ts b/packages/conform-validator/test/hook.test.ts index e20a9ecee..6a7571927 100644 --- a/packages/conform-validator/test/hook.test.ts +++ b/packages/conform-validator/test/hook.test.ts @@ -1,7 +1,6 @@ import * as z from 'zod' import { Hono } from 'hono' import { hc } from 'hono/client' -import { HTTPException } from 'hono/http-exception' import { parseWithZod } from '@conform-to/zod' import { conformValidator } from '../src' import { vi } from 'vitest' @@ -16,10 +15,6 @@ describe('Validate requests using a Valibot schema', () => { }) const handlerMockFn = vi.fn((c) => { const submission = c.req.valid('form') - if (submission.status !== 'success') { - throw new HTTPException() - } - const value = submission.value return c.json({ success: true, message: `name is ${value.name}` }) }) @@ -47,19 +42,21 @@ describe('Validate requests using a Valibot schema', () => { describe('When the hook return Response', () => { it('Should return response that the hook returned', async () => { const req = new Request('http://localhost/author', { body: new FormData(), method: 'POST' }) - const res = await app.request(req) - const hookRes = hookMockFn.mock.results[0].value + const res = (await app.request(req)).clone() + const hookRes = hookMockFn.mock.results[0].value.clone() expect(hookMockFn).toHaveReturnedWith(expect.any(Response)) expect(res.status).toBe(hookRes.status) + expect(await res.json()).toStrictEqual(await hookRes.json()) }) }) describe('When the hook not return Response', () => { it('Should return response that the handler function returned', async () => { - const res = await client.author.$post({ form: { name: 'Space Cat' } }) - const handlerRes = handlerMockFn.mock.results[0].value + const res = (await client.author.$post({ form: { name: 'Space Cat' } })).clone() + const handlerRes = handlerMockFn.mock.results[0].value.clone() expect(hookMockFn).not.toHaveReturnedWith(expect.any(Response)) expect(res.status).toBe(handlerRes.status) + expect(await res.json()).toStrictEqual(await handlerRes.json()) }) }) }) diff --git a/packages/conform-validator/test/valibot.test.ts b/packages/conform-validator/test/valibot.test.ts index 64c6f19fa..d8bc9a2a0 100644 --- a/packages/conform-validator/test/valibot.test.ts +++ b/packages/conform-validator/test/valibot.test.ts @@ -4,7 +4,6 @@ import type { StatusCode } from 'hono/utils/http-status' import * as v from 'valibot' import { Hono } from 'hono' import { hc } from 'hono/client' -import { HTTPException } from 'hono/http-exception' import { parseWithValibot } from 'conform-to-valibot' import { conformValidator } from '../src' @@ -26,20 +25,14 @@ describe('Validate requests using a Valibot schema', () => { conformValidator((formData) => parseWithValibot(formData, { schema })), (c) => { const submission = c.req.valid('form') - - if (submission.status === 'success') { - const value = submission.value - - return c.json({ - success: true, - message: `${value.name} is ${value.age}, nickname is ${ - value?.nickname || 'nothing yet :<' - }`, - }) - } - - const res = c.json({ success: false, message: 'Bad Request' }) - throw new HTTPException(400, { res }) + const value = submission.value + + return c.json({ + success: true, + message: `${value.name} is ${value.age}, nickname is ${ + value?.nickname || 'nothing yet :3' + }`, + }) } ) @@ -107,9 +100,12 @@ describe('Validate requests using a Valibot schema', () => { expect(res.status).toBe(400) const json = await res.json() - expect(json).toEqual({ - success: false, - message: 'Bad Request', + expect(json).toMatchObject({ + status: 'error', + error: { + name: ['Invalid type: Expected string but received undefined'], + age: ['Invalid type: Expected string but received undefined'], + }, }) }) }) diff --git a/packages/conform-validator/test/yup.test.ts b/packages/conform-validator/test/yup.test.ts index 8b7a68e1d..41a054407 100644 --- a/packages/conform-validator/test/yup.test.ts +++ b/packages/conform-validator/test/yup.test.ts @@ -4,7 +4,6 @@ import type { StatusCode } from 'hono/utils/http-status' import * as y from 'yup' import { Hono } from 'hono' import { hc } from 'hono/client' -import { HTTPException } from 'hono/http-exception' import { parseWithYup } from '@conform-to/yup' import { conformValidator } from '../src' @@ -22,20 +21,14 @@ describe('Validate requests using a Valibot schema', () => { conformValidator((formData) => parseWithYup(formData, { schema })), (c) => { const submission = c.req.valid('form') - - if (submission.status === 'success') { - const value = submission.value - - return c.json({ - success: true, - message: `${value.name} is ${value.age}, nickname is ${ - value?.nickname || 'nothing yet :3' - }`, - }) - } - - const res = c.json({ success: false, message: 'Bad Request' }) - throw new HTTPException(400, { res }) + const value = submission.value + + return c.json({ + success: true, + message: `${value.name} is ${value.age}, nickname is ${ + value?.nickname || 'nothing yet :3' + }`, + }) } ) @@ -103,9 +96,12 @@ describe('Validate requests using a Valibot schema', () => { expect(res.status).toBe(400) const json = await res.json() - expect(json).toEqual({ - success: false, - message: 'Bad Request', + expect(json).toMatchObject({ + status: 'error', + error: { + name: ['name is a required field'], + age: ['age is a required field'], + }, }) }) }) diff --git a/packages/conform-validator/test/zod.test.ts b/packages/conform-validator/test/zod.test.ts index 5eef72b08..662f2a830 100644 --- a/packages/conform-validator/test/zod.test.ts +++ b/packages/conform-validator/test/zod.test.ts @@ -4,7 +4,6 @@ import type { StatusCode } from 'hono/utils/http-status' import * as z from 'zod' import { Hono } from 'hono' import { hc } from 'hono/client' -import { HTTPException } from 'hono/http-exception' import { parseWithZod } from '@conform-to/zod' import { conformValidator } from '../src' @@ -22,20 +21,14 @@ describe('Validate requests using a Valibot schema', () => { conformValidator((formData) => parseWithZod(formData, { schema })), (c) => { const submission = c.req.valid('form') - - if (submission.status === 'success') { - const value = submission.value - - return c.json({ - success: true, - message: `${value.name} is ${value.age}, nickname is ${ - value?.nickname || 'nothing yet :<' - }`, - }) - } - - const res = c.json({ success: false, message: 'Bad Request' }) - throw new HTTPException(400, { res }) + const value = submission.value + + return c.json({ + success: true, + message: `${value.name} is ${value.age}, nickname is ${ + value?.nickname || 'nothing yet :3' + }`, + }) } ) @@ -103,9 +96,12 @@ describe('Validate requests using a Valibot schema', () => { expect(res.status).toBe(400) const json = await res.json() - expect(json).toEqual({ - success: false, - message: 'Bad Request', + expect(json).toMatchObject({ + status: 'error', + error: { + name: ['Required'], + age: ['Required'], + }, }) }) }) From b345f847bf44375108a37def31c7d3bce415c12f Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Sat, 27 Jul 2024 12:14:22 +0000 Subject: [PATCH 11/17] fix: Fixed node version used in CI from 18.x to 20.x --- .github/workflows/ci-conform-validator.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-conform-validator.yml b/.github/workflows/ci-conform-validator.yml index 6937b58d2..1ed7a98c0 100644 --- a/.github/workflows/ci-conform-validator.yml +++ b/.github/workflows/ci-conform-validator.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.x - run: yarn install --frozen-lockfile - run: yarn build - run: yarn test From e1b58f86145e72fded5c1a4237a8bd01a230cb29 Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Sat, 27 Jul 2024 12:36:58 +0000 Subject: [PATCH 12/17] fix: Fix to use tsup in build command --- packages/conform-validator/package.json | 24 +- yarn.lock | 542 +++++++++++++++++++++++- 2 files changed, 556 insertions(+), 10 deletions(-) diff --git a/packages/conform-validator/package.json b/packages/conform-validator/package.json index 9796c4d45..3fc6b7f02 100644 --- a/packages/conform-validator/package.json +++ b/packages/conform-validator/package.json @@ -2,20 +2,31 @@ "name": "@hono/conform-validator", "version": "0.0.0", "description": "Validator middleware using Conform", - "main": "dist/cjs/index.js", - "module": "dist/esm/index.js", - "types": "dist/esm/index.d.ts", + "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.js", + "types": "dist/index.d.ts", "files": [ "dist" ], "scripts": { "test": "vitest --run", - "build:cjs": "tsc -p tsconfig.cjs.json", - "build:esm": "tsc -p tsconfig.esm.json", - "build": "rimraf dist && yarn build:cjs && yarn build:esm", + "build": "tsup ./src/index.ts --format esm,cjs --dts", "prerelease": "yarn build && yarn test", "release": "yarn publish" }, + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, "license": "MIT", "publishConfig": { "registry": "https://registry.npmjs.org", @@ -37,6 +48,7 @@ "conform-to-valibot": "^1.10.0", "hono": "^4.5.1", "rimraf": "^5.0.9", + "tsup": "^8.2.3", "valibot": "^0.36.0", "vitest": "^2.0.4", "yup": "^1.4.0", diff --git a/yarn.lock b/yarn.lock index c5e58c7fb..332b8dae2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1096,6 +1096,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/aix-ppc64@npm:0.23.0" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-arm64@npm:0.17.19" @@ -1131,6 +1138,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/android-arm64@npm:0.23.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-arm@npm:0.17.19" @@ -1166,6 +1180,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/android-arm@npm:0.23.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/android-x64@npm:0.17.19" @@ -1201,6 +1222,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/android-x64@npm:0.23.0" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/darwin-arm64@npm:0.17.19" @@ -1236,6 +1264,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/darwin-arm64@npm:0.23.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/darwin-x64@npm:0.17.19" @@ -1271,6 +1306,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/darwin-x64@npm:0.23.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/freebsd-arm64@npm:0.17.19" @@ -1306,6 +1348,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/freebsd-arm64@npm:0.23.0" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/freebsd-x64@npm:0.17.19" @@ -1341,6 +1390,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/freebsd-x64@npm:0.23.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-arm64@npm:0.17.19" @@ -1376,6 +1432,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-arm64@npm:0.23.0" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-arm@npm:0.17.19" @@ -1411,6 +1474,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-arm@npm:0.23.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-ia32@npm:0.17.19" @@ -1446,6 +1516,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-ia32@npm:0.23.0" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-loong64@npm:0.17.19" @@ -1481,6 +1558,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-loong64@npm:0.23.0" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-mips64el@npm:0.17.19" @@ -1516,6 +1600,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-mips64el@npm:0.23.0" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-ppc64@npm:0.17.19" @@ -1551,6 +1642,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-ppc64@npm:0.23.0" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-riscv64@npm:0.17.19" @@ -1586,6 +1684,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-riscv64@npm:0.23.0" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-s390x@npm:0.17.19" @@ -1621,6 +1726,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-s390x@npm:0.23.0" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/linux-x64@npm:0.17.19" @@ -1656,6 +1768,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-x64@npm:0.23.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/netbsd-x64@npm:0.17.19" @@ -1691,6 +1810,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/netbsd-x64@npm:0.23.0" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/openbsd-arm64@npm:0.23.0" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/openbsd-x64@npm:0.17.19" @@ -1726,6 +1859,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/openbsd-x64@npm:0.23.0" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/sunos-x64@npm:0.17.19" @@ -1761,6 +1901,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/sunos-x64@npm:0.23.0" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-arm64@npm:0.17.19" @@ -1796,6 +1943,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/win32-arm64@npm:0.23.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-ia32@npm:0.17.19" @@ -1831,6 +1985,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/win32-ia32@npm:0.23.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.17.19": version: 0.17.19 resolution: "@esbuild/win32-x64@npm:0.17.19" @@ -1866,6 +2027,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/win32-x64@npm:0.23.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -2067,6 +2235,7 @@ __metadata: conform-to-valibot: "npm:^1.10.0" hono: "npm:^4.5.1" rimraf: "npm:^5.0.9" + tsup: "npm:^8.2.3" valibot: "npm:^0.36.0" vitest: "npm:^2.0.4" yup: "npm:^1.4.0" @@ -3757,6 +3926,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm-eabi@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.19.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@rollup/rollup-android-arm-eabi@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-android-arm-eabi@npm:4.9.0" @@ -3764,6 +3940,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm64@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-android-arm64@npm:4.19.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-android-arm64@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-android-arm64@npm:4.9.0" @@ -3771,6 +3954,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-arm64@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-darwin-arm64@npm:4.19.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-arm64@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-darwin-arm64@npm:4.9.0" @@ -3778,6 +3968,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-x64@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-darwin-x64@npm:4.19.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-x64@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-darwin-x64@npm:4.9.0" @@ -3785,6 +3982,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-gnueabihf@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.19.1" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-gnueabihf@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.9.0" @@ -3792,6 +3996,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-musleabihf@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.19.1" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + +"@rollup/rollup-linux-arm64-gnu@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.19.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-gnu@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.9.0" @@ -3799,6 +4017,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-musl@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.19.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-musl@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-linux-arm64-musl@npm:4.9.0" @@ -3806,6 +4031,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.19.1" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-riscv64-gnu@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.19.1" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-riscv64-gnu@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.9.0" @@ -3813,6 +4052,20 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-s390x-gnu@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.19.1" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@rollup/rollup-linux-x64-gnu@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.19.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-gnu@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-linux-x64-gnu@npm:4.9.0" @@ -3820,6 +4073,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-musl@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.19.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-musl@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-linux-x64-musl@npm:4.9.0" @@ -3827,6 +4087,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-arm64-msvc@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.19.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-win32-arm64-msvc@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.9.0" @@ -3834,6 +4101,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-ia32-msvc@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.19.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@rollup/rollup-win32-ia32-msvc@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.9.0" @@ -3841,6 +4115,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-x64-msvc@npm:4.19.1": + version: 4.19.1 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.19.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-win32-x64-msvc@npm:4.9.0": version: 4.9.0 resolution: "@rollup/rollup-win32-x64-msvc@npm:4.9.0" @@ -4152,7 +4433,7 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^1.0.0": +"@types/estree@npm:*, @types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": version: 1.0.5 resolution: "@types/estree@npm:1.0.5" checksum: b3b0e334288ddb407c7b3357ca67dbee75ee22db242ca7c56fe27db4e1a31989cb8af48a84dd401deb787fe10cc6b2ab1ee82dc4783be87ededbe3d53c79c70d @@ -6209,6 +6490,17 @@ __metadata: languageName: node linkType: hard +"bundle-require@npm:^5.0.0": + version: 5.0.0 + resolution: "bundle-require@npm:5.0.0" + dependencies: + load-tsconfig: "npm:^0.2.3" + peerDependencies: + esbuild: ">=0.18" + checksum: 92c46df02586e0ebd66ee4831c9b5775adb3c32a43fe2b2aaf7bc675135c141f751de6a9a26b146d64c607c5b40f9eef5f10dce3c364f602d4bed268444c32c6 + languageName: node + linkType: hard + "busboy@npm:^1.6.0": version: 1.6.0 resolution: "busboy@npm:1.6.0" @@ -6540,7 +6832,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:^3.5.3": +"chokidar@npm:^3.5.3, chokidar@npm:^3.6.0": version: 3.6.0 resolution: "chokidar@npm:3.6.0" dependencies: @@ -6965,6 +7257,13 @@ __metadata: languageName: node linkType: hard +"consola@npm:^3.2.3": + version: 3.2.3 + resolution: "consola@npm:3.2.3" + checksum: c606220524ec88a05bb1baf557e9e0e04a0c08a9c35d7a08652d99de195c4ddcb6572040a7df57a18ff38bbc13ce9880ad032d56630cef27bef72768ef0ac078 + languageName: node + linkType: hard + "content-disposition@npm:0.5.4": version: 0.5.4 resolution: "content-disposition@npm:0.5.4" @@ -8342,6 +8641,89 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.23.0": + version: 0.23.0 + resolution: "esbuild@npm:0.23.0" + dependencies: + "@esbuild/aix-ppc64": "npm:0.23.0" + "@esbuild/android-arm": "npm:0.23.0" + "@esbuild/android-arm64": "npm:0.23.0" + "@esbuild/android-x64": "npm:0.23.0" + "@esbuild/darwin-arm64": "npm:0.23.0" + "@esbuild/darwin-x64": "npm:0.23.0" + "@esbuild/freebsd-arm64": "npm:0.23.0" + "@esbuild/freebsd-x64": "npm:0.23.0" + "@esbuild/linux-arm": "npm:0.23.0" + "@esbuild/linux-arm64": "npm:0.23.0" + "@esbuild/linux-ia32": "npm:0.23.0" + "@esbuild/linux-loong64": "npm:0.23.0" + "@esbuild/linux-mips64el": "npm:0.23.0" + "@esbuild/linux-ppc64": "npm:0.23.0" + "@esbuild/linux-riscv64": "npm:0.23.0" + "@esbuild/linux-s390x": "npm:0.23.0" + "@esbuild/linux-x64": "npm:0.23.0" + "@esbuild/netbsd-x64": "npm:0.23.0" + "@esbuild/openbsd-arm64": "npm:0.23.0" + "@esbuild/openbsd-x64": "npm:0.23.0" + "@esbuild/sunos-x64": "npm:0.23.0" + "@esbuild/win32-arm64": "npm:0.23.0" + "@esbuild/win32-ia32": "npm:0.23.0" + "@esbuild/win32-x64": "npm:0.23.0" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 08c148c067795165798c0467ce02d2d1ecedc096989bded5f0d795c61a1fcbec6c14d0a3c9f4ad6185cc29ec52087acaa335ed6d98be6ad57f7fa4264626bde0 + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -8845,7 +9227,7 @@ __metadata: languageName: node linkType: hard -"execa@npm:^5.0.0": +"execa@npm:^5.0.0, execa@npm:^5.1.1": version: 5.1.1 resolution: "execa@npm:5.1.1" dependencies: @@ -12242,7 +12624,7 @@ __metadata: languageName: node linkType: hard -"joycon@npm:^3.0.1": +"joycon@npm:^3.0.1, joycon@npm:^3.1.1": version: 3.1.1 resolution: "joycon@npm:3.1.1" checksum: 131fb1e98c9065d067fd49b6e685487ac4ad4d254191d7aa2c9e3b90f4e9ca70430c43cad001602bdbdabcf58717d3b5c5b7461c1bd8e39478c8de706b3fe6ae @@ -12680,6 +13062,13 @@ __metadata: languageName: node linkType: hard +"lilconfig@npm:^3.1.1": + version: 3.1.2 + resolution: "lilconfig@npm:3.1.2" + checksum: f059630b1a9bddaeba83059db00c672b64dc14074e9f232adce32b38ca1b5686ab737eb665c5ba3c32f147f0002b4bee7311ad0386a9b98547b5623e87071fbe + languageName: node + linkType: hard + "lines-and-columns@npm:^1.1.6": version: 1.2.4 resolution: "lines-and-columns@npm:1.2.4" @@ -15513,6 +15902,29 @@ __metadata: languageName: node linkType: hard +"postcss-load-config@npm:^6.0.1": + version: 6.0.1 + resolution: "postcss-load-config@npm:6.0.1" + dependencies: + lilconfig: "npm:^3.1.1" + peerDependencies: + jiti: ">=1.21.0" + postcss: ">=8.0.9" + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + checksum: 74173a58816dac84e44853f7afbd283f4ef13ca0b6baeba27701214beec33f9e309b128f8102e2b173e8d45ecba45d279a9be94b46bf48d219626aa9b5730848 + languageName: node + linkType: hard + "postcss@npm:^8.4.27, postcss@npm:^8.4.32": version: 8.4.32 resolution: "postcss@npm:8.4.32" @@ -16537,6 +16949,69 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^4.19.0": + version: 4.19.1 + resolution: "rollup@npm:4.19.1" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.19.1" + "@rollup/rollup-android-arm64": "npm:4.19.1" + "@rollup/rollup-darwin-arm64": "npm:4.19.1" + "@rollup/rollup-darwin-x64": "npm:4.19.1" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.19.1" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.19.1" + "@rollup/rollup-linux-arm64-gnu": "npm:4.19.1" + "@rollup/rollup-linux-arm64-musl": "npm:4.19.1" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.19.1" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.19.1" + "@rollup/rollup-linux-s390x-gnu": "npm:4.19.1" + "@rollup/rollup-linux-x64-gnu": "npm:4.19.1" + "@rollup/rollup-linux-x64-musl": "npm:4.19.1" + "@rollup/rollup-win32-arm64-msvc": "npm:4.19.1" + "@rollup/rollup-win32-ia32-msvc": "npm:4.19.1" + "@rollup/rollup-win32-x64-msvc": "npm:4.19.1" + "@types/estree": "npm:1.0.5" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-powerpc64le-gnu": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 2e526c38b4bcb22a058cf95e40c8c105a86f27d582c677c47df9315a17b18e75c772edc0773ca4d12d58ceca254bb5d63d4172041f6fd9f01e1a613d8bba6d09 + languageName: node + linkType: hard + "router@npm:^1.3.1": version: 1.3.8 resolution: "router@npm:1.3.8" @@ -17550,6 +18025,24 @@ __metadata: languageName: node linkType: hard +"sucrase@npm:^3.35.0": + version: 3.35.0 + resolution: "sucrase@npm:3.35.0" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.2" + commander: "npm:^4.0.0" + glob: "npm:^10.3.10" + lines-and-columns: "npm:^1.1.6" + mz: "npm:^2.7.0" + pirates: "npm:^4.0.1" + ts-interface-checker: "npm:^0.1.9" + bin: + sucrase: bin/sucrase + sucrase-node: bin/sucrase-node + checksum: ac85f3359d2c2ecbf5febca6a24ae9bf96c931f05fde533c22a94f59c6a74895e5d5f0e871878dfd59c2697a75ebb04e4b2224ef0bfc24ca1210735c2ec191ef + languageName: node + linkType: hard + "superstatic@npm:^9.0.3": version: 9.0.3 resolution: "superstatic@npm:9.0.3" @@ -18282,6 +18775,47 @@ __metadata: languageName: node linkType: hard +"tsup@npm:^8.2.3": + version: 8.2.3 + resolution: "tsup@npm:8.2.3" + dependencies: + bundle-require: "npm:^5.0.0" + cac: "npm:^6.7.14" + chokidar: "npm:^3.6.0" + consola: "npm:^3.2.3" + debug: "npm:^4.3.5" + esbuild: "npm:^0.23.0" + execa: "npm:^5.1.1" + globby: "npm:^11.1.0" + joycon: "npm:^3.1.1" + picocolors: "npm:^1.0.1" + postcss-load-config: "npm:^6.0.1" + resolve-from: "npm:^5.0.0" + rollup: "npm:^4.19.0" + source-map: "npm:0.8.0-beta.0" + sucrase: "npm:^3.35.0" + tree-kill: "npm:^1.2.2" + peerDependencies: + "@microsoft/api-extractor": ^7.36.0 + "@swc/core": ^1 + postcss: ^8.4.12 + typescript: ">=4.5.0" + peerDependenciesMeta: + "@microsoft/api-extractor": + optional: true + "@swc/core": + optional: true + postcss: + optional: true + typescript: + optional: true + bin: + tsup: dist/cli-default.js + tsup-node: dist/cli-node.js + checksum: 4a6fba80b441b400e44633db7e52d383cfd502119e6bdf7680ac07d5110eab2473d8b980a664c1564d0418d89c0e680b24a9f43d2d7da1193ce72259a863725a + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0" From 518d780ba380568eac445968301645e38e3af2fe Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Sat, 27 Jul 2024 12:46:34 +0000 Subject: [PATCH 13/17] chore: delete `.skip` from `it` in test files. --- packages/conform-validator/test/valibot.test.ts | 2 +- packages/conform-validator/test/yup.test.ts | 2 +- packages/conform-validator/test/zod.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/conform-validator/test/valibot.test.ts b/packages/conform-validator/test/valibot.test.ts index d8bc9a2a0..363c2d407 100644 --- a/packages/conform-validator/test/valibot.test.ts +++ b/packages/conform-validator/test/valibot.test.ts @@ -36,7 +36,7 @@ describe('Validate requests using a Valibot schema', () => { } ) - it.skip('check the route object types', () => { + it('check the route object types', () => { type Actual = ExtractSchema type Expected = { '/author': { diff --git a/packages/conform-validator/test/yup.test.ts b/packages/conform-validator/test/yup.test.ts index 41a054407..d0870a089 100644 --- a/packages/conform-validator/test/yup.test.ts +++ b/packages/conform-validator/test/yup.test.ts @@ -32,7 +32,7 @@ describe('Validate requests using a Valibot schema', () => { } ) - it.skip('check the route object types', () => { + it('check the route object types', () => { type Actual = ExtractSchema type Expected = { '/author': { diff --git a/packages/conform-validator/test/zod.test.ts b/packages/conform-validator/test/zod.test.ts index 662f2a830..a8cd288a3 100644 --- a/packages/conform-validator/test/zod.test.ts +++ b/packages/conform-validator/test/zod.test.ts @@ -32,7 +32,7 @@ describe('Validate requests using a Valibot schema', () => { } ) - it.skip('check the route object types', () => { + it('check the route object types', () => { type Actual = ExtractSchema type Expected = { '/author': { From 846741a667f741c9b84854835e8446c130d37c30 Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Sat, 27 Jul 2024 12:51:36 +0000 Subject: [PATCH 14/17] chore: fix title in test files. --- packages/conform-validator/test/hook.test.ts | 2 +- packages/conform-validator/test/yup.test.ts | 2 +- packages/conform-validator/test/zod.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/conform-validator/test/hook.test.ts b/packages/conform-validator/test/hook.test.ts index 6a7571927..a4b07dee4 100644 --- a/packages/conform-validator/test/hook.test.ts +++ b/packages/conform-validator/test/hook.test.ts @@ -5,7 +5,7 @@ import { parseWithZod } from '@conform-to/zod' import { conformValidator } from '../src' import { vi } from 'vitest' -describe('Validate requests using a Valibot schema', () => { +describe('Validate the hook option processing', () => { const app = new Hono() const schema = z.object({ name: z.string() }) const hookMockFn = vi.fn((submission, c) => { diff --git a/packages/conform-validator/test/yup.test.ts b/packages/conform-validator/test/yup.test.ts index d0870a089..f8c81d0f2 100644 --- a/packages/conform-validator/test/yup.test.ts +++ b/packages/conform-validator/test/yup.test.ts @@ -7,7 +7,7 @@ import { hc } from 'hono/client' import { parseWithYup } from '@conform-to/yup' import { conformValidator } from '../src' -describe('Validate requests using a Valibot schema', () => { +describe('Validate requests using a Yup schema', () => { const app = new Hono() const schema = y.object({ diff --git a/packages/conform-validator/test/zod.test.ts b/packages/conform-validator/test/zod.test.ts index a8cd288a3..7cb091319 100644 --- a/packages/conform-validator/test/zod.test.ts +++ b/packages/conform-validator/test/zod.test.ts @@ -7,7 +7,7 @@ import { hc } from 'hono/client' import { parseWithZod } from '@conform-to/zod' import { conformValidator } from '../src' -describe('Validate requests using a Valibot schema', () => { +describe('Validate requests using a Zod schema', () => { const app = new Hono() const schema = z.object({ From 5ed20846e8324995394d992ddb206acd24cc8461 Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Sat, 27 Jul 2024 13:09:38 +0000 Subject: [PATCH 15/17] fix: Fixed to return 400 response when the request body is not FormData --- packages/conform-validator/src/utils.ts | 6 +++- .../conform-validator/test/common.test.ts | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 packages/conform-validator/test/common.test.ts diff --git a/packages/conform-validator/src/utils.ts b/packages/conform-validator/src/utils.ts index b0f76de51..9c628402b 100644 --- a/packages/conform-validator/src/utils.ts +++ b/packages/conform-validator/src/utils.ts @@ -1,9 +1,13 @@ import type { Context } from 'hono' import { bufferToFormData } from 'hono/utils/buffer' +// ref: https://github.com/honojs/hono/blob/a63bcfd6fba66297d8234c21aed8a42ac00711fe/src/validator/validator.ts#L27-L28 +const multipartRegex = /^multipart\/form-data(; boundary=[A-Za-z0-9'()+_,\-./:=?]+)?$/ +const urlencodedRegex = /^application\/x-www-form-urlencoded$/ + export const getFormDataFromContext = async (ctx: Context): Promise => { const contentType = ctx.req.header('Content-Type') - if (!contentType) { + if (!contentType || !(multipartRegex.test(contentType) || urlencodedRegex.test(contentType))) { return new FormData() } diff --git a/packages/conform-validator/test/common.test.ts b/packages/conform-validator/test/common.test.ts new file mode 100644 index 000000000..14faca9f3 --- /dev/null +++ b/packages/conform-validator/test/common.test.ts @@ -0,0 +1,35 @@ +import { Hono } from 'hono' +import { z } from 'zod' +import { parseWithZod } from '@conform-to/zod' +import { conformValidator } from '../src' + +describe('Validate common processing', () => { + const app = new Hono() + const schema = z.object({ name: z.string() }) + const route = app.post( + '/author', + conformValidator((formData) => parseWithZod(formData, { schema })), + (c) => { + const submission = c.req.valid('form') + const value = submission.value + return c.json({ success: true, message: `my name is ${value.name}` }) + } + ) + + describe('When the request body is empty', () => { + it('Should return 400 response', async () => { + const res = await route.request('/author', { method: 'POST' }) + expect(res.status).toBe(400) + }) + }) + + describe('When the request body is not FormData', () => { + it('Should return 400 response', async () => { + const res = await route.request('/author', { + method: 'POST', + body: JSON.stringify({ name: 'Space Cat!' }), + }) + expect(res.status).toBe(400) + }) + }) +}) From 0eee178b8f6b482aa81a779c5ac312d245c864e7 Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Sun, 28 Jul 2024 12:50:31 +0000 Subject: [PATCH 16/17] chore: fixed to change patch to major in changeset. --- .changeset/pretty-eels-prove.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/pretty-eels-prove.md b/.changeset/pretty-eels-prove.md index 6f8415acd..e47600f9f 100644 --- a/.changeset/pretty-eels-prove.md +++ b/.changeset/pretty-eels-prove.md @@ -1,5 +1,5 @@ --- -'@hono/conform-validator': patch +'@hono/conform-validator': major --- Create Conform validator middleware From 8775f57a3bf6a031d705ee8a42a599294630325f Mon Sep 17 00:00:00 2001 From: uttk <46495635+uttk@users.noreply.github.com> Date: Sun, 28 Jul 2024 13:00:20 +0000 Subject: [PATCH 17/17] chore: Removed unused libraries --- packages/conform-validator/package.json | 1 - yarn.lock | 12 ------------ 2 files changed, 13 deletions(-) diff --git a/packages/conform-validator/package.json b/packages/conform-validator/package.json index 3fc6b7f02..afa31f27e 100644 --- a/packages/conform-validator/package.json +++ b/packages/conform-validator/package.json @@ -47,7 +47,6 @@ "@conform-to/zod": "^1.1.5", "conform-to-valibot": "^1.10.0", "hono": "^4.5.1", - "rimraf": "^5.0.9", "tsup": "^8.2.3", "valibot": "^0.36.0", "vitest": "^2.0.4", diff --git a/yarn.lock b/yarn.lock index 332b8dae2..1457372a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2234,7 +2234,6 @@ __metadata: "@conform-to/zod": "npm:^1.1.5" conform-to-valibot: "npm:^1.10.0" hono: "npm:^4.5.1" - rimraf: "npm:^5.0.9" tsup: "npm:^8.2.3" valibot: "npm:^0.36.0" vitest: "npm:^2.0.4" @@ -16842,17 +16841,6 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:^5.0.9": - version: 5.0.9 - resolution: "rimraf@npm:5.0.9" - dependencies: - glob: "npm:^10.3.7" - bin: - rimraf: dist/esm/bin.mjs - checksum: 87374682492b9e64de9c6fcbf2c8f209c7a2cd0e9749b3732eef8a62c6f859a9ed996d46f662d9ad5dd38c2c469f8e88de56b6c509026070ee3f06369cac1bc8 - languageName: node - linkType: hard - "rollup-plugin-inject@npm:^3.0.0": version: 3.0.2 resolution: "rollup-plugin-inject@npm:3.0.2"