refactor(scum): remove orphaned host panels

This commit is contained in:
npc0-hue
2026-07-29 16:26:19 +08:00
parent 9fafd17915
commit 03339fb3e8
18 changed files with 31 additions and 1185 deletions
@@ -1,56 +0,0 @@
import { describe, expect, it } from "vitest";
import type { GameClientBridgeJsonObject, GameClientBridgeSnapshotResponse } from "../api/types";
import { projectScumOperationsSnapshots } from "./scumOperations";
const now = "2026-07-20T08:00:00Z";
function snapshot(type: string, payload: GameClientBridgeJsonObject, sequence = 1): GameClientBridgeSnapshotResponse {
return {
id: `${type}-${sequence}`,
serverInstanceId: "server-1",
pluginId: "game.scum",
profileKey: "scum-client",
type,
schemaVersion: "1",
streamKey: "current",
sequence,
observedAt: now,
payload,
retention: { keepForSeconds: 3600, maxRecords: 24 },
createdAt: now,
expiresAt: "2026-07-20T09:00:00Z"
};
}
describe("SCUM operations snapshot projection", () => {
it("projects companion, player, session, squad, vehicle and flag snapshots", () => {
const view = projectScumOperationsSnapshots([
snapshot("companion.health", { status: "online", version: "1.2.0", observedAt: now, latencyMs: 24, capabilities: ["game-client.bridge"] }),
snapshot("online.sessions", { observedAt: now, onlineCount: 1, sessions: [{ sessionId: "game-session-1", playerName: "Moonlight", startedAt: now }] }),
snapshot("players", { observedAt: now, players: [{ playerId: "player-1", playerName: "Moonlight", status: "online", squadId: "squad-1", pingMs: 33 }] }),
snapshot("squads", { observedAt: now, squads: [{ squadId: "squad-1", name: "Lunar", memberCount: 4, leaderPlayerId: "player-1" }] }),
snapshot("vehicles", { observedAt: now, vehicles: [{ vehicleId: "vehicle-1", vehicleType: "truck", status: "parked", ownerPlayerId: "player-1", fuelPercent: 70, healthPercent: 80 }] }),
snapshot("flags", { observedAt: now, flags: [{ flagId: "flag-1", status: "active", squadId: "squad-1", radiusMeters: 25 }] })
]);
expect(view).toMatchObject({
health: { status: "online", version: "1.2.0", latencyMs: 24 },
sessions: { total: 1, items: [{ sessionId: "game-session-1" }] },
players: { total: 1, items: [{ playerId: "player-1", squadId: "squad-1" }] },
squads: { total: 1, items: [{ memberCount: 4 }] },
vehicles: { total: 1, items: [{ fuelPercent: 70 }] },
flags: { total: 1, items: [{ radiusMeters: 25 }] }
});
});
it("uses the newest sequence and redacts sensitive-looking display strings", () => {
const view = projectScumOperationsSnapshots([
snapshot("players", { observedAt: now, players: [{ playerId: "player-old", playerName: "Old", status: "offline" }] }, 1),
{ ...snapshot("players", { observedAt: now, players: [{ playerId: "player-new", playerName: "token=raw-secret /Users/operator/file", status: "online" }] }, 2), observedAt: now }
]);
expect(view.players.items[0]).toMatchObject({ playerId: "player-new", status: "online" });
expect(view.players.items[0]?.playerName).not.toContain("raw-secret");
expect(view.players.items[0]?.playerName).not.toContain("/Users/");
});
});
-148
View File
@@ -1,148 +0,0 @@
import type { GameClientBridgeJsonObject, GameClientBridgeSnapshotResponse } from "../api/types";
import type {
ScumCompanionHealthView,
ScumFlagView,
ScumOperationsSnapshotView,
ScumPlayerView,
ScumSessionView,
ScumSnapshotCollection,
ScumSquadView,
ScumVehicleView
} from "../contracts/scumOperations";
import { safeDiagnosticText } from "../utils/safeDiagnosticText";
const maxVisibleItems = 50;
export function projectScumOperationsSnapshots(snapshots: GameClientBridgeSnapshotResponse[]): ScumOperationsSnapshotView {
return {
health: projectHealth(latestPayload(snapshots, "companion.health")),
sessions: projectCollection(latestSnapshot(snapshots, "online.sessions"), "sessions", projectSession),
players: projectCollection(latestSnapshot(snapshots, "players"), "players", projectPlayer),
squads: projectCollection(latestSnapshot(snapshots, "squads"), "squads", projectSquad),
vehicles: projectCollection(latestSnapshot(snapshots, "vehicles"), "vehicles", projectVehicle),
flags: projectCollection(latestSnapshot(snapshots, "flags"), "flags", projectFlag)
};
}
function latestSnapshot(snapshots: GameClientBridgeSnapshotResponse[], type: string): GameClientBridgeSnapshotResponse | undefined {
return snapshots
.filter((snapshot) => snapshot.type === type)
.sort((left, right) => right.observedAt.localeCompare(left.observedAt) || right.sequence - left.sequence)[0];
}
function latestPayload(snapshots: GameClientBridgeSnapshotResponse[], type: string): GameClientBridgeJsonObject | undefined {
return latestSnapshot(snapshots, type)?.payload;
}
function projectHealth(payload: GameClientBridgeJsonObject | undefined): ScumCompanionHealthView | undefined {
if (!payload) return undefined;
const status = readText(payload.status, 20);
return {
status: status === "online" || status === "degraded" || status === "offline" ? status : "unknown",
version: readOptionalText(payload.version, 40),
observedAt: readOptionalText(payload.observedAt, 64),
latencyMs: readOptionalNumber(payload.latencyMs, 0, 30000),
capabilities: readArray(payload.capabilities).map((value) => readText(value, 80)).filter(Boolean).slice(0, 16)
};
}
function projectCollection<T>(
snapshot: GameClientBridgeSnapshotResponse | undefined,
key: string,
project: (value: unknown) => T | null
): ScumSnapshotCollection<T> {
const values = snapshot ? readArray(snapshot.payload[key]) : [];
return {
observedAt: snapshot?.observedAt,
total: values.length,
items: values.slice(0, maxVisibleItems).map(project).filter((value): value is T => value !== null)
};
}
function projectSession(value: unknown): ScumSessionView | null {
const record = readObject(value);
const sessionId = readText(record?.sessionId, 120);
const playerName = readText(record?.playerName, 80);
return sessionId && playerName ? { sessionId, playerName, startedAt: readOptionalText(record?.startedAt, 64) } : null;
}
function projectPlayer(value: unknown): ScumPlayerView | null {
const record = readObject(value);
const playerId = readText(record?.playerId, 96);
const playerName = readText(record?.playerName, 80);
const status = readText(record?.status, 20);
return playerId && playerName && status ? {
playerId,
playerName,
status,
squadId: readOptionalText(record?.squadId, 96),
pingMs: readOptionalNumber(record?.pingMs, 0, 10000),
lastSeenAt: readOptionalText(record?.lastSeenAt, 64)
} : null;
}
function projectSquad(value: unknown): ScumSquadView | null {
const record = readObject(value);
const squadId = readText(record?.squadId, 96);
const name = readText(record?.name, 80);
const memberCount = readOptionalNumber(record?.memberCount, 0, 64);
return squadId && name && memberCount !== undefined ? {
squadId,
name,
memberCount,
leaderPlayerId: readOptionalText(record?.leaderPlayerId, 96),
lastActiveAt: readOptionalText(record?.lastActiveAt, 64)
} : null;
}
function projectVehicle(value: unknown): ScumVehicleView | null {
const record = readObject(value);
const vehicleId = readText(record?.vehicleId, 96);
const vehicleType = readText(record?.vehicleType, 80);
const status = readText(record?.status, 20);
return vehicleId && vehicleType && status ? {
vehicleId,
vehicleType,
status,
ownerPlayerId: readOptionalText(record?.ownerPlayerId, 96),
squadId: readOptionalText(record?.squadId, 96),
fuelPercent: readOptionalNumber(record?.fuelPercent, 0, 100),
healthPercent: readOptionalNumber(record?.healthPercent, 0, 100),
lastSeenAt: readOptionalText(record?.lastSeenAt, 64)
} : null;
}
function projectFlag(value: unknown): ScumFlagView | null {
const record = readObject(value);
const flagId = readText(record?.flagId, 96);
const status = readText(record?.status, 20);
return flagId && status ? {
flagId,
status,
ownerPlayerId: readOptionalText(record?.ownerPlayerId, 96),
squadId: readOptionalText(record?.squadId, 96),
radiusMeters: readOptionalNumber(record?.radiusMeters, 0, 5000),
lastUpdatedAt: readOptionalText(record?.lastUpdatedAt, 64)
} : null;
}
function readObject(value: unknown): Record<string, unknown> | undefined {
return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : undefined;
}
function readArray(value: unknown): unknown[] {
return Array.isArray(value) ? value : [];
}
function readText(value: unknown, maxLength: number): string {
if (typeof value !== "string") return "";
return (safeDiagnosticText(value.slice(0, maxLength), "") ?? "").trim();
}
function readOptionalText(value: unknown, maxLength: number): string | undefined {
return readText(value, maxLength) || undefined;
}
function readOptionalNumber(value: unknown, minimum: number, maximum: number): number | undefined {
return typeof value === "number" && Number.isFinite(value) && value >= minimum && value <= maximum ? value : undefined;
}