[{"data":1,"prerenderedAt":2482},["ShallowReactive",2],{"content-\u002Fdocs\u002Frecipes\u002Fcustom-adapter":3},{"id":4,"title":5,"body":6,"description":2475,"extension":2476,"meta":2477,"navigation":575,"path":2478,"seo":2479,"stem":2480,"__hash__":2481},"docs\u002Fdocs\u002Frecipes\u002Fcustom-adapter.md","Plug in your own schema library",{"type":7,"value":8,"toc":2461},"minimark",[9,13,22,27,30,283,441,451,465,472,476,479,499,1534,1544,1553,1557,1727,1737,1740,1750,1753,1781,1784,1807,1814,1821,1824,1830,1866,1871,1883,1983,1996,2000,2101,2105,2112,2166,2170,2176,2191,2194,2198,2212,2308,2317,2321,2324,2436,2457],[10,11,5],"h1",{"id":12},"plug-in-your-own-schema-library",[14,15,16,17,21],"p",{},"Attaform is schema-agnostic. Zod is just one implementation of\nthe internal ",[18,19,20],"code",{},"AbstractSchema"," contract. If you're on Valibot,\nArkType, Effect-Schema, or a hand-rolled validator, wire yours in\nwithout forking the library.",[23,24,26],"h2",{"id":25},"the-contract","The contract",[14,28,29],{},"Seven methods:",[31,32,37],"pre",{"className":33,"code":34,"language":35,"meta":36,"style":36},"language-ts shiki shiki-themes github-light github-dark","type AbstractSchema\u003CForm, GetValueFormType = Form> = {\n  fingerprint(): string\n  getDefaultValues(config): DefaultValuesResponse\u003CForm>\n  getDefaultAtPath(path: Path): unknown\n  getSchemasAtPath(path: Path): AbstractSchema\u003Cunknown, GetValueFormType>[]\n  getSlimPrimitiveTypesAtPath(path: Path): Set\u003CSlimPrimitiveKind>\n  isRequiredAtPath(path: Path): boolean\n  validateAtPath(data: unknown, path: Path | undefined): Promise\u003CValidationResponse\u003CForm>>\n}\n","ts","",[18,38,39,80,96,124,147,179,207,228,277],{"__ignoreMap":36},[40,41,44,48,52,56,59,62,65,68,71,74,77],"span",{"class":42,"line":43},"line",1,[40,45,47],{"class":46},"szBVR","type",[40,49,51],{"class":50},"sScJk"," AbstractSchema",[40,53,55],{"class":54},"sVt8B","\u003C",[40,57,58],{"class":50},"Form",[40,60,61],{"class":54},", ",[40,63,64],{"class":50},"GetValueFormType",[40,66,67],{"class":46}," =",[40,69,70],{"class":50}," Form",[40,72,73],{"class":54},"> ",[40,75,76],{"class":46},"=",[40,78,79],{"class":54}," {\n",[40,81,83,86,89,92],{"class":42,"line":82},2,[40,84,85],{"class":50},"  fingerprint",[40,87,88],{"class":54},"()",[40,90,91],{"class":46},":",[40,93,95],{"class":94},"sj4cs"," string\n",[40,97,99,102,105,109,112,114,117,119,121],{"class":42,"line":98},3,[40,100,101],{"class":50},"  getDefaultValues",[40,103,104],{"class":54},"(",[40,106,108],{"class":107},"s4XuR","config",[40,110,111],{"class":54},")",[40,113,91],{"class":46},[40,115,116],{"class":50}," DefaultValuesResponse",[40,118,55],{"class":54},[40,120,58],{"class":50},[40,122,123],{"class":54},">\n",[40,125,127,130,132,135,137,140,142,144],{"class":42,"line":126},4,[40,128,129],{"class":50},"  getDefaultAtPath",[40,131,104],{"class":54},[40,133,134],{"class":107},"path",[40,136,91],{"class":46},[40,138,139],{"class":50}," Path",[40,141,111],{"class":54},[40,143,91],{"class":46},[40,145,146],{"class":94}," unknown\n",[40,148,150,153,155,157,159,161,163,165,167,169,172,174,176],{"class":42,"line":149},5,[40,151,152],{"class":50},"  getSchemasAtPath",[40,154,104],{"class":54},[40,156,134],{"class":107},[40,158,91],{"class":46},[40,160,139],{"class":50},[40,162,111],{"class":54},[40,164,91],{"class":46},[40,166,51],{"class":50},[40,168,55],{"class":54},[40,170,171],{"class":94},"unknown",[40,173,61],{"class":54},[40,175,64],{"class":50},[40,177,178],{"class":54},">[]\n",[40,180,182,185,187,189,191,193,195,197,200,202,205],{"class":42,"line":181},6,[40,183,184],{"class":50},"  getSlimPrimitiveTypesAtPath",[40,186,104],{"class":54},[40,188,134],{"class":107},[40,190,91],{"class":46},[40,192,139],{"class":50},[40,194,111],{"class":54},[40,196,91],{"class":46},[40,198,199],{"class":50}," Set",[40,201,55],{"class":54},[40,203,204],{"class":50},"SlimPrimitiveKind",[40,206,123],{"class":54},[40,208,210,213,215,217,219,221,223,225],{"class":42,"line":209},7,[40,211,212],{"class":50},"  isRequiredAtPath",[40,214,104],{"class":54},[40,216,134],{"class":107},[40,218,91],{"class":46},[40,220,139],{"class":50},[40,222,111],{"class":54},[40,224,91],{"class":46},[40,226,227],{"class":94}," boolean\n",[40,229,231,234,236,239,241,244,246,248,250,252,255,258,260,262,265,267,270,272,274],{"class":42,"line":230},8,[40,232,233],{"class":50},"  validateAtPath",[40,235,104],{"class":54},[40,237,238],{"class":107},"data",[40,240,91],{"class":46},[40,242,243],{"class":94}," unknown",[40,245,61],{"class":54},[40,247,134],{"class":107},[40,249,91],{"class":46},[40,251,139],{"class":50},[40,253,254],{"class":46}," |",[40,256,257],{"class":94}," undefined",[40,259,111],{"class":54},[40,261,91],{"class":46},[40,263,264],{"class":50}," Promise",[40,266,55],{"class":54},[40,268,269],{"class":50},"ValidationResponse",[40,271,55],{"class":54},[40,273,58],{"class":50},[40,275,276],{"class":54},">>\n",[40,278,280],{"class":42,"line":279},9,[40,281,282],{"class":54},"}\n",[284,285,286,302,317,334,356,385,420],"ul",{},[287,288,289,295,296,301],"li",{},[290,291,292],"strong",{},[18,293,294],{},"fingerprint()"," — structural signature of the schema. Two\nschemas with the same shape must return the same string; two\nschemas with different shapes should (best-effort) return\ndifferent strings. Used to detect shared-key mismatches AND to\nkey persisted drafts — see\n",[297,298,300],"a",{"href":299},"#fingerprint-implementation","Fingerprint implementation",".",[287,303,304,309,310,313,314,301],{},[290,305,306],{},[18,307,308],{},"getDefaultValues({ useDefaultSchemaValues, constraints, strict })","\n— returns ",[18,311,312],{},"{ data, errors, success, formKey }",". Called at form\ncreation and on ",[18,315,316],{},"reset()",[287,318,319,324,325,328,329,333],{},[290,320,321],{},[18,322,323],{},"getDefaultAtPath(path)"," — returns the schema-prescribed\ndefault at a structured path. The runtime calls this on every\n",[18,326,327],{},"setValue"," to fill structural gaps. See\n",[297,330,332],{"href":331},"#getdefaultatpath","getDefaultAtPath"," below.",[287,335,336,341,342,344,345,347,348,351,352,355],{},[290,337,338],{},[18,339,340],{},"getSchemasAtPath(path)"," — returns the list of sub-schemas\nat ",[18,343,134],{},". ",[18,346,134],{}," is the canonical ",[18,349,350],{},"Segment[]",", not a dotted\nstring. Advanced introspection hook; return ",[18,353,354],{},"[]"," if you don't\nuse it.",[287,357,358,363,364,367,368,61,371,61,374,61,377,380,381,384],{},[290,359,360],{},[18,361,362],{},"getSlimPrimitiveTypesAtPath(path)"," — returns the set of\nprimitive ",[18,365,366],{},"typeof","-style kinds the path's leaf accepts at write\ntime (",[18,369,370],{},"'string'",[18,372,373],{},"'number'",[18,375,376],{},"'boolean'",[18,378,379],{},"'bigint'",", etc.). The\nruntime calls this to gate the slim-primitive write contract.\nReturn ",[18,382,383],{},"PERMISSIVE"," for paths the schema doesn't declare so\ndynamic writes don't get rejected.",[287,386,387,392,393,396,397,400,401,400,404,407,408,411,412,415,416,419],{},[290,388,389],{},[18,390,391],{},"isRequiredAtPath(path)"," — returns ",[18,394,395],{},"true"," when the leaf is\nrequired (no ",[18,398,399],{},".optional()"," \u002F ",[18,402,403],{},".nullable()",[18,405,406],{},".default()"," \u002F\n",[18,409,410],{},".catch()"," wrapper). Used by the blank validation augmentation\nto raise ",[18,413,414],{},"'No value supplied'"," for unfilled required fields.\nReturn ",[18,417,418],{},"false"," for any wrapper that admits the empty case.",[287,421,422,427,428,344,431,433,434,436,437,440],{},[290,423,424],{},[18,425,426],{},"validateAtPath(data, path?)"," — returns\n",[18,429,430],{},"Promise\u003CValidationResponse>",[18,432,134],{}," is a ",[18,435,350],{}," or\n",[18,438,439],{},"undefined"," (whole-form validation).",[14,442,443,446,447,450],{},[18,444,445],{},"validateAtPath"," must NOT throw. Return ",[18,448,449],{},"{ success: false, errors }"," for validation failures; return (or reject with) a synthetic\nerror only if your parser is genuinely misbehaving.",[14,452,453,456,457,460,461,464],{},[18,454,455],{},"fingerprint"," must NOT throw either. If it does, the library\ncatches the exception, logs it via ",[18,458,459],{},"console.error"," in dev, and\nskips the shared-key mismatch check for that call. An opaque\nstable string (",[18,462,463],{},"'custom-adapter:v1'",") is a valid fallback when\nyour schema library is hard to introspect — note that opaque\nfingerprints disable schema-change auto-invalidation for\npersisted drafts (the key never changes), so prefer a real\nstructural hash if your library exposes the metadata.",[14,466,467,446,469,471],{},[18,468,332],{},[18,470,439],{}," for paths\nthat don't exist in the schema; the runtime treats that as\n\"don't fill\" and falls back to the existing data.",[23,473,475],{"id":474},"a-minimal-valibot-ish-adapter","A minimal Valibot-ish adapter",[14,477,478],{},"Assume your library exposes:",[284,480,481,487],{},[287,482,483,486],{},[18,484,485],{},"schema.defaultValues()"," returning the schema's typed defaults.",[287,488,489,492,493,436,496,301],{},[18,490,491],{},"schema.parse(data)"," returning\n",[18,494,495],{},"{ success: true, data }",[18,497,498],{},"{ success: false, issues: { path: string[]; message: string }[] }",[31,500,502],{"className":33,"code":501,"language":35,"meta":36,"style":36},"\u002F\u002F adapter.ts\nimport type {\n  AbstractSchema,\n  DefaultValuesResponse,\n  SlimPrimitiveKind,\n  ValidationError,\n  ValidationResponse,\n} from 'attaform'\nimport type { DeepPartial, GenericForm } from 'attaform'\n\n\u002F\u002F Permissive fallback used by `getSlimPrimitiveTypesAtPath` when the\n\u002F\u002F schema doesn't declare a path. Adapter-specific — your library's\n\u002F\u002F supported primitive kinds may differ.\nconst PERMISSIVE: ReadonlySet\u003CSlimPrimitiveKind> = new Set\u003CSlimPrimitiveKind>([\n  'string',\n  'number',\n  'boolean',\n  'bigint',\n  'symbol',\n  'date',\n  'undefined',\n  'null',\n])\n\nexport function myLibAdapter\u003CF extends GenericForm>(schema: MyLibSchema\u003CF>): AbstractSchema\u003CF, F> {\n  return {\n    fingerprint() {\n      \u002F\u002F If your library exposes structural metadata, walk it and\n      \u002F\u002F hash; the Zod adapters do this. Otherwise, a stable\n      \u002F\u002F opaque string per schema instance is a valid fallback:\n      \u002F\u002F it disables cross-instance mismatch detection but never\n      \u002F\u002F false-positives.\n      return schema.signature?.() ?? 'my-lib:v1'\n    },\n\n    getDefaultValues({ constraints }): DefaultValuesResponse\u003CF> {\n      const defaults = schema.defaultValues()\n      const merged = mergeDeepPartial(defaults, constraints)\n      return { data: merged, errors: undefined, success: true, formKey: '' }\n    },\n\n    getDefaultAtPath(path) {\n      \u002F\u002F Walk the schema to `path` and return the default at that\n      \u002F\u002F node. The runtime uses this to fill structural gaps on\n      \u002F\u002F every setValue (sparse array writes, partial object writes,\n      \u002F\u002F path-form callback prev auto-default).\n      \u002F\u002F\n      \u002F\u002F Concretely: empty path → whole-form default; object property\n      \u002F\u002F → property's default; array index → element default; tuple\n      \u002F\u002F position → position's default; optional\u002Fnullable around a\n      \u002F\u002F structural inner → inner default; optional\u002Fnullable around\n      \u002F\u002F a primitive → undefined \u002F null (preserve the wrapper's\n      \u002F\u002F semantic); .default(x) wrapper → x. Return undefined for\n      \u002F\u002F paths that don't exist in the schema.\n      return walkSchemaToDefault(schema, path)\n    },\n\n    getSchemasAtPath(_path) {\n      return []\n    },\n\n    getSlimPrimitiveTypesAtPath(path) {\n      \u002F\u002F Return the set of primitive `typeof`-style kinds the leaf\n      \u002F\u002F at `path` accepts. Pick a sensible permissive fallback for\n      \u002F\u002F unknown paths — over-rejecting writes here breaks dynamic\n      \u002F\u002F \u002F SSR-rehydration flows.\n      return walkSchemaToSlimPrimitives(schema, path) ?? PERMISSIVE\n    },\n\n    isRequiredAtPath(path) {\n      \u002F\u002F Return true when the leaf is required. A wrapper around the\n      \u002F\u002F leaf that admits the empty case (Optional, Nullable, Default,\n      \u002F\u002F Catch) means the leaf is NOT required — return false.\n      const leaf = walkSchemaToLeaf(schema, path)\n      return leaf !== undefined && !isOptionalLikeWrapper(leaf)\n    },\n\n    async validateAtPath(data, _path): Promise\u003CValidationResponse\u003CF>> {\n      const result = schema.parse(data)\n      if (result.success) {\n        return { data: result.data as F, errors: undefined, success: true, formKey: '' }\n      }\n      return {\n        data: undefined,\n        errors: result.issues.map\u003CValidationError>((issue) => ({\n          path: issue.path,\n          message: issue.message,\n          formKey: '',\n          \u002F\u002F Pick a stable scope prefix for your adapter and forward\n          \u002F\u002F the library's issue code under it. Consumers branch on\n          \u002F\u002F `error.code` for adapter-agnostic UI logic.\n          code: `mylib:${issue.code ?? 'unknown'}`,\n        })),\n        success: false,\n        formKey: '',\n      }\n    },\n  }\n}\n\nfunction mergeDeepPartial\u003CT>(base: T, override?: DeepPartial\u003CT>): T {\n  \u002F\u002F Dependency-free deep merge; see test\u002Futils\u002Ffake-schema.ts for a\n  \u002F\u002F reference implementation.\n}\n",[18,503,504,510,520,525,530,535,540,545,557,570,577,583,589,595,629,638,646,654,662,670,678,686,694,700,705,761,769,778,784,790,796,802,808,829,835,840,865,884,900,924,929,934,947,953,959,965,971,977,983,989,995,1001,1007,1013,1019,1030,1035,1040,1053,1061,1066,1071,1083,1089,1095,1101,1107,1123,1128,1133,1145,1151,1157,1163,1178,1203,1208,1213,1247,1265,1274,1304,1310,1317,1327,1356,1362,1368,1378,1384,1390,1396,1419,1425,1435,1445,1450,1455,1461,1466,1471,1517,1523,1529],{"__ignoreMap":36},[40,505,506],{"class":42,"line":43},[40,507,509],{"class":508},"sJ8bj","\u002F\u002F adapter.ts\n",[40,511,512,515,518],{"class":42,"line":82},[40,513,514],{"class":46},"import",[40,516,517],{"class":46}," type",[40,519,79],{"class":54},[40,521,522],{"class":42,"line":98},[40,523,524],{"class":54},"  AbstractSchema,\n",[40,526,527],{"class":42,"line":126},[40,528,529],{"class":54},"  DefaultValuesResponse,\n",[40,531,532],{"class":42,"line":149},[40,533,534],{"class":54},"  SlimPrimitiveKind,\n",[40,536,537],{"class":42,"line":181},[40,538,539],{"class":54},"  ValidationError,\n",[40,541,542],{"class":42,"line":209},[40,543,544],{"class":54},"  ValidationResponse,\n",[40,546,547,550,553],{"class":42,"line":230},[40,548,549],{"class":54},"} ",[40,551,552],{"class":46},"from",[40,554,556],{"class":555},"sZZnC"," 'attaform'\n",[40,558,559,561,563,566,568],{"class":42,"line":279},[40,560,514],{"class":46},[40,562,517],{"class":46},[40,564,565],{"class":54}," { DeepPartial, GenericForm } ",[40,567,552],{"class":46},[40,569,556],{"class":555},[40,571,573],{"class":42,"line":572},10,[40,574,576],{"emptyLinePlaceholder":575},true,"\n",[40,578,580],{"class":42,"line":579},11,[40,581,582],{"class":508},"\u002F\u002F Permissive fallback used by `getSlimPrimitiveTypesAtPath` when the\n",[40,584,586],{"class":42,"line":585},12,[40,587,588],{"class":508},"\u002F\u002F schema doesn't declare a path. Adapter-specific — your library's\n",[40,590,592],{"class":42,"line":591},13,[40,593,594],{"class":508},"\u002F\u002F supported primitive kinds may differ.\n",[40,596,598,601,604,606,609,611,613,615,617,620,622,624,626],{"class":42,"line":597},14,[40,599,600],{"class":46},"const",[40,602,603],{"class":94}," PERMISSIVE",[40,605,91],{"class":46},[40,607,608],{"class":50}," ReadonlySet",[40,610,55],{"class":54},[40,612,204],{"class":50},[40,614,73],{"class":54},[40,616,76],{"class":46},[40,618,619],{"class":46}," new",[40,621,199],{"class":50},[40,623,55],{"class":54},[40,625,204],{"class":50},[40,627,628],{"class":54},">([\n",[40,630,632,635],{"class":42,"line":631},15,[40,633,634],{"class":555},"  'string'",[40,636,637],{"class":54},",\n",[40,639,641,644],{"class":42,"line":640},16,[40,642,643],{"class":555},"  'number'",[40,645,637],{"class":54},[40,647,649,652],{"class":42,"line":648},17,[40,650,651],{"class":555},"  'boolean'",[40,653,637],{"class":54},[40,655,657,660],{"class":42,"line":656},18,[40,658,659],{"class":555},"  'bigint'",[40,661,637],{"class":54},[40,663,665,668],{"class":42,"line":664},19,[40,666,667],{"class":555},"  'symbol'",[40,669,637],{"class":54},[40,671,673,676],{"class":42,"line":672},20,[40,674,675],{"class":555},"  'date'",[40,677,637],{"class":54},[40,679,681,684],{"class":42,"line":680},21,[40,682,683],{"class":555},"  'undefined'",[40,685,637],{"class":54},[40,687,689,692],{"class":42,"line":688},22,[40,690,691],{"class":555},"  'null'",[40,693,637],{"class":54},[40,695,697],{"class":42,"line":696},23,[40,698,699],{"class":54},"])\n",[40,701,703],{"class":42,"line":702},24,[40,704,576],{"emptyLinePlaceholder":575},[40,706,708,711,714,717,719,722,725,728,731,734,736,739,741,743,746,748,750,752,754,756,758],{"class":42,"line":707},25,[40,709,710],{"class":46},"export",[40,712,713],{"class":46}," function",[40,715,716],{"class":50}," myLibAdapter",[40,718,55],{"class":54},[40,720,721],{"class":50},"F",[40,723,724],{"class":46}," extends",[40,726,727],{"class":50}," GenericForm",[40,729,730],{"class":54},">(",[40,732,733],{"class":107},"schema",[40,735,91],{"class":46},[40,737,738],{"class":50}," MyLibSchema",[40,740,55],{"class":54},[40,742,721],{"class":50},[40,744,745],{"class":54},">)",[40,747,91],{"class":46},[40,749,51],{"class":50},[40,751,55],{"class":54},[40,753,721],{"class":50},[40,755,61],{"class":54},[40,757,721],{"class":50},[40,759,760],{"class":54},"> {\n",[40,762,764,767],{"class":42,"line":763},26,[40,765,766],{"class":46},"  return",[40,768,79],{"class":54},[40,770,772,775],{"class":42,"line":771},27,[40,773,774],{"class":50},"    fingerprint",[40,776,777],{"class":54},"() {\n",[40,779,781],{"class":42,"line":780},28,[40,782,783],{"class":508},"      \u002F\u002F If your library exposes structural metadata, walk it and\n",[40,785,787],{"class":42,"line":786},29,[40,788,789],{"class":508},"      \u002F\u002F hash; the Zod adapters do this. Otherwise, a stable\n",[40,791,793],{"class":42,"line":792},30,[40,794,795],{"class":508},"      \u002F\u002F opaque string per schema instance is a valid fallback:\n",[40,797,799],{"class":42,"line":798},31,[40,800,801],{"class":508},"      \u002F\u002F it disables cross-instance mismatch detection but never\n",[40,803,805],{"class":42,"line":804},32,[40,806,807],{"class":508},"      \u002F\u002F false-positives.\n",[40,809,811,814,817,820,823,826],{"class":42,"line":810},33,[40,812,813],{"class":46},"      return",[40,815,816],{"class":54}," schema.",[40,818,819],{"class":50},"signature",[40,821,822],{"class":54},"?.() ",[40,824,825],{"class":46},"??",[40,827,828],{"class":555}," 'my-lib:v1'\n",[40,830,832],{"class":42,"line":831},34,[40,833,834],{"class":54},"    },\n",[40,836,838],{"class":42,"line":837},35,[40,839,576],{"emptyLinePlaceholder":575},[40,841,843,846,849,852,855,857,859,861,863],{"class":42,"line":842},36,[40,844,845],{"class":50},"    getDefaultValues",[40,847,848],{"class":54},"({ ",[40,850,851],{"class":107},"constraints",[40,853,854],{"class":54}," })",[40,856,91],{"class":46},[40,858,116],{"class":50},[40,860,55],{"class":54},[40,862,721],{"class":50},[40,864,760],{"class":54},[40,866,868,871,874,876,878,881],{"class":42,"line":867},37,[40,869,870],{"class":46},"      const",[40,872,873],{"class":94}," defaults",[40,875,67],{"class":46},[40,877,816],{"class":54},[40,879,880],{"class":50},"defaultValues",[40,882,883],{"class":54},"()\n",[40,885,887,889,892,894,897],{"class":42,"line":886},38,[40,888,870],{"class":46},[40,890,891],{"class":94}," merged",[40,893,67],{"class":46},[40,895,896],{"class":50}," mergeDeepPartial",[40,898,899],{"class":54},"(defaults, constraints)\n",[40,901,903,905,908,910,913,915,918,921],{"class":42,"line":902},39,[40,904,813],{"class":46},[40,906,907],{"class":54}," { data: merged, errors: ",[40,909,439],{"class":94},[40,911,912],{"class":54},", success: ",[40,914,395],{"class":94},[40,916,917],{"class":54},", formKey: ",[40,919,920],{"class":555},"''",[40,922,923],{"class":54}," }\n",[40,925,927],{"class":42,"line":926},40,[40,928,834],{"class":54},[40,930,932],{"class":42,"line":931},41,[40,933,576],{"emptyLinePlaceholder":575},[40,935,937,940,942,944],{"class":42,"line":936},42,[40,938,939],{"class":50},"    getDefaultAtPath",[40,941,104],{"class":54},[40,943,134],{"class":107},[40,945,946],{"class":54},") {\n",[40,948,950],{"class":42,"line":949},43,[40,951,952],{"class":508},"      \u002F\u002F Walk the schema to `path` and return the default at that\n",[40,954,956],{"class":42,"line":955},44,[40,957,958],{"class":508},"      \u002F\u002F node. The runtime uses this to fill structural gaps on\n",[40,960,962],{"class":42,"line":961},45,[40,963,964],{"class":508},"      \u002F\u002F every setValue (sparse array writes, partial object writes,\n",[40,966,968],{"class":42,"line":967},46,[40,969,970],{"class":508},"      \u002F\u002F path-form callback prev auto-default).\n",[40,972,974],{"class":42,"line":973},47,[40,975,976],{"class":508},"      \u002F\u002F\n",[40,978,980],{"class":42,"line":979},48,[40,981,982],{"class":508},"      \u002F\u002F Concretely: empty path → whole-form default; object property\n",[40,984,986],{"class":42,"line":985},49,[40,987,988],{"class":508},"      \u002F\u002F → property's default; array index → element default; tuple\n",[40,990,992],{"class":42,"line":991},50,[40,993,994],{"class":508},"      \u002F\u002F position → position's default; optional\u002Fnullable around a\n",[40,996,998],{"class":42,"line":997},51,[40,999,1000],{"class":508},"      \u002F\u002F structural inner → inner default; optional\u002Fnullable around\n",[40,1002,1004],{"class":42,"line":1003},52,[40,1005,1006],{"class":508},"      \u002F\u002F a primitive → undefined \u002F null (preserve the wrapper's\n",[40,1008,1010],{"class":42,"line":1009},53,[40,1011,1012],{"class":508},"      \u002F\u002F semantic); .default(x) wrapper → x. Return undefined for\n",[40,1014,1016],{"class":42,"line":1015},54,[40,1017,1018],{"class":508},"      \u002F\u002F paths that don't exist in the schema.\n",[40,1020,1022,1024,1027],{"class":42,"line":1021},55,[40,1023,813],{"class":46},[40,1025,1026],{"class":50}," walkSchemaToDefault",[40,1028,1029],{"class":54},"(schema, path)\n",[40,1031,1033],{"class":42,"line":1032},56,[40,1034,834],{"class":54},[40,1036,1038],{"class":42,"line":1037},57,[40,1039,576],{"emptyLinePlaceholder":575},[40,1041,1043,1046,1048,1051],{"class":42,"line":1042},58,[40,1044,1045],{"class":50},"    getSchemasAtPath",[40,1047,104],{"class":54},[40,1049,1050],{"class":107},"_path",[40,1052,946],{"class":54},[40,1054,1056,1058],{"class":42,"line":1055},59,[40,1057,813],{"class":46},[40,1059,1060],{"class":54}," []\n",[40,1062,1064],{"class":42,"line":1063},60,[40,1065,834],{"class":54},[40,1067,1069],{"class":42,"line":1068},61,[40,1070,576],{"emptyLinePlaceholder":575},[40,1072,1074,1077,1079,1081],{"class":42,"line":1073},62,[40,1075,1076],{"class":50},"    getSlimPrimitiveTypesAtPath",[40,1078,104],{"class":54},[40,1080,134],{"class":107},[40,1082,946],{"class":54},[40,1084,1086],{"class":42,"line":1085},63,[40,1087,1088],{"class":508},"      \u002F\u002F Return the set of primitive `typeof`-style kinds the leaf\n",[40,1090,1092],{"class":42,"line":1091},64,[40,1093,1094],{"class":508},"      \u002F\u002F at `path` accepts. Pick a sensible permissive fallback for\n",[40,1096,1098],{"class":42,"line":1097},65,[40,1099,1100],{"class":508},"      \u002F\u002F unknown paths — over-rejecting writes here breaks dynamic\n",[40,1102,1104],{"class":42,"line":1103},66,[40,1105,1106],{"class":508},"      \u002F\u002F \u002F SSR-rehydration flows.\n",[40,1108,1110,1112,1115,1118,1120],{"class":42,"line":1109},67,[40,1111,813],{"class":46},[40,1113,1114],{"class":50}," walkSchemaToSlimPrimitives",[40,1116,1117],{"class":54},"(schema, path) ",[40,1119,825],{"class":46},[40,1121,1122],{"class":94}," PERMISSIVE\n",[40,1124,1126],{"class":42,"line":1125},68,[40,1127,834],{"class":54},[40,1129,1131],{"class":42,"line":1130},69,[40,1132,576],{"emptyLinePlaceholder":575},[40,1134,1136,1139,1141,1143],{"class":42,"line":1135},70,[40,1137,1138],{"class":50},"    isRequiredAtPath",[40,1140,104],{"class":54},[40,1142,134],{"class":107},[40,1144,946],{"class":54},[40,1146,1148],{"class":42,"line":1147},71,[40,1149,1150],{"class":508},"      \u002F\u002F Return true when the leaf is required. A wrapper around the\n",[40,1152,1154],{"class":42,"line":1153},72,[40,1155,1156],{"class":508},"      \u002F\u002F leaf that admits the empty case (Optional, Nullable, Default,\n",[40,1158,1160],{"class":42,"line":1159},73,[40,1161,1162],{"class":508},"      \u002F\u002F Catch) means the leaf is NOT required — return false.\n",[40,1164,1166,1168,1171,1173,1176],{"class":42,"line":1165},74,[40,1167,870],{"class":46},[40,1169,1170],{"class":94}," leaf",[40,1172,67],{"class":46},[40,1174,1175],{"class":50}," walkSchemaToLeaf",[40,1177,1029],{"class":54},[40,1179,1181,1183,1186,1189,1191,1194,1197,1200],{"class":42,"line":1180},75,[40,1182,813],{"class":46},[40,1184,1185],{"class":54}," leaf ",[40,1187,1188],{"class":46},"!==",[40,1190,257],{"class":94},[40,1192,1193],{"class":46}," &&",[40,1195,1196],{"class":46}," !",[40,1198,1199],{"class":50},"isOptionalLikeWrapper",[40,1201,1202],{"class":54},"(leaf)\n",[40,1204,1206],{"class":42,"line":1205},76,[40,1207,834],{"class":54},[40,1209,1211],{"class":42,"line":1210},77,[40,1212,576],{"emptyLinePlaceholder":575},[40,1214,1216,1219,1222,1224,1226,1228,1230,1232,1234,1236,1238,1240,1242,1244],{"class":42,"line":1215},78,[40,1217,1218],{"class":46},"    async",[40,1220,1221],{"class":50}," validateAtPath",[40,1223,104],{"class":54},[40,1225,238],{"class":107},[40,1227,61],{"class":54},[40,1229,1050],{"class":107},[40,1231,111],{"class":54},[40,1233,91],{"class":46},[40,1235,264],{"class":50},[40,1237,55],{"class":54},[40,1239,269],{"class":50},[40,1241,55],{"class":54},[40,1243,721],{"class":50},[40,1245,1246],{"class":54},">> {\n",[40,1248,1250,1252,1255,1257,1259,1262],{"class":42,"line":1249},79,[40,1251,870],{"class":46},[40,1253,1254],{"class":94}," result",[40,1256,67],{"class":46},[40,1258,816],{"class":54},[40,1260,1261],{"class":50},"parse",[40,1263,1264],{"class":54},"(data)\n",[40,1266,1268,1271],{"class":42,"line":1267},80,[40,1269,1270],{"class":46},"      if",[40,1272,1273],{"class":54}," (result.success) {\n",[40,1275,1277,1280,1283,1286,1289,1292,1294,1296,1298,1300,1302],{"class":42,"line":1276},81,[40,1278,1279],{"class":46},"        return",[40,1281,1282],{"class":54}," { data: result.data ",[40,1284,1285],{"class":46},"as",[40,1287,1288],{"class":50}," F",[40,1290,1291],{"class":54},", errors: ",[40,1293,439],{"class":94},[40,1295,912],{"class":54},[40,1297,395],{"class":94},[40,1299,917],{"class":54},[40,1301,920],{"class":555},[40,1303,923],{"class":54},[40,1305,1307],{"class":42,"line":1306},82,[40,1308,1309],{"class":54},"      }\n",[40,1311,1313,1315],{"class":42,"line":1312},83,[40,1314,813],{"class":46},[40,1316,79],{"class":54},[40,1318,1320,1323,1325],{"class":42,"line":1319},84,[40,1321,1322],{"class":54},"        data: ",[40,1324,439],{"class":94},[40,1326,637],{"class":54},[40,1328,1330,1333,1336,1338,1341,1344,1347,1350,1353],{"class":42,"line":1329},85,[40,1331,1332],{"class":54},"        errors: result.issues.",[40,1334,1335],{"class":50},"map",[40,1337,55],{"class":54},[40,1339,1340],{"class":50},"ValidationError",[40,1342,1343],{"class":54},">((",[40,1345,1346],{"class":107},"issue",[40,1348,1349],{"class":54},") ",[40,1351,1352],{"class":46},"=>",[40,1354,1355],{"class":54}," ({\n",[40,1357,1359],{"class":42,"line":1358},86,[40,1360,1361],{"class":54},"          path: issue.path,\n",[40,1363,1365],{"class":42,"line":1364},87,[40,1366,1367],{"class":54},"          message: issue.message,\n",[40,1369,1371,1374,1376],{"class":42,"line":1370},88,[40,1372,1373],{"class":54},"          formKey: ",[40,1375,920],{"class":555},[40,1377,637],{"class":54},[40,1379,1381],{"class":42,"line":1380},89,[40,1382,1383],{"class":508},"          \u002F\u002F Pick a stable scope prefix for your adapter and forward\n",[40,1385,1387],{"class":42,"line":1386},90,[40,1388,1389],{"class":508},"          \u002F\u002F the library's issue code under it. Consumers branch on\n",[40,1391,1393],{"class":42,"line":1392},91,[40,1394,1395],{"class":508},"          \u002F\u002F `error.code` for adapter-agnostic UI logic.\n",[40,1397,1399,1402,1405,1407,1409,1411,1414,1417],{"class":42,"line":1398},92,[40,1400,1401],{"class":54},"          code: ",[40,1403,1404],{"class":555},"`mylib:${",[40,1406,1346],{"class":54},[40,1408,301],{"class":555},[40,1410,18],{"class":54},[40,1412,1413],{"class":46}," ??",[40,1415,1416],{"class":555}," 'unknown'}`",[40,1418,637],{"class":54},[40,1420,1422],{"class":42,"line":1421},93,[40,1423,1424],{"class":54},"        })),\n",[40,1426,1428,1431,1433],{"class":42,"line":1427},94,[40,1429,1430],{"class":54},"        success: ",[40,1432,418],{"class":94},[40,1434,637],{"class":54},[40,1436,1438,1441,1443],{"class":42,"line":1437},95,[40,1439,1440],{"class":54},"        formKey: ",[40,1442,920],{"class":555},[40,1444,637],{"class":54},[40,1446,1448],{"class":42,"line":1447},96,[40,1449,1309],{"class":54},[40,1451,1453],{"class":42,"line":1452},97,[40,1454,834],{"class":54},[40,1456,1458],{"class":42,"line":1457},98,[40,1459,1460],{"class":54},"  }\n",[40,1462,1464],{"class":42,"line":1463},99,[40,1465,282],{"class":54},[40,1467,1469],{"class":42,"line":1468},100,[40,1470,576],{"emptyLinePlaceholder":575},[40,1472,1474,1477,1479,1481,1484,1486,1489,1491,1494,1496,1499,1502,1505,1507,1509,1511,1513,1515],{"class":42,"line":1473},101,[40,1475,1476],{"class":46},"function",[40,1478,896],{"class":50},[40,1480,55],{"class":54},[40,1482,1483],{"class":50},"T",[40,1485,730],{"class":54},[40,1487,1488],{"class":107},"base",[40,1490,91],{"class":46},[40,1492,1493],{"class":50}," T",[40,1495,61],{"class":54},[40,1497,1498],{"class":107},"override",[40,1500,1501],{"class":46},"?:",[40,1503,1504],{"class":50}," DeepPartial",[40,1506,55],{"class":54},[40,1508,1483],{"class":50},[40,1510,745],{"class":54},[40,1512,91],{"class":46},[40,1514,1493],{"class":50},[40,1516,79],{"class":54},[40,1518,1520],{"class":42,"line":1519},102,[40,1521,1522],{"class":508},"  \u002F\u002F Dependency-free deep merge; see test\u002Futils\u002Ffake-schema.ts for a\n",[40,1524,1526],{"class":42,"line":1525},103,[40,1527,1528],{"class":508},"  \u002F\u002F reference implementation.\n",[40,1530,1532],{"class":42,"line":1531},104,[40,1533,282],{"class":54},[14,1535,1536,1537,1540,1541,1543],{},"Leave ",[18,1538,1539],{},"formKey"," as ",[18,1542,920],{}," — the composable stamps the real key in.",[14,1545,1546,1548,1549,1552],{},[18,1547,445],{}," is declared ",[18,1550,1551],{},"async"," so the return type is\nautomatically Promise-wrapped. Sync-under-the-hood parsers pay one\nmicrotask and the caller's code works identically.",[23,1554,1556],{"id":1555},"wire-it-to-useform","Wire it to useForm",[31,1558,1560],{"className":33,"code":1559,"language":35,"meta":36,"style":36},"\u002F\u002F useForm.ts\nimport { useForm as useAbstractForm } from 'attaform'\nimport { myLibAdapter } from '.\u002Fadapter'\n\nexport function useForm\u003CF extends GenericForm>(options: {\n  schema: MyLibSchema\u003CF>\n  key: string\n  defaultValues?: DeepPartial\u003CF>\n  strict?: boolean\n}) {\n  return useAbstractForm\u003CF>({\n    schema: myLibAdapter(options.schema),\n    key: options.key,\n    defaultValues: options.defaultValues,\n    strict: options.strict,\n  })\n}\n",[18,1561,1562,1567,1583,1595,1599,1625,1640,1649,1664,1673,1678,1692,1703,1708,1713,1718,1723],{"__ignoreMap":36},[40,1563,1564],{"class":42,"line":43},[40,1565,1566],{"class":508},"\u002F\u002F useForm.ts\n",[40,1568,1569,1571,1574,1576,1579,1581],{"class":42,"line":82},[40,1570,514],{"class":46},[40,1572,1573],{"class":54}," { useForm ",[40,1575,1285],{"class":46},[40,1577,1578],{"class":54}," useAbstractForm } ",[40,1580,552],{"class":46},[40,1582,556],{"class":555},[40,1584,1585,1587,1590,1592],{"class":42,"line":98},[40,1586,514],{"class":46},[40,1588,1589],{"class":54}," { myLibAdapter } ",[40,1591,552],{"class":46},[40,1593,1594],{"class":555}," '.\u002Fadapter'\n",[40,1596,1597],{"class":42,"line":126},[40,1598,576],{"emptyLinePlaceholder":575},[40,1600,1601,1603,1605,1608,1610,1612,1614,1616,1618,1621,1623],{"class":42,"line":149},[40,1602,710],{"class":46},[40,1604,713],{"class":46},[40,1606,1607],{"class":50}," useForm",[40,1609,55],{"class":54},[40,1611,721],{"class":50},[40,1613,724],{"class":46},[40,1615,727],{"class":50},[40,1617,730],{"class":54},[40,1619,1620],{"class":107},"options",[40,1622,91],{"class":46},[40,1624,79],{"class":54},[40,1626,1627,1630,1632,1634,1636,1638],{"class":42,"line":181},[40,1628,1629],{"class":107},"  schema",[40,1631,91],{"class":46},[40,1633,738],{"class":50},[40,1635,55],{"class":54},[40,1637,721],{"class":50},[40,1639,123],{"class":54},[40,1641,1642,1645,1647],{"class":42,"line":209},[40,1643,1644],{"class":107},"  key",[40,1646,91],{"class":46},[40,1648,95],{"class":94},[40,1650,1651,1654,1656,1658,1660,1662],{"class":42,"line":230},[40,1652,1653],{"class":107},"  defaultValues",[40,1655,1501],{"class":46},[40,1657,1504],{"class":50},[40,1659,55],{"class":54},[40,1661,721],{"class":50},[40,1663,123],{"class":54},[40,1665,1666,1669,1671],{"class":42,"line":279},[40,1667,1668],{"class":107},"  strict",[40,1670,1501],{"class":46},[40,1672,227],{"class":94},[40,1674,1675],{"class":42,"line":572},[40,1676,1677],{"class":54},"}) {\n",[40,1679,1680,1682,1685,1687,1689],{"class":42,"line":579},[40,1681,766],{"class":46},[40,1683,1684],{"class":50}," useAbstractForm",[40,1686,55],{"class":54},[40,1688,721],{"class":50},[40,1690,1691],{"class":54},">({\n",[40,1693,1694,1697,1700],{"class":42,"line":585},[40,1695,1696],{"class":54},"    schema: ",[40,1698,1699],{"class":50},"myLibAdapter",[40,1701,1702],{"class":54},"(options.schema),\n",[40,1704,1705],{"class":42,"line":591},[40,1706,1707],{"class":54},"    key: options.key,\n",[40,1709,1710],{"class":42,"line":597},[40,1711,1712],{"class":54},"    defaultValues: options.defaultValues,\n",[40,1714,1715],{"class":42,"line":631},[40,1716,1717],{"class":54},"    strict: options.strict,\n",[40,1719,1720],{"class":42,"line":640},[40,1721,1722],{"class":54},"  })\n",[40,1724,1725],{"class":42,"line":648},[40,1726,282],{"class":54},[14,1728,1729,1730,1733,1734,1736],{},"Consumers call your ",[18,1731,1732],{},"useForm({ schema, key })"," exactly like the Zod\none. The typing flows from your schema shape through\n",[18,1735,20],{}," into the public API.",[23,1738,300],{"id":1739},"fingerprint-implementation",[14,1741,1742,1743,1745,1746,1749],{},"The library calls ",[18,1744,294],{}," when a second ",[18,1747,1748],{},"useForm({ key: 'x', schema })"," call lands on an already-resolved FormStore.\nMatching strings → the shared-store semantic is intentional, stay\nsilent. Differing strings → dev-mode warning that names both\nfingerprints; the first caller's schema stays canonical, the\nsecond is silently ignored. Shared-store without a key collision\nmeans one party sees stale shape information — the warning tells\nyou you probably wanted distinct keys.",[14,1751,1752],{},"Required guarantees:",[284,1754,1755,1761,1767],{},[287,1756,1757,1760],{},[290,1758,1759],{},"Determinism."," Equal shapes at different memory addresses\nmust produce the same string. Most adapters live across module\nboundaries, so reference identity fails ~99% of the time.",[287,1762,1763,1766],{},[290,1764,1765],{},"Key-order insensitivity"," for record-like shapes — two\nobjects with the same fields in different declaration order\nmust match.",[287,1768,1769,1772,1773,1776,1777,1780],{},[290,1770,1771],{},"Membership-order insensitivity for unions"," — ",[18,1774,1775],{},"a | b"," and\n",[18,1778,1779],{},"b | a"," must match.",[14,1782,1783],{},"Acceptable compromises:",[284,1785,1786,1800],{},[287,1787,1788,1789,61,1792,1795,1796,1799],{},"Function-valued metadata (",[18,1790,1791],{},"refine(fn)",[18,1793,1794],{},"transform(fn)",", lazy\nfactory defaults) is not stably hashable. Collapse it to an\nopaque sentinel (",[18,1797,1798],{},"'fn:*'",") — two schemas differing only in\nrefinement logic will look identical, which is a documented\nfalse-negative, not a bug. The warning is a footgun catcher,\nnot a soundness guarantee.",[287,1801,1802,1803,1806],{},"Cycles (lazy \u002F self-referential schemas). Track an ancestor\nset and emit a fixed ",[18,1804,1805],{},"'\u003Ccyclic>'"," sentinel on re-entry.",[14,1808,1809,1810,1813],{},"If your schema library has no introspection surface, returning a\nstable per-instance opaque string (",[18,1811,1812],{},"'my-lib:v1'"," computed once\nper adapter build) is a legal implementation — it disables\ncross-instance mismatch detection but never false-positives.",[14,1815,1816,1817,1820],{},"See ",[18,1818,1819],{},"src\u002Fruntime\u002Fadapters\u002Fzod-v4\u002Ffingerprint.ts"," for a full\nwalker with factory-default idempotence and shared-reference\nhandling.",[23,1822,332],{"id":1823},"getdefaultatpath",[14,1825,1826,1827,1829],{},"The runtime's structural-completeness invariant — every ",[18,1828,327],{},"\nwrite leaves the form satisfying the slim schema — depends on this\nmethod returning a sane default at any path. Three concrete\nruntime callers:",[284,1831,1832,1840,1852],{},[287,1833,1834,1839],{},[290,1835,1836],{},[18,1837,1838],{},"mergeStructural",", when a partial value-form write hits a\nschema key the consumer didn't supply. Asks for the default at\nthe missing sub-path and fills it in.",[287,1841,1842,1847,1848,1851],{},[290,1843,1844],{},[18,1845,1846],{},"setAtPathWithSchemaFill",", when a sparse array write\n(",[18,1849,1850],{},"setValue('posts.21', cb)"," against an empty array) needs to pad\nintermediate indices. Asks for the element default once and reuses\nit.",[287,1853,1854,1857,1858,1861,1862,1865],{},[290,1855,1856],{},"Path-form callback prev auto-default",", when the consumer\nwrites ",[18,1859,1860],{},"setValue('user', prev => ({ ...prev, name: 'X' }))"," and\nthe slot was previously empty. The runtime calls\n",[18,1863,1864],{},"getDefaultAtPath(['user'])"," and feeds the result to the callback.",[1867,1868,1870],"h3",{"id":1869},"the-peeling-rule","The peeling rule",[14,1872,1873,1874,1878,1879,1882],{},"Wrappers around ",[1875,1876,1877],"em",{},"structural"," types peel; wrappers around\n",[1875,1880,1881],{},"primitive"," leaves don't:",[31,1884,1886],{"className":33,"code":1885,"language":35,"meta":36,"style":36},"\u002F\u002F schema.profile is z.object({...}).optional()\ngetDefaultAtPath(['profile']) \u002F\u002F returns { name: '', age: 0 }  — peel\ngetDefaultAtPath(['profile', 'name']) \u002F\u002F returns ''            — peel + descend\n\n\u002F\u002F schema.notes is z.string().optional()\ngetDefaultAtPath(['notes']) \u002F\u002F returns undefined  — DO NOT peel\n\u002F\u002F (peeling would return '' and break mergeStructural — the wrapper's\n\u002F\u002F \"absent allowed\" semantic gets lost when filling sibling keys)\n\n\u002F\u002F schema.role is z.string().default('user')\ngetDefaultAtPath(['role']) \u002F\u002F returns 'user'  — explicit default wins\n",[18,1887,1888,1893,1909,1927,1931,1936,1950,1955,1960,1964,1969],{"__ignoreMap":36},[40,1889,1890],{"class":42,"line":43},[40,1891,1892],{"class":508},"\u002F\u002F schema.profile is z.object({...}).optional()\n",[40,1894,1895,1897,1900,1903,1906],{"class":42,"line":82},[40,1896,332],{"class":50},[40,1898,1899],{"class":54},"([",[40,1901,1902],{"class":555},"'profile'",[40,1904,1905],{"class":54},"]) ",[40,1907,1908],{"class":508},"\u002F\u002F returns { name: '', age: 0 }  — peel\n",[40,1910,1911,1913,1915,1917,1919,1922,1924],{"class":42,"line":98},[40,1912,332],{"class":50},[40,1914,1899],{"class":54},[40,1916,1902],{"class":555},[40,1918,61],{"class":54},[40,1920,1921],{"class":555},"'name'",[40,1923,1905],{"class":54},[40,1925,1926],{"class":508},"\u002F\u002F returns ''            — peel + descend\n",[40,1928,1929],{"class":42,"line":126},[40,1930,576],{"emptyLinePlaceholder":575},[40,1932,1933],{"class":42,"line":149},[40,1934,1935],{"class":508},"\u002F\u002F schema.notes is z.string().optional()\n",[40,1937,1938,1940,1942,1945,1947],{"class":42,"line":181},[40,1939,332],{"class":50},[40,1941,1899],{"class":54},[40,1943,1944],{"class":555},"'notes'",[40,1946,1905],{"class":54},[40,1948,1949],{"class":508},"\u002F\u002F returns undefined  — DO NOT peel\n",[40,1951,1952],{"class":42,"line":209},[40,1953,1954],{"class":508},"\u002F\u002F (peeling would return '' and break mergeStructural — the wrapper's\n",[40,1956,1957],{"class":42,"line":230},[40,1958,1959],{"class":508},"\u002F\u002F \"absent allowed\" semantic gets lost when filling sibling keys)\n",[40,1961,1962],{"class":42,"line":279},[40,1963,576],{"emptyLinePlaceholder":575},[40,1965,1966],{"class":42,"line":572},[40,1967,1968],{"class":508},"\u002F\u002F schema.role is z.string().default('user')\n",[40,1970,1971,1973,1975,1978,1980],{"class":42,"line":579},[40,1972,332],{"class":50},[40,1974,1899],{"class":54},[40,1976,1977],{"class":555},"'role'",[40,1979,1905],{"class":54},[40,1981,1982],{"class":508},"\u002F\u002F returns 'user'  — explicit default wins\n",[14,1984,1985,1986,400,1989,407,1992,1995],{},"If your library exposes wrapper introspection, classify each\nwrapper-inner combo: ",[18,1987,1988],{},"OptionalString",[18,1990,1991],{},"NullableNumber",[18,1993,1994],{},"OptionalBoolean"," etc. preserve the wrapper semantic; everything\nelse peels to the inner default.",[1867,1997,1999],{"id":1998},"return-values-for-special-positions","Return values for special positions",[2001,2002,2003,2016],"table",{},[2004,2005,2006],"thead",{},[2007,2008,2009,2013],"tr",{},[2010,2011,2012],"th",{},"Position",[2010,2014,2015],{},"Return",[2017,2018,2019,2030,2041,2055,2066,2076,2084,2092],"tbody",{},[2007,2020,2021,2027],{},[2022,2023,2024,2025],"td",{},"Empty path ",[18,2026,354],{},[2022,2028,2029],{},"The whole-form default",[2007,2031,2032,2038],{},[2022,2033,2034,2035],{},"Object property ",[18,2036,2037],{},"['user']",[2022,2039,2040],{},"The property's default",[2007,2042,2043,2049],{},[2022,2044,2045,2046],{},"Array index ",[18,2047,2048],{},"['posts', 0]",[2022,2050,2051,2052],{},"Element schema's default — same for any ",[18,2053,2054],{},"N",[2007,2056,2057,2063],{},[2022,2058,2059,2060],{},"Tuple position ",[18,2061,2062],{},"['xy', 1]",[2022,2064,2065],{},"Position-specific default",[2007,2067,2068,2071],{},[2022,2069,2070],{},"Tuple past length",[2022,2072,2073,2075],{},[18,2074,439],{}," (signal: don't pad)",[2007,2077,2078,2081],{},[2022,2079,2080],{},"Discriminated union root",[2022,2082,2083],{},"First variant's default",[2007,2085,2086,2089],{},[2022,2087,2088],{},"Discriminated union sub-path",[2022,2090,2091],{},"Matching variant's value (or first variant)",[2007,2093,2094,2097],{},[2022,2095,2096],{},"Path doesn't exist in schema",[2022,2098,2099],{},[18,2100,439],{},[1867,2102,2104],{"id":2103},"testing","Testing",[14,2106,2107,2108,2111],{},"The Zod adapter test suites\n(",[18,2109,2110],{},"test\u002Fadapters\u002Fzod-v4\u002Fget-default-at-path.test.ts"," and the v3\nmirror) double as a behavioural spec — port the cases to your\nadapter's test suite. Minimum coverage:",[284,2113,2114,2117,2126,2131,2134,2139,2142,2152,2155,2158,2161],{},[287,2115,2116],{},"Object property path returns property's default.",[287,2118,2119,2122,2123,301],{},[18,2120,2121],{},".default(x)"," wrapper returns ",[18,2124,2125],{},"x",[287,2127,2128,2129,301],{},"Array index path returns element default for any ",[18,2130,2054],{},[287,2132,2133],{},"Nested defaults through array → object → array.",[287,2135,2136,2137,301],{},"Tuple position-specific defaults; tuple-past-length →\n",[18,2138,439],{},[287,2140,2141],{},"Optional\u002FNullable around structural inner peels.",[287,2143,2144,2145,2147,2148,2151],{},"Optional\u002FNullable around primitive PRESERVES wrapper semantic\n(",[18,2146,439],{},"\u002F",[18,2149,2150],{},"null",").",[287,2153,2154],{},"Discriminated union returns first variant's default at the union\nroot.",[287,2156,2157],{},"Discriminated union descends into the matching variant for\nvariant-specific keys.",[287,2159,2160],{},"Record returns value-type default for any string key.",[287,2162,2163,2164,301],{},"Non-existent paths return ",[18,2165,439],{},[23,2167,2169],{"id":2168},"validating-a-single-path","Validating a single path",[14,2171,2172,2173,2175],{},"When the library needs to re-check just one field, it passes a\ndotted ",[18,2174,134],{},". Two implementation strategies:",[2177,2178,2179,2185],"ol",{},[287,2180,2181,2184],{},[290,2182,2183],{},"Full parse + filter issues"," — run the whole parse, return\nonly the issues that match. Simpler; extra work per call.",[287,2186,2187,2190],{},[290,2188,2189],{},"Walk to the sub-schema, validate only that"," — matches the\nZod v4 adapter's approach. Faster; needs your library's\nintrospection.",[14,2192,2193],{},"Start with (1). Upgrade if profiling shows it matters.",[23,2195,2197],{"id":2196},"type-inference","Type inference",[14,2199,2200,2201,2204,2205,2208,2209,2211],{},"The abstract ",[18,2202,2203],{},"useForm"," accepts any ",[18,2206,2207],{},"AbstractSchema\u003CForm, Form>",".\nYour adapter is what pins ",[18,2210,58],{}," to whatever your schema produces:",[31,2213,2215],{"className":33,"code":2214,"language":35,"meta":36,"style":36},"\u002F\u002F Zod v4:\ntype Form = z.output\u003Ctypeof schema>\n\u002F\u002F Valibot:\ntype Form = v.InferOutput\u003Ctypeof schema>\n\u002F\u002F Hand-rolled:\ntype Form = { email: string; password: string }\n",[18,2216,2217,2222,2245,2250,2272,2277],{"__ignoreMap":36},[40,2218,2219],{"class":42,"line":43},[40,2220,2221],{"class":508},"\u002F\u002F Zod v4:\n",[40,2223,2224,2226,2228,2230,2233,2235,2238,2240,2242],{"class":42,"line":82},[40,2225,47],{"class":46},[40,2227,70],{"class":50},[40,2229,67],{"class":46},[40,2231,2232],{"class":50}," z",[40,2234,301],{"class":54},[40,2236,2237],{"class":50},"output",[40,2239,55],{"class":54},[40,2241,366],{"class":46},[40,2243,2244],{"class":54}," schema>\n",[40,2246,2247],{"class":42,"line":98},[40,2248,2249],{"class":508},"\u002F\u002F Valibot:\n",[40,2251,2252,2254,2256,2258,2261,2263,2266,2268,2270],{"class":42,"line":126},[40,2253,47],{"class":46},[40,2255,70],{"class":50},[40,2257,67],{"class":46},[40,2259,2260],{"class":50}," v",[40,2262,301],{"class":54},[40,2264,2265],{"class":50},"InferOutput",[40,2267,55],{"class":54},[40,2269,366],{"class":46},[40,2271,2244],{"class":54},[40,2273,2274],{"class":42,"line":149},[40,2275,2276],{"class":508},"\u002F\u002F Hand-rolled:\n",[40,2278,2279,2281,2283,2285,2288,2291,2293,2296,2299,2302,2304,2306],{"class":42,"line":181},[40,2280,47],{"class":46},[40,2282,70],{"class":50},[40,2284,67],{"class":46},[40,2286,2287],{"class":54}," { ",[40,2289,2290],{"class":107},"email",[40,2292,91],{"class":46},[40,2294,2295],{"class":94}," string",[40,2297,2298],{"class":54},"; ",[40,2300,2301],{"class":107},"password",[40,2303,91],{"class":46},[40,2305,2295],{"class":94},[40,2307,923],{"class":54},[14,2309,2310,2312,2313,2316],{},[18,2311,2203],{},"'s return type (",[18,2314,2315],{},"UseFormReturnType\u003CForm>",")\ncarries no schema-library-specific types — the public surface is\nidentical regardless of adapter.",[23,2318,2320],{"id":2319},"testing-your-adapter","Testing your adapter",[14,2322,2323],{},"Minimum coverage:",[284,2325,2326,2334,2342,2347,2352,2357,2365,2371,2395,2403,2415,2421,2426,2431],{},[287,2327,2328,2331,2332,301],{},[18,2329,2330],{},"getDefaultValues"," returns schema defaults when no ",[18,2333,851],{},[287,2335,2336,2338,2339,2341],{},[18,2337,2330],{}," merges ",[18,2340,851],{}," over defaults.",[287,2343,2344,2346],{},[18,2345,332],{}," returns the property default for object paths.",[287,2348,2349,2351],{},[18,2350,332],{}," returns the element default for array indices.",[287,2353,2354,2356],{},[18,2355,332],{}," returns the inner default through structural\nwrappers (peels Optional\u002FNullable around objects; preserves them\naround primitives).",[287,2358,2359,2361,2362,2364],{},[18,2360,332],{}," returns ",[18,2363,439],{}," for paths not in the\nschema.",[287,2366,2367,2370],{},[18,2368,2369],{},"getSlimPrimitiveTypesAtPath"," returns the leaf's primitive kinds\nfor known paths, and a permissive fallback for unknown paths.",[287,2372,2373,2361,2376,2378,2379,2381,2382,400,2385,400,2388,400,2391,2394],{},[18,2374,2375],{},"isRequiredAtPath",[18,2377,395],{}," for plain leaves and ",[18,2380,418],{},"\nfor ",[18,2383,2384],{},"Optional",[18,2386,2387],{},"Nullable",[18,2389,2390],{},"Default",[18,2392,2393],{},"Catch"," wrappers.",[287,2396,2397,2361,2399,2402],{},[18,2398,445],{},[18,2400,2401],{},"{ success: true }"," for valid input.",[287,2404,2405,2407,2408,2411,2412,2414],{},[18,2406,445],{}," returns structured ",[18,2409,2410],{},"ValidationError[]"," for\ninvalid input — every entry carries a non-empty ",[18,2413,18],{}," under\nyour chosen scope prefix.",[287,2416,2417,2420],{},[18,2418,2419],{},"validateAtPath(undefined)"," validates the whole form.",[287,2422,2423,2425],{},[18,2424,294],{}," is stable across calls on the same schema.",[287,2427,2428,2430],{},[18,2429,294],{}," matches for two schemas with the same shape but\ndifferent key-declaration order (and different union member\norder, if you support unions).",[287,2432,2433,2435],{},[18,2434,294],{}," differs for schemas with different leaf types\nor missing fields.",[14,2437,2438,2439,2442,2443,2446,2447,2450,2451,2453,2454,2456],{},"The v4 adapter's test suite (",[18,2440,2441],{},"test\u002Fadapters\u002Fzod-v4\u002F",") is the\ntemplate. Add a fast-check property test over random forms if your\nadapter does non-trivial path walking —\n",[18,2444,2445],{},"test\u002Fcore\u002Fdiff-apply.property.test.ts"," shows the pattern. Pair the\nadapter tests with the runtime structural-completeness regressions\nat ",[18,2448,2449],{},"test\u002Fcomposables\u002Fset-value-schema-fill-regression.test.ts"," —\nthose drive ",[18,2452,332],{}," end-to-end through ",[18,2455,327],{},", so a\nbroken adapter implementation surfaces as a test failure with a\nclear \"schema didn't fill X\" diagnostic.",[2458,2459,2460],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":36,"searchDepth":82,"depth":82,"links":2462},[2463,2464,2465,2466,2467,2472,2473,2474],{"id":25,"depth":82,"text":26},{"id":474,"depth":82,"text":475},{"id":1555,"depth":82,"text":1556},{"id":1739,"depth":82,"text":300},{"id":1823,"depth":82,"text":332,"children":2468},[2469,2470,2471],{"id":1869,"depth":98,"text":1870},{"id":1998,"depth":98,"text":1999},{"id":2103,"depth":98,"text":2104},{"id":2168,"depth":82,"text":2169},{"id":2196,"depth":82,"text":2197},{"id":2319,"depth":82,"text":2320},"Attaform is schema-agnostic. Zod is just one implementation of\nthe internal AbstractSchema contract. If you're on Valibot,\nArkType, Effect-Schema, or a hand-rolled validator, wire yours in\nwithout forking the library.","md",{},"\u002Fdocs\u002Frecipes\u002Fcustom-adapter",{"title":5,"description":2475},"docs\u002Frecipes\u002Fcustom-adapter","n955PTyWq8u6e60aAPQTPR_0YesvzSVphdhdxFpu830",1777934136220]