naystack 1.8.18 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +94 -34
  2. package/dist/auth/client.cjs.js +2 -2
  3. package/dist/auth/client.esm.js +2 -2
  4. package/dist/auth/email/client.cjs.js +2 -2
  5. package/dist/auth/email/client.esm.js +2 -2
  6. package/dist/auth/email/index.cjs.js +2 -2
  7. package/dist/auth/email/index.esm.js +2 -2
  8. package/dist/auth/email/next.cjs.js +2 -2
  9. package/dist/auth/email/next.esm.js +2 -2
  10. package/dist/auth/email/routes/delete.cjs.js +2 -2
  11. package/dist/auth/email/routes/delete.esm.js +2 -2
  12. package/dist/auth/email/routes/get.cjs.js +2 -2
  13. package/dist/auth/email/routes/get.esm.js +2 -2
  14. package/dist/auth/email/routes/post.cjs.js +2 -2
  15. package/dist/auth/email/routes/post.esm.js +2 -2
  16. package/dist/auth/email/routes/put.cjs.js +2 -2
  17. package/dist/auth/email/routes/put.esm.js +2 -2
  18. package/dist/auth/email/token.cjs.js +2 -2
  19. package/dist/auth/email/token.esm.js +2 -2
  20. package/dist/auth/email/utils.cjs.js +2 -2
  21. package/dist/auth/email/utils.esm.js +2 -2
  22. package/dist/auth/google/get.cjs.js +2 -2
  23. package/dist/auth/google/get.esm.js +2 -2
  24. package/dist/auth/google/index.cjs.js +2 -2
  25. package/dist/auth/google/index.esm.js +2 -2
  26. package/dist/auth/index.cjs.js +4 -297
  27. package/dist/auth/index.d.mts +0 -5
  28. package/dist/auth/index.d.ts +0 -5
  29. package/dist/auth/index.esm.js +3 -293
  30. package/dist/auth/token-store.cjs.js +2 -2
  31. package/dist/auth/token-store.esm.js +2 -2
  32. package/dist/env.cjs.js +3 -3
  33. package/dist/env.d.mts +3 -2
  34. package/dist/env.d.ts +3 -2
  35. package/dist/env.esm.js +3 -3
  36. package/dist/file/client.cjs.js +2 -2
  37. package/dist/file/client.esm.js +2 -2
  38. package/dist/file/index.cjs.js +2 -2
  39. package/dist/file/index.esm.js +2 -2
  40. package/dist/file/put.cjs.js +2 -2
  41. package/dist/file/put.esm.js +2 -2
  42. package/dist/file/setup.cjs.js +2 -2
  43. package/dist/file/setup.esm.js +2 -2
  44. package/dist/file/utils.cjs.js +2 -2
  45. package/dist/file/utils.esm.js +2 -2
  46. package/dist/graphql/client.cjs.js +2 -2
  47. package/dist/graphql/client.esm.js +2 -2
  48. package/dist/graphql/index.cjs.js +2 -2
  49. package/dist/graphql/index.esm.js +2 -2
  50. package/dist/graphql/init.cjs.js +2 -2
  51. package/dist/graphql/init.esm.js +2 -2
  52. package/dist/graphql/links.cjs.js +2 -2
  53. package/dist/graphql/links.esm.js +2 -2
  54. package/dist/graphql/next.cjs.js +2 -2
  55. package/dist/graphql/next.esm.js +2 -2
  56. package/dist/graphql/server.cjs.js +2 -2
  57. package/dist/graphql/server.esm.js +2 -2
  58. package/dist/graphql/utils.cjs.js +2 -2
  59. package/dist/graphql/utils.esm.js +2 -2
  60. package/dist/socials/connect.cjs.js +151 -0
  61. package/dist/socials/connect.d.mts +70 -0
  62. package/dist/socials/connect.d.ts +70 -0
  63. package/dist/socials/connect.esm.js +126 -0
  64. package/dist/socials/index.cjs.js +367 -35
  65. package/dist/socials/index.d.mts +8 -2
  66. package/dist/socials/index.d.ts +8 -2
  67. package/dist/socials/index.esm.js +357 -34
  68. package/dist/socials/instagram/adapter.cjs.js +275 -0
  69. package/dist/socials/instagram/adapter.d.mts +30 -0
  70. package/dist/socials/instagram/adapter.d.ts +30 -0
  71. package/dist/socials/instagram/adapter.esm.js +248 -0
  72. package/dist/{auth/instagram/utils.cjs.js → socials/instagram/auth.cjs.js} +43 -41
  73. package/dist/socials/instagram/auth.d.mts +52 -0
  74. package/dist/socials/instagram/auth.d.ts +52 -0
  75. package/dist/{auth/instagram/utils.esm.js → socials/instagram/auth.esm.js} +37 -35
  76. package/dist/socials/instagram/getters.d.mts +1 -0
  77. package/dist/socials/instagram/getters.d.ts +1 -0
  78. package/dist/socials/instagram/setters.cjs.js +56 -31
  79. package/dist/socials/instagram/setters.d.mts +1 -0
  80. package/dist/socials/instagram/setters.d.ts +1 -0
  81. package/dist/socials/instagram/setters.esm.js +56 -31
  82. package/dist/socials/instagram/types.d.mts +2 -1
  83. package/dist/socials/instagram/types.d.ts +2 -1
  84. package/dist/socials/meta/container.cjs.js +57 -30
  85. package/dist/socials/meta/container.d.mts +15 -28
  86. package/dist/socials/meta/container.d.ts +15 -28
  87. package/dist/socials/meta/container.esm.js +56 -31
  88. package/dist/socials/provider.cjs.js +18 -0
  89. package/dist/socials/provider.d.mts +52 -0
  90. package/dist/socials/provider.d.ts +52 -0
  91. package/dist/socials/provider.esm.js +0 -0
  92. package/dist/socials/threads/getters.d.mts +1 -0
  93. package/dist/socials/threads/getters.d.ts +1 -0
  94. package/dist/socials/threads/setters.cjs.js +56 -31
  95. package/dist/socials/threads/setters.d.mts +1 -0
  96. package/dist/socials/threads/setters.d.ts +1 -0
  97. package/dist/socials/threads/setters.esm.js +56 -31
  98. package/dist/socials/threads/types.d.mts +2 -1
  99. package/dist/socials/threads/types.d.ts +2 -1
  100. package/dist/socials/types.cjs.js +43 -0
  101. package/dist/socials/types.d.mts +99 -0
  102. package/dist/socials/types.d.ts +99 -0
  103. package/dist/socials/types.esm.js +17 -0
  104. package/dist/socials/utils/poll.cjs.js +59 -0
  105. package/dist/socials/utils/poll.d.mts +85 -0
  106. package/dist/socials/utils/poll.d.ts +85 -0
  107. package/dist/socials/utils/poll.esm.js +33 -0
  108. package/dist/utils/client/index.cjs.js +2 -2
  109. package/dist/utils/client/index.esm.js +2 -2
  110. package/dist/utils/client/seo.cjs.js +2 -2
  111. package/dist/utils/client/seo.esm.js +2 -2
  112. package/package.json +1 -1
  113. package/dist/auth/instagram/index.cjs.js +0 -379
  114. package/dist/auth/instagram/index.d.mts +0 -58
  115. package/dist/auth/instagram/index.d.ts +0 -58
  116. package/dist/auth/instagram/index.esm.js +0 -350
  117. package/dist/auth/instagram/route.cjs.js +0 -359
  118. package/dist/auth/instagram/route.d.mts +0 -15
  119. package/dist/auth/instagram/route.d.ts +0 -15
  120. package/dist/auth/instagram/route.esm.js +0 -334
  121. package/dist/auth/instagram/utils.d.mts +0 -57
  122. package/dist/auth/instagram/utils.d.ts +0 -57
@@ -20,11 +20,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/socials/index.ts
21
21
  var socials_exports = {};
22
22
  __export(socials_exports, {
23
+ InstagramProvider: () => InstagramProvider,
23
24
  MetaMediaType: () => MetaMediaType,
25
+ SocialMediaKind: () => SocialMediaKind,
26
+ SocialPlatform: () => SocialPlatform,
24
27
  canPublishToInstagram: () => canPublishToInstagram,
25
28
  createInstagramPost: () => createInstagramPost,
26
29
  createThread: () => createThread,
27
30
  createThreadsPost: () => createThreadsPost,
31
+ getInstagramAuthorizationURL: () => getInstagramAuthorizationURL,
28
32
  getInstagramConversation: () => getInstagramConversation,
29
33
  getInstagramConversationByUser: () => getInstagramConversationByUser,
30
34
  getInstagramConversations: () => getInstagramConversations,
@@ -33,18 +37,203 @@ __export(socials_exports, {
33
37
  getInstagramMedia: () => getInstagramMedia,
34
38
  getInstagramMessage: () => getInstagramMessage,
35
39
  getInstagramUser: () => getInstagramUser,
40
+ getLongLivedInstagramToken: () => getLongLivedInstagramToken,
36
41
  getThread: () => getThread,
37
42
  getThreads: () => getThreads,
38
43
  getThreadsData: () => getThreadsData,
39
44
  getThreadsReplies: () => getThreadsReplies,
45
+ pollUntilReady: () => pollUntilReady,
40
46
  readGraphID: () => readGraphID,
47
+ refreshInstagramAccessToken: () => refreshInstagramAccessToken,
41
48
  replyToInstagramComment: () => replyToInstagramComment,
42
49
  sendInstagramMessage: () => sendInstagramMessage,
43
50
  setupInstagramWebhook: () => setupInstagramWebhook,
44
- setupThreadsWebhook: () => setupThreadsWebhook
51
+ setupSocialAuth: () => setupSocialAuth,
52
+ setupThreadsWebhook: () => setupThreadsWebhook,
53
+ withRetry: () => withRetry
45
54
  });
46
55
  module.exports = __toCommonJS(socials_exports);
47
56
 
57
+ // src/socials/connect.ts
58
+ var import_server2 = require("next/server");
59
+
60
+ // src/auth/email/token.ts
61
+ var import_bcryptjs = require("bcryptjs");
62
+ var import_jsonwebtoken = require("jsonwebtoken");
63
+ var import_headers = require("next/headers");
64
+ var import_navigation = require("next/navigation");
65
+ var import_server = require("next/server");
66
+
67
+ // src/env.ts
68
+ var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
69
+ var getEnvValue = (key) => {
70
+ switch (key) {
71
+ case "NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */:
72
+ return process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || EXTRA_ENV.GRAPHQL_ENDPOINT;
73
+ case "NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */:
74
+ return process.env.NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT || EXTRA_ENV.EMAIL_AUTH_ENDPOINT;
75
+ case "NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT" /* NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT */:
76
+ return process.env.NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT || EXTRA_ENV.GOOGLE_AUTH_ENDPOINT;
77
+ case "NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT" /* NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT */:
78
+ return process.env.NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT || EXTRA_ENV.SOCIAL_AUTH_ENDPOINT;
79
+ case "NEXT_PUBLIC_FILE_ENDPOINT" /* NEXT_PUBLIC_FILE_ENDPOINT */:
80
+ return process.env.NEXT_PUBLIC_FILE_ENDPOINT || EXTRA_ENV.FILE_ENDPOINT;
81
+ case "NEXT_PUBLIC_BASE_URL" /* NEXT_PUBLIC_BASE_URL */:
82
+ return process.env.NEXT_PUBLIC_BASE_URL || EXTRA_ENV.BASE_URL;
83
+ case "REFRESH_KEY" /* REFRESH_KEY */:
84
+ return process.env.REFRESH_KEY;
85
+ case "SIGNING_KEY" /* SIGNING_KEY */:
86
+ return process.env.SIGNING_KEY;
87
+ case "INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */:
88
+ return process.env.INSTAGRAM_CLIENT_SECRET;
89
+ case "INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */:
90
+ return process.env.INSTAGRAM_CLIENT_ID;
91
+ case "GOOGLE_CLIENT_SECRET" /* GOOGLE_CLIENT_SECRET */:
92
+ return process.env.GOOGLE_CLIENT_SECRET;
93
+ case "GOOGLE_CLIENT_ID" /* GOOGLE_CLIENT_ID */:
94
+ return process.env.GOOGLE_CLIENT_ID;
95
+ case "TURNSTILE_KEY" /* TURNSTILE_KEY */:
96
+ return process.env.TURNSTILE_KEY;
97
+ case "S3_ACCESS_KEY_ID" /* S3_ACCESS_KEY_ID */:
98
+ return process.env.S3_ACCESS_KEY_ID;
99
+ case "S3_ACCESS_KEY_SECRET" /* S3_ACCESS_KEY_SECRET */:
100
+ return process.env.S3_ACCESS_KEY_SECRET;
101
+ case "S3_REGION" /* S3_REGION */:
102
+ return process.env.S3_REGION;
103
+ case "S3_BUCKET" /* S3_BUCKET */:
104
+ return process.env.S3_BUCKET;
105
+ case "NODE_ENV" /* NODE_ENV */:
106
+ return process.env.NODE_ENV;
107
+ default:
108
+ return process.env[key];
109
+ }
110
+ };
111
+ function getEnv(key, skipCheck) {
112
+ const value = getEnvValue(key);
113
+ if (!skipCheck && !value) throw new Error(`${key} is not defined`);
114
+ return value;
115
+ }
116
+
117
+ // src/auth/email/token.ts
118
+ function getUserIdFromAccessToken(accessToken) {
119
+ if (accessToken)
120
+ try {
121
+ const decoded = (0, import_jsonwebtoken.verify)(accessToken, getEnv("SIGNING_KEY" /* SIGNING_KEY */));
122
+ if (typeof decoded !== "string" && typeof decoded.id === "number")
123
+ return decoded.id;
124
+ } catch (e) {
125
+ if (!(e instanceof import_jsonwebtoken.JsonWebTokenError)) console.error(e, "errors");
126
+ return null;
127
+ }
128
+ return null;
129
+ }
130
+
131
+ // src/socials/connect.ts
132
+ function setupSocialAuth({
133
+ providers,
134
+ endpoint,
135
+ onConnect,
136
+ redirectURL,
137
+ errorRedirectURL,
138
+ scopes
139
+ }) {
140
+ const handleError = (message) => import_server2.NextResponse.redirect(`${errorRedirectURL}?error=${message}`);
141
+ const GET = async (req, ctx) => {
142
+ const { platform } = await ctx.params;
143
+ const provider = providers.find((p) => p.platform === platform);
144
+ if (!provider) return handleError("Unsupported platform");
145
+ const code = req.nextUrl.searchParams.get("code");
146
+ const error = req.nextUrl.searchParams.get("error");
147
+ const state = req.nextUrl.searchParams.get("state");
148
+ if (error) return handleError(error);
149
+ if (!state) return handleError("Invalid request");
150
+ const redirectURI = `${endpoint}/${provider.platform}`;
151
+ if (!code) {
152
+ return import_server2.NextResponse.redirect(
153
+ provider.auth.authorizationURL({
154
+ state,
155
+ redirectURI,
156
+ scopes: scopes?.[provider.platform]
157
+ }),
158
+ 302
159
+ );
160
+ }
161
+ const exchanged = await provider.auth.exchangeCode({ code, redirectURI });
162
+ if (!exchanged) return handleError("Couldn't connect your account");
163
+ const { platformUserId, ...tokens } = exchanged;
164
+ const profile = await provider.fetchProfile(tokens.accessToken);
165
+ if (!profile) return handleError("Couldn't read your profile");
166
+ const errorMessage = await onConnect({
167
+ platform: provider.platform,
168
+ profile: {
169
+ ...profile,
170
+ platformUserId: profile.platformUserId ?? platformUserId
171
+ },
172
+ tokens,
173
+ userId: getUserIdFromAccessToken(state)
174
+ });
175
+ if (errorMessage) return handleError(errorMessage);
176
+ return import_server2.NextResponse.redirect(redirectURL);
177
+ };
178
+ return { GET };
179
+ }
180
+
181
+ // src/socials/types.ts
182
+ var SocialPlatform = /* @__PURE__ */ ((SocialPlatform2) => {
183
+ SocialPlatform2["Instagram"] = "instagram";
184
+ SocialPlatform2["YouTube"] = "youtube";
185
+ SocialPlatform2["TikTok"] = "tiktok";
186
+ return SocialPlatform2;
187
+ })(SocialPlatform || {});
188
+ var SocialMediaKind = /* @__PURE__ */ ((SocialMediaKind2) => {
189
+ SocialMediaKind2["Image"] = "image";
190
+ SocialMediaKind2["Video"] = "video";
191
+ SocialMediaKind2["Carousel"] = "carousel";
192
+ return SocialMediaKind2;
193
+ })(SocialMediaKind || {});
194
+
195
+ // src/socials/instagram/auth.ts
196
+ var getInstagramAuthorizationURL = (state, redirectURI, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
197
+ "INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
198
+ )}&response_type=code&enable_fb_login=0&force_authentication=1&scope=${scopes.join(",")}&state=${state}&redirect_uri=${redirectURI}#weblink`;
199
+ async function getLongLivedInstagramToken(code, redirectURI) {
200
+ const formData = new FormData();
201
+ formData.set("client_id", getEnv("INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */));
202
+ formData.set("client_secret", getEnv("INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */));
203
+ formData.set("grant_type", "authorization_code");
204
+ formData.set("redirect_uri", redirectURI);
205
+ formData.set("code", code);
206
+ const shortRes = await fetch("https://api.instagram.com/oauth/access_token", {
207
+ method: "POST",
208
+ body: formData
209
+ });
210
+ if (!shortRes.ok) return void 0;
211
+ const shortResData = await shortRes.json();
212
+ if (!shortResData.access_token) return void 0;
213
+ const longRes = await fetch(
214
+ `https://graph.instagram.com/access_token?grant_type=ig_exchange_token&access_token=${shortResData.access_token}&client_secret=${getEnv("INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */)}`
215
+ );
216
+ if (!longRes.ok) return void 0;
217
+ const longResData = await longRes.json();
218
+ return {
219
+ accessToken: longResData.access_token,
220
+ userId: shortResData.user_id,
221
+ expiresIn: longResData.expires_in,
222
+ permissions: shortResData.permissions
223
+ };
224
+ }
225
+ async function refreshInstagramAccessToken(token) {
226
+ const request = await fetch(
227
+ `https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=${token}`
228
+ );
229
+ const response = await request.json();
230
+ if (!response.access_token) return void 0;
231
+ return {
232
+ accessToken: response.access_token,
233
+ expiresIn: response.expires_in
234
+ };
235
+ }
236
+
48
237
  // src/socials/meta/request.ts
49
238
  function createGraphClient(baseURL) {
50
239
  const buildURL = (token, path, params = {}) => {
@@ -177,25 +366,151 @@ var canPublishToInstagram = async (token) => {
177
366
  return true;
178
367
  };
179
368
 
180
- // src/socials/meta/container.ts
369
+ // src/socials/instagram/adapter.ts
370
+ var PROFILE_FIELDS = [
371
+ "id",
372
+ "user_id",
373
+ "username",
374
+ "name",
375
+ "profile_picture_url",
376
+ "followers_count",
377
+ "media_count"
378
+ ];
379
+ var MEDIA_FIELDS = [
380
+ "id",
381
+ "caption",
382
+ "media_type",
383
+ "media_url",
384
+ "permalink",
385
+ "thumbnail_url",
386
+ "timestamp",
387
+ "like_count",
388
+ "comments_count"
389
+ ];
390
+ var expiresAt = (seconds) => seconds ? new Date(Date.now() + seconds * 1e3) : void 0;
391
+ function toKind(mediaType) {
392
+ if (mediaType === "VIDEO") return "video" /* Video */;
393
+ if (mediaType === "CAROUSEL_ALBUM") return "carousel" /* Carousel */;
394
+ return "image" /* Image */;
395
+ }
396
+ function toPost(media) {
397
+ const kind = toKind(media.media_type);
398
+ return {
399
+ platform: "instagram" /* Instagram */,
400
+ platformMediaId: media.id,
401
+ permalink: media.permalink,
402
+ // A video's `media_url` is the .mp4 itself, so it is never a stand-in for a
403
+ // missing poster frame — better no thumbnail than one that can't render.
404
+ thumbnail: media.thumbnail_url ?? (kind === "video" /* Video */ ? null : media.media_url ?? null),
405
+ mediaURL: media.media_url ?? null,
406
+ caption: media.caption ?? null,
407
+ kind,
408
+ publishedAt: media.timestamp,
409
+ likes: media.like_count ?? null,
410
+ comments: media.comments_count ?? null,
411
+ // Both need the Insights API; the basic media fields carry neither.
412
+ views: null,
413
+ shares: null,
414
+ metadata: {}
415
+ };
416
+ }
417
+ var InstagramProvider = {
418
+ platform: "instagram" /* Instagram */,
419
+ auth: {
420
+ authorizationURL: ({ state, redirectURI, scopes }) => getInstagramAuthorizationURL(
421
+ state,
422
+ redirectURI,
423
+ scopes
424
+ ),
425
+ exchangeCode: async ({ code, redirectURI }) => {
426
+ const result = await getLongLivedInstagramToken(code, redirectURI);
427
+ if (!result) {
428
+ console.error("[naystack] Instagram code exchange returned no token");
429
+ return null;
430
+ }
431
+ return {
432
+ accessToken: result.accessToken,
433
+ platformUserId: result.userId || null,
434
+ expiresAt: expiresAt(result.expiresIn),
435
+ scopes: result.permissions
436
+ };
437
+ },
438
+ refresh: async ({ accessToken }) => {
439
+ const refreshed = await refreshInstagramAccessToken(accessToken);
440
+ if (!refreshed) return null;
441
+ return {
442
+ accessToken: refreshed.accessToken,
443
+ expiresAt: expiresAt(refreshed.expiresIn)
444
+ };
445
+ }
446
+ },
447
+ fetchProfile: async (accessToken) => {
448
+ const user = await getInstagramUser(
449
+ accessToken,
450
+ void 0,
451
+ PROFILE_FIELDS
452
+ );
453
+ if (!user?.username) return null;
454
+ return {
455
+ platform: "instagram" /* Instagram */,
456
+ // `user_id` is what the OAuth exchange hands back, so preferring it keeps
457
+ // one account's id identical however it was read.
458
+ platformUserId: user.user_id || user.id || null,
459
+ username: user.username,
460
+ displayName: user.name ?? null,
461
+ avatar: user.profile_picture_url ?? null,
462
+ followers: user.followers_count ?? 0,
463
+ contentCount: user.media_count ?? 0,
464
+ metadata: {}
465
+ };
466
+ },
467
+ fetchMedia: async (accessToken, options) => {
468
+ const result = await getInstagramMedia(
469
+ accessToken,
470
+ MEDIA_FIELDS,
471
+ options?.limit
472
+ );
473
+ if (!result?.data) return [];
474
+ return result.data.map(toPost);
475
+ }
476
+ };
477
+
478
+ // src/socials/utils/poll.ts
181
479
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
182
- async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
480
+ async function pollUntilReady(read, isPending, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
183
481
  const deadline = Date.now() + timeoutMS;
184
- let state = await getState();
482
+ let state = await read();
185
483
  if (!state) {
186
484
  await sleep(2e3);
187
485
  return null;
188
486
  }
189
- while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
487
+ while (state && isPending(state) && Date.now() < deadline) {
190
488
  await sleep(intervalMS);
191
- state = await getState();
489
+ state = await read();
192
490
  }
193
491
  return state;
194
492
  }
493
+ async function withRetry(run, options = {}) {
494
+ const attempts = Math.max(1, options.attempts ?? 3);
495
+ const backoffMS = options.backoffMS ?? 2e3;
496
+ for (let attempt = 1; attempt <= attempts; attempt++) {
497
+ const result = await run(attempt);
498
+ if (result) return result;
499
+ if (attempt < attempts) {
500
+ const delayMS = backoffMS * 2 ** (attempt - 1);
501
+ options.onRetry?.(attempt, attempts, delayMS);
502
+ await sleep(delayMS);
503
+ }
504
+ }
505
+ return null;
506
+ }
507
+
508
+ // src/socials/meta/container.ts
195
509
  function createPublisher(platform) {
196
510
  const isReady = async (token, id, label, wait) => {
197
- const state = await waitForContainer(
511
+ const state = await pollUntilReady(
198
512
  () => platform.getStatus(token, id),
513
+ (current) => current.status === "IN_PROGRESS",
199
514
  wait
200
515
  );
201
516
  if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
@@ -206,22 +521,21 @@ function createPublisher(platform) {
206
521
  }
207
522
  return true;
208
523
  };
209
- const createReady = async (token, params, label, wait, retry) => {
210
- const attempts = Math.max(1, retry?.attempts ?? 3);
211
- const backoffMS = retry?.backoffMS ?? 2e3;
212
- for (let attempt = 1; attempt <= attempts; attempt++) {
524
+ const createReady = (token, params, label, wait, retry) => withRetry(
525
+ async () => {
213
526
  const id = await platform.createContainer(token, params);
214
- if (id && await isReady(token, id, label, wait)) return id;
215
- if (attempt < attempts) {
216
- const delay = backoffMS * 2 ** (attempt - 1);
527
+ if (!id || !await isReady(token, id, label, wait)) return null;
528
+ return id;
529
+ },
530
+ {
531
+ ...retry,
532
+ onRetry: (attempt, attempts, delayMS) => {
217
533
  console.warn(
218
- `[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${delay}ms`
534
+ `[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${delayMS}ms`
219
535
  );
220
- await sleep(delay);
221
536
  }
222
537
  }
223
- return null;
224
- };
538
+ );
225
539
  return {
226
540
  /** Creates a container, waits for it to finish processing, then publishes it. */
227
541
  publish: async (token, params, wait, retry) => {
@@ -235,21 +549,30 @@ function createPublisher(platform) {
235
549
  if (!containerID) return null;
236
550
  return platform.publish(token, containerID);
237
551
  },
238
- /** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
552
+ /**
553
+ * Creates and awaits every carousel child **in parallel**, resolving to
554
+ * their ids in input order — or `null` if any failed.
555
+ *
556
+ * Parallel because each child is an independent create-then-poll and Meta
557
+ * spends ~5s accepting a single container, so ten sequential children cost
558
+ * ~50s of mostly waiting — past what a serverless caller can fit in its
559
+ * timeout. `Promise.all` preserves input order, which the carousel's slide
560
+ * order depends on.
561
+ */
239
562
  createChildren: async (token, items, wait, retry) => {
240
- const children = [];
241
- for (const item of items) {
242
- const childID = await createReady(
243
- token,
244
- { ...item, is_carousel_item: true },
245
- "carousel item",
246
- wait,
247
- retry
248
- );
249
- if (!childID) return null;
250
- children.push(childID);
251
- }
252
- return children;
563
+ const children = await Promise.all(
564
+ items.map(
565
+ (item) => createReady(
566
+ token,
567
+ { ...item, is_carousel_item: true },
568
+ "carousel item",
569
+ wait,
570
+ retry
571
+ )
572
+ )
573
+ );
574
+ const created = children.filter((childID) => childID !== null);
575
+ return created.length === children.length ? created : null;
253
576
  }
254
577
  };
255
578
  }
@@ -375,11 +698,11 @@ var sendInstagramMessage = (token, to, text) => {
375
698
  };
376
699
 
377
700
  // src/socials/meta/webhook.ts
378
- var import_server = require("next/server");
701
+ var import_server3 = require("next/server");
379
702
  var verifyWebhook = (secret) => (req) => {
380
703
  const params = req.nextUrl.searchParams;
381
704
  if (params.get("hub.verify_token") === secret) {
382
- return new import_server.NextResponse(params.get("hub.challenge"));
705
+ return new import_server3.NextResponse(params.get("hub.challenge"));
383
706
  }
384
707
  };
385
708
 
@@ -513,11 +836,15 @@ var setupThreadsWebhook = (options) => {
513
836
  };
514
837
  // Annotate the CommonJS export names for ESM import in node:
515
838
  0 && (module.exports = {
839
+ InstagramProvider,
516
840
  MetaMediaType,
841
+ SocialMediaKind,
842
+ SocialPlatform,
517
843
  canPublishToInstagram,
518
844
  createInstagramPost,
519
845
  createThread,
520
846
  createThreadsPost,
847
+ getInstagramAuthorizationURL,
521
848
  getInstagramConversation,
522
849
  getInstagramConversationByUser,
523
850
  getInstagramConversations,
@@ -526,13 +853,18 @@ var setupThreadsWebhook = (options) => {
526
853
  getInstagramMedia,
527
854
  getInstagramMessage,
528
855
  getInstagramUser,
856
+ getLongLivedInstagramToken,
529
857
  getThread,
530
858
  getThreads,
531
859
  getThreadsData,
532
860
  getThreadsReplies,
861
+ pollUntilReady,
533
862
  readGraphID,
863
+ refreshInstagramAccessToken,
534
864
  replyToInstagramComment,
535
865
  sendInstagramMessage,
536
866
  setupInstagramWebhook,
537
- setupThreadsWebhook
867
+ setupSocialAuth,
868
+ setupThreadsWebhook,
869
+ withRetry
538
870
  });
@@ -1,14 +1,20 @@
1
+ export { SetupSocialAuthOptions, SocialConnection, setupSocialAuth } from './connect.mjs';
2
+ export { InstagramProvider } from './instagram/adapter.mjs';
3
+ export { InstagramScope, getInstagramAuthorizationURL, getLongLivedInstagramToken, refreshInstagramAccessToken } from './instagram/auth.mjs';
1
4
  export { canPublishToInstagram, getInstagramConversation, getInstagramConversationByUser, getInstagramConversations, getInstagramConversationsByUser, getInstagramMedia, getInstagramMessage, getInstagramUser } from './instagram/getters.mjs';
2
5
  export { createInstagramPost, replyToInstagramComment, sendInstagramMessage } from './instagram/setters.mjs';
3
6
  export { InstagramConversation, InstagramMedia, InstagramMessage, InstagramPostInput, InstagramPostMedia, InstagramUser } from './instagram/types.mjs';
4
7
  export { getInstagramData } from './instagram/utils.mjs';
5
8
  export { setupInstagramWebhook } from './instagram/webhook.mjs';
6
- export { RetryOptions, WaitForContainerOptions } from './meta/container.mjs';
9
+ export { WaitForContainerOptions } from './meta/container.mjs';
7
10
  export { GraphClient, GraphRequestOptions, readGraphID } from './meta/request.mjs';
8
11
  export { GraphError, GraphParams, MetaMediaType } from './meta/types.mjs';
12
+ export { SocialProvider } from './provider.mjs';
9
13
  export { getThread, getThreads, getThreadsReplies } from './threads/getters.mjs';
10
14
  export { createThread, createThreadsPost } from './threads/setters.mjs';
11
- export { getThreadsData } from './threads/utils.mjs';
12
15
  export { ThreadsPost, ThreadsPostInput, ThreadsPostMedia } from './threads/types.mjs';
16
+ export { getThreadsData } from './threads/utils.mjs';
13
17
  export { setupThreadsWebhook } from './threads/webhook.mjs';
18
+ export { SocialMediaKind, SocialPlatform, SocialPost, SocialProfile, SocialTokens } from './types.mjs';
19
+ export { PollOptions, RetryOptions, pollUntilReady, withRetry } from './utils/poll.mjs';
14
20
  import 'next/server';
@@ -1,14 +1,20 @@
1
+ export { SetupSocialAuthOptions, SocialConnection, setupSocialAuth } from './connect.js';
2
+ export { InstagramProvider } from './instagram/adapter.js';
3
+ export { InstagramScope, getInstagramAuthorizationURL, getLongLivedInstagramToken, refreshInstagramAccessToken } from './instagram/auth.js';
1
4
  export { canPublishToInstagram, getInstagramConversation, getInstagramConversationByUser, getInstagramConversations, getInstagramConversationsByUser, getInstagramMedia, getInstagramMessage, getInstagramUser } from './instagram/getters.js';
2
5
  export { createInstagramPost, replyToInstagramComment, sendInstagramMessage } from './instagram/setters.js';
3
6
  export { InstagramConversation, InstagramMedia, InstagramMessage, InstagramPostInput, InstagramPostMedia, InstagramUser } from './instagram/types.js';
4
7
  export { getInstagramData } from './instagram/utils.js';
5
8
  export { setupInstagramWebhook } from './instagram/webhook.js';
6
- export { RetryOptions, WaitForContainerOptions } from './meta/container.js';
9
+ export { WaitForContainerOptions } from './meta/container.js';
7
10
  export { GraphClient, GraphRequestOptions, readGraphID } from './meta/request.js';
8
11
  export { GraphError, GraphParams, MetaMediaType } from './meta/types.js';
12
+ export { SocialProvider } from './provider.js';
9
13
  export { getThread, getThreads, getThreadsReplies } from './threads/getters.js';
10
14
  export { createThread, createThreadsPost } from './threads/setters.js';
11
- export { getThreadsData } from './threads/utils.js';
12
15
  export { ThreadsPost, ThreadsPostInput, ThreadsPostMedia } from './threads/types.js';
16
+ export { getThreadsData } from './threads/utils.js';
13
17
  export { setupThreadsWebhook } from './threads/webhook.js';
18
+ export { SocialMediaKind, SocialPlatform, SocialPost, SocialProfile, SocialTokens } from './types.js';
19
+ export { PollOptions, RetryOptions, pollUntilReady, withRetry } from './utils/poll.js';
14
20
  import 'next/server';