Skip to content

Commit 3ccfc8b

Browse files
committed
Merge branch 'wl-CG-0MLYUBJB61JHCDAW-dev-server-helpers' into main
2 parents 6d3c750 + c5cfe37 commit 3ccfc8b

2 files changed

Lines changed: 74 additions & 63 deletions

File tree

scripts/dev-server-utils.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Shared dev server management utilities.
3+
*
4+
* Provides helpers to detect, start, and stop the Vite dev server.
5+
* Used by the replay tool and CLI export scripts.
6+
*/
7+
8+
import { spawn } from 'node:child_process';
9+
import type { ChildProcess } from 'node:child_process';
10+
import http from 'node:http';
11+
12+
// ── Constants ───────────────────────────────────────────────
13+
14+
export const DEV_SERVER_URL = 'http://localhost:3000';
15+
export const DEV_SERVER_START_TIMEOUT = 30_000;
16+
17+
// ── Functions ───────────────────────────────────────────────
18+
19+
/** Check if a URL is reachable with an HTTP GET. */
20+
export function isServerReady(url: string): Promise<boolean> {
21+
return new Promise((resolve) => {
22+
const req = http.get(url, (res) => {
23+
res.resume(); // consume response body
24+
resolve(res.statusCode !== undefined && res.statusCode < 500);
25+
});
26+
req.on('error', () => resolve(false));
27+
req.setTimeout(2000, () => {
28+
req.destroy();
29+
resolve(false);
30+
});
31+
});
32+
}
33+
34+
/** Start the dev server if not already running. Returns the child process (or null). */
35+
export async function ensureDevServer(): Promise<ChildProcess | null> {
36+
const ready = await isServerReady(DEV_SERVER_URL);
37+
if (ready) {
38+
console.log('Dev server already running at', DEV_SERVER_URL);
39+
return null;
40+
}
41+
42+
console.log('Starting dev server (npm run dev)...');
43+
const child = spawn('npm', ['run', 'dev'], {
44+
stdio: ['ignore', 'pipe', 'pipe'],
45+
detached: false,
46+
});
47+
48+
// Wait for the server to become ready
49+
const start = Date.now();
50+
while (Date.now() - start < DEV_SERVER_START_TIMEOUT) {
51+
await new Promise((resolve) => setTimeout(resolve, 1000));
52+
const ok = await isServerReady(DEV_SERVER_URL);
53+
if (ok) {
54+
console.log('Dev server is ready.');
55+
return child;
56+
}
57+
}
58+
59+
// Timeout -- kill and exit
60+
child.kill('SIGTERM');
61+
console.error(
62+
`Error: Dev server did not become ready within ${DEV_SERVER_START_TIMEOUT / 1000}s`,
63+
);
64+
process.exit(1);
65+
}
66+
67+
/** Kill the dev server process if we started it. */
68+
export function killDevServer(child: ChildProcess | null): void {
69+
if (child && !child.killed) {
70+
child.kill('SIGTERM');
71+
console.log('Dev server stopped.');
72+
}
73+
}

scripts/replay.ts

Lines changed: 1 addition & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@ import * as fs from 'node:fs';
2222
import * as path from 'node:path';
2323
import { chromium } from 'playwright';
2424
import type { Browser, Page } from 'playwright';
25-
import { spawn } from 'node:child_process';
26-
import type { ChildProcess } from 'node:child_process';
27-
import http from 'node:http';
25+
import { DEV_SERVER_URL, ensureDevServer, killDevServer } from './dev-server-utils';
2826

2927
// ── Types ───────────────────────────────────────────────────
3028

@@ -83,11 +81,9 @@ interface ReplaySummary {
8381

8482
// ── Constants ───────────────────────────────────────────────
8583

86-
const DEV_SERVER_URL = 'http://localhost:3000';
8784
const VIEWPORT = { width: 900, height: 700 };
8885
const SCENE_READY_TIMEOUT = 30_000;
8986
const STATE_SETTLED_TIMEOUT = 10_000;
90-
const DEV_SERVER_START_TIMEOUT = 30_000;
9187

9288
// ── CLI Arg Parsing ─────────────────────────────────────────
9389

@@ -180,64 +176,6 @@ function loadTranscript(filePath: string): GameTranscript {
180176
return transcript;
181177
}
182178

183-
// ── Dev Server Management ───────────────────────────────────
184-
185-
/** Check if a URL is reachable with an HTTP GET. */
186-
function isServerReady(url: string): Promise<boolean> {
187-
return new Promise((resolve) => {
188-
const req = http.get(url, (res) => {
189-
res.resume(); // consume response body
190-
resolve(res.statusCode !== undefined && res.statusCode < 500);
191-
});
192-
req.on('error', () => resolve(false));
193-
req.setTimeout(2000, () => {
194-
req.destroy();
195-
resolve(false);
196-
});
197-
});
198-
}
199-
200-
/** Start the dev server if not already running. Returns the child process (or null). */
201-
async function ensureDevServer(): Promise<ChildProcess | null> {
202-
const ready = await isServerReady(DEV_SERVER_URL);
203-
if (ready) {
204-
console.log('Dev server already running at', DEV_SERVER_URL);
205-
return null;
206-
}
207-
208-
console.log('Starting dev server (npm run dev)...');
209-
const child = spawn('npm', ['run', 'dev'], {
210-
stdio: ['ignore', 'pipe', 'pipe'],
211-
detached: false,
212-
});
213-
214-
// Wait for the server to become ready
215-
const start = Date.now();
216-
while (Date.now() - start < DEV_SERVER_START_TIMEOUT) {
217-
await new Promise((resolve) => setTimeout(resolve, 1000));
218-
const ok = await isServerReady(DEV_SERVER_URL);
219-
if (ok) {
220-
console.log('Dev server is ready.');
221-
return child;
222-
}
223-
}
224-
225-
// Timeout -- kill and exit
226-
child.kill('SIGTERM');
227-
console.error(
228-
`Error: Dev server did not become ready within ${DEV_SERVER_START_TIMEOUT / 1000}s`,
229-
);
230-
process.exit(1);
231-
}
232-
233-
/** Kill the dev server process if we started it. */
234-
function killDevServer(child: ChildProcess | null): void {
235-
if (child && !child.killed) {
236-
child.kill('SIGTERM');
237-
console.log('Dev server stopped.');
238-
}
239-
}
240-
241179
// ── Playwright Automation ───────────────────────────────────
242180

243181
/**

0 commit comments

Comments
 (0)