@classytic/social 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/LICENSE +21 -0
  3. package/README.md +368 -0
  4. package/dist/base-Bw7e52V8.mjs +246 -0
  5. package/dist/base-Bw7e52V8.mjs.map +1 -0
  6. package/dist/base-DBtKFiSX.d.mts +226 -0
  7. package/dist/base-DBtKFiSX.d.mts.map +1 -0
  8. package/dist/chunk-DQk6qfdC.mjs +18 -0
  9. package/dist/client/index.d.mts +44 -0
  10. package/dist/client/index.d.mts.map +1 -0
  11. package/dist/client/index.mjs +154 -0
  12. package/dist/client/index.mjs.map +1 -0
  13. package/dist/common/index.d.mts +3 -0
  14. package/dist/common/index.mjs +7 -0
  15. package/dist/contracts-Cdwa4zlg.d.mts +121 -0
  16. package/dist/contracts-Cdwa4zlg.d.mts.map +1 -0
  17. package/dist/contracts-lCa069IK.mjs +221 -0
  18. package/dist/contracts-lCa069IK.mjs.map +1 -0
  19. package/dist/env-Bl0cwwjC.mjs +955 -0
  20. package/dist/env-Bl0cwwjC.mjs.map +1 -0
  21. package/dist/env-DxOZHf0p.d.mts +394 -0
  22. package/dist/env-DxOZHf0p.d.mts.map +1 -0
  23. package/dist/errors-Cm6LeKf7.mjs +32 -0
  24. package/dist/errors-Cm6LeKf7.mjs.map +1 -0
  25. package/dist/facebook-l_4CghaA.mjs +95 -0
  26. package/dist/facebook-l_4CghaA.mjs.map +1 -0
  27. package/dist/http-DpcLSR1M.mjs +197 -0
  28. package/dist/http-DpcLSR1M.mjs.map +1 -0
  29. package/dist/index.d.mts +42 -0
  30. package/dist/index.d.mts.map +1 -0
  31. package/dist/index.mjs +71 -0
  32. package/dist/index.mjs.map +1 -0
  33. package/dist/instagram-BGaeUFU2.mjs +90 -0
  34. package/dist/instagram-BGaeUFU2.mjs.map +1 -0
  35. package/dist/linkedin-70whtVKa.mjs +101 -0
  36. package/dist/linkedin-70whtVKa.mjs.map +1 -0
  37. package/dist/meta-D3vcJU1c.mjs +126 -0
  38. package/dist/meta-D3vcJU1c.mjs.map +1 -0
  39. package/dist/pkce-jq5II68b.mjs +72 -0
  40. package/dist/pkce-jq5II68b.mjs.map +1 -0
  41. package/dist/polling-DZ1apXtA.mjs +25 -0
  42. package/dist/polling-DZ1apXtA.mjs.map +1 -0
  43. package/dist/providers/facebook.d.mts +135 -0
  44. package/dist/providers/facebook.d.mts.map +1 -0
  45. package/dist/providers/facebook.mjs +450 -0
  46. package/dist/providers/facebook.mjs.map +1 -0
  47. package/dist/providers/instagram.d.mts +122 -0
  48. package/dist/providers/instagram.d.mts.map +1 -0
  49. package/dist/providers/instagram.mjs +496 -0
  50. package/dist/providers/instagram.mjs.map +1 -0
  51. package/dist/providers/linkedin.d.mts +145 -0
  52. package/dist/providers/linkedin.d.mts.map +1 -0
  53. package/dist/providers/linkedin.mjs +574 -0
  54. package/dist/providers/linkedin.mjs.map +1 -0
  55. package/dist/providers/reddit.d.mts +102 -0
  56. package/dist/providers/reddit.d.mts.map +1 -0
  57. package/dist/providers/reddit.mjs +657 -0
  58. package/dist/providers/reddit.mjs.map +1 -0
  59. package/dist/providers/telegram.d.mts +139 -0
  60. package/dist/providers/telegram.d.mts.map +1 -0
  61. package/dist/providers/telegram.mjs +517 -0
  62. package/dist/providers/telegram.mjs.map +1 -0
  63. package/dist/providers/tiktok.d.mts +116 -0
  64. package/dist/providers/tiktok.d.mts.map +1 -0
  65. package/dist/providers/tiktok.mjs +676 -0
  66. package/dist/providers/tiktok.mjs.map +1 -0
  67. package/dist/providers/twitter.d.mts +150 -0
  68. package/dist/providers/twitter.d.mts.map +1 -0
  69. package/dist/providers/twitter.mjs +628 -0
  70. package/dist/providers/twitter.mjs.map +1 -0
  71. package/dist/providers/whatsapp.d.mts +79 -0
  72. package/dist/providers/whatsapp.d.mts.map +1 -0
  73. package/dist/providers/whatsapp.mjs +376 -0
  74. package/dist/providers/whatsapp.mjs.map +1 -0
  75. package/dist/providers/youtube.d.mts +153 -0
  76. package/dist/providers/youtube.d.mts.map +1 -0
  77. package/dist/providers/youtube.mjs +902 -0
  78. package/dist/providers/youtube.mjs.map +1 -0
  79. package/dist/reddit-B10kS4Se.mjs +126 -0
  80. package/dist/reddit-B10kS4Se.mjs.map +1 -0
  81. package/dist/schemas/index.d.mts +819 -0
  82. package/dist/schemas/index.d.mts.map +1 -0
  83. package/dist/schemas/index.mjs +31 -0
  84. package/dist/schemas/index.mjs.map +1 -0
  85. package/dist/security-BXhfebWm.d.mts +338 -0
  86. package/dist/security-BXhfebWm.d.mts.map +1 -0
  87. package/dist/shared-Fvc6xQku.mjs +100 -0
  88. package/dist/shared-Fvc6xQku.mjs.map +1 -0
  89. package/dist/telegram-FaUHpZgB.mjs +107 -0
  90. package/dist/telegram-FaUHpZgB.mjs.map +1 -0
  91. package/dist/tiktok-B_bMk4G-.mjs +94 -0
  92. package/dist/tiktok-B_bMk4G-.mjs.map +1 -0
  93. package/dist/twitter-BC22zfuc.mjs +98 -0
  94. package/dist/twitter-BC22zfuc.mjs.map +1 -0
  95. package/dist/types-BFE4psYI.d.mts +102 -0
  96. package/dist/types-BFE4psYI.d.mts.map +1 -0
  97. package/dist/types-Bv27tcT0.d.mts +230 -0
  98. package/dist/types-Bv27tcT0.d.mts.map +1 -0
  99. package/dist/types-BwkKyqpi.d.mts +253 -0
  100. package/dist/types-BwkKyqpi.d.mts.map +1 -0
  101. package/dist/types-CJrHMDV9.mjs +27 -0
  102. package/dist/types-CJrHMDV9.mjs.map +1 -0
  103. package/dist/types-ClbVc2rc.d.mts +117 -0
  104. package/dist/types-ClbVc2rc.d.mts.map +1 -0
  105. package/dist/types-D91N16Ym.d.mts +242 -0
  106. package/dist/types-D91N16Ym.d.mts.map +1 -0
  107. package/dist/types-DfLp_ibQ.d.mts +178 -0
  108. package/dist/types-DfLp_ibQ.d.mts.map +1 -0
  109. package/dist/types-DfjDgEoJ.d.mts +88 -0
  110. package/dist/types-DfjDgEoJ.d.mts.map +1 -0
  111. package/dist/types-Dp5Z9VBr.mjs +23 -0
  112. package/dist/types-Dp5Z9VBr.mjs.map +1 -0
  113. package/dist/types-hriBJTsU.d.mts +129 -0
  114. package/dist/types-hriBJTsU.d.mts.map +1 -0
  115. package/dist/types-rn6UuLL8.d.mts +184 -0
  116. package/dist/types-rn6UuLL8.d.mts.map +1 -0
  117. package/dist/whatsapp-CFp7ryR4.mjs +101 -0
  118. package/dist/whatsapp-CFp7ryR4.mjs.map +1 -0
  119. package/dist/youtube-Bs0fdY7H.mjs +98 -0
  120. package/dist/youtube-Bs0fdY7H.mjs.map +1 -0
  121. package/package.json +148 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"youtube.mjs","names":[],"sources":["../../src/providers/youtube/index.ts"],"sourcesContent":["/**\n * YouTube Platform Provider\n * =========================\n * YouTube integration with OAuth2, video upload, and content management.\n *\n * Features:\n * - OAuth2 authorization flow\n * - Resumable video uploads\n * - Upload progress tracking\n * - Video CRUD (get, list, update, delete)\n * - Credential validation\n */\n\nimport { PlatformProvider } from '../../base.js';\nimport type {\n AuthUrlOptions,\n CredentialField,\n ProviderMetadata,\n UploadParams,\n UploadResult,\n TestResult,\n AccountInfo,\n OAuthTokens,\n ProviderConfig,\n} from '../../base.js';\nimport { SocialError } from '../../errors.js';\nimport { YouTubeCredentialsSchema } from '../../schemas/youtube.js';\nimport type { z } from 'zod';\nimport { google } from 'googleapis';\nimport { createReadStream } from 'fs';\nimport { stat } from 'fs/promises';\nimport { Readable } from 'stream';\n\nimport type { OAuth2Client } from 'google-auth-library';\nimport type {\n YouTubeCredentials,\n YouTubeCredentialData,\n YouTubeUploadMetadata,\n YouTubeUploadOptions,\n InitUploadSessionParams,\n InitUploadSessionResult,\n YouTubeVideo,\n YouTubeAccountInfo,\n YouTubeCategory,\n YouTubeLicense,\n ListVideosOptions,\n ListVideosResult,\n UpdateVideoParams,\n YouTubePlaylist,\n CreatePlaylistParams,\n UpdatePlaylistParams,\n ListPlaylistsOptions,\n ListPlaylistsResult,\n PlaylistItem,\n AddToPlaylistOptions,\n ListPlaylistItemsOptions,\n ListPlaylistItemsResult,\n SearchVideosOptions,\n SearchVideosResult,\n YouTubeSearchOrder,\n YouTubeSafeSearch,\n YouTubeVideoType,\n YouTubeVideoRating,\n} from './types.js';\n\nexport type {\n YouTubeCredentials,\n YouTubeCredentialData,\n YouTubeUploadMetadata,\n YouTubeUploadOptions,\n InitUploadSessionParams,\n InitUploadSessionResult,\n YouTubeVideo,\n YouTubeAccountInfo,\n YouTubeCategory,\n YouTubeLicense,\n ListVideosOptions,\n ListVideosResult,\n UpdateVideoParams,\n YouTubePlaylist,\n CreatePlaylistParams,\n UpdatePlaylistParams,\n ListPlaylistsOptions,\n ListPlaylistsResult,\n PlaylistItem,\n AddToPlaylistOptions,\n ListPlaylistItemsOptions,\n ListPlaylistItemsResult,\n SearchVideosOptions,\n SearchVideosResult,\n YouTubeSearchOrder,\n YouTubeSafeSearch,\n YouTubeVideoType,\n YouTubeVideoRating,\n};\n\nexport class YouTubeProvider extends PlatformProvider {\n public defaultRedirectUri: string;\n public scopes: string[];\n\n constructor(config: ProviderConfig = {}) {\n super(config);\n this.name = 'youtube';\n this.displayName = 'YouTube';\n this.authType = 'oauth2';\n\n // Default redirect URI (can be overridden per credential)\n this.defaultRedirectUri = config.redirectUri || `http://localhost:${config.port || 4000}/api/oauth/youtube/callback`;\n\n // OAuth scopes required\n this.scopes = [\n 'https://www.googleapis.com/auth/youtube.upload',\n 'https://www.googleapis.com/auth/youtube',\n 'https://www.googleapis.com/auth/userinfo.profile',\n ];\n\n // OAuth2 client is created per-request via _createOAuthClient()\n }\n\n /**\n * Create OAuth2 client with dynamic credentials\n * @private\n */\n _createOAuthClient(clientId: string, clientSecret: string, redirectUri?: string): OAuth2Client {\n return new google.auth.OAuth2(\n clientId,\n clientSecret,\n redirectUri || this.defaultRedirectUri\n );\n }\n\n /**\n * Get OAuth authorization URL\n * @param state - State parameter for CSRF protection\n * @param credentials - OAuth credentials {clientId, clientSecret}\n */\n getAuthUrl(state: string, credentials: YouTubeCredentials = {} as YouTubeCredentials, _options?: AuthUrlOptions): string {\n const client = this._createOAuthClient(\n credentials.clientId,\n credentials.clientSecret,\n credentials.redirectUri\n );\n\n return client.generateAuthUrl({\n access_type: 'offline',\n scope: this.scopes,\n state,\n prompt: 'consent', // Force consent to get refresh token\n });\n }\n\n /**\n * Exchange authorization code for tokens\n * @param code - Authorization code\n * @param credentials - OAuth credentials {clientId, clientSecret}\n */\n async exchangeCode(code: string, credentials: YouTubeCredentials = {} as YouTubeCredentials): Promise<OAuthTokens> {\n const client = this._createOAuthClient(\n credentials.clientId,\n credentials.clientSecret,\n credentials.redirectUri\n );\n\n const { tokens } = await client.getToken(code);\n return {\n access_token: tokens.access_token as string,\n refresh_token: tokens.refresh_token as string | undefined,\n expires_in: tokens.expiry_date as number | undefined,\n scope: tokens.scope as string | undefined,\n token_type: tokens.token_type as string | undefined,\n };\n }\n\n /**\n * Refresh access token\n * @param refreshToken - Refresh token\n * @param credentials - OAuth credentials {clientId, clientSecret}\n */\n async refreshToken(refreshToken: string, credentials: YouTubeCredentials = {} as YouTubeCredentials): Promise<OAuthTokens> {\n const client = this._createOAuthClient(\n credentials.clientId,\n credentials.clientSecret,\n credentials.redirectUri\n );\n\n client.setCredentials({ refresh_token: refreshToken });\n // getAccessToken() performs the refresh and returns the new access token\n const tokenResponse = await client.getAccessToken();\n const newCreds = client.credentials;\n return {\n access_token: (tokenResponse.token ?? newCreds.access_token) as string,\n expires_in: newCreds.expiry_date as number | undefined,\n token_type: newCreds.token_type as string | undefined,\n };\n }\n\n /**\n * Get account information\n * @param accessToken - Access token\n * @param credentials - OAuth credentials {clientId, clientSecret}\n */\n async getAccountInfo(accessToken: string, credentials: YouTubeCredentials = {} as YouTubeCredentials): Promise<AccountInfo> {\n const client = this._createOAuthClient(\n credentials.clientId,\n credentials.clientSecret,\n credentials.redirectUri\n );\n\n client.setCredentials({ access_token: accessToken });\n\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n const response = await youtube.channels.list({\n part: ['snippet', 'contentDetails', 'statistics'],\n mine: true,\n });\n\n if (!response.data.items || response.data.items.length === 0) {\n throw new SocialError('youtube', 'No YouTube channel found for this account', { statusCode: 400, hint: 'Make sure your Google account has a YouTube channel.' });\n }\n\n const channel = response.data.items[0];\n\n return {\n id: channel.id as string,\n name: channel.snippet!.title as string,\n description: channel.snippet!.description as string,\n customUrl: channel.snippet!.customUrl as string,\n profileImage: channel.snippet!.thumbnails?.default?.url as string | undefined,\n subscriberCount: channel.statistics?.subscriberCount as string | undefined,\n videoCount: channel.statistics?.videoCount as string | undefined,\n viewCount: channel.statistics?.viewCount as string | undefined,\n };\n } catch (error: any) {\n throw new SocialError('youtube', `Failed to get account info: ${error.message}`, { originalError: error });\n }\n }\n\n /**\n * Test credential validity\n */\n async testCredential(credentialData: YouTubeCredentialData): Promise<TestResult> {\n try {\n // If we have OAuth tokens, test them\n if (credentialData.oauthTokenData) {\n const tokenData: OAuthTokens = JSON.parse(credentialData.oauthTokenData);\n const accountInfo = await this.getAccountInfo(tokenData.access_token);\n\n return {\n status: 'OK',\n message: 'YouTube credential is valid',\n data: {\n channelId: accountInfo.id,\n channelTitle: accountInfo.name,\n subscriberCount: (accountInfo as any).subscriberCount,\n },\n };\n }\n\n // If no tokens, check if OAuth config is valid\n if (!(this.config as Record<string, unknown>).clientId || !(this.config as Record<string, unknown>).clientSecret) {\n return {\n status: 'Error',\n message: 'YouTube OAuth credentials not configured',\n };\n }\n\n return {\n status: 'OK' as const,\n message: 'Credential needs OAuth authorization',\n authUrl: this.getAuthUrl('test'),\n } as TestResult;\n } catch (error: any) {\n return {\n status: 'Error',\n message: error.message || 'Failed to validate YouTube credential',\n };\n }\n }\n\n /**\n * Build video metadata for YouTube uploads.\n * Shared between initUploadSession() and uploadVideo().\n *\n * Supports all YouTube Data API v3 fields:\n * - snippet: title, description, tags, categoryId, defaultLanguage\n * - status: privacyStatus, publishAt, license, embeddable, publicStatsViewable, selfDeclaredMadeForKids\n * - recordingDetails: recordingDate\n */\n private _buildVideoMetadata(params: {\n title: string;\n description?: string;\n tags?: string[];\n privacy?: string;\n categoryId?: string;\n scheduledAt?: string | Date;\n options?: YouTubeUploadOptions;\n }): YouTubeUploadMetadata {\n const { title, description = '', tags = [], privacy = 'private', categoryId = '22', scheduledAt, options } = params;\n\n const metadata: YouTubeUploadMetadata = {\n snippet: {\n title: title.substring(0, 100),\n description: description.substring(0, 5000),\n tags: tags.slice(0, 500),\n categoryId,\n },\n status: {\n privacyStatus: scheduledAt ? 'private' : privacy,\n },\n };\n\n // Apply YouTube-specific upload options\n if (options?.defaultLanguage) {\n metadata.snippet.defaultLanguage = options.defaultLanguage;\n }\n if (options?.license !== undefined) {\n metadata.status.license = options.license;\n }\n if (options?.embeddable !== undefined) {\n metadata.status.embeddable = options.embeddable;\n }\n if (options?.publicStatsViewable !== undefined) {\n metadata.status.publicStatsViewable = options.publicStatsViewable;\n }\n if (options?.selfDeclaredMadeForKids !== undefined) {\n metadata.status.selfDeclaredMadeForKids = options.selfDeclaredMadeForKids;\n }\n if (options?.recordingDate) {\n metadata.recordingDetails = { recordingDate: options.recordingDate };\n }\n\n if (scheduledAt) {\n const publishDate = new Date(scheduledAt);\n if (publishDate <= new Date()) {\n throw new SocialError('youtube', 'Scheduled time must be in the future', { statusCode: 400, retryable: false });\n }\n metadata.status.publishAt = publishDate.toISOString();\n }\n\n return metadata;\n }\n\n /**\n * Initialize a resumable upload session (metadata only, no file).\n * Returns a self-authenticating upload URI that the frontend can PUT to directly.\n *\n * Flow: Server calls this → returns uploadUri → Frontend PUTs file to uploadUri\n * The upload URI is valid for ~24 hours and requires no additional auth.\n */\n async initUploadSession(params: InitUploadSessionParams): Promise<InitUploadSessionResult> {\n const { title, description, tags, privacy, categoryId, scheduledAt, credentials, tokens, options } = params;\n\n try {\n const videoMetadata = this._buildVideoMetadata({ title, description, tags, privacy, categoryId, scheduledAt, options });\n\n // Use raw fetch to init resumable upload — we need the Location header\n // which the googleapis SDK doesn't expose cleanly for split init/upload\n const initResponse = await fetch(\n 'https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status,recordingDetails',\n {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${tokens.access_token}`,\n 'Content-Type': 'application/json; charset=UTF-8',\n },\n body: JSON.stringify(videoMetadata),\n },\n );\n\n if (!initResponse.ok) {\n const error = await initResponse.json().catch(() => ({ error: { message: initResponse.statusText } }));\n throw new SocialError('youtube', `Upload session init failed: ${(error as any).error?.message || initResponse.statusText}`, {\n statusCode: initResponse.status,\n });\n }\n\n const uploadUri = initResponse.headers.get('location');\n if (!uploadUri) {\n throw new SocialError('youtube', 'No resumable upload URI returned by YouTube', { statusCode: 502 });\n }\n\n return { uploadUri };\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Upload session init failed: ${error.message}`, { originalError: error });\n }\n }\n\n /**\n * Upload video to YouTube (server-side).\n *\n * Supports two modes:\n * - filePath: Read from local file (original behavior)\n * - videoUrl: Stream-through from URL (no disk IO, for GCS-stored media)\n */\n async uploadVideo(params: UploadParams): Promise<UploadResult> {\n const {\n filePath,\n videoUrl,\n title,\n description = '',\n tags = [],\n privacy = 'private',\n categoryId = '22',\n credentials,\n tokens,\n scheduledAt,\n onProgress,\n } = params;\n\n try {\n const client = this._createOAuthClient(\n credentials.clientId,\n credentials.clientSecret,\n credentials.redirectUri\n );\n client.setCredentials({\n access_token: tokens.access_token,\n refresh_token: tokens.refresh_token,\n });\n\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n // Resolve media source: local file or remote URL\n let mediaBody: NodeJS.ReadableStream;\n let fileSize: number | undefined;\n\n if (filePath) {\n // Local file path (original behavior)\n const fileStats = await stat(filePath);\n fileSize = fileStats.size;\n mediaBody = createReadStream(filePath);\n } else if (videoUrl) {\n // Stream-through from URL (no disk, no temp file)\n const response = await fetch(videoUrl);\n if (!response.ok) {\n throw new SocialError('youtube', `Failed to fetch video from URL: ${response.status} ${response.statusText}`, { statusCode: 502 });\n }\n const contentLength = response.headers.get('content-length');\n fileSize = contentLength ? parseInt(contentLength, 10) : undefined;\n mediaBody = Readable.fromWeb(response.body as any);\n } else {\n throw new SocialError('youtube', 'uploadVideo requires either filePath or videoUrl', { statusCode: 400 });\n }\n\n const videoMetadata = this._buildVideoMetadata({ title: title || '', description, tags, privacy, categoryId, scheduledAt });\n\n const notifySubscribers = (params as any).notifySubscribers;\n const response = await youtube.videos.insert({\n part: ['snippet', 'status', 'recordingDetails'],\n notifySubscribers: notifySubscribers !== undefined ? notifySubscribers : true,\n requestBody: videoMetadata,\n media: { body: mediaBody },\n }, {\n onUploadProgress: (evt: { bytesRead?: number }) => {\n if (onProgress && evt.bytesRead && fileSize) {\n onProgress((evt.bytesRead / fileSize) * 100);\n }\n },\n });\n\n const video = response.data;\n\n return {\n platformVideoId: video.id as string,\n platformUrl: `https://www.youtube.com/watch?v=${video.id}`,\n status: scheduledAt ? 'scheduled' : 'published',\n uploadedAt: new Date(),\n scheduledAt: scheduledAt ? new Date(scheduledAt) : null,\n metadata: {\n title: video.snippet!.title,\n privacy: video.status!.privacyStatus,\n publishAt: video.status!.publishAt || null,\n },\n } as UploadResult;\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n console.error('[YouTubeProvider] Upload failed:', error.message);\n throw new SocialError('youtube', `Upload failed: ${error.message}`, { originalError: error });\n }\n }\n\n // ---------------------------------------------------------------------------\n // Video CRUD\n // ---------------------------------------------------------------------------\n\n /**\n * Parse a YouTube API video item into a normalized YouTubeVideo.\n */\n private _parseVideoItem(item: any): YouTubeVideo {\n return {\n id: item.id as string,\n title: item.snippet?.title ?? '',\n description: item.snippet?.description ?? '',\n publishedAt: item.snippet?.publishedAt ?? '',\n thumbnailUrl: item.snippet?.thumbnails?.default?.url ?? null,\n channelId: item.snippet?.channelId ?? '',\n channelTitle: item.snippet?.channelTitle ?? '',\n tags: item.snippet?.tags ?? [],\n categoryId: item.snippet?.categoryId ?? '',\n privacyStatus: item.status?.privacyStatus ?? '',\n duration: item.contentDetails?.duration ?? null,\n viewCount: item.statistics?.viewCount ?? null,\n likeCount: item.statistics?.likeCount ?? null,\n commentCount: item.statistics?.commentCount ?? null,\n defaultLanguage: item.snippet?.defaultLanguage,\n embeddable: item.status?.embeddable,\n license: item.status?.license,\n madeForKids: item.status?.madeForKids,\n publishAt: item.status?.publishAt ?? null,\n favoriteCount: item.statistics?.favoriteCount ?? null,\n };\n }\n\n /**\n * Get a single video by ID.\n *\n * @param accessToken - OAuth access token\n * @param videoId - YouTube video ID\n * @param credentials - OAuth credentials { clientId, clientSecret }\n * @returns Normalized video data with snippet, stats, and content details\n */\n async getVideo(\n accessToken: string,\n videoId: string,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n ): Promise<YouTubeVideo> {\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n const response = await youtube.videos.list({\n part: ['snippet', 'contentDetails', 'statistics', 'status'],\n id: [videoId],\n });\n\n const items = response.data.items;\n if (!items || items.length === 0) {\n throw new SocialError('youtube', `Video not found: ${videoId}`, {\n statusCode: 404,\n hint: 'Check that the video ID is correct and the video is accessible with your credentials.',\n });\n }\n\n return this._parseVideoItem(items[0]);\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to get video: ${error.message}`, { originalError: error });\n }\n }\n\n /**\n * List videos from the authenticated channel.\n *\n * Uses playlistItems.list on the channel's uploads playlist (1 quota unit per call)\n * instead of search.list (100 quota units per call — avoid).\n *\n * For text search within videos use videos.list with a separate search flow.\n */\n async listVideos(\n accessToken: string,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n options: ListVideosOptions = {},\n ): Promise<ListVideosResult> {\n const { maxResults = 10, pageToken } = options;\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n // Step 1: Get channel's uploads playlist ID (cached after first call ideally)\n const channelResponse = await youtube.channels.list({\n part: ['contentDetails'],\n mine: true,\n });\n const uploadsPlaylistId = channelResponse.data.items?.[0]?.contentDetails?.relatedPlaylists?.uploads;\n if (!uploadsPlaylistId) {\n return { videos: [], nextPageToken: null, prevPageToken: null, totalResults: 0 };\n }\n\n // Step 2: List playlist items (1 quota unit vs search.list's 100)\n const playlistResponse = await youtube.playlistItems.list({\n part: ['snippet', 'contentDetails'],\n playlistId: uploadsPlaylistId,\n maxResults: Math.min(maxResults, 50),\n pageToken,\n });\n const playlistData = playlistResponse.data;\n const videoIds = (playlistData.items ?? [])\n .map((item: any) => item.contentDetails?.videoId)\n .filter(Boolean) as string[];\n\n if (videoIds.length === 0) {\n return { videos: [], nextPageToken: null, prevPageToken: null, totalResults: 0 };\n }\n\n // Step 3: Get full video details\n const detailResponse = await youtube.videos.list({\n part: ['snippet', 'contentDetails', 'statistics', 'status'],\n id: videoIds,\n });\n\n const videos = (detailResponse.data.items ?? []).map((item: any) => this._parseVideoItem(item));\n\n return {\n videos,\n nextPageToken: playlistData.nextPageToken ?? null,\n prevPageToken: playlistData.prevPageToken ?? null,\n totalResults: playlistData.pageInfo?.totalResults ?? videos.length,\n };\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to list videos: ${error.message}`, { originalError: error });\n }\n }\n\n /**\n * Update video metadata (title, description, tags, category, privacy).\n *\n * Only the fields provided in `params` are updated. Omitted fields are left unchanged.\n * Note: You must have ownership of the video.\n *\n * @param accessToken - OAuth access token\n * @param videoId - YouTube video ID to update\n * @param params - Fields to update\n * @param credentials - OAuth credentials { clientId, clientSecret }\n */\n async updateVideo(\n accessToken: string,\n videoId: string,\n params: UpdateVideoParams,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n ): Promise<YouTubeVideo> {\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n const requestBody: Record<string, any> = {\n id: videoId,\n snippet: {},\n status: {},\n };\n\n if (params.title !== undefined) requestBody.snippet.title = params.title;\n if (params.description !== undefined) requestBody.snippet.description = params.description;\n if (params.tags !== undefined) requestBody.snippet.tags = params.tags;\n if (params.categoryId !== undefined) requestBody.snippet.categoryId = params.categoryId;\n if (params.defaultLanguage !== undefined) requestBody.snippet.defaultLanguage = params.defaultLanguage;\n if (params.privacy !== undefined) requestBody.status.privacyStatus = params.privacy;\n if (params.embeddable !== undefined) requestBody.status.embeddable = params.embeddable;\n if (params.license !== undefined) requestBody.status.license = params.license;\n if (params.publicStatsViewable !== undefined) requestBody.status.publicStatsViewable = params.publicStatsViewable;\n if (params.selfDeclaredMadeForKids !== undefined) requestBody.status.selfDeclaredMadeForKids = params.selfDeclaredMadeForKids;\n\n const response = await youtube.videos.update({\n part: ['snippet', 'status'],\n requestBody,\n });\n\n const item = response.data;\n return {\n id: item.id as string,\n title: item.snippet?.title ?? '',\n description: item.snippet?.description ?? '',\n publishedAt: '',\n thumbnailUrl: null,\n channelId: '',\n channelTitle: '',\n tags: item.snippet?.tags ?? [],\n categoryId: item.snippet?.categoryId ?? '',\n privacyStatus: item.status?.privacyStatus ?? '',\n duration: null,\n viewCount: null,\n likeCount: null,\n commentCount: null,\n };\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to update video: ${error.message}`, { originalError: error });\n }\n }\n\n /**\n * Delete a video by ID.\n *\n * @param accessToken - OAuth access token\n * @param videoId - YouTube video ID to delete\n * @param credentials - OAuth credentials { clientId, clientSecret }\n */\n async deleteVideo(\n accessToken: string,\n videoId: string,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n ): Promise<void> {\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n await youtube.videos.delete({ id: videoId });\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to delete video: ${error.message}`, { originalError: error });\n }\n }\n\n // ---------------------------------------------------------------------------\n // Playlists CRUD\n // ---------------------------------------------------------------------------\n\n private _parsePlaylist(item: any): YouTubePlaylist {\n return {\n id: item.id as string,\n title: item.snippet?.title ?? '',\n description: item.snippet?.description ?? '',\n publishedAt: item.snippet?.publishedAt ?? '',\n channelId: item.snippet?.channelId ?? '',\n thumbnailUrl: item.snippet?.thumbnails?.default?.url ?? null,\n itemCount: item.contentDetails?.itemCount ?? 0,\n privacyStatus: item.status?.privacyStatus ?? '',\n defaultLanguage: item.snippet?.defaultLanguage,\n };\n }\n\n private _parsePlaylistItem(item: any): PlaylistItem {\n return {\n id: item.id as string,\n playlistId: item.snippet?.playlistId ?? '',\n videoId: item.snippet?.resourceId?.videoId ?? '',\n title: item.snippet?.title ?? '',\n description: item.snippet?.description ?? '',\n thumbnailUrl: item.snippet?.thumbnails?.default?.url ?? null,\n position: item.snippet?.position ?? 0,\n };\n }\n\n async createPlaylist(\n accessToken: string,\n params: CreatePlaylistParams,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n ): Promise<YouTubePlaylist> {\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n const requestBody: Record<string, any> = {\n snippet: { title: params.title },\n status: {},\n };\n if (params.description !== undefined) requestBody.snippet.description = params.description;\n if (params.tags) requestBody.snippet.tags = params.tags;\n if (params.defaultLanguage) requestBody.snippet.defaultLanguage = params.defaultLanguage;\n if (params.privacy) requestBody.status.privacyStatus = params.privacy;\n\n const response = await youtube.playlists.insert({ part: ['snippet', 'status'], requestBody });\n return this._parsePlaylist(response.data);\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to create playlist: ${error.message}`, { originalError: error });\n }\n }\n\n async getPlaylist(\n accessToken: string,\n playlistId: string,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n ): Promise<YouTubePlaylist> {\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n const response = await youtube.playlists.list({\n part: ['snippet', 'contentDetails', 'status'],\n id: [playlistId],\n });\n\n const items = response.data.items;\n if (!items || items.length === 0) {\n throw new SocialError('youtube', `Playlist not found: ${playlistId}`, { statusCode: 404 });\n }\n return this._parsePlaylist(items[0]);\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to get playlist: ${error.message}`, { originalError: error });\n }\n }\n\n async listPlaylists(\n accessToken: string,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n options: ListPlaylistsOptions = {},\n ): Promise<ListPlaylistsResult> {\n const { maxResults = 25, pageToken, mine = true, channelId } = options;\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n const params: Record<string, any> = {\n part: ['snippet', 'contentDetails', 'status'],\n maxResults: Math.min(maxResults, 50),\n };\n if (channelId) {\n params.channelId = channelId;\n } else if (mine) {\n params.mine = true;\n }\n if (pageToken) params.pageToken = pageToken;\n\n const response = await youtube.playlists.list(params);\n const data = response.data;\n\n return {\n playlists: (data.items ?? []).map((item: any) => this._parsePlaylist(item)),\n nextPageToken: data.nextPageToken ?? null,\n prevPageToken: data.prevPageToken ?? null,\n totalResults: data.pageInfo?.totalResults ?? 0,\n };\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to list playlists: ${error.message}`, { originalError: error });\n }\n }\n\n async updatePlaylist(\n accessToken: string,\n playlistId: string,\n params: UpdatePlaylistParams,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n ): Promise<YouTubePlaylist> {\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n const requestBody: Record<string, any> = { id: playlistId, snippet: {}, status: {} };\n if (params.title !== undefined) requestBody.snippet.title = params.title;\n if (params.description !== undefined) requestBody.snippet.description = params.description;\n if (params.tags !== undefined) requestBody.snippet.tags = params.tags;\n if (params.defaultLanguage !== undefined) requestBody.snippet.defaultLanguage = params.defaultLanguage;\n if (params.privacy !== undefined) requestBody.status.privacyStatus = params.privacy;\n\n const response = await youtube.playlists.update({ part: ['snippet', 'status'], requestBody });\n return this._parsePlaylist(response.data);\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to update playlist: ${error.message}`, { originalError: error });\n }\n }\n\n async deletePlaylist(\n accessToken: string,\n playlistId: string,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n ): Promise<void> {\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n await youtube.playlists.delete({ id: playlistId });\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to delete playlist: ${error.message}`, { originalError: error });\n }\n }\n\n // ---------------------------------------------------------------------------\n // Playlist Items\n // ---------------------------------------------------------------------------\n\n async addToPlaylist(\n accessToken: string,\n playlistId: string,\n videoId: string,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n options: AddToPlaylistOptions = {},\n ): Promise<PlaylistItem> {\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n const requestBody: Record<string, any> = {\n snippet: {\n playlistId,\n resourceId: { kind: 'youtube#video', videoId },\n },\n };\n if (options.position !== undefined) requestBody.snippet.position = options.position;\n if (options.note) requestBody.contentDetails = { note: options.note };\n\n const response = await youtube.playlistItems.insert({ part: ['snippet', 'contentDetails'], requestBody });\n return this._parsePlaylistItem(response.data);\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to add to playlist: ${error.message}`, { originalError: error });\n }\n }\n\n async removeFromPlaylist(\n accessToken: string,\n playlistItemId: string,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n ): Promise<void> {\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n await youtube.playlistItems.delete({ id: playlistItemId });\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to remove from playlist: ${error.message}`, { originalError: error });\n }\n }\n\n async listPlaylistItems(\n accessToken: string,\n playlistId: string,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n options: ListPlaylistItemsOptions = {},\n ): Promise<ListPlaylistItemsResult> {\n const { maxResults = 25, pageToken } = options;\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n const params: Record<string, any> = {\n part: ['snippet', 'contentDetails'],\n playlistId,\n maxResults: Math.min(maxResults, 50),\n };\n if (pageToken) params.pageToken = pageToken;\n\n const response = await youtube.playlistItems.list(params);\n const data = response.data;\n\n return {\n items: (data.items ?? []).map((item: any) => this._parsePlaylistItem(item)),\n nextPageToken: data.nextPageToken ?? null,\n prevPageToken: data.prevPageToken ?? null,\n totalResults: data.pageInfo?.totalResults ?? 0,\n };\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to list playlist items: ${error.message}`, { originalError: error });\n }\n }\n\n // ---------------------------------------------------------------------------\n // Search\n // ---------------------------------------------------------------------------\n\n /**\n * Search YouTube videos with comprehensive filters.\n * Uses a 2-step approach: search.list → videos.list for full details.\n */\n async searchVideos(\n accessToken: string,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n options: SearchVideosOptions = {},\n ): Promise<SearchVideosResult> {\n const { q, publishedAfter, publishedBefore, videoCategoryId, channelId, order = 'relevance', safeSearch, videoType, regionCode, maxResults = 25, pageToken } = options;\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n const searchParams: Record<string, any> = {\n part: ['id'],\n type: ['video'],\n maxResults: Math.min(maxResults, 50),\n order,\n };\n if (q) searchParams.q = q;\n if (publishedAfter) searchParams.publishedAfter = publishedAfter;\n if (publishedBefore) searchParams.publishedBefore = publishedBefore;\n if (videoCategoryId) searchParams.videoCategoryId = videoCategoryId;\n if (channelId) searchParams.channelId = channelId;\n if (safeSearch) searchParams.safeSearch = safeSearch;\n if (videoType) searchParams.videoType = videoType;\n if (regionCode) searchParams.regionCode = regionCode;\n if (pageToken) searchParams.pageToken = pageToken;\n\n // If no channelId or q, search user's own videos\n if (!channelId && !q) searchParams.forMine = true;\n\n const searchResponse = await youtube.search.list(searchParams);\n const searchData = searchResponse.data;\n const videoIds = (searchData.items ?? [])\n .map((item: any) => item.id?.videoId)\n .filter(Boolean) as string[];\n\n if (videoIds.length === 0) {\n return { videos: [], nextPageToken: null, prevPageToken: null, totalResults: 0 };\n }\n\n // Step 2: Full video details\n const detailResponse = await youtube.videos.list({\n part: ['snippet', 'contentDetails', 'statistics', 'status'],\n id: videoIds,\n });\n\n return {\n videos: (detailResponse.data.items ?? []).map((item: any) => this._parseVideoItem(item)),\n nextPageToken: searchData.nextPageToken ?? null,\n prevPageToken: searchData.prevPageToken ?? null,\n totalResults: searchData.pageInfo?.totalResults ?? 0,\n };\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Search failed: ${error.message}`, { originalError: error });\n }\n }\n\n // ---------------------------------------------------------------------------\n // Video Categories\n // ---------------------------------------------------------------------------\n\n /**\n * Get available video categories for a region.\n * @param regionCode - ISO 3166-1 alpha-2 country code (e.g. 'US', 'BD')\n */\n async getVideoCategories(\n accessToken: string,\n regionCode: string,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n ): Promise<YouTubeCategory[]> {\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n const response = await youtube.videoCategories.list({\n part: ['snippet'],\n regionCode,\n });\n\n return (response.data.items ?? []).map((item: any) => ({\n id: item.id as string,\n title: item.snippet?.title ?? '',\n assignable: item.snippet?.assignable ?? false,\n }));\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to get video categories: ${error.message}`, { originalError: error });\n }\n }\n\n // ---------------------------------------------------------------------------\n // Video Rating\n // ---------------------------------------------------------------------------\n\n /**\n * Rate a video (like, dislike, or remove rating).\n */\n async rateVideo(\n accessToken: string,\n videoId: string,\n rating: YouTubeVideoRating,\n credentials: YouTubeCredentials = {} as YouTubeCredentials,\n ): Promise<void> {\n const client = this._createOAuthClient(credentials.clientId, credentials.clientSecret, credentials.redirectUri);\n client.setCredentials({ access_token: accessToken });\n const youtube = google.youtube({ version: 'v3', auth: client });\n\n try {\n await youtube.videos.rate({ id: videoId, rating });\n } catch (error: any) {\n if (error instanceof SocialError) throw error;\n throw new SocialError('youtube', `Failed to rate video: ${error.message}`, { originalError: error });\n }\n }\n\n // ---------------------------------------------------------------------------\n // Schema & Metadata\n // ---------------------------------------------------------------------------\n\n /**\n * Return the zod schema for runtime validation. Consumers can call\n * `z.toJSONSchema(provider.getCredentialZodSchema())` to render UI forms\n * or generate OpenAPI specs.\n */\n getCredentialZodSchema(): z.ZodType {\n return YouTubeCredentialsSchema;\n }\n\n /**\n * Get credential schema for UI (legacy CredentialField[] shape).\n */\n getCredentialSchema(): CredentialField[] {\n return [\n {\n name: 'clientId',\n displayName: 'Client ID',\n type: 'text',\n required: true,\n description: 'Google OAuth2 Client ID from Google Cloud Console',\n },\n {\n name: 'clientSecret',\n displayName: 'Client Secret',\n type: 'password',\n required: true,\n description: 'Google OAuth2 Client Secret',\n },\n ];\n }\n\n /**\n * Get provider metadata for frontend display\n */\n getMetadata(): ProviderMetadata {\n return {\n name: this.name,\n displayName: this.displayName,\n authType: this.authType,\n icon: 'youtube',\n brandColor: '#FF0000',\n supportsScheduling: true,\n supportsEnvironment: false,\n description: 'Upload and manage videos on YouTube',\n scopes: this.scopes,\n scopeDescriptions: {\n 'https://www.googleapis.com/auth/youtube.upload': 'Upload videos to your channel',\n 'https://www.googleapis.com/auth/youtube': 'Manage your YouTube account',\n 'https://www.googleapis.com/auth/userinfo.profile': 'View your basic profile info',\n },\n setupGuide: [\n { step: 1, title: 'Create Google Cloud Project', description: 'Go to console.cloud.google.com and create a new project or select an existing one' },\n { step: 2, title: 'Enable YouTube Data API v3', description: 'Navigate to APIs & Services > Library, search for \"YouTube Data API v3\" and enable it' },\n { step: 3, title: 'Create OAuth Credentials', description: 'Go to APIs & Services > Credentials, click \"Create Credentials\" > \"OAuth 2.0 Client ID\" for Web application' },\n { step: 4, title: 'Set Redirect URI', description: `Add this as an authorized redirect URI: ${this.defaultRedirectUri}` },\n { step: 5, title: 'Configure Consent Screen', description: 'Set up the OAuth consent screen with your app name and add your email as a test user' },\n ],\n redirectUriPattern: this.defaultRedirectUri,\n credentialSchema: this.getCredentialSchema(),\n };\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ import { t as __exportAll } from "./chunk-DQk6qfdC.mjs";
2
+ import { i as OAuthTokensSchema, r as NonEmptyString } from "./shared-Fvc6xQku.mjs";
3
+ import { z } from "zod";
4
+
5
+ //#region src/schemas/reddit.ts
6
+ /**
7
+ * Reddit zod v4 schemas.
8
+ */
9
+ var reddit_exports = /* @__PURE__ */ __exportAll({
10
+ RedditCreateCommentSchema: () => RedditCreateCommentSchema,
11
+ RedditCreatePostSchema: () => RedditCreatePostSchema,
12
+ RedditCredentialDataSchema: () => RedditCredentialDataSchema,
13
+ RedditCredentialsSchema: () => RedditCredentialsSchema,
14
+ RedditListPostsSchema: () => RedditListPostsSchema,
15
+ RedditListSort: () => RedditListSort,
16
+ RedditPostKind: () => RedditPostKind,
17
+ RedditSearchSchema: () => RedditSearchSchema,
18
+ RedditVoteSchema: () => RedditVoteSchema,
19
+ redditCapabilities: () => redditCapabilities,
20
+ redditInfo: () => redditInfo
21
+ });
22
+ const RedditCredentialsSchema = z.object({
23
+ clientId: NonEmptyString.describe("Reddit app client ID"),
24
+ clientSecret: NonEmptyString.describe("Reddit app client secret"),
25
+ userAgent: NonEmptyString.describe("User-Agent — e.g., `web:my-app:v1.0 (by /u/yourname)`"),
26
+ redirectUri: z.url().optional()
27
+ });
28
+ const RedditCredentialDataSchema = RedditCredentialsSchema.extend({ oauthTokenData: z.union([z.string(), OAuthTokensSchema]).optional() });
29
+ const SubredditName = z.string().regex(/^[A-Za-z0-9_]{2,21}$/, "Subreddit must be 2–21 characters of letters, digits, or underscores");
30
+ const RedditPostKind = z.enum([
31
+ "self",
32
+ "link",
33
+ "image",
34
+ "video"
35
+ ]);
36
+ const RedditCreatePostSchema = z.discriminatedUnion("kind", [z.object({
37
+ kind: z.literal("self"),
38
+ subreddit: SubredditName,
39
+ title: z.string().min(1).max(300),
40
+ text: z.string().max(4e4).optional(),
41
+ nsfw: z.boolean().optional().default(false),
42
+ spoiler: z.boolean().optional().default(false),
43
+ sendReplies: z.boolean().optional().default(true),
44
+ flairId: z.string().optional(),
45
+ flairText: z.string().optional()
46
+ }), z.object({
47
+ kind: z.literal("link"),
48
+ subreddit: SubredditName,
49
+ title: z.string().min(1).max(300),
50
+ url: z.url(),
51
+ nsfw: z.boolean().optional().default(false),
52
+ spoiler: z.boolean().optional().default(false),
53
+ sendReplies: z.boolean().optional().default(true),
54
+ flairId: z.string().optional(),
55
+ flairText: z.string().optional()
56
+ })]);
57
+ const RedditCreateCommentSchema = z.object({
58
+ parentFullname: z.string().regex(/^t[1-6]_[A-Za-z0-9]+$/, "Must be a Reddit fullname (e.g. t3_abc, t1_xyz)"),
59
+ text: z.string().min(1).max(1e4)
60
+ });
61
+ const RedditVoteSchema = z.object({
62
+ fullname: z.string().regex(/^t[1-6]_[A-Za-z0-9]+$/),
63
+ direction: z.union([
64
+ z.literal(1),
65
+ z.literal(0),
66
+ z.literal(-1)
67
+ ]).describe("1 = upvote, 0 = remove vote, -1 = downvote")
68
+ });
69
+ const RedditListSort = z.enum([
70
+ "hot",
71
+ "new",
72
+ "top",
73
+ "rising",
74
+ "controversial"
75
+ ]);
76
+ const RedditListPostsSchema = z.object({
77
+ subreddit: SubredditName,
78
+ sort: RedditListSort.optional().default("hot"),
79
+ limit: z.number().int().min(1).max(100).optional().default(25),
80
+ after: z.string().optional(),
81
+ before: z.string().optional()
82
+ });
83
+ const RedditSearchSchema = z.object({
84
+ query: z.string().min(1),
85
+ subreddit: SubredditName.optional(),
86
+ sort: z.enum([
87
+ "relevance",
88
+ "hot",
89
+ "top",
90
+ "new",
91
+ "comments"
92
+ ]).optional(),
93
+ limit: z.number().int().min(1).max(100).optional().default(25)
94
+ });
95
+ const redditCapabilities = {
96
+ auth: "oauth2",
97
+ posting: true,
98
+ upload: false,
99
+ messaging: false,
100
+ scheduling: false,
101
+ deletion: true,
102
+ listing: true,
103
+ analytics: false,
104
+ environments: false,
105
+ pkce: false
106
+ };
107
+ const redditInfo = {
108
+ name: "reddit",
109
+ displayName: "Reddit",
110
+ scopes: [
111
+ "identity",
112
+ "submit",
113
+ "read",
114
+ "edit",
115
+ "history",
116
+ "mysubreddits",
117
+ "subscribe",
118
+ "vote"
119
+ ],
120
+ rateLimits: { perMinute: 100 },
121
+ docsUrl: "https://www.reddit.com/dev/api"
122
+ };
123
+
124
+ //#endregion
125
+ export { reddit_exports as i, redditCapabilities as n, redditInfo as r, RedditCredentialsSchema as t };
126
+ //# sourceMappingURL=reddit-B10kS4Se.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reddit-B10kS4Se.mjs","names":[],"sources":["../src/schemas/reddit.ts"],"sourcesContent":["/**\n * Reddit zod v4 schemas.\n */\n\nimport { z } from 'zod';\nimport type { ProviderCapabilities } from '../common/contracts.js';\nimport { NonEmptyString, OAuthTokensSchema } from './shared.js';\n\n// ─── Credentials ────────────────────────────────────────────────────────────\n\nexport const RedditCredentialsSchema = z.object({\n clientId: NonEmptyString.describe('Reddit app client ID'),\n clientSecret: NonEmptyString.describe('Reddit app client secret'),\n /** Reddit requires a unique, descriptive User-Agent (script:appname:v1 by /u/handle). */\n userAgent: NonEmptyString.describe('User-Agent — e.g., `web:my-app:v1.0 (by /u/yourname)`'),\n redirectUri: z.url().optional(),\n});\nexport type RedditCredentialsInput = z.infer<typeof RedditCredentialsSchema>;\n\nexport const RedditCredentialDataSchema = RedditCredentialsSchema.extend({\n oauthTokenData: z.union([z.string(), OAuthTokensSchema]).optional(),\n});\n\n// ─── Subreddit name ─────────────────────────────────────────────────────────\n\nconst SubredditName = z.string().regex(\n /^[A-Za-z0-9_]{2,21}$/,\n 'Subreddit must be 2–21 characters of letters, digits, or underscores',\n);\n\n// ─── Post creation ──────────────────────────────────────────────────────────\n\nexport const RedditPostKind = z.enum(['self', 'link', 'image', 'video']);\n\nexport const RedditCreatePostSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('self'),\n subreddit: SubredditName,\n title: z.string().min(1).max(300),\n text: z.string().max(40_000).optional(),\n nsfw: z.boolean().optional().default(false),\n spoiler: z.boolean().optional().default(false),\n sendReplies: z.boolean().optional().default(true),\n flairId: z.string().optional(),\n flairText: z.string().optional(),\n }),\n z.object({\n kind: z.literal('link'),\n subreddit: SubredditName,\n title: z.string().min(1).max(300),\n url: z.url(),\n nsfw: z.boolean().optional().default(false),\n spoiler: z.boolean().optional().default(false),\n sendReplies: z.boolean().optional().default(true),\n flairId: z.string().optional(),\n flairText: z.string().optional(),\n }),\n]);\n\n// ─── Comments / votes ───────────────────────────────────────────────────────\n\nexport const RedditCreateCommentSchema = z.object({\n parentFullname: z.string().regex(/^t[1-6]_[A-Za-z0-9]+$/, 'Must be a Reddit fullname (e.g. t3_abc, t1_xyz)'),\n text: z.string().min(1).max(10_000),\n});\n\nexport const RedditVoteSchema = z.object({\n fullname: z.string().regex(/^t[1-6]_[A-Za-z0-9]+$/),\n direction: z.union([z.literal(1), z.literal(0), z.literal(-1)])\n .describe('1 = upvote, 0 = remove vote, -1 = downvote'),\n});\n\n// ─── Listing options ────────────────────────────────────────────────────────\n\nexport const RedditListSort = z.enum(['hot', 'new', 'top', 'rising', 'controversial']);\n\nexport const RedditListPostsSchema = z.object({\n subreddit: SubredditName,\n sort: RedditListSort.optional().default('hot'),\n limit: z.number().int().min(1).max(100).optional().default(25),\n after: z.string().optional(),\n before: z.string().optional(),\n});\n\nexport const RedditSearchSchema = z.object({\n query: z.string().min(1),\n subreddit: SubredditName.optional(),\n sort: z.enum(['relevance', 'hot', 'top', 'new', 'comments']).optional(),\n limit: z.number().int().min(1).max(100).optional().default(25),\n});\n\n// ─── Capabilities ───────────────────────────────────────────────────────────\n\nexport const redditCapabilities: ProviderCapabilities = {\n auth: 'oauth2',\n posting: true,\n upload: false,\n messaging: false,\n scheduling: false,\n deletion: true,\n listing: true,\n analytics: false,\n environments: false,\n pkce: false,\n};\n\nexport const redditInfo = {\n name: 'reddit' as const,\n displayName: 'Reddit',\n scopes: ['identity', 'submit', 'read', 'edit', 'history', 'mysubreddits', 'subscribe', 'vote'],\n /** OAuth: 100 QPM per user. Sleep ≥1s between requests. */\n rateLimits: { perMinute: 100 },\n docsUrl: 'https://www.reddit.com/dev/api',\n} as const;\n"],"mappings":"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"}