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.
- package/README.md +94 -34
- package/dist/auth/client.cjs.js +2 -2
- package/dist/auth/client.esm.js +2 -2
- package/dist/auth/email/client.cjs.js +2 -2
- package/dist/auth/email/client.esm.js +2 -2
- package/dist/auth/email/index.cjs.js +2 -2
- package/dist/auth/email/index.esm.js +2 -2
- package/dist/auth/email/next.cjs.js +2 -2
- package/dist/auth/email/next.esm.js +2 -2
- package/dist/auth/email/routes/delete.cjs.js +2 -2
- package/dist/auth/email/routes/delete.esm.js +2 -2
- package/dist/auth/email/routes/get.cjs.js +2 -2
- package/dist/auth/email/routes/get.esm.js +2 -2
- package/dist/auth/email/routes/post.cjs.js +2 -2
- package/dist/auth/email/routes/post.esm.js +2 -2
- package/dist/auth/email/routes/put.cjs.js +2 -2
- package/dist/auth/email/routes/put.esm.js +2 -2
- package/dist/auth/email/token.cjs.js +2 -2
- package/dist/auth/email/token.esm.js +2 -2
- package/dist/auth/email/utils.cjs.js +2 -2
- package/dist/auth/email/utils.esm.js +2 -2
- package/dist/auth/google/get.cjs.js +2 -2
- package/dist/auth/google/get.esm.js +2 -2
- package/dist/auth/google/index.cjs.js +2 -2
- package/dist/auth/google/index.esm.js +2 -2
- package/dist/auth/index.cjs.js +4 -297
- package/dist/auth/index.d.mts +0 -5
- package/dist/auth/index.d.ts +0 -5
- package/dist/auth/index.esm.js +3 -293
- package/dist/auth/token-store.cjs.js +2 -2
- package/dist/auth/token-store.esm.js +2 -2
- package/dist/env.cjs.js +3 -3
- package/dist/env.d.mts +3 -2
- package/dist/env.d.ts +3 -2
- package/dist/env.esm.js +3 -3
- package/dist/file/client.cjs.js +2 -2
- package/dist/file/client.esm.js +2 -2
- package/dist/file/index.cjs.js +2 -2
- package/dist/file/index.esm.js +2 -2
- package/dist/file/put.cjs.js +2 -2
- package/dist/file/put.esm.js +2 -2
- package/dist/file/setup.cjs.js +2 -2
- package/dist/file/setup.esm.js +2 -2
- package/dist/file/utils.cjs.js +2 -2
- package/dist/file/utils.esm.js +2 -2
- package/dist/graphql/client.cjs.js +2 -2
- package/dist/graphql/client.esm.js +2 -2
- package/dist/graphql/index.cjs.js +2 -2
- package/dist/graphql/index.esm.js +2 -2
- package/dist/graphql/init.cjs.js +2 -2
- package/dist/graphql/init.esm.js +2 -2
- package/dist/graphql/links.cjs.js +2 -2
- package/dist/graphql/links.esm.js +2 -2
- package/dist/graphql/next.cjs.js +2 -2
- package/dist/graphql/next.esm.js +2 -2
- package/dist/graphql/server.cjs.js +2 -2
- package/dist/graphql/server.esm.js +2 -2
- package/dist/graphql/utils.cjs.js +2 -2
- package/dist/graphql/utils.esm.js +2 -2
- package/dist/socials/connect.cjs.js +151 -0
- package/dist/socials/connect.d.mts +70 -0
- package/dist/socials/connect.d.ts +70 -0
- package/dist/socials/connect.esm.js +126 -0
- package/dist/socials/index.cjs.js +367 -35
- package/dist/socials/index.d.mts +8 -2
- package/dist/socials/index.d.ts +8 -2
- package/dist/socials/index.esm.js +357 -34
- package/dist/socials/instagram/adapter.cjs.js +275 -0
- package/dist/socials/instagram/adapter.d.mts +30 -0
- package/dist/socials/instagram/adapter.d.ts +30 -0
- package/dist/socials/instagram/adapter.esm.js +248 -0
- package/dist/{auth/instagram/utils.cjs.js → socials/instagram/auth.cjs.js} +43 -41
- package/dist/socials/instagram/auth.d.mts +52 -0
- package/dist/socials/instagram/auth.d.ts +52 -0
- package/dist/{auth/instagram/utils.esm.js → socials/instagram/auth.esm.js} +37 -35
- package/dist/socials/instagram/getters.d.mts +1 -0
- package/dist/socials/instagram/getters.d.ts +1 -0
- package/dist/socials/instagram/setters.cjs.js +56 -31
- package/dist/socials/instagram/setters.d.mts +1 -0
- package/dist/socials/instagram/setters.d.ts +1 -0
- package/dist/socials/instagram/setters.esm.js +56 -31
- package/dist/socials/instagram/types.d.mts +2 -1
- package/dist/socials/instagram/types.d.ts +2 -1
- package/dist/socials/meta/container.cjs.js +57 -30
- package/dist/socials/meta/container.d.mts +15 -28
- package/dist/socials/meta/container.d.ts +15 -28
- package/dist/socials/meta/container.esm.js +56 -31
- package/dist/socials/provider.cjs.js +18 -0
- package/dist/socials/provider.d.mts +52 -0
- package/dist/socials/provider.d.ts +52 -0
- package/dist/socials/provider.esm.js +0 -0
- package/dist/socials/threads/getters.d.mts +1 -0
- package/dist/socials/threads/getters.d.ts +1 -0
- package/dist/socials/threads/setters.cjs.js +56 -31
- package/dist/socials/threads/setters.d.mts +1 -0
- package/dist/socials/threads/setters.d.ts +1 -0
- package/dist/socials/threads/setters.esm.js +56 -31
- package/dist/socials/threads/types.d.mts +2 -1
- package/dist/socials/threads/types.d.ts +2 -1
- package/dist/socials/types.cjs.js +43 -0
- package/dist/socials/types.d.mts +99 -0
- package/dist/socials/types.d.ts +99 -0
- package/dist/socials/types.esm.js +17 -0
- package/dist/socials/utils/poll.cjs.js +59 -0
- package/dist/socials/utils/poll.d.mts +85 -0
- package/dist/socials/utils/poll.d.ts +85 -0
- package/dist/socials/utils/poll.esm.js +33 -0
- package/dist/utils/client/index.cjs.js +2 -2
- package/dist/utils/client/index.esm.js +2 -2
- package/dist/utils/client/seo.cjs.js +2 -2
- package/dist/utils/client/seo.esm.js +2 -2
- package/package.json +1 -1
- package/dist/auth/instagram/index.cjs.js +0 -379
- package/dist/auth/instagram/index.d.mts +0 -58
- package/dist/auth/instagram/index.d.ts +0 -58
- package/dist/auth/instagram/index.esm.js +0 -350
- package/dist/auth/instagram/route.cjs.js +0 -359
- package/dist/auth/instagram/route.d.mts +0 -15
- package/dist/auth/instagram/route.d.ts +0 -15
- package/dist/auth/instagram/route.esm.js +0 -334
- package/dist/auth/instagram/utils.d.mts +0 -57
- 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
|
-
|
|
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/
|
|
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
|
|
480
|
+
async function pollUntilReady(read, isPending, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
183
481
|
const deadline = Date.now() + timeoutMS;
|
|
184
|
-
let state = await
|
|
482
|
+
let state = await read();
|
|
185
483
|
if (!state) {
|
|
186
484
|
await sleep(2e3);
|
|
187
485
|
return null;
|
|
188
486
|
}
|
|
189
|
-
while (state
|
|
487
|
+
while (state && isPending(state) && Date.now() < deadline) {
|
|
190
488
|
await sleep(intervalMS);
|
|
191
|
-
state = await
|
|
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
|
|
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 =
|
|
210
|
-
|
|
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
|
|
215
|
-
|
|
216
|
-
|
|
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 ${
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
867
|
+
setupSocialAuth,
|
|
868
|
+
setupThreadsWebhook,
|
|
869
|
+
withRetry
|
|
538
870
|
});
|
package/dist/socials/index.d.mts
CHANGED
|
@@ -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 {
|
|
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';
|
package/dist/socials/index.d.ts
CHANGED
|
@@ -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 {
|
|
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';
|