@novedu/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +46 -3
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -40,6 +40,26 @@ function warning(code, message, extra = {}) {
|
|
|
40
40
|
...extra
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Flattens a treeified Zod error into `path: message` lines so a generic
|
|
45
|
+
* "Document does not match the expected structure" becomes actionable — e.g.
|
|
46
|
+
* `Unrecognized key: "nae"` and `name: Invalid input: expected string`.
|
|
47
|
+
* Framework-agnostic: shared by the web UI (`ErrorList`), the share-tutor
|
|
48
|
+
* action, and the CLI formatter, so a schema error reads the same everywhere.
|
|
49
|
+
*/
|
|
50
|
+
function formatZodIssues(zodIssues) {
|
|
51
|
+
const out = [];
|
|
52
|
+
const walk = (node, path) => {
|
|
53
|
+
if (!node || typeof node !== "object") return;
|
|
54
|
+
if (Array.isArray(node.errors)) for (const message of node.errors) out.push(path.length ? `${path.join(".")}: ${message}` : message);
|
|
55
|
+
if (node.properties) for (const [key, child] of Object.entries(node.properties)) walk(child, [...path, key]);
|
|
56
|
+
if (Array.isArray(node.items)) node.items.forEach((child, index) => {
|
|
57
|
+
walk(child, [...path, String(index)]);
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
walk(zodIssues, []);
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
43
63
|
//#endregion
|
|
44
64
|
//#region ../lib/tutors/consistency.ts
|
|
45
65
|
/** Compare a supplied value against its declared property type. Returns null when it matches. */
|
|
@@ -635,6 +655,20 @@ function context(item) {
|
|
|
635
655
|
function renderWarnings(warnings) {
|
|
636
656
|
return warnings.map((w) => ` ${yellow("⚠")} ${yellow(w.code)} ${w.message}${context(w)}`);
|
|
637
657
|
}
|
|
658
|
+
/**
|
|
659
|
+
* Render each error as a line, with any flattened Zod schema-issue detail
|
|
660
|
+
* indented beneath it — so a generic "Document does not match the expected
|
|
661
|
+
* structure" is followed by the actual field paths (e.g. `Unrecognized key:
|
|
662
|
+
* "nae"`), matching what the web UI shows.
|
|
663
|
+
*/
|
|
664
|
+
function renderErrors(errors) {
|
|
665
|
+
const lines = [];
|
|
666
|
+
for (const e of errors) {
|
|
667
|
+
lines.push(` ${red("✗")} ${red(e.code)} ${e.message}${context(e)}`);
|
|
668
|
+
if (e.zodIssues) for (const issue of formatZodIssues(e.zodIssues)) lines.push(` ${dim(issue)}`);
|
|
669
|
+
}
|
|
670
|
+
return lines;
|
|
671
|
+
}
|
|
638
672
|
function formatResult(result, source) {
|
|
639
673
|
const lines = [];
|
|
640
674
|
if (result.ok) {
|
|
@@ -652,7 +686,7 @@ function formatResult(result, source) {
|
|
|
652
686
|
lines.push(red(`✘ Invalid tutor`) + dim(` — ${source}`));
|
|
653
687
|
lines.push("");
|
|
654
688
|
lines.push(red(`${result.errors.length} error(s):`));
|
|
655
|
-
|
|
689
|
+
lines.push(...renderErrors(result.errors));
|
|
656
690
|
if (result.warnings.length) {
|
|
657
691
|
lines.push("");
|
|
658
692
|
lines.push(yellow(`${result.warnings.length} warning(s):`));
|
|
@@ -677,7 +711,7 @@ function formatFragmentResult(result, source) {
|
|
|
677
711
|
lines.push(red(`✘ Invalid fragment file`) + dim(` — ${source}`));
|
|
678
712
|
lines.push("");
|
|
679
713
|
lines.push(red(`${result.errors.length} error(s):`));
|
|
680
|
-
|
|
714
|
+
lines.push(...renderErrors(result.errors));
|
|
681
715
|
if (result.warnings.length) {
|
|
682
716
|
lines.push("");
|
|
683
717
|
lines.push(yellow(`${result.warnings.length} warning(s):`));
|
|
@@ -723,7 +757,16 @@ function runValidate(pathOrUrl, kind) {
|
|
|
723
757
|
}));
|
|
724
758
|
}
|
|
725
759
|
function registerValidate(program) {
|
|
726
|
-
program.command("validate").description("Validate a tutor YAML (default) or a fragment library by local path or public http(s) URL").argument("<pathOrUrl>", "path to a tutor or fragment YAML file, or a public http(s) URL").option("--kind <kind>", "what the file is: 'tutor' (default) or 'fragment'", "tutor").option("--json", "print the raw validation result as JSON").
|
|
760
|
+
program.command("validate").description("Validate a tutor YAML (default) or a fragment library by local path or public http(s) URL").argument("<pathOrUrl>", "path to a tutor or fragment YAML file, or a public http(s) URL").option("--kind <kind>", "what the file is: 'tutor' (default) or 'fragment'", "tutor").option("--json", "print the raw validation result as JSON").addHelpText("after", `
|
|
761
|
+
Examples:
|
|
762
|
+
# Validate a tutor (also strict-renders every fragment in every referenced library)
|
|
763
|
+
$ novedu-cli validate ./tutors/my-tutor.yaml
|
|
764
|
+
|
|
765
|
+
# Validate a fragment library on its own
|
|
766
|
+
$ novedu-cli validate ./tutors/my-fragments.yaml --kind fragment
|
|
767
|
+
|
|
768
|
+
# Machine-readable output for CI
|
|
769
|
+
$ novedu-cli validate https://example.com/tutor.yaml --json`).action(async (pathOrUrl, options) => {
|
|
727
770
|
if (options.kind !== void 0 && options.kind !== "tutor" && options.kind !== "fragment") {
|
|
728
771
|
console.error(`Invalid --kind "${options.kind}": expected "tutor" or "fragment".`);
|
|
729
772
|
process.exitCode = 1;
|
package/package.json
CHANGED