[{"data":1,"prerenderedAt":1526},["ShallowReactive",2],{"content-\u002Fdocs\u002Fbinding-inputs\u002Ftransforms":3},{"id":4,"title":5,"body":6,"description":1504,"extension":1505,"meta":1506,"metaRows":1507,"navigation":108,"path":1521,"seo":1522,"source":1523,"stem":1524,"__hash__":1525},"docs\u002Fdocs\u002Fbinding-inputs\u002Ftransforms.md","Register transforms",{"type":7,"value":8,"toc":1486},"minimark",[9,13,35,38,62,66,71,241,255,262,282,299,302,305,329,343,347,358,464,478,483,486,507,535,539,542,549,552,565,572,579,597,666,672,676,689,693,703,706,710,713,1212,1283,1314,1318,1333,1421,1427,1431,1482],[10,11,5],"h1",{"id":12},"register-transforms",[14,15,16],"blockquote",{},[17,18,19,20,24,25,24,28,24,31,34],"p",{},"A write-time pipeline that reshapes each value before it lands in storage. Compose ",[21,22,23],"code",{},"trim",", ",[21,26,27],{},"lowercase",[21,29,30],{},"clamp",[21,32,33],{},"format"," per field, and return a Promise when the reshape needs a round trip first.",[36,37],"docs-meta-table",{},[17,39,40,41,43,44,47,48,51,52,55,56,61],{},"Type a mixed-case title with spaces into the slug field and watch the readout: the value lands as a lowercased, dashified, alphanumeric-only string. The first input has no transforms; the second composes two (",[21,42,27],{}," then ",[21,45,46],{},"dashify",") through the ",[21,49,50],{},"transforms"," array on ",[21,53,54],{},"register",". The ",[57,58,60],"a",{"href":59},"#composition-order","Composition order"," section unpacks why left-to-right composition makes a personal transform library easy to assemble.",[63,64],"docs-demo",{"label":65,"slug":50},"Transforms Demo",[67,68,70],"h2",{"id":69},"a-transform-is-a-function","A transform is a function",[72,73,78],"pre",{"className":74,"code":75,"language":76,"meta":77,"style":77},"language-ts shiki shiki-themes github-light github-dark","import type { RegisterTransform } from 'attaform'\n\nconst lowercase: RegisterTransform = (v) => (typeof v === 'string' ? v.toLowerCase() : v)\nconst dashify: RegisterTransform = (v) => (typeof v === 'string' ? v.replace(\u002F\\s+\u002Fg, '-') : v)\n","ts","",[21,79,80,103,110,173],{"__ignoreMap":77},[81,82,85,89,92,96,99],"span",{"class":83,"line":84},"line",1,[81,86,88],{"class":87},"szBVR","import",[81,90,91],{"class":87}," type",[81,93,95],{"class":94},"sVt8B"," { RegisterTransform } ",[81,97,98],{"class":87},"from",[81,100,102],{"class":101},"sZZnC"," 'attaform'\n",[81,104,106],{"class":83,"line":105},2,[81,107,109],{"emptyLinePlaceholder":108},true,"\n",[81,111,113,116,120,123,126,129,132,136,139,142,144,147,150,153,156,159,162,165,168,170],{"class":83,"line":112},3,[81,114,115],{"class":87},"const",[81,117,119],{"class":118},"sScJk"," lowercase",[81,121,122],{"class":87},":",[81,124,125],{"class":118}," RegisterTransform",[81,127,128],{"class":87}," =",[81,130,131],{"class":94}," (",[81,133,135],{"class":134},"s4XuR","v",[81,137,138],{"class":94},") ",[81,140,141],{"class":87},"=>",[81,143,131],{"class":94},[81,145,146],{"class":87},"typeof",[81,148,149],{"class":94}," v ",[81,151,152],{"class":87},"===",[81,154,155],{"class":101}," 'string'",[81,157,158],{"class":87}," ?",[81,160,161],{"class":94}," v.",[81,163,164],{"class":118},"toLowerCase",[81,166,167],{"class":94},"() ",[81,169,122],{"class":87},[81,171,172],{"class":94}," v)\n",[81,174,176,178,181,183,185,187,189,191,193,195,197,199,201,203,205,207,209,212,215,218,222,225,227,230,232,235,237,239],{"class":83,"line":175},4,[81,177,115],{"class":87},[81,179,180],{"class":118}," dashify",[81,182,122],{"class":87},[81,184,125],{"class":118},[81,186,128],{"class":87},[81,188,131],{"class":94},[81,190,135],{"class":134},[81,192,138],{"class":94},[81,194,141],{"class":87},[81,196,131],{"class":94},[81,198,146],{"class":87},[81,200,149],{"class":94},[81,202,152],{"class":87},[81,204,155],{"class":101},[81,206,158],{"class":87},[81,208,161],{"class":94},[81,210,211],{"class":118},"replace",[81,213,214],{"class":94},"(",[81,216,217],{"class":101},"\u002F",[81,219,221],{"class":220},"sj4cs","\\s",[81,223,224],{"class":87},"+",[81,226,217],{"class":101},[81,228,229],{"class":87},"g",[81,231,24],{"class":94},[81,233,234],{"class":101},"'-'",[81,236,138],{"class":94},[81,238,122],{"class":87},[81,240,172],{"class":94},[17,242,243,246,247,250,251,254],{},[21,244,245],{},"RegisterTransform"," is ",[21,248,249],{},"(value: unknown) => unknown",". The shape is intentionally generic, so a personal library of transforms plugs into any ",[21,252,253],{},"register()"," call site regardless of leaf type. Library authors defend against type mismatches by no-op'ing on the unexpected branch.",[67,256,258,259],{"id":257},"attach-via-transforms","Attach via ",[21,260,261],{},"transforms: [...]",[72,263,265],{"className":74,"code":264,"language":76,"meta":77,"style":77},"form.register('slug', { transforms: [lowercase, dashify] })\n",[21,266,267],{"__ignoreMap":77},[81,268,269,272,274,276,279],{"class":83,"line":84},[81,270,271],{"class":94},"form.",[81,273,54],{"class":118},[81,275,214],{"class":94},[81,277,278],{"class":101},"'slug'",[81,280,281],{"class":94},", { transforms: [lowercase, dashify] })\n",[17,283,284,285,287,288,217,291,294,295,298],{},"Pass an ordered array on the ",[21,286,54],{}," options. Every keystroke (or ",[21,289,290],{},"change",[21,292,293],{},"blur"," with ",[21,296,297],{},".lazy",") flows through the transforms left-to-right; the final value is what lands in storage.",[67,300,60],{"id":301},"composition-order",[17,303,304],{},"Transforms run left-to-right:",[72,306,308],{"className":74,"code":307,"language":76,"meta":77,"style":77},"form.register('slug', { transforms: [trim, lowercase, dashify] })\n\u002F\u002F 'Hello World ' → 'Hello World' → 'hello world' → 'hello-world'\n",[21,309,310,323],{"__ignoreMap":77},[81,311,312,314,316,318,320],{"class":83,"line":84},[81,313,271],{"class":94},[81,315,54],{"class":118},[81,317,214],{"class":94},[81,319,278],{"class":101},[81,321,322],{"class":94},", { transforms: [trim, lowercase, dashify] })\n",[81,324,325],{"class":83,"line":105},[81,326,328],{"class":327},"sJ8bj","\u002F\u002F 'Hello World ' → 'Hello World' → 'hello world' → 'hello-world'\n",[17,330,331,332,338,339,342],{},"Pick the order that matches the data flow you want. The ",[57,333,335],{"href":334},"\u002Fdocs\u002Fbinding-inputs\u002Fmodifiers",[21,336,337],{},".trim"," modifier runs before any transform, so a ",[21,340,341],{},"[trim, ...]"," transforms array would be redundant. Reach for the modifier when you want trimming and the transforms array when you want anything else.",[67,344,346],{"id":345},"async-transforms","Async transforms",[17,348,349,350,353,354,357],{},"Hand a transform a ",[21,351,352],{},"Promise"," and Attaform shifts into async mode on its own. The chain stays fully synchronous, landing the value in the same tick, right up until a transform returns a thenable. From there Attaform defers the write, flips the field to ",[21,355,356],{},"busy",", and commits the resolved value the moment it arrives.",[72,359,361],{"className":74,"code":360,"language":76,"meta":77,"style":77},"const normalize: RegisterTransform = async (value, ctx) => {\n  const res = await fetch(`\u002Fapi\u002Fnormalize?q=${value}`, { signal: ctx?.signal })\n  return res.text()\n}\n\nform.register('handle', { transforms: [normalize] })\n",[21,362,363,396,425,439,444,449],{"__ignoreMap":77},[81,364,365,367,370,372,374,376,379,381,384,386,389,391,393],{"class":83,"line":84},[81,366,115],{"class":87},[81,368,369],{"class":118}," normalize",[81,371,122],{"class":87},[81,373,125],{"class":118},[81,375,128],{"class":87},[81,377,378],{"class":87}," async",[81,380,131],{"class":94},[81,382,383],{"class":134},"value",[81,385,24],{"class":94},[81,387,388],{"class":134},"ctx",[81,390,138],{"class":94},[81,392,141],{"class":87},[81,394,395],{"class":94}," {\n",[81,397,398,401,404,406,409,412,414,417,419,422],{"class":83,"line":105},[81,399,400],{"class":87},"  const",[81,402,403],{"class":220}," res",[81,405,128],{"class":87},[81,407,408],{"class":87}," await",[81,410,411],{"class":118}," fetch",[81,413,214],{"class":94},[81,415,416],{"class":101},"`\u002Fapi\u002Fnormalize?q=${",[81,418,383],{"class":94},[81,420,421],{"class":101},"}`",[81,423,424],{"class":94},", { signal: ctx?.signal })\n",[81,426,427,430,433,436],{"class":83,"line":112},[81,428,429],{"class":87},"  return",[81,431,432],{"class":94}," res.",[81,434,435],{"class":118},"text",[81,437,438],{"class":94},"()\n",[81,440,441],{"class":83,"line":175},[81,442,443],{"class":94},"}\n",[81,445,447],{"class":83,"line":446},5,[81,448,109],{"emptyLinePlaceholder":108},[81,450,452,454,456,458,461],{"class":83,"line":451},6,[81,453,271],{"class":94},[81,455,54],{"class":118},[81,457,214],{"class":94},[81,459,460],{"class":101},"'handle'",[81,462,463],{"class":94},", { transforms: [normalize] })\n",[17,465,466,467,469,470,473,474,477],{},"Mix sync and async freely in one array. The pipeline runs synchronously until the first ",[21,468,352],{}," shows up, so a ",[21,471,472],{},"[trim, normalize, lowercase]"," chain trims in the same tick, waits on ",[21,475,476],{},"normalize",", then lowercases the result before it lands.",[479,480,482],"h3",{"id":481},"watch-the-busy-state","Watch the busy state",[17,484,485],{},"While an async transform settles, the field reports it:",[72,487,489],{"className":74,"code":488,"language":76,"meta":77,"style":77},"form.fields.handle.busy \u002F\u002F true while the chain is in flight\nform.fields.handle.transforming \u002F\u002F true specifically for transform work\n",[21,490,491,499],{"__ignoreMap":77},[81,492,493,496],{"class":83,"line":84},[81,494,495],{"class":94},"form.fields.handle.busy ",[81,497,498],{"class":327},"\u002F\u002F true while the chain is in flight\n",[81,500,501,504],{"class":83,"line":105},[81,502,503],{"class":94},"form.fields.handle.transforming ",[81,505,506],{"class":327},"\u002F\u002F true specifically for transform work\n",[17,508,509,512,513,515,516,519,520,523,524,527,528,531,532,534],{},[21,510,511],{},"transforming"," is the transform-only signal; ",[21,514,356],{}," unions it with validation, so one read covers every reason the field is working. Both roll up to ",[21,517,518],{},"form.meta",", and on a revealed field they drive ",[21,521,522],{},"displayState"," to ",[21,525,526],{},"'pending'"," and set ",[21,529,530],{},"aria-busy",", the same anti-flash timing a pending validation rides. Bind ",[21,533,356],{}," directly when you want a spinner the instant work starts, even before the field has been revealed.",[479,536,538],{"id":537},"latest-edit-wins","Latest edit wins",[17,540,541],{},"Edit faster than the work settles and only the most recent run commits. Earlier in-flight runs are discarded the moment a newer value supersedes them, so the field never stutters back to a stale result or commits two answers out of order.",[479,543,545,546],{"id":544},"failures-land-on-transformerror","Failures land on ",[21,547,548],{},"transformError",[17,550,551],{},"An async transform that rejects routes its error to a dedicated channel instead of throwing into your app or logging to the console:",[72,553,555],{"className":74,"code":554,"language":76,"meta":77,"style":77},"form.fields.handle.transformError \u002F\u002F Error | null\n",[21,556,557],{"__ignoreMap":77},[81,558,559,562],{"class":83,"line":84},[81,560,561],{"class":94},"form.fields.handle.transformError ",[81,563,564],{"class":327},"\u002F\u002F Error | null\n",[17,566,567,568,571],{},"This is a separate surface from validation ",[21,569,570],{},"errors",": a normalization that could not finish is a different story from a value the schema rejected. The field keeps its prior committed value, and a later successful run clears the error.",[479,573,575,576],{"id":574},"cancel-stale-work-with-ctxsignal","Cancel stale work with ",[21,577,578],{},"ctx.signal",[17,580,581,582,584,585,588,589,592,593,596],{},"Every transform receives a ",[21,583,388],{}," whose ",[21,586,587],{},"signal"," is an ",[21,590,591],{},"AbortSignal",". Attaform aborts it when a newer edit supersedes the run, or when ",[21,594,595],{},"reset()"," or unmount tears the field down. Thread it into cancellable I\u002FO and a superseded request drops instead of racing the live one:",[72,598,600],{"className":74,"code":599,"language":76,"meta":77,"style":77},"const normalize: RegisterTransform = async (value, ctx) => {\n  const res = await fetch(`\u002Fapi\u002Fnormalize?q=${value}`, { signal: ctx?.signal })\n  return res.text()\n}\n",[21,601,602,630,652,662],{"__ignoreMap":77},[81,603,604,606,608,610,612,614,616,618,620,622,624,626,628],{"class":83,"line":84},[81,605,115],{"class":87},[81,607,369],{"class":118},[81,609,122],{"class":87},[81,611,125],{"class":118},[81,613,128],{"class":87},[81,615,378],{"class":87},[81,617,131],{"class":94},[81,619,383],{"class":134},[81,621,24],{"class":94},[81,623,388],{"class":134},[81,625,138],{"class":94},[81,627,141],{"class":87},[81,629,395],{"class":94},[81,631,632,634,636,638,640,642,644,646,648,650],{"class":83,"line":105},[81,633,400],{"class":87},[81,635,403],{"class":220},[81,637,128],{"class":87},[81,639,408],{"class":87},[81,641,411],{"class":118},[81,643,214],{"class":94},[81,645,416],{"class":101},[81,647,383],{"class":94},[81,649,421],{"class":101},[81,651,424],{"class":94},[81,653,654,656,658,660],{"class":83,"line":112},[81,655,429],{"class":87},[81,657,432],{"class":94},[81,659,435],{"class":118},[81,661,438],{"class":94},[81,663,664],{"class":83,"line":175},[81,665,443],{"class":94},[17,667,668,669,671],{},"A purely synchronous chain never touches ",[21,670,587],{}," and never allocates a controller, so the fast path stays allocation-free.",[479,673,675],{"id":674},"submit-waits-for-transforms-to-settle","Submit waits for transforms to settle",[17,677,678,684,685,688],{},[57,679,681],{"href":680},"\u002Fdocs\u002Fsubmitting\u002Fhandle-submit",[21,682,683],{},"handleSubmit"," drains every in-flight transform before it validates, so a submit fired one keystroke after an edit validates the resolved value, not the one mid-flight. Need the same guarantee by hand? ",[21,686,687],{},"await form.settleTransforms()"," resolves once the field (or the whole form) is quiet, and it resolves rather than rejects even when a transform failed.",[67,690,692],{"id":691},"from-a-dropped-file-to-clean-data","From a dropped file to clean data",[17,694,695,696,699,700,702],{},"Here is where async transforms earn their keep: point one at an ",[21,697,698],{},"\u003Cinput type=\"file\">"," and a file the visitor picks becomes finished form state. No upload handler, no change listener to wire, just a ",[21,701,50],{}," array doing the work.",[17,704,705],{},"Download the sample below (or write your own), then pick it in the demo. Each line becomes a URL, the lines that are not web addresses get dropped, and a busy indicator runs the whole time the file is being read and tidied.",[63,707],{"label":708,"slug":709},"Async file transform","transforms-async",[17,711,712],{},"Two transforms compose the result. The first reads the file and reshapes its lines; the second lowercases what survives:",[72,714,716],{"className":74,"code":715,"language":76,"meta":77,"style":77},"const schema = z.object({ links: z.array(z.string()).min(1) })\n\nconst linesToUrls: RegisterTransform = async (value) => {\n  const files = Array.isArray(value) ? (value as File[]) : []\n  const urls: string[] = []\n  for (const file of files) {\n    for (const line of (await file.text()).split(\u002F\\r?\\n\u002F)) {\n      const trimmed = line.trim()\n      if (trimmed === '' || trimmed.startsWith('#')) continue\n      try {\n        const url = new URL(\u002F^https?:\\\u002F\\\u002F\u002Fi.test(trimmed) ? trimmed : `https:\u002F\u002F${trimmed}`)\n        if (url.protocol === 'http:' || url.protocol === 'https:') urls.push(url.href)\n      } catch {\n        \u002F\u002F a line that cannot become a URL is dropped\n      }\n    }\n  }\n  return urls\n}\n\nconst lowercase: RegisterTransform = (value) =>\n  Array.isArray(value) ? value.map((url) => String(url).toLowerCase()) : value\n\nconst form = useForm({ schema })\n",[21,717,718,759,763,788,826,846,864,911,929,963,971,1039,1072,1083,1089,1095,1101,1107,1115,1120,1125,1147,1191,1196],{"__ignoreMap":77},[81,719,720,722,725,727,730,733,736,739,742,745,748,751,753,756],{"class":83,"line":84},[81,721,115],{"class":87},[81,723,724],{"class":220}," schema",[81,726,128],{"class":87},[81,728,729],{"class":94}," z.",[81,731,732],{"class":118},"object",[81,734,735],{"class":94},"({ links: z.",[81,737,738],{"class":118},"array",[81,740,741],{"class":94},"(z.",[81,743,744],{"class":118},"string",[81,746,747],{"class":94},"()).",[81,749,750],{"class":118},"min",[81,752,214],{"class":94},[81,754,755],{"class":220},"1",[81,757,758],{"class":94},") })\n",[81,760,761],{"class":83,"line":105},[81,762,109],{"emptyLinePlaceholder":108},[81,764,765,767,770,772,774,776,778,780,782,784,786],{"class":83,"line":112},[81,766,115],{"class":87},[81,768,769],{"class":118}," linesToUrls",[81,771,122],{"class":87},[81,773,125],{"class":118},[81,775,128],{"class":87},[81,777,378],{"class":87},[81,779,131],{"class":94},[81,781,383],{"class":134},[81,783,138],{"class":94},[81,785,141],{"class":87},[81,787,395],{"class":94},[81,789,790,792,795,797,800,803,806,809,812,815,818,821,823],{"class":83,"line":175},[81,791,400],{"class":87},[81,793,794],{"class":220}," files",[81,796,128],{"class":87},[81,798,799],{"class":94}," Array.",[81,801,802],{"class":118},"isArray",[81,804,805],{"class":94},"(value) ",[81,807,808],{"class":87},"?",[81,810,811],{"class":94}," (value ",[81,813,814],{"class":87},"as",[81,816,817],{"class":118}," File",[81,819,820],{"class":94},"[]) ",[81,822,122],{"class":87},[81,824,825],{"class":94}," []\n",[81,827,828,830,833,835,838,841,844],{"class":83,"line":446},[81,829,400],{"class":87},[81,831,832],{"class":220}," urls",[81,834,122],{"class":87},[81,836,837],{"class":220}," string",[81,839,840],{"class":94},"[] ",[81,842,843],{"class":87},"=",[81,845,825],{"class":94},[81,847,848,851,853,855,858,861],{"class":83,"line":451},[81,849,850],{"class":87},"  for",[81,852,131],{"class":94},[81,854,115],{"class":87},[81,856,857],{"class":220}," file",[81,859,860],{"class":87}," of",[81,862,863],{"class":94}," files) {\n",[81,865,867,870,872,874,877,879,881,884,887,889,891,894,896,898,901,903,906,908],{"class":83,"line":866},7,[81,868,869],{"class":87},"    for",[81,871,131],{"class":94},[81,873,115],{"class":87},[81,875,876],{"class":220}," line",[81,878,860],{"class":87},[81,880,131],{"class":94},[81,882,883],{"class":87},"await",[81,885,886],{"class":94}," file.",[81,888,435],{"class":118},[81,890,747],{"class":94},[81,892,893],{"class":118},"split",[81,895,214],{"class":94},[81,897,217],{"class":101},[81,899,900],{"class":220},"\\r",[81,902,808],{"class":87},[81,904,905],{"class":220},"\\n",[81,907,217],{"class":101},[81,909,910],{"class":94},")) {\n",[81,912,914,917,920,922,925,927],{"class":83,"line":913},8,[81,915,916],{"class":87},"      const",[81,918,919],{"class":220}," trimmed",[81,921,128],{"class":87},[81,923,924],{"class":94}," line.",[81,926,23],{"class":118},[81,928,438],{"class":94},[81,930,932,935,938,940,943,946,949,952,954,957,960],{"class":83,"line":931},9,[81,933,934],{"class":87},"      if",[81,936,937],{"class":94}," (trimmed ",[81,939,152],{"class":87},[81,941,942],{"class":101}," ''",[81,944,945],{"class":87}," ||",[81,947,948],{"class":94}," trimmed.",[81,950,951],{"class":118},"startsWith",[81,953,214],{"class":94},[81,955,956],{"class":101},"'#'",[81,958,959],{"class":94},")) ",[81,961,962],{"class":87},"continue\n",[81,964,966,969],{"class":83,"line":965},10,[81,967,968],{"class":87},"      try",[81,970,395],{"class":94},[81,972,974,977,980,982,985,988,990,992,995,999,1001,1003,1007,1009,1012,1015,1018,1021,1023,1026,1028,1031,1034,1036],{"class":83,"line":973},11,[81,975,976],{"class":87},"        const",[81,978,979],{"class":220}," url",[81,981,128],{"class":87},[81,983,984],{"class":87}," new",[81,986,987],{"class":118}," URL",[81,989,214],{"class":94},[81,991,217],{"class":101},[81,993,994],{"class":87},"^",[81,996,998],{"class":997},"sA_wV","https",[81,1000,808],{"class":87},[81,1002,122],{"class":997},[81,1004,1006],{"class":1005},"snhLl","\\\u002F\\\u002F",[81,1008,217],{"class":101},[81,1010,1011],{"class":87},"i",[81,1013,1014],{"class":94},".",[81,1016,1017],{"class":118},"test",[81,1019,1020],{"class":94},"(trimmed) ",[81,1022,808],{"class":87},[81,1024,1025],{"class":94}," trimmed ",[81,1027,122],{"class":87},[81,1029,1030],{"class":101}," `https:\u002F\u002F${",[81,1032,1033],{"class":94},"trimmed",[81,1035,421],{"class":101},[81,1037,1038],{"class":94},")\n",[81,1040,1042,1045,1048,1050,1053,1055,1058,1060,1063,1066,1069],{"class":83,"line":1041},12,[81,1043,1044],{"class":87},"        if",[81,1046,1047],{"class":94}," (url.protocol ",[81,1049,152],{"class":87},[81,1051,1052],{"class":101}," 'http:'",[81,1054,945],{"class":87},[81,1056,1057],{"class":94}," url.protocol ",[81,1059,152],{"class":87},[81,1061,1062],{"class":101}," 'https:'",[81,1064,1065],{"class":94},") urls.",[81,1067,1068],{"class":118},"push",[81,1070,1071],{"class":94},"(url.href)\n",[81,1073,1075,1078,1081],{"class":83,"line":1074},13,[81,1076,1077],{"class":94},"      } ",[81,1079,1080],{"class":87},"catch",[81,1082,395],{"class":94},[81,1084,1086],{"class":83,"line":1085},14,[81,1087,1088],{"class":327},"        \u002F\u002F a line that cannot become a URL is dropped\n",[81,1090,1092],{"class":83,"line":1091},15,[81,1093,1094],{"class":94},"      }\n",[81,1096,1098],{"class":83,"line":1097},16,[81,1099,1100],{"class":94},"    }\n",[81,1102,1104],{"class":83,"line":1103},17,[81,1105,1106],{"class":94},"  }\n",[81,1108,1110,1112],{"class":83,"line":1109},18,[81,1111,429],{"class":87},[81,1113,1114],{"class":94}," urls\n",[81,1116,1118],{"class":83,"line":1117},19,[81,1119,443],{"class":94},[81,1121,1123],{"class":83,"line":1122},20,[81,1124,109],{"emptyLinePlaceholder":108},[81,1126,1128,1130,1132,1134,1136,1138,1140,1142,1144],{"class":83,"line":1127},21,[81,1129,115],{"class":87},[81,1131,119],{"class":118},[81,1133,122],{"class":87},[81,1135,125],{"class":118},[81,1137,128],{"class":87},[81,1139,131],{"class":94},[81,1141,383],{"class":134},[81,1143,138],{"class":94},[81,1145,1146],{"class":87},"=>\n",[81,1148,1150,1153,1155,1157,1159,1162,1165,1168,1171,1173,1175,1178,1181,1183,1186,1188],{"class":83,"line":1149},22,[81,1151,1152],{"class":94},"  Array.",[81,1154,802],{"class":118},[81,1156,805],{"class":94},[81,1158,808],{"class":87},[81,1160,1161],{"class":94}," value.",[81,1163,1164],{"class":118},"map",[81,1166,1167],{"class":94},"((",[81,1169,1170],{"class":134},"url",[81,1172,138],{"class":94},[81,1174,141],{"class":87},[81,1176,1177],{"class":118}," String",[81,1179,1180],{"class":94},"(url).",[81,1182,164],{"class":118},[81,1184,1185],{"class":94},"()) ",[81,1187,122],{"class":87},[81,1189,1190],{"class":94}," value\n",[81,1192,1194],{"class":83,"line":1193},23,[81,1195,109],{"emptyLinePlaceholder":108},[81,1197,1199,1201,1204,1206,1209],{"class":83,"line":1198},24,[81,1200,115],{"class":87},[81,1202,1203],{"class":220}," form",[81,1205,128],{"class":87},[81,1207,1208],{"class":118}," useForm",[81,1210,1211],{"class":94},"({ schema })\n",[72,1213,1217],{"className":1214,"code":1215,"language":1216,"meta":77,"style":77},"language-vue shiki shiki-themes github-light github-dark","\u003Cinput\n  v-register=\"form.register('links', { transforms: [linesToUrls, lowercase] })\"\n  type=\"file\"\n  accept=\".txt,text\u002Fplain\"\n  multiple\n\u002F>\n","vue",[21,1218,1219,1228,1253,1263,1273,1278],{"__ignoreMap":77},[81,1220,1221,1224],{"class":83,"line":84},[81,1222,1223],{"class":94},"\u003C",[81,1225,1227],{"class":1226},"s9eBZ","input\n",[81,1229,1230,1233,1235,1238,1240,1242,1244,1247,1250],{"class":83,"line":105},[81,1231,1232],{"class":118},"  v-register",[81,1234,843],{"class":94},[81,1236,1237],{"class":101},"\"",[81,1239,271],{"class":94},[81,1241,54],{"class":118},[81,1243,214],{"class":94},[81,1245,1246],{"class":101},"'links'",[81,1248,1249],{"class":94},", { transforms: [linesToUrls, lowercase] })",[81,1251,1252],{"class":101},"\"\n",[81,1254,1255,1258,1260],{"class":83,"line":112},[81,1256,1257],{"class":118},"  type",[81,1259,843],{"class":94},[81,1261,1262],{"class":101},"\"file\"\n",[81,1264,1265,1268,1270],{"class":83,"line":175},[81,1266,1267],{"class":118},"  accept",[81,1269,843],{"class":94},[81,1271,1272],{"class":101},"\".txt,text\u002Fplain\"\n",[81,1274,1275],{"class":83,"line":446},[81,1276,1277],{"class":118},"  multiple\n",[81,1279,1280],{"class":83,"line":451},[81,1281,1282],{"class":94},"\u002F>\n",[17,1284,1285,1286,1289,1290,1293,1294,1297,1298,1301,1302,1305,1306,1309,1310,1313],{},"Because ",[21,1287,1288],{},"linesToUrls"," reads each file with ",[21,1291,1292],{},"await file.text()",", the chain goes async the instant files are picked: ",[21,1295,1296],{},"form.fields('links').busy"," flips true, the resolved ",[21,1299,1300],{},"string[]"," commits when the read finishes, and an unreadable file lands its message on ",[21,1303,1304],{},"form.fields('links').transformError",". (",[21,1307,1308],{},"links"," is an array field, so its rolled-up state reads through the call-form ",[21,1311,1312],{},"form.fields('links')"," rather than the dot-form, which would descend into a child path.)",[67,1315,1317],{"id":1316},"throws-are-caught","Throws are caught",[17,1319,1320,1321,1325,1326,1329,1330,1332],{},"A ",[1322,1323,1324],"em",{},"synchronous"," throw gets caught: the pipeline aborts, nothing writes, and the directive's assigner returns ",[21,1327,1328],{},"false",", so a buggy transform never crashes the host app. (An async rejection takes the ",[21,1331,548],{}," channel above instead, never a throw.) Defensive shape:",[72,1334,1336],{"className":74,"code":1335,"language":76,"meta":77,"style":77},"const safeBigInt: RegisterTransform = (v) => {\n  try {\n    return typeof v === 'string' ? BigInt(v) : v\n  } catch {\n    return v \u002F\u002F pass through; the slim gate will reject the bad value with a friendly message\n  }\n}\n",[21,1337,1338,1361,1368,1395,1404,1413,1417],{"__ignoreMap":77},[81,1339,1340,1342,1345,1347,1349,1351,1353,1355,1357,1359],{"class":83,"line":84},[81,1341,115],{"class":87},[81,1343,1344],{"class":118}," safeBigInt",[81,1346,122],{"class":87},[81,1348,125],{"class":118},[81,1350,128],{"class":87},[81,1352,131],{"class":94},[81,1354,135],{"class":134},[81,1356,138],{"class":94},[81,1358,141],{"class":87},[81,1360,395],{"class":94},[81,1362,1363,1366],{"class":83,"line":105},[81,1364,1365],{"class":87},"  try",[81,1367,395],{"class":94},[81,1369,1370,1373,1376,1378,1380,1382,1384,1387,1390,1392],{"class":83,"line":112},[81,1371,1372],{"class":87},"    return",[81,1374,1375],{"class":87}," typeof",[81,1377,149],{"class":94},[81,1379,152],{"class":87},[81,1381,155],{"class":101},[81,1383,158],{"class":87},[81,1385,1386],{"class":118}," BigInt",[81,1388,1389],{"class":94},"(v) ",[81,1391,122],{"class":87},[81,1393,1394],{"class":94}," v\n",[81,1396,1397,1400,1402],{"class":83,"line":175},[81,1398,1399],{"class":94},"  } ",[81,1401,1080],{"class":87},[81,1403,395],{"class":94},[81,1405,1406,1408,1410],{"class":83,"line":446},[81,1407,1372],{"class":87},[81,1409,149],{"class":94},[81,1411,1412],{"class":327},"\u002F\u002F pass through; the slim gate will reject the bad value with a friendly message\n",[81,1414,1415],{"class":83,"line":451},[81,1416,1106],{"class":94},[81,1418,1419],{"class":83,"line":866},[81,1420,443],{"class":94},[17,1422,1423,1426],{},[21,1424,1425],{},"BigInt('not-a-number')"," throws; the catch lets the original value through, and the schema's leaf validator handles the rejection.",[67,1428,1430],{"id":1429},"where-to-next","Where to next",[1432,1433,1434,1450,1464,1471],"ul",{},[1435,1436,1437,1441,1442,1445,1446,1449],"li",{},[57,1438,1440],{"href":1439},"\u002Fdocs\u002Fbinding-inputs\u002Ffile","File inputs",": the ",[21,1443,1444],{},"File"," and ",[21,1447,1448],{},"File[]"," leaves an async transform reshapes into finished data.",[1435,1451,1452,1455,1456,24,1458,24,1460,1463],{},[57,1453,1454],{"href":334},"Modifiers",": built-in ",[21,1457,297],{},[21,1459,337],{},[21,1461,1462],{},".number"," for the most common reshaping.",[1435,1465,1466,1470],{},[57,1467,1469],{"href":1468},"\u002Fdocs\u002Fbinding-inputs\u002Fcoercion","Schema-driven coercion",": what runs after the transforms array.",[1435,1472,1473,1477,1478,1481],{},[57,1474,1476],{"href":1475},"\u002Fdocs\u002Fbinding-inputs\u002Fcustom-assigners","Custom assigners",": ",[21,1479,1480],{},"assignKey"," for elements whose value surface isn't a DOM property.",[1483,1484,1485],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .snhLl, html code.shiki .snhLl{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":77,"searchDepth":105,"depth":105,"links":1487},[1488,1489,1491,1492,1501,1502,1503],{"id":69,"depth":105,"text":70},{"id":257,"depth":105,"text":1490},"Attach via transforms: [...]",{"id":301,"depth":105,"text":60},{"id":345,"depth":105,"text":346,"children":1493},[1494,1495,1496,1498,1500],{"id":481,"depth":112,"text":482},{"id":537,"depth":112,"text":538},{"id":544,"depth":112,"text":1497},"Failures land on transformError",{"id":574,"depth":112,"text":1499},"Cancel stale work with ctx.signal",{"id":674,"depth":112,"text":675},{"id":691,"depth":105,"text":692},{"id":1316,"depth":105,"text":1317},{"id":1429,"depth":105,"text":1430},"A per-field transforms array on register() composes write-time transformations left-to-right. Trim and dashify in the same tick, or return a Promise to normalize asynchronously while the field reads busy.","md",{},[1508,1511,1515,1518],{"label":1509,"value":1510},"Category","Register option",{"label":1512,"value":1513},"Signature",{"register(path, { transforms":1514,"kind":21},"RegisterTransform[] })",{"label":1516,"value":1517},"Composition","left-to-right",{"label":1519,"value":1520,"kind":21},"Type","(value, ctx?) => unknown | Promise\u003Cunknown>","\u002Fdocs\u002Fbinding-inputs\u002Ftransforms",{"title":5,"description":1504},null,"docs\u002Fbinding-inputs\u002Ftransforms","Nkk38RrQ4IzL6qvBmCkhCWqWJXoCH3k4UFWGeXvjQIc",1780949759481]