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
|
@@ -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) => {
|
|
@@ -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) => {
|
|
@@ -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.
|
|
@@ -23,24 +23,43 @@ __export(container_exports, {
|
|
|
23
23
|
createPublisher: () => createPublisher
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(container_exports);
|
|
26
|
+
|
|
27
|
+
// src/socials/utils/poll.ts
|
|
26
28
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
27
|
-
async function
|
|
29
|
+
async function pollUntilReady(read, isPending, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
28
30
|
const deadline = Date.now() + timeoutMS;
|
|
29
|
-
let state = await
|
|
31
|
+
let state = await read();
|
|
30
32
|
if (!state) {
|
|
31
33
|
await sleep(2e3);
|
|
32
34
|
return null;
|
|
33
35
|
}
|
|
34
|
-
while (state
|
|
36
|
+
while (state && isPending(state) && Date.now() < deadline) {
|
|
35
37
|
await sleep(intervalMS);
|
|
36
|
-
state = await
|
|
38
|
+
state = await read();
|
|
37
39
|
}
|
|
38
40
|
return state;
|
|
39
41
|
}
|
|
42
|
+
async function withRetry(run, options = {}) {
|
|
43
|
+
const attempts = Math.max(1, options.attempts ?? 3);
|
|
44
|
+
const backoffMS = options.backoffMS ?? 2e3;
|
|
45
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
46
|
+
const result = await run(attempt);
|
|
47
|
+
if (result) return result;
|
|
48
|
+
if (attempt < attempts) {
|
|
49
|
+
const delayMS = backoffMS * 2 ** (attempt - 1);
|
|
50
|
+
options.onRetry?.(attempt, attempts, delayMS);
|
|
51
|
+
await sleep(delayMS);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/socials/meta/container.ts
|
|
40
58
|
function createPublisher(platform) {
|
|
41
59
|
const isReady = async (token, id, label, wait) => {
|
|
42
|
-
const state = await
|
|
60
|
+
const state = await pollUntilReady(
|
|
43
61
|
() => platform.getStatus(token, id),
|
|
62
|
+
(current) => current.status === "IN_PROGRESS",
|
|
44
63
|
wait
|
|
45
64
|
);
|
|
46
65
|
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
@@ -51,22 +70,21 @@ function createPublisher(platform) {
|
|
|
51
70
|
}
|
|
52
71
|
return true;
|
|
53
72
|
};
|
|
54
|
-
const createReady =
|
|
55
|
-
|
|
56
|
-
const backoffMS = retry?.backoffMS ?? 2e3;
|
|
57
|
-
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
73
|
+
const createReady = (token, params, label, wait, retry) => withRetry(
|
|
74
|
+
async () => {
|
|
58
75
|
const id = await platform.createContainer(token, params);
|
|
59
|
-
if (id
|
|
60
|
-
|
|
61
|
-
|
|
76
|
+
if (!id || !await isReady(token, id, label, wait)) return null;
|
|
77
|
+
return id;
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
...retry,
|
|
81
|
+
onRetry: (attempt, attempts, delayMS) => {
|
|
62
82
|
console.warn(
|
|
63
|
-
`[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${
|
|
83
|
+
`[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${delayMS}ms`
|
|
64
84
|
);
|
|
65
|
-
await sleep(delay);
|
|
66
85
|
}
|
|
67
86
|
}
|
|
68
|
-
|
|
69
|
-
};
|
|
87
|
+
);
|
|
70
88
|
return {
|
|
71
89
|
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
72
90
|
publish: async (token, params, wait, retry) => {
|
|
@@ -1,35 +1,13 @@
|
|
|
1
1
|
import { GraphParams, ContainerState } from './types.mjs';
|
|
2
|
+
import { PollOptions, RetryOptions } from '../utils/poll.mjs';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
* Polling settings for a media container.
|
|
5
|
-
*
|
|
6
|
-
* @property intervalMS - Delay between status checks. Default: `5000`.
|
|
7
|
-
* @property timeoutMS - Give up after this long. Default: `300000` (Meta stops processing after ~5 min).
|
|
5
|
+
* Polling settings for a media container. The default timeout matches Meta,
|
|
6
|
+
* which stops processing after ~5 min.
|
|
8
7
|
*
|
|
9
8
|
* @category Socials
|
|
10
9
|
*/
|
|
11
|
-
type WaitForContainerOptions =
|
|
12
|
-
intervalMS?: number;
|
|
13
|
-
timeoutMS?: number;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Retry settings for creating a media container.
|
|
17
|
-
*
|
|
18
|
-
* Meta intermittently rejects a perfectly valid container — most visibly on
|
|
19
|
-
* carousels, where one arbitrary child fails with "Only photo or video can be
|
|
20
|
-
* accepted as media type." and the identical call succeeds moments later. The
|
|
21
|
-
* failure is transient and carries no distinguishing error code, so container
|
|
22
|
-
* creation is simply retried rather than pattern-matched against Meta's copy.
|
|
23
|
-
*
|
|
24
|
-
* @property attempts - Total tries, including the first. Default: `3`.
|
|
25
|
-
* @property backoffMS - Delay before the second try, doubled each time after. Default: `2000`.
|
|
26
|
-
*
|
|
27
|
-
* @category Socials
|
|
28
|
-
*/
|
|
29
|
-
type RetryOptions = {
|
|
30
|
-
attempts?: number;
|
|
31
|
-
backoffMS?: number;
|
|
32
|
-
};
|
|
10
|
+
type WaitForContainerOptions = PollOptions;
|
|
33
11
|
/**
|
|
34
12
|
* Wires up the create → wait → publish flow Meta platforms share. Instagram and
|
|
35
13
|
* Threads differ only in their endpoints, so each builds one publisher and every
|
|
@@ -62,4 +40,4 @@ declare function createPublisher(platform: {
|
|
|
62
40
|
createChildren: (token: string, items: GraphParams[], wait?: WaitForContainerOptions, retry?: RetryOptions) => Promise<string[] | null>;
|
|
63
41
|
};
|
|
64
42
|
|
|
65
|
-
export {
|
|
43
|
+
export { RetryOptions, type WaitForContainerOptions, createPublisher };
|
|
@@ -1,35 +1,13 @@
|
|
|
1
1
|
import { GraphParams, ContainerState } from './types.js';
|
|
2
|
+
import { PollOptions, RetryOptions } from '../utils/poll.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
* Polling settings for a media container.
|
|
5
|
-
*
|
|
6
|
-
* @property intervalMS - Delay between status checks. Default: `5000`.
|
|
7
|
-
* @property timeoutMS - Give up after this long. Default: `300000` (Meta stops processing after ~5 min).
|
|
5
|
+
* Polling settings for a media container. The default timeout matches Meta,
|
|
6
|
+
* which stops processing after ~5 min.
|
|
8
7
|
*
|
|
9
8
|
* @category Socials
|
|
10
9
|
*/
|
|
11
|
-
type WaitForContainerOptions =
|
|
12
|
-
intervalMS?: number;
|
|
13
|
-
timeoutMS?: number;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* Retry settings for creating a media container.
|
|
17
|
-
*
|
|
18
|
-
* Meta intermittently rejects a perfectly valid container — most visibly on
|
|
19
|
-
* carousels, where one arbitrary child fails with "Only photo or video can be
|
|
20
|
-
* accepted as media type." and the identical call succeeds moments later. The
|
|
21
|
-
* failure is transient and carries no distinguishing error code, so container
|
|
22
|
-
* creation is simply retried rather than pattern-matched against Meta's copy.
|
|
23
|
-
*
|
|
24
|
-
* @property attempts - Total tries, including the first. Default: `3`.
|
|
25
|
-
* @property backoffMS - Delay before the second try, doubled each time after. Default: `2000`.
|
|
26
|
-
*
|
|
27
|
-
* @category Socials
|
|
28
|
-
*/
|
|
29
|
-
type RetryOptions = {
|
|
30
|
-
attempts?: number;
|
|
31
|
-
backoffMS?: number;
|
|
32
|
-
};
|
|
10
|
+
type WaitForContainerOptions = PollOptions;
|
|
33
11
|
/**
|
|
34
12
|
* Wires up the create → wait → publish flow Meta platforms share. Instagram and
|
|
35
13
|
* Threads differ only in their endpoints, so each builds one publisher and every
|
|
@@ -62,4 +40,4 @@ declare function createPublisher(platform: {
|
|
|
62
40
|
createChildren: (token: string, items: GraphParams[], wait?: WaitForContainerOptions, retry?: RetryOptions) => Promise<string[] | null>;
|
|
63
41
|
};
|
|
64
42
|
|
|
65
|
-
export {
|
|
43
|
+
export { RetryOptions, type WaitForContainerOptions, createPublisher };
|
|
@@ -1,22 +1,39 @@
|
|
|
1
|
-
// src/socials/
|
|
1
|
+
// src/socials/utils/poll.ts
|
|
2
2
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3
|
-
async function
|
|
3
|
+
async function pollUntilReady(read, isPending, { intervalMS = 5e3, timeoutMS = 3e5 } = {}) {
|
|
4
4
|
const deadline = Date.now() + timeoutMS;
|
|
5
|
-
let state = await
|
|
5
|
+
let state = await read();
|
|
6
6
|
if (!state) {
|
|
7
7
|
await sleep(2e3);
|
|
8
8
|
return null;
|
|
9
9
|
}
|
|
10
|
-
while (state
|
|
10
|
+
while (state && isPending(state) && Date.now() < deadline) {
|
|
11
11
|
await sleep(intervalMS);
|
|
12
|
-
state = await
|
|
12
|
+
state = await read();
|
|
13
13
|
}
|
|
14
14
|
return state;
|
|
15
15
|
}
|
|
16
|
+
async function withRetry(run, options = {}) {
|
|
17
|
+
const attempts = Math.max(1, options.attempts ?? 3);
|
|
18
|
+
const backoffMS = options.backoffMS ?? 2e3;
|
|
19
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
20
|
+
const result = await run(attempt);
|
|
21
|
+
if (result) return result;
|
|
22
|
+
if (attempt < attempts) {
|
|
23
|
+
const delayMS = backoffMS * 2 ** (attempt - 1);
|
|
24
|
+
options.onRetry?.(attempt, attempts, delayMS);
|
|
25
|
+
await sleep(delayMS);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/socials/meta/container.ts
|
|
16
32
|
function createPublisher(platform) {
|
|
17
33
|
const isReady = async (token, id, label, wait) => {
|
|
18
|
-
const state = await
|
|
34
|
+
const state = await pollUntilReady(
|
|
19
35
|
() => platform.getStatus(token, id),
|
|
36
|
+
(current) => current.status === "IN_PROGRESS",
|
|
20
37
|
wait
|
|
21
38
|
);
|
|
22
39
|
if (state && state.status !== "FINISHED" && state.status !== "PUBLISHED") {
|
|
@@ -27,22 +44,21 @@ function createPublisher(platform) {
|
|
|
27
44
|
}
|
|
28
45
|
return true;
|
|
29
46
|
};
|
|
30
|
-
const createReady =
|
|
31
|
-
|
|
32
|
-
const backoffMS = retry?.backoffMS ?? 2e3;
|
|
33
|
-
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
47
|
+
const createReady = (token, params, label, wait, retry) => withRetry(
|
|
48
|
+
async () => {
|
|
34
49
|
const id = await platform.createContainer(token, params);
|
|
35
|
-
if (id
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
if (!id || !await isReady(token, id, label, wait)) return null;
|
|
51
|
+
return id;
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
...retry,
|
|
55
|
+
onRetry: (attempt, attempts, delayMS) => {
|
|
38
56
|
console.warn(
|
|
39
|
-
`[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${
|
|
57
|
+
`[naystack] ${platform.name} ${label} attempt ${attempt}/${attempts} failed \u2014 retrying in ${delayMS}ms`
|
|
40
58
|
);
|
|
41
|
-
await sleep(delay);
|
|
42
59
|
}
|
|
43
60
|
}
|
|
44
|
-
|
|
45
|
-
};
|
|
61
|
+
);
|
|
46
62
|
return {
|
|
47
63
|
/** Creates a container, waits for it to finish processing, then publishes it. */
|
|
48
64
|
publish: async (token, params, wait, retry) => {
|
|
@@ -0,0 +1,18 @@
|
|
|
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 __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// src/socials/provider.ts
|
|
17
|
+
var provider_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(provider_exports);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { SocialPlatform, SocialTokens, SocialProfile, SocialPost } from './types.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One platform's implementation of connect, read profile, read content.
|
|
5
|
+
*
|
|
6
|
+
* A plain object with no per-instance state — credentials come from the
|
|
7
|
+
* environment at call time, tokens are arguments — so each platform exports one
|
|
8
|
+
* ready-made constant. `InstagramProvider` is the reference implementation.
|
|
9
|
+
*
|
|
10
|
+
* Capability is method presence: a platform that can't refresh tokens omits
|
|
11
|
+
* `refresh`, one with no content-listing API omits `fetchMedia`, and callers
|
|
12
|
+
* narrow with `if (provider.fetchMedia)`. This describes the platform, not a
|
|
13
|
+
* token — a token missing a scope still fails at runtime with a `null` return.
|
|
14
|
+
*
|
|
15
|
+
* @property auth.authorizationURL - `state` comes back on the callback; `redirectURI` must match what the platform has registered.
|
|
16
|
+
* @property auth.exchangeCode - Trades the callback `code` for tokens. `platformUserId` is `null` on platforms whose token response carries no id (Google); the route then takes it from `fetchProfile`.
|
|
17
|
+
* @property auth.refresh - Omitted by platforms whose tokens don't expire or can't be refreshed.
|
|
18
|
+
* @property fetchMedia - Omitted by platforms with no content-listing API.
|
|
19
|
+
* @property profileURL - Public profile link for a handle. Platform knowledge, not a capability, so every adapter has one.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { InstagramProvider } from "naystack/socials";
|
|
24
|
+
*
|
|
25
|
+
* const profile = await InstagramProvider.fetchProfile(accessToken);
|
|
26
|
+
* const posts = await InstagramProvider.fetchMedia?.(accessToken, { limit: 12 });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @category Socials
|
|
30
|
+
*/
|
|
31
|
+
type SocialProvider = {
|
|
32
|
+
platform: SocialPlatform;
|
|
33
|
+
auth: {
|
|
34
|
+
authorizationURL: (input: {
|
|
35
|
+
state: string;
|
|
36
|
+
redirectURI: string;
|
|
37
|
+
scopes?: string[];
|
|
38
|
+
}) => string;
|
|
39
|
+
exchangeCode: (input: {
|
|
40
|
+
code: string;
|
|
41
|
+
redirectURI: string;
|
|
42
|
+
}) => Promise<(SocialTokens & {
|
|
43
|
+
platformUserId: string | null;
|
|
44
|
+
}) | null>;
|
|
45
|
+
refresh?: (tokens: SocialTokens) => Promise<SocialTokens | null>;
|
|
46
|
+
};
|
|
47
|
+
fetchProfile: (accessToken: string) => Promise<SocialProfile | null>;
|
|
48
|
+
profileURL: (username: string) => string;
|
|
49
|
+
fetchMedia?: (accessToken: string, options?: {
|
|
50
|
+
limit?: number;
|
|
51
|
+
}) => Promise<SocialPost[]>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type { SocialProvider };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { SocialPlatform, SocialTokens, SocialProfile, SocialPost } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One platform's implementation of connect, read profile, read content.
|
|
5
|
+
*
|
|
6
|
+
* A plain object with no per-instance state — credentials come from the
|
|
7
|
+
* environment at call time, tokens are arguments — so each platform exports one
|
|
8
|
+
* ready-made constant. `InstagramProvider` is the reference implementation.
|
|
9
|
+
*
|
|
10
|
+
* Capability is method presence: a platform that can't refresh tokens omits
|
|
11
|
+
* `refresh`, one with no content-listing API omits `fetchMedia`, and callers
|
|
12
|
+
* narrow with `if (provider.fetchMedia)`. This describes the platform, not a
|
|
13
|
+
* token — a token missing a scope still fails at runtime with a `null` return.
|
|
14
|
+
*
|
|
15
|
+
* @property auth.authorizationURL - `state` comes back on the callback; `redirectURI` must match what the platform has registered.
|
|
16
|
+
* @property auth.exchangeCode - Trades the callback `code` for tokens. `platformUserId` is `null` on platforms whose token response carries no id (Google); the route then takes it from `fetchProfile`.
|
|
17
|
+
* @property auth.refresh - Omitted by platforms whose tokens don't expire or can't be refreshed.
|
|
18
|
+
* @property fetchMedia - Omitted by platforms with no content-listing API.
|
|
19
|
+
* @property profileURL - Public profile link for a handle. Platform knowledge, not a capability, so every adapter has one.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { InstagramProvider } from "naystack/socials";
|
|
24
|
+
*
|
|
25
|
+
* const profile = await InstagramProvider.fetchProfile(accessToken);
|
|
26
|
+
* const posts = await InstagramProvider.fetchMedia?.(accessToken, { limit: 12 });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @category Socials
|
|
30
|
+
*/
|
|
31
|
+
type SocialProvider = {
|
|
32
|
+
platform: SocialPlatform;
|
|
33
|
+
auth: {
|
|
34
|
+
authorizationURL: (input: {
|
|
35
|
+
state: string;
|
|
36
|
+
redirectURI: string;
|
|
37
|
+
scopes?: string[];
|
|
38
|
+
}) => string;
|
|
39
|
+
exchangeCode: (input: {
|
|
40
|
+
code: string;
|
|
41
|
+
redirectURI: string;
|
|
42
|
+
}) => Promise<(SocialTokens & {
|
|
43
|
+
platformUserId: string | null;
|
|
44
|
+
}) | null>;
|
|
45
|
+
refresh?: (tokens: SocialTokens) => Promise<SocialTokens | null>;
|
|
46
|
+
};
|
|
47
|
+
fetchProfile: (accessToken: string) => Promise<SocialProfile | null>;
|
|
48
|
+
profileURL: (username: string) => string;
|
|
49
|
+
fetchMedia?: (accessToken: string, options?: {
|
|
50
|
+
limit?: number;
|
|
51
|
+
}) => Promise<SocialPost[]>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type { SocialProvider };
|
|
File without changes
|