@@ -395,6 +395,105 @@ describe('applyPatches', () => {
395395 } ) ;
396396 } ) ;
397397
398+ describe ( 'wrapSubAppMiddleware non-invasive patching' , ( ) => {
399+ it ( 'preserves symbol-keyed properties on sub-app middleware handlers' , async ( ) => {
400+ const app = new Hono ( ) ;
401+ applyPatches ( app ) ;
402+
403+ const OPENAPI = Symbol ( 'openapi' ) ;
404+ const META = Symbol ( 'meta' ) ;
405+ const middleware = async function authMiddleware ( _c : unknown , next : ( ) => Promise < void > ) {
406+ await next ( ) ;
407+ } ;
408+ ( middleware as any ) [ OPENAPI ] = { security : [ { bearer : [ ] } ] } ;
409+ ( middleware as any ) [ META ] = { rateLimit : 100 } ;
410+ ( middleware as any ) . customProp = 'preserved' ;
411+
412+ const subApp = new Hono ( ) ;
413+ subApp . use ( middleware ) ;
414+ subApp . get ( '/' , ( ) => new Response ( 'ok' ) ) ;
415+
416+ app . route ( '/api' , subApp ) ;
417+
418+ const route = ( subApp . routes as Array < { handler : Function } > ) . find (
419+ r => ( r . handler as any ) . __sentry_original__ === middleware || r . handler === middleware ,
420+ ) ;
421+ expect ( route ) . toBeDefined ( ) ;
422+
423+ const wrappedHandler = route ! . handler ;
424+ const symbols = Object . getOwnPropertySymbols ( wrappedHandler ) ;
425+ expect ( symbols ) . toContain ( OPENAPI ) ;
426+ expect ( symbols ) . toContain ( META ) ;
427+ expect ( ( wrappedHandler as any ) [ OPENAPI ] ) . toEqual ( { security : [ { bearer : [ ] } ] } ) ;
428+ expect ( ( wrappedHandler as any ) [ META ] ) . toEqual ( { rateLimit : 100 } ) ;
429+ expect ( ( wrappedHandler as any ) . customProp ) . toBe ( 'preserved' ) ;
430+ } ) ;
431+
432+ it ( 'preserves function.name on sub-app middleware after wrapping' , async ( ) => {
433+ const app = new Hono ( ) ;
434+ applyPatches ( app ) ;
435+
436+ const subApp = new Hono ( ) ;
437+ subApp . use ( async function corsMiddleware ( _c : unknown , next : ( ) => Promise < void > ) {
438+ await next ( ) ;
439+ } ) ;
440+ subApp . get ( '/' , ( ) => new Response ( 'ok' ) ) ;
441+
442+ app . route ( '/api' , subApp ) ;
443+
444+ const route = ( subApp . routes as Array < { handler : Function ; method : string } > ) . find (
445+ r => r . method === 'ALL' && r . handler . name === 'corsMiddleware' ,
446+ ) ;
447+ expect ( route ) . toBeDefined ( ) ;
448+ expect ( route ! . handler . name ) . toBe ( 'corsMiddleware' ) ;
449+ } ) ;
450+
451+ it ( 'preserves function.length on sub-app middleware after wrapping' , async ( ) => {
452+ const app = new Hono ( ) ;
453+ applyPatches ( app ) ;
454+
455+ const subApp = new Hono ( ) ;
456+ const mw = async function twoArgMw ( _c : unknown , next : ( ) => Promise < void > ) {
457+ await next ( ) ;
458+ } ;
459+ const originalLength = mw . length ;
460+ subApp . use ( mw ) ;
461+ subApp . get ( '/' , ( ) => new Response ( 'ok' ) ) ;
462+
463+ app . route ( '/api' , subApp ) ;
464+
465+ const route = ( subApp . routes as Array < { handler : Function ; method : string } > ) . find (
466+ r => r . method === 'ALL' && r . handler . length === originalLength ,
467+ ) ;
468+ expect ( route ) . toBeDefined ( ) ;
469+ expect ( route ! . handler . length ) . toBe ( originalLength ) ;
470+ } ) ;
471+
472+ it ( 'does not alter the behavior of sub-app route handlers (non-middleware)' , async ( ) => {
473+ const app = new Hono ( ) ;
474+ applyPatches ( app ) ;
475+
476+ const HANDLER_META = Symbol ( 'handler-meta' ) ;
477+ const handler = async function getItems ( ) {
478+ return new Response ( 'items' ) ;
479+ } ;
480+ ( handler as any ) [ HANDLER_META ] = { cached : true } ;
481+
482+ const subApp = new Hono ( ) ;
483+ subApp . get ( '/items' , handler ) ;
484+
485+ app . route ( '/api' , subApp ) ;
486+
487+ const route = ( subApp . routes as Array < { handler : Function ; path : string } > ) . find ( r => r . path === '/items' ) ;
488+ expect ( route ) . toBeDefined ( ) ;
489+ expect ( ( route ! . handler as any ) [ HANDLER_META ] ) . toEqual ( { cached : true } ) ;
490+
491+ const res = await app . fetch ( new Request ( 'http://localhost/api/items' ) ) ;
492+ expect ( res . status ) . toBe ( 200 ) ;
493+ expect ( await res . text ( ) ) . toBe ( 'items' ) ;
494+ } ) ;
495+ } ) ;
496+
398497 describe ( 'patchAppRequest integration' , ( ) => {
399498 it ( 'patches .request() on sub-apps when they are mounted via route()' , async ( ) => {
400499 const app = new Hono ( ) ;
0 commit comments