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,41 @@
|
|
|
1
|
+
import type { ContentError } from '../index.js';
|
|
2
|
+
export interface ArticleExcerptResult {
|
|
3
|
+
content?: string;
|
|
4
|
+
startIndex?: number;
|
|
5
|
+
endIndex?: number;
|
|
6
|
+
error?: ContentError;
|
|
7
|
+
}
|
|
8
|
+
export interface CollapsedExcerpt {
|
|
9
|
+
content: string;
|
|
10
|
+
collapsed_before?: string;
|
|
11
|
+
collapsed_after?: string;
|
|
12
|
+
error?: ContentError;
|
|
13
|
+
}
|
|
14
|
+
export interface ExcerptInput {
|
|
15
|
+
from: string;
|
|
16
|
+
to: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Extract content from an article between two anchor texts.
|
|
20
|
+
*
|
|
21
|
+
* Both anchors are optional:
|
|
22
|
+
* - Only fromAnchor → extract from anchor to end of article
|
|
23
|
+
* - Only toAnchor → extract from start of article to anchor
|
|
24
|
+
* - Neither → extract entire article (stripping frontmatter)
|
|
25
|
+
*
|
|
26
|
+
* @param article - The full article content
|
|
27
|
+
* @param fromAnchor - Text marking the start of the excerpt (inclusive), undefined means start
|
|
28
|
+
* @param toAnchor - Text marking the end of the excerpt (inclusive), undefined means end
|
|
29
|
+
* @param file - Source file path for error reporting
|
|
30
|
+
* @returns Extracted content or error
|
|
31
|
+
*/
|
|
32
|
+
export declare function extractArticleExcerpt(article: string, fromAnchor: string | undefined, toAnchor: string | undefined, file: string): ArticleExcerptResult;
|
|
33
|
+
/**
|
|
34
|
+
* Bundle multiple excerpts from an article with collapsed content information.
|
|
35
|
+
*
|
|
36
|
+
* @param article - The full article content
|
|
37
|
+
* @param excerpts - Array of excerpt specifications { from, to }
|
|
38
|
+
* @param file - Source file path for error reporting
|
|
39
|
+
* @returns Array of excerpts with collapsed_before/collapsed_after fields
|
|
40
|
+
*/
|
|
41
|
+
export declare function bundleArticleWithCollapsed(article: string, excerpts: ExcerptInput[], file: string): CollapsedExcerpt[];
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find all occurrences of a substring in text (case-insensitive).
|
|
3
|
+
* Returns array of start indices.
|
|
4
|
+
*/
|
|
5
|
+
function findAllOccurrences(text, anchor) {
|
|
6
|
+
const lowerText = text.toLowerCase();
|
|
7
|
+
const lowerAnchor = anchor.toLowerCase();
|
|
8
|
+
const indices = [];
|
|
9
|
+
let pos = 0;
|
|
10
|
+
while (true) {
|
|
11
|
+
const idx = lowerText.indexOf(lowerAnchor, pos);
|
|
12
|
+
if (idx === -1)
|
|
13
|
+
break;
|
|
14
|
+
indices.push(idx);
|
|
15
|
+
pos = idx + 1;
|
|
16
|
+
}
|
|
17
|
+
return indices;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Strip frontmatter from article content.
|
|
21
|
+
* Frontmatter is enclosed in --- markers at the start of the file.
|
|
22
|
+
*/
|
|
23
|
+
function stripFrontmatter(article) {
|
|
24
|
+
const match = article.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/);
|
|
25
|
+
return match ? match[1].trim() : article.trim();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Extract content from an article between two anchor texts.
|
|
29
|
+
*
|
|
30
|
+
* Both anchors are optional:
|
|
31
|
+
* - Only fromAnchor → extract from anchor to end of article
|
|
32
|
+
* - Only toAnchor → extract from start of article to anchor
|
|
33
|
+
* - Neither → extract entire article (stripping frontmatter)
|
|
34
|
+
*
|
|
35
|
+
* @param article - The full article content
|
|
36
|
+
* @param fromAnchor - Text marking the start of the excerpt (inclusive), undefined means start
|
|
37
|
+
* @param toAnchor - Text marking the end of the excerpt (inclusive), undefined means end
|
|
38
|
+
* @param file - Source file path for error reporting
|
|
39
|
+
* @returns Extracted content or error
|
|
40
|
+
*/
|
|
41
|
+
export function extractArticleExcerpt(article, fromAnchor, toAnchor, file) {
|
|
42
|
+
// If no anchors, return entire article (strip frontmatter)
|
|
43
|
+
if (!fromAnchor && !toAnchor) {
|
|
44
|
+
const content = stripFrontmatter(article);
|
|
45
|
+
return {
|
|
46
|
+
content,
|
|
47
|
+
startIndex: 0,
|
|
48
|
+
endIndex: article.length,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Get article body (strip frontmatter) for extraction
|
|
52
|
+
const body = stripFrontmatter(article);
|
|
53
|
+
// If only toAnchor, extract from start to anchor
|
|
54
|
+
if (!fromAnchor && toAnchor) {
|
|
55
|
+
const toOccurrences = findAllOccurrences(body, toAnchor);
|
|
56
|
+
if (toOccurrences.length === 0) {
|
|
57
|
+
return {
|
|
58
|
+
error: {
|
|
59
|
+
file,
|
|
60
|
+
message: `End anchor '${toAnchor}' not found in article`,
|
|
61
|
+
suggestion: 'Check that the anchor text exists exactly in the article',
|
|
62
|
+
severity: 'error',
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (toOccurrences.length > 1) {
|
|
67
|
+
return {
|
|
68
|
+
error: {
|
|
69
|
+
file,
|
|
70
|
+
message: `End anchor '${toAnchor}' found multiple times (${toOccurrences.length} occurrences) - ambiguous`,
|
|
71
|
+
suggestion: 'Use a more specific anchor text that appears only once',
|
|
72
|
+
severity: 'error',
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const endIndex = toOccurrences[0] + toAnchor.length;
|
|
77
|
+
const content = body.slice(0, endIndex);
|
|
78
|
+
return {
|
|
79
|
+
content,
|
|
80
|
+
startIndex: 0,
|
|
81
|
+
endIndex,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// If only fromAnchor, extract from anchor to end
|
|
85
|
+
if (fromAnchor && !toAnchor) {
|
|
86
|
+
const fromOccurrences = findAllOccurrences(body, fromAnchor);
|
|
87
|
+
if (fromOccurrences.length === 0) {
|
|
88
|
+
return {
|
|
89
|
+
error: {
|
|
90
|
+
file,
|
|
91
|
+
message: `Start anchor '${fromAnchor}' not found in article`,
|
|
92
|
+
suggestion: 'Check that the anchor text exists exactly in the article',
|
|
93
|
+
severity: 'error',
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (fromOccurrences.length > 1) {
|
|
98
|
+
return {
|
|
99
|
+
error: {
|
|
100
|
+
file,
|
|
101
|
+
message: `Start anchor '${fromAnchor}' found multiple times (${fromOccurrences.length} occurrences) - ambiguous`,
|
|
102
|
+
suggestion: 'Use a more specific anchor text that appears only once',
|
|
103
|
+
severity: 'error',
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const startIndex = fromOccurrences[0];
|
|
108
|
+
const content = body.slice(startIndex);
|
|
109
|
+
return {
|
|
110
|
+
content,
|
|
111
|
+
startIndex,
|
|
112
|
+
endIndex: body.length,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// Both anchors provided - find content between them
|
|
116
|
+
// At this point we know both fromAnchor and toAnchor are defined (not undefined)
|
|
117
|
+
const fromAnchorStr = fromAnchor;
|
|
118
|
+
const toAnchorStr = toAnchor;
|
|
119
|
+
const fromOccurrences = findAllOccurrences(article, fromAnchorStr);
|
|
120
|
+
if (fromOccurrences.length === 0) {
|
|
121
|
+
return {
|
|
122
|
+
error: {
|
|
123
|
+
file,
|
|
124
|
+
message: `Start anchor '${fromAnchorStr}' not found in article`,
|
|
125
|
+
suggestion: 'Check that the anchor text exists exactly in the article',
|
|
126
|
+
severity: 'error',
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (fromOccurrences.length > 1) {
|
|
131
|
+
return {
|
|
132
|
+
error: {
|
|
133
|
+
file,
|
|
134
|
+
message: `Start anchor '${fromAnchorStr}' found multiple times (${fromOccurrences.length} occurrences) - ambiguous`,
|
|
135
|
+
suggestion: 'Use a more specific anchor text that appears only once',
|
|
136
|
+
severity: 'error',
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const startIndex = fromOccurrences[0];
|
|
141
|
+
// Search for end anchor only AFTER the start anchor (case-insensitive)
|
|
142
|
+
const afterStart = article.slice(startIndex);
|
|
143
|
+
const toOccurrences = findAllOccurrences(afterStart, toAnchorStr);
|
|
144
|
+
if (toOccurrences.length === 0) {
|
|
145
|
+
return {
|
|
146
|
+
error: {
|
|
147
|
+
file,
|
|
148
|
+
message: `End anchor '${toAnchorStr}' not found in article after start anchor`,
|
|
149
|
+
suggestion: 'Check that the anchor text exists after the start anchor',
|
|
150
|
+
severity: 'error',
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (toOccurrences.length > 1) {
|
|
155
|
+
return {
|
|
156
|
+
error: {
|
|
157
|
+
file,
|
|
158
|
+
message: `End anchor '${toAnchorStr}' found multiple times (${toOccurrences.length} occurrences) after start - ambiguous`,
|
|
159
|
+
suggestion: 'Use a more specific anchor text that appears only once',
|
|
160
|
+
severity: 'error',
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
// Calculate absolute end index (end of the anchor text)
|
|
165
|
+
const relativeToIndex = toOccurrences[0];
|
|
166
|
+
const endIndex = startIndex + relativeToIndex + toAnchorStr.length;
|
|
167
|
+
// Extract the content between (and including) the anchors
|
|
168
|
+
const content = article.slice(startIndex, endIndex);
|
|
169
|
+
return {
|
|
170
|
+
content,
|
|
171
|
+
startIndex,
|
|
172
|
+
endIndex,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Bundle multiple excerpts from an article with collapsed content information.
|
|
177
|
+
*
|
|
178
|
+
* @param article - The full article content
|
|
179
|
+
* @param excerpts - Array of excerpt specifications { from, to }
|
|
180
|
+
* @param file - Source file path for error reporting
|
|
181
|
+
* @returns Array of excerpts with collapsed_before/collapsed_after fields
|
|
182
|
+
*/
|
|
183
|
+
export function bundleArticleWithCollapsed(article, excerpts, file) {
|
|
184
|
+
// First, extract all excerpts and their positions
|
|
185
|
+
const extractedExcerpts = [];
|
|
186
|
+
for (const excerpt of excerpts) {
|
|
187
|
+
const result = extractArticleExcerpt(article, excerpt.from, excerpt.to, file);
|
|
188
|
+
if (result.error) {
|
|
189
|
+
extractedExcerpts.push({
|
|
190
|
+
content: '',
|
|
191
|
+
startIndex: -1,
|
|
192
|
+
endIndex: -1,
|
|
193
|
+
error: result.error,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
extractedExcerpts.push({
|
|
198
|
+
content: result.content,
|
|
199
|
+
startIndex: result.startIndex,
|
|
200
|
+
endIndex: result.endIndex,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Build the result with collapsed content
|
|
205
|
+
const results = [];
|
|
206
|
+
for (let i = 0; i < extractedExcerpts.length; i++) {
|
|
207
|
+
const extracted = extractedExcerpts[i];
|
|
208
|
+
if (extracted.error) {
|
|
209
|
+
results.push({
|
|
210
|
+
content: '',
|
|
211
|
+
error: extracted.error,
|
|
212
|
+
});
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const result = {
|
|
216
|
+
content: extracted.content,
|
|
217
|
+
};
|
|
218
|
+
// Calculate collapsed_before (content between previous excerpt end and this excerpt start)
|
|
219
|
+
if (i > 0) {
|
|
220
|
+
const prevExcerpt = extractedExcerpts[i - 1];
|
|
221
|
+
if (!prevExcerpt.error && prevExcerpt.endIndex < extracted.startIndex) {
|
|
222
|
+
const collapsedBefore = article.slice(prevExcerpt.endIndex, extracted.startIndex).trim();
|
|
223
|
+
if (collapsedBefore.length > 0) {
|
|
224
|
+
result.collapsed_before = collapsedBefore;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// Calculate collapsed_after (content after this excerpt to next excerpt or end)
|
|
229
|
+
// Only set for the last excerpt
|
|
230
|
+
if (i === extractedExcerpts.length - 1) {
|
|
231
|
+
const collapsedAfter = article.slice(extracted.endIndex).trim();
|
|
232
|
+
if (collapsedAfter.length > 0) {
|
|
233
|
+
result.collapsed_after = collapsedAfter;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
results.push(result);
|
|
237
|
+
}
|
|
238
|
+
return results;
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=article.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"article.js","sourceRoot":"","sources":["../../src/bundler/article.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// src/bundler/article.test.ts
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { extractArticleExcerpt, bundleArticleWithCollapsed } from './article.js';
|
|
4
|
+
describe('extractArticleExcerpt', () => {
|
|
5
|
+
it('extracts content between anchors', () => {
|
|
6
|
+
const article = `# Article Title
|
|
7
|
+
|
|
8
|
+
Some intro text.
|
|
9
|
+
|
|
10
|
+
The key insight is that AI alignment requires careful consideration
|
|
11
|
+
of human values. This is a complex problem that involves
|
|
12
|
+
understanding this concept.
|
|
13
|
+
|
|
14
|
+
More content after.
|
|
15
|
+
`;
|
|
16
|
+
const result = extractArticleExcerpt(article, 'The key insight is', 'understanding this concept.', 'articles/test.md');
|
|
17
|
+
expect(result.content).toContain('AI alignment');
|
|
18
|
+
expect(result.content).toContain('human values');
|
|
19
|
+
expect(result.error).toBeUndefined();
|
|
20
|
+
});
|
|
21
|
+
it('returns error for missing start anchor', () => {
|
|
22
|
+
const article = 'Some content without the anchor.';
|
|
23
|
+
const result = extractArticleExcerpt(article, 'nonexistent anchor', 'also missing', 'articles/test.md');
|
|
24
|
+
expect(result.error).toBeDefined();
|
|
25
|
+
expect(result.error?.message).toContain('not found');
|
|
26
|
+
expect(result.error?.suggestion).toContain('anchor');
|
|
27
|
+
});
|
|
28
|
+
it('returns error for duplicate anchor', () => {
|
|
29
|
+
const article = `First occurrence of the phrase here.
|
|
30
|
+
|
|
31
|
+
And another occurrence of the phrase here.`;
|
|
32
|
+
const result = extractArticleExcerpt(article, 'occurrence of the phrase', 'here', 'articles/test.md');
|
|
33
|
+
expect(result.error).toBeDefined();
|
|
34
|
+
expect(result.error?.message).toContain('multiple');
|
|
35
|
+
});
|
|
36
|
+
it('is case-insensitive for matching', () => {
|
|
37
|
+
const article = 'THE KEY INSIGHT is important.';
|
|
38
|
+
const result = extractArticleExcerpt(article, 'the key insight', 'important.', 'articles/test.md');
|
|
39
|
+
expect(result.content).toBeDefined();
|
|
40
|
+
expect(result.error).toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
describe('special characters in anchors', () => {
|
|
43
|
+
it('handles apostrophes in anchor text', () => {
|
|
44
|
+
const article = `Introduction here.
|
|
45
|
+
|
|
46
|
+
It's important to understand that AI systems don't always behave as expected.
|
|
47
|
+
|
|
48
|
+
Conclusion here.`;
|
|
49
|
+
const result = extractArticleExcerpt(article, "It's important", "don't always behave", 'articles/test.md');
|
|
50
|
+
expect(result.error).toBeUndefined();
|
|
51
|
+
expect(result.content).toContain("It's important");
|
|
52
|
+
expect(result.content).toContain("don't always");
|
|
53
|
+
});
|
|
54
|
+
it('handles ampersand in anchor text', () => {
|
|
55
|
+
const article = `Introduction here.
|
|
56
|
+
|
|
57
|
+
The relationship between safety & alignment is crucial for AI development.
|
|
58
|
+
|
|
59
|
+
Conclusion here.`;
|
|
60
|
+
const result = extractArticleExcerpt(article, 'safety & alignment', 'AI development', 'articles/test.md');
|
|
61
|
+
expect(result.error).toBeUndefined();
|
|
62
|
+
expect(result.content).toContain('safety & alignment');
|
|
63
|
+
});
|
|
64
|
+
it('handles colons in anchor text', () => {
|
|
65
|
+
const article = `Introduction here.
|
|
66
|
+
|
|
67
|
+
Key point: this is the main argument we need to consider carefully.
|
|
68
|
+
|
|
69
|
+
Conclusion here.`;
|
|
70
|
+
const result = extractArticleExcerpt(article, 'Key point:', 'consider carefully', 'articles/test.md');
|
|
71
|
+
expect(result.error).toBeUndefined();
|
|
72
|
+
expect(result.content).toContain('Key point:');
|
|
73
|
+
});
|
|
74
|
+
it('handles percent signs in anchor text', () => {
|
|
75
|
+
const article = `Introduction here.
|
|
76
|
+
|
|
77
|
+
The model achieved 95% accuracy on the benchmark test results.
|
|
78
|
+
|
|
79
|
+
Conclusion here.`;
|
|
80
|
+
const result = extractArticleExcerpt(article, '95% accuracy', 'test results', 'articles/test.md');
|
|
81
|
+
expect(result.error).toBeUndefined();
|
|
82
|
+
expect(result.content).toContain('95% accuracy');
|
|
83
|
+
});
|
|
84
|
+
it('handles multiple special characters together', () => {
|
|
85
|
+
const article = `Introduction here.
|
|
86
|
+
|
|
87
|
+
Here's the key insight: AI systems won't achieve 100% safety & reliability without careful design.
|
|
88
|
+
|
|
89
|
+
Conclusion here.`;
|
|
90
|
+
const result = extractArticleExcerpt(article, "Here's the key insight:", "100% safety & reliability", 'articles/test.md');
|
|
91
|
+
expect(result.error).toBeUndefined();
|
|
92
|
+
expect(result.content).toContain("Here's");
|
|
93
|
+
expect(result.content).toContain("100%");
|
|
94
|
+
expect(result.content).toContain("&");
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
describe('bundleArticleWithCollapsed', () => {
|
|
99
|
+
it('computes collapsed_before for non-first excerpt', () => {
|
|
100
|
+
const article = `# Article
|
|
101
|
+
|
|
102
|
+
Intro paragraph.
|
|
103
|
+
|
|
104
|
+
First important section that we want to show.
|
|
105
|
+
|
|
106
|
+
Middle content that gets collapsed.
|
|
107
|
+
|
|
108
|
+
Second important section to show.
|
|
109
|
+
|
|
110
|
+
Conclusion.
|
|
111
|
+
`;
|
|
112
|
+
const excerpts = [
|
|
113
|
+
{ from: 'First important', to: 'want to show.' },
|
|
114
|
+
{ from: 'Second important', to: 'section to show.' },
|
|
115
|
+
];
|
|
116
|
+
const result = bundleArticleWithCollapsed(article, excerpts, 'articles/test.md');
|
|
117
|
+
expect(result[0].collapsed_before).toBeUndefined(); // First excerpt has no collapsed_before
|
|
118
|
+
expect(result[1].collapsed_before).toContain('Middle content');
|
|
119
|
+
});
|
|
120
|
+
it('computes collapsed_after for last excerpt', () => {
|
|
121
|
+
const article = `Intro.
|
|
122
|
+
|
|
123
|
+
Main content here.
|
|
124
|
+
|
|
125
|
+
Conclusion paragraph at the end.
|
|
126
|
+
`;
|
|
127
|
+
const excerpts = [
|
|
128
|
+
{ from: 'Main content', to: 'content here.' },
|
|
129
|
+
];
|
|
130
|
+
const result = bundleArticleWithCollapsed(article, excerpts, 'articles/test.md');
|
|
131
|
+
expect(result[0].collapsed_after).toContain('Conclusion paragraph');
|
|
132
|
+
});
|
|
133
|
+
it('handles adjacent excerpts with no collapsed content', () => {
|
|
134
|
+
const article = `First sentence. Second sentence.`;
|
|
135
|
+
const excerpts = [
|
|
136
|
+
{ from: 'First', to: 'sentence.' },
|
|
137
|
+
{ from: 'Second', to: 'sentence.' },
|
|
138
|
+
];
|
|
139
|
+
const result = bundleArticleWithCollapsed(article, excerpts, 'articles/test.md');
|
|
140
|
+
// Adjacent excerpts have minimal or no collapsed content
|
|
141
|
+
expect(result[0].collapsed_after).toBeUndefined();
|
|
142
|
+
expect(result[1].collapsed_before).toBeUndefined();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
describe('extractArticleExcerpt with optional anchors', () => {
|
|
146
|
+
const articleWithFrontmatter = `---
|
|
147
|
+
title: Test Article
|
|
148
|
+
author: John Doe
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
Introduction paragraph here.
|
|
152
|
+
|
|
153
|
+
Start here with the main content that we want to extract.
|
|
154
|
+
|
|
155
|
+
Middle section content.
|
|
156
|
+
|
|
157
|
+
End here and this is included.
|
|
158
|
+
|
|
159
|
+
Conclusion paragraph at the end.
|
|
160
|
+
`;
|
|
161
|
+
const articleWithoutFrontmatter = `Introduction paragraph here.
|
|
162
|
+
|
|
163
|
+
Start here with the main content that we want to extract.
|
|
164
|
+
|
|
165
|
+
Middle section content.
|
|
166
|
+
|
|
167
|
+
End here and this is included.
|
|
168
|
+
|
|
169
|
+
Conclusion paragraph at the end.
|
|
170
|
+
`;
|
|
171
|
+
it('extracts from anchor to end of article when toAnchor is undefined', () => {
|
|
172
|
+
const result = extractArticleExcerpt(articleWithoutFrontmatter, 'Start here', undefined, 'articles/test.md');
|
|
173
|
+
expect(result.error).toBeUndefined();
|
|
174
|
+
expect(result.content).toContain('Start here');
|
|
175
|
+
expect(result.content).toContain('Middle section');
|
|
176
|
+
expect(result.content).toContain('End here');
|
|
177
|
+
expect(result.content).toContain('Conclusion paragraph');
|
|
178
|
+
});
|
|
179
|
+
it('extracts from start of article to anchor when fromAnchor is undefined', () => {
|
|
180
|
+
const result = extractArticleExcerpt(articleWithoutFrontmatter, undefined, 'End here and this is included.', 'articles/test.md');
|
|
181
|
+
expect(result.error).toBeUndefined();
|
|
182
|
+
expect(result.content).toContain('Introduction paragraph');
|
|
183
|
+
expect(result.content).toContain('Start here');
|
|
184
|
+
expect(result.content).toContain('End here and this is included.');
|
|
185
|
+
expect(result.content).not.toContain('Conclusion paragraph');
|
|
186
|
+
});
|
|
187
|
+
it('extracts entire article when both anchors are undefined', () => {
|
|
188
|
+
const result = extractArticleExcerpt(articleWithoutFrontmatter, undefined, undefined, 'articles/test.md');
|
|
189
|
+
expect(result.error).toBeUndefined();
|
|
190
|
+
expect(result.content).toContain('Introduction paragraph');
|
|
191
|
+
expect(result.content).toContain('Conclusion paragraph');
|
|
192
|
+
expect(result.content).toBe(articleWithoutFrontmatter.trim());
|
|
193
|
+
});
|
|
194
|
+
it('strips frontmatter when extracting entire article', () => {
|
|
195
|
+
const result = extractArticleExcerpt(articleWithFrontmatter, undefined, undefined, 'articles/test.md');
|
|
196
|
+
expect(result.error).toBeUndefined();
|
|
197
|
+
expect(result.content).not.toContain('title:');
|
|
198
|
+
expect(result.content).not.toContain('author:');
|
|
199
|
+
expect(result.content).not.toContain('---');
|
|
200
|
+
expect(result.content).toContain('Introduction paragraph');
|
|
201
|
+
expect(result.content).toContain('Conclusion paragraph');
|
|
202
|
+
});
|
|
203
|
+
it('strips frontmatter when extracting from start to anchor', () => {
|
|
204
|
+
const result = extractArticleExcerpt(articleWithFrontmatter, undefined, 'Middle section content.', 'articles/test.md');
|
|
205
|
+
expect(result.error).toBeUndefined();
|
|
206
|
+
expect(result.content).not.toContain('title:');
|
|
207
|
+
expect(result.content).not.toContain('---');
|
|
208
|
+
expect(result.content).toContain('Introduction paragraph');
|
|
209
|
+
expect(result.content).toContain('Middle section content.');
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
//# sourceMappingURL=article.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"article.test.js","sourceRoot":"","sources":["../../src/bundler/article.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ContentError } from '../index.js';
|
|
2
|
+
export interface VideoExcerptResult {
|
|
3
|
+
from?: number;
|
|
4
|
+
to?: number | null;
|
|
5
|
+
transcript?: string;
|
|
6
|
+
error?: ContentError;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Word-level timestamp entry from .timestamps.json files.
|
|
10
|
+
*/
|
|
11
|
+
export interface TimestampEntry {
|
|
12
|
+
text: string;
|
|
13
|
+
start: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse a timestamp string into seconds.
|
|
17
|
+
*
|
|
18
|
+
* Supports formats:
|
|
19
|
+
* - MM:SS (e.g., "1:30" -> 90)
|
|
20
|
+
* - H:MM:SS (e.g., "1:30:00" -> 5400)
|
|
21
|
+
* - M:SS.ms (e.g., "0:00.40" -> 0.4) - used in .timestamps.json files
|
|
22
|
+
*
|
|
23
|
+
* @param str - Timestamp string
|
|
24
|
+
* @returns Number of seconds, or null if invalid format
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseTimestamp(str: string): number | null;
|
|
27
|
+
/**
|
|
28
|
+
* Extract words from timestamps data that fall within the requested time range.
|
|
29
|
+
*
|
|
30
|
+
* @param timestamps - Array of word-level timestamp entries
|
|
31
|
+
* @param fromSeconds - Start time in seconds
|
|
32
|
+
* @param toSeconds - End time in seconds
|
|
33
|
+
* @returns Words joined with spaces
|
|
34
|
+
*/
|
|
35
|
+
export declare function extractFromTimestamps(timestamps: TimestampEntry[], fromSeconds: number, toSeconds: number): string;
|
|
36
|
+
/**
|
|
37
|
+
* Extract transcript content between two timestamps.
|
|
38
|
+
*
|
|
39
|
+
* If timestamps data (from .timestamps.json) is provided, uses word-level
|
|
40
|
+
* extraction. Otherwise, falls back to inline timestamp markers in the
|
|
41
|
+
* transcript markdown.
|
|
42
|
+
*
|
|
43
|
+
* @param transcript - The full transcript content (markdown)
|
|
44
|
+
* @param fromTime - Start timestamp string (e.g., "1:30")
|
|
45
|
+
* @param toTime - End timestamp string (e.g., "5:45")
|
|
46
|
+
* @param file - Source file path for error reporting
|
|
47
|
+
* @param timestamps - Optional word-level timestamps from .timestamps.json
|
|
48
|
+
* @returns Extracted transcript with from/to as seconds, or error
|
|
49
|
+
*/
|
|
50
|
+
export declare function extractVideoExcerpt(transcript: string, fromTime: string, toTime: string, file: string, timestamps?: TimestampEntry[]): VideoExcerptResult;
|