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
|
@@ -17,14 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
|
|
20
|
-
// src/
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
20
|
+
// src/socials/instagram/auth.ts
|
|
21
|
+
var auth_exports = {};
|
|
22
|
+
__export(auth_exports, {
|
|
23
23
|
getInstagramAuthorizationURL: () => getInstagramAuthorizationURL,
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
getLongLivedInstagramToken: () => getLongLivedInstagramToken,
|
|
25
|
+
refreshInstagramAccessToken: () => refreshInstagramAccessToken
|
|
26
26
|
});
|
|
27
|
-
module.exports = __toCommonJS(
|
|
27
|
+
module.exports = __toCommonJS(auth_exports);
|
|
28
28
|
|
|
29
29
|
// src/env.ts
|
|
30
30
|
var EXTRA_ENV = globalThis.__NAYSTACK_ENV__ || (globalThis.__NAYSTACK_ENV__ = {});
|
|
@@ -36,8 +36,8 @@ var getEnvValue = (key) => {
|
|
|
36
36
|
return process.env.NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT || EXTRA_ENV.EMAIL_AUTH_ENDPOINT;
|
|
37
37
|
case "NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT" /* NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT */:
|
|
38
38
|
return process.env.NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT || EXTRA_ENV.GOOGLE_AUTH_ENDPOINT;
|
|
39
|
-
case "
|
|
40
|
-
return process.env.
|
|
39
|
+
case "NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT" /* NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT */:
|
|
40
|
+
return process.env.NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT || EXTRA_ENV.SOCIAL_AUTH_ENDPOINT;
|
|
41
41
|
case "NEXT_PUBLIC_FILE_ENDPOINT" /* NEXT_PUBLIC_FILE_ENDPOINT */:
|
|
42
42
|
return process.env.NEXT_PUBLIC_FILE_ENDPOINT || EXTRA_ENV.FILE_ENDPOINT;
|
|
43
43
|
case "NEXT_PUBLIC_BASE_URL" /* NEXT_PUBLIC_BASE_URL */:
|
|
@@ -76,48 +76,50 @@ function getEnv(key, skipCheck) {
|
|
|
76
76
|
return value;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// src/
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const response = await request.json();
|
|
85
|
-
return response.access_token;
|
|
86
|
-
}
|
|
87
|
-
async function getLongLivedToken(code, redirectURL, clientId, clientSecret) {
|
|
79
|
+
// src/socials/instagram/auth.ts
|
|
80
|
+
var getInstagramAuthorizationURL = (state, redirectURI, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
81
|
+
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
82
|
+
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope=${scopes.join(",")}&state=${state}&redirect_uri=${redirectURI}#weblink`;
|
|
83
|
+
async function getLongLivedInstagramToken(code, redirectURI) {
|
|
88
84
|
const formData = new FormData();
|
|
89
|
-
formData.set("client_id",
|
|
90
|
-
formData.set("client_secret",
|
|
85
|
+
formData.set("client_id", getEnv("INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */));
|
|
86
|
+
formData.set("client_secret", getEnv("INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */));
|
|
91
87
|
formData.set("grant_type", "authorization_code");
|
|
92
|
-
formData.set("redirect_uri",
|
|
88
|
+
formData.set("redirect_uri", redirectURI);
|
|
93
89
|
formData.set("code", code);
|
|
94
90
|
const shortRes = await fetch("https://api.instagram.com/oauth/access_token", {
|
|
95
91
|
method: "POST",
|
|
96
92
|
body: formData
|
|
97
93
|
});
|
|
98
|
-
if (shortRes.ok)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
94
|
+
if (!shortRes.ok) return void 0;
|
|
95
|
+
const shortResData = await shortRes.json();
|
|
96
|
+
if (!shortResData.access_token) return void 0;
|
|
97
|
+
const longRes = await fetch(
|
|
98
|
+
`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 */)}`
|
|
99
|
+
);
|
|
100
|
+
if (!longRes.ok) return void 0;
|
|
101
|
+
const longResData = await longRes.json();
|
|
102
|
+
return {
|
|
103
|
+
accessToken: longResData.access_token,
|
|
104
|
+
userId: shortResData.user_id,
|
|
105
|
+
expiresIn: longResData.expires_in,
|
|
106
|
+
permissions: shortResData.permissions
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
async function refreshInstagramAccessToken(token) {
|
|
110
|
+
const request = await fetch(
|
|
111
|
+
`https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=${token}`
|
|
112
|
+
);
|
|
113
|
+
const response = await request.json();
|
|
114
|
+
if (!response.access_token) return void 0;
|
|
115
|
+
return {
|
|
116
|
+
accessToken: response.access_token,
|
|
117
|
+
expiresIn: response.expires_in
|
|
118
|
+
};
|
|
114
119
|
}
|
|
115
|
-
var getInstagramAuthorizationURL = (token, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
116
|
-
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
117
|
-
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope=${scopes.join(",")}&state=${token}&redirect_uri=${getEnv("NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT" /* NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT */)}#weblink`;
|
|
118
120
|
// Annotate the CommonJS export names for ESM import in node:
|
|
119
121
|
0 && (module.exports = {
|
|
120
122
|
getInstagramAuthorizationURL,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
getLongLivedInstagramToken,
|
|
124
|
+
refreshInstagramAccessToken
|
|
123
125
|
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permissions requestable during Instagram Login OAuth. `instagram_business_basic`
|
|
3
|
+
* is enough to read a profile; publishing needs `instagram_business_content_publish`.
|
|
4
|
+
*
|
|
5
|
+
* Widening the scopes only affects users who authorize afterwards — tokens already
|
|
6
|
+
* stored keep the permissions they were granted, so existing users must reconnect.
|
|
7
|
+
*
|
|
8
|
+
* @category Socials
|
|
9
|
+
*/
|
|
10
|
+
type InstagramScope = "instagram_business_basic" | "instagram_business_content_publish" | "instagram_business_manage_messages" | "instagram_business_manage_comments" | "instagram_business_manage_insights";
|
|
11
|
+
/**
|
|
12
|
+
* Builds the Instagram OAuth authorization URL.
|
|
13
|
+
*
|
|
14
|
+
* **Server-only** — reads the non-public `INSTAGRAM_CLIENT_ID`. The client id still
|
|
15
|
+
* ends up in the URL the browser follows; it is simply built here rather than
|
|
16
|
+
* shipped in the client bundle.
|
|
17
|
+
*
|
|
18
|
+
* @param state - Passed back by Instagram on the callback; typically the logged-in user's access token.
|
|
19
|
+
* @param redirectURI - Where Instagram sends the user back. Must match a URI registered with Meta verbatim.
|
|
20
|
+
* @param scopes - Default: `["instagram_business_basic"]`.
|
|
21
|
+
*
|
|
22
|
+
* @category Socials
|
|
23
|
+
*/
|
|
24
|
+
declare const getInstagramAuthorizationURL: (state: string, redirectURI: string, scopes?: InstagramScope[]) => string;
|
|
25
|
+
/**
|
|
26
|
+
* Exchanges an OAuth code for a long-lived Instagram access token.
|
|
27
|
+
*
|
|
28
|
+
* @param code - Authorization code from the callback.
|
|
29
|
+
* @param redirectURI - The same URI used to obtain the code.
|
|
30
|
+
* @returns `{ accessToken, userId, expiresIn, permissions }`, or `undefined` if either exchange failed.
|
|
31
|
+
*
|
|
32
|
+
* @category Socials
|
|
33
|
+
*/
|
|
34
|
+
declare function getLongLivedInstagramToken(code: string, redirectURI: string): Promise<{
|
|
35
|
+
accessToken: string;
|
|
36
|
+
userId: string | undefined;
|
|
37
|
+
expiresIn: number;
|
|
38
|
+
permissions: string[] | undefined;
|
|
39
|
+
} | undefined>;
|
|
40
|
+
/**
|
|
41
|
+
* Refreshes a long-lived Instagram token.
|
|
42
|
+
*
|
|
43
|
+
* @returns `{ accessToken, expiresIn }` (seconds), or `undefined` if the refresh failed.
|
|
44
|
+
*
|
|
45
|
+
* @category Socials
|
|
46
|
+
*/
|
|
47
|
+
declare function refreshInstagramAccessToken(token: string): Promise<{
|
|
48
|
+
accessToken: string;
|
|
49
|
+
expiresIn: number | undefined;
|
|
50
|
+
} | undefined>;
|
|
51
|
+
|
|
52
|
+
export { type InstagramScope, getInstagramAuthorizationURL, getLongLivedInstagramToken, refreshInstagramAccessToken };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permissions requestable during Instagram Login OAuth. `instagram_business_basic`
|
|
3
|
+
* is enough to read a profile; publishing needs `instagram_business_content_publish`.
|
|
4
|
+
*
|
|
5
|
+
* Widening the scopes only affects users who authorize afterwards — tokens already
|
|
6
|
+
* stored keep the permissions they were granted, so existing users must reconnect.
|
|
7
|
+
*
|
|
8
|
+
* @category Socials
|
|
9
|
+
*/
|
|
10
|
+
type InstagramScope = "instagram_business_basic" | "instagram_business_content_publish" | "instagram_business_manage_messages" | "instagram_business_manage_comments" | "instagram_business_manage_insights";
|
|
11
|
+
/**
|
|
12
|
+
* Builds the Instagram OAuth authorization URL.
|
|
13
|
+
*
|
|
14
|
+
* **Server-only** — reads the non-public `INSTAGRAM_CLIENT_ID`. The client id still
|
|
15
|
+
* ends up in the URL the browser follows; it is simply built here rather than
|
|
16
|
+
* shipped in the client bundle.
|
|
17
|
+
*
|
|
18
|
+
* @param state - Passed back by Instagram on the callback; typically the logged-in user's access token.
|
|
19
|
+
* @param redirectURI - Where Instagram sends the user back. Must match a URI registered with Meta verbatim.
|
|
20
|
+
* @param scopes - Default: `["instagram_business_basic"]`.
|
|
21
|
+
*
|
|
22
|
+
* @category Socials
|
|
23
|
+
*/
|
|
24
|
+
declare const getInstagramAuthorizationURL: (state: string, redirectURI: string, scopes?: InstagramScope[]) => string;
|
|
25
|
+
/**
|
|
26
|
+
* Exchanges an OAuth code for a long-lived Instagram access token.
|
|
27
|
+
*
|
|
28
|
+
* @param code - Authorization code from the callback.
|
|
29
|
+
* @param redirectURI - The same URI used to obtain the code.
|
|
30
|
+
* @returns `{ accessToken, userId, expiresIn, permissions }`, or `undefined` if either exchange failed.
|
|
31
|
+
*
|
|
32
|
+
* @category Socials
|
|
33
|
+
*/
|
|
34
|
+
declare function getLongLivedInstagramToken(code: string, redirectURI: string): Promise<{
|
|
35
|
+
accessToken: string;
|
|
36
|
+
userId: string | undefined;
|
|
37
|
+
expiresIn: number;
|
|
38
|
+
permissions: string[] | undefined;
|
|
39
|
+
} | undefined>;
|
|
40
|
+
/**
|
|
41
|
+
* Refreshes a long-lived Instagram token.
|
|
42
|
+
*
|
|
43
|
+
* @returns `{ accessToken, expiresIn }` (seconds), or `undefined` if the refresh failed.
|
|
44
|
+
*
|
|
45
|
+
* @category Socials
|
|
46
|
+
*/
|
|
47
|
+
declare function refreshInstagramAccessToken(token: string): Promise<{
|
|
48
|
+
accessToken: string;
|
|
49
|
+
expiresIn: number | undefined;
|
|
50
|
+
} | undefined>;
|
|
51
|
+
|
|
52
|
+
export { type InstagramScope, getInstagramAuthorizationURL, getLongLivedInstagramToken, refreshInstagramAccessToken };
|
|
@@ -8,8 +8,8 @@ var getEnvValue = (key) => {
|
|
|
8
8
|
return process.env.NEXT_PUBLIC_EMAIL_AUTH_ENDPOINT || EXTRA_ENV.EMAIL_AUTH_ENDPOINT;
|
|
9
9
|
case "NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT" /* NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT */:
|
|
10
10
|
return process.env.NEXT_PUBLIC_GOOGLE_AUTH_ENDPOINT || EXTRA_ENV.GOOGLE_AUTH_ENDPOINT;
|
|
11
|
-
case "
|
|
12
|
-
return process.env.
|
|
11
|
+
case "NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT" /* NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT */:
|
|
12
|
+
return process.env.NEXT_PUBLIC_SOCIAL_AUTH_ENDPOINT || EXTRA_ENV.SOCIAL_AUTH_ENDPOINT;
|
|
13
13
|
case "NEXT_PUBLIC_FILE_ENDPOINT" /* NEXT_PUBLIC_FILE_ENDPOINT */:
|
|
14
14
|
return process.env.NEXT_PUBLIC_FILE_ENDPOINT || EXTRA_ENV.FILE_ENDPOINT;
|
|
15
15
|
case "NEXT_PUBLIC_BASE_URL" /* NEXT_PUBLIC_BASE_URL */:
|
|
@@ -48,47 +48,49 @@ function getEnv(key, skipCheck) {
|
|
|
48
48
|
return value;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
// src/
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const response = await request.json();
|
|
57
|
-
return response.access_token;
|
|
58
|
-
}
|
|
59
|
-
async function getLongLivedToken(code, redirectURL, clientId, clientSecret) {
|
|
51
|
+
// src/socials/instagram/auth.ts
|
|
52
|
+
var getInstagramAuthorizationURL = (state, redirectURI, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
53
|
+
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
54
|
+
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope=${scopes.join(",")}&state=${state}&redirect_uri=${redirectURI}#weblink`;
|
|
55
|
+
async function getLongLivedInstagramToken(code, redirectURI) {
|
|
60
56
|
const formData = new FormData();
|
|
61
|
-
formData.set("client_id",
|
|
62
|
-
formData.set("client_secret",
|
|
57
|
+
formData.set("client_id", getEnv("INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */));
|
|
58
|
+
formData.set("client_secret", getEnv("INSTAGRAM_CLIENT_SECRET" /* INSTAGRAM_CLIENT_SECRET */));
|
|
63
59
|
formData.set("grant_type", "authorization_code");
|
|
64
|
-
formData.set("redirect_uri",
|
|
60
|
+
formData.set("redirect_uri", redirectURI);
|
|
65
61
|
formData.set("code", code);
|
|
66
62
|
const shortRes = await fetch("https://api.instagram.com/oauth/access_token", {
|
|
67
63
|
method: "POST",
|
|
68
64
|
body: formData
|
|
69
65
|
});
|
|
70
|
-
if (shortRes.ok)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
66
|
+
if (!shortRes.ok) return void 0;
|
|
67
|
+
const shortResData = await shortRes.json();
|
|
68
|
+
if (!shortResData.access_token) return void 0;
|
|
69
|
+
const longRes = await fetch(
|
|
70
|
+
`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 */)}`
|
|
71
|
+
);
|
|
72
|
+
if (!longRes.ok) return void 0;
|
|
73
|
+
const longResData = await longRes.json();
|
|
74
|
+
return {
|
|
75
|
+
accessToken: longResData.access_token,
|
|
76
|
+
userId: shortResData.user_id,
|
|
77
|
+
expiresIn: longResData.expires_in,
|
|
78
|
+
permissions: shortResData.permissions
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function refreshInstagramAccessToken(token) {
|
|
82
|
+
const request = await fetch(
|
|
83
|
+
`https://graph.instagram.com/refresh_access_token?grant_type=ig_refresh_token&access_token=${token}`
|
|
84
|
+
);
|
|
85
|
+
const response = await request.json();
|
|
86
|
+
if (!response.access_token) return void 0;
|
|
87
|
+
return {
|
|
88
|
+
accessToken: response.access_token,
|
|
89
|
+
expiresIn: response.expires_in
|
|
90
|
+
};
|
|
86
91
|
}
|
|
87
|
-
var getInstagramAuthorizationURL = (token, scopes = ["instagram_business_basic"]) => `https://www.instagram.com/oauth/authorize/?client_id=${getEnv(
|
|
88
|
-
"INSTAGRAM_CLIENT_ID" /* INSTAGRAM_CLIENT_ID */
|
|
89
|
-
)}&response_type=code&enable_fb_login=0&force_authentication=1&scope=${scopes.join(",")}&state=${token}&redirect_uri=${getEnv("NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT" /* NEXT_PUBLIC_INSTAGRAM_AUTH_ENDPOINT */)}#weblink`;
|
|
90
92
|
export {
|
|
91
93
|
getInstagramAuthorizationURL,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
getLongLivedInstagramToken,
|
|
95
|
+
refreshInstagramAccessToken
|
|
94
96
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GraphError, ContainerState } from '../meta/types.mjs';
|
|
2
2
|
import { InstagramConversation, InstagramMedia, InstagramMessage, InstagramUser } from './types.mjs';
|
|
3
3
|
import '../meta/container.mjs';
|
|
4
|
+
import '../utils/poll.mjs';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Fetches an Instagram user's profile. Defaults to the authenticated user (`"me"`).
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GraphError, ContainerState } from '../meta/types.js';
|
|
2
2
|
import { InstagramConversation, InstagramMedia, InstagramMessage, InstagramUser } from './types.js';
|
|
3
3
|
import '../meta/container.js';
|
|
4
|
+
import '../utils/poll.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Fetches an Instagram user's profile. Defaults to the authenticated user (`"me"`).
|
|
@@ -71,25 +71,42 @@ var getInstagramContainerStatus = async (token, id) => {
|
|
|
71
71
|
};
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
// src/socials/
|
|
74
|
+
// src/socials/utils/poll.ts
|
|
75
75
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
76
|
-
async function
|
|
76
|
+
async function pollUntilReady(read, isPending, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
77
77
|
const deadline = Date.now() + timeoutMS;
|
|
78
|
-
let state = await
|
|
78
|
+
let state = await read();
|
|
79
79
|
if (!state) {
|
|
80
80
|
await sleep(2e3);
|
|
81
81
|
return null;
|
|
82
82
|
}
|
|
83
|
-
while (state
|
|
83
|
+
while (state && isPending(state) && Date.now() < deadline) {
|
|
84
84
|
await sleep(intervalMS);
|
|
85
|
-
state = await
|
|
85
|
+
state = await read();
|
|
86
86
|
}
|
|
87
87
|
return state;
|
|
88
88
|
}
|
|
89
|
+
async function withRetry(run, options = {}) {
|
|
90
|
+
const attempts = Math.max(1, options.attempts ?? 3);
|
|
91
|
+
const backoffMS = options.backoffMS ?? 2e3;
|
|
92
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
93
|
+
const result = await run(attempt);
|
|
94
|
+
if (result) return result;
|
|
95
|
+
if (attempt < attempts) {
|
|
96
|
+
const delayMS = backoffMS * 2 ** (attempt - 1);
|
|
97
|
+
options.onRetry?.(attempt, attempts, delayMS);
|
|
98
|
+
await sleep(delayMS);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/socials/meta/container.ts
|
|
89
105
|
function createPublisher(platform) {
|
|
90
106
|
const isReady = async (token, id, label, wait) => {
|
|
91
|
-
const state = await
|
|
107
|
+
const state = await pollUntilReady(
|
|
92
108
|
() => platform.getStatus(token, id),
|
|
109
|
+
(current) => current.status === "IN_PROGRESS",
|
|
93
110
|
wait
|
|
94
111
|
);
|
|
95
112
|
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
@@ -100,22 +117,21 @@ function createPublisher(platform) {
|
|
|
100
117
|
}
|
|
101
118
|
return true;
|
|
102
119
|
};
|
|
103
|
-
const createReady =
|
|
104
|
-
|
|
105
|
-
const backoffMS = retry?.backoffMS ?? 2e3;
|
|
106
|
-
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
120
|
+
const createReady = (token, params, label, wait, retry) => withRetry(
|
|
121
|
+
async () => {
|
|
107
122
|
const id = await platform.createContainer(token, params);
|
|
108
|
-
if (id
|
|
109
|
-
|
|
110
|
-
|
|
123
|
+
if (!id || !await isReady(token, id, label, wait)) return null;
|
|
124
|
+
return id;
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
...retry,
|
|
128
|
+
onRetry: (attempt, attempts, delayMS) => {
|
|
111
129
|
console.warn(
|
|
112
|
-
`[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${
|
|
130
|
+
`[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${delayMS}ms`
|
|
113
131
|
);
|
|
114
|
-
await sleep(delay);
|
|
115
132
|
}
|
|
116
133
|
}
|
|
117
|
-
|
|
118
|
-
};
|
|
134
|
+
);
|
|
119
135
|
return {
|
|
120
136
|
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
121
137
|
publish: async (token, params, wait, retry) => {
|
|
@@ -129,21 +145,30 @@ function createPublisher(platform) {
|
|
|
129
145
|
if (!containerID) return null;
|
|
130
146
|
return platform.publish(token, containerID);
|
|
131
147
|
},
|
|
132
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* Creates and awaits every carousel child **in parallel**, resolving to
|
|
150
|
+
* their ids in input order — or `null` if any failed.
|
|
151
|
+
*
|
|
152
|
+
* Parallel because each child is an independent create-then-poll and Meta
|
|
153
|
+
* spends ~5s accepting a single container, so ten sequential children cost
|
|
154
|
+
* ~50s of mostly waiting — past what a serverless caller can fit in its
|
|
155
|
+
* timeout. `Promise.all` preserves input order, which the carousel's slide
|
|
156
|
+
* order depends on.
|
|
157
|
+
*/
|
|
133
158
|
createChildren: async (token, items, wait, retry) => {
|
|
134
|
-
const children =
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return children;
|
|
159
|
+
const children = await Promise.all(
|
|
160
|
+
items.map(
|
|
161
|
+
(item) => createReady(
|
|
162
|
+
token,
|
|
163
|
+
{ ...item, is_carousel_item: true },
|
|
164
|
+
"carousel item",
|
|
165
|
+
wait,
|
|
166
|
+
retry
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
);
|
|
170
|
+
const created = children.filter((childID) => childID !== null);
|
|
171
|
+
return created.length === children.length ? created : null;
|
|
147
172
|
}
|
|
148
173
|
};
|
|
149
174
|
}
|
|
@@ -43,25 +43,42 @@ var getInstagramContainerStatus = async (token, id) => {
|
|
|
43
43
|
};
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
// src/socials/
|
|
46
|
+
// src/socials/utils/poll.ts
|
|
47
47
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
48
|
-
async function
|
|
48
|
+
async function pollUntilReady(read, isPending, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
49
49
|
const deadline = Date.now() + timeoutMS;
|
|
50
|
-
let state = await
|
|
50
|
+
let state = await read();
|
|
51
51
|
if (!state) {
|
|
52
52
|
await sleep(2e3);
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
|
-
while (state
|
|
55
|
+
while (state && isPending(state) && Date.now() < deadline) {
|
|
56
56
|
await sleep(intervalMS);
|
|
57
|
-
state = await
|
|
57
|
+
state = await read();
|
|
58
58
|
}
|
|
59
59
|
return state;
|
|
60
60
|
}
|
|
61
|
+
async function withRetry(run, options = {}) {
|
|
62
|
+
const attempts = Math.max(1, options.attempts ?? 3);
|
|
63
|
+
const backoffMS = options.backoffMS ?? 2e3;
|
|
64
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
65
|
+
const result = await run(attempt);
|
|
66
|
+
if (result) return result;
|
|
67
|
+
if (attempt < attempts) {
|
|
68
|
+
const delayMS = backoffMS * 2 ** (attempt - 1);
|
|
69
|
+
options.onRetry?.(attempt, attempts, delayMS);
|
|
70
|
+
await sleep(delayMS);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/socials/meta/container.ts
|
|
61
77
|
function createPublisher(platform) {
|
|
62
78
|
const isReady = async (token, id, label, wait) => {
|
|
63
|
-
const state = await
|
|
79
|
+
const state = await pollUntilReady(
|
|
64
80
|
() => platform.getStatus(token, id),
|
|
81
|
+
(current) => current.status === "IN_PROGRESS",
|
|
65
82
|
wait
|
|
66
83
|
);
|
|
67
84
|
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
@@ -72,22 +89,21 @@ function createPublisher(platform) {
|
|
|
72
89
|
}
|
|
73
90
|
return true;
|
|
74
91
|
};
|
|
75
|
-
const createReady =
|
|
76
|
-
|
|
77
|
-
const backoffMS = retry?.backoffMS ?? 2e3;
|
|
78
|
-
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
92
|
+
const createReady = (token, params, label, wait, retry) => withRetry(
|
|
93
|
+
async () => {
|
|
79
94
|
const id = await platform.createContainer(token, params);
|
|
80
|
-
if (id
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
if (!id || !await isReady(token, id, label, wait)) return null;
|
|
96
|
+
return id;
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
...retry,
|
|
100
|
+
onRetry: (attempt, attempts, delayMS) => {
|
|
83
101
|
console.warn(
|
|
84
|
-
`[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${
|
|
102
|
+
`[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${delayMS}ms`
|
|
85
103
|
);
|
|
86
|
-
await sleep(delay);
|
|
87
104
|
}
|
|
88
105
|
}
|
|
89
|
-
|
|
90
|
-
};
|
|
106
|
+
);
|
|
91
107
|
return {
|
|
92
108
|
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
93
109
|
publish: async (token, params, wait, retry) => {
|
|
@@ -101,21 +117,30 @@ function createPublisher(platform) {
|
|
|
101
117
|
if (!containerID) return null;
|
|
102
118
|
return platform.publish(token, containerID);
|
|
103
119
|
},
|
|
104
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Creates and awaits every carousel child **in parallel**, resolving to
|
|
122
|
+
* their ids in input order — or `null` if any failed.
|
|
123
|
+
*
|
|
124
|
+
* Parallel because each child is an independent create-then-poll and Meta
|
|
125
|
+
* spends ~5s accepting a single container, so ten sequential children cost
|
|
126
|
+
* ~50s of mostly waiting — past what a serverless caller can fit in its
|
|
127
|
+
* timeout. `Promise.all` preserves input order, which the carousel's slide
|
|
128
|
+
* order depends on.
|
|
129
|
+
*/
|
|
105
130
|
createChildren: async (token, items, wait, retry) => {
|
|
106
|
-
const children =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return children;
|
|
131
|
+
const children = await Promise.all(
|
|
132
|
+
items.map(
|
|
133
|
+
(item) => createReady(
|
|
134
|
+
token,
|
|
135
|
+
{ ...item, is_carousel_item: true },
|
|
136
|
+
"carousel item",
|
|
137
|
+
wait,
|
|
138
|
+
retry
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
);
|
|
142
|
+
const created = children.filter((childID) => childID !== null);
|
|
143
|
+
return created.length === children.length ? created : null;
|
|
119
144
|
}
|
|
120
145
|
};
|
|
121
146
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { WaitForContainerOptions
|
|
1
|
+
import { WaitForContainerOptions } from '../meta/container.mjs';
|
|
2
2
|
import { MetaMediaType } from '../meta/types.mjs';
|
|
3
|
+
import { RetryOptions } from '../utils/poll.mjs';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Instagram Messaging API message shape.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { WaitForContainerOptions
|
|
1
|
+
import { WaitForContainerOptions } from '../meta/container.js';
|
|
2
2
|
import { MetaMediaType } from '../meta/types.js';
|
|
3
|
+
import { RetryOptions } from '../utils/poll.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Instagram Messaging API message shape.
|