lens-content-processor 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundler/article.d.ts +41 -0
- package/dist/bundler/article.js +240 -0
- package/dist/bundler/article.js.map +1 -0
- package/dist/bundler/article.test.d.ts +1 -0
- package/dist/bundler/article.test.js +212 -0
- package/dist/bundler/article.test.js.map +1 -0
- package/dist/bundler/video.d.ts +50 -0
- package/dist/bundler/video.js +213 -0
- package/dist/bundler/video.js.map +1 -0
- package/dist/bundler/video.test.d.ts +1 -0
- package/dist/bundler/video.test.js +95 -0
- package/dist/bundler/video.test.js.map +1 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +80 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +45 -0
- package/dist/cli.test.js.map +1 -0
- package/dist/flattener/index.d.ts +20 -0
- package/dist/flattener/index.js +707 -0
- package/dist/flattener/index.js.map +1 -0
- package/dist/flattener/index.test.d.ts +1 -0
- package/dist/flattener/index.test.js +547 -0
- package/dist/flattener/index.test.js.map +1 -0
- package/dist/fs/read-vault.d.ts +4 -0
- package/dist/fs/read-vault.js +28 -0
- package/dist/fs/read-vault.js.map +1 -0
- package/dist/fs/read-vault.test.d.ts +1 -0
- package/dist/fs/read-vault.test.js +37 -0
- package/dist/fs/read-vault.test.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +137 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/course.d.ts +17 -0
- package/dist/parser/course.js +145 -0
- package/dist/parser/course.js.map +1 -0
- package/dist/parser/course.test.d.ts +1 -0
- package/dist/parser/course.test.js +117 -0
- package/dist/parser/course.test.js.map +1 -0
- package/dist/parser/frontmatter.d.ts +8 -0
- package/dist/parser/frontmatter.js +58 -0
- package/dist/parser/frontmatter.js.map +1 -0
- package/dist/parser/frontmatter.test.d.ts +1 -0
- package/dist/parser/frontmatter.test.js +54 -0
- package/dist/parser/frontmatter.test.js.map +1 -0
- package/dist/parser/learning-outcome.d.ts +21 -0
- package/dist/parser/learning-outcome.js +116 -0
- package/dist/parser/learning-outcome.js.map +1 -0
- package/dist/parser/learning-outcome.test.d.ts +1 -0
- package/dist/parser/learning-outcome.test.js +75 -0
- package/dist/parser/learning-outcome.test.js.map +1 -0
- package/dist/parser/lens.d.ts +51 -0
- package/dist/parser/lens.js +328 -0
- package/dist/parser/lens.js.map +1 -0
- package/dist/parser/lens.test.d.ts +1 -0
- package/dist/parser/lens.test.js +453 -0
- package/dist/parser/lens.test.js.map +1 -0
- package/dist/parser/module.d.ts +21 -0
- package/dist/parser/module.js +128 -0
- package/dist/parser/module.js.map +1 -0
- package/dist/parser/module.test.d.ts +1 -0
- package/dist/parser/module.test.js +99 -0
- package/dist/parser/module.test.js.map +1 -0
- package/dist/parser/sections.d.ts +18 -0
- package/dist/parser/sections.js +128 -0
- package/dist/parser/sections.js.map +1 -0
- package/dist/parser/sections.test.d.ts +1 -0
- package/dist/parser/sections.test.js +297 -0
- package/dist/parser/sections.test.js.map +1 -0
- package/dist/parser/wikilink.d.ts +12 -0
- package/dist/parser/wikilink.js +35 -0
- package/dist/parser/wikilink.js.map +1 -0
- package/dist/parser/wikilink.test.d.ts +1 -0
- package/dist/parser/wikilink.test.js +76 -0
- package/dist/parser/wikilink.test.js.map +1 -0
- package/dist/validator/field-typos.d.ts +10 -0
- package/dist/validator/field-typos.js +96 -0
- package/dist/validator/field-typos.js.map +1 -0
- package/dist/validator/field-typos.test.d.ts +1 -0
- package/dist/validator/field-typos.test.js +41 -0
- package/dist/validator/field-typos.test.js.map +1 -0
- package/dist/validator/field-values.d.ts +11 -0
- package/dist/validator/field-values.js +37 -0
- package/dist/validator/field-values.js.map +1 -0
- package/dist/validator/field-values.test.d.ts +1 -0
- package/dist/validator/field-values.test.js +45 -0
- package/dist/validator/field-values.test.js.map +1 -0
- package/dist/validator/segment-fields.d.ts +12 -0
- package/dist/validator/segment-fields.js +55 -0
- package/dist/validator/segment-fields.js.map +1 -0
- package/dist/validator/segment-fields.test.d.ts +1 -0
- package/dist/validator/segment-fields.test.js +73 -0
- package/dist/validator/segment-fields.test.js.map +1 -0
- package/dist/validator/standalone.test.d.ts +1 -0
- package/dist/validator/standalone.test.js +190 -0
- package/dist/validator/standalone.test.js.map +1 -0
- package/dist/validator/uuid.d.ts +17 -0
- package/dist/validator/uuid.js +46 -0
- package/dist/validator/uuid.js.map +1 -0
- package/dist/validator/uuid.test.d.ts +1 -0
- package/dist/validator/uuid.test.js +229 -0
- package/dist/validator/uuid.test.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// src/parser/wikilink.test.ts
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { parseWikilink, resolveWikilinkPath, findFileWithExtension } from './wikilink';
|
|
4
|
+
describe('parseWikilink', () => {
|
|
5
|
+
it('extracts path and display text', () => {
|
|
6
|
+
const result = parseWikilink('[[../Learning Outcomes/lo1.md|My LO]]');
|
|
7
|
+
expect(result?.path).toBe('../Learning Outcomes/lo1.md');
|
|
8
|
+
expect(result?.display).toBe('My LO');
|
|
9
|
+
});
|
|
10
|
+
it('handles wikilink without display text', () => {
|
|
11
|
+
const result = parseWikilink('[[path/to/file.md]]');
|
|
12
|
+
expect(result?.path).toBe('path/to/file.md');
|
|
13
|
+
expect(result?.display).toBeUndefined();
|
|
14
|
+
});
|
|
15
|
+
it('returns null for non-wikilink', () => {
|
|
16
|
+
expect(parseWikilink('not a wikilink')).toBeNull();
|
|
17
|
+
expect(parseWikilink('[regular](link)')).toBeNull();
|
|
18
|
+
});
|
|
19
|
+
it('handles embed syntax ![[path]]', () => {
|
|
20
|
+
const result = parseWikilink('![[images/diagram.png]]');
|
|
21
|
+
expect(result?.path).toBe('images/diagram.png');
|
|
22
|
+
expect(result?.isEmbed).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
it('handles embed with display text ![[path|alt text]]', () => {
|
|
25
|
+
const result = parseWikilink('![[images/diagram.png|Architecture diagram]]');
|
|
26
|
+
expect(result?.path).toBe('images/diagram.png');
|
|
27
|
+
expect(result?.display).toBe('Architecture diagram');
|
|
28
|
+
expect(result?.isEmbed).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('resolveWikilinkPath', () => {
|
|
32
|
+
it('resolves relative path from source file', () => {
|
|
33
|
+
const resolved = resolveWikilinkPath('../Learning Outcomes/lo1.md', 'modules/intro.md');
|
|
34
|
+
expect(resolved).toBe('Learning Outcomes/lo1.md');
|
|
35
|
+
});
|
|
36
|
+
it('handles nested paths', () => {
|
|
37
|
+
const resolved = resolveWikilinkPath('../Lenses/category/lens1.md', 'Learning Outcomes/lo1.md');
|
|
38
|
+
expect(resolved).toBe('Lenses/category/lens1.md');
|
|
39
|
+
});
|
|
40
|
+
it('resolves path without .md extension', () => {
|
|
41
|
+
const resolved = resolveWikilinkPath('../modules/introduction', 'courses/default.md');
|
|
42
|
+
expect(resolved).toBe('modules/introduction');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('findFileWithExtension', () => {
|
|
46
|
+
it('finds file with exact path', () => {
|
|
47
|
+
const files = new Map([
|
|
48
|
+
['modules/intro.md', 'content'],
|
|
49
|
+
]);
|
|
50
|
+
const result = findFileWithExtension('modules/intro.md', files);
|
|
51
|
+
expect(result).toBe('modules/intro.md');
|
|
52
|
+
});
|
|
53
|
+
it('finds file by adding .md extension', () => {
|
|
54
|
+
const files = new Map([
|
|
55
|
+
['modules/intro.md', 'content'],
|
|
56
|
+
]);
|
|
57
|
+
const result = findFileWithExtension('modules/intro', files);
|
|
58
|
+
expect(result).toBe('modules/intro.md');
|
|
59
|
+
});
|
|
60
|
+
it('returns null when file not found', () => {
|
|
61
|
+
const files = new Map([
|
|
62
|
+
['modules/other.md', 'content'],
|
|
63
|
+
]);
|
|
64
|
+
const result = findFileWithExtension('modules/intro', files);
|
|
65
|
+
expect(result).toBeNull();
|
|
66
|
+
});
|
|
67
|
+
it('prefers exact match over .md extension', () => {
|
|
68
|
+
const files = new Map([
|
|
69
|
+
['modules/intro', 'exact content'],
|
|
70
|
+
['modules/intro.md', 'md content'],
|
|
71
|
+
]);
|
|
72
|
+
const result = findFileWithExtension('modules/intro', files);
|
|
73
|
+
expect(result).toBe('modules/intro');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=wikilink.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wikilink.test.js","sourceRoot":"","sources":["../../src/parser/wikilink.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ContentError } from '../index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Detect likely typos in field names by comparing against known fields.
|
|
4
|
+
*
|
|
5
|
+
* @param fields - Object mapping field names to their values
|
|
6
|
+
* @param file - File path for error reporting
|
|
7
|
+
* @param line - Line number for error reporting
|
|
8
|
+
* @returns Array of warning ContentError objects for likely typos
|
|
9
|
+
*/
|
|
10
|
+
export declare function detectFieldTypos(fields: Record<string, string>, file: string, line: number): ContentError[];
|
|
@@ -0,0 +1,96 @@
|
|
|
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
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* Calculate the Levenshtein (edit) distance between two strings.
|
|
28
|
+
* This measures the minimum number of single-character edits
|
|
29
|
+
* (insertions, deletions, substitutions) needed to change one string into another.
|
|
30
|
+
*/
|
|
31
|
+
function levenshtein(a, b) {
|
|
32
|
+
const matrix = [];
|
|
33
|
+
// Initialize first column (delete operations)
|
|
34
|
+
for (let i = 0; i <= b.length; i++) {
|
|
35
|
+
matrix[i] = [i];
|
|
36
|
+
}
|
|
37
|
+
// Initialize first row (insert operations)
|
|
38
|
+
for (let j = 0; j <= a.length; j++) {
|
|
39
|
+
matrix[0][j] = j;
|
|
40
|
+
}
|
|
41
|
+
// Fill in the rest of the matrix
|
|
42
|
+
for (let i = 1; i <= b.length; i++) {
|
|
43
|
+
for (let j = 1; j <= a.length; j++) {
|
|
44
|
+
if (b[i - 1] === a[j - 1]) {
|
|
45
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
|
|
49
|
+
matrix[i][j - 1] + 1, // insertion
|
|
50
|
+
matrix[i - 1][j] + 1 // deletion
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return matrix[b.length][a.length];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Detect likely typos in field names by comparing against known fields.
|
|
59
|
+
*
|
|
60
|
+
* @param fields - Object mapping field names to their values
|
|
61
|
+
* @param file - File path for error reporting
|
|
62
|
+
* @param line - Line number for error reporting
|
|
63
|
+
* @returns Array of warning ContentError objects for likely typos
|
|
64
|
+
*/
|
|
65
|
+
export function detectFieldTypos(fields, file, line) {
|
|
66
|
+
const warnings = [];
|
|
67
|
+
for (const fieldName of Object.keys(fields)) {
|
|
68
|
+
// Skip if it's a known valid field (case-sensitive match)
|
|
69
|
+
if (KNOWN_FIELDS.includes(fieldName)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
// Find the closest known field by Levenshtein distance
|
|
73
|
+
let closest = '';
|
|
74
|
+
let minDistance = Infinity;
|
|
75
|
+
for (const known of KNOWN_FIELDS) {
|
|
76
|
+
const dist = levenshtein(fieldName.toLowerCase(), known.toLowerCase());
|
|
77
|
+
if (dist < minDistance && dist <= 2) {
|
|
78
|
+
// Only suggest if distance <= 2 (likely typo)
|
|
79
|
+
minDistance = dist;
|
|
80
|
+
closest = known;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Only warn if we found a close match (likely typo)
|
|
84
|
+
if (closest) {
|
|
85
|
+
warnings.push({
|
|
86
|
+
file,
|
|
87
|
+
line,
|
|
88
|
+
message: `Unrecognized field '${fieldName}'`,
|
|
89
|
+
suggestion: `Did you mean '${closest}'?`,
|
|
90
|
+
severity: 'warning',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return warnings;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=field-typos.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-typos.js","sourceRoot":"","sources":["../../src/validator/field-typos.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// src/validator/field-typos.test.ts
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { detectFieldTypos } from './field-typos.js';
|
|
4
|
+
describe('field typo detection', () => {
|
|
5
|
+
it('suggests correction for misspelled field "contnet" -> "content"', () => {
|
|
6
|
+
const warnings = detectFieldTypos({ contnet: 'value' }, 'test.md', 10);
|
|
7
|
+
expect(warnings).toHaveLength(1);
|
|
8
|
+
expect(warnings[0].message).toContain("'contnet'");
|
|
9
|
+
expect(warnings[0].suggestion).toContain("'content'");
|
|
10
|
+
expect(warnings[0].severity).toBe('warning');
|
|
11
|
+
});
|
|
12
|
+
it('suggests correction for "intructions" -> "instructions"', () => {
|
|
13
|
+
const warnings = detectFieldTypos({ intructions: 'value' }, 'test.md', 10);
|
|
14
|
+
expect(warnings).toHaveLength(1);
|
|
15
|
+
expect(warnings[0].suggestion).toContain("'instructions'");
|
|
16
|
+
});
|
|
17
|
+
it('suggests correction for "souce" -> "source"', () => {
|
|
18
|
+
const warnings = detectFieldTypos({ souce: 'value' }, 'test.md', 10);
|
|
19
|
+
expect(warnings).toHaveLength(1);
|
|
20
|
+
expect(warnings[0].suggestion).toContain("'source'");
|
|
21
|
+
});
|
|
22
|
+
it('ignores valid field names', () => {
|
|
23
|
+
const warnings = detectFieldTypos({ content: 'value', optional: 'true', instructions: 'do something' }, 'test.md', 10);
|
|
24
|
+
expect(warnings).toHaveLength(0);
|
|
25
|
+
});
|
|
26
|
+
it('does not suggest for completely unrelated field names', () => {
|
|
27
|
+
// "xyz123" is not close to any known field
|
|
28
|
+
const warnings = detectFieldTypos({ xyz123: 'value' }, 'test.md', 10);
|
|
29
|
+
expect(warnings).toHaveLength(0);
|
|
30
|
+
});
|
|
31
|
+
it('includes file and line in warnings', () => {
|
|
32
|
+
const warnings = detectFieldTypos({ contnet: 'value' }, 'Lenses/myfile.md', 42);
|
|
33
|
+
expect(warnings[0].file).toBe('Lenses/myfile.md');
|
|
34
|
+
expect(warnings[0].line).toBe(42);
|
|
35
|
+
});
|
|
36
|
+
it('handles multiple typos in same fields object', () => {
|
|
37
|
+
const warnings = detectFieldTypos({ contnet: 'value', intructions: 'do something' }, 'test.md', 10);
|
|
38
|
+
expect(warnings).toHaveLength(2);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
//# sourceMappingURL=field-typos.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-typos.test.js","sourceRoot":"","sources":["../../src/validator/field-typos.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ContentError } from '../index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validate field values, checking for appropriate types.
|
|
4
|
+
* Currently validates that boolean fields contain 'true' or 'false'.
|
|
5
|
+
*
|
|
6
|
+
* @param fields - Object mapping field names to their values
|
|
7
|
+
* @param file - File path for error reporting
|
|
8
|
+
* @param line - Line number for error reporting
|
|
9
|
+
* @returns Array of warning ContentError objects for invalid values
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateFieldValues(fields: Record<string, string>, file: string, line: number): ContentError[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fields that should only contain boolean values ('true' or 'false').
|
|
3
|
+
*/
|
|
4
|
+
const BOOLEAN_FIELDS = new Set([
|
|
5
|
+
'optional',
|
|
6
|
+
'hidePreviousContentFromUser',
|
|
7
|
+
'hidePreviousContentFromTutor',
|
|
8
|
+
]);
|
|
9
|
+
/**
|
|
10
|
+
* Validate field values, checking for appropriate types.
|
|
11
|
+
* Currently validates that boolean fields contain 'true' or 'false'.
|
|
12
|
+
*
|
|
13
|
+
* @param fields - Object mapping field names to their values
|
|
14
|
+
* @param file - File path for error reporting
|
|
15
|
+
* @param line - Line number for error reporting
|
|
16
|
+
* @returns Array of warning ContentError objects for invalid values
|
|
17
|
+
*/
|
|
18
|
+
export function validateFieldValues(fields, file, line) {
|
|
19
|
+
const warnings = [];
|
|
20
|
+
for (const [name, value] of Object.entries(fields)) {
|
|
21
|
+
// Check if this is a boolean field
|
|
22
|
+
if (BOOLEAN_FIELDS.has(name)) {
|
|
23
|
+
const normalizedValue = value.toLowerCase();
|
|
24
|
+
if (normalizedValue !== 'true' && normalizedValue !== 'false') {
|
|
25
|
+
warnings.push({
|
|
26
|
+
file,
|
|
27
|
+
line,
|
|
28
|
+
message: `Field '${name}' has non-boolean value '${value}'`,
|
|
29
|
+
suggestion: "Expected 'true' or 'false'",
|
|
30
|
+
severity: 'warning',
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return warnings;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=field-values.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-values.js","sourceRoot":"","sources":["../../src/validator/field-values.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,UAAU;IACV,6BAA6B;IAC7B,8BAA8B;CAC/B,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAA8B,EAC9B,IAAY,EACZ,IAAY;IAEZ,MAAM,QAAQ,GAAmB,EAAE,CAAC;IAEpC,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,mCAAmC;QACnC,IAAI,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,MAAM,eAAe,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;YAC5C,IAAI,eAAe,KAAK,MAAM,IAAI,eAAe,KAAK,OAAO,EAAE,CAAC;gBAC9D,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI;oBACJ,IAAI;oBACJ,OAAO,EAAE,UAAU,IAAI,4BAA4B,KAAK,GAAG;oBAC3D,UAAU,EAAE,4BAA4B;oBACxC,QAAQ,EAAE,SAAS;iBACpB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/validator/field-values.test.ts
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { validateFieldValues } from './field-values.js';
|
|
4
|
+
describe('validateFieldValues', () => {
|
|
5
|
+
describe('boolean field validation', () => {
|
|
6
|
+
it('warns about optional:: field with non-boolean value', () => {
|
|
7
|
+
const warnings = validateFieldValues({ optional: 'yes' }, 'test.md', 10);
|
|
8
|
+
expect(warnings).toHaveLength(1);
|
|
9
|
+
expect(warnings[0].message).toContain("'optional'");
|
|
10
|
+
expect(warnings[0].suggestion).toContain("'true' or 'false'");
|
|
11
|
+
expect(warnings[0].severity).toBe('warning');
|
|
12
|
+
});
|
|
13
|
+
it('warns about hidePreviousContentFromUser:: with non-boolean value', () => {
|
|
14
|
+
const warnings = validateFieldValues({ hidePreviousContentFromUser: '1' }, 'test.md', 10);
|
|
15
|
+
expect(warnings).toHaveLength(1);
|
|
16
|
+
expect(warnings[0].message).toContain("'hidePreviousContentFromUser'");
|
|
17
|
+
});
|
|
18
|
+
it('warns about hidePreviousContentFromTutor:: with non-boolean value', () => {
|
|
19
|
+
const warnings = validateFieldValues({ hidePreviousContentFromTutor: 'yes' }, 'test.md', 10);
|
|
20
|
+
expect(warnings).toHaveLength(1);
|
|
21
|
+
expect(warnings[0].message).toContain("'hidePreviousContentFromTutor'");
|
|
22
|
+
});
|
|
23
|
+
it('warns about multiple boolean fields with non-boolean values', () => {
|
|
24
|
+
const warnings = validateFieldValues({ optional: 'yes', hidePreviousContentFromUser: '1' }, 'test.md', 10);
|
|
25
|
+
expect(warnings).toHaveLength(2);
|
|
26
|
+
});
|
|
27
|
+
it('does not warn about boolean field with "true"', () => {
|
|
28
|
+
const warnings = validateFieldValues({ optional: 'true' }, 'test.md', 10);
|
|
29
|
+
expect(warnings).toHaveLength(0);
|
|
30
|
+
});
|
|
31
|
+
it('does not warn about boolean field with "false"', () => {
|
|
32
|
+
const warnings = validateFieldValues({ optional: 'false' }, 'test.md', 10);
|
|
33
|
+
expect(warnings).toHaveLength(0);
|
|
34
|
+
});
|
|
35
|
+
it('accepts case-insensitive boolean values (True, FALSE)', () => {
|
|
36
|
+
const warnings = validateFieldValues({ optional: 'True', hidePreviousContentFromUser: 'FALSE' }, 'test.md', 10);
|
|
37
|
+
expect(warnings).toHaveLength(0);
|
|
38
|
+
});
|
|
39
|
+
it('does not warn about non-boolean fields with any value', () => {
|
|
40
|
+
const warnings = validateFieldValues({ content: 'some text', instructions: 'do something', source: '[[file.md]]' }, 'test.md', 10);
|
|
41
|
+
expect(warnings).toHaveLength(0);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=field-values.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-values.test.js","sourceRoot":"","sources":["../../src/validator/field-values.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ContentError } from '../index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validate that fields are appropriate for the segment type.
|
|
4
|
+
* Warns about fields that belong to different segment types.
|
|
5
|
+
*
|
|
6
|
+
* @param segmentType - The type of segment (text, chat, article-excerpt, video-excerpt)
|
|
7
|
+
* @param fields - Object mapping field names to their values
|
|
8
|
+
* @param file - File path for error reporting
|
|
9
|
+
* @param line - Line number for error reporting
|
|
10
|
+
* @returns Array of warning ContentError objects for misplaced fields
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateSegmentFields(segmentType: string, fields: Record<string, string>, file: string, line: number): ContentError[];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Define which fields are valid for each segment type.
|
|
3
|
+
* This allows us to warn when a field appears in the wrong segment type.
|
|
4
|
+
*/
|
|
5
|
+
const VALID_FIELDS_BY_SEGMENT_TYPE = {
|
|
6
|
+
text: new Set(['content', 'optional']),
|
|
7
|
+
chat: new Set([
|
|
8
|
+
'instructions',
|
|
9
|
+
'optional',
|
|
10
|
+
'hidePreviousContentFromUser',
|
|
11
|
+
'hidePreviousContentFromTutor',
|
|
12
|
+
]),
|
|
13
|
+
'article-excerpt': new Set(['from', 'to', 'optional']),
|
|
14
|
+
'video-excerpt': new Set(['from', 'to', 'optional']),
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Fields that are specific to excerpt segments (not valid in text/chat).
|
|
18
|
+
*/
|
|
19
|
+
const EXCERPT_ONLY_FIELDS = new Set(['from', 'to']);
|
|
20
|
+
/**
|
|
21
|
+
* Validate that fields are appropriate for the segment type.
|
|
22
|
+
* Warns about fields that belong to different segment types.
|
|
23
|
+
*
|
|
24
|
+
* @param segmentType - The type of segment (text, chat, article-excerpt, video-excerpt)
|
|
25
|
+
* @param fields - Object mapping field names to their values
|
|
26
|
+
* @param file - File path for error reporting
|
|
27
|
+
* @param line - Line number for error reporting
|
|
28
|
+
* @returns Array of warning ContentError objects for misplaced fields
|
|
29
|
+
*/
|
|
30
|
+
export function validateSegmentFields(segmentType, fields, file, line) {
|
|
31
|
+
const warnings = [];
|
|
32
|
+
const validFields = VALID_FIELDS_BY_SEGMENT_TYPE[segmentType];
|
|
33
|
+
if (!validFields) {
|
|
34
|
+
// Unknown segment type - validation handled elsewhere
|
|
35
|
+
return warnings;
|
|
36
|
+
}
|
|
37
|
+
for (const fieldName of Object.keys(fields)) {
|
|
38
|
+
if (!validFields.has(fieldName)) {
|
|
39
|
+
// Check if this is a field that belongs to a different segment type
|
|
40
|
+
if (EXCERPT_ONLY_FIELDS.has(fieldName)) {
|
|
41
|
+
warnings.push({
|
|
42
|
+
file,
|
|
43
|
+
line,
|
|
44
|
+
message: `Field '${fieldName}' is not valid in ${segmentType} segment`,
|
|
45
|
+
suggestion: `'${fieldName}' is only valid in article-excerpt or video-excerpt segments`,
|
|
46
|
+
severity: 'warning',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
// Note: We don't warn about completely unknown fields here
|
|
50
|
+
// That's handled by field-typos.ts
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return warnings;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=segment-fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment-fields.js","sourceRoot":"","sources":["../../src/validator/segment-fields.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,4BAA4B,GAAgC;IAChE,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACtC,IAAI,EAAE,IAAI,GAAG,CAAC;QACZ,cAAc;QACd,UAAU;QACV,6BAA6B;QAC7B,8BAA8B;KAC/B,CAAC;IACF,iBAAiB,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IACtD,eAAe,EAAE,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;CACrD,CAAC;AAEF;;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,8DAA8D;oBACvF,QAAQ,EAAE,SAAS;iBACpB,CAAC,CAAC;YACL,CAAC;YACD,2DAA2D;YAC3D,mCAAmC;QACrC,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/validator/segment-fields.test.ts
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { validateSegmentFields } from './segment-fields.js';
|
|
4
|
+
describe('validateSegmentFields', () => {
|
|
5
|
+
describe('text segment', () => {
|
|
6
|
+
it('allows content and optional fields', () => {
|
|
7
|
+
const warnings = validateSegmentFields('text', { content: 'Some text', optional: 'true' }, 'test.md', 10);
|
|
8
|
+
expect(warnings).toHaveLength(0);
|
|
9
|
+
});
|
|
10
|
+
it('warns about from:: in text segment', () => {
|
|
11
|
+
const warnings = validateSegmentFields('text', { content: 'Some text', from: 'Start here' }, 'test.md', 10);
|
|
12
|
+
expect(warnings).toHaveLength(1);
|
|
13
|
+
expect(warnings[0].message).toContain('from');
|
|
14
|
+
expect(warnings[0].message).toContain('text');
|
|
15
|
+
expect(warnings[0].severity).toBe('warning');
|
|
16
|
+
});
|
|
17
|
+
it('warns about to:: in text segment', () => {
|
|
18
|
+
const warnings = validateSegmentFields('text', { content: 'Some text', to: 'End here' }, 'test.md', 10);
|
|
19
|
+
expect(warnings).toHaveLength(1);
|
|
20
|
+
expect(warnings[0].message).toContain('to');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('chat segment', () => {
|
|
24
|
+
it('allows instructions, optional, and hide fields', () => {
|
|
25
|
+
const warnings = validateSegmentFields('chat', {
|
|
26
|
+
instructions: 'Do something',
|
|
27
|
+
optional: 'false',
|
|
28
|
+
hidePreviousContentFromUser: 'true',
|
|
29
|
+
hidePreviousContentFromTutor: 'false',
|
|
30
|
+
}, 'test.md', 10);
|
|
31
|
+
expect(warnings).toHaveLength(0);
|
|
32
|
+
});
|
|
33
|
+
it('warns about from:: in chat segment', () => {
|
|
34
|
+
const warnings = validateSegmentFields('chat', { instructions: 'Do something', from: '1:00' }, 'test.md', 10);
|
|
35
|
+
expect(warnings).toHaveLength(1);
|
|
36
|
+
expect(warnings[0].message).toContain('from');
|
|
37
|
+
expect(warnings[0].message).toContain('chat');
|
|
38
|
+
});
|
|
39
|
+
it('warns about to:: in chat segment', () => {
|
|
40
|
+
const warnings = validateSegmentFields('chat', { instructions: 'Do something', to: '2:00' }, 'test.md', 10);
|
|
41
|
+
expect(warnings).toHaveLength(1);
|
|
42
|
+
expect(warnings[0].message).toContain('to');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('article-excerpt segment', () => {
|
|
46
|
+
it('allows from, to, and optional fields', () => {
|
|
47
|
+
const warnings = validateSegmentFields('article-excerpt', { from: 'Start', to: 'End', optional: 'true' }, 'test.md', 10);
|
|
48
|
+
expect(warnings).toHaveLength(0);
|
|
49
|
+
});
|
|
50
|
+
it('does not warn about from/to in excerpt segment', () => {
|
|
51
|
+
const warnings = validateSegmentFields('article-excerpt', { from: 'Start', to: 'End' }, 'test.md', 10);
|
|
52
|
+
expect(warnings).toHaveLength(0);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('video-excerpt segment', () => {
|
|
56
|
+
it('allows from, to, and optional fields', () => {
|
|
57
|
+
const warnings = validateSegmentFields('video-excerpt', { from: '1:00', to: '5:00', optional: 'false' }, 'test.md', 10);
|
|
58
|
+
expect(warnings).toHaveLength(0);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('unknown segment type', () => {
|
|
62
|
+
it('returns no warnings for unknown segment type', () => {
|
|
63
|
+
const warnings = validateSegmentFields('unknown-type', { content: 'value', from: '1:00' }, 'test.md', 10);
|
|
64
|
+
expect(warnings).toHaveLength(0);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
it('includes file and line in warnings', () => {
|
|
68
|
+
const warnings = validateSegmentFields('text', { content: 'value', from: 'start' }, 'Lenses/test.md', 42);
|
|
69
|
+
expect(warnings[0].file).toBe('Lenses/test.md');
|
|
70
|
+
expect(warnings[0].line).toBe(42);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=segment-fields.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment-fields.test.js","sourceRoot":"","sources":["../../src/validator/segment-fields.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|