@hookform/resolvers 4.1.2 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/ajv/package.json +1 -1
- package/arktype/dist/arktype.d.ts +8 -20
- package/arktype/dist/arktype.js +1 -1
- package/arktype/dist/arktype.js.map +1 -1
- package/arktype/dist/arktype.mjs +1 -1
- package/arktype/dist/arktype.modern.mjs +1 -1
- package/arktype/dist/arktype.modern.mjs.map +1 -1
- package/arktype/dist/arktype.module.js +1 -1
- package/arktype/dist/arktype.module.js.map +1 -1
- package/arktype/dist/arktype.umd.js +1 -1
- package/arktype/dist/arktype.umd.js.map +1 -1
- package/arktype/package.json +1 -1
- package/arktype/src/__tests__/Form.tsx +0 -28
- package/arktype/src/__tests__/arktype.ts +56 -0
- package/arktype/src/arktype.ts +58 -22
- package/class-validator/src/__tests__/Form.tsx +0 -26
- package/computed-types/dist/computed-types.d.ts +2 -3
- package/computed-types/dist/computed-types.js.map +1 -1
- package/computed-types/dist/computed-types.modern.mjs.map +1 -1
- package/computed-types/dist/computed-types.module.js.map +1 -1
- package/computed-types/dist/computed-types.umd.js.map +1 -1
- package/computed-types/package.json +1 -1
- package/computed-types/src/__tests__/Form.tsx +0 -28
- package/computed-types/src/__tests__/computed-types.ts +55 -1
- package/computed-types/src/computed-types.ts +7 -5
- package/dist/resolvers.js +1 -1
- package/dist/resolvers.js.map +1 -1
- package/dist/resolvers.mjs +1 -1
- package/dist/resolvers.mjs.map +1 -1
- package/dist/resolvers.module.js +1 -1
- package/dist/resolvers.module.js.map +1 -1
- package/dist/resolvers.umd.js +1 -1
- package/dist/resolvers.umd.js.map +1 -1
- package/effect-ts/dist/effect-ts.d.ts +8 -16
- package/effect-ts/dist/effect-ts.js.map +1 -1
- package/effect-ts/dist/effect-ts.modern.mjs.map +1 -1
- package/effect-ts/dist/effect-ts.module.js.map +1 -1
- package/effect-ts/dist/effect-ts.umd.js.map +1 -1
- package/effect-ts/package.json +1 -1
- package/effect-ts/src/__tests__/effect-ts.ts +61 -0
- package/effect-ts/src/effect-ts.ts +21 -3
- package/fluentvalidation-ts/package.json +1 -1
- package/io-ts/dist/io-ts.d.ts +8 -17
- package/io-ts/dist/io-ts.js.map +1 -1
- package/io-ts/dist/io-ts.modern.mjs.map +1 -1
- package/io-ts/dist/io-ts.module.js.map +1 -1
- package/io-ts/dist/io-ts.umd.js.map +1 -1
- package/io-ts/package.json +1 -1
- package/io-ts/src/__tests__/__fixtures__/data.ts +3 -3
- package/io-ts/src/__tests__/io-ts.ts +54 -0
- package/io-ts/src/io-ts.ts +35 -8
- package/joi/package.json +1 -1
- package/nope/package.json +1 -1
- package/package.json +13 -13
- package/standard-schema/dist/standard-schema.d.ts +6 -23
- package/standard-schema/dist/standard-schema.js +1 -1
- package/standard-schema/dist/standard-schema.js.map +1 -1
- package/standard-schema/dist/standard-schema.mjs +1 -1
- package/standard-schema/dist/standard-schema.modern.mjs +1 -1
- package/standard-schema/dist/standard-schema.modern.mjs.map +1 -1
- package/standard-schema/dist/standard-schema.module.js +1 -1
- package/standard-schema/dist/standard-schema.module.js.map +1 -1
- package/standard-schema/dist/standard-schema.umd.js +1 -1
- package/standard-schema/dist/standard-schema.umd.js.map +1 -1
- package/standard-schema/package.json +1 -1
- package/standard-schema/src/__tests__/Form.tsx +0 -28
- package/standard-schema/src/__tests__/__fixtures__/data.ts +2 -2
- package/standard-schema/src/__tests__/standard-schema.ts +78 -1
- package/standard-schema/src/standard-schema.ts +32 -5
- package/superstruct/dist/superstruct.d.ts +6 -20
- package/superstruct/dist/superstruct.js.map +1 -1
- package/superstruct/dist/superstruct.modern.mjs.map +1 -1
- package/superstruct/dist/superstruct.module.js.map +1 -1
- package/superstruct/dist/superstruct.umd.js.map +1 -1
- package/superstruct/package.json +1 -1
- package/superstruct/src/__tests__/superstruct.ts +60 -0
- package/superstruct/src/superstruct.ts +20 -4
- package/typanion/dist/typanion.d.ts +8 -16
- package/typanion/dist/typanion.js.map +1 -1
- package/typanion/dist/typanion.modern.mjs.map +1 -1
- package/typanion/dist/typanion.module.js.map +1 -1
- package/typanion/dist/typanion.umd.js.map +1 -1
- package/typanion/package.json +1 -1
- package/typanion/src/__tests__/Form.tsx +0 -32
- package/typanion/src/__tests__/typanion.ts +49 -0
- package/typanion/src/typanion.ts +22 -4
- package/typebox/dist/typebox.d.ts +3 -3
- package/typebox/dist/typebox.js.map +1 -1
- package/typebox/dist/typebox.modern.mjs.map +1 -1
- package/typebox/dist/typebox.module.js.map +1 -1
- package/typebox/dist/typebox.umd.js.map +1 -1
- package/typebox/package.json +1 -1
- package/typebox/src/__tests__/Form-native-validation-compiler.tsx +1 -1
- package/typebox/src/__tests__/Form.tsx +0 -28
- package/typebox/src/__tests__/__fixtures__/data.ts +3 -3
- package/typebox/src/__tests__/typebox.ts +98 -0
- package/typebox/src/typebox.ts +7 -14
- package/typeschema/dist/index.d.ts +0 -1
- package/typeschema/dist/typeschema.d.ts +8 -19
- package/typeschema/dist/typeschema.js +1 -1
- package/typeschema/dist/typeschema.js.map +1 -1
- package/typeschema/dist/typeschema.mjs +1 -1
- package/typeschema/dist/typeschema.modern.mjs +1 -1
- package/typeschema/dist/typeschema.modern.mjs.map +1 -1
- package/typeschema/dist/typeschema.module.js +1 -1
- package/typeschema/dist/typeschema.module.js.map +1 -1
- package/typeschema/dist/typeschema.umd.js +1 -1
- package/typeschema/dist/typeschema.umd.js.map +1 -1
- package/typeschema/package.json +1 -1
- package/typeschema/src/__tests__/Form.tsx +2 -4
- package/typeschema/src/__tests__/__fixtures__/data.ts +1 -1
- package/typeschema/src/__tests__/typeschema.ts +77 -0
- package/typeschema/src/index.ts +0 -1
- package/typeschema/src/typeschema.ts +52 -25
- package/valibot/dist/valibot.d.ts +9 -30
- package/valibot/dist/valibot.js.map +1 -1
- package/valibot/dist/valibot.modern.mjs.map +1 -1
- package/valibot/dist/valibot.module.js.map +1 -1
- package/valibot/dist/valibot.umd.js.map +1 -1
- package/valibot/package.json +1 -1
- package/valibot/src/__tests__/valibot.ts +69 -3
- package/valibot/src/valibot.ts +29 -13
- package/vest/package.json +1 -1
- package/vine/dist/vine.d.ts +10 -24
- package/vine/dist/vine.js.map +1 -1
- package/vine/dist/vine.modern.mjs.map +1 -1
- package/vine/dist/vine.module.js.map +1 -1
- package/vine/dist/vine.umd.js.map +1 -1
- package/vine/package.json +1 -1
- package/vine/src/__tests__/Form.tsx +0 -28
- package/vine/src/__tests__/__fixtures__/data.ts +4 -4
- package/vine/src/__tests__/vine.ts +69 -0
- package/vine/src/vine.ts +26 -4
- package/yup/dist/yup.d.ts +7 -21
- package/yup/dist/yup.js +1 -1
- package/yup/dist/yup.js.map +1 -1
- package/yup/dist/yup.mjs +1 -1
- package/yup/dist/yup.modern.mjs +1 -1
- package/yup/dist/yup.modern.mjs.map +1 -1
- package/yup/dist/yup.module.js +1 -1
- package/yup/dist/yup.module.js.map +1 -1
- package/yup/dist/yup.umd.js +1 -1
- package/yup/dist/yup.umd.js.map +1 -1
- package/yup/package.json +1 -1
- package/yup/src/__tests__/yup.ts +66 -0
- package/yup/src/yup.ts +30 -8
- package/zod/dist/zod.d.ts +7 -22
- package/zod/dist/zod.js.map +1 -1
- package/zod/dist/zod.modern.mjs.map +1 -1
- package/zod/dist/zod.module.js.map +1 -1
- package/zod/dist/zod.umd.js.map +1 -1
- package/zod/package.json +1 -1
- package/zod/src/__tests__/Form.tsx +0 -28
- package/zod/src/__tests__/__fixtures__/data.ts +2 -2
- package/zod/src/__tests__/zod.ts +69 -0
- package/zod/src/zod.ts +29 -9
- package/typeschema/dist/types.d.ts +0 -9
- package/typeschema/src/types.ts +0 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typanion.umd.js","sources":["../src/typanion.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport type {\n FieldError,\n FieldErrors,\n FieldValues,\n Resolver,\n} from 'react-hook-form';\nimport * as t from 'typanion';\n\nfunction parseErrors(errors: string[], parsedErrors: FieldErrors = {}) {\n return errors.reduce((acc, error) => {\n const fieldIndex = error.indexOf(':');\n\n const field = error.slice(1, fieldIndex);\n const message = error.slice(fieldIndex + 1).trim();\n\n acc[field] = {\n message,\n } as FieldError;\n\n return acc;\n }, parsedErrors);\n}\n\n/**\n * Creates a resolver for react-hook-form using Typanion schema validation\n * @param {t.StrictValidator<TFieldValues, TFieldValues>} schema - The Typanion schema to validate against\n * @param {Pick<t.ValidationState, 'coercions' | 'coercion'>} [schemaOptions] - Optional Typanion validation options\n * @returns {Resolver<t.InferType<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = t.isObject({\n * name: t.isString(),\n * age: t.isInteger()\n * });\n *\n * useForm({\n * resolver: typanionResolver(schema)\n * });\n */\nexport function typanionResolver<
|
|
1
|
+
{"version":3,"file":"typanion.umd.js","sources":["../src/typanion.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport type {\n FieldError,\n FieldErrors,\n FieldValues,\n Resolver,\n} from 'react-hook-form';\nimport * as t from 'typanion';\n\nfunction parseErrors(errors: string[], parsedErrors: FieldErrors = {}) {\n return errors.reduce((acc, error) => {\n const fieldIndex = error.indexOf(':');\n\n const field = error.slice(1, fieldIndex);\n const message = error.slice(fieldIndex + 1).trim();\n\n acc[field] = {\n message,\n } as FieldError;\n\n return acc;\n }, parsedErrors);\n}\n\nexport function typanionResolver<Input extends FieldValues, Context, Output>(\n schema: t.StrictValidator<Input, Input>,\n schemaOptions?: Pick<t.ValidationState, 'coercions' | 'coercion'>,\n resolverOptions?: {\n mode?: 'async' | 'sync';\n raw?: false;\n },\n): Resolver<Input, Context, t.InferType<typeof schema>>;\n\nexport function typanionResolver<Input extends FieldValues, Context, Output>(\n schema: t.StrictValidator<Input, Input>,\n schemaOptions: Pick<t.ValidationState, 'coercions' | 'coercion'> | undefined,\n resolverOptions: {\n mode?: 'async' | 'sync';\n raw: true;\n },\n): Resolver<Input, Context, Input>;\n\n/**\n * Creates a resolver for react-hook-form using Typanion schema validation\n * @param {t.StrictValidator<TFieldValues, TFieldValues>} schema - The Typanion schema to validate against\n * @param {Pick<t.ValidationState, 'coercions' | 'coercion'>} [schemaOptions] - Optional Typanion validation options\n * @returns {Resolver<t.InferType<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = t.isObject({\n * name: t.isString(),\n * age: t.isInteger()\n * });\n *\n * useForm({\n * resolver: typanionResolver(schema)\n * });\n */\nexport function typanionResolver<Input extends FieldValues, Context, Output>(\n schema: t.StrictValidator<Input, Input>,\n schemaOptions: Pick<t.ValidationState, 'coercions' | 'coercion'> = {},\n): Resolver<Input, Context, Output | Input> {\n return (values: Input, _, options) => {\n const rawErrors: string[] = [];\n const isValid = schema(\n values,\n Object.assign(\n {},\n {\n errors: rawErrors,\n },\n schemaOptions,\n ),\n );\n const parsedErrors = parseErrors(rawErrors);\n\n if (isValid) {\n options.shouldUseNativeValidation &&\n validateFieldsNatively(parsedErrors, options);\n\n return { values, errors: {} };\n }\n\n return { values: {}, errors: toNestErrors(parsedErrors, options) };\n };\n}\n"],"names":["schema","schemaOptions","values","_","options","rawErrors","isValid","Object","assign","errors","parsedErrors","reduce","acc","error","fieldIndex","indexOf","field","slice","message","trim","parseErrors","shouldUseNativeValidation","validateFieldsNatively","toNestErrors"],"mappings":"iVAyDgB,SACdA,EACAC,GAEA,YAFAA,IAAAA,IAAAA,EAAmE,CAAE,GAE7DC,SAAAA,EAAeC,EAAGC,GACxB,IAAMC,EAAsB,GACtBC,EAAUN,EACdE,EACAK,OAAOC,OACL,CAAE,EACF,CACEC,OAAQJ,GAEVJ,IAGES,EAhEV,SAAqBD,EAAkBC,GACrC,gBADqCA,IAAAA,EAA4B,IAC1DD,EAAOE,OAAO,SAACC,EAAKC,GACzB,IAAMC,EAAaD,EAAME,QAAQ,KAE3BC,EAAQH,EAAMI,MAAM,EAAGH,GACvBI,EAAUL,EAAMI,MAAMH,EAAa,GAAGK,OAM5C,OAJAP,EAAII,GAAS,CACXE,QAAAA,GAGKN,CACT,EAAGF,EACL,CAmDyBU,CAAYf,GAEjC,OAAIC,GACFF,EAAQiB,2BACNC,EAAAA,uBAAuBZ,EAAcN,GAEhC,CAAEF,OAAAA,EAAQO,OAAQ,CAAA,IAGpB,CAAEP,OAAQ,GAAIO,OAAQc,EAAYA,aAACb,EAAcN,GAC1D,CACF"}
|
package/typanion/package.json
CHANGED
|
@@ -10,12 +10,6 @@ const schema = t.isObject({
|
|
|
10
10
|
password: t.cascade(t.isString(), [t.hasMinLength(1)]),
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
interface FormData {
|
|
14
|
-
unusedProperty: string;
|
|
15
|
-
username: string;
|
|
16
|
-
password: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
13
|
function TestComponent({
|
|
20
14
|
onSubmit,
|
|
21
15
|
}: { onSubmit: (data: t.InferType<typeof schema>) => void }) {
|
|
@@ -55,29 +49,3 @@ test("form's validation with Typanion and TypeScript's integration", async () =>
|
|
|
55
49
|
).toHaveLength(2);
|
|
56
50
|
expect(handleSubmit).not.toHaveBeenCalled();
|
|
57
51
|
});
|
|
58
|
-
|
|
59
|
-
export function TestComponentManualType({
|
|
60
|
-
onSubmit,
|
|
61
|
-
}: {
|
|
62
|
-
onSubmit: (data: FormData) => void;
|
|
63
|
-
}) {
|
|
64
|
-
const {
|
|
65
|
-
register,
|
|
66
|
-
handleSubmit,
|
|
67
|
-
formState: { errors },
|
|
68
|
-
} = useForm<t.InferType<typeof schema>, undefined, FormData>({
|
|
69
|
-
resolver: typanionResolver(schema), // Useful to check TypeScript regressions
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
return (
|
|
73
|
-
<form onSubmit={handleSubmit(onSubmit)}>
|
|
74
|
-
<input {...register('username')} />
|
|
75
|
-
{errors.username && <span role="alert">{errors.username.message}</span>}
|
|
76
|
-
|
|
77
|
-
<input {...register('password')} />
|
|
78
|
-
{errors.password && <span role="alert">{errors.password.message}</span>}
|
|
79
|
-
|
|
80
|
-
<button type="submit">submit</button>
|
|
81
|
-
</form>
|
|
82
|
-
);
|
|
83
|
-
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { SubmitHandler } from 'react-hook-form';
|
|
2
|
+
import { useForm } from 'react-hook-form';
|
|
3
|
+
import { Resolver } from 'react-hook-form';
|
|
4
|
+
import * as t from 'typanion';
|
|
1
5
|
import { typanionResolver } from '..';
|
|
2
6
|
import { fields, invalidData, schema, validData } from './__fixtures__/data';
|
|
3
7
|
|
|
@@ -32,4 +36,49 @@ describe('typanionResolver', () => {
|
|
|
32
36
|
|
|
33
37
|
expect(result).toMatchSnapshot();
|
|
34
38
|
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Type inference tests
|
|
42
|
+
*/
|
|
43
|
+
it('should correctly infer the output type from a typanion schema', () => {
|
|
44
|
+
const resolver = typanionResolver(t.isObject({ id: t.isNumber() }));
|
|
45
|
+
|
|
46
|
+
expectTypeOf(resolver).toEqualTypeOf<
|
|
47
|
+
Resolver<ObjectType<{ id: number }>, unknown, ObjectType<{ id: number }>>
|
|
48
|
+
>();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should correctly infer the output type from a typanion schema for the handleSubmit function in useForm', () => {
|
|
52
|
+
const schema = t.isObject({ id: t.isNumber() });
|
|
53
|
+
|
|
54
|
+
const form = useForm({
|
|
55
|
+
resolver: typanionResolver(schema),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
59
|
+
|
|
60
|
+
expectTypeOf(form.handleSubmit)
|
|
61
|
+
.parameter(0)
|
|
62
|
+
.toEqualTypeOf<SubmitHandler<ObjectType<{ id: number }>>>();
|
|
63
|
+
});
|
|
35
64
|
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Copied from Typanion source code because it's not exported
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
declare type ExtractIndex<T> = {
|
|
71
|
+
// biome-ignore lint/complexity/noBannedTypes: for testing purposes
|
|
72
|
+
[K in keyof T as {} extends Record<K, 1> ? K : never]: T[K];
|
|
73
|
+
};
|
|
74
|
+
declare type RemoveIndex<T> = {
|
|
75
|
+
// biome-ignore lint/complexity/noBannedTypes: for testing purposes
|
|
76
|
+
[K in keyof T as {} extends Record<K, 1> ? never : K]: T[K];
|
|
77
|
+
};
|
|
78
|
+
declare type UndefinedProperties<T> = {
|
|
79
|
+
[P in keyof T]-?: undefined extends T[P] ? P : never;
|
|
80
|
+
}[keyof T];
|
|
81
|
+
declare type UndefinedToOptional<T> = Partial<Pick<T, UndefinedProperties<T>>> &
|
|
82
|
+
Pick<T, Exclude<keyof T, UndefinedProperties<T>>>;
|
|
83
|
+
declare type ObjectType<T> = UndefinedToOptional<RemoveIndex<T>> &
|
|
84
|
+
ExtractIndex<T>;
|
package/typanion/src/typanion.ts
CHANGED
|
@@ -22,6 +22,24 @@ function parseErrors(errors: string[], parsedErrors: FieldErrors = {}) {
|
|
|
22
22
|
}, parsedErrors);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export function typanionResolver<Input extends FieldValues, Context, Output>(
|
|
26
|
+
schema: t.StrictValidator<Input, Input>,
|
|
27
|
+
schemaOptions?: Pick<t.ValidationState, 'coercions' | 'coercion'>,
|
|
28
|
+
resolverOptions?: {
|
|
29
|
+
mode?: 'async' | 'sync';
|
|
30
|
+
raw?: false;
|
|
31
|
+
},
|
|
32
|
+
): Resolver<Input, Context, t.InferType<typeof schema>>;
|
|
33
|
+
|
|
34
|
+
export function typanionResolver<Input extends FieldValues, Context, Output>(
|
|
35
|
+
schema: t.StrictValidator<Input, Input>,
|
|
36
|
+
schemaOptions: Pick<t.ValidationState, 'coercions' | 'coercion'> | undefined,
|
|
37
|
+
resolverOptions: {
|
|
38
|
+
mode?: 'async' | 'sync';
|
|
39
|
+
raw: true;
|
|
40
|
+
},
|
|
41
|
+
): Resolver<Input, Context, Input>;
|
|
42
|
+
|
|
25
43
|
/**
|
|
26
44
|
* Creates a resolver for react-hook-form using Typanion schema validation
|
|
27
45
|
* @param {t.StrictValidator<TFieldValues, TFieldValues>} schema - The Typanion schema to validate against
|
|
@@ -37,11 +55,11 @@ function parseErrors(errors: string[], parsedErrors: FieldErrors = {}) {
|
|
|
37
55
|
* resolver: typanionResolver(schema)
|
|
38
56
|
* });
|
|
39
57
|
*/
|
|
40
|
-
export function typanionResolver<
|
|
41
|
-
schema: t.StrictValidator<
|
|
58
|
+
export function typanionResolver<Input extends FieldValues, Context, Output>(
|
|
59
|
+
schema: t.StrictValidator<Input, Input>,
|
|
42
60
|
schemaOptions: Pick<t.ValidationState, 'coercions' | 'coercion'> = {},
|
|
43
|
-
): Resolver<
|
|
44
|
-
return (values, _, options) => {
|
|
61
|
+
): Resolver<Input, Context, Output | Input> {
|
|
62
|
+
return (values: Input, _, options) => {
|
|
45
63
|
const rawErrors: string[] = [];
|
|
46
64
|
const isValid = schema(
|
|
47
65
|
values,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Static,
|
|
1
|
+
import { Static, StaticDecode, TObject } from '@sinclair/typebox';
|
|
2
2
|
import { TypeCheck } from '@sinclair/typebox/compiler';
|
|
3
|
-
import {
|
|
3
|
+
import { Resolver } from 'react-hook-form';
|
|
4
4
|
/**
|
|
5
5
|
* Creates a resolver for react-hook-form using Typebox schema validation
|
|
6
6
|
* @param {Schema | TypeCheck<Schema>} schema - The Typebox schema to validate against
|
|
@@ -17,4 +17,4 @@ import { FieldValues, Resolver } from 'react-hook-form';
|
|
|
17
17
|
* resolver: typeboxResolver(schema)
|
|
18
18
|
* });
|
|
19
19
|
*/
|
|
20
|
-
export declare function typeboxResolver<
|
|
20
|
+
export declare function typeboxResolver<Schema extends TObject, Context>(schema: Schema | TypeCheck<Schema>): Resolver<Static<Schema>, Context, StaticDecode<Schema>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typebox.js","sources":["../src/typebox.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Static,
|
|
1
|
+
{"version":3,"file":"typebox.js","sources":["../src/typebox.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Static, StaticDecode, TObject } from '@sinclair/typebox';\nimport { TypeCheck } from '@sinclair/typebox/compiler';\nimport { Value, type ValueError } from '@sinclair/typebox/value';\nimport { FieldError, Resolver, appendErrors } from 'react-hook-form';\n\nfunction parseErrorSchema(\n _errors: ValueError[],\n validateAllFieldCriteria: boolean,\n) {\n const errors: Record<string, FieldError> = {};\n for (; _errors.length; ) {\n const error = _errors[0];\n const { type, message, path } = error;\n const _path = path.substring(1).replace(/\\//g, '.');\n\n if (!errors[_path]) {\n errors[_path] = { message, type: '' + type };\n }\n\n if (validateAllFieldCriteria) {\n const types = errors[_path].types;\n const messages = types && types['' + type];\n\n errors[_path] = appendErrors(\n _path,\n validateAllFieldCriteria,\n errors,\n '' + type,\n messages\n ? ([] as string[]).concat(messages as string[], error.message)\n : error.message,\n ) as FieldError;\n }\n\n _errors.shift();\n }\n\n return errors;\n}\n\n/**\n * Creates a resolver for react-hook-form using Typebox schema validation\n * @param {Schema | TypeCheck<Schema>} schema - The Typebox schema to validate against\n * @param {Object} options - Additional resolver configuration\n * @param {string} [options.mode='async'] - Validation mode\n * @returns {Resolver<Static<Schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = Type.Object({\n * name: Type.String(),\n * age: Type.Number()\n * });\n *\n * useForm({\n * resolver: typeboxResolver(schema)\n * });\n */\nexport function typeboxResolver<Schema extends TObject, Context>(\n schema: Schema | TypeCheck<Schema>,\n): Resolver<Static<Schema>, Context, StaticDecode<Schema>> {\n return async (values: Static<Schema>, _, options) => {\n const errors = Array.from(\n schema instanceof TypeCheck\n ? schema.Errors(values)\n : Value.Errors(schema, values),\n );\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n if (!errors.length) {\n return {\n errors: {},\n values,\n };\n }\n\n return {\n values: {},\n errors: toNestErrors(\n parseErrorSchema(\n errors,\n !options.shouldUseNativeValidation && options.criteriaMode === 'all',\n ),\n options,\n ),\n };\n };\n}\n"],"names":["parseErrorSchema","_errors","validateAllFieldCriteria","errors","length","error","type","message","_path","path","substring","replace","types","messages","appendErrors","concat","shift","schema","values","_","options","Array","from","TypeCheck","Errors","Value","shouldUseNativeValidation","validateFieldsNatively","Promise","resolve","toNestErrors","criteriaMode","e","reject"],"mappings":"+IAMA,SAASA,EACPC,EACAC,GAGA,IADA,IAAMC,EAAqC,CAAE,EACtCF,EAAQG,QAAU,CACvB,IAAMC,EAAQJ,EAAQ,GACdK,EAAwBD,EAAxBC,KAAMC,EAAkBF,EAAlBE,QACRC,EAD0BH,EAATI,KACJC,UAAU,GAAGC,QAAQ,MAAO,KAM/C,GAJKR,EAAOK,KACVL,EAAOK,GAAS,CAAED,QAAAA,EAASD,KAAM,GAAKA,IAGpCJ,EAA0B,CAC5B,IAAMU,EAAQT,EAAOK,GAAOI,MACtBC,EAAWD,GAASA,EAAM,GAAKN,GAErCH,EAAOK,GAASM,EAAYA,aAC1BN,EACAN,EACAC,EACA,GAAKG,EACLO,EACK,GAAgBE,OAAOF,EAAsBR,EAAME,SACpDF,EAAME,QAEd,CAEAN,EAAQe,OACV,CAEA,OAAOb,CACT,yBAkBgB,SACdc,GAEA,OAAcC,SAAAA,EAAwBC,EAAGC,GAAO,IAC9C,IAAMjB,EAASkB,MAAMC,KACnBL,aAAkBM,YACdN,EAAOO,OAAON,GACdO,QAAMD,OAAOP,EAAQC,IAK3B,OAFAE,EAAQM,2BAA6BC,EAAsBA,uBAAC,CAAE,EAAEP,GAShEQ,QAAAC,QAPK1B,EAAOC,OAOL,CACLc,OAAQ,CAAE,EACVf,OAAQ2B,EAAAA,aACN9B,EACEG,GACCiB,EAAQM,2BAAsD,QAAzBN,EAAQW,cAEhDX,IAbK,CACLjB,OAAQ,GACRe,OAAAA,GAcN,CAAC,MAAAc,UAAAJ,QAAAK,OAAAD,EACH,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typebox.modern.mjs","sources":["../src/typebox.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Static,
|
|
1
|
+
{"version":3,"file":"typebox.modern.mjs","sources":["../src/typebox.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Static, StaticDecode, TObject } from '@sinclair/typebox';\nimport { TypeCheck } from '@sinclair/typebox/compiler';\nimport { Value, type ValueError } from '@sinclair/typebox/value';\nimport { FieldError, Resolver, appendErrors } from 'react-hook-form';\n\nfunction parseErrorSchema(\n _errors: ValueError[],\n validateAllFieldCriteria: boolean,\n) {\n const errors: Record<string, FieldError> = {};\n for (; _errors.length; ) {\n const error = _errors[0];\n const { type, message, path } = error;\n const _path = path.substring(1).replace(/\\//g, '.');\n\n if (!errors[_path]) {\n errors[_path] = { message, type: '' + type };\n }\n\n if (validateAllFieldCriteria) {\n const types = errors[_path].types;\n const messages = types && types['' + type];\n\n errors[_path] = appendErrors(\n _path,\n validateAllFieldCriteria,\n errors,\n '' + type,\n messages\n ? ([] as string[]).concat(messages as string[], error.message)\n : error.message,\n ) as FieldError;\n }\n\n _errors.shift();\n }\n\n return errors;\n}\n\n/**\n * Creates a resolver for react-hook-form using Typebox schema validation\n * @param {Schema | TypeCheck<Schema>} schema - The Typebox schema to validate against\n * @param {Object} options - Additional resolver configuration\n * @param {string} [options.mode='async'] - Validation mode\n * @returns {Resolver<Static<Schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = Type.Object({\n * name: Type.String(),\n * age: Type.Number()\n * });\n *\n * useForm({\n * resolver: typeboxResolver(schema)\n * });\n */\nexport function typeboxResolver<Schema extends TObject, Context>(\n schema: Schema | TypeCheck<Schema>,\n): Resolver<Static<Schema>, Context, StaticDecode<Schema>> {\n return async (values: Static<Schema>, _, options) => {\n const errors = Array.from(\n schema instanceof TypeCheck\n ? schema.Errors(values)\n : Value.Errors(schema, values),\n );\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n if (!errors.length) {\n return {\n errors: {},\n values,\n };\n }\n\n return {\n values: {},\n errors: toNestErrors(\n parseErrorSchema(\n errors,\n !options.shouldUseNativeValidation && options.criteriaMode === 'all',\n ),\n options,\n ),\n };\n };\n}\n"],"names":["parseErrorSchema","_errors","validateAllFieldCriteria","errors","length","error","type","message","path","_path","substring","replace","types","messages","appendErrors","concat","shift","typeboxResolver","schema","values","_","options","Array","from","TypeCheck","Errors","Value","shouldUseNativeValidation","validateFieldsNatively","toNestErrors","criteriaMode"],"mappings":"qOAMA,SAASA,EACPC,EACAC,GAEA,MAAMC,EAAqC,CAAA,EAC3C,KAAOF,EAAQG,QAAU,CACvB,MAAMC,EAAQJ,EAAQ,IAChBK,KAAEA,EAAIC,QAAEA,EAAOC,KAAEA,GAASH,EAC1BI,EAAQD,EAAKE,UAAU,GAAGC,QAAQ,MAAO,KAM/C,GAJKR,EAAOM,KACVN,EAAOM,GAAS,CAAEF,UAASD,KAAM,GAAKA,IAGpCJ,EAA0B,CAC5B,MAAMU,EAAQT,EAAOM,GAAOG,MACtBC,EAAWD,GAASA,EAAM,GAAKN,GAErCH,EAAOM,GAASK,EACdL,EACAP,EACAC,EACA,GAAKG,EACLO,EACK,GAAgBE,OAAOF,EAAsBR,EAAME,SACpDF,EAAME,QAEd,CAEAN,EAAQe,OACV,CAEA,OAAOb,CACT,CAkBM,SAAUc,EACdC,GAEA,OAAcC,MAAAA,EAAwBC,EAAGC,KACvC,MAAMlB,EAASmB,MAAMC,KACnBL,aAAkBM,EACdN,EAAOO,OAAON,GACdO,EAAMD,OAAOP,EAAQC,IAK3B,OAFAE,EAAQM,2BAA6BC,EAAuB,CAAE,EAAEP,GAE3DlB,EAAOC,OAOL,CACLe,OAAQ,CAAA,EACRhB,OAAQ0B,EACN7B,EACEG,GACCkB,EAAQM,2BAAsD,QAAzBN,EAAQS,cAEhDT,IAbK,CACLlB,OAAQ,CAAE,EACVgB,UAeR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typebox.module.js","sources":["../src/typebox.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Static,
|
|
1
|
+
{"version":3,"file":"typebox.module.js","sources":["../src/typebox.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Static, StaticDecode, TObject } from '@sinclair/typebox';\nimport { TypeCheck } from '@sinclair/typebox/compiler';\nimport { Value, type ValueError } from '@sinclair/typebox/value';\nimport { FieldError, Resolver, appendErrors } from 'react-hook-form';\n\nfunction parseErrorSchema(\n _errors: ValueError[],\n validateAllFieldCriteria: boolean,\n) {\n const errors: Record<string, FieldError> = {};\n for (; _errors.length; ) {\n const error = _errors[0];\n const { type, message, path } = error;\n const _path = path.substring(1).replace(/\\//g, '.');\n\n if (!errors[_path]) {\n errors[_path] = { message, type: '' + type };\n }\n\n if (validateAllFieldCriteria) {\n const types = errors[_path].types;\n const messages = types && types['' + type];\n\n errors[_path] = appendErrors(\n _path,\n validateAllFieldCriteria,\n errors,\n '' + type,\n messages\n ? ([] as string[]).concat(messages as string[], error.message)\n : error.message,\n ) as FieldError;\n }\n\n _errors.shift();\n }\n\n return errors;\n}\n\n/**\n * Creates a resolver for react-hook-form using Typebox schema validation\n * @param {Schema | TypeCheck<Schema>} schema - The Typebox schema to validate against\n * @param {Object} options - Additional resolver configuration\n * @param {string} [options.mode='async'] - Validation mode\n * @returns {Resolver<Static<Schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = Type.Object({\n * name: Type.String(),\n * age: Type.Number()\n * });\n *\n * useForm({\n * resolver: typeboxResolver(schema)\n * });\n */\nexport function typeboxResolver<Schema extends TObject, Context>(\n schema: Schema | TypeCheck<Schema>,\n): Resolver<Static<Schema>, Context, StaticDecode<Schema>> {\n return async (values: Static<Schema>, _, options) => {\n const errors = Array.from(\n schema instanceof TypeCheck\n ? schema.Errors(values)\n : Value.Errors(schema, values),\n );\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n if (!errors.length) {\n return {\n errors: {},\n values,\n };\n }\n\n return {\n values: {},\n errors: toNestErrors(\n parseErrorSchema(\n errors,\n !options.shouldUseNativeValidation && options.criteriaMode === 'all',\n ),\n options,\n ),\n };\n };\n}\n"],"names":["parseErrorSchema","_errors","validateAllFieldCriteria","errors","length","error","type","message","_path","path","substring","replace","types","messages","appendErrors","concat","shift","typeboxResolver","schema","values","_","options","Array","from","TypeCheck","Errors","Value","shouldUseNativeValidation","validateFieldsNatively","Promise","resolve","toNestErrors","criteriaMode","e","reject"],"mappings":"qOAMA,SAASA,EACPC,EACAC,GAGA,IADA,IAAMC,EAAqC,CAAE,EACtCF,EAAQG,QAAU,CACvB,IAAMC,EAAQJ,EAAQ,GACdK,EAAwBD,EAAxBC,KAAMC,EAAkBF,EAAlBE,QACRC,EAD0BH,EAATI,KACJC,UAAU,GAAGC,QAAQ,MAAO,KAM/C,GAJKR,EAAOK,KACVL,EAAOK,GAAS,CAAED,QAAAA,EAASD,KAAM,GAAKA,IAGpCJ,EAA0B,CAC5B,IAAMU,EAAQT,EAAOK,GAAOI,MACtBC,EAAWD,GAASA,EAAM,GAAKN,GAErCH,EAAOK,GAASM,EACdN,EACAN,EACAC,EACA,GAAKG,EACLO,EACK,GAAgBE,OAAOF,EAAsBR,EAAME,SACpDF,EAAME,QAEd,CAEAN,EAAQe,OACV,CAEA,OAAOb,CACT,CAkBgB,SAAAc,EACdC,GAEA,OAAcC,SAAAA,EAAwBC,EAAGC,GAAO,IAC9C,IAAMlB,EAASmB,MAAMC,KACnBL,aAAkBM,EACdN,EAAOO,OAAON,GACdO,EAAMD,OAAOP,EAAQC,IAK3B,OAFAE,EAAQM,2BAA6BC,EAAuB,CAAE,EAAEP,GAShEQ,QAAAC,QAPK3B,EAAOC,OAOL,CACLe,OAAQ,CAAE,EACVhB,OAAQ4B,EACN/B,EACEG,GACCkB,EAAQM,2BAAsD,QAAzBN,EAAQW,cAEhDX,IAbK,CACLlB,OAAQ,GACRgB,OAAAA,GAcN,CAAC,MAAAc,UAAAJ,QAAAK,OAAAD,EACH,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typebox.umd.js","sources":["../src/typebox.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Static,
|
|
1
|
+
{"version":3,"file":"typebox.umd.js","sources":["../src/typebox.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Static, StaticDecode, TObject } from '@sinclair/typebox';\nimport { TypeCheck } from '@sinclair/typebox/compiler';\nimport { Value, type ValueError } from '@sinclair/typebox/value';\nimport { FieldError, Resolver, appendErrors } from 'react-hook-form';\n\nfunction parseErrorSchema(\n _errors: ValueError[],\n validateAllFieldCriteria: boolean,\n) {\n const errors: Record<string, FieldError> = {};\n for (; _errors.length; ) {\n const error = _errors[0];\n const { type, message, path } = error;\n const _path = path.substring(1).replace(/\\//g, '.');\n\n if (!errors[_path]) {\n errors[_path] = { message, type: '' + type };\n }\n\n if (validateAllFieldCriteria) {\n const types = errors[_path].types;\n const messages = types && types['' + type];\n\n errors[_path] = appendErrors(\n _path,\n validateAllFieldCriteria,\n errors,\n '' + type,\n messages\n ? ([] as string[]).concat(messages as string[], error.message)\n : error.message,\n ) as FieldError;\n }\n\n _errors.shift();\n }\n\n return errors;\n}\n\n/**\n * Creates a resolver for react-hook-form using Typebox schema validation\n * @param {Schema | TypeCheck<Schema>} schema - The Typebox schema to validate against\n * @param {Object} options - Additional resolver configuration\n * @param {string} [options.mode='async'] - Validation mode\n * @returns {Resolver<Static<Schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = Type.Object({\n * name: Type.String(),\n * age: Type.Number()\n * });\n *\n * useForm({\n * resolver: typeboxResolver(schema)\n * });\n */\nexport function typeboxResolver<Schema extends TObject, Context>(\n schema: Schema | TypeCheck<Schema>,\n): Resolver<Static<Schema>, Context, StaticDecode<Schema>> {\n return async (values: Static<Schema>, _, options) => {\n const errors = Array.from(\n schema instanceof TypeCheck\n ? schema.Errors(values)\n : Value.Errors(schema, values),\n );\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n if (!errors.length) {\n return {\n errors: {},\n values,\n };\n }\n\n return {\n values: {},\n errors: toNestErrors(\n parseErrorSchema(\n errors,\n !options.shouldUseNativeValidation && options.criteriaMode === 'all',\n ),\n options,\n ),\n };\n };\n}\n"],"names":["parseErrorSchema","_errors","validateAllFieldCriteria","errors","length","error","type","message","_path","path","substring","replace","types","messages","appendErrors","concat","shift","schema","values","_","options","Array","from","TypeCheck","Errors","Value","shouldUseNativeValidation","validateFieldsNatively","Promise","resolve","toNestErrors","criteriaMode","e","reject"],"mappings":"mhBAMA,SAASA,EACPC,EACAC,GAGA,IADA,IAAMC,EAAqC,CAAE,EACtCF,EAAQG,QAAU,CACvB,IAAMC,EAAQJ,EAAQ,GACdK,EAAwBD,EAAxBC,KAAMC,EAAkBF,EAAlBE,QACRC,EAD0BH,EAATI,KACJC,UAAU,GAAGC,QAAQ,MAAO,KAM/C,GAJKR,EAAOK,KACVL,EAAOK,GAAS,CAAED,QAAAA,EAASD,KAAM,GAAKA,IAGpCJ,EAA0B,CAC5B,IAAMU,EAAQT,EAAOK,GAAOI,MACtBC,EAAWD,GAASA,EAAM,GAAKN,GAErCH,EAAOK,GAASM,EAAYA,aAC1BN,EACAN,EACAC,EACA,GAAKG,EACLO,EACK,GAAgBE,OAAOF,EAAsBR,EAAME,SACpDF,EAAME,QAEd,CAEAN,EAAQe,OACV,CAEA,OAAOb,CACT,mBAkBgB,SACdc,GAEA,OAAcC,SAAAA,EAAwBC,EAAGC,GAAO,IAC9C,IAAMjB,EAASkB,MAAMC,KACnBL,aAAkBM,YACdN,EAAOO,OAAON,GACdO,QAAMD,OAAOP,EAAQC,IAK3B,OAFAE,EAAQM,2BAA6BC,EAAsBA,uBAAC,CAAE,EAAEP,GAShEQ,QAAAC,QAPK1B,EAAOC,OAOL,CACLc,OAAQ,CAAE,EACVf,OAAQ2B,EAAAA,aACN9B,EACEG,GACCiB,EAAQM,2BAAsD,QAAzBN,EAAQW,cAEhDX,IAbK,CACLjB,OAAQ,GACRe,OAAAA,GAcN,CAAC,MAAAc,UAAAJ,QAAAK,OAAAD,EACH,CAAA,CAAA"}
|
package/typebox/package.json
CHANGED
|
@@ -21,7 +21,7 @@ interface Props {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function TestComponent({ onSubmit }: Props) {
|
|
24
|
-
const { register, handleSubmit } = useForm
|
|
24
|
+
const { register, handleSubmit } = useForm({
|
|
25
25
|
resolver: typeboxResolver(typecheck),
|
|
26
26
|
shouldUseNativeValidation: true,
|
|
27
27
|
});
|
|
@@ -10,8 +10,6 @@ const schema = Type.Object({
|
|
|
10
10
|
password: Type.String({ minLength: 1 }),
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
-
type FormData = Static<typeof schema> & { unusedProperty: string };
|
|
14
|
-
|
|
15
13
|
function TestComponent({
|
|
16
14
|
onSubmit,
|
|
17
15
|
}: {
|
|
@@ -52,29 +50,3 @@ test("form's validation with Typebox and TypeScript's integration", async () =>
|
|
|
52
50
|
|
|
53
51
|
expect(handleSubmit).not.toHaveBeenCalled();
|
|
54
52
|
});
|
|
55
|
-
|
|
56
|
-
export function TestComponentManualType({
|
|
57
|
-
onSubmit,
|
|
58
|
-
}: {
|
|
59
|
-
onSubmit: (data: FormData) => void;
|
|
60
|
-
}) {
|
|
61
|
-
const {
|
|
62
|
-
register,
|
|
63
|
-
handleSubmit,
|
|
64
|
-
formState: { errors },
|
|
65
|
-
} = useForm<Static<typeof schema>, undefined, FormData>({
|
|
66
|
-
resolver: typeboxResolver(schema), // Useful to check TypeScript regressions
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
<form onSubmit={handleSubmit(onSubmit)}>
|
|
71
|
-
<input {...register('username')} />
|
|
72
|
-
{errors.username && <span role="alert">{errors.username.message}</span>}
|
|
73
|
-
|
|
74
|
-
<input {...register('password')} />
|
|
75
|
-
{errors.password && <span role="alert">{errors.password.message}</span>}
|
|
76
|
-
|
|
77
|
-
<button type="submit">submit</button>
|
|
78
|
-
</form>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StaticDecode, Type } from '@sinclair/typebox';
|
|
2
2
|
import { Field, InternalFieldName } from 'react-hook-form';
|
|
3
3
|
|
|
4
4
|
export const schema = Type.Object({
|
|
@@ -37,7 +37,7 @@ export const schema = Type.Object({
|
|
|
37
37
|
dateStr: Type.Date(),
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
export const validData:
|
|
40
|
+
export const validData: StaticDecode<typeof schema> = {
|
|
41
41
|
username: 'Doe',
|
|
42
42
|
password: 'Password123_',
|
|
43
43
|
repeatPassword: 'Password123_',
|
|
@@ -61,7 +61,7 @@ export const invalidData = {
|
|
|
61
61
|
birthYear: 'birthYear',
|
|
62
62
|
like: [{ id: 'z' }],
|
|
63
63
|
url: 'abc',
|
|
64
|
-
} as
|
|
64
|
+
} as unknown as StaticDecode<typeof schema>;
|
|
65
65
|
|
|
66
66
|
export const fields: Record<InternalFieldName, Field['_f']> = {
|
|
67
67
|
username: {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { Resolver, SubmitHandler, useForm } from 'react-hook-form';
|
|
1
2
|
import { typeboxResolver } from '..';
|
|
2
3
|
import { fields, invalidData, schema, validData } from './__fixtures__/data';
|
|
4
|
+
import { Type } from '@sinclair/typebox';
|
|
5
|
+
import { TypeCompiler } from '@sinclair/typebox/compiler';
|
|
3
6
|
|
|
4
7
|
const shouldUseNativeValidation = false;
|
|
5
8
|
|
|
@@ -31,4 +34,99 @@ describe('typeboxResolver', () => {
|
|
|
31
34
|
|
|
32
35
|
expect(result).toEqual({ errors: {}, values: validData });
|
|
33
36
|
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Type inference tests
|
|
40
|
+
*/
|
|
41
|
+
it('should correctly infer the output type from a typebox schema', () => {
|
|
42
|
+
const resolver = typeboxResolver(Type.Object({ id: Type.Number() }));
|
|
43
|
+
|
|
44
|
+
expectTypeOf(resolver).toEqualTypeOf<
|
|
45
|
+
Resolver<{ id: number }, unknown, { id: number }>
|
|
46
|
+
>();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should correctly infer the output type from a typebox schema with TypeCompiler', () => {
|
|
50
|
+
const typecheck = TypeCompiler.Compile(Type.Object({ id: Type.Number() }));
|
|
51
|
+
const resolver = typeboxResolver(typecheck);
|
|
52
|
+
|
|
53
|
+
expectTypeOf(resolver).toEqualTypeOf<
|
|
54
|
+
Resolver<{ id: number }, unknown, { id: number }>
|
|
55
|
+
>();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should correctly infer the output type from a typebox schema using a transform', () => {
|
|
59
|
+
const resolver = typeboxResolver(
|
|
60
|
+
Type.Object({
|
|
61
|
+
id: Type.Transform(Type.Number())
|
|
62
|
+
.Decode((v) => String(v))
|
|
63
|
+
.Encode((v) => Number.parseInt(v)),
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expectTypeOf(resolver).toEqualTypeOf<
|
|
68
|
+
Resolver<{ id: number }, unknown, { id: string }>
|
|
69
|
+
>();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should correctly infer the output type from a typebox schema for the handleSubmit function in useForm', () => {
|
|
73
|
+
const schema = Type.Object({ id: Type.Number() });
|
|
74
|
+
|
|
75
|
+
const form = useForm({
|
|
76
|
+
resolver: typeboxResolver(schema),
|
|
77
|
+
defaultValues: {
|
|
78
|
+
id: 3,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
83
|
+
|
|
84
|
+
expectTypeOf(form.handleSubmit).parameter(0).toEqualTypeOf<
|
|
85
|
+
SubmitHandler<{
|
|
86
|
+
id: number;
|
|
87
|
+
}>
|
|
88
|
+
>();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should correctly infer the output type from a typebox schema with TypeCompiler for the handleSubmit function in useForm', () => {
|
|
92
|
+
const typecheck = TypeCompiler.Compile(Type.Object({ id: Type.Number() }));
|
|
93
|
+
|
|
94
|
+
const form = useForm({
|
|
95
|
+
resolver: typeboxResolver(typecheck),
|
|
96
|
+
defaultValues: {
|
|
97
|
+
id: 3,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
102
|
+
|
|
103
|
+
expectTypeOf(form.handleSubmit).parameter(0).toEqualTypeOf<
|
|
104
|
+
SubmitHandler<{
|
|
105
|
+
id: number;
|
|
106
|
+
}>
|
|
107
|
+
>();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should correctly infer the output type from a typebox schema with a transform for the handleSubmit function in useForm', () => {
|
|
111
|
+
const schema = Type.Object({
|
|
112
|
+
id: Type.Transform(Type.Number())
|
|
113
|
+
.Decode((v) => String(v))
|
|
114
|
+
.Encode((v) => Number.parseInt(v)),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const form = useForm({
|
|
118
|
+
resolver: typeboxResolver(schema),
|
|
119
|
+
defaultValues: {
|
|
120
|
+
id: 3,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
125
|
+
|
|
126
|
+
expectTypeOf(form.handleSubmit).parameter(0).toEqualTypeOf<
|
|
127
|
+
SubmitHandler<{
|
|
128
|
+
id: string;
|
|
129
|
+
}>
|
|
130
|
+
>();
|
|
131
|
+
});
|
|
34
132
|
});
|
package/typebox/src/typebox.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';
|
|
2
|
-
import { Static,
|
|
2
|
+
import { Static, StaticDecode, TObject } from '@sinclair/typebox';
|
|
3
3
|
import { TypeCheck } from '@sinclair/typebox/compiler';
|
|
4
4
|
import { Value, type ValueError } from '@sinclair/typebox/value';
|
|
5
|
-
import {
|
|
6
|
-
FieldError,
|
|
7
|
-
FieldErrors,
|
|
8
|
-
FieldValues,
|
|
9
|
-
Resolver,
|
|
10
|
-
appendErrors,
|
|
11
|
-
} from 'react-hook-form';
|
|
5
|
+
import { FieldError, Resolver, appendErrors } from 'react-hook-form';
|
|
12
6
|
|
|
13
7
|
function parseErrorSchema(
|
|
14
8
|
_errors: ValueError[],
|
|
@@ -61,11 +55,10 @@ function parseErrorSchema(
|
|
|
61
55
|
* resolver: typeboxResolver(schema)
|
|
62
56
|
* });
|
|
63
57
|
*/
|
|
64
|
-
export function typeboxResolver<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return async (values, _, options) => {
|
|
58
|
+
export function typeboxResolver<Schema extends TObject, Context>(
|
|
59
|
+
schema: Schema | TypeCheck<Schema>,
|
|
60
|
+
): Resolver<Static<Schema>, Context, StaticDecode<Schema>> {
|
|
61
|
+
return async (values: Static<Schema>, _, options) => {
|
|
69
62
|
const errors = Array.from(
|
|
70
63
|
schema instanceof TypeCheck
|
|
71
64
|
? schema.Errors(values)
|
|
@@ -76,7 +69,7 @@ export function typeboxResolver<
|
|
|
76
69
|
|
|
77
70
|
if (!errors.length) {
|
|
78
71
|
return {
|
|
79
|
-
errors: {}
|
|
72
|
+
errors: {},
|
|
80
73
|
values,
|
|
81
74
|
};
|
|
82
75
|
}
|
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* @example
|
|
10
|
-
* const schema = z.object({
|
|
11
|
-
* name: z.string().required(),
|
|
12
|
-
* age: z.number().required(),
|
|
13
|
-
* });
|
|
14
|
-
*
|
|
15
|
-
* useForm({
|
|
16
|
-
* resolver: typeschemaResolver(schema)
|
|
17
|
-
* });
|
|
18
|
-
*/
|
|
19
|
-
export declare const typeschemaResolver: Resolver;
|
|
1
|
+
import { FieldValues, Resolver } from 'react-hook-form';
|
|
2
|
+
import { StandardSchemaV1 } from 'zod/lib/standard-schema';
|
|
3
|
+
export declare function typeschemaResolver<Input extends FieldValues, Context, Output>(schema: StandardSchemaV1<Input, Output>, _schemaOptions?: never, resolverOptions?: {
|
|
4
|
+
raw?: false;
|
|
5
|
+
}): Resolver<Input, Context, Output>;
|
|
6
|
+
export declare function typeschemaResolver<Input extends FieldValues, Context, Output>(schema: StandardSchemaV1<Input, Output>, _schemaOptions: never | undefined, resolverOptions: {
|
|
7
|
+
raw: true;
|
|
8
|
+
}): Resolver<Input, Context, Input>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=require("@hookform/resolvers"),r=require("
|
|
1
|
+
var e=require("@hookform/resolvers"),r=require("react-hook-form");exports.typeschemaResolver=function(s,t,a){return void 0===a&&(a={}),function(t,i,o){try{var n=function(){if(u.issues){var s=function(e,s){for(var t=Object.assign([],e),a={};t.length;){var i=e[0];if(i.path){var o=i.path.join(".");if(a[o]||(a[o]={message:i.message,type:""}),s){var n=a[o].types,u=n&&n[""];a[o]=r.appendErrors(o,s,a,"",u?[].concat(u,i.message):i.message)}t.shift()}}return a}(u.issues,!o.shouldUseNativeValidation&&"all"===o.criteriaMode);return{values:{},errors:e.toNestErrors(s,o)}}return o.shouldUseNativeValidation&&e.validateFieldsNatively({},o),{values:a.raw?Object.assign({},t):u.value,errors:{}}},u=s["~standard"].validate(t),v=function(){if(u instanceof Promise)return Promise.resolve(u).then(function(e){u=e})}();return Promise.resolve(v&&v.then?v.then(n):n())}catch(e){return Promise.reject(e)}}};
|
|
2
2
|
//# sourceMappingURL=typeschema.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeschema.js","sources":["../src/typeschema.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport
|
|
1
|
+
{"version":3,"file":"typeschema.js","sources":["../src/typeschema.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport {\n FieldError,\n FieldErrors,\n FieldValues,\n Resolver,\n appendErrors,\n} from 'react-hook-form';\nimport { StandardSchemaV1 } from 'zod/lib/standard-schema';\n\nconst parseErrorSchema = (\n typeschemaErrors: readonly StandardSchemaV1.Issue[],\n validateAllFieldCriteria: boolean,\n): FieldErrors => {\n const schemaErrors = Object.assign([], typeschemaErrors);\n const errors: Record<string, FieldError> = {};\n\n for (; schemaErrors.length; ) {\n const error = typeschemaErrors[0];\n\n if (!error.path) {\n continue;\n }\n const _path = error.path.join('.');\n\n if (!errors[_path]) {\n errors[_path] = { message: error.message, type: '' };\n }\n\n if (validateAllFieldCriteria) {\n const types = errors[_path].types;\n const messages = types && types[''];\n\n errors[_path] = appendErrors(\n _path,\n validateAllFieldCriteria,\n errors,\n '',\n messages\n ? ([] as string[]).concat(messages as string[], error.message)\n : error.message,\n ) as FieldError;\n }\n\n schemaErrors.shift();\n }\n\n return errors;\n};\n\nexport function typeschemaResolver<Input extends FieldValues, Context, Output>(\n schema: StandardSchemaV1<Input, Output>,\n _schemaOptions?: never,\n resolverOptions?: {\n raw?: false;\n },\n): Resolver<Input, Context, Output>;\n\nexport function typeschemaResolver<Input extends FieldValues, Context, Output>(\n schema: StandardSchemaV1<Input, Output>,\n _schemaOptions: never | undefined,\n resolverOptions: {\n raw: true;\n },\n): Resolver<Input, Context, Input>;\n\n/**\n * Creates a resolver for react-hook-form using TypeSchema validation\n * @param {any} schema - The TypeSchema to validate against\n * @param {any} _ - Unused parameter\n * @param {Object} resolverOptions - Additional resolver configuration\n * @param {string} [resolverOptions.mode='async'] - Validation mode\n * @returns {Resolver} A resolver function compatible with react-hook-form\n * @example\n * const schema = z.object({\n * name: z.string().required(),\n * age: z.number().required(),\n * });\n *\n * useForm({\n * resolver: typeschemaResolver(schema)\n * });\n */\nexport function typeschemaResolver<Input extends FieldValues, Context, Output>(\n schema: StandardSchemaV1<Input, Output>,\n _schemaOptions?: never,\n resolverOptions: {\n raw?: boolean;\n } = {},\n): Resolver<Input, Context, Output | Input> {\n return async (values, _, options) => {\n let result = schema['~standard'].validate(values);\n if (result instanceof Promise) {\n result = await result;\n }\n\n if (result.issues) {\n const errors = parseErrorSchema(\n result.issues,\n !options.shouldUseNativeValidation && options.criteriaMode === 'all',\n );\n\n return {\n values: {},\n errors: toNestErrors(errors, options),\n };\n }\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n return {\n values: resolverOptions.raw ? Object.assign({}, values) : result.value,\n errors: {},\n };\n };\n}\n"],"names":["schema","_schemaOptions","resolverOptions","values","_","options","_temp2","result","issues","errors","typeschemaErrors","validateAllFieldCriteria","schemaErrors","Object","assign","length","error","path","_path","join","message","type","types","messages","appendErrors","concat","shift","parseErrorSchema","shouldUseNativeValidation","criteriaMode","toNestErrors","validateFieldsNatively","raw","value","validate","_temp","Promise","resolve","then","_result","e","reject"],"mappings":"6FAmFM,SACJA,EACAC,EACAC,GAIA,YAJAA,IAAAA,IAAAA,EAEI,IAEUC,SAAAA,EAAQC,EAAGC,GAAO,QAAIC,EAAA,WAMlC,GAAIC,EAAOC,OAAQ,CACjB,IAAMC,EAvFa,SACvBC,EACAC,GAKA,IAHA,IAAMC,EAAeC,OAAOC,OAAO,GAAIJ,GACjCD,EAAqC,GAEpCG,EAAaG,QAAU,CAC5B,IAAMC,EAAQN,EAAiB,GAE/B,GAAKM,EAAMC,KAAX,CAGA,IAAMC,EAAQF,EAAMC,KAAKE,KAAK,KAM9B,GAJKV,EAAOS,KACVT,EAAOS,GAAS,CAAEE,QAASJ,EAAMI,QAASC,KAAM,KAG9CV,EAA0B,CAC5B,IAAMW,EAAQb,EAAOS,GAAOI,MACtBC,EAAWD,GAASA,EAAM,IAEhCb,EAAOS,GAASM,EAAYA,aAC1BN,EACAP,EACAF,EACA,GACAc,EACK,GAAgBE,OAAOF,EAAsBP,EAAMI,SACpDJ,EAAMI,QAEd,CAEAR,EAAac,OAtBb,CAuBF,CAEA,OAAOjB,CACT,CAiDqBkB,CACbpB,EAAOC,QACNH,EAAQuB,2BAAsD,QAAzBvB,EAAQwB,cAGhD,MAAO,CACL1B,OAAQ,CAAE,EACVM,OAAQqB,EAAAA,aAAarB,EAAQJ,GAEjC,CAIA,OAFAA,EAAQuB,2BAA6BG,yBAAuB,CAAA,EAAI1B,GAEzD,CACLF,OAAQD,EAAgB8B,IAAMnB,OAAOC,OAAO,CAAA,EAAIX,GAAUI,EAAO0B,MACjExB,OAAQ,CAAA,EACR,EAtBEF,EAASP,EAAO,aAAakC,SAAS/B,GAAQgC,EAC9C5B,WAAAA,GAAAA,aAAkB6B,QAAOA,OAAAA,QAAAC,QACZ9B,GAAM+B,cAAAC,GAArBhC,EAAMgC,CAAgB,GADpBhC,UACoB6B,QAAAC,QAAAF,GAAAA,EAAAG,KAAAH,EAAAG,KAAAhC,GAAAA,IAqB1B,CAAC,MAAAkC,GAAAJ,OAAAA,QAAAK,OAAAD,EACH,CAAA,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{toNestErrors as e,validateFieldsNatively as r}from"@hookform/resolvers";import{appendErrors as t}from"react-hook-form";function s(s,o,a){return void 0===a&&(a={}),function(o,i,n){try{var u=function(){if(f.issues){var s=function(e,r){for(var s=Object.assign([],e),o={};s.length;){var a=e[0];if(a.path){var i=a.path.join(".");if(o[i]||(o[i]={message:a.message,type:""}),r){var n=o[i].types,u=n&&n[""];o[i]=t(i,r,o,"",u?[].concat(u,a.message):a.message)}s.shift()}}return o}(f.issues,!n.shouldUseNativeValidation&&"all"===n.criteriaMode);return{values:{},errors:e(s,n)}}return n.shouldUseNativeValidation&&r({},n),{values:a.raw?Object.assign({},o):f.value,errors:{}}},f=s["~standard"].validate(o),c=function(){if(f instanceof Promise)return Promise.resolve(f).then(function(e){f=e})}();return Promise.resolve(c&&c.then?c.then(u):u())}catch(e){return Promise.reject(e)}}}export{s as typeschemaResolver};
|
|
2
2
|
//# sourceMappingURL=typeschema.module.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{toNestErrors as e,validateFieldsNatively as s}from"@hookform/resolvers";import{appendErrors as t}from"react-hook-form";function o(o,a,r={}){return async(a,i,n)=>{let c=o["~standard"].validate(a);if(c instanceof Promise&&(c=await c),c.issues){const s=((e,s)=>{const o=Object.assign([],e),a={};for(;o.length;){const r=e[0];if(!r.path)continue;const i=r.path.join(".");if(a[i]||(a[i]={message:r.message,type:""}),s){const e=a[i].types,o=e&&e[""];a[i]=t(i,s,a,"",o?[].concat(o,r.message):r.message)}o.shift()}return a})(c.issues,!n.shouldUseNativeValidation&&"all"===n.criteriaMode);return{values:{},errors:e(s,n)}}return n.shouldUseNativeValidation&&s({},n),{values:r.raw?Object.assign({},a):c.value,errors:{}}}}export{o as typeschemaResolver};
|
|
2
2
|
//# sourceMappingURL=typeschema.modern.mjs.map
|