@olastudio/social-media-sdk 0.1.0 → 0.1.1

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