react-f0rm 0.8.0 → 0.9.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/README.md +22 -2
- package/dist/devtools/index.cjs.js +1 -1
- package/dist/devtools/index.mjs +1 -1
- package/dist/{form-B2pyaLdK.d.ts → form-7sbcY2uT.d.ts} +21 -8
- package/dist/form-DbDDJ8bt.cjs.js +2 -0
- package/dist/form-DbDDJ8bt.cjs.js.map +1 -0
- package/dist/form-DsydpBhT.mjs +2 -0
- package/dist/form-DsydpBhT.mjs.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +46 -9
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +2 -2
- package/dist/index.umd.min.js.map +1 -1
- package/dist/resolvers/standard-schema.cjs.js +1 -1
- package/dist/resolvers/standard-schema.mjs +1 -1
- package/dist/resolvers/yup.cjs.js +1 -1
- package/dist/resolvers/yup.d.ts +1 -1
- package/dist/resolvers/yup.mjs +1 -1
- package/dist/resolvers/zod.cjs.js +1 -1
- package/dist/resolvers/zod.d.ts +1 -1
- package/dist/resolvers/zod.mjs +1 -1
- package/dist/{validate-BhOEz8BS.d.ts → validate-DXGZBon4.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/form-DnM4ONxf.cjs.js +0 -2
- package/dist/form-DnM4ONxf.cjs.js.map +0 -1
- package/dist/form-O5yKsVPM.mjs +0 -2
- package/dist/form-O5yKsVPM.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -466,7 +466,7 @@ setError(form, 'password', [
|
|
|
466
466
|
|
|
467
467
|
### `setValue` options
|
|
468
468
|
|
|
469
|
-
The fourth argument to `setValue` opts into side effects.
|
|
469
|
+
The fourth argument to `setValue` opts into side effects. `shouldValidate`/`shouldTouch` default to `false`; omitting the object keeps the plain set-value behavior:
|
|
470
470
|
|
|
471
471
|
```jsx
|
|
472
472
|
import {setValue} from 'react-f0rm';
|
|
@@ -474,10 +474,24 @@ import {setValue} from 'react-f0rm';
|
|
|
474
474
|
setValue(form, 'email', 'a@b.com', {
|
|
475
475
|
shouldValidate: true, // run the field's registered validator after the value lands
|
|
476
476
|
shouldTouch: true, // mark the field as touched
|
|
477
|
-
shouldDirty:
|
|
477
|
+
shouldDirty: false // land the value as a commit: it becomes the field's dirty baseline
|
|
478
478
|
});
|
|
479
479
|
```
|
|
480
480
|
|
|
481
|
+
Dirty state is derived, not marked: a field is dirty while its live value differs from `initialValues` (reverting to the initial value makes it clean again). That makes `shouldDirty` a one-sided flag. `shouldDirty: false` declares this write a **commit instead of an edit** — the written value becomes that field's dirty-comparison baseline, so `getDirtyFields`/`isDirty`/`getFieldState().isDirty` read the field as clean immediately, and a later write dirties it only by differing from the new baseline:
|
|
482
|
+
|
|
483
|
+
```jsx
|
|
484
|
+
setValue(form, 'email', 'normalized@x.com', {shouldDirty: false});
|
|
485
|
+
getDirtyFields(form); // {} — the normalization is not a user edit
|
|
486
|
+
|
|
487
|
+
setValue(form, 'email', 'normalized@x.com'); // still clean: equal to the baseline
|
|
488
|
+
setValue(form, 'email', 'a@b.com'); // dirty: differs from it
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Use it whenever a programmatic write is not user input — normalized/formatted values, autofill, defaults applied after mount — and you don't want it to trip the "unsaved changes" state. `shouldDirty: true` (or omitting the flag) is the default derived behavior spelled out; unlike react-hook-form, where `setValue` skips dirty marking unless opted in, react-f0rm always derives dirty from the comparison and `false` is the opt-out.
|
|
492
|
+
|
|
493
|
+
Committed baselines follow the form's lifecycle: `reset`, `setInitialValues` and `resetField`/`removeField` drop them (the state they measured against is gone), and a wholesale write at an ancestor path — a `useFieldArray` rewrite, say — drops baselines beneath it, since the subtree they were committed against no longer exists.
|
|
494
|
+
|
|
481
495
|
### Writing as a user change (`changeValue`)
|
|
482
496
|
|
|
483
497
|
`setValue` is the imperative channel — `shouldValidate` kicks the field's validator unconditionally, ignoring any mode. `changeValue` is the user-change channel: the write routes through the mounted field's own `onChange`, so it fires exactly the validation a user typing into the field would fire — the field's effective `mode` (per-field override included) and the form's `reValidateMode`. With no mounted field on the path it degrades to a plain `setValue`.
|
|
@@ -492,6 +506,12 @@ changeValue(form, 'email', 'a@b.com');
|
|
|
492
506
|
|
|
493
507
|
This is the channel component libraries need when they hand a control a plain setter bound to a field (a `Control`/controlled-bridge over `useField`'s value): the mode gating — per-field override and live-error view — lives inside `useField`'s `onChange` closure and cannot be rebuilt from public form state, so `useField` publishes its `onChange` on the form (`form.changeHandlers`) and `changeValue`/`changeValueByPath` route through it.
|
|
494
508
|
|
|
509
|
+
`changeValue` takes the same options object as `setValue` (see [`setValue` options](#setvalue-options)). With a field mounted on the path, `shouldDirty: false` applies — the write lands as a commit while the field's own mode gating keeps driving validation, which is the point of this channel (`shouldValidate`/`shouldTouch` have no meaning there: forcing them would defeat the gating). With no mounted field, the options forward to the plain `setValue` fallback wholesale:
|
|
510
|
+
|
|
511
|
+
```jsx
|
|
512
|
+
changeValue(form, 'email', 'normalized@x.com', {shouldDirty: false});
|
|
513
|
+
```
|
|
514
|
+
|
|
495
515
|
### Field-level validation
|
|
496
516
|
|
|
497
517
|
Pass a `validate` function to `Field` or `useField`. Return an error string, a `FieldError` object or `undefined` — sync or async:
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("react"),t=require("../form-DnM4ONxf.cjs.js");function n(e){var t=Object.create(null);return e&&Object.keys(e).forEach(function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}}),t.default=e,Object.freeze(t)}var r,o=n(e),a={exports:{}},l={};var s,i,d={};var c=(i||(i=1,"production"===process.env.NODE_ENV?a.exports=function(){if(r)return l;r=1;var t=e,n="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=t.useState,a=t.useEffect,s=t.useLayoutEffect,i=t.useDebugValue;function d(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!n(e,r)}catch(e){return!0}}var c="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),r=o({inst:{value:n,getSnapshot:t}}),l=r[0].inst,c=r[1];return s(function(){l.value=n,l.getSnapshot=t,d(l)&&c({inst:l})},[e,n,t]),a(function(){return d(l)&&c({inst:l}),e(function(){d(l)&&c({inst:l})})},[e]),i(n),n};return l.useSyncExternalStore=void 0!==t.useSyncExternalStore?t.useSyncExternalStore:c,l}():a.exports=(s||(s=1,"production"!==process.env.NODE_ENV&&function(){function t(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!r(e,n)}catch(e){return!0}}"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var n=e,r="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=n.useState,a=n.useEffect,l=n.useLayoutEffect,s=n.useDebugValue,i=!1,c=!1,f="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,d){i||void 0===n.startTransition||(i=!0,console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));var f=d();if(!c){var p=d();r(f,p)||(console.error("The result of getSnapshot should be cached to avoid an infinite loop"),c=!0)}var u=(p=o({inst:{value:f,getSnapshot:d}}))[0].inst,m=p[1];return l(function(){u.value=f,u.getSnapshot=d,t(u)&&m({inst:u})},[e,f,d]),a(function(){return t(u)&&m({inst:u}),e(function(){t(u)&&m({inst:u})})},[e]),s(f),f};d.useSyncExternalStore=void 0!==n.useSyncExternalStore?n.useSyncExternalStore:f,"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())}()),d)),a.exports);function f(n,r,o){return function(t,n){const r=e.useRef(null);null===r.current&&(r.current={hasValue:!1});const o=r.current,a=e.useRef(n);a.current=n;const l=e.useCallback(()=>(o.hasValue||(o.value=a.current(),o.hasValue=!0),o.value),[o]),s=e.useCallback(e=>(o.hasValue=!1,t(()=>{o.hasValue=!1,e()})),[t,o]);return c.useSyncExternalStore(s,l,l)}(e.useCallback(e=>t.on(n,r,e),[n,r]),o)}const p=e.createContext(null);p.Provider;e.createContext(null).Provider;function u({name:t,value:n,depth:r=0}){const[a,l]=e.useState(r<=1),s=void 0===t?null:o.createElement(o.Fragment,null,o.createElement("span",{className:"rf0-dt-key"},String(t)),o.createElement("span",{className:"rf0-dt-punct"},": "));if(null!==n&&"object"==typeof n){const e=Array.isArray(n),t=e?n.map((e,t)=>[t,e]):Object.entries(n),i=e?"[":"{",d=e?"]":"}",c=a?"":`${i}…${d} ${t.length}`;return o.createElement("div",{className:"rf0-dt-row",style:{paddingLeft:12*r}},o.createElement("button",{type:"button",className:"rf0-dt-node-toggle","aria-expanded":a,onClick:()=>l(!a)},o.createElement("span",{className:"rf0-dt-caret"},a?"▾":"▸"),s,o.createElement("span",{className:"rf0-dt-punct"},a?i:c)),a&&o.createElement(o.Fragment,null,t.map(([e,t])=>o.createElement(u,{key:String(e),name:e,value:t,depth:r+1})),o.createElement("span",{className:"rf0-dt-punct",style:{paddingLeft:12*r}},d)))}return o.createElement("span",{className:"rf0-dt-row",style:{paddingLeft:12*r,display:"block"}},s,o.createElement(m,{value:n}))}function m({value:e}){return void 0===e?o.createElement("span",{className:"rf0-dt-null"},"undefined"):null===e?o.createElement("span",{className:"rf0-dt-null"},"null"):"string"==typeof e?o.createElement("span",{className:"rf0-dt-string"},'"',e,'"'):"boolean"==typeof e?o.createElement("span",{className:"rf0-dt-boolean"},String(e)):o.createElement("span",{className:"rf0-dt-number"},String(e))}const b="react-f0rm-devtools-style";!function(){if("undefined"==typeof document)return;if(document.getElementById(b))return;const e=document.createElement("style");e.id=b,e.textContent="\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n",document.head.appendChild(e)}();const g=["values","errors","touched","dirty"];function x(e){if(void 0!==e)return e?"rf0-dt-ok":"rf0-dt-err"}function h(e){if(null===e||"object"!=typeof e)return 1;let t=0;for(const n of Object.values(e))t+=h(n);return t}exports.Devtools=function({form:n,position:r="top-right"}){const a=e.useContext(p),l=n??a;if(!l)throw new Error("<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.");const[s,i]=e.useState(!0),[d,c]=e.useState("values"),m=e.useId().replace(/[^a-zA-Z0-9-]/g,""),b=f(l.emitter,"change",t.getValues.bind(null,l)),y=f(l.emitter,"errors",t.getErrors.bind(null,l)),E=function(e){return f(e.emitter,"touched",t.getTouchedFields.bind(null,e))}(l),v=function(e){return f(e.emitter,"change",t.getDirtyFields.bind(null,e))}(l),w=function(e){return f(e.emitter,"submitting",()=>e.isSubmitting)}(l),k=function(e){return f(e.emitter,"submitCount",()=>e.submitCount)}(l),S=f(l.emitter,"submitSuccessful",()=>l.isSubmitSuccessful);if(!s)return o.createElement("button",{type:"button",className:`rf0-dt-badge rf0-dt-badge--${r}${y.length>0?" rf0-dt-badge--has-errors":""}`,"aria-expanded":!1,"aria-label":`Open react-f0rm devtools (${y.length} errors)`,onClick:()=>i(!0)},"f0",o.createElement("span",{className:"rf0-dt-dot"}));const O={values:h(b),errors:y.length,touched:E.length,dirty:Object.keys(v).length};return o.createElement("section",{className:`rf0-dt rf0-dt--${r}`,"aria-label":"react-f0rm devtools"},o.createElement("header",{className:"rf0-dt-header"},o.createElement("span",{className:"rf0-dt-title"},"react-f0rm"),o.createElement("button",{type:"button",className:"rf0-dt-headerbtn","aria-label":"Collapse devtools",onClick:()=>i(!1)},"–")),o.createElement("div",{className:"rf0-dt-tablist",role:"tablist","aria-label":"Form state",tabIndex:-1,onKeyDown:e=>{const t={ArrowRight:1,ArrowLeft:-1}[e.key];if(!t)return;e.preventDefault();const n=g[(g.indexOf(d)+t+g.length)%g.length];c(n),document.getElementById(`${m}-tab-${n}`)?.focus()}},g.map(e=>o.createElement("button",{key:e,id:`${m}-tab-${e}`,type:"button",role:"tab",className:"rf0-dt-tab"+("errors"===e?" rf0-dt-tab--danger":""),"aria-selected":d===e,"aria-controls":`${m}-panel-${e}`,tabIndex:d===e?0:-1,onClick:()=>c(e)},e,o.createElement("span",{className:"rf0-dt-tab-count"},O[e])))),o.createElement("div",{id:`${m}-panel-${d}`,role:"tabpanel","aria-labelledby":`${m}-tab-${d}`,className:"rf0-dt-panel"},"values"===d&&o.createElement(u,{value:b}),"errors"===d&&(0===y.length?o.createElement("p",{className:"rf0-dt-empty"},"no errors"):y.map(({path:e,type:t,message:n},r)=>o.createElement("div",{key:`${e}:${r}`,className:"rf0-dt-item"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg"},n),o.createElement("span",{className:"rf0-dt-item-tag"},t)))),"touched"===d&&(0===E.length?o.createElement("p",{className:"rf0-dt-empty"},"no touched fields"):E.map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--touched"},o.createElement("span",{className:"rf0-dt-item-path"},e)))),"dirty"===d&&(0===Object.keys(v).length?o.createElement("p",{className:"rf0-dt-empty"},"no dirty fields"):Object.keys(v).map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--dirty"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg rf0-dt-item-msg--ok"},"changed"))))),o.createElement("p",{className:"rf0-dt-status","aria-live":"polite"},o.createElement("span",{className:w?"rf0-dt-on":void 0},"submitting ",o.createElement("b",null,String(w))),o.createElement("span",null,"submits ",o.createElement("b",null,k)),o.createElement("span",{className:x(S)},"ok"," ",o.createElement("b",null,void 0===S?"–":String(S)))),o.createElement("div",{className:"rf0-dt-actions"},o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.reset(l,l.initialValues)},"Reset"),o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.trigger(l)},"Validate")))};
|
|
1
|
+
"use strict";var e=require("react"),t=require("../form-DbDDJ8bt.cjs.js");function n(e){var t=Object.create(null);return e&&Object.keys(e).forEach(function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}}),t.default=e,Object.freeze(t)}var r,o=n(e),a={exports:{}},l={};var s,i,d={};var c=(i||(i=1,"production"===process.env.NODE_ENV?a.exports=function(){if(r)return l;r=1;var t=e,n="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=t.useState,a=t.useEffect,s=t.useLayoutEffect,i=t.useDebugValue;function d(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!n(e,r)}catch(e){return!0}}var c="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),r=o({inst:{value:n,getSnapshot:t}}),l=r[0].inst,c=r[1];return s(function(){l.value=n,l.getSnapshot=t,d(l)&&c({inst:l})},[e,n,t]),a(function(){return d(l)&&c({inst:l}),e(function(){d(l)&&c({inst:l})})},[e]),i(n),n};return l.useSyncExternalStore=void 0!==t.useSyncExternalStore?t.useSyncExternalStore:c,l}():a.exports=(s||(s=1,"production"!==process.env.NODE_ENV&&function(){function t(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!r(e,n)}catch(e){return!0}}"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var n=e,r="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=n.useState,a=n.useEffect,l=n.useLayoutEffect,s=n.useDebugValue,i=!1,c=!1,f="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,d){i||void 0===n.startTransition||(i=!0,console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));var f=d();if(!c){var p=d();r(f,p)||(console.error("The result of getSnapshot should be cached to avoid an infinite loop"),c=!0)}var u=(p=o({inst:{value:f,getSnapshot:d}}))[0].inst,m=p[1];return l(function(){u.value=f,u.getSnapshot=d,t(u)&&m({inst:u})},[e,f,d]),a(function(){return t(u)&&m({inst:u}),e(function(){t(u)&&m({inst:u})})},[e]),s(f),f};d.useSyncExternalStore=void 0!==n.useSyncExternalStore?n.useSyncExternalStore:f,"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())}()),d)),a.exports);function f(n,r,o){return function(t,n){const r=e.useRef(null);null===r.current&&(r.current={hasValue:!1});const o=r.current,a=e.useRef(n);a.current=n;const l=e.useCallback(()=>(o.hasValue||(o.value=a.current(),o.hasValue=!0),o.value),[o]),s=e.useCallback(e=>(o.hasValue=!1,t(()=>{o.hasValue=!1,e()})),[t,o]);return c.useSyncExternalStore(s,l,l)}(e.useCallback(e=>t.on(n,r,e),[n,r]),o)}const p=e.createContext(null);p.Provider;e.createContext(null).Provider;function u({name:t,value:n,depth:r=0}){const[a,l]=e.useState(r<=1),s=void 0===t?null:o.createElement(o.Fragment,null,o.createElement("span",{className:"rf0-dt-key"},String(t)),o.createElement("span",{className:"rf0-dt-punct"},": "));if(null!==n&&"object"==typeof n){const e=Array.isArray(n),t=e?n.map((e,t)=>[t,e]):Object.entries(n),i=e?"[":"{",d=e?"]":"}",c=a?"":`${i}…${d} ${t.length}`;return o.createElement("div",{className:"rf0-dt-row",style:{paddingLeft:12*r}},o.createElement("button",{type:"button",className:"rf0-dt-node-toggle","aria-expanded":a,onClick:()=>l(!a)},o.createElement("span",{className:"rf0-dt-caret"},a?"▾":"▸"),s,o.createElement("span",{className:"rf0-dt-punct"},a?i:c)),a&&o.createElement(o.Fragment,null,t.map(([e,t])=>o.createElement(u,{key:String(e),name:e,value:t,depth:r+1})),o.createElement("span",{className:"rf0-dt-punct",style:{paddingLeft:12*r}},d)))}return o.createElement("span",{className:"rf0-dt-row",style:{paddingLeft:12*r,display:"block"}},s,o.createElement(m,{value:n}))}function m({value:e}){return void 0===e?o.createElement("span",{className:"rf0-dt-null"},"undefined"):null===e?o.createElement("span",{className:"rf0-dt-null"},"null"):"string"==typeof e?o.createElement("span",{className:"rf0-dt-string"},'"',e,'"'):"boolean"==typeof e?o.createElement("span",{className:"rf0-dt-boolean"},String(e)):o.createElement("span",{className:"rf0-dt-number"},String(e))}const b="react-f0rm-devtools-style";!function(){if("undefined"==typeof document)return;if(document.getElementById(b))return;const e=document.createElement("style");e.id=b,e.textContent="\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n",document.head.appendChild(e)}();const g=["values","errors","touched","dirty"];function x(e){if(void 0!==e)return e?"rf0-dt-ok":"rf0-dt-err"}function h(e){if(null===e||"object"!=typeof e)return 1;let t=0;for(const n of Object.values(e))t+=h(n);return t}exports.Devtools=function({form:n,position:r="top-right"}){const a=e.useContext(p),l=n??a;if(!l)throw new Error("<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.");const[s,i]=e.useState(!0),[d,c]=e.useState("values"),m=e.useId().replace(/[^a-zA-Z0-9-]/g,""),b=f(l.emitter,"change",t.getValues.bind(null,l)),y=f(l.emitter,"errors",t.getErrors.bind(null,l)),E=function(e){return f(e.emitter,"touched",t.getTouchedFields.bind(null,e))}(l),v=function(e){return f(e.emitter,"change",t.getDirtyFields.bind(null,e))}(l),w=function(e){return f(e.emitter,"submitting",()=>e.isSubmitting)}(l),k=function(e){return f(e.emitter,"submitCount",()=>e.submitCount)}(l),S=f(l.emitter,"submitSuccessful",()=>l.isSubmitSuccessful);if(!s)return o.createElement("button",{type:"button",className:`rf0-dt-badge rf0-dt-badge--${r}${y.length>0?" rf0-dt-badge--has-errors":""}`,"aria-expanded":!1,"aria-label":`Open react-f0rm devtools (${y.length} errors)`,onClick:()=>i(!0)},"f0",o.createElement("span",{className:"rf0-dt-dot"}));const O={values:h(b),errors:y.length,touched:E.length,dirty:Object.keys(v).length};return o.createElement("section",{className:`rf0-dt rf0-dt--${r}`,"aria-label":"react-f0rm devtools"},o.createElement("header",{className:"rf0-dt-header"},o.createElement("span",{className:"rf0-dt-title"},"react-f0rm"),o.createElement("button",{type:"button",className:"rf0-dt-headerbtn","aria-label":"Collapse devtools",onClick:()=>i(!1)},"–")),o.createElement("div",{className:"rf0-dt-tablist",role:"tablist","aria-label":"Form state",tabIndex:-1,onKeyDown:e=>{const t={ArrowRight:1,ArrowLeft:-1}[e.key];if(!t)return;e.preventDefault();const n=g[(g.indexOf(d)+t+g.length)%g.length];c(n),document.getElementById(`${m}-tab-${n}`)?.focus()}},g.map(e=>o.createElement("button",{key:e,id:`${m}-tab-${e}`,type:"button",role:"tab",className:"rf0-dt-tab"+("errors"===e?" rf0-dt-tab--danger":""),"aria-selected":d===e,"aria-controls":`${m}-panel-${e}`,tabIndex:d===e?0:-1,onClick:()=>c(e)},e,o.createElement("span",{className:"rf0-dt-tab-count"},O[e])))),o.createElement("div",{id:`${m}-panel-${d}`,role:"tabpanel","aria-labelledby":`${m}-tab-${d}`,className:"rf0-dt-panel"},"values"===d&&o.createElement(u,{value:b}),"errors"===d&&(0===y.length?o.createElement("p",{className:"rf0-dt-empty"},"no errors"):y.map(({path:e,type:t,message:n},r)=>o.createElement("div",{key:`${e}:${r}`,className:"rf0-dt-item"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg"},n),o.createElement("span",{className:"rf0-dt-item-tag"},t)))),"touched"===d&&(0===E.length?o.createElement("p",{className:"rf0-dt-empty"},"no touched fields"):E.map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--touched"},o.createElement("span",{className:"rf0-dt-item-path"},e)))),"dirty"===d&&(0===Object.keys(v).length?o.createElement("p",{className:"rf0-dt-empty"},"no dirty fields"):Object.keys(v).map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--dirty"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg rf0-dt-item-msg--ok"},"changed"))))),o.createElement("p",{className:"rf0-dt-status","aria-live":"polite"},o.createElement("span",{className:w?"rf0-dt-on":void 0},"submitting ",o.createElement("b",null,String(w))),o.createElement("span",null,"submits ",o.createElement("b",null,k)),o.createElement("span",{className:x(S)},"ok"," ",o.createElement("b",null,void 0===S?"–":String(S)))),o.createElement("div",{className:"rf0-dt-actions"},o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.reset(l,l.initialValues)},"Reset"),o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.trigger(l)},"Validate")))};
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/devtools/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import*as e from"react";import t,{useCallback as n,useRef as r,createContext as o,useState as a,useContext as l,useId as i}from"react";import{o as s,g as d,a as c,b as f,c as p,r as u,t as m}from"../form-O5yKsVPM.mjs";var b,g={exports:{}},x={};var h,E,y={};var v=(E||(E=1,"production"===process.env.NODE_ENV?g.exports=function(){if(b)return x;b=1;var e=t,n="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},r=e.useState,o=e.useEffect,a=e.useLayoutEffect,l=e.useDebugValue;function i(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!n(e,r)}catch(e){return!0}}var s="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),s=r({inst:{value:n,getSnapshot:t}}),d=s[0].inst,c=s[1];return a(function(){d.value=n,d.getSnapshot=t,i(d)&&c({inst:d})},[e,n,t]),o(function(){return i(d)&&c({inst:d}),e(function(){i(d)&&c({inst:d})})},[e]),l(n),n};return x.useSyncExternalStore=void 0!==e.useSyncExternalStore?e.useSyncExternalStore:s,x}():g.exports=(h||(h=1,"production"!==process.env.NODE_ENV&&function(){function e(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!r(e,n)}catch(e){return!0}}"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var n=t,r="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=n.useState,a=n.useEffect,l=n.useLayoutEffect,i=n.useDebugValue,s=!1,d=!1,c="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(t,c){s||void 0===n.startTransition||(s=!0,console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));var f=c();if(!d){var p=c();r(f,p)||(console.error("The result of getSnapshot should be cached to avoid an infinite loop"),d=!0)}var u=(p=o({inst:{value:f,getSnapshot:c}}))[0].inst,m=p[1];return l(function(){u.value=f,u.getSnapshot=c,e(u)&&m({inst:u})},[t,f,c]),a(function(){return e(u)&&m({inst:u}),t(function(){e(u)&&m({inst:u})})},[t]),i(f),f};y.useSyncExternalStore=void 0!==n.useSyncExternalStore?n.useSyncExternalStore:c,"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())}()),y)),g.exports);function w(e,t,o){return function(e,t){const o=r(null);null===o.current&&(o.current={hasValue:!1});const a=o.current,l=r(t);l.current=t;const i=n(()=>(a.hasValue||(a.value=l.current(),a.hasValue=!0),a.value),[a]),s=n(t=>(a.hasValue=!1,e(()=>{a.hasValue=!1,t()})),[e,a]);return v.useSyncExternalStore(s,i,i)}(n(n=>s(e,t,n),[e,t]),o)}const S=o(null);S.Provider;o(null).Provider;function k({name:t,value:n,depth:r=0}){const[o,l]=a(r<=1),i=void 0===t?null:e.createElement(e.Fragment,null,e.createElement("span",{className:"rf0-dt-key"},String(t)),e.createElement("span",{className:"rf0-dt-punct"},": "));if(null!==n&&"object"==typeof n){const t=Array.isArray(n),a=t?n.map((e,t)=>[t,e]):Object.entries(n),s=t?"[":"{",d=t?"]":"}",c=o?"":`${s}…${d} ${a.length}`;return e.createElement("div",{className:"rf0-dt-row",style:{paddingLeft:12*r}},e.createElement("button",{type:"button",className:"rf0-dt-node-toggle","aria-expanded":o,onClick:()=>l(!o)},e.createElement("span",{className:"rf0-dt-caret"},o?"▾":"▸"),i,e.createElement("span",{className:"rf0-dt-punct"},o?s:c)),o&&e.createElement(e.Fragment,null,a.map(([t,n])=>e.createElement(k,{key:String(t),name:t,value:n,depth:r+1})),e.createElement("span",{className:"rf0-dt-punct",style:{paddingLeft:12*r}},d)))}return e.createElement("span",{className:"rf0-dt-row",style:{paddingLeft:12*r,display:"block"}},i,e.createElement(N,{value:n}))}function N({value:t}){return void 0===t?e.createElement("span",{className:"rf0-dt-null"},"undefined"):null===t?e.createElement("span",{className:"rf0-dt-null"},"null"):"string"==typeof t?e.createElement("span",{className:"rf0-dt-string"},'"',t,'"'):"boolean"==typeof t?e.createElement("span",{className:"rf0-dt-boolean"},String(t)):e.createElement("span",{className:"rf0-dt-number"},String(t))}const _="react-f0rm-devtools-style";!function(){if("undefined"==typeof document)return;if(document.getElementById(_))return;const e=document.createElement("style");e.id=_,e.textContent="\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n",document.head.appendChild(e)}();const O=["values","errors","touched","dirty"];function L(e){if(void 0!==e)return e?"rf0-dt-ok":"rf0-dt-err"}function C(e){if(null===e||"object"!=typeof e)return 1;let t=0;for(const n of Object.values(e))t+=C(n);return t}function $({form:t,position:n="top-right"}){const r=l(S),o=t??r;if(!o)throw new Error("<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.");const[s,b]=a(!0),[g,x]=a("values"),h=i().replace(/[^a-zA-Z0-9-]/g,""),E=w(o.emitter,"change",f.bind(null,o)),y=w(o.emitter,"errors",p.bind(null,o)),v=function(e){return w(e.emitter,"touched",d.bind(null,e))}(o),N=function(e){return w(e.emitter,"change",c.bind(null,e))}(o),_=function(e){return w(e.emitter,"submitting",()=>e.isSubmitting)}(o),$=function(e){return w(e.emitter,"submitCount",()=>e.submitCount)}(o),A=w(o.emitter,"submitSuccessful",()=>o.isSubmitSuccessful);if(!s)return e.createElement("button",{type:"button",className:`rf0-dt-badge rf0-dt-badge--${n}${y.length>0?" rf0-dt-badge--has-errors":""}`,"aria-expanded":!1,"aria-label":`Open react-f0rm devtools (${y.length} errors)`,onClick:()=>b(!0)},"f0",e.createElement("span",{className:"rf0-dt-dot"}));const V={values:C(E),errors:y.length,touched:v.length,dirty:Object.keys(N).length};return e.createElement("section",{className:`rf0-dt rf0-dt--${n}`,"aria-label":"react-f0rm devtools"},e.createElement("header",{className:"rf0-dt-header"},e.createElement("span",{className:"rf0-dt-title"},"react-f0rm"),e.createElement("button",{type:"button",className:"rf0-dt-headerbtn","aria-label":"Collapse devtools",onClick:()=>b(!1)},"–")),e.createElement("div",{className:"rf0-dt-tablist",role:"tablist","aria-label":"Form state",tabIndex:-1,onKeyDown:e=>{const t={ArrowRight:1,ArrowLeft:-1}[e.key];if(!t)return;e.preventDefault();const n=O[(O.indexOf(g)+t+O.length)%O.length];x(n),document.getElementById(`${h}-tab-${n}`)?.focus()}},O.map(t=>e.createElement("button",{key:t,id:`${h}-tab-${t}`,type:"button",role:"tab",className:"rf0-dt-tab"+("errors"===t?" rf0-dt-tab--danger":""),"aria-selected":g===t,"aria-controls":`${h}-panel-${t}`,tabIndex:g===t?0:-1,onClick:()=>x(t)},t,e.createElement("span",{className:"rf0-dt-tab-count"},V[t])))),e.createElement("div",{id:`${h}-panel-${g}`,role:"tabpanel","aria-labelledby":`${h}-tab-${g}`,className:"rf0-dt-panel"},"values"===g&&e.createElement(k,{value:E}),"errors"===g&&(0===y.length?e.createElement("p",{className:"rf0-dt-empty"},"no errors"):y.map(({path:t,type:n,message:r},o)=>e.createElement("div",{key:`${t}:${o}`,className:"rf0-dt-item"},e.createElement("span",{className:"rf0-dt-item-path"},t),e.createElement("span",{className:"rf0-dt-item-msg"},r),e.createElement("span",{className:"rf0-dt-item-tag"},n)))),"touched"===g&&(0===v.length?e.createElement("p",{className:"rf0-dt-empty"},"no touched fields"):v.map(t=>e.createElement("div",{key:t,className:"rf0-dt-item rf0-dt-item--touched"},e.createElement("span",{className:"rf0-dt-item-path"},t)))),"dirty"===g&&(0===Object.keys(N).length?e.createElement("p",{className:"rf0-dt-empty"},"no dirty fields"):Object.keys(N).map(t=>e.createElement("div",{key:t,className:"rf0-dt-item rf0-dt-item--dirty"},e.createElement("span",{className:"rf0-dt-item-path"},t),e.createElement("span",{className:"rf0-dt-item-msg rf0-dt-item-msg--ok"},"changed"))))),e.createElement("p",{className:"rf0-dt-status","aria-live":"polite"},e.createElement("span",{className:_?"rf0-dt-on":void 0},"submitting ",e.createElement("b",null,String(_))),e.createElement("span",null,"submits ",e.createElement("b",null,$)),e.createElement("span",{className:L(A)},"ok"," ",e.createElement("b",null,void 0===A?"–":String(A)))),e.createElement("div",{className:"rf0-dt-actions"},e.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>u(o,o.initialValues)},"Reset"),e.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>m(o)},"Validate")))}export{$ as Devtools};
|
|
1
|
+
import*as e from"react";import t,{useCallback as n,useRef as r,createContext as o,useState as a,useContext as l,useId as i}from"react";import{o as s,g as d,a as c,b as f,c as p,r as u,t as m}from"../form-DsydpBhT.mjs";var b,g={exports:{}},x={};var h,E,y={};var v=(E||(E=1,"production"===process.env.NODE_ENV?g.exports=function(){if(b)return x;b=1;var e=t,n="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},r=e.useState,o=e.useEffect,a=e.useLayoutEffect,l=e.useDebugValue;function i(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!n(e,r)}catch(e){return!0}}var s="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),s=r({inst:{value:n,getSnapshot:t}}),d=s[0].inst,c=s[1];return a(function(){d.value=n,d.getSnapshot=t,i(d)&&c({inst:d})},[e,n,t]),o(function(){return i(d)&&c({inst:d}),e(function(){i(d)&&c({inst:d})})},[e]),l(n),n};return x.useSyncExternalStore=void 0!==e.useSyncExternalStore?e.useSyncExternalStore:s,x}():g.exports=(h||(h=1,"production"!==process.env.NODE_ENV&&function(){function e(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!r(e,n)}catch(e){return!0}}"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var n=t,r="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=n.useState,a=n.useEffect,l=n.useLayoutEffect,i=n.useDebugValue,s=!1,d=!1,c="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(t,c){s||void 0===n.startTransition||(s=!0,console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));var f=c();if(!d){var p=c();r(f,p)||(console.error("The result of getSnapshot should be cached to avoid an infinite loop"),d=!0)}var u=(p=o({inst:{value:f,getSnapshot:c}}))[0].inst,m=p[1];return l(function(){u.value=f,u.getSnapshot=c,e(u)&&m({inst:u})},[t,f,c]),a(function(){return e(u)&&m({inst:u}),t(function(){e(u)&&m({inst:u})})},[t]),i(f),f};y.useSyncExternalStore=void 0!==n.useSyncExternalStore?n.useSyncExternalStore:c,"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())}()),y)),g.exports);function w(e,t,o){return function(e,t){const o=r(null);null===o.current&&(o.current={hasValue:!1});const a=o.current,l=r(t);l.current=t;const i=n(()=>(a.hasValue||(a.value=l.current(),a.hasValue=!0),a.value),[a]),s=n(t=>(a.hasValue=!1,e(()=>{a.hasValue=!1,t()})),[e,a]);return v.useSyncExternalStore(s,i,i)}(n(n=>s(e,t,n),[e,t]),o)}const S=o(null);S.Provider;o(null).Provider;function k({name:t,value:n,depth:r=0}){const[o,l]=a(r<=1),i=void 0===t?null:e.createElement(e.Fragment,null,e.createElement("span",{className:"rf0-dt-key"},String(t)),e.createElement("span",{className:"rf0-dt-punct"},": "));if(null!==n&&"object"==typeof n){const t=Array.isArray(n),a=t?n.map((e,t)=>[t,e]):Object.entries(n),s=t?"[":"{",d=t?"]":"}",c=o?"":`${s}…${d} ${a.length}`;return e.createElement("div",{className:"rf0-dt-row",style:{paddingLeft:12*r}},e.createElement("button",{type:"button",className:"rf0-dt-node-toggle","aria-expanded":o,onClick:()=>l(!o)},e.createElement("span",{className:"rf0-dt-caret"},o?"▾":"▸"),i,e.createElement("span",{className:"rf0-dt-punct"},o?s:c)),o&&e.createElement(e.Fragment,null,a.map(([t,n])=>e.createElement(k,{key:String(t),name:t,value:n,depth:r+1})),e.createElement("span",{className:"rf0-dt-punct",style:{paddingLeft:12*r}},d)))}return e.createElement("span",{className:"rf0-dt-row",style:{paddingLeft:12*r,display:"block"}},i,e.createElement(N,{value:n}))}function N({value:t}){return void 0===t?e.createElement("span",{className:"rf0-dt-null"},"undefined"):null===t?e.createElement("span",{className:"rf0-dt-null"},"null"):"string"==typeof t?e.createElement("span",{className:"rf0-dt-string"},'"',t,'"'):"boolean"==typeof t?e.createElement("span",{className:"rf0-dt-boolean"},String(t)):e.createElement("span",{className:"rf0-dt-number"},String(t))}const _="react-f0rm-devtools-style";!function(){if("undefined"==typeof document)return;if(document.getElementById(_))return;const e=document.createElement("style");e.id=_,e.textContent="\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n",document.head.appendChild(e)}();const O=["values","errors","touched","dirty"];function L(e){if(void 0!==e)return e?"rf0-dt-ok":"rf0-dt-err"}function C(e){if(null===e||"object"!=typeof e)return 1;let t=0;for(const n of Object.values(e))t+=C(n);return t}function $({form:t,position:n="top-right"}){const r=l(S),o=t??r;if(!o)throw new Error("<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.");const[s,b]=a(!0),[g,x]=a("values"),h=i().replace(/[^a-zA-Z0-9-]/g,""),E=w(o.emitter,"change",f.bind(null,o)),y=w(o.emitter,"errors",p.bind(null,o)),v=function(e){return w(e.emitter,"touched",d.bind(null,e))}(o),N=function(e){return w(e.emitter,"change",c.bind(null,e))}(o),_=function(e){return w(e.emitter,"submitting",()=>e.isSubmitting)}(o),$=function(e){return w(e.emitter,"submitCount",()=>e.submitCount)}(o),A=w(o.emitter,"submitSuccessful",()=>o.isSubmitSuccessful);if(!s)return e.createElement("button",{type:"button",className:`rf0-dt-badge rf0-dt-badge--${n}${y.length>0?" rf0-dt-badge--has-errors":""}`,"aria-expanded":!1,"aria-label":`Open react-f0rm devtools (${y.length} errors)`,onClick:()=>b(!0)},"f0",e.createElement("span",{className:"rf0-dt-dot"}));const V={values:C(E),errors:y.length,touched:v.length,dirty:Object.keys(N).length};return e.createElement("section",{className:`rf0-dt rf0-dt--${n}`,"aria-label":"react-f0rm devtools"},e.createElement("header",{className:"rf0-dt-header"},e.createElement("span",{className:"rf0-dt-title"},"react-f0rm"),e.createElement("button",{type:"button",className:"rf0-dt-headerbtn","aria-label":"Collapse devtools",onClick:()=>b(!1)},"–")),e.createElement("div",{className:"rf0-dt-tablist",role:"tablist","aria-label":"Form state",tabIndex:-1,onKeyDown:e=>{const t={ArrowRight:1,ArrowLeft:-1}[e.key];if(!t)return;e.preventDefault();const n=O[(O.indexOf(g)+t+O.length)%O.length];x(n),document.getElementById(`${h}-tab-${n}`)?.focus()}},O.map(t=>e.createElement("button",{key:t,id:`${h}-tab-${t}`,type:"button",role:"tab",className:"rf0-dt-tab"+("errors"===t?" rf0-dt-tab--danger":""),"aria-selected":g===t,"aria-controls":`${h}-panel-${t}`,tabIndex:g===t?0:-1,onClick:()=>x(t)},t,e.createElement("span",{className:"rf0-dt-tab-count"},V[t])))),e.createElement("div",{id:`${h}-panel-${g}`,role:"tabpanel","aria-labelledby":`${h}-tab-${g}`,className:"rf0-dt-panel"},"values"===g&&e.createElement(k,{value:E}),"errors"===g&&(0===y.length?e.createElement("p",{className:"rf0-dt-empty"},"no errors"):y.map(({path:t,type:n,message:r},o)=>e.createElement("div",{key:`${t}:${o}`,className:"rf0-dt-item"},e.createElement("span",{className:"rf0-dt-item-path"},t),e.createElement("span",{className:"rf0-dt-item-msg"},r),e.createElement("span",{className:"rf0-dt-item-tag"},n)))),"touched"===g&&(0===v.length?e.createElement("p",{className:"rf0-dt-empty"},"no touched fields"):v.map(t=>e.createElement("div",{key:t,className:"rf0-dt-item rf0-dt-item--touched"},e.createElement("span",{className:"rf0-dt-item-path"},t)))),"dirty"===g&&(0===Object.keys(N).length?e.createElement("p",{className:"rf0-dt-empty"},"no dirty fields"):Object.keys(N).map(t=>e.createElement("div",{key:t,className:"rf0-dt-item rf0-dt-item--dirty"},e.createElement("span",{className:"rf0-dt-item-path"},t),e.createElement("span",{className:"rf0-dt-item-msg rf0-dt-item-msg--ok"},"changed"))))),e.createElement("p",{className:"rf0-dt-status","aria-live":"polite"},e.createElement("span",{className:_?"rf0-dt-on":void 0},"submitting ",e.createElement("b",null,String(_))),e.createElement("span",null,"submits ",e.createElement("b",null,$)),e.createElement("span",{className:L(A)},"ok"," ",e.createElement("b",null,void 0===A?"–":String(A)))),e.createElement("div",{className:"rf0-dt-actions"},e.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>u(o,o.initialValues)},"Reset"),e.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>m(o)},"Validate")))}export{$ as Devtools};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -237,18 +237,23 @@ declare function getValue<T extends Record<string, any> = any, P extends FieldPa
|
|
|
237
237
|
* @param path
|
|
238
238
|
*/
|
|
239
239
|
declare function getValueByPath({ initialValues, parsedValues, values, deleted }: Form, path: Path): any;
|
|
240
|
-
/** Options accepted by {@link setValue} / {@link setValueByPath}
|
|
241
|
-
*
|
|
242
|
-
*
|
|
240
|
+
/** Options accepted by {@link setValue} / {@link setValueByPath} / {@link
|
|
241
|
+
* changeValue} / {@link changeValueByPath}. `shouldValidate`/`shouldTouch`
|
|
242
|
+
* default to `false`; omitting the options object entirely keeps the plain
|
|
243
|
+
* set-value behavior (no validation, no touched marking, dirty stays
|
|
244
|
+
* derived). */
|
|
243
245
|
interface SetFieldOptions {
|
|
244
246
|
/** Run the field's registered validator (if any) after the value lands,
|
|
245
247
|
* same as triggering that single field. Defaults to `false`. */
|
|
246
248
|
shouldValidate?: boolean;
|
|
247
249
|
/** Mark the field as touched. Defaults to `false`. */
|
|
248
250
|
shouldTouch?: boolean;
|
|
249
|
-
/**
|
|
250
|
-
*
|
|
251
|
-
*
|
|
251
|
+
/** Land the value as a commit instead of an edit: the value becomes the
|
|
252
|
+
* field's dirty-comparison baseline, so `getDirtyFields`/`isDirty`/
|
|
253
|
+
* `getFieldState().isDirty` read the field as clean, and a later write
|
|
254
|
+
* dirties it only by differing from the new baseline. `true` (or
|
|
255
|
+
* omitting the flag) keeps the default derived behavior — dirty while
|
|
256
|
+
* the live value differs from initialValues. */
|
|
252
257
|
shouldDirty?: boolean;
|
|
253
258
|
}
|
|
254
259
|
/**
|
|
@@ -288,18 +293,26 @@ declare function setValueByPath(form: Form, path: Path, value: any, options?: Se
|
|
|
288
293
|
* ignoring any mode. Functional updaters are the caller's to evaluate
|
|
289
294
|
* ({@link getValue}).
|
|
290
295
|
*
|
|
296
|
+
* `options` carries the same {@link SetFieldOptions}: on the fallback path
|
|
297
|
+
* (no mounted field) they forward to {@link setValueByPath} wholesale,
|
|
298
|
+
* while on the mounted-field path only `shouldDirty: false` applies — the
|
|
299
|
+
* write lands as a commit while the field's own mode gating keeps driving
|
|
300
|
+
* validation, which is the point of this channel.
|
|
301
|
+
*
|
|
291
302
|
* @param form
|
|
292
303
|
* @param name
|
|
293
304
|
* @param value
|
|
305
|
+
* @param options
|
|
294
306
|
*/
|
|
295
|
-
declare function changeValue<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P, value: PathValueOf<T, P
|
|
307
|
+
declare function changeValue<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P, value: PathValueOf<T, P>, options?: SetFieldOptions): void;
|
|
296
308
|
/**
|
|
297
309
|
* Set a field value as a user change, by parsed path
|
|
298
310
|
* @param form
|
|
299
311
|
* @param path
|
|
300
312
|
* @param value
|
|
313
|
+
* @param options
|
|
301
314
|
*/
|
|
302
|
-
declare function changeValueByPath(form: Form, path: Path, value: any): void;
|
|
315
|
+
declare function changeValueByPath(form: Form, path: Path, value: any, options?: SetFieldOptions): void;
|
|
303
316
|
/**
|
|
304
317
|
* Get field error
|
|
305
318
|
* @param form
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";function e(e,t){for(var r=arguments.length,n=new Array(r>2?r-2:0),o=2;o<r;o++)n[o-2]=arguments[o];(e.get(t)||[]).forEach(function(e){return e.apply(void 0,n)})}function t(e,t,r){var n=function(e,t){var r=e.get(t);if(r)return r;var n=new Set;return e.set(t,n),n}(e,t);return n.add(r),function(){return n.delete(r)}}const r=new Map;function n(e){if(Array.isArray(e))return e;const t=r.get(e);if(t)return t;const n=function(e){const t=[];let r="";const n=()=>{t.push(r),r=""};for(let o=0;o<e.length;o++){const s=e[o];if("."===s)""!==r&&n();else if("["===s){""!==r&&n();const s=e[o+1];if('"'===s||"'"===s){const r=e.indexOf(s,o+2);if(-1===r)throw new TypeError(`Unterminated quote in path: ${e}`);if("]"!==e[r+1])throw new TypeError(`Expected "]" after quoted segment in path: ${e}`);t.push(e.slice(o+2,r)),o=r+1}else{const r=e.indexOf("]",o+1);if(-1===r)throw new TypeError(`Unterminated bracket in path: ${e}`);const n=e.slice(o+1,r);t.push(/^-?\d+$/.test(n)?Number(n):n),o=r}}else r+=s}""===r&&0!==t.length||n();return t}(e);return r.set(e,n),n}function o(e,t){if(!t.length||null==e)return e;const[r,...n]=t;if(n.length){const t=o(e[r],n);return t===e[r]?e:s(e,[r],t)}if(Array.isArray(e)){if(!(r in e))return e;const t=e.slice();return delete t[r],t}if("object"!=typeof e||!(r in e))return e;const i={...e};return delete i[r],i}function s(e,t,r){if(!t.length)return r;const[n,...o]=t;if("number"==typeof n){const t=Array.isArray(e)?e.slice():[];return t[n]=s(t[n],o,r),t}return{...e,[n]:s(e&&e[n],o,r)}}function i(e,t,r,n){if(!t.length)return r;let o=e,s=null,i="";for(let u=0;u<t.length;u++){const c=t[u];if(!n.has(o)){let t;t="number"==typeof c?Array.isArray(o)?o.slice():[]:{...o},n.add(t),0===u?e=t:s[i]=t,o=t}u===t.length-1?o[c]=r:(s=o,i=c,o=o[c])}return e}function u(e){const t=n(e);return{value:t,key:JSON.stringify(t)}}const c=Symbol("validation-outcome");function a(e){const{initialValues:t,parsedValues:r,values:n,deleted:s}=e,u=new Set;let c=r??t;for(const[e,t]of n)c=i(c,JSON.parse(e),t,u);for(const e of s)c=o(c,JSON.parse(e));return c}function l(t,r,n,o){const{emitter:s,values:i,deleted:u}=t;i.set(r.key,n),function(e,{key:t}){if(!e.size)return;for(const r of e)(r===t||r.startsWith(`${t.slice(0,-1)},`)||t.startsWith(`${r.slice(0,-1)},`))&&e.delete(r)}(u,r),function(e,{key:t}){const r=d.get(e);if(!r?.size)return;const n=`${t.slice(0,-1)},`;for(const e of r.keys())e.startsWith(n)&&r.delete(e)}(t,r),p(t),e(s,"change",r)}function f(t,r,n){!function({emitter:t,errors:r},n,o){const s=function(e){if("string"==typeof e)return e?[{type:"custom",message:e}]:void 0;if(y(e))return[e];if(!e)return;const t=[];return e.forEach(e=>{"string"==typeof e&&e?t.push({type:"custom",message:e}):y(e)&&t.push(e)}),t.length?t:void 0}(o);s?r.set(n.key,s):r.delete(n.key);e(t,"errors",n)}(t,u(r),n)}const d=new WeakMap;function g(e,t,r){const n=d.get(e);return n?.has(t)?n.get(t):(o=e.initialValues,r.reduce((e,t)=>{if(null!=e)return e[t]},o));var o}const m=new WeakMap;function p(e){const t=m.get(e);t&&t.version++}function v(e){const t={};return function(e,t){for(const[r,n]of e.values){const o=JSON.parse(r);g(e,r,o)!==n&&t(o.join("."))}}(e,e=>{t[e]=!0}),t}function y(e){return!!e&&"object"==typeof e&&"string"==typeof e.type&&"string"==typeof e.message}function h(e,t,r=[]){Object.entries(t).forEach(([t,o])=>{const s=[...r,...n(t)];"string"==typeof o?o&&f(e,s,o):Array.isArray(o)||y(o)?f(e,s,o):o&&"object"==typeof o&&h(e,o,s)})}function w(t,r){if(r){if("object"==typeof r&&c in r){const n=r;return n.errors&&h(t,n.errors),void function(t,r){void 0!==r&&r!==t.parsedValues&&(t.parsedValues=r,e(t.emitter,"change"))}(t,n.values)}h(t,r)}}const b="__form_validate__";const k=Symbol("form-validate-settled"),A=new WeakMap;function O(t,r,n,o){if(r.round!==n)return;if(r.round=null,null!==r.timer)return;r.marked&&(r.marked=!1,t.validating.delete(b),e(t.emitter,"validating"));const s=r.waiters;r.waiters=[];for(const e of s)o===k?e.resolve():e.reject(o)}exports.VALIDATION_OUTCOME=c,exports.getDirtyFields=function(e){let t=m.get(e);if(t){if(t.version>0){const r=v(e);(function(e,t){const r=Object.keys(e);return r.length===Object.keys(t).length&&r.every(e=>!0===t[e])})(t.result,r)||(t.result=r),t.version=0}}else t={version:0,result:v(e)},m.set(e,t);return t.result},exports.getErrors=function({errors:e}){const t=[];for(const[r,n]of e){const e=JSON.parse(r).join(".");for(const{type:r,message:o}of n)t.push({path:e,type:r,message:o})}return t},exports.getTouchedFields=function({touched:e}){return Array.from(e,e=>JSON.parse(e).join("."))},exports.getValues=a,exports.on=t,exports.reset=function(t,r,n){const o=[];t.initialValues=r,t.parsedValues=void 0,function(t){const{emitter:r,errors:n}=t;n.clear(),e(r,"errors")}(t);const{emitter:s,touched:i,values:c,deleted:a,validating:f}=t;c.clear(),a.clear(),function(e){const t=d.get(e);t&&t.clear()}(t),i.clear(),f.clear(),t.isSubmitting=!1,t.submitCount=0,t.isSubmitSuccessful=void 0,p(t);for(const{key:e,value:r}of o)l(t,u(e),r);e(s,"change"),e(s,"touched"),e(s,"validating"),e(s,"submitting"),e(s,"submitCount"),e(s,"submitSuccessful"),e(s,"reset")},exports.trigger=async function(r,n){const o=()=>{return e=r.emitter,n="validating",o=()=>function(e){for(const t of e.validating)if(t!==b)return!1;return!0}(r),s=()=>!1,new Promise((r,i)=>{if(s())return void i();if(o())return void r();const u=t(e,n,()=>{if(s())return u(),void i();o()&&(u(),r())})});var e,n,o,s};return r.validators.forEach(e=>e()),await o(),r.validate&&await function(t){const r=t.validate;if(!r)return Promise.resolve();const n=t.validateDebounce??0;if(n<=0){const e=new AbortController;return Promise.resolve(r(a(t),{form:t,signal:e.signal})).then(e=>{w(t,e)})}const o=function(e){let t=A.get(e);t||(t={timer:null,controller:null,round:null,marked:!1,waiters:[]},A.set(e,t));return t}(t);null!==o.timer?clearTimeout(o.timer):(o.marked=!0,t.validating.add(b),e(t.emitter,"validating"));return o.timer=setTimeout(()=>{o.timer=null;const e=o.round={};(function(e,t,r){const n=e.validate;if(!n)return Promise.resolve();t.controller?.abort();const o=t.controller=new AbortController;let s;try{s=Promise.resolve(n(a(e),{form:e,signal:o.signal}))}catch(e){s=Promise.reject(e)}return s.then(n=>{t.round===r&&w(e,n)},e=>{if(t.round===r)throw e})})(t,o,e).then(()=>O(t,o,e,k),r=>O(t,o,e,r))},n),new Promise((e,t)=>{o.waiters.push({resolve:e,reject:t})})}(r),!function({errors:e}){return e.size>0}(r)};
|
|
2
|
+
//# sourceMappingURL=form-DbDDJ8bt.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-DbDDJ8bt.cjs.js","sources":["../node_modules/.pnpm/@for-fun+event-emitter@1.0.1/node_modules/@for-fun/event-emitter/dist/event-emitter.esm.js","../src/util.ts","../src/path.ts","../src/form.ts"],"sourcesContent":["function getSet(ee, key) {\n var set = ee.get(key);\n if (set) return set;\n var newSet = new Set();\n ee.set(key, newSet);\n return newSet;\n}\nvar errorEvent = Symbol('error');\nfunction create() {\n return new Map();\n}\nfunction emit(ee, key) {\n for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n args[_key - 2] = arguments[_key];\n }\n (ee.get(key) || []).forEach(function (h) {\n return h.apply(void 0, args);\n });\n}\nfunction emitError(ee, err) {\n var handlerSet = ee.get(errorEvent);\n if (!handlerSet || !handlerSet.size) throw err;\n handlerSet.forEach(function (h) {\n return h(err);\n });\n}\nfunction on(ee, key, handler) {\n var set = getSet(ee, key);\n set.add(handler);\n return function () {\n return set.delete(handler);\n };\n}\nfunction onError(ee, handler) {\n return on(ee, errorEvent, handler);\n}\nfunction once(ee, key, handler) {\n var off = on(ee, key, function () {\n off();\n handler.apply(void 0, arguments);\n });\n return off;\n}\nfunction onceError(ee, handler) {\n return once(ee, errorEvent, handler);\n}\nfunction bindContext(context) {\n return function (func) {\n return func.bind(null, context);\n };\n}\nfunction createAndBind() {\n var ee = create();\n var bind = bindContext(ee);\n return {\n emit: bind(emit),\n emitError: bind(emitError),\n on: bind(on),\n onError: bind(onError),\n once: bind(once),\n onceError: bind(onceError)\n };\n}\n\nexport { bindContext, create, createAndBind, emit, emitError, errorEvent, on, onError, once, onceError };\n//# sourceMappingURL=event-emitter.esm.js.map\n","import {on} from '@for-fun/event-emitter';\nimport type {EventEmitter} from '@for-fun/event-emitter';\n\nconst pathCache = new Map<string, (string | number)[]>();\n\nexport function normalizePath(\n path: string | (string | number)[]\n): (string | number)[] {\n if (Array.isArray(path)) return path;\n const cached = pathCache.get(path);\n if (cached) return cached;\n const value = parsePath(path);\n pathCache.set(path, value);\n return value;\n}\n\nfunction parsePath(path: string): (string | number)[] {\n const result: (string | number)[] = [];\n let identifier = '';\n const flushIdentifier = () => {\n result.push(identifier);\n identifier = '';\n };\n\n for (let i = 0; i < path.length; i++) {\n const char = path[i];\n if (char === '.') {\n if (identifier !== '') flushIdentifier();\n } else if (char === '[') {\n if (identifier !== '') flushIdentifier();\n const quote = path[i + 1];\n if (quote === '\"' || quote === \"'\") {\n const close = path.indexOf(quote, i + 2);\n if (close === -1) {\n throw new TypeError(`Unterminated quote in path: ${path}`);\n }\n if (path[close + 1] !== ']') {\n throw new TypeError(\n `Expected \"]\" after quoted segment in path: ${path}`\n );\n }\n result.push(path.slice(i + 2, close));\n i = close + 1;\n } else {\n const close = path.indexOf(']', i + 1);\n if (close === -1) {\n throw new TypeError(`Unterminated bracket in path: ${path}`);\n }\n const content = path.slice(i + 1, close);\n result.push(/^-?\\d+$/.test(content) ? Number(content) : content);\n i = close;\n }\n } else {\n identifier += char;\n }\n }\n if (identifier !== '' || result.length === 0) flushIdentifier();\n return result;\n}\n\nexport function get(values: any, path: (string | number)[]): any {\n return path.reduce((current: any, p: string | number) => {\n if (current == null) return undefined;\n return current[p];\n }, values);\n}\n\n/**\n * Immutable counterpart of {@link set}: removes the path from the value\n * tree, copying only along the touched branch (untouched branches stay\n * shared with the source, like set). Deletes the key entirely rather than\n * writing undefined, so the result carries no `a: undefined` entries.\n */\nexport function unset(values: any, path: (string | number)[]): any {\n if (!path.length || values == null) return values;\n const [prop, ...props] = path;\n if (props.length) {\n const next = unset(values[prop], props);\n // Reattach the pruned child at its parent key — NOT at the full path,\n // which would write the pruned subtree back under the removed key.\n return next === values[prop] ? values : set(values, [prop], next);\n }\n if (Array.isArray(values)) {\n if (!(prop in values)) return values;\n const arr = values.slice();\n delete arr[prop as number];\n return arr;\n }\n if (typeof values !== 'object' || !(prop in values)) return values;\n const copy = {...values};\n delete copy[prop as string];\n return copy;\n}\n\nexport function set(values: any, path: (string | number)[], value: any): any {\n if (!path.length) return value;\n\n const [prop, ...props] = path;\n if (typeof prop === 'number') {\n const arr = Array.isArray(values) ? values.slice() : [];\n arr[prop] = set(arr[prop], props, value);\n return arr;\n }\n return {...values, [prop]: set(values && values[prop], props, value)};\n}\n\n/**\n * Ownership-tracked {@link set}: merge many paths into one tree without\n * re-copying containers the merge itself already created.\n *\n * Containers present in `owned` (freshly created by an earlier call of the\n * same merge) are mutated in place; every other container -- nodes borrowed\n * from the seed tree and user leaf values -- is copied first with the exact\n * copy rules `set` applies (numeric prop: array slice, or a fresh array\n * when the node is not one; string prop: object spread). Chaining\n * `setOwned` over a list of paths therefore produces the tree chaining\n * `set` would, in the same insertion order, but allocates each distinct\n * container once (O(distinct path prefixes)) instead of re-copying the\n * whole branch for every path (O(paths x depth)).\n *\n * Use a fresh `owned` set per merge and thread the returned root (a copied\n * replacement when the seed root itself had to be copied) into the next\n * call. Borrowed containers are never mutated.\n */\nexport function setOwned(\n root: any,\n path: (string | number)[],\n value: any,\n owned: Set<object>\n): any {\n if (!path.length) return value;\n let container = root;\n let parent: any = null;\n let parentProp: string | number = '';\n for (let i = 0; i < path.length; i++) {\n const prop = path[i];\n if (!owned.has(container)) {\n let copy: any;\n if (typeof prop === 'number') {\n copy = Array.isArray(container) ? container.slice() : [];\n } else {\n copy = {...container};\n }\n owned.add(copy);\n if (i === 0) root = copy;\n else parent[parentProp] = copy;\n container = copy;\n }\n if (i === path.length - 1) {\n container[prop] = value;\n } else {\n parent = container;\n parentProp = prop;\n container = container[prop];\n }\n }\n return root;\n}\n\nexport function isEmpty(value: any): boolean {\n if (value == null) return true;\n if (typeof value !== 'object') return false;\n\n const values = Object.values(value);\n return values.length === 0 || values.every(isEmpty);\n}\n\nexport function isPromise(value: any): value is Promise<any> {\n return value && typeof value.then === 'function';\n}\n\n/** Structural equality for form default data (primitives, arrays, plain\n * objects, Dates). Class instances and other exotic objects compare as\n * unequal, which errs on the side of re-seeding when {@link\n * setInitialValues} uses it to tell a re-rendered inline literal from\n * genuinely changed content. */\nexport function isEqual(a: any, b: any): boolean {\n if (Object.is(a, b)) return true;\n if (a instanceof Date && b instanceof Date)\n return a.getTime() === b.getTime();\n if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false;\n const isArray = Array.isArray(a);\n if (isArray !== Array.isArray(b)) return false;\n if (isArray) {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!isEqual(a[i], b[i])) return false;\n }\n return true;\n }\n const proto = Object.getPrototypeOf(a);\n if (proto !== Object.prototype && proto !== null) return false;\n if (Object.getPrototypeOf(b) !== proto) return false;\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n for (const key of keysA) {\n if (!isEqual(a[key], b[key])) return false;\n }\n return true;\n}\n\nexport function waitUntil(\n emitter: EventEmitter<any>,\n event: string,\n isResolve: () => boolean,\n isReject: () => boolean\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (isReject()) return void reject();\n if (isResolve()) return void resolve();\n\n const off = on(emitter, event as any, () => {\n if (isReject()) {\n off();\n reject();\n return;\n }\n // Condition not reached yet — stay subscribed and keep waiting. (The\n // previous `if (isResolve()) return` was inverted: it hung forever\n // once the condition held and resolved prematurely while it didn't.)\n if (!isResolve()) return;\n off();\n resolve();\n });\n });\n}\n","import {normalizePath} from './util';\n\nexport type PathSegments = (string | number)[];\nexport type Name = string | PathSegments;\nexport type Path = {value: PathSegments; key: string};\n\nexport default function create(name: Name): Path {\n const value = normalizePath(name);\n return {value, key: JSON.stringify(value)};\n}\n","import {create as createEmitter, emit} from '@for-fun/event-emitter';\nimport type {EventEmitter} from '@for-fun/event-emitter';\nimport createPath from './path';\nimport type {Name, Path, PathSegments} from './path';\nimport type {FieldPath, PathValueOf} from './types';\nimport {get, isEqual, normalizePath, setOwned, unset, waitUntil} from './util';\n\nexport type {Name};\nexport type {FieldPath, PathValue} from './types';\n\n/** A field error: `type` identifies the error kind ('custom' for plain\n * string errors), `message` is the display text. */\nexport interface FieldError {\n type: string;\n message: string;\n}\n\n/** A flattened entry from {@link getErrors}. */\nexport type FieldErrorEntry = {path: string; type: string; message: string};\n\n/** When a field is validated:\n * - `'onSubmit'` (default): only on submit\n * - `'onBlur'`: when the field loses focus\n * - `'onChange'`: on every change\n * - `'onTouched'`: on first blur, then on every change\n * - `'all'`: on both change and blur\n */\nexport type ValidationMode =\n 'onSubmit' | 'onBlur' | 'onChange' | 'onTouched' | 'all';\n\n/** When a field is re-validated after it already has an error:\n * - `'onChange'` (default): on every change\n * - `'onBlur'`: when the field loses focus\n * - `'onSubmit'`: only on submit (no live re-validation)\n */\nexport type ReValidateMode = 'onChange' | 'onBlur' | 'onSubmit';\n\n/** Brand marking a form-level validate result as a structured\n * {@link ValidationOutcome} (parsed values and/or errors) rather than a\n * plain nested error record. Symbols cannot collide with user error\n * records, so detection is an exact `VALIDATION_OUTCOME in result`. */\nexport const VALIDATION_OUTCOME: unique symbol = Symbol('validation-outcome');\n\n/** Structured form-level validate result: `errors` uses the same nested\n * shape a plain error record uses, `values` is the schema's parsed output\n * (coerce/transform results included). Either side may be omitted. */\nexport type ValidationOutcome<T> = {\n [VALIDATION_OUTCOME]: true;\n errors?: Record<string, any>;\n values?: T;\n};\n\n/** What a form-level validate function may return: a plain nested error\n * record (flattened into field errors — the long-standing shape), or a\n * branded {@link ValidationOutcome} whose `values` become the form's\n * parsedValues baseline. */\nexport type ValidateResult<T> =\n | Record<string, any>\n | ValidationOutcome<T>\n | Promise<Record<string, any> | ValidationOutcome<T>>;\n\n/** Context passed to a form-level `validate` function's second argument.\n * `signal` aborts as soon as the round is superseded — a newer round\n * started (which only happens under a positive `validateDebounce`, where\n * kicks merge into windows) — so async validators can cancel their\n * underlying work instead of racing a stale result home. Stale results\n * are dropped independently by the round gate, so validators that ignore\n * the signal stay correct too; the same contract field-level validators\n * get through their own `meta`. */\nexport type FormValidateMeta<T extends Record<string, any> = any> = {\n form: Form<T>;\n signal: AbortSignal;\n};\n\n/** Form-level validator: receives all values (plus {@link\n * FormValidateMeta} as an optional second argument) and returns a\n * {@link ValidateResult} — sync or async — or `undefined`/nothing when\n * valid (the runtime skips falsy results, so implicit-return callbacks\n * type-check). */\nexport type FormValidateFn<T extends Record<string, any> = any> = (\n values: T,\n meta: FormValidateMeta<T>\n) => ValidateResult<T> | undefined;\n\nexport interface Form<T extends Record<string, any> = any> {\n emitter: EventEmitter;\n mode: ValidationMode;\n reValidateMode: ReValidateMode;\n initialValues: T;\n values: Map<string, any>;\n /** Tombstones of unregistered field paths (JSON path keys): reading or\n * merging values must not fall back to initialValues for these paths. */\n deleted: Set<string>;\n /** Every error registered for a field, as a non-empty array (the\n * write-side {@link setErrorByPath} normalizes to this invariant, so\n * readers never need to guard against an empty list). Readers wanting\n * the display error take the first entry ({@link getError}); readers\n * wanting all of them use {@link getFieldErrors}. */\n errors: Map<string, FieldError[]>;\n touched: Set<string>;\n validators: Map<string, () => void>;\n /** Change handlers published by mounted fields (`useField`): each is the\n * field's own onChange — the write plus its mode/reValidateMode-gated\n * validation kick. Path-based writes with user-change semantics\n * ({@link changeValueByPath}) route through the registered handler; the\n * effective per-field mode and live-error view live inside the field's\n * closure, so this map is the only channel that can reproduce them. */\n changeHandlers: Map<string, (value: any) => void>;\n validating: Set<string>;\n /** Parsed values from the last successful schema validation: the\n * schema's complete output tree (coerced/transformed values included).\n * Sits between initialValues and the values Map in {@link getValues}\n * until `reset`/`setInitialValues` clears it. Never affects dirty\n * state — that compares live edits against initialValues only. */\n parsedValues: T | undefined;\n /** Form-level validator, seeded from {@link Options.validate}. May\n * receive a second {@link FormValidateMeta} argument. */\n validate?: FormValidateFn<T>;\n /** Delay in milliseconds before the form-level `validate` runs; seeded\n * from {@link Options.validateDebounce} and fixed at create time. */\n validateDebounce?: number;\n isSubmitting: boolean;\n submitCount: number;\n isSubmitSuccessful: boolean | undefined;\n /** Form-level disabled flag, OR-ed into every bound field's `disabled`\n * (form flag || the field's own option). Seeded from\n * {@link Options}.disabled at create time and toggled at runtime with\n * {@link setDisabled}, which emits a payload-less 'disabled' event so\n * subscribed fields re-render. */\n disabled: boolean;\n}\n\nexport type Options<T extends Record<string, any> = any> = {\n initialValues?: T;\n /** When fields are validated. Defaults to `'onSubmit'`. See\n * {@link ValidationMode}. */\n mode?: ValidationMode;\n /** When a field is re-validated after it already has an error — it only\n * takes effect once the field has an error. Defaults to `'onChange'`. See\n * {@link ReValidateMode}. */\n reValidateMode?: ReValidateMode;\n /**\n * Form-level validator. Returns a record of errors keyed by field path;\n * nested objects are flattened ('a.b' style) and array values contribute\n * every non-empty string they hold as separate errors (zod `flatten()`\n * formErrors style). Schema adapters instead return a branded\n * {@link ValidationOutcome}: `errors` flattens the same way, `values`\n * (the schema's parsed output) becomes the form's parsedValues baseline\n * that {@link getValues} layers over initialValues.\n */\n validate?: FormValidateFn<T>;\n /**\n * Milliseconds to debounce the form-level `validate`: kicks from\n * `trigger`/`ensureValidate`/submit inside the window merge into one\n * run, and while the timer is pending the form counts as validating,\n * so `trigger` and submit wait the window out — the same contract the\n * per-field `validateDebounce` gives field validators. The merged run\n * reads the values current when its timer fires. Defaults to `0`\n * (validate runs immediately, exactly as before this option existed).\n */\n validateDebounce?: number;\n /** Start the form with every bound field disabled — the flag bound\n * fields OR with their own `disabled` option (a field cannot opt out\n * of a disabled form). Toggle later with {@link setDisabled}.\n * Defaults to `false`. */\n disabled?: boolean;\n};\n\n/**\n * Create form instance\n * @param options\n * @return form instance\n */\nexport default function create<T extends Record<string, any> = any>(\n options?: Options<T>\n): Form<T> {\n const emitter = createEmitter();\n return {\n emitter,\n ...options,\n mode: options?.mode ?? 'onSubmit',\n reValidateMode: options?.reValidateMode ?? 'onChange',\n disabled: options?.disabled ?? false,\n initialValues: (options?.initialValues ?? {}) as T,\n values: new Map(),\n deleted: new Set(),\n errors: new Map(),\n touched: new Set(),\n validators: new Map(),\n changeHandlers: new Map(),\n validating: new Set(),\n parsedValues: undefined,\n isSubmitting: false,\n submitCount: 0,\n isSubmitSuccessful: undefined\n };\n}\n\n/**\n * Get form values: the values Map layered over parsedValues (when a schema\n * validation produced them) layered over initialValues.\n *\n * Merged with copy-on-write ownership tracking ({@link setOwned}): every\n * distinct container on a written path is allocated once and shared by all\n * paths through it, instead of re-copying the whole branch for every key.\n * One owned set spans the whole merge, so containers borrowed from the\n * parsedValues tree are copied before mutation exactly like initialValues\n * ones. The result is still a freshly merged tree per call, with untouched\n * branches sharing references with the baseline exactly like chained\n * `set` did -- callers may treat it as their own copy.\n *\n * parsedValues is the schema's complete output tree: once validation\n * succeeds it replaces the initialValues baseline (fields the schema\n * dropped disappear), while live edits in the values Map still win over\n * both. It never affects dirty state — {@link isDirty} and\n * {@link getDirtyFields} compare live edits against initialValues only,\n * because parsing is not a user edit.\n *\n * @param form\n */\nexport function getValues<T extends Record<string, any> = any>(\n form: Form<T>\n): T {\n const {initialValues, parsedValues, values, deleted} = form;\n const owned = new Set<object>();\n let merged = parsedValues ?? initialValues;\n for (const [key, value] of values) {\n merged = setOwned(merged, JSON.parse(key), value, owned);\n }\n // Unregistered fields leave a tombstone in `deleted`; remove those paths\n // from the merged result so they don't fall back to initialValues. unset\n // is immutable (set() shares untouched branches with initialValues, so a\n // mutating delete would corrupt them) and deletes the key outright rather\n // than writing undefined, which would leave `a: undefined` entries behind\n // in anything that spreads getValues().\n for (const key of deleted) {\n merged = unset(merged, JSON.parse(key));\n }\n return merged;\n}\n\n/**\n * Get field value\n * @param form\n * @param name\n */\nexport function getValue<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): PathValueOf<T, P> {\n return getValueByPath(form, createPath(name));\n}\n\n/**\n * Get field value by path\n * @param form\n * @param path\n */\nexport function getValueByPath(\n {initialValues, parsedValues, values, deleted}: Form,\n path: Path\n): any {\n if (values.has(path.key)) return values.get(path.key);\n // Unregistered path: the tombstone blocks the initialValues fallback.\n if (deleted.has(path.key)) return undefined;\n // Same layering as getValues: parsed values (when present) are the\n // baseline above initialValues.\n return get(parsedValues ?? initialValues, path.value);\n}\n\n/** Options accepted by {@link setValue} / {@link setValueByPath} / {@link\n * changeValue} / {@link changeValueByPath}. `shouldValidate`/`shouldTouch`\n * default to `false`; omitting the options object entirely keeps the plain\n * set-value behavior (no validation, no touched marking, dirty stays\n * derived). */\nexport interface SetFieldOptions {\n /** Run the field's registered validator (if any) after the value lands,\n * same as triggering that single field. Defaults to `false`. */\n shouldValidate?: boolean;\n /** Mark the field as touched. Defaults to `false`. */\n shouldTouch?: boolean;\n /** Land the value as a commit instead of an edit: the value becomes the\n * field's dirty-comparison baseline, so `getDirtyFields`/`isDirty`/\n * `getFieldState().isDirty` read the field as clean, and a later write\n * dirties it only by differing from the new baseline. `true` (or\n * omitting the flag) keeps the default derived behavior — dirty while\n * the live value differs from initialValues. */\n shouldDirty?: boolean;\n}\n\n/**\n * Set field value\n * @param form\n * @param name\n * @param value\n * @param options\n */\nexport function setValue<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(\n form: Form<T>,\n name: P,\n value: PathValueOf<T, P>,\n options?: SetFieldOptions\n): void {\n setValueByPath(form, createPath(name), value, options);\n}\n\n/**\n * Set field value\n * @param form\n * @param path\n * @param value\n * @param options\n */\nexport function setValueByPath(\n form: Form,\n path: Path,\n value: any,\n options?: SetFieldOptions\n): void {\n const {emitter, values, deleted} = form;\n values.set(path.key, value);\n reviveBranch(deleted, path);\n // Baselines under the replaced subtree die with it — before the emit, so\n // subscribers reading dirty state inside the emission never see a stale\n // commit suppressing the write they are being told about.\n pruneDirtyBaselines(form, path);\n if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);\n bumpDirtyVersion(form);\n if (options?.shouldTouch) setTouchedByPath(form, path);\n if (options?.shouldValidate) form.validators.get(path.key)?.();\n emit(emitter, 'change', path);\n}\n\n/**\n * Set a field value as a user change.\n *\n * The write routes through the field's own onChange when one is mounted\n * (registered by `useField`), so it fires exactly the validation a user\n * typing into the field would fire: the field's effective `mode`\n * (per-field override included) and the form's `reValidateMode`. With no\n * mounted field on the path it degrades to a plain value set\n * ({@link setValue}).\n *\n * This is the channel for component-library bridges that hand a control a\n * plain setter bound to a field — they cannot rebuild the gating from\n * public form state, because the per-field mode override and the\n * live-error view that gates `reValidateMode` live inside the field's\n * onChange closure.\n *\n * Contrast {@link setValue}: that is the imperative channel — its\n * `shouldValidate` option kicks the field's validator unconditionally,\n * ignoring any mode. Functional updaters are the caller's to evaluate\n * ({@link getValue}).\n *\n * `options` carries the same {@link SetFieldOptions}: on the fallback path\n * (no mounted field) they forward to {@link setValueByPath} wholesale,\n * while on the mounted-field path only `shouldDirty: false` applies — the\n * write lands as a commit while the field's own mode gating keeps driving\n * validation, which is the point of this channel.\n *\n * @param form\n * @param name\n * @param value\n * @param options\n */\nexport function changeValue<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(\n form: Form<T>,\n name: P,\n value: PathValueOf<T, P>,\n options?: SetFieldOptions\n): void {\n changeValueByPath(form, createPath(name), value, options);\n}\n\n/**\n * Set a field value as a user change, by parsed path\n * @param form\n * @param path\n * @param value\n * @param options\n */\nexport function changeValueByPath(\n form: Form,\n path: Path,\n value: any,\n options?: SetFieldOptions\n): void {\n // The baseline must land before the mounted field's onChange runs — its\n // write emits synchronously and subscribers read dirty state inside the\n // emission, so installing after the call would flash dirty-then-clean.\n if (options?.shouldDirty === false) setDirtyBaseline(form, path, value);\n const change = form.changeHandlers.get(path.key);\n if (change) change(value);\n else setValueByPath(form, path, value, options);\n}\n\n/**\n * Get field error\n * @param form\n * @param name\n * @return FieldError object or undefined\n */\nexport function getError<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): FieldError | undefined {\n return getErrorByPath(form, createPath(name));\n}\n\n/**\n * Get field error by path\n * @param form\n * @param path\n * @return first FieldError of the field, or undefined\n */\nexport function getErrorByPath(\n {errors}: Form,\n path: Path\n): FieldError | undefined {\n return errors.get(path.key)?.[0];\n}\n\n/** Shared empty result for {@link getFieldErrorsByPath}: a fresh `[]` per\n * call would allocate on the hot no-error path, and the stored arrays are\n * handed out by reference too, so callers must treat results as read-only. */\nconst NO_ERRORS: FieldError[] = [];\n\n/**\n * Get all errors of a field\n * @param form\n * @param name\n * @return every error registered for the field (insertion order); an empty\n * array when the field has none\n */\nexport function getFieldErrors<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): FieldError[] {\n return getFieldErrorsByPath(form, createPath(name));\n}\n\n/**\n * Get all errors of a field by path\n * @param form\n * @param path\n * @return every error registered for the field (insertion order); an empty\n * array when the field has none\n */\nexport function getFieldErrorsByPath({errors}: Form, path: Path): FieldError[] {\n return errors.get(path.key) ?? NO_ERRORS;\n}\n\n/**\n * Get all errors\n * @param form\n * @return array of {path, type, message} entries, in insertion order; path\n * is the user-facing dotted field path ('a.b', 'list.0'), and a\n * field holding several errors contributes one entry per error\n */\nexport function getErrors({errors}: Form): FieldErrorEntry[] {\n const entries: FieldErrorEntry[] = [];\n for (const [key, list] of errors) {\n const path = (JSON.parse(key) as PathSegments).join('.');\n for (const {type, message} of list) entries.push({path, type, message});\n }\n return entries;\n}\n\n/**\n * Get first error message\n * @param form\n * @return first error's message string, or undefined when there are no errors\n */\nexport function getFirstError({errors}: Form): string | undefined {\n return errors.values().next().value?.[0]?.message;\n}\n\n/** Snapshot of one field's aggregated state, as {@link getFieldState}\n * returns it. `errors` is the stored array shared with the form — treat it\n * as read-only, like every {@link getFieldErrors} result. */\nexport interface FieldState<T = any> {\n value: T;\n error: FieldError | undefined;\n errors: FieldError[];\n isDirty: boolean;\n isTouched: boolean;\n isValidating: boolean;\n}\n\n/**\n * Get one field's aggregated state: the layered value ({@link getValue}),\n * the first error ({@link getError}) and every error ({@link\n * getFieldErrors}), dirtiness, the touched flag, and whether a validator\n * is in flight. `isDirty` applies the same per-field rule as {@link\n * getDirtyFields}: a live value exists and differs from initialValues at\n * that path (parsedValues never counts — parsing is not an edit).\n *\n * @param form\n * @param name\n */\nexport function getFieldState<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): FieldState<PathValueOf<T, P>> {\n const path = createPath(name);\n const {values, touched, validating} = form;\n const live = values.get(path.key);\n return {\n value: getValueByPath(form, path),\n error: getErrorByPath(form, path),\n errors: getFieldErrorsByPath(form, path),\n // Same rule as getDirtyFields, committed baselines included: the field\n // is dirty while its live value differs from its effective baseline.\n isDirty:\n values.has(path.key) &&\n getDirtyBaseline(form, path.key, path.value) !== live,\n isTouched: touched.has(path.key),\n isValidating: validating.has(path.key)\n };\n}\n\nexport function unsetValidatingByPath(\n {emitter, validating}: Form,\n path: Path\n): void {\n validating.delete(path.key);\n // Path payload lets key-scoped subscribers (onKeyEvent) skip unrelated\n // fields; payload-less listeners ignore it.\n emit(emitter, 'validating', path);\n}\n\nexport function setValidatingByPath(\n {emitter, validating}: Form,\n path: Path\n): void {\n validating.add(path.key);\n emit(emitter, 'validating', path);\n}\n\n/**\n * Set field error\n * @param form\n * @param name\n * @param error string is normalized to {type: 'custom', message}; a\n * FieldError object is stored as-is; an array holds several errors\n * (falsy items dropped, strings normalized); undefined clears\n */\nexport function setError<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(\n form: Form<T>,\n name: P,\n error: string | FieldError | (string | FieldError)[] | undefined\n): void {\n setErrorByPath(form, createPath(name), error);\n}\n\n/**\n * Set field error\n * @param form\n * @param path\n * @param error string is normalized to {type: 'custom', message}; a\n * FieldError object is stored as-is; an array holds several errors\n * (falsy items dropped, strings normalized); undefined clears\n */\nexport function setErrorByPath(\n {emitter, errors}: Form,\n path: Path,\n error: string | FieldError | (string | FieldError)[] | undefined\n): void {\n const list = normalizeErrors(error);\n // An empty result (undefined, '', or an array of only falsy items) clears\n // the key: the errors Map never stores an empty list, so hasErrors stays\n // a plain size check and readers can index [0] unguarded.\n if (list) errors.set(path.key, list);\n else errors.delete(path.key);\n // Path payload lets key-scoped subscribers (onKeyEvent) skip unrelated\n // fields; payload-less listeners ignore it.\n emit(emitter, 'errors', path);\n}\n\n/** Normalize any {@link setErrorByPath} input into the stored non-empty\n * FieldError[] shape, or undefined when there is nothing to store. */\nfunction normalizeErrors(\n error: string | FieldError | (string | FieldError)[] | undefined\n): FieldError[] | undefined {\n if (typeof error === 'string') {\n return error ? [{type: 'custom', message: error}] : undefined;\n }\n if (isFieldError(error)) return [error];\n if (!error) return undefined;\n // Falsy items drop out before normalization, so '' never becomes a\n // stored {type: 'custom', message: ''} placeholder.\n const list: FieldError[] = [];\n error.forEach(item => {\n if (typeof item === 'string' && item) {\n list.push({type: 'custom', message: item});\n } else if (isFieldError(item)) {\n list.push(item);\n }\n });\n return list.length ? list : undefined;\n}\n\n/**\n * Clear errors\n * @param form\n * @param name a single path or a list of paths; omit to clear every error\n */\nexport function clearErrors(form: Form, name?: Name | Name[]): void {\n const {emitter, errors} = form;\n if (name === undefined) {\n errors.clear();\n // Payload-less broadcast: every error subscriber re-syncs.\n emit(emitter, 'errors');\n return;\n }\n // Same single-path vs list discrimination as trigger: a segment array\n // holding a number is one path ('a.0' shape), not a list of names.\n const paths =\n typeof name === 'string' || isSegmentsPath(name)\n ? [createPath(name)]\n : name.map(one => createPath(one));\n for (const {key} of paths) errors.delete(key);\n // Path-payload emits — the setErrorByPath scoping — wake exactly the\n // affected fields' subscribers.\n for (const path of paths) emit(emitter, 'errors', path);\n}\n\n/** Options accepted by {@link setServerErrors}. */\nexport interface SetServerErrorsOptions {\n /** Keep existing field errors instead of clearing them first. Defaults\n * to `false`: a fresh server response replaces the prior error state. */\n keepExisting?: boolean;\n}\n\n/**\n * Land a server-side error response on the form: each entry becomes the\n * named field's error(s) with `type: 'server'`, ready for the same\n * renderError/`useError` channel client-side validation uses. Takes the\n * flat `Record<string, string | string[]>` shape REST APIs commonly\n * return (RealWorld: `422 {errors: {email: ['has already been taken']}}`)\n * without a hand-rolled `Object.entries` + `setError` loop.\n *\n * A string value lands as one error, a string array as several (first one\n * is what `getError`/`error` expose); an empty array clears that field's\n * errors. By default every existing error is cleared first — a fresh\n * response describes the current state, not a patch onto stale client\n * errors; pass `keepExisting: true` to layer instead.\n * @param form\n * @param errors field errors keyed by name\n * @param options\n */\nexport function setServerErrors(\n form: Form,\n errors: Record<string, string | string[]>,\n options?: SetServerErrorsOptions\n): void {\n if (!options?.keepExisting) clearErrors(form);\n for (const [name, error] of Object.entries(errors)) {\n setError(\n form,\n name,\n (Array.isArray(error) ? error : [error]).map(message => ({\n type: 'server',\n message\n }))\n );\n }\n}\n\n/**\n * Set field touched state\n * @param form\n * @param name\n */\nexport function setTouched(form: Form, name: Name): void {\n setTouchedByPath(form, createPath(name));\n}\n\n/**\n * Set field touched state\n * @param form\n * @param path\n */\nexport function setTouchedByPath({emitter, touched}: Form, path: Path): void {\n if (touched.has(path.key)) return;\n touched.add(path.key);\n // Path payload lets key-scoped subscribers (onKeyEvent) skip unrelated\n // fields; payload-less listeners ignore it.\n emit(emitter, 'touched', path);\n}\n\n/**\n * Check if field has been touched\n * @param form\n * @param name\n */\nexport function hasTouched<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): boolean {\n return hasTouchedByPath(form, createPath(name));\n}\n\n/**\n * Check if field has been touched\n * @param form\n * @param path\n */\nexport function hasTouchedByPath({touched}: Form, path: Path): boolean {\n return touched.has(path.key);\n}\n\n/**\n * Is dirty -- any value differs from initialValues\n * @param form\n */\nexport function isDirty(form: Form): boolean {\n let dirty = false;\n forEachDirtyField(form, () => {\n dirty = true;\n });\n return dirty;\n}\n\nfunction forEachDirtyField(form: Form, fn: (dottedKey: string) => void): void {\n for (const [key, value] of form.values) {\n const path = JSON.parse(key) as PathSegments;\n if (getDirtyBaseline(form, key, path) !== value) fn(path.join('.'));\n }\n}\n\n/** Per-path dirty-comparison baselines installed by writes with\n * `shouldDirty: false`: the written value becomes that field's baseline —\n * the write reads as a commit, not an edit. Module-private (like\n * {@link dirtyFieldsCaches}) so the Form shape is untouched for forms that\n * never opt in. */\nconst dirtyBaselines = new WeakMap<Form, Map<string, any>>();\n\n/** The value a field's dirtiness is measured against: its committed\n * baseline when one exists, initialValues at the path otherwise. */\nfunction getDirtyBaseline(\n form: Form,\n key: string,\n segments: PathSegments\n): any {\n const baselines = dirtyBaselines.get(form);\n if (baselines?.has(key)) return baselines.get(key);\n return get(form.initialValues, segments);\n}\n\n/** Install a committed baseline for a `shouldDirty: false` write: the\n * field reads clean until a later write diverges from the new baseline. */\nfunction setDirtyBaseline(form: Form, {key}: Path, value: any): void {\n let baselines = dirtyBaselines.get(form);\n if (!baselines) {\n baselines = new Map();\n dirtyBaselines.set(form, baselines);\n }\n baselines.set(key, value);\n}\n\n/** A wholesale write at a path replaces the subtree below it, so baselines\n * committed under the branch die with the data they were committed against\n * (array movers rewrite the parent path, re-aligning row indices). Called\n * from every write — a no-op unless the form ever opted in. */\nfunction pruneDirtyBaselines(form: Form, {key}: Path): void {\n const baselines = dirtyBaselines.get(form);\n if (!baselines?.size) return;\n const stem = `${key.slice(0, -1)},`;\n for (const baselineKey of baselines.keys()) {\n if (baselineKey.startsWith(stem)) baselines.delete(baselineKey);\n }\n}\n\n/** Drop committed baselines at one path ({@link removeFieldByPath} /\n * {@link resetField}) or all of them ({@link setInitialValues} /\n * {@link reset}) — the state they were measured against is gone. */\nfunction clearDirtyBaselines(form: Form, key?: string): void {\n const baselines = dirtyBaselines.get(form);\n if (!baselines) return;\n if (key === undefined) baselines.clear();\n else baselines.delete(key);\n}\n\n/** Per-form memoization of {@link getDirtyFields}. `version` counts value\n * mutations since the cached `result` was computed: bump points increment\n * it, reads reset it, so a non-zero version means the cache is stale. */\ninterface DirtyFieldsCache {\n version: number;\n result: Record<string, boolean>;\n}\n\nconst dirtyFieldsCaches = new WeakMap<Form, DirtyFieldsCache>();\n\n/**\n * Invalidate `form`'s cached {@link getDirtyFields} result. Called at every\n * point that can change values or initialValues (setValueByPath,\n * removeFieldByPath, setInitialValues, reset) so repeated reads hand out a\n * stable reference and useWatch's Object.is snapshot check can skip\n * re-renders.\n */\nfunction bumpDirtyVersion(form: Form): void {\n const cache = dirtyFieldsCaches.get(form);\n if (cache) cache.version++;\n}\n\nfunction computeDirtyFields(form: Form): Record<string, boolean> {\n const dirtyFields: Record<string, boolean> = {};\n forEachDirtyField(form, key => {\n dirtyFields[key] = true;\n });\n return dirtyFields;\n}\n\n/** Dirty entries only ever map to `true`, so equal key sets mean shallow\n * equal results. */\nfunction sameDirtyKeys(\n a: Record<string, boolean>,\n b: Record<string, boolean>\n): boolean {\n const aKeys = Object.keys(a);\n if (aKeys.length !== Object.keys(b).length) return false;\n return aKeys.every(key => b[key] === true);\n}\n\n/**\n * Get dirty fields -- fields whose current value differs from initialValues.\n * Keys are user-facing dotted paths ('a.b', 'a.0.c'), unlike the JSON array\n * keys stored in the values Map.\n * @param form\n * @return object mapping each dirty field's dotted path to true; the same\n * reference is returned until the dirty set actually changes\n */\nexport function getDirtyFields(form: Form): Record<string, boolean> {\n let cache = dirtyFieldsCaches.get(form);\n if (!cache) {\n cache = {version: 0, result: computeDirtyFields(form)};\n dirtyFieldsCaches.set(form, cache);\n } else if (cache.version > 0) {\n const result = computeDirtyFields(form);\n // Keep the old reference when the dirty set is unchanged (values always\n // map to true) so subscribers see identity-stable snapshots.\n if (!sameDirtyKeys(cache.result, result)) cache.result = result;\n cache.version = 0;\n }\n return cache.result;\n}\n\n/**\n * Get touched fields as user-facing dotted paths ('a.b', 'a.0.c'), unlike\n * the JSON array keys stored in the touched Set.\n * @param form\n * @return array of touched fields' dotted paths\n */\nexport function getTouchedFields({touched}: Form): string[] {\n return Array.from(touched, key =>\n (JSON.parse(key) as PathSegments).join('.')\n );\n}\n\n/**\n * Is touched -- any field has been touched\n * @param form\n */\nexport function isTouched({touched}: Form): boolean {\n return touched.size > 0;\n}\n\n/**\n * Remove field\n * @param form\n * @param name\n */\nexport function removeField(form: Form, name: Name): void {\n removeFieldByPath(form, createPath(name));\n}\n\n/**\n * Remove field\n * @param form\n * @param path\n */\nexport function removeFieldByPath(\n form: Form,\n {key, value: segments}: Path\n): void {\n const {emitter, values, touched, errors, validating, deleted} = form;\n values.delete(key);\n touched.delete(key);\n errors.delete(key);\n validating.delete(key);\n // The field is gone; a remount starts fresh rather than inheriting a\n // baseline committed by the previous incarnation.\n clearDirtyBaselines(form, key);\n // Tombstone the unregistered path so later reads do not fall back to\n // initialValues and \"revive\" the field's old initial value. A tombstone\n // never shadows live values: skip it when the branch is already covered\n // by a live ancestor key (e.g. a FieldArray rewrite stored the whole\n // array at the parent path) or a still-mounted descendant key.\n if (!hasLiveBranch(values, segments)) deleted.add(key);\n bumpDirtyVersion(form);\n emit(emitter, 'change');\n emit(emitter, 'touched');\n emit(emitter, 'errors');\n emit(emitter, 'validating');\n}\n\n/**\n * Does a live value cover the branch at `segments` -- either at an ancestor\n * key or below it at a descendant key?\n */\nfunction hasLiveBranch(\n values: Map<string, any>,\n segments: PathSegments\n): boolean {\n for (let i = 1; i < segments.length; i++) {\n if (values.has(JSON.stringify(segments.slice(0, i)))) return true;\n }\n const stem = `${JSON.stringify(segments).slice(0, -1)},`;\n for (const key of values.keys()) {\n if (key.startsWith(stem)) return true;\n }\n return false;\n}\n\n/**\n * Writing a value revives its whole branch: drop any removal tombstone for\n * the path itself, its ancestors, or its descendants (a remounted field\n * overwrites its own tombstone; rewriting a parent array supersedes the\n * tombstones of shifted child paths).\n */\nfunction reviveBranch(deleted: Set<string>, {key}: Path): void {\n if (!deleted.size) return;\n for (const tombstone of deleted) {\n if (\n tombstone === key ||\n tombstone.startsWith(`${key.slice(0, -1)},`) ||\n key.startsWith(`${tombstone.slice(0, -1)},`)\n ) {\n deleted.delete(tombstone);\n }\n }\n}\n\n/**\n * Set form initialValues\n *\n * Content-based early return: a new reference with equal content (the\n * re-rendered inline literal) is a no-op, so committed edits survive, while\n * genuinely changed content swaps the baseline and re-seeds — live values\n * and tombstones are cleared, touched flags and errors survive.\n * @param form\n * @param initialValues\n */\nexport function setInitialValues(form: Form, initialValues: any): void {\n if (\n form.initialValues === initialValues ||\n isEqual(form.initialValues, initialValues)\n ) {\n return;\n }\n form.initialValues = initialValues;\n // A new baseline invalidates the previous schema parse.\n form.parsedValues = undefined;\n form.values.clear();\n form.deleted.clear();\n // ...and every baseline committed against the old one.\n clearDirtyBaselines(form);\n bumpDirtyVersion(form);\n emit(form.emitter, 'change');\n}\n\n/** Options accepted by {@link reset}. Every flag defaults to `false` —\n * omitting the object (or any flag) keeps the plain full-reset behavior.\n * Names mirror react-hook-form's reset options to ease migration. */\nexport interface ResetOptions {\n /** Keep the current values of fields that are dirty — differ from the\n * pre-reset initialValues (the same rule {@link getDirtyFields} applies).\n * Clean fields fall back to the new initialValues as usual. */\n keepDirtyValues?: boolean;\n /** Keep the touched set instead of clearing it. */\n keepTouched?: boolean;\n /** Keep field errors instead of clearing them. */\n keepErrors?: boolean;\n /** Keep the submitted flag (`isSubmitSuccessful`) instead of clearing\n * it. */\n keepIsSubmitted?: boolean;\n /** Keep `submitCount` instead of resetting it to 0. */\n keepSubmitCount?: boolean;\n /** Keep `isSubmitting` instead of resetting it to false. */\n keepIsSubmitting?: boolean;\n}\n\n/**\n * Reset form\n * @param form\n * @param initialValues\n * @param options keep-flags to preserve slices of state through the reset\n */\nexport function reset(\n form: Form,\n initialValues?: any,\n options?: ResetOptions\n): void {\n // Snapshot dirty fields' live values before the wipe: dirtiness is\n // measured against the pre-reset initialValues, so capture must happen\n // before form.values and form.initialValues are touched.\n const dirtyValues = options?.keepDirtyValues\n ? Object.keys(getDirtyFields(form)).map(key => ({\n key,\n value: getValue(form, key)\n }))\n : [];\n form.initialValues = initialValues;\n // The fresh baseline drops any schema parse from the previous cycle.\n form.parsedValues = undefined;\n if (!options?.keepErrors) clearErrors(form);\n const {emitter, touched, values, deleted, validating} = form;\n values.clear();\n deleted.clear();\n clearDirtyBaselines(form);\n if (!options?.keepTouched) touched.clear();\n validating.clear();\n if (!options?.keepIsSubmitting) form.isSubmitting = false;\n if (!options?.keepSubmitCount) form.submitCount = 0;\n if (!options?.keepIsSubmitted) form.isSubmitSuccessful = undefined;\n bumpDirtyVersion(form);\n // Write the kept dirty values back over the fresh baseline: plain\n // setValueByPath, so no validation fires and nothing is marked touched.\n for (const {key, value} of dirtyValues) {\n setValueByPath(form, createPath(key), value);\n }\n emit(emitter, 'change');\n emit(emitter, 'touched');\n emit(emitter, 'validating');\n emit(emitter, 'submitting');\n emit(emitter, 'submitCount');\n emit(emitter, 'submitSuccessful');\n emit(emitter, 'reset');\n}\n\n/** Options accepted by {@link resetField}. The flags default to `false`;\n * `value` has no default — omitted, the field falls back to initialValues;\n * provided, the explicit value becomes the live value with no fallback at\n * all. Mirrors react-hook-form's resetField options (`value` plays their\n * `defaultValue`'s role) to ease migration. */\nexport interface ResetFieldOptions {\n /** Keep the field's touched flag instead of clearing it. */\n keepTouched?: boolean;\n /** Keep the field's errors instead of clearing them. */\n keepErrors?: boolean;\n /** Explicit post-reset value for the field — never falls back to\n * initialValues. */\n value?: any;\n}\n\n/**\n * Reset a single field: drop its live value (reads fall back to the\n * baseline — initialValues, or the schema's parsed output when one\n * exists, in which case the path is removed from parsedValues and the\n * initial value pinned back so the field reads initialValues again),\n * clear its touched flag and errors, and revive the path's removal\n * tombstones — the inverse of {@link removeFieldByPath}. Other fields\n * and the submission flags are untouched; see {@link reset} for the\n * form-wide counterpart.\n *\n * @param form\n * @param name\n * @param options\n */\nexport function resetField<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P, options?: ResetFieldOptions): void {\n const path = createPath(name);\n const {emitter, values, touched, errors, deleted} = form;\n values.delete(path.key);\n // The field returns to its baseline; commits from before the reset no\n // longer shadow the comparison.\n clearDirtyBaselines(form, path.key);\n // A parse baseline wholesale-shadows initialValues in reads (see\n // getValues), so unset alone would read the path as undefined. Remove\n // the path from the tree (immutable — parsedValues shares branches with\n // the schema's own output) and pin the initial value back as the live\n // value: equal to initialValues, so the field stays clean.\n if (form.parsedValues !== undefined) {\n form.parsedValues = unset(form.parsedValues, path.value);\n const initial = get(form.initialValues, path.value);\n if (initial !== undefined) values.set(path.key, initial);\n }\n if (options && 'value' in options) {\n values.set(path.key, options.value);\n }\n // A reset re-registers the branch, same as a write: tombstones on the\n // path or around it stop applying.\n reviveBranch(deleted, path);\n // Payload-less like removeFieldByPath: reviveBranch can un-tombstone\n // ancestor or descendant paths, whose readers must re-sync too.\n emit(emitter, 'change');\n if (!options?.keepTouched && touched.delete(path.key)) {\n emit(emitter, 'touched', path);\n }\n if (!options?.keepErrors && errors.delete(path.key)) {\n emit(emitter, 'errors', path);\n }\n bumpDirtyVersion(form);\n}\n\n/**\n * @param form\n */\nexport function hasErrors({errors}: Form): boolean {\n return errors.size > 0;\n}\n\n/**\n * Trigger field validation.\n *\n * Without `name` every registered field validator runs. A single `name` —\n * dotted string or segments array — runs only that field's validator, and\n * an array of names runs each one in order. An empty array is a no-op, as\n * is any name with no registered validator. An array argument counts as\n * one segments path only when it mixes in numbers (`['items', 0]`); pure\n * string arrays are name lists, so `['a', 'b']` triggers fields `a` and\n * `b`, not the nested path `a.b`.\n *\n * The returned promise waits for the triggered validation to settle —\n * async validators included — so their errors have already landed in\n * `form.errors` when it resolves. It never rejects: landing errors is the\n * expected outcome here, not a failure. Resolves `true` when the triggered\n * scope is error-free, `false` otherwise. Without `name` the scope is all\n * fields plus the form-level `validate` result (which runs after field\n * validators settle, same pipeline as {@link ensureValidate}); with `name`\n * only those fields' own errors count and form-level `validate` is\n * skipped (RHF semantics).\n *\n * Fire-and-forget callers may ignore the promise: the validator kicks\n * still happen synchronously, matching the pre-promise behavior.\n *\n * @param form\n * @param name field name(s) to trigger, or all fields when omitted\n * @return whether the triggered scope is error-free once validation settles\n */\nexport async function trigger(\n form: Form,\n name?: Name | Name[]\n): Promise<boolean> {\n // Never reject (an error landing is a normal outcome, not a failure), so\n // waitUntil's isReject is permanently false. Waiting on every FIELD\n // validator is deliberately conservative: it also rides out unrelated\n // in-flight field validators rather than racing them. The form-level\n // validate's own window is excluded (fieldsSettled) — callers wait that\n // out through the kick's promise instead, so a pending window never\n // gates the next kick.\n const settle = () =>\n waitUntil(\n form.emitter,\n 'validating',\n () => fieldsSettled(form),\n () => false\n );\n\n if (name === undefined) {\n form.validators.forEach(validator => validator());\n await settle();\n if (form.validate) await runFormValidate(form);\n return !hasErrors(form);\n }\n\n const keys: string[] =\n typeof name === 'string' || isSegmentsPath(name)\n ? [createPath(name).key]\n : name.map(one => createPath(one).key);\n keys.forEach(key => form.validators.get(key)?.());\n await settle();\n return keys.every(key => !form.errors.has(key));\n}\n\n/** Numbers only occur inside a segments path (`['a', 0]`), never as\n * standalone names, so a top-level number marks `name` as one single path\n * rather than a list of names. */\nfunction isSegmentsPath(name: PathSegments | Name[]): name is PathSegments {\n return (name as (number | unknown)[]).some(part => typeof part === 'number');\n}\n\nfunction isFieldError(value: any): value is FieldError {\n return (\n !!value &&\n typeof value === 'object' &&\n typeof value.type === 'string' &&\n typeof value.message === 'string'\n );\n}\n\n/**\n * Flatten a form-level validate result and write each leaf error through\n * setError. Nested objects descend into deeper paths ({a: {b: 'msg'}} sets\n * the 'a.b' error), array values contribute every non-empty string they\n * hold as separate errors (zod flatten() formErrors style), and\n * FieldError-shaped objects are stored as-is. Falsy values are skipped.\n */\nfunction setFormErrors(\n form: Form,\n result: Record<string, any>,\n segments: PathSegments = []\n): void {\n Object.entries(result).forEach(([key, value]) => {\n const path: PathSegments = [...segments, ...normalizePath(key)];\n if (typeof value === 'string') {\n if (value) setError(form, path, value);\n } else if (Array.isArray(value)) {\n setError(form, path, value);\n } else if (isFieldError(value)) {\n setError(form, path, value);\n } else if (value && typeof value === 'object') {\n setFormErrors(form, value, path);\n }\n });\n}\n\n/** Store a schema validator's parsed output as the getValues baseline\n * layer above initialValues. Payload-less 'change' notifies value\n * watchers (useValue, useDirtyFields, ...); dirty state is untouched —\n * it only compares live edits against initialValues, and parsing is not\n * an edit. */\nfunction setParsedValues(form: Form, values: any): void {\n if (values === undefined || values === form.parsedValues) return;\n form.parsedValues = values;\n emit(form.emitter, 'change');\n}\n\n/**\n * Land a form-level validate result. A plain record keeps the\n * long-standing behavior — flattened into field errors by\n * {@link setFormErrors}. A branded {@link ValidationOutcome} splits\n * instead: `errors` flattens exactly like a plain record, and `values`\n * (the schema's parsed output — coerced/transformed values included)\n * becomes the form's parsedValues baseline. Falsy results are skipped,\n * branded or not.\n */\nfunction applyValidateResult(\n form: Form,\n result: ValidateResult<any> | undefined\n): void {\n if (!result) return;\n if (typeof result === 'object' && VALIDATION_OUTCOME in result) {\n const outcome = result as ValidationOutcome<any>;\n if (outcome.errors) setFormErrors(form, outcome.errors);\n setParsedValues(form, outcome.values);\n return;\n }\n setFormErrors(form, result as Record<string, any>);\n}\n\n/** Key the form-level validate round reserves in `form.validating` while\n * its debounce window is pending or its async round is in flight. Real\n * path keys are JSON-stringified segments (always bracketed), so a bare\n * word can never collide. */\nconst FORM_VALIDATING_KEY = '__form_validate__';\n\n/** Are all FIELD validation rounds drained? trigger/ensureValidate wait on\n * this before kicking the form-level validate (its errors gate whether the\n * form-level round may run at all). The form validate's own reserved key\n * is deliberately excluded: its window is waited out through the kick's\n * returned promise instead, so a pending window or in-flight form round\n * never gates the next kick — a kick during an in-flight round opens a\n * new window and the newer round supersedes, mirroring the per-field\n * `validateDebounce` contract. */\nfunction fieldsSettled(form: Form): boolean {\n for (const key of form.validating) {\n if (key !== FORM_VALIDATING_KEY) return false;\n }\n return true;\n}\n\n/** Sentinel telling {@link settleFormValidate} the round landed cleanly —\n * distinct from every rejection payload, including `undefined`. */\nconst SETTLED = Symbol('form-validate-settled');\n\n/** Per-form bookkeeping for the debounced form-level validate: the\n * pending window timer, the in-flight round, and the waiters merged into\n * the current window group. Held in a WeakMap so the Form instance shape\n * is untouched for forms that never set `validateDebounce`. */\ninterface FormValidateState {\n timer: ReturnType<typeof setTimeout> | null;\n controller: AbortController | null;\n /** Identity of the in-flight round; a superseded round's outcome\n * (rejection included) is dropped by comparing against it. */\n round: object | null;\n /** Whether this state currently holds FORM_VALIDATING_KEY in\n * form.validating. */\n marked: boolean;\n waiters: Array<{resolve: () => void; reject: (error: unknown) => void}>;\n}\n\nconst formValidateStates = new WeakMap<Form, FormValidateState>();\n\nfunction getFormValidateState(form: Form): FormValidateState {\n let state = formValidateStates.get(form);\n if (!state) {\n state = {\n timer: null,\n controller: null,\n round: null,\n marked: false,\n waiters: []\n };\n formValidateStates.set(form, state);\n }\n return state;\n}\n\n/**\n * Run the form-level `validate` and land its result, honoring the form's\n * `validateDebounce` option.\n *\n * Undebounced (`0`/undefined) the caller's await *is* the validate call —\n * the long-standing pipeline, unchanged: no validating mark, no round\n * gating, immediate values snapshot, rejection propagating to the caller.\n *\n * Debounced, the kick opens (or restarts — kicks inside the window merge)\n * a window during which the form counts as validating, so `trigger` /\n * `ensureValidate` / submit wait the window out exactly like a field's\n * `validateDebounce` window. When the timer fires, the round reads the\n * then-current values, supersedes (aborts) any in-flight round, and lands\n * its result. The returned promise settles once the window group's final\n * round has landed — rejecting when that round's validate callback threw\n * or its promise rejected, mirroring the undebounced propagation — so\n * merged callers all observe the same outcome.\n *\n * Only called under `if (form.validate)`.\n */\nfunction runFormValidate(form: Form): Promise<void> {\n const validate = form.validate;\n if (!validate) return Promise.resolve();\n const debounce = form.validateDebounce ?? 0;\n if (debounce <= 0) {\n // Standalone controller: nothing supersedes an undebounced call, so\n // its signal never fires — it exists for argument-shape parity with\n // the debounced rounds (and with field-level meta.signal).\n const controller = new AbortController();\n return Promise.resolve(\n validate(getValues(form), {form, signal: controller.signal})\n ).then(result => {\n applyValidateResult(form, result);\n });\n }\n const state = getFormValidateState(form);\n // (Re)open the window: a kick while the timer is pending restarts it\n // (only the last kick's values run); one while a round is in flight\n // keeps the validating mark held and defers to the new window's round.\n if (state.timer !== null) clearTimeout(state.timer);\n else {\n state.marked = true;\n form.validating.add(FORM_VALIDATING_KEY);\n emit(form.emitter, 'validating');\n }\n state.timer = setTimeout(() => {\n state.timer = null;\n const round = (state.round = {});\n runFormValidateRound(form, state, round).then(\n () => settleFormValidate(form, state, round, SETTLED),\n error => settleFormValidate(form, state, round, error)\n );\n }, debounce);\n return new Promise<void>((resolve, reject) => {\n state.waiters.push({resolve, reject});\n });\n}\n\n/** Run one form-level validate round with the form's current values.\n * Aborts the previous in-flight round's signal; a superseded round's\n * outcome — rejection included — is dropped by the round gate, exactly\n * like the field-level lock. */\nfunction runFormValidateRound(\n form: Form,\n state: FormValidateState,\n round: object\n): Promise<void> {\n const validate = form.validate;\n if (!validate) return Promise.resolve();\n state.controller?.abort();\n const controller = (state.controller = new AbortController());\n let outcome: Promise<any>;\n try {\n outcome = Promise.resolve(\n validate(getValues(form), {form, signal: controller.signal})\n );\n } catch (error) {\n outcome = Promise.reject(error);\n }\n return outcome.then(\n result => {\n if (state.round === round) applyValidateResult(form, result);\n },\n error => {\n if (state.round === round) throw error;\n }\n );\n}\n\n/** Land the window group's outcome: release the validating mark — after\n * the round's errors/values have already landed, because 'validating'\n * subscribers (trigger, ensureValidate) re-read state on wake — and\n * settle every merged waiter. A superseded round never lands here (the\n * newer round owns the release), and a window that re-opened while the\n * round was in flight defers: the mark and the waiters carry over to the\n * pending timer's round. */\nfunction settleFormValidate(\n form: Form,\n state: FormValidateState,\n round: object,\n outcome: unknown\n): void {\n if (state.round !== round) return;\n state.round = null;\n if (state.timer !== null) return;\n if (state.marked) {\n state.marked = false;\n form.validating.delete(FORM_VALIDATING_KEY);\n emit(form.emitter, 'validating');\n }\n const waiters = state.waiters;\n state.waiters = [];\n for (const waiter of waiters) {\n if (outcome === SETTLED) waiter.resolve();\n else waiter.reject(outcome);\n }\n}\n\n/**\n * Validate and throw if any field error.\n * @param form\n * @return resolve if no error; reject and stop validate if has an error\n */\nexport async function ensureValidate(form: Form): Promise<void> {\n form.validators.forEach(validator => validator());\n\n await waitUntil(\n form.emitter,\n 'validating',\n () => fieldsSettled(form),\n () => hasErrors(form)\n ).catch(() => {\n throw new Error(getFirstError(form));\n });\n\n if (form.validate) {\n await runFormValidate(form);\n if (hasErrors(form)) throw new Error(getFirstError(form));\n }\n}\n\n/**\n * Validate and return if any field error.\n * @param form\n * @return error message string or void\n */\nexport async function validate(form: Form): Promise<void | string> {\n return ensureValidate(form).catch(e => e.message);\n}\n\nexport function setIsSubmitting(form: Form, value: boolean): void {\n form.isSubmitting = value;\n emit(form.emitter, 'submitting');\n}\n\nexport function incrementSubmitCount(form: Form): void {\n form.submitCount++;\n emit(form.emitter, 'submitCount');\n}\n\nexport function setSubmitSuccessful(form: Form, value: boolean): void {\n form.isSubmitSuccessful = value;\n emit(form.emitter, 'submitSuccessful');\n}\n\n/**\n * Set the form-level disabled flag and emit a payload-less 'disabled'\n * event — subscribed fields (useField and the components built on it)\n * re-render with the merged disabled state: form flag || their own\n * `disabled` option.\n * @param form\n * @param value\n */\nexport function setDisabled(form: Form, value: boolean): void {\n form.disabled = value;\n emit(form.emitter, 'disabled');\n}\n\n/** Structural slice of a <form>-like element: an elements collection whose\n * controls expose the constraint-validation members we read. Matches the\n * DOM HTMLFormElement shape without coupling the core to DOM types. */\ninterface NativeFormElement {\n elements: ArrayLike<{\n name: string;\n checkValidity: () => boolean;\n validationMessage: string;\n }>;\n}\n\n/**\n * Converts a control's DOM name to the user-visible dotted path. Field\n * components render the path key (JSON.stringify'd segments, '[\"a\",\"0\"]')\n * as the name attribute, so JSON keys are parsed back and joined with\n * dots; any other name value is returned as-is.\n */\nfunction nameToPath(name: string): string {\n if (name.startsWith('[')) {\n try {\n const segments = JSON.parse(name);\n if (Array.isArray(segments)) return segments.join('.');\n } catch {\n // Not a JSON path key — fall through and use the raw name.\n }\n }\n return name;\n}\n\n/**\n * Collects the constraints failing native validation on a <form> as\n * {@link FieldErrorEntry} entries, in DOM order.\n *\n * Design note: native errors are deliberately NOT written into the form's\n * errors Map. That Map tracks custom validator state, while native\n * validity is transient DOM state owned by the browser (surfaced through\n * reportValidity); onInvalidSubmit receives this snapshot directly.\n */\nfunction getNativeErrors(formEl: NativeFormElement): FieldErrorEntry[] {\n const errors: FieldErrorEntry[] = [];\n const {elements} = formEl;\n for (let i = 0; i < elements.length; i++) {\n const el = elements[i];\n if (\n el.name &&\n typeof el.checkValidity === 'function' &&\n !el.checkValidity()\n ) {\n errors.push({\n path: nameToPath(el.name),\n type: 'native',\n message: el.validationMessage\n });\n }\n }\n return errors;\n}\n\n/** Submit callbacks for {@link handleSubmit}. All optional — a missing\n * callback is simply skipped, matching the <Form> component semantics. */\nexport interface HandleSubmitOptions<T extends Record<string, any> = any> {\n /** Called after validation passes, before onValidSubmit. */\n onSubmit?: (values: T, e?: any) => void | Promise<void>;\n /** Called after validation passes, following a successful onSubmit. */\n onValidSubmit?: (values: T, e?: any) => void | Promise<void>;\n /**\n * Called when validation fails.\n * @param errors array of {path, type, message} entries in insertion\n * order; path is the dotted field path ('a.b', 'list.0'), type is\n * the error kind ('custom' for plain string errors, 'native' for\n * failed DOM constraint validation), message is the display text\n * @param values current form values\n */\n onInvalidSubmit?: (errors: FieldErrorEntry[], values: T) => void;\n /**\n * Focus the first error field after a failed submit. Defaults to true —\n * only an explicit `false` disables it. When custom validation fails,\n * a 'focusError' event carrying the first error's path key is emitted\n * on the form (bound fields such as <Field> subscribe and focus their\n * input); when native constraint validation fails, the submitted\n * form's first ':invalid' control is focused directly.\n */\n shouldFocusError?: boolean;\n}\n\n/**\n * Create an async submit handler for `form` — the headless counterpart of\n * the <Form> component's onSubmit wiring.\n *\n * Behavior mirrors <Form> exactly: preventDefault when present, then the\n * submit state machine (isSubmitting/submitCount/isSubmitSuccessful) runs\n * around native constraint validation (via `e.currentTarget.checkValidity`,\n * skipped when the target has no checkValidity — e.g. React Native or\n * toolbar-button submits) and custom validators. Failed validation fires\n * onInvalidSubmit with the flattened error entries; a passing submit runs\n * onSubmit then onValidSubmit. Errors thrown by either are swallowed into\n * isSubmitSuccessful=false rather than rejecting the returned promise.\n * Failed validation also focuses the offending field (see\n * {@link HandleSubmitOptions.shouldFocusError}).\n *\n * @param form form instance\n * @param options submit callbacks\n * @return async event handler, callable without an event object\n */\nexport function handleSubmit<T extends Record<string, any> = any>(\n form: Form<T>,\n options?: HandleSubmitOptions<T>\n): (e?: {preventDefault?: () => void; currentTarget?: any}) => Promise<void> {\n const {\n onSubmit,\n onValidSubmit,\n onInvalidSubmit,\n shouldFocusError = true\n } = options ?? {};\n return async e => {\n if (e && typeof e.preventDefault === 'function') {\n e.preventDefault();\n }\n const formEl = e?.currentTarget;\n setIsSubmitting(form, true);\n incrementSubmitCount(form);\n const values = getValues(form);\n\n if (\n formEl &&\n typeof formEl.checkValidity === 'function' &&\n formEl.checkValidity() === false\n ) {\n formEl.reportValidity();\n // Focus the first natively-invalid control directly off the DOM;\n // native failures never enter the errors Map (see below).\n if (shouldFocusError && typeof formEl.querySelector === 'function') {\n const invalid = formEl.querySelector(':invalid') as HTMLElement | null;\n if (invalid && typeof invalid.focus === 'function') invalid.focus();\n }\n setIsSubmitting(form, false);\n setSubmitSuccessful(form, false);\n // Native constraint failures are read from the DOM (not the errors\n // Map, which only holds custom validation state — see getNativeErrors).\n if (onInvalidSubmit) onInvalidSubmit(getNativeErrors(formEl), values);\n return;\n }\n\n const error = await validate(form);\n\n if (error) {\n setIsSubmitting(form, false);\n setSubmitSuccessful(form, false);\n // Notify bound fields (e.g. <Field>) so the first errored one can\n // focus its input; the payload is the errors Map's first key.\n if (shouldFocusError) {\n const firstKey = form.errors.keys().next().value;\n if (firstKey !== undefined) emit(form.emitter, 'focusError', firstKey);\n }\n if (onInvalidSubmit) onInvalidSubmit(getErrors(form), values);\n return;\n }\n\n try {\n // Re-read after validation: a schema validator's parsed output\n // (ValidationOutcome.values) landed in parsedValues during\n // validate(), and the submit callbacks must see the coerced /\n // transformed values, not the raw pre-validation snapshot.\n const submitted = getValues(form);\n if (onSubmit) await onSubmit(submitted, e);\n if (onValidSubmit) await onValidSubmit(submitted, e);\n setSubmitSuccessful(form, true);\n } catch {\n setSubmitSuccessful(form, false);\n } finally {\n setIsSubmitting(form, false);\n }\n };\n}\n\n/** Options accepted by {@link setFocus}. All flags default to `false`. */\nexport interface SetFocusOptions {\n /** Select the field's text after focusing it. Bound fields call\n * `select()` on their element; elements without one (custom `as`\n * components) just focus. */\n shouldSelect?: boolean;\n}\n\n/**\n * Programmatically focus a bound field's element (e.g. the <Field>'s\n * input).\n *\n * Rides the same 'focusError' event channel a failed handleSubmit uses to\n * focus the first errored field: the payload is the target's path key,\n * with the focus options as a second, backward-compatible argument (older\n * subscribers declared with a single `key` parameter simply ignore it).\n * Being event-driven, it is a silent no-op when the field is unmounted or\n * nothing subscribes — unknown names never throw.\n *\n * @param form form instance\n * @param name field name (dot path or segments path)\n * @param options focus options\n */\nexport function setFocus(\n form: Form,\n name: Name,\n options?: SetFocusOptions\n): void {\n const {key} = createPath(name);\n // Omit the options argument when absent so the payload is exactly the\n // shape handleSubmit emits after a failed submit.\n if (options) emit(form.emitter, 'focusError', key, options);\n else emit(form.emitter, 'focusError', key);\n}\n"],"names":["emit","ee","key","_len","arguments","length","args","Array","_key","get","forEach","h","apply","on","handler","set","newSet","Set","getSet","add","delete","pathCache","Map","normalizePath","path","isArray","cached","value","result","identifier","flushIdentifier","push","i","char","quote","close","indexOf","TypeError","slice","content","test","Number","parsePath","unset","values","prop","props","next","arr","copy","setOwned","root","owned","container","parent","parentProp","has","create","name","JSON","stringify","VALIDATION_OUTCOME","getValues","form","initialValues","parsedValues","deleted","merged","parse","setValueByPath","options","emitter","size","tombstone","startsWith","reviveBranch","baselines","dirtyBaselines","stem","baselineKey","keys","pruneDirtyBaselines","bumpDirtyVersion","setError","error","errors","list","type","message","isFieldError","item","normalizeErrors","setErrorByPath","createPath","WeakMap","getDirtyBaseline","segments","reduce","current","p","dirtyFieldsCaches","cache","version","computeDirtyFields","dirtyFields","fn","join","forEachDirtyField","setFormErrors","Object","entries","applyValidateResult","outcome","setParsedValues","FORM_VALIDATING_KEY","SETTLED","formValidateStates","settleFormValidate","state","round","timer","marked","validating","waiters","waiter","resolve","reject","a","b","aKeys","every","sameDirtyKeys","touched","from","dirtyValues","clear","clearErrors","clearDirtyBaselines","isSubmitting","submitCount","isSubmitSuccessful","async","settle","waitUntil","event","isResolve","fieldsSettled","isReject","Promise","off","validators","validator","validate","debounce","validateDebounce","controller","AbortController","signal","then","getFormValidateState","clearTimeout","setTimeout","abort","runFormValidateRound","runFormValidate","hasErrors"],"mappings":"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","x_google_ignoreList":[0]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function e(e,t){for(var n=arguments.length,r=new Array(n>2?n-2:0),o=2;o<n;o++)r[o-2]=arguments[o];(e.get(t)||[]).forEach(function(e){return e.apply(void 0,r)})}function t(e,t,n){var r=function(e,t){var n=e.get(t);if(n)return n;var r=new Set;return e.set(t,r),r}(e,t);return r.add(n),function(){return r.delete(n)}}const n=new Map;function r(e){if(Array.isArray(e))return e;const t=n.get(e);if(t)return t;const r=function(e){const t=[];let n="";const r=()=>{t.push(n),n=""};for(let o=0;o<e.length;o++){const i=e[o];if("."===i)""!==n&&r();else if("["===i){""!==n&&r();const i=e[o+1];if('"'===i||"'"===i){const n=e.indexOf(i,o+2);if(-1===n)throw new TypeError(`Unterminated quote in path: ${e}`);if("]"!==e[n+1])throw new TypeError(`Expected "]" after quoted segment in path: ${e}`);t.push(e.slice(o+2,n)),o=n+1}else{const n=e.indexOf("]",o+1);if(-1===n)throw new TypeError(`Unterminated bracket in path: ${e}`);const r=e.slice(o+1,n);t.push(/^-?\d+$/.test(r)?Number(r):r),o=n}}else n+=i}""===n&&0!==t.length||r();return t}(e);return n.set(e,r),r}function o(e,t){if(!t.length||null==e)return e;const[n,...r]=t;if(r.length){const t=o(e[n],r);return t===e[n]?e:i(e,[n],t)}if(Array.isArray(e)){if(!(n in e))return e;const t=e.slice();return delete t[n],t}if("object"!=typeof e||!(n in e))return e;const s={...e};return delete s[n],s}function i(e,t,n){if(!t.length)return n;const[r,...o]=t;if("number"==typeof r){const t=Array.isArray(e)?e.slice():[];return t[r]=i(t[r],o,n),t}return{...e,[r]:i(e&&e[r],o,n)}}function s(e,t,n,r){if(!t.length)return n;let o=e,i=null,s="";for(let u=0;u<t.length;u++){const c=t[u];if(!r.has(o)){let t;t="number"==typeof c?Array.isArray(o)?o.slice():[]:{...o},r.add(t),0===u?e=t:i[s]=t,o=t}u===t.length-1?o[c]=n:(i=o,s=c,o=o[c])}return e}function u(e){const t=r(e);return{value:t,key:JSON.stringify(t)}}const c=Symbol("validation-outcome");function a(e){const{initialValues:t,parsedValues:n,values:r,deleted:i}=e,u=new Set;let c=n??t;for(const[e,t]of r)c=s(c,JSON.parse(e),t,u);for(const e of i)c=o(c,JSON.parse(e));return c}function l(t,n,r,o){const{emitter:i,values:s,deleted:u}=t;s.set(n.key,r),function(e,{key:t}){if(!e.size)return;for(const n of e)(n===t||n.startsWith(`${t.slice(0,-1)},`)||t.startsWith(`${n.slice(0,-1)},`))&&e.delete(n)}(u,n),function(e,{key:t}){const n=m.get(e);if(!n?.size)return;const r=`${t.slice(0,-1)},`;for(const e of n.keys())e.startsWith(r)&&n.delete(e)}(t,n),y(t),e(i,"change",n)}function f({errors:e}){const t=[];for(const[n,r]of e){const e=JSON.parse(n).join(".");for(const{type:n,message:o}of r)t.push({path:e,type:n,message:o})}return t}function d(t,n,r){!function({emitter:t,errors:n},r,o){const i=function(e){if("string"==typeof e)return e?[{type:"custom",message:e}]:void 0;if(A(e))return[e];if(!e)return;const t=[];return e.forEach(e=>{"string"==typeof e&&e?t.push({type:"custom",message:e}):A(e)&&t.push(e)}),t.length?t:void 0}(o);i?n.set(r.key,i):n.delete(r.key);e(t,"errors",r)}(t,u(n),r)}const m=new WeakMap;function g(e,t,n){const r=m.get(e);return r?.has(t)?r.get(t):(o=e.initialValues,n.reduce((e,t)=>{if(null!=e)return e[t]},o));var o}const v=new WeakMap;function y(e){const t=v.get(e);t&&t.version++}function p(e){const t={};return function(e,t){for(const[n,r]of e.values){const o=JSON.parse(n);g(e,n,o)!==r&&t(o.join("."))}}(e,e=>{t[e]=!0}),t}function h(e){let t=v.get(e);if(t){if(t.version>0){const n=p(e);(function(e,t){const n=Object.keys(e);return n.length===Object.keys(t).length&&n.every(e=>!0===t[e])})(t.result,n)||(t.result=n),t.version=0}}else t={version:0,result:p(e)},v.set(e,t);return t.result}function b({touched:e}){return Array.from(e,e=>JSON.parse(e).join("."))}function w(t,n,r){const o=[];t.initialValues=n,t.parsedValues=void 0,function(t){const{emitter:n,errors:r}=t;r.clear(),e(n,"errors")}(t);const{emitter:i,touched:s,values:c,deleted:a,validating:f}=t;c.clear(),a.clear(),function(e){const t=m.get(e);t&&t.clear()}(t),s.clear(),f.clear(),t.isSubmitting=!1,t.submitCount=0,t.isSubmitSuccessful=void 0,y(t);for(const{key:e,value:n}of o)l(t,u(e),n);e(i,"change"),e(i,"touched"),e(i,"validating"),e(i,"submitting"),e(i,"submitCount"),e(i,"submitSuccessful"),e(i,"reset")}async function k(n,r){const o=()=>{return e=n.emitter,r="validating",o=()=>function(e){for(const t of e.validating)if(t!==O)return!1;return!0}(n),i=()=>!1,new Promise((n,s)=>{if(i())return void s();if(o())return void n();const u=t(e,r,()=>{if(i())return u(),void s();o()&&(u(),n())})});var e,r,o,i};return n.validators.forEach(e=>e()),await o(),n.validate&&await function(t){const n=t.validate;if(!n)return Promise.resolve();const r=t.validateDebounce??0;if(r<=0){const e=new AbortController;return Promise.resolve(n(a(t),{form:t,signal:e.signal})).then(e=>{j(t,e)})}const o=function(e){let t=V.get(e);t||(t={timer:null,controller:null,round:null,marked:!1,waiters:[]},V.set(e,t));return t}(t);null!==o.timer?clearTimeout(o.timer):(o.marked=!0,t.validating.add(O),e(t.emitter,"validating"));return o.timer=setTimeout(()=>{o.timer=null;const e=o.round={};(function(e,t,n){const r=e.validate;if(!r)return Promise.resolve();t.controller?.abort();const o=t.controller=new AbortController;let i;try{i=Promise.resolve(r(a(e),{form:e,signal:o.signal}))}catch(e){i=Promise.reject(e)}return i.then(r=>{t.round===n&&j(e,r)},e=>{if(t.round===n)throw e})})(t,o,e).then(()=>N(t,o,e,E),n=>N(t,o,e,n))},r),new Promise((e,t)=>{o.waiters.push({resolve:e,reject:t})})}(n),!function({errors:e}){return e.size>0}(n)}function A(e){return!!e&&"object"==typeof e&&"string"==typeof e.type&&"string"==typeof e.message}function S(e,t,n=[]){Object.entries(t).forEach(([t,o])=>{const i=[...n,...r(t)];"string"==typeof o?o&&d(e,i,o):Array.isArray(o)||A(o)?d(e,i,o):o&&"object"==typeof o&&S(e,o,i)})}function j(t,n){if(n){if("object"==typeof n&&c in n){const r=n;return r.errors&&S(t,r.errors),void function(t,n){void 0!==n&&n!==t.parsedValues&&(t.parsedValues=n,e(t.emitter,"change"))}(t,r.values)}S(t,n)}}const O="__form_validate__";const E=Symbol("form-validate-settled"),V=new WeakMap;function N(t,n,r,o){if(n.round!==r)return;if(n.round=null,null!==n.timer)return;n.marked&&(n.marked=!1,t.validating.delete(O),e(t.emitter,"validating"));const i=n.waiters;n.waiters=[];for(const e of i)o===E?e.resolve():e.reject(o)}export{c as V,h as a,a as b,f as c,b as g,t as o,w as r,k as t};
|
|
2
|
+
//# sourceMappingURL=form-DsydpBhT.mjs.map
|