@bluprynt/forms-core 2.0.0 → 4.0.1
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.cjs +281 -9
- package/dist/index.d.cts +15 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +15 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +281 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["formDefinitionSchema"],"sources":["../src/date-utils.ts","../src/condition-evaluator.ts","../src/dependency-graph.ts","../src/validators/array-validator.ts","../src/validators/boolean-validator.ts","../src/validators/date-validator.ts","../src/validators/file-validator.ts","../src/validators/number-validator.ts","../src/validators/select-validator.ts","../src/validators/string-validator.ts","../src/field-validator.ts","../src/form-definition-editor.ts","../src/form-definition.schema.json","../src/form-definition-validator.ts","../src/types/errors.ts","../src/visibility-resolver.ts","../src/form-engine.ts","../src/form-values-editor.ts"],"sourcesContent":["const RELATIVE_DATE_RE = /^([+-])(\\d+)([dwmy])$/\n\n/**\n * Type guard that checks whether a value is a relative date expression.\n *\n * Relative date expressions follow the pattern `[+-]<amount><unit>` where\n * `unit` is one of `d` (days), `w` (weeks), `m` (months), or `y` (years).\n *\n * @param value - The value to test.\n * @returns `true` if `value` is a string matching the relative date pattern.\n *\n * @example\n * ```ts\n * isRelativeDate(\"+7d\") // true (7 days from now)\n * isRelativeDate(\"-1m\") // true (1 month ago)\n * isRelativeDate(\"2024-01-01\") // false (absolute date)\n * isRelativeDate(42) // false (not a string)\n * ```\n */\nexport const isRelativeDate = (value: unknown): value is string =>\n typeof value === 'string' && RELATIVE_DATE_RE.test(value)\n\n/**\n * Resolves a relative date expression into an absolute ISO-8601 date string.\n *\n * Supported units:\n * - `d` -- days\n * - `w` -- weeks (7 days)\n * - `m` -- months\n * - `y` -- years\n *\n * Arithmetic is performed in UTC. If the input does not match the relative\n * date pattern, it is returned unchanged.\n *\n * @param relative - A relative date expression (e.g. `\"+7d\"`, `\"-3m\"`).\n * @param now - Reference date for the calculation. Defaults to `new Date()`.\n * @returns An ISO-8601 date-time string, or the original string if it is not\n * a valid relative expression.\n *\n * @example\n * ```ts\n * const base = new Date(\"2024-06-15T00:00:00Z\");\n * resolveRelativeDate(\"+7d\", base) // \"2024-06-22T00:00:00.000Z\"\n * resolveRelativeDate(\"-1m\", base) // \"2024-05-15T00:00:00.000Z\"\n * resolveRelativeDate(\"+1y\", base) // \"2025-06-15T00:00:00.000Z\"\n * ```\n */\nexport const resolveRelativeDate = (relative: string, now: Date): string => {\n const match = RELATIVE_DATE_RE.exec(relative)\n if (!match) return relative\n\n const sign = match[1] === '+' ? 1 : -1\n const amount = Number(match[2]) * sign\n const unit = match[3]\n\n const result = new Date(now.getTime())\n\n switch (unit) {\n case 'd':\n result.setUTCDate(result.getUTCDate() + amount)\n break\n case 'w':\n result.setUTCDate(result.getUTCDate() + amount * 7)\n break\n case 'm':\n result.setUTCMonth(result.getUTCMonth() + amount)\n break\n case 'y':\n result.setUTCFullYear(result.getUTCFullYear() + amount)\n break\n }\n\n return result.toISOString()\n}\n","import { isRelativeDate, resolveRelativeDate } from './date-utils'\nimport type { Condition, SimpleCondition } from './types/conditions'\n\n/**\n * Context passed to condition evaluation methods.\n *\n * @property values - Current form values keyed by stringified field id.\n * @property visibilityMap - Pre-computed visibility map. When provided, a\n * reference to a hidden field is treated as \"not set\" regardless of its\n * actual value.\n * @property now - Reference date for resolving relative date expressions in\n * condition values.\n */\nexport type EvaluationContext = {\n values: Record<string, unknown>\n visibilityMap?: Map<number, boolean>\n now: Date\n}\n\n/**\n * Evaluates condition trees against form state.\n *\n * Supports three kinds of conditions:\n * - **Simple** ({@link SimpleCondition}): compares a single field's value\n * using one of the supported operators (`set`, `notset`, `eq`, `ne`, `lt`,\n * `gt`, `lte`, `gte`, `in`, `notin`).\n * - **Compound AND**: `{ and: [...] }` -- all child conditions must be true.\n * - **Compound OR**: `{ or: [...] }` -- at least one child condition must be true.\n *\n * **Hidden-field rule**: when a `visibilityMap` is provided and the\n * referenced field is hidden (`false`), the condition evaluates as if the\n * field has no value. This means `notset` returns `true` and all other\n * operators return `false`.\n *\n * **Date handling**: condition values that are relative date expressions\n * (e.g. `\"+7d\"`) are resolved against `ctx.now` before comparison.\n */\nexport class ConditionEvaluator {\n /**\n * Evaluates a condition tree against the current form state.\n *\n * @param condition - The condition to evaluate (simple or compound).\n * @param ctx - Evaluation context containing form values and optional\n * visibility/date overrides.\n * @returns `true` if the condition is satisfied, `false` otherwise.\n */\n evalCondition(condition: Condition, ctx: EvaluationContext): boolean {\n if ('and' in condition) return condition.and.every((c) => this.evalCondition(c, ctx))\n if ('or' in condition) return condition.or.some((c) => this.evalCondition(c, ctx))\n return this.evalSimple(condition as SimpleCondition, ctx)\n }\n\n private evalSimple(cond: SimpleCondition, ctx: EvaluationContext): boolean {\n // Hidden-field rule: if the referenced field is hidden, treat as not set\n if (ctx.visibilityMap && ctx.visibilityMap.get(cond.field) === false) return cond.op === 'notset'\n\n const fieldValue = ctx.values[String(cond.field)]\n\n switch (cond.op) {\n case 'set':\n return fieldValue !== null && fieldValue !== undefined && fieldValue !== ''\n case 'notset':\n return fieldValue === null || fieldValue === undefined || fieldValue === ''\n case 'eq':\n return fieldValue === this.resolveIfDate(cond.value, ctx.now)\n case 'ne':\n return fieldValue !== this.resolveIfDate(cond.value, ctx.now)\n case 'lt':\n return this.compareTo(fieldValue, cond.value, ctx.now) < 0\n case 'gt':\n return this.compareTo(fieldValue, cond.value, ctx.now) > 0\n case 'lte':\n return this.compareTo(fieldValue, cond.value, ctx.now) <= 0\n case 'gte':\n return this.compareTo(fieldValue, cond.value, ctx.now) >= 0\n case 'in':\n return Array.isArray(cond.value) && cond.value.includes(fieldValue)\n case 'notin':\n return Array.isArray(cond.value) && !cond.value.includes(fieldValue)\n default:\n return false\n }\n }\n\n private resolveIfDate(value: unknown, now: Date): unknown {\n if (isRelativeDate(value)) return resolveRelativeDate(value, now)\n return value\n }\n\n private compareTo(a: unknown, b: unknown, now: Date): number {\n const resolvedB = this.resolveIfDate(b, now)\n\n if (typeof a === 'number' && typeof resolvedB === 'number') return a - resolvedB\n\n // Date comparison: both must be parseable date strings\n if (typeof a === 'string' && typeof resolvedB === 'string') {\n const ta = Date.parse(a)\n const tb = Date.parse(resolvedB)\n if (!Number.isNaN(ta) && !Number.isNaN(tb)) {\n return ta - tb\n }\n // Fall back to lexicographic for non-date strings\n if (a < resolvedB) return -1\n if (a > resolvedB) return 1\n return 0\n }\n\n return Number.NaN\n }\n}\n","import type { Condition } from './types/conditions'\nimport type { FieldEntry } from './types/field-entry'\n\nconst DfsVisitState = { Unvisited: 0, InProgress: 1, Completed: 2 } as const\ntype DfsVisitState = (typeof DfsVisitState)[keyof typeof DfsVisitState]\n\n/**\n * Manages the condition dependency graph for form fields.\n *\n * Built from the field registry during engine preparation. Provides:\n * - Forward dependency graph (`graph`): answers \"if field X changes, which\n * items need to re-evaluate their visibility?\"\n * - Topological ordering (`topologicalOrder`): guarantees that when computing\n * visibility, every item is evaluated after the fields it depends on.\n * - Affected-ids lookup (`getAffectedIds`): returns all transitively\n * affected item ids when a field value changes (lazily cached).\n *\n * Static methods (`extractFieldRefs`, `detectCycle`) can be used before\n * constructing an instance, e.g. during semantic validation.\n */\nexport class DependencyGraph {\n /**\n * Forward adjacencyMap map: key is a field id, value is the set of item ids\n * whose conditions reference that field.\n */\n readonly graph: Map<number, Set<number>>\n\n /**\n * Item ids in topological order. Dependencies come before dependents.\n */\n readonly topologicalOrder: number[]\n\n private readonly registry: Map<number, FieldEntry>\n private readonly affectedCache = new Map<number, Set<number>>()\n\n /**\n * @param registry - The engine's field registry (built during preparation).\n */\n constructor(registry: Map<number, FieldEntry>) {\n this.registry = registry\n this.graph = this.buildGraph()\n this.topologicalOrder = this.buildTopologicalOrder()\n }\n\n /**\n * Extracts the set of field ids referenced by a condition tree.\n *\n * Recursively walks compound conditions (`and`/`or`) and collects the\n * `field` property from every leaf {@link SimpleCondition}.\n *\n * @param condition - A simple or compound condition.\n * @returns Set of all unique field ids that appear in the condition.\n */\n static extractFieldRefs(condition: Condition): Set<number> {\n const refs = new Set<number>()\n DependencyGraph.collectRefs(condition, refs)\n return refs\n }\n\n /**\n * Detects circular dependencies in the condition graph.\n *\n * Uses DFS-based cycle detection (white/gray/black coloring). If a cycle\n * is found, the function reconstructs and returns a human-readable path\n * string (e.g. `\"1 -> 2 -> 3 -> 1\"`).\n *\n * @param registry - The engine's field registry.\n * @returns An array of field ids forming the cycle, or `undefined` if no cycle exists.\n */\n static detectCycle(registry: Map<number, FieldEntry>): number[] | undefined {\n const allIds = new Set(registry.keys())\n const adjacencyMap = new Map<number, Set<number>>()\n\n for (const [id, entry] of registry) {\n if (!entry.condition) continue\n\n const refs = DependencyGraph.extractFieldRefs(entry.condition)\n\n for (const ref of refs) {\n if (!allIds.has(ref)) continue\n\n let targets = adjacencyMap.get(ref)\n if (!targets) {\n targets = new Set()\n adjacencyMap.set(ref, targets)\n }\n\n targets.add(id)\n }\n }\n\n // DFS-based cycle detection\n\n const nodes = new Map<number, DfsVisitState>()\n const parent = new Map<number, number>()\n\n for (const id of allIds) {\n nodes.set(id, DfsVisitState.Unvisited)\n }\n\n for (const id of allIds) {\n if (nodes.get(id) === DfsVisitState.Unvisited) {\n const cyclePath = DependencyGraph.dfs(id, adjacencyMap, nodes, parent, allIds)\n if (cyclePath) return cyclePath\n }\n }\n\n return undefined\n }\n\n /**\n * Returns the set of item ids whose visibility could change when the given\n * field's value changes.\n *\n * Performs a transitive expansion of the forward dependency graph starting\n * from the field's direct dependents. Results are memoized for subsequent\n * calls with the same `fieldId`.\n *\n * @param fieldId - Id of the field whose value changed.\n * @returns Set of all transitively affected item ids. Empty set if no items\n * depend on `fieldId`.\n */\n getAffectedIds(fieldId: number): Set<number> {\n const cached = this.affectedCache.get(fieldId)\n if (cached) return cached\n\n const directDeps = this.graph.get(fieldId)\n if (!directDeps || directDeps.size === 0) {\n const empty = new Set<number>()\n this.affectedCache.set(fieldId, empty)\n return empty\n }\n\n const expanded = this.expandTransitiveDependencies(directDeps)\n this.affectedCache.set(fieldId, expanded)\n\n return expanded\n }\n\n private static collectRefs(condition: Condition, refs: Set<number>): void {\n if ('and' in condition) {\n for (const c of condition.and) DependencyGraph.collectRefs(c, refs)\n } else if ('or' in condition) {\n for (const c of condition.or) DependencyGraph.collectRefs(c, refs)\n } else {\n refs.add(condition.field)\n }\n }\n\n private static dfs(\n node: number,\n adjacencyMap: Map<number, Set<number>>,\n nodes: Map<number, DfsVisitState>,\n parent: Map<number, number>,\n allIds: Set<number>,\n ): number[] | undefined {\n nodes.set(node, DfsVisitState.InProgress)\n\n const neighbors = adjacencyMap.get(node)\n if (neighbors) {\n for (const next of neighbors) {\n if (!allIds.has(next)) continue\n\n if (nodes.get(next) === DfsVisitState.InProgress)\n return DependencyGraph.reconstructCycle(next, node, parent)\n\n if (nodes.get(next) === DfsVisitState.Completed) continue\n\n parent.set(next, node)\n\n const cycle = DependencyGraph.dfs(next, adjacencyMap, nodes, parent, allIds)\n if (cycle) return cycle\n }\n }\n\n nodes.set(node, DfsVisitState.Completed)\n return undefined\n }\n\n private static reconstructCycle(cycleStart: number, cycleEnd: number, parent: Map<number, number>): number[] {\n const path: number[] = [cycleStart]\n\n let current = cycleEnd\n while (current !== cycleStart) {\n path.push(current)\n\n const next = parent.get(current)\n if (next === undefined) break\n\n current = next\n }\n\n path.push(cycleStart)\n\n return path.reverse()\n }\n\n /**\n * Builds the forward dependency graph from the registry.\n */\n private buildGraph(): Map<number, Set<number>> {\n const graph = new Map<number, Set<number>>()\n\n for (const [id, entry] of this.registry) {\n if (!entry.condition) continue\n\n const refs = DependencyGraph.extractFieldRefs(entry.condition)\n\n for (const ref of refs) {\n let deps = graph.get(ref)\n if (!deps) {\n deps = new Set()\n graph.set(ref, deps)\n }\n\n deps.add(id)\n }\n }\n\n return graph\n }\n\n /**\n * Produces a topological ordering using Kahn's algorithm.\n */\n private buildTopologicalOrder(): number[] {\n const allIds = new Set(this.registry.keys())\n\n const inDegree = new Map<number, number>()\n const adjacencyMap = new Map<number, Set<number>>()\n\n for (const id of allIds) {\n inDegree.set(id, 0)\n }\n\n for (const [id, entry] of this.registry) {\n if (!entry.condition) continue\n\n const refs = DependencyGraph.extractFieldRefs(entry.condition)\n for (const ref of refs) {\n if (!allIds.has(ref)) continue\n\n let targets = adjacencyMap.get(ref)\n if (!targets) {\n targets = new Set()\n adjacencyMap.set(ref, targets)\n }\n if (!targets.has(id)) {\n targets.add(id)\n inDegree.set(id, (inDegree.get(id) ?? 0) + 1)\n }\n }\n }\n\n // Parent → child edges ensure parents are evaluated before children\n // in getVisibilityMap, so cascading visibility works correctly.\n for (const [id, entry] of this.registry) {\n if (entry.parentId === undefined) continue\n if (!allIds.has(entry.parentId)) continue\n\n let targets = adjacencyMap.get(entry.parentId)\n if (!targets) {\n targets = new Set()\n adjacencyMap.set(entry.parentId, targets)\n }\n if (!targets.has(id)) {\n targets.add(id)\n inDegree.set(id, (inDegree.get(id) ?? 0) + 1)\n }\n }\n\n // Kahn's algorithm\n const queue: number[] = []\n for (const [id, deg] of inDegree) {\n if (deg === 0) queue.push(id)\n }\n\n const sorted: number[] = []\n\n while (queue.length > 0) {\n const current = queue.shift()\n if (current === undefined) break\n sorted.push(current)\n\n const targets = adjacencyMap.get(current)\n if (targets) {\n for (const target of targets) {\n const newDeg = (inDegree.get(target) ?? 1) - 1\n inDegree.set(target, newDeg)\n if (newDeg === 0) {\n queue.push(target)\n }\n }\n }\n }\n\n if (sorted.length !== allIds.size) {\n const inCycle = [...allIds].filter((id) => !sorted.includes(id))\n const cyclePath = inCycle.join(' -> ')\n return sorted.length === 0\n ? []\n : (() => {\n throw cyclePath\n })()\n }\n\n return sorted\n }\n\n /**\n * Expands a set of item ids to include all transitive dependents via BFS.\n */\n private expandTransitiveDependencies(startIds: Set<number>): Set<number> {\n const result = new Set<number>()\n const queue = [...startIds]\n\n while (queue.length > 0) {\n const id = queue.shift()\n\n if (id === undefined) break\n if (result.has(id)) continue\n\n result.add(id)\n\n const deps = this.graph.get(id)\n if (deps) {\n for (const dep of deps) {\n if (!result.has(dep)) queue.push(dep)\n }\n }\n }\n\n return result\n }\n}\n","import type { ArrayItemDef } from '../types/array-item-def'\nimport type { FieldEntry } from '../types/field-entry'\nimport type { ArrayValidation } from '../types/validation/array'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport type ArrayValidatorContext = ValidatorContext & {\n item?: ArrayItemDef\n validateField: (fieldId: number, value: unknown, entry: FieldEntry, now: Date) => FieldValidationError[]\n}\n\nexport class ArrayValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value, now } = ctx\n const { item, validateField } = ctx as ArrayValidatorContext\n const validation = ctx.validation as ArrayValidation | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined\n\n if (isEmpty) return errors\n\n if (!Array.isArray(value)) {\n errors.push({ fieldId, rule: 'TYPE', message: 'Must be an array', params: { expectedType: 'array' } })\n return errors\n }\n\n if (validation?.minItems !== undefined && value.length < validation.minItems) {\n errors.push({\n fieldId,\n rule: 'MIN_ITEMS',\n message: `Must have at least ${validation.minItems} items`,\n params: { minItems: validation.minItems, actual: value.length },\n })\n }\n\n if (validation?.maxItems !== undefined && value.length > validation.maxItems) {\n errors.push({\n fieldId,\n rule: 'MAX_ITEMS',\n message: `Must have at most ${validation.maxItems} items`,\n params: { maxItems: validation.maxItems, actual: value.length },\n })\n }\n\n if (item) {\n for (let i = 0; i < value.length; i++) {\n const fakeEntry: FieldEntry = {\n id: fieldId,\n type: item.type,\n condition: undefined,\n validation: item.validation as FieldEntry['validation'],\n parentId: undefined,\n options: item.options,\n item: undefined,\n label: item.label,\n title: undefined,\n }\n\n const itemErrors = validateField(fieldId, value[i], fakeEntry, now)\n errors.push(...itemErrors.map((err) => ({ ...err, itemIndex: i })))\n }\n }\n\n return errors\n }\n}\n","import type { BooleanValidation } from '../types/validation/boolean'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class BooleanValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value } = ctx\n const validation = ctx.validation as BooleanValidation | undefined\n const errors: FieldValidationError[] = []\n\n if (validation?.required && value !== true && value !== false) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n }\n\n return errors\n }\n}\n","import { isRelativeDate, resolveRelativeDate } from '../date-utils'\nimport type { DateValidation } from '../types/validation/date'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class DateValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value, now } = ctx\n const validation = ctx.validation as DateValidation | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined || value === ''\n\n if (validation?.required && isEmpty) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n return errors\n }\n\n if (isEmpty) return errors\n\n if (typeof value !== 'string') {\n errors.push({ fieldId, rule: 'TYPE', message: 'Must be a valid date', params: { expectedType: 'date' } })\n return errors\n }\n\n const timestamp = Date.parse(value)\n if (Number.isNaN(timestamp)) {\n errors.push({ fieldId, rule: 'INVALID_DATE', message: 'Must be a valid date' })\n return errors\n }\n\n if (validation?.minDate !== undefined) {\n const minResolved = isRelativeDate(validation.minDate)\n ? resolveRelativeDate(validation.minDate, now)\n : validation.minDate\n if (timestamp < Date.parse(minResolved)) {\n errors.push({\n fieldId,\n rule: 'MIN_DATE',\n message: `Must be on or after ${minResolved}`,\n params: { minDate: minResolved },\n })\n }\n }\n\n if (validation?.maxDate !== undefined) {\n const maxResolved = isRelativeDate(validation.maxDate)\n ? resolveRelativeDate(validation.maxDate, now)\n : validation.maxDate\n if (timestamp > Date.parse(maxResolved)) {\n errors.push({\n fieldId,\n rule: 'MAX_DATE',\n message: `Must be on or before ${maxResolved}`,\n params: { maxDate: maxResolved },\n })\n }\n }\n\n return errors\n }\n}\n","import type { FileValidation } from '../types/validation/file'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class FileValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value } = ctx\n const validation = ctx.validation as FileValidation | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined\n\n if (validation?.required && isEmpty) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n return errors\n }\n\n if (isEmpty) return errors\n\n if (\n typeof value !== 'object' ||\n typeof (value as Record<string, unknown>).name !== 'string' ||\n typeof (value as Record<string, unknown>).mimeType !== 'string' ||\n typeof (value as Record<string, unknown>).size !== 'number' ||\n typeof (value as Record<string, unknown>).url !== 'string'\n ) {\n errors.push({\n fieldId,\n rule: 'TYPE',\n message: 'Must be a valid file object',\n params: { expectedType: 'file' },\n })\n }\n\n return errors\n }\n}\n","import type { NumberValidation } from '../types/validation/number'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class NumberValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value } = ctx\n const validation = ctx.validation as NumberValidation | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined\n\n if (validation?.required && isEmpty) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n return errors\n }\n\n if (isEmpty) return errors\n\n if (typeof value !== 'number') {\n errors.push({ fieldId, rule: 'TYPE', message: 'Must be a number', params: { expectedType: 'number' } })\n return errors\n }\n\n if (validation?.min !== undefined && value < validation.min) {\n errors.push({\n fieldId,\n rule: 'MIN',\n message: `Must be at least ${validation.min}`,\n params: { min: validation.min, actual: value },\n })\n }\n\n if (validation?.max !== undefined && value > validation.max) {\n errors.push({\n fieldId,\n rule: 'MAX',\n message: `Must be at most ${validation.max}`,\n params: { max: validation.max, actual: value },\n })\n }\n\n return errors\n }\n}\n","import type { SelectOption } from '../types/select-option'\nimport type { SelectValidation } from '../types/validation/select'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class SelectValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value } = ctx\n const validation = ctx.validation as SelectValidation | undefined\n const options = ctx.options as SelectOption[] | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined\n\n if (validation?.required && isEmpty) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n return errors\n }\n\n if (isEmpty) return errors\n\n if (options && !options.some((opt) => opt.value === value)) {\n errors.push({ fieldId, rule: 'INVALID_OPTION', message: 'Value is not a valid option' })\n }\n\n return errors\n }\n}\n","import type { StringValidation } from '../types/validation/string'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class StringValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value } = ctx\n const validation = ctx.validation as StringValidation | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined || value === ''\n\n if (validation?.required && isEmpty) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n return errors\n }\n\n if (isEmpty) return errors\n\n if (typeof value !== 'string') {\n errors.push({ fieldId, rule: 'TYPE', message: 'Must be a string', params: { expectedType: 'string' } })\n return errors\n }\n\n if (validation?.minLength !== undefined && value.length < validation.minLength) {\n errors.push({\n fieldId,\n rule: 'MIN_LENGTH',\n message: `Must be at least ${validation.minLength} characters`,\n params: { minLength: validation.minLength, actual: value.length },\n })\n }\n\n if (validation?.maxLength !== undefined && value.length > validation.maxLength) {\n errors.push({\n fieldId,\n rule: 'MAX_LENGTH',\n message: `Must be at most ${validation.maxLength} characters`,\n params: { maxLength: validation.maxLength, actual: value.length },\n })\n }\n\n if (validation?.pattern !== undefined) {\n const re = new RegExp(validation.pattern)\n if (!re.test(value)) {\n errors.push({\n fieldId,\n rule: 'PATTERN',\n message: validation.patternMessage ?? 'Value does not match the required pattern',\n })\n }\n }\n\n return errors\n }\n}\n","import type { FieldEntry } from './types/field-entry'\nimport type { FormValues } from './types/form-values'\nimport type { FieldValidationError, FormValidationResult } from './types/validation-results'\nimport type { ArrayValidatorContext } from './validators/array-validator'\nimport { ArrayValidator } from './validators/array-validator'\nimport { BooleanValidator } from './validators/boolean-validator'\nimport { DateValidator } from './validators/date-validator'\nimport { FileValidator } from './validators/file-validator'\nimport { NumberValidator } from './validators/number-validator'\nimport { SelectValidator } from './validators/select-validator'\nimport { StringValidator } from './validators/string-validator'\nimport type { TypeValidator } from './validators/type-validator'\n\n/**\n * Validates form values against the schema's validation rules.\n *\n * **Which fields are validated:**\n * - Only fields (not sections) are validated.\n * - Hidden fields (those with `visibilityMap.get(id) === false`) are skipped\n * entirely -- they produce no errors regardless of their value.\n *\n * **How each field type is validated:**\n * - `string` -- `required`, `minLength`, `maxLength`, `pattern`.\n * - `number` -- `required`, `min`, `max`.\n * - `boolean` -- `required` (must be explicitly `true` or `false`).\n * - `date` -- `required`, `minDate`, `maxDate`. Relative date boundaries\n * are resolved against `now`.\n * - `select` -- `required`, plus the value must be one of the defined options.\n * - `array` -- `minItems`, `maxItems`, plus each item is validated\n * individually according to the array's {@link ArrayItemDef}. Item-level\n * errors carry an `itemIndex`.\n *\n * For all types, if `required` fails, no further rules are checked for that\n * field (early return). If the value is empty/absent and `required` is not\n * set, no errors are produced.\n */\nexport class FieldValidator {\n private readonly registry: Map<number, FieldEntry>\n private readonly validators: Record<string, TypeValidator>\n\n /**\n * @param registry - The engine's field registry.\n */\n constructor(registry: Map<number, FieldEntry>) {\n this.registry = registry\n this.validators = {\n string: new StringValidator(),\n number: new NumberValidator(),\n boolean: new BooleanValidator(),\n date: new DateValidator(),\n select: new SelectValidator(),\n array: new ArrayValidator(),\n file: new FileValidator(),\n }\n }\n\n /**\n * Validates form values against the schema's validation rules.\n *\n * @param values - The form values to validate, keyed by stringified field id.\n * @param visibilityMap - Pre-computed visibility map for all items.\n * @param now - Reference date for resolving relative date expressions.\n * Defaults to `new Date()`.\n * @returns A {@link FormValidationResult} with `valid: true` when no errors\n * exist, or `valid: false` with a populated `fieldErrors` map.\n */\n validate(values: FormValues, visibilityMap: Map<number, boolean>, now: Date = new Date()): FormValidationResult {\n const fieldErrors = new Map<number, FieldValidationError[]>()\n\n for (const [id, entry] of this.registry) {\n if (entry.type === 'section') continue\n if (visibilityMap.get(id) === false) continue\n\n const value = values[String(id)]\n const errors = this.validateField(id, value, entry, now)\n if (errors.length > 0) {\n fieldErrors.set(id, errors)\n }\n }\n\n return { valid: fieldErrors.size === 0, fieldErrors }\n }\n\n private validateField(fieldId: number, value: unknown, entry: FieldEntry, now: Date): FieldValidationError[] {\n const validator = this.validators[entry.type]\n if (!validator) return []\n\n if (entry.type === 'array') {\n const ctx: ArrayValidatorContext = {\n fieldId,\n value,\n validation: entry.validation,\n now,\n item: entry.item,\n validateField: this.validateField.bind(this),\n }\n return validator.validate(ctx)\n }\n\n return validator.validate({\n fieldId,\n value,\n validation: entry.validation,\n now,\n options: entry.options,\n })\n }\n}\n","import type { ArrayItemDef } from './types/array-item-def'\nimport type { Condition } from './types/conditions'\nimport type { ContentItem, FieldContentItem, FormDefinition, SectionContentItem } from './types/form-definition'\nimport type { SelectOption } from './types/select-option'\nimport type { TypeSpecificValidation } from './types/validation/type-specific'\n\n/**\n * Descriptor for a field to be added via the editor.\n * `id` is optional -- when omitted the editor auto-assigns the next available id.\n */\nexport type FieldDescriptor = Omit<FieldContentItem, 'id'> & { id?: number }\n\n/**\n * Descriptor for a section to be added via the editor.\n * `id` is optional -- when omitted the editor auto-assigns the next available id.\n * `content` defaults to an empty array (items are added separately).\n */\nexport type SectionDescriptor = Omit<SectionContentItem, 'id' | 'content'> & {\n id?: number\n content?: ContentItem[]\n}\n\n/**\n * Flat info about a content item returned by listing methods.\n */\nexport type ContentItemInfo = {\n id: number\n type: ContentItem['type']\n label?: string\n title?: string\n parentId: number | undefined\n}\n\n/**\n * Mutable editor for building and modifying a {@link FormDefinition}.\n *\n * Operates directly on the definition tree. All mutating methods return\n * `this` for fluent chaining.\n *\n * @example\n * ```ts\n * const editor = new FormDefinitionEditor({\n * id: 'my-form', version: '1.0.0', title: 'My Form', content: [],\n * })\n * editor\n * .addField({ type: 'string', label: 'Name', validation: { required: true } })\n * .addSection({ type: 'section', title: 'Details' })\n * .addField({ type: 'number', label: 'Age' }, 2) // into section id=2\n *\n * const definition = editor.toJSON()\n * ```\n */\nexport class FormDefinitionEditor {\n private definition: FormDefinition\n\n constructor(definition: FormDefinition) {\n this.definition = JSON.parse(JSON.stringify(definition))\n }\n\n setTitle(title: string): this {\n this.definition.title = title\n return this\n }\n\n setDescription(description: string | undefined): this {\n if (description === undefined) {\n delete this.definition.description\n } else {\n this.definition.description = description\n }\n return this\n }\n\n setVersion(version: string): this {\n this.definition.version = version\n return this\n }\n\n setId(id: string): this {\n this.definition.id = id\n return this\n }\n\n /**\n * Returns the next available numeric id (max existing + 1).\n */\n nextId(): number {\n let max = 0\n this.walkAll(this.definition.content, (item) => {\n if (item.id > max) max = item.id\n })\n return max + 1\n }\n\n /**\n * Adds a field to the form.\n *\n * @param descriptor - Field properties. `id` is auto-assigned if omitted.\n * @param parentId - Section id to add into. `undefined` for top-level.\n * @param index - Position within the parent's content array. Appends if omitted.\n * @returns `this` for chaining.\n * @throws If `parentId` references a non-existent or non-section item, or if the id already exists.\n */\n addField(descriptor: FieldDescriptor, parentId?: number, index?: number): this {\n const id = descriptor.id ?? this.nextId()\n this.assertIdAvailable(id)\n const field: FieldContentItem = { ...descriptor, id }\n this.insertItem(field, parentId, index)\n return this\n }\n\n /**\n * Adds a section to the form.\n *\n * @param descriptor - Section properties. `id` is auto-assigned if omitted.\n * @param parentId - Parent section id. `undefined` for top-level.\n * @param index - Position within the parent's content array. Appends if omitted.\n * @returns `this` for chaining.\n * @throws If `parentId` references a non-existent or non-section item, or if the id already exists.\n */\n addSection(descriptor: SectionDescriptor, parentId?: number, index?: number): this {\n const id = descriptor.id ?? this.nextId()\n this.assertIdAvailable(id)\n const section: SectionContentItem = {\n ...descriptor,\n id,\n content: descriptor.content ?? [],\n }\n this.insertItem(section, parentId, index)\n return this\n }\n\n /**\n * Updates properties of an existing field.\n *\n * Cannot change `id` or `type`. Use {@link removeItem} + {@link addField}\n * to change the type.\n */\n updateField(id: number, updates: Partial<Omit<FieldContentItem, 'id' | 'type'>>): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n if (item.type === 'section') throw new Error(`Item ${id} is a section, not a field`)\n Object.assign(item, updates)\n return this\n }\n\n /**\n * Updates properties of an existing section.\n *\n * Cannot change `id`, `type`, or `content` directly. Use add/remove methods\n * for content manipulation.\n */\n updateSection(id: number, updates: Partial<Omit<SectionContentItem, 'id' | 'type' | 'content'>>): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n if (item.type !== 'section') throw new Error(`Item ${id} is not a section`)\n Object.assign(item, updates)\n return this\n }\n\n /**\n * Removes a field or section (and all its descendants) by id.\n *\n * @returns `this` for chaining.\n * @throws If the id is not found.\n */\n removeItem(id: number): this {\n const removed = this.removeFromContent(this.definition.content, id)\n if (!removed) throw new Error(`Item with id ${id} not found`)\n return this\n }\n\n /**\n * Moves an item to a new parent and/or position.\n *\n * @param id - Id of the item to move.\n * @param targetParentId - Destination section id, or `undefined` for top-level.\n * @param index - Position in the target content array. Appends if omitted.\n */\n moveItem(id: number, targetParentId: number | undefined, index?: number): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n // Prevent moving a section into itself or its own descendant\n if (targetParentId !== undefined && item.type === 'section') {\n if (targetParentId === id) throw new Error('Cannot move a section into itself')\n const descendantIds = this.collectDescendantIds(item as SectionContentItem)\n if (descendantIds.has(targetParentId)) {\n throw new Error('Cannot move a section into its own descendant')\n }\n }\n\n const clone: ContentItem = JSON.parse(JSON.stringify(item))\n this.removeFromContent(this.definition.content, id)\n this.insertItem(clone, targetParentId, index)\n return this\n }\n\n /**\n * Returns a flat list of all content items (fields + sections) with parent info.\n */\n listAll(): ContentItemInfo[] {\n const result: ContentItemInfo[] = []\n this.walkAllWithParent(this.definition.content, undefined, (item, parentId) => {\n result.push({\n id: item.id,\n type: item.type,\n label: item.type !== 'section' ? item.label : undefined,\n title: item.type === 'section' ? item.title : undefined,\n parentId,\n })\n })\n return result\n }\n\n /**\n * Returns a flat list of all fields (excludes sections).\n */\n listFields(): ContentItemInfo[] {\n return this.listAll().filter((i) => i.type !== 'section')\n }\n\n /**\n * Returns a flat list of all sections.\n */\n listSections(): ContentItemInfo[] {\n return this.listAll().filter((i) => i.type === 'section')\n }\n\n /**\n * Returns the content item with the given id, or `undefined` if not found.\n */\n getItem(id: number): ContentItem | undefined {\n return this.findItem(id) ?? undefined\n }\n\n /**\n * Sets or clears the validation rules for a field.\n */\n setValidation(id: number, validation: TypeSpecificValidation | undefined): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (item.type === 'section') throw new Error('Sections do not have validation')\n\n const field = item as FieldContentItem\n\n if (validation === undefined) delete field.validation\n else field.validation = validation\n\n return this\n }\n\n /**\n * Sets or clears the visibility condition for a field or section.\n */\n setCondition(id: number, condition: Condition | undefined): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (condition === undefined) delete item.condition\n else item.condition = condition\n\n return this\n }\n\n /**\n * Sets the select options for a `select` field.\n */\n setOptions(id: number, options: SelectOption[]): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (item.type !== 'select') throw new Error(`Field ${id} is not a select field`)\n\n const field = item as FieldContentItem\n field.options = options\n\n return this\n }\n\n /**\n * Sets the item definition for an `array` field.\n */\n setArrayItem(id: number, itemDef: ArrayItemDef): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (item.type !== 'array') throw new Error(`Field ${id} is not an array field`)\n\n const field = item as FieldContentItem\n field.item = itemDef\n\n return this\n }\n\n /**\n * Sets the label for a field.\n */\n setLabel(id: number, label: string): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (item.type === 'section') throw new Error('Sections use title, not label')\n\n const field = item as FieldContentItem\n field.label = label\n\n return this\n }\n\n /**\n * Sets the description for a field or section.\n */\n setFieldDescription(id: number, description: string | undefined): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (description === undefined) delete item.description\n else item.description = description\n\n return this\n }\n\n /**\n * Returns a deep clone of the current form definition.\n */\n toJSON(): FormDefinition {\n return JSON.parse(JSON.stringify(this.definition))\n }\n\n private findItem(id: number): ContentItem | undefined {\n let found: ContentItem | undefined\n\n this.walkAll(this.definition.content, (item) => {\n if (item.id === id) found = item\n })\n\n return found\n }\n\n private assertIdAvailable(id: number): void {\n if (this.findItem(id)) throw new Error(`Item with id ${id} already exists`)\n }\n\n private insertItem(item: ContentItem, parentId: number | undefined, index?: number): void {\n const target = this.getTargetContent(parentId)\n\n if (index !== undefined && index >= 0 && index < target.length) target.splice(index, 0, item)\n else target.push(item)\n }\n\n private getTargetContent(parentId: number | undefined): ContentItem[] {\n if (parentId === undefined) return this.definition.content\n\n const parent = this.findItem(parentId)\n if (!parent) throw new Error(`Parent section with id ${parentId} not found`)\n\n if (parent.type !== 'section') throw new Error(`Item ${parentId} is not a section`)\n\n const section = parent as SectionContentItem\n return section.content\n }\n\n private removeFromContent(content: ContentItem[], id: number): boolean {\n const idx = content.findIndex((item) => item.id === id)\n if (idx !== -1) {\n content.splice(idx, 1)\n return true\n }\n\n for (const item of content) {\n if (item.type === 'section') {\n if (this.removeFromContent((item as SectionContentItem).content, id)) return true\n }\n }\n\n return false\n }\n\n private walkAll(content: ContentItem[], fn: (item: ContentItem) => void): void {\n for (const item of content) {\n fn(item)\n if (item.type === 'section') {\n this.walkAll((item as SectionContentItem).content, fn)\n }\n }\n }\n\n private walkAllWithParent(\n content: ContentItem[],\n parentId: number | undefined,\n fn: (item: ContentItem, parentId: number | undefined) => void,\n ): void {\n for (const item of content) {\n fn(item, parentId)\n if (item.type === 'section') {\n this.walkAllWithParent((item as SectionContentItem).content, item.id, fn)\n }\n }\n }\n\n private collectDescendantIds(section: SectionContentItem): Set<number> {\n const ids = new Set<number>()\n this.walkAll(section.content, (item) => ids.add(item.id))\n return ids\n }\n}\n","","import Ajv2020 from 'ajv/dist/2020'\n\nimport { isRelativeDate } from './date-utils'\nimport { DependencyGraph } from './dependency-graph'\nimport formDefinitionSchema from './form-definition.schema.json'\nimport type { FieldEntry } from './types/field-entry'\nimport type { ContentItem, FormDefinition } from './types/form-definition'\nimport type { ArrayValidation } from './types/validation/array'\nimport type { DateValidation } from './types/validation/date'\nimport type { NumberValidation } from './types/validation/number'\nimport type { StringValidation } from './types/validation/string'\nimport type { DocumentValidationError } from './types/validation-results'\n\nconst ajv = new Ajv2020({ allErrors: true })\nconst validateFn = ajv.compile(formDefinitionSchema)\n\n/**\n * Validates form definitions at both the structural (JSON Schema) and\n * semantic levels.\n *\n * Used by {@link FormEngine} during construction before building the engine.\n *\n * ### Schema validation (`validateSchema`)\n * Validates raw input against the form definition JSON Schema. Returns\n * `SCHEMA_INVALID` issues for every violation found.\n *\n * ### Semantic validation (`validate`)\n * Checks for logical issues that go beyond JSON schema validity:\n * 1. **Duplicate IDs** (`DUPLICATE_ID`) -- every content item id must be unique.\n * 2. **Nesting depth** (`NESTING_DEPTH`) -- sections may not be nested more\n * than 3 levels deep.\n * 3. **Unknown field references** (`UNKNOWN_FIELD_REF`) -- conditions must\n * only reference field ids that exist in the registry.\n * 4. **Condition references section** (`CONDITION_REFS_SECTION`) -- conditions\n * must not reference section ids, because sections have no values.\n * 5. **Constraint contradictions** (`INVALID_MIN_MAX`) -- e.g. `minLength > maxLength`,\n * `min > max`, `minDate > maxDate` (absolute dates only), `minItems > maxItems`.\n * 6. **Invalid regex** (`INVALID_REGEX`) -- string field `pattern` values must\n * be valid regular expressions.\n */\nexport class FormDefinitionValidator {\n /**\n * Validates raw input against the form definition JSON schema.\n *\n * @param input - The raw input to validate.\n * @returns Array of `SCHEMA_INVALID` issues. Empty when the input conforms to the schema.\n */\n validateSchema(input: unknown): DocumentValidationError[] {\n if (validateFn(input)) return []\n\n return (validateFn.errors ?? []).map((err) => {\n const path = err.instancePath || '/'\n const message = err.message ?? 'Unknown error'\n\n if (err.keyword === 'additionalProperties') {\n const additional = (err.params as { additionalProperty?: string }).additionalProperty\n return { code: 'SCHEMA_INVALID', message: `${path}: ${message}: '${additional}'` }\n }\n\n return { code: 'SCHEMA_INVALID', message: `${path}: ${message}` }\n })\n }\n\n /**\n * Validates a form definition semantically.\n *\n * @param definition - The form definition to validate.\n * @param registry - The flattened field registry built from the definition.\n * @returns Array of issues found. Empty if the definition is semantically valid.\n */\n validate(definition: FormDefinition, registry: Map<number, FieldEntry>): DocumentValidationError[] {\n const issues: DocumentValidationError[] = []\n\n this.checkDuplicateIds(definition.content, issues)\n this.checkNestingDepth(definition.content, 0, issues)\n this.checkConditionRefs(registry, issues)\n this.checkConditionRefsSection(registry, issues)\n this.checkConstraintContradictions(registry, issues)\n this.checkInvalidRegex(registry, issues)\n\n return issues\n }\n\n private checkDuplicateIds(content: ContentItem[], issues: DocumentValidationError[]): void {\n const seen = new Set<number>()\n this.walkItems(content, (item) => {\n if (seen.has(item.id)) {\n issues.push({ code: 'DUPLICATE_ID', message: `Duplicate id: ${item.id}`, itemId: item.id })\n } else {\n seen.add(item.id)\n }\n })\n }\n\n private checkNestingDepth(content: ContentItem[], depth: number, issues: DocumentValidationError[]): void {\n for (const item of content) {\n if (item.type === 'section') {\n if (depth >= 3) {\n issues.push({\n code: 'NESTING_DEPTH',\n message: `Section nesting exceeds maximum depth of 3: ${item.id}`,\n itemId: item.id,\n })\n } else {\n this.checkNestingDepth(item.content, depth + 1, issues)\n }\n }\n }\n }\n\n private checkConditionRefs(registry: Map<number, FieldEntry>, issues: DocumentValidationError[]): void {\n for (const [id, entry] of registry) {\n if (!entry.condition) continue\n const refs = DependencyGraph.extractFieldRefs(entry.condition)\n for (const ref of refs) {\n if (!registry.has(ref)) {\n issues.push({\n code: 'UNKNOWN_FIELD_REF',\n message: `Condition references unknown field: ${ref} (in item ${id})`,\n itemId: id,\n })\n }\n }\n }\n }\n\n private checkConditionRefsSection(registry: Map<number, FieldEntry>, issues: DocumentValidationError[]): void {\n for (const [id, entry] of registry) {\n if (!entry.condition) continue\n const refs = DependencyGraph.extractFieldRefs(entry.condition)\n for (const ref of refs) {\n const refEntry = registry.get(ref)\n if (refEntry && refEntry.type === 'section') {\n issues.push({\n code: 'CONDITION_REFS_SECTION',\n message: `Condition references section ${ref}, which has no value (in item ${id})`,\n itemId: id,\n })\n }\n }\n }\n }\n\n private checkConstraintContradictions(registry: Map<number, FieldEntry>, issues: DocumentValidationError[]): void {\n for (const [id, entry] of registry) {\n if (!entry.validation) continue\n\n switch (entry.type) {\n case 'string': {\n const v = entry.validation as StringValidation\n if (v.minLength !== undefined && v.maxLength !== undefined && v.maxLength < v.minLength) {\n issues.push({\n code: 'INVALID_MIN_MAX',\n message: `maxLength must be >= minLength for field ${id}`,\n itemId: id,\n })\n }\n break\n }\n case 'number': {\n const v = entry.validation as NumberValidation\n if (v.min !== undefined && v.max !== undefined && v.max < v.min) {\n issues.push({\n code: 'INVALID_MIN_MAX',\n message: `max must be >= min for field ${id}`,\n itemId: id,\n })\n }\n break\n }\n case 'date': {\n const v = entry.validation as DateValidation\n if (v.minDate !== undefined && v.maxDate !== undefined) {\n const minIsAbsolute = !isRelativeDate(v.minDate)\n const maxIsAbsolute = !isRelativeDate(v.maxDate)\n if (minIsAbsolute && maxIsAbsolute) {\n if (Date.parse(v.maxDate) < Date.parse(v.minDate)) {\n issues.push({\n code: 'INVALID_MIN_MAX',\n message: `maxDate must be >= minDate for field ${id}`,\n itemId: id,\n })\n }\n }\n }\n break\n }\n case 'array': {\n const v = entry.validation as ArrayValidation\n if (v.minItems !== undefined && v.maxItems !== undefined && v.maxItems < v.minItems) {\n issues.push({\n code: 'INVALID_MIN_MAX',\n message: `maxItems must be >= minItems for field ${id}`,\n itemId: id,\n })\n }\n break\n }\n }\n }\n }\n\n private checkInvalidRegex(registry: Map<number, FieldEntry>, issues: DocumentValidationError[]): void {\n for (const [id, entry] of registry) {\n if (entry.type !== 'string' || !entry.validation) continue\n const v = entry.validation as StringValidation\n if (v.pattern === undefined) continue\n try {\n new RegExp(v.pattern)\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e)\n issues.push({\n code: 'INVALID_REGEX',\n message: `Invalid regex pattern for field ${id}: ${msg}`,\n itemId: id,\n })\n }\n }\n }\n\n private walkItems(content: ContentItem[], fn: (item: ContentItem) => void): void {\n for (const item of content) {\n fn(item)\n if (item.type === 'section') {\n this.walkItems(item.content, fn)\n }\n }\n }\n}\n","import type { DocumentValidationError } from './validation-results'\n\n/**\n * Error thrown when form definition or document validation fails.\n *\n * Inspect {@link errors} for structured programmatic access to all\n * validation issues.\n *\n * @example\n * ```ts\n * try {\n * const engine = new FormEngine(definition);\n * } catch (err) {\n * if (err instanceof DocumentError) {\n * for (const e of err.errors) {\n * console.log(e.code, e.message);\n * }\n * }\n * }\n * ```\n */\nexport class DocumentError extends Error {\n /** Structured list of all validation errors. */\n readonly errors: DocumentValidationError[]\n\n /**\n * @param errors - One or more validation errors that caused the error.\n */\n constructor(errors: DocumentValidationError[]) {\n const summary = errors.map((e) => e.message).join('; ')\n super(`Document validation failed: ${summary}`)\n this.name = 'DocumentError'\n this.errors = errors\n }\n}\n","import { ConditionEvaluator } from './condition-evaluator'\nimport type { FieldEntry } from './types/field-entry'\nimport type { FormValues } from './types/form-values'\n\n/**\n * Computes field and section visibility for a form.\n *\n * Provides two modes of visibility computation:\n * - **Single-item** (`isVisible`): evaluates one item's condition plus its\n * parent chain. Does not use the hidden-field rule.\n * - **Bulk** (`getVisibilityMap`): evaluates all items in topological order\n * with the hidden-field rule applied (references to hidden fields are\n * treated as \"not set\").\n */\nexport class VisibilityResolver {\n private readonly registry: Map<number, FieldEntry>\n private readonly conditionEvaluator: ConditionEvaluator\n private readonly topologicalOrder: number[]\n\n /**\n * @param registry - The engine's field registry.\n * @param conditionEvaluator - Evaluator for condition trees.\n * @param topologicalOrder - Item ids in topological order (from {@link DependencyGraph}).\n */\n constructor(registry: Map<number, FieldEntry>, conditionEvaluator: ConditionEvaluator, topologicalOrder: number[]) {\n this.registry = registry\n this.conditionEvaluator = conditionEvaluator\n this.topologicalOrder = topologicalOrder\n }\n\n /**\n * Determines whether a single field or section is visible.\n *\n * Evaluation logic:\n * 1. If the item has its own condition, evaluate it. If `false`, the item is hidden.\n * 2. If the item has a parent section, recursively check parent visibility.\n * An item is hidden whenever any ancestor is hidden.\n * 3. Items without conditions and without hidden parents are visible.\n *\n * Unlike {@link getVisibilityMap}, this method does not use the\n * pre-computed visibility map and does not apply the hidden-field rule.\n * Use it for one-off visibility checks; prefer `getVisibilityMap` when\n * evaluating many items at once.\n *\n * @param id - Numeric id of the field or section to check.\n * @param values - Current form values.\n * @param now - Reference date for relative date expressions.\n * @returns `true` if the item should be displayed.\n */\n isVisible(id: number, values: FormValues, now: Date): boolean {\n const entry = this.registry.get(id)\n if (!entry) return false\n\n // Check own condition\n if (entry.condition) {\n const result = this.conditionEvaluator.evalCondition(entry.condition, { values, now })\n if (!result) return false\n }\n\n // Check parent chain\n if (entry.parentId !== undefined) {\n return this.isVisible(entry.parentId, values, now)\n }\n\n return true\n }\n\n /**\n * Computes visibility for all fields and sections in a single pass.\n *\n * Iterates in topological order so that every item is evaluated after the\n * fields its condition depends on. This enables the **hidden-field rule**:\n * if a condition references a field that has already been determined hidden,\n * that field is treated as \"not set\".\n *\n * Cascading parent visibility is also enforced -- if a parent section is\n * hidden, all its children are immediately marked hidden without evaluating\n * their own conditions.\n *\n * @param values - Current form values.\n * @param now - Reference date for relative date expressions.\n * @returns Map from item id to visibility boolean (`true` = visible).\n */\n getVisibilityMap(values: FormValues, now: Date): Map<number, boolean> {\n const result = new Map<number, boolean>()\n\n for (const id of this.topologicalOrder) {\n const entry = this.registry.get(id)\n if (!entry) {\n result.set(id, false)\n continue\n }\n\n // Parent must be visible\n if (entry.parentId !== undefined && result.get(entry.parentId) === false) {\n result.set(id, false)\n continue\n }\n\n // Evaluate own condition\n if (entry.condition) {\n const visible = this.conditionEvaluator.evalCondition(entry.condition, {\n values,\n visibilityMap: result,\n now,\n })\n result.set(id, visible)\n } else {\n result.set(id, true)\n }\n }\n\n return result\n }\n}\n","import { ConditionEvaluator } from './condition-evaluator'\nimport { DependencyGraph } from './dependency-graph'\nimport { FieldValidator } from './field-validator'\nimport { FormDefinitionValidator } from './form-definition-validator'\nimport { DocumentError } from './types/errors'\nimport type { FieldEntry } from './types/field-entry'\nimport type { ContentItem, FormDefinition } from './types/form-definition'\nimport type { FormSnapshot } from './types/form-snapshot'\nimport type { FormDocument, FormValues } from './types/form-values'\nimport type { DocumentValidationError, FieldValidationError, FormValidationResult } from './types/validation-results'\nimport { VisibilityResolver } from './visibility-resolver'\n\n/**\n * The runtime form engine.\n *\n * Created by passing a {@link FormDefinition} to the constructor. The\n * construction lifecycle is:\n *\n * 1. **Build field registry** -- walks the definition tree depth-first,\n * creating a flat {@link FieldEntry} for every field and section while\n * recording document-order ids in `contentOrder`.\n * 2. **Semantic validation** -- checks for duplicate ids, excessive nesting,\n * unknown/invalid condition references, constraint contradictions, and\n * invalid regex patterns.\n * 3. **Cycle detection** -- verifies that condition dependencies form a DAG\n * (no circular references).\n * 4. **Error reporting** -- if any issues were found in steps 2-3, throws a\n * {@link DocumentError} containing all issues.\n * 5. **Build dependency graph** -- creates a forward adjacency map so the\n * engine can quickly determine which items are affected when a field\n * value changes.\n * 6. **Topological sort** -- orders all items so that dependencies are\n * evaluated before dependents (used by `getVisibilityMap`).\n * 7. **Assemble components** -- creates internal {@link ConditionEvaluator},\n * {@link VisibilityResolver}, and {@link FieldValidator} instances.\n *\n * @example\n * ```ts\n * const engine = new FormEngine(myFormDefinition);\n * const visibility = engine.getVisibilityMap(formValues);\n * const result = engine.validate(formValues);\n * ```\n */\nexport class FormEngine {\n private readonly registry: Map<number, FieldEntry>\n private readonly depGraph: DependencyGraph\n private readonly visibilityResolver: VisibilityResolver\n private readonly fieldValidator: FieldValidator\n private readonly definition: FormDefinition\n private readonly formId: string\n private readonly formVersion: string\n\n /**\n * Ordered list of all content item ids in depth-first document order.\n * Matches the order in which items appear in the form definition.\n */\n readonly contentOrder: readonly number[]\n\n /**\n * Compiles a {@link FormDefinition} into a ready-to-use engine.\n *\n * @param definition - A complete form definition to compile.\n * @throws {DocumentError} If the definition contains semantic issues\n * or circular condition dependencies.\n */\n constructor(definition: FormDefinition) {\n // 0. JSON schema validation\n const definitionValidator = new FormDefinitionValidator()\n const schemaIssues = definitionValidator.validateSchema(definition)\n if (schemaIssues.length > 0) {\n throw new DocumentError(schemaIssues)\n }\n\n // 1. Build field registry + content order\n const registry = new Map<number, FieldEntry>()\n const contentOrder: number[] = []\n FormEngine.walkContent(definition.content, undefined, registry, contentOrder)\n\n // 2. Semantic validation\n const issues = definitionValidator.validate(definition, registry)\n\n // 3. Cycle detection\n const cyclePath = DependencyGraph.detectCycle(registry)\n if (cyclePath) {\n issues.push({\n code: 'CIRCULAR_DEPENDENCY',\n message: `Circular condition dependency detected: ${cyclePath.join(' -> ')}`,\n })\n }\n\n if (issues.length > 0) {\n throw new DocumentError(issues)\n }\n\n // 4. Build dependency graph\n this.depGraph = new DependencyGraph(registry)\n\n // 5. Assemble components\n const conditionEvaluator = new ConditionEvaluator()\n this.visibilityResolver = new VisibilityResolver(registry, conditionEvaluator, this.depGraph.topologicalOrder)\n this.fieldValidator = new FieldValidator(registry)\n\n this.registry = registry\n this.contentOrder = contentOrder\n this.definition = definition\n this.formId = definition.id\n this.formVersion = definition.version\n }\n\n /**\n * Creates a {@link FormDocument} pre-populated with the form schema's\n * id and version.\n *\n * @param values - Optional initial field values. Defaults to an empty object.\n * @returns A new form document ready for use with engine methods.\n */\n createFormDocument(values?: FormValues): FormDocument {\n return {\n form: { id: this.formId, version: this.formVersion, submittedAt: new Date().toISOString() },\n values: values ?? {},\n }\n }\n\n /**\n * Serializes the form definition and document into a single {@link FormSnapshot}.\n *\n * The snapshot contains the original {@link FormDefinition} used to construct\n * the engine and the provided {@link FormDocument}. No validation is performed;\n * call {@link validate} separately if needed.\n *\n * @param doc - The form document to include in the snapshot.\n * @returns A snapshot containing both the definition and the document.\n */\n dumpDocument(doc: FormDocument): FormSnapshot {\n return {\n definition: this.definition,\n document: doc,\n }\n }\n\n /**\n * Loads a {@link FormDocument} from a previously created {@link FormSnapshot}.\n *\n * Verifies that the snapshot's form definition matches the engine's\n * compiled definition by comparing id and version. Throws a\n * {@link DocumentError} if there is a mismatch.\n *\n * @param snapshot - A snapshot previously produced by {@link dumpDocument}.\n * @returns The form document from the snapshot.\n * @throws {DocumentError} If the snapshot's definition id or version\n * does not match the engine's.\n */\n loadDocument(snapshot: FormSnapshot): FormDocument {\n const errors: DocumentValidationError[] = []\n if (snapshot.definition.id !== this.formId) {\n errors.push({\n code: 'FORM_ID_MISMATCH',\n message: `Snapshot form id \"${snapshot.definition.id}\" does not match expected \"${this.formId}\"`,\n params: { expected: this.formId, actual: snapshot.definition.id },\n })\n }\n if (snapshot.definition.version !== this.formVersion) {\n errors.push({\n code: 'FORM_VERSION_MISMATCH',\n message: `Snapshot form version \"${snapshot.definition.version}\" does not match expected \"${this.formVersion}\"`,\n params: { expected: this.formVersion, actual: snapshot.definition.version },\n })\n }\n if (errors.length > 0) {\n throw new DocumentError(errors)\n }\n return snapshot.document\n }\n\n /**\n * Determines whether a field or section is visible given the current form document.\n *\n * Evaluates the item's own condition and walks up the parent chain --\n * an item is hidden if any ancestor is hidden.\n *\n * @param id - Numeric id of the field or section.\n * @param doc - Current form document.\n * @returns `true` if the item should be displayed, `false` otherwise.\n */\n isVisible(id: number, doc: FormDocument): boolean {\n return this.visibilityResolver.isVisible(id, doc.values, FormEngine.parseNow(doc))\n }\n\n /**\n * Computes visibility for every field and section in topological order.\n *\n * The resulting map is keyed by item id. Items whose conditions depend on\n * other items are evaluated after their dependencies, ensuring correct\n * cascading visibility (e.g. a hidden parent hides all children).\n *\n * @param doc - Current form document.\n * @returns Map from item id to visibility boolean.\n */\n getVisibilityMap(doc: FormDocument): Map<number, boolean> {\n return this.visibilityResolver.getVisibilityMap(doc.values, FormEngine.parseNow(doc))\n }\n\n /**\n * Returns the set of item ids whose visibility may change when the\n * specified field's value changes.\n *\n * Includes transitive dependents -- if field A controls field B, and\n * field B controls field C, changing A returns `{B, C}`.\n * Results are cached for the lifetime of the engine.\n *\n * @param fieldId - Id of the field that changed.\n * @returns Set of affected item ids (does not include `fieldId` itself unless\n * it is part of a dependency chain).\n */\n getAffectedIds(fieldId: number): Set<number> {\n return this.depGraph.getAffectedIds(fieldId)\n }\n\n /**\n * Validates form values against the schema's validation rules.\n *\n * Only visible fields are validated -- hidden fields are skipped entirely.\n * Sections are never validated directly. For array fields, each item is\n * validated individually according to the array's item definition.\n *\n * The reference time for relative date validation is derived from\n * `doc.form.submittedAt`. If that value is missing or unparseable, a\n * document-level error is reported and `new Date()` is used as fallback.\n *\n * @param doc - Current form document to validate.\n * @returns Validation result with a `valid` flag and a `fieldErrors` map.\n */\n validate(doc: FormDocument): FormValidationResult {\n // Document compatibility check\n const documentErrors: DocumentValidationError[] = []\n if (doc.form.id !== this.formId) {\n documentErrors.push({\n code: 'FORM_ID_MISMATCH',\n message: `Document form id \"${doc.form.id}\" does not match expected \"${this.formId}\"`,\n params: { expected: this.formId, actual: doc.form.id },\n })\n }\n if (doc.form.version !== this.formVersion) {\n documentErrors.push({\n code: 'FORM_VERSION_MISMATCH',\n message: `Document form version \"${doc.form.version}\" does not match expected \"${this.formVersion}\"`,\n params: { expected: this.formVersion, actual: doc.form.version },\n })\n }\n\n // Derive `now` from submittedAt; report document errors for missing/invalid values\n let now: Date\n if (!doc.form.submittedAt) {\n documentErrors.push({\n code: 'FORM_SUBMITTED_AT_MISSING',\n message: 'Document form submittedAt is missing',\n })\n now = new Date()\n } else {\n const parsedSubmittedAt = new Date(doc.form.submittedAt)\n if (Number.isNaN(parsedSubmittedAt.getTime())) {\n documentErrors.push({\n code: 'FORM_SUBMITTED_AT_INVALID',\n message: `Document form submittedAt \"${doc.form.submittedAt}\" is not a valid date`,\n params: { actual: doc.form.submittedAt },\n })\n now = new Date()\n } else {\n now = parsedSubmittedAt\n }\n }\n\n // Document level validation errors\n\n if (documentErrors.length > 0)\n return {\n valid: false,\n fieldErrors: new Map<number, FieldValidationError[]>(),\n documentErrors,\n }\n\n // Field level validation errors\n\n const visibilityMap = this.visibilityResolver.getVisibilityMap(doc.values, now)\n return this.fieldValidator.validate(doc.values, visibilityMap, now)\n }\n\n /**\n * Retrieves the internal {@link FieldEntry} for a given id.\n *\n * @param id - Numeric id of the field or section.\n * @returns The field entry, or `undefined` if the id is not in the registry.\n */\n getFieldDef(id: number): FieldEntry | undefined {\n return this.registry.get(id)\n }\n\n private static parseNow(doc: FormDocument): Date {\n if (doc.form.submittedAt) {\n const parsed = new Date(doc.form.submittedAt)\n if (!Number.isNaN(parsed.getTime())) return parsed\n }\n return new Date()\n }\n\n private static walkContent(\n content: ContentItem[],\n parentId: number | undefined,\n registry: Map<number, FieldEntry>,\n contentOrder: number[],\n ): void {\n for (const item of content) {\n const entry: FieldEntry = {\n id: item.id,\n type: item.type,\n condition: item.condition,\n validation: item.type !== 'section' ? item.validation : undefined,\n parentId,\n options: item.type === 'select' ? item.options : undefined,\n item: item.type === 'array' ? item.item : undefined,\n label: item.type !== 'section' ? item.label : undefined,\n title: item.type === 'section' ? item.title : undefined,\n }\n\n registry.set(item.id, entry)\n contentOrder.push(item.id)\n\n if (item.type === 'section') {\n FormEngine.walkContent(item.content, item.id, registry, contentOrder)\n }\n }\n }\n}\n","import { FormEngine } from './form-engine'\nimport type { FormDefinition } from './types/form-definition'\nimport type { FormDocument } from './types/form-values'\nimport type { FormValidationResult } from './types/validation-results'\n\n/**\n * Mutable editor for building and modifying form values against a {@link FormDefinition}.\n *\n * Wraps a {@link FormEngine} and a mutable {@link FormDocument}. All mutating\n * methods return `this` for fluent chaining.\n *\n * @example\n * ```ts\n * const editor = new FormValuesEditor(definition)\n * editor\n * .setFieldValue(1, 'Alice')\n * .setFieldValue(2, 30)\n * .setSubmittedAt('2025-01-01T00:00:00Z')\n *\n * const result = editor.validate()\n * const doc = editor.toJSON()\n * ```\n */\nexport class FormValuesEditor {\n private readonly engine: FormEngine\n private doc: FormDocument\n\n /**\n * Creates a new editor for the given form definition.\n *\n * @param definition - The form definition to edit values against.\n * @param doc - An existing document to pre-populate. Deep-cloned internally.\n * When omitted a blank document is created via {@link FormEngine.createFormDocument}.\n */\n constructor(definition: FormDefinition, doc?: FormDocument) {\n this.engine = new FormEngine(definition)\n this.doc = doc ? JSON.parse(JSON.stringify(doc)) : this.engine.createFormDocument()\n }\n\n /**\n * Returns the current value of a field.\n *\n * @param fieldId - Numeric id of the field.\n * @returns The field value, or `undefined` if not set.\n */\n getFieldValue(fieldId: number): unknown {\n return this.doc.values[String(fieldId)]\n }\n\n /**\n * Sets the value of a field.\n *\n * @param fieldId - Numeric id of the field.\n * @param value - The value to set.\n * @returns `this` for chaining.\n * @throws If `fieldId` is unknown or references a section.\n */\n setFieldValue(fieldId: number, value: unknown): this {\n this.assertField(fieldId)\n this.doc.values[String(fieldId)] = value\n return this\n }\n\n /**\n * Removes the value of a field.\n *\n * @param fieldId - Numeric id of the field.\n * @returns `this` for chaining.\n */\n clearFieldValue(fieldId: number): this {\n delete this.doc.values[String(fieldId)]\n return this\n }\n\n /**\n * Appends an item to an array field.\n *\n * If the field currently has no value, it is initialized to an empty array\n * before appending.\n *\n * @param fieldId - Numeric id of the array field.\n * @param value - The value to append. Defaults to `undefined`.\n * @returns `this` for chaining.\n * @throws If `fieldId` is not an array field.\n */\n addArrayItem(fieldId: number, value?: unknown): this {\n const arr = this.getOrInitArray(fieldId)\n arr.push(value)\n return this\n }\n\n /**\n * Removes an item from an array field by index.\n *\n * @param fieldId - Numeric id of the array field.\n * @param index - Zero-based index of the item to remove.\n * @returns `this` for chaining.\n * @throws If `fieldId` is not an array field or the index is out of bounds.\n */\n removeArrayItem(fieldId: number, index: number): this {\n const arr = this.assertArray(fieldId)\n if (index < 0 || index >= arr.length) {\n throw new Error(`Index ${index} is out of bounds for array field ${fieldId} (length ${arr.length})`)\n }\n arr.splice(index, 1)\n return this\n }\n\n /**\n * Moves an item within an array field from one index to another.\n *\n * @param fieldId - Numeric id of the array field.\n * @param fromIndex - Current zero-based index of the item.\n * @param toIndex - Target zero-based index.\n * @returns `this` for chaining.\n * @throws If `fieldId` is not an array field or either index is out of bounds.\n */\n moveArrayItem(fieldId: number, fromIndex: number, toIndex: number): this {\n const arr = this.assertArray(fieldId)\n if (fromIndex < 0 || fromIndex >= arr.length) {\n throw new Error(`fromIndex ${fromIndex} is out of bounds for array field ${fieldId} (length ${arr.length})`)\n }\n if (toIndex < 0 || toIndex >= arr.length) {\n throw new Error(`toIndex ${toIndex} is out of bounds for array field ${fieldId} (length ${arr.length})`)\n }\n const [item] = arr.splice(fromIndex, 1)\n arr.splice(toIndex, 0, item)\n return this\n }\n\n /**\n * Sets the value of an item at a specific index in an array field.\n *\n * @param fieldId - Numeric id of the array field.\n * @param index - Zero-based index of the item to set.\n * @param value - The new value for the item.\n * @returns `this` for chaining.\n * @throws If `fieldId` is not an array field or the index is out of bounds.\n */\n setArrayItem(fieldId: number, index: number, value: unknown): this {\n const arr = this.assertArray(fieldId)\n if (index < 0 || index >= arr.length) {\n throw new Error(`Index ${index} is out of bounds for array field ${fieldId} (length ${arr.length})`)\n }\n arr[index] = value\n return this\n }\n\n /**\n * Sets the `submittedAt` timestamp on the document.\n *\n * @param submittedAt - ISO 8601 timestamp string.\n * @returns `this` for chaining.\n */\n setSubmittedAt(submittedAt: string): this {\n this.doc.form.submittedAt = submittedAt\n return this\n }\n\n /**\n * Validates the current document against the form definition.\n *\n * Delegates to {@link FormEngine.validate}.\n *\n * @returns The validation result.\n */\n validate(): FormValidationResult {\n return this.engine.validate(this.doc)\n }\n\n /**\n * Computes visibility for every field and section.\n *\n * Delegates to {@link FormEngine.getVisibilityMap}.\n *\n * @returns Map from item id to visibility boolean.\n */\n getVisibilityMap(): Map<number, boolean> {\n return this.engine.getVisibilityMap(this.doc)\n }\n\n /**\n * Determines whether a field or section is visible given current values.\n *\n * Delegates to {@link FormEngine.isVisible}.\n *\n * @param id - Numeric id of the field or section.\n * @returns `true` if the item should be displayed.\n */\n isVisible(id: number): boolean {\n return this.engine.isVisible(id, this.doc)\n }\n\n /**\n * Returns a deep clone of the current form document.\n *\n * @returns A new serializable {@link FormDocument} instance.\n */\n toJSON(): FormDocument {\n return JSON.parse(JSON.stringify(this.doc))\n }\n\n /**\n * Asserts that `fieldId` exists in the registry and is not a section.\n */\n private assertField(fieldId: number): void {\n const entry = this.engine.getFieldDef(fieldId)\n\n if (!entry) throw new Error(`Field with id ${fieldId} not found`)\n if (entry.type === 'section') throw new Error(`Item ${fieldId} is a section, not a field`)\n }\n\n /**\n * Asserts that `fieldId` is an array field and returns the current array value.\n * Throws if the field is not an array type or the current value is not an array.\n */\n private assertArray(fieldId: number): unknown[] {\n const entry = this.engine.getFieldDef(fieldId)\n if (!entry) throw new Error(`Field with id ${fieldId} not found`)\n\n if (entry.type !== 'array') throw new Error(`Field ${fieldId} is not an array field`)\n\n const val = this.doc.values[String(fieldId)]\n if (!Array.isArray(val)) throw new Error(`Field ${fieldId} does not currently hold an array value`)\n\n return val\n }\n\n /**\n * Returns the array value for `fieldId`, initializing to `[]` if not yet set.\n */\n private getOrInitArray(fieldId: number): unknown[] {\n const entry = this.engine.getFieldDef(fieldId)\n if (!entry) throw new Error(`Field with id ${fieldId} not found`)\n\n if (entry.type !== 'array') throw new Error(`Field ${fieldId} is not an array field`)\n\n const key = String(fieldId)\n let val = this.doc.values[key]\n if (!Array.isArray(val)) {\n val = []\n this.doc.values[key] = val\n }\n\n return val as unknown[]\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["formDefinitionSchema"],"sources":["../src/date-utils.ts","../src/condition-evaluator.ts","../src/dependency-graph.ts","../src/validators/array-validator.ts","../src/validators/boolean-validator.ts","../src/validators/date-validator.ts","../src/validators/file-validator.ts","../src/validators/number-validator.ts","../src/validators/select-validator.ts","../src/validators/string-validator.ts","../src/field-validator.ts","../src/form-definition-editor.ts","../src/form-definition.schema.json","../src/form-definition-validator.ts","../src/types/errors.ts","../src/visibility-resolver.ts","../src/form-engine.ts","../src/form-values-editor.ts"],"sourcesContent":["const RELATIVE_DATE_RE = /^([+-])(\\d+)([dwmy])$/\n\n/**\n * Type guard that checks whether a value is a relative date expression.\n *\n * Relative date expressions follow the pattern `[+-]<amount><unit>` where\n * `unit` is one of `d` (days), `w` (weeks), `m` (months), or `y` (years).\n *\n * @param value - The value to test.\n * @returns `true` if `value` is a string matching the relative date pattern.\n *\n * @example\n * ```ts\n * isRelativeDate(\"+7d\") // true (7 days from now)\n * isRelativeDate(\"-1m\") // true (1 month ago)\n * isRelativeDate(\"2024-01-01\") // false (absolute date)\n * isRelativeDate(42) // false (not a string)\n * ```\n */\nexport const isRelativeDate = (value: unknown): value is string =>\n typeof value === 'string' && RELATIVE_DATE_RE.test(value)\n\n/**\n * Resolves a relative date expression into an absolute ISO-8601 date string.\n *\n * Supported units:\n * - `d` -- days\n * - `w` -- weeks (7 days)\n * - `m` -- months\n * - `y` -- years\n *\n * Arithmetic is performed in UTC. If the input does not match the relative\n * date pattern, it is returned unchanged.\n *\n * @param relative - A relative date expression (e.g. `\"+7d\"`, `\"-3m\"`).\n * @param now - Reference date for the calculation. Defaults to `new Date()`.\n * @returns An ISO-8601 date-time string, or the original string if it is not\n * a valid relative expression.\n *\n * @example\n * ```ts\n * const base = new Date(\"2024-06-15T00:00:00Z\");\n * resolveRelativeDate(\"+7d\", base) // \"2024-06-22T00:00:00.000Z\"\n * resolveRelativeDate(\"-1m\", base) // \"2024-05-15T00:00:00.000Z\"\n * resolveRelativeDate(\"+1y\", base) // \"2025-06-15T00:00:00.000Z\"\n * ```\n */\nexport const resolveRelativeDate = (relative: string, now: Date): string => {\n const match = RELATIVE_DATE_RE.exec(relative)\n if (!match) return relative\n\n const sign = match[1] === '+' ? 1 : -1\n const amount = Number(match[2]) * sign\n const unit = match[3]\n\n const result = new Date(now.getTime())\n\n switch (unit) {\n case 'd':\n result.setUTCDate(result.getUTCDate() + amount)\n break\n case 'w':\n result.setUTCDate(result.getUTCDate() + amount * 7)\n break\n case 'm':\n result.setUTCMonth(result.getUTCMonth() + amount)\n break\n case 'y':\n result.setUTCFullYear(result.getUTCFullYear() + amount)\n break\n }\n\n return result.toISOString()\n}\n","import { isRelativeDate, resolveRelativeDate } from './date-utils'\nimport type { Condition, SimpleCondition } from './types/conditions'\n\n/**\n * Context passed to condition evaluation methods.\n *\n * @property values - Current form values keyed by stringified field id.\n * @property visibilityMap - Pre-computed visibility map. When provided, a\n * reference to a hidden field is treated as \"not set\" regardless of its\n * actual value.\n * @property now - Reference date for resolving relative date expressions in\n * condition values.\n */\nexport type EvaluationContext = {\n values: Record<string, unknown>\n visibilityMap?: Map<number, boolean>\n now: Date\n}\n\n/**\n * Evaluates condition trees against form state.\n *\n * Supports three kinds of conditions:\n * - **Simple** ({@link SimpleCondition}): compares a single field's value\n * using one of the supported operators (`set`, `notset`, `eq`, `ne`, `lt`,\n * `gt`, `lte`, `gte`, `in`, `notin`).\n * - **Compound AND**: `{ and: [...] }` -- all child conditions must be true.\n * - **Compound OR**: `{ or: [...] }` -- at least one child condition must be true.\n *\n * **Hidden-field rule**: when a `visibilityMap` is provided and the\n * referenced field is hidden (`false`), the condition evaluates as if the\n * field has no value. This means `notset` returns `true` and all other\n * operators return `false`.\n *\n * **Date handling**: condition values that are relative date expressions\n * (e.g. `\"+7d\"`) are resolved against `ctx.now` before comparison.\n */\nexport class ConditionEvaluator {\n /**\n * Evaluates a condition tree against the current form state.\n *\n * @param condition - The condition to evaluate (simple or compound).\n * @param ctx - Evaluation context containing form values and optional\n * visibility/date overrides.\n * @returns `true` if the condition is satisfied, `false` otherwise.\n */\n evalCondition(condition: Condition, ctx: EvaluationContext): boolean {\n if ('and' in condition) return condition.and.every((c) => this.evalCondition(c, ctx))\n if ('or' in condition) return condition.or.some((c) => this.evalCondition(c, ctx))\n return this.evalSimple(condition as SimpleCondition, ctx)\n }\n\n private evalSimple(cond: SimpleCondition, ctx: EvaluationContext): boolean {\n // Hidden-field rule: if the referenced field is hidden, treat as not set\n if (ctx.visibilityMap && ctx.visibilityMap.get(cond.field) === false) return cond.op === 'notset'\n\n const fieldValue = ctx.values[String(cond.field)]\n\n switch (cond.op) {\n case 'set':\n return fieldValue !== null && fieldValue !== undefined && fieldValue !== ''\n case 'notset':\n return fieldValue === null || fieldValue === undefined || fieldValue === ''\n case 'eq':\n return fieldValue === this.resolveIfDate(cond.value, ctx.now)\n case 'ne':\n return fieldValue !== this.resolveIfDate(cond.value, ctx.now)\n case 'lt':\n return this.compareTo(fieldValue, cond.value, ctx.now) < 0\n case 'gt':\n return this.compareTo(fieldValue, cond.value, ctx.now) > 0\n case 'lte':\n return this.compareTo(fieldValue, cond.value, ctx.now) <= 0\n case 'gte':\n return this.compareTo(fieldValue, cond.value, ctx.now) >= 0\n case 'in':\n return Array.isArray(cond.value) && cond.value.includes(fieldValue)\n case 'notin':\n return Array.isArray(cond.value) && !cond.value.includes(fieldValue)\n default:\n return false\n }\n }\n\n private resolveIfDate(value: unknown, now: Date): unknown {\n if (isRelativeDate(value)) return resolveRelativeDate(value, now)\n return value\n }\n\n private compareTo(a: unknown, b: unknown, now: Date): number {\n const resolvedB = this.resolveIfDate(b, now)\n\n if (typeof a === 'number' && typeof resolvedB === 'number') return a - resolvedB\n\n // Date comparison: both must be parseable date strings\n if (typeof a === 'string' && typeof resolvedB === 'string') {\n const ta = Date.parse(a)\n const tb = Date.parse(resolvedB)\n if (!Number.isNaN(ta) && !Number.isNaN(tb)) {\n return ta - tb\n }\n // Fall back to lexicographic for non-date strings\n if (a < resolvedB) return -1\n if (a > resolvedB) return 1\n return 0\n }\n\n return Number.NaN\n }\n}\n","import type { Condition } from './types/conditions'\nimport type { FieldEntry } from './types/field-entry'\n\nconst DfsVisitState = { Unvisited: 0, InProgress: 1, Completed: 2 } as const\ntype DfsVisitState = (typeof DfsVisitState)[keyof typeof DfsVisitState]\n\n/**\n * Manages the condition dependency graph for form fields.\n *\n * Built from the field registry during engine preparation. Provides:\n * - Forward dependency graph (`graph`): answers \"if field X changes, which\n * items need to re-evaluate their visibility?\"\n * - Topological ordering (`topologicalOrder`): guarantees that when computing\n * visibility, every item is evaluated after the fields it depends on.\n * - Affected-ids lookup (`getAffectedIds`): returns all transitively\n * affected item ids when a field value changes (lazily cached).\n *\n * Static methods (`extractFieldRefs`, `detectCycle`) can be used before\n * constructing an instance, e.g. during semantic validation.\n */\nexport class DependencyGraph {\n /**\n * Forward adjacencyMap map: key is a field id, value is the set of item ids\n * whose conditions reference that field.\n */\n readonly graph: Map<number, Set<number>>\n\n /**\n * Item ids in topological order. Dependencies come before dependents.\n */\n readonly topologicalOrder: number[]\n\n private readonly registry: Map<number, FieldEntry>\n private readonly affectedCache = new Map<number, Set<number>>()\n\n /**\n * @param registry - The engine's field registry (built during preparation).\n */\n constructor(registry: Map<number, FieldEntry>) {\n this.registry = registry\n this.graph = this.buildGraph()\n this.topologicalOrder = this.buildTopologicalOrder()\n }\n\n /**\n * Extracts the set of field ids referenced by a condition tree.\n *\n * Recursively walks compound conditions (`and`/`or`) and collects the\n * `field` property from every leaf {@link SimpleCondition}.\n *\n * @param condition - A simple or compound condition.\n * @returns Set of all unique field ids that appear in the condition.\n */\n static extractFieldRefs(condition: Condition): Set<number> {\n const refs = new Set<number>()\n DependencyGraph.collectRefs(condition, refs)\n return refs\n }\n\n /**\n * Detects circular dependencies in the condition graph.\n *\n * Uses DFS-based cycle detection (white/gray/black coloring). If a cycle\n * is found, the function reconstructs and returns a human-readable path\n * string (e.g. `\"1 -> 2 -> 3 -> 1\"`).\n *\n * @param registry - The engine's field registry.\n * @returns An array of field ids forming the cycle, or `undefined` if no cycle exists.\n */\n static detectCycle(registry: Map<number, FieldEntry>): number[] | undefined {\n const allIds = new Set(registry.keys())\n const adjacencyMap = new Map<number, Set<number>>()\n\n for (const [id, entry] of registry) {\n if (!entry.condition) continue\n\n const refs = DependencyGraph.extractFieldRefs(entry.condition)\n\n for (const ref of refs) {\n if (!allIds.has(ref)) continue\n\n let targets = adjacencyMap.get(ref)\n if (!targets) {\n targets = new Set()\n adjacencyMap.set(ref, targets)\n }\n\n targets.add(id)\n }\n }\n\n // DFS-based cycle detection\n\n const nodes = new Map<number, DfsVisitState>()\n const parent = new Map<number, number>()\n\n for (const id of allIds) {\n nodes.set(id, DfsVisitState.Unvisited)\n }\n\n for (const id of allIds) {\n if (nodes.get(id) === DfsVisitState.Unvisited) {\n const cyclePath = DependencyGraph.dfs(id, adjacencyMap, nodes, parent, allIds)\n if (cyclePath) return cyclePath\n }\n }\n\n return undefined\n }\n\n /**\n * Returns the set of item ids whose visibility could change when the given\n * field's value changes.\n *\n * Performs a transitive expansion of the forward dependency graph starting\n * from the field's direct dependents. Results are memoized for subsequent\n * calls with the same `fieldId`.\n *\n * @param fieldId - Id of the field whose value changed.\n * @returns Set of all transitively affected item ids. Empty set if no items\n * depend on `fieldId`.\n */\n getAffectedIds(fieldId: number): Set<number> {\n const cached = this.affectedCache.get(fieldId)\n if (cached) return cached\n\n const directDeps = this.graph.get(fieldId)\n if (!directDeps || directDeps.size === 0) {\n const empty = new Set<number>()\n this.affectedCache.set(fieldId, empty)\n return empty\n }\n\n const expanded = this.expandTransitiveDependencies(directDeps)\n this.affectedCache.set(fieldId, expanded)\n\n return expanded\n }\n\n private static collectRefs(condition: Condition, refs: Set<number>): void {\n if ('and' in condition) {\n for (const c of condition.and) DependencyGraph.collectRefs(c, refs)\n } else if ('or' in condition) {\n for (const c of condition.or) DependencyGraph.collectRefs(c, refs)\n } else {\n refs.add(condition.field)\n }\n }\n\n private static dfs(\n node: number,\n adjacencyMap: Map<number, Set<number>>,\n nodes: Map<number, DfsVisitState>,\n parent: Map<number, number>,\n allIds: Set<number>,\n ): number[] | undefined {\n nodes.set(node, DfsVisitState.InProgress)\n\n const neighbors = adjacencyMap.get(node)\n if (neighbors) {\n for (const next of neighbors) {\n if (!allIds.has(next)) continue\n\n if (nodes.get(next) === DfsVisitState.InProgress)\n return DependencyGraph.reconstructCycle(next, node, parent)\n\n if (nodes.get(next) === DfsVisitState.Completed) continue\n\n parent.set(next, node)\n\n const cycle = DependencyGraph.dfs(next, adjacencyMap, nodes, parent, allIds)\n if (cycle) return cycle\n }\n }\n\n nodes.set(node, DfsVisitState.Completed)\n return undefined\n }\n\n private static reconstructCycle(cycleStart: number, cycleEnd: number, parent: Map<number, number>): number[] {\n const path: number[] = [cycleStart]\n\n let current = cycleEnd\n while (current !== cycleStart) {\n path.push(current)\n\n const next = parent.get(current)\n if (next === undefined) break\n\n current = next\n }\n\n path.push(cycleStart)\n\n return path.reverse()\n }\n\n /**\n * Builds the forward dependency graph from the registry.\n */\n private buildGraph(): Map<number, Set<number>> {\n const graph = new Map<number, Set<number>>()\n\n for (const [id, entry] of this.registry) {\n if (!entry.condition) continue\n\n const refs = DependencyGraph.extractFieldRefs(entry.condition)\n\n for (const ref of refs) {\n let deps = graph.get(ref)\n if (!deps) {\n deps = new Set()\n graph.set(ref, deps)\n }\n\n deps.add(id)\n }\n }\n\n return graph\n }\n\n /**\n * Produces a topological ordering using Kahn's algorithm.\n */\n private buildTopologicalOrder(): number[] {\n const allIds = new Set(this.registry.keys())\n\n const inDegree = new Map<number, number>()\n const adjacencyMap = new Map<number, Set<number>>()\n\n for (const id of allIds) {\n inDegree.set(id, 0)\n }\n\n for (const [id, entry] of this.registry) {\n if (!entry.condition) continue\n\n const refs = DependencyGraph.extractFieldRefs(entry.condition)\n for (const ref of refs) {\n if (!allIds.has(ref)) continue\n\n let targets = adjacencyMap.get(ref)\n if (!targets) {\n targets = new Set()\n adjacencyMap.set(ref, targets)\n }\n if (!targets.has(id)) {\n targets.add(id)\n inDegree.set(id, (inDegree.get(id) ?? 0) + 1)\n }\n }\n }\n\n // Parent → child edges ensure parents are evaluated before children\n // in getVisibilityMap, so cascading visibility works correctly.\n for (const [id, entry] of this.registry) {\n if (entry.parentId === undefined) continue\n if (!allIds.has(entry.parentId)) continue\n\n let targets = adjacencyMap.get(entry.parentId)\n if (!targets) {\n targets = new Set()\n adjacencyMap.set(entry.parentId, targets)\n }\n if (!targets.has(id)) {\n targets.add(id)\n inDegree.set(id, (inDegree.get(id) ?? 0) + 1)\n }\n }\n\n // Kahn's algorithm\n const queue: number[] = []\n for (const [id, deg] of inDegree) {\n if (deg === 0) queue.push(id)\n }\n\n const sorted: number[] = []\n\n while (queue.length > 0) {\n const current = queue.shift()\n if (current === undefined) break\n sorted.push(current)\n\n const targets = adjacencyMap.get(current)\n if (targets) {\n for (const target of targets) {\n const newDeg = (inDegree.get(target) ?? 1) - 1\n inDegree.set(target, newDeg)\n if (newDeg === 0) {\n queue.push(target)\n }\n }\n }\n }\n\n if (sorted.length !== allIds.size) {\n const inCycle = [...allIds].filter((id) => !sorted.includes(id))\n const cyclePath = inCycle.join(' -> ')\n return sorted.length === 0\n ? []\n : (() => {\n throw cyclePath\n })()\n }\n\n return sorted\n }\n\n /**\n * Expands a set of item ids to include all transitive dependents via BFS.\n */\n private expandTransitiveDependencies(startIds: Set<number>): Set<number> {\n const result = new Set<number>()\n const queue = [...startIds]\n\n while (queue.length > 0) {\n const id = queue.shift()\n\n if (id === undefined) break\n if (result.has(id)) continue\n\n result.add(id)\n\n const deps = this.graph.get(id)\n if (deps) {\n for (const dep of deps) {\n if (!result.has(dep)) queue.push(dep)\n }\n }\n }\n\n return result\n }\n}\n","import type { ArrayItemDef } from '../types/array-item-def'\nimport type { FieldEntry } from '../types/field-entry'\nimport type { ArrayValidation } from '../types/validation/array'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport type ArrayValidatorContext = ValidatorContext & {\n item?: ArrayItemDef\n validateField: (fieldId: number, value: unknown, entry: FieldEntry, now: Date) => FieldValidationError[]\n}\n\nexport class ArrayValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value, now } = ctx\n const { item, validateField } = ctx as ArrayValidatorContext\n const validation = ctx.validation as ArrayValidation | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined\n\n if (isEmpty) return errors\n\n if (!Array.isArray(value)) {\n errors.push({ fieldId, rule: 'TYPE', message: 'Must be an array', params: { expectedType: 'array' } })\n return errors\n }\n\n if (validation?.minItems !== undefined && value.length < validation.minItems) {\n errors.push({\n fieldId,\n rule: 'MIN_ITEMS',\n message: `Must have at least ${validation.minItems} items`,\n params: { minItems: validation.minItems, actual: value.length },\n })\n }\n\n if (validation?.maxItems !== undefined && value.length > validation.maxItems) {\n errors.push({\n fieldId,\n rule: 'MAX_ITEMS',\n message: `Must have at most ${validation.maxItems} items`,\n params: { maxItems: validation.maxItems, actual: value.length },\n })\n }\n\n if (item) {\n for (let i = 0; i < value.length; i++) {\n const fakeEntry: FieldEntry = {\n id: fieldId,\n type: item.type,\n condition: undefined,\n validation: item.validation as FieldEntry['validation'],\n parentId: undefined,\n options: item.options,\n item: undefined,\n label: item.label,\n title: undefined,\n }\n\n const itemErrors = validateField(fieldId, value[i], fakeEntry, now)\n errors.push(...itemErrors.map((err) => ({ ...err, itemIndex: i })))\n }\n }\n\n return errors\n }\n}\n","import type { BooleanValidation } from '../types/validation/boolean'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class BooleanValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value } = ctx\n const validation = ctx.validation as BooleanValidation | undefined\n const errors: FieldValidationError[] = []\n\n if (validation?.required && value !== true && value !== false) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n }\n\n return errors\n }\n}\n","import { isRelativeDate, resolveRelativeDate } from '../date-utils'\nimport type { DateValidation } from '../types/validation/date'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class DateValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value, now } = ctx\n const validation = ctx.validation as DateValidation | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined || value === ''\n\n if (validation?.required && isEmpty) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n return errors\n }\n\n if (isEmpty) return errors\n\n if (typeof value !== 'string') {\n errors.push({ fieldId, rule: 'TYPE', message: 'Must be a valid date', params: { expectedType: 'date' } })\n return errors\n }\n\n const timestamp = Date.parse(value)\n if (Number.isNaN(timestamp)) {\n errors.push({ fieldId, rule: 'INVALID_DATE', message: 'Must be a valid date' })\n return errors\n }\n\n if (validation?.minDate !== undefined) {\n const minResolved = isRelativeDate(validation.minDate)\n ? resolveRelativeDate(validation.minDate, now)\n : validation.minDate\n if (timestamp < Date.parse(minResolved)) {\n errors.push({\n fieldId,\n rule: 'MIN_DATE',\n message: `Must be on or after ${minResolved}`,\n params: { minDate: minResolved },\n })\n }\n }\n\n if (validation?.maxDate !== undefined) {\n const maxResolved = isRelativeDate(validation.maxDate)\n ? resolveRelativeDate(validation.maxDate, now)\n : validation.maxDate\n if (timestamp > Date.parse(maxResolved)) {\n errors.push({\n fieldId,\n rule: 'MAX_DATE',\n message: `Must be on or before ${maxResolved}`,\n params: { maxDate: maxResolved },\n })\n }\n }\n\n return errors\n }\n}\n","import type { FileValidation } from '../types/validation/file'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class FileValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value } = ctx\n const validation = ctx.validation as FileValidation | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined\n\n if (validation?.required && isEmpty) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n return errors\n }\n\n if (isEmpty) return errors\n\n if (\n typeof value !== 'object' ||\n typeof (value as Record<string, unknown>).name !== 'string' ||\n typeof (value as Record<string, unknown>).mimeType !== 'string' ||\n typeof (value as Record<string, unknown>).size !== 'number' ||\n typeof (value as Record<string, unknown>).url !== 'string'\n ) {\n errors.push({\n fieldId,\n rule: 'TYPE',\n message: 'Must be a valid file object',\n params: { expectedType: 'file' },\n })\n }\n\n return errors\n }\n}\n","import type { NumberValidation } from '../types/validation/number'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class NumberValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value } = ctx\n const validation = ctx.validation as NumberValidation | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined\n\n if (validation?.required && isEmpty) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n return errors\n }\n\n if (isEmpty) return errors\n\n if (typeof value !== 'number') {\n errors.push({ fieldId, rule: 'TYPE', message: 'Must be a number', params: { expectedType: 'number' } })\n return errors\n }\n\n if (validation?.min !== undefined && value < validation.min) {\n errors.push({\n fieldId,\n rule: 'MIN',\n message: `Must be at least ${validation.min}`,\n params: { min: validation.min, actual: value },\n })\n }\n\n if (validation?.max !== undefined && value > validation.max) {\n errors.push({\n fieldId,\n rule: 'MAX',\n message: `Must be at most ${validation.max}`,\n params: { max: validation.max, actual: value },\n })\n }\n\n return errors\n }\n}\n","import type { SelectOption } from '../types/select-option'\nimport type { SelectValidation } from '../types/validation/select'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class SelectValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value } = ctx\n const validation = ctx.validation as SelectValidation | undefined\n const options = ctx.options as SelectOption[] | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined\n\n if (validation?.required && isEmpty) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n return errors\n }\n\n if (isEmpty) return errors\n\n if (options && !options.some((opt) => opt.value === value)) {\n errors.push({ fieldId, rule: 'INVALID_OPTION', message: 'Value is not a valid option' })\n }\n\n return errors\n }\n}\n","import type { StringValidation } from '../types/validation/string'\nimport type { FieldValidationError } from '../types/validation-results'\nimport type { TypeValidator, ValidatorContext } from './type-validator'\n\nexport class StringValidator implements TypeValidator {\n validate(ctx: ValidatorContext): FieldValidationError[] {\n const { fieldId, value } = ctx\n const validation = ctx.validation as StringValidation | undefined\n const errors: FieldValidationError[] = []\n const isEmpty = value === null || value === undefined || value === ''\n\n if (validation?.required && isEmpty) {\n errors.push({ fieldId, rule: 'REQUIRED', message: 'Value is required' })\n return errors\n }\n\n if (isEmpty) return errors\n\n if (typeof value !== 'string') {\n errors.push({ fieldId, rule: 'TYPE', message: 'Must be a string', params: { expectedType: 'string' } })\n return errors\n }\n\n if (validation?.minLength !== undefined && value.length < validation.minLength) {\n errors.push({\n fieldId,\n rule: 'MIN_LENGTH',\n message: `Must be at least ${validation.minLength} characters`,\n params: { minLength: validation.minLength, actual: value.length },\n })\n }\n\n if (validation?.maxLength !== undefined && value.length > validation.maxLength) {\n errors.push({\n fieldId,\n rule: 'MAX_LENGTH',\n message: `Must be at most ${validation.maxLength} characters`,\n params: { maxLength: validation.maxLength, actual: value.length },\n })\n }\n\n if (validation?.pattern !== undefined) {\n const re = new RegExp(validation.pattern)\n if (!re.test(value)) {\n errors.push({\n fieldId,\n rule: 'PATTERN',\n message: validation.patternMessage ?? 'Value does not match the required pattern',\n })\n }\n }\n\n return errors\n }\n}\n","import type { FieldEntry } from './types/field-entry'\nimport type { FormValues } from './types/form-values'\nimport type { FieldValidationError, FormValidationResult } from './types/validation-results'\nimport type { ArrayValidatorContext } from './validators/array-validator'\nimport { ArrayValidator } from './validators/array-validator'\nimport { BooleanValidator } from './validators/boolean-validator'\nimport { DateValidator } from './validators/date-validator'\nimport { FileValidator } from './validators/file-validator'\nimport { NumberValidator } from './validators/number-validator'\nimport { SelectValidator } from './validators/select-validator'\nimport { StringValidator } from './validators/string-validator'\nimport type { TypeValidator } from './validators/type-validator'\n\n/**\n * Validates form values against the schema's validation rules.\n *\n * **Which fields are validated:**\n * - Only fields (not sections) are validated.\n * - Hidden fields (those with `visibilityMap.get(id) === false`) are skipped\n * entirely -- they produce no errors regardless of their value.\n *\n * **How each field type is validated:**\n * - `string` -- `required`, `minLength`, `maxLength`, `pattern`.\n * - `number` -- `required`, `min`, `max`.\n * - `boolean` -- `required` (must be explicitly `true` or `false`).\n * - `date` -- `required`, `minDate`, `maxDate`. Relative date boundaries\n * are resolved against `now`.\n * - `select` -- `required`, plus the value must be one of the defined options.\n * - `array` -- `minItems`, `maxItems`, plus each item is validated\n * individually according to the array's {@link ArrayItemDef}. Item-level\n * errors carry an `itemIndex`.\n *\n * For all types, if `required` fails, no further rules are checked for that\n * field (early return). If the value is empty/absent and `required` is not\n * set, no errors are produced.\n */\nexport class FieldValidator {\n private readonly registry: Map<number, FieldEntry>\n private readonly validators: Record<string, TypeValidator>\n\n /**\n * @param registry - The engine's field registry.\n */\n constructor(registry: Map<number, FieldEntry>) {\n this.registry = registry\n this.validators = {\n string: new StringValidator(),\n number: new NumberValidator(),\n boolean: new BooleanValidator(),\n date: new DateValidator(),\n select: new SelectValidator(),\n array: new ArrayValidator(),\n file: new FileValidator(),\n }\n }\n\n /**\n * Validates form values against the schema's validation rules.\n *\n * @param values - The form values to validate, keyed by stringified field id.\n * @param visibilityMap - Pre-computed visibility map for all items.\n * @param now - Reference date for resolving relative date expressions.\n * Defaults to `new Date()`.\n * @returns A {@link FormValidationResult} with `valid: true` when no errors\n * exist, or `valid: false` with a populated `fieldErrors` map.\n */\n validate(values: FormValues, visibilityMap: Map<number, boolean>, now: Date = new Date()): FormValidationResult {\n const fieldErrors = new Map<number, FieldValidationError[]>()\n\n for (const [id, entry] of this.registry) {\n if (entry.type === 'section') continue\n if (visibilityMap.get(id) === false) continue\n\n const value = values[String(id)]\n const errors = this.validateField(id, value, entry, now)\n if (errors.length > 0) {\n fieldErrors.set(id, errors)\n }\n }\n\n return { valid: fieldErrors.size === 0, fieldErrors }\n }\n\n private validateField(fieldId: number, value: unknown, entry: FieldEntry, now: Date): FieldValidationError[] {\n const validator = this.validators[entry.type]\n if (!validator) return []\n\n if (entry.type === 'array') {\n const ctx: ArrayValidatorContext = {\n fieldId,\n value,\n validation: entry.validation,\n now,\n item: entry.item,\n validateField: this.validateField.bind(this),\n }\n return validator.validate(ctx)\n }\n\n return validator.validate({\n fieldId,\n value,\n validation: entry.validation,\n now,\n options: entry.options,\n })\n }\n}\n","import type { ArrayItemDef } from './types/array-item-def'\nimport type { Condition } from './types/conditions'\nimport type { ContentItem, FieldContentItem, FormDefinition, SectionContentItem } from './types/form-definition'\nimport type { SelectOption } from './types/select-option'\nimport type { TypeSpecificValidation } from './types/validation/type-specific'\n\n/**\n * Descriptor for a field to be added via the editor.\n * `id` is optional -- when omitted the editor auto-assigns the next available id.\n */\nexport type FieldDescriptor = Omit<FieldContentItem, 'id'> & { id?: number }\n\n/**\n * Descriptor for a section to be added via the editor.\n * `id` is optional -- when omitted the editor auto-assigns the next available id.\n * `content` defaults to an empty array (items are added separately).\n */\nexport type SectionDescriptor = Omit<SectionContentItem, 'id' | 'content'> & {\n id?: number\n content?: ContentItem[]\n}\n\n/**\n * Flat info about a content item returned by listing methods.\n */\nexport type ContentItemInfo = {\n id: number\n type: ContentItem['type']\n label?: string\n title?: string\n parentId: number | undefined\n}\n\n/**\n * Mutable editor for building and modifying a {@link FormDefinition}.\n *\n * Operates directly on the definition tree. All mutating methods return\n * `this` for fluent chaining.\n *\n * @example\n * ```ts\n * const editor = new FormDefinitionEditor({\n * id: 'my-form', version: '1.0.0', title: 'My Form', content: [],\n * })\n * editor\n * .addField({ type: 'string', label: 'Name', validation: { required: true } })\n * .addSection({ type: 'section', title: 'Details' })\n * .addField({ type: 'number', label: 'Age' }, 2) // into section id=2\n *\n * const definition = editor.toJSON()\n * ```\n */\nexport class FormDefinitionEditor {\n private definition: FormDefinition\n\n constructor(definition: FormDefinition) {\n this.definition = JSON.parse(JSON.stringify(definition))\n }\n\n setTitle(title: string): this {\n this.definition.title = title\n return this\n }\n\n setDescription(description: string | undefined): this {\n if (description === undefined) {\n delete this.definition.description\n } else {\n this.definition.description = description\n }\n return this\n }\n\n setVersion(version: string): this {\n this.definition.version = version\n return this\n }\n\n setId(id: string): this {\n this.definition.id = id\n return this\n }\n\n /**\n * Returns the next available numeric id (max existing + 1).\n */\n nextId(): number {\n let max = 0\n this.walkAll(this.definition.content, (item) => {\n if (item.id > max) max = item.id\n })\n return max + 1\n }\n\n /**\n * Adds a field to the form.\n *\n * @param descriptor - Field properties. `id` is auto-assigned if omitted.\n * @param parentId - Section id to add into. `undefined` for top-level.\n * @param index - Position within the parent's content array. Appends if omitted.\n * @returns `this` for chaining.\n * @throws If `parentId` references a non-existent or non-section item, or if the id already exists.\n */\n addField(descriptor: FieldDescriptor, parentId?: number, index?: number): this {\n const id = descriptor.id ?? this.nextId()\n this.assertIdAvailable(id)\n const field: FieldContentItem = { ...descriptor, id }\n this.insertItem(field, parentId, index)\n return this\n }\n\n /**\n * Adds a section to the form.\n *\n * @param descriptor - Section properties. `id` is auto-assigned if omitted.\n * @param parentId - Parent section id. `undefined` for top-level.\n * @param index - Position within the parent's content array. Appends if omitted.\n * @returns `this` for chaining.\n * @throws If `parentId` references a non-existent or non-section item, or if the id already exists.\n */\n addSection(descriptor: SectionDescriptor, parentId?: number, index?: number): this {\n const id = descriptor.id ?? this.nextId()\n this.assertIdAvailable(id)\n const section: SectionContentItem = {\n ...descriptor,\n id,\n content: descriptor.content ?? [],\n }\n this.insertItem(section, parentId, index)\n return this\n }\n\n /**\n * Updates properties of an existing field.\n *\n * Cannot change `id` or `type`. Use {@link removeItem} + {@link addField}\n * to change the type.\n */\n updateField(id: number, updates: Partial<Omit<FieldContentItem, 'id' | 'type'>>): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n if (item.type === 'section') throw new Error(`Item ${id} is a section, not a field`)\n Object.assign(item, updates)\n return this\n }\n\n /**\n * Updates properties of an existing section.\n *\n * Cannot change `id`, `type`, or `content` directly. Use add/remove methods\n * for content manipulation.\n */\n updateSection(id: number, updates: Partial<Omit<SectionContentItem, 'id' | 'type' | 'content'>>): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n if (item.type !== 'section') throw new Error(`Item ${id} is not a section`)\n Object.assign(item, updates)\n return this\n }\n\n /**\n * Removes a field or section (and all its descendants) by id.\n *\n * @returns `this` for chaining.\n * @throws If the id is not found.\n */\n removeItem(id: number): this {\n const removed = this.removeFromContent(this.definition.content, id)\n if (!removed) throw new Error(`Item with id ${id} not found`)\n return this\n }\n\n /**\n * Moves an item to a new parent and/or position.\n *\n * @param id - Id of the item to move.\n * @param targetParentId - Destination section id, or `undefined` for top-level.\n * @param index - Position in the target content array. Appends if omitted.\n */\n moveItem(id: number, targetParentId: number | undefined, index?: number): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n // Prevent moving a section into itself or its own descendant\n if (targetParentId !== undefined && item.type === 'section') {\n if (targetParentId === id) throw new Error('Cannot move a section into itself')\n const descendantIds = this.collectDescendantIds(item as SectionContentItem)\n if (descendantIds.has(targetParentId)) {\n throw new Error('Cannot move a section into its own descendant')\n }\n }\n\n const clone: ContentItem = JSON.parse(JSON.stringify(item))\n this.removeFromContent(this.definition.content, id)\n this.insertItem(clone, targetParentId, index)\n return this\n }\n\n /**\n * Returns a flat list of all content items (fields + sections) with parent info.\n */\n listAll(): ContentItemInfo[] {\n const result: ContentItemInfo[] = []\n this.walkAllWithParent(this.definition.content, undefined, (item, parentId) => {\n result.push({\n id: item.id,\n type: item.type,\n label: item.type !== 'section' ? item.label : undefined,\n title: item.type === 'section' ? item.title : undefined,\n parentId,\n })\n })\n return result\n }\n\n /**\n * Returns a flat list of all fields (excludes sections).\n */\n listFields(): ContentItemInfo[] {\n return this.listAll().filter((i) => i.type !== 'section')\n }\n\n /**\n * Returns a flat list of all sections.\n */\n listSections(): ContentItemInfo[] {\n return this.listAll().filter((i) => i.type === 'section')\n }\n\n /**\n * Returns the content item with the given id, or `undefined` if not found.\n */\n getItem(id: number): ContentItem | undefined {\n return this.findItem(id) ?? undefined\n }\n\n /**\n * Sets or clears the validation rules for a field.\n */\n setValidation(id: number, validation: TypeSpecificValidation | undefined): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (item.type === 'section') throw new Error('Sections do not have validation')\n\n const field = item as FieldContentItem\n\n if (validation === undefined) delete field.validation\n else field.validation = validation\n\n return this\n }\n\n /**\n * Sets or clears the visibility condition for a field or section.\n */\n setCondition(id: number, condition: Condition | undefined): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (condition === undefined) delete item.condition\n else item.condition = condition\n\n return this\n }\n\n /**\n * Sets the select options for a `select` field.\n */\n setOptions(id: number, options: SelectOption[]): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (item.type !== 'select') throw new Error(`Field ${id} is not a select field`)\n\n const field = item as FieldContentItem\n field.options = options\n\n return this\n }\n\n /**\n * Sets the item definition for an `array` field.\n */\n setArrayItem(id: number, itemDef: ArrayItemDef): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (item.type !== 'array') throw new Error(`Field ${id} is not an array field`)\n\n const field = item as FieldContentItem\n field.item = itemDef\n\n return this\n }\n\n /**\n * Sets the label for a field.\n */\n setLabel(id: number, label: string): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (item.type === 'section') throw new Error('Sections use title, not label')\n\n const field = item as FieldContentItem\n field.label = label\n\n return this\n }\n\n /**\n * Sets the description for a field or section.\n */\n setFieldDescription(id: number, description: string | undefined): this {\n const item = this.findItem(id)\n if (!item) throw new Error(`Item with id ${id} not found`)\n\n if (description === undefined) delete item.description\n else item.description = description\n\n return this\n }\n\n /**\n * Returns a deep clone of the current form definition.\n */\n toJSON(): FormDefinition {\n return JSON.parse(JSON.stringify(this.definition))\n }\n\n private findItem(id: number): ContentItem | undefined {\n let found: ContentItem | undefined\n\n this.walkAll(this.definition.content, (item) => {\n if (item.id === id) found = item\n })\n\n return found\n }\n\n private assertIdAvailable(id: number): void {\n if (this.findItem(id)) throw new Error(`Item with id ${id} already exists`)\n }\n\n private insertItem(item: ContentItem, parentId: number | undefined, index?: number): void {\n const target = this.getTargetContent(parentId)\n\n if (index !== undefined && index >= 0 && index < target.length) target.splice(index, 0, item)\n else target.push(item)\n }\n\n private getTargetContent(parentId: number | undefined): ContentItem[] {\n if (parentId === undefined) return this.definition.content\n\n const parent = this.findItem(parentId)\n if (!parent) throw new Error(`Parent section with id ${parentId} not found`)\n\n if (parent.type !== 'section') throw new Error(`Item ${parentId} is not a section`)\n\n const section = parent as SectionContentItem\n return section.content\n }\n\n private removeFromContent(content: ContentItem[], id: number): boolean {\n const idx = content.findIndex((item) => item.id === id)\n if (idx !== -1) {\n content.splice(idx, 1)\n return true\n }\n\n for (const item of content) {\n if (item.type === 'section') {\n if (this.removeFromContent((item as SectionContentItem).content, id)) return true\n }\n }\n\n return false\n }\n\n private walkAll(content: ContentItem[], fn: (item: ContentItem) => void): void {\n for (const item of content) {\n fn(item)\n if (item.type === 'section') {\n this.walkAll((item as SectionContentItem).content, fn)\n }\n }\n }\n\n private walkAllWithParent(\n content: ContentItem[],\n parentId: number | undefined,\n fn: (item: ContentItem, parentId: number | undefined) => void,\n ): void {\n for (const item of content) {\n fn(item, parentId)\n if (item.type === 'section') {\n this.walkAllWithParent((item as SectionContentItem).content, item.id, fn)\n }\n }\n }\n\n private collectDescendantIds(section: SectionContentItem): Set<number> {\n const ids = new Set<number>()\n this.walkAll(section.content, (item) => ids.add(item.id))\n return ids\n }\n}\n","","import type { ErrorObject } from 'ajv'\nimport Ajv2020 from 'ajv/dist/2020'\n\nimport { isRelativeDate } from './date-utils'\nimport { DependencyGraph } from './dependency-graph'\nimport formDefinitionSchema from './form-definition.schema.json'\nimport type { FieldEntry } from './types/field-entry'\nimport type { ContentItem, FormDefinition } from './types/form-definition'\nimport type { ArrayValidation } from './types/validation/array'\nimport type { DateValidation } from './types/validation/date'\nimport type { NumberValidation } from './types/validation/number'\nimport type { StringValidation } from './types/validation/string'\nimport type { DocumentValidationError } from './types/validation-results'\n\nconst ajv = new Ajv2020({ allErrors: true })\nconst validateFn = ajv.compile(formDefinitionSchema)\n\ntype SchemaIssue = {\n path: string\n keyword: string\n message: string\n property?: string\n}\n\nconst itemRequiredProperties = new Map<string, Set<string>>([\n ['string', new Set(['id', 'type', 'label'])],\n ['number', new Set(['id', 'type', 'label'])],\n ['boolean', new Set(['id', 'type', 'label'])],\n ['date', new Set(['id', 'type', 'label'])],\n ['select', new Set(['id', 'type', 'label', 'options'])],\n ['array', new Set(['id', 'type', 'label', 'item'])],\n ['file', new Set(['id', 'type', 'label'])],\n ['section', new Set(['id', 'type', 'title', 'content'])],\n])\n\nconst itemAllowedProperties = new Map<string, Set<string>>([\n ['string', new Set(['id', 'type', 'label', 'description', 'condition', 'validation'])],\n ['number', new Set(['id', 'type', 'label', 'description', 'condition', 'validation'])],\n ['boolean', new Set(['id', 'type', 'label', 'description', 'condition', 'validation'])],\n ['date', new Set(['id', 'type', 'label', 'description', 'condition', 'validation'])],\n ['select', new Set(['id', 'type', 'label', 'description', 'condition', 'options', 'validation'])],\n ['array', new Set(['id', 'type', 'label', 'description', 'condition', 'item', 'validation'])],\n ['file', new Set(['id', 'type', 'label', 'description', 'condition', 'validation'])],\n ['section', new Set(['id', 'type', 'title', 'description', 'condition', 'content'])],\n])\n\n/**\n * Validates form definitions at both the structural (JSON Schema) and\n * semantic levels.\n *\n * Used by {@link FormEngine} during construction before building the engine.\n *\n * ### Schema validation (`validateSchema`)\n * Validates raw input against the form definition JSON Schema. Returns\n * `SCHEMA_INVALID` issues for every violation found.\n *\n * ### Semantic validation (`validate`)\n * Checks for logical issues that go beyond JSON schema validity:\n * 1. **Duplicate IDs** (`DUPLICATE_ID`) -- every content item id must be unique.\n * 2. **Nesting depth** (`NESTING_DEPTH`) -- sections may not be nested more\n * than 3 levels deep.\n * 3. **Unknown field references** (`UNKNOWN_FIELD_REF`) -- conditions must\n * only reference field ids that exist in the registry.\n * 4. **Condition references section** (`CONDITION_REFS_SECTION`) -- conditions\n * must not reference section ids, because sections have no values.\n * 5. **Constraint contradictions** (`INVALID_MIN_MAX`) -- e.g. `minLength > maxLength`,\n * `min > max`, `minDate > maxDate` (absolute dates only), `minItems > maxItems`.\n * 6. **Invalid regex** (`INVALID_REGEX`) -- string field `pattern` values must\n * be valid regular expressions.\n */\nexport class FormDefinitionValidator {\n /**\n * Validates raw input against the form definition JSON schema.\n *\n * @param input - The raw input to validate.\n * @returns Array of `SCHEMA_INVALID` issues. Empty when the input conforms to the schema.\n */\n validateSchema(input: unknown): DocumentValidationError[] {\n if (validateFn(input)) return []\n\n return this.formatSchemaErrors(validateFn.errors ?? [], input)\n }\n\n private formatSchemaErrors(errors: ErrorObject[], input: unknown): DocumentValidationError[] {\n const issues = this.collectSchemaIssues(errors, input)\n const dedupedIssues = this.deduplicateSchemaIssues(issues)\n const specificIssuePaths = dedupedIssues.filter((issue) => issue.keyword !== 'oneOf').map((issue) => issue.path)\n\n return dedupedIssues\n .filter((issue) => !this.isRedundantOneOfIssue(issue, specificIssuePaths))\n .map((issue) => ({\n code: 'SCHEMA_INVALID',\n message: issue.message,\n params: {\n path: issue.path,\n keyword: issue.keyword,\n ...(issue.property ? { property: issue.property } : {}),\n },\n }))\n }\n\n private collectSchemaIssues(errors: ErrorObject[], input: unknown): SchemaIssue[] {\n const additionalByPath = new Map<string, Set<string>>()\n const issues: SchemaIssue[] = []\n\n for (const err of errors) {\n const path = err.instancePath || '/'\n\n if (!this.shouldKeepSchemaError(err, input)) continue\n\n if (err.keyword === 'additionalProperties') {\n const property = (err.params as { additionalProperty?: string }).additionalProperty\n if (!property) continue\n if (!this.shouldKeepAdditionalPropertyError(path, property, input)) continue\n\n const properties = additionalByPath.get(path) ?? new Set<string>()\n properties.add(property)\n additionalByPath.set(path, properties)\n continue\n }\n\n issues.push(this.formatSchemaIssue(err))\n }\n\n for (const [path, properties] of additionalByPath) {\n issues.push(this.formatAdditionalPropertiesIssue(path, [...properties].sort()))\n }\n\n return issues\n }\n\n private shouldKeepSchemaError(err: ErrorObject, input: unknown): boolean {\n if (err.keyword === 'const' && this.getLastPathSegment(err.instancePath) === 'type') {\n const value = this.getValueAtPath(input, this.getParentPath(err.instancePath))\n return !(this.isRecord(value) && typeof value.type === 'string' && itemAllowedProperties.has(value.type))\n }\n\n if (err.keyword !== 'required') return true\n\n const missingProperty = (err.params as { missingProperty?: string }).missingProperty\n if (!missingProperty) return true\n\n const value = this.getValueAtPath(input, err.instancePath)\n if (this.isRecord(value) && value.type === undefined && this.isContentItemPath(err.instancePath)) {\n return missingProperty === 'id' || missingProperty === 'type'\n }\n\n if (!this.isRecord(value) || typeof value.type !== 'string') return true\n\n const requiredProperties = itemRequiredProperties.get(value.type)\n return requiredProperties ? requiredProperties.has(missingProperty) : true\n }\n\n private shouldKeepAdditionalPropertyError(path: string, property: string, input: unknown): boolean {\n const value = this.getValueAtPath(input, path)\n if (!this.isRecord(value) || typeof value.type !== 'string') return true\n\n const allowedProperties = itemAllowedProperties.get(value.type)\n return allowedProperties ? !allowedProperties.has(property) : true\n }\n\n private formatSchemaIssue(err: ErrorObject): SchemaIssue {\n const path = err.instancePath || '/'\n\n switch (err.keyword) {\n case 'required': {\n const property = (err.params as { missingProperty?: string }).missingProperty ?? 'unknown'\n return {\n path,\n keyword: err.keyword,\n property,\n message: `${this.formatPath(path)} is missing required property \"${property}\".`,\n }\n }\n case 'const': {\n const propertyPath = this.formatPath(path)\n const parentPath = this.getParentPath(path)\n const property = this.getLastPathSegment(path)\n\n return {\n path,\n keyword: err.keyword,\n property,\n message:\n property === 'type'\n ? `${this.formatPath(parentPath)} has an invalid type.`\n : `${propertyPath} has an invalid value.`,\n }\n }\n case 'type': {\n const params = err.params as { type?: string }\n return {\n path,\n keyword: err.keyword,\n property: this.getLastPathSegment(path),\n message: `${this.formatPath(path)} must be ${this.formatArticle(params.type)} ${params.type ?? 'valid value'}.`,\n }\n }\n case 'oneOf':\n return {\n path,\n keyword: err.keyword,\n message: `${this.formatPath(path)} is invalid.`,\n }\n default:\n return {\n path,\n keyword: err.keyword,\n property: this.getLastPathSegment(path),\n message: `${this.formatPath(path)} ${err.message ?? 'is invalid'}.`,\n }\n }\n }\n\n private formatAdditionalPropertiesIssue(path: string, properties: string[]): SchemaIssue {\n const propertyList = properties.map((property) => `\"${property}\"`).join(', ')\n const noun = properties.length === 1 ? 'property' : 'properties'\n\n return {\n path,\n keyword: 'additionalProperties',\n property: properties.join(','),\n message: `${this.formatPath(path)} has unsupported ${noun}: ${propertyList}.`,\n }\n }\n\n private deduplicateSchemaIssues(issues: SchemaIssue[]): SchemaIssue[] {\n const seen = new Set<string>()\n const result: SchemaIssue[] = []\n\n for (const issue of issues) {\n const key = `${issue.path}:${issue.keyword}:${issue.property ?? ''}:${issue.message}`\n if (seen.has(key)) continue\n\n seen.add(key)\n result.push(issue)\n }\n\n return result\n }\n\n private isRedundantOneOfIssue(issue: SchemaIssue, specificIssuePaths: string[]): boolean {\n if (issue.keyword !== 'oneOf') return false\n\n return specificIssuePaths.some((path) => path === issue.path || path.startsWith(`${issue.path}/`))\n }\n\n private formatPath(path: string): string {\n if (!path || path === '/') return 'Form definition'\n\n const segments = path.split('/').filter(Boolean)\n const parts: string[] = []\n\n for (let index = 0; index < segments.length; index += 1) {\n const segment = segments[index]\n const nextSegment = segments[index + 1]\n if (segment === undefined) continue\n\n if (segment === 'content' && nextSegment !== undefined && /^\\d+$/.test(nextSegment)) {\n parts.push(`${parts.length === 0 ? 'Content' : 'content'} item ${Number(nextSegment) + 1}`)\n index += 1\n continue\n }\n\n if (segment === 'validation' && parts.length > 0) {\n parts[parts.length - 1] = `${parts[parts.length - 1]} validation`\n continue\n }\n\n parts.push(segment)\n }\n\n return parts.join(' > ')\n }\n\n private getValueAtPath(input: unknown, path: string): unknown {\n if (!path) return input\n\n return path\n .split('/')\n .filter(Boolean)\n .reduce<unknown>((value, segment) => {\n if (Array.isArray(value)) return value[Number(segment)]\n if (this.isRecord(value)) return value[segment]\n return undefined\n }, input)\n }\n\n private getParentPath(path: string): string {\n const segments = path.split('/').filter(Boolean)\n return segments.length > 1 ? `/${segments.slice(0, -1).join('/')}` : '/'\n }\n\n private getLastPathSegment(path: string): string | undefined {\n return path.split('/').filter(Boolean).at(-1)\n }\n\n private isContentItemPath(path: string): boolean {\n const segments = path.split('/').filter(Boolean)\n return segments.at(-2) === 'content' && /^\\d+$/.test(segments.at(-1) ?? '')\n }\n\n private formatArticle(value: string | undefined): string {\n if (!value) return 'a'\n\n return /^[aeiou]/i.test(value) ? 'an' : 'a'\n }\n\n private isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n }\n\n /**\n * Validates a form definition semantically.\n *\n * @param definition - The form definition to validate.\n * @param registry - The flattened field registry built from the definition.\n * @returns Array of issues found. Empty if the definition is semantically valid.\n */\n validate(definition: FormDefinition, registry: Map<number, FieldEntry>): DocumentValidationError[] {\n const issues: DocumentValidationError[] = []\n\n this.checkDuplicateIds(definition.content, issues)\n this.checkNestingDepth(definition.content, 0, issues)\n this.checkConditionRefs(registry, issues)\n this.checkConditionRefsSection(registry, issues)\n this.checkConstraintContradictions(registry, issues)\n this.checkInvalidRegex(registry, issues)\n\n return issues\n }\n\n private checkDuplicateIds(content: ContentItem[], issues: DocumentValidationError[]): void {\n const seen = new Set<number>()\n this.walkItems(content, (item) => {\n if (seen.has(item.id)) {\n issues.push({ code: 'DUPLICATE_ID', message: `Duplicate id: ${item.id}`, itemId: item.id })\n } else {\n seen.add(item.id)\n }\n })\n }\n\n private checkNestingDepth(content: ContentItem[], depth: number, issues: DocumentValidationError[]): void {\n for (const item of content) {\n if (item.type === 'section') {\n if (depth >= 3) {\n issues.push({\n code: 'NESTING_DEPTH',\n message: `Section nesting exceeds maximum depth of 3: ${item.id}`,\n itemId: item.id,\n })\n } else {\n this.checkNestingDepth(item.content, depth + 1, issues)\n }\n }\n }\n }\n\n private checkConditionRefs(registry: Map<number, FieldEntry>, issues: DocumentValidationError[]): void {\n for (const [id, entry] of registry) {\n if (!entry.condition) continue\n const refs = DependencyGraph.extractFieldRefs(entry.condition)\n for (const ref of refs) {\n if (!registry.has(ref)) {\n issues.push({\n code: 'UNKNOWN_FIELD_REF',\n message: `Condition references unknown field: ${ref} (in item ${id})`,\n itemId: id,\n })\n }\n }\n }\n }\n\n private checkConditionRefsSection(registry: Map<number, FieldEntry>, issues: DocumentValidationError[]): void {\n for (const [id, entry] of registry) {\n if (!entry.condition) continue\n const refs = DependencyGraph.extractFieldRefs(entry.condition)\n for (const ref of refs) {\n const refEntry = registry.get(ref)\n if (refEntry && refEntry.type === 'section') {\n issues.push({\n code: 'CONDITION_REFS_SECTION',\n message: `Condition references section ${ref}, which has no value (in item ${id})`,\n itemId: id,\n })\n }\n }\n }\n }\n\n private checkConstraintContradictions(registry: Map<number, FieldEntry>, issues: DocumentValidationError[]): void {\n for (const [id, entry] of registry) {\n if (!entry.validation) continue\n\n switch (entry.type) {\n case 'string': {\n const v = entry.validation as StringValidation\n if (v.minLength !== undefined && v.maxLength !== undefined && v.maxLength < v.minLength) {\n issues.push({\n code: 'INVALID_MIN_MAX',\n message: `maxLength must be >= minLength for field ${id}`,\n itemId: id,\n })\n }\n break\n }\n case 'number': {\n const v = entry.validation as NumberValidation\n if (v.min !== undefined && v.max !== undefined && v.max < v.min) {\n issues.push({\n code: 'INVALID_MIN_MAX',\n message: `max must be >= min for field ${id}`,\n itemId: id,\n })\n }\n break\n }\n case 'date': {\n const v = entry.validation as DateValidation\n if (v.minDate !== undefined && v.maxDate !== undefined) {\n const minIsAbsolute = !isRelativeDate(v.minDate)\n const maxIsAbsolute = !isRelativeDate(v.maxDate)\n if (minIsAbsolute && maxIsAbsolute) {\n if (Date.parse(v.maxDate) < Date.parse(v.minDate)) {\n issues.push({\n code: 'INVALID_MIN_MAX',\n message: `maxDate must be >= minDate for field ${id}`,\n itemId: id,\n })\n }\n }\n }\n break\n }\n case 'array': {\n const v = entry.validation as ArrayValidation\n if (v.minItems !== undefined && v.maxItems !== undefined && v.maxItems < v.minItems) {\n issues.push({\n code: 'INVALID_MIN_MAX',\n message: `maxItems must be >= minItems for field ${id}`,\n itemId: id,\n })\n }\n break\n }\n }\n }\n }\n\n private checkInvalidRegex(registry: Map<number, FieldEntry>, issues: DocumentValidationError[]): void {\n for (const [id, entry] of registry) {\n if (entry.type !== 'string' || !entry.validation) continue\n const v = entry.validation as StringValidation\n if (v.pattern === undefined) continue\n try {\n new RegExp(v.pattern)\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e)\n issues.push({\n code: 'INVALID_REGEX',\n message: `Invalid regex pattern for field ${id}: ${msg}`,\n itemId: id,\n })\n }\n }\n }\n\n private walkItems(content: ContentItem[], fn: (item: ContentItem) => void): void {\n for (const item of content) {\n fn(item)\n if (item.type === 'section') {\n this.walkItems(item.content, fn)\n }\n }\n }\n}\n","import type { DocumentValidationError } from './validation-results'\n\n/**\n * Error thrown when form definition or document validation fails.\n *\n * Inspect {@link errors} for structured programmatic access to all\n * validation issues.\n *\n * @example\n * ```ts\n * try {\n * const engine = new FormEngine(definition);\n * } catch (err) {\n * if (err instanceof DocumentError) {\n * for (const e of err.errors) {\n * console.log(e.code, e.message);\n * }\n * }\n * }\n * ```\n */\nexport class DocumentError extends Error {\n /** Structured list of all validation errors. */\n readonly errors: DocumentValidationError[]\n\n /**\n * @param errors - One or more validation errors that caused the error.\n */\n constructor(errors: DocumentValidationError[]) {\n const summary = errors.map((e) => e.message).join('; ')\n super(`Document validation failed: ${summary}`)\n this.name = 'DocumentError'\n this.errors = errors\n }\n}\n","import { ConditionEvaluator } from './condition-evaluator'\nimport type { FieldEntry } from './types/field-entry'\nimport type { FormValues } from './types/form-values'\n\n/**\n * Computes field and section visibility for a form.\n *\n * Provides two modes of visibility computation:\n * - **Single-item** (`isVisible`): evaluates one item's condition plus its\n * parent chain. Does not use the hidden-field rule.\n * - **Bulk** (`getVisibilityMap`): evaluates all items in topological order\n * with the hidden-field rule applied (references to hidden fields are\n * treated as \"not set\").\n */\nexport class VisibilityResolver {\n private readonly registry: Map<number, FieldEntry>\n private readonly conditionEvaluator: ConditionEvaluator\n private readonly topologicalOrder: number[]\n\n /**\n * @param registry - The engine's field registry.\n * @param conditionEvaluator - Evaluator for condition trees.\n * @param topologicalOrder - Item ids in topological order (from {@link DependencyGraph}).\n */\n constructor(registry: Map<number, FieldEntry>, conditionEvaluator: ConditionEvaluator, topologicalOrder: number[]) {\n this.registry = registry\n this.conditionEvaluator = conditionEvaluator\n this.topologicalOrder = topologicalOrder\n }\n\n /**\n * Determines whether a single field or section is visible.\n *\n * Evaluation logic:\n * 1. If the item has its own condition, evaluate it. If `false`, the item is hidden.\n * 2. If the item has a parent section, recursively check parent visibility.\n * An item is hidden whenever any ancestor is hidden.\n * 3. Items without conditions and without hidden parents are visible.\n *\n * Unlike {@link getVisibilityMap}, this method does not use the\n * pre-computed visibility map and does not apply the hidden-field rule.\n * Use it for one-off visibility checks; prefer `getVisibilityMap` when\n * evaluating many items at once.\n *\n * @param id - Numeric id of the field or section to check.\n * @param values - Current form values.\n * @param now - Reference date for relative date expressions.\n * @returns `true` if the item should be displayed.\n */\n isVisible(id: number, values: FormValues, now: Date): boolean {\n const entry = this.registry.get(id)\n if (!entry) return false\n\n // Check own condition\n if (entry.condition) {\n const result = this.conditionEvaluator.evalCondition(entry.condition, { values, now })\n if (!result) return false\n }\n\n // Check parent chain\n if (entry.parentId !== undefined) {\n return this.isVisible(entry.parentId, values, now)\n }\n\n return true\n }\n\n /**\n * Computes visibility for all fields and sections in a single pass.\n *\n * Iterates in topological order so that every item is evaluated after the\n * fields its condition depends on. This enables the **hidden-field rule**:\n * if a condition references a field that has already been determined hidden,\n * that field is treated as \"not set\".\n *\n * Cascading parent visibility is also enforced -- if a parent section is\n * hidden, all its children are immediately marked hidden without evaluating\n * their own conditions.\n *\n * @param values - Current form values.\n * @param now - Reference date for relative date expressions.\n * @returns Map from item id to visibility boolean (`true` = visible).\n */\n getVisibilityMap(values: FormValues, now: Date): Map<number, boolean> {\n const result = new Map<number, boolean>()\n\n for (const id of this.topologicalOrder) {\n const entry = this.registry.get(id)\n if (!entry) {\n result.set(id, false)\n continue\n }\n\n // Parent must be visible\n if (entry.parentId !== undefined && result.get(entry.parentId) === false) {\n result.set(id, false)\n continue\n }\n\n // Evaluate own condition\n if (entry.condition) {\n const visible = this.conditionEvaluator.evalCondition(entry.condition, {\n values,\n visibilityMap: result,\n now,\n })\n result.set(id, visible)\n } else {\n result.set(id, true)\n }\n }\n\n return result\n }\n}\n","import { ConditionEvaluator } from './condition-evaluator'\nimport { DependencyGraph } from './dependency-graph'\nimport { FieldValidator } from './field-validator'\nimport { FormDefinitionValidator } from './form-definition-validator'\nimport { DocumentError } from './types/errors'\nimport type { FieldEntry } from './types/field-entry'\nimport type { ContentItem, FormDefinition } from './types/form-definition'\nimport type { FormSnapshot } from './types/form-snapshot'\nimport type { FormDocument, FormValues } from './types/form-values'\nimport type { DocumentValidationError, FieldValidationError, FormValidationResult } from './types/validation-results'\nimport { VisibilityResolver } from './visibility-resolver'\n\n/**\n * The runtime form engine.\n *\n * Created by passing a {@link FormDefinition} to the constructor. The\n * construction lifecycle is:\n *\n * 1. **Build field registry** -- walks the definition tree depth-first,\n * creating a flat {@link FieldEntry} for every field and section while\n * recording document-order ids in `contentOrder`.\n * 2. **Semantic validation** -- checks for duplicate ids, excessive nesting,\n * unknown/invalid condition references, constraint contradictions, and\n * invalid regex patterns.\n * 3. **Cycle detection** -- verifies that condition dependencies form a DAG\n * (no circular references).\n * 4. **Error reporting** -- if any issues were found in steps 2-3, throws a\n * {@link DocumentError} containing all issues.\n * 5. **Build dependency graph** -- creates a forward adjacency map so the\n * engine can quickly determine which items are affected when a field\n * value changes.\n * 6. **Topological sort** -- orders all items so that dependencies are\n * evaluated before dependents (used by `getVisibilityMap`).\n * 7. **Assemble components** -- creates internal {@link ConditionEvaluator},\n * {@link VisibilityResolver}, and {@link FieldValidator} instances.\n *\n * @example\n * ```ts\n * const engine = new FormEngine(myFormDefinition);\n * const visibility = engine.getVisibilityMap(formValues);\n * const result = engine.validate(formValues);\n * ```\n */\nexport class FormEngine {\n private readonly registry: Map<number, FieldEntry>\n private readonly depGraph: DependencyGraph\n private readonly visibilityResolver: VisibilityResolver\n private readonly fieldValidator: FieldValidator\n private readonly definition: FormDefinition\n private readonly formId: string\n private readonly formVersion: string\n\n /**\n * Ordered list of all content item ids in depth-first document order.\n * Matches the order in which items appear in the form definition.\n */\n readonly contentOrder: readonly number[]\n\n /**\n * Compiles a {@link FormDefinition} into a ready-to-use engine.\n *\n * @param definition - A complete form definition to compile.\n * @throws {DocumentError} If the definition contains semantic issues\n * or circular condition dependencies.\n */\n constructor(definition: FormDefinition) {\n // 0. JSON schema validation\n const definitionValidator = new FormDefinitionValidator()\n const schemaIssues = definitionValidator.validateSchema(definition)\n if (schemaIssues.length > 0) {\n throw new DocumentError(schemaIssues)\n }\n\n // 1. Build field registry + content order\n const registry = new Map<number, FieldEntry>()\n const contentOrder: number[] = []\n FormEngine.walkContent(definition.content, undefined, registry, contentOrder)\n\n // 2. Semantic validation\n const issues = definitionValidator.validate(definition, registry)\n\n // 3. Cycle detection\n const cyclePath = DependencyGraph.detectCycle(registry)\n if (cyclePath) {\n issues.push({\n code: 'CIRCULAR_DEPENDENCY',\n message: `Circular condition dependency detected: ${cyclePath.join(' -> ')}`,\n })\n }\n\n if (issues.length > 0) {\n throw new DocumentError(issues)\n }\n\n // 4. Build dependency graph\n this.depGraph = new DependencyGraph(registry)\n\n // 5. Assemble components\n const conditionEvaluator = new ConditionEvaluator()\n this.visibilityResolver = new VisibilityResolver(registry, conditionEvaluator, this.depGraph.topologicalOrder)\n this.fieldValidator = new FieldValidator(registry)\n\n this.registry = registry\n this.contentOrder = contentOrder\n this.definition = definition\n this.formId = definition.id\n this.formVersion = definition.version\n }\n\n /**\n * Creates a {@link FormDocument} pre-populated with the form schema's\n * id and version.\n *\n * @param values - Optional initial field values. Defaults to an empty object.\n * @returns A new form document ready for use with engine methods.\n */\n createFormDocument(values?: FormValues): FormDocument {\n return {\n form: { id: this.formId, version: this.formVersion, submittedAt: new Date().toISOString() },\n values: values ?? {},\n }\n }\n\n /**\n * Serializes the form definition and document into a single {@link FormSnapshot}.\n *\n * The snapshot contains the original {@link FormDefinition} used to construct\n * the engine and the provided {@link FormDocument}. No validation is performed;\n * call {@link validate} separately if needed.\n *\n * @param doc - The form document to include in the snapshot.\n * @returns A snapshot containing both the definition and the document.\n */\n dumpDocument(doc: FormDocument): FormSnapshot {\n return {\n definition: this.definition,\n document: doc,\n }\n }\n\n /**\n * Loads a {@link FormDocument} from a previously created {@link FormSnapshot}.\n *\n * Verifies that the snapshot's form definition matches the engine's\n * compiled definition by comparing id and version. Throws a\n * {@link DocumentError} if there is a mismatch.\n *\n * @param snapshot - A snapshot previously produced by {@link dumpDocument}.\n * @returns The form document from the snapshot.\n * @throws {DocumentError} If the snapshot's definition id or version\n * does not match the engine's.\n */\n loadDocument(snapshot: FormSnapshot): FormDocument {\n const errors: DocumentValidationError[] = []\n if (snapshot.definition.id !== this.formId) {\n errors.push({\n code: 'FORM_ID_MISMATCH',\n message: `Snapshot form id \"${snapshot.definition.id}\" does not match expected \"${this.formId}\"`,\n params: { expected: this.formId, actual: snapshot.definition.id },\n })\n }\n if (snapshot.definition.version !== this.formVersion) {\n errors.push({\n code: 'FORM_VERSION_MISMATCH',\n message: `Snapshot form version \"${snapshot.definition.version}\" does not match expected \"${this.formVersion}\"`,\n params: { expected: this.formVersion, actual: snapshot.definition.version },\n })\n }\n if (errors.length > 0) {\n throw new DocumentError(errors)\n }\n return snapshot.document\n }\n\n /**\n * Determines whether a field or section is visible given the current form document.\n *\n * Evaluates the item's own condition and walks up the parent chain --\n * an item is hidden if any ancestor is hidden.\n *\n * @param id - Numeric id of the field or section.\n * @param doc - Current form document.\n * @returns `true` if the item should be displayed, `false` otherwise.\n */\n isVisible(id: number, doc: FormDocument): boolean {\n return this.visibilityResolver.isVisible(id, doc.values, FormEngine.parseNow(doc))\n }\n\n /**\n * Computes visibility for every field and section in topological order.\n *\n * The resulting map is keyed by item id. Items whose conditions depend on\n * other items are evaluated after their dependencies, ensuring correct\n * cascading visibility (e.g. a hidden parent hides all children).\n *\n * @param doc - Current form document.\n * @returns Map from item id to visibility boolean.\n */\n getVisibilityMap(doc: FormDocument): Map<number, boolean> {\n return this.visibilityResolver.getVisibilityMap(doc.values, FormEngine.parseNow(doc))\n }\n\n /**\n * Returns the set of item ids whose visibility may change when the\n * specified field's value changes.\n *\n * Includes transitive dependents -- if field A controls field B, and\n * field B controls field C, changing A returns `{B, C}`.\n * Results are cached for the lifetime of the engine.\n *\n * @param fieldId - Id of the field that changed.\n * @returns Set of affected item ids (does not include `fieldId` itself unless\n * it is part of a dependency chain).\n */\n getAffectedIds(fieldId: number): Set<number> {\n return this.depGraph.getAffectedIds(fieldId)\n }\n\n /**\n * Validates form values against the schema's validation rules.\n *\n * Only visible fields are validated -- hidden fields are skipped entirely.\n * Sections are never validated directly. For array fields, each item is\n * validated individually according to the array's item definition.\n *\n * The reference time for relative date validation is derived from\n * `doc.form.submittedAt`. If that value is missing or unparseable, a\n * document-level error is reported and `new Date()` is used as fallback.\n *\n * @param doc - Current form document to validate.\n * @returns Validation result with a `valid` flag and a `fieldErrors` map.\n */\n validate(doc: FormDocument): FormValidationResult {\n // Document compatibility check\n const documentErrors: DocumentValidationError[] = []\n if (doc.form.id !== this.formId) {\n documentErrors.push({\n code: 'FORM_ID_MISMATCH',\n message: `Document form id \"${doc.form.id}\" does not match expected \"${this.formId}\"`,\n params: { expected: this.formId, actual: doc.form.id },\n })\n }\n if (doc.form.version !== this.formVersion) {\n documentErrors.push({\n code: 'FORM_VERSION_MISMATCH',\n message: `Document form version \"${doc.form.version}\" does not match expected \"${this.formVersion}\"`,\n params: { expected: this.formVersion, actual: doc.form.version },\n })\n }\n\n // Derive `now` from submittedAt; report document errors for missing/invalid values\n let now: Date\n if (!doc.form.submittedAt) {\n documentErrors.push({\n code: 'FORM_SUBMITTED_AT_MISSING',\n message: 'Document form submittedAt is missing',\n })\n now = new Date()\n } else {\n const parsedSubmittedAt = new Date(doc.form.submittedAt)\n if (Number.isNaN(parsedSubmittedAt.getTime())) {\n documentErrors.push({\n code: 'FORM_SUBMITTED_AT_INVALID',\n message: `Document form submittedAt \"${doc.form.submittedAt}\" is not a valid date`,\n params: { actual: doc.form.submittedAt },\n })\n now = new Date()\n } else {\n now = parsedSubmittedAt\n }\n }\n\n // Document level validation errors\n\n if (documentErrors.length > 0)\n return {\n valid: false,\n fieldErrors: new Map<number, FieldValidationError[]>(),\n documentErrors,\n }\n\n // Field level validation errors\n\n const visibilityMap = this.visibilityResolver.getVisibilityMap(doc.values, now)\n return this.fieldValidator.validate(doc.values, visibilityMap, now)\n }\n\n /**\n * Retrieves the internal {@link FieldEntry} for a given id.\n *\n * @param id - Numeric id of the field or section.\n * @returns The field entry, or `undefined` if the id is not in the registry.\n */\n getFieldDef(id: number): FieldEntry | undefined {\n return this.registry.get(id)\n }\n\n private static parseNow(doc: FormDocument): Date {\n if (doc.form.submittedAt) {\n const parsed = new Date(doc.form.submittedAt)\n if (!Number.isNaN(parsed.getTime())) return parsed\n }\n return new Date()\n }\n\n private static walkContent(\n content: ContentItem[],\n parentId: number | undefined,\n registry: Map<number, FieldEntry>,\n contentOrder: number[],\n ): void {\n for (const item of content) {\n const entry: FieldEntry = {\n id: item.id,\n type: item.type,\n condition: item.condition,\n validation: item.type !== 'section' ? item.validation : undefined,\n parentId,\n options: item.type === 'select' ? item.options : undefined,\n item: item.type === 'array' ? item.item : undefined,\n label: item.type !== 'section' ? item.label : undefined,\n title: item.type === 'section' ? item.title : undefined,\n }\n\n registry.set(item.id, entry)\n contentOrder.push(item.id)\n\n if (item.type === 'section') {\n FormEngine.walkContent(item.content, item.id, registry, contentOrder)\n }\n }\n }\n}\n","import { FormEngine } from './form-engine'\nimport type { FormDefinition } from './types/form-definition'\nimport type { FormDocument } from './types/form-values'\nimport type { FormValidationResult } from './types/validation-results'\n\n/**\n * Mutable editor for building and modifying form values against a {@link FormDefinition}.\n *\n * Wraps a {@link FormEngine} and a mutable {@link FormDocument}. All mutating\n * methods return `this` for fluent chaining.\n *\n * @example\n * ```ts\n * const editor = new FormValuesEditor(definition)\n * editor\n * .setFieldValue(1, 'Alice')\n * .setFieldValue(2, 30)\n * .setSubmittedAt('2025-01-01T00:00:00Z')\n *\n * const result = editor.validate()\n * const doc = editor.toJSON()\n * ```\n */\nexport class FormValuesEditor {\n private readonly engine: FormEngine\n private doc: FormDocument\n\n /**\n * Creates a new editor for the given form definition.\n *\n * @param definition - The form definition to edit values against.\n * @param doc - An existing document to pre-populate. Deep-cloned internally.\n * When omitted a blank document is created via {@link FormEngine.createFormDocument}.\n */\n constructor(definition: FormDefinition, doc?: FormDocument) {\n this.engine = new FormEngine(definition)\n this.doc = doc ? JSON.parse(JSON.stringify(doc)) : this.engine.createFormDocument()\n }\n\n /**\n * Returns the current value of a field.\n *\n * @param fieldId - Numeric id of the field.\n * @returns The field value, or `undefined` if not set.\n */\n getFieldValue(fieldId: number): unknown {\n return this.doc.values[String(fieldId)]\n }\n\n /**\n * Sets the value of a field.\n *\n * @param fieldId - Numeric id of the field.\n * @param value - The value to set.\n * @returns `this` for chaining.\n * @throws If `fieldId` is unknown or references a section.\n */\n setFieldValue(fieldId: number, value: unknown): this {\n this.assertField(fieldId)\n this.doc.values[String(fieldId)] = value\n return this\n }\n\n /**\n * Removes the value of a field.\n *\n * @param fieldId - Numeric id of the field.\n * @returns `this` for chaining.\n */\n clearFieldValue(fieldId: number): this {\n delete this.doc.values[String(fieldId)]\n return this\n }\n\n /**\n * Appends an item to an array field.\n *\n * If the field currently has no value, it is initialized to an empty array\n * before appending.\n *\n * @param fieldId - Numeric id of the array field.\n * @param value - The value to append. Defaults to `undefined`.\n * @returns `this` for chaining.\n * @throws If `fieldId` is not an array field.\n */\n addArrayItem(fieldId: number, value?: unknown): this {\n const arr = this.getOrInitArray(fieldId)\n arr.push(value)\n return this\n }\n\n /**\n * Removes an item from an array field by index.\n *\n * @param fieldId - Numeric id of the array field.\n * @param index - Zero-based index of the item to remove.\n * @returns `this` for chaining.\n * @throws If `fieldId` is not an array field or the index is out of bounds.\n */\n removeArrayItem(fieldId: number, index: number): this {\n const arr = this.assertArray(fieldId)\n if (index < 0 || index >= arr.length) {\n throw new Error(`Index ${index} is out of bounds for array field ${fieldId} (length ${arr.length})`)\n }\n arr.splice(index, 1)\n return this\n }\n\n /**\n * Moves an item within an array field from one index to another.\n *\n * @param fieldId - Numeric id of the array field.\n * @param fromIndex - Current zero-based index of the item.\n * @param toIndex - Target zero-based index.\n * @returns `this` for chaining.\n * @throws If `fieldId` is not an array field or either index is out of bounds.\n */\n moveArrayItem(fieldId: number, fromIndex: number, toIndex: number): this {\n const arr = this.assertArray(fieldId)\n if (fromIndex < 0 || fromIndex >= arr.length) {\n throw new Error(`fromIndex ${fromIndex} is out of bounds for array field ${fieldId} (length ${arr.length})`)\n }\n if (toIndex < 0 || toIndex >= arr.length) {\n throw new Error(`toIndex ${toIndex} is out of bounds for array field ${fieldId} (length ${arr.length})`)\n }\n const [item] = arr.splice(fromIndex, 1)\n arr.splice(toIndex, 0, item)\n return this\n }\n\n /**\n * Sets the value of an item at a specific index in an array field.\n *\n * @param fieldId - Numeric id of the array field.\n * @param index - Zero-based index of the item to set.\n * @param value - The new value for the item.\n * @returns `this` for chaining.\n * @throws If `fieldId` is not an array field or the index is out of bounds.\n */\n setArrayItem(fieldId: number, index: number, value: unknown): this {\n const arr = this.assertArray(fieldId)\n if (index < 0 || index >= arr.length) {\n throw new Error(`Index ${index} is out of bounds for array field ${fieldId} (length ${arr.length})`)\n }\n arr[index] = value\n return this\n }\n\n /**\n * Sets the `submittedAt` timestamp on the document.\n *\n * @param submittedAt - ISO 8601 timestamp string.\n * @returns `this` for chaining.\n */\n setSubmittedAt(submittedAt: string): this {\n this.doc.form.submittedAt = submittedAt\n return this\n }\n\n /**\n * Validates the current document against the form definition.\n *\n * Delegates to {@link FormEngine.validate}.\n *\n * @returns The validation result.\n */\n validate(): FormValidationResult {\n return this.engine.validate(this.doc)\n }\n\n /**\n * Computes visibility for every field and section.\n *\n * Delegates to {@link FormEngine.getVisibilityMap}.\n *\n * @returns Map from item id to visibility boolean.\n */\n getVisibilityMap(): Map<number, boolean> {\n return this.engine.getVisibilityMap(this.doc)\n }\n\n /**\n * Determines whether a field or section is visible given current values.\n *\n * Delegates to {@link FormEngine.isVisible}.\n *\n * @param id - Numeric id of the field or section.\n * @returns `true` if the item should be displayed.\n */\n isVisible(id: number): boolean {\n return this.engine.isVisible(id, this.doc)\n }\n\n /**\n * Returns a deep clone of the current form document.\n *\n * @returns A new serializable {@link FormDocument} instance.\n */\n toJSON(): FormDocument {\n return JSON.parse(JSON.stringify(this.doc))\n }\n\n /**\n * Asserts that `fieldId` exists in the registry and is not a section.\n */\n private assertField(fieldId: number): void {\n const entry = this.engine.getFieldDef(fieldId)\n\n if (!entry) throw new Error(`Field with id ${fieldId} not found`)\n if (entry.type === 'section') throw new Error(`Item ${fieldId} is a section, not a field`)\n }\n\n /**\n * Asserts that `fieldId` is an array field and returns the current array value.\n * Throws if the field is not an array type or the current value is not an array.\n */\n private assertArray(fieldId: number): unknown[] {\n const entry = this.engine.getFieldDef(fieldId)\n if (!entry) throw new Error(`Field with id ${fieldId} not found`)\n\n if (entry.type !== 'array') throw new Error(`Field ${fieldId} is not an array field`)\n\n const val = this.doc.values[String(fieldId)]\n if (!Array.isArray(val)) throw new Error(`Field ${fieldId} does not currently hold an array value`)\n\n return val\n }\n\n /**\n * Returns the array value for `fieldId`, initializing to `[]` if not yet set.\n */\n private getOrInitArray(fieldId: number): unknown[] {\n const entry = this.engine.getFieldDef(fieldId)\n if (!entry) throw new Error(`Field with id ${fieldId} not found`)\n\n if (entry.type !== 'array') throw new Error(`Field ${fieldId} is not an array field`)\n\n const key = String(fieldId)\n let val = this.doc.values[key]\n if (!Array.isArray(val)) {\n val = []\n this.doc.values[key] = val\n }\n\n return val as unknown[]\n }\n}\n"],"mappings":"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"}
|