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,4 +1,6 @@
1
- import { InstagramConversation, InstagramError, InstagramMedia, InstagramMessage, InstagramUser } from './types.mjs';
1
+ import { GraphError, ContainerState } from '../meta/types.mjs';
2
+ import { InstagramConversation, InstagramMedia, InstagramMessage, InstagramUser } from './types.mjs';
3
+ import '../meta/container.mjs';
2
4
 
3
5
  /**
4
6
  * Fetches an Instagram user's profile. Defaults to the authenticated user (`"me"`).
@@ -18,7 +20,7 @@ import { InstagramConversation, InstagramError, InstagramMedia, InstagramMessage
18
20
  *
19
21
  * @category Socials
20
22
  */
21
- declare const getInstagramUser: <T = InstagramUser>(token: string, id?: string, fields?: string[]) => Promise<(T & InstagramError) | null>;
23
+ declare const getInstagramUser: <T = InstagramUser>(token: string, id?: string, fields?: string[]) => Promise<(T & GraphError) | null>;
22
24
  /**
23
25
  * Fetches the authenticated user's recent Instagram media.
24
26
  *
@@ -41,7 +43,7 @@ declare const getInstagramUser: <T = InstagramUser>(token: string, id?: string,
41
43
  */
42
44
  declare const getInstagramMedia: <T = InstagramMedia>(token: string, fields?: string[], limit?: number) => Promise<({
43
45
  data: T[];
44
- } & InstagramError) | null>;
46
+ } & GraphError) | null>;
45
47
  /**
46
48
  * Fetches Instagram conversations (DM threads) with pagination support.
47
49
  *
@@ -92,7 +94,7 @@ declare const getInstagramConversations: (token: string, limit?: number, cursor?
92
94
  */
93
95
  declare const getInstagramConversationsByUser: (token: string, userID: string) => Promise<({
94
96
  data: InstagramConversation[];
95
- } & InstagramError) | null>;
97
+ } & GraphError) | null>;
96
98
  /**
97
99
  * Fetches the single conversation between the authenticated user and the given user.
98
100
  * Filters for conversations with exactly 2 participants.
@@ -149,6 +151,54 @@ declare const getInstagramConversation: (token: string, id: string, cursor?: str
149
151
  *
150
152
  * @category Socials
151
153
  */
152
- declare const getInstagramMessage: <T = InstagramMessage>(token: string, id: string, fields?: string[]) => Promise<(T & InstagramError) | null>;
154
+ declare const getInstagramMessage: <T = InstagramMessage>(token: string, id: string, fields?: string[]) => Promise<(T & GraphError) | null>;
155
+ /**
156
+ * Reads a media container's processing status. Videos and reels must reach
157
+ * `FINISHED` before they can be published.
158
+ *
159
+ * @param token - Instagram access token.
160
+ * @param id - Container id from `createInstagramContainer`.
161
+ * @returns Promise of `{ status, error? }`, or `null` if the status was unreadable.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * import { getInstagramContainerStatus } from "naystack/socials";
166
+ *
167
+ * const state = await getInstagramContainerStatus(accessToken, containerId);
168
+ * if (state?.status === "FINISHED") await publishInstagramContainer(accessToken, containerId);
169
+ * ```
170
+ *
171
+ * @category Socials
172
+ */
173
+ declare const getInstagramContainerStatus: (token: string, id: string) => Promise<ContainerState | null>;
174
+ /**
175
+ * Checks whether an access token is allowed to publish.
176
+ *
177
+ * Meta exposes no scope-listing endpoint for Instagram Login tokens, so this probes
178
+ * the publishing quota — a read-only endpoint that needs exactly the same
179
+ * `instagram_business_content_publish` scope publishing does. Nothing is posted.
180
+ *
181
+ * Worth calling before {@link createInstagramPost} in a scheduled job: a token whose
182
+ * access was revoked fails here with a clear reason, instead of surfacing later as a
183
+ * container error.
184
+ *
185
+ * Rejects — rather than answering `false` — if the request itself fails, so a network
186
+ * blip is never reported as a missing permission.
187
+ *
188
+ * @param token - Instagram access token.
189
+ * @returns Promise of `true` if the token can publish. On `false`, the API's reason is logged.
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * import { canPublishToInstagram } from "naystack/socials";
194
+ *
195
+ * if (!(await canPublishToInstagram(process.env.INSTAGRAM_ACCESS_TOKEN!))) {
196
+ * throw new Error("Instagram token cannot publish — reconnect with the publish scope");
197
+ * }
198
+ * ```
199
+ *
200
+ * @category Socials
201
+ */
202
+ declare const canPublishToInstagram: (token: string) => Promise<boolean>;
153
203
 
154
- export { getInstagramConversation, getInstagramConversationByUser, getInstagramConversations, getInstagramConversationsByUser, getInstagramMedia, getInstagramMessage, getInstagramUser };
204
+ export { canPublishToInstagram, getInstagramContainerStatus, getInstagramConversation, getInstagramConversationByUser, getInstagramConversations, getInstagramConversationsByUser, getInstagramMedia, getInstagramMessage, getInstagramUser };
@@ -1,4 +1,6 @@
1
- import { InstagramConversation, InstagramError, InstagramMedia, InstagramMessage, InstagramUser } from './types.js';
1
+ import { GraphError, ContainerState } from '../meta/types.js';
2
+ import { InstagramConversation, InstagramMedia, InstagramMessage, InstagramUser } from './types.js';
3
+ import '../meta/container.js';
2
4
 
3
5
  /**
4
6
  * Fetches an Instagram user's profile. Defaults to the authenticated user (`"me"`).
@@ -18,7 +20,7 @@ import { InstagramConversation, InstagramError, InstagramMedia, InstagramMessage
18
20
  *
19
21
  * @category Socials
20
22
  */
21
- declare const getInstagramUser: <T = InstagramUser>(token: string, id?: string, fields?: string[]) => Promise<(T & InstagramError) | null>;
23
+ declare const getInstagramUser: <T = InstagramUser>(token: string, id?: string, fields?: string[]) => Promise<(T & GraphError) | null>;
22
24
  /**
23
25
  * Fetches the authenticated user's recent Instagram media.
24
26
  *
@@ -41,7 +43,7 @@ declare const getInstagramUser: <T = InstagramUser>(token: string, id?: string,
41
43
  */
42
44
  declare const getInstagramMedia: <T = InstagramMedia>(token: string, fields?: string[], limit?: number) => Promise<({
43
45
  data: T[];
44
- } & InstagramError) | null>;
46
+ } & GraphError) | null>;
45
47
  /**
46
48
  * Fetches Instagram conversations (DM threads) with pagination support.
47
49
  *
@@ -92,7 +94,7 @@ declare const getInstagramConversations: (token: string, limit?: number, cursor?
92
94
  */
93
95
  declare const getInstagramConversationsByUser: (token: string, userID: string) => Promise<({
94
96
  data: InstagramConversation[];
95
- } & InstagramError) | null>;
97
+ } & GraphError) | null>;
96
98
  /**
97
99
  * Fetches the single conversation between the authenticated user and the given user.
98
100
  * Filters for conversations with exactly 2 participants.
@@ -149,6 +151,54 @@ declare const getInstagramConversation: (token: string, id: string, cursor?: str
149
151
  *
150
152
  * @category Socials
151
153
  */
152
- declare const getInstagramMessage: <T = InstagramMessage>(token: string, id: string, fields?: string[]) => Promise<(T & InstagramError) | null>;
154
+ declare const getInstagramMessage: <T = InstagramMessage>(token: string, id: string, fields?: string[]) => Promise<(T & GraphError) | null>;
155
+ /**
156
+ * Reads a media container's processing status. Videos and reels must reach
157
+ * `FINISHED` before they can be published.
158
+ *
159
+ * @param token - Instagram access token.
160
+ * @param id - Container id from `createInstagramContainer`.
161
+ * @returns Promise of `{ status, error? }`, or `null` if the status was unreadable.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * import { getInstagramContainerStatus } from "naystack/socials";
166
+ *
167
+ * const state = await getInstagramContainerStatus(accessToken, containerId);
168
+ * if (state?.status === "FINISHED") await publishInstagramContainer(accessToken, containerId);
169
+ * ```
170
+ *
171
+ * @category Socials
172
+ */
173
+ declare const getInstagramContainerStatus: (token: string, id: string) => Promise<ContainerState | null>;
174
+ /**
175
+ * Checks whether an access token is allowed to publish.
176
+ *
177
+ * Meta exposes no scope-listing endpoint for Instagram Login tokens, so this probes
178
+ * the publishing quota — a read-only endpoint that needs exactly the same
179
+ * `instagram_business_content_publish` scope publishing does. Nothing is posted.
180
+ *
181
+ * Worth calling before {@link createInstagramPost} in a scheduled job: a token whose
182
+ * access was revoked fails here with a clear reason, instead of surfacing later as a
183
+ * container error.
184
+ *
185
+ * Rejects — rather than answering `false` — if the request itself fails, so a network
186
+ * blip is never reported as a missing permission.
187
+ *
188
+ * @param token - Instagram access token.
189
+ * @returns Promise of `true` if the token can publish. On `false`, the API's reason is logged.
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * import { canPublishToInstagram } from "naystack/socials";
194
+ *
195
+ * if (!(await canPublishToInstagram(process.env.INSTAGRAM_ACCESS_TOKEN!))) {
196
+ * throw new Error("Instagram token cannot publish — reconnect with the publish scope");
197
+ * }
198
+ * ```
199
+ *
200
+ * @category Socials
201
+ */
202
+ declare const canPublishToInstagram: (token: string) => Promise<boolean>;
153
203
 
154
- export { getInstagramConversation, getInstagramConversationByUser, getInstagramConversations, getInstagramConversationsByUser, getInstagramMedia, getInstagramMessage, getInstagramUser };
204
+ export { canPublishToInstagram, getInstagramContainerStatus, getInstagramConversation, getInstagramConversationByUser, getInstagramConversations, getInstagramConversationsByUser, getInstagramMedia, getInstagramMessage, getInstagramUser };
@@ -1,36 +1,55 @@
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
  }
15
24
 
25
+ // src/socials/instagram/utils.ts
26
+ var client = createGraphClient("https://graph.instagram.com/v23.0");
27
+ var getInstagramData = client.request;
28
+
16
29
  // src/socials/instagram/getters.ts
17
30
  var getInstagramUser = (token, id, fields) => {
18
31
  return getInstagramData(token, id || "me", {
19
- fields: fields ? fields.join(",") : "username,followers_count,media_count"
32
+ params: {
33
+ fields: fields ? fields.join(",") : "username,followers_count,media_count"
34
+ }
20
35
  });
21
36
  };
22
37
  var getInstagramMedia = (token, fields, limit = 12) => {
23
38
  return getInstagramData(token, "me/media", {
24
- fields: fields ? fields.join(",") : "like_count,comments_count,permalink",
25
- limit: limit?.toString()
39
+ params: {
40
+ fields: fields ? fields.join(",") : "like_count,comments_count,permalink",
41
+ limit
42
+ }
26
43
  });
27
44
  };
28
45
  var getInstagramConversations = async (token, limit = 25, cursor) => {
29
46
  const result = await getInstagramData(token, "me/conversations", {
30
- platform: "instagram",
31
- fields: "participants,messages,updated_time",
32
- limit: limit.toString(),
33
- ...cursor ? { after: cursor } : {}
47
+ params: {
48
+ platform: "instagram",
49
+ fields: "participants,messages,updated_time",
50
+ limit,
51
+ after: cursor
52
+ }
34
53
  });
35
54
  return {
36
55
  data: result?.data.map((item) => ({
@@ -46,8 +65,10 @@ var getInstagramConversationsByUser = (token, userID) => {
46
65
  token,
47
66
  "me/conversations",
48
67
  {
49
- fields: "participants,messages,updated_time",
50
- user_id: userID
68
+ params: {
69
+ fields: "participants,messages,updated_time",
70
+ user_id: userID
71
+ }
51
72
  }
52
73
  );
53
74
  };
@@ -56,9 +77,11 @@ var getInstagramConversationByUser = async (token, userID) => {
56
77
  return res?.data?.find((item) => item.participants?.data.length === 2);
57
78
  };
58
79
  var getInstagramConversation = async (token, id, cursor) => {
59
- const result = await getInstagramData(token, id + "", {
60
- fields: "participants,messages,updated_time",
61
- ...cursor ? { after: cursor } : {}
80
+ const result = await getInstagramData(token, id, {
81
+ params: {
82
+ fields: "participants,messages,updated_time",
83
+ after: cursor
84
+ }
62
85
  });
63
86
  return {
64
87
  messages: result?.messages?.data,
@@ -72,10 +95,36 @@ var getInstagramConversation = async (token, id, cursor) => {
72
95
  };
73
96
  var getInstagramMessage = (token, id, fields) => {
74
97
  return getInstagramData(token, id, {
75
- fields: fields ? fields.join(",") : "id,created_time,from,to,message"
98
+ params: {
99
+ fields: fields ? fields.join(",") : "id,created_time,from,to,message"
100
+ }
76
101
  });
77
102
  };
103
+ var getInstagramContainerStatus = async (token, id) => {
104
+ const result = await getInstagramData(token, id, { params: { fields: "status_code,status" } });
105
+ if (!result?.status_code) return null;
106
+ return {
107
+ status: result.status_code,
108
+ error: result.status_code === "ERROR" ? result.status : void 0
109
+ };
110
+ };
111
+ var canPublishToInstagram = async (token) => {
112
+ const result = await getInstagramData(
113
+ token,
114
+ "me/content_publishing_limit",
115
+ { params: { fields: "quota_usage" } }
116
+ );
117
+ if (!Array.isArray(result?.data)) {
118
+ console.error(
119
+ `[naystack] Instagram token cannot publish${result?.error ? `: ${result.error.message}` : ""}`
120
+ );
121
+ return false;
122
+ }
123
+ return true;
124
+ };
78
125
  export {
126
+ canPublishToInstagram,
127
+ getInstagramContainerStatus,
79
128
  getInstagramConversation,
80
129
  getInstagramConversationByUser,
81
130
  getInstagramConversations,
@@ -20,35 +20,213 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/socials/instagram/setters.ts
21
21
  var setters_exports = {};
22
22
  __export(setters_exports, {
23
+ createInstagramPost: () => createInstagramPost,
23
24
  sendInstagramMessage: () => sendInstagramMessage
24
25
  });
25
26
  module.exports = __toCommonJS(setters_exports);
26
27
 
28
+ // src/socials/meta/request.ts
29
+ function createGraphClient(baseURL) {
30
+ const buildURL = (token, path, params = {}) => {
31
+ const search = new URLSearchParams();
32
+ for (const [key, value] of Object.entries(params)) {
33
+ if (value !== void 0) search.set(key, String(value));
34
+ }
35
+ search.set("access_token", token);
36
+ return `${baseURL}/${path}?${search.toString()}`;
37
+ };
38
+ const request = async (token, path, options = {}) => {
39
+ const response = await fetch(buildURL(token, path, options.params), {
40
+ method: options.method || (options.body ? "POST" : "GET"),
41
+ headers: {
42
+ Authorization: `Bearer ${token}`,
43
+ ...options.body ? { "Content-Type": "application/json" } : {}
44
+ },
45
+ ...options.body ? { body: JSON.stringify(options.body) } : {}
46
+ });
47
+ return response.json().catch(() => null);
48
+ };
49
+ return { buildURL, request };
50
+ }
51
+ function readGraphID(label, result) {
52
+ if (result?.error) {
53
+ console.error(`[naystack] ${label} failed: ${result.error.message}`);
54
+ return null;
55
+ }
56
+ return result?.id || null;
57
+ }
58
+
27
59
  // src/socials/instagram/utils.ts
28
- function getInstagramURL(token, path, params) {
29
- return `https://graph.instagram.com/v23.0/${path}?${Object.keys(params).map((key) => `${key}=${params[key]}`).join("&")}&access_token=${token}`;
60
+ var client = createGraphClient("https://graph.instagram.com/v23.0");
61
+ var getInstagramData = client.request;
62
+
63
+ // src/socials/instagram/getters.ts
64
+ var getInstagramContainerStatus = async (token, id) => {
65
+ const result = await getInstagramData(token, id, { params: { fields: "status_code,status" } });
66
+ if (!result?.status_code) return null;
67
+ return {
68
+ status: result.status_code,
69
+ error: result.status_code === "ERROR" ? result.status : void 0
70
+ };
71
+ };
72
+
73
+ // src/socials/meta/container.ts
74
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
75
+ async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
76
+ const deadline = Date.now() + timeoutMS;
77
+ let state = await getState();
78
+ if (!state) {
79
+ await sleep(2e3);
80
+ return null;
81
+ }
82
+ while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
83
+ await sleep(intervalMS);
84
+ state = await getState();
85
+ }
86
+ return state;
30
87
  }
31
- function getInstagramData(token, path, params = {}, postData) {
32
- return fetch(getInstagramURL(token, path, params), {
33
- method: postData ? "POST" : "GET",
34
- body: JSON.stringify(postData),
35
- headers: {
36
- Authorization: `Bearer ${token}`,
37
- "Content-Type": "application/json"
88
+ function createPublisher(platform) {
89
+ const isReady = async (token, id, label, wait) => {
90
+ const state = await waitForContainer(
91
+ () => platform.getStatus(token, id),
92
+ wait
93
+ );
94
+ if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
95
+ console.error(
96
+ `[naystack] ${platform.name} ${label} ${id} is ${state.status}${state.error ? `: ${state.error}` : ""}`
97
+ );
98
+ return false;
99
+ }
100
+ return true;
101
+ };
102
+ return {
103
+ /** Creates a container, waits for it to finish processing, then publishes it. */
104
+ publish: async (token, params, wait) => {
105
+ const containerID = await platform.createContainer(token, params);
106
+ if (!containerID) return null;
107
+ if (!await isReady(token, containerID, "container", wait)) return null;
108
+ return platform.publish(token, containerID);
109
+ },
110
+ /** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
111
+ createChildren: async (token, items, wait) => {
112
+ const children = [];
113
+ for (const item of items) {
114
+ const childID = await platform.createContainer(token, {
115
+ ...item,
116
+ is_carousel_item: true
117
+ });
118
+ if (!childID) return null;
119
+ if (!await isReady(token, childID, "carousel item", wait))
120
+ return null;
121
+ children.push(childID);
122
+ }
123
+ return children;
38
124
  }
39
- }).then((res) => res.json());
125
+ };
40
126
  }
41
127
 
42
128
  // src/socials/instagram/setters.ts
129
+ var publisher = createPublisher({
130
+ name: "Instagram",
131
+ getStatus: getInstagramContainerStatus,
132
+ createContainer: async (token, params) => readGraphID(
133
+ "Instagram media container",
134
+ await getInstagramData(token, "me/media", {
135
+ params,
136
+ method: "POST"
137
+ })
138
+ ),
139
+ publish: async (token, creationID) => readGraphID(
140
+ "Instagram publish",
141
+ await getInstagramData(token, "me/media_publish", {
142
+ params: { creation_id: creationID },
143
+ method: "POST"
144
+ })
145
+ )
146
+ });
147
+ var mediaParams = (media) => ({
148
+ media_type: media.type === "video" /* Video */ ? "VIDEO" : void 0,
149
+ image_url: media.type === "photo" /* Photo */ ? media.url : void 0,
150
+ video_url: media.type === "video" /* Video */ ? media.url : void 0,
151
+ // Instagram accepts alt text on images only.
152
+ alt_text: media.type === "photo" /* Photo */ ? media.altText : void 0
153
+ });
154
+ var createInstagramPost = async (token, input) => {
155
+ const media = [input.media].flat();
156
+ const first = media[0];
157
+ if (!first) {
158
+ console.error(
159
+ "[naystack] Instagram posts need at least one image or video"
160
+ );
161
+ return null;
162
+ }
163
+ if (input.story) {
164
+ return publisher.publish(
165
+ token,
166
+ {
167
+ ...mediaParams(first),
168
+ media_type: "STORIES",
169
+ alt_text: void 0
170
+ },
171
+ input.wait
172
+ );
173
+ }
174
+ if (media.length > 1) {
175
+ const children = await publisher.createChildren(
176
+ token,
177
+ media.map(mediaParams),
178
+ input.wait
179
+ );
180
+ if (!children) return null;
181
+ return publisher.publish(
182
+ token,
183
+ {
184
+ media_type: "CAROUSEL",
185
+ children: children.join(","),
186
+ caption: input.caption
187
+ },
188
+ input.wait
189
+ );
190
+ }
191
+ if (first.type === "video" /* Video */) {
192
+ return publisher.publish(
193
+ token,
194
+ {
195
+ media_type: "REELS",
196
+ video_url: first.url,
197
+ caption: input.caption,
198
+ cover_url: input.coverURL,
199
+ thumb_offset: input.thumbOffset,
200
+ share_to_feed: input.shareToFeed,
201
+ audio_name: input.audioName,
202
+ collaborators: input.collaborators && JSON.stringify(input.collaborators)
203
+ },
204
+ input.wait
205
+ );
206
+ }
207
+ return publisher.publish(
208
+ token,
209
+ {
210
+ image_url: first.url,
211
+ caption: input.caption,
212
+ alt_text: first.altText,
213
+ location_id: input.locationID
214
+ },
215
+ input.wait
216
+ );
217
+ };
43
218
  var sendInstagramMessage = (token, to, text) => {
44
- return getInstagramData(token, "me/messages", void 0, {
45
- recipient: { id: to },
46
- message: {
47
- text
219
+ return getInstagramData(token, "me/messages", {
220
+ body: {
221
+ recipient: { id: to },
222
+ message: {
223
+ text
224
+ }
48
225
  }
49
226
  });
50
227
  };
51
228
  // Annotate the CommonJS export names for ESM import in node:
52
229
  0 && (module.exports = {
230
+ createInstagramPost,
53
231
  sendInstagramMessage
54
232
  });
@@ -1,5 +1,45 @@
1
- import { InstagramError } from './types.mjs';
1
+ import { GraphError } from '../meta/types.mjs';
2
+ import { InstagramPostInput } from './types.mjs';
3
+ import '../meta/container.mjs';
2
4
 
5
+ /**
6
+ * Publishes to Instagram — feed image, reel, story or carousel, chosen from the
7
+ * media you pass. Creates the media container, waits for Instagram to finish
8
+ * processing it, then publishes.
9
+ *
10
+ * Needs an access token with the `instagram_business_content_publish` scope and an
11
+ * Instagram professional account. Media URLs must be publicly reachable (Instagram
12
+ * downloads them server-side) and images must be JPEG. Instagram allows 100
13
+ * published posts per rolling 24 hours.
14
+ *
15
+ * @param token - Instagram access token.
16
+ * @param input - See {@link InstagramPostInput}.
17
+ * @returns Promise of the published media id, or `null` if any step failed (the API's error is logged).
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { createInstagramPost } from "naystack/socials";
22
+ *
23
+ * // Reel
24
+ * await createInstagramPost(accessToken, {
25
+ * media: { url: "https://cdn.example.com/promo.mp4", type: "video" },
26
+ * caption: "Behind the scenes",
27
+ * shareToFeed: true,
28
+ * });
29
+ *
30
+ * // Carousel
31
+ * await createInstagramPost(accessToken, {
32
+ * caption: "Campaign recap",
33
+ * media: [
34
+ * { url: "https://cdn.example.com/1.jpg", type: "image" },
35
+ * { url: "https://cdn.example.com/2.mp4", type: "video" },
36
+ * ],
37
+ * });
38
+ * ```
39
+ *
40
+ * @category Socials
41
+ */
42
+ declare const createInstagramPost: (token: string, input: InstagramPostInput) => Promise<string | null>;
3
43
  /**
4
44
  * Sends a text message to an Instagram user via the Instagram Messaging API.
5
45
  *
@@ -21,6 +61,6 @@ import { InstagramError } from './types.mjs';
21
61
  declare const sendInstagramMessage: (token: string, to: string, text: string) => Promise<({
22
62
  recipient_id?: string;
23
63
  message_id?: string;
24
- } & InstagramError) | null>;
64
+ } & GraphError) | null>;
25
65
 
26
- export { sendInstagramMessage };
66
+ export { createInstagramPost, sendInstagramMessage };
@@ -1,5 +1,45 @@
1
- import { InstagramError } from './types.js';
1
+ import { GraphError } from '../meta/types.js';
2
+ import { InstagramPostInput } from './types.js';
3
+ import '../meta/container.js';
2
4
 
5
+ /**
6
+ * Publishes to Instagram — feed image, reel, story or carousel, chosen from the
7
+ * media you pass. Creates the media container, waits for Instagram to finish
8
+ * processing it, then publishes.
9
+ *
10
+ * Needs an access token with the `instagram_business_content_publish` scope and an
11
+ * Instagram professional account. Media URLs must be publicly reachable (Instagram
12
+ * downloads them server-side) and images must be JPEG. Instagram allows 100
13
+ * published posts per rolling 24 hours.
14
+ *
15
+ * @param token - Instagram access token.
16
+ * @param input - See {@link InstagramPostInput}.
17
+ * @returns Promise of the published media id, or `null` if any step failed (the API's error is logged).
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { createInstagramPost } from "naystack/socials";
22
+ *
23
+ * // Reel
24
+ * await createInstagramPost(accessToken, {
25
+ * media: { url: "https://cdn.example.com/promo.mp4", type: "video" },
26
+ * caption: "Behind the scenes",
27
+ * shareToFeed: true,
28
+ * });
29
+ *
30
+ * // Carousel
31
+ * await createInstagramPost(accessToken, {
32
+ * caption: "Campaign recap",
33
+ * media: [
34
+ * { url: "https://cdn.example.com/1.jpg", type: "image" },
35
+ * { url: "https://cdn.example.com/2.mp4", type: "video" },
36
+ * ],
37
+ * });
38
+ * ```
39
+ *
40
+ * @category Socials
41
+ */
42
+ declare const createInstagramPost: (token: string, input: InstagramPostInput) => Promise<string | null>;
3
43
  /**
4
44
  * Sends a text message to an Instagram user via the Instagram Messaging API.
5
45
  *
@@ -21,6 +61,6 @@ import { InstagramError } from './types.js';
21
61
  declare const sendInstagramMessage: (token: string, to: string, text: string) => Promise<({
22
62
  recipient_id?: string;
23
63
  message_id?: string;
24
- } & InstagramError) | null>;
64
+ } & GraphError) | null>;
25
65
 
26
- export { sendInstagramMessage };
66
+ export { createInstagramPost, sendInstagramMessage };