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
+79 -24
View File
@@ -3,43 +3,78 @@ import { resolve } from 'node:path';
import { config } from '../config.js';
import { db } from '../db/index.js';
import type { MetricsQuery, MetricsData } from '../../metrics.js';
import type { LeaderboardOptions, LeaderboardResponse, PlayerProfile, LeaderboardEntry } from '../../leaderboard.js';
import type {
LeaderboardOptions,
LeaderboardResponse,
PlayerProfile,
LeaderboardEntry
} from '../../leaderboard.js';
import type { CapacityHistory, CapacityQuery } from '../../capacity.js';
import { ensureHostSchema } from '../hosts/schema.js';
class ReportWorker {
private worker: Worker | null = null;
private sequence = 0;
private pending = new Map<number, { resolve: (value: any) => void; reject: (error: Error) => void; timer: NodeJS.Timeout }>();
private pending = new Map<
number,
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Pending requests hold resolvers for different report result types.
{ resolve: (value: any) => void; reject: (error: Error) => void; timer: NodeJS.Timeout }
>();
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Each request key identifies a caller-specific report result type.
private flights = new Map<string, Promise<any>>();
async close() {
const worker = this.worker; this.worker = null;
for (const pending of this.pending.values()) { clearTimeout(pending.timer); pending.reject(new Error('Report service stopped')); }
this.pending.clear(); this.flights.clear();
const worker = this.worker;
this.worker = null;
for (const pending of this.pending.values()) {
clearTimeout(pending.timer);
pending.reject(new Error('Report service stopped'));
}
this.pending.clear();
this.flights.clear();
await worker?.terminate();
}
run<T>(kind: string, input: unknown): Promise<T> {
const requestInput = input as { query?: unknown; now?: number };
const timed = kind === 'metrics' || kind === 'capacity';
const key = JSON.stringify(timed ? [kind, requestInput.query, Math.floor(requestInput.now! / 30_000)] : [kind, input]);
const key = JSON.stringify(
timed
? [kind, requestInput.query, Math.floor(requestInput.now! / 30_000)]
: [kind, input]
);
const flight = this.flights.get(key);
if (flight) return flight;
if (this.pending.size >= 64) return Promise.reject(new Error('Reports are busy. Please retry shortly.'));
if (this.pending.size >= 64)
return Promise.reject(new Error('Reports are busy. Please retry shortly.'));
db(); // Finish migrations before opening the independent reader.
if (!this.worker) {
const worker = this.worker = new Worker(resolve(process.env.NODE_ENV === 'production' ? 'build/reports/worker.mjs' : '.svelte-kit/reports/worker.mjs'), { workerData: { dbPath: resolve(config.dbPath) } });
const worker = (this.worker = new Worker(
resolve(
process.env.NODE_ENV === 'production'
? 'build/reports/worker.mjs'
: '.svelte-kit/reports/worker.mjs'
),
{ workerData: { dbPath: resolve(config.dbPath) } }
));
worker.on('message', ({ id, value, error, elapsedMs, cached }) => {
const pending = this.pending.get(id); if (!pending) return;
clearTimeout(pending.timer); this.pending.delete(id);
if (process.env.EC_PERF_LOG === 'true') console.info('[report]', { kind, elapsedMs, cached });
if (error) pending.reject(new Error(error)); else pending.resolve(value);
const pending = this.pending.get(id);
if (!pending) return;
clearTimeout(pending.timer);
this.pending.delete(id);
if (process.env.EC_PERF_LOG === 'true')
console.info('[report]', { kind, elapsedMs, cached });
if (error) pending.reject(new Error(error));
else pending.resolve(value);
if (!this.pending.size) worker.unref();
});
const fail = (error: Error) => {
if (this.worker !== worker) return;
this.worker = null;
for (const pending of this.pending.values()) { clearTimeout(pending.timer); pending.reject(error); }
this.pending.clear(); void worker.terminate();
for (const pending of this.pending.values()) {
clearTimeout(pending.timer);
pending.reject(error);
}
this.pending.clear();
void worker.terminate();
};
worker.on('error', fail);
worker.on('exit', () => fail(new Error('Report worker stopped. Please retry.')));
@@ -49,12 +84,20 @@ class ReportWorker {
const promise = new Promise<T>((resolve, reject) => {
const timer = setTimeout(() => {
// Reset the worker rather than leaving an expensive abandoned query queued.
const worker = this.worker; this.worker = null;
for (const item of this.pending.values()) { clearTimeout(item.timer); item.reject(new Error('Report timed out. Please retry.')); }
this.pending.clear(); void worker?.terminate();
const worker = this.worker;
this.worker = null;
for (const item of this.pending.values()) {
clearTimeout(item.timer);
item.reject(new Error('Report timed out. Please retry.'));
}
this.pending.clear();
void worker?.terminate();
}, 60_000);
this.pending.set(id, { resolve, reject, timer }); this.worker!.postMessage({ id, kind, input });
}).finally(() => { if (this.flights.get(key) === promise) this.flights.delete(key); });
this.pending.set(id, { resolve, reject, timer });
this.worker!.postMessage({ id, kind, input });
}).finally(() => {
if (this.flights.get(key) === promise) this.flights.delete(key);
});
this.flights.set(key, promise);
return promise;
}
@@ -62,12 +105,24 @@ class ReportWorker {
const metrics = new ReportWorker();
const leaderboard = new ReportWorker();
const capacity = new ReportWorker();
export const metricsReport = (query: MetricsQuery) => metrics.run<MetricsData>('metrics', { query, now: Date.now() });
export const leaderboardReport = (query: LeaderboardOptions) => leaderboard.run<LeaderboardResponse>('leaderboard', query);
export const playerReport = (guid: string, offset: number, options: LeaderboardOptions) => leaderboard.run<{ profile: PlayerProfile | null; entry: LeaderboardEntry | null }>('player', { guid, offset, options });
export const metricsReport = (query: MetricsQuery) =>
metrics.run<MetricsData>('metrics', { query, now: Date.now() });
export const leaderboardReport = (query: LeaderboardOptions) =>
leaderboard.run<LeaderboardResponse>('leaderboard', query);
export const playerReport = (guid: string, offset: number, options: LeaderboardOptions) =>
leaderboard.run<{ profile: PlayerProfile | null; entry: LeaderboardEntry | null }>('player', {
guid,
offset,
options
});
export const capacityHistoryReport = (query: CapacityQuery) => {
ensureHostSchema(db());
// Game changes current placement estimates, not measured host resources.
return capacity.run<CapacityHistory>('capacity', { query: { ...query, game: 'all' }, now: Date.now() });
return capacity.run<CapacityHistory>('capacity', {
query: { ...query, game: 'all' },
now: Date.now()
});
};
export async function closeReports() { await Promise.all([metrics.close(), leaderboard.close(), capacity.close()]); }
export async function closeReports() {
await Promise.all([metrics.close(), leaderboard.close(), capacity.close()]);
}