lens-content-processor 0.28.0 → 0.33.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 (62) hide show
  1. package/dist/authoring-markup.d.ts +11 -0
  2. package/dist/authoring-markup.js +52 -0
  3. package/dist/authoring-markup.js.map +1 -1
  4. package/dist/cli.d.ts +1 -0
  5. package/dist/cli.js +6 -2
  6. package/dist/cli.js.map +1 -1
  7. package/dist/content-schema.js +7 -0
  8. package/dist/content-schema.js.map +1 -1
  9. package/dist/dedupe-errors.js +7 -1
  10. package/dist/dedupe-errors.js.map +1 -1
  11. package/dist/flattener/index.js +50 -16
  12. package/dist/flattener/index.js.map +1 -1
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.js +14 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/parser/article.js +34 -32
  17. package/dist/parser/article.js.map +1 -1
  18. package/dist/parser/course.d.ts +0 -10
  19. package/dist/parser/course.js +16 -14
  20. package/dist/parser/course.js.map +1 -1
  21. package/dist/parser/learning-outcome.d.ts +1 -0
  22. package/dist/parser/learning-outcome.js +28 -38
  23. package/dist/parser/learning-outcome.js.map +1 -1
  24. package/dist/parser/lens.d.ts +1 -6
  25. package/dist/parser/lens.js +64 -17
  26. package/dist/parser/lens.js.map +1 -1
  27. package/dist/parser/module.js +24 -17
  28. package/dist/parser/module.js.map +1 -1
  29. package/dist/parser/sections.d.ts +2 -1
  30. package/dist/parser/sections.js +5 -3
  31. package/dist/parser/sections.js.map +1 -1
  32. package/dist/parser/survey.js +16 -10
  33. package/dist/parser/survey.js.map +1 -1
  34. package/dist/source-location.d.ts +2 -0
  35. package/dist/source-location.js +12 -0
  36. package/dist/source-location.js.map +1 -0
  37. package/dist/validator/article-review-provenance.d.ts +5 -0
  38. package/dist/validator/article-review-provenance.js +57 -0
  39. package/dist/validator/article-review-provenance.js.map +1 -0
  40. package/dist/validator/article-structure.d.ts +8 -0
  41. package/dist/validator/article-structure.js +674 -0
  42. package/dist/validator/article-structure.js.map +1 -0
  43. package/dist/validator/directives.js +42 -0
  44. package/dist/validator/directives.js.map +1 -1
  45. package/dist/validator/emphasis.js +126 -2
  46. package/dist/validator/emphasis.js.map +1 -1
  47. package/dist/validator/html-tags.js +87 -2
  48. package/dist/validator/html-tags.js.map +1 -1
  49. package/dist/validator/math.js +66 -9
  50. package/dist/validator/math.js.map +1 -1
  51. package/dist/validator/output-integrity.js +7 -1
  52. package/dist/validator/output-integrity.js.map +1 -1
  53. package/dist/validator/same-lens-links.d.ts +4 -0
  54. package/dist/validator/same-lens-links.js +297 -0
  55. package/dist/validator/same-lens-links.js.map +1 -0
  56. package/dist/validator/suppressions.d.ts +42 -0
  57. package/dist/validator/suppressions.js +97 -0
  58. package/dist/validator/suppressions.js.map +1 -0
  59. package/dist/validator/uuid.d.ts +1 -0
  60. package/dist/validator/uuid.js +2 -0
  61. package/dist/validator/uuid.js.map +1 -1
  62. package/package.json +9 -1
@@ -1,8 +1,8 @@
1
1
  import katex from "katex";
2
- import { stripAuthoringMarkup } from "../authoring-markup.js";
2
+ import { stripAuthoringMarkupForValidation } from "../authoring-markup.js";
3
3
  const INLINE_MATH_RE = /(?<!\$)\$(?!\$)([^\s$](?:[^$\n]*?[^\s$])?)\$(?![\d$])/g;
4
4
  function maskNonMathMarkdown(content) {
5
- const authored = stripAuthoringMarkup(content);
5
+ const authored = stripAuthoringMarkupForValidation(content);
6
6
  const lines = authored.split("\n");
7
7
  let inFrontmatter = lines[0]?.trim() === "---";
8
8
  let inFence = null;
@@ -32,9 +32,26 @@ function maskNonMathMarkdown(content) {
32
32
  function lineAt(content, offset) {
33
33
  return content.slice(0, offset).split("\n").length;
34
34
  }
35
- function parseError(tex, file, line) {
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) {
36
49
  try {
37
- katex.renderToString(tex, { throwOnError: true, strict: "ignore" });
50
+ katex.renderToString(tex, {
51
+ throwOnError: true,
52
+ strict: "ignore",
53
+ displayMode,
54
+ });
38
55
  return null;
39
56
  }
40
57
  catch (error) {
@@ -48,17 +65,57 @@ function parseError(tex, file, line) {
48
65
  };
49
66
  }
50
67
  }
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
+ }
51
78
  /** Validate math exactly as isolated KaTeX expressions, matching Lens rendering. */
52
79
  export function validateMath(content, file) {
53
- const source = maskNonMathMarkdown(content);
80
+ const source = normalizeMathContainers(maskNonMathMarkdown(content));
54
81
  const errors = [];
55
82
  const withoutDisplay = source.split("");
56
83
  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
+ }
57
109
  for (const match of source.matchAll(displayRe)) {
58
110
  const offset = match.index ?? 0;
59
- const error = parseError(match[1], file, lineAt(source, offset));
60
- if (error)
61
- errors.push(error);
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
+ }
62
119
  for (let index = offset; index < offset + match[0].length; index += 1) {
63
120
  if (withoutDisplay[index] !== "\n")
64
121
  withoutDisplay[index] = " ";
@@ -77,7 +134,7 @@ export function validateMath(content, file) {
77
134
  }
78
135
  for (const match of inlineSource.matchAll(INLINE_MATH_RE)) {
79
136
  const offset = match.index ?? 0;
80
- const error = parseError(match[1], file, lineAt(inlineSource, offset));
137
+ const error = parseError(match[1], file, lineAt(inlineSource, offset), false);
81
138
  if (error)
82
139
  errors.push(error);
83
140
  }
@@ -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,3 +1,4 @@
1
+ import { getSourceLine } from '../source-location.js';
1
2
  /**
2
3
  * Broad safety-net validation on the flattened output.
3
4
  * Catches empty sections (no segments) and empty segments (no content)
@@ -6,12 +7,14 @@
6
7
  export function validateOutputIntegrity(modules, slugToPath) {
7
8
  const errors = [];
8
9
  for (const module of modules) {
9
- const file = slugToPath?.get(module.slug) ?? module.slug;
10
+ const moduleFile = slugToPath?.get(module.slug) ?? module.slug;
10
11
  for (const section of module.sections) {
12
+ const file = section.sourcePath ?? moduleFile;
11
13
  const sectionLabel = section.meta.title ?? section.type;
12
14
  if (section.segments.length === 0) {
13
15
  errors.push({
14
16
  file,
17
+ line: getSourceLine(section),
15
18
  message: `Section "${sectionLabel}" has no segments`,
16
19
  severity: 'error',
17
20
  });
@@ -24,6 +27,7 @@ export function validateOutputIntegrity(modules, slugToPath) {
24
27
  if (!segment.content?.trim()) {
25
28
  errors.push({
26
29
  file,
30
+ line: getSourceLine(segment) ?? getSourceLine(section),
27
31
  message: `Empty text segment in "${sectionLabel}" (segment ${i + 1})`,
28
32
  severity: 'error',
29
33
  });
@@ -33,6 +37,7 @@ export function validateOutputIntegrity(modules, slugToPath) {
33
37
  if (!segment.content?.trim()) {
34
38
  errors.push({
35
39
  file,
40
+ line: getSourceLine(segment) ?? getSourceLine(section),
36
41
  message: `Empty article segment in "${sectionLabel}" (segment ${i + 1})`,
37
42
  severity: 'error',
38
43
  });
@@ -42,6 +47,7 @@ export function validateOutputIntegrity(modules, slugToPath) {
42
47
  if (!segment.transcript?.length) {
43
48
  errors.push({
44
49
  file,
50
+ line: getSourceLine(segment) ?? getSourceLine(section),
45
51
  message: `Empty video transcript in "${sectionLabel}" (segment ${i + 1})`,
46
52
  severity: 'error',
47
53
  });
@@ -1 +1 @@
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
+ {"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"}
@@ -0,0 +1,4 @@
1
+ import type { ContentError } from "../index.js";
2
+ import type { ParsedLens } from "../parser/lens.js";
3
+ /** Validate canonical same-Lens links against every authored text segment. */
4
+ export declare function validateSameLensLinks(lens: ParsedLens, file: string, source: string, lineOffset?: number): ContentError[];
@@ -0,0 +1,297 @@
1
+ import remarkDirective from "remark-directive";
2
+ import remarkGfm from "remark-gfm";
3
+ import remarkMath from "remark-math";
4
+ import remarkParse from "remark-parse";
5
+ import { unified } from "unified";
6
+ import { visit } from "unist-util-visit";
7
+ import { stripAuthoringMarkupForValidation } from "../authoring-markup.js";
8
+ function authoredText(segment) {
9
+ const values = [];
10
+ if ("content" in segment && typeof segment.content === "string")
11
+ values.push(segment.content);
12
+ if (segment.type === "roleplay") {
13
+ values.push(segment.aiInstructions);
14
+ if (segment.openingMessage)
15
+ values.push(segment.openingMessage);
16
+ if (segment.assessmentInstructions)
17
+ values.push(segment.assessmentInstructions);
18
+ }
19
+ if (segment.type === "question" && segment.assessmentInstructions)
20
+ values.push(segment.assessmentInstructions);
21
+ return values;
22
+ }
23
+ function lineAt(source, offset) {
24
+ return source.slice(0, offset).split("\n").length;
25
+ }
26
+ function sourceLocator(source) {
27
+ const cursors = new Map();
28
+ return (needle) => {
29
+ const from = cursors.get(needle) ?? 0;
30
+ let offset = source.indexOf(needle, from);
31
+ if (offset < 0)
32
+ offset = source.indexOf(needle);
33
+ if (offset < 0)
34
+ return 1;
35
+ cursors.set(needle, offset + needle.length);
36
+ return lineAt(source, offset);
37
+ };
38
+ }
39
+ function headingAnchor(value) {
40
+ return value.trim().toLocaleLowerCase().replace(/\s+/g, " ");
41
+ }
42
+ function maskCode(value) {
43
+ const chars = value.split("");
44
+ let fence;
45
+ let offset = 0;
46
+ for (const line of value.split("\n")) {
47
+ const marker = line.match(/^ {0,3}(`{3,}|~{3,})/);
48
+ const wasInFence = fence !== undefined;
49
+ const closesFence = Boolean(marker &&
50
+ fence &&
51
+ marker[1][0] === fence.char &&
52
+ marker[1].length >= fence.length &&
53
+ line.slice(marker[1].length).trim() === "");
54
+ for (let index = 0; index < line.length; index += 1) {
55
+ if (wasInFence || marker)
56
+ chars[offset + index] = " ";
57
+ }
58
+ if (closesFence)
59
+ fence = undefined;
60
+ else if (marker && !wasInFence)
61
+ fence = { char: marker[1][0], length: marker[1].length };
62
+ offset += line.length + 1;
63
+ }
64
+ return chars
65
+ .join("")
66
+ .replace(/(`+)(.*?)\1/g, (match) => " ".repeat(match.length));
67
+ }
68
+ function markerSource(source) {
69
+ const validationSource = stripAuthoringMarkupForValidation(source);
70
+ const chars = validationSource.split("");
71
+ const tree = unified()
72
+ .use(remarkParse)
73
+ .use(remarkGfm)
74
+ .use(remarkDirective)
75
+ .use(remarkMath)
76
+ .parse(validationSource);
77
+ visit(tree, (node) => {
78
+ if (!["code", "inlineCode", "math", "inlineMath"].includes(node.type))
79
+ return;
80
+ const start = node.position?.start.offset;
81
+ const end = node.position?.end.offset;
82
+ if (start === undefined || end === undefined)
83
+ return;
84
+ for (let index = start; index < end; index += 1) {
85
+ if (chars[index] !== "\n")
86
+ chars[index] = " ";
87
+ }
88
+ });
89
+ const standaloneAttachment = new Map();
90
+ const walk = (parent) => {
91
+ for (let index = 0; index < parent.children.length; index += 1) {
92
+ const node = parent.children[index];
93
+ if (node.type === "paragraph" &&
94
+ node.children.length === 1 &&
95
+ node.children[0].type === "text" &&
96
+ /^\^[^\s]+\s*$/.test(node.children[0].value)) {
97
+ standaloneAttachment.set(node.position?.start.line ?? 1, index > 0);
98
+ }
99
+ if ("children" in node && Array.isArray(node.children)) {
100
+ walk(node);
101
+ }
102
+ }
103
+ };
104
+ walk(tree);
105
+ return { masked: chars.join(""), standaloneAttachment };
106
+ }
107
+ function blockMarkerErrors(file, source) {
108
+ const errors = [];
109
+ const blockIds = new Map();
110
+ const { masked, standaloneAttachment } = markerSource(source);
111
+ const lines = masked.split("\n");
112
+ for (let index = 0; index < lines.length; index += 1) {
113
+ const match = lines[index].match(/(?:^|[ \t])\^([^\s]+)\s*$/);
114
+ if (!match)
115
+ continue;
116
+ const id = match[1];
117
+ const line = index + 1;
118
+ if (!/^[A-Za-z0-9-]+$/.test(id)) {
119
+ errors.push({
120
+ file,
121
+ line,
122
+ code: "lens.block-id-invalid",
123
+ severity: "error",
124
+ message: `Invalid same-Lens block ID '^${id}'`,
125
+ suggestion: "Use only letters, numbers, and dashes.",
126
+ });
127
+ continue;
128
+ }
129
+ blockIds.set(id, [...(blockIds.get(id) ?? []), line]);
130
+ const standalone = lines[index].trim() === `^${id}`;
131
+ if (standalone) {
132
+ const hasPriorContent = lines
133
+ .slice(0, index)
134
+ .some((candidate) => candidate.trim() !== "");
135
+ const blankBefore = index > 0 && lines[index - 1].trim() === "";
136
+ const blankAfter = index === lines.length - 1 || lines[index + 1].trim() === "";
137
+ if (!hasPriorContent) {
138
+ errors.push({
139
+ file,
140
+ line,
141
+ code: "lens.block-id-orphan",
142
+ severity: "error",
143
+ message: `Block marker '^${id}' is not attached to a content block`,
144
+ suggestion: "Place it after the block it labels.",
145
+ });
146
+ }
147
+ else if (!blankBefore || !blankAfter) {
148
+ errors.push({
149
+ file,
150
+ line,
151
+ code: "lens.block-id-position",
152
+ severity: "error",
153
+ message: `Structured block marker '^${id}' must have a blank line before and after it`,
154
+ suggestion: `Use a blank line, ^${id}, then another blank line.`,
155
+ });
156
+ }
157
+ else if (!standaloneAttachment.get(line)) {
158
+ errors.push({
159
+ file,
160
+ line,
161
+ code: "lens.block-id-orphan",
162
+ severity: "error",
163
+ message: `Block marker '^${id}' is not attached to a content block`,
164
+ suggestion: "Place it after the block it labels.",
165
+ });
166
+ }
167
+ }
168
+ else if (/^:{3,}\s+\^/.test(lines[index].trim())) {
169
+ errors.push({
170
+ file,
171
+ line,
172
+ code: "lens.block-id-position",
173
+ severity: "error",
174
+ message: `Block marker '^${id}' cannot share a directive closing fence`,
175
+ suggestion: `Close with :::, then put ^${id} on its own line with blank lines around it.`,
176
+ });
177
+ }
178
+ }
179
+ for (const [id, markerLines] of blockIds) {
180
+ if (markerLines.length > 1) {
181
+ errors.push({
182
+ file,
183
+ line: markerLines[1],
184
+ code: "lens.block-id-duplicate",
185
+ severity: "error",
186
+ message: `Duplicate same-Lens block ID '^${id}'`,
187
+ });
188
+ }
189
+ }
190
+ return { errors, blockIds };
191
+ }
192
+ /** Validate canonical same-Lens links against every authored text segment. */
193
+ export function validateSameLensLinks(lens, file, source, lineOffset = 0) {
194
+ const markerResult = blockMarkerErrors(file, source);
195
+ const errors = [...markerResult.errors];
196
+ const texts = lens.segments.flatMap(authoredText).map(maskCode);
197
+ const headings = new Map();
198
+ const locate = sourceLocator(source);
199
+ for (const text of texts) {
200
+ for (const match of text.matchAll(/^#{1,6}\s+(.+?)\s*#*\s*$/gm)) {
201
+ const name = headingAnchor(match[1].replace(/\s+\^[A-Za-z0-9-]+\s*$/, ""));
202
+ headings.set(name, [...(headings.get(name) ?? []), locate(match[0])]);
203
+ }
204
+ }
205
+ for (const text of texts) {
206
+ const malformedOffsets = new Set();
207
+ for (const match of text.matchAll(/\[\[#[^\]\n]*(?:$|\](?!\]))/gm)) {
208
+ malformedOffsets.add(match.index ?? 0);
209
+ errors.push({
210
+ file,
211
+ line: locate(match[0]),
212
+ code: "lens.wikilink-malformed",
213
+ severity: "error",
214
+ message: `Malformed same-Lens wikilink '${match[0].trim()}'`,
215
+ suggestion: "Close the link with ]] and use at most one |label.",
216
+ });
217
+ }
218
+ for (const match of text.matchAll(/\[\[#([^\]\n]*)\]\]/g)) {
219
+ if (malformedOffsets.has(match.index ?? 0))
220
+ continue;
221
+ const raw = match[1];
222
+ const [target, label, ...extra] = raw.split("|");
223
+ const line = locate(match[0]);
224
+ if (!target.trim() || extra.length || (label !== undefined && !label.trim())) {
225
+ errors.push({
226
+ file,
227
+ line,
228
+ code: "lens.wikilink-malformed",
229
+ severity: "error",
230
+ message: `Malformed same-Lens wikilink '${match[0]}'`,
231
+ suggestion: "Use [[#Heading text|label]] or [[#^block-id|label]].",
232
+ });
233
+ continue;
234
+ }
235
+ if (target.startsWith("^")) {
236
+ const id = target.slice(1).trim();
237
+ if (!/^[A-Za-z0-9-]+$/.test(id)) {
238
+ errors.push({
239
+ file,
240
+ line,
241
+ code: "lens.wikilink-malformed",
242
+ severity: "error",
243
+ message: `Invalid same-Lens block link '#^${id}'`,
244
+ suggestion: "Use letters, numbers, and dashes in block IDs.",
245
+ });
246
+ }
247
+ else if (!markerResult.blockIds.has(id)) {
248
+ errors.push({
249
+ file,
250
+ line,
251
+ code: "lens.target-missing",
252
+ severity: "error",
253
+ message: `Same-Lens block target '^${id}' does not exist`,
254
+ });
255
+ }
256
+ }
257
+ else {
258
+ const count = headings.get(headingAnchor(target))?.length ?? 0;
259
+ if (count === 0) {
260
+ errors.push({
261
+ file,
262
+ line,
263
+ code: "lens.target-missing",
264
+ severity: "error",
265
+ message: `Same-Lens heading target '${target}' does not exist`,
266
+ });
267
+ }
268
+ else if (count > 1) {
269
+ errors.push({
270
+ file,
271
+ line,
272
+ code: "lens.heading-ambiguous",
273
+ severity: "error",
274
+ message: `Same-Lens heading target '${target}' is ambiguous because the heading is duplicated`,
275
+ });
276
+ }
277
+ }
278
+ }
279
+ for (const match of text.matchAll(/\]\(anchor:(?:block|heading):[^)]+\)/g)) {
280
+ errors.push({
281
+ file,
282
+ line: locate(match[0]),
283
+ code: "lens.anchor-private",
284
+ severity: "error",
285
+ message: "The renderer-private anchor: scheme cannot be authored directly",
286
+ suggestion: "Use [[#^block-id|label]] or [[#Heading text|label]].",
287
+ });
288
+ }
289
+ }
290
+ return lineOffset === 0
291
+ ? errors
292
+ : errors.map((error) => ({
293
+ ...error,
294
+ line: error.line === undefined ? undefined : error.line + lineOffset,
295
+ }));
296
+ }
297
+ //# sourceMappingURL=same-lens-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"same-lens-links.js","sourceRoot":"","sources":["../../src/validator/same-lens-links.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { ContentError } from "../index.js";
2
+ export interface ValidatorSuppressionDefinition {
3
+ code: string;
4
+ reason: string;
5
+ explanation: string;
6
+ }
7
+ export declare const VALIDATOR_SUPPRESSIONS: {
8
+ readonly imageLowResolutionAiSearchExhausted: {
9
+ readonly code: "article.image-low-resolution";
10
+ readonly reason: "AI-couldn't-find-higher-resolution-image";
11
+ readonly explanation: "An AI search could not find a higher-resolution image.";
12
+ };
13
+ readonly imageLowResolutionAiAndHumanSearchExhausted: {
14
+ readonly code: "article.image-low-resolution";
15
+ readonly reason: "AI-and-human-couldn't-find-higher-resolution-image";
16
+ readonly explanation: "Both AI and human searches could not find a higher-resolution image.";
17
+ };
18
+ readonly blockRepeatedDistinctStructuredEntries: {
19
+ readonly code: "article.block-repeated-nearby";
20
+ readonly reason: "intentional-repeat-in-distinct-structured-entries";
21
+ readonly explanation: "The same text intentionally applies to separate structured entries.";
22
+ };
23
+ readonly blockRepeatedAlternativeCitationFormats: {
24
+ readonly code: "article.block-repeated-nearby";
25
+ readonly reason: "source-provides-alternative-citation-formats";
26
+ readonly explanation: "The source intentionally repeats citation text in alternative formats.";
27
+ };
28
+ readonly externalSelfFragmentTargetsSourceOnlyContent: {
29
+ readonly code: "article.external-self-fragment";
30
+ readonly reason: "intentionally-targets-source-only-content";
31
+ readonly explanation: "The link intentionally targets source-page content that was not imported.";
32
+ };
33
+ };
34
+ export declare function formatValidatorSuppression(definition: ValidatorSuppressionDefinition): string;
35
+ export declare function validatorSuppressionsForCode(code: string): readonly ValidatorSuppressionDefinition[];
36
+ /**
37
+ * Apply allow-listed, next-line suppressions found in Obsidian comments.
38
+ *
39
+ * `source` must preserve the article body's line positions. Callers may mask
40
+ * code/math first so documentation examples are not interpreted as directives.
41
+ */
42
+ export declare function applyNextLineValidatorSuppressions(source: string, bodyStartLine: number, errors: ContentError[], file: string): ContentError[];
@@ -0,0 +1,97 @@
1
+ export const VALIDATOR_SUPPRESSIONS = {
2
+ imageLowResolutionAiSearchExhausted: {
3
+ code: "article.image-low-resolution",
4
+ reason: "AI-couldn't-find-higher-resolution-image",
5
+ explanation: "An AI search could not find a higher-resolution image.",
6
+ },
7
+ imageLowResolutionAiAndHumanSearchExhausted: {
8
+ code: "article.image-low-resolution",
9
+ reason: "AI-and-human-couldn't-find-higher-resolution-image",
10
+ explanation: "Both AI and human searches could not find a higher-resolution image.",
11
+ },
12
+ blockRepeatedDistinctStructuredEntries: {
13
+ code: "article.block-repeated-nearby",
14
+ reason: "intentional-repeat-in-distinct-structured-entries",
15
+ explanation: "The same text intentionally applies to separate structured entries.",
16
+ },
17
+ blockRepeatedAlternativeCitationFormats: {
18
+ code: "article.block-repeated-nearby",
19
+ reason: "source-provides-alternative-citation-formats",
20
+ explanation: "The source intentionally repeats citation text in alternative formats.",
21
+ },
22
+ externalSelfFragmentTargetsSourceOnlyContent: {
23
+ code: "article.external-self-fragment",
24
+ reason: "intentionally-targets-source-only-content",
25
+ explanation: "The link intentionally targets source-page content that was not imported.",
26
+ },
27
+ };
28
+ const suppressionDefinitions = Object.values(VALIDATOR_SUPPRESSIONS);
29
+ const suppressionsByCode = new Map();
30
+ for (const definition of suppressionDefinitions) {
31
+ const reasons = suppressionsByCode.get(definition.code) ?? new Map();
32
+ if (reasons.has(definition.reason)) {
33
+ throw new Error(`Duplicate validator suppression: ${definition.code}/${definition.reason}`);
34
+ }
35
+ reasons.set(definition.reason, definition);
36
+ suppressionsByCode.set(definition.code, reasons);
37
+ }
38
+ export function formatValidatorSuppression(definition) {
39
+ return `%% validator-ignore-next-line --code ${definition.code} --reason ${definition.reason} %%`;
40
+ }
41
+ export function validatorSuppressionsForCode(code) {
42
+ return [...(suppressionsByCode.get(code)?.values() ?? [])];
43
+ }
44
+ function suppressionIssue(file, bodyStartLine, relativeLine, code, message, suggestion) {
45
+ return {
46
+ file,
47
+ line: bodyStartLine + relativeLine - 1,
48
+ code,
49
+ severity: "error",
50
+ message,
51
+ suggestion,
52
+ };
53
+ }
54
+ /**
55
+ * Apply allow-listed, next-line suppressions found in Obsidian comments.
56
+ *
57
+ * `source` must preserve the article body's line positions. Callers may mask
58
+ * code/math first so documentation examples are not interpreted as directives.
59
+ */
60
+ export function applyNextLineValidatorSuppressions(source, bodyStartLine, errors, file) {
61
+ const remaining = [...errors];
62
+ const suppressionErrors = [];
63
+ const example = formatValidatorSuppression(suppressionDefinitions[0]);
64
+ source.split("\n").forEach((line, index) => {
65
+ if (!/^\s*%%\s*validator-ignore-next-line\b/.test(line))
66
+ return;
67
+ const markerLine = index + 1;
68
+ const match = line.match(/^\s*%%\s*validator-ignore-next-line\s+--code\s+([a-z0-9.-]+)\s+--reason\s+([A-Za-z0-9'-]+)\s*%%\s*$/);
69
+ if (!match) {
70
+ suppressionErrors.push(suppressionIssue(file, bodyStartLine, markerLine, "article.suppression-invalid", "Validator suppression is malformed", `Use the exact CLI-style form, for example: ${example}`));
71
+ return;
72
+ }
73
+ const [, code, reason] = match;
74
+ const allowedReasons = suppressionsByCode.get(code);
75
+ if (!allowedReasons) {
76
+ suppressionErrors.push(suppressionIssue(file, bodyStartLine, markerLine, "article.suppression-invalid", `Validation rule '${code}' cannot be suppressed`, "Only explicitly registered validation codes can be suppressed."));
77
+ return;
78
+ }
79
+ const definition = allowedReasons.get(reason);
80
+ if (!definition) {
81
+ const choices = [...allowedReasons.values()]
82
+ .map((entry) => `'${entry.reason}' (${entry.explanation})`)
83
+ .join(", ");
84
+ suppressionErrors.push(suppressionIssue(file, bodyStartLine, markerLine, "article.suppression-invalid", `Reason '${reason}' is not allowed for '${code}'`, `Allowed reason${allowedReasons.size === 1 ? "" : "s"}: ${choices}`));
85
+ return;
86
+ }
87
+ const targetLine = bodyStartLine + markerLine;
88
+ const errorIndex = remaining.findIndex((entry) => entry.code === code && entry.line === targetLine);
89
+ if (errorIndex === -1) {
90
+ suppressionErrors.push(suppressionIssue(file, bodyStartLine, markerLine, "article.suppression-unused", `Suppression for '${code}' does not match an error on the next line`, "Remove the stale suppression, or place it immediately before the affected content."));
91
+ return;
92
+ }
93
+ remaining.splice(errorIndex, 1);
94
+ });
95
+ return [...remaining, ...suppressionErrors];
96
+ }
97
+ //# sourceMappingURL=suppressions.js.map