mirror of
https://github.com/papra-hq/papra
synced 2026-04-21 13:37:23 +00:00
chore(server): fully remove remaining zod
This commit is contained in:
parent
2b0c258f8a
commit
3e6056972c
5 changed files with 13 additions and 217 deletions
|
|
@ -28,7 +28,8 @@
|
|||
"tailwind-merge": "^2.6.0",
|
||||
"unocss-preset-animations": "catalog:",
|
||||
"valibot": "catalog:",
|
||||
"yaml": "^2.8.0"
|
||||
"yaml": "^2.8.0",
|
||||
"zod": "3.25.76"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "catalog:",
|
||||
|
|
|
|||
|
|
@ -83,8 +83,7 @@
|
|||
"sanitize-html": "^2.17.0",
|
||||
"stripe": "^17.7.0",
|
||||
"tsx": "catalog:",
|
||||
"valibot": "catalog:",
|
||||
"zod": "^3.25.67"
|
||||
"valibot": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "catalog:",
|
||||
|
|
|
|||
|
|
@ -1,146 +0,0 @@
|
|||
import { Hono } from 'hono';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { z } from 'zod';
|
||||
import { legacyValidateJsonBody, legacyValidateParams, legacyValidateQuery } from './validation.legacy';
|
||||
|
||||
function makeJsonBodyPayload(payload: Record<string, unknown>) {
|
||||
return {
|
||||
body: JSON.stringify(payload),
|
||||
headers: new Headers({ 'Content-Type': 'application/json' }),
|
||||
};
|
||||
}
|
||||
|
||||
describe('validation', () => {
|
||||
describe('validateJsonBody', () => {
|
||||
describe('validateJsonBody creates a validation middleware that check the request json body against a schema', async () => {
|
||||
test('an invalid payload should trigger a 400 error', async () => {
|
||||
const app = new Hono().post('/', legacyValidateJsonBody(z.object({ name: z.string({ required_error: 'The name is required' }) })), (context) => {
|
||||
return context.json({ ok: true });
|
||||
});
|
||||
|
||||
const response = await app.request('/', { method: 'POST', ...makeJsonBodyPayload({ }) });
|
||||
const responseBody = await response.json();
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(responseBody).toEqual({
|
||||
error: {
|
||||
message: 'Invalid request body',
|
||||
code: 'server.invalid_request.body',
|
||||
details: [{
|
||||
path: 'name',
|
||||
message: 'The name is required',
|
||||
}],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('a valid request should pass through', async () => {
|
||||
const app = new Hono().post('/', legacyValidateJsonBody(z.object({ name: z.string() })), (context) => {
|
||||
return context.json({ ok: true });
|
||||
});
|
||||
|
||||
const response = await app.request('/', { method: 'POST', ...makeJsonBodyPayload({ name: 'hono' }) });
|
||||
const responseBody = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(responseBody).toEqual({ ok: true });
|
||||
});
|
||||
|
||||
test('no additional properties should be allowed', async () => {
|
||||
const app = new Hono().post('/', legacyValidateJsonBody(z.object({ name: z.string() })), (context) => {
|
||||
return context.json({ ok: true });
|
||||
});
|
||||
|
||||
const response = await app.request('/', { method: 'POST', ...makeJsonBodyPayload({ name: 'hono', foo: 'bar' }) });
|
||||
const responseBody = await response.json();
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(responseBody).toEqual({
|
||||
error: {
|
||||
message: 'Invalid request body',
|
||||
code: 'server.invalid_request.body',
|
||||
details: [{
|
||||
message: 'Unrecognized key(s) in object: \'foo\'',
|
||||
}],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateQuery', () => {
|
||||
describe('validateQuery creates a validation middleware that check the request query parameters against a schema', async () => {
|
||||
test('an invalid query should trigger a 400 error', async () => {
|
||||
const app = new Hono().get('/', legacyValidateQuery(z.object({ name: z.string({ required_error: 'The name is required' }) })), (context) => {
|
||||
return context.json({ ok: true });
|
||||
});
|
||||
|
||||
const response = await app.request('/');
|
||||
const responseBody = await response.json();
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(responseBody).toEqual({
|
||||
error: {
|
||||
message: 'Invalid query parameters',
|
||||
code: 'server.invalid_request.query',
|
||||
details: [{
|
||||
path: 'name',
|
||||
message: 'The name is required',
|
||||
}],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('a valid query should pass through', async () => {
|
||||
const app = new Hono().get('/', legacyValidateQuery(z.object({ name: z.string() })), (context) => {
|
||||
return context.json({ ok: true });
|
||||
});
|
||||
|
||||
const response = await app.request('/?name=hono');
|
||||
const responseBody = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(responseBody).toEqual({ ok: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateParams', () => {
|
||||
describe('validateParams creates a validation middleware that check the request url parameters against a schema', async () => {
|
||||
test('an invalid params should trigger a 400 error', async () => {
|
||||
const app = new Hono().get('/:name', legacyValidateParams(z.object({ name: z.string().startsWith('foo-') })), (context) => {
|
||||
return context.json({ ok: true });
|
||||
});
|
||||
|
||||
const response = await app.request('/test');
|
||||
const responseBody = await response.json();
|
||||
|
||||
expect(response.status).toBe(400);
|
||||
expect(responseBody).toEqual({
|
||||
error: {
|
||||
message: 'Invalid URL parameters',
|
||||
code: 'server.invalid_request.params',
|
||||
details: [
|
||||
{
|
||||
path: 'name',
|
||||
message: 'Invalid input: must start with "foo-"',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('a valid params should pass through', async () => {
|
||||
const app = new Hono().get('/:name', legacyValidateParams(z.object({ name: z.string().startsWith('foo-') })), (context) => {
|
||||
return context.json({ ok: true });
|
||||
});
|
||||
|
||||
const response = await app.request('/foo-bar');
|
||||
const responseBody = await response.json();
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(responseBody).toEqual({ ok: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
import type { ValidationTargets } from 'hono';
|
||||
import type z from 'zod';
|
||||
import { validator } from 'hono/validator';
|
||||
|
||||
function formatValidationError({ error }: { error: z.ZodError }) {
|
||||
const details = (error.errors ?? []).map((e) => {
|
||||
return {
|
||||
...(e.path.length === 0 ? {} : { path: e.path.join('.') }),
|
||||
message: e.message,
|
||||
};
|
||||
});
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
function buildLegacyValidator<Target extends keyof ValidationTargets>({ target, error }: { target: Target; error: { message: string; code: string } }) {
|
||||
return <Schema extends z.ZodTypeAny>(schema: Schema, { allowAdditionalFields = false }: { allowAdditionalFields?: boolean } = {}) => {
|
||||
return validator(target, (value, context) => {
|
||||
// @ts-expect-error try to enforce strict mode
|
||||
// eslint-disable-next-line ts/no-unsafe-assignment, ts/no-unsafe-call
|
||||
const refinedSchema: Schema = allowAdditionalFields ? schema : (schema.strict?.() ?? schema);
|
||||
|
||||
const result = refinedSchema.safeParse(value);
|
||||
|
||||
if (result.success) {
|
||||
// eslint-disable-next-line ts/no-unsafe-return
|
||||
return result.data as z.infer<Schema>;
|
||||
}
|
||||
|
||||
const details = formatValidationError({ error: result.error });
|
||||
|
||||
return context.json(
|
||||
{
|
||||
error: {
|
||||
...error,
|
||||
details,
|
||||
},
|
||||
},
|
||||
400,
|
||||
);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export const legacyValidateJsonBody = buildLegacyValidator({ target: 'json', error: { message: 'Invalid request body', code: 'server.invalid_request.body' } });
|
||||
export const legacyValidateQuery = buildLegacyValidator({ target: 'query', error: { message: 'Invalid query parameters', code: 'server.invalid_request.query' } });
|
||||
export const legacyValidateParams = buildLegacyValidator({ target: 'param', error: { message: 'Invalid URL parameters', code: 'server.invalid_request.params' } });
|
||||
export const legacyValidateFormData = buildLegacyValidator({ target: 'form', error: { message: 'Invalid form data', code: 'server.invalid_request.form_data' } });
|
||||
|
|
@ -116,6 +116,9 @@ importers:
|
|||
yaml:
|
||||
specifier: ^2.8.0
|
||||
version: 2.8.2
|
||||
zod:
|
||||
specifier: 3.25.76
|
||||
version: 3.25.76
|
||||
devDependencies:
|
||||
'@antfu/eslint-config':
|
||||
specifier: 'catalog:'
|
||||
|
|
@ -152,7 +155,7 @@ importers:
|
|||
dependencies:
|
||||
'@better-auth/expo':
|
||||
specifier: 'catalog:'
|
||||
version: 1.4.6(@better-auth/core@1.4.6(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.5(zod@3.25.76))(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1))(better-auth@1.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.10)(vue@3.5.13(typescript@5.9.3)))(expo-constants@18.0.10)(expo-linking@8.0.8)(expo-network@8.0.8(expo@54.0.23)(react@19.1.0))(expo-web-browser@15.0.9(expo@54.0.23)(react-native@0.81.5(@babel/core@7.28.4)(@react-native-community/cli@20.0.2(typescript@5.9.3))(@types/react@19.1.17)(react@19.1.0)))
|
||||
version: 1.4.6(@better-auth/core@1.4.6(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.5(zod@4.1.12))(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1))(better-auth@1.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.10)(vue@3.5.13(typescript@5.9.3)))(expo-constants@18.0.10)(expo-linking@8.0.8)(expo-network@8.0.8(expo@54.0.23)(react@19.1.0))(expo-web-browser@15.0.9(expo@54.0.23)(react-native@0.81.5(@babel/core@7.28.4)(@react-native-community/cli@20.0.2(typescript@5.9.3))(@types/react@19.1.17)(react@19.1.0)))
|
||||
'@corentinth/chisels':
|
||||
specifier: 'catalog:'
|
||||
version: 2.1.0
|
||||
|
|
@ -439,7 +442,7 @@ importers:
|
|||
version: 12.27.0
|
||||
'@better-auth/expo':
|
||||
specifier: 'catalog:'
|
||||
version: 1.4.6(@better-auth/core@1.4.6(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.5(zod@3.25.76))(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1))(better-auth@1.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.10)(vue@3.5.13(typescript@5.9.3)))(expo-constants@18.0.10)(expo-linking@8.0.8)(expo-network@8.0.8(expo@54.0.23)(react@19.1.0))(expo-web-browser@15.0.9(expo@54.0.23)(react-native@0.81.5(@babel/core@7.28.4)(@react-native-community/cli@20.0.2(typescript@5.9.3))(@types/react@19.1.17)(react@19.1.0)))
|
||||
version: 1.4.6(@better-auth/core@1.4.6(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.5(zod@4.1.12))(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1))(better-auth@1.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.10)(vue@3.5.13(typescript@5.9.3)))(expo-constants@18.0.10)(expo-linking@8.0.8)(expo-network@8.0.8(expo@54.0.23)(react@19.1.0))(expo-web-browser@15.0.9(expo@54.0.23)(react-native@0.81.5(@babel/core@7.28.4)(@react-native-community/cli@20.0.2(typescript@5.9.3))(@types/react@19.1.17)(react@19.1.0)))
|
||||
'@cadence-mq/core':
|
||||
specifier: ^0.2.1
|
||||
version: 0.2.1
|
||||
|
|
@ -560,9 +563,6 @@ importers:
|
|||
valibot:
|
||||
specifier: 'catalog:'
|
||||
version: 1.3.1(typescript@5.9.3)
|
||||
zod:
|
||||
specifier: ^3.25.67
|
||||
version: 3.25.76
|
||||
devDependencies:
|
||||
'@antfu/eslint-config':
|
||||
specifier: 'catalog:'
|
||||
|
|
@ -5511,6 +5511,7 @@ packages:
|
|||
'@xmldom/xmldom@0.8.11':
|
||||
resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
deprecated: this version has critical issues, please update to the latest version
|
||||
|
||||
abort-controller@3.0.0:
|
||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||
|
|
@ -13782,7 +13783,7 @@ snapshots:
|
|||
|
||||
'@bcoe/v8-coverage@1.0.2': {}
|
||||
|
||||
'@better-auth/core@1.4.6(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.5(zod@3.25.76))(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1)':
|
||||
'@better-auth/core@1.4.6(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.5(zod@4.1.12))(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1)':
|
||||
dependencies:
|
||||
'@better-auth/utils': 0.3.0
|
||||
'@better-fetch/fetch': 1.1.18
|
||||
|
|
@ -13793,20 +13794,9 @@ snapshots:
|
|||
nanostores: 1.0.1
|
||||
zod: 4.1.12
|
||||
|
||||
'@better-auth/core@1.4.6(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.5(zod@4.1.12))(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1)':
|
||||
'@better-auth/expo@1.4.6(@better-auth/core@1.4.6(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.5(zod@4.1.12))(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1))(better-auth@1.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.10)(vue@3.5.13(typescript@5.9.3)))(expo-constants@18.0.10)(expo-linking@8.0.8)(expo-network@8.0.8(expo@54.0.23)(react@19.1.0))(expo-web-browser@15.0.9(expo@54.0.23)(react-native@0.81.5(@babel/core@7.28.4)(@react-native-community/cli@20.0.2(typescript@5.9.3))(@types/react@19.1.17)(react@19.1.0)))':
|
||||
dependencies:
|
||||
'@better-auth/utils': 0.3.0
|
||||
'@better-fetch/fetch': 1.1.18
|
||||
'@standard-schema/spec': 1.0.0
|
||||
better-call: 1.1.5(zod@4.1.12)
|
||||
jose: 6.1.0
|
||||
kysely: 0.28.8
|
||||
nanostores: 1.0.1
|
||||
zod: 4.1.12
|
||||
|
||||
'@better-auth/expo@1.4.6(@better-auth/core@1.4.6(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.5(zod@3.25.76))(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1))(better-auth@1.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.10)(vue@3.5.13(typescript@5.9.3)))(expo-constants@18.0.10)(expo-linking@8.0.8)(expo-network@8.0.8(expo@54.0.23)(react@19.1.0))(expo-web-browser@15.0.9(expo@54.0.23)(react-native@0.81.5(@babel/core@7.28.4)(@react-native-community/cli@20.0.2(typescript@5.9.3))(@types/react@19.1.17)(react@19.1.0)))':
|
||||
dependencies:
|
||||
'@better-auth/core': 1.4.6(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.5(zod@3.25.76))(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1)
|
||||
'@better-auth/core': 1.4.6(@better-auth/utils@0.3.0)(@better-fetch/fetch@1.1.18)(better-call@1.1.5(zod@4.1.12))(jose@6.1.0)(kysely@0.28.8)(nanostores@1.0.1)
|
||||
'@better-fetch/fetch': 1.1.18
|
||||
better-auth: 1.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.10)(vue@3.5.13(typescript@5.9.3))
|
||||
better-call: 1.1.5(zod@4.1.12)
|
||||
|
|
@ -18538,7 +18528,7 @@ snapshots:
|
|||
'@better-fetch/fetch': 1.1.18
|
||||
'@noble/ciphers': 2.0.1
|
||||
'@noble/hashes': 2.0.1
|
||||
better-call: 1.1.5(zod@4.1.12)
|
||||
better-call: 1.1.5(zod@3.25.76)
|
||||
defu: 6.1.4
|
||||
jose: 6.1.0
|
||||
kysely: 0.28.8
|
||||
|
|
|
|||
Loading…
Reference in a new issue