@hookform/resolvers 5.9.1 → 6.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.
@@ -1,7 +1,6 @@
1
1
  import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';
2
- import { Effect, Schema } from 'effect';
3
- import { ArrayFormatter, decodeUnknown } from 'effect/ParseResult';
4
- import { ParseOptions } from 'effect/SchemaAST';
2
+ import { Effect, Schema, SchemaIssue } from 'effect';
3
+ import type { ParseOptions } from 'effect/SchemaAST';
5
4
  import {
6
5
  type FieldError,
7
6
  FieldValues,
@@ -9,8 +8,37 @@ import {
9
8
  appendErrors,
10
9
  } from 'react-hook-form';
11
10
 
11
+ type Issue = { path: PropertyKey[]; message: string; type: string };
12
+
13
+ const formatIssue = SchemaIssue.makeFormatterStandardSchemaV1();
14
+
15
+ // Flattens the issue tree like `makeFormatterStandardSchemaV1` does, but keeps
16
+ // the `_tag` of the failing node so it can be reported as the error `type`.
17
+ const flattenIssue = (
18
+ issue: SchemaIssue.Issue,
19
+ path: PropertyKey[],
20
+ ): Issue[] => {
21
+ switch (issue._tag) {
22
+ case 'Pointer':
23
+ return flattenIssue(issue.issue, [...path, ...issue.path]);
24
+ case 'Encoding':
25
+ return flattenIssue(issue.issue, path);
26
+ case 'Composite':
27
+ case 'AnyOf':
28
+ if (issue.issues.length) {
29
+ return issue.issues.flatMap((issue) => flattenIssue(issue, path));
30
+ }
31
+ }
32
+
33
+ return formatIssue(issue).issues.map((error) => ({
34
+ path: [...path, ...(error.path || [])] as PropertyKey[],
35
+ message: error.message,
36
+ type: issue._tag,
37
+ }));
38
+ };
39
+
12
40
  export function effectTsResolver<Input extends FieldValues, Context, Output>(
13
- schema: Schema.Schema<Output, Input>,
41
+ schema: Schema.Codec<Output, Input, never, unknown>,
14
42
  schemaOptions?: ParseOptions,
15
43
  resolverOptions?: {
16
44
  mode?: 'async' | 'sync';
@@ -19,7 +47,7 @@ export function effectTsResolver<Input extends FieldValues, Context, Output>(
19
47
  ): Resolver<Input, Context, Output>;
20
48
 
21
49
  export function effectTsResolver<Input extends FieldValues, Context, Output>(
22
- schema: Schema.Schema<Output, Input>,
50
+ schema: Schema.Codec<Output, Input, never, unknown>,
23
51
  schemaOptions: ParseOptions | undefined,
24
52
  resolverOptions: {
25
53
  mode?: 'async' | 'sync';
@@ -28,9 +56,9 @@ export function effectTsResolver<Input extends FieldValues, Context, Output>(
28
56
  ): Resolver<Input, Context, Input>;
29
57
 
30
58
  /**
31
- * Creates a resolver for react-hook-form using Effect.ts schema validation
32
- * @param {Schema.Schema<TFieldValues, I>} schema - The Effect.ts schema to validate against
33
- * @param {ParseOptions} [schemaOptions] - Optional Effect.ts validation options
59
+ * Creates a resolver for react-hook-form using Effect Schema validation
60
+ * @param {Schema.Codec<Output, Input>} schema - The Effect schema to validate against
61
+ * @param {ParseOptions} [schemaOptions] - Optional Effect Schema parse options
34
62
  * @returns {Resolver<Schema.Schema.Type<typeof schema>>} A resolver function compatible with react-hook-form
35
63
  * @example
36
64
  * const schema = Schema.Struct({
@@ -43,67 +71,61 @@ export function effectTsResolver<Input extends FieldValues, Context, Output>(
43
71
  * });
44
72
  */
45
73
  export function effectTsResolver<Input extends FieldValues, Context, Output>(
46
- schema: Schema.Schema<Output, Input>,
74
+ schema: Schema.Codec<Output, Input, never, unknown>,
47
75
  schemaOptions: ParseOptions = { errors: 'all', onExcessProperty: 'ignore' },
48
76
  ): Resolver<Input, Context, Output | Input> {
49
- return (values, _, options) => {
50
- return decodeUnknown(
51
- schema,
52
- schemaOptions,
53
- )(values).pipe(
54
- Effect.catchAll((parseIssue) =>
55
- Effect.flip(ArrayFormatter.formatIssue(parseIssue)),
56
- ),
57
- Effect.mapError((issues) => {
58
- const validateAllFieldCriteria =
59
- !options.shouldUseNativeValidation && options.criteriaMode === 'all';
77
+ return (values, _, options) =>
78
+ Schema.decodeUnknownEffect(schema)(values, schemaOptions).pipe(
79
+ Effect.match({
80
+ onFailure: ({ issue }) => {
81
+ const validateAllFieldCriteria =
82
+ !options.shouldUseNativeValidation &&
83
+ options.criteriaMode === 'all';
60
84
 
61
- // A null-prototype accumulator avoids false-positive hits from
62
- // inherited members (e.g. `toString`, `constructor`) when a field
63
- // is named after one of them — a plain `{}` would make `!acc[key]`
64
- // truthy-skip those paths and silently drop their errors.
65
- const errors = issues.reduce(
66
- (acc, error) => {
67
- const key = error.path.join('.');
85
+ // A null-prototype accumulator avoids false-positive hits from
86
+ // inherited members (e.g. `toString`, `constructor`) when a field
87
+ // is named after one of them — a plain `{}` would make `!acc[key]`
88
+ // truthy-skip those paths and silently drop their errors.
89
+ const errors = flattenIssue(issue, []).reduce(
90
+ (acc, error) => {
91
+ const key = error.path.join('.');
68
92
 
69
- if (!acc[key]) {
70
- acc[key] = { message: error.message, type: error._tag };
71
- }
93
+ if (!acc[key]) {
94
+ acc[key] = { message: error.message, type: error.type };
95
+ }
72
96
 
73
- if (validateAllFieldCriteria) {
74
- const types = acc[key].types;
75
- const messages = types && types[String(error._tag)];
97
+ if (validateAllFieldCriteria) {
98
+ const types = acc[key].types;
99
+ const messages = types && types[error.type];
76
100
 
77
- acc[key] = appendErrors(
78
- key,
79
- validateAllFieldCriteria,
80
- acc,
81
- error._tag,
82
- messages
83
- ? ([] as string[]).concat(messages as string[], error.message)
84
- : error.message,
85
- ) as FieldError;
86
- }
101
+ acc[key] = appendErrors(
102
+ key,
103
+ validateAllFieldCriteria,
104
+ acc,
105
+ error.type,
106
+ messages
107
+ ? ([] as string[]).concat(
108
+ messages as string[],
109
+ error.message,
110
+ )
111
+ : error.message,
112
+ ) as FieldError;
113
+ }
87
114
 
88
- return acc;
89
- },
90
- Object.create(null) as Record<string, FieldError>,
91
- );
115
+ return acc;
116
+ },
117
+ Object.create(null) as Record<string, FieldError>,
118
+ );
92
119
 
93
- return toNestErrors(errors, options);
94
- }),
95
- Effect.tap(() =>
96
- Effect.sync(
97
- () =>
98
- options.shouldUseNativeValidation &&
99
- validateFieldsNatively({}, options),
100
- ),
101
- ),
102
- Effect.match({
103
- onFailure: (errors) => ({ errors, values: {} }),
104
- onSuccess: (result) => ({ errors: {}, values: result }),
120
+ return { errors: toNestErrors(errors, options), values: {} };
121
+ },
122
+ onSuccess: (result) => {
123
+ options.shouldUseNativeValidation &&
124
+ validateFieldsNatively({}, options);
125
+
126
+ return { errors: {}, values: result };
127
+ },
105
128
  }),
106
129
  Effect.runPromise,
107
130
  );
108
- };
109
131
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hookform/resolvers",
3
3
  "amdName": "hookformResolvers",
4
- "version": "5.9.1",
4
+ "version": "6.0.0",
5
5
  "description": "React Hook Form validation resolvers: Yup, Joi, Superstruct, Zod, Vest, Class Validator, io-ts, Nope, computed-types, TypeBox, arktype, Typanion, Effect-TS and VineJS",
6
6
  "main": "dist/resolvers.js",
7
7
  "module": "dist/resolvers.module.js",
@@ -226,7 +226,7 @@
226
226
  "build:arktype": "microbundle --cwd arktype --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm",
227
227
  "build:valibot": "microbundle --cwd valibot --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm",
228
228
  "build:typeschema": "microbundle --cwd typeschema --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm,@typeschema/main=main",
229
- "build:effect-ts": "microbundle --cwd effect-ts --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm,effect=Effect,effect/SchemaAST=EffectSchemaAST,effect/ParseResult=EffectParseResult",
229
+ "build:effect-ts": "microbundle --cwd effect-ts --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm,effect=Effect,effect/SchemaAST=EffectSchemaAST",
230
230
  "build:vine": "microbundle --cwd vine --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm,@vinejs/vine=vine",
231
231
  "build:fluentvalidation-ts": "microbundle --cwd fluentvalidation-ts --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm",
232
232
  "build:standard-schema": "microbundle --cwd standard-schema --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm,@standard-schema/spec=standardSchema",
@@ -291,16 +291,16 @@
291
291
  "@vinejs/vine": "^4.4.0",
292
292
  "@vitejs/plugin-react": "^4.7.0",
293
293
  "ajv": "^8.20.0",
294
- "ata-validator": "^1.2.0",
295
294
  "ajv-errors": "^3.0.0",
296
295
  "ajv-formats": "^2.1.1",
297
296
  "arktype": "2.0.4",
297
+ "ata-validator": "^1.23.0",
298
298
  "check-export-map": "^1.3.1",
299
299
  "class-transformer": "^0.5.1",
300
300
  "class-validator": "^0.14.4",
301
301
  "computed-types": "^1.11.2",
302
302
  "cross-env": "^7.0.3",
303
- "effect": "^3.22.0",
303
+ "effect": "^4.0.5",
304
304
  "fluentvalidation-ts": "^3.3.0",
305
305
  "fp-ts": "^2.16.11",
306
306
  "io-ts": "^2.2.22",
@@ -337,11 +337,11 @@
337
337
  "ajv-errors": "^3.0.0",
338
338
  "ajv-formats": "^2.1.1",
339
339
  "arktype": "^2.0.0",
340
- "ata-validator": "^1.2.0",
340
+ "ata-validator": "^1.23.0",
341
341
  "class-transformer": ">=0.4.0",
342
342
  "class-validator": ">=0.12.0",
343
343
  "computed-types": "^1.0.0",
344
- "effect": "^3.10.3",
344
+ "effect": "^4.0.0",
345
345
  "fluentvalidation-ts": "^3.0.0",
346
346
  "fp-ts": "^2.7.0",
347
347
  "io-ts": "^2.0.0",