Add Prettier and ESLint tooling (Refs #77)

This commit is contained in:
2026-10-09 15:56:13 -04:00
parent 8ed059b8d3
commit 16469e2d43
711 changed files with 99737 additions and 28286 deletions
+127 -34
View File
@@ -5,7 +5,13 @@ import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { createServer, type ViteDevServer } from 'vite';
import type Database from 'better-sqlite3';
import { FIXTURE_KEY, FIXTURE_VERSION, fixtureServers, refreshFixtureData, seedFixtureData } from './fixture-data.js';
import {
FIXTURE_KEY,
FIXTURE_VERSION,
fixtureServers,
refreshFixtureData,
seedFixtureData
} from './fixture-data.js';
import { parseFixtureOptions, reserveFixture } from './fixture-options.js';
const help = `Create an isolated synthetic UI preview database.
@@ -22,7 +28,10 @@ No web server, worker, game agent or network request is started by this command.
async function main() {
const args = process.argv.slice(2);
if (args.length === 1 && ['--help', '-h'].includes(args[0])) { console.log(help); return; }
if (args.length === 1 && ['--help', '-h'].includes(args[0])) {
console.log(help);
return;
}
const options = parseFixtureOptions(args);
process.umask(0o077);
const destination = await reserveFixture(options.dbPath);
@@ -30,70 +39,154 @@ async function main() {
let closeDb: (() => void) | undefined;
let complete = false;
try {
const paths = { DB_PATH: destination.dbPath, SERVERS_ROOT: join(destination.root, 't5'), T6_SERVERS_ROOT: join(destination.root, 't6'),
BETA_ARCHIVE_DIR: join(destination.root, 'beta-archive'), GAME_FILES_ROOT: join(destination.root, 'game-files'), SQLITE_TMPDIR: join(destination.root, 'sqlite-tmp') };
const paths = {
DB_PATH: destination.dbPath,
SERVERS_ROOT: join(destination.root, 't5'),
T6_SERVERS_ROOT: join(destination.root, 't6'),
BETA_ARCHIVE_DIR: join(destination.root, 'beta-archive'),
GAME_FILES_ROOT: join(destination.root, 'game-files'),
SQLITE_TMPDIR: join(destination.root, 'sqlite-tmp')
};
for (const [key, value] of Object.entries(paths)) {
process.env[key] = value;
if (key !== 'DB_PATH') await mkdir(value, { mode: 0o700 });
}
Object.assign(process.env, { RCON_PASSWORD: '', RCON_HOST: '127.0.0.1', EC_AUTH_ALLOW_HTTP: 'true',
DEPLOY_KEEP_DAYS: '7', DEPLOYMENT_RESTART_CONCURRENCY: '1', DEPLOYMENT_CREATE_CONCURRENCY: '1',
EC_SQLITE_MMAP_MB: '0', PLUTAINER_IMAGE: 'fixture.invalid/electric-cherry/preview:local' });
Object.assign(process.env, {
RCON_PASSWORD: '',
RCON_HOST: '127.0.0.1',
EC_AUTH_ALLOW_HTTP: 'true',
DEPLOY_KEEP_DAYS: '7',
DEPLOYMENT_RESTART_CONCURRENCY: '1',
DEPLOYMENT_CREATE_CONCURRENCY: '1',
EC_SQLITE_MMAP_MB: '0',
PLUTAINER_IMAGE: 'fixture.invalid/electric-cherry/preview:local'
});
const repository = resolve(dirname(fileURLToPath(import.meta.url)), '..');
vite = await createServer({ root: repository, cacheDir: join(destination.root, 'vite-cache'), configFile: false, envFile: false, appType: 'custom',
vite = await createServer({
root: repository,
cacheDir: join(destination.root, 'vite-cache'),
configFile: false,
envFile: false,
appType: 'custom',
// Vite 5 otherwise opens its HMR WebSocket port even with hmr:false.
// Attach it to an HTTP server that is deliberately never listened on.
server: { middlewareMode: true, hmr: { server: createHttpServer() }, watch: null }, optimizeDeps: { noDiscovery: true },
resolve: { alias: { $lib: join(repository, 'src/lib'), $ui: join(repository, 'src/lib/ui') } },
logLevel: 'error' });
server: { middlewareMode: true, hmr: { server: createHttpServer() }, watch: null },
optimizeDeps: { noDiscovery: true },
resolve: {
alias: { $lib: join(repository, 'src/lib'), $ui: join(repository, 'src/lib/ui') }
},
logLevel: 'error'
});
// Load the real migrations through Vite's ?raw support. Importing hooks
// would start queues and agents; this script never loads the app router.
const database = await vite.ssrLoadModule('/src/lib/server/db/index.ts') as { db: () => Database.Database; closeDb: () => void };
const database = (await vite.ssrLoadModule('/src/lib/server/db/index.ts')) as {
db: () => Database.Database;
closeDb: () => void;
};
closeDb = database.closeDb;
const handle = database.db();
const hosts = await vite.ssrLoadModule('/src/lib/server/hosts/schema.ts');
const auth = await vite.ssrLoadModule('/src/lib/server/auth/schema.ts');
const files = await vite.ssrLoadModule('/src/lib/server/game-files/schema.ts');
hosts.ensureHostSchema(handle); auth.ensureAuthSchema(handle); files.ensureGameFileSchema(handle);
hosts.ensureHostSchema(handle);
auth.ensureAuthSchema(handle);
files.ensureGameFileSchema(handle);
seedFixtureData(handle, options.now);
const password = randomBytes(24).toString('base64url');
const { hashPassword } = await vite.ssrLoadModule('/src/lib/server/auth/passwords.js');
handle.prepare(`INSERT INTO auth_users(id,username,display_name,password_hash,role,created_at,updated_at)
VALUES('fixture-admin','demo','Demo operator',?,'admin',?,?)`).run(await hashPassword(password), options.now, options.now);
if ((handle.pragma('foreign_key_check') as unknown[]).length || handle.pragma('integrity_check', { simple: true }) !== 'ok') {
handle
.prepare(
`INSERT INTO auth_users(id,username,display_name,password_hash,role,created_at,updated_at)
VALUES('fixture-admin','demo','Demo operator',?,'admin',?,?)`
)
.run(await hashPassword(password), options.now, options.now);
if (
(handle.pragma('foreign_key_check') as unknown[]).length ||
handle.pragma('integrity_check', { simple: true }) !== 'ok'
) {
throw new Error('Fixture integrity validation failed.');
}
const environment = { ...paths, RCON_PASSWORD: '', RCON_HOST: '127.0.0.1', EC_AUTH_ALLOW_HTTP: 'true',
PLUTAINER_IMAGE: process.env.PLUTAINER_IMAGE! };
const environment = {
...paths,
RCON_PASSWORD: '',
RCON_HOST: '127.0.0.1',
EC_AUTH_ALLOW_HTTP: 'true',
PLUTAINER_IMAGE: process.env.PLUTAINER_IMAGE!
};
const shellQuote = (value: string) => `'${value.replaceAll("'", "'\\''")}'`;
await writeFile(join(destination.root, 'preview.env'), Object.entries(environment).map(([key, value]) => `export ${key}=${shellQuote(value)}`).join('\n') + '\n', { flag: 'wx', mode: 0o600 });
await writeFile(join(destination.root, 'fixture.json'), JSON.stringify({ format: FIXTURE_VERSION, marker: FIXTURE_KEY,
dbPath: destination.dbPath, createdAt: new Date(options.now).toISOString(), username: 'demo', password,
hosts: 3, servers: fixtureServers.map(server => server.name), matches: 500,
note: 'Synthetic local data only. Host credentials cannot authenticate; game images and library IDs are fictitious.' }, null, 2) + '\n', { flag: 'wx', mode: 0o600 });
await writeFile(
join(destination.root, 'preview.env'),
Object.entries(environment)
.map(([key, value]) => `export ${key}=${shellQuote(value)}`)
.join('\n') + '\n',
{ flag: 'wx', mode: 0o600 }
);
await writeFile(
join(destination.root, 'fixture.json'),
JSON.stringify(
{
format: FIXTURE_VERSION,
marker: FIXTURE_KEY,
dbPath: destination.dbPath,
createdAt: new Date(options.now).toISOString(),
username: 'demo',
password,
hosts: 3,
servers: fixtureServers.map((server) => server.name),
matches: 500,
note: 'Synthetic local data only. Host credentials cannot authenticate; game images and library IDs are fictitious.'
},
null,
2
) + '\n',
{ flag: 'wx', mode: 0o600 }
);
handle.pragma('wal_checkpoint(TRUNCATE)');
complete = true;
console.log(`Created ${destination.dbPath}\n3 hosts · 12 servers · 500 matches · 7 days of metrics\nLocal sign-in details: ${join(destination.root, 'fixture.json')}\nPreview environment: ${join(destination.root, 'preview.env')}`);
console.log(
`Created ${destination.dbPath}\n3 hosts · 12 servers · 500 matches · 7 days of metrics\nLocal sign-in details: ${join(destination.root, 'fixture.json')}\nPreview environment: ${join(destination.root, 'preview.env')}`
);
if (options.watch) {
refreshFixtureData(handle, Date.now());
console.log('Refreshing synthetic observations every 15 seconds. Leave this terminal open; Ctrl+C stops the feeder.');
console.log(
'Refreshing synthetic observations every 15 seconds. Leave this terminal open; Ctrl+C stops the feeder.'
);
await new Promise<void>((resolvePromise, reject) => {
const finish = (error?: unknown) => { clearInterval(timer); process.off('SIGINT', stop); process.off('SIGTERM', stop); if (error) reject(error); else resolvePromise(); };
const finish = (error?: unknown) => {
clearInterval(timer);
process.off('SIGINT', stop);
process.off('SIGTERM', stop);
if (error) reject(error);
else resolvePromise();
};
const stop = () => finish();
const timer = setInterval(() => { try { refreshFixtureData(handle, Date.now()); } catch (error) { finish(error); } }, 15_000);
process.on('SIGINT', stop); process.on('SIGTERM', stop);
const timer = setInterval(() => {
try {
refreshFixtureData(handle, Date.now());
} catch (error) {
finish(error);
}
}, 15_000);
process.on('SIGINT', stop);
process.on('SIGTERM', stop);
});
}
} finally {
try { closeDb?.(); }
finally {
try { await vite?.close(); }
finally { if (!complete) await rm(destination.root, { recursive: true, force: true }); }
try {
closeDb?.();
} finally {
try {
await vite?.close();
} finally {
if (!complete) await rm(destination.root, { recursive: true, force: true });
}
}
}
}
main().catch(error => {
console.error(`Fixture creation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
main().catch((error) => {
console.error(
`Fixture creation failed: ${error instanceof Error ? error.message : 'Unknown error'}`
);
process.exitCode = 1;
});