@book000/pixivts 0.55.1 → 0.56.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.
Files changed (204) hide show
  1. package/README.md +115 -19
  2. package/dist/index.cjs +1321 -0
  3. package/dist/index.d.cts +1662 -0
  4. package/dist/index.d.ts +1662 -45
  5. package/dist/index.js +1308 -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,1309 @@
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 WebView HTML for a novel.
885
+ * GET /webview/v2/novel
886
+ *
887
+ * Returns the raw HTML page that the pixiv app renders in a WebView.
888
+ * To extract the plain text, parse the returned HTML (e.g. strip tags).
889
+ *
890
+ * @param params - Request parameters
891
+ */
892
+ text(params) {
893
+ return this.#http.get(
894
+ "/webview/v2/novel",
895
+ // The webview endpoint uses the query parameter 'id', not 'novel_id'
896
+ buildParams({ id: params.novelId })
897
+ );
898
+ }
899
+ /**
900
+ * Fetches related novels for a given novel.
901
+ * GET /v1/novel/related
902
+ *
903
+ * @param params - Request parameters
904
+ */
905
+ related(params) {
906
+ return PaginatedResultAsync.fromResultAsync(
907
+ this.#http.get(
908
+ "/v1/novel/related",
909
+ buildParams({ novelId: params.novelId })
910
+ ),
911
+ this.#http,
912
+ (page) => page.novels
913
+ );
914
+ }
915
+ /**
916
+ * Searches for novels.
917
+ * GET /v1/search/novel
918
+ *
919
+ * @param params - Request parameters
920
+ */
921
+ search(params) {
922
+ return PaginatedResultAsync.fromResultAsync(
923
+ this.#http.get(
924
+ "/v1/search/novel",
925
+ buildParams({
926
+ word: params.word,
927
+ searchTarget: params.searchTarget ?? "partial_match_for_tags",
928
+ sort: params.sort ?? "date_desc",
929
+ filter: params.filter ?? "for_ios",
930
+ duration: params.duration,
931
+ startDate: params.startDate,
932
+ endDate: params.endDate,
933
+ searchAiType: params.searchAiType,
934
+ offset: params.offset
935
+ })
936
+ ),
937
+ this.#http,
938
+ (page) => page.novels
939
+ );
940
+ }
941
+ /**
942
+ * Fetches the novel ranking.
943
+ * GET /v1/novel/ranking
944
+ *
945
+ * @param params - Request parameters
946
+ */
947
+ ranking(params = {}) {
948
+ return PaginatedResultAsync.fromResultAsync(
949
+ this.#http.get(
950
+ "/v1/novel/ranking",
951
+ buildParams({
952
+ mode: params.mode ?? "day",
953
+ filter: params.filter ?? "for_ios",
954
+ date: params.date,
955
+ offset: params.offset
956
+ })
957
+ ),
958
+ this.#http,
959
+ (page) => page.novels
960
+ );
961
+ }
962
+ /**
963
+ * Fetches recommended novels.
964
+ * GET /v1/novel/recommended
965
+ *
966
+ * @param params - Request parameters
967
+ */
968
+ recommended(params = {}) {
969
+ return PaginatedResultAsync.fromResultAsync(
970
+ this.#http.get(
971
+ "/v1/novel/recommended",
972
+ buildParams({
973
+ filter: params.filter ?? "for_ios",
974
+ includeRankingNovels: true,
975
+ includePrivacyPolicy: true,
976
+ offset: params.offset
977
+ })
978
+ ),
979
+ this.#http,
980
+ (page) => page.novels
981
+ );
982
+ }
983
+ /**
984
+ * Fetches a novel series.
985
+ * GET /v2/novel/series
986
+ *
987
+ * @param params - Request parameters
988
+ */
989
+ series(params) {
990
+ return PaginatedResultAsync.fromResultAsync(
991
+ this.#http.get(
992
+ "/v2/novel/series",
993
+ buildParams({ seriesId: params.seriesId, lastOrder: params.lastOrder })
994
+ ),
995
+ this.#http,
996
+ (page) => page.novels
997
+ );
998
+ }
999
+ /**
1000
+ * Adds a novel bookmark.
1001
+ * POST /v2/novel/bookmark/add
1002
+ *
1003
+ * @param params - Request parameters
1004
+ */
1005
+ bookmarkAdd(params) {
1006
+ const body = buildParams({
1007
+ novelId: params.novelId,
1008
+ restrict: params.restrict ?? "public",
1009
+ ...params.tags ? { tags: params.tags } : {}
1010
+ });
1011
+ return this.#http.post(
1012
+ "/v2/novel/bookmark/add",
1013
+ body.toString()
1014
+ );
1015
+ }
1016
+ /**
1017
+ * Removes a novel bookmark.
1018
+ * POST /v1/novel/bookmark/delete
1019
+ *
1020
+ * @param params - Request parameters
1021
+ */
1022
+ bookmarkDelete(params) {
1023
+ const body = buildParams({ novelId: String(params.novelId) });
1024
+ return this.#http.post(
1025
+ "/v1/novel/bookmark/delete",
1026
+ body.toString()
1027
+ );
1028
+ }
1029
+ };
1030
+
1031
+ // src/resources/users.ts
1032
+ var UserBookmarksResource = class {
1033
+ #http;
1034
+ constructor(http) {
1035
+ this.#http = http;
1036
+ }
1037
+ /**
1038
+ * Fetches a user's bookmarked illusts.
1039
+ * GET /v1/user/bookmarks/illust
1040
+ *
1041
+ * @param params - Request parameters
1042
+ */
1043
+ illusts(params) {
1044
+ return PaginatedResultAsync.fromResultAsync(
1045
+ this.#http.get(
1046
+ "/v1/user/bookmarks/illust",
1047
+ buildParams({
1048
+ userId: params.userId,
1049
+ restrict: params.restrict ?? "public",
1050
+ filter: params.filter ?? "for_ios",
1051
+ tag: params.tag,
1052
+ maxBookmarkId: params.maxBookmarkId,
1053
+ offset: params.offset
1054
+ })
1055
+ ),
1056
+ this.#http,
1057
+ (page) => page.illusts
1058
+ );
1059
+ }
1060
+ /**
1061
+ * Fetches a user's bookmarked novels.
1062
+ * GET /v1/user/bookmarks/novel
1063
+ *
1064
+ * @param params - Request parameters
1065
+ */
1066
+ novels(params) {
1067
+ return PaginatedResultAsync.fromResultAsync(
1068
+ this.#http.get(
1069
+ "/v1/user/bookmarks/novel",
1070
+ buildParams({
1071
+ userId: params.userId,
1072
+ restrict: params.restrict ?? "public",
1073
+ filter: params.filter ?? "for_ios",
1074
+ tag: params.tag,
1075
+ offset: params.offset
1076
+ })
1077
+ ),
1078
+ this.#http,
1079
+ (page) => page.novels
1080
+ );
1081
+ }
1082
+ };
1083
+ var UserResource = class {
1084
+ /** User bookmarks sub-namespace. */
1085
+ bookmarks;
1086
+ #http;
1087
+ constructor(http) {
1088
+ this.#http = http;
1089
+ this.bookmarks = new UserBookmarksResource(http);
1090
+ }
1091
+ /**
1092
+ * Fetches detailed profile information for a user.
1093
+ * GET /v1/user/detail
1094
+ *
1095
+ * @param params - Request parameters
1096
+ */
1097
+ detail(params) {
1098
+ return this.#http.get(
1099
+ "/v1/user/detail",
1100
+ buildParams({ userId: params.userId, filter: params.filter ?? "for_ios" })
1101
+ );
1102
+ }
1103
+ /**
1104
+ * Fetches illusts posted by a user.
1105
+ * GET /v1/user/illusts
1106
+ *
1107
+ * @param params - Request parameters
1108
+ */
1109
+ illusts(params) {
1110
+ return PaginatedResultAsync.fromResultAsync(
1111
+ this.#http.get(
1112
+ "/v1/user/illusts",
1113
+ buildParams({
1114
+ userId: params.userId,
1115
+ type: params.type,
1116
+ filter: params.filter ?? "for_ios",
1117
+ offset: params.offset
1118
+ })
1119
+ ),
1120
+ this.#http,
1121
+ (page) => page.illusts
1122
+ );
1123
+ }
1124
+ /**
1125
+ * Fetches novels posted by a user.
1126
+ * GET /v1/user/novels
1127
+ *
1128
+ * @param params - Request parameters
1129
+ */
1130
+ novels(params) {
1131
+ return PaginatedResultAsync.fromResultAsync(
1132
+ this.#http.get(
1133
+ "/v1/user/novels",
1134
+ buildParams({
1135
+ userId: params.userId,
1136
+ filter: params.filter ?? "for_ios",
1137
+ offset: params.offset
1138
+ })
1139
+ ),
1140
+ this.#http,
1141
+ (page) => page.novels
1142
+ );
1143
+ }
1144
+ /**
1145
+ * Fetches the list of users that a user is following.
1146
+ * GET /v1/user/following
1147
+ *
1148
+ * @param params - Request parameters
1149
+ */
1150
+ following(params) {
1151
+ return PaginatedResultAsync.fromResultAsync(
1152
+ this.#http.get(
1153
+ "/v1/user/following",
1154
+ buildParams({
1155
+ userId: params.userId,
1156
+ restrict: params.restrict ?? "public",
1157
+ offset: params.offset
1158
+ })
1159
+ ),
1160
+ this.#http,
1161
+ (page) => page.user_previews
1162
+ );
1163
+ }
1164
+ /**
1165
+ * Follows a user.
1166
+ * POST /v1/user/follow/add
1167
+ *
1168
+ * @param params - Request parameters
1169
+ */
1170
+ followAdd(params) {
1171
+ const body = buildParams({
1172
+ userId: params.userId,
1173
+ restrict: params.restrict ?? "public"
1174
+ });
1175
+ return this.#http.post(
1176
+ "/v1/user/follow/add",
1177
+ body.toString()
1178
+ );
1179
+ }
1180
+ /**
1181
+ * Unfollows a user.
1182
+ * POST /v1/user/follow/delete
1183
+ *
1184
+ * @param params - Request parameters
1185
+ */
1186
+ followDelete(params) {
1187
+ const body = buildParams({ userId: String(params.userId) });
1188
+ return this.#http.post(
1189
+ "/v1/user/follow/delete",
1190
+ body.toString()
1191
+ );
1192
+ }
1193
+ };
1194
+
1195
+ // src/resources/manga.ts
1196
+ var MangaResource = class {
1197
+ #http;
1198
+ constructor(http) {
1199
+ this.#http = http;
1200
+ }
1201
+ /**
1202
+ * Fetches recommended manga.
1203
+ * GET /v1/manga/recommended
1204
+ *
1205
+ * @param params - Request parameters
1206
+ */
1207
+ recommended(params = {}) {
1208
+ return PaginatedResultAsync.fromResultAsync(
1209
+ this.#http.get(
1210
+ "/v1/manga/recommended",
1211
+ buildParams({
1212
+ filter: params.filter ?? "for_ios",
1213
+ includeRankingIllusts: true,
1214
+ includePrivacyPolicy: true,
1215
+ offset: params.offset
1216
+ })
1217
+ ),
1218
+ this.#http,
1219
+ (page) => page.illusts
1220
+ );
1221
+ }
1222
+ };
1223
+
1224
+ // src/resources/ugoira.ts
1225
+ var UgoiraResource = class {
1226
+ #http;
1227
+ constructor(http) {
1228
+ this.#http = http;
1229
+ }
1230
+ /**
1231
+ * Fetches ugoira metadata (ZIP URL and per-frame timings).
1232
+ * GET /v1/ugoira/metadata
1233
+ *
1234
+ * @param params - Request parameters
1235
+ */
1236
+ metadata(params) {
1237
+ return this.#http.get(
1238
+ "/v1/ugoira/metadata",
1239
+ buildParams({ illustId: params.illustId })
1240
+ );
1241
+ }
1242
+ };
1243
+
1244
+ // src/resources/images.ts
1245
+ var ImageResource = class {
1246
+ #http;
1247
+ constructor(http) {
1248
+ this.#http = http;
1249
+ }
1250
+ /**
1251
+ * Fetches a pixiv image.
1252
+ *
1253
+ * Uses a browser User-Agent and Referer (required for pixiv CDN).
1254
+ * No Authorization header is sent.
1255
+ *
1256
+ * @param imageUrl - Full CDN image URL
1257
+ */
1258
+ fetch(imageUrl) {
1259
+ return this.#http.fetchImage(imageUrl);
1260
+ }
1261
+ };
1262
+
1263
+ // src/client.ts
1264
+ var PixivClient = class _PixivClient {
1265
+ /** Illust API namespace. */
1266
+ illusts;
1267
+ /** Novel API namespace. */
1268
+ novels;
1269
+ /** User API namespace. */
1270
+ users;
1271
+ /** Manga API namespace. */
1272
+ manga;
1273
+ /** Ugoira API namespace. */
1274
+ ugoira;
1275
+ /** Image fetch helpers. */
1276
+ images;
1277
+ #auth;
1278
+ constructor(auth, http) {
1279
+ this.#auth = auth;
1280
+ this.illusts = new IllustResource(http);
1281
+ this.novels = new NovelResource(http);
1282
+ this.users = new UserResource(http);
1283
+ this.manga = new MangaResource(http);
1284
+ this.ugoira = new UgoiraResource(http);
1285
+ this.images = new ImageResource(http);
1286
+ }
1287
+ /**
1288
+ * Numeric user ID of the authenticated account, as a string.
1289
+ *
1290
+ * Available immediately after {@link PixivClient.of} resolves.
1291
+ */
1292
+ get userId() {
1293
+ return this.#auth.userId;
1294
+ }
1295
+ /**
1296
+ * Creates a PixivClient by refreshing the given token.
1297
+ *
1298
+ * @param refreshToken - Pixiv refresh token
1299
+ * @param options - Optional retry and response interceptor configuration
1300
+ * @returns A fully initialised {@link PixivClient}
1301
+ */
1302
+ static async of(refreshToken, options) {
1303
+ const auth = await AuthManager.login(refreshToken);
1304
+ const http = new HttpClient(auth, options);
1305
+ return new _PixivClient(auth, http);
1306
+ }
1307
+ };
1308
+
1309
+ export { PaginatedResultAsync, PixivClient, PixivFetchError, ResultAsync, apiError, authFailedError, err, failedPaginated, networkError, ok, rateLimitError };