lens-content-processor 0.33.0 → 0.34.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.
Files changed (74) hide show
  1. package/dist/authoring-markup.d.ts +0 -11
  2. package/dist/authoring-markup.js +0 -52
  3. package/dist/authoring-markup.js.map +1 -1
  4. package/dist/cli.d.ts +0 -1
  5. package/dist/cli.js +2 -6
  6. package/dist/cli.js.map +1 -1
  7. package/dist/content-schema.d.ts +3 -1
  8. package/dist/content-schema.js +46 -12
  9. package/dist/content-schema.js.map +1 -1
  10. package/dist/dedupe-errors.js +1 -7
  11. package/dist/dedupe-errors.js.map +1 -1
  12. package/dist/flattener/index.d.ts +23 -0
  13. package/dist/flattener/index.js +39 -51
  14. package/dist/flattener/index.js.map +1 -1
  15. package/dist/index.d.ts +24 -3
  16. package/dist/index.js +85 -28
  17. package/dist/index.js.map +1 -1
  18. package/dist/parser/article.js +32 -34
  19. package/dist/parser/article.js.map +1 -1
  20. package/dist/parser/course.d.ts +10 -0
  21. package/dist/parser/course.js +14 -16
  22. package/dist/parser/course.js.map +1 -1
  23. package/dist/parser/learning-outcome.d.ts +0 -1
  24. package/dist/parser/learning-outcome.js +50 -29
  25. package/dist/parser/learning-outcome.js.map +1 -1
  26. package/dist/parser/lens.d.ts +12 -2
  27. package/dist/parser/lens.js +27 -67
  28. package/dist/parser/lens.js.map +1 -1
  29. package/dist/parser/module.js +17 -24
  30. package/dist/parser/module.js.map +1 -1
  31. package/dist/parser/response-segments.d.ts +54 -0
  32. package/dist/parser/response-segments.js +499 -0
  33. package/dist/parser/response-segments.js.map +1 -0
  34. package/dist/parser/sections.d.ts +1 -2
  35. package/dist/parser/sections.js +3 -5
  36. package/dist/parser/sections.js.map +1 -1
  37. package/dist/parser/survey.js +51 -25
  38. package/dist/parser/survey.js.map +1 -1
  39. package/dist/validator/directives.js +0 -42
  40. package/dist/validator/directives.js.map +1 -1
  41. package/dist/validator/emphasis.js +2 -126
  42. package/dist/validator/emphasis.js.map +1 -1
  43. package/dist/validator/html-tags.js +111 -103
  44. package/dist/validator/html-tags.js.map +1 -1
  45. package/dist/validator/math.js +9 -66
  46. package/dist/validator/math.js.map +1 -1
  47. package/dist/validator/output-integrity.d.ts +1 -1
  48. package/dist/validator/output-integrity.js +8 -14
  49. package/dist/validator/output-integrity.js.map +1 -1
  50. package/dist/validator/segment-fields.d.ts +4 -2
  51. package/dist/validator/segment-fields.js +27 -5
  52. package/dist/validator/segment-fields.js.map +1 -1
  53. package/dist/validator/test-segments.d.ts +11 -0
  54. package/dist/validator/test-segments.js +45 -0
  55. package/dist/validator/test-segments.js.map +1 -1
  56. package/dist/validator/uuid.d.ts +1 -2
  57. package/dist/validator/uuid.js +4 -6
  58. package/dist/validator/uuid.js.map +1 -1
  59. package/package.json +1 -9
  60. package/dist/source-location.d.ts +0 -2
  61. package/dist/source-location.js +0 -12
  62. package/dist/source-location.js.map +0 -1
  63. package/dist/validator/article-review-provenance.d.ts +0 -5
  64. package/dist/validator/article-review-provenance.js +0 -57
  65. package/dist/validator/article-review-provenance.js.map +0 -1
  66. package/dist/validator/article-structure.d.ts +0 -8
  67. package/dist/validator/article-structure.js +0 -674
  68. package/dist/validator/article-structure.js.map +0 -1
  69. package/dist/validator/same-lens-links.d.ts +0 -4
  70. package/dist/validator/same-lens-links.js +0 -297
  71. package/dist/validator/same-lens-links.js.map +0 -1
  72. package/dist/validator/suppressions.d.ts +0 -42
  73. package/dist/validator/suppressions.js +0 -97
  74. package/dist/validator/suppressions.js.map +0 -1
@@ -36,102 +36,120 @@ const ALLOWED_TAGS = new Set([
36
36
  // unambiguous and are flagged for ANY non-allowed name, which also catches
37
37
  // XML tags and scrape artifacts like `</nbsp>`.
38
38
  const KNOWN_HTML_TAGS = new Set([
39
- "html", "head", "body", "title", "meta", "link", "base", "style", "script",
40
- "noscript", "template", "slot",
41
- "div", "span", "p", "hr", "pre", "blockquote", "figure", "figcaption",
42
- "h1", "h2", "h3", "h4", "h5", "h6",
43
- "ul", "ol", "li", "dl", "dt", "dd", "menu",
44
- "table", "caption", "thead", "tbody", "tfoot", "tr", "td", "th", "col", "colgroup",
45
- "em", "i", "b", "strong", "u", "s", "strike", "small", "big", "mark",
46
- "del", "ins", "q", "cite", "abbr", "dfn", "kbd", "samp", "var", "code",
47
- "wbr", "bdi", "bdo", "ruby", "rt", "rp", "data", "time", "address",
48
- "section", "article", "header", "footer", "nav", "aside", "main",
49
- "details", "summary", "dialog",
50
- "img", "picture", "video", "track", "embed", "object", "param", "iframe",
51
- "map", "area", "canvas",
52
- "form", "input", "button", "select", "option", "optgroup", "datalist",
53
- "textarea", "label", "fieldset", "legend", "output", "progress", "meter",
54
- "center", "font",
39
+ "html",
40
+ "head",
41
+ "body",
42
+ "title",
43
+ "meta",
44
+ "link",
45
+ "base",
46
+ "style",
47
+ "script",
48
+ "noscript",
49
+ "template",
50
+ "slot",
51
+ "div",
52
+ "span",
53
+ "p",
54
+ "hr",
55
+ "pre",
56
+ "blockquote",
57
+ "figure",
58
+ "figcaption",
59
+ "h1",
60
+ "h2",
61
+ "h3",
62
+ "h4",
63
+ "h5",
64
+ "h6",
65
+ "ul",
66
+ "ol",
67
+ "li",
68
+ "dl",
69
+ "dt",
70
+ "dd",
71
+ "menu",
72
+ "table",
73
+ "caption",
74
+ "thead",
75
+ "tbody",
76
+ "tfoot",
77
+ "tr",
78
+ "td",
79
+ "th",
80
+ "col",
81
+ "colgroup",
82
+ "em",
83
+ "i",
84
+ "b",
85
+ "strong",
86
+ "u",
87
+ "s",
88
+ "strike",
89
+ "small",
90
+ "big",
91
+ "mark",
92
+ "del",
93
+ "ins",
94
+ "q",
95
+ "cite",
96
+ "abbr",
97
+ "dfn",
98
+ "kbd",
99
+ "samp",
100
+ "var",
101
+ "code",
102
+ "wbr",
103
+ "bdi",
104
+ "bdo",
105
+ "ruby",
106
+ "rt",
107
+ "rp",
108
+ "data",
109
+ "time",
110
+ "address",
111
+ "section",
112
+ "article",
113
+ "header",
114
+ "footer",
115
+ "nav",
116
+ "aside",
117
+ "main",
118
+ "details",
119
+ "summary",
120
+ "dialog",
121
+ "img",
122
+ "picture",
123
+ "video",
124
+ "track",
125
+ "embed",
126
+ "object",
127
+ "param",
128
+ "iframe",
129
+ "map",
130
+ "area",
131
+ "canvas",
132
+ "form",
133
+ "input",
134
+ "button",
135
+ "select",
136
+ "option",
137
+ "optgroup",
138
+ "datalist",
139
+ "textarea",
140
+ "label",
141
+ "fieldset",
142
+ "legend",
143
+ "output",
144
+ "progress",
145
+ "meter",
146
+ "center",
147
+ "font",
55
148
  ]);
56
149
  const CODE_FENCE = /^(?:```|~~~)/;
57
150
  // <name ...>, <name/>, </name> — name must start with a letter.
58
151
  const TAG_PATTERN = /<(\/?)([a-zA-Z][a-zA-Z0-9-]*)((?:\s[^<>]*)?)(\/?)>/g;
59
152
  const XML_DECL_PATTERN = /<[?!](?:xml|doctype)/i;
60
- const VIDEO_EMBED_HOSTS = new Set([
61
- "youtube.com",
62
- "www.youtube.com",
63
- "m.youtube.com",
64
- "youtube-nocookie.com",
65
- "www.youtube-nocookie.com",
66
- "youtu.be",
67
- "www.youtu.be",
68
- "vimeo.com",
69
- "www.vimeo.com",
70
- "player.vimeo.com",
71
- ]);
72
- const VIDEO_IFRAME_ATTRIBUTES = new Set([
73
- "src",
74
- "width",
75
- "height",
76
- "frameborder",
77
- "allow",
78
- "referrerpolicy",
79
- "allowfullscreen",
80
- "title",
81
- "loading",
82
- ]);
83
- const HTML_ATTRIBUTE = /([A-Za-z_:][-A-Za-z0-9_:.]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/y;
84
- function isSafeVideoIframeAttributes(source) {
85
- const attributes = new Map();
86
- let offset = 0;
87
- while (offset < source.length) {
88
- const whitespace = /^\s+/.exec(source.slice(offset));
89
- if (!whitespace)
90
- return false;
91
- offset += whitespace[0].length;
92
- if (offset === source.length)
93
- break;
94
- HTML_ATTRIBUTE.lastIndex = offset;
95
- const match = HTML_ATTRIBUTE.exec(source);
96
- if (!match)
97
- return false;
98
- offset = HTML_ATTRIBUTE.lastIndex;
99
- const name = match[1].toLowerCase();
100
- if (!VIDEO_IFRAME_ATTRIBUTES.has(name) || attributes.has(name))
101
- return false;
102
- attributes.set(name, match[2] ?? match[3] ?? match[4] ?? null);
103
- }
104
- const src = attributes.get("src");
105
- if (!src)
106
- return false;
107
- try {
108
- const url = new URL(src);
109
- if ((url.protocol !== "https:" && url.protocol !== "http:") ||
110
- !VIDEO_EMBED_HOSTS.has(url.hostname.toLowerCase()))
111
- return false;
112
- }
113
- catch {
114
- return false;
115
- }
116
- for (const dimension of ["width", "height"]) {
117
- const value = attributes.get(dimension);
118
- if (value !== undefined && (value === null || !/^\d+$/.test(value)))
119
- return false;
120
- }
121
- const frameborder = attributes.get("frameborder");
122
- if (frameborder !== undefined && frameborder !== "0")
123
- return false;
124
- const allowfullscreen = attributes.get("allowfullscreen");
125
- if (allowfullscreen !== undefined && allowfullscreen !== null && allowfullscreen !== "")
126
- return false;
127
- const loading = attributes.get("loading");
128
- if (loading !== undefined && loading !== "lazy" && loading !== "eager")
129
- return false;
130
- return true;
131
- }
132
- function stripSafeVideoIframes(line) {
133
- return line.replace(/<iframe\b([^<>]*)>\s*<\/iframe>/gi, (whole, attributes) => isSafeVideoIframeAttributes(attributes) ? " ".repeat(whole.length) : whole);
134
- }
135
153
  /**
136
154
  * Flag raw HTML/XML tags in article body content.
137
155
  *
@@ -156,7 +174,7 @@ export function validateHtmlTags(body, file, bodyStartLine) {
156
174
  if (inCodeBlock)
157
175
  continue;
158
176
  // Strip inline code spans so `<div>` in backticks is not flagged.
159
- const line = stripSafeVideoIframes(rawLine.replace(/`[^`]*`/g, ""));
177
+ const line = rawLine.replace(/`[^`]*`/g, "");
160
178
  if (XML_DECL_PATTERN.test(line)) {
161
179
  errors.push({
162
180
  file,
@@ -166,16 +184,6 @@ export function validateHtmlTags(body, file, bodyStartLine) {
166
184
  severity: "error",
167
185
  });
168
186
  }
169
- if (/<svg(?:\s|>)/i.test(line)) {
170
- errors.push({
171
- file,
172
- line: absLine,
173
- code: "article.inline-svg-unsupported",
174
- message: "Inline SVG found in article content",
175
- suggestion: "Replace the inline SVG with a standard Markdown image that references a portable SVG or PNG asset",
176
- severity: "error",
177
- });
178
- }
179
187
  for (const match of line.matchAll(TAG_PATTERN)) {
180
188
  const isClosing = match[1] === "/";
181
189
  const name = match[2].toLowerCase();
@@ -191,7 +199,7 @@ export function validateHtmlTags(body, file, bodyStartLine) {
191
199
  line: absLine,
192
200
  message: `HTML tag '${rendered}' in article content`,
193
201
  suggestion: "Use markdown syntax instead of raw HTML tags (allowed exceptions: " +
194
- "<sup>, <sub>, <a>, <br>, <audio>, and approved YouTube/Vimeo video embeds)",
202
+ "<sup>, <sub>, <a>, <br>, <audio>, and inline SVG elements)",
195
203
  severity: "error",
196
204
  });
197
205
  }
@@ -1 +1 @@
1
- {"version":3,"file":"html-tags.js","sourceRoot":"","sources":["../../src/validator/html-tags.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"html-tags.js","sourceRoot":"","sources":["../../src/validator/html-tags.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import katex from "katex";
2
- import { stripAuthoringMarkupForValidation } from "../authoring-markup.js";
2
+ import { stripAuthoringMarkup } from "../authoring-markup.js";
3
3
  const INLINE_MATH_RE = /(?<!\$)\$(?!\$)([^\s$](?:[^$\n]*?[^\s$])?)\$(?![\d$])/g;
4
4
  function maskNonMathMarkdown(content) {
5
- const authored = stripAuthoringMarkupForValidation(content);
5
+ const authored = stripAuthoringMarkup(content);
6
6
  const lines = authored.split("\n");
7
7
  let inFrontmatter = lines[0]?.trim() === "---";
8
8
  let inFence = null;
@@ -32,26 +32,9 @@ function maskNonMathMarkdown(content) {
32
32
  function lineAt(content, offset) {
33
33
  return content.slice(0, offset).split("\n").length;
34
34
  }
35
- /** Remove Markdown container prefixes while preserving every offset/line. */
36
- function normalizeMathContainers(content) {
37
- return content
38
- .split("\n")
39
- .map((line) => {
40
- let normalized = line.replace(/^([ \t]*(?:>[ \t]*)+)/, (prefix) => " ".repeat(prefix.length));
41
- // A display opener may be the content of a list item or footnote.
42
- // Continuation and closing lines are already represented by indentation.
43
- normalized = normalized.replace(/^([ \t]*(?:(?:[-+*]|\d+[.)])[ \t]+|\[\^[^\]\n]+\]:[ \t]+))(?=\$\$)/, (prefix) => " ".repeat(prefix.length));
44
- return normalized;
45
- })
46
- .join("\n");
47
- }
48
- function parseError(tex, file, line, displayMode) {
35
+ function parseError(tex, file, line) {
49
36
  try {
50
- katex.renderToString(tex, {
51
- throwOnError: true,
52
- strict: "ignore",
53
- displayMode,
54
- });
37
+ katex.renderToString(tex, { throwOnError: true, strict: "ignore" });
55
38
  return null;
56
39
  }
57
40
  catch (error) {
@@ -65,57 +48,17 @@ function parseError(tex, file, line, displayMode) {
65
48
  };
66
49
  }
67
50
  }
68
- function emptyMathError(file, line) {
69
- return {
70
- file,
71
- line,
72
- code: "article.math-empty",
73
- severity: "error",
74
- message: "Empty math delimiters render no equation.",
75
- suggestion: "Remove the empty delimiters or put the intended equation inside them.",
76
- };
77
- }
78
51
  /** Validate math exactly as isolated KaTeX expressions, matching Lens rendering. */
79
52
  export function validateMath(content, file) {
80
- const source = normalizeMathContainers(maskNonMathMarkdown(content));
53
+ const source = maskNonMathMarkdown(content);
81
54
  const errors = [];
82
55
  const withoutDisplay = source.split("");
83
56
  const displayRe = /\$\$([\s\S]*?)\$\$/g;
84
- const isArticle = file.startsWith("articles/") || file.includes("/articles/");
85
- // Some converters preserve TeX-style delimiters even though Markdown math
86
- // normally uses dollars. Detect their empty form explicitly; non-empty
87
- // forms remain visible converter residue and are handled by prose checks.
88
- for (const match of source.matchAll(/\\(?:\(\s*\\\)|\[\s*\\\])/g)) {
89
- errors.push(emptyMathError(file, lineAt(source, match.index ?? 0)));
90
- }
91
- // Standalone display fences are an article-import quality convention, not a
92
- // Markdown/KaTeX validity requirement. Other authored content may use valid
93
- // single-line display math without failing vault-wide CI.
94
- if (isArticle) {
95
- for (const [index, line] of source.split("\n").entries()) {
96
- const delimiters = line.match(/\$\$/g)?.length ?? 0;
97
- if (delimiters > 0 && line.trim() !== "$$") {
98
- errors.push({
99
- file,
100
- line: index + 1,
101
- code: "article.math-display-delimiter-placement",
102
- severity: "error",
103
- message: "Invalid LaTeX math: $$ delimiters must be on their own lines.",
104
- suggestion: "Put the opening and closing $$ delimiters on separate lines without equation text beside them.",
105
- });
106
- }
107
- }
108
- }
109
57
  for (const match of source.matchAll(displayRe)) {
110
58
  const offset = match.index ?? 0;
111
- if (!match[1].trim()) {
112
- errors.push(emptyMathError(file, lineAt(source, offset)));
113
- }
114
- else {
115
- const error = parseError(match[1], file, lineAt(source, offset), true);
116
- if (error)
117
- errors.push(error);
118
- }
59
+ const error = parseError(match[1], file, lineAt(source, offset));
60
+ if (error)
61
+ errors.push(error);
119
62
  for (let index = offset; index < offset + match[0].length; index += 1) {
120
63
  if (withoutDisplay[index] !== "\n")
121
64
  withoutDisplay[index] = " ";
@@ -134,7 +77,7 @@ export function validateMath(content, file) {
134
77
  }
135
78
  for (const match of inlineSource.matchAll(INLINE_MATH_RE)) {
136
79
  const offset = match.index ?? 0;
137
- const error = parseError(match[1], file, lineAt(inlineSource, offset), false);
80
+ const error = parseError(match[1], file, lineAt(inlineSource, offset));
138
81
  if (error)
139
82
  errors.push(error);
140
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"math.js","sourceRoot":"","sources":["../../src/validator/math.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"math.js","sourceRoot":"","sources":["../../src/validator/math.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { ContentError, FlattenedModule } from '../index.js';
1
+ import type { ContentError, FlattenedModule } from "../index.js";
2
2
  /**
3
3
  * Broad safety-net validation on the flattened output.
4
4
  * Catches empty sections (no segments) and empty segments (no content)
@@ -1,4 +1,3 @@
1
- import { getSourceLine } from '../source-location.js';
2
1
  /**
3
2
  * Broad safety-net validation on the flattened output.
4
3
  * Catches empty sections (no segments) and empty segments (no content)
@@ -7,49 +6,44 @@ import { getSourceLine } from '../source-location.js';
7
6
  export function validateOutputIntegrity(modules, slugToPath) {
8
7
  const errors = [];
9
8
  for (const module of modules) {
10
- const moduleFile = slugToPath?.get(module.slug) ?? module.slug;
9
+ const file = slugToPath?.get(module.slug) ?? module.slug;
11
10
  for (const section of module.sections) {
12
- const file = section.sourcePath ?? moduleFile;
13
11
  const sectionLabel = section.meta.title ?? section.type;
14
12
  if (section.segments.length === 0) {
15
13
  errors.push({
16
14
  file,
17
- line: getSourceLine(section),
18
15
  message: `Section "${sectionLabel}" has no segments`,
19
- severity: 'error',
16
+ severity: "error",
20
17
  });
21
18
  continue;
22
19
  }
23
20
  for (let i = 0; i < section.segments.length; i++) {
24
21
  const segment = section.segments[i];
25
22
  switch (segment.type) {
26
- case 'text':
23
+ case "text":
27
24
  if (!segment.content?.trim()) {
28
25
  errors.push({
29
26
  file,
30
- line: getSourceLine(segment) ?? getSourceLine(section),
31
27
  message: `Empty text segment in "${sectionLabel}" (segment ${i + 1})`,
32
- severity: 'error',
28
+ severity: "error",
33
29
  });
34
30
  }
35
31
  break;
36
- case 'article':
32
+ case "article":
37
33
  if (!segment.content?.trim()) {
38
34
  errors.push({
39
35
  file,
40
- line: getSourceLine(segment) ?? getSourceLine(section),
41
36
  message: `Empty article segment in "${sectionLabel}" (segment ${i + 1})`,
42
- severity: 'error',
37
+ severity: "error",
43
38
  });
44
39
  }
45
40
  break;
46
- case 'video':
41
+ case "video":
47
42
  if (!segment.transcript?.length) {
48
43
  errors.push({
49
44
  file,
50
- line: getSourceLine(segment) ?? getSourceLine(section),
51
45
  message: `Empty video transcript in "${sectionLabel}" (segment ${i + 1})`,
52
- severity: 'error',
46
+ severity: "error",
53
47
  });
54
48
  }
55
49
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"output-integrity.js","sourceRoot":"","sources":["../../src/validator/output-integrity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAA0B,EAC1B,UAAgC;IAEhC,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,UAAU,GAAG,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC;QAE/D,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,IAAI,UAAU,CAAC;YAC9C,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAExD,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAClC,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI;oBACJ,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC;oBAC5B,OAAO,EAAE,YAAY,YAAY,mBAAmB;oBACpD,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACjD,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAEpC,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;oBACrB,KAAK,MAAM;wBACT,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;4BAC7B,MAAM,CAAC,IAAI,CAAC;gCACV,IAAI;gCACJ,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,OAAO,CAAC;gCACtD,OAAO,EAAE,0BAA0B,YAAY,cAAc,CAAC,GAAG,CAAC,GAAG;gCACrE,QAAQ,EAAE,OAAO;6BAClB,CAAC,CAAC;wBACL,CAAC;wBACD,MAAM;oBACR,KAAK,SAAS;wBACZ,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;4BAC7B,MAAM,CAAC,IAAI,CAAC;gCACV,IAAI;gCACJ,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,OAAO,CAAC;gCACtD,OAAO,EAAE,6BAA6B,YAAY,cAAc,CAAC,GAAG,CAAC,GAAG;gCACxE,QAAQ,EAAE,OAAO;6BAClB,CAAC,CAAC;wBACL,CAAC;wBACD,MAAM;oBACR,KAAK,OAAO;wBACV,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;4BAChC,MAAM,CAAC,IAAI,CAAC;gCACV,IAAI;gCACJ,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC,OAAO,CAAC;gCACtD,OAAO,EAAE,8BAA8B,YAAY,cAAc,CAAC,GAAG,CAAC,GAAG;gCACzE,QAAQ,EAAE,OAAO;6BAClB,CAAC,CAAC;wBACL,CAAC;wBACD,MAAM;oBACR,8CAA8C;gBAChD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"output-integrity.js","sourceRoot":"","sources":["../../src/validator/output-integrity.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAA0B,EAC1B,UAAgC;IAEhC,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC;QAEzD,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAExD,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAClC,MAAM,CAAC,IAAI,CAAC;oBACV,IAAI;oBACJ,OAAO,EAAE,YAAY,YAAY,mBAAmB;oBACpD,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACjD,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAEpC,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;oBACrB,KAAK,MAAM;wBACT,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;4BAC7B,MAAM,CAAC,IAAI,CAAC;gCACV,IAAI;gCACJ,OAAO,EAAE,0BAA0B,YAAY,cAAc,CAAC,GAAG,CAAC,GAAG;gCACrE,QAAQ,EAAE,OAAO;6BAClB,CAAC,CAAC;wBACL,CAAC;wBACD,MAAM;oBACR,KAAK,SAAS;wBACZ,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;4BAC7B,MAAM,CAAC,IAAI,CAAC;gCACV,IAAI;gCACJ,OAAO,EAAE,6BAA6B,YAAY,cAAc,CAAC,GAAG,CAAC,GAAG;gCACxE,QAAQ,EAAE,OAAO;6BAClB,CAAC,CAAC;wBACL,CAAC;wBACD,MAAM;oBACR,KAAK,OAAO;wBACV,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC;4BAChC,MAAM,CAAC,IAAI,CAAC;gCACV,IAAI;gCACJ,OAAO,EAAE,8BAA8B,YAAY,cAAc,CAAC,GAAG,CAAC,GAAG;gCACzE,QAAQ,EAAE,OAAO;6BAClB,CAAC,CAAC;wBACL,CAAC;wBACD,MAAM;oBACR,8CAA8C;gBAChD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1,9 +1,11 @@
1
- import type { ContentError } from '../index.js';
1
+ import type { ContentError } from "../index.js";
2
2
  /**
3
3
  * Validate that fields are appropriate for the segment type.
4
4
  * Warns about fields that belong to different segment types.
5
5
  *
6
- * @param segmentType - The type of segment (text, chat, article, video, question, roleplay)
6
+ * @param segmentType - The type of segment (text, chat, article, video,
7
+ * question, roleplay), or a "question:<subtype>" response-segment schema
8
+ * key (from responseSegmentSchemaKey)
7
9
  * @param fields - Object mapping field names to their values
8
10
  * @param file - File path for error reporting
9
11
  * @param line - Line number for error reporting
@@ -1,13 +1,15 @@
1
- import { VALID_FIELDS_BY_SEGMENT_TYPE } from '../content-schema.js';
1
+ import { VALID_FIELDS_BY_SEGMENT_TYPE } from "../content-schema.js";
2
2
  /**
3
3
  * Fields that are specific to excerpt segments (not valid in text/chat).
4
4
  */
5
- const EXCERPT_ONLY_FIELDS = new Set(['from', 'to']);
5
+ const EXCERPT_ONLY_FIELDS = new Set(["from", "to"]);
6
6
  /**
7
7
  * Validate that fields are appropriate for the segment type.
8
8
  * Warns about fields that belong to different segment types.
9
9
  *
10
- * @param segmentType - The type of segment (text, chat, article, video, question, roleplay)
10
+ * @param segmentType - The type of segment (text, chat, article, video,
11
+ * question, roleplay), or a "question:<subtype>" response-segment schema
12
+ * key (from responseSegmentSchemaKey)
11
13
  * @param fields - Object mapping field names to their values
12
14
  * @param file - File path for error reporting
13
15
  * @param line - Line number for error reporting
@@ -20,6 +22,13 @@ export function validateSegmentFields(segmentType, fields, file, line) {
20
22
  // Unknown segment type - validation handled elsewhere
21
23
  return warnings;
22
24
  }
25
+ // "question:open" → "Question: Open" for messages
26
+ const subtype = segmentType.startsWith("question:")
27
+ ? segmentType.slice("question:".length)
28
+ : null;
29
+ const displayType = subtype
30
+ ? `Question: ${subtype === "fillblank" ? "FillBlank" : subtype[0].toUpperCase() + subtype.slice(1)}`
31
+ : segmentType;
23
32
  for (const fieldName of Object.keys(fields)) {
24
33
  if (!validFields.has(fieldName)) {
25
34
  // Check if this is a field that belongs to a different segment type
@@ -27,9 +36,22 @@ export function validateSegmentFields(segmentType, fields, file, line) {
27
36
  warnings.push({
28
37
  file,
29
38
  line,
30
- message: `Field '${fieldName}' is not valid in ${segmentType} segment`,
39
+ message: `Field '${fieldName}' is not valid in ${displayType} segment`,
31
40
  suggestion: `'${fieldName}' is only valid in article, video, or embed segments`,
32
- severity: 'warning',
41
+ severity: "warning",
42
+ });
43
+ }
44
+ else if (subtype) {
45
+ // Response subtypes each have a small, closed field set; a known
46
+ // field from another subtype (e.g. scale:: on a Choice) would
47
+ // otherwise be silently ignored — field-typos.ts only catches
48
+ // unknown names.
49
+ warnings.push({
50
+ file,
51
+ line,
52
+ message: `Field '${fieldName}' is not valid in a ${displayType} segment`,
53
+ suggestion: `Valid fields: ${[...validFields].join(", ")}`,
54
+ severity: "warning",
33
55
  });
34
56
  }
35
57
  // Note: We don't warn about completely unknown fields here
@@ -1 +1 @@
1
- {"version":3,"file":"segment-fields.js","sourceRoot":"","sources":["../../src/validator/segment-fields.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAEpE;;GAEG;AACH,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AAEpD;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CACnC,WAAmB,EACnB,MAA8B,EAC9B,IAAY,EACZ,IAAY;IAEZ,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,MAAM,WAAW,GAAG,4BAA4B,CAAC,WAAW,CAAC,CAAC;IAE9D,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,sDAAsD;QACtD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,oEAAoE;YACpE,IAAI,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACvC,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI;oBACJ,IAAI;oBACJ,OAAO,EAAE,UAAU,SAAS,qBAAqB,WAAW,UAAU;oBACtE,UAAU,EAAE,IAAI,SAAS,sDAAsD;oBAC/E,QAAQ,EAAE,SAAS;iBACpB,CAAC,CAAC;YACL,CAAC;YACD,2DAA2D;YAC3D,mCAAmC;QACrC,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"segment-fields.js","sourceRoot":"","sources":["../../src/validator/segment-fields.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,4BAA4B,EAAE,MAAM,sBAAsB,CAAC;AAEpE;;GAEG;AACH,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CACnC,WAAmB,EACnB,MAA8B,EAC9B,IAAY,EACZ,IAAY;IAEZ,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,MAAM,WAAW,GAAG,4BAA4B,CAAC,WAAW,CAAC,CAAC;IAE9D,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,sDAAsD;QACtD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,kDAAkD;IAClD,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC;QACjD,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;QACvC,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,WAAW,GAAG,OAAO;QACzB,CAAC,CAAC,aAAa,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QACpG,CAAC,CAAC,WAAW,CAAC;IAEhB,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,oEAAoE;YACpE,IAAI,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACvC,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI;oBACJ,IAAI;oBACJ,OAAO,EAAE,UAAU,SAAS,qBAAqB,WAAW,UAAU;oBACtE,UAAU,EAAE,IAAI,SAAS,sDAAsD;oBAC/E,QAAQ,EAAE,SAAS;iBACpB,CAAC,CAAC;YACL,CAAC;iBAAM,IAAI,OAAO,EAAE,CAAC;gBACnB,iEAAiE;gBACjE,8DAA8D;gBAC9D,8DAA8D;gBAC9D,iBAAiB;gBACjB,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI;oBACJ,IAAI;oBACJ,OAAO,EAAE,UAAU,SAAS,uBAAuB,WAAW,UAAU;oBACxE,UAAU,EAAE,iBAAiB,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBAC1D,QAAQ,EAAE,SAAS;iBACpB,CAAC,CAAC;YACL,CAAC;YACD,2DAA2D;YAC3D,mCAAmC;QACrC,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import type { ContentError } from "../index.js";
2
2
  import type { ParsedSection } from "../parser/sections.js";
3
+ import type { ParsedLensSegment } from "../parser/lens.js";
3
4
  /**
4
5
  * Validate that an LO Test section contains only assessable segment types.
5
6
  * Flags non-assessable segments (text/article/video/chat) as errors, since
@@ -10,3 +11,13 @@ import type { ParsedSection } from "../parser/sections.js";
10
11
  * @returns Array of error ContentError objects for disallowed segments
11
12
  */
12
13
  export declare function validateTestSegments(segments: ParsedSection[], file: string): ContentError[];
14
+ /**
15
+ * Validate that a converted Test segment is gradable. A Test is an
16
+ * assessment, so every item in it must produce a score: legacy questions and
17
+ * roleplays always do, but new-style response segments only when they are
18
+ * graded — a Rating never is, and the other subtypes only with a rubric or
19
+ * correct answers. Returns an error for anything ungradable; the line is
20
+ * relative to the Test section body (callers offset it like the other
21
+ * conversion errors).
22
+ */
23
+ export declare function validateTestSegmentGradability(segment: ParsedLensSegment, file: string, line: number): ContentError[];
@@ -37,4 +37,49 @@ export function validateTestSegments(segments, file) {
37
37
  }
38
38
  return errors;
39
39
  }
40
+ /** How to make each response subtype gradable, for the errors below. */
41
+ const GRADABLE_HINTS = {
42
+ open: "add assessment-instructions:: with a scoring rubric",
43
+ choice: "mark at least one correct option with '- [x] Option text'",
44
+ fillblank: "make at least one blank graded ({{expected answer}} or {{number <reference>}}), or add assessment-instructions::",
45
+ ranking: "add assessment-instructions:: with a scoring rubric",
46
+ };
47
+ /**
48
+ * Validate that a converted Test segment is gradable. A Test is an
49
+ * assessment, so every item in it must produce a score: legacy questions and
50
+ * roleplays always do, but new-style response segments only when they are
51
+ * graded — a Rating never is, and the other subtypes only with a rubric or
52
+ * correct answers. Returns an error for anything ungradable; the line is
53
+ * relative to the Test section body (callers offset it like the other
54
+ * conversion errors).
55
+ */
56
+ export function validateTestSegmentGradability(segment, file, line) {
57
+ if (segment.type !== "question" || !("questionType" in segment))
58
+ return [];
59
+ if (segment.graded)
60
+ return [];
61
+ if (segment.questionType === "rating") {
62
+ return [
63
+ {
64
+ file,
65
+ line,
66
+ message: "Test section contains a 'Question: Rating' segment — ratings are never graded, so a rating cannot be part of an assessment.",
67
+ suggestion: "Remove the rating from the Test (move it into a lens or survey), or use a gradable subtype",
68
+ severity: "error",
69
+ },
70
+ ];
71
+ }
72
+ const display = segment.questionType === "fillblank"
73
+ ? "FillBlank"
74
+ : segment.questionType[0].toUpperCase() + segment.questionType.slice(1);
75
+ return [
76
+ {
77
+ file,
78
+ line,
79
+ message: `Test section contains an ungraded 'Question: ${display}' segment. Every segment in a Test must be gradable, because a Test is an assessment.`,
80
+ suggestion: `To make it gradable, ${GRADABLE_HINTS[segment.questionType]}`,
81
+ severity: "error",
82
+ },
83
+ ];
84
+ }
40
85
  //# sourceMappingURL=test-segments.js.map