@fogpipe/forma-core 0.11.2 → 0.12.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/types.d.ts CHANGED
@@ -3,6 +3,16 @@
3
3
  *
4
4
  * This module defines the complete type system for Forma specifications.
5
5
  * All conditional logic uses FEEL (Friendly Enough Expression Language).
6
+ *
7
+ * FieldDefinition is a discriminated union on the required `type` property,
8
+ * grouped by shared capabilities:
9
+ * - AdornableFieldDefinition: text-like and numeric inputs with prefix/suffix
10
+ * - SelectionFieldDefinition: select/multiselect with options
11
+ * - SimpleFieldDefinition: boolean, date, datetime (no type-specific props)
12
+ * - ArrayFieldDefinition: array with itemFields, minItems, maxItems
13
+ * - ObjectFieldDefinition: nested object grouping
14
+ * - DisplayFieldDefinition: presentation content and computed output (no data)
15
+ * - ComputedFieldDefinition: computed field reference in fields map
6
16
  */
7
17
  /**
8
18
  * FEEL expression - a string that will be evaluated at runtime.
@@ -79,7 +89,7 @@ export interface JSONSchemaEnum extends JSONSchemaBase {
79
89
  /**
80
90
  * Field type enumeration - maps to UI component types
81
91
  */
82
- export type FieldType = "text" | "password" | "number" | "integer" | "boolean" | "select" | "multiselect" | "date" | "datetime" | "email" | "url" | "textarea" | "array" | "object" | "computed";
92
+ export type FieldType = "text" | "password" | "number" | "integer" | "boolean" | "select" | "multiselect" | "date" | "datetime" | "email" | "url" | "textarea" | "array" | "object" | "computed" | "display";
83
93
  /**
84
94
  * Validation rule with FEEL expression
85
95
  */
@@ -102,34 +112,137 @@ export interface SelectOption {
102
112
  visibleWhen?: FEELExpression;
103
113
  }
104
114
  /**
105
- * Field definition with all conditional logic
115
+ * Base properties shared by all field types.
116
+ *
117
+ * Note: Display fields technically inherit all base properties including
118
+ * conditional logic (enabledWhen, requiredWhen, etc.), but these properties
119
+ * are semantically meaningless on display fields and should not be set.
120
+ * Runtime validation (Zod layer) enforces this constraint.
106
121
  */
107
- export interface FieldDefinition {
108
- /** Display label for the field */
122
+ export interface FieldDefinitionBase {
123
+ /** Field type - REQUIRED, used as discriminant for the union */
124
+ type: FieldType;
125
+ /** Display label */
109
126
  label?: string;
110
127
  /** Help text or description */
111
128
  description?: string;
112
129
  /** Placeholder text for input fields */
113
130
  placeholder?: string;
114
- /** Field type override (inferred from schema if not provided) */
115
- type?: FieldType;
116
131
  /** When to show this field. If omitted, always visible. */
117
132
  visibleWhen?: FEELExpression;
118
133
  /** When this field is required. If omitted, uses schema required. */
119
134
  requiredWhen?: FEELExpression;
120
135
  /** When this field is editable. If omitted, always enabled. */
121
136
  enabledWhen?: FEELExpression;
137
+ /** When this field is read-only (visible, not editable, value still submitted). */
138
+ readonlyWhen?: FEELExpression;
122
139
  /** Custom validation rules using FEEL expressions */
123
140
  validations?: ValidationRule[];
124
- /** For array fields: field definitions for each item. Requires type: "array" */
141
+ /** Presentation variant hint (e.g., "slider", "radio", "nps", "metric") */
142
+ variant?: string;
143
+ /** Variant-specific configuration */
144
+ variantConfig?: Record<string, unknown>;
145
+ }
146
+ /**
147
+ * Text-like and numeric input fields that support prefix/suffix adorners.
148
+ *
149
+ * Adorners are cross-variant: a "$" prefix applies to input, stepper,
150
+ * and slider variants alike. That's why they're top-level properties
151
+ * rather than inside variantConfig.
152
+ */
153
+ export interface AdornableFieldDefinition extends FieldDefinitionBase {
154
+ type: "text" | "email" | "url" | "password" | "textarea" | "number" | "integer";
155
+ /** Text/symbol displayed before input (e.g., "$", "https://") */
156
+ prefix?: string;
157
+ /** Text/symbol displayed after input (e.g., "USD", "kg", "%") */
158
+ suffix?: string;
159
+ }
160
+ /**
161
+ * Selection fields that have options.
162
+ */
163
+ export interface SelectionFieldDefinition extends FieldDefinitionBase {
164
+ type: "select" | "multiselect";
165
+ /** Available options for selection */
166
+ options?: SelectOption[];
167
+ }
168
+ /**
169
+ * Simple fields with no type-specific properties beyond the base.
170
+ */
171
+ export interface SimpleFieldDefinition extends FieldDefinitionBase {
172
+ type: "boolean" | "date" | "datetime";
173
+ }
174
+ /**
175
+ * Array fields with item field definitions and count constraints.
176
+ */
177
+ export interface ArrayFieldDefinition extends FieldDefinitionBase {
178
+ type: "array";
179
+ /** Field definitions for each array item. Use `item.fieldName` in FEEL. */
125
180
  itemFields?: Record<string, FieldDefinition>;
126
181
  /** Minimum number of items (overrides schema) */
127
182
  minItems?: number;
128
183
  /** Maximum number of items (overrides schema) */
129
184
  maxItems?: number;
130
- /** For select fields: available options */
131
- options?: SelectOption[];
132
185
  }
186
+ /**
187
+ * Object fields (nested grouping).
188
+ */
189
+ export interface ObjectFieldDefinition extends FieldDefinitionBase {
190
+ type: "object";
191
+ }
192
+ /**
193
+ * Display-only fields for presentation content and computed output.
194
+ * Collect no data - excluded from JSON Schema, validation, and submission.
195
+ *
196
+ * Although display fields inherit all base properties, conditional logic
197
+ * properties (readonlyWhen, requiredWhen, enabledWhen, validations, placeholder)
198
+ * are semantically meaningless on display fields. Only visibleWhen is meaningful
199
+ * (to conditionally show/hide display content). The Zod validation layer
200
+ * enforces this constraint at runtime.
201
+ */
202
+ export interface DisplayFieldDefinition extends FieldDefinitionBase {
203
+ type: "display";
204
+ /** Static content (plain text or markdown). For text, heading, alert, callout. */
205
+ content?: string;
206
+ /** Data source for dynamic display (e.g., "computed.totalPrice"). For metric, alert, summary. */
207
+ source?: string;
208
+ /** Display format (e.g., "currency", "percent", "decimal(2)", or template "{value} Nm") */
209
+ format?: string;
210
+ }
211
+ /**
212
+ * Computed fields (read-only calculated values).
213
+ * Note: Computed field definitions live in Forma.computed, not Forma.fields.
214
+ * This type exists for completeness if computed fields appear in the fields map.
215
+ */
216
+ export interface ComputedFieldDefinition extends FieldDefinitionBase {
217
+ type: "computed";
218
+ }
219
+ /**
220
+ * Field definition - discriminated union on `type`.
221
+ *
222
+ * Use type narrowing to access type-specific properties:
223
+ *
224
+ * @example
225
+ * if (field.type === "number") {
226
+ * field.prefix; // string | undefined
227
+ * }
228
+ * if (field.type === "display") {
229
+ * field.content; // string | undefined
230
+ * }
231
+ * if (field.type === "select") {
232
+ * field.options; // SelectOption[] | undefined
233
+ * }
234
+ */
235
+ export type FieldDefinition = AdornableFieldDefinition | SelectionFieldDefinition | SimpleFieldDefinition | ArrayFieldDefinition | ObjectFieldDefinition | DisplayFieldDefinition | ComputedFieldDefinition;
236
+ /** Check if field supports prefix/suffix adorners */
237
+ export declare function isAdornableField(field: FieldDefinition): field is AdornableFieldDefinition;
238
+ /** Check if field is a display-only field (no data) */
239
+ export declare function isDisplayField(field: FieldDefinition): field is DisplayFieldDefinition;
240
+ /** Check if field is a selection field (has options) */
241
+ export declare function isSelectionField(field: FieldDefinition): field is SelectionFieldDefinition;
242
+ /** Check if field is an array field */
243
+ export declare function isArrayField(field: FieldDefinition): field is ArrayFieldDefinition;
244
+ /** Check if field collects data (not display) */
245
+ export declare function isDataField(field: FieldDefinition): boolean;
133
246
  /**
134
247
  * Computed field definition - derived values from form data
135
248
  */
@@ -241,6 +354,12 @@ export type RequiredFieldsResult = RequiredResult;
241
354
  export interface EnabledResult {
242
355
  [fieldPath: string]: boolean;
243
356
  }
357
+ /**
358
+ * Readonly fields result - map of field path to readonly state
359
+ */
360
+ export interface ReadonlyResult {
361
+ [fieldPath: string]: boolean;
362
+ }
244
363
  /**
245
364
  * Field validation error
246
365
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fogpipe/forma-core",
3
- "version": "0.11.2",
3
+ "version": "0.12.0-alpha.2",
4
4
  "description": "Forma core runtime: Types and evaluation engines for dynamic forms with FEEL expressions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -589,6 +589,7 @@ describe("validate", () => {
589
589
  type: "array",
590
590
  itemFields: {
591
591
  quantity: {
592
+ type: "number",
592
593
  label: "Quantity",
593
594
  validations: [
594
595
  { rule: "value <= 100", message: "Cannot order more than 100 items" },
@@ -11,6 +11,7 @@ import type {
11
11
  FieldDefinition,
12
12
  EvaluationContext,
13
13
  EnabledResult,
14
+ ArrayFieldDefinition,
14
15
  } from "../types.js";
15
16
  import { calculate } from "./calculate.js";
16
17
 
@@ -69,24 +70,10 @@ export function getEnabled(
69
70
 
70
71
  // Also check array item fields
71
72
  for (const [fieldPath, fieldDef] of Object.entries(spec.fields)) {
72
- if (fieldDef.itemFields) {
73
+ if (fieldDef.type === "array" && fieldDef.itemFields) {
73
74
  const arrayData = data[fieldPath];
74
75
  if (Array.isArray(arrayData)) {
75
- for (let i = 0; i < arrayData.length; i++) {
76
- const item = arrayData[i] as Record<string, unknown>;
77
- const itemContext: EvaluationContext = {
78
- data,
79
- computed,
80
- referenceData: spec.referenceData,
81
- item,
82
- itemIndex: i,
83
- };
84
-
85
- for (const [itemFieldName, itemFieldDef] of Object.entries(fieldDef.itemFields)) {
86
- const itemFieldPath = `${fieldPath}[${i}].${itemFieldName}`;
87
- result[itemFieldPath] = isFieldEnabled(itemFieldDef, itemContext);
88
- }
89
- }
76
+ evaluateArrayItemEnabled(fieldPath, fieldDef, arrayData, data, computed, spec, result);
90
77
  }
91
78
  }
92
79
  }
@@ -114,6 +101,37 @@ function isFieldEnabled(
114
101
  return true;
115
102
  }
116
103
 
104
+ /**
105
+ * Evaluate enabled state for array item fields
106
+ */
107
+ function evaluateArrayItemEnabled(
108
+ arrayPath: string,
109
+ fieldDef: ArrayFieldDefinition,
110
+ arrayData: unknown[],
111
+ data: Record<string, unknown>,
112
+ computed: Record<string, unknown>,
113
+ spec: Forma,
114
+ result: EnabledResult
115
+ ): void {
116
+ if (!fieldDef.itemFields) return;
117
+
118
+ for (let i = 0; i < arrayData.length; i++) {
119
+ const item = arrayData[i] as Record<string, unknown>;
120
+ const itemContext: EvaluationContext = {
121
+ data,
122
+ computed,
123
+ referenceData: spec.referenceData,
124
+ item,
125
+ itemIndex: i,
126
+ };
127
+
128
+ for (const [itemFieldName, itemFieldDef] of Object.entries(fieldDef.itemFields)) {
129
+ const itemFieldPath = `${arrayPath}[${i}].${itemFieldName}`;
130
+ result[itemFieldPath] = isFieldEnabled(itemFieldDef, itemContext);
131
+ }
132
+ }
133
+ }
134
+
117
135
  /**
118
136
  * Check if a single field is currently enabled
119
137
  *
@@ -57,6 +57,16 @@ export type {
57
57
  EnabledOptions,
58
58
  } from "./enabled.js";
59
59
 
60
+ // Readonly
61
+ export {
62
+ getReadonly,
63
+ isReadonly,
64
+ } from "./readonly.js";
65
+
66
+ export type {
67
+ ReadonlyOptions,
68
+ } from "./readonly.js";
69
+
60
70
  // Validate
61
71
  export {
62
72
  validate,
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Readonly Fields Engine
3
+ *
4
+ * Determines which fields are currently read-only based on
5
+ * conditional readonlyWhen expressions.
6
+ *
7
+ * Readonly vs disabled:
8
+ * - readonlyWhen: field is visible with normal appearance, not editable, value IS submitted
9
+ * - enabledWhen: field is greyed out/dimmed, not interactive, value may be excluded
10
+ */
11
+
12
+ import { evaluateBoolean } from "../feel/index.js";
13
+ import type {
14
+ Forma,
15
+ FieldDefinition,
16
+ ArrayFieldDefinition,
17
+ EvaluationContext,
18
+ ReadonlyResult,
19
+ } from "../types.js";
20
+ import { calculate } from "./calculate.js";
21
+
22
+ // ============================================================================
23
+ // Types
24
+ // ============================================================================
25
+
26
+ export interface ReadonlyOptions {
27
+ /** Pre-calculated computed values */
28
+ computed?: Record<string, unknown>;
29
+ }
30
+
31
+ // ============================================================================
32
+ // Main Function
33
+ // ============================================================================
34
+
35
+ /**
36
+ * Determine which fields are currently read-only
37
+ *
38
+ * Returns a map of field paths to boolean readonly states.
39
+ * Fields without readonlyWhen expressions are never readonly.
40
+ *
41
+ * @param data - Current form data
42
+ * @param spec - Form specification
43
+ * @param options - Optional pre-calculated computed values
44
+ * @returns Map of field paths to readonly states
45
+ *
46
+ * @example
47
+ * const readonly = getReadonly(
48
+ * { status: "submitted" },
49
+ * forma
50
+ * );
51
+ * // => { status: false, policyNumber: true, ... }
52
+ */
53
+ export function getReadonly(
54
+ data: Record<string, unknown>,
55
+ spec: Forma,
56
+ options: ReadonlyOptions = {}
57
+ ): ReadonlyResult {
58
+ const computed = options.computed ?? calculate(data, spec);
59
+ const context: EvaluationContext = {
60
+ data,
61
+ computed,
62
+ referenceData: spec.referenceData,
63
+ };
64
+
65
+ const result: ReadonlyResult = {};
66
+
67
+ // Evaluate each field's readonly status
68
+ for (const fieldPath of spec.fieldOrder) {
69
+ const fieldDef = spec.fields[fieldPath];
70
+ if (fieldDef) {
71
+ result[fieldPath] = isFieldReadonly(fieldDef, context);
72
+ }
73
+ }
74
+
75
+ // Also check array item fields
76
+ for (const [fieldPath, fieldDef] of Object.entries(spec.fields)) {
77
+ if (fieldDef.type === "array" && fieldDef.itemFields) {
78
+ const arrayData = data[fieldPath];
79
+ if (Array.isArray(arrayData)) {
80
+ evaluateArrayItemReadonly(fieldPath, fieldDef, arrayData, data, computed, spec, result);
81
+ }
82
+ }
83
+ }
84
+
85
+ return result;
86
+ }
87
+
88
+ // ============================================================================
89
+ // Field Readonly Check
90
+ // ============================================================================
91
+
92
+ /**
93
+ * Check if a field is readonly based on its definition
94
+ */
95
+ function isFieldReadonly(
96
+ fieldDef: FieldDefinition,
97
+ context: EvaluationContext
98
+ ): boolean {
99
+ // If field has readonlyWhen, evaluate it
100
+ if (fieldDef.readonlyWhen) {
101
+ return evaluateBoolean(fieldDef.readonlyWhen, context);
102
+ }
103
+
104
+ // No condition = never readonly
105
+ return false;
106
+ }
107
+
108
+ /**
109
+ * Evaluate readonly state for array item fields
110
+ */
111
+ function evaluateArrayItemReadonly(
112
+ arrayPath: string,
113
+ fieldDef: ArrayFieldDefinition,
114
+ arrayData: unknown[],
115
+ data: Record<string, unknown>,
116
+ computed: Record<string, unknown>,
117
+ spec: Forma,
118
+ result: ReadonlyResult
119
+ ): void {
120
+ if (!fieldDef.itemFields) return;
121
+
122
+ for (let i = 0; i < arrayData.length; i++) {
123
+ const item = arrayData[i] as Record<string, unknown>;
124
+ const itemContext: EvaluationContext = {
125
+ data,
126
+ computed,
127
+ referenceData: spec.referenceData,
128
+ item,
129
+ itemIndex: i,
130
+ };
131
+
132
+ for (const [itemFieldName, itemFieldDef] of Object.entries(fieldDef.itemFields)) {
133
+ const itemFieldPath = `${arrayPath}[${i}].${itemFieldName}`;
134
+ result[itemFieldPath] = isFieldReadonly(itemFieldDef, itemContext);
135
+ }
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Check if a single field is currently readonly
141
+ *
142
+ * @param fieldPath - Path to the field
143
+ * @param data - Current form data
144
+ * @param spec - Form specification
145
+ * @returns True if the field is readonly
146
+ */
147
+ export function isReadonly(
148
+ fieldPath: string,
149
+ data: Record<string, unknown>,
150
+ spec: Forma
151
+ ): boolean {
152
+ const fieldDef = spec.fields[fieldPath];
153
+ if (!fieldDef) {
154
+ return false; // Unknown fields are not readonly by default
155
+ }
156
+
157
+ if (!fieldDef.readonlyWhen) {
158
+ return false; // No condition = never readonly
159
+ }
160
+
161
+ const computed = calculate(data, spec);
162
+ const context: EvaluationContext = {
163
+ data,
164
+ computed,
165
+ referenceData: spec.referenceData,
166
+ };
167
+
168
+ return evaluateBoolean(fieldDef.readonlyWhen, context);
169
+ }
@@ -69,7 +69,7 @@ export function getRequired(
69
69
 
70
70
  // Also check array item fields
71
71
  for (const [fieldPath, fieldDef] of Object.entries(spec.fields)) {
72
- if (fieldDef.itemFields) {
72
+ if (fieldDef.type === "array" && fieldDef.itemFields) {
73
73
  const arrayData = data[fieldPath];
74
74
  if (Array.isArray(arrayData)) {
75
75
  for (let i = 0; i < arrayData.length; i++) {
@@ -12,6 +12,7 @@ import { evaluateBoolean } from "../feel/index.js";
12
12
  import type {
13
13
  Forma,
14
14
  FieldDefinition,
15
+ ArrayFieldDefinition,
15
16
  ValidationRule,
16
17
  EvaluationContext,
17
18
  ValidationResult,
@@ -95,6 +96,11 @@ export function validate(
95
96
  continue;
96
97
  }
97
98
 
99
+ // Skip display fields - they collect no data and need no validation
100
+ if (fieldDef.type === "display") {
101
+ continue;
102
+ }
103
+
98
104
  // Get schema property for type validation
99
105
  const schemaProperty = spec.schema.properties[fieldPath];
100
106
 
@@ -173,7 +179,7 @@ function validateField(
173
179
  }
174
180
 
175
181
  // 4. Array validation
176
- if (Array.isArray(value)) {
182
+ if (Array.isArray(value) && fieldDef.type === "array") {
177
183
  const arrayErrors = validateArray(
178
184
  path,
179
185
  value,
@@ -212,7 +218,7 @@ function validateType(
212
218
  path: string,
213
219
  value: unknown,
214
220
  schema: JSONSchemaProperty,
215
- fieldDef: FieldDefinition
221
+ fieldDef: { label?: string }
216
222
  ): FieldError | null {
217
223
  const label = fieldDef.label ?? path;
218
224
 
@@ -514,7 +520,7 @@ function validateCustomRules(
514
520
  function validateArray(
515
521
  path: string,
516
522
  value: unknown[],
517
- fieldDef: FieldDefinition,
523
+ fieldDef: ArrayFieldDefinition,
518
524
  schemaProperty: JSONSchemaProperty | undefined,
519
525
  spec: Forma,
520
526
  data: Record<string, unknown>,
@@ -9,10 +9,12 @@ import { evaluateBoolean } from "../feel/index.js";
9
9
  import type {
10
10
  Forma,
11
11
  FieldDefinition,
12
+ ArrayFieldDefinition,
12
13
  EvaluationContext,
13
14
  VisibilityResult,
14
15
  SelectOption,
15
16
  } from "../types.js";
17
+ import { isArrayField, isSelectionField } from "../types.js";
16
18
  import { calculate } from "./calculate.js";
17
19
 
18
20
  // ============================================================================
@@ -62,7 +64,7 @@ function filterOptionsByContext(
62
64
  */
63
65
  function processArrayItemOptions(
64
66
  arrayPath: string,
65
- fieldDef: FieldDefinition,
67
+ fieldDef: ArrayFieldDefinition,
66
68
  arrayData: readonly unknown[],
67
69
  baseContext: EvaluationContext,
68
70
  result: Record<string, SelectOption[]>
@@ -78,7 +80,7 @@ function processArrayItemOptions(
78
80
  };
79
81
 
80
82
  for (const [itemFieldName, itemFieldDef] of Object.entries(fieldDef.itemFields)) {
81
- if (itemFieldDef.options && itemFieldDef.options.length > 0) {
83
+ if (isSelectionField(itemFieldDef) && itemFieldDef.options && itemFieldDef.options.length > 0) {
82
84
  const itemFieldPath = `${arrayPath}[${i}].${itemFieldName}`;
83
85
  result[itemFieldPath] = filterOptionsByContext(itemFieldDef.options, itemContext);
84
86
  }
@@ -153,7 +155,7 @@ function evaluateFieldVisibility(
153
155
  return;
154
156
  }
155
157
 
156
- if (fieldDef.itemFields) {
158
+ if (isArrayField(fieldDef) && fieldDef.itemFields) {
157
159
  const arrayData = data[path];
158
160
  if (Array.isArray(arrayData)) {
159
161
  evaluateArrayItemVisibility(path, fieldDef, arrayData, context, result);
@@ -166,7 +168,7 @@ function evaluateFieldVisibility(
166
168
  */
167
169
  function evaluateArrayItemVisibility(
168
170
  arrayPath: string,
169
- fieldDef: FieldDefinition,
171
+ fieldDef: ArrayFieldDefinition,
170
172
  arrayData: unknown[],
171
173
  baseContext: EvaluationContext,
172
174
  result: VisibilityResult
@@ -308,12 +310,12 @@ export function getOptionsVisibility(
308
310
  if (!fieldDef) continue;
309
311
 
310
312
  // Top-level fields with options
311
- if (fieldDef.options && fieldDef.options.length > 0) {
313
+ if (isSelectionField(fieldDef) && fieldDef.options && fieldDef.options.length > 0) {
312
314
  result[fieldPath] = filterOptionsByContext(fieldDef.options, baseContext);
313
315
  }
314
316
 
315
317
  // Array item fields with options
316
- if (fieldDef.itemFields) {
318
+ if (isArrayField(fieldDef) && fieldDef.itemFields) {
317
319
  const arrayData = data[fieldPath];
318
320
  if (Array.isArray(arrayData)) {
319
321
  processArrayItemOptions(fieldPath, fieldDef, arrayData, baseContext, result);
package/src/index.ts CHANGED
@@ -20,11 +20,19 @@ export type {
20
20
  JSONSchemaArray,
21
21
  JSONSchemaObject,
22
22
  JSONSchemaEnum,
23
- // Field types
23
+ // Field types - base and groups
24
24
  FieldType,
25
+ FieldDefinitionBase,
26
+ AdornableFieldDefinition,
27
+ SelectionFieldDefinition,
28
+ SimpleFieldDefinition,
29
+ ArrayFieldDefinition,
30
+ ObjectFieldDefinition,
31
+ DisplayFieldDefinition,
32
+ ComputedFieldDefinition,
33
+ FieldDefinition,
25
34
  ValidationRule,
26
35
  SelectOption,
27
- FieldDefinition,
28
36
  ComputedField,
29
37
  PageDefinition,
30
38
  FormMeta,
@@ -36,12 +44,22 @@ export type {
36
44
  RequiredResult,
37
45
  RequiredFieldsResult,
38
46
  EnabledResult,
47
+ ReadonlyResult,
39
48
  FieldError,
40
49
  ValidationResult,
41
50
  CalculationError,
42
51
  CalculationResult,
43
52
  } from "./types.js";
44
53
 
54
+ // Type guards (runtime exports)
55
+ export {
56
+ isAdornableField,
57
+ isDisplayField,
58
+ isSelectionField,
59
+ isArrayField,
60
+ isDataField,
61
+ } from "./types.js";
62
+
45
63
  // FEEL expression evaluation
46
64
  export * from "./feel/index.js";
47
65