instagram-graph-api-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1745 @@
1
+ 'use strict';
2
+
3
+ var axios = require('axios');
4
+ var crypto = require('crypto');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var axios__default = /*#__PURE__*/_interopDefault(axios);
9
+ var crypto__default = /*#__PURE__*/_interopDefault(crypto);
10
+
11
+ // src/http.ts
12
+
13
+ // src/endpoints.ts
14
+ var INSTAGRAM_BASE_URL = "https://graph.instagram.com";
15
+ function buildUrl(version, path) {
16
+ return `${INSTAGRAM_BASE_URL}/${version}${path}`;
17
+ }
18
+ var USER_ENDPOINTS = {
19
+ /** Get user profile: GET /{user-id} */
20
+ PROFILE: (userId) => `/${userId}`,
21
+ /** Get user media: GET /{user-id}/media */
22
+ MEDIA: (userId) => `/${userId}/media`,
23
+ /** Get user stories: GET /{user-id}/stories */
24
+ STORIES: (userId) => `/${userId}/stories`,
25
+ /** Get user insights: GET /{user-id}/insights */
26
+ INSIGHTS: (userId) => `/${userId}/insights`,
27
+ /** Get live media: GET /{user-id}/live_media */
28
+ LIVE_MEDIA: (userId) => `/${userId}/live_media`,
29
+ /** Get content publishing limit: GET /{user-id}/content_publishing_limit */
30
+ CONTENT_PUBLISHING_LIMIT: (userId) => `/${userId}/content_publishing_limit`,
31
+ /** Get business discovery: GET /{user-id}?fields=business_discovery.username(...) */
32
+ BUSINESS_DISCOVERY: (userId) => `/${userId}`,
33
+ /** Get mentioned media: GET /{user-id}/mentioned_media */
34
+ MENTIONED_MEDIA: (userId) => `/${userId}/mentioned_media`,
35
+ /** Get mentioned comment: GET /{user-id}/mentioned_comment */
36
+ MENTIONED_COMMENT: (userId) => `/${userId}/mentioned_comment`,
37
+ /** Get tags: GET /{user-id}/tags */
38
+ TAGS: (userId) => `/${userId}/tags`,
39
+ /** Get recently searched hashtags: GET /{user-id}/recently_searched_hashtags */
40
+ RECENTLY_SEARCHED_HASHTAGS: (userId) => `/${userId}/recently_searched_hashtags`,
41
+ /** Get available catalogs: GET /{user-id}/available_catalogs */
42
+ AVAILABLE_CATALOGS: (userId) => `/${userId}/available_catalogs`,
43
+ /** Search catalog products: GET /{user-id}/catalog_product_search */
44
+ CATALOG_PRODUCT_SEARCH: (userId) => `/${userId}/catalog_product_search`
45
+ };
46
+ var MEDIA_ENDPOINTS = {
47
+ /** Get media by ID: GET /{media-id} */
48
+ GET: (mediaId) => `/${mediaId}`,
49
+ /** Get media children (carousel): GET /{media-id}/children */
50
+ CHILDREN: (mediaId) => `/${mediaId}/children`,
51
+ /** Get media comments: GET /{media-id}/comments */
52
+ COMMENTS: (mediaId) => `/${mediaId}/comments`,
53
+ /** Get media insights: GET /{media-id}/insights */
54
+ INSIGHTS: (mediaId) => `/${mediaId}/insights`,
55
+ /** Get media collaborators: GET /{media-id}/collaborators */
56
+ COLLABORATORS: (mediaId) => `/${mediaId}/collaborators`,
57
+ /** Get product tags: GET /{media-id}/product_tags */
58
+ PRODUCT_TAGS: (mediaId) => `/${mediaId}/product_tags`
59
+ };
60
+ var PUBLISHING_ENDPOINTS = {
61
+ /** Create media container: POST /{user-id}/media */
62
+ CREATE_CONTAINER: (userId) => `/${userId}/media`,
63
+ /** Publish media: POST /{user-id}/media_publish */
64
+ PUBLISH: (userId) => `/${userId}/media_publish`,
65
+ /** Get container status: GET /{container-id} */
66
+ CONTAINER_STATUS: (containerId) => `/${containerId}`
67
+ };
68
+ var MESSAGING_ENDPOINTS = {
69
+ /** Send message: POST /{user-id}/messages */
70
+ SEND: (userId) => `/${userId}/messages`,
71
+ /** Get conversations: GET /{user-id}/conversations */
72
+ CONVERSATIONS: (userId) => `/${userId}/conversations`,
73
+ /** Get conversation by ID: GET /{conversation-id} */
74
+ CONVERSATION: (conversationId) => `/${conversationId}`,
75
+ /** Get message by ID: GET /{message-id} */
76
+ MESSAGE: (messageId) => `/${messageId}`
77
+ };
78
+ var WELCOME_FLOW_ENDPOINTS = {
79
+ /** Get/Create/Update flows: /{user-id}/welcome_message_flows */
80
+ FLOWS: (userId) => `/${userId}/welcome_message_flows`
81
+ };
82
+ var MESSENGER_PROFILE_ENDPOINTS = {
83
+ /** Get/Set/Delete profile: /{user-id}/messenger_profile */
84
+ PROFILE: (userId) => `/${userId}/messenger_profile`
85
+ };
86
+ var COMMENT_ENDPOINTS = {
87
+ /** Get comment: GET /{comment-id} */
88
+ GET: (commentId) => `/${commentId}`,
89
+ /** Get replies: GET /{comment-id}/replies */
90
+ REPLIES: (commentId) => `/${commentId}/replies`,
91
+ /** Reply to comment: POST /{comment-id}/replies */
92
+ REPLY: (commentId) => `/${commentId}/replies`,
93
+ /** Hide/Unhide comment: POST /{comment-id} */
94
+ UPDATE: (commentId) => `/${commentId}`,
95
+ /** Delete comment: DELETE /{comment-id} */
96
+ DELETE: (commentId) => `/${commentId}`
97
+ };
98
+ var HASHTAG_ENDPOINTS = {
99
+ /** Search hashtag: GET /ig_hashtag_search */
100
+ SEARCH: "/ig_hashtag_search",
101
+ /** Get hashtag: GET /{hashtag-id} */
102
+ GET: (hashtagId) => `/${hashtagId}`,
103
+ /** Get recent media: GET /{hashtag-id}/recent_media */
104
+ RECENT_MEDIA: (hashtagId) => `/${hashtagId}/recent_media`,
105
+ /** Get top media: GET /{hashtag-id}/top_media */
106
+ TOP_MEDIA: (hashtagId) => `/${hashtagId}/top_media`
107
+ };
108
+ var AUTH_ENDPOINTS = {
109
+ /** Get current user: GET /me */
110
+ ME: "/me",
111
+ /** Refresh token: GET /refresh_access_token */
112
+ REFRESH_TOKEN: "/refresh_access_token",
113
+ /** Exchange token: GET /access_token */
114
+ ACCESS_TOKEN: "/access_token"
115
+ };
116
+ var OAUTH_ENDPOINTS = {
117
+ /** Authorization URL - redirect users here to start OAuth flow */
118
+ AUTHORIZE: "https://www.instagram.com/oauth/authorize",
119
+ /** Short-lived token exchange: POST with form data */
120
+ TOKEN: "https://api.instagram.com/oauth/access_token",
121
+ /** Long-lived token exchange: GET with query params */
122
+ LONG_LIVED_TOKEN: "https://graph.instagram.com/access_token"
123
+ };
124
+ var OEMBED_ENDPOINTS = {
125
+ /** Get oEmbed: GET /instagram_oembed */
126
+ GET: "/instagram_oembed"
127
+ };
128
+
129
+ // src/errors.ts
130
+ var InstagramAPIError = class _InstagramAPIError extends Error {
131
+ constructor(message, code, type, subcode, fbTraceId) {
132
+ super(message);
133
+ this.name = "InstagramAPIError";
134
+ this.code = code;
135
+ this.type = type;
136
+ this.subcode = subcode;
137
+ this.fbTraceId = fbTraceId;
138
+ if (Error.captureStackTrace) {
139
+ Error.captureStackTrace(this, _InstagramAPIError);
140
+ }
141
+ }
142
+ /**
143
+ * Create an InstagramAPIError from an API response
144
+ */
145
+ static fromResponse(response) {
146
+ const { error } = response;
147
+ if (error.code === 190) {
148
+ return new AuthenticationError(error.message, error.code, error.fbtrace_id);
149
+ }
150
+ if (error.code === 4 || error.code === 17 || error.code === 32) {
151
+ return new RateLimitError(error.message, error.code, error.fbtrace_id);
152
+ }
153
+ if (error.code === 100) {
154
+ return new ValidationError(error.message, error.code, error.error_subcode, error.fbtrace_id);
155
+ }
156
+ return new _InstagramAPIError(
157
+ error.message,
158
+ error.code,
159
+ error.type,
160
+ error.error_subcode,
161
+ error.fbtrace_id
162
+ );
163
+ }
164
+ };
165
+ var AuthenticationError = class extends InstagramAPIError {
166
+ constructor(message, code = 190, fbTraceId) {
167
+ super(message, code, "OAuthException", void 0, fbTraceId);
168
+ this.name = "AuthenticationError";
169
+ }
170
+ };
171
+ var RateLimitError = class extends InstagramAPIError {
172
+ constructor(message, code = 4, fbTraceId, retryAfter) {
173
+ super(message, code, "RateLimitException", void 0, fbTraceId);
174
+ this.name = "RateLimitError";
175
+ this.retryAfter = retryAfter;
176
+ }
177
+ };
178
+ var ValidationError = class extends InstagramAPIError {
179
+ constructor(message, code = 100, subcode, fbTraceId) {
180
+ super(message, code, "ValidationException", subcode, fbTraceId);
181
+ this.name = "ValidationError";
182
+ }
183
+ };
184
+ var NetworkError = class _NetworkError extends Error {
185
+ constructor(message, originalError) {
186
+ super(message);
187
+ this.name = "NetworkError";
188
+ this.originalError = originalError;
189
+ if (Error.captureStackTrace) {
190
+ Error.captureStackTrace(this, _NetworkError);
191
+ }
192
+ }
193
+ };
194
+ function isInstagramAPIError(error) {
195
+ return error instanceof InstagramAPIError;
196
+ }
197
+ function isAuthenticationError(error) {
198
+ return error instanceof AuthenticationError;
199
+ }
200
+ function isRateLimitError(error) {
201
+ return error instanceof RateLimitError;
202
+ }
203
+ function isValidationError(error) {
204
+ return error instanceof ValidationError;
205
+ }
206
+
207
+ // src/http.ts
208
+ var HttpClient = class {
209
+ constructor(config) {
210
+ this.config = config;
211
+ this.client = axios__default.default.create({
212
+ baseURL: `${INSTAGRAM_BASE_URL}/${config.apiVersion}`,
213
+ timeout: config.timeout ?? 3e4,
214
+ headers: {
215
+ "Content-Type": "application/json",
216
+ ...config.headers
217
+ }
218
+ });
219
+ this.setupInterceptors();
220
+ }
221
+ /**
222
+ * Set up request and response interceptors
223
+ */
224
+ setupInterceptors() {
225
+ this.client.interceptors.request.use(
226
+ (config) => {
227
+ if (config.params) {
228
+ config.params.access_token = this.config.accessToken;
229
+ } else {
230
+ config.params = { access_token: this.config.accessToken };
231
+ }
232
+ return config;
233
+ },
234
+ (error) => Promise.reject(error)
235
+ );
236
+ this.client.interceptors.response.use(
237
+ (response) => response,
238
+ (error) => {
239
+ if (!error.response) {
240
+ throw new NetworkError(
241
+ error.message || "Network error occurred",
242
+ error
243
+ );
244
+ }
245
+ const response = error.response;
246
+ if (response.data?.error) {
247
+ throw InstagramAPIError.fromResponse(response.data);
248
+ }
249
+ if (response.status === 429) {
250
+ const retryAfter = parseInt(response.headers["retry-after"] || "60", 10);
251
+ throw new RateLimitError(
252
+ "Rate limit exceeded",
253
+ 4,
254
+ response.headers["x-fb-trace-id"],
255
+ retryAfter
256
+ );
257
+ }
258
+ throw new InstagramAPIError(
259
+ response.data?.message || "Unknown error occurred",
260
+ response.status,
261
+ "UnknownError"
262
+ );
263
+ }
264
+ );
265
+ }
266
+ /**
267
+ * Update access token
268
+ */
269
+ setAccessToken(accessToken) {
270
+ this.config.accessToken = accessToken;
271
+ }
272
+ /**
273
+ * GET request
274
+ */
275
+ async get(path, params, config) {
276
+ const response = await this.client.get(path, {
277
+ ...config,
278
+ params: { ...params }
279
+ });
280
+ return response.data;
281
+ }
282
+ /**
283
+ * POST request
284
+ */
285
+ async post(path, data, params, config) {
286
+ const response = await this.client.post(path, data, {
287
+ ...config,
288
+ params: { ...params }
289
+ });
290
+ return response.data;
291
+ }
292
+ /**
293
+ * DELETE request
294
+ */
295
+ async delete(path, params, config) {
296
+ const response = await this.client.delete(path, {
297
+ ...config,
298
+ params: { ...params }
299
+ });
300
+ return response.data;
301
+ }
302
+ /**
303
+ * POST with form data (for file uploads)
304
+ */
305
+ async postForm(path, data, config) {
306
+ const formData = new URLSearchParams();
307
+ Object.entries(data).forEach(([key, value]) => {
308
+ if (value !== void 0 && value !== null) {
309
+ formData.append(key, String(value));
310
+ }
311
+ });
312
+ const response = await this.client.post(path, formData, {
313
+ ...config,
314
+ headers: {
315
+ ...config?.headers,
316
+ "Content-Type": "application/x-www-form-urlencoded"
317
+ }
318
+ });
319
+ return response.data;
320
+ }
321
+ };
322
+
323
+ // src/api/auth.ts
324
+ var AuthApi = class {
325
+ constructor(http) {
326
+ this.http = http;
327
+ }
328
+ /**
329
+ * Get current user information
330
+ * @param fields - Fields to retrieve
331
+ */
332
+ async me(fields = "id,username") {
333
+ return this.http.get(AUTH_ENDPOINTS.ME, { fields });
334
+ }
335
+ /**
336
+ * Refresh a long-lived token (Instagram Login)
337
+ * Returns a new long-lived token with 60 days expiry
338
+ */
339
+ async refreshToken() {
340
+ return this.http.get(
341
+ AUTH_ENDPOINTS.REFRESH_TOKEN,
342
+ { grant_type: "ig_refresh_token" }
343
+ );
344
+ }
345
+ /**
346
+ * Exchange short-lived token for long-lived token (Instagram Login)
347
+ * @param shortLivedToken - Short-lived access token (1 hour expiry)
348
+ * @param appSecret - Instagram App Secret
349
+ */
350
+ async exchangeToken(shortLivedToken, appSecret) {
351
+ return this.http.get(
352
+ AUTH_ENDPOINTS.ACCESS_TOKEN,
353
+ {
354
+ grant_type: "ig_exchange_token",
355
+ client_secret: appSecret,
356
+ access_token: shortLivedToken
357
+ }
358
+ );
359
+ }
360
+ };
361
+
362
+ // src/api/oauth.ts
363
+ var InstagramOAuth = class {
364
+ /**
365
+ * Build the Instagram authorization URL
366
+ *
367
+ * Direct users to this URL to start the OAuth flow.
368
+ * They will be asked to grant permissions to your app.
369
+ *
370
+ * @param params - Authorization URL parameters
371
+ * @returns Full authorization URL to redirect user to
372
+ *
373
+ * @example
374
+ * ```typescript
375
+ * const url = InstagramOAuth.buildAuthorizationUrl({
376
+ * clientId: process.env.INSTAGRAM_APP_ID,
377
+ * redirectUri: `${process.env.APP_URL}/api/instagram/callback`,
378
+ * scopes: [
379
+ * 'instagram_business_basic',
380
+ * 'instagram_business_manage_messages',
381
+ * ],
382
+ * state: crypto.randomUUID(), // CSRF protection
383
+ * });
384
+ *
385
+ * // Redirect user to url
386
+ * ```
387
+ */
388
+ static buildAuthorizationUrl(params) {
389
+ const {
390
+ clientId,
391
+ redirectUri,
392
+ scopes,
393
+ state,
394
+ responseType = "code",
395
+ forceReauth
396
+ } = params;
397
+ const queryParams = new URLSearchParams({
398
+ client_id: clientId,
399
+ redirect_uri: redirectUri,
400
+ scope: scopes.join(","),
401
+ response_type: responseType
402
+ });
403
+ if (state) {
404
+ queryParams.set("state", state);
405
+ }
406
+ if (forceReauth) {
407
+ queryParams.set("force_reauth", "true");
408
+ }
409
+ return `${OAUTH_ENDPOINTS.AUTHORIZE}?${queryParams.toString()}`;
410
+ }
411
+ /**
412
+ * Parse OAuth callback parameters from URL
413
+ *
414
+ * Use this to extract code, state, and error info from the callback URL.
415
+ *
416
+ * @param url - The callback URL (or just the search params)
417
+ * @returns Parsed callback parameters
418
+ *
419
+ * @example
420
+ * ```typescript
421
+ * const params = InstagramOAuth.parseCallback(request.url);
422
+ *
423
+ * if (params.error) {
424
+ * // User denied access
425
+ * console.log(params.error_description);
426
+ * } else {
427
+ * // Exchange code for token
428
+ * const tokens = await InstagramOAuth.exchangeCodeForToken({
429
+ * code: params.code!,
430
+ * ...
431
+ * });
432
+ * }
433
+ * ```
434
+ */
435
+ static parseCallback(url) {
436
+ const urlObj = new URL(url, "https://placeholder.com");
437
+ const params = urlObj.searchParams;
438
+ let code = params.get("code") || void 0;
439
+ if (code) {
440
+ code = code.replace(/#_$/, "");
441
+ }
442
+ return {
443
+ code,
444
+ state: params.get("state") || void 0,
445
+ error: params.get("error") || void 0,
446
+ error_reason: params.get("error_reason") || void 0,
447
+ error_description: params.get("error_description") || void 0
448
+ };
449
+ }
450
+ /**
451
+ * Exchange authorization code for tokens
452
+ *
453
+ * This is the recommended method - it handles the full flow:
454
+ * 1. Exchange code for short-lived token (1 hour)
455
+ * 2. Exchange short-lived for long-lived token (60 days)
456
+ *
457
+ * @param params - Exchange parameters
458
+ * @returns Long-lived token response with user ID
459
+ * @throws Error if exchange fails
460
+ *
461
+ * @example
462
+ * ```typescript
463
+ * const tokens = await InstagramOAuth.exchangeCodeForToken({
464
+ * clientId: process.env.INSTAGRAM_APP_ID!,
465
+ * clientSecret: process.env.INSTAGRAM_APP_SECRET!,
466
+ * code: codeFromCallback,
467
+ * redirectUri: `${process.env.APP_URL}/api/instagram/callback`,
468
+ * });
469
+ *
470
+ * // Store tokens.access_token and tokens.user_id in your database
471
+ * // Token expires in tokens.expires_in seconds (~60 days)
472
+ * ```
473
+ */
474
+ static async exchangeCodeForToken(params) {
475
+ const shortLived = await this.getShortLivedToken(params);
476
+ const longLived = await this.getLongLivedToken({
477
+ clientSecret: params.clientSecret,
478
+ accessToken: shortLived.access_token
479
+ });
480
+ return {
481
+ access_token: longLived.access_token,
482
+ token_type: longLived.token_type,
483
+ expires_in: longLived.expires_in,
484
+ user_id: shortLived.user_id
485
+ };
486
+ }
487
+ /**
488
+ * Get short-lived token from authorization code
489
+ *
490
+ * Use this if you need more control over the token exchange process.
491
+ * The short-lived token is valid for 1 hour.
492
+ *
493
+ * @param params - Exchange parameters
494
+ * @returns Short-lived token response
495
+ * @throws Error if exchange fails
496
+ */
497
+ static async getShortLivedToken(params) {
498
+ const { clientId, clientSecret, code, redirectUri } = params;
499
+ const cleanCode = code.replace(/#_$/, "");
500
+ const formData = new FormData();
501
+ formData.append("client_id", clientId);
502
+ formData.append("client_secret", clientSecret);
503
+ formData.append("grant_type", "authorization_code");
504
+ formData.append("redirect_uri", redirectUri);
505
+ formData.append("code", cleanCode);
506
+ const response = await fetch(OAUTH_ENDPOINTS.TOKEN, {
507
+ method: "POST",
508
+ body: formData
509
+ });
510
+ const responseText = await response.text();
511
+ if (!response.ok) {
512
+ let error;
513
+ try {
514
+ error = JSON.parse(responseText);
515
+ } catch {
516
+ error = { error_message: responseText };
517
+ }
518
+ throw new Error(
519
+ error.error_message || error.error?.message || "Failed to exchange authorization code for token"
520
+ );
521
+ }
522
+ const data = JSON.parse(responseText);
523
+ if (data.data && Array.isArray(data.data) && data.data.length > 0) {
524
+ return {
525
+ access_token: data.data[0].access_token,
526
+ user_id: data.data[0].user_id,
527
+ permissions: data.data[0].permissions || ""
528
+ };
529
+ } else if (data.access_token) {
530
+ return {
531
+ access_token: data.access_token,
532
+ user_id: data.user_id,
533
+ permissions: data.permissions || ""
534
+ };
535
+ }
536
+ throw new Error("Unexpected token response format");
537
+ }
538
+ /**
539
+ * Exchange short-lived token for long-lived token
540
+ *
541
+ * Long-lived tokens are valid for 60 days and can be refreshed.
542
+ *
543
+ * @param params - Exchange parameters
544
+ * @returns Long-lived token response
545
+ * @throws Error if exchange fails
546
+ */
547
+ static async getLongLivedToken(params) {
548
+ const { clientSecret, accessToken } = params;
549
+ const queryParams = new URLSearchParams({
550
+ grant_type: "ig_exchange_token",
551
+ client_secret: clientSecret,
552
+ access_token: accessToken
553
+ });
554
+ const response = await fetch(
555
+ `${OAUTH_ENDPOINTS.LONG_LIVED_TOKEN}?${queryParams.toString()}`
556
+ );
557
+ const responseText = await response.text();
558
+ if (!response.ok) {
559
+ let error;
560
+ try {
561
+ error = JSON.parse(responseText);
562
+ } catch {
563
+ error = { error: { message: responseText } };
564
+ }
565
+ throw new Error(
566
+ error.error?.message || "Failed to exchange for long-lived token"
567
+ );
568
+ }
569
+ return JSON.parse(responseText);
570
+ }
571
+ /**
572
+ * Get the default scopes for Instagram Business Login
573
+ *
574
+ * @returns Array of commonly used scopes
575
+ */
576
+ static getDefaultScopes() {
577
+ return [
578
+ "instagram_business_basic",
579
+ "instagram_business_manage_messages",
580
+ "instagram_business_manage_comments",
581
+ "instagram_business_content_publish"
582
+ ];
583
+ }
584
+ /**
585
+ * Get all available Instagram Business Login scopes
586
+ *
587
+ * @returns Array of all available scopes
588
+ */
589
+ static getAllScopes() {
590
+ return [
591
+ "instagram_business_basic",
592
+ "instagram_business_manage_messages",
593
+ "instagram_business_manage_comments",
594
+ "instagram_business_content_publish",
595
+ "instagram_business_manage_insights"
596
+ ];
597
+ }
598
+ };
599
+
600
+ // src/types/common.ts
601
+ function formatFields(fields) {
602
+ if (!fields) return void 0;
603
+ if (Array.isArray(fields)) return fields.join(",");
604
+ return fields;
605
+ }
606
+
607
+ // src/api/users.ts
608
+ var UsersApi = class {
609
+ constructor(http, userId) {
610
+ this.http = http;
611
+ this.userId = userId;
612
+ }
613
+ /**
614
+ * Get user profile information
615
+ * @param options - Fields to retrieve
616
+ */
617
+ async getProfile(options) {
618
+ const fields = options?.fields?.join(",") || "id,username,account_type";
619
+ return this.http.get(USER_ENDPOINTS.PROFILE(this.userId), { fields });
620
+ }
621
+ /**
622
+ * Get user's media
623
+ * @param options - Pagination and fields options
624
+ */
625
+ async getMedia(options) {
626
+ return this.http.get(
627
+ USER_ENDPOINTS.MEDIA(this.userId),
628
+ {
629
+ fields: formatFields(options?.fields) || "id,caption,media_type,media_url,permalink,timestamp",
630
+ limit: options?.limit,
631
+ after: options?.after,
632
+ before: options?.before
633
+ }
634
+ );
635
+ }
636
+ /**
637
+ * Get user's stories
638
+ */
639
+ async getStories() {
640
+ return this.http.get(
641
+ USER_ENDPOINTS.STORIES(this.userId),
642
+ { fields: "id,media_type,media_url,timestamp" }
643
+ );
644
+ }
645
+ /**
646
+ * Get user's live media
647
+ */
648
+ async getLiveMedia() {
649
+ return this.http.get(
650
+ USER_ENDPOINTS.LIVE_MEDIA(this.userId),
651
+ { fields: "id,media_type,timestamp" }
652
+ );
653
+ }
654
+ /**
655
+ * Get content publishing limit (quota usage)
656
+ */
657
+ async getContentPublishingLimit() {
658
+ return this.http.get(
659
+ USER_ENDPOINTS.CONTENT_PUBLISHING_LIMIT(this.userId),
660
+ { fields: "quota_usage,config" }
661
+ );
662
+ }
663
+ /**
664
+ * Discover another business account by username
665
+ * @param options - Username and fields to retrieve
666
+ */
667
+ async getBusinessDiscovery(options) {
668
+ const fields = options.fields?.join(",") || "id,username,followers_count,media_count";
669
+ return this.http.get(
670
+ USER_ENDPOINTS.BUSINESS_DISCOVERY(this.userId),
671
+ {
672
+ fields: `business_discovery.username(${options.username}){${fields}}`
673
+ }
674
+ );
675
+ }
676
+ /**
677
+ * Get media where user is mentioned
678
+ */
679
+ async getMentionedMedia() {
680
+ return this.http.get(
681
+ USER_ENDPOINTS.MENTIONED_MEDIA(this.userId),
682
+ { fields: "id,caption,media_type,timestamp" }
683
+ );
684
+ }
685
+ /**
686
+ * Get comments where user is mentioned
687
+ */
688
+ async getMentionedComment() {
689
+ return this.http.get(
690
+ USER_ENDPOINTS.MENTIONED_COMMENT(this.userId),
691
+ { fields: "id,text,timestamp" }
692
+ );
693
+ }
694
+ /**
695
+ * Get media user is tagged in
696
+ */
697
+ async getTags() {
698
+ return this.http.get(
699
+ USER_ENDPOINTS.TAGS(this.userId),
700
+ { fields: "id,media_type,timestamp" }
701
+ );
702
+ }
703
+ /**
704
+ * Get recently searched hashtags
705
+ */
706
+ async getRecentlySearchedHashtags() {
707
+ return this.http.get(
708
+ USER_ENDPOINTS.RECENTLY_SEARCHED_HASHTAGS(this.userId)
709
+ );
710
+ }
711
+ /**
712
+ * Get available product catalogs
713
+ */
714
+ async getAvailableCatalogs() {
715
+ return this.http.get(
716
+ USER_ENDPOINTS.AVAILABLE_CATALOGS(this.userId)
717
+ );
718
+ }
719
+ /**
720
+ * Search products in a catalog
721
+ * @param catalogId - Catalog ID to search in
722
+ * @param query - Search query
723
+ */
724
+ async searchCatalogProducts(catalogId, query) {
725
+ return this.http.get(
726
+ USER_ENDPOINTS.CATALOG_PRODUCT_SEARCH(this.userId),
727
+ { catalog_id: catalogId, q: query }
728
+ );
729
+ }
730
+ };
731
+
732
+ // src/api/media.ts
733
+ var MediaApi = class {
734
+ constructor(http) {
735
+ this.http = http;
736
+ }
737
+ /**
738
+ * Get media by ID
739
+ * @param mediaId - Media ID
740
+ * @param options - Fields to retrieve
741
+ */
742
+ async get(mediaId, options) {
743
+ const fields = options?.fields?.join(",") || "id,caption,media_type,media_url,permalink,timestamp";
744
+ return this.http.get(MEDIA_ENDPOINTS.GET(mediaId), { fields });
745
+ }
746
+ /**
747
+ * Get carousel children
748
+ * @param mediaId - Carousel media ID
749
+ * @param options - Fields to retrieve
750
+ */
751
+ async getChildren(mediaId, options) {
752
+ const fields = formatFields(options?.fields) || "id,media_type,media_url";
753
+ return this.http.get(
754
+ MEDIA_ENDPOINTS.CHILDREN(mediaId),
755
+ { fields }
756
+ );
757
+ }
758
+ /**
759
+ * Get comments on media
760
+ * @param mediaId - Media ID
761
+ * @param options - Pagination and fields options
762
+ */
763
+ async getComments(mediaId, options) {
764
+ return this.http.get(
765
+ MEDIA_ENDPOINTS.COMMENTS(mediaId),
766
+ {
767
+ fields: formatFields(options?.fields) || "id,text,username,timestamp",
768
+ limit: options?.limit,
769
+ after: options?.after
770
+ }
771
+ );
772
+ }
773
+ /**
774
+ * Get media insights
775
+ * @param mediaId - Media ID
776
+ * @param options - Metrics to retrieve
777
+ */
778
+ async getInsights(mediaId, options) {
779
+ return this.http.get(
780
+ MEDIA_ENDPOINTS.INSIGHTS(mediaId),
781
+ { metric: options.metric.join(",") }
782
+ );
783
+ }
784
+ /**
785
+ * Get media collaborators
786
+ * @param mediaId - Media ID
787
+ */
788
+ async getCollaborators(mediaId) {
789
+ return this.http.get(
790
+ MEDIA_ENDPOINTS.COLLABORATORS(mediaId),
791
+ { fields: "id,username" }
792
+ );
793
+ }
794
+ /**
795
+ * Get product tags on media
796
+ * @param mediaId - Media ID
797
+ */
798
+ async getProductTags(mediaId) {
799
+ return this.http.get(
800
+ MEDIA_ENDPOINTS.PRODUCT_TAGS(mediaId)
801
+ );
802
+ }
803
+ };
804
+
805
+ // src/api/publishing.ts
806
+ var PublishingApi = class {
807
+ constructor(http, userId) {
808
+ this.http = http;
809
+ this.userId = userId;
810
+ }
811
+ /**
812
+ * Create an image container
813
+ * @param options - Image URL and optional caption, location, tags
814
+ */
815
+ async createImageContainer(options) {
816
+ return this.http.post(
817
+ PUBLISHING_ENDPOINTS.CREATE_CONTAINER(this.userId),
818
+ {
819
+ image_url: options.image_url,
820
+ caption: options.caption,
821
+ location_id: options.location_id,
822
+ user_tags: options.user_tags ? JSON.stringify(options.user_tags) : void 0,
823
+ collaborators: options.collaborators?.join(","),
824
+ alt_text: options.alt_text,
825
+ is_carousel_item: options.is_carousel_item
826
+ }
827
+ );
828
+ }
829
+ /**
830
+ * Create a video/reel/story container
831
+ * @param options - Video URL, media type, and optional settings
832
+ */
833
+ async createVideoContainer(options) {
834
+ return this.http.post(
835
+ PUBLISHING_ENDPOINTS.CREATE_CONTAINER(this.userId),
836
+ {
837
+ video_url: options.video_url,
838
+ media_type: options.media_type,
839
+ caption: options.caption,
840
+ location_id: options.location_id,
841
+ user_tags: options.user_tags ? JSON.stringify(options.user_tags) : void 0,
842
+ collaborators: options.collaborators?.join(","),
843
+ share_to_feed: options.share_to_feed,
844
+ is_carousel_item: options.is_carousel_item,
845
+ cover_url: options.cover_url,
846
+ thumb_offset: options.thumb_offset,
847
+ audio_name: options.audio_name
848
+ }
849
+ );
850
+ }
851
+ /**
852
+ * Create a carousel container
853
+ * @param options - Child container IDs and optional caption
854
+ */
855
+ async createCarouselContainer(options) {
856
+ return this.http.post(
857
+ PUBLISHING_ENDPOINTS.CREATE_CONTAINER(this.userId),
858
+ {
859
+ media_type: "CAROUSEL",
860
+ children: options.children.join(","),
861
+ caption: options.caption,
862
+ location_id: options.location_id,
863
+ collaborators: options.collaborators?.join(",")
864
+ }
865
+ );
866
+ }
867
+ /**
868
+ * Create a resumable upload session
869
+ * @param options - Media type and settings
870
+ */
871
+ async createResumableUpload(options) {
872
+ return this.http.post(
873
+ PUBLISHING_ENDPOINTS.CREATE_CONTAINER(this.userId),
874
+ {
875
+ media_type: options.media_type,
876
+ upload_type: "resumable",
877
+ caption: options.caption,
878
+ location_id: options.location_id,
879
+ collaborators: options.collaborators?.join(",")
880
+ }
881
+ );
882
+ }
883
+ /**
884
+ * Get container status
885
+ * @param containerId - Container ID
886
+ */
887
+ async getContainerStatus(containerId) {
888
+ return this.http.get(
889
+ PUBLISHING_ENDPOINTS.CONTAINER_STATUS(containerId),
890
+ { fields: "id,status_code,status" }
891
+ );
892
+ }
893
+ /**
894
+ * Publish a container
895
+ * @param containerId - Container ID to publish
896
+ */
897
+ async publishContainer(containerId) {
898
+ return this.http.post(
899
+ PUBLISHING_ENDPOINTS.PUBLISH(this.userId),
900
+ { creation_id: containerId }
901
+ );
902
+ }
903
+ /**
904
+ * Wait for container to be ready, then publish
905
+ * @param containerId - Container ID
906
+ * @param maxAttempts - Maximum number of status checks (default: 30)
907
+ * @param intervalMs - Interval between checks in ms (default: 2000)
908
+ */
909
+ async waitAndPublish(containerId, maxAttempts = 30, intervalMs = 2e3) {
910
+ for (let i = 0; i < maxAttempts; i++) {
911
+ const status = await this.getContainerStatus(containerId);
912
+ if (status.status_code === "FINISHED") {
913
+ return this.publishContainer(containerId);
914
+ }
915
+ if (status.status_code === "ERROR" || status.status_code === "EXPIRED") {
916
+ throw new Error(`Container failed with status: ${status.status_code}`);
917
+ }
918
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
919
+ }
920
+ throw new Error("Container did not become ready in time");
921
+ }
922
+ };
923
+
924
+ // src/api/messaging.ts
925
+ var MessagingApi = class {
926
+ constructor(http, userId) {
927
+ this.http = http;
928
+ this.userId = userId;
929
+ }
930
+ /**
931
+ * Send a text message
932
+ * @param recipientId - Instagram-scoped user ID (IGSID)
933
+ * @param text - Message text
934
+ * @param options - Optional settings (humanAgent for 7-day window)
935
+ */
936
+ async sendText(recipientId, text, options) {
937
+ const payload = {
938
+ recipient: { id: recipientId },
939
+ message: { text }
940
+ };
941
+ if (options?.humanAgent) {
942
+ payload.messaging_type = "MESSAGE_TAG";
943
+ payload.tag = "HUMAN_AGENT";
944
+ }
945
+ return this.http.post(
946
+ MESSAGING_ENDPOINTS.SEND(this.userId),
947
+ payload
948
+ );
949
+ }
950
+ /**
951
+ * Send a media message (image, video, audio)
952
+ * @param recipientId - Instagram-scoped user ID
953
+ * @param options - Media type and URL
954
+ */
955
+ async sendMedia(recipientId, options) {
956
+ return this.http.post(
957
+ MESSAGING_ENDPOINTS.SEND(this.userId),
958
+ {
959
+ recipient: { id: recipientId },
960
+ message: {
961
+ attachment: {
962
+ type: options.type,
963
+ payload: { url: options.url }
964
+ }
965
+ }
966
+ }
967
+ );
968
+ }
969
+ /**
970
+ * Send a sticker (like_heart)
971
+ * @param recipientId - Instagram-scoped user ID
972
+ */
973
+ async sendLikeHeart(recipientId) {
974
+ return this.http.post(
975
+ MESSAGING_ENDPOINTS.SEND(this.userId),
976
+ {
977
+ recipient: { id: recipientId },
978
+ message: {
979
+ attachment: {
980
+ type: "like_heart"
981
+ }
982
+ }
983
+ }
984
+ );
985
+ }
986
+ /**
987
+ * Send a generic template
988
+ * @param recipientId - Instagram-scoped user ID
989
+ * @param options - Template elements
990
+ */
991
+ async sendGenericTemplate(recipientId, options) {
992
+ return this.http.post(
993
+ MESSAGING_ENDPOINTS.SEND(this.userId),
994
+ {
995
+ recipient: { id: recipientId },
996
+ message: {
997
+ attachment: {
998
+ type: "template",
999
+ payload: {
1000
+ template_type: "generic",
1001
+ elements: options.elements
1002
+ }
1003
+ }
1004
+ }
1005
+ }
1006
+ );
1007
+ }
1008
+ /**
1009
+ * Send a button template
1010
+ * @param recipientId - Instagram-scoped user ID
1011
+ * @param options - Text and buttons
1012
+ */
1013
+ async sendButtonTemplate(recipientId, options) {
1014
+ return this.http.post(
1015
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1016
+ {
1017
+ recipient: { id: recipientId },
1018
+ message: {
1019
+ attachment: {
1020
+ type: "template",
1021
+ payload: {
1022
+ template_type: "button",
1023
+ text: options.text,
1024
+ buttons: options.buttons
1025
+ }
1026
+ }
1027
+ }
1028
+ }
1029
+ );
1030
+ }
1031
+ /**
1032
+ * Send quick replies
1033
+ * @param recipientId - Instagram-scoped user ID
1034
+ * @param options - Text and quick reply options
1035
+ */
1036
+ async sendQuickReplies(recipientId, options) {
1037
+ return this.http.post(
1038
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1039
+ {
1040
+ recipient: { id: recipientId },
1041
+ message: {
1042
+ text: options.text,
1043
+ quick_replies: options.replies
1044
+ }
1045
+ }
1046
+ );
1047
+ }
1048
+ /**
1049
+ * Send a private reply to a comment
1050
+ * @param commentId - Comment ID to reply to
1051
+ * @param message - Message text
1052
+ */
1053
+ async sendPrivateReply(commentId, message) {
1054
+ return this.http.post(
1055
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1056
+ {
1057
+ recipient: { comment_id: commentId },
1058
+ message: { text: message }
1059
+ }
1060
+ );
1061
+ }
1062
+ /**
1063
+ * Share a published post via message
1064
+ * @param recipientId - Instagram-scoped user ID
1065
+ * @param mediaId - Media ID of the post to share
1066
+ */
1067
+ async sendMediaShare(recipientId, mediaId) {
1068
+ return this.http.post(
1069
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1070
+ {
1071
+ recipient: { id: recipientId },
1072
+ message: {
1073
+ attachment: {
1074
+ type: "MEDIA_SHARE",
1075
+ payload: { id: mediaId }
1076
+ }
1077
+ }
1078
+ }
1079
+ );
1080
+ }
1081
+ /**
1082
+ * React to a message
1083
+ * @param recipientId - Instagram-scoped user ID
1084
+ * @param messageId - Message ID to react to
1085
+ * @param reaction - Reaction type
1086
+ */
1087
+ async reactToMessage(recipientId, messageId, reaction) {
1088
+ return this.http.post(
1089
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1090
+ {
1091
+ recipient: { id: recipientId },
1092
+ sender_action: "react",
1093
+ payload: {
1094
+ message_id: messageId,
1095
+ reaction
1096
+ }
1097
+ }
1098
+ );
1099
+ }
1100
+ /**
1101
+ * Remove reaction from a message
1102
+ * @param recipientId - Instagram-scoped user ID
1103
+ * @param messageId - Message ID to unreact from
1104
+ */
1105
+ async unreactToMessage(recipientId, messageId) {
1106
+ return this.http.post(
1107
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1108
+ {
1109
+ recipient: { id: recipientId },
1110
+ sender_action: "unreact",
1111
+ payload: {
1112
+ message_id: messageId
1113
+ }
1114
+ }
1115
+ );
1116
+ }
1117
+ /**
1118
+ * Send typing indicator
1119
+ * @param recipientId - Instagram-scoped user ID
1120
+ * @param typing - Whether to show typing (true) or stop (false)
1121
+ */
1122
+ async sendTypingIndicator(recipientId, typing = true) {
1123
+ return this.http.post(
1124
+ MESSAGING_ENDPOINTS.SEND(this.userId),
1125
+ {
1126
+ recipient: { id: recipientId },
1127
+ sender_action: typing ? "typing_on" : "typing_off"
1128
+ }
1129
+ );
1130
+ }
1131
+ };
1132
+
1133
+ // src/api/conversations.ts
1134
+ var ConversationsApi = class {
1135
+ constructor(http, userId) {
1136
+ this.http = http;
1137
+ this.userId = userId;
1138
+ }
1139
+ /**
1140
+ * Get list of conversations
1141
+ * @param options - Pagination and filter options
1142
+ */
1143
+ async list(options) {
1144
+ return this.http.get(
1145
+ MESSAGING_ENDPOINTS.CONVERSATIONS(this.userId),
1146
+ {
1147
+ platform: options?.platform || "instagram",
1148
+ user_id: options?.user_id,
1149
+ limit: options?.limit,
1150
+ after: options?.after,
1151
+ fields: "id,updated_time"
1152
+ }
1153
+ );
1154
+ }
1155
+ /**
1156
+ * Find conversation with a specific user
1157
+ * @param igsid - Instagram-scoped user ID
1158
+ */
1159
+ async findByUser(igsid) {
1160
+ return this.http.get(
1161
+ MESSAGING_ENDPOINTS.CONVERSATIONS(this.userId),
1162
+ {
1163
+ platform: "instagram",
1164
+ user_id: igsid,
1165
+ fields: "id,updated_time"
1166
+ }
1167
+ );
1168
+ }
1169
+ /**
1170
+ * Get messages in a conversation
1171
+ * @param conversationId - Conversation ID
1172
+ * @param fields - Fields to retrieve (default: from,to)
1173
+ */
1174
+ async getMessages(conversationId, fields = "messages{id,created_time,from,to,message}") {
1175
+ return this.http.get(
1176
+ MESSAGING_ENDPOINTS.CONVERSATION(conversationId),
1177
+ { fields }
1178
+ );
1179
+ }
1180
+ /**
1181
+ * Get a specific message
1182
+ * @param messageId - Message ID
1183
+ * @param fields - Fields to retrieve
1184
+ */
1185
+ async getMessage(messageId, fields = "id,created_time,from,to,message") {
1186
+ return this.http.get(
1187
+ MESSAGING_ENDPOINTS.MESSAGE(messageId),
1188
+ { fields }
1189
+ );
1190
+ }
1191
+ };
1192
+
1193
+ // src/api/comments.ts
1194
+ var CommentsApi = class {
1195
+ constructor(http) {
1196
+ this.http = http;
1197
+ }
1198
+ /**
1199
+ * Get comment by ID
1200
+ * @param commentId - Comment ID
1201
+ * @param fields - Fields to retrieve
1202
+ */
1203
+ async get(commentId, fields = ["id", "text", "username", "timestamp"]) {
1204
+ return this.http.get(
1205
+ COMMENT_ENDPOINTS.GET(commentId),
1206
+ { fields: fields.join(",") }
1207
+ );
1208
+ }
1209
+ /**
1210
+ * Get replies to a comment
1211
+ * @param commentId - Comment ID
1212
+ * @param options - Pagination and fields options
1213
+ */
1214
+ async getReplies(commentId, options) {
1215
+ return this.http.get(
1216
+ COMMENT_ENDPOINTS.REPLIES(commentId),
1217
+ {
1218
+ fields: options?.fields?.join(",") || "id,text,username,timestamp",
1219
+ limit: options?.limit,
1220
+ after: options?.after
1221
+ }
1222
+ );
1223
+ }
1224
+ /**
1225
+ * Reply to a comment
1226
+ * @param commentId - Comment ID to reply to
1227
+ * @param options - Reply message
1228
+ */
1229
+ async reply(commentId, options) {
1230
+ return this.http.post(
1231
+ COMMENT_ENDPOINTS.REPLY(commentId),
1232
+ { message: options.message }
1233
+ );
1234
+ }
1235
+ /**
1236
+ * Hide a comment
1237
+ * @param commentId - Comment ID
1238
+ */
1239
+ async hide(commentId) {
1240
+ return this.http.post(
1241
+ COMMENT_ENDPOINTS.UPDATE(commentId),
1242
+ { hide: true }
1243
+ );
1244
+ }
1245
+ /**
1246
+ * Unhide a comment
1247
+ * @param commentId - Comment ID
1248
+ */
1249
+ async unhide(commentId) {
1250
+ return this.http.post(
1251
+ COMMENT_ENDPOINTS.UPDATE(commentId),
1252
+ { hide: false }
1253
+ );
1254
+ }
1255
+ /**
1256
+ * Delete a comment
1257
+ * @param commentId - Comment ID
1258
+ */
1259
+ async delete(commentId) {
1260
+ return this.http.delete(COMMENT_ENDPOINTS.DELETE(commentId));
1261
+ }
1262
+ };
1263
+
1264
+ // src/api/hashtags.ts
1265
+ var HashtagsApi = class {
1266
+ constructor(http) {
1267
+ this.http = http;
1268
+ }
1269
+ /**
1270
+ * Search for a hashtag
1271
+ * @param options - User ID and search query
1272
+ */
1273
+ async search(options) {
1274
+ return this.http.get(
1275
+ HASHTAG_ENDPOINTS.SEARCH,
1276
+ {
1277
+ user_id: options.user_id,
1278
+ q: options.q
1279
+ }
1280
+ );
1281
+ }
1282
+ /**
1283
+ * Get hashtag information
1284
+ * @param hashtagId - Hashtag ID
1285
+ */
1286
+ async get(hashtagId) {
1287
+ return this.http.get(
1288
+ HASHTAG_ENDPOINTS.GET(hashtagId),
1289
+ { fields: "id,name" }
1290
+ );
1291
+ }
1292
+ /**
1293
+ * Get recent media with hashtag
1294
+ * @param hashtagId - Hashtag ID
1295
+ * @param options - User ID and pagination options
1296
+ */
1297
+ async getRecentMedia(hashtagId, options) {
1298
+ return this.http.get(
1299
+ HASHTAG_ENDPOINTS.RECENT_MEDIA(hashtagId),
1300
+ {
1301
+ user_id: options.user_id,
1302
+ fields: options.fields?.join(",") || "id,caption,media_type,permalink",
1303
+ limit: options.limit,
1304
+ after: options.after
1305
+ }
1306
+ );
1307
+ }
1308
+ /**
1309
+ * Get top media with hashtag
1310
+ * @param hashtagId - Hashtag ID
1311
+ * @param options - User ID and pagination options
1312
+ */
1313
+ async getTopMedia(hashtagId, options) {
1314
+ return this.http.get(
1315
+ HASHTAG_ENDPOINTS.TOP_MEDIA(hashtagId),
1316
+ {
1317
+ user_id: options.user_id,
1318
+ fields: options.fields?.join(",") || "id,caption,media_type,permalink",
1319
+ limit: options.limit,
1320
+ after: options.after
1321
+ }
1322
+ );
1323
+ }
1324
+ };
1325
+
1326
+ // src/api/insights.ts
1327
+ var InsightsApi = class {
1328
+ constructor(http, userId) {
1329
+ this.http = http;
1330
+ this.userId = userId;
1331
+ }
1332
+ /**
1333
+ * Get account insights
1334
+ * @param options - Metrics, period, and breakdown options
1335
+ */
1336
+ async getAccountInsights(options) {
1337
+ return this.http.get(
1338
+ USER_ENDPOINTS.INSIGHTS(this.userId),
1339
+ {
1340
+ metric: options.metric.join(","),
1341
+ period: options.period || "day",
1342
+ since: options.since,
1343
+ until: options.until,
1344
+ metric_type: options.metric_type,
1345
+ breakdown: options.breakdown
1346
+ }
1347
+ );
1348
+ }
1349
+ /**
1350
+ * Get media insights
1351
+ * @param mediaId - Media ID
1352
+ * @param options - Metrics to retrieve
1353
+ */
1354
+ async getMediaInsights(mediaId, options) {
1355
+ return this.http.get(
1356
+ MEDIA_ENDPOINTS.INSIGHTS(mediaId),
1357
+ { metric: options.metric.join(",") }
1358
+ );
1359
+ }
1360
+ };
1361
+
1362
+ // src/api/welcomeFlows.ts
1363
+ var WelcomeFlowsApi = class {
1364
+ constructor(http, userId) {
1365
+ this.http = http;
1366
+ this.userId = userId;
1367
+ }
1368
+ /**
1369
+ * Get all welcome message flows
1370
+ */
1371
+ async list() {
1372
+ return this.http.get(
1373
+ WELCOME_FLOW_ENDPOINTS.FLOWS(this.userId)
1374
+ );
1375
+ }
1376
+ /**
1377
+ * Get a specific welcome message flow
1378
+ * @param flowId - Flow ID
1379
+ */
1380
+ async get(flowId) {
1381
+ return this.http.get(
1382
+ WELCOME_FLOW_ENDPOINTS.FLOWS(this.userId),
1383
+ { flow_id: flowId }
1384
+ );
1385
+ }
1386
+ /**
1387
+ * Create a new welcome message flow
1388
+ * @param options - Flow name and screens
1389
+ */
1390
+ async create(options) {
1391
+ return this.http.post(
1392
+ WELCOME_FLOW_ENDPOINTS.FLOWS(this.userId),
1393
+ {
1394
+ name: options.name,
1395
+ screens: options.screens
1396
+ }
1397
+ );
1398
+ }
1399
+ /**
1400
+ * Update an existing welcome message flow
1401
+ * @param flowId - Flow ID
1402
+ * @param options - Updated flow name and screens
1403
+ */
1404
+ async update(flowId, options) {
1405
+ return this.http.post(
1406
+ WELCOME_FLOW_ENDPOINTS.FLOWS(this.userId),
1407
+ {
1408
+ flow_id: flowId,
1409
+ name: options.name,
1410
+ screens: options.screens
1411
+ }
1412
+ );
1413
+ }
1414
+ /**
1415
+ * Delete a welcome message flow
1416
+ * @param flowId - Flow ID
1417
+ */
1418
+ async delete(flowId) {
1419
+ return this.http.delete(
1420
+ WELCOME_FLOW_ENDPOINTS.FLOWS(this.userId),
1421
+ { flow_id: flowId }
1422
+ );
1423
+ }
1424
+ };
1425
+
1426
+ // src/api/messengerProfile.ts
1427
+ var MessengerProfileApi = class {
1428
+ constructor(http, userId) {
1429
+ this.http = http;
1430
+ this.userId = userId;
1431
+ }
1432
+ /**
1433
+ * Get current ice breakers
1434
+ */
1435
+ async getIceBreakers() {
1436
+ return this.http.get(
1437
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1438
+ { fields: "ice_breakers" }
1439
+ );
1440
+ }
1441
+ /**
1442
+ * Set ice breakers (FAQ questions)
1443
+ * @param iceBreakers - Ice breaker configurations (max 4 questions per locale)
1444
+ */
1445
+ async setIceBreakers(iceBreakers) {
1446
+ return this.http.post(
1447
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1448
+ {
1449
+ platform: "instagram",
1450
+ ice_breakers: iceBreakers
1451
+ }
1452
+ );
1453
+ }
1454
+ /**
1455
+ * Delete ice breakers
1456
+ */
1457
+ async deleteIceBreakers() {
1458
+ return this.http.delete(
1459
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1460
+ { fields: ["ice_breakers"] }
1461
+ );
1462
+ }
1463
+ /**
1464
+ * Get persistent menu
1465
+ */
1466
+ async getPersistentMenu() {
1467
+ return this.http.get(
1468
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1469
+ { fields: "persistent_menu" }
1470
+ );
1471
+ }
1472
+ /**
1473
+ * Set persistent menu
1474
+ * @param menus - Persistent menu configurations
1475
+ */
1476
+ async setPersistentMenu(menus) {
1477
+ return this.http.post(
1478
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1479
+ {
1480
+ platform: "instagram",
1481
+ persistent_menu: menus
1482
+ }
1483
+ );
1484
+ }
1485
+ /**
1486
+ * Delete persistent menu
1487
+ */
1488
+ async deletePersistentMenu() {
1489
+ return this.http.delete(
1490
+ MESSENGER_PROFILE_ENDPOINTS.PROFILE(this.userId),
1491
+ { fields: ["persistent_menu"] }
1492
+ );
1493
+ }
1494
+ };
1495
+
1496
+ // src/api/oembed.ts
1497
+ var OEmbedApi = class {
1498
+ constructor(http) {
1499
+ this.http = http;
1500
+ }
1501
+ /**
1502
+ * Get oEmbed data for an Instagram URL
1503
+ * @param options - URL and optional formatting options
1504
+ */
1505
+ async get(options) {
1506
+ return this.http.get(
1507
+ OEMBED_ENDPOINTS.GET,
1508
+ {
1509
+ url: options.url,
1510
+ maxwidth: options.maxwidth,
1511
+ hidecaption: options.hidecaption,
1512
+ omitscript: options.omitscript
1513
+ }
1514
+ );
1515
+ }
1516
+ };
1517
+
1518
+ // src/api/webhooks.ts
1519
+ var WebhooksApi = class {
1520
+ constructor(http, userId) {
1521
+ this.http = http;
1522
+ this.userId = userId;
1523
+ }
1524
+ /**
1525
+ * Subscribe your app to specific fields for this user/page.
1526
+ *
1527
+ * @param fields List of fields to subscribe to (e.g., ['messages', 'comments'])
1528
+ */
1529
+ async subscribe(fields) {
1530
+ return this.http.post(`/${this.userId}/subscribed_apps`, {
1531
+ subscribed_fields: fields.join(",")
1532
+ });
1533
+ }
1534
+ /**
1535
+ * Unsubscribe from specific fields or all fields.
1536
+ *
1537
+ * @param fields Optional list of fields to unsubscribe from. If empty, unsubscribes from all.
1538
+ */
1539
+ async unsubscribe(fields) {
1540
+ if (fields && fields.length > 0) {
1541
+ throw new Error("Partial unsubscribe not directly supported by API. Use subscribe() with the fields you want to keep.");
1542
+ }
1543
+ return this.http.delete(`/${this.userId}/subscribed_apps`);
1544
+ }
1545
+ /**
1546
+ * Get the list of fields your app is currently subscribed to.
1547
+ */
1548
+ async getSubscribedFields() {
1549
+ return this.http.get(`/${this.userId}/subscribed_apps`);
1550
+ }
1551
+ };
1552
+
1553
+ // src/client.ts
1554
+ var DEFAULT_API_VERSION = "v22.0";
1555
+ var InstagramClient = class {
1556
+ /**
1557
+ * Create a new Instagram client
1558
+ * @param config - Client configuration
1559
+ */
1560
+ constructor(config) {
1561
+ this.cachedUserId = null;
1562
+ this.config = {
1563
+ accessToken: config.accessToken,
1564
+ apiVersion: config.apiVersion || DEFAULT_API_VERSION,
1565
+ timeout: config.timeout || 3e4
1566
+ };
1567
+ this.http = new HttpClient({
1568
+ accessToken: this.config.accessToken,
1569
+ apiVersion: this.config.apiVersion,
1570
+ timeout: this.config.timeout
1571
+ });
1572
+ const userId = "me";
1573
+ this.auth = new AuthApi(this.http);
1574
+ this.users = new UsersApi(this.http, userId);
1575
+ this.media = new MediaApi(this.http);
1576
+ this.publishing = new PublishingApi(this.http, userId);
1577
+ this.messaging = new MessagingApi(this.http, userId);
1578
+ this.conversations = new ConversationsApi(this.http, userId);
1579
+ this.comments = new CommentsApi(this.http);
1580
+ this.hashtags = new HashtagsApi(this.http);
1581
+ this.insights = new InsightsApi(this.http, userId);
1582
+ this.welcomeFlows = new WelcomeFlowsApi(this.http, userId);
1583
+ this.messengerProfile = new MessengerProfileApi(this.http, userId);
1584
+ this.oembed = new OEmbedApi(this.http);
1585
+ this.webhooks = new WebhooksApi(this.http, userId);
1586
+ }
1587
+ /**
1588
+ * Get the current user ID
1589
+ * Fetches from API if not cached
1590
+ */
1591
+ async getUserId() {
1592
+ if (this.cachedUserId) {
1593
+ return this.cachedUserId;
1594
+ }
1595
+ const user = await this.auth.me("id");
1596
+ this.cachedUserId = user.id;
1597
+ return user.id;
1598
+ }
1599
+ /**
1600
+ * Update the access token
1601
+ * @param accessToken - New access token
1602
+ */
1603
+ setAccessToken(accessToken) {
1604
+ this.http.setAccessToken(accessToken);
1605
+ }
1606
+ /**
1607
+ * Get the current configuration
1608
+ */
1609
+ getConfig() {
1610
+ return { ...this.config };
1611
+ }
1612
+ /**
1613
+ * Get the API version
1614
+ */
1615
+ getApiVersion() {
1616
+ return this.config.apiVersion;
1617
+ }
1618
+ };
1619
+ var InstagramWebhooks = class {
1620
+ /**
1621
+ * Verify that the webhook request came from Meta.
1622
+ * Calculates SHA256 HMAC of the raw body using the App Secret.
1623
+ *
1624
+ * @param body Raw request body as string
1625
+ * @param signature Signature from X-Hub-Signature-256 header (e.g., "sha256=...")
1626
+ * @param appSecret Your Instagram App Secret
1627
+ * @returns true if signature is valid
1628
+ */
1629
+ static verifySignature(body, signature, appSecret) {
1630
+ if (!signature || !signature.startsWith("sha256=")) {
1631
+ return false;
1632
+ }
1633
+ const expectedSignature = "sha256=" + crypto__default.default.createHmac("sha256", appSecret).update(body).digest("hex");
1634
+ return crypto__default.default.timingSafeEqual(
1635
+ Buffer.from(signature),
1636
+ Buffer.from(expectedSignature)
1637
+ );
1638
+ }
1639
+ /**
1640
+ * Type guard and parser for webhook payloads.
1641
+ *
1642
+ * @param body Parsed JSON body
1643
+ * @returns Typed WebhookPayload or throws error
1644
+ */
1645
+ static parsePayload(body) {
1646
+ if (body?.object !== "instagram" || !Array.isArray(body?.entry)) {
1647
+ throw new Error("Invalid Instagram webhook payload");
1648
+ }
1649
+ return body;
1650
+ }
1651
+ };
1652
+
1653
+ // src/types/user.ts
1654
+ var IG_USER_FIELDS = [
1655
+ "id",
1656
+ "account_type",
1657
+ "biography",
1658
+ "followers_count",
1659
+ "follows_count",
1660
+ "media_count",
1661
+ "username",
1662
+ "name",
1663
+ "profile_picture_url",
1664
+ "website",
1665
+ "ig_id"
1666
+ ];
1667
+
1668
+ // src/types/media.ts
1669
+ var IG_MEDIA_FIELDS = [
1670
+ "id",
1671
+ "caption",
1672
+ "media_type",
1673
+ "media_product_type",
1674
+ "media_url",
1675
+ "permalink",
1676
+ "thumbnail_url",
1677
+ "timestamp",
1678
+ "username",
1679
+ "like_count",
1680
+ "comments_count",
1681
+ "is_comment_enabled",
1682
+ "is_shared_to_feed",
1683
+ "owner",
1684
+ "shortcode"
1685
+ ];
1686
+
1687
+ // src/types/comment.ts
1688
+ var IG_COMMENT_FIELDS = [
1689
+ "id",
1690
+ "text",
1691
+ "username",
1692
+ "timestamp",
1693
+ "like_count",
1694
+ "hidden",
1695
+ "from",
1696
+ "media",
1697
+ "parent_id",
1698
+ "replies"
1699
+ ];
1700
+
1701
+ exports.AUTH_ENDPOINTS = AUTH_ENDPOINTS;
1702
+ exports.AuthApi = AuthApi;
1703
+ exports.AuthenticationError = AuthenticationError;
1704
+ exports.COMMENT_ENDPOINTS = COMMENT_ENDPOINTS;
1705
+ exports.CommentsApi = CommentsApi;
1706
+ exports.ConversationsApi = ConversationsApi;
1707
+ exports.HASHTAG_ENDPOINTS = HASHTAG_ENDPOINTS;
1708
+ exports.HashtagsApi = HashtagsApi;
1709
+ exports.HttpClient = HttpClient;
1710
+ exports.IG_COMMENT_FIELDS = IG_COMMENT_FIELDS;
1711
+ exports.IG_MEDIA_FIELDS = IG_MEDIA_FIELDS;
1712
+ exports.IG_USER_FIELDS = IG_USER_FIELDS;
1713
+ exports.INSTAGRAM_BASE_URL = INSTAGRAM_BASE_URL;
1714
+ exports.InsightsApi = InsightsApi;
1715
+ exports.InstagramAPIError = InstagramAPIError;
1716
+ exports.InstagramClient = InstagramClient;
1717
+ exports.InstagramOAuth = InstagramOAuth;
1718
+ exports.InstagramWebhooks = InstagramWebhooks;
1719
+ exports.MEDIA_ENDPOINTS = MEDIA_ENDPOINTS;
1720
+ exports.MESSAGING_ENDPOINTS = MESSAGING_ENDPOINTS;
1721
+ exports.MESSENGER_PROFILE_ENDPOINTS = MESSENGER_PROFILE_ENDPOINTS;
1722
+ exports.MediaApi = MediaApi;
1723
+ exports.MessagingApi = MessagingApi;
1724
+ exports.MessengerProfileApi = MessengerProfileApi;
1725
+ exports.NetworkError = NetworkError;
1726
+ exports.OAUTH_ENDPOINTS = OAUTH_ENDPOINTS;
1727
+ exports.OEMBED_ENDPOINTS = OEMBED_ENDPOINTS;
1728
+ exports.OEmbedApi = OEmbedApi;
1729
+ exports.PUBLISHING_ENDPOINTS = PUBLISHING_ENDPOINTS;
1730
+ exports.PublishingApi = PublishingApi;
1731
+ exports.RateLimitError = RateLimitError;
1732
+ exports.USER_ENDPOINTS = USER_ENDPOINTS;
1733
+ exports.UsersApi = UsersApi;
1734
+ exports.ValidationError = ValidationError;
1735
+ exports.WELCOME_FLOW_ENDPOINTS = WELCOME_FLOW_ENDPOINTS;
1736
+ exports.WebhooksApi = WebhooksApi;
1737
+ exports.WelcomeFlowsApi = WelcomeFlowsApi;
1738
+ exports.buildUrl = buildUrl;
1739
+ exports.formatFields = formatFields;
1740
+ exports.isAuthenticationError = isAuthenticationError;
1741
+ exports.isInstagramAPIError = isInstagramAPIError;
1742
+ exports.isRateLimitError = isRateLimitError;
1743
+ exports.isValidationError = isValidationError;
1744
+ //# sourceMappingURL=index.js.map
1745
+ //# sourceMappingURL=index.js.map