@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,264 @@
1
+ import type { I18nDictionary, I18nEngine } from "@stapel/core";
2
+ import { FORMS_ERROR_CODES, formsErrorBundleEn } from "./generated/errors.gen.js";
3
+
4
+ /**
5
+ * forms-react's own translation KEYS (frontend-standard §4.2): headless
6
+ * components never render literal strings — hosts resolve these via core's
7
+ * i18n engine (`useT`). Backend error codes flow through the SAME contour: a
8
+ * `StapelApiError.code` is already a key, so the default bundle below ships
9
+ * English fallbacks for the backend error codes (generated — all 75 of them
10
+ * since stapel-forms 0.2.0, including the stapel_attributes family) and the
11
+ * pair's own UI keys. All UI keys live under the `forms.` namespace.
12
+ *
13
+ * ── What is NOT an i18n key here ───────────────────────────────────────────
14
+ *
15
+ * A form's FIELD LABELS are admin-authored content carried in the schema
16
+ * (`FeatureDef.name`), not keys. They render verbatim in whatever language
17
+ * the admin typed them. Multi-language form CONTENT is a v2 fork (spec §10) —
18
+ * translating a customer's question text is a different problem from
19
+ * translating the pair's chrome, and pretending otherwise would put
20
+ * user-generated strings through a bundle that can never contain them.
21
+ */
22
+ export const FORMS_I18N_KEYS = {
23
+ unknownError: "forms.error.unknown",
24
+
25
+ // ── fill: load + refusal states ──────────────────────────────────────────
26
+ fillLoading: "forms.fill.loading",
27
+ fillRetry: "forms.fill.retry",
28
+ fillLoadFailed: "forms.fill.load_failed",
29
+ fillNotFound: "forms.fill.not_found",
30
+ fillClosed: "forms.fill.closed",
31
+ fillSuperseded: "forms.fill.superseded",
32
+ fillUnsupportedField: "forms.fill.unsupported_field",
33
+ fillRequired: "forms.fill.required",
34
+ fillSubmit: "forms.fill.submit",
35
+ fillSubmitting: "forms.fill.submitting",
36
+ fillThanks: "forms.fill.thanks",
37
+ fillOptionalHint: "forms.fill.optional_hint",
38
+ fillBoolYes: "forms.fill.bool_yes",
39
+ fillBoolNo: "forms.fill.bool_no",
40
+ fillSelectPlaceholder: "forms.fill.select_placeholder",
41
+ fillUnlimited: "forms.fill.unlimited",
42
+
43
+ // ── fill: why the submit button is off ───────────────────────────────────
44
+ submitBlockedDone: "forms.submit.blocked.done",
45
+ submitBlockedInFlight: "forms.submit.blocked.in_flight",
46
+ submitBlockedUnsupported: "forms.submit.blocked.unsupported_kind",
47
+
48
+ // ── builder ──────────────────────────────────────────────────────────────
49
+ builderTitle: "forms.builder.title",
50
+ builderAddField: "forms.builder.add_field",
51
+ builderRemoveField: "forms.builder.remove_field",
52
+ builderMoveUp: "forms.builder.move_up",
53
+ builderMoveDown: "forms.builder.move_down",
54
+ builderFieldSlug: "forms.builder.field_slug",
55
+ builderFieldLabel: "forms.builder.field_label",
56
+ builderFieldRequired: "forms.builder.field_required",
57
+ builderFieldKind: "forms.builder.field_kind",
58
+ builderSave: "forms.builder.save",
59
+ builderPublish: "forms.builder.publish",
60
+ builderSaving: "forms.builder.blocked.saving",
61
+ builderPublishing: "forms.builder.blocked.publishing",
62
+ builderNoChanges: "forms.builder.blocked.no_changes",
63
+ builderEmptySchema: "forms.builder.blocked.empty_schema",
64
+ builderUnsavedDraft: "forms.builder.blocked.unsaved_draft",
65
+ builderBuilderLess: "forms.builder.builder_less",
66
+ builderKindUnregistered: "forms.builder.kind_unregistered",
67
+ builderKindsFailed: "forms.builder.kinds_failed",
68
+ builderNoKinds: "forms.builder.no_kinds",
69
+ builderUnsupportedConfig: "forms.builder.unsupported_config",
70
+ builderEmpty: "forms.builder.empty",
71
+ builderMetaTitle: "forms.builder.meta_title",
72
+ builderMetaDescription: "forms.builder.meta_description",
73
+ builderMetaSubmitLabel: "forms.builder.meta_submit_label",
74
+ builderMetaConfirmation: "forms.builder.meta_confirmation",
75
+ builderStateOpen: "forms.builder.state_open",
76
+ builderStateClosed: "forms.builder.state_closed",
77
+ builderStateDraft: "forms.builder.state_draft",
78
+ builderRotateLink: "forms.builder.rotate_link",
79
+ builderPublicLink: "forms.builder.public_link",
80
+
81
+ // ── responses ────────────────────────────────────────────────────────────
82
+ responsesTitle: "forms.responses.title",
83
+ responsesEmpty: "forms.responses.empty",
84
+ responsesLoadFailed: "forms.responses.load_failed",
85
+ responsesSubmittedAt: "forms.responses.submitted_at",
86
+ responsesRespondent: "forms.responses.respondent",
87
+ responsesAnonymous: "forms.responses.anonymous",
88
+ responsesVersion: "forms.responses.version",
89
+ responsesAllVersions: "forms.responses.all_versions",
90
+ responsesNext: "forms.responses.next",
91
+ responsesPrev: "forms.responses.prev",
92
+ responsesAtEnd: "forms.responses.blocked.at_end",
93
+ responsesAtStart: "forms.responses.blocked.at_start",
94
+ responsesDelete: "forms.responses.delete",
95
+ responsesDeleteConfirm: "forms.responses.delete_confirm",
96
+ responsesResend: "forms.responses.resend",
97
+ responsesResendSent: "forms.responses.resend_sent",
98
+ responsesResendOverride: "forms.responses.resend_override",
99
+ responsesResendOverrideHint: "forms.responses.resend_override_hint",
100
+ responsesExport: "forms.responses.export",
101
+ responsesExporting: "forms.responses.exporting",
102
+ responsesErased: "forms.responses.erased",
103
+ responsesDetail: "forms.responses.detail",
104
+ responsesClose: "forms.responses.close",
105
+
106
+ // ── form list ────────────────────────────────────────────────────────────
107
+ listTitle: "forms.list.title",
108
+ listEmpty: "forms.list.empty",
109
+ listLoadFailed: "forms.list.load_failed",
110
+ listCreate: "forms.list.create",
111
+ listNewTitle: "forms.list.new_title",
112
+ listSubmissionCount: "forms.list.submission_count",
113
+ } as const;
114
+
115
+ export type FormsI18nKey =
116
+ (typeof FORMS_I18N_KEYS)[keyof typeof FORMS_I18N_KEYS];
117
+
118
+ /**
119
+ * The `error.400.feature_*` codes, read off the generated registry.
120
+ *
121
+ * stapel-forms 0.2.0 put the stapel_attributes family INTO its published
122
+ * contract (75 keys, 12 of them owned by `stapel_attributes`), so the English
123
+ * the pair used to hand-carry now arrives through `gen:errors` like every other
124
+ * backend code and has been deleted. This list stays only so the locale bundles
125
+ * can be checked against it — derived, never typed out, so it cannot fall
126
+ * behind the registry.
127
+ *
128
+ * The ru/es strings ARE still authored by the pair: attributes ships English
129
+ * only (it has no `translations/` directory), so those 12 keys can appear in no
130
+ * locale catalog until upstream localizes them — logged as stapel-forms
131
+ * MODULE.md §12.6. `gen:errors` runs for this module with
132
+ * `ERRORS_LOCALE_EXEMPT_OWNERS=stapel_attributes`, which is why the generated
133
+ * ru/es bundles are `Partial` and the authored strings below them are load-bearing.
134
+ */
135
+ export const FEATURE_ERROR_KEYS: readonly string[] = FORMS_ERROR_CODES.filter(
136
+ (code) => code.startsWith("error.400.feature_")
137
+ );
138
+
139
+ /**
140
+ * English fallback bundle for forms-react UI keys + backend error codes.
141
+ * The generated `formsErrorBundleEn` (from stapel-forms's error registry,
142
+ * `pnpm gen:errors`) is spread FIRST so every backend `error.*` key has a
143
+ * fallback — a `StapelApiError.code` never renders as a raw key. The
144
+ * attributes family follows, then the pair's own copy.
145
+ */
146
+ export const formsI18nBundleEn: I18nDictionary = {
147
+ // Backend error codes — generated en fallbacks (coverage by construction,
148
+ // and since stapel-forms 0.2.0 that includes the error.400.feature_* family
149
+ // the pair used to hand-carry).
150
+ ...formsErrorBundleEn,
151
+
152
+ // forms-react UI
153
+ "forms.error.unknown": "Something went wrong. Please try again.",
154
+
155
+ "forms.fill.loading": "Loading the form…",
156
+ "forms.fill.retry": "Try again",
157
+ "forms.fill.load_failed":
158
+ "We could not load this form. This is a problem on our side, not with your link.",
159
+ "forms.fill.not_found": "This form link is not valid.",
160
+ "forms.fill.closed": "This form is closed and is no longer accepting responses.",
161
+ "forms.fill.superseded":
162
+ "The form changed while you were filling it in. Please review your answers and submit again.",
163
+ "forms.fill.unsupported_field":
164
+ "This field type ({kind}) cannot be shown in this version of the app.",
165
+ "forms.fill.required": "Required",
166
+ "forms.fill.submit": "Submit",
167
+ "forms.fill.submitting": "Submitting…",
168
+ "forms.fill.thanks": "Thank you — your response has been recorded.",
169
+ "forms.fill.optional_hint": "Optional",
170
+ "forms.fill.bool_yes": "Yes",
171
+ "forms.fill.bool_no": "No",
172
+ "forms.fill.select_placeholder": "Choose…",
173
+ "forms.fill.unlimited": "Unlimited",
174
+
175
+ "forms.submit.blocked.done": "You have already submitted this form.",
176
+ "forms.submit.blocked.in_flight": "Submitting your response…",
177
+ "forms.submit.blocked.unsupported_kind":
178
+ "This form uses a field type this app cannot show ({kinds}), so it cannot be submitted safely.",
179
+
180
+ "forms.builder.title": "Form builder",
181
+ "forms.builder.add_field": "Add field",
182
+ "forms.builder.remove_field": "Remove field",
183
+ "forms.builder.move_up": "Move up",
184
+ "forms.builder.move_down": "Move down",
185
+ "forms.builder.field_slug": "Key",
186
+ "forms.builder.field_label": "Label",
187
+ "forms.builder.field_required": "Required",
188
+ "forms.builder.field_kind": "Type",
189
+ "forms.builder.save": "Save draft",
190
+ "forms.builder.publish": "Publish",
191
+ "forms.builder.blocked.saving": "Saving the draft…",
192
+ "forms.builder.blocked.publishing": "Publishing…",
193
+ "forms.builder.blocked.no_changes": "Nothing has changed since the last save.",
194
+ "forms.builder.blocked.empty_schema": "Add at least one field before publishing.",
195
+ "forms.builder.blocked.unsaved_draft":
196
+ "Save the draft first — publishing would release the previously saved version.",
197
+ "forms.builder.builder_less":
198
+ "This field type has no editable options here. Its configuration is authored through the draft API.",
199
+ "forms.builder.kind_unregistered":
200
+ "This deployment does not recognise this field type, so it cannot be configured or rendered here. The field is kept so it is not silently dropped from the schema.",
201
+ "forms.builder.kinds_failed":
202
+ "We could not load the list of field types, so no fields can be added right now.",
203
+ "forms.builder.no_kinds":
204
+ "This deployment has no configurable field types available.",
205
+ "forms.builder.unsupported_config":
206
+ "Some options of this field ({keys}) cannot be edited here yet.",
207
+ "forms.builder.empty": "This form has no fields yet.",
208
+ "forms.builder.meta_title": "Form title",
209
+ "forms.builder.meta_description": "Description",
210
+ "forms.builder.meta_submit_label": "Submit button text",
211
+ "forms.builder.meta_confirmation": "Confirmation message",
212
+ "forms.builder.state_open": "Open",
213
+ "forms.builder.state_closed": "Closed",
214
+ "forms.builder.state_draft": "Draft",
215
+ "forms.builder.rotate_link": "Rotate public link",
216
+ "forms.builder.public_link": "Public link",
217
+
218
+ "forms.responses.title": "Responses",
219
+ "forms.responses.empty": "No responses yet.",
220
+ "forms.responses.load_failed": "We could not load the responses.",
221
+ "forms.responses.submitted_at": "Submitted",
222
+ "forms.responses.respondent": "Respondent",
223
+ "forms.responses.anonymous": "Anonymous",
224
+ "forms.responses.version": "Version",
225
+ "forms.responses.all_versions": "All versions",
226
+ "forms.responses.next": "Next",
227
+ "forms.responses.prev": "Previous",
228
+ "forms.responses.blocked.at_end": "This is the last page.",
229
+ "forms.responses.blocked.at_start": "This is the first page.",
230
+ "forms.responses.delete": "Delete",
231
+ "forms.responses.delete_confirm": "Delete this response permanently?",
232
+ "forms.responses.resend": "Resend",
233
+ "forms.responses.resend_sent": "Sent to {count} destination(s).",
234
+ "forms.responses.resend_override": "Send to specific addresses instead",
235
+ "forms.responses.resend_override_hint":
236
+ "These replace the form's configured recipients for this one send.",
237
+ "forms.responses.export": "Export CSV",
238
+ "forms.responses.exporting": "Exporting… ({pages} page(s))",
239
+ "forms.responses.erased": "Erased",
240
+ "forms.responses.detail": "Response detail",
241
+ "forms.responses.close": "Close",
242
+
243
+ "forms.list.title": "Forms",
244
+ "forms.list.empty": "No forms in this workspace yet.",
245
+ "forms.list.load_failed": "We could not load the forms.",
246
+ "forms.list.create": "New form",
247
+ "forms.list.new_title": "Untitled form",
248
+ "forms.list.submission_count": "{count} response(s)",
249
+ };
250
+
251
+ /**
252
+ * Register forms-react's key bundle into a core i18n engine (call once at
253
+ * startup). Registers under the given locale (default `"en"`); a later
254
+ * `loadLocale` from stapel-translate can layer localized overrides on top.
255
+ *
256
+ * MERGE-PRIORITY CONVENTION (i18n-shipping.md §3): registration order is
257
+ * override priority — later wins per key. A HOST bundle registered AFTER this
258
+ * call overrides any pair text without a fork. The `ru`/`es` locales ship as
259
+ * the `./i18n/ru` and `./i18n/es` subpaths so a host that does not need them
260
+ * never carries the strings.
261
+ */
262
+ export function registerFormsI18n(engine: I18nEngine, locale = "en"): void {
263
+ engine.registerBundle(locale, formsI18nBundleEn);
264
+ }
package/src/i18n/ru.ts ADDED
@@ -0,0 +1,150 @@
1
+ import type { I18nDictionary, I18nEngine } from "@stapel/core";
2
+ import { formsErrorBundleRu } from "./generated/errors.ru.gen.js";
3
+
4
+ export { formsErrorBundleRu } from "./generated/errors.ru.gen.js";
5
+
6
+ /**
7
+ * Russian bundle for forms-react — the pair's `ru` locale, shipped as the
8
+ * `@stapel/forms-react/i18n/ru` subpath (i18n-shipping.md §2) so the locale is
9
+ * opt-in: hosts that don't register it never carry these strings (the main
10
+ * entry does not import this module — gated by size-limit + the bundle-purity
11
+ * test).
12
+ *
13
+ * Composition mirrors `formsI18nBundleEn`: the GENERATED backend error texts
14
+ * (from stapel-forms's `translations/errors.ru.json`, merged under
15
+ * stapel-core's cross-cutting catalogue — `pnpm gen:errors`) are spread first
16
+ * for coverage by construction; the 12 `stapel_attributes`-owned keys the generated
17
+ * bundle cannot carry follow (attributes ships English only — no
18
+ * `translations/` directory — so `gen:errors` runs with
19
+ * `ERRORS_LOCALE_EXEMPT_OWNERS=stapel_attributes` and emits a `Partial`
20
+ * bundle; upstream localization is stapel-forms MODULE.md §12.6); then the ru UI copy.
21
+ *
22
+ * PROVENANCE, stated rather than implied: stapel-forms' ru/es catalogues ship
23
+ * `origin=seed:authored` and are UNREVIEWED (backend delta note 10) — authored
24
+ * rather than machine-translated, but approved by nobody. The pair-authored
25
+ * strings below are the same grade. Neither is a claim of review.
26
+ */
27
+ const ATTRIBUTES_ERRORS_RU: Readonly<Record<string, string>> = {
28
+ "error.400.feature_below_minimum": "Значение меньше минимального для «{feature}»",
29
+ "error.400.feature_above_maximum": "Значение больше максимального для «{feature}»",
30
+ "error.400.feature_not_in_options":
31
+ "Значение отсутствует среди допустимых вариантов для «{feature}»",
32
+ "error.400.feature_invalid_type": "Неверный тип значения для «{feature}»",
33
+ "error.400.feature_invalid_format": "Неверный формат значения для «{feature}»",
34
+ "error.400.feature_mandatory_missing": "Поле «{feature}» обязательно для заполнения",
35
+ "error.400.feature_unknown_type": "Неизвестный тип поля «{feature}»",
36
+ "error.400.feature_not_allowed": "Поле «{feature}» здесь недопустимо",
37
+ "error.400.feature_unknown": "Неизвестное поле «{feature}»",
38
+ "error.400.feature_invalid_config": "Неверная конфигурация поля «{feature}»",
39
+ "error.400.description_too_short": "Описание должно содержать не менее {min_length} символов",
40
+ "error.400.description_too_long": "Описание должно содержать не более {max_length} символов",
41
+ };
42
+
43
+ export const formsI18nBundleRu: I18nDictionary = {
44
+ ...formsErrorBundleRu,
45
+ ...ATTRIBUTES_ERRORS_RU,
46
+
47
+ "forms.error.unknown": "Что-то пошло не так. Попробуйте ещё раз.",
48
+
49
+ "forms.fill.loading": "Загрузка формы…",
50
+ "forms.fill.retry": "Повторить",
51
+ "forms.fill.load_failed":
52
+ "Не удалось загрузить форму. Проблема на нашей стороне, а не в вашей ссылке.",
53
+ "forms.fill.not_found": "Ссылка на форму недействительна.",
54
+ "forms.fill.closed": "Форма закрыта и больше не принимает ответы.",
55
+ "forms.fill.superseded":
56
+ "Форма изменилась, пока вы её заполняли. Проверьте ответы и отправьте ещё раз.",
57
+ "forms.fill.unsupported_field":
58
+ "Поле типа «{kind}» не может быть показано в этой версии приложения.",
59
+ "forms.fill.required": "Обязательно",
60
+ "forms.fill.submit": "Отправить",
61
+ "forms.fill.submitting": "Отправка…",
62
+ "forms.fill.thanks": "Спасибо — ваш ответ записан.",
63
+ "forms.fill.optional_hint": "Необязательно",
64
+ "forms.fill.bool_yes": "Да",
65
+ "forms.fill.bool_no": "Нет",
66
+ "forms.fill.select_placeholder": "Выберите…",
67
+ "forms.fill.unlimited": "Без ограничения",
68
+
69
+ "forms.submit.blocked.done": "Вы уже отправили эту форму.",
70
+ "forms.submit.blocked.in_flight": "Отправляем ваш ответ…",
71
+ "forms.submit.blocked.unsupported_kind":
72
+ "В форме есть поле типа, который приложение не умеет показывать ({kinds}), поэтому отправить её безопасно нельзя.",
73
+
74
+ "forms.builder.title": "Конструктор формы",
75
+ "forms.builder.add_field": "Добавить поле",
76
+ "forms.builder.remove_field": "Удалить поле",
77
+ "forms.builder.move_up": "Выше",
78
+ "forms.builder.move_down": "Ниже",
79
+ "forms.builder.field_slug": "Ключ",
80
+ "forms.builder.field_label": "Название",
81
+ "forms.builder.field_required": "Обязательное",
82
+ "forms.builder.field_kind": "Тип",
83
+ "forms.builder.save": "Сохранить черновик",
84
+ "forms.builder.publish": "Опубликовать",
85
+ "forms.builder.blocked.saving": "Сохраняем черновик…",
86
+ "forms.builder.blocked.publishing": "Публикуем…",
87
+ "forms.builder.blocked.no_changes": "С последнего сохранения ничего не изменилось.",
88
+ "forms.builder.blocked.empty_schema":
89
+ "Добавьте хотя бы одно поле перед публикацией.",
90
+ "forms.builder.blocked.unsaved_draft":
91
+ "Сначала сохраните черновик — иначе будет опубликована предыдущая сохранённая версия.",
92
+ "forms.builder.builder_less":
93
+ "У этого типа поля здесь нет редактируемых настроек. Его конфигурация задаётся через API черновика.",
94
+ "forms.builder.kind_unregistered":
95
+ "Эта установка не знает такой тип поля — его нельзя ни настроить, ни отобразить здесь. Поле сохранено, чтобы оно не исчезло из схемы незаметно.",
96
+ "forms.builder.kinds_failed":
97
+ "Не удалось загрузить список типов полей, поэтому сейчас нельзя добавить поле.",
98
+ "forms.builder.no_kinds":
99
+ "В этой установке нет доступных настраиваемых типов полей.",
100
+ "forms.builder.unsupported_config":
101
+ "Некоторые настройки этого поля ({keys}) пока нельзя изменить здесь.",
102
+ "forms.builder.empty": "В этой форме пока нет полей.",
103
+ "forms.builder.meta_title": "Название формы",
104
+ "forms.builder.meta_description": "Описание",
105
+ "forms.builder.meta_submit_label": "Текст кнопки отправки",
106
+ "forms.builder.meta_confirmation": "Сообщение после отправки",
107
+ "forms.builder.state_open": "Открыта",
108
+ "forms.builder.state_closed": "Закрыта",
109
+ "forms.builder.state_draft": "Черновик",
110
+ "forms.builder.rotate_link": "Обновить публичную ссылку",
111
+ "forms.builder.public_link": "Публичная ссылка",
112
+
113
+ "forms.responses.title": "Ответы",
114
+ "forms.responses.empty": "Ответов пока нет.",
115
+ "forms.responses.load_failed": "Не удалось загрузить ответы.",
116
+ "forms.responses.submitted_at": "Отправлен",
117
+ "forms.responses.respondent": "Респондент",
118
+ "forms.responses.anonymous": "Аноним",
119
+ "forms.responses.version": "Версия",
120
+ "forms.responses.all_versions": "Все версии",
121
+ "forms.responses.next": "Дальше",
122
+ "forms.responses.prev": "Назад",
123
+ "forms.responses.blocked.at_end": "Это последняя страница.",
124
+ "forms.responses.blocked.at_start": "Это первая страница.",
125
+ "forms.responses.delete": "Удалить",
126
+ "forms.responses.delete_confirm": "Удалить этот ответ безвозвратно?",
127
+ "forms.responses.resend": "Отправить повторно",
128
+ "forms.responses.resend_sent": "Отправлено получателям: {count}.",
129
+ "forms.responses.resend_override": "Отправить на другие адреса",
130
+ "forms.responses.resend_override_hint":
131
+ "Они заменяют настроенных получателей формы для этой отправки.",
132
+ "forms.responses.export": "Экспорт в CSV",
133
+ "forms.responses.exporting": "Экспорт… (страниц: {pages})",
134
+ "forms.responses.erased": "Стёрт",
135
+ "forms.responses.detail": "Ответ целиком",
136
+ "forms.responses.close": "Закрыть",
137
+
138
+ "forms.list.title": "Формы",
139
+ "forms.list.empty": "В этом рабочем пространстве пока нет форм.",
140
+ "forms.list.load_failed": "Не удалось загрузить формы.",
141
+ "forms.list.create": "Новая форма",
142
+ "forms.list.new_title": "Форма без названия",
143
+ "forms.list.submission_count": "Ответов: {count}",
144
+ };
145
+
146
+ /** Register the ru bundle. Call AFTER `registerFormsI18n` so it layers over
147
+ * the en floor (merge priority = registration order). */
148
+ export function registerFormsI18nRu(engine: I18nEngine): void {
149
+ engine.registerBundle("ru", formsI18nBundleRu);
150
+ }
package/src/index.ts ADDED
@@ -0,0 +1,184 @@
1
+ /**
2
+ * `@stapel/forms-react` — the headless React pair for stapel-forms
3
+ * (frontend-standard §2). Business + state only, zero visual opinion; the
4
+ * antd skin lives behind the `./default` subpath so a host that renders its
5
+ * own visuals never carries it.
6
+ *
7
+ * ── The one-liner the owner asked for ──────────────────────────────────────
8
+ *
9
+ * ```tsx
10
+ * const runtime = createFormsRuntime({ baseUrl: "/forms/api/v1/" });
11
+ * <FormsProvider runtime={runtime}>
12
+ * <StapelForm publicId="k3J…x9" /> // from @stapel/forms-react/default
13
+ * </FormsProvider>
14
+ * ```
15
+ *
16
+ * No session, no workspace id, no auth client: the two public routes are
17
+ * anonymous, so a marketing page can embed a form and nothing else.
18
+ *
19
+ * Layers: api → model → flows → headless → i18n. Generated surfaces (the
20
+ * typed schema, the error map, the manifest, llms.txt) are produced by the
21
+ * monorepo `gen:*` drivers from stapel-forms's own `docs/` artifacts and
22
+ * stand under drift gates.
23
+ */
24
+
25
+ // ── api ──────────────────────────────────────────────────────────────────────
26
+ export { createFormsApi } from "./api/formsApi.js";
27
+ export type { FormsApi, FormsApiOptions } from "./api/formsApi.js";
28
+ export type { Schemas } from "./api/types.js";
29
+ export type {
30
+ BuiltinFieldKind,
31
+ ConfigFieldSpec,
32
+ FieldKind,
33
+ FieldKindCatalogue,
34
+ FormCreateRequest,
35
+ FormFieldDef,
36
+ FormPatchRequest,
37
+ FormRow,
38
+ FormSchema,
39
+ FormSchemaMeta,
40
+ FormSettings,
41
+ FormState,
42
+ FormVersion,
43
+ PublicForm,
44
+ PublishResult,
45
+ ResendRequest,
46
+ ResendResult,
47
+ Submission,
48
+ SubmissionListParams,
49
+ SubmitRequest,
50
+ SubmitResult,
51
+ } from "./api/types.js";
52
+ export { BUILTIN_FIELD_KINDS as ALLOWED_FIELD_KINDS } from "./api/types.js";
53
+
54
+ /** The CSV export's raw transport — a header cursor and a file body are two
55
+ * things core's JSON client cannot carry (see `api/export.ts`). */
56
+ export {
57
+ concatCsvPages,
58
+ exportSubmissionsCsv,
59
+ FORMS_NEXT_BEFORE_HEADER,
60
+ } from "./api/export.js";
61
+ export type { CsvExportPage, FormsRawTransport } from "./api/export.js";
62
+
63
+ // ── flows ────────────────────────────────────────────────────────────────────
64
+ // The flow-machine primitive lives in `@stapel/core` (one reviewed copy for
65
+ // every pair — frontend-core-architecture §4b). Re-exported for ergonomics.
66
+ export { createFlowMachine, useFlow, isErrorCode } from "@stapel/core";
67
+ export type {
68
+ FlowMachine,
69
+ FlowMachineOptions,
70
+ FlowStateBase,
71
+ FlowError,
72
+ } from "@stapel/core";
73
+ export { toFlowError } from "./flows/errors.js";
74
+ export { FORMS_FLOWS, flowEndpoints } from "./flows/registry.js";
75
+ export type {
76
+ FormsFlowId,
77
+ FormsFlowSpec,
78
+ FlowEndpoint,
79
+ } from "./flows/registry.js";
80
+
81
+ // ── model (runtime wiring, query keys, context, hooks) ───────────────────────
82
+ export { createFormsRuntime } from "./model/runtime.js";
83
+ export type {
84
+ FormsRuntime,
85
+ CreateFormsRuntimeOptions,
86
+ } from "./model/runtime.js";
87
+ export {
88
+ FormsRuntimeContext,
89
+ useFormsRuntime,
90
+ useFormsApi,
91
+ useFormsAnalytics,
92
+ } from "./model/context.js";
93
+ export { formsQueryKeys } from "./model/queryKeys.js";
94
+
95
+ export {
96
+ useFieldKinds,
97
+ useForm,
98
+ useForms,
99
+ useFormVersions,
100
+ usePublicForm,
101
+ useSubmission,
102
+ useSubmissions,
103
+ } from "./model/queries.js";
104
+
105
+ export {
106
+ useCreateForm,
107
+ useCsvExport,
108
+ useDeleteForm,
109
+ useDeleteSubmission,
110
+ usePublishForm,
111
+ useResendSubmission,
112
+ useRotateLink,
113
+ useSaveDraft,
114
+ useSetFormState,
115
+ useSubmitForm,
116
+ useUpdateForm,
117
+ } from "./model/mutations.js";
118
+ export type {
119
+ CsvExportBag,
120
+ FormRef,
121
+ ResendVariables,
122
+ SaveDraftVariables,
123
+ SetFormStateVariables,
124
+ SubmissionRef,
125
+ SubmitVariables,
126
+ UpdateFormVariables,
127
+ } from "./model/mutations.js";
128
+
129
+ // ── widgets (the open seam) ──────────────────────────────────────────────────
130
+ export {
131
+ registerFormFieldWidget,
132
+ unregisterFormFieldWidget,
133
+ resolveFormFieldWidget,
134
+ registeredFormFieldKinds,
135
+ } from "./widgets/registry.js";
136
+ export type {
137
+ FormFieldWidget,
138
+ FormFieldWidgetProps,
139
+ } from "./widgets/registry.js";
140
+
141
+ export {
142
+ FEATURE_ERROR_CODES,
143
+ isBlank,
144
+ optionValues,
145
+ validateAnswers,
146
+ validateFieldValue,
147
+ } from "./widgets/validate.js";
148
+
149
+ // ── headless (renderless components) ─────────────────────────────────────────
150
+ export { FormsProvider } from "./headless/FormsProvider.js";
151
+ export { FormFill } from "./headless/FormFill.js";
152
+ export type { FormFillBag } from "./headless/FormFill.js";
153
+ export { FormBuilder } from "./headless/FormBuilder.js";
154
+ export type { BuilderField, FormBuilderBag } from "./headless/FormBuilder.js";
155
+ export { ResponsesTable } from "./headless/ResponsesTable.js";
156
+ export type {
157
+ ResponseColumn,
158
+ ResponsesTableBag,
159
+ ResponsesView,
160
+ } from "./headless/ResponsesTable.js";
161
+ export { FormList } from "./headless/FormList.js";
162
+ export type { FormListBag } from "./headless/FormList.js";
163
+
164
+ // ── i18n ─────────────────────────────────────────────────────────────────────
165
+ export {
166
+ FEATURE_ERROR_KEYS,
167
+ FORMS_I18N_KEYS,
168
+ formsI18nBundleEn,
169
+ registerFormsI18n,
170
+ } from "./i18n/keys.js";
171
+ export type { FormsI18nKey } from "./i18n/keys.js";
172
+
173
+ // ── errors map (code → status/params/remediation/en; generated) ──────────────
174
+ export {
175
+ FORMS_ERRORS,
176
+ FORMS_ERROR_CODES,
177
+ formsErrorBundleEn,
178
+ explainFormsError,
179
+ } from "./i18n/errorsMap.js";
180
+ export type {
181
+ FormsErrorCode,
182
+ FormsErrorSpec,
183
+ Remediation,
184
+ } from "./i18n/errorsMap.js";
@@ -0,0 +1,28 @@
1
+ import type { Context } from "react";
2
+ import { createModuleContext } from "@stapel/core";
3
+ import type { Analytics, ModuleContextKit } from "@stapel/core";
4
+ import type { FormsApi } from "../api/formsApi.js";
5
+ import type { FormsRuntime } from "./runtime.js";
6
+
7
+ /**
8
+ * The wired FormsRuntime shared through React context by
9
+ * `<FormsProvider>`. Hooks in `model/` and `headless/` read the singletons
10
+ * from here. One reviewed copy of this plumbing lives in `@stapel/core`
11
+ * (`createModuleContext`, slim wave §21/S2); this module binds it under the
12
+ * pair's public names.
13
+ */
14
+ const kit: ModuleContextKit<FormsRuntime> =
15
+ createModuleContext<FormsRuntime>("Forms");
16
+
17
+ export const FormsRuntimeContext: Context<FormsRuntime | null> =
18
+ kit.RuntimeContext;
19
+
20
+ export const useFormsRuntime: () => FormsRuntime = kit.useRuntime;
21
+
22
+ export const useFormsApi: () => FormsApi = kit.useApi;
23
+
24
+ export const useFormsAnalytics: () => Analytics | null = kit.useAnalytics;
25
+
26
+ /** @internal Re-exported as `<FormsProvider>` from `headless/`. */
27
+ export const ModuleProvider: ModuleContextKit<FormsRuntime>["Provider"] =
28
+ kit.Provider;