Files
buzz-sheet/lib/commission/draft.ts
T

62 lines
3.0 KiB
TypeScript

import * as z from "zod";
import type { CommissionRequest } from "./request";
const draftKey = z.string().max(64);
const draftRequestSchema = z.object({
teams: z.array(z.object({ members: z.array(z.object({
characterKey: draftKey, weaponKey: draftKey, artifactKey: draftKey,
constellation: z.enum(["0", "1", "2", "3", "4", "5", "6", "0-6"]),
refinement: z.enum(["1", "2", "3", "4", "5", "1-5"]),
})).length(4) })).max(20),
weapons: z.array(z.object({
characterKey: draftKey, weaponKeys: z.array(draftKey).min(1).max(100),
refinements: z.record(draftKey, z.enum(["1", "2", "3", "4", "5"])).optional(),
})).max(20),
constellations: z.array(z.object({
characterKey: draftKey, levels: z.array(z.number().int().min(0).max(6)).max(7),
})).max(20),
});
const draftSchema = z.object({ version: z.literal(1), savedAt: z.number(), request: draftRequestSchema });
const DRAFT_LIFETIME_MS = 7 * 24 * 60 * 60 * 1000;
type DraftCatalog = {
characters: { key: string; weaponType?: string | null }[];
weapons: { key: string; weaponType?: string | null }[];
artifacts: { key: string }[];
};
export function commissionDraftKey(userId: string) {
return `commission-request-draft:${userId}`;
}
export function serializeCommissionDraft(request: CommissionRequest, now = Date.now()) {
return JSON.stringify({ version: 1, savedAt: now, request });
}
export function restoreCommissionDraft(serialized: string | null, catalog: DraftCatalog, now = Date.now()): CommissionRequest | null {
if (!serialized) return null;
let parsed;
try { parsed = draftSchema.safeParse(JSON.parse(serialized)); } catch { return null; }
if (!parsed.success || now - parsed.data.savedAt > DRAFT_LIFETIME_MS || parsed.data.savedAt > now) return null;
const request = parsed.data.request;
const characterKeys = new Set(catalog.characters.map((item) => item.key));
const artifactKeys = new Set(catalog.artifacts.map((item) => item.key));
const compatibleWeapon = (characterKey: string, weaponKey: string) => {
const character = catalog.characters.find((item) => item.key === characterKey);
return character?.weaponType && catalog.weapons.some((item) => item.key === weaponKey && item.weaponType === character.weaponType)
? weaponKey : "";
};
for (const team of request.teams) for (const member of team.members) {
if (!characterKeys.has(member.characterKey)) member.characterKey = "";
member.weaponKey = compatibleWeapon(member.characterKey, member.weaponKey);
if (!artifactKeys.has(member.artifactKey)) member.artifactKey = "";
}
for (const set of request.weapons) {
if (!characterKeys.has(set.characterKey)) set.characterKey = "";
set.weaponKeys = set.weaponKeys.map((key) => compatibleWeapon(set.characterKey, key));
set.refinements = Object.fromEntries(Object.entries(set.refinements ?? {}).filter(([key]) => key && set.weaponKeys.includes(key)));
}
for (const set of request.constellations) if (!characterKeys.has(set.characterKey)) set.characterKey = "";
return request;
}