naystack 1.8.12 → 1.8.14

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 (74) hide show
  1. package/README.md +189 -31
  2. package/dist/auth/index.cjs.js +158 -18
  3. package/dist/auth/index.d.mts +3 -1
  4. package/dist/auth/index.d.ts +3 -1
  5. package/dist/auth/index.esm.js +158 -18
  6. package/dist/auth/instagram/index.cjs.js +158 -18
  7. package/dist/auth/instagram/index.d.mts +6 -1
  8. package/dist/auth/instagram/index.d.ts +6 -1
  9. package/dist/auth/instagram/index.esm.js +158 -18
  10. package/dist/auth/instagram/route.cjs.js +158 -18
  11. package/dist/auth/instagram/route.d.mts +3 -1
  12. package/dist/auth/instagram/route.d.ts +3 -1
  13. package/dist/auth/instagram/route.esm.js +158 -18
  14. package/dist/auth/instagram/utils.cjs.js +2 -2
  15. package/dist/auth/instagram/utils.d.mts +13 -2
  16. package/dist/auth/instagram/utils.d.ts +13 -2
  17. package/dist/auth/instagram/utils.esm.js +2 -2
  18. package/dist/file/setup.d.mts +13 -5
  19. package/dist/file/setup.d.ts +13 -5
  20. package/dist/index.d.mts +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/socials/index.cjs.js +298 -72
  23. package/dist/socials/index.d.mts +6 -4
  24. package/dist/socials/index.d.ts +6 -4
  25. package/dist/socials/index.esm.js +296 -72
  26. package/dist/socials/instagram/getters.cjs.js +76 -25
  27. package/dist/socials/instagram/getters.d.mts +56 -6
  28. package/dist/socials/instagram/getters.d.ts +56 -6
  29. package/dist/socials/instagram/getters.esm.js +74 -25
  30. package/dist/socials/instagram/setters.cjs.js +192 -14
  31. package/dist/socials/instagram/setters.d.mts +43 -3
  32. package/dist/socials/instagram/setters.d.ts +43 -3
  33. package/dist/socials/instagram/setters.esm.js +191 -14
  34. package/dist/socials/instagram/types.d.mts +48 -11
  35. package/dist/socials/instagram/types.d.ts +48 -11
  36. package/dist/socials/instagram/utils.cjs.js +28 -15
  37. package/dist/socials/instagram/utils.d.mts +7 -18
  38. package/dist/socials/instagram/utils.d.ts +7 -18
  39. package/dist/socials/instagram/utils.esm.js +26 -14
  40. package/dist/socials/instagram/webhook.cjs.js +1 -1
  41. package/dist/socials/instagram/webhook.esm.js +1 -1
  42. package/dist/socials/meta/container.cjs.js +82 -0
  43. package/dist/socials/meta/container.d.mts +38 -0
  44. package/dist/socials/meta/container.d.ts +38 -0
  45. package/dist/socials/meta/container.esm.js +57 -0
  46. package/dist/socials/meta/request.cjs.js +60 -0
  47. package/dist/socials/meta/request.d.mts +50 -0
  48. package/dist/socials/meta/request.d.ts +50 -0
  49. package/dist/socials/meta/request.esm.js +34 -0
  50. package/dist/socials/meta/types.cjs.js +34 -0
  51. package/dist/socials/meta/types.d.mts +51 -0
  52. package/dist/socials/meta/types.d.ts +51 -0
  53. package/dist/socials/meta/types.esm.js +9 -0
  54. package/dist/socials/{meta-webhook.cjs.js → meta/webhook.cjs.js} +4 -4
  55. package/dist/socials/{meta-webhook.esm.js → meta/webhook.esm.js} +1 -1
  56. package/dist/socials/threads/getters.cjs.js +36 -11
  57. package/dist/socials/threads/getters.d.mts +23 -2
  58. package/dist/socials/threads/getters.d.ts +23 -2
  59. package/dist/socials/threads/getters.esm.js +35 -11
  60. package/dist/socials/threads/setters.cjs.js +150 -44
  61. package/dist/socials/threads/setters.d.mts +25 -34
  62. package/dist/socials/threads/setters.d.ts +25 -34
  63. package/dist/socials/threads/setters.esm.js +149 -41
  64. package/dist/socials/threads/types.d.mts +40 -1
  65. package/dist/socials/threads/types.d.ts +40 -1
  66. package/dist/socials/threads/utils.cjs.js +29 -11
  67. package/dist/socials/threads/utils.d.mts +8 -17
  68. package/dist/socials/threads/utils.d.ts +8 -17
  69. package/dist/socials/threads/utils.esm.js +27 -10
  70. package/dist/socials/threads/webhook.cjs.js +1 -1
  71. package/dist/socials/threads/webhook.esm.js +1 -1
  72. package/package.json +1 -1
  73. /package/dist/socials/{meta-webhook.d.mts → meta/webhook.d.mts} +0 -0
  74. /package/dist/socials/{meta-webhook.d.ts → meta/webhook.d.ts} +0 -0
@@ -1,27 +1,204 @@
1
+ // src/socials/meta/request.ts
2
+ function createGraphClient(baseURL) {
3
+ const buildURL = (token, path, params = {}) => {
4
+ const search = new URLSearchParams();
5
+ for (const [key, value] of Object.entries(params)) {
6
+ if (value !== void 0) search.set(key, String(value));
7
+ }
8
+ search.set("access_token", token);
9
+ return `${baseURL}/${path}?${search.toString()}`;
10
+ };
11
+ const request = async (token, path, options = {}) => {
12
+ const response = await fetch(buildURL(token, path, options.params), {
13
+ method: options.method || (options.body ? "POST" : "GET"),
14
+ headers: {
15
+ Authorization: `Bearer ${token}`,
16
+ ...options.body ? { "Content-Type": "application/json" } : {}
17
+ },
18
+ ...options.body ? { body: JSON.stringify(options.body) } : {}
19
+ });
20
+ return response.json().catch(() => null);
21
+ };
22
+ return { buildURL, request };
23
+ }
24
+ function readGraphID(label, result) {
25
+ if (result?.error) {
26
+ console.error(`[naystack] ${label} failed: ${result.error.message}`);
27
+ return null;
28
+ }
29
+ return result?.id || null;
30
+ }
31
+
1
32
  // src/socials/instagram/utils.ts
2
- function getInstagramURL(token, path, params) {
3
- return `https://graph.instagram.com/v23.0/${path}?${Object.keys(params).map((key) => `${key}=${params[key]}`).join("&")}&access_token=${token}`;
33
+ var client = createGraphClient("https://graph.instagram.com/v23.0");
34
+ var getInstagramData = client.request;
35
+
36
+ // src/socials/instagram/getters.ts
37
+ var getInstagramContainerStatus = async (token, id) => {
38
+ const result = await getInstagramData(token, id, { params: { fields: "status_code,status" } });
39
+ if (!result?.status_code) return null;
40
+ return {
41
+ status: result.status_code,
42
+ error: result.status_code === "ERROR" ? result.status : void 0
43
+ };
44
+ };
45
+
46
+ // src/socials/meta/container.ts
47
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
48
+ async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
49
+ const deadline = Date.now() + timeoutMS;
50
+ let state = await getState();
51
+ if (!state) {
52
+ await sleep(2e3);
53
+ return null;
54
+ }
55
+ while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
56
+ await sleep(intervalMS);
57
+ state = await getState();
58
+ }
59
+ return state;
4
60
  }
5
- function getInstagramData(token, path, params = {}, postData) {
6
- return fetch(getInstagramURL(token, path, params), {
7
- method: postData ? "POST" : "GET",
8
- body: JSON.stringify(postData),
9
- headers: {
10
- Authorization: `Bearer ${token}`,
11
- "Content-Type": "application/json"
61
+ function createPublisher(platform) {
62
+ const isReady = async (token, id, label, wait) => {
63
+ const state = await waitForContainer(
64
+ () => platform.getStatus(token, id),
65
+ wait
66
+ );
67
+ if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
68
+ console.error(
69
+ `[naystack] ${platform.name} ${label} ${id} is ${state.status}${state.error ? `: ${state.error}` : ""}`
70
+ );
71
+ return false;
12
72
  }
13
- }).then((res) => res.json());
73
+ return true;
74
+ };
75
+ return {
76
+ /** Creates a container, waits for it to finish processing, then publishes it. */
77
+ publish: async (token, params, wait) => {
78
+ const containerID = await platform.createContainer(token, params);
79
+ if (!containerID) return null;
80
+ if (!await isReady(token, containerID, "container", wait)) return null;
81
+ return platform.publish(token, containerID);
82
+ },
83
+ /** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
84
+ createChildren: async (token, items, wait) => {
85
+ const children = [];
86
+ for (const item of items) {
87
+ const childID = await platform.createContainer(token, {
88
+ ...item,
89
+ is_carousel_item: true
90
+ });
91
+ if (!childID) return null;
92
+ if (!await isReady(token, childID, "carousel item", wait))
93
+ return null;
94
+ children.push(childID);
95
+ }
96
+ return children;
97
+ }
98
+ };
14
99
  }
15
100
 
16
101
  // src/socials/instagram/setters.ts
102
+ var publisher = createPublisher({
103
+ name: "Instagram",
104
+ getStatus: getInstagramContainerStatus,
105
+ createContainer: async (token, params) => readGraphID(
106
+ "Instagram media container",
107
+ await getInstagramData(token, "me/media", {
108
+ params,
109
+ method: "POST"
110
+ })
111
+ ),
112
+ publish: async (token, creationID) => readGraphID(
113
+ "Instagram publish",
114
+ await getInstagramData(token, "me/media_publish", {
115
+ params: { creation_id: creationID },
116
+ method: "POST"
117
+ })
118
+ )
119
+ });
120
+ var mediaParams = (media) => ({
121
+ media_type: media.type === "video" /* Video */ ? "VIDEO" : void 0,
122
+ image_url: media.type === "photo" /* Photo */ ? media.url : void 0,
123
+ video_url: media.type === "video" /* Video */ ? media.url : void 0,
124
+ // Instagram accepts alt text on images only.
125
+ alt_text: media.type === "photo" /* Photo */ ? media.altText : void 0
126
+ });
127
+ var createInstagramPost = async (token, input) => {
128
+ const media = [input.media].flat();
129
+ const first = media[0];
130
+ if (!first) {
131
+ console.error(
132
+ "[naystack] Instagram posts need at least one image or video"
133
+ );
134
+ return null;
135
+ }
136
+ if (input.story) {
137
+ return publisher.publish(
138
+ token,
139
+ {
140
+ ...mediaParams(first),
141
+ media_type: "STORIES",
142
+ alt_text: void 0
143
+ },
144
+ input.wait
145
+ );
146
+ }
147
+ if (media.length > 1) {
148
+ const children = await publisher.createChildren(
149
+ token,
150
+ media.map(mediaParams),
151
+ input.wait
152
+ );
153
+ if (!children) return null;
154
+ return publisher.publish(
155
+ token,
156
+ {
157
+ media_type: "CAROUSEL",
158
+ children: children.join(","),
159
+ caption: input.caption
160
+ },
161
+ input.wait
162
+ );
163
+ }
164
+ if (first.type === "video" /* Video */) {
165
+ return publisher.publish(
166
+ token,
167
+ {
168
+ media_type: "REELS",
169
+ video_url: first.url,
170
+ caption: input.caption,
171
+ cover_url: input.coverURL,
172
+ thumb_offset: input.thumbOffset,
173
+ share_to_feed: input.shareToFeed,
174
+ audio_name: input.audioName,
175
+ collaborators: input.collaborators && JSON.stringify(input.collaborators)
176
+ },
177
+ input.wait
178
+ );
179
+ }
180
+ return publisher.publish(
181
+ token,
182
+ {
183
+ image_url: first.url,
184
+ caption: input.caption,
185
+ alt_text: first.altText,
186
+ location_id: input.locationID
187
+ },
188
+ input.wait
189
+ );
190
+ };
17
191
  var sendInstagramMessage = (token, to, text) => {
18
- return getInstagramData(token, "me/messages", void 0, {
19
- recipient: { id: to },
20
- message: {
21
- text
192
+ return getInstagramData(token, "me/messages", {
193
+ body: {
194
+ recipient: { id: to },
195
+ message: {
196
+ text
197
+ }
22
198
  }
23
199
  });
24
200
  };
25
201
  export {
202
+ createInstagramPost,
26
203
  sendInstagramMessage
27
204
  };
@@ -1,3 +1,6 @@
1
+ import { WaitForContainerOptions } from '../meta/container.mjs';
2
+ import { MetaMediaType } from '../meta/types.mjs';
3
+
1
4
  /**
2
5
  * Instagram Messaging API message shape.
3
6
  *
@@ -84,20 +87,54 @@ type InstagramConversation = {
84
87
  };
85
88
  };
86
89
  /**
87
- * Instagram Graph API error response shape.
90
+ * One image or video to publish. The URL must be publicly reachable — Instagram
91
+ * downloads it server-side.
88
92
  *
89
- * @property error - Error details from the API (message, type, code, subcode, trace id).
93
+ * @property url - Public URL. Images must be **JPEG**.
94
+ * @property type - `"image"` or `"video"`.
95
+ * @property altText - Accessibility description, up to 1000 characters. Images only.
90
96
  *
91
97
  * @category Socials
92
98
  */
93
- type InstagramError = {
94
- error?: {
95
- message: string;
96
- type: string;
97
- code: number;
98
- error_subcode: number;
99
- fbtrace_id: string;
100
- };
99
+ type InstagramPostMedia = {
100
+ url: string;
101
+ type: MetaMediaType;
102
+ altText?: string;
103
+ };
104
+ /**
105
+ * Input for {@link createInstagramPost}. What gets published follows from `media`:
106
+ *
107
+ * | `media` | Result |
108
+ * | --- | --- |
109
+ * | one image | feed image |
110
+ * | one video | reel |
111
+ * | 2–10 items | carousel (counts as one post) |
112
+ * | any, with `story: true` | story, from the first item |
113
+ *
114
+ * @property media - One item or an array. Images are cropped to the first item's aspect ratio in a carousel.
115
+ * @property caption - Post caption.
116
+ * @property story - Publish as a 24-hour story instead of to the feed. Uses the first media item only.
117
+ * @property locationID - Facebook Page id to tag as the location. Single images only.
118
+ * @property coverURL - Reels: public URL of a cover image. Takes precedence over `thumbOffset`.
119
+ * @property thumbOffset - Reels: frame to use as the thumbnail, in milliseconds.
120
+ * @property shareToFeed - Reels: also show the reel in the feed, not only the Reels tab.
121
+ * @property audioName - Reels: name for the audio track. Can only be set once.
122
+ * @property collaborators - Reels: up to 3 public usernames to invite as collaborators.
123
+ * @property wait - Container polling settings.
124
+ *
125
+ * @category Socials
126
+ */
127
+ type InstagramPostInput = {
128
+ media: InstagramPostMedia | InstagramPostMedia[];
129
+ caption?: string;
130
+ story?: boolean;
131
+ locationID?: string;
132
+ coverURL?: string;
133
+ thumbOffset?: number;
134
+ shareToFeed?: boolean;
135
+ audioName?: string;
136
+ collaborators?: string[];
137
+ wait?: WaitForContainerOptions;
101
138
  };
102
139
 
103
- export type { InstagramConversation, InstagramError, InstagramMedia, InstagramMessage, InstagramUser };
140
+ export type { InstagramConversation, InstagramMedia, InstagramMessage, InstagramPostInput, InstagramPostMedia, InstagramUser };
@@ -1,3 +1,6 @@
1
+ import { WaitForContainerOptions } from '../meta/container.js';
2
+ import { MetaMediaType } from '../meta/types.js';
3
+
1
4
  /**
2
5
  * Instagram Messaging API message shape.
3
6
  *
@@ -84,20 +87,54 @@ type InstagramConversation = {
84
87
  };
85
88
  };
86
89
  /**
87
- * Instagram Graph API error response shape.
90
+ * One image or video to publish. The URL must be publicly reachable — Instagram
91
+ * downloads it server-side.
88
92
  *
89
- * @property error - Error details from the API (message, type, code, subcode, trace id).
93
+ * @property url - Public URL. Images must be **JPEG**.
94
+ * @property type - `"image"` or `"video"`.
95
+ * @property altText - Accessibility description, up to 1000 characters. Images only.
90
96
  *
91
97
  * @category Socials
92
98
  */
93
- type InstagramError = {
94
- error?: {
95
- message: string;
96
- type: string;
97
- code: number;
98
- error_subcode: number;
99
- fbtrace_id: string;
100
- };
99
+ type InstagramPostMedia = {
100
+ url: string;
101
+ type: MetaMediaType;
102
+ altText?: string;
103
+ };
104
+ /**
105
+ * Input for {@link createInstagramPost}. What gets published follows from `media`:
106
+ *
107
+ * | `media` | Result |
108
+ * | --- | --- |
109
+ * | one image | feed image |
110
+ * | one video | reel |
111
+ * | 2–10 items | carousel (counts as one post) |
112
+ * | any, with `story: true` | story, from the first item |
113
+ *
114
+ * @property media - One item or an array. Images are cropped to the first item's aspect ratio in a carousel.
115
+ * @property caption - Post caption.
116
+ * @property story - Publish as a 24-hour story instead of to the feed. Uses the first media item only.
117
+ * @property locationID - Facebook Page id to tag as the location. Single images only.
118
+ * @property coverURL - Reels: public URL of a cover image. Takes precedence over `thumbOffset`.
119
+ * @property thumbOffset - Reels: frame to use as the thumbnail, in milliseconds.
120
+ * @property shareToFeed - Reels: also show the reel in the feed, not only the Reels tab.
121
+ * @property audioName - Reels: name for the audio track. Can only be set once.
122
+ * @property collaborators - Reels: up to 3 public usernames to invite as collaborators.
123
+ * @property wait - Container polling settings.
124
+ *
125
+ * @category Socials
126
+ */
127
+ type InstagramPostInput = {
128
+ media: InstagramPostMedia | InstagramPostMedia[];
129
+ caption?: string;
130
+ story?: boolean;
131
+ locationID?: string;
132
+ coverURL?: string;
133
+ thumbOffset?: number;
134
+ shareToFeed?: boolean;
135
+ audioName?: string;
136
+ collaborators?: string[];
137
+ wait?: WaitForContainerOptions;
101
138
  };
102
139
 
103
- export type { InstagramConversation, InstagramError, InstagramMedia, InstagramMessage, InstagramUser };
140
+ export type { InstagramConversation, InstagramMedia, InstagramMessage, InstagramPostInput, InstagramPostMedia, InstagramUser };
@@ -20,25 +20,38 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/socials/instagram/utils.ts
21
21
  var utils_exports = {};
22
22
  __export(utils_exports, {
23
- getInstagramData: () => getInstagramData,
24
- getInstagramURL: () => getInstagramURL
23
+ getInstagramData: () => getInstagramData
25
24
  });
26
25
  module.exports = __toCommonJS(utils_exports);
27
- function getInstagramURL(token, path, params) {
28
- return `https://graph.instagram.com/v23.0/${path}?${Object.keys(params).map((key) => `${key}=${params[key]}`).join("&")}&access_token=${token}`;
29
- }
30
- function getInstagramData(token, path, params = {}, postData) {
31
- return fetch(getInstagramURL(token, path, params), {
32
- method: postData ? "POST" : "GET",
33
- body: JSON.stringify(postData),
34
- headers: {
35
- Authorization: `Bearer ${token}`,
36
- "Content-Type": "application/json"
26
+
27
+ // src/socials/meta/request.ts
28
+ function createGraphClient(baseURL) {
29
+ const buildURL = (token, path, params = {}) => {
30
+ const search = new URLSearchParams();
31
+ for (const [key, value] of Object.entries(params)) {
32
+ if (value !== void 0) search.set(key, String(value));
37
33
  }
38
- }).then((res) => res.json());
34
+ search.set("access_token", token);
35
+ return `${baseURL}/${path}?${search.toString()}`;
36
+ };
37
+ const request = async (token, path, options = {}) => {
38
+ const response = await fetch(buildURL(token, path, options.params), {
39
+ method: options.method || (options.body ? "POST" : "GET"),
40
+ headers: {
41
+ Authorization: `Bearer ${token}`,
42
+ ...options.body ? { "Content-Type": "application/json" } : {}
43
+ },
44
+ ...options.body ? { body: JSON.stringify(options.body) } : {}
45
+ });
46
+ return response.json().catch(() => null);
47
+ };
48
+ return { buildURL, request };
39
49
  }
50
+
51
+ // src/socials/instagram/utils.ts
52
+ var client = createGraphClient("https://graph.instagram.com/v23.0");
53
+ var getInstagramData = client.request;
40
54
  // Annotate the CommonJS export names for ESM import in node:
41
55
  0 && (module.exports = {
42
- getInstagramData,
43
- getInstagramURL
56
+ getInstagramData
44
57
  });
@@ -1,25 +1,14 @@
1
- import { InstagramError } from './types.mjs';
1
+ import { GraphError } from '../meta/types.mjs';
2
+ import { GraphRequestOptions } from '../meta/request.mjs';
2
3
 
3
4
  /**
4
- * Builds an Instagram Graph API URL with the given path and query parameters (includes `access_token`).
5
- *
6
- * @param token - Instagram access token.
7
- * @param path - API path (e.g. `"me"`, `"me/media"`, `"me/conversations"`).
8
- * @param params - Additional query parameters.
9
- * @returns Full URL string.
10
- * @category Socials
11
- */
12
- declare function getInstagramURL(token: string, path: string, params: Record<string, string>): string;
13
- /**
14
- * Fetches JSON from the Instagram Graph API (GET or POST depending on whether `postData` is provided).
5
+ * Fetches JSON from the Instagram Graph API.
15
6
  *
16
7
  * @param token - Instagram access token.
17
8
  * @param path - API path.
18
- * @param params - Query parameters (default: `{}`).
19
- * @param postData - Optional POST body (if provided, the request becomes a POST).
20
- * @returns Promise of `(T & InstagramError) | null`.
21
- * @category Socials
9
+ * @param options - `params` (query), `method`, `body`.
10
+ * @returns Promise of `(T & GraphError) | null`.
22
11
  */
23
- declare function getInstagramData<T>(token: string, path: string, params?: Record<string, string>, postData?: object): Promise<(T & InstagramError) | null>;
12
+ declare const getInstagramData: <T>(token: string, path: string, options?: GraphRequestOptions) => Promise<(T & GraphError) | null>;
24
13
 
25
- export { getInstagramData, getInstagramURL };
14
+ export { getInstagramData };
@@ -1,25 +1,14 @@
1
- import { InstagramError } from './types.js';
1
+ import { GraphError } from '../meta/types.js';
2
+ import { GraphRequestOptions } from '../meta/request.js';
2
3
 
3
4
  /**
4
- * Builds an Instagram Graph API URL with the given path and query parameters (includes `access_token`).
5
- *
6
- * @param token - Instagram access token.
7
- * @param path - API path (e.g. `"me"`, `"me/media"`, `"me/conversations"`).
8
- * @param params - Additional query parameters.
9
- * @returns Full URL string.
10
- * @category Socials
11
- */
12
- declare function getInstagramURL(token: string, path: string, params: Record<string, string>): string;
13
- /**
14
- * Fetches JSON from the Instagram Graph API (GET or POST depending on whether `postData` is provided).
5
+ * Fetches JSON from the Instagram Graph API.
15
6
  *
16
7
  * @param token - Instagram access token.
17
8
  * @param path - API path.
18
- * @param params - Query parameters (default: `{}`).
19
- * @param postData - Optional POST body (if provided, the request becomes a POST).
20
- * @returns Promise of `(T & InstagramError) | null`.
21
- * @category Socials
9
+ * @param options - `params` (query), `method`, `body`.
10
+ * @returns Promise of `(T & GraphError) | null`.
22
11
  */
23
- declare function getInstagramData<T>(token: string, path: string, params?: Record<string, string>, postData?: object): Promise<(T & InstagramError) | null>;
12
+ declare const getInstagramData: <T>(token: string, path: string, options?: GraphRequestOptions) => Promise<(T & GraphError) | null>;
24
13
 
25
- export { getInstagramData, getInstagramURL };
14
+ export { getInstagramData };
@@ -1,18 +1,30 @@
1
- // src/socials/instagram/utils.ts
2
- function getInstagramURL(token, path, params) {
3
- return `https://graph.instagram.com/v23.0/${path}?${Object.keys(params).map((key) => `${key}=${params[key]}`).join("&")}&access_token=${token}`;
4
- }
5
- function getInstagramData(token, path, params = {}, postData) {
6
- return fetch(getInstagramURL(token, path, params), {
7
- method: postData ? "POST" : "GET",
8
- body: JSON.stringify(postData),
9
- headers: {
10
- Authorization: `Bearer ${token}`,
11
- "Content-Type": "application/json"
1
+ // src/socials/meta/request.ts
2
+ function createGraphClient(baseURL) {
3
+ const buildURL = (token, path, params = {}) => {
4
+ const search = new URLSearchParams();
5
+ for (const [key, value] of Object.entries(params)) {
6
+ if (value !== void 0) search.set(key, String(value));
12
7
  }
13
- }).then((res) => res.json());
8
+ search.set("access_token", token);
9
+ return `${baseURL}/${path}?${search.toString()}`;
10
+ };
11
+ const request = async (token, path, options = {}) => {
12
+ const response = await fetch(buildURL(token, path, options.params), {
13
+ method: options.method || (options.body ? "POST" : "GET"),
14
+ headers: {
15
+ Authorization: `Bearer ${token}`,
16
+ ...options.body ? { "Content-Type": "application/json" } : {}
17
+ },
18
+ ...options.body ? { body: JSON.stringify(options.body) } : {}
19
+ });
20
+ return response.json().catch(() => null);
21
+ };
22
+ return { buildURL, request };
14
23
  }
24
+
25
+ // src/socials/instagram/utils.ts
26
+ var client = createGraphClient("https://graph.instagram.com/v23.0");
27
+ var getInstagramData = client.request;
15
28
  export {
16
- getInstagramData,
17
- getInstagramURL
29
+ getInstagramData
18
30
  };
@@ -24,7 +24,7 @@ __export(webhook_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(webhook_exports);
26
26
 
27
- // src/socials/meta-webhook.ts
27
+ // src/socials/meta/webhook.ts
28
28
  var import_server = require("next/server");
29
29
  var verifyWebhook = (secret) => (req) => {
30
30
  const params = req.nextUrl.searchParams;
@@ -1,4 +1,4 @@
1
- // src/socials/meta-webhook.ts
1
+ // src/socials/meta/webhook.ts
2
2
  import { NextResponse } from "next/server";
3
3
  var verifyWebhook = (secret) => (req) => {
4
4
  const params = req.nextUrl.searchParams;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/socials/meta/container.ts
21
+ var container_exports = {};
22
+ __export(container_exports, {
23
+ createPublisher: () => createPublisher
24
+ });
25
+ module.exports = __toCommonJS(container_exports);
26
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
27
+ async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
28
+ const deadline = Date.now() + timeoutMS;
29
+ let state = await getState();
30
+ if (!state) {
31
+ await sleep(2e3);
32
+ return null;
33
+ }
34
+ while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
35
+ await sleep(intervalMS);
36
+ state = await getState();
37
+ }
38
+ return state;
39
+ }
40
+ function createPublisher(platform) {
41
+ const isReady = async (token, id, label, wait) => {
42
+ const state = await waitForContainer(
43
+ () => platform.getStatus(token, id),
44
+ wait
45
+ );
46
+ if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
47
+ console.error(
48
+ `[naystack] ${platform.name} ${label} ${id} is ${state.status}${state.error ? `: ${state.error}` : ""}`
49
+ );
50
+ return false;
51
+ }
52
+ return true;
53
+ };
54
+ return {
55
+ /** Creates a container, waits for it to finish processing, then publishes it. */
56
+ publish: async (token, params, wait) => {
57
+ const containerID = await platform.createContainer(token, params);
58
+ if (!containerID) return null;
59
+ if (!await isReady(token, containerID, "container", wait)) return null;
60
+ return platform.publish(token, containerID);
61
+ },
62
+ /** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
63
+ createChildren: async (token, items, wait) => {
64
+ const children = [];
65
+ for (const item of items) {
66
+ const childID = await platform.createContainer(token, {
67
+ ...item,
68
+ is_carousel_item: true
69
+ });
70
+ if (!childID) return null;
71
+ if (!await isReady(token, childID, "carousel item", wait))
72
+ return null;
73
+ children.push(childID);
74
+ }
75
+ return children;
76
+ }
77
+ };
78
+ }
79
+ // Annotate the CommonJS export names for ESM import in node:
80
+ 0 && (module.exports = {
81
+ createPublisher
82
+ });
@@ -0,0 +1,38 @@
1
+ import { GraphParams, ContainerState } from './types.mjs';
2
+
3
+ /**
4
+ * Polling settings for a media container.
5
+ *
6
+ * @property intervalMS - Delay between status checks. Default: `5000`.
7
+ * @property timeoutMS - Give up after this long. Default: `300000` (Meta stops processing after ~5 min).
8
+ *
9
+ * @category Socials
10
+ */
11
+ type WaitForContainerOptions = {
12
+ intervalMS?: number;
13
+ timeoutMS?: number;
14
+ };
15
+ /**
16
+ * Wires up the create → wait → publish flow Meta platforms share. Instagram and
17
+ * Threads differ only in their endpoints, so each builds one publisher and every
18
+ * post shape goes through it.
19
+ *
20
+ * @param platform.name - Used in error logs, e.g. `"Instagram"`.
21
+ * @param platform.createContainer - Creates a container, resolving to its id.
22
+ * @param platform.getStatus - Reads a container's state by id.
23
+ * @param platform.publish - Publishes a finished container, resolving to the post id.
24
+ * @returns `{ publish, createChildren }`.
25
+ */
26
+ declare function createPublisher(platform: {
27
+ name: string;
28
+ createContainer: (token: string, params: GraphParams) => Promise<string | null>;
29
+ getStatus: (token: string, id: string) => Promise<ContainerState | null>;
30
+ publish: (token: string, creationID: string) => Promise<string | null>;
31
+ }): {
32
+ /** Creates a container, waits for it to finish processing, then publishes it. */
33
+ publish: (token: string, params: GraphParams, wait?: WaitForContainerOptions) => Promise<string | null>;
34
+ /** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
35
+ createChildren: (token: string, items: GraphParams[], wait?: WaitForContainerOptions) => Promise<string[] | null>;
36
+ };
37
+
38
+ export { type WaitForContainerOptions, createPublisher };