@formality-ui/core 0.3.1 → 0.4.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
@@ -306,7 +306,16 @@ type SelectFunction<TReturn = unknown> = (formState: FormState, methods: unknown
306
306
  interface InputConfig<TValue = unknown> {
307
307
  /** The component to render (typed `unknown` for framework agnosticism; React consumers see `ComponentType<any>` via `ReactInputConfig`) */
308
308
  component: unknown;
309
- /** Default value for this input type (e.g., '' for text, false for switch) */
309
+ /**
310
+ * Default value for this input type (e.g., `''` for text, `false` for switch).
311
+ *
312
+ * Per-field override via `FieldConfig.defaultValue` (§6.4.1); the
313
+ * field-level value wins when `!== undefined` (resolved via
314
+ * `resolveFieldOverType`, §6.4.0 — so null/false/0/"" are meaningful
315
+ * defaults). A field-level default is a new priority tier *below*
316
+ * `record`/`defaultValues` and *above* this type default (§6.4.1,
317
+ * §13.1) — not a bare `??` of this value.
318
+ */
310
319
  defaultValue: TValue;
311
320
  /**
312
321
  * Auto-save debounce for fields of this input type.
@@ -319,19 +328,55 @@ interface InputConfig<TValue = unknown> {
319
328
  * debounces fire on their own cadence. When unset, the field falls back
320
329
  * to the Form-level `debounce` prop (default 1000ms).
321
330
  *
331
+ * Three-tier precedence (§6.4.2): a field-level `FieldConfig.debounce`
332
+ * wins when set; otherwise this type-level value; otherwise the
333
+ * Form-level `debounce` prop (default 1000). All three share the single
334
+ * field-over-type rule from §6.4.0 (resolved via `resolveFieldOverType`,
335
+ * so a field-level `false`/`number` is honored when `!== undefined`).
336
+ *
322
337
  * This governs *auto-save timing only*. The field value is still committed
323
338
  * to the form state on every change (it does not throttle re-renders).
324
339
  */
325
340
  debounce?: number | false;
326
341
  /** Prop name for passing value to component (default: 'value') */
327
342
  inputFieldProp?: string;
328
- /** For complex values (objects), which property contains the actual value */
343
+ /**
344
+ * For complex values (objects), which property contains the actual value to submit.
345
+ *
346
+ * Per-field override via `FieldConfig.valueField` (§6.4.4); the field-level
347
+ * value wins when `!== undefined`, otherwise this type-level value applies.
348
+ * Resolved via `resolveFieldOverType` (§6.4.0), restoring read/write symmetry
349
+ * with `recordKey`.
350
+ */
329
351
  valueField?: string;
330
- /** Transform field name for submission (e.g., 'client' → 'clientId') */
352
+ /**
353
+ * Transform the field name for submission (e.g. `'client' → 'clientId'`).
354
+ *
355
+ * Per-field override via `FieldConfig.getSubmitField` (§6.4.4); the
356
+ * field-level value wins when `!== undefined`, otherwise this type-level
357
+ * value applies. Resolved via `resolveFieldOverType` (§6.4.0), restoring
358
+ * read/write symmetry with `recordKey`.
359
+ */
331
360
  getSubmitField?: (fieldName: string) => string;
332
- /** Transform user input to form value. String = named parser, function = inline */
361
+ /**
362
+ * Transform user input to form value. String = named parser, function = inline.
363
+ *
364
+ * Three-tier precedence (§6.4.3): field → type → none. Per-field override
365
+ * via `FieldConfig.parser`; the field-level value wins when `!== undefined`
366
+ * (resolved via `resolveFieldOverType`, §6.4.0 — so null/false/0/"" are
367
+ * meaningful overrides). Named (string) specs resolve against the
368
+ * provider's global `parsers` registry.
369
+ */
333
370
  parser?: string | ((value: unknown) => TValue);
334
- /** Transform form value to display value. String = named formatter, function = inline */
371
+ /**
372
+ * Transform form value to display value. String = named formatter, function = inline.
373
+ *
374
+ * Three-tier precedence (§6.4.3): field → type → none. Per-field override
375
+ * via `FieldConfig.formatter`; the field-level value wins when `!== undefined`
376
+ * (resolved via `resolveFieldOverType`, §6.4.0 — so null/false/0/"" are
377
+ * meaningful overrides). Named (string) specs resolve against the
378
+ * provider's global `formatters` registry.
379
+ */
335
380
  formatter?: string | ((value: TValue) => unknown);
336
381
  /** Type-level validation (runs after field-level validator) */
337
382
  validator?: ValidatorSpec;
@@ -367,6 +412,38 @@ interface FieldConfig {
367
412
  order?: number;
368
413
  /** Key to use when reading initial value from record (defaults to field name) */
369
414
  recordKey?: string;
415
+ /**
416
+ * Per-instance default value. Overrides the input type's defaultValue;
417
+ * superseded by record/defaultValues prop. Honored when !== undefined, so
418
+ * null/false/0/"" are meaningful. See §6.4.1, §13.1.
419
+ */
420
+ defaultValue?: unknown;
421
+ /**
422
+ * Per-instance auto-save debounce. Overrides the input type's debounce;
423
+ * falls back to Form-level debounce prop (default 1000). false = submit
424
+ * immediately. See §6.4.2.
425
+ */
426
+ debounce?: number | false;
427
+ /**
428
+ * Per-instance value transform (input→form). Overrides the input type's
429
+ * parser. String = named parser; function = inline. See §6.4.3.
430
+ */
431
+ parser?: string | ((value: unknown) => unknown);
432
+ /**
433
+ * Per-instance value transform (form→display). Overrides the input type's
434
+ * formatter. String = named formatter; function = inline. See §6.4.3.
435
+ */
436
+ formatter?: string | ((value: unknown) => unknown);
437
+ /**
438
+ * Submit-side value extraction from complex objects. Overrides the input
439
+ * type's valueField. See §6.4.4.
440
+ */
441
+ valueField?: string;
442
+ /**
443
+ * Submit-side field name transformation. Overrides the input type's
444
+ * getSubmitField. See §6.4.4.
445
+ */
446
+ getSubmitField?: (fieldName: string) => string;
370
447
  /** 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`) */
371
448
  rules?: Record<string, unknown>;
372
449
  /** Field-level validation (runs before type-level validator) */
@@ -1191,26 +1268,53 @@ declare function mergeConfigs(provider: FormalityProviderConfig, form?: FormConf
1191
1268
  fieldConfig: FieldConfig;
1192
1269
  };
1193
1270
 
1271
+ /**
1272
+ * Resolve a field-level override against its type-level default. Returns the
1273
+ * field value when it is not undefined (so null/false/0/"" are meaningful
1274
+ * overrides); otherwise the type value. This is the single precedence rule
1275
+ * shared by defaultValue, debounce, parser, formatter, getSubmitField, and
1276
+ * valueField (§6.4.0). Every adapter MUST call this helper at each
1277
+ * field-vs-type resolution site.
1278
+ *
1279
+ * @param fieldVal - The field-level (instance) value; `undefined` means "not specified".
1280
+ * @param typeVal - The type-level (InputConfig) default; `undefined` means "not specified".
1281
+ * @returns `fieldVal` when it is not `undefined`, else `typeVal`.
1282
+ *
1283
+ * @example
1284
+ * // Field override wins (even when falsy):
1285
+ * resolveFieldOverType(false, true); // → false
1286
+ * resolveFieldOverType(null, "x"); // → null
1287
+ * resolveFieldOverType(0, 100); // → 0
1288
+ * resolveFieldOverType("", "fallback");// → ""
1289
+ *
1290
+ * // Field unset → type default:
1291
+ * resolveFieldOverType(undefined, "type"); // → "type"
1292
+ * resolveFieldOverType(undefined, undefined); // → undefined
1293
+ */
1294
+ declare function resolveFieldOverType<T>(fieldVal: T | undefined, typeVal: T | undefined): T | undefined;
1194
1295
  /**
1195
1296
  * Resolve the initial value for a field
1196
1297
  *
1197
1298
  * Priority order (highest to lowest):
1198
1299
  * 1. defaultValues[fieldName] (from Form props)
1199
1300
  * 2. record[recordKey] (using recordKey if specified, else fieldName)
1200
- * 3. inputConfig.defaultValue (from input type definition)
1301
+ * 3. fieldConfig.defaultValue (per-instance default; §6.4.1, §13.1)
1302
+ * 4. inputConfig.defaultValue (from input type definition)
1201
1303
  *
1202
1304
  * **PRD deviation note (accepted, gap_analysis G5).** PRD §1.3.2's table
1203
1305
  * summarizes this export as `resolveInitialValue(record, config, inputConfig)`.
1204
1306
  * The implemented signature is a richer superset —
1205
1307
  * `(fieldName, fieldConfig?, inputConfig?, record?, defaultValues?)` — because
1206
1308
  * it drives the full priority chain above (defaultValues → record[recordKey] →
1207
- * inputConfig.defaultValue) from a single call. This is an internal API
1208
- * consumed by the framework adapters and by {@link resolveAllInitialValues},
1209
- * not a simplified end-user entry point; the PRD literal form is a condensed
1210
- * representation. No code change is planned.
1309
+ * fieldConfig.defaultValue → inputConfig.defaultValue) from a single call. This
1310
+ * is an internal API consumed by the framework adapters and by
1311
+ * {@link resolveAllInitialValues}, not a simplified end-user entry point; the
1312
+ * PRD literal form is a condensed representation. No code change is planned.
1211
1313
  *
1212
1314
  * @param fieldName - Field name
1213
- * @param fieldConfig - Field configuration
1315
+ * @param fieldConfig - Field configuration; `defaultValue` (when set) is the
1316
+ * Priority-3 per-instance default (§6.4.1), honored for any value
1317
+ * `!== undefined` (so null/false/0/"" are meaningful).
1214
1318
  * @param inputConfig - Input type configuration
1215
1319
  * @param record - Record data passed to form
1216
1320
  * @param defaultValues - Default values passed to form
@@ -1236,6 +1340,16 @@ declare function mergeConfigs(provider: FormalityProviderConfig, form?: FormConf
1236
1340
  * { status: 'active' }
1237
1341
  * )
1238
1342
  * // → 'active' (defaultValues takes precedence)
1343
+ *
1344
+ * // Field-level default wins over the type default (§6.4.1)
1345
+ * resolveInitialValue(
1346
+ * 'active',
1347
+ * { type: 'switch', defaultValue: true }, // field default: true
1348
+ * { defaultValue: false }, // type default: false
1349
+ * undefined,
1350
+ * undefined,
1351
+ * )
1352
+ * // → true (field-level default honored; null/false/0/"" would also win)
1239
1353
  */
1240
1354
  declare function resolveInitialValue(fieldName: string, fieldConfig?: FieldConfig, inputConfig?: InputConfig, record?: Record<string, unknown>, defaultValues?: Record<string, unknown>): unknown;
1241
1355
  /**
@@ -1329,30 +1443,46 @@ declare function humanizeLabel(fieldName: string): string;
1329
1443
  * Priority order (highest to lowest):
1330
1444
  * 1. Component prop (label from JSX props)
1331
1445
  * 2. Field config props.label
1332
- * 3. Evaluated selectProps.label
1446
+ * 3. Evaluated selectProps.label (field-level selectProps)
1333
1447
  * 4. Field config label
1334
1448
  * 5. Field config title (legacy alias)
1335
- * 6. Auto-generated from field name
1449
+ * 6. Evaluated form-level selectDefaultFieldProps.label
1450
+ * 7. Evaluated provider-level selectDefaultFieldProps.label
1451
+ * 8. Auto-generated from field name
1452
+ *
1453
+ * The provider/form `selectDefaultFieldProps` layers are documented as label
1454
+ * sources in PRD §16.1 (`providerConfig.selectDefaultFieldProps: { label:
1455
+ * "props.name" }` works; same for `formConfig`). They sit below the field-level
1456
+ * sources (matching their lower precedence in the 8-layer `mergeFieldProps`
1457
+ * pipeline — layers 5 and 7 vs. the field-level layers 2/3) but above the
1458
+ * humanized fallback, so a global label convention applies unless the field
1459
+ * itself overrides it.
1336
1460
  *
1337
1461
  * **PRD deviation note (accepted, gap_analysis G5).** PRD §1.3.2's table
1338
1462
  * summarizes this export as `resolveLabel(config, fieldName)`. The implemented
1339
1463
  * signature is a richer superset —
1340
- * `(fieldName, fieldConfig?, evaluatedSelectProps?, componentProps?)` — because
1341
- * it resolves the full 6-source priority chain above in one call, which
1342
- * requires the pre-evaluated `selectProps` and the JSX `componentProps`. This
1464
+ * `(fieldName, fieldConfig?, evaluatedSelectProps?, componentProps?,
1465
+ * providerSelectProps?, formSelectProps?)` — because it resolves the full
1466
+ * priority chain above in one call, which requires the pre-evaluated
1467
+ * `selectProps`/`selectDefaultFieldProps` and the JSX `componentProps`. This
1343
1468
  * is an internal API consumed by the framework adapters (e.g.
1344
1469
  * `@formality-ui/react`'s `Field` calls
1345
- * `resolveLabel(name, fieldConfig, fieldSelectProps, restProps)`), not a
1346
- * simplified end-user entry point; the PRD literal form is a condensed
1347
- * representation. No code change is planned.
1470
+ * `resolveLabel(name, fieldConfig, fieldSelectProps, restProps,
1471
+ * providerSelectProps, formSelectProps)`), not a simplified end-user entry
1472
+ * point; the PRD literal form is a condensed representation. No code change is
1473
+ * planned.
1348
1474
  *
1349
1475
  * @param fieldName - Field name
1350
1476
  * @param fieldConfig - Field configuration
1351
- * @param evaluatedSelectProps - Pre-evaluated selectProps
1477
+ * @param evaluatedSelectProps - Pre-evaluated field-level selectProps
1352
1478
  * @param componentProps - Props from JSX
1479
+ * @param providerSelectProps - Pre-evaluated provider-level
1480
+ * selectDefaultFieldProps (PRD §16.1)
1481
+ * @param formSelectProps - Pre-evaluated form-level selectDefaultFieldProps
1482
+ * (PRD §16.1)
1353
1483
  * @returns Resolved label string
1354
1484
  */
1355
- declare function resolveLabel(fieldName: string, fieldConfig?: FieldConfig, evaluatedSelectProps?: Record<string, unknown>, componentProps?: Record<string, unknown>): string;
1485
+ declare function resolveLabel(fieldName: string, fieldConfig?: FieldConfig, evaluatedSelectProps?: Record<string, unknown>, componentProps?: Record<string, unknown>, providerSelectProps?: Record<string, unknown>, formSelectProps?: Record<string, unknown>): string;
1356
1486
  /**
1357
1487
  * Resolve the title for a form
1358
1488
  *
@@ -1396,4 +1526,4 @@ declare function parseLabelWithUnit(label: string): {
1396
1526
  unit?: string;
1397
1527
  };
1398
1528
 
1399
- 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, mergeConfigs, mergeFieldProps, mergeInputConfigs, mergeRecordWithDefaults, mergeStaticProps, minLength, parse, parseLabelWithUnit, pattern, required, resolveAllInitialValues, resolveErrorMessage, resolveFieldType, resolveFormTitle, resolveInitialValue, resolveInputConfig, resolveLabel, runValidator, runValidatorSync, sortFieldsByOrder, transformFieldName, unwrapFieldProxy, validate };
1529
+ 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, mergeConfigs, mergeFieldProps, mergeInputConfigs, mergeRecordWithDefaults, mergeStaticProps, minLength, parse, parseLabelWithUnit, pattern, required, resolveAllInitialValues, resolveErrorMessage, resolveFieldOverType, resolveFieldType, resolveFormTitle, resolveInitialValue, resolveInputConfig, resolveLabel, runValidator, runValidatorSync, sortFieldsByOrder, transformFieldName, unwrapFieldProxy, validate };
package/dist/index.d.ts CHANGED
@@ -306,7 +306,16 @@ type SelectFunction<TReturn = unknown> = (formState: FormState, methods: unknown
306
306
  interface InputConfig<TValue = unknown> {
307
307
  /** The component to render (typed `unknown` for framework agnosticism; React consumers see `ComponentType<any>` via `ReactInputConfig`) */
308
308
  component: unknown;
309
- /** Default value for this input type (e.g., '' for text, false for switch) */
309
+ /**
310
+ * Default value for this input type (e.g., `''` for text, `false` for switch).
311
+ *
312
+ * Per-field override via `FieldConfig.defaultValue` (§6.4.1); the
313
+ * field-level value wins when `!== undefined` (resolved via
314
+ * `resolveFieldOverType`, §6.4.0 — so null/false/0/"" are meaningful
315
+ * defaults). A field-level default is a new priority tier *below*
316
+ * `record`/`defaultValues` and *above* this type default (§6.4.1,
317
+ * §13.1) — not a bare `??` of this value.
318
+ */
310
319
  defaultValue: TValue;
311
320
  /**
312
321
  * Auto-save debounce for fields of this input type.
@@ -319,19 +328,55 @@ interface InputConfig<TValue = unknown> {
319
328
  * debounces fire on their own cadence. When unset, the field falls back
320
329
  * to the Form-level `debounce` prop (default 1000ms).
321
330
  *
331
+ * Three-tier precedence (§6.4.2): a field-level `FieldConfig.debounce`
332
+ * wins when set; otherwise this type-level value; otherwise the
333
+ * Form-level `debounce` prop (default 1000). All three share the single
334
+ * field-over-type rule from §6.4.0 (resolved via `resolveFieldOverType`,
335
+ * so a field-level `false`/`number` is honored when `!== undefined`).
336
+ *
322
337
  * This governs *auto-save timing only*. The field value is still committed
323
338
  * to the form state on every change (it does not throttle re-renders).
324
339
  */
325
340
  debounce?: number | false;
326
341
  /** Prop name for passing value to component (default: 'value') */
327
342
  inputFieldProp?: string;
328
- /** For complex values (objects), which property contains the actual value */
343
+ /**
344
+ * For complex values (objects), which property contains the actual value to submit.
345
+ *
346
+ * Per-field override via `FieldConfig.valueField` (§6.4.4); the field-level
347
+ * value wins when `!== undefined`, otherwise this type-level value applies.
348
+ * Resolved via `resolveFieldOverType` (§6.4.0), restoring read/write symmetry
349
+ * with `recordKey`.
350
+ */
329
351
  valueField?: string;
330
- /** Transform field name for submission (e.g., 'client' → 'clientId') */
352
+ /**
353
+ * Transform the field name for submission (e.g. `'client' → 'clientId'`).
354
+ *
355
+ * Per-field override via `FieldConfig.getSubmitField` (§6.4.4); the
356
+ * field-level value wins when `!== undefined`, otherwise this type-level
357
+ * value applies. Resolved via `resolveFieldOverType` (§6.4.0), restoring
358
+ * read/write symmetry with `recordKey`.
359
+ */
331
360
  getSubmitField?: (fieldName: string) => string;
332
- /** Transform user input to form value. String = named parser, function = inline */
361
+ /**
362
+ * Transform user input to form value. String = named parser, function = inline.
363
+ *
364
+ * Three-tier precedence (§6.4.3): field → type → none. Per-field override
365
+ * via `FieldConfig.parser`; the field-level value wins when `!== undefined`
366
+ * (resolved via `resolveFieldOverType`, §6.4.0 — so null/false/0/"" are
367
+ * meaningful overrides). Named (string) specs resolve against the
368
+ * provider's global `parsers` registry.
369
+ */
333
370
  parser?: string | ((value: unknown) => TValue);
334
- /** Transform form value to display value. String = named formatter, function = inline */
371
+ /**
372
+ * Transform form value to display value. String = named formatter, function = inline.
373
+ *
374
+ * Three-tier precedence (§6.4.3): field → type → none. Per-field override
375
+ * via `FieldConfig.formatter`; the field-level value wins when `!== undefined`
376
+ * (resolved via `resolveFieldOverType`, §6.4.0 — so null/false/0/"" are
377
+ * meaningful overrides). Named (string) specs resolve against the
378
+ * provider's global `formatters` registry.
379
+ */
335
380
  formatter?: string | ((value: TValue) => unknown);
336
381
  /** Type-level validation (runs after field-level validator) */
337
382
  validator?: ValidatorSpec;
@@ -367,6 +412,38 @@ interface FieldConfig {
367
412
  order?: number;
368
413
  /** Key to use when reading initial value from record (defaults to field name) */
369
414
  recordKey?: string;
415
+ /**
416
+ * Per-instance default value. Overrides the input type's defaultValue;
417
+ * superseded by record/defaultValues prop. Honored when !== undefined, so
418
+ * null/false/0/"" are meaningful. See §6.4.1, §13.1.
419
+ */
420
+ defaultValue?: unknown;
421
+ /**
422
+ * Per-instance auto-save debounce. Overrides the input type's debounce;
423
+ * falls back to Form-level debounce prop (default 1000). false = submit
424
+ * immediately. See §6.4.2.
425
+ */
426
+ debounce?: number | false;
427
+ /**
428
+ * Per-instance value transform (input→form). Overrides the input type's
429
+ * parser. String = named parser; function = inline. See §6.4.3.
430
+ */
431
+ parser?: string | ((value: unknown) => unknown);
432
+ /**
433
+ * Per-instance value transform (form→display). Overrides the input type's
434
+ * formatter. String = named formatter; function = inline. See §6.4.3.
435
+ */
436
+ formatter?: string | ((value: unknown) => unknown);
437
+ /**
438
+ * Submit-side value extraction from complex objects. Overrides the input
439
+ * type's valueField. See §6.4.4.
440
+ */
441
+ valueField?: string;
442
+ /**
443
+ * Submit-side field name transformation. Overrides the input type's
444
+ * getSubmitField. See §6.4.4.
445
+ */
446
+ getSubmitField?: (fieldName: string) => string;
370
447
  /** 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`) */
371
448
  rules?: Record<string, unknown>;
372
449
  /** Field-level validation (runs before type-level validator) */
@@ -1191,26 +1268,53 @@ declare function mergeConfigs(provider: FormalityProviderConfig, form?: FormConf
1191
1268
  fieldConfig: FieldConfig;
1192
1269
  };
1193
1270
 
1271
+ /**
1272
+ * Resolve a field-level override against its type-level default. Returns the
1273
+ * field value when it is not undefined (so null/false/0/"" are meaningful
1274
+ * overrides); otherwise the type value. This is the single precedence rule
1275
+ * shared by defaultValue, debounce, parser, formatter, getSubmitField, and
1276
+ * valueField (§6.4.0). Every adapter MUST call this helper at each
1277
+ * field-vs-type resolution site.
1278
+ *
1279
+ * @param fieldVal - The field-level (instance) value; `undefined` means "not specified".
1280
+ * @param typeVal - The type-level (InputConfig) default; `undefined` means "not specified".
1281
+ * @returns `fieldVal` when it is not `undefined`, else `typeVal`.
1282
+ *
1283
+ * @example
1284
+ * // Field override wins (even when falsy):
1285
+ * resolveFieldOverType(false, true); // → false
1286
+ * resolveFieldOverType(null, "x"); // → null
1287
+ * resolveFieldOverType(0, 100); // → 0
1288
+ * resolveFieldOverType("", "fallback");// → ""
1289
+ *
1290
+ * // Field unset → type default:
1291
+ * resolveFieldOverType(undefined, "type"); // → "type"
1292
+ * resolveFieldOverType(undefined, undefined); // → undefined
1293
+ */
1294
+ declare function resolveFieldOverType<T>(fieldVal: T | undefined, typeVal: T | undefined): T | undefined;
1194
1295
  /**
1195
1296
  * Resolve the initial value for a field
1196
1297
  *
1197
1298
  * Priority order (highest to lowest):
1198
1299
  * 1. defaultValues[fieldName] (from Form props)
1199
1300
  * 2. record[recordKey] (using recordKey if specified, else fieldName)
1200
- * 3. inputConfig.defaultValue (from input type definition)
1301
+ * 3. fieldConfig.defaultValue (per-instance default; §6.4.1, §13.1)
1302
+ * 4. inputConfig.defaultValue (from input type definition)
1201
1303
  *
1202
1304
  * **PRD deviation note (accepted, gap_analysis G5).** PRD §1.3.2's table
1203
1305
  * summarizes this export as `resolveInitialValue(record, config, inputConfig)`.
1204
1306
  * The implemented signature is a richer superset —
1205
1307
  * `(fieldName, fieldConfig?, inputConfig?, record?, defaultValues?)` — because
1206
1308
  * it drives the full priority chain above (defaultValues → record[recordKey] →
1207
- * inputConfig.defaultValue) from a single call. This is an internal API
1208
- * consumed by the framework adapters and by {@link resolveAllInitialValues},
1209
- * not a simplified end-user entry point; the PRD literal form is a condensed
1210
- * representation. No code change is planned.
1309
+ * fieldConfig.defaultValue → inputConfig.defaultValue) from a single call. This
1310
+ * is an internal API consumed by the framework adapters and by
1311
+ * {@link resolveAllInitialValues}, not a simplified end-user entry point; the
1312
+ * PRD literal form is a condensed representation. No code change is planned.
1211
1313
  *
1212
1314
  * @param fieldName - Field name
1213
- * @param fieldConfig - Field configuration
1315
+ * @param fieldConfig - Field configuration; `defaultValue` (when set) is the
1316
+ * Priority-3 per-instance default (§6.4.1), honored for any value
1317
+ * `!== undefined` (so null/false/0/"" are meaningful).
1214
1318
  * @param inputConfig - Input type configuration
1215
1319
  * @param record - Record data passed to form
1216
1320
  * @param defaultValues - Default values passed to form
@@ -1236,6 +1340,16 @@ declare function mergeConfigs(provider: FormalityProviderConfig, form?: FormConf
1236
1340
  * { status: 'active' }
1237
1341
  * )
1238
1342
  * // → 'active' (defaultValues takes precedence)
1343
+ *
1344
+ * // Field-level default wins over the type default (§6.4.1)
1345
+ * resolveInitialValue(
1346
+ * 'active',
1347
+ * { type: 'switch', defaultValue: true }, // field default: true
1348
+ * { defaultValue: false }, // type default: false
1349
+ * undefined,
1350
+ * undefined,
1351
+ * )
1352
+ * // → true (field-level default honored; null/false/0/"" would also win)
1239
1353
  */
1240
1354
  declare function resolveInitialValue(fieldName: string, fieldConfig?: FieldConfig, inputConfig?: InputConfig, record?: Record<string, unknown>, defaultValues?: Record<string, unknown>): unknown;
1241
1355
  /**
@@ -1329,30 +1443,46 @@ declare function humanizeLabel(fieldName: string): string;
1329
1443
  * Priority order (highest to lowest):
1330
1444
  * 1. Component prop (label from JSX props)
1331
1445
  * 2. Field config props.label
1332
- * 3. Evaluated selectProps.label
1446
+ * 3. Evaluated selectProps.label (field-level selectProps)
1333
1447
  * 4. Field config label
1334
1448
  * 5. Field config title (legacy alias)
1335
- * 6. Auto-generated from field name
1449
+ * 6. Evaluated form-level selectDefaultFieldProps.label
1450
+ * 7. Evaluated provider-level selectDefaultFieldProps.label
1451
+ * 8. Auto-generated from field name
1452
+ *
1453
+ * The provider/form `selectDefaultFieldProps` layers are documented as label
1454
+ * sources in PRD §16.1 (`providerConfig.selectDefaultFieldProps: { label:
1455
+ * "props.name" }` works; same for `formConfig`). They sit below the field-level
1456
+ * sources (matching their lower precedence in the 8-layer `mergeFieldProps`
1457
+ * pipeline — layers 5 and 7 vs. the field-level layers 2/3) but above the
1458
+ * humanized fallback, so a global label convention applies unless the field
1459
+ * itself overrides it.
1336
1460
  *
1337
1461
  * **PRD deviation note (accepted, gap_analysis G5).** PRD §1.3.2's table
1338
1462
  * summarizes this export as `resolveLabel(config, fieldName)`. The implemented
1339
1463
  * signature is a richer superset —
1340
- * `(fieldName, fieldConfig?, evaluatedSelectProps?, componentProps?)` — because
1341
- * it resolves the full 6-source priority chain above in one call, which
1342
- * requires the pre-evaluated `selectProps` and the JSX `componentProps`. This
1464
+ * `(fieldName, fieldConfig?, evaluatedSelectProps?, componentProps?,
1465
+ * providerSelectProps?, formSelectProps?)` — because it resolves the full
1466
+ * priority chain above in one call, which requires the pre-evaluated
1467
+ * `selectProps`/`selectDefaultFieldProps` and the JSX `componentProps`. This
1343
1468
  * is an internal API consumed by the framework adapters (e.g.
1344
1469
  * `@formality-ui/react`'s `Field` calls
1345
- * `resolveLabel(name, fieldConfig, fieldSelectProps, restProps)`), not a
1346
- * simplified end-user entry point; the PRD literal form is a condensed
1347
- * representation. No code change is planned.
1470
+ * `resolveLabel(name, fieldConfig, fieldSelectProps, restProps,
1471
+ * providerSelectProps, formSelectProps)`), not a simplified end-user entry
1472
+ * point; the PRD literal form is a condensed representation. No code change is
1473
+ * planned.
1348
1474
  *
1349
1475
  * @param fieldName - Field name
1350
1476
  * @param fieldConfig - Field configuration
1351
- * @param evaluatedSelectProps - Pre-evaluated selectProps
1477
+ * @param evaluatedSelectProps - Pre-evaluated field-level selectProps
1352
1478
  * @param componentProps - Props from JSX
1479
+ * @param providerSelectProps - Pre-evaluated provider-level
1480
+ * selectDefaultFieldProps (PRD §16.1)
1481
+ * @param formSelectProps - Pre-evaluated form-level selectDefaultFieldProps
1482
+ * (PRD §16.1)
1353
1483
  * @returns Resolved label string
1354
1484
  */
1355
- declare function resolveLabel(fieldName: string, fieldConfig?: FieldConfig, evaluatedSelectProps?: Record<string, unknown>, componentProps?: Record<string, unknown>): string;
1485
+ declare function resolveLabel(fieldName: string, fieldConfig?: FieldConfig, evaluatedSelectProps?: Record<string, unknown>, componentProps?: Record<string, unknown>, providerSelectProps?: Record<string, unknown>, formSelectProps?: Record<string, unknown>): string;
1356
1486
  /**
1357
1487
  * Resolve the title for a form
1358
1488
  *
@@ -1396,4 +1526,4 @@ declare function parseLabelWithUnit(label: string): {
1396
1526
  unit?: string;
1397
1527
  };
1398
1528
 
1399
- 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, mergeConfigs, mergeFieldProps, mergeInputConfigs, mergeRecordWithDefaults, mergeStaticProps, minLength, parse, parseLabelWithUnit, pattern, required, resolveAllInitialValues, resolveErrorMessage, resolveFieldType, resolveFormTitle, resolveInitialValue, resolveInputConfig, resolveLabel, runValidator, runValidatorSync, sortFieldsByOrder, transformFieldName, unwrapFieldProxy, validate };
1529
+ 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, mergeConfigs, mergeFieldProps, mergeInputConfigs, mergeRecordWithDefaults, mergeStaticProps, minLength, parse, parseLabelWithUnit, pattern, required, resolveAllInitialValues, resolveErrorMessage, resolveFieldOverType, resolveFieldType, resolveFormTitle, resolveInitialValue, resolveInputConfig, resolveLabel, runValidator, runValidatorSync, sortFieldsByOrder, transformFieldName, unwrapFieldProxy, validate };
package/dist/index.js CHANGED
@@ -1220,6 +1220,9 @@ function mergeConfigs(provider, form, field) {
1220
1220
  }
1221
1221
 
1222
1222
  // src/config/defaults.ts
1223
+ function resolveFieldOverType(fieldVal, typeVal) {
1224
+ return fieldVal !== void 0 ? fieldVal : typeVal;
1225
+ }
1223
1226
  function resolveInitialValue(fieldName, fieldConfig, inputConfig, record, defaultValues) {
1224
1227
  if (defaultValues && fieldName in defaultValues) {
1225
1228
  return defaultValues[fieldName];
@@ -1228,8 +1231,12 @@ function resolveInitialValue(fieldName, fieldConfig, inputConfig, record, defaul
1228
1231
  if (record && recordKey in record) {
1229
1232
  return record[recordKey];
1230
1233
  }
1231
- if (inputConfig?.defaultValue !== void 0) {
1232
- return inputConfig.defaultValue;
1234
+ const resolvedDefault = resolveFieldOverType(
1235
+ fieldConfig?.defaultValue,
1236
+ inputConfig?.defaultValue
1237
+ );
1238
+ if (resolvedDefault !== void 0) {
1239
+ return resolvedDefault;
1233
1240
  }
1234
1241
  return void 0;
1235
1242
  }
@@ -1340,7 +1347,7 @@ function humanizeLabel(fieldName) {
1340
1347
  const words = fieldName.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").split(" ");
1341
1348
  return words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1342
1349
  }
1343
- function resolveLabel(fieldName, fieldConfig, evaluatedSelectProps, componentProps) {
1350
+ function resolveLabel(fieldName, fieldConfig, evaluatedSelectProps, componentProps, providerSelectProps, formSelectProps) {
1344
1351
  if (componentProps?.label !== void 0) {
1345
1352
  return String(componentProps.label);
1346
1353
  }
@@ -1356,6 +1363,12 @@ function resolveLabel(fieldName, fieldConfig, evaluatedSelectProps, componentPro
1356
1363
  if (fieldConfig?.title !== void 0) {
1357
1364
  return fieldConfig.title;
1358
1365
  }
1366
+ if (formSelectProps?.label !== void 0) {
1367
+ return String(formSelectProps.label);
1368
+ }
1369
+ if (providerSelectProps?.label !== void 0) {
1370
+ return String(providerSelectProps.label);
1371
+ }
1359
1372
  return humanizeLabel(fieldName);
1360
1373
  }
1361
1374
  function resolveFormTitle(formTitle, evaluatedSelectTitle) {
@@ -1381,6 +1394,6 @@ function parseLabelWithUnit(label) {
1381
1394
  return { base: label };
1382
1395
  }
1383
1396
 
1384
- export { FIELD_PROXY_MARKER, FIELD_PROXY_VALUE, KEYWORDS, QUALIFIED_PREFIXES, 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, mergeConfigs, mergeFieldProps, mergeInputConfigs, mergeRecordWithDefaults, mergeStaticProps, minLength, parse, parseLabelWithUnit, pattern, required, resolveAllInitialValues, resolveErrorMessage, resolveFieldType, resolveFormTitle, resolveInitialValue, resolveInputConfig, resolveLabel, runValidator, runValidatorSync, sortFieldsByOrder, transformFieldName, unwrapFieldProxy, validate };
1397
+ export { FIELD_PROXY_MARKER, FIELD_PROXY_VALUE, KEYWORDS, QUALIFIED_PREFIXES, 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, mergeConfigs, mergeFieldProps, mergeInputConfigs, mergeRecordWithDefaults, mergeStaticProps, minLength, parse, parseLabelWithUnit, pattern, required, resolveAllInitialValues, resolveErrorMessage, resolveFieldOverType, resolveFieldType, resolveFormTitle, resolveInitialValue, resolveInputConfig, resolveLabel, runValidator, runValidatorSync, sortFieldsByOrder, transformFieldName, unwrapFieldProxy, validate };
1385
1398
  //# sourceMappingURL=index.js.map
1386
1399
  //# sourceMappingURL=index.js.map