@hookform/resolvers 5.4.1 → 5.4.3
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/arktype/src/__tests__/__snapshots__/arktype.ts.snap +11 -11
- package/arktype/src/__tests__/arktype.ts +15 -6
- package/computed-types/src/__tests__/computed-types.ts +15 -6
- package/effect-ts/src/__tests__/effect-ts.ts +15 -6
- package/io-ts/src/__tests__/io-ts.ts +15 -6
- package/package.json +1 -1
- 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/src/__tests__/__snapshots__/standard-schema.ts.snap +31 -0
- package/standard-schema/src/__tests__/standard-schema.ts +126 -13
- package/standard-schema/src/standard-schema.ts +62 -14
- package/superstruct/dist/superstruct.d.ts +5 -0
- 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/src/__tests__/superstruct.ts +22 -8
- package/superstruct/src/superstruct.ts +13 -0
- package/typanion/src/__tests__/typanion.ts +8 -3
- package/typebox/src/__tests__/typebox.ts +31 -18
- 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/src/__tests__/typeschema.ts +23 -15
- package/typeschema/src/typeschema.ts +6 -6
- package/valibot/src/__tests__/valibot.ts +15 -6
- package/vine/src/__tests__/vine.ts +15 -6
- package/yup/dist/yup.d.ts +2 -2
- package/yup/dist/yup.js.map +1 -1
- package/yup/dist/yup.modern.mjs.map +1 -1
- package/yup/dist/yup.module.js.map +1 -1
- package/yup/dist/yup.umd.js.map +1 -1
- package/yup/src/__tests__/yup.ts +15 -6
- package/yup/src/yup.ts +10 -2
- package/zod/dist/zod.d.ts +8 -2
- package/zod/dist/zod.js +1 -1
- package/zod/dist/zod.js.map +1 -1
- package/zod/dist/zod.mjs +1 -1
- package/zod/dist/zod.modern.mjs +1 -1
- package/zod/dist/zod.modern.mjs.map +1 -1
- package/zod/dist/zod.module.js +1 -1
- package/zod/dist/zod.module.js.map +1 -1
- package/zod/dist/zod.umd.js +1 -1
- package/zod/dist/zod.umd.js.map +1 -1
- package/zod/src/__tests__/__snapshots__/zod-v3.ts.snap +430 -0
- package/zod/src/__tests__/__snapshots__/zod-v4-mini.ts.snap +472 -0
- package/zod/src/__tests__/__snapshots__/zod-v4.ts.snap +434 -0
- package/zod/src/__tests__/zod-v3.ts +45 -6
- package/zod/src/__tests__/zod-v4-mini.ts +21 -11
- package/zod/src/__tests__/zod-v4.ts +47 -11
- package/zod/src/zod.ts +28 -5
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
1
2
|
import { Resolver, SubmitHandler, useForm } from 'react-hook-form';
|
|
2
3
|
import { z } from 'zod/v3';
|
|
3
4
|
import { standardSchemaResolver } from '..';
|
|
@@ -74,6 +75,111 @@ describe('standardSchemaResolver', () => {
|
|
|
74
75
|
expect(result).toMatchSnapshot();
|
|
75
76
|
});
|
|
76
77
|
|
|
78
|
+
it('should return a root error when a standard schema issue has no path', async () => {
|
|
79
|
+
const result = await standardSchemaResolver({
|
|
80
|
+
'~standard': {
|
|
81
|
+
version: 1,
|
|
82
|
+
vendor: 'custom',
|
|
83
|
+
validate: () => ({
|
|
84
|
+
issues: [
|
|
85
|
+
{
|
|
86
|
+
message: 'Invalid input',
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
}),
|
|
90
|
+
},
|
|
91
|
+
})(validData, undefined, {
|
|
92
|
+
fields,
|
|
93
|
+
shouldUseNativeValidation,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(result).toMatchSnapshot();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should collect all root errors when validateAllFieldCriteria is true', async () => {
|
|
100
|
+
const result = await standardSchemaResolver({
|
|
101
|
+
'~standard': {
|
|
102
|
+
version: 1,
|
|
103
|
+
vendor: 'custom',
|
|
104
|
+
validate: () => ({
|
|
105
|
+
issues: [
|
|
106
|
+
{
|
|
107
|
+
message: 'Invalid union',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
message: 'Missing discriminator',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
}),
|
|
114
|
+
},
|
|
115
|
+
})(validData, undefined, {
|
|
116
|
+
fields,
|
|
117
|
+
criteriaMode: 'all',
|
|
118
|
+
shouldUseNativeValidation,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(result).toMatchSnapshot();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should preserve a root field error when a schema root error is present', async () => {
|
|
125
|
+
const result = await standardSchemaResolver({
|
|
126
|
+
'~standard': {
|
|
127
|
+
version: 1,
|
|
128
|
+
vendor: 'custom',
|
|
129
|
+
validate: () => ({
|
|
130
|
+
issues: [
|
|
131
|
+
{
|
|
132
|
+
path: [{ key: 'root' }],
|
|
133
|
+
message: 'Root field error',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
message: 'Schema root error',
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
}),
|
|
140
|
+
},
|
|
141
|
+
})(validData, undefined, {
|
|
142
|
+
fields,
|
|
143
|
+
shouldUseNativeValidation,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(result.errors).toMatchObject({
|
|
147
|
+
root: {
|
|
148
|
+
message: 'Root field error\nSchema root error',
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should only keep the first error message for the same field path in `firstError` mode', async () => {
|
|
154
|
+
const result = await standardSchemaResolver({
|
|
155
|
+
'~standard': {
|
|
156
|
+
version: 1,
|
|
157
|
+
vendor: 'custom',
|
|
158
|
+
validate: () => ({
|
|
159
|
+
issues: [
|
|
160
|
+
{
|
|
161
|
+
path: [{ key: 'username' }],
|
|
162
|
+
message: 'First username error',
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
path: [{ key: 'username' }],
|
|
166
|
+
message: 'Second username error',
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
}),
|
|
170
|
+
},
|
|
171
|
+
})(validData, undefined, {
|
|
172
|
+
fields,
|
|
173
|
+
shouldUseNativeValidation,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
expect(result.errors).toMatchObject({
|
|
177
|
+
username: {
|
|
178
|
+
message: 'First username error',
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
77
183
|
/**
|
|
78
184
|
* Type inference tests
|
|
79
185
|
*/
|
|
@@ -113,13 +219,16 @@ describe('standardSchemaResolver', () => {
|
|
|
113
219
|
|
|
114
220
|
it('should correctly infer the output type from a standardSchema schema for the handleSubmit function in useForm', () => {
|
|
115
221
|
const schema = z.object({ id: z.number() });
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
222
|
+
const {
|
|
223
|
+
result: { current: form },
|
|
224
|
+
} = renderHook(() =>
|
|
225
|
+
useForm({
|
|
226
|
+
resolver: standardSchemaResolver(schema),
|
|
227
|
+
defaultValues: {
|
|
228
|
+
id: 3,
|
|
229
|
+
},
|
|
230
|
+
}),
|
|
231
|
+
);
|
|
123
232
|
|
|
124
233
|
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
125
234
|
|
|
@@ -133,12 +242,16 @@ describe('standardSchemaResolver', () => {
|
|
|
133
242
|
it('should correctly infer the output type from a standardSchema schema with a transform for the handleSubmit function in useForm', () => {
|
|
134
243
|
const schema = z.object({ id: z.number().transform((val) => String(val)) });
|
|
135
244
|
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
245
|
+
const {
|
|
246
|
+
result: { current: form },
|
|
247
|
+
} = renderHook(() =>
|
|
248
|
+
useForm({
|
|
249
|
+
resolver: standardSchemaResolver(schema),
|
|
250
|
+
defaultValues: {
|
|
251
|
+
id: 3,
|
|
252
|
+
},
|
|
253
|
+
}),
|
|
254
|
+
);
|
|
142
255
|
|
|
143
256
|
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
144
257
|
|
|
@@ -3,33 +3,81 @@ import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
|
3
3
|
import { getDotPath } from '@standard-schema/utils';
|
|
4
4
|
import { FieldError, FieldValues, Resolver } from 'react-hook-form';
|
|
5
5
|
|
|
6
|
+
const schemaRootKey = Symbol('Schema root');
|
|
7
|
+
|
|
8
|
+
function appendErrorMessage(error: FieldError, message: string | undefined) {
|
|
9
|
+
if (!message) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
error.message = error.message ? `${error.message}\n${message}` : message;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function appendErrorTypes(error: FieldError, types: FieldError['types']) {
|
|
17
|
+
if (!types) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const nextTypes = error.types || {};
|
|
22
|
+
|
|
23
|
+
for (const message of Object.values(types)) {
|
|
24
|
+
nextTypes[Object.keys(nextTypes).length] = message;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
error.types = nextTypes;
|
|
28
|
+
}
|
|
29
|
+
|
|
6
30
|
function parseErrorSchema(
|
|
7
31
|
issues: readonly StandardSchemaV1.Issue[],
|
|
8
32
|
validateAllFieldCriteria: boolean,
|
|
9
33
|
) {
|
|
10
|
-
const errors
|
|
34
|
+
const errors = new Map<string | typeof schemaRootKey, FieldError>();
|
|
11
35
|
|
|
12
36
|
for (let i = 0; i < issues.length; i++) {
|
|
13
37
|
const error = issues[i];
|
|
14
|
-
const path = getDotPath(error);
|
|
38
|
+
const path = getDotPath(error) || schemaRootKey;
|
|
39
|
+
const fieldError = errors.get(path);
|
|
40
|
+
|
|
41
|
+
if (!fieldError) {
|
|
42
|
+
errors.set(path, { message: error.message, type: '' });
|
|
43
|
+
} else if (path === schemaRootKey) {
|
|
44
|
+
// Root-level issues aren't tied to a single field, so there's no
|
|
45
|
+
// natural "first" message to prefer the way there is for a field —
|
|
46
|
+
// keep every one of them regardless of criteriaMode.
|
|
47
|
+
appendErrorMessage(fieldError, error.message);
|
|
48
|
+
}
|
|
15
49
|
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
50
|
+
if (validateAllFieldCriteria) {
|
|
51
|
+
const currentError = errors.get(path)!;
|
|
52
|
+
const types = currentError.types || {};
|
|
53
|
+
|
|
54
|
+
currentError.types = {
|
|
55
|
+
...types,
|
|
56
|
+
[Object.keys(types).length]: error.message,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const fieldErrors: Record<string, FieldError> = {};
|
|
62
|
+
|
|
63
|
+
for (const [path, error] of errors) {
|
|
64
|
+
if (path !== schemaRootKey) {
|
|
65
|
+
fieldErrors[path] = error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
20
68
|
|
|
21
|
-
|
|
22
|
-
const types = errors[path].types || {};
|
|
69
|
+
const rootError = errors.get(schemaRootKey);
|
|
23
70
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
71
|
+
if (rootError) {
|
|
72
|
+
if (fieldErrors.root) {
|
|
73
|
+
appendErrorMessage(fieldErrors.root, rootError.message);
|
|
74
|
+
appendErrorTypes(fieldErrors.root, rootError.types);
|
|
75
|
+
} else {
|
|
76
|
+
fieldErrors.root = rootError;
|
|
29
77
|
}
|
|
30
78
|
}
|
|
31
79
|
|
|
32
|
-
return
|
|
80
|
+
return fieldErrors;
|
|
33
81
|
}
|
|
34
82
|
|
|
35
83
|
export function standardSchemaResolver<
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { FieldValues, Resolver } from 'react-hook-form';
|
|
2
2
|
import { Infer, Struct, validate } from 'superstruct';
|
|
3
|
+
export declare function superstructResolver<Input extends FieldValues, Context, Output>(schema: Struct<Output, any>, schemaOptions: Omit<Parameters<typeof validate>[2], 'coerce'> & {
|
|
4
|
+
coerce: true;
|
|
5
|
+
}, resolverOptions?: {
|
|
6
|
+
raw?: boolean;
|
|
7
|
+
}): Resolver<Input, Context, Output>;
|
|
3
8
|
export declare function superstructResolver<Input extends FieldValues, Context, Output>(schema: Struct<Input, any>, schemaOptions?: Parameters<typeof validate>[2], resolverOptions?: {
|
|
4
9
|
raw?: false;
|
|
5
10
|
}): Resolver<Input, Context, Infer<typeof schema>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"superstruct.js","sources":["../src/superstruct.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { FieldError, FieldValues, Resolver } from 'react-hook-form';\nimport { Infer, Struct, StructError, validate } from 'superstruct';\n\nfunction parseErrorSchema(error: StructError) {\n return error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n}\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions?: {\n raw?: false;\n },\n): Resolver<Input, Context, Infer<typeof schema>>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions: Parameters<typeof validate>[2] | undefined,\n resolverOptions: {\n raw: true;\n },\n): Resolver<Input, Context, Input>;\n\n/**\n * Creates a resolver for react-hook-form using Superstruct schema validation\n * @param {Struct<TFieldValues, any>} schema - The Superstruct schema to validate against\n * @param {Parameters<typeof validate>[2]} [schemaOptions] - Optional Superstruct validation options\n * @param {Object} resolverOptions - Additional resolver configuration\n * @param {boolean} [resolverOptions.raw=false] - If true, returns raw values rather than validated results\n * @returns {Resolver<Infer<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = struct({\n * name: string(),\n * age: number()\n * });\n *\n * useForm({\n * resolver: superstructResolver(schema)\n * });\n */\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions: {\n raw?: boolean;\n } = {},\n): Resolver<Input, Context, Input | Output> {\n return (values: Input, _, options) => {\n const result = validate(values, schema, schemaOptions);\n\n if (result[0]) {\n return {\n values: {},\n errors: toNestErrors(parseErrorSchema(result[0]), options),\n };\n }\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n return {\n values: resolverOptions.raw ? Object.assign({}, values) : result[1],\n errors: {},\n };\n };\n}\n"],"names":["schema","schemaOptions","resolverOptions","values","_","options","error","result","validate","errors","toNestErrors","failures","reduce","previous","path","join","message","type","shouldUseNativeValidation","validateFieldsNatively","raw","Object","assign"],"mappings":"
|
|
1
|
+
{"version":3,"file":"superstruct.js","sources":["../src/superstruct.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { FieldError, FieldValues, Resolver } from 'react-hook-form';\nimport { Infer, Struct, StructError, validate } from 'superstruct';\n\nfunction parseErrorSchema(error: StructError) {\n return error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n}\n\n// This is required to correctly type the input of the returned function when coerce is true\n// superstruct does not store the input type of a schema alongside it\n// Infer<typeof schema> gives the output type of the coercion\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Output, any>,\n schemaOptions: Omit<Parameters<typeof validate>[2], 'coerce'> & {\n coerce: true;\n },\n resolverOptions?: {\n raw?: boolean;\n },\n): Resolver<Input, Context, Output>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions?: {\n raw?: false;\n },\n): Resolver<Input, Context, Infer<typeof schema>>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions: Parameters<typeof validate>[2] | undefined,\n resolverOptions: {\n raw: true;\n },\n): Resolver<Input, Context, Input>;\n\n/**\n * Creates a resolver for react-hook-form using Superstruct schema validation\n * @param {Struct<TFieldValues, any>} schema - The Superstruct schema to validate against\n * @param {Parameters<typeof validate>[2]} [schemaOptions] - Optional Superstruct validation options\n * @param {Object} resolverOptions - Additional resolver configuration\n * @param {boolean} [resolverOptions.raw=false] - If true, returns raw values rather than validated results\n * @returns {Resolver<Infer<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = struct({\n * name: string(),\n * age: number()\n * });\n *\n * useForm({\n * resolver: superstructResolver(schema)\n * });\n */\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions: {\n raw?: boolean;\n } = {},\n): Resolver<Input, Context, Input | Output> {\n return (values: Input, _, options) => {\n const result = validate(values, schema, schemaOptions);\n\n if (result[0]) {\n return {\n values: {},\n errors: toNestErrors(parseErrorSchema(result[0]), options),\n };\n }\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n return {\n values: resolverOptions.raw ? Object.assign({}, values) : result[1],\n errors: {},\n };\n };\n}\n"],"names":["schema","schemaOptions","resolverOptions","values","_","options","error","result","validate","errors","toNestErrors","failures","reduce","previous","path","join","message","type","shouldUseNativeValidation","validateFieldsNatively","raw","Object","assign"],"mappings":"mGA8DEA,EACAC,EACAC,GAIA,gBAJAA,IAAAA,EAEI,CAAE,GAEEC,SAAAA,EAAeC,EAAGC,GACxB,IAjEsBC,EAiEhBC,EAASC,EAAAA,SAASL,EAAQH,EAAQC,GAExC,OAAIM,EAAO,GACF,CACLJ,OAAQ,CAAA,EACRM,OAAQC,EAAYA,cAtEFJ,EAsEoBC,EAAO,GArE5CD,EAAMK,WAAWC,OACtB,SAACC,EAAUP,GACT,OAACO,EAASP,EAAMQ,KAAKC,KAAK,MAAQ,CAChCC,QAASV,EAAMU,QACfC,KAAMX,EAAMW,QACRJ,CAAQ,EAChB,CAAA,IA+DsDR,KAItDA,EAAQa,2BAA6BC,yBAAuB,CAAA,EAAId,GAEzD,CACLF,OAAQD,EAAgBkB,IAAMC,OAAOC,OAAO,CAAE,EAAEnB,GAAUI,EAAO,GACjEE,OAAQ,CAAA,GAEZ,CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"superstruct.modern.mjs","sources":["../src/superstruct.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { FieldError, FieldValues, Resolver } from 'react-hook-form';\nimport { Infer, Struct, StructError, validate } from 'superstruct';\n\nfunction parseErrorSchema(error: StructError) {\n return error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n}\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions?: {\n raw?: false;\n },\n): Resolver<Input, Context, Infer<typeof schema>>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions: Parameters<typeof validate>[2] | undefined,\n resolverOptions: {\n raw: true;\n },\n): Resolver<Input, Context, Input>;\n\n/**\n * Creates a resolver for react-hook-form using Superstruct schema validation\n * @param {Struct<TFieldValues, any>} schema - The Superstruct schema to validate against\n * @param {Parameters<typeof validate>[2]} [schemaOptions] - Optional Superstruct validation options\n * @param {Object} resolverOptions - Additional resolver configuration\n * @param {boolean} [resolverOptions.raw=false] - If true, returns raw values rather than validated results\n * @returns {Resolver<Infer<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = struct({\n * name: string(),\n * age: number()\n * });\n *\n * useForm({\n * resolver: superstructResolver(schema)\n * });\n */\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions: {\n raw?: boolean;\n } = {},\n): Resolver<Input, Context, Input | Output> {\n return (values: Input, _, options) => {\n const result = validate(values, schema, schemaOptions);\n\n if (result[0]) {\n return {\n values: {},\n errors: toNestErrors(parseErrorSchema(result[0]), options),\n };\n }\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n return {\n values: resolverOptions.raw ? Object.assign({}, values) : result[1],\n errors: {},\n };\n };\n}\n"],"names":["superstructResolver","schema","schemaOptions","resolverOptions","values","_","options","result","validate","errors","toNestErrors","error","failures","reduce","previous","path","join","message","type","shouldUseNativeValidation","validateFieldsNatively","raw","Object","assign"],"mappings":"
|
|
1
|
+
{"version":3,"file":"superstruct.modern.mjs","sources":["../src/superstruct.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { FieldError, FieldValues, Resolver } from 'react-hook-form';\nimport { Infer, Struct, StructError, validate } from 'superstruct';\n\nfunction parseErrorSchema(error: StructError) {\n return error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n}\n\n// This is required to correctly type the input of the returned function when coerce is true\n// superstruct does not store the input type of a schema alongside it\n// Infer<typeof schema> gives the output type of the coercion\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Output, any>,\n schemaOptions: Omit<Parameters<typeof validate>[2], 'coerce'> & {\n coerce: true;\n },\n resolverOptions?: {\n raw?: boolean;\n },\n): Resolver<Input, Context, Output>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions?: {\n raw?: false;\n },\n): Resolver<Input, Context, Infer<typeof schema>>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions: Parameters<typeof validate>[2] | undefined,\n resolverOptions: {\n raw: true;\n },\n): Resolver<Input, Context, Input>;\n\n/**\n * Creates a resolver for react-hook-form using Superstruct schema validation\n * @param {Struct<TFieldValues, any>} schema - The Superstruct schema to validate against\n * @param {Parameters<typeof validate>[2]} [schemaOptions] - Optional Superstruct validation options\n * @param {Object} resolverOptions - Additional resolver configuration\n * @param {boolean} [resolverOptions.raw=false] - If true, returns raw values rather than validated results\n * @returns {Resolver<Infer<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = struct({\n * name: string(),\n * age: number()\n * });\n *\n * useForm({\n * resolver: superstructResolver(schema)\n * });\n */\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions: {\n raw?: boolean;\n } = {},\n): Resolver<Input, Context, Input | Output> {\n return (values: Input, _, options) => {\n const result = validate(values, schema, schemaOptions);\n\n if (result[0]) {\n return {\n values: {},\n errors: toNestErrors(parseErrorSchema(result[0]), options),\n };\n }\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n return {\n values: resolverOptions.raw ? Object.assign({}, values) : result[1],\n errors: {},\n };\n };\n}\n"],"names":["superstructResolver","schema","schemaOptions","resolverOptions","values","_","options","result","validate","errors","toNestErrors","error","failures","reduce","previous","path","join","message","type","shouldUseNativeValidation","validateFieldsNatively","raw","Object","assign"],"mappings":"sHA6DgB,SAAAA,EACdC,EACAC,EACAC,EAEI,CAAE,GAEN,MAAO,CAACC,EAAeC,EAAGC,KACxB,MAAMC,EAASC,EAASJ,EAAQH,EAAQC,GAExC,OAAIK,EAAO,GACF,CACLH,OAAQ,CAAA,EACRK,OAAQC,GAtEUC,EAsEoBJ,EAAO,GArE5CI,EAAMC,WAAWC,OACtB,CAACC,EAAUH,KACRG,EAASH,EAAMI,KAAKC,KAAK,MAAQ,CAChCC,QAASN,EAAMM,QACfC,KAAMP,EAAMO,QACRJ,EACR,KA+DsDR,KAItDA,EAAQa,2BAA6BC,EAAuB,GAAId,GAEzD,CACLF,OAAQD,EAAgBkB,IAAMC,OAAOC,OAAO,CAAA,EAAInB,GAAUG,EAAO,GACjEE,OAAQ,CAAA,IA9Ed,IAA0BE,EAiF1B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"superstruct.module.js","sources":["../src/superstruct.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { FieldError, FieldValues, Resolver } from 'react-hook-form';\nimport { Infer, Struct, StructError, validate } from 'superstruct';\n\nfunction parseErrorSchema(error: StructError) {\n return error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n}\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions?: {\n raw?: false;\n },\n): Resolver<Input, Context, Infer<typeof schema>>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions: Parameters<typeof validate>[2] | undefined,\n resolverOptions: {\n raw: true;\n },\n): Resolver<Input, Context, Input>;\n\n/**\n * Creates a resolver for react-hook-form using Superstruct schema validation\n * @param {Struct<TFieldValues, any>} schema - The Superstruct schema to validate against\n * @param {Parameters<typeof validate>[2]} [schemaOptions] - Optional Superstruct validation options\n * @param {Object} resolverOptions - Additional resolver configuration\n * @param {boolean} [resolverOptions.raw=false] - If true, returns raw values rather than validated results\n * @returns {Resolver<Infer<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = struct({\n * name: string(),\n * age: number()\n * });\n *\n * useForm({\n * resolver: superstructResolver(schema)\n * });\n */\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions: {\n raw?: boolean;\n } = {},\n): Resolver<Input, Context, Input | Output> {\n return (values: Input, _, options) => {\n const result = validate(values, schema, schemaOptions);\n\n if (result[0]) {\n return {\n values: {},\n errors: toNestErrors(parseErrorSchema(result[0]), options),\n };\n }\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n return {\n values: resolverOptions.raw ? Object.assign({}, values) : result[1],\n errors: {},\n };\n };\n}\n"],"names":["superstructResolver","schema","schemaOptions","resolverOptions","values","_","options","error","result","validate","errors","toNestErrors","failures","reduce","previous","path","join","message","type","shouldUseNativeValidation","validateFieldsNatively","raw","Object","assign"],"mappings":"+
|
|
1
|
+
{"version":3,"file":"superstruct.module.js","sources":["../src/superstruct.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { FieldError, FieldValues, Resolver } from 'react-hook-form';\nimport { Infer, Struct, StructError, validate } from 'superstruct';\n\nfunction parseErrorSchema(error: StructError) {\n return error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n}\n\n// This is required to correctly type the input of the returned function when coerce is true\n// superstruct does not store the input type of a schema alongside it\n// Infer<typeof schema> gives the output type of the coercion\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Output, any>,\n schemaOptions: Omit<Parameters<typeof validate>[2], 'coerce'> & {\n coerce: true;\n },\n resolverOptions?: {\n raw?: boolean;\n },\n): Resolver<Input, Context, Output>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions?: {\n raw?: false;\n },\n): Resolver<Input, Context, Infer<typeof schema>>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions: Parameters<typeof validate>[2] | undefined,\n resolverOptions: {\n raw: true;\n },\n): Resolver<Input, Context, Input>;\n\n/**\n * Creates a resolver for react-hook-form using Superstruct schema validation\n * @param {Struct<TFieldValues, any>} schema - The Superstruct schema to validate against\n * @param {Parameters<typeof validate>[2]} [schemaOptions] - Optional Superstruct validation options\n * @param {Object} resolverOptions - Additional resolver configuration\n * @param {boolean} [resolverOptions.raw=false] - If true, returns raw values rather than validated results\n * @returns {Resolver<Infer<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = struct({\n * name: string(),\n * age: number()\n * });\n *\n * useForm({\n * resolver: superstructResolver(schema)\n * });\n */\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions: {\n raw?: boolean;\n } = {},\n): Resolver<Input, Context, Input | Output> {\n return (values: Input, _, options) => {\n const result = validate(values, schema, schemaOptions);\n\n if (result[0]) {\n return {\n values: {},\n errors: toNestErrors(parseErrorSchema(result[0]), options),\n };\n }\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n return {\n values: resolverOptions.raw ? Object.assign({}, values) : result[1],\n errors: {},\n };\n };\n}\n"],"names":["superstructResolver","schema","schemaOptions","resolverOptions","values","_","options","error","result","validate","errors","toNestErrors","failures","reduce","previous","path","join","message","type","shouldUseNativeValidation","validateFieldsNatively","raw","Object","assign"],"mappings":"+HA6DgBA,EACdC,EACAC,EACAC,GAIA,gBAJAA,IAAAA,EAEI,CAAE,GAEEC,SAAAA,EAAeC,EAAGC,GACxB,IAjEsBC,EAiEhBC,EAASC,EAASL,EAAQH,EAAQC,GAExC,OAAIM,EAAO,GACF,CACLJ,OAAQ,CAAA,EACRM,OAAQC,GAtEUJ,EAsEoBC,EAAO,GArE5CD,EAAMK,WAAWC,OACtB,SAACC,EAAUP,GACT,OAACO,EAASP,EAAMQ,KAAKC,KAAK,MAAQ,CAChCC,QAASV,EAAMU,QACfC,KAAMX,EAAMW,QACRJ,CAAQ,EAChB,CAAA,IA+DsDR,KAItDA,EAAQa,2BAA6BC,EAAuB,CAAA,EAAId,GAEzD,CACLF,OAAQD,EAAgBkB,IAAMC,OAAOC,OAAO,CAAE,EAAEnB,GAAUI,EAAO,GACjEE,OAAQ,CAAA,GAEZ,CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"superstruct.umd.js","sources":["../src/superstruct.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { FieldError, FieldValues, Resolver } from 'react-hook-form';\nimport { Infer, Struct, StructError, validate } from 'superstruct';\n\nfunction parseErrorSchema(error: StructError) {\n return error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n}\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions?: {\n raw?: false;\n },\n): Resolver<Input, Context, Infer<typeof schema>>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions: Parameters<typeof validate>[2] | undefined,\n resolverOptions: {\n raw: true;\n },\n): Resolver<Input, Context, Input>;\n\n/**\n * Creates a resolver for react-hook-form using Superstruct schema validation\n * @param {Struct<TFieldValues, any>} schema - The Superstruct schema to validate against\n * @param {Parameters<typeof validate>[2]} [schemaOptions] - Optional Superstruct validation options\n * @param {Object} resolverOptions - Additional resolver configuration\n * @param {boolean} [resolverOptions.raw=false] - If true, returns raw values rather than validated results\n * @returns {Resolver<Infer<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = struct({\n * name: string(),\n * age: number()\n * });\n *\n * useForm({\n * resolver: superstructResolver(schema)\n * });\n */\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions: {\n raw?: boolean;\n } = {},\n): Resolver<Input, Context, Input | Output> {\n return (values: Input, _, options) => {\n const result = validate(values, schema, schemaOptions);\n\n if (result[0]) {\n return {\n values: {},\n errors: toNestErrors(parseErrorSchema(result[0]), options),\n };\n }\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n return {\n values: resolverOptions.raw ? Object.assign({}, values) : result[1],\n errors: {},\n };\n };\n}\n"],"names":["schema","schemaOptions","resolverOptions","values","_","options","error","result","validate","errors","toNestErrors","failures","reduce","previous","path","join","message","type","shouldUseNativeValidation","validateFieldsNatively","raw","Object","assign"],"mappings":"
|
|
1
|
+
{"version":3,"file":"superstruct.umd.js","sources":["../src/superstruct.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { FieldError, FieldValues, Resolver } from 'react-hook-form';\nimport { Infer, Struct, StructError, validate } from 'superstruct';\n\nfunction parseErrorSchema(error: StructError) {\n return error.failures().reduce<Record<string, FieldError>>(\n (previous, error) =>\n (previous[error.path.join('.')] = {\n message: error.message,\n type: error.type,\n }) && previous,\n {},\n );\n}\n\n// This is required to correctly type the input of the returned function when coerce is true\n// superstruct does not store the input type of a schema alongside it\n// Infer<typeof schema> gives the output type of the coercion\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Output, any>,\n schemaOptions: Omit<Parameters<typeof validate>[2], 'coerce'> & {\n coerce: true;\n },\n resolverOptions?: {\n raw?: boolean;\n },\n): Resolver<Input, Context, Output>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions?: {\n raw?: false;\n },\n): Resolver<Input, Context, Infer<typeof schema>>;\n\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions: Parameters<typeof validate>[2] | undefined,\n resolverOptions: {\n raw: true;\n },\n): Resolver<Input, Context, Input>;\n\n/**\n * Creates a resolver for react-hook-form using Superstruct schema validation\n * @param {Struct<TFieldValues, any>} schema - The Superstruct schema to validate against\n * @param {Parameters<typeof validate>[2]} [schemaOptions] - Optional Superstruct validation options\n * @param {Object} resolverOptions - Additional resolver configuration\n * @param {boolean} [resolverOptions.raw=false] - If true, returns raw values rather than validated results\n * @returns {Resolver<Infer<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = struct({\n * name: string(),\n * age: number()\n * });\n *\n * useForm({\n * resolver: superstructResolver(schema)\n * });\n */\nexport function superstructResolver<Input extends FieldValues, Context, Output>(\n schema: Struct<Input, any>,\n schemaOptions?: Parameters<typeof validate>[2],\n resolverOptions: {\n raw?: boolean;\n } = {},\n): Resolver<Input, Context, Input | Output> {\n return (values: Input, _, options) => {\n const result = validate(values, schema, schemaOptions);\n\n if (result[0]) {\n return {\n values: {},\n errors: toNestErrors(parseErrorSchema(result[0]), options),\n };\n }\n\n options.shouldUseNativeValidation && validateFieldsNatively({}, options);\n\n return {\n values: resolverOptions.raw ? Object.assign({}, values) : result[1],\n errors: {},\n };\n };\n}\n"],"names":["schema","schemaOptions","resolverOptions","values","_","options","error","result","validate","errors","toNestErrors","failures","reduce","previous","path","join","message","type","shouldUseNativeValidation","validateFieldsNatively","raw","Object","assign"],"mappings":"qZA8DEA,EACAC,EACAC,GAIA,gBAJAA,IAAAA,EAEI,CAAE,GAEEC,SAAAA,EAAeC,EAAGC,GACxB,IAjEsBC,EAiEhBC,EAASC,EAAAA,SAASL,EAAQH,EAAQC,GAExC,OAAIM,EAAO,GACF,CACLJ,OAAQ,CAAA,EACRM,OAAQC,EAAYA,cAtEFJ,EAsEoBC,EAAO,GArE5CD,EAAMK,WAAWC,OACtB,SAACC,EAAUP,GACT,OAACO,EAASP,EAAMQ,KAAKC,KAAK,MAAQ,CAChCC,QAASV,EAAMU,QACfC,KAAMX,EAAMW,QACRJ,CAAQ,EAChB,CAAA,IA+DsDR,KAItDA,EAAQa,2BAA6BC,yBAAuB,CAAA,EAAId,GAEzD,CACLF,OAAQD,EAAgBkB,IAAMC,OAAOC,OAAO,CAAE,EAAEnB,GAAUI,EAAO,GACjEE,OAAQ,CAAA,GAEZ,CACF"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
1
2
|
import { Resolver, SubmitHandler, useForm } from 'react-hook-form';
|
|
2
3
|
import * as s from 'superstruct';
|
|
3
4
|
import { superstructResolver } from '..';
|
|
@@ -18,8 +19,9 @@ describe('superstructResolver', () => {
|
|
|
18
19
|
it('should return values from superstructResolver with coerced values', async () => {
|
|
19
20
|
const result = await superstructResolver(
|
|
20
21
|
s.object({
|
|
21
|
-
id: s.coerce(s.
|
|
22
|
+
id: s.coerce(s.string(), s.number(), (val) => String(val)),
|
|
22
23
|
}),
|
|
24
|
+
{ coerce: true },
|
|
23
25
|
)({ id: 1 }, undefined, {
|
|
24
26
|
fields,
|
|
25
27
|
shouldUseNativeValidation,
|
|
@@ -64,9 +66,13 @@ describe('superstructResolver', () => {
|
|
|
64
66
|
it('should correctly infer the output type from a superstruct schema for the handleSubmit function in useForm', () => {
|
|
65
67
|
const schema = s.object({ id: s.number() });
|
|
66
68
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
})
|
|
69
|
+
const {
|
|
70
|
+
result: { current: form },
|
|
71
|
+
} = renderHook(() =>
|
|
72
|
+
useForm({
|
|
73
|
+
resolver: superstructResolver(schema, {}),
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
70
76
|
|
|
71
77
|
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
72
78
|
|
|
@@ -82,11 +88,19 @@ describe('superstructResolver', () => {
|
|
|
82
88
|
id: s.coerce(s.string(), s.number(), (val) => String(val)),
|
|
83
89
|
});
|
|
84
90
|
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
})
|
|
91
|
+
const {
|
|
92
|
+
result: { current: form },
|
|
93
|
+
} = renderHook(() =>
|
|
94
|
+
useForm({
|
|
95
|
+
// With coerce, there is no way to infer the input type of the schema
|
|
96
|
+
resolver: superstructResolver<{ id: number }, unknown, { id: string }>(
|
|
97
|
+
schema,
|
|
98
|
+
{ coerce: true },
|
|
99
|
+
),
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
88
102
|
|
|
89
|
-
expectTypeOf(form.watch('id')).toEqualTypeOf<
|
|
103
|
+
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
90
104
|
|
|
91
105
|
expectTypeOf(form.handleSubmit).parameter(0).toEqualTypeOf<
|
|
92
106
|
SubmitHandler<{
|
|
@@ -13,6 +13,19 @@ function parseErrorSchema(error: StructError) {
|
|
|
13
13
|
);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
// This is required to correctly type the input of the returned function when coerce is true
|
|
17
|
+
// superstruct does not store the input type of a schema alongside it
|
|
18
|
+
// Infer<typeof schema> gives the output type of the coercion
|
|
19
|
+
export function superstructResolver<Input extends FieldValues, Context, Output>(
|
|
20
|
+
schema: Struct<Output, any>,
|
|
21
|
+
schemaOptions: Omit<Parameters<typeof validate>[2], 'coerce'> & {
|
|
22
|
+
coerce: true;
|
|
23
|
+
},
|
|
24
|
+
resolverOptions?: {
|
|
25
|
+
raw?: boolean;
|
|
26
|
+
},
|
|
27
|
+
): Resolver<Input, Context, Output>;
|
|
28
|
+
|
|
16
29
|
export function superstructResolver<Input extends FieldValues, Context, Output>(
|
|
17
30
|
schema: Struct<Input, any>,
|
|
18
31
|
schemaOptions?: Parameters<typeof validate>[2],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
1
2
|
import { SubmitHandler } from 'react-hook-form';
|
|
2
3
|
import { useForm } from 'react-hook-form';
|
|
3
4
|
import { Resolver } from 'react-hook-form';
|
|
@@ -51,9 +52,13 @@ describe('typanionResolver', () => {
|
|
|
51
52
|
it('should correctly infer the output type from a typanion schema for the handleSubmit function in useForm', () => {
|
|
52
53
|
const schema = t.isObject({ id: t.isNumber() });
|
|
53
54
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
})
|
|
55
|
+
const {
|
|
56
|
+
result: { current: form },
|
|
57
|
+
} = renderHook(() =>
|
|
58
|
+
useForm({
|
|
59
|
+
resolver: typanionResolver(schema),
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
57
62
|
|
|
58
63
|
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
59
64
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Type } from '@sinclair/typebox';
|
|
2
2
|
import { TypeCompiler } from '@sinclair/typebox/compiler';
|
|
3
|
+
import { renderHook } from '@testing-library/react';
|
|
3
4
|
import { Resolver, SubmitHandler, useForm } from 'react-hook-form';
|
|
4
5
|
import { typeboxResolver } from '..';
|
|
5
6
|
import { fields, invalidData, schema, validData } from './__fixtures__/data';
|
|
@@ -72,12 +73,16 @@ describe('typeboxResolver', () => {
|
|
|
72
73
|
it('should correctly infer the output type from a typebox schema for the handleSubmit function in useForm', () => {
|
|
73
74
|
const schema = Type.Object({ id: Type.Number() });
|
|
74
75
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
const {
|
|
77
|
+
result: { current: form },
|
|
78
|
+
} = renderHook(() =>
|
|
79
|
+
useForm({
|
|
80
|
+
resolver: typeboxResolver(schema),
|
|
81
|
+
defaultValues: {
|
|
82
|
+
id: 3,
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
);
|
|
81
86
|
|
|
82
87
|
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
83
88
|
|
|
@@ -91,12 +96,16 @@ describe('typeboxResolver', () => {
|
|
|
91
96
|
it('should correctly infer the output type from a typebox schema with TypeCompiler for the handleSubmit function in useForm', () => {
|
|
92
97
|
const typecheck = TypeCompiler.Compile(Type.Object({ id: Type.Number() }));
|
|
93
98
|
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
const {
|
|
100
|
+
result: { current: form },
|
|
101
|
+
} = renderHook(() =>
|
|
102
|
+
useForm({
|
|
103
|
+
resolver: typeboxResolver(typecheck),
|
|
104
|
+
defaultValues: {
|
|
105
|
+
id: 3,
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
);
|
|
100
109
|
|
|
101
110
|
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
102
111
|
|
|
@@ -114,12 +123,16 @@ describe('typeboxResolver', () => {
|
|
|
114
123
|
.Encode((v) => Number.parseInt(v)),
|
|
115
124
|
});
|
|
116
125
|
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
126
|
+
const {
|
|
127
|
+
result: { current: form },
|
|
128
|
+
} = renderHook(() =>
|
|
129
|
+
useForm({
|
|
130
|
+
resolver: typeboxResolver(schema),
|
|
131
|
+
defaultValues: {
|
|
132
|
+
id: 3,
|
|
133
|
+
},
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
123
136
|
|
|
124
137
|
expectTypeOf(form.watch('id')).toEqualTypeOf<number>();
|
|
125
138
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var r=require("@hookform/resolvers"),e=require("react-hook-form");function t(r,e){(null==e||e>r.length)&&(e=r.length);for(var t=0,n=Array(e);t<e;t++)n[t]=r[t];return n}exports.typeschemaResolver=function(n,a,o){return void 0===o&&(o={}),function(a,i,s){try{var u=function(){if(l.issues){var n=function(r,n){for(var a,o={},i=function(r){var e="undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"];if(e)return(e=e.call(r)).next.bind(e);if(Array.isArray(r)||(e=function(r,e){if(r){if("string"==typeof r)return t(r,e);var n={}.toString.call(r).slice(8,-1);return"Object"===n&&r.constructor&&(n=r.constructor.name),"Map"===n||"Set"===n?Array.from(r):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?t(r,e):void 0}}(r))){e&&(r=e);var n=0;return function(){return n>=r.length?{done:!0}:{done:!1,value:r[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(Object.assign([],r));!(a=i()).done;){var s=a.value;if(s.path){var u=s.path.join(".");if(o[u]||(o[u]={message:s.message,type:""}),n){var l=o[u].types,c=l&&l[""];o[u]=e.appendErrors(u,n,o,"",c?[].concat(c,s.message):s.message)}}}return o}(l.issues,!s.shouldUseNativeValidation&&"all"===s.criteriaMode);return{values:{},errors:r.toNestErrors(n,s)}}return s.shouldUseNativeValidation&&r.validateFieldsNatively({},s),{values:o.raw?Object.assign({},a):l.value,errors:{}}},l=n["~standard"].validate(a),c=function(){if(l instanceof Promise)return Promise.resolve(l).then(function(r){l=r})}();return Promise.resolve(c&&c.then?c.then(u):u())}catch(r){return Promise.reject(r)}}};
|
|
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 { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n FieldError,\n FieldErrors,\n FieldValues,\n Resolver,\n appendErrors,\n} from 'react-hook-form';\n\nconst parseErrorSchema = (\n typeschemaErrors: readonly StandardSchemaV1.Issue[],\n validateAllFieldCriteria: boolean,\n): FieldErrors => {\n const schemaErrors = Object.assign([]
|
|
1
|
+
{"version":3,"file":"typeschema.js","sources":["../src/typeschema.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n FieldError,\n FieldErrors,\n FieldValues,\n Resolver,\n appendErrors,\n} from 'react-hook-form';\n\nconst parseErrorSchema = (\n typeschemaErrors: readonly StandardSchemaV1.Issue[],\n validateAllFieldCriteria: boolean,\n): FieldErrors => {\n // Need to pass generics so that the array isn't typed as \"never[]\"\n const schemaErrors = Object.assign(\n [] as typeof typeschemaErrors,\n typeschemaErrors,\n );\n const errors: Record<string, FieldError> = {};\n\n for (const error of schemaErrors) {\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\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","_step","_iterator","_createForOfIteratorHelperLoose","Object","assign","done","error","value","path","_path","join","message","type","types","messages","appendErrors","concat","parseErrorSchema","shouldUseNativeValidation","criteriaMode","toNestErrors","validateFieldsNatively","raw","validate","_temp","Promise","resolve","then","_result","e","reject"],"mappings":"mMAmFgB,SACdA,EACAC,EACAC,GAIA,gBAJAA,IAAAA,EAEI,CAAA,GAEJ,SAAcC,EAAQC,EAAGC,OAAWC,IAAAA,aAMlC,GAAIC,EAAOC,OAAQ,CACjB,IAAMC,EAvFa,SACvBC,EACAC,GASA,IANA,IAMgCC,EAF1BH,EAAqC,CAAA,EAE3CI,2pBAAAC,CANqBC,OAAOC,OAC1B,GACAN,MAI8BE,EAAAC,KAAAI,MAAE,CAAA,IAAvBC,EAAKN,EAAAO,MACd,GAAKD,EAAME,KAAX,CAGA,IAAMC,EAAQH,EAAME,KAAKE,KAAK,KAM9B,GAJKb,EAAOY,KACVZ,EAAOY,GAAS,CAAEE,QAASL,EAAMK,QAASC,KAAM,KAG9Cb,EAA0B,CAC5B,IAAMc,EAAQhB,EAAOY,GAAOI,MACtBC,EAAWD,GAASA,EAAM,IAEhChB,EAAOY,GAASM,eACdN,EACAV,EACAF,EACA,GACAiB,EACK,GAAgBE,OAAOF,EAAsBR,EAAMK,SACpDL,EAAMK,QAEd,CApBA,CAqBF,CAEA,OAAOd,CACT,CAiDqBoB,CACbtB,EAAOC,QACNH,EAAQyB,2BAAsD,QAAzBzB,EAAQ0B,cAGhD,MAAO,CACL5B,OAAQ,CAAA,EACRM,OAAQuB,eAAavB,EAAQJ,GAEjC,CAIA,OAFAA,EAAQyB,2BAA6BG,yBAAuB,CAAA,EAAI5B,GAEzD,CACLF,OAAQD,EAAgBgC,IAAMnB,OAAOC,OAAO,CAAA,EAAIb,GAAUI,EAAOY,MACjEV,OAAQ,CAAA,EACR,EAtBEF,EAASP,EAAO,aAAamC,SAAShC,GAAQiC,EAC9C7B,WAAAA,GAAAA,aAAkB8B,QAAOA,OAAAA,QAAAC,QACZ/B,GAAMgC,cAAAC,GAArBjC,EAAMiC,CAAgB,GADpBjC,UACoB8B,QAAAC,QAAAF,GAAAA,EAAAG,KAAAH,EAAAG,KAAAjC,GAAAA,IAqB1B,CAAC,MAAAmC,GAAAJ,OAAAA,QAAAK,OAAAD,EACH,CAAA,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{toNestErrors as
|
|
1
|
+
import{toNestErrors as r,validateFieldsNatively as e}from"@hookform/resolvers";import{appendErrors as t}from"react-hook-form";function n(r,e){(null==e||e>r.length)&&(e=r.length);for(var t=0,n=Array(e);t<e;t++)n[t]=r[t];return n}function o(o,a,i){return void 0===i&&(i={}),function(a,s,u){try{var l=function(){if(c.issues){var o=function(r,e){for(var o,a={},i=function(r){var e="undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"];if(e)return(e=e.call(r)).next.bind(e);if(Array.isArray(r)||(e=function(r,e){if(r){if("string"==typeof r)return n(r,e);var t={}.toString.call(r).slice(8,-1);return"Object"===t&&r.constructor&&(t=r.constructor.name),"Map"===t||"Set"===t?Array.from(r):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?n(r,e):void 0}}(r))){e&&(r=e);var t=0;return function(){return t>=r.length?{done:!0}:{done:!1,value:r[t++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}(Object.assign([],r));!(o=i()).done;){var s=o.value;if(s.path){var u=s.path.join(".");if(a[u]||(a[u]={message:s.message,type:""}),e){var l=a[u].types,c=l&&l[""];a[u]=t(u,e,a,"",c?[].concat(c,s.message):s.message)}}}return a}(c.issues,!u.shouldUseNativeValidation&&"all"===u.criteriaMode);return{values:{},errors:r(o,u)}}return u.shouldUseNativeValidation&&e({},u),{values:i.raw?Object.assign({},a):c.value,errors:{}}},c=o["~standard"].validate(a),f=function(){if(c instanceof Promise)return Promise.resolve(c).then(function(r){c=r})}();return Promise.resolve(f&&f.then?f.then(l):l())}catch(r){return Promise.reject(r)}}}export{o as typeschemaResolver};
|
|
2
2
|
//# sourceMappingURL=typeschema.module.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{toNestErrors as e,validateFieldsNatively as s}from"@hookform/resolvers";import{appendErrors as
|
|
1
|
+
import{toNestErrors as e,validateFieldsNatively as s}from"@hookform/resolvers";import{appendErrors as o}from"react-hook-form";function t(t,a,r={}){return async(a,i,n)=>{let c=t["~standard"].validate(a);if(c instanceof Promise&&(c=await c),c.issues){const s=((e,s)=>{const t=Object.assign([],e),a={};for(const e of t){if(!e.path)continue;const t=e.path.join(".");if(a[t]||(a[t]={message:e.message,type:""}),s){const r=a[t].types,i=r&&r[""];a[t]=o(t,s,a,"",i?[].concat(i,e.message):e.message)}}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{t as typeschemaResolver};
|
|
2
2
|
//# sourceMappingURL=typeschema.modern.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeschema.modern.mjs","sources":["../src/typeschema.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n FieldError,\n FieldErrors,\n FieldValues,\n Resolver,\n appendErrors,\n} from 'react-hook-form';\n\nconst parseErrorSchema = (\n typeschemaErrors: readonly StandardSchemaV1.Issue[],\n validateAllFieldCriteria: boolean,\n): FieldErrors => {\n const schemaErrors = Object.assign([]
|
|
1
|
+
{"version":3,"file":"typeschema.modern.mjs","sources":["../src/typeschema.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { StandardSchemaV1 } from '@standard-schema/spec';\nimport {\n FieldError,\n FieldErrors,\n FieldValues,\n Resolver,\n appendErrors,\n} from 'react-hook-form';\n\nconst parseErrorSchema = (\n typeschemaErrors: readonly StandardSchemaV1.Issue[],\n validateAllFieldCriteria: boolean,\n): FieldErrors => {\n // Need to pass generics so that the array isn't typed as \"never[]\"\n const schemaErrors = Object.assign(\n [] as typeof typeschemaErrors,\n typeschemaErrors,\n );\n const errors: Record<string, FieldError> = {};\n\n for (const error of schemaErrors) {\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\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":["typeschemaResolver","schema","_schemaOptions","resolverOptions","values","_","options","result","validate","Promise","issues","errors","parseErrorSchema","typeschemaErrors","validateAllFieldCriteria","schemaErrors","Object","assign","error","path","_path","join","message","type","types","messages","appendErrors","concat","shouldUseNativeValidation","criteriaMode","toNestErrors","validateFieldsNatively","raw","value"],"mappings":"uIAmFgBA,EACdC,EACAC,EACAC,EAEI,CAAA,GAEJ,OAAcC,MAAAA,EAAQC,EAAGC,KACvB,IAAIC,EAASN,EAAO,aAAaO,SAASJ,GAK1C,GAJIG,aAAkBE,UACpBF,QAAeA,GAGbA,EAAOG,OAAQ,CACjB,MAAMC,EAvFaC,EACvBC,EACAC,KAGA,MAAMC,EAAeC,OAAOC,OAC1B,GACAJ,GAEIF,EAAqC,CAAA,EAE3C,IAAK,MAAMO,KAASH,EAAc,CAChC,IAAKG,EAAMC,KACT,SAEF,MAAMC,EAAQF,EAAMC,KAAKE,KAAK,KAM9B,GAJKV,EAAOS,KACVT,EAAOS,GAAS,CAAEE,QAASJ,EAAMI,QAASC,KAAM,KAG9CT,EAA0B,CAC5B,MAAMU,EAAQb,EAAOS,GAAOI,MACtBC,EAAWD,GAASA,EAAM,IAEhCb,EAAOS,GAASM,EACdN,EACAN,EACAH,EACA,GACAc,EACK,GAAgBE,OAAOF,EAAsBP,EAAMI,SACpDJ,EAAMI,QAEd,CACF,CAEA,OAAOX,GAkDYC,CACbL,EAAOG,QACNJ,EAAQsB,2BAAsD,QAAzBtB,EAAQuB,cAGhD,MAAO,CACLzB,OAAQ,CAAA,EACRO,OAAQmB,EAAanB,EAAQL,GAEjC,CAIA,OAFAA,EAAQsB,2BAA6BG,EAAuB,CAAE,EAAEzB,GAEzD,CACLF,OAAQD,EAAgB6B,IAAMhB,OAAOC,OAAO,CAAE,EAAEb,GAAUG,EAAO0B,MACjEtB,OAAQ,CAAA,GAGd"}
|