@reformer/core 11.0.0-beta.8 → 11.0.0-beta.9

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.
Files changed (224) hide show
  1. package/README.md +91 -73
  2. package/dist/behaviors-value-HA_Q03hc.js +108 -0
  3. package/dist/behaviors.js +370 -242
  4. package/dist/{create-model-BBRHhHwK.js → create-model-BvcbNPgL.js} +6 -6
  5. package/dist/date-utils-90-qZbse.js +43 -0
  6. package/dist/{en-CjAqn7h0.js → en-HP8XEych.js} +6 -3
  7. package/dist/form/aggregate-signals.d.ts +21 -63
  8. package/dist/form/behaviors/collections.d.ts +28 -7
  9. package/dist/form/behaviors/context.d.ts +19 -2
  10. package/dist/form/behaviors/index.d.ts +3 -3
  11. package/dist/form/behaviors/internals.d.ts +9 -2
  12. package/dist/form/behaviors/node.d.ts +25 -2
  13. package/dist/form/behaviors/operators.d.ts +17 -9
  14. package/dist/form/behaviors/schema-operators.d.ts +6 -4
  15. package/dist/form/behaviors/types.d.ts +12 -4
  16. package/dist/form/create-form.d.ts +47 -35
  17. package/dist/form/flow/flow.d.ts +87 -0
  18. package/dist/form/form-bundle.d.ts +45 -28
  19. package/dist/form/form-config-base.d.ts +46 -0
  20. package/dist/form/form-level.d.ts +121 -0
  21. package/dist/form/form-proxy-builder.d.ts +6 -22
  22. package/dist/form/index.d.ts +16 -21
  23. package/dist/form/nodes/form-array-node.d.ts +100 -0
  24. package/dist/form/nodes/form-control-node.d.ts +73 -0
  25. package/dist/form/nodes/form-group-node.d.ts +66 -0
  26. package/dist/form/nodes/node-kind.d.ts +29 -0
  27. package/dist/form/nodes/state/availability.d.ts +6 -0
  28. package/dist/form/nodes/state/component.d.ts +6 -0
  29. package/dist/form/nodes/state/index.d.ts +12 -0
  30. package/dist/form/nodes/state/interaction.d.ts +13 -0
  31. package/dist/form/nodes/state/issues.d.ts +9 -0
  32. package/dist/form/nodes/state/pending.d.ts +6 -0
  33. package/dist/form/schema/compile.d.ts +59 -0
  34. package/dist/form/schema/compiled.d.ts +143 -0
  35. package/dist/form/schema/row-key.d.ts +11 -0
  36. package/dist/form/schema-controller.d.ts +30 -14
  37. package/dist/form/signal-node-registry.d.ts +6 -7
  38. package/dist/form/submission.d.ts +93 -0
  39. package/dist/form/type-guards.d.ts +14 -73
  40. package/dist/form/types/contracts.d.ts +19 -92
  41. package/dist/form/types/field-config.d.ts +27 -0
  42. package/dist/form/types/index.d.ts +5 -57
  43. package/dist/form/types/nodes.d.ts +159 -0
  44. package/dist/form/types/schema-node.d.ts +139 -63
  45. package/dist/form/types/validation-schema.d.ts +23 -27
  46. package/dist/form/validation/context.d.ts +42 -7
  47. package/dist/form/validation/index.d.ts +9 -8
  48. package/dist/form/validation/operators.d.ts +22 -30
  49. package/dist/form/validation/run.d.ts +93 -18
  50. package/dist/form/validation/strategy.d.ts +41 -13
  51. package/dist/form/validation/types.d.ts +74 -20
  52. package/dist/form/validators/date-utils.d.ts +13 -1
  53. package/dist/form/validators/email.d.ts +8 -7
  54. package/dist/form/validators/file-type.d.ts +8 -7
  55. package/dist/form/validators/file-utils.d.ts +2 -1
  56. package/dist/form/validators/future-date.d.ts +8 -7
  57. package/dist/form/validators/index.d.ts +1 -1
  58. package/dist/form/validators/integer.d.ts +8 -7
  59. package/dist/form/validators/is-date.d.ts +11 -9
  60. package/dist/form/validators/is-number.d.ts +11 -9
  61. package/dist/form/validators/max-age.d.ts +13 -9
  62. package/dist/form/validators/max-date.d.ts +8 -7
  63. package/dist/form/validators/max-file-size.d.ts +8 -7
  64. package/dist/form/validators/max-files.d.ts +8 -7
  65. package/dist/form/validators/max-length.d.ts +14 -7
  66. package/dist/form/validators/max-total-file-size.d.ts +8 -7
  67. package/dist/form/validators/max.d.ts +10 -8
  68. package/dist/form/validators/min-age.d.ts +12 -9
  69. package/dist/form/validators/min-date.d.ts +8 -7
  70. package/dist/form/validators/min-file-size.d.ts +8 -7
  71. package/dist/form/validators/min-files.d.ts +11 -9
  72. package/dist/form/validators/min-length.d.ts +14 -7
  73. package/dist/form/validators/min.d.ts +10 -8
  74. package/dist/form/validators/multiple-of.d.ts +10 -8
  75. package/dist/form/validators/non-negative.d.ts +8 -7
  76. package/dist/form/validators/non-zero.d.ts +8 -7
  77. package/dist/form/validators/past-date.d.ts +8 -7
  78. package/dist/form/validators/pattern.d.ts +9 -8
  79. package/dist/form/validators/phone.d.ts +8 -7
  80. package/dist/form/validators/required.d.ts +11 -9
  81. package/dist/form/validators/url.d.ts +20 -13
  82. package/dist/form/validators/validation-issue.d.ts +8 -0
  83. package/dist/i18n/descriptor.d.ts +1 -1
  84. package/dist/i18n/en.json.d.ts +5 -1
  85. package/dist/i18n/locale-store.d.ts +7 -0
  86. package/dist/i18n/message-format.d.ts +24 -4
  87. package/dist/i18n/ru.json.d.ts +5 -1
  88. package/dist/i18n/translator.d.ts +11 -0
  89. package/dist/i18n/validation-message.d.ts +42 -7
  90. package/dist/i18n.d.ts +7 -12
  91. package/dist/i18n.js +66 -283
  92. package/dist/index.d.ts +16 -7
  93. package/dist/index.js +924 -2855
  94. package/dist/loader-BWkigpVe.js +52 -0
  95. package/dist/locale/en.js +1 -1
  96. package/dist/locale/en.json +4 -1
  97. package/dist/locale/ru.js +4 -1
  98. package/dist/locale/ru.json +4 -1
  99. package/dist/locale-CG54AU2Q.js +219 -0
  100. package/dist/locale.js +1 -1
  101. package/dist/model/behaviors-value.d.ts +24 -6
  102. package/dist/model/create-model.d.ts +2 -2
  103. package/dist/model/derived-registry.d.ts +1 -1
  104. package/dist/model/index.d.ts +11 -5
  105. package/dist/model/model-nodes.d.ts +79 -12
  106. package/dist/model/model-signals-proxy.d.ts +5 -6
  107. package/dist/model/model-value-proxy.d.ts +20 -15
  108. package/dist/model/safe-effect.d.ts +33 -1
  109. package/dist/model/types.d.ts +63 -13
  110. package/dist/model.js +20 -18
  111. package/dist/node-kind-C-r6ZMdo.js +15 -0
  112. package/dist/platforms/react/field-binding/field-adapter.d.ts +1 -1
  113. package/dist/platforms/react/field-binding/field-handle.d.ts +5 -2
  114. package/dist/platforms/react/form-bundle-context.d.ts +2 -2
  115. package/dist/platforms/react/hooks/types.d.ts +57 -38
  116. package/dist/platforms/react/hooks/use-form-bundle.d.ts +19 -4
  117. package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
  118. package/dist/platforms/react/hooks/use-form-validation.d.ts +12 -6
  119. package/dist/platforms/react/hooks/useArrayLength.d.ts +6 -6
  120. package/dist/platforms/react/hooks/useFormControl.d.ts +7 -9
  121. package/dist/platforms/react/hooks/useFormControlValue.d.ts +18 -19
  122. package/dist/platforms/react/hooks/useSignalSubscription.d.ts +9 -1
  123. package/dist/platforms/react/i18n/context.d.ts +6 -1
  124. package/dist/platforms/react/i18n/hooks.d.ts +16 -10
  125. package/dist/platforms/react/i18n/provider.d.ts +9 -2
  126. package/dist/platforms/react/index.d.ts +6 -4
  127. package/dist/react.d.ts +22 -0
  128. package/dist/react.js +505 -0
  129. package/dist/runtime-token.d.ts +1 -2
  130. package/dist/safe-effect-BbtX3KuN.js +643 -0
  131. package/dist/schema-controller-CpiO2JoK.js +143 -0
  132. package/dist/signals.d.ts +15 -9
  133. package/dist/signals.js +11 -6
  134. package/dist/strategy-CoFSemuP.js +375 -0
  135. package/dist/type-guards-D-N_5Ddh.js +19 -0
  136. package/dist/validation-issue-B9mjI8jz.js +7 -0
  137. package/dist/validation-message-Ced9MGxs.js +194 -0
  138. package/dist/validation.js +9 -9
  139. package/dist/validators/email.js +5 -5
  140. package/dist/validators/future-date.js +8 -8
  141. package/dist/validators/integer.js +3 -3
  142. package/dist/validators/is-date.js +4 -4
  143. package/dist/validators/is-number.js +4 -4
  144. package/dist/validators/max-age.js +8 -9
  145. package/dist/validators/max-date.js +8 -8
  146. package/dist/validators/max-length.js +7 -7
  147. package/dist/validators/max.js +4 -4
  148. package/dist/validators/min-age.js +10 -8
  149. package/dist/validators/min-date.js +3 -3
  150. package/dist/validators/min-length.js +5 -5
  151. package/dist/validators/min.js +4 -4
  152. package/dist/validators/multiple-of.js +11 -7
  153. package/dist/validators/non-negative.js +3 -3
  154. package/dist/validators/non-zero.js +3 -3
  155. package/dist/validators/past-date.js +8 -8
  156. package/dist/validators/pattern.js +5 -5
  157. package/dist/validators/phone.js +11 -9
  158. package/dist/validators/required.js +2 -2
  159. package/dist/validators/url.js +48 -12
  160. package/dist/validators.js +17 -17
  161. package/docs/llms/01-api-reference.md +60 -34
  162. package/docs/llms/02-quick-start.md +111 -80
  163. package/docs/llms/03-api-signatures.md +368 -153
  164. package/docs/llms/04-common-patterns.md +58 -56
  165. package/docs/llms/05-common-mistakes.md +69 -44
  166. package/docs/llms/06-troubleshooting.md +14 -9
  167. package/docs/llms/07-complete-import.md +74 -43
  168. package/docs/llms/08-form-types.md +11 -4
  169. package/docs/llms/09-formschema.md +129 -81
  170. package/docs/llms/10-arrays.md +106 -75
  171. package/docs/llms/11-async-watchfield.md +2 -2
  172. package/docs/llms/13-multi-step.md +134 -63
  173. package/docs/llms/14-extended-mistakes.md +20 -17
  174. package/docs/llms/15-project-structure.md +221 -74
  175. package/docs/llms/16-ui-components.md +55 -42
  176. package/docs/llms/17-nonexistent-api.md +199 -32
  177. package/docs/llms/18-conditional-fields.md +66 -32
  178. package/docs/llms/19-reading-values.md +15 -3
  179. package/docs/llms/20-compute-vs-watch.md +16 -10
  180. package/docs/llms/21-array-operations.md +51 -27
  181. package/docs/llms/22-cycle-detection.md +1 -1
  182. package/docs/llms/23-copy-from.md +2 -2
  183. package/docs/llms/24-sync-fields.md +6 -5
  184. package/docs/llms/25-reset-when.md +14 -7
  185. package/docs/llms/26-transform-value.md +4 -4
  186. package/docs/llms/27-revalidate-when.md +16 -15
  187. package/docs/llms/28-submit-and-reset.md +276 -128
  188. package/docs/llms/29-async-preload.md +39 -29
  189. package/docs/llms/30-type-safety-recipes.md +105 -51
  190. package/docs/llms/31-async-validator-debounce.md +96 -53
  191. package/docs/llms/33-validation-strategy.md +177 -65
  192. package/docs/llms/34-i18n.md +69 -43
  193. package/docs/llms/35-unified-contract.md +100 -39
  194. package/docs/llms/36-issue-messages.md +284 -0
  195. package/docs/llms/37-compiled-schema.md +332 -0
  196. package/docs/llms/38-form-flow.md +397 -0
  197. package/llms-index.json +1 -1
  198. package/llms.txt +6084 -3420
  199. package/package.json +23 -4
  200. package/dist/behaviors-value-C7dSEhCc.js +0 -134
  201. package/dist/date-utils-dTT_x_Be.js +0 -31
  202. package/dist/form/create-core-form.d.ts +0 -72
  203. package/dist/form/factories/index.d.ts +0 -6
  204. package/dist/form/factories/node-factory.d.ts +0 -189
  205. package/dist/form/form-submitter.d.ts +0 -121
  206. package/dist/form/nodes/array-node.d.ts +0 -276
  207. package/dist/form/nodes/field-node.d.ts +0 -297
  208. package/dist/form/nodes/form-node.d.ts +0 -326
  209. package/dist/form/nodes/group-node.d.ts +0 -284
  210. package/dist/form/nodes/model-array-node.d.ts +0 -126
  211. package/dist/form/nodes/subscription-manager.d.ts +0 -180
  212. package/dist/form/status-machine.d.ts +0 -115
  213. package/dist/form/types/deep-schema.d.ts +0 -107
  214. package/dist/form/types/form-proxy.d.ts +0 -141
  215. package/dist/form/unique-id.d.ts +0 -45
  216. package/dist/form/validation/config.d.ts +0 -73
  217. package/dist/form/validation/error-handler.d.ts +0 -166
  218. package/dist/form/validators/validation-error.d.ts +0 -8
  219. package/dist/index-BrNkQj1y.js +0 -105
  220. package/dist/loader-Dv_Hl7JC.js +0 -216
  221. package/dist/model-value-proxy-caTmAiUd.js +0 -419
  222. package/dist/node-BeV_lV5i.js +0 -100
  223. package/dist/strategy-DgzELWno.js +0 -204
  224. package/dist/validation-error-B-U3RsV0.js +0 -11
package/README.md CHANGED
@@ -4,7 +4,8 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@reformer/core.svg)](https://www.npmjs.com/package/@reformer/core)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- Reactive form state management library for React with signals-based architecture.
7
+ Reactive form state management on signals: a UI-agnostic core with React bindings in
8
+ `@reformer/core/react`.
8
9
 
9
10
  ## Playground
10
11
 
@@ -17,10 +18,12 @@ Full documentation is available at [https://alexandrbukhtatyy.github.io/ReFormer
17
18
  ## Features
18
19
 
19
20
  - Signals-based reactive state management
20
- - Declarative form validation
21
+ - Declarative form validation with typed rules and status results
22
+ - Validation issues without text: codes and params, texts from a form dictionary and the locale
21
23
  - Dynamic form behaviors
24
+ - Multi-step flows: step rules, transitions, `submit` with a result object
22
25
  - TypeScript support
23
- - Tree-shakeable exports
26
+ - Tree-shakeable exports; the root entry has no React dependency
24
27
 
25
28
  ## Installation
26
29
 
@@ -30,28 +33,45 @@ npm install @reformer/core@beta # Active development is underway, so you can try
30
33
 
31
34
  ## Quick Start
32
35
 
33
- ReFormer is built around the **M1 architecture**: a reactive `FormModel` owns the values, a layout
34
- schema binds field config (component / props) to the model's signals, and `createFormFromModel({ model, schema })`
35
- wires them into a typed form. Validation is a **separate** ambient contract — `defineValidationSchema`,
36
- run on demand by `validateModel(model, schema)` — so the layout schema carries no validators.
36
+ A reactive `FormModel` owns the values, a form schema — a tree of nodes — binds field config
37
+ (component / props) to the model's handles (`model.$.<field>`), and `createForm` assembles the
38
+ model, the form and the form level (`validate`, `submission`, step flow) in one call. Validation is a
39
+ **separate** contract — `defineValidation` — so the form schema carries no validators. A rule
40
+ reports a code, not a text: the text comes from a form dictionary (`defineIssueMessages`) on top of
41
+ the locale.
37
42
 
38
43
  ```tsx
39
- import { useMemo } from 'react';
40
44
  import {
41
- createModel,
42
- createFormFromModel,
43
- createLegacyForm,
44
- useFormControl,
45
- type FieldNode,
46
- type ValidationError,
45
+ createForm,
46
+ type FormControl,
47
+ type FormModel,
48
+ type FormSchemaNode,
49
+ type ValidationIssue,
47
50
  } from '@reformer/core';
48
- import { validate, cross, defineValidationSchema, validateModel } from '@reformer/core/validation';
51
+ import { useFormBundle, useFormControl, useIssueMessage } from '@reformer/core/react';
52
+ import { defineIssueMessages } from '@reformer/core/i18n';
53
+ import { validate, defineValidation } from '@reformer/core/validation';
49
54
  import { required, email, minLength } from '@reformer/core/validators';
50
55
  import { defineFormBehavior, onChange } from '@reformer/core/behaviors';
51
56
 
52
- // 0. Simple FormField component
53
- function FormField({ label, control }: { label: string; control: FieldNode<string> }) {
54
- const { value, errors, shouldShowError } = useFormControl(control);
57
+ // 1. Form data type — `interface` or `type`, both work
58
+ interface RegistrationForm {
59
+ username: string;
60
+ email: string;
61
+ password: string;
62
+ confirmPassword: string;
63
+ }
64
+
65
+ // 2. Issue texts by code — the form dictionary (built-in codes fall back to the locale)
66
+ const messages = defineIssueMessages({
67
+ 'username.required': 'Enter a username',
68
+ 'confirmPassword.mismatch': 'Passwords do not match',
69
+ });
70
+
71
+ // 3. Simple FormField component: the issue text comes from the resolver
72
+ function FormField({ label, control }: { label: string; control: FormControl<string> }) {
73
+ const { value, issues, shouldShowError } = useFormControl(control);
74
+ const message = useIssueMessage(messages);
55
75
 
56
76
  return (
57
77
  <div>
@@ -61,81 +81,69 @@ function FormField({ label, control }: { label: string; control: FieldNode<strin
61
81
  onChange={(e) => control.setValue(e.target.value)}
62
82
  onBlur={() => control.markAsTouched()}
63
83
  />
64
- {shouldShowError && <span className="error">{errors[0].message}</span>}
84
+ {shouldShowError && issues[0] && <span className="error">{message(issues[0])}</span>}
65
85
  </div>
66
86
  );
67
87
  }
68
88
 
69
- // 1. Define form interface
70
- interface RegistrationForm {
71
- username: string;
72
- email: string;
73
- password: string;
74
- confirmPassword: string;
75
- }
76
-
77
- // 2. Reactive model — the source of truth for values
78
- const model = createModel<RegistrationForm>({
79
- username: '',
80
- email: '',
81
- password: '',
82
- confirmPassword: '',
83
- });
84
-
85
- // 3. Layout schema — binds field config (component / props) to model signals
86
- // (`model.$.<field>`). Layout carries NO validators; rules live in the
87
- // validation schema below.
88
- const schema = {
89
+ // 4. Form schema — a tree of nodes. A field node binds field config (component / props)
90
+ // to a model handle (`model: model.$.<field>`); nested nodes live in `children`.
91
+ // The schema carries NO validators; rules live in the validation schema below.
92
+ const schema = (model: FormModel<RegistrationForm>): FormSchemaNode => ({
89
93
  children: [
90
- { value: model.$.username, component: Input },
91
- { value: model.$.email, component: Input },
92
- { value: model.$.password, component: Input },
93
- { value: model.$.confirmPassword, component: Input },
94
+ { model: model.$.username, component: 'input' },
95
+ { model: model.$.email, component: 'input' },
96
+ { model: model.$.password, component: 'input' },
97
+ { model: model.$.confirmPassword, component: 'input' },
94
98
  ],
95
- };
96
-
97
- // 4. Validation schema — a separate ambient contract from `@reformer/core/validation`,
98
- // run on demand (not reactive). Field rules go through `validate(sig, [rules])`;
99
- // cross-field rules through `cross(sig, snapshot => error | null)` where the snapshot
100
- // is `model.get()`. Rule factories (`required()`/…) come from `@reformer/core/validators`.
101
- const passwordsMatch = (f: RegistrationForm): ValidationError | null =>
102
- f.confirmPassword && f.password && f.confirmPassword !== f.password
103
- ? { code: 'mismatch', message: 'Passwords do not match' }
99
+ });
100
+
101
+ // 5. Validation schema — a separate contract from `@reformer/core/validation`.
102
+ // Field rules go through `validate(sig, [rules])`; cross-field rules through
103
+ // `cross(sig, snapshot => issue | null)` taken from the schema argument — the snapshot is the
104
+ // model value, its type is inferred. Rule factories come from `@reformer/core/validators`.
105
+ const passwordsMatch = (form: RegistrationForm): ValidationIssue | null =>
106
+ form.confirmPassword && form.password && form.confirmPassword !== form.password
107
+ ? { code: 'confirmPassword.mismatch' }
104
108
  : null;
105
109
 
106
- const validationSchema = defineValidationSchema<RegistrationForm>(({ model }) => {
107
- validate(model.$.username, [required(), minLength(2)]);
110
+ const validation = defineValidation<RegistrationForm>(({ model, cross }) => {
111
+ validate(model.$.username, [required({ code: 'username.required' }), minLength(2)]);
108
112
  validate(model.$.email, [required(), email()]);
109
113
  validate(model.$.password, [required(), minLength(8)]);
110
114
  validate(model.$.confirmPassword, [required()]);
111
115
  cross(model.$.confirmPassword, passwordsMatch);
112
116
  });
113
117
 
114
- // 5. Behavior — declarative reactive logic, run by `createForm({ behavior })`.
115
- // Validation runs on demand, so the stale cross-field error on `confirmPassword`
116
- // is cleared the moment `password` changes — a behavior→validation bridge.
118
+ // 6. Behavior — declarative reactive logic, run by `createForm({ behavior })`.
119
+ // Validation runs on demand, so the stale cross-field issue on `confirmPassword`
120
+ // is cleared the moment `password` changes.
117
121
  const behavior = defineFormBehavior<RegistrationForm>(({ model, form }) => {
118
122
  onChange(model.$.password, () => {
119
- form.confirmPassword.clearErrors();
123
+ form.confirmPassword.clearIssues();
120
124
  });
121
125
  });
122
126
 
123
- // 6. Registration form component
124
- function RegistrationFormExample() {
125
- const form = useMemo(
126
- () => createFormFromModel<RegistrationForm>({ model, schema, behavior }),
127
- []
128
- );
127
+ // 7. One assembly: model, form, behavior, validation and the form level
128
+ const createRegistration = () =>
129
+ createForm<RegistrationForm>({
130
+ initial: { username: '', email: '', password: '', confirmPassword: '' },
131
+ schema,
132
+ behavior,
133
+ validation,
134
+ });
129
135
 
130
- const handleSubmit = async (e: React.FormEvent) => {
131
- e.preventDefault();
132
- form.touchAll();
133
- // Validate the whole model against the validation schema (sync + async) on demand;
134
- // errors route into the form nodes, so the UI highlights the offending fields.
135
- const valid = await validateModel(model, validationSchema);
136
- if (valid) {
137
- console.log('Form data:', model.get());
138
- }
136
+ function RegistrationFormExample() {
137
+ // useFormBundle calls the factory exactly once and keeps the bundle stable between renders
138
+ const bundle = useFormBundle(createRegistration);
139
+ const { form } = bundle;
140
+
141
+ const handleSubmit = async (event: React.FormEvent) => {
142
+ event.preventDefault();
143
+ // One run of all the rules (sync + async). Issues land on the form nodes and, on a refusal,
144
+ // the whole form is marked touched; the handler is called only when the status is 'valid'.
145
+ const outcome = await bundle.submission.submit((values) => console.log('Form data:', values));
146
+ if (outcome.status === 'error') console.warn('Could not check the form, try again');
139
147
  };
140
148
 
141
149
  return (
@@ -150,6 +158,16 @@ function RegistrationFormExample() {
150
158
  }
151
159
  ```
152
160
 
161
+ Submission is a separate object of the bundle: `bundle.submission.submit(handler)` returns a
162
+ `SubmitResult`: `submitted` (with the handler's result), `invalid` / `error` / `cancelled` (with
163
+ the validation result; the handler was not called) or `failed` (the handler threw). The handler
164
+ receives the whole model, disabled fields included; `bundle.submission.submitting` is `true` while
165
+ it runs. Multi-step forms declare their steps in the same call —
166
+ `createForm({ flow: { steps: [{ id, rules }] } })` — and navigate with `bundle.flow.next()`.
167
+ Outside React, the owner of the bundle releases it with `bundle.dispose()` when the form is no
168
+ longer needed: the behavior, the node subscriptions and the live validation stop, the model keeps
169
+ its values.
170
+
153
171
  ## License
154
172
 
155
173
  MIT
@@ -0,0 +1,108 @@
1
+ import { effect as s } from "@preact/signals-core";
2
+ import { h as i } from "./safe-effect-BbtX3KuN.js";
3
+ function m(n, l, r, t) {
4
+ return s(() => {
5
+ const e = n.map((a) => a.value);
6
+ if (t?.when && !t.when(...e)) return;
7
+ const u = r(...e);
8
+ l.peek() !== u && (l.value = u);
9
+ });
10
+ }
11
+ function h(n, l, r) {
12
+ return s(() => {
13
+ const t = n.value;
14
+ if (r?.when && !r.when()) return;
15
+ const e = r?.transform ? r.transform(t) : t;
16
+ l.peek() !== e && (l.value = e);
17
+ });
18
+ }
19
+ function w(n, l, r) {
20
+ let t = !0;
21
+ return s(() => {
22
+ const e = n.value;
23
+ t && (t = !1, !r?.immediate) || l(e);
24
+ });
25
+ }
26
+ function k(n, l) {
27
+ let r = !1;
28
+ const t = i(), e = s(() => {
29
+ n.value, !r && t.schedule(() => {
30
+ const u = l(n.peek());
31
+ if (n.peek() !== u) {
32
+ r = !0;
33
+ try {
34
+ n.value = u;
35
+ } finally {
36
+ r = !1;
37
+ }
38
+ }
39
+ });
40
+ });
41
+ return () => {
42
+ e(), t.cancelAll();
43
+ };
44
+ }
45
+ function y(n, l, ...[r]) {
46
+ const t = r?.resetValue ?? null, e = i(), u = s(() => {
47
+ l(), e.schedule(() => {
48
+ l() && n.peek() !== t && (n.value = t);
49
+ });
50
+ });
51
+ return () => {
52
+ u(), e.cancelAll();
53
+ };
54
+ }
55
+ function A(n, l, r) {
56
+ const t = r?.transform;
57
+ let e = !1, u = !0;
58
+ const a = i(), o = s(() => {
59
+ n.value, !e && (a.cancelAll(), a.schedule(() => {
60
+ e = !0;
61
+ try {
62
+ const c = n.peek(), f = t ? t(c) : c;
63
+ l.peek() !== f && (l.value = f);
64
+ } finally {
65
+ e = !1;
66
+ }
67
+ }));
68
+ }), v = s(() => {
69
+ if (l.value, u) {
70
+ u = !1;
71
+ return;
72
+ }
73
+ e || (a.cancelAll(), a.schedule(() => {
74
+ e = !0;
75
+ try {
76
+ const c = l.peek();
77
+ n.peek() !== c && (n.value = c);
78
+ } finally {
79
+ e = !1;
80
+ }
81
+ }));
82
+ });
83
+ return () => {
84
+ o(), v(), a.cancelAll();
85
+ };
86
+ }
87
+ function x(n, l) {
88
+ let r = !0;
89
+ const t = i(), e = s(() => {
90
+ if (n.forEach((u) => u.value), r) {
91
+ r = !1;
92
+ return;
93
+ }
94
+ t.schedule(() => l());
95
+ });
96
+ return () => {
97
+ e(), t.cancelAll();
98
+ };
99
+ }
100
+ export {
101
+ h as a,
102
+ x as b,
103
+ m as c,
104
+ y as r,
105
+ A as s,
106
+ k as t,
107
+ w
108
+ };