@opencoredev/social-sdk 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.md +4 -2
  2. package/package.json +1 -1
  3. package/dist/cli.d.ts +0 -14
  4. package/dist/cli.d.ts.map +0 -1
  5. package/dist/cli.js +0 -223
  6. package/dist/cli.js.map +0 -1
  7. package/dist/cloud/common.d.ts +0 -26
  8. package/dist/cloud/common.d.ts.map +0 -1
  9. package/dist/cloud/common.js +0 -307
  10. package/dist/cloud/common.js.map +0 -1
  11. package/dist/cloud/lifecycle.d.ts +0 -11
  12. package/dist/cloud/lifecycle.d.ts.map +0 -1
  13. package/dist/cloud/lifecycle.js +0 -108
  14. package/dist/cloud/lifecycle.js.map +0 -1
  15. package/dist/cloud/media.d.ts +0 -24
  16. package/dist/cloud/media.d.ts.map +0 -1
  17. package/dist/cloud/media.js +0 -97
  18. package/dist/cloud/media.js.map +0 -1
  19. package/dist/cloud/outcomes.d.ts +0 -10
  20. package/dist/cloud/outcomes.d.ts.map +0 -1
  21. package/dist/cloud/outcomes.js +0 -188
  22. package/dist/cloud/outcomes.js.map +0 -1
  23. package/dist/cloud/post-for-me.d.ts +0 -70
  24. package/dist/cloud/post-for-me.d.ts.map +0 -1
  25. package/dist/cloud/post-for-me.js +0 -326
  26. package/dist/cloud/post-for-me.js.map +0 -1
  27. package/dist/cloud/zernio.d.ts +0 -112
  28. package/dist/cloud/zernio.d.ts.map +0 -1
  29. package/dist/cloud/zernio.js +0 -542
  30. package/dist/cloud/zernio.js.map +0 -1
  31. package/dist/core/adapter.d.ts +0 -118
  32. package/dist/core/adapter.d.ts.map +0 -1
  33. package/dist/core/adapter.js +0 -4
  34. package/dist/core/adapter.js.map +0 -1
  35. package/dist/core/client.d.ts +0 -108
  36. package/dist/core/client.d.ts.map +0 -1
  37. package/dist/core/client.js +0 -893
  38. package/dist/core/client.js.map +0 -1
  39. package/dist/core/concurrency.d.ts +0 -10
  40. package/dist/core/concurrency.d.ts.map +0 -1
  41. package/dist/core/concurrency.js +0 -78
  42. package/dist/core/concurrency.js.map +0 -1
  43. package/dist/core/errors.d.ts +0 -45
  44. package/dist/core/errors.d.ts.map +0 -1
  45. package/dist/core/errors.js +0 -44
  46. package/dist/core/errors.js.map +0 -1
  47. package/dist/core/idempotency.d.ts +0 -34
  48. package/dist/core/idempotency.d.ts.map +0 -1
  49. package/dist/core/idempotency.js +0 -59
  50. package/dist/core/idempotency.js.map +0 -1
  51. package/dist/core/index.d.ts +0 -7
  52. package/dist/core/index.d.ts.map +0 -1
  53. package/dist/core/index.js +0 -7
  54. package/dist/core/index.js.map +0 -1
  55. package/dist/core/pagination.d.ts +0 -12
  56. package/dist/core/pagination.d.ts.map +0 -1
  57. package/dist/core/pagination.js +0 -81
  58. package/dist/core/pagination.js.map +0 -1
  59. package/dist/core/types.d.ts +0 -329
  60. package/dist/core/types.d.ts.map +0 -1
  61. package/dist/core/types.js +0 -7
  62. package/dist/core/types.js.map +0 -1
  63. package/dist/index.d.ts +0 -2
  64. package/dist/index.d.ts.map +0 -1
  65. package/dist/index.js +0 -2
  66. package/dist/index.js.map +0 -1
  67. package/dist/platforms/bluesky.d.ts +0 -132
  68. package/dist/platforms/bluesky.d.ts.map +0 -1
  69. package/dist/platforms/bluesky.js +0 -1049
  70. package/dist/platforms/bluesky.js.map +0 -1
  71. package/dist/platforms/instagram.d.ts +0 -63
  72. package/dist/platforms/instagram.d.ts.map +0 -1
  73. package/dist/platforms/instagram.js +0 -676
  74. package/dist/platforms/instagram.js.map +0 -1
  75. package/dist/platforms/linkedin.d.ts +0 -54
  76. package/dist/platforms/linkedin.d.ts.map +0 -1
  77. package/dist/platforms/linkedin.js +0 -580
  78. package/dist/platforms/linkedin.js.map +0 -1
  79. package/dist/platforms/threads.d.ts +0 -102
  80. package/dist/platforms/threads.d.ts.map +0 -1
  81. package/dist/platforms/threads.js +0 -571
  82. package/dist/platforms/threads.js.map +0 -1
  83. package/dist/platforms/tiktok.d.ts +0 -32
  84. package/dist/platforms/tiktok.d.ts.map +0 -1
  85. package/dist/platforms/tiktok.js +0 -511
  86. package/dist/platforms/tiktok.js.map +0 -1
  87. package/dist/platforms/x-engagement.d.ts +0 -17
  88. package/dist/platforms/x-engagement.d.ts.map +0 -1
  89. package/dist/platforms/x-engagement.js +0 -47
  90. package/dist/platforms/x-engagement.js.map +0 -1
  91. package/dist/platforms/x.d.ts +0 -82
  92. package/dist/platforms/x.d.ts.map +0 -1
  93. package/dist/platforms/x.js +0 -511
  94. package/dist/platforms/x.js.map +0 -1
  95. package/dist/platforms/youtube-upload.d.ts +0 -33
  96. package/dist/platforms/youtube-upload.d.ts.map +0 -1
  97. package/dist/platforms/youtube-upload.js +0 -281
  98. package/dist/platforms/youtube-upload.js.map +0 -1
  99. package/dist/platforms/youtube.d.ts +0 -55
  100. package/dist/platforms/youtube.d.ts.map +0 -1
  101. package/dist/platforms/youtube.js +0 -561
  102. package/dist/platforms/youtube.js.map +0 -1
  103. package/dist/server/connections.d.ts +0 -124
  104. package/dist/server/connections.d.ts.map +0 -1
  105. package/dist/server/connections.js +0 -334
  106. package/dist/server/connections.js.map +0 -1
  107. package/dist/server/credentials.d.ts +0 -52
  108. package/dist/server/credentials.d.ts.map +0 -1
  109. package/dist/server/credentials.js +0 -115
  110. package/dist/server/credentials.js.map +0 -1
  111. package/dist/server/index.d.ts +0 -5
  112. package/dist/server/index.d.ts.map +0 -1
  113. package/dist/server/index.js +0 -5
  114. package/dist/server/index.js.map +0 -1
  115. package/dist/server/oauth.d.ts +0 -48
  116. package/dist/server/oauth.d.ts.map +0 -1
  117. package/dist/server/oauth.js +0 -483
  118. package/dist/server/oauth.js.map +0 -1
  119. package/dist/server/webhooks.d.ts +0 -58
  120. package/dist/server/webhooks.d.ts.map +0 -1
  121. package/dist/server/webhooks.js +0 -198
  122. package/dist/server/webhooks.js.map +0 -1
  123. package/dist/testing/index.d.ts +0 -47
  124. package/dist/testing/index.d.ts.map +0 -1
  125. package/dist/testing/index.js +0 -413
  126. package/dist/testing/index.js.map +0 -1
  127. package/dist/testing.d.ts +0 -2
  128. package/dist/testing.d.ts.map +0 -1
  129. package/dist/testing.js +0 -2
  130. package/dist/testing.js.map +0 -1
  131. package/dist/transport/binary.d.ts +0 -3
  132. package/dist/transport/binary.d.ts.map +0 -1
  133. package/dist/transport/binary.js +0 -30
  134. package/dist/transport/binary.js.map +0 -1
  135. package/dist/transport/budget.d.ts +0 -4
  136. package/dist/transport/budget.d.ts.map +0 -1
  137. package/dist/transport/budget.js +0 -27
  138. package/dist/transport/budget.js.map +0 -1
  139. package/dist/transport/http.d.ts +0 -44
  140. package/dist/transport/http.d.ts.map +0 -1
  141. package/dist/transport/http.js +0 -230
  142. package/dist/transport/http.js.map +0 -1
  143. package/dist/transport/json.d.ts +0 -3
  144. package/dist/transport/json.d.ts.map +0 -1
  145. package/dist/transport/json.js +0 -11
  146. package/dist/transport/json.js.map +0 -1
  147. package/dist/transport/upload.d.ts +0 -25
  148. package/dist/transport/upload.d.ts.map +0 -1
  149. package/dist/transport/upload.js +0 -133
  150. package/dist/transport/upload.js.map +0 -1
  151. package/dist/transport/validation.d.ts +0 -6
  152. package/dist/transport/validation.d.ts.map +0 -1
  153. package/dist/transport/validation.js +0 -24
  154. package/dist/transport/validation.js.map +0 -1
@@ -1,676 +0,0 @@
1
- import { defineAdapter } from "../core/adapter.js";
2
- import { SocialError } from "../core/errors.js";
3
- import { managedHttp, publicFields } from "../cloud/common.js";
4
- import { array, object, optionalNumber, optionalString, string } from "../transport/validation.js";
5
- import { httpsUrl } from "../transport/upload.js";
6
- class MemoryInstagramWorkflowStore {
7
- workflows = new Map();
8
- claims = new Set();
9
- async create(input) {
10
- const workflow = { ...input, id: `igwf_${globalThis.crypto.randomUUID()}` };
11
- this.workflows.set(workflow.id, workflow);
12
- return workflow;
13
- }
14
- async get(id) {
15
- return this.workflows.get(id);
16
- }
17
- async update(id, update) {
18
- const current = this.workflows.get(id);
19
- if (!current)
20
- throw new Error("Instagram workflow not found");
21
- const next = { ...current, ...update, id };
22
- this.workflows.set(id, next);
23
- return next;
24
- }
25
- async claim(id) {
26
- if (!this.workflows.has(id) || this.claims.has(id))
27
- return false;
28
- this.claims.add(id);
29
- return true;
30
- }
31
- async release(id) {
32
- this.claims.delete(id);
33
- }
34
- }
35
- export function instagram(options) {
36
- const apiVersion = "v25.0";
37
- const request = managedHttp(`https://graph.instagram.com/${apiVersion}`, {
38
- apiKey: options.auth.accessToken,
39
- ...(options.fetch ? { fetch: options.fetch } : {}),
40
- });
41
- const now = () => (options.clock?.() ?? new Date()).toISOString();
42
- const workflows = options.workflowStore ?? new MemoryInstagramWorkflowStore();
43
- const authorize = (ref, context) => {
44
- if (ref.backend !== context.backendInstance ||
45
- ref.platform !== "instagram" ||
46
- ref.accountId !== options.auth.accountId)
47
- throw new SocialError({
48
- code: "unauthorized",
49
- operation: "instagram",
50
- message: "Account reference does not match this Instagram Login authorization.",
51
- });
52
- };
53
- const status = async (id, context) => object(await request(`/${encodeURIComponent(id)}`, context, undefined, {
54
- fields: "id,status_code,status",
55
- }));
56
- const listPosts = async (selected, input, context) => {
57
- authorize(selected, context);
58
- if (input.limit !== undefined &&
59
- (!Number.isSafeInteger(input.limit) || input.limit < 1 || input.limit > 100))
60
- throw new SocialError({
61
- code: "invalid_input",
62
- operation: "posts.list",
63
- message: "Instagram feed limit must be an integer from 1 through 100.",
64
- });
65
- const query = {
66
- fields: "id,caption,media_type,media_product_type,permalink,timestamp,username",
67
- };
68
- if (input.cursor !== undefined)
69
- query["after"] = input.cursor;
70
- if (input.limit !== undefined)
71
- query["limit"] = String(input.limit);
72
- const result = object(await request(`/${encodeURIComponent(selected.accountId)}/media`, context, undefined, query));
73
- const items = array(result["data"]).map((entry) => publicFields(entry, [
74
- "id",
75
- "caption",
76
- "media_type",
77
- "media_product_type",
78
- "permalink",
79
- "timestamp",
80
- "username",
81
- ]));
82
- const paging = result["paging"] === undefined ? {} : object(result["paging"]);
83
- const cursors = paging["cursors"] === undefined ? {} : object(paging["cursors"]);
84
- const hasNext = typeof paging["next"] === "string" && paging["next"].length > 0;
85
- const nextCursor = hasNext && typeof cursors["after"] === "string" ? cursors["after"] : undefined;
86
- return {
87
- items,
88
- ...(nextCursor === undefined ? {} : { nextCursor }),
89
- };
90
- };
91
- const getAccountMetrics = async (selected, context) => {
92
- authorize(selected, context);
93
- const result = object(await request("/me", context, undefined, {
94
- fields: "user_id,followers_count,media_count",
95
- }));
96
- const id = optionalString(result["user_id"]) ?? optionalString(result["id"]);
97
- if (id !== selected.accountId)
98
- throw new SocialError({
99
- code: "unauthorized",
100
- operation: "analytics.read",
101
- message: "Instagram returned metrics for a different account.",
102
- });
103
- return ["followers_count", "media_count"].flatMap((name) => {
104
- const value = optionalNumber(result[name]);
105
- return value === undefined
106
- ? []
107
- : [
108
- {
109
- name,
110
- value,
111
- unit: "count",
112
- period: "lifetime",
113
- fetchedAt: now(),
114
- freshness: "unknown",
115
- source: "Instagram Login Graph v25.0",
116
- },
117
- ];
118
- });
119
- };
120
- const publishContainer = async (account, containerId, context, workflowId) => {
121
- authorize(account, context);
122
- if (workflowId) {
123
- const workflow = await workflows.get(workflowId);
124
- if (workflow?.nativeId)
125
- return {
126
- ...processing(account, workflowId, "PUBLISHED"),
127
- state: "published",
128
- post: {
129
- kind: "platform-post",
130
- version: 1,
131
- backend: account.backend,
132
- platform: "instagram",
133
- accountId: account.accountId,
134
- postId: workflow.nativeId,
135
- },
136
- };
137
- }
138
- const state = await status(containerId, context);
139
- const code = string(state["status_code"]);
140
- const base = {
141
- account,
142
- targetIndex: 0,
143
- observedAt: now(),
144
- backendState: code,
145
- delivery: {
146
- kind: "delivery",
147
- version: 1,
148
- backend: account.backend,
149
- platform: "instagram",
150
- accountId: account.accountId,
151
- deliveryId: containerId,
152
- },
153
- };
154
- if (code === "IN_PROGRESS")
155
- return { ...base, state: "processing" };
156
- if (code === "ERROR" || code === "EXPIRED")
157
- return {
158
- ...base,
159
- state: "failed",
160
- code: "media_error",
161
- message: "Instagram container failed or expired before publication.",
162
- retryDisposition: { kind: "never" },
163
- };
164
- if (code === "PUBLISHED")
165
- return {
166
- ...base,
167
- state: "unknown",
168
- reason: "unmapped-state",
169
- diagnostic: "Instagram reports a published container but the native media ID was not persisted by this workflow.",
170
- };
171
- if (code !== "FINISHED")
172
- return {
173
- ...base,
174
- state: "unknown",
175
- reason: "unmapped-state",
176
- diagnostic: "Container is already published or has an unmapped state. No repeated publish was dispatched.",
177
- };
178
- if (workflowId)
179
- await workflows.update(workflowId, { stage: "unknown" });
180
- let result;
181
- try {
182
- result = object(await request(`/${encodeURIComponent(account.accountId)}/media_publish`, context, {
183
- creation_id: containerId,
184
- }));
185
- }
186
- catch (error) {
187
- if (workflowId)
188
- await workflows.update(workflowId, { stage: "unknown" });
189
- throw error;
190
- }
191
- const nativeId = string(result["id"]);
192
- if (workflowId)
193
- await workflows.update(workflowId, { nativeId, stage: "published", parentId: containerId });
194
- return {
195
- ...base,
196
- state: "published",
197
- backendState: "PUBLISHED",
198
- post: {
199
- kind: "platform-post",
200
- version: 1,
201
- backend: account.backend,
202
- platform: "instagram",
203
- accountId: account.accountId,
204
- postId: nativeId,
205
- },
206
- };
207
- };
208
- const processing = (account, workflowId, backendState) => ({
209
- state: "processing",
210
- targetIndex: 0,
211
- account,
212
- observedAt: now(),
213
- backendState,
214
- delivery: {
215
- kind: "delivery",
216
- version: 1,
217
- backend: account.backend,
218
- platform: "instagram",
219
- accountId: account.accountId,
220
- deliveryId: workflowId,
221
- },
222
- });
223
- const continueWorkflow = async (workflow, account, context) => {
224
- if (workflow.nativeId)
225
- return {
226
- ...processing(account, workflow.id, "PUBLISHED"),
227
- state: "published",
228
- post: {
229
- kind: "platform-post",
230
- version: 1,
231
- backend: account.backend,
232
- platform: "instagram",
233
- accountId: account.accountId,
234
- postId: workflow.nativeId,
235
- },
236
- };
237
- if (workflow.stage === "unknown")
238
- return {
239
- ...processing(account, workflow.id, "AMBIGUOUS"),
240
- state: "unknown",
241
- reason: "ambiguous-submission",
242
- diagnostic: "Instagram publish acceptance is unknown; no replay was attempted.",
243
- };
244
- if (workflow.parentId)
245
- return publishContainer(account, workflow.parentId, context, workflow.id);
246
- for (const child of workflow.childIds) {
247
- const childStatus = await status(child, context);
248
- const code = string(childStatus["status_code"]);
249
- if (code !== "FINISHED")
250
- return processing(account, workflow.id, code);
251
- }
252
- await workflows.update(workflow.id, { stage: "unknown" });
253
- const parent = object(await request(`/${encodeURIComponent(account.accountId)}/media`, context, {
254
- media_type: "CAROUSEL",
255
- children: workflow.childIds.join(","),
256
- caption: workflow.caption,
257
- }));
258
- const parentId = string(parent["id"]);
259
- await workflows.update(workflow.id, { parentId, stage: "parent" });
260
- return publishContainer(account, parentId, context, workflow.id);
261
- };
262
- const resumeWorkflow = async (workflow, account, context) => {
263
- if (!(await workflows.claim(workflow.id)))
264
- return processing(account, workflow.id, "CLAIMED");
265
- try {
266
- const current = (await workflows.get(workflow.id)) ?? workflow;
267
- return await continueWorkflow(current, account, context);
268
- }
269
- finally {
270
- await workflows.release?.(workflow.id);
271
- }
272
- };
273
- const readAccount = async (context) => {
274
- const response = object(await request("/me", context, undefined, { fields: "user_id,username,account_type" }));
275
- const id = optionalString(response["user_id"]) ?? optionalString(response["id"]);
276
- if (id !== options.auth.accountId)
277
- throw new SocialError({
278
- code: "unauthorized",
279
- operation: "accounts.read",
280
- message: "Instagram returned an account different from the configured professional account.",
281
- });
282
- return {
283
- ref: {
284
- kind: "connected-account",
285
- version: 1,
286
- backend: context.backendInstance,
287
- platform: "instagram",
288
- accountId: id,
289
- },
290
- displayName: string(response["username"]),
291
- status: "connected",
292
- };
293
- };
294
- return defineAdapter({
295
- id: "instagram",
296
- capabilities: {
297
- schemaVersion: 1,
298
- backend: "instagram",
299
- apiRevision: "Instagram Login Graph v25.0",
300
- runtime: ["node22", "node24", "bun"],
301
- capabilities: [
302
- {
303
- platform: "instagram",
304
- operation: "posts.publish",
305
- availability: "available",
306
- formats: ["image", "video", "carousel"],
307
- requiredScopes: ["instagram_business_basic", "instagram_business_content_publish"],
308
- notes: "Professional accounts, public HTTPS media, explicit native continuation for processing containers. Carousels must have matching aspect ratios to avoid upstream cropping.",
309
- },
310
- ...[
311
- "accounts.read",
312
- "posts.list",
313
- "posts.read",
314
- "posts.status",
315
- "comments.read",
316
- "comments.write",
317
- "analytics.read",
318
- ].map((operation) => ({
319
- operation,
320
- platform: "instagram",
321
- availability: "available",
322
- })),
323
- {
324
- platform: "instagram",
325
- operation: "reels.publish",
326
- availability: "available",
327
- formats: ["video"],
328
- },
329
- { platform: "instagram", operation: "stories.publish", availability: "available" },
330
- { platform: "instagram", operation: "posts.delete", availability: "available" },
331
- { platform: "instagram", operation: "hashtags.search", availability: "available" },
332
- {
333
- platform: "instagram",
334
- operation: "publishing.limit.read",
335
- availability: "available",
336
- },
337
- { platform: "instagram", operation: "mentions.read", availability: "available" },
338
- {
339
- platform: "instagram",
340
- operation: "product.tagging",
341
- availability: "approval-dependent",
342
- },
343
- {
344
- platform: "instagram",
345
- operation: "messages.read",
346
- availability: "approval-dependent",
347
- },
348
- ],
349
- },
350
- accounts: {
351
- async list(_input, context) {
352
- return { items: [await readAccount(context)] };
353
- },
354
- async get(ref, context) {
355
- authorize(ref, context);
356
- return readAccount(context);
357
- },
358
- },
359
- posts: {
360
- list: listPosts,
361
- prepareTarget(target) {
362
- const issues = [];
363
- const fail = (code, message) => issues.push({ code, message, severity: "error", targetIndex: target.targetIndex });
364
- if (target.account.platform !== "instagram" ||
365
- target.account.accountId !== options.auth.accountId)
366
- fail("instagram.account", "Select the configured professional Instagram account.");
367
- const media = target.content.media ?? [];
368
- if (media.length < 1 || media.length > 10)
369
- fail("instagram.media", "Instagram requires 1 to 10 image/video attachments.");
370
- const firstRatio = media[0]?.width && media[0]?.height ? media[0].width / media[0].height : undefined;
371
- for (const item of media) {
372
- if (item.source.kind !== "https-url")
373
- fail("instagram.source", "Instagram Login publishing requires public HTTPS media URLs.");
374
- else
375
- try {
376
- httpsUrl(item.source.url);
377
- }
378
- catch {
379
- fail("instagram.url", "Use public HTTPS media without local hosts or credentials.");
380
- }
381
- if (item.kind === "image" && item.mimeType !== "image/jpeg")
382
- fail("instagram.jpeg", "Instagram image publishing requires JPEG media.");
383
- if (item.kind === "video" &&
384
- item.mimeType !== "video/mp4" &&
385
- item.mimeType !== "video/quicktime")
386
- fail("instagram.video", "Provide MP4 or MOV video with a supported codec.");
387
- if (media.length > 1 &&
388
- (!item.width ||
389
- !item.height ||
390
- firstRatio === undefined ||
391
- Math.abs(item.width / item.height - firstRatio) > 0.001))
392
- fail("instagram.carousel_crop", "Provide matching known aspect ratios for every carousel item; the SDK will not silently accept cropping.");
393
- }
394
- if ((target.content.text?.length ?? 0) > 2200)
395
- fail("instagram.caption", "Caption exceeds 2,200 characters.");
396
- if (target.schedule || target.replyTo || target.content.link)
397
- fail("instagram.operation", "Use an explicit job runner, comment reply, or caption link instead of unsupported structured options.");
398
- return issues;
399
- },
400
- async publishTarget(target, context) {
401
- authorize(target.account, context);
402
- const media = target.content.media ?? [];
403
- const children = [];
404
- const workflow = await workflows.create({
405
- backend: target.account.backend,
406
- accountId: target.account.accountId,
407
- childIds: [],
408
- caption: target.content.text ?? "",
409
- stage: "children",
410
- });
411
- try {
412
- for (const item of media) {
413
- if (item.source.kind !== "https-url")
414
- throw new SocialError({
415
- code: "invalid_input",
416
- operation: "posts.publish",
417
- message: "Public HTTPS media required.",
418
- });
419
- const config = target.options === undefined ? {} : object(target.options);
420
- await workflows.update(workflow.id, { stage: "unknown" });
421
- const created = object(await request(`/${encodeURIComponent(target.account.accountId)}/media`, context, {
422
- ...(item.kind === "image"
423
- ? {
424
- image_url: item.source.url,
425
- ...(item.altText === undefined ? {} : { alt_text: item.altText }),
426
- }
427
- : {
428
- video_url: item.source.url,
429
- media_type: media.length > 1 ? "VIDEO" : "REELS",
430
- ...(typeof config["shareToFeed"] === "boolean"
431
- ? { share_to_feed: config["shareToFeed"] }
432
- : {}),
433
- }),
434
- ...(media.length > 1
435
- ? { is_carousel_item: true }
436
- : { caption: target.content.text ?? "" }),
437
- }));
438
- children.push(string(created["id"]));
439
- await workflows.update(workflow.id, { childIds: [...children], stage: "children" });
440
- }
441
- }
442
- catch (error) {
443
- if (error instanceof SocialError)
444
- throw new SocialError({
445
- code: "ambiguous_outcome",
446
- operation: "instagram.posts.publish",
447
- message: error.message,
448
- details: {
449
- createdContainerIds: children,
450
- workflowId: workflow.id,
451
- },
452
- retryDisposition: { kind: "reconcile-first" },
453
- cause: error,
454
- });
455
- throw error;
456
- }
457
- let containerId = children[0];
458
- if (!containerId)
459
- throw new Error("Missing Instagram container");
460
- if (children.length > 1) {
461
- const result = await resumeWorkflow(workflow, target.account, context);
462
- return { ...result, targetIndex: target.targetIndex };
463
- }
464
- await workflows.update(workflow.id, { parentId: containerId, stage: "parent" });
465
- const result = await publishContainer(target.account, containerId, context, workflow.id);
466
- return { ...result, targetIndex: target.targetIndex };
467
- },
468
- async get(ref, context) {
469
- authorize(ref, context);
470
- return publicFields(await request(`/${encodeURIComponent(ref.postId)}`, context, undefined, {
471
- fields: "id,caption,media_type,media_product_type,permalink,timestamp,username",
472
- }), [
473
- "id",
474
- "caption",
475
- "media_type",
476
- "media_product_type",
477
- "permalink",
478
- "timestamp",
479
- "username",
480
- ]);
481
- },
482
- async getDelivery(ref, context) {
483
- authorize(ref, context);
484
- const workflow = await workflows.get(ref.deliveryId);
485
- if (workflow) {
486
- if (workflow.backend !== ref.backend || workflow.accountId !== ref.accountId)
487
- throw new SocialError({
488
- code: "unauthorized",
489
- operation: "instagram.posts.status",
490
- message: "Instagram workflow handle is not authorized for this account.",
491
- });
492
- const account = {
493
- kind: "connected-account",
494
- version: 1,
495
- backend: ref.backend,
496
- platform: "instagram",
497
- accountId: ref.accountId,
498
- };
499
- if (workflow.nativeId)
500
- return {
501
- ...processing(account, workflow.id, "PUBLISHED"),
502
- state: "published",
503
- post: {
504
- kind: "platform-post",
505
- version: 1,
506
- backend: ref.backend,
507
- platform: "instagram",
508
- accountId: ref.accountId,
509
- postId: workflow.nativeId,
510
- },
511
- };
512
- if (workflow.stage === "unknown")
513
- return {
514
- ...processing(account, workflow.id, "AMBIGUOUS"),
515
- state: "unknown",
516
- reason: "ambiguous-submission",
517
- diagnostic: "Explicit resumePublication is required.",
518
- };
519
- return processing(account, workflow.id, workflow.stage.toUpperCase());
520
- }
521
- const result = await status(ref.deliveryId, context);
522
- const code = string(result["status_code"]);
523
- const base = {
524
- account: {
525
- kind: "connected-account",
526
- version: 1,
527
- backend: ref.backend,
528
- platform: "instagram",
529
- accountId: ref.accountId,
530
- },
531
- targetIndex: 0,
532
- observedAt: now(),
533
- backendState: code,
534
- delivery: { kind: "delivery", version: 1, ...ref },
535
- };
536
- if (code === "IN_PROGRESS" || code === "FINISHED")
537
- return { ...base, state: "processing" };
538
- if (code === "ERROR" || code === "EXPIRED")
539
- return {
540
- ...base,
541
- state: "failed",
542
- code: "media_error",
543
- message: "Instagram container failed or expired.",
544
- retryDisposition: { kind: "never" },
545
- };
546
- return {
547
- ...base,
548
- state: "unknown",
549
- reason: "unmapped-state",
550
- diagnostic: "Container status alone does not identify a published native post. Reconcile stored publish results.",
551
- };
552
- },
553
- },
554
- comments: {
555
- async list(ref, input, context) {
556
- if (input.cursor !== undefined)
557
- throw new SocialError({
558
- code: "unsupported_capability",
559
- operation: "comments.read",
560
- message: "This adapter does not expose upstream comment pagination.",
561
- });
562
- authorize(ref, context);
563
- const result = object(await request(`/${encodeURIComponent(ref.postId)}/comments`, context, undefined, {
564
- fields: "id,text,timestamp,username",
565
- limit: "25",
566
- }));
567
- return {
568
- items: array(result["data"]).map((entry) => publicFields(entry, ["id", "text", "timestamp", "username"])),
569
- };
570
- },
571
- async reply(ref, content, context) {
572
- authorize(ref, context);
573
- const result = object(await request(`/${encodeURIComponent(ref.commentId)}/replies`, context, {
574
- message: content.text,
575
- }));
576
- return { ...ref, commentId: string(result["id"]) };
577
- },
578
- },
579
- analytics: {
580
- getAccountMetrics,
581
- async getPostMetrics(ref, context) {
582
- authorize(ref, context);
583
- const result = object(await request(`/${encodeURIComponent(ref.postId)}/insights`, context, undefined, {
584
- metric: "likes,comments,saved,shares,reach",
585
- }));
586
- return array(result["data"]).flatMap((entry) => {
587
- const row = object(entry);
588
- const value = array(row["values"])[0];
589
- if (!value)
590
- return [];
591
- const count = optionalNumber(object(value)["value"]);
592
- return count === undefined
593
- ? []
594
- : [
595
- {
596
- name: string(row["name"]),
597
- value: count,
598
- unit: "count",
599
- period: "lifetime",
600
- fetchedAt: now(),
601
- freshness: "unknown",
602
- source: `instagram:${apiVersion}`,
603
- },
604
- ];
605
- });
606
- },
607
- },
608
- native: {
609
- publishContainer,
610
- async publishCarousel(account, children, caption, context) {
611
- authorize(account, context);
612
- const workflow = await workflows.create({
613
- backend: account.backend,
614
- accountId: account.accountId,
615
- childIds: [...children],
616
- caption,
617
- stage: "children",
618
- });
619
- return resumeWorkflow(workflow, account, context);
620
- },
621
- async resumePublication(account, workflowId, context) {
622
- authorize(account, context);
623
- const workflow = await workflows.get(workflowId);
624
- if (!workflow ||
625
- workflow.backend !== context.backendInstance ||
626
- workflow.accountId !== account.accountId)
627
- throw new SocialError({
628
- code: "unauthorized",
629
- operation: "instagram.posts.resume",
630
- message: "Instagram workflow handle is not authorized for this account.",
631
- });
632
- return resumeWorkflow(workflow, account, context);
633
- },
634
- async publishReel({ account, videoUrl, caption, context }) {
635
- authorize(account, context);
636
- const created = object(await request(`/${encodeURIComponent(account.accountId)}/media`, context, {
637
- media_type: "REELS",
638
- video_url: videoUrl,
639
- ...(caption ? { caption } : {}),
640
- }));
641
- return publishContainer(account, string(created["id"]), context);
642
- },
643
- async publishStory({ account, mediaUrl, context }) {
644
- authorize(account, context);
645
- const created = object(await request(`/${encodeURIComponent(account.accountId)}/media`, context, {
646
- media_type: "STORIES",
647
- image_url: mediaUrl,
648
- }));
649
- return publishContainer(account, string(created["id"]), context);
650
- },
651
- async deletePost({ account, postId, context }) {
652
- authorize(account, context);
653
- await request(`/${encodeURIComponent(postId)}`, context, undefined, {}, "DELETE");
654
- },
655
- async hashtagSearch({ account, hashtag, context }) {
656
- authorize(account, context);
657
- const tag = object(await request("/ig_hashtag_search", context, undefined, {
658
- user_id: account.accountId,
659
- q: hashtag.replace(/^#/, ""),
660
- }));
661
- return tag;
662
- },
663
- async publishingLimit({ account, context }) {
664
- authorize(account, context);
665
- return object(await request(`/${encodeURIComponent(account.accountId)}/content_publishing_limit`, context, undefined, { fields: "config,quota_usage" }));
666
- },
667
- async mentions({ account, context }) {
668
- authorize(account, context);
669
- return object(await request(`/${encodeURIComponent(account.accountId)}/tags`, context, undefined, {
670
- fields: "id,caption,media_type,timestamp",
671
- }));
672
- },
673
- },
674
- });
675
- }
676
- //# sourceMappingURL=instagram.js.map