Skip to content

Commit e75eb0b

Browse files
Copilothotlong
andauthored
refactor: extract mountRouteOnServer helper to eliminate duplicate route registration logic
Agent-Logs-Url: https://github.com/objectstack-ai/framework/sessions/35f9d46b-5132-451b-89f4-7dbee7066a1f Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 9e0a502 commit e75eb0b

File tree

1 file changed

+71
-113
lines changed

1 file changed

+71
-113
lines changed

packages/runtime/src/dispatcher-plugin.ts

Lines changed: 71 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)