/** * \@formspec/config * * Constraint validation for FormSpec - restrict features based on target * environment capabilities. * * This package provides: * - Type definitions for constraint configuration * - YAML/TypeScript config file loading * - Core validation logic for FormSpec elements * - JSON Schema for .formspec.yml validation * * @example * ```ts * import { loadConfig, validateFormSpecElements } from '@formspec/config'; * import { formspec, field } from '@formspec/dsl'; * * // Load constraints from .formspec.yml * const { config } = await loadConfig(); * * // Create a form * const form = formspec( * field.text("name"), * field.dynamicEnum("country", "countries"), * ); * * // Validate against constraints * const result = validateFormSpecElements(form.elements, { constraints: config }); * * if (!result.valid) { * console.error('Validation failed:', result.issues); * } * ``` * * @packageDocumentation */ /** * Union of all field types. * * @public */ export declare type AnyField = TextField | NumberField | BooleanField | StaticEnumField | DynamicEnumField | DynamicSchemaField | ArrayField | ObjectField; /** * An array field containing repeating items. * * Use this for lists of values (e.g., multiple addresses, line items). * * @typeParam N - The field name (string literal type) * @typeParam Items - The form elements that define each array item * * @public */ export declare interface ArrayField { /** Type discriminator for form elements */ readonly _type: "field"; /** Field type discriminator - identifies this as an array field */ readonly _field: "array"; /** Unique field identifier used as the schema key */ readonly name: N; /** Form elements that define the schema for each array item */ readonly items: Items; /** Display label for the field */ readonly label?: string; /** Canonical display name for the field. Alias of `label` in the chain DSL. */ readonly displayName?: string; /** JSON-facing serialized name for the field. */ readonly apiName?: string; /** Whether this field is required for form submission */ readonly required?: boolean; /** Minimum number of items required */ readonly minItems?: number; /** Maximum number of items allowed */ readonly maxItems?: number; } /** * A boolean checkbox field. * * @typeParam N - The field name (string literal type) * * @public */ export declare interface BooleanField { /** Type discriminator for form elements */ readonly _type: "field"; /** Field type discriminator - identifies this as a boolean field */ readonly _field: "boolean"; /** Unique field identifier used as the schema key */ readonly name: N; /** Display label for the field */ readonly label?: string; /** Canonical display name for the field. Alias of `label` in the chain DSL. */ readonly displayName?: string; /** JSON-facing serialized name for the field. */ readonly apiName?: string; /** Whether this field is required for form submission */ readonly required?: boolean; } /** * Registration for mapping a built-in TSDoc tag onto a custom constraint when * it is used on a particular custom type. * * @public */ declare interface BuiltinConstraintBroadeningRegistration { /** The built-in tag being broadened, without the `@` prefix. */ readonly tagName: BuiltinConstraintName; /** The custom constraint to emit for this built-in tag. */ readonly constraintName: string; /** Parser from raw TSDoc text to extension payload. */ readonly parseValue: (raw: string) => ExtensionPayloadValue; } /** * Type of a built-in constraint name. * * @public */ declare type BuiltinConstraintName = "minimum" | "maximum" | "exclusiveMinimum" | "exclusiveMaximum" | "multipleOf" | "minLength" | "maxLength" | "minItems" | "maxItems" | "uniqueItems" | "pattern" | "const" | "enumOptions"; /** * A conditional wrapper that shows/hides elements based on another field's value. * * @typeParam FieldName - The field to check * @typeParam Value - The value that triggers the condition * @typeParam Elements - Tuple of contained form elements * * @public */ export declare interface Conditional { /** Type discriminator - identifies this as a conditional element */ readonly _type: "conditional"; /** Name of the field whose value determines visibility */ readonly field: FieldName; /** Value that triggers the condition (shows nested elements) */ readonly value: Value; /** Form elements shown when condition is met */ readonly elements: Elements; } /** * Complete constraint configuration for a FormSpec project. * * @public */ export declare interface ConstraintConfig { /** Field type constraints */ fieldTypes?: FieldTypeConstraints; /** Layout and structure constraints */ layout?: LayoutConstraints; /** UI Schema feature constraints */ uiSchema?: UISchemaConstraints; /** Field configuration option constraints */ fieldOptions?: FieldOptionConstraints; /** Control options constraints */ controlOptions?: ControlOptionConstraints; } /** * Semantic metadata for ordered custom constraints that should participate in * the generic contradiction/broadening logic. * * @public */ declare interface ConstraintSemanticRole { /** * Logical family identifier shared by related constraints, for example * `"decimal-bound"` or `"date-bound"`. */ readonly family: string; /** Whether this constraint acts as a lower or upper bound. */ readonly bound: "lower" | "upper" | "exact"; /** Whether equality is allowed when comparing against the bound. */ readonly inclusive: boolean; } /** * Declarative authoring-side registration for a custom TSDoc constraint tag. * * @public */ declare interface ConstraintTagRegistration { /** Tag name without the `@` prefix, e.g. `"maxSigFig"`. */ readonly tagName: string; /** The custom constraint that this tag should produce. */ readonly constraintName: string; /** Parser from raw TSDoc text to JSON-serializable payload. */ readonly parseValue: (raw: string) => ExtensionPayloadValue; /** * Optional precise applicability predicate for the field type being parsed. * When omitted, the target custom constraint registration controls type * applicability during validation. */ readonly isApplicableToType?: (type: ExtensionApplicableType) => boolean; } /** * Control options constraints - control which JSONForms Control.options are allowed. * These are renderer-specific options that may not be universally supported. * * @public */ export declare interface ControlOptionConstraints { /** format - renderer format hint (e.g., "radio", "textarea") */ format?: Severity; /** readonly - read-only mode */ readonly?: Severity; /** multi - multi-select for enums */ multi?: Severity; /** showUnfocusedDescription - show description when unfocused */ showUnfocusedDescription?: Severity; /** hideRequiredAsterisk - hide required indicator */ hideRequiredAsterisk?: Severity; /** Custom control options (extensible dictionary) */ custom?: Record; } /** * Registration for a custom annotation that may produce JSON Schema keywords. * * Custom annotations are referenced by FormSpec's internal custom-annotation nodes. * They describe or present a field but do not affect which values are valid. * * @public */ declare interface CustomAnnotationRegistration { /** The annotation name, unique within the extension. */ readonly annotationName: string; /** * Optionally converts the annotation value into JSON Schema keywords. * If omitted, the annotation has no JSON Schema representation (UI-only). */ readonly toJsonSchema?: (value: ExtensionPayloadValue, vendorPrefix: string) => Record; } /** * Registration for a custom constraint that maps to JSON Schema keywords. * * Custom constraints are referenced by FormSpec's internal custom-constraint nodes. * * @public */ declare interface CustomConstraintRegistration { /** The constraint name, unique within the extension. */ readonly constraintName: string; /** * How this constraint composes with other constraints of the same kind. * - "intersect": combine with logical AND (both must hold) * - "override": last writer wins */ readonly compositionRule: "intersect" | "override"; /** * TypeNode kinds this constraint is applicable to, or `null` for any type. * Used by the validator to emit TYPE_MISMATCH diagnostics. */ readonly applicableTypes: readonly ExtensionApplicableType["kind"][] | null; /** * Optional precise type predicate used when kind-level applicability is too * broad (for example, constraints that apply to integer-like primitives but * not strings). */ readonly isApplicableToType?: (type: ExtensionApplicableType) => boolean; /** * Optional comparator for payloads belonging to the same custom constraint. * Return values follow the `Array.prototype.sort()` contract. */ readonly comparePayloads?: (left: ExtensionPayloadValue, right: ExtensionPayloadValue) => number; /** * Optional semantic family metadata for generic contradiction/broadening * handling across ordered constraints. */ readonly semanticRole?: ConstraintSemanticRole; /** * Converts the custom constraint's payload into JSON Schema keywords. * * @param payload - The opaque JSON payload stored on the custom constraint node. * @param vendorPrefix - The vendor prefix for extension keywords. * @returns A JSON Schema fragment with the constraint keywords. */ readonly toJsonSchema: (payload: ExtensionPayloadValue, vendorPrefix: string) => Record; /** * When true, `toJsonSchema` may emit vocabulary keywords that do not carry * the vendor prefix. By default, all keys returned from `toJsonSchema` must * start with `${vendorPrefix}-`; setting this flag relaxes that check so * the constraint can produce standard or custom vocabulary keywords such as * `decimalMinimum`. * * Use this for constraints that define their own JSON Schema vocabulary * rather than namespacing under the vendor prefix. */ readonly emitsVocabularyKeywords?: boolean; } /** * Registration for a custom type that maps to a JSON Schema representation. * * Custom types are referenced by FormSpec's internal custom-type IR nodes and * resolved to JSON Schema via `toJsonSchema` during generation. * * @public */ declare interface CustomTypeRegistration { /** The type name, unique within the extension. */ readonly typeName: string; /** * Optional TypeScript surface names that should resolve to this custom type * during TSDoc/class analysis. Defaults to `typeName` when omitted. * @deprecated Prefer `brand` for structural detection or type parameters * on `defineCustomType()` for symbol-based detection. String name * matching will be removed in a future major version. */ readonly tsTypeNames?: readonly string[]; /** * Optional brand identifier for structural type detection. * * When provided, the type resolver checks `type.getProperties()` for a * computed property whose name matches this identifier. This is more * reliable than `tsTypeNames` for aliased branded types because it does not * depend on the local type name. * * Brand detection is attempted after name-based resolution (`tsTypeNames`) * as a structural fallback. If both match, name-based resolution wins. * * The value should match the identifier text of a `unique symbol` declaration * used as a computed property key on the branded type. For example, if the * type is `string & { readonly [__decimalBrand]: true }`, the brand is * `"__decimalBrand"`. * * Brand identifiers are stored as plain strings in the extension registry, so * they must be unique across the extensions loaded into the same build. * * Note: `"__integerBrand"` is reserved for the builtin Integer type. */ readonly brand?: string; /** * Converts the custom type's payload into a JSON Schema fragment. * * @param payload - The opaque JSON payload stored on the custom type node. * @param vendorPrefix - The vendor prefix for extension keywords (e.g., "x-stripe"). * @returns A JSON Schema fragment representing this type. */ readonly toJsonSchema: (payload: ExtensionPayloadValue, vendorPrefix: string) => Record; /* Excluded from this release type: serializeDefault */ /** * Optional broadening of built-in constraint tags so they can apply to this * custom type without modifying the core built-in constraint tables. */ readonly builtinConstraintBroadenings?: readonly BuiltinConstraintBroadeningRegistration[]; } /** * Per-declaration metadata policy input. * * @public */ declare interface DeclarationMetadataPolicyInput { /** Policy for JSON-facing serialized names. */ readonly apiName?: MetadataValuePolicyInput | undefined; /** Policy for human-facing labels and titles. */ readonly displayName?: MetadataValuePolicyInput | undefined; } /** * Default FormSpec configuration. * * @beta */ export declare const DEFAULT_CONFIG: FormSpecConfig; /** * Default constraint configuration that allows all features. * All constraints default to "off" (allowed). * * @beta */ export declare const DEFAULT_CONSTRAINTS: ResolvedConstraintConfig; /** * Creates a constraint configuration directly from an object. * Useful for programmatic configuration without a config file. * * @param config - Partial constraint configuration * @returns Complete configuration with defaults applied * * @example * ```ts * const config = defineConstraints({ * fieldTypes: { * dynamicEnum: 'error', * dynamicSchema: 'error', * }, * layout: { * group: 'error', * }, * }); * ``` * * @public */ export declare function defineConstraints(config: ConstraintConfig): ResolvedConstraintConfig; /** * Identity function that provides type checking and IDE autocompletion * for FormSpec configuration objects. * * @example * ```typescript * import { defineFormSpecConfig } from '@formspec/config'; * * export default defineFormSpecConfig({ * extensions: [myExtension], * vendorPrefix: 'x-acme', * }); * ``` * * @public */ export declare function defineFormSpecConfig(config: FormSpecConfig): FormSpecConfig; /** * A field with dynamic enum options (fetched from a data source at runtime). * * @typeParam N - The field name (string literal type) * @typeParam Source - The data source key (from DataSourceRegistry) * * @public */ export declare interface DynamicEnumField { /** Type discriminator for form elements */ readonly _type: "field"; /** Field type discriminator - identifies this as a dynamic enum field */ readonly _field: "dynamic_enum"; /** Unique field identifier used as the schema key */ readonly name: N; /** Data source key for fetching options at runtime */ readonly source: Source; /** Display label for the field */ readonly label?: string; /** Canonical display name for the field. Alias of `label` in the chain DSL. */ readonly displayName?: string; /** JSON-facing serialized name for the field. */ readonly apiName?: string; /** Whether this field is required for form submission */ readonly required?: boolean; /** Field names whose values are needed to fetch options */ readonly params?: readonly string[]; } /** * A field that loads its schema dynamically (e.g., from an extension). * * @typeParam N - The field name (string literal type) * * @public */ export declare interface DynamicSchemaField { /** Type discriminator for form elements */ readonly _type: "field"; /** Field type discriminator - identifies this as a dynamic schema field */ readonly _field: "dynamic_schema"; /** Unique field identifier used as the schema key */ readonly name: N; /** Identifier for the schema source */ readonly schemaSource: string; /** Display label for the field */ readonly label?: string; /** Canonical display name for the field. Alias of `label` in the chain DSL. */ readonly displayName?: string; /** JSON-facing serialized name for the field. */ readonly apiName?: string; /** Whether this field is required for form submission */ readonly required?: boolean; /** Field names whose values are needed to configure the schema */ readonly params?: readonly string[]; } /** * Enum-member display names remain unset unless authored explicitly. * * @public */ declare interface EnumMemberDisplayNameDisabledPolicyInput { /** Leaves missing enum-member display names unresolved. */ readonly mode: "disabled"; } /** * Missing enum-member display names may be inferred. * * @public */ declare interface EnumMemberDisplayNameInferIfMissingPolicyInput { /** Infers an enum-member display name when it is not authored explicitly. */ readonly mode: "infer-if-missing"; /** Callback used to infer the missing display name. */ readonly infer: EnumMemberMetadataInferenceFn; } /** * Enum-member display-name policy input. * * @public */ declare type EnumMemberDisplayNamePolicyInput = EnumMemberDisplayNameDisabledPolicyInput | EnumMemberDisplayNameRequireExplicitPolicyInput | EnumMemberDisplayNameInferIfMissingPolicyInput; /** * Enum members must declare display names explicitly. * * @public */ declare interface EnumMemberDisplayNameRequireExplicitPolicyInput { /** Fails when an enum member has no authored display name. */ readonly mode: "require-explicit"; } /** * Build-facing context passed to enum-member metadata inference callbacks. * * Enum members are resolved separately from declaration-level metadata so they * do not participate in the shared declaration-kind model used by TSDoc and * extension metadata slots. * * @public */ declare interface EnumMemberMetadataInferenceContext { /** Authoring surface the enum originated from. */ readonly surface: MetadataAuthoringSurface; /** Logical member identifier used for policy inference. */ readonly logicalName: string; /** Underlying enum value before stringification. */ readonly memberValue: string | number; /** Optional build-only context supplied by the resolver. */ readonly buildContext?: unknown; } /** * Callback used to infer enum-member display names. * * @public */ declare type EnumMemberMetadataInferenceFn = (context: EnumMemberMetadataInferenceContext) => string; /** * User-facing enum-member metadata policy input. * * @public */ declare interface EnumMemberMetadataPolicyInput { /** Policy for human-facing enum-member labels. */ readonly displayName?: EnumMemberDisplayNamePolicyInput | undefined; } /** * An enum option with a separate ID and display label. * * Use this when the stored value (id) should differ from the display text (label). * * @public */ export declare interface EnumOption { /** Stored enum value written into submitted data. */ readonly id: string; /** Human-readable label shown to end users. */ readonly label: string; } /** * Valid enum option types: either plain strings or objects with id/label. * * @public */ export declare type EnumOptionValue = string | EnumOption; /** * A curated type shape exposed to extension applicability hooks. * * This intentionally exposes only the fields needed to determine tag/type * applicability without committing the entire canonical IR as public API. * * @public */ declare type ExtensionApplicableType = { readonly kind: "primitive"; readonly primitiveKind: "string" | "number" | "integer" | "bigint" | "boolean" | "null"; } | { readonly kind: "custom"; readonly typeId: string; readonly payload: ExtensionPayloadValue; } | { readonly kind: Exclude; }; /** * A complete extension definition bundling types, constraints, annotations, * and vocabulary keywords. * * @example * ```typescript * const monetaryExtension = defineExtension({ * extensionId: "x-stripe/monetary", * types: [ * defineCustomType({ * typeName: "Decimal", * toJsonSchema: (_payload, prefix) => ({ * type: "string", * [`${prefix}-decimal`]: true, * }), * }), * ], * }); * ``` * * @public */ declare interface ExtensionDefinition { /** Globally unique extension identifier, e.g., "x-stripe/monetary". */ readonly extensionId: string; /** Custom type registrations provided by this extension. */ readonly types?: readonly CustomTypeRegistration[]; /** Custom constraint registrations provided by this extension. */ readonly constraints?: readonly CustomConstraintRegistration[]; /** Authoring-side TSDoc tag registrations provided by this extension. */ readonly constraintTags?: readonly ConstraintTagRegistration[]; /** Metadata-slot registrations shared by build- and lint-time analysis. */ readonly metadataSlots?: readonly MetadataSlotRegistration[]; /** Custom annotation registrations provided by this extension. */ readonly annotations?: readonly CustomAnnotationRegistration[]; /** Vocabulary keyword registrations provided by this extension. */ readonly vocabularyKeywords?: readonly VocabularyKeywordRegistration[]; } /** * A JSON-serializable payload value used by extension registration hooks. * * @public */ declare type ExtensionPayloadValue = null | boolean | number | string | readonly ExtensionPayloadValue[] | { readonly [key: string]: ExtensionPayloadValue; }; /** * Top-level type kinds that extension applicability hooks may inspect. * * @public */ declare type ExtensionTypeKind = "primitive" | "enum" | "array" | "object" | "record" | "union" | "reference" | "dynamic" | "custom"; /** * Extracts which options are present on a field object. * Works with FormSpec field types. * * @param field - A field object with potential options * @returns Array of present option names * * @beta */ export declare function extractFieldOptions(field: Record): FieldOption[]; /** * Known field options that can be validated. * * @beta */ export declare type FieldOption = "label" | "placeholder" | "required" | "minValue" | "maxValue" | "minItems" | "maxItems"; /** * Field configuration option constraints - control which field options are allowed. * * @public */ export declare interface FieldOptionConstraints { /** label - field label text */ label?: Severity; /** placeholder - input placeholder text */ placeholder?: Severity; /** required - whether field is required */ required?: Severity; /** minValue - minimum value for numbers */ minValue?: Severity; /** maxValue - maximum value for numbers */ maxValue?: Severity; /** minItems - minimum array length */ minItems?: Severity; /** maxItems - maximum array length */ maxItems?: Severity; } /** * Context for field option validation. * * @beta */ export declare interface FieldOptionsContext { /** The field name */ fieldName: string; /** Which options are present on this field */ presentOptions: FieldOption[]; /** Path to this field */ path?: string; } /** * Field type constraints - control which field types are allowed. * Fine-grained control over each DSL field builder. * * @public */ export declare interface FieldTypeConstraints { /** field.text() - basic text input */ text?: Severity; /** field.number() - numeric input */ number?: Severity; /** field.boolean() - checkbox/toggle */ boolean?: Severity; /** field.enum() with literal options */ staticEnum?: Severity; /** field.dynamicEnum() - runtime-fetched options */ dynamicEnum?: Severity; /** field.dynamicSchema() - runtime-fetched schema */ dynamicSchema?: Severity; /** field.array() / field.arrayWithConfig() */ array?: Severity; /** field.object() / field.objectWithConfig() */ object?: Severity; } /** * Context for field type validation. * * @beta */ export declare interface FieldTypeContext { /** The _field discriminator value (e.g., "text", "number", "enum") */ fieldType: string; /** The field name */ fieldName: string; /** Optional path for nested fields */ path?: string; } /** * Union of all form element types (fields and structural elements). * * @public */ export declare type FormElement = AnyField | Group | Conditional; /** * A complete form specification. * * @typeParam Elements - Tuple of top-level form elements * * @public */ export declare interface FormSpec { /** Top-level form elements */ readonly elements: Elements; } /** * Top-level FormSpec configuration file structure. * The .formspec.yml file uses this structure. * * @public */ export declare interface FormSpecConfig { /** * Extension definitions providing custom types, constraints, * annotations, and vocabulary keywords. */ readonly extensions?: readonly ExtensionDefinition[]; /** Constraint surface configuration — controls which field types, * layouts, UI features, and field/control options are allowed. */ readonly constraints?: ConstraintConfig; /** * Metadata inference and naming policy. Controls how apiName, * displayName, and plural forms are derived when not authored. */ readonly metadata?: MetadataPolicyInput; /** * Vendor prefix for extension-emitted JSON Schema keywords. * Must start with "x-". * @defaultValue "x-formspec" */ readonly vendorPrefix?: string; /** * JSON Schema representation for static enums. * - "enum": compact `enum` output, plus a display-name extension when labels exist * - "oneOf": per-member `const` output, with `title` only for distinct labels * - "smart-size": uses `enum` unless a distinct display label would be lost * @defaultValue "enum" */ readonly enumSerialization?: "enum" | "oneOf" | "smart-size"; /** * Per-package configuration overrides for monorepos. * Keys are glob patterns matched against file paths relative to * the config file's directory. Values merge with root settings. */ readonly packages?: Readonly>; } /** * Per-package overrides that merge with the root config. * Only settings that genuinely vary per package are overridable. * * @public */ export declare interface FormSpecPackageOverride { /** Override constraint surface for this package. */ readonly constraints?: ConstraintConfig; /** Override enum serialization for this package. */ readonly enumSerialization?: "enum" | "oneOf" | "smart-size"; /** Override metadata policy for this package. */ readonly metadata?: MetadataPolicyInput; } /** * Options for validating FormSpec elements. * * @public */ export declare interface FormSpecValidationOptions { /** Constraint configuration (will be merged with defaults) */ constraints?: ConstraintConfig; } /** * Gets the severity level for a field option. * * @param option - The option to check * @param constraints - Field option constraints * @returns Severity level, or "off" if not constrained * * @beta */ export declare function getFieldOptionSeverity(option: FieldOption, constraints: FieldOptionConstraints): Severity; /** * Gets the severity level for a field type. * * @param fieldType - The _field discriminator value * @param constraints - Field type constraints * @returns Severity level, or "off" if not constrained * * @beta */ export declare function getFieldTypeSeverity(fieldType: string, constraints: FieldTypeConstraints): Severity; /** * A visual grouping of form elements. * * Groups provide visual organization and can be rendered as fieldsets or sections. * * @typeParam Elements - Tuple of contained form elements * * @public */ export declare interface Group { /** Type discriminator - identifies this as a group element */ readonly _type: "group"; /** Display label for the group */ readonly label: string; /** Form elements contained within this group */ readonly elements: Elements; } /** * Checks if a specific field option is allowed. * * @param option - The option to check * @param constraints - Field option constraints * @returns true if allowed, false if disallowed * * @beta */ export declare function isFieldOptionAllowed(option: FieldOption, constraints: FieldOptionConstraints): boolean; /** * Checks if a field type is allowed by the constraints. * Useful for quick checks without generating issues. * * @param fieldType - The _field discriminator value * @param constraints - Field type constraints * @returns true if allowed, false if disallowed * * @beta */ export declare function isFieldTypeAllowed(fieldType: string, constraints: FieldTypeConstraints): boolean; /** * Checks if a layout type is allowed by the constraints. * * @param layoutType - The type of layout element * @param constraints - Layout constraints * @returns true if allowed, false if disallowed * * @beta */ export declare function isLayoutTypeAllowed(layoutType: "group" | "conditional", constraints: LayoutConstraints): boolean; /** * Checks if a nesting depth is allowed. * * @param depth - Current nesting depth * @param constraints - Layout constraints * @returns true if allowed, false if exceeds limit * * @beta */ export declare function isNestingDepthAllowed(depth: number, constraints: LayoutConstraints): boolean; /** * Layout and structure constraints - control grouping, conditionals, nesting. * * @public */ export declare interface LayoutConstraints { /** group() - visual grouping of fields */ group?: Severity; /** when() - conditional field visibility */ conditionals?: Severity; /** Maximum nesting depth for objects/arrays (0 = flat only) */ maxNestingDepth?: number; } /** * Context for layout validation. * * @beta */ export declare interface LayoutContext { /** The type of layout element ("group" | "conditional") */ layoutType: "group" | "conditional"; /** Optional label for the element (for groups) */ label?: string; /** Current nesting depth */ depth: number; /** Path to this element */ path?: string; } /** * JSONForms layout type constraints. * * @public */ export declare interface LayoutTypeConstraints { /** VerticalLayout - stack elements vertically */ VerticalLayout?: Severity; /** HorizontalLayout - arrange elements horizontally */ HorizontalLayout?: Severity; /** Group - visual grouping with label */ Group?: Severity; /** Categorization - tabbed/wizard interface */ Categorization?: Severity; /** Category - individual tab/step in Categorization */ Category?: Severity; } /** * Loads FormSpec constraint configuration from a config file. * Returns the resolved constraints with defaults applied. * * @deprecated Use `loadFormSpecConfig` instead, which returns the full `FormSpecConfig`. * * @param options - Options for loading configuration * @returns The loaded configuration with defaults applied * * @public */ export declare function loadConfig(options?: LoadConfigOptions): Promise<{ config: ResolvedConstraintConfig; configPath: string | null; found: boolean; }>; /** * Result when a config file was found and loaded. * * @public */ export declare interface LoadConfigFoundResult { /** The loaded configuration */ config: FormSpecConfig; /** The absolute path to the config file that was loaded */ configPath: string; /** Whether a config file was found */ found: true; } /** * Result when no config file was found. * * @public */ export declare interface LoadConfigNotFoundResult { /** Whether a config file was found */ found: false; } /** * Options for loading configuration. * * @public */ export declare interface LoadConfigOptions { /** * The directory to start searching from. * Defaults to process.cwd(). */ searchFrom?: string; /** * Explicit path to a config file. * If provided, skips file discovery entirely. */ configPath?: string; /** * Optional logger for diagnostic output. Defaults to a no-op logger so * existing callers produce no output. */ logger?: LoggerLike | undefined; } /** * Result of loading configuration. * * @public */ export declare type LoadConfigResult = LoadConfigFoundResult | LoadConfigNotFoundResult; /** * Loads FormSpec configuration from a TypeScript config file. * * Searches for `formspec.config.ts` (and `.mts`, `.js`, `.mjs` variants) * starting from `searchFrom` and walking up the directory tree. Stops at * the filesystem root or a workspace root (a directory with a package.json * containing a "workspaces" field). * * @param options - Options for loading configuration * @returns The loaded configuration, or `{ found: false }` if no config file exists * * @example * ```ts * // Discover config from current directory * const result = await loadFormSpecConfig(); * if (result.found) { * console.log(result.config, result.configPath); * } * * // Discover config from a specific directory * const result = await loadFormSpecConfig({ searchFrom: '/path/to/project' }); * * // Load a specific config file * const result = await loadFormSpecConfig({ configPath: '/path/to/formspec.config.ts' }); * ``` * * @public */ export declare function loadFormSpecConfig(options?: LoadConfigOptions): Promise; /** * Minimal structured-logger contract used across FormSpec. * * Libraries accept a `LoggerLike` so callers can route diagnostics through * their own logger (e.g. pino in apps, the tsserver logger inside a TypeScript * language service plugin) without pulling a specific logger implementation * into published packages. * * The shape is a subset of pino's `Logger` and is trivially satisfiable by * most structured loggers. * * @public */ export declare interface LoggerLike { /** Writes a record at the finest-grained verbosity. */ trace(msg: string, ...args: unknown[]): void; /** Writes a diagnostic record used when investigating behaviour. */ debug(msg: string, ...args: unknown[]): void; /** Writes an informational record about normal operation. */ info(msg: string, ...args: unknown[]): void; /** Writes a record about a recoverable or unexpected condition. */ warn(msg: string, ...args: unknown[]): void; /** Writes a record about a failure that prevented the requested work. */ error(msg: string, ...args: unknown[]): void; /** * Returns a child logger that tags every subsequent record with the given * bindings (e.g. `{ stage: "ir" }`). */ child(bindings: Record): LoggerLike; } /** * Merges user constraints with defaults, filling in any missing values. * * @beta */ export declare function mergeWithDefaults(config: ConstraintConfig | undefined): ResolvedConstraintConfig; /** * Authoring surfaces that can contribute metadata. * * @public */ declare type MetadataAuthoringSurface = "tsdoc" | "chain-dsl"; /** * Declaration categories that metadata policy can target. * * @public */ declare type MetadataDeclarationKind = "type" | "field" | "method"; /** * Build-facing context passed to metadata inference callbacks. * * `buildContext` is intentionally opaque so browser/runtime packages do not * need to depend on TypeScript compiler types. * * @public */ declare interface MetadataInferenceContext { /** Authoring surface the metadata is being resolved for. */ readonly surface: MetadataAuthoringSurface; /** Declaration kind currently being resolved. */ readonly declarationKind: MetadataDeclarationKind; /** Logical identifier before any metadata policy is applied. */ readonly logicalName: string; /** Optional build-only context supplied by the resolver. */ readonly buildContext?: unknown; } /** * Callback used to infer a scalar metadata value. * * @public */ declare type MetadataInferenceFn = (context: MetadataInferenceContext) => string; /** * Context passed to pluralization callbacks. * * @public */ declare interface MetadataPluralizationContext extends MetadataInferenceContext { /** Singular value that pluralization should derive from. */ readonly singular: string; } /** * Pluralization disabled. * * @public */ declare interface MetadataPluralizationDisabledPolicyInput { /** Disables automatic plural-value generation. */ readonly mode?: "disabled" | undefined; } /** * Callback used to derive plural metadata from a singular value. * * @public */ declare type MetadataPluralizationFn = (context: MetadataPluralizationContext) => string; /** * Pluralization may be inferred when absent. * * @public */ declare interface MetadataPluralizationInferIfMissingPolicyInput { /** Infers plural values whenever no explicit plural is present. */ readonly mode: "infer-if-missing"; /** Callback that derives a plural form from the resolved singular value. */ readonly inflect: MetadataPluralizationFn; } /** * Pluralization policy input. * * @public */ declare type MetadataPluralizationPolicyInput = MetadataPluralizationDisabledPolicyInput | MetadataPluralizationRequireExplicitPolicyInput | MetadataPluralizationInferIfMissingPolicyInput; /** * Pluralization must be authored explicitly. * * @public */ declare interface MetadataPluralizationRequireExplicitPolicyInput { /** Requires plural values to be authored directly. */ readonly mode: "require-explicit"; } /** * User-facing metadata policy configuration. * * @public */ declare interface MetadataPolicyInput { /** Policy applied to named types and the analyzed root declaration. */ readonly type?: DeclarationMetadataPolicyInput | undefined; /** Policy applied to fields and object properties. */ readonly field?: DeclarationMetadataPolicyInput | undefined; /** Policy applied to callable/method declarations. */ readonly method?: DeclarationMetadataPolicyInput | undefined; /** Policy applied to enum-member display names during build-time IR resolution. */ readonly enumMember?: EnumMemberMetadataPolicyInput | undefined; } /** * Supported qualifier registration for an extensible metadata slot. * * @public */ declare interface MetadataQualifierRegistration { /** Qualifier text without the leading colon. */ readonly qualifier: string; /** * Optional source qualifier to use as the base input for this qualifier's * inference hook. Defaults to the slot's bare/default value when omitted. */ readonly sourceQualifier?: string | undefined; /** Optional inference hook for this qualified value. */ readonly inferValue?: MetadataSlotInferenceFn | undefined; } /** * Stable slot identifier for extensible metadata analysis. * * @public */ declare type MetadataSlotId = string; /** * Context passed to extensible metadata inference hooks. * * @public */ declare interface MetadataSlotInferenceContext extends MetadataInferenceContext { /** Stable logical slot identifier. */ readonly slotId: MetadataSlotId; /** Tag name associated with the slot, without the `@` prefix. */ readonly tagName: string; /** Optional qualifier being inferred (for example `plural`). */ readonly qualifier?: string | undefined; /** Resolved bare/default value used as the base input for derived qualifiers. */ readonly baseValue?: string | undefined; } /** * Callback used to infer an extensible metadata slot value. * * @public */ declare type MetadataSlotInferenceFn = (context: MetadataSlotInferenceContext) => string; /** * Extensible metadata slot definition shared across build- and lint-time analysis. * * @public */ declare interface MetadataSlotRegistration { /** Stable logical slot identifier. */ readonly slotId: MetadataSlotId; /** Tag name associated with this slot, without the `@` prefix. */ readonly tagName: string; /** Declaration kinds where the slot is meaningful. */ readonly declarationKinds: readonly MetadataDeclarationKind[]; /** Whether a bare tag without a qualifier is supported. Defaults to true. */ readonly allowBare?: boolean | undefined; /** Supported qualifiers for this slot. */ readonly qualifiers?: readonly MetadataQualifierRegistration[] | undefined; /** Optional inference hook for the bare/default slot value. */ readonly inferValue?: MetadataSlotInferenceFn | undefined; /** * Optional applicability hook for declaration-specific rules beyond * declaration kind. `buildContext` may carry compiler objects. */ readonly isApplicable?: ((context: MetadataInferenceContext) => boolean) | undefined; } /** * Scalar metadata disabled unless provided explicitly elsewhere. * * @public */ declare interface MetadataValueDisabledPolicyInput { /** Disables inference for this scalar metadata value. */ readonly mode?: "disabled" | undefined; /** Optional policy controlling plural forms of this scalar value. */ readonly pluralization?: MetadataPluralizationPolicyInput | undefined; } /** * Scalar metadata may be inferred when missing. * * @public */ declare interface MetadataValueInferIfMissingPolicyInput { /** Infers this scalar metadata value when it is not authored explicitly. */ readonly mode: "infer-if-missing"; /** Callback used to infer the missing singular value. */ readonly infer: MetadataInferenceFn; /** Optional policy controlling plural forms of this scalar value. */ readonly pluralization?: MetadataPluralizationPolicyInput | undefined; } /** * Scalar metadata policy input. * * @public */ declare type MetadataValuePolicyInput = MetadataValueDisabledPolicyInput | MetadataValueRequireExplicitPolicyInput | MetadataValueInferIfMissingPolicyInput; /** * Scalar metadata must be authored explicitly. * * @public */ declare interface MetadataValueRequireExplicitPolicyInput { /** Requires this scalar metadata value to be authored directly. */ readonly mode: "require-explicit"; /** Optional policy controlling plural forms of this scalar value. */ readonly pluralization?: MetadataPluralizationPolicyInput | undefined; } /** * A numeric input field. * * @typeParam N - The field name (string literal type) * * @public */ export declare interface NumberField { /** Type discriminator for form elements */ readonly _type: "field"; /** Field type discriminator - identifies this as a number field */ readonly _field: "number"; /** Unique field identifier used as the schema key */ readonly name: N; /** Display label for the field */ readonly label?: string; /** Canonical display name for the field. Alias of `label` in the chain DSL. */ readonly displayName?: string; /** JSON-facing serialized name for the field. */ readonly apiName?: string; /** Minimum allowed value */ readonly min?: number; /** Maximum allowed value */ readonly max?: number; /** Whether this field is required for form submission */ readonly required?: boolean; /** Value must be a multiple of this number (use 1 for integer semantics) */ readonly multipleOf?: number; } /** * An object field containing nested properties. * * Use this for grouping related fields under a single key in the schema. * * @typeParam N - The field name (string literal type) * @typeParam Properties - The form elements that define the object's properties * * @public */ export declare interface ObjectField { /** Type discriminator for form elements */ readonly _type: "field"; /** Field type discriminator - identifies this as an object field */ readonly _field: "object"; /** Unique field identifier used as the schema key */ readonly name: N; /** Form elements that define the properties of this object */ readonly properties: Properties; /** Display label for the field */ readonly label?: string; /** Canonical display name for the field. Alias of `label` in the chain DSL. */ readonly displayName?: string; /** JSON-facing serialized name for the field. */ readonly apiName?: string; /** Whether this field is required for form submission */ readonly required?: boolean; } /** * Resolves the effective config for a specific file by matching against * the `packages` glob map and merging with root-level settings. * * Package overrides are evaluated in declaration order — the first matching * pattern wins. Declare more specific patterns before broader ones. * * @param config - The root FormSpecConfig * @param filePath - Absolute or relative path to the file being processed * @param configDir - Directory containing the config file (for relative path resolution) * @returns Fully resolved config with defaults applied * * @public */ export declare function resolveConfigForFile(config: FormSpecConfig, filePath: string, configDir: string): ResolvedFormSpecConfig; /** * Fully resolved constraint configuration with all properties required. * This is the type returned by mergeWithDefaults(). * * @public */ export declare interface ResolvedConstraintConfig { /** Effective field-builder policy after defaults and overrides are merged. */ fieldTypes: Required; /** Effective nesting and grouping policy after defaults are applied. */ layout: Required; /** Effective UI-schema policy after defaults and overrides are merged. */ uiSchema: ResolvedUISchemaConstraints; /** Effective policy for field-level builder options. */ fieldOptions: Required; /** Effective policy for renderer-specific control options. */ controlOptions: Required; } /** * Resolved configuration for a specific file, with all defaults applied * and per-package overrides merged. * * @public */ export declare interface ResolvedFormSpecConfig { /** Resolved extensions (empty array if none configured). */ readonly extensions: readonly ExtensionDefinition[]; /** Resolved constraint config with all defaults filled in. */ readonly constraints: ResolvedConstraintConfig; /** Resolved metadata policy, or undefined for FormSpec built-in. */ readonly metadata: MetadataPolicyInput | undefined; /** Resolved vendor prefix. */ readonly vendorPrefix: string; /** Resolved enum serialization mode. */ readonly enumSerialization: "enum" | "oneOf" | "smart-size"; } /** * Fully resolved rule constraints with all properties required. * * @public */ export declare interface ResolvedRuleConstraints { /** Effective severity controlling whether UI schema rules are allowed. */ enabled: Severity; /** Effective severity for each supported JSON Forms rule effect. */ effects: Required; } /** * Fully resolved UI schema constraints with all properties required. * * @public */ export declare interface ResolvedUISchemaConstraints { /** Effective severities for each supported JSON Forms layout type. */ layouts: Required; /** Effective rule policy after defaults have been applied. */ rules: ResolvedRuleConstraints; } /** * JSONForms rule constraints. * * @public */ export declare interface RuleConstraints { /** Whether rules are enabled at all */ enabled?: Severity; /** Fine-grained control over rule effects */ effects?: RuleEffectConstraints; } /** * JSONForms rule effect constraints. * * @public */ export declare interface RuleEffectConstraints { /** SHOW - show element when condition is true */ SHOW?: Severity; /** HIDE - hide element when condition is true */ HIDE?: Severity; /** ENABLE - enable element when condition is true */ ENABLE?: Severity; /** DISABLE - disable element when condition is true */ DISABLE?: Severity; } /** * Severity level for constraint violations. * - "error": Violation fails validation * - "warn": Violation emits warning but passes * - "off": Feature is allowed (no violation) * * @public */ export declare type Severity = "error" | "warn" | "off"; /** * A field with static enum options (known at compile time). * * Options can be plain strings or objects with `id` and `label` properties. * * @typeParam N - The field name (string literal type) * @typeParam O - Tuple of option values (strings or EnumOption objects) * * @public */ export declare interface StaticEnumField { /** Type discriminator for form elements */ readonly _type: "field"; /** Field type discriminator - identifies this as an enum field */ readonly _field: "enum"; /** Unique field identifier used as the schema key */ readonly name: N; /** Array of allowed option values */ readonly options: O; /** Display label for the field */ readonly label?: string; /** Canonical display name for the field. Alias of `label` in the chain DSL. */ readonly displayName?: string; /** JSON-facing serialized name for the field. */ readonly apiName?: string; /** Whether this field is required for form submission */ readonly required?: boolean; } /** * Form element type definitions. * * These types define the structure of form specifications. * The structure IS the definition - nesting implies layout and conditional logic. */ /** * A text input field. * * @typeParam N - The field name (string literal type) * * @public */ export declare interface TextField { /** Type discriminator for form elements */ readonly _type: "field"; /** Field type discriminator - identifies this as a text field */ readonly _field: "text"; /** Unique field identifier used as the schema key */ readonly name: N; /** Display label for the field */ readonly label?: string; /** Canonical display name for the field. Alias of `label` in the chain DSL. */ readonly displayName?: string; /** JSON-facing serialized name for the field. */ readonly apiName?: string; /** Placeholder text shown when field is empty */ readonly placeholder?: string; /** Whether this field is required for form submission */ readonly required?: boolean; /** Minimum string length */ readonly minLength?: number; /** Maximum string length */ readonly maxLength?: number; /** Regular expression pattern the value must match */ readonly pattern?: string; } /** * UI Schema feature constraints - control JSONForms-specific features. * * @public */ export declare interface UISchemaConstraints { /** Layout type constraints */ layouts?: LayoutTypeConstraints; /** Rule (conditional) constraints */ rules?: RuleConstraints; } /** * Validates field options against constraints. * * @param context - Information about the field and its options * @param constraints - Field option constraints * @returns Array of validation issues (empty if valid) * * @beta */ export declare function validateFieldOptions(context: FieldOptionsContext, constraints: FieldOptionConstraints): ValidationIssue[]; /** * Validates a field type against constraints. * * @param context - Information about the field being validated * @param constraints - Field type constraints * @returns Array of validation issues (empty if valid) * * @beta */ export declare function validateFieldTypes(context: FieldTypeContext, constraints: FieldTypeConstraints): ValidationIssue[]; /** * Validates a complete FormSpec against constraints. * * @param formSpec - The FormSpec to validate * @param options - Validation options including constraints * @returns Validation result with all issues found * * @public */ export declare function validateFormSpec(formSpec: FormSpec, options?: FormSpecValidationOptions): ValidationResult; /** * Validates FormSpec elements against constraints. * * This is the main entry point for validating a form specification * against a constraint configuration. It walks through all elements * and checks each one against the configured constraints. * * @param elements - FormSpec elements to validate * @param options - Validation options including constraints * @returns Validation result with all issues found * * @example * ```ts * import { formspec, field, group } from '@formspec/dsl'; * import { validateFormSpecElements, defineConstraints } from '@formspec/config'; * * const form = formspec( * group("Contact", * field.text("name"), * field.dynamicEnum("country", "countries"), * ), * ); * * const result = validateFormSpecElements(form.elements, { * constraints: { * fieldTypes: { dynamicEnum: 'error' }, * layout: { group: 'error' }, * }, * }); * * if (!result.valid) { * console.error('Validation failed:', result.issues); * } * ``` * * @public */ export declare function validateFormSpecElements(elements: readonly FormElement[], options?: FormSpecValidationOptions): ValidationResult; /** * Validates a layout element against constraints. * * @param context - Information about the layout element * @param constraints - Layout constraints * @returns Array of validation issues (empty if valid) * * @beta */ export declare function validateLayout(context: LayoutContext, constraints: LayoutConstraints): ValidationIssue[]; /** * A single validation issue found during constraint checking. * * @public */ export declare interface ValidationIssue { /** Unique code identifying the issue type */ code: string; /** Human-readable description of the issue */ message: string; /** Severity level of this issue */ severity: "error" | "warning"; /** Which constraint category this issue belongs to */ category: "fieldTypes" | "layout" | "uiSchema" | "fieldOptions" | "controlOptions"; /** JSON pointer or field path to the issue location */ path?: string; /** Name of the affected field (if applicable) */ fieldName?: string; /** Type of the affected field (if applicable) */ fieldType?: string; } /** * Result of validating a FormSpec or schema against constraints. * * @public */ export declare interface ValidationResult { /** Whether validation passed (no errors, warnings OK) */ valid: boolean; /** List of all issues found */ issues: ValidationIssue[]; } /** * Registration for a vocabulary keyword to include in a JSON Schema `$vocabulary` declaration. * * @public */ declare interface VocabularyKeywordRegistration { /** The keyword name (without vendor prefix). */ readonly keyword: string; /** JSON Schema that describes the valid values for this keyword. */ readonly schema: ExtensionPayloadValue; } export { }