@@ -604,6 +604,20 @@ const routeActionRegistry = new Map<string, RouterAction>()
604604const CSRF_PROTECTED_METHODS = new Set ( [ 'POST' , 'PUT' , 'PATCH' , 'DELETE' ] )
605605const CSRF_SEEDED_BY_HANDLE_REQUEST = Symbol . for ( 'stacks.router.csrfSeededByHandleRequest' )
606606
607+ interface ResolvedMiddleware {
608+ name : string
609+ handler : MiddlewareHandler
610+ priority : number
611+ }
612+
613+ interface MiddlewareTiming {
614+ name : string
615+ ms : number
616+ }
617+
618+ const EMPTY_RESOLVED_MIDDLEWARE : ResolvedMiddleware [ ] = [ ]
619+ const EMPTY_MIDDLEWARE_TIMINGS : MiddlewareTiming [ ] = [ ]
620+
607621/**
608622 * Named route registry — keeps the original path plus the precomputed
609623 * placeholder names and a per-param replacement regex so `url()` can
@@ -1671,12 +1685,9 @@ function createMiddlewareHandler(routeKey: string, handler: StacksHandler): Rout
16711685 // contract requiring CORS to precede auth/throttle so 4xx
16721686 // responses still carry the right headers. See
16731687 // stacksjs/stacks#1863, #1859 (H-1).
1674- interface ResolvedMiddleware {
1675- name : string
1676- handler : MiddlewareHandler
1677- priority : number
1678- }
1679- const resolved : ResolvedMiddleware [ ] = [ ]
1688+ const resolved : ResolvedMiddleware [ ] = middlewareEntries . length === 0
1689+ ? EMPTY_RESOLVED_MIDDLEWARE
1690+ : [ ]
16801691 for ( const middlewareEntry of middlewareEntries ) {
16811692 const parsed = await parseMiddlewareEntry ( middlewareEntry )
16821693 const { name : middlewareName , params } = parsed
@@ -1725,10 +1736,13 @@ function createMiddlewareHandler(routeKey: string, handler: StacksHandler): Rout
17251736 // Stable sort — V8 + Bun guarantee Array.sort is stable since 2018,
17261737 // so same-priority entries preserve insertion order. This keeps
17271738 // declared sequencing within a priority band predictable.
1728- resolved . sort ( ( a , b ) => a . priority - b . priority )
1739+ if ( resolved . length > 1 )
1740+ resolved . sort ( ( a , b ) => a . priority - b . priority )
17291741
17301742 // Run middleware in priority order
1731- const middlewareTimings : Array < { name : string , ms : number } > = [ ]
1743+ const middlewareTimings : MiddlewareTiming [ ] = resolved . length === 0
1744+ ? EMPTY_MIDDLEWARE_TIMINGS
1745+ : [ ]
17321746
17331747 /*
17341748 * One budget for the chain, armed at most once per request.
@@ -1749,19 +1763,22 @@ function createMiddlewareHandler(routeKey: string, handler: StacksHandler): Rout
17491763 let chainTimer : ReturnType < typeof setTimeout > | undefined
17501764 let chainBudget : Promise < never > | undefined
17511765 let runningMiddleware = ''
1752- const armChainBudget = ( ) : Promise < never > => {
1753- if ( ! chainBudget ) {
1754- chainBudget = new Promise < never > ( ( _ , reject ) => {
1755- chainTimer = setTimeout (
1756- ( ) => reject ( new Error ( `Middleware '${ runningMiddleware } ' exceeded ${ MIDDLEWARE_TIMEOUT_MS } ms` ) ) ,
1757- MIDDLEWARE_TIMEOUT_MS ,
1758- )
1759- } )
1760- // Marks the budget handled so a chain that finishes normally does not
1761- // leave an unhandled rejection behind when the timer eventually fires.
1762- chainBudget . catch ( ( ) => { } )
1766+ let armChainBudget : ( ( ) => Promise < never > ) | undefined
1767+ if ( resolved . length > 0 ) {
1768+ armChainBudget = ( ) : Promise < never > => {
1769+ if ( ! chainBudget ) {
1770+ chainBudget = new Promise < never > ( ( _ , reject ) => {
1771+ chainTimer = setTimeout (
1772+ ( ) => reject ( new Error ( `Middleware '${ runningMiddleware } ' exceeded ${ MIDDLEWARE_TIMEOUT_MS } ms` ) ) ,
1773+ MIDDLEWARE_TIMEOUT_MS ,
1774+ )
1775+ } )
1776+ // Marks the budget handled so a chain that finishes normally does not
1777+ // leave an unhandled rejection behind when the timer eventually fires.
1778+ chainBudget . catch ( ( ) => { } )
1779+ }
1780+ return chainBudget
17631781 }
1764- return chainBudget
17651782 }
17661783
17671784 try {
@@ -1776,7 +1793,7 @@ function createMiddlewareHandler(routeKey: string, handler: StacksHandler): Rout
17761793 const outcome = middleware . handle ( enhancedReq )
17771794 // Nothing to time out when the layer already finished.
17781795 if ( outcome && typeof ( outcome as Promise < void > ) . then === 'function' )
1779- await Promise . race ( [ outcome as Promise < void > , armChainBudget ( ) ] )
1796+ await Promise . race ( [ outcome as Promise < void > , armChainBudget ! ( ) ] )
17801797 const elapsedMs = Number ( process . hrtime . bigint ( ) - mwStart ) / 1_000_000
17811798 middlewareTimings . push ( { name : middlewareName , ms : elapsedMs } )
17821799 }
@@ -1846,7 +1863,8 @@ function createMiddlewareHandler(routeKey: string, handler: StacksHandler): Rout
18461863 finally {
18471864 // One timer, cleared on every way out of the chain - including the
18481865 // early returns above - so a settled request leaves nothing pending.
1849- clearTimeout ( chainTimer )
1866+ if ( chainTimer )
1867+ clearTimeout ( chainTimer )
18501868 }
18511869
18521870 // Call the actual handler with the enhanced request.
0 commit comments