lens-content-processor 0.2.1 → 0.2.2

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 (60) hide show
  1. package/dist/bundler/article.js +16 -4
  2. package/dist/bundler/article.js.map +1 -1
  3. package/dist/bundler/video.js +4 -4
  4. package/dist/bundler/video.js.map +1 -1
  5. package/dist/cli.js +7 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/content-schema.d.ts +34 -0
  8. package/dist/content-schema.js +61 -0
  9. package/dist/content-schema.js.map +1 -0
  10. package/dist/flattener/index.js +58 -22
  11. package/dist/flattener/index.js.map +1 -1
  12. package/dist/index.d.ts +12 -1
  13. package/dist/index.js +121 -3
  14. package/dist/index.js.map +1 -1
  15. package/dist/parser/article.d.ts +16 -0
  16. package/dist/parser/article.js +67 -0
  17. package/dist/parser/article.js.map +1 -0
  18. package/dist/parser/course.js +13 -20
  19. package/dist/parser/course.js.map +1 -1
  20. package/dist/parser/frontmatter.js +2 -0
  21. package/dist/parser/frontmatter.js.map +1 -1
  22. package/dist/parser/learning-outcome.js +21 -9
  23. package/dist/parser/learning-outcome.js.map +1 -1
  24. package/dist/parser/lens.js +131 -14
  25. package/dist/parser/lens.js.map +1 -1
  26. package/dist/parser/module.d.ts +11 -5
  27. package/dist/parser/module.js +171 -75
  28. package/dist/parser/module.js.map +1 -1
  29. package/dist/parser/sections.js +113 -12
  30. package/dist/parser/sections.js.map +1 -1
  31. package/dist/parser/video-transcript.d.ts +11 -0
  32. package/dist/parser/video-transcript.js +39 -0
  33. package/dist/parser/video-transcript.js.map +1 -0
  34. package/dist/parser/wikilink.d.ts +2 -0
  35. package/dist/parser/wikilink.js +83 -24
  36. package/dist/parser/wikilink.js.map +1 -1
  37. package/dist/validator/duplicates.d.ts +13 -0
  38. package/dist/validator/duplicates.js +27 -0
  39. package/dist/validator/duplicates.js.map +1 -0
  40. package/dist/validator/field-typos.d.ts +11 -0
  41. package/dist/validator/field-typos.js +45 -28
  42. package/dist/validator/field-typos.js.map +1 -1
  43. package/dist/validator/field-values.d.ts +14 -0
  44. package/dist/validator/field-values.js +61 -7
  45. package/dist/validator/field-values.js.map +1 -1
  46. package/dist/validator/output-integrity.d.ts +7 -0
  47. package/dist/validator/output-integrity.js +57 -0
  48. package/dist/validator/output-integrity.js.map +1 -0
  49. package/dist/validator/segment-fields.js +1 -15
  50. package/dist/validator/segment-fields.js.map +1 -1
  51. package/dist/validator/timestamps.d.ts +2 -0
  52. package/dist/validator/timestamps.js +91 -0
  53. package/dist/validator/timestamps.js.map +1 -0
  54. package/dist/validator/url-reachability.d.ts +8 -0
  55. package/dist/validator/url-reachability.js +60 -0
  56. package/dist/validator/url-reachability.js.map +1 -0
  57. package/dist/validator/validate-frontmatter.d.ts +9 -0
  58. package/dist/validator/validate-frontmatter.js +53 -0
  59. package/dist/validator/validate-frontmatter.js.map +1 -0
  60. package/package.json +1 -1
@@ -1,29 +1,49 @@
1
+ import { levenshtein } from '../validator/field-typos.js';
1
2
  // Valid section types per file type (exported for use by other parsers)
2
3
  export const MODULE_SECTION_TYPES = new Set(['learning outcome', 'page', 'uncategorized']);
3
4
  export const LO_SECTION_TYPES = new Set(['lens', 'test']);
4
- // Lens sections: input headers are `### Article:`, `### Video:`, `### Text:`
5
- // Output types are `lens-article`, `lens-video`, `text` (v2 format)
6
- export const LENS_SECTION_TYPES = new Set(['text', 'article', 'video']);
5
+ // Lens sections: input headers are `### Article:`, `### Video:`, `### Page:`
6
+ // Output types are `lens-article`, `lens-video`, `page` (v2 format)
7
+ export const LENS_SECTION_TYPES = new Set(['page', 'article', 'video']);
8
+ // All known structural header types (sections + segments) for markdown heading detection
9
+ const ALL_STRUCTURAL_TYPES = new Set([
10
+ // Section types
11
+ 'learning outcome', 'page', 'uncategorized', 'lens', 'test', 'module', 'meeting', 'article', 'video',
12
+ // Segment types
13
+ 'text', 'chat', 'article-excerpt', 'video-excerpt',
14
+ ]);
15
+ // Fields that commonly contain markdown with headings
16
+ const MARKDOWN_CONTENT_FIELDS = new Set(['content', 'instructions']);
7
17
  // Map input section names to output types for Lens files
8
18
  export const LENS_OUTPUT_TYPE = {
9
- 'text': 'text',
19
+ 'page': 'page',
10
20
  'article': 'lens-article',
11
21
  'video': 'lens-video',
12
22
  };
13
23
  // Header pattern is parameterized by level (1-4)
14
24
  function makeSectionPattern(level) {
15
25
  const hashes = '#'.repeat(level);
16
- // Match: ^#{level} <type>: <title>$
17
- // Captures: group 1 = type, group 2 = title
18
- return new RegExp(`^${hashes}\\s+([^:]+):\\s*(.*)$`, 'i');
26
+ // Match: ^#{level} <type> OR ^#{level} <type>: <optional title>
27
+ // Captures: group 1 = type, group 2 = title (may be undefined)
28
+ return new RegExp(`^${hashes}\\s+([^:]+?)(?::\\s*(.*))?$`, 'i');
19
29
  }
30
+ // Note: unrecognized headers are now caught by makeSectionPattern matching all
31
+ // ### headers, with unknown types reported as "Unknown section type" errors.
20
32
  export function parseSections(content, headerLevel, validTypes, file = '') {
21
33
  const SECTION_HEADER_PATTERN = makeSectionPattern(headerLevel);
34
+ // Build patterns for adjacent levels to detect wrong heading level
35
+ const wrongLevelPatterns = [];
36
+ for (const adjLevel of [headerLevel - 1, headerLevel + 1]) {
37
+ if (adjLevel >= 1 && adjLevel <= 4) {
38
+ wrongLevelPatterns.push({ pattern: makeSectionPattern(adjLevel), level: adjLevel });
39
+ }
40
+ }
22
41
  const lines = content.split('\n');
23
42
  const sections = [];
24
43
  const errors = [];
25
44
  let currentSection = null;
26
45
  let currentBody = [];
46
+ let preHeaderWarned = false;
27
47
  for (let i = 0; i < lines.length; i++) {
28
48
  const line = lines[i];
29
49
  const lineNum = i + 1;
@@ -38,13 +58,14 @@ export function parseSections(content, headerLevel, validTypes, file = '') {
38
58
  }
39
59
  const rawType = headerMatch[1].trim();
40
60
  const normalizedType = rawType.toLowerCase();
41
- const title = headerMatch[2].trim();
61
+ const title = (headerMatch[2] ?? '').trim();
42
62
  if (!validTypes.has(normalizedType)) {
63
+ const capitalized = [...validTypes].map(t => t.split(' ').map(w => w[0].toUpperCase() + w.slice(1)).join(' '));
43
64
  errors.push({
44
65
  file,
45
66
  line: lineNum,
46
67
  message: `Unknown section type: ${rawType}`,
47
- suggestion: `Valid types: ${[...validTypes].join(', ')}`,
68
+ suggestion: `Valid types: ${capitalized.join(', ')}`,
48
69
  severity: 'error',
49
70
  });
50
71
  }
@@ -58,8 +79,42 @@ export function parseSections(content, headerLevel, validTypes, file = '') {
58
79
  };
59
80
  currentBody = [];
60
81
  }
61
- else if (currentSection) {
62
- currentBody.push(line);
82
+ else {
83
+ if (currentSection) {
84
+ currentBody.push(line);
85
+ }
86
+ else if (!currentSection) {
87
+ // Check for headers at wrong level that match known section types
88
+ for (const { pattern, level } of wrongLevelPatterns) {
89
+ const wrongMatch = line.match(pattern);
90
+ if (wrongMatch) {
91
+ const rawType = wrongMatch[1].trim();
92
+ if (validTypes.has(rawType.toLowerCase())) {
93
+ const expected = '#'.repeat(headerLevel);
94
+ const actual = '#'.repeat(level);
95
+ errors.push({
96
+ file,
97
+ line: lineNum,
98
+ message: `Found '${actual} ${rawType}:' (heading level ${level}) but expected heading level ${headerLevel} (${expected} ${rawType}:)`,
99
+ suggestion: `Change '${actual}' to '${expected}'`,
100
+ severity: 'warning',
101
+ });
102
+ }
103
+ break;
104
+ }
105
+ }
106
+ }
107
+ if (!currentSection && line.trim() && !preHeaderWarned) {
108
+ // Non-blank content before any section header — will be silently lost
109
+ preHeaderWarned = true;
110
+ errors.push({
111
+ file,
112
+ line: lineNum,
113
+ message: 'Content found before first section header — this text will be ignored',
114
+ suggestion: 'Move this text into a section, or remove it',
115
+ severity: 'warning',
116
+ });
117
+ }
63
118
  }
64
119
  }
65
120
  // Don't forget last section
@@ -78,11 +133,32 @@ function parseFields(section, file) {
78
133
  const seenFields = new Set();
79
134
  let currentField = null;
80
135
  let currentValue = [];
136
+ let freeTextWarned = false;
81
137
  for (let i = 0; i < lines.length; i++) {
82
138
  const line = lines[i];
83
139
  const lineNum = section.line + i + 1; // +1 because body starts after header
84
140
  // Check for sub-header first - starts a new scope for field tracking
85
- if (line.match(/^#{1,6}\s/)) {
141
+ const headerMatch = line.match(/^(#{1,6})\s+(.*)$/);
142
+ if (headerMatch) {
143
+ // Before resetting, check if this looks like a markdown heading
144
+ // inside a content/instructions field (not a structural header)
145
+ if (currentField && MARKDOWN_CONTENT_FIELDS.has(currentField)) {
146
+ const headingText = headerMatch[2].trim();
147
+ // Extract type word: "Text", "Chat: title", "Article-excerpt" etc.
148
+ const typeWord = headingText.replace(/:.*$/, '').trim().toLowerCase();
149
+ // Also check if it's a typo of a structural type (levenshtein ≤ 2)
150
+ const isTypoOfStructural = [...ALL_STRUCTURAL_TYPES].some(st => levenshtein(typeWord, st) <= 2);
151
+ if (!ALL_STRUCTURAL_TYPES.has(typeWord) && !isTypoOfStructural) {
152
+ const hashes = headerMatch[1];
153
+ warnings.push({
154
+ file,
155
+ line: lineNum,
156
+ message: `'${hashes} ${headingText}' looks like a Markdown heading inside ${currentField}:: field`,
157
+ suggestion: `Escape it as '!${hashes} ${headingText}' so it's treated as content, not a section boundary`,
158
+ severity: 'warning',
159
+ });
160
+ }
161
+ }
86
162
  // Save current field if any
87
163
  if (currentField) {
88
164
  section.fields[currentField] = currentValue.join('\n').trim();
@@ -91,6 +167,7 @@ function parseFields(section, file) {
91
167
  }
92
168
  // Reset seenFields for the new sub-section scope
93
169
  seenFields.clear();
170
+ freeTextWarned = false;
94
171
  continue;
95
172
  }
96
173
  const match = line.match(FIELD_PATTERN);
@@ -118,6 +195,30 @@ function parseFields(section, file) {
118
195
  // Continue multiline value
119
196
  currentValue.push(line);
120
197
  }
198
+ else {
199
+ // Not inside a field — check for single-colon that should be double-colon
200
+ const singleColonMatch = line.match(/^(\w+):\s+(.*)$/);
201
+ if (singleColonMatch && !line.match(/^https?:/)) {
202
+ warnings.push({
203
+ file,
204
+ line: lineNum,
205
+ message: `Found '${singleColonMatch[1]}:' with single colon — did you mean '${singleColonMatch[1]}::'?`,
206
+ suggestion: `Change '${singleColonMatch[1]}:' to '${singleColonMatch[1]}::' (double colon)`,
207
+ severity: 'warning',
208
+ });
209
+ }
210
+ else if (line.trim() && !freeTextWarned) {
211
+ freeTextWarned = true;
212
+ const preview = line.trim().length > 60 ? line.trim().slice(0, 60) + '...' : line.trim();
213
+ warnings.push({
214
+ file,
215
+ line: lineNum,
216
+ message: `Text outside of a field:: definition will be ignored: "${preview}"`,
217
+ suggestion: 'Place this text inside a field (e.g., content:: your text), or remove it',
218
+ severity: 'warning',
219
+ });
220
+ }
221
+ }
121
222
  }
122
223
  // Save final field
123
224
  if (currentField) {
@@ -1 +1 @@
1
- {"version":3,"file":"sections.js","sourceRoot":"","sources":["../../src/parser/sections.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sections.js","sourceRoot":"","sources":["../../src/parser/sections.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { ContentError } from '../index.js';
2
+ export interface ParsedVideoTranscript {
3
+ title: string;
4
+ channel: string;
5
+ url: string;
6
+ }
7
+ export interface VideoTranscriptParseResult {
8
+ transcript: ParsedVideoTranscript | null;
9
+ errors: ContentError[];
10
+ }
11
+ export declare function parseVideoTranscript(content: string, file: string): VideoTranscriptParseResult;
@@ -0,0 +1,39 @@
1
+ import { parseFrontmatter } from './frontmatter.js';
2
+ import { validateFrontmatter } from '../validator/validate-frontmatter.js';
3
+ export function parseVideoTranscript(content, file) {
4
+ const errors = [];
5
+ const frontmatterResult = parseFrontmatter(content, file);
6
+ if (frontmatterResult.error) {
7
+ errors.push(frontmatterResult.error);
8
+ return { transcript: null, errors };
9
+ }
10
+ const { frontmatter } = frontmatterResult;
11
+ // Validate frontmatter against schema (typo detection + required fields)
12
+ const frontmatterErrors = validateFrontmatter(frontmatter, 'video-transcript', file);
13
+ errors.push(...frontmatterErrors);
14
+ const hasRequiredError = frontmatterErrors.some(e => e.severity === 'error');
15
+ if (hasRequiredError) {
16
+ return { transcript: null, errors };
17
+ }
18
+ // Check text fields for wikilinks (not url — that's a URL)
19
+ const textFields = ['title', 'channel'];
20
+ for (const field of textFields) {
21
+ const value = String(frontmatter[field]);
22
+ if (/\[\[.+?\]\]/.test(value)) {
23
+ errors.push({
24
+ file,
25
+ line: 2,
26
+ message: `Field '${field}' contains a wikilink — use plain text`,
27
+ suggestion: `Remove [[...]] from '${field}'`,
28
+ severity: 'error',
29
+ });
30
+ }
31
+ }
32
+ const transcript = {
33
+ title: String(frontmatter.title),
34
+ channel: String(frontmatter.channel),
35
+ url: String(frontmatter.url),
36
+ };
37
+ return { transcript, errors };
38
+ }
39
+ //# sourceMappingURL=video-transcript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-transcript.js","sourceRoot":"","sources":["../../src/parser/video-transcript.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAa3E,MAAM,UAAU,oBAAoB,CAAC,OAAe,EAAE,IAAY;IAChE,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1D,IAAI,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACrC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACtC,CAAC;IAED,MAAM,EAAE,WAAW,EAAE,GAAG,iBAAiB,CAAC;IAE1C,yEAAyE;IACzE,MAAM,iBAAiB,GAAG,mBAAmB,CAAC,WAAW,EAAE,kBAAkB,EAAE,IAAI,CAAC,CAAC;IACrF,MAAM,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,CAAC;IAElC,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;IAC7E,IAAI,gBAAgB,EAAE,CAAC;QACrB,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACtC,CAAC;IAED,2DAA2D;IAC3D,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,SAAS,CAAU,CAAC;IACjD,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI;gBACJ,IAAI,EAAE,CAAC;gBACP,OAAO,EAAE,UAAU,KAAK,wCAAwC;gBAChE,UAAU,EAAE,wBAAwB,KAAK,GAAG;gBAC5C,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAA0B;QACxC,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC;QAChC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC;QACpC,GAAG,EAAE,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC;KAC7B,CAAC;IAEF,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AAChC,CAAC"}
@@ -2,6 +2,8 @@ export interface WikilinkParts {
2
2
  path: string;
3
3
  display?: string;
4
4
  isEmbed?: boolean;
5
+ error?: string;
6
+ correctedPath?: string;
5
7
  }
6
8
  export declare function parseWikilink(text: string): WikilinkParts | null;
7
9
  export declare function resolveWikilinkPath(linkPath: string, sourceFile: string): string;
@@ -1,13 +1,95 @@
1
1
  // src/parser/wikilink.ts
2
2
  import { join, dirname, normalize } from 'path';
3
+ import { levenshtein } from '../validator/field-typos.js';
3
4
  // Matches [[path]], [[path|display]], ![[embed]], ![[embed|display]]
4
5
  const WIKILINK_PATTERN = /^!?\[\[([^\]|]+)(?:\|([^\]]+))?\]\]$/;
6
+ /**
7
+ * Check if a path contains a path traversal attack.
8
+ *
9
+ * We allow:
10
+ * - Single `../` at the start for legitimate relative references (e.g., ../Lenses/foo.md)
11
+ * - Paths with dots in filenames (e.g., file.name.with.dots.md)
12
+ * - Single dot directories (e.g., ./relative/path)
13
+ *
14
+ * We block:
15
+ * - Multiple consecutive `../` (e.g., ../../.. or ../../../etc/passwd)
16
+ * - Windows-style `..\\` (always suspicious in markdown)
17
+ * - `../` after a non-traversal path segment (e.g., articles/../../../secrets)
18
+ */
19
+ function containsPathTraversal(path) {
20
+ // Block any Windows-style path traversal (..\ is suspicious in markdown context)
21
+ if (path.includes('..\\')) {
22
+ return true;
23
+ }
24
+ // Block multiple consecutive ../ at the start (../../ or more)
25
+ if (/^(\.\.[/]){2,}/.test(path)) {
26
+ return true;
27
+ }
28
+ // Block ../ appearing after a non-traversal path segment
29
+ // e.g., "articles/../../../secrets" - the ../ after "articles/" is suspicious
30
+ // This catches cases like "foo/../bar/../../../etc"
31
+ if (/[^./][/]\.\./.test(path)) {
32
+ return true;
33
+ }
34
+ return false;
35
+ }
36
+ // Patterns for detecting malformed wikilinks
37
+ const MISSING_CLOSING_PATTERN = /^!?\[\[[^\]]+\](?!\])$/; // [[foo] but not [[foo]]
38
+ const MISSING_OPENING_PATTERN = /^(?<!!)\[(?!\[)[^\]]*\]\]$/; // [foo]] but not [[foo]]
39
+ const EMPTY_WIKILINK_PATTERN = /^!?\[\[\s*\]\]$/; // [[]] or ![[]] with optional whitespace
5
40
  export function parseWikilink(text) {
41
+ // Check for empty wikilink first (before general pattern match)
42
+ if (EMPTY_WIKILINK_PATTERN.test(text)) {
43
+ return {
44
+ path: '',
45
+ error: 'Empty wikilink',
46
+ isEmbed: text.startsWith('!'),
47
+ };
48
+ }
49
+ // Check for missing closing bracket: [[foo] (one closing bracket, not two)
50
+ if (MISSING_CLOSING_PATTERN.test(text)) {
51
+ return {
52
+ path: '',
53
+ error: 'Missing closing bracket ]]',
54
+ isEmbed: text.startsWith('!'),
55
+ };
56
+ }
57
+ // Check for missing opening bracket: [foo]] (one opening bracket, not two)
58
+ if (MISSING_OPENING_PATTERN.test(text)) {
59
+ return {
60
+ path: '',
61
+ error: 'Missing opening bracket [[',
62
+ };
63
+ }
6
64
  const match = text.match(WIKILINK_PATTERN);
7
65
  if (!match)
8
66
  return null;
67
+ const path = match[1].trim();
68
+ // Check for empty path after trimming (whitespace-only content)
69
+ if (!path) {
70
+ return {
71
+ path: '',
72
+ error: 'Empty wikilink',
73
+ isEmbed: text.startsWith('!'),
74
+ };
75
+ }
76
+ // Block clearly malicious path traversal (Windows-style, mid-path escapes)
77
+ if (path.includes('..\\') || /[^./][/]\.\./.test(path)) {
78
+ return null;
79
+ }
80
+ // For multiple ../ at start: likely a mistake, suggest correction
81
+ if (/^(\.\.[/]){2,}/.test(path)) {
82
+ const corrected = path.replace(/^(\.\.[/])+/, '../');
83
+ return {
84
+ path,
85
+ display: match[2]?.trim(),
86
+ isEmbed: text.startsWith('!'),
87
+ error: `Path has too many '../' segments`,
88
+ correctedPath: corrected,
89
+ };
90
+ }
9
91
  return {
10
- path: match[1].trim(),
92
+ path,
11
93
  display: match[2]?.trim(),
12
94
  isEmbed: text.startsWith('!'),
13
95
  };
@@ -40,29 +122,6 @@ export function findFileWithExtension(path, files) {
40
122
  }
41
123
  return null;
42
124
  }
43
- /**
44
- * Calculate the Levenshtein (edit) distance between two strings.
45
- */
46
- function levenshtein(a, b) {
47
- const matrix = [];
48
- for (let i = 0; i <= b.length; i++) {
49
- matrix[i] = [i];
50
- }
51
- for (let j = 0; j <= a.length; j++) {
52
- matrix[0][j] = j;
53
- }
54
- for (let i = 1; i <= b.length; i++) {
55
- for (let j = 1; j <= a.length; j++) {
56
- if (b[i - 1] === a[j - 1]) {
57
- matrix[i][j] = matrix[i - 1][j - 1];
58
- }
59
- else {
60
- matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
61
- }
62
- }
63
- }
64
- return matrix[b.length][a.length];
65
- }
66
125
  /**
67
126
  * Extract the filename (without extension) from a path.
68
127
  */
@@ -1 +1 @@
1
- {"version":3,"file":"wikilink.js","sourceRoot":"","sources":["../../src/parser/wikilink.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAQhD,qEAAqE;AACrE,MAAM,gBAAgB,GAAG,sCAAsC,CAAC;AAEhE,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;QACrB,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE;QACzB,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;KAC9B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,UAAkB;IACtE,+DAA+D;IAC/D,OAAO,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC5E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,KAA0B;IAC5E,wBAAwB;IACxB,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2BAA2B;IAC3B,MAAM,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;IAC5B,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACtB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,CAAS,EAAE,CAAS;IACvC,MAAM,MAAM,GAAe,EAAE,CAAC;IAE9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC1B,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACtC,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CACrB,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EACxB,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EACpB,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CACrB,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,IAAY;IAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzC,OAAO,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAOD;;;GAGG;AACH,SAAS,eAAe,CAAC,OAAe,EAAE,WAAmB;IAC3D,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,gBAAgB,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC;IACnD,OAAO,YAAY,KAAK,gBAAgB;QACjC,YAAY,CAAC,QAAQ,CAAC,GAAG,GAAG,gBAAgB,CAAC;QAC7C,YAAY,CAAC,UAAU,CAAC,gBAAgB,GAAG,GAAG,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAC9B,YAAoB,EACpB,KAA0B,EAC1B,WAAoB,EACpB,cAAsB,CAAC;IAEvB,MAAM,cAAc,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/D,MAAM,SAAS,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;IAE3D,MAAM,oBAAoB,GAAuB,EAAE,CAAC;IACpD,MAAM,gBAAgB,GAAuB,EAAE,CAAC;IAChD,MAAM,YAAY,GAAuB,EAAE,CAAC;IAE5C,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QACpC,MAAM,YAAY,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;QACzD,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QAEvC,iCAAiC;QACjC,MAAM,QAAQ,GAAG,WAAW,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QAE3D,IAAI,QAAQ,IAAI,WAAW,EAAE,CAAC;YAC5B,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;YAE3C,0BAA0B;YAC1B,IAAI,WAAW,IAAI,eAAe,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC;gBACzD,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;iBAAM,IAAI,OAAO,CAAC,WAAW,EAAE,KAAK,SAAS,EAAE,CAAC;gBAC/C,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/B,CAAC;iBAAM,CAAC;gBACN,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,MAAM,cAAc,GAAG,CAAC,CAAmB,EAAE,CAAmB,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;IAC7F,oBAAoB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC1C,gBAAgB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACtC,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAElC,wEAAwE;IACxE,wDAAwD;IACxD,IAAI,WAAW,IAAI,oBAAoB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnD,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;IAED,6DAA6D;IAC7D,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACvD,CAAC;IAED,yFAAyF;IACzF,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,sEAAsE;IACtE,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAAC,UAAkB,EAAE,UAAkB;IACjE,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtC,MAAM,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1D,MAAM,WAAW,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE1C,qBAAqB;IACrB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,OACE,YAAY,GAAG,WAAW,CAAC,MAAM;QACjC,YAAY,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC;QACrC,WAAW,CAAC,YAAY,CAAC,KAAK,WAAW,CAAC,YAAY,CAAC,EACvD,CAAC;QACD,YAAY,EAAE,CAAC;IACjB,CAAC;IAED,sBAAsB;IACtB,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,GAAG,YAAY,CAAC;IAClD,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAElD,OAAO,CAAC,GAAG,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,YAAsB,EAAE,UAAkB;IACzE,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,4DAA4D;IAC5D,MAAM,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;IAEhF,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,iBAAiB,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,CAAC;IAED,OAAO,wBAAwB,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAChF,CAAC"}
1
+ {"version":3,"file":"wikilink.js","sourceRoot":"","sources":["../../src/parser/wikilink.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { ContentError } from '../index.js';
2
+ export interface SlugEntry {
3
+ slug: string;
4
+ file: string;
5
+ }
6
+ /**
7
+ * Detect duplicate slugs across module entries.
8
+ * Returns an error for each occurrence after the first.
9
+ *
10
+ * @param entries - Array of slug entries to check for duplicates
11
+ * @returns Array of ContentError objects for duplicate slugs
12
+ */
13
+ export declare function detectDuplicateSlugs(entries: SlugEntry[]): ContentError[];
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Detect duplicate slugs across module entries.
3
+ * Returns an error for each occurrence after the first.
4
+ *
5
+ * @param entries - Array of slug entries to check for duplicates
6
+ * @returns Array of ContentError objects for duplicate slugs
7
+ */
8
+ export function detectDuplicateSlugs(entries) {
9
+ const errors = [];
10
+ const seenSlugs = new Map(); // slug -> first occurrence file
11
+ for (const entry of entries) {
12
+ const existing = seenSlugs.get(entry.slug);
13
+ if (existing) {
14
+ errors.push({
15
+ file: entry.file,
16
+ message: `Duplicate slug '${entry.slug}' - also defined in ${existing}`,
17
+ suggestion: `Each module must have a unique slug. The slug '${entry.slug}' is already used in ${existing}`,
18
+ severity: 'error',
19
+ });
20
+ }
21
+ else {
22
+ seenSlugs.set(entry.slug, entry.file);
23
+ }
24
+ }
25
+ return errors;
26
+ }
27
+ //# sourceMappingURL=duplicates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicates.js","sourceRoot":"","sources":["../../src/validator/duplicates.ts"],"names":[],"mappings":"AAQA;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAoB;IACvD,MAAM,MAAM,GAAmB,EAAE,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,gCAAgC;IAE7E,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAE3C,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,OAAO,EAAE,mBAAmB,KAAK,CAAC,IAAI,uBAAuB,QAAQ,EAAE;gBACvE,UAAU,EAAE,kDAAkD,KAAK,CAAC,IAAI,wBAAwB,QAAQ,EAAE;gBAC1G,QAAQ,EAAE,OAAO;aAClB,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1,4 +1,10 @@
1
1
  import type { ContentError } from '../index.js';
2
+ /**
3
+ * Calculate the Levenshtein (edit) distance between two strings.
4
+ * This measures the minimum number of single-character edits
5
+ * (insertions, deletions, substitutions) needed to change one string into another.
6
+ */
7
+ export declare function levenshtein(a: string, b: string): number;
2
8
  /**
3
9
  * Detect likely typos in field names by comparing against known fields.
4
10
  *
@@ -8,3 +14,8 @@ import type { ContentError } from '../index.js';
8
14
  * @returns Array of warning ContentError objects for likely typos
9
15
  */
10
16
  export declare function detectFieldTypos(fields: Record<string, string>, file: string, line: number): ContentError[];
17
+ /**
18
+ * Detect likely typos in frontmatter field names by comparing against
19
+ * the valid fields for a specific file type.
20
+ */
21
+ export declare function detectFrontmatterTypos(frontmatter: Record<string, unknown>, validFields: string[], file: string): ContentError[];
@@ -1,34 +1,10 @@
1
- /**
2
- * Known valid field names across all content types.
3
- * Used to detect likely typos via Levenshtein distance.
4
- */
5
- const KNOWN_FIELDS = [
6
- // Common fields
7
- 'content',
8
- 'instructions',
9
- 'source',
10
- 'optional',
11
- 'from',
12
- 'to',
13
- 'id',
14
- 'slug',
15
- 'title',
16
- // Article metadata
17
- 'author',
18
- 'sourceUrl',
19
- // Video metadata
20
- 'channel',
21
- 'url',
22
- // Chat segment fields
23
- 'hidePreviousContentFromUser',
24
- 'hidePreviousContentFromTutor',
25
- ];
1
+ import { ALL_KNOWN_FIELDS } from '../content-schema.js';
26
2
  /**
27
3
  * Calculate the Levenshtein (edit) distance between two strings.
28
4
  * This measures the minimum number of single-character edits
29
5
  * (insertions, deletions, substitutions) needed to change one string into another.
30
6
  */
31
- function levenshtein(a, b) {
7
+ export function levenshtein(a, b) {
32
8
  const matrix = [];
33
9
  // Initialize first column (delete operations)
34
10
  for (let i = 0; i <= b.length; i++) {
@@ -66,13 +42,13 @@ export function detectFieldTypos(fields, file, line) {
66
42
  const warnings = [];
67
43
  for (const fieldName of Object.keys(fields)) {
68
44
  // Skip if it's a known valid field (case-sensitive match)
69
- if (KNOWN_FIELDS.includes(fieldName)) {
45
+ if (ALL_KNOWN_FIELDS.includes(fieldName)) {
70
46
  continue;
71
47
  }
72
48
  // Find the closest known field by Levenshtein distance
73
49
  let closest = '';
74
50
  let minDistance = Infinity;
75
- for (const known of KNOWN_FIELDS) {
51
+ for (const known of ALL_KNOWN_FIELDS) {
76
52
  const dist = levenshtein(fieldName.toLowerCase(), known.toLowerCase());
77
53
  if (dist < minDistance && dist <= 2) {
78
54
  // Only suggest if distance <= 2 (likely typo)
@@ -93,4 +69,45 @@ export function detectFieldTypos(fields, file, line) {
93
69
  }
94
70
  return warnings;
95
71
  }
72
+ /**
73
+ * Detect likely typos in frontmatter field names by comparing against
74
+ * the valid fields for a specific file type.
75
+ */
76
+ export function detectFrontmatterTypos(frontmatter, validFields, file) {
77
+ const warnings = [];
78
+ const validSet = new Set(validFields);
79
+ for (const fieldName of Object.keys(frontmatter)) {
80
+ if (validSet.has(fieldName))
81
+ continue;
82
+ // Find closest valid field by Levenshtein distance
83
+ let closest = '';
84
+ let minDistance = Infinity;
85
+ for (const valid of validFields) {
86
+ const dist = levenshtein(fieldName.toLowerCase(), valid.toLowerCase());
87
+ if (dist < minDistance && dist <= 2) {
88
+ minDistance = dist;
89
+ closest = valid;
90
+ }
91
+ }
92
+ if (closest) {
93
+ warnings.push({
94
+ file,
95
+ line: 2, // Frontmatter starts at line 2
96
+ message: `Unrecognized frontmatter field '${fieldName}'`,
97
+ suggestion: `Did you mean '${closest}'?`,
98
+ severity: 'warning',
99
+ });
100
+ }
101
+ else {
102
+ warnings.push({
103
+ file,
104
+ line: 2,
105
+ message: `Unrecognized frontmatter field '${fieldName}'`,
106
+ suggestion: `Valid fields: ${validFields.join(', ')}`,
107
+ severity: 'warning',
108
+ });
109
+ }
110
+ }
111
+ return warnings;
112
+ }
96
113
  //# sourceMappingURL=field-typos.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"field-typos.js","sourceRoot":"","sources":["../../src/validator/field-typos.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"field-typos.js","sourceRoot":"","sources":["../../src/validator/field-typos.ts"],"names":[],"mappings":"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"}