@formhaus/core 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cloud.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { b as FormDefinition } from './definition-H3WUR596.js';
1
+ import { b as FormDefinition } from './definition-BAHgjY7x.js';
2
2
 
3
3
  type CloudAction = 'retry' | 'reload' | null;
4
4
 
package/dist/cloud.js CHANGED
@@ -1,3 +1,3 @@
1
- var E="This form was updated. Please review your answers and submit again.",R="Please answer again.",h={message:"Something went wrong. Try again.",action:"retry"},j={message:"Could not send. Try again.",action:"retry"},w={message:"Could not send. Reload the page and try again.",action:"reload"},v={network:{message:"You are offline. Check your connection and try again.",action:"retry"},invalid:{message:"Some answers were not accepted.",action:null},rate_limited:{message:"Too many attempts. Wait a minute and try again.",action:"retry"},limit_reached:{message:"This form is not accepting responses right now.",action:null},not_found:{message:"This form no longer exists.",action:null},paused:{message:"This form is paused.",action:null},expired:{message:"This form has expired.",action:null},body_too_large:{message:"Your answers are too long. Shorten them and try again.",action:null},idempotency_key_reused:{message:"Your answers changed while sending. Try again.",action:"retry"},form_updated:{message:"This form was updated. Reload the page.",action:"reload"},malformed_body:w,unsupported_media_type:w,invalid_idempotency_key:w,unavailable:h,internal:h},M={0:"network",400:"malformed_body",402:"limit_reached",404:"not_found",410:"expired",413:"body_too_large",422:"invalid",429:"rate_limited"};function g(n,e=""){let o=v[e]??v[M[n]??""];return o||(n>=500?h:j)}var d=class extends Error{constructor(e,o,i,t,r){let a=g(e,o);super(a.message),this.name="CloudError",this.status=e,this.code=o,this.errors=i,this.version=t,this.action=a.action,this.cause=r;}get retryable(){return this.action==="retry"}};function F(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}function y(n){return new d(0,"network",void 0,void 0,n)}async function k(n){let e=await n.json().catch(()=>null);if(!F(e))return new d(n.status,"unknown");let o=F(e.errors)?e.errors:void 0,i=typeof e.version=="number"&&Number.isInteger(e.version)?e.version:void 0;return new d(n.status,typeof e.error=="string"?e.error:"unknown",o,i)}var x='input:not([type="hidden"]):not(:disabled), select:not(:disabled), textarea:not(:disabled), button:not(:disabled)';function A(n,e){e.scrollIntoView?.({block:"center"}),n.focus({preventScroll:true});}function q(n,e){if(e)return A(e,e);let o=n?.querySelector('[aria-invalid="true"]');o&&A(o.matches(x)?o:o.querySelector(x)??o,o);}function _(){if(typeof crypto.randomUUID=="function")return crypto.randomUUID();let n=crypto.getRandomValues(new Uint8Array(16));n[6]=n[6]&15|64,n[8]=n[8]&63|128;let e=Array.from(n,o=>o.toString(16).padStart(2,"0")).join("");return `${e.slice(0,8)}-${e.slice(8,12)}-${e.slice(12,16)}-${e.slice(16,20)}-${e.slice(20)}`}var B="https://api.formhaus.dev",G="Formhaus-Version";function D({id:n,apiBase:e=B}){return `${e.replace(/\/+$/,"")}/f/${encodeURIComponent(n)}`}async function O(n,e){try{return await fetch(n,e)}catch(o){throw e?.signal?.aborted?o:y(o)}}function Y(n){let e=Number(n.headers?.get(G));return Number.isInteger(e)&&e>0?e:void 0}async function b(n,e,o){let i=o===void 0?"":`?v=${o}`,t=await O(`${D(n)}/definition${i}`,{signal:e});if(!t.ok)throw await k(t);return {definition:await t.json(),version:Y(t)}}async function W(n,e){return (await b(n,e)).definition}function m(n){let e=null;return async(o,i=[])=>{let t=JSON.stringify({values:o,skippedSteps:i,version:n.version});e?.body!==t&&(e={body:t,key:_()});let r=await O(D(n),{method:"POST",headers:{"Content-Type":"application/json","Idempotency-Key":e.key},body:t});if(!r.ok)throw await k(r);return e=null,await r.json()}}var H={multiselect:"list",checkbox:"flag",switch:"flag",number:"number",file:"file"};function C(n){return [...n.fields??[],...(n.steps??[]).flatMap(e=>e.fields)]}function S(n){return H[n.type]??"text"}function K(n,e){if(!Array.isArray(n.options))return true;let o=new Set(n.options.map(i=>String(i.value)));return (Array.isArray(e)?e:[e]).every(i=>o.has(String(i)))}function L(n,e,o){if(n&&S(n)!==S(e))return false;switch(S(e)){case "list":return Array.isArray(o);case "flag":return typeof o=="boolean";case "number":return typeof o=="number";case "text":return typeof o=="string";default:return n!==void 0}}function P(n,e,o){let i=new Map(o?C(o).map(r=>[r.key,r]):[]),t={};for(let r of C(e)){let a=n[r.key];a!=null&&L(i.get(r.key),r,a)&&K(r,a)&&(t[r.key]=a);}return t}function T(n,e){let o=new Set(C(n).map(r=>r.key)),i={},t=new Set;for(let[r,a]of Object.entries(e))o.has(r)?i[r]=a:t.add(a);return {fields:i,other:[...t]}}function N(n,e,o,i){let t={};for(let r of C(n)){let a=e[r.key]!==void 0&&e[r.key]!==null&&e[r.key]!=="";a&&!(r.key in o)?t[r.key]=R:i[r.key]&&(!a||r.key in o)&&(t[r.key]=i[r.key]);}return t}var V={definition:null,version:null,loadError:null,errors:{},notice:null,initialValues:null,revision:0,attempt:0,done:false};function I(n,e=[]){return {tone:"error",message:n.message,action:n.action,details:e}}function J(n,e){let o=new AbortController,i=m(n),t=()=>!o.signal.aborted,r=V,a=s=>{r={...r,...s},e.onChange(r);},p=s=>a({...s,attempt:r.attempt+1});async function $(s,c){let u=r.definition;try{let l=await b(n,o.signal,s.version);if(!t())return;if(l.version===void 0||l.version<(s.version??0))throw new d(409,"form_updated");let f=u?P(c,l.definition,u):{};i=m({...n,version:l.version}),p({definition:l.definition,version:l.version,initialValues:f,revision:r.revision+1,errors:N(l.definition,c,f,s.errors??{}),notice:{tone:"info",message:E,action:null,details:[]}});}catch{t()&&p({notice:{tone:"error",...g(409,"form_updated"),details:[]}});}}async function U(s,c){let u=s instanceof d?s:y(s);if(u.code==="form_updated"&&u.version!==void 0)return $(u,c);if(u.status!==422)return p({notice:I(u)});let l=u.errors??{},f=r.definition?T(r.definition,l):{fields:l,other:[]};p({errors:f.fields,notice:f.other.length>0?I(u,f.other):null});}return {start(){b(n,o.signal).then(s=>{t()&&(i=m({...n,version:s.version}),a({definition:s.definition,version:s.version??null}));},s=>{t()&&(a({loadError:s}),e.onError?.(s));});},dispose(){o.abort();},async submit(s,c){r.notice&&a({notice:null});try{let u=await i(s,c);if(!t())return;a({done:!0}),e.onSuccess?.(u);}catch(u){throw t()&&(await U(u,s),e.onError?.(u)),u}}}}
2
- export{d as CloudError,J as createCloudController,m as createSubmitter,W as fetchDefinition,q as focusProblem,V as initialCloudState};//# sourceMappingURL=cloud.js.map
1
+ var F="This form was updated. Please review your answers and submit again.",x="Please answer again.",g={message:"Something went wrong. Try again.",action:"retry"},B={message:"Could not send. Try again.",action:"retry"},h={message:"Could not send. Reload the page and try again.",action:"reload"},E={network:{message:"Could not reach the form server. Check your connection and try again.",action:"retry"},timeout:{message:"The form server did not respond. Try again.",action:"retry"},invalid:{message:"Some answers were not accepted.",action:null},rate_limited:{message:"Too many attempts. Wait a minute and try again.",action:"retry"},limit_reached:{message:"This form is not accepting responses right now.",action:null},not_found:{message:"This form no longer exists.",action:null},paused:{message:"This form is paused.",action:null},expired:{message:"This form has expired.",action:null},body_too_large:{message:"Your answers are too long. Shorten them and try again.",action:null},idempotency_key_reused:{message:"Your answers changed while sending. Try again.",action:"retry"},form_updated:{message:"This form was updated. Reload the page.",action:"reload"},malformed_body:h,unsupported_media_type:h,invalid_idempotency_key:h,invalid_response:g,unavailable:g,internal:g},G={0:"network",400:"malformed_body",402:"limit_reached",404:"not_found",409:"paused",410:"expired",413:"body_too_large",422:"invalid",429:"rate_limited"};function y(n,e=""){let r=E[e]??E[G[n]??""];return r||(n>=500?g:B)}var d=class extends Error{constructor(e,r,i,t,o){let s=y(e,r);super(s.message),this.name="CloudError",this.status=e,this.code=r,this.errors=i,this.version=t,this.action=s.action,this.cause=o;}get retryable(){return this.action==="retry"}};function R(n){return n!==null&&typeof n=="object"&&!Array.isArray(n)}var L=new Map([["Form paused","paused"],["Form not found","not_found"],["Form expired","expired"],["internal error","internal"]]);function b(n,e="network"){return new d(0,e,void 0,void 0,n)}async function k(n){try{return await n.json()}catch(e){throw new d(n.status,"invalid_response",void 0,void 0,e)}}async function S(n){let e=await n.json().catch(()=>null);if(!R(e))return new d(n.status,"unknown");let r=R(e.errors)?e.errors:void 0,i=typeof e.version=="number"&&Number.isInteger(e.version)?e.version:void 0,t=typeof e.error=="string"?L.get(e.error)??e.error:"unknown";return new d(n.status,t,r,i)}var _='input:not([type="hidden"]):not(:disabled), select:not(:disabled), textarea:not(:disabled), button:not(:disabled)';function A(n,e){e.scrollIntoView?.({block:"center"}),n.focus({preventScroll:true});}function j(n,e){if(e)return A(e,e);let r=n?.querySelector('[aria-invalid="true"]');r&&A(r.matches(_)?r:r.querySelector(_)??r,r);}function O(){if(typeof crypto.randomUUID=="function")return crypto.randomUUID();let n=crypto.getRandomValues(new Uint8Array(16));n[6]=n[6]&15|64,n[8]=n[8]&63|128;let e=Array.from(n,r=>r.toString(16).padStart(2,"0")).join("");return `${e.slice(0,8)}-${e.slice(8,12)}-${e.slice(12,16)}-${e.slice(16,20)}-${e.slice(20)}`}var q="https://api.formhaus.dev",Y="Formhaus-Version";function D({id:n,apiBase:e=q}){return `${e.replace(/\/+$/,"")}/f/${encodeURIComponent(n)}`}var J=15e3,W=3e4;async function T(n,e,r={},i){let t=new AbortController,o=()=>t.abort(),s=setTimeout(o,e);i?.aborted&&o(),i?.addEventListener("abort",o);try{return await fetch(n,{...r,signal:t.signal})}catch(c){throw i?.aborted?c:b(c,t.signal.aborted?"timeout":"network")}finally{clearTimeout(s),i?.removeEventListener("abort",o);}}function H(n){let e=Number(n.headers?.get(Y));return Number.isInteger(e)&&e>0?e:void 0}async function C(n,e,r){let i=r===void 0?"":`?v=${r}`,t=await T(`${D(n)}/definition${i}`,J,{},e);if(!t.ok)throw await S(t);return {definition:await k(t),version:H(t)}}async function K(n,e){return (await C(n,e)).definition}function p(n){let e=null;return async(r,i=[])=>{let t=JSON.stringify({values:r,skippedSteps:i,version:n.version});e?.body!==t&&(e={body:t,key:O()});let o=await T(D(n),W,{method:"POST",headers:{"Content-Type":"application/json","Idempotency-Key":e.key},body:t});if(!o.ok)throw await S(o);let s=await k(o);return e=null,s}}var z={multiselect:"list",checkbox:"flag",switch:"flag",number:"number",file:"file"};function w(n){return [...n.fields??[],...(n.steps??[]).flatMap(e=>e.fields)]}function v(n){return z[n.type]??"text"}function Q(n,e){if(!Array.isArray(n.options))return true;let r=new Set(n.options.map(i=>String(i.value)));return (Array.isArray(e)?e:[e]).every(i=>r.has(String(i)))}function X(n,e,r){if(n&&v(n)!==v(e))return false;switch(v(e)){case "list":return Array.isArray(r);case "flag":return typeof r=="boolean";case "number":return typeof r=="number";case "text":return typeof r=="string";default:return n!==void 0}}function P(n,e,r){let i=new Map(r?w(r).map(o=>[o.key,o]):[]),t={};for(let o of w(e)){let s=n[o.key];s!=null&&X(i.get(o.key),o,s)&&Q(o,s)&&(t[o.key]=s);}return t}function I(n,e){let r=new Set(w(n).map(o=>o.key)),i={},t=new Set;for(let[o,s]of Object.entries(e))r.has(o)?i[o]=s:t.add(s);return {fields:i,other:[...t]}}function N(n,e,r,i){let t={};for(let o of w(n)){let s=e[o.key]!==void 0&&e[o.key]!==null&&e[o.key]!=="";s&&!(o.key in r)?t[o.key]=x:i[o.key]&&(!s||o.key in r)&&(t[o.key]=i[o.key]);}return t}var V={definition:null,version:null,loadError:null,errors:{},notice:null,initialValues:null,revision:0,attempt:0,done:false};function U(n,e=[]){return {tone:"error",message:n.message,action:n.action,details:e}}function Z(n,e){let r=new AbortController,i=p(n),t=()=>!r.signal.aborted,o=V,s=a=>{o={...o,...a},e.onChange(o);},c=a=>s({...a,attempt:o.attempt+1});async function $(a,f){let u=o.definition;try{let l=await C(n,r.signal,a.version);if(!t())return;if(l.version===void 0||l.version<(a.version??0))throw new d(409,"form_updated");let m=u?P(f,l.definition,u):{};i=p({...n,version:l.version}),c({definition:l.definition,version:l.version,initialValues:m,revision:o.revision+1,errors:N(l.definition,f,m,a.errors??{}),notice:{tone:"info",message:F,action:null,details:[]}});}catch{t()&&c({notice:{tone:"error",...y(409,"form_updated"),details:[]}});}}async function M(a,f){let u=a instanceof d?a:b(a);if(u.code==="form_updated"&&u.version!==void 0)return $(u,f);if(u.status!==422)return c({notice:U(u)});let l=u.errors??{},m=o.definition?I(o.definition,l):{fields:l,other:[]};c({errors:m.fields,notice:m.other.length>0?U(u,m.other):null});}return {start(){o.loadError&&s({loadError:null}),C(n,r.signal).then(a=>{t()&&(i=p({...n,version:a.version}),s({definition:a.definition,version:a.version??null}));},a=>{t()&&(s({loadError:a}),e.onError?.(a));});},dispose(){r.abort();},async submit(a,f){o.notice&&s({notice:null});try{let u=await i(a,f);if(!t())return;s({done:!0}),e.onSuccess?.(u);}catch(u){throw t()&&(await M(u,a),e.onError?.(u)),u}}}}
2
+ export{d as CloudError,Z as createCloudController,p as createSubmitter,K as fetchDefinition,j as focusProblem,V as initialCloudState};//# sourceMappingURL=cloud.js.map
3
3
  //# sourceMappingURL=cloud.js.map
package/dist/cloud.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cloud/failures.ts","../src/cloud/error.ts","../src/cloud/focus.ts","../src/cloud/uuid.ts","../src/cloud/client.ts","../src/cloud/carry.ts","../src/cloud/controller.ts"],"names":["FORM_UPDATED_MESSAGE","ANSWER_AGAIN","SERVER","GENERIC","BROKEN","BY_CODE","CODE_BY_STATUS","failureFor","status","code","known","CloudError","errors","version","cause","failure","isRecord","value","networkError","toCloudError","response","body","CONTROLS","reveal","target","scrollTo","focusProblem","root","notice","invalid","randomId","bytes","hex","byte","DEFAULT_API_BASE","VERSION_HEADER","endpoint","id","apiBase","send","url","init","error","versionOf","fetchPublished","options","signal","after","query","fetchDefinition","createSubmitter","pending","values","skippedSteps","SHAPES","fieldsOf","definition","step","shapeOf","field","allowed","option","item","fits","previous","next","carryValues","before","kept","splitErrors","fields","other","key","message","updatedErrors","sent","rejected","answered","initialCloudState","errorNotice","details","createCloudController","callbacks","controller","submitter","alive","state","update","patch","report","refresh","fail","cloud","split","published","submission"],"mappings":"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","file":"cloud.js","sourcesContent":["export type CloudAction = 'retry' | 'reload' | null;\n\nexport interface CloudFailure {\n message: string;\n action: CloudAction;\n}\n\nexport const FORM_UPDATED_MESSAGE = 'This form was updated. Please review your answers and submit again.';\nexport const ANSWER_AGAIN = 'Please answer again.';\n\nconst SERVER: CloudFailure = { message: 'Something went wrong. Try again.', action: 'retry' };\nconst GENERIC: CloudFailure = { message: 'Could not send. Try again.', action: 'retry' };\nconst BROKEN: CloudFailure = { message: 'Could not send. Reload the page and try again.', action: 'reload' };\n\nconst BY_CODE: Record<string, CloudFailure> = {\n network: { message: 'You are offline. Check your connection and try again.', action: 'retry' },\n invalid: { message: 'Some answers were not accepted.', action: null },\n rate_limited: { message: 'Too many attempts. Wait a minute and try again.', action: 'retry' },\n limit_reached: { message: 'This form is not accepting responses right now.', action: null },\n not_found: { message: 'This form no longer exists.', action: null },\n paused: { message: 'This form is paused.', action: null },\n expired: { message: 'This form has expired.', action: null },\n body_too_large: { message: 'Your answers are too long. Shorten them and try again.', action: null },\n idempotency_key_reused: { message: 'Your answers changed while sending. Try again.', action: 'retry' },\n form_updated: { message: 'This form was updated. Reload the page.', action: 'reload' },\n malformed_body: BROKEN,\n unsupported_media_type: BROKEN,\n invalid_idempotency_key: BROKEN,\n unavailable: SERVER,\n internal: SERVER,\n};\n\nconst CODE_BY_STATUS: Record<number, string> = {\n 0: 'network',\n 400: 'malformed_body',\n 402: 'limit_reached',\n 404: 'not_found',\n 410: 'expired',\n 413: 'body_too_large',\n 422: 'invalid',\n 429: 'rate_limited',\n};\n\nexport function failureFor(status: number, code = ''): CloudFailure {\n const known = BY_CODE[code] ?? BY_CODE[CODE_BY_STATUS[status] ?? ''];\n if (known) return known;\n return status >= 500 ? SERVER : GENERIC;\n}\n","import { failureFor, type CloudAction } from './failures';\n\nexport class CloudError extends Error {\n readonly status: number;\n readonly code: string;\n readonly errors?: Record<string, string>;\n readonly version?: number;\n readonly action: CloudAction;\n readonly cause?: unknown;\n\n constructor(status: number, code: string, errors?: Record<string, string>, version?: number, cause?: unknown) {\n const failure = failureFor(status, code);\n super(failure.message);\n this.name = 'CloudError';\n this.status = status;\n this.code = code;\n this.errors = errors;\n this.version = version;\n this.action = failure.action;\n this.cause = cause;\n }\n\n get retryable(): boolean {\n return this.action === 'retry';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nexport function networkError(cause: unknown): CloudError {\n return new CloudError(0, 'network', undefined, undefined, cause);\n}\n\nexport async function toCloudError(response: CloudResponse): Promise<CloudError> {\n const body: unknown = await response.json().catch(() => null);\n if (!isRecord(body)) return new CloudError(response.status, 'unknown');\n const errors = isRecord(body.errors) ? body.errors as Record<string, string> : undefined;\n const version = typeof body.version === 'number' && Number.isInteger(body.version) ? body.version : undefined;\n return new CloudError(response.status, typeof body.error === 'string' ? body.error : 'unknown', errors, version);\n}\n","interface Searchable {\n querySelector(selector: string): unknown;\n}\n\ninterface Focusable extends Searchable {\n focus(options?: { preventScroll?: boolean }): void;\n scrollIntoView?(options?: { block?: 'center' | 'nearest' }): void;\n matches(selector: string): boolean;\n}\n\nconst CONTROLS = 'input:not([type=\"hidden\"]):not(:disabled), select:not(:disabled), textarea:not(:disabled), button:not(:disabled)';\n\nfunction reveal(target: Focusable, scrollTo: Focusable): void {\n scrollTo.scrollIntoView?.({ block: 'center' });\n target.focus({ preventScroll: true });\n}\n\nexport function focusProblem(root: Searchable | null, notice?: Focusable | null): void {\n if (notice) return reveal(notice, notice);\n const invalid = root?.querySelector('[aria-invalid=\"true\"]') as Focusable | null | undefined;\n if (invalid) reveal(invalid.matches(CONTROLS) ? invalid : (invalid.querySelector(CONTROLS) as Focusable | null) ?? invalid, invalid);\n}\n","export function randomId(): string {\n if (typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n bytes[6] = (bytes[6] & 15) | 64;\n bytes[8] = (bytes[8] & 63) | 128;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","import type { FormDefinition } from '../types';\nimport { networkError, toCloudError } from './error';\nimport { randomId } from './uuid';\n\nconst DEFAULT_API_BASE = 'https://api.formhaus.dev';\nconst VERSION_HEADER = 'Formhaus-Version';\n\nexport interface CloudSubmission {\n id: string;\n values: Record<string, unknown>;\n}\n\nexport interface CloudClientOptions {\n id: string;\n apiBase?: string;\n}\n\nexport interface PublishedDefinition {\n definition: FormDefinition;\n version?: number;\n}\n\nfunction endpoint({ id, apiBase = DEFAULT_API_BASE }: CloudClientOptions): string {\n return `${apiBase.replace(/\\/+$/, '')}/f/${encodeURIComponent(id)}`;\n}\n\nasync function send(url: string, init?: CloudRequestInit): Promise<CloudResponse> {\n try {\n return await fetch(url, init);\n } catch (error) {\n if (init?.signal?.aborted) throw error;\n throw networkError(error);\n }\n}\n\nfunction versionOf(response: CloudResponse): number | undefined {\n const version = Number(response.headers?.get(VERSION_HEADER));\n return Number.isInteger(version) && version > 0 ? version : undefined;\n}\n\nexport async function fetchPublished(options: CloudClientOptions, signal?: AbortSignal, after?: number): Promise<PublishedDefinition> {\n const query = after === undefined ? '' : `?v=${after}`;\n const response = await send(`${endpoint(options)}/definition${query}`, { signal });\n if (!response.ok) throw await toCloudError(response);\n return { definition: await response.json() as FormDefinition, version: versionOf(response) };\n}\n\nexport async function fetchDefinition(options: CloudClientOptions, signal?: AbortSignal): Promise<FormDefinition> {\n return (await fetchPublished(options, signal)).definition;\n}\n\nexport interface CloudSubmitterOptions extends CloudClientOptions {\n version?: number;\n}\n\nexport function createSubmitter(options: CloudSubmitterOptions) {\n let pending: { body: string; key: string } | null = null;\n\n return async (values: Record<string, unknown>, skippedSteps: string[] = []): Promise<CloudSubmission> => {\n const body = JSON.stringify({ values, skippedSteps, version: options.version });\n if (pending?.body !== body) pending = { body, key: randomId() };\n const response = await send(endpoint(options), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Idempotency-Key': pending.key },\n body,\n });\n if (!response.ok) throw await toCloudError(response);\n pending = null;\n return await response.json() as CloudSubmission;\n };\n}\n","import type { FormDefinition, FormField } from '../types';\nimport { ANSWER_AGAIN } from './failures';\n\nconst SHAPES: Record<string, string> = {\n multiselect: 'list',\n checkbox: 'flag',\n switch: 'flag',\n number: 'number',\n file: 'file',\n};\n\nfunction fieldsOf(definition: FormDefinition): FormField[] {\n return [...(definition.fields ?? []), ...(definition.steps ?? []).flatMap((step) => step.fields)];\n}\n\nfunction shapeOf(field: FormField): string {\n return SHAPES[field.type] ?? 'text';\n}\n\nfunction allowed(field: FormField, value: unknown): boolean {\n if (!Array.isArray(field.options)) return true;\n const options = new Set(field.options.map((option) => String(option.value)));\n return (Array.isArray(value) ? value : [value]).every((item) => options.has(String(item)));\n}\n\nfunction fits(previous: FormField | undefined, next: FormField, value: unknown): boolean {\n if (previous && shapeOf(previous) !== shapeOf(next)) return false;\n switch (shapeOf(next)) {\n case 'list': return Array.isArray(value);\n case 'flag': return typeof value === 'boolean';\n case 'number': return typeof value === 'number';\n case 'text': return typeof value === 'string';\n default: return previous !== undefined;\n }\n}\n\nexport function carryValues(\n values: Record<string, unknown>,\n next: FormDefinition,\n previous?: FormDefinition,\n): Record<string, unknown> {\n const before = new Map(previous ? fieldsOf(previous).map((field) => [field.key, field]) : []);\n const kept: Record<string, unknown> = {};\n for (const field of fieldsOf(next)) {\n const value = values[field.key];\n if (value === undefined || value === null) continue;\n if (fits(before.get(field.key), field, value) && allowed(field, value)) kept[field.key] = value;\n }\n return kept;\n}\n\nexport function splitErrors(definition: FormDefinition, errors: Record<string, string>) {\n const known = new Set(fieldsOf(definition).map((field) => field.key));\n const fields: Record<string, string> = {};\n const other = new Set<string>();\n for (const [key, message] of Object.entries(errors)) {\n if (known.has(key)) fields[key] = message;\n else other.add(message);\n }\n return { fields, other: [...other] };\n}\n\nexport function updatedErrors(\n next: FormDefinition,\n sent: Record<string, unknown>,\n kept: Record<string, unknown>,\n rejected: Record<string, string>,\n): Record<string, string> {\n const errors: Record<string, string> = {};\n for (const field of fieldsOf(next)) {\n const answered = sent[field.key] !== undefined && sent[field.key] !== null && sent[field.key] !== '';\n if (answered && !(field.key in kept)) errors[field.key] = ANSWER_AGAIN;\n else if (rejected[field.key] && (!answered || field.key in kept)) errors[field.key] = rejected[field.key];\n }\n return errors;\n}\n","import type { FormDefinition } from '../types';\nimport { carryValues, splitErrors, updatedErrors } from './carry';\nimport { createSubmitter, fetchPublished, type CloudClientOptions, type CloudSubmission } from './client';\nimport { CloudError, networkError } from './error';\nimport { FORM_UPDATED_MESSAGE, failureFor, type CloudAction } from './failures';\n\nexport interface CloudNotice {\n tone: 'error' | 'info';\n message: string;\n action: CloudAction;\n details: string[];\n}\n\nexport interface CloudState {\n definition: FormDefinition | null;\n version: number | null;\n loadError: Error | null;\n errors: Record<string, string>;\n notice: CloudNotice | null;\n initialValues: Record<string, unknown> | null;\n revision: number;\n attempt: number;\n done: boolean;\n}\n\nexport interface CloudCallbacks {\n onChange(state: CloudState): void;\n onSuccess?(submission: CloudSubmission): void;\n onError?(error: unknown): void;\n}\n\nexport const initialCloudState: CloudState = {\n definition: null, version: null, loadError: null, errors: {}, notice: null,\n initialValues: null, revision: 0, attempt: 0, done: false,\n};\n\nfunction errorNotice(error: CloudError, details: string[] = []): CloudNotice {\n return { tone: 'error', message: error.message, action: error.action, details };\n}\n\nexport function createCloudController(options: CloudClientOptions, callbacks: CloudCallbacks) {\n const controller = new AbortController();\n let submitter = createSubmitter(options);\n const alive = () => !controller.signal.aborted;\n let state = initialCloudState;\n const update = (patch: Partial<CloudState>) => {\n state = { ...state, ...patch };\n callbacks.onChange(state);\n };\n const report = (patch: Partial<CloudState>) => update({ ...patch, attempt: state.attempt + 1 });\n\n async function refresh(error: CloudError, sent: Record<string, unknown>) {\n const before = state.definition;\n try {\n const next = await fetchPublished(options, controller.signal, error.version);\n if (!alive()) return;\n if (next.version === undefined || next.version < (error.version ?? 0)) throw new CloudError(409, 'form_updated');\n const kept = before ? carryValues(sent, next.definition, before) : {};\n submitter = createSubmitter({ ...options, version: next.version });\n report({\n definition: next.definition,\n version: next.version,\n initialValues: kept,\n revision: state.revision + 1,\n errors: updatedErrors(next.definition, sent, kept, error.errors ?? {}),\n notice: { tone: 'info', message: FORM_UPDATED_MESSAGE, action: null, details: [] },\n });\n } catch {\n if (alive()) report({ notice: { tone: 'error', ...failureFor(409, 'form_updated'), details: [] } });\n }\n }\n\n async function fail(error: unknown, values: Record<string, unknown>) {\n const cloud = error instanceof CloudError ? error : networkError(error);\n if (cloud.code === 'form_updated' && cloud.version !== undefined) return refresh(cloud, values);\n if (cloud.status !== 422) return report({ notice: errorNotice(cloud) });\n const errors = cloud.errors ?? {};\n const split = state.definition ? splitErrors(state.definition, errors) : { fields: errors, other: [] };\n report({ errors: split.fields, notice: split.other.length > 0 ? errorNotice(cloud, split.other) : null });\n }\n\n return {\n start() {\n fetchPublished(options, controller.signal).then(\n (published) => {\n if (!alive()) return;\n submitter = createSubmitter({ ...options, version: published.version });\n update({ definition: published.definition, version: published.version ?? null });\n },\n (error: Error) => {\n if (!alive()) return;\n update({ loadError: error });\n callbacks.onError?.(error);\n },\n );\n },\n dispose() {\n controller.abort();\n },\n async submit(values: Record<string, unknown>, skippedSteps?: string[]) {\n if (state.notice) update({ notice: null });\n try {\n const submission = await submitter(values, skippedSteps);\n if (!alive()) return;\n update({ done: true });\n callbacks.onSuccess?.(submission);\n } catch (error) {\n if (!alive()) throw error;\n await fail(error, values);\n callbacks.onError?.(error);\n throw error;\n }\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/cloud/failures.ts","../src/cloud/error.ts","../src/cloud/focus.ts","../src/cloud/uuid.ts","../src/cloud/client.ts","../src/cloud/carry.ts","../src/cloud/controller.ts"],"names":["FORM_UPDATED_MESSAGE","ANSWER_AGAIN","SERVER","GENERIC","BROKEN","BY_CODE","CODE_BY_STATUS","failureFor","status","code","known","CloudError","errors","version","cause","failure","isRecord","value","LEGACY_CODES","networkError","readJson","response","error","toCloudError","body","CONTROLS","reveal","target","scrollTo","focusProblem","root","notice","invalid","randomId","bytes","hex","byte","DEFAULT_API_BASE","VERSION_HEADER","endpoint","id","apiBase","LOAD_TIMEOUT","SUBMIT_TIMEOUT","send","url","timeout","init","signal","controller","abort","timer","versionOf","fetchPublished","options","after","query","fetchDefinition","createSubmitter","pending","values","skippedSteps","submission","SHAPES","fieldsOf","definition","step","shapeOf","field","allowed","option","item","fits","previous","next","carryValues","before","kept","splitErrors","fields","other","key","message","updatedErrors","sent","rejected","answered","initialCloudState","errorNotice","details","createCloudController","callbacks","submitter","alive","state","update","patch","report","refresh","fail","cloud","split","published"],"mappings":"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","file":"cloud.js","sourcesContent":["export type CloudAction = 'retry' | 'reload' | null;\n\nexport interface CloudFailure {\n message: string;\n action: CloudAction;\n}\n\nexport const FORM_UPDATED_MESSAGE = 'This form was updated. Please review your answers and submit again.';\nexport const ANSWER_AGAIN = 'Please answer again.';\n\nconst SERVER: CloudFailure = { message: 'Something went wrong. Try again.', action: 'retry' };\nconst GENERIC: CloudFailure = { message: 'Could not send. Try again.', action: 'retry' };\nconst BROKEN: CloudFailure = { message: 'Could not send. Reload the page and try again.', action: 'reload' };\n\nconst BY_CODE: Record<string, CloudFailure> = {\n network: { message: 'Could not reach the form server. Check your connection and try again.', action: 'retry' },\n timeout: { message: 'The form server did not respond. Try again.', action: 'retry' },\n invalid: { message: 'Some answers were not accepted.', action: null },\n rate_limited: { message: 'Too many attempts. Wait a minute and try again.', action: 'retry' },\n limit_reached: { message: 'This form is not accepting responses right now.', action: null },\n not_found: { message: 'This form no longer exists.', action: null },\n paused: { message: 'This form is paused.', action: null },\n expired: { message: 'This form has expired.', action: null },\n body_too_large: { message: 'Your answers are too long. Shorten them and try again.', action: null },\n idempotency_key_reused: { message: 'Your answers changed while sending. Try again.', action: 'retry' },\n form_updated: { message: 'This form was updated. Reload the page.', action: 'reload' },\n malformed_body: BROKEN,\n unsupported_media_type: BROKEN,\n invalid_idempotency_key: BROKEN,\n invalid_response: SERVER,\n unavailable: SERVER,\n internal: SERVER,\n};\n\nconst CODE_BY_STATUS: Record<number, string> = {\n 0: 'network',\n 400: 'malformed_body',\n 402: 'limit_reached',\n 404: 'not_found',\n 409: 'paused',\n 410: 'expired',\n 413: 'body_too_large',\n 422: 'invalid',\n 429: 'rate_limited',\n};\n\nexport function failureFor(status: number, code = ''): CloudFailure {\n const known = BY_CODE[code] ?? BY_CODE[CODE_BY_STATUS[status] ?? ''];\n if (known) return known;\n return status >= 500 ? SERVER : GENERIC;\n}\n","import { failureFor, type CloudAction } from './failures';\n\nexport class CloudError extends Error {\n readonly status: number;\n readonly code: string;\n readonly errors?: Record<string, string>;\n readonly version?: number;\n readonly action: CloudAction;\n readonly cause?: unknown;\n\n constructor(status: number, code: string, errors?: Record<string, string>, version?: number, cause?: unknown) {\n const failure = failureFor(status, code);\n super(failure.message);\n this.name = 'CloudError';\n this.status = status;\n this.code = code;\n this.errors = errors;\n this.version = version;\n this.action = failure.action;\n this.cause = cause;\n }\n\n get retryable(): boolean {\n return this.action === 'retry';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nconst LEGACY_CODES = new Map([\n ['Form paused', 'paused'],\n ['Form not found', 'not_found'],\n ['Form expired', 'expired'],\n ['internal error', 'internal'],\n]);\n\nexport function networkError(cause: unknown, code = 'network'): CloudError {\n return new CloudError(0, code, undefined, undefined, cause);\n}\n\nexport async function readJson(response: CloudResponse): Promise<unknown> {\n try {\n return await response.json();\n } catch (error) {\n throw new CloudError(response.status, 'invalid_response', undefined, undefined, error);\n }\n}\n\nexport async function toCloudError(response: CloudResponse): Promise<CloudError> {\n const body: unknown = await response.json().catch(() => null);\n if (!isRecord(body)) return new CloudError(response.status, 'unknown');\n const errors = isRecord(body.errors) ? body.errors as Record<string, string> : undefined;\n const version = typeof body.version === 'number' && Number.isInteger(body.version) ? body.version : undefined;\n const code = typeof body.error === 'string' ? LEGACY_CODES.get(body.error) ?? body.error : 'unknown';\n return new CloudError(response.status, code, errors, version);\n}\n","interface Searchable {\n querySelector(selector: string): unknown;\n}\n\ninterface Focusable extends Searchable {\n focus(options?: { preventScroll?: boolean }): void;\n scrollIntoView?(options?: { block?: 'center' | 'nearest' }): void;\n matches(selector: string): boolean;\n}\n\nconst CONTROLS = 'input:not([type=\"hidden\"]):not(:disabled), select:not(:disabled), textarea:not(:disabled), button:not(:disabled)';\n\nfunction reveal(target: Focusable, scrollTo: Focusable): void {\n scrollTo.scrollIntoView?.({ block: 'center' });\n target.focus({ preventScroll: true });\n}\n\nexport function focusProblem(root: Searchable | null, notice?: Focusable | null): void {\n if (notice) return reveal(notice, notice);\n const invalid = root?.querySelector('[aria-invalid=\"true\"]') as Focusable | null | undefined;\n if (invalid) reveal(invalid.matches(CONTROLS) ? invalid : (invalid.querySelector(CONTROLS) as Focusable | null) ?? invalid, invalid);\n}\n","export function randomId(): string {\n if (typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n bytes[6] = (bytes[6] & 15) | 64;\n bytes[8] = (bytes[8] & 63) | 128;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n}\n","import type { FormDefinition } from '../types';\nimport { networkError, readJson, toCloudError } from './error';\nimport { randomId } from './uuid';\n\nconst DEFAULT_API_BASE = 'https://api.formhaus.dev';\nconst VERSION_HEADER = 'Formhaus-Version';\n\nexport interface CloudSubmission {\n id: string;\n values: Record<string, unknown>;\n}\n\nexport interface CloudClientOptions {\n id: string;\n apiBase?: string;\n}\n\nexport interface PublishedDefinition {\n definition: FormDefinition;\n version?: number;\n}\n\nfunction endpoint({ id, apiBase = DEFAULT_API_BASE }: CloudClientOptions): string {\n return `${apiBase.replace(/\\/+$/, '')}/f/${encodeURIComponent(id)}`;\n}\n\nconst LOAD_TIMEOUT = 15_000;\nconst SUBMIT_TIMEOUT = 30_000;\n\nasync function send(url: string, timeout: number, init: CloudRequestInit = {}, signal?: AbortSignal): Promise<CloudResponse> {\n const controller = new AbortController();\n const abort = () => controller.abort();\n const timer = setTimeout(abort, timeout);\n if (signal?.aborted) abort();\n signal?.addEventListener('abort', abort);\n try {\n return await fetch(url, { ...init, signal: controller.signal });\n } catch (error) {\n if (signal?.aborted) throw error;\n throw networkError(error, controller.signal.aborted ? 'timeout' : 'network');\n } finally {\n clearTimeout(timer);\n signal?.removeEventListener('abort', abort);\n }\n}\n\nfunction versionOf(response: CloudResponse): number | undefined {\n const version = Number(response.headers?.get(VERSION_HEADER));\n return Number.isInteger(version) && version > 0 ? version : undefined;\n}\n\nexport async function fetchPublished(options: CloudClientOptions, signal?: AbortSignal, after?: number): Promise<PublishedDefinition> {\n const query = after === undefined ? '' : `?v=${after}`;\n const response = await send(`${endpoint(options)}/definition${query}`, LOAD_TIMEOUT, {}, signal);\n if (!response.ok) throw await toCloudError(response);\n return { definition: await readJson(response) as FormDefinition, version: versionOf(response) };\n}\n\nexport async function fetchDefinition(options: CloudClientOptions, signal?: AbortSignal): Promise<FormDefinition> {\n return (await fetchPublished(options, signal)).definition;\n}\n\nexport interface CloudSubmitterOptions extends CloudClientOptions {\n version?: number;\n}\n\nexport function createSubmitter(options: CloudSubmitterOptions) {\n let pending: { body: string; key: string } | null = null;\n\n return async (values: Record<string, unknown>, skippedSteps: string[] = []): Promise<CloudSubmission> => {\n const body = JSON.stringify({ values, skippedSteps, version: options.version });\n if (pending?.body !== body) pending = { body, key: randomId() };\n const response = await send(endpoint(options), SUBMIT_TIMEOUT, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Idempotency-Key': pending.key },\n body,\n });\n if (!response.ok) throw await toCloudError(response);\n const submission = await readJson(response) as CloudSubmission;\n pending = null;\n return submission;\n };\n}\n","import type { FormDefinition, FormField } from '../types';\nimport { ANSWER_AGAIN } from './failures';\n\nconst SHAPES: Record<string, string> = {\n multiselect: 'list',\n checkbox: 'flag',\n switch: 'flag',\n number: 'number',\n file: 'file',\n};\n\nfunction fieldsOf(definition: FormDefinition): FormField[] {\n return [...(definition.fields ?? []), ...(definition.steps ?? []).flatMap((step) => step.fields)];\n}\n\nfunction shapeOf(field: FormField): string {\n return SHAPES[field.type] ?? 'text';\n}\n\nfunction allowed(field: FormField, value: unknown): boolean {\n if (!Array.isArray(field.options)) return true;\n const options = new Set(field.options.map((option) => String(option.value)));\n return (Array.isArray(value) ? value : [value]).every((item) => options.has(String(item)));\n}\n\nfunction fits(previous: FormField | undefined, next: FormField, value: unknown): boolean {\n if (previous && shapeOf(previous) !== shapeOf(next)) return false;\n switch (shapeOf(next)) {\n case 'list': return Array.isArray(value);\n case 'flag': return typeof value === 'boolean';\n case 'number': return typeof value === 'number';\n case 'text': return typeof value === 'string';\n default: return previous !== undefined;\n }\n}\n\nexport function carryValues(\n values: Record<string, unknown>,\n next: FormDefinition,\n previous?: FormDefinition,\n): Record<string, unknown> {\n const before = new Map(previous ? fieldsOf(previous).map((field) => [field.key, field]) : []);\n const kept: Record<string, unknown> = {};\n for (const field of fieldsOf(next)) {\n const value = values[field.key];\n if (value === undefined || value === null) continue;\n if (fits(before.get(field.key), field, value) && allowed(field, value)) kept[field.key] = value;\n }\n return kept;\n}\n\nexport function splitErrors(definition: FormDefinition, errors: Record<string, string>) {\n const known = new Set(fieldsOf(definition).map((field) => field.key));\n const fields: Record<string, string> = {};\n const other = new Set<string>();\n for (const [key, message] of Object.entries(errors)) {\n if (known.has(key)) fields[key] = message;\n else other.add(message);\n }\n return { fields, other: [...other] };\n}\n\nexport function updatedErrors(\n next: FormDefinition,\n sent: Record<string, unknown>,\n kept: Record<string, unknown>,\n rejected: Record<string, string>,\n): Record<string, string> {\n const errors: Record<string, string> = {};\n for (const field of fieldsOf(next)) {\n const answered = sent[field.key] !== undefined && sent[field.key] !== null && sent[field.key] !== '';\n if (answered && !(field.key in kept)) errors[field.key] = ANSWER_AGAIN;\n else if (rejected[field.key] && (!answered || field.key in kept)) errors[field.key] = rejected[field.key];\n }\n return errors;\n}\n","import type { FormDefinition } from '../types';\nimport { carryValues, splitErrors, updatedErrors } from './carry';\nimport { createSubmitter, fetchPublished, type CloudClientOptions, type CloudSubmission } from './client';\nimport { CloudError, networkError } from './error';\nimport { FORM_UPDATED_MESSAGE, failureFor, type CloudAction } from './failures';\n\nexport interface CloudNotice {\n tone: 'error' | 'info';\n message: string;\n action: CloudAction;\n details: string[];\n}\n\nexport interface CloudState {\n definition: FormDefinition | null;\n version: number | null;\n loadError: Error | null;\n errors: Record<string, string>;\n notice: CloudNotice | null;\n initialValues: Record<string, unknown> | null;\n revision: number;\n attempt: number;\n done: boolean;\n}\n\nexport interface CloudCallbacks {\n onChange(state: CloudState): void;\n onSuccess?(submission: CloudSubmission): void;\n onError?(error: unknown): void;\n}\n\nexport const initialCloudState: CloudState = {\n definition: null, version: null, loadError: null, errors: {}, notice: null,\n initialValues: null, revision: 0, attempt: 0, done: false,\n};\n\nfunction errorNotice(error: CloudError, details: string[] = []): CloudNotice {\n return { tone: 'error', message: error.message, action: error.action, details };\n}\n\nexport function createCloudController(options: CloudClientOptions, callbacks: CloudCallbacks) {\n const controller = new AbortController();\n let submitter = createSubmitter(options);\n const alive = () => !controller.signal.aborted;\n let state = initialCloudState;\n const update = (patch: Partial<CloudState>) => {\n state = { ...state, ...patch };\n callbacks.onChange(state);\n };\n const report = (patch: Partial<CloudState>) => update({ ...patch, attempt: state.attempt + 1 });\n\n async function refresh(error: CloudError, sent: Record<string, unknown>) {\n const before = state.definition;\n try {\n const next = await fetchPublished(options, controller.signal, error.version);\n if (!alive()) return;\n if (next.version === undefined || next.version < (error.version ?? 0)) throw new CloudError(409, 'form_updated');\n const kept = before ? carryValues(sent, next.definition, before) : {};\n submitter = createSubmitter({ ...options, version: next.version });\n report({\n definition: next.definition,\n version: next.version,\n initialValues: kept,\n revision: state.revision + 1,\n errors: updatedErrors(next.definition, sent, kept, error.errors ?? {}),\n notice: { tone: 'info', message: FORM_UPDATED_MESSAGE, action: null, details: [] },\n });\n } catch {\n if (alive()) report({ notice: { tone: 'error', ...failureFor(409, 'form_updated'), details: [] } });\n }\n }\n\n async function fail(error: unknown, values: Record<string, unknown>) {\n const cloud = error instanceof CloudError ? error : networkError(error);\n if (cloud.code === 'form_updated' && cloud.version !== undefined) return refresh(cloud, values);\n if (cloud.status !== 422) return report({ notice: errorNotice(cloud) });\n const errors = cloud.errors ?? {};\n const split = state.definition ? splitErrors(state.definition, errors) : { fields: errors, other: [] };\n report({ errors: split.fields, notice: split.other.length > 0 ? errorNotice(cloud, split.other) : null });\n }\n\n return {\n start() {\n if (state.loadError) update({ loadError: null });\n fetchPublished(options, controller.signal).then(\n (published) => {\n if (!alive()) return;\n submitter = createSubmitter({ ...options, version: published.version });\n update({ definition: published.definition, version: published.version ?? null });\n },\n (error: Error) => {\n if (!alive()) return;\n update({ loadError: error });\n callbacks.onError?.(error);\n },\n );\n },\n dispose() {\n controller.abort();\n },\n async submit(values: Record<string, unknown>, skippedSteps?: string[]) {\n if (state.notice) update({ notice: null });\n try {\n const submission = await submitter(values, skippedSteps);\n if (!alive()) return;\n update({ done: true });\n callbacks.onSuccess?.(submission);\n } catch (error) {\n if (!alive()) throw error;\n await fail(error, values);\n callbacks.onError?.(error);\n throw error;\n }\n },\n };\n}\n"]}
@@ -22,7 +22,7 @@ interface FormField {
22
22
  accept?: string;
23
23
  rows?: number;
24
24
  mask?: string;
25
- inputMode?: 'text' | 'numeric' | 'tel' | 'email';
25
+ inputMode?: 'text' | 'numeric' | 'decimal' | 'tel' | 'email' | 'url' | 'search';
26
26
  }
27
27
  interface ShowCondition {
28
28
  field: string;
@@ -57,6 +57,7 @@ interface FormAction {
57
57
  }
58
58
 
59
59
  interface FormDefinition {
60
+ $schema?: string;
60
61
  id: string;
61
62
  title: string;
62
63
  description?: string;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FormField, a as FormStep, b as FormDefinition, S as ShowCondition } from './definition-H3WUR596.js';
2
- export { D as DefaultFieldType, c as FieldOption, d as FieldType, e as FieldValidation, f as FormAction, g as StepRoute } from './definition-H3WUR596.js';
1
+ import { F as FormField, a as FormStep, b as FormDefinition, S as ShowCondition } from './definition-BAHgjY7x.js';
2
+ export { D as DefaultFieldType, c as FieldOption, d as FieldType, e as FieldValidation, f as FormAction, g as StepRoute } from './definition-BAHgjY7x.js';
3
3
 
4
4
  type FormAnalyticsEvent = {
5
5
  type: 'step_viewed';
@@ -112,6 +112,7 @@ declare class FormEngine {
112
112
  getSubmitValues(): Record<string, unknown>;
113
113
  goToStepWithField(key: string): void;
114
114
  reset(values?: Record<string, unknown>): void;
115
+ private warnAboutDefinition;
115
116
  private get internals();
116
117
  private notify;
117
118
  }
@@ -122,7 +123,7 @@ declare const isStepVisible: (step: FormStep, values: Record<string, unknown>) =
122
123
 
123
124
  declare function isVisible(field: Pick<FormField, 'show' | 'showAny'>, values: Record<string, unknown>): boolean;
124
125
 
125
- declare function validateDefinition(definition: FormDefinition): string[];
126
+ declare function validateDefinition(input: unknown): string[];
126
127
 
127
128
  declare class FormLifecycleError extends Error {
128
129
  readonly phase: 'afterStepChange' | 'afterSubmit';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- function O(t){return t==null||t===""||Array.isArray(t)&&!t.length}function A(t,e){let i=e[t.field];return t.eq!==void 0?i===t.eq:t.neq!==void 0?i!==t.neq:t.in!==void 0?t.in.includes(i):t.notIn!==void 0?!t.notIn.includes(i):t.notEmpty===true?!O(i):true}function u(t,e){let i=r=>A(r,e);return (t.show??[]).every(i)&&(!t.showAny?.length||t.showAny.some(i))}var m=u;function U(t){return !!t.fields?.length&&!!t.steps?.length}function y(t){return !!t.steps?.some(e=>e.routes?.length)}function h(t){return [...t.show??[],...t.showAny??[]].map(e=>e.field)}function x(t){return t.steps?.length?t.steps.flatMap(e=>e.fields):t.fields??[]}function w(t,e){let i={};for(let r of t)r.defaultValue!==void 0&&(i[r.key]=r.defaultValue);return Object.assign(i,e)}function v(t,e){let i=new Set([...Object.keys(t),...Object.keys(e)]);return new Set([...i].filter(r=>!Object.is(t[r],e[r])))}function L(t){let e=t.steps??[],i=U(t)?['FormEngine rejects a definition with both "fields" and "steps".']:[],r=new Map,n=new Map;e.forEach((s,a)=>{r.has(s.id)&&i.push(`Duplicate step id "${s.id}".`),r.set(s.id,a);for(let o of s.fields)n.set(o.key,a);});for(let{key:s}of x(t))(s in Object.prototype||s==="prototype")&&i.push(`Reserved field key "${s}".`);return y(t)&&e.forEach((s,a)=>{for(let o of s.routes??[]){let l=o.to===null?null:r.get(o.to);(l===void 0||l!==null&&l<=a)&&i.push(`Invalid route from "${s.id}" to "${o.to}": targets must be later steps or null.`);for(let d of h(o))(n.get(d)??1/0)<=a||i.push(`Invalid route on "${s.id}": condition field "${d}" must belong to this or an earlier step.`);}}),i}function X(t){let e=t.steps??[],i=[];for(let r of e){let n=new Set((r.routes??[]).map(s=>s.to).filter(s=>s!==null));for(let s of n){let a=e.findIndex(d=>d.id===s),o=e[a+1],l=e[a]?.routes?.some(d=>!d.show?.length&&!d.showAny?.length);o&&n.has(o.id)&&!l&&i.push(`Route branch "${s}" from "${r.id}" continues into sibling branch "${o.id}". Add an unconditional route where it should continue.`);}}return i}function Q(t,e){let i=t.steps??[];if(!y(t))return i.filter(a=>m(a,e));let r=new Map(i.map((a,o)=>[a.id,o])),n=[],s={};for(let a=0;a<i.length;){let o=i[a];if(!m(o,s)){a++;continue}n.push(o),Object.assign(s,tt(o,e,s));let l=o.routes?.find(d=>u(d,s)&&(d.to===null||(r.get(d.to)??-1)>a&&m(i[r.get(d.to)],s)));if(l?.to===null)break;a=l?r.get(l.to):a+1;}return n}function Z(t,e,i){let r=t[i]??t[t.length-1],n=e.findIndex(a=>a.id===r?.id);if(n!==-1)return n;let s=new Map(e.map((a,o)=>[a.id,o]));for(let a=Math.min(i-1,t.length-1);a>=0;a--){let o=s.get(t[a].id);if(o!==void 0)return o}return 0}function tt(t,e,i){let r={...i};for(let s of t.fields)r[s.key]=e[s.key];let n={};for(let s of t.fields)u(s,r)&&e[s.key]!==void 0&&(n[s.key]=e[s.key]);return n}var $=class{constructor(e){this.definition=e;this.source=null;this.activeStepList=[];this.activeValueMap={};this.enabled=y(e);}invalidate(){this.source=null;}steps(e){return this.refresh(e),this.activeStepList}values(e){return this.refresh(e),this.activeValueMap}refresh(e){if(this.source===e)return;this.source=e,this.activeStepList=Q(this.definition,e);let i={};for(let r of this.activeStepList)Object.assign(i,tt(r,e,i));this.activeValueMap=i;}};function et(t){let e=[],i=new Map,r=x(t),n=new Set;for(let{key:o}of r)n.has(o)&&e.push(`Duplicate field key "${o}" \u2014 fields will share state and overwrite each other`),n.add(o);let s=(o,l,d,p)=>{let f=h(d);for(let c of f)n.has(c)||e.push(`${o} "${l}" has show condition referencing non-existent field "${c}"`);if(f.length)for(let c of p)i.set(c,[...i.get(c)??[],...f]);};for(let o of r){s("Field",o.key,o,[o.key]);let l=o.validation?.pattern;if(l!==void 0)try{new RegExp(l);}catch{e.push(`Field "${o.key}" has invalid regex pattern: "${l}"`);}}let a=new Set;for(let o of t.steps??[]){s("Step",o.id,o,o.fields.map(l=>l.key));for(let l of h(o))y(t)&&n.has(l)&&!a.has(l)&&e.push(`Step "${o.id}" condition field "${l}" must belong to an earlier step.`);for(let{key:l}of o.fields)a.add(l);o.skip&&(o.next===false||!t.steps[1])&&e.push(`Step "${o.id}" has "skip" but no Next button or later step.`);}return {graph:i,warnings:e}}function bt(t){let e=new Map,i=[];for(let r of t.keys()){if(e.has(r))continue;e.set(r,true);let n=[[r,0]];for(;n.length>0;){let s=n[n.length-1],a=t.get(s[0])?.[s[1]++];if(a===void 0)e.set(s[0],false),n.pop();else if(e.get(a)){let o=n.map(([l])=>l);i.push([...o.slice(o.indexOf(a)),a]);}else e.has(a)||(e.set(a,true),n.push([a,0]));}}return i}function j(t){let{graph:e,warnings:i}=et(t);for(let r of bt(e))i.push(`Circular show condition detected: ${r.join(" -> ")}`);return [...L(t),...i,...X(t)]}var K=class extends Error{constructor(i,r){super(`The action completed, but ${i} failed: ${r instanceof Error?r.message:String(r)}`);this.phase=i;this.committed=true;this.name="FormLifecycleError",this.cause=r;}};async function B(t,e){try{await e();}catch(i){throw new K(t,i)}}function it(t){return t.visibleSteps.map(e=>e.id).join(`
2
- `)}function I(t,e){let i=it(t);return ()=>it(t)!==i||Object.keys(e).some(r=>e[r]!==void 0&&!Object.is(t.values[r],e[r]))}function S(t,e){switch(t){case "required":return "This field is required";case "minLength":return `Must be at least ${e?.min} ${e?.unit??"characters"}`;case "maxLength":return `Must be at most ${e?.max} ${e?.unit??"characters"}`;case "min":return `Must be at least ${e?.min}`;case "max":return `Must be at most ${e?.max}`;case "pattern":return "Invalid format";case "matchField":return "Fields must match";default:return "Invalid value"}}var gt=new Set(["checkbox","switch"]);function vt(t,e){return O(t)||t===false&&gt.has(e.type)}function rt(t,e,i,r){let n=i[`min${e}`],s=i[`max${e}`];if(n!==void 0&&t<n)return i[`min${e}Message`]??S(`min${e}`,{min:n,unit:r});if(s!==void 0&&t>s)return i[`max${e}Message`]??S(`max${e}`,{max:s,unit:r})}function Ft(t,e){if(e.pattern!==void 0)try{return new RegExp(e.pattern).test(String(t))?void 0:e.patternMessage??S("pattern")}catch{return}}function F(t,e,i,r){let n=t.validation;if(!n)return null;if(vt(e,t))return n.required?typeof n.required=="string"?n.required:S("required"):null;let s=typeof e=="string"||Array.isArray(e)?rt(e.length,"Length",n,Array.isArray(e)?"items":void 0):typeof e=="number"?rt(e,"",n):void 0,a=n.matchField!==void 0&&e!==i[n.matchField]?n.matchFieldMessage??S("matchField"):void 0;return (s??Ft(e,n)??a??(n.validator?r?.[n.validator]?.(e,i):void 0))||null}function b(t,e,i){return R(t.fields,e,i)}function R(t,e,i){let r={};for(let n of t){if(!u(n,e))continue;let s=F(n,e[n.key],e,i);s&&(r[n.key]=s);}return r}function N(t,e,i=false){let r=t.errors;t.errors={},t.topLevelErrors=[];for(let[o,l]of Object.entries(e))t.visibility.isFieldVisible(o,t.values)?t.errors[o]=l:t.topLevelErrors.push(l);let n=Object.keys(e).find(o=>t.errors[o]!==void 0),s=i&&n&&t.isMultiStep?t.visibility.findStepIndex(n,t.values,t.currentStepIndex):null,a=s!==null&&s!==t.currentStepIndex;s!==null&&(t.currentStepIndex=s),t.notify({fieldKeys:v(r,t.errors),structureChanged:a});}function nt(t,e){let i=[];for(let r of t.visibility.allFields)r.validation?.matchField!==e||t.errors[r.key]===void 0||F(r,t.values[r.key],k(t),t.validators)||(delete t.errors[r.key],i.push(r.key));return i}function st(t){let e=new Set(t.visibleSteps.flatMap(r=>r.fields.map(n=>n.key))),i=Object.keys(t.errors).filter(r=>!e.has(r));for(let r of i)delete t.errors[r];return i}function k(t){return y(t.definition)?E(t):ot(t,{...t.values})}function ot(t,e){for(let i of [...t.skipped.values()].flat())delete e[i];return e}function C(t){let e=t.errors,i={};if(t.isMultiStep)for(let r of t.visibleSteps)t.skipped.has(r.id)||Object.assign(i,b(r,k(t),t.validators));else Object.assign(i,R(t.definition.fields??[],t.values,t.validators));return t.errors=i,t.notify({fieldKeys:v(e,i)}),i}function at(t,e){let i=t.visibility.fieldByKey.get(e);if(!i||!t.visibility.isFieldVisible(e,t.values))return null;let r=t.errors[e],n=F(i,t.values[e],k(t),t.validators);n?t.errors[e]=n:delete t.errors[e];let s=Object.is(r,t.errors[e])?[]:[e];return t.notify({fieldKeys:s}),n}function E(t){let e={},i=t.visibility.getVisibleFieldKeys(t.values,t.currentStepIndex);for(let r of i)t.values[r]!==void 0&&(e[r]=t.values[r]);return ot(t,e)}function lt(t,e){let i=t.values,r=t.errors,n=Object.keys(t.fieldLoading);t.validationEpoch++,t.operationEpoch++,t.submitting=false,t.values=w(t.visibility.allFields,e),t.errors={},t.topLevelErrors=[],t.fieldLoading={},t.stepValidating=false,t.currentStepIndex=0,t.skipped.clear(),t.visibility.reconcileHidden(t.values,t.errors);let s=v(i,t.values),a=new Set([...s,...v(r,t.errors),...n]);t.notify({fieldKeys:a,structureChanged:true,valuesChanged:s.size>0});}function dt(t){let e=t.currentStep?.fields??[],i={...t.values};for(let{key:r}of e)delete i[r];return Object.assign(i,w(e))}function P(t){return Q(t.definition,dt(t))[t.currentStepIndex+1]}function T(t){let e=t.currentStep,i=e.fields.map(({key:r})=>r);t.values=dt(t);for(let r of i)delete t.errors[r];return t.skipped.set(e.id,i),t.visibility.reconcileHidden(t.values,t.errors),t.notify({fieldKeys:i,structureChanged:true,valuesChanged:true}),i}function M(t){let e=t.currentStep;e&&t.skipped.delete(e.id);}async function G(t,e,i=false){if(t.submitting||t.stepValidating)return false;i||M(t);let r={...t.lifecycle},n=false,s=t.validationEpoch,a=++t.operationEpoch,o=E(t),l=I(t,o),d=()=>s!==t.validationEpoch||a!==t.operationEpoch||l();t.submitting=true,t.notify();try{if(d()||Object.keys(C(t)).length>0||d())return !1;if(r.onBeforeSubmit){let p=await r.onBeforeSubmit(o);if(d()||p===!1)return !1}if(d())return !1;if(r.onBeforeSubmit){if(Object.keys(C(t)).length>0||d())return !1;o=E(t);}return n=!0,await e(o,[...t.skipped.keys()]),a!==t.operationEpoch||await B("afterSubmit",()=>r.onAfterSubmit?.(o)),!0}catch(p){if(!n&&d())return false;throw p}finally{a===t.operationEpoch&&(t.submitting=false,t.notify());}}function Y(t){let e=t.currentStep;if(!e)return true;let i=b(e,k(t),t.validators);if(Object.keys(i).length===0)return false;let r={...t.errors};return Object.assign(t.errors,i),t.notify({fieldKeys:v(r,t.errors)}),true}function q(t,e){t.validationEpoch++,t.currentStepIndex=e,t.notify({structureChanged:true});}function ut(t){return t.isLastStep?false:(q(t,t.currentStepIndex+1),true)}function pt(t){return t.isMultiStep?(M(t),!Y(t)&&ut(t)):false}function ct(t){return !t.isMultiStep||!P(t)?false:(T(t),ut(t))}async function ft(t,e){if(!t.isMultiStep||t.stepValidating||t.submitting)return false;if(P(t))return H(t,"next","skip");let i=t.currentStep;if(!e||!i)return false;let r=t.values,n={...t.errors},s=false,a=T(t),o=t.values,l=I(t,{...o});try{return await G(t,async(d,p)=>{await e(d,p),s=!0;},!0)}finally{!s&&t.values===o&&!l()&&(t.values=r,t.errors={...n,...t.errors},t.skipped.delete(i.id),t.notify({fieldKeys:a,structureChanged:true,valuesChanged:true}));}}async function H(t,e,i=e){let r=i==="skip";if(!t.isMultiStep||t.stepValidating||t.submitting||!r&&(e==="next"?t.isLastStep:t.isFirstStep))return false;let n=t.currentStep,s=e==="next"&&!r;if(s&&M(t),s&&Y(t))return false;let a={...t.lifecycle},o=t.onStepValidate,l=false,p=t.currentStepIndex+(e==="next"?1:-1),f=r?P(t):t.visibleSteps[p];if(!f)return false;let c={fromStepId:n.id,toStepId:f.id,direction:e,reason:i,values:{...k(t)}},yt=t.validationEpoch,J=++t.operationEpoch,St=I(t,c.values),V=()=>yt!==t.validationEpoch||J!==t.operationEpoch||t.currentStep?.id!==n.id||St();t.stepValidating=true,t.notify();try{if(V())return !1;if(s&&o){let g=await o(n.id,c.values);if(V())return !1;if(g&&Object.keys(g).length>0)return N(t,g),!1}if(a.onBeforeStepChange){let g=await a.onBeforeStepChange(c);if(V()||g===!1)return !1}return V()||s&&Y(t)?!1:(l=!0,r&&T(t),q(t,p),await B("afterStepChange",()=>a.onAfterStepChange?.(c)),!0)}catch(g){if(!l&&V())return false;throw g}finally{J===t.operationEpoch&&(t.stepValidating=false,t.notify());}}function ht(t){t.isMultiStep&&!t.isFirstStep&&q(t,t.currentStepIndex-1);}function mt(t,e){if(!t.isMultiStep)return;let i=t.visibility.findStepIndex(e,t.values,t.currentStepIndex);i===t.currentStepIndex?t.notify():i!==null&&q(t,i);}var z=class{constructor(){this.version=0;this.listeners=new Set;this.fieldVersions=new Map;this.fieldListeners=new Map;this.structureVersion=0;this.structureListeners=new Set;}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}getSnapshot(){return this.version}subscribeField(e,i){let r=this.fieldListeners.get(e)??new Set;return r.add(i),this.fieldListeners.set(e,r),()=>{r.delete(i),r.size===0&&this.fieldListeners.delete(e);}}getFieldSnapshot(e){return this.fieldVersions.get(e)??0}subscribeStructure(e){return this.structureListeners.add(e),()=>this.structureListeners.delete(e)}getStructureSnapshot(){return this.structureVersion}notify({fieldKeys:e=[],structureChanged:i=false}={}){for(let r of new Set(e))this.notifyField(r);i&&this.notifyStructure(),this.version++;for(let r of this.listeners)r();}notifyField(e){this.fieldVersions.set(e,(this.fieldVersions.get(e)??0)+1);for(let i of this.fieldListeners.get(e)??[])i();}notifyStructure(){this.structureVersion++;for(let e of this.structureListeners)e();}};function _(t,e){for(let i of h(t)){let r=e.get(i)??new Set;r.add(t),e.set(i,r);}}var W=class{constructor(e){this.definition=e;this.routeFields=new Set;this.fieldDependents=new Map;this.stepDependents=new Map;this.stepByFieldKey=new Map;this.visibilityDirty=true;this.canGoNextDirty=true;this.cache={visibleSteps:[],currentStep:null,visibleFields:[],canGoNext:false};this.allFields=x(e),this.fieldByKey=new Map(this.allFields.map(i=>[i.key,i])),this.path=new $(e);for(let i of e.steps??[])for(let r of i.fields)this.stepByFieldKey.set(r.key,i);this.buildIndexes();}markChanged(e,i){(e||i)&&this.path.invalidate(),e?(this.visibilityDirty=true,this.canGoNextDirty=true):i&&(this.canGoNextDirty=true);}state(e,i){return this.recomputeVisibility(e,i),this.cache}getCanGoNext(e,i,r){if(this.recomputeVisibility(e,i),!this.canGoNextDirty)return this.cache.canGoNext;this.canGoNextDirty=false;let n=this.cache.currentStep,s=n?b(n,this.conditionValues(e),r):{};return this.cache.canGoNext=this.isMultiStep&&Object.keys(s).length===0,this.cache.canGoNext}findStepIndex(e,i,r){let s=this.state(i,r).visibleSteps.findIndex(a=>a.fields.some(o=>o.key===e));return s===-1?null:s}affectsStructure(e,i){return this.routeFields.has(e)||this.fieldDependents.has(e)||this.stepDependents.has(e)||i.size>0}cascadeHiddenFields(e,i,r){return this.path.invalidate(),this.hasRoutes?this.reconcileHidden(i,r):this.drainQueue(new Set([e]),i,r)}reconcileHidden(e,i){this.path.invalidate();let r=new Set([...this.fieldDependents.keys(),...this.stepDependents.keys()]);return this.drainQueue(r,e,i)}drainQueue(e,i,r){let n=[...e],s=new Set(n),a=new Set,o=l=>{delete i[l],delete r[l],this.path.invalidate(),a.add(l),!s.has(l)&&(s.add(l),n.push(l));};for(let l=0;l<n.length;l++){let d=n[l];s.delete(d),this.clearHiddenFields(this.fieldDependents.get(d),i,o),this.clearHiddenSteps(this.stepDependents.get(d),i,o);}return a}isFieldVisible(e,i){let r=this.fieldByKey.get(e);if(!r||!u(r,this.conditionValues(i)))return false;let n=this.stepByFieldKey.get(e);return !n||this.path.steps(i).includes(n)}getVisibleFieldKeys(e,i){if(this.hasRoutes)return new Set(Object.keys(this.path.values(e)));let r=new Set,n=this.isMultiStep?this.state(e,i).visibleSteps.flatMap(s=>s.fields):this.definition.fields??[];for(let s of n)u(s,this.conditionValues(e))&&r.add(s.key);return r}conditionValues(e){return this.hasRoutes?this.path.values(e):e}get hasRoutes(){return this.path.enabled}get isMultiStep(){return (this.definition.steps??[]).length>0}buildIndexes(){for(let e of this.allFields)_(e,this.fieldDependents);for(let e of this.definition.steps??[]){_(e,this.stepDependents);for(let i of e.routes??[])for(let r of h(i))this.routeFields.add(r);}}recomputeVisibility(e,i){if(!this.visibilityDirty)return;if(this.visibilityDirty=false,!this.isMultiStep){this.cache.visibleSteps=[],this.cache.currentStep=null,this.cache.visibleFields=(this.definition.fields??[]).filter(s=>u(s,this.conditionValues(e)));return}this.cache.visibleSteps=this.path.steps(e);let r=this.cache.visibleSteps;this.cache.currentStep=r[i]??r[r.length-1]??null;let n=this.cache.currentStep;this.cache.visibleFields=n?n.fields.filter(s=>u(s,this.conditionValues(e))):[];}clearHiddenFields(e,i,r){for(let n of e??[]){let s=this.stepByFieldKey.get(n.key);this.hasRoutes&&s&&!this.path.steps(i).includes(s)||u(n,this.conditionValues(i))||i[n.key]===void 0||r(n.key);}}clearHiddenSteps(e,i,r){for(let n of e??[])if(!m(n,this.conditionValues(i)))for(let s of n.fields)i[s.key]!==void 0&&r(s.key);}};var D=class{constructor(e,i,r){this.definition=e;this.errors={};this.topLevelErrors=[];this.currentStepIndex=0;this.fieldLoading={};this.stepValidating=false;this.submitting=false;this.skipped=new Map;this.validationEpoch=0;this.operationEpoch=0;this.subscriptions=new z;let n=L(e);if(n.length)throw new Error(n.join(`
3
- `));this.lifecycle=r??{},this.validators=r?.validators??{},this.visibility=new W(e);let s=j(e);for(let{key:a,validation:o}of this.visibility.allFields){let l=o?.validator;l&&!this.validators[l]&&s.push(`Field "${a}" references unknown validator "${l}".`);}for(let a of s)console.warn(`[FormEngine] ${a}`);this.values=w(this.visibility.allFields,i),this.visibility.reconcileHidden(this.values,this.errors);}get onStepValidate(){return this.lifecycle.onStepValidate}get isMultiStep(){return (this.definition.steps??[]).length>0}get visibleSteps(){return this.visibility.state(this.values,this.currentStepIndex).visibleSteps}get currentStep(){return this.visibility.state(this.values,this.currentStepIndex).currentStep}get visibleFields(){return this.visibility.state(this.values,this.currentStepIndex).visibleFields}get isFirstStep(){return this.currentStepIndex===0}get isLastStep(){return this.currentStepIndex>=this.visibleSteps.length-1}get canGoNext(){return this.visibility.getCanGoNext(this.values,this.currentStepIndex,this.validators)}get progress(){let e=this.visibleSteps.length;return {current:Math.min(this.currentStepIndex+1,e),total:e}}subscribe(e){return this.subscriptions.subscribe(e)}getSnapshot(){return this.subscriptions.getSnapshot()}subscribeField(e,i){return this.subscriptions.subscribeField(e,i)}getFieldSnapshot(e){return this.subscriptions.getFieldSnapshot(e)}subscribeStructure(e){return this.subscriptions.subscribeStructure(e)}getStructureSnapshot(){return this.subscriptions.getStructureSnapshot()}setValue(e,i){let r=this.isMultiStep?this.visibleSteps:null,n=!Object.is(this.values[e],i),s=this.errors[e]!==void 0;this.values[e]=i,delete this.errors[e];for(let[f,c]of this.skipped)n&&c.includes(e)&&this.skipped.delete(f);let a=this.visibility.cascadeHiddenFields(e,this.values,this.errors),o=nt(this.internals,e),l=new Set([...a,...o]);(n||s)&&l.add(e);let d=n||a.size>0,p=d&&this.visibility.affectsStructure(e,a);if(r&&p){this.visibility.markChanged(true,true),this.currentStepIndex=Z(r,this.visibleSteps,this.currentStepIndex);for(let f of st(this.internals))l.add(f);}this.notify({fieldKeys:l,structureChanged:p,valuesChanged:d});}setErrors(e){N(this.internals,e,true);}setFieldLoading(e,i){let r=this.fieldLoading[e]===true;i?this.fieldLoading[e]=true:delete this.fieldLoading[e],this.notify({fieldKeys:r===i?[]:[e]});}cancelPendingActions(){this.validationEpoch++,this.operationEpoch++,this.stepValidating=false,this.submitting=false,this.notify();}nextStep(){return pt(this.internals)}nextStepAsync(e="next"){return H(this.internals,"next",e)}prevStepAsync(){return H(this.internals,"back")}skipStep(){return ct(this.internals)}skipStepAsync(e){return ft(this.internals,e)}isStepSkipped(e){return this.skipped.has(e)}submitAsync(e){return G(this.internals,e)}prevStep(){ht(this.internals);}validate(){return C(this.internals)}validateField(e){return at(this.internals,e)}getSubmitValues(){return E(this.internals)}goToStepWithField(e){mt(this.internals,e);}reset(e){lt(this.internals,e);}get internals(){return this}notify(e={}){this.visibility.markChanged(!!e.structureChanged,!!e.valuesChanged),this.subscriptions.notify(e);}};function xt(t,e){let i=Object.keys(e);return i.length===Object.keys(t).length&&i.every(r=>t[r]===e[r])}function wt(t,e){return Object.entries(e).every(([i,r])=>t.errors[i]===r||t.topLevelErrors.includes(r))}function kt(t,e,i){return !e||!xt(e,i)||!wt(t,i)}export{D as FormEngine,K as FormLifecycleError,A as evaluateCondition,S as getDefaultMessage,m as isStepVisible,u as isVisible,kt as shouldApplyExternalErrors,j as validateDefinition,F as validateField,R as validateFields,b as validateStep};//# sourceMappingURL=index.js.map
1
+ function O(t){return t==null||t===""||Array.isArray(t)&&!t.length}function $(t,e){let r=e[t.field];return t.eq!==void 0?r===t.eq:t.neq!==void 0?r!==t.neq:t.in!==void 0?t.in.includes(r):t.notIn!==void 0?!t.notIn.includes(r):t.notEmpty===true?!O(r):true}function u(t,e){let r=i=>$(i,e);return (t.show??[]).every(r)&&(!t.showAny?.length||t.showAny.some(r))}var m=u;function rt(t){return !!t.fields?.length&&!!t.steps?.length}function y(t){return !!t.steps?.some(e=>e.routes?.length)}function h(t){return [...t.show??[],...t.showAny??[]].map(e=>e.field)}function w(t){return t.steps?.length?t.steps.flatMap(e=>e.fields):t.fields??[]}function x(t,e){let r={};for(let i of t)i.defaultValue!==void 0&&(r[i.key]=i.defaultValue);return Object.assign(r,e)}function v(t,e){let r=new Set([...Object.keys(t),...Object.keys(e)]);return new Set([...r].filter(i=>!Object.is(t[i],e[i])))}function L(t){let e=t.steps??[],r=rt(t)?['FormEngine rejects a definition with both "fields" and "steps".']:[],i=new Map,n=new Map;e.forEach((s,a)=>{i.has(s.id)&&r.push(`Duplicate step id "${s.id}".`),i.set(s.id,a);for(let o of s.fields)n.set(o.key,a);});for(let{key:s}of w(t))(s in Object.prototype||s==="prototype")&&r.push(`Reserved field key "${s}".`);return y(t)&&e.forEach((s,a)=>{for(let o of s.routes??[]){let l=o.to===null?null:i.get(o.to);(l===void 0||l!==null&&l<=a)&&r.push(`Invalid route from "${s.id}" to "${o.to}": targets must be later steps or null.`);for(let d of h(o))(n.get(d)??1/0)<=a||r.push(`Invalid route on "${s.id}": condition field "${d}" must belong to this or an earlier step.`);}}),r}function it(t){let e=t.steps??[],r=[];for(let i of e){let n=new Set((i.routes??[]).map(s=>s.to).filter(s=>s!==null));for(let s of n){let a=e.findIndex(d=>d.id===s),o=e[a+1],l=e[a]?.routes?.some(d=>!d.show?.length&&!d.showAny?.length);o&&n.has(o.id)&&!l&&r.push(`Route branch "${s}" from "${i.id}" continues into sibling branch "${o.id}". Add an unconditional route where it should continue.`);}}return r}function U(t,e){let r=t.steps??[];if(!y(t))return r.filter(a=>m(a,e));let i=new Map(r.map((a,o)=>[a.id,o])),n=[],s={};for(let a=0;a<r.length;){let o=r[a];if(!m(o,s)){a++;continue}n.push(o),Object.assign(s,st(o,e,s));let l=o.routes?.find(d=>u(d,s)&&(d.to===null||(i.get(d.to)??-1)>a&&m(r[i.get(d.to)],s)));if(l?.to===null)break;a=l?i.get(l.to):a+1;}return n}function nt(t,e,r){let i=t[r]??t[t.length-1],n=e.findIndex(a=>a.id===i?.id);if(n!==-1)return n;let s=new Map(e.map((a,o)=>[a.id,o]));for(let a=Math.min(r-1,t.length-1);a>=0;a--){let o=s.get(t[a].id);if(o!==void 0)return o}return 0}function st(t,e,r){let i={...r};for(let s of t.fields)i[s.key]=e[s.key];let n={};for(let s of t.fields)u(s,i)&&e[s.key]!==void 0&&(n[s.key]=e[s.key]);return n}var j=class{constructor(e){this.definition=e;this.source=null;this.activeStepList=[];this.activeValueMap={};this.enabled=y(e);}invalidate(){this.source=null;}steps(e){return this.refresh(e),this.activeStepList}values(e){return this.refresh(e),this.activeValueMap}refresh(e){if(this.source===e)return;this.source=e,this.activeStepList=U(this.definition,e);let r={};for(let i of this.activeStepList)Object.assign(r,st(i,e,r));this.activeValueMap=r;}};function ot(t){let e=[],r=new Map,i=w(t),n=new Set;for(let{key:o}of i)n.has(o)&&e.push(`Duplicate field key "${o}" \u2014 fields will share state and overwrite each other`),n.add(o);let s=(o,l,d,p)=>{let f=h(d);for(let c of f)typeof c!="string"?e.push(`${o} "${l}" has a show condition without a "field" name`):n.has(c)||e.push(`${o} "${l}" has show condition referencing non-existent field "${c}"`);if(f.length)for(let c of p)r.set(c,[...r.get(c)??[],...f]);};for(let o of i){s("Field",o.key,o,[o.key]);let l=o.validation?.pattern;if(l!==void 0)try{new RegExp(l);}catch{e.push(`Field "${o.key}" has invalid regex pattern: "${l}"`);}}let a=new Set;for(let o of t.steps??[]){s("Step",o.id,o,o.fields.map(l=>l.key));for(let l of h(o))y(t)&&n.has(l)&&!a.has(l)&&e.push(`Step "${o.id}" condition field "${l}" must belong to an earlier step.`);for(let{key:l}of o.fields)a.add(l);o.skip&&(o.next===false||!t.steps[1])&&e.push(`Step "${o.id}" has "skip" but no Next button or later step.`);}return {graph:r,warnings:e}}var K=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function k(t,e,r,i=false){return t==null&&!i?[]:Array.isArray(t)?t.flatMap((n,s)=>r(n,`${e}[${s}]`)):[`${e} must be an array.`]}function B(t,e){return (r,i)=>K(r)?e(r,i):[`${i} must be ${t}.`]}var at=B("a condition object",()=>[]);function X(t,e){return [...k(t.show,`${e}.show`,at),...k(t.showAny,`${e}.showAny`,at)]}var lt=B("a field object",X),Vt=B("a route object",X),It=B("a step object",(t,e)=>[...k(t.fields,`${e}.fields`,lt,true),...X(t,e),...k(t.routes,`${e}.routes`,Vt)]);function N(t){return K(t)?[...k(t.fields,"fields",lt),...k(t.steps,"steps",It)]:["Form definition must be an object."]}function Rt(t){let e=new Map,r=[];for(let i of t.keys()){if(e.has(i))continue;e.set(i,true);let n=[[i,0]];for(;n.length>0;){let s=n[n.length-1],a=t.get(s[0])?.[s[1]++];if(a===void 0)e.set(s[0],false),n.pop();else if(e.get(a)){let o=n.map(([l])=>l);r.push([...o.slice(o.indexOf(a)),a]);}else e.has(a)||(e.set(a,true),n.push([a,0]));}}return r}function P(t){let e=N(t);if(e.length)return e;let r=t,{graph:i,warnings:n}=ot(r);K(r.submit)||n.unshift('Form definition is missing the "submit" action.');for(let s of Rt(i))n.push(`Circular show condition detected: ${s.join(" -> ")}`);return [...L(r),...n,...it(r)]}var T=class extends Error{constructor(r,i){super(`The action completed, but ${r} failed: ${i instanceof Error?i.message:String(i)}`);this.phase=r;this.committed=true;this.name="FormLifecycleError",this.cause=i;}};async function G(t,e){try{await e();}catch(r){throw new T(t,r)}}function dt(t){return t.visibleSteps.map(e=>e.id).join(`
2
+ `)}function R(t,e){let r=dt(t);return ()=>dt(t)!==r||Object.keys(e).some(i=>e[i]!==void 0&&!Object.is(t.values[i],e[i]))}function g(t,e){switch(t){case "required":return "This field is required";case "minLength":return `Must be at least ${e?.min} ${e?.unit??"characters"}`;case "maxLength":return `Must be at most ${e?.max} ${e?.unit??"characters"}`;case "min":return `Must be at least ${e?.min}`;case "max":return `Must be at most ${e?.max}`;case "pattern":return "Invalid format";case "matchField":return "Fields must match";default:return "Invalid value"}}var Ct=new Set(["checkbox","switch"]);function Mt(t,e){return O(t)||t===false&&Ct.has(e.type)}function ut(t,e,r,i){let n=r[`min${e}`],s=r[`max${e}`];if(n!==void 0&&t<n)return r[`min${e}Message`]??g(`min${e}`,{min:n,unit:i});if(s!==void 0&&t>s)return r[`max${e}Message`]??g(`max${e}`,{max:s,unit:i})}function At(t,e){if(e.pattern!==void 0)try{return new RegExp(e.pattern).test(String(t))?void 0:e.patternMessage??g("pattern")}catch{return}}function F(t,e,r,i){let n=t.validation;if(!n)return null;if(Mt(e,t))return n.required?typeof n.required=="string"?n.required:g("required"):null;let s=typeof e=="string"||Array.isArray(e)?ut(e.length,"Length",n,Array.isArray(e)?"items":void 0):typeof e=="number"?ut(e,"",n):void 0,a=n.matchField!==void 0&&e!==r[n.matchField]?n.matchFieldMessage??g("matchField"):void 0;return (s??At(e,n)??a??(n.validator?i?.[n.validator]?.(e,r):void 0))||null}function b(t,e,r){return C(t.fields,e,r)}function C(t,e,r){let i={};for(let n of t){if(!u(n,e))continue;let s=F(n,e[n.key],e,r);s&&(i[n.key]=s);}return i}function q(t,e,r=false){let i=t.errors;t.errors={},t.topLevelErrors=[];for(let[s,a]of Object.entries(e))t.visibility.isFieldVisible(s,t.values)?t.errors[s]=a:t.topLevelErrors.push(a);let n=r&&pt(t,e);t.notify({fieldKeys:v(i,t.errors),structureChanged:n});}function pt(t,e){let r=Object.keys(e).find(n=>t.errors[n]!==void 0),i=r&&t.isMultiStep?t.visibility.findStepIndex(r,t.values,t.currentStepIndex):null;return i===null||i===t.currentStepIndex?false:(t.currentStepIndex=i,true)}function ct(t,e){let r=[];for(let i of t.visibility.allFields)i.validation?.matchField!==e||t.errors[i.key]===void 0||F(i,t.values[i.key],E(t),t.validators)||(delete t.errors[i.key],r.push(i.key));return r}function ft(t){let e=new Set(t.visibleSteps.flatMap(i=>i.fields.map(n=>n.key))),r=Object.keys(t.errors).filter(i=>!e.has(i));for(let i of r)delete t.errors[i];return r}function E(t){return y(t.definition)?V(t):ht(t,{...t.values})}function ht(t,e){for(let r of [...t.skipped.values()].flat())delete e[r];return e}function M(t,e=false){let r=t.errors,i={};if(t.isMultiStep)for(let s of t.visibleSteps)t.skipped.has(s.id)||Object.assign(i,b(s,E(t),t.validators));else Object.assign(i,C(t.definition.fields??[],t.values,t.validators));t.errors=i;let n=e&&pt(t,i);return t.notify({fieldKeys:v(r,i),structureChanged:n}),i}function mt(t,e){let r=t.visibility.fieldByKey.get(e);if(!r||!t.visibility.isFieldVisible(e,t.values))return null;let i=t.errors[e],n=F(r,t.values[e],E(t),t.validators);n?t.errors[e]=n:delete t.errors[e];let s=Object.is(i,t.errors[e])?[]:[e];return t.notify({fieldKeys:s}),n}function V(t){let e={},r=t.visibility.getVisibleFieldKeys(t.values,t.currentStepIndex);for(let i of r)t.values[i]!==void 0&&(e[i]=t.values[i]);return ht(t,e)}function yt(t,e){let r=t.values,i=t.errors,n=Object.keys(t.fieldLoading);t.validationEpoch++,t.operationEpoch++,t.submitting=false,t.values=x(t.visibility.allFields,e),t.errors={},t.topLevelErrors=[],t.fieldLoading={},t.stepValidating=false,t.currentStepIndex=0,t.skipped.clear(),t.visibility.reconcileHidden(t.values,t.errors);let s=v(r,t.values),a=new Set([...s,...v(i,t.errors),...n]);t.notify({fieldKeys:a,structureChanged:true,valuesChanged:s.size>0});}function gt(t){let e=t.currentStep?.fields??[],r={...t.values};for(let{key:i}of e)delete r[i];return Object.assign(r,x(e))}function H(t){return U(t.definition,gt(t))[t.currentStepIndex+1]}function z(t){let e=t.currentStep,r=e.fields.map(({key:i})=>i);t.values=gt(t);for(let i of r)delete t.errors[i];return t.skipped.set(e.id,r),t.visibility.reconcileHidden(t.values,t.errors),t.notify({fieldKeys:r,structureChanged:true,valuesChanged:true}),r}function A(t){let e=t.currentStep;e&&t.skipped.delete(e.id);}async function W(t,e,r=false){if(t.submitting||t.stepValidating)return false;r||A(t);let i={...t.lifecycle},n=false,s=t.validationEpoch,a=++t.operationEpoch,o=V(t),l=R(t,o),d=()=>s!==t.validationEpoch||a!==t.operationEpoch||l();t.submitting=true,t.notify();try{if(d()||Object.keys(M(t,!0)).length>0||d())return !1;if(i.onBeforeSubmit){let p=await i.onBeforeSubmit(o);if(d()||p===!1)return !1}if(d())return !1;if(i.onBeforeSubmit){if(Object.keys(M(t,!0)).length>0||d())return !1;o=V(t);}return n=!0,await e(o,[...t.skipped.keys()]),a!==t.operationEpoch||await G("afterSubmit",()=>i.onAfterSubmit?.(o)),!0}catch(p){if(!n&&d())return false;throw p}finally{a===t.operationEpoch&&(t.submitting=false,t.notify());}}function Z(t){let e=t.currentStep;if(!e)return true;let r=b(e,E(t),t.validators);if(Object.keys(r).length===0)return false;let i={...t.errors};return Object.assign(t.errors,r),t.notify({fieldKeys:v(i,t.errors)}),true}function Q(t,e){t.validationEpoch++,t.currentStepIndex=e,t.notify({structureChanged:true});}function bt(t){return t.isLastStep?false:(Q(t,t.currentStepIndex+1),true)}function St(t){return t.isMultiStep?(A(t),!Z(t)&&bt(t)):false}function vt(t){return !t.isMultiStep||!H(t)?false:(z(t),bt(t))}async function Ft(t,e){if(!t.isMultiStep||t.stepValidating||t.submitting)return false;if(H(t))return Y(t,"next","skip");let r=t.currentStep;if(!e||!r)return false;let i=t.values,n={...t.errors},s=false,a=z(t),o=t.values,l=R(t,{...o});try{return await W(t,async(d,p)=>{await e(d,p),s=!0;},!0)}finally{!s&&t.values===o&&!l()&&(t.values=i,t.errors={...n,...t.errors},t.skipped.delete(r.id),t.notify({fieldKeys:a,structureChanged:true,valuesChanged:true}));}}async function Y(t,e,r=e){let i=r==="skip";if(!t.isMultiStep||t.stepValidating||t.submitting||!i&&(e==="next"?t.isLastStep:t.isFirstStep))return false;let n=t.currentStep,s=e==="next"&&!i;if(s&&A(t),s&&Z(t))return false;let a={...t.lifecycle},o=t.onStepValidate,l=false,p=t.currentStepIndex+(e==="next"?1:-1),f=i?H(t):t.visibleSteps[p];if(!f)return false;let c={fromStepId:n.id,toStepId:f.id,direction:e,reason:r,values:{...E(t)}},kt=t.validationEpoch,et=++t.operationEpoch,Et=R(t,c.values),I=()=>kt!==t.validationEpoch||et!==t.operationEpoch||t.currentStep?.id!==n.id||Et();t.stepValidating=true,t.notify();try{if(I())return !1;if(s&&o){let S=await o(n.id,c.values);if(I())return !1;if(S&&Object.keys(S).length>0)return q(t,S),!1}if(a.onBeforeStepChange){let S=await a.onBeforeStepChange(c);if(I()||S===!1)return !1}return I()||s&&Z(t)?!1:(l=!0,i&&z(t),Q(t,p),await G("afterStepChange",()=>a.onAfterStepChange?.(c)),!0)}catch(S){if(!l&&I())return false;throw S}finally{et===t.operationEpoch&&(t.stepValidating=false,t.notify());}}function wt(t){t.isMultiStep&&!t.isFirstStep&&Q(t,t.currentStepIndex-1);}function xt(t,e){if(!t.isMultiStep)return;let r=t.visibility.findStepIndex(e,t.values,t.currentStepIndex);r===t.currentStepIndex?t.notify():r!==null&&Q(t,r);}var _=class{constructor(){this.version=0;this.listeners=new Set;this.fieldVersions=new Map;this.fieldListeners=new Map;this.structureVersion=0;this.structureListeners=new Set;}subscribe(e){return this.listeners.add(e),()=>this.listeners.delete(e)}getSnapshot(){return this.version}subscribeField(e,r){let i=this.fieldListeners.get(e)??new Set;return i.add(r),this.fieldListeners.set(e,i),()=>{i.delete(r),i.size===0&&this.fieldListeners.delete(e);}}getFieldSnapshot(e){return this.fieldVersions.get(e)??0}subscribeStructure(e){return this.structureListeners.add(e),()=>this.structureListeners.delete(e)}getStructureSnapshot(){return this.structureVersion}notify({fieldKeys:e=[],structureChanged:r=false}={}){for(let i of new Set(e))this.notifyField(i);r&&this.notifyStructure(),this.version++;for(let i of this.listeners)i();}notifyField(e){this.fieldVersions.set(e,(this.fieldVersions.get(e)??0)+1);for(let r of this.fieldListeners.get(e)??[])r();}notifyStructure(){this.structureVersion++;for(let e of this.structureListeners)e();}};function tt(t,e){for(let r of h(t)){let i=e.get(r)??new Set;i.add(t),e.set(r,i);}}var J=class{constructor(e){this.definition=e;this.routeFields=new Set;this.fieldDependents=new Map;this.stepDependents=new Map;this.stepByFieldKey=new Map;this.visibilityDirty=true;this.canGoNextDirty=true;this.cache={visibleSteps:[],currentStep:null,visibleFields:[],canGoNext:false};this.allFields=w(e),this.fieldByKey=new Map(this.allFields.map(r=>[r.key,r])),this.path=new j(e);for(let r of e.steps??[])for(let i of r.fields)this.stepByFieldKey.set(i.key,r);this.buildIndexes();}markChanged(e,r){(e||r)&&this.path.invalidate(),e?(this.visibilityDirty=true,this.canGoNextDirty=true):r&&(this.canGoNextDirty=true);}state(e,r){return this.recomputeVisibility(e,r),this.cache}getCanGoNext(e,r,i){if(this.recomputeVisibility(e,r),!this.canGoNextDirty)return this.cache.canGoNext;this.canGoNextDirty=false;let n=this.cache.currentStep,s=n?b(n,this.conditionValues(e),i):{};return this.cache.canGoNext=this.isMultiStep&&Object.keys(s).length===0,this.cache.canGoNext}findStepIndex(e,r,i){let s=this.state(r,i).visibleSteps.findIndex(a=>a.fields.some(o=>o.key===e));return s===-1?null:s}affectsStructure(e,r){return this.routeFields.has(e)||this.fieldDependents.has(e)||this.stepDependents.has(e)||r.size>0}cascadeHiddenFields(e,r,i){return this.path.invalidate(),this.hasRoutes?this.reconcileHidden(r,i):this.drainQueue(new Set([e]),r,i)}reconcileHidden(e,r){this.path.invalidate();let i=new Set([...this.fieldDependents.keys(),...this.stepDependents.keys()]);return this.drainQueue(i,e,r)}drainQueue(e,r,i){let n=[...e],s=new Set(n),a=new Set,o=l=>{delete r[l],delete i[l],this.path.invalidate(),a.add(l),!s.has(l)&&(s.add(l),n.push(l));};for(let l=0;l<n.length;l++){let d=n[l];s.delete(d),this.clearHiddenFields(this.fieldDependents.get(d),r,o),this.clearHiddenSteps(this.stepDependents.get(d),r,o);}return a}isFieldVisible(e,r){let i=this.fieldByKey.get(e);if(!i||!u(i,this.conditionValues(r)))return false;let n=this.stepByFieldKey.get(e);return !n||this.path.steps(r).includes(n)}getVisibleFieldKeys(e,r){if(this.hasRoutes)return new Set(Object.keys(this.path.values(e)));let i=new Set,n=this.isMultiStep?this.state(e,r).visibleSteps.flatMap(s=>s.fields):this.definition.fields??[];for(let s of n)u(s,this.conditionValues(e))&&i.add(s.key);return i}conditionValues(e){return this.hasRoutes?this.path.values(e):e}get hasRoutes(){return this.path.enabled}get isMultiStep(){return (this.definition.steps??[]).length>0}buildIndexes(){for(let e of this.allFields)tt(e,this.fieldDependents);for(let e of this.definition.steps??[]){tt(e,this.stepDependents);for(let r of e.routes??[])for(let i of h(r))this.routeFields.add(i);}}recomputeVisibility(e,r){if(!this.visibilityDirty)return;if(this.visibilityDirty=false,!this.isMultiStep){this.cache.visibleSteps=[],this.cache.currentStep=null,this.cache.visibleFields=(this.definition.fields??[]).filter(s=>u(s,this.conditionValues(e)));return}this.cache.visibleSteps=this.path.steps(e);let i=this.cache.visibleSteps;this.cache.currentStep=i[r]??i[i.length-1]??null;let n=this.cache.currentStep;this.cache.visibleFields=n?n.fields.filter(s=>u(s,this.conditionValues(e))):[];}clearHiddenFields(e,r,i){for(let n of e??[]){let s=this.stepByFieldKey.get(n.key);this.hasRoutes&&s&&!this.path.steps(r).includes(s)||u(n,this.conditionValues(r))||r[n.key]===void 0||i(n.key);}}clearHiddenSteps(e,r,i){for(let n of e??[])if(!m(n,this.conditionValues(r)))for(let s of n.fields)r[s.key]!==void 0&&i(s.key);}};var Dt=new WeakSet,D=class{constructor(e,r,i){this.definition=e;this.errors={};this.topLevelErrors=[];this.currentStepIndex=0;this.fieldLoading={};this.stepValidating=false;this.submitting=false;this.skipped=new Map;this.validationEpoch=0;this.operationEpoch=0;this.subscriptions=new _;let n=N(e),s=n.length?n:L(e);if(s.length)throw new Error(s.join(`
3
+ `));this.lifecycle=i??{},this.validators=i?.validators??{},this.visibility=new J(e),Dt.has(this.lifecycle)||this.warnAboutDefinition(),this.values=x(this.visibility.allFields,r),this.visibility.reconcileHidden(this.values,this.errors);}get onStepValidate(){return this.lifecycle.onStepValidate}get isMultiStep(){return (this.definition.steps??[]).length>0}get visibleSteps(){return this.visibility.state(this.values,this.currentStepIndex).visibleSteps}get currentStep(){return this.visibility.state(this.values,this.currentStepIndex).currentStep}get visibleFields(){return this.visibility.state(this.values,this.currentStepIndex).visibleFields}get isFirstStep(){return this.currentStepIndex===0}get isLastStep(){return this.currentStepIndex>=this.visibleSteps.length-1}get canGoNext(){return this.visibility.getCanGoNext(this.values,this.currentStepIndex,this.validators)}get progress(){let e=this.visibleSteps.length;return {current:Math.min(this.currentStepIndex+1,e),total:e}}subscribe(e){return this.subscriptions.subscribe(e)}getSnapshot(){return this.subscriptions.getSnapshot()}subscribeField(e,r){return this.subscriptions.subscribeField(e,r)}getFieldSnapshot(e){return this.subscriptions.getFieldSnapshot(e)}subscribeStructure(e){return this.subscriptions.subscribeStructure(e)}getStructureSnapshot(){return this.subscriptions.getStructureSnapshot()}setValue(e,r){let i=this.isMultiStep?this.visibleSteps:null,n=!Object.is(this.values[e],r),s=this.errors[e]!==void 0;this.values[e]=r,delete this.errors[e];for(let[f,c]of this.skipped)n&&c.includes(e)&&this.skipped.delete(f);let a=this.visibility.cascadeHiddenFields(e,this.values,this.errors),o=ct(this.internals,e),l=new Set([...a,...o]);(n||s)&&l.add(e);let d=n||a.size>0,p=d&&this.visibility.affectsStructure(e,a);if(i&&p){this.visibility.markChanged(true,true),this.currentStepIndex=nt(i,this.visibleSteps,this.currentStepIndex);for(let f of ft(this.internals))l.add(f);}this.notify({fieldKeys:l,structureChanged:p,valuesChanged:d});}setErrors(e){q(this.internals,e,true);}setFieldLoading(e,r){let i=this.fieldLoading[e]===true;r?this.fieldLoading[e]=true:delete this.fieldLoading[e],this.notify({fieldKeys:i===r?[]:[e]});}cancelPendingActions(){this.validationEpoch++,this.operationEpoch++,this.stepValidating=false,this.submitting=false,this.notify();}nextStep(){return St(this.internals)}nextStepAsync(e="next"){return Y(this.internals,"next",e)}prevStepAsync(){return Y(this.internals,"back")}skipStep(){return vt(this.internals)}skipStepAsync(e){return Ft(this.internals,e)}isStepSkipped(e){return this.skipped.has(e)}submitAsync(e){return W(this.internals,e)}prevStep(){wt(this.internals);}validate(){return M(this.internals)}validateField(e){return mt(this.internals,e)}getSubmitValues(){return V(this.internals)}goToStepWithField(e){xt(this.internals,e);}reset(e){yt(this.internals,e);}warnAboutDefinition(){let e=P(this.definition);for(let{key:r,validation:i}of this.visibility.allFields){let n=i?.validator;n&&!this.validators[n]&&e.push(`Field "${r}" references unknown validator "${n}".`);}for(let r of e)console.warn(`[FormEngine] ${r}`);}get internals(){return this}notify(e={}){this.visibility.markChanged(!!e.structureChanged,!!e.valuesChanged),this.subscriptions.notify(e);}};function Ot(t,e){let r=Object.keys(e);return r.length===Object.keys(t).length&&r.every(i=>t[i]===e[i])}function $t(t,e){return Object.entries(e).every(([r,i])=>t.errors[r]===i||t.topLevelErrors.includes(i))}function jt(t,e,r){return !e||!Ot(e,r)||!$t(t,r)}export{D as FormEngine,T as FormLifecycleError,$ as evaluateCondition,g as getDefaultMessage,m as isStepVisible,u as isVisible,jt as shouldApplyExternalErrors,P as validateDefinition,F as validateField,C as validateFields,b as validateStep};//# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map