@stapel/forms-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/MODULE.md +172 -0
  3. package/README.md +207 -0
  4. package/dist/api/export.d.ts +66 -0
  5. package/dist/api/export.d.ts.map +1 -0
  6. package/dist/api/export.js +138 -0
  7. package/dist/api/export.js.map +1 -0
  8. package/dist/api/extensions.d.ts +8 -0
  9. package/dist/api/extensions.d.ts.map +1 -0
  10. package/dist/api/extensions.js +8 -0
  11. package/dist/api/extensions.js.map +1 -0
  12. package/dist/api/formsApi.d.ts +113 -0
  13. package/dist/api/formsApi.d.ts.map +1 -0
  14. package/dist/api/formsApi.js +81 -0
  15. package/dist/api/formsApi.js.map +1 -0
  16. package/dist/api/generated/schema.d.ts +987 -0
  17. package/dist/api/generated/schema.d.ts.map +1 -0
  18. package/dist/api/generated/schema.js +2 -0
  19. package/dist/api/generated/schema.js.map +1 -0
  20. package/dist/api/types.d.ts +222 -0
  21. package/dist/api/types.d.ts.map +1 -0
  22. package/dist/api/types.js +14 -0
  23. package/dist/api/types.js.map +1 -0
  24. package/dist/default/ConfigField.d.ts +32 -0
  25. package/dist/default/ConfigField.d.ts.map +1 -0
  26. package/dist/default/ConfigField.js +101 -0
  27. package/dist/default/ConfigField.js.map +1 -0
  28. package/dist/default/ErrorAlert.d.ts +9 -0
  29. package/dist/default/ErrorAlert.d.ts.map +1 -0
  30. package/dist/default/ErrorAlert.js +27 -0
  31. package/dist/default/ErrorAlert.js.map +1 -0
  32. package/dist/default/FormBuilderPane.d.ts +36 -0
  33. package/dist/default/FormBuilderPane.d.ts.map +1 -0
  34. package/dist/default/FormBuilderPane.js +70 -0
  35. package/dist/default/FormBuilderPane.js.map +1 -0
  36. package/dist/default/FormsListPane.d.ts +18 -0
  37. package/dist/default/FormsListPane.d.ts.map +1 -0
  38. package/dist/default/FormsListPane.js +28 -0
  39. package/dist/default/FormsListPane.js.map +1 -0
  40. package/dist/default/ResponsesPane.d.ts +21 -0
  41. package/dist/default/ResponsesPane.d.ts.map +1 -0
  42. package/dist/default/ResponsesPane.js +118 -0
  43. package/dist/default/ResponsesPane.js.map +1 -0
  44. package/dist/default/StapelForm.d.ts +76 -0
  45. package/dist/default/StapelForm.d.ts.map +1 -0
  46. package/dist/default/StapelForm.js +118 -0
  47. package/dist/default/StapelForm.js.map +1 -0
  48. package/dist/default/fields.d.ts +11 -0
  49. package/dist/default/fields.d.ts.map +1 -0
  50. package/dist/default/fields.js +273 -0
  51. package/dist/default/fields.js.map +1 -0
  52. package/dist/default/index.d.ts +42 -0
  53. package/dist/default/index.d.ts.map +1 -0
  54. package/dist/default/index.js +37 -0
  55. package/dist/default/index.js.map +1 -0
  56. package/dist/default/slots.d.ts +59 -0
  57. package/dist/default/slots.d.ts.map +1 -0
  58. package/dist/default/slots.js +25 -0
  59. package/dist/default/slots.js.map +1 -0
  60. package/dist/default/theme.d.ts +23 -0
  61. package/dist/default/theme.d.ts.map +1 -0
  62. package/dist/default/theme.js +38 -0
  63. package/dist/default/theme.js.map +1 -0
  64. package/dist/default/types.d.ts +12 -0
  65. package/dist/default/types.d.ts.map +1 -0
  66. package/dist/default/types.js +2 -0
  67. package/dist/default/types.js.map +1 -0
  68. package/dist/flows/errors.d.ts +12 -0
  69. package/dist/flows/errors.d.ts.map +1 -0
  70. package/dist/flows/errors.js +13 -0
  71. package/dist/flows/errors.js.map +1 -0
  72. package/dist/flows/registry.d.ts +22 -0
  73. package/dist/flows/registry.d.ts.map +1 -0
  74. package/dist/flows/registry.js +20 -0
  75. package/dist/flows/registry.js.map +1 -0
  76. package/dist/headless/FormBuilder.d.ts +100 -0
  77. package/dist/headless/FormBuilder.d.ts.map +1 -0
  78. package/dist/headless/FormBuilder.js +278 -0
  79. package/dist/headless/FormBuilder.js.map +1 -0
  80. package/dist/headless/FormFill.d.ts +88 -0
  81. package/dist/headless/FormFill.d.ts.map +1 -0
  82. package/dist/headless/FormFill.js +279 -0
  83. package/dist/headless/FormFill.js.map +1 -0
  84. package/dist/headless/FormList.d.ts +34 -0
  85. package/dist/headless/FormList.d.ts.map +1 -0
  86. package/dist/headless/FormList.js +37 -0
  87. package/dist/headless/FormList.js.map +1 -0
  88. package/dist/headless/FormsProvider.d.ts +19 -0
  89. package/dist/headless/FormsProvider.d.ts.map +1 -0
  90. package/dist/headless/FormsProvider.js +15 -0
  91. package/dist/headless/FormsProvider.js.map +1 -0
  92. package/dist/headless/ResponsesTable.d.ts +82 -0
  93. package/dist/headless/ResponsesTable.d.ts.map +1 -0
  94. package/dist/headless/ResponsesTable.js +195 -0
  95. package/dist/headless/ResponsesTable.js.map +1 -0
  96. package/dist/i18n/errorsMap.d.ts +10 -0
  97. package/dist/i18n/errorsMap.d.ts.map +1 -0
  98. package/dist/i18n/errorsMap.js +20 -0
  99. package/dist/i18n/errorsMap.js.map +1 -0
  100. package/dist/i18n/es.d.ts +7 -0
  101. package/dist/i18n/es.d.ts.map +1 -0
  102. package/dist/i18n/es.js +118 -0
  103. package/dist/i18n/es.js.map +1 -0
  104. package/dist/i18n/generated/errors.es.gen.d.ts +16 -0
  105. package/dist/i18n/generated/errors.es.gen.d.ts.map +1 -0
  106. package/dist/i18n/generated/errors.es.gen.js +79 -0
  107. package/dist/i18n/generated/errors.es.gen.js.map +1 -0
  108. package/dist/i18n/generated/errors.gen.d.ts +485 -0
  109. package/dist/i18n/generated/errors.gen.d.ts.map +1 -0
  110. package/dist/i18n/generated/errors.gen.js +246 -0
  111. package/dist/i18n/generated/errors.gen.js.map +1 -0
  112. package/dist/i18n/generated/errors.ru.gen.d.ts +16 -0
  113. package/dist/i18n/generated/errors.ru.gen.d.ts.map +1 -0
  114. package/dist/i18n/generated/errors.ru.gen.js +79 -0
  115. package/dist/i18n/generated/errors.ru.gen.js.map +1 -0
  116. package/dist/i18n/keys.d.ts +141 -0
  117. package/dist/i18n/keys.d.ts.map +1 -0
  118. package/dist/i18n/keys.js +233 -0
  119. package/dist/i18n/keys.js.map +1 -0
  120. package/dist/i18n/ru.d.ts +7 -0
  121. package/dist/i18n/ru.d.ts.map +1 -0
  122. package/dist/i18n/ru.js +127 -0
  123. package/dist/i18n/ru.js.map +1 -0
  124. package/dist/index.d.ts +61 -0
  125. package/dist/index.d.ts.map +1 -0
  126. package/dist/index.js +55 -0
  127. package/dist/index.js.map +1 -0
  128. package/dist/model/context.d.ts +11 -0
  129. package/dist/model/context.d.ts.map +1 -0
  130. package/dist/model/context.js +16 -0
  131. package/dist/model/context.js.map +1 -0
  132. package/dist/model/mutations.d.ts +135 -0
  133. package/dist/model/mutations.d.ts.map +1 -0
  134. package/dist/model/mutations.js +223 -0
  135. package/dist/model/mutations.js.map +1 -0
  136. package/dist/model/queries.d.ts +59 -0
  137. package/dist/model/queries.d.ts.map +1 -0
  138. package/dist/model/queries.js +117 -0
  139. package/dist/model/queries.js.map +1 -0
  140. package/dist/model/queryKeys.d.ts +28 -0
  141. package/dist/model/queryKeys.d.ts.map +1 -0
  142. package/dist/model/queryKeys.js +23 -0
  143. package/dist/model/queryKeys.js.map +1 -0
  144. package/dist/model/runtime.d.ts +28 -0
  145. package/dist/model/runtime.d.ts.map +1 -0
  146. package/dist/model/runtime.js +14 -0
  147. package/dist/model/runtime.js.map +1 -0
  148. package/dist/widgets/configForms.d.ts +112 -0
  149. package/dist/widgets/configForms.d.ts.map +1 -0
  150. package/dist/widgets/configForms.js +211 -0
  151. package/dist/widgets/configForms.js.map +1 -0
  152. package/dist/widgets/registry.d.ts +83 -0
  153. package/dist/widgets/registry.d.ts.map +1 -0
  154. package/dist/widgets/registry.js +27 -0
  155. package/dist/widgets/registry.js.map +1 -0
  156. package/dist/widgets/validate.d.ts +57 -0
  157. package/dist/widgets/validate.d.ts.map +1 -0
  158. package/dist/widgets/validate.js +173 -0
  159. package/dist/widgets/validate.js.map +1 -0
  160. package/llms.txt +124 -0
  161. package/manifest.json +1038 -0
  162. package/package.json +108 -0
  163. package/src/analytics/generated/events.json +7 -0
  164. package/src/api/export.ts +195 -0
  165. package/src/api/extensions.ts +7 -0
  166. package/src/api/formsApi.ts +329 -0
  167. package/src/api/generated/schema.ts +989 -0
  168. package/src/api/types.ts +276 -0
  169. package/src/default/ConfigField.tsx +227 -0
  170. package/src/default/ErrorAlert.tsx +46 -0
  171. package/src/default/FormBuilderPane.tsx +414 -0
  172. package/src/default/FormsListPane.tsx +144 -0
  173. package/src/default/ResponsesPane.tsx +332 -0
  174. package/src/default/StapelForm.tsx +377 -0
  175. package/src/default/fields.tsx +454 -0
  176. package/src/default/index.ts +71 -0
  177. package/src/default/slots.ts +78 -0
  178. package/src/default/theme.tsx +65 -0
  179. package/src/default/types.ts +12 -0
  180. package/src/flows/errors.ts +16 -0
  181. package/src/flows/registry.ts +32 -0
  182. package/src/headless/FormBuilder.tsx +453 -0
  183. package/src/headless/FormFill.tsx +396 -0
  184. package/src/headless/FormList.tsx +74 -0
  185. package/src/headless/FormsProvider.tsx +20 -0
  186. package/src/headless/ResponsesTable.tsx +341 -0
  187. package/src/i18n/errorsMap.ts +31 -0
  188. package/src/i18n/es.ts +140 -0
  189. package/src/i18n/generated/errors.es.gen.ts +85 -0
  190. package/src/i18n/generated/errors.gen.ts +277 -0
  191. package/src/i18n/generated/errors.json +583 -0
  192. package/src/i18n/generated/errors.ru.gen.ts +85 -0
  193. package/src/i18n/keys.ts +264 -0
  194. package/src/i18n/ru.ts +150 -0
  195. package/src/index.ts +184 -0
  196. package/src/model/context.tsx +28 -0
  197. package/src/model/mutations.ts +387 -0
  198. package/src/model/queries.ts +154 -0
  199. package/src/model/queryKeys.ts +61 -0
  200. package/src/model/runtime.ts +48 -0
  201. package/src/widgets/registry.ts +101 -0
  202. package/src/widgets/validate.ts +224 -0
  203. package/tsconfig.json +26 -0
@@ -0,0 +1,173 @@
1
+ /** The `error.400.feature_*` keys this mirror can raise — a subset of
2
+ * `stapel_attributes.errors.ATTRIBUTES_ERRORS`, listed so the i18n bundle can
3
+ * be checked against it. */
4
+ export const FEATURE_ERROR_CODES = [
5
+ "error.400.feature_below_minimum",
6
+ "error.400.feature_above_maximum",
7
+ "error.400.feature_not_in_options",
8
+ "error.400.feature_invalid_type",
9
+ "error.400.feature_invalid_format",
10
+ "error.400.feature_mandatory_missing",
11
+ ];
12
+ function featureError(field, code, extra = {}) {
13
+ return {
14
+ code,
15
+ // `field` is what the fleet's `useFieldError` routes on; `slug` and
16
+ // `feature` are what the attributes catalogue's message templates
17
+ // interpolate. The adapter keeps all three server-side (delta note 1), so
18
+ // the mirror does too.
19
+ params: {
20
+ field: field.slug,
21
+ slug: field.slug,
22
+ feature: field.name ?? field.slug,
23
+ ...extra,
24
+ },
25
+ status: 400,
26
+ message: undefined,
27
+ language: undefined,
28
+ };
29
+ }
30
+ function num(config, key) {
31
+ const raw = config[key];
32
+ return typeof raw === "number" && Number.isFinite(raw) ? raw : undefined;
33
+ }
34
+ /** The declared choice values of an options-bearing config, or `undefined`
35
+ * when the kind has none. Accepts both the bare-scalar and the
36
+ * `{value, label}` option shapes the attributes types allow. */
37
+ export function optionValues(field) {
38
+ const raw = (field.config ?? {})["options"];
39
+ if (!Array.isArray(raw) || raw.length === 0)
40
+ return undefined;
41
+ return raw.map((option) => option !== null && typeof option === "object" && "value" in option
42
+ ? option.value
43
+ : option);
44
+ }
45
+ /** True when the answer is "nothing was entered" — the one shape the
46
+ * mandatory rule fires on. An explicit `false` is an ANSWER for a `bool`, not
47
+ * an absence, which is why this is not a falsiness check. */
48
+ export function isBlank(value) {
49
+ if (value === undefined || value === null)
50
+ return true;
51
+ if (typeof value === "string")
52
+ return value.trim().length === 0;
53
+ if (Array.isArray(value))
54
+ return value.length === 0;
55
+ return false;
56
+ }
57
+ /**
58
+ * Validate one answer against its field's config. Returns the mirrored
59
+ * refusal, or `undefined` when this side of the wire is satisfied.
60
+ *
61
+ * `header` fields are skipped outright: the engine regenerates a header's DAO
62
+ * from its config and REJECTS an answer to one (backend delta note 1), so a
63
+ * header has nothing to validate and must never be given a value.
64
+ */
65
+ export function validateFieldValue(field, value) {
66
+ if (field.kind === "header")
67
+ return undefined;
68
+ const config = field.config ?? {};
69
+ if (isBlank(value)) {
70
+ return field.mandatory === true
71
+ ? featureError(field, "error.400.feature_mandatory_missing")
72
+ : // An optional field left blank is complete, and no later rule may
73
+ // fire on an absent value — a `minLength` must not turn "did not
74
+ // answer" into "answered too short".
75
+ undefined;
76
+ }
77
+ // ── choices ────────────────────────────────────────────────────────────
78
+ const options = optionValues(field);
79
+ if (options !== undefined && config["allowCustom"] !== true) {
80
+ const answers = Array.isArray(value) ? value : [value];
81
+ const stray = answers.find((answer) => !options.some((option) => option === answer));
82
+ if (stray !== undefined) {
83
+ return featureError(field, "error.400.feature_not_in_options", {
84
+ ref_value: stray,
85
+ });
86
+ }
87
+ }
88
+ // ── multi-select cardinality ───────────────────────────────────────────
89
+ if (field.kind === "select") {
90
+ const count = Array.isArray(value) ? value.length : 1;
91
+ const minSelected = num(config, "minSelected");
92
+ const maxSelected = num(config, "maxSelected");
93
+ if (minSelected !== undefined && count < minSelected) {
94
+ return featureError(field, "error.400.feature_below_minimum", {
95
+ min: minSelected,
96
+ });
97
+ }
98
+ if (maxSelected !== undefined && count > maxSelected) {
99
+ return featureError(field, "error.400.feature_above_maximum", {
100
+ max: maxSelected,
101
+ });
102
+ }
103
+ return undefined;
104
+ }
105
+ // ── numeric range ──────────────────────────────────────────────────────
106
+ if (field.kind === "int" || field.kind === "float") {
107
+ const asNumber = typeof value === "number" ? value : Number(value);
108
+ if (!Number.isFinite(asNumber)) {
109
+ return featureError(field, "error.400.feature_invalid_type");
110
+ }
111
+ if (field.kind === "int" && !Number.isInteger(asNumber)) {
112
+ return featureError(field, "error.400.feature_invalid_type");
113
+ }
114
+ const min = num(config, "min");
115
+ const max = num(config, "max");
116
+ if (min !== undefined && asNumber < min) {
117
+ return featureError(field, "error.400.feature_below_minimum", { min });
118
+ }
119
+ if (max !== undefined && asNumber > max) {
120
+ return featureError(field, "error.400.feature_above_maximum", { max });
121
+ }
122
+ return undefined;
123
+ }
124
+ // ── string length + pattern ────────────────────────────────────────────
125
+ if (field.kind === "string") {
126
+ const text = String(value);
127
+ const minLength = num(config, "minLength");
128
+ const maxLength = num(config, "maxLength");
129
+ if (minLength !== undefined && text.length < minLength) {
130
+ return featureError(field, "error.400.feature_below_minimum", {
131
+ min_length: minLength,
132
+ });
133
+ }
134
+ if (maxLength !== undefined && text.length > maxLength) {
135
+ return featureError(field, "error.400.feature_above_maximum", {
136
+ max_length: maxLength,
137
+ });
138
+ }
139
+ const pattern = config["pattern"];
140
+ if (typeof pattern === "string" && pattern.length > 0) {
141
+ let re;
142
+ try {
143
+ re = new RegExp(pattern);
144
+ }
145
+ catch {
146
+ // An admin-authored pattern that JS cannot compile (a Python-only
147
+ // construct) is NOT the respondent's problem: skip the mirror and
148
+ // let the server, which compiled it, be the one to refuse.
149
+ re = undefined;
150
+ }
151
+ if (re !== undefined && !re.test(text)) {
152
+ return featureError(field, "error.400.feature_invalid_format");
153
+ }
154
+ }
155
+ return undefined;
156
+ }
157
+ return undefined;
158
+ }
159
+ /**
160
+ * Validate a whole answer set. Returns `{slug: FlowError}` for the fields that
161
+ * failed — the exact shape `FormFillBag.fieldErrors` carries, so a server
162
+ * response can be merged into it without a translation step.
163
+ */
164
+ export function validateAnswers(fields, values) {
165
+ const errors = {};
166
+ for (const field of fields) {
167
+ const error = validateFieldValue(field, values[field.slug]);
168
+ if (error !== undefined)
169
+ errors[field.slug] = error;
170
+ }
171
+ return errors;
172
+ }
173
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/widgets/validate.ts"],"names":[],"mappings":"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"}
package/llms.txt ADDED
@@ -0,0 +1,124 @@
1
+ # @stapel/forms-react 0.1.0
2
+
3
+ Headless React flow pair for stapel-forms (contract >=0.2 <0.3) — business + state, zero visual opinion.
4
+ Built on @stapel/core: typed client + StapelApiError envelope, auth token refresh,
5
+ verification-403 interception, i18n engine, analytics facade, TanStack Query layer.
6
+
7
+ ## The one right way (do this, the rest is a review/lint smell)
8
+ - No raw fetch/axios. The client is injected via <FormsProvider>/StapelConfigProvider;
9
+ every hook and flow already carries auth, refresh, and the error envelope.
10
+ - Render errors, never try/catch them: a flow's state carries FlowError{code,params};
11
+ render `t(code, params)` and branch on `explainFormsError(code)` remediation.
12
+ - Server state = the use* hooks (query layer); keys come only from formsQueryKeys.
13
+ - User strings = i18n keys (registerFormsI18n); never string literals.
14
+ - Sign-in UI = a headless flow component; copy it (shadcn-style) to restyle.
15
+
16
+ ## Layers
17
+ api (typed client) · model (hooks, session) · flows (machines) · headless · i18n
18
+
19
+ ## Documented flows (flows.json — canonical id, steps, endpoints)
20
+
21
+ ## Operations (typed; use the named op, never a path string)
22
+ Request/response schema names are in manifest.json + the generated types.
23
+ Paths are relative to `/forms/api/v1/`.
24
+ - forms_api_v1_field_kinds_retrieve: GET /field-kinds
25
+ - forms_api_v1_forms_create: POST /forms
26
+ - forms_api_v1_forms_destroy: DELETE /forms/{form_id}
27
+ - forms_api_v1_forms_draft_update: PUT /forms/{form_id}/draft
28
+ - forms_api_v1_forms_list: GET /forms
29
+ - forms_api_v1_forms_partial_update: PATCH /forms/{form_id}
30
+ - forms_api_v1_forms_publish_create: POST /forms/{form_id}/publish
31
+ - forms_api_v1_forms_retrieve: GET /forms/{form_id}
32
+ - forms_api_v1_forms_rotate_link_create: POST /forms/{form_id}/rotate-link
33
+ - forms_api_v1_forms_state_create: POST /forms/{form_id}/state
34
+ - forms_api_v1_forms_submissions_export_retrieve: GET /forms/{form_id}/submissions/export
35
+ - forms_api_v1_forms_submissions_list: GET /forms/{form_id}/submissions
36
+ - forms_api_v1_forms_versions_list: GET /forms/{form_id}/versions
37
+ - forms_api_v1_public_retrieve: GET /public/{public_id}/
38
+ - forms_api_v1_public_submissions_create: POST /public/{public_id}/submissions/
39
+ - forms_api_v1_submissions_destroy: DELETE /submissions/{submission_id}
40
+ - forms_api_v1_submissions_resend_create: POST /submissions/{submission_id}/resend
41
+ - forms_api_v1_submissions_retrieve: GET /submissions/{submission_id}
42
+
43
+ ## Query hooks (server state; keys come only from the key factory)
44
+ - useCreateForm (mutation) → createForm
45
+ - useDeleteForm (mutation) → deleteForm
46
+ - useDeleteSubmission (mutation) → deleteSubmission
47
+ - useFieldKinds (query) → listFieldKinds
48
+ - useForm (query) → getForm
49
+ - useForms (query) → listForms
50
+ - useFormVersions (query) → listVersions
51
+ - usePublicForm (query) → getPublicForm
52
+ - usePublishForm (mutation) → publish
53
+ - useResendSubmission (mutation) → resendSubmission
54
+ - useRotateLink (mutation) → rotateLink
55
+ - useSaveDraft (mutation) → putDraft
56
+ - useSetFormState (mutation) → setState
57
+ - useSubmission (query) → getSubmission
58
+ - useSubmissions (query) → listSubmissions
59
+ - useSubmitForm (mutation) → submit
60
+ - useUpdateForm (mutation) → patchForm
61
+
62
+ ## Errors (render t(code, params); UX from remediation)
63
+ 75 keys (full catalog: manifest.json §errors). By remediation: fix_input 41 · retry 14 · verify 7 · contact_support 6 · wait_and_retry 6 · reauthenticate 1.
64
+ Param-bearing keys (interpolation slots matter):
65
+ - error.400.description_too_long [400] → fix_input {max_length}
66
+ - error.400.description_too_short [400] → fix_input {min_length}
67
+ - error.400.feature_above_maximum [400] → fix_input {feature}
68
+ - error.400.feature_below_minimum [400] → fix_input {feature}
69
+ - error.400.feature_invalid_config [400] → fix_input {feature}
70
+ - error.400.feature_invalid_format [400] → fix_input {feature}
71
+ - error.400.feature_invalid_type [400] → fix_input {feature}
72
+ - error.400.feature_mandatory_missing [400] → fix_input {feature}
73
+ - error.400.feature_not_allowed [400] → fix_input {feature}
74
+ - error.400.feature_not_in_options [400] → fix_input {feature}
75
+ - error.400.feature_unknown [400] → fix_input {feature}
76
+ - error.400.feature_unknown_type [400] → fix_input {feature}
77
+ - error.400.field.blank [400] → fix_input {field}
78
+ - error.400.field.does_not_exist [400] → fix_input {field}
79
+ - error.400.field.invalid [400] → fix_input {field}
80
+ - error.400.field.invalid_choice [400] → fix_input {field}
81
+ - error.400.field.max_length [400] → fix_input {field,max_length}
82
+ - error.400.field.max_value [400] → fix_input {field,max_value}
83
+ - error.400.field.min_length [400] → fix_input {field,min_length}
84
+ - error.400.field.min_value [400] → fix_input {field,min_value}
85
+ - error.400.field.null [400] → fix_input {field}
86
+ - error.400.field.required [400] → fix_input {field}
87
+ - error.400.field.unique [400] → fix_input {field}
88
+ - error.400.forms_duplicate_slug [400] → fix_input {slug}
89
+ - error.400.forms_kind_not_allowed [400] → fix_input {kind}
90
+ - error.429.rate_limit [429] → wait_and_retry {retry_after_minutes}
91
+
92
+ ## Analytics events (typed; defineEvent → events.json, drift-gated)
93
+ - (no app defineEvent() in this pair — its analytic events are the
94
+ auto-instrumented flow funnels below)
95
+
96
+ ## Flow funnels (auto-instrumented: flow.<id>.<step> {phase})
97
+
98
+ ```tsx
99
+ // Typed event + tracked() click (the one right way; §3.1).
100
+ const planSelected = defineEvent({
101
+ name: "pricing.plan.selected",
102
+ description: "User picked a plan",
103
+ props: { plan: prop.oneOf(["free", "pro", "team"], "Plan code") },
104
+ });
105
+ const { tracked } = useTracked();
106
+ <Button onClick={tracked(planSelected, { plan }, startCheckout)} />
107
+ // A click that STEPS a flow machine is already instrumented — mark it
108
+ // data-analytics="flow" instead; tracked() on top double-counts (§3.2).
109
+ ```
110
+
111
+ ## Demos (defineDemo → manifest.demos; compiled, linted, rendered examples)
112
+ - forms.builder → <FormBuilder> [default] demo/FormBuilder.demo.tsx
113
+ - forms.fill → <FormFill> [default|closed|outage] demo/FormFill.demo.tsx
114
+ - forms.list → <FormList> [default] demo/FormList.demo.tsx
115
+ - forms.provider → <FormsProvider> [default] demo/Forms.demo.tsx
116
+ - forms.responses → <ResponsesTable> [default|empty] demo/ResponsesTable.demo.tsx
117
+ Each source file is the canonical usage snippet (open the default variant).
118
+
119
+ ## Snippets
120
+ ```tsx
121
+ // Error rendering + remediation branch (one pattern for every pair).
122
+ const r = explainFormsError(err.code); // 'wait_and_retry' | 'verify' | ...
123
+ return <Alert action={r}>{t(err.code, err.params)}</Alert>;
124
+ ```