@opencoredev/social-sdk 0.1.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/LICENSE +21 -0
- package/README.md +24 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +223 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloud/common.d.ts +26 -0
- package/dist/cloud/common.d.ts.map +1 -0
- package/dist/cloud/common.js +307 -0
- package/dist/cloud/common.js.map +1 -0
- package/dist/cloud/lifecycle.d.ts +11 -0
- package/dist/cloud/lifecycle.d.ts.map +1 -0
- package/dist/cloud/lifecycle.js +108 -0
- package/dist/cloud/lifecycle.js.map +1 -0
- package/dist/cloud/media.d.ts +24 -0
- package/dist/cloud/media.d.ts.map +1 -0
- package/dist/cloud/media.js +97 -0
- package/dist/cloud/media.js.map +1 -0
- package/dist/cloud/outcomes.d.ts +10 -0
- package/dist/cloud/outcomes.d.ts.map +1 -0
- package/dist/cloud/outcomes.js +188 -0
- package/dist/cloud/outcomes.js.map +1 -0
- package/dist/cloud/post-for-me.d.ts +70 -0
- package/dist/cloud/post-for-me.d.ts.map +1 -0
- package/dist/cloud/post-for-me.js +326 -0
- package/dist/cloud/post-for-me.js.map +1 -0
- package/dist/cloud/zernio.d.ts +112 -0
- package/dist/cloud/zernio.d.ts.map +1 -0
- package/dist/cloud/zernio.js +542 -0
- package/dist/cloud/zernio.js.map +1 -0
- package/dist/core/adapter.d.ts +118 -0
- package/dist/core/adapter.d.ts.map +1 -0
- package/dist/core/adapter.js +4 -0
- package/dist/core/adapter.js.map +1 -0
- package/dist/core/client.d.ts +108 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +893 -0
- package/dist/core/client.js.map +1 -0
- package/dist/core/concurrency.d.ts +10 -0
- package/dist/core/concurrency.d.ts.map +1 -0
- package/dist/core/concurrency.js +78 -0
- package/dist/core/concurrency.js.map +1 -0
- package/dist/core/errors.d.ts +45 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +44 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/idempotency.d.ts +34 -0
- package/dist/core/idempotency.d.ts.map +1 -0
- package/dist/core/idempotency.js +59 -0
- package/dist/core/idempotency.js.map +1 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +7 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/pagination.d.ts +12 -0
- package/dist/core/pagination.d.ts.map +1 -0
- package/dist/core/pagination.js +81 -0
- package/dist/core/pagination.js.map +1 -0
- package/dist/core/types.d.ts +329 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +7 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/platforms/bluesky.d.ts +132 -0
- package/dist/platforms/bluesky.d.ts.map +1 -0
- package/dist/platforms/bluesky.js +1049 -0
- package/dist/platforms/bluesky.js.map +1 -0
- package/dist/platforms/instagram.d.ts +63 -0
- package/dist/platforms/instagram.d.ts.map +1 -0
- package/dist/platforms/instagram.js +676 -0
- package/dist/platforms/instagram.js.map +1 -0
- package/dist/platforms/linkedin.d.ts +54 -0
- package/dist/platforms/linkedin.d.ts.map +1 -0
- package/dist/platforms/linkedin.js +580 -0
- package/dist/platforms/linkedin.js.map +1 -0
- package/dist/platforms/threads.d.ts +102 -0
- package/dist/platforms/threads.d.ts.map +1 -0
- package/dist/platforms/threads.js +571 -0
- package/dist/platforms/threads.js.map +1 -0
- package/dist/platforms/tiktok.d.ts +32 -0
- package/dist/platforms/tiktok.d.ts.map +1 -0
- package/dist/platforms/tiktok.js +511 -0
- package/dist/platforms/tiktok.js.map +1 -0
- package/dist/platforms/x-engagement.d.ts +17 -0
- package/dist/platforms/x-engagement.d.ts.map +1 -0
- package/dist/platforms/x-engagement.js +47 -0
- package/dist/platforms/x-engagement.js.map +1 -0
- package/dist/platforms/x.d.ts +82 -0
- package/dist/platforms/x.d.ts.map +1 -0
- package/dist/platforms/x.js +511 -0
- package/dist/platforms/x.js.map +1 -0
- package/dist/platforms/youtube-upload.d.ts +33 -0
- package/dist/platforms/youtube-upload.d.ts.map +1 -0
- package/dist/platforms/youtube-upload.js +281 -0
- package/dist/platforms/youtube-upload.js.map +1 -0
- package/dist/platforms/youtube.d.ts +55 -0
- package/dist/platforms/youtube.d.ts.map +1 -0
- package/dist/platforms/youtube.js +561 -0
- package/dist/platforms/youtube.js.map +1 -0
- package/dist/server/connections.d.ts +124 -0
- package/dist/server/connections.d.ts.map +1 -0
- package/dist/server/connections.js +334 -0
- package/dist/server/connections.js.map +1 -0
- package/dist/server/credentials.d.ts +52 -0
- package/dist/server/credentials.d.ts.map +1 -0
- package/dist/server/credentials.js +115 -0
- package/dist/server/credentials.js.map +1 -0
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +5 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/oauth.d.ts +48 -0
- package/dist/server/oauth.d.ts.map +1 -0
- package/dist/server/oauth.js +483 -0
- package/dist/server/oauth.js.map +1 -0
- package/dist/server/webhooks.d.ts +58 -0
- package/dist/server/webhooks.d.ts.map +1 -0
- package/dist/server/webhooks.js +198 -0
- package/dist/server/webhooks.js.map +1 -0
- package/dist/testing/index.d.ts +47 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +413 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +2 -0
- package/dist/testing.js.map +1 -0
- package/dist/transport/binary.d.ts +3 -0
- package/dist/transport/binary.d.ts.map +1 -0
- package/dist/transport/binary.js +30 -0
- package/dist/transport/binary.js.map +1 -0
- package/dist/transport/budget.d.ts +4 -0
- package/dist/transport/budget.d.ts.map +1 -0
- package/dist/transport/budget.js +27 -0
- package/dist/transport/budget.js.map +1 -0
- package/dist/transport/http.d.ts +44 -0
- package/dist/transport/http.d.ts.map +1 -0
- package/dist/transport/http.js +230 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/transport/json.d.ts +3 -0
- package/dist/transport/json.d.ts.map +1 -0
- package/dist/transport/json.js +11 -0
- package/dist/transport/json.js.map +1 -0
- package/dist/transport/upload.d.ts +25 -0
- package/dist/transport/upload.d.ts.map +1 -0
- package/dist/transport/upload.js +133 -0
- package/dist/transport/upload.js.map +1 -0
- package/dist/transport/validation.d.ts +6 -0
- package/dist/transport/validation.d.ts.map +1 -0
- package/dist/transport/validation.js +24 -0
- package/dist/transport/validation.js.map +1 -0
- package/package.json +96 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenCore
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Social SDK
|
|
2
|
+
|
|
3
|
+
TypeScript social platform integrations with direct and optional managed backends.
|
|
4
|
+
|
|
5
|
+
This local prerelease is ESM-only. Provider credentials stay on the server. Importing the root or constructing a client makes no network requests.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { createSocial, connectedAccountRef } from "@opencoredev/social-sdk";
|
|
9
|
+
import { mockBackend } from "@opencoredev/social-sdk/testing";
|
|
10
|
+
|
|
11
|
+
const social = createSocial({ backend: mockBackend() });
|
|
12
|
+
const account = connectedAccountRef({
|
|
13
|
+
backend: "default",
|
|
14
|
+
platform: "x",
|
|
15
|
+
accountId: "mock-account-1",
|
|
16
|
+
});
|
|
17
|
+
const result = await social.posts.publish({
|
|
18
|
+
targets: [{ account }],
|
|
19
|
+
content: { text: "Hello from the local mock." },
|
|
20
|
+
});
|
|
21
|
+
console.log(result.outcomes[0]?.state);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Run the workspace documentation for setup and capability limits. This prerelease has no live-verification guarantee. It is private and cannot be published until the owner authorizes release.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import type { SocialAdapter } from "./core/index.js";
|
|
3
|
+
declare const names: readonly ["mock", "zernio", "post-for-me", "bluesky", "x", "threads", "youtube", "tiktok", "instagram", "linkedin"];
|
|
4
|
+
type AdapterName = (typeof names)[number];
|
|
5
|
+
export declare function createDiagnosticAdapter(name: AdapterName, accountId?: string): SocialAdapter<unknown>;
|
|
6
|
+
export interface CliIO {
|
|
7
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
8
|
+
readonly readInput: (path: string) => Promise<string>;
|
|
9
|
+
readonly write: (text: string) => void;
|
|
10
|
+
}
|
|
11
|
+
/** All commands are offline and return 0 (valid), 1 (diagnostic failure), or 2 (usage/input error). */
|
|
12
|
+
export declare function runCli(args: readonly string[], io: CliIO): Promise<number>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAIA,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAYrE,QAAA,MAAM,KAAK,qHAWD,CAAC;AACX,KAAK,WAAW,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC;AAgB1C,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,WAAW,EACjB,SAAS,SAAuB,GAC/B,aAAa,CAAC,OAAO,CAAC,CAmDxB;AACD,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AACD,uGAAuG;AACvG,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CA6HhF"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile, stat } from "node:fs/promises";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { createSocial } from "./core/client.js";
|
|
5
|
+
import { mockBackend } from "./testing/index.js";
|
|
6
|
+
import { zernio } from "./cloud/zernio.js";
|
|
7
|
+
import { postForMe } from "./cloud/post-for-me.js";
|
|
8
|
+
import { bluesky } from "./platforms/bluesky.js";
|
|
9
|
+
import { x } from "./platforms/x.js";
|
|
10
|
+
import { threads } from "./platforms/threads.js";
|
|
11
|
+
import { youtube } from "./platforms/youtube.js";
|
|
12
|
+
import { tiktok } from "./platforms/tiktok.js";
|
|
13
|
+
import { instagram } from "./platforms/instagram.js";
|
|
14
|
+
import { linkedin } from "./platforms/linkedin.js";
|
|
15
|
+
const names = [
|
|
16
|
+
"mock",
|
|
17
|
+
"zernio",
|
|
18
|
+
"post-for-me",
|
|
19
|
+
"bluesky",
|
|
20
|
+
"x",
|
|
21
|
+
"threads",
|
|
22
|
+
"youtube",
|
|
23
|
+
"tiktok",
|
|
24
|
+
"instagram",
|
|
25
|
+
"linkedin",
|
|
26
|
+
];
|
|
27
|
+
const environmentNames = {
|
|
28
|
+
mock: [],
|
|
29
|
+
zernio: ["ZERNIO_API_KEY"],
|
|
30
|
+
"post-for-me": ["POST_FOR_ME_API_KEY"],
|
|
31
|
+
bluesky: ["BLUESKY_SERVICE", "BLUESKY_DID", "BLUESKY_ACCESS_JWT"],
|
|
32
|
+
x: ["X_USER_ID", "X_ACCESS_TOKEN"],
|
|
33
|
+
threads: ["THREADS_USER_ID", "THREADS_ACCESS_TOKEN"],
|
|
34
|
+
youtube: ["YOUTUBE_CHANNEL_ID", "YOUTUBE_ACCESS_TOKEN"],
|
|
35
|
+
tiktok: ["TIKTOK_OPEN_ID", "TIKTOK_ACCESS_TOKEN"],
|
|
36
|
+
instagram: ["INSTAGRAM_ACCOUNT_ID", "INSTAGRAM_ACCESS_TOKEN"],
|
|
37
|
+
linkedin: ["LINKEDIN_AUTHOR_URN", "LINKEDIN_ACCESS_TOKEN", "LINKEDIN_API_VERSION"],
|
|
38
|
+
};
|
|
39
|
+
const noNetwork = async () => {
|
|
40
|
+
throw new Error("Offline diagnostics cannot make network requests.");
|
|
41
|
+
};
|
|
42
|
+
export function createDiagnosticAdapter(name, accountId = "diagnostic-account") {
|
|
43
|
+
switch (name) {
|
|
44
|
+
case "mock":
|
|
45
|
+
return mockBackend();
|
|
46
|
+
case "zernio":
|
|
47
|
+
return zernio({ apiKey: "offline-placeholder", fetch: noNetwork });
|
|
48
|
+
case "post-for-me":
|
|
49
|
+
return postForMe({ apiKey: "offline-placeholder", fetch: noNetwork });
|
|
50
|
+
case "bluesky":
|
|
51
|
+
return bluesky({
|
|
52
|
+
auth: { service: "https://bsky.social", did: accountId, accessJwt: "offline-placeholder" },
|
|
53
|
+
fetch: noNetwork,
|
|
54
|
+
});
|
|
55
|
+
case "x":
|
|
56
|
+
return x({
|
|
57
|
+
auth: { userId: accountId, accessToken: "offline-placeholder" },
|
|
58
|
+
fetch: noNetwork,
|
|
59
|
+
});
|
|
60
|
+
case "threads":
|
|
61
|
+
return threads({
|
|
62
|
+
auth: { userId: accountId, accessToken: "offline-placeholder" },
|
|
63
|
+
fetch: noNetwork,
|
|
64
|
+
});
|
|
65
|
+
case "youtube":
|
|
66
|
+
return youtube({
|
|
67
|
+
auth: { channelId: accountId, accessToken: "offline-placeholder" },
|
|
68
|
+
fetch: noNetwork,
|
|
69
|
+
});
|
|
70
|
+
case "tiktok":
|
|
71
|
+
return tiktok({
|
|
72
|
+
auth: { openId: accountId, accessToken: "offline-placeholder" },
|
|
73
|
+
verifiedMediaOrigins: [],
|
|
74
|
+
fetch: noNetwork,
|
|
75
|
+
});
|
|
76
|
+
case "instagram":
|
|
77
|
+
return instagram({
|
|
78
|
+
auth: { accountId, accessToken: "offline-placeholder" },
|
|
79
|
+
fetch: noNetwork,
|
|
80
|
+
});
|
|
81
|
+
case "linkedin":
|
|
82
|
+
return linkedin({
|
|
83
|
+
auth: {
|
|
84
|
+
author: accountId.startsWith("urn:li:")
|
|
85
|
+
? accountId
|
|
86
|
+
: "urn:li:person:diagnostic",
|
|
87
|
+
accessToken: "offline-placeholder",
|
|
88
|
+
},
|
|
89
|
+
apiVersion: "202609",
|
|
90
|
+
fetch: noNetwork,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** All commands are offline and return 0 (valid), 1 (diagnostic failure), or 2 (usage/input error). */
|
|
95
|
+
export async function runCli(args, io) {
|
|
96
|
+
const command = args[0] ?? "help";
|
|
97
|
+
const json = args.includes("--json");
|
|
98
|
+
const finish = (code, data) => {
|
|
99
|
+
const result = { schemaVersion: 1, command, ok: code === 0, data };
|
|
100
|
+
io.write(json ? JSON.stringify(result) + "\n" : JSON.stringify(result, null, 2) + "\n");
|
|
101
|
+
return code;
|
|
102
|
+
};
|
|
103
|
+
const options = new Map();
|
|
104
|
+
for (let index = 1; index < args.length; index++) {
|
|
105
|
+
const arg = args[index];
|
|
106
|
+
if (arg === "--json")
|
|
107
|
+
continue;
|
|
108
|
+
if (!["--adapter", "--file"].includes(arg) ||
|
|
109
|
+
!args[index + 1] ||
|
|
110
|
+
args[index + 1].startsWith("--") ||
|
|
111
|
+
options.has(arg))
|
|
112
|
+
return finish(2, {
|
|
113
|
+
error: "Unknown, duplicate or missing option. Use help for supported arguments.",
|
|
114
|
+
});
|
|
115
|
+
options.set(arg, args[++index]);
|
|
116
|
+
}
|
|
117
|
+
if (command === "help" || command === "--help")
|
|
118
|
+
return finish(0, {
|
|
119
|
+
usage: "social-sdk <doctor|adapters|capabilities|validate|examples> [--adapter NAME] [--file PATH] [--json]",
|
|
120
|
+
offline: true,
|
|
121
|
+
exitCodes: { success: 0, diagnosticFailure: 1, invalidInput: 2 },
|
|
122
|
+
});
|
|
123
|
+
const selected = options.get("--adapter") ?? "mock";
|
|
124
|
+
if (!names.includes(selected))
|
|
125
|
+
return finish(2, { error: "Unknown adapter.", adapters: names });
|
|
126
|
+
const name = selected;
|
|
127
|
+
if (command === "adapters")
|
|
128
|
+
return finish(0, {
|
|
129
|
+
adapters: names,
|
|
130
|
+
verification: "Local contract tests; no live checks are performed by this CLI.",
|
|
131
|
+
});
|
|
132
|
+
if (command === "doctor") {
|
|
133
|
+
const missing = environmentNames[name].filter((key) => !io.env[key]?.trim());
|
|
134
|
+
return finish(missing.length ? 1 : 0, {
|
|
135
|
+
adapter: name,
|
|
136
|
+
offline: true,
|
|
137
|
+
missingEnvironmentVariables: missing,
|
|
138
|
+
authenticated: false,
|
|
139
|
+
nextStep: missing.length
|
|
140
|
+
? "Set the named variables on your server. No values are printed or sent."
|
|
141
|
+
: "Configuration presence checked only. Confirm platform scopes, account access and app approval separately.",
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (command === "capabilities")
|
|
145
|
+
return finish(0, {
|
|
146
|
+
manifest: createDiagnosticAdapter(name).capabilities,
|
|
147
|
+
verification: "Declared implementation capabilities; account permissions and live behavior were not checked.",
|
|
148
|
+
});
|
|
149
|
+
if (command === "examples")
|
|
150
|
+
return finish(0, {
|
|
151
|
+
adapter: "mock",
|
|
152
|
+
request: {
|
|
153
|
+
targets: [
|
|
154
|
+
{
|
|
155
|
+
account: {
|
|
156
|
+
kind: "connected-account",
|
|
157
|
+
version: 1,
|
|
158
|
+
backend: "default",
|
|
159
|
+
platform: "bluesky",
|
|
160
|
+
accountId: "demo",
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
content: { text: "Hello from Social SDK" },
|
|
165
|
+
},
|
|
166
|
+
run: "social-sdk validate --adapter mock --file request.json --json",
|
|
167
|
+
});
|
|
168
|
+
if (command !== "validate")
|
|
169
|
+
return finish(2, { error: "Unknown command. Use help for supported commands." });
|
|
170
|
+
const file = options.get("--file");
|
|
171
|
+
if (!file)
|
|
172
|
+
return finish(2, { error: "validate requires --file PATH containing a JSON publish request." });
|
|
173
|
+
try {
|
|
174
|
+
const raw = await io.readInput(file);
|
|
175
|
+
if (new TextEncoder().encode(raw).byteLength > 1024 * 1024)
|
|
176
|
+
return finish(2, { error: "Input exceeds the 1 MiB diagnostic limit." });
|
|
177
|
+
const request = JSON.parse(raw);
|
|
178
|
+
if (!request ||
|
|
179
|
+
typeof request !== "object" ||
|
|
180
|
+
!Array.isArray(request["targets"]))
|
|
181
|
+
return finish(2, { error: "Expected a JSON publish request with a targets array." });
|
|
182
|
+
const input = request;
|
|
183
|
+
// JSON diagnostics deliberately accept only portable URLs, never executable streams or Blob handles.
|
|
184
|
+
const contents = [input.content, ...input.targets.map((target) => target.content)];
|
|
185
|
+
if (contents.some((content) => content?.media?.some((media) => media.source?.kind !== "https-url")))
|
|
186
|
+
return finish(2, {
|
|
187
|
+
error: "CLI media validation accepts HTTPS URL inputs only. Validate Blob/stream/media handles through the SDK.",
|
|
188
|
+
});
|
|
189
|
+
const social = createSocial({
|
|
190
|
+
backend: createDiagnosticAdapter(name, input.targets[0]?.account?.accountId),
|
|
191
|
+
});
|
|
192
|
+
const prepared = social.posts.prepare(input);
|
|
193
|
+
// Do not echo content, URLs or account identifiers from the request.
|
|
194
|
+
return finish(prepared.ok ? 0 : 1, {
|
|
195
|
+
adapter: name,
|
|
196
|
+
localOnly: true,
|
|
197
|
+
targetCount: input.targets.length,
|
|
198
|
+
issues: prepared.issues.map(({ code, severity, targetIndex }) => ({
|
|
199
|
+
code,
|
|
200
|
+
severity,
|
|
201
|
+
targetIndex,
|
|
202
|
+
})),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return finish(2, {
|
|
207
|
+
error: "Unable to read or validate the JSON publish request. Check its shape and file permissions.",
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
212
|
+
process.exitCode = await runCli(process.argv.slice(2), {
|
|
213
|
+
env: process.env,
|
|
214
|
+
async readInput(path) {
|
|
215
|
+
const info = await stat(path);
|
|
216
|
+
if (!info.isFile() || info.size > 1024 * 1024)
|
|
217
|
+
throw new Error("Expected a JSON file up to 1 MiB.");
|
|
218
|
+
return readFile(path, "utf8");
|
|
219
|
+
},
|
|
220
|
+
write: (text) => process.stdout.write(text),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ManagedMediaStore } from "./media.js";
|
|
2
|
+
import type { AdapterOperationContext, CapabilityManifest, ConnectedAccountRef, JsonObject, MediaAttachment, Platform, PreparationIssue, PreparedPublishTarget } from "../core/types.js";
|
|
3
|
+
import { type HttpOptions } from "../transport/http.js";
|
|
4
|
+
export interface ManagedOptions extends HttpOptions {
|
|
5
|
+
readonly apiKey: string;
|
|
6
|
+
readonly mediaStore?: ManagedMediaStore;
|
|
7
|
+
readonly webhookSecret?: string;
|
|
8
|
+
readonly clock?: () => Date;
|
|
9
|
+
readonly uploadHostAllowed?: (hostname: string) => boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const selectedPlatforms: readonly ["x", "threads", "bluesky", "youtube", "tiktok", "instagram", "linkedin", "facebook"];
|
|
12
|
+
export declare function platform(value: unknown): Platform;
|
|
13
|
+
export declare function managedHttp(origin: string, options: ManagedOptions): (path: string, context: AdapterOperationContext, body?: JsonObject, query?: Record<string, string>, method?: "GET" | "POST" | "PUT" | "DELETE") => Promise<unknown>;
|
|
14
|
+
export declare function capabilityManifest(backend: string, apiRevision: string, operations: readonly string[]): CapabilityManifest;
|
|
15
|
+
export declare function optionsObject(target: PreparedPublishTarget): Record<string, unknown>;
|
|
16
|
+
/** Every accepted normalized option has an intentional provider mapping. */
|
|
17
|
+
export declare function managedOptionIssues(target: PreparedPublishTarget, provider: "zernio" | "post-for-me"): PreparationIssue[];
|
|
18
|
+
export declare function managedPreparation(target: PreparedPublishTarget): PreparationIssue[];
|
|
19
|
+
export declare function uploadManagedMedia(item: MediaAttachment, presign: (body: JsonObject) => Promise<unknown>, config: {
|
|
20
|
+
options: ManagedOptions;
|
|
21
|
+
provider: "zernio" | "post-for-me";
|
|
22
|
+
context: AdapterOperationContext;
|
|
23
|
+
}): Promise<string>;
|
|
24
|
+
export declare function accountMatches(ref: Pick<ConnectedAccountRef, "backend" | "platform" | "accountId">, context: AdapterOperationContext): void;
|
|
25
|
+
export declare function publicFields(value: unknown, fields: readonly string[]): JsonObject;
|
|
26
|
+
//# sourceMappingURL=common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/cloud/common.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,OAAO,KAAK,EACV,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EAEV,eAAe,EACf,QAAQ,EACR,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAyB,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAI/E,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;IACxC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5D;AAED,eAAO,MAAM,iBAAiB,gGASpB,CAAC;AACX,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CASjD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,IAS/D,MAAM,MAAM,EACZ,SAAS,uBAAuB,EAChC,OAAO,UAAU,EACjB,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EAClC,SAAQ,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAA8C,KAC9E,OAAO,CAAC,OAAO,CAAC,CA+DpB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,kBAAkB,CA8BpB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEpF;AAED,4EAA4E;AAC5E,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,qBAAqB,EAC7B,QAAQ,EAAE,QAAQ,GAAG,aAAa,GACjC,gBAAgB,EAAE,CAqFpB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,qBAAqB,GAAG,gBAAgB,EAAE,CAkEpF;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,EAC/C,MAAM,EAAE;IACN,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,QAAQ,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,uBAAuB,CAAC;CAClC,GACA,OAAO,CAAC,MAAM,CAAC,CAwCjB;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,IAAI,CAAC,mBAAmB,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC,EACpE,OAAO,EAAE,uBAAuB,GAC/B,IAAI,CAON;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAclF"}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { remainingBudget } from "../transport/budget.js";
|
|
2
|
+
import { SocialError } from "../core/errors.js";
|
|
3
|
+
import { createHttp, HttpError } from "../transport/http.js";
|
|
4
|
+
import { httpsUrl, upload } from "../transport/upload.js";
|
|
5
|
+
import { object, string } from "../transport/validation.js";
|
|
6
|
+
export const selectedPlatforms = [
|
|
7
|
+
"x",
|
|
8
|
+
"threads",
|
|
9
|
+
"bluesky",
|
|
10
|
+
"youtube",
|
|
11
|
+
"tiktok",
|
|
12
|
+
"instagram",
|
|
13
|
+
"linkedin",
|
|
14
|
+
"facebook",
|
|
15
|
+
];
|
|
16
|
+
export function platform(value) {
|
|
17
|
+
const slug = value === "twitter" ? "x" : value;
|
|
18
|
+
if (typeof slug !== "string" || !selectedPlatforms.some((item) => item === slug))
|
|
19
|
+
throw new SocialError({
|
|
20
|
+
code: "unsupported_capability",
|
|
21
|
+
operation: "accounts.read",
|
|
22
|
+
message: "The provider returned a platform outside this adapter's selected support.",
|
|
23
|
+
});
|
|
24
|
+
return slug;
|
|
25
|
+
}
|
|
26
|
+
export function managedHttp(origin, options) {
|
|
27
|
+
if (!options.apiKey.trim())
|
|
28
|
+
throw new SocialError({
|
|
29
|
+
code: "invalid_config",
|
|
30
|
+
operation: "createAdapter",
|
|
31
|
+
message: "Configure the selected provider's server-side API key.",
|
|
32
|
+
});
|
|
33
|
+
const http = createHttp(options);
|
|
34
|
+
return async (path, context, body, query = {}, method = body === undefined ? "GET" : "POST") => {
|
|
35
|
+
const url = new URL(origin + path);
|
|
36
|
+
for (const [key, value] of Object.entries(query))
|
|
37
|
+
url.searchParams.set(key, value);
|
|
38
|
+
const headers = new Headers({
|
|
39
|
+
Authorization: `Bearer ${options.apiKey}`,
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
});
|
|
42
|
+
if (context.targetIdempotencyKey && origin.includes("zernio.com"))
|
|
43
|
+
headers.set(path === "/v1/posts" ? "x-request-id" : "Idempotency-Key", context.targetIdempotencyKey);
|
|
44
|
+
try {
|
|
45
|
+
return await http({
|
|
46
|
+
url,
|
|
47
|
+
headers,
|
|
48
|
+
timeoutMs: remainingBudget(context),
|
|
49
|
+
method,
|
|
50
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
51
|
+
...(context.signal ? { signal: context.signal } : {}),
|
|
52
|
+
maxAttempts: method === "GET" ? Math.min(5, context.retryBudget.maxAttempts) : 1,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (!(error instanceof HttpError))
|
|
57
|
+
throw error;
|
|
58
|
+
const ambiguous = method !== "GET" &&
|
|
59
|
+
error.dispatched &&
|
|
60
|
+
(error.kind !== "http" || (error.status !== undefined && error.status >= 500));
|
|
61
|
+
throw new SocialError({
|
|
62
|
+
code: ambiguous
|
|
63
|
+
? "ambiguous_outcome"
|
|
64
|
+
: error.kind === "cancelled"
|
|
65
|
+
? "cancelled"
|
|
66
|
+
: error.kind === "timeout"
|
|
67
|
+
? "timeout"
|
|
68
|
+
: error.status === 401
|
|
69
|
+
? "reconnect_required"
|
|
70
|
+
: error.status === 403
|
|
71
|
+
? "missing_permission"
|
|
72
|
+
: error.status === 402
|
|
73
|
+
? "billing_required"
|
|
74
|
+
: error.status === 404
|
|
75
|
+
? "not_found"
|
|
76
|
+
: error.status === 410
|
|
77
|
+
? "gone"
|
|
78
|
+
: error.status === 429
|
|
79
|
+
? "rate_limited"
|
|
80
|
+
: "upstream_failure",
|
|
81
|
+
operation: path,
|
|
82
|
+
backend: context.backendInstance,
|
|
83
|
+
correlationId: context.correlationId,
|
|
84
|
+
message: error.message,
|
|
85
|
+
...(error.status === undefined ? {} : { upstreamStatus: error.status }),
|
|
86
|
+
retryDisposition: ambiguous
|
|
87
|
+
? { kind: "reconcile-first" }
|
|
88
|
+
: error.status === 401
|
|
89
|
+
? { kind: "after-reconnect" }
|
|
90
|
+
: error.status === 429 && error.retryAfterMs !== undefined
|
|
91
|
+
? { kind: "after-delay", delayMs: error.retryAfterMs }
|
|
92
|
+
: { kind: "never" },
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export function capabilityManifest(backend, apiRevision, operations) {
|
|
98
|
+
return {
|
|
99
|
+
schemaVersion: 1,
|
|
100
|
+
backend,
|
|
101
|
+
apiRevision,
|
|
102
|
+
runtime: ["node22", "node24", "bun"],
|
|
103
|
+
capabilities: selectedPlatforms.flatMap((platform) => operations.map((operation) => ({
|
|
104
|
+
platform,
|
|
105
|
+
operation,
|
|
106
|
+
availability: "available",
|
|
107
|
+
...(operation === "posts.publish"
|
|
108
|
+
? {
|
|
109
|
+
formats: (platform === "youtube"
|
|
110
|
+
? ["video"]
|
|
111
|
+
: platform === "instagram" || platform === "tiktok"
|
|
112
|
+
? ["image", "video", "carousel"]
|
|
113
|
+
: ["text", "image", "video", "carousel"]),
|
|
114
|
+
}
|
|
115
|
+
: {}),
|
|
116
|
+
notes: "Contract implementation; live account verification and provider/platform eligibility are separate.",
|
|
117
|
+
}))),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export function optionsObject(target) {
|
|
121
|
+
return target.options === undefined ? {} : object(target.options);
|
|
122
|
+
}
|
|
123
|
+
/** Every accepted normalized option has an intentional provider mapping. */
|
|
124
|
+
export function managedOptionIssues(target, provider) {
|
|
125
|
+
const config = optionsObject(target);
|
|
126
|
+
const keys = {
|
|
127
|
+
youtube: ["title", "visibility", "madeForKids"],
|
|
128
|
+
instagram: ["shareToFeed"],
|
|
129
|
+
x: ["replySettings"],
|
|
130
|
+
tiktok: [
|
|
131
|
+
"privacy",
|
|
132
|
+
"consentGiven",
|
|
133
|
+
"disableComments",
|
|
134
|
+
"disableDuet",
|
|
135
|
+
"disableStitch",
|
|
136
|
+
"brandedContent",
|
|
137
|
+
"ownBrand",
|
|
138
|
+
"aiGenerated",
|
|
139
|
+
"draft",
|
|
140
|
+
...(provider === "zernio" ? ["photoCoverIndex"] : []),
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
const issues = [];
|
|
144
|
+
const fail = (code, message) => issues.push({ code, message, severity: "error", targetIndex: target.targetIndex });
|
|
145
|
+
if (Object.keys(config).some((key) => !keys[target.account.platform]?.includes(key)))
|
|
146
|
+
fail("options.unmapped", "A supplied platform option has no verified mapping in this managed adapter.");
|
|
147
|
+
for (const key of [
|
|
148
|
+
"shareToFeed",
|
|
149
|
+
"madeForKids",
|
|
150
|
+
"consentGiven",
|
|
151
|
+
"disableComments",
|
|
152
|
+
"disableDuet",
|
|
153
|
+
"disableStitch",
|
|
154
|
+
"brandedContent",
|
|
155
|
+
"ownBrand",
|
|
156
|
+
"aiGenerated",
|
|
157
|
+
"draft",
|
|
158
|
+
]) {
|
|
159
|
+
if (config[key] !== undefined && typeof config[key] !== "boolean")
|
|
160
|
+
fail("options.boolean", "Consent, audience, interaction and disclosure choices must be booleans.");
|
|
161
|
+
}
|
|
162
|
+
if (config["replySettings"] !== undefined &&
|
|
163
|
+
!["everyone", "following", "mentionedUsers"].includes(String(config["replySettings"])))
|
|
164
|
+
fail("x.reply_settings", "Select a supported reply setting explicitly.");
|
|
165
|
+
if (target.account.platform === "tiktok") {
|
|
166
|
+
for (const key of [
|
|
167
|
+
"disableComments",
|
|
168
|
+
"disableDuet",
|
|
169
|
+
"disableStitch",
|
|
170
|
+
"brandedContent",
|
|
171
|
+
"ownBrand",
|
|
172
|
+
"aiGenerated",
|
|
173
|
+
"draft",
|
|
174
|
+
])
|
|
175
|
+
if (typeof config[key] !== "boolean")
|
|
176
|
+
fail("tiktok.explicit_choice", "Select every interaction, disclosure, AI-content and draft/direct-post choice before submission.");
|
|
177
|
+
if (config["brandedContent"] === true && config["privacy"] === "SELF_ONLY")
|
|
178
|
+
fail("tiktok.branded_privacy", "TikTok branded content cannot use private visibility.");
|
|
179
|
+
const media = target.content.media ?? [];
|
|
180
|
+
if (media.some((item) => item.kind === "video") &&
|
|
181
|
+
(media.length !== 1 || media[0]?.kind !== "video"))
|
|
182
|
+
fail("tiktok.media_mix", "Select one video or an image-only photo post.");
|
|
183
|
+
if (media.length > 35)
|
|
184
|
+
fail("tiktok.media_count", "TikTok photo posts support at most 35 images.");
|
|
185
|
+
if (config["photoCoverIndex"] !== undefined &&
|
|
186
|
+
(!Number.isInteger(config["photoCoverIndex"]) ||
|
|
187
|
+
Number(config["photoCoverIndex"]) < 0 ||
|
|
188
|
+
Number(config["photoCoverIndex"]) >= media.length ||
|
|
189
|
+
media.some((item) => item.kind !== "image")))
|
|
190
|
+
fail("tiktok.cover", "Choose an existing photo index for the cover.");
|
|
191
|
+
}
|
|
192
|
+
return issues;
|
|
193
|
+
}
|
|
194
|
+
export function managedPreparation(target) {
|
|
195
|
+
const issues = [];
|
|
196
|
+
const fail = (code, message) => issues.push({ code, message, severity: "error", targetIndex: target.targetIndex });
|
|
197
|
+
if (!selectedPlatforms.some((value) => value === target.account.platform))
|
|
198
|
+
fail("platform.unsupported", "This platform is outside the selected adapter coverage.");
|
|
199
|
+
if (target.replyTo)
|
|
200
|
+
fail("reply.use_comments", "Use the supported comment reply operation rather than publishing a reply through managed post creation.");
|
|
201
|
+
const media = target.content.media ?? [];
|
|
202
|
+
if (!target.content.text && media.length === 0)
|
|
203
|
+
fail("content.empty", "Text or media is required.");
|
|
204
|
+
if (target.content.link)
|
|
205
|
+
fail("link.explicit_text", "Include the link in text; this adapter does not silently discard structured link metadata.");
|
|
206
|
+
for (const item of media) {
|
|
207
|
+
if (!item.mimeType || !item.mimeType.startsWith(`${item.kind}/`))
|
|
208
|
+
fail("media.mime", "Provide the actual image/video MIME type matching the attachment kind.");
|
|
209
|
+
if (item.source.kind === "https-url") {
|
|
210
|
+
try {
|
|
211
|
+
httpsUrl(item.source.url);
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
fail("media.url", "Provide a public HTTPS media URL without local hosts, credentials, or a custom port.");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else if (item.source.kind === "media-ref") {
|
|
218
|
+
const ref = item.source.ref;
|
|
219
|
+
if (ref.backend !== target.account.backend ||
|
|
220
|
+
ref.accountId !== target.account.accountId ||
|
|
221
|
+
ref.platform !== target.account.platform)
|
|
222
|
+
fail("media.handle_scope", "Media reference belongs to another account or backend.");
|
|
223
|
+
}
|
|
224
|
+
else if (!item.filename)
|
|
225
|
+
fail("media.filename", "Uploadable bytes require a filename.");
|
|
226
|
+
}
|
|
227
|
+
if (target.account.platform === "youtube") {
|
|
228
|
+
const options = optionsObject(target);
|
|
229
|
+
if (media.length !== 1 || media[0]?.kind !== "video")
|
|
230
|
+
fail("youtube.video", "YouTube requires exactly one video.");
|
|
231
|
+
if (typeof options["title"] !== "string" || !options["title"])
|
|
232
|
+
fail("youtube.title", "Select a YouTube title explicitly.");
|
|
233
|
+
if (!["public", "unlisted", "private"].includes(String(options["visibility"])))
|
|
234
|
+
fail("youtube.visibility", "Select public, unlisted, or private visibility explicitly.");
|
|
235
|
+
if (typeof options["madeForKids"] !== "boolean")
|
|
236
|
+
fail("youtube.audience", "Declare whether the video is made for kids.");
|
|
237
|
+
}
|
|
238
|
+
if (["instagram", "tiktok"].includes(target.account.platform) && media.length === 0)
|
|
239
|
+
fail("media.required", "This destination requires image or video media.");
|
|
240
|
+
if (target.account.platform === "tiktok") {
|
|
241
|
+
const options = optionsObject(target);
|
|
242
|
+
if (options["consentGiven"] !== true)
|
|
243
|
+
fail("tiktok.consent", "Obtain creator consent after preview and before uploading.");
|
|
244
|
+
if (!["SELF_ONLY", "MUTUAL_FOLLOW_FRIENDS", "FOLLOWER_OF_CREATOR", "PUBLIC_TO_EVERYONE"].includes(String(options["privacy"])))
|
|
245
|
+
fail("tiktok.privacy", "Select a privacy level offered by the current creator information.");
|
|
246
|
+
}
|
|
247
|
+
return issues;
|
|
248
|
+
}
|
|
249
|
+
export async function uploadManagedMedia(item, presign, config) {
|
|
250
|
+
if (item.source.kind === "https-url")
|
|
251
|
+
return httpsUrl(item.source.url).href;
|
|
252
|
+
if (item.source.kind === "media-ref")
|
|
253
|
+
throw new SocialError({
|
|
254
|
+
code: "media_error",
|
|
255
|
+
operation: "media.upload",
|
|
256
|
+
message: "Unvalidated media references cannot cross account/backend boundaries.",
|
|
257
|
+
});
|
|
258
|
+
const mimeType = string(item.mimeType);
|
|
259
|
+
const filename = string(item.filename);
|
|
260
|
+
const source = item.source;
|
|
261
|
+
const size = item.byteSize ?? (source.kind === "blob" ? source.blob.size : undefined);
|
|
262
|
+
const data = object(await presign(config.provider === "zernio"
|
|
263
|
+
? { filename, contentType: mimeType, ...(size === undefined ? {} : { size }) }
|
|
264
|
+
: {}));
|
|
265
|
+
const uploadUrl = string(data[config.provider === "zernio" ? "uploadUrl" : "upload_url"]);
|
|
266
|
+
const publicUrl = string(data[config.provider === "zernio" ? "publicUrl" : "media_url"]);
|
|
267
|
+
const allowHost = config.options.uploadHostAllowed ??
|
|
268
|
+
(config.provider === "zernio"
|
|
269
|
+
? (host) => host.endsWith(".r2.cloudflarestorage.com")
|
|
270
|
+
: (host) => host.endsWith(".supabase.co"));
|
|
271
|
+
await upload({
|
|
272
|
+
url: uploadUrl,
|
|
273
|
+
source: {
|
|
274
|
+
mimeType,
|
|
275
|
+
...(size === undefined ? {} : { size }),
|
|
276
|
+
open: source.kind === "blob" ? () => source.blob.stream() : source.open,
|
|
277
|
+
},
|
|
278
|
+
allowHost,
|
|
279
|
+
maxBytes: 5 * 1024 * 1024 * 1024,
|
|
280
|
+
timeoutMs: remainingBudget(config.context),
|
|
281
|
+
...(config.options.fetch ? { fetch: config.options.fetch } : {}),
|
|
282
|
+
...(config.context.signal ? { signal: config.context.signal } : {}),
|
|
283
|
+
});
|
|
284
|
+
return httpsUrl(publicUrl).href;
|
|
285
|
+
}
|
|
286
|
+
export function accountMatches(ref, context) {
|
|
287
|
+
if (ref.backend !== context.backendInstance)
|
|
288
|
+
throw new SocialError({
|
|
289
|
+
code: "unauthorized",
|
|
290
|
+
operation: "accounts.read",
|
|
291
|
+
message: "Account reference belongs to another backend instance.",
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
export function publicFields(value, fields) {
|
|
295
|
+
const data = object(value);
|
|
296
|
+
const result = {};
|
|
297
|
+
for (const field of fields) {
|
|
298
|
+
const value = data[field];
|
|
299
|
+
if (typeof value === "string" ||
|
|
300
|
+
typeof value === "boolean" ||
|
|
301
|
+
typeof value === "number" ||
|
|
302
|
+
value === null)
|
|
303
|
+
result[field] = value;
|
|
304
|
+
}
|
|
305
|
+
return result;
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=common.js.map
|