naystack 1.8.19 → 1.9.1
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 +99 -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 -306
- package/dist/auth/index.d.mts +0 -5
- package/dist/auth/index.d.ts +0 -5
- package/dist/auth/index.esm.js +3 -302
- 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 +345 -21
- package/dist/socials/index.d.mts +8 -2
- package/dist/socials/index.d.ts +8 -2
- package/dist/socials/index.esm.js +335 -20
- package/dist/socials/instagram/adapter.cjs.js +276 -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 +249 -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 +33 -17
- 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 +33 -17
- 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 +34 -16
- package/dist/socials/meta/container.d.mts +5 -27
- package/dist/socials/meta/container.d.ts +5 -27
- package/dist/socials/meta/container.esm.js +33 -17
- package/dist/socials/provider.cjs.js +18 -0
- package/dist/socials/provider.d.mts +54 -0
- package/dist/socials/provider.d.ts +54 -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 +33 -17
- 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 +33 -17
- 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 -388
- 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 -359
- package/dist/auth/instagram/route.cjs.js +0 -368
- 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 -343
- package/dist/auth/instagram/utils.d.mts +0 -57
- 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,152 @@ var canPublishToInstagram = async (token) => {
|
|
|
130
310
|
return true;
|
|
131
311
|
};
|
|
132
312
|
|
|
133
|
-
// src/socials/
|
|
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
|
+
profileURL: (username) => `https://instagram.com/${username}`,
|
|
392
|
+
fetchProfile: async (accessToken) => {
|
|
393
|
+
const user = await getInstagramUser(
|
|
394
|
+
accessToken,
|
|
395
|
+
void 0,
|
|
396
|
+
PROFILE_FIELDS
|
|
397
|
+
);
|
|
398
|
+
if (!user?.username) return null;
|
|
399
|
+
return {
|
|
400
|
+
platform: "instagram" /* Instagram */,
|
|
401
|
+
// `user_id` is what the OAuth exchange hands back, so preferring it keeps
|
|
402
|
+
// one account's id identical however it was read.
|
|
403
|
+
platformUserId: user.user_id || user.id || null,
|
|
404
|
+
username: user.username,
|
|
405
|
+
displayName: user.name ?? null,
|
|
406
|
+
avatar: user.profile_picture_url ?? null,
|
|
407
|
+
followers: user.followers_count ?? 0,
|
|
408
|
+
contentCount: user.media_count ?? 0,
|
|
409
|
+
metadata: {}
|
|
410
|
+
};
|
|
411
|
+
},
|
|
412
|
+
fetchMedia: async (accessToken, options) => {
|
|
413
|
+
const result = await getInstagramMedia(
|
|
414
|
+
accessToken,
|
|
415
|
+
MEDIA_FIELDS,
|
|
416
|
+
options?.limit
|
|
417
|
+
);
|
|
418
|
+
if (!result?.data) return [];
|
|
419
|
+
return result.data.map(toPost);
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
// src/socials/utils/poll.ts
|
|
134
424
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
135
|
-
async function
|
|
425
|
+
async function pollUntilReady(read, isPending, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
136
426
|
const deadline = Date.now() + timeoutMS;
|
|
137
|
-
let state = await
|
|
427
|
+
let state = await read();
|
|
138
428
|
if (!state) {
|
|
139
429
|
await sleep(2e3);
|
|
140
430
|
return null;
|
|
141
431
|
}
|
|
142
|
-
while (state
|
|
432
|
+
while (state && isPending(state) && Date.now() < deadline) {
|
|
143
433
|
await sleep(intervalMS);
|
|
144
|
-
state = await
|
|
434
|
+
state = await read();
|
|
145
435
|
}
|
|
146
436
|
return state;
|
|
147
437
|
}
|
|
438
|
+
async function withRetry(run, options = {}) {
|
|
439
|
+
const attempts = Math.max(1, options.attempts ?? 3);
|
|
440
|
+
const backoffMS = options.backoffMS ?? 2e3;
|
|
441
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
442
|
+
const result = await run(attempt);
|
|
443
|
+
if (result) return result;
|
|
444
|
+
if (attempt < attempts) {
|
|
445
|
+
const delayMS = backoffMS * 2 ** (attempt - 1);
|
|
446
|
+
options.onRetry?.(attempt, attempts, delayMS);
|
|
447
|
+
await sleep(delayMS);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// src/socials/meta/container.ts
|
|
148
454
|
function createPublisher(platform) {
|
|
149
455
|
const isReady = async (token, id, label, wait) => {
|
|
150
|
-
const state = await
|
|
456
|
+
const state = await pollUntilReady(
|
|
151
457
|
() => platform.getStatus(token, id),
|
|
458
|
+
(current) => current.status === "IN_PROGRESS",
|
|
152
459
|
wait
|
|
153
460
|
);
|
|
154
461
|
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
@@ -159,22 +466,21 @@ function createPublisher(platform) {
|
|
|
159
466
|
}
|
|
160
467
|
return true;
|
|
161
468
|
};
|
|
162
|
-
const createReady =
|
|
163
|
-
|
|
164
|
-
const backoffMS = retry?.backoffMS ?? 2e3;
|
|
165
|
-
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
469
|
+
const createReady = (token, params, label, wait, retry) => withRetry(
|
|
470
|
+
async () => {
|
|
166
471
|
const id = await platform.createContainer(token, params);
|
|
167
|
-
if (id
|
|
168
|
-
|
|
169
|
-
|
|
472
|
+
if (!id || !await isReady(token, id, label, wait)) return null;
|
|
473
|
+
return id;
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
...retry,
|
|
477
|
+
onRetry: (attempt, attempts, delayMS) => {
|
|
170
478
|
console.warn(
|
|
171
|
-
`[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${
|
|
479
|
+
`[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${delayMS}ms`
|
|
172
480
|
);
|
|
173
|
-
await sleep(delay);
|
|
174
481
|
}
|
|
175
482
|
}
|
|
176
|
-
|
|
177
|
-
};
|
|
483
|
+
);
|
|
178
484
|
return {
|
|
179
485
|
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
180
486
|
publish: async (token, params, wait, retry) => {
|
|
@@ -337,11 +643,11 @@ var sendInstagramMessage = (token, to, text) => {
|
|
|
337
643
|
};
|
|
338
644
|
|
|
339
645
|
// src/socials/meta/webhook.ts
|
|
340
|
-
import { NextResponse } from "next/server";
|
|
646
|
+
import { NextResponse as NextResponse3 } from "next/server";
|
|
341
647
|
var verifyWebhook = (secret) => (req) => {
|
|
342
648
|
const params = req.nextUrl.searchParams;
|
|
343
649
|
if (params.get("hub.verify_token") === secret) {
|
|
344
|
-
return new
|
|
650
|
+
return new NextResponse3(params.get("hub.challenge"));
|
|
345
651
|
}
|
|
346
652
|
};
|
|
347
653
|
|
|
@@ -474,11 +780,15 @@ var setupThreadsWebhook = (options) => {
|
|
|
474
780
|
};
|
|
475
781
|
};
|
|
476
782
|
export {
|
|
783
|
+
InstagramProvider,
|
|
477
784
|
MetaMediaType,
|
|
785
|
+
SocialMediaKind,
|
|
786
|
+
SocialPlatform,
|
|
478
787
|
canPublishToInstagram,
|
|
479
788
|
createInstagramPost,
|
|
480
789
|
createThread,
|
|
481
790
|
createThreadsPost,
|
|
791
|
+
getInstagramAuthorizationURL,
|
|
482
792
|
getInstagramConversation,
|
|
483
793
|
getInstagramConversationByUser,
|
|
484
794
|
getInstagramConversations,
|
|
@@ -487,13 +797,18 @@ export {
|
|
|
487
797
|
getInstagramMedia,
|
|
488
798
|
getInstagramMessage,
|
|
489
799
|
getInstagramUser,
|
|
800
|
+
getLongLivedInstagramToken,
|
|
490
801
|
getThread,
|
|
491
802
|
getThreads,
|
|
492
803
|
getThreadsData,
|
|
493
804
|
getThreadsReplies,
|
|
805
|
+
pollUntilReady,
|
|
494
806
|
readGraphID,
|
|
807
|
+
refreshInstagramAccessToken,
|
|
495
808
|
replyToInstagramComment,
|
|
496
809
|
sendInstagramMessage,
|
|
497
810
|
setupInstagramWebhook,
|
|
498
|
-
|
|
811
|
+
setupSocialAuth,
|
|
812
|
+
setupThreadsWebhook,
|
|
813
|
+
withRetry
|
|
499
814
|
};
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/socials/instagram/adapter.ts
|
|
21
|
+
var adapter_exports = {};
|
|
22
|
+
__export(adapter_exports, {
|
|
23
|
+
InstagramProvider: () => InstagramProvider
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(adapter_exports);
|
|
26
|
+
|
|
27
|
+
// src/env.ts
|
|
28
|
+
var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
|
|
29
|
+
var getEnvValue = (key) => {
|
|
30
|
+
switch (key) {
|
|
31
|
+
case "NEXT_PUBLIC_GRAPHQL_ENDPOINT" /* NEXT_PUBLIC_GRAPHQL_ENDPOINT */:
|
|
32
|
+
return process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT || EXTRA_ENV.GRAPHQL_ENDPOINT;
|
|
33
|
+
case "NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT" /* NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT */:
|
|
34
|
+
return process.env.NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT || EXTRA_ENV.EMAIL_AUTH_ENDPOINT;
|
|
35
|
+
case "NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT" /* NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT */:
|
|
36
|
+
return process.env.NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT || EXTRA_ENV.GOOGLE_AUTH_ENDPOINT;
|
|
37
|
+
case "NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT" /* NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT */:
|
|
38
|
+
return process.env.NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT || EXTRA_ENV.SOCIAL_AUTH_ENDPOINT;
|
|
39
|
+
case "NEXT_PUBLIC_FILE_ENDPOINT" /* NEXT_PUBLIC_FILE_ENDPOINT */:
|
|
40
|
+
return process.env.NEXT_PUBLIC_FILE_ENDPOINT || EXTRA_ENV.FILE_ENDPOINT;
|
|
41
|
+
case "NEXT_PUBLIC_BASE_URL" /* NEXT_PUBLIC_BASE_URL */:
|
|
42
|
+
return process.env.NEXT_PUBLIC_BASE_URL || EXTRA_ENV.BASE_URL;
|
|
43
|
+
case "REFRESH_KEY" /* REFRESH_KEY */:
|
|
44
|
+
return process.env.REFRESH_KEY;
|
|
45
|
+
case "SIGNING_KEY" /* SIGNING_KEY */:
|
|
46
|
+
return process.env.SIGNING_KEY;
|
|
47
|
+
case "INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */:
|
|
48
|
+
return process.env.INSTAGRAM_CLIENT_SECRET;
|
|
49
|
+
case "INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */:
|
|
50
|
+
return process.env.INSTAGRAM_CLIENT_ID;
|
|
51
|
+
case "GOOGLE_CLIENT_SECRET" /* GOOGLE_CLIENT_SECRET */:
|
|
52
|
+
return process.env.GOOGLE_CLIENT_SECRET;
|
|
53
|
+
case "GOOGLE_CLIENT_ID" /* GOOGLE_CLIENT_ID */:
|
|
54
|
+
return process.env.GOOGLE_CLIENT_ID;
|
|
55
|
+
case "TURNSTILE_KEY" /* TURNSTILE_KEY */:
|
|
56
|
+
return process.env.TURNSTILE_KEY;
|
|
57
|
+
case "S3_ACCESS_KEY_ID" /* S3_ACCESS_KEY_ID */:
|
|
58
|
+
return process.env.S3_ACCESS_KEY_ID;
|
|
59
|
+
case "S3_ACCESS_KEY_SECRET" /* S3_ACCESS_KEY_SECRET */:
|
|
60
|
+
return process.env.S3_ACCESS_KEY_SECRET;
|
|
61
|
+
case "S3_REGION" /* S3_REGION */:
|
|
62
|
+
return process.env.S3_REGION;
|
|
63
|
+
case "S3_BUCKET" /* S3_BUCKET */:
|
|
64
|
+
return process.env.S3_BUCKET;
|
|
65
|
+
case "NODE_ENV" /* NODE_ENV */:
|
|
66
|
+
return process.env.NODE_ENV;
|
|
67
|
+
default:
|
|
68
|
+
return process.env[key];
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
function getEnv(key, skipCheck) {
|
|
72
|
+
const value = getEnvValue(key);
|
|
73
|
+
if (!skipCheck && !value) throw new Error(`${key} is not defined`);
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/socials/instagram/auth.ts
|
|
78
|
+
var getInstagramAuthorizationURL = (state, redirectURI, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
79
|
+
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
80
|
+
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope=${scopes.join(",")}&state=${state}&redirect_uri=${redirectURI}#weblink`;
|
|
81
|
+
async function getLongLivedInstagramToken(code, redirectURI) {
|
|
82
|
+
const formData = new FormData();
|
|
83
|
+
formData.set("client_id", getEnv("INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */));
|
|
84
|
+
formData.set("client_secret", getEnv("INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */));
|
|
85
|
+
formData.set("grant_type", "authorization_code");
|
|
86
|
+
formData.set("redirect_uri", redirectURI);
|
|
87
|
+
formData.set("code", code);
|
|
88
|
+
const shortRes = await fetch("https://api.instagram.com/oauth/access_token", {
|
|
89
|
+
method: "POST",
|
|
90
|
+
body: formData
|
|
91
|
+
});
|
|
92
|
+
if (!shortRes.ok) return void 0;
|
|
93
|
+
const shortResData = await shortRes.json();
|
|
94
|
+
if (!shortResData.access_token) return void 0;
|
|
95
|
+
const longRes = await fetch(
|
|
96
|
+
`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 */)}`
|
|
97
|
+
);
|
|
98
|
+
if (!longRes.ok) return void 0;
|
|
99
|
+
const longResData = await longRes.json();
|
|
100
|
+
return {
|
|
101
|
+
accessToken: longResData.access_token,
|
|
102
|
+
userId: shortResData.user_id,
|
|
103
|
+
expiresIn: longResData.expires_in,
|
|
104
|
+
permissions: shortResData.permissions
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async function refreshInstagramAccessToken(token) {
|
|
108
|
+
const request = await fetch(
|
|
109
|
+
`https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=${token}`
|
|
110
|
+
);
|
|
111
|
+
const response = await request.json();
|
|
112
|
+
if (!response.access_token) return void 0;
|
|
113
|
+
return {
|
|
114
|
+
accessToken: response.access_token,
|
|
115
|
+
expiresIn: response.expires_in
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/socials/meta/request.ts
|
|
120
|
+
function createGraphClient(baseURL) {
|
|
121
|
+
const buildURL = (token, path, params = {}) => {
|
|
122
|
+
const search = new URLSearchParams();
|
|
123
|
+
for (const [key, value] of Object.entries(params)) {
|
|
124
|
+
if (value !== void 0) search.set(key, String(value));
|
|
125
|
+
}
|
|
126
|
+
search.set("access_token", token);
|
|
127
|
+
return `${baseURL}/${path}?${search.toString()}`;
|
|
128
|
+
};
|
|
129
|
+
const request = async (token, path, options = {}) => {
|
|
130
|
+
const response = await fetch(buildURL(token, path, options.params), {
|
|
131
|
+
method: options.method || (options.body ? "POST" : "GET"),
|
|
132
|
+
headers: {
|
|
133
|
+
Authorization: `Bearer ${token}`,
|
|
134
|
+
...options.body ? { "Content-Type": "application/json" } : {}
|
|
135
|
+
},
|
|
136
|
+
...options.body ? { body: JSON.stringify(options.body) } : {}
|
|
137
|
+
});
|
|
138
|
+
return response.json().catch(() => null);
|
|
139
|
+
};
|
|
140
|
+
return { buildURL, request };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/socials/instagram/utils.ts
|
|
144
|
+
var client = createGraphClient("https://graph.instagram.com/v23.0");
|
|
145
|
+
var getInstagramData = client.request;
|
|
146
|
+
|
|
147
|
+
// src/socials/instagram/getters.ts
|
|
148
|
+
var getInstagramUser = (token, id, fields) => {
|
|
149
|
+
return getInstagramData(token, id || "me", {
|
|
150
|
+
params: {
|
|
151
|
+
fields: fields ? fields.join(",") : "username,followers_count,media_count"
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
var getInstagramMedia = (token, fields, limit = 12) => {
|
|
156
|
+
return getInstagramData(token, "me/media", {
|
|
157
|
+
params: {
|
|
158
|
+
fields: fields ? fields.join(",") : "like_count,comments_count,permalink",
|
|
159
|
+
limit
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// src/socials/instagram/adapter.ts
|
|
165
|
+
var PROFILE_FIELDS = [
|
|
166
|
+
"id",
|
|
167
|
+
"user_id",
|
|
168
|
+
"username",
|
|
169
|
+
"name",
|
|
170
|
+
"profile_picture_url",
|
|
171
|
+
"followers_count",
|
|
172
|
+
"media_count"
|
|
173
|
+
];
|
|
174
|
+
var MEDIA_FIELDS = [
|
|
175
|
+
"id",
|
|
176
|
+
"caption",
|
|
177
|
+
"media_type",
|
|
178
|
+
"media_url",
|
|
179
|
+
"permalink",
|
|
180
|
+
"thumbnail_url",
|
|
181
|
+
"timestamp",
|
|
182
|
+
"like_count",
|
|
183
|
+
"comments_count"
|
|
184
|
+
];
|
|
185
|
+
var expiresAt = (seconds) => seconds ? new Date(Date.now() + seconds * 1e3) : void 0;
|
|
186
|
+
function toKind(mediaType) {
|
|
187
|
+
if (mediaType === "VIDEO") return "video" /* Video */;
|
|
188
|
+
if (mediaType === "CAROUSEL_ALBUM") return "carousel" /* Carousel */;
|
|
189
|
+
return "image" /* Image */;
|
|
190
|
+
}
|
|
191
|
+
function toPost(media) {
|
|
192
|
+
const kind = toKind(media.media_type);
|
|
193
|
+
return {
|
|
194
|
+
platform: "instagram" /* Instagram */,
|
|
195
|
+
platformMediaId: media.id,
|
|
196
|
+
permalink: media.permalink,
|
|
197
|
+
// A video's `media_url` is the .mp4 itself, so it is never a stand-in for a
|
|
198
|
+
// missing poster frame — better no thumbnail than one that can't render.
|
|
199
|
+
thumbnail: media.thumbnail_url ?? (kind === "video" /* Video */ ? null : media.media_url ?? null),
|
|
200
|
+
mediaURL: media.media_url ?? null,
|
|
201
|
+
caption: media.caption ?? null,
|
|
202
|
+
kind,
|
|
203
|
+
publishedAt: media.timestamp,
|
|
204
|
+
likes: media.like_count ?? null,
|
|
205
|
+
comments: media.comments_count ?? null,
|
|
206
|
+
// Both need the Insights API; the basic media fields carry neither.
|
|
207
|
+
views: null,
|
|
208
|
+
shares: null,
|
|
209
|
+
metadata: {}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
var InstagramProvider = {
|
|
213
|
+
platform: "instagram" /* Instagram */,
|
|
214
|
+
auth: {
|
|
215
|
+
authorizationURL: ({ state, redirectURI, scopes }) => getInstagramAuthorizationURL(
|
|
216
|
+
state,
|
|
217
|
+
redirectURI,
|
|
218
|
+
scopes
|
|
219
|
+
),
|
|
220
|
+
exchangeCode: async ({ code, redirectURI }) => {
|
|
221
|
+
const result = await getLongLivedInstagramToken(code, redirectURI);
|
|
222
|
+
if (!result) {
|
|
223
|
+
console.error("[naystack] Instagram code exchange returned no token");
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
accessToken: result.accessToken,
|
|
228
|
+
platformUserId: result.userId || null,
|
|
229
|
+
expiresAt: expiresAt(result.expiresIn),
|
|
230
|
+
scopes: result.permissions
|
|
231
|
+
};
|
|
232
|
+
},
|
|
233
|
+
refresh: async ({ accessToken }) => {
|
|
234
|
+
const refreshed = await refreshInstagramAccessToken(accessToken);
|
|
235
|
+
if (!refreshed) return null;
|
|
236
|
+
return {
|
|
237
|
+
accessToken: refreshed.accessToken,
|
|
238
|
+
expiresAt: expiresAt(refreshed.expiresIn)
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
profileURL: (username) => `https://instagram.com/${username}`,
|
|
243
|
+
fetchProfile: async (accessToken) => {
|
|
244
|
+
const user = await getInstagramUser(
|
|
245
|
+
accessToken,
|
|
246
|
+
void 0,
|
|
247
|
+
PROFILE_FIELDS
|
|
248
|
+
);
|
|
249
|
+
if (!user?.username) return null;
|
|
250
|
+
return {
|
|
251
|
+
platform: "instagram" /* Instagram */,
|
|
252
|
+
// `user_id` is what the OAuth exchange hands back, so preferring it keeps
|
|
253
|
+
// one account's id identical however it was read.
|
|
254
|
+
platformUserId: user.user_id || user.id || null,
|
|
255
|
+
username: user.username,
|
|
256
|
+
displayName: user.name ?? null,
|
|
257
|
+
avatar: user.profile_picture_url ?? null,
|
|
258
|
+
followers: user.followers_count ?? 0,
|
|
259
|
+
contentCount: user.media_count ?? 0,
|
|
260
|
+
metadata: {}
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
fetchMedia: async (accessToken, options) => {
|
|
264
|
+
const result = await getInstagramMedia(
|
|
265
|
+
accessToken,
|
|
266
|
+
MEDIA_FIELDS,
|
|
267
|
+
options?.limit
|
|
268
|
+
);
|
|
269
|
+
if (!result?.data) return [];
|
|
270
|
+
return result.data.map(toPost);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
274
|
+
0 && (module.exports = {
|
|
275
|
+
InstagramProvider
|
|
276
|
+
});
|