@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,450 @@
1
+ import { t as PlatformProvider } from "../base-Bw7e52V8.mjs";
2
+ import { t as SocialError } from "../errors-Cm6LeKf7.mjs";
3
+ import { a as parseGraphError, c as META_GRAPH_VERSION, l as META_GRAPH_VIDEO_BASE, n as metaExchangeLongLived, o as META_AUTH_URL, r as metaRefreshLongLivedFacebook, s as META_GRAPH_BASE } from "../meta-D3vcJU1c.mjs";
4
+ import { t as httpRequest } from "../http-DpcLSR1M.mjs";
5
+ import { t as FacebookCredentialsSchema } from "../facebook-l_4CghaA.mjs";
6
+
7
+ //#region src/providers/facebook/index.ts
8
+ /**
9
+ * Facebook Pages Platform Provider
10
+ * =================================
11
+ * Facebook Page integration via Meta Graph API v25.0.
12
+ *
13
+ * Features:
14
+ * - OAuth2 via Facebook Login dialog
15
+ * - Two-step token exchange (short-lived -> long-lived, 60 days)
16
+ * - Facebook Page discovery and page-level access tokens
17
+ * - Text post publishing
18
+ * - Link/article sharing
19
+ * - Photo post publishing (via URL)
20
+ * - Video post publishing (via URL, uses graph-video.facebook.com)
21
+ * - Scheduled post publishing
22
+ * - Post management (get, update, delete)
23
+ * - Page insights/analytics
24
+ * - Credential validation
25
+ *
26
+ * Facebook Page API Quirks:
27
+ * - User tokens vs Page tokens: user token grants access to /me/accounts;
28
+ * each page in the response includes its own page-specific access_token
29
+ * - Page tokens never expire if derived from a long-lived user token
30
+ * - Video uploads use graph-video.facebook.com (not graph.facebook.com)
31
+ * - Scheduled posts: set published=false + scheduled_publish_time (Unix epoch, 10 min-6 months)
32
+ * - Photos accept URLs or binary upload (we use URL-based for simplicity, matching Instagram pattern)
33
+ * - Webhook signature validation uses SHA256 HMAC of app secret
34
+ *
35
+ * @see https://developers.facebook.com/docs/pages-api
36
+ * @see https://developers.facebook.com/docs/graph-api/reference/page/feed
37
+ * @see https://developers.facebook.com/docs/video-api/guides/publishing
38
+ */
39
+ const GRAPH_API_VERSION = META_GRAPH_VERSION;
40
+ const FB_AUTH_URL = META_AUTH_URL;
41
+ const GRAPH_API_BASE = META_GRAPH_BASE;
42
+ const GRAPH_VIDEO_BASE = META_GRAPH_VIDEO_BASE;
43
+ var FacebookProvider = class extends PlatformProvider {
44
+ defaultRedirectUri;
45
+ scopes;
46
+ constructor(cfg = {}) {
47
+ super(cfg);
48
+ this.name = "facebook";
49
+ this.displayName = "Facebook Pages";
50
+ this.authType = "oauth2";
51
+ this.defaultRedirectUri = cfg.redirectUri || `http://localhost:${cfg.port || 8060}/api/oauth/facebook/callback`;
52
+ this.scopes = [
53
+ "business_management",
54
+ "pages_show_list",
55
+ "pages_read_engagement",
56
+ "pages_manage_posts",
57
+ "pages_read_user_content"
58
+ ];
59
+ }
60
+ /**
61
+ * Unified Graph API request — used by all GET / POST / DELETE call sites.
62
+ * Delegates to the shared `httpRequest` helper for retry, timeout, and 429 handling.
63
+ *
64
+ * @param method HTTP method
65
+ * @param path Graph API path (e.g. `/me/accounts`)
66
+ * @param accessToken Access token (sent as `access_token` query for GET/DELETE; body field for POST)
67
+ * @param body POST body (merged with `access_token`); ignored for GET/DELETE
68
+ * @param query Extra query params for GET
69
+ * @param baseUrl Optional base URL override (video uploads use graph-video.facebook.com)
70
+ */
71
+ async _graph(method, path, accessToken, body, query, baseUrl = GRAPH_API_BASE) {
72
+ const isWriteWithBody = method === "POST" && body !== void 0;
73
+ return (await httpRequest("facebook", {
74
+ method,
75
+ url: `${baseUrl}${path}`,
76
+ query: !isWriteWithBody ? {
77
+ access_token: accessToken,
78
+ ...query ?? {}
79
+ } : query,
80
+ json: isWriteWithBody ? {
81
+ ...body,
82
+ access_token: accessToken
83
+ } : void 0,
84
+ timeout: 6e4,
85
+ retry: { attempts: 2 },
86
+ parseError: parseGraphError
87
+ })).data;
88
+ }
89
+ async _graphGet(path, accessToken, params = {}) {
90
+ return this._graph("GET", path, accessToken, void 0, params);
91
+ }
92
+ async _graphPost(path, body, accessToken, baseUrl = GRAPH_API_BASE) {
93
+ return this._graph("POST", path, accessToken, body, void 0, baseUrl);
94
+ }
95
+ async _graphDelete(path, accessToken) {
96
+ return this._graph("DELETE", path, accessToken);
97
+ }
98
+ /**
99
+ * Get Facebook OAuth authorization URL
100
+ */
101
+ getAuthUrl(state, credentials = {}, _options) {
102
+ return `${FB_AUTH_URL}?${new URLSearchParams({
103
+ client_id: credentials.appId,
104
+ redirect_uri: credentials.redirectUri || this.defaultRedirectUri,
105
+ scope: this.scopes.join(","),
106
+ response_type: "code",
107
+ state
108
+ }).toString()}`;
109
+ }
110
+ /**
111
+ * Exchange authorization code for tokens (two-step).
112
+ *
113
+ * Step 1: code → short-lived token (1 hour)
114
+ * Step 2: short-lived → long-lived token (60 days)
115
+ */
116
+ async exchangeCode(code, credentials = {}) {
117
+ return metaExchangeLongLived("facebook", {
118
+ graphVersion: GRAPH_API_VERSION,
119
+ clientId: credentials.appId,
120
+ clientSecret: credentials.appSecret,
121
+ redirectUri: credentials.redirectUri || this.defaultRedirectUri,
122
+ code
123
+ });
124
+ }
125
+ /**
126
+ * Refresh long-lived user token. Facebook long-lived tokens can be refreshed
127
+ * before expiry by re-exchanging them via `fb_exchange_token`.
128
+ */
129
+ async refreshToken(refreshToken, credentials = {}) {
130
+ return metaRefreshLongLivedFacebook({
131
+ graphVersion: GRAPH_API_VERSION,
132
+ clientId: credentials.appId,
133
+ clientSecret: credentials.appSecret,
134
+ refreshToken
135
+ });
136
+ }
137
+ /**
138
+ * Get user's Facebook Pages with page-level access tokens
139
+ * Each page has its own never-expiring token (when derived from long-lived user token)
140
+ *
141
+ * @param userAccessToken - Long-lived user access token
142
+ * @returns List of pages with { id, name, access_token, category, ... }
143
+ */
144
+ async getPages(userAccessToken) {
145
+ return (await this._graphGet("/me/accounts", userAccessToken, { fields: "id,name,access_token,category,picture{url},fan_count,followers_count" })).data || [];
146
+ }
147
+ /**
148
+ * Get account information -- returns the first managed page
149
+ */
150
+ async getAccountInfo(accessToken) {
151
+ const pages = await this.getPages(accessToken);
152
+ if (!pages.length) throw new SocialError("facebook", "No Facebook Pages found. Your account must manage at least one Facebook Page.");
153
+ const page = pages[0];
154
+ return {
155
+ id: page.id,
156
+ name: page.name,
157
+ profileImage: page.picture?.data?.url || null,
158
+ category: page.category,
159
+ fanCount: page.fan_count,
160
+ pageAccessToken: page.access_token,
161
+ pageId: page.id,
162
+ pages: pages.map((p) => ({
163
+ id: p.id,
164
+ name: p.name,
165
+ category: p.category,
166
+ profileImage: p.picture?.data?.url || null
167
+ }))
168
+ };
169
+ }
170
+ /**
171
+ * Test credential validity
172
+ */
173
+ async testCredential(credentialData) {
174
+ try {
175
+ if (credentialData.oauthTokenData) {
176
+ const tokenData = typeof credentialData.oauthTokenData === "string" ? JSON.parse(credentialData.oauthTokenData) : credentialData.oauthTokenData;
177
+ const accountInfo = await this.getAccountInfo(tokenData.access_token);
178
+ return {
179
+ status: "OK",
180
+ message: `Connected — managing ${accountInfo.pages.length} page(s)`,
181
+ data: {
182
+ channelId: accountInfo.id,
183
+ channelTitle: accountInfo.name,
184
+ profileImage: accountInfo.profileImage,
185
+ pages: accountInfo.pages
186
+ }
187
+ };
188
+ }
189
+ if (!credentialData.appId || !credentialData.appSecret) return {
190
+ status: "Error",
191
+ message: "Meta App ID and App Secret are required"
192
+ };
193
+ return {
194
+ status: "Pending",
195
+ message: "Credential needs OAuth authorization. Click \"Connect Account\" to link your Facebook."
196
+ };
197
+ } catch (error) {
198
+ return {
199
+ status: "Error",
200
+ message: error.message || "Failed to validate Facebook credential"
201
+ };
202
+ }
203
+ }
204
+ /**
205
+ * Get the page-level access token for a specific page
206
+ * Page tokens derived from long-lived user tokens never expire.
207
+ *
208
+ * @param userAccessToken - Long-lived user access token
209
+ * @param pageId - Facebook Page ID
210
+ * @returns Page access token
211
+ */
212
+ async getPageAccessToken(userAccessToken, pageId) {
213
+ const page = (await this.getPages(userAccessToken)).find((p) => p.id === pageId);
214
+ if (!page) throw new SocialError("facebook", `Page ${pageId} not found or you don't have admin access. Check your page permissions.`);
215
+ return page.access_token;
216
+ }
217
+ /**
218
+ * Publish a text post to a Facebook Page
219
+ * @param pageAccessToken - Page-level access token
220
+ * @param pageId - Facebook Page ID
221
+ * @param message - Post text
222
+ * @param options
223
+ */
224
+ async createPost(pageAccessToken, pageId, message, options = {}) {
225
+ const body = { message };
226
+ if (options.scheduledAt) {
227
+ const publishTime = Math.floor(new Date(options.scheduledAt).getTime() / 1e3);
228
+ body.published = false;
229
+ body.scheduled_publish_time = publishTime;
230
+ }
231
+ return {
232
+ postId: (await this._graphPost(`/${pageId}/feed`, body, pageAccessToken)).id,
233
+ status: options.scheduledAt ? "scheduled" : "published",
234
+ scheduledAt: options.scheduledAt ? new Date(options.scheduledAt) : null
235
+ };
236
+ }
237
+ /**
238
+ * Publish a link/article share to a Facebook Page
239
+ * @param pageAccessToken
240
+ * @param pageId
241
+ * @param link - URL to share
242
+ * @param options
243
+ */
244
+ async createLinkPost(pageAccessToken, pageId, link, options = {}) {
245
+ const body = { link };
246
+ if (options.message) body.message = options.message;
247
+ if (options.scheduledAt) {
248
+ const publishTime = Math.floor(new Date(options.scheduledAt).getTime() / 1e3);
249
+ body.published = false;
250
+ body.scheduled_publish_time = publishTime;
251
+ }
252
+ return {
253
+ postId: (await this._graphPost(`/${pageId}/feed`, body, pageAccessToken)).id,
254
+ status: options.scheduledAt ? "scheduled" : "published"
255
+ };
256
+ }
257
+ /**
258
+ * Publish a photo to a Facebook Page (via public URL)
259
+ * @param pageAccessToken
260
+ * @param pageId
261
+ * @param photoUrl - Publicly accessible photo URL
262
+ * @param options
263
+ */
264
+ async createPhotoPost(pageAccessToken, pageId, photoUrl, options = {}) {
265
+ const body = { url: photoUrl };
266
+ if (options.caption) body.message = options.caption;
267
+ if (options.scheduledAt) {
268
+ const publishTime = Math.floor(new Date(options.scheduledAt).getTime() / 1e3);
269
+ body.published = false;
270
+ body.scheduled_publish_time = publishTime;
271
+ }
272
+ const data = await this._graphPost(`/${pageId}/photos`, body, pageAccessToken);
273
+ return {
274
+ postId: data.post_id || data.id,
275
+ photoId: data.id,
276
+ status: options.scheduledAt ? "scheduled" : "published"
277
+ };
278
+ }
279
+ /**
280
+ * Upload a video to a Facebook Page (via public URL)
281
+ * Uses graph-video.facebook.com for video uploads.
282
+ */
283
+ async uploadVideo(params) {
284
+ const { videoUrl, title = "", description = "", tokens, scheduledAt, onProgress } = params;
285
+ const pageAccessToken = tokens.pageAccessToken || tokens.access_token;
286
+ const pageId = tokens.pageId;
287
+ if (!pageId) throw new SocialError("facebook", "Page ID is required. Select a page from your connected pages.");
288
+ if (!videoUrl) throw new SocialError("facebook", "Video URL is required. Facebook accepts public URLs for video uploads.");
289
+ const body = {
290
+ file_url: videoUrl,
291
+ title: title.substring(0, 255),
292
+ description: description.substring(0, 5e3)
293
+ };
294
+ if (scheduledAt) {
295
+ const publishTime = Math.floor(new Date(scheduledAt).getTime() / 1e3);
296
+ body.published = false;
297
+ body.scheduled_publish_time = publishTime;
298
+ }
299
+ if (onProgress) onProgress(10);
300
+ const data = await this._graphPost(`/${pageId}/videos`, body, pageAccessToken, GRAPH_VIDEO_BASE);
301
+ if (onProgress) onProgress(100);
302
+ return {
303
+ platformVideoId: data.id,
304
+ platformUrl: `https://www.facebook.com/${pageId}/videos/${data.id}/`,
305
+ status: scheduledAt ? "scheduled" : "published",
306
+ uploadedAt: /* @__PURE__ */ new Date(),
307
+ scheduledAt: scheduledAt ? new Date(scheduledAt) : null,
308
+ metadata: {
309
+ pageId,
310
+ videoId: data.id,
311
+ title
312
+ }
313
+ };
314
+ }
315
+ /**
316
+ * Get a post by ID
317
+ * @param pageAccessToken
318
+ * @param postId
319
+ * @param fields - Comma-separated fields
320
+ */
321
+ async getPost(pageAccessToken, postId, fields = "id,message,created_time,permalink_url,shares,likes.summary(true),comments.summary(true)") {
322
+ return this._graphGet(`/${postId}`, pageAccessToken, { fields });
323
+ }
324
+ /**
325
+ * Update a post's message
326
+ * @param pageAccessToken
327
+ * @param postId
328
+ * @param message - New message text
329
+ */
330
+ async updatePost(pageAccessToken, postId, message) {
331
+ return this._graphPost(`/${postId}`, { message }, pageAccessToken);
332
+ }
333
+ /**
334
+ * Delete a post
335
+ * @param pageAccessToken
336
+ * @param postId
337
+ */
338
+ async deletePost(pageAccessToken, postId) {
339
+ return this._graphDelete(`/${postId}`, pageAccessToken);
340
+ }
341
+ /**
342
+ * Get page feed (recent posts)
343
+ * @param pageAccessToken
344
+ * @param pageId
345
+ * @param limit
346
+ */
347
+ async getPageFeed(pageAccessToken, pageId, limit = 25) {
348
+ return this._graphGet(`/${pageId}/feed`, pageAccessToken, {
349
+ fields: "id,message,created_time,permalink_url,type,shares",
350
+ limit: String(limit)
351
+ });
352
+ }
353
+ /**
354
+ * Get page insights (basic analytics)
355
+ * @param pageAccessToken
356
+ * @param pageId
357
+ * @param period - 'day' | 'week' | 'days_28'
358
+ */
359
+ async getPageInsights(pageAccessToken, pageId, period = "day") {
360
+ return this._graphGet(`/${pageId}/insights`, pageAccessToken, {
361
+ metric: "page_impressions,page_engaged_users,page_post_engagements,page_fan_adds",
362
+ period
363
+ });
364
+ }
365
+ getCredentialZodSchema() {
366
+ return FacebookCredentialsSchema;
367
+ }
368
+ getCredentialSchema() {
369
+ return [{
370
+ name: "appId",
371
+ displayName: "Meta App ID",
372
+ type: "text",
373
+ required: true,
374
+ description: "App ID from Meta Developer Portal (developers.facebook.com)"
375
+ }, {
376
+ name: "appSecret",
377
+ displayName: "Meta App Secret",
378
+ type: "password",
379
+ required: true,
380
+ description: "App Secret from Meta Developer Portal"
381
+ }];
382
+ }
383
+ getMetadata() {
384
+ return {
385
+ name: this.name,
386
+ displayName: this.displayName,
387
+ authType: this.authType,
388
+ icon: "facebook",
389
+ brandColor: "#1877F2",
390
+ description: "Publish posts, photos, and videos to Facebook Pages",
391
+ scopes: this.scopes,
392
+ scopeDescriptions: {
393
+ pages_show_list: "View the list of Facebook Pages you manage",
394
+ pages_read_engagement: "Read engagement metrics from your pages",
395
+ pages_manage_posts: "Create, edit, and delete posts on your pages",
396
+ pages_read_user_content: "Read user-generated content on your pages"
397
+ },
398
+ supportsScheduling: true,
399
+ supportsEnvironment: false,
400
+ setupGuide: [
401
+ {
402
+ step: 1,
403
+ title: "Create Meta Developer Account",
404
+ description: "Go to developers.facebook.com and create an account or log in"
405
+ },
406
+ {
407
+ step: 2,
408
+ title: "Create a New App",
409
+ description: "Click \"My Apps\" → \"Create App\". Enter your app name and email. On the use case step, filter by \"Content management\" and select \"Manage everything on your Page\""
410
+ },
411
+ {
412
+ step: 3,
413
+ title: "Connect Business Portfolio",
414
+ description: "Select or create a Business Portfolio when prompted. Click \"Create App\" to proceed to the dashboard"
415
+ },
416
+ {
417
+ step: 4,
418
+ title: "Set Redirect URI",
419
+ description: `Go to "Facebook Login" → "Settings" in the left sidebar. Add this as a Valid OAuth Redirect URI: ${this.defaultRedirectUri}`
420
+ },
421
+ {
422
+ step: 5,
423
+ title: "Configure App Settings",
424
+ description: "Go to Settings → Basic. Add your App Domain and a Privacy Policy URL. Copy your App ID and App Secret and paste them above"
425
+ },
426
+ {
427
+ step: 6,
428
+ title: "Add Permissions",
429
+ description: "Click \"Customize\" on your use case → \"Permissions and features\". pages_show_list, business_management, and public_profile are pre-added. Click \"+ Add\" for: pages_manage_posts, pages_read_engagement, pages_read_user_content, pages_manage_engagement, read_insights"
430
+ },
431
+ {
432
+ step: 7,
433
+ title: "Go Live",
434
+ description: "Switch app to \"Live\" mode. For development, Standard Access works with app role users. For production, request Advanced Access via App Review"
435
+ },
436
+ {
437
+ step: 8,
438
+ title: "Page Admin Required",
439
+ description: "You must be an admin of the Facebook Page(s) you want to post to. The page will be auto-discovered during authorization"
440
+ }
441
+ ],
442
+ redirectUriPattern: this.defaultRedirectUri,
443
+ credentialSchema: this.getCredentialSchema()
444
+ };
445
+ }
446
+ };
447
+
448
+ //#endregion
449
+ export { FacebookProvider };
450
+ //# sourceMappingURL=facebook.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"facebook.mjs","names":[],"sources":["../../src/providers/facebook/index.ts"],"sourcesContent":["/**\n * Facebook Pages Platform Provider\n * =================================\n * Facebook Page integration via Meta Graph API v25.0.\n *\n * Features:\n * - OAuth2 via Facebook Login dialog\n * - Two-step token exchange (short-lived -> long-lived, 60 days)\n * - Facebook Page discovery and page-level access tokens\n * - Text post publishing\n * - Link/article sharing\n * - Photo post publishing (via URL)\n * - Video post publishing (via URL, uses graph-video.facebook.com)\n * - Scheduled post publishing\n * - Post management (get, update, delete)\n * - Page insights/analytics\n * - Credential validation\n *\n * Facebook Page API Quirks:\n * - User tokens vs Page tokens: user token grants access to /me/accounts;\n * each page in the response includes its own page-specific access_token\n * - Page tokens never expire if derived from a long-lived user token\n * - Video uploads use graph-video.facebook.com (not graph.facebook.com)\n * - Scheduled posts: set published=false + scheduled_publish_time (Unix epoch, 10 min-6 months)\n * - Photos accept URLs or binary upload (we use URL-based for simplicity, matching Instagram pattern)\n * - Webhook signature validation uses SHA256 HMAC of app secret\n *\n * @see https://developers.facebook.com/docs/pages-api\n * @see https://developers.facebook.com/docs/graph-api/reference/page/feed\n * @see https://developers.facebook.com/docs/video-api/guides/publishing\n */\n\nimport {\n PlatformProvider,\n type AuthUrlOptions,\n type CredentialField,\n type ProviderMetadata,\n type UploadResult,\n type OAuthTokens,\n type ProviderConfig,\n} from '../../base.js';\nimport { SocialError } from '../../errors.js';\nimport { FacebookCredentialsSchema } from '../../schemas/facebook.js';\nimport { httpRequest } from '../../common/http.js';\nimport { META_GRAPH_VERSION, META_GRAPH_BASE, META_GRAPH_VIDEO_BASE, META_AUTH_URL, META_TOKEN_URL } from '../../common/meta.js';\nimport {\n metaExchangeLongLived,\n metaRefreshLongLivedFacebook,\n parseGraphError,\n} from '../../common/oauth/index.js';\nimport type { z } from 'zod';\nimport type {\n FacebookCredentials,\n FacebookPage,\n FacebookAccountInfo,\n FacebookTestResult,\n FacebookUploadParams,\n CreatePostOptions,\n CreatePostResult,\n CreateLinkPostOptions,\n CreateLinkPostResult,\n CreatePhotoPostOptions,\n CreatePhotoPostResult,\n GraphApiErrorResponse,\n FacebookCredentialData,\n FacebookPost,\n FacebookInsightsPeriod,\n} from './types.js';\n\nexport type {\n FacebookCredentials,\n FacebookCredentialData,\n FacebookPage,\n FacebookAccountInfo,\n FacebookTestResult,\n FacebookUploadParams,\n CreatePostOptions,\n CreatePostResult,\n CreateLinkPostOptions,\n CreateLinkPostResult,\n CreatePhotoPostOptions,\n CreatePhotoPostResult,\n FacebookPost,\n FacebookInsightsPeriod,\n GraphApiErrorResponse,\n} from './types.js';\n\n// --- Graph API Endpoints (version in src/common/meta.ts) ---\n\nconst GRAPH_API_VERSION = META_GRAPH_VERSION;\nconst FB_AUTH_URL = META_AUTH_URL;\nconst FB_TOKEN_URL = META_TOKEN_URL;\nconst GRAPH_API_BASE = META_GRAPH_BASE;\nconst GRAPH_VIDEO_BASE = META_GRAPH_VIDEO_BASE;\n\n// --- Provider ---\n\nexport class FacebookProvider extends PlatformProvider {\n public defaultRedirectUri: string;\n public scopes: string[];\n\n constructor(cfg: ProviderConfig = {}) {\n super(cfg);\n this.name = 'facebook';\n this.displayName = 'Facebook Pages';\n this.authType = 'oauth2';\n\n this.defaultRedirectUri =\n cfg.redirectUri ||\n `http://localhost:${cfg.port || 8060}/api/oauth/facebook/callback`;\n\n this.scopes = [\n 'business_management',\n 'pages_show_list',\n 'pages_read_engagement',\n 'pages_manage_posts',\n 'pages_read_user_content',\n ];\n }\n\n // --- API Helper ---\n\n /**\n * Unified Graph API request — used by all GET / POST / DELETE call sites.\n * Delegates to the shared `httpRequest` helper for retry, timeout, and 429 handling.\n *\n * @param method HTTP method\n * @param path Graph API path (e.g. `/me/accounts`)\n * @param accessToken Access token (sent as `access_token` query for GET/DELETE; body field for POST)\n * @param body POST body (merged with `access_token`); ignored for GET/DELETE\n * @param query Extra query params for GET\n * @param baseUrl Optional base URL override (video uploads use graph-video.facebook.com)\n */\n private async _graph<T = any>(\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n accessToken: string,\n body?: Record<string, any>,\n query?: Record<string, string>,\n baseUrl: string = GRAPH_API_BASE,\n ): Promise<T> {\n const isWriteWithBody = method === 'POST' && body !== undefined;\n const result = await httpRequest<T>('facebook', {\n method,\n url: `${baseUrl}${path}`,\n query: !isWriteWithBody ? { access_token: accessToken, ...(query ?? {}) } : query,\n json: isWriteWithBody ? { ...body, access_token: accessToken } : undefined,\n timeout: 60_000,\n retry: { attempts: 2 },\n parseError: parseGraphError,\n });\n return result.data;\n }\n\n // Backward-compatible shims so existing call sites work unchanged.\n private async _graphGet(path: string, accessToken: string, params: Record<string, string> = {}): Promise<any> {\n return this._graph('GET', path, accessToken, undefined, params);\n }\n private async _graphPost(path: string, body: Record<string, any>, accessToken: string, baseUrl: string = GRAPH_API_BASE): Promise<any> {\n return this._graph('POST', path, accessToken, body, undefined, baseUrl);\n }\n private async _graphDelete(path: string, accessToken: string): Promise<any> {\n return this._graph('DELETE', path, accessToken);\n }\n\n // --- OAuth ---\n\n /**\n * Get Facebook OAuth authorization URL\n */\n getAuthUrl(state: string, credentials: Partial<FacebookCredentials> = {}, _options?: AuthUrlOptions): string {\n const params = new URLSearchParams({\n client_id: credentials.appId!,\n redirect_uri: credentials.redirectUri || this.defaultRedirectUri,\n scope: this.scopes.join(','),\n response_type: 'code',\n state,\n });\n\n return `${FB_AUTH_URL}?${params.toString()}`;\n }\n\n /**\n * Exchange authorization code for tokens (two-step).\n *\n * Step 1: code → short-lived token (1 hour)\n * Step 2: short-lived → long-lived token (60 days)\n */\n async exchangeCode(code: string, credentials: Partial<FacebookCredentials> = {}): Promise<OAuthTokens> {\n return metaExchangeLongLived('facebook', {\n graphVersion: GRAPH_API_VERSION,\n clientId: credentials.appId!,\n clientSecret: credentials.appSecret!,\n redirectUri: credentials.redirectUri || this.defaultRedirectUri,\n code,\n });\n }\n\n /**\n * Refresh long-lived user token. Facebook long-lived tokens can be refreshed\n * before expiry by re-exchanging them via `fb_exchange_token`.\n */\n async refreshToken(refreshToken: string, credentials: Partial<FacebookCredentials> = {}): Promise<OAuthTokens> {\n return metaRefreshLongLivedFacebook({\n graphVersion: GRAPH_API_VERSION,\n clientId: credentials.appId!,\n clientSecret: credentials.appSecret!,\n refreshToken,\n });\n }\n\n // --- Account & Page Info ---\n\n /**\n * Get user's Facebook Pages with page-level access tokens\n * Each page has its own never-expiring token (when derived from long-lived user token)\n *\n * @param userAccessToken - Long-lived user access token\n * @returns List of pages with { id, name, access_token, category, ... }\n */\n async getPages(userAccessToken: string): Promise<FacebookPage[]> {\n const data = await this._graphGet(\n '/me/accounts',\n userAccessToken,\n { fields: 'id,name,access_token,category,picture{url},fan_count,followers_count' },\n );\n return data.data || [];\n }\n\n /**\n * Get account information -- returns the first managed page\n */\n async getAccountInfo(accessToken: string): Promise<FacebookAccountInfo> {\n const pages = await this.getPages(accessToken);\n\n if (!pages.length) {\n throw new SocialError(\n 'facebook',\n 'No Facebook Pages found. Your account must manage at least one Facebook Page.',\n );\n }\n\n const page = pages[0];\n return {\n id: page.id,\n name: page.name,\n profileImage: page.picture?.data?.url || null,\n category: page.category,\n fanCount: page.fan_count,\n pageAccessToken: page.access_token,\n pageId: page.id,\n pages: pages.map(p => ({\n id: p.id,\n name: p.name,\n category: p.category,\n profileImage: p.picture?.data?.url || null,\n })),\n };\n }\n\n /**\n * Test credential validity\n */\n async testCredential(credentialData: Record<string, any>): Promise<FacebookTestResult> {\n try {\n if (credentialData.oauthTokenData) {\n const tokenData = typeof credentialData.oauthTokenData === 'string'\n ? JSON.parse(credentialData.oauthTokenData)\n : credentialData.oauthTokenData;\n const accountInfo = await this.getAccountInfo(tokenData.access_token);\n\n return {\n status: 'OK',\n message: `Connected — managing ${accountInfo.pages.length} page(s)`,\n data: {\n channelId: accountInfo.id,\n channelTitle: accountInfo.name,\n profileImage: accountInfo.profileImage,\n pages: accountInfo.pages,\n },\n };\n }\n\n if (!credentialData.appId || !credentialData.appSecret) {\n return {\n status: 'Error',\n message: 'Meta App ID and App Secret are required',\n };\n }\n\n return {\n status: 'Pending',\n message: 'Credential needs OAuth authorization. Click \"Connect Account\" to link your Facebook.',\n };\n } catch (error: any) {\n return {\n status: 'Error',\n message: error.message || 'Failed to validate Facebook credential',\n };\n }\n }\n\n // --- Page Token Resolution ---\n\n /**\n * Get the page-level access token for a specific page\n * Page tokens derived from long-lived user tokens never expire.\n *\n * @param userAccessToken - Long-lived user access token\n * @param pageId - Facebook Page ID\n * @returns Page access token\n */\n async getPageAccessToken(userAccessToken: string, pageId: string): Promise<string> {\n const pages = await this.getPages(userAccessToken);\n const page = pages.find(p => p.id === pageId);\n\n if (!page) {\n throw new SocialError(\n 'facebook',\n `Page ${pageId} not found or you don't have admin access. Check your page permissions.`,\n );\n }\n\n return page.access_token;\n }\n\n // --- Content Publishing ---\n\n /**\n * Publish a text post to a Facebook Page\n * @param pageAccessToken - Page-level access token\n * @param pageId - Facebook Page ID\n * @param message - Post text\n * @param options\n */\n async createPost(\n pageAccessToken: string,\n pageId: string,\n message: string,\n options: CreatePostOptions = {},\n ): Promise<CreatePostResult> {\n const body: Record<string, any> = { message };\n\n if (options.scheduledAt) {\n const publishTime = Math.floor(new Date(options.scheduledAt).getTime() / 1000);\n body.published = false;\n body.scheduled_publish_time = publishTime;\n }\n\n const data = await this._graphPost(`/${pageId}/feed`, body, pageAccessToken);\n\n return {\n postId: data.id,\n status: options.scheduledAt ? 'scheduled' : 'published',\n scheduledAt: options.scheduledAt ? new Date(options.scheduledAt) : null,\n };\n }\n\n /**\n * Publish a link/article share to a Facebook Page\n * @param pageAccessToken\n * @param pageId\n * @param link - URL to share\n * @param options\n */\n async createLinkPost(\n pageAccessToken: string,\n pageId: string,\n link: string,\n options: CreateLinkPostOptions = {},\n ): Promise<CreateLinkPostResult> {\n const body: Record<string, any> = { link };\n if (options.message) body.message = options.message;\n\n if (options.scheduledAt) {\n const publishTime = Math.floor(new Date(options.scheduledAt).getTime() / 1000);\n body.published = false;\n body.scheduled_publish_time = publishTime;\n }\n\n const data = await this._graphPost(`/${pageId}/feed`, body, pageAccessToken);\n\n return {\n postId: data.id,\n status: options.scheduledAt ? 'scheduled' : 'published',\n };\n }\n\n /**\n * Publish a photo to a Facebook Page (via public URL)\n * @param pageAccessToken\n * @param pageId\n * @param photoUrl - Publicly accessible photo URL\n * @param options\n */\n async createPhotoPost(\n pageAccessToken: string,\n pageId: string,\n photoUrl: string,\n options: CreatePhotoPostOptions = {},\n ): Promise<CreatePhotoPostResult> {\n const body: Record<string, any> = { url: photoUrl };\n if (options.caption) body.message = options.caption;\n\n if (options.scheduledAt) {\n const publishTime = Math.floor(new Date(options.scheduledAt).getTime() / 1000);\n body.published = false;\n body.scheduled_publish_time = publishTime;\n }\n\n const data = await this._graphPost(`/${pageId}/photos`, body, pageAccessToken);\n\n return {\n postId: data.post_id || data.id,\n photoId: data.id,\n status: options.scheduledAt ? 'scheduled' : 'published',\n };\n }\n\n /**\n * Upload a video to a Facebook Page (via public URL)\n * Uses graph-video.facebook.com for video uploads.\n */\n async uploadVideo(params: FacebookUploadParams): Promise<UploadResult> {\n const {\n videoUrl,\n title = '',\n description = '',\n tokens,\n scheduledAt,\n onProgress,\n } = params;\n\n const pageAccessToken = tokens.pageAccessToken || tokens.access_token;\n const pageId = tokens.pageId;\n\n if (!pageId) {\n throw new SocialError('facebook', 'Page ID is required. Select a page from your connected pages.');\n }\n\n if (!videoUrl) {\n throw new SocialError('facebook', 'Video URL is required. Facebook accepts public URLs for video uploads.');\n }\n\n const body: Record<string, any> = {\n file_url: videoUrl,\n title: title.substring(0, 255),\n description: description.substring(0, 5000),\n };\n\n if (scheduledAt) {\n const publishTime = Math.floor(new Date(scheduledAt).getTime() / 1000);\n body.published = false;\n body.scheduled_publish_time = publishTime;\n }\n\n if (onProgress) onProgress(10);\n\n // Video uploads use graph-video.facebook.com\n const data = await this._graphPost(\n `/${pageId}/videos`,\n body,\n pageAccessToken,\n GRAPH_VIDEO_BASE,\n );\n\n if (onProgress) onProgress(100);\n\n return {\n platformVideoId: data.id,\n platformUrl: `https://www.facebook.com/${pageId}/videos/${data.id}/`,\n status: scheduledAt ? 'scheduled' : 'published',\n uploadedAt: new Date(),\n scheduledAt: scheduledAt ? new Date(scheduledAt) : null,\n metadata: {\n pageId,\n videoId: data.id,\n title,\n },\n };\n }\n\n // --- Post Management ---\n\n /**\n * Get a post by ID\n * @param pageAccessToken\n * @param postId\n * @param fields - Comma-separated fields\n */\n async getPost(\n pageAccessToken: string,\n postId: string,\n fields: string = 'id,message,created_time,permalink_url,shares,likes.summary(true),comments.summary(true)',\n ): Promise<any> {\n return this._graphGet(`/${postId}`, pageAccessToken, { fields });\n }\n\n /**\n * Update a post's message\n * @param pageAccessToken\n * @param postId\n * @param message - New message text\n */\n async updatePost(pageAccessToken: string, postId: string, message: string): Promise<any> {\n return this._graphPost(`/${postId}`, { message }, pageAccessToken);\n }\n\n /**\n * Delete a post\n * @param pageAccessToken\n * @param postId\n */\n async deletePost(pageAccessToken: string, postId: string): Promise<any> {\n return this._graphDelete(`/${postId}`, pageAccessToken);\n }\n\n /**\n * Get page feed (recent posts)\n * @param pageAccessToken\n * @param pageId\n * @param limit\n */\n async getPageFeed(pageAccessToken: string, pageId: string, limit: number = 25): Promise<any> {\n return this._graphGet(\n `/${pageId}/feed`,\n pageAccessToken,\n { fields: 'id,message,created_time,permalink_url,type,shares', limit: String(limit) },\n );\n }\n\n /**\n * Get page insights (basic analytics)\n * @param pageAccessToken\n * @param pageId\n * @param period - 'day' | 'week' | 'days_28'\n */\n async getPageInsights(\n pageAccessToken: string,\n pageId: string,\n period: 'day' | 'week' | 'days_28' = 'day',\n ): Promise<any> {\n return this._graphGet(\n `/${pageId}/insights`,\n pageAccessToken,\n {\n metric: 'page_impressions,page_engaged_users,page_post_engagements,page_fan_adds',\n period,\n },\n );\n }\n\n // --- Schema & Metadata ---\n\n getCredentialZodSchema(): z.ZodType {\n return FacebookCredentialsSchema;\n }\n\n getCredentialSchema(): CredentialField[] {\n return [\n {\n name: 'appId',\n displayName: 'Meta App ID',\n type: 'text',\n required: true,\n description: 'App ID from Meta Developer Portal (developers.facebook.com)',\n },\n {\n name: 'appSecret',\n displayName: 'Meta App Secret',\n type: 'password',\n required: true,\n description: 'App Secret from Meta Developer Portal',\n },\n ];\n }\n\n getMetadata(): ProviderMetadata {\n return {\n name: this.name,\n displayName: this.displayName,\n authType: this.authType,\n icon: 'facebook',\n brandColor: '#1877F2',\n description: 'Publish posts, photos, and videos to Facebook Pages',\n scopes: this.scopes,\n scopeDescriptions: {\n pages_show_list: 'View the list of Facebook Pages you manage',\n pages_read_engagement: 'Read engagement metrics from your pages',\n pages_manage_posts: 'Create, edit, and delete posts on your pages',\n pages_read_user_content: 'Read user-generated content on your pages',\n },\n supportsScheduling: true,\n supportsEnvironment: false,\n setupGuide: [\n { step: 1, title: 'Create Meta Developer Account', description: 'Go to developers.facebook.com and create an account or log in' },\n { step: 2, title: 'Create a New App', description: 'Click \"My Apps\" → \"Create App\". Enter your app name and email. On the use case step, filter by \"Content management\" and select \"Manage everything on your Page\"' },\n { step: 3, title: 'Connect Business Portfolio', description: 'Select or create a Business Portfolio when prompted. Click \"Create App\" to proceed to the dashboard' },\n { step: 4, title: 'Set Redirect URI', description: `Go to \"Facebook Login\" → \"Settings\" in the left sidebar. Add this as a Valid OAuth Redirect URI: ${this.defaultRedirectUri}` },\n { step: 5, title: 'Configure App Settings', description: 'Go to Settings → Basic. Add your App Domain and a Privacy Policy URL. Copy your App ID and App Secret and paste them above' },\n { step: 6, title: 'Add Permissions', description: 'Click \"Customize\" on your use case → \"Permissions and features\". pages_show_list, business_management, and public_profile are pre-added. Click \"+ Add\" for: pages_manage_posts, pages_read_engagement, pages_read_user_content, pages_manage_engagement, read_insights' },\n { step: 7, title: 'Go Live', description: 'Switch app to \"Live\" mode. For development, Standard Access works with app role users. For production, request Advanced Access via App Review' },\n { step: 8, title: 'Page Admin Required', description: 'You must be an admin of the Facebook Page(s) you want to post to. The page will be auto-discovered during authorization' },\n ],\n redirectUriPattern: this.defaultRedirectUri,\n credentialSchema: this.getCredentialSchema(),\n };\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ import { d as ProviderMetadata, g as UploadResult, i as CredentialField, l as ProviderConfig, o as OAuthTokens, r as AuthUrlOptions, s as PlatformProvider } from "../base-DBtKFiSX.mjs";
2
+ import { _ as MediaInsights, a as InstagramCredentialData, c as InstagramSetupStep, d as InstagramUploadCarouselParams, f as InstagramUploadPhotoParams, g as ListMediaResult, h as ListMediaOptions, i as InstagramAccountInfo, l as InstagramTestResult, m as InstagramUserTag, n as ContainerStatus, o as InstagramMedia, p as InstagramUploadVideoParams, r as ContainerStatusCode, s as InstagramMediaType, t as CarouselItem, u as InstagramTokens } from "../types-hriBJTsU.mjs";
3
+ import { z } from "zod";
4
+
5
+ //#region src/providers/instagram/index.d.ts
6
+ declare class InstagramProvider extends PlatformProvider {
7
+ private defaultRedirectUri;
8
+ private scopes;
9
+ constructor(cfg?: ProviderConfig);
10
+ /**
11
+ * Get Facebook OAuth authorization URL
12
+ * Instagram uses Facebook's OAuth dialog for authorization
13
+ */
14
+ getAuthUrl(state: string, credentials?: Record<string, any>, _options?: AuthUrlOptions): string;
15
+ /**
16
+ * Exchange authorization code for tokens (two-step).
17
+ *
18
+ * Step 1: code → short-lived token (1 hour)
19
+ * Step 2: short-lived → long-lived token (60 days)
20
+ */
21
+ exchangeCode(code: string, credentials?: Record<string, any>): Promise<OAuthTokens>;
22
+ /**
23
+ * Refresh long-lived token. Instagram uses the access token itself as the
24
+ * refresh credential (must be ≥24h old, not yet expired) and hits the
25
+ * `graph.instagram.com` refresh endpoint.
26
+ */
27
+ refreshToken(refreshToken: string): Promise<OAuthTokens>;
28
+ /**
29
+ * Get Instagram Business Account info
30
+ *
31
+ * Flow:
32
+ * 1. Get user's Facebook Pages via /me/accounts
33
+ * 2. For first page, get linked instagram_business_account
34
+ * 3. Get IG profile details
35
+ */
36
+ getAccountInfo(accessToken: string): Promise<InstagramAccountInfo>;
37
+ /**
38
+ * Test credential validity
39
+ */
40
+ testCredential(credentialData: InstagramCredentialData): Promise<InstagramTestResult>;
41
+ /**
42
+ * Upload a video as a Reel to Instagram
43
+ *
44
+ * Instagram requires publicly accessible video URLs (no binary upload).
45
+ * Flow: create container → poll status → publish
46
+ *
47
+ * @param params
48
+ * @param params.videoUrl - Publicly accessible video URL (required)
49
+ * @param params.title - Used as caption
50
+ * @param params.description - Appended to caption if title is empty
51
+ * @param params.tokens - { access_token, ig_user_id }
52
+ * @param params.onProgress - Progress callback
53
+ */
54
+ uploadVideo(params: InstagramUploadVideoParams): Promise<UploadResult>;
55
+ /**
56
+ * Create a single photo post on Instagram
57
+ *
58
+ * @param params
59
+ * @param params.imageUrl - Publicly accessible image URL
60
+ * @param params.caption - Post caption (max 2200 chars)
61
+ * @param params.tokens - { access_token, ig_user_id }
62
+ */
63
+ uploadPhoto(params: InstagramUploadPhotoParams): Promise<{
64
+ mediaId: string;
65
+ status: string;
66
+ }>;
67
+ /**
68
+ * Create a carousel post on Instagram
69
+ *
70
+ * @param params
71
+ * @param params.items - 2-10 media items with type ('IMAGE'|'VIDEO') and url
72
+ * @param params.caption - Post caption (max 2200 chars)
73
+ * @param params.tokens - { access_token, ig_user_id }
74
+ */
75
+ uploadCarousel(params: InstagramUploadCarouselParams): Promise<{
76
+ mediaId: string;
77
+ status: string;
78
+ }>;
79
+ /**
80
+ * Get a single media object by ID
81
+ *
82
+ * @param accessToken - Valid access token
83
+ * @param mediaId - Instagram media ID
84
+ */
85
+ getMedia(accessToken: string, mediaId: string): Promise<InstagramMedia>;
86
+ /**
87
+ * List media for an Instagram user
88
+ *
89
+ * @param accessToken - Valid access token
90
+ * @param userId - IG user ID (if omitted, fetched via getAccountInfo)
91
+ * @param options - Pagination options (limit, after, before)
92
+ */
93
+ listMedia(accessToken: string, userId?: string, options?: ListMediaOptions): Promise<ListMediaResult>;
94
+ /**
95
+ * Get insights for a specific media object
96
+ *
97
+ * @param accessToken - Valid access token
98
+ * @param mediaId - Instagram media ID
99
+ */
100
+ getMediaInsights(accessToken: string, mediaId: string): Promise<MediaInsights>;
101
+ getCredentialZodSchema(): z.ZodType;
102
+ getCredentialSchema(): CredentialField[];
103
+ getMetadata(): ProviderMetadata;
104
+ /**
105
+ * Make a GET request to the Graph API
106
+ * @private
107
+ */
108
+ private _graphGet;
109
+ /**
110
+ * Make a POST request to the Graph API
111
+ * @private
112
+ */
113
+ private _graphPost;
114
+ /**
115
+ * Poll container status until FINISHED, ERROR, or timeout
116
+ * @private
117
+ */
118
+ private _pollContainerStatus;
119
+ }
120
+ //#endregion
121
+ export { type CarouselItem, type ContainerStatus, type ContainerStatusCode, type InstagramAccountInfo, type InstagramCredentialData, type InstagramMedia, type InstagramMediaType, InstagramProvider, type InstagramSetupStep, type InstagramTestResult, type InstagramTokens, type InstagramUploadCarouselParams, type InstagramUploadPhotoParams, type InstagramUploadVideoParams, type InstagramUserTag, type ListMediaOptions, type ListMediaResult, type MediaInsights };
122
+ //# sourceMappingURL=instagram.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instagram.d.mts","names":[],"sources":["../../src/providers/instagram/index.ts"],"mappings":";;;;;cA0Fa,iBAAA,SAA0B,gBAAA;EAAA,QAC7B,kBAAA;EAAA,QACA,MAAA;cAEI,GAAA,GAAK,cAAA;EAkc6C;;;;EAxa9D,UAAA,CAAW,KAAA,UAAe,WAAA,GAAa,MAAA,eAA0B,QAAA,GAAW,cAAA;EA9BvB;;;;;;EAgD/C,YAAA,CAAa,IAAA,UAAc,WAAA,GAAa,MAAA,gBAA2B,OAAA,CAAQ,WAAA;EA5CrE;;;;;EA2DN,YAAA,CAAa,YAAA,WAAuB,OAAA,CAAQ,WAAA;EAjCe;;;;;;;;EA+C3D,cAAA,CAAe,WAAA,WAAsB,OAAA,CAAQ,oBAAA;EAdT;;;EAmEpC,cAAA,CAAe,cAAA,EAAgB,uBAAA,GAA0B,OAAA,CAAQ,mBAAA;EArD5B;;;;;;;;;;;;;EA0GrC,WAAA,CAAY,MAAA,EAAQ,0BAAA,GAA6B,OAAA,CAAQ,YAAA;EAmF7C;;;;;;;;EAAZ,WAAA,CAAY,MAAA,EAAQ,0BAAA,GAA6B,OAAA;IAAU,OAAA;IAAiB,MAAA;EAAA;EAmH9C;;;;;;;;EAnE9B,cAAA,CAAe,MAAA,EAAQ,6BAAA,GAAgC,OAAA;IAAU,OAAA;IAAiB,MAAA;EAAA;EA4I5C;;;;;;EAzEtC,QAAA,CAAS,WAAA,UAAqB,OAAA,WAAkB,OAAA,CAAQ,cAAA;EAgHvC;;;;;;;EAjFjB,SAAA,CAAU,WAAA,UAAqB,MAAA,WAAiB,OAAA,GAAS,gBAAA,GAAwB,OAAA,CAAQ,eAAA;;;;;;;EA0CzF,gBAAA,CAAiB,WAAA,UAAqB,OAAA,WAAkB,OAAA,CAAQ,aAAA;EAmCtE,sBAAA,CAAA,GAA0B,CAAA,CAAE,OAAA;EAI5B,mBAAA,CAAA,GAAuB,eAAA;EAmBvB,WAAA,CAAA,GAAe,gBAAA;;;;;UAuED,SAAA;;;;;UAkBA,UAAA;;;;;UAgBA,oBAAA;AAAA"}