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
@@ -1,3 +1,183 @@
1
+ // src/socials/connect.ts
2
+ import { NextResponse as NextResponse2 } from "next/server";
3
+
4
+ // src/auth/email/token.ts
5
+ import { compare } from "bcryptjs";
6
+ import { JsonWebTokenError, sign, verify } from "jsonwebtoken";
7
+ import { cookies } from "next/headers";
8
+ import { redirect } from "next/navigation";
9
+ import { NextResponse } from "next/server";
10
+
11
+ // src/env.ts
12
+ var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
13
+ var getEnvValue = (key) => {
14
+ switch (key) {
15
+ case "NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */:
16
+ return process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || EXTRA_ENV.GRAPHQL_ENDPOINT;
17
+ case "NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */:
18
+ return process.env.NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT || EXTRA_ENV.EMAIL_AUTH_ENDPOINT;
19
+ case "NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT" /* NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT */:
20
+ return process.env.NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT || EXTRA_ENV.GOOGLE_AUTH_ENDPOINT;
21
+ case "NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT" /* NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT */:
22
+ return process.env.NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT || EXTRA_ENV.SOCIAL_AUTH_ENDPOINT;
23
+ case "NEXT_PUBLIC_FILE_ENDPOINT" /* NEXT_PUBLIC_FILE_ENDPOINT */:
24
+ return process.env.NEXT_PUBLIC_FILE_ENDPOINT || EXTRA_ENV.FILE_ENDPOINT;
25
+ case "NEXT_PUBLIC_BASE_URL" /* NEXT_PUBLIC_BASE_URL */:
26
+ return process.env.NEXT_PUBLIC_BASE_URL || EXTRA_ENV.BASE_URL;
27
+ case "REFRESH_KEY" /* REFRESH_KEY */:
28
+ return process.env.REFRESH_KEY;
29
+ case "SIGNING_KEY" /* SIGNING_KEY */:
30
+ return process.env.SIGNING_KEY;
31
+ case "INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */:
32
+ return process.env.INSTAGRAM_CLIENT_SECRET;
33
+ case "INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */:
34
+ return process.env.INSTAGRAM_CLIENT_ID;
35
+ case "GOOGLE_CLIENT_SECRET" /* GOOGLE_CLIENT_SECRET */:
36
+ return process.env.GOOGLE_CLIENT_SECRET;
37
+ case "GOOGLE_CLIENT_ID" /* GOOGLE_CLIENT_ID */:
38
+ return process.env.GOOGLE_CLIENT_ID;
39
+ case "TURNSTILE_KEY" /* TURNSTILE_KEY */:
40
+ return process.env.TURNSTILE_KEY;
41
+ case "S3_ACCESS_KEY_ID" /* S3_ACCESS_KEY_ID */:
42
+ return process.env.S3_ACCESS_KEY_ID;
43
+ case "S3_ACCESS_KEY_SECRET" /* S3_ACCESS_KEY_SECRET */:
44
+ return process.env.S3_ACCESS_KEY_SECRET;
45
+ case "S3_REGION" /* S3_REGION */:
46
+ return process.env.S3_REGION;
47
+ case "S3_BUCKET" /* S3_BUCKET */:
48
+ return process.env.S3_BUCKET;
49
+ case "NODE_ENV" /* NODE_ENV */:
50
+ return process.env.NODE_ENV;
51
+ default:
52
+ return process.env[key];
53
+ }
54
+ };
55
+ function getEnv(key, skipCheck) {
56
+ const value = getEnvValue(key);
57
+ if (!skipCheck && !value) throw new Error(`${key} is not defined`);
58
+ return value;
59
+ }
60
+
61
+ // src/auth/email/token.ts
62
+ function getUserIdFromAccessToken(accessToken) {
63
+ if (accessToken)
64
+ try {
65
+ const decoded = verify(accessToken, getEnv("SIGNING_KEY" /* SIGNING_KEY */));
66
+ if (typeof decoded !== "string" && typeof decoded.id === "number")
67
+ return decoded.id;
68
+ } catch (e) {
69
+ if (!(e instanceof JsonWebTokenError)) console.error(e, "errors");
70
+ return null;
71
+ }
72
+ return null;
73
+ }
74
+
75
+ // src/socials/connect.ts
76
+ function setupSocialAuth({
77
+ providers,
78
+ endpoint,
79
+ onConnect,
80
+ redirectURL,
81
+ errorRedirectURL,
82
+ scopes
83
+ }) {
84
+ const handleError = (message) => NextResponse2.redirect(`${errorRedirectURL}?error=${message}`);
85
+ const GET = async (req, ctx) => {
86
+ const { platform } = await ctx.params;
87
+ const provider = providers.find((p) => p.platform === platform);
88
+ if (!provider) return handleError("Unsupported platform");
89
+ const code = req.nextUrl.searchParams.get("code");
90
+ const error = req.nextUrl.searchParams.get("error");
91
+ const state = req.nextUrl.searchParams.get("state");
92
+ if (error) return handleError(error);
93
+ if (!state) return handleError("Invalid request");
94
+ const redirectURI = `${endpoint}/${provider.platform}`;
95
+ if (!code) {
96
+ return NextResponse2.redirect(
97
+ provider.auth.authorizationURL({
98
+ state,
99
+ redirectURI,
100
+ scopes: scopes?.[provider.platform]
101
+ }),
102
+ 302
103
+ );
104
+ }
105
+ const exchanged = await provider.auth.exchangeCode({ code, redirectURI });
106
+ if (!exchanged) return handleError("Couldn't connect your account");
107
+ const { platformUserId, ...tokens } = exchanged;
108
+ const profile = await provider.fetchProfile(tokens.accessToken);
109
+ if (!profile) return handleError("Couldn't read your profile");
110
+ const errorMessage = await onConnect({
111
+ platform: provider.platform,
112
+ profile: {
113
+ ...profile,
114
+ platformUserId: profile.platformUserId ?? platformUserId
115
+ },
116
+ tokens,
117
+ userId: getUserIdFromAccessToken(state)
118
+ });
119
+ if (errorMessage) return handleError(errorMessage);
120
+ return NextResponse2.redirect(redirectURL);
121
+ };
122
+ return { GET };
123
+ }
124
+
125
+ // src/socials/types.ts
126
+ var SocialPlatform = /* @__PURE__ */ ((SocialPlatform2) => {
127
+ SocialPlatform2["Instagram"] = "instagram";
128
+ SocialPlatform2["YouTube"] = "youtube";
129
+ SocialPlatform2["TikTok"] = "tiktok";
130
+ return SocialPlatform2;
131
+ })(SocialPlatform || {});
132
+ var SocialMediaKind = /* @__PURE__ */ ((SocialMediaKind2) => {
133
+ SocialMediaKind2["Image"] = "image";
134
+ SocialMediaKind2["Video"] = "video";
135
+ SocialMediaKind2["Carousel"] = "carousel";
136
+ return SocialMediaKind2;
137
+ })(SocialMediaKind || {});
138
+
139
+ // src/socials/instagram/auth.ts
140
+ var getInstagramAuthorizationURL = (state, redirectURI, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
141
+ "INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
142
+ )}&response_type=code&enable_fb_login=0&force_authentication=1&scope=${scopes.join(",")}&state=${state}&redirect_uri=${redirectURI}#weblink`;
143
+ async function getLongLivedInstagramToken(code, redirectURI) {
144
+ const formData = new FormData();
145
+ formData.set("client_id", getEnv("INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */));
146
+ formData.set("client_secret", getEnv("INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */));
147
+ formData.set("grant_type", "authorization_code");
148
+ formData.set("redirect_uri", redirectURI);
149
+ formData.set("code", code);
150
+ const shortRes = await fetch("https://api.instagram.com/oauth/access_token", {
151
+ method: "POST",
152
+ body: formData
153
+ });
154
+ if (!shortRes.ok) return void 0;
155
+ const shortResData = await shortRes.json();
156
+ if (!shortResData.access_token) return void 0;
157
+ const longRes = await fetch(
158
+ `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 */)}`
159
+ );
160
+ if (!longRes.ok) return void 0;
161
+ const longResData = await longRes.json();
162
+ return {
163
+ accessToken: longResData.access_token,
164
+ userId: shortResData.user_id,
165
+ expiresIn: longResData.expires_in,
166
+ permissions: shortResData.permissions
167
+ };
168
+ }
169
+ async function refreshInstagramAccessToken(token) {
170
+ const request = await fetch(
171
+ `https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=${token}`
172
+ );
173
+ const response = await request.json();
174
+ if (!response.access_token) return void 0;
175
+ return {
176
+ accessToken: response.access_token,
177
+ expiresIn: response.expires_in
178
+ };
179
+ }
180
+
1
181
  // src/socials/meta/request.ts
2
182
  function createGraphClient(baseURL) {
3
183
  const buildURL = (token, path, params = {}) => {
@@ -130,25 +310,151 @@ var canPublishToInstagram = async (token) => {
130
310
  return true;
131
311
  };
132
312
 
133
- // src/socials/meta/container.ts
313
+ // src/socials/instagram/adapter.ts
314
+ var PROFILE_FIELDS = [
315
+ "id",
316
+ "user_id",
317
+ "username",
318
+ "name",
319
+ "profile_picture_url",
320
+ "followers_count",
321
+ "media_count"
322
+ ];
323
+ var MEDIA_FIELDS = [
324
+ "id",
325
+ "caption",
326
+ "media_type",
327
+ "media_url",
328
+ "permalink",
329
+ "thumbnail_url",
330
+ "timestamp",
331
+ "like_count",
332
+ "comments_count"
333
+ ];
334
+ var expiresAt = (seconds) => seconds ? new Date(Date.now() + seconds * 1e3) : void 0;
335
+ function toKind(mediaType) {
336
+ if (mediaType === "VIDEO") return "video" /* Video */;
337
+ if (mediaType === "CAROUSEL_ALBUM") return "carousel" /* Carousel */;
338
+ return "image" /* Image */;
339
+ }
340
+ function toPost(media) {
341
+ const kind = toKind(media.media_type);
342
+ return {
343
+ platform: "instagram" /* Instagram */,
344
+ platformMediaId: media.id,
345
+ permalink: media.permalink,
346
+ // A video's `media_url` is the .mp4 itself, so it is never a stand-in for a
347
+ // missing poster frame — better no thumbnail than one that can't render.
348
+ thumbnail: media.thumbnail_url ?? (kind === "video" /* Video */ ? null : media.media_url ?? null),
349
+ mediaURL: media.media_url ?? null,
350
+ caption: media.caption ?? null,
351
+ kind,
352
+ publishedAt: media.timestamp,
353
+ likes: media.like_count ?? null,
354
+ comments: media.comments_count ?? null,
355
+ // Both need the Insights API; the basic media fields carry neither.
356
+ views: null,
357
+ shares: null,
358
+ metadata: {}
359
+ };
360
+ }
361
+ var InstagramProvider = {
362
+ platform: "instagram" /* Instagram */,
363
+ auth: {
364
+ authorizationURL: ({ state, redirectURI, scopes }) => getInstagramAuthorizationURL(
365
+ state,
366
+ redirectURI,
367
+ scopes
368
+ ),
369
+ exchangeCode: async ({ code, redirectURI }) => {
370
+ const result = await getLongLivedInstagramToken(code, redirectURI);
371
+ if (!result) {
372
+ console.error("[naystack] Instagram code exchange returned no token");
373
+ return null;
374
+ }
375
+ return {
376
+ accessToken: result.accessToken,
377
+ platformUserId: result.userId || null,
378
+ expiresAt: expiresAt(result.expiresIn),
379
+ scopes: result.permissions
380
+ };
381
+ },
382
+ refresh: async ({ accessToken }) => {
383
+ const refreshed = await refreshInstagramAccessToken(accessToken);
384
+ if (!refreshed) return null;
385
+ return {
386
+ accessToken: refreshed.accessToken,
387
+ expiresAt: expiresAt(refreshed.expiresIn)
388
+ };
389
+ }
390
+ },
391
+ fetchProfile: async (accessToken) => {
392
+ const user = await getInstagramUser(
393
+ accessToken,
394
+ void 0,
395
+ PROFILE_FIELDS
396
+ );
397
+ if (!user?.username) return null;
398
+ return {
399
+ platform: "instagram" /* Instagram */,
400
+ // `user_id` is what the OAuth exchange hands back, so preferring it keeps
401
+ // one account's id identical however it was read.
402
+ platformUserId: user.user_id || user.id || null,
403
+ username: user.username,
404
+ displayName: user.name ?? null,
405
+ avatar: user.profile_picture_url ?? null,
406
+ followers: user.followers_count ?? 0,
407
+ contentCount: user.media_count ?? 0,
408
+ metadata: {}
409
+ };
410
+ },
411
+ fetchMedia: async (accessToken, options) => {
412
+ const result = await getInstagramMedia(
413
+ accessToken,
414
+ MEDIA_FIELDS,
415
+ options?.limit
416
+ );
417
+ if (!result?.data) return [];
418
+ return result.data.map(toPost);
419
+ }
420
+ };
421
+
422
+ // src/socials/utils/poll.ts
134
423
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
135
- async function waitForContainer(getState, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
424
+ async function pollUntilReady(read, isPending, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
136
425
  const deadline = Date.now() + timeoutMS;
137
- let state = await getState();
426
+ let state = await read();
138
427
  if (!state) {
139
428
  await sleep(2e3);
140
429
  return null;
141
430
  }
142
- while (state?.status === "IN_PROGRESS" && Date.now() < deadline) {
431
+ while (state && isPending(state) && Date.now() < deadline) {
143
432
  await sleep(intervalMS);
144
- state = await getState();
433
+ state = await read();
145
434
  }
146
435
  return state;
147
436
  }
437
+ async function withRetry(run, options = {}) {
438
+ const attempts = Math.max(1, options.attempts ?? 3);
439
+ const backoffMS = options.backoffMS ?? 2e3;
440
+ for (let attempt = 1; attempt <= attempts; attempt++) {
441
+ const result = await run(attempt);
442
+ if (result) return result;
443
+ if (attempt < attempts) {
444
+ const delayMS = backoffMS * 2 ** (attempt - 1);
445
+ options.onRetry?.(attempt, attempts, delayMS);
446
+ await sleep(delayMS);
447
+ }
448
+ }
449
+ return null;
450
+ }
451
+
452
+ // src/socials/meta/container.ts
148
453
  function createPublisher(platform) {
149
454
  const isReady = async (token, id, label, wait) => {
150
- const state = await waitForContainer(
455
+ const state = await pollUntilReady(
151
456
  () => platform.getStatus(token, id),
457
+ (current) => current.status === "IN_PROGRESS",
152
458
  wait
153
459
  );
154
460
  if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
@@ -159,22 +465,21 @@ function createPublisher(platform) {
159
465
  }
160
466
  return true;
161
467
  };
162
- const createReady = async (token, params, label, wait, retry) => {
163
- const attempts = Math.max(1, retry?.attempts ?? 3);
164
- const backoffMS = retry?.backoffMS ?? 2e3;
165
- for (let attempt = 1; attempt <= attempts; attempt++) {
468
+ const createReady = (token, params, label, wait, retry) => withRetry(
469
+ async () => {
166
470
  const id = await platform.createContainer(token, params);
167
- if (id && await isReady(token, id, label, wait)) return id;
168
- if (attempt < attempts) {
169
- const delay = backoffMS * 2 ** (attempt - 1);
471
+ if (!id || !await isReady(token, id, label, wait)) return null;
472
+ return id;
473
+ },
474
+ {
475
+ ...retry,
476
+ onRetry: (attempt, attempts, delayMS) => {
170
477
  console.warn(
171
- `[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${delay}ms`
478
+ `[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${delayMS}ms`
172
479
  );
173
- await sleep(delay);
174
480
  }
175
481
  }
176
- return null;
177
- };
482
+ );
178
483
  return {
179
484
  /** Creates a container, waits for it to finish processing, then publishes it. */
180
485
  publish: async (token, params, wait, retry) => {
@@ -188,21 +493,30 @@ function createPublisher(platform) {
188
493
  if (!containerID) return null;
189
494
  return platform.publish(token, containerID);
190
495
  },
191
- /** Creates and awaits every carousel child, resolving to their ids — or `null` if any failed. */
496
+ /**
497
+ * Creates and awaits every carousel child **in parallel**, resolving to
498
+ * their ids in input order — or `null` if any failed.
499
+ *
500
+ * Parallel because each child is an independent create-then-poll and Meta
501
+ * spends ~5s accepting a single container, so ten sequential children cost
502
+ * ~50s of mostly waiting — past what a serverless caller can fit in its
503
+ * timeout. `Promise.all` preserves input order, which the carousel's slide
504
+ * order depends on.
505
+ */
192
506
  createChildren: async (token, items, wait, retry) => {
193
- const children = [];
194
- for (const item of items) {
195
- const childID = await createReady(
196
- token,
197
- { ...item, is_carousel_item: true },
198
- "carousel item",
199
- wait,
200
- retry
201
- );
202
- if (!childID) return null;
203
- children.push(childID);
204
- }
205
- return children;
507
+ const children = await Promise.all(
508
+ items.map(
509
+ (item) => createReady(
510
+ token,
511
+ { ...item, is_carousel_item: true },
512
+ "carousel item",
513
+ wait,
514
+ retry
515
+ )
516
+ )
517
+ );
518
+ const created = children.filter((childID) => childID !== null);
519
+ return created.length === children.length ? created : null;
206
520
  }
207
521
  };
208
522
  }
@@ -328,11 +642,11 @@ var sendInstagramMessage = (token, to, text) => {
328
642
  };
329
643
 
330
644
  // src/socials/meta/webhook.ts
331
- import { NextResponse } from "next/server";
645
+ import { NextResponse as NextResponse3 } from "next/server";
332
646
  var verifyWebhook = (secret) => (req) => {
333
647
  const params = req.nextUrl.searchParams;
334
648
  if (params.get("hub.verify_token") === secret) {
335
- return new NextResponse(params.get("hub.challenge"));
649
+ return new NextResponse3(params.get("hub.challenge"));
336
650
  }
337
651
  };
338
652
 
@@ -465,11 +779,15 @@ var setupThreadsWebhook = (options) => {
465
779
  };
466
780
  };
467
781
  export {
782
+ InstagramProvider,
468
783
  MetaMediaType,
784
+ SocialMediaKind,
785
+ SocialPlatform,
469
786
  canPublishToInstagram,
470
787
  createInstagramPost,
471
788
  createThread,
472
789
  createThreadsPost,
790
+ getInstagramAuthorizationURL,
473
791
  getInstagramConversation,
474
792
  getInstagramConversationByUser,
475
793
  getInstagramConversations,
@@ -478,13 +796,18 @@ export {
478
796
  getInstagramMedia,
479
797
  getInstagramMessage,
480
798
  getInstagramUser,
799
+ getLongLivedInstagramToken,
481
800
  getThread,
482
801
  getThreads,
483
802
  getThreadsData,
484
803
  getThreadsReplies,
804
+ pollUntilReady,
485
805
  readGraphID,
806
+ refreshInstagramAccessToken,
486
807
  replyToInstagramComment,
487
808
  sendInstagramMessage,
488
809
  setupInstagramWebhook,
489
- setupThreadsWebhook
810
+ setupSocialAuth,
811
+ setupThreadsWebhook,
812
+ withRetry
490
813
  };