@petrarca/sonnet-core 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/format.ts","../src/jsonUtils.ts","../src/utils/copyTextToClipboard.tsx","../src/utils/snakeCaseToTitleCase.ts","../src/errorNormalizer.ts","../src/compose-refs.ts","../src/resolvePointerRanges.ts","../src/pagination.ts"],"sourcesContent":["// Reusable formatting utilities.\n// Thin wrappers around Intl for consistent output across the app.\n\nconst numberFormatter = new Intl.NumberFormat(undefined, {\n maximumFractionDigits: 0,\n});\n\nexport function formatNumber(\n value: number | string | undefined | null,\n): string {\n if (value === null || value === undefined) return \"0\";\n const num = typeof value === \"string\" ? Number(value) : value;\n if (!Number.isFinite(num)) return String(value);\n return numberFormatter.format(num as number);\n}\n\nexport function formatNumberCompact(\n value: number | string | undefined | null,\n): string {\n if (value === null || value === undefined) return \"0\";\n const num = typeof value === \"string\" ? Number(value) : value;\n if (!Number.isFinite(num)) return String(value);\n return new Intl.NumberFormat(undefined, {\n notation: \"compact\",\n maximumFractionDigits: 1,\n }).format(num as number);\n}\n\n/** Format an ISO 8601 string as a short date (e.g. \"Jan 15, 2026\"). */\nexport function formatDate(iso: string | null | undefined): string {\n if (!iso) return \"\\u2014\";\n return new Date(iso).toLocaleDateString(undefined, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n}\n\n/** Format an ISO 8601 string as date + time (e.g. \"Jan 15, 2026, 09:32 AM\"). */\nexport function formatDateTime(iso: string | null | undefined): string {\n if (!iso) return \"\\u2014\";\n return new Date(iso).toLocaleString(undefined, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n}\n\n/**\n * Format a timezone-aware ISO 8601 string preserving the original offset.\n *\n * Parses the date/time components and offset directly from the string so\n * the emitter's local time and offset are shown exactly as recorded --\n * independent of the viewer's timezone.\n *\n * Examples:\n * \"2026-02-22T10:00:00+01:00\" -> \"22 Feb 2026 at 10:00 +01:00\"\n * \"2026-02-22T09:00:00Z\" -> \"22 Feb 2026 at 09:00 UTC\"\n */\nexport function formatDateTimeTz(iso: string | null | undefined): string {\n if (!iso) return \"\\u2014\";\n\n // Match ISO 8601: date, time (HH:MM), optional seconds, offset or Z.\n const match = iso.match(\n /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::\\d{2}(?:\\.\\d+)?)?(Z|[+-]\\d{2}:\\d{2})$/,\n );\n if (!match) return iso;\n\n const [, year, month, day, hour, minute, rawOffset] = match;\n\n const months = [\n \"Jan\",\n \"Feb\",\n \"Mar\",\n \"Apr\",\n \"May\",\n \"Jun\",\n \"Jul\",\n \"Aug\",\n \"Sep\",\n \"Oct\",\n \"Nov\",\n \"Dec\",\n ];\n const monthName = months[parseInt(month, 10) - 1];\n const offset = rawOffset === \"Z\" ? \"UTC\" : rawOffset;\n\n return `${parseInt(day, 10)} ${monthName} ${year} at ${hour}:${minute} ${offset}`;\n}\n\n/** Return \"{count} {word}\" with the word pluralized when count != 1. */\nexport function pluralize(count: number, word: string): string {\n return `${count} ${word}${count !== 1 ? \"s\" : \"\"}`;\n}\n","/**\n * Escapes HTML characters to prevent XSS and rendering issues\n */\nexport function escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n/**\n * Formats a JSON value into colored HTML with proper syntax highlighting\n * @param value - The value to format (any JSON-serializable type)\n * @param indent - Current indentation level for nested objects/arrays\n * @returns HTML string with syntax highlighting\n */\nfunction jsonSpan(cssVar: string, content: string): string {\n return `<span style=\"color: var(${cssVar})\">${content}</span>`;\n}\n\nfunction formatPrimitive(value: unknown): string | null {\n if (value === null) return jsonSpan(\"--json-null\", \"null\");\n if (typeof value === \"undefined\") return jsonSpan(\"--json-null\", \"undefined\");\n if (typeof value === \"boolean\")\n return jsonSpan(\"--json-boolean\", String(value));\n if (typeof value === \"number\")\n return jsonSpan(\"--json-number\", String(value));\n if (typeof value === \"string\")\n return jsonSpan(\"--json-string\", `\"${escapeHtml(value)}\"`);\n return null;\n}\n\nfunction formatArray(value: unknown[], indent: number): string {\n if (value.length === 0) return jsonSpan(\"--json-punctuation\", \"[]\");\n const indentStr = \" \".repeat(indent);\n const items = value\n .map((item) => `${indentStr} ${formatJsonValue(item, indent + 1)}`)\n .join(\",\\n\");\n return `${jsonSpan(\"--json-punctuation\", \"[\")}\\n${items}\\n${indentStr}${jsonSpan(\"--json-punctuation\", \"]\")}`;\n}\n\nfunction formatObject(value: object, indent: number): string {\n const entries = Object.entries(value);\n if (entries.length === 0) return jsonSpan(\"--json-punctuation\", \"{}\");\n const indentStr = \" \".repeat(indent);\n const items = entries\n .map(\n ([key, val]) =>\n `${indentStr} ${jsonSpan(\"--json-key\", `\"${escapeHtml(key)}\"`)}${jsonSpan(\"--json-punctuation\", \":\")} ${formatJsonValue(val, indent + 1)}`,\n )\n .join(\",\\n\");\n return `${jsonSpan(\"--json-punctuation\", \"{\")}\\n${items}\\n${indentStr}${jsonSpan(\"--json-punctuation\", \"}\")}`;\n}\n\nexport function formatJsonValue(value: unknown, indent = 0): string {\n const primitive = formatPrimitive(value);\n if (primitive !== null) return primitive;\n if (Array.isArray(value)) return formatArray(value, indent);\n if (typeof value === \"object\" && value !== null)\n return formatObject(value, indent);\n return jsonSpan(\"--json-fallback\", escapeHtml(String(value)));\n}\n\n/**\n * CSS custom properties for JSON syntax highlighting colors\n */\nexport const JSON_CSS_VARIABLES = {\n \"--json-key\": \"#2563eb\",\n \"--json-string\": \"#16a34a\",\n \"--json-number\": \"#ea580c\",\n \"--json-boolean\": \"#2563eb\",\n \"--json-null\": \"#64748b\",\n \"--json-punctuation\": \"#475569\",\n \"--json-fallback\": \"#6b7280\",\n} as const;\n","/**\n * Copy text to the system clipboard.\n *\n * Optionally calls `onSuccess` after a successful write. The caller\n * provides its own notification mechanism (e.g. `notification.success`\n * from the shell API) to keep this utility free of sonner or any other\n * toast dependency.\n */\nexport async function copyTextToClipboard(\n text: string,\n options?: { onSuccess?: (message: string) => void },\n): Promise<void> {\n await navigator.clipboard.writeText(text);\n options?.onSuccess?.(`${text} copied to clipboard`);\n}\n","function snakeCaseToTitleCase(snakeCase: string): string {\n const words = snakeCase.split(\"_\");\n const titleCaseWords = words.map(\n (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),\n );\n return titleCaseWords.join(\" \");\n}\n\nexport default snakeCaseToTitleCase;\n","/**\n * Error normalizer -- extract typed error info from any thrown value.\n *\n * Generic utility with no framework dependencies. Handles standard\n * Error objects and Axios-shaped error responses via duck-typing\n * (no axios import required).\n */\n\nexport interface NormalizedError {\n message: string;\n status?: number;\n details?: string[];\n}\n\n/**\n * Normalize any thrown value into a structured error object.\n *\n * Handles:\n * - Axios-shaped errors (duck-typed via `isAxiosError` property)\n * - Standard Error objects\n * - Arbitrary values (stringified)\n */\n// Extract validation detail messages from a FastAPI-style detail array.\nfunction extractDetailMessages(detail: unknown[]): string[] {\n const msgs: string[] = [];\n for (const item of detail) {\n if (\n item &&\n typeof item === \"object\" &&\n typeof (item as Record<string, unknown>).msg === \"string\"\n ) {\n msgs.push((item as Record<string, unknown>).msg as string);\n }\n }\n return msgs;\n}\n\n// Extract message and details from an Axios-like response data payload.\nfunction extractFromResponseData(\n data: unknown,\n fallback: string,\n): { message: string; details: string[] } {\n if (typeof data === \"string\") return { message: data, details: [] };\n if (typeof data !== \"object\" || data === null)\n return { message: fallback, details: [] };\n\n const d = data as Record<string, unknown>;\n if (typeof d.detail === \"string\") return { message: d.detail, details: [] };\n if (Array.isArray(d.detail)) {\n const details = extractDetailMessages(d.detail);\n return { message: details.length ? details.join(\"; \") : fallback, details };\n }\n if (typeof d.error === \"string\") return { message: d.error, details: [] };\n return { message: fallback, details: [] };\n}\n\n// Normalize an Axios-like error into a structured error object.\nfunction normalizeAxiosError(err: AxiosLikeError): NormalizedError {\n const status = err.response?.status;\n const { message, details } = extractFromResponseData(\n err.response?.data,\n err.message,\n );\n return { message, status, details: details.length ? details : undefined };\n}\n\nexport function normalizeError(err: unknown): NormalizedError {\n if (isAxiosLikeError(err)) return normalizeAxiosError(err);\n if (err instanceof Error) return { message: err.message };\n return { message: String(err) };\n}\n\n// Duck-type check for Axios-like errors.\ninterface AxiosLikeError {\n isAxiosError: true;\n message: string;\n response?: {\n status?: number;\n data?: unknown;\n };\n}\n\nfunction isAxiosLikeError(err: unknown): err is AxiosLikeError {\n return (\n err !== null &&\n typeof err === \"object\" &&\n \"isAxiosError\" in err &&\n (err as Record<string, unknown>).isAxiosError === true\n );\n}\n","import * as React from \"react\";\n\ntype PossibleRef<T> = React.Ref<T> | undefined;\n\n/**\n * Set a given ref to a given value\n * This utility takes care of different types of refs: callback refs and RefObject(s)\n */\nfunction setRef<T>(ref: PossibleRef<T>, value: T) {\n if (typeof ref === \"function\") {\n return ref(value);\n }\n\n if (ref !== null && ref !== undefined) {\n ref.current = value;\n }\n}\n\n/**\n * A utility to compose multiple refs together\n * Accepts callback refs and RefObject(s)\n */\nfunction composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n return (node) => {\n let hasCleanup = false;\n const cleanups = refs.map((ref) => {\n const cleanup = setRef(ref, node);\n if (!hasCleanup && typeof cleanup === \"function\") {\n hasCleanup = true;\n }\n return cleanup;\n });\n\n // React <19 will log an error to the console if a callback ref returns a\n // value. We don't use ref cleanups internally so this will only happen if a\n // user's ref callback returns a value, which we only expect if they are\n // using the cleanup functionality added in React 19.\n if (hasCleanup) {\n return () => {\n for (let i = 0; i < cleanups.length; i++) {\n const cleanup = cleanups[i];\n if (typeof cleanup === \"function\") {\n cleanup();\n } else {\n setRef(refs[i], null);\n }\n }\n };\n }\n };\n}\n\n/**\n * A custom hook that composes multiple refs.\n * Accepts callback refs and RefObject(s).\n */\nfunction useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n // Store the latest refs in a mutable ref so the stable callback always\n // sees the current values without needing a dynamic dependency array.\n const refsRef = React.useRef(refs);\n React.useEffect(() => {\n refsRef.current = refs;\n });\n\n return React.useCallback((node: T) => {\n composeRefs(...refsRef.current)(node);\n }, []);\n}\n\nexport { composeRefs, useComposedRefs };\n","/**\n * Resolve JSON Pointer (RFC 6901) paths to character offset ranges\n * within a formatted JSON string.\n *\n * Given a JSON string and a list of JSON Pointers, returns the\n * [from, to] byte ranges covering the *value* of each pointer.\n * The range includes the full value (object braces, array brackets,\n * string quotes, nested content) so the user can freely edit\n * everything inside.\n *\n * Zero external dependencies — pure TypeScript.\n * Maps to @petrarca/sonnet-core on extraction.\n */\n\nexport interface OffsetRange {\n /** JSON Pointer that was resolved */\n pointer: string;\n /** Start character offset (inclusive) */\n from: number;\n /** End character offset (exclusive) */\n to: number;\n}\n\n/**\n * Unescape a JSON Pointer token per RFC 6901:\n * ~1 -> /\n * ~0 -> ~\n */\nfunction unescapeToken(token: string): string {\n return token.replace(/~1/g, \"/\").replace(/~0/g, \"~\");\n}\n\n/**\n * Parse a JSON Pointer string into an array of unescaped tokens.\n * \"\" returns [] (root document, per RFC 6901)\n * \"/\" returns [] (sentinel for \"entire document\" -- treated as root)\n * \"/a/b\" returns [\"a\", \"b\"]\n */\nfunction parsePointer(pointer: string): string[] {\n if (pointer === \"\" || pointer === \"/\") return [];\n if (!pointer.startsWith(\"/\")) {\n throw new Error(`Invalid JSON Pointer: \"${pointer}\" (must start with /)`);\n }\n return pointer.slice(1).split(\"/\").map(unescapeToken);\n}\n\n/**\n * Find the character offset range of a value at the given path\n * within a JSON string. Walks the string character by character\n * to find the key, then captures the full value range.\n */\n/** Mutable cursor for walking a JSON string character by character. */\ninterface Cursor {\n pos: number;\n json: string;\n}\n\nfunction skipWs(c: Cursor): void {\n while (c.pos < c.json.length && /\\s/.test(c.json[c.pos])) c.pos++;\n}\n\nfunction skipString(c: Cursor): number {\n c.pos++; // skip opening \"\n while (c.pos < c.json.length) {\n if (c.json[c.pos] === \"\\\\\") {\n c.pos += 2;\n continue;\n }\n if (c.json[c.pos] === '\"') {\n c.pos++;\n return c.pos;\n }\n c.pos++;\n }\n return c.pos;\n}\n\nfunction skipContainer(c: Cursor, open: string, close: string): number {\n c.pos++; // skip opening bracket\n skipWs(c);\n if (c.json[c.pos] === close) {\n c.pos++;\n return c.pos;\n }\n while (c.pos < c.json.length) {\n if (open === \"{\") {\n skipValue(c); // key\n skipWs(c);\n if (c.json[c.pos] === \":\") c.pos++;\n }\n skipValue(c);\n skipWs(c);\n if (c.json[c.pos] === \",\") {\n c.pos++;\n skipWs(c);\n continue;\n }\n if (c.json[c.pos] === close) {\n c.pos++;\n return c.pos;\n }\n break;\n }\n return c.pos;\n}\n\nfunction skipValue(c: Cursor): number {\n skipWs(c);\n if (c.pos >= c.json.length) return c.pos;\n const ch = c.json[c.pos];\n if (ch === '\"') return skipString(c);\n if (ch === \"{\") return skipContainer(c, \"{\", \"}\");\n if (ch === \"[\") return skipContainer(c, \"[\", \"]\");\n // Number, bool, null -- consume until delimiter\n while (c.pos < c.json.length && !/[,\\]}\\s]/.test(c.json[c.pos])) c.pos++;\n return c.pos;\n}\n\n/** Read and return the next JSON string key, advancing past the closing quote. */\nfunction readKey(c: Cursor): string {\n const keyStart = c.pos + 1;\n c.pos++;\n while (c.pos < c.json.length) {\n if (c.json[c.pos] === \"\\\\\") {\n c.pos += 2;\n continue;\n }\n if (c.json[c.pos] === '\"') break;\n c.pos++;\n }\n const key = c.json.slice(keyStart, c.pos);\n c.pos++; // skip closing \"\n return key;\n}\n\n/** Navigate into a JSON object to the value at `token`. */\nfunction navigateObject(c: Cursor, token: string): boolean {\n c.pos++; // skip {\n skipWs(c);\n while (c.pos < c.json.length && c.json[c.pos] !== \"}\") {\n if (c.json[c.pos] !== '\"') return false;\n const key = readKey(c);\n skipWs(c);\n if (c.json[c.pos] === \":\") c.pos++;\n skipWs(c);\n if (key === token) return true;\n skipValue(c);\n skipWs(c);\n if (c.json[c.pos] === \",\") {\n c.pos++;\n skipWs(c);\n }\n }\n return false;\n}\n\n/** Navigate into a JSON array to the element at `index`. */\nfunction navigateArray(c: Cursor, index: number): boolean {\n c.pos++; // skip [\n skipWs(c);\n for (let j = 0; j < index; j++) {\n if (c.pos >= c.json.length || c.json[c.pos] === \"]\") return false;\n skipValue(c);\n skipWs(c);\n if (c.json[c.pos] === \",\") {\n c.pos++;\n skipWs(c);\n }\n }\n return true;\n}\n\nfunction findValueRange(\n json: string,\n tokens: string[],\n): { from: number; to: number } | null {\n const c: Cursor = { pos: 0, json };\n\n for (const token of tokens) {\n skipWs(c);\n if (c.json[c.pos] === \"{\") {\n if (!navigateObject(c, token)) return null;\n } else if (c.json[c.pos] === \"[\") {\n const index = parseInt(token, 10);\n if (isNaN(index) || !navigateArray(c, index)) return null;\n } else {\n return null;\n }\n }\n\n skipWs(c);\n const from = c.pos;\n const to = skipValue(c);\n return { from, to };\n}\n\n/**\n * Resolve a list of JSON Pointers to character offset ranges.\n *\n * @param json - The formatted JSON string\n * @param pointers - Array of JSON Pointer strings (e.g. \"/metadata\", \"/definitions/Patient\")\n * @returns Array of resolved ranges sorted by position (unresolvable pointers are silently skipped)\n */\nexport function resolvePointerRanges(\n json: string,\n pointers: string[],\n): OffsetRange[] {\n const ranges: OffsetRange[] = [];\n\n for (const pointer of pointers) {\n const tokens = parsePointer(pointer);\n const range = findValueRange(json, tokens);\n if (range) {\n ranges.push({ pointer, from: range.from, to: range.to });\n }\n }\n\n // Sort by position so consumers can walk ranges in document order\n ranges.sort((a, b) => a.from - b.from);\n return ranges;\n}\n","/**\n * Pagination types for paginated list responses.\n *\n * Generic shapes with no application-specific content.\n * Extractable to @petrarca/sonnet-core: no app-specific imports.\n */\n\n/** Pagination metadata included in paginated list responses. */\nexport interface PageMeta {\n total: number;\n limit: number;\n offset: number;\n has_next: boolean;\n has_prev: boolean;\n}\n\n/** Paginated list response envelope. */\nexport interface PageResult<T> {\n data: T[];\n meta: PageMeta;\n}\n\n/**\n * Wrap a full array as a single-page PageResult.\n *\n * Bridges \"regular\" (un-paginated) list APIs that return a bare array into the\n * PageResult envelope that paginated consumers (e.g. EntityTable) expect. The\n * whole array is treated as one complete page: total = length, offset = 0, and\n * no next/prev page. Use for small lists served without server-side pagination.\n */\nexport function toPageResult<T>(rows: T[]): PageResult<T> {\n return {\n data: rows,\n meta: {\n total: rows.length,\n limit: rows.length,\n offset: 0,\n has_next: false,\n has_prev: false,\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/format.ts","../src/dateParser.ts","../src/timeParser.ts","../src/localeFormat.ts","../src/jsonUtils.ts","../src/utils/copyTextToClipboard.tsx","../src/utils/snakeCaseToTitleCase.ts","../src/errorNormalizer.ts","../src/compose-refs.ts","../src/resolvePointerRanges.ts","../src/pagination.ts"],"sourcesContent":["// Reusable formatting utilities.\n// Thin wrappers around Intl for consistent output across the app.\n\nconst numberFormatter = new Intl.NumberFormat(undefined, {\n maximumFractionDigits: 0,\n});\n\nexport function formatNumber(\n value: number | string | undefined | null,\n): string {\n if (value === null || value === undefined) return \"0\";\n const num = typeof value === \"string\" ? Number(value) : value;\n if (!Number.isFinite(num)) return String(value);\n return numberFormatter.format(num as number);\n}\n\nexport function formatNumberCompact(\n value: number | string | undefined | null,\n): string {\n if (value === null || value === undefined) return \"0\";\n const num = typeof value === \"string\" ? Number(value) : value;\n if (!Number.isFinite(num)) return String(value);\n return new Intl.NumberFormat(undefined, {\n notation: \"compact\",\n maximumFractionDigits: 1,\n }).format(num as number);\n}\n\n/** Format an ISO 8601 string as a short date (e.g. \"Jan 15, 2026\"). */\nexport function formatDate(iso: string | null | undefined): string {\n if (!iso) return \"\\u2014\";\n return new Date(iso).toLocaleDateString(undefined, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n });\n}\n\n/** Format an ISO 8601 string as date + time (e.g. \"Jan 15, 2026, 09:32 AM\"). */\nexport function formatDateTime(iso: string | null | undefined): string {\n if (!iso) return \"\\u2014\";\n return new Date(iso).toLocaleString(undefined, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n}\n\n/**\n * Format a timezone-aware ISO 8601 string preserving the original offset.\n *\n * Parses the date/time components and offset directly from the string so\n * the emitter's local time and offset are shown exactly as recorded --\n * independent of the viewer's timezone.\n *\n * Examples:\n * \"2026-02-22T10:00:00+01:00\" -> \"22 Feb 2026 at 10:00 +01:00\"\n * \"2026-02-22T09:00:00Z\" -> \"22 Feb 2026 at 09:00 UTC\"\n */\nexport function formatDateTimeTz(iso: string | null | undefined): string {\n if (!iso) return \"\\u2014\";\n\n // Match ISO 8601: date, time (HH:MM), optional seconds, offset or Z.\n const match = iso.match(\n /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2})(?::\\d{2}(?:\\.\\d+)?)?(Z|[+-]\\d{2}:\\d{2})$/,\n );\n if (!match) return iso;\n\n const [, year, month, day, hour, minute, rawOffset] = match;\n\n const months = [\n \"Jan\",\n \"Feb\",\n \"Mar\",\n \"Apr\",\n \"May\",\n \"Jun\",\n \"Jul\",\n \"Aug\",\n \"Sep\",\n \"Oct\",\n \"Nov\",\n \"Dec\",\n ];\n const monthName = months[parseInt(month, 10) - 1];\n const offset = rawOffset === \"Z\" ? \"UTC\" : rawOffset;\n\n return `${parseInt(day, 10)} ${monthName} ${year} at ${hour}:${minute} ${offset}`;\n}\n\n/** Return \"{count} {word}\" with the word pluralized when count != 1. */\nexport function pluralize(count: number, word: string): string {\n return `${count} ${word}${count !== 1 ? \"s\" : \"\"}`;\n}\n","/**\n * Date parsing and formatting -- pure functional API.\n *\n * The public surface is a set of pure functions. A `CompiledDateFormat`\n * pre-computes the format structure so repeated parse/format calls (e.g.\n * on every keystroke) don't re-derive component positions each time. Call\n * `compileDateFormat` once in a `useMemo` and pass the result to\n * `parseDate` / `formatDateInput`.\n *\n * The parser operates on native `Date` values internally. Widgets and form\n * data exchange locale-independent ISO 8601 strings -- use\n * `formatIsoDate` / `parseIsoDate` at the boundary.\n */\n\n// ---- Types ------------------------------------------------------------------\n\n/**\n * Pre-compiled date format. Opaque to callers; produced by\n * {@link compileDateFormat} and consumed by {@link parseDate} and\n * {@link formatDateInput}.\n */\nexport interface CompiledDateFormat {\n readonly format: string;\n readonly yearThreshold: number;\n readonly dayIndex: number;\n readonly monthIndex: number;\n readonly yearIndex: number;\n readonly has2DigitYear: boolean;\n readonly validSeparators: readonly string[];\n}\n\n/** Options for {@link compileDateFormat}. */\nexport interface DateFormatOptions {\n /**\n * Component order for parsing and display. Supported tokens: `DD` (day),\n * `MM` (month), `YYYY` or `YY` (year). Default: `\"DD/MM/YYYY\"`.\n */\n format?: string;\n\n /**\n * Threshold for expanding 2-digit years. If a 2-digit year is greater than\n * this value it is treated as the prior century, otherwise the current one.\n * Special cases: `0` always current century, `-1` always prior century.\n * Default: the current year modulo 100, captured at compile time.\n */\n yearThreshold?: number;\n}\n\n/** Result of {@link parseDate}. */\nexport interface DateParseResult {\n /** Normalized display string (formatted per the compiled format), or \"\" on failure. */\n value: string;\n /** Parsed `Date` object, or null on failure. Needed by calendar components. */\n date: Date | null;\n /** Error message on failure, or \"\" on success. */\n error: string;\n}\n\n/**\n * Precision of a partial ISO 8601 date:\n * year -> \"YYYY\"\n * month -> \"YYYY-MM\"\n * day -> \"YYYY-MM-DD\"\n */\nexport type DatePrecision = \"year\" | \"month\" | \"day\";\n\n// ---- Compilation ------------------------------------------------------------\n\nconst VALID_SEPARATORS = [\"/\", \"-\", \".\"] as const;\n\n/**\n * Pre-compile a date format string into an opaque `CompiledDateFormat`.\n *\n * Call once per format string (e.g. in `useMemo`) and pass the result to\n * {@link parseDate} and {@link formatDateInput}.\n */\nexport function compileDateFormat(\n options: DateFormatOptions = {},\n): CompiledDateFormat {\n const format = options.format ?? \"DD/MM/YYYY\";\n const yearThreshold = options.yearThreshold ?? new Date().getFullYear() % 100;\n\n const parts = format.split(/[^A-Z]+/);\n const dayIndex = parts.findIndex((p) => p === \"DD\");\n const monthIndex = parts.findIndex((p) => p === \"MM\");\n const yearIndex = parts.findIndex((p) => p === \"YYYY\" || p === \"YY\");\n const has2DigitYear = yearIndex !== -1 && parts[yearIndex] === \"YY\";\n\n if (dayIndex === -1 && monthIndex === -1 && yearIndex === -1) {\n throw new Error(\n \"Invalid date format. Must contain at least one of DD, MM, or YYYY/YY.\",\n );\n }\n\n return {\n format,\n yearThreshold,\n dayIndex,\n monthIndex,\n yearIndex,\n has2DigitYear,\n validSeparators: VALID_SEPARATORS,\n };\n}\n\n// ---- Parsing ----------------------------------------------------------------\n\n// Strip characters that are not digits, separators, or sign markers.\nfunction cleanInput(input: string, fmt: CompiledDateFormat): string {\n return input.replace(\n new RegExp(\n `[^0-9${fmt.validSeparators.map((s) => \"\\\\\" + s).join(\"\")}+-]`,\n \"g\",\n ),\n \"\",\n );\n}\n\n// Expand a year string, using the century captured at compile time.\nfunction expandYear(yearStr: string, fmt: CompiledDateFormat): number {\n const year = parseInt(yearStr, 10);\n if (yearStr.length >= 4) return year;\n\n // Century is derived from the yearThreshold captured at compileDateFormat\n // time, so both year and century use the same reference point.\n const refYear =\n fmt.yearThreshold + Math.floor(new Date().getFullYear() / 100) * 100;\n const century = Math.floor(refYear / 100) * 100;\n\n if (fmt.yearThreshold === 0) return century + year;\n if (fmt.yearThreshold === -1) return century - 100 + year;\n return year > fmt.yearThreshold ? century - 100 + year : century + year;\n}\n\n// Parse colon/separator-split input assigning parts by component position.\nfunction parseWithSeparators(\n input: string,\n fmt: CompiledDateFormat,\n): { day: number | null; month: number | null; year: number | null } {\n const parts = input.split(\n new RegExp(`[${fmt.validSeparators.map((s) => \"\\\\\" + s).join(\"\")}]`),\n );\n const result = {\n day: null as number | null,\n month: null as number | null,\n year: null as number | null,\n };\n for (let i = 0; i < parts.length; i++) {\n if (!parts[i]) continue;\n if (i === fmt.dayIndex) result.day = parseInt(parts[i], 10);\n else if (i === fmt.monthIndex) result.month = parseInt(parts[i], 10);\n else if (i === fmt.yearIndex) result.year = expandYear(parts[i], fmt);\n }\n return result;\n}\n\n// Parse no-separator input by slicing fixed-width components in format order.\nfunction parseWithoutSeparators(\n input: string,\n fmt: CompiledDateFormat,\n): { day: number | null; month: number | null; year: number | null } {\n const result = {\n day: null as number | null,\n month: null as number | null,\n year: null as number | null,\n };\n const yearLength = fmt.has2DigitYear ? 2 : 4;\n const components = [\n { type: \"day\", index: fmt.dayIndex, length: 2 },\n { type: \"month\", index: fmt.monthIndex, length: 2 },\n { type: \"year\", index: fmt.yearIndex, length: yearLength },\n ]\n .filter((c) => c.index !== -1)\n .sort((a, b) => a.index - b.index);\n\n let remaining = input;\n for (const c of components) {\n if (!remaining.length) break;\n const len = Math.min(c.length, remaining.length);\n const val = remaining.substring(0, len);\n remaining = remaining.substring(len);\n if (c.type === \"day\") result.day = parseInt(val, 10);\n else if (c.type === \"month\") result.month = parseInt(val, 10);\n else if (c.type === \"year\") result.year = expandYear(val, fmt);\n }\n return result;\n}\n\n// Number of days in the given 1-based month, accounting for leap years.\nfunction daysInMonth(month: number, year: number): number {\n if (month === 2) {\n const leap = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n return leap ? 29 : 28;\n }\n return [4, 6, 9, 11].includes(month) ? 30 : 31;\n}\n\nfunction isValidDate(day: number, month: number, year: number): boolean {\n if (month < 1 || month > 12) return false;\n if (year < 1900 || year > 2100) return false;\n if (day < 1 || day > daysInMonth(month, year)) return false;\n return true;\n}\n\n// Fill missing components from today, validate, and build the result.\nfunction buildDateResult(\n parsed: { day: number | null; month: number | null; year: number | null },\n fmt: CompiledDateFormat,\n): DateParseResult {\n const today = new Date();\n const day = parsed.day ?? today.getDate();\n const month = parsed.month ?? today.getMonth() + 1;\n const year = parsed.year ?? today.getFullYear();\n\n if (!isValidDate(day, month, year)) {\n return { value: \"\", date: null, error: \"Invalid date\" };\n }\n\n const date = new Date(year, month - 1, day);\n return { value: formatDateInput(date, fmt), date, error: \"\" };\n}\n\n/**\n * Parse a user-entered date string against a compiled format.\n *\n * Accepts colon/separator-split and compact numeric input. The shorthands\n * `\".\"`, `\"t\"`, and `\"today\"` resolve to the current date. Empty input\n * returns `{ value: \"\", date: null, error: \"\" }`.\n */\nexport function parseDate(\n input: string,\n fmt: CompiledDateFormat,\n): DateParseResult {\n if (!input || !input.trim()) return { value: \"\", date: null, error: \"\" };\n\n if ([\".\", \"t\", \"today\"].includes(input.trim().toLowerCase())) {\n const today = new Date();\n return { value: formatDateInput(today, fmt), date: today, error: \"\" };\n }\n\n const cleaned = cleanInput(input, fmt);\n if (!cleaned) {\n return { value: \"\", date: null, error: \"Invalid characters in date\" };\n }\n\n const hasSeparators = fmt.validSeparators.some((s) => cleaned.includes(s));\n\n try {\n const parsed = hasSeparators\n ? parseWithSeparators(cleaned, fmt)\n : parseWithoutSeparators(cleaned, fmt);\n return buildDateResult(parsed, fmt);\n } catch (e) {\n return {\n value: \"\",\n date: null,\n error: e instanceof Error ? e.message : \"Invalid date format\",\n };\n }\n}\n\n// ---- Formatting -------------------------------------------------------------\n\n/**\n * Format a `Date` as a display string using the compiled format.\n * Returns \"\" for null or invalid dates.\n */\nexport function formatDateInput(\n date: Date | null | undefined,\n fmt: CompiledDateFormat,\n): string {\n if (!date || isNaN(date.getTime())) return \"\";\n const tokens: Record<string, string> = {\n DD: date.getDate().toString().padStart(2, \"0\"),\n MM: (date.getMonth() + 1).toString().padStart(2, \"0\"),\n YYYY: date.getFullYear().toString(),\n YY: date.getFullYear().toString().slice(-2),\n };\n return fmt.format.replace(/DD|MM|YYYY|YY/g, (m) => tokens[m]);\n}\n\n/** Whether two dates fall on the same calendar day. */\nexport function isDateEqual(a: Date | null, b: Date | null): boolean {\n if (!a || !b) return false;\n return (\n a.getDate() === b.getDate() &&\n a.getMonth() === b.getMonth() &&\n a.getFullYear() === b.getFullYear()\n );\n}\n\n// ---- ISO boundary helpers ---------------------------------------------------\n\n/**\n * Convert a local `Date` to a partial ISO date string at the given precision\n * (default `\"day\"`), using local components (no timezone shift).\n * Returns \"\" for null or invalid dates.\n */\nexport function formatIsoDate(\n date: Date | null | undefined,\n precision: DatePrecision = \"day\",\n): string {\n if (!date || isNaN(date.getTime())) return \"\";\n const year = date.getFullYear().toString().padStart(4, \"0\");\n if (precision === \"year\") return year;\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\n if (precision === \"month\") return `${year}-${month}`;\n const day = date.getDate().toString().padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n}\n\n/**\n * Parse a partial ISO date string (`YYYY`, `YYYY-MM`, or `YYYY-MM-DD`) into a\n * local `Date`. Missing month/day default to January/1st. Returns null for\n * empty or malformed input.\n */\nexport function parseIsoDate(iso: string | null | undefined): Date | null {\n if (!iso) return null;\n const match = iso.match(/^(\\d{4})(?:-(\\d{2})(?:-(\\d{2}))?)?$/);\n if (!match) return null;\n const [, year, month, day] = match;\n const date = new Date(\n Number(year),\n month ? Number(month) - 1 : 0,\n day ? Number(day) : 1,\n );\n return isNaN(date.getTime()) ? null : date;\n}\n\n/**\n * Detect the precision of a partial ISO date string.\n * Returns `\"day\"` for empty, malformed, or full-date input.\n */\nexport function isoDatePrecision(\n iso: string | null | undefined,\n): DatePrecision {\n if (!iso) return \"day\";\n if (/^\\d{4}$/.test(iso)) return \"year\";\n if (/^\\d{4}-\\d{2}$/.test(iso)) return \"month\";\n return \"day\";\n}\n","/**\n * Time parsing and formatting -- pure functional API.\n *\n * A time-of-day is a zone-free wall-clock value (Temporal.PlainTime): it\n * carries no date and no timezone. Values are normalized ISO time strings:\n * `\"HH:mm\"` (minute granularity) or `\"HH:mm:ss\"` (second granularity).\n *\n * API conventions match dateParser.ts:\n * parseTime(input, granularity) -> TimeParseResult { value, error }\n * formatIsoTime(h, m, s, granularity) -> string\n * normalizeTime(iso, granularity) -> string\n * An empty or failed result is represented as `value: \"\"`, not null.\n */\n\n/** Granularity of a time value: minutes or seconds. */\nexport type TimeGranularity = \"minute\" | \"second\";\n\n/** Result of {@link parseTime}. */\nexport interface TimeParseResult {\n /** Normalized ISO time string, or \"\" when parsing failed or input is empty. */\n value: string;\n /** Error message on failure, or \"\" on success. */\n error: string;\n}\n\n// Split a numeric-only run (e.g. \"0930\", \"213005\") into H/M/S parts.\nfunction splitDigits(digits: string): number[] {\n if (digits.length <= 2) return [Number(digits)];\n if (digits.length <= 4) {\n return [\n Number(digits.slice(0, digits.length - 2)),\n Number(digits.slice(-2)),\n ];\n }\n return [\n Number(digits.slice(0, digits.length - 4)),\n Number(digits.slice(-4, -2)),\n Number(digits.slice(-2)),\n ];\n}\n\n// Extract [hour, minute, second] from a cleaned input string.\nfunction extractParts(input: string): number[] {\n if (input.includes(\":\")) {\n const parts = input.split(\":\");\n // Reject trailing colon or more than 3 segments.\n if (parts.length > 3 || parts[parts.length - 1] === \"\") {\n return [];\n }\n return parts.map((p) => Number(p));\n }\n return splitDigits(input);\n}\n\n// Whether the H/M/S components form a valid 24-hour time of day.\nfunction isValidTime(hour: number, minute: number, second: number): boolean {\n const inRange = (n: number, max: number): boolean =>\n Number.isInteger(n) && n >= 0 && n <= max;\n return inRange(hour, 23) && inRange(minute, 59) && inRange(second, 59);\n}\n\n/**\n * Format H/M/S integer components as a normalized ISO time string.\n * This is an internal building block; consumers typically use {@link normalizeTime}.\n */\nexport function formatIsoTime(\n hour: number,\n minute: number,\n second: number,\n granularity: TimeGranularity = \"minute\",\n): string {\n const hh = hour.toString().padStart(2, \"0\");\n const mm = minute.toString().padStart(2, \"0\");\n if (granularity === \"minute\") return `${hh}:${mm}`;\n const ss = second.toString().padStart(2, \"0\");\n return `${hh}:${mm}:${ss}`;\n}\n\n/**\n * Parse a user-entered time string into a normalized ISO time value.\n *\n * Accepts colon-separated (`\"9:30\"`, `\"21:05:00\"`) and compact numeric\n * (`\"0930\"`, `\"213005\"`) forms. Missing minutes/seconds default to zero.\n * The shorthands `\".\"`, `\"n\"`, and `\"now\"` resolve to the current time.\n * Empty input returns `{ value: \"\", error: \"\" }`.\n */\nexport function parseTime(\n input: string,\n granularity: TimeGranularity = \"minute\",\n): TimeParseResult {\n if (!input || !input.trim()) return { value: \"\", error: \"\" };\n\n // \".\", \"n\", and \"now\" are shorthand for the current time.\n if ([\".\", \"n\", \"now\"].includes(input.trim().toLowerCase())) {\n const now = new Date();\n return {\n value: formatIsoTime(\n now.getHours(),\n now.getMinutes(),\n now.getSeconds(),\n granularity,\n ),\n error: \"\",\n };\n }\n\n const cleaned = input.replace(/[^0-9:]/g, \"\");\n if (!cleaned) return { value: \"\", error: \"Invalid characters in time\" };\n\n return parseCleanedTime(cleaned, granularity);\n}\n\nfunction parseCleanedTime(\n cleaned: string,\n granularity: TimeGranularity,\n): TimeParseResult {\n const parts = extractParts(cleaned);\n if (!parts.length) return { value: \"\", error: \"Invalid time format\" };\n\n const hour = parts[0] ?? 0;\n const minute = parts[1] ?? 0;\n const second = parts[2] ?? 0;\n\n if (!isValidTime(hour, minute, second)) {\n return { value: \"\", error: \"Invalid time\" };\n }\n\n return { value: formatIsoTime(hour, minute, second, granularity), error: \"\" };\n}\n\n/**\n * Normalize an ISO time string (`\"HH:mm\"` or `\"HH:mm:ss\"`) to the requested\n * granularity. Returns \"\" for empty or malformed input.\n */\nexport function normalizeTime(\n iso: string | null | undefined,\n granularity: TimeGranularity = \"minute\",\n): string {\n if (!iso) return \"\";\n return parseTime(iso, granularity).value;\n}\n\n/**\n * Split a plain wall-clock datetime (`\"YYYY-MM-DDTHH:mm[:ss]\"`) into its date\n * (`\"YYYY-MM-DD\"`) and time (`\"HH:mm[:ss]\"`) halves. Missing halves return \"\".\n */\nexport function splitDateTime(iso: string | null | undefined): {\n date: string;\n time: string;\n} {\n if (!iso) return { date: \"\", time: \"\" };\n const [date, time] = iso.split(\"T\");\n return { date: date ?? \"\", time: time ?? \"\" };\n}\n\n/**\n * Join a date (`\"YYYY-MM-DD\"`) and time (`\"HH:mm[:ss]\"`) into a plain\n * wall-clock datetime. Returns \"\" when the date is missing; a missing time\n * yields the date alone.\n */\nexport function joinDateTime(date: string, time: string): string {\n if (!date) return \"\";\n return time ? `${date}T${time}` : date;\n}\n","/**\n * Locale-derived date formatting metadata.\n *\n * Uses the Intl APIs (no dependency) to derive, for a BCP-47 locale:\n * - the day/month/year order and separator, as a DateParser format string\n * - localized short weekday labels\n * - the first day of the week (0 = Sunday .. 6 = Saturday)\n *\n * These drive display and input parsing only; exchanged values stay ISO.\n */\n\nimport type { DatePrecision } from \"./dateParser\";\n\n/** DateParser format string per precision, derived for a locale. */\nexport interface LocaleDateFormats {\n year: string;\n month: string;\n day: string;\n}\n\n// Build the day-precision format (e.g. \"MM/DD/YYYY\") from Intl parts.\nfunction dayFormatFromParts(locale: string): string {\n const parts = new Intl.DateTimeFormat(locale, {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n }).formatToParts(new Date(2023, 0, 2));\n\n const tokens: string[] = [];\n let separator = \"/\";\n for (const part of parts) {\n if (part.type === \"day\") tokens.push(\"DD\");\n else if (part.type === \"month\") tokens.push(\"MM\");\n else if (part.type === \"year\") tokens.push(\"YYYY\");\n else if (part.type === \"literal\") {\n // First non-space literal is the field separator.\n const trimmed = part.value.trim();\n if (trimmed) separator = trimmed[0];\n }\n }\n\n if (tokens.length !== 3) return \"DD/MM/YYYY\";\n return tokens.join(separator);\n}\n\n/** Derive DateParser format strings for each precision in the given locale. */\nexport function getLocaleDateFormats(locale: string): LocaleDateFormats {\n const day = dayFormatFromParts(locale);\n // Month precision drops the day token and its trailing separator.\n const month = day.replace(/DD.|.DD/, \"\");\n return { year: \"YYYY\", month, day };\n}\n\n/** Derive the DateParser format for a single precision in the given locale. */\nexport function getLocaleDateFormat(\n locale: string,\n precision: DatePrecision,\n): string {\n return getLocaleDateFormats(locale)[precision];\n}\n\n/**\n * Localized short weekday labels ordered from the locale's first day of week.\n * Returns 7 entries, e.g. `[\"Mon\",\"Tue\",...]` or `[\"Sun\",\"Mon\",...]`.\n */\nexport function getWeekdayLabels(locale: string): string[] {\n const first = getFirstDayOfWeek(locale);\n const formatter = new Intl.DateTimeFormat(locale, { weekday: \"short\" });\n // 2023-01-01 is a Sunday; offset by the locale's first day.\n return Array.from({ length: 7 }, (_, i) =>\n formatter.format(new Date(2023, 0, 1 + ((first + i) % 7))),\n );\n}\n\n// Intl.Locale week info is not yet in the TS lib types; describe the shape we\n// read. Both the getWeekInfo() method and the weekInfo property exist across\n// engines.\ninterface WeekInfo {\n firstDay?: number;\n}\ninterface LocaleWithWeekInfo {\n getWeekInfo?: () => WeekInfo;\n weekInfo?: WeekInfo;\n}\n\n/** First day of the week for the locale: 0 = Sunday .. 6 = Saturday. */\nexport function getFirstDayOfWeek(locale: string): number {\n try {\n const loc = new Intl.Locale(locale) as unknown as LocaleWithWeekInfo;\n const info = loc.getWeekInfo?.() ?? loc.weekInfo;\n if (info?.firstDay !== undefined) {\n // Intl weekInfo uses 1 = Monday .. 7 = Sunday; map to 0 = Sunday.\n return info.firstDay === 7 ? 0 : info.firstDay;\n }\n } catch {\n // Fall through to default when Intl.Locale/weekInfo is unavailable.\n }\n return 1; // Monday is the most common global default.\n}\n","/**\n * Escapes HTML characters to prevent XSS and rendering issues\n */\nexport function escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n/**\n * Formats a JSON value into colored HTML with proper syntax highlighting\n * @param value - The value to format (any JSON-serializable type)\n * @param indent - Current indentation level for nested objects/arrays\n * @returns HTML string with syntax highlighting\n */\nfunction jsonSpan(cssVar: string, content: string): string {\n return `<span style=\"color: var(${cssVar})\">${content}</span>`;\n}\n\nfunction formatPrimitive(value: unknown): string | null {\n if (value === null) return jsonSpan(\"--json-null\", \"null\");\n if (typeof value === \"undefined\") return jsonSpan(\"--json-null\", \"undefined\");\n if (typeof value === \"boolean\")\n return jsonSpan(\"--json-boolean\", String(value));\n if (typeof value === \"number\")\n return jsonSpan(\"--json-number\", String(value));\n if (typeof value === \"string\")\n return jsonSpan(\"--json-string\", `\"${escapeHtml(value)}\"`);\n return null;\n}\n\nfunction formatArray(value: unknown[], indent: number): string {\n if (value.length === 0) return jsonSpan(\"--json-punctuation\", \"[]\");\n const indentStr = \" \".repeat(indent);\n const items = value\n .map((item) => `${indentStr} ${formatJsonValue(item, indent + 1)}`)\n .join(\",\\n\");\n return `${jsonSpan(\"--json-punctuation\", \"[\")}\\n${items}\\n${indentStr}${jsonSpan(\"--json-punctuation\", \"]\")}`;\n}\n\nfunction formatObject(value: object, indent: number): string {\n const entries = Object.entries(value);\n if (entries.length === 0) return jsonSpan(\"--json-punctuation\", \"{}\");\n const indentStr = \" \".repeat(indent);\n const items = entries\n .map(\n ([key, val]) =>\n `${indentStr} ${jsonSpan(\"--json-key\", `\"${escapeHtml(key)}\"`)}${jsonSpan(\"--json-punctuation\", \":\")} ${formatJsonValue(val, indent + 1)}`,\n )\n .join(\",\\n\");\n return `${jsonSpan(\"--json-punctuation\", \"{\")}\\n${items}\\n${indentStr}${jsonSpan(\"--json-punctuation\", \"}\")}`;\n}\n\nexport function formatJsonValue(value: unknown, indent = 0): string {\n const primitive = formatPrimitive(value);\n if (primitive !== null) return primitive;\n if (Array.isArray(value)) return formatArray(value, indent);\n if (typeof value === \"object\" && value !== null)\n return formatObject(value, indent);\n return jsonSpan(\"--json-fallback\", escapeHtml(String(value)));\n}\n\n/**\n * CSS custom properties for JSON syntax highlighting colors\n */\nexport const JSON_CSS_VARIABLES = {\n \"--json-key\": \"#2563eb\",\n \"--json-string\": \"#16a34a\",\n \"--json-number\": \"#ea580c\",\n \"--json-boolean\": \"#2563eb\",\n \"--json-null\": \"#64748b\",\n \"--json-punctuation\": \"#475569\",\n \"--json-fallback\": \"#6b7280\",\n} as const;\n","/**\n * Copy text to the system clipboard.\n *\n * Optionally calls `onSuccess` after a successful write. The caller\n * provides its own notification mechanism (e.g. `notification.success`\n * from the shell API) to keep this utility free of sonner or any other\n * toast dependency.\n */\nexport async function copyTextToClipboard(\n text: string,\n options?: { onSuccess?: (message: string) => void },\n): Promise<void> {\n await navigator.clipboard.writeText(text);\n options?.onSuccess?.(`${text} copied to clipboard`);\n}\n","function snakeCaseToTitleCase(snakeCase: string): string {\n const words = snakeCase.split(\"_\");\n const titleCaseWords = words.map(\n (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(),\n );\n return titleCaseWords.join(\" \");\n}\n\nexport default snakeCaseToTitleCase;\n","/**\n * Error normalizer -- extract typed error info from any thrown value.\n *\n * Generic utility with no framework dependencies. Handles standard\n * Error objects and Axios-shaped error responses via duck-typing\n * (no axios import required).\n */\n\nexport interface NormalizedError {\n message: string;\n status?: number;\n details?: string[];\n}\n\n/**\n * Normalize any thrown value into a structured error object.\n *\n * Handles:\n * - Axios-shaped errors (duck-typed via `isAxiosError` property)\n * - Standard Error objects\n * - Arbitrary values (stringified)\n */\n// Extract validation detail messages from a FastAPI-style detail array.\nfunction extractDetailMessages(detail: unknown[]): string[] {\n const msgs: string[] = [];\n for (const item of detail) {\n if (\n item &&\n typeof item === \"object\" &&\n typeof (item as Record<string, unknown>).msg === \"string\"\n ) {\n msgs.push((item as Record<string, unknown>).msg as string);\n }\n }\n return msgs;\n}\n\n// Extract message and details from an Axios-like response data payload.\nfunction extractFromResponseData(\n data: unknown,\n fallback: string,\n): { message: string; details: string[] } {\n if (typeof data === \"string\") return { message: data, details: [] };\n if (typeof data !== \"object\" || data === null)\n return { message: fallback, details: [] };\n\n const d = data as Record<string, unknown>;\n if (typeof d.detail === \"string\") return { message: d.detail, details: [] };\n if (Array.isArray(d.detail)) {\n const details = extractDetailMessages(d.detail);\n return { message: details.length ? details.join(\"; \") : fallback, details };\n }\n if (typeof d.error === \"string\") return { message: d.error, details: [] };\n return { message: fallback, details: [] };\n}\n\n// Normalize an Axios-like error into a structured error object.\nfunction normalizeAxiosError(err: AxiosLikeError): NormalizedError {\n const status = err.response?.status;\n const { message, details } = extractFromResponseData(\n err.response?.data,\n err.message,\n );\n return { message, status, details: details.length ? details : undefined };\n}\n\nexport function normalizeError(err: unknown): NormalizedError {\n if (isAxiosLikeError(err)) return normalizeAxiosError(err);\n if (err instanceof Error) return { message: err.message };\n return { message: String(err) };\n}\n\n// Duck-type check for Axios-like errors.\ninterface AxiosLikeError {\n isAxiosError: true;\n message: string;\n response?: {\n status?: number;\n data?: unknown;\n };\n}\n\nfunction isAxiosLikeError(err: unknown): err is AxiosLikeError {\n return (\n err !== null &&\n typeof err === \"object\" &&\n \"isAxiosError\" in err &&\n (err as Record<string, unknown>).isAxiosError === true\n );\n}\n","import * as React from \"react\";\n\ntype PossibleRef<T> = React.Ref<T> | undefined;\n\n/**\n * Set a given ref to a given value\n * This utility takes care of different types of refs: callback refs and RefObject(s)\n */\nfunction setRef<T>(ref: PossibleRef<T>, value: T) {\n if (typeof ref === \"function\") {\n return ref(value);\n }\n\n if (ref !== null && ref !== undefined) {\n ref.current = value;\n }\n}\n\n/**\n * A utility to compose multiple refs together\n * Accepts callback refs and RefObject(s)\n */\nfunction composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n return (node) => {\n let hasCleanup = false;\n const cleanups = refs.map((ref) => {\n const cleanup = setRef(ref, node);\n if (!hasCleanup && typeof cleanup === \"function\") {\n hasCleanup = true;\n }\n return cleanup;\n });\n\n // React <19 will log an error to the console if a callback ref returns a\n // value. We don't use ref cleanups internally so this will only happen if a\n // user's ref callback returns a value, which we only expect if they are\n // using the cleanup functionality added in React 19.\n if (hasCleanup) {\n return () => {\n for (let i = 0; i < cleanups.length; i++) {\n const cleanup = cleanups[i];\n if (typeof cleanup === \"function\") {\n cleanup();\n } else {\n setRef(refs[i], null);\n }\n }\n };\n }\n };\n}\n\n/**\n * A custom hook that composes multiple refs.\n * Accepts callback refs and RefObject(s).\n */\nfunction useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {\n // Store the latest refs in a mutable ref so the stable callback always\n // sees the current values without needing a dynamic dependency array.\n const refsRef = React.useRef(refs);\n React.useEffect(() => {\n refsRef.current = refs;\n });\n\n return React.useCallback((node: T) => {\n composeRefs(...refsRef.current)(node);\n }, []);\n}\n\nexport { composeRefs, useComposedRefs };\n","/**\n * Resolve JSON Pointer (RFC 6901) paths to character offset ranges\n * within a formatted JSON string.\n *\n * Given a JSON string and a list of JSON Pointers, returns the\n * [from, to] byte ranges covering the *value* of each pointer.\n * The range includes the full value (object braces, array brackets,\n * string quotes, nested content) so the user can freely edit\n * everything inside.\n *\n * Zero external dependencies — pure TypeScript.\n * Maps to @petrarca/sonnet-core on extraction.\n */\n\nexport interface OffsetRange {\n /** JSON Pointer that was resolved */\n pointer: string;\n /** Start character offset (inclusive) */\n from: number;\n /** End character offset (exclusive) */\n to: number;\n}\n\n/**\n * Unescape a JSON Pointer token per RFC 6901:\n * ~1 -> /\n * ~0 -> ~\n */\nfunction unescapeToken(token: string): string {\n return token.replace(/~1/g, \"/\").replace(/~0/g, \"~\");\n}\n\n/**\n * Parse a JSON Pointer string into an array of unescaped tokens.\n * \"\" returns [] (root document, per RFC 6901)\n * \"/\" returns [] (sentinel for \"entire document\" -- treated as root)\n * \"/a/b\" returns [\"a\", \"b\"]\n */\nfunction parsePointer(pointer: string): string[] {\n if (pointer === \"\" || pointer === \"/\") return [];\n if (!pointer.startsWith(\"/\")) {\n throw new Error(`Invalid JSON Pointer: \"${pointer}\" (must start with /)`);\n }\n return pointer.slice(1).split(\"/\").map(unescapeToken);\n}\n\n/**\n * Find the character offset range of a value at the given path\n * within a JSON string. Walks the string character by character\n * to find the key, then captures the full value range.\n */\n/** Mutable cursor for walking a JSON string character by character. */\ninterface Cursor {\n pos: number;\n json: string;\n}\n\nfunction skipWs(c: Cursor): void {\n while (c.pos < c.json.length && /\\s/.test(c.json[c.pos])) c.pos++;\n}\n\nfunction skipString(c: Cursor): number {\n c.pos++; // skip opening \"\n while (c.pos < c.json.length) {\n if (c.json[c.pos] === \"\\\\\") {\n c.pos += 2;\n continue;\n }\n if (c.json[c.pos] === '\"') {\n c.pos++;\n return c.pos;\n }\n c.pos++;\n }\n return c.pos;\n}\n\nfunction skipContainer(c: Cursor, open: string, close: string): number {\n c.pos++; // skip opening bracket\n skipWs(c);\n if (c.json[c.pos] === close) {\n c.pos++;\n return c.pos;\n }\n while (c.pos < c.json.length) {\n if (open === \"{\") {\n skipValue(c); // key\n skipWs(c);\n if (c.json[c.pos] === \":\") c.pos++;\n }\n skipValue(c);\n skipWs(c);\n if (c.json[c.pos] === \",\") {\n c.pos++;\n skipWs(c);\n continue;\n }\n if (c.json[c.pos] === close) {\n c.pos++;\n return c.pos;\n }\n break;\n }\n return c.pos;\n}\n\nfunction skipValue(c: Cursor): number {\n skipWs(c);\n if (c.pos >= c.json.length) return c.pos;\n const ch = c.json[c.pos];\n if (ch === '\"') return skipString(c);\n if (ch === \"{\") return skipContainer(c, \"{\", \"}\");\n if (ch === \"[\") return skipContainer(c, \"[\", \"]\");\n // Number, bool, null -- consume until delimiter\n while (c.pos < c.json.length && !/[,\\]}\\s]/.test(c.json[c.pos])) c.pos++;\n return c.pos;\n}\n\n/** Read and return the next JSON string key, advancing past the closing quote. */\nfunction readKey(c: Cursor): string {\n const keyStart = c.pos + 1;\n c.pos++;\n while (c.pos < c.json.length) {\n if (c.json[c.pos] === \"\\\\\") {\n c.pos += 2;\n continue;\n }\n if (c.json[c.pos] === '\"') break;\n c.pos++;\n }\n const key = c.json.slice(keyStart, c.pos);\n c.pos++; // skip closing \"\n return key;\n}\n\n/** Navigate into a JSON object to the value at `token`. */\nfunction navigateObject(c: Cursor, token: string): boolean {\n c.pos++; // skip {\n skipWs(c);\n while (c.pos < c.json.length && c.json[c.pos] !== \"}\") {\n if (c.json[c.pos] !== '\"') return false;\n const key = readKey(c);\n skipWs(c);\n if (c.json[c.pos] === \":\") c.pos++;\n skipWs(c);\n if (key === token) return true;\n skipValue(c);\n skipWs(c);\n if (c.json[c.pos] === \",\") {\n c.pos++;\n skipWs(c);\n }\n }\n return false;\n}\n\n/** Navigate into a JSON array to the element at `index`. */\nfunction navigateArray(c: Cursor, index: number): boolean {\n c.pos++; // skip [\n skipWs(c);\n for (let j = 0; j < index; j++) {\n if (c.pos >= c.json.length || c.json[c.pos] === \"]\") return false;\n skipValue(c);\n skipWs(c);\n if (c.json[c.pos] === \",\") {\n c.pos++;\n skipWs(c);\n }\n }\n return true;\n}\n\nfunction findValueRange(\n json: string,\n tokens: string[],\n): { from: number; to: number } | null {\n const c: Cursor = { pos: 0, json };\n\n for (const token of tokens) {\n skipWs(c);\n if (c.json[c.pos] === \"{\") {\n if (!navigateObject(c, token)) return null;\n } else if (c.json[c.pos] === \"[\") {\n const index = parseInt(token, 10);\n if (isNaN(index) || !navigateArray(c, index)) return null;\n } else {\n return null;\n }\n }\n\n skipWs(c);\n const from = c.pos;\n const to = skipValue(c);\n return { from, to };\n}\n\n/**\n * Resolve a list of JSON Pointers to character offset ranges.\n *\n * @param json - The formatted JSON string\n * @param pointers - Array of JSON Pointer strings (e.g. \"/metadata\", \"/definitions/Patient\")\n * @returns Array of resolved ranges sorted by position (unresolvable pointers are silently skipped)\n */\nexport function resolvePointerRanges(\n json: string,\n pointers: string[],\n): OffsetRange[] {\n const ranges: OffsetRange[] = [];\n\n for (const pointer of pointers) {\n const tokens = parsePointer(pointer);\n const range = findValueRange(json, tokens);\n if (range) {\n ranges.push({ pointer, from: range.from, to: range.to });\n }\n }\n\n // Sort by position so consumers can walk ranges in document order\n ranges.sort((a, b) => a.from - b.from);\n return ranges;\n}\n","/**\n * Pagination types for paginated list responses.\n *\n * Generic shapes with no application-specific content.\n * Extractable to @petrarca/sonnet-core: no app-specific imports.\n */\n\n/** Pagination metadata included in paginated list responses. */\nexport interface PageMeta {\n total: number;\n limit: number;\n offset: number;\n has_next: boolean;\n has_prev: boolean;\n}\n\n/** Paginated list response envelope. */\nexport interface PageResult<T> {\n data: T[];\n meta: PageMeta;\n}\n\n/**\n * Wrap a full array as a single-page PageResult.\n *\n * Bridges \"regular\" (un-paginated) list APIs that return a bare array into the\n * PageResult envelope that paginated consumers (e.g. EntityTable) expect. The\n * whole array is treated as one complete page: total = length, offset = 0, and\n * no next/prev page. Use for small lists served without server-side pagination.\n */\nexport function toPageResult<T>(rows: T[]): PageResult<T> {\n return {\n data: rows,\n meta: {\n total: rows.length,\n limit: rows.length,\n offset: 0,\n has_next: false,\n has_prev: false,\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,3 +1,4 @@
1
+ import Ajv from 'ajv';
1
2
  import { z } from 'zod';
2
3
 
3
4
  /**
@@ -7,6 +8,37 @@ import { z } from 'zod';
7
8
  * for form/widget integration. They are domain-agnostic -- no
8
9
  * application-specific entity concepts live here.
9
10
  */
11
+ /**
12
+ * A declarative presentation rule (JSONForms-style) attached to a field.
13
+ *
14
+ * The rule lives on the field it controls (the target). Its `condition` points
15
+ * at another field's value via a JSON Pointer (RFC 6901) into the form DATA
16
+ * (rooted at the whole form), and validates that value against `condition.schema`.
17
+ * When the condition holds, `effect` is applied to the target:
18
+ * - "show" -- render only when the condition holds
19
+ * - "hide" -- render only when the condition does NOT hold
20
+ * - "enable" -- interactive only when the condition holds
21
+ * - "disable"-- non-interactive when the condition holds
22
+ *
23
+ * Example: show `pregnancy_status` when `gender` equals "female":
24
+ * ```json
25
+ * { "effect": "show",
26
+ * "condition": { "scope": "/gender", "schema": { "const": "female" } } }
27
+ * ```
28
+ * Unlike if/then/else, the target field stays declared at its own position, so
29
+ * layout placement is stable.
30
+ */
31
+ interface UIRuleCondition {
32
+ /** JSON Pointer (RFC 6901) into the form data, e.g. "/gender" or "/patient/gender". */
33
+ scope: string;
34
+ /** Schema the value at `scope` is validated against (e.g. { const: "female" }). */
35
+ schema: JsonSchemaProperty;
36
+ }
37
+ type UIRuleEffect = "show" | "hide" | "enable" | "disable";
38
+ interface UIRule {
39
+ effect: UIRuleEffect;
40
+ condition: UIRuleCondition;
41
+ }
10
42
  /**
11
43
  * JSON Schema property definition following Draft 2020-12 specification.
12
44
  * Supports basic types, validation constraints, conditionals, and custom extensions.
@@ -24,20 +56,34 @@ type JsonSchemaProperty = {
24
56
  maxLength?: number;
25
57
  minimum?: number;
26
58
  maximum?: number;
59
+ exclusiveMinimum?: number;
60
+ exclusiveMaximum?: number;
27
61
  items?: JsonSchemaProperty;
28
62
  minItems?: number;
29
63
  maxItems?: number;
30
64
  properties?: Record<string, JsonSchemaProperty>;
31
65
  required?: string[];
32
66
  $ref?: string;
67
+ $defs?: Record<string, JsonSchemaProperty>;
68
+ definitions?: Record<string, JsonSchemaProperty>;
33
69
  allOf?: JsonSchemaProperty[];
34
70
  anyOf?: JsonSchemaProperty[];
35
71
  oneOf?: JsonSchemaProperty[];
36
72
  not?: JsonSchemaProperty;
73
+ /**
74
+ * Discriminator for a `oneOf` union (OpenAPI convention). `propertyName` is
75
+ * the shared property whose `const` value tags the active branch. When
76
+ * absent, the discriminator is auto-detected as the property that is const
77
+ * in every branch.
78
+ */
79
+ discriminator?: {
80
+ propertyName: string;
81
+ };
37
82
  if?: JsonSchemaProperty;
38
83
  then?: JsonSchemaProperty;
39
84
  else?: JsonSchemaProperty;
40
85
  "x-ui-order"?: string[];
86
+ "x-ui-rule"?: UIRule;
41
87
  "x-ui-widget"?: string;
42
88
  "x-ui-options"?: Record<string, any>;
43
89
  "x-ui-layout"?: UILayoutConfig;
@@ -113,6 +159,8 @@ interface UISchema {
113
159
  "x-ui-options"?: UISchemaOptions;
114
160
  "x-ui-globalOptions"?: UISchemaOptions;
115
161
  "x-ui-order"?: string[];
162
+ /** Declarative show/hide or enable/disable rule (see UIRule). */
163
+ "x-ui-rule"?: UIRule;
116
164
  "x-ui-title"?: string | false;
117
165
  "x-ui-description"?: string;
118
166
  "x-ui-placeholder"?: string;
@@ -180,6 +228,31 @@ interface UILayoutRowConfig {
180
228
  */
181
229
  arrange?: "grid" | "flow";
182
230
  }
231
+ /**
232
+ * One named group of a grouped form layout. A group holds rows of fields under
233
+ * a label; the data stays flat (grouping is presentation only, like `group` on
234
+ * a single row). How groups are shown -- tabs, wizard steps, stacked sections
235
+ * -- is chosen by {@link UILayoutConfig.variant}, not by this structure.
236
+ *
237
+ * A group may carry an `x-ui-rule`-style {@link UIRule} to appear conditionally
238
+ * (e.g. show an "Address" group only when "provide address" is checked). The
239
+ * rule is evaluated against the form data exactly as a field-level rule is.
240
+ */
241
+ interface UILayoutGroupConfig {
242
+ /** Label shown on the group's trigger or heading. */
243
+ label: string;
244
+ /** Rows of fields in this group (same shape as UILayoutConfig.rows). */
245
+ rows: UILayoutRowConfig[];
246
+ /**
247
+ * Optional show/hide rule for the whole group: a hidden group disappears
248
+ * along with its fields. Narrowed to the visibility effects -- a disabled
249
+ * group has no meaning, and permitting the effect would let it be authored
250
+ * and then silently ignored.
251
+ */
252
+ rule?: UIRule & {
253
+ effect: "show" | "hide";
254
+ };
255
+ }
183
256
  /**
184
257
  * UILayoutConfig -- layout configuration for ObjectWidget and ArrayWidget.
185
258
  *
@@ -189,6 +262,8 @@ interface UILayoutRowConfig {
189
262
  interface UILayoutConfig {
190
263
  direction?: "vertical" | "horizontal";
191
264
  rows?: UILayoutRowConfig[];
265
+ groups?: UILayoutGroupConfig[];
266
+ variant?: "tabs" | "stepper" | "sections" | (string & {});
192
267
  columns?: string;
193
268
  gap?: number;
194
269
  labels?: boolean;
@@ -263,6 +338,8 @@ interface UISchemaOptions {
263
338
  rows?: number;
264
339
  searchable?: boolean;
265
340
  clearable?: boolean;
341
+ /** Allow the user to type and submit a value not in the enum list (combobox / open-choice). */
342
+ creatable?: boolean;
266
343
  /** Label placement: "top" (default, above) or "start" (inline, RTL-safe). */
267
344
  labelPlacement?: "top" | "start";
268
345
  /** Semantic field-slot width (default "full"). */
@@ -326,6 +403,38 @@ declare function getRequiredFields(schema: JsonSchema): string[];
326
403
  declare function getOptionalFields(schema: JsonSchema): string[];
327
404
  declare function hasProperties(schema: JsonSchema): boolean;
328
405
 
406
+ /**
407
+ * oneOf (discriminated-union) helpers.
408
+ *
409
+ * A oneOf property is an object whose shape is one of several branches, modeled
410
+ * as `oneOf: [branchSchema, ...]`. Each branch shares a discriminator property
411
+ * -- a field with a `const` value that tags which branch is active. The
412
+ * discriminator is named by `discriminator.propertyName` (OpenAPI convention),
413
+ * or auto-detected as the property that carries a `const` in every branch.
414
+ *
415
+ * These helpers are pure and domain-agnostic.
416
+ */
417
+
418
+ /** Whether a property is a oneOf union (has a non-empty `oneOf`). */
419
+ declare function isOneOf(property: JsonSchemaProperty): boolean;
420
+ /**
421
+ * Resolve the discriminator property name for a oneOf union.
422
+ *
423
+ * Uses `discriminator.propertyName` when set; otherwise auto-detects the single
424
+ * property that carries a `const` in every branch. Returns undefined when no
425
+ * consistent discriminator can be determined.
426
+ */
427
+ declare function getDiscriminator(property: JsonSchemaProperty): string | undefined;
428
+ /** The discriminator const value for a branch, or undefined. */
429
+ declare function getBranchValue(branch: JsonSchemaProperty, discriminator: string): string | number | boolean | undefined;
430
+ /** Human label for a branch: its `title`, else the discriminator value. */
431
+ declare function getBranchLabel(branch: JsonSchemaProperty, discriminator: string | undefined, index: number): string;
432
+ /**
433
+ * Find the branch that matches the current form data by comparing the
434
+ * discriminator value. Returns the branch index, or -1 when none match.
435
+ */
436
+ declare function findActiveBranch(property: JsonSchemaProperty, data: Record<string, unknown> | undefined, discriminator: string | undefined): number;
437
+
329
438
  /**
330
439
  * Schema Resolver - Resolves conditional schemas (if-then-else, allOf, etc.)
331
440
  *
@@ -342,6 +451,7 @@ declare function hasProperties(schema: JsonSchema): boolean;
342
451
  * @module services/schemaResolver
343
452
  */
344
453
 
454
+ declare const ajv: Ajv;
345
455
  /**
346
456
  * Resolve conditional schema based on form data
347
457
  *
@@ -412,6 +522,33 @@ declare function matchesConst(value: any, constValue: any): boolean;
412
522
  */
413
523
  declare function hasConditionals(schema: JsonSchema): boolean;
414
524
 
525
+ /**
526
+ * Declarative UI rules (x-ui-rule): show/hide or enable/disable a field based
527
+ * on another field's value.
528
+ *
529
+ * The rule lives on the target field. Its condition references a value elsewhere
530
+ * in the form data via a JSON Pointer (RFC 6901) rooted at the whole form, and
531
+ * validates that value against a schema. Evaluation yields presentation flags
532
+ * (visible / enabled) the renderer applies -- the field itself stays declared at
533
+ * its own position, so layout is stable (unlike if/then/else).
534
+ */
535
+
536
+ /** The presentation state a rule resolves to for its target field. */
537
+ interface RuleState {
538
+ visible: boolean;
539
+ enabled: boolean;
540
+ }
541
+ /**
542
+ * Resolve a JSON Pointer against a data object. Returns the value at the pointer,
543
+ * or undefined when any segment is missing. "" (empty pointer) returns the root.
544
+ */
545
+ declare function getAtPointer(data: unknown, pointer: string): unknown;
546
+ /**
547
+ * Resolve a field's presentation state from its `x-ui-rule` (if any) against the
548
+ * current form data. Fields without a rule are visible and enabled.
549
+ */
550
+ declare function evaluateUiRule(rule: UIRule | undefined, formData: unknown): RuleState;
551
+
415
552
  /**
416
553
  * Schema utilities
417
554
  *
@@ -518,42 +655,67 @@ declare function generateJsonSchemaFromVariables(variables: string[], existingSc
518
655
  declare function toJsonSchema(schema: z.ZodType): JsonSchema;
519
656
 
520
657
  /**
521
- * $ref Resolution -- resolves local JSON Schema references.
658
+ * $ref Resolution -- resolves JSON Schema references.
522
659
  *
523
- * Walks the schema tree and replaces { "$ref": "#/$defs/name" } nodes
524
- * with the corresponding sub-schema from the document's $defs (Draft
525
- * 2020-12) or definitions (Draft 7) block.
660
+ * Walks the schema tree and replaces `{ "$ref": ... }` nodes with the
661
+ * corresponding sub-schema. By default it resolves local, in-document refs:
662
+ * `#/$defs/name` (Draft 2020-12) or `#/definitions/name` (Draft 7).
526
663
  *
527
- * Properties on the referencing node are merged on top of the resolved
528
- * target, so context-specific overrides (e.g. a custom title) work
529
- * without duplicating the definition.
664
+ * A caller may inject a `RefResolver` to resolve ANY ref string (e.g. a schema
665
+ * registry id like `sdf_...`, or a canonical URI). The injected resolver is
666
+ * tried first; the in-document `$defs`/`definitions` lookup is the fallback, so
667
+ * both ref styles work together. The resolver is synchronous: consumers that
668
+ * fetch schemas asynchronously (e.g. from a registry over HTTP) pre-load them
669
+ * into a map and pass a `(ref) => map[ref]` lookup. This keeps the toolkit pure
670
+ * (no HTTP, no async).
530
671
  *
531
- * Only local refs (starting with "#/") are supported. External URIs
532
- * are left as-is -- a future pluggable SchemaResolver interface will
533
- * handle those.
672
+ * Properties on the referencing node are merged on top of the resolved target,
673
+ * so context-specific overrides (e.g. a custom title) work without duplicating
674
+ * the definition. Refs that no resolver can satisfy are left as-is.
534
675
  */
535
676
 
677
+ /** Resolve a `$ref` string to its target sub-schema, or undefined if unknown. */
678
+ type RefResolver = (ref: string) => JsonSchemaProperty | undefined;
536
679
  /**
537
- * Resolve all local $ref pointers in a JSON Schema document.
680
+ * Resolve `$ref` pointers in a JSON Schema document.
538
681
  *
539
- * Returns the schema unchanged if it contains no $defs/definitions
540
- * or no $ref nodes. The original schema object is never mutated.
682
+ * By default resolves in-document `#/$defs` / `#/definitions` refs. Pass a
683
+ * `resolve` to also resolve registry/URI refs; it is tried first and the
684
+ * in-document lookup is the fallback. The original schema is never mutated;
685
+ * a document with no refs to resolve is returned unchanged.
541
686
  */
542
- declare function resolveRefs(schema: JsonSchema): JsonSchema;
687
+ declare function resolveRefs(schema: JsonSchema, resolve?: RefResolver): JsonSchema;
543
688
 
544
689
  /**
545
690
  * Recursive form data validation against a JSON Schema.
546
691
  *
547
692
  * Validates required fields at every nesting level: top-level properties,
548
693
  * nested objects (type: "object"), and array items (type: "array" with
549
- * items.type: "object"). Returns a flat list of human-readable error
550
- * strings and a boolean summary.
694
+ * items.type: "object"). Returns human-readable error strings, the same
695
+ * errors keyed by JSON Pointer, and a boolean summary.
696
+ *
697
+ * The pointer form lets callers attribute an error to a specific field --
698
+ * needed to surface errors that are not currently on screen (e.g. a badge on
699
+ * a form tab whose fields are hidden). `errors` remains the flat message list.
551
700
  *
552
701
  * Pure function -- no React, no side effects.
553
702
  */
554
703
 
704
+ /** A validation error attributed to the field that produced it. */
705
+ interface FieldError {
706
+ /**
707
+ * JSON Pointer (RFC 6901) to the offending field, e.g. `/email`,
708
+ * `/contact/email`, or `/tags/2/label` for the third array item.
709
+ */
710
+ pointer: string;
711
+ /** Human-readable message, e.g. "Contact > Email is required". */
712
+ message: string;
713
+ }
555
714
  interface ValidationResult {
715
+ /** Flat, human-readable messages (in field declaration order). */
556
716
  errors: string[];
717
+ /** The same errors, each attributed to its field via a JSON Pointer. */
718
+ fieldErrors: FieldError[];
557
719
  isValid: boolean;
558
720
  }
559
721
  /**
@@ -579,4 +741,4 @@ declare function validateFormData(schema: JsonSchema, data: Record<string, unkno
579
741
  */
580
742
  declare function diffFormData(original: Record<string, unknown>, updated: Record<string, unknown>): FormDiff;
581
743
 
582
- export { type ArrayItemChange, type FieldDiff, type FormDiff, type JsonSchema, type JsonSchemaProperty, type UILayoutConfig, type UILayoutRowConfig, type UISchema, type UISchemaOptions, type ValidationResult, type ValueChange, WIDTH_TOKEN_REMS, type WidthToken, applySchemaDefaults, diffFormData, extractEnumValues, extractSchemaDefaults, generateJsonSchemaFromVariables, getFieldConstraints, getFieldDefault, getFieldHelpText, getFieldPlaceholder, getFieldTitle, getFieldType, getFieldWidget, getOptionalFields, getRequiredFields, getSchemaProperties, hasConditionals, hasProperties, isFieldOptional, isFieldRequired, isNullableBoolean, matchesConst, resolveRefs, resolveSchema, toJsonSchema, validateFormData, validateSchema };
744
+ export { type ArrayItemChange, type FieldDiff, type FieldError, type FormDiff, type JsonSchema, type JsonSchemaProperty, type RefResolver, type RuleState, type UILayoutConfig, type UILayoutGroupConfig, type UILayoutRowConfig, type UIRule, type UIRuleCondition, type UIRuleEffect, type UISchema, type UISchemaOptions, type ValidationResult, type ValueChange, WIDTH_TOKEN_REMS, type WidthToken, ajv, applySchemaDefaults, diffFormData, evaluateUiRule, extractEnumValues, extractSchemaDefaults, findActiveBranch, generateJsonSchemaFromVariables, getAtPointer, getBranchLabel, getBranchValue, getDiscriminator, getFieldConstraints, getFieldDefault, getFieldHelpText, getFieldPlaceholder, getFieldTitle, getFieldType, getFieldWidget, getOptionalFields, getRequiredFields, getSchemaProperties, hasConditionals, hasProperties, isFieldOptional, isFieldRequired, isNullableBoolean, isOneOf, matchesConst, resolveRefs, resolveSchema, toJsonSchema, validateFormData, validateSchema };