import type { DialogMap, DialogOption, DialogSet } from "./types"; /** * Returns true, if input is a valid name of a dialogSet. * Intended to protect from loading anything other than dialogSet json. * @param input input to check */ function isValidDialogSetName(input: unknown) { if (typeof input !== "string") { return false; } if (input.length === 0) { return false; } if (input.length > 50) { return false; } return !/[^a-zA-Z0-9\-\_]/i.test(input); } function getParamValue(paramName: string): string | null { return new URLSearchParams(window.location.search).get(paramName); } /** * Fetch a dialogSet based on the hash value */ export async function fetchDialogSet(): Promise<DialogSet> { const setName = getParamValue("dialogSet"); if (!isValidDialogSetName(setName)) throw new Error("Name is not valid dialogSet name"); const dialogSetUrl = "./dialogs/" + setName + ".json"; try { const dialogSet: DialogSet = await (await fetch(dialogSetUrl)).json(); return dialogSet; } catch (error) { throw error; } } export function findDialogSetProblems(dialogSet: DialogSet) { const dialogReferenceProblems = checkForInvalidDialogReferences(dialogSet); return [...dialogReferenceProblems]; } interface DialogReferenceProblem { type: "DialogReferenceProblem"; sourceDialog: string; text: string; } function checkForInvalidDialogReferences( dialogSet: DialogSet ): DialogReferenceProblem[] { const dialogs = dialogSet.dialogs; const problems: DialogReferenceProblem[] = []; const allDialogKeys = Object.keys(dialogs); const allLinkedToDialogKeys: Set<string> = new Set(); for (const dialogName of allDialogKeys) { // TODO check all buttons for invalid references for (const { text, linksToDialog } of dialogs[dialogName].options) { allLinkedToDialogKeys.add(linksToDialog); if (!allDialogKeys.includes(linksToDialog)) { problems.push({ type: "DialogReferenceProblem", sourceDialog: dialogName, text: `Dialog ${dialogName}'s option "${text}" links to unknown dialog '${linksToDialog}'`, }); } if (linksToDialog === dialogName) { problems.push({ type: "DialogReferenceProblem", sourceDialog: dialogName, text: `Dialog ${dialogName}'s option "${text}" links to itself!`, }); } } } for (const dialogName of allDialogKeys) { if ( !allLinkedToDialogKeys.has(dialogName) && dialogSet.startDialogName !== dialogName ) { problems.push({ type: "DialogReferenceProblem", sourceDialog: dialogName, text: `Dialog ${dialogName} is unreachable: Neither linked to from any other dialog, nor startDialog`, }); } } return problems; }