@opencoredev/social-sdk 0.3.0 → 0.4.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/dist/cli-request.d.ts +15 -0
- package/dist/cli-request.js +193 -0
- package/dist/cli.d.ts +4 -3
- package/dist/cli.js +19 -21
- package/dist/cloud/common.d.ts +7 -6
- package/dist/cloud/common.js +35 -54
- package/dist/cloud/lifecycle.js +31 -35
- package/dist/cloud/media.d.ts +2 -2
- package/dist/cloud/media.js +13 -3
- package/dist/cloud/outcomes.d.ts +4 -3
- package/dist/cloud/outcomes.js +8 -15
- package/dist/cloud/post-for-me.js +41 -49
- package/dist/cloud/zernio.js +58 -98
- package/dist/core/client.js +79 -99
- package/dist/core/fields.d.ts +14 -0
- package/dist/core/fields.js +14 -0
- package/dist/core/idempotency.d.ts +7 -2
- package/dist/core/idempotency.js +37 -20
- package/dist/core/pagination.js +8 -7
- package/dist/core/types.d.ts +3 -2
- package/dist/platforms/bluesky.d.ts +65 -1
- package/dist/platforms/bluesky.js +675 -276
- package/dist/platforms/instagram.d.ts +2 -0
- package/dist/platforms/instagram.js +130 -105
- package/dist/platforms/linkedin.d.ts +58 -1
- package/dist/platforms/linkedin.js +877 -107
- package/dist/platforms/threads.d.ts +13 -1
- package/dist/platforms/threads.js +204 -302
- package/dist/platforms/tiktok.d.ts +4 -0
- package/dist/platforms/tiktok.js +140 -124
- package/dist/platforms/webhook-adapter.d.ts +9 -0
- package/dist/platforms/webhook-adapter.js +24 -0
- package/dist/platforms/x-engagement.js +7 -12
- package/dist/platforms/x-stream.d.ts +83 -0
- package/dist/platforms/x-stream.js +350 -0
- package/dist/platforms/x.d.ts +72 -0
- package/dist/platforms/x.js +328 -119
- package/dist/platforms/youtube-upload.d.ts +1 -1
- package/dist/platforms/youtube-upload.js +6 -2
- package/dist/platforms/youtube.d.ts +28 -4
- package/dist/platforms/youtube.js +291 -133
- package/dist/server/bluesky-oauth.d.ts +177 -0
- package/dist/server/bluesky-oauth.js +1229 -0
- package/dist/server/connections.d.ts +14 -0
- package/dist/server/connections.js +10 -2
- package/dist/server/egress.d.ts +14 -0
- package/dist/server/egress.js +115 -0
- package/dist/server/oauth-internal.d.ts +6 -0
- package/dist/server/oauth-internal.js +66 -0
- package/dist/server/oauth.d.ts +1 -1
- package/dist/server/oauth.js +46 -99
- package/dist/server/webhooks.d.ts +136 -3
- package/dist/server/webhooks.js +639 -25
- package/dist/testing/index.js +14 -28
- package/dist/transport/http.d.ts +1 -1
- package/dist/transport/http.js +0 -1
- package/dist/transport/json.d.ts +7 -0
- package/dist/transport/json.js +32 -4
- package/dist/transport/upload.d.ts +1 -1
- package/dist/transport/upload.js +46 -38
- package/dist/transport/validation.d.ts +16 -5
- package/dist/transport/validation.js +29 -7
- package/package.json +2 -2
|
@@ -22,8 +22,13 @@ export interface IdempotencyStore {
|
|
|
22
22
|
readonly outcome: DeliveryOutcome;
|
|
23
23
|
}): Promise<void>;
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Serializes any caller value to canonical JSON. The input is parsed at runtime:
|
|
27
|
+
* non-finite numbers, bigints, symbols, functions, `Blob`s, and cycles throw a TypeError.
|
|
28
|
+
*/
|
|
29
|
+
export declare function stableSerialize<Payload>(value: Payload): string;
|
|
30
|
+
/** SHA-256 hex digest of {@link stableSerialize}. */
|
|
31
|
+
export declare function fingerprint<Payload>(value: Payload): Promise<string>;
|
|
27
32
|
export declare function deriveTargetIdempotencyKey(input: {
|
|
28
33
|
readonly logicalKey: string;
|
|
29
34
|
readonly scope?: string;
|
package/dist/core/idempotency.js
CHANGED
|
@@ -1,43 +1,59 @@
|
|
|
1
|
+
function isJsonScalar(value) {
|
|
2
|
+
return value === null || typeof value === "string" || typeof value === "boolean";
|
|
3
|
+
}
|
|
4
|
+
function isNumber(value) {
|
|
5
|
+
return typeof value === "number";
|
|
6
|
+
}
|
|
7
|
+
/** Arrays and plain or class objects; excludes bigints, symbols, and functions. */
|
|
8
|
+
function isWalkableObject(value) {
|
|
9
|
+
return typeof value === "object" && value !== null;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Parses an arbitrary caller value into canonical JSON: object keys are sorted,
|
|
13
|
+
* undefined properties are dropped, and values JSON cannot represent throw a TypeError.
|
|
14
|
+
*/
|
|
1
15
|
function normalizeForJson(value, seen) {
|
|
2
|
-
if (value
|
|
16
|
+
if (isJsonScalar(value))
|
|
3
17
|
return value;
|
|
4
|
-
if (
|
|
18
|
+
if (isNumber(value)) {
|
|
5
19
|
if (!Number.isFinite(value))
|
|
6
20
|
throw new TypeError("Idempotency payload numbers must be finite");
|
|
7
21
|
return value;
|
|
8
22
|
}
|
|
9
|
-
if (
|
|
23
|
+
if (value === undefined)
|
|
10
24
|
return undefined;
|
|
11
|
-
if (
|
|
25
|
+
if (!isWalkableObject(value))
|
|
12
26
|
throw new TypeError("Idempotency payload must be JSON-safe");
|
|
13
|
-
}
|
|
14
27
|
if (Array.isArray(value)) {
|
|
15
28
|
if (seen.has(value))
|
|
16
29
|
throw new TypeError("Idempotency payload must not contain cycles");
|
|
17
30
|
seen.add(value);
|
|
18
|
-
|
|
31
|
+
// JSON.stringify writes an undefined array entry as null, so mapping it here keeps the output identical.
|
|
32
|
+
const result = value.map((entry) => normalizeForJson(entry, seen) ?? null);
|
|
19
33
|
seen.delete(value);
|
|
20
34
|
return result;
|
|
21
35
|
}
|
|
22
36
|
if (typeof Blob !== "undefined" && value instanceof Blob) {
|
|
23
37
|
throw new TypeError("Blob inputs require a caller-provided media fingerprint");
|
|
24
38
|
}
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
seen.delete(value);
|
|
37
|
-
return result;
|
|
39
|
+
if (seen.has(value))
|
|
40
|
+
throw new TypeError("Idempotency payload must not contain cycles");
|
|
41
|
+
seen.add(value);
|
|
42
|
+
const entries = Object.entries(value);
|
|
43
|
+
entries.sort(([left], [right]) => left.localeCompare(right));
|
|
44
|
+
const result = {};
|
|
45
|
+
for (const [key, entry] of entries) {
|
|
46
|
+
const normalized = normalizeForJson(entry, seen);
|
|
47
|
+
if (normalized !== undefined)
|
|
48
|
+
result[key] = normalized;
|
|
38
49
|
}
|
|
39
|
-
|
|
50
|
+
seen.delete(value);
|
|
51
|
+
return result;
|
|
40
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Serializes any caller value to canonical JSON. The input is parsed at runtime:
|
|
55
|
+
* non-finite numbers, bigints, symbols, functions, `Blob`s, and cycles throw a TypeError.
|
|
56
|
+
*/
|
|
41
57
|
export function stableSerialize(value) {
|
|
42
58
|
return JSON.stringify(normalizeForJson(value, new Set()));
|
|
43
59
|
}
|
|
@@ -47,6 +63,7 @@ function bytesToHex(bytes) {
|
|
|
47
63
|
result += byte.toString(16).padStart(2, "0");
|
|
48
64
|
return result;
|
|
49
65
|
}
|
|
66
|
+
/** SHA-256 hex digest of {@link stableSerialize}. */
|
|
50
67
|
export async function fingerprint(value) {
|
|
51
68
|
const bytes = new TextEncoder().encode(stableSerialize(value));
|
|
52
69
|
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
package/dist/core/pagination.js
CHANGED
|
@@ -56,18 +56,19 @@ export function encodeCursor(scope, value) {
|
|
|
56
56
|
});
|
|
57
57
|
return `social-v1.${encodeURIComponent(JSON.stringify([scope, value]))}`;
|
|
58
58
|
}
|
|
59
|
+
/** A decoded cursor body: `[scope, upstreamCursor]`. */
|
|
60
|
+
function isCursorPayload(value) {
|
|
61
|
+
return (Array.isArray(value) &&
|
|
62
|
+
value.length === 2 &&
|
|
63
|
+
typeof value[0] === "string" &&
|
|
64
|
+
typeof value[1] === "string");
|
|
65
|
+
}
|
|
59
66
|
export function decodeCursor(scope, cursor) {
|
|
60
67
|
try {
|
|
61
68
|
if (!cursor.startsWith("social-v1.") || cursor.length > 100_000)
|
|
62
69
|
throw new Error();
|
|
63
70
|
const parsed = JSON.parse(decodeURIComponent(cursor.slice(10)));
|
|
64
|
-
if (!
|
|
65
|
-
parsed.length !== 2 ||
|
|
66
|
-
parsed[0] !== scope ||
|
|
67
|
-
// oxlint-disable-next-line anti-slop/no-runtime-typeof -- Validate the untrusted decoded cursor tuple.
|
|
68
|
-
typeof parsed[1] !== "string" ||
|
|
69
|
-
!parsed[1] ||
|
|
70
|
-
parsed[1].length > 16_384)
|
|
71
|
+
if (!isCursorPayload(parsed) || parsed[0] !== scope || !parsed[1] || parsed[1].length > 16_384)
|
|
71
72
|
throw new Error();
|
|
72
73
|
return parsed[1];
|
|
73
74
|
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -94,7 +94,8 @@ export type MediaInput = {
|
|
|
94
94
|
readonly fingerprint: string;
|
|
95
95
|
};
|
|
96
96
|
export interface MediaAttachment {
|
|
97
|
-
|
|
97
|
+
/** `document` covers paged files such as PDF, PPTX or DOCX where a platform accepts them. */
|
|
98
|
+
readonly kind: "image" | "video" | "document";
|
|
98
99
|
readonly source: MediaInput;
|
|
99
100
|
readonly mimeType?: string;
|
|
100
101
|
readonly filename?: string;
|
|
@@ -327,7 +328,7 @@ export interface CapabilityDeclaration {
|
|
|
327
328
|
readonly operation: string;
|
|
328
329
|
readonly platform: Platform | "*";
|
|
329
330
|
readonly availability: CapabilityAvailability;
|
|
330
|
-
readonly formats?: readonly ("text" | "image" | "video" | "carousel" | "sequence")[];
|
|
331
|
+
readonly formats?: readonly ("text" | "image" | "video" | "carousel" | "sequence" | "document")[];
|
|
331
332
|
readonly requiredScopes?: readonly string[];
|
|
332
333
|
readonly notes?: string;
|
|
333
334
|
}
|
|
@@ -17,6 +17,36 @@ export interface BlueskyOptions {
|
|
|
17
17
|
readonly did: string;
|
|
18
18
|
readonly fetchHandler: (pathname: string, init?: RequestInit) => Promise<Response>;
|
|
19
19
|
};
|
|
20
|
+
/** HTTPS origin of the Bluesky video service. Defaults to https://video.bsky.app. */
|
|
21
|
+
readonly videoService?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Service DID of the account's PDS (for example `did:web:pds.example.com`). It is the
|
|
24
|
+
* audience of the service token that lets the video service store the processed blob.
|
|
25
|
+
* When omitted, `uploadVideo` reads the `#atproto_pds` endpoint from the DID document
|
|
26
|
+
* returned by `com.atproto.server.getSession`.
|
|
27
|
+
*/
|
|
28
|
+
readonly pdsDid?: string;
|
|
29
|
+
}
|
|
30
|
+
/** A Bluesky video processing job (`app.bsky.video.defs#jobStatus`). */
|
|
31
|
+
export interface BlueskyVideoJob {
|
|
32
|
+
readonly jobId: string;
|
|
33
|
+
readonly did: string;
|
|
34
|
+
/** `JOB_STATE_COMPLETED`, `JOB_STATE_FAILED`, or an in-progress state. */
|
|
35
|
+
readonly state: string;
|
|
36
|
+
readonly progress?: number;
|
|
37
|
+
/** The processed blob. Present once the video is stored on the PDS. */
|
|
38
|
+
readonly blob?: JsonObject;
|
|
39
|
+
readonly failureCode?: string;
|
|
40
|
+
readonly error?: string;
|
|
41
|
+
readonly message?: string;
|
|
42
|
+
}
|
|
43
|
+
/** Output of `app.bsky.video.getUploadLimits` for the configured account. */
|
|
44
|
+
export interface BlueskyVideoUploadLimits {
|
|
45
|
+
readonly canUpload: boolean;
|
|
46
|
+
readonly remainingDailyVideos?: number;
|
|
47
|
+
readonly remainingDailyBytes?: number;
|
|
48
|
+
readonly message?: string;
|
|
49
|
+
readonly error?: string;
|
|
20
50
|
}
|
|
21
51
|
export interface BlueskyPostRef {
|
|
22
52
|
readonly uri: string;
|
|
@@ -84,12 +114,29 @@ export interface BlueskyNative {
|
|
|
84
114
|
readonly account: ConnectedAccountRef;
|
|
85
115
|
readonly context?: AdapterOperationContext;
|
|
86
116
|
}) => Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Sends one MP4 to the video service with a single upload request and returns the
|
|
119
|
+
* processing job. It does not wait for processing; poll `getVideoJobStatus` explicitly.
|
|
120
|
+
*/
|
|
87
121
|
readonly uploadVideo: (input: {
|
|
88
122
|
readonly account: ConnectedAccountRef;
|
|
89
123
|
readonly video: Blob;
|
|
90
124
|
readonly mimeType?: string;
|
|
125
|
+
/** File name reported to the video service. Defaults to `video.mp4`. */
|
|
126
|
+
readonly name?: string;
|
|
91
127
|
readonly context?: AdapterOperationContext;
|
|
92
|
-
}) => Promise<
|
|
128
|
+
}) => Promise<BlueskyVideoJob>;
|
|
129
|
+
/** Reads a video processing job once. The caller decides when to check again. */
|
|
130
|
+
readonly getVideoJobStatus: (input: {
|
|
131
|
+
readonly account: ConnectedAccountRef;
|
|
132
|
+
readonly jobId: string;
|
|
133
|
+
readonly context?: AdapterOperationContext;
|
|
134
|
+
}) => Promise<BlueskyVideoJob>;
|
|
135
|
+
/** Reads the account's daily video upload allowance from the video service. */
|
|
136
|
+
readonly getVideoUploadLimits: (input: {
|
|
137
|
+
readonly account: ConnectedAccountRef;
|
|
138
|
+
readonly context?: AdapterOperationContext;
|
|
139
|
+
}) => Promise<BlueskyVideoUploadLimits>;
|
|
93
140
|
readonly follow: (input: {
|
|
94
141
|
readonly account: ConnectedAccountRef;
|
|
95
142
|
readonly did: string;
|
|
@@ -186,6 +233,23 @@ export interface BlueskyNative {
|
|
|
186
233
|
readonly text: string;
|
|
187
234
|
readonly context?: AdapterOperationContext;
|
|
188
235
|
}) => Promise<JsonObject>;
|
|
236
|
+
/**
|
|
237
|
+
* Hides or unhides a reply in a thread whose root post belongs to this account.
|
|
238
|
+
* Updates the root post's `app.bsky.feed.threadgate` record `hiddenReplies` list.
|
|
239
|
+
*/
|
|
240
|
+
readonly hideReply: (input: BlueskyHideReplyInput) => Promise<BlueskyHideReplyResult>;
|
|
241
|
+
}
|
|
242
|
+
export interface BlueskyHideReplyInput {
|
|
243
|
+
readonly account: ConnectedAccountRef;
|
|
244
|
+
/** AT-URI of the reply post to hide or unhide. */
|
|
245
|
+
readonly replyUri: string;
|
|
246
|
+
readonly hidden: boolean;
|
|
247
|
+
readonly context?: AdapterOperationContext;
|
|
248
|
+
}
|
|
249
|
+
export interface BlueskyHideReplyResult {
|
|
250
|
+
readonly hidden: boolean;
|
|
251
|
+
/** The threadgate record after the change. Absent when no threadgate exists. */
|
|
252
|
+
readonly threadgate?: BlueskyPostRef;
|
|
189
253
|
}
|
|
190
254
|
export interface BlueskyPageInput {
|
|
191
255
|
readonly account: ConnectedAccountRef;
|