@book000/pixivts 0.60.0 → 0.60.1
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/index.cjs +1529 -1448
- package/dist/index.d.cts +1272 -1327
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +1272 -1327
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1530 -1446
- package/dist/index.js.map +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,1520 +1,1604 @@
|
|
|
1
|
-
|
|
2
|
-
var OkResultImpl = class
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
unwrapOr(_fallback) {
|
|
25
|
-
return this.value;
|
|
26
|
-
}
|
|
1
|
+
//#region src/result.ts
|
|
2
|
+
var OkResultImpl = class OkResultImpl {
|
|
3
|
+
value;
|
|
4
|
+
isOk = true;
|
|
5
|
+
isErr = false;
|
|
6
|
+
constructor(value) {
|
|
7
|
+
this.value = value;
|
|
8
|
+
}
|
|
9
|
+
map(fn) {
|
|
10
|
+
return new OkResultImpl(fn(this.value));
|
|
11
|
+
}
|
|
12
|
+
mapErr(_fn) {
|
|
13
|
+
return this;
|
|
14
|
+
}
|
|
15
|
+
andThen(fn) {
|
|
16
|
+
return fn(this.value);
|
|
17
|
+
}
|
|
18
|
+
match(onOk, _onErr) {
|
|
19
|
+
return onOk(this.value);
|
|
20
|
+
}
|
|
21
|
+
unwrapOr(_fallback) {
|
|
22
|
+
return this.value;
|
|
23
|
+
}
|
|
27
24
|
};
|
|
28
|
-
var ErrResultImpl = class
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
unwrapOr(fallback) {
|
|
51
|
-
return fallback;
|
|
52
|
-
}
|
|
25
|
+
var ErrResultImpl = class ErrResultImpl {
|
|
26
|
+
error;
|
|
27
|
+
isOk = false;
|
|
28
|
+
isErr = true;
|
|
29
|
+
constructor(error) {
|
|
30
|
+
this.error = error;
|
|
31
|
+
}
|
|
32
|
+
map(_fn) {
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
mapErr(fn) {
|
|
36
|
+
return new ErrResultImpl(fn(this.error));
|
|
37
|
+
}
|
|
38
|
+
andThen(_fn) {
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
match(_onOk, onErr) {
|
|
42
|
+
return onErr(this.error);
|
|
43
|
+
}
|
|
44
|
+
unwrapOr(fallback) {
|
|
45
|
+
return fallback;
|
|
46
|
+
}
|
|
53
47
|
};
|
|
48
|
+
/**
|
|
49
|
+
* Creates a successful `Result<T, never>`.
|
|
50
|
+
*
|
|
51
|
+
* @param value - The success value
|
|
52
|
+
*/
|
|
54
53
|
function ok(value) {
|
|
55
|
-
|
|
54
|
+
return new OkResultImpl(value);
|
|
56
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Creates a failed `Result<never, E>`.
|
|
58
|
+
*
|
|
59
|
+
* @param error - The error value
|
|
60
|
+
*/
|
|
57
61
|
function err(error) {
|
|
58
|
-
|
|
62
|
+
return new ErrResultImpl(error);
|
|
59
63
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const r = await this._promise;
|
|
157
|
-
if (r.isOk) return r.value;
|
|
158
|
-
return fallback;
|
|
159
|
-
}
|
|
64
|
+
/**
|
|
65
|
+
* A `PromiseLike<Result<T, E>>` that is directly `await`-able and supports
|
|
66
|
+
* chainable `map / mapErr / andThen` operators.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const result = await ResultAsync.fromPromise(fetch('/api'), networkError)
|
|
71
|
+
* .andThen((res) =>
|
|
72
|
+
* ResultAsync.fromPromise(res.json() as Promise<unknown>, networkError)
|
|
73
|
+
* )
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
var ResultAsync = class ResultAsync {
|
|
77
|
+
_promise;
|
|
78
|
+
constructor(promise) {
|
|
79
|
+
this._promise = promise;
|
|
80
|
+
}
|
|
81
|
+
then(onfulfilled, onrejected) {
|
|
82
|
+
return this._promise.then(onfulfilled, onrejected);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Wraps a `Promise<T>` into a `ResultAsync<T, E>`.
|
|
86
|
+
*
|
|
87
|
+
* If the promise rejects, `onError` maps the rejection reason to `E`.
|
|
88
|
+
*
|
|
89
|
+
* @param promise - The promise to wrap
|
|
90
|
+
* @param onError - Error mapper
|
|
91
|
+
*/
|
|
92
|
+
static fromPromise(promise, onError) {
|
|
93
|
+
return new ResultAsync(promise.then((v) => ok(v), (error) => err(onError(error))));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Wraps an already-resolved `Result<T, E>` into a `ResultAsync<T, E>`.
|
|
97
|
+
*
|
|
98
|
+
* @param result - The result to wrap
|
|
99
|
+
*/
|
|
100
|
+
static fromResult(result) {
|
|
101
|
+
return new ResultAsync(Promise.resolve(result));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Transforms the success value.
|
|
105
|
+
*
|
|
106
|
+
* If the inner result is `Err`, `fn` is not called.
|
|
107
|
+
*
|
|
108
|
+
* @param fn - Synchronous mapper
|
|
109
|
+
*/
|
|
110
|
+
map(fn) {
|
|
111
|
+
return new ResultAsync(this._promise.then((r) => r.map(fn)));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Transforms the error value.
|
|
115
|
+
*
|
|
116
|
+
* If the inner result is `Ok`, `fn` is not called.
|
|
117
|
+
*
|
|
118
|
+
* @param fn - Synchronous error mapper
|
|
119
|
+
*/
|
|
120
|
+
mapErr(fn) {
|
|
121
|
+
return new ResultAsync(this._promise.then((r) => r.mapErr(fn)));
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Chains another async operation that may fail.
|
|
125
|
+
*
|
|
126
|
+
* If the inner result is `Err`, `fn` is not called.
|
|
127
|
+
*
|
|
128
|
+
* @param fn - Async mapper that returns a `ResultAsync<U, F>`
|
|
129
|
+
*/
|
|
130
|
+
andThen(fn) {
|
|
131
|
+
return new ResultAsync(this._promise.then(async (r) => {
|
|
132
|
+
if (r.isErr) return r;
|
|
133
|
+
const next = fn(r.value);
|
|
134
|
+
if (next instanceof ResultAsync) return next._promise;
|
|
135
|
+
return next;
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Pattern-matches on success / failure.
|
|
140
|
+
*
|
|
141
|
+
* @param onOk - Called with the success value
|
|
142
|
+
* @param onErr - Called with the error value
|
|
143
|
+
* @returns A `Promise<U>`
|
|
144
|
+
*/
|
|
145
|
+
async match(onOk, onErr) {
|
|
146
|
+
const r = await this._promise;
|
|
147
|
+
if (r.isOk) return onOk(r.value);
|
|
148
|
+
return onErr(r.error);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Returns the success value, or `fallback` if the result is `Err`.
|
|
152
|
+
*
|
|
153
|
+
* @param fallback - The fallback value
|
|
154
|
+
*/
|
|
155
|
+
async unwrapOr(fallback) {
|
|
156
|
+
const r = await this._promise;
|
|
157
|
+
if (r.isOk) return r.value;
|
|
158
|
+
return fallback;
|
|
159
|
+
}
|
|
160
160
|
};
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/errors.ts
|
|
163
|
+
/**
|
|
164
|
+
* An `Error` subclass that wraps a `PixivError` for use in thrown contexts
|
|
165
|
+
* (e.g. async generators that must throw proper `Error` objects).
|
|
166
|
+
*
|
|
167
|
+
* All `PixivError` properties are spread directly onto this instance so that
|
|
168
|
+
* callers can use `instanceof PixivFetchError` or access `error.type` etc.
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```ts
|
|
172
|
+
* try {
|
|
173
|
+
* for await (const page of result.pages()) { ... }
|
|
174
|
+
* } catch (e) {
|
|
175
|
+
* if (e instanceof PixivFetchError) {
|
|
176
|
+
* console.error(e.pixivError.type)
|
|
177
|
+
* }
|
|
178
|
+
* }
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
163
181
|
var PixivFetchError = class extends Error {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
182
|
+
/** The underlying structured `PixivError`. */
|
|
183
|
+
pixivError;
|
|
184
|
+
constructor(pixivError) {
|
|
185
|
+
super(`pixiv API error: ${pixivError.type}`);
|
|
186
|
+
this.name = "PixivFetchError";
|
|
187
|
+
this.pixivError = pixivError;
|
|
188
|
+
Object.assign(this, pixivError);
|
|
189
|
+
}
|
|
172
190
|
};
|
|
191
|
+
/** Creates a rate-limit error. */
|
|
173
192
|
function rateLimitError(retryAfter) {
|
|
174
|
-
|
|
193
|
+
return {
|
|
194
|
+
type: "rate_limit",
|
|
195
|
+
retryAfter
|
|
196
|
+
};
|
|
175
197
|
}
|
|
198
|
+
/** Creates an auth-failed error. */
|
|
176
199
|
function authFailedError(status) {
|
|
177
|
-
|
|
200
|
+
return {
|
|
201
|
+
type: "auth_failed",
|
|
202
|
+
status
|
|
203
|
+
};
|
|
178
204
|
}
|
|
205
|
+
/** Creates a network error. */
|
|
179
206
|
function networkError(cause) {
|
|
180
|
-
|
|
207
|
+
return {
|
|
208
|
+
type: "network",
|
|
209
|
+
cause
|
|
210
|
+
};
|
|
181
211
|
}
|
|
212
|
+
/** Creates an API error. */
|
|
182
213
|
function apiError(status, body) {
|
|
183
|
-
|
|
214
|
+
return {
|
|
215
|
+
type: "api_error",
|
|
216
|
+
status,
|
|
217
|
+
body
|
|
218
|
+
};
|
|
184
219
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/paginated.ts
|
|
222
|
+
/**
|
|
223
|
+
* A `ResultAsync<TPage, PixivError>` with additional `.pages()` / `.items()`
|
|
224
|
+
* async generators for consuming paginated pixiv list responses.
|
|
225
|
+
*
|
|
226
|
+
* Returned by all resource methods that produce a `nextUrl` field.
|
|
227
|
+
*/
|
|
228
|
+
var PaginatedResultAsync = class PaginatedResultAsync extends ResultAsync {
|
|
229
|
+
#http;
|
|
230
|
+
#getItems;
|
|
231
|
+
constructor(promise, http, getItems) {
|
|
232
|
+
super(promise);
|
|
233
|
+
this.#http = http;
|
|
234
|
+
this.#getItems = getItems;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Creates a `PaginatedResultAsync` from a `ResultAsync`.
|
|
238
|
+
*
|
|
239
|
+
* @param inner - The first-page result
|
|
240
|
+
* @param http - HTTP client for fetching subsequent pages
|
|
241
|
+
* @param getItems - Extracts item array from a page
|
|
242
|
+
*/
|
|
243
|
+
static fromResultAsync(inner, http, getItems) {
|
|
244
|
+
return new PaginatedResultAsync(Promise.resolve(inner), http, getItems);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Async generator that yields each page starting from the first.
|
|
248
|
+
*
|
|
249
|
+
* If any page fetch fails, the generator throws a `PixivFetchError`.
|
|
250
|
+
*
|
|
251
|
+
* @example
|
|
252
|
+
* ```ts
|
|
253
|
+
* for await (const page of client.illusts.search({ word: 'cat' }).pages()) {
|
|
254
|
+
* console.log(page.illusts.length)
|
|
255
|
+
* }
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
async *pages() {
|
|
259
|
+
const first = await Promise.resolve(this);
|
|
260
|
+
if (first.isErr) throw new PixivFetchError(first.error);
|
|
261
|
+
yield first.value;
|
|
262
|
+
let nextUrl = first.value.nextUrl;
|
|
263
|
+
while (nextUrl !== null) {
|
|
264
|
+
const pageResult = await this.#http.getAbsolute(nextUrl);
|
|
265
|
+
if (pageResult.isErr) throw new PixivFetchError(pageResult.error);
|
|
266
|
+
yield pageResult.value;
|
|
267
|
+
nextUrl = pageResult.value.nextUrl;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Async generator that yields individual items across all pages.
|
|
272
|
+
*
|
|
273
|
+
* If any page fetch fails, the generator throws a `PixivFetchError`.
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```ts
|
|
277
|
+
* for await (const illust of client.illusts.search({ word: 'cat' }).items()) {
|
|
278
|
+
* console.log(illust.title)
|
|
279
|
+
* }
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
async *items() {
|
|
283
|
+
for await (const page of this.pages()) for (const item of this.#getItems(page)) yield item;
|
|
284
|
+
}
|
|
249
285
|
};
|
|
286
|
+
/**
|
|
287
|
+
* Creates an immediately-failed `PaginatedResultAsync`.
|
|
288
|
+
*
|
|
289
|
+
* Useful when validation or auth fails before any HTTP request is made.
|
|
290
|
+
*
|
|
291
|
+
* @param error - The error to return
|
|
292
|
+
* @param http - HTTP client (used for signature compatibility)
|
|
293
|
+
* @param getItems - Item extractor (used for signature compatibility)
|
|
294
|
+
*/
|
|
250
295
|
function failedPaginated(error, http, getItems) {
|
|
251
|
-
|
|
252
|
-
Promise.resolve(err(error)),
|
|
253
|
-
http,
|
|
254
|
-
getItems
|
|
255
|
-
);
|
|
296
|
+
return new PaginatedResultAsync(Promise.resolve(err(error)), http, getItems);
|
|
256
297
|
}
|
|
257
|
-
|
|
258
|
-
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/params.ts
|
|
300
|
+
/**
|
|
301
|
+
* Parameter building utilities.
|
|
302
|
+
*
|
|
303
|
+
* Provides camelCase → snake_case conversion and a URLSearchParams builder
|
|
304
|
+
* that replicates the behaviour of the `qs` library used in the legacy code
|
|
305
|
+
* (without the `qs` runtime dependency).
|
|
306
|
+
*/
|
|
307
|
+
/**
|
|
308
|
+
* Converts a camelCase string to snake_case.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* camelToSnake('illustId') // 'illust_id'
|
|
312
|
+
* camelToSnake('searchAiType') // 'search_ai_type'
|
|
313
|
+
*
|
|
314
|
+
* @param key - camelCase string
|
|
315
|
+
* @returns snake_case string
|
|
316
|
+
*/
|
|
259
317
|
function camelToSnake(key) {
|
|
260
|
-
|
|
318
|
+
return key.replaceAll(/([A-Z])/g, (m) => `_${m.toLowerCase()}`);
|
|
261
319
|
}
|
|
320
|
+
/**
|
|
321
|
+
* Converts all keys of a plain object from camelCase to snake_case, shallow.
|
|
322
|
+
*
|
|
323
|
+
* Values are preserved as-is; nested objects are NOT recursed into.
|
|
324
|
+
*
|
|
325
|
+
* @param obj - Object with camelCase keys
|
|
326
|
+
* @returns New object with snake_case keys
|
|
327
|
+
*/
|
|
262
328
|
function toSnakeKeys(obj) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
return out;
|
|
329
|
+
const out = {};
|
|
330
|
+
for (const key of Object.keys(obj)) out[camelToSnake(key)] = obj[key];
|
|
331
|
+
return out;
|
|
268
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* Serialises a record of query parameters into a `URLSearchParams` instance.
|
|
335
|
+
*
|
|
336
|
+
* Rules:
|
|
337
|
+
* - `null` / `undefined` values are skipped.
|
|
338
|
+
* - Arrays are appended with a `[]` suffix: `foo[]=1&foo[]=2` (Rails/pixiv convention).
|
|
339
|
+
* - Booleans are serialised as `'true'` / `'false'`.
|
|
340
|
+
* - Numbers are serialised via `.toString()`.
|
|
341
|
+
*
|
|
342
|
+
* @param params - Key/value pairs to serialise
|
|
343
|
+
* @returns Populated `URLSearchParams`
|
|
344
|
+
*/
|
|
269
345
|
function buildSearchParams(params) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
return usp;
|
|
346
|
+
const usp = new URLSearchParams();
|
|
347
|
+
for (const [key, value] of Object.entries(params)) {
|
|
348
|
+
if (value === null || value === void 0) continue;
|
|
349
|
+
if (Array.isArray(value)) for (const item of value) usp.append(`${key}[]`, String(item));
|
|
350
|
+
else usp.set(key, String(value));
|
|
351
|
+
}
|
|
352
|
+
return usp;
|
|
280
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Merges camelCase→snake_case conversion with URLSearchParams building.
|
|
356
|
+
*
|
|
357
|
+
* Convenience wrapper used by resource methods.
|
|
358
|
+
*
|
|
359
|
+
* @param params - camelCase record
|
|
360
|
+
* @returns Populated `URLSearchParams` with snake_case keys
|
|
361
|
+
*/
|
|
281
362
|
function buildParams(params) {
|
|
282
|
-
|
|
363
|
+
return buildSearchParams(toSnakeKeys(params));
|
|
283
364
|
}
|
|
365
|
+
/**
|
|
366
|
+
* Converts a snake_case string to lowerCamelCase.
|
|
367
|
+
*
|
|
368
|
+
* Already-camelCase keys pass through unchanged (idempotent).
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* snakeToCamel('image_urls') // 'imageUrls'
|
|
372
|
+
* snakeToCamel('x_restrict') // 'xRestrict'
|
|
373
|
+
* snakeToCamel('imageUrls') // 'imageUrls' (no-op)
|
|
374
|
+
*
|
|
375
|
+
* @param key - snake_case string
|
|
376
|
+
* @returns lowerCamelCase string
|
|
377
|
+
*/
|
|
284
378
|
function snakeToCamel(key) {
|
|
285
|
-
|
|
379
|
+
return key.replaceAll(/_([a-z0-9])/g, (_m, c) => c.toUpperCase());
|
|
286
380
|
}
|
|
381
|
+
/**
|
|
382
|
+
* Recursively converts all object keys from snake_case to lowerCamelCase.
|
|
383
|
+
*
|
|
384
|
+
* - Plain objects: keys are converted, values are recursed.
|
|
385
|
+
* - Arrays: each element is recursed.
|
|
386
|
+
* - Primitives / null: returned as-is.
|
|
387
|
+
*
|
|
388
|
+
* All keys are converted uniformly — no path-based exclusions.
|
|
389
|
+
*
|
|
390
|
+
* @param value - Any JSON-compatible value
|
|
391
|
+
* @returns Deep copy with all object keys in lowerCamelCase
|
|
392
|
+
*/
|
|
287
393
|
function camelizeKeys(value) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
return out;
|
|
394
|
+
if (value === null || typeof value !== "object") return value;
|
|
395
|
+
if (Array.isArray(value)) return value.map((v) => camelizeKeys(v));
|
|
396
|
+
const out = {};
|
|
397
|
+
for (const [k, v] of Object.entries(value)) out[snakeToCamel(k)] = camelizeKeys(v);
|
|
398
|
+
return out;
|
|
295
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* Parses a pixiv `next_url` into a typed cursor object.
|
|
402
|
+
*
|
|
403
|
+
* Pass the `next_url` field from any paginated response to extract the
|
|
404
|
+
* cursor parameters needed to resume pagination from a saved position.
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* ```ts
|
|
408
|
+
* const page = await client.users.bookmarks.illusts({ userId: client.userId })
|
|
409
|
+
* if (page.isOk && page.value.nextUrl) {
|
|
410
|
+
* const cursor = parseNextUrl(page.value.nextUrl)
|
|
411
|
+
* // Resume later:
|
|
412
|
+
* const next = await client.users.bookmarks.illusts({
|
|
413
|
+
* userId: client.userId,
|
|
414
|
+
* maxBookmarkId: cursor.maxBookmarkId,
|
|
415
|
+
* })
|
|
416
|
+
* }
|
|
417
|
+
* ```
|
|
418
|
+
*
|
|
419
|
+
* @param url - The `next_url` string returned by a pixiv list endpoint
|
|
420
|
+
* @returns Typed cursor parameters; fields absent in the URL are `undefined`
|
|
421
|
+
*/
|
|
296
422
|
function parseNextUrl(url) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if (lastOrder !== void 0) result.lastOrder = lastOrder;
|
|
317
|
-
return result;
|
|
423
|
+
const usp = new URL(url).searchParams;
|
|
424
|
+
const result = {};
|
|
425
|
+
const toNum = (key) => {
|
|
426
|
+
const v = usp.get(key);
|
|
427
|
+
if (v === null || v === "") return void 0;
|
|
428
|
+
const n = Number(v);
|
|
429
|
+
return Number.isNaN(n) ? void 0 : n;
|
|
430
|
+
};
|
|
431
|
+
const maxBookmarkId = toNum("max_bookmark_id");
|
|
432
|
+
if (maxBookmarkId !== void 0) result.maxBookmarkId = maxBookmarkId;
|
|
433
|
+
const maxBookmarkIdForRecommend = toNum("max_bookmark_id_for_recommend");
|
|
434
|
+
if (maxBookmarkIdForRecommend !== void 0) result.maxBookmarkIdForRecommend = maxBookmarkIdForRecommend;
|
|
435
|
+
const minBookmarkIdForRecentIllust = toNum("min_bookmark_id_for_recent_illust");
|
|
436
|
+
if (minBookmarkIdForRecentIllust !== void 0) result.minBookmarkIdForRecentIllust = minBookmarkIdForRecentIllust;
|
|
437
|
+
const offset = toNum("offset");
|
|
438
|
+
if (offset !== void 0) result.offset = offset;
|
|
439
|
+
const lastOrder = toNum("last_order");
|
|
440
|
+
if (lastOrder !== void 0) result.lastOrder = lastOrder;
|
|
441
|
+
return result;
|
|
318
442
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
443
|
+
//#endregion
|
|
444
|
+
//#region src/options.ts
|
|
445
|
+
/**
|
|
446
|
+
* Public option constants for @book000/pixivts.
|
|
447
|
+
*
|
|
448
|
+
* Each option is exported as a runtime `const` object for enum-like access
|
|
449
|
+
* (e.g. `BookmarkRestrict.PUBLIC`). Plain string literals are also accepted
|
|
450
|
+
* wherever these values are used as parameters.
|
|
451
|
+
*
|
|
452
|
+
* @example
|
|
453
|
+
* ```ts
|
|
454
|
+
* // Enum-like usage
|
|
455
|
+
* await client.illusts.bookmarkAdd({ illustId: 123, restrict: BookmarkRestrict.PUBLIC })
|
|
456
|
+
*
|
|
457
|
+
* // Plain string literal — also valid
|
|
458
|
+
* await client.illusts.bookmarkAdd({ illustId: 123, restrict: 'public' })
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
/**
|
|
462
|
+
* Search match target for illust / novel searches.
|
|
463
|
+
*
|
|
464
|
+
* - `partial_match_for_tags` — tags contain the word (default)
|
|
465
|
+
* - `exact_match_for_tags` — tags exactly equal the word
|
|
466
|
+
* - `title_and_caption` — title or caption contains the word
|
|
467
|
+
* - `keyword` — general keyword search (novel only)
|
|
468
|
+
*/
|
|
469
|
+
const SearchTarget = {
|
|
470
|
+
PARTIAL_MATCH_FOR_TAGS: "partial_match_for_tags",
|
|
471
|
+
EXACT_MATCH_FOR_TAGS: "exact_match_for_tags",
|
|
472
|
+
TITLE_AND_CAPTION: "title_and_caption",
|
|
473
|
+
KEYWORD: "keyword"
|
|
326
474
|
};
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
475
|
+
/**
|
|
476
|
+
* Sort order for search results.
|
|
477
|
+
*
|
|
478
|
+
* - `date_desc` — newest first (default)
|
|
479
|
+
* - `date_asc` — oldest first
|
|
480
|
+
* - `popular_desc` — most bookmarks first (premium only)
|
|
481
|
+
*/
|
|
482
|
+
const SearchSort = {
|
|
483
|
+
DATE_DESC: "date_desc",
|
|
484
|
+
DATE_ASC: "date_asc",
|
|
485
|
+
POPULAR_DESC: "popular_desc"
|
|
331
486
|
};
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
487
|
+
/**
|
|
488
|
+
* Date range filter for search results.
|
|
489
|
+
*
|
|
490
|
+
* - `within_last_day` — past 24 hours
|
|
491
|
+
* - `within_last_week` — past 7 days
|
|
492
|
+
* - `within_last_month` — past 30 days
|
|
493
|
+
*/
|
|
494
|
+
const SearchDuration = {
|
|
495
|
+
WITHIN_LAST_DAY: "within_last_day",
|
|
496
|
+
WITHIN_LAST_WEEK: "within_last_week",
|
|
497
|
+
WITHIN_LAST_MONTH: "within_last_month"
|
|
336
498
|
};
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
499
|
+
/**
|
|
500
|
+
* Ranking mode for illust rankings.
|
|
501
|
+
*
|
|
502
|
+
* R-18 modes require a premium account with R-18 content enabled.
|
|
503
|
+
*/
|
|
504
|
+
const RankingMode = {
|
|
505
|
+
DAY: "day",
|
|
506
|
+
DAY_MALE: "day_male",
|
|
507
|
+
DAY_FEMALE: "day_female",
|
|
508
|
+
WEEK_ORIGINAL: "week_original",
|
|
509
|
+
WEEK_ROOKIE: "week_rookie",
|
|
510
|
+
WEEK: "week",
|
|
511
|
+
MONTH: "month",
|
|
512
|
+
DAY_AI: "day_ai",
|
|
513
|
+
DAY_R18: "day_r18",
|
|
514
|
+
WEEK_R18: "week_r18",
|
|
515
|
+
DAY_MALE_R18: "day_male_r18",
|
|
516
|
+
DAY_FEMALE_R18: "day_female_r18",
|
|
517
|
+
DAY_R18_AI: "day_r18_ai"
|
|
351
518
|
};
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
519
|
+
/**
|
|
520
|
+
* Ranking mode for novel rankings.
|
|
521
|
+
*
|
|
522
|
+
* R-18 modes require a premium account with R-18 content enabled.
|
|
523
|
+
*/
|
|
524
|
+
const NovelRankingMode = {
|
|
525
|
+
DAY: "day",
|
|
526
|
+
WEEK: "week",
|
|
527
|
+
DAY_MALE: "day_male",
|
|
528
|
+
DAY_FEMALE: "day_female",
|
|
529
|
+
WEEK_ROOKIE: "week_rookie",
|
|
530
|
+
DAY_R18: "day_r18",
|
|
531
|
+
WEEK_R18: "week_r18",
|
|
532
|
+
DAY_R18_AI: "day_r18_ai"
|
|
361
533
|
};
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
534
|
+
/**
|
|
535
|
+
* Visibility restriction for bookmarks.
|
|
536
|
+
*
|
|
537
|
+
* - `public` — publicly visible (default)
|
|
538
|
+
* - `private` — visible only to the owner
|
|
539
|
+
*/
|
|
540
|
+
const BookmarkRestrict = {
|
|
541
|
+
PUBLIC: "public",
|
|
542
|
+
PRIVATE: "private"
|
|
365
543
|
};
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
544
|
+
/**
|
|
545
|
+
* Visibility restriction for follows.
|
|
546
|
+
*
|
|
547
|
+
* - `public` — publicly visible (default)
|
|
548
|
+
* - `private` — visible only to the owner
|
|
549
|
+
*/
|
|
550
|
+
const FollowRestrict = {
|
|
551
|
+
PUBLIC: "public",
|
|
552
|
+
PRIVATE: "private"
|
|
369
553
|
};
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
554
|
+
/**
|
|
555
|
+
* OS filter used to request works compatible with the given platform.
|
|
556
|
+
*
|
|
557
|
+
* - `for_ios` — iOS-compatible works (default)
|
|
558
|
+
* - `for_android` — Android-compatible works
|
|
559
|
+
*/
|
|
560
|
+
const OSFilter = {
|
|
561
|
+
FOR_IOS: "for_ios",
|
|
562
|
+
FOR_ANDROID: "for_android"
|
|
373
563
|
};
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
564
|
+
/**
|
|
565
|
+
* Work type filter for user illust listings.
|
|
566
|
+
*
|
|
567
|
+
* - `illust` — illustrations only
|
|
568
|
+
* - `manga` — manga only
|
|
569
|
+
*/
|
|
570
|
+
const UserIllustType = {
|
|
571
|
+
ILLUST: "illust",
|
|
572
|
+
MANGA: "manga"
|
|
377
573
|
};
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
10,
|
|
447
|
-
15,
|
|
448
|
-
21
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region src/auth.ts
|
|
576
|
+
const T = Array.from({ length: 64 }, (_, i) => Math.floor(Math.abs(Math.sin(i + 1)) * 2 ** 32));
|
|
577
|
+
const S = [
|
|
578
|
+
7,
|
|
579
|
+
12,
|
|
580
|
+
17,
|
|
581
|
+
22,
|
|
582
|
+
7,
|
|
583
|
+
12,
|
|
584
|
+
17,
|
|
585
|
+
22,
|
|
586
|
+
7,
|
|
587
|
+
12,
|
|
588
|
+
17,
|
|
589
|
+
22,
|
|
590
|
+
7,
|
|
591
|
+
12,
|
|
592
|
+
17,
|
|
593
|
+
22,
|
|
594
|
+
5,
|
|
595
|
+
9,
|
|
596
|
+
14,
|
|
597
|
+
20,
|
|
598
|
+
5,
|
|
599
|
+
9,
|
|
600
|
+
14,
|
|
601
|
+
20,
|
|
602
|
+
5,
|
|
603
|
+
9,
|
|
604
|
+
14,
|
|
605
|
+
20,
|
|
606
|
+
5,
|
|
607
|
+
9,
|
|
608
|
+
14,
|
|
609
|
+
20,
|
|
610
|
+
4,
|
|
611
|
+
11,
|
|
612
|
+
16,
|
|
613
|
+
23,
|
|
614
|
+
4,
|
|
615
|
+
11,
|
|
616
|
+
16,
|
|
617
|
+
23,
|
|
618
|
+
4,
|
|
619
|
+
11,
|
|
620
|
+
16,
|
|
621
|
+
23,
|
|
622
|
+
4,
|
|
623
|
+
11,
|
|
624
|
+
16,
|
|
625
|
+
23,
|
|
626
|
+
6,
|
|
627
|
+
10,
|
|
628
|
+
15,
|
|
629
|
+
21,
|
|
630
|
+
6,
|
|
631
|
+
10,
|
|
632
|
+
15,
|
|
633
|
+
21,
|
|
634
|
+
6,
|
|
635
|
+
10,
|
|
636
|
+
15,
|
|
637
|
+
21,
|
|
638
|
+
6,
|
|
639
|
+
10,
|
|
640
|
+
15,
|
|
641
|
+
21
|
|
449
642
|
];
|
|
450
643
|
function md5Bytes(bytes) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
644
|
+
const len = bytes.length;
|
|
645
|
+
bytes.push(128);
|
|
646
|
+
while (bytes.length % 64 !== 56) bytes.push(0);
|
|
647
|
+
const bitLen = len * 8;
|
|
648
|
+
for (let i = 0; i < 8; i++) bytes.push(i < 4 ? bitLen >>> i * 8 & 255 : 0);
|
|
649
|
+
let a = 1732584193;
|
|
650
|
+
let b = 4023233417;
|
|
651
|
+
let c = 2562383102;
|
|
652
|
+
let d = 271733878;
|
|
653
|
+
for (let chunk = 0; chunk < bytes.length; chunk += 64) {
|
|
654
|
+
const M = [];
|
|
655
|
+
for (let w = 0; w < 16; w++) {
|
|
656
|
+
const off = chunk + w * 4;
|
|
657
|
+
M.push(bytes[off] | bytes[off + 1] << 8 | bytes[off + 2] << 16 | bytes[off + 3] << 24);
|
|
658
|
+
}
|
|
659
|
+
let aa = a;
|
|
660
|
+
let bb = b;
|
|
661
|
+
let cc = c;
|
|
662
|
+
let dd = d;
|
|
663
|
+
for (let i = 0; i < 64; i++) {
|
|
664
|
+
let f;
|
|
665
|
+
let g;
|
|
666
|
+
if (i < 16) {
|
|
667
|
+
f = bb & cc | ~bb & dd;
|
|
668
|
+
g = i;
|
|
669
|
+
} else if (i < 32) {
|
|
670
|
+
f = dd & bb | ~dd & cc;
|
|
671
|
+
g = (5 * i + 1) % 16;
|
|
672
|
+
} else if (i < 48) {
|
|
673
|
+
f = bb ^ cc ^ dd;
|
|
674
|
+
g = (3 * i + 5) % 16;
|
|
675
|
+
} else {
|
|
676
|
+
f = cc ^ (bb | ~dd);
|
|
677
|
+
g = 7 * i % 16;
|
|
678
|
+
}
|
|
679
|
+
const tmp = dd;
|
|
680
|
+
dd = cc;
|
|
681
|
+
cc = bb;
|
|
682
|
+
const sum = Math.trunc(aa + f + M[g] + T[i]);
|
|
683
|
+
const rotated = sum << S[i] | sum >>> 32 - S[i];
|
|
684
|
+
bb = Math.trunc(bb + rotated);
|
|
685
|
+
aa = tmp;
|
|
686
|
+
}
|
|
687
|
+
a = Math.trunc(a + aa);
|
|
688
|
+
b = Math.trunc(b + bb);
|
|
689
|
+
c = Math.trunc(c + cc);
|
|
690
|
+
d = Math.trunc(d + dd);
|
|
691
|
+
}
|
|
692
|
+
return [
|
|
693
|
+
a,
|
|
694
|
+
b,
|
|
695
|
+
c,
|
|
696
|
+
d
|
|
697
|
+
].map((n) => [
|
|
698
|
+
n & 255,
|
|
699
|
+
n >>> 8 & 255,
|
|
700
|
+
n >>> 16 & 255,
|
|
701
|
+
n >>> 24 & 255
|
|
702
|
+
].map((byte) => byte.toString(16).padStart(2, "0")).join("")).join("");
|
|
506
703
|
}
|
|
704
|
+
/**
|
|
705
|
+
* Produces a hex-encoded MD5 digest of `input`.
|
|
706
|
+
*
|
|
707
|
+
* This is a minimal, spec-compliant implementation (RFC 1321) that avoids
|
|
708
|
+
* any runtime platform dependency.
|
|
709
|
+
*
|
|
710
|
+
* @param input - UTF-8 string to hash
|
|
711
|
+
* @returns Lowercase hex-encoded MD5 digest
|
|
712
|
+
*/
|
|
507
713
|
function md5(input) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
bytes.push(
|
|
517
|
-
224 | code >> 12,
|
|
518
|
-
128 | code >> 6 & 63,
|
|
519
|
-
128 | code & 63
|
|
520
|
-
);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
return md5Bytes(bytes);
|
|
714
|
+
const bytes = [];
|
|
715
|
+
for (let i = 0; i < input.length; i++) {
|
|
716
|
+
const code = input.codePointAt(i) ?? 0;
|
|
717
|
+
if (code < 128) bytes.push(code);
|
|
718
|
+
else if (code < 2048) bytes.push(192 | code >> 6, 128 | code & 63);
|
|
719
|
+
else bytes.push(224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63);
|
|
720
|
+
}
|
|
721
|
+
return md5Bytes(bytes);
|
|
524
722
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
723
|
+
const CLIENT_ID = "MOBrBDS8blbauoSck0ZfDbtuzpyT";
|
|
724
|
+
const CLIENT_SECRET = "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj";
|
|
725
|
+
const HASH_SECRET = "28c1fdd170a5204386cb1313c7077b34f83e4aaf4aa829ce78c231e05b0bae2c";
|
|
726
|
+
const AUTH_URL = "https://oauth.secure.pixiv.net/auth/token";
|
|
727
|
+
/** Builds the x-client-hash header value for a given UTC timestamp string. */
|
|
529
728
|
function buildClientHash(localTime) {
|
|
530
|
-
|
|
729
|
+
return md5(localTime + HASH_SECRET);
|
|
531
730
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
731
|
+
/**
|
|
732
|
+
* Manages access tokens for the pixiv API.
|
|
733
|
+
*
|
|
734
|
+
* Holds the current access token and refresh token in memory.
|
|
735
|
+
* The refresh() method exchanges the refresh token for a new access token
|
|
736
|
+
* via the pixiv OAuth endpoint.
|
|
737
|
+
*/
|
|
738
|
+
var AuthManager = class AuthManager {
|
|
739
|
+
#accessToken;
|
|
740
|
+
#refreshToken;
|
|
741
|
+
userId;
|
|
742
|
+
constructor(credentials) {
|
|
743
|
+
this.#accessToken = credentials.accessToken;
|
|
744
|
+
this.#refreshToken = credentials.refreshToken;
|
|
745
|
+
this.userId = credentials.userId;
|
|
746
|
+
}
|
|
747
|
+
/** Returns the current access token. */
|
|
748
|
+
get accessToken() {
|
|
749
|
+
return this.#accessToken;
|
|
750
|
+
}
|
|
751
|
+
/** Returns the current refresh token. */
|
|
752
|
+
get refreshToken() {
|
|
753
|
+
return this.#refreshToken;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Exchanges the stored refresh token for a fresh access token.
|
|
757
|
+
*
|
|
758
|
+
* Updates the internal credentials on success.
|
|
759
|
+
* Throws if the token endpoint returns a non-200 response.
|
|
760
|
+
*/
|
|
761
|
+
async refresh() {
|
|
762
|
+
const localTime = (/* @__PURE__ */ new Date()).toISOString().replace(/Z$/, "+00:00");
|
|
763
|
+
const headers = {
|
|
764
|
+
"x-client-time": localTime,
|
|
765
|
+
"x-client-hash": buildClientHash(localTime),
|
|
766
|
+
"app-os": "ios",
|
|
767
|
+
"app-os-version": "16.4.1",
|
|
768
|
+
"user-agent": "PixivIOSApp/7.16.9 (iOS 16.4.1; iPad13,4)",
|
|
769
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
770
|
+
};
|
|
771
|
+
const body = new URLSearchParams({
|
|
772
|
+
client_id: CLIENT_ID,
|
|
773
|
+
client_secret: CLIENT_SECRET,
|
|
774
|
+
get_secure_url: "1",
|
|
775
|
+
grant_type: "refresh_token",
|
|
776
|
+
refresh_token: this.#refreshToken
|
|
777
|
+
}).toString();
|
|
778
|
+
const response = await fetch(AUTH_URL, {
|
|
779
|
+
method: "POST",
|
|
780
|
+
headers,
|
|
781
|
+
body
|
|
782
|
+
});
|
|
783
|
+
if (response.status !== 200) throw new Error(`Failed to refresh pixiv token: HTTP ${response.status}`);
|
|
784
|
+
const data = await response.json();
|
|
785
|
+
this.#accessToken = data.response.access_token;
|
|
786
|
+
this.#refreshToken = data.response.refresh_token;
|
|
787
|
+
this.userId = data.user.id;
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Creates an `AuthManager` by performing the initial token refresh.
|
|
791
|
+
*
|
|
792
|
+
* @param refreshToken - Pixiv refresh token
|
|
793
|
+
* @returns Initialized `AuthManager`
|
|
794
|
+
*/
|
|
795
|
+
static async login(refreshToken) {
|
|
796
|
+
const manager = new AuthManager({
|
|
797
|
+
userId: "",
|
|
798
|
+
accessToken: "",
|
|
799
|
+
refreshToken
|
|
800
|
+
});
|
|
801
|
+
await manager.refresh();
|
|
802
|
+
return manager;
|
|
803
|
+
}
|
|
602
804
|
};
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
Host: "app-api.pixiv.net",
|
|
609
|
-
"App-OS": "ios",
|
|
610
|
-
"App-OS-Version": "14.6",
|
|
611
|
-
"User-Agent": "PixivIOSApp/7.13.3 (iOS 14.6; iPhone13,2)",
|
|
612
|
-
"Accept-Language": "ja"
|
|
805
|
+
//#endregion
|
|
806
|
+
//#region src/http.ts
|
|
807
|
+
const DEFAULT_RETRY = {
|
|
808
|
+
maxRetries: 3,
|
|
809
|
+
waitMs: 1e4
|
|
613
810
|
};
|
|
811
|
+
const BASE_URL = "https://app-api.pixiv.net";
|
|
812
|
+
const DEFAULT_HEADERS = {
|
|
813
|
+
Host: "app-api.pixiv.net",
|
|
814
|
+
"App-OS": "ios",
|
|
815
|
+
"App-OS-Version": "14.6",
|
|
816
|
+
"User-Agent": "PixivIOSApp/7.13.3 (iOS 14.6; iPhone13,2)",
|
|
817
|
+
"Accept-Language": "ja"
|
|
818
|
+
};
|
|
819
|
+
/**
|
|
820
|
+
* Parses the `Retry-After` header value into milliseconds.
|
|
821
|
+
*
|
|
822
|
+
* Supports delay-seconds format and HTTP-date format.
|
|
823
|
+
* Falls back to `defaultMs` if the header is absent or unparseable.
|
|
824
|
+
*
|
|
825
|
+
* @param retryAfter - Header value (null if not present)
|
|
826
|
+
* @param defaultMs - Fallback wait time in milliseconds
|
|
827
|
+
* @returns Wait time in milliseconds (clamped to ≥ 0)
|
|
828
|
+
*/
|
|
614
829
|
function parseRetryAfter(retryAfter, defaultMs) {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
if (!Number.isNaN(retryDate)) {
|
|
621
|
-
return Math.max(0, retryDate - Date.now());
|
|
622
|
-
}
|
|
623
|
-
return defaultMs;
|
|
830
|
+
if (!retryAfter) return defaultMs;
|
|
831
|
+
if (/^\d+$/.test(retryAfter.trim())) return Number.parseInt(retryAfter, 10) * 1e3;
|
|
832
|
+
const retryDate = Date.parse(retryAfter);
|
|
833
|
+
if (!Number.isNaN(retryDate)) return Math.max(0, retryDate - Date.now());
|
|
834
|
+
return defaultMs;
|
|
624
835
|
}
|
|
625
836
|
function headersToRecord(headers) {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
}
|
|
630
|
-
return result;
|
|
837
|
+
const result = {};
|
|
838
|
+
for (const [key, value] of headers) result[key] = value;
|
|
839
|
+
return result;
|
|
631
840
|
}
|
|
841
|
+
/**
|
|
842
|
+
* HTTP client for the pixiv API.
|
|
843
|
+
*
|
|
844
|
+
* All methods return `ResultAsync<T, PixivError>` — no throws.
|
|
845
|
+
* A 429 → retry loop and a 401 → refresh → retry are handled internally.
|
|
846
|
+
*/
|
|
632
847
|
var HttpClient = class {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
method,
|
|
786
|
-
endpoint,
|
|
787
|
-
url: response.url || url,
|
|
788
|
-
requestHeaders: JSON.stringify(requestHeaders),
|
|
789
|
-
requestBody: body ?? null,
|
|
790
|
-
responseType: isJson ? "JSON" : "TEXT",
|
|
791
|
-
statusCode: response.status,
|
|
792
|
-
responseHeaders: JSON.stringify(responseHeaders),
|
|
793
|
-
responseBody: text
|
|
794
|
-
};
|
|
795
|
-
Promise.resolve(this.#interceptor(record)).catch(() => void 0);
|
|
796
|
-
}
|
|
797
|
-
return ok(httpResponse.data);
|
|
798
|
-
}
|
|
799
|
-
return err(rateLimitError(lastRetryAfterMs));
|
|
800
|
-
}
|
|
848
|
+
#auth;
|
|
849
|
+
#retry;
|
|
850
|
+
#interceptor;
|
|
851
|
+
constructor(auth, options) {
|
|
852
|
+
this.#auth = auth;
|
|
853
|
+
this.#retry = {
|
|
854
|
+
maxRetries: options?.retry?.maxRetries ?? DEFAULT_RETRY.maxRetries,
|
|
855
|
+
waitMs: options?.retry?.waitMs ?? DEFAULT_RETRY.waitMs
|
|
856
|
+
};
|
|
857
|
+
this.#interceptor = options?.onResponse;
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Sends a GET request to the pixiv API.
|
|
861
|
+
*
|
|
862
|
+
* @param path - API endpoint path (e.g. "/v1/illust/detail")
|
|
863
|
+
* @param params - Query parameters as a URLSearchParams instance
|
|
864
|
+
* @returns `ResultAsync<T, PixivError>`
|
|
865
|
+
*/
|
|
866
|
+
get(path, params) {
|
|
867
|
+
const url = `${BASE_URL}${path}${params ? `?${params.toString()}` : ""}`;
|
|
868
|
+
return this.#send(url, "GET", path, void 0);
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Sends a POST request to the pixiv API.
|
|
872
|
+
*
|
|
873
|
+
* @param path - API endpoint path (e.g. "/v2/illust/bookmark/add")
|
|
874
|
+
* @param body - URL-encoded request body string
|
|
875
|
+
* @returns `ResultAsync<T, PixivError>`
|
|
876
|
+
*/
|
|
877
|
+
post(path, body) {
|
|
878
|
+
const url = `${BASE_URL}${path}`;
|
|
879
|
+
return this.#send(url, "POST", path, body);
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Fetches a pixiv image URL without an Authorization header.
|
|
883
|
+
*
|
|
884
|
+
* Uses a browser User-Agent and the pixiv Referer, which are required for
|
|
885
|
+
* image CDN access. Retry and interceptor are not applied here.
|
|
886
|
+
*
|
|
887
|
+
* @param imageUrl - Full image URL
|
|
888
|
+
* @returns `ResultAsync<Response, PixivError>`
|
|
889
|
+
*/
|
|
890
|
+
fetchImage(imageUrl) {
|
|
891
|
+
return ResultAsync.fromPromise(fetch(imageUrl, { headers: {
|
|
892
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36",
|
|
893
|
+
Referer: "https://www.pixiv.net/"
|
|
894
|
+
} }), networkError).andThen((response) => {
|
|
895
|
+
if (!response.ok) return ResultAsync.fromResult(err(apiError(response.status, null)));
|
|
896
|
+
return ResultAsync.fromResult(ok(response));
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Sends a request to an absolute URL returned in a `next_url` field.
|
|
901
|
+
*
|
|
902
|
+
* Applies the same retry / interceptor / auth logic as `get()`.
|
|
903
|
+
*
|
|
904
|
+
* @param absoluteUrl - Full URL including query string
|
|
905
|
+
* @returns `ResultAsync<T, PixivError>`
|
|
906
|
+
*/
|
|
907
|
+
getAbsolute(absoluteUrl) {
|
|
908
|
+
let endpoint;
|
|
909
|
+
try {
|
|
910
|
+
endpoint = new URL(absoluteUrl).pathname;
|
|
911
|
+
} catch {
|
|
912
|
+
endpoint = absoluteUrl;
|
|
913
|
+
}
|
|
914
|
+
return this.#send(absoluteUrl, "GET", endpoint, void 0);
|
|
915
|
+
}
|
|
916
|
+
#send(url, method, endpoint, body) {
|
|
917
|
+
return new ResultAsync(this.#sendWithRetry(url, method, endpoint, body));
|
|
918
|
+
}
|
|
919
|
+
async #sendWithRetry(url, method, endpoint, body, allowRefresh = true) {
|
|
920
|
+
const maxRetries = Math.max(0, this.#retry.maxRetries);
|
|
921
|
+
const waitMs = Math.max(0, this.#retry.waitMs);
|
|
922
|
+
let lastRetryAfterMs = 0;
|
|
923
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
924
|
+
const requestHeaders = {
|
|
925
|
+
...DEFAULT_HEADERS,
|
|
926
|
+
Authorization: `Bearer ${this.#auth.accessToken}`,
|
|
927
|
+
...method === "POST" ? { "Content-Type": "application/x-www-form-urlencoded" } : {}
|
|
928
|
+
};
|
|
929
|
+
let response;
|
|
930
|
+
try {
|
|
931
|
+
response = await fetch(url, {
|
|
932
|
+
method,
|
|
933
|
+
headers: requestHeaders,
|
|
934
|
+
body: method === "POST" ? body : void 0
|
|
935
|
+
});
|
|
936
|
+
} catch (fetchError) {
|
|
937
|
+
return err(networkError(fetchError));
|
|
938
|
+
}
|
|
939
|
+
if (response.status === 429) {
|
|
940
|
+
await response.body?.cancel();
|
|
941
|
+
const retryAfterMs = parseRetryAfter(response.headers.get("Retry-After"), waitMs);
|
|
942
|
+
lastRetryAfterMs = retryAfterMs;
|
|
943
|
+
if (attempt < maxRetries) {
|
|
944
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfterMs));
|
|
945
|
+
continue;
|
|
946
|
+
}
|
|
947
|
+
return err(rateLimitError(lastRetryAfterMs));
|
|
948
|
+
}
|
|
949
|
+
if (response.status === 401) {
|
|
950
|
+
await response.body?.cancel();
|
|
951
|
+
if (allowRefresh) {
|
|
952
|
+
try {
|
|
953
|
+
await this.#auth.refresh();
|
|
954
|
+
} catch {
|
|
955
|
+
return err(authFailedError(401));
|
|
956
|
+
}
|
|
957
|
+
return this.#sendWithRetry(url, method, endpoint, body, false);
|
|
958
|
+
}
|
|
959
|
+
return err(authFailedError(401));
|
|
960
|
+
}
|
|
961
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
962
|
+
const text = await response.text();
|
|
963
|
+
let data;
|
|
964
|
+
const isJson = contentType.includes("application/json");
|
|
965
|
+
if (isJson) try {
|
|
966
|
+
data = camelizeKeys(JSON.parse(text));
|
|
967
|
+
} catch {
|
|
968
|
+
data = text;
|
|
969
|
+
}
|
|
970
|
+
else data = text;
|
|
971
|
+
const responseHeaders = headersToRecord(response.headers);
|
|
972
|
+
if (!response.ok) return err(apiError(response.status, data));
|
|
973
|
+
const httpResponse = {
|
|
974
|
+
data,
|
|
975
|
+
status: response.status,
|
|
976
|
+
headers: responseHeaders,
|
|
977
|
+
requestHeaders,
|
|
978
|
+
requestBody: body ?? null,
|
|
979
|
+
responseUrl: response.url || void 0,
|
|
980
|
+
endpoint
|
|
981
|
+
};
|
|
982
|
+
if (this.#interceptor) {
|
|
983
|
+
const record = {
|
|
984
|
+
method,
|
|
985
|
+
endpoint,
|
|
986
|
+
url: response.url || url,
|
|
987
|
+
requestHeaders: JSON.stringify(requestHeaders),
|
|
988
|
+
requestBody: body ?? null,
|
|
989
|
+
responseType: isJson ? "JSON" : "TEXT",
|
|
990
|
+
statusCode: response.status,
|
|
991
|
+
responseHeaders: JSON.stringify(responseHeaders),
|
|
992
|
+
responseBody: text
|
|
993
|
+
};
|
|
994
|
+
Promise.resolve(this.#interceptor(record)).catch(() => void 0);
|
|
995
|
+
}
|
|
996
|
+
return ok(httpResponse.data);
|
|
997
|
+
}
|
|
998
|
+
return err(rateLimitError(lastRetryAfterMs));
|
|
999
|
+
}
|
|
801
1000
|
};
|
|
802
|
-
|
|
803
|
-
|
|
1001
|
+
//#endregion
|
|
1002
|
+
//#region src/resources/illusts.ts
|
|
1003
|
+
/** Methods for the illust API namespace. */
|
|
804
1004
|
var IllustResource = class {
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
this.#http.get(
|
|
945
|
-
"/v1/illust/series",
|
|
946
|
-
buildParams({
|
|
947
|
-
illustSeriesId: params.illustSeriesId,
|
|
948
|
-
filter: params.filter ?? "for_ios"
|
|
949
|
-
})
|
|
950
|
-
),
|
|
951
|
-
this.#http,
|
|
952
|
-
(page) => page.illusts
|
|
953
|
-
);
|
|
954
|
-
}
|
|
955
|
-
/**
|
|
956
|
-
* Adds an illust bookmark.
|
|
957
|
-
* POST /v2/illust/bookmark/add
|
|
958
|
-
*
|
|
959
|
-
* @param params - Request parameters
|
|
960
|
-
*/
|
|
961
|
-
bookmarkAdd(params) {
|
|
962
|
-
const body = buildParams({
|
|
963
|
-
illustId: params.illustId,
|
|
964
|
-
restrict: params.restrict ?? "public",
|
|
965
|
-
...params.tags ? { tags: params.tags } : {}
|
|
966
|
-
});
|
|
967
|
-
return this.#http.post(
|
|
968
|
-
"/v2/illust/bookmark/add",
|
|
969
|
-
body.toString()
|
|
970
|
-
);
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
|
-
* Removes an illust bookmark.
|
|
974
|
-
* POST /v1/illust/bookmark/delete
|
|
975
|
-
*
|
|
976
|
-
* @param params - Request parameters
|
|
977
|
-
*/
|
|
978
|
-
bookmarkDelete(params) {
|
|
979
|
-
const body = buildParams({ illustId: String(params.illustId) });
|
|
980
|
-
return this.#http.post(
|
|
981
|
-
"/v1/illust/bookmark/delete",
|
|
982
|
-
body.toString()
|
|
983
|
-
);
|
|
984
|
-
}
|
|
1005
|
+
#http;
|
|
1006
|
+
constructor(http) {
|
|
1007
|
+
this.#http = http;
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Fetches a single illust by ID.
|
|
1011
|
+
* GET /v1/illust/detail
|
|
1012
|
+
*
|
|
1013
|
+
* @param params - Request parameters
|
|
1014
|
+
*
|
|
1015
|
+
* @example
|
|
1016
|
+
* ```ts
|
|
1017
|
+
* const result = await client.illusts.detail({ illustId: 12345 })
|
|
1018
|
+
* if (result.isOk) {
|
|
1019
|
+
* console.log(result.value.illust.title)
|
|
1020
|
+
* } else {
|
|
1021
|
+
* console.error(result.error)
|
|
1022
|
+
* }
|
|
1023
|
+
* ```
|
|
1024
|
+
*/
|
|
1025
|
+
detail(params) {
|
|
1026
|
+
return this.#http.get("/v1/illust/detail", buildParams({
|
|
1027
|
+
illustId: params.illustId,
|
|
1028
|
+
filter: params.filter ?? "for_ios"
|
|
1029
|
+
}));
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Fetches related illusts for a given illust.
|
|
1033
|
+
* GET /v2/illust/related
|
|
1034
|
+
*
|
|
1035
|
+
* @param params - Request parameters
|
|
1036
|
+
*/
|
|
1037
|
+
related(params) {
|
|
1038
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v2/illust/related", buildParams({
|
|
1039
|
+
illustId: params.illustId,
|
|
1040
|
+
filter: params.filter ?? "for_ios",
|
|
1041
|
+
...params.seedIllustIds ? { seedIllustIds: params.seedIllustIds } : {}
|
|
1042
|
+
})), this.#http, (page) => page.illusts);
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Searches for illusts.
|
|
1046
|
+
* GET /v1/search/illust
|
|
1047
|
+
*
|
|
1048
|
+
* @param params - Request parameters
|
|
1049
|
+
*
|
|
1050
|
+
* @example
|
|
1051
|
+
* ```ts
|
|
1052
|
+
* // Iterate all results across pages
|
|
1053
|
+
* for await (const illust of client.illusts.search({ word: 'cat' }).items()) {
|
|
1054
|
+
* console.log(illust.title)
|
|
1055
|
+
* }
|
|
1056
|
+
*
|
|
1057
|
+
* // Fetch only the first page
|
|
1058
|
+
* const page = await client.illusts.search({ word: 'cat' })
|
|
1059
|
+
* if (page.isOk) {
|
|
1060
|
+
* console.log(page.value.illusts.length)
|
|
1061
|
+
* }
|
|
1062
|
+
* ```
|
|
1063
|
+
*/
|
|
1064
|
+
search(params) {
|
|
1065
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/search/illust", buildParams({
|
|
1066
|
+
word: params.word,
|
|
1067
|
+
searchTarget: params.searchTarget ?? "partial_match_for_tags",
|
|
1068
|
+
sort: params.sort ?? "date_desc",
|
|
1069
|
+
filter: params.filter ?? "for_ios",
|
|
1070
|
+
duration: params.duration,
|
|
1071
|
+
startDate: params.startDate,
|
|
1072
|
+
endDate: params.endDate,
|
|
1073
|
+
searchAiType: params.searchAiType,
|
|
1074
|
+
offset: params.offset
|
|
1075
|
+
})), this.#http, (page) => page.illusts);
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Fetches the illust ranking.
|
|
1079
|
+
* GET /v1/illust/ranking
|
|
1080
|
+
*
|
|
1081
|
+
* @param params - Request parameters
|
|
1082
|
+
*/
|
|
1083
|
+
ranking(params = {}) {
|
|
1084
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/illust/ranking", buildParams({
|
|
1085
|
+
mode: params.mode ?? "day",
|
|
1086
|
+
filter: params.filter ?? "for_ios",
|
|
1087
|
+
date: params.date,
|
|
1088
|
+
offset: params.offset
|
|
1089
|
+
})), this.#http, (page) => page.illusts);
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Fetches recommended illusts.
|
|
1093
|
+
* GET /v1/illust/recommended
|
|
1094
|
+
*
|
|
1095
|
+
* @param params - Request parameters
|
|
1096
|
+
*/
|
|
1097
|
+
recommended(params = {}) {
|
|
1098
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/illust/recommended", buildParams({
|
|
1099
|
+
filter: params.filter ?? "for_ios",
|
|
1100
|
+
includeRankingLabel: true,
|
|
1101
|
+
includeRankingIllusts: true,
|
|
1102
|
+
includePrivacyPolicy: true,
|
|
1103
|
+
offset: params.offset,
|
|
1104
|
+
maxBookmarkIdForRecommend: params.maxBookmarkIdForRecommend,
|
|
1105
|
+
minBookmarkIdForRecentIllust: params.minBookmarkIdForRecentIllust
|
|
1106
|
+
})), this.#http, (page) => page.illusts);
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Fetches an illust series.
|
|
1110
|
+
* GET /v1/illust/series
|
|
1111
|
+
*
|
|
1112
|
+
* @param params - Request parameters
|
|
1113
|
+
*/
|
|
1114
|
+
series(params) {
|
|
1115
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/illust/series", buildParams({
|
|
1116
|
+
illustSeriesId: params.illustSeriesId,
|
|
1117
|
+
filter: params.filter ?? "for_ios"
|
|
1118
|
+
})), this.#http, (page) => page.illusts);
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Adds an illust bookmark.
|
|
1122
|
+
* POST /v2/illust/bookmark/add
|
|
1123
|
+
*
|
|
1124
|
+
* @param params - Request parameters
|
|
1125
|
+
*/
|
|
1126
|
+
bookmarkAdd(params) {
|
|
1127
|
+
const body = buildParams({
|
|
1128
|
+
illustId: params.illustId,
|
|
1129
|
+
restrict: params.restrict ?? "public",
|
|
1130
|
+
...params.tags ? { tags: params.tags } : {}
|
|
1131
|
+
});
|
|
1132
|
+
return this.#http.post("/v2/illust/bookmark/add", body.toString());
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Removes an illust bookmark.
|
|
1136
|
+
* POST /v1/illust/bookmark/delete
|
|
1137
|
+
*
|
|
1138
|
+
* @param params - Request parameters
|
|
1139
|
+
*/
|
|
1140
|
+
bookmarkDelete(params) {
|
|
1141
|
+
const body = buildParams({ illustId: String(params.illustId) });
|
|
1142
|
+
return this.#http.post("/v1/illust/bookmark/delete", body.toString());
|
|
1143
|
+
}
|
|
985
1144
|
};
|
|
986
|
-
|
|
987
|
-
|
|
1145
|
+
//#endregion
|
|
1146
|
+
//#region src/resources/novels.ts
|
|
1147
|
+
/** Methods for the novel API namespace. */
|
|
988
1148
|
var NovelResource = class {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
* Fetches a novel series.
|
|
1132
|
-
* GET /v2/novel/series
|
|
1133
|
-
*
|
|
1134
|
-
* @param params - Request parameters
|
|
1135
|
-
*/
|
|
1136
|
-
series(params) {
|
|
1137
|
-
return PaginatedResultAsync.fromResultAsync(
|
|
1138
|
-
this.#http.get(
|
|
1139
|
-
"/v2/novel/series",
|
|
1140
|
-
buildParams({ seriesId: params.seriesId, lastOrder: params.lastOrder })
|
|
1141
|
-
),
|
|
1142
|
-
this.#http,
|
|
1143
|
-
(page) => page.novels
|
|
1144
|
-
);
|
|
1145
|
-
}
|
|
1146
|
-
/**
|
|
1147
|
-
* Adds a novel bookmark.
|
|
1148
|
-
* POST /v2/novel/bookmark/add
|
|
1149
|
-
*
|
|
1150
|
-
* @param params - Request parameters
|
|
1151
|
-
*/
|
|
1152
|
-
bookmarkAdd(params) {
|
|
1153
|
-
const body = buildParams({
|
|
1154
|
-
novelId: params.novelId,
|
|
1155
|
-
restrict: params.restrict ?? "public",
|
|
1156
|
-
...params.tags ? { tags: params.tags } : {}
|
|
1157
|
-
});
|
|
1158
|
-
return this.#http.post(
|
|
1159
|
-
"/v2/novel/bookmark/add",
|
|
1160
|
-
body.toString()
|
|
1161
|
-
);
|
|
1162
|
-
}
|
|
1163
|
-
/**
|
|
1164
|
-
* Removes a novel bookmark.
|
|
1165
|
-
* POST /v1/novel/bookmark/delete
|
|
1166
|
-
*
|
|
1167
|
-
* @param params - Request parameters
|
|
1168
|
-
*/
|
|
1169
|
-
bookmarkDelete(params) {
|
|
1170
|
-
const body = buildParams({ novelId: String(params.novelId) });
|
|
1171
|
-
return this.#http.post(
|
|
1172
|
-
"/v1/novel/bookmark/delete",
|
|
1173
|
-
body.toString()
|
|
1174
|
-
);
|
|
1175
|
-
}
|
|
1149
|
+
#http;
|
|
1150
|
+
constructor(http) {
|
|
1151
|
+
this.#http = http;
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Fetches a single novel by ID.
|
|
1155
|
+
* GET /v2/novel/detail
|
|
1156
|
+
*
|
|
1157
|
+
* @param params - Request parameters
|
|
1158
|
+
*
|
|
1159
|
+
* @example
|
|
1160
|
+
* ```ts
|
|
1161
|
+
* const result = await client.novels.detail({ novelId: 67890 })
|
|
1162
|
+
* if (result.isOk) {
|
|
1163
|
+
* console.log(result.value.novel.title)
|
|
1164
|
+
* } else {
|
|
1165
|
+
* console.error(result.error)
|
|
1166
|
+
* }
|
|
1167
|
+
* ```
|
|
1168
|
+
*/
|
|
1169
|
+
detail(params) {
|
|
1170
|
+
return this.#http.get("/v2/novel/detail", buildParams({ novelId: params.novelId }));
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Fetches the WebView HTML for a novel.
|
|
1174
|
+
* GET /webview/v2/novel
|
|
1175
|
+
*
|
|
1176
|
+
* Returns the raw HTML page that the pixiv app renders in a WebView.
|
|
1177
|
+
* To extract the plain text, parse the returned HTML (e.g. strip tags).
|
|
1178
|
+
*
|
|
1179
|
+
* @param params - Request parameters
|
|
1180
|
+
*/
|
|
1181
|
+
text(params) {
|
|
1182
|
+
return this.#http.get("/webview/v2/novel", buildParams({ id: params.novelId }));
|
|
1183
|
+
}
|
|
1184
|
+
/**
|
|
1185
|
+
* Fetches related novels for a given novel.
|
|
1186
|
+
* GET /v1/novel/related
|
|
1187
|
+
*
|
|
1188
|
+
* @param params - Request parameters
|
|
1189
|
+
*/
|
|
1190
|
+
related(params) {
|
|
1191
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/novel/related", buildParams({ novelId: params.novelId })), this.#http, (page) => page.novels);
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Searches for novels.
|
|
1195
|
+
* GET /v1/search/novel
|
|
1196
|
+
*
|
|
1197
|
+
* @param params - Request parameters
|
|
1198
|
+
*
|
|
1199
|
+
* @example
|
|
1200
|
+
* ```ts
|
|
1201
|
+
* // Iterate all results across pages
|
|
1202
|
+
* for await (const novel of client.novels.search({ word: 'fantasy' }).items()) {
|
|
1203
|
+
* console.log(novel.title)
|
|
1204
|
+
* }
|
|
1205
|
+
*
|
|
1206
|
+
* // Fetch only the first page
|
|
1207
|
+
* const page = await client.novels.search({ word: 'fantasy' })
|
|
1208
|
+
* if (page.isOk) {
|
|
1209
|
+
* console.log(page.value.novels.length)
|
|
1210
|
+
* }
|
|
1211
|
+
* ```
|
|
1212
|
+
*/
|
|
1213
|
+
search(params) {
|
|
1214
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/search/novel", buildParams({
|
|
1215
|
+
word: params.word,
|
|
1216
|
+
searchTarget: params.searchTarget ?? "partial_match_for_tags",
|
|
1217
|
+
sort: params.sort ?? "date_desc",
|
|
1218
|
+
filter: params.filter ?? "for_ios",
|
|
1219
|
+
duration: params.duration,
|
|
1220
|
+
startDate: params.startDate,
|
|
1221
|
+
endDate: params.endDate,
|
|
1222
|
+
searchAiType: params.searchAiType,
|
|
1223
|
+
offset: params.offset
|
|
1224
|
+
})), this.#http, (page) => page.novels);
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Fetches the novel ranking.
|
|
1228
|
+
* GET /v1/novel/ranking
|
|
1229
|
+
*
|
|
1230
|
+
* @param params - Request parameters
|
|
1231
|
+
*/
|
|
1232
|
+
ranking(params = {}) {
|
|
1233
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/novel/ranking", buildParams({
|
|
1234
|
+
mode: params.mode ?? "day",
|
|
1235
|
+
filter: params.filter ?? "for_ios",
|
|
1236
|
+
date: params.date,
|
|
1237
|
+
offset: params.offset
|
|
1238
|
+
})), this.#http, (page) => page.novels);
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Fetches recommended novels.
|
|
1242
|
+
* GET /v1/novel/recommended
|
|
1243
|
+
*
|
|
1244
|
+
* @param params - Request parameters
|
|
1245
|
+
*/
|
|
1246
|
+
recommended(params = {}) {
|
|
1247
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/novel/recommended", buildParams({
|
|
1248
|
+
filter: params.filter ?? "for_ios",
|
|
1249
|
+
includeRankingNovels: true,
|
|
1250
|
+
includePrivacyPolicy: true,
|
|
1251
|
+
offset: params.offset,
|
|
1252
|
+
maxBookmarkIdForRecommend: params.maxBookmarkIdForRecommend
|
|
1253
|
+
})), this.#http, (page) => page.novels);
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* Fetches a novel series.
|
|
1257
|
+
* GET /v2/novel/series
|
|
1258
|
+
*
|
|
1259
|
+
* @param params - Request parameters
|
|
1260
|
+
*/
|
|
1261
|
+
series(params) {
|
|
1262
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v2/novel/series", buildParams({
|
|
1263
|
+
seriesId: params.seriesId,
|
|
1264
|
+
lastOrder: params.lastOrder
|
|
1265
|
+
})), this.#http, (page) => page.novels);
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Adds a novel bookmark.
|
|
1269
|
+
* POST /v2/novel/bookmark/add
|
|
1270
|
+
*
|
|
1271
|
+
* @param params - Request parameters
|
|
1272
|
+
*/
|
|
1273
|
+
bookmarkAdd(params) {
|
|
1274
|
+
const body = buildParams({
|
|
1275
|
+
novelId: params.novelId,
|
|
1276
|
+
restrict: params.restrict ?? "public",
|
|
1277
|
+
...params.tags ? { tags: params.tags } : {}
|
|
1278
|
+
});
|
|
1279
|
+
return this.#http.post("/v2/novel/bookmark/add", body.toString());
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Removes a novel bookmark.
|
|
1283
|
+
* POST /v1/novel/bookmark/delete
|
|
1284
|
+
*
|
|
1285
|
+
* @param params - Request parameters
|
|
1286
|
+
*/
|
|
1287
|
+
bookmarkDelete(params) {
|
|
1288
|
+
const body = buildParams({ novelId: String(params.novelId) });
|
|
1289
|
+
return this.#http.post("/v1/novel/bookmark/delete", body.toString());
|
|
1290
|
+
}
|
|
1176
1291
|
};
|
|
1177
|
-
|
|
1178
|
-
|
|
1292
|
+
//#endregion
|
|
1293
|
+
//#region src/resources/users.ts
|
|
1294
|
+
/** Methods for the user bookmarks sub-namespace. */
|
|
1179
1295
|
var UserBookmarksResource = class {
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
"/v1/user/bookmarks/novel",
|
|
1244
|
-
buildParams({
|
|
1245
|
-
userId: params.userId,
|
|
1246
|
-
restrict: params.restrict ?? "public",
|
|
1247
|
-
filter: params.filter ?? "for_ios",
|
|
1248
|
-
tag: params.tag,
|
|
1249
|
-
maxBookmarkId: params.maxBookmarkId,
|
|
1250
|
-
offset: params.offset
|
|
1251
|
-
})
|
|
1252
|
-
),
|
|
1253
|
-
this.#http,
|
|
1254
|
-
(page) => page.novels
|
|
1255
|
-
);
|
|
1256
|
-
}
|
|
1296
|
+
#http;
|
|
1297
|
+
constructor(http) {
|
|
1298
|
+
this.#http = http;
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Fetches a user's bookmarked illusts.
|
|
1302
|
+
* GET /v1/user/bookmarks/illust
|
|
1303
|
+
*
|
|
1304
|
+
* @param params - Request parameters
|
|
1305
|
+
*
|
|
1306
|
+
* @example
|
|
1307
|
+
* ```ts
|
|
1308
|
+
* // Iterate all bookmarked illusts across pages
|
|
1309
|
+
* for await (const illust of client.users.bookmarks.illusts({ userId: client.userId }).items()) {
|
|
1310
|
+
* console.log(illust.title)
|
|
1311
|
+
* }
|
|
1312
|
+
*
|
|
1313
|
+
* // Resume from a saved cursor
|
|
1314
|
+
* import { parseNextUrl } from '@book000/pixivts'
|
|
1315
|
+
* const page = await client.users.bookmarks.illusts({ userId: client.userId })
|
|
1316
|
+
* if (page.isOk && page.value.nextUrl) {
|
|
1317
|
+
* const cursor = parseNextUrl(page.value.nextUrl)
|
|
1318
|
+
* const next = await client.users.bookmarks.illusts({
|
|
1319
|
+
* userId: client.userId,
|
|
1320
|
+
* maxBookmarkId: cursor.maxBookmarkId,
|
|
1321
|
+
* })
|
|
1322
|
+
* }
|
|
1323
|
+
* ```
|
|
1324
|
+
*/
|
|
1325
|
+
illusts(params) {
|
|
1326
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/user/bookmarks/illust", buildParams({
|
|
1327
|
+
userId: params.userId,
|
|
1328
|
+
restrict: params.restrict ?? "public",
|
|
1329
|
+
filter: params.filter ?? "for_ios",
|
|
1330
|
+
tag: params.tag,
|
|
1331
|
+
maxBookmarkId: params.maxBookmarkId,
|
|
1332
|
+
offset: params.offset
|
|
1333
|
+
})), this.#http, (page) => page.illusts);
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Fetches a user's bookmarked novels.
|
|
1337
|
+
* GET /v1/user/bookmarks/novel
|
|
1338
|
+
*
|
|
1339
|
+
* @param params - Request parameters
|
|
1340
|
+
*
|
|
1341
|
+
* @example
|
|
1342
|
+
* ```ts
|
|
1343
|
+
* // Iterate all bookmarked novels across pages
|
|
1344
|
+
* for await (const novel of client.users.bookmarks.novels({ userId: client.userId }).items()) {
|
|
1345
|
+
* console.log(novel.title)
|
|
1346
|
+
* }
|
|
1347
|
+
* ```
|
|
1348
|
+
*/
|
|
1349
|
+
novels(params) {
|
|
1350
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/user/bookmarks/novel", buildParams({
|
|
1351
|
+
userId: params.userId,
|
|
1352
|
+
restrict: params.restrict ?? "public",
|
|
1353
|
+
filter: params.filter ?? "for_ios",
|
|
1354
|
+
tag: params.tag,
|
|
1355
|
+
maxBookmarkId: params.maxBookmarkId,
|
|
1356
|
+
offset: params.offset
|
|
1357
|
+
})), this.#http, (page) => page.novels);
|
|
1358
|
+
}
|
|
1257
1359
|
};
|
|
1360
|
+
/** Methods for the user API namespace. */
|
|
1258
1361
|
var UserResource = class {
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
* POST /v1/user/follow/add
|
|
1342
|
-
*
|
|
1343
|
-
* @param params - Request parameters
|
|
1344
|
-
*/
|
|
1345
|
-
followAdd(params) {
|
|
1346
|
-
const body = buildParams({
|
|
1347
|
-
userId: params.userId,
|
|
1348
|
-
restrict: params.restrict ?? "public"
|
|
1349
|
-
});
|
|
1350
|
-
return this.#http.post(
|
|
1351
|
-
"/v1/user/follow/add",
|
|
1352
|
-
body.toString()
|
|
1353
|
-
);
|
|
1354
|
-
}
|
|
1355
|
-
/**
|
|
1356
|
-
* Unfollows a user.
|
|
1357
|
-
* POST /v1/user/follow/delete
|
|
1358
|
-
*
|
|
1359
|
-
* @param params - Request parameters
|
|
1360
|
-
*/
|
|
1361
|
-
followDelete(params) {
|
|
1362
|
-
const body = buildParams({ userId: String(params.userId) });
|
|
1363
|
-
return this.#http.post(
|
|
1364
|
-
"/v1/user/follow/delete",
|
|
1365
|
-
body.toString()
|
|
1366
|
-
);
|
|
1367
|
-
}
|
|
1362
|
+
/** User bookmarks sub-namespace. */
|
|
1363
|
+
bookmarks;
|
|
1364
|
+
#http;
|
|
1365
|
+
constructor(http) {
|
|
1366
|
+
this.#http = http;
|
|
1367
|
+
this.bookmarks = new UserBookmarksResource(http);
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Fetches detailed profile information for a user.
|
|
1371
|
+
* GET /v1/user/detail
|
|
1372
|
+
*
|
|
1373
|
+
* @param params - Request parameters
|
|
1374
|
+
*/
|
|
1375
|
+
detail(params) {
|
|
1376
|
+
return this.#http.get("/v1/user/detail", buildParams({
|
|
1377
|
+
userId: params.userId,
|
|
1378
|
+
filter: params.filter ?? "for_ios"
|
|
1379
|
+
}));
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1382
|
+
* Fetches illusts posted by a user.
|
|
1383
|
+
* GET /v1/user/illusts
|
|
1384
|
+
*
|
|
1385
|
+
* @param params - Request parameters
|
|
1386
|
+
*/
|
|
1387
|
+
illusts(params) {
|
|
1388
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/user/illusts", buildParams({
|
|
1389
|
+
userId: params.userId,
|
|
1390
|
+
type: params.type,
|
|
1391
|
+
filter: params.filter ?? "for_ios",
|
|
1392
|
+
offset: params.offset
|
|
1393
|
+
})), this.#http, (page) => page.illusts);
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Fetches novels posted by a user.
|
|
1397
|
+
* GET /v1/user/novels
|
|
1398
|
+
*
|
|
1399
|
+
* @param params - Request parameters
|
|
1400
|
+
*/
|
|
1401
|
+
novels(params) {
|
|
1402
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/user/novels", buildParams({
|
|
1403
|
+
userId: params.userId,
|
|
1404
|
+
filter: params.filter ?? "for_ios",
|
|
1405
|
+
offset: params.offset
|
|
1406
|
+
})), this.#http, (page) => page.novels);
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Fetches the list of users that a user is following.
|
|
1410
|
+
* GET /v1/user/following
|
|
1411
|
+
*
|
|
1412
|
+
* @param params - Request parameters
|
|
1413
|
+
*/
|
|
1414
|
+
following(params) {
|
|
1415
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/user/following", buildParams({
|
|
1416
|
+
userId: params.userId,
|
|
1417
|
+
restrict: params.restrict ?? "public",
|
|
1418
|
+
offset: params.offset
|
|
1419
|
+
})), this.#http, (page) => page.userPreviews);
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Follows a user.
|
|
1423
|
+
* POST /v1/user/follow/add
|
|
1424
|
+
*
|
|
1425
|
+
* @param params - Request parameters
|
|
1426
|
+
*/
|
|
1427
|
+
followAdd(params) {
|
|
1428
|
+
const body = buildParams({
|
|
1429
|
+
userId: params.userId,
|
|
1430
|
+
restrict: params.restrict ?? "public"
|
|
1431
|
+
});
|
|
1432
|
+
return this.#http.post("/v1/user/follow/add", body.toString());
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Unfollows a user.
|
|
1436
|
+
* POST /v1/user/follow/delete
|
|
1437
|
+
*
|
|
1438
|
+
* @param params - Request parameters
|
|
1439
|
+
*/
|
|
1440
|
+
followDelete(params) {
|
|
1441
|
+
const body = buildParams({ userId: String(params.userId) });
|
|
1442
|
+
return this.#http.post("/v1/user/follow/delete", body.toString());
|
|
1443
|
+
}
|
|
1368
1444
|
};
|
|
1369
|
-
|
|
1370
|
-
|
|
1445
|
+
//#endregion
|
|
1446
|
+
//#region src/resources/manga.ts
|
|
1447
|
+
/** Methods for the manga API namespace. */
|
|
1371
1448
|
var MangaResource = class {
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
offset: params.offset
|
|
1391
|
-
})
|
|
1392
|
-
),
|
|
1393
|
-
this.#http,
|
|
1394
|
-
(page) => page.illusts
|
|
1395
|
-
);
|
|
1396
|
-
}
|
|
1449
|
+
#http;
|
|
1450
|
+
constructor(http) {
|
|
1451
|
+
this.#http = http;
|
|
1452
|
+
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Fetches recommended manga.
|
|
1455
|
+
* GET /v1/manga/recommended
|
|
1456
|
+
*
|
|
1457
|
+
* @param params - Request parameters
|
|
1458
|
+
*/
|
|
1459
|
+
recommended(params = {}) {
|
|
1460
|
+
return PaginatedResultAsync.fromResultAsync(this.#http.get("/v1/manga/recommended", buildParams({
|
|
1461
|
+
filter: params.filter ?? "for_ios",
|
|
1462
|
+
includeRankingIllusts: true,
|
|
1463
|
+
includePrivacyPolicy: true,
|
|
1464
|
+
offset: params.offset
|
|
1465
|
+
})), this.#http, (page) => page.illusts);
|
|
1466
|
+
}
|
|
1397
1467
|
};
|
|
1398
|
-
|
|
1399
|
-
|
|
1468
|
+
//#endregion
|
|
1469
|
+
//#region src/resources/ugoira.ts
|
|
1470
|
+
/** Methods for the ugoira API namespace. */
|
|
1400
1471
|
var UgoiraResource = class {
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
buildParams({ illustId: params.illustId })
|
|
1415
|
-
);
|
|
1416
|
-
}
|
|
1472
|
+
#http;
|
|
1473
|
+
constructor(http) {
|
|
1474
|
+
this.#http = http;
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Fetches ugoira metadata (ZIP URL and per-frame timings).
|
|
1478
|
+
* GET /v1/ugoira/metadata
|
|
1479
|
+
*
|
|
1480
|
+
* @param params - Request parameters
|
|
1481
|
+
*/
|
|
1482
|
+
metadata(params) {
|
|
1483
|
+
return this.#http.get("/v1/ugoira/metadata", buildParams({ illustId: params.illustId }));
|
|
1484
|
+
}
|
|
1417
1485
|
};
|
|
1418
|
-
|
|
1419
|
-
|
|
1486
|
+
//#endregion
|
|
1487
|
+
//#region src/resources/images.ts
|
|
1488
|
+
/** Methods for fetching pixiv images. */
|
|
1420
1489
|
var ImageResource = class {
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1490
|
+
#http;
|
|
1491
|
+
constructor(http) {
|
|
1492
|
+
this.#http = http;
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Fetches a pixiv image.
|
|
1496
|
+
*
|
|
1497
|
+
* Uses a browser User-Agent and Referer (required for pixiv CDN).
|
|
1498
|
+
* No Authorization header is sent.
|
|
1499
|
+
*
|
|
1500
|
+
* @param imageUrl - Full CDN image URL
|
|
1501
|
+
*/
|
|
1502
|
+
fetch(imageUrl) {
|
|
1503
|
+
return this.#http.fetchImage(imageUrl);
|
|
1504
|
+
}
|
|
1436
1505
|
};
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1506
|
+
//#endregion
|
|
1507
|
+
//#region src/client.ts
|
|
1508
|
+
/**
|
|
1509
|
+
* PixivClient — main entry point for the @book000/pixivts library.
|
|
1510
|
+
*
|
|
1511
|
+
* @example
|
|
1512
|
+
* ```ts
|
|
1513
|
+
* const client = await PixivClient.of(process.env.PIXIV_REFRESH_TOKEN!)
|
|
1514
|
+
* const result = await client.illusts.detail({ illustId: 12345 })
|
|
1515
|
+
* if (result.isOk) console.log(result.value.illust.title)
|
|
1516
|
+
* ```
|
|
1517
|
+
*/
|
|
1518
|
+
/**
|
|
1519
|
+
* Main client for the pixiv API.
|
|
1520
|
+
*
|
|
1521
|
+
* Create an instance via {@link PixivClient.of} — the constructor is private
|
|
1522
|
+
* because initialisation requires an async token refresh.
|
|
1523
|
+
*/
|
|
1524
|
+
var PixivClient = class PixivClient {
|
|
1525
|
+
/** Illust API namespace. */
|
|
1526
|
+
illusts;
|
|
1527
|
+
/** Novel API namespace. */
|
|
1528
|
+
novels;
|
|
1529
|
+
/** User API namespace. */
|
|
1530
|
+
users;
|
|
1531
|
+
/** Manga API namespace. */
|
|
1532
|
+
manga;
|
|
1533
|
+
/** Ugoira API namespace. */
|
|
1534
|
+
ugoira;
|
|
1535
|
+
/** Image fetch helpers. */
|
|
1536
|
+
images;
|
|
1537
|
+
#auth;
|
|
1538
|
+
constructor(auth, http) {
|
|
1539
|
+
this.#auth = auth;
|
|
1540
|
+
this.illusts = new IllustResource(http);
|
|
1541
|
+
this.novels = new NovelResource(http);
|
|
1542
|
+
this.users = new UserResource(http);
|
|
1543
|
+
this.manga = new MangaResource(http);
|
|
1544
|
+
this.ugoira = new UgoiraResource(http);
|
|
1545
|
+
this.images = new ImageResource(http);
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Numeric user ID of the authenticated account.
|
|
1549
|
+
*
|
|
1550
|
+
* Available immediately after {@link PixivClient.of} resolves.
|
|
1551
|
+
* The pixiv OAuth endpoint returns the ID as a string; this getter
|
|
1552
|
+
* normalises it to `number` for consistency with resource method params
|
|
1553
|
+
* (e.g. `UserBookmarksIllustParams.userId`).
|
|
1554
|
+
*
|
|
1555
|
+
* @example
|
|
1556
|
+
* ```ts
|
|
1557
|
+
* const client = await PixivClient.of(refreshToken)
|
|
1558
|
+
* const bookmarks = await client.users.bookmarks.illusts({ userId: client.userId })
|
|
1559
|
+
* ```
|
|
1560
|
+
*/
|
|
1561
|
+
get userId() {
|
|
1562
|
+
const id = Number(this.#auth.userId);
|
|
1563
|
+
if (Number.isNaN(id)) throw new TypeError(`Invalid userId: "${this.#auth.userId}"`);
|
|
1564
|
+
return id;
|
|
1565
|
+
}
|
|
1566
|
+
/**
|
|
1567
|
+
* Returns the current OAuth access token.
|
|
1568
|
+
*
|
|
1569
|
+
* The access token is short-lived and changes after each call to
|
|
1570
|
+
* {@link PixivClient.of} and after each automatic token refresh triggered
|
|
1571
|
+
* by a 401 response.
|
|
1572
|
+
*
|
|
1573
|
+
* @returns The current bearer access token string
|
|
1574
|
+
*/
|
|
1575
|
+
getAccessToken() {
|
|
1576
|
+
return this.#auth.accessToken;
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Returns the current OAuth refresh token.
|
|
1580
|
+
*
|
|
1581
|
+
* The refresh token is long-lived and is used to obtain new access tokens.
|
|
1582
|
+
* It may rotate after a successful token refresh.
|
|
1583
|
+
*
|
|
1584
|
+
* @returns The current refresh token string
|
|
1585
|
+
*/
|
|
1586
|
+
getRefreshToken() {
|
|
1587
|
+
return this.#auth.refreshToken;
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1590
|
+
* Creates a PixivClient by refreshing the given token.
|
|
1591
|
+
*
|
|
1592
|
+
* @param refreshToken - Pixiv refresh token
|
|
1593
|
+
* @param options - Optional retry and response interceptor configuration
|
|
1594
|
+
* @returns A fully initialised {@link PixivClient}
|
|
1595
|
+
*/
|
|
1596
|
+
static async of(refreshToken, options) {
|
|
1597
|
+
const auth = await AuthManager.login(refreshToken);
|
|
1598
|
+
return new PixivClient(auth, new HttpClient(auth, options));
|
|
1599
|
+
}
|
|
1518
1600
|
};
|
|
1519
|
-
|
|
1601
|
+
//#endregion
|
|
1520
1602
|
export { BookmarkRestrict, FollowRestrict, NovelRankingMode, OSFilter, PaginatedResultAsync, PixivClient, PixivFetchError, RankingMode, ResultAsync, SearchDuration, SearchSort, SearchTarget, UserIllustType, apiError, authFailedError, err, failedPaginated, networkError, ok, parseNextUrl, rateLimitError };
|
|
1603
|
+
|
|
1604
|
+
//# sourceMappingURL=index.js.map
|