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; }