lens-content-processor 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/bundler/video.js +4 -4
  2. package/dist/bundler/video.js.map +1 -1
  3. package/dist/cli.d.ts +1 -2
  4. package/dist/cli.js +11 -9
  5. package/dist/cli.js.map +1 -1
  6. package/dist/content-schema.d.ts +34 -0
  7. package/dist/content-schema.js +62 -0
  8. package/dist/content-schema.js.map +1 -0
  9. package/dist/flattener/index.d.ts +21 -1
  10. package/dist/flattener/index.js +316 -33
  11. package/dist/flattener/index.js.map +1 -1
  12. package/dist/fs/read-vault.d.ts +1 -4
  13. package/dist/fs/read-vault.js +4 -8
  14. package/dist/fs/read-vault.js.map +1 -1
  15. package/dist/index.d.ts +25 -2
  16. package/dist/index.js +231 -6
  17. package/dist/index.js.map +1 -1
  18. package/dist/parser/article.d.ts +16 -0
  19. package/dist/parser/article.js +70 -0
  20. package/dist/parser/article.js.map +1 -0
  21. package/dist/parser/course.d.ts +2 -2
  22. package/dist/parser/course.js +18 -35
  23. package/dist/parser/course.js.map +1 -1
  24. package/dist/parser/frontmatter.js +2 -0
  25. package/dist/parser/frontmatter.js.map +1 -1
  26. package/dist/parser/learning-outcome.d.ts +4 -2
  27. package/dist/parser/learning-outcome.js +65 -23
  28. package/dist/parser/learning-outcome.js.map +1 -1
  29. package/dist/parser/lens.d.ts +38 -1
  30. package/dist/parser/lens.js +173 -18
  31. package/dist/parser/lens.js.map +1 -1
  32. package/dist/parser/module.d.ts +11 -5
  33. package/dist/parser/module.js +172 -76
  34. package/dist/parser/module.js.map +1 -1
  35. package/dist/parser/sections.js +116 -13
  36. package/dist/parser/sections.js.map +1 -1
  37. package/dist/parser/video-transcript.d.ts +11 -0
  38. package/dist/parser/video-transcript.js +39 -0
  39. package/dist/parser/video-transcript.js.map +1 -0
  40. package/dist/parser/wikilink.d.ts +2 -0
  41. package/dist/parser/wikilink.js +88 -24
  42. package/dist/parser/wikilink.js.map +1 -1
  43. package/dist/utils/slug.d.ts +5 -0
  44. package/dist/utils/slug.js +16 -0
  45. package/dist/utils/slug.js.map +1 -0
  46. package/dist/validator/chat-precedence.d.ts +9 -0
  47. package/dist/validator/chat-precedence.js +32 -0
  48. package/dist/validator/chat-precedence.js.map +1 -0
  49. package/dist/validator/duplicates.d.ts +13 -0
  50. package/dist/validator/duplicates.js +27 -0
  51. package/dist/validator/duplicates.js.map +1 -0
  52. package/dist/validator/field-typos.d.ts +11 -0
  53. package/dist/validator/field-typos.js +36 -28
  54. package/dist/validator/field-typos.js.map +1 -1
  55. package/dist/validator/field-values.d.ts +14 -0
  56. package/dist/validator/field-values.js +61 -7
  57. package/dist/validator/field-values.js.map +1 -1
  58. package/dist/validator/output-integrity.d.ts +7 -0
  59. package/dist/validator/output-integrity.js +57 -0
  60. package/dist/validator/output-integrity.js.map +1 -0
  61. package/dist/validator/segment-fields.js +1 -15
  62. package/dist/validator/segment-fields.js.map +1 -1
  63. package/dist/validator/tier.d.ts +18 -0
  64. package/dist/validator/tier.js +74 -0
  65. package/dist/validator/tier.js.map +1 -0
  66. package/dist/validator/timestamps.d.ts +2 -0
  67. package/dist/validator/timestamps.js +91 -0
  68. package/dist/validator/timestamps.js.map +1 -0
  69. package/dist/validator/url-reachability.d.ts +8 -0
  70. package/dist/validator/url-reachability.js +60 -0
  71. package/dist/validator/url-reachability.js.map +1 -0
  72. package/dist/validator/validate-frontmatter.d.ts +9 -0
  73. package/dist/validator/validate-frontmatter.js +53 -0
  74. package/dist/validator/validate-frontmatter.js.map +1 -0
  75. package/package.json +5 -5
@@ -0,0 +1,70 @@
1
+ import { parseFrontmatter } from './frontmatter.js';
2
+ import { validateFrontmatter } from '../validator/validate-frontmatter.js';
3
+ export function parseArticle(content, file) {
4
+ const errors = [];
5
+ const frontmatterResult = parseFrontmatter(content, file);
6
+ if (frontmatterResult.error) {
7
+ errors.push(frontmatterResult.error);
8
+ return { article: null, errors };
9
+ }
10
+ const { frontmatter } = frontmatterResult;
11
+ // Validate frontmatter against schema (typo detection + required fields)
12
+ const frontmatterErrors = validateFrontmatter(frontmatter, 'article', file);
13
+ errors.push(...frontmatterErrors);
14
+ const hasRequiredError = frontmatterErrors.some(e => e.severity === 'error');
15
+ if (hasRequiredError) {
16
+ return { article: null, errors };
17
+ }
18
+ // Check text fields for wikilinks (not source_url — that's a URL)
19
+ const textFields = ['title', 'author'];
20
+ for (const field of textFields) {
21
+ const raw = frontmatter[field];
22
+ const values = Array.isArray(raw) ? raw.map(String) : [String(raw)];
23
+ if (values.some(v => /\[\[.+?\]\]/.test(v))) {
24
+ errors.push({
25
+ file,
26
+ line: 2,
27
+ message: `Field '${field}' contains a wikilink — use plain text`,
28
+ suggestion: `Remove [[...]] from '${field}'`,
29
+ severity: 'error',
30
+ });
31
+ }
32
+ }
33
+ const { body, bodyStartLine } = frontmatterResult;
34
+ // Scan body for images
35
+ const imageUrls = [];
36
+ const bodyLines = body.split('\n');
37
+ for (let i = 0; i < bodyLines.length; i++) {
38
+ const line = bodyLines[i];
39
+ const absoluteLine = bodyStartLine + i;
40
+ // Detect wiki-link images: ![[...]]
41
+ const wikiImagePattern = /!\[\[([^\]]+)\]\]/g;
42
+ let wikiMatch;
43
+ while ((wikiMatch = wikiImagePattern.exec(line)) !== null) {
44
+ errors.push({
45
+ file,
46
+ line: absoluteLine,
47
+ message: `Wiki-link image not supported: ![[${wikiMatch[1]}]]`,
48
+ suggestion: 'Use standard markdown image syntax: ![alt](url)',
49
+ severity: 'error',
50
+ });
51
+ }
52
+ // Collect standard markdown images: ![alt](url) or ![alt](url "title")
53
+ const mdImagePattern = /!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g;
54
+ let mdMatch;
55
+ while ((mdMatch = mdImagePattern.exec(line)) !== null) {
56
+ imageUrls.push({ url: mdMatch[2], line: absoluteLine });
57
+ }
58
+ }
59
+ const article = {
60
+ title: String(frontmatter.title),
61
+ author: Array.isArray(frontmatter.author)
62
+ ? frontmatter.author.join(', ')
63
+ : String(frontmatter.author),
64
+ sourceUrl: String(frontmatter.source_url),
65
+ date: frontmatter.date !== undefined ? String(frontmatter.date) : undefined,
66
+ imageUrls,
67
+ };
68
+ return { article, errors };
69
+ }
70
+ //# sourceMappingURL=article.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article.js","sourceRoot":"","sources":["../../src/parser/article.ts"],"names":[],"mappings":"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"}
@@ -11,7 +11,7 @@ export interface CourseParseResult {
11
11
  * - `# Module: [[../modules/name.md|Display]]` - references a module
12
12
  * - `# Meeting: 1` - marks a meeting point
13
13
  *
14
- * The module slug is extracted from the filename in the wikilink path.
15
- * Reference validation (checking if modules exist) happens in processContent.
14
+ * The raw wikilink path is stored in the progression item; processContent
15
+ * resolves it to the frontmatter slug after all files are parsed.
16
16
  */
17
17
  export declare function parseCourse(content: string, file: string): CourseParseResult;
@@ -1,19 +1,12 @@
1
1
  import { parseFrontmatter } from './frontmatter.js';
2
2
  import { parseSections } from './sections.js';
3
3
  import { parseWikilink } from './wikilink.js';
4
- import { basename } from 'path';
4
+ import { validateSlugFormat } from '../validator/field-values.js';
5
+ import { validateFrontmatter } from '../validator/validate-frontmatter.js';
5
6
  // Valid section types for course files
6
7
  export const COURSE_SECTION_TYPES = new Set(['module', 'meeting']);
7
8
  /**
8
- * Extracts the slug from a module path (filename without extension)
9
- * e.g., '../modules/intro.md' -> 'intro'
10
- */
11
- function extractSlugFromPath(path) {
12
- const filename = basename(path);
13
- return filename.replace(/\.md$/, '');
14
- }
15
- /**
16
- * Parses a Module section title to extract the wikilink and module slug
9
+ * Parses a Module section title to extract the wikilink path
17
10
  * Title format: [[../modules/intro.md|Display Text]]
18
11
  */
19
12
  function parseModuleSection(section, file) {
@@ -30,9 +23,8 @@ function parseModuleSection(section, file) {
30
23
  },
31
24
  };
32
25
  }
33
- const slug = extractSlugFromPath(wikilink.path);
34
- const optional = section.fields.optional === 'true';
35
- return { slug, optional };
26
+ const optional = section.fields.optional?.toLowerCase() === 'true';
27
+ return { path: wikilink.path, optional };
36
28
  }
37
29
  /**
38
30
  * Parses a Meeting section title to extract the meeting number
@@ -60,8 +52,8 @@ function parseMeetingSection(section, file) {
60
52
  * - `# Module: [[../modules/name.md|Display]]` - references a module
61
53
  * - `# Meeting: 1` - marks a meeting point
62
54
  *
63
- * The module slug is extracted from the filename in the wikilink path.
64
- * Reference validation (checking if modules exist) happens in processContent.
55
+ * The raw wikilink path is stored in the progression item; processContent
56
+ * resolves it to the frontmatter slug after all files are parsed.
65
57
  */
66
58
  export function parseCourse(content, file) {
67
59
  const errors = [];
@@ -72,26 +64,17 @@ export function parseCourse(content, file) {
72
64
  return { course: null, errors };
73
65
  }
74
66
  const { frontmatter, body, bodyStartLine } = frontmatterResult;
75
- // Validate required frontmatter fields
76
- if (!frontmatter.slug) {
77
- errors.push({
78
- file,
79
- line: 2,
80
- message: 'Missing required field: slug',
81
- suggestion: "Add 'slug: your-course-slug' to frontmatter",
82
- severity: 'error',
83
- });
84
- }
85
- if (!frontmatter.title) {
86
- errors.push({
87
- file,
88
- line: 2,
89
- message: 'Missing required field: title',
90
- suggestion: "Add 'title: Your Course Title' to frontmatter",
91
- severity: 'error',
92
- });
67
+ const frontmatterErrors = validateFrontmatter(frontmatter, 'course', file);
68
+ errors.push(...frontmatterErrors);
69
+ // Course-specific: validate slug format
70
+ const slug = frontmatter.slug;
71
+ if (typeof slug === 'string' && slug.trim() !== '') {
72
+ const slugFormatError = validateSlugFormat(slug, file, 2);
73
+ if (slugFormatError) {
74
+ errors.push(slugFormatError);
75
+ }
93
76
  }
94
- if (errors.length > 0) {
77
+ if (errors.some(e => e.severity === 'error')) {
95
78
  return { course: null, errors };
96
79
  }
97
80
  // Parse sections (H1 headers for course files)
@@ -117,7 +100,7 @@ export function parseCourse(content, file) {
117
100
  else {
118
101
  progression.push({
119
102
  type: 'module',
120
- slug: result.slug,
103
+ path: result.path,
121
104
  optional: result.optional,
122
105
  });
123
106
  }
@@ -1 +1 @@
1
- {"version":3,"file":"course.js","sourceRoot":"","sources":["../../src/parser/course.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"course.js","sourceRoot":"","sources":["../../src/parser/course.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,8 @@ import { parse as parseYaml } from 'yaml';
3
3
  // Matches: ---\n<yaml>\n---\n<body>
4
4
  const FRONTMATTER_PATTERN = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/;
5
5
  export function parseFrontmatter(content, file = '') {
6
+ // Normalize Windows line endings
7
+ content = content.replace(/\r\n/g, '\n');
6
8
  const match = content.match(FRONTMATTER_PATTERN);
7
9
  if (!match) {
8
10
  // Check if it starts with --- but doesn't close
@@ -1 +1 @@
1
- {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/parser/frontmatter.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAU1C,oCAAoC;AACpC,MAAM,mBAAmB,GAAG,oCAAoC,CAAC;AAEjE,MAAM,UAAU,gBAAgB,CAAC,OAAe,EAAE,OAAe,EAAE;IACjE,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAEjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,gDAAgD;QAChD,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChE,OAAO;gBACL,WAAW,EAAE,EAAE;gBACf,IAAI,EAAE,OAAO;gBACb,aAAa,EAAE,CAAC;gBAChB,KAAK,EAAE;oBACL,IAAI;oBACJ,IAAI,EAAE,CAAC;oBACP,OAAO,EAAE,4CAA4C;oBACrD,UAAU,EAAE,kDAAkD;oBAC9D,QAAQ,EAAE,OAAO;iBAClB;aACF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,WAAW,EAAE,EAAE;YACf,IAAI,EAAE,OAAO;YACb,aAAa,EAAE,CAAC;YAChB,KAAK,EAAE;gBACL,IAAI;gBACJ,IAAI,EAAE,CAAC;gBACP,OAAO,EAAE,qBAAqB;gBAC9B,UAAU,EAAE,qDAAqD;gBACjE,QAAQ,EAAE,OAAO;aAClB;SACF,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,aAAa,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,mDAAmD;IAE7G,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QACjD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAC9C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,WAAW,EAAE,EAAE;YACf,IAAI;YACJ,aAAa;YACb,KAAK,EAAE;gBACL,IAAI;gBACJ,IAAI,EAAE,CAAC;gBACP,OAAO,EAAE,iBAAiB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;gBACtE,UAAU,EAAE,kDAAkD;gBAC9D,QAAQ,EAAE,OAAO;aAClB;SACF,CAAC;IACJ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../../src/parser/frontmatter.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAU1C,oCAAoC;AACpC,MAAM,mBAAmB,GAAG,oCAAoC,CAAC;AAEjE,MAAM,UAAU,gBAAgB,CAAC,OAAe,EAAE,OAAe,EAAE;IACjE,iCAAiC;IACjC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAEzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAEjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,gDAAgD;QAChD,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChE,OAAO;gBACL,WAAW,EAAE,EAAE;gBACf,IAAI,EAAE,OAAO;gBACb,aAAa,EAAE,CAAC;gBAChB,KAAK,EAAE;oBACL,IAAI;oBACJ,IAAI,EAAE,CAAC;oBACP,OAAO,EAAE,4CAA4C;oBACrD,UAAU,EAAE,kDAAkD;oBAC9D,QAAQ,EAAE,OAAO;iBAClB;aACF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,WAAW,EAAE,EAAE;YACf,IAAI,EAAE,OAAO;YACb,aAAa,EAAE,CAAC;YAChB,KAAK,EAAE;gBACL,IAAI;gBACJ,IAAI,EAAE,CAAC;gBACP,OAAO,EAAE,qBAAqB;gBAC9B,UAAU,EAAE,qDAAqD;gBACjE,QAAQ,EAAE,OAAO;aAClB;SACF,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,aAAa,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,mDAAmD;IAE7G,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QACjD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAC9C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,WAAW,EAAE,EAAE;YACf,IAAI;YACJ,aAAa;YACb,KAAK,EAAE;gBACL,IAAI;gBACJ,IAAI,EAAE,CAAC;gBACP,OAAO,EAAE,iBAAiB,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;gBACtE,UAAU,EAAE,kDAAkD;gBAC9D,QAAQ,EAAE,OAAO;aAClB;SACF,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -1,12 +1,14 @@
1
1
  import type { ContentError } from '../index.js';
2
+ import { type ParsedLensSegment } from './lens.js';
2
3
  export interface ParsedLensRef {
3
4
  source: string;
4
5
  resolvedPath: string;
5
6
  optional: boolean;
6
7
  }
7
8
  export interface ParsedTestRef {
8
- source: string;
9
- resolvedPath: string;
9
+ source?: string;
10
+ resolvedPath?: string;
11
+ segments: ParsedLensSegment[];
10
12
  }
11
13
  export interface ParsedLearningOutcome {
12
14
  id: string;
@@ -2,8 +2,12 @@ import { parseFrontmatter } from './frontmatter.js';
2
2
  import { parseSections, LO_SECTION_TYPES } from './sections.js';
3
3
  import { parseWikilink, resolveWikilinkPath, hasRelativePath } from './wikilink.js';
4
4
  import { detectFieldTypos } from '../validator/field-typos.js';
5
+ import { validateFrontmatter } from '../validator/validate-frontmatter.js';
6
+ import { parseSegments, convertSegment, stripObsidianComments } from './lens.js';
5
7
  export function parseLearningOutcome(content, file) {
6
8
  const errors = [];
9
+ // Strip Obsidian comments before parsing
10
+ content = stripObsidianComments(content);
7
11
  // Step 1: Parse frontmatter and validate id field
8
12
  const frontmatterResult = parseFrontmatter(content, file);
9
13
  if (frontmatterResult.error) {
@@ -11,13 +15,18 @@ export function parseLearningOutcome(content, file) {
11
15
  return { learningOutcome: null, errors };
12
16
  }
13
17
  const { frontmatter, body, bodyStartLine } = frontmatterResult;
14
- // Validate required id field
15
- if (!frontmatter.id) {
18
+ const frontmatterErrors = validateFrontmatter(frontmatter, 'learning-outcome', file);
19
+ errors.push(...frontmatterErrors);
20
+ if (frontmatterErrors.some(e => e.severity === 'error')) {
21
+ return { learningOutcome: null, errors };
22
+ }
23
+ // LO-specific: id must be a string
24
+ if (typeof frontmatter.id !== 'string') {
16
25
  errors.push({
17
26
  file,
18
27
  line: 2,
19
- message: 'Missing required field: id',
20
- suggestion: "Add 'id: <uuid>' to frontmatter",
28
+ message: `Field 'id' must be a string, got ${typeof frontmatter.id}`,
29
+ suggestion: "Use quotes: id: '12345'",
21
30
  severity: 'error',
22
31
  });
23
32
  return { learningOutcome: null, errors };
@@ -55,12 +64,17 @@ export function parseLearningOutcome(content, file) {
55
64
  }
56
65
  // Parse wikilink and resolve path
57
66
  const wikilink = parseWikilink(source);
58
- if (!wikilink) {
67
+ if (!wikilink || wikilink.error) {
68
+ const suggestion = wikilink?.correctedPath
69
+ ? `Did you mean '[[${wikilink.correctedPath}]]'?`
70
+ : 'Use format [[../Lenses/filename.md|Display Text]]';
59
71
  errors.push({
60
72
  file,
61
73
  line: section.line,
62
- message: `Invalid wikilink format in source:: field: ${source}`,
63
- suggestion: 'Use format [[../Lenses/filename.md|Display Text]]',
74
+ message: wikilink?.error
75
+ ? `${wikilink.error} in source:: field: ${source}`
76
+ : `Invalid wikilink format in source:: field: ${source}`,
77
+ suggestion,
64
78
  severity: 'error',
65
79
  });
66
80
  continue;
@@ -77,7 +91,7 @@ export function parseLearningOutcome(content, file) {
77
91
  continue;
78
92
  }
79
93
  const resolvedPath = resolveWikilinkPath(wikilink.path, file);
80
- const optional = section.fields.optional === 'true';
94
+ const optional = section.fields.optional?.toLowerCase() === 'true';
81
95
  lenses.push({
82
96
  source,
83
97
  resolvedPath,
@@ -86,26 +100,54 @@ export function parseLearningOutcome(content, file) {
86
100
  }
87
101
  else if (section.type === 'test') {
88
102
  const source = section.fields.source;
89
- // source:: is optional for test sections (tests not fully implemented yet)
90
- if (!source) {
91
- continue;
103
+ let resolvedPath;
104
+ if (source) {
105
+ // Parse wikilink and resolve path
106
+ const wikilink = parseWikilink(source);
107
+ if (!wikilink || wikilink.error) {
108
+ const suggestion = wikilink?.correctedPath
109
+ ? `Did you mean '[[${wikilink.correctedPath}]]'?`
110
+ : 'Use format [[../Tests/filename.md|Display Text]]';
111
+ errors.push({
112
+ file,
113
+ line: section.line,
114
+ message: wikilink?.error
115
+ ? `${wikilink.error} in source:: field: ${source}`
116
+ : `Invalid wikilink format in source:: field: ${source}`,
117
+ suggestion,
118
+ severity: 'error',
119
+ });
120
+ continue;
121
+ }
122
+ resolvedPath = resolveWikilinkPath(wikilink.path, file);
92
123
  }
93
- // Parse wikilink and resolve path
94
- const wikilink = parseWikilink(source);
95
- if (!wikilink) {
96
- errors.push({
97
- file,
98
- line: section.line,
99
- message: `Invalid wikilink format in source:: field: ${source}`,
100
- suggestion: 'Use format [[../Tests/filename.md|Display Text]]',
101
- severity: 'error',
102
- });
103
- continue;
124
+ // Parse inline H4 segments (#### Question, #### Chat, #### Text) from body
125
+ const { segments: rawSegments, errors: segmentErrors } = parseSegments(section.body, section.line + 1, file);
126
+ // Adjust segment error line numbers for frontmatter offset
127
+ for (const err of segmentErrors) {
128
+ if (err.line) {
129
+ err.line += bodyStartLine - 1;
130
+ }
131
+ }
132
+ errors.push(...segmentErrors);
133
+ const testSegments = [];
134
+ for (const rawSeg of rawSegments) {
135
+ const { segment, errors: conversionErrors } = convertSegment(rawSeg, 'page', file);
136
+ // Adjust conversion error line numbers for frontmatter offset
137
+ for (const err of conversionErrors) {
138
+ if (err.line) {
139
+ err.line += bodyStartLine - 1;
140
+ }
141
+ }
142
+ errors.push(...conversionErrors);
143
+ if (segment) {
144
+ testSegments.push(segment);
145
+ }
104
146
  }
105
- const resolvedPath = resolveWikilinkPath(wikilink.path, file);
106
147
  testRef = {
107
148
  source,
108
149
  resolvedPath,
150
+ segments: testSegments,
109
151
  };
110
152
  }
111
153
  }
@@ -1 +1 @@
1
- {"version":3,"file":"learning-outcome.js","sourceRoot":"","sources":["../../src/parser/learning-outcome.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"learning-outcome.js","sourceRoot":"","sources":["../../src/parser/learning-outcome.ts"],"names":[],"mappings":"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"}
@@ -24,7 +24,17 @@ export interface ParsedVideoExcerptSegment {
24
24
  toTimeStr: string;
25
25
  optional?: boolean;
26
26
  }
27
- export type ParsedLensSegment = ParsedTextSegment | ParsedChatSegment | ParsedArticleExcerptSegment | ParsedVideoExcerptSegment;
27
+ export interface ParsedQuestionSegment {
28
+ type: 'question';
29
+ content: string;
30
+ assessmentInstructions?: string;
31
+ maxTime?: string;
32
+ maxChars?: number;
33
+ enforceVoice?: boolean;
34
+ optional?: boolean;
35
+ feedback?: boolean;
36
+ }
37
+ export type ParsedLensSegment = ParsedTextSegment | ParsedChatSegment | ParsedArticleExcerptSegment | ParsedVideoExcerptSegment | ParsedQuestionSegment;
28
38
  export interface ParsedLensSection {
29
39
  type: string;
30
40
  title: string;
@@ -41,6 +51,32 @@ export interface LensParseResult {
41
51
  lens: ParsedLens | null;
42
52
  errors: ContentError[];
43
53
  }
54
+ interface RawSegment {
55
+ type: string;
56
+ title?: string;
57
+ fields: Record<string, string>;
58
+ line: number;
59
+ }
60
+ /**
61
+ * Parse H4 segments from a section body.
62
+ * Segments are defined by `#### <type>` headers within a section.
63
+ */
64
+ export declare function parseSegments(sectionBody: string, bodyStartLine: number, file: string): {
65
+ segments: RawSegment[];
66
+ errors: ContentError[];
67
+ };
68
+ /**
69
+ * Convert a raw segment to a typed ParsedLensSegment.
70
+ */
71
+ export declare function convertSegment(raw: RawSegment, sectionType: string, file: string): {
72
+ segment: ParsedLensSegment | null;
73
+ errors: ContentError[];
74
+ };
75
+ /**
76
+ * Strip Obsidian %% comments %% from content.
77
+ * Handles both inline (%% ... %% on same line) and block (multiline) comments.
78
+ */
79
+ export declare function stripObsidianComments(content: string): string;
44
80
  /**
45
81
  * Parse a lens file into structured lens data.
46
82
  *
@@ -49,3 +85,4 @@ export interface LensParseResult {
49
85
  * - H4 (`####`) for segments: Text, Chat, Article-excerpt, Video-excerpt
50
86
  */
51
87
  export declare function parseLens(content: string, file: string): LensParseResult;
88
+ export {};