fxtwitter 1.0.0 → 2.0.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/v2.mjs ADDED
@@ -0,0 +1,181 @@
1
+ import { n as FxTwitterError, t as HttpClient } from "./http-BTdKgEdX.mjs";
2
+ //#region src/v2.ts
3
+ /** Builds the `id:<rest_id>` handle form accepted by the profile endpoints. */
4
+ function byUserId(id) {
5
+ return `id:${id}`;
6
+ }
7
+ /**
8
+ * Recovers the body of a 404 that carries a well-formed, empty result page,
9
+ * which is how the list endpoints report "no results or timeline unavailable".
10
+ */
11
+ function emptyPage(error) {
12
+ if (!(error instanceof FxTwitterError) || error.status !== 404) return null;
13
+ const body = error.body;
14
+ if (typeof body !== "object" || body === null) return null;
15
+ const results = body.results;
16
+ return Array.isArray(results) ? body : null;
17
+ }
18
+ /** Client for the FxTwitter/FxEmbed v2 API (https://docs.fxembed.com/api/twitter/). */
19
+ var FxTwitterV2 = class {
20
+ http;
21
+ constructor(options = {}) {
22
+ this.http = new HttpClient(options);
23
+ }
24
+ /** Only `/2/profile/{handle}/statuses` can answer 204, so everything else demands a body. */
25
+ async get(path, query, signal) {
26
+ const body = await this.http.get({
27
+ path,
28
+ query,
29
+ signal
30
+ });
31
+ if (body === null) throw new FxTwitterError("The API returned an empty response");
32
+ return body;
33
+ }
34
+ /**
35
+ * Same as {@link get}, for the endpoints that report "no results" as a 404
36
+ * carrying an otherwise normal body. An empty page is a result, not an
37
+ * error, so it is returned rather than thrown.
38
+ *
39
+ * The API uses that same 404 for an unknown handle, which it does not
40
+ * distinguish from an empty timeline.
41
+ */
42
+ async getPage(path, query, signal) {
43
+ try {
44
+ return await this.get(path, query, signal);
45
+ } catch (error) {
46
+ const page = emptyPage(error);
47
+ if (page) return page;
48
+ throw error;
49
+ }
50
+ }
51
+ /** `GET /2/status/{id}` — a single post by snowflake ID. */
52
+ getStatus(id, options = {}) {
53
+ return this.get(`/2/status/${encodeURIComponent(id)}`, {
54
+ about_account: options.aboutAccount ? "1" : void 0,
55
+ lang: options.lang
56
+ }, options.signal);
57
+ }
58
+ /** `GET /2/status/{id}/reposts` — users who reposted a post. */
59
+ getStatusReposts(id, options = {}) {
60
+ return this.getPage(`/2/status/${encodeURIComponent(id)}/reposts`, {
61
+ count: options.count,
62
+ cursor: options.cursor
63
+ }, options.signal);
64
+ }
65
+ /** `GET /2/status/{id}/quotes` — posts quoting a post. */
66
+ getStatusQuotes(id, options = {}) {
67
+ return this.getPage(`/2/status/${encodeURIComponent(id)}/quotes`, {
68
+ count: options.count,
69
+ cursor: options.cursor,
70
+ lang: options.lang
71
+ }, options.signal);
72
+ }
73
+ /** `GET /2/thread/{id}` — a post with its unrolled thread. */
74
+ getThread(id, options = {}) {
75
+ return this.get(`/2/thread/${encodeURIComponent(id)}`, {
76
+ about_account: options.aboutAccount ? "1" : void 0,
77
+ lang: options.lang
78
+ }, options.signal);
79
+ }
80
+ /** `GET /2/conversation/{id}` — a post, its thread, and ranked replies. */
81
+ getConversation(id, options = {}) {
82
+ return this.get(`/2/conversation/${encodeURIComponent(id)}`, {
83
+ ranking_mode: options.rankingMode,
84
+ cursor: options.cursor,
85
+ about_account: options.aboutAccount ? "1" : void 0,
86
+ lang: options.lang
87
+ }, options.signal);
88
+ }
89
+ /**
90
+ * `GET /2/profile/{handle}` — a user's profile. `handle` is a username
91
+ * without `@`, or `byUserId(id)` for a numeric user ID.
92
+ */
93
+ getProfile(handle, options = {}) {
94
+ return this.get(`/2/profile/${encodeURIComponent(handle)}`, { about_account: options.aboutAccount ? "1" : void 0 }, options.signal);
95
+ }
96
+ /**
97
+ * `GET /2/profile/{handle}/statuses` — a user's posts. Resolves to `null`
98
+ * when `since` is set (without `cursor`) and nothing is newer than it.
99
+ */
100
+ async getProfileStatuses(handle, options = {}) {
101
+ try {
102
+ return await this.http.get({
103
+ path: `/2/profile/${encodeURIComponent(handle)}/statuses`,
104
+ query: {
105
+ count: options.count,
106
+ cursor: options.cursor,
107
+ since: options.since,
108
+ with_replies: options.withReplies ? "1" : void 0,
109
+ groupthreads: options.groupThreads ? "1" : void 0,
110
+ lang: options.lang
111
+ },
112
+ signal: options.signal
113
+ });
114
+ } catch (error) {
115
+ const page = emptyPage(error);
116
+ if (page) return page;
117
+ throw error;
118
+ }
119
+ }
120
+ /** `GET /2/profile/{handle}/articles` — a user's long-form articles. */
121
+ getProfileArticles(handle, options = {}) {
122
+ return this.getPage(`/2/profile/${encodeURIComponent(handle)}/articles`, {
123
+ count: options.count,
124
+ cursor: options.cursor,
125
+ lang: options.lang
126
+ }, options.signal);
127
+ }
128
+ /** `GET /2/profile/{handle}/about` — account metadata (based-in country, name changes). */
129
+ getProfileAbout(handle, options = {}) {
130
+ return this.get(`/2/profile/${encodeURIComponent(handle)}/about`, void 0, options.signal);
131
+ }
132
+ /** `GET /2/profile/{handle}/media` — a user's posts that contain media. */
133
+ getProfileMedia(handle, options = {}) {
134
+ return this.getPage(`/2/profile/${encodeURIComponent(handle)}/media`, {
135
+ count: options.count,
136
+ cursor: options.cursor,
137
+ lang: options.lang
138
+ }, options.signal);
139
+ }
140
+ /** `GET /2/profile/{handle}/followers` — a user's followers. */
141
+ getProfileFollowers(handle, options = {}) {
142
+ return this.getPage(`/2/profile/${encodeURIComponent(handle)}/followers`, {
143
+ count: options.count,
144
+ cursor: options.cursor
145
+ }, options.signal);
146
+ }
147
+ /** `GET /2/profile/{handle}/following` — accounts a user follows. */
148
+ getProfileFollowing(handle, options = {}) {
149
+ return this.getPage(`/2/profile/${encodeURIComponent(handle)}/following`, {
150
+ count: options.count,
151
+ cursor: options.cursor
152
+ }, options.signal);
153
+ }
154
+ /** `GET /2/search` — search posts. */
155
+ search(query, options = {}) {
156
+ return this.getPage("/2/search", {
157
+ q: query,
158
+ feed: options.feed,
159
+ count: options.count,
160
+ cursor: options.cursor,
161
+ lang: options.lang
162
+ }, options.signal);
163
+ }
164
+ /** `GET /2/typeahead` — search-box autocomplete suggestions. */
165
+ typeahead(query, options = {}) {
166
+ return this.get("/2/typeahead", {
167
+ q: query,
168
+ result_type: options.resultType?.join(","),
169
+ src: options.src
170
+ }, options.signal);
171
+ }
172
+ /** `GET /2/trends` — trending topics. */
173
+ trends(options = {}) {
174
+ return this.get("/2/trends", {
175
+ type: "trending",
176
+ count: options.count
177
+ }, options.signal);
178
+ }
179
+ };
180
+ //#endregion
181
+ export { FxTwitterV2, byUserId };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fxtwitter",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Typed wrapper for the FxTwitter API.",
5
5
  "keywords": [
6
6
  "x",
@@ -40,6 +40,14 @@
40
40
  "import": "./dist/v1.mjs",
41
41
  "require": "./dist/v1.cjs"
42
42
  },
43
+ "./v2": {
44
+ "types": {
45
+ "import": "./dist/v2.d.mts",
46
+ "require": "./dist/v2.d.cts"
47
+ },
48
+ "import": "./dist/v2.mjs",
49
+ "require": "./dist/v2.cjs"
50
+ },
43
51
  "./package.json": "./package.json"
44
52
  },
45
53
  "files": [
@@ -1,267 +0,0 @@
1
- //#region src/http.d.ts
2
- /** Options shared by every client, forwarded to ofetch. */
3
- interface ClientOptions {
4
- /** Overrides the API host. Defaults to `https://api.fxtwitter.com`. */
5
- baseUrl?: string;
6
- /** Custom fetch implementation (e.g. a mock, or undici in older runtimes). */
7
- fetch?: typeof fetch;
8
- /**
9
- * Extra headers sent with every request.
10
- *
11
- * When no `User-Agent` is given, one describing the current runtime is
12
- * added, because the API answers `401` to requests that do not identify
13
- * themselves. Set your own here to override it.
14
- */
15
- headers?: Record<string, string>;
16
- /** Abort a request after this many milliseconds. Disabled by default. */
17
- timeout?: number;
18
- /** Retry count for failed requests. Defaults to ofetch's `1` for GET. */
19
- retry?: number | false;
20
- /** Delay between retries in milliseconds. Defaults to `0`. */
21
- retryDelay?: number;
22
- }
23
- //#endregion
24
- //#region src/types/v1.d.ts
25
- interface Facet {
26
- /** e.g. `url`, `mention`, `hashtag`, `media`. */
27
- type: string;
28
- /** Start/end UTF-16 indices. */
29
- indices: [number, number];
30
- original?: string;
31
- replacement?: string;
32
- display?: string;
33
- id?: string;
34
- }
35
- interface RawText {
36
- text: string;
37
- facets: Facet[];
38
- }
39
- interface StatusRawText extends RawText {
40
- display_text_range: [number, number];
41
- }
42
- interface Website {
43
- url: string;
44
- display_url: string;
45
- }
46
- type VerificationType = "organization" | "government" | "individual" | null;
47
- interface Verification {
48
- verified: boolean;
49
- type: VerificationType;
50
- verified_at?: string | null;
51
- identity_verified?: boolean;
52
- verified_by?: string;
53
- }
54
- /** The author embedded in a status. Same shape as {@link User}, plus `avatar_color`. */
55
- interface Author {
56
- id: string;
57
- name: string;
58
- screen_name: string;
59
- url: string;
60
- description: string;
61
- raw_description: RawText;
62
- location: string;
63
- avatar_url: string | null;
64
- banner_url: string | null;
65
- /** @deprecated Always `null` upstream. */
66
- avatar_color?: string | null;
67
- followers: number;
68
- following: number;
69
- likes: number;
70
- media_count: number;
71
- joined: string;
72
- protected: boolean;
73
- website: Website | null;
74
- verification?: Verification;
75
- }
76
- /** The user returned by `GET /:screen_name`. Uses `tweets` where v2 uses `statuses`. */
77
- interface User extends Author {
78
- /** Status count. Named `statuses` in the v2 API. */
79
- tweets: number;
80
- }
81
- interface Translation {
82
- text: string;
83
- source_lang: string;
84
- source_lang_en: string;
85
- target_lang: string;
86
- provider: string;
87
- }
88
- interface PollChoice {
89
- label: string;
90
- count: number;
91
- /** 0-100. */
92
- percentage: number;
93
- }
94
- interface Poll {
95
- choices: PollChoice[];
96
- total_votes: number;
97
- ends_at: string;
98
- time_left_en: string;
99
- }
100
- interface VideoFormat {
101
- container?: "mp4" | "webm" | "m3u8";
102
- codec?: "h264" | "hevc" | "vp9" | "av1";
103
- bitrate?: number;
104
- url: string;
105
- size?: number;
106
- height?: number;
107
- width?: number;
108
- }
109
- interface Photo {
110
- id?: string;
111
- /** MIME type, e.g. `image/jpeg`. */
112
- format?: string;
113
- type: "photo" | "gif";
114
- url: string;
115
- width: number;
116
- height: number;
117
- transcode_url?: string | null;
118
- altText?: string;
119
- }
120
- interface Video {
121
- id?: string;
122
- /** MIME type, e.g. `video/mp4`. */
123
- format?: string;
124
- type: "video" | "gif";
125
- url: string;
126
- width: number;
127
- height: number;
128
- thumbnail_url?: string | null;
129
- transcode_url?: string | null;
130
- duration: number;
131
- filesize?: number;
132
- formats?: VideoFormat[];
133
- }
134
- interface MosaicPhoto {
135
- id?: string;
136
- type: "mosaic_photo";
137
- url?: string;
138
- width: number;
139
- height: number;
140
- formats: {
141
- webp: string;
142
- jpeg: string;
143
- };
144
- }
145
- interface ExternalMedia {
146
- /** Currently always `video`. */
147
- type: string;
148
- url: string;
149
- thumbnail_url?: string;
150
- height?: number;
151
- width?: number;
152
- duration?: number;
153
- }
154
- type AnyMedia = Photo | Video | MosaicPhoto;
155
- interface StatusMedia {
156
- /** Preserves the original ordering across photos and videos. */
157
- all?: AnyMedia[];
158
- external?: ExternalMedia;
159
- photos?: Photo[];
160
- videos?: Video[];
161
- mosaic?: MosaicPhoto;
162
- }
163
- interface CommunityNote {
164
- text: string;
165
- facets: Facet[];
166
- }
167
- interface RepostedBy {
168
- id: string;
169
- name: string;
170
- screen_name: string;
171
- avatar_url?: string | null;
172
- url?: string;
173
- }
174
- type TwitterCard = "tweet" | "summary" | "summary_large_image" | "player";
175
- interface Tweet {
176
- id: string;
177
- url: string;
178
- text: string;
179
- raw_text: StatusRawText;
180
- created_at: string;
181
- created_timestamp: number;
182
- author: Author;
183
- likes: number;
184
- /** Named `reposts` in the v2 API. */
185
- retweets: number;
186
- replies: number;
187
- quotes: number;
188
- views: number | null;
189
- bookmarks?: number | null;
190
- lang: string | null;
191
- /** Screen name of the account being replied to. An object in the v2 API. */
192
- replying_to: string | null;
193
- /** Status ID being replied to. Folded into `replying_to` in the v2 API. */
194
- replying_to_status: string | null;
195
- source: string | null;
196
- /** Named `embed_card` in the v2 API. */
197
- twitter_card: TwitterCard;
198
- provider?: string;
199
- possibly_sensitive?: boolean;
200
- is_note_tweet: boolean;
201
- community_note: CommunityNote | null;
202
- reposted_by: RepostedBy | null;
203
- /** @deprecated Always `null` upstream. */
204
- color?: string | null;
205
- quote?: Tweet;
206
- poll?: Poll;
207
- /** Only present when a translation target was requested. */
208
- translation?: Translation;
209
- media?: StatusMedia;
210
- }
211
- /**
212
- * `message` values the API maps onto its status codes: 200, 401, 404, 503
213
- * and 500 respectively.
214
- */
215
- type StatusMessage = "OK" | "PRIVATE_TWEET" | "NOT_FOUND" | "UPSTREAM_UNAVAILABLE" | "API_FAIL";
216
- interface StatusResponse {
217
- /** Mirrors the HTTP status code. */
218
- code: number;
219
- message: StatusMessage | string;
220
- /** `null` when the status could not be retrieved. */
221
- tweet: Tweet | null;
222
- }
223
- interface UserResponse {
224
- /** Mirrors the HTTP status code. */
225
- code: number;
226
- message: string;
227
- /** Omitted entirely when the user could not be retrieved. */
228
- user?: User;
229
- /** Set to `suspended` when the account is suspended. */
230
- reason?: "suspended";
231
- }
232
- //#endregion
233
- //#region src/v1.d.ts
234
- type FxTwitterV1Options = ClientOptions;
235
- interface GetStatusOptions {
236
- /**
237
- * Included in the URL for readability only. The API resolves the status from
238
- * `id` alone and never validates this, so a wrong handle still succeeds.
239
- */
240
- screenName?: string;
241
- /**
242
- * Language to translate the status into, as an ISO 639-1 code (`es`) or a
243
- * locale (`zh-cn`). Adds a `translation` object to the returned status.
244
- */
245
- translateTo?: string;
246
- /** Per-call cancellation, composed with the client-level `timeout`. */
247
- signal?: AbortSignal;
248
- }
249
- interface GetUserOptions {
250
- signal?: AbortSignal;
251
- }
252
- /** Client for the FxTwitter v1 API. */
253
- declare class FxTwitterV1 {
254
- private readonly http;
255
- constructor(options?: FxTwitterV1Options);
256
- /**
257
- * Fetches a single status by its snowflake ID.
258
- *
259
- * `GET /status/:id`, `GET /status/:id/:language`,
260
- * `GET /:handle/status/:id`, `GET /:handle/status/:id/:language`
261
- */
262
- getStatus(id: string, options?: GetStatusOptions): Promise<StatusResponse>;
263
- /** Fetches a user profile by handle (`GET /:handle`). */
264
- getUser(screenName: string, options?: GetUserOptions): Promise<UserResponse>;
265
- }
266
- //#endregion
267
- export { ClientOptions as A, User as C, Video as D, VerificationType as E, VideoFormat as O, TwitterCard as S, Verification as T, StatusMessage as _, AnyMedia as a, Translation as b, ExternalMedia as c, Photo as d, Poll as f, StatusMedia as g, RepostedBy as h, GetUserOptions as i, Website as k, Facet as l, RawText as m, FxTwitterV1Options as n, Author as o, PollChoice as p, GetStatusOptions as r, CommunityNote as s, FxTwitterV1 as t, MosaicPhoto as u, StatusRawText as v, UserResponse as w, Tweet as x, StatusResponse as y };