const WOW_SPECIALIZATIONS = Object.freeze([ ["warrior:arms", "damage"], ["warrior:fury", "damage"], ["warrior:protection", "tank"], ["paladin:holy", "healer"], ["paladin:protection", "tank"], ["paladin:retribution", "damage"], ["hunter:beast-mastery", "damage"], ["hunter:marksmanship", "damage"], ["hunter:survival", "damage"], ["rogue:assassination", "damage"], ["rogue:combat", "damage"], ["rogue:subtlety", "damage"], ["priest:discipline", "healer"], ["priest:holy", "healer"], ["priest:shadow", "damage"], ["death-knight:blood", "tank"], ["death-knight:frost", "damage"], ["death-knight:unholy", "damage"], ["shaman:elemental", "damage"], ["shaman:enhancement", "damage"], ["shaman:restoration", "healer"], ["mage:arcane", "damage"], ["mage:fire", "damage"], ["mage:frost", "damage"], ["warlock:affliction", "damage"], ["warlock:demonology", "damage"], ["warlock:destruction", "damage"], ["druid:balance", "damage"], ["druid:feral-bear", "tank"], ["druid:feral-cat", "damage"], ["druid:restoration", "healer"], ]); const READY_IDS = new Set([ "warrior:protection", "priest:discipline", "hunter:marksmanship", "mage:fire", "rogue:assassination", ]); const SPECIALIZATION_ROLE = new Map(WOW_SPECIALIZATIONS); const VALID_STATUSES = new Set(["ready", "beta", "needs-testing"]); export const DEFAULT_COMPANION_SPEC_READINESS_POLICY = Object.freeze({ schemaVersion: 1, statuses: Object.freeze(Object.fromEntries(WOW_SPECIALIZATIONS.map(([id]) => [ id, READY_IDS.has(id) ? "ready" : "needs-testing", ]))), }); function invalid(message) { const error = new Error(message); error.code = "invalid_companion_spec_readiness"; return error; } export function normalizeCompanionSpecReadinessPolicy(value) { if (!value || typeof value !== "object" || Array.isArray(value) || value.schemaVersion !== 1) { throw invalid("Companion readiness policy schemaVersion must be 1."); } if (!value.statuses || typeof value.statuses !== "object" || Array.isArray(value.statuses)) { throw invalid("Companion readiness statuses are required."); } for (const [id, status] of Object.entries(value.statuses)) { if (!SPECIALIZATION_ROLE.has(id)) throw invalid(`Unknown WoW specialization: ${id}.`); if (!VALID_STATUSES.has(status)) throw invalid(`Invalid readiness status for ${id}.`); } const statuses = Object.fromEntries(WOW_SPECIALIZATIONS.map(([id]) => [id, value.statuses[id] ?? "needs-testing"])); const ready = WOW_SPECIALIZATIONS.filter(([id]) => statuses[id] === "ready"); if (!ready.some(([, role]) => role === "tank")) throw invalid("At least one tank specialization must be Ready."); if (!ready.some(([, role]) => role === "healer")) throw invalid("At least one healer specialization must be Ready."); if (ready.filter(([, role]) => role === "damage").length < 3) { throw invalid("At least three distinct damage specializations must be Ready."); } return { schemaVersion: 1, statuses }; } export function cloneDefaultCompanionSpecReadinessPolicy() { return JSON.parse(JSON.stringify(DEFAULT_COMPANION_SPEC_READINESS_POLICY)); }