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.
Files changed (103) hide show
  1. package/dist/bundler/article.d.ts +41 -0
  2. package/dist/bundler/article.js +240 -0
  3. package/dist/bundler/article.js.map +1 -0
  4. package/dist/bundler/article.test.d.ts +1 -0
  5. package/dist/bundler/article.test.js +212 -0
  6. package/dist/bundler/article.test.js.map +1 -0
  7. package/dist/bundler/video.d.ts +50 -0
  8. package/dist/bundler/video.js +213 -0
  9. package/dist/bundler/video.js.map +1 -0
  10. package/dist/bundler/video.test.d.ts +1 -0
  11. package/dist/bundler/video.test.js +95 -0
  12. package/dist/bundler/video.test.js.map +1 -0
  13. package/dist/cli.d.ts +10 -0
  14. package/dist/cli.js +80 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/cli.test.d.ts +1 -0
  17. package/dist/cli.test.js +45 -0
  18. package/dist/cli.test.js.map +1 -0
  19. package/dist/flattener/index.d.ts +20 -0
  20. package/dist/flattener/index.js +707 -0
  21. package/dist/flattener/index.js.map +1 -0
  22. package/dist/flattener/index.test.d.ts +1 -0
  23. package/dist/flattener/index.test.js +547 -0
  24. package/dist/flattener/index.test.js.map +1 -0
  25. package/dist/fs/read-vault.d.ts +4 -0
  26. package/dist/fs/read-vault.js +28 -0
  27. package/dist/fs/read-vault.js.map +1 -0
  28. package/dist/fs/read-vault.test.d.ts +1 -0
  29. package/dist/fs/read-vault.test.js +37 -0
  30. package/dist/fs/read-vault.test.js.map +1 -0
  31. package/dist/index.d.ts +75 -0
  32. package/dist/index.js +137 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/parser/course.d.ts +17 -0
  35. package/dist/parser/course.js +145 -0
  36. package/dist/parser/course.js.map +1 -0
  37. package/dist/parser/course.test.d.ts +1 -0
  38. package/dist/parser/course.test.js +117 -0
  39. package/dist/parser/course.test.js.map +1 -0
  40. package/dist/parser/frontmatter.d.ts +8 -0
  41. package/dist/parser/frontmatter.js +58 -0
  42. package/dist/parser/frontmatter.js.map +1 -0
  43. package/dist/parser/frontmatter.test.d.ts +1 -0
  44. package/dist/parser/frontmatter.test.js +54 -0
  45. package/dist/parser/frontmatter.test.js.map +1 -0
  46. package/dist/parser/learning-outcome.d.ts +21 -0
  47. package/dist/parser/learning-outcome.js +116 -0
  48. package/dist/parser/learning-outcome.js.map +1 -0
  49. package/dist/parser/learning-outcome.test.d.ts +1 -0
  50. package/dist/parser/learning-outcome.test.js +75 -0
  51. package/dist/parser/learning-outcome.test.js.map +1 -0
  52. package/dist/parser/lens.d.ts +51 -0
  53. package/dist/parser/lens.js +328 -0
  54. package/dist/parser/lens.js.map +1 -0
  55. package/dist/parser/lens.test.d.ts +1 -0
  56. package/dist/parser/lens.test.js +453 -0
  57. package/dist/parser/lens.test.js.map +1 -0
  58. package/dist/parser/module.d.ts +21 -0
  59. package/dist/parser/module.js +128 -0
  60. package/dist/parser/module.js.map +1 -0
  61. package/dist/parser/module.test.d.ts +1 -0
  62. package/dist/parser/module.test.js +99 -0
  63. package/dist/parser/module.test.js.map +1 -0
  64. package/dist/parser/sections.d.ts +18 -0
  65. package/dist/parser/sections.js +128 -0
  66. package/dist/parser/sections.js.map +1 -0
  67. package/dist/parser/sections.test.d.ts +1 -0
  68. package/dist/parser/sections.test.js +297 -0
  69. package/dist/parser/sections.test.js.map +1 -0
  70. package/dist/parser/wikilink.d.ts +12 -0
  71. package/dist/parser/wikilink.js +35 -0
  72. package/dist/parser/wikilink.js.map +1 -0
  73. package/dist/parser/wikilink.test.d.ts +1 -0
  74. package/dist/parser/wikilink.test.js +76 -0
  75. package/dist/parser/wikilink.test.js.map +1 -0
  76. package/dist/validator/field-typos.d.ts +10 -0
  77. package/dist/validator/field-typos.js +96 -0
  78. package/dist/validator/field-typos.js.map +1 -0
  79. package/dist/validator/field-typos.test.d.ts +1 -0
  80. package/dist/validator/field-typos.test.js +41 -0
  81. package/dist/validator/field-typos.test.js.map +1 -0
  82. package/dist/validator/field-values.d.ts +11 -0
  83. package/dist/validator/field-values.js +37 -0
  84. package/dist/validator/field-values.js.map +1 -0
  85. package/dist/validator/field-values.test.d.ts +1 -0
  86. package/dist/validator/field-values.test.js +45 -0
  87. package/dist/validator/field-values.test.js.map +1 -0
  88. package/dist/validator/segment-fields.d.ts +12 -0
  89. package/dist/validator/segment-fields.js +55 -0
  90. package/dist/validator/segment-fields.js.map +1 -0
  91. package/dist/validator/segment-fields.test.d.ts +1 -0
  92. package/dist/validator/segment-fields.test.js +73 -0
  93. package/dist/validator/segment-fields.test.js.map +1 -0
  94. package/dist/validator/standalone.test.d.ts +1 -0
  95. package/dist/validator/standalone.test.js +190 -0
  96. package/dist/validator/standalone.test.js.map +1 -0
  97. package/dist/validator/uuid.d.ts +17 -0
  98. package/dist/validator/uuid.js +46 -0
  99. package/dist/validator/uuid.js.map +1 -0
  100. package/dist/validator/uuid.test.d.ts +1 -0
  101. package/dist/validator/uuid.test.js +229 -0
  102. package/dist/validator/uuid.test.js.map +1 -0
  103. 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;