@heripo/research-radar 1.0.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/LICENSE +73 -0
- package/NOTICE +60 -0
- package/README.md +216 -0
- package/dist/index.cjs +2311 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +346 -0
- package/dist/index.js +2281 -0
- package/dist/index.js.map +1 -0
- package/package.json +98 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { UrlString, ParsedTarget, CrawlingTargetGroup, CrawlingTarget, UnscoredArticle, ArticleForUpdateByAnalysis, ArticleForGenerateContent, Newsletter, AppLogger, EmailService, EmailMessage, DateService as DateService$1, IsoDateString, TaskService as TaskService$1, AnalysisProvider as AnalysisProvider$1, ContentGenerateProvider as ContentGenerateProvider$1, HtmlTemplate, CrawlingProvider as CrawlingProvider$1 } from '@llm-newsletter-kit/core';
|
|
2
|
+
import { OpenAIProvider } from '@ai-sdk/openai';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Repository interface for task management
|
|
6
|
+
*/
|
|
7
|
+
interface TaskRepository {
|
|
8
|
+
/**
|
|
9
|
+
* Create and save a new task
|
|
10
|
+
* @returns Created task ID
|
|
11
|
+
*/
|
|
12
|
+
createTask(): Promise<number>;
|
|
13
|
+
/**
|
|
14
|
+
* Complete a task
|
|
15
|
+
* @param taskId Task ID to complete
|
|
16
|
+
*/
|
|
17
|
+
completeTask(taskId: number): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Repository interface for article management
|
|
21
|
+
*/
|
|
22
|
+
interface ArticleRepository {
|
|
23
|
+
/**
|
|
24
|
+
* Find existing articles by URLs
|
|
25
|
+
* @param urls URLs to query
|
|
26
|
+
* @returns Previously saved articles
|
|
27
|
+
*/
|
|
28
|
+
findByUrls(urls: UrlString[]): Promise<ParsedTarget[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Save crawled articles
|
|
31
|
+
* @param articles Articles to save
|
|
32
|
+
* @param context Task context information
|
|
33
|
+
* @returns Number of saved articles
|
|
34
|
+
*/
|
|
35
|
+
saveCrawledArticles<TaskId>(articles: ParsedTarget[], context: {
|
|
36
|
+
taskId: TaskId;
|
|
37
|
+
targetGroup: Omit<CrawlingTargetGroup, 'targets'>;
|
|
38
|
+
target: CrawlingTarget;
|
|
39
|
+
}): Promise<number>;
|
|
40
|
+
/**
|
|
41
|
+
* Find unscored articles (targets for analysis)
|
|
42
|
+
* @returns Articles without scores
|
|
43
|
+
*/
|
|
44
|
+
findUnscoredArticles(): Promise<UnscoredArticle[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Update article with analysis results
|
|
47
|
+
* @param article Article information to update
|
|
48
|
+
*/
|
|
49
|
+
updateAnalysis(article: ArticleForUpdateByAnalysis): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Find candidate articles for newsletter generation
|
|
52
|
+
* @returns Candidate articles
|
|
53
|
+
*/
|
|
54
|
+
findCandidatesForNewsletter(): Promise<ArticleForGenerateContent[]>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Repository interface for tag management
|
|
58
|
+
*/
|
|
59
|
+
interface TagRepository {
|
|
60
|
+
/**
|
|
61
|
+
* Find all existing tags
|
|
62
|
+
* @returns Tag name list
|
|
63
|
+
*/
|
|
64
|
+
findAllTags(): Promise<string[]>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Repository interface for newsletter management
|
|
68
|
+
*/
|
|
69
|
+
interface NewsletterRepository {
|
|
70
|
+
/**
|
|
71
|
+
* Get the next issue order number
|
|
72
|
+
* @returns Next issue order
|
|
73
|
+
*/
|
|
74
|
+
getNextIssueOrder(): Promise<number>;
|
|
75
|
+
/**
|
|
76
|
+
* Save newsletter
|
|
77
|
+
* @param input - Input parameters
|
|
78
|
+
* @param input.newsletter - Newsletter data
|
|
79
|
+
* @param input.usedArticles - List of used articles
|
|
80
|
+
* @returns Saved newsletter ID
|
|
81
|
+
*/
|
|
82
|
+
saveNewsletter(input: {
|
|
83
|
+
newsletter: Newsletter;
|
|
84
|
+
usedArticles: ArticleForGenerateContent[];
|
|
85
|
+
}): Promise<{
|
|
86
|
+
id: string | number;
|
|
87
|
+
}>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* This implementation is currently hardcoded to use OpenAI.
|
|
92
|
+
* To use a different LLM provider, you need to modify:
|
|
93
|
+
* - This file: Change `createOpenAI` import and initialization
|
|
94
|
+
* - src/providers/analysis.provider.ts: Change OpenAIProvider type and model names
|
|
95
|
+
* - src/providers/content-generate.provider.ts: Change OpenAIProvider type and model name
|
|
96
|
+
*
|
|
97
|
+
* For details on switching providers, see README.md section:
|
|
98
|
+
* "⚠️ Fork하여 나만의 뉴스레터 만들기 > 4. LLM 프로바이더 변경"
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Preview newsletter configuration options
|
|
103
|
+
*/
|
|
104
|
+
interface PreviewNewsletterOptions {
|
|
105
|
+
/** Function to fetch newsletter for preview */
|
|
106
|
+
fetchNewsletterForPreview: () => Promise<Newsletter>;
|
|
107
|
+
/** Email sending service */
|
|
108
|
+
emailService: EmailService;
|
|
109
|
+
/** Email message configuration (subject, html, text are auto-generated) */
|
|
110
|
+
emailMessage: Omit<EmailMessage, 'subject' | 'html' | 'text'>;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Newsletter generator dependencies interface
|
|
114
|
+
*/
|
|
115
|
+
interface NewsletterGeneratorDependencies {
|
|
116
|
+
/** OpenAI API key */
|
|
117
|
+
openAIApiKey: string;
|
|
118
|
+
/** Task management repository */
|
|
119
|
+
taskRepository: TaskRepository;
|
|
120
|
+
/** Article management repository */
|
|
121
|
+
articleRepository: ArticleRepository;
|
|
122
|
+
/** Tag management repository */
|
|
123
|
+
tagRepository: TagRepository;
|
|
124
|
+
/** Newsletter management repository */
|
|
125
|
+
newsletterRepository: NewsletterRepository;
|
|
126
|
+
/** Logger (optional) */
|
|
127
|
+
logger?: AppLogger;
|
|
128
|
+
/** Preview email configuration (optional) */
|
|
129
|
+
previewNewsletter?: PreviewNewsletterOptions;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Newsletter generation execution function
|
|
133
|
+
*
|
|
134
|
+
* @param dependencies - Repository implementations and options
|
|
135
|
+
* @returns Generated newsletter ID
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* const newsletterId = await generateNewsletter({
|
|
140
|
+
* openAIApiKey: process.env.OPENAI_API_KEY,
|
|
141
|
+
* taskRepository: new PrismaTaskRepository(prisma),
|
|
142
|
+
* articleRepository: new PrismaArticleRepository(prisma),
|
|
143
|
+
* tagRepository: new PrismaTagRepository(prisma),
|
|
144
|
+
* newsletterRepository: new PrismaNewsletterRepository(prisma),
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare function generateNewsletter(dependencies: NewsletterGeneratorDependencies): Promise<string | number | null>;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Date service implementation
|
|
152
|
+
* - Provides current date and display date strings
|
|
153
|
+
*/
|
|
154
|
+
declare class DateService implements DateService$1 {
|
|
155
|
+
/**
|
|
156
|
+
* Get current date in ISO format (YYYY-MM-DD)
|
|
157
|
+
* @returns ISO date string (e.g., "2024-10-15")
|
|
158
|
+
*/
|
|
159
|
+
getCurrentISODateString(): IsoDateString;
|
|
160
|
+
/**
|
|
161
|
+
* Get formatted display date string
|
|
162
|
+
* @returns Korean formatted date (e.g., "2024년 10월 15일")
|
|
163
|
+
*/
|
|
164
|
+
getDisplayDateString(): string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Task service implementation
|
|
169
|
+
* - Manages newsletter generation task lifecycle (start/end)
|
|
170
|
+
* - Prevents duplicate execution
|
|
171
|
+
*/
|
|
172
|
+
declare class TaskService implements TaskService$1<number> {
|
|
173
|
+
private readonly taskRepository;
|
|
174
|
+
private currentTaskId;
|
|
175
|
+
constructor(taskRepository: TaskRepository);
|
|
176
|
+
/**
|
|
177
|
+
* Start a new task
|
|
178
|
+
* @throws Error if a task is already running
|
|
179
|
+
* @returns Task ID
|
|
180
|
+
*/
|
|
181
|
+
start(): Promise<number>;
|
|
182
|
+
/**
|
|
183
|
+
* End the current task
|
|
184
|
+
* @throws Error if no task is running
|
|
185
|
+
*/
|
|
186
|
+
end(): Promise<void>;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Analysis provider implementation
|
|
191
|
+
* - LLM-based article analysis
|
|
192
|
+
* - Tag classification, image analysis, importance scoring
|
|
193
|
+
*/
|
|
194
|
+
declare class AnalysisProvider implements AnalysisProvider$1 {
|
|
195
|
+
private readonly openai;
|
|
196
|
+
private readonly articleRepository;
|
|
197
|
+
private readonly tagRepository;
|
|
198
|
+
classifyTagOptions: {
|
|
199
|
+
model: ReturnType<OpenAIProvider>;
|
|
200
|
+
};
|
|
201
|
+
analyzeImagesOptions: {
|
|
202
|
+
model: ReturnType<OpenAIProvider>;
|
|
203
|
+
};
|
|
204
|
+
determineScoreOptions: {
|
|
205
|
+
model: ReturnType<OpenAIProvider>;
|
|
206
|
+
minimumImportanceScoreRules: Array<{
|
|
207
|
+
targetUrl: string;
|
|
208
|
+
minScore: number;
|
|
209
|
+
}>;
|
|
210
|
+
};
|
|
211
|
+
constructor(openai: OpenAIProvider, articleRepository: ArticleRepository, tagRepository: TagRepository);
|
|
212
|
+
/**
|
|
213
|
+
* Fetch articles that haven't been scored yet
|
|
214
|
+
* @returns Unscored articles awaiting analysis
|
|
215
|
+
*/
|
|
216
|
+
fetchUnscoredArticles(): Promise<UnscoredArticle[]>;
|
|
217
|
+
/**
|
|
218
|
+
* Fetch all existing tags for classification
|
|
219
|
+
* @returns List of tag names
|
|
220
|
+
*/
|
|
221
|
+
fetchTags(): Promise<string[]>;
|
|
222
|
+
/**
|
|
223
|
+
* Update article with analysis results (tags, image analysis, importance score)
|
|
224
|
+
* @param article - Article with analysis data
|
|
225
|
+
*/
|
|
226
|
+
update(article: ArticleForUpdateByAnalysis): Promise<void>;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Content generation provider implementation
|
|
231
|
+
* - LLM-based newsletter content generation
|
|
232
|
+
* - HTML template provisioning
|
|
233
|
+
* - Newsletter persistence
|
|
234
|
+
*/
|
|
235
|
+
declare class ContentGenerateProvider implements ContentGenerateProvider$1 {
|
|
236
|
+
private readonly openai;
|
|
237
|
+
private readonly articleRepository;
|
|
238
|
+
private readonly newsletterRepository;
|
|
239
|
+
private _issueOrder;
|
|
240
|
+
model: ReturnType<OpenAIProvider>;
|
|
241
|
+
constructor(openai: OpenAIProvider, articleRepository: ArticleRepository, newsletterRepository: NewsletterRepository);
|
|
242
|
+
/** LLM temperature setting for content generation */
|
|
243
|
+
temperature: number;
|
|
244
|
+
/** Newsletter brand name */
|
|
245
|
+
newsletterBrandName: string;
|
|
246
|
+
/** Subscribe page URL */
|
|
247
|
+
subscribePageUrl: UrlString;
|
|
248
|
+
/** Publication criteria (minimum article count, priority score threshold) */
|
|
249
|
+
publicationCriteria: {
|
|
250
|
+
minimumArticleCountForIssue: number;
|
|
251
|
+
priorityArticleScoreThreshold: number;
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* Get current issue order
|
|
255
|
+
* @throws Error if issue order not initialized
|
|
256
|
+
*/
|
|
257
|
+
get issueOrder(): number;
|
|
258
|
+
/**
|
|
259
|
+
* Initialize issue order before newsletter generation
|
|
260
|
+
*/
|
|
261
|
+
initializeIssueOrder(): Promise<void>;
|
|
262
|
+
/**
|
|
263
|
+
* Fetch candidate articles for newsletter generation
|
|
264
|
+
* @returns Articles eligible for inclusion in the newsletter
|
|
265
|
+
*/
|
|
266
|
+
fetchArticleCandidates(): Promise<ArticleForGenerateContent[]>;
|
|
267
|
+
/** HTML template with markers for title and content injection */
|
|
268
|
+
htmlTemplate: HtmlTemplate;
|
|
269
|
+
/**
|
|
270
|
+
* Save generated newsletter to the repository
|
|
271
|
+
* @param input - Newsletter data and used articles
|
|
272
|
+
* @returns Saved newsletter ID
|
|
273
|
+
*/
|
|
274
|
+
saveNewsletter(input: {
|
|
275
|
+
newsletter: Newsletter;
|
|
276
|
+
usedArticles: ArticleForGenerateContent[];
|
|
277
|
+
}): Promise<{
|
|
278
|
+
id: string | number;
|
|
279
|
+
}>;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Crawling provider implementation
|
|
284
|
+
* - Defines crawling targets
|
|
285
|
+
* - Saves crawling results
|
|
286
|
+
* - Fetches existing articles
|
|
287
|
+
*/
|
|
288
|
+
declare class CrawlingProvider implements CrawlingProvider$1 {
|
|
289
|
+
private readonly articleRepository;
|
|
290
|
+
/** Maximum number of concurrent crawling operations */
|
|
291
|
+
maxConcurrency: number;
|
|
292
|
+
constructor(articleRepository: ArticleRepository);
|
|
293
|
+
/** Crawling target groups configuration */
|
|
294
|
+
crawlingTargetGroups: CrawlingTargetGroup[];
|
|
295
|
+
/**
|
|
296
|
+
* Fetch existing articles by URLs to avoid duplicate crawling
|
|
297
|
+
* @param articleUrls - URLs to check
|
|
298
|
+
* @returns Existing articles
|
|
299
|
+
*/
|
|
300
|
+
fetchExistingArticlesByUrls(articleUrls: UrlString[]): Promise<ParsedTarget[]>;
|
|
301
|
+
/**
|
|
302
|
+
* Save crawled articles to the repository
|
|
303
|
+
* @param articles - Articles to save
|
|
304
|
+
* @param context - Task context (task ID, target group, target)
|
|
305
|
+
* @returns Number of saved articles
|
|
306
|
+
*/
|
|
307
|
+
saveCrawledArticles<TaskId>(articles: ParsedTarget[], context: {
|
|
308
|
+
taskId: TaskId;
|
|
309
|
+
targetGroup: Omit<CrawlingTargetGroup, 'targets'>;
|
|
310
|
+
target: CrawlingTarget;
|
|
311
|
+
}): Promise<number>;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
declare const crawlingTargetGroups: CrawlingTargetGroup[];
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Newsletter content configuration
|
|
318
|
+
*/
|
|
319
|
+
declare const contentOptions: {
|
|
320
|
+
outputLanguage: string;
|
|
321
|
+
expertField: string[];
|
|
322
|
+
};
|
|
323
|
+
/**
|
|
324
|
+
* Newsletter brand configuration
|
|
325
|
+
*/
|
|
326
|
+
declare const newsletterConfig: {
|
|
327
|
+
brandName: string;
|
|
328
|
+
subscribePageUrl: string;
|
|
329
|
+
publicationCriteria: {
|
|
330
|
+
minimumArticleCountForIssue: number;
|
|
331
|
+
priorityArticleScoreThreshold: number;
|
|
332
|
+
};
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* LLM configuration
|
|
336
|
+
*/
|
|
337
|
+
declare const llmConfig: {
|
|
338
|
+
maxRetries: number;
|
|
339
|
+
chainStopAfterAttempt: number;
|
|
340
|
+
generation: {
|
|
341
|
+
temperature: number;
|
|
342
|
+
};
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
export { AnalysisProvider, ContentGenerateProvider, CrawlingProvider, DateService, TaskService, contentOptions, crawlingTargetGroups, generateNewsletter, llmConfig, newsletterConfig };
|
|
346
|
+
export type { ArticleRepository, NewsletterGeneratorDependencies, NewsletterRepository, PreviewNewsletterOptions, TagRepository, TaskRepository };
|