@apicity/x 0.1.0-alpha.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Justin Tanner
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,327 @@
1
+ # @apicity/x
2
+
3
+ [![npm](https://img.shields.io/npm/v/@apicity/x?color=cb0000)](https://www.npmjs.com/package/@apicity/x)
4
+ [![zero dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](package.json)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue?logo=typescript&logoColor=white)](tsconfig.json)
6
+
7
+ X (formerly Twitter) social API provider for posting content (api.x.com).
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @apicity/x
13
+ # or
14
+ pnpm add @apicity/x
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { x as createX } from "@apicity/x";
21
+
22
+ const x = createX({ accessToken: process.env.X_ACCESS_TOKEN! });
23
+ ```
24
+
25
+ ## Setup
26
+
27
+ X requires an **OAuth 2.0 user-context access token** to post or upload
28
+ media. App-only Bearer tokens are read-only and rejected by the upload
29
+ and tweets endpoints.
30
+
31
+ ### 1. Configure your X app
32
+
33
+ Open [console.x.com](https://console.x.com) and make sure your app lives
34
+ in a **Pay Per Use** project — the legacy *Free* project is deprecated
35
+ and v2 endpoints reject its tokens with `client-not-enrolled`. Move the
36
+ app from the Apps list if needed.
37
+
38
+ Then open the app and click **User authentication settings → Set up**:
39
+
40
+ - Type of App: **Web App, Automated App or Bot** (this yields a Client Secret)
41
+ - App permissions: **Read and write**
42
+ - Callback URI: `http://127.0.0.1:8765/callback`
43
+ - Website URL: any valid URL
44
+
45
+ Save and copy the **OAuth 2.0 Client ID** and **Client Secret**.
46
+
47
+ ### 2. Load credits
48
+
49
+ Pay-per-use bills per write (~$0.015 / post). Open **Billing → Credits**
50
+ and load the minimum (typically $5). Without credits, write endpoints
51
+ return `402 Your enrolled account does not have any credits to fulfill
52
+ this request` — even though authentication itself succeeds.
53
+
54
+ ### 3. Mint an access token
55
+
56
+ Save the script below as `mint-x-token.mjs` and run it:
57
+
58
+ ```bash
59
+ X_CLIENT_ID=<your-client-id> \
60
+ X_CLIENT_SECRET=<your-client-secret> \
61
+ node mint-x-token.mjs
62
+ ```
63
+
64
+ It prints an authorize URL — open it, click **Authorize app**, and the
65
+ helper captures the redirect on `127.0.0.1:8765` and prints the access
66
+ token + refresh token. Access tokens last 2 hours; the refresh token
67
+ (via `offline.access` scope) lets you mint a new one without
68
+ re-authorizing.
69
+
70
+ <details>
71
+ <summary><code>mint-x-token.mjs</code> — zero-dep OAuth 2.0 PKCE helper</summary>
72
+
73
+ ```javascript
74
+ import http from "node:http";
75
+ import crypto from "node:crypto";
76
+
77
+ const CLIENT_ID = process.env.X_CLIENT_ID;
78
+ const CLIENT_SECRET = process.env.X_CLIENT_SECRET;
79
+ const REDIRECT = "http://127.0.0.1:8765/callback";
80
+ const SCOPES = [
81
+ "tweet.read",
82
+ "tweet.write",
83
+ "media.write",
84
+ "users.read",
85
+ "offline.access",
86
+ ].join(" ");
87
+
88
+ if (!CLIENT_ID || !CLIENT_SECRET) {
89
+ console.error("Set X_CLIENT_ID and X_CLIENT_SECRET");
90
+ process.exit(1);
91
+ }
92
+
93
+ const verifier = crypto.randomBytes(32).toString("base64url");
94
+ const challenge = crypto
95
+ .createHash("sha256")
96
+ .update(verifier)
97
+ .digest("base64url");
98
+ const state = crypto.randomBytes(16).toString("hex");
99
+
100
+ const authURL = new URL("https://x.com/i/oauth2/authorize");
101
+ authURL.searchParams.set("response_type", "code");
102
+ authURL.searchParams.set("client_id", CLIENT_ID);
103
+ authURL.searchParams.set("redirect_uri", REDIRECT);
104
+ authURL.searchParams.set("scope", SCOPES);
105
+ authURL.searchParams.set("state", state);
106
+ authURL.searchParams.set("code_challenge", challenge);
107
+ authURL.searchParams.set("code_challenge_method", "S256");
108
+
109
+ console.log("Open this URL and click \"Authorize app\":\n" + authURL.toString());
110
+
111
+ const server = http.createServer(async (req, res) => {
112
+ const url = new URL(req.url, REDIRECT);
113
+ if (!url.pathname.startsWith("/callback")) {
114
+ res.writeHead(404).end();
115
+ return;
116
+ }
117
+ const code = url.searchParams.get("code");
118
+ if (!code || url.searchParams.get("state") !== state) {
119
+ res.writeHead(400).end("bad state");
120
+ server.close();
121
+ process.exit(1);
122
+ }
123
+ const basic = Buffer.from(
124
+ `${CLIENT_ID}:${CLIENT_SECRET}`
125
+ ).toString("base64");
126
+ const tokenRes = await fetch("https://api.x.com/2/oauth2/token", {
127
+ method: "POST",
128
+ headers: {
129
+ Authorization: `Basic ${basic}`,
130
+ "Content-Type": "application/x-www-form-urlencoded",
131
+ },
132
+ body: new URLSearchParams({
133
+ grant_type: "authorization_code",
134
+ code,
135
+ redirect_uri: REDIRECT,
136
+ code_verifier: verifier,
137
+ }),
138
+ });
139
+ const tok = await tokenRes.json();
140
+ console.log(JSON.stringify(tok, null, 2));
141
+ res.writeHead(200).end("Authorized — check your terminal.");
142
+ server.close();
143
+ });
144
+
145
+ server.listen(8765, "127.0.0.1");
146
+ ```
147
+
148
+ </details>
149
+
150
+ ### 4. Use the token
151
+
152
+ ```typescript
153
+ import { x as createX } from "@apicity/x";
154
+
155
+ const x = createX({ accessToken: process.env.X_ACCESS_TOKEN });
156
+
157
+ await x.post.v2.tweets({
158
+ text: "hello from @apicity/x",
159
+ });
160
+ ```
161
+
162
+ ## Real-world example: post a video
163
+
164
+ Posting a video on X is a four-call dance — initialize a chunked
165
+ media upload, append the bytes, finalize to kick off transcoding,
166
+ poll until the media is ready, then attach the resulting `media_id`
167
+ to the tweet. The flow below is taken verbatim from
168
+ [`tests/integration/x-post-video.test.ts`](../../../tests/integration/x-post-video.test.ts)
169
+ and replays against
170
+ [`tests/recordings/x_*/post-video_*/recording.har`](../../../tests/recordings/),
171
+ so the response shapes match what X actually returns.
172
+
173
+ ```typescript
174
+ import { readFileSync } from "node:fs";
175
+ import { x as createX } from "@apicity/x";
176
+
177
+ const x = createX({ accessToken: process.env.X_ACCESS_TOKEN! });
178
+
179
+ // 1. Initialize a chunked upload — declare the media type, total
180
+ // byte length, and category up-front. X reserves a media_id we'll
181
+ // thread through every later call.
182
+ const bytes = readFileSync("./jump.mp4"); // 1,318,021 bytes in the recording
183
+ const init = await x.post.v2.media.upload.initialize({
184
+ media_type: "video/mp4",
185
+ total_bytes: bytes.length,
186
+ media_category: "tweet_video",
187
+ });
188
+ const mediaId = init.data.id;
189
+ // → "2050123807214718976"
190
+
191
+ // 2. Append the bytes. For files >5MB slice the buffer into
192
+ // segments and call append once per chunk with segment_index 0..n.
193
+ await x.post.v2.media.upload.append(mediaId, {
194
+ media: new Blob([bytes], { type: "video/mp4" }),
195
+ segment_index: 0,
196
+ });
197
+
198
+ // 3. Finalize. X queues server-side transcoding and returns
199
+ // processing_info.state = "pending" while the worker is busy.
200
+ const fin = await x.post.v2.media.upload.finalize(mediaId);
201
+ // fin.data.processing_info → { state: "pending", check_after_secs: 1 }
202
+
203
+ // 4. Poll status until the media is ready. Honor
204
+ // `check_after_secs` so the loop respects X's pacing hint.
205
+ let state = fin.data.processing_info?.state ?? "succeeded";
206
+ let wait = fin.data.processing_info?.check_after_secs ?? 1;
207
+ while (state === "pending" || state === "in_progress") {
208
+ await new Promise((r) => setTimeout(r, wait * 1000));
209
+ const status = await x.get.v2.media.upload(mediaId);
210
+ state = status.data.processing_info?.state ?? "succeeded";
211
+ wait = status.data.processing_info?.check_after_secs ?? 1;
212
+ }
213
+ // status.data.processing_info → { state: "succeeded", progress_percent: 100 }
214
+
215
+ // 5. Post the tweet, attaching the now-ready media id.
216
+ const tweet = await x.post.v2.tweets({
217
+ text: "jump",
218
+ media: { media_ids: [mediaId] },
219
+ });
220
+
221
+ console.log(tweet.data.id);
222
+ // → "2050123819986378933"
223
+ console.log(tweet.data.text);
224
+ // → "jump https://t.co/X8cTIpcy3s"
225
+ // X auto-appends the attached media's t.co URL to the returned
226
+ // text — the literal request body just had "jump".
227
+ ```
228
+
229
+ **Notes**
230
+
231
+ - `media_category` must match the asset: `tweet_video`, `tweet_image`,
232
+ `tweet_gif`, or `amplify_video` for long-form. Mismatches are rejected
233
+ at finalize, not initialize.
234
+ - Uploads expire after `data.expires_after_secs` (24h). If you finalize
235
+ but never reference the `media_id` in a tweet, it is garbage-collected.
236
+ - Errors from any step throw `XError` with `status` and the parsed body
237
+ attached, so `try { ... } catch (e) { if (e instanceof XError) ... }`
238
+ gives you the upstream `errors[0].message` or `detail` directly.
239
+
240
+ ## API Reference
241
+
242
+ 5 endpoints across 2 groups. Each method mirrors an upstream URL path.
243
+
244
+ ### media
245
+
246
+ <details>
247
+ <summary><code>GET</code> <b><code>x.v2.media.upload</code></b></summary>
248
+
249
+ <code>GET https://api.x.com/2/media/upload{query}</code>
250
+
251
+ [Upstream docs ↗](https://docs.x.com/x-api/media/get-media-upload-status)
252
+
253
+ ```typescript
254
+ const res = await x.v2.media.upload({ /* ... */ });
255
+ ```
256
+
257
+ Source: [`packages/provider/x/src/x.ts`](src/x.ts)
258
+
259
+ </details>
260
+
261
+ <details>
262
+ <summary><code>POST</code> <b><code>x.v2.media.upload.append</code></b></summary>
263
+
264
+ <code>POST https://api.x.com/2/media/upload/{id}/append</code>
265
+
266
+ [Upstream docs ↗](https://docs.x.com/x-api/media/append-media-upload)
267
+
268
+ ```typescript
269
+ const res = await x.v2.media.upload.append({ /* ... */ });
270
+ ```
271
+
272
+ Source: [`packages/provider/x/src/x.ts`](src/x.ts)
273
+
274
+ </details>
275
+
276
+ <details>
277
+ <summary><code>POST</code> <b><code>x.v2.media.upload.finalize</code></b></summary>
278
+
279
+ <code>POST https://api.x.com/2/media/upload/{id}/finalize</code>
280
+
281
+ [Upstream docs ↗](https://docs.x.com/x-api/media/finalize-media-upload)
282
+
283
+ ```typescript
284
+ const res = await x.v2.media.upload.finalize({ /* ... */ });
285
+ ```
286
+
287
+ Source: [`packages/provider/x/src/x.ts`](src/x.ts)
288
+
289
+ </details>
290
+
291
+ <details>
292
+ <summary><code>POST</code> <b><code>x.v2.media.upload.initialize</code></b></summary>
293
+
294
+ <code>POST https://api.x.com/2/media/upload/initialize</code>
295
+
296
+ [Upstream docs ↗](https://docs.x.com/x-api/media/media-upload-initialize)
297
+
298
+ ```typescript
299
+ const res = await x.v2.media.upload.initialize({ /* ... */ });
300
+ ```
301
+
302
+ Source: [`packages/provider/x/src/x.ts`](src/x.ts)
303
+
304
+ </details>
305
+
306
+ ### tweets
307
+
308
+ <details>
309
+ <summary><code>POST</code> <b><code>x.v2.tweets</code></b></summary>
310
+
311
+ <code>POST https://api.x.com/2/tweets</code>
312
+
313
+ [Upstream docs ↗](https://docs.x.com/x-api/posts/create-post)
314
+
315
+ ```typescript
316
+ const res = await x.v2.tweets({ /* ... */ });
317
+ ```
318
+
319
+ Source: [`packages/provider/x/src/x.ts`](src/x.ts)
320
+
321
+ </details>
322
+
323
+ Part of the [apicity](https://github.com/justintanner/apicity) monorepo.
324
+
325
+ ## License
326
+
327
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,8 @@
1
+ export interface EndpointExample {
2
+ source: string;
3
+ payload: unknown;
4
+ }
5
+ declare const EXAMPLES: Record<string, EndpointExample>;
6
+ export default EXAMPLES;
7
+ export declare function attachExamples<T>(provider: T): T;
8
+ //# sourceMappingURL=example.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example.d.ts","sourceRoot":"","sources":["../../src/example.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,QAAA,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAe7C,CAAC;AAEF,eAAe,QAAQ,CAAC;AAOxB,wBAAgB,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CA6ChD"}
@@ -0,0 +1,100 @@
1
+ // Auto-generated by `pnpm run gen:examples` — do not edit by hand.
2
+ // Source: tests/recordings/<provider>_*/<test>_*/recording.har
3
+ //
4
+ // Each entry is the green-path payload for one endpoint, mined from a real
5
+ // integration-test recording. `attachExamples` walks the provider tree and
6
+ // hangs the matching entry off each endpoint function as `.example`.
7
+ const EXAMPLES = {
8
+ "POST v2.media.upload.initialize": {
9
+ "source": "x/media-upload-initialize",
10
+ "payload": {
11
+ "media_type": "video/mp4",
12
+ "total_bytes": 64,
13
+ "media_category": "tweet_video"
14
+ }
15
+ },
16
+ "POST v2.tweets": {
17
+ "source": "x/tweets-create",
18
+ "payload": {
19
+ "text": "hello from @apicity/x"
20
+ }
21
+ }
22
+ };
23
+ export default EXAMPLES;
24
+ // Walks each "<METHOD> <dotPath>" key onto the provider's tree. Tries the
25
+ // standard `provider.<method>.<dotPath>` shape first, then a few fallbacks
26
+ // to cover providers with non-standard layouts (fal's `.run.` namespace,
27
+ // kie's sub-providers, `free`'s flat root). Returns the same provider for
28
+ // drop-in use as `return attachExamples({ ... });`.
29
+ export function attachExamples(provider) {
30
+ const root = provider;
31
+ const HTTP_KEYS = new Set(["post", "get", "put", "delete", "patch", "head"]);
32
+ for (const [key, example] of Object.entries(EXAMPLES)) {
33
+ const sp = key.indexOf(" ");
34
+ if (sp < 0)
35
+ continue;
36
+ const method = key.slice(0, sp).toLowerCase();
37
+ const segs = key.slice(sp + 1).split(".");
38
+ const candidates = [
39
+ root[method],
40
+ root[method]?.run,
41
+ root,
42
+ ];
43
+ if (segs.length > 1) {
44
+ const sub = root[segs[0]];
45
+ if (sub && typeof sub === "object") {
46
+ const subMethod = sub[method];
47
+ if (subMethod)
48
+ candidates.push({ __nested: subMethod, __segs: segs.slice(1) });
49
+ }
50
+ }
51
+ let attached = false;
52
+ for (const c of candidates) {
53
+ const fn = walkToFn(c, segs);
54
+ if (fn) {
55
+ Object.assign(fn, { example });
56
+ attached = true;
57
+ break;
58
+ }
59
+ }
60
+ if (attached)
61
+ continue;
62
+ for (const k of Object.keys(root)) {
63
+ if (HTTP_KEYS.has(k))
64
+ continue;
65
+ if (!segs.includes(k))
66
+ continue;
67
+ const sub = root[k];
68
+ if (!sub || typeof sub !== "object")
69
+ continue;
70
+ const subMethod = sub[method];
71
+ if (!subMethod)
72
+ continue;
73
+ const fn = walkToFn(subMethod, segs);
74
+ if (fn) {
75
+ Object.assign(fn, { example });
76
+ break;
77
+ }
78
+ }
79
+ }
80
+ return provider;
81
+ }
82
+ function walkToFn(start, segs) {
83
+ if (start && typeof start === "object" && "__nested" in start) {
84
+ const wrapper = start;
85
+ return walkToFn(wrapper.__nested, wrapper.__segs);
86
+ }
87
+ let cur = start;
88
+ for (const seg of segs) {
89
+ if (cur === null || cur === undefined)
90
+ return null;
91
+ const t = typeof cur;
92
+ if (t !== "object" && t !== "function")
93
+ return null;
94
+ cur = cur[seg];
95
+ }
96
+ return typeof cur === "function"
97
+ ? cur
98
+ : null;
99
+ }
100
+ //# sourceMappingURL=example.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example.js","sourceRoot":"","sources":["../../src/example.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { x } from "./x";
2
+ export { XError } from "./types";
3
+ export type { XOptions, XMediaUploadInitializeRequest, XMediaUploadInitializeResponse, XMediaUploadInitializeData, XMediaUploadInitializeMethod, XMediaUploadAppendRequest, XMediaUploadAppendResponse, XMediaUploadAppendMethod, XMediaUploadFinalizeResponse, XMediaUploadFinalizeMethod, XMediaUploadStatusResponse, XMediaUploadStatusMethod, XTweetCreateRequest, XTweetCreateResponse, XTweetCreateMethod, XMediaProcessingInfo, XMediaUploadNamespace, XMediaNamespace, XPostV2Namespace, XPostNamespace, XGetMediaNamespace, XGetV2Namespace, XGetNamespace, XProvider, } from "./types";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,YAAY,EACV,QAAQ,EACR,6BAA6B,EAC7B,8BAA8B,EAC9B,0BAA0B,EAC1B,4BAA4B,EAC5B,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,4BAA4B,EAC5B,0BAA0B,EAC1B,0BAA0B,EAC1B,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,SAAS,GACV,MAAM,SAAS,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { x } from "./x.js";
2
+ export { XError } from "./types.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,89 @@
1
+ import type { z } from "zod";
2
+ import type { XMediaUploadInitializeRequest, XMediaUploadAppendRequest, XTweetCreateRequest } from "./zod";
3
+ export type { XOptions, XMediaUploadInitializeRequest, XMediaUploadAppendRequest, XTweetCreateRequest, } from "./zod";
4
+ export declare class XError extends Error {
5
+ readonly status: number;
6
+ readonly body: unknown;
7
+ readonly code?: string;
8
+ constructor(message: string, status: number, body?: unknown, code?: string);
9
+ }
10
+ export interface XMediaProcessingInfo {
11
+ type: string;
12
+ state: "pending" | "in_progress" | "succeeded" | "failed";
13
+ progress_percent?: number;
14
+ check_after_secs?: number;
15
+ error?: {
16
+ name?: string;
17
+ message?: string;
18
+ };
19
+ }
20
+ export interface XMediaUploadInitializeData {
21
+ id: string;
22
+ media_key: string;
23
+ expires_after_secs: number;
24
+ size: number;
25
+ processing_info?: XMediaProcessingInfo;
26
+ }
27
+ export interface XMediaUploadInitializeResponse {
28
+ data: XMediaUploadInitializeData;
29
+ }
30
+ export interface XMediaUploadAppendResponse {
31
+ data: {
32
+ expires_at: number;
33
+ };
34
+ }
35
+ export type XMediaUploadFinalizeResponse = XMediaUploadInitializeResponse;
36
+ export type XMediaUploadStatusResponse = XMediaUploadInitializeResponse;
37
+ export interface XTweetCreateResponse {
38
+ data: {
39
+ id: string;
40
+ text: string;
41
+ };
42
+ }
43
+ export interface XMediaUploadInitializeMethod {
44
+ (req: XMediaUploadInitializeRequest, signal?: AbortSignal): Promise<XMediaUploadInitializeResponse>;
45
+ schema: z.ZodType<XMediaUploadInitializeRequest>;
46
+ }
47
+ export interface XMediaUploadAppendMethod {
48
+ (id: string, req: XMediaUploadAppendRequest, signal?: AbortSignal): Promise<XMediaUploadAppendResponse>;
49
+ schema: z.ZodType<XMediaUploadAppendRequest>;
50
+ }
51
+ export interface XMediaUploadFinalizeMethod {
52
+ (id: string, signal?: AbortSignal): Promise<XMediaUploadFinalizeResponse>;
53
+ }
54
+ export interface XMediaUploadStatusMethod {
55
+ (mediaId: string, signal?: AbortSignal): Promise<XMediaUploadStatusResponse>;
56
+ }
57
+ export interface XTweetCreateMethod {
58
+ (req: XTweetCreateRequest, signal?: AbortSignal): Promise<XTweetCreateResponse>;
59
+ schema: z.ZodType<XTweetCreateRequest>;
60
+ }
61
+ export interface XMediaUploadNamespace {
62
+ initialize: XMediaUploadInitializeMethod;
63
+ append: XMediaUploadAppendMethod;
64
+ finalize: XMediaUploadFinalizeMethod;
65
+ }
66
+ export interface XMediaNamespace {
67
+ upload: XMediaUploadNamespace;
68
+ }
69
+ export interface XPostV2Namespace {
70
+ media: XMediaNamespace;
71
+ tweets: XTweetCreateMethod;
72
+ }
73
+ export interface XPostNamespace {
74
+ v2: XPostV2Namespace;
75
+ }
76
+ export interface XGetMediaNamespace {
77
+ upload: XMediaUploadStatusMethod;
78
+ }
79
+ export interface XGetV2Namespace {
80
+ media: XGetMediaNamespace;
81
+ }
82
+ export interface XGetNamespace {
83
+ v2: XGetV2Namespace;
84
+ }
85
+ export interface XProvider {
86
+ post: XPostNamespace;
87
+ get: XGetNamespace;
88
+ }
89
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ // -- Error -------------------------------------------------------------------
2
+ export class XError extends Error {
3
+ status;
4
+ body;
5
+ code;
6
+ constructor(message, status, body, code) {
7
+ super(message);
8
+ this.name = "XError";
9
+ this.status = status;
10
+ this.body = body ?? null;
11
+ this.code = code;
12
+ }
13
+ }
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAcA,+EAA+E;AAE/E,MAAM,OAAO,MAAO,SAAQ,KAAK;IACtB,MAAM,CAAS;IACf,IAAI,CAAU;IACd,IAAI,CAAU;IAEvB,YAAY,OAAe,EAAE,MAAc,EAAE,IAAc,EAAE,IAAa;QACxE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import { XOptions, XProvider } from "./types";
2
+ export declare function x(opts: XOptions): XProvider;
3
+ //# sourceMappingURL=x.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x.d.ts","sourceRoot":"","sources":["../../src/x.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EASR,SAAS,EAEV,MAAM,SAAS,CAAC;AAQjB,wBAAgB,CAAC,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,CAsP3C"}