@f1studio/form-spec 5.0.0-alpha.101 → 5.0.0-alpha.103

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 (161) hide show
  1. package/Components/HelloComponent.d.ts +20 -0
  2. package/Components/HelloComponent.d.ts.map +1 -0
  3. package/Designer.d.ts.map +1 -0
  4. package/FormSpec.TS/Designer.js +2 -2
  5. package/FormSpec.TS/Designer.js.map +1 -1
  6. package/FormSpec.TS/Designer.ts.map +1 -1
  7. package/FormSpec.TS/FormSpec.js +210 -143
  8. package/FormSpec.TS/FormSpec.js.map +1 -1
  9. package/FormSpec.TS/FormSpec.ts.map +1 -1
  10. package/FormSpec.TS/FormSpecHelpers.js +62 -40
  11. package/FormSpec.TS/FormSpecHelpers.js.map +1 -1
  12. package/FormSpec.TS/FormSpecHelpers.ts.map +1 -1
  13. package/FormSpec.TS/FormSpecValues.js +207 -0
  14. package/FormSpec.TS/FormSpecValues.js.map +1 -0
  15. package/FormSpec.TS/FormSpecValues.ts.map +1 -0
  16. package/FormSpec.TS/Helpers.js +26 -25
  17. package/FormSpec.TS/Helpers.js.map +1 -1
  18. package/FormSpec.TS/Helpers.ts.map +1 -1
  19. package/FormSpec.TS/Interop/FormSpec.Api.Helpers.js +85 -125
  20. package/FormSpec.TS/Interop/FormSpec.Api.Helpers.js.map +1 -1
  21. package/FormSpec.TS/Interop/FormSpec.Api.Helpers.ts.map +1 -1
  22. package/FormSpec.TS/Interop/FormSpec.Api.Option.js +37 -7
  23. package/FormSpec.TS/Interop/FormSpec.Api.Option.js.map +1 -1
  24. package/FormSpec.TS/Interop/FormSpec.Api.Option.ts.map +1 -1
  25. package/FormSpec.TS/Interop/FormSpec.Values.Api.Option.js +103 -80
  26. package/FormSpec.TS/Interop/FormSpec.Values.Api.Option.js.map +1 -1
  27. package/FormSpec.TS/Interop/FormSpec.Values.Api.Option.ts.map +1 -1
  28. package/FormSpec.TS/PathwayExecutor.js +371 -125
  29. package/FormSpec.TS/PathwayExecutor.js.map +1 -1
  30. package/FormSpec.TS/PathwayExecutor.ts.map +1 -1
  31. package/FormSpec.TS/PathwayValidator.js +14 -26
  32. package/FormSpec.TS/PathwayValidator.js.map +1 -1
  33. package/FormSpec.TS/PathwayValidator.ts.map +1 -1
  34. package/FormSpec.TS/Renderers/FormSpecMarkdownRenderer.ts.map +1 -1
  35. package/FormSpec.TS/Renderers/PathwayRenderers.js +35 -26
  36. package/FormSpec.TS/Renderers/PathwayRenderers.js.map +1 -1
  37. package/FormSpec.TS/Renderers/PathwayRenderers.ts.map +1 -1
  38. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Base.fs +0 -0
  39. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Error.fs +0 -0
  40. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Extensions.fs +0 -0
  41. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Fable.Form.fableproj +0 -0
  42. package/FormSpec.TS/fable_modules/Fable.Form.3.0.0/Field.fs +0 -0
  43. package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Fable.Form.Simple.fableproj +0 -0
  44. package/FormSpec.TS/fable_modules/Fable.Form.Simple.5.0.1/Form.fs +0 -0
  45. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Extensions.fs +0 -0
  46. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Hooks.fs +0 -0
  47. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.Types.fableproj +0 -0
  48. package/FormSpec.TS/fable_modules/Fable.React.Types.18.3.0/Fable.React.fs +0 -0
  49. package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.Types.fableproj +0 -0
  50. package/FormSpec.TS/fable_modules/Fable.ReactDom.Types.18.2.0/Fable.ReactDom.fs +0 -0
  51. package/FormSpec.TS/fable_modules/Feliz.2.7.0/BorderStyle.fs +0 -0
  52. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Colors.fs +0 -0
  53. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Feliz.fableproj +0 -0
  54. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Fonts.fs +0 -0
  55. package/FormSpec.TS/fable_modules/Feliz.2.7.0/GridTypes.fs +0 -0
  56. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Html.fs +0 -0
  57. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Interop.fs +0 -0
  58. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Key.fs +0 -0
  59. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Length.fs +0 -0
  60. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Locale.fs +0 -0
  61. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Properties.fs +0 -0
  62. package/FormSpec.TS/fable_modules/Feliz.2.7.0/React.fs +0 -0
  63. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactDOM.fs +0 -0
  64. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactInterop.js +0 -0
  65. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactInterop.js.map +0 -0
  66. package/FormSpec.TS/fable_modules/Feliz.2.7.0/ReactTypes.fs +0 -0
  67. package/FormSpec.TS/fable_modules/Feliz.2.7.0/StyleTypes.fs +0 -0
  68. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Styles.fs +0 -0
  69. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Svg.fs +0 -0
  70. package/FormSpec.TS/fable_modules/Feliz.2.7.0/TextDecorationLine.fs +0 -0
  71. package/FormSpec.TS/fable_modules/Feliz.2.7.0/TextDecorationStyle.fs +0 -0
  72. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Transform.fs +0 -0
  73. package/FormSpec.TS/fable_modules/Feliz.2.7.0/TransformOrigin.fs +0 -0
  74. package/FormSpec.TS/fable_modules/Feliz.2.7.0/TransitionProperty.fs +0 -0
  75. package/FormSpec.TS/fable_modules/Feliz.2.7.0/Types.fs +0 -0
  76. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Decode.fs +0 -0
  77. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Encode.fs +0 -0
  78. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Encode.fs.js.map +1 -1
  79. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Encode.fs.ts.map +1 -1
  80. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Extra.fs +0 -0
  81. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Thoth.Json.fableproj +0 -0
  82. package/FormSpec.TS/fable_modules/Thoth.Json.10.4.1/Types.fs +0 -0
  83. package/FormSpec.TS/fable_modules/project_cracked.json +1 -1
  84. package/FormSpec.d.ts +56 -43
  85. package/FormSpec.d.ts.map +1 -0
  86. package/FormSpecHelpers.d.ts +18 -12
  87. package/FormSpecHelpers.d.ts.map +1 -0
  88. package/FormSpecValues.d.ts +62 -0
  89. package/FormSpecValues.d.ts.map +1 -0
  90. package/Helpers.d.ts +11 -10
  91. package/Helpers.d.ts.map +1 -0
  92. package/Interfaces.d.ts.map +1 -0
  93. package/Interop/FormSpec.Api.Helpers.d.ts +25 -28
  94. package/Interop/FormSpec.Api.Helpers.d.ts.map +1 -1
  95. package/Interop/FormSpec.Api.Option.d.ts +10 -6
  96. package/Interop/FormSpec.Api.Option.d.ts.map +1 -1
  97. package/Interop/FormSpec.Values.Api.Option.d.ts +35 -22
  98. package/Interop/FormSpec.Values.Api.Option.d.ts.map +1 -1
  99. package/Logging/LogTypes.d.ts +112 -0
  100. package/Logging/LogTypes.d.ts.map +1 -0
  101. package/Migrator.d.ts.map +1 -0
  102. package/PathwayDataExtractor.d.ts.map +1 -0
  103. package/PathwayExecutor.d.ts +63 -33
  104. package/PathwayExecutor.d.ts.map +1 -0
  105. package/PathwayValidator.d.ts.map +1 -0
  106. package/PluginInterface.d.ts.map +1 -0
  107. package/Prelude.d.ts.map +1 -0
  108. package/README.TS.md +621 -621
  109. package/README.md +98 -85
  110. package/Renderers/FormSpecMarkdownRenderer.d.ts +11 -0
  111. package/Renderers/FormSpecMarkdownRenderer.d.ts.map +1 -0
  112. package/Renderers/MermaidRenderer.d.ts +48 -0
  113. package/Renderers/MermaidRenderer.d.ts.map +1 -0
  114. package/Renderers/PathwayRenderers.d.ts +59 -0
  115. package/Renderers/PathwayRenderers.d.ts.map +1 -0
  116. package/fable_modules/Thoth.Json.10.4.1/Decode.fs.d.ts +126 -0
  117. package/fable_modules/Thoth.Json.10.4.1/Decode.fs.d.ts.map +1 -0
  118. package/fable_modules/Thoth.Json.10.4.1/Encode.fs.d.ts +163 -0
  119. package/fable_modules/Thoth.Json.10.4.1/Encode.fs.d.ts.map +1 -0
  120. package/fable_modules/Thoth.Json.10.4.1/Types.fs.d.ts +66 -0
  121. package/fable_modules/Thoth.Json.10.4.1/Types.fs.d.ts.map +1 -0
  122. package/package.json +53 -39
  123. package/src/Components/HelloComponent.ts +48 -48
  124. package/src/Designer.ts +389 -389
  125. package/src/FormSpec.ts +3154 -3114
  126. package/src/FormSpecHelpers.ts +397 -374
  127. package/src/FormSpecValues.ts +158 -0
  128. package/src/Helpers.ts +766 -765
  129. package/src/Interfaces.ts +166 -166
  130. package/src/Interop/FormSpec.Api.Helpers.ts +835 -872
  131. package/src/Interop/FormSpec.Api.Option.ts +1637 -1618
  132. package/src/Interop/FormSpec.Values.Api.Option.ts +1241 -1214
  133. package/src/Logging/LogTypes.ts +212 -212
  134. package/src/Migrator.ts +156 -156
  135. package/src/PathwayDataExtractor.ts +290 -290
  136. package/src/PathwayExecutor.ts +1379 -1102
  137. package/src/PathwayValidator.ts +238 -244
  138. package/src/PluginInterface.ts +79 -79
  139. package/src/Prelude.ts +21 -21
  140. package/src/Renderers/FormSpecMarkdownRenderer.ts +875 -874
  141. package/src/Renderers/MermaidRenderer.ts +218 -218
  142. package/src/Renderers/PathwayRenderers.ts +208 -200
  143. package/src/Components/HelloComponent.ts.map +0 -1
  144. package/src/Designer.ts.map +0 -1
  145. package/src/FormSpec.ts.map +0 -1
  146. package/src/FormSpecHelpers.ts.map +0 -1
  147. package/src/Helpers.ts.map +0 -1
  148. package/src/Interfaces.ts.map +0 -1
  149. package/src/Interop/FormSpec.Api.Helpers.ts.map +0 -1
  150. package/src/Interop/FormSpec.Api.Option.ts.map +0 -1
  151. package/src/Interop/FormSpec.Values.Api.Option.ts.map +0 -1
  152. package/src/Logging/LogTypes.ts.map +0 -1
  153. package/src/Migrator.ts.map +0 -1
  154. package/src/PathwayDataExtractor.ts.map +0 -1
  155. package/src/PathwayExecutor.ts.map +0 -1
  156. package/src/PathwayValidator.ts.map +0 -1
  157. package/src/PluginInterface.ts.map +0 -1
  158. package/src/Prelude.ts.map +0 -1
  159. package/src/Renderers/FormSpecMarkdownRenderer.ts.map +0 -1
  160. package/src/Renderers/MermaidRenderer.ts.map +0 -1
  161. package/src/Renderers/PathwayRenderers.ts.map +0 -1
package/README.TS.md CHANGED
@@ -1,622 +1,622 @@
1
- # FormSpec TypeScript API Documentation
2
-
3
- ## 🚀 F# to TypeScript Interop for Form Specifications
4
-
5
- The FormSpec TypeScript API provides a type-safe, validated bridge between F# domain models and TypeScript applications. Built with Fable and Thoth.Json, it enables seamless form specification management across the F#/TypeScript boundary.
6
-
7
- ## Installation
8
-
9
- ```bash
10
- # From Verdaccio local registry
11
- npm install @f1studio/form-spec@5.0.0-alpha.13 --registry http://localhost:4873
12
- # or
13
- pnpm add @f1studio/form-spec@5.0.0-alpha.13 --registry http://localhost:4873
14
- # or
15
- yarn add @f1studio/form-spec@5.0.0-alpha.13 --registry http://localhost:4873
16
- ```
17
-
18
- ## Core Concepts
19
-
20
- ### FableResult Type
21
- All validation functions return a `FableResult<T, E>` - a TypeScript-friendly tagged union:
22
-
23
- ```typescript
24
- type FableResult<T, E> =
25
- | { result: "fableOk"; Item: T }
26
- | { result: "fableError"; Item: E }
27
- ```
28
-
29
- This enables safe pattern matching in TypeScript:
30
-
31
- ```typescript
32
- const result = validateFormSpec(unknownData);
33
-
34
- switch (result.result) {
35
- case "fableOk":
36
- console.log("Valid FormSpec:", result.Item);
37
- break;
38
- case "fableError":
39
- console.error("Validation failed:", result.Item);
40
- break;
41
- }
42
- ```
43
-
44
- ## Quick Start
45
-
46
- ### 1. Validate Unknown Data
47
-
48
- ```typescript
49
- import {
50
- Api_validateFormSpec,
51
- Api_validateAndConvertFormSpec
52
- } from '@f1studio/form-spec/Interop/FormSpec.Api';
53
-
54
- // Validate JSON data from an API
55
- async function loadFormSpec(url: string) {
56
- const response = await fetch(url);
57
- const data = await response.json();
58
-
59
- // Validate to TypeScript-friendly types
60
- const result = Api_validateFormSpec()(data);
61
-
62
- if (result.result === "fableOk") {
63
- return result.Item; // Fully typed FormSpecTS
64
- } else {
65
- throw new Error(result.Item);
66
- }
67
- }
68
-
69
- // Validate AND convert to F# domain types
70
- const domainResult = Api_validateAndConvertFormSpec()(data);
71
- ```
72
-
73
- ### 2. Create Forms with Builders
74
-
75
- ```typescript
76
- import {
77
- Api_createFormSpec,
78
- Api_createTextField,
79
- Api_createCheckboxField,
80
- Api_createDropdownField,
81
- Api_createFieldOption,
82
- Builders_formField,
83
- Builders_formStep,
84
- Builders_formSpec
85
- } from '@f1studio/form-spec/Interop/FormSpec.Api';
86
-
87
- // Create a new empty FormSpec
88
- const emptySpec = Api_createFormSpec();
89
-
90
- // Build a complete form specification
91
- function createPatientIntakeForm() {
92
- // Create field options
93
- const genderOptions = [
94
- Api_createFieldOption("Male", "male"),
95
- Api_createFieldOption("Female", "female"),
96
- Api_createFieldOption("Other", "other")
97
- ];
98
-
99
- // Create fields
100
- const nameField = Builders_formField(
101
- "Patient Name",
102
- Api_createTextField(null)
103
- )
104
- .WithOrder(1)
105
- .AsOptional()
106
- .Build();
107
-
108
- const genderField = Builders_formField(
109
- "Gender",
110
- Api_createDropdownField(genderOptions)
111
- )
112
- .WithOrder(2)
113
- .Build();
114
-
115
- const consentField = Builders_formField(
116
- "I consent to treatment",
117
- Api_createCheckboxField(false, null)
118
- )
119
- .WithOrder(3)
120
- .Build();
121
-
122
- // Create form step
123
- const demographicsStep = Builders_formStep("Demographics")
124
- .WithOrder(1)
125
- .AddField(nameField)
126
- .AddField(genderField)
127
- .AddField(consentField)
128
- .Build();
129
-
130
- // Create complete FormSpec
131
- const formSpec = Builders_formSpec("Patient Intake Form")
132
- .WithCode("INTAKE-001")
133
- .WithAbstract("Standard patient intake and consent form")
134
- .WithVersion("2.0.0")
135
- .AddStep(demographicsStep)
136
- .WithTags(["intake", "patient", "consent"])
137
- .RequiresReview()
138
- .Build();
139
-
140
- return formSpec;
141
- }
142
- ```
143
-
144
- ### 3. Field Types
145
-
146
- The API supports all standard HTML input types plus specialized medical fields:
147
-
148
- ```typescript
149
- import {
150
- Api_createTextField,
151
- Api_createTextArea,
152
- Api_createEmailField,
153
- Api_createPasswordField,
154
- Api_createNumberField,
155
- Api_createDateField,
156
- Api_createTimeField,
157
- Api_createCheckboxField,
158
- Api_createRadioField,
159
- Api_createDropdownField,
160
- Api_createMultiChoiceField,
161
- Api_createMessageField,
162
- Types_MessageTypeTS
163
- } from '@f1studio/form-spec/Interop/FormSpec.Api';
164
-
165
- // Text inputs
166
- const textField = Api_createTextField("default value");
167
- const emailField = Api_createEmailField(null);
168
- const numberField = Api_createNumberField("42");
169
-
170
- // Date/Time inputs
171
- const dateField = Api_createDateField("2024-01-01");
172
- const timeField = Api_createTimeField("14:30");
173
-
174
- // Choice inputs
175
- const options = [
176
- Api_createFieldOption("Option 1", "opt1"),
177
- Api_createFieldOption("Option 2", "opt2")
178
- ];
179
- const radioField = Api_createRadioField(options);
180
- const dropdownField = Api_createDropdownField(options);
181
- const multiChoiceField = Api_createMultiChoiceField(options);
182
-
183
- // Informational
184
- const messageField = Api_createMessageField(
185
- "Important Notice",
186
- "Please read carefully before proceeding",
187
- Types_MessageTypeTS.Warning
188
- );
189
- ```
190
-
191
- ### 4. Field Dependencies
192
-
193
- Create conditional field visibility based on other field values:
194
-
195
- ```typescript
196
- import {
197
- Types_EvaluatorTS,
198
- Api_createFieldKey
199
- } from '@f1studio/form-spec/Interop/FormSpec.Api';
200
-
201
- const hasAllergyField = Builders_formField(
202
- "Do you have allergies?",
203
- Api_createCheckboxField(false, null)
204
- )
205
- .WithOrder(1)
206
- .Build();
207
-
208
- const allergyDetailsField = Builders_formField(
209
- "Please describe your allergies",
210
- Api_createTextField(null)
211
- )
212
- .WithOrder(2)
213
- .WithDependency({
214
- FieldKey: hasAllergyField.FieldKey,
215
- FieldValue: "true",
216
- Evaluator: Types_EvaluatorTS.Equals
217
- })
218
- .Build();
219
- ```
220
-
221
- ### 5. Serialization
222
-
223
- ```typescript
224
- import {
225
- Api_serializeFormSpec,
226
- Api_deserializeFormSpec
227
- } from '@f1studio/form-spec/Interop/FormSpec.Api';
228
-
229
- // Convert FormSpec to JSON string
230
- const formSpec = createPatientIntakeForm();
231
- const json = Api_serializeFormSpec(formSpec);
232
- localStorage.setItem('formSpec', json);
233
-
234
- // Deserialize from JSON string
235
- const savedJson = localStorage.getItem('formSpec');
236
- const result = Api_deserializeFormSpec(savedJson);
237
-
238
- if (result.result === "fableOk") {
239
- const restoredSpec = result.Item;
240
- console.log("Loaded form:", restoredSpec.Title);
241
- }
242
- ```
243
-
244
- ### 6. Query and Utility Functions
245
-
246
- ```typescript
247
- import {
248
- Api_getFieldKeys,
249
- Api_findFieldByKey,
250
- Api_getFieldsByType,
251
- Api_isValidFormSpec,
252
- Api_getFormSpecSummary
253
- } from '@f1studio/form-spec/Interop/FormSpec.Api';
254
-
255
- const formSpec = createPatientIntakeForm();
256
-
257
- // Get all field keys
258
- const fieldKeys = Api_getFieldKeys(formSpec);
259
- console.log(`Form has ${fieldKeys.length} fields`);
260
-
261
- // Find a specific field
262
- const field = Api_findFieldByKey(formSpec, fieldKeys[0]);
263
- if (field) {
264
- console.log("Found field:", field.Label);
265
- }
266
-
267
- // Get all text fields
268
- const textFields = Api_getFieldsByType(formSpec, "Text");
269
- console.log(`Found ${textFields.length} text fields`);
270
-
271
- // Validate structure
272
- if (Api_isValidFormSpec(formSpec)) {
273
- console.log("FormSpec is valid");
274
- }
275
-
276
- // Get summary information
277
- const summary = Api_getFormSpecSummary(formSpec);
278
- console.log(summary);
279
- // Output: { Id, Title, Version, StepCount, FieldCount, RequiresReview, RequiresApproval }
280
- ```
281
-
282
- ### 7. Scoring System
283
-
284
- Add scoring ranges for assessments and evaluations:
285
-
286
- ```typescript
287
- import {
288
- Types_ScoreColorTS,
289
- Api_createScoreRange
290
- } from '@f1studio/form-spec/Interop/FormSpec.Api';
291
-
292
- const scoreRanges = [
293
- {
294
- Id: "550e8400-e29b-41d4-a716-446655440001",
295
- Min: 0,
296
- Max: 10,
297
- Label: "Low Risk",
298
- Tag: Types_ScoreColorTS.Success
299
- },
300
- {
301
- Id: "550e8400-e29b-41d4-a716-446655440002",
302
- Min: 11,
303
- Max: 20,
304
- Label: "Medium Risk",
305
- Tag: Types_ScoreColorTS.Warning
306
- },
307
- {
308
- Id: "550e8400-e29b-41d4-a716-446655440003",
309
- Min: 21,
310
- Max: 30,
311
- Label: "High Risk",
312
- Tag: Types_ScoreColorTS.Danger
313
- }
314
- ];
315
-
316
- const formWithScore = Builders_formSpec("Risk Assessment")
317
- .WithScore({
318
- MaxScore: 30,
319
- ScoreRanges: scoreRanges
320
- })
321
- .Build();
322
- ```
323
-
324
- ### 8. Matrix/Likert Scale Fields
325
-
326
- Create matrix questions for surveys and assessments:
327
-
328
- ```typescript
329
- const satisfactionItems = [
330
- Api_createFieldOption("Wait time", "wait_time"),
331
- Api_createFieldOption("Staff friendliness", "staff"),
332
- Api_createFieldOption("Cleanliness", "clean"),
333
- ];
334
-
335
- const ratingScale = [
336
- Api_createFieldOption("Very Dissatisfied", "1"),
337
- Api_createFieldOption("Dissatisfied", "2"),
338
- Api_createFieldOption("Neutral", "3"),
339
- Api_createFieldOption("Satisfied", "4"),
340
- Api_createFieldOption("Very Satisfied", "5"),
341
- ];
342
-
343
- const matrixField = Builders_formField(
344
- "Please rate your experience",
345
- {
346
- Type: "Matrix",
347
- MatrixInfo: {
348
- Items: satisfactionItems,
349
- Options: ratingScale
350
- },
351
- // ... other properties set to null
352
- }
353
- ).Build();
354
- ```
355
-
356
- ## Advanced Usage
357
-
358
- ### Type Guards and Validation
359
-
360
- ```typescript
361
- import { FormSpecTS } from '@f1studio/form-spec/Interop/FormSpec.Api';
362
-
363
- // Type guard for FormSpec
364
- function isFormSpec(obj: any): obj is FormSpecTS {
365
- const result = Api_validateFormSpec()(obj);
366
- return result.result === "fableOk";
367
- }
368
-
369
- // Use in your code
370
- function processForm(data: unknown) {
371
- if (isFormSpec(data)) {
372
- // data is now typed as FormSpecTS
373
- console.log(data.Title);
374
- data.Steps.forEach(step => {
375
- console.log(step.StepLabel);
376
- });
377
- }
378
- }
379
- ```
380
-
381
- ### Batch Validation
382
-
383
- ```typescript
384
- import { Api_validateFormSpecs } from '@f1studio/form-spec/Interop/FormSpec.Api';
385
-
386
- async function validateMultipleForms(urls: string[]) {
387
- const responses = await Promise.all(
388
- urls.map(url => fetch(url).then(r => r.json()))
389
- );
390
-
391
- const result = Api_validateFormSpecs(responses);
392
-
393
- if (result.result === "fableOk") {
394
- return result.Item; // FormSpecTS[]
395
- } else {
396
- throw new Error(`Validation failed: ${result.Item}`);
397
- }
398
- }
399
- ```
400
-
401
- ### Working with GUIDs
402
-
403
- All keys in the system use GUIDs wrapped in typed objects:
404
-
405
- ```typescript
406
- import {
407
- Api_createFieldKey,
408
- Api_createStateKey,
409
- Api_createTransitionKey
410
- } from '@f1studio/form-spec/Interop/FormSpec.Api';
411
-
412
- // Generate new keys
413
- const fieldKey = Api_createFieldKey(); // { Value: "uuid..." }
414
- const stateKey = Api_createStateKey(); // { Value: "uuid..." }
415
- const transitionKey = Api_createTransitionKey(); // { Value: "uuid..." }
416
-
417
- // Keys are automatically generated when using builders
418
- const field = Builders_formField("Name", Api_createTextField(null))
419
- .Build();
420
- console.log(field.FieldKey.Value); // UUID string
421
- ```
422
-
423
- ## React Integration Example
424
-
425
- ```typescript
426
- import React, { useState, useEffect } from 'react';
427
- import {
428
- FormSpecTS,
429
- Api_validateFormSpec,
430
- Api_getFieldsByType
431
- } from '@f1studio/form-spec/Interop/FormSpec.Api';
432
-
433
- function FormRenderer({ formData }: { formData: unknown }) {
434
- const [formSpec, setFormSpec] = useState<FormSpecTS | null>(null);
435
- const [error, setError] = useState<string | null>(null);
436
-
437
- useEffect(() => {
438
- const result = Api_validateFormSpec()(formData);
439
-
440
- if (result.result === "fableOk") {
441
- setFormSpec(result.Item);
442
- setError(null);
443
- } else {
444
- setError(result.Item);
445
- setFormSpec(null);
446
- }
447
- }, [formData]);
448
-
449
- if (error) {
450
- return <div className="error">Invalid form: {error}</div>;
451
- }
452
-
453
- if (!formSpec) {
454
- return <div>Loading...</div>;
455
- }
456
-
457
- return (
458
- <div className="form-renderer">
459
- <h1>{formSpec.Title}</h1>
460
- <p>{formSpec.Abstract}</p>
461
-
462
- {formSpec.Steps.map((step, stepIdx) => (
463
- <div key={stepIdx} className="form-step">
464
- <h2>{step.StepLabel}</h2>
465
- {step.Fields.map((field, fieldIdx) => (
466
- <div key={field.FieldKey.Value} className="form-field">
467
- <label>{field.Label}</label>
468
- {/* Render appropriate input based on field.FieldType.Type */}
469
- </div>
470
- ))}
471
- </div>
472
- ))}
473
- </div>
474
- );
475
- }
476
- ```
477
-
478
- ## Type Definitions
479
-
480
- ### Core Types
481
-
482
- ```typescript
483
- interface FormSpecTS {
484
- Id: string;
485
- Code?: string;
486
- Title: string;
487
- Abstract: string;
488
- Version: string;
489
- FormSpecVersion: string;
490
- Steps: FormStepTS[];
491
- CategoryTags: string[];
492
- Score?: ScoreTS;
493
- AssociatedCodes: string[];
494
- RequiresReview: boolean;
495
- RequiresReviewAndApproval: boolean;
496
- ClinicalPathway?: ClinicalPathwaySpecTS;
497
- }
498
-
499
- interface FormStepTS {
500
- StepOrder: number;
501
- StepLabel: string;
502
- Fields: FormFieldTS[];
503
- }
504
-
505
- interface FormFieldTS {
506
- FieldOrder: number;
507
- FieldKey: FieldKeyTS;
508
- Label: string;
509
- DependsOn?: DependsOnTS;
510
- IsOptional: boolean;
511
- IsDeprecated: boolean;
512
- FieldType: FieldTypeTS;
513
- }
514
-
515
- interface FieldTypeTS {
516
- Type: string; // "Text", "Checkbox", "Radio", etc.
517
- TextInfo?: TextInfoTS;
518
- BooleanInfo?: BooleanInfoTS;
519
- SingleChoiceInfo?: SingleChoiceInfoTS;
520
- MultiChoiceInfo?: MultiChoiceInfoTS;
521
- MessageInfo?: MessageInfoTS;
522
- MatrixInfo?: MatrixInfoTS;
523
- SignatureInfo?: SignatureInfoTS;
524
- PluginConfig?: PluginFieldConfigTS;
525
- }
526
- ```
527
-
528
- ## Best Practices
529
-
530
- 1. **Always Validate External Data**: Never trust data from APIs, files, or user input without validation
531
- 2. **Use Builders for Type Safety**: Builders ensure all required fields are set correctly
532
- 3. **Handle Both Result Cases**: Always handle both `fableOk` and `fableError` cases
533
- 4. **Leverage TypeScript Types**: Import type definitions for IntelliSense and compile-time checking
534
- 5. **Store GUIDs as Strings**: When persisting to databases, use the `Value` property of key types
535
-
536
- ## Migration from Legacy Forms
537
-
538
- If you have existing form data in a different format:
539
-
540
- ```typescript
541
- function migrateOldForm(oldForm: any): FormSpecTS {
542
- const builder = Builders_formSpec(oldForm.title || "Untitled Form")
543
- .WithVersion(oldForm.version || "1.0.0");
544
-
545
- // Map old fields to new structure
546
- oldForm.questions?.forEach((q: any, idx: number) => {
547
- const field = Builders_formField(
548
- q.text,
549
- mapOldFieldType(q.type, q.options)
550
- )
551
- .WithOrder(idx + 1)
552
- .Build();
553
-
554
- // Add to step...
555
- });
556
-
557
- return builder.Build();
558
- }
559
-
560
- function mapOldFieldType(type: string, options?: any[]): FieldTypeTS {
561
- switch(type) {
562
- case 'text': return Api_createTextField(null);
563
- case 'select': return Api_createDropdownField(
564
- options?.map(o => Api_createFieldOption(o.label, o.value)) || []
565
- );
566
- // ... map other types
567
- default: return Api_createTextField(null);
568
- }
569
- }
570
- ```
571
-
572
- ## Troubleshooting
573
-
574
- ### Common Issues
575
-
576
- **Issue**: Validation fails with "Invalid GUID for FieldKey"
577
- **Solution**: Ensure you're using the key creation functions or builders which generate valid GUIDs
578
-
579
- **Issue**: TypeScript can't find types
580
- **Solution**: Import from the specific path: `@f1studio/form-spec/Interop/FormSpec.Api`
581
-
582
- **Issue**: FableResult is not being recognized in switch statements
583
- **Solution**: Check the discriminator property is exactly `result` (lowercase)
584
-
585
- ## API Reference
586
-
587
- ### Validation Functions
588
- - `Api_validateFormSpec(input: any): FableResult<FormSpecTS, string>` - Validate unknown data to FormSpecTS
589
- - `Api_validateAndConvertFormSpec(input: any): FableResult<FormSpec, string>` - Validate and convert to F# domain type
590
- - `Api_validateFormField(input: any): FableResult<FormFieldTS, string>` - Validate a single field
591
- - `Api_validateFormStep(input: any): FableResult<FormStepTS, string>` - Validate a form step
592
- - `Api_validateFieldType(input: any): FableResult<FieldTypeTS, string>` - Validate a field type
593
-
594
- ### Builder Functions
595
- - `Api_createFormSpec(): FormSpecTS` - Create empty FormSpec
596
- - `Api_createFieldKey(): FieldKeyTS` - Generate new field key
597
- - `Api_createFieldOption(description: string, value: string): FieldOptionTS` - Create field option
598
- - `Api_createTextField(value: string | null): FieldTypeTS` - Create text field
599
- - `Api_createCheckboxField(defaultValue: boolean | null, selection: boolean | null): FieldTypeTS` - Create checkbox
600
- - `Api_createRadioField(options: FieldOptionTS[]): FieldTypeTS` - Create radio button group
601
- - `Api_createDropdownField(options: FieldOptionTS[]): FieldTypeTS` - Create dropdown
602
-
603
- ### Serialization Functions
604
- - `Api_serializeFormSpec(spec: FormSpecTS): string` - Convert to JSON string
605
- - `Api_deserializeFormSpec(json: string): FableResult<FormSpecTS, string>` - Parse from JSON
606
-
607
- ### Query Functions
608
- - `Api_getFieldKeys(spec: FormSpecTS): FieldKeyTS[]` - Get all field keys
609
- - `Api_findFieldByKey(spec: FormSpecTS, key: FieldKeyTS): FormFieldTS | undefined` - Find field by key
610
- - `Api_getFieldsByType(spec: FormSpecTS, type: string): FormFieldTS[]` - Get fields of specific type
611
- - `Api_isValidFormSpec(spec: FormSpecTS): boolean` - Check if spec is valid
612
- - `Api_getFormSpecSummary(spec: FormSpecTS): object` - Get spec summary
613
-
614
- ## Support
615
-
616
- For issues, questions, or contributions:
617
- - GitHub: https://github.com/f1-studio/f1-monorepo
618
- - Documentation: https://f1studio.ai/docs
619
-
620
- ## License
621
-
1
+ # FormSpec TypeScript API Documentation
2
+
3
+ ## 🚀 F# to TypeScript Interop for Form Specifications
4
+
5
+ The FormSpec TypeScript API provides a type-safe, validated bridge between F# domain models and TypeScript applications. Built with Fable and Thoth.Json, it enables seamless form specification management across the F#/TypeScript boundary.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ # From Verdaccio local registry
11
+ npm install @f1studio/form-spec@5.0.0-alpha.13 --registry http://localhost:4873
12
+ # or
13
+ pnpm add @f1studio/form-spec@5.0.0-alpha.13 --registry http://localhost:4873
14
+ # or
15
+ yarn add @f1studio/form-spec@5.0.0-alpha.13 --registry http://localhost:4873
16
+ ```
17
+
18
+ ## Core Concepts
19
+
20
+ ### FableResult Type
21
+ All validation functions return a `FableResult<T, E>` - a TypeScript-friendly tagged union:
22
+
23
+ ```typescript
24
+ type FableResult<T, E> =
25
+ | { result: "fableOk"; Item: T }
26
+ | { result: "fableError"; Item: E }
27
+ ```
28
+
29
+ This enables safe pattern matching in TypeScript:
30
+
31
+ ```typescript
32
+ const result = validateFormSpec(unknownData);
33
+
34
+ switch (result.result) {
35
+ case "fableOk":
36
+ console.log("Valid FormSpec:", result.Item);
37
+ break;
38
+ case "fableError":
39
+ console.error("Validation failed:", result.Item);
40
+ break;
41
+ }
42
+ ```
43
+
44
+ ## Quick Start
45
+
46
+ ### 1. Validate Unknown Data
47
+
48
+ ```typescript
49
+ import {
50
+ Api_validateFormSpec,
51
+ Api_validateAndConvertFormSpec
52
+ } from '@f1studio/form-spec/Interop/FormSpec.Api';
53
+
54
+ // Validate JSON data from an API
55
+ async function loadFormSpec(url: string) {
56
+ const response = await fetch(url);
57
+ const data = await response.json();
58
+
59
+ // Validate to TypeScript-friendly types
60
+ const result = Api_validateFormSpec()(data);
61
+
62
+ if (result.result === "fableOk") {
63
+ return result.Item; // Fully typed FormSpecTS
64
+ } else {
65
+ throw new Error(result.Item);
66
+ }
67
+ }
68
+
69
+ // Validate AND convert to F# domain types
70
+ const domainResult = Api_validateAndConvertFormSpec()(data);
71
+ ```
72
+
73
+ ### 2. Create Forms with Builders
74
+
75
+ ```typescript
76
+ import {
77
+ Api_createFormSpec,
78
+ Api_createTextField,
79
+ Api_createCheckboxField,
80
+ Api_createDropdownField,
81
+ Api_createFieldOption,
82
+ Builders_formField,
83
+ Builders_formStep,
84
+ Builders_formSpec
85
+ } from '@f1studio/form-spec/Interop/FormSpec.Api';
86
+
87
+ // Create a new empty FormSpec
88
+ const emptySpec = Api_createFormSpec();
89
+
90
+ // Build a complete form specification
91
+ function createPatientIntakeForm() {
92
+ // Create field options
93
+ const genderOptions = [
94
+ Api_createFieldOption("Male", "male"),
95
+ Api_createFieldOption("Female", "female"),
96
+ Api_createFieldOption("Other", "other")
97
+ ];
98
+
99
+ // Create fields
100
+ const nameField = Builders_formField(
101
+ "Patient Name",
102
+ Api_createTextField(null)
103
+ )
104
+ .WithOrder(1)
105
+ .AsOptional()
106
+ .Build();
107
+
108
+ const genderField = Builders_formField(
109
+ "Gender",
110
+ Api_createDropdownField(genderOptions)
111
+ )
112
+ .WithOrder(2)
113
+ .Build();
114
+
115
+ const consentField = Builders_formField(
116
+ "I consent to treatment",
117
+ Api_createCheckboxField(false, null)
118
+ )
119
+ .WithOrder(3)
120
+ .Build();
121
+
122
+ // Create form step
123
+ const demographicsStep = Builders_formStep("Demographics")
124
+ .WithOrder(1)
125
+ .AddField(nameField)
126
+ .AddField(genderField)
127
+ .AddField(consentField)
128
+ .Build();
129
+
130
+ // Create complete FormSpec
131
+ const formSpec = Builders_formSpec("Patient Intake Form")
132
+ .WithCode("INTAKE-001")
133
+ .WithAbstract("Standard patient intake and consent form")
134
+ .WithVersion("2.0.0")
135
+ .AddStep(demographicsStep)
136
+ .WithTags(["intake", "patient", "consent"])
137
+ .RequiresReview()
138
+ .Build();
139
+
140
+ return formSpec;
141
+ }
142
+ ```
143
+
144
+ ### 3. Field Types
145
+
146
+ The API supports all standard HTML input types plus specialized medical fields:
147
+
148
+ ```typescript
149
+ import {
150
+ Api_createTextField,
151
+ Api_createTextArea,
152
+ Api_createEmailField,
153
+ Api_createPasswordField,
154
+ Api_createNumberField,
155
+ Api_createDateField,
156
+ Api_createTimeField,
157
+ Api_createCheckboxField,
158
+ Api_createRadioField,
159
+ Api_createDropdownField,
160
+ Api_createMultiChoiceField,
161
+ Api_createMessageField,
162
+ Types_MessageTypeTS
163
+ } from '@f1studio/form-spec/Interop/FormSpec.Api';
164
+
165
+ // Text inputs
166
+ const textField = Api_createTextField("default value");
167
+ const emailField = Api_createEmailField(null);
168
+ const numberField = Api_createNumberField("42");
169
+
170
+ // Date/Time inputs
171
+ const dateField = Api_createDateField("2024-01-01");
172
+ const timeField = Api_createTimeField("14:30");
173
+
174
+ // Choice inputs
175
+ const options = [
176
+ Api_createFieldOption("Option 1", "opt1"),
177
+ Api_createFieldOption("Option 2", "opt2")
178
+ ];
179
+ const radioField = Api_createRadioField(options);
180
+ const dropdownField = Api_createDropdownField(options);
181
+ const multiChoiceField = Api_createMultiChoiceField(options);
182
+
183
+ // Informational
184
+ const messageField = Api_createMessageField(
185
+ "Important Notice",
186
+ "Please read carefully before proceeding",
187
+ Types_MessageTypeTS.Warning
188
+ );
189
+ ```
190
+
191
+ ### 4. Field Dependencies
192
+
193
+ Create conditional field visibility based on other field values:
194
+
195
+ ```typescript
196
+ import {
197
+ Types_EvaluatorTS,
198
+ Api_createFieldKey
199
+ } from '@f1studio/form-spec/Interop/FormSpec.Api';
200
+
201
+ const hasAllergyField = Builders_formField(
202
+ "Do you have allergies?",
203
+ Api_createCheckboxField(false, null)
204
+ )
205
+ .WithOrder(1)
206
+ .Build();
207
+
208
+ const allergyDetailsField = Builders_formField(
209
+ "Please describe your allergies",
210
+ Api_createTextField(null)
211
+ )
212
+ .WithOrder(2)
213
+ .WithDependency({
214
+ FieldKey: hasAllergyField.FieldKey,
215
+ FieldValue: "true",
216
+ Evaluator: Types_EvaluatorTS.Equals
217
+ })
218
+ .Build();
219
+ ```
220
+
221
+ ### 5. Serialization
222
+
223
+ ```typescript
224
+ import {
225
+ Api_serializeFormSpec,
226
+ Api_deserializeFormSpec
227
+ } from '@f1studio/form-spec/Interop/FormSpec.Api';
228
+
229
+ // Convert FormSpec to JSON string
230
+ const formSpec = createPatientIntakeForm();
231
+ const json = Api_serializeFormSpec(formSpec);
232
+ localStorage.setItem('formSpec', json);
233
+
234
+ // Deserialize from JSON string
235
+ const savedJson = localStorage.getItem('formSpec');
236
+ const result = Api_deserializeFormSpec(savedJson);
237
+
238
+ if (result.result === "fableOk") {
239
+ const restoredSpec = result.Item;
240
+ console.log("Loaded form:", restoredSpec.Title);
241
+ }
242
+ ```
243
+
244
+ ### 6. Query and Utility Functions
245
+
246
+ ```typescript
247
+ import {
248
+ Api_getFieldKeys,
249
+ Api_findFieldByKey,
250
+ Api_getFieldsByType,
251
+ Api_isValidFormSpec,
252
+ Api_getFormSpecSummary
253
+ } from '@f1studio/form-spec/Interop/FormSpec.Api';
254
+
255
+ const formSpec = createPatientIntakeForm();
256
+
257
+ // Get all field keys
258
+ const fieldKeys = Api_getFieldKeys(formSpec);
259
+ console.log(`Form has ${fieldKeys.length} fields`);
260
+
261
+ // Find a specific field
262
+ const field = Api_findFieldByKey(formSpec, fieldKeys[0]);
263
+ if (field) {
264
+ console.log("Found field:", field.Label);
265
+ }
266
+
267
+ // Get all text fields
268
+ const textFields = Api_getFieldsByType(formSpec, "Text");
269
+ console.log(`Found ${textFields.length} text fields`);
270
+
271
+ // Validate structure
272
+ if (Api_isValidFormSpec(formSpec)) {
273
+ console.log("FormSpec is valid");
274
+ }
275
+
276
+ // Get summary information
277
+ const summary = Api_getFormSpecSummary(formSpec);
278
+ console.log(summary);
279
+ // Output: { Id, Title, Version, StepCount, FieldCount, RequiresReview, RequiresApproval }
280
+ ```
281
+
282
+ ### 7. Scoring System
283
+
284
+ Add scoring ranges for assessments and evaluations:
285
+
286
+ ```typescript
287
+ import {
288
+ Types_ScoreColorTS,
289
+ Api_createScoreRange
290
+ } from '@f1studio/form-spec/Interop/FormSpec.Api';
291
+
292
+ const scoreRanges = [
293
+ {
294
+ Id: "550e8400-e29b-41d4-a716-446655440001",
295
+ Min: 0,
296
+ Max: 10,
297
+ Label: "Low Risk",
298
+ Tag: Types_ScoreColorTS.Success
299
+ },
300
+ {
301
+ Id: "550e8400-e29b-41d4-a716-446655440002",
302
+ Min: 11,
303
+ Max: 20,
304
+ Label: "Medium Risk",
305
+ Tag: Types_ScoreColorTS.Warning
306
+ },
307
+ {
308
+ Id: "550e8400-e29b-41d4-a716-446655440003",
309
+ Min: 21,
310
+ Max: 30,
311
+ Label: "High Risk",
312
+ Tag: Types_ScoreColorTS.Danger
313
+ }
314
+ ];
315
+
316
+ const formWithScore = Builders_formSpec("Risk Assessment")
317
+ .WithScore({
318
+ MaxScore: 30,
319
+ ScoreRanges: scoreRanges
320
+ })
321
+ .Build();
322
+ ```
323
+
324
+ ### 8. Matrix/Likert Scale Fields
325
+
326
+ Create matrix questions for surveys and assessments:
327
+
328
+ ```typescript
329
+ const satisfactionItems = [
330
+ Api_createFieldOption("Wait time", "wait_time"),
331
+ Api_createFieldOption("Staff friendliness", "staff"),
332
+ Api_createFieldOption("Cleanliness", "clean"),
333
+ ];
334
+
335
+ const ratingScale = [
336
+ Api_createFieldOption("Very Dissatisfied", "1"),
337
+ Api_createFieldOption("Dissatisfied", "2"),
338
+ Api_createFieldOption("Neutral", "3"),
339
+ Api_createFieldOption("Satisfied", "4"),
340
+ Api_createFieldOption("Very Satisfied", "5"),
341
+ ];
342
+
343
+ const matrixField = Builders_formField(
344
+ "Please rate your experience",
345
+ {
346
+ Type: "Matrix",
347
+ MatrixInfo: {
348
+ Items: satisfactionItems,
349
+ Options: ratingScale
350
+ },
351
+ // ... other properties set to null
352
+ }
353
+ ).Build();
354
+ ```
355
+
356
+ ## Advanced Usage
357
+
358
+ ### Type Guards and Validation
359
+
360
+ ```typescript
361
+ import { FormSpecTS } from '@f1studio/form-spec/Interop/FormSpec.Api';
362
+
363
+ // Type guard for FormSpec
364
+ function isFormSpec(obj: any): obj is FormSpecTS {
365
+ const result = Api_validateFormSpec()(obj);
366
+ return result.result === "fableOk";
367
+ }
368
+
369
+ // Use in your code
370
+ function processForm(data: unknown) {
371
+ if (isFormSpec(data)) {
372
+ // data is now typed as FormSpecTS
373
+ console.log(data.Title);
374
+ data.Steps.forEach(step => {
375
+ console.log(step.StepLabel);
376
+ });
377
+ }
378
+ }
379
+ ```
380
+
381
+ ### Batch Validation
382
+
383
+ ```typescript
384
+ import { Api_validateFormSpecs } from '@f1studio/form-spec/Interop/FormSpec.Api';
385
+
386
+ async function validateMultipleForms(urls: string[]) {
387
+ const responses = await Promise.all(
388
+ urls.map(url => fetch(url).then(r => r.json()))
389
+ );
390
+
391
+ const result = Api_validateFormSpecs(responses);
392
+
393
+ if (result.result === "fableOk") {
394
+ return result.Item; // FormSpecTS[]
395
+ } else {
396
+ throw new Error(`Validation failed: ${result.Item}`);
397
+ }
398
+ }
399
+ ```
400
+
401
+ ### Working with GUIDs
402
+
403
+ All keys in the system use GUIDs wrapped in typed objects:
404
+
405
+ ```typescript
406
+ import {
407
+ Api_createFieldKey,
408
+ Api_createStateKey,
409
+ Api_createTransitionKey
410
+ } from '@f1studio/form-spec/Interop/FormSpec.Api';
411
+
412
+ // Generate new keys
413
+ const fieldKey = Api_createFieldKey(); // { Value: "uuid..." }
414
+ const stateKey = Api_createStateKey(); // { Value: "uuid..." }
415
+ const transitionKey = Api_createTransitionKey(); // { Value: "uuid..." }
416
+
417
+ // Keys are automatically generated when using builders
418
+ const field = Builders_formField("Name", Api_createTextField(null))
419
+ .Build();
420
+ console.log(field.FieldKey.Value); // UUID string
421
+ ```
422
+
423
+ ## React Integration Example
424
+
425
+ ```typescript
426
+ import React, { useState, useEffect } from 'react';
427
+ import {
428
+ FormSpecTS,
429
+ Api_validateFormSpec,
430
+ Api_getFieldsByType
431
+ } from '@f1studio/form-spec/Interop/FormSpec.Api';
432
+
433
+ function FormRenderer({ formData }: { formData: unknown }) {
434
+ const [formSpec, setFormSpec] = useState<FormSpecTS | null>(null);
435
+ const [error, setError] = useState<string | null>(null);
436
+
437
+ useEffect(() => {
438
+ const result = Api_validateFormSpec()(formData);
439
+
440
+ if (result.result === "fableOk") {
441
+ setFormSpec(result.Item);
442
+ setError(null);
443
+ } else {
444
+ setError(result.Item);
445
+ setFormSpec(null);
446
+ }
447
+ }, [formData]);
448
+
449
+ if (error) {
450
+ return <div className="error">Invalid form: {error}</div>;
451
+ }
452
+
453
+ if (!formSpec) {
454
+ return <div>Loading...</div>;
455
+ }
456
+
457
+ return (
458
+ <div className="form-renderer">
459
+ <h1>{formSpec.Title}</h1>
460
+ <p>{formSpec.Abstract}</p>
461
+
462
+ {formSpec.Steps.map((step, stepIdx) => (
463
+ <div key={stepIdx} className="form-step">
464
+ <h2>{step.StepLabel}</h2>
465
+ {step.Fields.map((field, fieldIdx) => (
466
+ <div key={field.FieldKey.Value} className="form-field">
467
+ <label>{field.Label}</label>
468
+ {/* Render appropriate input based on field.FieldType.Type */}
469
+ </div>
470
+ ))}
471
+ </div>
472
+ ))}
473
+ </div>
474
+ );
475
+ }
476
+ ```
477
+
478
+ ## Type Definitions
479
+
480
+ ### Core Types
481
+
482
+ ```typescript
483
+ interface FormSpecTS {
484
+ Id: string;
485
+ Code?: string;
486
+ Title: string;
487
+ Abstract: string;
488
+ Version: string;
489
+ FormSpecVersion: string;
490
+ Steps: FormStepTS[];
491
+ CategoryTags: string[];
492
+ Score?: ScoreTS;
493
+ AssociatedCodes: string[];
494
+ RequiresReview: boolean;
495
+ RequiresReviewAndApproval: boolean;
496
+ ClinicalPathway?: ClinicalPathwaySpecTS;
497
+ }
498
+
499
+ interface FormStepTS {
500
+ StepOrder: number;
501
+ StepLabel: string;
502
+ Fields: FormFieldTS[];
503
+ }
504
+
505
+ interface FormFieldTS {
506
+ FieldOrder: number;
507
+ FieldKey: FieldKeyTS;
508
+ Label: string;
509
+ DependsOn?: DependsOnTS;
510
+ IsOptional: boolean;
511
+ IsDeprecated: boolean;
512
+ FieldType: FieldTypeTS;
513
+ }
514
+
515
+ interface FieldTypeTS {
516
+ Type: string; // "Text", "Checkbox", "Radio", etc.
517
+ TextInfo?: TextInfoTS;
518
+ BooleanInfo?: BooleanInfoTS;
519
+ SingleChoiceInfo?: SingleChoiceInfoTS;
520
+ MultiChoiceInfo?: MultiChoiceInfoTS;
521
+ MessageInfo?: MessageInfoTS;
522
+ MatrixInfo?: MatrixInfoTS;
523
+ SignatureInfo?: SignatureInfoTS;
524
+ PluginConfig?: PluginFieldConfigTS;
525
+ }
526
+ ```
527
+
528
+ ## Best Practices
529
+
530
+ 1. **Always Validate External Data**: Never trust data from APIs, files, or user input without validation
531
+ 2. **Use Builders for Type Safety**: Builders ensure all required fields are set correctly
532
+ 3. **Handle Both Result Cases**: Always handle both `fableOk` and `fableError` cases
533
+ 4. **Leverage TypeScript Types**: Import type definitions for IntelliSense and compile-time checking
534
+ 5. **Store GUIDs as Strings**: When persisting to databases, use the `Value` property of key types
535
+
536
+ ## Migration from Legacy Forms
537
+
538
+ If you have existing form data in a different format:
539
+
540
+ ```typescript
541
+ function migrateOldForm(oldForm: any): FormSpecTS {
542
+ const builder = Builders_formSpec(oldForm.title || "Untitled Form")
543
+ .WithVersion(oldForm.version || "1.0.0");
544
+
545
+ // Map old fields to new structure
546
+ oldForm.questions?.forEach((q: any, idx: number) => {
547
+ const field = Builders_formField(
548
+ q.text,
549
+ mapOldFieldType(q.type, q.options)
550
+ )
551
+ .WithOrder(idx + 1)
552
+ .Build();
553
+
554
+ // Add to step...
555
+ });
556
+
557
+ return builder.Build();
558
+ }
559
+
560
+ function mapOldFieldType(type: string, options?: any[]): FieldTypeTS {
561
+ switch(type) {
562
+ case 'text': return Api_createTextField(null);
563
+ case 'select': return Api_createDropdownField(
564
+ options?.map(o => Api_createFieldOption(o.label, o.value)) || []
565
+ );
566
+ // ... map other types
567
+ default: return Api_createTextField(null);
568
+ }
569
+ }
570
+ ```
571
+
572
+ ## Troubleshooting
573
+
574
+ ### Common Issues
575
+
576
+ **Issue**: Validation fails with "Invalid GUID for FieldKey"
577
+ **Solution**: Ensure you're using the key creation functions or builders which generate valid GUIDs
578
+
579
+ **Issue**: TypeScript can't find types
580
+ **Solution**: Import from the specific path: `@f1studio/form-spec/Interop/FormSpec.Api`
581
+
582
+ **Issue**: FableResult is not being recognized in switch statements
583
+ **Solution**: Check the discriminator property is exactly `result` (lowercase)
584
+
585
+ ## API Reference
586
+
587
+ ### Validation Functions
588
+ - `Api_validateFormSpec(input: any): FableResult<FormSpecTS, string>` - Validate unknown data to FormSpecTS
589
+ - `Api_validateAndConvertFormSpec(input: any): FableResult<FormSpec, string>` - Validate and convert to F# domain type
590
+ - `Api_validateFormField(input: any): FableResult<FormFieldTS, string>` - Validate a single field
591
+ - `Api_validateFormStep(input: any): FableResult<FormStepTS, string>` - Validate a form step
592
+ - `Api_validateFieldType(input: any): FableResult<FieldTypeTS, string>` - Validate a field type
593
+
594
+ ### Builder Functions
595
+ - `Api_createFormSpec(): FormSpecTS` - Create empty FormSpec
596
+ - `Api_createFieldKey(): FieldKeyTS` - Generate new field key
597
+ - `Api_createFieldOption(description: string, value: string): FieldOptionTS` - Create field option
598
+ - `Api_createTextField(value: string | null): FieldTypeTS` - Create text field
599
+ - `Api_createCheckboxField(defaultValue: boolean | null, selection: boolean | null): FieldTypeTS` - Create checkbox
600
+ - `Api_createRadioField(options: FieldOptionTS[]): FieldTypeTS` - Create radio button group
601
+ - `Api_createDropdownField(options: FieldOptionTS[]): FieldTypeTS` - Create dropdown
602
+
603
+ ### Serialization Functions
604
+ - `Api_serializeFormSpec(spec: FormSpecTS): string` - Convert to JSON string
605
+ - `Api_deserializeFormSpec(json: string): FableResult<FormSpecTS, string>` - Parse from JSON
606
+
607
+ ### Query Functions
608
+ - `Api_getFieldKeys(spec: FormSpecTS): FieldKeyTS[]` - Get all field keys
609
+ - `Api_findFieldByKey(spec: FormSpecTS, key: FieldKeyTS): FormFieldTS | undefined` - Find field by key
610
+ - `Api_getFieldsByType(spec: FormSpecTS, type: string): FormFieldTS[]` - Get fields of specific type
611
+ - `Api_isValidFormSpec(spec: FormSpecTS): boolean` - Check if spec is valid
612
+ - `Api_getFormSpecSummary(spec: FormSpecTS): object` - Get spec summary
613
+
614
+ ## Support
615
+
616
+ For issues, questions, or contributions:
617
+ - GitHub: https://github.com/f1-studio/f1-monorepo
618
+ - Documentation: https://f1studio.ai/docs
619
+
620
+ ## License
621
+
622
622
  MIT © F1 Studio