@@ -22,6 +22,75 @@ interface RouteDefinition {
2222 handler : ( req : any ) => Promise < any > ;
2323}
2424
25+ /**
26+ * Register a single RouteDefinition on the HTTP server.
27+ * Returns true if the route was successfully registered.
28+ */
29+ function mountRouteOnServer ( route : RouteDefinition , server : IHttpServer , routePath : string ) : boolean {
30+ const handler = async ( req : any , res : any ) => {
31+ try {
32+ const result = await route . handler ( {
33+ body : req . body ,
34+ params : req . params ,
35+ query : req . query ,
36+ } ) ;
37+
38+ if ( result . stream && result . events ) {
39+ // SSE streaming response
40+ res . status ( result . status ) ;
41+
42+ // Apply headers from the route result if available
43+ if ( result . headers ) {
44+ for ( const [ k , v ] of Object . entries ( result . headers ) ) {
45+ res . header ( k , v as string ) ;
46+ }
47+ } else {
48+ res . header ( 'Content-Type' , 'text/event-stream' ) ;
49+ res . header ( 'Cache-Control' , 'no-cache' ) ;
50+ res . header ( 'Connection' , 'keep-alive' ) ;
51+ }
52+
53+ // Write the stream — events are pre-encoded SSE strings
54+ if ( typeof res . write === 'function' && typeof res . end === 'function' ) {
55+ for await ( const event of result . events ) {
56+ res . write ( typeof event === 'string' ? event : `data: ${ JSON . stringify ( event ) } \n\n` ) ;
57+ }
58+ res . end ( ) ;
59+ } else {
60+ // Fallback: collect events into array
61+ const events = [ ] ;
62+ for await ( const event of result . events ) {
63+ events . push ( event ) ;
64+ }
65+ res . json ( { events } ) ;
66+ }
67+ } else {
68+ res . status ( result . status ) ;
69+ if ( result . body !== undefined ) {
70+ res . json ( result . body ) ;
71+ } else {
72+ res . end ( ) ;
73+ }
74+ }
75+ } catch ( err : any ) {
76+ errorResponse ( err , res ) ;
77+ }
78+ } ;
79+
80+ const m = route . method . toLowerCase ( ) ;
81+ if ( m === 'get' && typeof server . get === 'function' ) {
82+ server . get ( routePath , handler ) ;
83+ return true ;
84+ } else if ( m === 'post' && typeof server . post === 'function' ) {
85+ server . post ( routePath , handler ) ;
86+ return true ;
87+ } else if ( m === 'delete' && typeof server . delete === 'function' ) {
88+ server . delete ( routePath , handler ) ;
89+ return true ;
90+ }
91+ return false ;
92+ }
93+
2594/**
2695 * Send an HttpDispatcherResult through IHttpResponse.
2796 * Differentiates between handled, unhandled (404), and special results.
@@ -402,65 +471,7 @@ export function createDispatcherPlugin(config: DispatcherPluginConfig = {}): Plu
402471 const routePath = route . path . startsWith ( '/api/v1' )
403472 ? route . path
404473 : `${ prefix } ${ route . path } ` ;
405-
406- const handler = async ( req : any , res : any ) => {
407- try {
408- const result = await route . handler ( {
409- body : req . body ,
410- params : req . params ,
411- query : req . query ,
412- } ) ;
413-
414- if ( result . stream && result . events ) {
415- // SSE streaming response
416- res . status ( result . status ) ;
417-
418- // Apply headers from the route result if available
419- if ( result . headers ) {
420- for ( const [ k , v ] of Object . entries ( result . headers ) ) {
421- res . header ( k , v ) ;
422- }
423- } else {
424- res . header ( 'Content-Type' , 'text/event-stream' ) ;
425- res . header ( 'Cache-Control' , 'no-cache' ) ;
426- res . header ( 'Connection' , 'keep-alive' ) ;
427- }
428-
429- // Write the stream — events are pre-encoded SSE strings
430- if ( typeof res . write === 'function' && typeof res . end === 'function' ) {
431- for await ( const event of result . events ) {
432- res . write ( typeof event === 'string' ? event : `data: ${ JSON . stringify ( event ) } \n\n` ) ;
433- }
434- res . end ( ) ;
435- } else {
436- // Fallback: collect events into array
437- const events = [ ] ;
438- for await ( const event of result . events ) {
439- events . push ( event ) ;
440- }
441- res . json ( { events } ) ;
442- }
443- } else {
444- res . status ( result . status ) ;
445- if ( result . body !== undefined ) {
446- res . json ( result . body ) ;
447- } else {
448- res . end ( ) ;
449- }
450- }
451- } catch ( err : any ) {
452- errorResponse ( err , res ) ;
453- }
454- } ;
455-
456- const m = route . method . toLowerCase ( ) ;
457- if ( m === 'get' && typeof server . get === 'function' ) {
458- server . get ( routePath , handler ) ;
459- } else if ( m === 'post' && typeof server . post === 'function' ) {
460- server . post ( routePath , handler ) ;
461- } else if ( m === 'delete' && typeof server . delete === 'function' ) {
462- server . delete ( routePath , handler ) ;
463- }
474+ mountRouteOnServer ( route , server , routePath ) ;
464475 }
465476 ctx . logger . info ( `[Dispatcher] Registered ${ routes . length } AI routes` ) ;
466477 } ) ;
@@ -477,60 +488,7 @@ export function createDispatcherPlugin(config: DispatcherPluginConfig = {}): Plu
477488 const routePath = route . path . startsWith ( '/api/v1' )
478489 ? route . path
479490 : `${ prefix } ${ route . path } ` ;
480-
481- const handler = async ( req : any , res : any ) => {
482- try {
483- const result = await route . handler ( {
484- body : req . body ,
485- params : req . params ,
486- query : req . query ,
487- } ) ;
488-
489- if ( result . stream && result . events ) {
490- res . status ( result . status ) ;
491- if ( result . headers ) {
492- for ( const [ k , v ] of Object . entries ( result . headers ) ) {
493- res . header ( k , v as string ) ;
494- }
495- } else {
496- res . header ( 'Content-Type' , 'text/event-stream' ) ;
497- res . header ( 'Cache-Control' , 'no-cache' ) ;
498- res . header ( 'Connection' , 'keep-alive' ) ;
499- }
500- if ( typeof res . write === 'function' && typeof res . end === 'function' ) {
501- for await ( const event of result . events ) {
502- res . write ( typeof event === 'string' ? event : `data: ${ JSON . stringify ( event ) } \n\n` ) ;
503- }
504- res . end ( ) ;
505- } else {
506- const events = [ ] ;
507- for await ( const event of result . events ) {
508- events . push ( event ) ;
509- }
510- res . json ( { events } ) ;
511- }
512- } else {
513- res . status ( result . status ) ;
514- if ( result . body !== undefined ) {
515- res . json ( result . body ) ;
516- } else {
517- res . end ( ) ;
518- }
519- }
520- } catch ( err : any ) {
521- errorResponse ( err , res ) ;
522- }
523- } ;
524-
525- const m = route . method . toLowerCase ( ) ;
526- if ( m === 'get' && typeof server . get === 'function' ) {
527- server . get ( routePath , handler ) ;
528- registered ++ ;
529- } else if ( m === 'post' && typeof server . post === 'function' ) {
530- server . post ( routePath , handler ) ;
531- registered ++ ;
532- } else if ( m === 'delete' && typeof server . delete === 'function' ) {
533- server . delete ( routePath , handler ) ;
491+ if ( mountRouteOnServer ( route , server , routePath ) ) {
534492 registered ++ ;
535493 }
536494 }
0 commit comments