Add FeedOrchestrator that coordinates fetch→parse→dedup→store pipeline: - FeedSource type for managing RSS/Atom feed configurations - Feed source CRUD operations in IStorage interface - Database schema migration for feed_sources table - Exponential backoff retry with configurable delays - Per-feed poll intervals with health tracking - Concurrency-limited parallel feed processing - ProcessResult and FeedHealth interfaces for status monitoring Files added: - orchestrator/orchestrator.ts - main orchestrator class - orchestrator/scheduler.ts - backoff calculation utilities - orchestrator/index.ts - module exports - orchestrator/orchestrator.test.ts - comprehensive test suite Files modified: - interfaces/feed.types.ts - add FeedSource type - interfaces/storage.interface.ts - extend with feed source methods - infrastructure/db/database.ts - add FeedSourceTable interface - infrastructure/db/schema.ts - add feed_sources table migration - modules/storage/storage.ts - implement feed source CRUD - modules/storage/storage.test.ts - add feed source tests
50 lines
1.6 KiB
TypeScript
50 lines
1.6 KiB
TypeScript
import type { IFormatter, FormatterError, OutputFormat } from '../../interfaces/formatter.interface.js';
|
|
import type { FeedItem } from '../../interfaces/feed.types.js';
|
|
import { TerminalFormatter } from './terminal.formatter.js';
|
|
import { JsonFormatter } from './json.formatter.js';
|
|
|
|
export class Formatter implements IFormatter {
|
|
private terminalFormatter: TerminalFormatter;
|
|
private jsonFormatter: JsonFormatter;
|
|
|
|
constructor() {
|
|
this.terminalFormatter = new TerminalFormatter();
|
|
this.jsonFormatter = new JsonFormatter();
|
|
}
|
|
|
|
async format(items: FeedItem[], format: OutputFormat): Promise<string> {
|
|
try {
|
|
switch (format) {
|
|
case 'terminal':
|
|
return this.terminalFormatter.format(items);
|
|
case 'json':
|
|
return this.jsonFormatter.format(items);
|
|
case 'html':
|
|
// HTML not implemented yet per requirements
|
|
throw this.createError('UNKNOWN', 'HTML format not implemented');
|
|
default:
|
|
throw this.createError('SERIALIZE_ERROR', `Unknown format: ${format}`);
|
|
}
|
|
} catch (error) {
|
|
if (this.isFormatterError(error)) {
|
|
throw error;
|
|
}
|
|
throw this.createError('UNKNOWN', error instanceof Error ? error.message : 'Unknown error during formatting');
|
|
}
|
|
}
|
|
|
|
private createError(code: FormatterError['code'], message: string): FormatterError {
|
|
return { code, message };
|
|
}
|
|
|
|
private isFormatterError(error: unknown): error is FormatterError {
|
|
return (
|
|
typeof error === 'object' &&
|
|
error !== null &&
|
|
'code' in error &&
|
|
'message' in error &&
|
|
(error as FormatterError).code !== undefined
|
|
);
|
|
}
|
|
}
|