mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
fix(server): keep external annotation SSE streams alive (#439)
This commit is contained in:
@@ -127,7 +127,7 @@ export async function startAnnotateServer(
|
||||
server = Bun.serve({
|
||||
port: configuredPort,
|
||||
|
||||
async fetch(req) {
|
||||
async fetch(req, server) {
|
||||
const url = new URL(req.url);
|
||||
|
||||
// API: Get plan content (reuse /api/plan so the plan editor UI works)
|
||||
@@ -195,7 +195,9 @@ export async function startAnnotateServer(
|
||||
}
|
||||
|
||||
// API: External annotations (SSE-based, for any external tool)
|
||||
const externalResponse = await externalAnnotations.handle(req, url);
|
||||
const externalResponse = await externalAnnotations.handle(req, url, {
|
||||
disableIdleTimeout: () => server.timeout(req, 0),
|
||||
});
|
||||
if (externalResponse) return externalResponse;
|
||||
|
||||
// API: Submit annotation feedback
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { describe, expect, test, mock } from "bun:test";
|
||||
import { createExternalAnnotationHandler } from "./external-annotations";
|
||||
|
||||
describe("external annotations SSE", () => {
|
||||
test("disables idle timeout for stream requests", async () => {
|
||||
const handler = createExternalAnnotationHandler("plan");
|
||||
const disableIdleTimeout = mock(() => {});
|
||||
|
||||
const res = await handler.handle(
|
||||
new Request("http://localhost/api/external-annotations/stream"),
|
||||
new URL("http://localhost/api/external-annotations/stream"),
|
||||
{ disableIdleTimeout },
|
||||
);
|
||||
|
||||
expect(disableIdleTimeout).toHaveBeenCalledTimes(1);
|
||||
expect(res?.headers.get("content-type")).toBe("text/event-stream");
|
||||
});
|
||||
});
|
||||
@@ -28,7 +28,11 @@ export type { ExternalAnnotationEvent } from "@plannotator/shared/external-annot
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface ExternalAnnotationHandler {
|
||||
handle: (req: Request, url: URL) => Promise<Response | null>;
|
||||
handle: (
|
||||
req: Request,
|
||||
url: URL,
|
||||
options?: { disableIdleTimeout?: () => void },
|
||||
) => Promise<Response | null>;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -64,9 +68,15 @@ export function createExternalAnnotationHandler(
|
||||
});
|
||||
|
||||
return {
|
||||
async handle(req: Request, url: URL): Promise<Response | null> {
|
||||
async handle(
|
||||
req: Request,
|
||||
url: URL,
|
||||
options?: { disableIdleTimeout?: () => void },
|
||||
): Promise<Response | null> {
|
||||
// --- SSE stream ---
|
||||
if (url.pathname === STREAM && req.method === "GET") {
|
||||
options?.disableIdleTimeout?.();
|
||||
|
||||
let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
||||
let ctrl: ReadableStreamDefaultController;
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ export async function startPlannotatorServer(
|
||||
server = Bun.serve({
|
||||
port: configuredPort,
|
||||
|
||||
async fetch(req) {
|
||||
async fetch(req, server) {
|
||||
const url = new URL(req.url);
|
||||
|
||||
// API: Get a specific plan version from history
|
||||
@@ -367,7 +367,9 @@ export async function startPlannotatorServer(
|
||||
if (editorResponse) return editorResponse;
|
||||
|
||||
// API: External annotations (SSE-based, for any external tool)
|
||||
const externalResponse = await externalAnnotations?.handle(req, url);
|
||||
const externalResponse = await externalAnnotations?.handle(req, url, {
|
||||
disableIdleTimeout: () => server.timeout(req, 0),
|
||||
});
|
||||
if (externalResponse) return externalResponse;
|
||||
|
||||
// API: Save to notes (decoupled from approve/deny)
|
||||
|
||||
@@ -248,7 +248,7 @@ export async function startReviewServer(
|
||||
server = Bun.serve({
|
||||
port: configuredPort,
|
||||
|
||||
async fetch(req) {
|
||||
async fetch(req, server) {
|
||||
const url = new URL(req.url);
|
||||
|
||||
// API: Get diff content
|
||||
@@ -447,7 +447,9 @@ export async function startReviewServer(
|
||||
if (editorResponse) return editorResponse;
|
||||
|
||||
// API: External annotations (SSE-based, for any external tool)
|
||||
const externalResponse = await externalAnnotations.handle(req, url);
|
||||
const externalResponse = await externalAnnotations.handle(req, url, {
|
||||
disableIdleTimeout: () => server.timeout(req, 0),
|
||||
});
|
||||
if (externalResponse) return externalResponse;
|
||||
|
||||
// API: Submit review feedback
|
||||
|
||||
Reference in New Issue
Block a user