Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 9x 9x 9x 73x 73x 70x 82x 80x 82x 436791x 2x 2x 2x 2x 80x | import { parse as parseYaml } from "yaml";
import {
curriculumManifestSchema,
curriculumTopicSchema,
validateCurriculumManifest,
validateCurriculumTopic,
type CurriculumManifest,
type CurriculumTopic,
} from "./curriculum-schema";
/**
* YAML parsing for curriculum manifests and topics with the same safety rules
* as Course Content (no YAML tags, unique keys). Production loads Course
* Content through CourseContentLoader; tests and fixtures use these helpers.
*/
export function parseManifest(source: string): CurriculumManifest {
const parsed = curriculumManifestSchema.safeParse(parseSafeYaml(source));
Iif (!parsed.success) throw new Error("Invalid curriculum manifest");
return validateCurriculumManifest(parsed.data);
}
export function parseTopic(source: string): CurriculumTopic {
const parsed = curriculumTopicSchema.safeParse(parseSafeYaml(source));
if (!parsed.success) throw new Error("Invalid curriculum topic");
return validateCurriculumTopic(parsed.data);
}
function parseSafeYaml(source: string): unknown {
if (containsYamlTag(source)) throw new Error("YAML tags are not allowed");
return parseYaml(source, { schema: "core", uniqueKeys: true });
}
function containsYamlTag(source: string): boolean {
for (let index = 0; index < source.length; index += 1) {
if (source[index] !== "!") continue;
const previous = source[index - 1];
Iif (previous !== undefined && !/\s|,|:|\{|\}|\[|\]/.test(previous)) continue;
const next = source[index + 1];
Eif (next === "!" || next === "<" || (next !== undefined && /[A-Za-z]/.test(next))) return true;
}
return false;
}
|