react-f0rm 1.3.0 → 1.5.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.
Files changed (99) hide show
  1. package/README.md +98 -1140
  2. package/devtools.d.ts +1 -0
  3. package/devtools.js +1 -0
  4. package/dist/array-Bu7W8BSz.d.ts +54 -0
  5. package/dist/devtools/index.cjs.js +1 -1
  6. package/dist/devtools/index.cjs.js.map +1 -1
  7. package/dist/devtools/index.d.cts +24 -0
  8. package/dist/devtools/index.d.mts +24 -0
  9. package/dist/devtools/index.d.ts +8 -17
  10. package/dist/devtools/index.mjs +1 -1
  11. package/dist/devtools/index.mjs.map +1 -1
  12. package/dist/errors-DA4ReEd9.mjs +2 -0
  13. package/dist/errors-DA4ReEd9.mjs.map +1 -0
  14. package/dist/errors-TzyWwBfw.cjs.js +2 -0
  15. package/dist/errors-TzyWwBfw.cjs.js.map +1 -0
  16. package/dist/index.cjs.js +1 -1
  17. package/dist/index.cjs.js.map +1 -1
  18. package/dist/index.d.cts +1253 -0
  19. package/dist/index.d.mts +1253 -0
  20. package/dist/index.d.ts +570 -895
  21. package/dist/index.mjs +1 -1
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/index.umd.js +1256 -535
  24. package/dist/index.umd.js.map +1 -1
  25. package/dist/index.umd.min.js +2 -2
  26. package/dist/index.umd.min.js.map +1 -1
  27. package/dist/persist.cjs.js +1 -1
  28. package/dist/persist.cjs.js.map +1 -1
  29. package/dist/persist.d.cts +28 -0
  30. package/dist/persist.d.mts +28 -0
  31. package/dist/persist.d.ts +9 -30
  32. package/dist/persist.mjs +1 -1
  33. package/dist/persist.mjs.map +1 -1
  34. package/dist/resolvers/standard-schema.cjs.js +1 -1
  35. package/dist/resolvers/standard-schema.cjs.js.map +1 -1
  36. package/dist/resolvers/standard-schema.d.cts +2 -0
  37. package/dist/resolvers/standard-schema.d.mts +2 -0
  38. package/dist/resolvers/standard-schema.d.ts +1 -67
  39. package/dist/resolvers/standard-schema.mjs +1 -1
  40. package/dist/resolvers/standard-schema.mjs.map +1 -1
  41. package/dist/resolvers/yup.cjs.js +1 -1
  42. package/dist/resolvers/yup.cjs.js.map +1 -1
  43. package/dist/resolvers/yup.d.cts +6 -0
  44. package/dist/resolvers/yup.d.mts +6 -0
  45. package/dist/resolvers/yup.d.ts +0 -1
  46. package/dist/resolvers/yup.mjs +1 -1
  47. package/dist/resolvers/yup.mjs.map +1 -1
  48. package/dist/resolvers/zod.cjs.js +1 -1
  49. package/dist/resolvers/zod.cjs.js.map +1 -1
  50. package/dist/resolvers/zod.d.cts +34 -0
  51. package/dist/resolvers/zod.d.mts +34 -0
  52. package/dist/resolvers/zod.d.ts +30 -3
  53. package/dist/resolvers/zod.mjs +1 -1
  54. package/dist/resolvers/zod.mjs.map +1 -1
  55. package/dist/server/index.cjs.js +1 -1
  56. package/dist/server/index.cjs.js.map +1 -1
  57. package/dist/server/index.d.cts +38 -0
  58. package/dist/server/index.d.mts +38 -0
  59. package/dist/server/index.d.ts +26 -65
  60. package/dist/server/index.mjs +1 -1
  61. package/dist/server/index.mjs.map +1 -1
  62. package/dist/standard-schema-BAaTmAHh.d.ts +682 -0
  63. package/dist/standardSchema-5WezjHlp.mjs +2 -0
  64. package/dist/standardSchema-5WezjHlp.mjs.map +1 -0
  65. package/dist/standardSchema-DINHlsYR.cjs.js +2 -0
  66. package/dist/standardSchema-DINHlsYR.cjs.js.map +1 -0
  67. package/dist/validate-2pX-N1O6.cjs.js +2 -0
  68. package/dist/validate-2pX-N1O6.cjs.js.map +1 -0
  69. package/dist/validate-C0HOsP9v.mjs +2 -0
  70. package/dist/validate-C0HOsP9v.mjs.map +1 -0
  71. package/dist/values-BbPnLByD.cjs.js +2 -0
  72. package/dist/values-BbPnLByD.cjs.js.map +1 -0
  73. package/dist/values-CHsmcZk4.mjs +2 -0
  74. package/dist/values-CHsmcZk4.mjs.map +1 -0
  75. package/package.json +75 -28
  76. package/persist.d.ts +1 -0
  77. package/persist.js +1 -0
  78. package/resolvers/standard-schema.d.ts +1 -0
  79. package/resolvers/standard-schema.js +1 -0
  80. package/resolvers/yup.d.ts +1 -0
  81. package/resolvers/yup.js +1 -0
  82. package/resolvers/zod.d.ts +1 -0
  83. package/resolvers/zod.js +1 -0
  84. package/server.d.ts +1 -0
  85. package/server.js +1 -0
  86. package/dist/errors-BKrUdpfI.cjs.js +0 -2
  87. package/dist/errors-BKrUdpfI.cjs.js.map +0 -1
  88. package/dist/errors-CrQBddrJ.mjs +0 -2
  89. package/dist/errors-CrQBddrJ.mjs.map +0 -1
  90. package/dist/form-CeKSBs31.d.ts +0 -486
  91. package/dist/validate-CNtuUhmk.mjs +0 -2
  92. package/dist/validate-CNtuUhmk.mjs.map +0 -1
  93. package/dist/validate-Cl4ksNFu.cjs.js +0 -2
  94. package/dist/validate-Cl4ksNFu.cjs.js.map +0 -1
  95. package/dist/validate-nksgv1pR.d.ts +0 -272
  96. package/dist/values-Cu6awQOJ.cjs.js +0 -2
  97. package/dist/values-Cu6awQOJ.cjs.js.map +0 -1
  98. package/dist/values-DRY-a32G.mjs +0 -2
  99. package/dist/values-DRY-a32G.mjs.map +0 -1
@@ -0,0 +1,2 @@
1
+ import{V as t,F as n}from"./errors-DA4ReEd9.mjs";function e(t){return!!t&&"object"==typeof t&&"function"==typeof t["~standard"]?.validate}function r(t){return{type:"standard",message:t?.message||"Validation failed"}}function s(t){return async n=>{const e=await t["~standard"].validate(n);if(e.issues?.length)return e.issues.map(r)}}function a(e){return async s=>{const a=await e["~standard"].validate(s),{issues:u}=a;if(!u?.length)return{[t]:!0,values:"value"in a?a.value:void 0};const f={};for(const t of u){const e=o(t);if(e.length)i(f,e,r(t));else{const e=f[n]??=[];Array.isArray(e)&&e.push(r(t))}}return{[t]:!0,errors:c(f)||{}}}}function o(t){return(t.path||[]).map(t=>String("object"==typeof t&&null!==t?t.key:t))}function i(t,n,e){let r=t;for(let t=0;t<n.length-1;t++){const e=n[t];void 0===r[e]&&(r[e]={});const s=r[e];if(!u(s))return;r=s}const s=n[n.length-1],a=r[s];void 0===a?r[s]=[e]:Array.isArray(a)&&a.push(e)}function u(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}function c(t){const n={};for(const[e,r]of Object.entries(t))if(u(r)){const t=c(r);t&&(n[e]=t)}else n[e]=r;return Object.keys(n).length?n:void 0}export{s as a,e as h,a as s};
2
+ //# sourceMappingURL=standardSchema-5WezjHlp.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standardSchema-5WezjHlp.mjs","sources":["../src/standardSchema.ts"],"sourcesContent":["/** Standard Schema v1 (standardschema.dev) contract and adapters, shared\n * by the resolver entry and direct-schema support. The types are a\n * structural copy of the spec — no dependency on any schema library, so\n * zod/valibot/arktype and every v1 implementer work without importing\n * them. */\nimport {FORM_ERROR, VALIDATION_OUTCOME} from './core/errors';\nimport type {FieldError, ValidationOutcome, Validator} from './form';\n\n/** A single Standard Schema issue. */\nexport type StandardSchemaIssue = {\n readonly message: string;\n readonly path?:\n ReadonlyArray<PropertyKey | {readonly key: PropertyKey}> | undefined;\n};\n\n/** The union Standard Schema's `validate` resolves to. */\nexport type StandardSchemaResult<Output = unknown> =\n | {readonly value: Output; readonly issues?: undefined}\n | {readonly issues: ReadonlyArray<StandardSchemaIssue>};\n\n/** Structural Standard Schema v1. `types` (optional in the spec) is what\n * {@link InferSchemaValues} and `createForm`'s inference read. */\nexport type StandardSchemaV1<Input = unknown, Output = Input> = {\n readonly '~standard': {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (\n value: Input\n ) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;\n readonly types?: {\n readonly input: Input;\n readonly output: Output;\n };\n };\n};\n\n/** The values type a Standard Schema produces: the `Output` of its\n * `~standard.types` (coercions/transforms included). `never` when the\n * schema lacks `types` — pass an explicit `TValues`. */\nexport type InferSchemaValues<S> = S extends {\n readonly '~standard': {readonly types: {readonly output: infer Out}};\n}\n ? Out\n : never;\n\n/** Implements Standard Schema v1 props? A boolean predicate, not a type\n * guard — narrowing the union through a guard fails either way, so call\n * sites cast after the check. */\nexport function hasStandardProps(schema: any): boolean {\n return (\n !!schema &&\n typeof schema === 'object' &&\n typeof schema['~standard']?.validate === 'function'\n );\n}\n\n/** Issue → stored error entry. */\nfunction toFieldError(issue: StandardSchemaIssue | undefined): FieldError {\n return {type: 'standard', message: issue?.message || 'Validation failed'};\n}\n\n/** Field-level adapter: validate one value with a schema and map every\n * issue to a FieldError (all issues surface; readers see the first). The\n * parsed output is validation-only — field validators never rewrite. */\nexport function schemaToFieldValidator(\n schema: StandardSchemaV1<any, any>\n): Validator {\n return async (value: any) => {\n const result = await schema['~standard'].validate(value);\n if (!result.issues?.length) return undefined;\n return result.issues.map(toFieldError);\n };\n}\n\n/** Form-level adapter: validate the whole values object and return a\n * ValidationOutcome. Failure nests errors ({a: {b: FieldError[]}});\n * pathless issues land on FORM_ERROR. Success exposes the schema's parsed\n * output as the parsedValues baseline. `createForm({validate: schema})`\n * wraps through this automatically. */\nexport function schemaToFormValidator<T extends Record<string, any>>(\n schema: StandardSchemaV1<any, T>\n): (values: T) => Promise<ValidationOutcome<T>> {\n return async (values: T) => {\n const result = await schema['~standard'].validate(values);\n const {issues} = result;\n if (!issues?.length) {\n // `in` keeps the union narrowed: the success variant carries `value`.\n return {\n [VALIDATION_OUTCOME]: true,\n values: 'value' in result ? result.value : undefined\n };\n }\n const errors: Record<string, any> = {};\n for (const issue of issues) {\n const segments = toPathSegments(issue);\n if (segments.length) {\n assignAtPath(errors, segments, toFieldError(issue));\n } else {\n // Pathless issues accumulate on FORM_ERROR instead of shadowing each other.\n const slot = (errors[FORM_ERROR] ??= []);\n if (Array.isArray(slot)) slot.push(toFieldError(issue));\n }\n }\n return {[VALIDATION_OUTCOME]: true, errors: pruneEmpty(errors) || {}};\n };\n}\n\n/** Stringify an issue path: PropertyKey or {key} segments → strings. */\nfunction toPathSegments(issue: StandardSchemaIssue): string[] {\n return (issue.path || []).map(segment =>\n String(\n typeof segment === 'object' && segment !== null\n ? (segment as {key: PropertyKey}).key\n : segment\n )\n );\n}\n\n/** Append an error at a nested path: leaves are FieldError[] (issues\n * accumulate in order); a path conflicting with a leaf is skipped. */\nfunction assignAtPath(\n root: Record<string, any>,\n segments: string[],\n error: FieldError\n): void {\n let node = root;\n for (let i = 0; i < segments.length - 1; i++) {\n const segment = segments[i];\n if (node[segment] === undefined) node[segment] = {};\n const next = node[segment];\n if (!isBranch(next)) return;\n node = next;\n }\n const leaf = segments[segments.length - 1];\n const slot = node[leaf];\n if (slot === undefined) node[leaf] = [error];\n else if (Array.isArray(slot)) slot.push(error);\n}\n\n/** A branch is a plain container built while nesting; its leaves are the FieldError[] arrays. */\nfunction isBranch(value: any): value is Record<string, any> {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\n/** Drop empty branch objects left behind by conflicting issue paths. */\nfunction pruneEmpty(\n node: Record<string, any>\n): Record<string, any> | undefined {\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(node)) {\n if (isBranch(value)) {\n const pruned = pruneEmpty(value);\n if (pruned) result[key] = pruned;\n } else {\n result[key] = value;\n }\n }\n return Object.keys(result).length ? result : undefined;\n}\n"],"names":["hasStandardProps","schema","validate","toFieldError","issue","type","message","schemaToFieldValidator","async","value","result","issues","length","map","schemaToFormValidator","values","VALIDATION_OUTCOME","errors","segments","toPathSegments","assignAtPath","slot","FORM_ERROR","Array","isArray","push","pruneEmpty","path","segment","String","key","root","error","node","i","next","isBranch","leaf","Object","entries","pruned","keys"],"mappings":"iDAgDO,SAASA,EAAiBC,GAC/B,QACIA,GACgB,iBAAXA,GACkC,mBAAlCA,EAAO,cAAcC,QAEhC,CAGA,SAASC,EAAaC,GACpB,MAAO,CAACC,KAAM,WAAYC,QAASF,GAAOE,SAAW,oBACvD,CAKO,SAASC,EACdN,GAEA,OAAOO,MAAOC,IACZ,MAAMC,QAAeT,EAAO,aAAaC,SAASO,GAClD,GAAKC,EAAOC,QAAQC,OACpB,OAAOF,EAAOC,OAAOE,IAAIV,GAE7B,CAOO,SAASW,EACdb,GAEA,OAAOO,MAAOO,IACZ,MAAML,QAAeT,EAAO,aAAaC,SAASa,IAC5CJ,OAACA,GAAUD,EACjB,IAAKC,GAAQC,OAEX,MAAO,CACLI,CAACA,IAAqB,EACtBD,OAAQ,UAAWL,EAASA,EAAOD,WAAQ,GAG/C,MAAMQ,EAA8B,CAAA,EACpC,IAAA,MAAWb,KAASO,EAAQ,CAC1B,MAAMO,EAAWC,EAAef,GAChC,GAAIc,EAASN,OACXQ,EAAaH,EAAQC,EAAUf,EAAaC,QACvC,CAEL,MAAMiB,EAAQJ,EAAOK,KAAgB,GACjCC,MAAMC,QAAQH,MAAYI,KAAKtB,EAAaC,GAClD,CACF,CACA,MAAO,CAACY,CAACA,IAAqB,EAAMC,OAAQS,EAAWT,IAAW,IAEtE,CAGA,SAASE,EAAef,GACtB,OAAQA,EAAMuB,MAAQ,IAAId,IAAIe,GAC5BC,OACqB,iBAAZD,GAAoC,OAAZA,EAC1BA,EAA+BE,IAChCF,GAGV,CAIA,SAASR,EACPW,EACAb,EACAc,GAEA,IAAIC,EAAOF,EACX,IAAA,IAASG,EAAI,EAAGA,EAAIhB,EAASN,OAAS,EAAGsB,IAAK,CAC5C,MAAMN,EAAUV,EAASgB,QACH,IAAlBD,EAAKL,KAAwBK,EAAKL,GAAW,CAAA,GACjD,MAAMO,EAAOF,EAAKL,GAClB,IAAKQ,EAASD,GAAO,OACrBF,EAAOE,CACT,CACA,MAAME,EAAOnB,EAASA,EAASN,OAAS,GAClCS,EAAOY,EAAKI,QACL,IAAThB,EAAoBY,EAAKI,GAAQ,CAACL,GAC7BT,MAAMC,QAAQH,IAAOA,EAAKI,KAAKO,EAC1C,CAGA,SAASI,EAAS3B,GAChB,QAASA,GAA0B,iBAAVA,IAAuBc,MAAMC,QAAQf,EAChE,CAGA,SAASiB,EACPO,GAEA,MAAMvB,EAA8B,CAAA,EACpC,IAAA,MAAYoB,EAAKrB,KAAU6B,OAAOC,QAAQN,GACxC,GAAIG,EAAS3B,GAAQ,CACnB,MAAM+B,EAASd,EAAWjB,GACtB+B,IAAQ9B,EAAOoB,GAAOU,EAC5B,MACE9B,EAAOoB,GAAOrB,EAGlB,OAAO6B,OAAOG,KAAK/B,GAAQE,OAASF,OAAS,CAC/C"}
@@ -0,0 +1,2 @@
1
+ "use strict";var t=require("./errors-TzyWwBfw.cjs.js");function e(t){return{type:"standard",message:t?.message||"Validation failed"}}function r(t){return(t.path||[]).map(t=>String("object"==typeof t&&null!==t?t.key:t))}function n(t,e,r){let n=t;for(let t=0;t<e.length-1;t++){const r=e[t];void 0===n[r]&&(n[r]={});const o=n[r];if(!s(o))return;n=o}const o=e[e.length-1],a=n[o];void 0===a?n[o]=[r]:Array.isArray(a)&&a.push(r)}function s(t){return!!t&&"object"==typeof t&&!Array.isArray(t)}function o(t){const e={};for(const[r,n]of Object.entries(t))if(s(n)){const t=o(n);t&&(e[r]=t)}else e[r]=n;return Object.keys(e).length?e:void 0}exports.hasStandardProps=function(t){return!!t&&"object"==typeof t&&"function"==typeof t["~standard"]?.validate},exports.schemaToFieldValidator=function(t){return async r=>{const n=await t["~standard"].validate(r);if(n.issues?.length)return n.issues.map(e)}},exports.schemaToFormValidator=function(s){return async a=>{const i=await s["~standard"].validate(a),{issues:c}=i;if(!c?.length)return{[t.VALIDATION_OUTCOME]:!0,values:"value"in i?i.value:void 0};const u={};for(const s of c){const o=r(s);if(o.length)n(u,o,e(s));else{const r=u[t.FORM_ERROR]??=[];Array.isArray(r)&&r.push(e(s))}}return{[t.VALIDATION_OUTCOME]:!0,errors:o(u)||{}}}};
2
+ //# sourceMappingURL=standardSchema-DINHlsYR.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standardSchema-DINHlsYR.cjs.js","sources":["../src/standardSchema.ts"],"sourcesContent":["/** Standard Schema v1 (standardschema.dev) contract and adapters, shared\n * by the resolver entry and direct-schema support. The types are a\n * structural copy of the spec — no dependency on any schema library, so\n * zod/valibot/arktype and every v1 implementer work without importing\n * them. */\nimport {FORM_ERROR, VALIDATION_OUTCOME} from './core/errors';\nimport type {FieldError, ValidationOutcome, Validator} from './form';\n\n/** A single Standard Schema issue. */\nexport type StandardSchemaIssue = {\n readonly message: string;\n readonly path?:\n ReadonlyArray<PropertyKey | {readonly key: PropertyKey}> | undefined;\n};\n\n/** The union Standard Schema's `validate` resolves to. */\nexport type StandardSchemaResult<Output = unknown> =\n | {readonly value: Output; readonly issues?: undefined}\n | {readonly issues: ReadonlyArray<StandardSchemaIssue>};\n\n/** Structural Standard Schema v1. `types` (optional in the spec) is what\n * {@link InferSchemaValues} and `createForm`'s inference read. */\nexport type StandardSchemaV1<Input = unknown, Output = Input> = {\n readonly '~standard': {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (\n value: Input\n ) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;\n readonly types?: {\n readonly input: Input;\n readonly output: Output;\n };\n };\n};\n\n/** The values type a Standard Schema produces: the `Output` of its\n * `~standard.types` (coercions/transforms included). `never` when the\n * schema lacks `types` — pass an explicit `TValues`. */\nexport type InferSchemaValues<S> = S extends {\n readonly '~standard': {readonly types: {readonly output: infer Out}};\n}\n ? Out\n : never;\n\n/** Implements Standard Schema v1 props? A boolean predicate, not a type\n * guard — narrowing the union through a guard fails either way, so call\n * sites cast after the check. */\nexport function hasStandardProps(schema: any): boolean {\n return (\n !!schema &&\n typeof schema === 'object' &&\n typeof schema['~standard']?.validate === 'function'\n );\n}\n\n/** Issue → stored error entry. */\nfunction toFieldError(issue: StandardSchemaIssue | undefined): FieldError {\n return {type: 'standard', message: issue?.message || 'Validation failed'};\n}\n\n/** Field-level adapter: validate one value with a schema and map every\n * issue to a FieldError (all issues surface; readers see the first). The\n * parsed output is validation-only — field validators never rewrite. */\nexport function schemaToFieldValidator(\n schema: StandardSchemaV1<any, any>\n): Validator {\n return async (value: any) => {\n const result = await schema['~standard'].validate(value);\n if (!result.issues?.length) return undefined;\n return result.issues.map(toFieldError);\n };\n}\n\n/** Form-level adapter: validate the whole values object and return a\n * ValidationOutcome. Failure nests errors ({a: {b: FieldError[]}});\n * pathless issues land on FORM_ERROR. Success exposes the schema's parsed\n * output as the parsedValues baseline. `createForm({validate: schema})`\n * wraps through this automatically. */\nexport function schemaToFormValidator<T extends Record<string, any>>(\n schema: StandardSchemaV1<any, T>\n): (values: T) => Promise<ValidationOutcome<T>> {\n return async (values: T) => {\n const result = await schema['~standard'].validate(values);\n const {issues} = result;\n if (!issues?.length) {\n // `in` keeps the union narrowed: the success variant carries `value`.\n return {\n [VALIDATION_OUTCOME]: true,\n values: 'value' in result ? result.value : undefined\n };\n }\n const errors: Record<string, any> = {};\n for (const issue of issues) {\n const segments = toPathSegments(issue);\n if (segments.length) {\n assignAtPath(errors, segments, toFieldError(issue));\n } else {\n // Pathless issues accumulate on FORM_ERROR instead of shadowing each other.\n const slot = (errors[FORM_ERROR] ??= []);\n if (Array.isArray(slot)) slot.push(toFieldError(issue));\n }\n }\n return {[VALIDATION_OUTCOME]: true, errors: pruneEmpty(errors) || {}};\n };\n}\n\n/** Stringify an issue path: PropertyKey or {key} segments → strings. */\nfunction toPathSegments(issue: StandardSchemaIssue): string[] {\n return (issue.path || []).map(segment =>\n String(\n typeof segment === 'object' && segment !== null\n ? (segment as {key: PropertyKey}).key\n : segment\n )\n );\n}\n\n/** Append an error at a nested path: leaves are FieldError[] (issues\n * accumulate in order); a path conflicting with a leaf is skipped. */\nfunction assignAtPath(\n root: Record<string, any>,\n segments: string[],\n error: FieldError\n): void {\n let node = root;\n for (let i = 0; i < segments.length - 1; i++) {\n const segment = segments[i];\n if (node[segment] === undefined) node[segment] = {};\n const next = node[segment];\n if (!isBranch(next)) return;\n node = next;\n }\n const leaf = segments[segments.length - 1];\n const slot = node[leaf];\n if (slot === undefined) node[leaf] = [error];\n else if (Array.isArray(slot)) slot.push(error);\n}\n\n/** A branch is a plain container built while nesting; its leaves are the FieldError[] arrays. */\nfunction isBranch(value: any): value is Record<string, any> {\n return !!value && typeof value === 'object' && !Array.isArray(value);\n}\n\n/** Drop empty branch objects left behind by conflicting issue paths. */\nfunction pruneEmpty(\n node: Record<string, any>\n): Record<string, any> | undefined {\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(node)) {\n if (isBranch(value)) {\n const pruned = pruneEmpty(value);\n if (pruned) result[key] = pruned;\n } else {\n result[key] = value;\n }\n }\n return Object.keys(result).length ? result : undefined;\n}\n"],"names":["toFieldError","issue","type","message","toPathSegments","path","map","segment","String","key","assignAtPath","root","segments","error","node","i","length","next","isBranch","leaf","slot","Array","isArray","push","value","pruneEmpty","result","Object","entries","pruned","keys","schema","validate","async","issues","values","VALIDATION_OUTCOME","errors","FORM_ERROR"],"mappings":"uDAyDA,SAASA,EAAaC,GACpB,MAAO,CAACC,KAAM,WAAYC,QAASF,GAAOE,SAAW,oBACvD,CAiDA,SAASC,EAAeH,GACtB,OAAQA,EAAMI,MAAQ,IAAIC,IAAIC,GAC5BC,OACqB,iBAAZD,GAAoC,OAAZA,EAC1BA,EAA+BE,IAChCF,GAGV,CAIA,SAASG,EACPC,EACAC,EACAC,GAEA,IAAIC,EAAOH,EACX,IAAA,IAASI,EAAI,EAAGA,EAAIH,EAASI,OAAS,EAAGD,IAAK,CAC5C,MAAMR,EAAUK,EAASG,QACH,IAAlBD,EAAKP,KAAwBO,EAAKP,GAAW,CAAA,GACjD,MAAMU,EAAOH,EAAKP,GAClB,IAAKW,EAASD,GAAO,OACrBH,EAAOG,CACT,CACA,MAAME,EAAOP,EAASA,EAASI,OAAS,GAClCI,EAAON,EAAKK,QACL,IAATC,EAAoBN,EAAKK,GAAQ,CAACN,GAC7BQ,MAAMC,QAAQF,IAAOA,EAAKG,KAAKV,EAC1C,CAGA,SAASK,EAASM,GAChB,QAASA,GAA0B,iBAAVA,IAAuBH,MAAMC,QAAQE,EAChE,CAGA,SAASC,EACPX,GAEA,MAAMY,EAA8B,CAAA,EACpC,IAAA,MAAYjB,EAAKe,KAAUG,OAAOC,QAAQd,GACxC,GAAII,EAASM,GAAQ,CACnB,MAAMK,EAASJ,EAAWD,GACtBK,IAAQH,EAAOjB,GAAOoB,EAC5B,MACEH,EAAOjB,GAAOe,EAGlB,OAAOG,OAAOG,KAAKJ,GAAQV,OAASU,OAAS,CAC/C,0BA9GO,SAA0BK,GAC/B,QACIA,GACgB,iBAAXA,GACkC,mBAAlCA,EAAO,cAAcC,QAEhC,iCAUO,SACLD,GAEA,OAAOE,MAAOT,IACZ,MAAME,QAAeK,EAAO,aAAaC,SAASR,GAClD,GAAKE,EAAOQ,QAAQlB,OACpB,OAAOU,EAAOQ,OAAO5B,IAAIN,GAE7B,gCAOO,SACL+B,GAEA,OAAOE,MAAOE,IACZ,MAAMT,QAAeK,EAAO,aAAaC,SAASG,IAC5CD,OAACA,GAAUR,EACjB,IAAKQ,GAAQlB,OAEX,MAAO,CACL,CAACoB,EAAAA,qBAAqB,EACtBD,OAAQ,UAAWT,EAASA,EAAOF,WAAQ,GAG/C,MAAMa,EAA8B,CAAA,EACpC,IAAA,MAAWpC,KAASiC,EAAQ,CAC1B,MAAMtB,EAAWR,EAAeH,GAChC,GAAIW,EAASI,OACXN,EAAa2B,EAAQzB,EAAUZ,EAAaC,QACvC,CAEL,MAAMmB,EAAQiB,EAAOC,EAAAA,cAAgB,GACjCjB,MAAMC,QAAQF,MAAYG,KAAKvB,EAAaC,GAClD,CACF,CACA,MAAO,CAAC,CAACmC,EAAAA,qBAAqB,EAAMC,OAAQZ,EAAWY,IAAW,IAEtE"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("@for-fun/event-emitter"),t=require("./errors-TzyWwBfw.cjs.js"),r=require("./values-BbPnLByD.cjs.js");function n(e){if(void 0===e)return null;const t=Array.isArray(e)?e:[e];return t.length?t:null}function o(e,r,n=[],i){Object.entries(r).forEach(([r,l])=>{const s=[...n,...t.isIndex(r)?[r]:t.normalizePath(r)];"string"==typeof l?l&&a(e,s,l,i):Array.isArray(l)||t.isFieldError(l)?a(e,s,l,i):l&&"object"==typeof l&&o(e,l,s,i)})}function a(e,r,n,o){t.setError(e,r,n),function(e,r,n){if(!n)return;const o=t.create(r),a=e.errors.get(o.key);a&&n.set(o.key,a)}(e,r,o)}function i(r,n){const a=r.validateDeps||"onSubmit"!==r.validateMode?function(e){return t.getOrCreate(l,e,()=>new Map)}(r):void 0;if(a&&(!function(r,n){for(const[o,a]of n){r.errors.get(o)===a&&(r.errors.delete(o),t.bumpErrorsVersion(r),e.emit(r.emitter,"errors",t.create(t.segmentsFromKey(o))))}}(r,a),a.clear()),n){if("object"==typeof n&&t.VALIDATION_OUTCOME in n){const e=n;return e.errors&&o(r,e.errors,[],a),void t.setParsedValues(r,e.values)}o(r,n,[],a)}}const l=new WeakMap;const s="__form_validate__";const u=Symbol("form-validate-settled"),c=new WeakMap;function d(n){const o=n.validate;if(!o)return Promise.resolve();const a=n.validateDebounce??0;if(a<=0){const e=new AbortController;return Promise.resolve(o(r.getValues(n),{form:n,signal:e.signal})).then(e=>{i(n,e)})}const l=function(e){return t.getOrCreate(c,e,()=>({timer:null,controller:null,round:null,marked:!1,waiters:[]}))}(n);return null!==l.timer?clearTimeout(l.timer):(l.marked=!0,n.validating.add(s),e.emit(n.emitter,"validating")),l.timer=setTimeout(()=>{l.timer=null;const e=l.round={};(function(e,t,n){const o=e.validate;if(!o)return Promise.resolve();t.controller?.abort();const a=t.controller=new AbortController,l=new Promise(t=>t(o(r.getValues(e),{form:e,signal:a.signal})));return l.then(r=>{t.round===n&&i(e,r)},e=>{if(t.round===n)throw e})})(n,l,e).then(()=>f(n,l,e,u),t=>f(n,l,e,t))},a),new Promise((e,t)=>{l.waiters.push({resolve:e,reject:t})})}function f(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(s),e.emit(t.emitter,"validating"));const a=r.waiters;r.waiters=[];for(const e of a)o===u?e.resolve():e.reject(o)}function m(e,t,r,n,o){return e===t||"all"===e||"onTouched"===e&&r||o===t&&n()}const v=new WeakMap;exports.registerValidatorByPath=function(o,a,i){let l=null,s=null,u=!1,c=null,d=null;const f=()=>{u||(u=!0,function({emitter:t,validating:r},n){r.add(n.key),e.emit(t,"validating",n)}(o,a))},m=()=>{u&&(u=!1,function({emitter:t,validating:r},n){r.delete(n.key),e.emit(t,"validating",n)}(o,a))},v=e=>{if(i.asyncAlways?.()){const l=(()=>{const e=i.sync();return e?n(e(r.getValueByPath(o,a),{form:o,path:a})):null})(),s=n(e)??[];t.setErrorByPath(o,a,[...l??[],...s]),d=s.length?"validator":l?"sync":null}else t.setErrorByPath(o,a,e),d=n(e)?"validator":null},g=()=>{null!==l&&(clearTimeout(l),l=null),s?.abort(),c={},m()},h=()=>{const e=i.validate();if(!e)return void m();s?.abort(),s=new AbortController;const n=c={};let l;try{l=e(r.getValueByPath(o,a),{form:o,path:a,signal:s.signal})}catch(e){throw m(),e}if(!t.isPromise(l))return v(l),void m();f(),l.then(e=>{c===n&&v(e)}).catch(()=>{}).finally(()=>{c===n&&(m(),c=null)})},y=()=>!(!(()=>{const e=i.sync();if(!e)return!1;const l=n(e(r.getValueByPath(o,a),{form:o,path:a}));return l?(t.setErrorByPath(o,a,l),d="sync",!0):(i.validate()&&"sync"!==d||(t.setErrorByPath(o,a,void 0),d=null),!1)})()||i.asyncAlways?.())&&(g(),!0),p=()=>{l=null,y()||h()};return o.validators.set(a.key,()=>{if(y())return;if(!i.validate())return;const e=i.debounce();if(e>0)return null!==l?clearTimeout(l):f(),void(l=setTimeout(p,e));h()}),()=>{o.validators.delete(a.key),g()}},exports.revalidateDependentsOnChange=function(e,t,n){const o=v.get(e)?.get(t.key);if(o?.size)for(const a of o)a!==t.key&&m(n,"onChange",r.hasTouchedByPath(e,t),()=>e.errors.has(a),e.reValidateMode)&&e.validators.get(a)?.()},exports.revalidateFormOnChange=function(e,t,n){e.validateDeps?.has(t.key)&&e.validate&&m(n,"onChange",r.hasTouchedByPath(e,t),()=>function(e){const t=l.get(e);if(!t)return!1;for(const[r,n]of t)if(e.errors.get(r)===n)return!0;return!1}(e),e.reValidateMode)&&d(e).catch(()=>{})},exports.runFormValidate=d,exports.shouldKick=m,exports.trigger=async function(e,r,n){const o=r=>t.waitUntil(e.emitter,"validating",()=>function(e){for(const t of e.validating)if(t!==s)return!1;return!0}(e),()=>!1);return e.validators.forEach(e=>e()),await o(),e.validate&&await d(e),!t.hasErrors(e)};
2
+ //# sourceMappingURL=validate-2pX-N1O6.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-2pX-N1O6.cjs.js","sources":["../src/core/validate.ts"],"sourcesContent":["import {emit} from '../emitter';\nimport createPath, {segmentsFromKey} from '../path';\nimport type {Name, Path, PathSegments} from '../path';\nimport {isIndex, isPromise, normalizePath, waitUntil} from '../util';\nimport type {\n FieldError,\n FieldErrorEntry,\n Form,\n ReValidateMode,\n ValidationMode,\n ValidateResult,\n ValidationOutcome\n} from '../form';\nimport {\n VALIDATION_OUTCOME,\n ValidatorOutput,\n getErrors,\n getFirstError,\n hasErrors,\n setError,\n setErrorByPath\n} from './errors';\nimport {hasTouchedByPath, setTouchedByPath} from './touched';\nimport {getValueByPath, getValues} from './values';\nimport {\n bumpErrorsVersion,\n getOrCreate,\n isFieldError,\n isSegmentsPath,\n setParsedValues\n} from './internals';\n\ntype ErrorFootprint = Map<string, FieldError[]>;\n\n/** The meta bag passed to a validator's second argument. */\ntype ValidatorMeta = {form: Form; path: Path};\n\n/** A pending debounce-window handle, Node or DOM flavored. */\ntype Timer = ReturnType<typeof setTimeout> | null;\n\nexport function unsetValidatingByPath(\n {emitter, validating}: Form,\n path: Path\n): void {\n validating.delete(path.key);\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/** Field validator: an error (string, FieldError, or an array mixing\n * both) or undefined — synchronously or as a Promise. `meta.signal`\n * aborts when the round is superseded, so async work can be cancelled;\n * stale results are dropped by the round lock either way. Older\n * two-argument signatures keep working. */\nexport type Validator = (\n value: any,\n meta: ValidatorMeta & {signal: AbortSignal}\n) => ValidatorOutput | undefined | Promise<ValidatorOutput | undefined>;\n\n/** Synchronous pre-validator (declarative `required` gates in practice):\n * runs on every kick, never debounced; its errors land immediately and\n * short-circuit the debounced validator while present. No Promise, no\n * `signal` — nothing to abort. */\nexport type SyncValidator = (\n value: any,\n meta: ValidatorMeta\n) => ValidatorOutput | undefined;\n\n/** Live options for {@link registerValidatorByPath}: accessors, read at\n * every kick, so `useValidate` can swap them per render without\n * re-subscribing mid-flight. */\nexport type ValidatorRegistration = {\n validate: () => Validator | undefined;\n /** Debounce delay in milliseconds; 0 (default) runs immediately. */\n debounce: () => number;\n /** Runs on every kick, never debounced. */\n sync: () => SyncValidator | undefined;\n /** Keep the debounced validator running when the sync gate failed\n * (TanStack `asyncAlways`): gate errors land immediately, the\n * validator's result lands alongside them per-source. Absent = false\n * (gate owns the outcome). */\n asyncAlways?: () => boolean;\n};\n\n/** Normalize a validator verdict into its non-empty error list; null\n * when the verdict is clean. Field-error coercion stays with\n * setErrorByPath — this only answers \"is there anything to land\". */\nfunction asErrorList(\n output: ValidatorOutput | undefined\n): (string | FieldError)[] | null {\n if (output === undefined) return null;\n const list = Array.isArray(output) ? output : [output];\n return list.length ? list : null;\n}\n\n/**\n * Register a field validator's kick at `path` ({@link Form.validators})\n * — the framework-free machinery behind `useValidate`. Returns a\n * disposer that drops the registration and cancels pending work.\n *\n * Contract (what `trigger`/`ensureValidate`/the user-change gate rely\n * on): the `sync` gate runs on every kick, never debounced, and\n * short-circuits the debounced validator while failing — unless\n * `asyncAlways`, which lands the validator's result alongside the\n * gate's (per-source); a positive `debounce` merges kicks, and the\n * field counts as validating while the window is pending; async results\n * land under a round lock — superseded outcomes drop, only the owning\n * round releases the mark; a synchronous throw propagates. Registering\n * at an occupied path replaces the entry (last-wins).\n */\nexport function registerValidatorByPath(\n form: Form,\n path: Path,\n registration: ValidatorRegistration\n): () => void {\n let timer: Timer = null;\n let controller: AbortController | null = null;\n let marked = false;\n let lock: object | null = null;\n // Which source wrote the error on display — so a passing sync check\n // clears its own stale error immediately. External writers (setError,\n // form validate) are invisible here; clearing them on a pass is the\n // long-standing \"validator owns its whole key\" contract.\n let errorSource: 'sync' | 'validator' | null = null;\n const mark = () => {\n if (marked) return;\n marked = true;\n setValidatingByPath(form, path);\n };\n const unmark = () => {\n if (!marked) return;\n marked = false;\n unsetValidatingByPath(form, path);\n };\n\n /** Run the sync gate (never debounced). Errors land immediately; a\n * passing gate clears its own stale error — or any error when no\n * debounced validator exists to own the round. */\n const runSync = (): boolean => {\n const sync = registration.sync();\n if (!sync) return false;\n const gate = asErrorList(sync(getValueByPath(form, path), {form, path}));\n if (!gate) {\n if (!registration.validate() || errorSource === 'sync') {\n setErrorByPath(form, path, undefined);\n errorSource = null;\n }\n return false;\n }\n setErrorByPath(form, path, gate);\n errorSource = 'sync';\n return true;\n };\n\n /** Re-run the sync gate read-only: asyncAlways landings merge its\n * verdict with the validator's result (per-source). */\n const collectSyncErrors = (): (string | FieldError)[] | null => {\n const sync = registration.sync();\n if (!sync) return null;\n return asErrorList(sync(getValueByPath(form, path), {form, path}));\n };\n\n /** Land a validator result. asyncAlways merges the re-collected gate\n * verdict ahead of the validator's errors; the default path keeps the\n * historical \"validator owns the whole key\" write. */\n const land = (result: ValidatorOutput | undefined): void => {\n if (registration.asyncAlways?.()) {\n const gate = collectSyncErrors();\n const own = asErrorList(result) ?? [];\n setErrorByPath(form, path, [...(gate ?? []), ...own]);\n errorSource = own.length ? 'validator' : gate ? 'sync' : null;\n } else {\n setErrorByPath(form, path, result);\n errorSource = asErrorList(result) ? 'validator' : null;\n }\n };\n\n /** Terminal cancel: drop the pending window and in-flight round,\n * release the mark — one invariant, so no caller strands the mark (a\n * stranded mark hangs settle-waiters). Starting a NEW round\n * (runValidator) refreshes the lock inline instead: an unmark/remark\n * there would expose a false settled state to waiters. */\n const cancel = () => {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n controller?.abort();\n lock = {};\n unmark();\n };\n\n const runValidator = () => {\n const fn = registration.validate();\n if (!fn) {\n unmark();\n return;\n }\n // The lock refresh below drops results that ignore the abort.\n controller?.abort();\n controller = new AbortController();\n const round = (lock = {});\n let result;\n try {\n result = fn(getValueByPath(form, path), {\n form,\n path,\n signal: controller.signal\n });\n } catch (e) {\n // A throwing sync validator propagates to the caller as it always\n // has; just don't leave the validating mark stuck behind it.\n unmark();\n throw e;\n }\n if (!isPromise(result)) {\n land(result);\n // Error first, then release the mark: 'validating' subscribers\n // (trigger) re-read state on wake and must see the landed error.\n unmark();\n return;\n }\n mark();\n result\n .then((error: ValidatorOutput | undefined) => {\n if (lock === round) {\n land(error);\n }\n })\n // Rejection is how aborted validators give up (AbortError); the\n // owning round writes the outcome.\n .catch(() => {})\n .finally(() => {\n if (lock === round) {\n unmark();\n lock = null;\n }\n });\n };\n\n /** Run the sync gate; when it fails without asyncAlways, the gate owns\n * the outcome — supersede pending work, release the mark. */\n const gateOwnsKick = (): boolean => {\n if (!(runSync() && !registration.asyncAlways?.())) return false;\n cancel();\n return true;\n };\n\n /** Window fired: re-run the sync gate first — programmatic writes do\n * not kick validators, so the value may have drifted. */\n const run = () => {\n timer = null;\n if (gateOwnsKick()) return;\n runValidator();\n };\n\n const kick = () => {\n if (gateOwnsKick()) return;\n if (!registration.validate()) return;\n const debounce = registration.debounce();\n if (debounce > 0) {\n // The mark keeps trigger/ensureValidate waiting through the\n // pending timer, not just in-flight promises.\n if (timer !== null) clearTimeout(timer);\n else mark();\n timer = setTimeout(run, debounce);\n return;\n }\n runValidator();\n };\n\n form.validators.set(path.key, kick);\n return () => {\n form.validators.delete(path.key);\n cancel();\n };\n}\n\n/** Options accepted by {@link trigger}. `shouldTouch` defaults to `false`;\n * omitting the options object entirely keeps the plain validate-only\n * behavior, so the historical two-argument calls are untouched. */\nexport type TriggerOptions = {\n /** Mark the triggered scope touched once the round settles, pass or\n * fail (RHF trigger `shouldTouch`). Defaults to `false`. */\n shouldTouch?: boolean;\n /** Focus the first errored field in the triggered scope once the round\n * settles (RHF trigger `shouldFocus`). Rides the 'focusError' channel\n * like failed-submit auto-focus; unmounted paths are silent no-ops.\n * Defaults to `false`. */\n shouldFocus?: boolean;\n};\n\n/**\n * Trigger validation. Without `name`: every registered field validator,\n * then the form-level `validate` (after fields settle, same pipeline as\n * {@link ensureValidate}). One name runs that field only; a name array\n * runs each in order; `[]` is a no-op. A segments array mixes in numbers\n * (`['items', 0]`); pure string arrays are name lists (`['a', 'b']`\n * triggers fields `a` and `b`, not the nested path).\n *\n * The promise waits for the round to settle (async validators included)\n * and never rejects — it resolves whether the triggered scope is\n * error-free. With `name`, only those fields' errors count and\n * form-level `validate` is skipped (RHF semantics). Fire-and-forget\n * callers may ignore it: the kicks still run synchronously.\n */\nexport async function trigger(\n form: Form,\n name?: Name | Name[],\n options?: TriggerOptions\n): Promise<boolean> {\n // Never reject: an error landing is a normal outcome. Without a name\n // the wait is deliberately conservative — every field validator,\n // unrelated in-flight ones included, because the round covers the\n // whole form (the form-level validate's own window is excluded via\n // fieldsSettled — callers wait that out through the kick's promise).\n // With a name the wait narrows to the triggered keys: a slow async\n // validator on field B must not hold trigger('a') hostage.\n const settle = (keys?: string[]) =>\n waitUntil(\n form.emitter,\n 'validating',\n () =>\n keys === undefined\n ? fieldsSettled(form)\n : keys.every(key => !form.validating.has(key)),\n () => false\n );\n // First error wins, like failed-submit auto-focus; a form-level error\n // lands first but has no element — a silent no-op like every unbound\n // path.\n const focusFirstError = (keys?: string[]) => {\n const firstKey =\n keys === undefined\n ? form.errors.keys().next().value\n : keys.find(key => form.errors.has(key));\n if (firstKey !== undefined) emit(form.emitter, 'focusError', firstKey);\n };\n\n if (name === undefined) {\n form.validators.forEach(validator => validator());\n await settle();\n if (form.validate) await runFormValidate(form);\n if (options?.shouldTouch) touchKeys(form, [...form.validators.keys()]);\n if (options?.shouldFocus) focusFirstError();\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(keys);\n if (options?.shouldTouch) touchKeys(form, keys);\n if (options?.shouldFocus) focusFirstError(keys);\n return keys.every(key => !form.errors.has(key));\n}\n\n/** trigger's shouldTouch marking: setTouchedByPath no-ops on\n * already-touched keys and emits the path-payload 'touched' event per\n * newly touched one. */\nfunction touchKeys(form: Form, keys: string[]): void {\n keys.forEach(key => setTouchedByPath(form, createPath(segmentsFromKey(key))));\n}\n\n/** Flatten a form-level validate result into field errors: nested objects\n * descend ({a: {b: 'msg'}} → the 'a.b' error), arrays contribute every\n * non-empty string (zod formErrors style), FieldError objects are stored\n * as-is, falsy values skipped. With `footprint` (validateDeps / live\n * validateMode forms) each stored leaf is recorded — the exact stored\n * array — so the next round can drop exactly what this one wrote. */\nfunction setFormErrors(\n form: Form,\n result: Record<string, any>,\n segments: PathSegments = [],\n footprint?: ErrorFootprint\n): void {\n Object.entries(result).forEach(([key, value]) => {\n // Numeric keys are explicit object keys, not path expressions: '0'\n // (Standard Schema issue paths stringify array indices) stays a\n // literal string segment instead of feeding the path parser.\n const path: PathSegments = [\n ...segments,\n ...(isIndex(key) ? [key] : normalizePath(key))\n ];\n if (typeof value === 'string') {\n if (value) landLeafError(form, path, value, footprint);\n } else if (Array.isArray(value) || isFieldError(value)) {\n landLeafError(form, path, value, footprint);\n } else if (value && typeof value === 'object') {\n setFormErrors(form, value, path, footprint);\n }\n });\n}\n\n/** Record one leaf write into the round's footprint: the path key and\n * the exact stored array (read back — setErrorByPath owns\n * normalization; all-empty writes record nothing). */\nfunction recordFootprint(\n form: Form,\n segments: PathSegments,\n footprint: ErrorFootprint | undefined\n): void {\n if (!footprint) return;\n const path = createPath(segments);\n const stored = form.errors.get(path.key);\n if (stored) footprint.set(path.key, stored);\n}\n\nfunction landLeafError(\n form: Form,\n path: PathSegments,\n error: ValidatorOutput,\n footprint?: ErrorFootprint\n): void {\n setError(form, path, error);\n recordFootprint(form, path, footprint);\n}\n\n/** Land a form-level validate result. A plain record flattens into field\n * errors; a branded {@link ValidationOutcome} also sets `values` as the\n * parsedValues baseline. Forms re-running the validate on user input\n * (validateDeps or a live validateMode) get round-scoped ownership: the\n * previous round's errors are dropped first, so a passing re-run clears\n * its cross-field error; submit-only forms keep the write-only\n * behavior. */\nfunction applyValidateResult(\n form: Form,\n result: ValidateResult<any> | undefined\n): void {\n const footprint =\n form.validateDeps || form.validateMode !== 'onSubmit'\n ? getFormErrorFootprint(form)\n : undefined;\n if (footprint) {\n clearFormValidateErrors(form, footprint);\n footprint.clear();\n }\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, [], footprint);\n setParsedValues(form, outcome.values);\n return;\n }\n setFormErrors(form, result as Record<string, any>, [], footprint);\n}\n\n/** Per-form footprint of the last form-level round: path key → the exact\n * stored array. WeakMap — the Form shape stays untouched. */\nconst formErrorFootprints = new WeakMap<Form, ErrorFootprint>();\n\nfunction getFormErrorFootprint(form: Form): ErrorFootprint {\n return getOrCreate(formErrorFootprints, form, () => new Map());\n}\n\n/** Does the form still show an error the last round wrote? Identity\n * comparison: once a field validator, setServerErrors, setError or\n * clearErrors replaces the stored array, the error is no longer the\n * round's to own. */\nfunction hasFormValidateErrors(form: Form): boolean {\n const footprint = formErrorFootprints.get(form);\n if (!footprint) return false;\n for (const [key, written] of footprint) {\n if (form.errors.get(key) === written) return true;\n }\n return false;\n}\n\n/** Drop the last round's errors before the next lands, per key\n * identity-checked (overwritten/cleared errors survive); each drop emits\n * the same path-payload 'errors' event as setErrorByPath. */\nfunction clearFormValidateErrors(form: Form, footprint: ErrorFootprint): void {\n for (const [key, written] of footprint) {\n const stored = form.errors.get(key);\n if (stored !== written) continue;\n form.errors.delete(key);\n bumpErrorsVersion(form);\n emit(form.emitter, 'errors', createPath(segmentsFromKey(key)));\n }\n}\n\n/** Reserved validating key for the form-level round; real path keys are\n * JSON arrays, so a bare word can never collide. */\nconst FORM_VALIDATING_KEY = '__form_validate__';\n\n/** Are all FIELD rounds drained? The form validate's own reserved key is\n * excluded — its window is waited out through the kick's promise, so a\n * pending form window never gates the next kick (a newer round\n * supersedes, mirroring per-field debounce). */\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\ntype FormValidateState = {\n timer: Timer;\n controller: AbortController | null;\n /** Identity of the in-flight round; superseded outcomes (rejections\n * included) are dropped by comparing against it. */\n round: object | null;\n marked: boolean;\n waiters: Array<{resolve: () => void; reject: (error: unknown) => void}>;\n};\n\n/** Per-form debounced-validate bookkeeping — WeakMap, so the Form shape\n * stays untouched for forms that never set `validateDebounce`. */\nconst formValidateStates = new WeakMap<Form, FormValidateState>();\n\nfunction getFormValidateState(form: Form): FormValidateState {\n return getOrCreate(formValidateStates, form, () => ({\n timer: null,\n controller: null,\n round: null,\n marked: false,\n waiters: []\n }));\n}\n\n/**\n * Run the form-level `validate` and land its result, honoring\n * `validateDebounce`. Undebounced: the caller's await is the call — no\n * mark, no gating, rejection propagates. Debounced: kicks merge into a\n * window (the form counts as validating), the round reads then-current\n * values and supersedes in-flight rounds, and every merged caller's\n * promise settles with the final round's outcome. Form-level-only round:\n * `useForm`'s validateOnMount and cross-cutting re-checks; `trigger` /\n * `ensureValidate` compose it after fields settle.\n */\nexport function 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 // Argument-shape parity with the debounced rounds; the signal never\n // fires (nothing supersedes an undebounced call).\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: pending kicks restart the timer; an in-flight\n // round keeps the mark held until 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/** One round with the current values; aborts and drops the superseded\n * round, field-lock style. */\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 // The Promise constructor turns a synchronous validator throw into a\n // rejection — the try/catch comes for free.\n const outcome = new Promise<any>(resolve =>\n resolve(validate(getValues(form), {form, signal: controller.signal}))\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: release the mark (after the round's errors\n * landed — 'validating' subscribers re-read state on wake), settle every\n * merged waiter. A superseded round never lands here; a re-opened window\n * carries the mark and waiters over to the pending 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/** The mode/reValidateMode gate shared by change/blur kicks and both\n * validateDeps re-runs: fires for `cadence` and `'all'`, for\n * `'onTouched'` once touched, else when `reValidateMode` matches\n * `cadence` and an error is still live. `hasError` reads lazily. */\nexport function shouldKick(\n mode: ValidationMode,\n cadence: 'onChange' | 'onBlur',\n touched: boolean,\n hasError: () => boolean,\n reValidateMode: ReValidateMode\n): boolean {\n return (\n mode === cadence ||\n mode === 'all' ||\n (mode === 'onTouched' && touched) ||\n (reValidateMode === cadence && hasError())\n );\n}\n\n/**\n * Form-level twin of the gated change kick: re-run `validate` after a\n * user change to a field in `validateDeps`. Rides the changed field's\n * onChange pipeline, so programmatic setValue writes never fire it. The\n * gate uses the changed field's effective `mode` and the form-level\n * `reValidateMode` against the last round's error footprint:\n * onChange/all — every dep change; onTouched — once touched; otherwise\n * while the round's error is still live under reValidateMode onChange\n * (the submit-then-fix flow). Fire-and-forget: async rejections are\n * swallowed, sync throws propagate. A no-op (one property check) unless\n * `validateDeps` lists `path`.\n */\nexport function revalidateFormOnChange(\n form: Form,\n path: Path,\n mode: ValidationMode\n): void {\n if (!form.validateDeps?.has(path.key) || !form.validate) return;\n if (\n shouldKick(\n mode,\n 'onChange',\n hasTouchedByPath(form, path),\n () => hasFormValidateErrors(form),\n form.reValidateMode\n )\n ) {\n runFormValidate(form).catch(() => {});\n }\n}\n\n/** Field-level validateDeps registry: dep key → dependents listing it\n * (WeakMap — Form shape untouched). */\nconst fieldValidateDeps = new WeakMap<Form, Map<string, Set<string>>>();\n\n/** Register one field's deps: `key` re-validates when any path in\n * `depKeys` changes. Idempotent per pair — StrictMode's double effect is\n * harmless. */\nexport function registerFieldValidateDeps(\n form: Form,\n key: string,\n depKeys: string[]\n): void {\n const deps = getOrCreate(fieldValidateDeps, form, () => new Map());\n for (const depKey of depKeys) {\n const dependents = deps.get(depKey) ?? new Set<string>();\n dependents.add(key);\n deps.set(depKey, dependents);\n }\n}\n\n/** Drop one field's deps registration; empty entries are removed so the\n * registry never outlives its fields. */\nexport function unregisterFieldValidateDeps(\n form: Form,\n key: string,\n depKeys: string[]\n): void {\n const deps = fieldValidateDeps.get(form);\n if (!deps) return;\n for (const depKey of depKeys) {\n const dependents = deps.get(depKey);\n if (!dependents?.delete(key)) continue;\n if (!dependents.size) deps.delete(depKey);\n }\n}\n\n/**\n * Field-level twin: after a user change to `path`, re-run every field\n * validator that declared it as a dep. Same channel and gate as\n * {@link revalidateFormOnChange} — the dependent's own error arms\n * reValidateMode, and a passing re-run replaces it (a field validator\n * owns its whole key). Ordinary validator kicks: the dependent's\n * debounce applies, sync throws propagate. A no-op unless some field\n * declared `path` as a dep.\n */\nexport function revalidateDependentsOnChange(\n form: Form,\n path: Path,\n mode: ValidationMode\n): void {\n const dependents = fieldValidateDeps.get(form)?.get(path.key);\n if (!dependents?.size) return;\n for (const dependent of dependents) {\n // A self-dep: the field's own onChange already validated it.\n if (dependent === path.key) continue;\n if (\n shouldKick(\n mode,\n 'onChange',\n hasTouchedByPath(form, path),\n () => form.errors.has(dependent),\n form.reValidateMode\n )\n ) {\n form.validators.get(dependent)?.();\n }\n }\n}\n\n/** {@link ensureValidate}'s rejection: `message` is the first error's\n * display text; `.errors` carries the full flattened list ({@link\n * getErrors}) for branching without re-reading the form. */\nexport type FormValidationError = Error & {errors: FieldErrorEntry[]};\n\nfunction validationError(form: Form): FormValidationError {\n const error = new Error(getFirstError(form)) as FormValidationError;\n error.errors = getErrors(form);\n return error;\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 validationError(form);\n });\n\n if (form.validate) {\n await runFormValidate(form);\n if (hasErrors(form)) throw validationError(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"],"names":["asErrorList","output","list","Array","isArray","length","setFormErrors","form","result","segments","footprint","Object","entries","forEach","key","value","path","isIndex","normalizePath","landLeafError","isFieldError","error","setError","createPath","stored","errors","get","set","recordFootprint","applyValidateResult","validateDeps","validateMode","getOrCreate","formErrorFootprints","Map","getFormErrorFootprint","written","delete","bumpErrorsVersion","emit","emitter","segmentsFromKey","clearFormValidateErrors","clear","VALIDATION_OUTCOME","outcome","setParsedValues","values","WeakMap","FORM_VALIDATING_KEY","SETTLED","formValidateStates","runFormValidate","validate","Promise","resolve","debounce","validateDebounce","controller","AbortController","getValues","signal","then","state","timer","round","marked","waiters","getFormValidateState","clearTimeout","validating","add","setTimeout","abort","runFormValidateRound","settleFormValidate","reject","push","waiter","shouldKick","mode","cadence","touched","hasError","reValidateMode","fieldValidateDeps","registration","lock","errorSource","mark","setValidatingByPath","unmark","unsetValidatingByPath","land","asyncAlways","gate","sync","getValueByPath","collectSyncErrors","own","setErrorByPath","cancel","runValidator","fn","e","isPromise","catch","finally","gateOwnsKick","runSync","run","validators","dependents","size","dependent","hasTouchedByPath","has","hasFormValidateErrors","async","name","options","settle","keys","waitUntil","fieldsSettled","validator","hasErrors"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{emit as t}from"@for-fun/event-emitter";import{h as e,w as n,V as r,s as o,g as a,b as i,c as l,a as s,i as u,n as c,d as f,e as d,f as m,j as v}from"./errors-DA4ReEd9.mjs";import{g,a as y,h}from"./values-CHsmcZk4.mjs";function w(t){if(void 0===t)return null;const e=Array.isArray(t)?t:[t];return e.length?e:null}function p(e,n,r){let o=null,a=null,i=!1,l=null,s=null;const u=()=>{i||(i=!0,function({emitter:e,validating:n},r){n.add(r.key),t(e,"validating",r)}(e,n))},c=()=>{i&&(i=!1,function({emitter:e,validating:n},r){n.delete(r.key),t(e,"validating",r)}(e,n))},f=t=>{if(r.asyncAlways?.()){const o=(()=>{const t=r.sync();return t?w(t(y(e,n),{form:e,path:n})):null})(),a=w(t)??[];v(e,n,[...o??[],...a]),s=a.length?"validator":o?"sync":null}else v(e,n,t),s=w(t)?"validator":null},d=()=>{null!==o&&(clearTimeout(o),o=null),a?.abort(),l={},c()},g=()=>{const t=r.validate();if(!t)return void c();a?.abort(),a=new AbortController;const o=l={};let i;try{i=t(y(e,n),{form:e,path:n,signal:a.signal})}catch(t){throw c(),t}if(!m(i))return f(i),void c();u(),i.then(t=>{l===o&&f(t)}).catch(()=>{}).finally(()=>{l===o&&(c(),l=null)})},h=()=>!(!(()=>{const t=r.sync();if(!t)return!1;const o=w(t(y(e,n),{form:e,path:n}));return o?(v(e,n,o),s="sync",!0):(r.validate()&&"sync"!==s||(v(e,n,void 0),s=null),!1)})()||r.asyncAlways?.())&&(d(),!0),p=()=>{o=null,h()||g()};return e.validators.set(n.key,()=>{if(h())return;if(!r.validate())return;const t=r.debounce();if(t>0)return null!==o?clearTimeout(o):u(),void(o=setTimeout(p,t));g()}),()=>{e.validators.delete(n.key),d()}}async function k(t,r,o){const a=e=>n(t.emitter,"validating",()=>function(t){for(const e of t.validating)if(e!==T)return!1;return!0}(t),()=>!1);return t.validators.forEach(t=>t()),await a(),t.validate&&await _(t),!e(t)}function b(t,e,n=[],r){Object.entries(e).forEach(([e,o])=>{const a=[...n,...u(e)?[e]:c(e)];"string"==typeof o?o&&A(t,a,o,r):Array.isArray(o)||f(o)?A(t,a,o,r):o&&"object"==typeof o&&b(t,o,a,r)})}function A(t,e,n,r){d(t,e,n),function(t,e,n){if(!n)return;const r=l(e),o=t.errors.get(r.key);o&&n.set(r.key,o)}(t,e,r)}function j(e,n){const u=e.validateDeps||"onSubmit"!==e.validateMode?function(t){return a(M,t,()=>new Map)}(e):void 0;if(u&&(!function(e,n){for(const[r,o]of n){e.errors.get(r)===o&&(e.errors.delete(r),i(e),t(e.emitter,"errors",l(s(r))))}}(e,u),u.clear()),n){if("object"==typeof n&&r in n){const t=n;return t.errors&&b(e,t.errors,[],u),void o(e,t.values)}b(e,n,[],u)}}const M=new WeakMap;const T="__form_validate__";const C=Symbol("form-validate-settled"),P=new WeakMap;function _(e){const n=e.validate;if(!n)return Promise.resolve();const r=e.validateDebounce??0;if(r<=0){const t=new AbortController;return Promise.resolve(n(g(e),{form:e,signal:t.signal})).then(t=>{j(e,t)})}const o=function(t){return a(P,t,()=>({timer:null,controller:null,round:null,marked:!1,waiters:[]}))}(e);return null!==o.timer?clearTimeout(o.timer):(o.marked=!0,e.validating.add(T),t(e.emitter,"validating")),o.timer=setTimeout(()=>{o.timer=null;const t=o.round={};(function(t,e,n){const r=t.validate;if(!r)return Promise.resolve();e.controller?.abort();const o=e.controller=new AbortController,a=new Promise(e=>e(r(g(t),{form:t,signal:o.signal})));return a.then(r=>{e.round===n&&j(t,r)},t=>{if(e.round===n)throw t})})(e,o,t).then(()=>D(e,o,t,C),n=>D(e,o,t,n))},r),new Promise((t,e)=>{o.waiters.push({resolve:t,reject:e})})}function D(e,n,r,o){if(n.round!==r)return;if(n.round=null,null!==n.timer)return;n.marked&&(n.marked=!1,e.validating.delete(T),t(e.emitter,"validating"));const a=n.waiters;n.waiters=[];for(const t of a)o===C?t.resolve():t.reject(o)}function V(t,e,n,r,o){return t===e||"all"===t||"onTouched"===t&&n||o===e&&r()}function W(t,e,n){t.validateDeps?.has(e.key)&&t.validate&&V(n,"onChange",h(t,e),()=>function(t){const e=M.get(t);if(!e)return!1;for(const[n,r]of e)if(t.errors.get(n)===r)return!0;return!1}(t),t.reValidateMode)&&_(t).catch(()=>{})}const E=new WeakMap;function S(t,e,n){const r=E.get(t)?.get(e.key);if(r?.size)for(const o of r)o!==e.key&&V(n,"onChange",h(t,e),()=>t.errors.has(o),t.reValidateMode)&&t.validators.get(o)?.()}export{W as a,S as b,p as c,_ as r,V as s,k as t};
2
+ //# sourceMappingURL=validate-C0HOsP9v.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-C0HOsP9v.mjs","sources":["../src/core/validate.ts"],"sourcesContent":["import {emit} from '../emitter';\nimport createPath, {segmentsFromKey} from '../path';\nimport type {Name, Path, PathSegments} from '../path';\nimport {isIndex, isPromise, normalizePath, waitUntil} from '../util';\nimport type {\n FieldError,\n FieldErrorEntry,\n Form,\n ReValidateMode,\n ValidationMode,\n ValidateResult,\n ValidationOutcome\n} from '../form';\nimport {\n VALIDATION_OUTCOME,\n ValidatorOutput,\n getErrors,\n getFirstError,\n hasErrors,\n setError,\n setErrorByPath\n} from './errors';\nimport {hasTouchedByPath, setTouchedByPath} from './touched';\nimport {getValueByPath, getValues} from './values';\nimport {\n bumpErrorsVersion,\n getOrCreate,\n isFieldError,\n isSegmentsPath,\n setParsedValues\n} from './internals';\n\ntype ErrorFootprint = Map<string, FieldError[]>;\n\n/** The meta bag passed to a validator's second argument. */\ntype ValidatorMeta = {form: Form; path: Path};\n\n/** A pending debounce-window handle, Node or DOM flavored. */\ntype Timer = ReturnType<typeof setTimeout> | null;\n\nexport function unsetValidatingByPath(\n {emitter, validating}: Form,\n path: Path\n): void {\n validating.delete(path.key);\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/** Field validator: an error (string, FieldError, or an array mixing\n * both) or undefined — synchronously or as a Promise. `meta.signal`\n * aborts when the round is superseded, so async work can be cancelled;\n * stale results are dropped by the round lock either way. Older\n * two-argument signatures keep working. */\nexport type Validator = (\n value: any,\n meta: ValidatorMeta & {signal: AbortSignal}\n) => ValidatorOutput | undefined | Promise<ValidatorOutput | undefined>;\n\n/** Synchronous pre-validator (declarative `required` gates in practice):\n * runs on every kick, never debounced; its errors land immediately and\n * short-circuit the debounced validator while present. No Promise, no\n * `signal` — nothing to abort. */\nexport type SyncValidator = (\n value: any,\n meta: ValidatorMeta\n) => ValidatorOutput | undefined;\n\n/** Live options for {@link registerValidatorByPath}: accessors, read at\n * every kick, so `useValidate` can swap them per render without\n * re-subscribing mid-flight. */\nexport type ValidatorRegistration = {\n validate: () => Validator | undefined;\n /** Debounce delay in milliseconds; 0 (default) runs immediately. */\n debounce: () => number;\n /** Runs on every kick, never debounced. */\n sync: () => SyncValidator | undefined;\n /** Keep the debounced validator running when the sync gate failed\n * (TanStack `asyncAlways`): gate errors land immediately, the\n * validator's result lands alongside them per-source. Absent = false\n * (gate owns the outcome). */\n asyncAlways?: () => boolean;\n};\n\n/** Normalize a validator verdict into its non-empty error list; null\n * when the verdict is clean. Field-error coercion stays with\n * setErrorByPath — this only answers \"is there anything to land\". */\nfunction asErrorList(\n output: ValidatorOutput | undefined\n): (string | FieldError)[] | null {\n if (output === undefined) return null;\n const list = Array.isArray(output) ? output : [output];\n return list.length ? list : null;\n}\n\n/**\n * Register a field validator's kick at `path` ({@link Form.validators})\n * — the framework-free machinery behind `useValidate`. Returns a\n * disposer that drops the registration and cancels pending work.\n *\n * Contract (what `trigger`/`ensureValidate`/the user-change gate rely\n * on): the `sync` gate runs on every kick, never debounced, and\n * short-circuits the debounced validator while failing — unless\n * `asyncAlways`, which lands the validator's result alongside the\n * gate's (per-source); a positive `debounce` merges kicks, and the\n * field counts as validating while the window is pending; async results\n * land under a round lock — superseded outcomes drop, only the owning\n * round releases the mark; a synchronous throw propagates. Registering\n * at an occupied path replaces the entry (last-wins).\n */\nexport function registerValidatorByPath(\n form: Form,\n path: Path,\n registration: ValidatorRegistration\n): () => void {\n let timer: Timer = null;\n let controller: AbortController | null = null;\n let marked = false;\n let lock: object | null = null;\n // Which source wrote the error on display — so a passing sync check\n // clears its own stale error immediately. External writers (setError,\n // form validate) are invisible here; clearing them on a pass is the\n // long-standing \"validator owns its whole key\" contract.\n let errorSource: 'sync' | 'validator' | null = null;\n const mark = () => {\n if (marked) return;\n marked = true;\n setValidatingByPath(form, path);\n };\n const unmark = () => {\n if (!marked) return;\n marked = false;\n unsetValidatingByPath(form, path);\n };\n\n /** Run the sync gate (never debounced). Errors land immediately; a\n * passing gate clears its own stale error — or any error when no\n * debounced validator exists to own the round. */\n const runSync = (): boolean => {\n const sync = registration.sync();\n if (!sync) return false;\n const gate = asErrorList(sync(getValueByPath(form, path), {form, path}));\n if (!gate) {\n if (!registration.validate() || errorSource === 'sync') {\n setErrorByPath(form, path, undefined);\n errorSource = null;\n }\n return false;\n }\n setErrorByPath(form, path, gate);\n errorSource = 'sync';\n return true;\n };\n\n /** Re-run the sync gate read-only: asyncAlways landings merge its\n * verdict with the validator's result (per-source). */\n const collectSyncErrors = (): (string | FieldError)[] | null => {\n const sync = registration.sync();\n if (!sync) return null;\n return asErrorList(sync(getValueByPath(form, path), {form, path}));\n };\n\n /** Land a validator result. asyncAlways merges the re-collected gate\n * verdict ahead of the validator's errors; the default path keeps the\n * historical \"validator owns the whole key\" write. */\n const land = (result: ValidatorOutput | undefined): void => {\n if (registration.asyncAlways?.()) {\n const gate = collectSyncErrors();\n const own = asErrorList(result) ?? [];\n setErrorByPath(form, path, [...(gate ?? []), ...own]);\n errorSource = own.length ? 'validator' : gate ? 'sync' : null;\n } else {\n setErrorByPath(form, path, result);\n errorSource = asErrorList(result) ? 'validator' : null;\n }\n };\n\n /** Terminal cancel: drop the pending window and in-flight round,\n * release the mark — one invariant, so no caller strands the mark (a\n * stranded mark hangs settle-waiters). Starting a NEW round\n * (runValidator) refreshes the lock inline instead: an unmark/remark\n * there would expose a false settled state to waiters. */\n const cancel = () => {\n if (timer !== null) {\n clearTimeout(timer);\n timer = null;\n }\n controller?.abort();\n lock = {};\n unmark();\n };\n\n const runValidator = () => {\n const fn = registration.validate();\n if (!fn) {\n unmark();\n return;\n }\n // The lock refresh below drops results that ignore the abort.\n controller?.abort();\n controller = new AbortController();\n const round = (lock = {});\n let result;\n try {\n result = fn(getValueByPath(form, path), {\n form,\n path,\n signal: controller.signal\n });\n } catch (e) {\n // A throwing sync validator propagates to the caller as it always\n // has; just don't leave the validating mark stuck behind it.\n unmark();\n throw e;\n }\n if (!isPromise(result)) {\n land(result);\n // Error first, then release the mark: 'validating' subscribers\n // (trigger) re-read state on wake and must see the landed error.\n unmark();\n return;\n }\n mark();\n result\n .then((error: ValidatorOutput | undefined) => {\n if (lock === round) {\n land(error);\n }\n })\n // Rejection is how aborted validators give up (AbortError); the\n // owning round writes the outcome.\n .catch(() => {})\n .finally(() => {\n if (lock === round) {\n unmark();\n lock = null;\n }\n });\n };\n\n /** Run the sync gate; when it fails without asyncAlways, the gate owns\n * the outcome — supersede pending work, release the mark. */\n const gateOwnsKick = (): boolean => {\n if (!(runSync() && !registration.asyncAlways?.())) return false;\n cancel();\n return true;\n };\n\n /** Window fired: re-run the sync gate first — programmatic writes do\n * not kick validators, so the value may have drifted. */\n const run = () => {\n timer = null;\n if (gateOwnsKick()) return;\n runValidator();\n };\n\n const kick = () => {\n if (gateOwnsKick()) return;\n if (!registration.validate()) return;\n const debounce = registration.debounce();\n if (debounce > 0) {\n // The mark keeps trigger/ensureValidate waiting through the\n // pending timer, not just in-flight promises.\n if (timer !== null) clearTimeout(timer);\n else mark();\n timer = setTimeout(run, debounce);\n return;\n }\n runValidator();\n };\n\n form.validators.set(path.key, kick);\n return () => {\n form.validators.delete(path.key);\n cancel();\n };\n}\n\n/** Options accepted by {@link trigger}. `shouldTouch` defaults to `false`;\n * omitting the options object entirely keeps the plain validate-only\n * behavior, so the historical two-argument calls are untouched. */\nexport type TriggerOptions = {\n /** Mark the triggered scope touched once the round settles, pass or\n * fail (RHF trigger `shouldTouch`). Defaults to `false`. */\n shouldTouch?: boolean;\n /** Focus the first errored field in the triggered scope once the round\n * settles (RHF trigger `shouldFocus`). Rides the 'focusError' channel\n * like failed-submit auto-focus; unmounted paths are silent no-ops.\n * Defaults to `false`. */\n shouldFocus?: boolean;\n};\n\n/**\n * Trigger validation. Without `name`: every registered field validator,\n * then the form-level `validate` (after fields settle, same pipeline as\n * {@link ensureValidate}). One name runs that field only; a name array\n * runs each in order; `[]` is a no-op. A segments array mixes in numbers\n * (`['items', 0]`); pure string arrays are name lists (`['a', 'b']`\n * triggers fields `a` and `b`, not the nested path).\n *\n * The promise waits for the round to settle (async validators included)\n * and never rejects — it resolves whether the triggered scope is\n * error-free. With `name`, only those fields' errors count and\n * form-level `validate` is skipped (RHF semantics). Fire-and-forget\n * callers may ignore it: the kicks still run synchronously.\n */\nexport async function trigger(\n form: Form,\n name?: Name | Name[],\n options?: TriggerOptions\n): Promise<boolean> {\n // Never reject: an error landing is a normal outcome. Without a name\n // the wait is deliberately conservative — every field validator,\n // unrelated in-flight ones included, because the round covers the\n // whole form (the form-level validate's own window is excluded via\n // fieldsSettled — callers wait that out through the kick's promise).\n // With a name the wait narrows to the triggered keys: a slow async\n // validator on field B must not hold trigger('a') hostage.\n const settle = (keys?: string[]) =>\n waitUntil(\n form.emitter,\n 'validating',\n () =>\n keys === undefined\n ? fieldsSettled(form)\n : keys.every(key => !form.validating.has(key)),\n () => false\n );\n // First error wins, like failed-submit auto-focus; a form-level error\n // lands first but has no element — a silent no-op like every unbound\n // path.\n const focusFirstError = (keys?: string[]) => {\n const firstKey =\n keys === undefined\n ? form.errors.keys().next().value\n : keys.find(key => form.errors.has(key));\n if (firstKey !== undefined) emit(form.emitter, 'focusError', firstKey);\n };\n\n if (name === undefined) {\n form.validators.forEach(validator => validator());\n await settle();\n if (form.validate) await runFormValidate(form);\n if (options?.shouldTouch) touchKeys(form, [...form.validators.keys()]);\n if (options?.shouldFocus) focusFirstError();\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(keys);\n if (options?.shouldTouch) touchKeys(form, keys);\n if (options?.shouldFocus) focusFirstError(keys);\n return keys.every(key => !form.errors.has(key));\n}\n\n/** trigger's shouldTouch marking: setTouchedByPath no-ops on\n * already-touched keys and emits the path-payload 'touched' event per\n * newly touched one. */\nfunction touchKeys(form: Form, keys: string[]): void {\n keys.forEach(key => setTouchedByPath(form, createPath(segmentsFromKey(key))));\n}\n\n/** Flatten a form-level validate result into field errors: nested objects\n * descend ({a: {b: 'msg'}} → the 'a.b' error), arrays contribute every\n * non-empty string (zod formErrors style), FieldError objects are stored\n * as-is, falsy values skipped. With `footprint` (validateDeps / live\n * validateMode forms) each stored leaf is recorded — the exact stored\n * array — so the next round can drop exactly what this one wrote. */\nfunction setFormErrors(\n form: Form,\n result: Record<string, any>,\n segments: PathSegments = [],\n footprint?: ErrorFootprint\n): void {\n Object.entries(result).forEach(([key, value]) => {\n // Numeric keys are explicit object keys, not path expressions: '0'\n // (Standard Schema issue paths stringify array indices) stays a\n // literal string segment instead of feeding the path parser.\n const path: PathSegments = [\n ...segments,\n ...(isIndex(key) ? [key] : normalizePath(key))\n ];\n if (typeof value === 'string') {\n if (value) landLeafError(form, path, value, footprint);\n } else if (Array.isArray(value) || isFieldError(value)) {\n landLeafError(form, path, value, footprint);\n } else if (value && typeof value === 'object') {\n setFormErrors(form, value, path, footprint);\n }\n });\n}\n\n/** Record one leaf write into the round's footprint: the path key and\n * the exact stored array (read back — setErrorByPath owns\n * normalization; all-empty writes record nothing). */\nfunction recordFootprint(\n form: Form,\n segments: PathSegments,\n footprint: ErrorFootprint | undefined\n): void {\n if (!footprint) return;\n const path = createPath(segments);\n const stored = form.errors.get(path.key);\n if (stored) footprint.set(path.key, stored);\n}\n\nfunction landLeafError(\n form: Form,\n path: PathSegments,\n error: ValidatorOutput,\n footprint?: ErrorFootprint\n): void {\n setError(form, path, error);\n recordFootprint(form, path, footprint);\n}\n\n/** Land a form-level validate result. A plain record flattens into field\n * errors; a branded {@link ValidationOutcome} also sets `values` as the\n * parsedValues baseline. Forms re-running the validate on user input\n * (validateDeps or a live validateMode) get round-scoped ownership: the\n * previous round's errors are dropped first, so a passing re-run clears\n * its cross-field error; submit-only forms keep the write-only\n * behavior. */\nfunction applyValidateResult(\n form: Form,\n result: ValidateResult<any> | undefined\n): void {\n const footprint =\n form.validateDeps || form.validateMode !== 'onSubmit'\n ? getFormErrorFootprint(form)\n : undefined;\n if (footprint) {\n clearFormValidateErrors(form, footprint);\n footprint.clear();\n }\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, [], footprint);\n setParsedValues(form, outcome.values);\n return;\n }\n setFormErrors(form, result as Record<string, any>, [], footprint);\n}\n\n/** Per-form footprint of the last form-level round: path key → the exact\n * stored array. WeakMap — the Form shape stays untouched. */\nconst formErrorFootprints = new WeakMap<Form, ErrorFootprint>();\n\nfunction getFormErrorFootprint(form: Form): ErrorFootprint {\n return getOrCreate(formErrorFootprints, form, () => new Map());\n}\n\n/** Does the form still show an error the last round wrote? Identity\n * comparison: once a field validator, setServerErrors, setError or\n * clearErrors replaces the stored array, the error is no longer the\n * round's to own. */\nfunction hasFormValidateErrors(form: Form): boolean {\n const footprint = formErrorFootprints.get(form);\n if (!footprint) return false;\n for (const [key, written] of footprint) {\n if (form.errors.get(key) === written) return true;\n }\n return false;\n}\n\n/** Drop the last round's errors before the next lands, per key\n * identity-checked (overwritten/cleared errors survive); each drop emits\n * the same path-payload 'errors' event as setErrorByPath. */\nfunction clearFormValidateErrors(form: Form, footprint: ErrorFootprint): void {\n for (const [key, written] of footprint) {\n const stored = form.errors.get(key);\n if (stored !== written) continue;\n form.errors.delete(key);\n bumpErrorsVersion(form);\n emit(form.emitter, 'errors', createPath(segmentsFromKey(key)));\n }\n}\n\n/** Reserved validating key for the form-level round; real path keys are\n * JSON arrays, so a bare word can never collide. */\nconst FORM_VALIDATING_KEY = '__form_validate__';\n\n/** Are all FIELD rounds drained? The form validate's own reserved key is\n * excluded — its window is waited out through the kick's promise, so a\n * pending form window never gates the next kick (a newer round\n * supersedes, mirroring per-field debounce). */\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\ntype FormValidateState = {\n timer: Timer;\n controller: AbortController | null;\n /** Identity of the in-flight round; superseded outcomes (rejections\n * included) are dropped by comparing against it. */\n round: object | null;\n marked: boolean;\n waiters: Array<{resolve: () => void; reject: (error: unknown) => void}>;\n};\n\n/** Per-form debounced-validate bookkeeping — WeakMap, so the Form shape\n * stays untouched for forms that never set `validateDebounce`. */\nconst formValidateStates = new WeakMap<Form, FormValidateState>();\n\nfunction getFormValidateState(form: Form): FormValidateState {\n return getOrCreate(formValidateStates, form, () => ({\n timer: null,\n controller: null,\n round: null,\n marked: false,\n waiters: []\n }));\n}\n\n/**\n * Run the form-level `validate` and land its result, honoring\n * `validateDebounce`. Undebounced: the caller's await is the call — no\n * mark, no gating, rejection propagates. Debounced: kicks merge into a\n * window (the form counts as validating), the round reads then-current\n * values and supersedes in-flight rounds, and every merged caller's\n * promise settles with the final round's outcome. Form-level-only round:\n * `useForm`'s validateOnMount and cross-cutting re-checks; `trigger` /\n * `ensureValidate` compose it after fields settle.\n */\nexport function 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 // Argument-shape parity with the debounced rounds; the signal never\n // fires (nothing supersedes an undebounced call).\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: pending kicks restart the timer; an in-flight\n // round keeps the mark held until 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/** One round with the current values; aborts and drops the superseded\n * round, field-lock style. */\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 // The Promise constructor turns a synchronous validator throw into a\n // rejection — the try/catch comes for free.\n const outcome = new Promise<any>(resolve =>\n resolve(validate(getValues(form), {form, signal: controller.signal}))\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: release the mark (after the round's errors\n * landed — 'validating' subscribers re-read state on wake), settle every\n * merged waiter. A superseded round never lands here; a re-opened window\n * carries the mark and waiters over to the pending 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/** The mode/reValidateMode gate shared by change/blur kicks and both\n * validateDeps re-runs: fires for `cadence` and `'all'`, for\n * `'onTouched'` once touched, else when `reValidateMode` matches\n * `cadence` and an error is still live. `hasError` reads lazily. */\nexport function shouldKick(\n mode: ValidationMode,\n cadence: 'onChange' | 'onBlur',\n touched: boolean,\n hasError: () => boolean,\n reValidateMode: ReValidateMode\n): boolean {\n return (\n mode === cadence ||\n mode === 'all' ||\n (mode === 'onTouched' && touched) ||\n (reValidateMode === cadence && hasError())\n );\n}\n\n/**\n * Form-level twin of the gated change kick: re-run `validate` after a\n * user change to a field in `validateDeps`. Rides the changed field's\n * onChange pipeline, so programmatic setValue writes never fire it. The\n * gate uses the changed field's effective `mode` and the form-level\n * `reValidateMode` against the last round's error footprint:\n * onChange/all — every dep change; onTouched — once touched; otherwise\n * while the round's error is still live under reValidateMode onChange\n * (the submit-then-fix flow). Fire-and-forget: async rejections are\n * swallowed, sync throws propagate. A no-op (one property check) unless\n * `validateDeps` lists `path`.\n */\nexport function revalidateFormOnChange(\n form: Form,\n path: Path,\n mode: ValidationMode\n): void {\n if (!form.validateDeps?.has(path.key) || !form.validate) return;\n if (\n shouldKick(\n mode,\n 'onChange',\n hasTouchedByPath(form, path),\n () => hasFormValidateErrors(form),\n form.reValidateMode\n )\n ) {\n runFormValidate(form).catch(() => {});\n }\n}\n\n/** Field-level validateDeps registry: dep key → dependents listing it\n * (WeakMap — Form shape untouched). */\nconst fieldValidateDeps = new WeakMap<Form, Map<string, Set<string>>>();\n\n/** Register one field's deps: `key` re-validates when any path in\n * `depKeys` changes. Idempotent per pair — StrictMode's double effect is\n * harmless. */\nexport function registerFieldValidateDeps(\n form: Form,\n key: string,\n depKeys: string[]\n): void {\n const deps = getOrCreate(fieldValidateDeps, form, () => new Map());\n for (const depKey of depKeys) {\n const dependents = deps.get(depKey) ?? new Set<string>();\n dependents.add(key);\n deps.set(depKey, dependents);\n }\n}\n\n/** Drop one field's deps registration; empty entries are removed so the\n * registry never outlives its fields. */\nexport function unregisterFieldValidateDeps(\n form: Form,\n key: string,\n depKeys: string[]\n): void {\n const deps = fieldValidateDeps.get(form);\n if (!deps) return;\n for (const depKey of depKeys) {\n const dependents = deps.get(depKey);\n if (!dependents?.delete(key)) continue;\n if (!dependents.size) deps.delete(depKey);\n }\n}\n\n/**\n * Field-level twin: after a user change to `path`, re-run every field\n * validator that declared it as a dep. Same channel and gate as\n * {@link revalidateFormOnChange} — the dependent's own error arms\n * reValidateMode, and a passing re-run replaces it (a field validator\n * owns its whole key). Ordinary validator kicks: the dependent's\n * debounce applies, sync throws propagate. A no-op unless some field\n * declared `path` as a dep.\n */\nexport function revalidateDependentsOnChange(\n form: Form,\n path: Path,\n mode: ValidationMode\n): void {\n const dependents = fieldValidateDeps.get(form)?.get(path.key);\n if (!dependents?.size) return;\n for (const dependent of dependents) {\n // A self-dep: the field's own onChange already validated it.\n if (dependent === path.key) continue;\n if (\n shouldKick(\n mode,\n 'onChange',\n hasTouchedByPath(form, path),\n () => form.errors.has(dependent),\n form.reValidateMode\n )\n ) {\n form.validators.get(dependent)?.();\n }\n }\n}\n\n/** {@link ensureValidate}'s rejection: `message` is the first error's\n * display text; `.errors` carries the full flattened list ({@link\n * getErrors}) for branching without re-reading the form. */\nexport type FormValidationError = Error & {errors: FieldErrorEntry[]};\n\nfunction validationError(form: Form): FormValidationError {\n const error = new Error(getFirstError(form)) as FormValidationError;\n error.errors = getErrors(form);\n return error;\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 validationError(form);\n });\n\n if (form.validate) {\n await runFormValidate(form);\n if (hasErrors(form)) throw validationError(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"],"names":["asErrorList","output","list","Array","isArray","length","registerValidatorByPath","form","path","registration","timer","controller","marked","lock","errorSource","mark","emitter","validating","add","key","emit","setValidatingByPath","unmark","delete","unsetValidatingByPath","land","result","asyncAlways","gate","sync","getValueByPath","collectSyncErrors","own","setErrorByPath","cancel","clearTimeout","abort","runValidator","fn","validate","AbortController","round","signal","e","isPromise","then","error","catch","finally","gateOwnsKick","runSync","run","validators","set","debounce","setTimeout","async","trigger","name","options","settle","keys","waitUntil","FORM_VALIDATING_KEY","fieldsSettled","forEach","validator","runFormValidate","hasErrors","setFormErrors","segments","footprint","Object","entries","value","isIndex","normalizePath","landLeafError","isFieldError","setError","createPath","stored","errors","get","recordFootprint","applyValidateResult","validateDeps","validateMode","getOrCreate","formErrorFootprints","Map","getFormErrorFootprint","written","bumpErrorsVersion","segmentsFromKey","clearFormValidateErrors","clear","VALIDATION_OUTCOME","outcome","setParsedValues","values","WeakMap","SETTLED","formValidateStates","Promise","resolve","validateDebounce","getValues","state","waiters","getFormValidateState","runFormValidateRound","settleFormValidate","reject","push","waiter","shouldKick","mode","cadence","touched","hasError","reValidateMode","revalidateFormOnChange","has","hasTouchedByPath","hasFormValidateErrors","fieldValidateDeps","revalidateDependentsOnChange","dependents","size","dependent"],"mappings":"iOA8FA,SAASA,EACPC,GAEA,QAAe,IAAXA,EAAsB,OAAO,KACjC,MAAMC,EAAOC,MAAMC,QAAQH,GAAUA,EAAS,CAACA,GAC/C,OAAOC,EAAKG,OAASH,EAAO,IAC9B,CAiBO,SAASI,EACdC,EACAC,EACAC,GAEA,IAAIC,EAAe,KACfC,EAAqC,KACrCC,GAAS,EACTC,EAAsB,KAKtBC,EAA2C,KAC/C,MAAMC,EAAO,KACPH,IACJA,GAAS,EArFN,UACLI,QAACA,EAAAC,WAASA,GACVT,GAEAS,EAAWC,IAAIV,EAAKW,KACpBC,EAAKJ,EAAS,aAAcR,EAC9B,CAgFIa,CAAoBd,EAAMC,KAEtBc,EAAS,KACRV,IACLA,GAAS,EAlGN,UACLI,QAACA,EAAAC,WAASA,GACVT,GAEAS,EAAWM,OAAOf,EAAKW,KACvBC,EAAKJ,EAAS,aAAcR,EAC9B,CA6FIgB,CAAsBjB,EAAMC,KAiCxBiB,EAAQC,IACZ,GAAIjB,EAAakB,gBAAiB,CAChC,MAAMC,EAXgB,MACxB,MAAMC,EAAOpB,EAAaoB,OAC1B,OAAKA,EACE7B,EAAY6B,EAAKC,EAAevB,EAAMC,GAAO,CAACD,OAAMC,UADzC,MASHuB,GACPC,EAAMhC,EAAY0B,IAAW,GACnCO,EAAe1B,EAAMC,EAAM,IAAKoB,GAAQ,MAAQI,IAChDlB,EAAckB,EAAI3B,OAAS,YAAcuB,EAAO,OAAS,IAC3D,MACEK,EAAe1B,EAAMC,EAAMkB,GAC3BZ,EAAcd,EAAY0B,GAAU,YAAc,MAShDQ,EAAS,KACC,OAAVxB,IACFyB,aAAazB,GACbA,EAAQ,MAEVC,GAAYyB,QACZvB,EAAO,CAAA,EACPS,KAGIe,EAAe,KACnB,MAAMC,EAAK7B,EAAa8B,WACxB,IAAKD,EAEH,YADAhB,IAIFX,GAAYyB,QACZzB,EAAa,IAAI6B,gBACjB,MAAMC,EAAS5B,EAAO,CAAA,EACtB,IAAIa,EACJ,IACEA,EAASY,EAAGR,EAAevB,EAAMC,GAAO,CACtCD,OACAC,OACAkC,OAAQ/B,EAAW+B,QAEvB,OAASC,GAIP,MADArB,IACMqB,CACR,CACA,IAAKC,EAAUlB,GAKb,OAJAD,EAAKC,QAGLJ,IAGFP,IACAW,EACGmB,KAAMC,IACDjC,IAAS4B,GACXhB,EAAKqB,KAKRC,MAAM,QACNC,QAAQ,KACHnC,IAAS4B,IACXnB,IACAT,EAAO,SAOToC,EAAe,OAxGL,MACd,MAAMpB,EAAOpB,EAAaoB,OAC1B,IAAKA,EAAM,OAAO,EAClB,MAAMD,EAAO5B,EAAY6B,EAAKC,EAAevB,EAAMC,GAAO,CAACD,OAAMC,UACjE,OAAKoB,GAOLK,EAAe1B,EAAMC,EAAMoB,GAC3Bd,EAAc,QACP,IARAL,EAAa8B,YAA8B,SAAhBzB,IAC9BmB,EAAe1B,EAAMC,UACrBM,EAAc,OAET,IAgGHoC,IAAczC,EAAakB,mBACjCO,KACO,GAKHiB,EAAM,KACVzC,EAAQ,KACJuC,KACJZ,KAmBF,OADA9B,EAAK6C,WAAWC,IAAI7C,EAAKW,IAfZ,KACX,GAAI8B,IAAgB,OACpB,IAAKxC,EAAa8B,WAAY,OAC9B,MAAMe,EAAW7C,EAAa6C,WAC9B,GAAIA,EAAW,EAMb,OAHc,OAAV5C,EAAgByB,aAAazB,GAC5BK,SACLL,EAAQ6C,WAAWJ,EAAKG,IAG1BjB,MAIK,KACL9B,EAAK6C,WAAW7B,OAAOf,EAAKW,KAC5Be,IAEJ,CA8BAsB,eAAsBC,EACpBlD,EACAmD,EACAC,GASA,MAAMC,EAAUC,GACdC,EACEvD,EAAKS,QACL,aACA,IAyKN,SAAuBT,GACrB,IAAA,MAAWY,KAAOZ,EAAKU,WACrB,GAAIE,IAAQ4C,EAAqB,OAAO,EAE1C,OAAO,CACT,CA5KYC,CAAczD,GAEpB,KAAM,GAmBR,OALAA,EAAK6C,WAAWa,QAAQC,GAAaA,WAC/BN,IACFrD,EAAKgC,gBAAgB4B,EAAgB5D,IAGjC6D,EAAU7D,EAYtB,CAeA,SAAS8D,EACP9D,EACAmB,EACA4C,EAAyB,GACzBC,GAEAC,OAAOC,QAAQ/C,GAAQuC,QAAQ,EAAE9C,EAAKuD,MAIpC,MAAMlE,EAAqB,IACtB8D,KACCK,EAAQxD,GAAO,CAACA,GAAOyD,EAAczD,IAEtB,iBAAVuD,EACLA,GAAOG,EAActE,EAAMC,EAAMkE,EAAOH,GACnCpE,MAAMC,QAAQsE,IAAUI,EAAaJ,GAC9CG,EAActE,EAAMC,EAAMkE,EAAOH,GACxBG,GAA0B,iBAAVA,GACzBL,EAAc9D,EAAMmE,EAAOlE,EAAM+D,IAGvC,CAgBA,SAASM,EACPtE,EACAC,EACAsC,EACAyB,GAEAQ,EAASxE,EAAMC,EAAMsC,GAjBvB,SACEvC,EACA+D,EACAC,GAEA,IAAKA,EAAW,OAChB,MAAM/D,EAAOwE,EAAWV,GAClBW,EAAS1E,EAAK2E,OAAOC,IAAI3E,EAAKW,KAChC8D,GAAQV,EAAUlB,IAAI7C,EAAKW,IAAK8D,EACtC,CASEG,CAAgB7E,EAAMC,EAAM+D,EAC9B,CASA,SAASc,EACP9E,EACAmB,GAEA,MAAM6C,EACJhE,EAAK+E,cAAsC,aAAtB/E,EAAKgF,aAqB9B,SAA+BhF,GAC7B,OAAOiF,EAAYC,EAAqBlF,EAAM,IAAM,IAAImF,IAC1D,CAtBQC,CAAsBpF,QACtB,EAKN,GAJIgE,KAsCN,SAAiChE,EAAYgE,GAC3C,IAAA,MAAYpD,EAAKyE,KAAYrB,EAAW,CACvBhE,EAAK2E,OAAOC,IAAIhE,KAChByE,IACfrF,EAAK2E,OAAO3D,OAAOJ,GACnB0E,EAAkBtF,GAClBa,EAAKb,EAAKS,QAAS,SAAUgE,EAAWc,EAAgB3E,KAC1D,CACF,CA7CI4E,CAAwBxF,EAAMgE,GAC9BA,EAAUyB,SAEPtE,EAAL,CACA,GAAsB,iBAAXA,GAAuBuE,KAAsBvE,EAAQ,CAC9D,MAAMwE,EAAUxE,EAGhB,OAFIwE,EAAQhB,QAAQb,EAAc9D,EAAM2F,EAAQhB,OAAQ,GAAIX,QAC5D4B,EAAgB5F,EAAM2F,EAAQE,OAEhC,CACA/B,EAAc9D,EAAMmB,EAA+B,GAAI6C,EAP1C,CAQf,CAIA,MAAMkB,MAA0BY,QAkChC,MAAMtC,EAAsB,oBAe5B,MAAMuC,SAAiB,yBAcjBC,MAAyBF,QAsBxB,SAASlC,EAAgB5D,GAC9B,MAAMgC,EAAWhC,EAAKgC,SACtB,IAAKA,EAAU,OAAOiE,QAAQC,UAC9B,MAAMnD,EAAW/C,EAAKmG,kBAAoB,EAC1C,GAAIpD,GAAY,EAAG,CAGjB,MAAM3C,EAAa,IAAI6B,gBACvB,OAAOgE,QAAQC,QACblE,EAASoE,EAAUpG,GAAO,CAACA,OAAMmC,OAAQ/B,EAAW+B,UACpDG,KAAKnB,IACL2D,EAAoB9E,EAAMmB,IAE9B,CACA,MAAMkF,EAlCR,SAA8BrG,GAC5B,OAAOiF,EAAYe,EAAoBhG,EAAM,KAAA,CAC3CG,MAAO,KACPC,WAAY,KACZ8B,MAAO,KACP7B,QAAQ,EACRiG,QAAS,KAEb,CA0BgBC,CAAqBvG,GAiBnC,OAdoB,OAAhBqG,EAAMlG,MAAgByB,aAAayE,EAAMlG,QAE3CkG,EAAMhG,QAAS,EACfL,EAAKU,WAAWC,IAAI6C,GACpB3C,EAAKb,EAAKS,QAAS,eAErB4F,EAAMlG,MAAQ6C,WAAW,KACvBqD,EAAMlG,MAAQ,KACd,MAAM+B,EAASmE,EAAMnE,MAAQ,CAAA,GAajC,SACElC,EACAqG,EACAnE,GAEA,MAAMF,EAAWhC,EAAKgC,SACtB,IAAKA,EAAU,OAAOiE,QAAQC,UAC9BG,EAAMjG,YAAYyB,QAClB,MAAMzB,EAAciG,EAAMjG,WAAa,IAAI6B,gBAGrC0D,EAAU,IAAIM,QAAaC,GAC/BA,EAAQlE,EAASoE,EAAUpG,GAAO,CAACA,OAAMmC,OAAQ/B,EAAW+B,WAE9D,OAAOwD,EAAQrD,KACbnB,IACMkF,EAAMnE,QAAUA,GAAO4C,EAAoB9E,EAAMmB,IAEvDoB,IACE,GAAI8D,EAAMnE,QAAUA,EAAO,MAAMK,GAGvC,EAlCIiE,CAAqBxG,EAAMqG,EAAOnE,GAAOI,KACvC,IAAMmE,EAAmBzG,EAAMqG,EAAOnE,EAAO6D,GAC7CxD,GAASkE,EAAmBzG,EAAMqG,EAAOnE,EAAOK,KAEjDQ,GACI,IAAIkD,QAAc,CAACC,EAASQ,KACjCL,EAAMC,QAAQK,KAAK,CAACT,UAASQ,YAEjC,CAgCA,SAASD,EACPzG,EACAqG,EACAnE,EACAyD,GAEA,GAAIU,EAAMnE,QAAUA,EAAO,OAE3B,GADAmE,EAAMnE,MAAQ,KACM,OAAhBmE,EAAMlG,MAAgB,OACtBkG,EAAMhG,SACRgG,EAAMhG,QAAS,EACfL,EAAKU,WAAWM,OAAOwC,GACvB3C,EAAKb,EAAKS,QAAS,eAErB,MAAM6F,EAAUD,EAAMC,QACtBD,EAAMC,QAAU,GAChB,IAAA,MAAWM,KAAUN,EACfX,IAAYI,EAASa,EAAOV,UAC3BU,EAAOF,OAAOf,EAEvB,CAMO,SAASkB,EACdC,EACAC,EACAC,EACAC,EACAC,GAEA,OACEJ,IAASC,GACA,QAATD,GACU,cAATA,GAAwBE,GACxBE,IAAmBH,GAAWE,GAEnC,CAcO,SAASE,EACdnH,EACAC,EACA6G,GAEK9G,EAAK+E,cAAcqC,IAAInH,EAAKW,MAASZ,EAAKgC,UAE7C6E,EACEC,EACA,WACAO,EAAiBrH,EAAMC,GACvB,IA7MN,SAA+BD,GAC7B,MAAMgE,EAAYkB,EAAoBN,IAAI5E,GAC1C,IAAKgE,EAAW,OAAO,EACvB,IAAA,MAAYpD,EAAKyE,KAAYrB,EAC3B,GAAIhE,EAAK2E,OAAOC,IAAIhE,KAASyE,EAAS,OAAO,EAE/C,OAAO,CACT,CAsMYiC,CAAsBtH,GAC5BA,EAAKkH,iBAGPtD,EAAgB5D,GAAMwC,MAAM,OAEhC,CAIA,MAAM+E,MAAwBzB,QA2CvB,SAAS0B,EACdxH,EACAC,EACA6G,GAEA,MAAMW,EAAaF,EAAkB3C,IAAI5E,IAAO4E,IAAI3E,EAAKW,KACzD,GAAK6G,GAAYC,KACjB,IAAA,MAAWC,KAAaF,EAElBE,IAAc1H,EAAKW,KAErBiG,EACEC,EACA,WACAO,EAAiBrH,EAAMC,GACvB,IAAMD,EAAK2E,OAAOyC,IAAIO,GACtB3H,EAAKkH,iBAGPlH,EAAK6C,WAAW+B,IAAI+C,EAApB3H,IAGN"}
@@ -0,0 +1,2 @@
1
+ "use strict";var e=require("@for-fun/event-emitter"),t=require("./errors-TzyWwBfw.cjs.js");function s({emitter:t,touched:s},i){s.has(i.key)||(s.add(i.key),e.emit(t,"touched",i))}function i(e){const{initialValues:s,parsedValues:i,values:r,deleted:n}=e,u=new Set;let o=i??s;for(const[e,s]of r)o=t.setOwned(o,t.segmentsFromKey(e),s,u);for(const e of n)o=t.unset(o,t.segmentsFromKey(e));return o}function r({initialValues:e,parsedValues:s,values:i,deleted:r},n){const{key:u,value:o}=n;if(i.has(u))return i.get(u);if(!r.has(u)){for(let e=o.length-1;e>0;e--){const s=JSON.stringify(o.slice(0,e));if(i.has(s))return t.get(i.get(s),o.slice(e))}return t.get(s??e,o)}}function n(i,n,a,l){const{emitter:c,values:d,deleted:m}=i,f="function"==typeof a?a(r(i,n)):a;d.set(n.key,f),u(d,n),o(m,n),t.pruneDirtyBaselines(i,n),!1===l?.shouldDirty&&t.setDirtyBaseline(i,n,f),t.bumpDirtyVersion(i),t.bumpValuesVersion(i),l?.shouldTouch&&s(i,n),l?.shouldValidate&&i.validators.get(n.key)?.(),e.emit(c,"change",n)}function u(e,{key:t}){if(!e.size)return;const s=`${t.slice(0,-1)},`;for(const t of e.keys())t.startsWith(s)&&e.delete(t)}function o(e,{key:t}){if(e.size)for(const s of e)(s===t||s.startsWith(`${t.slice(0,-1)},`)||t.startsWith(`${s.slice(0,-1)},`))&&e.delete(s)}exports.emitChangeByPath=function({emitter:t},s){e.emit(t,"change",s)},exports.getTouchedFields=function({touched:e}){return Array.from(e,e=>t.segmentsFromKey(e).join("."))},exports.getValueByPath=r,exports.getValues=function(e){const s=t.getOrCreate(t.valuesCaches,e,()=>({version:0,result:i(e)}));return s.version>0&&(s.result=i(e),s.version=0),s.result},exports.hasTouchedByPath=function({touched:e},t){return e.has(t.key)},exports.removeFieldByPath=function(s,i,r){const{key:n,value:u}=i,{emitter:o,values:a,touched:l,errors:c,validating:d,deleted:m}=s;a.delete(n),t.clearDirtyBaselines(s,n),function(e,t){for(let s=1;s<t.length;s++)if(e.has(JSON.stringify(t.slice(0,s))))return!0;const s=`${JSON.stringify(t).slice(0,-1)},`;for(const t of e.keys())if(t.startsWith(s))return!0;return!1}(a,u)||m.add(n),l.delete(n),c.delete(n)&&t.bumpErrorsVersion(s),d.delete(n),t.bumpDirtyVersion(s),t.bumpValuesVersion(s),e.emit(o,"change",i),e.emit(o,"touched",i),e.emit(o,"errors",i),e.emit(o,"validating",i)},exports.reset=function(s,i,r){const u=[];s.initialValues=i??s.initialValues,s.parsedValues=void 0,t.clearErrors(s);const{emitter:o,touched:a,values:l,deleted:c,validating:d}=s;l.clear(),c.clear(),t.clearDirtyBaselines(s),a.clear(),d.clear(),s.isSubmitting=!1,s.submitCount=0,s.isSubmitted=!1,s.isSubmitSuccessful=void 0,t.bumpDirtyVersion(s),t.bumpValuesVersion(s);for(const{segments:e,value:i}of u)n(s,t.create(e),i);e.emit(o,"change"),e.emit(o,"touched"),e.emit(o,"validating"),e.emit(o,"submitting"),e.emit(o,"submitCount"),e.emit(o,"submitSuccessful"),e.emit(o,"reset")},exports.seedValueByPath=function(e,s,i){const{values:r,deleted:n}=e;r.set(s.key,i),u(r,s),o(n,s),t.pruneDirtyBaselines(e,s),t.bumpDirtyVersion(e),t.bumpValuesVersion(e)},exports.setInitialValues=function(s,i){s.initialValues===i||t.isEqual(s.initialValues,i)||(s.initialValues=i,s.parsedValues=void 0,s.values.clear(),s.deleted.clear(),t.clearDirtyBaselines(s),t.bumpDirtyVersion(s),t.bumpValuesVersion(s),e.emit(s.emitter,"change"))},exports.setTouchedByPath=s,exports.setValueByPath=n;
2
+ //# sourceMappingURL=values-BbPnLByD.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values-BbPnLByD.cjs.js","sources":["../src/core/touched.ts","../src/core/values.ts"],"sourcesContent":["import {emit} from '../emitter';\nimport createPath, {segmentsFromKey} from '../path';\nimport type {Name, Path} from '../path';\nimport type {AnyPath} from '../types';\nimport type {Form} from '../form';\n\n/** Set a field's touched state. */\nexport function setTouched(form: Form, name: Name): void {\n setTouchedByPath(form, createPath(name));\n}\n\n/** Set a field's touched state by path. */\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 skip unrelated fields;\n // payload-less listeners ignore it.\n emit(emitter, 'touched', path);\n}\n\n/** Check if a field has been touched. */\nexport function hasTouched<\n T extends Record<string, any> = any,\n P extends AnyPath<T> = AnyPath<T>\n>(form: Form<T>, name: P): boolean {\n return hasTouchedByPath(form, createPath(name));\n}\n\n/** Check if a field has been touched by path. */\nexport function hasTouchedByPath({touched}: Form, path: Path): boolean {\n return touched.has(path.key);\n}\n\n/** Get touched fields as user-facing dotted paths ('a.b', 'a.0.c'). */\nexport function getTouchedFields({touched}: Form): string[] {\n return Array.from(touched, key => segmentsFromKey(key).join('.'));\n}\n\n/** Is touched — any field has been touched. */\nexport function isTouched({touched}: Form): boolean {\n return touched.size > 0;\n}\n","import {emit} from '../emitter';\nimport createPath, {segmentsFromKey} from '../path';\nimport type {Name, Path, PathSegments} from '../path';\nimport type {PathValueOf, AnyPath} from '../types';\nimport {freezeValues, get, isEqual, setOwned, unset} from '../util';\nimport type {FieldError, Form} from '../form';\nimport {clearErrors, getErrorByPath, getFieldErrorsByPath} from './errors';\nimport {isFieldDirtyByPath} from './dirty';\nimport {setTouchedByPath} from './touched';\nimport {\n bumpDirtyVersion,\n bumpErrorsVersion,\n bumpValuesVersion,\n clearDirtyBaselines,\n getDirtyBaseline,\n getOrCreate,\n pruneDirtyBaselines,\n setDirtyBaseline,\n valuesCaches\n} from './internals';\n\n/** Dev-only flag, replaced at build time (rollup.config.js `replace`);\n * defined for the test environment in vitest.config.ts. */\ndeclare const __DEV__: boolean;\n\n/** Get form values: the values Map layered over parsedValues (when a\n * schema validation produced them) over initialValues. Merged with\n * copy-on-write ownership tracking ({@link setOwned}), so untouched\n * branches share references with the baseline. Memoized per form via a\n * version-bump/read counter ({@link bumpValuesVersion}): consecutive reads\n * hand back the same reference; treat it as read-only. parsedValues never\n * affects dirty state — dirty compares live edits against initialValues. */\nexport function getValues<T extends Record<string, any> = any>(\n form: Form<T>\n): T {\n const cache = getOrCreate(valuesCaches, form, () => ({\n version: 0,\n result: computeValues(form)\n }));\n if (cache.version > 0) {\n cache.result = computeValues(form);\n cache.version = 0;\n }\n return cache.result as T;\n}\n\nfunction computeValues(form: Form): any {\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, segmentsFromKey(key), value, owned);\n }\n // Tombstoned paths are removed so they don't fall back to initialValues.\n // unset is immutable (mutating delete would corrupt shared baseline\n // branches) and deletes outright rather than writing undefined.\n for (const key of deleted) {\n merged = unset(merged, segmentsFromKey(key));\n }\n // DEV-only: hand back a frozen clone (freezing in place would also\n // freeze the baseline containers it borrows from), so consumer mutations\n // throw at the offending site instead of corrupting the shared cache.\n return __DEV__ ? freezeValues(merged) : merged;\n}\n\n/** Get a field's value. */\nexport function getValue<\n T extends Record<string, any> = any,\n P extends AnyPath<T> = AnyPath<T>\n>(form: Form<T>, name: P): PathValueOf<T, P> {\n return getValueByPath(form, createPath(name));\n}\n\n/** Get a field's value by path. */\nexport function getValueByPath(\n {initialValues, parsedValues, values, deleted}: Form,\n path: Path\n): any {\n const {key, value: segments} = path;\n if (values.has(key)) return values.get(key);\n // Unregistered path: the tombstone blocks the initialValues fallback.\n if (deleted.has(key)) return undefined;\n // A live ancestor key is a whole-branch write (setValue at a parent\n // path, every useFieldArray operation): it replaces the subtree below\n // it, so reads under it resolve from that stored value, nearest\n // ancestor first — a finer write is layered over a coarser one and\n // drops the superseded descendant keys, so the closest ancestor is the\n // newest generation. Paths the ancestor's value does not carry read\n // undefined — the baseline must not fill holes inside a replaced branch.\n for (let i = segments.length - 1; i > 0; i--) {\n const ancestorKey = JSON.stringify(segments.slice(0, i));\n if (values.has(ancestorKey)) {\n return get(values.get(ancestorKey), segments.slice(i));\n }\n }\n // Same layering as getValues: parsed values (when present) are the\n // baseline above initialValues.\n return get(parsedValues ?? initialValues, segments);\n}\n\n/** Options accepted by {@link setValue} / {@link setValueByPath} / {@link\n * changeValue} / {@link changeValueByPath}. `shouldValidate`/`shouldTouch`\n * default to `false`. */\nexport type SetFieldOptions = {\n /** Run the field's registered validator (if any) after the value lands.\n * 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: it becomes the field's\n * dirty-comparison baseline. `true` (or omitting) keeps the default\n * derived behavior — dirty while the live value differs from\n * initialValues. */\n shouldDirty?: boolean;\n};\n\n/** Set a field's value. `value` may be an updater function receiving the\n * current value and returning the next (TanStack Form's `setFieldValue`\n * contract) — so a function can never itself be stored as a value. */\nexport function setValue<\n T extends Record<string, any> = any,\n P extends AnyPath<T> = AnyPath<T>\n>(\n form: Form<T>,\n name: P,\n value: PathValueOf<T, P> | ((prev: PathValueOf<T, P>) => PathValueOf<T, P>),\n options?: SetFieldOptions\n): void {\n setValueByPath(form, createPath(name), value, options);\n}\n\n/** Set a field's value by path. `value` may be an updater function (see\n * {@link setValue}). */\nexport function setValueByPath(\n form: Form,\n path: Path,\n value: any | ((prev: any) => any),\n options?: SetFieldOptions\n): void {\n const {emitter, values, deleted} = form;\n const next =\n typeof value === 'function' ? value(getValueByPath(form, path)) : value;\n values.set(path.key, next);\n // The write replaces the whole subtree below it, so descendant keys in\n // the values Map belong to an older generation: without this prune they\n // would shadow the new value on exact-key reads and double-apply over it\n // in getValues' merge (a stale `a.b` would survive a fresh `a` write).\n pruneDescendantKeys(values, path);\n reviveBranch(deleted, path);\n // Baselines under the replaced subtree die with it — before the emit, so\n // subscribers reading dirty state in 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, next);\n bumpDirtyVersion(form);\n bumpValuesVersion(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/** The write of {@link setValueByPath} minus the `'change'` emit: the\n * render-time `useField` `initialValue` seed. The field's first paint\n * (SSR included) must already carry the value, so the write happens during\n * render where emitting is illegal; the field announces it post-commit via\n * {@link emitChangeByPath}. */\nexport function seedValueByPath(form: Form, path: Path, value: any): void {\n const {values, deleted} = form;\n values.set(path.key, value);\n pruneDescendantKeys(values, path);\n reviveBranch(deleted, path);\n pruneDirtyBaselines(form, path);\n bumpDirtyVersion(form);\n bumpValuesVersion(form);\n}\n\n/** Announce a {@link seedValueByPath} that happened during render: the\n * payload-carrying `'change'` emit {@link setValueByPath} would have\n * fired, split out so it runs post-commit where emitting is safe. */\nexport function emitChangeByPath({emitter}: Form, path: Path): void {\n emit(emitter, 'change', path);\n}\n\n/** One field's aggregated state, as {@link getFieldState} returns it.\n * `errors` is the stored array shared with the form — treat it read-only. */\nexport type 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/** Get one field's aggregated state: layered value, first/every error,\n * dirtiness, touched flag, and validating flag. `isDirty` applies the same\n * per-field rule as {@link getDirtyFields}. */\nexport function getFieldState<\n T extends Record<string, any> = any,\n P extends AnyPath<T> = AnyPath<T>\n>(form: Form<T>, name: P): FieldState<PathValueOf<T, P>> {\n const path = createPath(name);\n const {touched, validating} = form;\n return {\n value: getValueByPath(form, path),\n error: getErrorByPath(form, path),\n errors: getFieldErrorsByPath(form, path),\n // The shared per-field rule (committed baselines included): the field\n // is dirty while its live value differs from its effective baseline.\n isDirty: isFieldDirtyByPath(form, path),\n isTouched: touched.has(path.key),\n isValidating: validating.has(path.key)\n };\n}\n\n/** Options accepted by {@link removeField}. All flags default to `false` —\n * the historical remove semantics (value dropped, path tombstoned, dirty\n * baseline/touched/errors cleared). */\nexport type RemoveFieldOptions = {\n /** Keep the field's live value and dirty baseline instead of\n * tombstoning: reads, `getValues()`, and submit keep the value. */\n keepValue?: boolean;\n /** Keep the field's dirty baseline. Implies `keepValue`. */\n keepDirty?: boolean;\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 keepError?: boolean;\n};\n\nexport function removeField(\n form: Form,\n name: Name,\n options?: RemoveFieldOptions\n): void {\n removeFieldByPath(form, createPath(name), options);\n}\n\n/** Remove a field by path; the keep-flags preserve slices of state. */\nexport function removeFieldByPath(\n form: Form,\n path: Path,\n options?: RemoveFieldOptions\n): void {\n const {key, value: segments} = path;\n const {emitter, values, touched, errors, validating, deleted} = form;\n if (!options?.keepValue && !options?.keepDirty) {\n values.delete(key);\n // A remount starts fresh rather than inheriting a baseline committed\n // 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 old initial value. A tombstone never\n // shadows live values: skip it when a live ancestor or descendant key\n // already covers the branch.\n if (!hasLiveBranch(values, segments)) deleted.add(key);\n }\n if (!options?.keepTouched) touched.delete(key);\n if (!options?.keepError && errors.delete(key)) bumpErrorsVersion(form);\n validating.delete(key);\n bumpDirtyVersion(form);\n bumpValuesVersion(form);\n // Path-payload emits, scoped exactly like the writes above (every\n // mutation is bounded to this path's key): leaf watchers on the path and\n // below it wake, ancestor branch watchers wake, and payload-less global\n // listeners wake regardless — while sibling fields stay asleep.\n emit(emitter, 'change', path);\n emit(emitter, 'touched', path);\n emit(emitter, 'errors', path);\n emit(emitter, 'validating', path);\n}\n\ntype ValuesMap = Map<string, any>;\n\n/** Does a live value cover the branch at `segments` — at an ancestor key\n * or below it at a descendant key? */\nfunction hasLiveBranch(values: ValuesMap, segments: PathSegments): 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/** Writing a value replaces the subtree below the written path, so drop\n * the values Map keys under it: they were set against an older generation\n * and would otherwise shadow the fresh value or re-apply over it. */\nfunction pruneDescendantKeys(values: ValuesMap, {key}: Path): void {\n if (!values.size) return;\n const stem = `${key.slice(0, -1)},`;\n for (const k of values.keys()) {\n if (k.startsWith(stem)) values.delete(k);\n }\n}\n\n/** Writing a value revives its whole branch: drop any removal tombstone\n * for the path itself, its ancestors, or its descendants. */\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/** Set form initialValues. Content-based early return: a new reference with\n * equal content is a no-op (committed edits survive); genuinely changed\n * content swaps the baseline and re-seeds — live values and tombstones are\n * cleared, touched flags and errors survive. */\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 and every\n // baseline committed against the old one.\n form.parsedValues = undefined;\n form.values.clear();\n form.deleted.clear();\n clearDirtyBaselines(form);\n bumpDirtyVersion(form);\n bumpValuesVersion(form);\n emit(form.emitter, 'change');\n}\n\n/** Options accepted by {@link reset}. Every flag defaults to `false`. */\nexport type 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 keepDirtyValues?: boolean;\n /** Keep every field's current live value instead of returning to the\n * baseline (a superset of `keepDirtyValues`). Dirtiness is recomputed\n * against the post-reset baseline. */\n keepValues?: boolean;\n /** Ignore a newly provided `initialValues` and keep the current baseline. */\n keepDefaultValues?: 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 (`isSubmitted`) instead of clearing it. */\n keepIsSubmitted?: boolean;\n /** Keep the last submit's success flag (`isSubmitSuccessful`). */\n keepIsSubmitSuccessful?: 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\ntype KeptValue = {segments: PathSegments; value: any};\n\n/** Collect every leaf path of the merged values tree into `out` —\n * structured segments so each leaf can be written back with\n * setValueByPath. Objects with no enumerable keys are leaves themselves. */\nfunction collectValueLeaves(\n node: any,\n segments: PathSegments,\n out: KeptValue[]\n): void {\n if (node !== null && typeof node === 'object') {\n if (Array.isArray(node)) {\n node.forEach((item, i) =>\n collectValueLeaves(item, [...segments, i], out)\n );\n return;\n }\n const keys = Object.keys(node);\n if (keys.length > 0) {\n for (const k of keys) {\n collectValueLeaves(node[k], [...segments, k], out);\n }\n return;\n }\n }\n out.push({segments, value: node});\n}\n\n/** Snapshot the values {@link reset} re-seeds after the wipe: dirtiness is\n * measured against the pre-reset initialValues, so capture must happen\n * before form.values/form.initialValues are touched. The snapshot carries\n * structured segments, not dotted strings — dotted keys don't round-trip\n * through the parser. keepValues keeps every live value; keepDirtyValues\n * narrows the snapshot to fields differing from their effective baseline. */\nfunction collectKeptValues(form: Form, options?: ResetOptions): KeptValue[] {\n const kept: KeptValue[] = [];\n if (options?.keepValues) {\n // Every leaf of the CURRENT merged tree is written back after the\n // wipe, so a field with no live edit keeps its pre-reset value instead\n // of adopting the new baseline.\n collectValueLeaves(getValues(form), [], kept);\n } else if (options?.keepDirtyValues) {\n for (const [key, value] of form.values) {\n const segments = segmentsFromKey(key);\n // Same predicate as getDirtyFields: a live value differing from its\n // effective baseline (committed baselines read clean, not kept).\n if (getDirtyBaseline(form, key, segments) !== value) {\n kept.push({segments, value});\n }\n }\n }\n return kept;\n}\n\n/** Reset the form. Omitted (or undefined) `initialValues` keeps the current\n * baseline — fields simply return to it (react-hook-form's\n * reset-without-values semantics). */\nexport function reset(\n form: Form,\n initialValues?: any,\n options?: ResetOptions\n): void {\n const keptValues = collectKeptValues(form, options);\n // Omitting values is a return-to-initialValues reset, not a wipe: an\n // undefined baseline would make getValues() return undefined, so the\n // current baseline survives when no new one is provided.\n form.initialValues = options?.keepDefaultValues\n ? form.initialValues\n : (initialValues ?? form.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.isSubmitted = false;\n if (!options?.keepIsSubmitSuccessful) form.isSubmitSuccessful = undefined;\n bumpDirtyVersion(form);\n bumpValuesVersion(form);\n // Write the kept values back over the fresh baseline: plain\n // setValueByPath, so no validation fires and nothing is marked touched.\n for (const {segments, value} of keptValues) {\n setValueByPath(form, createPath(segments), 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}. Flags default to `false`;\n * `value` has no default — omitted, the field falls back to initialValues. */\nexport type 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 — never falls back to initialValues. */\n value?: any;\n};\n\n/** Reset a single field: drop its live value (reads fall back to the\n * baseline), clear touched/errors, and revive the path's tombstones — the\n * inverse of {@link removeFieldByPath}. Other fields are untouched. */\nexport function resetField<\n T extends Record<string, any> = any,\n P extends AnyPath<T> = AnyPath<T>\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 // Commits from before the reset no longer shadow the comparison.\n clearDirtyBaselines(form, path.key);\n // A parse baseline wholesale-shadows initialValues in reads, so unset\n // alone would read undefined. Remove the path from the tree (immutable —\n // parsedValues shares branches with the schema's output) and pin the\n // initial value back as the live value 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 by design (unlike removeFieldByPath): reviveBranch can\n // un-tombstone ancestor or descendant paths, whose readers must re-sync.\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 bumpErrorsVersion(form);\n emit(emitter, 'errors', path);\n }\n bumpDirtyVersion(form);\n bumpValuesVersion(form);\n}\n"],"names":["setTouchedByPath","emitter","touched","path","has","key","add","emit","computeValues","form","initialValues","parsedValues","values","deleted","owned","Set","merged","value","setOwned","segmentsFromKey","unset","getValueByPath","segments","get","i","length","ancestorKey","JSON","stringify","slice","setValueByPath","options","next","set","pruneDescendantKeys","reviveBranch","pruneDirtyBaselines","shouldDirty","setDirtyBaseline","bumpDirtyVersion","bumpValuesVersion","shouldTouch","shouldValidate","validators","size","stem","k","keys","startsWith","delete","tombstone","Array","from","join","cache","getOrCreate","valuesCaches","version","result","errors","validating","clearDirtyBaselines","hasLiveBranch","keptValues","clearErrors","clear","isSubmitting","submitCount","isSubmitted","isSubmitSuccessful","createPath","isEqual"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{emit as e}from"@for-fun/event-emitter";import{a as t,g as s,k as i,l as a,m as n,o as r,c as o,v as u,p as l,u as c,q as d,r as f,t as h,b as v,x as g}from"./errors-DA4ReEd9.mjs";function m({emitter:t,touched:s},i){s.has(i.key)||(s.add(i.key),e(t,"touched",i))}function y({touched:e},t){return e.has(t.key)}function k({touched:e}){return Array.from(e,e=>t(e).join("."))}function V(e){const t=s(u,e,()=>({version:0,result:b(e)}));return t.version>0&&(t.result=b(e),t.version=0),t.result}function b(e){const{initialValues:s,parsedValues:i,values:a,deleted:n}=e,r=new Set;let o=i??s;for(const[e,s]of a)o=l(o,t(e),s,r);for(const e of n)o=c(o,t(e));return o}function p({initialValues:e,parsedValues:t,values:s,deleted:i},a){const{key:n,value:r}=a;if(s.has(n))return s.get(n);if(!i.has(n)){for(let e=r.length-1;e>0;e--){const t=JSON.stringify(r.slice(0,e));if(s.has(t))return h(s.get(t),r.slice(e))}return h(t??e,r)}}function S(t,s,i,a){const{emitter:o,values:u,deleted:l}=t,c="function"==typeof i?i(p(t,s)):i;u.set(s.key,c),N(u,s),O(l,s),d(t,s),!1===a?.shouldDirty&&g(t,s,c),n(t),r(t),a?.shouldTouch&&m(t,s),a?.shouldValidate&&t.validators.get(s.key)?.(),e(o,"change",s)}function W(e,t,s){const{values:i,deleted:a}=e;i.set(t.key,s),N(i,t),O(a,t),d(e,t),n(e),r(e)}function $({emitter:t},s){e(t,"change",s)}function J(t,s,i){const{key:o,value:u}=s,{emitter:l,values:c,touched:d,errors:f,validating:h,deleted:g}=t;c.delete(o),a(t,o),function(e,t){for(let s=1;s<t.length;s++)if(e.has(JSON.stringify(t.slice(0,s))))return!0;const s=`${JSON.stringify(t).slice(0,-1)},`;for(const t of e.keys())if(t.startsWith(s))return!0;return!1}(c,u)||g.add(o),d.delete(o),f.delete(o)&&v(t),h.delete(o),n(t),r(t),e(l,"change",s),e(l,"touched",s),e(l,"errors",s),e(l,"validating",s)}function N(e,{key:t}){if(!e.size)return;const s=`${t.slice(0,-1)},`;for(const t of e.keys())t.startsWith(s)&&e.delete(t)}function O(e,{key:t}){if(e.size)for(const s of e)(s===t||s.startsWith(`${t.slice(0,-1)},`)||t.startsWith(`${s.slice(0,-1)},`))&&e.delete(s)}function j(t,s){t.initialValues===s||f(t.initialValues,s)||(t.initialValues=s,t.parsedValues=void 0,t.values.clear(),t.deleted.clear(),a(t),n(t),r(t),e(t.emitter,"change"))}function x(t,s,u){const l=[];t.initialValues=s??t.initialValues,t.parsedValues=void 0,i(t);const{emitter:c,touched:d,values:f,deleted:h,validating:v}=t;f.clear(),h.clear(),a(t),d.clear(),v.clear(),t.isSubmitting=!1,t.submitCount=0,t.isSubmitted=!1,t.isSubmitSuccessful=void 0,n(t),r(t);for(const{segments:e,value:s}of l)S(t,o(e),s);e(c,"change"),e(c,"touched"),e(c,"validating"),e(c,"submitting"),e(c,"submitCount"),e(c,"submitSuccessful"),e(c,"reset")}export{p as a,k as b,m as c,S as d,J as e,W as f,V as g,y as h,$ as i,x as r,j as s};
2
+ //# sourceMappingURL=values-CHsmcZk4.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values-CHsmcZk4.mjs","sources":["../src/core/touched.ts","../src/core/values.ts"],"sourcesContent":["import {emit} from '../emitter';\nimport createPath, {segmentsFromKey} from '../path';\nimport type {Name, Path} from '../path';\nimport type {AnyPath} from '../types';\nimport type {Form} from '../form';\n\n/** Set a field's touched state. */\nexport function setTouched(form: Form, name: Name): void {\n setTouchedByPath(form, createPath(name));\n}\n\n/** Set a field's touched state by path. */\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 skip unrelated fields;\n // payload-less listeners ignore it.\n emit(emitter, 'touched', path);\n}\n\n/** Check if a field has been touched. */\nexport function hasTouched<\n T extends Record<string, any> = any,\n P extends AnyPath<T> = AnyPath<T>\n>(form: Form<T>, name: P): boolean {\n return hasTouchedByPath(form, createPath(name));\n}\n\n/** Check if a field has been touched by path. */\nexport function hasTouchedByPath({touched}: Form, path: Path): boolean {\n return touched.has(path.key);\n}\n\n/** Get touched fields as user-facing dotted paths ('a.b', 'a.0.c'). */\nexport function getTouchedFields({touched}: Form): string[] {\n return Array.from(touched, key => segmentsFromKey(key).join('.'));\n}\n\n/** Is touched — any field has been touched. */\nexport function isTouched({touched}: Form): boolean {\n return touched.size > 0;\n}\n","import {emit} from '../emitter';\nimport createPath, {segmentsFromKey} from '../path';\nimport type {Name, Path, PathSegments} from '../path';\nimport type {PathValueOf, AnyPath} from '../types';\nimport {freezeValues, get, isEqual, setOwned, unset} from '../util';\nimport type {FieldError, Form} from '../form';\nimport {clearErrors, getErrorByPath, getFieldErrorsByPath} from './errors';\nimport {isFieldDirtyByPath} from './dirty';\nimport {setTouchedByPath} from './touched';\nimport {\n bumpDirtyVersion,\n bumpErrorsVersion,\n bumpValuesVersion,\n clearDirtyBaselines,\n getDirtyBaseline,\n getOrCreate,\n pruneDirtyBaselines,\n setDirtyBaseline,\n valuesCaches\n} from './internals';\n\n/** Dev-only flag, replaced at build time (rollup.config.js `replace`);\n * defined for the test environment in vitest.config.ts. */\ndeclare const __DEV__: boolean;\n\n/** Get form values: the values Map layered over parsedValues (when a\n * schema validation produced them) over initialValues. Merged with\n * copy-on-write ownership tracking ({@link setOwned}), so untouched\n * branches share references with the baseline. Memoized per form via a\n * version-bump/read counter ({@link bumpValuesVersion}): consecutive reads\n * hand back the same reference; treat it as read-only. parsedValues never\n * affects dirty state — dirty compares live edits against initialValues. */\nexport function getValues<T extends Record<string, any> = any>(\n form: Form<T>\n): T {\n const cache = getOrCreate(valuesCaches, form, () => ({\n version: 0,\n result: computeValues(form)\n }));\n if (cache.version > 0) {\n cache.result = computeValues(form);\n cache.version = 0;\n }\n return cache.result as T;\n}\n\nfunction computeValues(form: Form): any {\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, segmentsFromKey(key), value, owned);\n }\n // Tombstoned paths are removed so they don't fall back to initialValues.\n // unset is immutable (mutating delete would corrupt shared baseline\n // branches) and deletes outright rather than writing undefined.\n for (const key of deleted) {\n merged = unset(merged, segmentsFromKey(key));\n }\n // DEV-only: hand back a frozen clone (freezing in place would also\n // freeze the baseline containers it borrows from), so consumer mutations\n // throw at the offending site instead of corrupting the shared cache.\n return __DEV__ ? freezeValues(merged) : merged;\n}\n\n/** Get a field's value. */\nexport function getValue<\n T extends Record<string, any> = any,\n P extends AnyPath<T> = AnyPath<T>\n>(form: Form<T>, name: P): PathValueOf<T, P> {\n return getValueByPath(form, createPath(name));\n}\n\n/** Get a field's value by path. */\nexport function getValueByPath(\n {initialValues, parsedValues, values, deleted}: Form,\n path: Path\n): any {\n const {key, value: segments} = path;\n if (values.has(key)) return values.get(key);\n // Unregistered path: the tombstone blocks the initialValues fallback.\n if (deleted.has(key)) return undefined;\n // A live ancestor key is a whole-branch write (setValue at a parent\n // path, every useFieldArray operation): it replaces the subtree below\n // it, so reads under it resolve from that stored value, nearest\n // ancestor first — a finer write is layered over a coarser one and\n // drops the superseded descendant keys, so the closest ancestor is the\n // newest generation. Paths the ancestor's value does not carry read\n // undefined — the baseline must not fill holes inside a replaced branch.\n for (let i = segments.length - 1; i > 0; i--) {\n const ancestorKey = JSON.stringify(segments.slice(0, i));\n if (values.has(ancestorKey)) {\n return get(values.get(ancestorKey), segments.slice(i));\n }\n }\n // Same layering as getValues: parsed values (when present) are the\n // baseline above initialValues.\n return get(parsedValues ?? initialValues, segments);\n}\n\n/** Options accepted by {@link setValue} / {@link setValueByPath} / {@link\n * changeValue} / {@link changeValueByPath}. `shouldValidate`/`shouldTouch`\n * default to `false`. */\nexport type SetFieldOptions = {\n /** Run the field's registered validator (if any) after the value lands.\n * 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: it becomes the field's\n * dirty-comparison baseline. `true` (or omitting) keeps the default\n * derived behavior — dirty while the live value differs from\n * initialValues. */\n shouldDirty?: boolean;\n};\n\n/** Set a field's value. `value` may be an updater function receiving the\n * current value and returning the next (TanStack Form's `setFieldValue`\n * contract) — so a function can never itself be stored as a value. */\nexport function setValue<\n T extends Record<string, any> = any,\n P extends AnyPath<T> = AnyPath<T>\n>(\n form: Form<T>,\n name: P,\n value: PathValueOf<T, P> | ((prev: PathValueOf<T, P>) => PathValueOf<T, P>),\n options?: SetFieldOptions\n): void {\n setValueByPath(form, createPath(name), value, options);\n}\n\n/** Set a field's value by path. `value` may be an updater function (see\n * {@link setValue}). */\nexport function setValueByPath(\n form: Form,\n path: Path,\n value: any | ((prev: any) => any),\n options?: SetFieldOptions\n): void {\n const {emitter, values, deleted} = form;\n const next =\n typeof value === 'function' ? value(getValueByPath(form, path)) : value;\n values.set(path.key, next);\n // The write replaces the whole subtree below it, so descendant keys in\n // the values Map belong to an older generation: without this prune they\n // would shadow the new value on exact-key reads and double-apply over it\n // in getValues' merge (a stale `a.b` would survive a fresh `a` write).\n pruneDescendantKeys(values, path);\n reviveBranch(deleted, path);\n // Baselines under the replaced subtree die with it — before the emit, so\n // subscribers reading dirty state in 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, next);\n bumpDirtyVersion(form);\n bumpValuesVersion(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/** The write of {@link setValueByPath} minus the `'change'` emit: the\n * render-time `useField` `initialValue` seed. The field's first paint\n * (SSR included) must already carry the value, so the write happens during\n * render where emitting is illegal; the field announces it post-commit via\n * {@link emitChangeByPath}. */\nexport function seedValueByPath(form: Form, path: Path, value: any): void {\n const {values, deleted} = form;\n values.set(path.key, value);\n pruneDescendantKeys(values, path);\n reviveBranch(deleted, path);\n pruneDirtyBaselines(form, path);\n bumpDirtyVersion(form);\n bumpValuesVersion(form);\n}\n\n/** Announce a {@link seedValueByPath} that happened during render: the\n * payload-carrying `'change'` emit {@link setValueByPath} would have\n * fired, split out so it runs post-commit where emitting is safe. */\nexport function emitChangeByPath({emitter}: Form, path: Path): void {\n emit(emitter, 'change', path);\n}\n\n/** One field's aggregated state, as {@link getFieldState} returns it.\n * `errors` is the stored array shared with the form — treat it read-only. */\nexport type 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/** Get one field's aggregated state: layered value, first/every error,\n * dirtiness, touched flag, and validating flag. `isDirty` applies the same\n * per-field rule as {@link getDirtyFields}. */\nexport function getFieldState<\n T extends Record<string, any> = any,\n P extends AnyPath<T> = AnyPath<T>\n>(form: Form<T>, name: P): FieldState<PathValueOf<T, P>> {\n const path = createPath(name);\n const {touched, validating} = form;\n return {\n value: getValueByPath(form, path),\n error: getErrorByPath(form, path),\n errors: getFieldErrorsByPath(form, path),\n // The shared per-field rule (committed baselines included): the field\n // is dirty while its live value differs from its effective baseline.\n isDirty: isFieldDirtyByPath(form, path),\n isTouched: touched.has(path.key),\n isValidating: validating.has(path.key)\n };\n}\n\n/** Options accepted by {@link removeField}. All flags default to `false` —\n * the historical remove semantics (value dropped, path tombstoned, dirty\n * baseline/touched/errors cleared). */\nexport type RemoveFieldOptions = {\n /** Keep the field's live value and dirty baseline instead of\n * tombstoning: reads, `getValues()`, and submit keep the value. */\n keepValue?: boolean;\n /** Keep the field's dirty baseline. Implies `keepValue`. */\n keepDirty?: boolean;\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 keepError?: boolean;\n};\n\nexport function removeField(\n form: Form,\n name: Name,\n options?: RemoveFieldOptions\n): void {\n removeFieldByPath(form, createPath(name), options);\n}\n\n/** Remove a field by path; the keep-flags preserve slices of state. */\nexport function removeFieldByPath(\n form: Form,\n path: Path,\n options?: RemoveFieldOptions\n): void {\n const {key, value: segments} = path;\n const {emitter, values, touched, errors, validating, deleted} = form;\n if (!options?.keepValue && !options?.keepDirty) {\n values.delete(key);\n // A remount starts fresh rather than inheriting a baseline committed\n // 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 old initial value. A tombstone never\n // shadows live values: skip it when a live ancestor or descendant key\n // already covers the branch.\n if (!hasLiveBranch(values, segments)) deleted.add(key);\n }\n if (!options?.keepTouched) touched.delete(key);\n if (!options?.keepError && errors.delete(key)) bumpErrorsVersion(form);\n validating.delete(key);\n bumpDirtyVersion(form);\n bumpValuesVersion(form);\n // Path-payload emits, scoped exactly like the writes above (every\n // mutation is bounded to this path's key): leaf watchers on the path and\n // below it wake, ancestor branch watchers wake, and payload-less global\n // listeners wake regardless — while sibling fields stay asleep.\n emit(emitter, 'change', path);\n emit(emitter, 'touched', path);\n emit(emitter, 'errors', path);\n emit(emitter, 'validating', path);\n}\n\ntype ValuesMap = Map<string, any>;\n\n/** Does a live value cover the branch at `segments` — at an ancestor key\n * or below it at a descendant key? */\nfunction hasLiveBranch(values: ValuesMap, segments: PathSegments): 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/** Writing a value replaces the subtree below the written path, so drop\n * the values Map keys under it: they were set against an older generation\n * and would otherwise shadow the fresh value or re-apply over it. */\nfunction pruneDescendantKeys(values: ValuesMap, {key}: Path): void {\n if (!values.size) return;\n const stem = `${key.slice(0, -1)},`;\n for (const k of values.keys()) {\n if (k.startsWith(stem)) values.delete(k);\n }\n}\n\n/** Writing a value revives its whole branch: drop any removal tombstone\n * for the path itself, its ancestors, or its descendants. */\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/** Set form initialValues. Content-based early return: a new reference with\n * equal content is a no-op (committed edits survive); genuinely changed\n * content swaps the baseline and re-seeds — live values and tombstones are\n * cleared, touched flags and errors survive. */\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 and every\n // baseline committed against the old one.\n form.parsedValues = undefined;\n form.values.clear();\n form.deleted.clear();\n clearDirtyBaselines(form);\n bumpDirtyVersion(form);\n bumpValuesVersion(form);\n emit(form.emitter, 'change');\n}\n\n/** Options accepted by {@link reset}. Every flag defaults to `false`. */\nexport type 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 keepDirtyValues?: boolean;\n /** Keep every field's current live value instead of returning to the\n * baseline (a superset of `keepDirtyValues`). Dirtiness is recomputed\n * against the post-reset baseline. */\n keepValues?: boolean;\n /** Ignore a newly provided `initialValues` and keep the current baseline. */\n keepDefaultValues?: 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 (`isSubmitted`) instead of clearing it. */\n keepIsSubmitted?: boolean;\n /** Keep the last submit's success flag (`isSubmitSuccessful`). */\n keepIsSubmitSuccessful?: 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\ntype KeptValue = {segments: PathSegments; value: any};\n\n/** Collect every leaf path of the merged values tree into `out` —\n * structured segments so each leaf can be written back with\n * setValueByPath. Objects with no enumerable keys are leaves themselves. */\nfunction collectValueLeaves(\n node: any,\n segments: PathSegments,\n out: KeptValue[]\n): void {\n if (node !== null && typeof node === 'object') {\n if (Array.isArray(node)) {\n node.forEach((item, i) =>\n collectValueLeaves(item, [...segments, i], out)\n );\n return;\n }\n const keys = Object.keys(node);\n if (keys.length > 0) {\n for (const k of keys) {\n collectValueLeaves(node[k], [...segments, k], out);\n }\n return;\n }\n }\n out.push({segments, value: node});\n}\n\n/** Snapshot the values {@link reset} re-seeds after the wipe: dirtiness is\n * measured against the pre-reset initialValues, so capture must happen\n * before form.values/form.initialValues are touched. The snapshot carries\n * structured segments, not dotted strings — dotted keys don't round-trip\n * through the parser. keepValues keeps every live value; keepDirtyValues\n * narrows the snapshot to fields differing from their effective baseline. */\nfunction collectKeptValues(form: Form, options?: ResetOptions): KeptValue[] {\n const kept: KeptValue[] = [];\n if (options?.keepValues) {\n // Every leaf of the CURRENT merged tree is written back after the\n // wipe, so a field with no live edit keeps its pre-reset value instead\n // of adopting the new baseline.\n collectValueLeaves(getValues(form), [], kept);\n } else if (options?.keepDirtyValues) {\n for (const [key, value] of form.values) {\n const segments = segmentsFromKey(key);\n // Same predicate as getDirtyFields: a live value differing from its\n // effective baseline (committed baselines read clean, not kept).\n if (getDirtyBaseline(form, key, segments) !== value) {\n kept.push({segments, value});\n }\n }\n }\n return kept;\n}\n\n/** Reset the form. Omitted (or undefined) `initialValues` keeps the current\n * baseline — fields simply return to it (react-hook-form's\n * reset-without-values semantics). */\nexport function reset(\n form: Form,\n initialValues?: any,\n options?: ResetOptions\n): void {\n const keptValues = collectKeptValues(form, options);\n // Omitting values is a return-to-initialValues reset, not a wipe: an\n // undefined baseline would make getValues() return undefined, so the\n // current baseline survives when no new one is provided.\n form.initialValues = options?.keepDefaultValues\n ? form.initialValues\n : (initialValues ?? form.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.isSubmitted = false;\n if (!options?.keepIsSubmitSuccessful) form.isSubmitSuccessful = undefined;\n bumpDirtyVersion(form);\n bumpValuesVersion(form);\n // Write the kept values back over the fresh baseline: plain\n // setValueByPath, so no validation fires and nothing is marked touched.\n for (const {segments, value} of keptValues) {\n setValueByPath(form, createPath(segments), 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}. Flags default to `false`;\n * `value` has no default — omitted, the field falls back to initialValues. */\nexport type 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 — never falls back to initialValues. */\n value?: any;\n};\n\n/** Reset a single field: drop its live value (reads fall back to the\n * baseline), clear touched/errors, and revive the path's tombstones — the\n * inverse of {@link removeFieldByPath}. Other fields are untouched. */\nexport function resetField<\n T extends Record<string, any> = any,\n P extends AnyPath<T> = AnyPath<T>\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 // Commits from before the reset no longer shadow the comparison.\n clearDirtyBaselines(form, path.key);\n // A parse baseline wholesale-shadows initialValues in reads, so unset\n // alone would read undefined. Remove the path from the tree (immutable —\n // parsedValues shares branches with the schema's output) and pin the\n // initial value back as the live value 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 by design (unlike removeFieldByPath): reviveBranch can\n // un-tombstone ancestor or descendant paths, whose readers must re-sync.\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 bumpErrorsVersion(form);\n emit(emitter, 'errors', path);\n }\n bumpDirtyVersion(form);\n bumpValuesVersion(form);\n}\n"],"names":["setTouchedByPath","emitter","touched","path","has","key","add","emit","hasTouchedByPath","getTouchedFields","Array","from","segmentsFromKey","join","getValues","form","cache","getOrCreate","valuesCaches","version","result","computeValues","initialValues","parsedValues","values","deleted","owned","Set","merged","value","setOwned","unset","getValueByPath","segments","get","i","length","ancestorKey","JSON","stringify","slice","setValueByPath","options","next","set","pruneDescendantKeys","reviveBranch","pruneDirtyBaselines","shouldDirty","setDirtyBaseline","bumpDirtyVersion","bumpValuesVersion","shouldTouch","shouldValidate","validators","seedValueByPath","emitChangeByPath","removeFieldByPath","errors","validating","delete","clearDirtyBaselines","stem","keys","startsWith","hasLiveBranch","size","k","tombstone","setInitialValues","isEqual","clear","reset","keptValues","clearErrors","isSubmitting","submitCount","isSubmitted","isSubmitSuccessful","createPath"],"mappings":"0LAYO,SAASA,GAAiBC,QAACA,EAAAC,QAASA,GAAgBC,GACrDD,EAAQE,IAAID,EAAKE,OACrBH,EAAQI,IAAIH,EAAKE,KAGjBE,EAAKN,EAAS,UAAWE,GAC3B,CAWO,SAASK,GAAiBN,QAACA,GAAgBC,GAChD,OAAOD,EAAQE,IAAID,EAAKE,IAC1B,CAGO,SAASI,GAAiBP,QAACA,IAChC,OAAOQ,MAAMC,KAAKT,EAASG,GAAOO,EAAgBP,GAAKQ,KAAK,KAC9D,CCJO,SAASC,EACdC,GAEA,MAAMC,EAAQC,EAAYC,EAAcH,EAAM,KAAA,CAC5CI,QAAS,EACTC,OAAQC,EAAcN,MAMxB,OAJIC,EAAMG,QAAU,IAClBH,EAAMI,OAASC,EAAcN,GAC7BC,EAAMG,QAAU,GAEXH,EAAMI,MACf,CAEA,SAASC,EAAcN,GACrB,MAAMO,cAACA,EAAAC,aAAeA,EAAAC,OAAcA,EAAAC,QAAQA,GAAWV,EACjDW,MAAYC,IAClB,IAAIC,EAASL,GAAgBD,EAC7B,IAAA,MAAYjB,EAAKwB,KAAUL,EACzBI,EAASE,EAASF,EAAQhB,EAAgBP,GAAMwB,EAAOH,GAKzD,IAAA,MAAWrB,KAAOoB,EAChBG,EAASG,EAAMH,EAAQhB,EAAgBP,IAKzC,OAAwCuB,CAC1C,CAWO,SAASI,GACdV,cAACA,EAAAC,aAAeA,SAAcC,EAAAC,QAAQA,GACtCtB,GAEA,MAAME,IAACA,EAAKwB,MAAOI,GAAY9B,EAC/B,GAAIqB,EAAOpB,IAAIC,GAAM,OAAOmB,EAAOU,IAAI7B,GAEvC,IAAIoB,EAAQrB,IAAIC,GAAhB,CAQA,IAAA,IAAS8B,EAAIF,EAASG,OAAS,EAAGD,EAAI,EAAGA,IAAK,CAC5C,MAAME,EAAcC,KAAKC,UAAUN,EAASO,MAAM,EAAGL,IACrD,GAAIX,EAAOpB,IAAIiC,GACb,OAAOH,EAAIV,EAAOU,IAAIG,GAAcJ,EAASO,MAAML,GAEvD,CAGA,OAAOD,EAAIX,GAAgBD,EAAeW,EAhBb,CAiB/B,CAmCO,SAASQ,EACd1B,EACAZ,EACA0B,EACAa,GAEA,MAAMzC,QAACA,EAAAuB,OAASA,EAAAC,QAAQA,GAAWV,EAC7B4B,EACa,mBAAVd,EAAuBA,EAAMG,EAAejB,EAAMZ,IAAS0B,EACpEL,EAAOoB,IAAIzC,EAAKE,IAAKsC,GAKrBE,EAAoBrB,EAAQrB,GAC5B2C,EAAarB,EAAStB,GAItB4C,EAAoBhC,EAAMZ,IACG,IAAzBuC,GAASM,aAAuBC,EAAiBlC,EAAMZ,EAAMwC,GACjEO,EAAiBnC,GACjBoC,EAAkBpC,GACd2B,GAASU,aAAapD,EAAiBe,EAAMZ,GAC7CuC,GAASW,gBAAgBtC,EAAKuC,WAAWpB,IAAI/B,EAAKE,IAAzBU,KAC7BR,EAAKN,EAAS,SAAUE,EAC1B,CAOO,SAASoD,EAAgBxC,EAAYZ,EAAY0B,GACtD,MAAML,OAACA,EAAAC,QAAQA,GAAWV,EAC1BS,EAAOoB,IAAIzC,EAAKE,IAAKwB,GACrBgB,EAAoBrB,EAAQrB,GAC5B2C,EAAarB,EAAStB,GACtB4C,EAAoBhC,EAAMZ,GAC1B+C,EAAiBnC,GACjBoC,EAAkBpC,EACpB,CAKO,SAASyC,GAAiBvD,QAACA,GAAgBE,GAChDI,EAAKN,EAAS,SAAUE,EAC1B,CA0DO,SAASsD,EACd1C,EACAZ,EACAuC,GAEA,MAAMrC,IAACA,EAAKwB,MAAOI,GAAY9B,GACzBF,QAACA,EAAAuB,OAASA,EAAAtB,QAAQA,SAASwD,EAAAC,WAAQA,EAAAlC,QAAYA,GAAWV,EAE9DS,EAAOoC,OAAOvD,GAGdwD,EAAoB9C,EAAMV,GA0B9B,SAAuBmB,EAAmBS,GACxC,IAAA,IAASE,EAAI,EAAGA,EAAIF,EAASG,OAAQD,IACnC,GAAIX,EAAOpB,IAAIkC,KAAKC,UAAUN,EAASO,MAAM,EAAGL,KAAM,OAAO,EAE/D,MAAM2B,EAAO,GAAGxB,KAAKC,UAAUN,GAAUO,MAAM,SAC/C,IAAA,MAAWnC,KAAOmB,EAAOuC,OACvB,GAAI1D,EAAI2D,WAAWF,GAAO,OAAO,EAEnC,OAAO,CACT,CA9BSG,CAAczC,EAAQS,IAAWR,EAAQnB,IAAID,GAEzBH,EAAQ0D,OAAOvD,GACfqD,EAAOE,OAAOvD,MAAwBU,GACjE4C,EAAWC,OAAOvD,GAClB6C,EAAiBnC,GACjBoC,EAAkBpC,GAKlBR,EAAKN,EAAS,SAAUE,GACxBI,EAAKN,EAAS,UAAWE,GACzBI,EAAKN,EAAS,SAAUE,GACxBI,EAAKN,EAAS,aAAcE,EAC9B,CAoBA,SAAS0C,EAAoBrB,GAAmBnB,IAACA,IAC/C,IAAKmB,EAAO0C,KAAM,OAClB,MAAMJ,EAAO,GAAGzD,EAAImC,MAAM,GAAG,MAC7B,IAAA,MAAW2B,KAAK3C,EAAOuC,OACjBI,EAAEH,WAAWF,IAAOtC,EAAOoC,OAAOO,EAE1C,CAIA,SAASrB,EAAarB,GAAsBpB,IAACA,IAC3C,GAAKoB,EAAQyC,KACb,IAAA,MAAWE,KAAa3C,GAEpB2C,IAAc/D,GACd+D,EAAUJ,WAAW,GAAG3D,EAAImC,MAAM,GAAG,QACrCnC,EAAI2D,WAAW,GAAGI,EAAU5B,MAAM,GAAG,SAErCf,EAAQmC,OAAOQ,EAGrB,CAMO,SAASC,EAAiBtD,EAAYO,GAEzCP,EAAKO,gBAAkBA,GACvBgD,EAAQvD,EAAKO,cAAeA,KAI9BP,EAAKO,cAAgBA,EAGrBP,EAAKQ,kBAAe,EACpBR,EAAKS,OAAO+C,QACZxD,EAAKU,QAAQ8C,QACbV,EAAoB9C,GACpBmC,EAAiBnC,GACjBoC,EAAkBpC,GAClBR,EAAKQ,EAAKd,QAAS,UACrB,CAoFO,SAASuE,EACdzD,EACAO,EACAoB,GAEA,MAAM+B,EA3BoB,GA+B1B1D,EAAKO,cAEAA,GAAiBP,EAAKO,cAE3BP,EAAKQ,kBAAe,EACMmD,EAAY3D,GACtC,MAAMd,QAACA,EAAAC,QAASA,EAAAsB,OAASA,EAAAC,QAAQA,EAAAkC,WAASA,GAAc5C,EACxDS,EAAO+C,QACP9C,EAAQ8C,QACRV,EAAoB9C,GACOb,EAAQqE,QACnCZ,EAAWY,QACqBxD,EAAK4D,cAAe,EACrB5D,EAAK6D,YAAc,EACnB7D,EAAK8D,aAAc,EACZ9D,EAAK+D,wBAAqB,EAChE5B,EAAiBnC,GACjBoC,EAAkBpC,GAGlB,IAAA,MAAWkB,SAACA,EAAAJ,MAAUA,KAAU4C,EAC9BhC,EAAe1B,EAAMgE,EAAW9C,GAAWJ,GAE7CtB,EAAKN,EAAS,UACdM,EAAKN,EAAS,WACdM,EAAKN,EAAS,cACdM,EAAKN,EAAS,cACdM,EAAKN,EAAS,eACdM,EAAKN,EAAS,oBACdM,EAAKN,EAAS,QAChB"}