@formality-ui/core 0.0.0 → 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.
package/dist/index.d.cts CHANGED
@@ -1,3 +1,28 @@
1
+ /**
2
+ * FieldMatcher - Matchers for a single field in multi-field conditions
3
+ *
4
+ * Used when `when` is an object mapping field names to their matchers.
5
+ * All specified matchers must pass (AND logic).
6
+ */
7
+ interface FieldMatcher {
8
+ /** Exact value match */
9
+ is?: unknown;
10
+ /** Truthy/falsy check (alias: isTruthy) */
11
+ truthy?: boolean;
12
+ /** Truthy/falsy check (alias for truthy) */
13
+ isTruthy?: boolean;
14
+ /** Field validity check - true if field has no errors */
15
+ isValid?: boolean;
16
+ /** Field disabled state check */
17
+ isDisabled?: boolean;
18
+ }
19
+ /**
20
+ * Multi-field when trigger - maps field names to their matchers
21
+ *
22
+ * All field conditions must match (AND logic).
23
+ * Example: { email: { isValid: true }, name: { isTruthy: true } }
24
+ */
25
+ type WhenMultiField = Record<string, FieldMatcher>;
1
26
  /**
2
27
  * ConditionDescriptor - Defines a single condition rule
3
28
  *
@@ -7,10 +32,11 @@
7
32
  */
8
33
  interface ConditionDescriptor {
9
34
  /**
10
- * Simple field reference trigger
11
- * Example: "client" - triggers when client field has a value
35
+ * Field reference trigger - can be:
36
+ * - string: Single field reference (e.g., "client")
37
+ * - object: Multi-field with per-field matchers (e.g., { email: { isValid: true }, name: { isTruthy: true } })
12
38
  */
13
- when?: string;
39
+ when?: string | WhenMultiField;
14
40
  /**
15
41
  * Expression or function trigger
16
42
  * Example: "client.id > 5" or ({ fields }) => fields.client?.value?.id > 5
@@ -27,6 +53,18 @@ interface ConditionDescriptor {
27
53
  * false: matches if trigger is falsy
28
54
  */
29
55
  truthy?: boolean;
56
+ /**
57
+ * Field validity check (requires 'when' trigger)
58
+ * true: matches if the 'when' field is valid (no errors)
59
+ * false: matches if the 'when' field is invalid (has errors)
60
+ */
61
+ isValid?: boolean;
62
+ /**
63
+ * Field disabled state check (requires 'when' trigger)
64
+ * true: matches if the 'when' field is disabled
65
+ * false: matches if the 'when' field is enabled
66
+ */
67
+ isDisabled?: boolean;
30
68
  /**
31
69
  * Set disabled state
32
70
  * Multiple conditions use OR logic (any true = disabled)
@@ -117,16 +155,33 @@ type ValidatorSpec = string | ValidatorFunction | Array<string | ValidatorFuncti
117
155
  /**
118
156
  * ValidatorFactory - Factory function for parameterized validators
119
157
  *
120
- * Example: minLength(5) returns a ValidatorFunction
158
+ * Accepts any number of factory arguments and returns a {@link ValidatorFunction}.
159
+ * The `...args: any[]` rest signature is intentional: it lets concrete factories
160
+ * with specific parameter types (e.g. `(min: number) => ValidatorFunction`,
161
+ * `(regex: RegExp, message: string) => ValidatorFunction`) be assignable to this
162
+ * type without parameter-invariance errors, while still producing a fully typed
163
+ * `ValidatorFunction` return.
164
+ *
165
+ * @example
166
+ * const min: ValidatorFactory = (minVal: number) => (value) =>
167
+ * Number(value) < minVal ? { type: 'min' } : true;
168
+ */
169
+ type ValidatorFactory = (...args: any[]) => ValidatorFunction;
170
+ /**
171
+ * ValidatorEntry - A named validator or a validator factory.
172
+ *
173
+ * Either a direct {@link ValidatorFunction} (called with `value` + `formValues`)
174
+ * or a {@link ValidatorFactory} (called with factory args to produce a
175
+ * `ValidatorFunction`).
121
176
  */
122
- type ValidatorFactory<TArgs = unknown> = (args: TArgs) => ValidatorFunction;
177
+ type ValidatorEntry = ValidatorFunction | ValidatorFactory;
123
178
  /**
124
179
  * ValidatorsConfig - Named validators configuration
125
180
  *
126
- * Values can be direct validators or validator factories
181
+ * Values can be direct {@link ValidatorFunction}s or {@link ValidatorFactory}s.
127
182
  */
128
183
  interface ValidatorsConfig {
129
- [name: string]: ValidatorFunction | ValidatorFactory;
184
+ [name: string]: ValidatorEntry;
130
185
  }
131
186
  /**
132
187
  * ErrorMessagesConfig - Error message templates by type key
@@ -164,6 +219,8 @@ interface FieldState {
164
219
  error?: FieldError;
165
220
  /** Inverse of valid state for convenience */
166
221
  invalid: boolean;
222
+ /** Whether the field is disabled */
223
+ disabled?: boolean;
167
224
  /** Map of subscriber field names watching this field */
168
225
  watchers?: Record<string, boolean>;
169
226
  }
@@ -230,9 +287,24 @@ type SelectFunction<TReturn = unknown> = (formState: FormState, methods: unknown
230
287
  *
231
288
  * This defines how a particular input type (e.g., textField, switch, autocomplete)
232
289
  * behaves across all forms.
290
+ *
291
+ * Framework agnosticism: `component` and `template` are intentionally `unknown`
292
+ * here (core cannot import a UI framework). React consumers should use the
293
+ * `ReactInputConfig<TValue>` overlay exported from `@formality-ui/react`, which
294
+ * narrows both to `ComponentType<...>`.
295
+ *
296
+ * `TValue` (default `unknown`) links `defaultValue`, `parser`, and `formatter`
297
+ * to a single value type. It defaults to `unknown` because the framework-agnostic
298
+ * core cannot know the value type of every UI component; React consumers can
299
+ * parameterize it via `ReactInputConfig<TValue>` (e.g.
300
+ * `ReactInputConfig<string>` for a text field). Today only the overlay exposes
301
+ * this parameterization; end-to-end per-input value inference is a future
302
+ * enhancement.
303
+ *
304
+ * @template TValue - The form value type this input produces/consumes.
233
305
  */
234
306
  interface InputConfig<TValue = unknown> {
235
- /** The React component to render */
307
+ /** The component to render (typed `unknown` for framework agnosticism; React consumers see `ComponentType<any>` via `ReactInputConfig`) */
236
308
  component: unknown;
237
309
  /** Default value for this input type (e.g., '' for text, false for switch) */
238
310
  defaultValue: TValue;
@@ -250,7 +322,7 @@ interface InputConfig<TValue = unknown> {
250
322
  formatter?: string | ((value: TValue) => unknown);
251
323
  /** Type-level validation (runs after field-level validator) */
252
324
  validator?: ValidatorSpec;
253
- /** Template component wrapper for consistent styling */
325
+ /** Template component wrapper for consistent styling (typed `unknown` for framework agnosticism; React consumers see `ComponentType<InputTemplateProps>` via `ReactInputConfig`) */
254
326
  template?: unknown;
255
327
  /** Default props for this input type */
256
328
  props?: Record<string, unknown>;
@@ -260,6 +332,12 @@ interface InputConfig<TValue = unknown> {
260
332
  *
261
333
  * This defines field-level behavior including conditions, validation,
262
334
  * and dynamic props.
335
+ *
336
+ * Framework agnosticism: `rules` is intentionally `Record<string, unknown>` here
337
+ * (core cannot import react-hook-form). React consumers should use the
338
+ * `ReactFieldConfig` overlay exported from `@formality-ui/react`, which narrows
339
+ * `rules` to react-hook-form's `RegisterOptions` for full autocomplete and
340
+ * checking (required, min, max, pattern, validate, valueAsNumber, …).
263
341
  */
264
342
  interface FieldConfig {
265
343
  /** Input type key (resolves to InputConfig) */
@@ -276,7 +354,7 @@ interface FieldConfig {
276
354
  order?: number;
277
355
  /** Key to use when reading initial value from record (defaults to field name) */
278
356
  recordKey?: string;
279
- /** React Hook Form RegisterOptions (required, min, max, pattern, etc.) */
357
+ /** Register options forwarded to the framework's field register call (typed loose for framework agnosticism; React consumers see react-hook-form's `RegisterOptions` via `ReactFieldConfig`) */
280
358
  rules?: Record<string, unknown>;
281
359
  /** Field-level validation (runs before type-level validator) */
282
360
  validator?: ValidatorSpec;
@@ -297,8 +375,12 @@ interface FieldConfig {
297
375
  }
298
376
  /**
299
377
  * FormFieldsConfig - Map of field names to their configurations
378
+ *
379
+ * Generic over the field-name union so a typed `<Form<TFieldValues>>` can reject
380
+ * unknown config keys. Defaults to `string`, which is identical to the previous
381
+ * non-generic `Record<string, FieldConfig>` (backwards compatible).
300
382
  */
301
- type FormFieldsConfig = Record<string, FieldConfig>;
383
+ type FormFieldsConfig<TName extends string = string> = Record<TName, FieldConfig>;
302
384
  /**
303
385
  * GroupConfig - Configuration for a FieldGroup
304
386
  */
@@ -334,6 +416,11 @@ interface FormConfig {
334
416
  * FormalityProviderConfig - Global provider configuration
335
417
  *
336
418
  * Sets up input types, transformers, validators, and global defaults.
419
+ *
420
+ * Framework agnosticism: `defaultInputTemplate` and `inputTemplates` are
421
+ * intentionally `unknown` here (core cannot import a UI framework). React's
422
+ * `FormalityProviderProps` and `ConfigContextValue` overlay
423
+ * `ComponentType<InputTemplateProps>` on top of these loose fields.
337
424
  */
338
425
  interface FormalityProviderConfig {
339
426
  /** Input type definitions */
@@ -359,6 +446,10 @@ interface FormalityProviderConfig {
359
446
  }
360
447
  /**
361
448
  * InputTemplateProps - Props passed to input template components
449
+ *
450
+ * Framework agnosticism: `Field` is intentionally `unknown` here. React overlays
451
+ * this same-named type in `@formality-ui/react` with `Field: ComponentType<any>`
452
+ * and RHF-typed `fieldState`/`formState`; React consumers import that overlay.
362
453
  */
363
454
  interface InputTemplateProps {
364
455
  /** The input component to render */
@@ -481,6 +572,7 @@ interface FieldStateForContext {
481
572
  isValidating?: boolean;
482
573
  error?: unknown;
483
574
  invalid?: boolean;
575
+ disabled?: boolean;
484
576
  }
485
577
  /**
486
578
  * Build a minimal evaluation context from field values
@@ -500,8 +592,8 @@ declare function buildEvaluationContext(fieldValues: Record<string, unknown>, re
500
592
  * Extract field names from an expression string
501
593
  *
502
594
  * Scans the expression for unqualified identifiers that represent field names.
503
- * Skips JavaScript keywords. Qualified prefixes (fields, record, props, etc.)
504
- * are only skipped when followed by a dot.
595
+ * Skips JavaScript keywords and identifiers inside string literals.
596
+ * Qualified prefixes (fields, record, props, etc.) are only skipped when followed by a dot.
505
597
  *
506
598
  * @param expr - Expression string to analyze
507
599
  * @returns Array of unique field names referenced
@@ -512,6 +604,7 @@ declare function buildEvaluationContext(fieldValues: Record<string, unknown>, re
512
604
  * inferFieldsFromExpression("record.name") // → [] (qualified path)
513
605
  * inferFieldsFromExpression("true && false") // → [] (keywords)
514
606
  * inferFieldsFromExpression("fields === null") // → ["fields"] (not followed by dot)
607
+ * inferFieldsFromExpression('signed ? "target-on" : "target-off"') // → ["signed"] (string contents skipped)
515
608
  */
516
609
  declare function inferFieldsFromExpression(expr: string): string[];
517
610
  /**
@@ -541,6 +634,7 @@ interface FieldStateInput {
541
634
  isValidating?: boolean;
542
635
  error?: unknown;
543
636
  invalid?: boolean;
637
+ disabled?: boolean;
544
638
  }
545
639
  /**
546
640
  * Input for condition evaluation
@@ -589,9 +683,10 @@ declare function evaluateConditions(input: EvaluateConditionsInput): ConditionRe
589
683
  * @param fieldValues - Map of field names to their current values
590
684
  * @param record - Optional record for expression context
591
685
  * @param props - Optional props for expression context
686
+ * @param fieldStates - Optional field states for state-based matchers
592
687
  * @returns true if the condition matches
593
688
  */
594
- declare function conditionMatches(condition: ConditionDescriptor, fieldValues: Record<string, unknown>, record?: Record<string, unknown>, props?: Record<string, unknown>): boolean;
689
+ declare function conditionMatches(condition: ConditionDescriptor, fieldValues: Record<string, unknown>, record?: Record<string, unknown>, props?: Record<string, unknown>, fieldStates?: Record<string, FieldStateInput>): boolean;
595
690
  /**
596
691
  * Merge condition results from multiple sources (e.g., group + field)
597
692
  *
@@ -917,7 +1012,7 @@ declare function deepMerge<T extends object>(base: T, override: Partial<T> | und
917
1012
  * @param formInputs - Form input configs (or function to transform)
918
1013
  * @returns Merged input configs
919
1014
  */
920
- declare function mergeInputConfigs(providerInputs: Record<string, InputConfig>, formInputs?: FormConfig['inputs']): Record<string, InputConfig>;
1015
+ declare function mergeInputConfigs(providerInputs: Record<string, InputConfig>, formInputs?: FormConfig["inputs"]): Record<string, InputConfig>;
921
1016
  /**
922
1017
  * Resolve input config for a specific type
923
1018
  *
@@ -1179,4 +1274,4 @@ declare function getUnusedFields(allFields: string[], declaredFields: Set<string
1179
1274
  */
1180
1275
  declare function getOrderedUnusedFields(allFields: string[], declaredFields: Set<string>, fieldConfigs: Record<string, FieldConfig>): string[];
1181
1276
 
1182
- export { type ConditionDescriptor, type ConditionResult, type ErrorMessagesConfig, type EvaluateConditionsInput, type EvaluationContext, FIELD_PROXY_MARKER, FIELD_PROXY_VALUE, type FieldConfig, type FieldError, type FieldState, type FieldStateInput, type FormConfig, type FormFieldsConfig, type FormState, type FormalityProviderConfig, type FormatterFunction, type FormatterSpec, type FormattersConfig, type GroupConfig, type InputConfig, type InputTemplateProps, KEYWORDS, type ParserFunction, type ParserSpec, type ParsersConfig, QUALIFIED_PREFIXES, type SelectFunction, type SelectValue, type ValidationResult, type ValidatorFactory, type ValidatorFunction, type ValidatorSpec, type ValidatorsConfig, buildEvaluationContext, buildFieldContext, buildFormContext, clearExpressionCache, composeValidators, conditionMatches, createConfigContext, createDefaultFormatters, createDefaultParsers, createErrorMessages, createFieldStateProxy, createFloatFormatter, createFloatParser, createIntParser, createLabelWithUnit, createTrimParser, createValidationError, deepMerge, evaluate, evaluateConditions, evaluateDescriptor, extractValueField, format, formatTypeAsMessage, getErrorType, getInputDefaultValue, getOrderedUnusedFields, getUnusedFields, humanizeLabel, inferFieldsFromConditions, inferFieldsFromDescriptor, inferFieldsFromExpression, isAutoGeneratedLabel, isEmptyValue, isFieldProxy, isValid, maxLength, mergeConditionResults, mergeFieldProps, mergeInputConfigs, mergeRecordWithDefaults, mergeStaticProps, minLength, parse, parseLabelWithUnit, pattern, required, resolveAllInitialValues, resolveErrorMessage, resolveFieldType, resolveFormTitle, resolveInitialValue, resolveInputConfig, resolveLabel, runValidator, runValidatorSync, sortFieldsByOrder, transformFieldName, unwrapFieldProxy };
1277
+ export { type ConditionDescriptor, type ConditionResult, type ErrorMessagesConfig, type EvaluateConditionsInput, type EvaluationContext, FIELD_PROXY_MARKER, FIELD_PROXY_VALUE, type FieldConfig, type FieldError, type FieldState, type FieldStateInput, type FormConfig, type FormFieldsConfig, type FormState, type FormalityProviderConfig, type FormatterFunction, type FormatterSpec, type FormattersConfig, type GroupConfig, type InputConfig, type InputTemplateProps, KEYWORDS, type ParserFunction, type ParserSpec, type ParsersConfig, QUALIFIED_PREFIXES, type SelectFunction, type SelectValue, type ValidationResult, type ValidatorEntry, type ValidatorFactory, type ValidatorFunction, type ValidatorSpec, type ValidatorsConfig, buildEvaluationContext, buildFieldContext, buildFormContext, clearExpressionCache, composeValidators, conditionMatches, createConfigContext, createDefaultFormatters, createDefaultParsers, createErrorMessages, createFieldStateProxy, createFloatFormatter, createFloatParser, createIntParser, createLabelWithUnit, createTrimParser, createValidationError, deepMerge, evaluate, evaluateConditions, evaluateDescriptor, extractValueField, format, formatTypeAsMessage, getErrorType, getInputDefaultValue, getOrderedUnusedFields, getUnusedFields, humanizeLabel, inferFieldsFromConditions, inferFieldsFromDescriptor, inferFieldsFromExpression, isAutoGeneratedLabel, isEmptyValue, isFieldProxy, isValid, maxLength, mergeConditionResults, mergeFieldProps, mergeInputConfigs, mergeRecordWithDefaults, mergeStaticProps, minLength, parse, parseLabelWithUnit, pattern, required, resolveAllInitialValues, resolveErrorMessage, resolveFieldType, resolveFormTitle, resolveInitialValue, resolveInputConfig, resolveLabel, runValidator, runValidatorSync, sortFieldsByOrder, transformFieldName, unwrapFieldProxy };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,28 @@
1
+ /**
2
+ * FieldMatcher - Matchers for a single field in multi-field conditions
3
+ *
4
+ * Used when `when` is an object mapping field names to their matchers.
5
+ * All specified matchers must pass (AND logic).
6
+ */
7
+ interface FieldMatcher {
8
+ /** Exact value match */
9
+ is?: unknown;
10
+ /** Truthy/falsy check (alias: isTruthy) */
11
+ truthy?: boolean;
12
+ /** Truthy/falsy check (alias for truthy) */
13
+ isTruthy?: boolean;
14
+ /** Field validity check - true if field has no errors */
15
+ isValid?: boolean;
16
+ /** Field disabled state check */
17
+ isDisabled?: boolean;
18
+ }
19
+ /**
20
+ * Multi-field when trigger - maps field names to their matchers
21
+ *
22
+ * All field conditions must match (AND logic).
23
+ * Example: { email: { isValid: true }, name: { isTruthy: true } }
24
+ */
25
+ type WhenMultiField = Record<string, FieldMatcher>;
1
26
  /**
2
27
  * ConditionDescriptor - Defines a single condition rule
3
28
  *
@@ -7,10 +32,11 @@
7
32
  */
8
33
  interface ConditionDescriptor {
9
34
  /**
10
- * Simple field reference trigger
11
- * Example: "client" - triggers when client field has a value
35
+ * Field reference trigger - can be:
36
+ * - string: Single field reference (e.g., "client")
37
+ * - object: Multi-field with per-field matchers (e.g., { email: { isValid: true }, name: { isTruthy: true } })
12
38
  */
13
- when?: string;
39
+ when?: string | WhenMultiField;
14
40
  /**
15
41
  * Expression or function trigger
16
42
  * Example: "client.id > 5" or ({ fields }) => fields.client?.value?.id > 5
@@ -27,6 +53,18 @@ interface ConditionDescriptor {
27
53
  * false: matches if trigger is falsy
28
54
  */
29
55
  truthy?: boolean;
56
+ /**
57
+ * Field validity check (requires 'when' trigger)
58
+ * true: matches if the 'when' field is valid (no errors)
59
+ * false: matches if the 'when' field is invalid (has errors)
60
+ */
61
+ isValid?: boolean;
62
+ /**
63
+ * Field disabled state check (requires 'when' trigger)
64
+ * true: matches if the 'when' field is disabled
65
+ * false: matches if the 'when' field is enabled
66
+ */
67
+ isDisabled?: boolean;
30
68
  /**
31
69
  * Set disabled state
32
70
  * Multiple conditions use OR logic (any true = disabled)
@@ -117,16 +155,33 @@ type ValidatorSpec = string | ValidatorFunction | Array<string | ValidatorFuncti
117
155
  /**
118
156
  * ValidatorFactory - Factory function for parameterized validators
119
157
  *
120
- * Example: minLength(5) returns a ValidatorFunction
158
+ * Accepts any number of factory arguments and returns a {@link ValidatorFunction}.
159
+ * The `...args: any[]` rest signature is intentional: it lets concrete factories
160
+ * with specific parameter types (e.g. `(min: number) => ValidatorFunction`,
161
+ * `(regex: RegExp, message: string) => ValidatorFunction`) be assignable to this
162
+ * type without parameter-invariance errors, while still producing a fully typed
163
+ * `ValidatorFunction` return.
164
+ *
165
+ * @example
166
+ * const min: ValidatorFactory = (minVal: number) => (value) =>
167
+ * Number(value) < minVal ? { type: 'min' } : true;
168
+ */
169
+ type ValidatorFactory = (...args: any[]) => ValidatorFunction;
170
+ /**
171
+ * ValidatorEntry - A named validator or a validator factory.
172
+ *
173
+ * Either a direct {@link ValidatorFunction} (called with `value` + `formValues`)
174
+ * or a {@link ValidatorFactory} (called with factory args to produce a
175
+ * `ValidatorFunction`).
121
176
  */
122
- type ValidatorFactory<TArgs = unknown> = (args: TArgs) => ValidatorFunction;
177
+ type ValidatorEntry = ValidatorFunction | ValidatorFactory;
123
178
  /**
124
179
  * ValidatorsConfig - Named validators configuration
125
180
  *
126
- * Values can be direct validators or validator factories
181
+ * Values can be direct {@link ValidatorFunction}s or {@link ValidatorFactory}s.
127
182
  */
128
183
  interface ValidatorsConfig {
129
- [name: string]: ValidatorFunction | ValidatorFactory;
184
+ [name: string]: ValidatorEntry;
130
185
  }
131
186
  /**
132
187
  * ErrorMessagesConfig - Error message templates by type key
@@ -164,6 +219,8 @@ interface FieldState {
164
219
  error?: FieldError;
165
220
  /** Inverse of valid state for convenience */
166
221
  invalid: boolean;
222
+ /** Whether the field is disabled */
223
+ disabled?: boolean;
167
224
  /** Map of subscriber field names watching this field */
168
225
  watchers?: Record<string, boolean>;
169
226
  }
@@ -230,9 +287,24 @@ type SelectFunction<TReturn = unknown> = (formState: FormState, methods: unknown
230
287
  *
231
288
  * This defines how a particular input type (e.g., textField, switch, autocomplete)
232
289
  * behaves across all forms.
290
+ *
291
+ * Framework agnosticism: `component` and `template` are intentionally `unknown`
292
+ * here (core cannot import a UI framework). React consumers should use the
293
+ * `ReactInputConfig<TValue>` overlay exported from `@formality-ui/react`, which
294
+ * narrows both to `ComponentType<...>`.
295
+ *
296
+ * `TValue` (default `unknown`) links `defaultValue`, `parser`, and `formatter`
297
+ * to a single value type. It defaults to `unknown` because the framework-agnostic
298
+ * core cannot know the value type of every UI component; React consumers can
299
+ * parameterize it via `ReactInputConfig<TValue>` (e.g.
300
+ * `ReactInputConfig<string>` for a text field). Today only the overlay exposes
301
+ * this parameterization; end-to-end per-input value inference is a future
302
+ * enhancement.
303
+ *
304
+ * @template TValue - The form value type this input produces/consumes.
233
305
  */
234
306
  interface InputConfig<TValue = unknown> {
235
- /** The React component to render */
307
+ /** The component to render (typed `unknown` for framework agnosticism; React consumers see `ComponentType<any>` via `ReactInputConfig`) */
236
308
  component: unknown;
237
309
  /** Default value for this input type (e.g., '' for text, false for switch) */
238
310
  defaultValue: TValue;
@@ -250,7 +322,7 @@ interface InputConfig<TValue = unknown> {
250
322
  formatter?: string | ((value: TValue) => unknown);
251
323
  /** Type-level validation (runs after field-level validator) */
252
324
  validator?: ValidatorSpec;
253
- /** Template component wrapper for consistent styling */
325
+ /** Template component wrapper for consistent styling (typed `unknown` for framework agnosticism; React consumers see `ComponentType<InputTemplateProps>` via `ReactInputConfig`) */
254
326
  template?: unknown;
255
327
  /** Default props for this input type */
256
328
  props?: Record<string, unknown>;
@@ -260,6 +332,12 @@ interface InputConfig<TValue = unknown> {
260
332
  *
261
333
  * This defines field-level behavior including conditions, validation,
262
334
  * and dynamic props.
335
+ *
336
+ * Framework agnosticism: `rules` is intentionally `Record<string, unknown>` here
337
+ * (core cannot import react-hook-form). React consumers should use the
338
+ * `ReactFieldConfig` overlay exported from `@formality-ui/react`, which narrows
339
+ * `rules` to react-hook-form's `RegisterOptions` for full autocomplete and
340
+ * checking (required, min, max, pattern, validate, valueAsNumber, …).
263
341
  */
264
342
  interface FieldConfig {
265
343
  /** Input type key (resolves to InputConfig) */
@@ -276,7 +354,7 @@ interface FieldConfig {
276
354
  order?: number;
277
355
  /** Key to use when reading initial value from record (defaults to field name) */
278
356
  recordKey?: string;
279
- /** React Hook Form RegisterOptions (required, min, max, pattern, etc.) */
357
+ /** Register options forwarded to the framework's field register call (typed loose for framework agnosticism; React consumers see react-hook-form's `RegisterOptions` via `ReactFieldConfig`) */
280
358
  rules?: Record<string, unknown>;
281
359
  /** Field-level validation (runs before type-level validator) */
282
360
  validator?: ValidatorSpec;
@@ -297,8 +375,12 @@ interface FieldConfig {
297
375
  }
298
376
  /**
299
377
  * FormFieldsConfig - Map of field names to their configurations
378
+ *
379
+ * Generic over the field-name union so a typed `<Form<TFieldValues>>` can reject
380
+ * unknown config keys. Defaults to `string`, which is identical to the previous
381
+ * non-generic `Record<string, FieldConfig>` (backwards compatible).
300
382
  */
301
- type FormFieldsConfig = Record<string, FieldConfig>;
383
+ type FormFieldsConfig<TName extends string = string> = Record<TName, FieldConfig>;
302
384
  /**
303
385
  * GroupConfig - Configuration for a FieldGroup
304
386
  */
@@ -334,6 +416,11 @@ interface FormConfig {
334
416
  * FormalityProviderConfig - Global provider configuration
335
417
  *
336
418
  * Sets up input types, transformers, validators, and global defaults.
419
+ *
420
+ * Framework agnosticism: `defaultInputTemplate` and `inputTemplates` are
421
+ * intentionally `unknown` here (core cannot import a UI framework). React's
422
+ * `FormalityProviderProps` and `ConfigContextValue` overlay
423
+ * `ComponentType<InputTemplateProps>` on top of these loose fields.
337
424
  */
338
425
  interface FormalityProviderConfig {
339
426
  /** Input type definitions */
@@ -359,6 +446,10 @@ interface FormalityProviderConfig {
359
446
  }
360
447
  /**
361
448
  * InputTemplateProps - Props passed to input template components
449
+ *
450
+ * Framework agnosticism: `Field` is intentionally `unknown` here. React overlays
451
+ * this same-named type in `@formality-ui/react` with `Field: ComponentType<any>`
452
+ * and RHF-typed `fieldState`/`formState`; React consumers import that overlay.
362
453
  */
363
454
  interface InputTemplateProps {
364
455
  /** The input component to render */
@@ -481,6 +572,7 @@ interface FieldStateForContext {
481
572
  isValidating?: boolean;
482
573
  error?: unknown;
483
574
  invalid?: boolean;
575
+ disabled?: boolean;
484
576
  }
485
577
  /**
486
578
  * Build a minimal evaluation context from field values
@@ -500,8 +592,8 @@ declare function buildEvaluationContext(fieldValues: Record<string, unknown>, re
500
592
  * Extract field names from an expression string
501
593
  *
502
594
  * Scans the expression for unqualified identifiers that represent field names.
503
- * Skips JavaScript keywords. Qualified prefixes (fields, record, props, etc.)
504
- * are only skipped when followed by a dot.
595
+ * Skips JavaScript keywords and identifiers inside string literals.
596
+ * Qualified prefixes (fields, record, props, etc.) are only skipped when followed by a dot.
505
597
  *
506
598
  * @param expr - Expression string to analyze
507
599
  * @returns Array of unique field names referenced
@@ -512,6 +604,7 @@ declare function buildEvaluationContext(fieldValues: Record<string, unknown>, re
512
604
  * inferFieldsFromExpression("record.name") // → [] (qualified path)
513
605
  * inferFieldsFromExpression("true && false") // → [] (keywords)
514
606
  * inferFieldsFromExpression("fields === null") // → ["fields"] (not followed by dot)
607
+ * inferFieldsFromExpression('signed ? "target-on" : "target-off"') // → ["signed"] (string contents skipped)
515
608
  */
516
609
  declare function inferFieldsFromExpression(expr: string): string[];
517
610
  /**
@@ -541,6 +634,7 @@ interface FieldStateInput {
541
634
  isValidating?: boolean;
542
635
  error?: unknown;
543
636
  invalid?: boolean;
637
+ disabled?: boolean;
544
638
  }
545
639
  /**
546
640
  * Input for condition evaluation
@@ -589,9 +683,10 @@ declare function evaluateConditions(input: EvaluateConditionsInput): ConditionRe
589
683
  * @param fieldValues - Map of field names to their current values
590
684
  * @param record - Optional record for expression context
591
685
  * @param props - Optional props for expression context
686
+ * @param fieldStates - Optional field states for state-based matchers
592
687
  * @returns true if the condition matches
593
688
  */
594
- declare function conditionMatches(condition: ConditionDescriptor, fieldValues: Record<string, unknown>, record?: Record<string, unknown>, props?: Record<string, unknown>): boolean;
689
+ declare function conditionMatches(condition: ConditionDescriptor, fieldValues: Record<string, unknown>, record?: Record<string, unknown>, props?: Record<string, unknown>, fieldStates?: Record<string, FieldStateInput>): boolean;
595
690
  /**
596
691
  * Merge condition results from multiple sources (e.g., group + field)
597
692
  *
@@ -917,7 +1012,7 @@ declare function deepMerge<T extends object>(base: T, override: Partial<T> | und
917
1012
  * @param formInputs - Form input configs (or function to transform)
918
1013
  * @returns Merged input configs
919
1014
  */
920
- declare function mergeInputConfigs(providerInputs: Record<string, InputConfig>, formInputs?: FormConfig['inputs']): Record<string, InputConfig>;
1015
+ declare function mergeInputConfigs(providerInputs: Record<string, InputConfig>, formInputs?: FormConfig["inputs"]): Record<string, InputConfig>;
921
1016
  /**
922
1017
  * Resolve input config for a specific type
923
1018
  *
@@ -1179,4 +1274,4 @@ declare function getUnusedFields(allFields: string[], declaredFields: Set<string
1179
1274
  */
1180
1275
  declare function getOrderedUnusedFields(allFields: string[], declaredFields: Set<string>, fieldConfigs: Record<string, FieldConfig>): string[];
1181
1276
 
1182
- export { type ConditionDescriptor, type ConditionResult, type ErrorMessagesConfig, type EvaluateConditionsInput, type EvaluationContext, FIELD_PROXY_MARKER, FIELD_PROXY_VALUE, type FieldConfig, type FieldError, type FieldState, type FieldStateInput, type FormConfig, type FormFieldsConfig, type FormState, type FormalityProviderConfig, type FormatterFunction, type FormatterSpec, type FormattersConfig, type GroupConfig, type InputConfig, type InputTemplateProps, KEYWORDS, type ParserFunction, type ParserSpec, type ParsersConfig, QUALIFIED_PREFIXES, type SelectFunction, type SelectValue, type ValidationResult, type ValidatorFactory, type ValidatorFunction, type ValidatorSpec, type ValidatorsConfig, buildEvaluationContext, buildFieldContext, buildFormContext, clearExpressionCache, composeValidators, conditionMatches, createConfigContext, createDefaultFormatters, createDefaultParsers, createErrorMessages, createFieldStateProxy, createFloatFormatter, createFloatParser, createIntParser, createLabelWithUnit, createTrimParser, createValidationError, deepMerge, evaluate, evaluateConditions, evaluateDescriptor, extractValueField, format, formatTypeAsMessage, getErrorType, getInputDefaultValue, getOrderedUnusedFields, getUnusedFields, humanizeLabel, inferFieldsFromConditions, inferFieldsFromDescriptor, inferFieldsFromExpression, isAutoGeneratedLabel, isEmptyValue, isFieldProxy, isValid, maxLength, mergeConditionResults, mergeFieldProps, mergeInputConfigs, mergeRecordWithDefaults, mergeStaticProps, minLength, parse, parseLabelWithUnit, pattern, required, resolveAllInitialValues, resolveErrorMessage, resolveFieldType, resolveFormTitle, resolveInitialValue, resolveInputConfig, resolveLabel, runValidator, runValidatorSync, sortFieldsByOrder, transformFieldName, unwrapFieldProxy };
1277
+ export { type ConditionDescriptor, type ConditionResult, type ErrorMessagesConfig, type EvaluateConditionsInput, type EvaluationContext, FIELD_PROXY_MARKER, FIELD_PROXY_VALUE, type FieldConfig, type FieldError, type FieldState, type FieldStateInput, type FormConfig, type FormFieldsConfig, type FormState, type FormalityProviderConfig, type FormatterFunction, type FormatterSpec, type FormattersConfig, type GroupConfig, type InputConfig, type InputTemplateProps, KEYWORDS, type ParserFunction, type ParserSpec, type ParsersConfig, QUALIFIED_PREFIXES, type SelectFunction, type SelectValue, type ValidationResult, type ValidatorEntry, type ValidatorFactory, type ValidatorFunction, type ValidatorSpec, type ValidatorsConfig, buildEvaluationContext, buildFieldContext, buildFormContext, clearExpressionCache, composeValidators, conditionMatches, createConfigContext, createDefaultFormatters, createDefaultParsers, createErrorMessages, createFieldStateProxy, createFloatFormatter, createFloatParser, createIntParser, createLabelWithUnit, createTrimParser, createValidationError, deepMerge, evaluate, evaluateConditions, evaluateDescriptor, extractValueField, format, formatTypeAsMessage, getErrorType, getInputDefaultValue, getOrderedUnusedFields, getUnusedFields, humanizeLabel, inferFieldsFromConditions, inferFieldsFromDescriptor, inferFieldsFromExpression, isAutoGeneratedLabel, isEmptyValue, isFieldProxy, isValid, maxLength, mergeConditionResults, mergeFieldProps, mergeInputConfigs, mergeRecordWithDefaults, mergeStaticProps, minLength, parse, parseLabelWithUnit, pattern, required, resolveAllInitialValues, resolveErrorMessage, resolveFieldType, resolveFormTitle, resolveInitialValue, resolveInputConfig, resolveLabel, runValidator, runValidatorSync, sortFieldsByOrder, transformFieldName, unwrapFieldProxy };