Add experimental metrics insights, comparisons, and scope filters

This commit is contained in:
2026-09-27 22:34:56 -04:00
parent 3922e28ca8
commit eb2a852fdc
18 changed files with 992 additions and 61 deletions
+60
View File
@@ -1,3 +1,5 @@
import { DEFAULT_TIME_ZONE, validTimeZone } from './time-zone.js';
export type MetricsDays = 1 | 7 | 30;
export function parseMetricsDays(value: string | null | undefined): MetricsDays {
@@ -5,6 +7,28 @@ export function parseMetricsDays(value: string | null | undefined): MetricsDays
return days === 1 || days === 7 || days === 30 ? days : 7;
}
export interface MetricsScope {
game: string | null;
map: string | null;
timeZone: string;
}
export interface MetricsQuery extends MetricsScope {
days: MetricsDays;
}
/** A map is an exact recorded label within a game, never a cross-game match. */
export function parseMetricsQuery(params: URLSearchParams): MetricsQuery {
const requestedGame = params.get('game')?.trim() ?? '';
const game = /^[a-z0-9_-]{1,32}$/.test(requestedGame) ? requestedGame : null;
const requestedMap = params.get('map');
const map = game && requestedMap && requestedMap.trim() && requestedMap.length <= 200 && !/[\u0000-\u001f\u007f]/.test(requestedMap)
? requestedMap : null;
const requestedZone = params.get('timeZone');
return { days: parseMetricsDays(params.get('days')), game, map,
timeZone: validTimeZone(requestedZone) ? requestedZone : DEFAULT_TIME_ZONE };
}
/** All durations are hours. Observed is known server time, not collection coverage. */
export interface Utilization {
observed: number;
@@ -16,6 +40,8 @@ export interface Utilization {
export interface ServerMetrics extends Utilization {
server: string;
/** A current registration with this name exists; historical identity may differ. */
currentRegistered: boolean;
}
export interface MapMetrics extends Utilization {
@@ -34,6 +60,33 @@ export interface MetricPoint extends Utilization {
partial: boolean;
}
export interface PreviousMetrics {
since: number;
until: number;
summary: Utilization;
firstSampleAt: number | null;
lastSampleAt: number | null;
/** Retained scope evidence starts late or is absent, not a completeness guarantee. */
partialHistory: boolean;
}
export interface HeatmapCell extends Utilization {
/** Monday = 0, Sunday = 6. */
weekday: number;
hour: number;
/** Distinct local dates with any captured time, including unknown status. */
distinctDates: number;
}
export interface PooledPressure {
/** Share of known map-hours with every tracked lobby full, from 0 to 100. */
value: number | null;
fullMapHours: number;
knownMapHours: number;
coveredMaps: number;
totalMaps: number;
}
export interface MetricsData {
days: MetricsDays;
since: number;
@@ -42,8 +95,15 @@ export interface MetricsData {
firstRetainedSampleAt: number | null;
latestSampleAt: number | null;
registeredServerCount: number;
scope: MetricsScope;
scopeOptions: { games: string[]; maps: { game: string; map: string }[] };
scopeFirstSampleAt: number | null;
scopeLatestSampleAt: number | null;
summary: Utilization;
servers: ServerMetrics[];
maps: MapMetrics[];
points: MetricPoint[];
previous: PreviousMetrics;
heatmap: { timeZone: string; cells: HeatmapCell[] };
pooledPressure: PooledPressure;
}