@dracoonghost/trndup-sdk 1.3.22 → 1.3.23

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/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../client.ts","../modules/auth.ts","../modules/youtube.ts","../modules/instagram.ts","../modules/social.ts","../modules/insights.ts","../modules/activity.ts","../types.ts","../index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * TrndUp SDK - Base Client\n * \n * Core HTTP client with Firebase authentication support,\n * error handling, and automatic retries.\n */\n\nimport type { ApiResponse, ApiErrorResponse, ApiSuccessResponse } from './types';\n\n// =============================================================================\n// CONFIGURATION\n// =============================================================================\n\nexport interface TrndUpClientConfig {\n /** Base URL for the API (e.g., 'https://api.trndup.app' or 'http://localhost:3000') */\n baseUrl: string;\n \n /** Function to get the current Firebase ID token */\n getToken: () => string | null | Promise<string | null>;\n \n /** Called when auth completely fails (user needs to re-login) */\n onAuthFailure?: () => void | Promise<void>;\n \n /** Called on any API error */\n onError?: (error: ApiErrorResponse, endpoint: string) => void;\n \n /** Custom headers to include in all requests */\n defaultHeaders?: Record<string, string>;\n \n /** Request timeout in milliseconds (default: 30000) */\n timeout?: number;\n \n /** Enable debug logging (default: false) */\n debug?: boolean;\n}\n\nexport interface RequestOptions {\n /** Skip authentication for this request */\n skipAuth?: boolean;\n \n /** Additional headers for this request */\n headers?: Record<string, string>;\n \n /** AbortController signal for cancellation */\n signal?: AbortSignal;\n \n /** Custom timeout for this request */\n timeout?: number;\n}\n\ninterface RequestConfig extends RequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n path: string;\n params?: Record<string, string | number | boolean | undefined>;\n body?: unknown;\n}\n\n// =============================================================================\n// ERROR CLASSES\n// =============================================================================\n\nexport class TrndUpApiError extends Error {\n constructor(\n public readonly response: ApiErrorResponse,\n public readonly status: number,\n public readonly endpoint: string\n ) {\n super(response.error);\n this.name = 'TrndUpApiError';\n }\n \n get code(): string | undefined {\n return this.response.code;\n }\n\n get metadata(): Record<string, unknown> | undefined {\n return this.response.metadata;\n }\n\n /** Check if error is due to authentication failure */\n isAuthError(): boolean {\n return this.status === 401 || this.code === 'AUTH_EXPIRED' || this.code === 'AUTH_INVALID';\n }\n\n /** Check if error is due to rate limiting */\n isRateLimitError(): boolean {\n return this.status === 429 || this.code === 'RATE_LIMITED';\n }\n}\n\nexport class TrndUpNetworkError extends Error {\n constructor(\n public readonly originalError: Error,\n public readonly endpoint: string\n ) {\n super(`Network error calling ${endpoint}: ${originalError.message}`);\n this.name = 'TrndUpNetworkError';\n }\n}\n\n// =============================================================================\n// BASE CLIENT\n// =============================================================================\n\nexport class TrndUpClient {\n private config: Required<Omit<TrndUpClientConfig, 'onAuthFailure' | 'onError' | 'defaultHeaders' | 'debug'>> & {\n onAuthFailure?: TrndUpClientConfig['onAuthFailure'];\n onError?: TrndUpClientConfig['onError'];\n defaultHeaders?: Record<string, string>;\n debug?: boolean;\n };\n\n // Module instances (imported by subclasses or external modules)\n public auth!: any;\n public youtube!: any;\n public instagram!: any;\n public social!: any;\n public insights!: any;\n\n constructor(config: TrndUpClientConfig) {\n this.config = {\n baseUrl: config.baseUrl.replace(/\\/$/, ''), // Remove trailing slash\n getToken: config.getToken,\n timeout: config.timeout ?? 30000,\n onAuthFailure: config.onAuthFailure,\n onError: config.onError,\n defaultHeaders: config.defaultHeaders,\n debug: config.debug ?? false,\n };\n }\n\n // =============================================================================\n // PUBLIC REQUEST METHODS\n // =============================================================================\n\n async get<T = unknown>(\n path: string,\n params?: Record<string, string | number | boolean | undefined>,\n options?: RequestOptions\n ): Promise<T> {\n return this.request<T>({ method: 'GET', path, params, ...options });\n }\n\n async post<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<T> {\n return this.request<T>({ method: 'POST', path, body, ...options });\n }\n\n async put<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<T> {\n return this.request<T>({ method: 'PUT', path, body, ...options });\n }\n\n async patch<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<T> {\n return this.request<T>({ method: 'PATCH', path, body, ...options });\n }\n\n async delete<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<T> {\n return this.request<T>({ method: 'DELETE', path, ...options });\n }\n\n // =============================================================================\n // CORE REQUEST LOGIC\n // =============================================================================\n\n private async request<T>(config: RequestConfig): Promise<T> {\n const { method, path, params, body, skipAuth, headers, signal, timeout } = config;\n\n // Build URL\n const url = this.buildUrl(path, params);\n\n // Build headers\n const requestHeaders = await this.buildHeaders(skipAuth, headers);\n\n // Build request init\n const init: RequestInit = {\n method,\n headers: requestHeaders,\n signal,\n };\n\n if (body && method !== 'GET') {\n init.body = JSON.stringify(body);\n }\n\n // Add timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n timeout ?? this.config.timeout\n );\n\n try {\n if (this.config.debug) {\n console.log(`[TrndUp SDK] ${method} ${url}`, { body, headers: requestHeaders });\n }\n\n const response = await fetch(url, {\n ...init,\n signal: signal ?? controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n return await this.handleResponse<T>(response, path);\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof TrndUpApiError) {\n throw error;\n }\n\n // Network error\n const networkError = new TrndUpNetworkError(\n error as Error,\n path\n );\n\n if (this.config.debug) {\n console.error('[TrndUp SDK] Network error:', networkError);\n }\n\n throw networkError;\n }\n }\n\n private buildUrl(path: string, params?: Record<string, string | number | boolean | undefined>): string {\n const url = new URL(`${this.config.baseUrl}${path}`);\n\n if (params) {\n Object.entries(params).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n url.searchParams.append(key, String(value));\n }\n });\n }\n\n return url.toString();\n }\n\n private async buildHeaders(\n skipAuth?: boolean,\n additionalHeaders?: Record<string, string>\n ): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this.config.defaultHeaders,\n ...additionalHeaders,\n };\n\n // Add Firebase ID token for authentication\n if (!skipAuth) {\n const token = await this.config.getToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n }\n\n return headers;\n }\n\n private async handleResponse<T>(response: Response, endpoint: string): Promise<T> {\n const contentType = response.headers.get('content-type');\n const isJson = contentType?.includes('application/json');\n\n if (!response.ok) {\n // Try to parse error response\n let errorResponse: ApiErrorResponse;\n\n if (isJson) {\n try {\n const parsed = await response.json();\n errorResponse = parsed as ApiErrorResponse;\n } catch {\n errorResponse = {\n success: false,\n error: response.statusText || 'Unknown error',\n code: `HTTP_${response.status}`,\n };\n }\n } else {\n const text = await response.text();\n errorResponse = {\n success: false,\n error: text || response.statusText || 'Unknown error',\n code: `HTTP_${response.status}`,\n };\n }\n\n const apiError = new TrndUpApiError(errorResponse, response.status, endpoint);\n\n // Call error handler\n if (this.config.onError) {\n this.config.onError(errorResponse, endpoint);\n }\n\n // Call auth failure handler for 401 errors\n if (apiError.isAuthError() && this.config.onAuthFailure) {\n await this.config.onAuthFailure();\n }\n\n if (this.config.debug) {\n console.error('[TrndUp SDK] API error:', apiError);\n }\n\n throw apiError;\n }\n\n // Parse success response\n if (isJson) {\n const parsed = await response.json();\n const jsonResponse = parsed as ApiResponse<T>;\n\n if (jsonResponse.success === false) {\n // This shouldn't happen with 2xx status, but handle it\n throw new TrndUpApiError(jsonResponse, response.status, endpoint);\n }\n\n return (jsonResponse as ApiSuccessResponse<T>).data;\n }\n\n // Non-JSON response\n return (await response.text()) as unknown as T;\n }\n}\n","/**\n * TrndUp SDK - Auth Module\n * \n * Firebase authentication methods with Twilio phone OTP\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { Auth } from '../types';\n\nexport class AuthModule {\n constructor(private client: TrndUpClient) {}\n\n // =========================================================================\n // PHONE OTP AUTHENTICATION (via Twilio - no reCAPTCHA needed)\n // =========================================================================\n\n /**\n * Send OTP verification code to phone number\n * POST /auth/phone/send-otp\n * \n * @param phoneNumber - Phone number (E.164 format recommended, e.g., +14155551234)\n */\n async sendOTP(phoneNumber: string): Promise<Auth.SendOTPResponse> {\n return this.client.post<Auth.SendOTPResponse>(\n '/auth/phone/send-otp', \n { phoneNumber }, \n { skipAuth: true }\n );\n }\n\n /**\n * Verify OTP and get Firebase custom token\n * POST /auth/phone/verify-otp\n * \n * After calling this, use Firebase signInWithCustomToken(customToken)\n * to complete authentication on the client.\n * \n * @param phoneNumber - Phone number that received the OTP\n * @param code - 6-digit verification code\n */\n async verifyOTP(phoneNumber: string, code: string): Promise<Auth.VerifyOTPResponse> {\n return this.client.post<Auth.VerifyOTPResponse>(\n '/auth/phone/verify-otp',\n { phoneNumber, code },\n { skipAuth: true }\n );\n }\n\n // =========================================================================\n // FIREBASE ID TOKEN AUTHENTICATION\n // =========================================================================\n\n /**\n * Login with Firebase ID token (for Google, Apple, Email/Password)\n * POST /auth/login\n */\n async login(idToken: string): Promise<Auth.LoginResponse> {\n return this.client.post<Auth.LoginResponse>('/auth/login', { idToken }, { skipAuth: true });\n }\n\n /**\n * Get current authenticated user's profile\n * GET /auth/me\n */\n async getCurrentUser(): Promise<Auth.CurrentUserResponse> {\n return this.client.get<Auth.CurrentUserResponse>('/auth/me');\n }\n\n /**\n * Update user profile\n * PATCH /auth/me\n */\n async updateProfile(data: Auth.UpdateProfileRequest): Promise<Auth.UpdateProfileResponse> {\n return this.client.patch<Auth.UpdateProfileResponse>('/auth/me', data);\n }\n\n /**\n * Logout (acknowledge logout on backend)\n * POST /auth/logout\n */\n async logout(): Promise<{ message: string }> {\n return this.client.post<{ message: string }>('/auth/logout');\n }\n\n /**\n * Check auth service status\n * GET /auth/status\n */\n async getStatus(): Promise<{ success: boolean; message: string }> {\n return this.client.get('/auth/status', undefined, { skipAuth: true });\n }\n\n /**\n * Get user's platform connection status\n * GET /user/platforms/status\n */\n async getPlatformStatus(): Promise<Auth.PlatformStatus> {\n return this.client.get<Auth.PlatformStatus>('/user/platforms/status');\n }\n\n // =========================================================================\n // OAUTH FLOWS\n // =========================================================================\n\n /**\n * Get Facebook OAuth URL for connecting Instagram Business accounts\n * GET /auth/facebook\n * \n * After receiving the authUrl, redirect/open this URL in a browser.\n * User will authenticate with Facebook and grant Instagram permissions.\n * After completion, user is redirected to the specified redirect_uri.\n * \n * @param redirectUri - URL to redirect to after OAuth (your app's deep link)\n * @returns OAuth authorization URL to redirect user to\n */\n async getFacebookOAuthUrl(redirectUri?: string): Promise<Auth.OAuthUrlResponse> {\n const params = redirectUri ? { redirect_uri: redirectUri } : undefined;\n return this.client.get<Auth.OAuthUrlResponse>('/auth/facebook', params);\n }\n\n /**\n * Get Instagram OAuth URL for connecting Instagram Professional accounts\n * GET /auth/instagram\n * \n * After receiving the authUrl, redirect/open this URL in a browser.\n * User will authenticate with Instagram and grant permissions.\n * After completion, user is redirected to the specified redirect_uri.\n * \n * @param redirectUri - URL to redirect to after OAuth (your app's deep link)\n * @returns OAuth authorization URL to redirect user to\n */\n async getInstagramOAuthUrl(redirectUri?: string): Promise<Auth.OAuthUrlResponse> {\n const params = redirectUri ? { redirect_uri: redirectUri } : undefined;\n return this.client.get<Auth.OAuthUrlResponse>('/auth/instagram', params);\n }\n\n /**\n * Build OAuth URL for Facebook (Instagram Business)\n * Convenience method that returns just the URL string\n * \n * @param redirectUri - URL to redirect to after OAuth\n */\n async buildFacebookOAuthUrl(redirectUri?: string): Promise<string> {\n const response = await this.getFacebookOAuthUrl(redirectUri);\n return response.authUrl;\n }\n\n /**\n * Build OAuth URL for Instagram\n * Convenience method that returns just the URL string\n * \n * @param redirectUri - URL to redirect to after OAuth\n */\n async buildInstagramOAuthUrl(redirectUri?: string): Promise<string> {\n const response = await this.getInstagramOAuthUrl(redirectUri);\n return response.authUrl;\n }\n}\n","/**\n * TrndUp SDK - YouTube Module\n * \n * YouTube analytics and data methods\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { YouTube } from '../types';\n\nexport class YouTubeModule {\n constructor(private client: TrndUpClient) {}\n\n /**\n * Get YouTube initialization status (channel sync)\n * Check if user needs to init or has already synced channel\n * \n * GET /v1/platforms/youtube/init/status\n */\n async getInitStatus(): Promise<YouTube.InitStatusResponse> {\n return this.client.get<YouTube.InitStatusResponse>('/v1/platforms/youtube/init/status');\n }\n\n /**\n * Initialize YouTube channel sync\n * Fetches channel info and stores in database\n * \n * POST /v1/platforms/youtube/init\n */\n async initialize(): Promise<YouTube.InitResponse> {\n return this.client.post<YouTube.InitResponse>('/v1/platforms/youtube/init');\n }\n\n /**\n * Get videos sync status\n * Check if videos need to be synced\n * \n * GET /v1/platforms/youtube/videos/sync/status\n */\n async getVideosSyncStatus(): Promise<YouTube.VideosSyncStatusResponse> {\n return this.client.get<YouTube.VideosSyncStatusResponse>('/v1/platforms/youtube/videos/sync/status');\n }\n\n /**\n * Sync all videos from YouTube\n * Fetches all videos and stores in database\n * Returns video counts and oldest video date (for analytics start date)\n * \n * POST /v1/platforms/youtube/videos/sync\n */\n async syncVideos(): Promise<YouTube.VideosSyncResponse> {\n return this.client.post<YouTube.VideosSyncResponse>('/v1/platforms/youtube/videos/sync');\n }\n\n /**\n * Get YouTube videos from database\n * GET /v1/platforms/youtube/videos\n */\n async getVideos(params?: YouTube.GetVideosParams): Promise<YouTube.GetVideosResponse> {\n return this.client.get<YouTube.GetVideosResponse>(\n '/v1/platforms/youtube/videos', \n params as Record<string, string | number | boolean | undefined>\n );\n }\n\n /**\n * Get specific video by ID\n * GET /v1/platforms/youtube/videos/:videoId\n */\n async getVideo(videoId: string): Promise<YouTube.Video> {\n return this.client.get<YouTube.Video>(`/v1/platforms/youtube/videos/${videoId}`);\n }\n\n /**\n * Get channel metrics\n * GET /v1/platforms/youtube/channel/metrics\n */\n async getChannelMetrics(): Promise<YouTube.ChannelMetrics> {\n return this.client.get<YouTube.ChannelMetrics>('/v1/platforms/youtube/channel/metrics');\n }\n\n /**\n * Get channel health score\n * GET /v1/platforms/youtube/health\n */\n async getHealthScore(params?: YouTube.GetHealthScoreParams): Promise<YouTube.HealthScore> {\n return this.client.get<YouTube.HealthScore>(\n '/v1/platforms/youtube/health', \n params as Record<string, string | number | boolean | undefined>\n );\n }\n\n /**\n * Refresh YouTube data\n * POST /v1/platforms/youtube/refresh\n */\n async refresh(): Promise<{ message: string; jobId?: string }> {\n return this.client.post<{ message: string; jobId?: string }>('/v1/platforms/youtube/refresh');\n }\n\n // =========================================================================\n // UNIFIED SYNC STATUS\n // =========================================================================\n\n /**\n * Get unified sync status for all YouTube data types.\n * \n * Use this for:\n * - Checking if UI cache needs refresh (compare lastSyncAt)\n * - Showing sync status in settings/debug screens\n * - Detecting if background sync is running\n * \n * GET /v1/platforms/youtube/sync/status\n * \n * @example\n * ```typescript\n * const status = await client.youtube.getUnifiedSyncStatus();\n * \n * // Check if backend synced since our last fetch\n * const storedLastSync = await AsyncStorage.getItem('videosLastSync');\n * if (status.syncs.videos.lastSyncAt > storedLastSync) {\n * const videos = await client.youtube.getVideos();\n * await AsyncStorage.setItem('videosLastSync', status.syncs.videos.lastSyncAt);\n * }\n * ```\n */\n async getUnifiedSyncStatus(): Promise<YouTube.UnifiedSyncStatusResponse> {\n return this.client.get<YouTube.UnifiedSyncStatusResponse>(\n '/v1/platforms/youtube/sync/status'\n );\n }\n\n // =========================================================================\n // CHANNEL ANALYTICS SYNC\n // =========================================================================\n\n /**\n * Get channel analytics sync status\n * GET /v1/platforms/youtube/analytics/channel/sync/status\n */\n async getChannelAnalyticsSyncStatus(): Promise<YouTube.ChannelAnalyticsSyncStatusResponse> {\n return this.client.get<YouTube.ChannelAnalyticsSyncStatusResponse>(\n '/v1/platforms/youtube/analytics/channel/sync/status'\n );\n }\n\n /**\n * Sync channel analytics (daily + weekly aggregates)\n * Fetches from YouTube Analytics API and stores in database\n * Uses oldest video date as start date\n * \n * POST /v1/platforms/youtube/analytics/channel/sync\n */\n async syncChannelAnalytics(): Promise<YouTube.ChannelAnalyticsSyncResponse> {\n return this.client.post<YouTube.ChannelAnalyticsSyncResponse>(\n '/v1/platforms/youtube/analytics/channel/sync'\n );\n }\n\n // =========================================================================\n // VIDEO ANALYTICS SYNC\n // =========================================================================\n\n /**\n * Get video analytics sync status\n * GET /v1/platforms/youtube/analytics/videos/sync/status\n */\n async getVideoAnalyticsSyncStatus(): Promise<YouTube.VideoAnalyticsSyncStatusResponse> {\n return this.client.get<YouTube.VideoAnalyticsSyncStatusResponse>(\n '/v1/platforms/youtube/analytics/videos/sync/status'\n );\n }\n\n /**\n * Sync analytics for all videos\n * Fetches from YouTube Analytics API and updates video records\n * \n * POST /v1/platforms/youtube/analytics/videos/sync\n */\n async syncVideoAnalytics(): Promise<YouTube.VideoAnalyticsSyncResponse> {\n return this.client.post<YouTube.VideoAnalyticsSyncResponse>(\n '/v1/platforms/youtube/analytics/videos/sync'\n );\n }\n}\n","/**\n * TrndUp SDK - Instagram Module\n * \n * Instagram analytics and data methods\n * Route structure mirrors YouTube for UI consistency\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { Instagram } from '../types';\n\nexport class InstagramModule {\n constructor(private client: TrndUpClient) {}\n\n // ===== UNIFIED SYNC STATUS =====\n\n /**\n * Get unified sync status for all Instagram data types\n * GET /v1/platforms/instagram/sync/status\n * \n * Shows when each type was last synced and when next sync is due.\n */\n async getSyncStatus(): Promise<Instagram.UnifiedSyncStatusResponse> {\n return this.client.get<Instagram.UnifiedSyncStatusResponse>('/v1/platforms/instagram/sync/status');\n }\n\n // ===== INITIALIZATION =====\n\n /**\n * Initialize Instagram data sync\n * POST /v1/platforms/instagram/init\n */\n async initialize(): Promise<Instagram.InitResponse> {\n return this.client.post<Instagram.InitResponse>('/v1/platforms/instagram/init');\n }\n\n /**\n * Get Instagram initialization status\n * GET /v1/platforms/instagram/init/status\n */\n async getInitStatus(): Promise<Instagram.InitStatusResponse> {\n return this.client.get<Instagram.InitStatusResponse>('/v1/platforms/instagram/init/status');\n }\n\n // ===== POSTS SYNC =====\n\n /**\n * Sync all posts from Instagram to database\n * POST /v1/platforms/instagram/posts/sync\n */\n async syncPosts(): Promise<Instagram.PostsSyncResponse> {\n return this.client.post<Instagram.PostsSyncResponse>('/v1/platforms/instagram/posts/sync');\n }\n\n /**\n * Check posts sync status without triggering a sync\n * GET /v1/platforms/instagram/posts/sync/status\n */\n async getPostsSyncStatus(): Promise<Instagram.PostsSyncStatusResponse> {\n return this.client.get<Instagram.PostsSyncStatusResponse>('/v1/platforms/instagram/posts/sync/status');\n }\n\n // ===== ACCOUNT ANALYTICS SYNC =====\n\n /**\n * Sync account-level analytics (reach, impressions, engagement)\n * POST /v1/platforms/instagram/analytics/account/sync\n * @param range Time range: '7d', '14d', '30d' (default '30d')\n */\n async syncAccountAnalytics(params?: { range?: string }): Promise<Instagram.AccountAnalyticsSyncResponse> {\n const queryParams = params?.range ? `?range=${params.range}` : '';\n return this.client.post<Instagram.AccountAnalyticsSyncResponse>(\n `/v1/platforms/instagram/analytics/account/sync${queryParams}`\n );\n }\n\n /**\n * Get account analytics sync status\n * GET /v1/platforms/instagram/analytics/account/sync/status\n */\n async getAccountAnalyticsSyncStatus(): Promise<Instagram.AccountAnalyticsSyncStatusResponse> {\n return this.client.get<Instagram.AccountAnalyticsSyncStatusResponse>(\n '/v1/platforms/instagram/analytics/account/sync/status'\n );\n }\n\n // ===== POST INSIGHTS SYNC =====\n\n /**\n * Sync post-level insights for all posts\n * POST /v1/platforms/instagram/analytics/posts/sync\n */\n async syncPostInsights(): Promise<Instagram.PostInsightsSyncResponse> {\n return this.client.post<Instagram.PostInsightsSyncResponse>(\n '/v1/platforms/instagram/analytics/posts/sync'\n );\n }\n\n /**\n * Get post insights sync status\n * GET /v1/platforms/instagram/analytics/posts/sync/status\n */\n async getPostInsightsSyncStatus(): Promise<Instagram.PostInsightsSyncStatusResponse> {\n return this.client.get<Instagram.PostInsightsSyncStatusResponse>(\n '/v1/platforms/instagram/analytics/posts/sync/status'\n );\n }\n\n // ===== DATA RETRIEVAL =====\n\n /**\n * Get user profile info\n * GET /v1/platforms/instagram/profile\n */\n async getProfile(): Promise<Instagram.ProfileResponse> {\n return this.client.get<Instagram.ProfileResponse>('/v1/platforms/instagram/profile');\n }\n\n /**\n * Get account overview for dashboard\n * GET /v1/platforms/instagram/account/overview\n * @param range Time range: '7d', '14d', '28d', '30d'\n */\n async getAccountOverview(params?: Instagram.GetAccountOverviewParams): Promise<Instagram.AccountOverviewResponse> {\n return this.client.get<Instagram.AccountOverviewResponse>(\n '/v1/platforms/instagram/account/overview',\n params as Record<string, string | number | boolean | undefined>\n );\n }\n\n /**\n * Get account-level insights (historical data)\n * GET /v1/platforms/instagram/account/insights\n * @param range Time range: '7d', '14d', '28d', '30d'\n */\n async getAccountInsights(params?: Instagram.GetAccountInsightsParams): Promise<Instagram.AccountInsightsResponse> {\n return this.client.get<Instagram.AccountInsightsResponse>(\n '/v1/platforms/instagram/account/insights',\n params as Record<string, string | number | boolean | undefined>\n );\n }\n\n /**\n * Get Instagram posts\n * GET /v1/platforms/instagram/posts\n */\n async getPosts(params?: Instagram.GetPostsParams): Promise<Instagram.GetPostsResponse> {\n return this.client.get<Instagram.GetPostsResponse>(\n '/v1/platforms/instagram/posts', \n params as Record<string, string | number | boolean | undefined>\n );\n }\n\n /**\n * Get insights for a specific post\n * GET /v1/platforms/instagram/posts/:postId/insights\n */\n async getPostInsights(postId: string): Promise<Instagram.PostInsightsResponse> {\n return this.client.get<Instagram.PostInsightsResponse>(\n `/v1/platforms/instagram/posts/${postId}/insights`\n );\n }\n\n /**\n * Get active stories\n * GET /v1/platforms/instagram/stories\n */\n async getStories(): Promise<Instagram.StoriesResponse> {\n return this.client.get<Instagram.StoriesResponse>('/v1/platforms/instagram/stories');\n }\n\n // ===== LEGACY SYNC (backward compat) =====\n\n /**\n * Trigger manual data sync (legacy - use specific sync methods)\n * POST /v1/platforms/instagram/sync\n * @deprecated Use syncPosts, syncAccountAnalytics, syncPostInsights instead\n */\n async sync(): Promise<Instagram.SyncResponse> {\n return this.client.post<Instagram.SyncResponse>('/v1/platforms/instagram/sync');\n }\n}\n","/**\n * TrndUp SDK - Social Module\n * \n * Social account linking methods\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { Social } from '../types';\n\nexport class SocialModule {\n constructor(private client: TrndUpClient) {}\n\n /**\n * Link YouTube account\n * POST /social/link/youtube\n */\n async linkYouTube(data: Social.LinkYouTubeRequest): Promise<Social.LinkAccountResponse> {\n return this.client.post<Social.LinkAccountResponse>('/social/link/youtube', data);\n }\n\n /**\n * Link Instagram account\n * POST /social/link/instagram\n */\n async linkInstagram(data: Social.LinkInstagramRequest): Promise<Social.LinkAccountResponse> {\n return this.client.post<Social.LinkAccountResponse>('/social/link/instagram', data);\n }\n\n /**\n * Get all connected social accounts\n * GET /social/accounts\n */\n async getConnectedAccounts(): Promise<Social.ConnectedAccount[]> {\n return this.client.get<Social.ConnectedAccount[]>('/social/accounts');\n }\n\n /**\n * Unlink a social account\n * DELETE /social/unlink/:provider\n */\n async unlinkAccount(provider: string): Promise<{ message: string }> {\n return this.client.delete<{ message: string }>(`/social/unlink/${provider}`);\n }\n}\n","/**\n * TrndUp SDK - Insights Module\n * \n * Cross-platform insights and analytics\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { Insights } from '../types';\n\nexport class InsightsModule {\n constructor(private client: TrndUpClient) {}\n\n /**\n * Get cross-platform metrics\n * GET /v1/insights/overview\n */\n async getOverview(): Promise<Insights.CrossPlatformMetrics> {\n return this.client.get<Insights.CrossPlatformMetrics>('/v1/insights/overview');\n }\n\n /**\n * Get trending content across all platforms\n * GET /v1/insights/trending\n */\n async getTrending(): Promise<Insights.TrendingContent> {\n return this.client.get<Insights.TrendingContent>('/v1/insights/trending');\n }\n\n // =========================================================================\n // YOUTUBE MOMENTUM\n // =========================================================================\n\n /**\n * Get YouTube channel momentum status\n * \n * Returns a 0-100 score indicating channel momentum:\n * - 50 = Steady (no change week over week)\n * - 0 = Severe decline\n * - 100 = Explosive growth\n * \n * Results are cached for 12 hours. Use `refreshYouTubeMomentum()` to force recalculation.\n * \n * @example\n * ```typescript\n * const momentum = await client.insights.getYouTubeMomentum();\n * \n * console.log(`Score: ${momentum.score}/100`);\n * console.log(`Status: ${momentum.status}`); // 'surging' | 'rising' | 'steady' | 'cooling' | 'dropping'\n * console.log(`Trend: ${momentum.trend}`); // 'accelerating' | 'holding' | 'decelerating'\n * console.log(momentum.insight); // Human-readable summary\n * \n * // Show breakdown\n * console.log(`Views: ${momentum.breakdown.views.change}%`);\n * console.log(`Engagement: ${momentum.breakdown.engagement.change}%`);\n * ```\n * \n * GET /v1/insights/youtube/momentum\n */\n async getYouTubeMomentum(): Promise<Insights.MomentumStatusResponse> {\n return this.client.get<Insights.MomentumStatusResponse>('/v1/insights/youtube/momentum');\n }\n\n /**\n * Get YouTube momentum history for graphing\n * \n * Returns historical momentum scores, oldest first.\n * Use this to plot momentum trends over time.\n * \n * @param params - Query parameters\n * @param params.limit - Max data points (default: 30, max: 90)\n * \n * @example\n * ```typescript\n * const { history } = await client.insights.getYouTubeMomentumHistory({ limit: 30 });\n * \n * // Plot on a chart\n * const chartData = history.map(point => ({\n * x: new Date(point.date),\n * y: point.score,\n * label: point.status\n * }));\n * ```\n * \n * GET /v1/insights/youtube/momentum/history\n */\n async getYouTubeMomentumHistory(\n params?: Insights.GetMomentumHistoryParams\n ): Promise<Insights.MomentumHistoryResponse> {\n const query = params?.limit ? `?limit=${params.limit}` : '';\n return this.client.get<Insights.MomentumHistoryResponse>(\n `/v1/insights/youtube/momentum/history${query}`\n );\n }\n\n // =========================================================================\n // YOUTUBE BEST PERFORMING VIDEO\n // =========================================================================\n\n /**\n * Get the best performing video in a time period\n * \n * Identifies your top video by views and explains WHY it performed well.\n * \n * @param params - Query parameters\n * @param params.period - Days to analyze (1-90, default: 30)\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeBestVideo({ period: 30 });\n * \n * if (result.hasData) {\n * console.log(`🏆 ${result.data.topVideo.title}`);\n * console.log(`Views: ${result.data.metrics.views}`);\n * console.log(`${result.data.comparison.vsChannelAverage.description}`);\n * console.log(result.data.insight);\n * \n * // Show how it was calculated\n * console.log(result._meta.howWeCalculatedThis.method);\n * } else {\n * console.log(result.suggestion);\n * }\n * ```\n * \n * GET /v1/insights/youtube/best-video\n */\n async getYouTubeBestVideo(\n params?: Insights.GetBestVideoParams\n ): Promise<Insights.BestPerformingVideoResponse> {\n const query = params?.period ? `?period=${params.period}` : '';\n return this.client.get<Insights.BestPerformingVideoResponse>(\n `/v1/insights/youtube/best-video${query}`\n );\n }\n\n // =========================================================================\n // YOUTUBE CONTENT DECAY\n // =========================================================================\n\n /**\n * Find videos that are losing momentum\n * \n * Compares current period vs previous period to identify declining videos.\n * Provides actionable suggestions for each decaying video.\n * \n * @param params - Query parameters\n * @param params.period - Days per comparison period (3-14, default: 7)\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeContentDecay({ period: 7 });\n * \n * if (result.hasData) {\n * console.log(result.data.insight);\n * console.log(`${result.data.summary.totalDecaying} videos declining`);\n * \n * for (const video of result.data.decayingVideos) {\n * console.log(`📉 ${video.title}: ${video.decayRate}%`);\n * console.log(` ${video.suggestion}`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/content-decay\n */\n async getYouTubeContentDecay(\n params?: Insights.GetContentDecayParams\n ): Promise<Insights.ContentDecayResponse> {\n const query = params?.period ? `?period=${params.period}` : '';\n return this.client.get<Insights.ContentDecayResponse>(\n `/v1/insights/youtube/content-decay${query}`\n );\n }\n\n // =========================================================================\n // YOUTUBE AUDIENCE ACTIVITY\n // =========================================================================\n\n /**\n * Get when your audience is most active\n * \n * Unlike generic advice, this is based on YOUR audience's behavior.\n * Analyzes first-day performance by publish day and time to identify\n * when your audience is most engaged.\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeAudienceActivity();\n * \n * if (result.hasData) {\n * console.log(`📅 Most active day: ${result.data.mostActiveDay.day}`);\n * console.log(`⏰ Peak time: ${result.data.mostActiveTimeSlot.label}`);\n * console.log(result.data.insight);\n * \n * // Show day breakdown\n * for (const day of result.data.dayBreakdown) {\n * console.log(`${day.day}: ${day.avgFirstDayViews} avg views`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/audience-activity\n */\n async getYouTubeAudienceActivity(): Promise<Insights.AudienceActivityResponse> {\n return this.client.get<Insights.AudienceActivityResponse>(\n '/v1/insights/youtube/audience-activity'\n );\n }\n\n /**\n * @deprecated Use `getYouTubeAudienceActivity()` instead\n */\n async getYouTubeBestUploadTime(): Promise<Insights.BestUploadTimeResponse> {\n return this.getYouTubeAudienceActivity();\n }\n\n // =========================================================================\n // YOUTUBE FADING HITS\n // =========================================================================\n\n /**\n * Find videos that were hits but are now declining\n * \n * Identifies videos that performed above your channel average initially\n * but have since lost momentum. These are revival opportunities.\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeFadingHits();\n * \n * if (result.hasData) {\n * console.log(result.data.insight);\n * console.log(`${result.data.summary.totalFading} videos could use a boost`);\n * \n * for (const video of result.data.fadingHits) {\n * console.log(`📉 ${video.title}`);\n * console.log(` Peak: ${video.peakPeriod.avgDailyViews} views/day`);\n * console.log(` Now: ${video.currentPeriod.avgDailyViews} views/day`);\n * console.log(` Decline: ${video.decline.percentage}%`);\n * console.log(` Revival potential: ${video.revivalPotential}`);\n * console.log(` ${video.whyRevive}`);\n * for (const suggestion of video.suggestions) {\n * console.log(` 💡 ${suggestion}`);\n * }\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/fading-hits\n */\n async getYouTubeFadingHits(): Promise<Insights.FadingHitsResponse> {\n return this.client.get<Insights.FadingHitsResponse>(\n '/v1/insights/youtube/fading-hits'\n );\n }\n\n // =========================================================================\n // YOUTUBE SUBSCRIBER QUALITY\n // =========================================================================\n\n /**\n * Measure subscriber quality score (0-100)\n * \n * Analyzes whether new subscribers are engaged or just numbers.\n * Helps identify fake growth or low-quality audiences.\n * \n * @param params - Query parameters\n * @param params.period - Days to analyze (7-90, default: 30)\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeSubscriberQuality({ period: 90 });\n * \n * if (result.hasData) {\n * console.log(`Quality Score: ${result.data.qualityScore}/100 (${result.data.scoreLabel})`);\n * console.log(`Retention: ${result.data.metrics.retentionRate}%`);\n * console.log(result.data.insight);\n * \n * // Show recommendations\n * for (const rec of result.data.recommendations) {\n * console.log(`💡 ${rec}`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/subscriber-quality\n */\n async getYouTubeSubscriberQuality(\n params?: Insights.GetSubscriberQualityParams\n ): Promise<Insights.SubscriberQualityResponse> {\n const query = params?.period ? `?period=${params.period}` : '';\n return this.client.get<Insights.SubscriberQualityResponse>(\n `/v1/insights/youtube/subscriber-quality${query}`\n );\n }\n\n // =========================================================================\n // YOUTUBE AUDIENCE FATIGUE\n // =========================================================================\n\n /**\n * Detect if your audience is getting tired of your content\n * \n * Compares key engagement metrics between recent and previous periods\n * to identify declining audience interest before it becomes critical.\n * \n * @param params - Query parameters\n * @param params.period - Days per comparison period (14-90, default: 30)\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeAudienceFatigue({ period: 30 });\n * \n * if (result.hasData) {\n * console.log(`Fatigue Index: ${result.data.fatigueIndex}/100`);\n * console.log(`Level: ${result.data.fatigueLevel}`);\n * console.log(result.data.insight);\n * \n * // Check individual signals\n * if (result.data.signals.watchTimePerView.indicatesFatigue) {\n * console.log('⚠️ Watch time is declining');\n * }\n * \n * // Get recommendations\n * for (const rec of result.data.recommendations) {\n * console.log(`💡 ${rec}`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/audience-fatigue\n */\n async getYouTubeAudienceFatigue(\n params?: Insights.GetAudienceFatigueParams\n ): Promise<Insights.AudienceFatigueResponse> {\n const query = params?.period ? `?period=${params.period}` : '';\n return this.client.get<Insights.AudienceFatigueResponse>(\n `/v1/insights/youtube/audience-fatigue${query}`\n );\n }\n\n // =========================================================================\n // YOUTUBE OPTIMAL VIDEO LENGTH\n // =========================================================================\n\n /**\n * Find the ideal video length for YOUR audience\n * \n * Unlike generic \"8-12 minutes is best\" advice, this is personalized\n * based on how YOUR videos perform at different lengths.\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeOptimalLength();\n * \n * if (result.hasData) {\n * console.log(`Optimal: ${result.data.optimalLength.bucket}`);\n * console.log(`Confidence: ${result.data.optimalLength.confidence}`);\n * console.log(result.data.insight);\n * \n * // Show breakdown by length\n * for (const bucket of result.data.breakdown) {\n * const marker = bucket.isOptimal ? '🏆' : ' ';\n * console.log(`${marker} ${bucket.bucket}: ${bucket.avgRetention}% retention, ${bucket.avgEngagement}% engagement`);\n * }\n * \n * // Recommendations\n * for (const rec of result.data.recommendations) {\n * console.log(`💡 ${rec}`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/optimal-length\n */\n async getYouTubeOptimalLength(): Promise<Insights.OptimalVideoLengthResponse> {\n return this.client.get<Insights.OptimalVideoLengthResponse>(\n '/v1/insights/youtube/optimal-length'\n );\n }\n\n // =========================================================================\n // YOUTUBE UPLOAD CONSISTENCY\n // =========================================================================\n\n /**\n * Analyze your upload schedule consistency\n * \n * Measures how regularly you upload and provides recommendations\n * for maintaining a consistent schedule that helps algorithm performance.\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeUploadConsistency();\n * \n * if (result.hasData) {\n * console.log(`Consistency Score: ${result.data.consistencyScore}/100`);\n * console.log(`Status: ${result.data.status}`);\n * console.log(`Avg gap: ${result.data.metrics.avgDaysBetweenUploads} days`);\n * console.log(`Days since last upload: ${result.data.metrics.daysSinceLastUpload}`);\n * \n * // Pattern analysis\n * console.log(`Pattern: ${result.data.pattern.typicalGap}`);\n * console.log(`Trend: ${result.data.pattern.trendDescription}`);\n * \n * // Recommendation\n * console.log(`Current: ${result.data.recommendation.current}`);\n * console.log(`Suggested: ${result.data.recommendation.suggested}`);\n * \n * // Monthly breakdown\n * for (const month of result.data.monthlyBreakdown) {\n * console.log(`${month.month}: ${month.uploads} uploads`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/upload-consistency\n */\n async getYouTubeUploadConsistency(): Promise<Insights.UploadConsistencyResponse> {\n return this.client.get<Insights.UploadConsistencyResponse>(\n '/v1/insights/youtube/upload-consistency'\n );\n }\n\n // =========================================================================\n // YOUTUBE ALL INSIGHTS\n // =========================================================================\n\n /**\n * Get all YouTube insights in one call\n * \n * Efficient for dashboard display - fetches all insights in parallel.\n * Each insight has `hasData: boolean` to check availability.\n * \n * @example\n * ```typescript\n * const all = await client.insights.getYouTubeAllInsights();\n * \n * // Check which insights are available\n * console.log('Available:', all._meta.insightsAvailable);\n * \n * // Use each insight\n * if (all.data.bestPerformingVideo.hasData) {\n * console.log(`Best: ${all.data.bestPerformingVideo.data.topVideo.title}`);\n * }\n * \n * if (all.data.audienceFatigue.hasData) {\n * console.log(`Fatigue: ${all.data.audienceFatigue.data.fatigueLevel}`);\n * }\n * \n * if (all.data.uploadConsistency.hasData) {\n * console.log(`Consistency: ${all.data.uploadConsistency.data.consistencyScore}/100`);\n * }\n * ```\n * \n * GET /v1/insights/youtube/all\n */\n async getYouTubeAllInsights(): Promise<Insights.AllInsightsResponse> {\n return this.client.get<Insights.AllInsightsResponse>('/v1/insights/youtube/all');\n }\n}\n","/**\n * Activity Module\n * \n * View sync job activity and logs\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { ApiSuccessResponse, Activity } from '../types';\n\nexport class ActivityModule {\n constructor(private client: TrndUpClient) {}\n\n /**\n * Get recent activity logs\n * \n * @param params - Optional filters\n * @returns List of activity log entries\n * \n * @example\n * ```typescript\n * // Get all recent logs\n * const { logs } = await client.activity.getLogs();\n * \n * // Get only failed jobs\n * const { logs } = await client.activity.getLogs({ status: 'failed' });\n * \n * // Get video sync jobs only\n * const { logs } = await client.activity.getLogs({ type: 'videos_sync' });\n * ```\n */\n async getLogs(params?: Activity.GetLogsParams): Promise<Activity.LogsResponse> {\n const query = new URLSearchParams();\n if (params?.type) query.set('type', params.type);\n if (params?.status) query.set('status', params.status);\n if (params?.limit) query.set('limit', params.limit.toString());\n \n const queryString = query.toString();\n const url = queryString ? `/v1/activity/logs?${queryString}` : '/v1/activity/logs';\n \n const response = await this.client.get<ApiSuccessResponse<Activity.LogsResponse>>(url);\n return response.data;\n }\n\n /**\n * Get activity summary with counts by type\n * \n * @returns Summary with total/completed/failed counts\n * \n * @example\n * ```typescript\n * const summary = await client.activity.getSummary();\n * console.log(`Total: ${summary.totalJobs}, Failed: ${summary.failed}`);\n * ```\n */\n async getSummary(): Promise<Activity.SummaryResponse> {\n const response = await this.client.get<ApiSuccessResponse<Activity.SummaryResponse>>(\n '/v1/activity/summary'\n );\n return response.data;\n }\n\n /**\n * Get the latest log entry for a specific job type\n * \n * @param jobType - The type of job to get\n * @returns Latest log entry or null if none exists\n * \n * @example\n * ```typescript\n * const latestVideoSync = await client.activity.getLatest('videos_sync');\n * if (latestVideoSync) {\n * console.log(`Last sync: ${latestVideoSync.completedAt}`);\n * }\n * ```\n */\n async getLatest(jobType: Activity.JobType): Promise<Activity.LogEntry | null> {\n const response = await this.client.get<ApiSuccessResponse<Activity.LogEntry | null>>(\n `/v1/activity/logs/${jobType}/latest`\n );\n return response.data;\n }\n}\n","/**\n * TrndUp SDK Types\n * \n * Type definitions for the TrndUp API SDK\n */\n\n// =============================================================================\n// OAUTH SCOPES\n// =============================================================================\n\n/**\n * Required OAuth scopes for YouTube account linking\n * Use these when configuring Google Sign-In in your mobile app\n * \n * @example\n * ```typescript\n * import { GoogleSignin } from '@react-native-google-signin/google-signin';\n * import { YOUTUBE_SCOPES } from '@dracoonghost/trndup-sdk';\n * \n * GoogleSignin.configure({\n * webClientId: 'YOUR_WEB_CLIENT_ID',\n * offlineAccess: true,\n * scopes: YOUTUBE_SCOPES\n * });\n * ```\n */\nexport const YOUTUBE_SCOPES = [\n 'openid',\n 'https://www.googleapis.com/auth/userinfo.profile',\n 'https://www.googleapis.com/auth/userinfo.email',\n 'https://www.googleapis.com/auth/youtube.readonly',\n 'https://www.googleapis.com/auth/yt-analytics.readonly',\n 'https://www.googleapis.com/auth/yt-analytics-monetary.readonly',\n 'https://www.googleapis.com/auth/youtube.force-ssl',\n 'https://www.googleapis.com/auth/youtubepartner'\n];\n\n/**\n * Required OAuth scopes for Instagram account linking\n */\nexport const INSTAGRAM_SCOPES = [\n 'instagram_business_basic',\n 'instagram_business_manage_insights',\n 'instagram_business_manage_comments'\n];\n\n// =============================================================================\n// API RESPONSE WRAPPER\n// =============================================================================\n\nexport interface ApiSuccessResponse<T = unknown> {\n success: true;\n data: T;\n meta?: {\n page?: number;\n limit?: number;\n total?: number;\n };\n}\n\nexport interface ApiErrorResponse {\n success: false;\n error: string;\n code?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport type ApiResponse<T = unknown> = ApiSuccessResponse<T> | ApiErrorResponse;\n\n// =============================================================================\n// AUTH TYPES\n// =============================================================================\n\nexport namespace Auth {\n export interface User {\n id: string;\n firebaseUid?: string;\n email?: string;\n phoneNumber?: string;\n username: string;\n picture?: string;\n emailVerified?: boolean;\n signInProvider?: string; // 'phone', 'google.com', 'password', 'apple.com'\n createdAt: string;\n updatedAt: string;\n }\n\n export interface SocialAccount {\n provider: string;\n accountName?: string;\n username?: string;\n profilePictureUrl?: string;\n connectedAt: string;\n }\n\n // Phone OTP Authentication (Twilio)\n export interface SendOTPRequest {\n phoneNumber: string;\n }\n\n export interface SendOTPResponse {\n message: string;\n expiresIn: number;\n }\n\n export interface VerifyOTPRequest {\n phoneNumber: string;\n code: string;\n }\n\n export interface VerifyOTPResponse {\n customToken: string;\n isNewUser: boolean;\n user: User;\n platformStatus: PlatformStatus;\n }\n\n // Firebase ID Token Login\n export interface LoginRequest {\n idToken: string;\n }\n\n export interface LoginResponse {\n user: User;\n platformStatus: PlatformStatus;\n idToken: string;\n }\n\n export interface CurrentUserResponse {\n user: User;\n socialAccounts: SocialAccount[];\n }\n\n export interface UpdateProfileRequest {\n username?: string;\n picture?: string;\n }\n\n export interface UpdateProfileResponse {\n user: User;\n }\n\n // OAuth Types\n\n /**\n * Response from OAuth URL request\n */\n export interface OAuthUrlResponse {\n /** URL to redirect user to for OAuth consent */\n authUrl: string;\n }\n\n /**\n * OAuth callback result (returned in redirect URL params)\n */\n export interface OAuthCallbackResult {\n success: boolean;\n provider: 'facebook' | 'instagram';\n message?: string;\n error?: string;\n }\n\n /**\n * Status for a single sync step\n */\n export interface SyncStepStatus {\n status: 'never_synced' | 'pending' | 'in_progress' | 'completed' | 'failed';\n lastSyncAt?: string | null;\n }\n\n /**\n * Sync status for a single platform\n */\n export interface PlatformSyncInfo {\n connected: boolean;\n /** Overall sync status (completed only if ALL steps done) */\n syncStatus?: 'never_synced' | 'pending' | 'in_progress' | 'completed' | 'failed';\n lastSyncAt?: string | null;\n /** True if any sync step incomplete - show loading/sync screen */\n needsInit?: boolean;\n /** Next step UI should perform */\n nextStep?: 'channel' | 'videos' | 'channel_analytics' | 'video_analytics' | null;\n /** Per-step status for UI progress display */\n steps?: {\n channel: SyncStepStatus;\n videos: SyncStepStatus;\n channelAnalytics: SyncStepStatus;\n videoAnalytics: SyncStepStatus;\n };\n }\n\n /**\n * Platform connection and sync status returned in login responses\n * Used by UI to decide navigation: onboarding vs loading screen vs dashboard\n */\n export interface PlatformStatus {\n hasConnectedPlatforms: boolean;\n connectedPlatforms: Array<'youtube' | 'instagram'>;\n youtube: PlatformSyncInfo;\n instagram: PlatformSyncInfo;\n }\n}\n\n// =============================================================================\n// SOCIAL TYPES\n// =============================================================================\n\nexport namespace Social {\n /**\n * Link YouTube account request\n * \n * Two options:\n * 1. serverAuthCode (recommended): Backend exchanges for tokens with proper expiry\n * 2. Direct tokens: Frontend provides accessToken (expiresAt defaults to 1 hour)\n */\n export interface LinkYouTubeRequest {\n idToken: string; // Google ID token for verification (always required)\n serverAuthCode?: string; // Google server auth code - backend exchanges for tokens (recommended)\n accessToken?: string; // Google access token (alternative to serverAuthCode)\n refreshToken?: string; // Google refresh token (only with accessToken flow)\n expiresAt?: number; // Token expiry timestamp in ms (only with accessToken flow)\n grantedScopes?: string; // Actual scopes user granted (space-separated)\n platform?: string; // 'ios' | 'android' | 'web'\n environment?: string; // 'development' | 'production'\n }\n\n export interface LinkInstagramRequest {\n accessToken: string;\n }\n\n export interface LinkAccountResponse {\n message: string;\n provider: string;\n accountName?: string;\n }\n\n export interface ConnectedAccount {\n provider: string;\n accountName?: string;\n username?: string;\n profilePictureUrl?: string;\n connectedAt: string;\n lastSyncAt?: string;\n }\n}\n\n// =============================================================================\n// YOUTUBE TYPES\n// =============================================================================\n\nexport namespace YouTube {\n /**\n * Response from GET /v1/platforms/youtube/init/status\n */\n export interface InitStatusResponse {\n needsInit: boolean;\n status: 'NEVER_SYNCED' | 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED' | 'never_synced';\n lastSyncAt: string | null;\n }\n\n /**\n * Response from POST /v1/platforms/youtube/init\n */\n export interface InitResponse {\n channel: {\n channelId: string;\n channelTitle: string;\n subscriberCount: number;\n videoCount: number;\n viewCount: number;\n thumbnailUrl: string | null;\n };\n syncStatus: {\n isFirstSync: boolean;\n lastSyncAt: string | null;\n };\n }\n\n /**\n * Response from GET /v1/platforms/youtube/videos/sync/status\n */\n export interface VideosSyncStatusResponse {\n needsSync: boolean;\n status: 'NEVER_SYNCED' | 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED' | 'never_synced';\n lastSyncAt: string | null;\n videoCount: number;\n oldestVideoDate: string | null;\n analyticsStartDate: string | null;\n }\n\n /**\n * Response from POST /v1/platforms/youtube/videos/sync\n */\n export interface VideosSyncResponse {\n videos: {\n total: number;\n synced: number;\n oldestVideoDate: string | null;\n };\n syncStatus: {\n isFirstSync: boolean;\n lastSyncAt: string;\n };\n }\n\n export interface Video {\n id: string;\n videoId: string;\n title: string;\n description?: string;\n thumbnailUrl?: string;\n publishedAt: string;\n duration?: number;\n viewCount?: number;\n likeCount?: number;\n commentCount?: number;\n engagementRate?: number;\n }\n\n export interface GetVideosParams {\n limit?: number;\n offset?: number;\n sortBy?: 'recent' | 'views' | 'engagement';\n }\n\n export interface GetVideosResponse {\n videos: Video[];\n total: number;\n hasMore: boolean;\n }\n\n export interface ChannelMetrics {\n subscriberCount: number;\n totalViews: number;\n totalVideos: number;\n averageViews: number;\n engagementRate: number;\n }\n\n export interface HealthScore {\n score: number;\n grade: 'A' | 'B' | 'C' | 'D' | 'F';\n metrics: {\n consistency: number;\n engagement: number;\n growth: number;\n quality: number;\n };\n }\n\n export interface GetHealthScoreParams {\n range?: string; // e.g., '7d', '30d', '90d'\n }\n\n // =========================================================================\n // UNIFIED SYNC STATUS\n // =========================================================================\n\n /**\n * Status of a single sync type\n */\n export interface SyncTypeStatus {\n /** Current sync status */\n status: 'never_synced' | 'pending' | 'in_progress' | 'completed' | 'failed';\n /** \n * When the backend last fetched data from YouTube API.\n * UI should store this value and compare on next call.\n * If lastSyncAt is newer than stored → refetch data from API\n */\n lastSyncAt: string | null;\n /** Last error message if sync failed */\n lastError: string | null;\n /** Number of consecutive failed attempts */\n failedAttempts: number;\n /** Whether data is older than the refresh interval (backend will auto-sync) */\n isStale: boolean;\n /** When the next automatic sync will run */\n nextSyncAt: string | null;\n /** Human-readable time until next sync (e.g., \"6 hours\") */\n timeUntilNextSync: string;\n /** Sync interval for this data type (e.g., \"12 hours\") */\n interval: string;\n }\n\n /**\n * Response from GET /v1/platforms/youtube/sync/status\n * \n * Use this endpoint to:\n * 1. Check if any sync is stale (anyStale) - backend will auto-sync\n * 2. Check if new data available (compare lastSyncAt with stored value)\n * 3. Show sync status in UI settings/debug screen\n * \n * @example\n * ```typescript\n * const status = await client.youtube.getUnifiedSyncStatus();\n * \n * // Check if backend has newer data than our cache\n * const storedLastSync = localStorage.get('videosLastSync');\n * if (status.data.syncs.videos.lastSyncAt > storedLastSync) {\n * // Backend synced since our last fetch, refetch videos\n * const videos = await client.youtube.getVideos();\n * localStorage.set('videosLastSync', status.data.syncs.videos.lastSyncAt);\n * }\n * ```\n */\n export interface UnifiedSyncStatusResponse {\n /** Whether YouTube account is connected */\n isConnected: boolean;\n /** YouTube channel ID */\n channelId: string | null;\n /** YouTube channel name */\n channelName: string | null;\n /** Total videos stored in database */\n totalVideos: number;\n \n /** Sync status for each data type */\n syncs: {\n /** Channel info (subscribers, video count) */\n channelInfo: SyncTypeStatus;\n /** Videos list */\n videos: SyncTypeStatus;\n /** Channel analytics (daily metrics, demographics) */\n channelAnalytics: SyncTypeStatus;\n /** Video analytics (per-video metrics) */\n videoAnalytics: SyncTypeStatus;\n };\n }\n\n // =========================================================================\n // ANALYTICS SYNC TYPES\n // =========================================================================\n\n /**\n * Response from GET /v1/platforms/youtube/analytics/channel/sync/status\n */\n export interface ChannelAnalyticsSyncStatusResponse {\n needsSync: boolean;\n status: 'NEVER_SYNCED' | 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED' | 'never_synced';\n dailyRecords: number;\n dateRange: {\n start: string;\n end: string;\n } | null;\n lastSyncAt: string | null;\n message: string;\n }\n\n /**\n * Response from POST /v1/platforms/youtube/analytics/channel/sync\n */\n export interface ChannelAnalyticsSyncResponse {\n syncedDays: number;\n hasRevenue: boolean;\n dateRange: {\n start: string;\n end: string;\n };\n demographics: number;\n trafficSources: number;\n countries: number;\n devices: number;\n syncStatus: 'COMPLETED' | 'FAILED';\n lastSyncAt: string;\n }\n\n /**\n * Response from GET /v1/platforms/youtube/analytics/videos/sync/status\n */\n export interface VideoAnalyticsSyncStatusResponse {\n needsSync: boolean;\n status: 'NEVER_SYNCED' | 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED';\n /** \n * Progress tracking - USE THESE FOR UI DISPLAY\n * These values reflect actual database state, not queue state\n */\n progress: {\n /** Number of videos with analytics saved to database */\n completed: number;\n /** Total number of videos to sync */\n total: number;\n /** Videos still needing sync */\n remaining: number;\n /** Progress percentage (0-100) */\n percent: number;\n };\n /** @deprecated Use progress.total instead */\n totalVideos: number;\n /** @deprecated Use progress.completed instead */\n videosWithAnalytics: number;\n /** @deprecated Use progress.remaining instead */\n videosNeedingSync: number;\n lastSyncAt: string | null;\n lastError: string | null;\n /** \n * Queue info (for debugging only, NOT for progress display)\n * Note: Queue counts reset and don't reflect true progress\n */\n queue: {\n /** Whether workers are actively processing */\n isProcessing: boolean;\n /** Jobs currently being processed */\n jobsActive: number;\n /** Jobs waiting in queue */\n jobsWaiting: number;\n /** Jobs that failed after retries */\n jobsFailed: number;\n };\n message: string;\n }\n\n /**\n * Response from POST /v1/platforms/youtube/analytics/videos/sync\n * \n * Returns immediately - sync runs in background via BullMQ queue.\n * Poll VideoAnalyticsSyncStatusResponse for progress.\n */\n export interface VideoAnalyticsSyncResponse {\n /** Status message */\n message: string;\n /** Current sync status - will be 'IN_PROGRESS' when sync starts */\n status: 'IN_PROGRESS' | 'COMPLETED' | 'FAILED';\n /** Total number of videos */\n totalVideos: number;\n /** Number of videos queued for sync (excludes already-synced) */\n queued: number;\n /** Number of videos skipped (already have analytics) */\n skipped: number;\n }\n\n /**\n * Daily analytics data for a single video\n * Stored in YouTubeVideoDaily collection\n */\n export interface VideoDailyAnalytics {\n videoId: string;\n date: string;\n views: number;\n likes: number;\n dislikes: number;\n comments: number;\n estimatedMinutesWatched: number;\n averageViewDuration: number;\n subscribersGained: number;\n subscribersLost: number;\n }\n}\n\n// =============================================================================\n// INSTAGRAM TYPES\n// =============================================================================\n\nexport namespace Instagram {\n // ===== INITIALIZATION =====\n\n export interface InitStatusResponse {\n connected: boolean;\n needsInit: boolean;\n isInitialSyncDone?: boolean;\n lastSyncedAt?: string | null;\n authMethod?: 'facebook' | 'instagram';\n account?: {\n username?: string;\n name?: string;\n profilePictureUrl?: string;\n followersCount?: number;\n mediaCount?: number;\n } | null;\n syncedMediaCount?: number;\n message?: string;\n }\n\n export interface InitResponse {\n needsInit: boolean;\n message: string;\n profile?: {\n username?: string;\n name?: string;\n profilePictureUrl?: string;\n followersCount?: number;\n mediaCount?: number;\n };\n syncedMedia?: number;\n lastSyncedAt?: string;\n }\n\n // ===== ACCOUNT INSIGHTS =====\n\n export interface GetAccountInsightsParams {\n range?: '7d' | '14d' | '28d' | '30d';\n }\n\n export interface AccountInsightsResponse {\n range: string;\n aggregated: Record<string, number>;\n timeSeries: Record<string, Array<{ date: string; value: number }>>;\n }\n\n // ===== PROFILE =====\n\n export interface ProfileResponse {\n id: string;\n username: string;\n name?: string;\n biography?: string;\n profilePictureUrl?: string;\n followersCount: number;\n followingCount: number;\n mediaCount: number;\n accountType?: 'BUSINESS' | 'CREATOR' | 'PERSONAL';\n website?: string;\n cached?: boolean;\n cachedAt?: string;\n }\n\n // ===== ACCOUNT OVERVIEW =====\n\n export interface GetAccountOverviewParams {\n range?: '7d' | '14d' | '28d' | '30d';\n }\n\n export interface AccountOverviewResponse {\n profile: {\n id: string;\n username: string;\n name?: string;\n biography?: string;\n profilePictureUrl?: string;\n followersCount: number;\n followingCount: number;\n mediaCount: number;\n accountType?: string;\n website?: string;\n };\n stats: {\n range: string;\n reach: number;\n impressions: number;\n profileViews: number;\n websiteClicks: number;\n totalLikes: number;\n totalComments: number;\n totalShares: number;\n totalSaves: number;\n engagementRate: number;\n accountsEngaged: number;\n };\n topPosts: Array<{\n id: string;\n mediaType: string;\n mediaUrl?: string;\n thumbnailUrl?: string;\n caption?: string;\n likeCount?: number;\n commentsCount?: number;\n timestamp?: string;\n permalink?: string;\n }>;\n syncedMediaCount: number;\n }\n\n // ===== MEDIA / POSTS =====\n\n export interface Post {\n id: string;\n media_id: string;\n caption?: string;\n media_type: 'IMAGE' | 'VIDEO' | 'CAROUSEL_ALBUM' | 'REELS';\n media_url?: string;\n thumbnail_url?: string;\n permalink?: string;\n timestamp: string;\n like_count?: number;\n comments_count?: number;\n }\n\n export interface GetPostsParams {\n limit?: number;\n offset?: number;\n mediaType?: string;\n sortBy?: 'timestamp' | 'like_count' | 'comments_count';\n sortOrder?: 'asc' | 'desc';\n }\n\n export interface GetPostsResponse {\n posts: Post[];\n pagination: {\n limit: number;\n offset: number;\n total: number;\n hasMore: boolean;\n };\n }\n\n export interface PostInsightsResponse {\n postId: string;\n mediaType: string;\n insights: {\n data: Array<{\n name: string;\n title: string;\n values: Array<{ value: number }>;\n }>;\n };\n }\n\n // ===== STORIES =====\n\n export interface Story {\n id: string;\n media_type: 'IMAGE' | 'VIDEO';\n media_url?: string;\n timestamp: string;\n permalink?: string;\n }\n\n export interface StoriesResponse {\n stories: Story[];\n count: number;\n }\n\n // ===== SYNC STATUS =====\n\n /** Sync status enum matching backend */\n export type SyncStatus = 'NEVER_SYNCED' | 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED';\n\n /** Individual sync type info */\n export interface SyncTypeInfo {\n status: SyncStatus;\n lastSyncAt: string | null;\n lastError?: string | null;\n failedAttempts?: number;\n isStale: boolean;\n nextSyncAt: string | null;\n timeUntilNextSync: string;\n interval: string;\n }\n\n /**\n * Unified sync status response\n * GET /v1/platforms/instagram/sync/status\n */\n export interface UnifiedSyncStatusResponse {\n isConnected: boolean;\n igUserId: string | null;\n username: string | null;\n totalPosts: number;\n syncs: {\n accountInfo: SyncTypeInfo;\n posts: SyncTypeInfo;\n accountAnalytics: SyncTypeInfo;\n postInsights: SyncTypeInfo;\n stories: SyncTypeInfo;\n };\n }\n\n // ===== POSTS SYNC =====\n\n /**\n * Response from POST /v1/platforms/instagram/posts/sync\n */\n export interface PostsSyncResponse {\n posts: {\n total: number;\n synced: number;\n oldestPostDate: string | null;\n };\n syncStatus: {\n isFirstSync: boolean;\n lastSyncAt: string;\n };\n }\n\n /**\n * Response from GET /v1/platforms/instagram/posts/sync/status\n */\n export interface PostsSyncStatusResponse {\n needsSync: boolean;\n status: SyncStatus;\n lastSyncAt: string | null;\n postCount: number;\n oldestPostDate: string | null;\n }\n\n // ===== ACCOUNT ANALYTICS SYNC =====\n\n /**\n * Response from POST /v1/platforms/instagram/analytics/account/sync\n */\n export interface AccountAnalyticsSyncResponse {\n range: string;\n metricsCount: number;\n syncStatus: SyncStatus;\n lastSyncAt: string;\n }\n\n /**\n * Response from GET /v1/platforms/instagram/analytics/account/sync/status\n */\n export interface AccountAnalyticsSyncStatusResponse {\n needsSync: boolean;\n status: SyncStatus;\n insightRecords: number;\n dateRange: {\n start: string;\n end: string;\n } | null;\n lastSyncAt: string | null;\n message: string;\n }\n\n // ===== POST INSIGHTS SYNC =====\n\n /**\n * Response from POST /v1/platforms/instagram/analytics/posts/sync\n */\n export interface PostInsightsSyncResponse {\n totalPosts: number;\n postsWithInsights: number;\n errorCount: number;\n syncStatus: SyncStatus;\n lastSyncAt: string;\n }\n\n /**\n * Response from GET /v1/platforms/instagram/analytics/posts/sync/status\n */\n export interface PostInsightsSyncStatusResponse {\n needsSync: boolean;\n status: SyncStatus;\n totalPosts: number;\n postsWithInsights: number;\n coverage: number;\n lastSyncAt: string | null;\n metadata?: {\n postsWithInsights?: number;\n totalPosts?: number;\n lastCheckedAt?: string;\n } | null;\n message: string;\n }\n\n // ===== LEGACY SYNC =====\n\n export interface SyncResponse {\n message: string;\n profile?: {\n username?: string;\n followersCount?: number;\n mediaCount?: number;\n };\n syncedMedia?: number;\n }\n\n // ===== LEGACY (backward compatibility) =====\n\n /** @deprecated Use InitStatusResponse */\n export interface AccountMetrics {\n followersCount: number;\n followsCount: number;\n mediaCount: number;\n averageLikes?: number;\n engagementRate?: number;\n }\n}\n\n// =============================================================================\n// INSIGHTS TYPES\n// =============================================================================\n\nexport namespace Insights {\n export interface CrossPlatformMetrics {\n totalFollowers: number;\n totalEngagement: number;\n platforms: {\n youtube?: {\n subscribers: number;\n views: number;\n videos: number;\n };\n instagram?: {\n followers: number;\n posts: number;\n avgEngagement: number;\n };\n };\n }\n\n export interface TrendingContent {\n youtube?: YouTube.Video[];\n instagram?: Instagram.Post[];\n }\n\n // =========================================================================\n // MOMENTUM STATUS\n // =========================================================================\n\n /** Momentum status labels */\n export type MomentumStatusLabel = 'surging' | 'rising' | 'steady' | 'cooling' | 'dropping';\n \n /** Momentum trend direction */\n export type MomentumTrend = 'accelerating' | 'holding' | 'decelerating';\n\n /**\n * Metric breakdown showing current vs previous period with contribution\n */\n export interface MetricComparison {\n /** Current period value */\n current: number;\n /** Previous period value */\n previous: number;\n /** Percentage change (-100 to +100+) */\n change: number;\n /** Points contributed to momentum score (can be negative) */\n points: number;\n /** Human-readable description: \"+25% Views × 30% weight = +7.5 pts\" */\n description: string;\n }\n\n /**\n * Momentum Status Response\n * \n * Provides a 0-100 score indicating channel momentum:\n * - 50 = Steady (no change week over week)\n * - 0 = Severe decline (-100% or worse)\n * - 100 = Explosive growth (+100% or better)\n * \n * Status labels:\n * - 80-100: Surging 🚀\n * - 60-79: Rising 📈\n * - 40-59: Steady ➡️\n * - 20-39: Cooling 📉\n * - 0-19: Dropping 🔻\n */\n export interface MomentumStatusResponse {\n /** Normalized score 0-100 (50 = no change) */\n score: number;\n /** Raw weighted average change before normalization */\n rawChange: number;\n /** Human-friendly status label */\n status: MomentumStatusLabel;\n /** Trend direction compared to previous period */\n trend: MomentumTrend;\n /** Previous period's score (null if first calculation) */\n previousScore: number | null;\n /** Time periods being compared */\n period: {\n current: { start: string; end: string };\n previous: { start: string; end: string };\n };\n /** Breakdown of individual metrics with contribution info */\n breakdown: {\n views: MetricComparison;\n engagement: MetricComparison;\n watchTime: MetricComparison;\n subscribers: MetricComparison;\n };\n /** Weights used in calculation */\n weights: {\n views: number;\n engagement: number;\n watchTime: number;\n subscribers: number;\n };\n /** Human-readable insight summary */\n insight: string;\n /** Cache metadata */\n _meta?: {\n fromCache: boolean;\n refreshed?: boolean;\n cacheValidFor: string;\n algorithmVersion: string;\n };\n }\n\n /**\n * Single history point for momentum graphing\n */\n export interface MomentumHistoryPoint {\n /** Date (YYYY-MM-DD) */\n date: string;\n /** Momentum score at that time */\n score: number;\n /** Status label at that time */\n status: MomentumStatusLabel;\n }\n\n /**\n * Momentum history response for graphing\n */\n export interface MomentumHistoryResponse {\n /** Historical data points (oldest first) */\n history: MomentumHistoryPoint[];\n /** Number of points returned */\n count: number;\n }\n\n /**\n * Parameters for momentum history request\n */\n export interface GetMomentumHistoryParams {\n /** Max number of data points (default: 30, max: 90) */\n limit?: number;\n }\n\n // =========================================================================\n // COMMON INSIGHT TYPES\n // =========================================================================\n\n /**\n * How we calculated this insight - shown to user for transparency\n */\n export interface CalculationExplanation {\n /** Short description of the methodology */\n method: string;\n /** Time range analyzed */\n timeRange: string;\n /** Key factors that influenced the result */\n factors: string[];\n /** Data quality/reliability indicator */\n dataQuality: 'high' | 'medium' | 'low';\n /** Why data quality might be limited */\n dataQualityNote?: string;\n }\n\n /**\n * Common metadata for all insights\n */\n export interface InsightMeta {\n /** When this insight was calculated */\n calculatedAt: string;\n /** When this insight expires (should be recalculated) */\n validUntil: string;\n /** Algorithm version for tracking changes */\n algorithmVersion: string;\n /** Whether result came from cache */\n fromCache: boolean;\n /** How this was calculated (for transparency) */\n howWeCalculatedThis: CalculationExplanation;\n }\n\n /**\n * Response when there's not enough data for an insight\n */\n export interface InsufficientDataResponse {\n hasData: false;\n reason: string;\n minimumRequired: string;\n currentAmount: string;\n suggestion: string;\n }\n\n // =========================================================================\n // BEST PERFORMING VIDEO\n // =========================================================================\n\n export interface TopVideoData {\n videoId: string;\n title: string;\n thumbnailUrl: string | null;\n publishedAt: string;\n /** Link to watch on YouTube */\n watchUrl: string;\n }\n\n export interface TopVideoMetrics {\n views: number;\n likes: number;\n comments: number;\n estimatedMinutesWatched: number;\n engagementRate: number;\n }\n\n export interface TopVideoComparison {\n /** Performance vs user's average video */\n vsChannelAverage: {\n views: number; // e.g., +145 (percent)\n engagement: number; // e.g., +32 (percent)\n description: string; // e.g., \"2.4x better than your average video\"\n };\n /** Why this video performed well */\n standoutReasons: string[];\n }\n\n export interface BestPerformingVideoData {\n /** Time range analyzed */\n period: {\n start: string;\n end: string;\n days: number;\n };\n /** The top performing video */\n topVideo: TopVideoData;\n /** Video's metrics for this period */\n metrics: TopVideoMetrics;\n /** How it compares to your other content */\n comparison: TopVideoComparison;\n /** Human-readable summary */\n insight: string;\n /** Runner-up videos (for context) */\n runnersUp: Array<{\n videoId: string;\n title: string;\n views: number;\n thumbnailUrl: string | null;\n }>;\n }\n\n export type BestPerformingVideoResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: BestPerformingVideoData; _meta: InsightMeta };\n\n // =========================================================================\n // CONTENT DECAY\n // =========================================================================\n\n export interface DecayingVideo {\n videoId: string;\n title: string;\n thumbnailUrl: string | null;\n publishedAt: string;\n watchUrl: string;\n /** Age of video in days */\n ageInDays: number;\n /** Views in current period */\n currentViews: number;\n /** Views in previous period */\n previousViews: number;\n /** Decay rate as percentage (negative = declining) */\n decayRate: number;\n /** Severity of decay */\n severity: 'severe' | 'moderate' | 'mild';\n /** Actionable suggestion */\n suggestion: string;\n }\n\n export interface ContentDecayData {\n /** Time range analyzed */\n period: {\n current: { start: string; end: string };\n previous: { start: string; end: string };\n days: number;\n };\n /** Number of videos analyzed */\n videosAnalyzed: number;\n /** Videos experiencing significant decay */\n decayingVideos: DecayingVideo[];\n /** Summary stats */\n summary: {\n totalDecaying: number;\n severeCount: number;\n moderateCount: number;\n mildCount: number;\n /** Combined view loss from all decaying videos */\n totalViewsLost: number;\n };\n /** Human-readable summary */\n insight: string;\n /** What content decay means */\n whatThisMeans: string;\n }\n\n export type ContentDecayResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: ContentDecayData; _meta: InsightMeta };\n\n // =========================================================================\n // AUDIENCE ACTIVITY (When your audience is most active)\n // =========================================================================\n\n export interface DayPerformance {\n day: string; // 'Monday', 'Tuesday', etc.\n avgFirstDayViews: number;\n avgEngagement: number;\n videoCount: number;\n /** Confidence in this data */\n confidence: 'high' | 'medium' | 'low';\n }\n\n export interface TimeSlotPerformance {\n slot: string; // e.g., \"14:00-17:00\"\n label: string; // e.g., \"Afternoon (2-5pm)\"\n avgFirstDayViews: number;\n videoCount: number;\n confidence: 'high' | 'medium' | 'low';\n }\n\n export interface AudienceActivityData {\n /** How many videos were analyzed */\n videosAnalyzed: number;\n /** Day when your audience is most active */\n mostActiveDay: {\n day: string;\n confidence: 'high' | 'medium' | 'low';\n /** Why this day shows highest activity */\n reason: string;\n };\n /** Time slot when your audience is most active */\n mostActiveTimeSlot: {\n slot: string;\n label: string;\n confidence: 'high' | 'medium' | 'low';\n reason: string;\n };\n /** Full breakdown by day */\n dayBreakdown: DayPerformance[];\n /** Full breakdown by time slot (if enough data) */\n timeBreakdown: TimeSlotPerformance[] | null;\n /** Human-readable summary */\n insight: string;\n /** Disclaimer about the data */\n disclaimer: string;\n }\n\n export type AudienceActivityResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: AudienceActivityData; _meta: InsightMeta };\n\n /** @deprecated Use AudienceActivityData instead */\n export type BestUploadTimeData = AudienceActivityData;\n /** @deprecated Use AudienceActivityResponse instead */\n export type BestUploadTimeResponse = AudienceActivityResponse;\n\n // =========================================================================\n // FADING HITS (Videos that were hits but are now declining)\n // =========================================================================\n\n export interface FadingHitVideo {\n videoId: string;\n title: string;\n thumbnailUrl: string | null;\n publishedAt: string;\n watchUrl: string;\n /** How old the video is in days */\n ageInDays: number;\n /** Performance during peak period (actual peak, not just first 14 days) */\n peakPerformance: {\n /** Average daily views during peak window */\n dailyViews: number;\n /** Comparison to channel average (e.g., \"45% above average\") */\n vsChannelAverage: string;\n /** Was this a top performer (2x+ channel average)? */\n wasTopPerformer: boolean;\n /** When the actual peak occurred */\n peakPeriod: {\n start: string;\n end: string;\n /** How many days after upload the peak occurred */\n daysAfterUpload: number;\n /** True if video went viral later (not at launch) - delayed virality */\n wasDelayedViral: boolean;\n };\n };\n /** Performance in recent period (last 14 days) */\n currentPerformance: {\n /** Current average daily views */\n dailyViews: number;\n /** Percentage decline from peak (negative number) */\n declineFromPeak: number;\n };\n /** How likely revival efforts would work */\n revivalPotential: 'high' | 'medium' | 'low';\n /** Why this video might be worth reviving */\n whyRevive: string;\n /** Specific suggestions to revive this video */\n suggestions: string[];\n }\n\n export interface FadingHitsData {\n /** Time period for \"current\" performance check */\n period: {\n current: { start: string; end: string };\n label: string;\n };\n /** Number of videos analyzed */\n videosAnalyzed: number;\n /** Videos that were performing above average but are now declining */\n fadingHits: FadingHitVideo[];\n /** Summary stats */\n summary: {\n totalFading: number;\n highPotentialCount: number;\n mediumPotentialCount: number;\n lowPotentialCount: number;\n /** Combined daily views being lost */\n totalDailyViewsLost: number;\n };\n /** Human-readable summary */\n insight: string;\n /** What fading hits means */\n whatThisMeans: string;\n }\n\n export type FadingHitsResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: FadingHitsData; _meta: InsightMeta };\n\n // =========================================================================\n // SUBSCRIBER QUALITY\n // =========================================================================\n\n export type SubscriberQualityLabel = 'excellent' | 'good' | 'fair' | 'needs_attention' | 'concerning';\n\n export interface SubscriberQualityData {\n /** Time range analyzed */\n period: {\n start: string;\n end: string;\n days: number;\n };\n /** Overall quality score (0-100) */\n qualityScore: number;\n /** Score interpretation */\n scoreLabel: SubscriberQualityLabel;\n /** Core metrics */\n metrics: {\n /** Total subscribers gained */\n subscribersGained: number;\n /** Total subscribers lost */\n subscribersLost: number;\n /** Net subscriber change */\n netSubscribers: number;\n /** Retention rate (how many stay) */\n retentionRate: number;\n /** Average watch time per view */\n avgWatchTimeMinutes: number;\n /** Views per subscriber (engagement proxy) */\n viewsPerSubscriber: number;\n };\n /** Comparison to previous period */\n trend: {\n scoreChange: number;\n direction: 'improving' | 'stable' | 'declining';\n description: string;\n };\n /** Human-readable summary */\n insight: string;\n /** Specific recommendations */\n recommendations: string[];\n }\n\n export type SubscriberQualityResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: SubscriberQualityData; _meta: InsightMeta };\n\n // =========================================================================\n // AUDIENCE FATIGUE\n // =========================================================================\n\n export type FatigueLevel = 'none' | 'low' | 'moderate' | 'high' | 'critical';\n\n export interface FatigueSignal {\n /** Name of the signal */\n name: string;\n /** Current period value */\n current: number;\n /** Previous period value */\n previous: number;\n /** Change percentage (negative = declining) */\n changePercent: number;\n /** Whether this signal indicates fatigue */\n indicatesFatigue: boolean;\n /** Description of what this means */\n interpretation: string;\n }\n\n export interface AudienceFatigueData {\n /** Time periods compared */\n period: {\n current: { start: string; end: string };\n previous: { start: string; end: string };\n daysPerPeriod: number;\n };\n /** Overall fatigue index (0-100, higher = more fatigue) */\n fatigueIndex: number;\n /** Human-readable fatigue level */\n fatigueLevel: FatigueLevel;\n /** Individual signals that contribute to the score */\n signals: {\n watchTimePerView: FatigueSignal;\n engagementRate: FatigueSignal;\n subscriberChurn: FatigueSignal;\n viewsPerVideo: FatigueSignal;\n };\n /** Summary of signals */\n summary: {\n signalsIndicatingFatigue: number;\n totalSignals: number;\n primaryConcern: string | null;\n };\n /** Human-readable insight */\n insight: string;\n /** What to do about it */\n recommendations: string[];\n }\n\n export type AudienceFatigueResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: AudienceFatigueData; _meta: InsightMeta };\n\n // =========================================================================\n // OPTIMAL VIDEO LENGTH\n // =========================================================================\n\n export interface LengthBucketPerformance {\n /** Bucket name (e.g., \"5-10 minutes\") */\n bucket: string;\n /** Bucket range in seconds */\n range: { min: number; max: number | null };\n /** Number of videos in this bucket */\n videoCount: number;\n /** Average retention as percentage */\n avgRetention: number;\n /** Average engagement rate */\n avgEngagement: number;\n /** Average views per video */\n avgViews: number;\n /** Combined performance score (0-100) */\n performanceScore: number;\n /** Is this the optimal bucket? */\n isOptimal: boolean;\n }\n\n export interface OptimalVideoLengthData {\n /** How many videos were analyzed */\n videosAnalyzed: number;\n /** The recommended optimal length range */\n optimalLength: {\n bucket: string;\n range: { min: number; max: number | null };\n confidence: 'high' | 'medium' | 'low';\n reason: string;\n };\n /** Performance breakdown by length bucket */\n breakdown: LengthBucketPerformance[];\n /** Key findings */\n findings: {\n bestRetention: { bucket: string; value: number };\n bestEngagement: { bucket: string; value: number };\n mostCommon: { bucket: string; count: number };\n };\n /** Human-readable insight */\n insight: string;\n /** Specific recommendations */\n recommendations: string[];\n }\n\n export type OptimalVideoLengthResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: OptimalVideoLengthData; _meta: InsightMeta };\n\n // =========================================================================\n // UPLOAD CONSISTENCY\n // =========================================================================\n\n /** Consistency status levels */\n export type ConsistencyStatus = 'excellent' | 'good' | 'needs_improvement' | 'poor' | 'inactive';\n\n /** Monthly upload breakdown */\n export interface MonthlyUploadData {\n month: string;\n uploads: number;\n avgGap: number | null;\n }\n\n /** Upload consistency insight data */\n export interface UploadConsistencyData {\n /** Period analyzed */\n period: {\n start: string;\n end: string;\n days: number;\n };\n /** Overall consistency score (0-100) */\n consistencyScore: number;\n /** Status label */\n status: ConsistencyStatus;\n /** Key metrics */\n metrics: {\n totalUploads: number;\n avgDaysBetweenUploads: number;\n uploadVariance: number;\n daysSinceLastUpload: number;\n lastUploadDate: string | null;\n };\n /** Upload pattern analysis */\n pattern: {\n isRegular: boolean;\n typicalGap: string;\n longestStreak: number;\n currentStreak: number;\n trend: 'increasing' | 'stable' | 'decreasing';\n trendDescription: string;\n };\n /** Monthly breakdown */\n monthlyBreakdown: MonthlyUploadData[];\n /** Frequency recommendation */\n recommendation: {\n suggested: string;\n current: string;\n reason: string;\n };\n /** Human-readable summary */\n insight: string;\n /** Actionable recommendations */\n recommendations: string[];\n }\n\n export type UploadConsistencyResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: UploadConsistencyData; _meta: InsightMeta };\n\n // =========================================================================\n // ALL INSIGHTS (COMBINED)\n // =========================================================================\n\n export interface AllInsightsData {\n bestPerformingVideo: BestPerformingVideoResponse;\n contentDecay: ContentDecayResponse;\n audienceActivity: AudienceActivityResponse;\n subscriberQuality: SubscriberQualityResponse;\n fadingHits: FadingHitsResponse;\n audienceFatigue: AudienceFatigueResponse;\n optimalLength: OptimalVideoLengthResponse;\n uploadConsistency: UploadConsistencyResponse;\n }\n\n export interface AllInsightsResponse {\n data: AllInsightsData;\n _meta: {\n calculatedAt: string;\n insightsAvailable: string[];\n };\n }\n\n // =========================================================================\n // REQUEST PARAMETERS\n // =========================================================================\n\n export interface GetBestVideoParams {\n /** Number of days to analyze (1-90, default: 30) */\n period?: number;\n }\n\n export interface GetContentDecayParams {\n /** Days per comparison period (3-14, default: 7) */\n period?: number;\n }\n\n export interface GetSubscriberQualityParams {\n /** Number of days to analyze (14-180, default: 90) */\n period?: number;\n }\n\n export interface GetAudienceFatigueParams {\n /** Days per comparison period (14-90, default: 30) */\n period?: number;\n }\n}\n\n// =============================================================================\n// ACTIVITY TYPES\n// =============================================================================\n\nexport namespace Activity {\n /**\n * Job types for sync operations\n */\n export type JobType = 'videos_sync' | 'channel_analytics' | 'video_analytics';\n\n /**\n * Job status values\n */\n export type JobStatus = 'started' | 'completed' | 'failed';\n\n /**\n * Job trigger source\n */\n export type JobTrigger = 'scheduler' | 'api' | 'manual';\n\n /**\n * Job data details (varies by job type)\n */\n export interface JobData {\n // videos_sync\n videosFound?: number;\n newVideos?: number;\n updatedVideos?: number;\n totalVideos?: number;\n // channel_analytics\n daysProcessed?: number;\n metricsUpdated?: number;\n syncedDays?: number;\n dateRange?: { start: string; end: string };\n // video_analytics\n videoId?: string;\n videoTitle?: string;\n recordsCreated?: number;\n recordsUpdated?: number;\n queued?: number;\n skipped?: number;\n // Generic\n message?: string;\n [key: string]: unknown;\n }\n\n /**\n * A single activity log entry\n */\n export interface LogEntry {\n /** Unique log ID */\n id: string;\n /** BullMQ job ID */\n jobId: string;\n /** Type of sync job */\n type: JobType;\n /** Current status */\n status: JobStatus;\n /** When the job started */\n startedAt: string;\n /** When the job completed (if finished) */\n completedAt?: string;\n /** Duration in milliseconds */\n durationMs?: number;\n /** What triggered this job */\n triggeredBy: JobTrigger;\n /** Job result data */\n data?: JobData;\n /** Error message (if failed) */\n error?: string;\n }\n\n /**\n * Response for activity logs list\n */\n export interface LogsResponse {\n /** List of log entries */\n logs: LogEntry[];\n /** Number of entries returned */\n count: number;\n }\n\n /**\n * Activity summary with counts\n */\n export interface SummaryResponse {\n /** Total jobs ever run */\n totalJobs: number;\n /** Successfully completed jobs */\n completed: number;\n /** Failed jobs */\n failed: number;\n /** Breakdown by job type */\n byType: Record<JobType, { total: number; lastRun?: string }>;\n }\n\n /**\n * Parameters for fetching activity logs\n */\n export interface GetLogsParams {\n /** Filter by job type */\n type?: JobType;\n /** Filter by status */\n status?: JobStatus;\n /** Max entries to return (default: 50, max: 100) */\n limit?: number;\n }\n}\n","/**\n * TrndUp API SDK\n * \n * Official TypeScript SDK for the TrndUp API.\n * Provides type-safe methods for all API endpoints with Firebase authentication.\n * \n * @example\n * ```typescript\n * import { TrndUpSDK, YOUTUBE_SCOPES } from '@dracoonghost/trndup-sdk';\n * import auth from '@react-native-firebase/auth';\n * import { GoogleSignin } from '@react-native-google-signin/google-signin';\n * \n * // Configure Google Sign-In with YouTube scopes\n * GoogleSignin.configure({\n * webClientId: 'YOUR_WEB_CLIENT_ID',\n * offlineAccess: true,\n * scopes: YOUTUBE_SCOPES\n * });\n * \n * const sdk = new TrndUpSDK({\n * baseUrl: 'https://api.trndup.app',\n * getToken: async () => {\n * const user = auth().currentUser;\n * return user ? await user.getIdToken() : null;\n * },\n * onAuthFailure: () => {\n * // Handle auth failure - navigate to login\n * navigation.navigate('Login');\n * },\n * });\n * \n * // Type-safe API calls\n * const user = await sdk.auth.getCurrentUser();\n * const videos = await sdk.youtube.getVideos({ limit: 10 });\n * const healthScore = await sdk.youtube.getHealthScore({ range: '30d' });\n * ```\n */\n\nimport { TrndUpClient, TrndUpClientConfig } from './client';\nimport { AuthModule } from './modules/auth';\nimport { YouTubeModule } from './modules/youtube';\nimport { InstagramModule } from './modules/instagram';\nimport { SocialModule } from './modules/social';\nimport { InsightsModule } from './modules/insights';\nimport { ActivityModule } from './modules/activity';\n\nexport class TrndUpSDK extends TrndUpClient {\n public auth: AuthModule;\n public youtube: YouTubeModule;\n public instagram: InstagramModule;\n public social: SocialModule;\n public insights: InsightsModule;\n public activity: ActivityModule;\n\n constructor(config: TrndUpClientConfig) {\n super(config);\n\n // Initialize all modules\n this.auth = new AuthModule(this);\n this.youtube = new YouTubeModule(this);\n this.instagram = new InstagramModule(this);\n this.social = new SocialModule(this);\n this.insights = new InsightsModule(this);\n this.activity = new ActivityModule(this);\n }\n}\n\n// Re-export types and classes\nexport type { TrndUpClientConfig, RequestOptions } from './client';\nexport { TrndUpApiError, TrndUpNetworkError } from './client';\n\n// Re-export all type namespaces\nexport type { Auth, Social, YouTube, Instagram, Insights, Activity } from './types';\n\n// Re-export OAuth scopes for configuring Google Sign-In\nexport { YOUTUBE_SCOPES, INSTAGRAM_SCOPES } from './types';\n\n// Version\nexport const SDK_VERSION = '1.0.0';\n"]}
1
+ {"version":3,"sources":["../client.ts","../modules/auth.ts","../modules/youtube.ts","../modules/instagram.ts","../modules/social.ts","../modules/insights.ts","../modules/activity.ts","../types.ts","../index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * TrndUp SDK - Base Client\n * \n * Core HTTP client with Firebase authentication support,\n * error handling, and automatic retries.\n */\n\nimport type { ApiResponse, ApiErrorResponse, ApiSuccessResponse } from './types';\n\n// =============================================================================\n// CONFIGURATION\n// =============================================================================\n\nexport interface TrndUpClientConfig {\n /** Base URL for the API (e.g., 'https://api.trndup.app' or 'http://localhost:3000') */\n baseUrl: string;\n \n /** Function to get the current Firebase ID token */\n getToken: () => string | null | Promise<string | null>;\n \n /** Called when auth completely fails (user needs to re-login) */\n onAuthFailure?: () => void | Promise<void>;\n \n /** Called on any API error */\n onError?: (error: ApiErrorResponse, endpoint: string) => void;\n \n /** Custom headers to include in all requests */\n defaultHeaders?: Record<string, string>;\n \n /** Request timeout in milliseconds (default: 30000) */\n timeout?: number;\n \n /** Enable debug logging (default: false) */\n debug?: boolean;\n}\n\nexport interface RequestOptions {\n /** Skip authentication for this request */\n skipAuth?: boolean;\n \n /** Additional headers for this request */\n headers?: Record<string, string>;\n \n /** AbortController signal for cancellation */\n signal?: AbortSignal;\n \n /** Custom timeout for this request */\n timeout?: number;\n}\n\ninterface RequestConfig extends RequestOptions {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n path: string;\n params?: Record<string, string | number | boolean | undefined>;\n body?: unknown;\n}\n\n// =============================================================================\n// ERROR CLASSES\n// =============================================================================\n\nexport class TrndUpApiError extends Error {\n constructor(\n public readonly response: ApiErrorResponse,\n public readonly status: number,\n public readonly endpoint: string\n ) {\n super(response.error);\n this.name = 'TrndUpApiError';\n }\n \n get code(): string | undefined {\n return this.response.code;\n }\n\n get metadata(): Record<string, unknown> | undefined {\n return this.response.metadata;\n }\n\n /** Check if error is due to authentication failure */\n isAuthError(): boolean {\n return this.status === 401 || this.code === 'AUTH_EXPIRED' || this.code === 'AUTH_INVALID';\n }\n\n /** Check if error is due to rate limiting */\n isRateLimitError(): boolean {\n return this.status === 429 || this.code === 'RATE_LIMITED';\n }\n}\n\nexport class TrndUpNetworkError extends Error {\n constructor(\n public readonly originalError: Error,\n public readonly endpoint: string\n ) {\n super(`Network error calling ${endpoint}: ${originalError.message}`);\n this.name = 'TrndUpNetworkError';\n }\n}\n\n// =============================================================================\n// BASE CLIENT\n// =============================================================================\n\nexport class TrndUpClient {\n private config: Required<Omit<TrndUpClientConfig, 'onAuthFailure' | 'onError' | 'defaultHeaders' | 'debug'>> & {\n onAuthFailure?: TrndUpClientConfig['onAuthFailure'];\n onError?: TrndUpClientConfig['onError'];\n defaultHeaders?: Record<string, string>;\n debug?: boolean;\n };\n\n // Module instances (imported by subclasses or external modules)\n public auth!: any;\n public youtube!: any;\n public instagram!: any;\n public social!: any;\n public insights!: any;\n\n constructor(config: TrndUpClientConfig) {\n this.config = {\n baseUrl: config.baseUrl.replace(/\\/$/, ''), // Remove trailing slash\n getToken: config.getToken,\n timeout: config.timeout ?? 30000,\n onAuthFailure: config.onAuthFailure,\n onError: config.onError,\n defaultHeaders: config.defaultHeaders,\n debug: config.debug ?? false,\n };\n }\n\n // =============================================================================\n // PUBLIC REQUEST METHODS\n // =============================================================================\n\n async get<T = unknown>(\n path: string,\n params?: Record<string, string | number | boolean | undefined>,\n options?: RequestOptions\n ): Promise<T> {\n return this.request<T>({ method: 'GET', path, params, ...options });\n }\n\n async post<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<T> {\n return this.request<T>({ method: 'POST', path, body, ...options });\n }\n\n async put<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<T> {\n return this.request<T>({ method: 'PUT', path, body, ...options });\n }\n\n async patch<T = unknown>(\n path: string,\n body?: unknown,\n options?: RequestOptions\n ): Promise<T> {\n return this.request<T>({ method: 'PATCH', path, body, ...options });\n }\n\n async delete<T = unknown>(\n path: string,\n options?: RequestOptions\n ): Promise<T> {\n return this.request<T>({ method: 'DELETE', path, ...options });\n }\n\n // =============================================================================\n // CORE REQUEST LOGIC\n // =============================================================================\n\n private async request<T>(config: RequestConfig): Promise<T> {\n const { method, path, params, body, skipAuth, headers, signal, timeout } = config;\n\n // Build URL\n const url = this.buildUrl(path, params);\n\n // Build headers\n const requestHeaders = await this.buildHeaders(skipAuth, headers);\n\n // Build request init\n const init: RequestInit = {\n method,\n headers: requestHeaders,\n signal,\n };\n\n if (body && method !== 'GET') {\n init.body = JSON.stringify(body);\n }\n\n // Add timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n timeout ?? this.config.timeout\n );\n\n try {\n if (this.config.debug) {\n console.log(`[TrndUp SDK] ${method} ${url}`, { body, headers: requestHeaders });\n }\n\n const response = await fetch(url, {\n ...init,\n signal: signal ?? controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n return await this.handleResponse<T>(response, path);\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error instanceof TrndUpApiError) {\n throw error;\n }\n\n // Network error\n const networkError = new TrndUpNetworkError(\n error as Error,\n path\n );\n\n if (this.config.debug) {\n console.error('[TrndUp SDK] Network error:', networkError);\n }\n\n throw networkError;\n }\n }\n\n private buildUrl(path: string, params?: Record<string, string | number | boolean | undefined>): string {\n const url = new URL(`${this.config.baseUrl}${path}`);\n\n if (params) {\n Object.entries(params).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n url.searchParams.append(key, String(value));\n }\n });\n }\n\n return url.toString();\n }\n\n private async buildHeaders(\n skipAuth?: boolean,\n additionalHeaders?: Record<string, string>\n ): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this.config.defaultHeaders,\n ...additionalHeaders,\n };\n\n // Add Firebase ID token for authentication\n if (!skipAuth) {\n const token = await this.config.getToken();\n if (token) {\n headers['Authorization'] = `Bearer ${token}`;\n }\n }\n\n return headers;\n }\n\n private async handleResponse<T>(response: Response, endpoint: string): Promise<T> {\n const contentType = response.headers.get('content-type');\n const isJson = contentType?.includes('application/json');\n\n if (!response.ok) {\n // Try to parse error response\n let errorResponse: ApiErrorResponse;\n\n if (isJson) {\n try {\n const parsed = await response.json();\n errorResponse = parsed as ApiErrorResponse;\n } catch {\n errorResponse = {\n success: false,\n error: response.statusText || 'Unknown error',\n code: `HTTP_${response.status}`,\n };\n }\n } else {\n const text = await response.text();\n errorResponse = {\n success: false,\n error: text || response.statusText || 'Unknown error',\n code: `HTTP_${response.status}`,\n };\n }\n\n const apiError = new TrndUpApiError(errorResponse, response.status, endpoint);\n\n // Call error handler\n if (this.config.onError) {\n this.config.onError(errorResponse, endpoint);\n }\n\n // Call auth failure handler for 401 errors\n if (apiError.isAuthError() && this.config.onAuthFailure) {\n await this.config.onAuthFailure();\n }\n\n if (this.config.debug) {\n console.error('[TrndUp SDK] API error:', apiError);\n }\n\n throw apiError;\n }\n\n // Parse success response\n if (isJson) {\n const parsed = await response.json();\n const jsonResponse = parsed as ApiResponse<T>;\n\n if (jsonResponse.success === false) {\n // This shouldn't happen with 2xx status, but handle it\n throw new TrndUpApiError(jsonResponse, response.status, endpoint);\n }\n\n return (jsonResponse as ApiSuccessResponse<T>).data;\n }\n\n // Non-JSON response\n return (await response.text()) as unknown as T;\n }\n}\n","/**\n * TrndUp SDK - Auth Module\n * \n * Firebase authentication methods with Twilio phone OTP\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { Auth } from '../types';\n\nexport class AuthModule {\n constructor(private client: TrndUpClient) {}\n\n // =========================================================================\n // PHONE OTP AUTHENTICATION (via Twilio - no reCAPTCHA needed)\n // =========================================================================\n\n /**\n * Send OTP verification code to phone number\n * POST /auth/phone/send-otp\n * \n * @param phoneNumber - Phone number (E.164 format recommended, e.g., +14155551234)\n */\n async sendOTP(phoneNumber: string): Promise<Auth.SendOTPResponse> {\n return this.client.post<Auth.SendOTPResponse>(\n '/auth/phone/send-otp', \n { phoneNumber }, \n { skipAuth: true }\n );\n }\n\n /**\n * Verify OTP and get Firebase custom token\n * POST /auth/phone/verify-otp\n * \n * After calling this, use Firebase signInWithCustomToken(customToken)\n * to complete authentication on the client.\n * \n * @param phoneNumber - Phone number that received the OTP\n * @param code - 6-digit verification code\n */\n async verifyOTP(phoneNumber: string, code: string): Promise<Auth.VerifyOTPResponse> {\n return this.client.post<Auth.VerifyOTPResponse>(\n '/auth/phone/verify-otp',\n { phoneNumber, code },\n { skipAuth: true }\n );\n }\n\n // =========================================================================\n // FIREBASE ID TOKEN AUTHENTICATION\n // =========================================================================\n\n /**\n * Login with Firebase ID token (for Google, Apple, Email/Password)\n * POST /auth/login\n */\n async login(idToken: string): Promise<Auth.LoginResponse> {\n return this.client.post<Auth.LoginResponse>('/auth/login', { idToken }, { skipAuth: true });\n }\n\n /**\n * Get current authenticated user's profile\n * GET /auth/me\n */\n async getCurrentUser(): Promise<Auth.CurrentUserResponse> {\n return this.client.get<Auth.CurrentUserResponse>('/auth/me');\n }\n\n /**\n * Update user profile\n * PATCH /auth/me\n */\n async updateProfile(data: Auth.UpdateProfileRequest): Promise<Auth.UpdateProfileResponse> {\n return this.client.patch<Auth.UpdateProfileResponse>('/auth/me', data);\n }\n\n /**\n * Logout (acknowledge logout on backend)\n * POST /auth/logout\n */\n async logout(): Promise<{ message: string }> {\n return this.client.post<{ message: string }>('/auth/logout');\n }\n\n /**\n * Check auth service status\n * GET /auth/status\n */\n async getStatus(): Promise<{ success: boolean; message: string }> {\n return this.client.get('/auth/status', undefined, { skipAuth: true });\n }\n\n /**\n * Get user's platform connection status\n * GET /user/platforms/status\n */\n async getPlatformStatus(): Promise<Auth.PlatformStatus> {\n return this.client.get<Auth.PlatformStatus>('/user/platforms/status');\n }\n\n // =========================================================================\n // OAUTH FLOWS\n // =========================================================================\n\n /**\n * Get Facebook OAuth URL for connecting Instagram Business accounts\n * GET /auth/facebook\n * \n * After receiving the authUrl, redirect/open this URL in a browser.\n * User will authenticate with Facebook and grant Instagram permissions.\n * After completion, user is redirected to the specified redirect_uri.\n * \n * @param redirectUri - URL to redirect to after OAuth (your app's deep link)\n * @returns OAuth authorization URL to redirect user to\n */\n async getFacebookOAuthUrl(redirectUri?: string): Promise<Auth.OAuthUrlResponse> {\n const params = redirectUri ? { redirect_uri: redirectUri } : undefined;\n return this.client.get<Auth.OAuthUrlResponse>('/auth/facebook', params);\n }\n\n /**\n * Get Instagram OAuth URL for connecting Instagram Professional accounts\n * GET /auth/instagram\n * \n * After receiving the authUrl, redirect/open this URL in a browser.\n * User will authenticate with Instagram and grant permissions.\n * After completion, user is redirected to the specified redirect_uri.\n * \n * @param redirectUri - URL to redirect to after OAuth (your app's deep link)\n * @returns OAuth authorization URL to redirect user to\n */\n async getInstagramOAuthUrl(redirectUri?: string): Promise<Auth.OAuthUrlResponse> {\n const params = redirectUri ? { redirect_uri: redirectUri } : undefined;\n return this.client.get<Auth.OAuthUrlResponse>('/auth/instagram', params);\n }\n\n /**\n * Build OAuth URL for Facebook (Instagram Business)\n * Convenience method that returns just the URL string\n * \n * @param redirectUri - URL to redirect to after OAuth\n */\n async buildFacebookOAuthUrl(redirectUri?: string): Promise<string> {\n const response = await this.getFacebookOAuthUrl(redirectUri);\n return response.authUrl;\n }\n\n /**\n * Build OAuth URL for Instagram\n * Convenience method that returns just the URL string\n * \n * @param redirectUri - URL to redirect to after OAuth\n */\n async buildInstagramOAuthUrl(redirectUri?: string): Promise<string> {\n const response = await this.getInstagramOAuthUrl(redirectUri);\n return response.authUrl;\n }\n}\n","/**\n * TrndUp SDK - YouTube Module\n * \n * YouTube analytics and data methods\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { YouTube } from '../types';\n\nexport class YouTubeModule {\n constructor(private client: TrndUpClient) {}\n\n /**\n * Get YouTube initialization status (channel sync)\n * Check if user needs to init or has already synced channel\n * \n * GET /v1/platforms/youtube/init/status\n */\n async getInitStatus(): Promise<YouTube.InitStatusResponse> {\n return this.client.get<YouTube.InitStatusResponse>('/v1/platforms/youtube/init/status');\n }\n\n /**\n * Initialize YouTube channel sync\n * Fetches channel info and stores in database\n * \n * POST /v1/platforms/youtube/init\n */\n async initialize(): Promise<YouTube.InitResponse> {\n return this.client.post<YouTube.InitResponse>('/v1/platforms/youtube/init');\n }\n\n /**\n * Get videos sync status\n * Check if videos need to be synced\n * \n * GET /v1/platforms/youtube/videos/sync/status\n */\n async getVideosSyncStatus(): Promise<YouTube.VideosSyncStatusResponse> {\n return this.client.get<YouTube.VideosSyncStatusResponse>('/v1/platforms/youtube/videos/sync/status');\n }\n\n /**\n * Sync all videos from YouTube\n * Fetches all videos and stores in database\n * Returns video counts and oldest video date (for analytics start date)\n * \n * POST /v1/platforms/youtube/videos/sync\n */\n async syncVideos(): Promise<YouTube.VideosSyncResponse> {\n return this.client.post<YouTube.VideosSyncResponse>('/v1/platforms/youtube/videos/sync');\n }\n\n /**\n * Get YouTube videos from database\n * GET /v1/platforms/youtube/videos\n */\n async getVideos(params?: YouTube.GetVideosParams): Promise<YouTube.GetVideosResponse> {\n return this.client.get<YouTube.GetVideosResponse>(\n '/v1/platforms/youtube/videos', \n params as Record<string, string | number | boolean | undefined>\n );\n }\n\n /**\n * Get specific video by ID\n * GET /v1/platforms/youtube/videos/:videoId\n */\n async getVideo(videoId: string): Promise<YouTube.Video> {\n return this.client.get<YouTube.Video>(`/v1/platforms/youtube/videos/${videoId}`);\n }\n\n /**\n * Get channel metrics\n * GET /v1/platforms/youtube/channel/metrics\n */\n async getChannelMetrics(): Promise<YouTube.ChannelMetrics> {\n return this.client.get<YouTube.ChannelMetrics>('/v1/platforms/youtube/channel/metrics');\n }\n\n /**\n * Get channel health score\n * GET /v1/platforms/youtube/health\n */\n async getHealthScore(params?: YouTube.GetHealthScoreParams): Promise<YouTube.HealthScore> {\n return this.client.get<YouTube.HealthScore>(\n '/v1/platforms/youtube/health', \n params as Record<string, string | number | boolean | undefined>\n );\n }\n\n /**\n * Refresh YouTube data\n * POST /v1/platforms/youtube/refresh\n */\n async refresh(): Promise<{ message: string; jobId?: string }> {\n return this.client.post<{ message: string; jobId?: string }>('/v1/platforms/youtube/refresh');\n }\n\n // =========================================================================\n // UNIFIED SYNC STATUS\n // =========================================================================\n\n /**\n * Get unified sync status for all YouTube data types.\n * \n * Use this for:\n * - Checking if UI cache needs refresh (compare lastSyncAt)\n * - Showing sync status in settings/debug screens\n * - Detecting if background sync is running\n * \n * GET /v1/platforms/youtube/sync/status\n * \n * @example\n * ```typescript\n * const status = await client.youtube.getUnifiedSyncStatus();\n * \n * // Check if backend synced since our last fetch\n * const storedLastSync = await AsyncStorage.getItem('videosLastSync');\n * if (status.syncs.videos.lastSyncAt > storedLastSync) {\n * const videos = await client.youtube.getVideos();\n * await AsyncStorage.setItem('videosLastSync', status.syncs.videos.lastSyncAt);\n * }\n * ```\n */\n async getUnifiedSyncStatus(): Promise<YouTube.UnifiedSyncStatusResponse> {\n return this.client.get<YouTube.UnifiedSyncStatusResponse>(\n '/v1/platforms/youtube/sync/status'\n );\n }\n\n // =========================================================================\n // CHANNEL ANALYTICS SYNC\n // =========================================================================\n\n /**\n * Get channel analytics sync status\n * GET /v1/platforms/youtube/analytics/channel/sync/status\n */\n async getChannelAnalyticsSyncStatus(): Promise<YouTube.ChannelAnalyticsSyncStatusResponse> {\n return this.client.get<YouTube.ChannelAnalyticsSyncStatusResponse>(\n '/v1/platforms/youtube/analytics/channel/sync/status'\n );\n }\n\n /**\n * Sync channel analytics (daily + weekly aggregates)\n * Fetches from YouTube Analytics API and stores in database\n * Uses oldest video date as start date\n * \n * POST /v1/platforms/youtube/analytics/channel/sync\n */\n async syncChannelAnalytics(): Promise<YouTube.ChannelAnalyticsSyncResponse> {\n return this.client.post<YouTube.ChannelAnalyticsSyncResponse>(\n '/v1/platforms/youtube/analytics/channel/sync'\n );\n }\n\n // =========================================================================\n // VIDEO ANALYTICS SYNC\n // =========================================================================\n\n /**\n * Get video analytics sync status\n * GET /v1/platforms/youtube/analytics/videos/sync/status\n */\n async getVideoAnalyticsSyncStatus(): Promise<YouTube.VideoAnalyticsSyncStatusResponse> {\n return this.client.get<YouTube.VideoAnalyticsSyncStatusResponse>(\n '/v1/platforms/youtube/analytics/videos/sync/status'\n );\n }\n\n /**\n * Sync analytics for all videos\n * Fetches from YouTube Analytics API and updates video records\n * \n * POST /v1/platforms/youtube/analytics/videos/sync\n */\n async syncVideoAnalytics(): Promise<YouTube.VideoAnalyticsSyncResponse> {\n return this.client.post<YouTube.VideoAnalyticsSyncResponse>(\n '/v1/platforms/youtube/analytics/videos/sync'\n );\n }\n}\n","/**\n * TrndUp SDK - Instagram Module\n * \n * Instagram analytics and data methods\n * Route structure mirrors YouTube for UI consistency\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { Instagram } from '../types';\n\nexport class InstagramModule {\n constructor(private client: TrndUpClient) {}\n\n // ===== UNIFIED SYNC STATUS =====\n\n /**\n * Get unified sync status for all Instagram data types\n * GET /v1/platforms/instagram/sync/status\n * \n * Shows when each type was last synced and when next sync is due.\n */\n async getSyncStatus(): Promise<Instagram.UnifiedSyncStatusResponse> {\n return this.client.get<Instagram.UnifiedSyncStatusResponse>('/v1/platforms/instagram/sync/status');\n }\n\n // ===== INITIALIZATION =====\n\n /**\n * Initialize Instagram data sync\n * POST /v1/platforms/instagram/init\n */\n async initialize(): Promise<Instagram.InitResponse> {\n return this.client.post<Instagram.InitResponse>('/v1/platforms/instagram/init');\n }\n\n /**\n * Get Instagram initialization status\n * GET /v1/platforms/instagram/init/status\n */\n async getInitStatus(): Promise<Instagram.InitStatusResponse> {\n return this.client.get<Instagram.InitStatusResponse>('/v1/platforms/instagram/init/status');\n }\n\n // ===== POSTS SYNC =====\n\n /**\n * Sync all posts from Instagram to database\n * POST /v1/platforms/instagram/posts/sync\n */\n async syncPosts(): Promise<Instagram.PostsSyncResponse> {\n return this.client.post<Instagram.PostsSyncResponse>('/v1/platforms/instagram/posts/sync');\n }\n\n /**\n * Check posts sync status without triggering a sync\n * GET /v1/platforms/instagram/posts/sync/status\n */\n async getPostsSyncStatus(): Promise<Instagram.PostsSyncStatusResponse> {\n return this.client.get<Instagram.PostsSyncStatusResponse>('/v1/platforms/instagram/posts/sync/status');\n }\n\n // ===== ACCOUNT ANALYTICS SYNC =====\n\n /**\n * Sync account-level analytics (reach, impressions, engagement)\n * POST /v1/platforms/instagram/analytics/account/sync\n * @param range Time range: '7d', '14d', '30d' (default '30d')\n */\n async syncAccountAnalytics(params?: { range?: string }): Promise<Instagram.AccountAnalyticsSyncResponse> {\n const queryParams = params?.range ? `?range=${params.range}` : '';\n return this.client.post<Instagram.AccountAnalyticsSyncResponse>(\n `/v1/platforms/instagram/analytics/account/sync${queryParams}`\n );\n }\n\n /**\n * Get account analytics sync status\n * GET /v1/platforms/instagram/analytics/account/sync/status\n */\n async getAccountAnalyticsSyncStatus(): Promise<Instagram.AccountAnalyticsSyncStatusResponse> {\n return this.client.get<Instagram.AccountAnalyticsSyncStatusResponse>(\n '/v1/platforms/instagram/analytics/account/sync/status'\n );\n }\n\n // ===== POST INSIGHTS SYNC =====\n\n /**\n * Queue post-level insights sync for all posts (BullMQ-based)\n * POST /v1/platforms/instagram/analytics/posts/sync\n * \n * Jobs are queued and processed in the background.\n * Poll getPostInsightsSyncStatus() to check progress.\n * \n * @returns Response with jobsQueued count and status\n */\n async syncPostInsights(): Promise<Instagram.PostInsightsSyncResponse> {\n return this.client.post<Instagram.PostInsightsSyncResponse>(\n '/v1/platforms/instagram/analytics/posts/sync'\n );\n }\n\n /**\n * Get post insights sync status\n * GET /v1/platforms/instagram/analytics/posts/sync/status\n * \n * Use to poll progress while sync is in_progress\n */\n async getPostInsightsSyncStatus(): Promise<Instagram.PostInsightsSyncStatusResponse> {\n return this.client.get<Instagram.PostInsightsSyncStatusResponse>(\n '/v1/platforms/instagram/analytics/posts/sync/status'\n );\n }\n\n // ===== DATA RETRIEVAL =====\n\n /**\n * Get user profile info\n * GET /v1/platforms/instagram/profile\n */\n async getProfile(): Promise<Instagram.ProfileResponse> {\n return this.client.get<Instagram.ProfileResponse>('/v1/platforms/instagram/profile');\n }\n\n /**\n * Get account overview for dashboard\n * GET /v1/platforms/instagram/account/overview\n * @param range Time range: '7d', '14d', '28d', '30d'\n */\n async getAccountOverview(params?: Instagram.GetAccountOverviewParams): Promise<Instagram.AccountOverviewResponse> {\n return this.client.get<Instagram.AccountOverviewResponse>(\n '/v1/platforms/instagram/account/overview',\n params as Record<string, string | number | boolean | undefined>\n );\n }\n\n /**\n * Get account-level insights (historical data)\n * GET /v1/platforms/instagram/account/insights\n * @param range Time range: '7d', '14d', '28d', '30d'\n */\n async getAccountInsights(params?: Instagram.GetAccountInsightsParams): Promise<Instagram.AccountInsightsResponse> {\n return this.client.get<Instagram.AccountInsightsResponse>(\n '/v1/platforms/instagram/account/insights',\n params as Record<string, string | number | boolean | undefined>\n );\n }\n\n /**\n * Get Instagram posts\n * GET /v1/platforms/instagram/posts\n */\n async getPosts(params?: Instagram.GetPostsParams): Promise<Instagram.GetPostsResponse> {\n return this.client.get<Instagram.GetPostsResponse>(\n '/v1/platforms/instagram/posts', \n params as Record<string, string | number | boolean | undefined>\n );\n }\n\n /**\n * Get insights for a specific post\n * GET /v1/platforms/instagram/posts/:postId/insights\n */\n async getPostInsights(postId: string): Promise<Instagram.PostInsightsResponse> {\n return this.client.get<Instagram.PostInsightsResponse>(\n `/v1/platforms/instagram/posts/${postId}/insights`\n );\n }\n\n /**\n * Get active stories\n * GET /v1/platforms/instagram/stories\n */\n async getStories(): Promise<Instagram.StoriesResponse> {\n return this.client.get<Instagram.StoriesResponse>('/v1/platforms/instagram/stories');\n }\n\n // ===== LEGACY SYNC (backward compat) =====\n\n /**\n * Trigger manual data sync (legacy - use specific sync methods)\n * POST /v1/platforms/instagram/sync\n * @deprecated Use syncPosts, syncAccountAnalytics, syncPostInsights instead\n */\n async sync(): Promise<Instagram.SyncResponse> {\n return this.client.post<Instagram.SyncResponse>('/v1/platforms/instagram/sync');\n }\n}\n","/**\n * TrndUp SDK - Social Module\n * \n * Social account linking methods\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { Social } from '../types';\n\nexport class SocialModule {\n constructor(private client: TrndUpClient) {}\n\n /**\n * Link YouTube account\n * POST /social/link/youtube\n */\n async linkYouTube(data: Social.LinkYouTubeRequest): Promise<Social.LinkAccountResponse> {\n return this.client.post<Social.LinkAccountResponse>('/social/link/youtube', data);\n }\n\n /**\n * Link Instagram account\n * POST /social/link/instagram\n */\n async linkInstagram(data: Social.LinkInstagramRequest): Promise<Social.LinkAccountResponse> {\n return this.client.post<Social.LinkAccountResponse>('/social/link/instagram', data);\n }\n\n /**\n * Get all connected social accounts\n * GET /social/accounts\n */\n async getConnectedAccounts(): Promise<Social.ConnectedAccount[]> {\n return this.client.get<Social.ConnectedAccount[]>('/social/accounts');\n }\n\n /**\n * Unlink a social account\n * DELETE /social/unlink/:provider\n */\n async unlinkAccount(provider: string): Promise<{ message: string }> {\n return this.client.delete<{ message: string }>(`/social/unlink/${provider}`);\n }\n}\n","/**\n * TrndUp SDK - Insights Module\n * \n * Cross-platform insights and analytics\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { Insights } from '../types';\n\nexport class InsightsModule {\n constructor(private client: TrndUpClient) {}\n\n /**\n * Get cross-platform metrics\n * GET /v1/insights/overview\n */\n async getOverview(): Promise<Insights.CrossPlatformMetrics> {\n return this.client.get<Insights.CrossPlatformMetrics>('/v1/insights/overview');\n }\n\n /**\n * Get trending content across all platforms\n * GET /v1/insights/trending\n */\n async getTrending(): Promise<Insights.TrendingContent> {\n return this.client.get<Insights.TrendingContent>('/v1/insights/trending');\n }\n\n // =========================================================================\n // YOUTUBE MOMENTUM\n // =========================================================================\n\n /**\n * Get YouTube channel momentum status\n * \n * Returns a 0-100 score indicating channel momentum:\n * - 50 = Steady (no change week over week)\n * - 0 = Severe decline\n * - 100 = Explosive growth\n * \n * Results are cached for 12 hours. Use `refreshYouTubeMomentum()` to force recalculation.\n * \n * @example\n * ```typescript\n * const momentum = await client.insights.getYouTubeMomentum();\n * \n * console.log(`Score: ${momentum.score}/100`);\n * console.log(`Status: ${momentum.status}`); // 'surging' | 'rising' | 'steady' | 'cooling' | 'dropping'\n * console.log(`Trend: ${momentum.trend}`); // 'accelerating' | 'holding' | 'decelerating'\n * console.log(momentum.insight); // Human-readable summary\n * \n * // Show breakdown\n * console.log(`Views: ${momentum.breakdown.views.change}%`);\n * console.log(`Engagement: ${momentum.breakdown.engagement.change}%`);\n * ```\n * \n * GET /v1/insights/youtube/momentum\n */\n async getYouTubeMomentum(): Promise<Insights.MomentumStatusResponse> {\n return this.client.get<Insights.MomentumStatusResponse>('/v1/insights/youtube/momentum');\n }\n\n /**\n * Get YouTube momentum history for graphing\n * \n * Returns historical momentum scores, oldest first.\n * Use this to plot momentum trends over time.\n * \n * @param params - Query parameters\n * @param params.limit - Max data points (default: 30, max: 90)\n * \n * @example\n * ```typescript\n * const { history } = await client.insights.getYouTubeMomentumHistory({ limit: 30 });\n * \n * // Plot on a chart\n * const chartData = history.map(point => ({\n * x: new Date(point.date),\n * y: point.score,\n * label: point.status\n * }));\n * ```\n * \n * GET /v1/insights/youtube/momentum/history\n */\n async getYouTubeMomentumHistory(\n params?: Insights.GetMomentumHistoryParams\n ): Promise<Insights.MomentumHistoryResponse> {\n const query = params?.limit ? `?limit=${params.limit}` : '';\n return this.client.get<Insights.MomentumHistoryResponse>(\n `/v1/insights/youtube/momentum/history${query}`\n );\n }\n\n // =========================================================================\n // YOUTUBE BEST PERFORMING VIDEO\n // =========================================================================\n\n /**\n * Get the best performing video in a time period\n * \n * Identifies your top video by views and explains WHY it performed well.\n * \n * @param params - Query parameters\n * @param params.period - Days to analyze (1-90, default: 30)\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeBestVideo({ period: 30 });\n * \n * if (result.hasData) {\n * console.log(`🏆 ${result.data.topVideo.title}`);\n * console.log(`Views: ${result.data.metrics.views}`);\n * console.log(`${result.data.comparison.vsChannelAverage.description}`);\n * console.log(result.data.insight);\n * \n * // Show how it was calculated\n * console.log(result._meta.howWeCalculatedThis.method);\n * } else {\n * console.log(result.suggestion);\n * }\n * ```\n * \n * GET /v1/insights/youtube/best-video\n */\n async getYouTubeBestVideo(\n params?: Insights.GetBestVideoParams\n ): Promise<Insights.BestPerformingVideoResponse> {\n const query = params?.period ? `?period=${params.period}` : '';\n return this.client.get<Insights.BestPerformingVideoResponse>(\n `/v1/insights/youtube/best-video${query}`\n );\n }\n\n // =========================================================================\n // YOUTUBE CONTENT DECAY\n // =========================================================================\n\n /**\n * Find videos that are losing momentum\n * \n * Compares current period vs previous period to identify declining videos.\n * Provides actionable suggestions for each decaying video.\n * \n * @param params - Query parameters\n * @param params.period - Days per comparison period (3-14, default: 7)\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeContentDecay({ period: 7 });\n * \n * if (result.hasData) {\n * console.log(result.data.insight);\n * console.log(`${result.data.summary.totalDecaying} videos declining`);\n * \n * for (const video of result.data.decayingVideos) {\n * console.log(`📉 ${video.title}: ${video.decayRate}%`);\n * console.log(` ${video.suggestion}`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/content-decay\n */\n async getYouTubeContentDecay(\n params?: Insights.GetContentDecayParams\n ): Promise<Insights.ContentDecayResponse> {\n const query = params?.period ? `?period=${params.period}` : '';\n return this.client.get<Insights.ContentDecayResponse>(\n `/v1/insights/youtube/content-decay${query}`\n );\n }\n\n // =========================================================================\n // YOUTUBE AUDIENCE ACTIVITY\n // =========================================================================\n\n /**\n * Get when your audience is most active\n * \n * Unlike generic advice, this is based on YOUR audience's behavior.\n * Analyzes first-day performance by publish day and time to identify\n * when your audience is most engaged.\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeAudienceActivity();\n * \n * if (result.hasData) {\n * console.log(`📅 Most active day: ${result.data.mostActiveDay.day}`);\n * console.log(`⏰ Peak time: ${result.data.mostActiveTimeSlot.label}`);\n * console.log(result.data.insight);\n * \n * // Show day breakdown\n * for (const day of result.data.dayBreakdown) {\n * console.log(`${day.day}: ${day.avgFirstDayViews} avg views`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/audience-activity\n */\n async getYouTubeAudienceActivity(): Promise<Insights.AudienceActivityResponse> {\n return this.client.get<Insights.AudienceActivityResponse>(\n '/v1/insights/youtube/audience-activity'\n );\n }\n\n /**\n * @deprecated Use `getYouTubeAudienceActivity()` instead\n */\n async getYouTubeBestUploadTime(): Promise<Insights.BestUploadTimeResponse> {\n return this.getYouTubeAudienceActivity();\n }\n\n // =========================================================================\n // YOUTUBE FADING HITS\n // =========================================================================\n\n /**\n * Find videos that were hits but are now declining\n * \n * Identifies videos that performed above your channel average initially\n * but have since lost momentum. These are revival opportunities.\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeFadingHits();\n * \n * if (result.hasData) {\n * console.log(result.data.insight);\n * console.log(`${result.data.summary.totalFading} videos could use a boost`);\n * \n * for (const video of result.data.fadingHits) {\n * console.log(`📉 ${video.title}`);\n * console.log(` Peak: ${video.peakPeriod.avgDailyViews} views/day`);\n * console.log(` Now: ${video.currentPeriod.avgDailyViews} views/day`);\n * console.log(` Decline: ${video.decline.percentage}%`);\n * console.log(` Revival potential: ${video.revivalPotential}`);\n * console.log(` ${video.whyRevive}`);\n * for (const suggestion of video.suggestions) {\n * console.log(` 💡 ${suggestion}`);\n * }\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/fading-hits\n */\n async getYouTubeFadingHits(): Promise<Insights.FadingHitsResponse> {\n return this.client.get<Insights.FadingHitsResponse>(\n '/v1/insights/youtube/fading-hits'\n );\n }\n\n // =========================================================================\n // YOUTUBE SUBSCRIBER QUALITY\n // =========================================================================\n\n /**\n * Measure subscriber quality score (0-100)\n * \n * Analyzes whether new subscribers are engaged or just numbers.\n * Helps identify fake growth or low-quality audiences.\n * \n * @param params - Query parameters\n * @param params.period - Days to analyze (7-90, default: 30)\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeSubscriberQuality({ period: 90 });\n * \n * if (result.hasData) {\n * console.log(`Quality Score: ${result.data.qualityScore}/100 (${result.data.scoreLabel})`);\n * console.log(`Retention: ${result.data.metrics.retentionRate}%`);\n * console.log(result.data.insight);\n * \n * // Show recommendations\n * for (const rec of result.data.recommendations) {\n * console.log(`💡 ${rec}`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/subscriber-quality\n */\n async getYouTubeSubscriberQuality(\n params?: Insights.GetSubscriberQualityParams\n ): Promise<Insights.SubscriberQualityResponse> {\n const query = params?.period ? `?period=${params.period}` : '';\n return this.client.get<Insights.SubscriberQualityResponse>(\n `/v1/insights/youtube/subscriber-quality${query}`\n );\n }\n\n // =========================================================================\n // YOUTUBE AUDIENCE FATIGUE\n // =========================================================================\n\n /**\n * Detect if your audience is getting tired of your content\n * \n * Compares key engagement metrics between recent and previous periods\n * to identify declining audience interest before it becomes critical.\n * \n * @param params - Query parameters\n * @param params.period - Days per comparison period (14-90, default: 30)\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeAudienceFatigue({ period: 30 });\n * \n * if (result.hasData) {\n * console.log(`Fatigue Index: ${result.data.fatigueIndex}/100`);\n * console.log(`Level: ${result.data.fatigueLevel}`);\n * console.log(result.data.insight);\n * \n * // Check individual signals\n * if (result.data.signals.watchTimePerView.indicatesFatigue) {\n * console.log('⚠️ Watch time is declining');\n * }\n * \n * // Get recommendations\n * for (const rec of result.data.recommendations) {\n * console.log(`💡 ${rec}`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/audience-fatigue\n */\n async getYouTubeAudienceFatigue(\n params?: Insights.GetAudienceFatigueParams\n ): Promise<Insights.AudienceFatigueResponse> {\n const query = params?.period ? `?period=${params.period}` : '';\n return this.client.get<Insights.AudienceFatigueResponse>(\n `/v1/insights/youtube/audience-fatigue${query}`\n );\n }\n\n // =========================================================================\n // YOUTUBE OPTIMAL VIDEO LENGTH\n // =========================================================================\n\n /**\n * Find the ideal video length for YOUR audience\n * \n * Unlike generic \"8-12 minutes is best\" advice, this is personalized\n * based on how YOUR videos perform at different lengths.\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeOptimalLength();\n * \n * if (result.hasData) {\n * console.log(`Optimal: ${result.data.optimalLength.bucket}`);\n * console.log(`Confidence: ${result.data.optimalLength.confidence}`);\n * console.log(result.data.insight);\n * \n * // Show breakdown by length\n * for (const bucket of result.data.breakdown) {\n * const marker = bucket.isOptimal ? '🏆' : ' ';\n * console.log(`${marker} ${bucket.bucket}: ${bucket.avgRetention}% retention, ${bucket.avgEngagement}% engagement`);\n * }\n * \n * // Recommendations\n * for (const rec of result.data.recommendations) {\n * console.log(`💡 ${rec}`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/optimal-length\n */\n async getYouTubeOptimalLength(): Promise<Insights.OptimalVideoLengthResponse> {\n return this.client.get<Insights.OptimalVideoLengthResponse>(\n '/v1/insights/youtube/optimal-length'\n );\n }\n\n // =========================================================================\n // YOUTUBE UPLOAD CONSISTENCY\n // =========================================================================\n\n /**\n * Analyze your upload schedule consistency\n * \n * Measures how regularly you upload and provides recommendations\n * for maintaining a consistent schedule that helps algorithm performance.\n * \n * @example\n * ```typescript\n * const result = await client.insights.getYouTubeUploadConsistency();\n * \n * if (result.hasData) {\n * console.log(`Consistency Score: ${result.data.consistencyScore}/100`);\n * console.log(`Status: ${result.data.status}`);\n * console.log(`Avg gap: ${result.data.metrics.avgDaysBetweenUploads} days`);\n * console.log(`Days since last upload: ${result.data.metrics.daysSinceLastUpload}`);\n * \n * // Pattern analysis\n * console.log(`Pattern: ${result.data.pattern.typicalGap}`);\n * console.log(`Trend: ${result.data.pattern.trendDescription}`);\n * \n * // Recommendation\n * console.log(`Current: ${result.data.recommendation.current}`);\n * console.log(`Suggested: ${result.data.recommendation.suggested}`);\n * \n * // Monthly breakdown\n * for (const month of result.data.monthlyBreakdown) {\n * console.log(`${month.month}: ${month.uploads} uploads`);\n * }\n * }\n * ```\n * \n * GET /v1/insights/youtube/upload-consistency\n */\n async getYouTubeUploadConsistency(): Promise<Insights.UploadConsistencyResponse> {\n return this.client.get<Insights.UploadConsistencyResponse>(\n '/v1/insights/youtube/upload-consistency'\n );\n }\n\n // =========================================================================\n // YOUTUBE ALL INSIGHTS\n // =========================================================================\n\n /**\n * Get all YouTube insights in one call\n * \n * Efficient for dashboard display - fetches all insights in parallel.\n * Each insight has `hasData: boolean` to check availability.\n * \n * @example\n * ```typescript\n * const all = await client.insights.getYouTubeAllInsights();\n * \n * // Check which insights are available\n * console.log('Available:', all._meta.insightsAvailable);\n * \n * // Use each insight\n * if (all.data.bestPerformingVideo.hasData) {\n * console.log(`Best: ${all.data.bestPerformingVideo.data.topVideo.title}`);\n * }\n * \n * if (all.data.audienceFatigue.hasData) {\n * console.log(`Fatigue: ${all.data.audienceFatigue.data.fatigueLevel}`);\n * }\n * \n * if (all.data.uploadConsistency.hasData) {\n * console.log(`Consistency: ${all.data.uploadConsistency.data.consistencyScore}/100`);\n * }\n * ```\n * \n * GET /v1/insights/youtube/all\n */\n async getYouTubeAllInsights(): Promise<Insights.AllInsightsResponse> {\n return this.client.get<Insights.AllInsightsResponse>('/v1/insights/youtube/all');\n }\n}\n","/**\n * Activity Module\n * \n * View sync job activity and logs\n */\n\nimport type { TrndUpClient } from '../client';\nimport type { ApiSuccessResponse, Activity } from '../types';\n\nexport class ActivityModule {\n constructor(private client: TrndUpClient) {}\n\n /**\n * Get recent activity logs\n * \n * @param params - Optional filters\n * @returns List of activity log entries\n * \n * @example\n * ```typescript\n * // Get all recent logs\n * const { logs } = await client.activity.getLogs();\n * \n * // Get only failed jobs\n * const { logs } = await client.activity.getLogs({ status: 'failed' });\n * \n * // Get video sync jobs only\n * const { logs } = await client.activity.getLogs({ type: 'videos_sync' });\n * ```\n */\n async getLogs(params?: Activity.GetLogsParams): Promise<Activity.LogsResponse> {\n const query = new URLSearchParams();\n if (params?.type) query.set('type', params.type);\n if (params?.status) query.set('status', params.status);\n if (params?.limit) query.set('limit', params.limit.toString());\n \n const queryString = query.toString();\n const url = queryString ? `/v1/activity/logs?${queryString}` : '/v1/activity/logs';\n \n const response = await this.client.get<ApiSuccessResponse<Activity.LogsResponse>>(url);\n return response.data;\n }\n\n /**\n * Get activity summary with counts by type\n * \n * @returns Summary with total/completed/failed counts\n * \n * @example\n * ```typescript\n * const summary = await client.activity.getSummary();\n * console.log(`Total: ${summary.totalJobs}, Failed: ${summary.failed}`);\n * ```\n */\n async getSummary(): Promise<Activity.SummaryResponse> {\n const response = await this.client.get<ApiSuccessResponse<Activity.SummaryResponse>>(\n '/v1/activity/summary'\n );\n return response.data;\n }\n\n /**\n * Get the latest log entry for a specific job type\n * \n * @param jobType - The type of job to get\n * @returns Latest log entry or null if none exists\n * \n * @example\n * ```typescript\n * const latestVideoSync = await client.activity.getLatest('videos_sync');\n * if (latestVideoSync) {\n * console.log(`Last sync: ${latestVideoSync.completedAt}`);\n * }\n * ```\n */\n async getLatest(jobType: Activity.JobType): Promise<Activity.LogEntry | null> {\n const response = await this.client.get<ApiSuccessResponse<Activity.LogEntry | null>>(\n `/v1/activity/logs/${jobType}/latest`\n );\n return response.data;\n }\n}\n","/**\n * TrndUp SDK Types\n * \n * Type definitions for the TrndUp API SDK\n */\n\n// =============================================================================\n// OAUTH SCOPES\n// =============================================================================\n\n/**\n * Required OAuth scopes for YouTube account linking\n * Use these when configuring Google Sign-In in your mobile app\n * \n * @example\n * ```typescript\n * import { GoogleSignin } from '@react-native-google-signin/google-signin';\n * import { YOUTUBE_SCOPES } from '@dracoonghost/trndup-sdk';\n * \n * GoogleSignin.configure({\n * webClientId: 'YOUR_WEB_CLIENT_ID',\n * offlineAccess: true,\n * scopes: YOUTUBE_SCOPES\n * });\n * ```\n */\nexport const YOUTUBE_SCOPES = [\n 'openid',\n 'https://www.googleapis.com/auth/userinfo.profile',\n 'https://www.googleapis.com/auth/userinfo.email',\n 'https://www.googleapis.com/auth/youtube.readonly',\n 'https://www.googleapis.com/auth/yt-analytics.readonly',\n 'https://www.googleapis.com/auth/yt-analytics-monetary.readonly',\n 'https://www.googleapis.com/auth/youtube.force-ssl',\n 'https://www.googleapis.com/auth/youtubepartner'\n];\n\n/**\n * Required OAuth scopes for Instagram account linking\n */\nexport const INSTAGRAM_SCOPES = [\n 'instagram_business_basic',\n 'instagram_business_manage_insights',\n 'instagram_business_manage_comments'\n];\n\n// =============================================================================\n// API RESPONSE WRAPPER\n// =============================================================================\n\nexport interface ApiSuccessResponse<T = unknown> {\n success: true;\n data: T;\n meta?: {\n page?: number;\n limit?: number;\n total?: number;\n };\n}\n\nexport interface ApiErrorResponse {\n success: false;\n error: string;\n code?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport type ApiResponse<T = unknown> = ApiSuccessResponse<T> | ApiErrorResponse;\n\n// =============================================================================\n// AUTH TYPES\n// =============================================================================\n\nexport namespace Auth {\n export interface User {\n id: string;\n firebaseUid?: string;\n email?: string;\n phoneNumber?: string;\n username: string;\n picture?: string;\n emailVerified?: boolean;\n signInProvider?: string; // 'phone', 'google.com', 'password', 'apple.com'\n createdAt: string;\n updatedAt: string;\n }\n\n export interface SocialAccount {\n provider: string;\n accountName?: string;\n username?: string;\n profilePictureUrl?: string;\n connectedAt: string;\n }\n\n // Phone OTP Authentication (Twilio)\n export interface SendOTPRequest {\n phoneNumber: string;\n }\n\n export interface SendOTPResponse {\n message: string;\n expiresIn: number;\n }\n\n export interface VerifyOTPRequest {\n phoneNumber: string;\n code: string;\n }\n\n export interface VerifyOTPResponse {\n customToken: string;\n isNewUser: boolean;\n user: User;\n platformStatus: PlatformStatus;\n }\n\n // Firebase ID Token Login\n export interface LoginRequest {\n idToken: string;\n }\n\n export interface LoginResponse {\n user: User;\n platformStatus: PlatformStatus;\n idToken: string;\n }\n\n export interface CurrentUserResponse {\n user: User;\n socialAccounts: SocialAccount[];\n }\n\n export interface UpdateProfileRequest {\n username?: string;\n picture?: string;\n }\n\n export interface UpdateProfileResponse {\n user: User;\n }\n\n // OAuth Types\n\n /**\n * Response from OAuth URL request\n */\n export interface OAuthUrlResponse {\n /** URL to redirect user to for OAuth consent */\n authUrl: string;\n }\n\n /**\n * OAuth callback result (returned in redirect URL params)\n */\n export interface OAuthCallbackResult {\n success: boolean;\n provider: 'facebook' | 'instagram';\n message?: string;\n error?: string;\n }\n\n /**\n * Status for a single sync step\n */\n export interface SyncStepStatus {\n status: 'never_synced' | 'pending' | 'in_progress' | 'completed' | 'failed';\n lastSyncAt?: string | null;\n }\n\n /**\n * Sync status for a single platform\n */\n export interface PlatformSyncInfo {\n connected: boolean;\n /** Overall sync status (completed only if ALL steps done) */\n syncStatus?: 'never_synced' | 'pending' | 'in_progress' | 'completed' | 'failed';\n lastSyncAt?: string | null;\n /** True if any sync step incomplete - show loading/sync screen */\n needsInit?: boolean;\n /** Next step UI should perform */\n nextStep?: 'channel' | 'videos' | 'channel_analytics' | 'video_analytics' | null;\n /** Per-step status for UI progress display */\n steps?: {\n channel: SyncStepStatus;\n videos: SyncStepStatus;\n channelAnalytics: SyncStepStatus;\n videoAnalytics: SyncStepStatus;\n };\n }\n\n /**\n * Platform connection and sync status returned in login responses\n * Used by UI to decide navigation: onboarding vs loading screen vs dashboard\n */\n export interface PlatformStatus {\n hasConnectedPlatforms: boolean;\n connectedPlatforms: Array<'youtube' | 'instagram'>;\n youtube: PlatformSyncInfo;\n instagram: PlatformSyncInfo;\n }\n}\n\n// =============================================================================\n// SOCIAL TYPES\n// =============================================================================\n\nexport namespace Social {\n /**\n * Link YouTube account request\n * \n * Two options:\n * 1. serverAuthCode (recommended): Backend exchanges for tokens with proper expiry\n * 2. Direct tokens: Frontend provides accessToken (expiresAt defaults to 1 hour)\n */\n export interface LinkYouTubeRequest {\n idToken: string; // Google ID token for verification (always required)\n serverAuthCode?: string; // Google server auth code - backend exchanges for tokens (recommended)\n accessToken?: string; // Google access token (alternative to serverAuthCode)\n refreshToken?: string; // Google refresh token (only with accessToken flow)\n expiresAt?: number; // Token expiry timestamp in ms (only with accessToken flow)\n grantedScopes?: string; // Actual scopes user granted (space-separated)\n platform?: string; // 'ios' | 'android' | 'web'\n environment?: string; // 'development' | 'production'\n }\n\n export interface LinkInstagramRequest {\n accessToken: string;\n }\n\n export interface LinkAccountResponse {\n message: string;\n provider: string;\n accountName?: string;\n }\n\n export interface ConnectedAccount {\n provider: string;\n accountName?: string;\n username?: string;\n profilePictureUrl?: string;\n connectedAt: string;\n lastSyncAt?: string;\n }\n}\n\n// =============================================================================\n// YOUTUBE TYPES\n// =============================================================================\n\nexport namespace YouTube {\n /**\n * Response from GET /v1/platforms/youtube/init/status\n */\n export interface InitStatusResponse {\n needsInit: boolean;\n status: 'NEVER_SYNCED' | 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED' | 'never_synced';\n lastSyncAt: string | null;\n }\n\n /**\n * Response from POST /v1/platforms/youtube/init\n */\n export interface InitResponse {\n channel: {\n channelId: string;\n channelTitle: string;\n subscriberCount: number;\n videoCount: number;\n viewCount: number;\n thumbnailUrl: string | null;\n };\n syncStatus: {\n isFirstSync: boolean;\n lastSyncAt: string | null;\n };\n }\n\n /**\n * Response from GET /v1/platforms/youtube/videos/sync/status\n */\n export interface VideosSyncStatusResponse {\n needsSync: boolean;\n status: 'NEVER_SYNCED' | 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED' | 'never_synced';\n lastSyncAt: string | null;\n videoCount: number;\n oldestVideoDate: string | null;\n analyticsStartDate: string | null;\n }\n\n /**\n * Response from POST /v1/platforms/youtube/videos/sync\n */\n export interface VideosSyncResponse {\n videos: {\n total: number;\n synced: number;\n oldestVideoDate: string | null;\n };\n syncStatus: {\n isFirstSync: boolean;\n lastSyncAt: string;\n };\n }\n\n export interface Video {\n id: string;\n videoId: string;\n title: string;\n description?: string;\n thumbnailUrl?: string;\n publishedAt: string;\n duration?: number;\n viewCount?: number;\n likeCount?: number;\n commentCount?: number;\n engagementRate?: number;\n }\n\n export interface GetVideosParams {\n limit?: number;\n offset?: number;\n sortBy?: 'recent' | 'views' | 'engagement';\n }\n\n export interface GetVideosResponse {\n videos: Video[];\n total: number;\n hasMore: boolean;\n }\n\n export interface ChannelMetrics {\n subscriberCount: number;\n totalViews: number;\n totalVideos: number;\n averageViews: number;\n engagementRate: number;\n }\n\n export interface HealthScore {\n score: number;\n grade: 'A' | 'B' | 'C' | 'D' | 'F';\n metrics: {\n consistency: number;\n engagement: number;\n growth: number;\n quality: number;\n };\n }\n\n export interface GetHealthScoreParams {\n range?: string; // e.g., '7d', '30d', '90d'\n }\n\n // =========================================================================\n // UNIFIED SYNC STATUS\n // =========================================================================\n\n /**\n * Status of a single sync type\n */\n export interface SyncTypeStatus {\n /** Current sync status */\n status: 'never_synced' | 'pending' | 'in_progress' | 'completed' | 'failed';\n /** \n * When the backend last fetched data from YouTube API.\n * UI should store this value and compare on next call.\n * If lastSyncAt is newer than stored → refetch data from API\n */\n lastSyncAt: string | null;\n /** Last error message if sync failed */\n lastError: string | null;\n /** Number of consecutive failed attempts */\n failedAttempts: number;\n /** Whether data is older than the refresh interval (backend will auto-sync) */\n isStale: boolean;\n /** When the next automatic sync will run */\n nextSyncAt: string | null;\n /** Human-readable time until next sync (e.g., \"6 hours\") */\n timeUntilNextSync: string;\n /** Sync interval for this data type (e.g., \"12 hours\") */\n interval: string;\n }\n\n /**\n * Response from GET /v1/platforms/youtube/sync/status\n * \n * Use this endpoint to:\n * 1. Check if any sync is stale (anyStale) - backend will auto-sync\n * 2. Check if new data available (compare lastSyncAt with stored value)\n * 3. Show sync status in UI settings/debug screen\n * \n * @example\n * ```typescript\n * const status = await client.youtube.getUnifiedSyncStatus();\n * \n * // Check if backend has newer data than our cache\n * const storedLastSync = localStorage.get('videosLastSync');\n * if (status.data.syncs.videos.lastSyncAt > storedLastSync) {\n * // Backend synced since our last fetch, refetch videos\n * const videos = await client.youtube.getVideos();\n * localStorage.set('videosLastSync', status.data.syncs.videos.lastSyncAt);\n * }\n * ```\n */\n export interface UnifiedSyncStatusResponse {\n /** Whether YouTube account is connected */\n isConnected: boolean;\n /** YouTube channel ID */\n channelId: string | null;\n /** YouTube channel name */\n channelName: string | null;\n /** Total videos stored in database */\n totalVideos: number;\n \n /** Sync status for each data type */\n syncs: {\n /** Channel info (subscribers, video count) */\n channelInfo: SyncTypeStatus;\n /** Videos list */\n videos: SyncTypeStatus;\n /** Channel analytics (daily metrics, demographics) */\n channelAnalytics: SyncTypeStatus;\n /** Video analytics (per-video metrics) */\n videoAnalytics: SyncTypeStatus;\n };\n }\n\n // =========================================================================\n // ANALYTICS SYNC TYPES\n // =========================================================================\n\n /**\n * Response from GET /v1/platforms/youtube/analytics/channel/sync/status\n */\n export interface ChannelAnalyticsSyncStatusResponse {\n needsSync: boolean;\n status: 'NEVER_SYNCED' | 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED' | 'never_synced';\n dailyRecords: number;\n dateRange: {\n start: string;\n end: string;\n } | null;\n lastSyncAt: string | null;\n message: string;\n }\n\n /**\n * Response from POST /v1/platforms/youtube/analytics/channel/sync\n */\n export interface ChannelAnalyticsSyncResponse {\n syncedDays: number;\n hasRevenue: boolean;\n dateRange: {\n start: string;\n end: string;\n };\n demographics: number;\n trafficSources: number;\n countries: number;\n devices: number;\n syncStatus: 'COMPLETED' | 'FAILED';\n lastSyncAt: string;\n }\n\n /**\n * Response from GET /v1/platforms/youtube/analytics/videos/sync/status\n */\n export interface VideoAnalyticsSyncStatusResponse {\n needsSync: boolean;\n status: 'NEVER_SYNCED' | 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED';\n /** \n * Progress tracking - USE THESE FOR UI DISPLAY\n * These values reflect actual database state, not queue state\n */\n progress: {\n /** Number of videos with analytics saved to database */\n completed: number;\n /** Total number of videos to sync */\n total: number;\n /** Videos still needing sync */\n remaining: number;\n /** Progress percentage (0-100) */\n percent: number;\n };\n /** @deprecated Use progress.total instead */\n totalVideos: number;\n /** @deprecated Use progress.completed instead */\n videosWithAnalytics: number;\n /** @deprecated Use progress.remaining instead */\n videosNeedingSync: number;\n lastSyncAt: string | null;\n lastError: string | null;\n /** \n * Queue info (for debugging only, NOT for progress display)\n * Note: Queue counts reset and don't reflect true progress\n */\n queue: {\n /** Whether workers are actively processing */\n isProcessing: boolean;\n /** Jobs currently being processed */\n jobsActive: number;\n /** Jobs waiting in queue */\n jobsWaiting: number;\n /** Jobs that failed after retries */\n jobsFailed: number;\n };\n message: string;\n }\n\n /**\n * Response from POST /v1/platforms/youtube/analytics/videos/sync\n * \n * Returns immediately - sync runs in background via BullMQ queue.\n * Poll VideoAnalyticsSyncStatusResponse for progress.\n */\n export interface VideoAnalyticsSyncResponse {\n /** Status message */\n message: string;\n /** Current sync status - will be 'IN_PROGRESS' when sync starts */\n status: 'IN_PROGRESS' | 'COMPLETED' | 'FAILED';\n /** Total number of videos */\n totalVideos: number;\n /** Number of videos queued for sync (excludes already-synced) */\n queued: number;\n /** Number of videos skipped (already have analytics) */\n skipped: number;\n }\n\n /**\n * Daily analytics data for a single video\n * Stored in YouTubeVideoDaily collection\n */\n export interface VideoDailyAnalytics {\n videoId: string;\n date: string;\n views: number;\n likes: number;\n dislikes: number;\n comments: number;\n estimatedMinutesWatched: number;\n averageViewDuration: number;\n subscribersGained: number;\n subscribersLost: number;\n }\n}\n\n// =============================================================================\n// INSTAGRAM TYPES\n// =============================================================================\n\nexport namespace Instagram {\n // ===== INITIALIZATION =====\n\n export interface InitStatusResponse {\n connected: boolean;\n needsInit: boolean;\n isInitialSyncDone?: boolean;\n lastSyncedAt?: string | null;\n authMethod?: 'facebook' | 'instagram';\n account?: {\n username?: string;\n name?: string;\n profilePictureUrl?: string;\n followersCount?: number;\n mediaCount?: number;\n } | null;\n syncedMediaCount?: number;\n message?: string;\n }\n\n export interface InitResponse {\n needsInit: boolean;\n message: string;\n profile?: {\n username?: string;\n name?: string;\n profilePictureUrl?: string;\n followersCount?: number;\n mediaCount?: number;\n };\n syncedMedia?: number;\n lastSyncedAt?: string;\n }\n\n // ===== ACCOUNT INSIGHTS =====\n\n export interface GetAccountInsightsParams {\n range?: '7d' | '14d' | '28d' | '30d';\n }\n\n export interface AccountInsightsResponse {\n range: string;\n aggregated: Record<string, number>;\n timeSeries: Record<string, Array<{ date: string; value: number }>>;\n }\n\n // ===== PROFILE =====\n\n export interface ProfileResponse {\n id: string;\n username: string;\n name?: string;\n biography?: string;\n profilePictureUrl?: string;\n followersCount: number;\n followingCount: number;\n mediaCount: number;\n accountType?: 'BUSINESS' | 'CREATOR' | 'PERSONAL';\n website?: string;\n cached?: boolean;\n cachedAt?: string;\n }\n\n // ===== ACCOUNT OVERVIEW =====\n\n export interface GetAccountOverviewParams {\n range?: '7d' | '14d' | '28d' | '30d';\n }\n\n export interface AccountOverviewResponse {\n profile: {\n id: string;\n username: string;\n name?: string;\n biography?: string;\n profilePictureUrl?: string;\n followersCount: number;\n followingCount: number;\n mediaCount: number;\n accountType?: string;\n website?: string;\n };\n stats: {\n range: string;\n reach: number;\n impressions: number;\n profileViews: number;\n websiteClicks: number;\n totalLikes: number;\n totalComments: number;\n totalShares: number;\n totalSaves: number;\n engagementRate: number;\n accountsEngaged: number;\n };\n topPosts: Array<{\n id: string;\n mediaType: string;\n mediaUrl?: string;\n thumbnailUrl?: string;\n caption?: string;\n likeCount?: number;\n commentsCount?: number;\n timestamp?: string;\n permalink?: string;\n }>;\n syncedMediaCount: number;\n }\n\n // ===== MEDIA / POSTS =====\n\n export interface Post {\n id: string;\n media_id: string;\n caption?: string;\n media_type: 'IMAGE' | 'VIDEO' | 'CAROUSEL_ALBUM' | 'REELS';\n media_url?: string;\n thumbnail_url?: string;\n permalink?: string;\n timestamp: string;\n like_count?: number;\n comments_count?: number;\n }\n\n export interface GetPostsParams {\n limit?: number;\n offset?: number;\n mediaType?: string;\n sortBy?: 'timestamp' | 'like_count' | 'comments_count';\n sortOrder?: 'asc' | 'desc';\n }\n\n export interface GetPostsResponse {\n posts: Post[];\n pagination: {\n limit: number;\n offset: number;\n total: number;\n hasMore: boolean;\n };\n }\n\n export interface PostInsightsResponse {\n postId: string;\n mediaType: string;\n insights: {\n data: Array<{\n name: string;\n title: string;\n values: Array<{ value: number }>;\n }>;\n };\n }\n\n // ===== STORIES =====\n\n export interface Story {\n id: string;\n media_type: 'IMAGE' | 'VIDEO';\n media_url?: string;\n timestamp: string;\n permalink?: string;\n }\n\n export interface StoriesResponse {\n stories: Story[];\n count: number;\n }\n\n // ===== SYNC STATUS =====\n\n /** Sync status enum matching backend */\n export type SyncStatus = 'NEVER_SYNCED' | 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'FAILED';\n\n /** Individual sync type info */\n export interface SyncTypeInfo {\n status: SyncStatus;\n lastSyncAt: string | null;\n lastError?: string | null;\n failedAttempts?: number;\n isStale: boolean;\n nextSyncAt: string | null;\n timeUntilNextSync: string;\n interval: string;\n }\n\n /**\n * Unified sync status response\n * GET /v1/platforms/instagram/sync/status\n */\n export interface UnifiedSyncStatusResponse {\n isConnected: boolean;\n igUserId: string | null;\n username: string | null;\n totalPosts: number;\n syncs: {\n accountInfo: SyncTypeInfo;\n posts: SyncTypeInfo;\n accountAnalytics: SyncTypeInfo;\n postInsights: SyncTypeInfo;\n stories: SyncTypeInfo;\n };\n }\n\n // ===== POSTS SYNC =====\n\n /**\n * Response from POST /v1/platforms/instagram/posts/sync\n */\n export interface PostsSyncResponse {\n posts: {\n total: number;\n synced: number;\n oldestPostDate: string | null;\n };\n syncStatus: {\n isFirstSync: boolean;\n lastSyncAt: string;\n };\n }\n\n /**\n * Response from GET /v1/platforms/instagram/posts/sync/status\n */\n export interface PostsSyncStatusResponse {\n needsSync: boolean;\n status: SyncStatus;\n lastSyncAt: string | null;\n postCount: number;\n oldestPostDate: string | null;\n }\n\n // ===== ACCOUNT ANALYTICS SYNC =====\n\n /**\n * Response from POST /v1/platforms/instagram/analytics/account/sync\n */\n export interface AccountAnalyticsSyncResponse {\n range: string;\n metricsCount: number;\n syncStatus: SyncStatus;\n lastSyncAt: string;\n }\n\n /**\n * Response from GET /v1/platforms/instagram/analytics/account/sync/status\n */\n export interface AccountAnalyticsSyncStatusResponse {\n needsSync: boolean;\n status: SyncStatus;\n insightRecords: number;\n dateRange: {\n start: string;\n end: string;\n } | null;\n lastSyncAt: string | null;\n message: string;\n }\n\n // ===== POST INSIGHTS SYNC =====\n\n /**\n * Response from POST /v1/platforms/instagram/analytics/posts/sync\n * Post insights are queued for background processing via BullMQ\n */\n export interface PostInsightsSyncResponse {\n message: string;\n totalPosts: number;\n jobsQueued: number;\n status: SyncStatus;\n queuedAt: string;\n }\n\n /**\n * Response when sync is already in progress\n */\n export interface PostInsightsInProgressResponse {\n message: string;\n status: 'in_progress';\n progress: {\n waiting: number;\n active: number;\n completed: number;\n failed: number;\n delayed: number;\n };\n }\n\n /**\n * Response from GET /v1/platforms/instagram/analytics/posts/sync/status\n */\n export interface PostInsightsSyncStatusResponse {\n needsSync: boolean;\n status: SyncStatus;\n totalPosts: number;\n postsWithInsights: number;\n coverage: number;\n lastSyncAt: string | null;\n metadata?: {\n postsWithInsights?: number;\n totalPosts?: number;\n lastCheckedAt?: string;\n } | null;\n message: string;\n }\n\n // ===== LEGACY SYNC =====\n\n export interface SyncResponse {\n message: string;\n profile?: {\n username?: string;\n followersCount?: number;\n mediaCount?: number;\n };\n syncedMedia?: number;\n }\n\n // ===== LEGACY (backward compatibility) =====\n\n /** @deprecated Use InitStatusResponse */\n export interface AccountMetrics {\n followersCount: number;\n followsCount: number;\n mediaCount: number;\n averageLikes?: number;\n engagementRate?: number;\n }\n}\n\n// =============================================================================\n// INSIGHTS TYPES\n// =============================================================================\n\nexport namespace Insights {\n export interface CrossPlatformMetrics {\n totalFollowers: number;\n totalEngagement: number;\n platforms: {\n youtube?: {\n subscribers: number;\n views: number;\n videos: number;\n };\n instagram?: {\n followers: number;\n posts: number;\n avgEngagement: number;\n };\n };\n }\n\n export interface TrendingContent {\n youtube?: YouTube.Video[];\n instagram?: Instagram.Post[];\n }\n\n // =========================================================================\n // MOMENTUM STATUS\n // =========================================================================\n\n /** Momentum status labels */\n export type MomentumStatusLabel = 'surging' | 'rising' | 'steady' | 'cooling' | 'dropping';\n \n /** Momentum trend direction */\n export type MomentumTrend = 'accelerating' | 'holding' | 'decelerating';\n\n /**\n * Metric breakdown showing current vs previous period with contribution\n */\n export interface MetricComparison {\n /** Current period value */\n current: number;\n /** Previous period value */\n previous: number;\n /** Percentage change (-100 to +100+) */\n change: number;\n /** Points contributed to momentum score (can be negative) */\n points: number;\n /** Human-readable description: \"+25% Views × 30% weight = +7.5 pts\" */\n description: string;\n }\n\n /**\n * Momentum Status Response\n * \n * Provides a 0-100 score indicating channel momentum:\n * - 50 = Steady (no change week over week)\n * - 0 = Severe decline (-100% or worse)\n * - 100 = Explosive growth (+100% or better)\n * \n * Status labels:\n * - 80-100: Surging 🚀\n * - 60-79: Rising 📈\n * - 40-59: Steady ➡️\n * - 20-39: Cooling 📉\n * - 0-19: Dropping 🔻\n */\n export interface MomentumStatusResponse {\n /** Normalized score 0-100 (50 = no change) */\n score: number;\n /** Raw weighted average change before normalization */\n rawChange: number;\n /** Human-friendly status label */\n status: MomentumStatusLabel;\n /** Trend direction compared to previous period */\n trend: MomentumTrend;\n /** Previous period's score (null if first calculation) */\n previousScore: number | null;\n /** Time periods being compared */\n period: {\n current: { start: string; end: string };\n previous: { start: string; end: string };\n };\n /** Breakdown of individual metrics with contribution info */\n breakdown: {\n views: MetricComparison;\n engagement: MetricComparison;\n watchTime: MetricComparison;\n subscribers: MetricComparison;\n };\n /** Weights used in calculation */\n weights: {\n views: number;\n engagement: number;\n watchTime: number;\n subscribers: number;\n };\n /** Human-readable insight summary */\n insight: string;\n /** Cache metadata */\n _meta?: {\n fromCache: boolean;\n refreshed?: boolean;\n cacheValidFor: string;\n algorithmVersion: string;\n };\n }\n\n /**\n * Single history point for momentum graphing\n */\n export interface MomentumHistoryPoint {\n /** Date (YYYY-MM-DD) */\n date: string;\n /** Momentum score at that time */\n score: number;\n /** Status label at that time */\n status: MomentumStatusLabel;\n }\n\n /**\n * Momentum history response for graphing\n */\n export interface MomentumHistoryResponse {\n /** Historical data points (oldest first) */\n history: MomentumHistoryPoint[];\n /** Number of points returned */\n count: number;\n }\n\n /**\n * Parameters for momentum history request\n */\n export interface GetMomentumHistoryParams {\n /** Max number of data points (default: 30, max: 90) */\n limit?: number;\n }\n\n // =========================================================================\n // COMMON INSIGHT TYPES\n // =========================================================================\n\n /**\n * How we calculated this insight - shown to user for transparency\n */\n export interface CalculationExplanation {\n /** Short description of the methodology */\n method: string;\n /** Time range analyzed */\n timeRange: string;\n /** Key factors that influenced the result */\n factors: string[];\n /** Data quality/reliability indicator */\n dataQuality: 'high' | 'medium' | 'low';\n /** Why data quality might be limited */\n dataQualityNote?: string;\n }\n\n /**\n * Common metadata for all insights\n */\n export interface InsightMeta {\n /** When this insight was calculated */\n calculatedAt: string;\n /** When this insight expires (should be recalculated) */\n validUntil: string;\n /** Algorithm version for tracking changes */\n algorithmVersion: string;\n /** Whether result came from cache */\n fromCache: boolean;\n /** How this was calculated (for transparency) */\n howWeCalculatedThis: CalculationExplanation;\n }\n\n /**\n * Response when there's not enough data for an insight\n */\n export interface InsufficientDataResponse {\n hasData: false;\n reason: string;\n minimumRequired: string;\n currentAmount: string;\n suggestion: string;\n }\n\n // =========================================================================\n // BEST PERFORMING VIDEO\n // =========================================================================\n\n export interface TopVideoData {\n videoId: string;\n title: string;\n thumbnailUrl: string | null;\n publishedAt: string;\n /** Link to watch on YouTube */\n watchUrl: string;\n }\n\n export interface TopVideoMetrics {\n views: number;\n likes: number;\n comments: number;\n estimatedMinutesWatched: number;\n engagementRate: number;\n }\n\n export interface TopVideoComparison {\n /** Performance vs user's average video */\n vsChannelAverage: {\n views: number; // e.g., +145 (percent)\n engagement: number; // e.g., +32 (percent)\n description: string; // e.g., \"2.4x better than your average video\"\n };\n /** Why this video performed well */\n standoutReasons: string[];\n }\n\n export interface BestPerformingVideoData {\n /** Time range analyzed */\n period: {\n start: string;\n end: string;\n days: number;\n };\n /** The top performing video */\n topVideo: TopVideoData;\n /** Video's metrics for this period */\n metrics: TopVideoMetrics;\n /** How it compares to your other content */\n comparison: TopVideoComparison;\n /** Human-readable summary */\n insight: string;\n /** Runner-up videos (for context) */\n runnersUp: Array<{\n videoId: string;\n title: string;\n views: number;\n thumbnailUrl: string | null;\n }>;\n }\n\n export type BestPerformingVideoResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: BestPerformingVideoData; _meta: InsightMeta };\n\n // =========================================================================\n // CONTENT DECAY\n // =========================================================================\n\n export interface DecayingVideo {\n videoId: string;\n title: string;\n thumbnailUrl: string | null;\n publishedAt: string;\n watchUrl: string;\n /** Age of video in days */\n ageInDays: number;\n /** Views in current period */\n currentViews: number;\n /** Views in previous period */\n previousViews: number;\n /** Decay rate as percentage (negative = declining) */\n decayRate: number;\n /** Severity of decay */\n severity: 'severe' | 'moderate' | 'mild';\n /** Actionable suggestion */\n suggestion: string;\n }\n\n export interface ContentDecayData {\n /** Time range analyzed */\n period: {\n current: { start: string; end: string };\n previous: { start: string; end: string };\n days: number;\n };\n /** Number of videos analyzed */\n videosAnalyzed: number;\n /** Videos experiencing significant decay */\n decayingVideos: DecayingVideo[];\n /** Summary stats */\n summary: {\n totalDecaying: number;\n severeCount: number;\n moderateCount: number;\n mildCount: number;\n /** Combined view loss from all decaying videos */\n totalViewsLost: number;\n };\n /** Human-readable summary */\n insight: string;\n /** What content decay means */\n whatThisMeans: string;\n }\n\n export type ContentDecayResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: ContentDecayData; _meta: InsightMeta };\n\n // =========================================================================\n // AUDIENCE ACTIVITY (When your audience is most active)\n // =========================================================================\n\n export interface DayPerformance {\n day: string; // 'Monday', 'Tuesday', etc.\n avgFirstDayViews: number;\n avgEngagement: number;\n videoCount: number;\n /** Confidence in this data */\n confidence: 'high' | 'medium' | 'low';\n }\n\n export interface TimeSlotPerformance {\n slot: string; // e.g., \"14:00-17:00\"\n label: string; // e.g., \"Afternoon (2-5pm)\"\n avgFirstDayViews: number;\n videoCount: number;\n confidence: 'high' | 'medium' | 'low';\n }\n\n export interface AudienceActivityData {\n /** How many videos were analyzed */\n videosAnalyzed: number;\n /** Day when your audience is most active */\n mostActiveDay: {\n day: string;\n confidence: 'high' | 'medium' | 'low';\n /** Why this day shows highest activity */\n reason: string;\n };\n /** Time slot when your audience is most active */\n mostActiveTimeSlot: {\n slot: string;\n label: string;\n confidence: 'high' | 'medium' | 'low';\n reason: string;\n };\n /** Full breakdown by day */\n dayBreakdown: DayPerformance[];\n /** Full breakdown by time slot (if enough data) */\n timeBreakdown: TimeSlotPerformance[] | null;\n /** Human-readable summary */\n insight: string;\n /** Disclaimer about the data */\n disclaimer: string;\n }\n\n export type AudienceActivityResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: AudienceActivityData; _meta: InsightMeta };\n\n /** @deprecated Use AudienceActivityData instead */\n export type BestUploadTimeData = AudienceActivityData;\n /** @deprecated Use AudienceActivityResponse instead */\n export type BestUploadTimeResponse = AudienceActivityResponse;\n\n // =========================================================================\n // FADING HITS (Videos that were hits but are now declining)\n // =========================================================================\n\n export interface FadingHitVideo {\n videoId: string;\n title: string;\n thumbnailUrl: string | null;\n publishedAt: string;\n watchUrl: string;\n /** How old the video is in days */\n ageInDays: number;\n /** Performance during peak period (actual peak, not just first 14 days) */\n peakPerformance: {\n /** Average daily views during peak window */\n dailyViews: number;\n /** Comparison to channel average (e.g., \"45% above average\") */\n vsChannelAverage: string;\n /** Was this a top performer (2x+ channel average)? */\n wasTopPerformer: boolean;\n /** When the actual peak occurred */\n peakPeriod: {\n start: string;\n end: string;\n /** How many days after upload the peak occurred */\n daysAfterUpload: number;\n /** True if video went viral later (not at launch) - delayed virality */\n wasDelayedViral: boolean;\n };\n };\n /** Performance in recent period (last 14 days) */\n currentPerformance: {\n /** Current average daily views */\n dailyViews: number;\n /** Percentage decline from peak (negative number) */\n declineFromPeak: number;\n };\n /** How likely revival efforts would work */\n revivalPotential: 'high' | 'medium' | 'low';\n /** Why this video might be worth reviving */\n whyRevive: string;\n /** Specific suggestions to revive this video */\n suggestions: string[];\n }\n\n export interface FadingHitsData {\n /** Time period for \"current\" performance check */\n period: {\n current: { start: string; end: string };\n label: string;\n };\n /** Number of videos analyzed */\n videosAnalyzed: number;\n /** Videos that were performing above average but are now declining */\n fadingHits: FadingHitVideo[];\n /** Summary stats */\n summary: {\n totalFading: number;\n highPotentialCount: number;\n mediumPotentialCount: number;\n lowPotentialCount: number;\n /** Combined daily views being lost */\n totalDailyViewsLost: number;\n };\n /** Human-readable summary */\n insight: string;\n /** What fading hits means */\n whatThisMeans: string;\n }\n\n export type FadingHitsResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: FadingHitsData; _meta: InsightMeta };\n\n // =========================================================================\n // SUBSCRIBER QUALITY\n // =========================================================================\n\n export type SubscriberQualityLabel = 'excellent' | 'good' | 'fair' | 'needs_attention' | 'concerning';\n\n export interface SubscriberQualityData {\n /** Time range analyzed */\n period: {\n start: string;\n end: string;\n days: number;\n };\n /** Overall quality score (0-100) */\n qualityScore: number;\n /** Score interpretation */\n scoreLabel: SubscriberQualityLabel;\n /** Core metrics */\n metrics: {\n /** Total subscribers gained */\n subscribersGained: number;\n /** Total subscribers lost */\n subscribersLost: number;\n /** Net subscriber change */\n netSubscribers: number;\n /** Retention rate (how many stay) */\n retentionRate: number;\n /** Average watch time per view */\n avgWatchTimeMinutes: number;\n /** Views per subscriber (engagement proxy) */\n viewsPerSubscriber: number;\n };\n /** Comparison to previous period */\n trend: {\n scoreChange: number;\n direction: 'improving' | 'stable' | 'declining';\n description: string;\n };\n /** Human-readable summary */\n insight: string;\n /** Specific recommendations */\n recommendations: string[];\n }\n\n export type SubscriberQualityResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: SubscriberQualityData; _meta: InsightMeta };\n\n // =========================================================================\n // AUDIENCE FATIGUE\n // =========================================================================\n\n export type FatigueLevel = 'none' | 'low' | 'moderate' | 'high' | 'critical';\n\n export interface FatigueSignal {\n /** Name of the signal */\n name: string;\n /** Current period value */\n current: number;\n /** Previous period value */\n previous: number;\n /** Change percentage (negative = declining) */\n changePercent: number;\n /** Whether this signal indicates fatigue */\n indicatesFatigue: boolean;\n /** Description of what this means */\n interpretation: string;\n }\n\n export interface AudienceFatigueData {\n /** Time periods compared */\n period: {\n current: { start: string; end: string };\n previous: { start: string; end: string };\n daysPerPeriod: number;\n };\n /** Overall fatigue index (0-100, higher = more fatigue) */\n fatigueIndex: number;\n /** Human-readable fatigue level */\n fatigueLevel: FatigueLevel;\n /** Individual signals that contribute to the score */\n signals: {\n watchTimePerView: FatigueSignal;\n engagementRate: FatigueSignal;\n subscriberChurn: FatigueSignal;\n viewsPerVideo: FatigueSignal;\n };\n /** Summary of signals */\n summary: {\n signalsIndicatingFatigue: number;\n totalSignals: number;\n primaryConcern: string | null;\n };\n /** Human-readable insight */\n insight: string;\n /** What to do about it */\n recommendations: string[];\n }\n\n export type AudienceFatigueResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: AudienceFatigueData; _meta: InsightMeta };\n\n // =========================================================================\n // OPTIMAL VIDEO LENGTH\n // =========================================================================\n\n export interface LengthBucketPerformance {\n /** Bucket name (e.g., \"5-10 minutes\") */\n bucket: string;\n /** Bucket range in seconds */\n range: { min: number; max: number | null };\n /** Number of videos in this bucket */\n videoCount: number;\n /** Average retention as percentage */\n avgRetention: number;\n /** Average engagement rate */\n avgEngagement: number;\n /** Average views per video */\n avgViews: number;\n /** Combined performance score (0-100) */\n performanceScore: number;\n /** Is this the optimal bucket? */\n isOptimal: boolean;\n }\n\n export interface OptimalVideoLengthData {\n /** How many videos were analyzed */\n videosAnalyzed: number;\n /** The recommended optimal length range */\n optimalLength: {\n bucket: string;\n range: { min: number; max: number | null };\n confidence: 'high' | 'medium' | 'low';\n reason: string;\n };\n /** Performance breakdown by length bucket */\n breakdown: LengthBucketPerformance[];\n /** Key findings */\n findings: {\n bestRetention: { bucket: string; value: number };\n bestEngagement: { bucket: string; value: number };\n mostCommon: { bucket: string; count: number };\n };\n /** Human-readable insight */\n insight: string;\n /** Specific recommendations */\n recommendations: string[];\n }\n\n export type OptimalVideoLengthResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: OptimalVideoLengthData; _meta: InsightMeta };\n\n // =========================================================================\n // UPLOAD CONSISTENCY\n // =========================================================================\n\n /** Consistency status levels */\n export type ConsistencyStatus = 'excellent' | 'good' | 'needs_improvement' | 'poor' | 'inactive';\n\n /** Monthly upload breakdown */\n export interface MonthlyUploadData {\n month: string;\n uploads: number;\n avgGap: number | null;\n }\n\n /** Upload consistency insight data */\n export interface UploadConsistencyData {\n /** Period analyzed */\n period: {\n start: string;\n end: string;\n days: number;\n };\n /** Overall consistency score (0-100) */\n consistencyScore: number;\n /** Status label */\n status: ConsistencyStatus;\n /** Key metrics */\n metrics: {\n totalUploads: number;\n avgDaysBetweenUploads: number;\n uploadVariance: number;\n daysSinceLastUpload: number;\n lastUploadDate: string | null;\n };\n /** Upload pattern analysis */\n pattern: {\n isRegular: boolean;\n typicalGap: string;\n longestStreak: number;\n currentStreak: number;\n trend: 'increasing' | 'stable' | 'decreasing';\n trendDescription: string;\n };\n /** Monthly breakdown */\n monthlyBreakdown: MonthlyUploadData[];\n /** Frequency recommendation */\n recommendation: {\n suggested: string;\n current: string;\n reason: string;\n };\n /** Human-readable summary */\n insight: string;\n /** Actionable recommendations */\n recommendations: string[];\n }\n\n export type UploadConsistencyResponse = \n | (InsufficientDataResponse)\n | { hasData: true; data: UploadConsistencyData; _meta: InsightMeta };\n\n // =========================================================================\n // ALL INSIGHTS (COMBINED)\n // =========================================================================\n\n export interface AllInsightsData {\n bestPerformingVideo: BestPerformingVideoResponse;\n contentDecay: ContentDecayResponse;\n audienceActivity: AudienceActivityResponse;\n subscriberQuality: SubscriberQualityResponse;\n fadingHits: FadingHitsResponse;\n audienceFatigue: AudienceFatigueResponse;\n optimalLength: OptimalVideoLengthResponse;\n uploadConsistency: UploadConsistencyResponse;\n }\n\n export interface AllInsightsResponse {\n data: AllInsightsData;\n _meta: {\n calculatedAt: string;\n insightsAvailable: string[];\n };\n }\n\n // =========================================================================\n // REQUEST PARAMETERS\n // =========================================================================\n\n export interface GetBestVideoParams {\n /** Number of days to analyze (1-90, default: 30) */\n period?: number;\n }\n\n export interface GetContentDecayParams {\n /** Days per comparison period (3-14, default: 7) */\n period?: number;\n }\n\n export interface GetSubscriberQualityParams {\n /** Number of days to analyze (14-180, default: 90) */\n period?: number;\n }\n\n export interface GetAudienceFatigueParams {\n /** Days per comparison period (14-90, default: 30) */\n period?: number;\n }\n}\n\n// =============================================================================\n// ACTIVITY TYPES\n// =============================================================================\n\nexport namespace Activity {\n /**\n * Job types for sync operations\n */\n export type JobType = 'videos_sync' | 'channel_analytics' | 'video_analytics';\n\n /**\n * Job status values\n */\n export type JobStatus = 'started' | 'completed' | 'failed';\n\n /**\n * Job trigger source\n */\n export type JobTrigger = 'scheduler' | 'api' | 'manual';\n\n /**\n * Job data details (varies by job type)\n */\n export interface JobData {\n // videos_sync\n videosFound?: number;\n newVideos?: number;\n updatedVideos?: number;\n totalVideos?: number;\n // channel_analytics\n daysProcessed?: number;\n metricsUpdated?: number;\n syncedDays?: number;\n dateRange?: { start: string; end: string };\n // video_analytics\n videoId?: string;\n videoTitle?: string;\n recordsCreated?: number;\n recordsUpdated?: number;\n queued?: number;\n skipped?: number;\n // Generic\n message?: string;\n [key: string]: unknown;\n }\n\n /**\n * A single activity log entry\n */\n export interface LogEntry {\n /** Unique log ID */\n id: string;\n /** BullMQ job ID */\n jobId: string;\n /** Type of sync job */\n type: JobType;\n /** Current status */\n status: JobStatus;\n /** When the job started */\n startedAt: string;\n /** When the job completed (if finished) */\n completedAt?: string;\n /** Duration in milliseconds */\n durationMs?: number;\n /** What triggered this job */\n triggeredBy: JobTrigger;\n /** Job result data */\n data?: JobData;\n /** Error message (if failed) */\n error?: string;\n }\n\n /**\n * Response for activity logs list\n */\n export interface LogsResponse {\n /** List of log entries */\n logs: LogEntry[];\n /** Number of entries returned */\n count: number;\n }\n\n /**\n * Activity summary with counts\n */\n export interface SummaryResponse {\n /** Total jobs ever run */\n totalJobs: number;\n /** Successfully completed jobs */\n completed: number;\n /** Failed jobs */\n failed: number;\n /** Breakdown by job type */\n byType: Record<JobType, { total: number; lastRun?: string }>;\n }\n\n /**\n * Parameters for fetching activity logs\n */\n export interface GetLogsParams {\n /** Filter by job type */\n type?: JobType;\n /** Filter by status */\n status?: JobStatus;\n /** Max entries to return (default: 50, max: 100) */\n limit?: number;\n }\n}\n","/**\n * TrndUp API SDK\n * \n * Official TypeScript SDK for the TrndUp API.\n * Provides type-safe methods for all API endpoints with Firebase authentication.\n * \n * @example\n * ```typescript\n * import { TrndUpSDK, YOUTUBE_SCOPES } from '@dracoonghost/trndup-sdk';\n * import auth from '@react-native-firebase/auth';\n * import { GoogleSignin } from '@react-native-google-signin/google-signin';\n * \n * // Configure Google Sign-In with YouTube scopes\n * GoogleSignin.configure({\n * webClientId: 'YOUR_WEB_CLIENT_ID',\n * offlineAccess: true,\n * scopes: YOUTUBE_SCOPES\n * });\n * \n * const sdk = new TrndUpSDK({\n * baseUrl: 'https://api.trndup.app',\n * getToken: async () => {\n * const user = auth().currentUser;\n * return user ? await user.getIdToken() : null;\n * },\n * onAuthFailure: () => {\n * // Handle auth failure - navigate to login\n * navigation.navigate('Login');\n * },\n * });\n * \n * // Type-safe API calls\n * const user = await sdk.auth.getCurrentUser();\n * const videos = await sdk.youtube.getVideos({ limit: 10 });\n * const healthScore = await sdk.youtube.getHealthScore({ range: '30d' });\n * ```\n */\n\nimport { TrndUpClient, TrndUpClientConfig } from './client';\nimport { AuthModule } from './modules/auth';\nimport { YouTubeModule } from './modules/youtube';\nimport { InstagramModule } from './modules/instagram';\nimport { SocialModule } from './modules/social';\nimport { InsightsModule } from './modules/insights';\nimport { ActivityModule } from './modules/activity';\n\nexport class TrndUpSDK extends TrndUpClient {\n public auth: AuthModule;\n public youtube: YouTubeModule;\n public instagram: InstagramModule;\n public social: SocialModule;\n public insights: InsightsModule;\n public activity: ActivityModule;\n\n constructor(config: TrndUpClientConfig) {\n super(config);\n\n // Initialize all modules\n this.auth = new AuthModule(this);\n this.youtube = new YouTubeModule(this);\n this.instagram = new InstagramModule(this);\n this.social = new SocialModule(this);\n this.insights = new InsightsModule(this);\n this.activity = new ActivityModule(this);\n }\n}\n\n// Re-export types and classes\nexport type { TrndUpClientConfig, RequestOptions } from './client';\nexport { TrndUpApiError, TrndUpNetworkError } from './client';\n\n// Re-export all type namespaces\nexport type { Auth, Social, YouTube, Instagram, Insights, Activity } from './types';\n\n// Re-export OAuth scopes for configuring Google Sign-In\nexport { YOUTUBE_SCOPES, INSTAGRAM_SCOPES } from './types';\n\n// Version\nexport const SDK_VERSION = '1.0.0';\n"]}