@stacksjs/router 0.75.27 → 0.75.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/runtime.d.ts CHANGED
@@ -6,5 +6,5 @@
6
6
  * eagerly loading root-only sessions, signed URLs, model binding, and typed
7
7
  * client helpers.
8
8
  */
9
- export { response } from '@stacksjs/bun-router';
9
+ export { response } from '@stacksjs/bun-router/runtime';
10
10
  export * from './stacks-router';
package/dist/runtime.js CHANGED
@@ -1 +1 @@
1
- export{response}from"@stacksjs/bun-router";export*from"./stacks-router";
1
+ export{response}from"@stacksjs/bun-router/runtime";export*from"./stacks-router";
@@ -1,8 +1,7 @@
1
1
  import type { Server } from 'bun';
2
2
  import './request-augmentation';
3
- import { response } from '@stacksjs/bun-router';
4
- import { Router } from '@stacksjs/bun-router';
5
- import type { ActionHandler, ActionPath, EnhancedRequest, ExtractRouteParams, KnownRouteName, MiddlewareHandler as BunMiddlewareHandler, MiddlewareReference, PathForRouteName, RequestFor, Route, ServerOptions } from '@stacksjs/bun-router';
3
+ import { response, Router } from '@stacksjs/bun-router/runtime';
4
+ import type { ActionHandler, ActionPath, EnhancedRequest, ExtractRouteParams, KnownRouteName, MiddlewareHandler as BunMiddlewareHandler, MiddlewareReference, PathForRouteName, RequestFor, Route, ServerOptions } from '@stacksjs/bun-router/runtime';
6
5
  import type { ActionResult, ActionValidations, ValidationResult } from '@stacksjs/actions';
7
6
  /**
8
7
  * Run every registered boot hook, once per process.
@@ -1,6 +1,6 @@
1
- var {require}=import.meta;import{response}from"@stacksjs/bun-router";import{ENRICHED_NOT_FOUND_RESPONSE}from"@stacksjs/bun-router/runtime";import"./request-augmentation";import process from"node:process";import{Buffer}from"node:buffer";import{existsSync}from"node:fs";import{log,report}from"@stacksjs/logging/runtime";import{appPath,frameworkPath,projectPath,storagePath}from"@stacksjs/path/project";import{applyRequestEnhancements,applyResponseCompression,Router,runWithRequest as runWithBunRouterRequest}from"@stacksjs/bun-router";const DATABASE_QUERY_TRACKER_KEY=Symbol.for("stacks.database.queryTracker"),queryTrackerGlobal=globalThis,loadQueryTracker=(...args)=>{const{trackQuery}=require("./query-tracking");queryTrackerGlobal[DATABASE_QUERY_TRACKER_KEY]=trackQuery;trackQuery(...args)};queryTrackerGlobal[DATABASE_QUERY_TRACKER_KEY]=loadQueryTracker;const ROUTER_INSTANCES_KEY=Symbol.for("@stacksjs/router:loaded-instances"),loadedRouterInstances=globalThis[ROUTER_INSTANCES_KEY]??=new Set;loadedRouterInstances.add(import.meta.path);const MULTI_INSTANCE_WARNED_KEY=Symbol.for("@stacksjs/router:multi-instance-warned"),bootHooks=[];let bootHooksRun=!1;export async function runBootHooks(){if(bootHooksRun)return;bootHooksRun=!0;for(const hook of bootHooks)try{await hook.run()}catch(error){log.error(`[router] boot hook "${hook.name}" failed:`,error)}}export function resetBootHooks(){bootHooks.length=0;bootHooksRun=!1}export function warnOnMultipleRouterInstances(){if(loadedRouterInstances.size<=1)return!1;const g=globalThis;if(!g[MULTI_INSTANCE_WARNED_KEY]){g[MULTI_INSTANCE_WARNED_KEY]=!0;const paths=[...loadedRouterInstances].map((p)=>` - ${p}`).join(`
1
+ var {require}=import.meta;import{applyRequestEnhancements,applyResponseCompression,ENRICHED_NOT_FOUND_RESPONSE,response,Router,runWithRequest as runWithBunRouterRequest}from"@stacksjs/bun-router/runtime";import"./request-augmentation";import process from"node:process";import{Buffer}from"node:buffer";import{existsSync}from"node:fs";import{log,report}from"@stacksjs/logging/runtime";import{appPath,frameworkPath,projectPath,storagePath}from"@stacksjs/path/project";const DATABASE_QUERY_TRACKER_KEY=Symbol.for("stacks.database.queryTracker"),queryTrackerGlobal=globalThis,loadQueryTracker=(...args)=>{const{trackQuery}=require("./query-tracking");queryTrackerGlobal[DATABASE_QUERY_TRACKER_KEY]=trackQuery;trackQuery(...args)};queryTrackerGlobal[DATABASE_QUERY_TRACKER_KEY]=loadQueryTracker;const ROUTER_INSTANCES_KEY=Symbol.for("@stacksjs/router:loaded-instances"),loadedRouterInstances=globalThis[ROUTER_INSTANCES_KEY]??=new Set;loadedRouterInstances.add(import.meta.path);const MULTI_INSTANCE_WARNED_KEY=Symbol.for("@stacksjs/router:multi-instance-warned"),bootHooks=[];let bootHooksRun=!1;export async function runBootHooks(){if(bootHooksRun)return;bootHooksRun=!0;for(const hook of bootHooks)try{await hook.run()}catch(error){log.error(`[router] boot hook "${hook.name}" failed:`,error)}}export function resetBootHooks(){bootHooks.length=0;bootHooksRun=!1}export function warnOnMultipleRouterInstances(){if(loadedRouterInstances.size<=1)return!1;const g=globalThis;if(!g[MULTI_INSTANCE_WARNED_KEY]){g[MULTI_INSTANCE_WARNED_KEY]=!0;const paths=[...loadedRouterInstances].map((p)=>` - ${p}`).join(`
2
2
  `);log.warn(`${loadedRouterInstances.size} distinct @stacksjs/router modules loaded in one process; they now share one route table (stacksjs/stacks#1982) so routing still works, but this is a duplicated install worth fixing. It usually means an app vendors storage/framework/core AND installs the published @stacksjs/* dist, and a tsconfig \`paths\` mapping (\`@stacksjs/* -> ./*/src\`) splits module resolution between core files and root files. See stacksjs/stacks#1975.
3
3
  Loaded instances:
4
- ${paths}`)}return!0}let __defaultsPkgRoot,__defaultsPathCache;function resolveDefaultsPath(rel){const cached=__defaultsPathCache?.get(rel);if(cached!==void 0)return cached;if(rel.startsWith("app/")){const published=appPath(rel.slice(4));if(existsSync(published)){(__defaultsPathCache??=new Map).set(rel,published);return published}}const vendored=storagePath(`framework/defaults/${rel}`);let resolved;if(existsSync(vendored))resolved=vendored;else{if(__defaultsPkgRoot===void 0)try{const pkgJson=Bun.resolveSync("@stacksjs/defaults/package.json",process.cwd());__defaultsPkgRoot=pkgJson.slice(0,pkgJson.lastIndexOf("/"))}catch{__defaultsPkgRoot=null}resolved=__defaultsPkgRoot?`${__defaultsPkgRoot}/${rel}`:vendored}(__defaultsPathCache??=new Map).set(rel,resolved);return resolved}const NATIVE_ROUTE_PARAM=/^\{[A-Z_$][\w$]*\}$/i;function isNativeRouteMethod(method){return method==="GET"||method==="POST"||method==="PUT"||method==="PATCH"||method==="DELETE"||method==="OPTIONS"||method==="HEAD"}function hasNativeCompatiblePath(path){if(path==="*")return!0;const segments=path.split("/");for(let index=0;index<segments.length;index++){const segment=segments[index];if(segment===""||NATIVE_ROUTE_PARAM.test(segment))continue;if(segment==="*"&&index===segments.length-1)continue;if(segment.includes("{")||segment.includes(":")||segment.includes("*"))return!1}return!0}export function shouldUseNativeRoutesByDefault(routes){let hasEligibleRoute=!1;for(const route of routes){if(!isNativeRouteMethod(route.method)||route.handler instanceof Response)continue;hasEligibleRoute=!0;if(route.nativeDispatch===!1||route.domain||route.constraints&&Object.keys(route.constraints).length>0||!hasNativeCompatiblePath(route.path))return!1}return hasEligibleRoute}import{runWithRequestArgument,runWithRequestArguments,setAmbientRequestContext}from"./request-context";import{isApiRequest,JSON_CONTENT_TYPE}from"./api-shape";import{applySecurityHeaders,createJsonSecurityHeaders,secureSerializedJsonResponse}from"./security-headers";import{isCursorPaginator,isPaginator,isSimplePaginator}from"@stacksjs/pagination";let errorHandlerModule,errorHandlerModuleLoad;async function loadErrorHandlerModule(){return errorHandlerModule??=await(errorHandlerModuleLoad??=import("./error-handler"))}async function createErrorResponse(...args){return(await loadErrorHandlerModule()).createErrorResponse(...args)}async function createMiddlewareErrorResponse(...args){return(await loadErrorHandlerModule()).createMiddlewareErrorResponse(...args)}function bindAsyncRouteHandler(handler){return(req,preparedBaseResult,hasPreparedBaseResult=!1)=>runWithRequestArguments(req,handler,req,preparedBaseResult,hasPreparedBaseResult)}const stacksRouteHandlers=new WeakSet;function rememberStacksRouteHandler(handler){stacksRouteHandlers.add(handler);return handler}function invokeInlineHandler(req,handler,_unused){const result=handler(req);if(result instanceof Promise)return result.then((value)=>formatResult(value,req));return formatResult(result,req)}const STATIC_RESPONSE_METHODS=new Set(["GET","POST","PUT","PATCH","DELETE","OPTIONS"]),CSRF_DEFAULT=0,CSRF_SKIPPED=1,CSRF_REQUIRED=2,ROUTE_ACCEPTS_CSRF=1,ROUTE_MAY_HAVE_BODY=2,ROUTE_RENDERS_CSRF=4,ROUTE_SEEDS_CSRF=8,ROUTE_FORCES_JSON=16,ROUTE_ACTION_SKIPS_CSRF=32,routeStateRegistries=new Set,clearedRouteKeysByRegistry=new WeakMap;function rateLimitWindowToSeconds(window){if(typeof window==="number"){if(!Number.isFinite(window)||window<=0)throw Error(`[Router] .rateLimit(): window must be a positive number of seconds, got ${window}`);return Math.floor(window)}switch(window){case"second":return 1;case"minute":return 60;case"hour":return 3600;case"day":return 86400;default:throw Error(`[Router] .rateLimit(): unknown period '${String(window)}'`)}}class BoundedMap{max;map;constructor(max){this.max=max}get(key){return this.map?.get(key)}has(key){return this.map?.has(key)??!1}set(key,value){const map=this.map??=new Map;if(map.has(key))map.delete(key);map.set(key,value);if(map.size>this.max){const oldest=map.keys().next().value;if(oldest!==void 0)map.delete(oldest)}return this}delete(key){return this.map?.delete(key)??!1}clear(){this.map=void 0}get size(){return this.map?.size??0}}function isExposeRoutesAuthorized(req){const flag=process.env.STACKS_EXPOSE_ROUTES??"";if(!flag)return!((process.env.APP_ENV??"").toLowerCase()==="production"||process.env.NODE_ENV==="production");if(flag==="1")return!((process.env.APP_ENV??"").toLowerCase()==="production"||process.env.NODE_ENV==="production");const url=new URL(req.url),submitted=req.headers.get("x-stacks-routes-token")||req.headers.get("X-Stacks-Routes-Token")||url.searchParams.get("token")||"";if(typeof submitted!=="string"||submitted.length===0||submitted.length!==flag.length)return!1;try{const encoder=new TextEncoder;return crypto.timingSafeEqual(encoder.encode(submitted),encoder.encode(flag))}catch{return!1}}let corsHeaderApplierCache={},compressionApplierCache={};function applyCorsIfConfigured(req,response){if(!req._corsConfig||!response)return response;const cache=corsHeaderApplierCache;if(!cache.apply)return loadAndApplyCorsHeaders(req,response,cache);try{return cache.apply(req,response,req._corsConfig)}catch(err){log.warn("[router] CORS header injection failed",{error:err});return response}}async function loadAndApplyCorsHeaders(req,response,cache){try{return(cache.apply??=(await import(resolveDefaultsPath("app/Middleware/Cors.ts"))).applyCorsHeaders)(req,response,req._corsConfig)}catch(err){log.warn("[router] CORS header injection failed",{error:err});return response}}const ACTION_CACHE_MAX=5000,actionSkipsCsrfCache=new BoundedMap(ACTION_CACHE_MAX),routeHandlerKeyRegistry=new BoundedMap(ACTION_CACHE_MAX);let routeActionRegistry;const CSRF_SEEDED_BY_HANDLE_REQUEST=Symbol.for("stacks.router.csrfSeededByHandleRequest"),FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND=Symbol("stacks.router.frameworkResponseBodySizeUpperBound"),NATIVE_DIRECT_ROUTE_REQUEST=Symbol("stacks.router.nativeDirectRouteRequest"),CSRF_SECURE_TRANSPORT=Symbol.for("@stacksjs/router:csrf-secure-transport"),secureNativeRouteRouters=new WeakSet,csrfEnabledNativeRouteRouters=new WeakSet,routersWithoutRequestContext=new WeakSet,EMPTY_MIDDLEWARE_TIMINGS=[],EMPTY_MIDDLEWARE_ENTRIES=[],CSRF_ONLY_MIDDLEWARE=["csrf"];let resolvedMiddlewareEntryCache,resolvedCsrfOnlyMiddleware,namedRouteRegistry;function compileNamedRoute(path){const paramNames=extractRouteParamNames(path),colonRegex=new Map;for(const name of paramNames)colonRegex.set(name,new RegExp(`(^|/):${name}(?=$|/)`,"g"));return{path,paramNames,colonRegex}}function extractRouteParamNames(routePath){const names=new Set;for(const m of routePath.matchAll(/\{(\w+)\}/g))if(m[1])names.add(m[1]);for(const m of routePath.matchAll(/(?:^|\/):(\w+)(?=$|\/)/g))if(m[1])names.add(m[1]);return[...names]}export function url(routeName,params={}){const named=namedRouteRegistry?.get(routeName);if(!named){const availableRoutes=namedRouteRegistry?[...namedRouteRegistry.keys()].join(", "):"";throw Error(`Route '${routeName}' is not defined. Available routes: ${availableRoutes}`)}const missing=named.paramNames.filter((name)=>!(name in params)||params[name]===void 0);if(missing.length>0)throw Error(`url('${routeName}'): missing required path param${missing.length>1?"s":""} [${missing.join(", ")}] for path '${named.path}'. Pass them as the second argument: url('${routeName}', { ${named.paramNames.join(", ")} })`);let appUrl;try{appUrl=process.env.APP_URL||"https://localhost"}catch{appUrl="https://localhost"}appUrl=appUrl.replace(/\/$/,"");if(!appUrl.startsWith("http"))appUrl=`https://${appUrl}`;let resolvedPath=named.path;const queryParams={};for(const[key,value]of Object.entries(params)){const curly=`{${key}}`;if(resolvedPath.includes(curly))resolvedPath=resolvedPath.replaceAll(curly,encodeURIComponent(String(value)));else{const re=named.colonRegex.get(key);if(re&&re.test(resolvedPath)){re.lastIndex=0;resolvedPath=resolvedPath.replace(re,`$1${encodeURIComponent(String(value))}`)}else queryParams[key]=String(value)}}const queryString=Object.keys(queryParams).length>0?`?${new URLSearchParams(queryParams).toString()}`:"";return`${appUrl}${resolvedPath}${queryString}`}export function routeParams(routeName){const named=namedRouteRegistry?.get(routeName);return named?[...named.paramNames]:[]}export function listNamedRoutes(){const out={};if(namedRouteRegistry)for(const[name,named]of namedRouteRegistry)out[name]=named.path;return out}export function listRegisteredRoutes(){const namesByPath=namedRouteRegistry?new Map:void 0;if(namesByPath&&namedRouteRegistry){for(const[name,named]of namedRouteRegistry)if(!namesByPath.has(named.path))namesByPath.set(named.path,name)}const out=[],seen=new Set;for(const registry of routeStateRegistries)for(const key of registry.keys()){if(seen.has(key))continue;seen.add(key);const idx=key.indexOf(":");if(idx===-1)continue;const method=key.slice(0,idx),path=key.slice(idx+1),routeName=namesByPath?.get(path);out.push({method,path,name:routeName,handler:routeHandlerKeyRegistry.get(key),action:routeActionRegistry?.get(key),middleware:registry.get(key)?.middleware})}return out.sort((a,b)=>a.path.localeCompare(b.path))}const MIDDLEWARE_TIMEOUT_MS=30000;let _debugLoggingCache;function isDebugLogging(){if(_debugLoggingCache===void 0){const level=(process.env.LOG_LEVEL||"info").toLowerCase();_debugLoggingCache=level!=="info"&&level!=="warn"&&level!=="error"}return _debugLoggingCache}const DEFAULT_MIDDLEWARE_PRIORITY=10;let _warnedInvalidPriorities;function warnInvalidMiddlewarePriority(name,raw){const key=`${name}:${String(raw)}`;if(_warnedInvalidPriorities?.has(key))return;(_warnedInvalidPriorities??=new Set).add(key);log.warn(`[Router] Middleware '${name}' declared an invalid priority (${String(raw)}). Priorities must be a finite non-negative number; falling back to default ${DEFAULT_MIDDLEWARE_PRIORITY}.`)}function adaptMiddlewareForBunRouter(middleware){if(middleware&&typeof middleware==="object"&&typeof middleware.toRouterHandler==="function")return middleware.toRouterHandler();if(middleware&&typeof middleware==="object"&&typeof middleware.handle==="function"&&typeof middleware!=="function"){const handle=middleware.handle.bind(middleware);return async(req,next)=>{try{await handle(req)}catch(thrown){if(thrown instanceof Response)return thrown;throw thrown}return next()}}return middleware}let middlewareCache,middlewareAliasesPromise=null;async function getMiddlewareAliases(){if(middlewareAliasesPromise)return middlewareAliasesPromise;middlewareAliasesPromise=(async()=>{const merged={};for(const load of[()=>import(resolveDefaultsPath("app/Middleware.ts")),()=>import(appPath("Middleware.ts"))])try{const module=await load();Object.assign(merged,module.default??{})}catch{}return merged})();return middlewareAliasesPromise}const PASCAL_SPLIT_REGEX=/[-_\s]+/;let pascalCaseCache;function toPascalCase(input){if(!input)return input;const cached=pascalCaseCache?.get(input);if(cached!==void 0)return cached;const out=input.split(PASCAL_SPLIT_REGEX).filter(Boolean).map((part)=>part.charAt(0).toUpperCase()+part.slice(1)).join("");(pascalCaseCache??=new Map).set(input,out);return out}async function resolveMiddlewareName(name){const resolved=(await getMiddlewareAliases())[name]||toPascalCase(name);log.debug(`[middleware] Resolved: ${name} \u2192 ${resolved}`);return resolved}let middlewareRegistryPromise=null;async function getMiddlewareRegistry(){if(middlewareRegistryPromise)return middlewareRegistryPromise;middlewareRegistryPromise=(async()=>{try{const dir=storagePath("framework/auto-imports"),module=await import(`${dir}/middleware.ts`);if(!module.middleware)return null;const{resolve}=await import("node:path");return Object.fromEntries(Object.entries(module.middleware).map(([name,file])=>[name,resolve(dir,file)]))}catch{return null}})();return middlewareRegistryPromise}async function loadMiddleware(name){const cachedMiddleware=middlewareCache??=new Map;if(cachedMiddleware.has(name))return cachedMiddleware.get(name)??null;const className=await resolveMiddlewareName(name),registered=(await getMiddlewareRegistry())?.[className];if(registered)try{const handler=(await import(registered)).default??null;if(!handler||typeof handler.handle!=="function"){log.error(`[Router] Middleware '${name}' resolved to ${registered}, but the file has no default export with a handle() method`);cachedMiddleware.set(name,null);return null}cachedMiddleware.set(name,handler);return handler}catch(err){log.error(`[Router] Failed to load middleware '${name}' from ${registered}:`,err);return null}let userPathError;try{const userPath=appPath(`Middleware/${className}.ts`),handler=(await import(userPath)).default??null;if(!handler||typeof handler.handle!=="function"){log.error(`[Router] Middleware '${name}' resolved to ${userPath}, but the file has no default export with a handle() method`);cachedMiddleware.set(name,null);return null}cachedMiddleware.set(name,handler);return handler}catch(err){userPathError=err}try{const defaultPath=resolveDefaultsPath(`app/Middleware/${className}.ts`),handler=(await import(defaultPath)).default??null;if(!handler||typeof handler.handle!=="function"){log.error(`[Router] Middleware '${name}' resolved to ${defaultPath}, but the file has no default export with a handle() method`);cachedMiddleware.set(name,null);return null}cachedMiddleware.set(name,handler);return handler}catch(err){const userMsg=userPathError instanceof Error?userPathError.message:String(userPathError);log.error(`[Router] Failed to load middleware '${name}' (resolved to '${className}'). app/Middleware: ${userMsg}; defaults:`,err);return null}}export async function middlewareAliases(){return{...await getMiddlewareAliases()}}export async function loadMiddlewareHandlers(){const aliases=await getMiddlewareAliases(),generated=await getMiddlewareRegistry(),references=new Set([...Object.keys(aliases),...Object.values(aliases),...Object.keys(generated??{})]),handlers={};await Promise.all([...references].map(async(reference)=>{const handler=await loadMiddleware(reference);if(handler)handlers[reference]=handler}));return handlers}let negatedMiddlewareCache,routerHttpErrorModule;function isShortCircuit(thrown){if(thrown instanceof Response)return!0;return typeof thrown==="object"&&thrown!==null&&(("status"in thrown)||("statusCode"in thrown))}function negateMiddleware(name,inner,cache){const cached=cache.get(name);if(cached)return cached;const negated={priority:inner.priority,async handle(req){try{await inner.handle(req)}catch(thrown){if(isShortCircuit(thrown))return;throw thrown}const{HttpError}=routerHttpErrorModule??=await import("@stacksjs/error-handling/http-error");throw new HttpError(403,`Access denied. This route requires "${name}" not to apply.`)}};cache.set(name,negated);return negated}function loadParsedMiddleware(parsed){const negatedCache=parsed.negated?negatedMiddlewareCache??=new Map:void 0,cached=middlewareCache?.get(parsed.name);if(cached!==void 0)return cached&&negatedCache?negateMiddleware(parsed.name,cached,negatedCache):cached;return loadMiddleware(parsed.name).then((handler)=>handler&&negatedCache?negateMiddleware(parsed.name,handler,negatedCache):handler)}export function clearMiddlewareCache(){corsHeaderApplierCache={};compressionApplierCache={};middlewareCache=void 0;negatedMiddlewareCache=void 0;resolvedMiddlewareEntryCache=void 0;resolvedCsrfOnlyMiddleware=void 0;parsedMiddlewareCache=void 0;middlewareAliasesPromise=null;middlewareRegistryPromise=null;actionRegistryPromise=null;actionSkipsCsrfCache.clear();routeHandlerKeyRegistry.clear();routeActionRegistry=void 0;clearCsrfModuleCache()}export function installMiddlewareHotReload(){if(process.env.APP_ENV==="production"||process.env.NODE_ENV==="production")return()=>{};let fsWatchers=[];(async()=>{try{const fs=await import("node:fs"),targets=[appPath("Middleware"),appPath("Middleware.ts")];for(const target of targets)try{if(!fs.existsSync(target))continue;const w=fs.watch(target,{recursive:!0},()=>{log.debug("[middleware] hot-reload: clearing cache");clearMiddlewareCache()});fsWatchers.push(w)}catch{}}catch{}})();return()=>{for(const w of fsWatchers)try{w.close()}catch{}fsWatchers=[]}}export function clearRouteMiddlewareRegistry(){for(const registry of routeStateRegistries){const registeredKeys=clearedRouteKeysByRegistry.get(registry)??new Set;for(const routeKey of registry.keys())registeredKeys.add(routeKey);clearedRouteKeysByRegistry.set(registry,registeredKeys);for(const state of registry.values())if(state.middleware)state.middleware.length=0;registry.clear()}routeStateRegistries.clear()}let routeApiResponseRegistry,parsedMiddlewareCache;function parseMiddlewareEntry(middleware){const cache=parsedMiddlewareCache??=new Map;let parsed=cache.get(middleware);if(!parsed){parsed=parseMiddlewareEntryUncached(middleware).then((resolved)=>{cache.set(middleware,resolved);return resolved},(error)=>{cache.delete(middleware);throw error});cache.set(middleware,parsed)}return parsed}async function parseMiddlewareEntryUncached(middleware){const timingName=middleware.replace(/[^A-Za-z0-9_-]/g,"_").slice(0,32),negated=middleware.startsWith("!"),bare=negated?middleware.slice(1):middleware,aliases=await getMiddlewareAliases();if(Object.hasOwn(aliases,bare))return{name:bare,negated,timingName};const colonIndex=bare.indexOf(":");if(colonIndex===-1)return{name:bare,negated,timingName};return{name:bare.substring(0,colonIndex),negated,timingName,params:bare.substring(colonIndex+1)}}export async function findUnresolvableRouteMiddleware(){const usage=new Map;for(const registry of routeStateRegistries)for(const[routeKey,state]of registry){const entries=state.middleware;if(!entries)continue;for(const entry of entries){const pending=parseMiddlewareEntry(entry),parsed=pending instanceof Promise?await pending:pending,alias=parsed.negated?`!${parsed.name}`:parsed.name,seen=usage.get(alias)??{parsed,routes:[]};seen.routes.push(routeKey);usage.set(alias,seen)}}if(!usage.has("csrf"))usage.set("csrf",{parsed:{name:"csrf",negated:!1,timingName:"csrf"},routes:["(auto-injected on POST/PUT/PATCH/DELETE)"]});const unresolvable=[];for(const[alias,{parsed,routes}]of usage){const handler=await loadParsedMiddleware(parsed);if(!handler||typeof handler.handle!=="function")unresolvable.push({alias,routes})}return unresolvable}export async function assertRouteMiddlewareResolvable(){const unresolvable=await findUnresolvableRouteMiddleware();if(unresolvable.length===0)return;const detail=unresolvable.map((u)=>`"${u.alias}" (used by ${u.routes.join(", ")})`).join("; ");throw Error(`[Router] Unresolvable middleware alias(es): ${detail}. Check the alias map in app/Middleware.ts or add app/Middleware/<Class>.ts.`)}function finishSynchronousResult(req,response,csrfHandledByOuter,routeSeedsCsrf){const frameworkMetadataApplied=typeof response[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND]==="number";if(response.status>=400||req._corsConfig||req._compress===!0||req._startNs!=null||Array.isArray(req._afterResponse)||req._responseHeaders)return;if(frameworkMetadataApplied&&!routeSeedsCsrf)return response;let finalized=response;if(routeSeedsCsrf&&!csrfHandledByOuter&&req[CSRF_SEEDED_BY_HANDLE_REQUEST]!==!0){const mod=loadCsrfModule();if(mod instanceof Promise)return;if(mod)finalized=mod.seedCsrfCookieIfMissing(req,finalized,req._csrfToken,frameworkMetadataApplied)}if(!frameworkMetadataApplied)try{const requestId=req._requestId;if(requestId)finalized.headers.set("X-Request-ID",requestId);applySecurityHeaders(finalized.headers)}catch{return}return finalized}function routeCapabilityFlags(routeKey,handler,csrfEnabled){const routeMethod=routeKey.slice(0,routeKey.indexOf(":")).toUpperCase(),action=isRouterAction(handler)?handler:void 0,forcesJson=routeApiResponseRegistry?.has(routeKey)===!0||action?.apiResponse===!0;let flags=0;if(csrfEnabled&&(routeMethod==="POST"||routeMethod==="PUT"||routeMethod==="PATCH"||routeMethod==="DELETE"))flags|=ROUTE_ACCEPTS_CSRF;if(routeMethod!=="GET"&&routeMethod!=="HEAD")flags|=ROUTE_MAY_HAVE_BODY;if(csrfEnabled&&!forcesJson&&(routeMethod==="GET"||routeMethod==="HEAD"))flags|=ROUTE_RENDERS_CSRF;if(csrfEnabled&&(routeMethod==="GET"||routeMethod==="HEAD"||routeMethod==="OPTIONS"))flags|=ROUTE_SEEDS_CSRF;if(forcesJson)flags|=ROUTE_FORCES_JSON;if(action&&(action.skipCsrf===!0||action.csrf===!1))flags|=ROUTE_ACTION_SKIPS_CSRF;return flags}let routeRateLimitModule;function createMiddlewareHandler(router,routeStates,routeKey,handler,csrfEnabled=!0){const routeState=routeStates.get(routeKey),routeFlags=routeCapabilityFlags(routeKey,handler,csrfEnabled),handlerKey=typeof handler==="string"?handler:void 0,synchronousInlineHandler=typeof handler==="function"&&handler.constructor.name!=="AsyncFunction"?handler:void 0,asynchronousInlineHandler=typeof handler==="function"&&handler.constructor.name==="AsyncFunction"?handler:void 0,wrappedBase=typeof handler==="function"?void 0:wrapHandler(handler,!0,routeKey),handleAsyncInContext=async(req,preparedBaseResult,hasPreparedBaseResult=!1)=>{if((routeFlags&ROUTE_MAY_HAVE_BODY)!==0)try{await parseRequestBody(req)}catch(err){const error=err instanceof Error?err:Error(String(err));return createMiddlewareErrorResponse(error,req)}const enhancedReq=req,csrfHandledByOuter=enhancedReq[CSRF_SEEDED_BY_HANDLE_REQUEST]===!0;if(!csrfHandledByOuter&&(routeFlags&ROUTE_RENDERS_CSRF)!==0&&requestMayRenderHtml(enhancedReq)){const renderTokenSeeding=seedCsrfTokenForRender(enhancedReq);if(renderTokenSeeding)await renderTokenSeeding}if(handlerKey&&(routeFlags&ROUTE_ACCEPTS_CSRF)!==0&&actionSkipsCsrfCache.get(handlerKey)===void 0){const pending=resolveStringHandler(handlerKey);if(pending instanceof Promise)try{await pending}catch{}}if((routeFlags&ROUTE_FORCES_JSON)!==0)req._forceJson=!0;if(!hasPreparedBaseResult&&synchronousInlineHandler&&(routeFlags&ROUTE_ACCEPTS_CSRF)===0&&!routeState?.middleware?.length&&!routeState?.rateLimit){try{preparedBaseResult=runWithRequestArguments(enhancedReq,invokeInlineHandler,enhancedReq,synchronousInlineHandler,void 0)}catch(error){preparedBaseResult=createHandlerErrorResponse(error,enhancedReq,routeKey,router)}hasPreparedBaseResult=!0;if(preparedBaseResult instanceof Response){const finished=finishSynchronousResult(enhancedReq,preparedBaseResult,csrfHandledByOuter,(routeFlags&ROUTE_SEEDS_CSRF)!==0);if(finished)return finished}}const rl=routeState?.rateLimit;if(rl)try{const{rateLimit:enforceRateLimit}=routeRateLimitModule??=await import("./rate-limit");await enforceRateLimit(routeKey,rl.max).over(rl.windowSeconds)}catch(err){return createMiddlewareErrorResponse(err,req)}const userMiddleware=routeState?.middleware??EMPTY_MIDDLEWARE_ENTRIES;let shouldInjectCsrf=!1;if((routeFlags&ROUTE_ACCEPTS_CSRF)!==0){const alreadyHasCsrf=userMiddleware.some((m)=>m==="csrf"||m.startsWith("csrf:")),routeCsrfMode=routeState?.csrfMode??CSRF_DEFAULT,routeSkipped=routeCsrfMode===CSRF_SKIPPED,routeRequired=routeCsrfMode===CSRF_REQUIRED,actionSkipped=(routeFlags&ROUTE_ACTION_SKIPS_CSRF)!==0||(handlerKey?actionSkipsCsrfCache.get(handlerKey)===!0:!1);shouldInjectCsrf=!alreadyHasCsrf&&(routeRequired||!routeSkipped&&!actionSkipped)}const middlewareEntries=shouldInjectCsrf?userMiddleware.length===0?CSRF_ONLY_MIDDLEWARE:["csrf",...userMiddleware]:userMiddleware;let middlewareTimings=EMPTY_MIDDLEWARE_TIMINGS;if(middlewareEntries.length>0){if(isDebugLogging()){const schemeEnd=req.url.indexOf("://"),pathStart=schemeEnd===-1?0:req.url.indexOf("/",schemeEnd+3),q=req.url.indexOf("?",pathStart<0?0:pathStart),urlPath=pathStart<0?"/":req.url.slice(pathStart,q===-1?void 0:q),routeMethod=routeKey.slice(0,routeKey.indexOf(":")).toUpperCase();log.debug(`[middleware] Executing chain: [${middlewareEntries.join(", ")}] for ${routeMethod} ${urlPath}`)}const cachedCsrfOnlyMiddleware=middlewareEntries===CSRF_ONLY_MIDDLEWARE?resolvedCsrfOnlyMiddleware:void 0,resolved=cachedCsrfOnlyMiddleware??[],resolvedCache=resolvedMiddlewareEntryCache??=new Map,unresolvedEntries=cachedCsrfOnlyMiddleware?EMPTY_MIDDLEWARE_ENTRIES:middlewareEntries;for(const middlewareEntry of unresolvedEntries){let resolvedEntry=resolvedCache.get(middlewareEntry);if(!resolvedEntry){const pending=parseMiddlewareEntry(middlewareEntry),parsed=pending instanceof Promise?await pending:pending,loaded=loadParsedMiddleware(parsed),middleware=loaded instanceof Promise?await loaded:loaded;if(middleware&&typeof middleware.handle==="function"){const rawPriority=middleware.priority;let priority=DEFAULT_MIDDLEWARE_PRIORITY;if(typeof rawPriority==="number"&&Number.isFinite(rawPriority)&&rawPriority>=0)priority=rawPriority;else if(rawPriority!==void 0)warnInvalidMiddlewarePriority(middlewareEntry,rawPriority);resolvedEntry={name:middlewareEntry,timingName:parsed.timingName,handler:middleware,priority,parameterName:parsed.params?parsed.name:void 0,params:parsed.params};resolvedCache.set(middlewareEntry,resolvedEntry)}}if(!resolvedEntry){log.error(`[Router] Middleware '${middlewareEntry}' on ${routeKey} could not be resolved - failing closed`);const failClosedError=Error(`Middleware '${middlewareEntry}' could not be resolved`),failClosedResponse=await createErrorResponse(failClosedError,enhancedReq,{status:500});return await applyCorsIfConfigured(enhancedReq,failClosedResponse)}if(resolvedEntry.params&&resolvedEntry.parameterName){enhancedReq._middlewareParams=enhancedReq._middlewareParams||{};enhancedReq._middlewareParams[resolvedEntry.parameterName]=resolvedEntry.params}resolved.push(resolvedEntry)}if(middlewareEntries===CSRF_ONLY_MIDDLEWARE&&!cachedCsrfOnlyMiddleware&&resolved.length===1&&resolvedCache===resolvedMiddlewareEntryCache)resolvedCsrfOnlyMiddleware=resolved;if(resolved.length>1)resolved.sort((a,b)=>a.priority-b.priority);const collectsMiddlewareTimings=enhancedReq._startNs!=null;if(collectsMiddlewareTimings)middlewareTimings=[];let chainTimer,chainBudget,runningMiddleware="";try{for(const{name:middlewareName,timingName,handler:middleware}of resolved){const mwStart=collectsMiddlewareTimings?performance.now():0;runningMiddleware=middlewareName;try{const outcome=middleware.handle(enhancedReq);if(outcome&&typeof outcome.then==="function"){if(!chainBudget){chainBudget=new Promise((_,reject)=>{chainTimer=setTimeout(()=>reject(Error(`Middleware '${runningMiddleware}' exceeded ${MIDDLEWARE_TIMEOUT_MS}ms`)),MIDDLEWARE_TIMEOUT_MS)});chainBudget.catch(()=>{})}await Promise.race([outcome,chainBudget])}if(collectsMiddlewareTimings){const elapsedMs=performance.now()-mwStart;middlewareTimings.push({name:timingName,ms:elapsedMs})}}catch(error){if(collectsMiddlewareTimings){const elapsedMs=performance.now()-mwStart;middlewareTimings.push({name:timingName,ms:elapsedMs})}log.debug(`[middleware] Blocked by: ${middlewareName}`);if(error instanceof Response){try{const{_requestId:reqId,_startNs:startNs}=enhancedReq,total=startNs!=null?Number(process.hrtime.bigint()-startNs)/1e6:null,parts=total!=null?[`total;dur=${total.toFixed(1)}`]:[];for(const t of middlewareTimings)parts.push(`mw_${t.name};dur=${t.ms.toFixed(1)}`);if(parts.length>0)error.headers.set("Server-Timing",parts.join(", "));if(reqId)error.headers.set("X-Request-ID",reqId)}catch{}return await applyCorsIfConfigured(enhancedReq,error)}const err=error instanceof Error?error:Error(String(error)),errorResponse="statusCode"in err||"status"in err?await createMiddlewareErrorResponse(err,enhancedReq):await(()=>{log.error(`[Router] Middleware '${middlewareName}' threw an unexpected error:`,err);return createErrorResponse(err,enhancedReq,{status:500})})();try{const{_requestId:reqId,_startNs:startNs}=enhancedReq,total=startNs!=null?Number(process.hrtime.bigint()-startNs)/1e6:null,parts=total!=null?[`total;dur=${total.toFixed(1)}`]:[];for(const t of middlewareTimings)parts.push(`mw_${t.name};dur=${t.ms.toFixed(1)}`);if(parts.length>0)errorResponse.headers.set("Server-Timing",parts.join(", "));if(reqId)errorResponse.headers.set("X-Request-ID",reqId)}catch{}return await applyCorsIfConfigured(enhancedReq,errorResponse)}}}finally{if(chainTimer)clearTimeout(chainTimer)}}const formatsResolvedInlineResult=asynchronousInlineHandler!==void 0&&!hasPreparedBaseResult;let response;try{const baseResult=hasPreparedBaseResult?preparedBaseResult:asynchronousInlineHandler?asynchronousInlineHandler(enhancedReq):synchronousInlineHandler?invokeInlineHandler(enhancedReq,synchronousInlineHandler,void 0):wrappedBase(enhancedReq),resolvedResult=baseResult instanceof Response?baseResult:await baseResult;response=formatsResolvedInlineResult?formatResult(resolvedResult,enhancedReq):resolvedResult}catch(error){response=await createHandlerErrorResponse(error,enhancedReq,handlerKey||routeKey,router)}if(response){if((routeFlags&ROUTE_SEEDS_CSRF)!==0&&!csrfHandledByOuter&&enhancedReq[CSRF_SEEDED_BY_HANDLE_REQUEST]!==!0)try{const mod=loadCsrfModule(),csrf=mod instanceof Promise?await mod:mod;if(csrf)response=csrf.seedCsrfCookieIfMissing(enhancedReq,response,enhancedReq._csrfToken,typeof response[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND]==="number")}catch(err){log.warn("[router] CSRF cookie seeding failed",{error:err})}}if(response&&enhancedReq._corsConfig){const corsResponse=applyCorsIfConfigured(enhancedReq,response);response=corsResponse instanceof Response?corsResponse:await corsResponse}const{_requestId:reqId,_startNs:startNs}=enhancedReq,durMs=startNs!=null?Number(process.hrtime.bigint()-startNs)/1e6:null,{_afterResponse:after,_responseHeaders:requested}=enhancedReq,frameworkMetadataApplied=response&&typeof response[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND]==="number";if(response&&(response.status>=400||Array.isArray(after)||requested&&typeof requested==="object"||durMs!=null||!frameworkMetadataApplied)&&typeof response.headers?.set==="function"){const setHeaders=(h)=>{if(Array.isArray(after))for(const callback of after)try{if(typeof callback==="function")callback({status:response.status,durationMs:durMs??0})}catch{}if(requested&&typeof requested==="object"){for(const[name,value]of Object.entries(requested))if(typeof value==="string")h.set(name,value)}if(reqId&&!frameworkMetadataApplied)h.set("X-Request-ID",reqId);if(durMs!=null){let timing=`total;dur=${durMs.toFixed(1)}`;for(const t of middlewareTimings)timing+=`, mw_${t.name};dur=${t.ms.toFixed(1)}`;h.set("Server-Timing",timing)}if(!frameworkMetadataApplied)applySecurityHeaders(h)};if(response.status>=400&&(response.headers.get("content-type")||"").includes("json")&&reqId)try{const text=await response.clone().text(),parsed=JSON.parse(text);if(parsed&&typeof parsed==="object"){const newHeaders=new Headers(response.headers);setHeaders(newHeaders);return new Response(JSON.stringify({...parsed,request_id:reqId}),{status:response.status,statusText:response.statusText,headers:newHeaders})}}catch{}try{setHeaders(response.headers)}catch{try{const cloned=response.clone(),newHeaders=new Headers(response.headers);setHeaders(newHeaders);return new Response(cloned.body,{status:response.status,statusText:response.statusText,headers:newHeaders})}catch{}}}if(enhancedReq._compress===!0&&response)try{const cache=compressionApplierCache,applyCompression=cache.apply??=(await import(resolveDefaultsPath("app/Middleware/Compress.ts"))).applyCompression,bodySizeUpperBound=response[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND];return await applyCompression(enhancedReq,response,typeof bodySizeUpperBound==="number"?bodySizeUpperBound:void 0)}catch(err){log.warn(`[router] Compression failed; sending uncompressed response: ${err instanceof Error?err.message:String(err)}`)}return response};if(!synchronousInlineHandler||(routeFlags&ROUTE_MAY_HAVE_BODY)!==0)return rememberStacksRouteHandler(bindAsyncRouteHandler(handleAsyncInContext));return rememberStacksRouteHandler((req)=>{if(routeState?.middleware?.length||routeState?.rateLimit)return runWithRequestArgument(req,handleAsyncInContext,req);const csrfHandledByOuter=req[CSRF_SEEDED_BY_HANDLE_REQUEST]===!0;if((routeFlags&ROUTE_RENDERS_CSRF)!==0&&!csrfHandledByOuter&&requestMayRenderHtml(req)){const renderTokenSeeding=seedCsrfTokenForRender(req);if(renderTokenSeeding)return renderTokenSeeding.then(()=>runWithRequestArgument(req,handleAsyncInContext,req))}if((routeFlags&ROUTE_FORCES_JSON)!==0)req._forceJson=!0;let preparedBaseResult;try{preparedBaseResult=runWithRequestArguments(req,invokeInlineHandler,req,synchronousInlineHandler,void 0)}catch(error){preparedBaseResult=createHandlerErrorResponse(error,req,routeKey,router)}if(preparedBaseResult instanceof Response){const finished=finishSynchronousResult(req,preparedBaseResult,csrfHandledByOuter,(routeFlags&ROUTE_SEEDS_CSRF)!==0);if(finished)return finished}return runWithRequestArguments(req,handleAsyncInContext,req,preparedBaseResult,!0)})}function createInertRoute(){const inert={middleware:()=>inert,name:()=>inert,skipCsrf:()=>inert,requireCsrf:()=>inert,rateLimit:()=>inert};return inert}function createChainableRoute(routeStates,routeKey,shadowed=!1){if(shadowed)return createInertRoute();const routePath=routeKey.includes(":")?routeKey.substring(routeKey.indexOf(":")+1):routeKey,routeState=routeStates.get(routeKey),chain={middleware(name){if(!routeState)return chain;const middlewareList=routeState.middleware??=[];for(const entry of Array.isArray(name)?name:[name]){if(typeof entry!=="string")throw TypeError(`[Router] middleware() on ${routeKey} was given a ${typeof entry}; it takes an alias or an array of aliases`);middlewareList.push(entry)}return chain},name(routeName){(namedRouteRegistry??=new Map).set(routeName,compileNamedRoute(routePath));return chain},skipCsrf(){if(routeState)routeState.csrfMode=CSRF_SKIPPED;return chain},requireCsrf(){if(routeState)routeState.csrfMode=CSRF_REQUIRED;return chain},rateLimit(max,window){if(!Number.isFinite(max)||max<=0)throw Error(`[Router] .rateLimit(): max must be a positive number, got ${String(max)}`);const windowSeconds=rateLimitWindowToSeconds(window);if(routeState)routeState.rateLimit={max:Math.floor(max),windowSeconds};return chain}};return chain}async function fileExists(path){try{return await Bun.file(path).exists()}catch{return!1}}function assertSafeHandlerPath(handlerPath){if(typeof handlerPath!=="string"||handlerPath.length===0)throw Error(`[Router] Refusing to resolve handler '${String(handlerPath)}': empty or non-string`);if(handlerPath.includes("\x00"))throw Error("[Router] Refusing to resolve handler with null byte");if(handlerPath.startsWith("/")||/^[A-Za-z]:[\\/]/.test(handlerPath))throw Error(`[Router] Refusing to resolve absolute handler path '${handlerPath}'`);if(handlerPath.split(/[/\\]/).some((s)=>s===".."))throw Error(`[Router] Refusing to resolve handler path '${handlerPath}' (contains '..' segment)`)}let _moduleImportCache;function cachedImport(fullPath){const cache=_moduleImportCache??=new Map;let p=cache.get(fullPath);if(!p){p=import(fullPath);cache.set(fullPath,p)}return p}let _resolvedHandlerCache;function resolveStringHandler(handlerPath){const cache=_resolvedHandlerCache??=new Map;let resolved=cache.get(handlerPath);if(!resolved){resolved=resolveStringHandlerUncached(handlerPath).then((handler)=>{cache.set(handlerPath,handler);return handler},(error)=>{cache.delete(handlerPath);throw error});cache.set(handlerPath,resolved)}return resolved}function validationFailureResponse(errors){return response.validationError(errors)}export function precognitionRequest(req,knownHeader){const header=knownHeader===void 0?req.headers?.get?.("Precognition"):knownHeader,viaHeader=typeof header==="string"&&header.toLowerCase()==="true";let viaQuery=!1;try{if(!viaHeader&&req.url.includes("?"))viaQuery=new URL(req.url).searchParams.get("_validate")==="1"}catch{viaQuery=!1}if(!viaHeader&&!viaQuery)return null;return{only:(req.headers?.get?.("Precognition-Validate-Only")??"").split(",").map((field)=>field.trim()).filter(Boolean)}}export function precognitionSuccess(){return new Response(null,{status:204,headers:{Precognition:"true","Precognition-Success":"true",Vary:"Precognition, Precognition-Validate-Only"}})}let actionRegistryPromise=null;async function getActionRegistry(){if(actionRegistryPromise)return actionRegistryPromise;actionRegistryPromise=(async()=>{try{const dir=storagePath("framework/auto-imports"),module=await import(`${dir}/actions.ts`);if(!module.actions)return null;const{resolve}=await import("node:path");return Object.fromEntries(Object.entries(module.actions).map(([name,file])=>[name,resolve(dir,file)]))}catch{return null}})();return actionRegistryPromise}async function resolveStringHandlerUncached(handlerPath){assertSafeHandlerPath(handlerPath);let modulePath=handlerPath;modulePath=modulePath.endsWith(".ts")?modulePath.slice(0,-3):modulePath;if(modulePath.includes("Controller")){const[controllerPath,methodName="index"]=modulePath.split("@"),userPath=appPath(`${controllerPath}.ts`),defaultPath=resolveDefaultsPath(`app/${controllerPath}.ts`),fullPath=await fileExists(userPath)?userPath:defaultPath;try{const controller=await cachedImport(fullPath);if(!controller.default||typeof controller.default!=="function")throw Error(`Controller ${controllerPath} does not export a default class`);const instance=new controller.default;if(typeof instance[methodName]!=="function")throw Error(`Method ${methodName} not found in controller ${controllerPath}`);return async(req)=>{const result=await instance[methodName](req);return formatResult(result,req)}}catch(error){log.error(`[Router] Failed to load controller '${fullPath}':`,error);throw error}}let fullPath;if(modulePath.includes("storage/framework/orm"))fullPath=modulePath;else if(modulePath.includes("OrmAction"))fullPath=storagePath(`framework/actions/src/${modulePath}.ts`);else if(modulePath.includes("Actions")){const registered=(await getActionRegistry())?.[modulePath];if(registered)fullPath=registered;else{const userPath=projectPath(`app/${modulePath}.ts`),defaultPath=resolveDefaultsPath(`app/${modulePath}.ts`);fullPath=await fileExists(userPath)?userPath:defaultPath}}else{const userPath=appPath(`${modulePath}.ts`),defaultPath=resolveDefaultsPath(`app/${modulePath}.ts`);fullPath=await fileExists(userPath)?userPath:defaultPath}try{const action=(await cachedImport(fullPath)).default;if(!action)throw Error(`Action '${handlerPath}' has no default export`);if(typeof action.handle!=="function"){log.error(`[Router] Action '${handlerPath}' structure:`,Object.keys(action));throw Error(`Action '${handlerPath}' has no handle() method. Got: ${typeof action.handle}`)}return wrapAction(action,handlerPath)}catch(importError){log.error(`[Router] Failed to import action '${fullPath}':`,importError);throw importError}}export function isRouterAction(handler){return typeof handler==="object"&&handler!==null&&typeof handler.handle==="function"}export function wrapAction(action,handlerKey){const actionSkipsCsrf=action.skipCsrf===!0||action.csrf===!1;actionSkipsCsrfCache.set(handlerKey,actionSkipsCsrf);const actionForcesJson=action.apiResponse===!0,requestValidationRules=action.validations??modelValidationRules(action.modelDefinition??action.model),actionValidationEntries=action.validations?compileActionValidationEntries(action.validations):void 0,routeSeparator=handlerKey.indexOf(":"),actionMayHaveRouteParams=routeSeparator===-1||handlerKey.indexOf("{",routeSeparator+1)!==-1,prepareRequest=(req)=>{const precognitionHeader=req.headers.get("Precognition"),requestHasQuery=req.url.includes("?"),precognition=precognitionHeader===null&&!requestHasQuery?null:precognitionRequest(req,precognitionHeader);if(precognition){if(!action.validations)return precognitionSuccess();const rules=precognition.only.length>0?Object.fromEntries(Object.entries(action.validations).filter(([field])=>precognition.only.includes(field))):action.validations,precognitionResult=validateActionInputSync(req,rules,rules===action.validations?actionValidationEntries:void 0,actionMayHaveRouteParams,!0,requestHasQuery);return precognitionResult.valid?precognitionSuccess():validationFailureResponse(precognitionResult.errors)}if(action.validations){const validationResult=validateActionInputSync(req,action.validations,actionValidationEntries,actionMayHaveRouteParams,!0,requestHasQuery);if(!validationResult.valid)return validationFailureResponse(validationResult.errors)}},rethrowActionError=(handleError)=>{report(handleError,{label:`[Router] action.handle() for '${handlerKey}'`});throw handleError};if(typeof action.authorize!=="function"&&typeof action.before!=="function")return(req)=>{if(actionSkipsCsrf)req._skipCsrf=!0;if(actionForcesJson)req._forceJson=!0;req._requestValidationRules=requestValidationRules;try{const earlyResponse=prepareRequest(req);if(earlyResponse)return earlyResponse;const result=action.handle(req);return result instanceof Promise?result.then((value)=>{try{return formatResult(value,req)}catch(error){return rethrowActionError(error)}},rethrowActionError):formatResult(result,req)}catch(handleError){return rethrowActionError(handleError)}};return async(req)=>{if(actionSkipsCsrf)req._skipCsrf=!0;if(actionForcesJson)req._forceJson=!0;req._requestValidationRules=requestValidationRules;try{const earlyResponse=prepareRequest(req);if(earlyResponse)return earlyResponse;if(typeof action.authorize==="function"){const pendingAuth=action.authorize(req),auth=pendingAuth!==null&&(typeof pendingAuth==="object"||typeof pendingAuth==="function")?await pendingAuth:pendingAuth;if(auth instanceof Response)return auth;if(auth===!1)return Response.json({error:"Forbidden"},{status:403})}if(typeof action.before==="function"){const pendingBefore=action.before(req),pre=pendingBefore!==null&&(typeof pendingBefore==="object"||typeof pendingBefore==="function")?await pendingBefore:pendingBefore;if(pre instanceof Response)return pre}const pendingResult=action.handle(req),result=pendingResult instanceof Promise?await pendingResult:pendingResult;return formatResult(result,req)}catch(handleError){return rethrowActionError(handleError)}}}const EMPTY_VALIDATION_ERRORS=Object.freeze({}),VALID_ACTION_VALIDATION_RESULT=Object.freeze({valid:!0,errors:EMPTY_VALIDATION_ERRORS});function propertyOwner(value,property){let current=value;while(current){if(Object.hasOwn(current,property))return current;current=Object.getPrototypeOf(current)}}function compileActionValidationEntries(validations){return Object.entries(validations).map(([field,validation])=>{const rule=validation.rule;if(typeof rule.getRules!=="function"||propertyOwner(rule,"validate")!==propertyOwner(rule,"getRules"))return[field,validation,void 0,void 0];const rules=rule.getRules();if(!Array.isArray(rules)||rules.some((entry)=>typeof entry?.test!=="function"))return[field,validation,void 0,void 0];return[field,validation,rules.map((entry)=>entry.test),void 0]})}function createValidationFieldLabel(field){const label=field.replace(/[-_]+/g," ").replace(/([a-z])([A-Z])/g,"$1 $2").replace(/^./,(c)=>c.toUpperCase()),lowerField=field.toLowerCase();return{label,decorate:(message)=>message.toLowerCase().startsWith(lowerField)||message.includes(label)?message:`${label} ${message}`}}function validateActionInputSync(req,validations,compiledEntries,mayHaveRouteParams=!0,reuseSuccessResult=!1,requestHasQuery=req.url.includes("?")){let errors;const validated={};let valid=!0;const entries=compiledEntries??Object.entries(validations),input=getRequestInput(req,entries,mayHaveRouteParams,requestHasQuery);for(let entryIndex=0;entryIndex<entries.length;entryIndex++){const entry=entries[entryIndex],field=entry[0],validation=entry[1],ruleTests=entry[2],value=input[field];let result;try{const rule=validation.rule;let passedCompiledRules=!1;if(ruleTests){const empty=value===void 0||value===null||value==="";passedCompiledRules=empty&&rule.isRequired!==!0;if(!empty){passedCompiledRules=!0;for(let i=0;i<ruleTests.length;i++)if(!ruleTests[i](value)){passedCompiledRules=!1;break}}}if(!passedCompiledRules)result=rule.validate(value)}catch{result={valid:!1,errors:[{message:`${field} validation failed`}]}}if(result&&!result.valid){valid=!1;const fieldErrors=[],{label,decorate}=entry[3]??=createValidationFieldLabel(field);if(result.errors&&result.errors.length>0)if(validation.message){const firstMessage=result.errors[0]?.message??"";fieldErrors.push(typeof validation.message==="string"?validation.message:validation.message[field]||decorate(firstMessage))}else result.errors.forEach((err)=>fieldErrors.push(decorate(err.message)));else fieldErrors.push(validation.message?typeof validation.message==="string"?validation.message:`${label} is invalid`:`${label} is invalid`);(errors??={})[field]=fieldErrors}else if(value!==void 0)validated[field]=value}if(valid)req._validatedInput=validated;if(valid&&reuseSuccessResult)return VALID_ACTION_VALIDATION_RESULT;return{valid,errors:errors??{}}}export async function validateActionInput(req,validations){return validateActionInputSync(req,validations)}function modelValidationRules(model){if(!model?.attributes||typeof model.attributes!=="object")return;const rules={};for(const[field,attribute]of Object.entries(model.attributes)){const validation=attribute?.validation;if(validation?.rule)rules[field]=validation}return Object.keys(rules).length>0?rules:void 0}function getRequestInput(req,validationEntries,mayHaveRouteParams=!0,requestHasQuery=req.url.includes("?")){let input,mayNeedCoercion=!1;if(requestHasQuery){const q=req.query;if(q)for(const key in q){(input??={})[key]=q[key];mayNeedCoercion=!0}else new URL(req.url).searchParams.forEach((value,key)=>{(input??={})[key]=value;mayNeedCoercion=!0})}let hasRouteParams=!1;if(mayHaveRouteParams&&req.params)for(const key in req.params){if(!Object.hasOwn(req.params,key))continue;(input??={})[key]=req.params[key];hasRouteParams=!0;mayNeedCoercion=!0}if(req.jsonBody&&typeof req.jsonBody==="object")input=mayNeedCoercion?Object.assign(input??{},req.jsonBody):req.jsonBody;else if(req.formBody&&typeof req.formBody==="object"){input=mayNeedCoercion?Object.assign(input??{},req.formBody):{...req.formBody};mayNeedCoercion=!0}if(!requestHasQuery&&!mayHaveRouteParams&&req.jsonBody&&typeof req.jsonBody==="object"&&!Array.isArray(req.jsonBody)&&!req.formBody&&!req.files)req._allInputCache=input;if(req.files&&typeof req.allFiles==="function")try{const files=req.allFiles();for(const key of Object.keys(files??{})){const values=input??={};if(!(key in values))values[key]=files[key]}}catch{}if(!validationEntries)return input??{};input??={};let coerced=!1;if(mayNeedCoercion)for(const[field,validation]of validationEntries){const value=input[field];if(typeof value!=="string")continue;const validatorName=validation.rule?.name;if(validatorName==="number"){const n=Number(value);if(Number.isFinite(n)){input[field]=n;coerced=!0}}else if(validatorName==="boolean"){if(value==="true"||value==="1"){input[field]=!0;coerced=!0}else if(value==="false"||value==="0"){input[field]=!1;coerced=!0}}}if(!coerced&&!req.formBody&&!req.files&&!hasRouteParams)req._allInputCache=input;return input}function formatResult(result,req){if(result instanceof Response)return result;if(result instanceof ReadableStream)return new Response(result,{headers:{"Content-Type":"application/octet-stream"}});if(result!==null&&typeof result==="object"){const linkHeader="data"in result?buildPaginatorLinkHeader(result):null;return formatJsonResult(result,req,linkHeader)}const apiShaped=req._forceJson===!0||isApiRequest(req);if(result===null||result===void 0)return apiShaped?new Response(null,{status:204}):new Response("",{status:200});if(apiShaped){if(typeof result==="string"||typeof result==="number"||typeof result==="boolean")return formatJsonResult(result,req);return serializedResponse(JSON.stringify(result)??"","application/json;charset=utf-8")}return serializedResponse(String(result),"text/plain; charset=utf-8")}function serializedResponse(body,contentType){return new Response(body,{headers:{"Content-Type":contentType,"Content-Length":String(Buffer.byteLength(body))}})}function formatJsonResult(result,req,linkHeader){const canPreapplyMetadata=!req._responseHeaders,body=JSON.stringify(result)??"",requestMarkers=req,directNativeRequest=typeof requestMarkers[CSRF_SECURE_TRANSPORT]==="boolean"||requestMarkers[NATIVE_DIRECT_ROUTE_REQUEST]===!0,bodyLength=canPreapplyMetadata&&directNativeRequest?void 0:Buffer.byteLength(body),csrf=requestMarkers[CSRF_SEEDED_BY_HANDLE_REQUEST]!==!0&&typeof requestMarkers[CSRF_SECURE_TRANSPORT]==="boolean"?loadCsrfModule():null,csrfCookie=canPreapplyMetadata&&csrf&&!(csrf instanceof Promise)&&typeof csrf.createCsrfCookie==="function"?csrf.createCsrfCookie(req,req._csrfToken):void 0,response=canPreapplyMetadata?secureSerializedJsonResponse(body,bodyLength,req._requestId,csrfCookie):new Response(body,{headers:createJsonSecurityHeaders()});if(!canPreapplyMetadata)response.headers.set("Content-Length",String(bodyLength));if(linkHeader)response.headers.set("Link",linkHeader);if(canPreapplyMetadata){const frameworkResponse=response;frameworkResponse[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND]=bodyLength??(body.length*3>=1024&&body.length<1024?Buffer.byteLength(body):body.length*3)}if(csrfCookie)requestMarkers[CSRF_SEEDED_BY_HANDLE_REQUEST]=!0;return response}function buildPaginatorLinkHeader(value){if(!isPaginator(value)&&!isSimplePaginator(value)&&!isCursorPaginator(value))return null;const v=value,parts=[];if(v.prev_page_url)parts.push(`<${v.prev_page_url}>; rel="prev"`);if(v.next_page_url)parts.push(`<${v.next_page_url}>; rel="next"`);if(v.first_page_url)parts.push(`<${v.first_page_url}>; rel="first"`);if(v.last_page_url)parts.push(`<${v.last_page_url}>; rel="last"`);return parts.length>0?parts.join(", "):null}export function stream(source,options={}){const baseHeaders={};if(options.type==="sse"){baseHeaders["Content-Type"]="text/event-stream; charset=utf-8";baseHeaders["Cache-Control"]="no-cache";baseHeaders.Connection="keep-alive"}else if(options.type==="ndjson")baseHeaders["Content-Type"]="application/x-ndjson; charset=utf-8";else baseHeaders["Content-Type"]=options.contentType??"application/octet-stream";const body=source instanceof ReadableStream?source:new ReadableStream({async start(controller){try{for await(const chunk of source)controller.enqueue(typeof chunk==="string"?new TextEncoder().encode(chunk):chunk);controller.close()}catch(err){controller.error(err)}}}),merged=new Headers(baseHeaders);if(options.headers)new Headers(options.headers).forEach((value,key)=>merged.set(key,value));return new Response(body,{status:options.status??200,headers:merged})}function getAllInputFor(req){const cached=req._allInputCache;if(cached)return cached;const input={},query=req.query;if(query)for(const key in query)input[key]=query[key];if(req.jsonBody&&typeof req.jsonBody==="object")Object.assign(input,req.jsonBody);if(req.formBody&&typeof req.formBody==="object")Object.assign(input,req.formBody);if(req.params&&typeof req.params==="object")Object.assign(input,req.params);req._allInputCache=input;return input}function flashInputFor(req,keys){const input=getAllInputFor(req);req._oldInput=keys?Object.fromEntries(keys.filter((key)=>(key in input)).map((key)=>[key,input[key]])):{...input}}const nativeRequestText=Request.prototype.text,nativeRequestJson=Request.prototype.json,nativeRequestBytes=Request.prototype.bytes,nativeRequestArrayBuffer=Request.prototype.arrayBuffer,nativeRequestBlob=Request.prototype.blob,nativeRequestClone=Request.prototype.clone;let requestBodyEncoder,requestValidationModule,UploadedFileConstructor,requestGateModule;function createUploadedFile(file){UploadedFileConstructor??=require("@stacksjs/storage/uploaded-file").UploadedFile;return new UploadedFileConstructor(file)}const REQUEST_METHODS={text(){return this._rawBody===void 0?nativeRequestText.call(this):Promise.resolve(this._rawBody)},json(){if(this._rawBody===void 0)return nativeRequestJson.call(this);try{return Promise.resolve(this._rawBody.length===0?null:JSON.parse(this._rawBody))}catch(error){return Promise.reject(error)}},bytes(){return this._rawBody===void 0?nativeRequestBytes.call(this):Promise.resolve((requestBodyEncoder??=new TextEncoder).encode(this._rawBody))},arrayBuffer(){if(this._rawBody===void 0)return nativeRequestArrayBuffer.call(this);const bytes=(requestBodyEncoder??=new TextEncoder).encode(this._rawBody);return Promise.resolve(bytes.buffer)},blob(){return this._rawBody===void 0?nativeRequestBlob.call(this):Promise.resolve(new Blob([this._rawBody],{type:this._rawBodyContentType}))},clone(){if(this._rawBody===void 0)return nativeRequestClone.call(this);return new Request(this.url,{method:this.method,headers:this.headers,body:this._rawBody})},get(key,defaultValue){const value=getAllInputFor(this)[key];return value!==void 0?value:defaultValue},input(key,defaultValue){const value=getAllInputFor(this)[key];return value!==void 0?value:defaultValue},all(){return getAllInputFor(this)},only(keys){const input=getAllInputFor(this),result={};for(const key of keys)if(key in input)result[key]=input[key];return result},except(keys){const result={...getAllInputFor(this)};for(const key of keys)delete result[key];return result},has(key){const input=getAllInputFor(this);if(Array.isArray(key))return key.every((k)=>(k in input)&&input[k]!==void 0);return key in input&&input[key]!==void 0},hasAny(keys){const input=getAllInputFor(this);return keys.some((k)=>(k in input)&&input[k]!==void 0)},filled(key){const input=getAllInputFor(this),isFilled=(k)=>{const value=input[k];return value!==void 0&&value!==null&&value!==""&&!(Array.isArray(value)&&value.length===0)};if(Array.isArray(key))return key.every(isFilled);return isFilled(key)},missing(key){const input=getAllInputFor(this);if(Array.isArray(key))return key.every((k)=>!(k in input)||input[k]===void 0);return!(key in input)||input[key]===void 0},merge(data){Object.assign(getAllInputFor(this),data)},keys(){return Object.keys(getAllInputFor(this))},string(key,defaultValue=""){const value=getAllInputFor(this)[key];return value!==void 0&&value!==null?String(value):defaultValue},integer(key,defaultValue=0){const value=getAllInputFor(this)[key];if(value===void 0||value===null||value==="")return defaultValue;if(typeof value==="number")return Number.isFinite(value)?Math.trunc(value):defaultValue;const str=String(value).trim();if(!/^-?\d+$/.test(str))return defaultValue;const parsed=Number.parseInt(str,10);return Number.isFinite(parsed)?parsed:defaultValue},float(key,defaultValue=0){const value=getAllInputFor(this)[key];if(value===void 0||value===null||value==="")return defaultValue;if(typeof value==="number")return Number.isFinite(value)?value:defaultValue;const str=String(value).trim();if(!/^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(str))return defaultValue;const parsed=Number.parseFloat(str);return Number.isFinite(parsed)?parsed:defaultValue},boolean(key,defaultValue=!1){const value=getAllInputFor(this)[key];if(value===void 0||value===null)return defaultValue;if(typeof value==="boolean")return value;if(value==="true"||value==="1"||value===1)return!0;if(value==="false"||value==="0"||value===0)return!1;return defaultValue},array(key){const value=getAllInputFor(this)[key];if(Array.isArray(value))return value;return value!==void 0&&value!==null?[value]:[]},date(key){const value=getAllInputFor(this)[key];if(value===void 0||value===null||value==="")return null;const parsed=new Date(value);return Number.isNaN(parsed.getTime())?null:parsed},enum(key,enumType){const value=getAllInputFor(this)[key];if(value===void 0||value===null)return null;if(Object.values(enumType).includes(value))return value;const enumKey=String(value);return enumKey in enumType?enumType[enumKey]:null},collect(key){const{collect}=require("@stacksjs/collections"),value=getAllInputFor(this)[key];if(Array.isArray(value))return collect(value);return collect(value===void 0||value===null?[]:[value])},whenHas(key,callback,defaultCallback){const input=getAllInputFor(this);if(key in input&&input[key]!==void 0)callback(input[key]);else defaultCallback?.()},whenFilled(key,callback,defaultCallback){const value=getAllInputFor(this)[key];if(value!==void 0&&value!==null&&value!==""&&!(Array.isArray(value)&&value.length===0))callback(value);else defaultCallback?.()},isValue(key,value){return getAllInputFor(this)[key]===value},async validate(rules,messages={}){const selectedRules=rules??this._requestValidationRules;if(!selectedRules||Object.keys(selectedRules).length===0){const input=getAllInputFor(this);this._validatedInput=input;return input}const entries=Object.entries(selectedRules);let normalized=selectedRules;for(let index=0;index<entries.length;index++){const[field,definition]=entries[index];if(typeof definition==="string")throw TypeError(`String validation rules are not supported for "${field}". Use schema validators.`);if(definition&&typeof definition==="object"&&"rule"in definition){const message=messages[field];if(message){if(normalized===selectedRules){normalized={};for(let previousIndex=0;previousIndex<index;previousIndex++){const[previousField,previousDefinition]=entries[previousIndex];normalized[previousField]=previousDefinition}}normalized[field]={...definition,message};continue}}if(normalized!==selectedRules)normalized[field]=definition}const{validate}=requestValidationModule??=await import("@stacksjs/validation/request-validator"),validated=await validate(this,normalized);this._validatedInput=validated;return validated},getValidated(){return this._validatedInput??{}},safe(){const data=this._validatedInput??{};return{all:()=>({...data}),get:(key,defaultValue)=>(key in data)?data[key]:defaultValue,only:(keys)=>Object.fromEntries(keys.filter((key)=>(key in data)).map((key)=>[key,data[key]])),except:(keys)=>Object.fromEntries(Object.entries(data).filter(([key])=>!keys.includes(key)))}},old(key,defaultValue){return this._oldInput?.[key]??defaultValue},flashInput(keys){flashInputFor(this,keys)},flashInputOnly(keys){flashInputFor(this,keys)},flashInputExcept(keys){const input=getAllInputFor(this);this._oldInput=Object.fromEntries(Object.entries(input).filter(([key])=>!keys.includes(key)))},file(key){const file=(this.files||{})[key];if(!file)return null;const rawFile=Array.isArray(file)?file[0]:file;return rawFile?createUploadedFile(rawFile):null},getFiles(key){const file=(this.files||{})[key];if(!file)return[];return(Array.isArray(file)?file:[file]).map(createUploadedFile)},hasFile(key){const files=this.files||{};return key in files&&files[key]!==void 0},allFiles(){const files=this.files||{},result={};for(const[key,value]of Object.entries(files))if(Array.isArray(value))result[key]=value.map((f)=>createUploadedFile(f));else result[key]=createUploadedFile(value);return result},getParams(){return{...this.params}},isEmpty(){return Object.keys(getAllInputFor(this)).length===0},browser(){return this.headers.get("sec-ch-ua")||this.headers.get("user-agent")},ipForRateLimit(){const ip=this.ip;if(typeof ip==="function")return ip.call(this)||null;return typeof ip==="string"&&ip?ip:null},getMethod(){return this.method.toUpperCase()},async user(){return this._authenticatedUser},async userToken(){return this._currentAccessToken},async tokenCan(ability){if(typeof ability!=="string"||ability.length===0)return!1;const token=this._currentAccessToken;if(!token||typeof token!=="object")return!1;const abilities=token.abilities;if(!Array.isArray(abilities))return!1;if(abilities.includes("*"))return!0;return abilities.includes(ability)},async tokenCant(ability){return!await this.tokenCan(ability)},async can(ability,...args){if(typeof ability!=="string"||ability.length===0)return!1;const{Gate}=requestGateModule??=await import("@stacksjs/auth/gate"),user=this._authenticatedUser??null;return Gate.allows(ability,user,...args)},async cannot(ability,...args){return!await this.can(ability,...args)},async authorize(ability,...args){const{Gate}=requestGateModule??=await import("@stacksjs/auth/gate"),user=this._authenticatedUser??null;await Gate.authorize(ability,user,...args)}},stacksRequestPrototypes=new WeakMap,STACKS_REQUEST_ENHANCED=Symbol.for("stacks.router.requestEnhanced");function fuseRequestEnhancements(router,initializeRequestIds=!0){const original=router.enhanceRequest.bind(router),combinedPrototypes=new WeakMap;router.enhanceRequest=(request,params={})=>{const basePrototype=Object.getPrototypeOf(request),combined=combinedPrototypes.get(basePrototype);if(combined){const enhanced=request;if(enhanced.params!==params)if("params"in request)Object.defineProperty(enhanced,"params",{value:params,writable:!0,configurable:!0,enumerable:!1});else enhanced.params=params;Object.setPrototypeOf(enhanced,combined);if(initializeRequestIds&&!enhanced._requestId)enhanced._requestId=incomingRequestId(enhanced)??generateRequestId();return enhanced}const enhanced=enhanceRequest(original(request,params),initializeRequestIds);combinedPrototypes.set(basePrototype,Object.getPrototypeOf(enhanced));return enhanced}}let csrfModule,csrfModuleLoad;function loadCsrfModule(){if(csrfModule!==void 0)return csrfModule;csrfModuleLoad??=import(resolveDefaultsPath("app/Middleware/Csrf.ts")).then((mod)=>{csrfModule=mod;return csrfModule}).catch(()=>{csrfModule=null;return null});return csrfModuleLoad}export function clearCsrfModuleCache(){csrfModule=void 0;csrfModuleLoad=void 0}function applyCsrfRenderToken(req,cookieHeader,mod){const token=mod.generateCsrfToken();req._csrfToken=token;try{const merged=cookieHeader?`${cookieHeader}; ${mod.CSRF_COOKIE_NAME}=${token}`:`${mod.CSRF_COOKIE_NAME}=${token}`;req.headers.set("cookie",merged)}catch{}}function requestMayRenderHtml(req){return req.headers.get("accept")?.includes("text/html")===!0||req.headers.get("sec-fetch-dest")==="document"}function hasCsrfCookie(cookieHeader){return cookieHeader.length!==0&&(cookieHeader.startsWith("X-CSRF-Token=")||cookieHeader.includes("X-CSRF-Token=")||cookieHeader.includes("csrf-token="))}function seedCsrfTokenForRender(req,cookieHeader=req.headers?.get?.("cookie")??""){if(hasCsrfCookie(cookieHeader))return;const mod=loadCsrfModule();if(mod===null)return;if(mod instanceof Promise)return mod.then((resolved)=>{if(resolved)applyCsrfRenderToken(req,cookieHeader,resolved)});applyCsrfRenderToken(req,cookieHeader,mod)}export function enhanceRequest(req,initializeRequestId=!0){const routeParams=req.params??{};req.params=routeParams;if(initializeRequestId&&!req._requestId)req._requestId=incomingRequestId(req)??generateRequestId();if(req[STACKS_REQUEST_ENHANCED]===!0)return req;applyRequestEnhancements(req,routeParams);const routerPrototype=Object.getPrototypeOf(req);let stacksPrototype=stacksRequestPrototypes.get(routerPrototype);if(!stacksPrototype){stacksPrototype=Object.assign(Object.create(routerPrototype),REQUEST_METHODS);Object.defineProperty(stacksPrototype,STACKS_REQUEST_ENHANCED,{value:!0});stacksRequestPrototypes.set(routerPrototype,stacksPrototype)}Object.setPrototypeOf(req,stacksPrototype);return req}const INVALID_REQUEST_ID_CHARACTER=/[^\w.:-]/;function incomingRequestId(req){try{const supplied=req.headers.get("x-request-id")?.trim();if(supplied&&supplied.length>=8&&supplied.length<=200&&!INVALID_REQUEST_ID_CHARACTER.test(supplied))return supplied}catch{}return}const REQUEST_ID_DIGITS="0123456789abcdefghijklmnopqrstuvwxyz",REQUEST_ID_SUFFIX_COUNT=36;function createRequestIdGenerator(){const prefix=crypto.randomUUID().replaceAll("-","").slice(0,16);let block=0,suffix=0,blockPrefix=`${prefix}-0`;return()=>{suffix++;if(suffix===REQUEST_ID_SUFFIX_COUNT){suffix=0;blockPrefix=`${prefix}-${(++block).toString(36)}`}return blockPrefix+REQUEST_ID_DIGITS[suffix]}}let generateRequestId=()=>{generateRequestId=createRequestIdGenerator();return generateRequestId()};function createHandlerErrorResponse(error,req,handlerPath,router){if(router?.errorHandler)return Promise.reject(error);report(error,{label:`[Router] ${handlerPath}`});const rawStatus=error?.statusCode??error?.status,status=typeof rawStatus==="number"&&Number.isInteger(rawStatus)&&rawStatus>=400&&rawStatus<600?rawStatus:void 0;return createErrorResponse(error instanceof Error?error:Error(String(error)),req,{handlerPath,status})}function wrapHandler(handler,skipParsing=!1,handlerKey=""){if(isRouterAction(handler))return wrapAction(handler,handlerKey);if(typeof handler==="string"){const handlerPath=handler;return async(req)=>{try{if(!skipParsing){await parseRequestBody(req);req=enhanceRequest(req)}const pending=resolveStringHandler(handlerPath);return await(pending instanceof Promise?await pending:pending)(req)}catch(error){return await createHandlerErrorResponse(error,req,handlerPath)}}}const fn=handler;return(req)=>{const result=fn(req);if(result instanceof Promise)return result.then((value)=>formatResult(value,req));return formatResult(result,req)}}async function parseRequestBody(req){if(req._bodyParsed)return;req._bodyParsed=!0;const contentType=req.headers.get("content-type")||"";try{if(contentType==="application/json"||JSON_CONTENT_TYPE.test(contentType)){const raw=await req.text();req._rawBody=raw;req._rawBodyContentType=contentType;if(raw.length===0)req.jsonBody={};else try{const body=JSON.parse(raw);req.jsonBody=body&&typeof body==="object"?body:{}}catch(parseErr){const message=parseErr instanceof Error?parseErr.message:"Invalid JSON",{HttpError}=routerHttpErrorModule??=await import("@stacksjs/error-handling/http-error");throw new HttpError(400,`Invalid JSON body: ${message}`)}}else if(contentType.includes("application/x-www-form-urlencoded")){const text=await req.clone().text(),params=new URLSearchParams(text),formBody={};params.forEach((value,key)=>{formBody[key]=value});req.formBody=formBody}else if(contentType.includes("multipart/form-data")){const formData=await req.clone().formData(),formBody={},files={};formData.forEach((value,key)=>{if(value instanceof File)if(files[key])if(Array.isArray(files[key]))files[key].push(value);else files[key]=[files[key],value];else files[key]=value;else formBody[key]=value});Reflect.set(req,"formBody",formBody);Reflect.set(req,"files",files)}}catch(e){if(typeof(e?.status??e?.statusCode)==="number")throw e;log.debug("[stacks-router] Body parsing failed:",e)}}let signedStorageModule;export function createStacksRouter(config={}){const bunRouter=new Router({verbose:config.verbose??!1});if(config.autoDiscoverRoutes===!1)bunRouter._apiRoutesInitialized=!0;const requestContextEnabled=config.requestContext!==!1;setAmbientRequestContext(requestContextEnabled);if(!requestContextEnabled)routersWithoutRequestContext.add(bunRouter);const csrfEnabled=config.csrf!==!1;if(csrfEnabled)csrfEnabledNativeRouteRouters.add(bunRouter);fuseRequestEnhancements(bunRouter,config.requestIds!==!1);let currentPrefix="",currentGroupMiddleware=[],currentGroupApiResponse=!1;const routeStates=new Map;function registerRoute(method,path,_handler){const fullPath=currentPrefix+path,routeKey=`${method}:${fullPath}`,handlerKind=_handler instanceof Response?"static response":typeof _handler==="string"?_handler:"function";log.debug(`[router] ${method} ${fullPath} \u2192 ${handlerKind}`);const shadowed=routeStates.has(routeKey)||clearedRouteKeysByRegistry.get(routeStates)?.has(routeKey)===!0;if(!shadowed){const routeState={csrfMode:CSRF_DEFAULT};if(currentGroupMiddleware.length>0)routeState.middleware=[...currentGroupMiddleware];routeStates.set(routeKey,routeState);routeStateRegistries.add(routeStates)}if(!shadowed&&currentGroupApiResponse)(routeApiResponseRegistry??=new Set).add(routeKey);if(!shadowed&&typeof _handler==="string")routeHandlerKeyRegistry.set(routeKey,_handler);if(!shadowed&&isRouterAction(_handler))(routeActionRegistry??=new Map).set(routeKey,_handler);return{fullPath,routeKey,shadowed}}const stacksRouter={bunRouter,get routes(){return bunRouter.routes},staticResponse(method,path,response){if(!STATIC_RESPONSE_METHODS.has(method))throw TypeError(`[Router] staticResponse(): unsupported HTTP method '${String(method)}'`);if(!(response instanceof Response))throw TypeError("[Router] staticResponse(): response must be a prebuilt Response");if(currentGroupMiddleware.length>0)throw Error("[Router] staticResponse() cannot be registered inside a group with middleware because static responses bypass middleware");if(currentGroupApiResponse)throw Error("[Router] staticResponse() cannot be registered inside a group with apiResponse because static responses bypass response formatting");const{fullPath,shadowed}=registerRoute(method,path,response);if(!shadowed)switch(method){case"GET":bunRouter.get(fullPath,response);break;case"POST":bunRouter.post(fullPath,response);break;case"PUT":bunRouter.put(fullPath,response);break;case"PATCH":bunRouter.patch(fullPath,response);break;case"DELETE":bunRouter.delete(fullPath,response);break;case"OPTIONS":bunRouter.options(fullPath,response);break}return stacksRouter},get(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("GET",path,handler);bunRouter.get(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},post(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("POST",path,handler);bunRouter.post(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},put(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("PUT",path,handler);bunRouter.put(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},patch(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("PATCH",path,handler);bunRouter.patch(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},delete(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("DELETE",path,handler);bunRouter.delete(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},options(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("OPTIONS",path,handler);bunRouter.options(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},group(options,callback){const previousPrefix=currentPrefix,previousMiddleware=[...currentGroupMiddleware],previousApiResponse=currentGroupApiResponse,restoreGroupState=()=>{currentPrefix=previousPrefix;currentGroupMiddleware=previousMiddleware;currentGroupApiResponse=previousApiResponse};if(options.prefix)currentPrefix=previousPrefix+options.prefix;const middlewareList=options.middleware?Array.isArray(options.middleware)?options.middleware:[options.middleware]:void 0;if(middlewareList)currentGroupMiddleware=[...currentGroupMiddleware,...middlewareList];if(options.apiResponse===!0)currentGroupApiResponse=!0;log.debug(`[router] Entering group: prefix=${options.prefix||"/"} middleware=[${middlewareList?.join(", ")||""}]${currentGroupApiResponse?" apiResponse=true":""}`);let result;try{result=callback()}catch(error){restoreGroupState();throw error}if(result instanceof Promise)return result.then(()=>stacksRouter).finally(restoreGroupState);restoreGroupState();return stacksRouter},resource(name,handler,options){const actions=["index","store","show","update","destroy"],activeActions=options?.only?actions.filter((a)=>options.only.includes(a)):options?.except?actions.filter((a)=>!options.except.includes(a)):actions,stripped=handler.replace(/Action$/,""),handlerBase=stripped.startsWith("Actions/")?stripped:`Actions/${stripped}`;log.debug(`[router] Resource: /${name} \u2192 ${handlerBase}*Action [${activeActions.join(", ")}]`);const sibling=(suffix)=>`${handlerBase}${suffix}`,registerResourceRoutes=()=>{for(const action of activeActions)switch(action){case"index":stacksRouter.get(`/${name}`,sibling("IndexAction"));break;case"store":stacksRouter.post(`/${name}`,sibling("StoreAction"));break;case"show":stacksRouter.get(`/${name}/:id`,sibling("ShowAction"));break;case"update":stacksRouter.put(`/${name}/:id`,sibling("UpdateAction"));break;case"destroy":stacksRouter.delete(`/${name}/:id`,sibling("DestroyAction"));break}};if(options?.middleware)stacksRouter.group({middleware:options.middleware},registerResourceRoutes);else registerResourceRoutes();return stacksRouter},match(methods,path,handler){log.debug(`[router] Match: [${methods.join(", ")}] ${path} \u2192 ${typeof handler==="string"?handler:"function"}`);let firstShadowed=!1;for(const[index,method]of methods.entries()){const m=method.toUpperCase(),{fullPath,routeKey,shadowed}=registerRoute(m,path,handler);if(index===0)firstShadowed=shadowed;const wrappedHandler=createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled);switch(m){case"GET":bunRouter.get(fullPath,wrappedHandler);break;case"POST":bunRouter.post(fullPath,wrappedHandler);break;case"PUT":bunRouter.put(fullPath,wrappedHandler);break;case"PATCH":bunRouter.patch(fullPath,wrappedHandler);break;case"DELETE":bunRouter.delete(fullPath,wrappedHandler);break;case"OPTIONS":bunRouter.options(fullPath,wrappedHandler);break}}return createChainableRoute(routeStates,`${methods[0]}:${currentPrefix}${path}`,firstShadowed)},health(){bunRouter.get("/api/health",async()=>{const{checkApplicationHealth}=await import("./health"),health=await checkApplicationHealth();return Response.json(health,{status:health.status==="healthy"?200:503})});bunRouter.get("/__routes",(req)=>{if(!isExposeRoutesAuthorized(req))return Response.json({error:"disabled"},{status:404});return Response.json(listRegisteredRoutes())});bunRouter.get("/__storage/{path}",async(req)=>{const url=new URL(req.url),token=url.searchParams.get("token"),params=req.params,rawPath=params?.path?params.path:decodeURIComponent(url.pathname.replace(/^\/__storage\//,""));if(!token||typeof rawPath!=="string"||rawPath.length===0)return new Response("Forbidden",{status:403});const{verifySignedStorageToken,Storage}=signedStorageModule??=await import("@stacksjs/storage");if(!verifySignedStorageToken(token,rawPath).valid)return new Response("Forbidden",{status:403});try{const adapter=Storage.disk();if(!await adapter.fileExists(rawPath))return new Response("Not Found",{status:404});const buf=await adapter.readToBuffer(rawPath),mime=await adapter.mimeType(rawPath).catch(()=>"application/octet-stream");return new Response(buf,{status:200,headers:{"Content-Type":mime,"Cache-Control":"private, max-age=60","X-Content-Type-Options":"nosniff"}})}catch(err){log.error("[storage] signed-url fetch failed:",err);return new Response("Internal Error",{status:500})}});bunRouter.get("/__openapi.json",async(req)=>{if(!isExposeRoutesAuthorized(req))return Response.json({error:"disabled"},{status:404});try{const{generateOpenApi}=await import("@stacksjs/api"),spec=await generateOpenApi({write:!1,portable:!1});return Response.json(spec)}catch(err){return Response.json({error:"OpenAPI generation failed",message:err instanceof Error?err.message:String(err)},{status:500})}});return stacksRouter},use(middleware){const adapted=adaptMiddlewareForBunRouter(middleware);bunRouter.globalMiddleware.push(adapted);return stacksRouter},booting(name,run){bootHooks.push({name,run});return stacksRouter},async serve(options={}){warnOnMultipleRouterInstances();configureViewDirectories(bunRouter);if(csrfEnabled)wrapHandleRequestForCsrf(bunRouter);await wrapHandleRequestForDatabaseContext(bunRouter);await runBootHooks();const production=(process.env.APP_ENV??"").toLowerCase()==="production"||process.env.NODE_ENV==="production",callerSuppliedRoutes=Object.prototype.hasOwnProperty.call(options,"routes"),nativeRoutes=options.nativeRoutes??(production&&!callerSuppliedRoutes&&shouldUseNativeRoutesByDefault(bunRouter.routes));if(options.tls)secureNativeRouteRouters.add(bunRouter);else secureNativeRouteRouters.delete(bunRouter);return bunRouter.serve(nativeRoutes===options.nativeRoutes?options:{...options,nativeRoutes})},handleRequest(req){return bunRouter.handleRequest(req)},getAllowedMethods(pathname,domain){return bunRouter.getAllowedMethods(pathname,domain)},async register(routePath,options){log.debug(`[router] Register: ${routePath} prefix=${options?.prefix||"none"}`);const callback=async()=>{await import(routePath)};if(options?.prefix||options?.middleware)await stacksRouter.group({prefix:options.prefix,middleware:options.middleware},callback);else await callback();return stacksRouter},async importRoutes(){log.debug("[router] Loading user routes from registry...");try{const{appRouteRegistry,loadRoutes}=await import("./route-loader");await loadRoutes(await appRouteRegistry())}catch(error){log.error("Failed to load route registry:",error);throw error}log.debug("[router] Loading ORM routes...");const ormRoutesPackage="@stacksjs/orm/routes";let ormRoutesLoaded=!1;try{await import(ormRoutesPackage);ormRoutesLoaded=!0;log.debug(`[router] ORM routes loaded from ${ormRoutesPackage}`)}catch(error){log.debug(`[router] ORM routes not available from the package, trying the vendored copy
4
+ ${paths}`)}return!0}let __defaultsPkgRoot,__defaultsPathCache;function resolveDefaultsPath(rel){const cached=__defaultsPathCache?.get(rel);if(cached!==void 0)return cached;if(rel.startsWith("app/")){const published=appPath(rel.slice(4));if(existsSync(published)){(__defaultsPathCache??=new Map).set(rel,published);return published}}const vendored=storagePath(`framework/defaults/${rel}`);let resolved;if(existsSync(vendored))resolved=vendored;else{if(__defaultsPkgRoot===void 0)try{const pkgJson=Bun.resolveSync("@stacksjs/defaults/package.json",process.cwd());__defaultsPkgRoot=pkgJson.slice(0,pkgJson.lastIndexOf("/"))}catch{__defaultsPkgRoot=null}resolved=__defaultsPkgRoot?`${__defaultsPkgRoot}/${rel}`:vendored}(__defaultsPathCache??=new Map).set(rel,resolved);return resolved}const NATIVE_ROUTE_PARAM=/^\{[A-Z_$][\w$]*\}$/i;function isNativeRouteMethod(method){return method==="GET"||method==="POST"||method==="PUT"||method==="PATCH"||method==="DELETE"||method==="OPTIONS"||method==="HEAD"}function hasNativeCompatiblePath(path){if(path==="*")return!0;const segments=path.split("/");for(let index=0;index<segments.length;index++){const segment=segments[index];if(segment===""||NATIVE_ROUTE_PARAM.test(segment))continue;if(segment==="*"&&index===segments.length-1)continue;if(segment.includes("{")||segment.includes(":")||segment.includes("*"))return!1}return!0}export function shouldUseNativeRoutesByDefault(routes){let hasEligibleRoute=!1;for(const route of routes){if(!isNativeRouteMethod(route.method)||route.handler instanceof Response)continue;hasEligibleRoute=!0;if(route.nativeDispatch===!1||route.domain||route.constraints&&Object.keys(route.constraints).length>0||!hasNativeCompatiblePath(route.path))return!1}return hasEligibleRoute}import{runWithRequestArgument,runWithRequestArguments,setAmbientRequestContext}from"./request-context";import{isApiRequest,JSON_CONTENT_TYPE}from"./api-shape";import{applySecurityHeaders,createJsonSecurityHeaders,secureSerializedJsonResponse}from"./security-headers";import{isCursorPaginator,isPaginator,isSimplePaginator}from"@stacksjs/pagination";let errorHandlerModule,errorHandlerModuleLoad;async function loadErrorHandlerModule(){return errorHandlerModule??=await(errorHandlerModuleLoad??=import("./error-handler"))}async function createErrorResponse(...args){return(await loadErrorHandlerModule()).createErrorResponse(...args)}async function createMiddlewareErrorResponse(...args){return(await loadErrorHandlerModule()).createMiddlewareErrorResponse(...args)}function bindAsyncRouteHandler(handler){return(req,preparedBaseResult,hasPreparedBaseResult=!1)=>runWithRequestArguments(req,handler,req,preparedBaseResult,hasPreparedBaseResult)}const stacksRouteHandlers=new WeakSet;function rememberStacksRouteHandler(handler){stacksRouteHandlers.add(handler);return handler}function invokeInlineHandler(req,handler,_unused){const result=handler(req);if(result instanceof Promise)return result.then((value)=>formatResult(value,req));return formatResult(result,req)}const STATIC_RESPONSE_METHODS=new Set(["GET","POST","PUT","PATCH","DELETE","OPTIONS"]),CSRF_DEFAULT=0,CSRF_SKIPPED=1,CSRF_REQUIRED=2,ROUTE_ACCEPTS_CSRF=1,ROUTE_MAY_HAVE_BODY=2,ROUTE_RENDERS_CSRF=4,ROUTE_SEEDS_CSRF=8,ROUTE_FORCES_JSON=16,ROUTE_ACTION_SKIPS_CSRF=32,routeStateRegistries=new Set,clearedRouteKeysByRegistry=new WeakMap;function rateLimitWindowToSeconds(window){if(typeof window==="number"){if(!Number.isFinite(window)||window<=0)throw Error(`[Router] .rateLimit(): window must be a positive number of seconds, got ${window}`);return Math.floor(window)}switch(window){case"second":return 1;case"minute":return 60;case"hour":return 3600;case"day":return 86400;default:throw Error(`[Router] .rateLimit(): unknown period '${String(window)}'`)}}class BoundedMap{max;map;constructor(max){this.max=max}get(key){return this.map?.get(key)}has(key){return this.map?.has(key)??!1}set(key,value){const map=this.map??=new Map;if(map.has(key))map.delete(key);map.set(key,value);if(map.size>this.max){const oldest=map.keys().next().value;if(oldest!==void 0)map.delete(oldest)}return this}delete(key){return this.map?.delete(key)??!1}clear(){this.map=void 0}get size(){return this.map?.size??0}}function isExposeRoutesAuthorized(req){const flag=process.env.STACKS_EXPOSE_ROUTES??"";if(!flag)return!((process.env.APP_ENV??"").toLowerCase()==="production"||process.env.NODE_ENV==="production");if(flag==="1")return!((process.env.APP_ENV??"").toLowerCase()==="production"||process.env.NODE_ENV==="production");const url=new URL(req.url),submitted=req.headers.get("x-stacks-routes-token")||req.headers.get("X-Stacks-Routes-Token")||url.searchParams.get("token")||"";if(typeof submitted!=="string"||submitted.length===0||submitted.length!==flag.length)return!1;try{const encoder=new TextEncoder;return crypto.timingSafeEqual(encoder.encode(submitted),encoder.encode(flag))}catch{return!1}}let corsHeaderApplierCache={},compressionApplierCache={};function applyCorsIfConfigured(req,response){if(!req._corsConfig||!response)return response;const cache=corsHeaderApplierCache;if(!cache.apply)return loadAndApplyCorsHeaders(req,response,cache);try{return cache.apply(req,response,req._corsConfig)}catch(err){log.warn("[router] CORS header injection failed",{error:err});return response}}async function loadAndApplyCorsHeaders(req,response,cache){try{return(cache.apply??=(await import(resolveDefaultsPath("app/Middleware/Cors.ts"))).applyCorsHeaders)(req,response,req._corsConfig)}catch(err){log.warn("[router] CORS header injection failed",{error:err});return response}}const ACTION_CACHE_MAX=5000,actionSkipsCsrfCache=new BoundedMap(ACTION_CACHE_MAX),routeHandlerKeyRegistry=new BoundedMap(ACTION_CACHE_MAX);let routeActionRegistry;const CSRF_SEEDED_BY_HANDLE_REQUEST=Symbol.for("stacks.router.csrfSeededByHandleRequest"),FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND=Symbol("stacks.router.frameworkResponseBodySizeUpperBound"),NATIVE_DIRECT_ROUTE_REQUEST=Symbol("stacks.router.nativeDirectRouteRequest"),CSRF_SECURE_TRANSPORT=Symbol.for("@stacksjs/router:csrf-secure-transport"),secureNativeRouteRouters=new WeakSet,csrfEnabledNativeRouteRouters=new WeakSet,routersWithoutRequestContext=new WeakSet,EMPTY_MIDDLEWARE_TIMINGS=[],EMPTY_MIDDLEWARE_ENTRIES=[],CSRF_ONLY_MIDDLEWARE=["csrf"];let resolvedMiddlewareEntryCache,resolvedCsrfOnlyMiddleware,namedRouteRegistry;function compileNamedRoute(path){const paramNames=extractRouteParamNames(path),colonRegex=new Map;for(const name of paramNames)colonRegex.set(name,new RegExp(`(^|/):${name}(?=$|/)`,"g"));return{path,paramNames,colonRegex}}function extractRouteParamNames(routePath){const names=new Set;for(const m of routePath.matchAll(/\{(\w+)\}/g))if(m[1])names.add(m[1]);for(const m of routePath.matchAll(/(?:^|\/):(\w+)(?=$|\/)/g))if(m[1])names.add(m[1]);return[...names]}export function url(routeName,params={}){const named=namedRouteRegistry?.get(routeName);if(!named){const availableRoutes=namedRouteRegistry?[...namedRouteRegistry.keys()].join(", "):"";throw Error(`Route '${routeName}' is not defined. Available routes: ${availableRoutes}`)}const missing=named.paramNames.filter((name)=>!(name in params)||params[name]===void 0);if(missing.length>0)throw Error(`url('${routeName}'): missing required path param${missing.length>1?"s":""} [${missing.join(", ")}] for path '${named.path}'. Pass them as the second argument: url('${routeName}', { ${named.paramNames.join(", ")} })`);let appUrl;try{appUrl=process.env.APP_URL||"https://localhost"}catch{appUrl="https://localhost"}appUrl=appUrl.replace(/\/$/,"");if(!appUrl.startsWith("http"))appUrl=`https://${appUrl}`;let resolvedPath=named.path;const queryParams={};for(const[key,value]of Object.entries(params)){const curly=`{${key}}`;if(resolvedPath.includes(curly))resolvedPath=resolvedPath.replaceAll(curly,encodeURIComponent(String(value)));else{const re=named.colonRegex.get(key);if(re&&re.test(resolvedPath)){re.lastIndex=0;resolvedPath=resolvedPath.replace(re,`$1${encodeURIComponent(String(value))}`)}else queryParams[key]=String(value)}}const queryString=Object.keys(queryParams).length>0?`?${new URLSearchParams(queryParams).toString()}`:"";return`${appUrl}${resolvedPath}${queryString}`}export function routeParams(routeName){const named=namedRouteRegistry?.get(routeName);return named?[...named.paramNames]:[]}export function listNamedRoutes(){const out={};if(namedRouteRegistry)for(const[name,named]of namedRouteRegistry)out[name]=named.path;return out}export function listRegisteredRoutes(){const namesByPath=namedRouteRegistry?new Map:void 0;if(namesByPath&&namedRouteRegistry){for(const[name,named]of namedRouteRegistry)if(!namesByPath.has(named.path))namesByPath.set(named.path,name)}const out=[],seen=new Set;for(const registry of routeStateRegistries)for(const key of registry.keys()){if(seen.has(key))continue;seen.add(key);const idx=key.indexOf(":");if(idx===-1)continue;const method=key.slice(0,idx),path=key.slice(idx+1),routeName=namesByPath?.get(path);out.push({method,path,name:routeName,handler:routeHandlerKeyRegistry.get(key),action:routeActionRegistry?.get(key),middleware:registry.get(key)?.middleware})}return out.sort((a,b)=>a.path.localeCompare(b.path))}const MIDDLEWARE_TIMEOUT_MS=30000;let _debugLoggingCache;function isDebugLogging(){if(_debugLoggingCache===void 0){const level=(process.env.LOG_LEVEL||"info").toLowerCase();_debugLoggingCache=level!=="info"&&level!=="warn"&&level!=="error"}return _debugLoggingCache}const DEFAULT_MIDDLEWARE_PRIORITY=10;let _warnedInvalidPriorities;function warnInvalidMiddlewarePriority(name,raw){const key=`${name}:${String(raw)}`;if(_warnedInvalidPriorities?.has(key))return;(_warnedInvalidPriorities??=new Set).add(key);log.warn(`[Router] Middleware '${name}' declared an invalid priority (${String(raw)}). Priorities must be a finite non-negative number; falling back to default ${DEFAULT_MIDDLEWARE_PRIORITY}.`)}function adaptMiddlewareForBunRouter(middleware){if(middleware&&typeof middleware==="object"&&typeof middleware.toRouterHandler==="function")return middleware.toRouterHandler();if(middleware&&typeof middleware==="object"&&typeof middleware.handle==="function"&&typeof middleware!=="function"){const handle=middleware.handle.bind(middleware);return async(req,next)=>{try{await handle(req)}catch(thrown){if(thrown instanceof Response)return thrown;throw thrown}return next()}}return middleware}let middlewareCache,middlewareAliasesPromise=null;async function getMiddlewareAliases(){if(middlewareAliasesPromise)return middlewareAliasesPromise;middlewareAliasesPromise=(async()=>{const merged={};for(const load of[()=>import(resolveDefaultsPath("app/Middleware.ts")),()=>import(appPath("Middleware.ts"))])try{const module=await load();Object.assign(merged,module.default??{})}catch{}return merged})();return middlewareAliasesPromise}const PASCAL_SPLIT_REGEX=/[-_\s]+/;let pascalCaseCache;function toPascalCase(input){if(!input)return input;const cached=pascalCaseCache?.get(input);if(cached!==void 0)return cached;const out=input.split(PASCAL_SPLIT_REGEX).filter(Boolean).map((part)=>part.charAt(0).toUpperCase()+part.slice(1)).join("");(pascalCaseCache??=new Map).set(input,out);return out}async function resolveMiddlewareName(name){const resolved=(await getMiddlewareAliases())[name]||toPascalCase(name);log.debug(`[middleware] Resolved: ${name} \u2192 ${resolved}`);return resolved}let middlewareRegistryPromise=null;async function getMiddlewareRegistry(){if(middlewareRegistryPromise)return middlewareRegistryPromise;middlewareRegistryPromise=(async()=>{try{const dir=storagePath("framework/auto-imports"),module=await import(`${dir}/middleware.ts`);if(!module.middleware)return null;const{resolve}=await import("node:path");return Object.fromEntries(Object.entries(module.middleware).map(([name,file])=>[name,resolve(dir,file)]))}catch{return null}})();return middlewareRegistryPromise}async function loadMiddleware(name){const cachedMiddleware=middlewareCache??=new Map;if(cachedMiddleware.has(name))return cachedMiddleware.get(name)??null;const className=await resolveMiddlewareName(name),registered=(await getMiddlewareRegistry())?.[className];if(registered)try{const handler=(await import(registered)).default??null;if(!handler||typeof handler.handle!=="function"){log.error(`[Router] Middleware '${name}' resolved to ${registered}, but the file has no default export with a handle() method`);cachedMiddleware.set(name,null);return null}cachedMiddleware.set(name,handler);return handler}catch(err){log.error(`[Router] Failed to load middleware '${name}' from ${registered}:`,err);return null}let userPathError;try{const userPath=appPath(`Middleware/${className}.ts`),handler=(await import(userPath)).default??null;if(!handler||typeof handler.handle!=="function"){log.error(`[Router] Middleware '${name}' resolved to ${userPath}, but the file has no default export with a handle() method`);cachedMiddleware.set(name,null);return null}cachedMiddleware.set(name,handler);return handler}catch(err){userPathError=err}try{const defaultPath=resolveDefaultsPath(`app/Middleware/${className}.ts`),handler=(await import(defaultPath)).default??null;if(!handler||typeof handler.handle!=="function"){log.error(`[Router] Middleware '${name}' resolved to ${defaultPath}, but the file has no default export with a handle() method`);cachedMiddleware.set(name,null);return null}cachedMiddleware.set(name,handler);return handler}catch(err){const userMsg=userPathError instanceof Error?userPathError.message:String(userPathError);log.error(`[Router] Failed to load middleware '${name}' (resolved to '${className}'). app/Middleware: ${userMsg}; defaults:`,err);return null}}export async function middlewareAliases(){return{...await getMiddlewareAliases()}}export async function loadMiddlewareHandlers(){const aliases=await getMiddlewareAliases(),generated=await getMiddlewareRegistry(),references=new Set([...Object.keys(aliases),...Object.values(aliases),...Object.keys(generated??{})]),handlers={};await Promise.all([...references].map(async(reference)=>{const handler=await loadMiddleware(reference);if(handler)handlers[reference]=handler}));return handlers}let negatedMiddlewareCache,routerHttpErrorModule;function isShortCircuit(thrown){if(thrown instanceof Response)return!0;return typeof thrown==="object"&&thrown!==null&&(("status"in thrown)||("statusCode"in thrown))}function negateMiddleware(name,inner,cache){const cached=cache.get(name);if(cached)return cached;const negated={priority:inner.priority,async handle(req){try{await inner.handle(req)}catch(thrown){if(isShortCircuit(thrown))return;throw thrown}const{HttpError}=routerHttpErrorModule??=await import("@stacksjs/error-handling/http-error");throw new HttpError(403,`Access denied. This route requires "${name}" not to apply.`)}};cache.set(name,negated);return negated}function loadParsedMiddleware(parsed){const negatedCache=parsed.negated?negatedMiddlewareCache??=new Map:void 0,cached=middlewareCache?.get(parsed.name);if(cached!==void 0)return cached&&negatedCache?negateMiddleware(parsed.name,cached,negatedCache):cached;return loadMiddleware(parsed.name).then((handler)=>handler&&negatedCache?negateMiddleware(parsed.name,handler,negatedCache):handler)}export function clearMiddlewareCache(){corsHeaderApplierCache={};compressionApplierCache={};middlewareCache=void 0;negatedMiddlewareCache=void 0;resolvedMiddlewareEntryCache=void 0;resolvedCsrfOnlyMiddleware=void 0;parsedMiddlewareCache=void 0;middlewareAliasesPromise=null;middlewareRegistryPromise=null;actionRegistryPromise=null;actionSkipsCsrfCache.clear();routeHandlerKeyRegistry.clear();routeActionRegistry=void 0;clearCsrfModuleCache()}export function installMiddlewareHotReload(){if(process.env.APP_ENV==="production"||process.env.NODE_ENV==="production")return()=>{};let fsWatchers=[];(async()=>{try{const fs=await import("node:fs"),targets=[appPath("Middleware"),appPath("Middleware.ts")];for(const target of targets)try{if(!fs.existsSync(target))continue;const w=fs.watch(target,{recursive:!0},()=>{log.debug("[middleware] hot-reload: clearing cache");clearMiddlewareCache()});fsWatchers.push(w)}catch{}}catch{}})();return()=>{for(const w of fsWatchers)try{w.close()}catch{}fsWatchers=[]}}export function clearRouteMiddlewareRegistry(){for(const registry of routeStateRegistries){const registeredKeys=clearedRouteKeysByRegistry.get(registry)??new Set;for(const routeKey of registry.keys())registeredKeys.add(routeKey);clearedRouteKeysByRegistry.set(registry,registeredKeys);for(const state of registry.values())if(state.middleware)state.middleware.length=0;registry.clear()}routeStateRegistries.clear()}let routeApiResponseRegistry,parsedMiddlewareCache;function parseMiddlewareEntry(middleware){const cache=parsedMiddlewareCache??=new Map;let parsed=cache.get(middleware);if(!parsed){parsed=parseMiddlewareEntryUncached(middleware).then((resolved)=>{cache.set(middleware,resolved);return resolved},(error)=>{cache.delete(middleware);throw error});cache.set(middleware,parsed)}return parsed}async function parseMiddlewareEntryUncached(middleware){const timingName=middleware.replace(/[^A-Za-z0-9_-]/g,"_").slice(0,32),negated=middleware.startsWith("!"),bare=negated?middleware.slice(1):middleware,aliases=await getMiddlewareAliases();if(Object.hasOwn(aliases,bare))return{name:bare,negated,timingName};const colonIndex=bare.indexOf(":");if(colonIndex===-1)return{name:bare,negated,timingName};return{name:bare.substring(0,colonIndex),negated,timingName,params:bare.substring(colonIndex+1)}}export async function findUnresolvableRouteMiddleware(){const usage=new Map;for(const registry of routeStateRegistries)for(const[routeKey,state]of registry){const entries=state.middleware;if(!entries)continue;for(const entry of entries){const pending=parseMiddlewareEntry(entry),parsed=pending instanceof Promise?await pending:pending,alias=parsed.negated?`!${parsed.name}`:parsed.name,seen=usage.get(alias)??{parsed,routes:[]};seen.routes.push(routeKey);usage.set(alias,seen)}}if(!usage.has("csrf"))usage.set("csrf",{parsed:{name:"csrf",negated:!1,timingName:"csrf"},routes:["(auto-injected on POST/PUT/PATCH/DELETE)"]});const unresolvable=[];for(const[alias,{parsed,routes}]of usage){const handler=await loadParsedMiddleware(parsed);if(!handler||typeof handler.handle!=="function")unresolvable.push({alias,routes})}return unresolvable}export async function assertRouteMiddlewareResolvable(){const unresolvable=await findUnresolvableRouteMiddleware();if(unresolvable.length===0)return;const detail=unresolvable.map((u)=>`"${u.alias}" (used by ${u.routes.join(", ")})`).join("; ");throw Error(`[Router] Unresolvable middleware alias(es): ${detail}. Check the alias map in app/Middleware.ts or add app/Middleware/<Class>.ts.`)}function finishSynchronousResult(req,response,csrfHandledByOuter,routeSeedsCsrf){const frameworkMetadataApplied=typeof response[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND]==="number";if(response.status>=400||req._corsConfig||req._compress===!0||req._startNs!=null||Array.isArray(req._afterResponse)||req._responseHeaders)return;if(frameworkMetadataApplied&&!routeSeedsCsrf)return response;let finalized=response;if(routeSeedsCsrf&&!csrfHandledByOuter&&req[CSRF_SEEDED_BY_HANDLE_REQUEST]!==!0){const mod=loadCsrfModule();if(mod instanceof Promise)return;if(mod)finalized=mod.seedCsrfCookieIfMissing(req,finalized,req._csrfToken,frameworkMetadataApplied)}if(!frameworkMetadataApplied)try{const requestId=req._requestId;if(requestId)finalized.headers.set("X-Request-ID",requestId);applySecurityHeaders(finalized.headers)}catch{return}return finalized}function routeCapabilityFlags(routeKey,handler,csrfEnabled){const routeMethod=routeKey.slice(0,routeKey.indexOf(":")).toUpperCase(),action=isRouterAction(handler)?handler:void 0,forcesJson=routeApiResponseRegistry?.has(routeKey)===!0||action?.apiResponse===!0;let flags=0;if(csrfEnabled&&(routeMethod==="POST"||routeMethod==="PUT"||routeMethod==="PATCH"||routeMethod==="DELETE"))flags|=ROUTE_ACCEPTS_CSRF;if(routeMethod!=="GET"&&routeMethod!=="HEAD")flags|=ROUTE_MAY_HAVE_BODY;if(csrfEnabled&&!forcesJson&&(routeMethod==="GET"||routeMethod==="HEAD"))flags|=ROUTE_RENDERS_CSRF;if(csrfEnabled&&(routeMethod==="GET"||routeMethod==="HEAD"||routeMethod==="OPTIONS"))flags|=ROUTE_SEEDS_CSRF;if(forcesJson)flags|=ROUTE_FORCES_JSON;if(action&&(action.skipCsrf===!0||action.csrf===!1))flags|=ROUTE_ACTION_SKIPS_CSRF;return flags}let routeRateLimitModule;function createMiddlewareHandler(router,routeStates,routeKey,handler,csrfEnabled=!0){const routeState=routeStates.get(routeKey),routeFlags=routeCapabilityFlags(routeKey,handler,csrfEnabled),handlerKey=typeof handler==="string"?handler:void 0,synchronousInlineHandler=typeof handler==="function"&&handler.constructor.name!=="AsyncFunction"?handler:void 0,asynchronousInlineHandler=typeof handler==="function"&&handler.constructor.name==="AsyncFunction"?handler:void 0,wrappedBase=typeof handler==="function"?void 0:wrapHandler(handler,!0,routeKey),handleAsyncInContext=async(req,preparedBaseResult,hasPreparedBaseResult=!1)=>{if((routeFlags&ROUTE_MAY_HAVE_BODY)!==0)try{await parseRequestBody(req)}catch(err){const error=err instanceof Error?err:Error(String(err));return createMiddlewareErrorResponse(error,req)}const enhancedReq=req,csrfHandledByOuter=enhancedReq[CSRF_SEEDED_BY_HANDLE_REQUEST]===!0;if(!csrfHandledByOuter&&(routeFlags&ROUTE_RENDERS_CSRF)!==0&&requestMayRenderHtml(enhancedReq)){const renderTokenSeeding=seedCsrfTokenForRender(enhancedReq);if(renderTokenSeeding)await renderTokenSeeding}if(handlerKey&&(routeFlags&ROUTE_ACCEPTS_CSRF)!==0&&actionSkipsCsrfCache.get(handlerKey)===void 0){const pending=resolveStringHandler(handlerKey);if(pending instanceof Promise)try{await pending}catch{}}if((routeFlags&ROUTE_FORCES_JSON)!==0)req._forceJson=!0;if(!hasPreparedBaseResult&&synchronousInlineHandler&&(routeFlags&ROUTE_ACCEPTS_CSRF)===0&&!routeState?.middleware?.length&&!routeState?.rateLimit){try{preparedBaseResult=runWithRequestArguments(enhancedReq,invokeInlineHandler,enhancedReq,synchronousInlineHandler,void 0)}catch(error){preparedBaseResult=createHandlerErrorResponse(error,enhancedReq,routeKey,router)}hasPreparedBaseResult=!0;if(preparedBaseResult instanceof Response){const finished=finishSynchronousResult(enhancedReq,preparedBaseResult,csrfHandledByOuter,(routeFlags&ROUTE_SEEDS_CSRF)!==0);if(finished)return finished}}const rl=routeState?.rateLimit;if(rl)try{const{rateLimit:enforceRateLimit}=routeRateLimitModule??=await import("./rate-limit");await enforceRateLimit(routeKey,rl.max).over(rl.windowSeconds)}catch(err){return createMiddlewareErrorResponse(err,req)}const userMiddleware=routeState?.middleware??EMPTY_MIDDLEWARE_ENTRIES;let shouldInjectCsrf=!1;if((routeFlags&ROUTE_ACCEPTS_CSRF)!==0){const alreadyHasCsrf=userMiddleware.some((m)=>m==="csrf"||m.startsWith("csrf:")),routeCsrfMode=routeState?.csrfMode??CSRF_DEFAULT,routeSkipped=routeCsrfMode===CSRF_SKIPPED,routeRequired=routeCsrfMode===CSRF_REQUIRED,actionSkipped=(routeFlags&ROUTE_ACTION_SKIPS_CSRF)!==0||(handlerKey?actionSkipsCsrfCache.get(handlerKey)===!0:!1);shouldInjectCsrf=!alreadyHasCsrf&&(routeRequired||!routeSkipped&&!actionSkipped)}const middlewareEntries=shouldInjectCsrf?userMiddleware.length===0?CSRF_ONLY_MIDDLEWARE:["csrf",...userMiddleware]:userMiddleware;let middlewareTimings=EMPTY_MIDDLEWARE_TIMINGS;if(middlewareEntries.length>0){if(isDebugLogging()){const schemeEnd=req.url.indexOf("://"),pathStart=schemeEnd===-1?0:req.url.indexOf("/",schemeEnd+3),q=req.url.indexOf("?",pathStart<0?0:pathStart),urlPath=pathStart<0?"/":req.url.slice(pathStart,q===-1?void 0:q),routeMethod=routeKey.slice(0,routeKey.indexOf(":")).toUpperCase();log.debug(`[middleware] Executing chain: [${middlewareEntries.join(", ")}] for ${routeMethod} ${urlPath}`)}const cachedCsrfOnlyMiddleware=middlewareEntries===CSRF_ONLY_MIDDLEWARE?resolvedCsrfOnlyMiddleware:void 0,resolved=cachedCsrfOnlyMiddleware??[],resolvedCache=resolvedMiddlewareEntryCache??=new Map,unresolvedEntries=cachedCsrfOnlyMiddleware?EMPTY_MIDDLEWARE_ENTRIES:middlewareEntries;for(const middlewareEntry of unresolvedEntries){let resolvedEntry=resolvedCache.get(middlewareEntry);if(!resolvedEntry){const pending=parseMiddlewareEntry(middlewareEntry),parsed=pending instanceof Promise?await pending:pending,loaded=loadParsedMiddleware(parsed),middleware=loaded instanceof Promise?await loaded:loaded;if(middleware&&typeof middleware.handle==="function"){const rawPriority=middleware.priority;let priority=DEFAULT_MIDDLEWARE_PRIORITY;if(typeof rawPriority==="number"&&Number.isFinite(rawPriority)&&rawPriority>=0)priority=rawPriority;else if(rawPriority!==void 0)warnInvalidMiddlewarePriority(middlewareEntry,rawPriority);resolvedEntry={name:middlewareEntry,timingName:parsed.timingName,handler:middleware,priority,parameterName:parsed.params?parsed.name:void 0,params:parsed.params};resolvedCache.set(middlewareEntry,resolvedEntry)}}if(!resolvedEntry){log.error(`[Router] Middleware '${middlewareEntry}' on ${routeKey} could not be resolved - failing closed`);const failClosedError=Error(`Middleware '${middlewareEntry}' could not be resolved`),failClosedResponse=await createErrorResponse(failClosedError,enhancedReq,{status:500});return await applyCorsIfConfigured(enhancedReq,failClosedResponse)}if(resolvedEntry.params&&resolvedEntry.parameterName){enhancedReq._middlewareParams=enhancedReq._middlewareParams||{};enhancedReq._middlewareParams[resolvedEntry.parameterName]=resolvedEntry.params}resolved.push(resolvedEntry)}if(middlewareEntries===CSRF_ONLY_MIDDLEWARE&&!cachedCsrfOnlyMiddleware&&resolved.length===1&&resolvedCache===resolvedMiddlewareEntryCache)resolvedCsrfOnlyMiddleware=resolved;if(resolved.length>1)resolved.sort((a,b)=>a.priority-b.priority);const collectsMiddlewareTimings=enhancedReq._startNs!=null;if(collectsMiddlewareTimings)middlewareTimings=[];let chainTimer,chainBudget,runningMiddleware="";try{for(const{name:middlewareName,timingName,handler:middleware}of resolved){const mwStart=collectsMiddlewareTimings?performance.now():0;runningMiddleware=middlewareName;try{const outcome=middleware.handle(enhancedReq);if(outcome&&typeof outcome.then==="function"){if(!chainBudget){chainBudget=new Promise((_,reject)=>{chainTimer=setTimeout(()=>reject(Error(`Middleware '${runningMiddleware}' exceeded ${MIDDLEWARE_TIMEOUT_MS}ms`)),MIDDLEWARE_TIMEOUT_MS)});chainBudget.catch(()=>{})}await Promise.race([outcome,chainBudget])}if(collectsMiddlewareTimings){const elapsedMs=performance.now()-mwStart;middlewareTimings.push({name:timingName,ms:elapsedMs})}}catch(error){if(collectsMiddlewareTimings){const elapsedMs=performance.now()-mwStart;middlewareTimings.push({name:timingName,ms:elapsedMs})}log.debug(`[middleware] Blocked by: ${middlewareName}`);if(error instanceof Response){try{const{_requestId:reqId,_startNs:startNs}=enhancedReq,total=startNs!=null?Number(process.hrtime.bigint()-startNs)/1e6:null,parts=total!=null?[`total;dur=${total.toFixed(1)}`]:[];for(const t of middlewareTimings)parts.push(`mw_${t.name};dur=${t.ms.toFixed(1)}`);if(parts.length>0)error.headers.set("Server-Timing",parts.join(", "));if(reqId)error.headers.set("X-Request-ID",reqId)}catch{}return await applyCorsIfConfigured(enhancedReq,error)}const err=error instanceof Error?error:Error(String(error)),errorResponse="statusCode"in err||"status"in err?await createMiddlewareErrorResponse(err,enhancedReq):await(()=>{log.error(`[Router] Middleware '${middlewareName}' threw an unexpected error:`,err);return createErrorResponse(err,enhancedReq,{status:500})})();try{const{_requestId:reqId,_startNs:startNs}=enhancedReq,total=startNs!=null?Number(process.hrtime.bigint()-startNs)/1e6:null,parts=total!=null?[`total;dur=${total.toFixed(1)}`]:[];for(const t of middlewareTimings)parts.push(`mw_${t.name};dur=${t.ms.toFixed(1)}`);if(parts.length>0)errorResponse.headers.set("Server-Timing",parts.join(", "));if(reqId)errorResponse.headers.set("X-Request-ID",reqId)}catch{}return await applyCorsIfConfigured(enhancedReq,errorResponse)}}}finally{if(chainTimer)clearTimeout(chainTimer)}}const formatsResolvedInlineResult=asynchronousInlineHandler!==void 0&&!hasPreparedBaseResult;let response;try{const baseResult=hasPreparedBaseResult?preparedBaseResult:asynchronousInlineHandler?asynchronousInlineHandler(enhancedReq):synchronousInlineHandler?invokeInlineHandler(enhancedReq,synchronousInlineHandler,void 0):wrappedBase(enhancedReq),resolvedResult=baseResult instanceof Response?baseResult:await baseResult;response=formatsResolvedInlineResult?formatResult(resolvedResult,enhancedReq):resolvedResult}catch(error){response=await createHandlerErrorResponse(error,enhancedReq,handlerKey||routeKey,router)}if(response){if((routeFlags&ROUTE_SEEDS_CSRF)!==0&&!csrfHandledByOuter&&enhancedReq[CSRF_SEEDED_BY_HANDLE_REQUEST]!==!0)try{const mod=loadCsrfModule(),csrf=mod instanceof Promise?await mod:mod;if(csrf)response=csrf.seedCsrfCookieIfMissing(enhancedReq,response,enhancedReq._csrfToken,typeof response[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND]==="number")}catch(err){log.warn("[router] CSRF cookie seeding failed",{error:err})}}if(response&&enhancedReq._corsConfig){const corsResponse=applyCorsIfConfigured(enhancedReq,response);response=corsResponse instanceof Response?corsResponse:await corsResponse}const{_requestId:reqId,_startNs:startNs}=enhancedReq,durMs=startNs!=null?Number(process.hrtime.bigint()-startNs)/1e6:null,{_afterResponse:after,_responseHeaders:requested}=enhancedReq,frameworkMetadataApplied=response&&typeof response[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND]==="number";if(response&&(response.status>=400||Array.isArray(after)||requested&&typeof requested==="object"||durMs!=null||!frameworkMetadataApplied)&&typeof response.headers?.set==="function"){const setHeaders=(h)=>{if(Array.isArray(after))for(const callback of after)try{if(typeof callback==="function")callback({status:response.status,durationMs:durMs??0})}catch{}if(requested&&typeof requested==="object"){for(const[name,value]of Object.entries(requested))if(typeof value==="string")h.set(name,value)}if(reqId&&!frameworkMetadataApplied)h.set("X-Request-ID",reqId);if(durMs!=null){let timing=`total;dur=${durMs.toFixed(1)}`;for(const t of middlewareTimings)timing+=`, mw_${t.name};dur=${t.ms.toFixed(1)}`;h.set("Server-Timing",timing)}if(!frameworkMetadataApplied)applySecurityHeaders(h)};if(response.status>=400&&(response.headers.get("content-type")||"").includes("json")&&reqId)try{const text=await response.clone().text(),parsed=JSON.parse(text);if(parsed&&typeof parsed==="object"){const newHeaders=new Headers(response.headers);setHeaders(newHeaders);return new Response(JSON.stringify({...parsed,request_id:reqId}),{status:response.status,statusText:response.statusText,headers:newHeaders})}}catch{}try{setHeaders(response.headers)}catch{try{const cloned=response.clone(),newHeaders=new Headers(response.headers);setHeaders(newHeaders);return new Response(cloned.body,{status:response.status,statusText:response.statusText,headers:newHeaders})}catch{}}}if(enhancedReq._compress===!0&&response)try{const cache=compressionApplierCache,applyCompression=cache.apply??=(await import(resolveDefaultsPath("app/Middleware/Compress.ts"))).applyCompression,bodySizeUpperBound=response[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND];return await applyCompression(enhancedReq,response,typeof bodySizeUpperBound==="number"?bodySizeUpperBound:void 0)}catch(err){log.warn(`[router] Compression failed; sending uncompressed response: ${err instanceof Error?err.message:String(err)}`)}return response};if(!synchronousInlineHandler||(routeFlags&ROUTE_MAY_HAVE_BODY)!==0)return rememberStacksRouteHandler(bindAsyncRouteHandler(handleAsyncInContext));return rememberStacksRouteHandler((req)=>{if(routeState?.middleware?.length||routeState?.rateLimit)return runWithRequestArgument(req,handleAsyncInContext,req);const csrfHandledByOuter=req[CSRF_SEEDED_BY_HANDLE_REQUEST]===!0;if((routeFlags&ROUTE_RENDERS_CSRF)!==0&&!csrfHandledByOuter&&requestMayRenderHtml(req)){const renderTokenSeeding=seedCsrfTokenForRender(req);if(renderTokenSeeding)return renderTokenSeeding.then(()=>runWithRequestArgument(req,handleAsyncInContext,req))}if((routeFlags&ROUTE_FORCES_JSON)!==0)req._forceJson=!0;let preparedBaseResult;try{preparedBaseResult=runWithRequestArguments(req,invokeInlineHandler,req,synchronousInlineHandler,void 0)}catch(error){preparedBaseResult=createHandlerErrorResponse(error,req,routeKey,router)}if(preparedBaseResult instanceof Response){const finished=finishSynchronousResult(req,preparedBaseResult,csrfHandledByOuter,(routeFlags&ROUTE_SEEDS_CSRF)!==0);if(finished)return finished}return runWithRequestArguments(req,handleAsyncInContext,req,preparedBaseResult,!0)})}function createInertRoute(){const inert={middleware:()=>inert,name:()=>inert,skipCsrf:()=>inert,requireCsrf:()=>inert,rateLimit:()=>inert};return inert}function createChainableRoute(routeStates,routeKey,shadowed=!1){if(shadowed)return createInertRoute();const routePath=routeKey.includes(":")?routeKey.substring(routeKey.indexOf(":")+1):routeKey,routeState=routeStates.get(routeKey),chain={middleware(name){if(!routeState)return chain;const middlewareList=routeState.middleware??=[];for(const entry of Array.isArray(name)?name:[name]){if(typeof entry!=="string")throw TypeError(`[Router] middleware() on ${routeKey} was given a ${typeof entry}; it takes an alias or an array of aliases`);middlewareList.push(entry)}return chain},name(routeName){(namedRouteRegistry??=new Map).set(routeName,compileNamedRoute(routePath));return chain},skipCsrf(){if(routeState)routeState.csrfMode=CSRF_SKIPPED;return chain},requireCsrf(){if(routeState)routeState.csrfMode=CSRF_REQUIRED;return chain},rateLimit(max,window){if(!Number.isFinite(max)||max<=0)throw Error(`[Router] .rateLimit(): max must be a positive number, got ${String(max)}`);const windowSeconds=rateLimitWindowToSeconds(window);if(routeState)routeState.rateLimit={max:Math.floor(max),windowSeconds};return chain}};return chain}async function fileExists(path){try{return await Bun.file(path).exists()}catch{return!1}}function assertSafeHandlerPath(handlerPath){if(typeof handlerPath!=="string"||handlerPath.length===0)throw Error(`[Router] Refusing to resolve handler '${String(handlerPath)}': empty or non-string`);if(handlerPath.includes("\x00"))throw Error("[Router] Refusing to resolve handler with null byte");if(handlerPath.startsWith("/")||/^[A-Za-z]:[\\/]/.test(handlerPath))throw Error(`[Router] Refusing to resolve absolute handler path '${handlerPath}'`);if(handlerPath.split(/[/\\]/).some((s)=>s===".."))throw Error(`[Router] Refusing to resolve handler path '${handlerPath}' (contains '..' segment)`)}let _moduleImportCache;function cachedImport(fullPath){const cache=_moduleImportCache??=new Map;let p=cache.get(fullPath);if(!p){p=import(fullPath);cache.set(fullPath,p)}return p}let _resolvedHandlerCache;function resolveStringHandler(handlerPath){const cache=_resolvedHandlerCache??=new Map;let resolved=cache.get(handlerPath);if(!resolved){resolved=resolveStringHandlerUncached(handlerPath).then((handler)=>{cache.set(handlerPath,handler);return handler},(error)=>{cache.delete(handlerPath);throw error});cache.set(handlerPath,resolved)}return resolved}function validationFailureResponse(errors){return response.validationError(errors)}export function precognitionRequest(req,knownHeader){const header=knownHeader===void 0?req.headers?.get?.("Precognition"):knownHeader,viaHeader=typeof header==="string"&&header.toLowerCase()==="true";let viaQuery=!1;try{if(!viaHeader&&req.url.includes("?"))viaQuery=new URL(req.url).searchParams.get("_validate")==="1"}catch{viaQuery=!1}if(!viaHeader&&!viaQuery)return null;return{only:(req.headers?.get?.("Precognition-Validate-Only")??"").split(",").map((field)=>field.trim()).filter(Boolean)}}export function precognitionSuccess(){return new Response(null,{status:204,headers:{Precognition:"true","Precognition-Success":"true",Vary:"Precognition, Precognition-Validate-Only"}})}let actionRegistryPromise=null;async function getActionRegistry(){if(actionRegistryPromise)return actionRegistryPromise;actionRegistryPromise=(async()=>{try{const dir=storagePath("framework/auto-imports"),module=await import(`${dir}/actions.ts`);if(!module.actions)return null;const{resolve}=await import("node:path");return Object.fromEntries(Object.entries(module.actions).map(([name,file])=>[name,resolve(dir,file)]))}catch{return null}})();return actionRegistryPromise}async function resolveStringHandlerUncached(handlerPath){assertSafeHandlerPath(handlerPath);let modulePath=handlerPath;modulePath=modulePath.endsWith(".ts")?modulePath.slice(0,-3):modulePath;if(modulePath.includes("Controller")){const[controllerPath,methodName="index"]=modulePath.split("@"),userPath=appPath(`${controllerPath}.ts`),defaultPath=resolveDefaultsPath(`app/${controllerPath}.ts`),fullPath=await fileExists(userPath)?userPath:defaultPath;try{const controller=await cachedImport(fullPath);if(!controller.default||typeof controller.default!=="function")throw Error(`Controller ${controllerPath} does not export a default class`);const instance=new controller.default;if(typeof instance[methodName]!=="function")throw Error(`Method ${methodName} not found in controller ${controllerPath}`);return async(req)=>{const result=await instance[methodName](req);return formatResult(result,req)}}catch(error){log.error(`[Router] Failed to load controller '${fullPath}':`,error);throw error}}let fullPath;if(modulePath.includes("storage/framework/orm"))fullPath=modulePath;else if(modulePath.includes("OrmAction"))fullPath=storagePath(`framework/actions/src/${modulePath}.ts`);else if(modulePath.includes("Actions")){const registered=(await getActionRegistry())?.[modulePath];if(registered)fullPath=registered;else{const userPath=projectPath(`app/${modulePath}.ts`),defaultPath=resolveDefaultsPath(`app/${modulePath}.ts`);fullPath=await fileExists(userPath)?userPath:defaultPath}}else{const userPath=appPath(`${modulePath}.ts`),defaultPath=resolveDefaultsPath(`app/${modulePath}.ts`);fullPath=await fileExists(userPath)?userPath:defaultPath}try{const action=(await cachedImport(fullPath)).default;if(!action)throw Error(`Action '${handlerPath}' has no default export`);if(typeof action.handle!=="function"){log.error(`[Router] Action '${handlerPath}' structure:`,Object.keys(action));throw Error(`Action '${handlerPath}' has no handle() method. Got: ${typeof action.handle}`)}return wrapAction(action,handlerPath)}catch(importError){log.error(`[Router] Failed to import action '${fullPath}':`,importError);throw importError}}export function isRouterAction(handler){return typeof handler==="object"&&handler!==null&&typeof handler.handle==="function"}export function wrapAction(action,handlerKey){const actionSkipsCsrf=action.skipCsrf===!0||action.csrf===!1;actionSkipsCsrfCache.set(handlerKey,actionSkipsCsrf);const actionForcesJson=action.apiResponse===!0,requestValidationRules=action.validations??modelValidationRules(action.modelDefinition??action.model),actionValidationEntries=action.validations?compileActionValidationEntries(action.validations):void 0,routeSeparator=handlerKey.indexOf(":"),actionMayHaveRouteParams=routeSeparator===-1||handlerKey.indexOf("{",routeSeparator+1)!==-1,prepareRequest=(req)=>{const precognitionHeader=req.headers.get("Precognition"),requestHasQuery=req.url.includes("?"),precognition=precognitionHeader===null&&!requestHasQuery?null:precognitionRequest(req,precognitionHeader);if(precognition){if(!action.validations)return precognitionSuccess();const rules=precognition.only.length>0?Object.fromEntries(Object.entries(action.validations).filter(([field])=>precognition.only.includes(field))):action.validations,precognitionResult=validateActionInputSync(req,rules,rules===action.validations?actionValidationEntries:void 0,actionMayHaveRouteParams,!0,requestHasQuery);return precognitionResult.valid?precognitionSuccess():validationFailureResponse(precognitionResult.errors)}if(action.validations){const validationResult=validateActionInputSync(req,action.validations,actionValidationEntries,actionMayHaveRouteParams,!0,requestHasQuery);if(!validationResult.valid)return validationFailureResponse(validationResult.errors)}},rethrowActionError=(handleError)=>{report(handleError,{label:`[Router] action.handle() for '${handlerKey}'`});throw handleError};if(typeof action.authorize!=="function"&&typeof action.before!=="function")return(req)=>{if(actionSkipsCsrf)req._skipCsrf=!0;if(actionForcesJson)req._forceJson=!0;req._requestValidationRules=requestValidationRules;try{const earlyResponse=prepareRequest(req);if(earlyResponse)return earlyResponse;const result=action.handle(req);return result instanceof Promise?result.then((value)=>{try{return formatResult(value,req)}catch(error){return rethrowActionError(error)}},rethrowActionError):formatResult(result,req)}catch(handleError){return rethrowActionError(handleError)}};return async(req)=>{if(actionSkipsCsrf)req._skipCsrf=!0;if(actionForcesJson)req._forceJson=!0;req._requestValidationRules=requestValidationRules;try{const earlyResponse=prepareRequest(req);if(earlyResponse)return earlyResponse;if(typeof action.authorize==="function"){const pendingAuth=action.authorize(req),auth=pendingAuth!==null&&(typeof pendingAuth==="object"||typeof pendingAuth==="function")?await pendingAuth:pendingAuth;if(auth instanceof Response)return auth;if(auth===!1)return Response.json({error:"Forbidden"},{status:403})}if(typeof action.before==="function"){const pendingBefore=action.before(req),pre=pendingBefore!==null&&(typeof pendingBefore==="object"||typeof pendingBefore==="function")?await pendingBefore:pendingBefore;if(pre instanceof Response)return pre}const pendingResult=action.handle(req),result=pendingResult instanceof Promise?await pendingResult:pendingResult;return formatResult(result,req)}catch(handleError){return rethrowActionError(handleError)}}}const EMPTY_VALIDATION_ERRORS=Object.freeze({}),VALID_ACTION_VALIDATION_RESULT=Object.freeze({valid:!0,errors:EMPTY_VALIDATION_ERRORS});function propertyOwner(value,property){let current=value;while(current){if(Object.hasOwn(current,property))return current;current=Object.getPrototypeOf(current)}}function compileActionValidationEntries(validations){return Object.entries(validations).map(([field,validation])=>{const rule=validation.rule;if(typeof rule.getRules!=="function"||propertyOwner(rule,"validate")!==propertyOwner(rule,"getRules"))return[field,validation,void 0,void 0];const rules=rule.getRules();if(!Array.isArray(rules)||rules.some((entry)=>typeof entry?.test!=="function"))return[field,validation,void 0,void 0];return[field,validation,rules.map((entry)=>entry.test),void 0]})}function createValidationFieldLabel(field){const label=field.replace(/[-_]+/g," ").replace(/([a-z])([A-Z])/g,"$1 $2").replace(/^./,(c)=>c.toUpperCase()),lowerField=field.toLowerCase();return{label,decorate:(message)=>message.toLowerCase().startsWith(lowerField)||message.includes(label)?message:`${label} ${message}`}}function validateActionInputSync(req,validations,compiledEntries,mayHaveRouteParams=!0,reuseSuccessResult=!1,requestHasQuery=req.url.includes("?")){let errors;const validated={};let valid=!0;const entries=compiledEntries??Object.entries(validations),input=getRequestInput(req,entries,mayHaveRouteParams,requestHasQuery);for(let entryIndex=0;entryIndex<entries.length;entryIndex++){const entry=entries[entryIndex],field=entry[0],validation=entry[1],ruleTests=entry[2],value=input[field];let result;try{const rule=validation.rule;let passedCompiledRules=!1;if(ruleTests){const empty=value===void 0||value===null||value==="";passedCompiledRules=empty&&rule.isRequired!==!0;if(!empty){passedCompiledRules=!0;for(let i=0;i<ruleTests.length;i++)if(!ruleTests[i](value)){passedCompiledRules=!1;break}}}if(!passedCompiledRules)result=rule.validate(value)}catch{result={valid:!1,errors:[{message:`${field} validation failed`}]}}if(result&&!result.valid){valid=!1;const fieldErrors=[],{label,decorate}=entry[3]??=createValidationFieldLabel(field);if(result.errors&&result.errors.length>0)if(validation.message){const firstMessage=result.errors[0]?.message??"";fieldErrors.push(typeof validation.message==="string"?validation.message:validation.message[field]||decorate(firstMessage))}else result.errors.forEach((err)=>fieldErrors.push(decorate(err.message)));else fieldErrors.push(validation.message?typeof validation.message==="string"?validation.message:`${label} is invalid`:`${label} is invalid`);(errors??={})[field]=fieldErrors}else if(value!==void 0)validated[field]=value}if(valid)req._validatedInput=validated;if(valid&&reuseSuccessResult)return VALID_ACTION_VALIDATION_RESULT;return{valid,errors:errors??{}}}export async function validateActionInput(req,validations){return validateActionInputSync(req,validations)}function modelValidationRules(model){if(!model?.attributes||typeof model.attributes!=="object")return;const rules={};for(const[field,attribute]of Object.entries(model.attributes)){const validation=attribute?.validation;if(validation?.rule)rules[field]=validation}return Object.keys(rules).length>0?rules:void 0}function getRequestInput(req,validationEntries,mayHaveRouteParams=!0,requestHasQuery=req.url.includes("?")){let input,mayNeedCoercion=!1;if(requestHasQuery){const q=req.query;if(q)for(const key in q){(input??={})[key]=q[key];mayNeedCoercion=!0}else new URL(req.url).searchParams.forEach((value,key)=>{(input??={})[key]=value;mayNeedCoercion=!0})}let hasRouteParams=!1;if(mayHaveRouteParams&&req.params)for(const key in req.params){if(!Object.hasOwn(req.params,key))continue;(input??={})[key]=req.params[key];hasRouteParams=!0;mayNeedCoercion=!0}if(req.jsonBody&&typeof req.jsonBody==="object")input=mayNeedCoercion?Object.assign(input??{},req.jsonBody):req.jsonBody;else if(req.formBody&&typeof req.formBody==="object"){input=mayNeedCoercion?Object.assign(input??{},req.formBody):{...req.formBody};mayNeedCoercion=!0}if(!requestHasQuery&&!mayHaveRouteParams&&req.jsonBody&&typeof req.jsonBody==="object"&&!Array.isArray(req.jsonBody)&&!req.formBody&&!req.files)req._allInputCache=input;if(req.files&&typeof req.allFiles==="function")try{const files=req.allFiles();for(const key of Object.keys(files??{})){const values=input??={};if(!(key in values))values[key]=files[key]}}catch{}if(!validationEntries)return input??{};input??={};let coerced=!1;if(mayNeedCoercion)for(const[field,validation]of validationEntries){const value=input[field];if(typeof value!=="string")continue;const validatorName=validation.rule?.name;if(validatorName==="number"){const n=Number(value);if(Number.isFinite(n)){input[field]=n;coerced=!0}}else if(validatorName==="boolean"){if(value==="true"||value==="1"){input[field]=!0;coerced=!0}else if(value==="false"||value==="0"){input[field]=!1;coerced=!0}}}if(!coerced&&!req.formBody&&!req.files&&!hasRouteParams)req._allInputCache=input;return input}function formatResult(result,req){if(result instanceof Response)return result;if(result instanceof ReadableStream)return new Response(result,{headers:{"Content-Type":"application/octet-stream"}});if(result!==null&&typeof result==="object"){const linkHeader="data"in result?buildPaginatorLinkHeader(result):null;return formatJsonResult(result,req,linkHeader)}const apiShaped=req._forceJson===!0||isApiRequest(req);if(result===null||result===void 0)return apiShaped?new Response(null,{status:204}):new Response("",{status:200});if(apiShaped){if(typeof result==="string"||typeof result==="number"||typeof result==="boolean")return formatJsonResult(result,req);return serializedResponse(JSON.stringify(result)??"","application/json;charset=utf-8")}return serializedResponse(String(result),"text/plain; charset=utf-8")}function serializedResponse(body,contentType){return new Response(body,{headers:{"Content-Type":contentType,"Content-Length":String(Buffer.byteLength(body))}})}function formatJsonResult(result,req,linkHeader){const canPreapplyMetadata=!req._responseHeaders,body=JSON.stringify(result)??"",requestMarkers=req,directNativeRequest=typeof requestMarkers[CSRF_SECURE_TRANSPORT]==="boolean"||requestMarkers[NATIVE_DIRECT_ROUTE_REQUEST]===!0,bodyLength=canPreapplyMetadata&&directNativeRequest?void 0:Buffer.byteLength(body),csrf=requestMarkers[CSRF_SEEDED_BY_HANDLE_REQUEST]!==!0&&typeof requestMarkers[CSRF_SECURE_TRANSPORT]==="boolean"?loadCsrfModule():null,csrfCookie=canPreapplyMetadata&&csrf&&!(csrf instanceof Promise)&&typeof csrf.createCsrfCookie==="function"?csrf.createCsrfCookie(req,req._csrfToken):void 0,response=canPreapplyMetadata?secureSerializedJsonResponse(body,bodyLength,req._requestId,csrfCookie):new Response(body,{headers:createJsonSecurityHeaders()});if(!canPreapplyMetadata)response.headers.set("Content-Length",String(bodyLength));if(linkHeader)response.headers.set("Link",linkHeader);if(canPreapplyMetadata){const frameworkResponse=response;frameworkResponse[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND]=bodyLength??(body.length*3>=1024&&body.length<1024?Buffer.byteLength(body):body.length*3)}if(csrfCookie)requestMarkers[CSRF_SEEDED_BY_HANDLE_REQUEST]=!0;return response}function buildPaginatorLinkHeader(value){if(!isPaginator(value)&&!isSimplePaginator(value)&&!isCursorPaginator(value))return null;const v=value,parts=[];if(v.prev_page_url)parts.push(`<${v.prev_page_url}>; rel="prev"`);if(v.next_page_url)parts.push(`<${v.next_page_url}>; rel="next"`);if(v.first_page_url)parts.push(`<${v.first_page_url}>; rel="first"`);if(v.last_page_url)parts.push(`<${v.last_page_url}>; rel="last"`);return parts.length>0?parts.join(", "):null}export function stream(source,options={}){const baseHeaders={};if(options.type==="sse"){baseHeaders["Content-Type"]="text/event-stream; charset=utf-8";baseHeaders["Cache-Control"]="no-cache";baseHeaders.Connection="keep-alive"}else if(options.type==="ndjson")baseHeaders["Content-Type"]="application/x-ndjson; charset=utf-8";else baseHeaders["Content-Type"]=options.contentType??"application/octet-stream";const body=source instanceof ReadableStream?source:new ReadableStream({async start(controller){try{for await(const chunk of source)controller.enqueue(typeof chunk==="string"?new TextEncoder().encode(chunk):chunk);controller.close()}catch(err){controller.error(err)}}}),merged=new Headers(baseHeaders);if(options.headers)new Headers(options.headers).forEach((value,key)=>merged.set(key,value));return new Response(body,{status:options.status??200,headers:merged})}function getAllInputFor(req){const cached=req._allInputCache;if(cached)return cached;const input={},query=req.query;if(query)for(const key in query)input[key]=query[key];if(req.jsonBody&&typeof req.jsonBody==="object")Object.assign(input,req.jsonBody);if(req.formBody&&typeof req.formBody==="object")Object.assign(input,req.formBody);if(req.params&&typeof req.params==="object")Object.assign(input,req.params);req._allInputCache=input;return input}function flashInputFor(req,keys){const input=getAllInputFor(req);req._oldInput=keys?Object.fromEntries(keys.filter((key)=>(key in input)).map((key)=>[key,input[key]])):{...input}}const nativeRequestText=Request.prototype.text,nativeRequestJson=Request.prototype.json,nativeRequestBytes=Request.prototype.bytes,nativeRequestArrayBuffer=Request.prototype.arrayBuffer,nativeRequestBlob=Request.prototype.blob,nativeRequestClone=Request.prototype.clone;let requestBodyEncoder,requestValidationModule,UploadedFileConstructor,requestGateModule;function createUploadedFile(file){UploadedFileConstructor??=require("@stacksjs/storage/uploaded-file").UploadedFile;return new UploadedFileConstructor(file)}const REQUEST_METHODS={text(){return this._rawBody===void 0?nativeRequestText.call(this):Promise.resolve(this._rawBody)},json(){if(this._rawBody===void 0)return nativeRequestJson.call(this);try{return Promise.resolve(this._rawBody.length===0?null:JSON.parse(this._rawBody))}catch(error){return Promise.reject(error)}},bytes(){return this._rawBody===void 0?nativeRequestBytes.call(this):Promise.resolve((requestBodyEncoder??=new TextEncoder).encode(this._rawBody))},arrayBuffer(){if(this._rawBody===void 0)return nativeRequestArrayBuffer.call(this);const bytes=(requestBodyEncoder??=new TextEncoder).encode(this._rawBody);return Promise.resolve(bytes.buffer)},blob(){return this._rawBody===void 0?nativeRequestBlob.call(this):Promise.resolve(new Blob([this._rawBody],{type:this._rawBodyContentType}))},clone(){if(this._rawBody===void 0)return nativeRequestClone.call(this);return new Request(this.url,{method:this.method,headers:this.headers,body:this._rawBody})},get(key,defaultValue){const value=getAllInputFor(this)[key];return value!==void 0?value:defaultValue},input(key,defaultValue){const value=getAllInputFor(this)[key];return value!==void 0?value:defaultValue},all(){return getAllInputFor(this)},only(keys){const input=getAllInputFor(this),result={};for(const key of keys)if(key in input)result[key]=input[key];return result},except(keys){const result={...getAllInputFor(this)};for(const key of keys)delete result[key];return result},has(key){const input=getAllInputFor(this);if(Array.isArray(key))return key.every((k)=>(k in input)&&input[k]!==void 0);return key in input&&input[key]!==void 0},hasAny(keys){const input=getAllInputFor(this);return keys.some((k)=>(k in input)&&input[k]!==void 0)},filled(key){const input=getAllInputFor(this),isFilled=(k)=>{const value=input[k];return value!==void 0&&value!==null&&value!==""&&!(Array.isArray(value)&&value.length===0)};if(Array.isArray(key))return key.every(isFilled);return isFilled(key)},missing(key){const input=getAllInputFor(this);if(Array.isArray(key))return key.every((k)=>!(k in input)||input[k]===void 0);return!(key in input)||input[key]===void 0},merge(data){Object.assign(getAllInputFor(this),data)},keys(){return Object.keys(getAllInputFor(this))},string(key,defaultValue=""){const value=getAllInputFor(this)[key];return value!==void 0&&value!==null?String(value):defaultValue},integer(key,defaultValue=0){const value=getAllInputFor(this)[key];if(value===void 0||value===null||value==="")return defaultValue;if(typeof value==="number")return Number.isFinite(value)?Math.trunc(value):defaultValue;const str=String(value).trim();if(!/^-?\d+$/.test(str))return defaultValue;const parsed=Number.parseInt(str,10);return Number.isFinite(parsed)?parsed:defaultValue},float(key,defaultValue=0){const value=getAllInputFor(this)[key];if(value===void 0||value===null||value==="")return defaultValue;if(typeof value==="number")return Number.isFinite(value)?value:defaultValue;const str=String(value).trim();if(!/^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(str))return defaultValue;const parsed=Number.parseFloat(str);return Number.isFinite(parsed)?parsed:defaultValue},boolean(key,defaultValue=!1){const value=getAllInputFor(this)[key];if(value===void 0||value===null)return defaultValue;if(typeof value==="boolean")return value;if(value==="true"||value==="1"||value===1)return!0;if(value==="false"||value==="0"||value===0)return!1;return defaultValue},array(key){const value=getAllInputFor(this)[key];if(Array.isArray(value))return value;return value!==void 0&&value!==null?[value]:[]},date(key){const value=getAllInputFor(this)[key];if(value===void 0||value===null||value==="")return null;const parsed=new Date(value);return Number.isNaN(parsed.getTime())?null:parsed},enum(key,enumType){const value=getAllInputFor(this)[key];if(value===void 0||value===null)return null;if(Object.values(enumType).includes(value))return value;const enumKey=String(value);return enumKey in enumType?enumType[enumKey]:null},collect(key){const{collect}=require("@stacksjs/collections"),value=getAllInputFor(this)[key];if(Array.isArray(value))return collect(value);return collect(value===void 0||value===null?[]:[value])},whenHas(key,callback,defaultCallback){const input=getAllInputFor(this);if(key in input&&input[key]!==void 0)callback(input[key]);else defaultCallback?.()},whenFilled(key,callback,defaultCallback){const value=getAllInputFor(this)[key];if(value!==void 0&&value!==null&&value!==""&&!(Array.isArray(value)&&value.length===0))callback(value);else defaultCallback?.()},isValue(key,value){return getAllInputFor(this)[key]===value},async validate(rules,messages={}){const selectedRules=rules??this._requestValidationRules;if(!selectedRules||Object.keys(selectedRules).length===0){const input=getAllInputFor(this);this._validatedInput=input;return input}const entries=Object.entries(selectedRules);let normalized=selectedRules;for(let index=0;index<entries.length;index++){const[field,definition]=entries[index];if(typeof definition==="string")throw TypeError(`String validation rules are not supported for "${field}". Use schema validators.`);if(definition&&typeof definition==="object"&&"rule"in definition){const message=messages[field];if(message){if(normalized===selectedRules){normalized={};for(let previousIndex=0;previousIndex<index;previousIndex++){const[previousField,previousDefinition]=entries[previousIndex];normalized[previousField]=previousDefinition}}normalized[field]={...definition,message};continue}}if(normalized!==selectedRules)normalized[field]=definition}const{validate}=requestValidationModule??=await import("@stacksjs/validation/request-validator"),validated=await validate(this,normalized);this._validatedInput=validated;return validated},getValidated(){return this._validatedInput??{}},safe(){const data=this._validatedInput??{};return{all:()=>({...data}),get:(key,defaultValue)=>(key in data)?data[key]:defaultValue,only:(keys)=>Object.fromEntries(keys.filter((key)=>(key in data)).map((key)=>[key,data[key]])),except:(keys)=>Object.fromEntries(Object.entries(data).filter(([key])=>!keys.includes(key)))}},old(key,defaultValue){return this._oldInput?.[key]??defaultValue},flashInput(keys){flashInputFor(this,keys)},flashInputOnly(keys){flashInputFor(this,keys)},flashInputExcept(keys){const input=getAllInputFor(this);this._oldInput=Object.fromEntries(Object.entries(input).filter(([key])=>!keys.includes(key)))},file(key){const file=(this.files||{})[key];if(!file)return null;const rawFile=Array.isArray(file)?file[0]:file;return rawFile?createUploadedFile(rawFile):null},getFiles(key){const file=(this.files||{})[key];if(!file)return[];return(Array.isArray(file)?file:[file]).map(createUploadedFile)},hasFile(key){const files=this.files||{};return key in files&&files[key]!==void 0},allFiles(){const files=this.files||{},result={};for(const[key,value]of Object.entries(files))if(Array.isArray(value))result[key]=value.map((f)=>createUploadedFile(f));else result[key]=createUploadedFile(value);return result},getParams(){return{...this.params}},isEmpty(){return Object.keys(getAllInputFor(this)).length===0},browser(){return this.headers.get("sec-ch-ua")||this.headers.get("user-agent")},ipForRateLimit(){const ip=this.ip;if(typeof ip==="function")return ip.call(this)||null;return typeof ip==="string"&&ip?ip:null},getMethod(){return this.method.toUpperCase()},async user(){return this._authenticatedUser},async userToken(){return this._currentAccessToken},async tokenCan(ability){if(typeof ability!=="string"||ability.length===0)return!1;const token=this._currentAccessToken;if(!token||typeof token!=="object")return!1;const abilities=token.abilities;if(!Array.isArray(abilities))return!1;if(abilities.includes("*"))return!0;return abilities.includes(ability)},async tokenCant(ability){return!await this.tokenCan(ability)},async can(ability,...args){if(typeof ability!=="string"||ability.length===0)return!1;const{Gate}=requestGateModule??=await import("@stacksjs/auth/gate"),user=this._authenticatedUser??null;return Gate.allows(ability,user,...args)},async cannot(ability,...args){return!await this.can(ability,...args)},async authorize(ability,...args){const{Gate}=requestGateModule??=await import("@stacksjs/auth/gate"),user=this._authenticatedUser??null;await Gate.authorize(ability,user,...args)}},stacksRequestPrototypes=new WeakMap,STACKS_REQUEST_ENHANCED=Symbol.for("stacks.router.requestEnhanced");function fuseRequestEnhancements(router,initializeRequestIds=!0){const original=router.enhanceRequest.bind(router),combinedPrototypes=new WeakMap;router.enhanceRequest=(request,params={})=>{const basePrototype=Object.getPrototypeOf(request),combined=combinedPrototypes.get(basePrototype);if(combined){const enhanced=request;if(enhanced.params!==params)if("params"in request)Object.defineProperty(enhanced,"params",{value:params,writable:!0,configurable:!0,enumerable:!1});else enhanced.params=params;Object.setPrototypeOf(enhanced,combined);if(initializeRequestIds&&!enhanced._requestId)enhanced._requestId=incomingRequestId(enhanced)??generateRequestId();return enhanced}const enhanced=enhanceRequest(original(request,params),initializeRequestIds);combinedPrototypes.set(basePrototype,Object.getPrototypeOf(enhanced));return enhanced}}let csrfModule,csrfModuleLoad;function loadCsrfModule(){if(csrfModule!==void 0)return csrfModule;csrfModuleLoad??=import(resolveDefaultsPath("app/Middleware/Csrf.ts")).then((mod)=>{csrfModule=mod;return csrfModule}).catch(()=>{csrfModule=null;return null});return csrfModuleLoad}export function clearCsrfModuleCache(){csrfModule=void 0;csrfModuleLoad=void 0}function applyCsrfRenderToken(req,cookieHeader,mod){const token=mod.generateCsrfToken();req._csrfToken=token;try{const merged=cookieHeader?`${cookieHeader}; ${mod.CSRF_COOKIE_NAME}=${token}`:`${mod.CSRF_COOKIE_NAME}=${token}`;req.headers.set("cookie",merged)}catch{}}function requestMayRenderHtml(req){return req.headers.get("accept")?.includes("text/html")===!0||req.headers.get("sec-fetch-dest")==="document"}function hasCsrfCookie(cookieHeader){return cookieHeader.length!==0&&(cookieHeader.startsWith("X-CSRF-Token=")||cookieHeader.includes("X-CSRF-Token=")||cookieHeader.includes("csrf-token="))}function seedCsrfTokenForRender(req,cookieHeader=req.headers?.get?.("cookie")??""){const mod=loadCsrfModule();if(mod===null)return;if(mod instanceof Promise)return mod.then((resolved)=>{if(!resolved)return;if(hasCsrfCookie(cookieHeader)){req._csrfToken=resolved.csrfCookieToken(req)||void 0;return}applyCsrfRenderToken(req,cookieHeader,resolved)});if(hasCsrfCookie(cookieHeader)){req._csrfToken=mod.csrfCookieToken(req)||void 0;return}applyCsrfRenderToken(req,cookieHeader,mod)}export function enhanceRequest(req,initializeRequestId=!0){const routeParams=req.params??{};req.params=routeParams;if(initializeRequestId&&!req._requestId)req._requestId=incomingRequestId(req)??generateRequestId();if(req[STACKS_REQUEST_ENHANCED]===!0)return req;applyRequestEnhancements(req,routeParams);const routerPrototype=Object.getPrototypeOf(req);let stacksPrototype=stacksRequestPrototypes.get(routerPrototype);if(!stacksPrototype){stacksPrototype=Object.assign(Object.create(routerPrototype),REQUEST_METHODS);Object.defineProperty(stacksPrototype,STACKS_REQUEST_ENHANCED,{value:!0});stacksRequestPrototypes.set(routerPrototype,stacksPrototype)}Object.setPrototypeOf(req,stacksPrototype);return req}const INVALID_REQUEST_ID_CHARACTER=/[^\w.:-]/;function incomingRequestId(req){try{const supplied=req.headers.get("x-request-id")?.trim();if(supplied&&supplied.length>=8&&supplied.length<=200&&!INVALID_REQUEST_ID_CHARACTER.test(supplied))return supplied}catch{}return}const REQUEST_ID_DIGITS="0123456789abcdefghijklmnopqrstuvwxyz",REQUEST_ID_SUFFIX_COUNT=36;function createRequestIdGenerator(){const prefix=crypto.randomUUID().replaceAll("-","").slice(0,16);let block=0,suffix=0,blockPrefix=`${prefix}-0`;return()=>{suffix++;if(suffix===REQUEST_ID_SUFFIX_COUNT){suffix=0;blockPrefix=`${prefix}-${(++block).toString(36)}`}return blockPrefix+REQUEST_ID_DIGITS[suffix]}}let generateRequestId=()=>{generateRequestId=createRequestIdGenerator();return generateRequestId()};function createHandlerErrorResponse(error,req,handlerPath,router){if(router?.errorHandler)return Promise.reject(error);report(error,{label:`[Router] ${handlerPath}`});const rawStatus=error?.statusCode??error?.status,status=typeof rawStatus==="number"&&Number.isInteger(rawStatus)&&rawStatus>=400&&rawStatus<600?rawStatus:void 0;return createErrorResponse(error instanceof Error?error:Error(String(error)),req,{handlerPath,status})}function wrapHandler(handler,skipParsing=!1,handlerKey=""){if(isRouterAction(handler))return wrapAction(handler,handlerKey);if(typeof handler==="string"){const handlerPath=handler;return async(req)=>{try{if(!skipParsing){await parseRequestBody(req);req=enhanceRequest(req)}const pending=resolveStringHandler(handlerPath);return await(pending instanceof Promise?await pending:pending)(req)}catch(error){return await createHandlerErrorResponse(error,req,handlerPath)}}}const fn=handler;return(req)=>{const result=fn(req);if(result instanceof Promise)return result.then((value)=>formatResult(value,req));return formatResult(result,req)}}async function parseRequestBody(req){if(req._bodyParsed)return;req._bodyParsed=!0;const contentType=req.headers.get("content-type")||"";try{if(contentType==="application/json"||JSON_CONTENT_TYPE.test(contentType)){const raw=await req.text();req._rawBody=raw;req._rawBodyContentType=contentType;if(raw.length===0)req.jsonBody={};else try{const body=JSON.parse(raw);req.jsonBody=body&&typeof body==="object"?body:{}}catch(parseErr){const message=parseErr instanceof Error?parseErr.message:"Invalid JSON",{HttpError}=routerHttpErrorModule??=await import("@stacksjs/error-handling/http-error");throw new HttpError(400,`Invalid JSON body: ${message}`)}}else if(contentType.includes("application/x-www-form-urlencoded")){const text=await req.clone().text(),params=new URLSearchParams(text),formBody={};params.forEach((value,key)=>{formBody[key]=value});req.formBody=formBody}else if(contentType.includes("multipart/form-data")){const formData=await req.clone().formData(),formBody={},files={};formData.forEach((value,key)=>{if(value instanceof File)if(files[key])if(Array.isArray(files[key]))files[key].push(value);else files[key]=[files[key],value];else files[key]=value;else formBody[key]=value});Reflect.set(req,"formBody",formBody);Reflect.set(req,"files",files)}}catch(e){if(typeof(e?.status??e?.statusCode)==="number")throw e;log.debug("[stacks-router] Body parsing failed:",e)}}let signedStorageModule;export function createStacksRouter(config={}){const bunRouter=new Router({verbose:config.verbose??!1});if(config.autoDiscoverRoutes===!1)bunRouter._apiRoutesInitialized=!0;const requestContextEnabled=config.requestContext!==!1;setAmbientRequestContext(requestContextEnabled);if(!requestContextEnabled)routersWithoutRequestContext.add(bunRouter);const csrfEnabled=config.csrf!==!1;if(csrfEnabled)csrfEnabledNativeRouteRouters.add(bunRouter);fuseRequestEnhancements(bunRouter,config.requestIds!==!1);let currentPrefix="",currentGroupMiddleware=[],currentGroupApiResponse=!1;const routeStates=new Map;function registerRoute(method,path,_handler){const fullPath=currentPrefix+path,routeKey=`${method}:${fullPath}`,handlerKind=_handler instanceof Response?"static response":typeof _handler==="string"?_handler:"function";log.debug(`[router] ${method} ${fullPath} \u2192 ${handlerKind}`);const shadowed=routeStates.has(routeKey)||clearedRouteKeysByRegistry.get(routeStates)?.has(routeKey)===!0;if(!shadowed){const routeState={csrfMode:CSRF_DEFAULT};if(currentGroupMiddleware.length>0)routeState.middleware=[...currentGroupMiddleware];routeStates.set(routeKey,routeState);routeStateRegistries.add(routeStates)}if(!shadowed&&currentGroupApiResponse)(routeApiResponseRegistry??=new Set).add(routeKey);if(!shadowed&&typeof _handler==="string")routeHandlerKeyRegistry.set(routeKey,_handler);if(!shadowed&&isRouterAction(_handler))(routeActionRegistry??=new Map).set(routeKey,_handler);return{fullPath,routeKey,shadowed}}const stacksRouter={bunRouter,get routes(){return bunRouter.routes},staticResponse(method,path,response){if(!STATIC_RESPONSE_METHODS.has(method))throw TypeError(`[Router] staticResponse(): unsupported HTTP method '${String(method)}'`);if(!(response instanceof Response))throw TypeError("[Router] staticResponse(): response must be a prebuilt Response");if(currentGroupMiddleware.length>0)throw Error("[Router] staticResponse() cannot be registered inside a group with middleware because static responses bypass middleware");if(currentGroupApiResponse)throw Error("[Router] staticResponse() cannot be registered inside a group with apiResponse because static responses bypass response formatting");const{fullPath,shadowed}=registerRoute(method,path,response);if(!shadowed)switch(method){case"GET":bunRouter.get(fullPath,response);break;case"POST":bunRouter.post(fullPath,response);break;case"PUT":bunRouter.put(fullPath,response);break;case"PATCH":bunRouter.patch(fullPath,response);break;case"DELETE":bunRouter.delete(fullPath,response);break;case"OPTIONS":bunRouter.options(fullPath,response);break}return stacksRouter},get(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("GET",path,handler);bunRouter.get(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},post(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("POST",path,handler);bunRouter.post(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},put(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("PUT",path,handler);bunRouter.put(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},patch(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("PATCH",path,handler);bunRouter.patch(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},delete(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("DELETE",path,handler);bunRouter.delete(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},options(path,handler){const{fullPath,routeKey,shadowed}=registerRoute("OPTIONS",path,handler);bunRouter.options(fullPath,createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled));return createChainableRoute(routeStates,routeKey,shadowed)},group(options,callback){const previousPrefix=currentPrefix,previousMiddleware=[...currentGroupMiddleware],previousApiResponse=currentGroupApiResponse,restoreGroupState=()=>{currentPrefix=previousPrefix;currentGroupMiddleware=previousMiddleware;currentGroupApiResponse=previousApiResponse};if(options.prefix)currentPrefix=previousPrefix+options.prefix;const middlewareList=options.middleware?Array.isArray(options.middleware)?options.middleware:[options.middleware]:void 0;if(middlewareList)currentGroupMiddleware=[...currentGroupMiddleware,...middlewareList];if(options.apiResponse===!0)currentGroupApiResponse=!0;log.debug(`[router] Entering group: prefix=${options.prefix||"/"} middleware=[${middlewareList?.join(", ")||""}]${currentGroupApiResponse?" apiResponse=true":""}`);let result;try{result=callback()}catch(error){restoreGroupState();throw error}if(result instanceof Promise)return result.then(()=>stacksRouter).finally(restoreGroupState);restoreGroupState();return stacksRouter},resource(name,handler,options){const actions=["index","store","show","update","destroy"],activeActions=options?.only?actions.filter((a)=>options.only.includes(a)):options?.except?actions.filter((a)=>!options.except.includes(a)):actions,stripped=handler.replace(/Action$/,""),handlerBase=stripped.startsWith("Actions/")?stripped:`Actions/${stripped}`;log.debug(`[router] Resource: /${name} \u2192 ${handlerBase}*Action [${activeActions.join(", ")}]`);const sibling=(suffix)=>`${handlerBase}${suffix}`,registerResourceRoutes=()=>{for(const action of activeActions)switch(action){case"index":stacksRouter.get(`/${name}`,sibling("IndexAction"));break;case"store":stacksRouter.post(`/${name}`,sibling("StoreAction"));break;case"show":stacksRouter.get(`/${name}/:id`,sibling("ShowAction"));break;case"update":stacksRouter.put(`/${name}/:id`,sibling("UpdateAction"));break;case"destroy":stacksRouter.delete(`/${name}/:id`,sibling("DestroyAction"));break}};if(options?.middleware)stacksRouter.group({middleware:options.middleware},registerResourceRoutes);else registerResourceRoutes();return stacksRouter},match(methods,path,handler){log.debug(`[router] Match: [${methods.join(", ")}] ${path} \u2192 ${typeof handler==="string"?handler:"function"}`);let firstShadowed=!1;for(const[index,method]of methods.entries()){const m=method.toUpperCase(),{fullPath,routeKey,shadowed}=registerRoute(m,path,handler);if(index===0)firstShadowed=shadowed;const wrappedHandler=createMiddlewareHandler(bunRouter,routeStates,routeKey,handler,csrfEnabled);switch(m){case"GET":bunRouter.get(fullPath,wrappedHandler);break;case"POST":bunRouter.post(fullPath,wrappedHandler);break;case"PUT":bunRouter.put(fullPath,wrappedHandler);break;case"PATCH":bunRouter.patch(fullPath,wrappedHandler);break;case"DELETE":bunRouter.delete(fullPath,wrappedHandler);break;case"OPTIONS":bunRouter.options(fullPath,wrappedHandler);break}}return createChainableRoute(routeStates,`${methods[0]}:${currentPrefix}${path}`,firstShadowed)},health(){bunRouter.get("/api/health",async()=>{const{checkApplicationHealth}=await import("./health"),health=await checkApplicationHealth();return Response.json(health,{status:health.status==="healthy"?200:503})});bunRouter.get("/__routes",(req)=>{if(!isExposeRoutesAuthorized(req))return Response.json({error:"disabled"},{status:404});return Response.json(listRegisteredRoutes())});bunRouter.get("/__storage/{path}",async(req)=>{const url=new URL(req.url),token=url.searchParams.get("token"),params=req.params,rawPath=params?.path?params.path:decodeURIComponent(url.pathname.replace(/^\/__storage\//,""));if(!token||typeof rawPath!=="string"||rawPath.length===0)return new Response("Forbidden",{status:403});const{verifySignedStorageToken,Storage}=signedStorageModule??=await import("@stacksjs/storage");if(!verifySignedStorageToken(token,rawPath).valid)return new Response("Forbidden",{status:403});try{const adapter=Storage.disk();if(!await adapter.fileExists(rawPath))return new Response("Not Found",{status:404});const buf=await adapter.readToBuffer(rawPath),mime=await adapter.mimeType(rawPath).catch(()=>"application/octet-stream");return new Response(buf,{status:200,headers:{"Content-Type":mime,"Cache-Control":"private, max-age=60","X-Content-Type-Options":"nosniff"}})}catch(err){log.error("[storage] signed-url fetch failed:",err);return new Response("Internal Error",{status:500})}});bunRouter.get("/__openapi.json",async(req)=>{if(!isExposeRoutesAuthorized(req))return Response.json({error:"disabled"},{status:404});try{const{generateOpenApi}=await import("@stacksjs/api"),spec=await generateOpenApi({write:!1,portable:!1});return Response.json(spec)}catch(err){return Response.json({error:"OpenAPI generation failed",message:err instanceof Error?err.message:String(err)},{status:500})}});return stacksRouter},use(middleware){const adapted=adaptMiddlewareForBunRouter(middleware);bunRouter.globalMiddleware.push(adapted);return stacksRouter},booting(name,run){bootHooks.push({name,run});return stacksRouter},async serve(options={}){warnOnMultipleRouterInstances();configureViewDirectories(bunRouter);if(csrfEnabled)wrapHandleRequestForCsrf(bunRouter);await wrapHandleRequestForDatabaseContext(bunRouter);await runBootHooks();const production=(process.env.APP_ENV??"").toLowerCase()==="production"||process.env.NODE_ENV==="production",callerSuppliedRoutes=Object.prototype.hasOwnProperty.call(options,"routes"),nativeRoutes=options.nativeRoutes??(production&&!callerSuppliedRoutes&&shouldUseNativeRoutesByDefault(bunRouter.routes));if(options.tls)secureNativeRouteRouters.add(bunRouter);else secureNativeRouteRouters.delete(bunRouter);return bunRouter.serve(nativeRoutes===options.nativeRoutes?options:{...options,nativeRoutes})},handleRequest(req){return bunRouter.handleRequest(req)},getAllowedMethods(pathname,domain){return bunRouter.getAllowedMethods(pathname,domain)},async register(routePath,options){log.debug(`[router] Register: ${routePath} prefix=${options?.prefix||"none"}`);const callback=async()=>{await import(routePath)};if(options?.prefix||options?.middleware)await stacksRouter.group({prefix:options.prefix,middleware:options.middleware},callback);else await callback();return stacksRouter},async importRoutes(){log.debug("[router] Loading user routes from registry...");try{const{appRouteRegistry,loadRoutes}=await import("./route-loader");await loadRoutes(await appRouteRegistry())}catch(error){log.error("Failed to load route registry:",error);throw error}log.debug("[router] Loading ORM routes...");const ormRoutesPackage="@stacksjs/orm/routes";let ormRoutesLoaded=!1;try{await import(ormRoutesPackage);ormRoutesLoaded=!0;log.debug(`[router] ORM routes loaded from ${ormRoutesPackage}`)}catch(error){log.debug(`[router] ORM routes not available from the package, trying the vendored copy
5
5
  `,error)}const ormRoutesCandidates=ormRoutesLoaded?[]:[frameworkPath("orm/routes.ts"),frameworkPath("core/orm/routes.ts")];for(const candidate of ormRoutesCandidates)try{if(await Bun.file(candidate).exists()){await import(candidate);ormRoutesLoaded=!0;log.info(`[router] ORM routes loaded from the vendored copy at ${candidate}. This file is not refreshed by upgrading @stacksjs/orm - delete it to use the package.`);break}}catch(error){log.warn(`[router] ORM routes candidate failed to load, falling back to next: ${candidate}
6
- `,error)}if(!ormRoutesLoaded)log.warn("[router] No ORM routes candidate loaded - model useApi endpoints are unavailable.");log.debug("[router] Loading discovered package routes...");try{await stacksRouter.loadDiscoveredRoutes()}catch(error){log.debug("Package route discovery skipped:",error)}await assertRouteMiddlewareResolvable()},async loadDiscoveredRoutes(){try{const manifestPath=storagePath("framework/discovered-packages.json"),file=Bun.file(manifestPath);if(!await file.exists())return;const packages=(await file.json())?.packages;if(!packages)return;for(const[pkgName,meta]of Object.entries(packages)){const routes=meta?.routes;if(!routes)continue;const routeList=Array.isArray(routes)?routes:[routes],pkgDir=meta?.root?projectPath(meta.root):`${projectPath("pantry")}/${pkgName}`;for(const routeFile of routeList){log.debug(`[router] Discovered route: ${pkgName} \u2192 ${routeFile}`);const fullPath=routeFile.startsWith("/")?routeFile:`${pkgDir}/${routeFile}`,prefix=meta?.routePrefix,middleware=meta?.routeMiddleware;try{await stacksRouter.register(fullPath,{prefix,middleware})}catch(err){log.warn(`Failed to load routes from package '${pkgName}': ${err}`)}}}}catch{}}};return stacksRouter}function wrapHandleRequestForCsrf(bunRouter){const router=bunRouter;if(router._csrfSeedingWrapped)return;const original=router.handleRequest.bind(router),handleUnseededSafeRequest=async(request,rendersCsrf,cookieHeader)=>{if(rendersCsrf&&requestMayRenderHtml(request)){const seeding=seedCsrfTokenForRender(request,cookieHeader);if(seeding)await seeding}request[CSRF_SEEDED_BY_HANDLE_REQUEST]=!0;const response=await original(request);if(!response)return response;try{const mod=loadCsrfModule(),csrf=mod instanceof Promise?await mod:mod;if(!csrf)return response;return csrf.seedCsrfCookieIfMissing(request,response,request._csrfToken)}catch(err){log.warn("[router] CSRF cookie seeding failed on the view path",{error:err});return response}},handleSafeRequest=(request,rendersCsrf)=>{const cookieHeader=request.headers.get("cookie")??"";if(hasCsrfCookie(cookieHeader)){request[CSRF_SEEDED_BY_HANDLE_REQUEST]=!0;return original(request)}return handleUnseededSafeRequest(request,rendersCsrf,cookieHeader)};router.handleRequest=(request)=>{const method=request.method;if(method==="GET"||method==="HEAD")return handleSafeRequest(request,!0);if(method==="OPTIONS")return handleSafeRequest(request,!1);return original(request)};router._csrfSeedingWrapped=!0}export function configureViewDirectories(bunRouter){const router=bunRouter;if(typeof router.views!=="function")return;const existing=router._fileRoutingConfig;if(existing&&Object.keys(existing).length>0)return;if(!existsSync(projectPath("resources/views")))return;const layouts=[projectPath("resources/views/layouts"),projectPath("resources/layouts")].find(existsSync),partials=[projectPath("resources/views/partials"),projectPath("resources/partials")].find(existsSync),components=[projectPath("resources/components"),projectPath("resources/views/components")].find(existsSync);router.views({...components?{componentsDir:components}:{},...layouts?{layoutsDir:layouts}:{},...partials?{partialsDir:partials}:{}})}export function disableViewRouting(bunRouter){const router=bunRouter;if(typeof router.disableFileRouting!=="function")return!1;const existing=router._fileRoutingConfig;if(existing&&Object.keys(existing).length>0)return!1;router.disableFileRouting();return!0}const ROUTE_SINGLETON_KEY=Symbol.for("@stacksjs/router:route-singleton");export const route=globalThis[ROUTE_SINGLETON_KEY]??=createStacksRouter();let routesLoadPromise=null,routingContextDispatcher=null;const databaseContextWrappedRouters=new WeakSet;function applyDirectNativeCookies(router,response,request){const cookieChanges=request;return cookieChanges._cookiesToSet?.length||cookieChanges._cookiesToDelete?.length?router.applyModifiedCookies(response,request):response}function nativeRouteErrorResponse(error){return new Response(JSON.stringify({success:!1,message:"Internal Server Error",error:error instanceof Error?error.message:String(error)}),{status:500,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST, PUT, DELETE, PATCH, OPTIONS","Access-Control-Allow-Headers":"Content-Type, Authorization, X-Requested-With, Accept, Origin"}})}function handleDirectNativeRouteError(router,error){console.error("Error handling request:",error);return router.errorHandler?router.errorHandler(error instanceof Error?error:Error(String(error))):nativeRouteErrorResponse(error)}function directNativePath(path){const segments=path.split("/");for(let index=0;index<segments.length;index++){const segment=segments[index];if(!segment)continue;const parameter=segment.match(/^\{([A-Z_$][\w$]*)\}$/i);if(parameter){segments[index]=`:${parameter[1]}`;continue}if(segment.includes("{")||segment.includes(":")||segment.includes("*"))return}return segments.join("/")}function buildDirectNativeHandlers(router,finalizeResponse){const nativeRouter=router,handlers=new Map;if(nativeRouter.globalMiddleware.length!==0)return handlers;const enterBunRouterContext=routersWithoutRequestContext.has(router)?(_req,fn)=>fn():runWithBunRouterRequest;for(const route of nativeRouter.routes){if(!isNativeRouteMethod(route.method)||route.nativeDispatch===!1||route.constraints&&Object.keys(route.constraints).length>0||(route.middleware?.length??0)!==0||typeof route.handler!=="function"||!stacksRouteHandlers.has(route.handler))continue;const handler=route.handler,nativePath=directNativePath(route.path);if(!nativePath)continue;const key=`${route.method}:${nativePath}`;if(handlers.has(key))continue;handlers.set(key,(request)=>{const nativeParams=request.params??{},enhancedRequest=nativeRouter.enhanceRequest(request,nativeParams);enhancedRequest.route=route;const requestMarkers=enhancedRequest;if(typeof requestMarkers[CSRF_SECURE_TRANSPORT]!=="boolean")requestMarkers[NATIVE_DIRECT_ROUTE_REQUEST]=!0;return enterBunRouterContext(enhancedRequest,()=>{try{const response=handler(enhancedRequest);return response instanceof Response?finalizeResponse(applyDirectNativeCookies(nativeRouter,response,enhancedRequest),request):response.then((result)=>finalizeResponse(applyDirectNativeCookies(nativeRouter,result,enhancedRequest),request),(error)=>{const result=handleDirectNativeRouteError(nativeRouter,error);return result instanceof Response?finalizeResponse(result,request):result.then((response)=>finalizeResponse(response,request))})}catch(error){const result=handleDirectNativeRouteError(nativeRouter,error);return result instanceof Response?finalizeResponse(result,request):result.then((response)=>finalizeResponse(response,request))}})})}return handlers}function wrapNativeRoutesForDatabaseContext(router,dispatchInRoutingContext){const nativeRouter=router;if(nativeRouter._nativeDatabaseContextWrapped||typeof nativeRouter._buildNativeRoutes!=="function")return;const buildNativeRoutes=nativeRouter._buildNativeRoutes.bind(nativeRouter);nativeRouter._buildNativeRoutes=()=>{const routes=buildNativeRoutes();if(!routes)return routes;const compression=nativeRouter.config.compression,compressionDisabled=compression?.enabled===!1,compressionThreshold=compression?.threshold??1024,finalizeResponse=(response,request)=>{const bodySizeUpperBound=response[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND];if(compressionDisabled||typeof bodySizeUpperBound==="number"&&bodySizeUpperBound<compressionThreshold)return response;if(bodySizeUpperBound===void 0){const declaredLength=response.headers.get("content-length");if(declaredLength!==null){const length=Number(declaredLength);if(Number.isSafeInteger(length)&&length>=0&&length<compressionThreshold)return response}}return applyResponseCompression(response,request,compression)},directHandlers=buildDirectNativeHandlers(router,finalizeResponse),secureTransport=secureNativeRouteRouters.has(router),csrfEnabled=csrfEnabledNativeRouteRouters.has(router);for(const[path,methods]of Object.entries(routes))for(const[method,handler]of Object.entries(methods)){const safeMethod=method==="GET"||method==="HEAD"||method==="OPTIONS",directDispatch=directHandlers.get(`${method}:${path}`)??(method==="HEAD"?directHandlers.get(`GET:${path}`):void 0);if(directDispatch){if(safeMethod&&csrfEnabled)methods[method]=(request)=>{const cookie=request.headers.get("cookie")??"",markedRequest=request;if(hasCsrfCookie(cookie))markedRequest[CSRF_SEEDED_BY_HANDLE_REQUEST]=!0;else markedRequest[CSRF_SECURE_TRANSPORT]=secureTransport;return dispatchInRoutingContext(directDispatch,request)};else methods[method]=(request)=>dispatchInRoutingContext(directDispatch,request);continue}methods[method]=(request)=>{if(safeMethod&&csrfEnabled){const cookie=request.headers.get("cookie")??"",markedRequest=request;if(hasCsrfCookie(cookie))markedRequest[CSRF_SEEDED_BY_HANDLE_REQUEST]=!0;else markedRequest[CSRF_SECURE_TRANSPORT]=secureTransport}const response=dispatchInRoutingContext(handler,request);return response instanceof Response?finalizeResponse(response,request):response.then((resolved)=>finalizeResponse(resolved,request))}}return routes};nativeRouter._nativeDatabaseContextWrapped=!0}async function wrapHandleRequestForDatabaseContext(router){if(databaseContextWrappedRouters.has(router))return;const dispatchInRoutingContext=await getRoutingContextDispatcher();if(databaseContextWrappedRouters.has(router))return;wrapNativeRoutesForDatabaseContext(router,dispatchInRoutingContext);const original=router.handleRequest.bind(router);router.handleRequest=(request)=>dispatchInRoutingContext(original,request);databaseContextWrappedRouters.add(router)}async function getRoutingContextDispatcher(){if(!routingContextDispatcher)try{let database=await import("@stacksjs/database/replicas");if(typeof database.runInDatabaseRoutingContext!=="function"&&typeof database.withDatabaseRoutingContext!=="function")database=await import("@stacksjs/database/runtime");const dispatcher=database.runInDatabaseRoutingContext,runner=database.withDatabaseRoutingContext??database.withRoutingContext;routingContextDispatcher=typeof dispatcher==="function"?dispatcher:typeof runner==="function"?(fn,arg)=>runner(()=>fn(arg)):(fn,arg)=>fn(arg)}catch{routingContextDispatcher=(fn,arg)=>fn(arg)}return routingContextDispatcher}export function serverResponse(request,_body){if(routingContextDispatcher)return routingContextDispatcher(handleServerRequest,request);return dispatchServerRequestAfterContextInit(request)}async function dispatchServerRequestAfterContextInit(request){return(await getRoutingContextDispatcher())(handleServerRequest,request)}let handleServerRequest=handleServerRequestBeforeRoutes;async function handleServerRequestBeforeRoutes(request){if(!routesLoadPromise){log.debug("[router] Loading routes for first time...");routesLoadPromise=route.importRoutes().catch((err)=>{routesLoadPromise=null;throw err})}await routesLoadPromise;handleServerRequest=handleLoadedServerRequest;return handleLoadedServerRequest(request)}async function handleLoadedServerRequest(request){const response=await route.handleRequest(request);if(response[ENRICHED_NOT_FOUND_RESPONSE]===!0)return response;if(response.status===404&&response.headers.get("content-type")?.includes("json"))try{const path=new URL(request.url).pathname,method=request.method,text=await response.clone().text();if(text===JSON.stringify({success:!1,message:"Not Found",path,method})||text===JSON.stringify({error:"Not Found",path,method}))return response;const body=JSON.parse(text);if(body?.message==="Not Found"||body?.error==="Not Found"){const enriched={...body,path,method};return new Response(JSON.stringify(enriched),{status:404,headers:response.headers})}}catch{}return response}export async function serve(options={}){return route.serve(options)}
6
+ `,error)}if(!ormRoutesLoaded)log.warn("[router] No ORM routes candidate loaded - model useApi endpoints are unavailable.");log.debug("[router] Loading discovered package routes...");try{await stacksRouter.loadDiscoveredRoutes()}catch(error){log.debug("Package route discovery skipped:",error)}await assertRouteMiddlewareResolvable()},async loadDiscoveredRoutes(){try{const manifestPath=storagePath("framework/discovered-packages.json"),file=Bun.file(manifestPath);if(!await file.exists())return;const packages=(await file.json())?.packages;if(!packages)return;for(const[pkgName,meta]of Object.entries(packages)){const routes=meta?.routes;if(!routes)continue;const routeList=Array.isArray(routes)?routes:[routes],pkgDir=meta?.root?projectPath(meta.root):`${projectPath("pantry")}/${pkgName}`;for(const routeFile of routeList){log.debug(`[router] Discovered route: ${pkgName} \u2192 ${routeFile}`);const fullPath=routeFile.startsWith("/")?routeFile:`${pkgDir}/${routeFile}`,prefix=meta?.routePrefix,middleware=meta?.routeMiddleware;try{await stacksRouter.register(fullPath,{prefix,middleware})}catch(err){log.warn(`Failed to load routes from package '${pkgName}': ${err}`)}}}}catch{}}};return stacksRouter}function wrapHandleRequestForCsrf(bunRouter){const router=bunRouter;if(router._csrfSeedingWrapped)return;const original=router.handleRequest.bind(router),handleUnseededSafeRequest=async(request,rendersCsrf,cookieHeader)=>{if(rendersCsrf&&requestMayRenderHtml(request)){const seeding=seedCsrfTokenForRender(request,cookieHeader);if(seeding)await seeding}request[CSRF_SEEDED_BY_HANDLE_REQUEST]=!0;const response=await original(request);if(!response)return response;try{const mod=loadCsrfModule(),csrf=mod instanceof Promise?await mod:mod;if(!csrf)return response;return csrf.seedCsrfCookieIfMissing(request,response,request._csrfToken)}catch(err){log.warn("[router] CSRF cookie seeding failed on the view path",{error:err});return response}},handleSafeRequest=(request,rendersCsrf)=>{const cookieHeader=request.headers.get("cookie")??"";if(hasCsrfCookie(cookieHeader)){if(rendersCsrf&&requestMayRenderHtml(request)){const seeding=seedCsrfTokenForRender(request,cookieHeader);if(seeding)return seeding.then(()=>{request[CSRF_SEEDED_BY_HANDLE_REQUEST]=!0;return original(request)})}request[CSRF_SEEDED_BY_HANDLE_REQUEST]=!0;return original(request)}return handleUnseededSafeRequest(request,rendersCsrf,cookieHeader)};router.handleRequest=(request)=>{const method=request.method;if(method==="GET"||method==="HEAD")return handleSafeRequest(request,!0);if(method==="OPTIONS")return handleSafeRequest(request,!1);return original(request)};router._csrfSeedingWrapped=!0}export function configureViewDirectories(bunRouter){const router=bunRouter;if(typeof router.views!=="function")return;const existing=router._fileRoutingConfig;if(existing&&Object.keys(existing).length>0)return;if(!existsSync(projectPath("resources/views")))return;const layouts=[projectPath("resources/views/layouts"),projectPath("resources/layouts")].find(existsSync),partials=[projectPath("resources/views/partials"),projectPath("resources/partials")].find(existsSync),components=[projectPath("resources/components"),projectPath("resources/views/components")].find(existsSync);router.views({...components?{componentsDir:components}:{},...layouts?{layoutsDir:layouts}:{},...partials?{partialsDir:partials}:{}})}export function disableViewRouting(bunRouter){const router=bunRouter;if(typeof router.disableFileRouting!=="function")return!1;const existing=router._fileRoutingConfig;if(existing&&Object.keys(existing).length>0)return!1;router.disableFileRouting();return!0}const ROUTE_SINGLETON_KEY=Symbol.for("@stacksjs/router:route-singleton");export const route=globalThis[ROUTE_SINGLETON_KEY]??=createStacksRouter();let routesLoadPromise=null,routingContextDispatcher=null;const databaseContextWrappedRouters=new WeakSet;function applyDirectNativeCookies(router,response,request){const cookieChanges=request;return cookieChanges._cookiesToSet?.length||cookieChanges._cookiesToDelete?.length?router.applyModifiedCookies(response,request):response}function nativeRouteErrorResponse(error){return new Response(JSON.stringify({success:!1,message:"Internal Server Error",error:error instanceof Error?error.message:String(error)}),{status:500,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*","Access-Control-Allow-Methods":"GET, POST, PUT, DELETE, PATCH, OPTIONS","Access-Control-Allow-Headers":"Content-Type, Authorization, X-Requested-With, Accept, Origin"}})}function handleDirectNativeRouteError(router,error){console.error("Error handling request:",error);return router.errorHandler?router.errorHandler(error instanceof Error?error:Error(String(error))):nativeRouteErrorResponse(error)}function directNativePath(path){const segments=path.split("/");for(let index=0;index<segments.length;index++){const segment=segments[index];if(!segment)continue;const parameter=segment.match(/^\{([A-Z_$][\w$]*)\}$/i);if(parameter){segments[index]=`:${parameter[1]}`;continue}if(segment.includes("{")||segment.includes(":")||segment.includes("*"))return}return segments.join("/")}function buildDirectNativeHandlers(router,finalizeResponse){const nativeRouter=router,handlers=new Map;if(nativeRouter.globalMiddleware.length!==0)return handlers;const enterBunRouterContext=routersWithoutRequestContext.has(router)?(_req,fn)=>fn():runWithBunRouterRequest;for(const route of nativeRouter.routes){if(!isNativeRouteMethod(route.method)||route.nativeDispatch===!1||route.constraints&&Object.keys(route.constraints).length>0||(route.middleware?.length??0)!==0||typeof route.handler!=="function"||!stacksRouteHandlers.has(route.handler))continue;const handler=route.handler,nativePath=directNativePath(route.path);if(!nativePath)continue;const key=`${route.method}:${nativePath}`;if(handlers.has(key))continue;handlers.set(key,(request)=>{const nativeParams=request.params??{},enhancedRequest=nativeRouter.enhanceRequest(request,nativeParams);enhancedRequest.route=route;const requestMarkers=enhancedRequest;if(typeof requestMarkers[CSRF_SECURE_TRANSPORT]!=="boolean")requestMarkers[NATIVE_DIRECT_ROUTE_REQUEST]=!0;return enterBunRouterContext(enhancedRequest,()=>{try{const response=handler(enhancedRequest);return response instanceof Response?finalizeResponse(applyDirectNativeCookies(nativeRouter,response,enhancedRequest),request):response.then((result)=>finalizeResponse(applyDirectNativeCookies(nativeRouter,result,enhancedRequest),request),(error)=>{const result=handleDirectNativeRouteError(nativeRouter,error);return result instanceof Response?finalizeResponse(result,request):result.then((response)=>finalizeResponse(response,request))})}catch(error){const result=handleDirectNativeRouteError(nativeRouter,error);return result instanceof Response?finalizeResponse(result,request):result.then((response)=>finalizeResponse(response,request))}})})}return handlers}function wrapNativeRoutesForDatabaseContext(router,dispatchInRoutingContext){const nativeRouter=router;if(nativeRouter._nativeDatabaseContextWrapped||typeof nativeRouter._buildNativeRoutes!=="function")return;const buildNativeRoutes=nativeRouter._buildNativeRoutes.bind(nativeRouter);nativeRouter._buildNativeRoutes=()=>{const routes=buildNativeRoutes();if(!routes)return routes;const compression=nativeRouter.config.compression,compressionDisabled=compression?.enabled===!1,compressionThreshold=compression?.threshold??1024,finalizeResponse=(response,request)=>{const bodySizeUpperBound=response[FRAMEWORK_RESPONSE_BODY_SIZE_UPPER_BOUND];if(compressionDisabled||typeof bodySizeUpperBound==="number"&&bodySizeUpperBound<compressionThreshold)return response;if(bodySizeUpperBound===void 0){const declaredLength=response.headers.get("content-length");if(declaredLength!==null){const length=Number(declaredLength);if(Number.isSafeInteger(length)&&length>=0&&length<compressionThreshold)return response}}return applyResponseCompression(response,request,compression)},directHandlers=buildDirectNativeHandlers(router,finalizeResponse),secureTransport=secureNativeRouteRouters.has(router),csrfEnabled=csrfEnabledNativeRouteRouters.has(router);for(const[path,methods]of Object.entries(routes))for(const[method,handler]of Object.entries(methods)){const safeMethod=method==="GET"||method==="HEAD"||method==="OPTIONS",directDispatch=directHandlers.get(`${method}:${path}`)??(method==="HEAD"?directHandlers.get(`GET:${path}`):void 0);if(directDispatch){if(safeMethod&&csrfEnabled)methods[method]=(request)=>{const cookie=request.headers.get("cookie")??"",markedRequest=request;if(hasCsrfCookie(cookie))markedRequest[CSRF_SEEDED_BY_HANDLE_REQUEST]=!0;else markedRequest[CSRF_SECURE_TRANSPORT]=secureTransport;return dispatchInRoutingContext(directDispatch,request)};else methods[method]=(request)=>dispatchInRoutingContext(directDispatch,request);continue}methods[method]=(request)=>{if(safeMethod&&csrfEnabled){const cookie=request.headers.get("cookie")??"",markedRequest=request;if(hasCsrfCookie(cookie))markedRequest[CSRF_SEEDED_BY_HANDLE_REQUEST]=!0;else markedRequest[CSRF_SECURE_TRANSPORT]=secureTransport}const response=dispatchInRoutingContext(handler,request);return response instanceof Response?finalizeResponse(response,request):response.then((resolved)=>finalizeResponse(resolved,request))}}return routes};nativeRouter._nativeDatabaseContextWrapped=!0}async function wrapHandleRequestForDatabaseContext(router){if(databaseContextWrappedRouters.has(router))return;const dispatchInRoutingContext=await getRoutingContextDispatcher();if(databaseContextWrappedRouters.has(router))return;wrapNativeRoutesForDatabaseContext(router,dispatchInRoutingContext);const original=router.handleRequest.bind(router);router.handleRequest=(request)=>dispatchInRoutingContext(original,request);databaseContextWrappedRouters.add(router)}async function getRoutingContextDispatcher(){if(!routingContextDispatcher)try{let database=await import("@stacksjs/database/replicas");if(typeof database.runInDatabaseRoutingContext!=="function"&&typeof database.withDatabaseRoutingContext!=="function")database=await import("@stacksjs/database/runtime");const dispatcher=database.runInDatabaseRoutingContext,runner=database.withDatabaseRoutingContext??database.withRoutingContext;routingContextDispatcher=typeof dispatcher==="function"?dispatcher:typeof runner==="function"?(fn,arg)=>runner(()=>fn(arg)):(fn,arg)=>fn(arg)}catch{routingContextDispatcher=(fn,arg)=>fn(arg)}return routingContextDispatcher}export function serverResponse(request,_body){if(routingContextDispatcher)return routingContextDispatcher(handleServerRequest,request);return dispatchServerRequestAfterContextInit(request)}async function dispatchServerRequestAfterContextInit(request){return(await getRoutingContextDispatcher())(handleServerRequest,request)}let handleServerRequest=handleServerRequestBeforeRoutes;async function handleServerRequestBeforeRoutes(request){if(!routesLoadPromise){log.debug("[router] Loading routes for first time...");routesLoadPromise=route.importRoutes().catch((err)=>{routesLoadPromise=null;throw err})}await routesLoadPromise;handleServerRequest=handleLoadedServerRequest;return handleLoadedServerRequest(request)}async function handleLoadedServerRequest(request){const response=await route.handleRequest(request);if(response[ENRICHED_NOT_FOUND_RESPONSE]===!0)return response;if(response.status===404&&response.headers.get("content-type")?.includes("json"))try{const path=new URL(request.url).pathname,method=request.method,text=await response.clone().text();if(text===JSON.stringify({success:!1,message:"Not Found",path,method})||text===JSON.stringify({error:"Not Found",path,method}))return response;const body=JSON.parse(text);if(body?.message==="Not Found"||body?.error==="Not Found"){const enriched={...body,path,method};return new Response(JSON.stringify(enriched),{status:404,headers:response.headers})}}catch{}return response}export async function serve(options={}){return route.serve(options)}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/router",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.27",
5
+ "version": "0.75.29",
6
6
  "description": "The Stacks framework router.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -67,27 +67,27 @@
67
67
  },
68
68
  "dependencies": {
69
69
  "@stacksjs/bun-router": "^0.1.20",
70
- "@stacksjs/cache": "0.75.27",
71
- "@stacksjs/collections": "0.75.27",
72
- "@stacksjs/config": "0.75.27",
73
- "@stacksjs/error-handling": "0.75.27",
74
- "@stacksjs/logging": "0.75.27",
75
- "@stacksjs/pagination": "0.75.27",
76
- "@stacksjs/path": "0.75.27",
77
- "@stacksjs/security": "0.75.27",
78
- "@stacksjs/storage": "0.75.27",
70
+ "@stacksjs/cache": "0.75.29",
71
+ "@stacksjs/collections": "0.75.29",
72
+ "@stacksjs/config": "0.75.29",
73
+ "@stacksjs/error-handling": "0.75.29",
74
+ "@stacksjs/logging": "0.75.29",
75
+ "@stacksjs/pagination": "0.75.29",
76
+ "@stacksjs/path": "0.75.29",
77
+ "@stacksjs/security": "0.75.29",
78
+ "@stacksjs/storage": "0.75.29",
79
79
  "ts-rate-limiter": "^0.4.10"
80
80
  },
81
81
  "devDependencies": {
82
- "@stacksjs/actions": "0.75.27",
83
- "@stacksjs/types": "0.75.27",
82
+ "@stacksjs/actions": "0.75.29",
83
+ "@stacksjs/types": "0.75.29",
84
84
  "better-dx": "^0.2.24"
85
85
  },
86
86
  "peerDependencies": {
87
- "@stacksjs/api": "0.75.27",
88
- "@stacksjs/auth": "0.75.27",
89
- "@stacksjs/database": "0.75.27",
90
- "@stacksjs/validation": "0.75.27"
87
+ "@stacksjs/api": "0.75.29",
88
+ "@stacksjs/auth": "0.75.29",
89
+ "@stacksjs/database": "0.75.29",
90
+ "@stacksjs/validation": "0.75.29"
91
91
  },
92
92
  "peerDependenciesMeta": {
93
93
  "@stacksjs/api": {