Files
2026-10-17 19:52:24 +07:00

59 lines
2.7 KiB
TypeScript

import type { ResultsSnapshot } from '../src/types/screen.js'
function object(value: unknown): Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value))
throw new Error('Некорректный снимок результатов')
return value as Record<string, unknown>
}
function list(value: unknown): unknown[] {
if (!Array.isArray(value)) throw new Error('Некорректный список результатов')
return value
}
function text(value: unknown): string {
if (typeof value !== 'string') throw new Error('Некорректный текст результатов')
return value
}
function integer(value: unknown, minimum = -2147483648): number {
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < minimum)
throw new Error('Некорректное число результатов')
return value
}
// Сохраняем только поля публичной таблицы, без участников, паролей и токенов.
export function parseResults(value: unknown, packageKey: string): ResultsSnapshot {
const data = object(value)
if (data.packageKey !== packageKey || !/^[a-f0-9]{64}$/.test(packageKey))
throw new Error('Результаты относятся к другой игре')
const captured = Date.parse(text(data.capturedAt))
if (!Number.isFinite(captured)) throw new Error('Некорректное время снимка результатов')
const questions = list(data.questions).map((value) => {
const q = object(value)
return { code: text(q.code), text: text(q.text) }
})
const codes = new Set(questions.map((q) => q.code))
if (codes.size !== questions.length || codes.has(''))
throw new Error('Повтор или отсутствие кода вопроса')
const teams = list(data.teams).map((value) => {
const team = object(value)
const answers = list(team.answers).map((value) => {
const a = object(value)
return { questionCode: text(a.questionCode), score: integer(a.score) }
})
if (
new Set(answers.map((a) => a.questionCode)).size !== answers.length ||
answers.some((a) => !codes.has(a.questionCode))
)
throw new Error('Некорректные ответы результатов')
return {
id: text(team.id),
name: text(team.name),
place: integer(team.place, 1),
totalScore: integer(team.totalScore),
actionsCount: integer(team.actionsCount, 0),
answers,
}
})
if (teams.some((team) => !team.id) || new Set(teams.map((t) => t.id)).size !== teams.length)
throw new Error('Повтор или отсутствие команды')
return { packageKey, capturedAt: new Date(captured).toISOString(), questions, teams }
}