@embeddables/forms 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","names":[],"sources":["../src/react/use-form-store.ts","../src/react/forms-registry.ts","../src/react/use-forms.ts","../src/react/use-form.ts","../src/react/use-form-errors.ts","../src/react/use-form-field.ts","../src/react/use-form-file-upload.ts","../src/react/resolve-core-analytics.ts","../src/react/register-forms-client.ts","../src/react/forms-module.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\n\nimport type { FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport type FormSnapshot<TSchema extends FormSchema> = Readonly<{\n values: Partial<FormValues<TSchema>>\n errors: FieldErrors<TSchema>\n}>\n\nconst EMPTY_SNAPSHOT: FormSnapshot<FormSchema> = Object.freeze({\n values: {},\n errors: {},\n})\n\nfunction createSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema>,\n): FormSnapshot<TSchema> {\n return Object.freeze({\n values: form.getAll(),\n errors: form.errors(),\n })\n}\n\ninterface CachedSnapshot<TSchema extends FormSchema> {\n readonly form: FormInstance<TSchema>\n readonly snapshot: FormSnapshot<TSchema>\n}\n\nexport function useFormSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FormSnapshot<TSchema> {\n const cachedRef = useRef<CachedSnapshot<TSchema> | null>(null)\n const emptyRef = useRef<FormSnapshot<TSchema>>(EMPTY_SNAPSHOT as FormSnapshot<TSchema>)\n\n const subscribe = useCallback(\n (listener: () => void) => {\n if (form === null) return () => undefined\n return form.subscribe(() => {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n listener()\n })\n },\n [form],\n )\n\n const getSnapshot = useCallback((): FormSnapshot<TSchema> => {\n if (form === null) {\n // * Otherwise a later A -> null -> mutate A -> A sequence would resurrect\n // * a snapshot cached before the detach, missing the mutation in between.\n cachedRef.current = null\n return emptyRef.current\n }\n // * A cached snapshot from a previous form instance must not survive a\n // * form change — recompute so switching forms without a mutation still\n // * returns the new form's values and errors.\n if (cachedRef.current === null || cachedRef.current.form !== form) {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n }\n return cachedRef.current.snapshot\n }, [form])\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n","import type { FormSchemaMap, FormsClient } from '../core/form.js'\n\nexport const FORMS_MODULE_KEY = 'forms'\n\n/**\n * Idempotence guard for `registerFormsClient` only — React reads the client\n * from provider context, not from here.\n */\nexport const formsByCore = new WeakMap<object, FormsClient<FormSchemaMap>>()\n\nexport function getRegisteredFormsClient(core: object): FormsClient<FormSchemaMap> | undefined {\n return formsByCore.get(core)\n}\n","import { useEmbeddablesModule } from '@embeddables/core/react'\n\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\n\nimport type { FormSchemaMap, FormsClient, InitFormsOptions } from '../core/form.js'\n\nexport type FormsReactOptions<TSchemas extends FormSchemaMap = FormSchemaMap> = Omit<\n InitFormsOptions<TSchemas>,\n 'core'\n>\n\n/** @internal Product hooks read the module client from provider context. */\nexport function useRegisteredFormsClient<\n TSchemas extends FormSchemaMap = FormSchemaMap,\n>(): FormsClient<TSchemas> | null {\n return useEmbeddablesModule<FormsClient<TSchemas>>({ key: FORMS_MODULE_KEY })\n}\n","import { useFormSnapshot } from './use-form-store.js'\nimport { useRegisteredFormsClient } from './use-forms.js'\n\nimport type { FormInstance, FormSchemaMap } from '../core/form.js'\nimport type { RegisteredSchemas } from './schema-registry.js'\n\n/**\n * Reactive binding for one declared form.\n *\n * Takes no type arguments: `TFormId` is inferred from `formId`, and the schema\n * map comes from the registry `em build` augments. Pass both explicitly\n * (`useForm<'signup', OtherSchemas>`) only to work against a map other than the\n * registered one.\n */\nexport function useForm<\n TFormId extends keyof TSchemas & string,\n TSchemas extends FormSchemaMap = RegisteredSchemas,\n>({\n formId,\n}: {\n formId: TFormId\n}): {\n form: FormInstance<TSchemas[TFormId]> | null\n values: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['values']\n errors: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['errors']\n} {\n const client = useRegisteredFormsClient<TSchemas>()\n\n // * Safe to call on every render: the client returns one instance per form id.\n const form = client === null ? null : client.getForm({ formId })\n const { values, errors } = useFormSnapshot(form)\n\n return { form, values, errors }\n}\n","import { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormSchema } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport function useFormErrors<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FieldErrors<TSchema> {\n const { errors } = useFormSnapshot(form)\n return errors\n}\n","import { useCallback, useEffect, useState } from 'react'\n\nimport { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormFieldKey, FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance, SetResult } from '../core/form.js'\n\nconst noopSetValue = async (): Promise<SetResult<FormSchema>> => ({\n ok: false,\n errors: {},\n})\n\nexport function useFormField<const TSchema extends FormSchema, K extends FormFieldKey<TSchema>>({\n form,\n key,\n}: {\n form: FormInstance<TSchema> | null\n key: K\n}): {\n value: FormValues<TSchema>[K] | undefined\n error: readonly string[] | undefined\n setValue: (value: FormValues<TSchema>[K] | undefined) => void\n commit: (value: FormValues<TSchema>[K] | undefined) => Promise<SetResult<TSchema>>\n onBlur: () => Promise<SetResult<TSchema>>\n} {\n const { values, errors } = useFormSnapshot(form)\n const committed = values[key]\n const [draft, setDraft] = useState<FormValues<TSchema>[K] | undefined>(committed)\n\n useEffect(() => {\n setDraft(committed)\n }, [form, key, committed])\n\n const setValue = useCallback((value: FormValues<TSchema>[K] | undefined) => {\n setDraft(value)\n }, [])\n\n // * Takes the value as an argument rather than reading `draft`, so it is\n // * correct when called in the same tick as the change that produced it.\n // * `onBlur` cannot be: it compares the `draft`/`committed` pair captured in\n // * the current render, which a same-tick `setValue` has not updated yet.\n const commit = useCallback(\n (value: FormValues<TSchema>[K] | undefined): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetValue()\n // * Keeps the control responsive while `set` runs, and keeps a rejected\n // * value on screen next to its error instead of snapping back.\n setDraft(value)\n return form.set({ [key]: value } as Partial<FormValues<TSchema>>)\n },\n [form, key],\n )\n\n const onBlur = useCallback(async (): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetValue()\n if (draft === committed) {\n return { ok: true, errors: {} as FieldErrors<TSchema> }\n }\n return form.set({ [key]: draft } as Partial<FormValues<TSchema>>)\n }, [committed, draft, form, key])\n\n if (form === null) {\n return {\n value: undefined,\n error: undefined,\n setValue: () => undefined,\n commit: async () => noopSetValue(),\n onBlur: async () => noopSetValue(),\n }\n }\n\n return {\n value: draft,\n error: errors[key],\n setValue,\n commit,\n onBlur,\n }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\n\nimport { FormsError } from '../errors.js'\nimport { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance, SetResult } from '../core/form.js'\nimport type { ChangeEvent } from 'react'\n\n/** Keys declared with `type: file` on a schema. */\nexport type FormFileFieldKey<TSchema extends FormSchema> = Extract<\n TSchema['fields'][number],\n { type: 'file' }\n>['key']\n\nconst noopSetResult = async <TSchema extends FormSchema>(): Promise<SetResult<TSchema>> => ({\n ok: false,\n errors: {} as FieldErrors<TSchema>,\n})\n\n/**\n * Reactive binding for a `type: file` field: uploads through the public API,\n * commits the returned `FormFileRef`, and exposes a native `<input type=\"file\">`\n * handler.\n */\nexport function useFormFileUpload<\n const TSchema extends FormSchema,\n K extends FormFileFieldKey<TSchema>,\n>({\n form,\n key,\n}: {\n form: FormInstance<TSchema> | null\n key: K\n}): {\n value: FormValues<TSchema>[K] | undefined\n error: readonly string[] | undefined\n isLoading: boolean\n uploadError: string | undefined\n upload: (file: File) => Promise<SetResult<TSchema>>\n clear: () => Promise<SetResult<TSchema>>\n inputProps: {\n type: 'file'\n disabled: boolean\n onChange: (event: ChangeEvent<HTMLInputElement>) => void\n }\n} {\n const { values, errors } = useFormSnapshot(form)\n const [isLoading, setIsLoading] = useState(false)\n const [uploadError, setUploadError] = useState<string | undefined>(undefined)\n const uploadGenerationRef = useRef(0)\n\n useEffect(() => {\n return () => {\n uploadGenerationRef.current += 1\n setIsLoading(false)\n setUploadError(undefined)\n }\n }, [form, key])\n\n const upload = useCallback(\n async (file: File): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetResult<TSchema>()\n\n const generation = ++uploadGenerationRef.current\n setIsLoading(true)\n setUploadError(undefined)\n try {\n const ref = await form.uploadFile({ key, file })\n if (generation !== uploadGenerationRef.current) {\n return { ok: false, errors: {} as FieldErrors<TSchema> }\n }\n return await form.set({ [key]: ref } as Partial<FormValues<TSchema>>)\n } catch (error) {\n if (generation !== uploadGenerationRef.current) {\n return { ok: false, errors: {} as FieldErrors<TSchema> }\n }\n const message = error instanceof FormsError ? error.message : 'File upload failed.'\n setUploadError(message)\n return { ok: false, errors: {} as FieldErrors<TSchema> }\n } finally {\n if (generation === uploadGenerationRef.current) {\n setIsLoading(false)\n }\n }\n },\n [form, key],\n )\n\n const clear = useCallback(async (): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetResult<TSchema>()\n uploadGenerationRef.current += 1\n setIsLoading(false)\n setUploadError(undefined)\n return form.set({ [key]: null } as Partial<FormValues<TSchema>>)\n }, [form, key])\n\n const onChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0]\n if (file === undefined) return\n void upload(file)\n // * Lets the user pick the same file again after a failed upload.\n event.target.value = ''\n },\n [upload],\n )\n\n if (form === null) {\n return {\n value: undefined,\n error: undefined,\n isLoading: false,\n uploadError: undefined,\n upload: async () => noopSetResult<TSchema>(),\n clear: async () => noopSetResult<TSchema>(),\n inputProps: {\n type: 'file',\n disabled: true,\n onChange: () => undefined,\n },\n }\n }\n\n return {\n value: values[key],\n error: errors[key],\n isLoading,\n uploadError,\n upload,\n clear,\n inputProps: {\n type: 'file',\n disabled: isLoading,\n onChange,\n },\n }\n}\n","import type { AnalyticsInstance } from '@embeddables/core'\nimport type { EmbeddablesInstance } from '@embeddables/core'\n\nexport function resolveCoreAnalyticsInstance(\n core: EmbeddablesInstance,\n): AnalyticsInstance | undefined {\n if (typeof core.getAnalyticsInstance !== 'function') return undefined\n return core.getAnalyticsInstance() ?? undefined\n}\n","import { initForms } from '../core/form.js'\nimport { formsByCore } from './forms-registry.js'\nimport { resolveCoreAnalyticsInstance } from './resolve-core-analytics.js'\n\nimport type { FormsClient, InitFormsOptions } from '../core/form.js'\nimport type { RegisteredSchemas } from './schema-registry.js'\n\ninterface RegisterFormsModuleClientOptions extends InitFormsOptions<RegisteredSchemas> {\n /**\n * Set when `excludeAnalyticsInModules` names Forms. The exclusion is absolute:\n * it suppresses the Core-instance fallback as well as the injected client.\n */\n disableAnalyticsFallback?: boolean\n}\n\nfunction register({\n disableAnalyticsFallback,\n ...options\n}: RegisterFormsModuleClientOptions): FormsClient<RegisteredSchemas> {\n const { core } = options\n const existing = formsByCore.get(core)\n if (existing !== undefined) return existing\n\n const analyticsInstance = disableAnalyticsFallback\n ? undefined\n : (options.analyticsInstance ?? resolveCoreAnalyticsInstance(core))\n\n const client = initForms<RegisteredSchemas>({ ...options, core, analyticsInstance })\n formsByCore.set(core, client)\n return client\n}\n\nexport function registerFormsClient(\n options: InitFormsOptions<RegisteredSchemas>,\n): FormsClient<RegisteredSchemas> {\n return register(options)\n}\n\nexport function registerFormsModuleClient(\n options: RegisterFormsModuleClientOptions,\n): FormsClient<RegisteredSchemas> {\n return register(options)\n}\n","import type { EmbeddablesReactModule } from '@embeddables/core/react'\n\nimport { FormsError } from '../errors.js'\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\nimport { registerFormsModuleClient } from './register-forms-client.js'\n\nimport type { AnalyticsInstance } from '../core/analytics.js'\nimport type { FormsClient } from '../core/form.js'\nimport type { RegisteredSchemas } from './schema-registry.js'\nimport type { FormsReactOptions } from './use-forms.js'\n\nexport type { FormsReactOptions } from './use-forms.js'\n\n/**\n * What `EmbeddablesProvider` accepts under its top-level `forms` prop.\n *\n * `analyticsInstance` is deliberately absent: the provider injects the\n * Analytics module client through module dependencies, so an app never wires\n * that instance by hand.\n */\nexport type FormsProviderOptions = Omit<FormsReactOptions<RegisteredSchemas>, 'analyticsInstance'>\n\ndeclare module '@embeddables/core/react' {\n interface EmbeddablesReactModuleParameters {\n readonly forms: FormsProviderOptions\n }\n}\n\nconst ANALYTICS_MODULE_KEY = 'analytics'\n\nfunction resolveInjectedAnalytics(value: unknown): AnalyticsInstance | undefined {\n if (value === undefined) return undefined\n if (typeof value !== 'object' || value === null) {\n throw new FormsError('Forms received an invalid Analytics module dependency.')\n }\n const candidate = value as Partial<AnalyticsInstance>\n if (\n typeof candidate.trackEvent !== 'function' ||\n typeof candidate.getAppUserId !== 'function' ||\n typeof candidate.getProjectId !== 'function'\n ) {\n throw new FormsError('Forms received an invalid Analytics module dependency.')\n }\n return candidate as AnalyticsInstance\n}\n\nexport function forms(\n options: FormsReactOptions<RegisteredSchemas> = {},\n): EmbeddablesReactModule<FormsClient<RegisteredSchemas>, FormsProviderOptions> {\n return {\n key: FORMS_MODULE_KEY,\n dependencies: [{ key: ANALYTICS_MODULE_KEY, optional: true }],\n init: (core, context) => {\n const analyticsInstance = context?.analyticsExcluded\n ? undefined\n : (options.analyticsInstance ??\n resolveInjectedAnalytics(context?.dependencies.get(ANALYTICS_MODULE_KEY)))\n\n return registerFormsModuleClient({\n core,\n customValidations: context?.parameters?.customValidations ?? options.customValidations,\n serverFormData: context?.parameters?.serverFormData ?? options.serverFormData,\n analyticsInstance,\n disableAnalyticsFallback: context?.analyticsExcluded,\n })\n },\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"react.js","names":[],"sources":["../src/react/use-form-store.ts","../src/react/forms-registry.ts","../src/react/use-forms.ts","../src/react/use-form.ts","../src/react/use-form-errors.ts","../src/react/use-form-field.ts","../src/react/use-form-file-upload.ts","../src/react/resolve-core-analytics.ts","../src/react/register-forms-client.ts","../src/react/forms-module.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react'\n\nimport type { FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport type FormSnapshot<TSchema extends FormSchema> = Readonly<{\n values: Partial<FormValues<TSchema>>\n errors: FieldErrors<TSchema>\n}>\n\nconst EMPTY_SNAPSHOT: FormSnapshot<FormSchema> = Object.freeze({\n values: {},\n errors: {},\n})\n\nfunction createSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema>,\n): FormSnapshot<TSchema> {\n return Object.freeze({\n values: form.getAll(),\n errors: form.errors(),\n })\n}\n\ninterface CachedSnapshot<TSchema extends FormSchema> {\n readonly form: FormInstance<TSchema>\n readonly snapshot: FormSnapshot<TSchema>\n}\n\nexport function useFormSnapshot<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FormSnapshot<TSchema> {\n const cachedRef = useRef<CachedSnapshot<TSchema> | null>(null)\n const emptyRef = useRef<FormSnapshot<TSchema>>(EMPTY_SNAPSHOT as FormSnapshot<TSchema>)\n\n const subscribe = useCallback(\n (listener: () => void) => {\n if (form === null) return () => undefined\n return form.subscribe(() => {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n listener()\n })\n },\n [form],\n )\n\n const getSnapshot = useCallback((): FormSnapshot<TSchema> => {\n if (form === null) {\n // * Otherwise a later A -> null -> mutate A -> A sequence would resurrect\n // * a snapshot cached before the detach, missing the mutation in between.\n cachedRef.current = null\n return emptyRef.current\n }\n // * A cached snapshot from a previous form instance must not survive a\n // * form change — recompute so switching forms without a mutation still\n // * returns the new form's values and errors.\n if (cachedRef.current === null || cachedRef.current.form !== form) {\n cachedRef.current = { form, snapshot: createSnapshot(form) }\n }\n return cachedRef.current.snapshot\n }, [form])\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)\n}\n","import type { FormSchemaMap, FormsClient } from '../core/form.js'\n\nexport const FORMS_MODULE_KEY = 'forms'\n\n/**\n * Idempotence guard for `registerFormsClient` only — React reads the client\n * from provider context, not from here.\n */\nexport const formsByCore = new WeakMap<object, FormsClient<FormSchemaMap>>()\n\nexport function getRegisteredFormsClient(core: object): FormsClient<FormSchemaMap> | undefined {\n return formsByCore.get(core)\n}\n","import { useEmbeddablesModule } from '@embeddables/core/react'\n\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\n\nimport type { FormSchemaMap, FormsClient, InitFormsOptions } from '../core/form.js'\n\nexport type FormsReactOptions<TSchemas extends FormSchemaMap = FormSchemaMap> = Omit<\n InitFormsOptions<TSchemas>,\n 'core'\n>\n\n/** @internal Product hooks read the module client from provider context. */\nexport function useRegisteredFormsClient<\n TSchemas extends FormSchemaMap = FormSchemaMap,\n>(): FormsClient<TSchemas> | null {\n return useEmbeddablesModule<FormsClient<TSchemas>>({ key: FORMS_MODULE_KEY })\n}\n","import { useFormSnapshot } from './use-form-store.js'\nimport { useRegisteredFormsClient } from './use-forms.js'\n\nimport type { FormInstance, FormSchemaMap } from '../core/form.js'\nimport type { RegisteredSchemas } from './schema-registry.js'\n\n/**\n * Reactive binding for one declared form.\n *\n * Takes no type arguments: `TFormId` is inferred from `formId`, and the schema\n * map comes from the registry `em build` augments. Pass both explicitly\n * (`useForm<'signup', OtherSchemas>`) only to work against a map other than the\n * registered one.\n */\nexport function useForm<\n TFormId extends keyof TSchemas & string,\n TSchemas extends FormSchemaMap = RegisteredSchemas,\n>({\n formId,\n}: {\n formId: TFormId\n}): {\n form: FormInstance<TSchemas[TFormId]> | null\n values: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['values']\n errors: ReturnType<typeof useFormSnapshot<TSchemas[TFormId]>>['errors']\n} {\n const client = useRegisteredFormsClient<TSchemas>()\n\n // * Safe to call on every render: the client returns one instance per form id.\n const form = client === null ? null : client.getForm({ formId })\n const { values, errors } = useFormSnapshot(form)\n\n return { form, values, errors }\n}\n","import { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormSchema } from '../core/config.js'\nimport type { FieldErrors, FormInstance } from '../core/form.js'\n\nexport function useFormErrors<TSchema extends FormSchema>(\n form: FormInstance<TSchema> | null,\n): FieldErrors<TSchema> {\n const { errors } = useFormSnapshot(form)\n return errors\n}\n","import { useCallback, useEffect, useState } from 'react'\n\nimport { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormFieldKey, FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance, SetResult } from '../core/form.js'\n\nconst noopSetValue = async (): Promise<SetResult<FormSchema>> => ({\n ok: false,\n errors: {},\n})\n\nexport function useFormField<const TSchema extends FormSchema, K extends FormFieldKey<TSchema>>({\n form,\n key,\n}: {\n form: FormInstance<TSchema> | null\n key: K\n}): {\n value: FormValues<TSchema>[K] | undefined\n error: readonly string[] | undefined\n setValue: (value: FormValues<TSchema>[K] | undefined) => void\n commit: (value: FormValues<TSchema>[K] | undefined) => Promise<SetResult<TSchema>>\n onBlur: () => Promise<SetResult<TSchema>>\n} {\n const { values, errors } = useFormSnapshot(form)\n const committed = values[key]\n const [draft, setDraft] = useState<FormValues<TSchema>[K] | undefined>(committed)\n\n useEffect(() => {\n setDraft(committed)\n }, [form, key, committed])\n\n const setValue = useCallback((value: FormValues<TSchema>[K] | undefined) => {\n setDraft(value)\n }, [])\n\n // * Takes the value as an argument rather than reading `draft`, so it is\n // * correct when called in the same tick as the change that produced it.\n // * `onBlur` cannot be: it compares the `draft`/`committed` pair captured in\n // * the current render, which a same-tick `setValue` has not updated yet.\n const commit = useCallback(\n (value: FormValues<TSchema>[K] | undefined): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetValue()\n // * Keeps the control responsive while `set` runs, and keeps a rejected\n // * value on screen next to its error instead of snapping back.\n setDraft(value)\n return form.set({ [key]: value } as Partial<FormValues<TSchema>>)\n },\n [form, key],\n )\n\n const onBlur = useCallback(async (): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetValue()\n if (draft === committed) {\n return { ok: true, errors: {} as FieldErrors<TSchema> }\n }\n return form.set({ [key]: draft } as Partial<FormValues<TSchema>>)\n }, [committed, draft, form, key])\n\n if (form === null) {\n return {\n value: undefined,\n error: undefined,\n setValue: () => undefined,\n commit: async () => noopSetValue(),\n onBlur: async () => noopSetValue(),\n }\n }\n\n return {\n value: draft,\n error: errors[key],\n setValue,\n commit,\n onBlur,\n }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\n\nimport { FormsError } from '../errors.js'\nimport { useFormSnapshot } from './use-form-store.js'\n\nimport type { FormSchema, FormValues } from '../core/config.js'\nimport type { FieldErrors, FormInstance, SetResult } from '../core/form.js'\nimport type { ChangeEvent } from 'react'\n\n/** Keys declared with `value_type: file` on a schema. */\nexport type FormFileFieldKey<TSchema extends FormSchema> = Extract<\n TSchema['fields'][number],\n { value_type: 'file' }\n>['key']\n\nconst noopSetResult = async <TSchema extends FormSchema>(): Promise<SetResult<TSchema>> => ({\n ok: false,\n errors: {} as FieldErrors<TSchema>,\n})\n\n/**\n * Reactive binding for a `type: file` field: uploads through the public API,\n * commits the returned `FormFileRef`, and exposes a native `<input type=\"file\">`\n * handler.\n */\nexport function useFormFileUpload<\n const TSchema extends FormSchema,\n K extends FormFileFieldKey<TSchema>,\n>({\n form,\n key,\n}: {\n form: FormInstance<TSchema> | null\n key: K\n}): {\n value: FormValues<TSchema>[K] | undefined\n error: readonly string[] | undefined\n isLoading: boolean\n uploadError: string | undefined\n upload: (file: File) => Promise<SetResult<TSchema>>\n clear: () => Promise<SetResult<TSchema>>\n inputProps: {\n type: 'file'\n disabled: boolean\n onChange: (event: ChangeEvent<HTMLInputElement>) => void\n }\n} {\n const { values, errors } = useFormSnapshot(form)\n const [isLoading, setIsLoading] = useState(false)\n const [uploadError, setUploadError] = useState<string | undefined>(undefined)\n const uploadGenerationRef = useRef(0)\n\n useEffect(() => {\n return () => {\n uploadGenerationRef.current += 1\n setIsLoading(false)\n setUploadError(undefined)\n }\n }, [form, key])\n\n const upload = useCallback(\n async (file: File): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetResult<TSchema>()\n\n const generation = ++uploadGenerationRef.current\n setIsLoading(true)\n setUploadError(undefined)\n try {\n const ref = await form.uploadFile({ key, file })\n if (generation !== uploadGenerationRef.current) {\n return { ok: false, errors: {} as FieldErrors<TSchema> }\n }\n return await form.set({ [key]: ref } as Partial<FormValues<TSchema>>)\n } catch (error) {\n if (generation !== uploadGenerationRef.current) {\n return { ok: false, errors: {} as FieldErrors<TSchema> }\n }\n const message = error instanceof FormsError ? error.message : 'File upload failed.'\n setUploadError(message)\n return { ok: false, errors: {} as FieldErrors<TSchema> }\n } finally {\n if (generation === uploadGenerationRef.current) {\n setIsLoading(false)\n }\n }\n },\n [form, key],\n )\n\n const clear = useCallback(async (): Promise<SetResult<TSchema>> => {\n if (form === null) return noopSetResult<TSchema>()\n uploadGenerationRef.current += 1\n setIsLoading(false)\n setUploadError(undefined)\n return form.set({ [key]: null } as Partial<FormValues<TSchema>>)\n }, [form, key])\n\n const onChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n const file = event.target.files?.[0]\n if (file === undefined) return\n void upload(file)\n // * Lets the user pick the same file again after a failed upload.\n event.target.value = ''\n },\n [upload],\n )\n\n if (form === null) {\n return {\n value: undefined,\n error: undefined,\n isLoading: false,\n uploadError: undefined,\n upload: async () => noopSetResult<TSchema>(),\n clear: async () => noopSetResult<TSchema>(),\n inputProps: {\n type: 'file',\n disabled: true,\n onChange: () => undefined,\n },\n }\n }\n\n return {\n value: values[key],\n error: errors[key],\n isLoading,\n uploadError,\n upload,\n clear,\n inputProps: {\n type: 'file',\n disabled: isLoading,\n onChange,\n },\n }\n}\n","import type { AnalyticsInstance } from '@embeddables/core'\nimport type { EmbeddablesInstance } from '@embeddables/core'\n\nexport function resolveCoreAnalyticsInstance(\n core: EmbeddablesInstance,\n): AnalyticsInstance | undefined {\n if (typeof core.getAnalyticsInstance !== 'function') return undefined\n return core.getAnalyticsInstance() ?? undefined\n}\n","import { initForms } from '../core/form.js'\nimport { formsByCore } from './forms-registry.js'\nimport { resolveCoreAnalyticsInstance } from './resolve-core-analytics.js'\n\nimport type { FormsClient, InitFormsOptions } from '../core/form.js'\nimport type { RegisteredSchemas } from './schema-registry.js'\n\ninterface RegisterFormsModuleClientOptions extends InitFormsOptions<RegisteredSchemas> {\n /**\n * Set when `excludeAnalyticsInModules` names Forms. The exclusion is absolute:\n * it suppresses the Core-instance fallback as well as the injected client.\n */\n disableAnalyticsFallback?: boolean\n}\n\nfunction register({\n disableAnalyticsFallback,\n ...options\n}: RegisterFormsModuleClientOptions): FormsClient<RegisteredSchemas> {\n const { core } = options\n const existing = formsByCore.get(core)\n if (existing !== undefined) return existing\n\n const analyticsInstance = disableAnalyticsFallback\n ? undefined\n : (options.analyticsInstance ?? resolveCoreAnalyticsInstance(core))\n\n const client = initForms<RegisteredSchemas>({ ...options, core, analyticsInstance })\n formsByCore.set(core, client)\n return client\n}\n\nexport function registerFormsClient(\n options: InitFormsOptions<RegisteredSchemas>,\n): FormsClient<RegisteredSchemas> {\n return register(options)\n}\n\nexport function registerFormsModuleClient(\n options: RegisterFormsModuleClientOptions,\n): FormsClient<RegisteredSchemas> {\n return register(options)\n}\n","import type { EmbeddablesReactModule } from '@embeddables/core/react'\n\nimport { FormsError } from '../errors.js'\nimport { FORMS_MODULE_KEY } from './forms-registry.js'\nimport { registerFormsModuleClient } from './register-forms-client.js'\n\nimport type { AnalyticsInstance } from '../core/analytics.js'\nimport type { FormsClient } from '../core/form.js'\nimport type { RegisteredSchemas } from './schema-registry.js'\nimport type { FormsReactOptions } from './use-forms.js'\n\nexport type { FormsReactOptions } from './use-forms.js'\n\n/**\n * What `EmbeddablesProvider` accepts under its top-level `forms` prop.\n *\n * `analyticsInstance` is deliberately absent: the provider injects the\n * Analytics module client through module dependencies, so an app never wires\n * that instance by hand.\n */\nexport type FormsProviderOptions = Omit<FormsReactOptions<RegisteredSchemas>, 'analyticsInstance'>\n\ndeclare module '@embeddables/core/react' {\n interface EmbeddablesReactModuleParameters {\n readonly forms: FormsProviderOptions\n }\n}\n\nconst ANALYTICS_MODULE_KEY = 'analytics'\n\nfunction resolveInjectedAnalytics(value: unknown): AnalyticsInstance | undefined {\n if (value === undefined) return undefined\n if (typeof value !== 'object' || value === null) {\n throw new FormsError('Forms received an invalid Analytics module dependency.')\n }\n const candidate = value as Partial<AnalyticsInstance>\n if (\n typeof candidate.trackEvent !== 'function' ||\n typeof candidate.getAppUserId !== 'function' ||\n typeof candidate.getProjectId !== 'function'\n ) {\n throw new FormsError('Forms received an invalid Analytics module dependency.')\n }\n return candidate as AnalyticsInstance\n}\n\nexport function forms(\n options: FormsReactOptions<RegisteredSchemas> = {},\n): EmbeddablesReactModule<FormsClient<RegisteredSchemas>, FormsProviderOptions> {\n return {\n key: FORMS_MODULE_KEY,\n dependencies: [{ key: ANALYTICS_MODULE_KEY, optional: true }],\n init: (core, context) => {\n const analyticsInstance = context?.analyticsExcluded\n ? undefined\n : (options.analyticsInstance ??\n resolveInjectedAnalytics(context?.dependencies.get(ANALYTICS_MODULE_KEY)))\n\n return registerFormsModuleClient({\n core,\n customValidations: context?.parameters?.customValidations ?? options.customValidations,\n serverFormData: context?.parameters?.serverFormData ?? options.serverFormData,\n analyticsInstance,\n disableAnalyticsFallback: context?.analyticsExcluded,\n })\n },\n }\n}\n"],"mappings":"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"}
package/dist/server.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_form = require("./form-CdIfHqNc.cjs");
2
+ const require_form = require("./form-lmr-O7j7.cjs");
3
3
  //#region src/core/server.ts
4
4
  function toServerFormInstance(instance) {
5
5
  return {
package/dist/server.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as FormSchemaMap, c as FormsServerInitOptions, d as ServerFormInstance, u as ServerFormDataByFormId } from "./form-x4EJcR9A.js";
1
+ import { a as FormSchemaMap, c as FormsServerInitOptions, d as ServerFormInstance, u as ServerFormDataByFormId } from "./form-DKPfOrTG.js";
2
2
  //#region src/core/server.d.ts
3
3
  interface FormsServerClient<TSchemas extends FormSchemaMap = FormSchemaMap> {
4
4
  getForm<K extends keyof TSchemas & string>(params: {
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
1
- import { i as seedServerFormsStorageFromCookies, o as createMemoryFormsStorage, r as createNoopPersistence, s as FormsError, t as createFormsClient } from "./form-7wmC3G_q.js";
1
+ import { i as seedServerFormsStorageFromCookies, o as createMemoryFormsStorage, r as createNoopPersistence, s as FormsError, t as createFormsClient } from "./form-CR5xJ_nQ.js";
2
2
  //#region src/core/server.ts
3
3
  function toServerFormInstance(instance) {
4
4
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embeddables/forms",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Schema-driven form state, local persistence, and analytics events for Embeddables funnels.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -44,7 +44,7 @@
44
44
  "hono": "^4.12.25"
45
45
  },
46
46
  "peerDependencies": {
47
- "@embeddables/core": ">=0.1.0",
47
+ "@embeddables/core": ">=0.2.0",
48
48
  "react": ">=18"
49
49
  },
50
50
  "devDependencies": {
@@ -57,8 +57,8 @@
57
57
  "tsdown": "^0.22.14",
58
58
  "typescript": "~6.0.2",
59
59
  "vitest": "^4.1.9",
60
- "@embeddables/core": "0.1.0",
61
60
  "@embeddables/shared-types": "1.0.0",
61
+ "@embeddables/core": "0.2.0",
62
62
  "backend-worker": "1.0.0"
63
63
  },
64
64
  "scripts": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"form-7wmC3G_q.js","names":["entries","FIELD_TYPES","isObjectLike","isStoredField","FIELD_TYPES","isObjectLike","schema","PUBLISHABLE_KEY_HEADER","items"],"sources":["../src/errors.ts","../src/types/form-file.ts","../src/core/validation.ts","../src/storage/storage.ts","../src/storage/cookie-form-data.ts","../src/storage/persistence-config.ts","../src/storage/persistence.ts","../src/storage/persistence-client.ts","../src/storage/upload-client.ts","../src/core/analytics.ts","../src/core/options.ts","../src/core/resolve.ts","../src/core/form.ts"],"sourcesContent":["/**\n * Typed error hierarchy. Every failure this SDK raises on its own behalf is an\n * instance of one of these, so consumers branch on the type\n * (`if (e instanceof SchemaError) …`) instead of string-matching messages.\n * Catch `FormsError` to handle them all.\n *\n * An error thrown by a consumer's own custom validator is never wrapped in one\n * of these — it propagates with its original type and stack.\n */\n\n/** Base class for every error the SDK throws. */\nexport class FormsError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options)\n this.name = 'FormsError'\n }\n}\n\n/** The schema is malformed. */\nexport class SchemaError extends FormsError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options)\n this.name = 'SchemaError'\n }\n}\n\n/** A custom validator returned a thenable, or a shape that is not a message. */\nexport class ValidatorError extends FormsError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options)\n this.name = 'ValidatorError'\n }\n}\n","import type { FormFileRef } from '@embeddables/shared-types'\n\nexport type { FormFileRef } from '@embeddables/shared-types'\n\nexport function contentTypeMatchesAccept({\n contentType,\n accept,\n}: {\n contentType: string\n accept: readonly string[]\n}): boolean {\n const normalized = contentType.split(';', 1)[0]?.trim().toLowerCase() || ''\n return accept.some((entry) => {\n const pattern = entry.trim().toLowerCase()\n if (pattern.endsWith('/*')) return normalized.startsWith(pattern.slice(0, -1))\n return normalized === pattern\n })\n}\n\nexport function isFormFileRef(value: unknown): value is FormFileRef {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n const record = value as Record<string, unknown>\n return (\n typeof record.file_id === 'string' &&\n typeof record.name === 'string' &&\n typeof record.content_type === 'string' &&\n typeof record.size === 'number' &&\n Number.isFinite(record.size) &&\n (record.status === 'uploading' || record.status === 'done' || record.status === 'error')\n )\n}\n","import type { JsonValue } from '@embeddables/shared-types/json'\n\nimport { ValidatorError } from '../errors.js'\nimport { contentTypeMatchesAccept, isFormFileRef } from '../types/form-file.js'\n\nimport type { FieldConfig, FieldType, FieldValidator } from './config.js'\n\n/** Runtime counterpart to `FieldType`. Exhaustive by construction. */\n// ! Must stay in lockstep with `ValueOfFieldType` in `src/config.ts`: the two\n// ! are the compile-time and runtime halves of one claim. Typing this as\n// ! `Record<FieldType, …>` is what makes a new field type a compile error\n// ! here; only the type tests catch a mismatch between the two.\nexport const FIELD_TYPE_PREDICATES: Record<FieldType, (value: JsonValue) => boolean> = {\n text: (value) => typeof value === 'string',\n email: (value) => typeof value === 'string',\n number: (value) => typeof value === 'number',\n boolean: (value) => typeof value === 'boolean',\n select: (value) => typeof value === 'string',\n multiselect: (value) => Array.isArray(value),\n json: () => true,\n file: (value) => value === null || isFormFileRef(value),\n}\n\n// * The WHATWG `input[type=email]` production, so a value the browser accepts in\n// * an email input is a value this accepts. It is deliberately narrower than\n// * RFC 5322 (no quoted local parts, no comments) and deliberately wider than\n// * \"must have a dot\": `user@localhost` and intranet hosts are valid. A form that\n// * needs a public TLD adds `pattern` on top.\nconst EMAIL_PATTERN =\n /^[\\w.!#$%&'*+/=?^`{|}~-]+@[a-zA-Z\\d](?:[a-zA-Z\\d-]{0,61}[a-zA-Z\\d])?(?:\\.[a-zA-Z\\d](?:[a-zA-Z\\d-]{0,61}[a-zA-Z\\d])?)*$/\n\n/**\n * Every message a single field's value earns. Empty means valid. Not generic:\n * the per-field types live at the instance boundary, and the cast down to\n * `JsonValue` happens once, in `initForm`.\n */\nexport function validateValue({\n field,\n value,\n values,\n pattern,\n validator,\n}: {\n field: FieldConfig\n value: JsonValue | undefined\n values: Readonly<Record<string, JsonValue>>\n pattern?: RegExp\n validator?: FieldValidator\n}): readonly string[] {\n const rules = field.validations\n const messages: string[] = []\n\n const isAbsent = value === undefined || value === null\n const isBlank = isAbsent || value === '' || (Array.isArray(value) && value.length === 0)\n if (rules?.required === true && isBlank) messages.push(`${field.label} is required`)\n\n // * An absent value earns no further message and never reaches the custom\n // * validator, whether or not it was required.\n if (isAbsent) return messages\n\n if (!FIELD_TYPE_PREDICATES[field.type](value))\n messages.push(\n `${field.label} expects ${/^[aeiou]/.test(field.type) ? 'an' : 'a'} ${field.type} value`,\n )\n\n if (typeof value === 'string') {\n // * Intrinsic to the declared type, so it runs before the author's own rules\n // * and cannot be switched off. That is why the blank case belongs to\n // * `required` alone: an author who wants an optional email cleared has no\n // * way to opt out of this check, so it must not claim `''` is malformed.\n if (field.type === 'email' && value !== '' && !EMAIL_PATTERN.test(value))\n messages.push(`${field.label} must be a valid email address`)\n if (rules?.minLength !== undefined && value.length < rules.minLength)\n messages.push(`${field.label} must be at least ${rules.minLength} characters`)\n if (rules?.maxLength !== undefined && value.length > rules.maxLength)\n messages.push(`${field.label} must be at most ${rules.maxLength} characters`)\n if (pattern && !pattern.test(value))\n messages.push(`${field.label} is not in the expected format`)\n }\n\n if (Array.isArray(value)) {\n if (rules?.minLength !== undefined && value.length < rules.minLength)\n messages.push(`${field.label} must have at least ${rules.minLength} items`)\n if (rules?.maxLength !== undefined && value.length > rules.maxLength)\n messages.push(`${field.label} must have at most ${rules.maxLength} items`)\n }\n\n if (typeof value === 'number') {\n if (rules?.min !== undefined && value < rules.min)\n messages.push(`${field.label} must be at least ${rules.min}`)\n if (rules?.max !== undefined && value > rules.max)\n messages.push(`${field.label} must be at most ${rules.max}`)\n }\n\n // * Membership in the field's declared choices. Plain string equality on both\n // * sides, not the canonical-JSON comparison `oneOf` uses: a `select` holds a\n // * string and a `multiselect` an array of them. The `select` branch reuses\n // * the `oneOf` wording so the rename reads as no change to that author; the\n // * `multiselect` branch is worded apart because it describes a per-entry\n // * failure `oneOf` could never express.\n if (field.type === 'select' || field.type === 'multiselect') {\n const options = field.options\n if (options) {\n const allowed = new Set(options.map((option) => option.value))\n\n if (field.type === 'select' && typeof value === 'string' && !allowed.has(value))\n messages.push(`${field.label} must be one of the allowed options`)\n\n if (field.type === 'multiselect' && Array.isArray(value)) {\n const entries: readonly JsonValue[] = value\n if (entries.some((entry) => typeof entry !== 'string' || !allowed.has(entry)))\n messages.push(`${field.label} has values that are not allowed options`)\n }\n }\n }\n\n if (rules?.oneOf) {\n // * Canonical JSON equality, so an object option matches whatever key order\n // * the stored value happens to carry, while arrays still compare\n // * positionally.\n const encoded = canonicalize(value)\n if (!rules.oneOf.some((option) => canonicalize(option) === encoded))\n messages.push(`${field.label} must be one of the allowed options`)\n }\n\n if (field.type === 'file' && isFormFileRef(value)) {\n if (value.status !== 'done') {\n messages.push(`${field.label} upload is not complete`)\n }\n if (rules?.maxSize !== undefined && value.size > rules.maxSize) {\n messages.push(`${field.label} must be at most ${rules.maxSize} bytes`)\n }\n if (\n rules?.accept !== undefined &&\n !contentTypeMatchesAccept({ contentType: value.content_type, accept: rules.accept })\n ) {\n messages.push(`${field.label} must be one of the allowed file types`)\n }\n }\n\n // ! The validator runs only on a present, type-correct value whose every\n // ! declarative rule passed. Those three conditions are what make the\n // ! declared `value` type honest: a validator body dereferences `value` with\n // ! no guard because the type says it can. Deleting or reordering this check\n // ! hands consumer code a value whose runtime type contradicts its declared\n // ! type, with no error anywhere.\n if (!validator || messages.length > 0) return messages\n\n return normalizeValidatorResult({\n // * Read as `unknown` because a JavaScript consumer, or a widened config,\n // * can return anything at all from here.\n result: validator({ value, values }),\n field,\n })\n}\n\nfunction normalizeValidatorResult({\n result,\n field,\n}: {\n result: unknown\n field: FieldConfig\n}): readonly string[] {\n if (isThenable(result))\n throw new ValidatorError(\n `Validator for \"${field.key}\" returned a promise; custom validators must be synchronous`,\n )\n if (result === null || result === undefined) return []\n if (typeof result === 'string') return [result]\n if (Array.isArray(result))\n return (result as unknown[]).filter((entry): entry is string => typeof entry === 'string')\n\n throw new ValidatorError(\n `Validator for \"${field.key}\" returned ${typeof result}; expected a string, an array of strings, or null`,\n )\n}\n\nfunction isThenable(value: unknown): boolean {\n return typeof (value as { then?: unknown } | null | undefined)?.then === 'function'\n}\n\n/**\n * JSON encoding with object keys sorted at every depth, so two values compare\n * by content rather than by insertion order.\n */\n// * A stored value reaches this through `JSON.parse` of the shared document, so\n// * its key order is whatever was written first, not the order the config\n// * author wrote the option in. Plain `JSON.stringify` equality would reject a\n// * `json` value that differs from its option only by key order.\nfunction canonicalize(value: JsonValue): string {\n const walk = (input: JsonValue): JsonValue => {\n if (Array.isArray(input)) return input.map(walk)\n if (input !== null && typeof input === 'object')\n return Object.fromEntries(\n Object.entries(input)\n .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))\n .map(([key, item]) => [key, walk(item)]),\n )\n return input\n }\n return JSON.stringify(walk(value))\n}\n","import type { JsonValue } from '@embeddables/shared-types/json'\n\nimport type { FieldConfig, FieldType } from '../core/config.js'\n\n/** Every form on the origin shares this one entry, indexed by form ID. */\nexport const FORM_DATA_KEY = 'EMBEDDABLES-FORM-DATA'\n\nexport interface FormsStorage {\n getItem(key: string): string | null\n setItem(key: string, value: string): void\n removeItem(key: string): void\n}\n\ninterface StoredField {\n value: JsonValue\n type: FieldType\n label: string\n registryId?: string\n protocolFieldId?: string\n}\n\n/** Internal. The whole document: form ID → field key → self-describing field. */\ntype StoredForm = Record<string, StoredField>\ntype FormsDocument = Record<string, StoredForm>\n\nconst FIELD_TYPES: readonly FieldType[] = [\n 'text',\n 'email',\n 'number',\n 'boolean',\n 'select',\n 'multiselect',\n 'json',\n 'file',\n]\n\n// * Keyed by storage object identity so two form instances that share one\n// * store share one parsed document. Entries die with the storage object —\n// * this is not a name-guessable registry.\nconst LIVE_DOCUMENTS = new WeakMap<FormsStorage, FormsDocument>()\n\nfunction readDocumentFromStorage({ storage }: { storage: FormsStorage }): FormsDocument {\n try {\n const raw = storage.getItem(FORM_DATA_KEY)\n if (raw === null) return {}\n\n const parsed: unknown = JSON.parse(raw)\n if (!isObjectLike(parsed)) return {}\n\n // * Returned as-is, with no per-form validation: this function's job is to\n // * hand back exactly what is stored so a write can preserve it. A sibling\n // * whose value is a string or `null` is carried through untouched.\n return parsed as FormsDocument\n } catch {\n return {}\n }\n}\n\n/**\n * Load once per storage object; later calls reuse the in-memory document.\n *\n * There is no invalidation: a write from another tab or a user clearing site\n * data is never picked up, and the next write here overwrites it. Recovering\n * from an external mutation means constructing a new storage object.\n */\nfunction loadDocument({ storage }: { storage: FormsStorage }): FormsDocument {\n const cached = LIVE_DOCUMENTS.get(storage)\n if (cached) return cached\n\n const document = readDocumentFromStorage({ storage })\n LIVE_DOCUMENTS.set(storage, document)\n return document\n}\n\nexport function resolveStorage({ storage }: { storage?: FormsStorage }): FormsStorage {\n if (storage) return storage\n\n try {\n const candidate = globalThis.localStorage\n // * Safari private mode throws on access rather than being absent, so\n // * presence alone is not a usable probe — a throwaway read is.\n candidate.getItem(FORM_DATA_KEY)\n return candidate\n } catch {\n return createMemoryFormsStorage()\n }\n}\n\n/**\n * A fresh in-memory shim seeded with the document as last read, for an instance\n * whose real storage started throwing mid-session.\n */\nexport function degradeToMemory({ storage }: { storage: FormsStorage }): FormsStorage {\n const shim = createMemoryFormsStorage()\n // * Prefer the live snapshot: a throwing `getItem` after a quota failure\n // * would otherwise seed an empty shim and drop every sibling this instance\n // * already had in memory.\n const document = LIVE_DOCUMENTS.get(storage) ?? readDocumentFromStorage({ storage })\n const snapshot: FormsDocument = { ...document }\n shim.setItem(FORM_DATA_KEY, JSON.stringify(snapshot))\n LIVE_DOCUMENTS.set(shim, snapshot)\n return shim\n}\n\nexport function readFields({\n storage,\n formKey,\n fieldDefinitions,\n}: {\n storage: FormsStorage\n formKey: string\n fieldDefinitions: readonly FieldConfig[]\n}): Record<string, JsonValue> {\n const bag: unknown = loadDocument({ storage })[formKey]\n if (!isObjectLike(bag)) return {}\n\n const declared = new Set(fieldDefinitions.map((field) => field.key))\n const values: Record<string, JsonValue> = {}\n for (const [key, entry] of Object.entries(bag)) {\n if (!declared.has(key)) continue\n if (isStoredField(entry)) values[key] = entry.value\n }\n return values\n}\n\nexport function writeFields({\n storage,\n formKey,\n fields,\n fieldDefinitions,\n}: {\n storage: FormsStorage\n formKey: string\n fields: Record<string, JsonValue>\n fieldDefinitions: readonly FieldConfig[]\n}): void {\n // * Replaces exactly one subtree and carries every other form key through\n // * verbatim, including keys this page has no config for. Merging *within* the\n // * bag is the caller's job. A throwing `setItem` propagates so the form\n // * instance can degrade and report. The live snapshot is updated only after\n // * `setItem` succeeds, so a quota failure leaves memory matching storage.\n // ! `fields` is copied rather than stored by reference: the caller keeps its\n // ! own handle on that object, and aliasing it into the cached document would\n // ! make a later mutation there visible to every instance on this storage\n // ! without a write.\n const current = loadDocument({ storage })\n const currentForm = isObjectLike(current[formKey]) ? current[formKey] : {}\n const declaredKeys = new Set(fieldDefinitions.map((field) => field.key))\n const nextForm: Record<string, unknown> = {}\n\n for (const [key, entry] of Object.entries(currentForm)) {\n if (!declaredKeys.has(key)) nextForm[key] = entry\n }\n\n for (const field of fieldDefinitions) {\n const value = fields[field.key]\n if (value === undefined) continue\n\n nextForm[field.key] = {\n value,\n type: field.type,\n label: field.label,\n ...(field.registryId === undefined ? {} : { registryId: field.registryId }),\n ...(field.protocolFieldId === undefined ? {} : { protocolFieldId: field.protocolFieldId }),\n } satisfies StoredField\n }\n\n const next = { ...current, [formKey]: nextForm as StoredForm }\n storage.setItem(FORM_DATA_KEY, JSON.stringify(next))\n LIVE_DOCUMENTS.set(storage, next)\n}\n\nexport function removeFields({\n storage,\n formKey,\n}: {\n storage: FormsStorage\n formKey: string\n}): void {\n const { [formKey]: _dropped, ...rest } = loadDocument({ storage })\n\n if (Object.keys(rest).length === 0) {\n // * A missing entry and a stored `{}` are indistinguishable to every\n // * reader, so releasing the entry is strictly better: a leftover key\n // * visible in devtools reads as data that was not deleted.\n storage.removeItem(FORM_DATA_KEY)\n LIVE_DOCUMENTS.set(storage, rest)\n return\n }\n storage.setItem(FORM_DATA_KEY, JSON.stringify(rest))\n LIVE_DOCUMENTS.set(storage, rest)\n}\n\n/**\n * Whether a value survives `JSON.stringify`. `undefined`, a function, and a\n * `Symbol` make it return `undefined`; a circular reference and a `BigInt` make\n * it throw. All five must be rejected before a write, because a value that\n * cannot stringify aborts a write carrying every form's data.\n */\nexport function isSerializable({ value }: { value: unknown }): boolean {\n try {\n return typeof JSON.stringify(value) === 'string'\n } catch {\n return false\n }\n}\n\n/** In-memory storage for server-side form init and tests that inject a store. */\nexport function createMemoryFormsStorage(): FormsStorage {\n // * Created per call and never module-level, because it holds user data.\n const entries = new Map<string, string>()\n\n return {\n getItem: (key) => entries.get(key) ?? null,\n setItem: (key, value) => {\n entries.set(key, value)\n },\n removeItem: (key) => {\n entries.delete(key)\n },\n }\n}\n\nfunction isObjectLike(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isStoredField(value: unknown): value is StoredField {\n if (!isObjectLike(value) || !Object.hasOwn(value, 'value')) return false\n if (!isSerializable({ value: value['value'] })) return false\n if (\n typeof value['type'] !== 'string' ||\n !FIELD_TYPES.includes(value['type'] as FieldType) ||\n typeof value['label'] !== 'string'\n )\n return false\n if (value['registryId'] !== undefined && typeof value['registryId'] !== 'string') return false\n if (value['protocolFieldId'] !== undefined && typeof value['protocolFieldId'] !== 'string')\n return false\n return true\n}\n","import type { JsonValue } from '@embeddables/shared-types/json'\n\nimport { FIELD_TYPE_PREDICATES } from '../core/validation.js'\nimport { FORM_DATA_KEY, isSerializable, type FormsStorage } from './storage.js'\n\nimport type { FieldConfig, FieldType, FormSchema } from '../core/config.js'\n\nexport interface StoredField {\n value: JsonValue\n type: FieldType\n label: string\n registryId?: string\n protocolFieldId?: string\n}\n\nexport type StoredFormCookiePayload = Record<string, StoredField>\n\nconst FIELD_TYPES: readonly FieldType[] = [\n 'text',\n 'email',\n 'number',\n 'boolean',\n 'select',\n 'multiselect',\n 'json',\n]\n\nexport function formatFormCookieDataKey({\n projectId,\n formId,\n}: {\n projectId: string\n formId: string\n}): string {\n return `EMBEDDABLES--${projectId}--COOKIES-FORM-DATA--${formId}`\n}\n\nexport function buildCookiePayloadFromBag({\n bag,\n fieldDefinitions,\n}: {\n bag: Record<string, JsonValue>\n fieldDefinitions: readonly FieldConfig[]\n}): StoredFormCookiePayload {\n const payload: StoredFormCookiePayload = {}\n\n for (const field of fieldDefinitions) {\n if (field.includeInCookies !== true) continue\n\n const value = bag[field.key]\n if (value === undefined) continue\n\n payload[field.key] = {\n value,\n type: field.type,\n label: field.label,\n ...(field.registryId === undefined ? {} : { registryId: field.registryId }),\n ...(field.protocolFieldId === undefined ? {} : { protocolFieldId: field.protocolFieldId }),\n }\n }\n\n return payload\n}\n\nfunction isObjectLike(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction isStoredField(value: unknown): value is StoredField {\n if (!isObjectLike(value) || !Object.hasOwn(value, 'value')) return false\n if (!isSerializable({ value: value['value'] })) return false\n if (\n typeof value['type'] !== 'string' ||\n !FIELD_TYPES.includes(value['type'] as FieldType) ||\n typeof value['label'] !== 'string'\n )\n return false\n if (value['registryId'] !== undefined && typeof value['registryId'] !== 'string') return false\n if (value['protocolFieldId'] !== undefined && typeof value['protocolFieldId'] !== 'string')\n return false\n return true\n}\n\nfunction entryMatchesFieldDefinition(entry: StoredField, field: FieldConfig): boolean {\n if (entry.type !== field.type || entry.label !== field.label) return false\n if ((entry.registryId ?? undefined) !== (field.registryId ?? undefined)) return false\n if ((entry.protocolFieldId ?? undefined) !== (field.protocolFieldId ?? undefined)) return false\n return FIELD_TYPE_PREDICATES[field.type](entry.value)\n}\n\nfunction serializeBrowserCookie(key: string, value: string): string {\n const locationRef = (globalThis as { location?: { protocol?: string } }).location\n const parts = [`${key}=${encodeURIComponent(value)}`, 'Path=/', 'SameSite=Lax']\n if (locationRef?.protocol === 'https:') parts.push('Secure')\n return parts.join('; ')\n}\n\nfunction writeBrowserCookie(key: string, value: string): void {\n const documentRef = (globalThis as { document?: { cookie: string } }).document\n if (!documentRef) return\n\n try {\n documentRef.cookie = serializeBrowserCookie(key, value)\n } catch {\n // * Host cookie jars can throw; form state already lives in localStorage.\n }\n}\n\nfunction clearBrowserCookie(key: string): void {\n const documentRef = (globalThis as { document?: { cookie: string } }).document\n if (!documentRef) return\n\n try {\n const locationRef = (globalThis as { location?: { protocol?: string } }).location\n const parts = [`${key}=`, 'Path=/', 'Max-Age=0', 'SameSite=Lax']\n if (locationRef?.protocol === 'https:') parts.push('Secure')\n documentRef.cookie = parts.join('; ')\n } catch {\n // * Host cookie jars can throw; form state is already cleared locally.\n }\n}\n\nexport function writeFormCookieData({\n projectId,\n formId,\n bag,\n fieldDefinitions,\n}: {\n projectId: string\n formId: string\n bag: Record<string, JsonValue>\n fieldDefinitions: readonly FieldConfig[]\n}): void {\n try {\n const payload = buildCookiePayloadFromBag({ bag, fieldDefinitions })\n writeBrowserCookie(formatFormCookieDataKey({ projectId, formId }), JSON.stringify(payload))\n } catch {\n // best-effort: a failing cookie write must not affect form.set()\n }\n}\n\nexport function clearFormCookieData({\n projectId,\n formId,\n}: {\n projectId: string\n formId: string\n}): void {\n try {\n clearBrowserCookie(formatFormCookieDataKey({ projectId, formId }))\n } catch {\n // best-effort: a failing cookie clear must not affect form.clear()\n }\n}\n\nexport function readFormCookieStoredForm({\n projectId,\n formId,\n getCookie,\n fieldDefinitions,\n}: {\n projectId: string\n formId: string\n getCookie: (key: string) => string | null\n fieldDefinitions: readonly FieldConfig[]\n}): StoredFormCookiePayload {\n try {\n const raw = getCookie(formatFormCookieDataKey({ projectId, formId }))\n if (raw === null || raw === '') return {}\n\n const decoded = decodeURIComponent(raw)\n const parsed: unknown = JSON.parse(decoded)\n if (!isObjectLike(parsed)) return {}\n\n const optedInFields = new Map(\n fieldDefinitions\n .filter((field) => field.includeInCookies === true)\n .map((field) => [field.key, field] as const),\n )\n const storedForm: StoredFormCookiePayload = {}\n\n for (const [key, entry] of Object.entries(parsed)) {\n const field = optedInFields.get(key)\n if (!field || !isStoredField(entry)) continue\n if (!entryMatchesFieldDefinition(entry, field)) continue\n storedForm[key] = entry\n }\n\n return storedForm\n } catch {\n return {}\n }\n}\n\nexport function readFormCookieData({\n projectId,\n formId,\n getCookie,\n fieldDefinitions,\n}: {\n projectId: string\n formId: string\n getCookie: (key: string) => string | null\n fieldDefinitions: readonly FieldConfig[]\n}): Record<string, JsonValue> {\n const storedForm = readFormCookieStoredForm({\n projectId,\n formId,\n getCookie,\n fieldDefinitions,\n })\n const values: Record<string, JsonValue> = {}\n for (const [key, entry] of Object.entries(storedForm)) {\n values[key] = entry.value\n }\n return values\n}\n\nexport function seedServerFormsStorageFromCookies({\n storage,\n projectId,\n formIds,\n getFormSchema,\n getCookie,\n}: {\n storage: FormsStorage\n projectId: string\n formIds: readonly string[]\n getFormSchema: (formId: string) => unknown\n getCookie: (key: string) => string | null\n}): void {\n const document: Record<string, StoredFormCookiePayload> = {}\n\n for (const formId of formIds) {\n const rawSchema = getFormSchema(formId)\n if (typeof rawSchema !== 'object' || rawSchema === null) continue\n\n const schema = rawSchema as FormSchema\n const storedForm = readFormCookieStoredForm({\n projectId,\n formId,\n getCookie,\n fieldDefinitions: schema.fields,\n })\n if (Object.keys(storedForm).length === 0) continue\n document[formId] = storedForm\n }\n\n if (Object.keys(document).length === 0) return\n\n storage.setItem(FORM_DATA_KEY, JSON.stringify(document))\n}\n","import { isValidPublishableKey } from '@embeddables/core'\nimport type { EmbeddablesInstance } from '@embeddables/core'\n\nexport const DEFAULT_BASE_URL = 'https://backend-worker.heysavvy.workers.dev'\nexport const DEFAULT_TIMEOUT_MS = 10_000\n\ndeclare const __EMBEDDABLES_API_URL_FOR_DEVELOPMENT__: string | undefined\n\nconst DEVELOPMENT_BASE_URL =\n typeof __EMBEDDABLES_API_URL_FOR_DEVELOPMENT__ === 'undefined'\n ? undefined\n : __EMBEDDABLES_API_URL_FOR_DEVELOPMENT__\n\nexport interface PersistenceClientConfig {\n core: EmbeddablesInstance\n /** Takes precedence over the key exposed by the core instance. */\n publishableKey?: string\n baseUrl?: string\n fetch?: typeof fetch\n timeoutMs?: number\n}\n\nexport interface ResolvedPersistenceConfig {\n core: EmbeddablesInstance\n projectId: string\n appUserId: string\n publishableKey: string\n baseUrl: string\n fetch: typeof fetch\n timeoutMs: number\n}\n\n/**\n * Returns null when persistence cannot be configured (missing publishable key\n * or fetch). The SDK keeps the no-op default in that case.\n */\nexport function resolvePersistenceConfig(\n config: PersistenceClientConfig,\n): ResolvedPersistenceConfig | null {\n const core = config.core\n const publishableKey = config.publishableKey ?? core.getPublishableKey()\n if (!publishableKey || !isValidPublishableKey({ value: publishableKey })) {\n return null\n }\n\n const projectId = core.getProjectId()\n const appUserId = core.getAppUserId()\n if (!projectId || !appUserId) {\n return null\n }\n\n const fetchImpl =\n config.fetch ??\n (typeof globalThis.fetch === 'function' ? globalThis.fetch.bind(globalThis) : undefined)\n if (!fetchImpl) {\n return null\n }\n\n return {\n core,\n projectId,\n appUserId,\n publishableKey,\n baseUrl: config.baseUrl ?? DEVELOPMENT_BASE_URL ?? DEFAULT_BASE_URL,\n fetch: fetchImpl,\n timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n }\n}\n","import type { JsonValue } from '@embeddables/shared-types/json'\n\n// * Provisional payload shapes. The real R2 key scheme and the Supabase table\n// * (and therefore these argument shapes) are blocked on the Jeremy meeting.\n// * Everything here is a drop-in behind this port: when the table lands, the\n// * only edits are a real implementation plus, if the table forces it, these\n// * shapes and their call sites in form.ts together.\n\n/** A changed field plus the registry/protocol ids the persistence layer keys on. */\nexport interface PersistedField {\n readonly key: string\n readonly value: JsonValue\n readonly registryId?: string\n readonly protocolFieldId?: string\n}\n\n/** A field whose stored value the persistence layer may be asked to recover. */\nexport interface RecoverableField {\n readonly key: string\n readonly registryId?: string\n readonly protocolFieldId?: string\n}\n\n/**\n * The seam between the SDK and durable storage (R2 for raw form data, Supabase\n * for the queryable fields table). Every method is best-effort: a form must\n * work with the no-op mock, and a real implementation that throws or rejects\n * must never break `set` / `submit` / `initForm`.\n */\nexport interface FormsPersistence {\n /** Partial save to R2 on every successful `set`. */\n savePartial(args: { formKey: string; values: Record<string, JsonValue> }): void | Promise<void>\n /** Per-field save to the Supabase table on every successful `set`. */\n saveFields(args: { formKey: string; fields: readonly PersistedField[] }): void | Promise<void>\n /** Full submission save to R2 on `submit`. */\n saveSubmission(args: { formKey: string; values: Record<string, JsonValue> }): void | Promise<void>\n /**\n * Cross-form recovery from Supabase, consulted at `initForm` only for\n * registry/protocol fields absent from `localStorage`. localStorage always\n * wins; the returned map is merged only for still-absent keys.\n */\n recoverRegistryFields(args: {\n formKey: string\n fields: readonly RecoverableField[]\n }): Record<string, JsonValue> | Promise<Record<string, JsonValue>>\n}\n\n/**\n * The default: does nothing, never throws, and recovers nothing. With this in\n * place a form is pure local state, exactly as before the port existed.\n */\nexport function createNoopPersistence(): FormsPersistence {\n return {\n savePartial: () => undefined,\n saveFields: () => undefined,\n saveSubmission: () => undefined,\n recoverRegistryFields: () => ({}),\n }\n}\n","import { hc } from 'hono/client'\n\nimport type { FormsApiErrorCode } from '@embeddables/shared-types'\nimport type { ProblemBody } from '@embeddables/shared-types/errors'\n\nimport { resolvePersistenceConfig } from './persistence-config.js'\nimport { createNoopPersistence } from './persistence.js'\n\nimport type { PersistenceClientConfig, ResolvedPersistenceConfig } from './persistence-config.js'\nimport type { FormsPersistence } from './persistence.js'\nimport type { FormsAppType } from 'backend-worker'\nimport type { ClientResponse } from 'hono/client'\n\nconst PUBLISHABLE_KEY_HEADER = 'x-publishable-key'\n\ntype FormsRpc = ReturnType<typeof hc<FormsAppType>>\nconst hcWithType = (...args: Parameters<typeof hc>): FormsRpc => hc<FormsAppType>(...args)\n\ntype SuccessBody<R extends ClientResponse<unknown, number, string>> =\n R extends ClientResponse<infer T, infer _S, infer _F> ? T : never\n\nfunction withTimeout(fetchImpl: typeof fetch, timeoutMs: number): typeof fetch {\n return async (input, init) => {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeoutMs)\n const path = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url\n\n try {\n return await fetchImpl(input, { ...init, signal: controller.signal })\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request to ${path} timed out after ${timeoutMs}ms`, { cause: error })\n }\n throw new Error(`Request to ${path} failed to reach the API`, { cause: error })\n } finally {\n clearTimeout(timer)\n }\n }\n}\n\nasync function rpcCall<R extends ClientResponse<unknown, number, string>>(\n fn: () => Promise<R>,\n): Promise<SuccessBody<R>> {\n const res = await fn()\n if (!res.ok) {\n const body: unknown = await res.json().catch(() => null)\n const problem = body as ProblemBody<FormsApiErrorCode> | null\n const message = problem?.detail ?? problem?.title ?? `forms persistence failed: ${res.status}`\n throw new Error(message)\n }\n return res.json() as Promise<SuccessBody<R>>\n}\n\nexport function createApiPersistence(config: ResolvedPersistenceConfig): FormsPersistence {\n const root = config.baseUrl.replace(/\\/+$/, '')\n const rpc = hcWithType(`${root}/forms`, {\n fetch: withTimeout(config.fetch, config.timeoutMs),\n headers: { [PUBLISHABLE_KEY_HEADER]: config.publishableKey },\n })\n\n return {\n savePartial({ formKey, values }) {\n return rpcCall(() =>\n rpc.v1.public.sessions.$post({\n json: {\n project_id: config.projectId,\n app_user_id: config.appUserId,\n form_id: formKey,\n data: values,\n },\n }),\n ).then(() => undefined)\n },\n saveFields: () => undefined,\n saveSubmission({ formKey, values }) {\n return rpcCall(() =>\n rpc.v1.public.submissions.$post({\n json: {\n project_id: config.projectId,\n app_user_id: config.appUserId,\n form_id: formKey,\n values,\n },\n }),\n ).then(() => undefined)\n },\n recoverRegistryFields: () => ({}),\n }\n}\n\nexport function resolveDefaultPersistence(config: PersistenceClientConfig): FormsPersistence {\n const resolved = resolvePersistenceConfig(config)\n if (!resolved) return createNoopPersistence()\n return createApiPersistence(resolved)\n}\n","import type { FormsApiErrorCode } from '@embeddables/shared-types'\nimport type { UploadFormFileResult } from '@embeddables/shared-types'\nimport type { ProblemBody } from '@embeddables/shared-types/errors'\n\nimport { FormsError } from '../errors.js'\n\nimport type { FormFileRef } from '../types/form-file.js'\nimport type { ResolvedPersistenceConfig } from './persistence-config.js'\n\nconst PUBLISHABLE_KEY_HEADER = 'x-publishable-key'\n\n/** Matches backend `FORM_UPLOAD_MAX_BYTES` (25 MiB). */\nexport const FORM_UPLOAD_MAX_BYTES = 26_214_400\n\nexport function mapUploadErrorMessage(\n problem: ProblemBody<FormsApiErrorCode> | null,\n status: number,\n): string {\n const code = problem?.code\n if (code === 'validation.file_too_large') {\n return problem?.detail ?? 'Maximum upload size is 25 MiB.'\n }\n if (code === 'validation.unsupported_content_type') {\n return problem?.detail ?? 'Unsupported file type.'\n }\n if (code === 'validation.upload_failed') {\n return problem?.detail ?? 'Upload could not be completed.'\n }\n if (code === 'service.form_uploads_not_configured') {\n return problem?.detail ?? 'File uploads are not configured for this project.'\n }\n if (code === 'validation.failed') {\n return problem?.detail ?? 'Upload metadata is invalid.'\n }\n return problem?.detail ?? problem?.title ?? `File upload failed (${status}).`\n}\n\nfunction uploadResultToFormFileRef(result: UploadFormFileResult): FormFileRef {\n return {\n file_id: result.file_id,\n name: result.name,\n content_type: result.content_type,\n size: result.size,\n status: 'done',\n uploaded_at: result.uploaded_at,\n }\n}\n\nfunction toUploadFetchError(error: unknown, path: string, timeoutMs: number): FormsError {\n if (error instanceof FormsError) return error\n if (error instanceof Error && error.name === 'AbortError') {\n return new FormsError(`Request to ${path} timed out after ${timeoutMs}ms`, { cause: error })\n }\n return new FormsError(`Request to ${path} failed to reach the API`, { cause: error })\n}\n\nfunction toUploadDecodeError(error: unknown, path: string): FormsError {\n return new FormsError(`Upload response from ${path} could not be decoded`, { cause: error })\n}\n\nexport async function uploadFormFile(\n config: ResolvedPersistenceConfig,\n input: {\n formId: string\n fieldKey: string\n file: Blob\n fileName?: string\n },\n): Promise<FormFileRef> {\n const root = config.baseUrl.replace(/\\/+$/, '')\n const url = `${root}/forms/v1/public/uploads`\n const formData = new FormData()\n formData.append('project_id', config.projectId)\n formData.append('app_user_id', config.appUserId)\n formData.append('form_id', input.formId)\n formData.append('field_key', input.fieldKey)\n\n const fileName =\n input.fileName ??\n (typeof File !== 'undefined' && input.file instanceof File ? input.file.name : 'upload')\n formData.append('file', input.file, fileName)\n\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), config.timeoutMs)\n\n try {\n const res = await config.fetch(url, {\n method: 'POST',\n headers: { [PUBLISHABLE_KEY_HEADER]: config.publishableKey },\n body: formData,\n signal: controller.signal,\n })\n // * Timeout covers fetch only. Aborting after headers arrive can fail the\n // body read and look like a transport error even when the API returned 201.\n clearTimeout(timer)\n\n if (!res.ok) {\n const body: unknown = await res.json().catch(() => null)\n const problem = body as ProblemBody<FormsApiErrorCode> | null\n throw new FormsError(mapUploadErrorMessage(problem, res.status))\n }\n\n try {\n const result = (await res.json()) as UploadFormFileResult\n return uploadResultToFormFileRef(result)\n } catch (error) {\n throw toUploadDecodeError(error, url)\n }\n } catch (error) {\n throw toUploadFetchError(error, url, config.timeoutMs)\n } finally {\n clearTimeout(timer)\n }\n}\n","import type {\n DataUpdatedEvent,\n FieldUpdatedEvent,\n FieldUpdatedType,\n FormSubmittedEvent,\n} from '@embeddables/shared-types/analytics-ingest'\nimport type { JsonValue } from '@embeddables/shared-types/json'\n\nimport type { FieldConfig, FieldType } from './config.js'\n\nexport type {\n AnalyticsInstance,\n AnalyticsTrackEvent,\n AnalyticsTrackResult,\n} from '@embeddables/shared-types/analytics-instance'\nexport type {\n DataUpdatedEvent,\n FieldUpdatedEvent,\n FieldUpdatedType,\n FormSubmittedEvent,\n} from '@embeddables/shared-types/analytics-ingest'\n\n/** The ingest bound on a `data:updated` entry's `value`. */\nconst MAX_VALUE_LENGTH = 1024\n\n/** The ingest bound on a `data:updated` entry's `label`. */\nconst MAX_LABEL_LENGTH = 256\n\nexport type FormsAnalyticsEvent = DataUpdatedEvent | FieldUpdatedEvent | FormSubmittedEvent\n\n/** Maps a form field's declared type to the analytics `field:updated` class. */\nexport function mapFieldUpdatedType(type: FieldType): FieldUpdatedType {\n return type\n}\n\n/**\n * Stringifies values for `data:updated` entries. `field:updated` carries the\n * raw `field_value`; only the batch event caps and stringifies for ingest.\n */\nexport function formatFieldValue({ value }: { value: JsonValue; field?: FieldConfig }): string {\n // * A cleared field arrives as `undefined`, which `JSON.stringify` maps to\n // * `undefined` rather than a string. `data:updated` types `value` as a\n // * required string, so the empty string is what \"no value\" looks like here.\n if (value === undefined) return ''\n return (typeof value === 'string' ? value : JSON.stringify(value)).slice(0, MAX_VALUE_LENGTH)\n}\n\n/** One event carrying every key in one `.set()` call. */\nexport function buildDataUpdatedEvent({\n fields,\n patch,\n formId,\n}: {\n fields: readonly FieldConfig[]\n patch: Record<string, JsonValue>\n formId: string\n}): DataUpdatedEvent {\n const byKey = new Map(fields.map((field) => [field.key, field]))\n\n // * The key count is intentionally unbounded: `.set()` only ever applies keys\n // * the config declares, so it can never exceed the form's field count — a\n // * developer-authored, code-reviewed number rather than user input.\n const data = Object.fromEntries(\n Object.entries(patch).map(([key, value]) => {\n const field = byKey.get(key)\n return [\n key,\n {\n value: formatFieldValue({ value, field }),\n label: (field?.label ?? key).slice(0, MAX_LABEL_LENGTH),\n },\n ]\n }),\n )\n\n return { event_name: 'data:updated', data, form_id: formId }\n}\n\n/** One `field:updated` per changed key, emitted alongside `data:updated`. */\nexport function buildFieldUpdatedEvents({\n fields,\n patch,\n formId,\n}: {\n fields: readonly FieldConfig[]\n patch: Record<string, JsonValue>\n formId: string\n}): FieldUpdatedEvent[] {\n const byKey = new Map(fields.map((field) => [field.key, field]))\n\n return Object.entries(patch).map(([key, value]) => {\n const field = byKey.get(key)\n const event: FieldUpdatedEvent = {\n event_name: 'field:updated',\n field_key: key,\n field_type: mapFieldUpdatedType(field?.type ?? 'text'),\n form_id: formId,\n }\n // * `field_value` is optional on the ingest contract, which is already its\n // * representation for \"no value\" — so a cleared field omits the key rather\n // * than sending `undefined` through as a value.\n if (value !== undefined) {\n event.field_value = value\n }\n if (field?.registryId !== undefined) {\n event.registry_field_id = field.registryId\n }\n if (field?.protocolFieldId !== undefined) {\n event.protocol_field_id = field.protocolFieldId\n }\n return event\n })\n}\n","import type { FieldOption } from './config.js'\n\n/**\n * The array a `multiselect` patch should actually store, given what it added\n * relative to the previous value. An added exclusive option wins outright; an\n * added regular option evicts every exclusive value; a patch that only removed\n * values passes through.\n */\n// ! Driven by the diff against the previous value, not by the incoming array\n// ! alone: \"the author just picked this\" is the only thing that can decide\n// ! which of two conflicting values survives. That makes this history\n// ! dependent, so it belongs on the write path and nowhere else.\nexport function normalizeExclusiveSelection({\n next,\n previous,\n options,\n}: {\n next: readonly string[]\n previous: readonly string[]\n options: readonly FieldOption[]\n}): readonly string[] {\n const exclusive = new Set(\n options.filter((option) => option.exclusive === true).map((option) => option.value),\n )\n // * The overwhelmingly common field declares no exclusive option at all, so\n // * it leaves with one allocation and the caller's own array.\n if (exclusive.size === 0) return next\n\n const previousValues = new Set(previous)\n const added = next.filter((entry) => !previousValues.has(entry))\n const addedExclusive = added.filter((entry) => exclusive.has(entry))\n\n // * Last in `next` order rather than first, so a single patch that adds two\n // * exclusive values resolves the same way every time.\n if (addedExclusive.length > 0) return [addedExclusive[addedExclusive.length - 1] as string]\n\n if (added.length > 0) return next.filter((entry) => !exclusive.has(entry))\n\n // * Nothing was added — the patch only removed values. Whatever mix it leaves\n // * behind is what the author asked for.\n return next\n}\n","import { SchemaError } from '../errors.js'\n\nimport type { FieldConfig, FieldType, FormSchema } from './config.js'\n\nconst FIELD_TYPES: readonly FieldType[] = [\n 'text',\n 'email',\n 'number',\n 'boolean',\n 'select',\n 'multiselect',\n 'json',\n 'file',\n]\n\nconst VALIDATION_RULES: readonly string[] = [\n 'required',\n 'minLength',\n 'maxLength',\n 'min',\n 'max',\n 'pattern',\n 'oneOf',\n 'accept',\n 'maxSize',\n 'custom',\n]\n\nconst NUMERIC_RULES: readonly string[] = ['minLength', 'maxLength', 'min', 'max', 'maxSize']\n\nconst FILE_ONLY_RULES: readonly string[] = ['accept', 'maxSize']\n\nconst OPTION_KEYS: readonly string[] = ['value', 'label', 'exclusive']\n\n/** The two field types whose choices are declared through a field-level `options`. */\nconst OPTION_FIELD_TYPES: readonly FieldType[] = ['select', 'multiselect']\n\n/** The ingest `z.string().max(128)` bound on a `data:updated` key. */\nconst MAX_FIELD_KEY_LENGTH = 128\n\n/** The ingest `z.string().max(128)` bound on a `form:submitted` key. */\nconst MAX_FORM_KEY_LENGTH = 128\n\n// ! A leading `(?letters)` is a SyntaxError in every JavaScript RegExp, so\n// ! reserving this prefix cannot shadow a pattern that compiles today. The\n// ! ES2025 modifier group is spelled `(?i:` with a colon and is not matched.\nconst INLINE_PATTERN_FLAGS = /^\\(\\?([dgimsuvy]+)\\)/\n\n/** Internal. The snapshot `initForm` holds for the life of an instance. */\nexport interface ResolvedForm {\n readonly formKey: string\n readonly fields: readonly FieldConfig[]\n /** Compiled once per config object, keyed by field key. */\n readonly patterns: ReadonlyMap<string, RegExp>\n}\n\n// ! The only module-level binding in this package. Keyed by schema object\n// ! identity, it holds nothing but data derived from an argument the caller\n// ! already had, and nothing reads it except the call that supplied the key.\n// ! It must never hold user or per-request state. This is not a registry: there\n// ! is no name a caller can guess, and entries are collectable with the schema.\nconst RESOLVED_SCHEMAS = new WeakMap<FormSchema, ResolvedForm>()\n\nexport function resolveForm({ schema }: { schema: FormSchema }): ResolvedForm {\n const memoized = RESOLVED_SCHEMAS.get(schema)\n if (memoized) return memoized\n\n const resolved = validateAndCompile({ schema })\n RESOLVED_SCHEMAS.set(schema, resolved)\n return resolved\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nfunction validateAndCompile({ schema }: { schema: FormSchema }): ResolvedForm {\n // * Every check below reads the schema as `unknown`, because the whole point\n // * is the input the compiler never saw: a JavaScript consumer, a schema that\n // * arrived from the platform, or one whose `as const` was dropped.\n const root: unknown = schema\n\n assertJsonRepresentable({ root })\n\n if (!isObjectLike(root)) throw new SchemaError('schema: must be an object')\n\n const formKey = root['id']\n if (typeof formKey !== 'string' || formKey.trim() === '')\n throw new SchemaError('schema.id: must be a non-empty string')\n // ! The id is the storage key verbatim, so `' signup '` would persist under a\n // ! different key than `'signup'`. Rejected rather than trimmed: silently\n // ! normalizing would orphan whatever a schema had already stored.\n if (formKey !== formKey.trim())\n throw new SchemaError('schema.id: must not have leading or trailing whitespace')\n if (formKey.length > MAX_FORM_KEY_LENGTH)\n throw new SchemaError(\n `schema.id: must be at most ${MAX_FORM_KEY_LENGTH} characters (received ${formKey.length})`,\n )\n\n const name = root['name']\n if (name !== undefined) {\n if (typeof name !== 'string') throw new SchemaError('schema.name: must be a string')\n if (name.trim() === '') throw new SchemaError('schema.name: must be a non-empty string')\n }\n\n if (!Array.isArray(root['fields'])) throw new SchemaError('schema.fields: must be an array')\n\n const fields: unknown[] = root['fields']\n if (fields.length === 0) throw new SchemaError('schema.fields: must declare at least one field')\n\n const patterns = new Map<string, RegExp>()\n const seenKeys = new Set<string>()\n fields.forEach((field, index) => {\n validateField({ field, path: `schema.fields[${index}]`, seenKeys, patterns })\n })\n\n // * The field list is copied, not aliased: an instance holds this snapshot for\n // * its whole life, so a schema array mutated afterwards must not change what\n // * a live form validates and writes against.\n return { formKey, fields: [...fields] as readonly FieldConfig[], patterns }\n}\n\nfunction validateField({\n field,\n path,\n seenKeys,\n patterns,\n}: {\n field: unknown\n path: string\n seenKeys: Set<string>\n patterns: Map<string, RegExp>\n}): void {\n if (!isObjectLike(field)) throw new SchemaError(`${path}: must be an object`)\n\n const key = field['key']\n if (typeof key !== 'string' || key.trim() === '')\n throw new SchemaError(`${path}.key: must be a non-empty string`)\n if (key.length > MAX_FIELD_KEY_LENGTH)\n throw new SchemaError(\n `${path}.key: must be at most ${MAX_FIELD_KEY_LENGTH} characters (received ${key.length})`,\n )\n if (seenKeys.has(key))\n throw new SchemaError(`${path}.key: duplicate field key \"${key}\" in this form`)\n seenKeys.add(key)\n\n const label = field['label']\n if (typeof label !== 'string' || label.trim() === '')\n throw new SchemaError(`${path}.label: must be a non-empty string`)\n\n const type = field['type']\n if (typeof type !== 'string' || !FIELD_TYPES.includes(type as FieldType))\n throw new SchemaError(`${path}.type: must be one of ${FIELD_TYPES.join(', ')}`)\n\n const registryId = field['registryId']\n if (registryId !== undefined && typeof registryId !== 'string')\n throw new SchemaError(`${path}.registryId: must be a string`)\n\n const protocolFieldId = field['protocolFieldId']\n if (protocolFieldId !== undefined && typeof protocolFieldId !== 'string')\n throw new SchemaError(`${path}.protocolFieldId: must be a string`)\n\n const includeInCookies = field['includeInCookies']\n if (includeInCookies !== undefined && typeof includeInCookies !== 'boolean')\n throw new SchemaError(`${path}.includeInCookies: must be a boolean`)\n\n validateOptions({ field, type: type as FieldType, path })\n\n // * Unknown keys on the field object itself are tolerated, deliberately\n // * asymmetric with `validations` below: the platform may add presentation\n // * metadata, and an older SDK should not reject a newer config outright.\n // * An unknown key inside `validations` can only be a typo for a rule, and\n // * silently skipping a rule is the worst failure available. The JSON\n // * round-trip check still rejects a *function* on an unknown field key, so\n // * this tolerance opens no second door for functions.\n const validations = field['validations']\n if (validations === undefined) return\n\n validateValidations({ validations, type: type as FieldType, path: `${path}.validations` })\n if (!isObjectLike(validations)) return\n\n const pattern = validations['pattern']\n if (typeof pattern !== 'string') return\n\n patterns.set(key, compilePattern({ pattern, path: `${path}.validations.pattern` }))\n}\n\n// ! Deliberately strict about unknown keys, unlike the field object that holds\n// ! it: an unknown key inside an option can only be a typo for `value`,\n// ! `label` or `exclusive`, and silently ignoring one would drop a choice's\n// ! exclusivity without a word.\nfunction validateOptions({\n field,\n type,\n path,\n}: {\n field: Record<string, unknown>\n type: FieldType\n path: string\n}): void {\n const options = field['options']\n if (options === undefined) return\n\n if (!OPTION_FIELD_TYPES.includes(type))\n throw new SchemaError(`${path}.options: only a select or multiselect field may declare options`)\n\n if (!(Array.isArray(options) && options.length > 0))\n throw new SchemaError(`${path}.options: must be a non-empty array`)\n\n const entries: unknown[] = options\n const seenValues = new Set<string>()\n\n entries.forEach((option, index) => {\n const at = `${path}.options[${index}]`\n if (!isObjectLike(option)) throw new SchemaError(`${at}: must be an object`)\n\n for (const optionKey of Object.keys(option))\n if (!OPTION_KEYS.includes(optionKey))\n throw new SchemaError(\n `${at}.${optionKey}: unknown option key; expected one of ${OPTION_KEYS.join(', ')}`,\n )\n\n const value = option['value']\n if (typeof value !== 'string' || value.trim() === '')\n throw new SchemaError(`${at}.value: must be a non-empty string`)\n\n const label = option['label']\n if (label !== undefined && typeof label !== 'string')\n throw new SchemaError(`${at}.label: must be a string`)\n\n const exclusive = option['exclusive']\n if (exclusive !== undefined && typeof exclusive !== 'boolean')\n throw new SchemaError(`${at}.exclusive: must be a boolean`)\n // * A `select` already holds exactly one value, so an exclusive flag there\n // * can only be a misunderstanding. Several exclusive options on one\n // * `multiselect` are fine: selecting either clears everything else.\n if (exclusive === true && type === 'select')\n throw new SchemaError(\n `${at}.exclusive: only a multiselect field may declare an exclusive option`,\n )\n\n if (seenValues.has(value))\n throw new SchemaError(`${at}.value: duplicate option value \"${value}\" on this field`)\n seenValues.add(value)\n })\n}\n\nfunction validateValidations({\n validations,\n type,\n path,\n}: {\n validations: unknown\n type: FieldType\n path: string\n}): void {\n if (!isObjectLike(validations)) throw new SchemaError(`${path}: must be an object`)\n\n for (const rule of Object.keys(validations)) {\n if (!VALIDATION_RULES.includes(rule))\n throw new SchemaError(\n `${path}.${rule}: unknown validation rule; expected one of ${VALIDATION_RULES.join(', ')}`,\n )\n if (type !== 'file' && FILE_ONLY_RULES.includes(rule))\n throw new SchemaError(`${path}.${rule}: is only valid for file fields`)\n }\n\n const required = validations['required']\n if (required !== undefined && typeof required !== 'boolean')\n throw new SchemaError(`${path}.required: must be a boolean`)\n\n for (const rule of NUMERIC_RULES) {\n const value = validations[rule]\n // * `Number.isFinite` is also what rejects the `NaN`/`Infinity` pair that\n // * survives TypeScript's `number` but serializes to `null`.\n if (value !== undefined && !(typeof value === 'number' && Number.isFinite(value)))\n throw new SchemaError(`${path}.${rule}: must be a finite number`)\n }\n\n const minLength = validations['minLength']\n const maxLength = validations['maxLength']\n if (typeof minLength === 'number' && typeof maxLength === 'number' && maxLength < minLength)\n throw new SchemaError(`${path}.maxLength: must be greater than or equal to minLength`)\n\n const min = validations['min']\n const max = validations['max']\n if (typeof min === 'number' && typeof max === 'number' && max < min)\n throw new SchemaError(`${path}.max: must be greater than or equal to min`)\n\n const oneOf = validations['oneOf']\n // ! Before the shape check on purpose: an author migrating from the old model\n // ! must read the rename, not a complaint about the array they already wrote.\n if (oneOf !== undefined && OPTION_FIELD_TYPES.includes(type))\n throw new SchemaError(\n `${path}.oneOf: not allowed on a ${type} field; declare choices through the field's options instead`,\n )\n if (oneOf !== undefined && !(Array.isArray(oneOf) && oneOf.length > 0))\n throw new SchemaError(`${path}.oneOf: must be a non-empty array`)\n\n const accept = validations['accept']\n if (\n accept !== undefined &&\n !(\n Array.isArray(accept) &&\n accept.length > 0 &&\n accept.every((entry) => typeof entry === 'string' && entry.trim() !== '')\n )\n ) {\n throw new SchemaError(`${path}.accept: must be a non-empty array of strings`)\n }\n\n const maxSize = validations['maxSize']\n if (\n maxSize !== undefined &&\n !(typeof maxSize === 'number' && Number.isFinite(maxSize) && maxSize > 0)\n )\n throw new SchemaError(`${path}.maxSize: must be a positive finite number`)\n\n const pattern = validations['pattern']\n if (pattern !== undefined && typeof pattern !== 'string')\n throw new SchemaError(`${path}.pattern: must be a string`)\n\n // * The runtime half of the one JSON exception: a widened config or a\n // * JavaScript consumer can put anything here, and a non-function would be\n // * called and throw a TypeError from inside validation on the first\n // * keystroke. Arity and async-ness are deliberately not inspected — the\n // * return-shape and thenable checks at call time own that.\n const custom = validations['custom']\n if (custom !== undefined && typeof custom !== 'function')\n throw new SchemaError(`${path}.custom: must be a function (received ${typeof custom})`)\n}\n\nfunction compilePattern({ pattern, path }: { pattern: string; path: string }): RegExp {\n const prefix = INLINE_PATTERN_FLAGS.exec(pattern)\n const source = prefix ? pattern.slice(prefix[0].length) : pattern\n const flags = prefix?.[1] ?? ''\n\n // * `g` and `y` carry `lastIndex` across `.test()` calls, so a pattern reused\n // * for every keystroke would silently alternate pass and fail.\n try {\n return new RegExp(source, flags.replace(/[gy]/g, ''))\n } catch (error) {\n throw new SchemaError(`${path}: invalid pattern — ${errorMessage(error)}`, { cause: error })\n }\n}\n\n// ---------------------------------------------------------------------------\n// JSON representability\n// ---------------------------------------------------------------------------\n\n/**\n * Rejects every value in the config that would not survive\n * `JSON.parse(JSON.stringify(x))` — a function included, except at the one\n * permitted location, `validations.custom` on a field.\n */\nfunction assertJsonRepresentable({ root }: { root: unknown }): void {\n const stripped = withoutFieldValidators(root)\n const serialized = stringifyOrUndefined(stripped)\n\n if (serialized === undefined) {\n const path = findNonJsonPath({ value: stripped, path: 'schema', seen: new Set() })\n throw new SchemaError(`${path ?? 'schema'}: value cannot be serialized to JSON`)\n }\n\n const mismatch = firstMismatch({\n actual: stripped,\n expected: JSON.parse(serialized),\n path: 'schema',\n })\n if (mismatch)\n throw new SchemaError(\n `${mismatch}: value does not survive a JSON round trip; only a field's validations.custom may hold a function, and every other value must be JSON-representable`,\n )\n}\n\n// ! Copies along `schema.fields[*].validations` only, so the caller's schema is\n// ! never mutated and no other key named `custom` at any depth is stripped.\n// ! A blanket strip would let a function through anywhere and gut the check.\nfunction withoutFieldValidators(root: unknown): unknown {\n if (!isObjectLike(root)) return root\n if (!Array.isArray(root['fields'])) return root\n\n const fields: unknown[] = root['fields']\n return { ...root, fields: fields.map(stripField) }\n}\n\nfunction stripField(field: unknown): unknown {\n if (!isObjectLike(field)) return field\n const validations = field['validations']\n if (!isObjectLike(validations) || !('custom' in validations)) return field\n\n const { custom: _custom, ...rest } = validations\n return { ...field, validations: rest }\n}\n\nfunction stringifyOrUndefined(value: unknown): string | undefined {\n try {\n return JSON.stringify(value)\n } catch {\n return undefined\n }\n}\n\n/** The path of the first value `JSON.stringify` cannot handle at all. */\nfunction findNonJsonPath({\n value,\n path,\n seen,\n}: {\n value: unknown\n path: string\n seen: Set<object>\n}): string | undefined {\n if (typeof value === 'bigint' || typeof value === 'symbol') return path\n if (value === null || typeof value !== 'object') return undefined\n if (seen.has(value)) return path\n\n seen.add(value)\n for (const [childPath, child] of childEntries({ value, path })) {\n const found = findNonJsonPath({ value: child, path: childPath, seen })\n if (found) return found\n }\n seen.delete(value)\n return undefined\n}\n\nfunction childEntries({ value, path }: { value: object; path: string }): [string, unknown][] {\n if (Array.isArray(value)) {\n const items: unknown[] = value\n return items.map((item, index) => [`${path}[${index}]`, item])\n }\n return Object.entries(value as Record<string, unknown>).map(([key, item]) => [\n `${path}.${key}`,\n item,\n ])\n}\n\n/** The path of the first value that changed across the round trip. */\nfunction firstMismatch({\n actual,\n expected,\n path,\n}: {\n actual: unknown\n expected: unknown\n path: string\n}): string | undefined {\n if (Array.isArray(actual) || Array.isArray(expected)) {\n if (!Array.isArray(actual) || !Array.isArray(expected)) return path\n\n const actualItems: unknown[] = actual\n const expectedItems: unknown[] = expected\n if (actualItems.length !== expectedItems.length) return path\n\n for (const [index, item] of actualItems.entries()) {\n const found = firstMismatch({\n actual: item,\n expected: expectedItems[index],\n path: `${path}[${index}]`,\n })\n if (found) return found\n }\n return undefined\n }\n\n if (isJsonObject(actual) && isJsonObject(expected)) {\n const actualKeys = Object.keys(actual)\n const expectedKeys = Object.keys(expected)\n // * A key present before and absent after is exactly how `undefined`, a\n // * function, and a `Symbol` value disappear.\n if (actualKeys.length !== expectedKeys.length) {\n const dropped = actualKeys.find((key) => !expectedKeys.includes(key))\n return dropped === undefined ? path : `${path}.${dropped}`\n }\n for (const key of actualKeys) {\n const found = firstMismatch({\n actual: actual[key],\n expected: expected[key],\n path: `${path}.${key}`,\n })\n if (found) return found\n }\n return undefined\n }\n\n // * Catches `NaN`/`Infinity` (both become `null`), a `Date` (becomes a\n // * string), and a `RegExp`, `Map`, `Set`, or class instance (all become\n // * `{}`, which is not the original object).\n return actual === expected ? undefined : path\n}\n\n// ---------------------------------------------------------------------------\n// Shared predicates\n// ---------------------------------------------------------------------------\n\nfunction isObjectLike(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\n/** Narrower than `isObjectLike`: a `Date`, `RegExp`, or class instance is not one. */\nfunction isJsonObject(value: unknown): value is Record<string, unknown> {\n if (!isObjectLike(value)) return false\n const prototype = Object.getPrototypeOf(value)\n return prototype === Object.prototype || prototype === null\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error)\n}\n","import type { EmbeddablesInstance } from '@embeddables/core'\nimport type { JsonValue } from '@embeddables/shared-types/json'\n\nimport { FormsError, SchemaError } from '../errors.js'\nimport { clearFormCookieData, writeFormCookieData } from '../storage/cookie-form-data.js'\nimport { resolveDefaultPersistence } from '../storage/persistence-client.js'\nimport { resolvePersistenceConfig } from '../storage/persistence-config.js'\nimport {\n degradeToMemory,\n isSerializable,\n readFields,\n removeFields,\n resolveStorage,\n writeFields,\n} from '../storage/storage.js'\nimport { FORM_UPLOAD_MAX_BYTES, uploadFormFile } from '../storage/upload-client.js'\nimport { contentTypeMatchesAccept, type FormFileRef } from '../types/form-file.js'\nimport { buildDataUpdatedEvent, buildFieldUpdatedEvents } from './analytics.js'\nimport { normalizeExclusiveSelection } from './options.js'\nimport { resolveForm } from './resolve.js'\nimport { validateValue } from './validation.js'\n\nimport type { ResolvedPersistenceConfig } from '../storage/persistence-config.js'\nimport type { FormsPersistence, PersistedField, RecoverableField } from '../storage/persistence.js'\nimport type { FormsStorage } from '../storage/storage.js'\nimport type { AnalyticsInstance } from './analytics.js'\nimport type {\n FieldConfig,\n FieldValidator,\n FormFieldKey,\n FormSchema,\n FormValues,\n ProtocolFieldId,\n} from './config.js'\n\n/** Per-key validation errors. An empty object means the operation succeeded. */\nexport type FieldErrors<TSchema extends FormSchema> = Readonly<\n Partial<Record<FormFieldKey<TSchema>, readonly string[]>>\n>\n\nexport interface SetResult<TSchema extends FormSchema> {\n ok: boolean\n errors: FieldErrors<TSchema>\n /** Set when an `analyticsInstance` was configured and `trackEvent` rejected. Never thrown. */\n trackError?: unknown\n}\n\nexport interface SubmitResult<TSchema extends FormSchema> {\n ok: boolean\n errors: FieldErrors<TSchema>\n values: Partial<FormValues<TSchema>>\n trackError?: unknown\n}\n\nexport interface ValidateResult<TSchema extends FormSchema> {\n ok: boolean\n errors: FieldErrors<TSchema>\n values: Partial<FormValues<TSchema>>\n}\n\nexport interface FormInstance<TSchema extends FormSchema> {\n readonly key: TSchema['id']\n /**\n * Applies every key atomically: all or nothing, one write, one event.\n * Validates and persists synchronously; the returned promise never rejects.\n * Throws synchronously only if a custom validator throws or returns an\n * illegal shape.\n */\n set(patch: Partial<FormValues<TSchema>>): Promise<SetResult<TSchema>>\n /** Typed by the field's declared `type`. Nothing verifies the stored value against it. */\n get<K extends FormFieldKey<TSchema>>(key: K): FormValues<TSchema>[K] | undefined\n /**\n * Read a stored value by the field's declared `protocolFieldId`. Returns\n * `undefined` when no field maps to the id or the value is unset. Typed like\n * `get()` for the backing field.\n */\n getValueByProtocolFieldId<P extends ProtocolFieldId<TSchema>>(\n protocolFieldId: P,\n ):\n | FormValues<TSchema>[Extract<TSchema['fields'][number], { protocolFieldId: P }>['key']]\n | undefined\n getAll(): Partial<FormValues<TSchema>>\n /**\n * Validates every declared field, then emits one `form:submitted` event.\n *\n * Not idempotent: every call emits another event. The caller owns dedupe —\n * disable the button, or guard on a route transition.\n *\n * Same synchronous-throw and never-reject contract as `set`.\n */\n submit(): Promise<SubmitResult<TSchema>>\n /**\n * Runs validation without writing to storage or emitting analytics.\n *\n * With no argument, validates every declared field against stored values and\n * replaces `errors()` wholesale. With a patch, validates only those keys\n * against a merged snapshot and updates errors for those keys only.\n *\n * Same synchronous-throw and never-reject contract as `set` / `submit`.\n */\n validate(patch?: Partial<FormValues<TSchema>>): Promise<ValidateResult<TSchema>>\n errors(): FieldErrors<TSchema>\n clear(): void\n /**\n * Uploads a file for a declared `type: file` field. Returns a `FormFileRef`\n * with `status: 'done'`; the caller commits it with `.set()`.\n */\n uploadFile(args: {\n key: FormFieldKey<TSchema>\n file: Blob\n fileName?: string\n }): Promise<FormFileRef>\n /** Synchronous listener for value and error mutations. Returns an unsubscribe function. */\n subscribe(listener: () => void): () => void\n}\n\nconst REQUIRED_CORE_METHODS = ['getAppUserId', 'getProjectId', 'getPublishableKey'] as const\n\nfunction hasRequiredCoreMethods(value: unknown): value is EmbeddablesInstance {\n if (typeof value !== 'object' || value === null) return false\n return REQUIRED_CORE_METHODS.every(\n (method) => typeof (value as Record<string, unknown>)[method] === 'function',\n )\n}\n\nfunction mergeCustomValidations<TSchema extends FormSchema>({\n schema,\n customValidations,\n}: {\n schema: TSchema\n customValidations?: { readonly [K in FormFieldKey<TSchema>]?: FieldValidator }\n}): TSchema {\n if (!customValidations) return schema\n\n const declared = new Set(schema.fields.map((field) => field.key))\n for (const key of Object.keys(customValidations)) {\n if (!declared.has(key)) {\n throw new SchemaError(`customValidations: unknown field key \"${key}\"`)\n }\n const validator = customValidations[key as FormFieldKey<TSchema>]\n if (typeof validator !== 'function') {\n throw new SchemaError(`customValidations.${key}: must be a function`)\n }\n }\n\n const fields = schema.fields.map((field) => {\n const custom = customValidations[field.key as FormFieldKey<TSchema>]\n if (!custom) return field\n return {\n ...field,\n validations: { ...field.validations, custom },\n }\n })\n\n return { ...schema, fields }\n}\n\n/** Every form the app can open, keyed by form id — the shape `_dist` exports. */\nexport type FormSchemaMap = Readonly<Record<string, FormSchema>>\n\nexport type CustomValidationsFor<TSchema extends FormSchema> = {\n readonly [K in FormFieldKey<TSchema>]?: FieldValidator\n}\n\nexport interface FormCustomValidationsEntry<TSchema extends FormSchema = FormSchema> {\n formId: Extract<TSchema['id'], string>\n customValidations: CustomValidationsFor<TSchema>\n}\n\n/** Per-form SSR/hydration seed passed from `initFormsServer().getServerFormData()`. */\nexport interface FormServerDataEntry<TSchema extends FormSchema = FormSchema> {\n formId: Extract<TSchema['id'], string>\n serverFormData: Partial<FormValues<TSchema>>\n}\n\nexport type FormCustomValidationsEntriesFor<TSchemas extends FormSchemaMap> = ReadonlyArray<\n {\n [K in keyof TSchemas & string]: FormCustomValidationsEntry<TSchemas[K]>\n }[keyof TSchemas & string]\n>\n\nexport type FormServerDataEntriesFor<TSchemas extends FormSchemaMap> = ReadonlyArray<\n {\n [K in keyof TSchemas & string]: FormServerDataEntry<TSchemas[K]>\n }[keyof TSchemas & string]\n>\n\nexport interface FormsServerInitOptions<TSchemas extends FormSchemaMap = FormSchemaMap> {\n server: EmbeddablesInstance\n customValidations?: FormCustomValidationsEntriesFor<TSchemas>\n analyticsInstance?: AnalyticsInstance\n}\n\nexport type ServerFormInstance<TSchema extends FormSchema> = Pick<\n FormInstance<TSchema>,\n 'key' | 'set' | 'get' | 'getAll' | 'getValueByProtocolFieldId'\n>\n\nexport type ServerFormDataByFormId<TSchemas extends FormSchemaMap> = {\n [K in keyof TSchemas]?: Partial<FormValues<TSchemas[K]>>\n}\n\n// ! The public surface: `core`, every form the app declares, and an optional\n// ! analytics client.\n// ! Storage and persistence ports are not consumer options — internal seams\n// ! (see `FormsClientOptions`).\nexport interface InitFormsOptions<TSchemas extends FormSchemaMap = FormSchemaMap> {\n core: EmbeddablesInstance\n customValidations?: FormCustomValidationsEntriesFor<TSchemas>\n serverFormData?: FormServerDataEntriesFor<TSchemas>\n analyticsInstance?: AnalyticsInstance\n}\n\nexport interface FormsClient<TSchemas extends FormSchemaMap = FormSchemaMap> {\n /**\n * The form for one of the declared schemas.\n *\n * One instance per form id, per client: the first call builds it and every\n * later call returns that same instance. Two live instances over one form id\n * would not observe each other's writes and the later one's `.set()` would\n * overwrite the earlier one's storage, so the client never hands out a second.\n *\n * ! `customValidations` belong to the matching form id on Core. `getForm`\n * ! only selects which memoized instance to return.\n */\n getForm<K extends keyof TSchemas & string>(params: { formId: K }): FormInstance<TSchemas[K]>\n}\n\n/**\n * Validates the core instance and registered form schemas once, then returns a\n * client whose `getForm` builds each form by id.\n */\nexport function initForms<TSchemas extends FormSchemaMap = FormSchemaMap>(\n options: InitFormsOptions<TSchemas>,\n): FormsClient<TSchemas> {\n return createFormsClient(options)\n}\n\ninterface ResolvedFormEntry<TSchema extends FormSchema = FormSchema> {\n schema: TSchema\n customValidations?: CustomValidationsFor<TSchema>\n serverFormData?: Partial<FormValues<TSchema>>\n}\n\nconst REQUIRED_CORE_FORM_METHODS = ['getFormIds', 'getFormSchema'] as const\n\nfunction hasCoreFormSchemaMethods(value: EmbeddablesInstance): value is EmbeddablesInstance & {\n getFormIds(): readonly string[]\n getFormSchema(formId: string): unknown\n} {\n return REQUIRED_CORE_FORM_METHODS.every(\n (method) => typeof (value as unknown as Record<string, unknown>)[method] === 'function',\n )\n}\n\nfunction toFormSchema(value: unknown, formId: string): FormSchema {\n if (typeof value !== 'object' || value === null) {\n throw new SchemaError(`Registered form \"${formId}\": schema must be an object.`)\n }\n\n const schema = value as FormSchema\n if (typeof schema.id !== 'string' || schema.id === '') {\n throw new SchemaError(`Registered form \"${formId}\": schema.id must be a non-empty string.`)\n }\n if (schema.id !== formId) {\n throw new SchemaError(`Registered form \"${formId}\": schema id must match map key.`)\n }\n\n return schema\n}\n\nfunction indexOverrideEntries<TEntry extends { formId: string }>({\n entries,\n label,\n valueKey,\n}: {\n entries: readonly TEntry[] | undefined\n label: string\n valueKey: keyof TEntry\n}): Map<string, TEntry[typeof valueKey]> {\n if (entries === undefined) return new Map()\n\n const byFormId = new Map<string, TEntry[typeof valueKey]>()\n for (let index = 0; index < entries.length; index++) {\n const entry = entries[index]\n if (typeof entry !== 'object' || entry === null) {\n throw new SchemaError(`${label}[${index}]: must be an object.`)\n }\n\n const { formId } = entry\n if (typeof formId !== 'string' || formId === '') {\n throw new SchemaError(`${label}[${index}].formId: must be a non-empty string.`)\n }\n\n const value = entry[valueKey]\n if (value === undefined) {\n throw new SchemaError(`${label}[${index}].${String(valueKey)}: is required.`)\n }\n\n if (byFormId.has(formId)) {\n throw new SchemaError(`${label}: duplicate form id \"${formId}\".`)\n }\n\n byFormId.set(formId, value)\n }\n\n return byFormId\n}\n\n// * Schemas are read from Core's registry; per-form options are matched by formId.\nfunction resolveInitConfig({\n core,\n customValidations,\n serverFormData,\n}: {\n core: EmbeddablesInstance\n customValidations?: readonly FormCustomValidationsEntry[]\n serverFormData?: readonly FormServerDataEntry[]\n}): Map<string, ResolvedFormEntry> {\n if (!hasCoreFormSchemaMethods(core)) {\n throw new FormsError('initForms requires a Core instance with registered form schemas.')\n }\n\n const formIds = core.getFormIds()\n if (formIds.length === 0) {\n throw new FormsError('initForms requires at least one form schema registered on Core.')\n }\n\n const customByFormId = indexOverrideEntries({\n entries: customValidations,\n label: 'customValidations',\n valueKey: 'customValidations',\n })\n const serverDataByFormId = indexOverrideEntries({\n entries: serverFormData,\n label: 'serverFormData',\n valueKey: 'serverFormData',\n })\n\n const registeredIds = new Set(formIds)\n for (const formId of customByFormId.keys()) {\n if (!registeredIds.has(formId)) {\n throw new SchemaError(`customValidations: unknown form id \"${formId}\".`)\n }\n }\n for (const formId of serverDataByFormId.keys()) {\n if (!registeredIds.has(formId)) {\n throw new SchemaError(`serverFormData: unknown form id \"${formId}\".`)\n }\n }\n\n const registry = new Map<string, ResolvedFormEntry>()\n for (const formId of formIds) {\n const schema = toFormSchema(core.getFormSchema(formId), formId)\n const entryCustomValidations = customByFormId.get(formId) as\n | CustomValidationsFor<FormSchema>\n | undefined\n const entryServerFormData = serverDataByFormId.get(formId) as\n | Partial<FormValues<FormSchema>>\n | undefined\n\n mergeCustomValidations({ schema, customValidations: entryCustomValidations })\n registry.set(formId, {\n schema,\n customValidations: entryCustomValidations,\n serverFormData: entryServerFormData,\n })\n }\n\n return registry\n}\n\n// ! Internal, not exported from the package barrel. The storage and persistence\n// ! seams live here so tests can inject a memory store / spy port and the real\n// ! R2/Supabase client can be wired later, without widening the public\n// ! `initForms` signature beyond what the Miro shows.\nexport interface FormsClientOptions<\n TSchemas extends FormSchemaMap = FormSchemaMap,\n> extends InitFormsOptions<TSchemas> {\n baseUrl?: string\n fetch?: typeof fetch\n storage?: FormsStorage\n persistence?: FormsPersistence\n}\n\nexport function createFormsClient<TSchemas extends FormSchemaMap = FormSchemaMap>({\n core,\n customValidations,\n serverFormData,\n analyticsInstance,\n baseUrl,\n fetch: fetchImpl,\n storage,\n persistence,\n}: FormsClientOptions<TSchemas>): FormsClient<TSchemas> {\n if (!hasRequiredCoreMethods(core)) {\n throw new FormsError('initForms requires an initialized Embeddables core instance.')\n }\n\n const registry = resolveInitConfig({ core, customValidations, serverFormData })\n const uploadConfig = resolvePersistenceConfig({ core, baseUrl, fetch: fetchImpl })\n const resolvedPersistence =\n persistence ?? resolveDefaultPersistence({ core, baseUrl, fetch: fetchImpl })\n\n // * Retained as the composition root; Forms does not persist identity.\n void core\n\n const instances = new Map<string, FormInstance<FormSchema>>()\n\n return {\n getForm({ formId }) {\n const existing = instances.get(formId)\n if (existing !== undefined) return existing\n\n const entry = registry.get(formId)\n if (entry === undefined) {\n throw new FormsError(`Unknown form id \"${formId}\".`)\n }\n\n const instance = createFormInstance({\n analyticsInstance,\n storage,\n persistence: resolvedPersistence,\n uploadConfig,\n schema: entry.schema,\n customValidations: entry.customValidations,\n serverFormData: entry.serverFormData,\n projectId: core.getProjectId(),\n })\n instances.set(formId, instance)\n return instance\n },\n } as FormsClient<TSchemas>\n}\n\nfunction mergeInitialBag({\n fromStorage,\n serverFormData,\n}: {\n fromStorage: Record<string, JsonValue>\n serverFormData?: Record<string, JsonValue>\n}): Record<string, JsonValue> {\n if (serverFormData === undefined) return { ...fromStorage }\n // * serverFormData wins on overlapping keys; localStorage fills gaps only.\n return { ...fromStorage, ...serverFormData }\n}\n\nfunction createFormInstance<const TSchema extends FormSchema>({\n analyticsInstance,\n storage,\n persistence,\n uploadConfig,\n schema,\n customValidations,\n serverFormData,\n projectId,\n}: {\n analyticsInstance?: AnalyticsInstance\n storage?: FormsStorage\n persistence: FormsPersistence\n uploadConfig: ResolvedPersistenceConfig | null\n schema: TSchema\n customValidations?: { readonly [K in FormFieldKey<TSchema>]?: FieldValidator }\n serverFormData?: Partial<FormValues<TSchema>>\n projectId: string\n}): FormInstance<TSchema> {\n const schemaForResolve = mergeCustomValidations({ schema, customValidations })\n // * Snapshotted for the instance's life, so a schema mutated afterwards\n // * cannot change a live form. Memoized on schema object identity, so a second\n // * instance over the same literal is free — but `customValidations` builds a\n // * new object above and therefore always resolves afresh.\n const resolved = resolveForm({ schema: schemaForResolve })\n const declared = new Map<string, FieldConfig>(resolved.fields.map((field) => [field.key, field]))\n const keyByProtocolFieldId = new Map<string, string>()\n for (const field of resolved.fields) {\n const protocolId = field.protocolFieldId\n if (typeof protocolId === 'string' && protocolId.length > 0) {\n keyByProtocolFieldId.set(protocolId, field.key)\n }\n }\n\n // * Held in one object rather than as bindings, because `storage` is swapped\n // * on degradation and the bag/error map are mutated in place.\n const resolvedStorage = resolveStorage({ storage })\n const listeners = new Set<() => void>()\n const notify = (): void => {\n for (const listener of listeners) {\n try {\n listener()\n } catch {\n // * Subscriber errors must not change form state operations.\n }\n }\n }\n\n const state = {\n storage: resolvedStorage,\n // ! Copied once at init and never refreshed: get/submit read this bag, and\n // ! set/clear persist it without another storage read. One live instance per\n // ! `schema.id` is therefore assumed. A second instance, another tab, or a\n // ! user clearing site data is invisible here, and the next `set` overwrites\n // ! it.\n bag: mergeInitialBag({\n fromStorage: readFields({\n storage: resolvedStorage,\n formKey: resolved.formKey,\n fieldDefinitions: resolved.fields,\n }),\n serverFormData: serverFormData as Record<string, JsonValue> | undefined,\n }),\n errors: new Map<string, readonly string[]>(),\n }\n\n // * Runs a persistence call without ever letting it break the caller: a\n // * synchronous throw is caught and a returned promise's rejection is\n // * swallowed. Fire-and-forget by contract — the SDK never awaits a durable\n // * write, so `set` / `submit` keep their never-rejects guarantee.\n const firePersistence = (run: () => void | Promise<void>): void => {\n try {\n const result = run()\n if (result instanceof Promise) {\n void result.then(undefined, () => undefined)\n }\n } catch {\n // best-effort: a failing persistence port never surfaces to set/submit\n }\n }\n\n // * localStorage was seeded above and always wins. Recovery is eligible only\n // * for a field that declares a registry/protocol id and is still absent\n // * locally, and a recovered value is merged only while the local value stays\n // * missing. With the no-op default this whole block is inert.\n const mergeRecovered = (recovered: Record<string, JsonValue>): void => {\n let mergedCount = 0\n for (const [key, value] of Object.entries(recovered)) {\n if (value !== undefined && state.bag[key] === undefined) {\n state.bag[key] = value\n mergedCount++\n }\n }\n if (mergedCount > 0) notify()\n }\n\n const recoverable: RecoverableField[] = resolved.fields\n .filter(\n (field) =>\n (field.registryId !== undefined || field.protocolFieldId !== undefined) &&\n state.bag[field.key] === undefined,\n )\n .map((field) => ({\n key: field.key,\n ...(field.registryId === undefined ? {} : { registryId: field.registryId }),\n ...(field.protocolFieldId === undefined ? {} : { protocolFieldId: field.protocolFieldId }),\n }))\n\n if (recoverable.length > 0) {\n try {\n const result = persistence.recoverRegistryFields({\n formKey: resolved.formKey,\n fields: recoverable,\n })\n if (result instanceof Promise) {\n void result.then(\n (recovered) => mergeRecovered(recovered ?? {}),\n () => undefined,\n )\n } else {\n mergeRecovered(result)\n }\n } catch {\n // best-effort: recovery must never throw out of getForm\n }\n }\n\n const freeze = (entries: Map<string, readonly string[]>): FieldErrors<TSchema> =>\n Object.freeze(Object.fromEntries(entries)) as FieldErrors<TSchema>\n\n const noErrors = (): FieldErrors<TSchema> => freeze(new Map())\n\n /** The stored bag narrowed to the keys the config declares. */\n const narrow = (bag: Record<string, JsonValue>): Record<string, JsonValue> => {\n const narrowed: Record<string, JsonValue> = {}\n for (const field of resolved.fields) {\n const value = bag[field.key]\n if (value !== undefined) narrowed[field.key] = value\n }\n return narrowed\n }\n\n const readBag = (): Record<string, JsonValue> => state.bag\n\n // * The whole type boundary, in one place: a field declares its validator\n // * against its own value type, while `validateValue` is a runtime predicate\n // * over `JsonValue`. Sound because `validateValue` calls the validator only\n // * after the field's type predicate passed.\n const validatorFor = (field: FieldConfig): FieldValidator | undefined =>\n field.validations?.custom as FieldValidator | undefined\n\n // * Validates the given keys against one snapshot. Callers that pass every\n // * declared field — `submit()` and parameterless `validate()` — do so to\n // * catch a field invalidated by an earlier `.set()` on some other key, not\n // * only the keys touched in the latest patch.\n const validateDeclaredFields = ({\n snapshot,\n keys,\n }: {\n snapshot: Record<string, JsonValue>\n keys: readonly string[]\n }): Map<string, readonly string[]> => {\n const errors = new Map<string, readonly string[]>()\n\n for (const key of keys) {\n const field = declared.get(key)\n if (!field) continue\n\n const messages = validateValue({\n field,\n value: snapshot[field.key],\n values: snapshot,\n pattern: resolved.patterns.get(key),\n validator: validatorFor(field),\n })\n if (messages.length > 0) errors.set(key, messages)\n }\n\n return errors\n }\n\n const replaceErrors = (errors: Map<string, readonly string[]>): void => {\n state.errors.clear()\n for (const [key, messages] of errors) state.errors.set(key, messages)\n }\n\n const applyPatchValidationErrors = ({\n errors,\n patchKeys,\n }: {\n errors: Map<string, readonly string[]>\n patchKeys: readonly string[]\n }): void => {\n for (const key of patchKeys) {\n const messages = errors.get(key)\n if (messages) state.errors.set(key, messages)\n else state.errors.delete(key)\n }\n }\n\n // ! Deliberately not `async`. An `async` function turns the synchronous throw\n // ! from a misbehaving custom validator into a rejected promise, which would\n // ! break the never-rejects contract and hide a loud developer error inside\n // ! an unhandled rejection that `void form.set(…)` swallows. Every write and\n // ! every validation below completes before this function returns.\n const set = (patch: Partial<FormValues<TSchema>>): Promise<SetResult<TSchema>> => {\n // ! A copy, not the caller's object. Exclusive normalization below writes\n // ! back into this bag, and doing that through an alias would both hand the\n // ! caller a patch they never wrote and throw on a frozen one — turning a\n // ! validation call into a synchronous TypeError.\n const changes = { ...patch } as Record<string, JsonValue>\n\n // * Mirrors the analytics SDK's `track([])`: no validation, no write, no\n // * event. The in-memory bag is already the source of truth.\n if (Object.keys(changes).length === 0) return Promise.resolve({ ok: true, errors: noErrors() })\n\n // * Read once and reused for `candidate` below, so the patch is normalized\n // * against exactly the bag it is then merged into.\n const previousBag = readBag()\n\n // ! Before `entries` is taken, and so before validation and the write: every\n // ! one of them must see the array that actually gets stored. Normalizing\n // ! afterwards would let `maxLength` count values the author never keeps.\n for (const [key, value] of Object.entries(changes)) {\n const field = declared.get(key)\n if (!field || field.type !== 'multiselect' || !field.options) continue\n if (!Array.isArray(value) || !value.every((entry) => typeof entry === 'string')) continue\n\n const stored = previousBag[key]\n changes[key] = [\n ...normalizeExclusiveSelection({\n next: value,\n previous: Array.isArray(stored) ? (stored as string[]) : [],\n options: field.options,\n }),\n ]\n }\n\n const entries = Object.entries(changes)\n\n const errors = new Map<string, readonly string[]>()\n\n for (const [key, value] of entries) {\n const field = declared.get(key)\n if (!field) {\n // * The compiler rejects this for a typed config, so this is the path a\n // * JavaScript caller or a widened config takes. It is reachable.\n errors.set(key, [`Unknown field: ${key}`])\n continue\n }\n // * An explicit `undefined` means \"unset this field\", not \"store the value\n // * `undefined`\" — so it skips the serializability gate. Validation below\n // * still sees the key as absent, which is what makes a required field\n // * report its own `required` message instead of a serialization one.\n if (value === undefined) continue\n if (!isSerializable({ value }))\n errors.set(key, [`${field.label} value is not JSON-serializable`])\n }\n\n // * The candidate bag is assembled whole and validated whole before a\n // * single byte is written, so a cross-field validator sees every key\n // * arriving in the same call rather than the stale stored one.\n const candidate: Record<string, JsonValue> = { ...previousBag, ...changes }\n // * Spreading `changes` copies the key across with an `undefined` value;\n // * deleting it is what makes the field genuinely absent for validators,\n // * `getAll()`, and `writeFields` (which drops undeclared-value fields).\n for (const [key, value] of entries) {\n if (value === undefined) delete candidate[key]\n }\n const snapshot = narrow(candidate)\n\n for (const [key, value] of entries) {\n const field = declared.get(key)\n if (!field || errors.has(key)) continue\n\n // * A throwing validator propagates out of `set` synchronously. No write\n // * has happened yet, so the batch is trivially atomic — which is why\n // * this is not wrapped in a try/catch.\n const messages = validateValue({\n field,\n value,\n values: snapshot,\n pattern: resolved.patterns.get(key),\n validator: validatorFor(field),\n })\n if (messages.length > 0) errors.set(key, messages)\n }\n\n if (errors.size > 0) {\n for (const [key, messages] of errors) state.errors.set(key, messages)\n // * Not one key of the patch is applied: storage is byte-identical and\n // * nothing is emitted.\n notify()\n return Promise.resolve({ ok: false, errors: freeze(errors) })\n }\n\n for (const [key] of entries) state.errors.delete(key)\n\n try {\n writeFields({\n storage: state.storage,\n formKey: resolved.formKey,\n fields: candidate,\n fieldDefinitions: resolved.fields,\n })\n state.bag = candidate\n } catch {\n return Promise.resolve(degradeAndReport({ entries }))\n }\n\n writeFormCookieData({\n projectId,\n formId: resolved.formKey,\n bag: candidate,\n fieldDefinitions: resolved.fields,\n })\n\n notify()\n\n // * Best-effort durable persistence, fired after the local write succeeds\n // * and swallowed whole. Every key here is declared (an unknown key would\n // * have failed validation above), so its config carries the ids.\n const persistedFields: PersistedField[] = entries.map(([key, value]) => {\n const field = declared.get(key)\n return {\n key,\n // * `PersistedField.value` is `JsonValue`, which has no `undefined`.\n // * `null` is this port's deletion marker; `savePartial` above carries\n // * the same deletion implicitly, as `snapshot` no longer has the key.\n value: value === undefined ? null : value,\n ...(field?.registryId === undefined ? {} : { registryId: field.registryId }),\n ...(field?.protocolFieldId === undefined ? {} : { protocolFieldId: field.protocolFieldId }),\n }\n })\n firePersistence(() => persistence.savePartial({ formKey: resolved.formKey, values: snapshot }))\n firePersistence(() =>\n persistence.saveFields({ formKey: resolved.formKey, fields: persistedFields }),\n )\n\n if (!analyticsInstance) return Promise.resolve({ ok: true, errors: noErrors() })\n\n return analyticsInstance\n .trackEvent([\n buildDataUpdatedEvent({ fields: resolved.fields, patch: changes, formId: schema.id }),\n ...buildFieldUpdatedEvents({ fields: resolved.fields, patch: changes, formId: schema.id }),\n ])\n .then(() => ({ ok: true, errors: noErrors() }))\n .catch((error: unknown) => ({\n // * `ok` stays true only for values that were persisted; here the write\n // * succeeded and only the emission failed.\n ok: true,\n errors: noErrors(),\n trackError: error,\n }))\n }\n\n const degradeAndReport = ({\n entries,\n }: {\n entries: [string, JsonValue][]\n }): SetResult<TSchema> => {\n state.storage = degradeToMemory({ storage: state.storage })\n\n const errors = new Map<string, readonly string[]>()\n for (const [key] of entries) {\n const label = declared.get(key)?.label ?? key\n const message = `${label} could not be persisted; this form is now in-memory only`\n errors.set(key, [message])\n state.errors.set(key, [message])\n }\n notify()\n return { ok: false, errors: freeze(errors) }\n }\n\n const get = <K extends FormFieldKey<TSchema>>(key: K): FormValues<TSchema>[K] | undefined => {\n const fieldKey = key as unknown as string\n // * An undeclared key is never handed back, even when the stored bag holds\n // * one: the config is the source of truth for what a form has.\n if (!declared.has(fieldKey)) return undefined\n\n // ! The one place this package asserts something it has not verified.\n // ! `localStorage` is untrusted, so a hand-edited or stale value comes back\n // ! typed as whatever the config declares. Do not \"fix\" it by returning\n // ! `JsonValue` — that drops the typing this surface exists to provide —\n // ! and do not add a runtime coercion, which would rewrite user data.\n return state.bag[fieldKey] as FormValues<TSchema>[K] | undefined\n }\n\n const getValueByProtocolFieldId = ((protocolFieldId: string): JsonValue | undefined => {\n const fieldKey = keyByProtocolFieldId.get(protocolFieldId)\n if (fieldKey === undefined) return undefined\n return get(fieldKey as FormFieldKey<TSchema>) as JsonValue | undefined\n }) as FormInstance<TSchema>['getValueByProtocolFieldId']\n\n const getAll = (): Partial<FormValues<TSchema>> =>\n narrow(state.bag) as Partial<FormValues<TSchema>>\n\n // ! Not `async`, for the same reason as `set`.\n const submit = (): Promise<SubmitResult<TSchema>> => {\n const snapshot = narrow(readBag())\n const values = snapshot as Partial<FormValues<TSchema>>\n const errors = validateDeclaredFields({\n snapshot,\n keys: resolved.fields.map((field) => field.key),\n })\n\n if (errors.size > 0) {\n replaceErrors(errors)\n notify()\n // * A `form:submitted` row must mean a real submission, so nothing is sent.\n return Promise.resolve({ ok: false, errors: freeze(errors), values })\n }\n\n state.errors.clear()\n notify()\n\n // * Best-effort full-submission save, swallowed like the `set` persistence.\n firePersistence(() =>\n persistence.saveSubmission({ formKey: resolved.formKey, values: snapshot }),\n )\n\n if (!analyticsInstance) return Promise.resolve({ ok: true, errors: noErrors(), values })\n\n return analyticsInstance\n .trackEvent([{ event_name: 'form:submitted', form_key: resolved.formKey }])\n .then(() => ({ ok: true, errors: noErrors(), values }))\n .catch((error: unknown) => ({\n ok: true,\n errors: noErrors(),\n values,\n trackError: error,\n }))\n }\n\n // ! Not `async`, for the same reason as `set`.\n const validate = (patch?: Partial<FormValues<TSchema>>): Promise<ValidateResult<TSchema>> => {\n if (patch === undefined) {\n const snapshot = narrow(readBag())\n const values = snapshot as Partial<FormValues<TSchema>>\n const errors = validateDeclaredFields({\n snapshot,\n keys: resolved.fields.map((field) => field.key),\n })\n\n if (errors.size > 0) {\n replaceErrors(errors)\n notify()\n return Promise.resolve({ ok: false, errors: freeze(errors), values })\n }\n\n state.errors.clear()\n notify()\n return Promise.resolve({ ok: true, errors: noErrors(), values })\n }\n\n const changes = patch as Record<string, JsonValue>\n const entries = Object.entries(changes)\n const values = narrow({ ...readBag(), ...changes }) as Partial<FormValues<TSchema>>\n\n // * Mirrors `set({})`: nothing to validate, no error-state change.\n if (entries.length === 0) return Promise.resolve({ ok: true, errors: noErrors(), values })\n\n const errors = new Map<string, readonly string[]>()\n const patchKeys = entries.map(([key]) => key)\n\n for (const [key, value] of entries) {\n const field = declared.get(key)\n if (!field) {\n errors.set(key, [`Unknown field: ${key}`])\n continue\n }\n if (!isSerializable({ value }))\n errors.set(key, [`${field.label} value is not JSON-serializable`])\n }\n\n const snapshot = narrow({ ...readBag(), ...changes })\n\n for (const [key] of entries) {\n if (errors.has(key)) continue\n\n const field = declared.get(key)\n if (!field) continue\n\n const messages = validateValue({\n field,\n value: snapshot[key],\n values: snapshot,\n pattern: resolved.patterns.get(key),\n validator: validatorFor(field),\n })\n if (messages.length > 0) errors.set(key, messages)\n }\n\n applyPatchValidationErrors({ errors, patchKeys })\n\n if (errors.size > 0) {\n notify()\n return Promise.resolve({ ok: false, errors: freeze(errors), values })\n }\n\n notify()\n return Promise.resolve({ ok: true, errors: noErrors(), values })\n }\n\n // ! All-or-nothing within the form: undeclared field keys in this form's bag\n // ! go too, making this the one operation that does not preserve them. Every\n // ! other form key survives — removing the whole entry would wipe every form\n // ! on the origin.\n const clear = (): void => {\n removeFields({ storage: state.storage, formKey: resolved.formKey })\n state.bag = {}\n state.errors.clear()\n clearFormCookieData({ projectId, formId: resolved.formKey })\n notify()\n }\n\n const subscribe = (listener: () => void): (() => void) => {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n }\n\n const uploadFile = async ({\n key,\n file,\n fileName,\n }: {\n key: FormFieldKey<TSchema>\n file: Blob\n fileName?: string\n }): Promise<FormFileRef> => {\n const field = declared.get(key)\n if (!field) throw new FormsError(`Unknown field: ${key}`)\n if (field.type !== 'file') {\n throw new FormsError(`Field \"${key}\" is not a file field`)\n }\n if (!uploadConfig) {\n throw new FormsError(\n 'File uploads require a valid publishable key and initialized Embeddables core instance.',\n )\n }\n\n const rules = field.validations\n const contentType = file.type || 'application/octet-stream'\n const byteLength = file.size\n\n if (byteLength > FORM_UPLOAD_MAX_BYTES) {\n throw new FormsError('Maximum upload size is 25 MiB.')\n }\n if (rules?.maxSize !== undefined && byteLength > rules.maxSize) {\n throw new FormsError(`${field.label} must be at most ${rules.maxSize} bytes`)\n }\n if (\n rules?.accept !== undefined &&\n !contentTypeMatchesAccept({ contentType, accept: rules.accept })\n ) {\n throw new FormsError(`${field.label} must be one of the allowed file types`)\n }\n\n return uploadFormFile(uploadConfig, {\n formId: resolved.formKey,\n fieldKey: key,\n file,\n fileName,\n })\n }\n\n return {\n key: schema.id,\n set,\n get,\n getValueByProtocolFieldId,\n getAll,\n submit,\n validate,\n errors: () => freeze(state.errors),\n clear,\n uploadFile,\n subscribe,\n }\n}\n"],"mappings":"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"}