[{"data":1,"prerenderedAt":1881},["ShallowReactive",2],{"navigation_docs":3,"-guide-routes":93,"-guide-routes-surround":1876},[4,71],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":70},"Guide","i-lucide-book-open","\u002Fguide","1.guide",[10,14,18,22,26,30,34,38,42,46,50,54,58,62,66],{"title":11,"path":12,"stem":13},"Getting started","\u002Fguide\u002Fgetting-started","1.guide\u002F01.getting-started",{"title":15,"path":16,"stem":17},"Adoption paths","\u002Fguide\u002Fadoption","1.guide\u002F02.adoption",{"title":19,"path":20,"stem":21},"Routes and typed $fetch","\u002Fguide\u002Froutes","1.guide\u002F02.routes",{"title":23,"path":24,"stem":25},"Kyo handlers","\u002Fguide\u002Fkyo","1.guide\u002F03.kyo",{"title":27,"path":28,"stem":29},"ZIO handlers","\u002Fguide\u002Fzio","1.guide\u002F04.zio",{"title":31,"path":32,"stem":33},"Configuration and secrets","\u002Fguide\u002Fconfiguration","1.guide\u002F05.configuration",{"title":35,"path":36,"stem":37},"Application and request lifetimes","\u002Fguide\u002Flifetimes","1.guide\u002F06.lifetimes",{"title":39,"path":40,"stem":41},"Schemas","\u002Fguide\u002Fschema","1.guide\u002F07.schema",{"title":43,"path":44,"stem":45},"PostgreSQL","\u002Fguide\u002Fdatabase","1.guide\u002F08.database",{"title":47,"path":48,"stem":49},"Streaming and cancellation","\u002Fguide\u002Fstreaming","1.guide\u002F09.streaming",{"title":51,"path":52,"stem":53},"Testing","\u002Fguide\u002Ftesting","1.guide\u002F10.testing",{"title":55,"path":56,"stem":57},"Nitro storage and NuxtHub KV","\u002Fguide\u002Fstorage","1.guide\u002F11.storage",{"title":59,"path":60,"stem":61},"Debug Scala handlers","\u002Fguide\u002Fdebugging","1.guide\u002F12.debugging",{"title":63,"path":64,"stem":65},"npm build tooling","\u002Fguide\u002Fnpm-tooling","1.guide\u002F13.npm-tooling",{"title":67,"path":68,"stem":69},"Set up h34s with a coding assistant","\u002Fguide\u002Fagent-setup","1.guide\u002F14.agent-setup",false,{"title":72,"icon":73,"path":74,"stem":75,"children":76,"page":70},"Reference","i-lucide-library","\u002Freference","2.reference",[77,81,85,89],{"title":78,"path":79,"stem":80},"Architecture","\u002Freference\u002Farchitecture","2.reference\u002F1.architecture",{"title":82,"path":83,"stem":84},"Releases","\u002Freference\u002Freleases","2.reference\u002F2.releases",{"title":86,"path":87,"stem":88},"Compatibility","\u002Freference\u002Fcompatibility","2.reference\u002F3.compatibility",{"title":90,"path":91,"stem":92},"Migration guide","\u002Freference\u002Fmigration","2.reference\u002F4.migration",{"id":94,"title":19,"body":95,"description":1870,"extension":1871,"links":1872,"meta":1873,"navigation":215,"path":20,"seo":1874,"stem":21,"__hash__":1875},"docs\u002F1.guide\u002F02.routes.md",{"type":96,"value":97,"toc":1859},"minimark",[98,115,120,478,544,557,562,566,579,614,625,636,646,653,662,756,814,824,932,941,945,961,1258,1267,1270,1371,1392,1396,1399,1633,1638,1642,1645,1670,1692,1695,1816,1820,1855],[99,100,101,102,106,107,110,111,114],"p",{},"Mount your Scala handlers from Scala, and let Nuxt type the calls to them. You write no route file in ",[103,104,105],"code",{},"server\u002Fapi"," and no TypeScript interface for the ",[103,108,109],{},"{ data }"," and ",[103,112,113],{},"{ errors }"," bodies.",[116,117,119],"h2",{"id":118},"mount-a-handler","Mount a handler",[121,122,123,131,461,472],"ol",{},[124,125,126,127,130],"li",{},"Export each handler as the ",[103,128,129],{},"default"," export of its own Scala.js module, as before. The module ID becomes the linked file name.",[124,132,133,134,137,138,339,342,343,346,347],{},"Give the handler a route with ",[103,135,136],{},".at(...)",":",[139,140,145],"pre",{"className":141,"code":142,"language":143,"meta":144,"style":144},"language-scala shiki shiki-themes github-light github-light github-dark","import dev.yuri4n.exts.kyoh3.*\nimport scala.scalajs.js.annotation.JSExportTopLevel\n\n@JSExportTopLevel(\"default\", \"notes-get\")\nval listNotes: EventHandler = AppHandler.define(NotesHandler.list).at(Route.get(\"\u002Fapi\u002Fnotes\"))\n\n@JSExportTopLevel(\"default\", \"notes-post\")\nval createNote: EventHandler = AppHandler.define(NotesHandler.create).at(Route.post(\"\u002Fapi\u002Fnotes\"))\n","scala","",[103,146,147,182,210,217,242,284,289,307],{"__ignoreMap":144},[148,149,152,156,160,164,167,169,172,174,177,179],"span",{"class":150,"line":151},"line",1,[148,153,155],{"class":154},"s8jYJ","import",[148,157,159],{"class":158},"snPdu"," dev",[148,161,163],{"class":162},"sxrX7",".",[148,165,166],{"class":158},"yuri4n",[148,168,163],{"class":162},[148,170,171],{"class":158},"exts",[148,173,163],{"class":162},[148,175,176],{"class":158},"kyoh3",[148,178,163],{"class":162},[148,180,181],{"class":158},"*\n",[148,183,185,187,190,192,195,197,200,202,205,207],{"class":150,"line":184},2,[148,186,155],{"class":154},[148,188,189],{"class":158}," scala",[148,191,163],{"class":162},[148,193,194],{"class":158},"scalajs",[148,196,163],{"class":162},[148,198,199],{"class":158},"js",[148,201,163],{"class":162},[148,203,204],{"class":158},"annotation",[148,206,163],{"class":162},[148,208,209],{"class":158},"JSExportTopLevel\n",[148,211,213],{"class":150,"line":212},3,[148,214,216],{"emptyLinePlaceholder":215},true,"\n",[148,218,220,223,226,229,233,236,239],{"class":150,"line":219},4,[148,221,222],{"class":154},"@",[148,224,225],{"class":158},"JSExportTopLevel",[148,227,228],{"class":162},"(",[148,230,232],{"class":231},"sIIMD","\"default\"",[148,234,235],{"class":162},", ",[148,237,238],{"class":231},"\"notes-get\"",[148,240,241],{"class":162},")\n",[148,243,245,248,252,254,257,260,263,266,269,272,275,278,281],{"class":150,"line":244},5,[148,246,247],{"class":154},"val",[148,249,251],{"class":250},"sP4rz"," listNotes",[148,253,137],{"class":154},[148,255,256],{"class":158}," EventHandler",[148,258,259],{"class":154}," =",[148,261,262],{"class":158}," AppHandler",[148,264,265],{"class":162},".define(",[148,267,268],{"class":158},"NotesHandler",[148,270,271],{"class":162},".list).at(",[148,273,274],{"class":158},"Route",[148,276,277],{"class":162},".get(",[148,279,280],{"class":231},"\"\u002Fapi\u002Fnotes\"",[148,282,283],{"class":162},"))\n",[148,285,287],{"class":150,"line":286},6,[148,288,216],{"emptyLinePlaceholder":215},[148,290,292,294,296,298,300,302,305],{"class":150,"line":291},7,[148,293,222],{"class":154},[148,295,225],{"class":158},[148,297,228],{"class":162},[148,299,232],{"class":231},[148,301,235],{"class":162},[148,303,304],{"class":231},"\"notes-post\"",[148,306,241],{"class":162},[148,308,310,312,315,317,319,321,323,325,327,330,332,335,337],{"class":150,"line":309},8,[148,311,247],{"class":154},[148,313,314],{"class":250}," createNote",[148,316,137],{"class":154},[148,318,256],{"class":158},[148,320,259],{"class":154},[148,322,262],{"class":158},[148,324,265],{"class":162},[148,326,268],{"class":158},[148,328,329],{"class":162},".create).at(",[148,331,274],{"class":158},[148,333,334],{"class":162},".post(",[148,336,280],{"class":231},[148,338,283],{"class":162},[340,341],"br",{},"With a braceless handler function, put ",[103,344,345],{},".at"," on its own line after the function:",[139,348,350],{"className":141,"code":349,"language":143,"meta":144,"style":144},"@JSExportTopLevel(\"default\", \"kyo-greeting-post\")\nval personalizedGreeting: EventHandler = AppHandler\n  .define: request =>\n      for\n        input    \u003C- request.body.json[GreetingRequest]\n        greeting \u003C- Env.use[Greetings](_.greet(input.name))\n      yield greeting\n  .at(Route.post(\"\u002Fapi\u002Fkyo\u002Fgreeting\"))\n",[103,351,352,369,385,398,403,420,439,447],{"__ignoreMap":144},[148,353,354,356,358,360,362,364,367],{"class":150,"line":151},[148,355,222],{"class":154},[148,357,225],{"class":158},[148,359,228],{"class":162},[148,361,232],{"class":231},[148,363,235],{"class":162},[148,365,366],{"class":231},"\"kyo-greeting-post\"",[148,368,241],{"class":162},[148,370,371,373,376,378,380,382],{"class":150,"line":184},[148,372,247],{"class":154},[148,374,375],{"class":250}," personalizedGreeting",[148,377,137],{"class":154},[148,379,256],{"class":158},[148,381,259],{"class":154},[148,383,384],{"class":158}," AppHandler\n",[148,386,387,390,392,395],{"class":150,"line":212},[148,388,389],{"class":162},"  .define",[148,391,137],{"class":154},[148,393,394],{"class":162}," request ",[148,396,397],{"class":154},"=>\n",[148,399,400],{"class":150,"line":219},[148,401,402],{"class":154},"      for\n",[148,404,405,408,411,414,417],{"class":150,"line":244},[148,406,407],{"class":162},"        input    ",[148,409,410],{"class":154},"\u003C-",[148,412,413],{"class":162}," request.body.json[",[148,415,416],{"class":158},"GreetingRequest",[148,418,419],{"class":162},"]\n",[148,421,422,425,427,430,433,436],{"class":150,"line":286},[148,423,424],{"class":162},"        greeting ",[148,426,410],{"class":154},[148,428,429],{"class":158}," Env",[148,431,432],{"class":162},".use[",[148,434,435],{"class":158},"Greetings",[148,437,438],{"class":162},"](_.greet(input.name))\n",[148,440,441,444],{"class":150,"line":291},[148,442,443],{"class":154},"      yield",[148,445,446],{"class":162}," greeting\n",[148,448,449,452,454,456,459],{"class":150,"line":309},[148,450,451],{"class":162},"  .at(",[148,453,274],{"class":158},[148,455,334],{"class":162},[148,457,458],{"class":231},"\"\u002Fapi\u002Fkyo\u002Fgreeting\"",[148,460,283],{"class":162},[124,462,463,464,467,468,471],{},"Link with ",[103,465,466],{},"h34s-scala dev"," (or ",[103,469,470],{},"build","). The command prints how many routes it found.",[124,473,474,475,163],{},"Start Nuxt. The h34s Nuxt module mounts every route. Request it, for example ",[103,476,477],{},"GET \u002Fapi\u002Fnotes",[99,479,480,235,483,235,486,235,489,235,492,235,495,110,498,501,502,505,506,509,510,513,514,517,518,521,522,525,526,529,530,235,533,235,536,539,540,543],{},[103,481,482],{},"Route.get",[103,484,485],{},"post",[103,487,488],{},"put",[103,490,491],{},"patch",[103,493,494],{},"delete",[103,496,497],{},"head",[103,499,500],{},"options"," match one method. ",[103,503,504],{},"Route.any(path)"," matches every method, and ",[103,507,508],{},"Route(Method.Patch, path)"," takes the method as a value. Paths use Nitro's syntax: ",[103,511,512],{},"\u002Fapi\u002Fnotes",", a parameter in ",[103,515,516],{},"\u002Fapi\u002Fnotes\u002F:id"," (read it with ",[103,519,520],{},"request.param(\"id\")","), and a last segment ",[103,523,524],{},"**"," for the rest of the path. A path that does not start with ",[103,527,528],{},"\u002F",", or that contains spaces, ",[103,531,532],{},"?",[103,534,535],{},"#",[103,537,538],{},"\\"," or an empty segment, fails when the module loads, so ",[103,541,542],{},"h34s-scala"," reports it at link time.",[99,545,546,547,550,551,553,554,163],{},"The ZIO API is the same: ",[103,548,549],{},"ZHandler.define(program).at(Route.get(\"\u002Fapi\u002Fnotes\"))",", with ",[103,552,274],{}," from ",[103,555,556],{},"dev.yuri4n.exts.zioh3",[99,558,559,561],{},[103,560,345],{}," returns a new handler and leaves the original unchanged. One linked module mounts one route: export a separate module for each method of a path.",[116,563,565],{"id":564},"what-h34s-does-with-your-routes","What h34s does with your routes",[99,567,568,569,235,572,110,575,578],{},"After linking, ",[103,570,571],{},"h34s-scala build",[103,573,574],{},"dev",[103,576,577],{},"watch"," load your linked modules in a separate Node process and read the route and response types of each default export. Then they write two kinds of files into each Scala.js output directory:",[580,581,582,588,598],"ul",{},[124,583,584,587],{},[103,585,586],{},"h34s-routes.json",", the route manifest: the module, route and method of each mounted handler, and the models file with its model names. ",[124,589,590,593,594,597],{},[103,591,592],{},"\u003Cmodule>.d.ts"," next to each handler module, for example ",[103,595,596],{},"server\u002F.scala\u002Fnotes-get.d.ts",". It declares the module's default export with its real response body.",[124,599,600,603,604,607,608,613],{},[103,601,602],{},"h34s-models.d.ts",": one ",[103,605,606],{},"export type"," for each named model that the routes of the directory return. See ",[609,610,612],"a",{"href":611},"#use-the-models-in-your-app","use the models in your app",". ",[99,615,616,617,620,621,624],{},"The Nuxt module reads the manifests and registers each route with Nitro (",[103,618,619],{},"nitro.handlers",") as a lazy handler: the module loads on its first request. Nitro's ",[103,622,623],{},"routeRules"," apply to these routes as to any other. In development, Nuxt loads the linked files directly with their source maps, as before.",[99,626,627,628,631,632,635],{},"Loading the modules runs their top-level code. Keep top-level handler values free of side effects: build resources in ",[103,629,630],{},"Shared"," or ",[103,633,634],{},"ZShared",", which acquire on first use, and read configuration inside the handler or the shared resource.",[99,637,638,639,642,643,645],{},"When a route is added, removed or changed while ",[103,640,641],{},"nuxt dev"," runs, the next link writes a new manifest and Nuxt reloads its server routes without a restart. A code change alone reloads the Nitro worker, as before. The Scala.js linker deletes the generated files while it links; ",[103,644,542],{}," writes them again right after, and Nuxt keeps the previous routes until it does.",[116,647,649,650],{"id":648},"call-your-routes-with-typed-fetch","Call your routes with typed ",[103,651,652],{},"$fetch",[99,654,655,656,110,658,661],{},"Nuxt types ",[103,657,652],{},[103,659,660],{},"useFetch"," from the generated declarations:",[139,663,667],{"className":664,"code":665,"language":666,"meta":144,"style":144},"language-ts shiki shiki-themes github-light github-light github-dark","const { data } = await $fetch('\u002Fapi\u002Fnotes')\n\u002F\u002F data: { id: number; text: string }[]\nconst created = await $fetch(\n  '\u002Fapi\u002Fnotes',\n  { method: 'POST', body: { text: 'Hello' } }\n)\n\u002F\u002F created.data: { id: number; text: string }\n","ts",[103,668,669,700,706,722,730,747,751],{"__ignoreMap":144},[148,670,671,674,677,681,684,687,690,693,695,698],{"class":150,"line":151},[148,672,673],{"class":154},"const",[148,675,676],{"class":162}," { ",[148,678,680],{"class":679},"sBjJW","data",[148,682,683],{"class":162}," } ",[148,685,686],{"class":154},"=",[148,688,689],{"class":154}," await",[148,691,692],{"class":158}," $fetch",[148,694,228],{"class":162},[148,696,697],{"class":231},"'\u002Fapi\u002Fnotes'",[148,699,241],{"class":162},[148,701,702],{"class":150,"line":184},[148,703,705],{"class":704},"sCsY4","\u002F\u002F data: { id: number; text: string }[]\n",[148,707,708,710,713,715,717,719],{"class":150,"line":212},[148,709,673],{"class":154},[148,711,712],{"class":679}," created",[148,714,259],{"class":154},[148,716,689],{"class":154},[148,718,692],{"class":158},[148,720,721],{"class":162},"(\n",[148,723,724,727],{"class":150,"line":219},[148,725,726],{"class":231},"  '\u002Fapi\u002Fnotes'",[148,728,729],{"class":162},",\n",[148,731,732,735,738,741,744],{"class":150,"line":244},[148,733,734],{"class":162},"  { method: ",[148,736,737],{"class":231},"'POST'",[148,739,740],{"class":162},", body: { text: ",[148,742,743],{"class":231},"'Hello'",[148,745,746],{"class":162}," } }\n",[148,748,749],{"class":150,"line":286},[148,750,241],{"class":162},[148,752,753],{"class":150,"line":291},[148,754,755],{"class":704},"\u002F\u002F created.data: { id: number; text: string }\n",[99,757,758,759,762,763,766,767,770,771,774,775,778,779,782,783,786,787,788,791,792,795,796,798,799,802,803,806,807,810,811,163],{},"The type includes the handler's envelope from ",[103,760,761],{},"HandlerOptions",": ",[103,764,765],{},"{ data: T }"," by default, ",[103,768,769],{},"T"," with ",[103,772,773],{},"SuccessEnvelope.Raw",". A ",[103,776,777],{},"Unit"," body has no content, and a stream sent with ",[103,780,781],{},"Response.sendStream"," is text (",[103,784,785],{},"string","). "," With ",[103,789,790],{},"Raw",", a value that converts to ",[103,793,794],{},"null"," is sent as 204 without a body, so ",[103,797,652],{}," returns ",[103,800,801],{},"undefined",": an ",[103,804,805],{},"Option[Int]"," is typed ",[103,808,809],{},"number | undefined",", not ",[103,812,813],{},"number | null",[99,815,816,817,820,821,137],{},"Errors do not take part in Nitro's types. Each declaration exports the error body as ",[103,818,819],{},"ErrorBody",", in the handler's ",[103,822,823],{},"ErrorEnvelope",[139,825,827],{"className":664,"code":826,"language":666,"meta":144,"style":144},"import type { ErrorBody } from '~~\u002Fserver\u002F.scala\u002Fnotes-post'\n\nawait $fetch('\u002Fapi\u002Fnotes', {\n  method: 'POST',\n  body: { text: '' },\n  onResponseError({ response }) {\n    const body: ErrorBody = response._data\n    \u002F\u002F body.errors: { message: string; path?: string }[]\n  },\n})\n",[103,828,829,845,849,863,872,883,897,915,920,926],{"__ignoreMap":144},[148,830,831,833,836,839,842],{"class":150,"line":151},[148,832,155],{"class":154},[148,834,835],{"class":154}," type",[148,837,838],{"class":162}," { ErrorBody } ",[148,840,841],{"class":154},"from",[148,843,844],{"class":231}," '~~\u002Fserver\u002F.scala\u002Fnotes-post'\n",[148,846,847],{"class":150,"line":184},[148,848,216],{"emptyLinePlaceholder":215},[148,850,851,854,856,858,860],{"class":150,"line":212},[148,852,853],{"class":154},"await",[148,855,692],{"class":158},[148,857,228],{"class":162},[148,859,697],{"class":231},[148,861,862],{"class":162},", {\n",[148,864,865,868,870],{"class":150,"line":219},[148,866,867],{"class":162},"  method: ",[148,869,737],{"class":231},[148,871,729],{"class":162},[148,873,874,877,880],{"class":150,"line":244},[148,875,876],{"class":162},"  body: { text: ",[148,878,879],{"class":231},"''",[148,881,882],{"class":162}," },\n",[148,884,885,888,891,894],{"class":150,"line":286},[148,886,887],{"class":158},"  onResponseError",[148,889,890],{"class":162},"({ ",[148,892,893],{"class":250},"response",[148,895,896],{"class":162}," }) {\n",[148,898,899,902,905,907,910,912],{"class":150,"line":291},[148,900,901],{"class":154},"    const",[148,903,904],{"class":679}," body",[148,906,137],{"class":154},[148,908,909],{"class":158}," ErrorBody",[148,911,259],{"class":154},[148,913,914],{"class":162}," response._data\n",[148,916,917],{"class":150,"line":309},[148,918,919],{"class":704},"    \u002F\u002F body.errors: { message: string; path?: string }[]\n",[148,921,923],{"class":150,"line":922},9,[148,924,925],{"class":162},"  },\n",[148,927,929],{"class":150,"line":928},10,[148,930,931],{"class":162},"})\n",[99,933,934,937,938,163],{},[103,935,936],{},"debug"," appears only on internal errors in development or debug mode. See ",[609,939,940],{"href":60},"debugging",[116,942,944],{"id":943},"use-the-models-in-your-app","Use the models in your app",[99,946,947,948,950,951,953,954,957,958,163],{},"Your Scala models also have names in TypeScript. ",[103,949,542],{}," declares each model that your routes return once, in ",[103,952,602],{}," next to the route declarations, and the h34s Nuxt module maps ",[103,955,956],{},"#h34s\u002Fmodels"," to it. Import a model wherever your app needs it: in a composable, a component prop or ",[103,959,960],{},"useAsyncData",[121,962,963,1040,1147,1160],{},[124,964,965,966,969,970,973,974,977,978,981,982],{},"Derive ",[103,967,968],{},"NativeSchema"," for the model that your route returns. The Notes example calls its wire model ",[103,971,972],{},"NoteView"," (",[103,975,976],{},"examples\u002Fnuxt\u002Fscala\u002Fzio\u002F...\u002Fnotes\u002FNotes.scala",", region ",[103,979,980],{},"guide-models-scala","):",[139,983,985],{"className":141,"code":984,"language":143,"meta":144,"style":144},"\u002F** The JSON that the Notes routes send. One derivation: the ZIO Schema and the native converter. *\u002F\nfinal case class NoteView(id: Int, text: String) derives Schema, NativeSchema\n",[103,986,987,992],{"__ignoreMap":144},[148,988,989],{"class":150,"line":151},[148,990,991],{"class":704},"\u002F** The JSON that the Notes routes send. One derivation: the ZIO Schema and the native converter. *\u002F\n",[148,993,994,997,1000,1003,1006,1008,1011,1013,1016,1018,1021,1023,1026,1029,1032,1035,1037],{"class":150,"line":184},[148,995,996],{"class":154},"final",[148,998,999],{"class":154}," case",[148,1001,1002],{"class":154}," class",[148,1004,1005],{"class":158}," NoteView",[148,1007,228],{"class":162},[148,1009,1010],{"class":250},"id",[148,1012,762],{"class":162},[148,1014,1015],{"class":158},"Int",[148,1017,235],{"class":162},[148,1019,1020],{"class":250},"text",[148,1022,762],{"class":162},[148,1024,1025],{"class":158},"String",[148,1027,1028],{"class":162},") ",[148,1030,1031],{"class":154},"derives",[148,1033,1034],{"class":158}," Schema",[148,1036,235],{"class":162},[148,1038,1039],{"class":158},"NativeSchema\n",[124,1041,463,1042,1044,1045,1048,1049,1052,1053],{},[103,1043,466],{},". The command prints the routes and the models it found, for example ",[103,1046,1047],{},"[h34s] 5 routes and 2 models in ...",". It writes the model into ",[103,1050,1051],{},"server\u002F.scala\u002Fh34s-models.d.ts",", and the route declaration refers to it:",[139,1054,1056],{"className":664,"code":1055,"language":666,"meta":144,"style":144},"\u002F\u002F server\u002F.scala\u002Fh34s-models.d.ts\nexport type NoteView = { id: number; text: string }\n\n\u002F\u002F server\u002F.scala\u002Fnotes-get.d.ts\nimport type { NoteView } from '.\u002Fh34s-models'\nexport type SuccessBody = { data: Array\u003CNoteView> }\n",[103,1057,1058,1063,1096,1100,1105,1119],{"__ignoreMap":144},[148,1059,1060],{"class":150,"line":151},[148,1061,1062],{"class":704},"\u002F\u002F server\u002F.scala\u002Fh34s-models.d.ts\n",[148,1064,1065,1068,1070,1072,1074,1076,1078,1080,1083,1086,1088,1090,1093],{"class":150,"line":184},[148,1066,1067],{"class":154},"export",[148,1069,835],{"class":154},[148,1071,1005],{"class":158},[148,1073,259],{"class":154},[148,1075,676],{"class":162},[148,1077,1010],{"class":250},[148,1079,137],{"class":154},[148,1081,1082],{"class":679}," number",[148,1084,1085],{"class":162},"; ",[148,1087,1020],{"class":250},[148,1089,137],{"class":154},[148,1091,1092],{"class":679}," string",[148,1094,1095],{"class":162}," }\n",[148,1097,1098],{"class":150,"line":212},[148,1099,216],{"emptyLinePlaceholder":215},[148,1101,1102],{"class":150,"line":219},[148,1103,1104],{"class":704},"\u002F\u002F server\u002F.scala\u002Fnotes-get.d.ts\n",[148,1106,1107,1109,1111,1114,1116],{"class":150,"line":244},[148,1108,155],{"class":154},[148,1110,835],{"class":154},[148,1112,1113],{"class":162}," { NoteView } ",[148,1115,841],{"class":154},[148,1117,1118],{"class":231}," '.\u002Fh34s-models'\n",[148,1120,1121,1123,1125,1128,1130,1132,1134,1136,1139,1142,1144],{"class":150,"line":286},[148,1122,1067],{"class":154},[148,1124,835],{"class":154},[148,1126,1127],{"class":158}," SuccessBody",[148,1129,259],{"class":154},[148,1131,676],{"class":162},[148,1133,680],{"class":250},[148,1135,137],{"class":154},[148,1137,1138],{"class":158}," Array",[148,1140,1141],{"class":162},"\u003C",[148,1143,972],{"class":158},[148,1145,1146],{"class":162},"> }\n",[124,1148,1149,1150,1152,1153,1156,1157,163],{},"Start ",[103,1151,641],{},", or run ",[103,1154,1155],{},"nuxt prepare",", so that Nuxt writes the alias into ",[103,1158,1159],{},".nuxt\u002Ftsconfig.json",[124,1161,1162,1163,1166,1167,977,1170,981,1173,1248,1250,1251,1254,1255,163],{},"Import the model with ",[103,1164,1165],{},"import type",". This is the example's composable (",[103,1168,1169],{},"examples\u002Fnuxt\u002Fapp\u002Fcomposables\u002FuseNotes.ts",[103,1171,1172],{},"guide-models-ts",[139,1174,1176],{"className":664,"code":1175,"language":666,"meta":144,"style":144},"import type { NoteView } from '#h34s\u002Fmodels'\n\n\u002F** The notes of `GET \u002Fapi\u002Fnotes`, typed with the generated `NoteView`. *\u002F\nexport function useNotes() {\n  return useAsyncData\u003CNoteView[]>('notes', () => fetchNotes())\n}\n",[103,1177,1178,1191,1195,1200,1213,1243],{"__ignoreMap":144},[148,1179,1180,1182,1184,1186,1188],{"class":150,"line":151},[148,1181,155],{"class":154},[148,1183,835],{"class":154},[148,1185,1113],{"class":162},[148,1187,841],{"class":154},[148,1189,1190],{"class":231}," '#h34s\u002Fmodels'\n",[148,1192,1193],{"class":150,"line":184},[148,1194,216],{"emptyLinePlaceholder":215},[148,1196,1197],{"class":150,"line":212},[148,1198,1199],{"class":704},"\u002F** The notes of `GET \u002Fapi\u002Fnotes`, typed with the generated `NoteView`. *\u002F\n",[148,1201,1202,1204,1207,1210],{"class":150,"line":219},[148,1203,1067],{"class":154},[148,1205,1206],{"class":154}," function",[148,1208,1209],{"class":158}," useNotes",[148,1211,1212],{"class":162},"() {\n",[148,1214,1215,1218,1221,1223,1225,1228,1231,1234,1237,1240],{"class":150,"line":244},[148,1216,1217],{"class":154},"  return",[148,1219,1220],{"class":158}," useAsyncData",[148,1222,1141],{"class":162},[148,1224,972],{"class":158},[148,1226,1227],{"class":162},"[]>(",[148,1229,1230],{"class":231},"'notes'",[148,1232,1233],{"class":162},", () ",[148,1235,1236],{"class":154},"=>",[148,1238,1239],{"class":158}," fetchNotes",[148,1241,1242],{"class":162},"())\n",[148,1244,1245],{"class":150,"line":286},[148,1246,1247],{"class":162},"}\n",[340,1249],{},"The example's ",[103,1252,1253],{},"NoteList"," component takes the notes as a typed prop: ",[103,1256,1257],{},"defineProps\u003C{ notes: NoteView[] }>()",[99,1259,1260,1261,1263,1264,1266],{},"Always write ",[103,1262,1165],{},". The models file has types only, so a value import of ",[103,1265,956],{}," fails when Vite bundles your app.",[99,1268,1269],{},"Which types get a name:",[580,1271,1272,1279,1288,1298,1324,1335,1364],{},[124,1273,1274,1275,1278],{},"The Kyo bridge names each record, sealed trait and enum after its Scala type (",[103,1276,1277],{},"Note","). The variants of a sealed trait or an enum stay inside the sum's type.",[124,1280,1281,1282,973,1285,1287],{},"The ZIO bridge names each record after its ",[103,1283,1284],{},"TypeId",[103,1286,972],{},").",[124,1289,1290,1291,1294,1295,163],{},"Your own converter can name its type: ",[103,1292,1293],{},"val wireType = WireType.Named(\"Email\", WireType.StringValue)"," gives ",[103,1296,1297],{},"export type Email = string",[124,1299,1300,1301,1294,1304,1307,1308,1310,1311,1313,1314,1317,1318,1320,1321,1323],{},"A generic type is written out where it is used: ",[103,1302,1303],{},"Page[Note]",[103,1305,1306],{},"{ items: Array\u003CNote>; total: number }",", and ",[103,1309,1277],{}," keeps its name inside. ZIO's ",[103,1312,1284],{}," has no type arguments, so the ZIO bridge names a generic record after its class (",[103,1315,1316],{},"Page","). When two of its instantiations meet, in one output or in two, ",[103,1319,542],{}," writes ",[103,1322,1316],{}," out where it is used, and the routes that return it use their full type.",[124,1325,1326,1327,1330,1331,1334],{},"A model refers to itself by name: a recursive ZIO record gives ",[103,1328,1329],{},"type Tree = { value: number; children: Array\u003CTree> }",". A recursive Kyo model is ",[103,1332,1333],{},"unknown",", as before.",[124,1336,1337,1338,631,1341,1343,1344,235,1347,235,1350,235,1353,235,1356,235,1358,110,1361,163],{},"A name that is not a TypeScript identifier, or that TypeScript or h34s reserve, is written out where it is used. The reserved names are the JavaScript and TypeScript keywords (such as ",[103,1339,1340],{},"class",[103,1342,785],{},") and ",[103,1345,1346],{},"Array",[103,1348,1349],{},"Record",[103,1351,1352],{},"Promise",[103,1354,1355],{},"SuccessBody",[103,1357,819],{},[103,1359,1360],{},"EventHandler",[103,1362,1363],{},"EventHandlerRequest",[124,1365,1366,1367,163],{},"A model with the same name and the same structure is declared once, even when two Scala types or two routes give it. Two Scala types with the same name and different structures stop the generation; see ",[609,1368,1370],{"href":1369},"#troubleshooting","troubleshooting",[99,1372,1373,1374,1376,1377,1379,1380,1383,1384,1387,1388,1391],{},"With one Scala.js output, ",[103,1375,956],{}," is that output's ",[103,1378,602],{},". With several outputs, it is ",[103,1381,1382],{},".nuxt\u002Fh34s\u002Fmodels.d.ts",", which re-exports the models of every output, so a name must mean one structure across all your outputs. To point the alias somewhere else, set ",[103,1385,1386],{},"alias['#h34s\u002Fmodels']"," in ",[103,1389,1390],{},"nuxt.config.ts",": the module keeps your value.",[116,1393,1395],{"id":1394},"where-the-types-come-from","Where the types come from",[99,1397,1398],{},"h34s writes the type of what your converter really writes, not a type guessed from the Scala class:",[580,1400,1401,1426,1469],{},[124,1402,1403,1404,1406,1407,1409,1410,1413,1414,1417,1418,1421,1422,613],{},"A ",[103,1405,968],{}," type (Kyo Schema or ZIO Schema) is a named model (see ",[609,1408,612],{"href":611},") and gives its fields with their wire names, optional fields as ",[103,1411,1412],{},"T | null"," (Kyo: may also be absent), collections as arrays, string-keyed maps as ",[103,1415,1416],{},"Record\u003Cstring, T>",", and sealed traits and enums as one-key objects or objects with the ",[103,1419,1420],{},"@discriminator"," field. The Kyo bridge also checks the type against its encoder with sample values; see ",[609,1423,1425],{"href":1424},"\u002Fguide\u002Fschema#typescript-types","schemas",[124,1427,1428,1429,235,1431,110,1434,1437,1438,1440,1441,1444,1445,235,1448,235,1451,235,1454,235,1457,235,1460,235,1462,110,1465,1468],{},"native-converter's built-in converters give theirs: ",[103,1430,1025],{},[103,1432,1433],{},"Char",[103,1435,1436],{},"Long"," are strings (native-converter writes ",[103,1439,1436],{}," as text), the other numbers are numbers, ",[103,1442,1443],{},"Boolean"," is a boolean, and ",[103,1446,1447],{},"List",[103,1449,1450],{},"Vector",[103,1452,1453],{},"Seq",[103,1455,1456],{},"Set",[103,1458,1459],{},"Iterable",[103,1461,1346],{},[103,1463,1464],{},"Option",[103,1466,1467],{},"Map[String, A]"," follow their elements.",[124,1470,1471,1472,1475,1476,1478,1479,1482,1483,1627,1629,1630,163],{},"Any other converter, such as ",[103,1473,1474],{},"derives NativeConverter"," or a hand-written one, gives ",[103,1477,1333],{},". Mix ",[103,1480,1481],{},"WireType.Described"," into your own converter to type it:",[139,1484,1486],{"className":141,"code":1485,"language":143,"meta":144,"style":144},"given NativeConverter[Email] = new NativeConverter[Email] with WireType.Described:\n  val wireType = WireType.StringValue\n  extension (email: Email) def toNative: js.Any = email.value\n  def fromNative(state: ParseState): Email = Email(state.json.asInstanceOf[String])\n",[103,1487,1488,1532,1549,1584],{"__ignoreMap":144},[148,1489,1490,1493,1496,1499,1502,1505,1507,1510,1512,1514,1516,1518,1521,1524,1526,1529],{"class":150,"line":151},[148,1491,1492],{"class":154},"given",[148,1494,1495],{"class":158}," NativeConverter",[148,1497,1498],{"class":162},"[",[148,1500,1501],{"class":158},"Email",[148,1503,1504],{"class":162},"] ",[148,1506,686],{"class":154},[148,1508,1509],{"class":154}," new",[148,1511,1495],{"class":158},[148,1513,1498],{"class":162},[148,1515,1501],{"class":158},[148,1517,1504],{"class":162},[148,1519,1520],{"class":154},"with",[148,1522,1523],{"class":158}," WireType",[148,1525,163],{"class":162},[148,1527,1528],{"class":158},"Described",[148,1530,1531],{"class":154},":\n",[148,1533,1534,1537,1540,1542,1544,1546],{"class":150,"line":184},[148,1535,1536],{"class":154},"  val",[148,1538,1539],{"class":250}," wireType",[148,1541,259],{"class":154},[148,1543,1523],{"class":158},[148,1545,163],{"class":162},[148,1547,1548],{"class":158},"StringValue\n",[148,1550,1551,1554,1556,1559,1561,1563,1565,1568,1571,1573,1576,1579,1581],{"class":150,"line":212},[148,1552,1553],{"class":154},"  extension",[148,1555,973],{"class":162},[148,1557,1558],{"class":250},"email",[148,1560,762],{"class":162},[148,1562,1501],{"class":158},[148,1564,1028],{"class":162},[148,1566,1567],{"class":154},"def",[148,1569,1570],{"class":158}," toNative",[148,1572,137],{"class":154},[148,1574,1575],{"class":162}," js.",[148,1577,1578],{"class":158},"Any",[148,1580,259],{"class":154},[148,1582,1583],{"class":162}," email.value\n",[148,1585,1586,1589,1592,1594,1597,1599,1602,1605,1607,1610,1612,1614,1617,1620,1622,1624],{"class":150,"line":219},[148,1587,1588],{"class":154},"  def",[148,1590,1591],{"class":158}," fromNative",[148,1593,228],{"class":162},[148,1595,1596],{"class":250},"state",[148,1598,762],{"class":162},[148,1600,1601],{"class":158},"ParseState",[148,1603,1604],{"class":162},")",[148,1606,137],{"class":154},[148,1608,1609],{"class":158}," Email",[148,1611,259],{"class":154},[148,1613,1609],{"class":158},[148,1615,1616],{"class":162},"(state.json.",[148,1618,1619],{"class":679},"asInstanceOf",[148,1621,1498],{"class":162},[148,1623,1025],{"class":158},[148,1625,1626],{"class":162},"])\n",[340,1628],{},"Check your declared type in a test with ",[103,1631,1632],{},"wireType.describes(value)",[99,1634,1635,1637],{},[103,1636,1333],{}," is safe: TypeScript makes you check the value before you use it. h34s never writes a type that the converter might not follow.",[116,1639,1641],{"id":1640},"mount-a-handler-with-a-route-file-instead","Mount a handler with a route file instead",[99,1643,1644],{},"The one-line route file still works, for example when you prefer Nuxt's file layout for a route:",[139,1646,1648],{"className":664,"code":1647,"language":666,"meta":144,"style":144},"\u002F\u002F server\u002Fapi\u002Fnotes.get.ts\nexport { default } from '..\u002F.scala\u002Fnotes-get.js'\n",[103,1649,1650,1655],{"__ignoreMap":144},[148,1651,1652],{"class":150,"line":151},[148,1653,1654],{"class":704},"\u002F\u002F server\u002Fapi\u002Fnotes.get.ts\n",[148,1656,1657,1659,1661,1663,1665,1667],{"class":150,"line":184},[148,1658,1067],{"class":154},[148,1660,676],{"class":162},[148,1662,129],{"class":154},[148,1664,683],{"class":162},[148,1666,841],{"class":154},[148,1668,1669],{"class":231}," '..\u002F.scala\u002Fnotes-get.js'\n",[99,1671,1672,1673,1675,1676,1678,1679,1387,1682,1685,1686,1688,1689,1691],{},"Leave ",[103,1674,345],{}," off that handler, or remove the route file when you add ",[103,1677,345],{},": Nitro would register both. The declaration next to the linked module also types the route file. Keep ",[103,1680,1681],{},"entries",[103,1683,1684],{},"h34s.config.mjs"," for outputs that have only such handlers, so that ",[103,1687,542],{}," checks the linked files; without ",[103,1690,1681],{},", an output must contain at least one h34s handler.",[116,1693,1694],{"id":1370},"Troubleshooting",[580,1696,1697,1710,1718,1736,1750,1763,1776,1802],{},[124,1698,1699,1703,1704,1706,1707,1709],{},[1700,1701,1702],"strong",{},"404 for a Scala route."," Run ",[103,1705,466],{}," and check that it prints your route. If Nuxt says that ",[103,1708,586],{}," is missing, it started before the first link: link, then restart Nuxt.",[124,1711,1712,1717],{},[1700,1713,1714,163],{},[103,1715,1716],{},"Two Scala.js handlers mount GET \u002Fapi\u002Fx"," Two exports have the same route and method. Change one route or method.",[124,1719,1720,1725,1726,631,1729,1732,1733,1735],{},[1700,1721,1722,163],{},[103,1723,1724],{},"... has no h34s handlers"," No module in that output has a default export from ",[103,1727,1728],{},"KHandler.define",[103,1730,1731],{},"ZHandler.define",". Export one, or list ",[103,1734,1681],{}," for route files.",[124,1737,1738,1743,1744,1746,1747,1749],{},[1700,1739,1740,163],{},[103,1741,1742],{},"Could not load the linked Scala.js module"," A module's top-level code failed while ",[103,1745,542],{}," loaded it. The message names the module and shows the stack (with Scala lines after ",[103,1748,574],{},", which links with source maps).",[124,1751,1752,1757,1758,1760,1761,163],{},[1700,1753,1754,1755,163],{},"A response is ",[103,1756,1333],{}," Its converter does not describe itself. Derive ",[103,1759,968],{},", or mix in ",[103,1762,1481],{},[124,1764,1765,1771,1772,1775],{},[1700,1766,1767,1770],{},[103,1768,1769],{},"Cannot find module 'h3'"," in a declaration."," Add ",[103,1773,1774],{},"h3@1.15.4"," to your app's dependencies, as the linked modules need anyway.",[124,1777,1778,467,1783,1786,1787,1789,1790,1793,1794,1797,1798,1801],{},[1700,1779,1780],{},[103,1781,1782],{},"two Scala types have the TypeScript name Note and different structures",[103,1784,1785],{},"several Scala types","). Two models with one name reach the same models file, or two outputs that ",[103,1788,956],{}," exports together. The message names each Scala type and the routes that use it. A hand-written ",[103,1791,1792],{},"WireType.Named"," without a Scala type shows as ",[103,1795,1796],{},"a type without a known Scala name",". Rename one of the Scala types. h34s does not rename a model for you, because a silent ",[103,1799,1800],{},"Note2"," would change your app's types without a warning. ",[124,1803,1804,1809,1810,1812,1813,1815],{},[1700,1805,1806,163],{},[103,1807,1808],{},"Cannot find module '#h34s\u002Fmodels'"," Link first, so that the models file exists, then restart ",[103,1811,641],{}," or run ",[103,1814,1155],{},", so that Nuxt writes the alias into its tsconfig. ",[116,1817,1819],{"id":1818},"limits","Limits",[580,1821,1822,1825,1838,1849,1852],{},[124,1823,1824],{},"Nitro types response bodies only. Request bodies and query strings have no generated type.",[124,1826,1827,1828,1831,1832,786],{},"The models file holds the models that your routes return. A request-body model, such as the example's ",[103,1829,1830],{},"CreateNote",", gets its name with the typed route registry (",[609,1833,1837],{"href":1834,"rel":1835},"https:\u002F\u002Fgithub.com\u002Fyuri4n\u002Fh34s\u002Fissues\u002F46",[1836],"nofollow","#46",[124,1839,1840,1841,1844,1845,1848],{},"Generic models such as ",[103,1842,1843],{},"Page[A]"," have no TypeScript generic (",[103,1846,1847],{},"Page\u003CA>",") yet: each use is written out. ",[124,1850,1851],{},"Only default exports are mounted. A module ID can hold one route.",[124,1853,1854],{},"Generated types are recreated on each link. In development, your editor can show untyped calls for a moment while the linker runs.",[1856,1857,1858],"style",{},"html pre.shiki code .s8jYJ, html code.shiki .s8jYJ{--shiki-light:#D73A49;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .snPdu, html code.shiki .snPdu{--shiki-light:#6F42C1;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sxrX7, html code.shiki .sxrX7{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sIIMD, html code.shiki .sIIMD{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sP4rz, html code.shiki .sP4rz{--shiki-light:#E36209;--shiki-default:#E36209;--shiki-dark:#FFAB70}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sCsY4, html code.shiki .sCsY4{--shiki-light:#6A737D;--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sBjJW, html code.shiki .sBjJW{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":144,"searchDepth":184,"depth":184,"links":1860},[1861,1862,1863,1865,1866,1867,1868,1869],{"id":118,"depth":184,"text":119},{"id":564,"depth":184,"text":565},{"id":648,"depth":184,"text":1864},"Call your routes with typed $fetch",{"id":943,"depth":184,"text":944},{"id":1394,"depth":184,"text":1395},{"id":1640,"depth":184,"text":1641},{"id":1370,"depth":184,"text":1694},{"id":1818,"depth":184,"text":1819},"Mount Scala handlers from Scala with .at(Route...), and let Nuxt type $fetch from the generated declarations.","md",null,{},{"title":19,"description":1870},"tAGVjgkG8u2RMu0R01wJ-sd0iXWSVjyyhOwhEwCX28Q",[1877,1879],{"title":15,"path":16,"stem":17,"description":1878,"children":-1},"Add h34s to a new Nuxt app, an existing Nuxt app, or an existing Scala.js project.",{"title":23,"path":24,"stem":25,"description":1880,"children":-1},"Write handlers as Kyo functions from the request to the body, set the response head, fail safely, and wire services in your AppHandler.",1792533383165]