fix: stabilize worker console refresh

This commit is contained in:
Keisuke Hirata 2026-06-27 03:18:26 +09:00
parent c3fed59109
commit a1083908b6
No known key found for this signature in database
2 changed files with 49 additions and 16 deletions

View File

@ -57,7 +57,7 @@ Deno.test("Worker Console page is routed by runtime_id and worker_id through bac
);
assert(
consolePage.includes(
"/api/runtimes/${encodeURIComponent(runtimeId)}/workers/${encodeURIComponent(workerId)}",
"/api/runtimes/${encodeURIComponent(target.runtimeId)}/workers/${encodeURIComponent(target.workerId)}",
),
"Worker detail should use the backend Worker detail API",
);
@ -75,4 +75,17 @@ Deno.test("Worker Console page is routed by runtime_id and worker_id through bac
consolePage.includes("Streaming observation is not available"),
"Console should show an explicit non-streaming degradation path",
);
assert(
consolePage.includes("function advanceReloadToken()") &&
consolePage.includes("nextReloadToken += 1") &&
!consolePage.includes("reloadToken += 1"),
"reload token advancement should not synchronously read and write the rune state",
);
assert(
consolePage.includes(
"advanceReloadToken();\n void loadConsoleData(target);",
) &&
!consolePage.includes("void refreshConsole();\n });\n\n $effect"),
"target-change effect should load data without depending on manual refresh state reads",
);
});

View File

@ -39,7 +39,15 @@
let streamState = $state<'connecting' | 'open' | 'unsupported' | 'closed' | 'error'>('connecting');
let streamDiagnostics = $state<Diagnostic[]>([]);
let observedEvents = $state<Array<{ cursor: string; event: ClientWorkerEventWsFrame & { kind: 'event' } }>>([]);
let reloadToken = 0;
let nextReloadToken = 0;
let reloadToken = $state(0);
type ConsoleTarget = {
runtimeId: string;
workerId: string;
};
const consoleTarget = $derived({ runtimeId, workerId });
const projection = $derived(
projectConsole(
@ -101,11 +109,11 @@
}
}
async function loadWorker() {
async function loadWorker(target: ConsoleTarget) {
workerError = null;
try {
worker = await getJson<Worker>(
`/api/runtimes/${encodeURIComponent(runtimeId)}/workers/${encodeURIComponent(workerId)}`
`/api/runtimes/${encodeURIComponent(target.runtimeId)}/workers/${encodeURIComponent(target.workerId)}`
);
} catch (error) {
workerError = error instanceof Error ? error.message : String(error);
@ -113,11 +121,11 @@
}
}
async function loadTranscript() {
async function loadTranscript(target: ConsoleTarget) {
transcriptError = null;
try {
transcript = await getJson<WorkerTranscriptProjection>(
`/api/runtimes/${encodeURIComponent(runtimeId)}/workers/${encodeURIComponent(workerId)}/transcript?limit=200`
`/api/runtimes/${encodeURIComponent(target.runtimeId)}/workers/${encodeURIComponent(target.workerId)}/transcript?limit=200`
);
} catch (error) {
transcriptError = error instanceof Error ? error.message : String(error);
@ -125,9 +133,19 @@
}
}
async function loadConsoleData(target: ConsoleTarget) {
await Promise.all([loadWorker(target), loadTranscript(target)]);
}
function advanceReloadToken(): number {
nextReloadToken += 1;
reloadToken = nextReloadToken;
return nextReloadToken;
}
async function refreshConsole() {
reloadToken += 1;
await Promise.all([loadWorker(), loadTranscript()]);
advanceReloadToken();
await loadConsoleData(consoleTarget);
}
async function sendMessage(event: SubmitEvent) {
@ -149,7 +167,7 @@
} else {
sendError = diagnosticsToText(result.diagnostics) || `Input was ${result.state}.`;
}
await loadTranscript();
await loadTranscript(consoleTarget);
} catch (error) {
sendError = error instanceof Error ? error.message : String(error);
} finally {
@ -157,12 +175,12 @@
}
}
function connectObservation(target: Worker | null, token: number) {
if (!target) {
function connectObservation(targetWorker: Worker | null, token: number, target: ConsoleTarget) {
if (!targetWorker) {
streamState = 'closed';
return;
}
if (!target.capabilities.can_stream_events) {
if (!targetWorker.capabilities.can_stream_events) {
streamState = 'unsupported';
streamDiagnostics = [
{
@ -177,8 +195,8 @@
streamState = 'connecting';
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const ws = new WebSocket(
`${protocol}//${window.location.host}/api/runtimes/${encodeURIComponent(runtimeId)}/workers/${encodeURIComponent(
workerId
`${protocol}//${window.location.host}/api/runtimes/${encodeURIComponent(target.runtimeId)}/workers/${encodeURIComponent(
target.workerId
)}/events/ws`
);
@ -261,12 +279,14 @@
});
$effect(() => {
const target = consoleTarget;
observedEvents = [];
streamDiagnostics = [];
void refreshConsole();
advanceReloadToken();
void loadConsoleData(target);
});
$effect(() => connectObservation(worker, reloadToken));
$effect(() => connectObservation(worker, reloadToken, consoleTarget));
</script>
<svelte:head>