@hookform/resolvers 5.9.2 → 6.0.1
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 +6 -6
- package/effect-ts/dist/effect-ts.d.ts +3 -3
- package/effect-ts/dist/effect-ts.js +1 -1
- package/effect-ts/dist/effect-ts.js.map +1 -1
- package/effect-ts/dist/effect-ts.mjs +1 -1
- package/effect-ts/dist/effect-ts.modern.mjs +1 -1
- package/effect-ts/dist/effect-ts.modern.mjs.map +1 -1
- package/effect-ts/dist/effect-ts.module.js +1 -1
- package/effect-ts/dist/effect-ts.module.js.map +1 -1
- package/effect-ts/dist/effect-ts.umd.js +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__/Form-native-validation.tsx +4 -4
- package/effect-ts/src/__tests__/Form.tsx +4 -4
- package/effect-ts/src/__tests__/__fixtures__/data.ts +24 -24
- package/effect-ts/src/__tests__/__snapshots__/effect-ts.ts.snap +10 -11
- package/effect-ts/src/__tests__/effect-ts.ts +196 -15
- package/effect-ts/src/__tests__/special-path-names.ts +1 -1
- package/effect-ts/src/effect-ts.ts +82 -60
- package/package.json +5 -9
- package/typeschema/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<p align="center">
|
|
3
3
|
<a href="https://react-hook-form.com" title="React Hook Form - Simple React forms validation">
|
|
4
|
-
<img src="https://
|
|
4
|
+
<img src="https://react-hook-form.com/images/react-hook-form-og.png" alt="React Hook Form Logo - React hook custom hook for form validation" />
|
|
5
5
|
</a>
|
|
6
6
|
</p>
|
|
7
7
|
</div>
|
|
@@ -782,7 +782,7 @@ const App = () => {
|
|
|
782
782
|
|
|
783
783
|
### [effect-ts](https://github.com/Effect-TS/effect)
|
|
784
784
|
|
|
785
|
-
A powerful TypeScript framework that provides a fully-fledged functional effect system with a rich standard library.
|
|
785
|
+
A powerful TypeScript framework that provides a fully-fledged functional effect system with a rich standard library. Requires `effect` v4 (use `@hookform/resolvers` v5 for `effect` v3).
|
|
786
786
|
|
|
787
787
|
[](https://bundlephobia.com/result?p=effect)
|
|
788
788
|
|
|
@@ -793,11 +793,11 @@ import { effectTsResolver } from '@hookform/resolvers/effect-ts';
|
|
|
793
793
|
import { Schema } from 'effect';
|
|
794
794
|
|
|
795
795
|
const schema = Schema.Struct({
|
|
796
|
-
username: Schema.String.
|
|
797
|
-
Schema.
|
|
796
|
+
username: Schema.String.check(
|
|
797
|
+
Schema.isNonEmpty({ message: 'username required' }),
|
|
798
798
|
),
|
|
799
|
-
password: Schema.String.
|
|
800
|
-
Schema.
|
|
799
|
+
password: Schema.String.check(
|
|
800
|
+
Schema.isNonEmpty({ message: 'password required' }),
|
|
801
801
|
),
|
|
802
802
|
});
|
|
803
803
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Schema } from 'effect';
|
|
2
|
-
import { ParseOptions } from 'effect/SchemaAST';
|
|
2
|
+
import type { ParseOptions } from 'effect/SchemaAST';
|
|
3
3
|
import { FieldValues, Resolver } from 'react-hook-form';
|
|
4
|
-
export declare function effectTsResolver<Input extends FieldValues, Context, Output>(schema: Schema.
|
|
4
|
+
export declare function effectTsResolver<Input extends FieldValues, Context, Output>(schema: Schema.Codec<Output, Input, never, unknown>, schemaOptions?: ParseOptions, resolverOptions?: {
|
|
5
5
|
mode?: 'async' | 'sync';
|
|
6
6
|
raw?: false;
|
|
7
7
|
}): Resolver<Input, Context, Output>;
|
|
8
|
-
export declare function effectTsResolver<Input extends FieldValues, Context, Output>(schema: Schema.
|
|
8
|
+
export declare function effectTsResolver<Input extends FieldValues, Context, Output>(schema: Schema.Codec<Output, Input, never, unknown>, schemaOptions: ParseOptions | undefined, resolverOptions: {
|
|
9
9
|
mode?: 'async' | 'sync';
|
|
10
10
|
raw: true;
|
|
11
11
|
}): Resolver<Input, Context, Input>;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=require("@hookform/resolvers"),r=require("effect"),t=require("
|
|
1
|
+
var e=require("@hookform/resolvers"),r=require("effect"),t=require("react-hook-form"),s=r.SchemaIssue.makeFormatterStandardSchemaV1(),a=function(e,r){switch(e._tag){case"Pointer":return a(e.issue,[].concat(r,e.path));case"Encoding":return a(e.issue,r);case"Composite":case"AnyOf":if(e.issues.length)return e.issues.flatMap(function(e){return a(e,r)})}return s(e).issues.map(function(t){return{path:[].concat(r,t.path||[]),message:t.message,type:e._tag}})};exports.effectTsResolver=function(s,n){return void 0===n&&(n={errors:"all",onExcessProperty:"ignore"}),function(o,i,c){return r.Schema.decodeUnknownEffect(s)(o,n).pipe(r.Effect.match({onFailure:function(r){var s=!c.shouldUseNativeValidation&&"all"===c.criteriaMode,n=a(r.issue,[]).reduce(function(e,r){var a=r.path.join(".");if(e[a]||(e[a]={message:r.message,type:r.type}),s){var n=e[a].types,o=n&&n[r.type];e[a]=t.appendErrors(a,s,e,r.type,o?[].concat(o,r.message):r.message)}return e},Object.create(null));return{errors:e.toNestErrors(n,c),values:{}}},onSuccess:function(r){return c.shouldUseNativeValidation&&e.validateFieldsNatively({},c),{errors:{},values:r}}}),r.Effect.runPromise)}};
|
|
2
2
|
//# sourceMappingURL=effect-ts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effect-ts.js","sources":["../src/effect-ts.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Effect, Schema
|
|
1
|
+
{"version":3,"file":"effect-ts.js","sources":["../src/effect-ts.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Effect, Schema, SchemaIssue } from 'effect';\nimport type { ParseOptions } from 'effect/SchemaAST';\nimport {\n type FieldError,\n FieldValues,\n Resolver,\n appendErrors,\n} from 'react-hook-form';\n\ntype Issue = { path: PropertyKey[]; message: string; type: string };\n\nconst formatIssue = SchemaIssue.makeFormatterStandardSchemaV1();\n\n// Flattens the issue tree like `makeFormatterStandardSchemaV1` does, but keeps\n// the `_tag` of the failing node so it can be reported as the error `type`.\nconst flattenIssue = (\n issue: SchemaIssue.Issue,\n path: PropertyKey[],\n): Issue[] => {\n switch (issue._tag) {\n case 'Pointer':\n return flattenIssue(issue.issue, [...path, ...issue.path]);\n case 'Encoding':\n return flattenIssue(issue.issue, path);\n case 'Composite':\n case 'AnyOf':\n if (issue.issues.length) {\n return issue.issues.flatMap((issue) => flattenIssue(issue, path));\n }\n }\n\n return formatIssue(issue).issues.map((error) => ({\n path: [...path, ...(error.path || [])] as PropertyKey[],\n message: error.message,\n type: issue._tag,\n }));\n};\n\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions?: ParseOptions,\n resolverOptions?: {\n mode?: 'async' | 'sync';\n raw?: false;\n },\n): Resolver<Input, Context, Output>;\n\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions: ParseOptions | 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 Effect Schema validation\n * @param {Schema.Codec<Output, Input>} schema - The Effect schema to validate against\n * @param {ParseOptions} [schemaOptions] - Optional Effect Schema parse options\n * @returns {Resolver<Schema.Schema.Type<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = Schema.Struct({\n * name: Schema.String,\n * age: Schema.Number\n * });\n *\n * useForm({\n * resolver: effectTsResolver(schema)\n * });\n */\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions: ParseOptions = { errors: 'all', onExcessProperty: 'ignore' },\n): Resolver<Input, Context, Output | Input> {\n return (values, _, options) =>\n Schema.decodeUnknownEffect(schema)(values, schemaOptions).pipe(\n Effect.match({\n onFailure: ({ issue }) => {\n const validateAllFieldCriteria =\n !options.shouldUseNativeValidation &&\n options.criteriaMode === 'all';\n\n // A null-prototype accumulator avoids false-positive hits from\n // inherited members (e.g. `toString`, `constructor`) when a field\n // is named after one of them — a plain `{}` would make `!acc[key]`\n // truthy-skip those paths and silently drop their errors.\n const errors = flattenIssue(issue, []).reduce(\n (acc, error) => {\n const key = error.path.join('.');\n\n if (!acc[key]) {\n acc[key] = { message: error.message, type: error.type };\n }\n\n if (validateAllFieldCriteria) {\n const types = acc[key].types;\n const messages = types && types[error.type];\n\n acc[key] = appendErrors(\n key,\n validateAllFieldCriteria,\n acc,\n error.type,\n messages\n ? ([] as string[]).concat(\n messages as string[],\n error.message,\n )\n : error.message,\n ) as FieldError;\n }\n\n return acc;\n },\n Object.create(null) as Record<string, FieldError>,\n );\n\n return { errors: toNestErrors(errors, options), values: {} };\n },\n onSuccess: (result) => {\n options.shouldUseNativeValidation &&\n validateFieldsNatively({}, options);\n\n return { errors: {}, values: result };\n },\n }),\n Effect.runPromise,\n );\n}\n"],"names":["formatIssue","SchemaIssue","makeFormatterStandardSchemaV1","flattenIssue","issue","path","_tag","concat","issues","length","flatMap","map","error","message","type","schema","schemaOptions","errors","onExcessProperty","values","_","options","Schema","decodeUnknownEffect","pipe","Effect","match","onFailure","_ref","validateAllFieldCriteria","shouldUseNativeValidation","criteriaMode","reduce","acc","key","join","types","messages","appendErrors","Object","create","toNestErrors","onSuccess","result","validateFieldsNatively","runPromise"],"mappings":"sFAYMA,EAAcC,EAAWA,YAACC,gCAI1BC,EAAe,SACnBC,EACAC,GAEA,OAAQD,EAAME,MACZ,IAAK,UACH,OAAOH,EAAaC,EAAMA,MAAK,GAAAG,OAAMF,EAASD,EAAMC,OACtD,IAAK,WACH,OAAOF,EAAaC,EAAMA,MAAOC,GACnC,IAAK,YACL,IAAK,QACH,GAAID,EAAMI,OAAOC,OACf,OAAOL,EAAMI,OAAOE,QAAQ,SAACN,GAAK,OAAKD,EAAaC,EAAOC,EAAK,GAItE,OAAOL,EAAYI,GAAOI,OAAOG,IAAI,SAACC,GAAW,MAAA,CAC/CP,KAAI,GAAAE,OAAMF,EAAUO,EAAMP,MAAQ,IAClCQ,QAASD,EAAMC,QACfC,KAAMV,EAAME,KACb,EACH,2BAmCgB,SACdS,EACAC,GAEA,YAF8B,IAA9BA,IAAAA,EAA8B,CAAEC,OAAQ,MAAOC,iBAAkB,WAE1D,SAACC,EAAQC,EAAGC,GACjB,OAAAC,EAAMA,OAACC,oBAAoBR,EAA3BO,CAAmCH,EAAQH,GAAeQ,KACxDC,EAAAA,OAAOC,MAAM,CACXC,UAAW,SAAFC,GAAgB,IACjBC,GACHR,EAAQS,2BACgB,QAAzBT,EAAQU,aAMJd,EAASd,EATEyB,EAALxB,MASuB,IAAI4B,OACrC,SAACC,EAAKrB,GACJ,IAAMsB,EAAMtB,EAAMP,KAAK8B,KAAK,KAM5B,GAJKF,EAAIC,KACPD,EAAIC,GAAO,CAAErB,QAASD,EAAMC,QAASC,KAAMF,EAAME,OAG/Ce,EAA0B,CAC5B,IAAMO,EAAQH,EAAIC,GAAKE,MACjBC,EAAWD,GAASA,EAAMxB,EAAME,MAEtCmB,EAAIC,GAAOI,eACTJ,EACAL,EACAI,EACArB,EAAME,KACNuB,EACK,GAAgB9B,OACf8B,EACAzB,EAAMC,SAERD,EAAMC,QAEd,CAEA,OAAOoB,CACT,EACAM,OAAOC,OAAO,OAGhB,MAAO,CAAEvB,OAAQwB,EAAYA,aAACxB,EAAQI,GAAUF,OAAQ,GAC1D,EACAuB,UAAW,SAACC,GAIV,OAHAtB,EAAQS,2BACNc,EAAsBA,uBAAC,GAAIvB,GAEtB,CAAEJ,OAAQ,CAAE,EAAEE,OAAQwB,EAC/B,IAEFlB,SAAOoB,WACR,CACL"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{toNestErrors as
|
|
1
|
+
import{toNestErrors as e,validateFieldsNatively as r}from"@hookform/resolvers";import{SchemaIssue as t,Schema as s,Effect as n}from"effect";import{appendErrors as o}from"react-hook-form";var a=t.makeFormatterStandardSchemaV1(),i=function(e,r){switch(e._tag){case"Pointer":return i(e.issue,[].concat(r,e.path));case"Encoding":return i(e.issue,r);case"Composite":case"AnyOf":if(e.issues.length)return e.issues.flatMap(function(e){return i(e,r)})}return a(e).issues.map(function(t){return{path:[].concat(r,t.path||[]),message:t.message,type:e._tag}})};function u(t,a){return void 0===a&&(a={errors:"all",onExcessProperty:"ignore"}),function(u,c,f){return s.decodeUnknownEffect(t)(u,a).pipe(n.match({onFailure:function(r){var t=!f.shouldUseNativeValidation&&"all"===f.criteriaMode,s=i(r.issue,[]).reduce(function(e,r){var s=r.path.join(".");if(e[s]||(e[s]={message:r.message,type:r.type}),t){var n=e[s].types,a=n&&n[r.type];e[s]=o(s,t,e,r.type,a?[].concat(a,r.message):r.message)}return e},Object.create(null));return{errors:e(s,f),values:{}}},onSuccess:function(e){return f.shouldUseNativeValidation&&r({},f),{errors:{},values:e}}}),n.runPromise)}}export{u as effectTsResolver};
|
|
2
2
|
//# sourceMappingURL=effect-ts.module.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{toNestErrors as e,validateFieldsNatively as
|
|
1
|
+
import{toNestErrors as e,validateFieldsNatively as s}from"@hookform/resolvers";import{SchemaIssue as r,Schema as t,Effect as o}from"effect";import{appendErrors as a}from"react-hook-form";const n=r.makeFormatterStandardSchemaV1(),i=(e,s)=>{switch(e._tag){case"Pointer":return i(e.issue,[...s,...e.path]);case"Encoding":return i(e.issue,s);case"Composite":case"AnyOf":if(e.issues.length)return e.issues.flatMap(e=>i(e,s))}return n(e).issues.map(r=>({path:[...s,...r.path||[]],message:r.message,type:e._tag}))};function c(r,n={errors:"all",onExcessProperty:"ignore"}){return(c,u,m)=>t.decodeUnknownEffect(r)(c,n).pipe(o.match({onFailure:({issue:s})=>{const r=!m.shouldUseNativeValidation&&"all"===m.criteriaMode,t=i(s,[]).reduce((e,s)=>{const t=s.path.join(".");if(e[t]||(e[t]={message:s.message,type:s.type}),r){const o=e[t].types,n=o&&o[s.type];e[t]=a(t,r,e,s.type,n?[].concat(n,s.message):s.message)}return e},Object.create(null));return{errors:e(t,m),values:{}}},onSuccess:e=>(m.shouldUseNativeValidation&&s({},m),{errors:{},values:e})}),o.runPromise)}export{c as effectTsResolver};
|
|
2
2
|
//# sourceMappingURL=effect-ts.modern.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effect-ts.modern.mjs","sources":["../src/effect-ts.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Effect, Schema
|
|
1
|
+
{"version":3,"file":"effect-ts.modern.mjs","sources":["../src/effect-ts.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Effect, Schema, SchemaIssue } from 'effect';\nimport type { ParseOptions } from 'effect/SchemaAST';\nimport {\n type FieldError,\n FieldValues,\n Resolver,\n appendErrors,\n} from 'react-hook-form';\n\ntype Issue = { path: PropertyKey[]; message: string; type: string };\n\nconst formatIssue = SchemaIssue.makeFormatterStandardSchemaV1();\n\n// Flattens the issue tree like `makeFormatterStandardSchemaV1` does, but keeps\n// the `_tag` of the failing node so it can be reported as the error `type`.\nconst flattenIssue = (\n issue: SchemaIssue.Issue,\n path: PropertyKey[],\n): Issue[] => {\n switch (issue._tag) {\n case 'Pointer':\n return flattenIssue(issue.issue, [...path, ...issue.path]);\n case 'Encoding':\n return flattenIssue(issue.issue, path);\n case 'Composite':\n case 'AnyOf':\n if (issue.issues.length) {\n return issue.issues.flatMap((issue) => flattenIssue(issue, path));\n }\n }\n\n return formatIssue(issue).issues.map((error) => ({\n path: [...path, ...(error.path || [])] as PropertyKey[],\n message: error.message,\n type: issue._tag,\n }));\n};\n\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions?: ParseOptions,\n resolverOptions?: {\n mode?: 'async' | 'sync';\n raw?: false;\n },\n): Resolver<Input, Context, Output>;\n\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions: ParseOptions | 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 Effect Schema validation\n * @param {Schema.Codec<Output, Input>} schema - The Effect schema to validate against\n * @param {ParseOptions} [schemaOptions] - Optional Effect Schema parse options\n * @returns {Resolver<Schema.Schema.Type<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = Schema.Struct({\n * name: Schema.String,\n * age: Schema.Number\n * });\n *\n * useForm({\n * resolver: effectTsResolver(schema)\n * });\n */\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions: ParseOptions = { errors: 'all', onExcessProperty: 'ignore' },\n): Resolver<Input, Context, Output | Input> {\n return (values, _, options) =>\n Schema.decodeUnknownEffect(schema)(values, schemaOptions).pipe(\n Effect.match({\n onFailure: ({ issue }) => {\n const validateAllFieldCriteria =\n !options.shouldUseNativeValidation &&\n options.criteriaMode === 'all';\n\n // A null-prototype accumulator avoids false-positive hits from\n // inherited members (e.g. `toString`, `constructor`) when a field\n // is named after one of them — a plain `{}` would make `!acc[key]`\n // truthy-skip those paths and silently drop their errors.\n const errors = flattenIssue(issue, []).reduce(\n (acc, error) => {\n const key = error.path.join('.');\n\n if (!acc[key]) {\n acc[key] = { message: error.message, type: error.type };\n }\n\n if (validateAllFieldCriteria) {\n const types = acc[key].types;\n const messages = types && types[error.type];\n\n acc[key] = appendErrors(\n key,\n validateAllFieldCriteria,\n acc,\n error.type,\n messages\n ? ([] as string[]).concat(\n messages as string[],\n error.message,\n )\n : error.message,\n ) as FieldError;\n }\n\n return acc;\n },\n Object.create(null) as Record<string, FieldError>,\n );\n\n return { errors: toNestErrors(errors, options), values: {} };\n },\n onSuccess: (result) => {\n options.shouldUseNativeValidation &&\n validateFieldsNatively({}, options);\n\n return { errors: {}, values: result };\n },\n }),\n Effect.runPromise,\n );\n}\n"],"names":["formatIssue","SchemaIssue","makeFormatterStandardSchemaV1","flattenIssue","issue","path","_tag","issues","length","flatMap","map","error","message","type","effectTsResolver","schema","schemaOptions","errors","onExcessProperty","values","_","options","Schema","decodeUnknownEffect","pipe","Effect","match","onFailure","validateAllFieldCriteria","shouldUseNativeValidation","criteriaMode","reduce","acc","key","join","types","messages","appendErrors","concat","Object","create","toNestErrors","onSuccess","result","validateFieldsNatively","runPromise"],"mappings":"2LAYA,MAAMA,EAAcC,EAAYC,gCAI1BC,EAAeA,CACnBC,EACAC,KAEA,OAAQD,EAAME,MACZ,IAAK,UACH,OAAOH,EAAaC,EAAMA,MAAO,IAAIC,KAASD,EAAMC,OACtD,IAAK,WACH,OAAOF,EAAaC,EAAMA,MAAOC,GACnC,IAAK,YACL,IAAK,QACH,GAAID,EAAMG,OAAOC,OACf,OAAOJ,EAAMG,OAAOE,QAASL,GAAUD,EAAaC,EAAOC,IAIjE,OAAOL,EAAYI,GAAOG,OAAOG,IAAKC,IAAW,CAC/CN,KAAM,IAAIA,KAAUM,EAAMN,MAAQ,IAClCO,QAASD,EAAMC,QACfC,KAAMT,EAAME,OACX,EAoCW,SAAAQ,EACdC,EACAC,EAA8B,CAAEC,OAAQ,MAAOC,iBAAkB,WAEjE,MAAO,CAACC,EAAQC,EAAGC,IACjBC,EAAOC,oBAAoBR,EAA3BO,CAAmCH,EAAQH,GAAeQ,KACxDC,EAAOC,MAAM,CACXC,UAAWA,EAAGvB,YACZ,MAAMwB,GACHP,EAAQQ,2BACgB,QAAzBR,EAAQS,aAMJb,EAASd,EAAaC,EAAO,IAAI2B,OACrC,CAACC,EAAKrB,KACJ,MAAMsB,EAAMtB,EAAMN,KAAK6B,KAAK,KAM5B,GAJKF,EAAIC,KACPD,EAAIC,GAAO,CAAErB,QAASD,EAAMC,QAASC,KAAMF,EAAME,OAG/Ce,EAA0B,CAC5B,MAAMO,EAAQH,EAAIC,GAAKE,MACjBC,EAAWD,GAASA,EAAMxB,EAAME,MAEtCmB,EAAIC,GAAOI,EACTJ,EACAL,EACAI,EACArB,EAAME,KACNuB,EACK,GAAgBE,OACfF,EACAzB,EAAMC,SAERD,EAAMC,QAEd,CAEA,OAAOoB,CAAG,EAEZO,OAAOC,OAAO,OAGhB,MAAO,CAAEvB,OAAQwB,EAAaxB,EAAQI,GAAUF,OAAQ,GAAI,EAE9DuB,UAAYC,IACVtB,EAAQQ,2BACNe,EAAuB,CAAA,EAAIvB,GAEtB,CAAEJ,OAAQ,CAAA,EAAIE,OAAQwB,MAGjClB,EAAOoB,WAEb"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{toNestErrors as
|
|
1
|
+
import{toNestErrors as e,validateFieldsNatively as r}from"@hookform/resolvers";import{SchemaIssue as t,Schema as s,Effect as n}from"effect";import{appendErrors as o}from"react-hook-form";var a=t.makeFormatterStandardSchemaV1(),i=function(e,r){switch(e._tag){case"Pointer":return i(e.issue,[].concat(r,e.path));case"Encoding":return i(e.issue,r);case"Composite":case"AnyOf":if(e.issues.length)return e.issues.flatMap(function(e){return i(e,r)})}return a(e).issues.map(function(t){return{path:[].concat(r,t.path||[]),message:t.message,type:e._tag}})};function u(t,a){return void 0===a&&(a={errors:"all",onExcessProperty:"ignore"}),function(u,c,f){return s.decodeUnknownEffect(t)(u,a).pipe(n.match({onFailure:function(r){var t=!f.shouldUseNativeValidation&&"all"===f.criteriaMode,s=i(r.issue,[]).reduce(function(e,r){var s=r.path.join(".");if(e[s]||(e[s]={message:r.message,type:r.type}),t){var n=e[s].types,a=n&&n[r.type];e[s]=o(s,t,e,r.type,a?[].concat(a,r.message):r.message)}return e},Object.create(null));return{errors:e(s,f),values:{}}},onSuccess:function(e){return f.shouldUseNativeValidation&&r({},f),{errors:{},values:e}}}),n.runPromise)}}export{u as effectTsResolver};
|
|
2
2
|
//# sourceMappingURL=effect-ts.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effect-ts.module.js","sources":["../src/effect-ts.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Effect, Schema
|
|
1
|
+
{"version":3,"file":"effect-ts.module.js","sources":["../src/effect-ts.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Effect, Schema, SchemaIssue } from 'effect';\nimport type { ParseOptions } from 'effect/SchemaAST';\nimport {\n type FieldError,\n FieldValues,\n Resolver,\n appendErrors,\n} from 'react-hook-form';\n\ntype Issue = { path: PropertyKey[]; message: string; type: string };\n\nconst formatIssue = SchemaIssue.makeFormatterStandardSchemaV1();\n\n// Flattens the issue tree like `makeFormatterStandardSchemaV1` does, but keeps\n// the `_tag` of the failing node so it can be reported as the error `type`.\nconst flattenIssue = (\n issue: SchemaIssue.Issue,\n path: PropertyKey[],\n): Issue[] => {\n switch (issue._tag) {\n case 'Pointer':\n return flattenIssue(issue.issue, [...path, ...issue.path]);\n case 'Encoding':\n return flattenIssue(issue.issue, path);\n case 'Composite':\n case 'AnyOf':\n if (issue.issues.length) {\n return issue.issues.flatMap((issue) => flattenIssue(issue, path));\n }\n }\n\n return formatIssue(issue).issues.map((error) => ({\n path: [...path, ...(error.path || [])] as PropertyKey[],\n message: error.message,\n type: issue._tag,\n }));\n};\n\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions?: ParseOptions,\n resolverOptions?: {\n mode?: 'async' | 'sync';\n raw?: false;\n },\n): Resolver<Input, Context, Output>;\n\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions: ParseOptions | 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 Effect Schema validation\n * @param {Schema.Codec<Output, Input>} schema - The Effect schema to validate against\n * @param {ParseOptions} [schemaOptions] - Optional Effect Schema parse options\n * @returns {Resolver<Schema.Schema.Type<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = Schema.Struct({\n * name: Schema.String,\n * age: Schema.Number\n * });\n *\n * useForm({\n * resolver: effectTsResolver(schema)\n * });\n */\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions: ParseOptions = { errors: 'all', onExcessProperty: 'ignore' },\n): Resolver<Input, Context, Output | Input> {\n return (values, _, options) =>\n Schema.decodeUnknownEffect(schema)(values, schemaOptions).pipe(\n Effect.match({\n onFailure: ({ issue }) => {\n const validateAllFieldCriteria =\n !options.shouldUseNativeValidation &&\n options.criteriaMode === 'all';\n\n // A null-prototype accumulator avoids false-positive hits from\n // inherited members (e.g. `toString`, `constructor`) when a field\n // is named after one of them — a plain `{}` would make `!acc[key]`\n // truthy-skip those paths and silently drop their errors.\n const errors = flattenIssue(issue, []).reduce(\n (acc, error) => {\n const key = error.path.join('.');\n\n if (!acc[key]) {\n acc[key] = { message: error.message, type: error.type };\n }\n\n if (validateAllFieldCriteria) {\n const types = acc[key].types;\n const messages = types && types[error.type];\n\n acc[key] = appendErrors(\n key,\n validateAllFieldCriteria,\n acc,\n error.type,\n messages\n ? ([] as string[]).concat(\n messages as string[],\n error.message,\n )\n : error.message,\n ) as FieldError;\n }\n\n return acc;\n },\n Object.create(null) as Record<string, FieldError>,\n );\n\n return { errors: toNestErrors(errors, options), values: {} };\n },\n onSuccess: (result) => {\n options.shouldUseNativeValidation &&\n validateFieldsNatively({}, options);\n\n return { errors: {}, values: result };\n },\n }),\n Effect.runPromise,\n );\n}\n"],"names":["formatIssue","SchemaIssue","makeFormatterStandardSchemaV1","flattenIssue","issue","path","_tag","concat","issues","length","flatMap","map","error","message","type","effectTsResolver","schema","schemaOptions","errors","onExcessProperty","values","_","options","Schema","decodeUnknownEffect","pipe","Effect","match","onFailure","_ref","validateAllFieldCriteria","shouldUseNativeValidation","criteriaMode","reduce","acc","key","join","types","messages","appendErrors","Object","create","toNestErrors","onSuccess","result","validateFieldsNatively","runPromise"],"mappings":"2LAYA,IAAMA,EAAcC,EAAYC,gCAI1BC,EAAe,SACnBC,EACAC,GAEA,OAAQD,EAAME,MACZ,IAAK,UACH,OAAOH,EAAaC,EAAMA,MAAK,GAAAG,OAAMF,EAASD,EAAMC,OACtD,IAAK,WACH,OAAOF,EAAaC,EAAMA,MAAOC,GACnC,IAAK,YACL,IAAK,QACH,GAAID,EAAMI,OAAOC,OACf,OAAOL,EAAMI,OAAOE,QAAQ,SAACN,GAAK,OAAKD,EAAaC,EAAOC,EAAK,GAItE,OAAOL,EAAYI,GAAOI,OAAOG,IAAI,SAACC,GAAW,MAAA,CAC/CP,KAAI,GAAAE,OAAMF,EAAUO,EAAMP,MAAQ,IAClCQ,QAASD,EAAMC,QACfC,KAAMV,EAAME,KACb,EACH,EAmCgB,SAAAS,EACdC,EACAC,GAEA,YAF8B,IAA9BA,IAAAA,EAA8B,CAAEC,OAAQ,MAAOC,iBAAkB,WAE1D,SAACC,EAAQC,EAAGC,GACjB,OAAAC,EAAOC,oBAAoBR,EAA3BO,CAAmCH,EAAQH,GAAeQ,KACxDC,EAAOC,MAAM,CACXC,UAAW,SAAFC,GAAgB,IACjBC,GACHR,EAAQS,2BACgB,QAAzBT,EAAQU,aAMJd,EAASf,EATE0B,EAALzB,MASuB,IAAI6B,OACrC,SAACC,EAAKtB,GACJ,IAAMuB,EAAMvB,EAAMP,KAAK+B,KAAK,KAM5B,GAJKF,EAAIC,KACPD,EAAIC,GAAO,CAAEtB,QAASD,EAAMC,QAASC,KAAMF,EAAME,OAG/CgB,EAA0B,CAC5B,IAAMO,EAAQH,EAAIC,GAAKE,MACjBC,EAAWD,GAASA,EAAMzB,EAAME,MAEtCoB,EAAIC,GAAOI,EACTJ,EACAL,EACAI,EACAtB,EAAME,KACNwB,EACK,GAAgB/B,OACf+B,EACA1B,EAAMC,SAERD,EAAMC,QAEd,CAEA,OAAOqB,CACT,EACAM,OAAOC,OAAO,OAGhB,MAAO,CAAEvB,OAAQwB,EAAaxB,EAAQI,GAAUF,OAAQ,GAC1D,EACAuB,UAAW,SAACC,GAIV,OAHAtB,EAAQS,2BACNc,EAAuB,GAAIvB,GAEtB,CAAEJ,OAAQ,CAAE,EAAEE,OAAQwB,EAC/B,IAEFlB,EAAOoB,WACR,CACL"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@hookform/resolvers"),require("effect"),require("
|
|
1
|
+
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@hookform/resolvers"),require("effect"),require("react-hook-form")):"function"==typeof define&&define.amd?define(["exports","@hookform/resolvers","effect","react-hook-form"],r):r((e||self).hookformResolversEffectTs={},e.hookformResolvers,e.Effect,e.ReactHookForm)}(this,function(e,r,t,o){var s=t.SchemaIssue.makeFormatterStandardSchemaV1(),n=function(e,r){switch(e._tag){case"Pointer":return n(e.issue,[].concat(r,e.path));case"Encoding":return n(e.issue,r);case"Composite":case"AnyOf":if(e.issues.length)return e.issues.flatMap(function(e){return n(e,r)})}return s(e).issues.map(function(t){return{path:[].concat(r,t.path||[]),message:t.message,type:e._tag}})};e.effectTsResolver=function(e,s){return void 0===s&&(s={errors:"all",onExcessProperty:"ignore"}),function(a,i,c){return t.Schema.decodeUnknownEffect(e)(a,s).pipe(t.Effect.match({onFailure:function(e){var t=!c.shouldUseNativeValidation&&"all"===c.criteriaMode,s=n(e.issue,[]).reduce(function(e,r){var s=r.path.join(".");if(e[s]||(e[s]={message:r.message,type:r.type}),t){var n=e[s].types,a=n&&n[r.type];e[s]=o.appendErrors(s,t,e,r.type,a?[].concat(a,r.message):r.message)}return e},Object.create(null));return{errors:r.toNestErrors(s,c),values:{}}},onSuccess:function(e){return c.shouldUseNativeValidation&&r.validateFieldsNatively({},c),{errors:{},values:e}}}),t.Effect.runPromise)}}});
|
|
2
2
|
//# sourceMappingURL=effect-ts.umd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effect-ts.umd.js","sources":["../src/effect-ts.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Effect, Schema
|
|
1
|
+
{"version":3,"file":"effect-ts.umd.js","sources":["../src/effect-ts.ts"],"sourcesContent":["import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';\nimport { Effect, Schema, SchemaIssue } from 'effect';\nimport type { ParseOptions } from 'effect/SchemaAST';\nimport {\n type FieldError,\n FieldValues,\n Resolver,\n appendErrors,\n} from 'react-hook-form';\n\ntype Issue = { path: PropertyKey[]; message: string; type: string };\n\nconst formatIssue = SchemaIssue.makeFormatterStandardSchemaV1();\n\n// Flattens the issue tree like `makeFormatterStandardSchemaV1` does, but keeps\n// the `_tag` of the failing node so it can be reported as the error `type`.\nconst flattenIssue = (\n issue: SchemaIssue.Issue,\n path: PropertyKey[],\n): Issue[] => {\n switch (issue._tag) {\n case 'Pointer':\n return flattenIssue(issue.issue, [...path, ...issue.path]);\n case 'Encoding':\n return flattenIssue(issue.issue, path);\n case 'Composite':\n case 'AnyOf':\n if (issue.issues.length) {\n return issue.issues.flatMap((issue) => flattenIssue(issue, path));\n }\n }\n\n return formatIssue(issue).issues.map((error) => ({\n path: [...path, ...(error.path || [])] as PropertyKey[],\n message: error.message,\n type: issue._tag,\n }));\n};\n\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions?: ParseOptions,\n resolverOptions?: {\n mode?: 'async' | 'sync';\n raw?: false;\n },\n): Resolver<Input, Context, Output>;\n\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions: ParseOptions | 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 Effect Schema validation\n * @param {Schema.Codec<Output, Input>} schema - The Effect schema to validate against\n * @param {ParseOptions} [schemaOptions] - Optional Effect Schema parse options\n * @returns {Resolver<Schema.Schema.Type<typeof schema>>} A resolver function compatible with react-hook-form\n * @example\n * const schema = Schema.Struct({\n * name: Schema.String,\n * age: Schema.Number\n * });\n *\n * useForm({\n * resolver: effectTsResolver(schema)\n * });\n */\nexport function effectTsResolver<Input extends FieldValues, Context, Output>(\n schema: Schema.Codec<Output, Input, never, unknown>,\n schemaOptions: ParseOptions = { errors: 'all', onExcessProperty: 'ignore' },\n): Resolver<Input, Context, Output | Input> {\n return (values, _, options) =>\n Schema.decodeUnknownEffect(schema)(values, schemaOptions).pipe(\n Effect.match({\n onFailure: ({ issue }) => {\n const validateAllFieldCriteria =\n !options.shouldUseNativeValidation &&\n options.criteriaMode === 'all';\n\n // A null-prototype accumulator avoids false-positive hits from\n // inherited members (e.g. `toString`, `constructor`) when a field\n // is named after one of them — a plain `{}` would make `!acc[key]`\n // truthy-skip those paths and silently drop their errors.\n const errors = flattenIssue(issue, []).reduce(\n (acc, error) => {\n const key = error.path.join('.');\n\n if (!acc[key]) {\n acc[key] = { message: error.message, type: error.type };\n }\n\n if (validateAllFieldCriteria) {\n const types = acc[key].types;\n const messages = types && types[error.type];\n\n acc[key] = appendErrors(\n key,\n validateAllFieldCriteria,\n acc,\n error.type,\n messages\n ? ([] as string[]).concat(\n messages as string[],\n error.message,\n )\n : error.message,\n ) as FieldError;\n }\n\n return acc;\n },\n Object.create(null) as Record<string, FieldError>,\n );\n\n return { errors: toNestErrors(errors, options), values: {} };\n },\n onSuccess: (result) => {\n options.shouldUseNativeValidation &&\n validateFieldsNatively({}, options);\n\n return { errors: {}, values: result };\n },\n }),\n Effect.runPromise,\n );\n}\n"],"names":["formatIssue","SchemaIssue","makeFormatterStandardSchemaV1","flattenIssue","issue","path","_tag","concat","issues","length","flatMap","map","error","message","type","schema","schemaOptions","errors","onExcessProperty","values","_","options","Schema","decodeUnknownEffect","pipe","Effect","match","onFailure","_ref","validateAllFieldCriteria","shouldUseNativeValidation","criteriaMode","reduce","acc","key","join","types","messages","appendErrors","Object","create","toNestErrors","onSuccess","result","validateFieldsNatively","runPromise"],"mappings":"maAYA,IAAMA,EAAcC,EAAWA,YAACC,gCAI1BC,EAAe,SACnBC,EACAC,GAEA,OAAQD,EAAME,MACZ,IAAK,UACH,OAAOH,EAAaC,EAAMA,MAAK,GAAAG,OAAMF,EAASD,EAAMC,OACtD,IAAK,WACH,OAAOF,EAAaC,EAAMA,MAAOC,GACnC,IAAK,YACL,IAAK,QACH,GAAID,EAAMI,OAAOC,OACf,OAAOL,EAAMI,OAAOE,QAAQ,SAACN,GAAK,OAAKD,EAAaC,EAAOC,EAAK,GAItE,OAAOL,EAAYI,GAAOI,OAAOG,IAAI,SAACC,GAAW,MAAA,CAC/CP,KAAI,GAAAE,OAAMF,EAAUO,EAAMP,MAAQ,IAClCQ,QAASD,EAAMC,QACfC,KAAMV,EAAME,KACb,EACH,qBAmCgB,SACdS,EACAC,GAEA,YAF8B,IAA9BA,IAAAA,EAA8B,CAAEC,OAAQ,MAAOC,iBAAkB,WAE1D,SAACC,EAAQC,EAAGC,GACjB,OAAAC,EAAMA,OAACC,oBAAoBR,EAA3BO,CAAmCH,EAAQH,GAAeQ,KACxDC,EAAAA,OAAOC,MAAM,CACXC,UAAW,SAAFC,GAAgB,IACjBC,GACHR,EAAQS,2BACgB,QAAzBT,EAAQU,aAMJd,EAASd,EATEyB,EAALxB,MASuB,IAAI4B,OACrC,SAACC,EAAKrB,GACJ,IAAMsB,EAAMtB,EAAMP,KAAK8B,KAAK,KAM5B,GAJKF,EAAIC,KACPD,EAAIC,GAAO,CAAErB,QAASD,EAAMC,QAASC,KAAMF,EAAME,OAG/Ce,EAA0B,CAC5B,IAAMO,EAAQH,EAAIC,GAAKE,MACjBC,EAAWD,GAASA,EAAMxB,EAAME,MAEtCmB,EAAIC,GAAOI,eACTJ,EACAL,EACAI,EACArB,EAAME,KACNuB,EACK,GAAgB9B,OACf8B,EACAzB,EAAMC,SAERD,EAAMC,QAEd,CAEA,OAAOoB,CACT,EACAM,OAAOC,OAAO,OAGhB,MAAO,CAAEvB,OAAQwB,EAAYA,aAACxB,EAAQI,GAAUF,OAAQ,GAC1D,EACAuB,UAAW,SAACC,GAIV,OAHAtB,EAAQS,2BACNc,EAAsBA,uBAAC,GAAIvB,GAEtB,CAAEJ,OAAQ,CAAE,EAAEE,OAAQwB,EAC/B,IAEFlB,SAAOoB,WACR,CACL"}
|
package/effect-ts/package.json
CHANGED
|
@@ -9,11 +9,11 @@ const USERNAME_REQUIRED_MESSAGE = 'username field is required';
|
|
|
9
9
|
const PASSWORD_REQUIRED_MESSAGE = 'password field is required';
|
|
10
10
|
|
|
11
11
|
const schema = Schema.Struct({
|
|
12
|
-
username: Schema.String.
|
|
13
|
-
Schema.
|
|
12
|
+
username: Schema.String.check(
|
|
13
|
+
Schema.isNonEmpty({ message: USERNAME_REQUIRED_MESSAGE }),
|
|
14
14
|
),
|
|
15
|
-
password: Schema.String.
|
|
16
|
-
Schema.
|
|
15
|
+
password: Schema.String.check(
|
|
16
|
+
Schema.isNonEmpty({ message: PASSWORD_REQUIRED_MESSAGE }),
|
|
17
17
|
),
|
|
18
18
|
});
|
|
19
19
|
|
|
@@ -9,11 +9,11 @@ const USERNAME_REQUIRED_MESSAGE = 'username field is required';
|
|
|
9
9
|
const PASSWORD_REQUIRED_MESSAGE = 'password field is required';
|
|
10
10
|
|
|
11
11
|
const schema = Schema.Struct({
|
|
12
|
-
username: Schema.String.
|
|
13
|
-
Schema.
|
|
12
|
+
username: Schema.String.check(
|
|
13
|
+
Schema.isNonEmpty({ message: USERNAME_REQUIRED_MESSAGE }),
|
|
14
14
|
),
|
|
15
|
-
password: Schema.String.
|
|
16
|
-
Schema.
|
|
15
|
+
password: Schema.String.check(
|
|
16
|
+
Schema.isNonEmpty({ message: PASSWORD_REQUIRED_MESSAGE }),
|
|
17
17
|
),
|
|
18
18
|
});
|
|
19
19
|
|
|
@@ -2,43 +2,43 @@ import { Schema } from 'effect';
|
|
|
2
2
|
import { Field, InternalFieldName } from 'react-hook-form';
|
|
3
3
|
|
|
4
4
|
export const schema = Schema.Struct({
|
|
5
|
-
username: Schema.String.
|
|
6
|
-
Schema.
|
|
5
|
+
username: Schema.String.check(
|
|
6
|
+
Schema.isNonEmpty({ message: 'A username is required' }),
|
|
7
7
|
),
|
|
8
|
-
password: Schema.String.
|
|
9
|
-
Schema.
|
|
10
|
-
message:
|
|
8
|
+
password: Schema.String.check(
|
|
9
|
+
Schema.isPattern(new RegExp('.*[A-Z].*'), {
|
|
10
|
+
message: 'At least 1 uppercase letter.',
|
|
11
11
|
}),
|
|
12
|
-
Schema.
|
|
13
|
-
message:
|
|
12
|
+
Schema.isPattern(new RegExp('.*[a-z].*'), {
|
|
13
|
+
message: 'At least 1 lowercase letter.',
|
|
14
14
|
}),
|
|
15
|
-
Schema.
|
|
16
|
-
message:
|
|
15
|
+
Schema.isPattern(new RegExp('.*\\d.*'), {
|
|
16
|
+
message: 'At least 1 number.',
|
|
17
17
|
}),
|
|
18
|
-
Schema.
|
|
18
|
+
Schema.isPattern(
|
|
19
19
|
new RegExp('.*[`~<>?,./!@#$%^&*()\\-_+="\'|{}\\[\\];:\\\\].*'),
|
|
20
20
|
{
|
|
21
|
-
message:
|
|
21
|
+
message: 'At least 1 special character.',
|
|
22
22
|
},
|
|
23
23
|
),
|
|
24
|
-
Schema.
|
|
24
|
+
Schema.isMinLength(8, { message: 'Must be at least 8 characters.' }),
|
|
25
25
|
),
|
|
26
|
-
accessToken: Schema.Union(Schema.String, Schema.Number),
|
|
27
|
-
birthYear: Schema.Number.
|
|
28
|
-
Schema.
|
|
29
|
-
message:
|
|
26
|
+
accessToken: Schema.Union([Schema.String, Schema.Number]),
|
|
27
|
+
birthYear: Schema.Number.check(
|
|
28
|
+
Schema.isGreaterThan(1900, {
|
|
29
|
+
message: 'Must be greater than the year 1900',
|
|
30
30
|
}),
|
|
31
|
-
Schema.
|
|
32
|
-
message:
|
|
31
|
+
Schema.makeFilter((value: number) => value < new Date().getFullYear(), {
|
|
32
|
+
message: 'Must be before the current year.',
|
|
33
33
|
}),
|
|
34
34
|
),
|
|
35
|
-
email: Schema.String.
|
|
36
|
-
Schema.
|
|
35
|
+
email: Schema.String.check(
|
|
36
|
+
Schema.isPattern(
|
|
37
37
|
new RegExp(
|
|
38
38
|
/^(?!\.)(?!.*\.\.)([A-Z0-9_+-.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9-]*\.)+[A-Z]{2,}$/i,
|
|
39
39
|
),
|
|
40
40
|
{
|
|
41
|
-
message:
|
|
41
|
+
message: 'A valid email address is required.',
|
|
42
42
|
},
|
|
43
43
|
),
|
|
44
44
|
),
|
|
@@ -49,9 +49,9 @@ export const schema = Schema.Struct({
|
|
|
49
49
|
),
|
|
50
50
|
luckyNumbers: Schema.Array(Schema.Number),
|
|
51
51
|
enabled: Schema.Boolean,
|
|
52
|
-
animal: Schema.Union(Schema.String, Schema.
|
|
52
|
+
animal: Schema.Union([Schema.String, Schema.Literals(['bird', 'snake'])]),
|
|
53
53
|
vehicles: Schema.Array(
|
|
54
|
-
Schema.Union(
|
|
54
|
+
Schema.Union([
|
|
55
55
|
Schema.Struct({
|
|
56
56
|
type: Schema.Literal('car'),
|
|
57
57
|
brand: Schema.String,
|
|
@@ -61,7 +61,7 @@ export const schema = Schema.Struct({
|
|
|
61
61
|
type: Schema.Literal('bike'),
|
|
62
62
|
speed: Schema.Number,
|
|
63
63
|
}),
|
|
64
|
-
),
|
|
64
|
+
]),
|
|
65
65
|
),
|
|
66
66
|
});
|
|
67
67
|
|
|
@@ -4,17 +4,17 @@ exports[`effectTsResolver > should return a single error from effectTsResolver w
|
|
|
4
4
|
{
|
|
5
5
|
"errors": {
|
|
6
6
|
"animal": {
|
|
7
|
-
"message": "Expected string
|
|
7
|
+
"message": "Expected string | "bird" | "snake"",
|
|
8
8
|
"ref": undefined,
|
|
9
|
-
"type": "
|
|
9
|
+
"type": "AnyOf",
|
|
10
10
|
},
|
|
11
11
|
"luckyNumbers": [
|
|
12
12
|
,
|
|
13
13
|
,
|
|
14
14
|
{
|
|
15
|
-
"message": "Expected number
|
|
15
|
+
"message": "Expected number",
|
|
16
16
|
"ref": undefined,
|
|
17
|
-
"type": "
|
|
17
|
+
"type": "InvalidType",
|
|
18
18
|
},
|
|
19
19
|
],
|
|
20
20
|
"password": {
|
|
@@ -22,15 +22,15 @@ exports[`effectTsResolver > should return a single error from effectTsResolver w
|
|
|
22
22
|
"ref": {
|
|
23
23
|
"name": "password",
|
|
24
24
|
},
|
|
25
|
-
"type": "
|
|
25
|
+
"type": "Filter",
|
|
26
26
|
},
|
|
27
27
|
"vehicles": [
|
|
28
28
|
,
|
|
29
29
|
{
|
|
30
30
|
"horsepower": {
|
|
31
|
-
"message": "
|
|
31
|
+
"message": "Missing key",
|
|
32
32
|
"ref": undefined,
|
|
33
|
-
"type": "
|
|
33
|
+
"type": "MissingKey",
|
|
34
34
|
},
|
|
35
35
|
},
|
|
36
36
|
],
|
|
@@ -47,10 +47,9 @@ exports[`effectTsResolver > should return all the errors from effectTsResolver w
|
|
|
47
47
|
"ref": {
|
|
48
48
|
"name": "phoneNumber",
|
|
49
49
|
},
|
|
50
|
-
"type": "
|
|
50
|
+
"type": "Filter",
|
|
51
51
|
"types": {
|
|
52
|
-
"
|
|
53
|
-
"Type": "Expected undefined, actual "123"",
|
|
52
|
+
"Filter": "Please enter a valid phone number in international format.",
|
|
54
53
|
},
|
|
55
54
|
},
|
|
56
55
|
},
|
|
@@ -66,7 +65,7 @@ exports[`effectTsResolver > should return the first error from effectTsResolver
|
|
|
66
65
|
"ref": {
|
|
67
66
|
"name": "phoneNumber",
|
|
68
67
|
},
|
|
69
|
-
"type": "
|
|
68
|
+
"type": "Filter",
|
|
70
69
|
},
|
|
71
70
|
},
|
|
72
71
|
"values": {},
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { renderHook } from '@testing-library/react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Effect,
|
|
4
|
+
Schema,
|
|
5
|
+
SchemaGetter,
|
|
6
|
+
SchemaIssue,
|
|
7
|
+
SchemaTransformation,
|
|
8
|
+
} from 'effect';
|
|
3
9
|
import { Resolver, useForm } from 'react-hook-form';
|
|
4
10
|
import { SubmitHandler } from 'react-hook-form';
|
|
5
11
|
import { effectTsResolver } from '..';
|
|
@@ -29,9 +35,9 @@ describe('effectTsResolver', () => {
|
|
|
29
35
|
it('should return the first error from effectTsResolver when validation fails with `validateAllFieldCriteria` set to firstError', async () => {
|
|
30
36
|
const SignupSchema = Schema.Struct({
|
|
31
37
|
phoneNumber: Schema.optional(
|
|
32
|
-
Schema.String.
|
|
33
|
-
Schema.
|
|
34
|
-
message:
|
|
38
|
+
Schema.String.check(
|
|
39
|
+
Schema.isPattern(/^\+\d{7,15}$/, {
|
|
40
|
+
message:
|
|
35
41
|
'Please enter a valid phone number in international format.',
|
|
36
42
|
}),
|
|
37
43
|
),
|
|
@@ -59,9 +65,9 @@ describe('effectTsResolver', () => {
|
|
|
59
65
|
it('should return all the errors from effectTsResolver when validation fails with `validateAllFieldCriteria` set to true', async () => {
|
|
60
66
|
const SignupSchema = Schema.Struct({
|
|
61
67
|
phoneNumber: Schema.optional(
|
|
62
|
-
Schema.String.
|
|
63
|
-
Schema.
|
|
64
|
-
message:
|
|
68
|
+
Schema.String.check(
|
|
69
|
+
Schema.isPattern(/^\+\d{7,15}$/, {
|
|
70
|
+
message:
|
|
65
71
|
'Please enter a valid phone number in international format.',
|
|
66
72
|
}),
|
|
67
73
|
),
|
|
@@ -86,6 +92,171 @@ describe('effectTsResolver', () => {
|
|
|
86
92
|
expect(result).toMatchSnapshot();
|
|
87
93
|
});
|
|
88
94
|
|
|
95
|
+
it('should group all messages of the same issue type when `criteriaMode` is "all"', async () => {
|
|
96
|
+
const schema = Schema.Struct({
|
|
97
|
+
password: Schema.String.check(
|
|
98
|
+
Schema.isMinLength(8, { message: 'Too short' }),
|
|
99
|
+
Schema.isPattern(/\d/, { message: 'Needs a number' }),
|
|
100
|
+
),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const result = await effectTsResolver(schema)(
|
|
104
|
+
{ password: 'abc' },
|
|
105
|
+
undefined,
|
|
106
|
+
{ fields, criteriaMode: 'all', shouldUseNativeValidation },
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(result.errors.password).toMatchObject({
|
|
110
|
+
message: 'Too short',
|
|
111
|
+
type: 'Filter',
|
|
112
|
+
types: { Filter: ['Too short', 'Needs a number'] },
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should return nested and indexed errors with their issue type', async () => {
|
|
117
|
+
const schema = Schema.Struct({
|
|
118
|
+
user: Schema.Struct({ name: Schema.String }),
|
|
119
|
+
tags: Schema.Array(Schema.String),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const result = await effectTsResolver(schema)(
|
|
123
|
+
{ user: {}, tags: ['a', 1] } as any,
|
|
124
|
+
undefined,
|
|
125
|
+
{ fields, shouldUseNativeValidation },
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(result).toEqual({
|
|
129
|
+
values: {},
|
|
130
|
+
errors: {
|
|
131
|
+
user: { name: { message: 'Missing key', type: 'MissingKey' } },
|
|
132
|
+
tags: [undefined, { message: 'Expected string', type: 'InvalidType' }],
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should only report the first error when `schemaOptions.errors` is "first"', async () => {
|
|
138
|
+
const schema = Schema.Struct({ a: Schema.String, b: Schema.String });
|
|
139
|
+
|
|
140
|
+
const result = await effectTsResolver(schema, { errors: 'first' })(
|
|
141
|
+
{} as any,
|
|
142
|
+
undefined,
|
|
143
|
+
{ fields, shouldUseNativeValidation },
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
expect(Object.keys(result.errors)).toEqual(['a']);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should ignore excess properties by default and report them when `onExcessProperty` is "error"', async () => {
|
|
150
|
+
const schema = Schema.Struct({ a: Schema.String });
|
|
151
|
+
const values = { a: 'a', b: 'b' } as any;
|
|
152
|
+
|
|
153
|
+
const ignored = await effectTsResolver(schema)(values, undefined, {
|
|
154
|
+
fields,
|
|
155
|
+
shouldUseNativeValidation,
|
|
156
|
+
});
|
|
157
|
+
expect(ignored).toEqual({ errors: {}, values: { a: 'a' } });
|
|
158
|
+
|
|
159
|
+
const rejected = await effectTsResolver(schema, {
|
|
160
|
+
errors: 'all',
|
|
161
|
+
onExcessProperty: 'error',
|
|
162
|
+
})(values, undefined, { fields, shouldUseNativeValidation });
|
|
163
|
+
expect(rejected.errors).toMatchObject({
|
|
164
|
+
b: { type: 'UnexpectedKey' },
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should return the decoded output when a transformation is used', async () => {
|
|
169
|
+
const schema = Schema.Struct({ age: Schema.NumberFromString });
|
|
170
|
+
|
|
171
|
+
const result = await effectTsResolver(schema)({ age: '42' }, undefined, {
|
|
172
|
+
fields,
|
|
173
|
+
shouldUseNativeValidation,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
expect(result).toEqual({ errors: {}, values: { age: 42 } });
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should report errors raised by a failing transformation', async () => {
|
|
180
|
+
const schema = Schema.Struct({
|
|
181
|
+
age: Schema.String.pipe(
|
|
182
|
+
Schema.decodeTo(Schema.Number, {
|
|
183
|
+
decode: SchemaGetter.transformEffect((s: string) =>
|
|
184
|
+
Number.isNaN(Number(s))
|
|
185
|
+
? Effect.fail(
|
|
186
|
+
new SchemaIssue.InvalidValue({ message: 'Not a number' }),
|
|
187
|
+
)
|
|
188
|
+
: Effect.succeed(Number(s)),
|
|
189
|
+
),
|
|
190
|
+
encode: SchemaGetter.String(),
|
|
191
|
+
}),
|
|
192
|
+
),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const result = await effectTsResolver(schema)({ age: 'abc' }, undefined, {
|
|
196
|
+
fields,
|
|
197
|
+
shouldUseNativeValidation,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
expect(result).toEqual({
|
|
201
|
+
values: {},
|
|
202
|
+
errors: { age: { message: 'Not a number', type: 'InvalidValue' } },
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should support asynchronous validation', async () => {
|
|
207
|
+
const schema = Schema.Struct({
|
|
208
|
+
username: Schema.String.pipe(
|
|
209
|
+
Schema.decodeTo(Schema.String, {
|
|
210
|
+
decode: SchemaGetter.checkEffect((s: string) =>
|
|
211
|
+
Effect.sleep('1 millis').pipe(
|
|
212
|
+
Effect.as(s === 'taken' ? 'Username is taken' : undefined),
|
|
213
|
+
),
|
|
214
|
+
),
|
|
215
|
+
encode: SchemaGetter.passthrough(),
|
|
216
|
+
}),
|
|
217
|
+
),
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const valid = await effectTsResolver(schema)(
|
|
221
|
+
{ username: 'free' },
|
|
222
|
+
undefined,
|
|
223
|
+
{ fields, shouldUseNativeValidation },
|
|
224
|
+
);
|
|
225
|
+
expect(valid).toEqual({ errors: {}, values: { username: 'free' } });
|
|
226
|
+
|
|
227
|
+
const invalid = await effectTsResolver(schema)(
|
|
228
|
+
{ username: 'taken' },
|
|
229
|
+
undefined,
|
|
230
|
+
{ fields, shouldUseNativeValidation },
|
|
231
|
+
);
|
|
232
|
+
expect(invalid.errors).toMatchObject({
|
|
233
|
+
username: { message: 'Username is taken' },
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should validate fields natively when `shouldUseNativeValidation` is true', async () => {
|
|
238
|
+
const setCustomValidity = vi.fn();
|
|
239
|
+
const reportValidity = vi.fn();
|
|
240
|
+
const nativeFields = {
|
|
241
|
+
username: {
|
|
242
|
+
name: 'username',
|
|
243
|
+
ref: { name: 'username', setCustomValidity, reportValidity },
|
|
244
|
+
},
|
|
245
|
+
} as any;
|
|
246
|
+
|
|
247
|
+
const result = await effectTsResolver(
|
|
248
|
+
Schema.Struct({ username: Schema.String }),
|
|
249
|
+
)({ username: 'john' }, undefined, {
|
|
250
|
+
fields: nativeFields,
|
|
251
|
+
names: ['username'],
|
|
252
|
+
shouldUseNativeValidation: true,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
expect(result).toEqual({ errors: {}, values: { username: 'john' } });
|
|
256
|
+
expect(setCustomValidity).toHaveBeenCalledWith('');
|
|
257
|
+
expect(reportValidity).toHaveBeenCalled();
|
|
258
|
+
});
|
|
259
|
+
|
|
89
260
|
/**
|
|
90
261
|
* Type inference tests
|
|
91
262
|
*/
|
|
@@ -100,10 +271,15 @@ describe('effectTsResolver', () => {
|
|
|
100
271
|
it('should correctly infer the output type from a effectTs schema using a transform', () => {
|
|
101
272
|
const resolver = effectTsResolver(
|
|
102
273
|
Schema.Struct({
|
|
103
|
-
id: Schema.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
274
|
+
id: Schema.Number.pipe(
|
|
275
|
+
Schema.decodeTo(
|
|
276
|
+
Schema.String,
|
|
277
|
+
SchemaTransformation.transform({
|
|
278
|
+
decode: (val) => String(val),
|
|
279
|
+
encode: (val) => Number(val),
|
|
280
|
+
}),
|
|
281
|
+
),
|
|
282
|
+
),
|
|
107
283
|
}),
|
|
108
284
|
);
|
|
109
285
|
|
|
@@ -132,10 +308,15 @@ describe('effectTsResolver', () => {
|
|
|
132
308
|
|
|
133
309
|
it('should correctly infer the output type from a effectTs schema with a transform for the handleSubmit function in useForm', () => {
|
|
134
310
|
const schema = Schema.Struct({
|
|
135
|
-
id: Schema.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
311
|
+
id: Schema.Number.pipe(
|
|
312
|
+
Schema.decodeTo(
|
|
313
|
+
Schema.String,
|
|
314
|
+
SchemaTransformation.transform({
|
|
315
|
+
decode: (val) => String(val),
|
|
316
|
+
encode: (val) => Number(val),
|
|
317
|
+
}),
|
|
318
|
+
),
|
|
319
|
+
),
|
|
139
320
|
});
|
|
140
321
|
|
|
141
322
|
const {
|
|
@@ -8,7 +8,7 @@ describe('effectTsResolver special path names', () => {
|
|
|
8
8
|
'reports a validation error for a field named "%s"',
|
|
9
9
|
async (name) => {
|
|
10
10
|
const schema = Schema.Struct({
|
|
11
|
-
[name]: Schema.String.
|
|
11
|
+
[name]: Schema.String.check(Schema.isMinLength(1)),
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
const result = await effectTsResolver(schema as any)(
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';
|
|
2
|
-
import { Effect, Schema } from 'effect';
|
|
3
|
-
import {
|
|
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.
|
|
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.
|
|
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
|
|
32
|
-
* @param {Schema.
|
|
33
|
-
* @param {ParseOptions} [schemaOptions] - Optional Effect
|
|
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.
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
93
|
+
if (!acc[key]) {
|
|
94
|
+
acc[key] = { message: error.message, type: error.type };
|
|
95
|
+
}
|
|
72
96
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
97
|
+
if (validateAllFieldCriteria) {
|
|
98
|
+
const types = acc[key].types;
|
|
99
|
+
const messages = types && types[error.type];
|
|
76
100
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
115
|
+
return acc;
|
|
116
|
+
},
|
|
117
|
+
Object.create(null) as Record<string, FieldError>,
|
|
118
|
+
);
|
|
92
119
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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": "
|
|
4
|
+
"version": "6.0.1",
|
|
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
|
|
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.23.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": "^
|
|
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",
|
|
@@ -331,7 +331,6 @@
|
|
|
331
331
|
"peerDependencies": {
|
|
332
332
|
"@sinclair/typebox": ">=0.25.24",
|
|
333
333
|
"@standard-schema/spec": "^1.0.0",
|
|
334
|
-
"@typeschema/main": ">=0.13.7",
|
|
335
334
|
"@vinejs/vine": "^2.0.0 || ^3.0.0 || ^4.0.0",
|
|
336
335
|
"ajv": "^8.12.0",
|
|
337
336
|
"ajv-errors": "^3.0.0",
|
|
@@ -341,7 +340,7 @@
|
|
|
341
340
|
"class-transformer": ">=0.4.0",
|
|
342
341
|
"class-validator": ">=0.12.0",
|
|
343
342
|
"computed-types": "^1.0.0",
|
|
344
|
-
"effect": "^
|
|
343
|
+
"effect": "^4.0.0",
|
|
345
344
|
"fluentvalidation-ts": "^3.0.0",
|
|
346
345
|
"fp-ts": "^2.7.0",
|
|
347
346
|
"io-ts": "^2.0.0",
|
|
@@ -362,9 +361,6 @@
|
|
|
362
361
|
"@standard-schema/spec": {
|
|
363
362
|
"optional": true
|
|
364
363
|
},
|
|
365
|
-
"@typeschema/main": {
|
|
366
|
-
"optional": true
|
|
367
|
-
},
|
|
368
364
|
"@vinejs/vine": {
|
|
369
365
|
"optional": true
|
|
370
366
|
},
|