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/README.md +90 -31
- package/dist/{v1-KUPCoHD9.mjs → http-BTdKgEdX.mjs} +1 -56
- package/dist/http-CidaL0TV.d.cts +24 -0
- package/dist/http-CidaL0TV.d.mts +24 -0
- package/dist/{v1-Da3KbdIz.cjs → http-Cs4GJeg-.cjs} +2 -57
- package/dist/index.cjs +48 -2
- package/dist/index.d.cts +40 -2
- package/dist/index.d.mts +40 -2
- package/dist/index.mjs +45 -2
- package/dist/v1-BFKgLuJi.cjs +92 -0
- package/dist/v1-ByqsBlpO.mjs +87 -0
- package/dist/v1.cjs +1 -1
- package/dist/v1.d.cts +252 -1
- package/dist/v1.d.mts +252 -1
- package/dist/v1.mjs +1 -1
- package/dist/v2.cjs +183 -0
- package/dist/v2.d.cts +685 -0
- package/dist/v2.d.mts +685 -0
- package/dist/v2.mjs +181 -0
- package/package.json +9 -1
- package/dist/v1-BKUGwGbc.d.cts +0 -267
- package/dist/v1-BKUGwGbc.d.mts +0 -267
package/README.md
CHANGED
|
@@ -16,30 +16,37 @@ npm install fxtwitter
|
|
|
16
16
|
## Usage
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
|
-
import {
|
|
19
|
+
import { FxTwitterV2 } from "fxtwitter/v2";
|
|
20
20
|
|
|
21
|
-
const fx = new
|
|
21
|
+
const fx = new FxTwitterV2();
|
|
22
22
|
|
|
23
|
-
const {
|
|
24
|
-
const { user } = await fx.
|
|
23
|
+
const { status } = await fx.getStatus("20");
|
|
24
|
+
const { user } = await fx.getProfile("X");
|
|
25
|
+
const { results } = await fx.search("puppies");
|
|
25
26
|
```
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
Each version ships under its own subpath, where its types are exported
|
|
29
|
+
alongside the client:
|
|
28
30
|
|
|
29
31
|
```ts
|
|
30
|
-
import {
|
|
32
|
+
import { FxTwitterV2, type TwitterStatus } from "fxtwitter/v2";
|
|
33
|
+
import { FxTwitterV1, type Tweet } from "fxtwitter/v1";
|
|
31
34
|
```
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
If you need both at once, `FxTwitter` bundles them. `v1` is only constructed —
|
|
37
|
+
and so only warns — on first access:
|
|
35
38
|
|
|
36
39
|
```ts
|
|
37
|
-
import {
|
|
40
|
+
import { FxTwitter } from "fxtwitter";
|
|
41
|
+
|
|
42
|
+
const fx = new FxTwitter({ v1: { silenceDeprecationWarning: true } });
|
|
43
|
+
await fx.v2.getStatus("20");
|
|
44
|
+
await fx.v1.getStatus("20");
|
|
38
45
|
```
|
|
39
46
|
|
|
40
|
-
##
|
|
47
|
+
## Options
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
Every client takes the same options:
|
|
43
50
|
|
|
44
51
|
| Option | Type | Default | Description |
|
|
45
52
|
| --- | --- | --- | --- |
|
|
@@ -50,13 +57,15 @@ import { FxTwitterV1 } from "fxtwitter/v1";
|
|
|
50
57
|
| `retryDelay` | `number` | `0` | Delay between retries, in ms |
|
|
51
58
|
| `fetch` | `typeof fetch` | global `fetch` | Custom fetch implementation |
|
|
52
59
|
|
|
60
|
+
Every method takes an optional `signal` for per-call cancellation.
|
|
61
|
+
|
|
53
62
|
> The API requires a `User-Agent` identifying the caller and answers `401`
|
|
54
63
|
> without one. When you don't set one, a header describing the current runtime
|
|
55
64
|
> is sent — `Node.js/22.16.0`, `Bun/1.3.0`, `Cloudflare-Workers` and so on.
|
|
56
65
|
> Set your own to identify your app instead:
|
|
57
66
|
>
|
|
58
67
|
> ```ts
|
|
59
|
-
> new
|
|
68
|
+
> new FxTwitterV2({
|
|
60
69
|
> headers: { "User-Agent": "MyApp/1.0 (+https://example.com)" },
|
|
61
70
|
> });
|
|
62
71
|
> ```
|
|
@@ -64,14 +73,69 @@ import { FxTwitterV1 } from "fxtwitter/v1";
|
|
|
64
73
|
> Nothing is sent in browsers, where `User-Agent` is a forbidden header and the
|
|
65
74
|
> browser supplies its own.
|
|
66
75
|
|
|
67
|
-
|
|
76
|
+
## v2
|
|
77
|
+
|
|
78
|
+
### `new FxTwitterV2(options?)`
|
|
79
|
+
|
|
80
|
+
| Method | Returns |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `getStatus(id, options?)` | A single post |
|
|
83
|
+
| `getThread(id, options?)` | A post with its unrolled thread |
|
|
84
|
+
| `getConversation(id, options?)` | A post, its thread, and ranked replies |
|
|
85
|
+
| `getStatusReposts(id, options?)` | Users who reposted a post |
|
|
86
|
+
| `getStatusQuotes(id, options?)` | Posts quoting a post |
|
|
87
|
+
| `getProfile(handle, options?)` | A user profile |
|
|
88
|
+
| `getProfileStatuses(handle, options?)` | A user's posts |
|
|
89
|
+
| `getProfileArticles(handle, options?)` | A user's long-form articles |
|
|
90
|
+
| `getProfileMedia(handle, options?)` | A user's posts containing media |
|
|
91
|
+
| `getProfileAbout(handle, options?)` | Account metadata |
|
|
92
|
+
| `getProfileFollowers(handle, options?)` | A user's followers |
|
|
93
|
+
| `getProfileFollowing(handle, options?)` | Accounts a user follows |
|
|
94
|
+
| `search(query, options?)` | Post search results |
|
|
95
|
+
| `typeahead(query, options?)` | Autocomplete suggestions |
|
|
96
|
+
| `trends(options?)` | Trending topics |
|
|
97
|
+
|
|
98
|
+
Common options: `count` and `cursor` paginate list endpoints, `lang` requests an
|
|
99
|
+
inline translation, and `aboutAccount` adds `about_account` to an author.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
// Paginate
|
|
103
|
+
let cursor: string | undefined;
|
|
104
|
+
do {
|
|
105
|
+
const page = await fx.search("puppies", { feed: "top", count: 50, cursor });
|
|
106
|
+
cursor = page.cursor.bottom ?? undefined;
|
|
107
|
+
} while (cursor);
|
|
108
|
+
|
|
109
|
+
// Look a profile up by numeric ID
|
|
110
|
+
import { byUserId } from "fxtwitter/v2";
|
|
111
|
+
await fx.getProfile(byUserId("783214"));
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The list endpoints report "no results" as a `404` carrying an otherwise normal
|
|
115
|
+
body. An empty page is a result rather than an error, so it resolves with
|
|
116
|
+
`results: []`. The API uses that same `404` for an unknown handle, which it does
|
|
117
|
+
not distinguish from an empty timeline.
|
|
118
|
+
|
|
119
|
+
`getProfileStatuses` resolves to `null` when `since` is set without a `cursor`
|
|
120
|
+
and nothing is newer — the API's documented `204`. Every other method either
|
|
121
|
+
resolves with a body or throws.
|
|
122
|
+
|
|
123
|
+
## v1
|
|
124
|
+
|
|
125
|
+
> **Deprecated.** v1 is kept only for backwards compatibility and does not
|
|
126
|
+
> receive new features — use [v2](#v2) instead. Constructing `FxTwitterV1`
|
|
127
|
+
> emits a one-time `DeprecationWarning` (so `--no-deprecation` and
|
|
128
|
+
> `--throw-deprecation` apply); pass `silenceDeprecationWarning: true` to
|
|
129
|
+
> suppress it. `FxTwitter`'s `v1` property only constructs the client, and so
|
|
130
|
+
> only warns, on first access.
|
|
131
|
+
|
|
132
|
+
### `new FxTwitterV1(options?)`
|
|
133
|
+
|
|
134
|
+
#### `getStatus(id, options?)`
|
|
68
135
|
|
|
69
136
|
Fetches a single status by its snowflake ID.
|
|
70
137
|
|
|
71
138
|
```ts
|
|
72
|
-
const { tweet } = await fx.getStatus("20");
|
|
73
|
-
|
|
74
|
-
// Translate the status
|
|
75
139
|
const { tweet } = await fx.getStatus("20", { translateTo: "es" });
|
|
76
140
|
tweet?.translation?.text;
|
|
77
141
|
```
|
|
@@ -80,12 +144,11 @@ tweet?.translation?.text;
|
|
|
80
144
|
| --- | --- | --- |
|
|
81
145
|
| `translateTo` | `string` | Target language — an ISO 639-1 code (`es`) or locale (`zh-cn`). Adds `translation` to the status |
|
|
82
146
|
| `screenName` | `string` | Author handle, for a readable URL. The API resolves the status from `id` alone and never checks it |
|
|
83
|
-
| `signal` | `AbortSignal` | Per-call cancellation |
|
|
84
147
|
|
|
85
148
|
Resolves to `{ code, message, tweet }`. `tweet` is `null` when the status could
|
|
86
149
|
not be retrieved.
|
|
87
150
|
|
|
88
|
-
|
|
151
|
+
#### `getUser(handle, options?)`
|
|
89
152
|
|
|
90
153
|
Fetches a user profile by handle, without a leading `@`.
|
|
91
154
|
|
|
@@ -93,15 +156,19 @@ Fetches a user profile by handle, without a leading `@`.
|
|
|
93
156
|
const { user } = await fx.getUser("jack");
|
|
94
157
|
```
|
|
95
158
|
|
|
96
|
-
| Option | Type | Description |
|
|
97
|
-
| --- | --- | --- |
|
|
98
|
-
| `signal` | `AbortSignal` | Per-call cancellation |
|
|
99
|
-
|
|
100
159
|
Resolves to `{ code, message, user, reason? }`. `user` is absent when the
|
|
101
160
|
profile could not be retrieved, and `reason` is `"suspended"` for a suspended
|
|
102
161
|
account.
|
|
103
162
|
|
|
104
|
-
|
|
163
|
+
On success, `code` mirrors the HTTP status and `message` is one of `OK`,
|
|
164
|
+
`PRIVATE_TWEET`, `NOT_FOUND`, `UPSTREAM_UNAVAILABLE` or `API_FAIL`.
|
|
165
|
+
|
|
166
|
+
Malformed input is rejected before a request is sent, because v1 answers those
|
|
167
|
+
cases with HTML or a redirect rather than JSON: a status ID must be 2-20 digits,
|
|
168
|
+
and a handle passed to `getUser` must match `\w{1,15}`. A well-formed handle
|
|
169
|
+
that does not exist is a normal `404`.
|
|
170
|
+
|
|
171
|
+
## Errors
|
|
105
172
|
|
|
106
173
|
HTTP 4xx/5xx, network failures, timeouts, invalid input, and non-JSON responses
|
|
107
174
|
are thrown as `FxTwitterError`:
|
|
@@ -122,18 +189,10 @@ try {
|
|
|
122
189
|
}
|
|
123
190
|
```
|
|
124
191
|
|
|
125
|
-
On success, `code` mirrors the HTTP status and `message` is one of `OK`,
|
|
126
|
-
`PRIVATE_TWEET`, `NOT_FOUND`, `UPSTREAM_UNAVAILABLE` or `API_FAIL`.
|
|
127
|
-
|
|
128
192
|
For a network failure or a timeout there is no response, so `status`, `code`
|
|
129
193
|
and `body` are all `undefined` and `message` is a generic description. The
|
|
130
194
|
original error is still available as `cause`.
|
|
131
195
|
|
|
132
|
-
Malformed input is rejected before a request is sent, because the API answers
|
|
133
|
-
those cases with HTML or a redirect rather than JSON: a status ID must be 2-20
|
|
134
|
-
digits, and a handle passed to `getUser` must match `\w{1,15}`. A well-formed
|
|
135
|
-
handle that does not exist is a normal `404`.
|
|
136
|
-
|
|
137
196
|
## Requirements
|
|
138
197
|
|
|
139
198
|
- Node.js >= 22
|
|
@@ -144,59 +144,4 @@ function toFxTwitterError(cause) {
|
|
|
144
144
|
});
|
|
145
145
|
}
|
|
146
146
|
//#endregion
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* The API resolves a status from the first 2-20 digit run in the path segment,
|
|
150
|
-
* and answers anything else with its embed HTML at HTTP 200 rather than JSON.
|
|
151
|
-
*/
|
|
152
|
-
const STATUS_ID_PATTERN = /^\d{2,20}$/;
|
|
153
|
-
/**
|
|
154
|
-
* Handles are matched as `\w{1,15}` upstream; anything else is redirected to
|
|
155
|
-
* the project's GitHub page instead of returning JSON.
|
|
156
|
-
*/
|
|
157
|
-
const HANDLE_PATTERN = /^\w{1,15}$/;
|
|
158
|
-
/** Client for the FxTwitter v1 API. */
|
|
159
|
-
var FxTwitterV1 = class {
|
|
160
|
-
http;
|
|
161
|
-
constructor(options = {}) {
|
|
162
|
-
this.http = new HttpClient(options);
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Fetches a single status by its snowflake ID.
|
|
166
|
-
*
|
|
167
|
-
* `GET /status/:id`, `GET /status/:id/:language`,
|
|
168
|
-
* `GET /:handle/status/:id`, `GET /:handle/status/:id/:language`
|
|
169
|
-
*/
|
|
170
|
-
async getStatus(id, options = {}) {
|
|
171
|
-
if (!STATUS_ID_PATTERN.test(id)) throw new FxTwitterError(`Invalid status ID ${JSON.stringify(id)}: expected 2-20 digits`);
|
|
172
|
-
if (options.screenName !== void 0 && options.screenName.trim() === "") throw new FxTwitterError("screenName must not be empty");
|
|
173
|
-
const segments = [
|
|
174
|
-
options.screenName,
|
|
175
|
-
"status",
|
|
176
|
-
id,
|
|
177
|
-
options.translateTo
|
|
178
|
-
].filter((segment) => segment !== void 0);
|
|
179
|
-
return requireBody(await this.http.get({
|
|
180
|
-
path: `/${segments.map(encodeURIComponent).join("/")}`,
|
|
181
|
-
signal: options.signal
|
|
182
|
-
}));
|
|
183
|
-
}
|
|
184
|
-
/** Fetches a user profile by handle (`GET /:handle`). */
|
|
185
|
-
async getUser(screenName, options = {}) {
|
|
186
|
-
assertHandle(screenName);
|
|
187
|
-
return requireBody(await this.http.get({
|
|
188
|
-
path: `/${encodeURIComponent(screenName)}`,
|
|
189
|
-
signal: options.signal
|
|
190
|
-
}));
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
function assertHandle(handle) {
|
|
194
|
-
if (!HANDLE_PATTERN.test(handle)) throw new FxTwitterError(`Invalid handle ${JSON.stringify(handle)}: expected 1-15 letters, digits or underscores, without a leading @`);
|
|
195
|
-
}
|
|
196
|
-
/** v1 never answers 204, so an empty body here means something went wrong. */
|
|
197
|
-
function requireBody(body) {
|
|
198
|
-
if (body === null) throw new FxTwitterError("The API returned an empty response");
|
|
199
|
-
return body;
|
|
200
|
-
}
|
|
201
|
-
//#endregion
|
|
202
|
-
export { FxTwitterError as n, FxTwitterV1 as t };
|
|
147
|
+
export { FxTwitterError as n, HttpClient as t };
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
export { ClientOptions as t };
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
export { ClientOptions as t };
|
|
@@ -144,70 +144,15 @@ function toFxTwitterError(cause) {
|
|
|
144
144
|
});
|
|
145
145
|
}
|
|
146
146
|
//#endregion
|
|
147
|
-
//#region src/v1.ts
|
|
148
|
-
/**
|
|
149
|
-
* The API resolves a status from the first 2-20 digit run in the path segment,
|
|
150
|
-
* and answers anything else with its embed HTML at HTTP 200 rather than JSON.
|
|
151
|
-
*/
|
|
152
|
-
const STATUS_ID_PATTERN = /^\d{2,20}$/;
|
|
153
|
-
/**
|
|
154
|
-
* Handles are matched as `\w{1,15}` upstream; anything else is redirected to
|
|
155
|
-
* the project's GitHub page instead of returning JSON.
|
|
156
|
-
*/
|
|
157
|
-
const HANDLE_PATTERN = /^\w{1,15}$/;
|
|
158
|
-
/** Client for the FxTwitter v1 API. */
|
|
159
|
-
var FxTwitterV1 = class {
|
|
160
|
-
http;
|
|
161
|
-
constructor(options = {}) {
|
|
162
|
-
this.http = new HttpClient(options);
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Fetches a single status by its snowflake ID.
|
|
166
|
-
*
|
|
167
|
-
* `GET /status/:id`, `GET /status/:id/:language`,
|
|
168
|
-
* `GET /:handle/status/:id`, `GET /:handle/status/:id/:language`
|
|
169
|
-
*/
|
|
170
|
-
async getStatus(id, options = {}) {
|
|
171
|
-
if (!STATUS_ID_PATTERN.test(id)) throw new FxTwitterError(`Invalid status ID ${JSON.stringify(id)}: expected 2-20 digits`);
|
|
172
|
-
if (options.screenName !== void 0 && options.screenName.trim() === "") throw new FxTwitterError("screenName must not be empty");
|
|
173
|
-
const segments = [
|
|
174
|
-
options.screenName,
|
|
175
|
-
"status",
|
|
176
|
-
id,
|
|
177
|
-
options.translateTo
|
|
178
|
-
].filter((segment) => segment !== void 0);
|
|
179
|
-
return requireBody(await this.http.get({
|
|
180
|
-
path: `/${segments.map(encodeURIComponent).join("/")}`,
|
|
181
|
-
signal: options.signal
|
|
182
|
-
}));
|
|
183
|
-
}
|
|
184
|
-
/** Fetches a user profile by handle (`GET /:handle`). */
|
|
185
|
-
async getUser(screenName, options = {}) {
|
|
186
|
-
assertHandle(screenName);
|
|
187
|
-
return requireBody(await this.http.get({
|
|
188
|
-
path: `/${encodeURIComponent(screenName)}`,
|
|
189
|
-
signal: options.signal
|
|
190
|
-
}));
|
|
191
|
-
}
|
|
192
|
-
};
|
|
193
|
-
function assertHandle(handle) {
|
|
194
|
-
if (!HANDLE_PATTERN.test(handle)) throw new FxTwitterError(`Invalid handle ${JSON.stringify(handle)}: expected 1-15 letters, digits or underscores, without a leading @`);
|
|
195
|
-
}
|
|
196
|
-
/** v1 never answers 204, so an empty body here means something went wrong. */
|
|
197
|
-
function requireBody(body) {
|
|
198
|
-
if (body === null) throw new FxTwitterError("The API returned an empty response");
|
|
199
|
-
return body;
|
|
200
|
-
}
|
|
201
|
-
//#endregion
|
|
202
147
|
Object.defineProperty(exports, "FxTwitterError", {
|
|
203
148
|
enumerable: true,
|
|
204
149
|
get: function() {
|
|
205
150
|
return FxTwitterError;
|
|
206
151
|
}
|
|
207
152
|
});
|
|
208
|
-
Object.defineProperty(exports, "
|
|
153
|
+
Object.defineProperty(exports, "HttpClient", {
|
|
209
154
|
enumerable: true,
|
|
210
155
|
get: function() {
|
|
211
|
-
return
|
|
156
|
+
return HttpClient;
|
|
212
157
|
}
|
|
213
158
|
});
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,50 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_v1 = require("./v1-
|
|
3
|
-
|
|
2
|
+
const require_v1 = require("./v1-BFKgLuJi.cjs");
|
|
3
|
+
const require_http = require("./http-Cs4GJeg-.cjs");
|
|
4
|
+
const require_v2 = require("./v2.cjs");
|
|
5
|
+
//#region src/client.ts
|
|
6
|
+
/**
|
|
7
|
+
* Combined client exposing every API version.
|
|
8
|
+
*
|
|
9
|
+
* Prefer importing a single version when you only need one — `FxTwitterV2`
|
|
10
|
+
* from `fxtwitter/v2` — and reach for this only when you use both.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const fx = new FxTwitter();
|
|
14
|
+
* const { status } = await fx.v2.getStatus("20");
|
|
15
|
+
* const { tweet } = await fx.v1.getStatus("20");
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
var FxTwitter = class {
|
|
19
|
+
v2;
|
|
20
|
+
v1Options;
|
|
21
|
+
v1Client;
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
const { v1, v2, ...shared } = options;
|
|
24
|
+
this.v2 = new require_v2.FxTwitterV2({
|
|
25
|
+
...shared,
|
|
26
|
+
baseUrl: v2?.baseUrl
|
|
27
|
+
});
|
|
28
|
+
this.v1Options = {
|
|
29
|
+
...shared,
|
|
30
|
+
baseUrl: v1?.baseUrl,
|
|
31
|
+
silenceDeprecationWarning: v1?.silenceDeprecationWarning
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The legacy v1 client, constructed on first access so its deprecation
|
|
36
|
+
* warning is only emitted if you actually use it.
|
|
37
|
+
*
|
|
38
|
+
* @deprecated Use {@link FxTwitter.v2} instead.
|
|
39
|
+
*/
|
|
40
|
+
get v1() {
|
|
41
|
+
this.v1Client ??= new require_v1.FxTwitterV1(this.v1Options);
|
|
42
|
+
return this.v1Client;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
//#endregion
|
|
46
|
+
exports.FxTwitter = FxTwitter;
|
|
47
|
+
exports.FxTwitterError = require_http.FxTwitterError;
|
|
4
48
|
exports.FxTwitterV1 = require_v1.FxTwitterV1;
|
|
49
|
+
exports.FxTwitterV2 = require_v2.FxTwitterV2;
|
|
50
|
+
exports.byUserId = require_v2.byUserId;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as ClientOptions } from "./http-CidaL0TV.cjs";
|
|
2
|
+
import { AnyMedia, Author, CommunityNote, ExternalMedia, Facet, FxTwitterV1, FxTwitterV1Options, GetStatusOptions, GetUserOptions, MosaicPhoto, Photo, Poll, PollChoice, RawText, RepostedBy, StatusMedia, StatusMessage, StatusRawText, StatusResponse, Translation, Tweet, TwitterCard, User, UserResponse, Verification, VerificationType, Video, VideoFormat, Website } from "./v1.cjs";
|
|
3
|
+
import { FxTwitterV2, FxTwitterV2Options, byUserId, t as v2_d_exports } from "./v2.cjs";
|
|
4
|
+
//#region src/client.d.ts
|
|
5
|
+
type FxTwitterOptions = ClientOptions & {
|
|
6
|
+
/** Overrides applied only to the v1 client. */
|
|
7
|
+
v1?: Pick<ClientOptions, "baseUrl"> & {
|
|
8
|
+
/** Suppresses the one-time deprecation warning emitted when `v1` is first accessed. */
|
|
9
|
+
silenceDeprecationWarning?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/** Overrides applied only to the v2 client. */
|
|
12
|
+
v2?: Pick<ClientOptions, "baseUrl">;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Combined client exposing every API version.
|
|
16
|
+
*
|
|
17
|
+
* Prefer importing a single version when you only need one — `FxTwitterV2`
|
|
18
|
+
* from `fxtwitter/v2` — and reach for this only when you use both.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* const fx = new FxTwitter();
|
|
22
|
+
* const { status } = await fx.v2.getStatus("20");
|
|
23
|
+
* const { tweet } = await fx.v1.getStatus("20");
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare class FxTwitter {
|
|
27
|
+
readonly v2: FxTwitterV2;
|
|
28
|
+
private readonly v1Options;
|
|
29
|
+
private v1Client?;
|
|
30
|
+
constructor(options?: FxTwitterOptions);
|
|
31
|
+
/**
|
|
32
|
+
* The legacy v1 client, constructed on first access so its deprecation
|
|
33
|
+
* warning is only emitted if you actually use it.
|
|
34
|
+
*
|
|
35
|
+
* @deprecated Use {@link FxTwitter.v2} instead.
|
|
36
|
+
*/
|
|
37
|
+
get v1(): FxTwitterV1;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
2
40
|
//#region src/errors.d.ts
|
|
3
41
|
interface FxTwitterErrorOptions {
|
|
4
42
|
/** HTTP status code returned by the API. */
|
|
@@ -17,4 +55,4 @@ declare class FxTwitterError extends Error {
|
|
|
17
55
|
constructor(message: string, options?: FxTwitterErrorOptions);
|
|
18
56
|
}
|
|
19
57
|
//#endregion
|
|
20
|
-
export { type AnyMedia, type Author, type ClientOptions, type CommunityNote, type ExternalMedia, type Facet, FxTwitterError, type FxTwitterErrorOptions, FxTwitterV1, type FxTwitterV1Options, type GetStatusOptions, type GetUserOptions, type MosaicPhoto, type Photo, type Poll, type PollChoice, type RawText, type RepostedBy, type StatusMedia, type StatusMessage, type StatusRawText, type StatusResponse, type Translation, type Tweet, type TwitterCard, type User, type UserResponse, type Verification, type VerificationType, type Video, type VideoFormat, type Website };
|
|
58
|
+
export { type AnyMedia, type Author, type ClientOptions, type CommunityNote, type ExternalMedia, type Facet, FxTwitter, FxTwitterError, type FxTwitterErrorOptions, type FxTwitterOptions, FxTwitterV1, type FxTwitterV1Options, FxTwitterV2, type FxTwitterV2Options, type GetStatusOptions, type GetUserOptions, type MosaicPhoto, type Photo, type Poll, type PollChoice, type RawText, type RepostedBy, type StatusMedia, type StatusMessage, type StatusRawText, type StatusResponse, type Translation, type Tweet, type TwitterCard, type User, type UserResponse, type v2_d_exports as V2, type Verification, type VerificationType, type Video, type VideoFormat, type Website, byUserId };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,42 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as ClientOptions } from "./http-CidaL0TV.mjs";
|
|
2
|
+
import { AnyMedia, Author, CommunityNote, ExternalMedia, Facet, FxTwitterV1, FxTwitterV1Options, GetStatusOptions, GetUserOptions, MosaicPhoto, Photo, Poll, PollChoice, RawText, RepostedBy, StatusMedia, StatusMessage, StatusRawText, StatusResponse, Translation, Tweet, TwitterCard, User, UserResponse, Verification, VerificationType, Video, VideoFormat, Website } from "./v1.mjs";
|
|
3
|
+
import { FxTwitterV2, FxTwitterV2Options, byUserId, t as v2_d_exports } from "./v2.mjs";
|
|
4
|
+
//#region src/client.d.ts
|
|
5
|
+
type FxTwitterOptions = ClientOptions & {
|
|
6
|
+
/** Overrides applied only to the v1 client. */
|
|
7
|
+
v1?: Pick<ClientOptions, "baseUrl"> & {
|
|
8
|
+
/** Suppresses the one-time deprecation warning emitted when `v1` is first accessed. */
|
|
9
|
+
silenceDeprecationWarning?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/** Overrides applied only to the v2 client. */
|
|
12
|
+
v2?: Pick<ClientOptions, "baseUrl">;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Combined client exposing every API version.
|
|
16
|
+
*
|
|
17
|
+
* Prefer importing a single version when you only need one — `FxTwitterV2`
|
|
18
|
+
* from `fxtwitter/v2` — and reach for this only when you use both.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* const fx = new FxTwitter();
|
|
22
|
+
* const { status } = await fx.v2.getStatus("20");
|
|
23
|
+
* const { tweet } = await fx.v1.getStatus("20");
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare class FxTwitter {
|
|
27
|
+
readonly v2: FxTwitterV2;
|
|
28
|
+
private readonly v1Options;
|
|
29
|
+
private v1Client?;
|
|
30
|
+
constructor(options?: FxTwitterOptions);
|
|
31
|
+
/**
|
|
32
|
+
* The legacy v1 client, constructed on first access so its deprecation
|
|
33
|
+
* warning is only emitted if you actually use it.
|
|
34
|
+
*
|
|
35
|
+
* @deprecated Use {@link FxTwitter.v2} instead.
|
|
36
|
+
*/
|
|
37
|
+
get v1(): FxTwitterV1;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
2
40
|
//#region src/errors.d.ts
|
|
3
41
|
interface FxTwitterErrorOptions {
|
|
4
42
|
/** HTTP status code returned by the API. */
|
|
@@ -17,4 +55,4 @@ declare class FxTwitterError extends Error {
|
|
|
17
55
|
constructor(message: string, options?: FxTwitterErrorOptions);
|
|
18
56
|
}
|
|
19
57
|
//#endregion
|
|
20
|
-
export { type AnyMedia, type Author, type ClientOptions, type CommunityNote, type ExternalMedia, type Facet, FxTwitterError, type FxTwitterErrorOptions, FxTwitterV1, type FxTwitterV1Options, type GetStatusOptions, type GetUserOptions, type MosaicPhoto, type Photo, type Poll, type PollChoice, type RawText, type RepostedBy, type StatusMedia, type StatusMessage, type StatusRawText, type StatusResponse, type Translation, type Tweet, type TwitterCard, type User, type UserResponse, type Verification, type VerificationType, type Video, type VideoFormat, type Website };
|
|
58
|
+
export { type AnyMedia, type Author, type ClientOptions, type CommunityNote, type ExternalMedia, type Facet, FxTwitter, FxTwitterError, type FxTwitterErrorOptions, type FxTwitterOptions, FxTwitterV1, type FxTwitterV1Options, FxTwitterV2, type FxTwitterV2Options, type GetStatusOptions, type GetUserOptions, type MosaicPhoto, type Photo, type Poll, type PollChoice, type RawText, type RepostedBy, type StatusMedia, type StatusMessage, type StatusRawText, type StatusResponse, type Translation, type Tweet, type TwitterCard, type User, type UserResponse, type v2_d_exports as V2, type Verification, type VerificationType, type Video, type VideoFormat, type Website, byUserId };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { t as FxTwitterV1 } from "./v1-ByqsBlpO.mjs";
|
|
2
|
+
import { n as FxTwitterError } from "./http-BTdKgEdX.mjs";
|
|
3
|
+
import { FxTwitterV2, byUserId } from "./v2.mjs";
|
|
4
|
+
//#region src/client.ts
|
|
5
|
+
/**
|
|
6
|
+
* Combined client exposing every API version.
|
|
7
|
+
*
|
|
8
|
+
* Prefer importing a single version when you only need one — `FxTwitterV2`
|
|
9
|
+
* from `fxtwitter/v2` — and reach for this only when you use both.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* const fx = new FxTwitter();
|
|
13
|
+
* const { status } = await fx.v2.getStatus("20");
|
|
14
|
+
* const { tweet } = await fx.v1.getStatus("20");
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
var FxTwitter = class {
|
|
18
|
+
v2;
|
|
19
|
+
v1Options;
|
|
20
|
+
v1Client;
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
const { v1, v2, ...shared } = options;
|
|
23
|
+
this.v2 = new FxTwitterV2({
|
|
24
|
+
...shared,
|
|
25
|
+
baseUrl: v2?.baseUrl
|
|
26
|
+
});
|
|
27
|
+
this.v1Options = {
|
|
28
|
+
...shared,
|
|
29
|
+
baseUrl: v1?.baseUrl,
|
|
30
|
+
silenceDeprecationWarning: v1?.silenceDeprecationWarning
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The legacy v1 client, constructed on first access so its deprecation
|
|
35
|
+
* warning is only emitted if you actually use it.
|
|
36
|
+
*
|
|
37
|
+
* @deprecated Use {@link FxTwitter.v2} instead.
|
|
38
|
+
*/
|
|
39
|
+
get v1() {
|
|
40
|
+
this.v1Client ??= new FxTwitterV1(this.v1Options);
|
|
41
|
+
return this.v1Client;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
export { FxTwitter, FxTwitterError, FxTwitterV1, FxTwitterV2, byUserId };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const require_http = require("./http-Cs4GJeg-.cjs");
|
|
2
|
+
//#region src/deprecation.ts
|
|
3
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
4
|
+
/**
|
|
5
|
+
* Emits a deprecation warning once per process, the way Node's own deprecated
|
|
6
|
+
* APIs (and libraries such as discord.js) do — so `--no-deprecation` and
|
|
7
|
+
* `--throw-deprecation` apply, and repeated construction doesn't spam the
|
|
8
|
+
* console. Falls back to `console.warn` in runtimes without
|
|
9
|
+
* `process.emitWarning`.
|
|
10
|
+
*/
|
|
11
|
+
function emitDeprecationWarning(code, message) {
|
|
12
|
+
if (emitted.has(code)) return;
|
|
13
|
+
emitted.add(code);
|
|
14
|
+
const proc = globalThis.process;
|
|
15
|
+
if (typeof proc?.emitWarning === "function") {
|
|
16
|
+
proc.emitWarning(message, {
|
|
17
|
+
type: "DeprecationWarning",
|
|
18
|
+
code
|
|
19
|
+
});
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
console.warn(`DeprecationWarning [${code}]: ${message}`);
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/v1.ts
|
|
26
|
+
/**
|
|
27
|
+
* The API resolves a status from the first 2-20 digit run in the path segment,
|
|
28
|
+
* and answers anything else with its embed HTML at HTTP 200 rather than JSON.
|
|
29
|
+
*/
|
|
30
|
+
const STATUS_ID_PATTERN = /^\d{2,20}$/;
|
|
31
|
+
/**
|
|
32
|
+
* Handles are matched as `\w{1,15}` upstream; anything else is redirected to
|
|
33
|
+
* the project's GitHub page instead of returning JSON.
|
|
34
|
+
*/
|
|
35
|
+
const HANDLE_PATTERN = /^\w{1,15}$/;
|
|
36
|
+
const DEPRECATION_MESSAGE = "FxTwitterV1 targets the legacy FxTwitter v1 API, which is kept only for backwards compatibility and does not receive new features. Use FxTwitterV2 (import from \"fxtwitter/v2\") instead.";
|
|
37
|
+
/**
|
|
38
|
+
* Client for the legacy FxTwitter v1 API.
|
|
39
|
+
*
|
|
40
|
+
* @deprecated Use {@link import("./v2").FxTwitterV2} instead. v1 is kept only
|
|
41
|
+
* for backwards compatibility and does not receive new features.
|
|
42
|
+
*/
|
|
43
|
+
var FxTwitterV1 = class {
|
|
44
|
+
http;
|
|
45
|
+
constructor(options = {}) {
|
|
46
|
+
if (!options.silenceDeprecationWarning) emitDeprecationWarning("FXTWITTER_V1_DEPRECATED", DEPRECATION_MESSAGE);
|
|
47
|
+
this.http = new require_http.HttpClient(options);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Fetches a single status by its snowflake ID.
|
|
51
|
+
*
|
|
52
|
+
* `GET /status/:id`, `GET /status/:id/:language`,
|
|
53
|
+
* `GET /:handle/status/:id`, `GET /:handle/status/:id/:language`
|
|
54
|
+
*/
|
|
55
|
+
async getStatus(id, options = {}) {
|
|
56
|
+
if (!STATUS_ID_PATTERN.test(id)) throw new require_http.FxTwitterError(`Invalid status ID ${JSON.stringify(id)}: expected 2-20 digits`);
|
|
57
|
+
if (options.screenName !== void 0 && options.screenName.trim() === "") throw new require_http.FxTwitterError("screenName must not be empty");
|
|
58
|
+
const segments = [
|
|
59
|
+
options.screenName,
|
|
60
|
+
"status",
|
|
61
|
+
id,
|
|
62
|
+
options.translateTo
|
|
63
|
+
].filter((segment) => segment !== void 0);
|
|
64
|
+
return requireBody(await this.http.get({
|
|
65
|
+
path: `/${segments.map(encodeURIComponent).join("/")}`,
|
|
66
|
+
signal: options.signal
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
/** Fetches a user profile by handle (`GET /:handle`). */
|
|
70
|
+
async getUser(screenName, options = {}) {
|
|
71
|
+
assertHandle(screenName);
|
|
72
|
+
return requireBody(await this.http.get({
|
|
73
|
+
path: `/${encodeURIComponent(screenName)}`,
|
|
74
|
+
signal: options.signal
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
function assertHandle(handle) {
|
|
79
|
+
if (!HANDLE_PATTERN.test(handle)) throw new require_http.FxTwitterError(`Invalid handle ${JSON.stringify(handle)}: expected 1-15 letters, digits or underscores, without a leading @`);
|
|
80
|
+
}
|
|
81
|
+
/** v1 never answers 204, so an empty body here means something went wrong. */
|
|
82
|
+
function requireBody(body) {
|
|
83
|
+
if (body === null) throw new require_http.FxTwitterError("The API returned an empty response");
|
|
84
|
+
return body;
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
Object.defineProperty(exports, "FxTwitterV1", {
|
|
88
|
+
enumerable: true,
|
|
89
|
+
get: function() {
|
|
90
|
+
return FxTwitterV1;
|
|
91
|
+
}
|
|
92
|
+
});
|