Initial import

This commit is contained in:
Andraxion 2026-06-26 18:18:14 -04:00
commit ab891a315c
773 changed files with 257255 additions and 0 deletions

View file

@ -0,0 +1,108 @@
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
type JsonObject = { [key: string]: JsonValue };
type ValidationRequest = {
requestId: number;
activeType: string;
rootKey: string;
parsedPayload: JsonObject | null;
records: JsonObject[];
};
type ValidationResponse = {
requestId: number;
issues: string[];
};
function isPlainObject(value: JsonValue | undefined): value is JsonObject {
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function getDialogueNodes(record: JsonObject): JsonObject[] {
const raw = record.dialogueNodes;
if (!Array.isArray(raw)) {
return [];
}
return raw.filter((entry) => isPlainObject(entry));
}
function computeValidationIssues(input: Omit<ValidationRequest, "requestId">): string[] {
const { activeType, rootKey, parsedPayload, records } = input;
const issues: string[] = [];
if (!parsedPayload || !rootKey) {
return issues;
}
const rawList = parsedPayload[rootKey];
if (!Array.isArray(rawList)) {
issues.push(`Expected ${rootKey} to be an array.`);
return issues;
}
const seen = new Set<string | number>();
records.forEach((record, index) => {
if (activeType === "quests") {
const questId = Number(record.questId);
if (!Number.isInteger(questId) || questId < 1) {
issues.push(`Record ${index + 1}: questId must be an integer >= 1.`);
} else if (seen.has(questId)) {
issues.push(`Duplicate questId found: ${questId}.`);
} else {
seen.add(questId);
}
} else {
const id = String(record.id || "").trim();
if (!id) {
issues.push(`Record ${index + 1}: id is required.`);
} else if (seen.has(id)) {
issues.push(`Duplicate id found: ${id}.`);
} else {
seen.add(id);
}
}
const name = String(record.name || "").trim();
if (activeType !== "npcs" && !name) {
issues.push(`Record ${index + 1}: name is required.`);
}
if (activeType === "npcs") {
const templateId = String(record.templateId || "").trim();
const mapId = String(record.mapId || "").trim();
if (templateId) {
issues.push(`NPC instance ${record.id || index + 1}: templateId is no longer stored on disk.`);
}
if (mapId) {
issues.push(`NPC instance ${record.id || index + 1}: mapId is no longer stored on disk.`);
}
}
if (activeType === "dialogues") {
const npcNodes = getDialogueNodes(record);
const seenNodeIds = new Set<string>();
npcNodes.forEach((node, nodeIndex) => {
const nodeId = String(node.id || "").trim();
if (!nodeId) {
issues.push(`Dialogue ${record.id || index + 1} node ${nodeIndex + 1}: id is required.`);
return;
}
if (seenNodeIds.has(nodeId)) {
issues.push(`Dialogue ${record.id || index + 1}: duplicate node id ${nodeId}.`);
return;
}
seenNodeIds.add(nodeId);
});
}
});
return issues;
}
self.onmessage = (event: MessageEvent<ValidationRequest>) => {
const { requestId, activeType, rootKey, parsedPayload, records } = event.data;
const issues = computeValidationIssues({ activeType, rootKey, parsedPayload, records });
const response: ValidationResponse = { requestId, issues };
self.postMessage(response);
};