[{"data":1,"prerenderedAt":4310},["ShallowReactive",2],{"navigation_docs_en":3,"-en-framework-server":448,"-en-framework-server-surround":4305},[4,45,120,157,173,203,233,259,271,287,307,326,339,356,373,386,398,425],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F01.getting-started",[10,15,20,25,30,35,40],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F01.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F01.getting-started\u002F2.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F01.getting-started\u002F3.first-module","i-lucide-package-plus",{"title":26,"path":27,"stem":28,"icon":29},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F01.getting-started\u002F4.project-structure","i-lucide-folder-tree",{"title":31,"path":32,"stem":33,"icon":34},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F01.getting-started\u002F5.sample-app","i-lucide-shopping-cart",{"title":36,"path":37,"stem":38,"icon":39},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F01.getting-started\u002F6.coming-from-laravel","i-lucide-arrow-right-left",{"title":41,"path":42,"stem":43,"icon":39},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F01.getting-started\u002F7.coming-from-symfony",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F02.cli",[51,55,60,65,70,75,80,85,90,95,100,105,110,115],{"title":52,"path":53,"stem":54,"icon":47},"Overview","\u002Fen\u002Fcli\u002Foverview","en\u002F02.cli\u002F1.overview",{"title":56,"path":57,"stem":58,"icon":59},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F02.cli\u002F10.add-compose","i-lucide-container",{"title":61,"path":62,"stem":63,"icon":64},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F02.cli\u002F11.add-mail","i-lucide-mail-plus",{"title":66,"path":67,"stem":68,"icon":69},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F02.cli\u002F12.add-notification","i-lucide-bell-plus",{"title":71,"path":72,"stem":73,"icon":74},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F02.cli\u002F13.add-realtime","i-lucide-radio-tower",{"title":76,"path":77,"stem":78,"icon":79},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F02.cli\u002F14.add-seeder","i-lucide-sprout",{"title":81,"path":82,"stem":83,"icon":84},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F02.cli\u002F2.new-project","i-lucide-folder-plus",{"title":86,"path":87,"stem":88,"icon":89},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F02.cli\u002F3.new-module","i-lucide-blocks",{"title":91,"path":92,"stem":93,"icon":94},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F02.cli\u002F4.add-surface","i-lucide-layers-2",{"title":96,"path":97,"stem":98,"icon":99},"add domain","\u002Fen\u002Fcli\u002Fadd-domain","en\u002F02.cli\u002F5.add-domain","i-lucide-box",{"title":101,"path":102,"stem":103,"icon":104},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F02.cli\u002F6.add-handler","i-lucide-webhook",{"title":106,"path":107,"stem":108,"icon":109},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F02.cli\u002F7.new-migration","i-lucide-file-plus",{"title":111,"path":112,"stem":113,"icon":114},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F02.cli\u002F8.worker-generators","i-lucide-cog",{"title":116,"path":117,"stem":118,"icon":119},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F02.cli\u002F9.add-db","i-lucide-database-zap",{"title":121,"icon":99,"path":122,"stem":123,"children":124,"page":44},"Framework","\u002Fen\u002Fframework","en\u002F03.framework",[125,128,133,138,142,147,152],{"title":52,"path":126,"stem":127,"icon":99},"\u002Fen\u002Fframework\u002Foverview","en\u002F03.framework\u002F1.overview",{"title":129,"path":130,"stem":131,"icon":132},"Application lifecycle","\u002Fen\u002Fframework\u002Fapp","en\u002F03.framework\u002F2.app","i-lucide-power",{"title":134,"path":135,"stem":136,"icon":137},"Server","\u002Fen\u002Fframework\u002Fserver","en\u002F03.framework\u002F3.server","i-lucide-server",{"title":139,"path":140,"stem":141,"icon":114},"Worker","\u002Fen\u002Fframework\u002Fworker","en\u002F03.framework\u002F4.worker",{"title":143,"path":144,"stem":145,"icon":146},"Realtime","\u002Fen\u002Fframework\u002Frealtime","en\u002F03.framework\u002F5.realtime","i-lucide-radio",{"title":148,"path":149,"stem":150,"icon":151},"Migrations","\u002Fen\u002Fframework\u002Fmigrations","en\u002F03.framework\u002F6.migrations","i-lucide-file-stack",{"title":153,"path":154,"stem":155,"icon":156},"Social login","\u002Fen\u002Fframework\u002Fsocial-login","en\u002F03.framework\u002F7.social-login","i-lucide-log-in",{"title":158,"icon":159,"path":160,"stem":161,"children":162,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F04.reference",[163,168],{"title":164,"path":165,"stem":166,"icon":167},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F04.reference\u002F1.configuration","i-lucide-settings",{"title":169,"path":170,"stem":171,"icon":172},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F04.reference\u002F2.dependencies","i-lucide-package",{"title":174,"icon":175,"path":176,"stem":177,"children":178,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F05.concepts",[179,184,189,193,198],{"title":180,"path":181,"stem":182,"icon":183},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F05.concepts\u002F1.architecture","i-lucide-layers",{"title":185,"path":186,"stem":187,"icon":188},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F05.concepts\u002F2.error-model","i-lucide-shield-alert",{"title":164,"path":190,"stem":191,"icon":192},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F05.concepts\u002F3.configuration","i-lucide-settings-2",{"title":194,"path":195,"stem":196,"icon":197},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F05.concepts\u002F4.codegen-pipeline","i-lucide-file-json",{"title":199,"path":200,"stem":201,"icon":202},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F05.concepts\u002F5.design-patterns","i-lucide-puzzle",{"title":204,"icon":205,"path":206,"stem":207,"children":208,"page":44},"auth","i-lucide-shield-check","\u002Fen\u002Fauth","en\u002F06.auth",[209,214,219,224,229],{"title":210,"path":211,"stem":212,"icon":213},"Authentication","\u002Fen\u002Fauth\u002Foverview","en\u002F06.auth\u002F1.overview","i-lucide-key-round",{"title":215,"path":216,"stem":217,"icon":218},"RBAC","\u002Fen\u002Fauth\u002Frbac","en\u002F06.auth\u002F2.rbac","i-lucide-users",{"title":220,"path":221,"stem":222,"icon":223},"Policies","\u002Fen\u002Fauth\u002Fpolicy","en\u002F06.auth\u002F3.policy","i-lucide-gavel",{"title":225,"path":226,"stem":227,"icon":228},"Using auth standalone","\u002Fen\u002Fauth\u002Fstandalone","en\u002F06.auth\u002F4.standalone","i-lucide-plug",{"title":230,"path":231,"stem":232,"icon":156},"Social login with goth, standalone","\u002Fen\u002Fauth\u002Fsocial-standalone","en\u002F06.auth\u002F5.social-standalone",{"title":234,"icon":235,"path":236,"stem":237,"children":238,"page":44},"dbx","i-lucide-database","\u002Fen\u002Fdbx","en\u002F07.dbx",[239,242,246,250,255],{"title":52,"path":240,"stem":241,"icon":235},"\u002Fen\u002Fdbx\u002Foverview","en\u002F07.dbx\u002F1.overview",{"title":243,"path":244,"stem":245,"icon":228},"pgx","\u002Fen\u002Fdbx\u002Fpg","en\u002F07.dbx\u002F2.pg",{"title":247,"path":248,"stem":249,"icon":183},"bun","\u002Fen\u002Fdbx\u002Fbunx","en\u002F07.dbx\u002F3.bunx",{"title":251,"path":252,"stem":253,"icon":254},"Transactions","\u002Fen\u002Fdbx\u002Ftransactions","en\u002F07.dbx\u002F4.transactions","i-lucide-git-merge",{"title":256,"path":257,"stem":258,"icon":79},"Seeders","\u002Fen\u002Fdbx\u002Fseed","en\u002F07.dbx\u002F5.seed",{"title":260,"icon":192,"path":261,"stem":262,"children":263,"page":44},"envconf","\u002Fen\u002Fenvconf","en\u002F08.envconf",[264,267],{"title":52,"path":265,"stem":266,"icon":192},"\u002Fen\u002Fenvconf\u002Foverview","en\u002F08.envconf\u002F1.overview",{"title":268,"path":269,"stem":270,"icon":192},"Recipes","\u002Fen\u002Fenvconf\u002Frecipes","en\u002F08.envconf\u002F2.recipes",{"title":272,"icon":188,"path":273,"stem":274,"children":275,"page":44},"errs","\u002Fen\u002Ferrs","en\u002F09.errs",[276,279,283],{"title":52,"path":277,"stem":278,"icon":188},"\u002Fen\u002Ferrs\u002Foverview","en\u002F09.errs\u002F1.overview",{"title":280,"path":281,"stem":282,"icon":188},"API","\u002Fen\u002Ferrs\u002Fapi","en\u002F09.errs\u002F2.api",{"title":284,"path":285,"stem":286,"icon":188},"Integration","\u002Fen\u002Ferrs\u002Fintegration","en\u002F09.errs\u002F3.integration",{"title":288,"icon":289,"path":290,"stem":291,"children":292,"page":44},"events","i-lucide-workflow","\u002Fen\u002Fevents","en\u002F10.events",[293,297,302],{"title":294,"path":295,"stem":296,"icon":146},"Events","\u002Fen\u002Fevents\u002Foverview","en\u002F10.events\u002F1.overview",{"title":298,"path":299,"stem":300,"icon":301},"Outbox","\u002Fen\u002Fevents\u002Foutbox","en\u002F10.events\u002F2.outbox","i-lucide-inbox",{"title":303,"path":304,"stem":305,"icon":306},"Scheduling","\u002Fen\u002Fevents\u002Fscheduler","en\u002F10.events\u002F3.scheduler","i-lucide-calendar-clock",{"title":308,"icon":309,"path":310,"stem":311,"children":312,"page":44},"httperr","i-lucide-globe","\u002Fen\u002Fhttperr","en\u002F11.httperr",[313,316,321],{"title":52,"path":314,"stem":315,"icon":309},"\u002Fen\u002Fhttperr\u002Foverview","en\u002F11.httperr\u002F1.overview",{"title":317,"path":318,"stem":319,"icon":320},"Error responses","\u002Fen\u002Fhttperr\u002Ferror-responses","en\u002F11.httperr\u002F2.error-responses","i-lucide-octagon-alert",{"title":322,"path":323,"stem":324,"icon":325},"Validation","\u002Fen\u002Fhttperr\u002Fvalidation","en\u002F11.httperr\u002F3.validation","i-lucide-badge-check",{"title":327,"icon":328,"path":329,"stem":330,"children":331,"page":44},"logx","i-lucide-scroll-text","\u002Fen\u002Flogx","en\u002F12.logx",[332,335],{"title":52,"path":333,"stem":334,"icon":328},"\u002Fen\u002Flogx\u002Foverview","en\u002F12.logx\u002F1.overview",{"title":336,"path":337,"stem":338,"icon":47},"Monolog formatter","\u002Fen\u002Flogx\u002Fmonolog","en\u002F12.logx\u002F2.monolog",{"title":340,"icon":341,"path":342,"stem":343,"children":344,"page":44},"mail","i-lucide-mail","\u002Fen\u002Fmail","en\u002F13.mail",[345,348,352],{"title":52,"path":346,"stem":347,"icon":341},"\u002Fen\u002Fmail\u002Foverview","en\u002F13.mail\u002F1.overview",{"title":349,"path":350,"stem":351,"icon":341},"SMTP","\u002Fen\u002Fmail\u002Fsmtp","en\u002F13.mail\u002F2.smtp",{"title":353,"path":354,"stem":355,"icon":341},"MJML","\u002Fen\u002Fmail\u002Fmjml","en\u002F13.mail\u002F3.mjml",{"title":357,"icon":358,"path":359,"stem":360,"children":361,"page":44},"notify","i-lucide-bell","\u002Fen\u002Fnotify","en\u002F14.notify",[362,365,369],{"title":52,"path":363,"stem":364,"icon":358},"\u002Fen\u002Fnotify\u002Foverview","en\u002F14.notify\u002F1.overview",{"title":366,"path":367,"stem":368,"icon":358},"Database channel","\u002Fen\u002Fnotify\u002Fdatabase","en\u002F14.notify\u002F2.database",{"title":370,"path":371,"stem":372,"icon":358},"Broadcast channel","\u002Fen\u002Fnotify\u002Fbroadcast","en\u002F14.notify\u002F3.broadcast",{"title":374,"icon":375,"path":376,"stem":377,"children":378,"page":44},"paginate","i-lucide-list-ordered","\u002Fen\u002Fpaginate","en\u002F15.paginate",[379,382],{"title":52,"path":380,"stem":381,"icon":375},"\u002Fen\u002Fpaginate\u002Foverview","en\u002F15.paginate\u002F1.overview",{"title":383,"path":384,"stem":385,"icon":39},"Cursor pagination","\u002Fen\u002Fpaginate\u002Fcursor","en\u002F15.paginate\u002F2.cursor",{"title":387,"icon":183,"path":388,"stem":389,"children":390,"page":44},"queue","\u002Fen\u002Fqueue","en\u002F16.queue",[391,394],{"title":52,"path":392,"stem":393,"icon":375},"\u002Fen\u002Fqueue\u002Foverview","en\u002F16.queue\u002F1.overview",{"title":395,"path":396,"stem":397,"icon":183},"Tasks","\u002Fen\u002Fqueue\u002Ftasks","en\u002F16.queue\u002F2.tasks",{"title":399,"icon":400,"path":401,"stem":402,"children":403,"page":44},"storage","i-lucide-hard-drive","\u002Fen\u002Fstorage","en\u002F17.storage",[404,407,411,415,420],{"title":52,"path":405,"stem":406,"icon":400},"\u002Fen\u002Fstorage\u002Foverview","en\u002F17.storage\u002F1.overview",{"title":408,"path":409,"stem":410,"icon":400},"Disk API","\u002Fen\u002Fstorage\u002Fdisk-api","en\u002F17.storage\u002F2.disk-api",{"title":412,"path":413,"stem":414,"icon":400},"Memory and local disk","\u002Fen\u002Fstorage\u002Flocal","en\u002F17.storage\u002F3.local",{"title":416,"path":417,"stem":418,"icon":419},"S3","\u002Fen\u002Fstorage\u002Fs3","en\u002F17.storage\u002F4.s3","i-lucide-cloud",{"title":421,"path":422,"stem":423,"icon":424},"Testing","\u002Fen\u002Fstorage\u002Ftesting","en\u002F17.storage\u002F5.testing","i-lucide-flask-conical",{"title":426,"icon":427,"path":428,"stem":429,"children":430,"page":44},"telemetry","i-lucide-activity","\u002Fen\u002Ftelemetry","en\u002F18.telemetry",[431,434,438,443],{"title":52,"path":432,"stem":433,"icon":427},"\u002Fen\u002Ftelemetry\u002Foverview","en\u002F18.telemetry\u002F1.overview",{"title":435,"path":436,"stem":437,"icon":328},"Logging","\u002Fen\u002Ftelemetry\u002Flogging","en\u002F18.telemetry\u002F2.logging",{"title":439,"path":440,"stem":441,"icon":442},"OpenTelemetry","\u002Fen\u002Ftelemetry\u002Fopentelemetry","en\u002F18.telemetry\u002F3.opentelemetry","i-lucide-radar",{"title":444,"path":445,"stem":446,"icon":447},"Sentry","\u002Fen\u002Ftelemetry\u002Fsentry","en\u002F18.telemetry\u002F4.sentry","i-lucide-bug",{"id":449,"title":134,"body":450,"description":4298,"extension":4299,"links":4300,"meta":4301,"navigation":4302,"path":135,"seo":4303,"stem":136,"__hash__":4304},"docs_en\u002Fen\u002F03.framework\u002F3.server.md",{"type":451,"value":452,"toc":4276},"minimark",[453,497,526,544,549,562,631,634,1005,1011,1016,1019,1048,1052,1115,1140,1143,1178,1182,1224,1228,1250,1297,1306,1312,1316,1439,1480,1494,1772,1804,1836,1849,1864,1868,1871,1915,1919,1925,2577,2608,2612,2869,2967,2971,2989,3341,3344,3427,3431,3549,3564,3589,3593,3608,3949,3995,4035,4038,4046,4200,4203,4207,4239,4243,4272],[454,455,456,460,461,468,469,472,473,476,477,481,482,484,485,488,489,492,493,496],"p",{},[457,458,459],"code",{},"server"," is the framework's HTTP chassis: a ",[462,463,467],"a",{"href":464,"rel":465},"https:\u002F\u002Fgithub.com\u002Fgo-chi\u002Fchi",[466],"nofollow","chi"," router over the standard ",[457,470,471],{},"net\u002Fhttp",", with the framework's error handling, validation and telemetry wired in, run under the ",[462,474,475],{"href":130},"shared lifecycle",". Its positioning is ",[478,479,480],"strong",{},"stdlib-first",": chi is not a framework, it is routing over ",[457,483,471],{},". Every middleware is a plain ",[457,486,487],{},"func(http.Handler) http.Handler",", every handler is ",[457,490,491],{},"http.HandlerFunc","-compatible, so any net\u002Fhttp tool in the Go ecosystem (middleware libraries, ",[457,494,495],{},"httptest",", profilers) works unmodified.",[498,499,504],"pre",{"className":500,"code":501,"language":502,"meta":503,"style":503},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import \"github.com\u002Fgp-system\u002Fframework\u002Fserver\"\n","go","",[457,505,506],{"__ignoreMap":503},[507,508,511,515,519,523],"span",{"class":509,"line":510},"line",1,[507,512,514],{"class":513},"s7zQu","import",[507,516,518],{"class":517},"sMK4o"," \"",[507,520,522],{"class":521},"sBMFI","github.com\u002Fgp-system\u002Fframework\u002Fserver",[507,524,525],{"class":517},"\"\n",[454,527,528,529,532,533,535,536,539,540,543],{},"It is the chassis every ",[462,530,531],{"href":82},"generated"," project sits on; application code never talks to ",[457,534,471],{}," directly, it calls ",[457,537,538],{},"server.Run"," once, in ",[457,541,542],{},"main",".",[545,546,548],"h2",{"id":547},"config","Config",[454,550,551,552,555,556,561],{},"In a generated project, ",[457,553,554],{},"server.Config"," is embedded without a prefix in the application config and loaded from environment variables by ",[462,557,558],{"href":265},[457,559,560],{},"envconf.MustLoad",":",[498,563,566],{"className":500,"code":564,"filename":565,"language":502,"meta":503,"style":503},"type Config struct {\n    Server server.Config\n    DB     dbx.Config `envPrefix:\"DB_\"`\n    \u002F\u002F ...\n}\n","internal\u002Fplatform\u002Fconfig\u002Fconfig.go",[457,567,568,582,596,618,625],{"__ignoreMap":503},[507,569,570,573,576,579],{"class":509,"line":510},[507,571,572],{"class":517},"type",[507,574,575],{"class":521}," Config",[507,577,578],{"class":517}," struct",[507,580,581],{"class":517}," {\n",[507,583,585,589,591,593],{"class":509,"line":584},2,[507,586,588],{"class":587},"sTEyZ","    Server ",[507,590,459],{"class":521},[507,592,543],{"class":517},[507,594,595],{"class":521},"Config\n",[507,597,599,602,604,606,608,611,615],{"class":509,"line":598},3,[507,600,601],{"class":587},"    DB     ",[507,603,234],{"class":521},[507,605,543],{"class":517},[507,607,548],{"class":521},[507,609,610],{"class":517}," `",[507,612,614],{"class":613},"sfazB","envPrefix:\"DB_\"",[507,616,617],{"class":517},"`\n",[507,619,621],{"class":509,"line":620},4,[507,622,624],{"class":623},"sHwdD","    \u002F\u002F ...\n",[507,626,628],{"class":509,"line":627},5,[507,629,630],{"class":517},"}\n",[454,632,633],{},"The fields and their environment variables:",[635,636,637,656],"table",{},[638,639,640],"thead",{},[641,642,643,647,650,653],"tr",{},[644,645,646],"th",{},"Field",[644,648,649],{},"Env",[644,651,652],{},"Default",[644,654,655],{},"What it controls",[657,658,659,680,707,727,747,768,788,808,828,848,868,902,921,943,964,983],"tbody",{},[641,660,661,667,672,677],{},[662,663,664],"td",{},[457,665,666],{},"ListenAddr",[662,668,669],{},[457,670,671],{},"LISTEN_ADDR",[662,673,674],{},[457,675,676],{},":3000",[662,678,679],{},"the address the server listens on",[641,681,682,687,692,697],{},[662,683,684],{},[457,685,686],{},"ShutdownTimeout",[662,688,689],{},[457,690,691],{},"SHUTDOWN_TIMEOUT",[662,693,694],{},[457,695,696],{},"10s",[662,698,699,700,703,704],{},"how long in-flight requests get after ",[457,701,702],{},"SIGINT","\u002F",[457,705,706],{},"SIGTERM",[641,708,709,714,719,724],{},[662,710,711],{},[457,712,713],{},"ReadHeaderTimeout",[662,715,716],{},[457,717,718],{},"READ_HEADER_TIMEOUT",[662,720,721],{},[457,722,723],{},"5s",[662,725,726],{},"how long a client may take to send request headers (Slowloris defense)",[641,728,729,734,739,744],{},[662,730,731],{},[457,732,733],{},"ReadTimeout",[662,735,736],{},[457,737,738],{},"READ_TIMEOUT",[662,740,741],{},[457,742,743],{},"30s",[662,745,746],{},"budget for reading the whole request (headers + body)",[641,748,749,754,759,765],{},[662,750,751],{},[457,752,753],{},"WriteTimeout",[662,755,756],{},[457,757,758],{},"WRITE_TIMEOUT",[662,760,761,764],{},[457,762,763],{},"0s"," (disabled)",[662,766,767],{},"budget for writing the response",[641,769,770,775,780,785],{},[662,771,772],{},[457,773,774],{},"IdleTimeout",[662,776,777],{},[457,778,779],{},"IDLE_TIMEOUT",[662,781,782],{},[457,783,784],{},"120s",[662,786,787],{},"how long an idle keep-alive connection stays open",[641,789,790,795,800,805],{},[662,791,792],{},[457,793,794],{},"CORSOrigins",[662,796,797],{},[457,798,799],{},"CORS_ORIGINS",[662,801,802],{},[457,803,804],{},"*",[662,806,807],{},"allowed origins, comma-separated",[641,809,810,815,820,825],{},[662,811,812],{},[457,813,814],{},"CORSMethods",[662,816,817],{},[457,818,819],{},"CORS_METHODS",[662,821,822],{},[457,823,824],{},"GET,POST,PUT,PATCH,DELETE,OPTIONS",[662,826,827],{},"the methods CORS preflights allow",[641,829,830,835,840,845],{},[662,831,832],{},[457,833,834],{},"CORSHeaders",[662,836,837],{},[457,838,839],{},"CORS_HEADERS",[662,841,842],{},[457,843,844],{},"Content-Type,Authorization",[662,846,847],{},"the request headers CORS preflights allow",[641,849,850,855,860,865],{},[662,851,852],{},[457,853,854],{},"CORSAllowCredentials",[662,856,857],{},[457,858,859],{},"CORS_ALLOW_CREDENTIALS",[662,861,862],{},[457,863,864],{},"false",[662,866,867],{},"cookies\u002Fcredentials on cross-origin requests (invalid together with wildcard origins)",[641,869,870,875,880,884],{},[662,871,872],{},[457,873,874],{},"TrustProxy",[662,876,877],{},[457,878,879],{},"HTTP_TRUST_PROXY",[662,881,882],{},[457,883,864],{},[662,885,886,887,890,891,894,895,703,898,901],{},"the ",[457,888,889],{},"realip"," middleware: ",[457,892,893],{},"RemoteAddr"," rewritten from ",[457,896,897],{},"X-Real-Ip",[457,899,900],{},"X-Forwarded-For","; enable only behind a proxy that overwrites those headers",[641,903,904,909,914,918],{},[662,905,906],{},[457,907,908],{},"AccessLog",[662,910,911],{},[457,912,913],{},"HTTP_ACCESS_LOG",[662,915,916],{},[457,917,864],{},[662,919,920],{},"the structured access log (one record per request); telemetry dev mode always logs regardless",[641,922,923,928,933,937],{},[662,924,925],{},[457,926,927],{},"RequestTimeout",[662,929,930],{},[457,931,932],{},"HTTP_REQUEST_TIMEOUT",[662,934,935,764],{},[457,936,763],{},[662,938,886,939,942],{},[457,940,941],{},"timeout"," middleware: a per-request context deadline; off by default because it also caps streaming responses",[641,944,945,950,955,961],{},[662,946,947],{},[457,948,949],{},"BodyLimit",[662,951,952],{},[457,953,954],{},"BODY_LIMIT",[662,956,957,960],{},[457,958,959],{},"4194304"," (4 MiB)",[662,962,963],{},"max request body size in bytes",[641,965,966,971,976,980],{},[662,967,968],{},[457,969,970],{},"ExposeInternalErrors",[662,972,973],{},[457,974,975],{},"HTTP_EXPOSE_INTERNAL_ERRORS",[662,977,978],{},[457,979,864],{},[662,981,982],{},"internal error details in 5xx responses (dev only!)",[641,984,985,990,996,999],{},[662,986,987],{},[457,988,989],{},"Telemetry",[662,991,992],{},[993,994,995],"em",{},"(embedded)",[662,997,998],{},"none",[662,1000,1001,1004],{},[457,1002,1003],{},"telemetry.Config"," (OTel, logger, Sentry)",[454,1006,1007,1008,543],{},"The full env reference (including the DB, worker and telemetry variables): ",[462,1009,1010],{"href":165},"Configuration reference",[1012,1013,1015],"h3",{"id":1014},"timeouts","Timeouts",[454,1017,1018],{},"The defaults are tuned for production traffic, not demos:",[1020,1021,1022,1030,1037],"ul",{},[1023,1024,1025,1029],"li",{},[478,1026,1027],{},[457,1028,713],{}," is the first line of Slowloris defense: a client that drips headers byte by byte gets disconnected after 5 seconds.",[1023,1031,1032,1036],{},[478,1033,1034],{},[457,1035,733],{}," covers reading the whole request.",[1023,1038,1039,1043,1044,1047],{},[478,1040,1041],{},[457,1042,753],{}," is deliberately ",[457,1045,1046],{},"0"," (disabled): it would also cut off slow or streaming responses (large downloads, SSE). Enable it when all your endpoints are short-lived.",[1012,1049,1051],{"id":1050},"body-limit-never-accidentally-disabled","Body limit: never accidentally disabled",[498,1053,1055],{"className":500,"code":1054,"language":502,"meta":503,"style":503},"const DefaultBodyLimit = 4 \u003C\u003C 20 \u002F\u002F 4 MiB\n\nfunc (c Config) EffectiveBodyLimit() int\n",[457,1056,1057,1081,1087],{"__ignoreMap":503},[507,1058,1059,1062,1065,1068,1072,1075,1078],{"class":509,"line":510},[507,1060,1061],{"class":517},"const",[507,1063,1064],{"class":587}," DefaultBodyLimit ",[507,1066,1067],{"class":517},"=",[507,1069,1071],{"class":1070},"sbssI"," 4",[507,1073,1074],{"class":517}," \u003C\u003C",[507,1076,1077],{"class":1070}," 20",[507,1079,1080],{"class":623}," \u002F\u002F 4 MiB\n",[507,1082,1083],{"class":509,"line":584},[507,1084,1086],{"emptyLinePlaceholder":1085},true,"\n",[507,1088,1089,1092,1095,1099,1101,1104,1108,1111],{"class":509,"line":598},[507,1090,1091],{"class":517},"func",[507,1093,1094],{"class":517}," (",[507,1096,1098],{"class":1097},"sHdIc","c ",[507,1100,548],{"class":521},[507,1102,1103],{"class":517},")",[507,1105,1107],{"class":1106},"s2Zo4"," EffectiveBodyLimit",[507,1109,1110],{"class":517},"()",[507,1112,1114],{"class":1113},"spNyl"," int\n",[454,1116,1117,1119,1120,1122,1123,1126,1127,1129,1130,1133,1134,1136,1137,543],{},[457,1118,459],{}," never reads the raw ",[457,1121,949],{}," field; it calls ",[457,1124,1125],{},"EffectiveBodyLimit()",": a ",[457,1128,1046],{}," or negative value does ",[478,1131,1132],{},"not"," mean \"unlimited\", it falls back to the 4 MiB default. A typo'd or empty ",[457,1135,954],{}," therefore cannot silently remove the cap. Exceeding the limit produces a 413 ",[462,1138,1139],{"href":318},"problem response",[1012,1141,975],{"id":1142},"http_expose_internal_errors",[454,1144,1145,1146,1149,1150,1153,1154,1158,1159,1162,1163,1166,1167,1170,1171,1174,1175,1177],{},"With ",[457,1147,1148],{},"true",", 5xx responses carry the underlying error message in ",[457,1151,1152],{},"detail",", and, when the chain holds an ",[462,1155,1156],{"href":277},[457,1157,272],{}," error, the response also gains ",[457,1160,1161],{},"stack"," and ",[457,1164,1165],{},"chain"," extension members. It is deliberately a ",[478,1168,1169],{},"separate"," switch from ",[457,1172,1173],{},"OTEL_DEV_MODE",": turning on dev logging can never accidentally start leaking internals to clients. Keep it ",[457,1176,864],{}," in production: details go to the log and Sentry there, not into the response.",[1012,1179,1181],{"id":1180},"the-embedded-telemetry-config","The embedded telemetry config",[454,1183,1184,1185,1187,1188,1190,1191,1194,1195,1198,1199,1198,1201,1198,1204,1207,1208,1210,1211,1214,1215,1220,1221,543],{},"The ",[457,1186,989],{}," field is a ",[457,1189,1003],{},": settings for the OTel bootstrap, the default ",[457,1192,1193],{},"slog"," logger and the optional Sentry integration (",[457,1196,1197],{},"OTEL_SERVICE_NAME",", ",[457,1200,1173],{},[457,1202,1203],{},"LOG_LEVEL",[457,1205,1206],{},"SENTRY_DSN",", ..., all standard names, no prefix). ",[457,1209,538],{}," passes it, alongside ",[457,1212,1213],{},"cfg.Log",", to ",[462,1216,1217],{"href":130},[457,1218,1219],{},"app.Telemetry"," before anything else starts. Details: ",[462,1222,1223],{"href":432},"Observability",[545,1225,1227],{"id":1226},"the-shared-lifecycle","The shared lifecycle",[454,1229,1230,1232,1233,1238,1239,1162,1244,1249],{},[457,1231,459],{}," does not implement graceful shutdown itself: it delegates to the engine-agnostic ",[462,1234,1235],{"href":130},[457,1236,1237],{},"app"," package, the same one ",[462,1240,1241],{"href":140},[457,1242,1243],{},"worker",[462,1245,1246],{"href":144},[457,1247,1248],{},"realtime"," sit on. The flow is identical in every gpsystem process:",[1251,1252,1253,1258,1265,1268,1283,1294],"ol",{},[1023,1254,1255,1257],{},[457,1256,1219],{},": OTel, logger, Sentry.",[1023,1259,1260,1261,1264],{},"Route registration (your ",[457,1262,1263],{},"register"," function).",[1023,1266,1267],{},"Listen: the process blocks from here.",[1023,1269,1270,1271,703,1273,1275,1276,1278,1279,1282],{},"On ",[457,1272,702],{},[457,1274,706],{},": the server stops accepting requests and drains in-flight ones within ",[457,1277,686],{},". A ",[478,1280,1281],{},"second"," signal kills the process immediately.",[1023,1284,1285,1286,1289,1290,1293],{},"Cleanups registered with ",[457,1287,1288],{},"WithCloser"," run in ",[478,1291,1292],{},"LIFO"," order (with their own grace period, even when the drain consumed the budget).",[1023,1295,1296],{},"Telemetry flushes last: spans emitted by closers still get exported.",[454,1298,1299,1300,1302,1303,1305],{},"The exact ordering and signal-handling details live on the ",[462,1301,129],{"href":130}," page; this page covers what ",[457,1304,538],{}," itself wires on top of that shared skeleton.",[1307,1308,1309,1311],"note",{},[457,1310,554],{}," carries the full web-server configuration: every field is loaded from an environment variable, typed, and graceful shutdown is the binary's job, not an external process manager's.",[545,1313,1315],{"id":1314},"what-serverrun-wires","What server.Run wires",[498,1317,1319],{"className":500,"code":1318,"language":502,"meta":503,"style":503},"func Run(ctx context.Context, cfg Config, register RegisterFunc, opts ...Option) error\nfunc New(cfg Config, opts ...Option) *chi.Mux \u002F\u002F for tests and special cases\n\ntype RegisterFunc func(r chi.Router) error\n",[457,1320,1321,1374,1411,1415],{"__ignoreMap":503},[507,1322,1323,1325,1328,1331,1334,1337,1339,1342,1345,1348,1350,1352,1355,1358,1360,1363,1366,1369,1371],{"class":509,"line":510},[507,1324,1091],{"class":517},[507,1326,1327],{"class":1106}," Run",[507,1329,1330],{"class":517},"(",[507,1332,1333],{"class":1097},"ctx",[507,1335,1336],{"class":521}," context",[507,1338,543],{"class":517},[507,1340,1341],{"class":521},"Context",[507,1343,1344],{"class":517},",",[507,1346,1347],{"class":1097}," cfg",[507,1349,575],{"class":521},[507,1351,1344],{"class":517},[507,1353,1354],{"class":1097}," register",[507,1356,1357],{"class":521}," RegisterFunc",[507,1359,1344],{"class":517},[507,1361,1362],{"class":1097}," opts",[507,1364,1365],{"class":517}," ...",[507,1367,1368],{"class":521},"Option",[507,1370,1103],{"class":517},[507,1372,1373],{"class":1113}," error\n",[507,1375,1376,1378,1381,1383,1386,1388,1390,1392,1394,1396,1398,1401,1403,1405,1408],{"class":509,"line":584},[507,1377,1091],{"class":517},[507,1379,1380],{"class":1106}," New",[507,1382,1330],{"class":517},[507,1384,1385],{"class":1097},"cfg",[507,1387,575],{"class":521},[507,1389,1344],{"class":517},[507,1391,1362],{"class":1097},[507,1393,1365],{"class":517},[507,1395,1368],{"class":521},[507,1397,1103],{"class":517},[507,1399,1400],{"class":517}," *",[507,1402,467],{"class":521},[507,1404,543],{"class":517},[507,1406,1407],{"class":521},"Mux",[507,1409,1410],{"class":623}," \u002F\u002F for tests and special cases\n",[507,1412,1413],{"class":509,"line":598},[507,1414,1086],{"emptyLinePlaceholder":1085},[507,1416,1417,1419,1421,1424,1427,1430,1432,1435,1437],{"class":509,"line":620},[507,1418,572],{"class":517},[507,1420,1357],{"class":521},[507,1422,1423],{"class":517}," func(",[507,1425,1426],{"class":1097},"r",[507,1428,1429],{"class":521}," chi",[507,1431,543],{"class":517},[507,1433,1434],{"class":521},"Router",[507,1436,1103],{"class":517},[507,1438,1373],{"class":1113},[454,1440,1441,1444,1445,1447,1448,1451,1452,1455,1456,1459,1460,703,1462,1464,1465,1468,1469,1472,1473,1475,1476,1479],{},[457,1442,1443],{},"Run"," blocks until exit: telemetry bootstrap (",[457,1446,1219],{},") → router construction → ",[457,1449,1450],{},"register(r)"," → ",[457,1453,1454],{},"http.Server"," + ",[457,1457,1458],{},"ListenAndServe"," → graceful shutdown on ",[457,1461,702],{},[457,1463,706],{},". It returns ",[457,1466,1467],{},"nil"," on clean shutdown. ",[457,1470,1471],{},"New"," returns the fully wired router without running it or bootstrapping telemetry: you pair it with ",[457,1474,495],{}," (see ",[462,1477,421],{"href":1478},"#testing",").",[454,1481,1482,1483,1094,1486,1489,1490,1493],{},"The middleware chain is a ",[478,1484,1485],{},"named, ordered stack",[457,1487,1488],{},"server.Stack","), pre-populated with the framework defaults. Each entry has a name; the ",[457,1491,1492],{},"server.MW*"," constants spell them, so a typo in a stack mutation is a compile error. The default order, outermost first:",[635,1495,1496,1509],{},[638,1497,1498],{},[641,1499,1500,1503,1506],{},[644,1501,1502],{},"Name",[644,1504,1505],{},"Present",[644,1507,1508],{},"What it does",[657,1510,1511,1534,1560,1582,1600,1626,1651,1709,1732,1755],{},[641,1512,1513,1521,1524],{},[662,1514,1515,1094,1518,1103],{},[457,1516,1517],{},"requestid",[457,1519,1520],{},"MWRequestID",[662,1522,1523],{},"always",[662,1525,1526,1527,1530,1531],{},"assigns every request an id: a well-formed inbound ",[457,1528,1529],{},"X-Request-Id"," is kept, otherwise a UUID is generated; echoed on the response header, readable via ",[457,1532,1533],{},"httpmw.RequestIDFromContext",[641,1535,1536,1543,1549],{},[662,1537,1538,1094,1540,1103],{},[457,1539,889],{},[457,1541,1542],{},"MWRealIP",[662,1544,1545,1546],{},"only with ",[457,1547,1548],{},"HTTP_TRUST_PROXY=true",[662,1550,1551,1552,1554,1555,703,1557,1559],{},"rewrites ",[457,1553,893],{}," from ",[457,1556,897],{},[457,1558,900],{}," (first hop), so logs and Sentry see the client address instead of the proxy's",[641,1561,1562,1570,1576],{},[662,1563,1564,1094,1567,1103],{},[457,1565,1566],{},"otel",[457,1568,1569],{},"MWOtel",[662,1571,1572,1573],{},"unless ",[457,1574,1575],{},"WithoutTelemetry",[662,1577,1578,1579,1581],{},"per-request server span + HTTP metrics (",[462,1580,439],{"href":440},"); the span wraps recovery and logging, so a recovered 500 is recorded on it and access-log records carry trace context",[641,1583,1584,1592,1594],{},[662,1585,1586,1094,1589,1103],{},[457,1587,1588],{},"sentryhub",[457,1590,1591],{},"MWSentryHub",[662,1593,1523],{},[662,1595,1596,1597,1599],{},"an isolated Sentry hub per request, so breadcrumbs never mix across requests; no-op when ",[462,1598,444],{"href":445}," is off",[641,1601,1602,1610,1612],{},[662,1603,1604,1094,1607,1103],{},[457,1605,1606],{},"recover",[457,1608,1609],{},"MWRecover",[662,1611,1523],{},[662,1613,1614,1615,1617,1618,1621,1622,1625],{},"a panic becomes an ",[457,1616,272],{}," error whose stack points at the ",[478,1619,1620],{},"panic site",", rendered as a 500 problem response (it re-raises ",[457,1623,1624],{},"http.ErrAbortHandler",", net\u002Fhttp's flow-control signal)",[641,1627,1628,1636,1641],{},[662,1629,1630,1094,1633,1103],{},[457,1631,1632],{},"validator",[457,1634,1635],{},"MWValidator",[662,1637,1545,1638],{},[457,1639,1640],{},"WithValidator",[662,1642,1643,1644,1646,1647,1650],{},"puts the validator given via ",[457,1645,1640],{}," into the request context, where ",[457,1648,1649],{},"StrictValidator(nil)"," finds it (see below)",[641,1652,1653,1661,1671],{},[662,1654,1655,1094,1658,1103],{},[457,1656,1657],{},"accesslog",[457,1659,1660],{},"MWAccessLog",[662,1662,1663,1664,1667,1668],{},"with ",[457,1665,1666],{},"HTTP_ACCESS_LOG=true"," or ",[457,1669,1670],{},"OTEL_DEV_MODE=true",[662,1672,1673,1674,1676,1677,1198,1680,1198,1683,1686,1687,1198,1690,1198,1693,1198,1696,1198,1699,1702,1703,1705,1706,1103],{},"one structured ",[457,1675,1193],{}," record per request: ",[457,1678,1679],{},"method",[457,1681,1682],{},"path",[457,1684,1685],{},"route"," (the chi route pattern), ",[457,1688,1689],{},"status",[457,1691,1692],{},"bytes",[457,1694,1695],{},"duration",[457,1697,1698],{},"remote",[457,1700,1701],{},"request_id","; 5xx at error level; sits inside ",[457,1704,1606],{},", so a panicking request still logs (with ",[457,1707,1708],{},"panic=true",[641,1710,1711,1719,1721],{},[662,1712,1713,1094,1716,1103],{},[457,1714,1715],{},"cors",[457,1717,1718],{},"MWCORS",[662,1720,1523],{},[662,1722,1723,1724,1198,1726,1198,1728,1162,1730],{},"from ",[457,1725,799],{},[457,1727,819],{},[457,1729,839],{},[457,1731,859],{},[641,1733,1734,1742,1744],{},[662,1735,1736,1094,1739,1103],{},[457,1737,1738],{},"bodylimit",[457,1740,1741],{},"MWBodyLimit",[662,1743,1523],{},[662,1745,1746,1747,1750,1751,1754],{},"wraps the body in ",[457,1748,1749],{},"http.MaxBytesReader"," with ",[457,1752,1753],{},"cfg.EffectiveBodyLimit()",": exceeding it yields a 413 problem",[641,1756,1757,1764,1769],{},[662,1758,1759,1094,1761,1103],{},[457,1760,941],{},[457,1762,1763],{},"MWTimeout",[662,1765,1545,1766,1768],{},[457,1767,932],{}," > 0",[662,1770,1771],{},"cancels the request context after the configured duration, nearest the handler",[454,1773,1774,1162,1777,1780,1781,1094,1783,1786,1787,1792,1793,1796,1797,703,1800,1803],{},[457,1775,1776],{},"WithMiddleware",[457,1778,1779],{},"WithStack"," extras apply on top of this. The name ",[457,1782,204],{},[457,1784,1785],{},"MWAuth",") is reserved for the identity middleware when a project mounts it on the whole stack; the framework installs no default entry under it (",[462,1788,1789],{"href":211},[457,1790,1791],{},"add auth"," wires identity at the ",[457,1794,1795],{},"api"," router instead). Below the stack, the router's ",[457,1798,1799],{},"NotFound",[457,1801,1802],{},"MethodNotAllowed"," handlers render 404\u002F405 as problem+json instead of chi's stock plain-text errors; mounted subrouters inherit them.",[454,1805,1806,1807,1810,1811,1198,1814,1198,1817,1198,1820,1198,1823,1198,1826,1198,1828,1831,1832,1835],{},"The built-in entries themselves are exported building blocks in the ",[457,1808,1809],{},"gpsystem\u002Fhttpmw"," package (",[457,1812,1813],{},"RequestID",[457,1815,1816],{},"RealIP",[457,1818,1819],{},"Otel",[457,1821,1822],{},"RequestHub",[457,1824,1825],{},"Recoverer",[457,1827,908],{},[457,1829,1830],{},"CORSAPI",", ...), shared with the ",[462,1833,1834],{"href":144},"realtime gateway",", so a custom stack can reuse or rewrap them.",[454,1837,1838,1839,1841,1842,1845,1846,1848],{},"Two behavior notes. Every response carries an ",[457,1840,1529],{}," header, so a support ticket, a log record and a trace can reference the same request. And the dev console request log is no longer chi's stock ",[457,1843,1844],{},"Logger"," but the structured access log above (telemetry dev mode always enables it): with the OTel ",[457,1847,1193],{}," bridge active its records are trace-correlated.",[454,1850,1184,1851,1853,1854,1094,1856,1198,1858,1198,1860,1198,1862,1479],{},[457,1852,554],{}," timeouts translate into the ",[457,1855,1454],{},[457,1857,713],{},[457,1859,733],{},[457,1861,753],{},[457,1863,774],{},[1012,1865,1867],{"id":1866},"error-rendering-nethttp-has-no-central-error-handler","Error rendering: net\u002Fhttp has no central error handler",[454,1869,1870],{},"Unlike frameworks with a built-in error hook, net\u002Fhttp gives a handler no way to \"return\" an error to the framework: a handler writes the response itself. The framework bridges this at three points so the on-the-wire behavior is uniform everywhere:",[1020,1872,1873,1895,1901],{},[1023,1874,886,1875,1878,1879,1094,1884,1198,1887,1890,1891,1894],{},[478,1876,1877],{},"generated server code's error hooks"," are wired to the ",[462,1880,1881,1883],{"href":318},[457,1882,308],{}," net\u002Fhttp writers",[457,1885,1886],{},"WriteError",[457,1888,1889],{},"WriteBadRequest","): an error returned from your handler goes through the ",[462,1892,1893],{"href":318},"full mapping",";",[1023,1896,886,1897,1900],{},[478,1898,1899],{},"router-level cases"," (panic, 404, 405, body limit) are rendered as problems per the table above;",[1023,1902,1903,1906,1907,1910,1911,1914],{},[478,1904,1905],{},"request validation"," does not run at bind time (the strict server decodes with ",[457,1908,1909],{},"json.Decoder",") but in the ",[457,1912,1913],{},"server.StrictValidator"," strict middleware (see below).",[545,1916,1918],{"id":1917},"the-shop-entry-point","The shop entry point",[454,1920,1184,1921,1924],{},[462,1922,1923],{"href":32},"sample app's"," HTTP binary, every line of which was written by the generator:",[498,1926,1929],{"className":500,"code":1927,"filename":1928,"language":502,"meta":503,"style":503},"package main\n\nimport (\n    \"context\"\n    \"log\"\n\n    \"github.com\u002Fgo-chi\u002Fchi\u002Fv5\"\n\n    \"github.com\u002Fgp-system\u002Fdbx\u002Fpg\"\n    \"github.com\u002Fgp-system\u002Fenvconf\"\n    \"github.com\u002Fgp-system\u002Fevents\u002Foutbox\"\n    \"github.com\u002Fgp-system\u002Fframework\u002Fserver\"\n\n    \"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop\"\n    \"github.com\u002Facme\u002Fshop\u002Finternal\u002Fplatform\u002Fconfig\"\n)\n\nfunc main() {\n    cfg := envconf.MustLoad[config.Config]()\n    ctx := context.Background()\n    pool := pg.MustNewPool(ctx, cfg.DB)\n\n    err := server.Run(ctx, cfg.Server, func(r chi.Router) error {\n        api := chi.NewRouter()\n        api.Use(server.IdentityMiddleware(cfg.JWT))\n        \u002F\u002F gpsystem:api-middleware\n        deps := shop.Dependencies{\n            DB:         pg.NewDB(pool),\n            Transactor: pg.NewTransactor(pool),\n            Dispatcher: outbox.NewDispatcher(outbox.NewStore(pg.NewDB(pool))),\n        }\n        shop.RegisterApi(api, deps)\n        shop.RegisterAdmin(api, deps)\n        r.Mount(\"\u002Fapi\u002Fv1\", api)\n        return nil\n    },\n        \u002F\u002F gpsystem:middleware\n        server.WithCloser(\"pgxpool\", func(context.Context) error {\n            pool.Close()\n            return nil\n        }),\n    )\n    if err != nil {\n        log.Fatal(err)\n    }\n}\n","cmd\u002Fshop\u002Fmain.go",[457,1930,1931,1939,1943,1950,1960,1969,1974,1984,1989,1999,2009,2019,2028,2033,2043,2053,2059,2064,2076,2105,2123,2154,2159,2205,2222,2254,2260,2279,2303,2324,2366,2372,2394,2414,2442,2451,2457,2463,2498,2511,2519,2525,2531,2548,2566,2572],{"__ignoreMap":503},[507,1932,1933,1936],{"class":509,"line":510},[507,1934,1935],{"class":517},"package",[507,1937,1938],{"class":521}," main\n",[507,1940,1941],{"class":509,"line":584},[507,1942,1086],{"emptyLinePlaceholder":1085},[507,1944,1945,1947],{"class":509,"line":598},[507,1946,514],{"class":513},[507,1948,1949],{"class":517}," (\n",[507,1951,1952,1955,1958],{"class":509,"line":620},[507,1953,1954],{"class":517},"    \"",[507,1956,1957],{"class":521},"context",[507,1959,525],{"class":517},[507,1961,1962,1964,1967],{"class":509,"line":627},[507,1963,1954],{"class":517},[507,1965,1966],{"class":521},"log",[507,1968,525],{"class":517},[507,1970,1972],{"class":509,"line":1971},6,[507,1973,1086],{"emptyLinePlaceholder":1085},[507,1975,1977,1979,1982],{"class":509,"line":1976},7,[507,1978,1954],{"class":517},[507,1980,1981],{"class":521},"github.com\u002Fgo-chi\u002Fchi\u002Fv5",[507,1983,525],{"class":517},[507,1985,1987],{"class":509,"line":1986},8,[507,1988,1086],{"emptyLinePlaceholder":1085},[507,1990,1992,1994,1997],{"class":509,"line":1991},9,[507,1993,1954],{"class":517},[507,1995,1996],{"class":521},"github.com\u002Fgp-system\u002Fdbx\u002Fpg",[507,1998,525],{"class":517},[507,2000,2002,2004,2007],{"class":509,"line":2001},10,[507,2003,1954],{"class":517},[507,2005,2006],{"class":521},"github.com\u002Fgp-system\u002Fenvconf",[507,2008,525],{"class":517},[507,2010,2012,2014,2017],{"class":509,"line":2011},11,[507,2013,1954],{"class":517},[507,2015,2016],{"class":521},"github.com\u002Fgp-system\u002Fevents\u002Foutbox",[507,2018,525],{"class":517},[507,2020,2022,2024,2026],{"class":509,"line":2021},12,[507,2023,1954],{"class":517},[507,2025,522],{"class":521},[507,2027,525],{"class":517},[507,2029,2031],{"class":509,"line":2030},13,[507,2032,1086],{"emptyLinePlaceholder":1085},[507,2034,2036,2038,2041],{"class":509,"line":2035},14,[507,2037,1954],{"class":517},[507,2039,2040],{"class":521},"github.com\u002Facme\u002Fshop\u002Finternal\u002Fmodules\u002Fshop",[507,2042,525],{"class":517},[507,2044,2046,2048,2051],{"class":509,"line":2045},15,[507,2047,1954],{"class":517},[507,2049,2050],{"class":521},"github.com\u002Facme\u002Fshop\u002Finternal\u002Fplatform\u002Fconfig",[507,2052,525],{"class":517},[507,2054,2056],{"class":509,"line":2055},16,[507,2057,2058],{"class":517},")\n",[507,2060,2062],{"class":509,"line":2061},17,[507,2063,1086],{"emptyLinePlaceholder":1085},[507,2065,2067,2069,2072,2074],{"class":509,"line":2066},18,[507,2068,1091],{"class":517},[507,2070,2071],{"class":1106}," main",[507,2073,1110],{"class":517},[507,2075,581],{"class":517},[507,2077,2079,2082,2085,2088,2090,2093,2096,2098,2100,2102],{"class":509,"line":2078},19,[507,2080,2081],{"class":587},"    cfg ",[507,2083,2084],{"class":517},":=",[507,2086,2087],{"class":587}," envconf",[507,2089,543],{"class":517},[507,2091,2092],{"class":1106},"MustLoad",[507,2094,2095],{"class":517},"[",[507,2097,547],{"class":521},[507,2099,543],{"class":517},[507,2101,548],{"class":521},[507,2103,2104],{"class":517},"]()\n",[507,2106,2108,2111,2113,2115,2117,2120],{"class":509,"line":2107},20,[507,2109,2110],{"class":587},"    ctx ",[507,2112,2084],{"class":517},[507,2114,1336],{"class":587},[507,2116,543],{"class":517},[507,2118,2119],{"class":1106},"Background",[507,2121,2122],{"class":517},"()\n",[507,2124,2126,2129,2131,2134,2136,2139,2141,2143,2145,2147,2149,2152],{"class":509,"line":2125},21,[507,2127,2128],{"class":587},"    pool ",[507,2130,2084],{"class":517},[507,2132,2133],{"class":587}," pg",[507,2135,543],{"class":517},[507,2137,2138],{"class":1106},"MustNewPool",[507,2140,1330],{"class":517},[507,2142,1333],{"class":587},[507,2144,1344],{"class":517},[507,2146,1347],{"class":587},[507,2148,543],{"class":517},[507,2150,2151],{"class":587},"DB",[507,2153,2058],{"class":517},[507,2155,2157],{"class":509,"line":2156},22,[507,2158,1086],{"emptyLinePlaceholder":1085},[507,2160,2162,2165,2167,2170,2172,2174,2176,2178,2180,2182,2184,2186,2188,2190,2192,2194,2196,2198,2200,2203],{"class":509,"line":2161},23,[507,2163,2164],{"class":587},"    err ",[507,2166,2084],{"class":517},[507,2168,2169],{"class":587}," server",[507,2171,543],{"class":517},[507,2173,1443],{"class":1106},[507,2175,1330],{"class":517},[507,2177,1333],{"class":587},[507,2179,1344],{"class":517},[507,2181,1347],{"class":587},[507,2183,543],{"class":517},[507,2185,134],{"class":587},[507,2187,1344],{"class":517},[507,2189,1423],{"class":517},[507,2191,1426],{"class":1097},[507,2193,1429],{"class":521},[507,2195,543],{"class":517},[507,2197,1434],{"class":521},[507,2199,1103],{"class":517},[507,2201,2202],{"class":1113}," error",[507,2204,581],{"class":517},[507,2206,2208,2211,2213,2215,2217,2220],{"class":509,"line":2207},24,[507,2209,2210],{"class":587},"        api ",[507,2212,2084],{"class":517},[507,2214,1429],{"class":587},[507,2216,543],{"class":517},[507,2218,2219],{"class":1106},"NewRouter",[507,2221,2122],{"class":517},[507,2223,2225,2228,2230,2233,2235,2237,2239,2242,2244,2246,2248,2251],{"class":509,"line":2224},25,[507,2226,2227],{"class":587},"        api",[507,2229,543],{"class":517},[507,2231,2232],{"class":1106},"Use",[507,2234,1330],{"class":517},[507,2236,459],{"class":587},[507,2238,543],{"class":517},[507,2240,2241],{"class":1106},"IdentityMiddleware",[507,2243,1330],{"class":517},[507,2245,1385],{"class":587},[507,2247,543],{"class":517},[507,2249,2250],{"class":587},"JWT",[507,2252,2253],{"class":517},"))\n",[507,2255,2257],{"class":509,"line":2256},26,[507,2258,2259],{"class":623},"        \u002F\u002F gpsystem:api-middleware\n",[507,2261,2263,2266,2268,2271,2273,2276],{"class":509,"line":2262},27,[507,2264,2265],{"class":587},"        deps ",[507,2267,2084],{"class":517},[507,2269,2270],{"class":521}," shop",[507,2272,543],{"class":517},[507,2274,2275],{"class":521},"Dependencies",[507,2277,2278],{"class":517},"{\n",[507,2280,2282,2285,2287,2290,2292,2295,2297,2300],{"class":509,"line":2281},28,[507,2283,2284],{"class":587},"            DB",[507,2286,561],{"class":517},[507,2288,2289],{"class":587},"         pg",[507,2291,543],{"class":517},[507,2293,2294],{"class":1106},"NewDB",[507,2296,1330],{"class":517},[507,2298,2299],{"class":587},"pool",[507,2301,2302],{"class":517},"),\n",[507,2304,2306,2309,2311,2313,2315,2318,2320,2322],{"class":509,"line":2305},29,[507,2307,2308],{"class":587},"            Transactor",[507,2310,561],{"class":517},[507,2312,2133],{"class":587},[507,2314,543],{"class":517},[507,2316,2317],{"class":1106},"NewTransactor",[507,2319,1330],{"class":517},[507,2321,2299],{"class":587},[507,2323,2302],{"class":517},[507,2325,2327,2330,2332,2335,2337,2340,2342,2345,2347,2350,2352,2355,2357,2359,2361,2363],{"class":509,"line":2326},30,[507,2328,2329],{"class":587},"            Dispatcher",[507,2331,561],{"class":517},[507,2333,2334],{"class":587}," outbox",[507,2336,543],{"class":517},[507,2338,2339],{"class":1106},"NewDispatcher",[507,2341,1330],{"class":517},[507,2343,2344],{"class":587},"outbox",[507,2346,543],{"class":517},[507,2348,2349],{"class":1106},"NewStore",[507,2351,1330],{"class":517},[507,2353,2354],{"class":587},"pg",[507,2356,543],{"class":517},[507,2358,2294],{"class":1106},[507,2360,1330],{"class":517},[507,2362,2299],{"class":587},[507,2364,2365],{"class":517},"))),\n",[507,2367,2369],{"class":509,"line":2368},31,[507,2370,2371],{"class":517},"        }\n",[507,2373,2375,2378,2380,2383,2385,2387,2389,2392],{"class":509,"line":2374},32,[507,2376,2377],{"class":587},"        shop",[507,2379,543],{"class":517},[507,2381,2382],{"class":1106},"RegisterApi",[507,2384,1330],{"class":517},[507,2386,1795],{"class":587},[507,2388,1344],{"class":517},[507,2390,2391],{"class":587}," deps",[507,2393,2058],{"class":517},[507,2395,2397,2399,2401,2404,2406,2408,2410,2412],{"class":509,"line":2396},33,[507,2398,2377],{"class":587},[507,2400,543],{"class":517},[507,2402,2403],{"class":1106},"RegisterAdmin",[507,2405,1330],{"class":517},[507,2407,1795],{"class":587},[507,2409,1344],{"class":517},[507,2411,2391],{"class":587},[507,2413,2058],{"class":517},[507,2415,2417,2420,2422,2425,2427,2430,2433,2435,2437,2440],{"class":509,"line":2416},34,[507,2418,2419],{"class":587},"        r",[507,2421,543],{"class":517},[507,2423,2424],{"class":1106},"Mount",[507,2426,1330],{"class":517},[507,2428,2429],{"class":517},"\"",[507,2431,2432],{"class":613},"\u002Fapi\u002Fv1",[507,2434,2429],{"class":517},[507,2436,1344],{"class":517},[507,2438,2439],{"class":587}," api",[507,2441,2058],{"class":517},[507,2443,2445,2448],{"class":509,"line":2444},35,[507,2446,2447],{"class":513},"        return",[507,2449,2450],{"class":517}," nil\n",[507,2452,2454],{"class":509,"line":2453},36,[507,2455,2456],{"class":517},"    },\n",[507,2458,2460],{"class":509,"line":2459},37,[507,2461,2462],{"class":623},"        \u002F\u002F gpsystem:middleware\n",[507,2464,2466,2469,2471,2473,2475,2477,2480,2482,2484,2486,2488,2490,2492,2494,2496],{"class":509,"line":2465},38,[507,2467,2468],{"class":587},"        server",[507,2470,543],{"class":517},[507,2472,1288],{"class":1106},[507,2474,1330],{"class":517},[507,2476,2429],{"class":517},[507,2478,2479],{"class":613},"pgxpool",[507,2481,2429],{"class":517},[507,2483,1344],{"class":517},[507,2485,1423],{"class":517},[507,2487,1957],{"class":521},[507,2489,543],{"class":517},[507,2491,1341],{"class":521},[507,2493,1103],{"class":517},[507,2495,2202],{"class":1113},[507,2497,581],{"class":517},[507,2499,2501,2504,2506,2509],{"class":509,"line":2500},39,[507,2502,2503],{"class":587},"            pool",[507,2505,543],{"class":517},[507,2507,2508],{"class":1106},"Close",[507,2510,2122],{"class":517},[507,2512,2514,2517],{"class":509,"line":2513},40,[507,2515,2516],{"class":513},"            return",[507,2518,2450],{"class":517},[507,2520,2522],{"class":509,"line":2521},41,[507,2523,2524],{"class":517},"        }),\n",[507,2526,2528],{"class":509,"line":2527},42,[507,2529,2530],{"class":517},"    )\n",[507,2532,2534,2537,2540,2543,2546],{"class":509,"line":2533},43,[507,2535,2536],{"class":513},"    if",[507,2538,2539],{"class":587}," err ",[507,2541,2542],{"class":517},"!=",[507,2544,2545],{"class":517}," nil",[507,2547,581],{"class":517},[507,2549,2551,2554,2556,2559,2561,2564],{"class":509,"line":2550},44,[507,2552,2553],{"class":587},"        log",[507,2555,543],{"class":517},[507,2557,2558],{"class":1106},"Fatal",[507,2560,1330],{"class":517},[507,2562,2563],{"class":587},"err",[507,2565,2058],{"class":517},[507,2567,2569],{"class":509,"line":2568},45,[507,2570,2571],{"class":517},"    }\n",[507,2573,2575],{"class":509,"line":2574},46,[507,2576,630],{"class":517},[454,2578,2579,2580,2582,2583,2585,2586,2589,2590,2594,2595,2598,2599,2602,2603,703,2605,2607],{},"Prefixing happens with ",[457,2581,2424],{},": each module's surfaces register on a fresh subrouter, which gets mounted under ",[457,2584,2432],{},". The two anchor comments are middleware seams: the ",[457,2587,2588],{},"api.Use(server.IdentityMiddleware(cfg.JWT))"," line above them is what ",[462,2591,2592],{"href":211},[457,2593,1791],{}," inserted at ",[457,2596,2597],{},"gpsystem:api-middleware",", and ",[457,2600,2601],{},"gpsystem:middleware"," marks where ",[457,2604,1779],{},[457,2606,1776],{}," options go in the option list.",[1012,2609,2611],{"id":2610},"options","Options",[498,2613,2615],{"className":500,"code":2614,"language":502,"meta":503,"style":503},"server.WithCloser(\"pgxpool\", func(ctx context.Context) error { pool.Close(); return nil })\nserver.WithMiddleware(myRateLimiter)                       \u002F\u002F ...func(http.Handler) http.Handler\nserver.WithStack(func(s *server.Stack) {                   \u002F\u002F surgery on the named chain\n    s.InsertBefore(server.MWCORS, \"tenant\", tenantMW)\n    s.Remove(server.MWSentryHub)\n})\nserver.WithHTTPServer(func(s *http.Server) { s.MaxHeaderBytes = 1 \u003C\u003C 16 })\nserver.WithValidator(validate.New(validate.WithRegister(registerCustomTags)))\nserver.WithoutTelemetry()                                  \u002F\u002F tests \u002F external OTel setup\n",[457,2616,2617,2670,2688,2718,2752,2771,2776,2822,2856],{"__ignoreMap":503},[507,2618,2619,2621,2623,2625,2627,2629,2631,2633,2635,2637,2639,2641,2643,2645,2647,2649,2652,2655,2657,2659,2662,2665,2667],{"class":509,"line":510},[507,2620,459],{"class":587},[507,2622,543],{"class":517},[507,2624,1288],{"class":1106},[507,2626,1330],{"class":517},[507,2628,2429],{"class":517},[507,2630,2479],{"class":613},[507,2632,2429],{"class":517},[507,2634,1344],{"class":517},[507,2636,1423],{"class":517},[507,2638,1333],{"class":1097},[507,2640,1336],{"class":521},[507,2642,543],{"class":517},[507,2644,1341],{"class":521},[507,2646,1103],{"class":517},[507,2648,2202],{"class":1113},[507,2650,2651],{"class":517}," {",[507,2653,2654],{"class":587}," pool",[507,2656,543],{"class":517},[507,2658,2508],{"class":1106},[507,2660,2661],{"class":517},"();",[507,2663,2664],{"class":513}," return",[507,2666,2545],{"class":517},[507,2668,2669],{"class":517}," })\n",[507,2671,2672,2674,2676,2678,2680,2683,2685],{"class":509,"line":584},[507,2673,459],{"class":587},[507,2675,543],{"class":517},[507,2677,1776],{"class":1106},[507,2679,1330],{"class":517},[507,2681,2682],{"class":587},"myRateLimiter",[507,2684,1103],{"class":517},[507,2686,2687],{"class":623},"                       \u002F\u002F ...func(http.Handler) http.Handler\n",[507,2689,2690,2692,2694,2696,2699,2702,2704,2706,2708,2711,2713,2715],{"class":509,"line":598},[507,2691,459],{"class":587},[507,2693,543],{"class":517},[507,2695,1779],{"class":1106},[507,2697,2698],{"class":517},"(func(",[507,2700,2701],{"class":1097},"s",[507,2703,1400],{"class":517},[507,2705,459],{"class":521},[507,2707,543],{"class":517},[507,2709,2710],{"class":521},"Stack",[507,2712,1103],{"class":517},[507,2714,2651],{"class":517},[507,2716,2717],{"class":623},"                   \u002F\u002F surgery on the named chain\n",[507,2719,2720,2723,2725,2728,2730,2732,2734,2736,2738,2740,2743,2745,2747,2750],{"class":509,"line":620},[507,2721,2722],{"class":587},"    s",[507,2724,543],{"class":517},[507,2726,2727],{"class":1106},"InsertBefore",[507,2729,1330],{"class":517},[507,2731,459],{"class":587},[507,2733,543],{"class":517},[507,2735,1718],{"class":587},[507,2737,1344],{"class":517},[507,2739,518],{"class":517},[507,2741,2742],{"class":613},"tenant",[507,2744,2429],{"class":517},[507,2746,1344],{"class":517},[507,2748,2749],{"class":587}," tenantMW",[507,2751,2058],{"class":517},[507,2753,2754,2756,2758,2761,2763,2765,2767,2769],{"class":509,"line":627},[507,2755,2722],{"class":587},[507,2757,543],{"class":517},[507,2759,2760],{"class":1106},"Remove",[507,2762,1330],{"class":517},[507,2764,459],{"class":587},[507,2766,543],{"class":517},[507,2768,1591],{"class":587},[507,2770,2058],{"class":517},[507,2772,2773],{"class":509,"line":1971},[507,2774,2775],{"class":517},"})\n",[507,2777,2778,2780,2782,2785,2787,2789,2791,2794,2796,2798,2800,2802,2805,2807,2810,2812,2815,2817,2820],{"class":509,"line":1976},[507,2779,459],{"class":587},[507,2781,543],{"class":517},[507,2783,2784],{"class":1106},"WithHTTPServer",[507,2786,2698],{"class":517},[507,2788,2701],{"class":1097},[507,2790,1400],{"class":517},[507,2792,2793],{"class":521},"http",[507,2795,543],{"class":517},[507,2797,134],{"class":521},[507,2799,1103],{"class":517},[507,2801,2651],{"class":517},[507,2803,2804],{"class":587}," s",[507,2806,543],{"class":517},[507,2808,2809],{"class":587},"MaxHeaderBytes ",[507,2811,1067],{"class":517},[507,2813,2814],{"class":1070}," 1",[507,2816,1074],{"class":517},[507,2818,2819],{"class":1070}," 16",[507,2821,2669],{"class":517},[507,2823,2824,2826,2828,2830,2832,2835,2837,2839,2841,2843,2845,2848,2850,2853],{"class":509,"line":1986},[507,2825,459],{"class":587},[507,2827,543],{"class":517},[507,2829,1640],{"class":1106},[507,2831,1330],{"class":517},[507,2833,2834],{"class":587},"validate",[507,2836,543],{"class":517},[507,2838,1471],{"class":1106},[507,2840,1330],{"class":517},[507,2842,2834],{"class":587},[507,2844,543],{"class":517},[507,2846,2847],{"class":1106},"WithRegister",[507,2849,1330],{"class":517},[507,2851,2852],{"class":587},"registerCustomTags",[507,2854,2855],{"class":517},")))\n",[507,2857,2858,2860,2862,2864,2866],{"class":509,"line":1991},[507,2859,459],{"class":587},[507,2861,543],{"class":517},[507,2863,1575],{"class":1106},[507,2865,1110],{"class":517},[507,2867,2868],{"class":623},"                                  \u002F\u002F tests \u002F external OTel setup\n",[1020,2870,2871,2882,2894,2925,2940,2956],{},[1023,2872,2873,2878,2879,2881],{},[478,2874,2875],{},[457,2876,2877],{},"WithCloser(name, fn)",": cleanup during graceful shutdown, after the server has stopped accepting requests. Closers run in ",[478,2880,1292],{}," order; telemetry flushes last, so a span emitted in a closer still gets exported.",[1023,2883,2884,2889,2890,2893],{},[478,2885,2886],{},[457,2887,2888],{},"WithMiddleware(...func(http.Handler) http.Handler)",": router-level middleware appended after the framework defaults. The standard net\u002Fhttp middleware shape: any ecosystem middleware fits here. Anything needed by a single surface only should go on that surface's ",[457,2891,2892],{},"Route"," instead (see below).",[1023,2895,2896,2901,2902,2904,2905,2908,2909,2912,2913,2916,2917,2920,2921,2924],{},[478,2897,2898],{},[457,2899,2900],{},"WithStack(func(*server.Stack))",": everything ",[457,2903,1776],{}," cannot do: insert before or after a named default (",[457,2906,2907],{},"s.InsertBefore(server.MWCORS, \"tenant\", tenantMW)","), swap an implementation (",[457,2910,2911],{},"Replace","), or drop one (",[457,2914,2915],{},"s.Remove(server.MWSentryHub)","; removing an absent name is a no-op, so conditional defaults can be removed unconditionally). Mutations ",[478,2918,2919],{},"panic"," on an unknown target or a duplicate name: a wiring bug fails at boot, not at request time. ",[457,2922,2923],{},"Names()"," returns the chain order for tests.",[1023,2926,2927,2932,2933,2935,2936,2939],{},[478,2928,2929],{},[457,2930,2931],{},"WithHTTPServer(func(*http.Server))",": escape hatch for ",[457,2934,1454],{}," fields the framework does not expose (TLS, ",[457,2937,2938],{},"MaxHeaderBytes",", ...). You may also override the framework-set fields: your mutation runs last.",[1023,2941,2942,2947,2948,2952,2953,2955],{},[478,2943,2944],{},[457,2945,2946],{},"WithValidator(*validate.Validator)",": the application-wide request validator, typically one carrying ",[462,2949,2951],{"href":2950},"\u002Fen\u002Fhttperr\u002Fvalidation#custom-tags","custom validation tags","; ",[457,2954,1649],{}," middlewares resolve it at request time.",[1023,2957,2958,2963,2964,2966],{},[478,2959,2960],{},[457,2961,2962],{},"WithoutTelemetry()",": skips ",[457,2965,1219],{}," and the OTel middleware; for tests, or when the process configures telemetry itself.",[545,2968,2970],{"id":2969},"registerfunc-and-strictvalidator","RegisterFunc and StrictValidator",[454,2972,2973,2977,2978,2981,2982,2985,2986,2988],{},[462,2974,2975],{"href":92},[457,2976,91],{}," splices one ",[457,2979,2980],{},"Register\u003CSurface>"," function per surface into the module's ",[457,2983,2984],{},"register.go",". For the shop's ",[457,2987,1795],{}," surface this is generated verbatim:",[498,2990,2993],{"className":500,"code":2991,"filename":2992,"language":502,"meta":503,"style":503},"func RegisterApi(router chi.Router, deps Dependencies) {\n    apiSvc := apiservice.New()\n    apiHandler := apihttp.New(apiSvc)\n    apiPolicies := apipolicy.New()\n    router.Route(\"\u002Fshop\", func(r chi.Router) {\n        \u002F\u002F gpsystem:surface-middleware\n        shopapigen.HandlerWithOptions(shopapigen.NewStrictHandlerWithOptions(\n            apiHandler,\n            []shopapigen.StrictMiddlewareFunc{\n                server.StrictValidator[shopapigen.StrictHandlerFunc](nil),\n                \u002F\u002F Last = outermost: authorization runs before validation.\n                kitpolicy.Enforcer[shopapigen.StrictHandlerFunc](apiPolicies, shopapigen.PermissionByOperation, shopapigen.PolicyByOperation),\n            },\n            shopapigen.StrictHTTPServerOptions{\n                RequestErrorHandlerFunc:  httperr.WriteBadRequest,\n                ResponseErrorHandlerFunc: server.WriteError,\n            },\n        ), shopapigen.ChiServerOptions{\n            BaseRouter:       r,\n            ErrorHandlerFunc: httperr.WriteBadRequest,\n        })\n    })\n}\n","internal\u002Fmodules\u002Fshop\u002Fregister.go",[457,2994,2995,3024,3040,3061,3077,3111,3116,3139,3147,3161,3183,3188,3233,3238,3250,3266,3281,3285,3299,3311,3327,3332,3337],{"__ignoreMap":503},[507,2996,2997,2999,3002,3004,3007,3009,3011,3013,3015,3017,3020,3022],{"class":509,"line":510},[507,2998,1091],{"class":517},[507,3000,3001],{"class":1106}," RegisterApi",[507,3003,1330],{"class":517},[507,3005,3006],{"class":1097},"router",[507,3008,1429],{"class":521},[507,3010,543],{"class":517},[507,3012,1434],{"class":521},[507,3014,1344],{"class":517},[507,3016,2391],{"class":1097},[507,3018,3019],{"class":521}," Dependencies",[507,3021,1103],{"class":517},[507,3023,581],{"class":517},[507,3025,3026,3029,3031,3034,3036,3038],{"class":509,"line":584},[507,3027,3028],{"class":587},"    apiSvc ",[507,3030,2084],{"class":517},[507,3032,3033],{"class":587}," apiservice",[507,3035,543],{"class":517},[507,3037,1471],{"class":1106},[507,3039,2122],{"class":517},[507,3041,3042,3045,3047,3050,3052,3054,3056,3059],{"class":509,"line":598},[507,3043,3044],{"class":587},"    apiHandler ",[507,3046,2084],{"class":517},[507,3048,3049],{"class":587}," apihttp",[507,3051,543],{"class":517},[507,3053,1471],{"class":1106},[507,3055,1330],{"class":517},[507,3057,3058],{"class":587},"apiSvc",[507,3060,2058],{"class":517},[507,3062,3063,3066,3068,3071,3073,3075],{"class":509,"line":620},[507,3064,3065],{"class":587},"    apiPolicies ",[507,3067,2084],{"class":517},[507,3069,3070],{"class":587}," apipolicy",[507,3072,543],{"class":517},[507,3074,1471],{"class":1106},[507,3076,2122],{"class":517},[507,3078,3079,3082,3084,3086,3088,3090,3093,3095,3097,3099,3101,3103,3105,3107,3109],{"class":509,"line":627},[507,3080,3081],{"class":587},"    router",[507,3083,543],{"class":517},[507,3085,2892],{"class":1106},[507,3087,1330],{"class":517},[507,3089,2429],{"class":517},[507,3091,3092],{"class":613},"\u002Fshop",[507,3094,2429],{"class":517},[507,3096,1344],{"class":517},[507,3098,1423],{"class":517},[507,3100,1426],{"class":1097},[507,3102,1429],{"class":521},[507,3104,543],{"class":517},[507,3106,1434],{"class":521},[507,3108,1103],{"class":517},[507,3110,581],{"class":517},[507,3112,3113],{"class":509,"line":1971},[507,3114,3115],{"class":623},"        \u002F\u002F gpsystem:surface-middleware\n",[507,3117,3118,3121,3123,3126,3128,3131,3133,3136],{"class":509,"line":1976},[507,3119,3120],{"class":587},"        shopapigen",[507,3122,543],{"class":517},[507,3124,3125],{"class":1106},"HandlerWithOptions",[507,3127,1330],{"class":517},[507,3129,3130],{"class":587},"shopapigen",[507,3132,543],{"class":517},[507,3134,3135],{"class":1106},"NewStrictHandlerWithOptions",[507,3137,3138],{"class":517},"(\n",[507,3140,3141,3144],{"class":509,"line":1986},[507,3142,3143],{"class":587},"            apiHandler",[507,3145,3146],{"class":517},",\n",[507,3148,3149,3152,3154,3156,3159],{"class":509,"line":1991},[507,3150,3151],{"class":517},"            []",[507,3153,3130],{"class":521},[507,3155,543],{"class":517},[507,3157,3158],{"class":521},"StrictMiddlewareFunc",[507,3160,2278],{"class":517},[507,3162,3163,3166,3168,3171,3173,3175,3177,3180],{"class":509,"line":2001},[507,3164,3165],{"class":587},"                server",[507,3167,543],{"class":517},[507,3169,3170],{"class":1106},"StrictValidator",[507,3172,2095],{"class":517},[507,3174,3130],{"class":521},[507,3176,543],{"class":517},[507,3178,3179],{"class":521},"StrictHandlerFunc",[507,3181,3182],{"class":517},"](nil),\n",[507,3184,3185],{"class":509,"line":2011},[507,3186,3187],{"class":623},"                \u002F\u002F Last = outermost: authorization runs before validation.\n",[507,3189,3190,3193,3195,3198,3200,3202,3204,3206,3209,3212,3214,3217,3219,3222,3224,3226,3228,3231],{"class":509,"line":2021},[507,3191,3192],{"class":587},"                kitpolicy",[507,3194,543],{"class":517},[507,3196,3197],{"class":1106},"Enforcer",[507,3199,2095],{"class":517},[507,3201,3130],{"class":521},[507,3203,543],{"class":517},[507,3205,3179],{"class":521},[507,3207,3208],{"class":517},"](",[507,3210,3211],{"class":587},"apiPolicies",[507,3213,1344],{"class":517},[507,3215,3216],{"class":587}," shopapigen",[507,3218,543],{"class":517},[507,3220,3221],{"class":587},"PermissionByOperation",[507,3223,1344],{"class":517},[507,3225,3216],{"class":587},[507,3227,543],{"class":517},[507,3229,3230],{"class":587},"PolicyByOperation",[507,3232,2302],{"class":517},[507,3234,3235],{"class":509,"line":2030},[507,3236,3237],{"class":517},"            },\n",[507,3239,3240,3243,3245,3248],{"class":509,"line":2035},[507,3241,3242],{"class":521},"            shopapigen",[507,3244,543],{"class":517},[507,3246,3247],{"class":521},"StrictHTTPServerOptions",[507,3249,2278],{"class":517},[507,3251,3252,3255,3257,3260,3262,3264],{"class":509,"line":2045},[507,3253,3254],{"class":587},"                RequestErrorHandlerFunc",[507,3256,561],{"class":517},[507,3258,3259],{"class":587},"  httperr",[507,3261,543],{"class":517},[507,3263,1889],{"class":587},[507,3265,3146],{"class":517},[507,3267,3268,3271,3273,3275,3277,3279],{"class":509,"line":2055},[507,3269,3270],{"class":587},"                ResponseErrorHandlerFunc",[507,3272,561],{"class":517},[507,3274,2169],{"class":587},[507,3276,543],{"class":517},[507,3278,1886],{"class":587},[507,3280,3146],{"class":517},[507,3282,3283],{"class":509,"line":2061},[507,3284,3237],{"class":517},[507,3286,3287,3290,3292,3294,3297],{"class":509,"line":2066},[507,3288,3289],{"class":517},"        ),",[507,3291,3216],{"class":521},[507,3293,543],{"class":517},[507,3295,3296],{"class":521},"ChiServerOptions",[507,3298,2278],{"class":517},[507,3300,3301,3304,3306,3309],{"class":509,"line":2078},[507,3302,3303],{"class":587},"            BaseRouter",[507,3305,561],{"class":517},[507,3307,3308],{"class":587},"       r",[507,3310,3146],{"class":517},[507,3312,3313,3316,3318,3321,3323,3325],{"class":509,"line":2107},[507,3314,3315],{"class":587},"            ErrorHandlerFunc",[507,3317,561],{"class":517},[507,3319,3320],{"class":587}," httperr",[507,3322,543],{"class":517},[507,3324,1889],{"class":587},[507,3326,3146],{"class":517},[507,3328,3329],{"class":509,"line":2125},[507,3330,3331],{"class":517},"        })\n",[507,3333,3334],{"class":509,"line":2156},[507,3335,3336],{"class":517},"    })\n",[507,3338,3339],{"class":509,"line":2161},[507,3340,630],{"class":517},[454,3342,3343],{},"Line by line:",[1251,3345,3346,3352,3370,3413],{},[1023,3347,3348,3351],{},[478,3349,3350],{},"Service → handler → policy registry",": an explicit construction, no container involved.",[1023,3353,3354,3359,3360,3362,3363,3366,3367,1479],{},[478,3355,3356],{},[457,3357,3358],{},"router.Route(\"\u002Fshop\", ...)",": the ",[457,3361,1795],{}," surface sits at the module root (",[457,3364,3365],{},"\u002Fapi\u002Fv1\u002Fshop\u002F...","); every other surface under its own prefix (",[457,3368,3369],{},"\u002Fadmin\u002Fshop",[1023,3371,3372,3359,3376,3378,3379,1198,3382,3385,3386,3389,3390,3393,3394,3397,3398,703,3400,703,3403,3406,3407,3410,3411,543],{},[478,3373,3374],{},[457,3375,3135],{},[462,3377,531],{"href":195}," strict server, plus the error hooks: request-decoding failures (",[457,3380,3381],{},"RequestErrorHandlerFunc",[457,3383,3384],{},"ErrorHandlerFunc",") render as 400 via ",[457,3387,3388],{},"httperr.WriteBadRequest",", your handler's errors (",[457,3391,3392],{},"ResponseErrorHandlerFunc",") via ",[457,3395,3396],{},"server.WriteError",", which maps the ",[457,3399,204],{},[457,3401,3402],{},"rbac",[457,3404,3405],{},"policy"," sentinels onto Problems and delegates everything else to ",[457,3408,3409],{},"httperr.WriteError",", through the ",[462,3412,1893],{"href":318},[1023,3414,3415,3422,3423,3426],{},[478,3416,3417,1455,3419],{},[457,3418,1913],{},[457,3420,3421],{},"kitpolicy.Enforcer",": validation and ",[462,3424,3425],{"href":221},"policy enforcement"," as strict middleware; the last element of the slice wraps outermost, so the enforcer runs before the validator.",[1012,3428,3430],{"id":3429},"strictvalidator-validation-before-the-handler","StrictValidator: validation before the handler",[498,3432,3434],{"className":500,"code":3433,"language":502,"meta":503,"style":503},"func StrictValidator[H ~func(ctx context.Context, w http.ResponseWriter, r *http.Request, request any) (any, error)](\n    v *validate.Validator,\n) func(f H, operationID string) H\n",[457,3435,3436,3508,3524],{"__ignoreMap":503},[507,3437,3438,3440,3443,3445,3448,3451,3453,3455,3457,3459,3461,3464,3467,3469,3472,3474,3477,3479,3481,3483,3486,3488,3491,3494,3496,3498,3501,3503,3505],{"class":509,"line":510},[507,3439,1091],{"class":517},[507,3441,3442],{"class":1106}," StrictValidator",[507,3444,2095],{"class":517},[507,3446,3447],{"class":1097},"H",[507,3449,3450],{"class":517}," ~func(",[507,3452,1333],{"class":1097},[507,3454,1336],{"class":521},[507,3456,543],{"class":517},[507,3458,1341],{"class":521},[507,3460,1344],{"class":517},[507,3462,3463],{"class":1097}," w",[507,3465,3466],{"class":521}," http",[507,3468,543],{"class":517},[507,3470,3471],{"class":521},"ResponseWriter",[507,3473,1344],{"class":517},[507,3475,3476],{"class":1097}," r",[507,3478,1400],{"class":517},[507,3480,2793],{"class":521},[507,3482,543],{"class":517},[507,3484,3485],{"class":521},"Request",[507,3487,1344],{"class":517},[507,3489,3490],{"class":1097}," request",[507,3492,3493],{"class":521}," any",[507,3495,1103],{"class":517},[507,3497,1094],{"class":517},[507,3499,3500],{"class":521},"any",[507,3502,1344],{"class":517},[507,3504,2202],{"class":1113},[507,3506,3507],{"class":517},")](\n",[507,3509,3510,3513,3515,3517,3519,3522],{"class":509,"line":584},[507,3511,3512],{"class":1097},"    v",[507,3514,1400],{"class":517},[507,3516,2834],{"class":521},[507,3518,543],{"class":517},[507,3520,3521],{"class":521},"Validator",[507,3523,3146],{"class":517},[507,3525,3526,3528,3530,3533,3536,3538,3541,3544,3546],{"class":509,"line":598},[507,3527,1103],{"class":517},[507,3529,1423],{"class":517},[507,3531,3532],{"class":1097},"f",[507,3534,3535],{"class":521}," H",[507,3537,1344],{"class":517},[507,3539,3540],{"class":1097}," operationID",[507,3542,3543],{"class":1113}," string",[507,3545,1103],{"class":517},[507,3547,3548],{"class":521}," H\n",[454,3550,3551,3552,3554,3555,3557,3558,3560,3561,3563],{},"The strict server decodes with ",[457,3553,1909],{},", without validation. ",[457,3556,3170],{}," closes that gap: it validates the fully bound request object (parameters + body) with the framework validator before your handler runs; a failure surfaces as a 422 problem through ",[457,3559,3392],{},". The type parameter exists because oapi-codegen defines ",[457,3562,3179],{}," per generated package.",[454,3565,3566,3567,3570,3571,3574,3575,3578,3579,3581,3582,3584,3585,3588],{},"The validator is resolved per request, in this order: the explicit ",[457,3568,3569],{},"v"," argument → the one set via ",[457,3572,3573],{},"server.WithValidator"," (from the context) → ",[457,3576,3577],{},"validate.New()",". That's why the generated code passes ",[457,3580,1467],{},": a single ",[457,3583,1640],{}," option in ",[457,3586,3587],{},"main.go"," applies application-wide, and the generated files need no edits.",[545,3590,3592],{"id":3591},"surface-middleware","Surface middleware",[454,3594,3595,3596,703,3598,3600,3601,3603,3604,3607],{},"Router-level middleware comes from ",[457,3597,1776],{},[457,3599,1779],{},"; anything scoped to one surface (like the role protection of the shop's admin surface) you wire inside the surface's ",[457,3602,2892],{}," block, at the ",[457,3605,3606],{},"\u002F\u002F gpsystem:surface-middleware"," anchor the generator leaves on its first line:",[498,3609,3611],{"className":500,"code":3610,"filename":2992,"language":502,"meta":503,"style":503},"func RegisterAdmin(router chi.Router, deps Dependencies) {\n    adminSvc := adminservice.New()\n    adminHandler := adminhttp.New(adminSvc)\n    adminPolicies := adminpolicy.New()\n    router.Route(\"\u002Fadmin\u002Fshop\", func(r chi.Router) {\n        \u002F\u002F gpsystem:surface-middleware\n        r.Use(server.RequireRole(\"admin\"))\n        shopadmingen.HandlerWithOptions(shopadmingen.NewStrictHandlerWithOptions(\n            adminHandler,\n            []shopadmingen.StrictMiddlewareFunc{\n                server.StrictValidator[shopadmingen.StrictHandlerFunc](nil),\n                kitpolicy.Enforcer[shopadmingen.StrictHandlerFunc](adminPolicies, shopadmingen.PermissionByOperation, shopadmingen.PolicyByOperation),\n            },\n            shopadmingen.StrictHTTPServerOptions{\n                RequestErrorHandlerFunc:  httperr.WriteBadRequest,\n                ResponseErrorHandlerFunc: server.WriteError,\n            },\n        ), shopadmingen.ChiServerOptions{\n            BaseRouter:       r,\n            ErrorHandlerFunc: httperr.WriteBadRequest,\n        })\n    })\n}\n",[457,3612,3613,3640,3656,3677,3693,3725,3729,3757,3777,3784,3796,3814,3854,3858,3869,3883,3897,3901,3913,3923,3937,3941,3945],{"__ignoreMap":503},[507,3614,3615,3617,3620,3622,3624,3626,3628,3630,3632,3634,3636,3638],{"class":509,"line":510},[507,3616,1091],{"class":517},[507,3618,3619],{"class":1106}," RegisterAdmin",[507,3621,1330],{"class":517},[507,3623,3006],{"class":1097},[507,3625,1429],{"class":521},[507,3627,543],{"class":517},[507,3629,1434],{"class":521},[507,3631,1344],{"class":517},[507,3633,2391],{"class":1097},[507,3635,3019],{"class":521},[507,3637,1103],{"class":517},[507,3639,581],{"class":517},[507,3641,3642,3645,3647,3650,3652,3654],{"class":509,"line":584},[507,3643,3644],{"class":587},"    adminSvc ",[507,3646,2084],{"class":517},[507,3648,3649],{"class":587}," adminservice",[507,3651,543],{"class":517},[507,3653,1471],{"class":1106},[507,3655,2122],{"class":517},[507,3657,3658,3661,3663,3666,3668,3670,3672,3675],{"class":509,"line":598},[507,3659,3660],{"class":587},"    adminHandler ",[507,3662,2084],{"class":517},[507,3664,3665],{"class":587}," adminhttp",[507,3667,543],{"class":517},[507,3669,1471],{"class":1106},[507,3671,1330],{"class":517},[507,3673,3674],{"class":587},"adminSvc",[507,3676,2058],{"class":517},[507,3678,3679,3682,3684,3687,3689,3691],{"class":509,"line":620},[507,3680,3681],{"class":587},"    adminPolicies ",[507,3683,2084],{"class":517},[507,3685,3686],{"class":587}," adminpolicy",[507,3688,543],{"class":517},[507,3690,1471],{"class":1106},[507,3692,2122],{"class":517},[507,3694,3695,3697,3699,3701,3703,3705,3707,3709,3711,3713,3715,3717,3719,3721,3723],{"class":509,"line":627},[507,3696,3081],{"class":587},[507,3698,543],{"class":517},[507,3700,2892],{"class":1106},[507,3702,1330],{"class":517},[507,3704,2429],{"class":517},[507,3706,3369],{"class":613},[507,3708,2429],{"class":517},[507,3710,1344],{"class":517},[507,3712,1423],{"class":517},[507,3714,1426],{"class":1097},[507,3716,1429],{"class":521},[507,3718,543],{"class":517},[507,3720,1434],{"class":521},[507,3722,1103],{"class":517},[507,3724,581],{"class":517},[507,3726,3727],{"class":509,"line":1971},[507,3728,3115],{"class":623},[507,3730,3731,3733,3735,3737,3739,3741,3743,3746,3748,3750,3753,3755],{"class":509,"line":1976},[507,3732,2419],{"class":587},[507,3734,543],{"class":517},[507,3736,2232],{"class":1106},[507,3738,1330],{"class":517},[507,3740,459],{"class":587},[507,3742,543],{"class":517},[507,3744,3745],{"class":1106},"RequireRole",[507,3747,1330],{"class":517},[507,3749,2429],{"class":517},[507,3751,3752],{"class":613},"admin",[507,3754,2429],{"class":517},[507,3756,2253],{"class":517},[507,3758,3759,3762,3764,3766,3768,3771,3773,3775],{"class":509,"line":1986},[507,3760,3761],{"class":587},"        shopadmingen",[507,3763,543],{"class":517},[507,3765,3125],{"class":1106},[507,3767,1330],{"class":517},[507,3769,3770],{"class":587},"shopadmingen",[507,3772,543],{"class":517},[507,3774,3135],{"class":1106},[507,3776,3138],{"class":517},[507,3778,3779,3782],{"class":509,"line":1991},[507,3780,3781],{"class":587},"            adminHandler",[507,3783,3146],{"class":517},[507,3785,3786,3788,3790,3792,3794],{"class":509,"line":2001},[507,3787,3151],{"class":517},[507,3789,3770],{"class":521},[507,3791,543],{"class":517},[507,3793,3158],{"class":521},[507,3795,2278],{"class":517},[507,3797,3798,3800,3802,3804,3806,3808,3810,3812],{"class":509,"line":2011},[507,3799,3165],{"class":587},[507,3801,543],{"class":517},[507,3803,3170],{"class":1106},[507,3805,2095],{"class":517},[507,3807,3770],{"class":521},[507,3809,543],{"class":517},[507,3811,3179],{"class":521},[507,3813,3182],{"class":517},[507,3815,3816,3818,3820,3822,3824,3826,3828,3830,3832,3835,3837,3840,3842,3844,3846,3848,3850,3852],{"class":509,"line":2021},[507,3817,3192],{"class":587},[507,3819,543],{"class":517},[507,3821,3197],{"class":1106},[507,3823,2095],{"class":517},[507,3825,3770],{"class":521},[507,3827,543],{"class":517},[507,3829,3179],{"class":521},[507,3831,3208],{"class":517},[507,3833,3834],{"class":587},"adminPolicies",[507,3836,1344],{"class":517},[507,3838,3839],{"class":587}," shopadmingen",[507,3841,543],{"class":517},[507,3843,3221],{"class":587},[507,3845,1344],{"class":517},[507,3847,3839],{"class":587},[507,3849,543],{"class":517},[507,3851,3230],{"class":587},[507,3853,2302],{"class":517},[507,3855,3856],{"class":509,"line":2030},[507,3857,3237],{"class":517},[507,3859,3860,3863,3865,3867],{"class":509,"line":2035},[507,3861,3862],{"class":521},"            shopadmingen",[507,3864,543],{"class":517},[507,3866,3247],{"class":521},[507,3868,2278],{"class":517},[507,3870,3871,3873,3875,3877,3879,3881],{"class":509,"line":2045},[507,3872,3254],{"class":587},[507,3874,561],{"class":517},[507,3876,3259],{"class":587},[507,3878,543],{"class":517},[507,3880,1889],{"class":587},[507,3882,3146],{"class":517},[507,3884,3885,3887,3889,3891,3893,3895],{"class":509,"line":2055},[507,3886,3270],{"class":587},[507,3888,561],{"class":517},[507,3890,2169],{"class":587},[507,3892,543],{"class":517},[507,3894,1886],{"class":587},[507,3896,3146],{"class":517},[507,3898,3899],{"class":509,"line":2061},[507,3900,3237],{"class":517},[507,3902,3903,3905,3907,3909,3911],{"class":509,"line":2066},[507,3904,3289],{"class":517},[507,3906,3839],{"class":521},[507,3908,543],{"class":517},[507,3910,3296],{"class":521},[507,3912,2278],{"class":517},[507,3914,3915,3917,3919,3921],{"class":509,"line":2078},[507,3916,3303],{"class":587},[507,3918,561],{"class":517},[507,3920,3308],{"class":587},[507,3922,3146],{"class":517},[507,3924,3925,3927,3929,3931,3933,3935],{"class":509,"line":2107},[507,3926,3315],{"class":587},[507,3928,561],{"class":517},[507,3930,3320],{"class":587},[507,3932,543],{"class":517},[507,3934,1889],{"class":587},[507,3936,3146],{"class":517},[507,3938,3939],{"class":509,"line":2125},[507,3940,3331],{"class":517},[507,3942,3943],{"class":509,"line":2156},[507,3944,3336],{"class":517},[507,3946,3947],{"class":509,"line":2161},[507,3948,630],{"class":517},[454,3950,1184,3951,3954,3955,3959,3960,3963,3964,3966,3967,3972,3973,3976,3977,3979,3980,3982,3983,3988,3989,3992,3993,543],{},[457,3952,3953],{},"rbac.Identity"," is already in the context: ",[462,3956,3957],{"href":211},[457,3958,1791],{}," wired ",[457,3961,3962],{},"server.IdentityMiddleware"," on the ",[457,3965,1795],{}," router, so ",[462,3968,3969],{"href":216},[457,3970,3971],{},"server.RequireRole(\"admin\")"," alone protects every ",[457,3974,3975],{},"\u002Fapi\u002Fv1\u002Fadmin\u002Fshop\u002F..."," route (401 without an identity, 403 without the role). The ",[457,3978,1795],{}," surface's public routes are untouched. In a project without ",[457,3981,1791],{},", wire the hard-require ",[462,3984,3985],{"href":211},[457,3986,3987],{},"server.AuthMiddleware"," in front of it, passing the ",[457,3990,3991],{},"auth.Config"," through the module's ",[457,3994,2275],{},[3996,3997,3998,3999,4002,4003,703,4006,2952,4009,4012,4013,4016,4017,4020,4021,4023,4024,4027,4028,703,4031,4034],"tip",{},"Rule of thumb for middleware levels: ",[478,4000,4001],{},"process-level"," (rate limit, tenant resolution) → ",[457,4004,4005],{},"server.WithMiddleware",[457,4007,4008],{},"server.WithStack",[478,4010,4011],{},"surface-level"," (auth, role) → ",[457,4014,4015],{},"r.Use"," at the ",[457,4018,4019],{},"gpsystem:surface-middleware"," anchor in the surface's ",[457,4022,2892],{}," block; ",[478,4025,4026],{},"operation-level"," (permission, ownership check) → ",[457,4029,4030],{},"@permission",[457,4032,4033],{},"@policy"," in the spec, enforced by the enforcer.",[545,4036,421],{"id":4037},"testing",[454,4039,4040,4042,4043,561],{},[457,4041,1471],{}," returns the fully wired router; you test with plain ",[457,4044,4045],{},"net\u002Fhttp\u002Fhttptest",[498,4047,4049],{"className":500,"code":4048,"language":502,"meta":503,"style":503},"r := server.New(server.Config{}, server.WithoutTelemetry())\nr.Post(\"\u002Fthings\", createThing)\n\nsrv := httptest.NewServer(r)\ndefer srv.Close()\nresp, err := http.Post(srv.URL+\"\u002Fthings\", \"application\u002Fjson\", body)\n",[457,4050,4051,4084,4109,4113,4134,4148],{"__ignoreMap":503},[507,4052,4053,4056,4058,4060,4062,4064,4066,4068,4070,4072,4075,4077,4079,4081],{"class":509,"line":510},[507,4054,4055],{"class":587},"r ",[507,4057,2084],{"class":517},[507,4059,2169],{"class":587},[507,4061,543],{"class":517},[507,4063,1471],{"class":1106},[507,4065,1330],{"class":517},[507,4067,459],{"class":521},[507,4069,543],{"class":517},[507,4071,548],{"class":521},[507,4073,4074],{"class":517},"{},",[507,4076,2169],{"class":587},[507,4078,543],{"class":517},[507,4080,1575],{"class":1106},[507,4082,4083],{"class":517},"())\n",[507,4085,4086,4088,4090,4093,4095,4097,4100,4102,4104,4107],{"class":509,"line":584},[507,4087,1426],{"class":587},[507,4089,543],{"class":517},[507,4091,4092],{"class":1106},"Post",[507,4094,1330],{"class":517},[507,4096,2429],{"class":517},[507,4098,4099],{"class":613},"\u002Fthings",[507,4101,2429],{"class":517},[507,4103,1344],{"class":517},[507,4105,4106],{"class":587}," createThing",[507,4108,2058],{"class":517},[507,4110,4111],{"class":509,"line":598},[507,4112,1086],{"emptyLinePlaceholder":1085},[507,4114,4115,4118,4120,4123,4125,4128,4130,4132],{"class":509,"line":620},[507,4116,4117],{"class":587},"srv ",[507,4119,2084],{"class":517},[507,4121,4122],{"class":587}," httptest",[507,4124,543],{"class":517},[507,4126,4127],{"class":1106},"NewServer",[507,4129,1330],{"class":517},[507,4131,1426],{"class":587},[507,4133,2058],{"class":517},[507,4135,4136,4139,4142,4144,4146],{"class":509,"line":627},[507,4137,4138],{"class":513},"defer",[507,4140,4141],{"class":587}," srv",[507,4143,543],{"class":517},[507,4145,2508],{"class":1106},[507,4147,2122],{"class":517},[507,4149,4150,4153,4155,4157,4159,4161,4163,4165,4167,4170,4172,4175,4178,4180,4182,4184,4186,4188,4191,4193,4195,4198],{"class":509,"line":1971},[507,4151,4152],{"class":587},"resp",[507,4154,1344],{"class":517},[507,4156,2539],{"class":587},[507,4158,2084],{"class":517},[507,4160,3466],{"class":587},[507,4162,543],{"class":517},[507,4164,4092],{"class":1106},[507,4166,1330],{"class":517},[507,4168,4169],{"class":587},"srv",[507,4171,543],{"class":517},[507,4173,4174],{"class":587},"URL",[507,4176,4177],{"class":517},"+",[507,4179,2429],{"class":517},[507,4181,4099],{"class":613},[507,4183,2429],{"class":517},[507,4185,1344],{"class":517},[507,4187,518],{"class":517},[507,4189,4190],{"class":613},"application\u002Fjson",[507,4192,2429],{"class":517},[507,4194,1344],{"class":517},[507,4196,4197],{"class":587}," body",[507,4199,2058],{"class":517},[454,4201,4202],{},"The panic recoverer, the 404\u002F405 problem handlers and the body limit are active here too, so the test sees exactly the problem+json response the client will.",[545,4204,4206],{"id":4205},"patterns-used","Patterns used",[1020,4208,4209,4224],{},[1023,4210,4211,1094,4214,703,4217,4220,4221,543],{},[478,4212,4213],{},"Context-injected validator",[457,4215,4216],{},"injectValidator",[457,4218,4219],{},"validatorFromContext",", with an unexported context key): ",[462,4222,199],{"href":4223},"\u002Fen\u002Fconcepts\u002Fdesign-patterns#context-injected-dependency",[1023,4225,4226,1094,4229,4232,4233,4235,4236,543],{},[478,4227,4228],{},"Generic middleware adapter",[457,4230,4231],{},"StrictValidator[H ~func(...)]",", fitting every generated ",[457,4234,3179],{},"): ",[462,4237,199],{"href":4238},"\u002Fen\u002Fconcepts\u002Fdesign-patterns#generic-middleware-adapter",[545,4240,4242],{"id":4241},"related-pages","Related pages",[1020,4244,4245,4257,4262,4267],{},[1023,4246,4247,4249,4250,4253,4254,4256],{},[462,4248,129],{"href":130},": the shared ",[457,4251,4252],{},"app.Run"," skeleton ",[457,4255,538],{}," builds on.",[1023,4258,4259,4261],{},[462,4260,317],{"href":318},": the full error-mapping table.",[1023,4263,4264,4266],{},[462,4265,322],{"href":323},": custom validation tags.",[1023,4268,4269,4271],{},[462,4270,220],{"href":221},": the enforcer used above.",[4273,4274,4275],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}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 .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}",{"title":503,"searchDepth":584,"depth":584,"links":4277},[4278,4284,4285,4288,4291,4294,4295,4296,4297],{"id":547,"depth":584,"text":548,"children":4279},[4280,4281,4282,4283],{"id":1014,"depth":598,"text":1015},{"id":1050,"depth":598,"text":1051},{"id":1142,"depth":598,"text":975},{"id":1180,"depth":598,"text":1181},{"id":1226,"depth":584,"text":1227},{"id":1314,"depth":584,"text":1315,"children":4286},[4287],{"id":1866,"depth":598,"text":1867},{"id":1917,"depth":584,"text":1918,"children":4289},[4290],{"id":2610,"depth":598,"text":2611},{"id":2969,"depth":584,"text":2970,"children":4292},[4293],{"id":3429,"depth":598,"text":3430},{"id":3591,"depth":584,"text":3592},{"id":4037,"depth":584,"text":421},{"id":4205,"depth":584,"text":4206},{"id":4241,"depth":584,"text":4242},"The chi-based HTTP chassis: Config (timeouts, CORS, body limit), the shared lifecycle, and how server.Run wires a router.","md",null,{},{"icon":137},{"title":134,"description":4298},"P7BErRba08vZ73HGGab3anN-WytYhkdVs_LNtSys_Jo",[4306,4308],{"title":129,"path":130,"stem":131,"description":4307,"icon":132,"children":-1},"The app package: the signal-driven graceful shutdown every gpsystem binary sits on.",{"title":139,"path":140,"stem":141,"description":4309,"icon":114,"children":-1},"The background-work chassis, asynq server, outbox relay and schedule runner in one binary, under a shared graceful shutdown.",1787346801837]