website refactor

This commit is contained in:
2026-01-17 22:55:03 +01:00
parent 64d9e7fd16
commit 69d4cce7f1
64 changed files with 1146 additions and 1014 deletions

View File

@@ -11,8 +11,10 @@ export class WebsiteServerHarness {
private process: ChildProcess | null = null;
private logs: string[] = [];
private port: number;
private options: WebsiteServerHarnessOptions;
constructor(options: WebsiteServerHarnessOptions = {}) {
this.options = options;
this.port = options.port || 3000;
}
@@ -28,46 +30,75 @@ export class WebsiteServerHarness {
cwd,
env: {
...process.env,
...this.options.env,
PORT: this.port.toString(),
...((this.process as unknown as { env: Record<string, string> })?.env || {}),
},
shell: true,
detached: true, // Start in a new process group
});
let resolved = false;
const checkReadiness = async () => {
if (resolved) return;
try {
const res = await fetch(`http://localhost:${this.port}`, { method: 'HEAD' });
if (res.ok || res.status === 307 || res.status === 200) {
resolved = true;
resolve();
}
} catch (e) {
// Not ready yet
}
};
this.process.stdout?.on('data', (data) => {
const str = data.toString();
this.logs.push(str);
if (str.includes('ready') || str.includes('started') || str.includes('Local:')) {
resolve();
checkReadiness();
}
});
this.process.stderr?.on('data', (data) => {
const str = data.toString();
this.logs.push(str);
console.error(`[Website Server Error] ${str}`);
// Don't console.error here as it might be noisy, but keep in logs
});
this.process.on('error', (err) => {
reject(err);
});
this.process.on('exit', (code) => {
if (code !== 0 && code !== null) {
console.error(`Website server exited with code ${code}`);
if (!resolved) {
resolved = true;
reject(err);
}
});
// Timeout after 30 seconds
this.process.on('exit', (code) => {
if (!resolved && code !== 0 && code !== null) {
resolved = true;
reject(new Error(`Website server exited with code ${code}`));
}
});
// Timeout after 60 seconds (Next.js dev can be slow)
setTimeout(() => {
reject(new Error('Website server failed to start within 30s'));
}, 30000);
if (!resolved) {
resolved = true;
reject(new Error(`Website server failed to start within 60s. Logs:\n${this.getLogTail(20)}`));
}
}, 60000);
});
}
async stop(): Promise<void> {
if (this.process) {
this.process.kill();
if (this.process && this.process.pid) {
try {
// Kill the process group since we used detached: true
process.kill(-this.process.pid);
} catch (e) {
// Fallback to normal kill
this.process.kill();
}
this.process = null;
}
}
@@ -84,8 +115,10 @@ export class WebsiteServerHarness {
const errorPatterns = [
'uncaughtException',
'unhandledRejection',
'Error: ',
// 'Error: ', // Too broad, catches expected API errors
];
// Only fail on actual process-level errors or unexpected server crashes
return this.logs.some(log => errorPatterns.some(pattern => log.includes(pattern)));
}
}