form-snapshots 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,7 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { SyntheticEvent, ReactNode } from 'react';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
- import { UseFormReturn } from 'react-hook-form';
5
4
  import { Dexie, EntityTable } from 'dexie';
6
5
 
7
6
  type FormSnapshot = Record<string, unknown>;
@@ -159,7 +158,15 @@ declare function FormSnapshotTable({ data, emptyMessage, className, }: Readonly<
159
158
  type WithoutSnapshotOverrides = Omit<FormSnapshotsOptions, "getValues" | "applyValues">;
160
159
  interface RHFSnapshotsOptions extends WithoutSnapshotOverrides {
161
160
  }
162
- declare function useRHFFormSnapshots<TFieldValues extends Record<string, unknown>>(formName: string, form: UseFormReturn<TFieldValues>, options?: RHFSnapshotsOptions): {
161
+ /**
162
+ * Minimal subset of a React Hook Form instance that this library needs.
163
+ * Kept structural so consumers are not forced to install `react-hook-form`.
164
+ */
165
+ interface RHFFormLike<TFieldValues extends Record<string, unknown>> {
166
+ getValues: () => TFieldValues;
167
+ reset: (values: TFieldValues) => void;
168
+ }
169
+ declare function useRHFFormSnapshots<TFieldValues extends Record<string, unknown>>(formName: string, form: RHFFormLike<TFieldValues>, options?: RHFSnapshotsOptions): {
163
170
  formRef: react.RefObject<HTMLFormElement>;
164
171
  handleBlur: () => void;
165
172
  wrapSubmit: (userSubmit?: (e: react.SyntheticEvent<HTMLFormElement>) => void) => (e: react.SyntheticEvent<HTMLFormElement>) => void;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { SyntheticEvent, ReactNode } from 'react';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
- import { UseFormReturn } from 'react-hook-form';
5
4
  import { Dexie, EntityTable } from 'dexie';
6
5
 
7
6
  type FormSnapshot = Record<string, unknown>;
@@ -159,7 +158,15 @@ declare function FormSnapshotTable({ data, emptyMessage, className, }: Readonly<
159
158
  type WithoutSnapshotOverrides = Omit<FormSnapshotsOptions, "getValues" | "applyValues">;
160
159
  interface RHFSnapshotsOptions extends WithoutSnapshotOverrides {
161
160
  }
162
- declare function useRHFFormSnapshots<TFieldValues extends Record<string, unknown>>(formName: string, form: UseFormReturn<TFieldValues>, options?: RHFSnapshotsOptions): {
161
+ /**
162
+ * Minimal subset of a React Hook Form instance that this library needs.
163
+ * Kept structural so consumers are not forced to install `react-hook-form`.
164
+ */
165
+ interface RHFFormLike<TFieldValues extends Record<string, unknown>> {
166
+ getValues: () => TFieldValues;
167
+ reset: (values: TFieldValues) => void;
168
+ }
169
+ declare function useRHFFormSnapshots<TFieldValues extends Record<string, unknown>>(formName: string, form: RHFFormLike<TFieldValues>, options?: RHFSnapshotsOptions): {
163
170
  formRef: react.RefObject<HTMLFormElement>;
164
171
  handleBlur: () => void;
165
172
  wrapSubmit: (userSubmit?: (e: react.SyntheticEvent<HTMLFormElement>) => void) => (e: react.SyntheticEvent<HTMLFormElement>) => void;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../form-snapshots/index.ts","../form-snapshots/hooks/use-form-snapshots.ts","../form-snapshots/client.ts","../form-snapshots/local-db/db.ts","../form-snapshots/dexie-storage.ts","../form-snapshots/dom-snapshot.ts","../form-snapshots/context.tsx","../form-snapshots/hooks/use-form-snapshots-list.ts","../form-snapshots/devtools.tsx","../form-snapshots/snapshot-table.tsx","../form-snapshots/styles.ts","../form-snapshots/adapters.ts"],"sourcesContent":["export { useFormSnapshots } from \"./hooks/use-form-snapshots\"\nexport type { FormSnapshotsOptions } from \"./hooks/use-form-snapshots\"\n\nexport {\n\tuseFormSnapshotsList,\n\ttype useFormSnapshotsListOptions,\n\ttype FormHistoryListItem,\n} from \"./hooks/use-form-snapshots-list\"\n\nexport {\n\tFormSnapshotsProvider,\n\tuseFormSnapshotsConfig,\n\ttype FormSnapshotsProviderProps,\n} from \"./context\"\n\nexport { FormSnapshotsDevtools } from \"./devtools\"\n\nexport {\n\tFormSnapshotTable,\n\ttype FormSnapshotTableProps,\n} from \"./snapshot-table\"\n\nexport {\n\tuseRHFFormSnapshots,\n\tuseObjectFormSnapshots,\n\ttype RHFSnapshotsOptions,\n\ttype ObjectStateSnapshotsOptions,\n} from \"./adapters\"\n\nexport { FormSnapshotsClient } from \"./client\"\n\nexport type {\n\tFormSnapshot,\n\tFormSessionBase,\n\tFormSnapshotsStorage,\n} from \"./types\"\n\nexport { db } from \"./local-db/db\"\nexport type { FormSession } from \"./local-db/db\"\n","import {\n\tuseCallback,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n\ttype SyntheticEvent,\n} from \"react\"\nimport { FormSnapshotsClient } from \"../client\"\nimport { DexieFormSnapshotsStorage } from \"../dexie-storage\"\nimport { type FormSnapshot, type FormSnapshotsStorage } from \"../types\"\nimport {\n\trestoreSnapshotToForm,\n\tsnapshotFromDOMForm,\n} from \"../dom-snapshot\"\nimport { useFormSnapshotsConfig } from \"../context\"\n\nexport interface FormSnapshotsOptions {\n\t/** How long in ms to retain history entries. Default: 24 h */\n\tsnapshotsLimit?: number\n\n\t/**\n\t * Field paths (path/prefix) to exclude while saving snapshots.\n\t * E.g. \"password\", \"patient.address\".\n\t * Merged with the global excludeFields config.\n\t */\n\texcludeFields?: string[]\n\n\t/**\n\t * Override how the current form state is read.\n\t *\n\t * Use this for controlled forms or nested / structured data models\n\t * (e.g. HL7 FHIR Address, HumanName, ContactPoint[]) where values live\n\t * in React state rather than plain DOM elements.\n\t */\n\tgetValues?: () => FormSnapshot\n\n\t/**\n\t * Override how a saved snapshot is applied back to the form.\n\t *\n\t * Receives the full snapshot that was previously returned by `getValues`.\n\t */\n\tapplyValues?: (snapshot: FormSnapshot) => void\n\n\t/**\n\t * When true, the active session is deleted from storage and the form\n\t * is cleared immediately after submit so that submitted data is not\n\t * kept in history. Can be overridden globally via provider config.\n\t */\n\tdiscardOnSubmit?: boolean\n}\n\nfunction applyExcludeFields(\n\tsnapshot: FormSnapshot,\n\texcludeFields?: string[],\n): FormSnapshot {\n\tif (!excludeFields || excludeFields.length === 0) return snapshot\n\n\tfunction pruneByPrefix(\n\t\tobj: FormSnapshot,\n\t\tpaths: string[],\n\t\tparentPath = \"\",\n\t): FormSnapshot {\n\t\tconst result: FormSnapshot = {}\n\n\t\tfor (const [key, value] of Object.entries(obj)) {\n\t\t\tconst path = parentPath ? `${parentPath}.${key}` : key\n\n\t\t\tconst isExcluded = paths.some(\n\t\t\t\t(p) => path === p || path.startsWith(`${p}.`),\n\t\t\t)\n\t\t\tif (isExcluded) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (value && typeof value === \"object\" && !Array.isArray(value)) {\n\t\t\t\tresult[key] = pruneByPrefix(\n\t\t\t\t\tvalue as FormSnapshot,\n\t\t\t\t\tpaths,\n\t\t\t\t\tpath,\n\t\t\t\t)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// For now, arrays are treated as atomic: you can exclude the whole\n\t\t\t// array field with its path, but not individual items.\n\t\t\tresult[key] = value\n\t\t}\n\n\t\treturn result\n\t}\n\n\treturn pruneByPrefix(snapshot, excludeFields)\n}\n\nexport function useFormSnapshots(\n\tformName: string,\n\toptions?: FormSnapshotsOptions,\n) {\n\tconst { snapshotsLimit, getValues, applyValues, excludeFields, discardOnSubmit } =\n\t\toptions ?? {}\n\n\tconst formRef = useRef<HTMLFormElement>(null)\n\tconst sessionIdRef = useRef<number | null>(null)\n\tconst lastSubmittedSessionIdRef = useRef<number | null>(null)\n\tconst clientRef = useRef<FormSnapshotsClient | null>(null)\n\tconst [isSubmitted, setIsSubmitted] = useState(false)\n\tconst storageRef = useRef<FormSnapshotsStorage | null>(null)\n\n\tconst {\n\t\tdefaultSnapshotsLimit,\n\t\texcludeFields: globalExcludeFields,\n\t\tdiscardOnSubmit: globalDiscardOnSubmit,\n\t} = useFormSnapshotsConfig()\n\n\tconst effectiveSnapshotsLimit =\n\t\tsnapshotsLimit ?? defaultSnapshotsLimit ?? 24 * 60 * 60 * 1000\n\n\tconst effectiveDiscardOnSubmit =\n\t\ttypeof discardOnSubmit === \"boolean\"\n\t\t\t? discardOnSubmit\n\t\t\t: globalDiscardOnSubmit ?? false\n\n\tconst mergedExcludeFields =\n\t\tglobalExcludeFields || excludeFields\n\t\t\t? Array.from(\n\t\t\t\t\tnew Set([...(globalExcludeFields ?? []), ...(excludeFields ?? [])]),\n\t\t\t\t)\n\t\t\t: undefined\n\n\t// Keep latest callbacks in refs so they never invalidate the init effect\n\tconst getValuesRef = useRef(getValues)\n\tconst applyValuesRef = useRef(applyValues)\n\tuseEffect(() => { getValuesRef.current = getValues }, [getValues])\n\tuseEffect(() => { applyValuesRef.current = applyValues }, [applyValues])\n\n\t// ── Initialise: create client, prune, resume or open session ───────────────\n\tuseEffect(() => {\n\t\tlet cancelled = false\n\n\t\tasync function init() {\n\t\t\tif (!storageRef.current) {\n\t\t\t\tstorageRef.current = new DexieFormSnapshotsStorage()\n\t\t\t}\n\n\t\t\tconst client = new FormSnapshotsClient({\n\t\t\t\tstorage: storageRef.current!,\n\t\t\t\tformName,\n\t\t\t\tsnapshotsLimit: effectiveSnapshotsLimit,\n\t\t\t})\n\n\t\t\tclientRef.current = client\n\n\t\t\tconst session = await client.initSession()\n\t\t\tif (cancelled) return\n\n\t\t\tsessionIdRef.current = session.id\n\n\t\t\t// Restore saved values (after mount) via applyValues callback when\n\t\t\t// provided, or fall back to DOM-based restoration for flat forms.\n\t\t\tif (session.data && session.data !== \"{}\") {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tif (cancelled) return\n\t\t\t\t\tconst snapshot = JSON.parse(session.data) as FormSnapshot\n\t\t\t\t\tif (applyValuesRef.current) {\n\t\t\t\t\t\tapplyValuesRef.current(snapshot)\n\t\t\t\t\t} else if (formRef.current) {\n\t\t\t\t\t\trestoreSnapshotToForm(formRef.current, snapshot)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tinit()\n\n\t\treturn () => {\n\t\t\tcancelled = true\n\t\t}\n\t}, [formName, effectiveSnapshotsLimit])\n\n\t// ── Snapshot current form values into the active session ──────────────────\n\tconst handleBlur = useCallback(() => {\n\t\tconst sessionId = sessionIdRef.current\n\t\tif (sessionId === null) return\n\n\t\tlet snapshot: FormSnapshot\n\n\t\tif (getValuesRef.current) {\n\t\t\t// Controlled / structured form — caller owns the state.\n\t\t\t// Supports arbitrarily nested values: HL7 Address, HumanName,\n\t\t\t// ContactPoint[], CodeableConcept, etc.\n\t\t\tsnapshot = getValuesRef.current()\n\t\t} else {\n\t\t\t// Fallback: enumerate flat DOM form elements (strings / booleans only).\n\t\t\tconst form = formRef.current\n\t\t\tif (!form) return\n\t\t\tsnapshot = snapshotFromDOMForm(form)\n\t\t}\n\n\t\tsnapshot = applyExcludeFields(snapshot, mergedExcludeFields)\n\n\t\tif (!clientRef.current) return\n\n\t\tclientRef.current.saveSnapshot(sessionId, snapshot)\n\t}, []) // getValues is read via ref — no dep needed\n\n\t// ── Clear current form values after submit when requested ─────────────────\n\tconst clearFormState = useCallback(() => {\n\t\tif (applyValuesRef.current) {\n\t\t\t// For controlled / structured forms, an empty snapshot is passed\n\t\t\t// back to the caller so they can reset their own state.\n\t\t\tapplyValuesRef.current({} as FormSnapshot)\n\t\t} else if (formRef.current) {\n\t\t\t// For plain DOM forms, reset back to the initial values.\n\t\t\tformRef.current.reset()\n\t\t}\n\t}, [])\n\n\t// ── Wrap the form's onSubmit to close the session ─────────────────────────\n\tconst wrapSubmit = useCallback(\n\t\t(\n\t\t\tuserSubmit?: (e: SyntheticEvent<HTMLFormElement>) => void,\n\t\t) =>\n\t\t\t(e: SyntheticEvent<HTMLFormElement>) => {\n\t\t\t\te.preventDefault()\n\t\t\t\thandleBlur() // capture the final state before closing\n\n\t\t\t\tconst sessionId = sessionIdRef.current\n\t\t\t\tconst client = clientRef.current\n\t\t\t\tif (sessionId !== null && client) {\n\t\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\t\t// Discard the entire session so submitted data is not kept.\n\t\t\t\t\t\tclient.deleteSession(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = null\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclient.markSubmitted(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = sessionId\n\t\t\t\t\t}\n\t\t\t\t\t// Closed session should no longer receive snapshots\n\t\t\t\t\tsessionIdRef.current = null\n\t\t\t\t}\n\n\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\tclearFormState()\n\t\t\t\t}\n\n\t\t\t\tsetIsSubmitted(true)\n\t\t\t\tuserSubmit?.(e)\n\t\t\t},\n\t\t[handleBlur, clearFormState, effectiveDiscardOnSubmit],\n\t)\n\n\tconst markSubmitResult = useCallback(\n\t\tasync (params: {\n\t\t\tstatusCode?: number | null\n\t\t\terrorMessage?: string | null\n\t\t}) => {\n\t\t\tconst client = clientRef.current\n\t\t\tconst sessionId = lastSubmittedSessionIdRef.current\n\t\t\tif (!client || sessionId == null) return\n\n\t\t\tawait client.setSubmissionResult({\n\t\t\t\tsessionId,\n\t\t\t\tstatusCode: params.statusCode,\n\t\t\t\terrorMessage: params.errorMessage,\n\t\t\t})\n\t\t},\n\t\t[],\n\t)\n\n\tconst wrapSubmitAsync = useCallback(\n\t\t(\n\t\t\t\tuserSubmit?: (\n\t\t\t\t\te: SyntheticEvent<HTMLFormElement>,\n\t\t\t\t) => Promise<\n\t\t\t\t\t| void\n\t\t\t\t\t| {\n\t\t\t\t\t\t\tstatusCode?: number | null\n\t\t\t\t\t\t\terrorMessage?: string | null\n\t\t\t\t\t }\n\t\t\t\t>,\n\t\t\t) =>\n\t\t\tasync (e: SyntheticEvent<HTMLFormElement>) => {\n\t\t\t\te.preventDefault()\n\t\t\t\thandleBlur()\n\n\t\t\t\tconst sessionId = sessionIdRef.current\n\t\t\t\tconst client = clientRef.current\n\t\t\t\tif (sessionId !== null && client) {\n\t\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\t\tawait client.deleteSession(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = null\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait client.markSubmitted(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = sessionId\n\t\t\t\t\t}\n\t\t\t\t\tsessionIdRef.current = null\n\t\t\t\t}\n\n\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\tclearFormState()\n\t\t\t\t}\n\n\t\t\t\tsetIsSubmitted(true)\n\n\t\t\t\tif (!userSubmit) return\n\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await userSubmit(e)\n\t\t\t\t\tif (result && typeof result === \"object\") {\n\t\t\t\t\t\tawait markSubmitResult({\n\t\t\t\t\t\t\tstatusCode: \"statusCode\" in result ? result.statusCode : null,\n\t\t\t\t\t\t\terrorMessage:\n\t\t\t\t\t\t\t\t\"errorMessage\" in result ? result.errorMessage : null,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t// In case of error we leave handling to the caller;\n\t\t\t// logging here is enough. Callers can still invoke\n\t\t\t// markSubmitResult manually if they want.\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.error(\"wrapSubmitAsync userSubmit error:\", error)\n\t\t\t\t}\n\t\t\t},\n\t\t[handleBlur, markSubmitResult, clearFormState, effectiveDiscardOnSubmit],\n\t)\n\n\t// ── Restore the latest saved snapshot into the DOM form ───────────────────\n\tconst restoreLatest = useCallback(async () => {\n\t\tif (!clientRef.current) return\n\n\t\tconst snapshot = await clientRef.current.getLatestSnapshot()\n\t\tif (!snapshot) return\n\n\t\tif (applyValuesRef.current) {\n\t\t\t// Structured form — route the full nested snapshot back to state.\n\t\t\tapplyValuesRef.current(snapshot)\n\t\t} else {\n\t\t\tif (!formRef.current) return\n\t\t\trestoreSnapshotToForm(formRef.current, snapshot)\n\t\t}\n\n\t\t// Open a new session pre-seeded with the restored data so further edits\n\t\t// are tracked against the fresh session\n\t\tconst newSession = await clientRef.current.openNewSessionFromSnapshot(\n\t\t\tsnapshot,\n\t\t)\n\t\tsessionIdRef.current = newSession.id\n\n\t\tsetIsSubmitted(false)\n\t}, []) // applyValues is read via ref — no dep needed\n\n\treturn {\n\t\tformRef,\n\t\thandleBlur,\n\t\twrapSubmit,\n\t\twrapSubmitAsync,\n\t\trestoreLatest,\n\t\tisSubmitted,\n\t\tmarkSubmitResult,\n\t}\n}\n","import type {\n\tFormSnapshotsStorage,\n\tFormSessionBase,\n\tFormSnapshot,\n} from \"./types\"\n\nexport class FormSnapshotsClient {\n\tprivate readonly storage: FormSnapshotsStorage\n\tprivate readonly formName: string\n\tprivate readonly snapshotsLimit: number\n\n\tconstructor(params: {\n\t\tstorage: FormSnapshotsStorage\n\t\tformName: string\n\t\tsnapshotsLimit: number\n\t}) {\n\t\tthis.storage = params.storage\n\t\tthis.formName = params.formName\n\t\tthis.snapshotsLimit = params.snapshotsLimit\n\t}\n\n\tasync initSession(): Promise<FormSessionBase> {\n\t\tconst now = Date.now()\n\t\tconst cutoff = now - this.snapshotsLimit\n\n\t\tawait this.storage.pruneOlderThan(cutoff)\n\n\t\tconst existing = await this.storage.findActiveSession(this.formName)\n\t\tif (existing) return existing\n\n\t\treturn this.storage.createSession(this.formName, {})\n\t}\n\n\tasync saveSnapshot(\n\t\tsessionId: number,\n\t\tsnapshot: FormSnapshot,\n\t): Promise<void> {\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tdata: JSON.stringify(snapshot),\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync markSubmitted(sessionId: number): Promise<void> {\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tsubmitted: true,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync deleteSession(sessionId: number): Promise<void> {\n\t\tif (typeof this.storage.deleteSession === \"function\") {\n\t\t\tawait this.storage.deleteSession(sessionId)\n\t\t}\n\t}\n\n\tasync setSubmissionResult(params: {\n\t\tsessionId: number\n\t\tstatusCode?: number | null\n\t\terrorMessage?: string | null\n\t}): Promise<void> {\n\t\tconst { sessionId, statusCode, errorMessage } = params\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tstatusCode: statusCode ?? null,\n\t\t\terrorMessage: errorMessage ?? null,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync getLatestSnapshot(): Promise<FormSnapshot | null> {\n\t\tconst latest = await this.storage.getLatestSession(this.formName)\n\t\tif (!latest) return null\n\t\treturn JSON.parse(latest.data) as FormSnapshot\n\t}\n\n\tasync openNewSessionFromSnapshot(\n\t\tsnapshot: FormSnapshot,\n\t): Promise<FormSessionBase> {\n\t\treturn this.storage.createSession(this.formName, snapshot)\n\t}\n}\n\n","import { Dexie, type EntityTable } from \"dexie\"\n\nexport interface FormSession {\n\tid: number\n\tformName: string\n\t/** JSON-serialised record of field name → value */\n\tdata: string\n\tcreatedAt: number\n\tupdatedAt: number\n\t/** true = the form was submitted; session is closed / read-only */\n\tsubmitted: boolean\n\t/** Optional HTTP-like status code for the last submit attempt */\n\tstatusCode?: number | null\n\t/** Optional error message when the last submit attempt failed */\n\terrorMessage?: string | null\n}\n\nconst db = new Dexie(\"FormSnapshotsDatabase\") as Dexie & {\n\tformSessions: EntityTable<FormSession, \"id\">\n}\n\ndb.version(1).stores({\n\tformSessions: \"++id, formName, createdAt, submitted, [formName+createdAt]\",\n})\n\ndb.version(2).stores({\n\tformSessions:\n\t\t\"++id, formName, createdAt, updatedAt, submitted, [formName+createdAt]\",\n})\n\nexport { db }\n\n","import { db, type FormSession } from \"./local-db/db\"\nimport {\n\ttype FormSnapshotsStorage,\n\ttype FormSessionBase,\n\ttype FormSnapshot,\n} from \"./types\"\n\nfunction mapToBase(session: FormSession): FormSessionBase {\n\treturn {\n\t\tid: session.id,\n\t\tformName: session.formName,\n\t\tdata: session.data,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t\tsubmitted: session.submitted,\n\t\tstatusCode: session.statusCode ?? null,\n\t\terrorMessage: session.errorMessage ?? null,\n\t}\n}\n\n\texport class DexieFormSnapshotsStorage implements FormSnapshotsStorage {\n\tasync findActiveSession(formName: string): Promise<FormSessionBase | null> {\n\t\tconst existing = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.filter((s) => !s.submitted)\n\t\t\t.last()\n\n\t\treturn existing ? mapToBase(existing) : null\n\t}\n\n\tasync createSession(\n\t\tformName: string,\n\t\tsnapshot: FormSnapshot,\n\t): Promise<FormSessionBase> {\n\t\tconst now = Date.now()\n\t\tconst id = await db.formSessions.add({\n\t\t\tformName,\n\t\t\tdata: JSON.stringify(snapshot),\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tsubmitted: false,\n\t\t\tstatusCode: null,\n\t\t\terrorMessage: null,\n\t\t})\n\n\t\tconst created = await db.formSessions.get(id)\n\t\tif (!created) {\n\t\t\tthrow new Error(\"Failed to create form session\")\n\t\t}\n\n\t\treturn mapToBase(created)\n\t}\n\n\tasync updateSession(\n\t\tid: number,\n\t\tpatch: Partial<\n\t\t\tPick<\n\t\t\t\tFormSessionBase,\n\t\t\t\t\"data\" | \"updatedAt\" | \"submitted\" | \"statusCode\" | \"errorMessage\"\n\t\t\t>\n\t\t>,\n\t): Promise<void> {\n\t\tawait db.formSessions.update(id, patch)\n\t}\n\n\tasync getLatestSession(formName: string): Promise<FormSessionBase | null> {\n\t\tconst sessions = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.sortBy(\"updatedAt\")\n\n\t\tconst latest = sessions[sessions.length - 1]\n\t\treturn latest ? mapToBase(latest) : null\n\t}\n\n\tasync listSessions(formName: string): Promise<FormSessionBase[]> {\n\t\tconst sessions = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.sortBy(\"updatedAt\")\n\n\t\treturn sessions.map(mapToBase)\n\t}\n\n\tasync pruneOlderThan(cutoffMs: number): Promise<void> {\n\t\tawait db.formSessions.where(\"createdAt\").below(cutoffMs).delete()\n\t}\n\n\tasync deleteSession(id: number): Promise<void> {\n\t\tawait db.formSessions.delete(id)\n\t}\n}\n\n","type AnyFormEl = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement\n\nfunction toStringValue(val: unknown): string {\n\tif (val == null) return \"\"\n\tif (typeof val === \"object\") return JSON.stringify(val)\n\treturn String(val as string | number | boolean | bigint)\n}\n\nfunction applyValueToInput(input: AnyFormEl, val: unknown): void {\n\tif (input instanceof HTMLInputElement) {\n\t\tif (input.type === \"checkbox\") {\n\t\t\tinput.checked = Boolean(val)\n\t\t\tinput.dispatchEvent(new Event(\"change\", { bubbles: true }))\n\t\t\treturn\n\t\t}\n\t\tif (input.type === \"radio\") {\n\t\t\tinput.checked = input.value === val\n\t\t\tif (input.checked) input.dispatchEvent(new Event(\"change\", { bubbles: true }))\n\t\t\treturn\n\t\t}\n\t}\n\tinput.value = toStringValue(val)\n\tinput.dispatchEvent(new Event(\"input\", { bubbles: true }))\n}\n\n/** Populate DOM form elements from a plain snapshot object */\nexport function restoreSnapshotToForm(\n\tform: HTMLFormElement,\n\tsnapshot: Record<string, unknown>,\n) {\n\tfor (const el of Array.from(form.elements)) {\n\t\tconst input = el as AnyFormEl\n\t\tif (!input.name || !(input.name in snapshot)) continue\n\t\tapplyValueToInput(input, snapshot[input.name])\n\t}\n}\n\n/** Read all named fields from a plain DOM form into a flat snapshot object */\nexport function snapshotFromDOMForm(\n\tform: HTMLFormElement,\n): Record<string, unknown> {\n\tconst snapshot: Record<string, unknown> = {}\n\tfor (const el of Array.from(form.elements)) {\n\t\tconst input = el as AnyFormEl\n\t\tif (!input.name) continue\n\t\treadInputIntoSnapshot(input, snapshot)\n\t}\n\treturn snapshot\n}\n\nexport function readInputIntoSnapshot(\n\tinput: AnyFormEl,\n\tsnapshot: Record<string, unknown>,\n) {\n\tif (input instanceof HTMLInputElement) {\n\t\tif (input.type === \"checkbox\") {\n\t\t\tsnapshot[input.name] = input.checked\n\t\t} else if (input.type === \"radio\") {\n\t\t\tif (input.checked) snapshot[input.name] = input.value\n\t\t} else if (input.type !== \"file\") {\n\t\t\tsnapshot[input.name] = input.value\n\t\t}\n\t} else {\n\t\tsnapshot[input.name] = input.value\n\t}\n}\n\n","import type { ReactNode } from \"react\"\nimport { createContext, useContext } from \"react\"\n\nexport interface FormSnapshotsGlobalConfig {\n\tdefaultSnapshotsLimit?: number\n\t/**\n\t * Field paths (path/prefix) to exclude while saving snapshots.\n\t * E.g. [\"password\", \"patient.address\"]\n\t */\n\texcludeFields?: string[]\n\n\t/**\n\t * When true, submitted sessions are discarded and the form is cleared\n\t * immediately after submit instead of keeping the data in history.\n\t * Can be overridden per form via hook options.\n\t */\n\tdiscardOnSubmit?: boolean\n}\n\nconst FormSnapshotsConfigContext = createContext<FormSnapshotsGlobalConfig | undefined>(\n\tundefined,\n)\n\nexport interface FormSnapshotsProviderProps {\n\tvalue?: FormSnapshotsGlobalConfig\n\tchildren: ReactNode\n}\n\nexport function FormSnapshotsProvider({\n\tvalue,\n\tchildren,\n}: Readonly<FormSnapshotsProviderProps>) {\n\treturn (\n\t\t<FormSnapshotsConfigContext.Provider value={value ?? {}}>\n\t\t\t{children}\n\t\t</FormSnapshotsConfigContext.Provider>\n\t)\n}\n\nexport function useFormSnapshotsConfig(): FormSnapshotsGlobalConfig {\n\treturn useContext(FormSnapshotsConfigContext) ?? {}\n}\n\n","import { useLiveQuery } from \"dexie-react-hooks\"\nimport { db, type FormSession } from \"../local-db/db\"\n\nexport interface useFormSnapshotsListOptions {\n\t/**\n\t * Whether to return only submitted (completed) sessions.\n\t * Default: false (both submitted and in-progress).\n\t */\n\tonlySubmitted?: boolean\n\n\t/**\n\t * Whether to return only successful (2xx) submission results.\n\t * Default: false (all statusCode values).\n\t */\n\tonlySuccessful?: boolean\n\n\t/**\n\t * Maximum age in ms. E.g. last 24 hours = 24 * 60 * 60 * 1000.\n\t * Default: unlimited.\n\t */\n\tmaxAgeMs?: number\n\n\t/**\n\t * Maximum number of records to return.\n\t * Default: unlimited.\n\t */\n\tlimit?: number\n}\n\nexport interface FormHistoryListItem {\n\tid: number\n\tformName: string\n\tcreatedAt: number\n\tupdatedAt: number\n\tsubmitted: boolean\n\tstatusCode: number | null\n\terrorMessage: string | null\n\t/**\n\t * Age in ms relative to \\\"now\\\".\n\t */\n\tageMs: number\n}\n\nexport function useFormSnapshotsList(\n\tformName: string,\n\toptions?: useFormSnapshotsListOptions,\n) {\n\tconst {\n\t\tonlySubmitted = false,\n\t\tonlySuccessful = false,\n\t\tmaxAgeMs,\n\t\tlimit,\n\t} = options ?? {}\n\n\tconst items = useLiveQuery<FormHistoryListItem[] | undefined>(async () => {\n\t\tconst now = Date.now()\n\t\tconst cutoff = maxAgeMs ? now - maxAgeMs : undefined\n\n\t\tlet query = db.formSessions.where(\"formName\").equals(formName)\n\n\t\t// Other filters are not supported directly by Dexie, so we refine via JS filter.\n\t\tlet sessions: FormSession[] = await query.toArray()\n\n\t\tif (onlySubmitted) {\n\t\t\tsessions = sessions.filter((s) => s.submitted)\n\t\t}\n\n\t\tif (onlySuccessful) {\n\t\t\tsessions = sessions.filter((s) => {\n\t\t\t\tif (s.statusCode == null) return false\n\t\t\t\treturn s.statusCode >= 200 && s.statusCode < 300\n\t\t\t})\n\t\t}\n\n\t\tif (cutoff != null) {\n\t\t\tsessions = sessions.filter((s) => s.createdAt >= cutoff)\n\t\t}\n\n\t\tsessions.sort((a, b) => b.updatedAt - a.updatedAt)\n\n\t\tif (typeof limit === \"number\") {\n\t\t\tsessions = sessions.slice(0, limit)\n\t\t}\n\n\t\treturn sessions.map((s) => ({\n\t\t\tid: s.id,\n\t\t\tformName: s.formName,\n\t\t\tcreatedAt: s.createdAt,\n\t\t\tupdatedAt: s.updatedAt,\n\t\t\tsubmitted: s.submitted,\n\t\t\tstatusCode: s.statusCode ?? null,\n\t\t\terrorMessage: s.errorMessage ?? null,\n\t\t\tageMs: now - s.updatedAt,\n\t\t}))\n\t}, [formName, onlySubmitted, onlySuccessful, maxAgeMs, limit])\n\n\tconst isLoading = items === undefined\n\n\treturn { items: items ?? [], isLoading }\n}\n\n","import { useMemo, useState } from \"react\"\nimport { useLiveQuery } from \"dexie-react-hooks\"\nimport { db, type FormSession } from \"./local-db/db\"\nimport { FormSnapshotTable } from \"./snapshot-table\"\nimport { ensureFormSnapshotsStyles } from \"./styles\"\n\nfunction formatDateShort(ms: number) {\n\treturn new Intl.DateTimeFormat(\"en-GB\", {\n\t\tmonth: \"short\",\n\t\tday: \"2-digit\",\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t}).format(new Date(ms))\n}\n\nfunction formatAge(ms: number) {\n\tconst seconds = Math.floor(ms / 1000)\n\tconst minutes = Math.floor(seconds / 60)\n\tconst hours = Math.floor(minutes / 60)\n\tconst days = Math.floor(hours / 24)\n\n\tif (days > 0) return `${days}d ${hours % 24}h`\n\tif (hours > 0) return `${hours}h ${minutes % 60}m`\n\tif (minutes > 0) return `${minutes}m`\n\treturn `${seconds}s`\n}\n\nexport function FormSnapshotsDevtools() {\n\tensureFormSnapshotsStyles()\n\n\tconst isProd =\n\t\ttypeof import.meta !== \"undefined\" &&\n\t\t(import.meta as any).env?.PROD\n\n\tif (isProd) {\n\t\treturn null\n\t}\n\n\tconst [open, setOpen] = useState(false)\n\tconst [selectedForm, setSelectedForm] = useState<string>(\"all\")\n\tconst [onlySubmitted, setOnlySubmitted] = useState(false)\n\tconst [onlyErrors, setOnlyErrors] = useState(false)\n\tconst [expandedId, setExpandedId] = useState<number | null>(null)\n\tconst [isClearing, setIsClearing] = useState(false)\n\n\tconst sessions = useLiveQuery<FormSession[]>(() => {\n\t\treturn db.formSessions\n\t\t\t.toCollection()\n\t\t\t.sortBy(\"updatedAt\")\n\t\t\t.then((all) => all.reverse())\n\t}, [])\n\n\tconst now = Date.now()\n\n\tconst formNames = useMemo(() => {\n\t\tif (!sessions) return []\n\t\treturn Array.from(new Set(sessions.map((s) => s.formName))).sort()\n\t}, [sessions])\n\n\tconst filtered = useMemo(() => {\n\t\tif (!sessions) return []\n\n\t\treturn sessions.filter((s) => {\n\t\t\tif (selectedForm !== \"all\" && s.formName !== selectedForm) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (onlySubmitted && !s.submitted) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (onlyErrors && (s.statusCode == null || s.statusCode < 400)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}, [sessions, selectedForm, onlySubmitted, onlyErrors])\n\n\tconst handleDeleteSession = async (id: number) => {\n\t\ttry {\n\t\t\tawait db.formSessions.delete(id)\n\t\t} catch (error) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(\"Failed to delete form session\", error)\n\t\t}\n\t}\n\n\tconst handleClearAll = async () => {\n\t\tif (!sessions || sessions.length === 0) return\n\t\tsetIsClearing(true)\n\t\ttry {\n\t\t\tconst ids = sessions.map((s) => s.id)\n\t\t\tawait db.formSessions.bulkDelete(ids)\n\t\t} catch (error) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(\"Failed to clear form sessions\", error)\n\t\t} finally {\n\t\t\tsetIsClearing(false)\n\t\t}\n\t}\n\n\tconst total = sessions?.length ?? 0\n\n\tif (!sessions || sessions.length === 0) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={() => setOpen((v) => !v)}\n\t\t\t\t\tclassName=\"fs-devtools-toggle\"\n\t\t\t>\n\t\t\t\t\t<HistoryIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t<span>Form Snapshot Devtools</span>\n\t\t\t\t{total > 0 && (\n\t\t\t\t\t\t<span className=\"fs-devtools-toggle-count\">\n\t\t\t\t\t\t{total}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</button>\n\n\t\t\t{open && (\n\t\t\t\t<div className=\"fs-devtools-panel\">\n\t\t\t\t\t<div className=\"fs-devtools-header\">\n\t\t\t\t\t\t<div className=\"fs-devtools-header-left\">\n\t\t\t\t\t\t\t<HistoryIcon style={{ width: 14, height: 14 }} />\n\t\t\t\t\t\t\t<div className=\"fs-devtools-header-text\">\n\t\t\t\t\t\t\t\t<span className=\"fs-devtools-header-title\">\n\t\t\t\t\t\t\t\t\tForm Snapshot Devtools\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span className=\"fs-devtools-header-subtitle\">\n\t\t\t\t\t\t\t\t\t{filtered.length} / {total} session\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"fs-devtools-header-actions\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={handleClearAll}\n\t\t\t\t\t\t\t\tclassName=\"fs-devtools-clear\"\n\t\t\t\t\t\t\t\tdisabled={isClearing || !sessions || sessions.length === 0}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tClear all\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => setOpen(false)}\n\t\t\t\t\t\t\t\tclassName=\"fs-devtools-close\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<XIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"fs-devtools-filters\">\n\t\t\t\t\t\t<FilterIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tclassName=\"fs-devtools-select\"\n\t\t\t\t\t\t\tvalue={selectedForm}\n\t\t\t\t\t\t\tonChange={(e) => setSelectedForm(e.target.value)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"all\">All forms</option>\n\t\t\t\t\t\t\t{formNames.map((name) => (\n\t\t\t\t\t\t\t\t<option key={name} value={name}>\n\t\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t\t<label className=\"fs-devtools-filter-label\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={onlySubmitted}\n\t\t\t\t\t\t\t\tonChange={(e) => setOnlySubmitted(e.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span>submitted</span>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<label className=\"fs-devtools-filter-label\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={onlyErrors}\n\t\t\t\t\t\t\t\tonChange={(e) => setOnlyErrors(e.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span>errors</span>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"fs-devtools-body\">\n\t\t\t\t\t\t{filtered.length === 0 ? (\n\t\t\t\t\t\t\t<p className=\"fs-devtools-empty\">\n\t\t\t\t\t\t\t\tNo sessions match the selected filters.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<ul className=\"fs-devtools-list\">\n\t\t\t\t\t\t\t\t{filtered.map((s) => (\n\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\tkey={s.id}\n\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-item\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\tsetExpandedId((current) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tcurrent === s.id ? null : s.id,\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-item-toggle\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-main\">\n\t\t\t\t\t\t\t\t\t\t\t\t<ChevronDownIcon\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: 12,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: 12,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttransform:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\texpandedId === s.id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"rotate(0deg)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"rotate(-90deg)\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttransition: \"transform 120ms ease-out\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-tags\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-tag\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.formName}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-id\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t#{s.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-meta\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span>{formatDateShort(s.updatedAt)}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span>· {formatAge(now - s.updatedAt)} ago</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.submitted ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-badge fs-devtools-badge-success\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsubmitted\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-badge fs-devtools-badge-warn\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tin progress\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.statusCode != null && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-status\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstatus {s.statusCode}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-delete\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvoid handleDeleteSession(s.id)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tDelete\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{typeof s.errorMessage === \"string\" && s.errorMessage && (\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-error\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{s.errorMessage}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t\t\t{expandedId === s.id && (\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-snapshot\">\n\t\t\t\t\t\t\t\t\t\t\t\t<FormSnapshotTable\n\t\t\t\t\t\t\t\t\t\t\t\t\tdata={s.data}\n\t\t\t\t\t\t\t\t\t\t\t\t\temptyMessage=\"This session does not contain any field data yet.\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</>\n\t)\n}\n\ntype IconProps = React.SVGProps<SVGSVGElement>\n\nfunction HistoryIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<path d=\"M3 3v6h6\" />\n\t\t\t<path d=\"M3.05 13A9 9 0 1 0 9 3.05\" />\n\t\t\t<path d=\"M12 7v5l3 3\" />\n\t\t</svg>\n\t)\n}\n\nfunction XIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n\t\t\t<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n\t\t</svg>\n\t)\n}\n\nfunction FilterIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\" />\n\t\t</svg>\n\t)\n}\n\nfunction ChevronDownIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<polyline points=\"6 9 12 15 18 9\" />\n\t\t</svg>\n\t)\n}\n\n","import { useMemo } from \"react\"\nimport { ensureFormSnapshotsStyles } from \"./styles\"\n\ntype Primitive = string | number | boolean | null | undefined\n\nfunction isPrimitive(val: unknown): val is Primitive {\n\treturn (\n\t\tval === null ||\n\t\tval === undefined ||\n\t\ttypeof val === \"string\" ||\n\t\ttypeof val === \"number\" ||\n\t\ttypeof val === \"boolean\"\n\t)\n}\n\nexport interface FormSnapshotTableProps {\n\t/**\n\t * JSON-stringified snapshot. Genellikle `FormSession.data`.\n\t */\n\tdata: string\n\t/**\n\t * Message to display when the table is empty.\n\t * Default: \"No data captured yet.\"\n\t */\n\temptyMessage?: string\n\t/**\n\t * Extra class names for the outer wrapper.\n\t */\n\tclassName?: string\n}\n\ntype ParsedSnapshot = {\n\tsnapshot: Record<string, unknown>\n\tfieldCount: number\n}\n\nconst snapshotCache = new Map<string, ParsedSnapshot>()\n\nfunction parseSnapshot(data: string): ParsedSnapshot {\n\tconst cached = snapshotCache.get(data)\n\tif (cached) return cached\n\n\tlet snapshot: Record<string, unknown> = {}\n\ttry {\n\t\tsnapshot = JSON.parse(data) as Record<string, unknown>\n\t} catch {\n\t\tsnapshot = {}\n\t}\n\n\tconst parsed = {\n\t\tsnapshot,\n\t\tfieldCount: Object.keys(snapshot).length,\n\t}\n\n\tsnapshotCache.set(data, parsed)\n\treturn parsed\n}\n\nexport function FormSnapshotTable({\n\tdata,\n\temptyMessage = \"No data captured yet.\",\n\tclassName,\n}: Readonly<FormSnapshotTableProps>) {\n\tensureFormSnapshotsStyles()\n\n\tconst { snapshot, fieldCount } = useMemo(() => parseSnapshot(data), [data])\n\n\tif (fieldCount === 0) {\n\t\treturn (\n\t\t\t<p className=\"fs-snapshot-empty\">\n\t\t\t\t{emptyMessage}\n\t\t\t</p>\n\t\t)\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tclassName={[\n\t\t\t\t\"fs-snapshot-wrapper\",\n\t\t\t\tclassName,\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \")}\n\t\t>\n\t\t\t<table className=\"fs-snapshot-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr className=\"fs-snapshot-header-row\">\n\t\t\t\t\t\t<th className=\"fs-snapshot-header-cell\">\n\t\t\t\t\t\t\tField\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"fs-snapshot-header-cell\">\n\t\t\t\t\t\t\tValue\n\t\t\t\t\t\t</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{Object.entries(snapshot).map(([key, value]) => (\n\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\tclassName=\"fs-snapshot-row\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<td className=\"fs-snapshot-key\">\n\t\t\t\t\t\t\t\t{key}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td className=\"fs-snapshot-value\">\n\t\t\t\t\t\t\t\t<SnapshotValueCell value={value} />\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t)\n}\n\nfunction SnapshotValueCell({ value }: Readonly<{ value: unknown }>) {\n\tif (isPrimitive(value)) {\n\t\tif (value === null || value === undefined || value === \"\") {\n\t\t\treturn (\n\t\t\t\t<span className=\"fs-snapshot-muted\">\n\t\t\t\t\t—\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\tif (typeof value === \"boolean\") {\n\t\t\treturn (\n\t\t\t\t<span\n\t\t\t\t\tclassName={\n\t\t\t\t\t\tvalue\n\t\t\t\t\t\t\t? \"fs-snapshot-badge fs-snapshot-badge-true\"\n\t\t\t\t\t\t\t: \"fs-snapshot-badge fs-snapshot-badge-false\"\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t{String(value)}\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\treturn <span className=\"fs-snapshot-text\">{String(value)}</span>\n\t}\n\n\tif (Array.isArray(value)) {\n\t\tif (value.length === 0) {\n\t\t\treturn (\n\t\t\t\t<span className=\"fs-snapshot-muted\">\n\t\t\t\t\t—\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\treturn (\n\t\t\t<div className=\"fs-snapshot-array\">\n\t\t\t\t{value.map((v, idx) => (\n\t\t\t\t\t// eslint-disable-next-line react/no-array-index-key\n\t\t\t\t\t<span\n\t\t\t\t\t\tkey={idx}\n\t\t\t\t\t\tclassName=\"fs-snapshot-chip\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{String(v)}\n\t\t\t\t\t</span>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t)\n\t}\n\n\treturn (\n\t\t<pre className=\"fs-snapshot-object\">\n\t\t\t{JSON.stringify(value, null, 2)}\n\t\t</pre>\n\t)\n}\n\n","const STYLE_ID = \"form-snapshots-styles\"\n\nconst SNAPSHOT_TABLE_CSS = `:root {\n /* light – neutral-like palette */\n --fs-st-border: #e5e5e5; /* neutral-200 */\n --fs-st-border-soft: #d4d4d4; /* neutral-300 */\n --fs-st-header-bg: #f5f5f5; /* neutral-100 */\n --fs-st-key-text: #737373; /* neutral-500 */\n --fs-st-muted-text: #737373; /* neutral-500 */\n --fs-st-muted-strong: #a3a3a3; /* neutral-400 */\n --fs-st-chip-bg: #e5e5e5; /* neutral-200 */\n --fs-st-chip-text: #171717; /* neutral-900 */\n --fs-st-object-bg: #f5f5f5; /* neutral-100 */\n --fs-st-badge-true-bg: rgba(22, 163, 74, 0.1);\n --fs-st-badge-true-text: #166534;\n --fs-st-badge-false-border: #d4d4d4;\n --fs-st-badge-false-text: #525252; /* neutral-600 */\n --fs-st-text-main: #171717; /* neutral-900 */\n}\n\nhtml.dark {\n /* dark – neutral-900/950 style */\n --fs-st-border: #404040; /* neutral-700 */\n --fs-st-border-soft: #262626; /* neutral-800 */\n --fs-st-header-bg: #171717; /* neutral-900 */\n --fs-st-key-text: #a3a3a3; /* neutral-400 */\n --fs-st-muted-text: #a3a3a3; /* neutral-400 */\n --fs-st-muted-strong: #737373; /* neutral-500 */\n --fs-st-chip-bg: #262626; /* neutral-800 */\n --fs-st-chip-text: #f5f5f5; /* neutral-100 */\n --fs-st-object-bg: #0a0a0a; /* neutral-950ish */\n --fs-st-badge-true-bg: rgba(22, 163, 74, 0.25);\n --fs-st-badge-true-text: #bbf7d0;\n --fs-st-badge-false-border: #404040;\n --fs-st-badge-false-text: #e5e5e5; /* neutral-200 */\n --fs-st-text-main: #f5f5f5; /* neutral-100 */\n}\n.fs-snapshot-wrapper {\n border-radius: 6px;\n border: 1px solid var(--fs-st-border);\n overflow: hidden;\n color: var(--fs-st-text-main);\n}\n\n.fs-snapshot-table {\n width: 100%;\n font-size: 12px;\n border-collapse: collapse;\n}\n\n.fs-snapshot-header-row {\n border-bottom: 1px solid var(--fs-st-border);\n background: var(--fs-st-header-bg);\n}\n\n.fs-snapshot-header-cell {\n padding: 4px 6px;\n text-align: left;\n font-weight: 500;\n color: var(--fs-st-muted-text);\n}\n\n.fs-snapshot-row {\n border-bottom: 1px solid var(--fs-st-border-soft);\n}\n\n.fs-snapshot-row:last-child {\n border-bottom: none;\n}\n\n.fs-snapshot-key {\n padding: 4px 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 10px;\n color: var(--fs-st-key-text);\n vertical-align: top;\n}\n\n.fs-snapshot-value {\n padding: 4px 6px;\n vertical-align: top;\n}\n\n.fs-snapshot-empty {\n font-size: 12px;\n color: var(--fs-st-muted-text);\n font-style: italic;\n}\n\n.fs-snapshot-muted {\n color: var(--fs-st-muted-strong);\n font-style: italic;\n}\n\n.fs-snapshot-text {\n word-break: break-all;\n}\n\n.fs-snapshot-array {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n}\n\n.fs-snapshot-chip {\n background: var(--fs-st-chip-bg);\n color: var(--fs-st-chip-text);\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 999px;\n}\n\n.fs-snapshot-object {\n max-height: 128px;\n overflow: auto;\n border-radius: 4px;\n background: var(--fs-st-object-bg);\n padding: 4px;\n font-size: 10px;\n}\n\n.fs-snapshot-badge {\n display: inline-flex;\n align-items: center;\n border-radius: 3px;\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.fs-snapshot-badge-true {\n background: var(--fs-st-badge-true-bg);\n color: var(--fs-st-badge-true-text);\n}\n\n.fs-snapshot-badge-false {\n background: transparent;\n color: var(--fs-st-badge-false-text);\n border: 1px solid var(--fs-st-badge-false-border);\n}\n`\n\nconst DEVTOOLS_CSS = `:root {\n /* light mode – Tailwind neutral-ish */\n --fs-dev-bg-surface: rgba(250, 250, 250, 0.95); /* neutral-50 */\n --fs-dev-bg-panel: rgba(250, 250, 250, 0.98); /* neutral-50 */\n --fs-dev-bg-header: #f5f5f5; /* neutral-100 */\n --fs-dev-bg-header-muted: #f5f5f5;\n --fs-dev-bg-toggle-count: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-close-hover: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-tag: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-status: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-snapshot: #f5f5f5; /* neutral-100 */\n\n --fs-dev-border-strong: #e5e5e5; /* neutral-200 */\n --fs-dev-border-subtle: #e5e5e5; /* neutral-200 */\n --fs-dev-border-soft: #d4d4d4; /* neutral-300 */\n --fs-dev-border-snapshot: #d4d4d4; /* neutral-300 */\n\n --fs-dev-text-primary: #171717; /* neutral-900 */\n --fs-dev-text-muted: #737373; /* neutral-500 */\n --fs-dev-text-soft: #525252; /* neutral-600 */\n --fs-dev-text-error: #b91c1c;\n\n --fs-dev-badge-success-bg: rgba(22, 163, 74, 0.1);\n --fs-dev-badge-success-text: #166534;\n --fs-dev-badge-warn-bg: rgba(217, 119, 6, 0.12);\n --fs-dev-badge-warn-text: #92400e;\n\n --fs-dev-shadow-toggle: 0 1px 3px rgba(0, 0, 0, 0.08);\n --fs-dev-shadow-toggle-hover: 0 2px 6px rgba(0, 0, 0, 0.12);\n --fs-dev-shadow-panel: 0 8px 24px rgba(0, 0, 0, 0.12);\n}\n\nhtml.dark .fs-color-scheme-root:root,\nhtml.dark {\n /* dark mode – neutral-900/950 style */\n --fs-dev-bg-surface: rgba(23, 23, 23, 0.95); /* neutral-900 */\n --fs-dev-bg-panel: rgba(23, 23, 23, 0.98); /* neutral-900 */\n --fs-dev-bg-header: #171717; /* neutral-900 */\n --fs-dev-bg-header-muted: #171717;\n --fs-dev-bg-toggle-count: #262626; /* neutral-800 */\n --fs-dev-bg-close-hover: #404040; /* neutral-700 */\n --fs-dev-bg-tag: #262626; /* neutral-800 */\n --fs-dev-bg-status: #171717; /* neutral-900 */\n --fs-dev-bg-snapshot: #0a0a0a; /* neutral-950ish */\n\n --fs-dev-border-strong: #404040; /* neutral-700 */\n --fs-dev-border-subtle: #262626; /* neutral-800 */\n --fs-dev-border-soft: #262626; /* neutral-800 */\n --fs-dev-border-snapshot: #404040; /* neutral-700 */\n\n --fs-dev-text-primary: #fafafa; /* neutral-50 */\n --fs-dev-text-muted: #a3a3a3; /* neutral-400 */\n --fs-dev-text-soft: #d4d4d4; /* neutral-300 */\n --fs-dev-text-error: #fecaca;\n\n --fs-dev-badge-success-bg: rgba(22, 163, 74, 0.25);\n --fs-dev-badge-success-text: #bbf7d0;\n --fs-dev-badge-warn-bg: rgba(217, 119, 6, 0.3);\n --fs-dev-badge-warn-text: #fed7aa;\n\n --fs-dev-shadow-toggle: 0 1px 3px rgba(0, 0, 0, 0.6);\n --fs-dev-shadow-toggle-hover: 0 2px 6px rgba(0, 0, 0, 0.7);\n --fs-dev-shadow-panel: 0 20px 40px rgba(0, 0, 0, 0.8);\n}\n.fs-devtools-toggle {\n position: fixed;\n bottom: 16px;\n right: 16px;\n z-index: 9999;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n border-radius: 999px;\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-surface);\n padding: 4px 8px;\n font-size: 12px;\n color: var(--fs-dev-text-soft);\n box-shadow: var(--fs-dev-shadow-toggle);\n cursor: pointer;\n}\n\n.fs-devtools-toggle:hover {\n box-shadow: var(--fs-dev-shadow-toggle-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-toggle-count {\n margin-left: 4px;\n border-radius: 999px;\n background: var(--fs-dev-bg-toggle-count);\n padding: 0 4px;\n font-size: 10px;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-panel {\n position: fixed;\n bottom: 56px;\n overflow: hidden;\n right: 16px;\n z-index: 9999;\n width: 420px;\n max-height: 60vh;\n border-radius: 12px;\n color: var(--fs-dev-text-primary);\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-panel);\n box-shadow: var(--fs-dev-shadow-panel);\n display: flex;\n flex-direction: column;\n font-size: 14px;\n}\n\n.fs-devtools-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-subtle);\n background: var(--fs-dev-bg-header);\n}\n\n.fs-devtools-header-left {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.fs-devtools-header-text {\n display: flex;\n flex-direction: column;\n}\n\n.fs-devtools-header-title {\n font-size: 12px;\n font-weight: 600;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-header-subtitle {\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-header-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.fs-devtools-clear {\n border-radius: 999px;\n padding: 2px 8px;\n font-size: 11px;\n border: 1px solid var(--fs-dev-border-subtle);\n background: transparent;\n color: var(--fs-dev-text-muted);\n cursor: pointer;\n}\n\n.fs-devtools-clear:hover:enabled {\n background: var(--fs-dev-bg-close-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-clear:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.fs-devtools-close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 999px;\n padding: 4px;\n background: transparent;\n border: none;\n cursor: pointer;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-close:hover {\n background: var(--fs-dev-bg-close-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-filters {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-subtle);\n}\n\n.fs-devtools-select {\n flex: 1;\n border-radius: 4px;\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-panel);\n padding: 2px 6px;\n font-size: 12px;\n}\n\n.fs-devtools-filter-label {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-body {\n flex: 1;\n overflow: auto;\n}\n\n.fs-devtools-empty {\n padding: 10px 12px;\n font-size: 12px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-list {\n list-style: none;\n margin: 0;\n padding: 0;\n border-top: 1px solid var(--fs-dev-border-soft);\n}\n\n.fs-devtools-item {\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-soft);\n}\n\n.fs-devtools-item-toggle {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n background: transparent;\n border: none;\n padding: 0;\n cursor: pointer;\n text-align: left;\n}\n\n.fs-devtools-item-main {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n min-width: 0;\n}\n\n.fs-devtools-item-text {\n min-width: 0;\n}\n\n.fs-devtools-item-tags {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.fs-devtools-tag {\n display: inline-flex;\n align-items: center;\n border-radius: 999px;\n background: var(--fs-dev-bg-tag);\n padding: 2px 6px;\n font-size: 10px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n \"Courier New\", monospace;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-id {\n font-size: 10px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-meta {\n margin-top: 2px;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-delete {\n border: none;\n background: transparent;\n padding: 0;\n font-size: 11px;\n color: var(--fs-dev-text-soft);\n cursor: pointer;\n}\n\n.fs-devtools-delete:hover {\n text-decoration: underline;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-badge {\n border-radius: 999px;\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.fs-devtools-badge-success {\n background: var(--fs-dev-badge-success-bg);\n color: var(--fs-dev-badge-success-text);\n}\n\n.fs-devtools-badge-warn {\n background: var(--fs-dev-badge-warn-bg);\n color: var(--fs-dev-badge-warn-text);\n}\n\n.fs-devtools-status {\n border-radius: 999px;\n background: var(--fs-dev-bg-status);\n padding: 2px 6px;\n font-size: 10px;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-error {\n margin-left: 8px;\n max-width: 40%;\n font-size: 11px;\n color: var(--fs-dev-text-error);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.fs-devtools-snapshot {\n margin-top: 6px;\n border-radius: 6px;\n background: var(--fs-dev-bg-snapshot);\n font-size: 12px;\n}\n`\n\nexport function ensureFormSnapshotsStyles() {\n\tif (typeof document === \"undefined\") return\n\tif (document.getElementById(STYLE_ID)) return\n\n\tconst style = document.createElement(\"style\")\n\tstyle.id = STYLE_ID\n\tstyle.type = \"text/css\"\n\tstyle.appendChild(\n\t\tdocument.createTextNode(`${SNAPSHOT_TABLE_CSS}\\n\\n${DEVTOOLS_CSS}`),\n\t)\n\t;(document.head || document.documentElement).appendChild(style)\n}\n\n","import type { UseFormReturn } from \"react-hook-form\"\nimport { useFormSnapshots, type FormSnapshotsOptions } from \"./hooks/use-form-snapshots\"\n\ntype WithoutSnapshotOverrides = Omit<\n\tFormSnapshotsOptions,\n\t\"getValues\" | \"applyValues\"\n>\n\nexport interface RHFSnapshotsOptions extends WithoutSnapshotOverrides {}\n\nexport function useRHFFormSnapshots<TFieldValues extends Record<string, unknown>>(\n\tformName: string,\n\tform: UseFormReturn<TFieldValues>,\n\toptions?: RHFSnapshotsOptions,\n) {\n\treturn useFormSnapshots(formName, {\n\t\t...options,\n\t\tgetValues: () => form.getValues() as Record<string, unknown>,\n\t\tapplyValues: (snap) => form.reset(snap as TFieldValues),\n\t})\n}\n\nexport interface ObjectStateSnapshotsOptions\n\textends WithoutSnapshotOverrides {}\n\nexport function useObjectFormSnapshots<TState extends Record<string, unknown>>(\n\tformName: string,\n\tstate: TState,\n\tsetState: (next: TState) => void,\n\toptions?: ObjectStateSnapshotsOptions,\n) {\n\treturn useFormSnapshots(formName, {\n\t\t...options,\n\t\tgetValues: () => state as Record<string, unknown>,\n\t\tapplyValues: (snap) => setState(snap as TState),\n\t})\n}\n\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4I3B,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["import_react","import_react","import_dexie_react_hooks","import_react","import_jsx_runtime","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../form-snapshots/index.ts","../form-snapshots/hooks/use-form-snapshots.ts","../form-snapshots/client.ts","../form-snapshots/local-db/db.ts","../form-snapshots/dexie-storage.ts","../form-snapshots/dom-snapshot.ts","../form-snapshots/context.tsx","../form-snapshots/hooks/use-form-snapshots-list.ts","../form-snapshots/devtools.tsx","../form-snapshots/snapshot-table.tsx","../form-snapshots/styles.ts","../form-snapshots/adapters.ts"],"sourcesContent":["export { useFormSnapshots } from \"./hooks/use-form-snapshots\"\nexport type { FormSnapshotsOptions } from \"./hooks/use-form-snapshots\"\n\nexport {\n\tuseFormSnapshotsList,\n\ttype useFormSnapshotsListOptions,\n\ttype FormHistoryListItem,\n} from \"./hooks/use-form-snapshots-list\"\n\nexport {\n\tFormSnapshotsProvider,\n\tuseFormSnapshotsConfig,\n\ttype FormSnapshotsProviderProps,\n} from \"./context\"\n\nexport { FormSnapshotsDevtools } from \"./devtools\"\n\nexport {\n\tFormSnapshotTable,\n\ttype FormSnapshotTableProps,\n} from \"./snapshot-table\"\n\nexport {\n\tuseRHFFormSnapshots,\n\tuseObjectFormSnapshots,\n\ttype RHFSnapshotsOptions,\n\ttype ObjectStateSnapshotsOptions,\n} from \"./adapters\"\n\nexport { FormSnapshotsClient } from \"./client\"\n\nexport type {\n\tFormSnapshot,\n\tFormSessionBase,\n\tFormSnapshotsStorage,\n} from \"./types\"\n\nexport { db } from \"./local-db/db\"\nexport type { FormSession } from \"./local-db/db\"\n","import {\n\tuseCallback,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n\ttype SyntheticEvent,\n} from \"react\"\nimport { FormSnapshotsClient } from \"../client\"\nimport { DexieFormSnapshotsStorage } from \"../dexie-storage\"\nimport { type FormSnapshot, type FormSnapshotsStorage } from \"../types\"\nimport {\n\trestoreSnapshotToForm,\n\tsnapshotFromDOMForm,\n} from \"../dom-snapshot\"\nimport { useFormSnapshotsConfig } from \"../context\"\n\nexport interface FormSnapshotsOptions {\n\t/** How long in ms to retain history entries. Default: 24 h */\n\tsnapshotsLimit?: number\n\n\t/**\n\t * Field paths (path/prefix) to exclude while saving snapshots.\n\t * E.g. \"password\", \"patient.address\".\n\t * Merged with the global excludeFields config.\n\t */\n\texcludeFields?: string[]\n\n\t/**\n\t * Override how the current form state is read.\n\t *\n\t * Use this for controlled forms or nested / structured data models\n\t * (e.g. HL7 FHIR Address, HumanName, ContactPoint[]) where values live\n\t * in React state rather than plain DOM elements.\n\t */\n\tgetValues?: () => FormSnapshot\n\n\t/**\n\t * Override how a saved snapshot is applied back to the form.\n\t *\n\t * Receives the full snapshot that was previously returned by `getValues`.\n\t */\n\tapplyValues?: (snapshot: FormSnapshot) => void\n\n\t/**\n\t * When true, the active session is deleted from storage and the form\n\t * is cleared immediately after submit so that submitted data is not\n\t * kept in history. Can be overridden globally via provider config.\n\t */\n\tdiscardOnSubmit?: boolean\n}\n\nfunction applyExcludeFields(\n\tsnapshot: FormSnapshot,\n\texcludeFields?: string[],\n): FormSnapshot {\n\tif (!excludeFields || excludeFields.length === 0) return snapshot\n\n\tfunction pruneByPrefix(\n\t\tobj: FormSnapshot,\n\t\tpaths: string[],\n\t\tparentPath = \"\",\n\t): FormSnapshot {\n\t\tconst result: FormSnapshot = {}\n\n\t\tfor (const [key, value] of Object.entries(obj)) {\n\t\t\tconst path = parentPath ? `${parentPath}.${key}` : key\n\n\t\t\tconst isExcluded = paths.some(\n\t\t\t\t(p) => path === p || path.startsWith(`${p}.`),\n\t\t\t)\n\t\t\tif (isExcluded) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (value && typeof value === \"object\" && !Array.isArray(value)) {\n\t\t\t\tresult[key] = pruneByPrefix(\n\t\t\t\t\tvalue as FormSnapshot,\n\t\t\t\t\tpaths,\n\t\t\t\t\tpath,\n\t\t\t\t)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// For now, arrays are treated as atomic: you can exclude the whole\n\t\t\t// array field with its path, but not individual items.\n\t\t\tresult[key] = value\n\t\t}\n\n\t\treturn result\n\t}\n\n\treturn pruneByPrefix(snapshot, excludeFields)\n}\n\nexport function useFormSnapshots(\n\tformName: string,\n\toptions?: FormSnapshotsOptions,\n) {\n\tconst { snapshotsLimit, getValues, applyValues, excludeFields, discardOnSubmit } =\n\t\toptions ?? {}\n\n\tconst formRef = useRef<HTMLFormElement>(null)\n\tconst sessionIdRef = useRef<number | null>(null)\n\tconst lastSubmittedSessionIdRef = useRef<number | null>(null)\n\tconst clientRef = useRef<FormSnapshotsClient | null>(null)\n\tconst [isSubmitted, setIsSubmitted] = useState(false)\n\tconst storageRef = useRef<FormSnapshotsStorage | null>(null)\n\n\tconst {\n\t\tdefaultSnapshotsLimit,\n\t\texcludeFields: globalExcludeFields,\n\t\tdiscardOnSubmit: globalDiscardOnSubmit,\n\t} = useFormSnapshotsConfig()\n\n\tconst effectiveSnapshotsLimit =\n\t\tsnapshotsLimit ?? defaultSnapshotsLimit ?? 24 * 60 * 60 * 1000\n\n\tconst effectiveDiscardOnSubmit =\n\t\ttypeof discardOnSubmit === \"boolean\"\n\t\t\t? discardOnSubmit\n\t\t\t: globalDiscardOnSubmit ?? false\n\n\tconst mergedExcludeFields =\n\t\tglobalExcludeFields || excludeFields\n\t\t\t? Array.from(\n\t\t\t\t\tnew Set([...(globalExcludeFields ?? []), ...(excludeFields ?? [])]),\n\t\t\t\t)\n\t\t\t: undefined\n\n\t// Keep latest callbacks in refs so they never invalidate the init effect\n\tconst getValuesRef = useRef(getValues)\n\tconst applyValuesRef = useRef(applyValues)\n\tuseEffect(() => { getValuesRef.current = getValues }, [getValues])\n\tuseEffect(() => { applyValuesRef.current = applyValues }, [applyValues])\n\n\t// ── Initialise: create client, prune, resume or open session ───────────────\n\tuseEffect(() => {\n\t\tlet cancelled = false\n\n\t\tasync function init() {\n\t\t\tif (!storageRef.current) {\n\t\t\t\tstorageRef.current = new DexieFormSnapshotsStorage()\n\t\t\t}\n\n\t\t\tconst client = new FormSnapshotsClient({\n\t\t\t\tstorage: storageRef.current!,\n\t\t\t\tformName,\n\t\t\t\tsnapshotsLimit: effectiveSnapshotsLimit,\n\t\t\t})\n\n\t\t\tclientRef.current = client\n\n\t\t\tconst session = await client.initSession()\n\t\t\tif (cancelled) return\n\n\t\t\tsessionIdRef.current = session.id\n\n\t\t\t// Restore saved values (after mount) via applyValues callback when\n\t\t\t// provided, or fall back to DOM-based restoration for flat forms.\n\t\t\tif (session.data && session.data !== \"{}\") {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tif (cancelled) return\n\t\t\t\t\tconst snapshot = JSON.parse(session.data) as FormSnapshot\n\t\t\t\t\tif (applyValuesRef.current) {\n\t\t\t\t\t\tapplyValuesRef.current(snapshot)\n\t\t\t\t\t} else if (formRef.current) {\n\t\t\t\t\t\trestoreSnapshotToForm(formRef.current, snapshot)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tinit()\n\n\t\treturn () => {\n\t\t\tcancelled = true\n\t\t}\n\t}, [formName, effectiveSnapshotsLimit])\n\n\t// ── Snapshot current form values into the active session ──────────────────\n\tconst handleBlur = useCallback(() => {\n\t\tconst sessionId = sessionIdRef.current\n\t\tif (sessionId === null) return\n\n\t\tlet snapshot: FormSnapshot\n\n\t\tif (getValuesRef.current) {\n\t\t\t// Controlled / structured form — caller owns the state.\n\t\t\t// Supports arbitrarily nested values: HL7 Address, HumanName,\n\t\t\t// ContactPoint[], CodeableConcept, etc.\n\t\t\tsnapshot = getValuesRef.current()\n\t\t} else {\n\t\t\t// Fallback: enumerate flat DOM form elements (strings / booleans only).\n\t\t\tconst form = formRef.current\n\t\t\tif (!form) return\n\t\t\tsnapshot = snapshotFromDOMForm(form)\n\t\t}\n\n\t\tsnapshot = applyExcludeFields(snapshot, mergedExcludeFields)\n\n\t\tif (!clientRef.current) return\n\n\t\tclientRef.current.saveSnapshot(sessionId, snapshot)\n\t}, []) // getValues is read via ref — no dep needed\n\n\t// ── Clear current form values after submit when requested ─────────────────\n\tconst clearFormState = useCallback(() => {\n\t\tif (applyValuesRef.current) {\n\t\t\t// For controlled / structured forms, an empty snapshot is passed\n\t\t\t// back to the caller so they can reset their own state.\n\t\t\tapplyValuesRef.current({} as FormSnapshot)\n\t\t} else if (formRef.current) {\n\t\t\t// For plain DOM forms, reset back to the initial values.\n\t\t\tformRef.current.reset()\n\t\t}\n\t}, [])\n\n\t// ── Wrap the form's onSubmit to close the session ─────────────────────────\n\tconst wrapSubmit = useCallback(\n\t\t(\n\t\t\tuserSubmit?: (e: SyntheticEvent<HTMLFormElement>) => void,\n\t\t) =>\n\t\t\t(e: SyntheticEvent<HTMLFormElement>) => {\n\t\t\t\te.preventDefault()\n\t\t\t\thandleBlur() // capture the final state before closing\n\n\t\t\t\tconst sessionId = sessionIdRef.current\n\t\t\t\tconst client = clientRef.current\n\t\t\t\tif (sessionId !== null && client) {\n\t\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\t\t// Discard the entire session so submitted data is not kept.\n\t\t\t\t\t\tclient.deleteSession(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = null\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclient.markSubmitted(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = sessionId\n\t\t\t\t\t}\n\t\t\t\t\t// Closed session should no longer receive snapshots\n\t\t\t\t\tsessionIdRef.current = null\n\t\t\t\t}\n\n\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\tclearFormState()\n\t\t\t\t}\n\n\t\t\t\tsetIsSubmitted(true)\n\t\t\t\tuserSubmit?.(e)\n\t\t\t},\n\t\t[handleBlur, clearFormState, effectiveDiscardOnSubmit],\n\t)\n\n\tconst markSubmitResult = useCallback(\n\t\tasync (params: {\n\t\t\tstatusCode?: number | null\n\t\t\terrorMessage?: string | null\n\t\t}) => {\n\t\t\tconst client = clientRef.current\n\t\t\tconst sessionId = lastSubmittedSessionIdRef.current\n\t\t\tif (!client || sessionId == null) return\n\n\t\t\tawait client.setSubmissionResult({\n\t\t\t\tsessionId,\n\t\t\t\tstatusCode: params.statusCode,\n\t\t\t\terrorMessage: params.errorMessage,\n\t\t\t})\n\t\t},\n\t\t[],\n\t)\n\n\tconst wrapSubmitAsync = useCallback(\n\t\t(\n\t\t\t\tuserSubmit?: (\n\t\t\t\t\te: SyntheticEvent<HTMLFormElement>,\n\t\t\t\t) => Promise<\n\t\t\t\t\t| void\n\t\t\t\t\t| {\n\t\t\t\t\t\t\tstatusCode?: number | null\n\t\t\t\t\t\t\terrorMessage?: string | null\n\t\t\t\t\t }\n\t\t\t\t>,\n\t\t\t) =>\n\t\t\tasync (e: SyntheticEvent<HTMLFormElement>) => {\n\t\t\t\te.preventDefault()\n\t\t\t\thandleBlur()\n\n\t\t\t\tconst sessionId = sessionIdRef.current\n\t\t\t\tconst client = clientRef.current\n\t\t\t\tif (sessionId !== null && client) {\n\t\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\t\tawait client.deleteSession(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = null\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait client.markSubmitted(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = sessionId\n\t\t\t\t\t}\n\t\t\t\t\tsessionIdRef.current = null\n\t\t\t\t}\n\n\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\tclearFormState()\n\t\t\t\t}\n\n\t\t\t\tsetIsSubmitted(true)\n\n\t\t\t\tif (!userSubmit) return\n\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await userSubmit(e)\n\t\t\t\t\tif (result && typeof result === \"object\") {\n\t\t\t\t\t\tawait markSubmitResult({\n\t\t\t\t\t\t\tstatusCode: \"statusCode\" in result ? result.statusCode : null,\n\t\t\t\t\t\t\terrorMessage:\n\t\t\t\t\t\t\t\t\"errorMessage\" in result ? result.errorMessage : null,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t// In case of error we leave handling to the caller;\n\t\t\t// logging here is enough. Callers can still invoke\n\t\t\t// markSubmitResult manually if they want.\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.error(\"wrapSubmitAsync userSubmit error:\", error)\n\t\t\t\t}\n\t\t\t},\n\t\t[handleBlur, markSubmitResult, clearFormState, effectiveDiscardOnSubmit],\n\t)\n\n\t// ── Restore the latest saved snapshot into the DOM form ───────────────────\n\tconst restoreLatest = useCallback(async () => {\n\t\tif (!clientRef.current) return\n\n\t\tconst snapshot = await clientRef.current.getLatestSnapshot()\n\t\tif (!snapshot) return\n\n\t\tif (applyValuesRef.current) {\n\t\t\t// Structured form — route the full nested snapshot back to state.\n\t\t\tapplyValuesRef.current(snapshot)\n\t\t} else {\n\t\t\tif (!formRef.current) return\n\t\t\trestoreSnapshotToForm(formRef.current, snapshot)\n\t\t}\n\n\t\t// Open a new session pre-seeded with the restored data so further edits\n\t\t// are tracked against the fresh session\n\t\tconst newSession = await clientRef.current.openNewSessionFromSnapshot(\n\t\t\tsnapshot,\n\t\t)\n\t\tsessionIdRef.current = newSession.id\n\n\t\tsetIsSubmitted(false)\n\t}, []) // applyValues is read via ref — no dep needed\n\n\treturn {\n\t\tformRef,\n\t\thandleBlur,\n\t\twrapSubmit,\n\t\twrapSubmitAsync,\n\t\trestoreLatest,\n\t\tisSubmitted,\n\t\tmarkSubmitResult,\n\t}\n}\n","import type {\n\tFormSnapshotsStorage,\n\tFormSessionBase,\n\tFormSnapshot,\n} from \"./types\"\n\nexport class FormSnapshotsClient {\n\tprivate readonly storage: FormSnapshotsStorage\n\tprivate readonly formName: string\n\tprivate readonly snapshotsLimit: number\n\n\tconstructor(params: {\n\t\tstorage: FormSnapshotsStorage\n\t\tformName: string\n\t\tsnapshotsLimit: number\n\t}) {\n\t\tthis.storage = params.storage\n\t\tthis.formName = params.formName\n\t\tthis.snapshotsLimit = params.snapshotsLimit\n\t}\n\n\tasync initSession(): Promise<FormSessionBase> {\n\t\tconst now = Date.now()\n\t\tconst cutoff = now - this.snapshotsLimit\n\n\t\tawait this.storage.pruneOlderThan(cutoff)\n\n\t\tconst existing = await this.storage.findActiveSession(this.formName)\n\t\tif (existing) return existing\n\n\t\treturn this.storage.createSession(this.formName, {})\n\t}\n\n\tasync saveSnapshot(\n\t\tsessionId: number,\n\t\tsnapshot: FormSnapshot,\n\t): Promise<void> {\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tdata: JSON.stringify(snapshot),\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync markSubmitted(sessionId: number): Promise<void> {\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tsubmitted: true,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync deleteSession(sessionId: number): Promise<void> {\n\t\tif (typeof this.storage.deleteSession === \"function\") {\n\t\t\tawait this.storage.deleteSession(sessionId)\n\t\t}\n\t}\n\n\tasync setSubmissionResult(params: {\n\t\tsessionId: number\n\t\tstatusCode?: number | null\n\t\terrorMessage?: string | null\n\t}): Promise<void> {\n\t\tconst { sessionId, statusCode, errorMessage } = params\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tstatusCode: statusCode ?? null,\n\t\t\terrorMessage: errorMessage ?? null,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync getLatestSnapshot(): Promise<FormSnapshot | null> {\n\t\tconst latest = await this.storage.getLatestSession(this.formName)\n\t\tif (!latest) return null\n\t\treturn JSON.parse(latest.data) as FormSnapshot\n\t}\n\n\tasync openNewSessionFromSnapshot(\n\t\tsnapshot: FormSnapshot,\n\t): Promise<FormSessionBase> {\n\t\treturn this.storage.createSession(this.formName, snapshot)\n\t}\n}\n\n","import { Dexie, type EntityTable } from \"dexie\"\n\nexport interface FormSession {\n\tid: number\n\tformName: string\n\t/** JSON-serialised record of field name → value */\n\tdata: string\n\tcreatedAt: number\n\tupdatedAt: number\n\t/** true = the form was submitted; session is closed / read-only */\n\tsubmitted: boolean\n\t/** Optional HTTP-like status code for the last submit attempt */\n\tstatusCode?: number | null\n\t/** Optional error message when the last submit attempt failed */\n\terrorMessage?: string | null\n}\n\nconst db = new Dexie(\"FormSnapshotsDatabase\") as Dexie & {\n\tformSessions: EntityTable<FormSession, \"id\">\n}\n\ndb.version(1).stores({\n\tformSessions: \"++id, formName, createdAt, submitted, [formName+createdAt]\",\n})\n\ndb.version(2).stores({\n\tformSessions:\n\t\t\"++id, formName, createdAt, updatedAt, submitted, [formName+createdAt]\",\n})\n\nexport { db }\n\n","import { db, type FormSession } from \"./local-db/db\"\nimport {\n\ttype FormSnapshotsStorage,\n\ttype FormSessionBase,\n\ttype FormSnapshot,\n} from \"./types\"\n\nfunction mapToBase(session: FormSession): FormSessionBase {\n\treturn {\n\t\tid: session.id,\n\t\tformName: session.formName,\n\t\tdata: session.data,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t\tsubmitted: session.submitted,\n\t\tstatusCode: session.statusCode ?? null,\n\t\terrorMessage: session.errorMessage ?? null,\n\t}\n}\n\n\texport class DexieFormSnapshotsStorage implements FormSnapshotsStorage {\n\tasync findActiveSession(formName: string): Promise<FormSessionBase | null> {\n\t\tconst existing = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.filter((s) => !s.submitted)\n\t\t\t.last()\n\n\t\treturn existing ? mapToBase(existing) : null\n\t}\n\n\tasync createSession(\n\t\tformName: string,\n\t\tsnapshot: FormSnapshot,\n\t): Promise<FormSessionBase> {\n\t\tconst now = Date.now()\n\t\tconst id = await db.formSessions.add({\n\t\t\tformName,\n\t\t\tdata: JSON.stringify(snapshot),\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tsubmitted: false,\n\t\t\tstatusCode: null,\n\t\t\terrorMessage: null,\n\t\t})\n\n\t\tconst created = await db.formSessions.get(id)\n\t\tif (!created) {\n\t\t\tthrow new Error(\"Failed to create form session\")\n\t\t}\n\n\t\treturn mapToBase(created)\n\t}\n\n\tasync updateSession(\n\t\tid: number,\n\t\tpatch: Partial<\n\t\t\tPick<\n\t\t\t\tFormSessionBase,\n\t\t\t\t\"data\" | \"updatedAt\" | \"submitted\" | \"statusCode\" | \"errorMessage\"\n\t\t\t>\n\t\t>,\n\t): Promise<void> {\n\t\tawait db.formSessions.update(id, patch)\n\t}\n\n\tasync getLatestSession(formName: string): Promise<FormSessionBase | null> {\n\t\tconst sessions = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.sortBy(\"updatedAt\")\n\n\t\tconst latest = sessions[sessions.length - 1]\n\t\treturn latest ? mapToBase(latest) : null\n\t}\n\n\tasync listSessions(formName: string): Promise<FormSessionBase[]> {\n\t\tconst sessions = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.sortBy(\"updatedAt\")\n\n\t\treturn sessions.map(mapToBase)\n\t}\n\n\tasync pruneOlderThan(cutoffMs: number): Promise<void> {\n\t\tawait db.formSessions.where(\"createdAt\").below(cutoffMs).delete()\n\t}\n\n\tasync deleteSession(id: number): Promise<void> {\n\t\tawait db.formSessions.delete(id)\n\t}\n}\n\n","type AnyFormEl = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement\n\nfunction toStringValue(val: unknown): string {\n\tif (val == null) return \"\"\n\tif (typeof val === \"object\") return JSON.stringify(val)\n\treturn String(val as string | number | boolean | bigint)\n}\n\nfunction applyValueToInput(input: AnyFormEl, val: unknown): void {\n\tif (input instanceof HTMLInputElement) {\n\t\tif (input.type === \"checkbox\") {\n\t\t\tinput.checked = Boolean(val)\n\t\t\tinput.dispatchEvent(new Event(\"change\", { bubbles: true }))\n\t\t\treturn\n\t\t}\n\t\tif (input.type === \"radio\") {\n\t\t\tinput.checked = input.value === val\n\t\t\tif (input.checked) input.dispatchEvent(new Event(\"change\", { bubbles: true }))\n\t\t\treturn\n\t\t}\n\t}\n\tinput.value = toStringValue(val)\n\tinput.dispatchEvent(new Event(\"input\", { bubbles: true }))\n}\n\n/** Populate DOM form elements from a plain snapshot object */\nexport function restoreSnapshotToForm(\n\tform: HTMLFormElement,\n\tsnapshot: Record<string, unknown>,\n) {\n\tfor (const el of Array.from(form.elements)) {\n\t\tconst input = el as AnyFormEl\n\t\tif (!input.name || !(input.name in snapshot)) continue\n\t\tapplyValueToInput(input, snapshot[input.name])\n\t}\n}\n\n/** Read all named fields from a plain DOM form into a flat snapshot object */\nexport function snapshotFromDOMForm(\n\tform: HTMLFormElement,\n): Record<string, unknown> {\n\tconst snapshot: Record<string, unknown> = {}\n\tfor (const el of Array.from(form.elements)) {\n\t\tconst input = el as AnyFormEl\n\t\tif (!input.name) continue\n\t\treadInputIntoSnapshot(input, snapshot)\n\t}\n\treturn snapshot\n}\n\nexport function readInputIntoSnapshot(\n\tinput: AnyFormEl,\n\tsnapshot: Record<string, unknown>,\n) {\n\tif (input instanceof HTMLInputElement) {\n\t\tif (input.type === \"checkbox\") {\n\t\t\tsnapshot[input.name] = input.checked\n\t\t} else if (input.type === \"radio\") {\n\t\t\tif (input.checked) snapshot[input.name] = input.value\n\t\t} else if (input.type !== \"file\") {\n\t\t\tsnapshot[input.name] = input.value\n\t\t}\n\t} else {\n\t\tsnapshot[input.name] = input.value\n\t}\n}\n\n","import type { ReactNode } from \"react\"\nimport { createContext, useContext } from \"react\"\n\nexport interface FormSnapshotsGlobalConfig {\n\tdefaultSnapshotsLimit?: number\n\t/**\n\t * Field paths (path/prefix) to exclude while saving snapshots.\n\t * E.g. [\"password\", \"patient.address\"]\n\t */\n\texcludeFields?: string[]\n\n\t/**\n\t * When true, submitted sessions are discarded and the form is cleared\n\t * immediately after submit instead of keeping the data in history.\n\t * Can be overridden per form via hook options.\n\t */\n\tdiscardOnSubmit?: boolean\n}\n\nconst FormSnapshotsConfigContext = createContext<FormSnapshotsGlobalConfig | undefined>(\n\tundefined,\n)\n\nexport interface FormSnapshotsProviderProps {\n\tvalue?: FormSnapshotsGlobalConfig\n\tchildren: ReactNode\n}\n\nexport function FormSnapshotsProvider({\n\tvalue,\n\tchildren,\n}: Readonly<FormSnapshotsProviderProps>) {\n\treturn (\n\t\t<FormSnapshotsConfigContext.Provider value={value ?? {}}>\n\t\t\t{children}\n\t\t</FormSnapshotsConfigContext.Provider>\n\t)\n}\n\nexport function useFormSnapshotsConfig(): FormSnapshotsGlobalConfig {\n\treturn useContext(FormSnapshotsConfigContext) ?? {}\n}\n\n","import { useLiveQuery } from \"dexie-react-hooks\"\nimport { db, type FormSession } from \"../local-db/db\"\n\nexport interface useFormSnapshotsListOptions {\n\t/**\n\t * Whether to return only submitted (completed) sessions.\n\t * Default: false (both submitted and in-progress).\n\t */\n\tonlySubmitted?: boolean\n\n\t/**\n\t * Whether to return only successful (2xx) submission results.\n\t * Default: false (all statusCode values).\n\t */\n\tonlySuccessful?: boolean\n\n\t/**\n\t * Maximum age in ms. E.g. last 24 hours = 24 * 60 * 60 * 1000.\n\t * Default: unlimited.\n\t */\n\tmaxAgeMs?: number\n\n\t/**\n\t * Maximum number of records to return.\n\t * Default: unlimited.\n\t */\n\tlimit?: number\n}\n\nexport interface FormHistoryListItem {\n\tid: number\n\tformName: string\n\tcreatedAt: number\n\tupdatedAt: number\n\tsubmitted: boolean\n\tstatusCode: number | null\n\terrorMessage: string | null\n\t/**\n\t * Age in ms relative to \\\"now\\\".\n\t */\n\tageMs: number\n}\n\nexport function useFormSnapshotsList(\n\tformName: string,\n\toptions?: useFormSnapshotsListOptions,\n) {\n\tconst {\n\t\tonlySubmitted = false,\n\t\tonlySuccessful = false,\n\t\tmaxAgeMs,\n\t\tlimit,\n\t} = options ?? {}\n\n\tconst items = useLiveQuery<FormHistoryListItem[] | undefined>(async () => {\n\t\tconst now = Date.now()\n\t\tconst cutoff = maxAgeMs ? now - maxAgeMs : undefined\n\n\t\tlet query = db.formSessions.where(\"formName\").equals(formName)\n\n\t\t// Other filters are not supported directly by Dexie, so we refine via JS filter.\n\t\tlet sessions: FormSession[] = await query.toArray()\n\n\t\tif (onlySubmitted) {\n\t\t\tsessions = sessions.filter((s) => s.submitted)\n\t\t}\n\n\t\tif (onlySuccessful) {\n\t\t\tsessions = sessions.filter((s) => {\n\t\t\t\tif (s.statusCode == null) return false\n\t\t\t\treturn s.statusCode >= 200 && s.statusCode < 300\n\t\t\t})\n\t\t}\n\n\t\tif (cutoff != null) {\n\t\t\tsessions = sessions.filter((s) => s.createdAt >= cutoff)\n\t\t}\n\n\t\tsessions.sort((a, b) => b.updatedAt - a.updatedAt)\n\n\t\tif (typeof limit === \"number\") {\n\t\t\tsessions = sessions.slice(0, limit)\n\t\t}\n\n\t\treturn sessions.map((s) => ({\n\t\t\tid: s.id,\n\t\t\tformName: s.formName,\n\t\t\tcreatedAt: s.createdAt,\n\t\t\tupdatedAt: s.updatedAt,\n\t\t\tsubmitted: s.submitted,\n\t\t\tstatusCode: s.statusCode ?? null,\n\t\t\terrorMessage: s.errorMessage ?? null,\n\t\t\tageMs: now - s.updatedAt,\n\t\t}))\n\t}, [formName, onlySubmitted, onlySuccessful, maxAgeMs, limit])\n\n\tconst isLoading = items === undefined\n\n\treturn { items: items ?? [], isLoading }\n}\n\n","import { useMemo, useState } from \"react\"\nimport { useLiveQuery } from \"dexie-react-hooks\"\nimport { db, type FormSession } from \"./local-db/db\"\nimport { FormSnapshotTable } from \"./snapshot-table\"\nimport { ensureFormSnapshotsStyles } from \"./styles\"\n\nfunction formatDateShort(ms: number) {\n\treturn new Intl.DateTimeFormat(\"en-GB\", {\n\t\tmonth: \"short\",\n\t\tday: \"2-digit\",\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t}).format(new Date(ms))\n}\n\nfunction formatAge(ms: number) {\n\tconst seconds = Math.floor(ms / 1000)\n\tconst minutes = Math.floor(seconds / 60)\n\tconst hours = Math.floor(minutes / 60)\n\tconst days = Math.floor(hours / 24)\n\n\tif (days > 0) return `${days}d ${hours % 24}h`\n\tif (hours > 0) return `${hours}h ${minutes % 60}m`\n\tif (minutes > 0) return `${minutes}m`\n\treturn `${seconds}s`\n}\n\nexport function FormSnapshotsDevtools() {\n\tensureFormSnapshotsStyles()\n\n\tconst isProd =\n\t\ttypeof import.meta !== \"undefined\" &&\n\t\t(import.meta as any).env?.PROD\n\n\tif (isProd) {\n\t\treturn null\n\t}\n\n\tconst [open, setOpen] = useState(false)\n\tconst [selectedForm, setSelectedForm] = useState<string>(\"all\")\n\tconst [onlySubmitted, setOnlySubmitted] = useState(false)\n\tconst [onlyErrors, setOnlyErrors] = useState(false)\n\tconst [expandedId, setExpandedId] = useState<number | null>(null)\n\tconst [isClearing, setIsClearing] = useState(false)\n\n\tconst sessions = useLiveQuery<FormSession[]>(() => {\n\t\treturn db.formSessions\n\t\t\t.toCollection()\n\t\t\t.sortBy(\"updatedAt\")\n\t\t\t.then((all) => all.reverse())\n\t}, [])\n\n\tconst now = Date.now()\n\n\tconst formNames = useMemo(() => {\n\t\tif (!sessions) return []\n\t\treturn Array.from(new Set(sessions.map((s) => s.formName))).sort()\n\t}, [sessions])\n\n\tconst filtered = useMemo(() => {\n\t\tif (!sessions) return []\n\n\t\treturn sessions.filter((s) => {\n\t\t\tif (selectedForm !== \"all\" && s.formName !== selectedForm) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (onlySubmitted && !s.submitted) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (onlyErrors && (s.statusCode == null || s.statusCode < 400)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}, [sessions, selectedForm, onlySubmitted, onlyErrors])\n\n\tconst handleDeleteSession = async (id: number) => {\n\t\ttry {\n\t\t\tawait db.formSessions.delete(id)\n\t\t} catch (error) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(\"Failed to delete form session\", error)\n\t\t}\n\t}\n\n\tconst handleClearAll = async () => {\n\t\tif (!sessions || sessions.length === 0) return\n\t\tsetIsClearing(true)\n\t\ttry {\n\t\t\tconst ids = sessions.map((s) => s.id)\n\t\t\tawait db.formSessions.bulkDelete(ids)\n\t\t} catch (error) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(\"Failed to clear form sessions\", error)\n\t\t} finally {\n\t\t\tsetIsClearing(false)\n\t\t}\n\t}\n\n\tconst total = sessions?.length ?? 0\n\n\tif (!sessions || sessions.length === 0) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={() => setOpen((v) => !v)}\n\t\t\t\t\tclassName=\"fs-devtools-toggle\"\n\t\t\t>\n\t\t\t\t\t<HistoryIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t<span>Form Snapshot Devtools</span>\n\t\t\t\t{total > 0 && (\n\t\t\t\t\t\t<span className=\"fs-devtools-toggle-count\">\n\t\t\t\t\t\t{total}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</button>\n\n\t\t\t{open && (\n\t\t\t\t<div className=\"fs-devtools-panel\">\n\t\t\t\t\t<div className=\"fs-devtools-header\">\n\t\t\t\t\t\t<div className=\"fs-devtools-header-left\">\n\t\t\t\t\t\t\t<HistoryIcon style={{ width: 14, height: 14 }} />\n\t\t\t\t\t\t\t<div className=\"fs-devtools-header-text\">\n\t\t\t\t\t\t\t\t<span className=\"fs-devtools-header-title\">\n\t\t\t\t\t\t\t\t\tForm Snapshot Devtools\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span className=\"fs-devtools-header-subtitle\">\n\t\t\t\t\t\t\t\t\t{filtered.length} / {total} session\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"fs-devtools-header-actions\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={handleClearAll}\n\t\t\t\t\t\t\t\tclassName=\"fs-devtools-clear\"\n\t\t\t\t\t\t\t\tdisabled={isClearing || !sessions || sessions.length === 0}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tClear all\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => setOpen(false)}\n\t\t\t\t\t\t\t\tclassName=\"fs-devtools-close\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<XIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"fs-devtools-filters\">\n\t\t\t\t\t\t<FilterIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tclassName=\"fs-devtools-select\"\n\t\t\t\t\t\t\tvalue={selectedForm}\n\t\t\t\t\t\t\tonChange={(e) => setSelectedForm(e.target.value)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"all\">All forms</option>\n\t\t\t\t\t\t\t{formNames.map((name) => (\n\t\t\t\t\t\t\t\t<option key={name} value={name}>\n\t\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t\t<label className=\"fs-devtools-filter-label\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={onlySubmitted}\n\t\t\t\t\t\t\t\tonChange={(e) => setOnlySubmitted(e.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span>submitted</span>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<label className=\"fs-devtools-filter-label\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={onlyErrors}\n\t\t\t\t\t\t\t\tonChange={(e) => setOnlyErrors(e.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span>errors</span>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"fs-devtools-body\">\n\t\t\t\t\t\t{filtered.length === 0 ? (\n\t\t\t\t\t\t\t<p className=\"fs-devtools-empty\">\n\t\t\t\t\t\t\t\tNo sessions match the selected filters.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<ul className=\"fs-devtools-list\">\n\t\t\t\t\t\t\t\t{filtered.map((s) => (\n\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\tkey={s.id}\n\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-item\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\tsetExpandedId((current) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tcurrent === s.id ? null : s.id,\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-item-toggle\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-main\">\n\t\t\t\t\t\t\t\t\t\t\t\t<ChevronDownIcon\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: 12,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: 12,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttransform:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\texpandedId === s.id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"rotate(0deg)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"rotate(-90deg)\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttransition: \"transform 120ms ease-out\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-tags\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-tag\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.formName}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-id\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t#{s.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-meta\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span>{formatDateShort(s.updatedAt)}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span>· {formatAge(now - s.updatedAt)} ago</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.submitted ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-badge fs-devtools-badge-success\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsubmitted\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-badge fs-devtools-badge-warn\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tin progress\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.statusCode != null && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-status\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstatus {s.statusCode}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-delete\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvoid handleDeleteSession(s.id)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tDelete\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{typeof s.errorMessage === \"string\" && s.errorMessage && (\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-error\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{s.errorMessage}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t\t\t{expandedId === s.id && (\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-snapshot\">\n\t\t\t\t\t\t\t\t\t\t\t\t<FormSnapshotTable\n\t\t\t\t\t\t\t\t\t\t\t\t\tdata={s.data}\n\t\t\t\t\t\t\t\t\t\t\t\t\temptyMessage=\"This session does not contain any field data yet.\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</>\n\t)\n}\n\ntype IconProps = React.SVGProps<SVGSVGElement>\n\nfunction HistoryIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<path d=\"M3 3v6h6\" />\n\t\t\t<path d=\"M3.05 13A9 9 0 1 0 9 3.05\" />\n\t\t\t<path d=\"M12 7v5l3 3\" />\n\t\t</svg>\n\t)\n}\n\nfunction XIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n\t\t\t<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n\t\t</svg>\n\t)\n}\n\nfunction FilterIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\" />\n\t\t</svg>\n\t)\n}\n\nfunction ChevronDownIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<polyline points=\"6 9 12 15 18 9\" />\n\t\t</svg>\n\t)\n}\n\n","import { useMemo } from \"react\"\nimport { ensureFormSnapshotsStyles } from \"./styles\"\n\ntype Primitive = string | number | boolean | null | undefined\n\nfunction isPrimitive(val: unknown): val is Primitive {\n\treturn (\n\t\tval === null ||\n\t\tval === undefined ||\n\t\ttypeof val === \"string\" ||\n\t\ttypeof val === \"number\" ||\n\t\ttypeof val === \"boolean\"\n\t)\n}\n\nexport interface FormSnapshotTableProps {\n\t/**\n\t * JSON-stringified snapshot. Genellikle `FormSession.data`.\n\t */\n\tdata: string\n\t/**\n\t * Message to display when the table is empty.\n\t * Default: \"No data captured yet.\"\n\t */\n\temptyMessage?: string\n\t/**\n\t * Extra class names for the outer wrapper.\n\t */\n\tclassName?: string\n}\n\ntype ParsedSnapshot = {\n\tsnapshot: Record<string, unknown>\n\tfieldCount: number\n}\n\nconst snapshotCache = new Map<string, ParsedSnapshot>()\n\nfunction parseSnapshot(data: string): ParsedSnapshot {\n\tconst cached = snapshotCache.get(data)\n\tif (cached) return cached\n\n\tlet snapshot: Record<string, unknown> = {}\n\ttry {\n\t\tsnapshot = JSON.parse(data) as Record<string, unknown>\n\t} catch {\n\t\tsnapshot = {}\n\t}\n\n\tconst parsed = {\n\t\tsnapshot,\n\t\tfieldCount: Object.keys(snapshot).length,\n\t}\n\n\tsnapshotCache.set(data, parsed)\n\treturn parsed\n}\n\nexport function FormSnapshotTable({\n\tdata,\n\temptyMessage = \"No data captured yet.\",\n\tclassName,\n}: Readonly<FormSnapshotTableProps>) {\n\tensureFormSnapshotsStyles()\n\n\tconst { snapshot, fieldCount } = useMemo(() => parseSnapshot(data), [data])\n\n\tif (fieldCount === 0) {\n\t\treturn (\n\t\t\t<p className=\"fs-snapshot-empty\">\n\t\t\t\t{emptyMessage}\n\t\t\t</p>\n\t\t)\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tclassName={[\n\t\t\t\t\"fs-snapshot-wrapper\",\n\t\t\t\tclassName,\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \")}\n\t\t>\n\t\t\t<table className=\"fs-snapshot-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr className=\"fs-snapshot-header-row\">\n\t\t\t\t\t\t<th className=\"fs-snapshot-header-cell\">\n\t\t\t\t\t\t\tField\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"fs-snapshot-header-cell\">\n\t\t\t\t\t\t\tValue\n\t\t\t\t\t\t</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{Object.entries(snapshot).map(([key, value]) => (\n\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\tclassName=\"fs-snapshot-row\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<td className=\"fs-snapshot-key\">\n\t\t\t\t\t\t\t\t{key}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td className=\"fs-snapshot-value\">\n\t\t\t\t\t\t\t\t<SnapshotValueCell value={value} />\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t)\n}\n\nfunction SnapshotValueCell({ value }: Readonly<{ value: unknown }>) {\n\tif (isPrimitive(value)) {\n\t\tif (value === null || value === undefined || value === \"\") {\n\t\t\treturn (\n\t\t\t\t<span className=\"fs-snapshot-muted\">\n\t\t\t\t\t—\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\tif (typeof value === \"boolean\") {\n\t\t\treturn (\n\t\t\t\t<span\n\t\t\t\t\tclassName={\n\t\t\t\t\t\tvalue\n\t\t\t\t\t\t\t? \"fs-snapshot-badge fs-snapshot-badge-true\"\n\t\t\t\t\t\t\t: \"fs-snapshot-badge fs-snapshot-badge-false\"\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t{String(value)}\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\treturn <span className=\"fs-snapshot-text\">{String(value)}</span>\n\t}\n\n\tif (Array.isArray(value)) {\n\t\tif (value.length === 0) {\n\t\t\treturn (\n\t\t\t\t<span className=\"fs-snapshot-muted\">\n\t\t\t\t\t—\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\treturn (\n\t\t\t<div className=\"fs-snapshot-array\">\n\t\t\t\t{value.map((v, idx) => (\n\t\t\t\t\t// eslint-disable-next-line react/no-array-index-key\n\t\t\t\t\t<span\n\t\t\t\t\t\tkey={idx}\n\t\t\t\t\t\tclassName=\"fs-snapshot-chip\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{String(v)}\n\t\t\t\t\t</span>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t)\n\t}\n\n\treturn (\n\t\t<pre className=\"fs-snapshot-object\">\n\t\t\t{JSON.stringify(value, null, 2)}\n\t\t</pre>\n\t)\n}\n\n","const STYLE_ID = \"form-snapshots-styles\"\n\nconst SNAPSHOT_TABLE_CSS = `:root {\n /* light – neutral-like palette */\n --fs-st-border: #e5e5e5; /* neutral-200 */\n --fs-st-border-soft: #d4d4d4; /* neutral-300 */\n --fs-st-header-bg: #f5f5f5; /* neutral-100 */\n --fs-st-key-text: #737373; /* neutral-500 */\n --fs-st-muted-text: #737373; /* neutral-500 */\n --fs-st-muted-strong: #a3a3a3; /* neutral-400 */\n --fs-st-chip-bg: #e5e5e5; /* neutral-200 */\n --fs-st-chip-text: #171717; /* neutral-900 */\n --fs-st-object-bg: #f5f5f5; /* neutral-100 */\n --fs-st-badge-true-bg: rgba(22, 163, 74, 0.1);\n --fs-st-badge-true-text: #166534;\n --fs-st-badge-false-border: #d4d4d4;\n --fs-st-badge-false-text: #525252; /* neutral-600 */\n --fs-st-text-main: #171717; /* neutral-900 */\n}\n\nhtml.dark {\n /* dark – neutral-900/950 style */\n --fs-st-border: #404040; /* neutral-700 */\n --fs-st-border-soft: #262626; /* neutral-800 */\n --fs-st-header-bg: #171717; /* neutral-900 */\n --fs-st-key-text: #a3a3a3; /* neutral-400 */\n --fs-st-muted-text: #a3a3a3; /* neutral-400 */\n --fs-st-muted-strong: #737373; /* neutral-500 */\n --fs-st-chip-bg: #262626; /* neutral-800 */\n --fs-st-chip-text: #f5f5f5; /* neutral-100 */\n --fs-st-object-bg: #0a0a0a; /* neutral-950ish */\n --fs-st-badge-true-bg: rgba(22, 163, 74, 0.25);\n --fs-st-badge-true-text: #bbf7d0;\n --fs-st-badge-false-border: #404040;\n --fs-st-badge-false-text: #e5e5e5; /* neutral-200 */\n --fs-st-text-main: #f5f5f5; /* neutral-100 */\n}\n.fs-snapshot-wrapper {\n border-radius: 6px;\n border: 1px solid var(--fs-st-border);\n overflow: hidden;\n color: var(--fs-st-text-main);\n}\n\n.fs-snapshot-table {\n width: 100%;\n font-size: 12px;\n border-collapse: collapse;\n}\n\n.fs-snapshot-header-row {\n border-bottom: 1px solid var(--fs-st-border);\n background: var(--fs-st-header-bg);\n}\n\n.fs-snapshot-header-cell {\n padding: 4px 6px;\n text-align: left;\n font-weight: 500;\n color: var(--fs-st-muted-text);\n}\n\n.fs-snapshot-row {\n border-bottom: 1px solid var(--fs-st-border-soft);\n}\n\n.fs-snapshot-row:last-child {\n border-bottom: none;\n}\n\n.fs-snapshot-key {\n padding: 4px 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 10px;\n color: var(--fs-st-key-text);\n vertical-align: top;\n}\n\n.fs-snapshot-value {\n padding: 4px 6px;\n vertical-align: top;\n}\n\n.fs-snapshot-empty {\n font-size: 12px;\n color: var(--fs-st-muted-text);\n font-style: italic;\n}\n\n.fs-snapshot-muted {\n color: var(--fs-st-muted-strong);\n font-style: italic;\n}\n\n.fs-snapshot-text {\n word-break: break-all;\n}\n\n.fs-snapshot-array {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n}\n\n.fs-snapshot-chip {\n background: var(--fs-st-chip-bg);\n color: var(--fs-st-chip-text);\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 999px;\n}\n\n.fs-snapshot-object {\n max-height: 128px;\n overflow: auto;\n border-radius: 4px;\n background: var(--fs-st-object-bg);\n padding: 4px;\n font-size: 10px;\n}\n\n.fs-snapshot-badge {\n display: inline-flex;\n align-items: center;\n border-radius: 3px;\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.fs-snapshot-badge-true {\n background: var(--fs-st-badge-true-bg);\n color: var(--fs-st-badge-true-text);\n}\n\n.fs-snapshot-badge-false {\n background: transparent;\n color: var(--fs-st-badge-false-text);\n border: 1px solid var(--fs-st-badge-false-border);\n}\n`\n\nconst DEVTOOLS_CSS = `:root {\n /* light mode – Tailwind neutral-ish */\n --fs-dev-bg-surface: rgba(250, 250, 250, 0.95); /* neutral-50 */\n --fs-dev-bg-panel: rgba(250, 250, 250, 0.98); /* neutral-50 */\n --fs-dev-bg-header: #f5f5f5; /* neutral-100 */\n --fs-dev-bg-header-muted: #f5f5f5;\n --fs-dev-bg-toggle-count: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-close-hover: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-tag: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-status: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-snapshot: #f5f5f5; /* neutral-100 */\n\n --fs-dev-border-strong: #e5e5e5; /* neutral-200 */\n --fs-dev-border-subtle: #e5e5e5; /* neutral-200 */\n --fs-dev-border-soft: #d4d4d4; /* neutral-300 */\n --fs-dev-border-snapshot: #d4d4d4; /* neutral-300 */\n\n --fs-dev-text-primary: #171717; /* neutral-900 */\n --fs-dev-text-muted: #737373; /* neutral-500 */\n --fs-dev-text-soft: #525252; /* neutral-600 */\n --fs-dev-text-error: #b91c1c;\n\n --fs-dev-badge-success-bg: rgba(22, 163, 74, 0.1);\n --fs-dev-badge-success-text: #166534;\n --fs-dev-badge-warn-bg: rgba(217, 119, 6, 0.12);\n --fs-dev-badge-warn-text: #92400e;\n\n --fs-dev-shadow-toggle: 0 1px 3px rgba(0, 0, 0, 0.08);\n --fs-dev-shadow-toggle-hover: 0 2px 6px rgba(0, 0, 0, 0.12);\n --fs-dev-shadow-panel: 0 8px 24px rgba(0, 0, 0, 0.12);\n}\n\nhtml.dark .fs-color-scheme-root:root,\nhtml.dark {\n /* dark mode – neutral-900/950 style */\n --fs-dev-bg-surface: rgba(23, 23, 23, 0.95); /* neutral-900 */\n --fs-dev-bg-panel: rgba(23, 23, 23, 0.98); /* neutral-900 */\n --fs-dev-bg-header: #171717; /* neutral-900 */\n --fs-dev-bg-header-muted: #171717;\n --fs-dev-bg-toggle-count: #262626; /* neutral-800 */\n --fs-dev-bg-close-hover: #404040; /* neutral-700 */\n --fs-dev-bg-tag: #262626; /* neutral-800 */\n --fs-dev-bg-status: #171717; /* neutral-900 */\n --fs-dev-bg-snapshot: #0a0a0a; /* neutral-950ish */\n\n --fs-dev-border-strong: #404040; /* neutral-700 */\n --fs-dev-border-subtle: #262626; /* neutral-800 */\n --fs-dev-border-soft: #262626; /* neutral-800 */\n --fs-dev-border-snapshot: #404040; /* neutral-700 */\n\n --fs-dev-text-primary: #fafafa; /* neutral-50 */\n --fs-dev-text-muted: #a3a3a3; /* neutral-400 */\n --fs-dev-text-soft: #d4d4d4; /* neutral-300 */\n --fs-dev-text-error: #fecaca;\n\n --fs-dev-badge-success-bg: rgba(22, 163, 74, 0.25);\n --fs-dev-badge-success-text: #bbf7d0;\n --fs-dev-badge-warn-bg: rgba(217, 119, 6, 0.3);\n --fs-dev-badge-warn-text: #fed7aa;\n\n --fs-dev-shadow-toggle: 0 1px 3px rgba(0, 0, 0, 0.6);\n --fs-dev-shadow-toggle-hover: 0 2px 6px rgba(0, 0, 0, 0.7);\n --fs-dev-shadow-panel: 0 20px 40px rgba(0, 0, 0, 0.8);\n}\n.fs-devtools-toggle {\n position: fixed;\n bottom: 16px;\n right: 16px;\n z-index: 9999;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n border-radius: 999px;\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-surface);\n padding: 4px 8px;\n font-size: 12px;\n color: var(--fs-dev-text-soft);\n box-shadow: var(--fs-dev-shadow-toggle);\n cursor: pointer;\n}\n\n.fs-devtools-toggle:hover {\n box-shadow: var(--fs-dev-shadow-toggle-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-toggle-count {\n margin-left: 4px;\n border-radius: 999px;\n background: var(--fs-dev-bg-toggle-count);\n padding: 0 4px;\n font-size: 10px;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-panel {\n position: fixed;\n bottom: 56px;\n overflow: hidden;\n right: 16px;\n z-index: 9999;\n width: 420px;\n max-height: 60vh;\n border-radius: 12px;\n color: var(--fs-dev-text-primary);\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-panel);\n box-shadow: var(--fs-dev-shadow-panel);\n display: flex;\n flex-direction: column;\n font-size: 14px;\n}\n\n.fs-devtools-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-subtle);\n background: var(--fs-dev-bg-header);\n}\n\n.fs-devtools-header-left {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.fs-devtools-header-text {\n display: flex;\n flex-direction: column;\n}\n\n.fs-devtools-header-title {\n font-size: 12px;\n font-weight: 600;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-header-subtitle {\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-header-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.fs-devtools-clear {\n border-radius: 999px;\n padding: 2px 8px;\n font-size: 11px;\n border: 1px solid var(--fs-dev-border-subtle);\n background: transparent;\n color: var(--fs-dev-text-muted);\n cursor: pointer;\n}\n\n.fs-devtools-clear:hover:enabled {\n background: var(--fs-dev-bg-close-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-clear:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.fs-devtools-close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 999px;\n padding: 4px;\n background: transparent;\n border: none;\n cursor: pointer;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-close:hover {\n background: var(--fs-dev-bg-close-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-filters {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-subtle);\n}\n\n.fs-devtools-select {\n flex: 1;\n border-radius: 4px;\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-panel);\n padding: 2px 6px;\n font-size: 12px;\n}\n\n.fs-devtools-filter-label {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-body {\n flex: 1;\n overflow: auto;\n}\n\n.fs-devtools-empty {\n padding: 10px 12px;\n font-size: 12px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-list {\n list-style: none;\n margin: 0;\n padding: 0;\n border-top: 1px solid var(--fs-dev-border-soft);\n}\n\n.fs-devtools-item {\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-soft);\n}\n\n.fs-devtools-item-toggle {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n background: transparent;\n border: none;\n padding: 0;\n cursor: pointer;\n text-align: left;\n}\n\n.fs-devtools-item-main {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n min-width: 0;\n}\n\n.fs-devtools-item-text {\n min-width: 0;\n}\n\n.fs-devtools-item-tags {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.fs-devtools-tag {\n display: inline-flex;\n align-items: center;\n border-radius: 999px;\n background: var(--fs-dev-bg-tag);\n padding: 2px 6px;\n font-size: 10px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n \"Courier New\", monospace;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-id {\n font-size: 10px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-meta {\n margin-top: 2px;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-delete {\n border: none;\n background: transparent;\n padding: 0;\n font-size: 11px;\n color: var(--fs-dev-text-soft);\n cursor: pointer;\n}\n\n.fs-devtools-delete:hover {\n text-decoration: underline;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-badge {\n border-radius: 999px;\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.fs-devtools-badge-success {\n background: var(--fs-dev-badge-success-bg);\n color: var(--fs-dev-badge-success-text);\n}\n\n.fs-devtools-badge-warn {\n background: var(--fs-dev-badge-warn-bg);\n color: var(--fs-dev-badge-warn-text);\n}\n\n.fs-devtools-status {\n border-radius: 999px;\n background: var(--fs-dev-bg-status);\n padding: 2px 6px;\n font-size: 10px;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-error {\n margin-left: 8px;\n max-width: 40%;\n font-size: 11px;\n color: var(--fs-dev-text-error);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.fs-devtools-snapshot {\n margin-top: 6px;\n border-radius: 6px;\n background: var(--fs-dev-bg-snapshot);\n font-size: 12px;\n}\n`\n\nexport function ensureFormSnapshotsStyles() {\n\tif (typeof document === \"undefined\") return\n\tif (document.getElementById(STYLE_ID)) return\n\n\tconst style = document.createElement(\"style\")\n\tstyle.id = STYLE_ID\n\tstyle.type = \"text/css\"\n\tstyle.appendChild(\n\t\tdocument.createTextNode(`${SNAPSHOT_TABLE_CSS}\\n\\n${DEVTOOLS_CSS}`),\n\t)\n\t;(document.head || document.documentElement).appendChild(style)\n}\n\n","import { useFormSnapshots, type FormSnapshotsOptions } from \"./hooks/use-form-snapshots\"\n\ntype WithoutSnapshotOverrides = Omit<\n\tFormSnapshotsOptions,\n\t\"getValues\" | \"applyValues\"\n>\n\nexport interface RHFSnapshotsOptions extends WithoutSnapshotOverrides {}\n\n/**\n * Minimal subset of a React Hook Form instance that this library needs.\n * Kept structural so consumers are not forced to install `react-hook-form`.\n */\nexport interface RHFFormLike<TFieldValues extends Record<string, unknown>> {\n\tgetValues: () => TFieldValues\n\treset: (values: TFieldValues) => void\n}\n\nexport function useRHFFormSnapshots<TFieldValues extends Record<string, unknown>>(\n\tformName: string,\n\tform: RHFFormLike<TFieldValues>,\n\toptions?: RHFSnapshotsOptions,\n) {\n\treturn useFormSnapshots(formName, {\n\t\t...options,\n\t\tgetValues: () => form.getValues() as Record<string, unknown>,\n\t\tapplyValues: (snap) => form.reset(snap as TFieldValues),\n\t})\n}\n\nexport interface ObjectStateSnapshotsOptions\n\textends WithoutSnapshotOverrides {}\n\nexport function useObjectFormSnapshots<TState extends Record<string, unknown>>(\n\tformName: string,\n\tstate: TState,\n\tsetState: (next: TState) => void,\n\toptions?: ObjectStateSnapshotsOptions,\n) {\n\treturn useFormSnapshots(formName, {\n\t\t...options,\n\t\tgetValues: () => state as Record<string, unknown>,\n\t\tapplyValues: (snap) => setState(snap as TState),\n\t})\n}\n\n"],"mappings":"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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4I3B,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["import_react","import_react","import_dexie_react_hooks","import_react","import_jsx_runtime","import_jsx_runtime"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../form-snapshots/hooks/use-form-snapshots.ts","../form-snapshots/client.ts","../form-snapshots/local-db/db.ts","../form-snapshots/dexie-storage.ts","../form-snapshots/dom-snapshot.ts","../form-snapshots/context.tsx","../form-snapshots/hooks/use-form-snapshots-list.ts","../form-snapshots/devtools.tsx","../form-snapshots/snapshot-table.tsx","../form-snapshots/styles.ts","../form-snapshots/adapters.ts"],"sourcesContent":["import {\n\tuseCallback,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n\ttype SyntheticEvent,\n} from \"react\"\nimport { FormSnapshotsClient } from \"../client\"\nimport { DexieFormSnapshotsStorage } from \"../dexie-storage\"\nimport { type FormSnapshot, type FormSnapshotsStorage } from \"../types\"\nimport {\n\trestoreSnapshotToForm,\n\tsnapshotFromDOMForm,\n} from \"../dom-snapshot\"\nimport { useFormSnapshotsConfig } from \"../context\"\n\nexport interface FormSnapshotsOptions {\n\t/** How long in ms to retain history entries. Default: 24 h */\n\tsnapshotsLimit?: number\n\n\t/**\n\t * Field paths (path/prefix) to exclude while saving snapshots.\n\t * E.g. \"password\", \"patient.address\".\n\t * Merged with the global excludeFields config.\n\t */\n\texcludeFields?: string[]\n\n\t/**\n\t * Override how the current form state is read.\n\t *\n\t * Use this for controlled forms or nested / structured data models\n\t * (e.g. HL7 FHIR Address, HumanName, ContactPoint[]) where values live\n\t * in React state rather than plain DOM elements.\n\t */\n\tgetValues?: () => FormSnapshot\n\n\t/**\n\t * Override how a saved snapshot is applied back to the form.\n\t *\n\t * Receives the full snapshot that was previously returned by `getValues`.\n\t */\n\tapplyValues?: (snapshot: FormSnapshot) => void\n\n\t/**\n\t * When true, the active session is deleted from storage and the form\n\t * is cleared immediately after submit so that submitted data is not\n\t * kept in history. Can be overridden globally via provider config.\n\t */\n\tdiscardOnSubmit?: boolean\n}\n\nfunction applyExcludeFields(\n\tsnapshot: FormSnapshot,\n\texcludeFields?: string[],\n): FormSnapshot {\n\tif (!excludeFields || excludeFields.length === 0) return snapshot\n\n\tfunction pruneByPrefix(\n\t\tobj: FormSnapshot,\n\t\tpaths: string[],\n\t\tparentPath = \"\",\n\t): FormSnapshot {\n\t\tconst result: FormSnapshot = {}\n\n\t\tfor (const [key, value] of Object.entries(obj)) {\n\t\t\tconst path = parentPath ? `${parentPath}.${key}` : key\n\n\t\t\tconst isExcluded = paths.some(\n\t\t\t\t(p) => path === p || path.startsWith(`${p}.`),\n\t\t\t)\n\t\t\tif (isExcluded) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (value && typeof value === \"object\" && !Array.isArray(value)) {\n\t\t\t\tresult[key] = pruneByPrefix(\n\t\t\t\t\tvalue as FormSnapshot,\n\t\t\t\t\tpaths,\n\t\t\t\t\tpath,\n\t\t\t\t)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// For now, arrays are treated as atomic: you can exclude the whole\n\t\t\t// array field with its path, but not individual items.\n\t\t\tresult[key] = value\n\t\t}\n\n\t\treturn result\n\t}\n\n\treturn pruneByPrefix(snapshot, excludeFields)\n}\n\nexport function useFormSnapshots(\n\tformName: string,\n\toptions?: FormSnapshotsOptions,\n) {\n\tconst { snapshotsLimit, getValues, applyValues, excludeFields, discardOnSubmit } =\n\t\toptions ?? {}\n\n\tconst formRef = useRef<HTMLFormElement>(null)\n\tconst sessionIdRef = useRef<number | null>(null)\n\tconst lastSubmittedSessionIdRef = useRef<number | null>(null)\n\tconst clientRef = useRef<FormSnapshotsClient | null>(null)\n\tconst [isSubmitted, setIsSubmitted] = useState(false)\n\tconst storageRef = useRef<FormSnapshotsStorage | null>(null)\n\n\tconst {\n\t\tdefaultSnapshotsLimit,\n\t\texcludeFields: globalExcludeFields,\n\t\tdiscardOnSubmit: globalDiscardOnSubmit,\n\t} = useFormSnapshotsConfig()\n\n\tconst effectiveSnapshotsLimit =\n\t\tsnapshotsLimit ?? defaultSnapshotsLimit ?? 24 * 60 * 60 * 1000\n\n\tconst effectiveDiscardOnSubmit =\n\t\ttypeof discardOnSubmit === \"boolean\"\n\t\t\t? discardOnSubmit\n\t\t\t: globalDiscardOnSubmit ?? false\n\n\tconst mergedExcludeFields =\n\t\tglobalExcludeFields || excludeFields\n\t\t\t? Array.from(\n\t\t\t\t\tnew Set([...(globalExcludeFields ?? []), ...(excludeFields ?? [])]),\n\t\t\t\t)\n\t\t\t: undefined\n\n\t// Keep latest callbacks in refs so they never invalidate the init effect\n\tconst getValuesRef = useRef(getValues)\n\tconst applyValuesRef = useRef(applyValues)\n\tuseEffect(() => { getValuesRef.current = getValues }, [getValues])\n\tuseEffect(() => { applyValuesRef.current = applyValues }, [applyValues])\n\n\t// ── Initialise: create client, prune, resume or open session ───────────────\n\tuseEffect(() => {\n\t\tlet cancelled = false\n\n\t\tasync function init() {\n\t\t\tif (!storageRef.current) {\n\t\t\t\tstorageRef.current = new DexieFormSnapshotsStorage()\n\t\t\t}\n\n\t\t\tconst client = new FormSnapshotsClient({\n\t\t\t\tstorage: storageRef.current!,\n\t\t\t\tformName,\n\t\t\t\tsnapshotsLimit: effectiveSnapshotsLimit,\n\t\t\t})\n\n\t\t\tclientRef.current = client\n\n\t\t\tconst session = await client.initSession()\n\t\t\tif (cancelled) return\n\n\t\t\tsessionIdRef.current = session.id\n\n\t\t\t// Restore saved values (after mount) via applyValues callback when\n\t\t\t// provided, or fall back to DOM-based restoration for flat forms.\n\t\t\tif (session.data && session.data !== \"{}\") {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tif (cancelled) return\n\t\t\t\t\tconst snapshot = JSON.parse(session.data) as FormSnapshot\n\t\t\t\t\tif (applyValuesRef.current) {\n\t\t\t\t\t\tapplyValuesRef.current(snapshot)\n\t\t\t\t\t} else if (formRef.current) {\n\t\t\t\t\t\trestoreSnapshotToForm(formRef.current, snapshot)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tinit()\n\n\t\treturn () => {\n\t\t\tcancelled = true\n\t\t}\n\t}, [formName, effectiveSnapshotsLimit])\n\n\t// ── Snapshot current form values into the active session ──────────────────\n\tconst handleBlur = useCallback(() => {\n\t\tconst sessionId = sessionIdRef.current\n\t\tif (sessionId === null) return\n\n\t\tlet snapshot: FormSnapshot\n\n\t\tif (getValuesRef.current) {\n\t\t\t// Controlled / structured form — caller owns the state.\n\t\t\t// Supports arbitrarily nested values: HL7 Address, HumanName,\n\t\t\t// ContactPoint[], CodeableConcept, etc.\n\t\t\tsnapshot = getValuesRef.current()\n\t\t} else {\n\t\t\t// Fallback: enumerate flat DOM form elements (strings / booleans only).\n\t\t\tconst form = formRef.current\n\t\t\tif (!form) return\n\t\t\tsnapshot = snapshotFromDOMForm(form)\n\t\t}\n\n\t\tsnapshot = applyExcludeFields(snapshot, mergedExcludeFields)\n\n\t\tif (!clientRef.current) return\n\n\t\tclientRef.current.saveSnapshot(sessionId, snapshot)\n\t}, []) // getValues is read via ref — no dep needed\n\n\t// ── Clear current form values after submit when requested ─────────────────\n\tconst clearFormState = useCallback(() => {\n\t\tif (applyValuesRef.current) {\n\t\t\t// For controlled / structured forms, an empty snapshot is passed\n\t\t\t// back to the caller so they can reset their own state.\n\t\t\tapplyValuesRef.current({} as FormSnapshot)\n\t\t} else if (formRef.current) {\n\t\t\t// For plain DOM forms, reset back to the initial values.\n\t\t\tformRef.current.reset()\n\t\t}\n\t}, [])\n\n\t// ── Wrap the form's onSubmit to close the session ─────────────────────────\n\tconst wrapSubmit = useCallback(\n\t\t(\n\t\t\tuserSubmit?: (e: SyntheticEvent<HTMLFormElement>) => void,\n\t\t) =>\n\t\t\t(e: SyntheticEvent<HTMLFormElement>) => {\n\t\t\t\te.preventDefault()\n\t\t\t\thandleBlur() // capture the final state before closing\n\n\t\t\t\tconst sessionId = sessionIdRef.current\n\t\t\t\tconst client = clientRef.current\n\t\t\t\tif (sessionId !== null && client) {\n\t\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\t\t// Discard the entire session so submitted data is not kept.\n\t\t\t\t\t\tclient.deleteSession(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = null\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclient.markSubmitted(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = sessionId\n\t\t\t\t\t}\n\t\t\t\t\t// Closed session should no longer receive snapshots\n\t\t\t\t\tsessionIdRef.current = null\n\t\t\t\t}\n\n\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\tclearFormState()\n\t\t\t\t}\n\n\t\t\t\tsetIsSubmitted(true)\n\t\t\t\tuserSubmit?.(e)\n\t\t\t},\n\t\t[handleBlur, clearFormState, effectiveDiscardOnSubmit],\n\t)\n\n\tconst markSubmitResult = useCallback(\n\t\tasync (params: {\n\t\t\tstatusCode?: number | null\n\t\t\terrorMessage?: string | null\n\t\t}) => {\n\t\t\tconst client = clientRef.current\n\t\t\tconst sessionId = lastSubmittedSessionIdRef.current\n\t\t\tif (!client || sessionId == null) return\n\n\t\t\tawait client.setSubmissionResult({\n\t\t\t\tsessionId,\n\t\t\t\tstatusCode: params.statusCode,\n\t\t\t\terrorMessage: params.errorMessage,\n\t\t\t})\n\t\t},\n\t\t[],\n\t)\n\n\tconst wrapSubmitAsync = useCallback(\n\t\t(\n\t\t\t\tuserSubmit?: (\n\t\t\t\t\te: SyntheticEvent<HTMLFormElement>,\n\t\t\t\t) => Promise<\n\t\t\t\t\t| void\n\t\t\t\t\t| {\n\t\t\t\t\t\t\tstatusCode?: number | null\n\t\t\t\t\t\t\terrorMessage?: string | null\n\t\t\t\t\t }\n\t\t\t\t>,\n\t\t\t) =>\n\t\t\tasync (e: SyntheticEvent<HTMLFormElement>) => {\n\t\t\t\te.preventDefault()\n\t\t\t\thandleBlur()\n\n\t\t\t\tconst sessionId = sessionIdRef.current\n\t\t\t\tconst client = clientRef.current\n\t\t\t\tif (sessionId !== null && client) {\n\t\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\t\tawait client.deleteSession(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = null\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait client.markSubmitted(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = sessionId\n\t\t\t\t\t}\n\t\t\t\t\tsessionIdRef.current = null\n\t\t\t\t}\n\n\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\tclearFormState()\n\t\t\t\t}\n\n\t\t\t\tsetIsSubmitted(true)\n\n\t\t\t\tif (!userSubmit) return\n\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await userSubmit(e)\n\t\t\t\t\tif (result && typeof result === \"object\") {\n\t\t\t\t\t\tawait markSubmitResult({\n\t\t\t\t\t\t\tstatusCode: \"statusCode\" in result ? result.statusCode : null,\n\t\t\t\t\t\t\terrorMessage:\n\t\t\t\t\t\t\t\t\"errorMessage\" in result ? result.errorMessage : null,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t// In case of error we leave handling to the caller;\n\t\t\t// logging here is enough. Callers can still invoke\n\t\t\t// markSubmitResult manually if they want.\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.error(\"wrapSubmitAsync userSubmit error:\", error)\n\t\t\t\t}\n\t\t\t},\n\t\t[handleBlur, markSubmitResult, clearFormState, effectiveDiscardOnSubmit],\n\t)\n\n\t// ── Restore the latest saved snapshot into the DOM form ───────────────────\n\tconst restoreLatest = useCallback(async () => {\n\t\tif (!clientRef.current) return\n\n\t\tconst snapshot = await clientRef.current.getLatestSnapshot()\n\t\tif (!snapshot) return\n\n\t\tif (applyValuesRef.current) {\n\t\t\t// Structured form — route the full nested snapshot back to state.\n\t\t\tapplyValuesRef.current(snapshot)\n\t\t} else {\n\t\t\tif (!formRef.current) return\n\t\t\trestoreSnapshotToForm(formRef.current, snapshot)\n\t\t}\n\n\t\t// Open a new session pre-seeded with the restored data so further edits\n\t\t// are tracked against the fresh session\n\t\tconst newSession = await clientRef.current.openNewSessionFromSnapshot(\n\t\t\tsnapshot,\n\t\t)\n\t\tsessionIdRef.current = newSession.id\n\n\t\tsetIsSubmitted(false)\n\t}, []) // applyValues is read via ref — no dep needed\n\n\treturn {\n\t\tformRef,\n\t\thandleBlur,\n\t\twrapSubmit,\n\t\twrapSubmitAsync,\n\t\trestoreLatest,\n\t\tisSubmitted,\n\t\tmarkSubmitResult,\n\t}\n}\n","import type {\n\tFormSnapshotsStorage,\n\tFormSessionBase,\n\tFormSnapshot,\n} from \"./types\"\n\nexport class FormSnapshotsClient {\n\tprivate readonly storage: FormSnapshotsStorage\n\tprivate readonly formName: string\n\tprivate readonly snapshotsLimit: number\n\n\tconstructor(params: {\n\t\tstorage: FormSnapshotsStorage\n\t\tformName: string\n\t\tsnapshotsLimit: number\n\t}) {\n\t\tthis.storage = params.storage\n\t\tthis.formName = params.formName\n\t\tthis.snapshotsLimit = params.snapshotsLimit\n\t}\n\n\tasync initSession(): Promise<FormSessionBase> {\n\t\tconst now = Date.now()\n\t\tconst cutoff = now - this.snapshotsLimit\n\n\t\tawait this.storage.pruneOlderThan(cutoff)\n\n\t\tconst existing = await this.storage.findActiveSession(this.formName)\n\t\tif (existing) return existing\n\n\t\treturn this.storage.createSession(this.formName, {})\n\t}\n\n\tasync saveSnapshot(\n\t\tsessionId: number,\n\t\tsnapshot: FormSnapshot,\n\t): Promise<void> {\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tdata: JSON.stringify(snapshot),\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync markSubmitted(sessionId: number): Promise<void> {\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tsubmitted: true,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync deleteSession(sessionId: number): Promise<void> {\n\t\tif (typeof this.storage.deleteSession === \"function\") {\n\t\t\tawait this.storage.deleteSession(sessionId)\n\t\t}\n\t}\n\n\tasync setSubmissionResult(params: {\n\t\tsessionId: number\n\t\tstatusCode?: number | null\n\t\terrorMessage?: string | null\n\t}): Promise<void> {\n\t\tconst { sessionId, statusCode, errorMessage } = params\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tstatusCode: statusCode ?? null,\n\t\t\terrorMessage: errorMessage ?? null,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync getLatestSnapshot(): Promise<FormSnapshot | null> {\n\t\tconst latest = await this.storage.getLatestSession(this.formName)\n\t\tif (!latest) return null\n\t\treturn JSON.parse(latest.data) as FormSnapshot\n\t}\n\n\tasync openNewSessionFromSnapshot(\n\t\tsnapshot: FormSnapshot,\n\t): Promise<FormSessionBase> {\n\t\treturn this.storage.createSession(this.formName, snapshot)\n\t}\n}\n\n","import { Dexie, type EntityTable } from \"dexie\"\n\nexport interface FormSession {\n\tid: number\n\tformName: string\n\t/** JSON-serialised record of field name → value */\n\tdata: string\n\tcreatedAt: number\n\tupdatedAt: number\n\t/** true = the form was submitted; session is closed / read-only */\n\tsubmitted: boolean\n\t/** Optional HTTP-like status code for the last submit attempt */\n\tstatusCode?: number | null\n\t/** Optional error message when the last submit attempt failed */\n\terrorMessage?: string | null\n}\n\nconst db = new Dexie(\"FormSnapshotsDatabase\") as Dexie & {\n\tformSessions: EntityTable<FormSession, \"id\">\n}\n\ndb.version(1).stores({\n\tformSessions: \"++id, formName, createdAt, submitted, [formName+createdAt]\",\n})\n\ndb.version(2).stores({\n\tformSessions:\n\t\t\"++id, formName, createdAt, updatedAt, submitted, [formName+createdAt]\",\n})\n\nexport { db }\n\n","import { db, type FormSession } from \"./local-db/db\"\nimport {\n\ttype FormSnapshotsStorage,\n\ttype FormSessionBase,\n\ttype FormSnapshot,\n} from \"./types\"\n\nfunction mapToBase(session: FormSession): FormSessionBase {\n\treturn {\n\t\tid: session.id,\n\t\tformName: session.formName,\n\t\tdata: session.data,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t\tsubmitted: session.submitted,\n\t\tstatusCode: session.statusCode ?? null,\n\t\terrorMessage: session.errorMessage ?? null,\n\t}\n}\n\n\texport class DexieFormSnapshotsStorage implements FormSnapshotsStorage {\n\tasync findActiveSession(formName: string): Promise<FormSessionBase | null> {\n\t\tconst existing = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.filter((s) => !s.submitted)\n\t\t\t.last()\n\n\t\treturn existing ? mapToBase(existing) : null\n\t}\n\n\tasync createSession(\n\t\tformName: string,\n\t\tsnapshot: FormSnapshot,\n\t): Promise<FormSessionBase> {\n\t\tconst now = Date.now()\n\t\tconst id = await db.formSessions.add({\n\t\t\tformName,\n\t\t\tdata: JSON.stringify(snapshot),\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tsubmitted: false,\n\t\t\tstatusCode: null,\n\t\t\terrorMessage: null,\n\t\t})\n\n\t\tconst created = await db.formSessions.get(id)\n\t\tif (!created) {\n\t\t\tthrow new Error(\"Failed to create form session\")\n\t\t}\n\n\t\treturn mapToBase(created)\n\t}\n\n\tasync updateSession(\n\t\tid: number,\n\t\tpatch: Partial<\n\t\t\tPick<\n\t\t\t\tFormSessionBase,\n\t\t\t\t\"data\" | \"updatedAt\" | \"submitted\" | \"statusCode\" | \"errorMessage\"\n\t\t\t>\n\t\t>,\n\t): Promise<void> {\n\t\tawait db.formSessions.update(id, patch)\n\t}\n\n\tasync getLatestSession(formName: string): Promise<FormSessionBase | null> {\n\t\tconst sessions = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.sortBy(\"updatedAt\")\n\n\t\tconst latest = sessions[sessions.length - 1]\n\t\treturn latest ? mapToBase(latest) : null\n\t}\n\n\tasync listSessions(formName: string): Promise<FormSessionBase[]> {\n\t\tconst sessions = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.sortBy(\"updatedAt\")\n\n\t\treturn sessions.map(mapToBase)\n\t}\n\n\tasync pruneOlderThan(cutoffMs: number): Promise<void> {\n\t\tawait db.formSessions.where(\"createdAt\").below(cutoffMs).delete()\n\t}\n\n\tasync deleteSession(id: number): Promise<void> {\n\t\tawait db.formSessions.delete(id)\n\t}\n}\n\n","type AnyFormEl = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement\n\nfunction toStringValue(val: unknown): string {\n\tif (val == null) return \"\"\n\tif (typeof val === \"object\") return JSON.stringify(val)\n\treturn String(val as string | number | boolean | bigint)\n}\n\nfunction applyValueToInput(input: AnyFormEl, val: unknown): void {\n\tif (input instanceof HTMLInputElement) {\n\t\tif (input.type === \"checkbox\") {\n\t\t\tinput.checked = Boolean(val)\n\t\t\tinput.dispatchEvent(new Event(\"change\", { bubbles: true }))\n\t\t\treturn\n\t\t}\n\t\tif (input.type === \"radio\") {\n\t\t\tinput.checked = input.value === val\n\t\t\tif (input.checked) input.dispatchEvent(new Event(\"change\", { bubbles: true }))\n\t\t\treturn\n\t\t}\n\t}\n\tinput.value = toStringValue(val)\n\tinput.dispatchEvent(new Event(\"input\", { bubbles: true }))\n}\n\n/** Populate DOM form elements from a plain snapshot object */\nexport function restoreSnapshotToForm(\n\tform: HTMLFormElement,\n\tsnapshot: Record<string, unknown>,\n) {\n\tfor (const el of Array.from(form.elements)) {\n\t\tconst input = el as AnyFormEl\n\t\tif (!input.name || !(input.name in snapshot)) continue\n\t\tapplyValueToInput(input, snapshot[input.name])\n\t}\n}\n\n/** Read all named fields from a plain DOM form into a flat snapshot object */\nexport function snapshotFromDOMForm(\n\tform: HTMLFormElement,\n): Record<string, unknown> {\n\tconst snapshot: Record<string, unknown> = {}\n\tfor (const el of Array.from(form.elements)) {\n\t\tconst input = el as AnyFormEl\n\t\tif (!input.name) continue\n\t\treadInputIntoSnapshot(input, snapshot)\n\t}\n\treturn snapshot\n}\n\nexport function readInputIntoSnapshot(\n\tinput: AnyFormEl,\n\tsnapshot: Record<string, unknown>,\n) {\n\tif (input instanceof HTMLInputElement) {\n\t\tif (input.type === \"checkbox\") {\n\t\t\tsnapshot[input.name] = input.checked\n\t\t} else if (input.type === \"radio\") {\n\t\t\tif (input.checked) snapshot[input.name] = input.value\n\t\t} else if (input.type !== \"file\") {\n\t\t\tsnapshot[input.name] = input.value\n\t\t}\n\t} else {\n\t\tsnapshot[input.name] = input.value\n\t}\n}\n\n","import type { ReactNode } from \"react\"\nimport { createContext, useContext } from \"react\"\n\nexport interface FormSnapshotsGlobalConfig {\n\tdefaultSnapshotsLimit?: number\n\t/**\n\t * Field paths (path/prefix) to exclude while saving snapshots.\n\t * E.g. [\"password\", \"patient.address\"]\n\t */\n\texcludeFields?: string[]\n\n\t/**\n\t * When true, submitted sessions are discarded and the form is cleared\n\t * immediately after submit instead of keeping the data in history.\n\t * Can be overridden per form via hook options.\n\t */\n\tdiscardOnSubmit?: boolean\n}\n\nconst FormSnapshotsConfigContext = createContext<FormSnapshotsGlobalConfig | undefined>(\n\tundefined,\n)\n\nexport interface FormSnapshotsProviderProps {\n\tvalue?: FormSnapshotsGlobalConfig\n\tchildren: ReactNode\n}\n\nexport function FormSnapshotsProvider({\n\tvalue,\n\tchildren,\n}: Readonly<FormSnapshotsProviderProps>) {\n\treturn (\n\t\t<FormSnapshotsConfigContext.Provider value={value ?? {}}>\n\t\t\t{children}\n\t\t</FormSnapshotsConfigContext.Provider>\n\t)\n}\n\nexport function useFormSnapshotsConfig(): FormSnapshotsGlobalConfig {\n\treturn useContext(FormSnapshotsConfigContext) ?? {}\n}\n\n","import { useLiveQuery } from \"dexie-react-hooks\"\nimport { db, type FormSession } from \"../local-db/db\"\n\nexport interface useFormSnapshotsListOptions {\n\t/**\n\t * Whether to return only submitted (completed) sessions.\n\t * Default: false (both submitted and in-progress).\n\t */\n\tonlySubmitted?: boolean\n\n\t/**\n\t * Whether to return only successful (2xx) submission results.\n\t * Default: false (all statusCode values).\n\t */\n\tonlySuccessful?: boolean\n\n\t/**\n\t * Maximum age in ms. E.g. last 24 hours = 24 * 60 * 60 * 1000.\n\t * Default: unlimited.\n\t */\n\tmaxAgeMs?: number\n\n\t/**\n\t * Maximum number of records to return.\n\t * Default: unlimited.\n\t */\n\tlimit?: number\n}\n\nexport interface FormHistoryListItem {\n\tid: number\n\tformName: string\n\tcreatedAt: number\n\tupdatedAt: number\n\tsubmitted: boolean\n\tstatusCode: number | null\n\terrorMessage: string | null\n\t/**\n\t * Age in ms relative to \\\"now\\\".\n\t */\n\tageMs: number\n}\n\nexport function useFormSnapshotsList(\n\tformName: string,\n\toptions?: useFormSnapshotsListOptions,\n) {\n\tconst {\n\t\tonlySubmitted = false,\n\t\tonlySuccessful = false,\n\t\tmaxAgeMs,\n\t\tlimit,\n\t} = options ?? {}\n\n\tconst items = useLiveQuery<FormHistoryListItem[] | undefined>(async () => {\n\t\tconst now = Date.now()\n\t\tconst cutoff = maxAgeMs ? now - maxAgeMs : undefined\n\n\t\tlet query = db.formSessions.where(\"formName\").equals(formName)\n\n\t\t// Other filters are not supported directly by Dexie, so we refine via JS filter.\n\t\tlet sessions: FormSession[] = await query.toArray()\n\n\t\tif (onlySubmitted) {\n\t\t\tsessions = sessions.filter((s) => s.submitted)\n\t\t}\n\n\t\tif (onlySuccessful) {\n\t\t\tsessions = sessions.filter((s) => {\n\t\t\t\tif (s.statusCode == null) return false\n\t\t\t\treturn s.statusCode >= 200 && s.statusCode < 300\n\t\t\t})\n\t\t}\n\n\t\tif (cutoff != null) {\n\t\t\tsessions = sessions.filter((s) => s.createdAt >= cutoff)\n\t\t}\n\n\t\tsessions.sort((a, b) => b.updatedAt - a.updatedAt)\n\n\t\tif (typeof limit === \"number\") {\n\t\t\tsessions = sessions.slice(0, limit)\n\t\t}\n\n\t\treturn sessions.map((s) => ({\n\t\t\tid: s.id,\n\t\t\tformName: s.formName,\n\t\t\tcreatedAt: s.createdAt,\n\t\t\tupdatedAt: s.updatedAt,\n\t\t\tsubmitted: s.submitted,\n\t\t\tstatusCode: s.statusCode ?? null,\n\t\t\terrorMessage: s.errorMessage ?? null,\n\t\t\tageMs: now - s.updatedAt,\n\t\t}))\n\t}, [formName, onlySubmitted, onlySuccessful, maxAgeMs, limit])\n\n\tconst isLoading = items === undefined\n\n\treturn { items: items ?? [], isLoading }\n}\n\n","import { useMemo, useState } from \"react\"\nimport { useLiveQuery } from \"dexie-react-hooks\"\nimport { db, type FormSession } from \"./local-db/db\"\nimport { FormSnapshotTable } from \"./snapshot-table\"\nimport { ensureFormSnapshotsStyles } from \"./styles\"\n\nfunction formatDateShort(ms: number) {\n\treturn new Intl.DateTimeFormat(\"en-GB\", {\n\t\tmonth: \"short\",\n\t\tday: \"2-digit\",\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t}).format(new Date(ms))\n}\n\nfunction formatAge(ms: number) {\n\tconst seconds = Math.floor(ms / 1000)\n\tconst minutes = Math.floor(seconds / 60)\n\tconst hours = Math.floor(minutes / 60)\n\tconst days = Math.floor(hours / 24)\n\n\tif (days > 0) return `${days}d ${hours % 24}h`\n\tif (hours > 0) return `${hours}h ${minutes % 60}m`\n\tif (minutes > 0) return `${minutes}m`\n\treturn `${seconds}s`\n}\n\nexport function FormSnapshotsDevtools() {\n\tensureFormSnapshotsStyles()\n\n\tconst isProd =\n\t\ttypeof import.meta !== \"undefined\" &&\n\t\t(import.meta as any).env?.PROD\n\n\tif (isProd) {\n\t\treturn null\n\t}\n\n\tconst [open, setOpen] = useState(false)\n\tconst [selectedForm, setSelectedForm] = useState<string>(\"all\")\n\tconst [onlySubmitted, setOnlySubmitted] = useState(false)\n\tconst [onlyErrors, setOnlyErrors] = useState(false)\n\tconst [expandedId, setExpandedId] = useState<number | null>(null)\n\tconst [isClearing, setIsClearing] = useState(false)\n\n\tconst sessions = useLiveQuery<FormSession[]>(() => {\n\t\treturn db.formSessions\n\t\t\t.toCollection()\n\t\t\t.sortBy(\"updatedAt\")\n\t\t\t.then((all) => all.reverse())\n\t}, [])\n\n\tconst now = Date.now()\n\n\tconst formNames = useMemo(() => {\n\t\tif (!sessions) return []\n\t\treturn Array.from(new Set(sessions.map((s) => s.formName))).sort()\n\t}, [sessions])\n\n\tconst filtered = useMemo(() => {\n\t\tif (!sessions) return []\n\n\t\treturn sessions.filter((s) => {\n\t\t\tif (selectedForm !== \"all\" && s.formName !== selectedForm) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (onlySubmitted && !s.submitted) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (onlyErrors && (s.statusCode == null || s.statusCode < 400)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}, [sessions, selectedForm, onlySubmitted, onlyErrors])\n\n\tconst handleDeleteSession = async (id: number) => {\n\t\ttry {\n\t\t\tawait db.formSessions.delete(id)\n\t\t} catch (error) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(\"Failed to delete form session\", error)\n\t\t}\n\t}\n\n\tconst handleClearAll = async () => {\n\t\tif (!sessions || sessions.length === 0) return\n\t\tsetIsClearing(true)\n\t\ttry {\n\t\t\tconst ids = sessions.map((s) => s.id)\n\t\t\tawait db.formSessions.bulkDelete(ids)\n\t\t} catch (error) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(\"Failed to clear form sessions\", error)\n\t\t} finally {\n\t\t\tsetIsClearing(false)\n\t\t}\n\t}\n\n\tconst total = sessions?.length ?? 0\n\n\tif (!sessions || sessions.length === 0) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={() => setOpen((v) => !v)}\n\t\t\t\t\tclassName=\"fs-devtools-toggle\"\n\t\t\t>\n\t\t\t\t\t<HistoryIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t<span>Form Snapshot Devtools</span>\n\t\t\t\t{total > 0 && (\n\t\t\t\t\t\t<span className=\"fs-devtools-toggle-count\">\n\t\t\t\t\t\t{total}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</button>\n\n\t\t\t{open && (\n\t\t\t\t<div className=\"fs-devtools-panel\">\n\t\t\t\t\t<div className=\"fs-devtools-header\">\n\t\t\t\t\t\t<div className=\"fs-devtools-header-left\">\n\t\t\t\t\t\t\t<HistoryIcon style={{ width: 14, height: 14 }} />\n\t\t\t\t\t\t\t<div className=\"fs-devtools-header-text\">\n\t\t\t\t\t\t\t\t<span className=\"fs-devtools-header-title\">\n\t\t\t\t\t\t\t\t\tForm Snapshot Devtools\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span className=\"fs-devtools-header-subtitle\">\n\t\t\t\t\t\t\t\t\t{filtered.length} / {total} session\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"fs-devtools-header-actions\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={handleClearAll}\n\t\t\t\t\t\t\t\tclassName=\"fs-devtools-clear\"\n\t\t\t\t\t\t\t\tdisabled={isClearing || !sessions || sessions.length === 0}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tClear all\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => setOpen(false)}\n\t\t\t\t\t\t\t\tclassName=\"fs-devtools-close\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<XIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"fs-devtools-filters\">\n\t\t\t\t\t\t<FilterIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tclassName=\"fs-devtools-select\"\n\t\t\t\t\t\t\tvalue={selectedForm}\n\t\t\t\t\t\t\tonChange={(e) => setSelectedForm(e.target.value)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"all\">All forms</option>\n\t\t\t\t\t\t\t{formNames.map((name) => (\n\t\t\t\t\t\t\t\t<option key={name} value={name}>\n\t\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t\t<label className=\"fs-devtools-filter-label\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={onlySubmitted}\n\t\t\t\t\t\t\t\tonChange={(e) => setOnlySubmitted(e.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span>submitted</span>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<label className=\"fs-devtools-filter-label\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={onlyErrors}\n\t\t\t\t\t\t\t\tonChange={(e) => setOnlyErrors(e.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span>errors</span>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"fs-devtools-body\">\n\t\t\t\t\t\t{filtered.length === 0 ? (\n\t\t\t\t\t\t\t<p className=\"fs-devtools-empty\">\n\t\t\t\t\t\t\t\tNo sessions match the selected filters.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<ul className=\"fs-devtools-list\">\n\t\t\t\t\t\t\t\t{filtered.map((s) => (\n\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\tkey={s.id}\n\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-item\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\tsetExpandedId((current) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tcurrent === s.id ? null : s.id,\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-item-toggle\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-main\">\n\t\t\t\t\t\t\t\t\t\t\t\t<ChevronDownIcon\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: 12,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: 12,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttransform:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\texpandedId === s.id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"rotate(0deg)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"rotate(-90deg)\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttransition: \"transform 120ms ease-out\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-tags\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-tag\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.formName}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-id\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t#{s.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-meta\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span>{formatDateShort(s.updatedAt)}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span>· {formatAge(now - s.updatedAt)} ago</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.submitted ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-badge fs-devtools-badge-success\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsubmitted\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-badge fs-devtools-badge-warn\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tin progress\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.statusCode != null && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-status\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstatus {s.statusCode}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-delete\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvoid handleDeleteSession(s.id)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tDelete\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{typeof s.errorMessage === \"string\" && s.errorMessage && (\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-error\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{s.errorMessage}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t\t\t{expandedId === s.id && (\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-snapshot\">\n\t\t\t\t\t\t\t\t\t\t\t\t<FormSnapshotTable\n\t\t\t\t\t\t\t\t\t\t\t\t\tdata={s.data}\n\t\t\t\t\t\t\t\t\t\t\t\t\temptyMessage=\"This session does not contain any field data yet.\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</>\n\t)\n}\n\ntype IconProps = React.SVGProps<SVGSVGElement>\n\nfunction HistoryIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<path d=\"M3 3v6h6\" />\n\t\t\t<path d=\"M3.05 13A9 9 0 1 0 9 3.05\" />\n\t\t\t<path d=\"M12 7v5l3 3\" />\n\t\t</svg>\n\t)\n}\n\nfunction XIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n\t\t\t<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n\t\t</svg>\n\t)\n}\n\nfunction FilterIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\" />\n\t\t</svg>\n\t)\n}\n\nfunction ChevronDownIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<polyline points=\"6 9 12 15 18 9\" />\n\t\t</svg>\n\t)\n}\n\n","import { useMemo } from \"react\"\nimport { ensureFormSnapshotsStyles } from \"./styles\"\n\ntype Primitive = string | number | boolean | null | undefined\n\nfunction isPrimitive(val: unknown): val is Primitive {\n\treturn (\n\t\tval === null ||\n\t\tval === undefined ||\n\t\ttypeof val === \"string\" ||\n\t\ttypeof val === \"number\" ||\n\t\ttypeof val === \"boolean\"\n\t)\n}\n\nexport interface FormSnapshotTableProps {\n\t/**\n\t * JSON-stringified snapshot. Genellikle `FormSession.data`.\n\t */\n\tdata: string\n\t/**\n\t * Message to display when the table is empty.\n\t * Default: \"No data captured yet.\"\n\t */\n\temptyMessage?: string\n\t/**\n\t * Extra class names for the outer wrapper.\n\t */\n\tclassName?: string\n}\n\ntype ParsedSnapshot = {\n\tsnapshot: Record<string, unknown>\n\tfieldCount: number\n}\n\nconst snapshotCache = new Map<string, ParsedSnapshot>()\n\nfunction parseSnapshot(data: string): ParsedSnapshot {\n\tconst cached = snapshotCache.get(data)\n\tif (cached) return cached\n\n\tlet snapshot: Record<string, unknown> = {}\n\ttry {\n\t\tsnapshot = JSON.parse(data) as Record<string, unknown>\n\t} catch {\n\t\tsnapshot = {}\n\t}\n\n\tconst parsed = {\n\t\tsnapshot,\n\t\tfieldCount: Object.keys(snapshot).length,\n\t}\n\n\tsnapshotCache.set(data, parsed)\n\treturn parsed\n}\n\nexport function FormSnapshotTable({\n\tdata,\n\temptyMessage = \"No data captured yet.\",\n\tclassName,\n}: Readonly<FormSnapshotTableProps>) {\n\tensureFormSnapshotsStyles()\n\n\tconst { snapshot, fieldCount } = useMemo(() => parseSnapshot(data), [data])\n\n\tif (fieldCount === 0) {\n\t\treturn (\n\t\t\t<p className=\"fs-snapshot-empty\">\n\t\t\t\t{emptyMessage}\n\t\t\t</p>\n\t\t)\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tclassName={[\n\t\t\t\t\"fs-snapshot-wrapper\",\n\t\t\t\tclassName,\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \")}\n\t\t>\n\t\t\t<table className=\"fs-snapshot-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr className=\"fs-snapshot-header-row\">\n\t\t\t\t\t\t<th className=\"fs-snapshot-header-cell\">\n\t\t\t\t\t\t\tField\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"fs-snapshot-header-cell\">\n\t\t\t\t\t\t\tValue\n\t\t\t\t\t\t</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{Object.entries(snapshot).map(([key, value]) => (\n\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\tclassName=\"fs-snapshot-row\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<td className=\"fs-snapshot-key\">\n\t\t\t\t\t\t\t\t{key}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td className=\"fs-snapshot-value\">\n\t\t\t\t\t\t\t\t<SnapshotValueCell value={value} />\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t)\n}\n\nfunction SnapshotValueCell({ value }: Readonly<{ value: unknown }>) {\n\tif (isPrimitive(value)) {\n\t\tif (value === null || value === undefined || value === \"\") {\n\t\t\treturn (\n\t\t\t\t<span className=\"fs-snapshot-muted\">\n\t\t\t\t\t—\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\tif (typeof value === \"boolean\") {\n\t\t\treturn (\n\t\t\t\t<span\n\t\t\t\t\tclassName={\n\t\t\t\t\t\tvalue\n\t\t\t\t\t\t\t? \"fs-snapshot-badge fs-snapshot-badge-true\"\n\t\t\t\t\t\t\t: \"fs-snapshot-badge fs-snapshot-badge-false\"\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t{String(value)}\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\treturn <span className=\"fs-snapshot-text\">{String(value)}</span>\n\t}\n\n\tif (Array.isArray(value)) {\n\t\tif (value.length === 0) {\n\t\t\treturn (\n\t\t\t\t<span className=\"fs-snapshot-muted\">\n\t\t\t\t\t—\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\treturn (\n\t\t\t<div className=\"fs-snapshot-array\">\n\t\t\t\t{value.map((v, idx) => (\n\t\t\t\t\t// eslint-disable-next-line react/no-array-index-key\n\t\t\t\t\t<span\n\t\t\t\t\t\tkey={idx}\n\t\t\t\t\t\tclassName=\"fs-snapshot-chip\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{String(v)}\n\t\t\t\t\t</span>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t)\n\t}\n\n\treturn (\n\t\t<pre className=\"fs-snapshot-object\">\n\t\t\t{JSON.stringify(value, null, 2)}\n\t\t</pre>\n\t)\n}\n\n","const STYLE_ID = \"form-snapshots-styles\"\n\nconst SNAPSHOT_TABLE_CSS = `:root {\n /* light – neutral-like palette */\n --fs-st-border: #e5e5e5; /* neutral-200 */\n --fs-st-border-soft: #d4d4d4; /* neutral-300 */\n --fs-st-header-bg: #f5f5f5; /* neutral-100 */\n --fs-st-key-text: #737373; /* neutral-500 */\n --fs-st-muted-text: #737373; /* neutral-500 */\n --fs-st-muted-strong: #a3a3a3; /* neutral-400 */\n --fs-st-chip-bg: #e5e5e5; /* neutral-200 */\n --fs-st-chip-text: #171717; /* neutral-900 */\n --fs-st-object-bg: #f5f5f5; /* neutral-100 */\n --fs-st-badge-true-bg: rgba(22, 163, 74, 0.1);\n --fs-st-badge-true-text: #166534;\n --fs-st-badge-false-border: #d4d4d4;\n --fs-st-badge-false-text: #525252; /* neutral-600 */\n --fs-st-text-main: #171717; /* neutral-900 */\n}\n\nhtml.dark {\n /* dark – neutral-900/950 style */\n --fs-st-border: #404040; /* neutral-700 */\n --fs-st-border-soft: #262626; /* neutral-800 */\n --fs-st-header-bg: #171717; /* neutral-900 */\n --fs-st-key-text: #a3a3a3; /* neutral-400 */\n --fs-st-muted-text: #a3a3a3; /* neutral-400 */\n --fs-st-muted-strong: #737373; /* neutral-500 */\n --fs-st-chip-bg: #262626; /* neutral-800 */\n --fs-st-chip-text: #f5f5f5; /* neutral-100 */\n --fs-st-object-bg: #0a0a0a; /* neutral-950ish */\n --fs-st-badge-true-bg: rgba(22, 163, 74, 0.25);\n --fs-st-badge-true-text: #bbf7d0;\n --fs-st-badge-false-border: #404040;\n --fs-st-badge-false-text: #e5e5e5; /* neutral-200 */\n --fs-st-text-main: #f5f5f5; /* neutral-100 */\n}\n.fs-snapshot-wrapper {\n border-radius: 6px;\n border: 1px solid var(--fs-st-border);\n overflow: hidden;\n color: var(--fs-st-text-main);\n}\n\n.fs-snapshot-table {\n width: 100%;\n font-size: 12px;\n border-collapse: collapse;\n}\n\n.fs-snapshot-header-row {\n border-bottom: 1px solid var(--fs-st-border);\n background: var(--fs-st-header-bg);\n}\n\n.fs-snapshot-header-cell {\n padding: 4px 6px;\n text-align: left;\n font-weight: 500;\n color: var(--fs-st-muted-text);\n}\n\n.fs-snapshot-row {\n border-bottom: 1px solid var(--fs-st-border-soft);\n}\n\n.fs-snapshot-row:last-child {\n border-bottom: none;\n}\n\n.fs-snapshot-key {\n padding: 4px 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 10px;\n color: var(--fs-st-key-text);\n vertical-align: top;\n}\n\n.fs-snapshot-value {\n padding: 4px 6px;\n vertical-align: top;\n}\n\n.fs-snapshot-empty {\n font-size: 12px;\n color: var(--fs-st-muted-text);\n font-style: italic;\n}\n\n.fs-snapshot-muted {\n color: var(--fs-st-muted-strong);\n font-style: italic;\n}\n\n.fs-snapshot-text {\n word-break: break-all;\n}\n\n.fs-snapshot-array {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n}\n\n.fs-snapshot-chip {\n background: var(--fs-st-chip-bg);\n color: var(--fs-st-chip-text);\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 999px;\n}\n\n.fs-snapshot-object {\n max-height: 128px;\n overflow: auto;\n border-radius: 4px;\n background: var(--fs-st-object-bg);\n padding: 4px;\n font-size: 10px;\n}\n\n.fs-snapshot-badge {\n display: inline-flex;\n align-items: center;\n border-radius: 3px;\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.fs-snapshot-badge-true {\n background: var(--fs-st-badge-true-bg);\n color: var(--fs-st-badge-true-text);\n}\n\n.fs-snapshot-badge-false {\n background: transparent;\n color: var(--fs-st-badge-false-text);\n border: 1px solid var(--fs-st-badge-false-border);\n}\n`\n\nconst DEVTOOLS_CSS = `:root {\n /* light mode – Tailwind neutral-ish */\n --fs-dev-bg-surface: rgba(250, 250, 250, 0.95); /* neutral-50 */\n --fs-dev-bg-panel: rgba(250, 250, 250, 0.98); /* neutral-50 */\n --fs-dev-bg-header: #f5f5f5; /* neutral-100 */\n --fs-dev-bg-header-muted: #f5f5f5;\n --fs-dev-bg-toggle-count: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-close-hover: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-tag: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-status: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-snapshot: #f5f5f5; /* neutral-100 */\n\n --fs-dev-border-strong: #e5e5e5; /* neutral-200 */\n --fs-dev-border-subtle: #e5e5e5; /* neutral-200 */\n --fs-dev-border-soft: #d4d4d4; /* neutral-300 */\n --fs-dev-border-snapshot: #d4d4d4; /* neutral-300 */\n\n --fs-dev-text-primary: #171717; /* neutral-900 */\n --fs-dev-text-muted: #737373; /* neutral-500 */\n --fs-dev-text-soft: #525252; /* neutral-600 */\n --fs-dev-text-error: #b91c1c;\n\n --fs-dev-badge-success-bg: rgba(22, 163, 74, 0.1);\n --fs-dev-badge-success-text: #166534;\n --fs-dev-badge-warn-bg: rgba(217, 119, 6, 0.12);\n --fs-dev-badge-warn-text: #92400e;\n\n --fs-dev-shadow-toggle: 0 1px 3px rgba(0, 0, 0, 0.08);\n --fs-dev-shadow-toggle-hover: 0 2px 6px rgba(0, 0, 0, 0.12);\n --fs-dev-shadow-panel: 0 8px 24px rgba(0, 0, 0, 0.12);\n}\n\nhtml.dark .fs-color-scheme-root:root,\nhtml.dark {\n /* dark mode – neutral-900/950 style */\n --fs-dev-bg-surface: rgba(23, 23, 23, 0.95); /* neutral-900 */\n --fs-dev-bg-panel: rgba(23, 23, 23, 0.98); /* neutral-900 */\n --fs-dev-bg-header: #171717; /* neutral-900 */\n --fs-dev-bg-header-muted: #171717;\n --fs-dev-bg-toggle-count: #262626; /* neutral-800 */\n --fs-dev-bg-close-hover: #404040; /* neutral-700 */\n --fs-dev-bg-tag: #262626; /* neutral-800 */\n --fs-dev-bg-status: #171717; /* neutral-900 */\n --fs-dev-bg-snapshot: #0a0a0a; /* neutral-950ish */\n\n --fs-dev-border-strong: #404040; /* neutral-700 */\n --fs-dev-border-subtle: #262626; /* neutral-800 */\n --fs-dev-border-soft: #262626; /* neutral-800 */\n --fs-dev-border-snapshot: #404040; /* neutral-700 */\n\n --fs-dev-text-primary: #fafafa; /* neutral-50 */\n --fs-dev-text-muted: #a3a3a3; /* neutral-400 */\n --fs-dev-text-soft: #d4d4d4; /* neutral-300 */\n --fs-dev-text-error: #fecaca;\n\n --fs-dev-badge-success-bg: rgba(22, 163, 74, 0.25);\n --fs-dev-badge-success-text: #bbf7d0;\n --fs-dev-badge-warn-bg: rgba(217, 119, 6, 0.3);\n --fs-dev-badge-warn-text: #fed7aa;\n\n --fs-dev-shadow-toggle: 0 1px 3px rgba(0, 0, 0, 0.6);\n --fs-dev-shadow-toggle-hover: 0 2px 6px rgba(0, 0, 0, 0.7);\n --fs-dev-shadow-panel: 0 20px 40px rgba(0, 0, 0, 0.8);\n}\n.fs-devtools-toggle {\n position: fixed;\n bottom: 16px;\n right: 16px;\n z-index: 9999;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n border-radius: 999px;\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-surface);\n padding: 4px 8px;\n font-size: 12px;\n color: var(--fs-dev-text-soft);\n box-shadow: var(--fs-dev-shadow-toggle);\n cursor: pointer;\n}\n\n.fs-devtools-toggle:hover {\n box-shadow: var(--fs-dev-shadow-toggle-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-toggle-count {\n margin-left: 4px;\n border-radius: 999px;\n background: var(--fs-dev-bg-toggle-count);\n padding: 0 4px;\n font-size: 10px;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-panel {\n position: fixed;\n bottom: 56px;\n overflow: hidden;\n right: 16px;\n z-index: 9999;\n width: 420px;\n max-height: 60vh;\n border-radius: 12px;\n color: var(--fs-dev-text-primary);\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-panel);\n box-shadow: var(--fs-dev-shadow-panel);\n display: flex;\n flex-direction: column;\n font-size: 14px;\n}\n\n.fs-devtools-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-subtle);\n background: var(--fs-dev-bg-header);\n}\n\n.fs-devtools-header-left {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.fs-devtools-header-text {\n display: flex;\n flex-direction: column;\n}\n\n.fs-devtools-header-title {\n font-size: 12px;\n font-weight: 600;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-header-subtitle {\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-header-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.fs-devtools-clear {\n border-radius: 999px;\n padding: 2px 8px;\n font-size: 11px;\n border: 1px solid var(--fs-dev-border-subtle);\n background: transparent;\n color: var(--fs-dev-text-muted);\n cursor: pointer;\n}\n\n.fs-devtools-clear:hover:enabled {\n background: var(--fs-dev-bg-close-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-clear:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.fs-devtools-close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 999px;\n padding: 4px;\n background: transparent;\n border: none;\n cursor: pointer;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-close:hover {\n background: var(--fs-dev-bg-close-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-filters {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-subtle);\n}\n\n.fs-devtools-select {\n flex: 1;\n border-radius: 4px;\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-panel);\n padding: 2px 6px;\n font-size: 12px;\n}\n\n.fs-devtools-filter-label {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-body {\n flex: 1;\n overflow: auto;\n}\n\n.fs-devtools-empty {\n padding: 10px 12px;\n font-size: 12px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-list {\n list-style: none;\n margin: 0;\n padding: 0;\n border-top: 1px solid var(--fs-dev-border-soft);\n}\n\n.fs-devtools-item {\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-soft);\n}\n\n.fs-devtools-item-toggle {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n background: transparent;\n border: none;\n padding: 0;\n cursor: pointer;\n text-align: left;\n}\n\n.fs-devtools-item-main {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n min-width: 0;\n}\n\n.fs-devtools-item-text {\n min-width: 0;\n}\n\n.fs-devtools-item-tags {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.fs-devtools-tag {\n display: inline-flex;\n align-items: center;\n border-radius: 999px;\n background: var(--fs-dev-bg-tag);\n padding: 2px 6px;\n font-size: 10px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n \"Courier New\", monospace;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-id {\n font-size: 10px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-meta {\n margin-top: 2px;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-delete {\n border: none;\n background: transparent;\n padding: 0;\n font-size: 11px;\n color: var(--fs-dev-text-soft);\n cursor: pointer;\n}\n\n.fs-devtools-delete:hover {\n text-decoration: underline;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-badge {\n border-radius: 999px;\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.fs-devtools-badge-success {\n background: var(--fs-dev-badge-success-bg);\n color: var(--fs-dev-badge-success-text);\n}\n\n.fs-devtools-badge-warn {\n background: var(--fs-dev-badge-warn-bg);\n color: var(--fs-dev-badge-warn-text);\n}\n\n.fs-devtools-status {\n border-radius: 999px;\n background: var(--fs-dev-bg-status);\n padding: 2px 6px;\n font-size: 10px;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-error {\n margin-left: 8px;\n max-width: 40%;\n font-size: 11px;\n color: var(--fs-dev-text-error);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.fs-devtools-snapshot {\n margin-top: 6px;\n border-radius: 6px;\n background: var(--fs-dev-bg-snapshot);\n font-size: 12px;\n}\n`\n\nexport function ensureFormSnapshotsStyles() {\n\tif (typeof document === \"undefined\") return\n\tif (document.getElementById(STYLE_ID)) return\n\n\tconst style = document.createElement(\"style\")\n\tstyle.id = STYLE_ID\n\tstyle.type = \"text/css\"\n\tstyle.appendChild(\n\t\tdocument.createTextNode(`${SNAPSHOT_TABLE_CSS}\\n\\n${DEVTOOLS_CSS}`),\n\t)\n\t;(document.head || document.documentElement).appendChild(style)\n}\n\n","import type { UseFormReturn } from \"react-hook-form\"\nimport { useFormSnapshots, type FormSnapshotsOptions } from \"./hooks/use-form-snapshots\"\n\ntype WithoutSnapshotOverrides = Omit<\n\tFormSnapshotsOptions,\n\t\"getValues\" | \"applyValues\"\n>\n\nexport interface RHFSnapshotsOptions extends WithoutSnapshotOverrides {}\n\nexport function useRHFFormSnapshots<TFieldValues extends Record<string, unknown>>(\n\tformName: string,\n\tform: UseFormReturn<TFieldValues>,\n\toptions?: RHFSnapshotsOptions,\n) {\n\treturn useFormSnapshots(formName, {\n\t\t...options,\n\t\tgetValues: () => form.getValues() as Record<string, unknown>,\n\t\tapplyValues: (snap) => form.reset(snap as TFieldValues),\n\t})\n}\n\nexport interface ObjectStateSnapshotsOptions\n\textends WithoutSnapshotOverrides {}\n\nexport function useObjectFormSnapshots<TState extends Record<string, unknown>>(\n\tformName: string,\n\tstate: TState,\n\tsetState: (next: TState) => void,\n\toptions?: ObjectStateSnapshotsOptions,\n) {\n\treturn useFormSnapshots(formName, {\n\t\t...options,\n\t\tgetValues: () => state as Record<string, unknown>,\n\t\tapplyValues: (snap) => setState(snap as TState),\n\t})\n}\n\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4I3B,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4Vd,SAAS,4BAA4B;AAC3C,MAAI,OAAO,aAA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","names":["useMemo","useState","useLiveQuery","jsx","jsx","jsxs","useState","useLiveQuery","useMemo"]}
1
+ {"version":3,"sources":["../form-snapshots/hooks/use-form-snapshots.ts","../form-snapshots/client.ts","../form-snapshots/local-db/db.ts","../form-snapshots/dexie-storage.ts","../form-snapshots/dom-snapshot.ts","../form-snapshots/context.tsx","../form-snapshots/hooks/use-form-snapshots-list.ts","../form-snapshots/devtools.tsx","../form-snapshots/snapshot-table.tsx","../form-snapshots/styles.ts","../form-snapshots/adapters.ts"],"sourcesContent":["import {\n\tuseCallback,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n\ttype SyntheticEvent,\n} from \"react\"\nimport { FormSnapshotsClient } from \"../client\"\nimport { DexieFormSnapshotsStorage } from \"../dexie-storage\"\nimport { type FormSnapshot, type FormSnapshotsStorage } from \"../types\"\nimport {\n\trestoreSnapshotToForm,\n\tsnapshotFromDOMForm,\n} from \"../dom-snapshot\"\nimport { useFormSnapshotsConfig } from \"../context\"\n\nexport interface FormSnapshotsOptions {\n\t/** How long in ms to retain history entries. Default: 24 h */\n\tsnapshotsLimit?: number\n\n\t/**\n\t * Field paths (path/prefix) to exclude while saving snapshots.\n\t * E.g. \"password\", \"patient.address\".\n\t * Merged with the global excludeFields config.\n\t */\n\texcludeFields?: string[]\n\n\t/**\n\t * Override how the current form state is read.\n\t *\n\t * Use this for controlled forms or nested / structured data models\n\t * (e.g. HL7 FHIR Address, HumanName, ContactPoint[]) where values live\n\t * in React state rather than plain DOM elements.\n\t */\n\tgetValues?: () => FormSnapshot\n\n\t/**\n\t * Override how a saved snapshot is applied back to the form.\n\t *\n\t * Receives the full snapshot that was previously returned by `getValues`.\n\t */\n\tapplyValues?: (snapshot: FormSnapshot) => void\n\n\t/**\n\t * When true, the active session is deleted from storage and the form\n\t * is cleared immediately after submit so that submitted data is not\n\t * kept in history. Can be overridden globally via provider config.\n\t */\n\tdiscardOnSubmit?: boolean\n}\n\nfunction applyExcludeFields(\n\tsnapshot: FormSnapshot,\n\texcludeFields?: string[],\n): FormSnapshot {\n\tif (!excludeFields || excludeFields.length === 0) return snapshot\n\n\tfunction pruneByPrefix(\n\t\tobj: FormSnapshot,\n\t\tpaths: string[],\n\t\tparentPath = \"\",\n\t): FormSnapshot {\n\t\tconst result: FormSnapshot = {}\n\n\t\tfor (const [key, value] of Object.entries(obj)) {\n\t\t\tconst path = parentPath ? `${parentPath}.${key}` : key\n\n\t\t\tconst isExcluded = paths.some(\n\t\t\t\t(p) => path === p || path.startsWith(`${p}.`),\n\t\t\t)\n\t\t\tif (isExcluded) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (value && typeof value === \"object\" && !Array.isArray(value)) {\n\t\t\t\tresult[key] = pruneByPrefix(\n\t\t\t\t\tvalue as FormSnapshot,\n\t\t\t\t\tpaths,\n\t\t\t\t\tpath,\n\t\t\t\t)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// For now, arrays are treated as atomic: you can exclude the whole\n\t\t\t// array field with its path, but not individual items.\n\t\t\tresult[key] = value\n\t\t}\n\n\t\treturn result\n\t}\n\n\treturn pruneByPrefix(snapshot, excludeFields)\n}\n\nexport function useFormSnapshots(\n\tformName: string,\n\toptions?: FormSnapshotsOptions,\n) {\n\tconst { snapshotsLimit, getValues, applyValues, excludeFields, discardOnSubmit } =\n\t\toptions ?? {}\n\n\tconst formRef = useRef<HTMLFormElement>(null)\n\tconst sessionIdRef = useRef<number | null>(null)\n\tconst lastSubmittedSessionIdRef = useRef<number | null>(null)\n\tconst clientRef = useRef<FormSnapshotsClient | null>(null)\n\tconst [isSubmitted, setIsSubmitted] = useState(false)\n\tconst storageRef = useRef<FormSnapshotsStorage | null>(null)\n\n\tconst {\n\t\tdefaultSnapshotsLimit,\n\t\texcludeFields: globalExcludeFields,\n\t\tdiscardOnSubmit: globalDiscardOnSubmit,\n\t} = useFormSnapshotsConfig()\n\n\tconst effectiveSnapshotsLimit =\n\t\tsnapshotsLimit ?? defaultSnapshotsLimit ?? 24 * 60 * 60 * 1000\n\n\tconst effectiveDiscardOnSubmit =\n\t\ttypeof discardOnSubmit === \"boolean\"\n\t\t\t? discardOnSubmit\n\t\t\t: globalDiscardOnSubmit ?? false\n\n\tconst mergedExcludeFields =\n\t\tglobalExcludeFields || excludeFields\n\t\t\t? Array.from(\n\t\t\t\t\tnew Set([...(globalExcludeFields ?? []), ...(excludeFields ?? [])]),\n\t\t\t\t)\n\t\t\t: undefined\n\n\t// Keep latest callbacks in refs so they never invalidate the init effect\n\tconst getValuesRef = useRef(getValues)\n\tconst applyValuesRef = useRef(applyValues)\n\tuseEffect(() => { getValuesRef.current = getValues }, [getValues])\n\tuseEffect(() => { applyValuesRef.current = applyValues }, [applyValues])\n\n\t// ── Initialise: create client, prune, resume or open session ───────────────\n\tuseEffect(() => {\n\t\tlet cancelled = false\n\n\t\tasync function init() {\n\t\t\tif (!storageRef.current) {\n\t\t\t\tstorageRef.current = new DexieFormSnapshotsStorage()\n\t\t\t}\n\n\t\t\tconst client = new FormSnapshotsClient({\n\t\t\t\tstorage: storageRef.current!,\n\t\t\t\tformName,\n\t\t\t\tsnapshotsLimit: effectiveSnapshotsLimit,\n\t\t\t})\n\n\t\t\tclientRef.current = client\n\n\t\t\tconst session = await client.initSession()\n\t\t\tif (cancelled) return\n\n\t\t\tsessionIdRef.current = session.id\n\n\t\t\t// Restore saved values (after mount) via applyValues callback when\n\t\t\t// provided, or fall back to DOM-based restoration for flat forms.\n\t\t\tif (session.data && session.data !== \"{}\") {\n\t\t\t\trequestAnimationFrame(() => {\n\t\t\t\t\tif (cancelled) return\n\t\t\t\t\tconst snapshot = JSON.parse(session.data) as FormSnapshot\n\t\t\t\t\tif (applyValuesRef.current) {\n\t\t\t\t\t\tapplyValuesRef.current(snapshot)\n\t\t\t\t\t} else if (formRef.current) {\n\t\t\t\t\t\trestoreSnapshotToForm(formRef.current, snapshot)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tinit()\n\n\t\treturn () => {\n\t\t\tcancelled = true\n\t\t}\n\t}, [formName, effectiveSnapshotsLimit])\n\n\t// ── Snapshot current form values into the active session ──────────────────\n\tconst handleBlur = useCallback(() => {\n\t\tconst sessionId = sessionIdRef.current\n\t\tif (sessionId === null) return\n\n\t\tlet snapshot: FormSnapshot\n\n\t\tif (getValuesRef.current) {\n\t\t\t// Controlled / structured form — caller owns the state.\n\t\t\t// Supports arbitrarily nested values: HL7 Address, HumanName,\n\t\t\t// ContactPoint[], CodeableConcept, etc.\n\t\t\tsnapshot = getValuesRef.current()\n\t\t} else {\n\t\t\t// Fallback: enumerate flat DOM form elements (strings / booleans only).\n\t\t\tconst form = formRef.current\n\t\t\tif (!form) return\n\t\t\tsnapshot = snapshotFromDOMForm(form)\n\t\t}\n\n\t\tsnapshot = applyExcludeFields(snapshot, mergedExcludeFields)\n\n\t\tif (!clientRef.current) return\n\n\t\tclientRef.current.saveSnapshot(sessionId, snapshot)\n\t}, []) // getValues is read via ref — no dep needed\n\n\t// ── Clear current form values after submit when requested ─────────────────\n\tconst clearFormState = useCallback(() => {\n\t\tif (applyValuesRef.current) {\n\t\t\t// For controlled / structured forms, an empty snapshot is passed\n\t\t\t// back to the caller so they can reset their own state.\n\t\t\tapplyValuesRef.current({} as FormSnapshot)\n\t\t} else if (formRef.current) {\n\t\t\t// For plain DOM forms, reset back to the initial values.\n\t\t\tformRef.current.reset()\n\t\t}\n\t}, [])\n\n\t// ── Wrap the form's onSubmit to close the session ─────────────────────────\n\tconst wrapSubmit = useCallback(\n\t\t(\n\t\t\tuserSubmit?: (e: SyntheticEvent<HTMLFormElement>) => void,\n\t\t) =>\n\t\t\t(e: SyntheticEvent<HTMLFormElement>) => {\n\t\t\t\te.preventDefault()\n\t\t\t\thandleBlur() // capture the final state before closing\n\n\t\t\t\tconst sessionId = sessionIdRef.current\n\t\t\t\tconst client = clientRef.current\n\t\t\t\tif (sessionId !== null && client) {\n\t\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\t\t// Discard the entire session so submitted data is not kept.\n\t\t\t\t\t\tclient.deleteSession(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = null\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclient.markSubmitted(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = sessionId\n\t\t\t\t\t}\n\t\t\t\t\t// Closed session should no longer receive snapshots\n\t\t\t\t\tsessionIdRef.current = null\n\t\t\t\t}\n\n\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\tclearFormState()\n\t\t\t\t}\n\n\t\t\t\tsetIsSubmitted(true)\n\t\t\t\tuserSubmit?.(e)\n\t\t\t},\n\t\t[handleBlur, clearFormState, effectiveDiscardOnSubmit],\n\t)\n\n\tconst markSubmitResult = useCallback(\n\t\tasync (params: {\n\t\t\tstatusCode?: number | null\n\t\t\terrorMessage?: string | null\n\t\t}) => {\n\t\t\tconst client = clientRef.current\n\t\t\tconst sessionId = lastSubmittedSessionIdRef.current\n\t\t\tif (!client || sessionId == null) return\n\n\t\t\tawait client.setSubmissionResult({\n\t\t\t\tsessionId,\n\t\t\t\tstatusCode: params.statusCode,\n\t\t\t\terrorMessage: params.errorMessage,\n\t\t\t})\n\t\t},\n\t\t[],\n\t)\n\n\tconst wrapSubmitAsync = useCallback(\n\t\t(\n\t\t\t\tuserSubmit?: (\n\t\t\t\t\te: SyntheticEvent<HTMLFormElement>,\n\t\t\t\t) => Promise<\n\t\t\t\t\t| void\n\t\t\t\t\t| {\n\t\t\t\t\t\t\tstatusCode?: number | null\n\t\t\t\t\t\t\terrorMessage?: string | null\n\t\t\t\t\t }\n\t\t\t\t>,\n\t\t\t) =>\n\t\t\tasync (e: SyntheticEvent<HTMLFormElement>) => {\n\t\t\t\te.preventDefault()\n\t\t\t\thandleBlur()\n\n\t\t\t\tconst sessionId = sessionIdRef.current\n\t\t\t\tconst client = clientRef.current\n\t\t\t\tif (sessionId !== null && client) {\n\t\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\t\tawait client.deleteSession(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = null\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait client.markSubmitted(sessionId)\n\t\t\t\t\t\tlastSubmittedSessionIdRef.current = sessionId\n\t\t\t\t\t}\n\t\t\t\t\tsessionIdRef.current = null\n\t\t\t\t}\n\n\t\t\t\tif (effectiveDiscardOnSubmit) {\n\t\t\t\t\tclearFormState()\n\t\t\t\t}\n\n\t\t\t\tsetIsSubmitted(true)\n\n\t\t\t\tif (!userSubmit) return\n\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await userSubmit(e)\n\t\t\t\t\tif (result && typeof result === \"object\") {\n\t\t\t\t\t\tawait markSubmitResult({\n\t\t\t\t\t\t\tstatusCode: \"statusCode\" in result ? result.statusCode : null,\n\t\t\t\t\t\t\terrorMessage:\n\t\t\t\t\t\t\t\t\"errorMessage\" in result ? result.errorMessage : null,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t// In case of error we leave handling to the caller;\n\t\t\t// logging here is enough. Callers can still invoke\n\t\t\t// markSubmitResult manually if they want.\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.error(\"wrapSubmitAsync userSubmit error:\", error)\n\t\t\t\t}\n\t\t\t},\n\t\t[handleBlur, markSubmitResult, clearFormState, effectiveDiscardOnSubmit],\n\t)\n\n\t// ── Restore the latest saved snapshot into the DOM form ───────────────────\n\tconst restoreLatest = useCallback(async () => {\n\t\tif (!clientRef.current) return\n\n\t\tconst snapshot = await clientRef.current.getLatestSnapshot()\n\t\tif (!snapshot) return\n\n\t\tif (applyValuesRef.current) {\n\t\t\t// Structured form — route the full nested snapshot back to state.\n\t\t\tapplyValuesRef.current(snapshot)\n\t\t} else {\n\t\t\tif (!formRef.current) return\n\t\t\trestoreSnapshotToForm(formRef.current, snapshot)\n\t\t}\n\n\t\t// Open a new session pre-seeded with the restored data so further edits\n\t\t// are tracked against the fresh session\n\t\tconst newSession = await clientRef.current.openNewSessionFromSnapshot(\n\t\t\tsnapshot,\n\t\t)\n\t\tsessionIdRef.current = newSession.id\n\n\t\tsetIsSubmitted(false)\n\t}, []) // applyValues is read via ref — no dep needed\n\n\treturn {\n\t\tformRef,\n\t\thandleBlur,\n\t\twrapSubmit,\n\t\twrapSubmitAsync,\n\t\trestoreLatest,\n\t\tisSubmitted,\n\t\tmarkSubmitResult,\n\t}\n}\n","import type {\n\tFormSnapshotsStorage,\n\tFormSessionBase,\n\tFormSnapshot,\n} from \"./types\"\n\nexport class FormSnapshotsClient {\n\tprivate readonly storage: FormSnapshotsStorage\n\tprivate readonly formName: string\n\tprivate readonly snapshotsLimit: number\n\n\tconstructor(params: {\n\t\tstorage: FormSnapshotsStorage\n\t\tformName: string\n\t\tsnapshotsLimit: number\n\t}) {\n\t\tthis.storage = params.storage\n\t\tthis.formName = params.formName\n\t\tthis.snapshotsLimit = params.snapshotsLimit\n\t}\n\n\tasync initSession(): Promise<FormSessionBase> {\n\t\tconst now = Date.now()\n\t\tconst cutoff = now - this.snapshotsLimit\n\n\t\tawait this.storage.pruneOlderThan(cutoff)\n\n\t\tconst existing = await this.storage.findActiveSession(this.formName)\n\t\tif (existing) return existing\n\n\t\treturn this.storage.createSession(this.formName, {})\n\t}\n\n\tasync saveSnapshot(\n\t\tsessionId: number,\n\t\tsnapshot: FormSnapshot,\n\t): Promise<void> {\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tdata: JSON.stringify(snapshot),\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync markSubmitted(sessionId: number): Promise<void> {\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tsubmitted: true,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync deleteSession(sessionId: number): Promise<void> {\n\t\tif (typeof this.storage.deleteSession === \"function\") {\n\t\t\tawait this.storage.deleteSession(sessionId)\n\t\t}\n\t}\n\n\tasync setSubmissionResult(params: {\n\t\tsessionId: number\n\t\tstatusCode?: number | null\n\t\terrorMessage?: string | null\n\t}): Promise<void> {\n\t\tconst { sessionId, statusCode, errorMessage } = params\n\t\tawait this.storage.updateSession(sessionId, {\n\t\t\tstatusCode: statusCode ?? null,\n\t\t\terrorMessage: errorMessage ?? null,\n\t\t\tupdatedAt: Date.now(),\n\t\t})\n\t}\n\n\tasync getLatestSnapshot(): Promise<FormSnapshot | null> {\n\t\tconst latest = await this.storage.getLatestSession(this.formName)\n\t\tif (!latest) return null\n\t\treturn JSON.parse(latest.data) as FormSnapshot\n\t}\n\n\tasync openNewSessionFromSnapshot(\n\t\tsnapshot: FormSnapshot,\n\t): Promise<FormSessionBase> {\n\t\treturn this.storage.createSession(this.formName, snapshot)\n\t}\n}\n\n","import { Dexie, type EntityTable } from \"dexie\"\n\nexport interface FormSession {\n\tid: number\n\tformName: string\n\t/** JSON-serialised record of field name → value */\n\tdata: string\n\tcreatedAt: number\n\tupdatedAt: number\n\t/** true = the form was submitted; session is closed / read-only */\n\tsubmitted: boolean\n\t/** Optional HTTP-like status code for the last submit attempt */\n\tstatusCode?: number | null\n\t/** Optional error message when the last submit attempt failed */\n\terrorMessage?: string | null\n}\n\nconst db = new Dexie(\"FormSnapshotsDatabase\") as Dexie & {\n\tformSessions: EntityTable<FormSession, \"id\">\n}\n\ndb.version(1).stores({\n\tformSessions: \"++id, formName, createdAt, submitted, [formName+createdAt]\",\n})\n\ndb.version(2).stores({\n\tformSessions:\n\t\t\"++id, formName, createdAt, updatedAt, submitted, [formName+createdAt]\",\n})\n\nexport { db }\n\n","import { db, type FormSession } from \"./local-db/db\"\nimport {\n\ttype FormSnapshotsStorage,\n\ttype FormSessionBase,\n\ttype FormSnapshot,\n} from \"./types\"\n\nfunction mapToBase(session: FormSession): FormSessionBase {\n\treturn {\n\t\tid: session.id,\n\t\tformName: session.formName,\n\t\tdata: session.data,\n\t\tcreatedAt: session.createdAt,\n\t\tupdatedAt: session.updatedAt,\n\t\tsubmitted: session.submitted,\n\t\tstatusCode: session.statusCode ?? null,\n\t\terrorMessage: session.errorMessage ?? null,\n\t}\n}\n\n\texport class DexieFormSnapshotsStorage implements FormSnapshotsStorage {\n\tasync findActiveSession(formName: string): Promise<FormSessionBase | null> {\n\t\tconst existing = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.filter((s) => !s.submitted)\n\t\t\t.last()\n\n\t\treturn existing ? mapToBase(existing) : null\n\t}\n\n\tasync createSession(\n\t\tformName: string,\n\t\tsnapshot: FormSnapshot,\n\t): Promise<FormSessionBase> {\n\t\tconst now = Date.now()\n\t\tconst id = await db.formSessions.add({\n\t\t\tformName,\n\t\t\tdata: JSON.stringify(snapshot),\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t\tsubmitted: false,\n\t\t\tstatusCode: null,\n\t\t\terrorMessage: null,\n\t\t})\n\n\t\tconst created = await db.formSessions.get(id)\n\t\tif (!created) {\n\t\t\tthrow new Error(\"Failed to create form session\")\n\t\t}\n\n\t\treturn mapToBase(created)\n\t}\n\n\tasync updateSession(\n\t\tid: number,\n\t\tpatch: Partial<\n\t\t\tPick<\n\t\t\t\tFormSessionBase,\n\t\t\t\t\"data\" | \"updatedAt\" | \"submitted\" | \"statusCode\" | \"errorMessage\"\n\t\t\t>\n\t\t>,\n\t): Promise<void> {\n\t\tawait db.formSessions.update(id, patch)\n\t}\n\n\tasync getLatestSession(formName: string): Promise<FormSessionBase | null> {\n\t\tconst sessions = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.sortBy(\"updatedAt\")\n\n\t\tconst latest = sessions[sessions.length - 1]\n\t\treturn latest ? mapToBase(latest) : null\n\t}\n\n\tasync listSessions(formName: string): Promise<FormSessionBase[]> {\n\t\tconst sessions = await db.formSessions\n\t\t\t.where(\"formName\")\n\t\t\t.equals(formName)\n\t\t\t.sortBy(\"updatedAt\")\n\n\t\treturn sessions.map(mapToBase)\n\t}\n\n\tasync pruneOlderThan(cutoffMs: number): Promise<void> {\n\t\tawait db.formSessions.where(\"createdAt\").below(cutoffMs).delete()\n\t}\n\n\tasync deleteSession(id: number): Promise<void> {\n\t\tawait db.formSessions.delete(id)\n\t}\n}\n\n","type AnyFormEl = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement\n\nfunction toStringValue(val: unknown): string {\n\tif (val == null) return \"\"\n\tif (typeof val === \"object\") return JSON.stringify(val)\n\treturn String(val as string | number | boolean | bigint)\n}\n\nfunction applyValueToInput(input: AnyFormEl, val: unknown): void {\n\tif (input instanceof HTMLInputElement) {\n\t\tif (input.type === \"checkbox\") {\n\t\t\tinput.checked = Boolean(val)\n\t\t\tinput.dispatchEvent(new Event(\"change\", { bubbles: true }))\n\t\t\treturn\n\t\t}\n\t\tif (input.type === \"radio\") {\n\t\t\tinput.checked = input.value === val\n\t\t\tif (input.checked) input.dispatchEvent(new Event(\"change\", { bubbles: true }))\n\t\t\treturn\n\t\t}\n\t}\n\tinput.value = toStringValue(val)\n\tinput.dispatchEvent(new Event(\"input\", { bubbles: true }))\n}\n\n/** Populate DOM form elements from a plain snapshot object */\nexport function restoreSnapshotToForm(\n\tform: HTMLFormElement,\n\tsnapshot: Record<string, unknown>,\n) {\n\tfor (const el of Array.from(form.elements)) {\n\t\tconst input = el as AnyFormEl\n\t\tif (!input.name || !(input.name in snapshot)) continue\n\t\tapplyValueToInput(input, snapshot[input.name])\n\t}\n}\n\n/** Read all named fields from a plain DOM form into a flat snapshot object */\nexport function snapshotFromDOMForm(\n\tform: HTMLFormElement,\n): Record<string, unknown> {\n\tconst snapshot: Record<string, unknown> = {}\n\tfor (const el of Array.from(form.elements)) {\n\t\tconst input = el as AnyFormEl\n\t\tif (!input.name) continue\n\t\treadInputIntoSnapshot(input, snapshot)\n\t}\n\treturn snapshot\n}\n\nexport function readInputIntoSnapshot(\n\tinput: AnyFormEl,\n\tsnapshot: Record<string, unknown>,\n) {\n\tif (input instanceof HTMLInputElement) {\n\t\tif (input.type === \"checkbox\") {\n\t\t\tsnapshot[input.name] = input.checked\n\t\t} else if (input.type === \"radio\") {\n\t\t\tif (input.checked) snapshot[input.name] = input.value\n\t\t} else if (input.type !== \"file\") {\n\t\t\tsnapshot[input.name] = input.value\n\t\t}\n\t} else {\n\t\tsnapshot[input.name] = input.value\n\t}\n}\n\n","import type { ReactNode } from \"react\"\nimport { createContext, useContext } from \"react\"\n\nexport interface FormSnapshotsGlobalConfig {\n\tdefaultSnapshotsLimit?: number\n\t/**\n\t * Field paths (path/prefix) to exclude while saving snapshots.\n\t * E.g. [\"password\", \"patient.address\"]\n\t */\n\texcludeFields?: string[]\n\n\t/**\n\t * When true, submitted sessions are discarded and the form is cleared\n\t * immediately after submit instead of keeping the data in history.\n\t * Can be overridden per form via hook options.\n\t */\n\tdiscardOnSubmit?: boolean\n}\n\nconst FormSnapshotsConfigContext = createContext<FormSnapshotsGlobalConfig | undefined>(\n\tundefined,\n)\n\nexport interface FormSnapshotsProviderProps {\n\tvalue?: FormSnapshotsGlobalConfig\n\tchildren: ReactNode\n}\n\nexport function FormSnapshotsProvider({\n\tvalue,\n\tchildren,\n}: Readonly<FormSnapshotsProviderProps>) {\n\treturn (\n\t\t<FormSnapshotsConfigContext.Provider value={value ?? {}}>\n\t\t\t{children}\n\t\t</FormSnapshotsConfigContext.Provider>\n\t)\n}\n\nexport function useFormSnapshotsConfig(): FormSnapshotsGlobalConfig {\n\treturn useContext(FormSnapshotsConfigContext) ?? {}\n}\n\n","import { useLiveQuery } from \"dexie-react-hooks\"\nimport { db, type FormSession } from \"../local-db/db\"\n\nexport interface useFormSnapshotsListOptions {\n\t/**\n\t * Whether to return only submitted (completed) sessions.\n\t * Default: false (both submitted and in-progress).\n\t */\n\tonlySubmitted?: boolean\n\n\t/**\n\t * Whether to return only successful (2xx) submission results.\n\t * Default: false (all statusCode values).\n\t */\n\tonlySuccessful?: boolean\n\n\t/**\n\t * Maximum age in ms. E.g. last 24 hours = 24 * 60 * 60 * 1000.\n\t * Default: unlimited.\n\t */\n\tmaxAgeMs?: number\n\n\t/**\n\t * Maximum number of records to return.\n\t * Default: unlimited.\n\t */\n\tlimit?: number\n}\n\nexport interface FormHistoryListItem {\n\tid: number\n\tformName: string\n\tcreatedAt: number\n\tupdatedAt: number\n\tsubmitted: boolean\n\tstatusCode: number | null\n\terrorMessage: string | null\n\t/**\n\t * Age in ms relative to \\\"now\\\".\n\t */\n\tageMs: number\n}\n\nexport function useFormSnapshotsList(\n\tformName: string,\n\toptions?: useFormSnapshotsListOptions,\n) {\n\tconst {\n\t\tonlySubmitted = false,\n\t\tonlySuccessful = false,\n\t\tmaxAgeMs,\n\t\tlimit,\n\t} = options ?? {}\n\n\tconst items = useLiveQuery<FormHistoryListItem[] | undefined>(async () => {\n\t\tconst now = Date.now()\n\t\tconst cutoff = maxAgeMs ? now - maxAgeMs : undefined\n\n\t\tlet query = db.formSessions.where(\"formName\").equals(formName)\n\n\t\t// Other filters are not supported directly by Dexie, so we refine via JS filter.\n\t\tlet sessions: FormSession[] = await query.toArray()\n\n\t\tif (onlySubmitted) {\n\t\t\tsessions = sessions.filter((s) => s.submitted)\n\t\t}\n\n\t\tif (onlySuccessful) {\n\t\t\tsessions = sessions.filter((s) => {\n\t\t\t\tif (s.statusCode == null) return false\n\t\t\t\treturn s.statusCode >= 200 && s.statusCode < 300\n\t\t\t})\n\t\t}\n\n\t\tif (cutoff != null) {\n\t\t\tsessions = sessions.filter((s) => s.createdAt >= cutoff)\n\t\t}\n\n\t\tsessions.sort((a, b) => b.updatedAt - a.updatedAt)\n\n\t\tif (typeof limit === \"number\") {\n\t\t\tsessions = sessions.slice(0, limit)\n\t\t}\n\n\t\treturn sessions.map((s) => ({\n\t\t\tid: s.id,\n\t\t\tformName: s.formName,\n\t\t\tcreatedAt: s.createdAt,\n\t\t\tupdatedAt: s.updatedAt,\n\t\t\tsubmitted: s.submitted,\n\t\t\tstatusCode: s.statusCode ?? null,\n\t\t\terrorMessage: s.errorMessage ?? null,\n\t\t\tageMs: now - s.updatedAt,\n\t\t}))\n\t}, [formName, onlySubmitted, onlySuccessful, maxAgeMs, limit])\n\n\tconst isLoading = items === undefined\n\n\treturn { items: items ?? [], isLoading }\n}\n\n","import { useMemo, useState } from \"react\"\nimport { useLiveQuery } from \"dexie-react-hooks\"\nimport { db, type FormSession } from \"./local-db/db\"\nimport { FormSnapshotTable } from \"./snapshot-table\"\nimport { ensureFormSnapshotsStyles } from \"./styles\"\n\nfunction formatDateShort(ms: number) {\n\treturn new Intl.DateTimeFormat(\"en-GB\", {\n\t\tmonth: \"short\",\n\t\tday: \"2-digit\",\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t}).format(new Date(ms))\n}\n\nfunction formatAge(ms: number) {\n\tconst seconds = Math.floor(ms / 1000)\n\tconst minutes = Math.floor(seconds / 60)\n\tconst hours = Math.floor(minutes / 60)\n\tconst days = Math.floor(hours / 24)\n\n\tif (days > 0) return `${days}d ${hours % 24}h`\n\tif (hours > 0) return `${hours}h ${minutes % 60}m`\n\tif (minutes > 0) return `${minutes}m`\n\treturn `${seconds}s`\n}\n\nexport function FormSnapshotsDevtools() {\n\tensureFormSnapshotsStyles()\n\n\tconst isProd =\n\t\ttypeof import.meta !== \"undefined\" &&\n\t\t(import.meta as any).env?.PROD\n\n\tif (isProd) {\n\t\treturn null\n\t}\n\n\tconst [open, setOpen] = useState(false)\n\tconst [selectedForm, setSelectedForm] = useState<string>(\"all\")\n\tconst [onlySubmitted, setOnlySubmitted] = useState(false)\n\tconst [onlyErrors, setOnlyErrors] = useState(false)\n\tconst [expandedId, setExpandedId] = useState<number | null>(null)\n\tconst [isClearing, setIsClearing] = useState(false)\n\n\tconst sessions = useLiveQuery<FormSession[]>(() => {\n\t\treturn db.formSessions\n\t\t\t.toCollection()\n\t\t\t.sortBy(\"updatedAt\")\n\t\t\t.then((all) => all.reverse())\n\t}, [])\n\n\tconst now = Date.now()\n\n\tconst formNames = useMemo(() => {\n\t\tif (!sessions) return []\n\t\treturn Array.from(new Set(sessions.map((s) => s.formName))).sort()\n\t}, [sessions])\n\n\tconst filtered = useMemo(() => {\n\t\tif (!sessions) return []\n\n\t\treturn sessions.filter((s) => {\n\t\t\tif (selectedForm !== \"all\" && s.formName !== selectedForm) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (onlySubmitted && !s.submitted) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (onlyErrors && (s.statusCode == null || s.statusCode < 400)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\t})\n\t}, [sessions, selectedForm, onlySubmitted, onlyErrors])\n\n\tconst handleDeleteSession = async (id: number) => {\n\t\ttry {\n\t\t\tawait db.formSessions.delete(id)\n\t\t} catch (error) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(\"Failed to delete form session\", error)\n\t\t}\n\t}\n\n\tconst handleClearAll = async () => {\n\t\tif (!sessions || sessions.length === 0) return\n\t\tsetIsClearing(true)\n\t\ttry {\n\t\t\tconst ids = sessions.map((s) => s.id)\n\t\t\tawait db.formSessions.bulkDelete(ids)\n\t\t} catch (error) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.error(\"Failed to clear form sessions\", error)\n\t\t} finally {\n\t\t\tsetIsClearing(false)\n\t\t}\n\t}\n\n\tconst total = sessions?.length ?? 0\n\n\tif (!sessions || sessions.length === 0) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={() => setOpen((v) => !v)}\n\t\t\t\t\tclassName=\"fs-devtools-toggle\"\n\t\t\t>\n\t\t\t\t\t<HistoryIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t<span>Form Snapshot Devtools</span>\n\t\t\t\t{total > 0 && (\n\t\t\t\t\t\t<span className=\"fs-devtools-toggle-count\">\n\t\t\t\t\t\t{total}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</button>\n\n\t\t\t{open && (\n\t\t\t\t<div className=\"fs-devtools-panel\">\n\t\t\t\t\t<div className=\"fs-devtools-header\">\n\t\t\t\t\t\t<div className=\"fs-devtools-header-left\">\n\t\t\t\t\t\t\t<HistoryIcon style={{ width: 14, height: 14 }} />\n\t\t\t\t\t\t\t<div className=\"fs-devtools-header-text\">\n\t\t\t\t\t\t\t\t<span className=\"fs-devtools-header-title\">\n\t\t\t\t\t\t\t\t\tForm Snapshot Devtools\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span className=\"fs-devtools-header-subtitle\">\n\t\t\t\t\t\t\t\t\t{filtered.length} / {total} session\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"fs-devtools-header-actions\">\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={handleClearAll}\n\t\t\t\t\t\t\t\tclassName=\"fs-devtools-clear\"\n\t\t\t\t\t\t\t\tdisabled={isClearing || !sessions || sessions.length === 0}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tClear all\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => setOpen(false)}\n\t\t\t\t\t\t\t\tclassName=\"fs-devtools-close\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<XIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"fs-devtools-filters\">\n\t\t\t\t\t\t<FilterIcon style={{ width: 12, height: 12 }} />\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tclassName=\"fs-devtools-select\"\n\t\t\t\t\t\t\tvalue={selectedForm}\n\t\t\t\t\t\t\tonChange={(e) => setSelectedForm(e.target.value)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"all\">All forms</option>\n\t\t\t\t\t\t\t{formNames.map((name) => (\n\t\t\t\t\t\t\t\t<option key={name} value={name}>\n\t\t\t\t\t\t\t\t\t{name}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t\t<label className=\"fs-devtools-filter-label\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={onlySubmitted}\n\t\t\t\t\t\t\t\tonChange={(e) => setOnlySubmitted(e.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span>submitted</span>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<label className=\"fs-devtools-filter-label\">\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\tchecked={onlyErrors}\n\t\t\t\t\t\t\t\tonChange={(e) => setOnlyErrors(e.target.checked)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<span>errors</span>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"fs-devtools-body\">\n\t\t\t\t\t\t{filtered.length === 0 ? (\n\t\t\t\t\t\t\t<p className=\"fs-devtools-empty\">\n\t\t\t\t\t\t\t\tNo sessions match the selected filters.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<ul className=\"fs-devtools-list\">\n\t\t\t\t\t\t\t\t{filtered.map((s) => (\n\t\t\t\t\t\t\t\t\t<li\n\t\t\t\t\t\t\t\t\t\tkey={s.id}\n\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-item\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() =>\n\t\t\t\t\t\t\t\t\t\t\t\tsetExpandedId((current) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tcurrent === s.id ? null : s.id,\n\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-item-toggle\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-main\">\n\t\t\t\t\t\t\t\t\t\t\t\t<ChevronDownIcon\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: 12,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: 12,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttransform:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\texpandedId === s.id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"rotate(0deg)\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"rotate(-90deg)\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttransition: \"transform 120ms ease-out\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-text\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-item-tags\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-tag\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.formName}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-id\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t#{s.id}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-meta\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span>{formatDateShort(s.updatedAt)}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span>· {formatAge(now - s.updatedAt)} ago</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.submitted ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-badge fs-devtools-badge-success\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsubmitted\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-badge fs-devtools-badge-warn\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tin progress\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{s.statusCode != null && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"fs-devtools-status\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstatus {s.statusCode}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"fs-devtools-delete\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\te.stopPropagation()\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvoid handleDeleteSession(s.id)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tDelete\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t{typeof s.errorMessage === \"string\" && s.errorMessage && (\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-error\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{s.errorMessage}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t\t\t\t{expandedId === s.id && (\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"fs-devtools-snapshot\">\n\t\t\t\t\t\t\t\t\t\t\t\t<FormSnapshotTable\n\t\t\t\t\t\t\t\t\t\t\t\t\tdata={s.data}\n\t\t\t\t\t\t\t\t\t\t\t\t\temptyMessage=\"This session does not contain any field data yet.\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</>\n\t)\n}\n\ntype IconProps = React.SVGProps<SVGSVGElement>\n\nfunction HistoryIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<path d=\"M3 3v6h6\" />\n\t\t\t<path d=\"M3.05 13A9 9 0 1 0 9 3.05\" />\n\t\t\t<path d=\"M12 7v5l3 3\" />\n\t\t</svg>\n\t)\n}\n\nfunction XIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" />\n\t\t\t<line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n\t\t</svg>\n\t)\n}\n\nfunction FilterIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\" />\n\t\t</svg>\n\t)\n}\n\nfunction ChevronDownIcon(props: IconProps) {\n\treturn (\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth=\"2\"\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t\t{...props}\n\t\t>\n\t\t\t<polyline points=\"6 9 12 15 18 9\" />\n\t\t</svg>\n\t)\n}\n\n","import { useMemo } from \"react\"\nimport { ensureFormSnapshotsStyles } from \"./styles\"\n\ntype Primitive = string | number | boolean | null | undefined\n\nfunction isPrimitive(val: unknown): val is Primitive {\n\treturn (\n\t\tval === null ||\n\t\tval === undefined ||\n\t\ttypeof val === \"string\" ||\n\t\ttypeof val === \"number\" ||\n\t\ttypeof val === \"boolean\"\n\t)\n}\n\nexport interface FormSnapshotTableProps {\n\t/**\n\t * JSON-stringified snapshot. Genellikle `FormSession.data`.\n\t */\n\tdata: string\n\t/**\n\t * Message to display when the table is empty.\n\t * Default: \"No data captured yet.\"\n\t */\n\temptyMessage?: string\n\t/**\n\t * Extra class names for the outer wrapper.\n\t */\n\tclassName?: string\n}\n\ntype ParsedSnapshot = {\n\tsnapshot: Record<string, unknown>\n\tfieldCount: number\n}\n\nconst snapshotCache = new Map<string, ParsedSnapshot>()\n\nfunction parseSnapshot(data: string): ParsedSnapshot {\n\tconst cached = snapshotCache.get(data)\n\tif (cached) return cached\n\n\tlet snapshot: Record<string, unknown> = {}\n\ttry {\n\t\tsnapshot = JSON.parse(data) as Record<string, unknown>\n\t} catch {\n\t\tsnapshot = {}\n\t}\n\n\tconst parsed = {\n\t\tsnapshot,\n\t\tfieldCount: Object.keys(snapshot).length,\n\t}\n\n\tsnapshotCache.set(data, parsed)\n\treturn parsed\n}\n\nexport function FormSnapshotTable({\n\tdata,\n\temptyMessage = \"No data captured yet.\",\n\tclassName,\n}: Readonly<FormSnapshotTableProps>) {\n\tensureFormSnapshotsStyles()\n\n\tconst { snapshot, fieldCount } = useMemo(() => parseSnapshot(data), [data])\n\n\tif (fieldCount === 0) {\n\t\treturn (\n\t\t\t<p className=\"fs-snapshot-empty\">\n\t\t\t\t{emptyMessage}\n\t\t\t</p>\n\t\t)\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tclassName={[\n\t\t\t\t\"fs-snapshot-wrapper\",\n\t\t\t\tclassName,\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \")}\n\t\t>\n\t\t\t<table className=\"fs-snapshot-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr className=\"fs-snapshot-header-row\">\n\t\t\t\t\t\t<th className=\"fs-snapshot-header-cell\">\n\t\t\t\t\t\t\tField\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th className=\"fs-snapshot-header-cell\">\n\t\t\t\t\t\t\tValue\n\t\t\t\t\t\t</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{Object.entries(snapshot).map(([key, value]) => (\n\t\t\t\t\t\t<tr\n\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\tclassName=\"fs-snapshot-row\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<td className=\"fs-snapshot-key\">\n\t\t\t\t\t\t\t\t{key}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td className=\"fs-snapshot-value\">\n\t\t\t\t\t\t\t\t<SnapshotValueCell value={value} />\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t)\n}\n\nfunction SnapshotValueCell({ value }: Readonly<{ value: unknown }>) {\n\tif (isPrimitive(value)) {\n\t\tif (value === null || value === undefined || value === \"\") {\n\t\t\treturn (\n\t\t\t\t<span className=\"fs-snapshot-muted\">\n\t\t\t\t\t—\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\tif (typeof value === \"boolean\") {\n\t\t\treturn (\n\t\t\t\t<span\n\t\t\t\t\tclassName={\n\t\t\t\t\t\tvalue\n\t\t\t\t\t\t\t? \"fs-snapshot-badge fs-snapshot-badge-true\"\n\t\t\t\t\t\t\t: \"fs-snapshot-badge fs-snapshot-badge-false\"\n\t\t\t\t\t}\n\t\t\t\t>\n\t\t\t\t\t{String(value)}\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\treturn <span className=\"fs-snapshot-text\">{String(value)}</span>\n\t}\n\n\tif (Array.isArray(value)) {\n\t\tif (value.length === 0) {\n\t\t\treturn (\n\t\t\t\t<span className=\"fs-snapshot-muted\">\n\t\t\t\t\t—\n\t\t\t\t</span>\n\t\t\t)\n\t\t}\n\n\t\treturn (\n\t\t\t<div className=\"fs-snapshot-array\">\n\t\t\t\t{value.map((v, idx) => (\n\t\t\t\t\t// eslint-disable-next-line react/no-array-index-key\n\t\t\t\t\t<span\n\t\t\t\t\t\tkey={idx}\n\t\t\t\t\t\tclassName=\"fs-snapshot-chip\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{String(v)}\n\t\t\t\t\t</span>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t)\n\t}\n\n\treturn (\n\t\t<pre className=\"fs-snapshot-object\">\n\t\t\t{JSON.stringify(value, null, 2)}\n\t\t</pre>\n\t)\n}\n\n","const STYLE_ID = \"form-snapshots-styles\"\n\nconst SNAPSHOT_TABLE_CSS = `:root {\n /* light – neutral-like palette */\n --fs-st-border: #e5e5e5; /* neutral-200 */\n --fs-st-border-soft: #d4d4d4; /* neutral-300 */\n --fs-st-header-bg: #f5f5f5; /* neutral-100 */\n --fs-st-key-text: #737373; /* neutral-500 */\n --fs-st-muted-text: #737373; /* neutral-500 */\n --fs-st-muted-strong: #a3a3a3; /* neutral-400 */\n --fs-st-chip-bg: #e5e5e5; /* neutral-200 */\n --fs-st-chip-text: #171717; /* neutral-900 */\n --fs-st-object-bg: #f5f5f5; /* neutral-100 */\n --fs-st-badge-true-bg: rgba(22, 163, 74, 0.1);\n --fs-st-badge-true-text: #166534;\n --fs-st-badge-false-border: #d4d4d4;\n --fs-st-badge-false-text: #525252; /* neutral-600 */\n --fs-st-text-main: #171717; /* neutral-900 */\n}\n\nhtml.dark {\n /* dark – neutral-900/950 style */\n --fs-st-border: #404040; /* neutral-700 */\n --fs-st-border-soft: #262626; /* neutral-800 */\n --fs-st-header-bg: #171717; /* neutral-900 */\n --fs-st-key-text: #a3a3a3; /* neutral-400 */\n --fs-st-muted-text: #a3a3a3; /* neutral-400 */\n --fs-st-muted-strong: #737373; /* neutral-500 */\n --fs-st-chip-bg: #262626; /* neutral-800 */\n --fs-st-chip-text: #f5f5f5; /* neutral-100 */\n --fs-st-object-bg: #0a0a0a; /* neutral-950ish */\n --fs-st-badge-true-bg: rgba(22, 163, 74, 0.25);\n --fs-st-badge-true-text: #bbf7d0;\n --fs-st-badge-false-border: #404040;\n --fs-st-badge-false-text: #e5e5e5; /* neutral-200 */\n --fs-st-text-main: #f5f5f5; /* neutral-100 */\n}\n.fs-snapshot-wrapper {\n border-radius: 6px;\n border: 1px solid var(--fs-st-border);\n overflow: hidden;\n color: var(--fs-st-text-main);\n}\n\n.fs-snapshot-table {\n width: 100%;\n font-size: 12px;\n border-collapse: collapse;\n}\n\n.fs-snapshot-header-row {\n border-bottom: 1px solid var(--fs-st-border);\n background: var(--fs-st-header-bg);\n}\n\n.fs-snapshot-header-cell {\n padding: 4px 6px;\n text-align: left;\n font-weight: 500;\n color: var(--fs-st-muted-text);\n}\n\n.fs-snapshot-row {\n border-bottom: 1px solid var(--fs-st-border-soft);\n}\n\n.fs-snapshot-row:last-child {\n border-bottom: none;\n}\n\n.fs-snapshot-key {\n padding: 4px 6px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,\n \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 10px;\n color: var(--fs-st-key-text);\n vertical-align: top;\n}\n\n.fs-snapshot-value {\n padding: 4px 6px;\n vertical-align: top;\n}\n\n.fs-snapshot-empty {\n font-size: 12px;\n color: var(--fs-st-muted-text);\n font-style: italic;\n}\n\n.fs-snapshot-muted {\n color: var(--fs-st-muted-strong);\n font-style: italic;\n}\n\n.fs-snapshot-text {\n word-break: break-all;\n}\n\n.fs-snapshot-array {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n}\n\n.fs-snapshot-chip {\n background: var(--fs-st-chip-bg);\n color: var(--fs-st-chip-text);\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 999px;\n}\n\n.fs-snapshot-object {\n max-height: 128px;\n overflow: auto;\n border-radius: 4px;\n background: var(--fs-st-object-bg);\n padding: 4px;\n font-size: 10px;\n}\n\n.fs-snapshot-badge {\n display: inline-flex;\n align-items: center;\n border-radius: 3px;\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.fs-snapshot-badge-true {\n background: var(--fs-st-badge-true-bg);\n color: var(--fs-st-badge-true-text);\n}\n\n.fs-snapshot-badge-false {\n background: transparent;\n color: var(--fs-st-badge-false-text);\n border: 1px solid var(--fs-st-badge-false-border);\n}\n`\n\nconst DEVTOOLS_CSS = `:root {\n /* light mode – Tailwind neutral-ish */\n --fs-dev-bg-surface: rgba(250, 250, 250, 0.95); /* neutral-50 */\n --fs-dev-bg-panel: rgba(250, 250, 250, 0.98); /* neutral-50 */\n --fs-dev-bg-header: #f5f5f5; /* neutral-100 */\n --fs-dev-bg-header-muted: #f5f5f5;\n --fs-dev-bg-toggle-count: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-close-hover: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-tag: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-status: #e5e5e5; /* neutral-200 */\n --fs-dev-bg-snapshot: #f5f5f5; /* neutral-100 */\n\n --fs-dev-border-strong: #e5e5e5; /* neutral-200 */\n --fs-dev-border-subtle: #e5e5e5; /* neutral-200 */\n --fs-dev-border-soft: #d4d4d4; /* neutral-300 */\n --fs-dev-border-snapshot: #d4d4d4; /* neutral-300 */\n\n --fs-dev-text-primary: #171717; /* neutral-900 */\n --fs-dev-text-muted: #737373; /* neutral-500 */\n --fs-dev-text-soft: #525252; /* neutral-600 */\n --fs-dev-text-error: #b91c1c;\n\n --fs-dev-badge-success-bg: rgba(22, 163, 74, 0.1);\n --fs-dev-badge-success-text: #166534;\n --fs-dev-badge-warn-bg: rgba(217, 119, 6, 0.12);\n --fs-dev-badge-warn-text: #92400e;\n\n --fs-dev-shadow-toggle: 0 1px 3px rgba(0, 0, 0, 0.08);\n --fs-dev-shadow-toggle-hover: 0 2px 6px rgba(0, 0, 0, 0.12);\n --fs-dev-shadow-panel: 0 8px 24px rgba(0, 0, 0, 0.12);\n}\n\nhtml.dark .fs-color-scheme-root:root,\nhtml.dark {\n /* dark mode – neutral-900/950 style */\n --fs-dev-bg-surface: rgba(23, 23, 23, 0.95); /* neutral-900 */\n --fs-dev-bg-panel: rgba(23, 23, 23, 0.98); /* neutral-900 */\n --fs-dev-bg-header: #171717; /* neutral-900 */\n --fs-dev-bg-header-muted: #171717;\n --fs-dev-bg-toggle-count: #262626; /* neutral-800 */\n --fs-dev-bg-close-hover: #404040; /* neutral-700 */\n --fs-dev-bg-tag: #262626; /* neutral-800 */\n --fs-dev-bg-status: #171717; /* neutral-900 */\n --fs-dev-bg-snapshot: #0a0a0a; /* neutral-950ish */\n\n --fs-dev-border-strong: #404040; /* neutral-700 */\n --fs-dev-border-subtle: #262626; /* neutral-800 */\n --fs-dev-border-soft: #262626; /* neutral-800 */\n --fs-dev-border-snapshot: #404040; /* neutral-700 */\n\n --fs-dev-text-primary: #fafafa; /* neutral-50 */\n --fs-dev-text-muted: #a3a3a3; /* neutral-400 */\n --fs-dev-text-soft: #d4d4d4; /* neutral-300 */\n --fs-dev-text-error: #fecaca;\n\n --fs-dev-badge-success-bg: rgba(22, 163, 74, 0.25);\n --fs-dev-badge-success-text: #bbf7d0;\n --fs-dev-badge-warn-bg: rgba(217, 119, 6, 0.3);\n --fs-dev-badge-warn-text: #fed7aa;\n\n --fs-dev-shadow-toggle: 0 1px 3px rgba(0, 0, 0, 0.6);\n --fs-dev-shadow-toggle-hover: 0 2px 6px rgba(0, 0, 0, 0.7);\n --fs-dev-shadow-panel: 0 20px 40px rgba(0, 0, 0, 0.8);\n}\n.fs-devtools-toggle {\n position: fixed;\n bottom: 16px;\n right: 16px;\n z-index: 9999;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n border-radius: 999px;\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-surface);\n padding: 4px 8px;\n font-size: 12px;\n color: var(--fs-dev-text-soft);\n box-shadow: var(--fs-dev-shadow-toggle);\n cursor: pointer;\n}\n\n.fs-devtools-toggle:hover {\n box-shadow: var(--fs-dev-shadow-toggle-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-toggle-count {\n margin-left: 4px;\n border-radius: 999px;\n background: var(--fs-dev-bg-toggle-count);\n padding: 0 4px;\n font-size: 10px;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-panel {\n position: fixed;\n bottom: 56px;\n overflow: hidden;\n right: 16px;\n z-index: 9999;\n width: 420px;\n max-height: 60vh;\n border-radius: 12px;\n color: var(--fs-dev-text-primary);\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-panel);\n box-shadow: var(--fs-dev-shadow-panel);\n display: flex;\n flex-direction: column;\n font-size: 14px;\n}\n\n.fs-devtools-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-subtle);\n background: var(--fs-dev-bg-header);\n}\n\n.fs-devtools-header-left {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.fs-devtools-header-text {\n display: flex;\n flex-direction: column;\n}\n\n.fs-devtools-header-title {\n font-size: 12px;\n font-weight: 600;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-header-subtitle {\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-header-actions {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.fs-devtools-clear {\n border-radius: 999px;\n padding: 2px 8px;\n font-size: 11px;\n border: 1px solid var(--fs-dev-border-subtle);\n background: transparent;\n color: var(--fs-dev-text-muted);\n cursor: pointer;\n}\n\n.fs-devtools-clear:hover:enabled {\n background: var(--fs-dev-bg-close-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-clear:disabled {\n opacity: 0.5;\n cursor: default;\n}\n\n.fs-devtools-close {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 999px;\n padding: 4px;\n background: transparent;\n border: none;\n cursor: pointer;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-close:hover {\n background: var(--fs-dev-bg-close-hover);\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-filters {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-subtle);\n}\n\n.fs-devtools-select {\n flex: 1;\n border-radius: 4px;\n border: 1px solid var(--fs-dev-border-strong);\n background: var(--fs-dev-bg-panel);\n padding: 2px 6px;\n font-size: 12px;\n}\n\n.fs-devtools-filter-label {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-body {\n flex: 1;\n overflow: auto;\n}\n\n.fs-devtools-empty {\n padding: 10px 12px;\n font-size: 12px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-list {\n list-style: none;\n margin: 0;\n padding: 0;\n border-top: 1px solid var(--fs-dev-border-soft);\n}\n\n.fs-devtools-item {\n padding: 6px 8px;\n border-bottom: 1px solid var(--fs-dev-border-soft);\n}\n\n.fs-devtools-item-toggle {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n background: transparent;\n border: none;\n padding: 0;\n cursor: pointer;\n text-align: left;\n}\n\n.fs-devtools-item-main {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n min-width: 0;\n}\n\n.fs-devtools-item-text {\n min-width: 0;\n}\n\n.fs-devtools-item-tags {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.fs-devtools-tag {\n display: inline-flex;\n align-items: center;\n border-radius: 999px;\n background: var(--fs-dev-bg-tag);\n padding: 2px 6px;\n font-size: 10px;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n \"Courier New\", monospace;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-id {\n font-size: 10px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-meta {\n margin-top: 2px;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 11px;\n color: var(--fs-dev-text-muted);\n}\n\n.fs-devtools-delete {\n border: none;\n background: transparent;\n padding: 0;\n font-size: 11px;\n color: var(--fs-dev-text-soft);\n cursor: pointer;\n}\n\n.fs-devtools-delete:hover {\n text-decoration: underline;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-badge {\n border-radius: 999px;\n padding: 2px 6px;\n font-size: 10px;\n}\n\n.fs-devtools-badge-success {\n background: var(--fs-dev-badge-success-bg);\n color: var(--fs-dev-badge-success-text);\n}\n\n.fs-devtools-badge-warn {\n background: var(--fs-dev-badge-warn-bg);\n color: var(--fs-dev-badge-warn-text);\n}\n\n.fs-devtools-status {\n border-radius: 999px;\n background: var(--fs-dev-bg-status);\n padding: 2px 6px;\n font-size: 10px;\n color: var(--fs-dev-text-primary);\n}\n\n.fs-devtools-error {\n margin-left: 8px;\n max-width: 40%;\n font-size: 11px;\n color: var(--fs-dev-text-error);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.fs-devtools-snapshot {\n margin-top: 6px;\n border-radius: 6px;\n background: var(--fs-dev-bg-snapshot);\n font-size: 12px;\n}\n`\n\nexport function ensureFormSnapshotsStyles() {\n\tif (typeof document === \"undefined\") return\n\tif (document.getElementById(STYLE_ID)) return\n\n\tconst style = document.createElement(\"style\")\n\tstyle.id = STYLE_ID\n\tstyle.type = \"text/css\"\n\tstyle.appendChild(\n\t\tdocument.createTextNode(`${SNAPSHOT_TABLE_CSS}\\n\\n${DEVTOOLS_CSS}`),\n\t)\n\t;(document.head || document.documentElement).appendChild(style)\n}\n\n","import { useFormSnapshots, type FormSnapshotsOptions } from \"./hooks/use-form-snapshots\"\n\ntype WithoutSnapshotOverrides = Omit<\n\tFormSnapshotsOptions,\n\t\"getValues\" | \"applyValues\"\n>\n\nexport interface RHFSnapshotsOptions extends WithoutSnapshotOverrides {}\n\n/**\n * Minimal subset of a React Hook Form instance that this library needs.\n * Kept structural so consumers are not forced to install `react-hook-form`.\n */\nexport interface RHFFormLike<TFieldValues extends Record<string, unknown>> {\n\tgetValues: () => TFieldValues\n\treset: (values: TFieldValues) => void\n}\n\nexport function useRHFFormSnapshots<TFieldValues extends Record<string, unknown>>(\n\tformName: string,\n\tform: RHFFormLike<TFieldValues>,\n\toptions?: RHFSnapshotsOptions,\n) {\n\treturn useFormSnapshots(formName, {\n\t\t...options,\n\t\tgetValues: () => form.getValues() as Record<string, unknown>,\n\t\tapplyValues: (snap) => form.reset(snap as TFieldValues),\n\t})\n}\n\nexport interface ObjectStateSnapshotsOptions\n\textends WithoutSnapshotOverrides {}\n\nexport function useObjectFormSnapshots<TState extends Record<string, unknown>>(\n\tformName: string,\n\tstate: TState,\n\tsetState: (next: TState) => void,\n\toptions?: ObjectStateSnapshotsOptions,\n) {\n\treturn useFormSnapshots(formName, {\n\t\t...options,\n\t\tgetValues: () => state as Record<string, unknown>,\n\t\tapplyValues: (snap) => setState(snap as TState),\n\t})\n}\n\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4I3B,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4Vd,SAAS,4BAA4B;AAC3C,MAAI,OAAO,aAA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","names":["useMemo","useState","useLiveQuery","jsx","jsx","jsxs","useState","useLiveQuery","useMemo"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-snapshots",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "React + Dexie based form autosave & history snapshots library.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -27,15 +27,14 @@
27
27
  "react-dom": ">=18"
28
28
  },
29
29
  "dependencies": {
30
- "dexie": "^4.0.8",
31
- "dexie-react-hooks": "^1.1.7",
32
- "react-hook-form": "^7.54.0"
30
+ "dexie": "^4.3.0",
31
+ "dexie-react-hooks": "^4.2.0"
33
32
  },
34
33
  "devDependencies": {
35
- "@types/react": "^18.3.0",
36
- "@types/react-dom": "^18.3.0",
37
- "tsup": "^8.0.0",
38
- "typescript": "^5.6.0"
34
+ "@types/react": "^18.3.28",
35
+ "@types/react-dom": "^18.3.7",
36
+ "tsup": "^8.5.1",
37
+ "typescript": "^5.9.3"
39
38
  },
40
39
  "sideEffects": false,
41
40
  "author": "",