@crowdlisten/harness 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.
Files changed (109) hide show
  1. package/AGENTS.md +167 -0
  2. package/LICENSE +21 -0
  3. package/README.md +153 -0
  4. package/dist/agent-proxy.d.ts +24 -0
  5. package/dist/agent-proxy.js +140 -0
  6. package/dist/agent-tools.d.ts +736 -0
  7. package/dist/agent-tools.js +409 -0
  8. package/dist/context/api.d.ts +5 -0
  9. package/dist/context/api.js +164 -0
  10. package/dist/context/cli.d.ts +19 -0
  11. package/dist/context/cli.js +108 -0
  12. package/dist/context/extractor.d.ts +12 -0
  13. package/dist/context/extractor.js +43 -0
  14. package/dist/context/index.d.ts +12 -0
  15. package/dist/context/index.js +11 -0
  16. package/dist/context/matcher.d.ts +39 -0
  17. package/dist/context/matcher.js +246 -0
  18. package/dist/context/parser.d.ts +28 -0
  19. package/dist/context/parser.js +157 -0
  20. package/dist/context/pipeline.d.ts +26 -0
  21. package/dist/context/pipeline.js +56 -0
  22. package/dist/context/prompts.d.ts +6 -0
  23. package/dist/context/prompts.js +60 -0
  24. package/dist/context/providers.d.ts +6 -0
  25. package/dist/context/providers.js +106 -0
  26. package/dist/context/redactor.d.ts +10 -0
  27. package/dist/context/redactor.js +68 -0
  28. package/dist/context/server.d.ts +5 -0
  29. package/dist/context/server.js +134 -0
  30. package/dist/context/store.d.ts +12 -0
  31. package/dist/context/store.js +82 -0
  32. package/dist/context/types.d.ts +79 -0
  33. package/dist/context/types.js +4 -0
  34. package/dist/context/user-state.d.ts +40 -0
  35. package/dist/context/user-state.js +144 -0
  36. package/dist/index.d.ts +14 -0
  37. package/dist/index.js +385 -0
  38. package/dist/insights/browser/BrowserPool.d.ts +87 -0
  39. package/dist/insights/browser/BrowserPool.js +266 -0
  40. package/dist/insights/browser/RequestInterceptor.d.ts +46 -0
  41. package/dist/insights/browser/RequestInterceptor.js +115 -0
  42. package/dist/insights/cli.d.ts +8 -0
  43. package/dist/insights/cli.js +206 -0
  44. package/dist/insights/core/base/BaseAdapter.d.ts +37 -0
  45. package/dist/insights/core/base/BaseAdapter.js +123 -0
  46. package/dist/insights/core/health/HealthMonitor.d.ts +75 -0
  47. package/dist/insights/core/health/HealthMonitor.js +171 -0
  48. package/dist/insights/core/interfaces/SocialMediaPlatform.d.ts +125 -0
  49. package/dist/insights/core/interfaces/SocialMediaPlatform.js +42 -0
  50. package/dist/insights/core/utils/DataNormalizer.d.ts +53 -0
  51. package/dist/insights/core/utils/DataNormalizer.js +349 -0
  52. package/dist/insights/core/utils/InstagramUrlUtils.d.ts +11 -0
  53. package/dist/insights/core/utils/InstagramUrlUtils.js +60 -0
  54. package/dist/insights/core/utils/TikTokUrlUtils.d.ts +10 -0
  55. package/dist/insights/core/utils/TikTokUrlUtils.js +57 -0
  56. package/dist/insights/handlers.d.ts +157 -0
  57. package/dist/insights/handlers.js +246 -0
  58. package/dist/insights/index.d.ts +437 -0
  59. package/dist/insights/index.js +426 -0
  60. package/dist/insights/platforms/instagram/InstagramAdapter.d.ts +34 -0
  61. package/dist/insights/platforms/instagram/InstagramAdapter.js +342 -0
  62. package/dist/insights/platforms/moltbook/MoltbookAdapter.d.ts +31 -0
  63. package/dist/insights/platforms/moltbook/MoltbookAdapter.js +227 -0
  64. package/dist/insights/platforms/reddit/RedditAdapter.d.ts +21 -0
  65. package/dist/insights/platforms/reddit/RedditAdapter.js +212 -0
  66. package/dist/insights/platforms/tiktok/TikTokAdapter.d.ts +34 -0
  67. package/dist/insights/platforms/tiktok/TikTokAdapter.js +269 -0
  68. package/dist/insights/platforms/twitter/TwitterAdapter.d.ts +23 -0
  69. package/dist/insights/platforms/twitter/TwitterAdapter.js +211 -0
  70. package/dist/insights/platforms/xiaohongshu/XiaohongshuAdapter.d.ts +35 -0
  71. package/dist/insights/platforms/xiaohongshu/XiaohongshuAdapter.js +258 -0
  72. package/dist/insights/platforms/youtube/YouTubeAdapter.d.ts +22 -0
  73. package/dist/insights/platforms/youtube/YouTubeAdapter.js +254 -0
  74. package/dist/insights/service-config.d.ts +7 -0
  75. package/dist/insights/service-config.js +60 -0
  76. package/dist/insights/services/UnifiedSocialMediaService.d.ts +94 -0
  77. package/dist/insights/services/UnifiedSocialMediaService.js +259 -0
  78. package/dist/insights/vision/VisionExtractor.d.ts +46 -0
  79. package/dist/insights/vision/VisionExtractor.js +236 -0
  80. package/dist/learnings.d.ts +50 -0
  81. package/dist/learnings.js +130 -0
  82. package/dist/openapi.d.ts +29 -0
  83. package/dist/openapi.js +169 -0
  84. package/dist/server-factory.d.ts +20 -0
  85. package/dist/server-factory.js +41 -0
  86. package/dist/suggestions.d.ts +16 -0
  87. package/dist/suggestions.js +72 -0
  88. package/dist/telemetry.d.ts +44 -0
  89. package/dist/telemetry.js +93 -0
  90. package/dist/tools/registry.d.ts +65 -0
  91. package/dist/tools/registry.js +256 -0
  92. package/dist/tools.d.ts +2433 -0
  93. package/dist/tools.js +2294 -0
  94. package/dist/transport/http.d.ts +15 -0
  95. package/dist/transport/http.js +154 -0
  96. package/package.json +76 -0
  97. package/skills/catalog.json +272 -0
  98. package/skills/community-catalog.json +4202 -0
  99. package/skills/competitive-analysis/SKILL.md +174 -0
  100. package/skills/content-creator/SKILL.md +256 -0
  101. package/skills/content-strategy/SKILL.md +222 -0
  102. package/skills/data-storytelling/SKILL.md +248 -0
  103. package/skills/heuristic-evaluation/SKILL.md +201 -0
  104. package/skills/market-research-reports/SKILL.md +184 -0
  105. package/skills/user-stories/SKILL.md +178 -0
  106. package/skills/ux-researcher/SKILL.md +239 -0
  107. package/web-dist/assets/index-B1b25lNd.css +1 -0
  108. package/web-dist/assets/index-CDWHwHbl.js +64 -0
  109. package/web-dist/index.html +16 -0
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Base adapter class providing common functionality for all social media platforms
3
+ * Implements shared features like rate limiting, error handling, and logging
4
+ *
5
+ * Analysis (clustering, enrichment) has been moved to the CrowdListen API.
6
+ * This class now only handles data retrieval.
7
+ */
8
+ import { SocialMediaError, RateLimitError } from '../interfaces/SocialMediaPlatform.js';
9
+ export class BaseAdapter {
10
+ config;
11
+ isInitialized = false;
12
+ lastRequestTime = 0;
13
+ requestCount = 0;
14
+ rateLimitWindow = 60000; // 1 minute window
15
+ maxRequestsPerWindow = 30; // Max 30 requests per minute
16
+ constructor(config) {
17
+ this.config = config;
18
+ }
19
+ async enforceRateLimit() {
20
+ const now = Date.now();
21
+ const timeSinceLastRequest = now - this.lastRequestTime;
22
+ // Reset request count if window has passed
23
+ if (timeSinceLastRequest > this.rateLimitWindow) {
24
+ this.requestCount = 0;
25
+ }
26
+ // If we've hit the rate limit, wait
27
+ if (this.requestCount >= this.maxRequestsPerWindow) {
28
+ const waitTime = this.rateLimitWindow - timeSinceLastRequest;
29
+ if (waitTime > 0) {
30
+ this.log(`Rate limit reached. Waiting ${waitTime}ms...`);
31
+ await this.sleep(waitTime);
32
+ this.requestCount = 0;
33
+ }
34
+ }
35
+ this.requestCount++;
36
+ this.lastRequestTime = Date.now();
37
+ }
38
+ async sleep(ms) {
39
+ return new Promise(resolve => setTimeout(resolve, ms));
40
+ }
41
+ handleError(error, operation) {
42
+ this.log(`Error in ${operation}: ${error.message || error}`, 'error');
43
+ if (this.isRateLimitError(error)) {
44
+ throw new RateLimitError(this.getPlatformName(), error);
45
+ }
46
+ if (this.isAuthError(error)) {
47
+ throw new SocialMediaError(`Authentication failed during ${operation}`, 'authentication_error', this.getPlatformName(), error.statusCode);
48
+ }
49
+ if (this.isNotFoundError(error)) {
50
+ throw new SocialMediaError(`Resource not found during ${operation}`, 'not_found', this.getPlatformName(), error.statusCode);
51
+ }
52
+ throw new SocialMediaError(`Operation ${operation} failed: ${error.message || error}`, 'unknown_error', this.getPlatformName(), error.statusCode);
53
+ }
54
+ isRateLimitError(error) {
55
+ return error.statusCode === 429 || error.code === 'rate_limit_exceeded';
56
+ }
57
+ isAuthError(error) {
58
+ return error.statusCode === 401 || error.statusCode === 403;
59
+ }
60
+ isNotFoundError(error) {
61
+ return error.statusCode === 404;
62
+ }
63
+ log(message, level = 'info') {
64
+ const timestamp = new Date().toISOString();
65
+ const platformName = this.getPlatformName();
66
+ const prefix = `[${timestamp}] [${platformName.toUpperCase()}]`;
67
+ switch (level) {
68
+ case 'error':
69
+ console.error(`${prefix} ERROR: ${message}`);
70
+ break;
71
+ case 'warn':
72
+ console.warn(`${prefix} WARN: ${message}`);
73
+ break;
74
+ default:
75
+ console.log(`${prefix} INFO: ${message}`);
76
+ }
77
+ }
78
+ validateUserId(userId) {
79
+ if (!userId || userId.trim().length === 0) {
80
+ throw new SocialMediaError('User ID cannot be empty', 'validation_error', this.getPlatformName());
81
+ }
82
+ }
83
+ validateContentId(contentId) {
84
+ if (!contentId || contentId.trim().length === 0) {
85
+ throw new SocialMediaError('Content ID cannot be empty', 'validation_error', this.getPlatformName());
86
+ }
87
+ }
88
+ validateLimit(limit) {
89
+ if (limit < 1 || limit > 1000) {
90
+ throw new SocialMediaError('Limit must be between 1 and 1000', 'validation_error', this.getPlatformName());
91
+ }
92
+ }
93
+ async analyzeContent(contentId, enableClustering = true) {
94
+ this.validateContentId(contentId);
95
+ try {
96
+ await this.enforceRateLimit();
97
+ const comments = await this.getContentComments(contentId, 200);
98
+ // Return basic post metadata only — analysis is handled by the CrowdListen API
99
+ const analysis = {
100
+ postId: contentId,
101
+ platform: this.getPlatformName(),
102
+ sentiment: 'neutral',
103
+ themes: ['general'],
104
+ summary: `Retrieved ${comments.length} comments for ${contentId}. Use analyze_content with CROWDLISTEN_API_KEY for full analysis.`,
105
+ commentCount: comments.length,
106
+ topComments: comments.slice(0, 5),
107
+ };
108
+ return analysis;
109
+ }
110
+ catch (error) {
111
+ this.handleError(error, 'analyzeContent');
112
+ }
113
+ }
114
+ ensureInitialized() {
115
+ if (!this.isInitialized) {
116
+ throw new SocialMediaError('Adapter not initialized. Call initialize() first.', 'initialization_error', this.getPlatformName());
117
+ }
118
+ }
119
+ async cleanup() {
120
+ this.isInitialized = false;
121
+ this.log('Adapter cleaned up');
122
+ }
123
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Proactive Health Monitor
3
+ *
4
+ * Periodically checks platform health in the background, tracks response
5
+ * times, consecutive failures, and degradation state. The MCP health_check
6
+ * tool reads cached state from here instead of doing a live probe every
7
+ * time, making it fast and non-blocking.
8
+ *
9
+ * Three-state model: healthy -> degraded -> down
10
+ * - healthy: last check succeeded with results
11
+ * - degraded: last check returned 0 results, or 1-2 consecutive failures
12
+ * - down: 3+ consecutive failures
13
+ */
14
+ import { PlatformType } from '../interfaces/SocialMediaPlatform.js';
15
+ export type HealthStatus = 'healthy' | 'degraded' | 'down';
16
+ export interface PlatformHealthState {
17
+ platform: PlatformType;
18
+ status: HealthStatus;
19
+ lastChecked: Date;
20
+ lastHealthy: Date | null;
21
+ consecutiveFailures: number;
22
+ responseTimeMs: number | null;
23
+ error?: string;
24
+ }
25
+ export interface HealthSummary {
26
+ overall: HealthStatus;
27
+ platforms: Record<string, PlatformHealthState>;
28
+ lastFullCheck: Date | null;
29
+ }
30
+ export type HealthCheckFn = (platform: PlatformType) => Promise<{
31
+ success: boolean;
32
+ resultCount: number;
33
+ }>;
34
+ export declare class HealthMonitor {
35
+ private checkFn;
36
+ private platforms;
37
+ private intervalMs;
38
+ private states;
39
+ private intervalId;
40
+ private startupTimeoutId;
41
+ private lastFullCheck;
42
+ constructor(checkFn: HealthCheckFn, platforms: PlatformType[], intervalMs?: number);
43
+ /**
44
+ * Start background health checks.
45
+ * The first check runs after a short delay so it does not block MCP
46
+ * server startup.
47
+ */
48
+ start(): void;
49
+ /**
50
+ * Stop background health checks and clean up timers.
51
+ */
52
+ stop(): void;
53
+ /**
54
+ * Check all platforms in parallel. One failing platform does not block
55
+ * or affect the others.
56
+ */
57
+ checkAll(): Promise<HealthSummary>;
58
+ /**
59
+ * Check a single platform and update its state.
60
+ */
61
+ checkPlatform(platform: PlatformType): Promise<PlatformHealthState>;
62
+ /**
63
+ * Get a snapshot summary of all platform health states.
64
+ */
65
+ getSummary(): HealthSummary;
66
+ /**
67
+ * Get health state for a single platform. Returns undefined if the
68
+ * platform is not being monitored.
69
+ */
70
+ getState(platform: PlatformType): PlatformHealthState | undefined;
71
+ /**
72
+ * Whether the monitor has recent data (checked within the given ms).
73
+ */
74
+ hasRecentData(withinMs?: number): boolean;
75
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Proactive Health Monitor
3
+ *
4
+ * Periodically checks platform health in the background, tracks response
5
+ * times, consecutive failures, and degradation state. The MCP health_check
6
+ * tool reads cached state from here instead of doing a live probe every
7
+ * time, making it fast and non-blocking.
8
+ *
9
+ * Three-state model: healthy -> degraded -> down
10
+ * - healthy: last check succeeded with results
11
+ * - degraded: last check returned 0 results, or 1-2 consecutive failures
12
+ * - down: 3+ consecutive failures
13
+ */
14
+ /**
15
+ * Default check interval: 5 minutes.
16
+ * Default per-platform timeout: 15 seconds.
17
+ */
18
+ const DEFAULT_INTERVAL_MS = 5 * 60 * 1000;
19
+ const CHECK_TIMEOUT_MS = 15_000;
20
+ const STARTUP_DELAY_MS = 10_000;
21
+ const CONSECUTIVE_FAILURE_THRESHOLD = 3;
22
+ export class HealthMonitor {
23
+ checkFn;
24
+ platforms;
25
+ intervalMs;
26
+ states = new Map();
27
+ intervalId = null;
28
+ startupTimeoutId = null;
29
+ lastFullCheck = null;
30
+ constructor(checkFn, platforms, intervalMs = DEFAULT_INTERVAL_MS) {
31
+ this.checkFn = checkFn;
32
+ this.platforms = platforms;
33
+ this.intervalMs = intervalMs;
34
+ for (const p of platforms) {
35
+ this.states.set(p, {
36
+ platform: p,
37
+ status: 'healthy', // optimistic start
38
+ lastChecked: new Date(0),
39
+ lastHealthy: null,
40
+ consecutiveFailures: 0,
41
+ responseTimeMs: null,
42
+ });
43
+ }
44
+ }
45
+ /**
46
+ * Start background health checks.
47
+ * The first check runs after a short delay so it does not block MCP
48
+ * server startup.
49
+ */
50
+ start() {
51
+ if (this.intervalId)
52
+ return;
53
+ this.startupTimeoutId = setTimeout(() => {
54
+ this.checkAll().catch((err) => console.error('[HealthMonitor] Initial check error:', err));
55
+ }, STARTUP_DELAY_MS);
56
+ this.intervalId = setInterval(() => {
57
+ this.checkAll().catch((err) => console.error('[HealthMonitor] Periodic check error:', err));
58
+ }, this.intervalMs);
59
+ console.error(`[HealthMonitor] Started — checking ${this.platforms.length} platforms every ${Math.round(this.intervalMs / 1000)}s`);
60
+ }
61
+ /**
62
+ * Stop background health checks and clean up timers.
63
+ */
64
+ stop() {
65
+ if (this.startupTimeoutId) {
66
+ clearTimeout(this.startupTimeoutId);
67
+ this.startupTimeoutId = null;
68
+ }
69
+ if (this.intervalId) {
70
+ clearInterval(this.intervalId);
71
+ this.intervalId = null;
72
+ }
73
+ console.error('[HealthMonitor] Stopped');
74
+ }
75
+ /**
76
+ * Check all platforms in parallel. One failing platform does not block
77
+ * or affect the others.
78
+ */
79
+ async checkAll() {
80
+ await Promise.allSettled(this.platforms.map((p) => this.checkPlatform(p)));
81
+ this.lastFullCheck = new Date();
82
+ return this.getSummary();
83
+ }
84
+ /**
85
+ * Check a single platform and update its state.
86
+ */
87
+ async checkPlatform(platform) {
88
+ const state = this.states.get(platform);
89
+ if (!state) {
90
+ throw new Error(`Unknown platform: ${platform}`);
91
+ }
92
+ const start = Date.now();
93
+ try {
94
+ const result = await Promise.race([
95
+ this.checkFn(platform),
96
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Health check timeout')), CHECK_TIMEOUT_MS)),
97
+ ]);
98
+ const elapsed = Date.now() - start;
99
+ if (result.success && result.resultCount > 0) {
100
+ state.status = 'healthy';
101
+ state.lastHealthy = new Date();
102
+ state.consecutiveFailures = 0;
103
+ state.error = undefined;
104
+ }
105
+ else if (result.success && result.resultCount === 0) {
106
+ // Responded but returned nothing — might be a transient issue
107
+ state.consecutiveFailures++;
108
+ state.status =
109
+ state.consecutiveFailures >= CONSECUTIVE_FAILURE_THRESHOLD
110
+ ? 'down'
111
+ : 'degraded';
112
+ }
113
+ else {
114
+ state.consecutiveFailures++;
115
+ state.status =
116
+ state.consecutiveFailures >= CONSECUTIVE_FAILURE_THRESHOLD
117
+ ? 'down'
118
+ : 'degraded';
119
+ }
120
+ state.responseTimeMs = elapsed;
121
+ }
122
+ catch (error) {
123
+ state.consecutiveFailures++;
124
+ state.status =
125
+ state.consecutiveFailures >= CONSECUTIVE_FAILURE_THRESHOLD
126
+ ? 'down'
127
+ : 'degraded';
128
+ state.error =
129
+ error instanceof Error ? error.message : String(error);
130
+ state.responseTimeMs = Date.now() - start;
131
+ }
132
+ state.lastChecked = new Date();
133
+ return { ...state };
134
+ }
135
+ /**
136
+ * Get a snapshot summary of all platform health states.
137
+ */
138
+ getSummary() {
139
+ const platforms = {};
140
+ let hasDown = false;
141
+ let hasDegraded = false;
142
+ for (const [key, state] of this.states) {
143
+ platforms[key] = { ...state };
144
+ if (state.status === 'down')
145
+ hasDown = true;
146
+ if (state.status === 'degraded')
147
+ hasDegraded = true;
148
+ }
149
+ return {
150
+ overall: hasDown ? 'down' : hasDegraded ? 'degraded' : 'healthy',
151
+ platforms,
152
+ lastFullCheck: this.lastFullCheck,
153
+ };
154
+ }
155
+ /**
156
+ * Get health state for a single platform. Returns undefined if the
157
+ * platform is not being monitored.
158
+ */
159
+ getState(platform) {
160
+ const state = this.states.get(platform);
161
+ return state ? { ...state } : undefined;
162
+ }
163
+ /**
164
+ * Whether the monitor has recent data (checked within the given ms).
165
+ */
166
+ hasRecentData(withinMs = DEFAULT_INTERVAL_MS) {
167
+ if (!this.lastFullCheck)
168
+ return false;
169
+ return Date.now() - this.lastFullCheck.getTime() < withinMs;
170
+ }
171
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Core interface that all social media platform adapters must implement
3
+ * Provides a unified API for content retrieval across different platforms
4
+ *
5
+ * Analysis types (EnrichedComment, OpinionUnit, etc.) have been moved to
6
+ * the CrowdListen API. This file now only contains retrieval types.
7
+ */
8
+ export interface Post {
9
+ id: string;
10
+ platform: PlatformType;
11
+ author: User;
12
+ content: string;
13
+ mediaUrl?: string;
14
+ engagement: EngagementMetrics;
15
+ timestamp: Date;
16
+ url: string;
17
+ hashtags?: string[];
18
+ }
19
+ export interface User {
20
+ id: string;
21
+ username: string;
22
+ displayName?: string;
23
+ followerCount?: number;
24
+ verified?: boolean;
25
+ profileImageUrl?: string;
26
+ bio?: string;
27
+ }
28
+ export interface EngagementMetrics {
29
+ likes: number;
30
+ comments: number;
31
+ shares?: number;
32
+ views?: number;
33
+ engagementRate?: number;
34
+ }
35
+ export interface Comment {
36
+ id: string;
37
+ author: User;
38
+ text: string;
39
+ timestamp: Date;
40
+ likes: number;
41
+ replies?: Comment[];
42
+ engagement?: {
43
+ upvotes?: number;
44
+ downvotes?: number;
45
+ shares?: number;
46
+ views?: number;
47
+ score?: number;
48
+ };
49
+ }
50
+ export interface ContentAnalysis {
51
+ postId: string;
52
+ platform: PlatformType;
53
+ sentiment?: 'positive' | 'negative' | 'neutral';
54
+ themes?: string[];
55
+ summary?: string;
56
+ commentCount: number;
57
+ topComments: Comment[];
58
+ analysisMetadata?: Record<string, unknown>;
59
+ }
60
+ export interface CommentCluster {
61
+ id: number;
62
+ theme: string;
63
+ sentiment: 'positive' | 'negative' | 'neutral' | 'mixed';
64
+ comments: Comment[];
65
+ summary: string;
66
+ size: number;
67
+ }
68
+ export interface TrendingHashtag {
69
+ hashtag: string;
70
+ postCount: number;
71
+ engagementScore: number;
72
+ }
73
+ export type PlatformType = 'tiktok' | 'twitter' | 'reddit' | 'instagram' | 'youtube' | 'moltbook' | 'xiaohongshu';
74
+ export interface PlatformCapabilities {
75
+ supportsTrending: boolean;
76
+ supportsUserContent: boolean;
77
+ supportsSearch: boolean;
78
+ supportsComments: boolean;
79
+ supportsAnalysis: boolean;
80
+ }
81
+ /**
82
+ * Main interface that all platform adapters must implement
83
+ */
84
+ export interface SocialMediaPlatform {
85
+ getTrendingContent(limit?: number): Promise<Post[]>;
86
+ getUserContent(userId: string, limit?: number): Promise<Post[]>;
87
+ searchContent(query: string, limit?: number): Promise<Post[]>;
88
+ getContentComments(contentId: string, limit?: number): Promise<Comment[]>;
89
+ analyzeContent(contentId: string, enableClustering?: boolean): Promise<ContentAnalysis>;
90
+ getPlatformName(): PlatformType;
91
+ getSupportedFeatures(): PlatformCapabilities;
92
+ initialize(): Promise<boolean>;
93
+ cleanup(): Promise<void>;
94
+ }
95
+ /**
96
+ * Configuration interface for platform adapters
97
+ */
98
+ export interface PlatformConfig {
99
+ platform: PlatformType;
100
+ credentials?: Record<string, string>;
101
+ options?: {
102
+ rateLimit?: number;
103
+ timeout?: number;
104
+ retries?: number;
105
+ };
106
+ }
107
+ /**
108
+ * Error types for unified error handling
109
+ */
110
+ export declare class SocialMediaError extends Error {
111
+ code: string;
112
+ platform: PlatformType;
113
+ statusCode?: number | undefined;
114
+ originalError?: Error | undefined;
115
+ constructor(message: string, code: string, platform: PlatformType, statusCode?: number | undefined, originalError?: Error | undefined);
116
+ }
117
+ export declare class AuthenticationError extends SocialMediaError {
118
+ constructor(platform: PlatformType, originalError?: Error);
119
+ }
120
+ export declare class RateLimitError extends SocialMediaError {
121
+ constructor(platform: PlatformType, originalError?: Error);
122
+ }
123
+ export declare class NotFoundError extends SocialMediaError {
124
+ constructor(platform: PlatformType, resource: string, originalError?: Error);
125
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Core interface that all social media platform adapters must implement
3
+ * Provides a unified API for content retrieval across different platforms
4
+ *
5
+ * Analysis types (EnrichedComment, OpinionUnit, etc.) have been moved to
6
+ * the CrowdListen API. This file now only contains retrieval types.
7
+ */
8
+ /**
9
+ * Error types for unified error handling
10
+ */
11
+ export class SocialMediaError extends Error {
12
+ code;
13
+ platform;
14
+ statusCode;
15
+ originalError;
16
+ constructor(message, code, platform, statusCode, originalError) {
17
+ super(message);
18
+ this.code = code;
19
+ this.platform = platform;
20
+ this.statusCode = statusCode;
21
+ this.originalError = originalError;
22
+ this.name = 'SocialMediaError';
23
+ }
24
+ }
25
+ export class AuthenticationError extends SocialMediaError {
26
+ constructor(platform, originalError) {
27
+ super(`Authentication failed for ${platform}`, 'AUTH_ERROR', platform, undefined, originalError);
28
+ this.name = 'AuthenticationError';
29
+ }
30
+ }
31
+ export class RateLimitError extends SocialMediaError {
32
+ constructor(platform, originalError) {
33
+ super(`Rate limit exceeded for ${platform}`, 'RATE_LIMIT', platform, undefined, originalError);
34
+ this.name = 'RateLimitError';
35
+ }
36
+ }
37
+ export class NotFoundError extends SocialMediaError {
38
+ constructor(platform, resource, originalError) {
39
+ super(`${resource} not found on ${platform}`, 'NOT_FOUND', platform, undefined, originalError);
40
+ this.name = 'NotFoundError';
41
+ }
42
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Data normalization utilities for converting platform-specific data
3
+ * into standardized formats
4
+ */
5
+ import { Post, User, Comment, PlatformType } from '../interfaces/SocialMediaPlatform.js';
6
+ /**
7
+ * Normalize user data from different platforms
8
+ */
9
+ export declare class DataNormalizer {
10
+ /**
11
+ * Normalize a post from any platform to standard format
12
+ */
13
+ static normalizePost(rawData: any, platform: PlatformType, baseUrl?: string): Post;
14
+ /**
15
+ * Normalize user data from any platform
16
+ */
17
+ static normalizeUser(rawData: any, platform: PlatformType): User;
18
+ /**
19
+ * Normalize comment data from any platform
20
+ */
21
+ static normalizeComment(rawData: any, platform: PlatformType): Comment;
22
+ private static normalizeTikTokPost;
23
+ private static normalizeTikTokUser;
24
+ private static normalizeTikTokComment;
25
+ private static normalizeTwitterPost;
26
+ private static normalizeTwitterUser;
27
+ private static normalizeTwitterComment;
28
+ private static normalizeRedditPost;
29
+ private static normalizeRedditUser;
30
+ private static normalizeRedditComment;
31
+ private static normalizeInstagramPost;
32
+ private static normalizeInstagramUser;
33
+ private static normalizeInstagramComment;
34
+ private static normalizeYouTubePost;
35
+ private static normalizeYouTubeUser;
36
+ private static normalizeYouTubeComment;
37
+ /**
38
+ * Extract hashtags from text
39
+ */
40
+ private static extractHashtags;
41
+ /**
42
+ * Calculate engagement rate
43
+ */
44
+ static calculateEngagementRate(likes: number, comments: number, shares: number | undefined, followerCount: number): number;
45
+ /**
46
+ * Sanitize and validate text content
47
+ */
48
+ static sanitizeText(text: string): string;
49
+ /**
50
+ * Normalize timestamp from various formats
51
+ */
52
+ static normalizeTimestamp(timestamp: any): Date;
53
+ }