@olastudio/social-media-sdk 0.1.0 → 0.1.2

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 (56) hide show
  1. package/README.md +190 -55
  2. package/dist/adapters/expo.js +23 -2
  3. package/dist/adapters/expo.js.map +1 -1
  4. package/dist/adapters/expo.mjs +22 -1
  5. package/dist/adapters/expo.mjs.map +1 -1
  6. package/dist/adapters/index.js +0 -2
  7. package/dist/adapters/index.mjs +1 -1
  8. package/dist/core/index.js +318 -87
  9. package/dist/core/index.js.map +1 -1
  10. package/dist/core/index.mjs +301 -3
  11. package/dist/core/index.mjs.map +1 -1
  12. package/dist/index.js +2225 -170
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.mjs +2186 -7
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/providers/facebook/index.js +1071 -26
  17. package/dist/providers/facebook/index.js.map +1 -1
  18. package/dist/providers/facebook/index.mjs +1067 -3
  19. package/dist/providers/facebook/index.mjs.map +1 -1
  20. package/dist/providers/instagram/index.js +866 -22
  21. package/dist/providers/instagram/index.js.map +1 -1
  22. package/dist/providers/instagram/index.mjs +863 -3
  23. package/dist/providers/instagram/index.mjs.map +1 -1
  24. package/dist/providers/tiktok/index.js +395 -41
  25. package/dist/providers/tiktok/index.js.map +1 -1
  26. package/dist/providers/tiktok/index.mjs +389 -1
  27. package/dist/providers/tiktok/index.mjs.map +1 -1
  28. package/package.json +3 -4
  29. package/dist/chunk-36RADUUO.mjs +0 -31
  30. package/dist/chunk-36RADUUO.mjs.map +0 -1
  31. package/dist/chunk-7QAMNVQU.js +0 -666
  32. package/dist/chunk-7QAMNVQU.js.map +0 -1
  33. package/dist/chunk-B6NUTR54.js +0 -4
  34. package/dist/chunk-B6NUTR54.js.map +0 -1
  35. package/dist/chunk-BX3RO5PW.js +0 -4
  36. package/dist/chunk-BX3RO5PW.js.map +0 -1
  37. package/dist/chunk-CGNGZNVG.mjs +0 -391
  38. package/dist/chunk-CGNGZNVG.mjs.map +0 -1
  39. package/dist/chunk-ER5A6TIL.js +0 -296
  40. package/dist/chunk-ER5A6TIL.js.map +0 -1
  41. package/dist/chunk-GF3OEIKI.mjs +0 -3
  42. package/dist/chunk-GF3OEIKI.mjs.map +0 -1
  43. package/dist/chunk-H5GAC4UG.mjs +0 -277
  44. package/dist/chunk-H5GAC4UG.mjs.map +0 -1
  45. package/dist/chunk-HPLIHYLQ.js +0 -35
  46. package/dist/chunk-HPLIHYLQ.js.map +0 -1
  47. package/dist/chunk-MV6HJQQO.mjs +0 -3
  48. package/dist/chunk-MV6HJQQO.mjs.map +0 -1
  49. package/dist/chunk-ONR2OJOB.mjs +0 -848
  50. package/dist/chunk-ONR2OJOB.mjs.map +0 -1
  51. package/dist/chunk-PJ4KYVHH.js +0 -854
  52. package/dist/chunk-PJ4KYVHH.js.map +0 -1
  53. package/dist/chunk-QRGJXASL.js +0 -402
  54. package/dist/chunk-QRGJXASL.js.map +0 -1
  55. package/dist/chunk-QZHJXRRW.mjs +0 -661
  56. package/dist/chunk-QZHJXRRW.mjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,9 +1,2188 @@
1
- import './chunk-MV6HJQQO.mjs';
2
- export { FacebookAPI, FacebookAuth, FacebookProvider, FacebookScopesBusiness, FacebookScopesUser } from './chunk-ONR2OJOB.mjs';
3
- export { InstagramAPI, InstagramAuth, InstagramProvider, InstagramScopes } from './chunk-QZHJXRRW.mjs';
4
- export { FACEBOOK_GRAPH_API_BASE_URL, FACEBOOK_GRAPH_API_VERSION, FACEBOOK_OAUTH_AUTHORIZATION_URL, HttpClient, INSIGHTS_PERIODS, INSTAGRAM_ACCOUNT_METRICS, INSTAGRAM_DEMOGRAPHICS_METRICS, INSTAGRAM_MEDIA_METRICS_COMMON, INSTAGRAM_REELS_METRICS, INSTAGRAM_STORIES_METRICS, METRIC_PRESETS, PAGE_METRICS_LIFETIME, PAGE_METRICS_PERIODIC, PHOTO_METRICS, POST_METRICS, REELS_METRICS, REELS_METRICS_EXTENDED, VIDEO_METRICS } from './chunk-H5GAC4UG.mjs';
5
- export { TIKTOK_API_BASE_URL, TIKTOK_API_VERSION, TIKTOK_DEFAULT_SCOPES, TIKTOK_OAUTH_AUTHORIZATION_URL, TIKTOK_OAUTH_REVOKE_URL, TIKTOK_OAUTH_TOKEN_URL, TikTokAPI, TikTokAuth, TikTokProvider, TikTokScopes } from './chunk-CGNGZNVG.mjs';
6
- import './chunk-GF3OEIKI.mjs';
7
- export { APIError, AuthError, BaseError } from './chunk-36RADUUO.mjs';
1
+ // core/errors/BaseError.ts
2
+ var BaseError = class extends Error {
3
+ constructor(message, code, details) {
4
+ super(message);
5
+ this.name = this.constructor.name;
6
+ this.code = code;
7
+ this.details = details;
8
+ Error.captureStackTrace(this, this.constructor);
9
+ }
10
+ };
11
+
12
+ // core/errors/AuthError.ts
13
+ var AuthError = class extends BaseError {
14
+ constructor(message, code, details) {
15
+ super(message, code, details);
16
+ this.name = "AuthError";
17
+ }
18
+ };
19
+
20
+ // core/errors/APIError.ts
21
+ var APIError = class extends BaseError {
22
+ constructor(message, statusCode, code, details) {
23
+ super(message, code, details);
24
+ this.name = "APIError";
25
+ this.statusCode = statusCode;
26
+ }
27
+ };
28
+
29
+ // core/http/HttpClient.ts
30
+ var HttpClient = class {
31
+ constructor(config) {
32
+ this.config = {
33
+ timeout: 3e4,
34
+ // 30 seconds default
35
+ headers: {
36
+ "Content-Type": "application/json"
37
+ },
38
+ ...config
39
+ };
40
+ }
41
+ /**
42
+ * Generic request
43
+ */
44
+ async request(endpoint, options = {}) {
45
+ const url = this.buildURL(endpoint, options.params);
46
+ const method = options.method || "GET";
47
+ const headers = {
48
+ ...this.config.headers,
49
+ ...options.headers
50
+ };
51
+ if (this.config.accessToken) {
52
+ headers["Authorization"] = `Bearer ${this.config.accessToken}`;
53
+ }
54
+ const config = {
55
+ method,
56
+ headers,
57
+ signal: this.createAbortSignal(options.timeout || this.config.timeout)
58
+ };
59
+ if (options.body && method !== "GET") {
60
+ if (headers["Content-Type"] === "application/json") {
61
+ config.body = JSON.stringify(options.body);
62
+ } else if (headers["Content-Type"] === "application/x-www-form-urlencoded") {
63
+ config.body = this.encodeFormBody(options.body);
64
+ } else {
65
+ config.body = options.body;
66
+ }
67
+ }
68
+ try {
69
+ const response = await fetch(url, config);
70
+ return await this.handleResponse(response);
71
+ } catch (error) {
72
+ if (error instanceof APIError) {
73
+ throw error;
74
+ }
75
+ throw new APIError(
76
+ `Network error: ${error instanceof Error ? error.message : "Unknown error"}`,
77
+ 0,
78
+ "NETWORK_ERROR",
79
+ error
80
+ );
81
+ }
82
+ }
83
+ /**
84
+ * GET request
85
+ */
86
+ async get(endpoint, params) {
87
+ return this.request(endpoint, { method: "GET", params });
88
+ }
89
+ /**
90
+ * POST request
91
+ */
92
+ async post(endpoint, body) {
93
+ return this.request(endpoint, { method: "POST", body });
94
+ }
95
+ /**
96
+ * PUT request
97
+ */
98
+ async put(endpoint, body) {
99
+ return this.request(endpoint, { method: "PUT", body });
100
+ }
101
+ /**
102
+ * DELETE request
103
+ */
104
+ async delete(endpoint) {
105
+ return this.request(endpoint, { method: "DELETE" });
106
+ }
107
+ /**
108
+ * Build URL with query params
109
+ */
110
+ buildURL(endpoint, params) {
111
+ const url = endpoint.startsWith("http") ? endpoint : `${this.config.baseURL}${endpoint}`;
112
+ if (!params || Object.keys(params).length === 0) {
113
+ return url;
114
+ }
115
+ const queryString = Object.entries(params).filter(([_, value]) => value !== void 0 && value !== null).map(
116
+ ([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`
117
+ ).join("&");
118
+ return queryString ? `${url}?${queryString}` : url;
119
+ }
120
+ /**
121
+ * Encode body as form-urlencoded
122
+ */
123
+ encodeFormBody(body) {
124
+ return Object.entries(body).filter(([_, value]) => value !== void 0 && value !== null).map(
125
+ ([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`
126
+ ).join("&");
127
+ }
128
+ /**
129
+ * Create AbortSignal for timeout
130
+ */
131
+ createAbortSignal(timeout) {
132
+ if (!timeout) return void 0;
133
+ const controller = new AbortController();
134
+ setTimeout(() => controller.abort(), timeout);
135
+ return controller.signal;
136
+ }
137
+ /**
138
+ * Handle HTTP response
139
+ */
140
+ async handleResponse(response) {
141
+ let data;
142
+ const contentType = response.headers.get("content-type");
143
+ if (contentType?.includes("application/json")) {
144
+ data = await response.json();
145
+ } else {
146
+ data = await response.text();
147
+ }
148
+ if (!response.ok) {
149
+ const errorMessage = data?.error?.message || data?.message || "API request failed";
150
+ const errorCode = data?.error?.code || data?.code || response.status;
151
+ throw new APIError(errorMessage, response.status, errorCode, data);
152
+ }
153
+ return data;
154
+ }
155
+ /**
156
+ * Update access token
157
+ */
158
+ setAccessToken(token) {
159
+ this.config.accessToken = token;
160
+ }
161
+ /**
162
+ * Get current configuration
163
+ */
164
+ getConfig() {
165
+ return { ...this.config };
166
+ }
167
+ };
168
+
169
+ // core/constants.ts
170
+ var FACEBOOK_GRAPH_API_VERSION = "v24.0";
171
+ var FACEBOOK_GRAPH_API_BASE_URL = `https://graph.facebook.com/${FACEBOOK_GRAPH_API_VERSION}`;
172
+ var FACEBOOK_OAUTH_AUTHORIZATION_URL = `https://www.facebook.com/${FACEBOOK_GRAPH_API_VERSION}/dialog/oauth`;
173
+
174
+ // core/insights.constants.ts
175
+ var PAGE_METRICS_LIFETIME = [
176
+ "page_fans_country",
177
+ "page_fans_city",
178
+ "page_fans_gender_age"
179
+ ];
180
+ var PAGE_METRICS_PERIODIC = [
181
+ "page_views_total",
182
+ "page_views",
183
+ "page_post_engagements",
184
+ "page_fans_online",
185
+ "page_fans_online_per_day",
186
+ "page_video_views",
187
+ "page_daily_follows",
188
+ "page_daily_unfollows"
189
+ ];
190
+ var POST_METRICS = [
191
+ "post_engaged_users",
192
+ "post_negative_feedback",
193
+ "post_engaged_fan",
194
+ "post_clicks",
195
+ "post_clicks_unique",
196
+ "post_reactions_by_type_total"
197
+ ];
198
+ var VIDEO_METRICS = [
199
+ "total_video_views",
200
+ "total_video_views_unique",
201
+ "total_video_views_autoplayed",
202
+ "total_video_views_clicked_to_play",
203
+ "total_video_views_organic",
204
+ "total_video_views_organic_unique",
205
+ "total_video_views_paid",
206
+ "total_video_views_paid_unique",
207
+ "total_video_avg_time_watched",
208
+ "total_video_view_total_time",
209
+ "total_video_complete_views",
210
+ "total_video_complete_views_unique",
211
+ "total_video_complete_views_organic",
212
+ "total_video_complete_views_organic_unique",
213
+ "total_video_complete_views_paid",
214
+ "total_video_complete_views_paid_unique"
215
+ ];
216
+ var REELS_METRICS = [
217
+ "blue_reels_play_count",
218
+ "post_video_avg_time_watched",
219
+ "post_video_views",
220
+ "post_video_social_actions"
221
+ ];
222
+ var REELS_METRICS_EXTENDED = [
223
+ "plays",
224
+ "comments",
225
+ "likes",
226
+ "shares",
227
+ "reach",
228
+ "total_plays",
229
+ "average_minutes_viewed",
230
+ "minutes_viewed"
231
+ ];
232
+ var PHOTO_METRICS = [
233
+ "photo_views",
234
+ "photo_reach",
235
+ "engaged_users"
236
+ ];
237
+ var INSTAGRAM_ACCOUNT_METRICS = [
238
+ "views",
239
+ "reach",
240
+ "accounts_engaged",
241
+ "total_interactions",
242
+ "likes",
243
+ "comments",
244
+ "shares",
245
+ "saves",
246
+ "follows_and_unfollows",
247
+ "profile_links_taps",
248
+ "profile_views"
249
+ ];
250
+ var INSTAGRAM_DEMOGRAPHICS_METRICS = [
251
+ "follower_demographics",
252
+ "reached_audience_demographics",
253
+ "engaged_audience_demographics"
254
+ ];
255
+ var INSTAGRAM_MEDIA_METRICS_COMMON = [
256
+ "views",
257
+ "reach",
258
+ "likes",
259
+ "comments",
260
+ "shares",
261
+ "saved",
262
+ "total_interactions"
263
+ ];
264
+ var INSTAGRAM_REELS_METRICS = [
265
+ "plays",
266
+ "reach",
267
+ "likes",
268
+ "comments",
269
+ "shares",
270
+ "saved",
271
+ "total_interactions"
272
+ ];
273
+ var INSTAGRAM_STORIES_METRICS = [
274
+ "exits",
275
+ "replies",
276
+ "taps_forward",
277
+ "taps_back",
278
+ "views",
279
+ "reach"
280
+ ];
281
+ var INSIGHTS_PERIODS = {
282
+ DAY: "day",
283
+ WEEK: "week",
284
+ DAYS_28: "days_28",
285
+ MONTH: "month",
286
+ LIFETIME: "lifetime"
287
+ };
288
+ var METRIC_PRESETS = {
289
+ // Facebook Page quick overview
290
+ PAGE_OVERVIEW: ["page_views_total", "page_post_engagements", "page_video_views"],
291
+ // Facebook Video performance
292
+ VIDEO_PERFORMANCE: ["total_video_views", "total_video_avg_time_watched", "total_video_complete_views"],
293
+ // Facebook Reels performance
294
+ REELS_PERFORMANCE: ["blue_reels_play_count", "post_video_avg_time_watched", "post_video_social_actions"],
295
+ // Instagram account overview
296
+ INSTAGRAM_OVERVIEW: ["views", "reach", "accounts_engaged", "total_interactions"],
297
+ // Instagram content engagement
298
+ INSTAGRAM_ENGAGEMENT: ["likes", "comments", "shares", "saves"]
299
+ };
300
+
301
+ // providers/facebook/api/FacebookAPI.ts
302
+ var FacebookAPI = class {
303
+ constructor(config, accessToken) {
304
+ this.config = config;
305
+ this.accessToken = accessToken;
306
+ this.httpClient = new HttpClient({
307
+ baseURL: FACEBOOK_GRAPH_API_BASE_URL,
308
+ accessToken: this.accessToken
309
+ });
310
+ }
311
+ /**
312
+ * Generic request
313
+ */
314
+ async request(endpoint, options = {}) {
315
+ if (this.accessToken && !options.params?.access_token) {
316
+ options.params = {
317
+ ...options.params,
318
+ access_token: this.accessToken
319
+ };
320
+ }
321
+ return this.httpClient.request(endpoint, options);
322
+ }
323
+ /**
324
+ * GET request
325
+ */
326
+ async get(endpoint, params) {
327
+ return this.request(endpoint, { method: "GET", params });
328
+ }
329
+ /**
330
+ * POST request
331
+ */
332
+ async post(endpoint, body) {
333
+ return this.request(endpoint, { method: "POST", body });
334
+ }
335
+ /**
336
+ * PUT request
337
+ */
338
+ async put(endpoint, body) {
339
+ return this.request(endpoint, { method: "PUT", body });
340
+ }
341
+ /**
342
+ * DELETE request
343
+ */
344
+ async delete(endpoint) {
345
+ return this.request(endpoint, { method: "DELETE" });
346
+ }
347
+ /**
348
+ * Update access token
349
+ */
350
+ setAccessToken(token) {
351
+ this.accessToken = token;
352
+ this.httpClient.setAccessToken(token);
353
+ }
354
+ /**
355
+ * Get current access token
356
+ */
357
+ getAccessToken() {
358
+ return this.accessToken;
359
+ }
360
+ // ============= Facebook-specific methods =============
361
+ /**
362
+ * Get user's pages
363
+ */
364
+ async getPages(userAccessToken) {
365
+ try {
366
+ const token = userAccessToken || this.accessToken;
367
+ if (!token) {
368
+ throw new APIError("Access token is required", 401, "NO_TOKEN");
369
+ }
370
+ const response = await this.get("/me/accounts", {
371
+ fields: "id,name,access_token,picture",
372
+ access_token: token
373
+ });
374
+ return response.data;
375
+ } catch (error) {
376
+ throw new APIError(
377
+ `Failed to fetch Facebook pages: ${error instanceof Error ? error.message : "Unknown error"}`,
378
+ void 0,
379
+ "PAGES_FETCH_ERROR",
380
+ error
381
+ );
382
+ }
383
+ }
384
+ /**
385
+ * Publish post to Facebook page
386
+ */
387
+ async publishPost(params) {
388
+ const { pageId, pageAccessToken, message, imageUrl, scheduledPublishTime } = params;
389
+ try {
390
+ let endpoint = `/${pageId}/`;
391
+ const body = {
392
+ access_token: pageAccessToken
393
+ };
394
+ if (scheduledPublishTime) {
395
+ body.published = false;
396
+ body.scheduled_publish_time = scheduledPublishTime;
397
+ }
398
+ if (imageUrl) {
399
+ endpoint += "photos";
400
+ body.url = imageUrl;
401
+ if (message) body.caption = message;
402
+ } else if (message) {
403
+ endpoint += "feed";
404
+ body.message = message;
405
+ } else {
406
+ throw new APIError(
407
+ "Either message or imageUrl must be provided",
408
+ 400,
409
+ "INVALID_PARAMS"
410
+ );
411
+ }
412
+ const response = await this.request(endpoint, {
413
+ method: "POST",
414
+ headers: {
415
+ "Content-Type": "application/x-www-form-urlencoded"
416
+ },
417
+ body
418
+ });
419
+ return response;
420
+ } catch (error) {
421
+ throw new APIError(
422
+ `Failed to publish post: ${error instanceof Error ? error.message : "Unknown error"}`,
423
+ void 0,
424
+ "POST_PUBLISH_ERROR",
425
+ error
426
+ );
427
+ }
428
+ }
429
+ /**
430
+ * Delete Facebook post
431
+ */
432
+ async deletePost(postId, pageAccessToken) {
433
+ try {
434
+ const response = await this.delete(
435
+ `/${postId}?access_token=${pageAccessToken}`
436
+ );
437
+ return response.success === true;
438
+ } catch (error) {
439
+ throw new APIError(
440
+ `Failed to delete post: ${error instanceof Error ? error.message : "Unknown error"}`,
441
+ void 0,
442
+ "POST_DELETE_ERROR",
443
+ error
444
+ );
445
+ }
446
+ }
447
+ /**
448
+ * Get page information
449
+ */
450
+ async getPage(pageId, fields) {
451
+ try {
452
+ const fieldsParam = fields?.join(",") || "id,name,access_token,picture";
453
+ return await this.get(`/${pageId}`, {
454
+ fields: fieldsParam
455
+ });
456
+ } catch (error) {
457
+ throw new APIError(
458
+ `Failed to fetch page info: ${error instanceof Error ? error.message : "Unknown error"}`,
459
+ void 0,
460
+ "PAGE_FETCH_ERROR",
461
+ error
462
+ );
463
+ }
464
+ }
465
+ /**
466
+ * Debug/validate a Facebook access token
467
+ * Uses Facebook's debug_token endpoint
468
+ *
469
+ * @param inputToken - The token to validate
470
+ * @param appAccessToken - App token (app_id|app_secret)
471
+ * @returns Token debug information
472
+ */
473
+ async debugToken(inputToken, appAccessToken) {
474
+ try {
475
+ return await this.get("/debug_token", {
476
+ input_token: inputToken,
477
+ access_token: appAccessToken
478
+ });
479
+ } catch (error) {
480
+ throw new APIError(
481
+ `Failed to debug token: ${error instanceof Error ? error.message : "Unknown error"}`,
482
+ void 0,
483
+ "TOKEN_DEBUG_ERROR",
484
+ error
485
+ );
486
+ }
487
+ }
488
+ /**
489
+ * Get page insights/analytics
490
+ * @param pageId - Facebook Page ID
491
+ * @param options - Insights options
492
+ * @returns Page insights data
493
+ */
494
+ async getPageInsights(pageId, options) {
495
+ try {
496
+ const params = {
497
+ metric: options.metric.join(","),
498
+ period: options.period || "day"
499
+ };
500
+ if (options.since) params.since = options.since;
501
+ if (options.until) params.until = options.until;
502
+ const response = await this.get(`/${pageId}/insights`, params);
503
+ return response;
504
+ } catch (error) {
505
+ throw new APIError(
506
+ `Failed to fetch page insights: ${error instanceof Error ? error.message : "Unknown error"}`,
507
+ void 0,
508
+ "INSIGHTS_FETCH_ERROR",
509
+ error
510
+ );
511
+ }
512
+ }
513
+ /**
514
+ * Get page fans count (total followers)
515
+ * @param pageId - Facebook Page ID
516
+ * @returns Page with fan count
517
+ */
518
+ async getPageFansCount(pageId) {
519
+ try {
520
+ return await this.get(`/${pageId}`, {
521
+ fields: "id,fan_count"
522
+ });
523
+ } catch (error) {
524
+ throw new APIError(
525
+ `Failed to fetch page fans count: ${error instanceof Error ? error.message : "Unknown error"}`,
526
+ void 0,
527
+ "FANS_COUNT_ERROR",
528
+ error
529
+ );
530
+ }
531
+ }
532
+ /**
533
+ * Get page overview with key metrics
534
+ * @param pageId - Facebook Page ID
535
+ * @returns Page overview data
536
+ */
537
+ async getPageOverview(pageId) {
538
+ try {
539
+ const response = await this.get(`/${pageId}`, {
540
+ fields: "id,name,followers_count,fan_count"
541
+ });
542
+ return {
543
+ id: response.id,
544
+ name: response.name,
545
+ followers_count: response.followers_count || response.fan_count || 0,
546
+ fan_count: response.fan_count
547
+ };
548
+ } catch (error) {
549
+ throw new APIError(
550
+ `Failed to fetch page overview: ${error instanceof Error ? error.message : "Unknown error"}`,
551
+ void 0,
552
+ "PAGE_OVERVIEW_ERROR",
553
+ error
554
+ );
555
+ }
556
+ }
557
+ // ============= Video Insights Methods =============
558
+ /**
559
+ * Get video insights
560
+ * @param videoId - Facebook Video ID
561
+ * @param metrics - Specific metrics to fetch (defaults to common metrics)
562
+ * @returns Video insights data
563
+ */
564
+ async getVideoInsights(videoId, metrics) {
565
+ try {
566
+ const metricsToFetch = metrics || METRIC_PRESETS.VIDEO_PERFORMANCE;
567
+ const response = await this.get(
568
+ `/${videoId}/video_insights`,
569
+ {
570
+ metric: metricsToFetch.join(",")
571
+ }
572
+ );
573
+ return response;
574
+ } catch (error) {
575
+ throw new APIError(
576
+ `Failed to fetch video insights: ${error instanceof Error ? error.message : "Unknown error"}`,
577
+ void 0,
578
+ "VIDEO_INSIGHTS_ERROR",
579
+ error
580
+ );
581
+ }
582
+ }
583
+ /**
584
+ * Get video insights with parsed values
585
+ * @param videoId - Facebook Video ID
586
+ * @returns Parsed video insights
587
+ */
588
+ async getVideoInsightsParsed(videoId) {
589
+ try {
590
+ const response = await this.getVideoInsights(videoId, [...VIDEO_METRICS.slice(0, 8)]);
591
+ const insights = { id: videoId };
592
+ for (const item of response.data) {
593
+ const value = item.values?.[0]?.value ?? item.total_value?.value;
594
+ if (typeof value !== "number") continue;
595
+ switch (item.name) {
596
+ case "total_video_views":
597
+ insights.total_views = value;
598
+ break;
599
+ case "total_video_views_unique":
600
+ insights.unique_views = value;
601
+ break;
602
+ case "total_video_avg_time_watched":
603
+ insights.avg_time_watched = value;
604
+ break;
605
+ case "total_video_complete_views":
606
+ insights.complete_views = value;
607
+ break;
608
+ case "total_video_views_organic":
609
+ insights.organic_views = value;
610
+ break;
611
+ case "total_video_views_paid":
612
+ insights.paid_views = value;
613
+ break;
614
+ case "total_video_view_total_time":
615
+ insights.total_time_watched = value;
616
+ break;
617
+ }
618
+ }
619
+ return insights;
620
+ } catch (error) {
621
+ throw new APIError(
622
+ `Failed to fetch parsed video insights: ${error instanceof Error ? error.message : "Unknown error"}`,
623
+ void 0,
624
+ "VIDEO_INSIGHTS_PARSED_ERROR",
625
+ error
626
+ );
627
+ }
628
+ }
629
+ /**
630
+ * Get page videos with basic info
631
+ * @param pageId - Facebook Page ID
632
+ * @param limit - Number of videos to fetch
633
+ * @returns List of videos
634
+ */
635
+ async getPageVideos(pageId, limit = 25) {
636
+ try {
637
+ return await this.get(`/${pageId}/videos`, {
638
+ fields: "id,title,description,created_time,length",
639
+ limit
640
+ });
641
+ } catch (error) {
642
+ throw new APIError(
643
+ `Failed to fetch page videos: ${error instanceof Error ? error.message : "Unknown error"}`,
644
+ void 0,
645
+ "PAGE_VIDEOS_ERROR",
646
+ error
647
+ );
648
+ }
649
+ }
650
+ // ============= Reels Insights Methods =============
651
+ /**
652
+ * Get reels insights
653
+ * @param reelId - Facebook Reel ID
654
+ * @param metrics - Specific metrics to fetch
655
+ * @returns Reels insights data
656
+ */
657
+ async getReelsInsights(reelId, metrics) {
658
+ try {
659
+ const metricsToFetch = metrics || METRIC_PRESETS.REELS_PERFORMANCE;
660
+ const response = await this.get(
661
+ `/${reelId}/video_insights`,
662
+ {
663
+ metric: metricsToFetch.join(",")
664
+ }
665
+ );
666
+ return response;
667
+ } catch (error) {
668
+ throw new APIError(
669
+ `Failed to fetch reels insights: ${error instanceof Error ? error.message : "Unknown error"}`,
670
+ void 0,
671
+ "REELS_INSIGHTS_ERROR",
672
+ error
673
+ );
674
+ }
675
+ }
676
+ /**
677
+ * Get reels insights with parsed values
678
+ * @param reelId - Facebook Reel ID
679
+ * @returns Parsed reels insights
680
+ */
681
+ async getReelsInsightsParsed(reelId) {
682
+ try {
683
+ const response = await this.getReelsInsights(reelId, [...REELS_METRICS]);
684
+ const insights = { id: reelId };
685
+ for (const item of response.data) {
686
+ const value = item.values?.[0]?.value ?? item.total_value?.value;
687
+ if (typeof value !== "number") continue;
688
+ switch (item.name) {
689
+ case "blue_reels_play_count":
690
+ insights.plays = value;
691
+ break;
692
+ case "post_video_avg_time_watched":
693
+ insights.avg_time_watched = value;
694
+ break;
695
+ case "post_video_views":
696
+ insights.total_plays = value;
697
+ break;
698
+ }
699
+ }
700
+ try {
701
+ const postResponse = await this.get(`/${reelId}`, {
702
+ fields: "reactions.summary(true),comments.summary(true),shares"
703
+ });
704
+ insights.likes = postResponse.reactions?.summary?.total_count;
705
+ insights.comments = postResponse.comments?.summary?.total_count;
706
+ insights.shares = postResponse.shares?.count;
707
+ } catch {
708
+ }
709
+ return insights;
710
+ } catch (error) {
711
+ throw new APIError(
712
+ `Failed to fetch parsed reels insights: ${error instanceof Error ? error.message : "Unknown error"}`,
713
+ void 0,
714
+ "REELS_INSIGHTS_PARSED_ERROR",
715
+ error
716
+ );
717
+ }
718
+ }
719
+ /**
720
+ * Get page reels
721
+ * @param pageId - Facebook Page ID
722
+ * @param limit - Number of reels to fetch
723
+ * @returns List of reels
724
+ */
725
+ async getPageReels(pageId, limit = 25) {
726
+ try {
727
+ return await this.get(`/${pageId}/video_reels`, {
728
+ fields: "id,description,created_time",
729
+ limit
730
+ });
731
+ } catch (error) {
732
+ throw new APIError(
733
+ `Failed to fetch page reels: ${error instanceof Error ? error.message : "Unknown error"}`,
734
+ void 0,
735
+ "PAGE_REELS_ERROR",
736
+ error
737
+ );
738
+ }
739
+ }
740
+ // ============= Post Insights Methods =============
741
+ /**
742
+ * Get post insights
743
+ * @param postId - Facebook Post ID
744
+ * @param metrics - Specific metrics to fetch
745
+ * @returns Post insights data
746
+ */
747
+ async getPostInsights(postId, metrics) {
748
+ try {
749
+ const metricsToFetch = metrics || [...POST_METRICS];
750
+ const response = await this.get(
751
+ `/${postId}/insights`,
752
+ {
753
+ metric: metricsToFetch.join(",")
754
+ }
755
+ );
756
+ return response;
757
+ } catch (error) {
758
+ throw new APIError(
759
+ `Failed to fetch post insights: ${error instanceof Error ? error.message : "Unknown error"}`,
760
+ void 0,
761
+ "POST_INSIGHTS_ERROR",
762
+ error
763
+ );
764
+ }
765
+ }
766
+ /**
767
+ * Get post insights with parsed values
768
+ * @param postId - Facebook Post ID
769
+ * @returns Parsed post insights
770
+ */
771
+ async getPostInsightsParsed(postId) {
772
+ try {
773
+ const response = await this.getPostInsights(postId);
774
+ const insights = { id: postId };
775
+ for (const item of response.data) {
776
+ const value = item.values?.[0]?.value ?? item.total_value?.value;
777
+ switch (item.name) {
778
+ case "post_engaged_users":
779
+ if (typeof value === "number") insights.engaged_users = value;
780
+ break;
781
+ case "post_clicks":
782
+ if (typeof value === "number") insights.clicks = value;
783
+ break;
784
+ case "post_negative_feedback":
785
+ if (typeof value === "number") insights.negative_feedback = value;
786
+ break;
787
+ case "post_reactions_by_type_total":
788
+ if (typeof value === "object") insights.reactions_by_type = value;
789
+ break;
790
+ }
791
+ }
792
+ return insights;
793
+ } catch (error) {
794
+ throw new APIError(
795
+ `Failed to fetch parsed post insights: ${error instanceof Error ? error.message : "Unknown error"}`,
796
+ void 0,
797
+ "POST_INSIGHTS_PARSED_ERROR",
798
+ error
799
+ );
800
+ }
801
+ }
802
+ /**
803
+ * Get page posts/feed
804
+ * @param pageId - Facebook Page ID
805
+ * @param limit - Number of posts to fetch
806
+ * @returns List of posts
807
+ */
808
+ async getPagePosts(pageId, limit = 25) {
809
+ try {
810
+ return await this.get(`/${pageId}/feed`, {
811
+ fields: "id,message,created_time,type",
812
+ limit
813
+ });
814
+ } catch (error) {
815
+ throw new APIError(
816
+ `Failed to fetch page posts: ${error instanceof Error ? error.message : "Unknown error"}`,
817
+ void 0,
818
+ "PAGE_POSTS_ERROR",
819
+ error
820
+ );
821
+ }
822
+ }
823
+ // ============= Photo Insights Methods =============
824
+ /**
825
+ * Get photo insights
826
+ * @param photoId - Facebook Photo ID
827
+ * @param metrics - Specific metrics to fetch
828
+ * @returns Photo insights data
829
+ */
830
+ async getPhotoInsights(photoId, metrics) {
831
+ try {
832
+ const metricsToFetch = metrics || [...PHOTO_METRICS];
833
+ const response = await this.get(
834
+ `/${photoId}/insights`,
835
+ {
836
+ metric: metricsToFetch.join(",")
837
+ }
838
+ );
839
+ return response;
840
+ } catch (error) {
841
+ throw new APIError(
842
+ `Failed to fetch photo insights: ${error instanceof Error ? error.message : "Unknown error"}`,
843
+ void 0,
844
+ "PHOTO_INSIGHTS_ERROR",
845
+ error
846
+ );
847
+ }
848
+ }
849
+ /**
850
+ * Get photo insights with parsed values
851
+ * @param photoId - Facebook Photo ID
852
+ * @returns Parsed photo insights
853
+ */
854
+ async getPhotoInsightsParsed(photoId) {
855
+ try {
856
+ const response = await this.getPhotoInsights(photoId);
857
+ const insights = { id: photoId };
858
+ for (const item of response.data) {
859
+ const value = item.values?.[0]?.value ?? item.total_value?.value;
860
+ if (typeof value !== "number") continue;
861
+ switch (item.name) {
862
+ case "photo_views":
863
+ insights.views = value;
864
+ break;
865
+ case "photo_reach":
866
+ insights.reach = value;
867
+ break;
868
+ case "engaged_users":
869
+ insights.engaged_users = value;
870
+ break;
871
+ }
872
+ }
873
+ return insights;
874
+ } catch (error) {
875
+ throw new APIError(
876
+ `Failed to fetch parsed photo insights: ${error instanceof Error ? error.message : "Unknown error"}`,
877
+ void 0,
878
+ "PHOTO_INSIGHTS_PARSED_ERROR",
879
+ error
880
+ );
881
+ }
882
+ }
883
+ /**
884
+ * Get page photos
885
+ * @param pageId - Facebook Page ID
886
+ * @param limit - Number of photos to fetch
887
+ * @returns List of photos
888
+ */
889
+ async getPagePhotos(pageId, limit = 25) {
890
+ try {
891
+ return await this.get(`/${pageId}/photos`, {
892
+ fields: "id,name,created_time",
893
+ type: "uploaded",
894
+ limit
895
+ });
896
+ } catch (error) {
897
+ throw new APIError(
898
+ `Failed to fetch page photos: ${error instanceof Error ? error.message : "Unknown error"}`,
899
+ void 0,
900
+ "PAGE_PHOTOS_ERROR",
901
+ error
902
+ );
903
+ }
904
+ }
905
+ // ============= Batch/Helper Methods =============
906
+ /**
907
+ * Get quick insights summary for a page
908
+ * Combines key metrics in a single call
909
+ * @param pageId - Facebook Page ID
910
+ * @param period - Period for time-based metrics
911
+ * @returns Summary of page insights
912
+ */
913
+ async getPageInsightsSummary(pageId, period = "days_28") {
914
+ try {
915
+ const overview = await this.getPageOverview(pageId);
916
+ const insightsResponse = await this.getPageInsights(pageId, {
917
+ metric: [...METRIC_PRESETS.PAGE_OVERVIEW],
918
+ period
919
+ });
920
+ const engagement = {};
921
+ for (const item of insightsResponse.data) {
922
+ const values = item.values || [];
923
+ const total = values.reduce((sum, v) => sum + (typeof v.value === "number" ? v.value : 0), 0);
924
+ switch (item.name) {
925
+ case "page_views_total":
926
+ engagement.views = total;
927
+ break;
928
+ case "page_post_engagements":
929
+ engagement.post_engagements = total;
930
+ break;
931
+ case "page_video_views":
932
+ engagement.video_views = total;
933
+ break;
934
+ }
935
+ }
936
+ return { overview, engagement };
937
+ } catch (error) {
938
+ throw new APIError(
939
+ `Failed to fetch page insights summary: ${error instanceof Error ? error.message : "Unknown error"}`,
940
+ void 0,
941
+ "PAGE_SUMMARY_ERROR",
942
+ error
943
+ );
944
+ }
945
+ }
946
+ /**
947
+ * Get current API version (read-only)
948
+ */
949
+ getApiVersion() {
950
+ return FACEBOOK_GRAPH_API_VERSION;
951
+ }
952
+ };
953
+
954
+ // providers/facebook/auth/FacebookAuth.ts
955
+ var FacebookAuth = class {
956
+ constructor(config) {
957
+ this.config = config;
958
+ this.httpClient = new HttpClient({
959
+ baseURL: FACEBOOK_GRAPH_API_BASE_URL
960
+ });
961
+ }
962
+ /**
963
+ * Exchange Facebook token for session
964
+ * In Expo + Supabase, this is handled in the app
965
+ * This function only validates and gets profile info
966
+ */
967
+ async exchangeToken(facebookToken) {
968
+ try {
969
+ const profile = await this.getProfile(facebookToken);
970
+ return {
971
+ accessToken: facebookToken,
972
+ user: profile
973
+ };
974
+ } catch (error) {
975
+ throw new AuthError(
976
+ `Failed to exchange Facebook token: ${error instanceof Error ? error.message : "Unknown error"}`,
977
+ "TOKEN_EXCHANGE_ERROR",
978
+ error
979
+ );
980
+ }
981
+ }
982
+ /**
983
+ * Get Facebook user profile
984
+ */
985
+ async getProfile(accessToken) {
986
+ try {
987
+ const profile = await this.httpClient.get("/me", {
988
+ fields: "id,name,email,picture",
989
+ access_token: accessToken
990
+ });
991
+ return {
992
+ id: profile.id,
993
+ name: profile.name,
994
+ email: profile.email,
995
+ picture: profile.picture?.data?.url
996
+ };
997
+ } catch (error) {
998
+ throw new AuthError(
999
+ `Failed to fetch Facebook profile: ${error instanceof Error ? error.message : "Unknown error"}`,
1000
+ "PROFILE_FETCH_ERROR",
1001
+ error
1002
+ );
1003
+ }
1004
+ }
1005
+ /**
1006
+ * Exchange short-lived token for long-lived token
1007
+ */
1008
+ async exchangeForLongLivedToken(params) {
1009
+ try {
1010
+ const response = await this.httpClient.get(
1011
+ "/oauth/access_token",
1012
+ {
1013
+ grant_type: "fb_exchange_token",
1014
+ client_id: params.appId,
1015
+ client_secret: params.appSecret,
1016
+ fb_exchange_token: params.shortLivedToken
1017
+ }
1018
+ );
1019
+ return response.access_token;
1020
+ } catch (error) {
1021
+ throw new AuthError(
1022
+ `Failed to exchange for long-lived token: ${error instanceof Error ? error.message : "Unknown error"}`,
1023
+ "LONG_LIVED_TOKEN_ERROR",
1024
+ error
1025
+ );
1026
+ }
1027
+ }
1028
+ /**
1029
+ * Get OAuth configuration for Facebook
1030
+ */
1031
+ getOAuthConfig(scopes, redirectUri) {
1032
+ return {
1033
+ authorizationEndpoint: FACEBOOK_OAUTH_AUTHORIZATION_URL,
1034
+ tokenEndpoint: `${FACEBOOK_GRAPH_API_BASE_URL}/oauth/access_token`,
1035
+ clientId: this.config.appId,
1036
+ clientSecret: this.config.appSecret,
1037
+ redirectUri,
1038
+ scopes,
1039
+ responseType: "token",
1040
+ extraParams: {
1041
+ display: "popup"
1042
+ }
1043
+ };
1044
+ }
1045
+ /**
1046
+ * Get authorization URL
1047
+ */
1048
+ getAuthorizationUrl(scopes, redirectUri, state) {
1049
+ const params = new URLSearchParams({
1050
+ client_id: this.config.appId,
1051
+ redirect_uri: redirectUri,
1052
+ scope: scopes.join(","),
1053
+ response_type: "token",
1054
+ display: "popup",
1055
+ ...state && { state }
1056
+ });
1057
+ return `${FACEBOOK_OAUTH_AUTHORIZATION_URL}?${params.toString()}`;
1058
+ }
1059
+ /**
1060
+ * Get current API version (read-only)
1061
+ */
1062
+ getApiVersion() {
1063
+ return FACEBOOK_GRAPH_API_VERSION;
1064
+ }
1065
+ };
1066
+
1067
+ // providers/facebook/FacebookProvider.ts
1068
+ var FacebookProvider = class {
1069
+ constructor(config) {
1070
+ this.name = "facebook";
1071
+ this.config = config;
1072
+ this.auth = new FacebookAuth(config);
1073
+ this.api = new FacebookAPI(config);
1074
+ }
1075
+ /**
1076
+ * Create new API instance with access token
1077
+ */
1078
+ createAPIClient(accessToken) {
1079
+ return new FacebookAPI(this.config, accessToken);
1080
+ }
1081
+ /**
1082
+ * Update access token of existing API client
1083
+ */
1084
+ setAccessToken(accessToken) {
1085
+ this.api.setAccessToken(accessToken);
1086
+ }
1087
+ /**
1088
+ * Get current access token
1089
+ */
1090
+ getAccessToken() {
1091
+ return this.api.getAccessToken();
1092
+ }
1093
+ };
1094
+
1095
+ // providers/facebook/types/index.ts
1096
+ var FacebookScopesBusiness = /* @__PURE__ */ ((FacebookScopesBusiness2) => {
1097
+ FacebookScopesBusiness2["PUBLIC_PROFILE"] = "public_profile";
1098
+ FacebookScopesBusiness2["EMAIL"] = "email";
1099
+ FacebookScopesBusiness2["PAGES_SHOW_LIST"] = "pages_show_list";
1100
+ FacebookScopesBusiness2["PAGES_READ_ENGAGEMENT"] = "pages_read_engagement";
1101
+ FacebookScopesBusiness2["PAGES_MANAGE_POSTS"] = "pages_manage_posts";
1102
+ FacebookScopesBusiness2["ADS_MANAGEMENT"] = "ads_management";
1103
+ FacebookScopesBusiness2["ADS_READ"] = "ads_read";
1104
+ FacebookScopesBusiness2["BUSINESS_MANAGEMENT"] = "business_management";
1105
+ FacebookScopesBusiness2["CATALOG_MANAGEMENT"] = "catalog_management";
1106
+ FacebookScopesBusiness2["COMMERCE_ACCOUNT_MANAGE_ORDERS"] = "commerce_account_manage_orders";
1107
+ FacebookScopesBusiness2["COMMERCE_ACCOUNT_READ_ORDERS"] = "commerce_account_read_orders";
1108
+ FacebookScopesBusiness2["COMMERCE_ACCOUNT_READ_REPORTS"] = "commerce_account_read_reports";
1109
+ FacebookScopesBusiness2["COMMERCE_ACCOUNT_READ_SETTINGS"] = "commerce_account_read_settings";
1110
+ FacebookScopesBusiness2["COMMERCE_MANAGE_ACCOUNTS"] = "commerce_manage_accounts";
1111
+ FacebookScopesBusiness2["INSTAGRAM_BASIC"] = "instagram_basic";
1112
+ FacebookScopesBusiness2["INSTAGRAM_CONTENT_PUBLISH"] = "instagram_content_publish";
1113
+ FacebookScopesBusiness2["INSTAGRAM_MANAGE_COMMENTS"] = "instagram_manage_comments";
1114
+ FacebookScopesBusiness2["INSTAGRAM_MANAGE_INSIGHTS"] = "instagram_manage_insights";
1115
+ FacebookScopesBusiness2["INSTAGRAM_MANAGE_MESSAGES"] = "instagram_manage_messages";
1116
+ FacebookScopesBusiness2["INSTAGRAM_SHOPPING_TAG_PRODUCTS"] = "instagram_shopping_tag_products";
1117
+ FacebookScopesBusiness2["LEADS_RETRIEVAL"] = "leads_retrieval";
1118
+ FacebookScopesBusiness2["MANAGE_APP_SOLUTIONS"] = "manage_app_solutions";
1119
+ FacebookScopesBusiness2["MANAGE_FUNDRAISERS"] = "manage_fundraisers";
1120
+ FacebookScopesBusiness2["PAGES_MANAGE_CTA"] = "pages_manage_cta";
1121
+ FacebookScopesBusiness2["PAGE_EVENTS"] = "page_events";
1122
+ FacebookScopesBusiness2["PAGES_MANAGE_ADS"] = "pages_manage_ads";
1123
+ FacebookScopesBusiness2["PAGES_MANAGE_ENGAGEMENT"] = "pages_manage_engagement";
1124
+ FacebookScopesBusiness2["PAGES_MANAGE_INSTANT_ARTICLES"] = "pages_manage_instant_articles";
1125
+ FacebookScopesBusiness2["PAGES_MANAGE_METADATA"] = "pages_manage_metadata";
1126
+ FacebookScopesBusiness2["PAGES_MESSAGING"] = "pages_messaging";
1127
+ FacebookScopesBusiness2["PAGES_READ_USER_CONTENT"] = "pages_read_user_content";
1128
+ FacebookScopesBusiness2["PRIVATE_COMPUTATION_ACCESS"] = "private_computation_access";
1129
+ FacebookScopesBusiness2["PUBLISH_VIDEO"] = "publish_video";
1130
+ FacebookScopesBusiness2["READ_INSIGHTS"] = "read_insights";
1131
+ FacebookScopesBusiness2["READ_AUDIENCE_NETWORK_INSIGHTS"] = "read_audience_network_insights";
1132
+ FacebookScopesBusiness2["WHATSAPP_BUSINESS_MANAGEMENT"] = "whatsapp_business_management";
1133
+ FacebookScopesBusiness2["WHATSAPP_BUSINESS_MESSAGING"] = "whatsapp_business_messaging";
1134
+ return FacebookScopesBusiness2;
1135
+ })(FacebookScopesBusiness || {});
1136
+ var FacebookScopesUser = /* @__PURE__ */ ((FacebookScopesUser2) => {
1137
+ FacebookScopesUser2["PUBLIC_PROFILE"] = "public_profile";
1138
+ FacebookScopesUser2["EMAIL"] = "email";
1139
+ FacebookScopesUser2["USER_FRIENDS"] = "user_friends";
1140
+ return FacebookScopesUser2;
1141
+ })(FacebookScopesUser || {});
1142
+
1143
+ // providers/instagram/auth/InstagramAuth.ts
1144
+ var InstagramAuth = class {
1145
+ constructor(config) {
1146
+ this.config = config;
1147
+ this.httpClient = new HttpClient({
1148
+ baseURL: FACEBOOK_GRAPH_API_BASE_URL
1149
+ });
1150
+ }
1151
+ /**
1152
+ * Exchange Instagram/Facebook token for session
1153
+ */
1154
+ async exchangeToken(instagramToken) {
1155
+ try {
1156
+ const profile = await this.getProfile(instagramToken);
1157
+ return {
1158
+ accessToken: instagramToken,
1159
+ user: profile
1160
+ };
1161
+ } catch (error) {
1162
+ throw new AuthError(
1163
+ `Failed to exchange Instagram token: ${error instanceof Error ? error.message : "Unknown error"}`,
1164
+ "TOKEN_EXCHANGE_ERROR",
1165
+ error
1166
+ );
1167
+ }
1168
+ }
1169
+ /**
1170
+ * Get Instagram user profile
1171
+ * First gets Facebook profile, then Instagram Business profile
1172
+ */
1173
+ async getProfile(accessToken) {
1174
+ try {
1175
+ const response = await this.httpClient.get("/me/accounts", {
1176
+ fields: "instagram_business_account",
1177
+ access_token: accessToken
1178
+ });
1179
+ if (!response.data || response.data.length === 0) {
1180
+ throw new AuthError(
1181
+ "No Instagram business account found",
1182
+ "NO_INSTAGRAM_ACCOUNT"
1183
+ );
1184
+ }
1185
+ const instagramAccountId = response.data[0]?.instagram_business_account?.id;
1186
+ if (!instagramAccountId) {
1187
+ throw new AuthError(
1188
+ "No Instagram business account ID found",
1189
+ "NO_INSTAGRAM_ACCOUNT_ID"
1190
+ );
1191
+ }
1192
+ const profile = await this.httpClient.get(
1193
+ `/${instagramAccountId}`,
1194
+ {
1195
+ fields: "id,username,name,account_type,profile_picture_url",
1196
+ access_token: accessToken
1197
+ }
1198
+ );
1199
+ return {
1200
+ id: profile.id,
1201
+ name: profile.name || profile.username,
1202
+ username: profile.username,
1203
+ picture: profile.profile_picture_url
1204
+ };
1205
+ } catch (error) {
1206
+ throw new AuthError(
1207
+ `Failed to fetch Instagram profile: ${error instanceof Error ? error.message : "Unknown error"}`,
1208
+ "PROFILE_FETCH_ERROR",
1209
+ error
1210
+ );
1211
+ }
1212
+ }
1213
+ /**
1214
+ * Get OAuth configuration for Instagram
1215
+ * Instagram uses the same OAuth flow as Facebook
1216
+ */
1217
+ getOAuthConfig(scopes, redirectUri) {
1218
+ return {
1219
+ authorizationEndpoint: FACEBOOK_OAUTH_AUTHORIZATION_URL,
1220
+ tokenEndpoint: `${FACEBOOK_GRAPH_API_BASE_URL}/oauth/access_token`,
1221
+ clientId: this.config.appId,
1222
+ clientSecret: this.config.appSecret,
1223
+ redirectUri,
1224
+ scopes,
1225
+ responseType: "token",
1226
+ extraParams: {
1227
+ display: "popup"
1228
+ }
1229
+ };
1230
+ }
1231
+ /**
1232
+ * Get authorization URL
1233
+ */
1234
+ getAuthorizationUrl(scopes, redirectUri, state) {
1235
+ const params = new URLSearchParams({
1236
+ client_id: this.config.appId,
1237
+ redirect_uri: redirectUri,
1238
+ scope: scopes.join(","),
1239
+ response_type: "token",
1240
+ display: "popup",
1241
+ ...state && { state }
1242
+ });
1243
+ return `${FACEBOOK_OAUTH_AUTHORIZATION_URL}?${params.toString()}`;
1244
+ }
1245
+ /**
1246
+ * Get current API version (read-only)
1247
+ */
1248
+ getApiVersion() {
1249
+ return FACEBOOK_GRAPH_API_VERSION;
1250
+ }
1251
+ };
1252
+
1253
+ // providers/instagram/api/InstagramAPI.ts
1254
+ var InstagramAPI = class {
1255
+ constructor(config, accessToken) {
1256
+ this.config = config;
1257
+ this.accessToken = accessToken;
1258
+ this.httpClient = new HttpClient({
1259
+ baseURL: FACEBOOK_GRAPH_API_BASE_URL,
1260
+ accessToken: this.accessToken
1261
+ });
1262
+ }
1263
+ /**
1264
+ * Generic request
1265
+ */
1266
+ async request(endpoint, options = {}) {
1267
+ if (this.accessToken && !options.params?.access_token) {
1268
+ options.params = {
1269
+ ...options.params,
1270
+ access_token: this.accessToken
1271
+ };
1272
+ }
1273
+ return this.httpClient.request(endpoint, options);
1274
+ }
1275
+ /**
1276
+ * GET request
1277
+ */
1278
+ async get(endpoint, params) {
1279
+ return this.request(endpoint, { method: "GET", params });
1280
+ }
1281
+ /**
1282
+ * POST request
1283
+ */
1284
+ async post(endpoint, body) {
1285
+ return this.request(endpoint, { method: "POST", body });
1286
+ }
1287
+ /**
1288
+ * PUT request
1289
+ */
1290
+ async put(endpoint, body) {
1291
+ return this.request(endpoint, { method: "PUT", body });
1292
+ }
1293
+ /**
1294
+ * DELETE request
1295
+ */
1296
+ async delete(endpoint) {
1297
+ return this.request(endpoint, { method: "DELETE" });
1298
+ }
1299
+ /**
1300
+ * Update access token
1301
+ */
1302
+ setAccessToken(token) {
1303
+ this.accessToken = token;
1304
+ this.httpClient.setAccessToken(token);
1305
+ }
1306
+ /**
1307
+ * Get current access token
1308
+ */
1309
+ getAccessToken() {
1310
+ return this.accessToken;
1311
+ }
1312
+ // ============= Instagram-specific methods =============
1313
+ /**
1314
+ * Get user's Instagram Business account (single account from first page)
1315
+ */
1316
+ async getAccount(pageAccessToken) {
1317
+ try {
1318
+ const token = pageAccessToken || this.accessToken;
1319
+ if (!token) {
1320
+ throw new APIError("Access token is required", 401, "NO_TOKEN");
1321
+ }
1322
+ const response = await this.get("/me/accounts", {
1323
+ fields: "instagram_business_account",
1324
+ access_token: token
1325
+ });
1326
+ if (!response.data || response.data.length === 0) {
1327
+ throw new APIError(
1328
+ "No Instagram business account found",
1329
+ 404,
1330
+ "NO_INSTAGRAM_ACCOUNT"
1331
+ );
1332
+ }
1333
+ const instagramAccountId = response.data[0]?.instagram_business_account?.id;
1334
+ if (!instagramAccountId) {
1335
+ throw new APIError(
1336
+ "No Instagram business account ID found",
1337
+ 404,
1338
+ "NO_INSTAGRAM_ACCOUNT_ID"
1339
+ );
1340
+ }
1341
+ const account = await this.get(
1342
+ `/${instagramAccountId}`,
1343
+ {
1344
+ fields: "id,username,name,profile_picture_url,followers_count,follows_count,media_count",
1345
+ access_token: token
1346
+ }
1347
+ );
1348
+ return account;
1349
+ } catch (error) {
1350
+ throw new APIError(
1351
+ `Failed to fetch Instagram account: ${error instanceof Error ? error.message : "Unknown error"}`,
1352
+ void 0,
1353
+ "ACCOUNT_FETCH_ERROR",
1354
+ error
1355
+ );
1356
+ }
1357
+ }
1358
+ /**
1359
+ * Get all Instagram Business accounts linked to user's Facebook pages
1360
+ * Returns all Instagram accounts from all connected Facebook pages
1361
+ */
1362
+ async getInstagramAccounts(userAccessToken) {
1363
+ try {
1364
+ const token = userAccessToken || this.accessToken;
1365
+ if (!token) {
1366
+ throw new APIError("Access token is required", 401, "NO_TOKEN");
1367
+ }
1368
+ const response = await this.get("/me/accounts", {
1369
+ fields: "id,name,instagram_business_account{id,username,name,profile_picture_url,followers_count,follows_count,media_count}",
1370
+ access_token: token
1371
+ });
1372
+ if (!response.data || response.data.length === 0) {
1373
+ console.log(" \u2139\uFE0F No Facebook pages found");
1374
+ return [];
1375
+ }
1376
+ const instagramAccounts = [];
1377
+ for (const page of response.data) {
1378
+ if (page.instagram_business_account) {
1379
+ instagramAccounts.push({
1380
+ id: page.instagram_business_account.id,
1381
+ username: page.instagram_business_account.username,
1382
+ name: page.instagram_business_account.name,
1383
+ profile_picture_url: page.instagram_business_account.profile_picture_url,
1384
+ followers_count: page.instagram_business_account.followers_count,
1385
+ follows_count: page.instagram_business_account.follows_count,
1386
+ media_count: page.instagram_business_account.media_count
1387
+ });
1388
+ }
1389
+ }
1390
+ console.log(` \u2705 Found ${instagramAccounts.length} Instagram account(s)`);
1391
+ return instagramAccounts;
1392
+ } catch (error) {
1393
+ throw new APIError(
1394
+ `Failed to fetch Instagram accounts: ${error instanceof Error ? error.message : "Unknown error"}`,
1395
+ void 0,
1396
+ "ACCOUNTS_FETCH_ERROR",
1397
+ error
1398
+ );
1399
+ }
1400
+ }
1401
+ /**
1402
+ * Get Instagram media
1403
+ */
1404
+ async getMedia(instagramAccountId, limit = 25) {
1405
+ try {
1406
+ const response = await this.get(
1407
+ `/${instagramAccountId}/media`,
1408
+ {
1409
+ fields: "id,media_type,media_url,thumbnail_url,permalink,caption,timestamp,like_count,comments_count",
1410
+ limit
1411
+ }
1412
+ );
1413
+ return response.data;
1414
+ } catch (error) {
1415
+ throw new APIError(
1416
+ `Failed to fetch Instagram media: ${error instanceof Error ? error.message : "Unknown error"}`,
1417
+ void 0,
1418
+ "MEDIA_FETCH_ERROR",
1419
+ error
1420
+ );
1421
+ }
1422
+ }
1423
+ /**
1424
+ * Publish photo to Instagram (2-step process)
1425
+ */
1426
+ async publishPhoto(params) {
1427
+ const { instagramAccountId, imageUrl, caption } = params;
1428
+ if (!imageUrl) {
1429
+ throw new APIError(
1430
+ "Image URL is required for photo posts",
1431
+ 400,
1432
+ "INVALID_PARAMS"
1433
+ );
1434
+ }
1435
+ try {
1436
+ const containerResponse = await this.post(
1437
+ `/${instagramAccountId}/media`,
1438
+ {
1439
+ image_url: imageUrl,
1440
+ caption: caption || ""
1441
+ }
1442
+ );
1443
+ const publishResponse = await this.post(
1444
+ `/${instagramAccountId}/media_publish`,
1445
+ {
1446
+ creation_id: containerResponse.id
1447
+ }
1448
+ );
1449
+ return publishResponse;
1450
+ } catch (error) {
1451
+ throw new APIError(
1452
+ `Failed to publish photo: ${error instanceof Error ? error.message : "Unknown error"}`,
1453
+ void 0,
1454
+ "PHOTO_PUBLISH_ERROR",
1455
+ error
1456
+ );
1457
+ }
1458
+ }
1459
+ /**
1460
+ * Publish video to Instagram (2-step process)
1461
+ */
1462
+ async publishVideo(params) {
1463
+ const { instagramAccountId, videoUrl, caption } = params;
1464
+ if (!videoUrl) {
1465
+ throw new APIError(
1466
+ "Video URL is required for video posts",
1467
+ 400,
1468
+ "INVALID_PARAMS"
1469
+ );
1470
+ }
1471
+ try {
1472
+ const containerResponse = await this.post(
1473
+ `/${instagramAccountId}/media`,
1474
+ {
1475
+ media_type: "VIDEO",
1476
+ video_url: videoUrl,
1477
+ caption: caption || ""
1478
+ }
1479
+ );
1480
+ const publishResponse = await this.post(
1481
+ `/${instagramAccountId}/media_publish`,
1482
+ {
1483
+ creation_id: containerResponse.id
1484
+ }
1485
+ );
1486
+ return publishResponse;
1487
+ } catch (error) {
1488
+ throw new APIError(
1489
+ `Failed to publish video: ${error instanceof Error ? error.message : "Unknown error"}`,
1490
+ void 0,
1491
+ "VIDEO_PUBLISH_ERROR",
1492
+ error
1493
+ );
1494
+ }
1495
+ }
1496
+ /**
1497
+ * Delete Instagram media
1498
+ */
1499
+ async deleteMedia(mediaId) {
1500
+ try {
1501
+ const response = await this.delete(
1502
+ `/${mediaId}`
1503
+ );
1504
+ return response.success === true;
1505
+ } catch (error) {
1506
+ throw new APIError(
1507
+ `Failed to delete media: ${error instanceof Error ? error.message : "Unknown error"}`,
1508
+ void 0,
1509
+ "MEDIA_DELETE_ERROR",
1510
+ error
1511
+ );
1512
+ }
1513
+ }
1514
+ /**
1515
+ * Get Instagram account insights
1516
+ */
1517
+ async getAccountInsights(instagramAccountId, params) {
1518
+ try {
1519
+ const { metric, period, metric_type, since, until } = params;
1520
+ const response = await this.get(
1521
+ `/${instagramAccountId}/insights`,
1522
+ {
1523
+ metric: metric.join(","),
1524
+ period: period || "day",
1525
+ metric_type: metric_type || "total_value",
1526
+ ...since && { since },
1527
+ ...until && { until }
1528
+ }
1529
+ );
1530
+ return response;
1531
+ } catch (error) {
1532
+ throw new APIError(
1533
+ `Failed to fetch insights: ${error instanceof Error ? error.message : "Unknown error"}`,
1534
+ void 0,
1535
+ "INSIGHTS_FETCH_ERROR",
1536
+ error
1537
+ );
1538
+ }
1539
+ }
1540
+ /**
1541
+ * Get specific media insights
1542
+ */
1543
+ async getMediaInsights(mediaId, metrics) {
1544
+ try {
1545
+ const response = await this.get(
1546
+ `/${mediaId}/insights`,
1547
+ {
1548
+ metric: metrics.join(",")
1549
+ }
1550
+ );
1551
+ return response;
1552
+ } catch (error) {
1553
+ throw new APIError(
1554
+ `Failed to fetch media insights: ${error instanceof Error ? error.message : "Unknown error"}`,
1555
+ void 0,
1556
+ "MEDIA_INSIGHTS_ERROR",
1557
+ error
1558
+ );
1559
+ }
1560
+ }
1561
+ // ============= Enhanced Insights Methods =============
1562
+ /**
1563
+ * Get account insights with default metrics
1564
+ * Uses updated metrics (views instead of impressions)
1565
+ * @param instagramAccountId - Instagram Account ID
1566
+ * @param period - Period for metrics
1567
+ * @returns Account insights
1568
+ */
1569
+ async getAccountInsightsOverview(instagramAccountId, period = "days_28") {
1570
+ return this.getAccountInsights(instagramAccountId, {
1571
+ metric: [...METRIC_PRESETS.INSTAGRAM_OVERVIEW],
1572
+ period,
1573
+ metric_type: "total_value"
1574
+ });
1575
+ }
1576
+ /**
1577
+ * Get account summary with parsed values
1578
+ * @param instagramAccountId - Instagram Account ID
1579
+ * @param period - Period for metrics
1580
+ * @returns Parsed account summary
1581
+ */
1582
+ async getAccountSummary(instagramAccountId, period = "days_28") {
1583
+ try {
1584
+ const account = await this.get(`/${instagramAccountId}`, {
1585
+ fields: "id,username,followers_count"
1586
+ });
1587
+ const insights = await this.getAccountInsightsOverview(instagramAccountId, period);
1588
+ const summary = {
1589
+ id: account.id,
1590
+ username: account.username,
1591
+ followers_count: account.followers_count || 0
1592
+ };
1593
+ for (const item of insights.data) {
1594
+ const value = item.total_value?.value ?? item.values?.[0]?.value;
1595
+ if (typeof value !== "number") continue;
1596
+ switch (item.name) {
1597
+ case "views":
1598
+ summary.views = value;
1599
+ break;
1600
+ case "reach":
1601
+ summary.reach = value;
1602
+ break;
1603
+ case "accounts_engaged":
1604
+ summary.accounts_engaged = value;
1605
+ break;
1606
+ case "total_interactions":
1607
+ summary.total_interactions = value;
1608
+ break;
1609
+ }
1610
+ }
1611
+ return summary;
1612
+ } catch (error) {
1613
+ throw new APIError(
1614
+ `Failed to fetch account summary: ${error instanceof Error ? error.message : "Unknown error"}`,
1615
+ void 0,
1616
+ "ACCOUNT_SUMMARY_ERROR",
1617
+ error
1618
+ );
1619
+ }
1620
+ }
1621
+ /**
1622
+ * Get media insights based on media type
1623
+ * Automatically selects appropriate metrics
1624
+ * @param mediaId - Media ID
1625
+ * @param mediaType - Type of media
1626
+ * @returns Media insights
1627
+ */
1628
+ async getMediaInsightsByType(mediaId, mediaType) {
1629
+ let metrics;
1630
+ switch (mediaType) {
1631
+ case "REELS":
1632
+ metrics = INSTAGRAM_REELS_METRICS;
1633
+ break;
1634
+ case "VIDEO":
1635
+ metrics = [...INSTAGRAM_MEDIA_METRICS_COMMON, "plays"];
1636
+ break;
1637
+ default:
1638
+ metrics = INSTAGRAM_MEDIA_METRICS_COMMON;
1639
+ }
1640
+ return this.getMediaInsights(mediaId, [...metrics]);
1641
+ }
1642
+ /**
1643
+ * Get media insights with parsed values
1644
+ * @param mediaId - Media ID
1645
+ * @param mediaType - Type of media
1646
+ * @returns Parsed media insights
1647
+ */
1648
+ async getMediaInsightsParsed(mediaId, mediaType = "IMAGE") {
1649
+ try {
1650
+ const response = await this.getMediaInsightsByType(mediaId, mediaType);
1651
+ const insights = {
1652
+ id: mediaId,
1653
+ media_type: mediaType
1654
+ };
1655
+ for (const item of response.data) {
1656
+ const value = item.total_value?.value ?? item.values?.[0]?.value;
1657
+ if (typeof value !== "number") continue;
1658
+ switch (item.name) {
1659
+ case "views":
1660
+ insights.views = value;
1661
+ break;
1662
+ case "reach":
1663
+ insights.reach = value;
1664
+ break;
1665
+ case "likes":
1666
+ insights.likes = value;
1667
+ break;
1668
+ case "comments":
1669
+ insights.comments = value;
1670
+ break;
1671
+ case "shares":
1672
+ insights.shares = value;
1673
+ break;
1674
+ case "saved":
1675
+ insights.saves = value;
1676
+ break;
1677
+ case "plays":
1678
+ insights.plays = value;
1679
+ break;
1680
+ case "total_interactions":
1681
+ insights.total_interactions = value;
1682
+ break;
1683
+ }
1684
+ }
1685
+ return insights;
1686
+ } catch (error) {
1687
+ throw new APIError(
1688
+ `Failed to fetch parsed media insights: ${error instanceof Error ? error.message : "Unknown error"}`,
1689
+ void 0,
1690
+ "MEDIA_INSIGHTS_PARSED_ERROR",
1691
+ error
1692
+ );
1693
+ }
1694
+ }
1695
+ /**
1696
+ * Get story insights
1697
+ * @param storyId - Story media ID
1698
+ * @returns Story insights
1699
+ */
1700
+ async getStoryInsights(storyId) {
1701
+ return this.getMediaInsights(storyId, [...INSTAGRAM_STORIES_METRICS]);
1702
+ }
1703
+ /**
1704
+ * Get reels insights
1705
+ * @param reelId - Reel media ID
1706
+ * @returns Reel insights
1707
+ */
1708
+ async getReelInsights(reelId) {
1709
+ return this.getMediaInsights(reelId, [...INSTAGRAM_REELS_METRICS]);
1710
+ }
1711
+ /**
1712
+ * Get engagement metrics for account
1713
+ * @param instagramAccountId - Instagram Account ID
1714
+ * @param period - Period for metrics
1715
+ * @returns Engagement insights
1716
+ */
1717
+ async getEngagementInsights(instagramAccountId, period = "days_28") {
1718
+ return this.getAccountInsights(instagramAccountId, {
1719
+ metric: [...METRIC_PRESETS.INSTAGRAM_ENGAGEMENT],
1720
+ period,
1721
+ metric_type: "total_value"
1722
+ });
1723
+ }
1724
+ /**
1725
+ * Get demographics insights for account
1726
+ * @param instagramAccountId - Instagram Account ID
1727
+ * @param breakdown - Demographic breakdown type
1728
+ * @returns Demographics insights
1729
+ */
1730
+ async getDemographicsInsights(instagramAccountId, breakdown = "country") {
1731
+ try {
1732
+ const response = await this.get(
1733
+ `/${instagramAccountId}/insights`,
1734
+ {
1735
+ metric: "follower_demographics",
1736
+ period: "lifetime",
1737
+ metric_type: "total_value",
1738
+ breakdown
1739
+ }
1740
+ );
1741
+ return response;
1742
+ } catch (error) {
1743
+ throw new APIError(
1744
+ `Failed to fetch demographics insights: ${error instanceof Error ? error.message : "Unknown error"}`,
1745
+ void 0,
1746
+ "DEMOGRAPHICS_ERROR",
1747
+ error
1748
+ );
1749
+ }
1750
+ }
1751
+ /**
1752
+ * Get current API version (read-only)
1753
+ */
1754
+ getApiVersion() {
1755
+ return FACEBOOK_GRAPH_API_VERSION;
1756
+ }
1757
+ };
1758
+
1759
+ // providers/instagram/InstagramProvider.ts
1760
+ var InstagramProvider = class {
1761
+ constructor(config) {
1762
+ this.name = "instagram";
1763
+ this.config = config;
1764
+ this.auth = new InstagramAuth(config);
1765
+ this.api = new InstagramAPI(config);
1766
+ }
1767
+ /**
1768
+ * Create new API instance with access token
1769
+ */
1770
+ createAPIClient(accessToken) {
1771
+ return new InstagramAPI(this.config, accessToken);
1772
+ }
1773
+ /**
1774
+ * Update access token of existing API client
1775
+ */
1776
+ setAccessToken(accessToken) {
1777
+ this.api.setAccessToken(accessToken);
1778
+ }
1779
+ /**
1780
+ * Get current access token
1781
+ */
1782
+ getAccessToken() {
1783
+ return this.api.getAccessToken();
1784
+ }
1785
+ };
1786
+
1787
+ // providers/instagram/types/index.ts
1788
+ var InstagramScopes = /* @__PURE__ */ ((InstagramScopes2) => {
1789
+ InstagramScopes2["BASIC"] = "instagram_basic";
1790
+ InstagramScopes2["CONTENT_PUBLISH"] = "instagram_content_publish";
1791
+ InstagramScopes2["MANAGE_COMMENTS"] = "instagram_manage_comments";
1792
+ InstagramScopes2["MANAGE_INSIGHTS"] = "instagram_manage_insights";
1793
+ InstagramScopes2["MANAGE_MESSAGES"] = "instagram_manage_messages";
1794
+ InstagramScopes2["SHOPPING_TAG_PRODUCTS"] = "instagram_shopping_tag_products";
1795
+ return InstagramScopes2;
1796
+ })(InstagramScopes || {});
1797
+
1798
+ // providers/tiktok/constants.ts
1799
+ var TIKTOK_API_VERSION = "v2";
1800
+ var TIKTOK_API_BASE_URL = `https://open.tiktokapis.com/${TIKTOK_API_VERSION}`;
1801
+ var TIKTOK_OAUTH_AUTHORIZATION_URL = "https://www.tiktok.com/v2/auth/authorize/";
1802
+ var TIKTOK_OAUTH_TOKEN_URL = "https://open.tiktokapis.com/v2/oauth/token/";
1803
+ var TIKTOK_OAUTH_REVOKE_URL = "https://open.tiktokapis.com/v2/oauth/revoke/";
1804
+ var TikTokScopes = {
1805
+ // User info scopes
1806
+ USER_INFO_BASIC: "user.info.basic",
1807
+ USER_INFO_PROFILE: "user.info.profile",
1808
+ USER_INFO_STATS: "user.info.stats",
1809
+ // Video scopes
1810
+ VIDEO_LIST: "video.list",
1811
+ VIDEO_PUBLISH: "video.publish",
1812
+ VIDEO_UPLOAD: "video.upload"
1813
+ };
1814
+ var TIKTOK_DEFAULT_SCOPES = [
1815
+ TikTokScopes.USER_INFO_BASIC,
1816
+ TikTokScopes.USER_INFO_PROFILE,
1817
+ TikTokScopes.USER_INFO_STATS,
1818
+ TikTokScopes.VIDEO_LIST,
1819
+ TikTokScopes.VIDEO_PUBLISH,
1820
+ TikTokScopes.VIDEO_UPLOAD
1821
+ ];
1822
+
1823
+ // providers/tiktok/api/index.ts
1824
+ var TikTokAPI = class {
1825
+ constructor(config, accessToken) {
1826
+ this._config = config;
1827
+ this.accessToken = accessToken;
1828
+ }
1829
+ /**
1830
+ * Set access token
1831
+ */
1832
+ setAccessToken(accessToken) {
1833
+ this.accessToken = accessToken;
1834
+ }
1835
+ /**
1836
+ * Get current access token
1837
+ */
1838
+ getAccessToken() {
1839
+ return this.accessToken;
1840
+ }
1841
+ /**
1842
+ * Make authenticated API request
1843
+ */
1844
+ async request(endpoint, options = {}) {
1845
+ if (!this.accessToken) {
1846
+ throw new Error("Access token is required");
1847
+ }
1848
+ const url = `${TIKTOK_API_BASE_URL}${endpoint}`;
1849
+ const response = await fetch(url, {
1850
+ ...options,
1851
+ headers: {
1852
+ "Authorization": `Bearer ${this.accessToken}`,
1853
+ "Content-Type": "application/json",
1854
+ ...options.headers
1855
+ }
1856
+ });
1857
+ const data = await response.json();
1858
+ if (data.error && data.error.code !== "ok") {
1859
+ throw new Error(data.error.message || "TikTok API error");
1860
+ }
1861
+ return data;
1862
+ }
1863
+ /**
1864
+ * Get user info
1865
+ * @see https://developers.tiktok.com/doc/tiktok-api-v2-get-user-info
1866
+ */
1867
+ async getUserInfo(fields) {
1868
+ const defaultFields = [
1869
+ "open_id",
1870
+ "union_id",
1871
+ "avatar_url",
1872
+ "avatar_url_100",
1873
+ "avatar_large_url",
1874
+ "display_name",
1875
+ "bio_description",
1876
+ "profile_deep_link",
1877
+ "is_verified",
1878
+ "follower_count",
1879
+ "following_count",
1880
+ "likes_count",
1881
+ "video_count"
1882
+ ];
1883
+ const params = new URLSearchParams({
1884
+ fields: (fields || defaultFields).join(",")
1885
+ });
1886
+ const response = await this.request(
1887
+ `/user/info/?${params.toString()}`
1888
+ );
1889
+ return response.data.user;
1890
+ }
1891
+ /**
1892
+ * Get user's videos
1893
+ * @see https://developers.tiktok.com/doc/tiktok-api-v2-video-list
1894
+ */
1895
+ async getVideoList(options) {
1896
+ const defaultFields = [
1897
+ "id",
1898
+ "create_time",
1899
+ "cover_image_url",
1900
+ "share_url",
1901
+ "video_description",
1902
+ "duration",
1903
+ "height",
1904
+ "width",
1905
+ "title",
1906
+ "embed_html",
1907
+ "embed_link",
1908
+ "like_count",
1909
+ "comment_count",
1910
+ "share_count",
1911
+ "view_count"
1912
+ ];
1913
+ const body = {
1914
+ max_count: options?.maxCount || 20,
1915
+ cursor: options?.cursor,
1916
+ fields: options?.fields || defaultFields
1917
+ };
1918
+ const response = await this.request("/video/list/", {
1919
+ method: "POST",
1920
+ body: JSON.stringify(body)
1921
+ });
1922
+ return response.data;
1923
+ }
1924
+ /**
1925
+ * Initialize direct video upload
1926
+ * @see https://developers.tiktok.com/doc/tiktok-api-v2-post-publish-video-init
1927
+ */
1928
+ async initVideoUpload(options) {
1929
+ const response = await this.request(
1930
+ "/post/publish/video/init/",
1931
+ {
1932
+ method: "POST",
1933
+ body: JSON.stringify({
1934
+ source_info: options.sourceInfo,
1935
+ post_info: options.postInfo
1936
+ })
1937
+ }
1938
+ );
1939
+ return response.data;
1940
+ }
1941
+ /**
1942
+ * Get publish status
1943
+ * @see https://developers.tiktok.com/doc/tiktok-api-v2-post-publish-status-fetch
1944
+ */
1945
+ async getPublishStatus(publishId) {
1946
+ const response = await this.request(
1947
+ "/post/publish/status/fetch/",
1948
+ {
1949
+ method: "POST",
1950
+ body: JSON.stringify({ publish_id: publishId })
1951
+ }
1952
+ );
1953
+ return response.data;
1954
+ }
1955
+ /**
1956
+ * Get account insights (aggregated from user info and recent videos)
1957
+ * TikTok doesn't have a dedicated insights API like Instagram/Facebook,
1958
+ * so we compute metrics from user info and video stats
1959
+ */
1960
+ async getAccountInsights() {
1961
+ const userInfo = await this.getUserInfo();
1962
+ const videoList = await this.getVideoList({ maxCount: 50 });
1963
+ let totalViews = 0;
1964
+ let totalLikes = 0;
1965
+ let totalComments = 0;
1966
+ let totalShares = 0;
1967
+ for (const video of videoList.videos) {
1968
+ totalViews += video.view_count || 0;
1969
+ totalLikes += video.like_count || 0;
1970
+ totalComments += video.comment_count || 0;
1971
+ totalShares += video.share_count || 0;
1972
+ }
1973
+ const totalEngagements = totalLikes + totalComments + totalShares;
1974
+ const averageEngagementRate = totalViews > 0 ? totalEngagements / totalViews * 100 : 0;
1975
+ return {
1976
+ followerCount: userInfo.follower_count || 0,
1977
+ followingCount: userInfo.following_count || 0,
1978
+ likesCount: userInfo.likes_count || 0,
1979
+ videoCount: userInfo.video_count || 0,
1980
+ totalViews,
1981
+ totalComments,
1982
+ totalShares,
1983
+ averageEngagementRate: Math.round(averageEngagementRate * 100) / 100
1984
+ // Round to 2 decimals
1985
+ };
1986
+ }
1987
+ /**
1988
+ * Get insights for a specific video
1989
+ */
1990
+ async getVideoInsights(videoId) {
1991
+ const videoList = await this.getVideoList({ maxCount: 50 });
1992
+ const video = videoList.videos.find((v) => v.id === videoId);
1993
+ if (!video) {
1994
+ return null;
1995
+ }
1996
+ const viewCount = video.view_count || 0;
1997
+ const likeCount = video.like_count || 0;
1998
+ const commentCount = video.comment_count || 0;
1999
+ const shareCount = video.share_count || 0;
2000
+ const engagementRate = viewCount > 0 ? (likeCount + commentCount + shareCount) / viewCount * 100 : 0;
2001
+ return {
2002
+ videoId: video.id,
2003
+ viewCount,
2004
+ likeCount,
2005
+ commentCount,
2006
+ shareCount,
2007
+ engagementRate: Math.round(engagementRate * 100) / 100,
2008
+ createTime: video.create_time
2009
+ };
2010
+ }
2011
+ /**
2012
+ * Get insights for multiple videos
2013
+ */
2014
+ async getVideosInsights(maxCount = 20) {
2015
+ const videoList = await this.getVideoList({ maxCount });
2016
+ return videoList.videos.map((video) => {
2017
+ const viewCount = video.view_count || 0;
2018
+ const likeCount = video.like_count || 0;
2019
+ const commentCount = video.comment_count || 0;
2020
+ const shareCount = video.share_count || 0;
2021
+ const engagementRate = viewCount > 0 ? (likeCount + commentCount + shareCount) / viewCount * 100 : 0;
2022
+ return {
2023
+ videoId: video.id,
2024
+ viewCount,
2025
+ likeCount,
2026
+ commentCount,
2027
+ shareCount,
2028
+ engagementRate: Math.round(engagementRate * 100) / 100,
2029
+ createTime: video.create_time
2030
+ };
2031
+ });
2032
+ }
2033
+ /**
2034
+ * Get formatted insights (unified format similar to Instagram/Facebook)
2035
+ * This allows for easier aggregation across platforms
2036
+ */
2037
+ async getInsights(params) {
2038
+ const accountInsights = await this.getAccountInsights();
2039
+ const now = /* @__PURE__ */ new Date();
2040
+ const endTime = now.toISOString();
2041
+ const metricsMap = {
2042
+ followers: accountInsights.followerCount,
2043
+ following: accountInsights.followingCount,
2044
+ likes: accountInsights.likesCount,
2045
+ video_count: accountInsights.videoCount,
2046
+ views: accountInsights.totalViews,
2047
+ comments: accountInsights.totalComments,
2048
+ shares: accountInsights.totalShares,
2049
+ engagement_rate: accountInsights.averageEngagementRate
2050
+ };
2051
+ const data = params.metric.map((metricName) => ({
2052
+ name: metricName,
2053
+ period: "lifetime",
2054
+ values: [{
2055
+ value: metricsMap[metricName] || 0,
2056
+ end_time: endTime
2057
+ }],
2058
+ title: metricName.charAt(0).toUpperCase() + metricName.slice(1).replace(/_/g, " ")
2059
+ }));
2060
+ return { data };
2061
+ }
2062
+ };
2063
+
2064
+ // providers/tiktok/auth/index.ts
2065
+ var TikTokAuth = class {
2066
+ constructor(config) {
2067
+ this.config = config;
2068
+ }
2069
+ /**
2070
+ * Build authorization URL for OAuth flow
2071
+ */
2072
+ getAuthorizationUrl(options) {
2073
+ const params = new URLSearchParams({
2074
+ client_key: this.config.clientKey,
2075
+ redirect_uri: options?.redirectUri || this.config.redirectUri || "",
2076
+ response_type: "code",
2077
+ scope: (options?.scopes || this.config.scopes || TIKTOK_DEFAULT_SCOPES).join(",")
2078
+ });
2079
+ if (options?.state) {
2080
+ params.set("state", options.state);
2081
+ }
2082
+ return `${TIKTOK_OAUTH_AUTHORIZATION_URL}?${params.toString()}`;
2083
+ }
2084
+ /**
2085
+ * Exchange authorization code for access token
2086
+ * NOTE: This should be done server-side to protect client_secret
2087
+ */
2088
+ async exchangeCodeForToken(code, redirectUri) {
2089
+ if (!this.config.clientSecret) {
2090
+ throw new Error("Client secret is required for token exchange");
2091
+ }
2092
+ const response = await fetch(TIKTOK_OAUTH_TOKEN_URL, {
2093
+ method: "POST",
2094
+ headers: {
2095
+ "Content-Type": "application/x-www-form-urlencoded"
2096
+ },
2097
+ body: new URLSearchParams({
2098
+ client_key: this.config.clientKey,
2099
+ client_secret: this.config.clientSecret,
2100
+ code,
2101
+ grant_type: "authorization_code",
2102
+ redirect_uri: redirectUri || this.config.redirectUri || ""
2103
+ }).toString()
2104
+ });
2105
+ if (!response.ok) {
2106
+ const error = await response.json();
2107
+ throw new Error(error.error?.message || "Failed to exchange code for token");
2108
+ }
2109
+ return response.json();
2110
+ }
2111
+ /**
2112
+ * Refresh access token using refresh token
2113
+ * NOTE: This should be done server-side to protect client_secret
2114
+ */
2115
+ async refreshToken(refreshToken) {
2116
+ if (!this.config.clientSecret) {
2117
+ throw new Error("Client secret is required for token refresh");
2118
+ }
2119
+ const response = await fetch(TIKTOK_OAUTH_TOKEN_URL, {
2120
+ method: "POST",
2121
+ headers: {
2122
+ "Content-Type": "application/x-www-form-urlencoded"
2123
+ },
2124
+ body: new URLSearchParams({
2125
+ client_key: this.config.clientKey,
2126
+ client_secret: this.config.clientSecret,
2127
+ grant_type: "refresh_token",
2128
+ refresh_token: refreshToken
2129
+ }).toString()
2130
+ });
2131
+ if (!response.ok) {
2132
+ const error = await response.json();
2133
+ throw new Error(error.error?.message || "Failed to refresh token");
2134
+ }
2135
+ return response.json();
2136
+ }
2137
+ /**
2138
+ * Revoke access token
2139
+ */
2140
+ async revokeToken(accessToken) {
2141
+ const response = await fetch(TIKTOK_OAUTH_REVOKE_URL, {
2142
+ method: "POST",
2143
+ headers: {
2144
+ "Content-Type": "application/x-www-form-urlencoded"
2145
+ },
2146
+ body: new URLSearchParams({
2147
+ client_key: this.config.clientKey,
2148
+ token: accessToken
2149
+ }).toString()
2150
+ });
2151
+ if (!response.ok) {
2152
+ const error = await response.json();
2153
+ throw new Error(error.error?.message || "Failed to revoke token");
2154
+ }
2155
+ }
2156
+ };
2157
+
2158
+ // providers/tiktok/TikTokProvider.ts
2159
+ var TikTokProvider = class {
2160
+ constructor(config) {
2161
+ this.name = "tiktok";
2162
+ this.config = config;
2163
+ this.auth = new TikTokAuth(config);
2164
+ this.api = new TikTokAPI(config);
2165
+ }
2166
+ /**
2167
+ * Create new API instance with access token
2168
+ */
2169
+ createAPIClient(accessToken) {
2170
+ return new TikTokAPI(this.config, accessToken);
2171
+ }
2172
+ /**
2173
+ * Update access token of existing API client
2174
+ */
2175
+ setAccessToken(accessToken) {
2176
+ this.api.setAccessToken(accessToken);
2177
+ }
2178
+ /**
2179
+ * Get current access token
2180
+ */
2181
+ getAccessToken() {
2182
+ return this.api.getAccessToken();
2183
+ }
2184
+ };
2185
+
2186
+ export { APIError, AuthError, BaseError, FACEBOOK_GRAPH_API_BASE_URL, FACEBOOK_GRAPH_API_VERSION, FACEBOOK_OAUTH_AUTHORIZATION_URL, FacebookAPI, FacebookAuth, FacebookProvider, FacebookScopesBusiness, FacebookScopesUser, HttpClient, INSIGHTS_PERIODS, INSTAGRAM_ACCOUNT_METRICS, INSTAGRAM_DEMOGRAPHICS_METRICS, INSTAGRAM_MEDIA_METRICS_COMMON, INSTAGRAM_REELS_METRICS, INSTAGRAM_STORIES_METRICS, InstagramAPI, InstagramAuth, InstagramProvider, InstagramScopes, METRIC_PRESETS, PAGE_METRICS_LIFETIME, PAGE_METRICS_PERIODIC, PHOTO_METRICS, POST_METRICS, REELS_METRICS, REELS_METRICS_EXTENDED, TIKTOK_API_BASE_URL, TIKTOK_API_VERSION, TIKTOK_DEFAULT_SCOPES, TIKTOK_OAUTH_AUTHORIZATION_URL, TIKTOK_OAUTH_REVOKE_URL, TIKTOK_OAUTH_TOKEN_URL, TikTokAPI, TikTokAuth, TikTokProvider, TikTokScopes, VIDEO_METRICS };
8
2187
  //# sourceMappingURL=index.mjs.map
9
2188
  //# sourceMappingURL=index.mjs.map