@book000/pixivts 0.55.0 → 0.56.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.
Files changed (204) hide show
  1. package/README.md +115 -19
  2. package/dist/index.cjs +1317 -0
  3. package/dist/index.d.cts +1659 -0
  4. package/dist/index.d.ts +1659 -45
  5. package/dist/index.js +1304 -65
  6. package/package.json +35 -72
  7. package/dist/checks.d.ts +0 -19
  8. package/dist/checks.d.ts.map +0 -1
  9. package/dist/checks.js +0 -85
  10. package/dist/checks.js.map +0 -1
  11. package/dist/checks.test.d.ts +0 -2
  12. package/dist/checks.test.d.ts.map +0 -1
  13. package/dist/checks.test.js +0 -306
  14. package/dist/checks.test.js.map +0 -1
  15. package/dist/http-client.d.ts +0 -66
  16. package/dist/http-client.d.ts.map +0 -1
  17. package/dist/http-client.js +0 -138
  18. package/dist/http-client.js.map +0 -1
  19. package/dist/http-client.test.d.ts +0 -2
  20. package/dist/http-client.test.d.ts.map +0 -1
  21. package/dist/http-client.test.js +0 -149
  22. package/dist/http-client.test.js.map +0 -1
  23. package/dist/index.d.ts.map +0 -1
  24. package/dist/index.js.map +0 -1
  25. package/dist/options.d.ts +0 -317
  26. package/dist/options.d.ts.map +0 -1
  27. package/dist/options.js +0 -222
  28. package/dist/options.js.map +0 -1
  29. package/dist/options.test.d.ts +0 -2
  30. package/dist/options.test.d.ts.map +0 -1
  31. package/dist/options.test.js +0 -117
  32. package/dist/options.test.js.map +0 -1
  33. package/dist/pixiv.d.ts +0 -317
  34. package/dist/pixiv.d.ts.map +0 -1
  35. package/dist/pixiv.js +0 -810
  36. package/dist/pixiv.js.map +0 -1
  37. package/dist/pixiv.test.d.ts +0 -2
  38. package/dist/pixiv.test.d.ts.map +0 -1
  39. package/dist/pixiv.test.js +0 -883
  40. package/dist/pixiv.test.js.map +0 -1
  41. package/dist/saving-responses/index.d.ts +0 -130
  42. package/dist/saving-responses/index.d.ts.map +0 -1
  43. package/dist/saving-responses/index.js +0 -263
  44. package/dist/saving-responses/index.js.map +0 -1
  45. package/dist/saving-responses/index.test.d.ts +0 -2
  46. package/dist/saving-responses/index.test.d.ts.map +0 -1
  47. package/dist/saving-responses/index.test.js +0 -468
  48. package/dist/saving-responses/index.test.js.map +0 -1
  49. package/dist/saving-responses/response-entity.d.ts +0 -16
  50. package/dist/saving-responses/response-entity.d.ts.map +0 -1
  51. package/dist/saving-responses/response-entity.js +0 -101
  52. package/dist/saving-responses/response-entity.js.map +0 -1
  53. package/dist/types/endpoints/v1/illust/bookmark/delete.d.ts +0 -14
  54. package/dist/types/endpoints/v1/illust/bookmark/delete.d.ts.map +0 -1
  55. package/dist/types/endpoints/v1/illust/bookmark/delete.js +0 -3
  56. package/dist/types/endpoints/v1/illust/bookmark/delete.js.map +0 -1
  57. package/dist/types/endpoints/v1/illust/detail.d.ts +0 -25
  58. package/dist/types/endpoints/v1/illust/detail.d.ts.map +0 -1
  59. package/dist/types/endpoints/v1/illust/detail.js +0 -20
  60. package/dist/types/endpoints/v1/illust/detail.js.map +0 -1
  61. package/dist/types/endpoints/v1/illust/ranking.d.ts +0 -51
  62. package/dist/types/endpoints/v1/illust/ranking.d.ts.map +0 -1
  63. package/dist/types/endpoints/v1/illust/ranking.js +0 -40
  64. package/dist/types/endpoints/v1/illust/ranking.js.map +0 -1
  65. package/dist/types/endpoints/v1/illust/recommended.d.ts +0 -104
  66. package/dist/types/endpoints/v1/illust/recommended.d.ts.map +0 -1
  67. package/dist/types/endpoints/v1/illust/recommended.js +0 -44
  68. package/dist/types/endpoints/v1/illust/recommended.js.map +0 -1
  69. package/dist/types/endpoints/v1/illust/series.d.ts +0 -45
  70. package/dist/types/endpoints/v1/illust/series.d.ts.map +0 -1
  71. package/dist/types/endpoints/v1/illust/series.js +0 -31
  72. package/dist/types/endpoints/v1/illust/series.js.map +0 -1
  73. package/dist/types/endpoints/v1/illust/ugoira/metadata.d.ts +0 -25
  74. package/dist/types/endpoints/v1/illust/ugoira/metadata.d.ts.map +0 -1
  75. package/dist/types/endpoints/v1/illust/ugoira/metadata.js +0 -20
  76. package/dist/types/endpoints/v1/illust/ugoira/metadata.js.map +0 -1
  77. package/dist/types/endpoints/v1/manga/recommended.d.ts +0 -72
  78. package/dist/types/endpoints/v1/manga/recommended.d.ts.map +0 -1
  79. package/dist/types/endpoints/v1/manga/recommended.js +0 -33
  80. package/dist/types/endpoints/v1/manga/recommended.js.map +0 -1
  81. package/dist/types/endpoints/v1/novel/bookmark/delete.d.ts +0 -14
  82. package/dist/types/endpoints/v1/novel/bookmark/delete.d.ts.map +0 -1
  83. package/dist/types/endpoints/v1/novel/bookmark/delete.js +0 -3
  84. package/dist/types/endpoints/v1/novel/bookmark/delete.js.map +0 -1
  85. package/dist/types/endpoints/v1/novel/ranking.d.ts +0 -47
  86. package/dist/types/endpoints/v1/novel/ranking.d.ts.map +0 -1
  87. package/dist/types/endpoints/v1/novel/ranking.js +0 -39
  88. package/dist/types/endpoints/v1/novel/ranking.js.map +0 -1
  89. package/dist/types/endpoints/v1/novel/recommended.d.ts +0 -72
  90. package/dist/types/endpoints/v1/novel/recommended.d.ts.map +0 -1
  91. package/dist/types/endpoints/v1/novel/recommended.js +0 -32
  92. package/dist/types/endpoints/v1/novel/recommended.js.map +0 -1
  93. package/dist/types/endpoints/v1/novel/related.d.ts +0 -37
  94. package/dist/types/endpoints/v1/novel/related.d.ts.map +0 -1
  95. package/dist/types/endpoints/v1/novel/related.js +0 -24
  96. package/dist/types/endpoints/v1/novel/related.js.map +0 -1
  97. package/dist/types/endpoints/v1/search/illust.d.ts +0 -109
  98. package/dist/types/endpoints/v1/search/illust.d.ts.map +0 -1
  99. package/dist/types/endpoints/v1/search/illust.js +0 -40
  100. package/dist/types/endpoints/v1/search/illust.js.map +0 -1
  101. package/dist/types/endpoints/v1/search/novel.d.ts +0 -103
  102. package/dist/types/endpoints/v1/search/novel.d.ts.map +0 -1
  103. package/dist/types/endpoints/v1/search/novel.js +0 -38
  104. package/dist/types/endpoints/v1/search/novel.js.map +0 -1
  105. package/dist/types/endpoints/v1/user/bookmarks/illust.d.ts +0 -48
  106. package/dist/types/endpoints/v1/user/bookmarks/illust.d.ts.map +0 -1
  107. package/dist/types/endpoints/v1/user/bookmarks/illust.js +0 -31
  108. package/dist/types/endpoints/v1/user/bookmarks/illust.js.map +0 -1
  109. package/dist/types/endpoints/v1/user/bookmarks/novel.d.ts +0 -44
  110. package/dist/types/endpoints/v1/user/bookmarks/novel.d.ts.map +0 -1
  111. package/dist/types/endpoints/v1/user/bookmarks/novel.js +0 -28
  112. package/dist/types/endpoints/v1/user/bookmarks/novel.js.map +0 -1
  113. package/dist/types/endpoints/v1/user/detail.d.ts +0 -44
  114. package/dist/types/endpoints/v1/user/detail.d.ts.map +0 -1
  115. package/dist/types/endpoints/v1/user/detail.js +0 -26
  116. package/dist/types/endpoints/v1/user/detail.js.map +0 -1
  117. package/dist/types/endpoints/v1/user/follow/add.d.ts +0 -24
  118. package/dist/types/endpoints/v1/user/follow/add.d.ts.map +0 -1
  119. package/dist/types/endpoints/v1/user/follow/add.js +0 -3
  120. package/dist/types/endpoints/v1/user/follow/add.js.map +0 -1
  121. package/dist/types/endpoints/v1/user/follow/delete.d.ts +0 -14
  122. package/dist/types/endpoints/v1/user/follow/delete.d.ts.map +0 -1
  123. package/dist/types/endpoints/v1/user/follow/delete.js +0 -3
  124. package/dist/types/endpoints/v1/user/follow/delete.js.map +0 -1
  125. package/dist/types/endpoints/v1/user/following.d.ts +0 -38
  126. package/dist/types/endpoints/v1/user/following.d.ts.map +0 -1
  127. package/dist/types/endpoints/v1/user/following.js +0 -26
  128. package/dist/types/endpoints/v1/user/following.js.map +0 -1
  129. package/dist/types/endpoints/v1/user/illusts.d.ts +0 -51
  130. package/dist/types/endpoints/v1/user/illusts.d.ts.map +0 -1
  131. package/dist/types/endpoints/v1/user/illusts.js +0 -31
  132. package/dist/types/endpoints/v1/user/illusts.js.map +0 -1
  133. package/dist/types/endpoints/v1/user/novels.d.ts +0 -43
  134. package/dist/types/endpoints/v1/user/novels.d.ts.map +0 -1
  135. package/dist/types/endpoints/v1/user/novels.js +0 -29
  136. package/dist/types/endpoints/v1/user/novels.js.map +0 -1
  137. package/dist/types/endpoints/v2/illust/bookmark/add.d.ts +0 -28
  138. package/dist/types/endpoints/v2/illust/bookmark/add.d.ts.map +0 -1
  139. package/dist/types/endpoints/v2/illust/bookmark/add.js +0 -3
  140. package/dist/types/endpoints/v2/illust/bookmark/add.js.map +0 -1
  141. package/dist/types/endpoints/v2/illust/related.d.ts +0 -48
  142. package/dist/types/endpoints/v2/illust/related.d.ts.map +0 -1
  143. package/dist/types/endpoints/v2/illust/related.js +0 -32
  144. package/dist/types/endpoints/v2/illust/related.js.map +0 -1
  145. package/dist/types/endpoints/v2/novel/bookmark/add.d.ts +0 -27
  146. package/dist/types/endpoints/v2/novel/bookmark/add.d.ts.map +0 -1
  147. package/dist/types/endpoints/v2/novel/bookmark/add.js +0 -3
  148. package/dist/types/endpoints/v2/novel/bookmark/add.js.map +0 -1
  149. package/dist/types/endpoints/v2/novel/detail.d.ts +0 -25
  150. package/dist/types/endpoints/v2/novel/detail.d.ts.map +0 -1
  151. package/dist/types/endpoints/v2/novel/detail.js +0 -20
  152. package/dist/types/endpoints/v2/novel/detail.js.map +0 -1
  153. package/dist/types/endpoints/v2/novel/series.d.ts +0 -49
  154. package/dist/types/endpoints/v2/novel/series.d.ts.map +0 -1
  155. package/dist/types/endpoints/v2/novel/series.js +0 -31
  156. package/dist/types/endpoints/v2/novel/series.js.map +0 -1
  157. package/dist/types/endpoints/webview/v2/novel.d.ts +0 -21
  158. package/dist/types/endpoints/webview/v2/novel.d.ts.map +0 -1
  159. package/dist/types/endpoints/webview/v2/novel.js +0 -18
  160. package/dist/types/endpoints/webview/v2/novel.js.map +0 -1
  161. package/dist/types/error-response.d.ts +0 -31
  162. package/dist/types/error-response.d.ts.map +0 -1
  163. package/dist/types/error-response.js +0 -3
  164. package/dist/types/error-response.js.map +0 -1
  165. package/dist/types/errors.d.ts +0 -7
  166. package/dist/types/errors.d.ts.map +0 -1
  167. package/dist/types/errors.js +0 -14
  168. package/dist/types/errors.js.map +0 -1
  169. package/dist/types/pixiv-common.d.ts +0 -103
  170. package/dist/types/pixiv-common.d.ts.map +0 -1
  171. package/dist/types/pixiv-common.js +0 -68
  172. package/dist/types/pixiv-common.js.map +0 -1
  173. package/dist/types/pixiv-illust-series.d.ts +0 -56
  174. package/dist/types/pixiv-illust-series.d.ts.map +0 -1
  175. package/dist/types/pixiv-illust-series.js +0 -26
  176. package/dist/types/pixiv-illust-series.js.map +0 -1
  177. package/dist/types/pixiv-illust.d.ts +0 -180
  178. package/dist/types/pixiv-illust.d.ts.map +0 -1
  179. package/dist/types/pixiv-illust.js +0 -56
  180. package/dist/types/pixiv-illust.js.map +0 -1
  181. package/dist/types/pixiv-novel-series.d.ts +0 -84
  182. package/dist/types/pixiv-novel-series.d.ts.map +0 -1
  183. package/dist/types/pixiv-novel-series.js +0 -43
  184. package/dist/types/pixiv-novel-series.js.map +0 -1
  185. package/dist/types/pixiv-novel.d.ts +0 -131
  186. package/dist/types/pixiv-novel.d.ts.map +0 -1
  187. package/dist/types/pixiv-novel.js +0 -44
  188. package/dist/types/pixiv-novel.js.map +0 -1
  189. package/dist/types/pixiv-ugoira.d.ts +0 -50
  190. package/dist/types/pixiv-ugoira.d.ts.map +0 -1
  191. package/dist/types/pixiv-ugoira.js +0 -34
  192. package/dist/types/pixiv-ugoira.js.map +0 -1
  193. package/dist/types/pixiv-user.d.ts +0 -212
  194. package/dist/types/pixiv-user.d.ts.map +0 -1
  195. package/dist/types/pixiv-user.js +0 -118
  196. package/dist/types/pixiv-user.js.map +0 -1
  197. package/dist/utils.d.ts +0 -3
  198. package/dist/utils.d.ts.map +0 -1
  199. package/dist/utils.js +0 -15
  200. package/dist/utils.js.map +0 -1
  201. package/dist/utils.test.d.ts +0 -2
  202. package/dist/utils.test.d.ts.map +0 -1
  203. package/dist/utils.test.js +0 -15
  204. package/dist/utils.test.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,66 +1,1305 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
1
+ // src/result.ts
2
+ var OkResultImpl = class _OkResultImpl {
3
+ constructor(value) {
4
+ this.value = value;
5
+ }
6
+ isOk = true;
7
+ isErr = false;
8
+ map(fn) {
9
+ return new _OkResultImpl(fn(this.value));
10
+ }
11
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters, @typescript-eslint/no-unused-vars -- F is part of the public API contract; _fn is intentionally unused (OkResult.mapErr is a no-op)
12
+ mapErr(_fn) {
13
+ return this;
14
+ }
15
+ andThen(fn) {
16
+ return fn(this.value);
17
+ }
18
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- _onErr is intentionally unused: OkResult.match always calls onOk
19
+ match(onOk, _onErr) {
20
+ return onOk(this.value);
21
+ }
22
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- _fallback is intentionally unused: OkResult.unwrapOr always returns value
23
+ unwrapOr(_fallback) {
24
+ return this.value;
25
+ }
26
+ };
27
+ var ErrResultImpl = class _ErrResultImpl {
28
+ // eslint-disable-next-line n/handle-callback-err -- 'error' is a stored value, not a Node.js callback error parameter
29
+ constructor(error) {
30
+ this.error = error;
31
+ }
32
+ isOk = false;
33
+ isErr = true;
34
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters, @typescript-eslint/no-unused-vars -- U is part of the public API contract; _fn is intentionally unused (ErrResult.map is a no-op)
35
+ map(_fn) {
36
+ return this;
37
+ }
38
+ mapErr(fn) {
39
+ return new _ErrResultImpl(fn(this.error));
40
+ }
41
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- _fn is intentionally unused: ErrResult.andThen is a no-op (the success path does not apply)
42
+ andThen(_fn) {
43
+ return this;
44
+ }
45
+ match(_onOk, onErr) {
46
+ return onErr(this.error);
47
+ }
48
+ unwrapOr(fallback) {
49
+ return fallback;
50
+ }
51
+ };
52
+ function ok(value) {
53
+ return new OkResultImpl(value);
54
+ }
55
+ function err(error) {
56
+ return new ErrResultImpl(error);
57
+ }
58
+ var ResultAsync = class _ResultAsync {
59
+ _promise;
60
+ constructor(promise) {
61
+ this._promise = promise;
62
+ }
63
+ // PromiseLike contract — makes `await resultAsync` work
64
+ // eslint-disable-next-line unicorn/no-thenable -- ResultAsync intentionally implements PromiseLike to be directly awaitable
65
+ then(onfulfilled, onrejected) {
66
+ return this._promise.then(onfulfilled, onrejected);
67
+ }
68
+ /**
69
+ * Wraps a `Promise<T>` into a `ResultAsync<T, E>`.
70
+ *
71
+ * If the promise rejects, `onError` maps the rejection reason to `E`.
72
+ *
73
+ * @param promise - The promise to wrap
74
+ * @param onError - Error mapper
75
+ */
76
+ static fromPromise(promise, onError) {
77
+ return new _ResultAsync(
78
+ promise.then(
79
+ (v) => ok(v),
80
+ (error) => err(onError(error))
81
+ )
82
+ );
83
+ }
84
+ /**
85
+ * Wraps an already-resolved `Result<T, E>` into a `ResultAsync<T, E>`.
86
+ *
87
+ * @param result - The result to wrap
88
+ */
89
+ static fromResult(result) {
90
+ return new _ResultAsync(Promise.resolve(result));
91
+ }
92
+ /**
93
+ * Transforms the success value.
94
+ *
95
+ * If the inner result is `Err`, `fn` is not called.
96
+ *
97
+ * @param fn - Synchronous mapper
98
+ */
99
+ map(fn) {
100
+ return new _ResultAsync(
101
+ // eslint-disable-next-line unicorn/no-array-callback-reference -- r.map(fn) is safe here; fn is a user-supplied mapper, not a DOM/Array method reference
102
+ this._promise.then((r) => r.map(fn))
103
+ );
104
+ }
105
+ /**
106
+ * Transforms the error value.
107
+ *
108
+ * If the inner result is `Ok`, `fn` is not called.
109
+ *
110
+ * @param fn - Synchronous error mapper
111
+ */
112
+ mapErr(fn) {
113
+ return new _ResultAsync(
114
+ this._promise.then((r) => r.mapErr(fn))
115
+ );
116
+ }
117
+ /**
118
+ * Chains another async operation that may fail.
119
+ *
120
+ * If the inner result is `Err`, `fn` is not called.
121
+ *
122
+ * @param fn - Async mapper that returns a `ResultAsync<U, F>`
123
+ */
124
+ andThen(fn) {
125
+ return new _ResultAsync(
126
+ this._promise.then(async (r) => {
127
+ if (r.isErr) return r;
128
+ const next = fn(r.value);
129
+ if (next instanceof _ResultAsync) {
130
+ return next._promise;
131
+ }
132
+ return next;
133
+ })
134
+ );
135
+ }
136
+ /**
137
+ * Pattern-matches on success / failure.
138
+ *
139
+ * @param onOk - Called with the success value
140
+ * @param onErr - Called with the error value
141
+ * @returns A `Promise<U>`
142
+ */
143
+ async match(onOk, onErr) {
144
+ const r = await this._promise;
145
+ if (r.isOk) return onOk(r.value);
146
+ return onErr(r.error);
147
+ }
148
+ /**
149
+ * Returns the success value, or `fallback` if the result is `Err`.
150
+ *
151
+ * @param fallback - The fallback value
152
+ */
153
+ async unwrapOr(fallback) {
154
+ const r = await this._promise;
155
+ if (r.isOk) return r.value;
156
+ return fallback;
157
+ }
158
+ };
159
+
160
+ // src/errors.ts
161
+ var PixivFetchError = class extends Error {
162
+ /** The underlying structured `PixivError`. */
163
+ pixivError;
164
+ constructor(pixivError) {
165
+ super(`pixiv API error: ${pixivError.type}`);
166
+ this.name = "PixivFetchError";
167
+ this.pixivError = pixivError;
168
+ Object.assign(this, pixivError);
169
+ }
170
+ };
171
+ function rateLimitError(retryAfter) {
172
+ return { type: "rate_limit", retryAfter };
173
+ }
174
+ function authFailedError(status) {
175
+ return { type: "auth_failed", status };
176
+ }
177
+ function networkError(cause) {
178
+ return { type: "network", cause };
179
+ }
180
+ function apiError(status, body) {
181
+ return { type: "api_error", status, body };
182
+ }
183
+
184
+ // src/paginated.ts
185
+ var PaginatedResultAsync = class _PaginatedResultAsync extends ResultAsync {
186
+ #http;
187
+ #getItems;
188
+ constructor(promise, http, getItems) {
189
+ super(promise);
190
+ this.#http = http;
191
+ this.#getItems = getItems;
192
+ }
193
+ /**
194
+ * Creates a `PaginatedResultAsync` from a `ResultAsync`.
195
+ *
196
+ * @param inner - The first-page result
197
+ * @param http - HTTP client for fetching subsequent pages
198
+ * @param getItems - Extracts item array from a page
199
+ */
200
+ static fromResultAsync(inner, http, getItems) {
201
+ const promise = Promise.resolve(inner);
202
+ return new _PaginatedResultAsync(promise, http, getItems);
203
+ }
204
+ /**
205
+ * Async generator that yields each page starting from the first.
206
+ *
207
+ * If any page fetch fails, the generator throws a `PixivFetchError`.
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * for await (const page of client.illusts.search({ word: 'cat' }).pages()) {
212
+ * console.log(page.illusts.length)
213
+ * }
214
+ * ```
215
+ */
216
+ async *pages() {
217
+ const first = await Promise.resolve(this);
218
+ if (first.isErr) throw new PixivFetchError(first.error);
219
+ yield first.value;
220
+ let nextUrl = first.value.next_url;
221
+ while (nextUrl !== null) {
222
+ const pageResult = await this.#http.getAbsolute(nextUrl);
223
+ if (pageResult.isErr) throw new PixivFetchError(pageResult.error);
224
+ yield pageResult.value;
225
+ nextUrl = pageResult.value.next_url;
226
+ }
227
+ }
228
+ /**
229
+ * Async generator that yields individual items across all pages.
230
+ *
231
+ * If any page fetch fails, the generator throws a `PixivFetchError`.
232
+ *
233
+ * @example
234
+ * ```ts
235
+ * for await (const illust of client.illusts.search({ word: 'cat' }).items()) {
236
+ * console.log(illust.title)
237
+ * }
238
+ * ```
239
+ */
240
+ async *items() {
241
+ for await (const page of this.pages()) {
242
+ for (const item of this.#getItems(page)) {
243
+ yield item;
244
+ }
245
+ }
246
+ }
247
+ };
248
+ function failedPaginated(error, http, getItems) {
249
+ return new PaginatedResultAsync(
250
+ Promise.resolve(err(error)),
251
+ http,
252
+ getItems
253
+ );
254
+ }
255
+
256
+ // src/auth.ts
257
+ var T = Array.from(
258
+ { length: 64 },
259
+ (_, i) => Math.floor(Math.abs(Math.sin(i + 1)) * 2 ** 32)
260
+ );
261
+ var S = [
262
+ 7,
263
+ 12,
264
+ 17,
265
+ 22,
266
+ 7,
267
+ 12,
268
+ 17,
269
+ 22,
270
+ 7,
271
+ 12,
272
+ 17,
273
+ 22,
274
+ 7,
275
+ 12,
276
+ 17,
277
+ 22,
278
+ 5,
279
+ 9,
280
+ 14,
281
+ 20,
282
+ 5,
283
+ 9,
284
+ 14,
285
+ 20,
286
+ 5,
287
+ 9,
288
+ 14,
289
+ 20,
290
+ 5,
291
+ 9,
292
+ 14,
293
+ 20,
294
+ 4,
295
+ 11,
296
+ 16,
297
+ 23,
298
+ 4,
299
+ 11,
300
+ 16,
301
+ 23,
302
+ 4,
303
+ 11,
304
+ 16,
305
+ 23,
306
+ 4,
307
+ 11,
308
+ 16,
309
+ 23,
310
+ 6,
311
+ 10,
312
+ 15,
313
+ 21,
314
+ 6,
315
+ 10,
316
+ 15,
317
+ 21,
318
+ 6,
319
+ 10,
320
+ 15,
321
+ 21,
322
+ 6,
323
+ 10,
324
+ 15,
325
+ 21
326
+ ];
327
+ function md5Bytes(bytes) {
328
+ const len = bytes.length;
329
+ bytes.push(128);
330
+ while (bytes.length % 64 !== 56) bytes.push(0);
331
+ const bitLen = len * 8;
332
+ for (let i = 0; i < 8; i++) {
333
+ bytes.push(i < 4 ? bitLen >>> i * 8 & 255 : 0);
334
+ }
335
+ let a = 1732584193;
336
+ let b = 4023233417;
337
+ let c = 2562383102;
338
+ let d = 271733878;
339
+ for (let chunk = 0; chunk < bytes.length; chunk += 64) {
340
+ const M = [];
341
+ for (let w = 0; w < 16; w++) {
342
+ const off = chunk + w * 4;
343
+ M.push(
344
+ bytes[off] | bytes[off + 1] << 8 | bytes[off + 2] << 16 | bytes[off + 3] << 24
345
+ );
346
+ }
347
+ let aa = a;
348
+ let bb = b;
349
+ let cc = c;
350
+ let dd = d;
351
+ for (let i = 0; i < 64; i++) {
352
+ let f;
353
+ let g;
354
+ if (i < 16) {
355
+ f = bb & cc | ~bb & dd;
356
+ g = i;
357
+ } else if (i < 32) {
358
+ f = dd & bb | ~dd & cc;
359
+ g = (5 * i + 1) % 16;
360
+ } else if (i < 48) {
361
+ f = bb ^ cc ^ dd;
362
+ g = (3 * i + 5) % 16;
363
+ } else {
364
+ f = cc ^ (bb | ~dd);
365
+ g = 7 * i % 16;
366
+ }
367
+ const tmp = dd;
368
+ dd = cc;
369
+ cc = bb;
370
+ const sum = Math.trunc(aa + f + M[g] + T[i]);
371
+ const rotated = sum << S[i] | sum >>> 32 - S[i];
372
+ bb = Math.trunc(bb + rotated);
373
+ aa = tmp;
374
+ }
375
+ a = Math.trunc(a + aa);
376
+ b = Math.trunc(b + bb);
377
+ c = Math.trunc(c + cc);
378
+ d = Math.trunc(d + dd);
379
+ }
380
+ return [a, b, c, d].map(
381
+ (n) => [n & 255, n >>> 8 & 255, n >>> 16 & 255, n >>> 24 & 255].map((byte) => byte.toString(16).padStart(2, "0")).join("")
382
+ ).join("");
383
+ }
384
+ function md5(input) {
385
+ const bytes = [];
386
+ for (let i = 0; i < input.length; i++) {
387
+ const code = input.codePointAt(i) ?? 0;
388
+ if (code < 128) {
389
+ bytes.push(code);
390
+ } else if (code < 2048) {
391
+ bytes.push(192 | code >> 6, 128 | code & 63);
392
+ } else {
393
+ bytes.push(
394
+ 224 | code >> 12,
395
+ 128 | code >> 6 & 63,
396
+ 128 | code & 63
397
+ );
398
+ }
399
+ }
400
+ return md5Bytes(bytes);
401
+ }
402
+ var CLIENT_ID = "MOBrBDS8blbauoSck0ZfDbtuzpyT";
403
+ var CLIENT_SECRET = "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj";
404
+ var HASH_SECRET = "28c1fdd170a5204386cb1313c7077b34f83e4aaf4aa829ce78c231e05b0bae2c";
405
+ var AUTH_URL = "https://oauth.secure.pixiv.net/auth/token";
406
+ function buildClientHash(localTime) {
407
+ return md5(localTime + HASH_SECRET);
408
+ }
409
+ var AuthManager = class _AuthManager {
410
+ #accessToken;
411
+ #refreshToken;
412
+ userId;
413
+ constructor(credentials) {
414
+ this.#accessToken = credentials.accessToken;
415
+ this.#refreshToken = credentials.refreshToken;
416
+ this.userId = credentials.userId;
417
+ }
418
+ /** Returns the current access token. */
419
+ get accessToken() {
420
+ return this.#accessToken;
421
+ }
422
+ /** Returns the current refresh token. */
423
+ get refreshToken() {
424
+ return this.#refreshToken;
425
+ }
426
+ /**
427
+ * Exchanges the stored refresh token for a fresh access token.
428
+ *
429
+ * Updates the internal credentials on success.
430
+ * Throws if the token endpoint returns a non-200 response.
431
+ */
432
+ async refresh() {
433
+ const localTime = (/* @__PURE__ */ new Date()).toISOString().replace(/Z$/, "+00:00");
434
+ const headers = {
435
+ "x-client-time": localTime,
436
+ "x-client-hash": buildClientHash(localTime),
437
+ "app-os": "ios",
438
+ "app-os-version": "16.4.1",
439
+ "user-agent": "PixivIOSApp/7.16.9 (iOS 16.4.1; iPad13,4)",
440
+ "Content-Type": "application/x-www-form-urlencoded"
441
+ };
442
+ const body = new URLSearchParams({
443
+ client_id: CLIENT_ID,
444
+ client_secret: CLIENT_SECRET,
445
+ get_secure_url: "1",
446
+ grant_type: "refresh_token",
447
+ refresh_token: this.#refreshToken
448
+ }).toString();
449
+ const response = await fetch(AUTH_URL, {
450
+ method: "POST",
451
+ headers,
452
+ body
453
+ });
454
+ if (response.status !== 200) {
455
+ throw new Error(
456
+ `Failed to refresh pixiv token: HTTP ${response.status}`
457
+ );
458
+ }
459
+ const data = await response.json();
460
+ this.#accessToken = data.response.access_token;
461
+ this.#refreshToken = data.response.refresh_token;
462
+ this.userId = data.user.id;
463
+ }
464
+ /**
465
+ * Creates an `AuthManager` by performing the initial token refresh.
466
+ *
467
+ * @param refreshToken - Pixiv refresh token
468
+ * @returns Initialized `AuthManager`
469
+ */
470
+ static async login(refreshToken) {
471
+ const manager = new _AuthManager({
472
+ userId: "",
473
+ accessToken: "",
474
+ refreshToken
475
+ });
476
+ await manager.refresh();
477
+ return manager;
478
+ }
479
+ };
480
+
481
+ // src/http.ts
482
+ var DEFAULT_RETRY = { maxRetries: 3, waitMs: 1e4 };
483
+ var BASE_URL = "https://app-api.pixiv.net";
484
+ var DEFAULT_HEADERS = {
485
+ Host: "app-api.pixiv.net",
486
+ "App-OS": "ios",
487
+ "App-OS-Version": "14.6",
488
+ "User-Agent": "PixivIOSApp/7.13.3 (iOS 14.6; iPhone13,2)",
489
+ "Accept-Language": "ja"
490
+ };
491
+ function parseRetryAfter(retryAfter, defaultMs) {
492
+ if (!retryAfter) return defaultMs;
493
+ if (/^\d+$/.test(retryAfter.trim())) {
494
+ return Number.parseInt(retryAfter, 10) * 1e3;
495
+ }
496
+ const retryDate = Date.parse(retryAfter);
497
+ if (!Number.isNaN(retryDate)) {
498
+ return Math.max(0, retryDate - Date.now());
499
+ }
500
+ return defaultMs;
501
+ }
502
+ function headersToRecord(headers) {
503
+ const result = {};
504
+ for (const [key, value] of headers) {
505
+ result[key] = value;
506
+ }
507
+ return result;
508
+ }
509
+ var HttpClient = class {
510
+ #auth;
511
+ #retry;
512
+ #interceptor;
513
+ constructor(auth, options) {
514
+ this.#auth = auth;
515
+ this.#retry = {
516
+ maxRetries: options?.retry?.maxRetries ?? DEFAULT_RETRY.maxRetries,
517
+ waitMs: options?.retry?.waitMs ?? DEFAULT_RETRY.waitMs
518
+ };
519
+ this.#interceptor = options?.onResponse;
520
+ }
521
+ /**
522
+ * Sends a GET request to the pixiv API.
523
+ *
524
+ * @param path - API endpoint path (e.g. "/v1/illust/detail")
525
+ * @param params - Query parameters as a URLSearchParams instance
526
+ * @returns `ResultAsync<T, PixivError>`
527
+ */
528
+ get(path, params) {
529
+ const qs = params ? `?${params.toString()}` : "";
530
+ const url = `${BASE_URL}${path}${qs}`;
531
+ return this.#send(url, "GET", path, void 0);
532
+ }
533
+ /**
534
+ * Sends a POST request to the pixiv API.
535
+ *
536
+ * @param path - API endpoint path (e.g. "/v2/illust/bookmark/add")
537
+ * @param body - URL-encoded request body string
538
+ * @returns `ResultAsync<T, PixivError>`
539
+ */
540
+ post(path, body) {
541
+ const url = `${BASE_URL}${path}`;
542
+ return this.#send(url, "POST", path, body);
543
+ }
544
+ /**
545
+ * Fetches a pixiv image URL without an Authorization header.
546
+ *
547
+ * Uses a browser User-Agent and the pixiv Referer, which are required for
548
+ * image CDN access. Retry and interceptor are not applied here.
549
+ *
550
+ * @param imageUrl - Full image URL
551
+ * @returns `ResultAsync<Response, PixivError>`
552
+ */
553
+ fetchImage(imageUrl) {
554
+ return ResultAsync.fromPromise(
555
+ fetch(imageUrl, {
556
+ headers: {
557
+ "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",
558
+ Referer: "https://www.pixiv.net/"
559
+ }
560
+ }),
561
+ networkError
562
+ ).andThen((response) => {
563
+ if (!response.ok) {
564
+ return ResultAsync.fromResult(
565
+ err(apiError(response.status, null))
566
+ );
567
+ }
568
+ return ResultAsync.fromResult(ok(response));
569
+ });
570
+ }
571
+ /**
572
+ * Sends a request to an absolute URL returned in a `next_url` field.
573
+ *
574
+ * Applies the same retry / interceptor / auth logic as `get()`.
575
+ *
576
+ * @param absoluteUrl - Full URL including query string
577
+ * @returns `ResultAsync<T, PixivError>`
578
+ */
579
+ getAbsolute(absoluteUrl) {
580
+ let endpoint;
581
+ try {
582
+ endpoint = new URL(absoluteUrl).pathname;
583
+ } catch {
584
+ endpoint = absoluteUrl;
585
+ }
586
+ return this.#send(absoluteUrl, "GET", endpoint, void 0);
587
+ }
588
+ // ---------------------------------------------------------------------------
589
+ // Private helpers
590
+ // ---------------------------------------------------------------------------
591
+ #send(url, method, endpoint, body) {
592
+ return new ResultAsync(this.#sendWithRetry(url, method, endpoint, body));
593
+ }
594
+ async #sendWithRetry(url, method, endpoint, body, allowRefresh = true) {
595
+ const maxRetries = Math.max(0, this.#retry.maxRetries);
596
+ const waitMs = Math.max(0, this.#retry.waitMs);
597
+ let lastRetryAfterMs = 0;
598
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
599
+ const requestHeaders = {
600
+ ...DEFAULT_HEADERS,
601
+ Authorization: `Bearer ${this.#auth.accessToken}`,
602
+ ...method === "POST" ? { "Content-Type": "application/x-www-form-urlencoded" } : {}
603
+ };
604
+ let response;
605
+ try {
606
+ response = await fetch(url, {
607
+ method,
608
+ headers: requestHeaders,
609
+ body: method === "POST" ? body : void 0
610
+ });
611
+ } catch (fetchError) {
612
+ return err(networkError(fetchError));
613
+ }
614
+ if (response.status === 429) {
615
+ await response.body?.cancel();
616
+ const retryAfterMs = parseRetryAfter(
617
+ response.headers.get("Retry-After"),
618
+ waitMs
619
+ );
620
+ lastRetryAfterMs = retryAfterMs;
621
+ if (attempt < maxRetries) {
622
+ await new Promise((resolve) => setTimeout(resolve, retryAfterMs));
623
+ continue;
624
+ }
625
+ return err(rateLimitError(lastRetryAfterMs));
626
+ }
627
+ if (response.status === 401) {
628
+ await response.body?.cancel();
629
+ if (allowRefresh) {
630
+ try {
631
+ await this.#auth.refresh();
632
+ } catch {
633
+ return err(authFailedError(401));
634
+ }
635
+ return this.#sendWithRetry(url, method, endpoint, body, false);
636
+ }
637
+ return err(authFailedError(401));
638
+ }
639
+ const contentType = response.headers.get("content-type") ?? "";
640
+ const text = await response.text();
641
+ let data;
642
+ const isJson = contentType.includes("application/json");
643
+ if (isJson) {
644
+ try {
645
+ data = JSON.parse(text);
646
+ } catch {
647
+ data = text;
648
+ }
649
+ } else {
650
+ data = text;
651
+ }
652
+ const responseHeaders = headersToRecord(response.headers);
653
+ if (!response.ok) {
654
+ return err(apiError(response.status, data));
655
+ }
656
+ const httpResponse = {
657
+ data,
658
+ status: response.status,
659
+ responseUrl: response.url || void 0};
660
+ if (this.#interceptor) {
661
+ const record = {
662
+ method,
663
+ endpoint,
664
+ url: response.url || url,
665
+ requestHeaders: JSON.stringify(requestHeaders),
666
+ requestBody: body ?? null,
667
+ responseType: isJson ? "JSON" : "TEXT",
668
+ statusCode: response.status,
669
+ responseHeaders: JSON.stringify(responseHeaders),
670
+ responseBody: isJson ? JSON.stringify(data) : text
671
+ };
672
+ Promise.resolve(this.#interceptor(record)).catch(() => void 0);
673
+ }
674
+ return ok(httpResponse.data);
675
+ }
676
+ return err(rateLimitError(lastRetryAfterMs));
677
+ }
678
+ };
679
+
680
+ // src/params.ts
681
+ function camelToSnake(key) {
682
+ return key.replaceAll(/([A-Z])/g, (m) => `_${m.toLowerCase()}`);
683
+ }
684
+ function toSnakeKeys(obj) {
685
+ const out = {};
686
+ for (const key of Object.keys(obj)) {
687
+ out[camelToSnake(key)] = obj[key];
688
+ }
689
+ return out;
690
+ }
691
+ function buildSearchParams(params) {
692
+ const usp = new URLSearchParams();
693
+ for (const [key, value] of Object.entries(params)) {
694
+ if (value === null || value === void 0) continue;
695
+ if (Array.isArray(value)) {
696
+ for (const item of value) usp.append(key, String(item));
697
+ } else {
698
+ usp.set(key, String(value));
7
699
  }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- var __importDefault = (this && this.__importDefault) || function (mod) {
17
- return (mod && mod.__esModule) ? mod : { "default": mod };
18
- };
19
- Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.Pixiv = void 0;
21
- __exportStar(require("./checks"), exports);
22
- __exportStar(require("./http-client"), exports);
23
- __exportStar(require("./options"), exports);
24
- var pixiv_1 = require("./pixiv");
25
- Object.defineProperty(exports, "Pixiv", { enumerable: true, get: function () { return __importDefault(pixiv_1).default; } });
26
- __exportStar(require("./pixiv"), exports);
27
- __exportStar(require("./utils"), exports);
28
- __exportStar(require("./saving-responses/index"), exports);
29
- __exportStar(require("./saving-responses/response-entity"), exports);
30
- __exportStar(require("./types/error-response"), exports);
31
- __exportStar(require("./types/errors"), exports);
32
- __exportStar(require("./types/pixiv-common"), exports);
33
- __exportStar(require("./types/pixiv-illust-series"), exports);
34
- __exportStar(require("./types/pixiv-illust"), exports);
35
- __exportStar(require("./types/pixiv-novel-series"), exports);
36
- __exportStar(require("./types/pixiv-novel"), exports);
37
- __exportStar(require("./types/pixiv-ugoira"), exports);
38
- __exportStar(require("./types/pixiv-user"), exports);
39
- __exportStar(require("./types/endpoints/v1/illust/detail"), exports);
40
- __exportStar(require("./types/endpoints/v1/illust/ranking"), exports);
41
- __exportStar(require("./types/endpoints/v1/illust/recommended"), exports);
42
- __exportStar(require("./types/endpoints/v1/illust/series"), exports);
43
- __exportStar(require("./types/endpoints/v1/manga/recommended"), exports);
44
- __exportStar(require("./types/endpoints/v1/novel/ranking"), exports);
45
- __exportStar(require("./types/endpoints/v1/novel/recommended"), exports);
46
- __exportStar(require("./types/endpoints/v1/novel/related"), exports);
47
- __exportStar(require("./types/endpoints/v1/search/illust"), exports);
48
- __exportStar(require("./types/endpoints/v1/search/novel"), exports);
49
- __exportStar(require("./types/endpoints/v1/user/detail"), exports);
50
- __exportStar(require("./types/endpoints/v1/user/following"), exports);
51
- __exportStar(require("./types/endpoints/v1/user/illusts"), exports);
52
- __exportStar(require("./types/endpoints/v1/user/novels"), exports);
53
- __exportStar(require("./types/endpoints/v2/illust/related"), exports);
54
- __exportStar(require("./types/endpoints/v2/novel/detail"), exports);
55
- __exportStar(require("./types/endpoints/v2/novel/series"), exports);
56
- __exportStar(require("./types/endpoints/webview/v2/novel"), exports);
57
- __exportStar(require("./types/endpoints/v1/illust/bookmark/delete"), exports);
58
- __exportStar(require("./types/endpoints/v1/illust/ugoira/metadata"), exports);
59
- __exportStar(require("./types/endpoints/v1/novel/bookmark/delete"), exports);
60
- __exportStar(require("./types/endpoints/v1/user/bookmarks/illust"), exports);
61
- __exportStar(require("./types/endpoints/v1/user/bookmarks/novel"), exports);
62
- __exportStar(require("./types/endpoints/v1/user/follow/add"), exports);
63
- __exportStar(require("./types/endpoints/v1/user/follow/delete"), exports);
64
- __exportStar(require("./types/endpoints/v2/illust/bookmark/add"), exports);
65
- __exportStar(require("./types/endpoints/v2/novel/bookmark/add"), exports);
66
- //# sourceMappingURL=index.js.map
700
+ }
701
+ return usp;
702
+ }
703
+ function buildParams(params) {
704
+ return buildSearchParams(toSnakeKeys(params));
705
+ }
706
+
707
+ // src/resources/illusts.ts
708
+ var IllustResource = class {
709
+ #http;
710
+ constructor(http) {
711
+ this.#http = http;
712
+ }
713
+ /**
714
+ * Fetches a single illust by ID.
715
+ * GET /v1/illust/detail
716
+ *
717
+ * @param params - Request parameters
718
+ */
719
+ detail(params) {
720
+ return this.#http.get(
721
+ "/v1/illust/detail",
722
+ buildParams({ illustId: params.illustId, filter: params.filter ?? "for_ios" })
723
+ );
724
+ }
725
+ /**
726
+ * Fetches related illusts for a given illust.
727
+ * GET /v2/illust/related
728
+ *
729
+ * @param params - Request parameters
730
+ */
731
+ related(params) {
732
+ return PaginatedResultAsync.fromResultAsync(
733
+ this.#http.get(
734
+ "/v2/illust/related",
735
+ buildParams({
736
+ illustId: params.illustId,
737
+ filter: params.filter ?? "for_ios",
738
+ ...params.seedIllustIds ? { seedIllustIds: params.seedIllustIds } : {}
739
+ })
740
+ ),
741
+ this.#http,
742
+ (page) => page.illusts
743
+ );
744
+ }
745
+ /**
746
+ * Searches for illusts.
747
+ * GET /v1/search/illust
748
+ *
749
+ * @param params - Request parameters
750
+ */
751
+ search(params) {
752
+ return PaginatedResultAsync.fromResultAsync(
753
+ this.#http.get(
754
+ "/v1/search/illust",
755
+ buildParams({
756
+ word: params.word,
757
+ searchTarget: params.searchTarget ?? "partial_match_for_tags",
758
+ sort: params.sort ?? "date_desc",
759
+ filter: params.filter ?? "for_ios",
760
+ duration: params.duration,
761
+ startDate: params.startDate,
762
+ endDate: params.endDate,
763
+ searchAiType: params.searchAiType,
764
+ offset: params.offset
765
+ })
766
+ ),
767
+ this.#http,
768
+ (page) => page.illusts
769
+ );
770
+ }
771
+ /**
772
+ * Fetches the illust ranking.
773
+ * GET /v1/illust/ranking
774
+ *
775
+ * @param params - Request parameters
776
+ */
777
+ ranking(params = {}) {
778
+ return PaginatedResultAsync.fromResultAsync(
779
+ this.#http.get(
780
+ "/v1/illust/ranking",
781
+ buildParams({
782
+ mode: params.mode ?? "day",
783
+ filter: params.filter ?? "for_ios",
784
+ date: params.date,
785
+ offset: params.offset
786
+ })
787
+ ),
788
+ this.#http,
789
+ (page) => page.illusts
790
+ );
791
+ }
792
+ /**
793
+ * Fetches recommended illusts.
794
+ * GET /v1/illust/recommended
795
+ *
796
+ * @param params - Request parameters
797
+ */
798
+ recommended(params = {}) {
799
+ return PaginatedResultAsync.fromResultAsync(
800
+ this.#http.get(
801
+ "/v1/illust/recommended",
802
+ buildParams({
803
+ filter: params.filter ?? "for_ios",
804
+ includeRankingLabel: true,
805
+ includeRankingIllusts: true,
806
+ includePrivacyPolicy: true,
807
+ offset: params.offset
808
+ })
809
+ ),
810
+ this.#http,
811
+ (page) => page.illusts
812
+ );
813
+ }
814
+ /**
815
+ * Fetches an illust series.
816
+ * GET /v1/illust/series
817
+ *
818
+ * @param params - Request parameters
819
+ */
820
+ series(params) {
821
+ return PaginatedResultAsync.fromResultAsync(
822
+ this.#http.get(
823
+ "/v1/illust/series",
824
+ buildParams({
825
+ illustSeriesId: params.illustSeriesId,
826
+ filter: params.filter ?? "for_ios"
827
+ })
828
+ ),
829
+ this.#http,
830
+ (page) => page.illusts
831
+ );
832
+ }
833
+ /**
834
+ * Adds an illust bookmark.
835
+ * POST /v2/illust/bookmark/add
836
+ *
837
+ * @param params - Request parameters
838
+ */
839
+ bookmarkAdd(params) {
840
+ const body = buildParams({
841
+ illustId: params.illustId,
842
+ restrict: params.restrict ?? "public",
843
+ ...params.tags ? { tags: params.tags } : {}
844
+ });
845
+ return this.#http.post(
846
+ "/v2/illust/bookmark/add",
847
+ body.toString()
848
+ );
849
+ }
850
+ /**
851
+ * Removes an illust bookmark.
852
+ * POST /v1/illust/bookmark/delete
853
+ *
854
+ * @param params - Request parameters
855
+ */
856
+ bookmarkDelete(params) {
857
+ const body = buildParams({ illustId: String(params.illustId) });
858
+ return this.#http.post(
859
+ "/v1/illust/bookmark/delete",
860
+ body.toString()
861
+ );
862
+ }
863
+ };
864
+
865
+ // src/resources/novels.ts
866
+ var NovelResource = class {
867
+ #http;
868
+ constructor(http) {
869
+ this.#http = http;
870
+ }
871
+ /**
872
+ * Fetches a single novel by ID.
873
+ * GET /v2/novel/detail
874
+ *
875
+ * @param params - Request parameters
876
+ */
877
+ detail(params) {
878
+ return this.#http.get(
879
+ "/v2/novel/detail",
880
+ buildParams({ novelId: params.novelId })
881
+ );
882
+ }
883
+ /**
884
+ * Fetches the full text of a novel.
885
+ * GET /webview/v2/novel
886
+ *
887
+ * @param params - Request parameters
888
+ */
889
+ text(params) {
890
+ return this.#http.get(
891
+ "/webview/v2/novel",
892
+ buildParams({ id: params.id })
893
+ );
894
+ }
895
+ /**
896
+ * Fetches related novels for a given novel.
897
+ * GET /v1/novel/related
898
+ *
899
+ * @param params - Request parameters
900
+ */
901
+ related(params) {
902
+ return PaginatedResultAsync.fromResultAsync(
903
+ this.#http.get(
904
+ "/v1/novel/related",
905
+ buildParams({ novelId: params.novelId })
906
+ ),
907
+ this.#http,
908
+ (page) => page.novels
909
+ );
910
+ }
911
+ /**
912
+ * Searches for novels.
913
+ * GET /v1/search/novel
914
+ *
915
+ * @param params - Request parameters
916
+ */
917
+ search(params) {
918
+ return PaginatedResultAsync.fromResultAsync(
919
+ this.#http.get(
920
+ "/v1/search/novel",
921
+ buildParams({
922
+ word: params.word,
923
+ searchTarget: params.searchTarget ?? "partial_match_for_tags",
924
+ sort: params.sort ?? "date_desc",
925
+ filter: params.filter ?? "for_ios",
926
+ duration: params.duration,
927
+ startDate: params.startDate,
928
+ endDate: params.endDate,
929
+ searchAiType: params.searchAiType,
930
+ offset: params.offset
931
+ })
932
+ ),
933
+ this.#http,
934
+ (page) => page.novels
935
+ );
936
+ }
937
+ /**
938
+ * Fetches the novel ranking.
939
+ * GET /v1/novel/ranking
940
+ *
941
+ * @param params - Request parameters
942
+ */
943
+ ranking(params = {}) {
944
+ return PaginatedResultAsync.fromResultAsync(
945
+ this.#http.get(
946
+ "/v1/novel/ranking",
947
+ buildParams({
948
+ mode: params.mode ?? "day",
949
+ filter: params.filter ?? "for_ios",
950
+ date: params.date,
951
+ offset: params.offset
952
+ })
953
+ ),
954
+ this.#http,
955
+ (page) => page.novels
956
+ );
957
+ }
958
+ /**
959
+ * Fetches recommended novels.
960
+ * GET /v1/novel/recommended
961
+ *
962
+ * @param params - Request parameters
963
+ */
964
+ recommended(params = {}) {
965
+ return PaginatedResultAsync.fromResultAsync(
966
+ this.#http.get(
967
+ "/v1/novel/recommended",
968
+ buildParams({
969
+ filter: params.filter ?? "for_ios",
970
+ includeRankingNovels: true,
971
+ includePrivacyPolicy: true,
972
+ offset: params.offset
973
+ })
974
+ ),
975
+ this.#http,
976
+ (page) => page.novels
977
+ );
978
+ }
979
+ /**
980
+ * Fetches a novel series.
981
+ * GET /v2/novel/series
982
+ *
983
+ * @param params - Request parameters
984
+ */
985
+ series(params) {
986
+ return PaginatedResultAsync.fromResultAsync(
987
+ this.#http.get(
988
+ "/v2/novel/series",
989
+ buildParams({ seriesId: params.seriesId, lastOrder: params.lastOrder })
990
+ ),
991
+ this.#http,
992
+ (page) => page.novels
993
+ );
994
+ }
995
+ /**
996
+ * Adds a novel bookmark.
997
+ * POST /v2/novel/bookmark/add
998
+ *
999
+ * @param params - Request parameters
1000
+ */
1001
+ bookmarkAdd(params) {
1002
+ const body = buildParams({
1003
+ novelId: params.novelId,
1004
+ restrict: params.restrict ?? "public",
1005
+ ...params.tags ? { tags: params.tags } : {}
1006
+ });
1007
+ return this.#http.post(
1008
+ "/v2/novel/bookmark/add",
1009
+ body.toString()
1010
+ );
1011
+ }
1012
+ /**
1013
+ * Removes a novel bookmark.
1014
+ * POST /v1/novel/bookmark/delete
1015
+ *
1016
+ * @param params - Request parameters
1017
+ */
1018
+ bookmarkDelete(params) {
1019
+ const body = buildParams({ novelId: String(params.novelId) });
1020
+ return this.#http.post(
1021
+ "/v1/novel/bookmark/delete",
1022
+ body.toString()
1023
+ );
1024
+ }
1025
+ };
1026
+
1027
+ // src/resources/users.ts
1028
+ var UserBookmarksResource = class {
1029
+ #http;
1030
+ constructor(http) {
1031
+ this.#http = http;
1032
+ }
1033
+ /**
1034
+ * Fetches a user's bookmarked illusts.
1035
+ * GET /v1/user/bookmarks/illust
1036
+ *
1037
+ * @param params - Request parameters
1038
+ */
1039
+ illusts(params) {
1040
+ return PaginatedResultAsync.fromResultAsync(
1041
+ this.#http.get(
1042
+ "/v1/user/bookmarks/illust",
1043
+ buildParams({
1044
+ userId: params.userId,
1045
+ restrict: params.restrict ?? "public",
1046
+ filter: params.filter ?? "for_ios",
1047
+ tag: params.tag,
1048
+ maxBookmarkId: params.maxBookmarkId,
1049
+ offset: params.offset
1050
+ })
1051
+ ),
1052
+ this.#http,
1053
+ (page) => page.illusts
1054
+ );
1055
+ }
1056
+ /**
1057
+ * Fetches a user's bookmarked novels.
1058
+ * GET /v1/user/bookmarks/novel
1059
+ *
1060
+ * @param params - Request parameters
1061
+ */
1062
+ novels(params) {
1063
+ return PaginatedResultAsync.fromResultAsync(
1064
+ this.#http.get(
1065
+ "/v1/user/bookmarks/novel",
1066
+ buildParams({
1067
+ userId: params.userId,
1068
+ restrict: params.restrict ?? "public",
1069
+ filter: params.filter ?? "for_ios",
1070
+ tag: params.tag,
1071
+ offset: params.offset
1072
+ })
1073
+ ),
1074
+ this.#http,
1075
+ (page) => page.novels
1076
+ );
1077
+ }
1078
+ };
1079
+ var UserResource = class {
1080
+ /** User bookmarks sub-namespace. */
1081
+ bookmarks;
1082
+ #http;
1083
+ constructor(http) {
1084
+ this.#http = http;
1085
+ this.bookmarks = new UserBookmarksResource(http);
1086
+ }
1087
+ /**
1088
+ * Fetches detailed profile information for a user.
1089
+ * GET /v1/user/detail
1090
+ *
1091
+ * @param params - Request parameters
1092
+ */
1093
+ detail(params) {
1094
+ return this.#http.get(
1095
+ "/v1/user/detail",
1096
+ buildParams({ userId: params.userId, filter: params.filter ?? "for_ios" })
1097
+ );
1098
+ }
1099
+ /**
1100
+ * Fetches illusts posted by a user.
1101
+ * GET /v1/user/illusts
1102
+ *
1103
+ * @param params - Request parameters
1104
+ */
1105
+ illusts(params) {
1106
+ return PaginatedResultAsync.fromResultAsync(
1107
+ this.#http.get(
1108
+ "/v1/user/illusts",
1109
+ buildParams({
1110
+ userId: params.userId,
1111
+ type: params.type,
1112
+ filter: params.filter ?? "for_ios",
1113
+ offset: params.offset
1114
+ })
1115
+ ),
1116
+ this.#http,
1117
+ (page) => page.illusts
1118
+ );
1119
+ }
1120
+ /**
1121
+ * Fetches novels posted by a user.
1122
+ * GET /v1/user/novels
1123
+ *
1124
+ * @param params - Request parameters
1125
+ */
1126
+ novels(params) {
1127
+ return PaginatedResultAsync.fromResultAsync(
1128
+ this.#http.get(
1129
+ "/v1/user/novels",
1130
+ buildParams({
1131
+ userId: params.userId,
1132
+ filter: params.filter ?? "for_ios",
1133
+ offset: params.offset
1134
+ })
1135
+ ),
1136
+ this.#http,
1137
+ (page) => page.novels
1138
+ );
1139
+ }
1140
+ /**
1141
+ * Fetches the list of users that a user is following.
1142
+ * GET /v1/user/following
1143
+ *
1144
+ * @param params - Request parameters
1145
+ */
1146
+ following(params) {
1147
+ return PaginatedResultAsync.fromResultAsync(
1148
+ this.#http.get(
1149
+ "/v1/user/following",
1150
+ buildParams({
1151
+ userId: params.userId,
1152
+ restrict: params.restrict ?? "public",
1153
+ offset: params.offset
1154
+ })
1155
+ ),
1156
+ this.#http,
1157
+ (page) => page.user_previews
1158
+ );
1159
+ }
1160
+ /**
1161
+ * Follows a user.
1162
+ * POST /v1/user/follow/add
1163
+ *
1164
+ * @param params - Request parameters
1165
+ */
1166
+ followAdd(params) {
1167
+ const body = buildParams({
1168
+ userId: params.userId,
1169
+ restrict: params.restrict ?? "public"
1170
+ });
1171
+ return this.#http.post(
1172
+ "/v1/user/follow/add",
1173
+ body.toString()
1174
+ );
1175
+ }
1176
+ /**
1177
+ * Unfollows a user.
1178
+ * POST /v1/user/follow/delete
1179
+ *
1180
+ * @param params - Request parameters
1181
+ */
1182
+ followDelete(params) {
1183
+ const body = buildParams({ userId: String(params.userId) });
1184
+ return this.#http.post(
1185
+ "/v1/user/follow/delete",
1186
+ body.toString()
1187
+ );
1188
+ }
1189
+ };
1190
+
1191
+ // src/resources/manga.ts
1192
+ var MangaResource = class {
1193
+ #http;
1194
+ constructor(http) {
1195
+ this.#http = http;
1196
+ }
1197
+ /**
1198
+ * Fetches recommended manga.
1199
+ * GET /v1/manga/recommended
1200
+ *
1201
+ * @param params - Request parameters
1202
+ */
1203
+ recommended(params = {}) {
1204
+ return PaginatedResultAsync.fromResultAsync(
1205
+ this.#http.get(
1206
+ "/v1/manga/recommended",
1207
+ buildParams({
1208
+ filter: params.filter ?? "for_ios",
1209
+ includeRankingIllusts: true,
1210
+ includePrivacyPolicy: true,
1211
+ offset: params.offset
1212
+ })
1213
+ ),
1214
+ this.#http,
1215
+ (page) => page.illusts
1216
+ );
1217
+ }
1218
+ };
1219
+
1220
+ // src/resources/ugoira.ts
1221
+ var UgoiraResource = class {
1222
+ #http;
1223
+ constructor(http) {
1224
+ this.#http = http;
1225
+ }
1226
+ /**
1227
+ * Fetches ugoira metadata (ZIP URL and per-frame timings).
1228
+ * GET /v1/ugoira/metadata
1229
+ *
1230
+ * @param params - Request parameters
1231
+ */
1232
+ metadata(params) {
1233
+ return this.#http.get(
1234
+ "/v1/ugoira/metadata",
1235
+ buildParams({ illustId: params.illustId })
1236
+ );
1237
+ }
1238
+ };
1239
+
1240
+ // src/resources/images.ts
1241
+ var ImageResource = class {
1242
+ #http;
1243
+ constructor(http) {
1244
+ this.#http = http;
1245
+ }
1246
+ /**
1247
+ * Fetches a pixiv image.
1248
+ *
1249
+ * Uses a browser User-Agent and Referer (required for pixiv CDN).
1250
+ * No Authorization header is sent.
1251
+ *
1252
+ * @param imageUrl - Full CDN image URL
1253
+ */
1254
+ fetch(imageUrl) {
1255
+ return this.#http.fetchImage(imageUrl);
1256
+ }
1257
+ };
1258
+
1259
+ // src/client.ts
1260
+ var PixivClient = class _PixivClient {
1261
+ /** Illust API namespace. */
1262
+ illusts;
1263
+ /** Novel API namespace. */
1264
+ novels;
1265
+ /** User API namespace. */
1266
+ users;
1267
+ /** Manga API namespace. */
1268
+ manga;
1269
+ /** Ugoira API namespace. */
1270
+ ugoira;
1271
+ /** Image fetch helpers. */
1272
+ images;
1273
+ #auth;
1274
+ constructor(auth, http) {
1275
+ this.#auth = auth;
1276
+ this.illusts = new IllustResource(http);
1277
+ this.novels = new NovelResource(http);
1278
+ this.users = new UserResource(http);
1279
+ this.manga = new MangaResource(http);
1280
+ this.ugoira = new UgoiraResource(http);
1281
+ this.images = new ImageResource(http);
1282
+ }
1283
+ /**
1284
+ * Numeric user ID of the authenticated account, as a string.
1285
+ *
1286
+ * Available immediately after {@link PixivClient.of} resolves.
1287
+ */
1288
+ get userId() {
1289
+ return this.#auth.userId;
1290
+ }
1291
+ /**
1292
+ * Creates a PixivClient by refreshing the given token.
1293
+ *
1294
+ * @param refreshToken - Pixiv refresh token
1295
+ * @param options - Optional retry and response interceptor configuration
1296
+ * @returns A fully initialised {@link PixivClient}
1297
+ */
1298
+ static async of(refreshToken, options) {
1299
+ const auth = await AuthManager.login(refreshToken);
1300
+ const http = new HttpClient(auth, options);
1301
+ return new _PixivClient(auth, http);
1302
+ }
1303
+ };
1304
+
1305
+ export { PaginatedResultAsync, PixivClient, PixivFetchError, ResultAsync, apiError, authFailedError, err, failedPaginated, networkError, ok, rateLimitError };