@beignet/react-hook-form 0.0.49 → 0.0.51
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/CHANGELOG.md +10 -0
- package/README.md +22 -2
- package/dist/index.d.ts +25 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/forms/SKILL.md +10 -3
- package/src/index.ts +55 -32
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @beignet/react-hook-form
|
|
2
2
|
|
|
3
|
+
## 0.0.51
|
|
4
|
+
|
|
5
|
+
## 0.0.50
|
|
6
|
+
|
|
7
|
+
### Patch Changes
|
|
8
|
+
|
|
9
|
+
- cede255: Keep JSON body nulls distinct in React Query keys, expose scoped service work
|
|
10
|
+
through Fetch servers, preserve streamed responses, and reject non-object form
|
|
11
|
+
body inputs at compile time.
|
|
12
|
+
|
|
3
13
|
## 0.0.49
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
> Beignet is experimental alpha software. The `0.0.x` package line is for early
|
|
10
10
|
> evaluation, and APIs may change between releases while the framework settles.
|
|
11
11
|
|
|
12
|
-
This package provides automatic form validation using
|
|
12
|
+
This package provides automatic form validation using a contract body schema
|
|
13
|
+
whose input is object-shaped. It works with any
|
|
14
|
+
[Standard Schema](https://github.com/standard-schema/standard-schema) library
|
|
15
|
+
(Zod, Valibot, ArkType, etc.).
|
|
13
16
|
|
|
14
17
|
## Installation
|
|
15
18
|
|
|
@@ -176,7 +179,10 @@ const rhf = createReactHookForm();
|
|
|
176
179
|
|
|
177
180
|
### `rhf(contract)`
|
|
178
181
|
|
|
179
|
-
Creates a React Hook Form adapter for a contract
|
|
182
|
+
Creates a React Hook Form adapter for a contract whose body schema input is an
|
|
183
|
+
object. TypeScript rejects contracts with scalar or array body inputs because
|
|
184
|
+
they do not define named form fields. The schema output may still transform to
|
|
185
|
+
any submit value.
|
|
180
186
|
|
|
181
187
|
```ts
|
|
182
188
|
const adapter = rhf(createTodo);
|
|
@@ -222,6 +228,20 @@ input/output split:
|
|
|
222
228
|
- `handleSubmit` callbacks receive the schema **output**: the parsed values
|
|
223
229
|
after coercion, transforms, and defaults run.
|
|
224
230
|
|
|
231
|
+
The schema input must be object-shaped. Keep lists and scalar values inside
|
|
232
|
+
named fields rather than making the request body itself an array or scalar:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
const replaceTags = todos
|
|
236
|
+
.put("/api/todos/:id/tags")
|
|
237
|
+
.body(z.object({ tags: z.array(z.string()) }))
|
|
238
|
+
.responses({ 200: TodoSchema });
|
|
239
|
+
|
|
240
|
+
const form = rhf(replaceTags).useForm({
|
|
241
|
+
defaultValues: { tags: [] },
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
225
245
|
```ts
|
|
226
246
|
// Contract definition
|
|
227
247
|
const createTodo = todos
|
package/dist/index.d.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { type ErrorMessageOverrides } from "@beignet/core/client";
|
|
|
2
2
|
import { type ContractLike, type HttpContractConfig, type InferInput, type InferOutput, type ResolveContract, type StandardSchemaV1 } from "@beignet/core/contracts";
|
|
3
3
|
import { type FieldValues, type UseFormProps, type UseFormReturn } from "react-hook-form";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Keep only object-shaped React Hook Form field values. Arrays are objects in
|
|
6
|
+
* JavaScript, but they do not define the named field-value record expected by
|
|
7
|
+
* this adapter.
|
|
7
8
|
*/
|
|
8
|
-
type
|
|
9
|
+
type ObjectFieldValues<T> = [T] extends [FieldValues] ? Extract<T, readonly unknown[]> extends never ? T : never : never;
|
|
9
10
|
/**
|
|
10
11
|
* Live form field values inferred from the contract body schema input.
|
|
11
12
|
*
|
|
@@ -13,13 +14,19 @@ type AsFieldValues<T> = T extends FieldValues ? T : T & FieldValues;
|
|
|
13
14
|
* `defaultValues`) hold pre-validation input, so coercing or transforming body
|
|
14
15
|
* schemas type fields as what the user edits, not what the schema produces.
|
|
15
16
|
*/
|
|
16
|
-
type FormInput<TContract extends HttpContractConfig> = TContract["body"] extends StandardSchemaV1 ?
|
|
17
|
+
type FormInput<TContract extends HttpContractConfig> = TContract["body"] extends StandardSchemaV1 ? ObjectFieldValues<InferInput<TContract["body"]>> : never;
|
|
17
18
|
/**
|
|
18
19
|
* Validated submit values inferred from the contract body schema output.
|
|
19
20
|
*
|
|
20
21
|
* `handleSubmit` callbacks receive the resolver-parsed output.
|
|
21
22
|
*/
|
|
22
|
-
type FormOutput<TContract extends HttpContractConfig> = TContract["body"] extends StandardSchemaV1 ? InferOutput<TContract["body"]> :
|
|
23
|
+
type FormOutput<TContract extends HttpContractConfig> = TContract["body"] extends StandardSchemaV1 ? InferOutput<TContract["body"]> : never;
|
|
24
|
+
/**
|
|
25
|
+
* Contract input accepted by the React Hook Form adapter.
|
|
26
|
+
*/
|
|
27
|
+
type ReactHookFormContract<TContractLike extends ContractLike> = [
|
|
28
|
+
FormInput<ResolveContract<TContractLike>>
|
|
29
|
+
] extends [never] ? never : TContractLike;
|
|
23
30
|
/**
|
|
24
31
|
* React Hook Form options accepted by the Beignet adapter.
|
|
25
32
|
*/
|
|
@@ -36,9 +43,9 @@ type RhfFormOptions<TContract extends HttpContractConfig> = Omit<UseFormProps<Fo
|
|
|
36
43
|
resolverEnabled?: boolean;
|
|
37
44
|
};
|
|
38
45
|
/**
|
|
39
|
-
* React Hook Form adapter for one contract.
|
|
46
|
+
* React Hook Form adapter methods for one compatible contract.
|
|
40
47
|
*/
|
|
41
|
-
|
|
48
|
+
type ReactHookFormContractAdapterShape<TContract extends HttpContractConfig> = {
|
|
42
49
|
/**
|
|
43
50
|
* Generate `UseFormProps` for any React Hook Form usage.
|
|
44
51
|
*/
|
|
@@ -48,15 +55,23 @@ export type ReactHookFormContractAdapter<TContract extends HttpContractConfig> =
|
|
|
48
55
|
*/
|
|
49
56
|
useForm: (props?: RhfFormOptions<TContract>) => UseFormReturn<FormInput<TContract>, unknown, FormOutput<TContract>>;
|
|
50
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* React Hook Form adapter for one contract whose body schema input is
|
|
60
|
+
* object-shaped.
|
|
61
|
+
*/
|
|
62
|
+
export type ReactHookFormContractAdapter<TContract extends HttpContractConfig> = [
|
|
63
|
+
FormInput<TContract>
|
|
64
|
+
] extends [never] ? never : ReactHookFormContractAdapterShape<TContract>;
|
|
51
65
|
/**
|
|
52
66
|
* Create a React Hook Form adapter factory.
|
|
53
67
|
*
|
|
54
68
|
* Mirrors the React integration pattern used by `createReactQuery()` and `createNuqs()`:
|
|
55
69
|
* create the adapter once, then bind individual contracts with `rhf(contract)`.
|
|
56
|
-
* Contracts must declare a body schema
|
|
57
|
-
* disable the generated resolver through
|
|
70
|
+
* Contracts must declare a body schema whose input is object-shaped. After
|
|
71
|
+
* binding, callers may override or disable the generated resolver through
|
|
72
|
+
* `formOptions(...)` or `useForm(...)`.
|
|
58
73
|
*/
|
|
59
|
-
export declare function createReactHookForm(): <TContractLike extends ContractLike>(contract: TContractLike) => ReactHookFormContractAdapter<ResolveContract<TContractLike>>;
|
|
74
|
+
export declare function createReactHookForm(): <TContractLike extends ContractLike>(contract: ReactHookFormContract<TContractLike>) => ReactHookFormContractAdapter<ResolveContract<TContractLike>>;
|
|
60
75
|
/**
|
|
61
76
|
* Root-level form error for `form.setError("root", ...)`.
|
|
62
77
|
*/
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,EAEpB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,YAAY,EACjB,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AAEzB
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ function createReactHookFormAdapter(contract) {
|
|
|
14
14
|
const bodySchema = resolvedContract.body;
|
|
15
15
|
const { resolverEnabled = true, resolver: resolverOverride, ...rest } = props ?? {};
|
|
16
16
|
const resolver = resolverOverride ??
|
|
17
|
-
(
|
|
17
|
+
(resolverEnabled
|
|
18
18
|
? standardSchemaResolver(bodySchema)
|
|
19
19
|
: undefined);
|
|
20
20
|
return {
|
|
@@ -33,8 +33,9 @@ function createReactHookFormAdapter(contract) {
|
|
|
33
33
|
*
|
|
34
34
|
* Mirrors the React integration pattern used by `createReactQuery()` and `createNuqs()`:
|
|
35
35
|
* create the adapter once, then bind individual contracts with `rhf(contract)`.
|
|
36
|
-
* Contracts must declare a body schema
|
|
37
|
-
* disable the generated resolver through
|
|
36
|
+
* Contracts must declare a body schema whose input is object-shaped. After
|
|
37
|
+
* binding, callers may override or disable the generated resolver through
|
|
38
|
+
* `formOptions(...)` or `useForm(...)`.
|
|
38
39
|
*/
|
|
39
40
|
export function createReactHookForm() {
|
|
40
41
|
return function rhf(contract) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EACL,oBAAoB,GAErB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAML,eAAe,GAEhB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAEL,OAAO,IAAI,UAAU,GAGtB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EACL,oBAAoB,GAErB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAML,eAAe,GAEhB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAEL,OAAO,IAAI,UAAU,GAGtB,MAAM,iBAAiB,CAAC;AAmGzB,SAAS,0BAA0B,CACjC,QAA8C;IAE9C,MAAM,gBAAgB,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAEnD,IAAI,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,OAAO,gBAAgB,CAAC,IAAI,kCAAkC;YAC5D,iFAAiF;YACjF,6EAA6E,CAChF,CAAC;IACJ,CAAC;IAKD,SAAS,WAAW,CAClB,KAAsD;QAEtD,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAuC,CAAC;QAE5E,MAAM,EACJ,eAAe,GAAG,IAAI,EACtB,QAAQ,EAAE,gBAAgB,EAC1B,GAAG,IAAI,EACR,GAAG,KAAK,IAAI,EAAE,CAAC;QAEhB,MAAM,QAAQ,GACZ,gBAAgB;YAChB,CAAC,eAAe;gBACd,CAAC,CAAC,sBAAsB,CAAyB,UAAU,CAAC;gBAC5D,CAAC,CAAC,SAAS,CAAC,CAAC;QAEjB,OAAO;YACL,GAAI,IAA6C;YACjD,QAAQ;SACT,CAAC;IACJ,CAAC;IAED,SAAS,OAAO,CACd,KAAsD;QAEtD,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QACnC,OAAO,UAAU,CAAyB,OAAO,CAAC,CAAC;IACrD,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AAClC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,SAAS,GAAG,CACjB,QAA8C;QAE9C,OAAO,0BAA0B,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC,CAAC;AACJ,CAAC;AAUD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAc,EACd,QAAgB,EAChB,SAAiC;IAEjC,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,oBAAoB,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC;KAC1D,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
package/skills/forms/SKILL.md
CHANGED
|
@@ -26,9 +26,14 @@ routes, providers, infra adapters, `server/`, or `app-context.ts`.
|
|
|
26
26
|
|
|
27
27
|
## Contract Requirements
|
|
28
28
|
|
|
29
|
-
React Hook Form binds to the contract body schema.
|
|
30
|
-
`.body(...)
|
|
31
|
-
|
|
29
|
+
React Hook Form binds to the contract body schema. TypeScript rejects contracts
|
|
30
|
+
without `.body(...)` because there are no form fields or body validation to
|
|
31
|
+
infer; untyped JavaScript calls receive the same failure at runtime.
|
|
32
|
+
|
|
33
|
+
The body schema input must be an object with named fields. TypeScript rejects
|
|
34
|
+
scalar and array body inputs. Put scalar values or lists under a field, for
|
|
35
|
+
example `.body(z.object({ tags: z.array(z.string()) }))`, when the endpoint is
|
|
36
|
+
backed by a form.
|
|
32
37
|
|
|
33
38
|
Keep shared form DTOs in `features/<feature>/schemas.ts`, then reuse them from
|
|
34
39
|
contracts, use cases, tests, and components.
|
|
@@ -119,6 +124,8 @@ codes declared on the contract with `.errors(...)`.
|
|
|
119
124
|
params, headers, tenant IDs, and auth-derived values belong in mutation
|
|
120
125
|
variables or app context, not form fields.
|
|
121
126
|
- Contracts without `.body(...)` are not form contracts.
|
|
127
|
+
- Scalar and array request bodies are not form contracts; wrap their values in
|
|
128
|
+
an object with named fields.
|
|
122
129
|
- `handleSubmit` returns parsed schema output. For transforms that change field
|
|
123
130
|
types, submit `form.getValues()` after validation when the contract client
|
|
124
131
|
expects body input.
|
package/src/index.ts
CHANGED
|
@@ -22,10 +22,15 @@ import {
|
|
|
22
22
|
} from "react-hook-form";
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* Keep only object-shaped React Hook Form field values. Arrays are objects in
|
|
26
|
+
* JavaScript, but they do not define the named field-value record expected by
|
|
27
|
+
* this adapter.
|
|
27
28
|
*/
|
|
28
|
-
type
|
|
29
|
+
type ObjectFieldValues<T> = [T] extends [FieldValues]
|
|
30
|
+
? Extract<T, readonly unknown[]> extends never
|
|
31
|
+
? T
|
|
32
|
+
: never
|
|
33
|
+
: never;
|
|
29
34
|
|
|
30
35
|
/**
|
|
31
36
|
* Live form field values inferred from the contract body schema input.
|
|
@@ -36,8 +41,8 @@ type AsFieldValues<T> = T extends FieldValues ? T : T & FieldValues;
|
|
|
36
41
|
*/
|
|
37
42
|
type FormInput<TContract extends HttpContractConfig> =
|
|
38
43
|
TContract["body"] extends StandardSchemaV1
|
|
39
|
-
?
|
|
40
|
-
:
|
|
44
|
+
? ObjectFieldValues<InferInput<TContract["body"]>>
|
|
45
|
+
: never;
|
|
41
46
|
|
|
42
47
|
/**
|
|
43
48
|
* Validated submit values inferred from the contract body schema output.
|
|
@@ -47,7 +52,16 @@ type FormInput<TContract extends HttpContractConfig> =
|
|
|
47
52
|
type FormOutput<TContract extends HttpContractConfig> =
|
|
48
53
|
TContract["body"] extends StandardSchemaV1
|
|
49
54
|
? InferOutput<TContract["body"]>
|
|
50
|
-
:
|
|
55
|
+
: never;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Contract input accepted by the React Hook Form adapter.
|
|
59
|
+
*/
|
|
60
|
+
type ReactHookFormContract<TContractLike extends ContractLike> = [
|
|
61
|
+
FormInput<ResolveContract<TContractLike>>,
|
|
62
|
+
] extends [never]
|
|
63
|
+
? never
|
|
64
|
+
: TContractLike;
|
|
51
65
|
|
|
52
66
|
/**
|
|
53
67
|
* React Hook Form options accepted by the Beignet adapter.
|
|
@@ -74,28 +88,39 @@ type RhfFormOptions<TContract extends HttpContractConfig> = Omit<
|
|
|
74
88
|
};
|
|
75
89
|
|
|
76
90
|
/**
|
|
77
|
-
* React Hook Form adapter for one contract.
|
|
91
|
+
* React Hook Form adapter methods for one compatible contract.
|
|
92
|
+
*/
|
|
93
|
+
type ReactHookFormContractAdapterShape<TContract extends HttpContractConfig> = {
|
|
94
|
+
/**
|
|
95
|
+
* Generate `UseFormProps` for any React Hook Form usage.
|
|
96
|
+
*/
|
|
97
|
+
formOptions: (
|
|
98
|
+
props?: RhfFormOptions<TContract>,
|
|
99
|
+
) => UseFormProps<FormInput<TContract>, unknown, FormOutput<TContract>>;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Convenience wrapper around `useForm(formOptions(props))`.
|
|
103
|
+
*/
|
|
104
|
+
useForm: (
|
|
105
|
+
props?: RhfFormOptions<TContract>,
|
|
106
|
+
) => UseFormReturn<FormInput<TContract>, unknown, FormOutput<TContract>>;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* React Hook Form adapter for one contract whose body schema input is
|
|
111
|
+
* object-shaped.
|
|
78
112
|
*/
|
|
79
113
|
export type ReactHookFormContractAdapter<TContract extends HttpContractConfig> =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
*/
|
|
84
|
-
formOptions: (
|
|
85
|
-
props?: RhfFormOptions<TContract>,
|
|
86
|
-
) => UseFormProps<FormInput<TContract>, unknown, FormOutput<TContract>>;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Convenience wrapper around `useForm(formOptions(props))`.
|
|
90
|
-
*/
|
|
91
|
-
useForm: (
|
|
92
|
-
props?: RhfFormOptions<TContract>,
|
|
93
|
-
) => UseFormReturn<FormInput<TContract>, unknown, FormOutput<TContract>>;
|
|
94
|
-
};
|
|
114
|
+
[FormInput<TContract>] extends [never]
|
|
115
|
+
? never
|
|
116
|
+
: ReactHookFormContractAdapterShape<TContract>;
|
|
95
117
|
|
|
96
118
|
function createReactHookFormAdapter<TContractLike extends ContractLike>(
|
|
97
|
-
contract: TContractLike
|
|
98
|
-
): ReactHookFormContractAdapter<ResolveContract<TContractLike
|
|
119
|
+
contract: ReactHookFormContract<TContractLike>,
|
|
120
|
+
): ReactHookFormContractAdapter<ResolveContract<TContractLike>>;
|
|
121
|
+
function createReactHookFormAdapter<TContractLike extends ContractLike>(
|
|
122
|
+
contract: ReactHookFormContract<TContractLike>,
|
|
123
|
+
): ReactHookFormContractAdapterShape<ResolveContract<TContractLike>> {
|
|
99
124
|
const resolvedContract = resolveContract(contract);
|
|
100
125
|
|
|
101
126
|
if (!resolvedContract.body) {
|
|
@@ -112,10 +137,7 @@ function createReactHookFormAdapter<TContractLike extends ContractLike>(
|
|
|
112
137
|
function formOptions(
|
|
113
138
|
props?: RhfFormOptions<ResolveContract<TContractLike>>,
|
|
114
139
|
): UseFormProps<Input, unknown, Output> {
|
|
115
|
-
const bodySchema = resolvedContract.body as
|
|
116
|
-
| StandardSchemaV1<Input, Output>
|
|
117
|
-
| null
|
|
118
|
-
| undefined;
|
|
140
|
+
const bodySchema = resolvedContract.body as StandardSchemaV1<Input, Output>;
|
|
119
141
|
|
|
120
142
|
const {
|
|
121
143
|
resolverEnabled = true,
|
|
@@ -125,7 +147,7 @@ function createReactHookFormAdapter<TContractLike extends ContractLike>(
|
|
|
125
147
|
|
|
126
148
|
const resolver =
|
|
127
149
|
resolverOverride ??
|
|
128
|
-
(
|
|
150
|
+
(resolverEnabled
|
|
129
151
|
? standardSchemaResolver<Input, unknown, Output>(bodySchema)
|
|
130
152
|
: undefined);
|
|
131
153
|
|
|
@@ -150,12 +172,13 @@ function createReactHookFormAdapter<TContractLike extends ContractLike>(
|
|
|
150
172
|
*
|
|
151
173
|
* Mirrors the React integration pattern used by `createReactQuery()` and `createNuqs()`:
|
|
152
174
|
* create the adapter once, then bind individual contracts with `rhf(contract)`.
|
|
153
|
-
* Contracts must declare a body schema
|
|
154
|
-
* disable the generated resolver through
|
|
175
|
+
* Contracts must declare a body schema whose input is object-shaped. After
|
|
176
|
+
* binding, callers may override or disable the generated resolver through
|
|
177
|
+
* `formOptions(...)` or `useForm(...)`.
|
|
155
178
|
*/
|
|
156
179
|
export function createReactHookForm() {
|
|
157
180
|
return function rhf<TContractLike extends ContractLike>(
|
|
158
|
-
contract: TContractLike
|
|
181
|
+
contract: ReactHookFormContract<TContractLike>,
|
|
159
182
|
): ReactHookFormContractAdapter<ResolveContract<TContractLike>> {
|
|
160
183
|
return createReactHookFormAdapter(contract);
|
|
161
184
|
};
|