@book000/pixivts 0.57.1 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -446,12 +446,71 @@ declare class PaginatedResultAsync<TPage extends PagedResponse, TItem> extends R
446
446
  */
447
447
  declare function failedPaginated<TPage extends PagedResponse, TItem>(error: PixivError, http: HttpClient, getItems: (page: TPage) => TItem[]): PaginatedResultAsync<TPage, TItem>;
448
448
 
449
+ /**
450
+ * Typed cursor parameters extracted from a pixiv `next_url`.
451
+ *
452
+ * Different endpoints use different cursor fields; only the fields present
453
+ * in the URL will be defined.
454
+ *
455
+ * | Field | Endpoint(s) |
456
+ * |---|---|
457
+ * | `maxBookmarkId` | `GET /v1/user/bookmarks/illust` |
458
+ * | `maxBookmarkIdForRecommend` | `GET /v1/illust/recommended`, `GET /v1/novel/recommended` |
459
+ * | `minBookmarkIdForRecentIllust` | `GET /v1/illust/recommended` |
460
+ * | `offset` | search, ranking, recommended, user lists, … |
461
+ * | `lastOrder` | `GET /v2/novel/series` |
462
+ */
463
+ interface ParsedNextUrl {
464
+ /** Cursor for `GET /v1/user/bookmarks/illust`. */
465
+ maxBookmarkId?: number;
466
+ /** Cursor for `GET /v1/illust/recommended` and `GET /v1/novel/recommended`. */
467
+ maxBookmarkIdForRecommend?: number;
468
+ /** Secondary cursor for `GET /v1/illust/recommended`. */
469
+ minBookmarkIdForRecentIllust?: number;
470
+ /** Zero-based offset for general list endpoints. */
471
+ offset?: number;
472
+ /** Cursor for `GET /v2/novel/series`. */
473
+ lastOrder?: number;
474
+ }
475
+ /**
476
+ * Parses a pixiv `next_url` into a typed cursor object.
477
+ *
478
+ * Pass the `next_url` field from any paginated response to extract the
479
+ * cursor parameters needed to resume pagination from a saved position.
480
+ *
481
+ * @example
482
+ * ```ts
483
+ * const page = await client.users.bookmarks.illusts({ userId: client.userId })
484
+ * if (page.isOk && page.value.next_url) {
485
+ * const cursor = parseNextUrl(page.value.next_url)
486
+ * // Resume later:
487
+ * const next = await client.users.bookmarks.illusts({
488
+ * userId: client.userId,
489
+ * maxBookmarkId: cursor.maxBookmarkId,
490
+ * })
491
+ * }
492
+ * ```
493
+ *
494
+ * @param url - The `next_url` string returned by a pixiv list endpoint
495
+ * @returns Typed cursor parameters; fields absent in the URL are `undefined`
496
+ */
497
+ declare function parseNextUrl(url: string): ParsedNextUrl;
498
+
449
499
  /**
450
500
  * Public option types for @book000/pixivts.
451
501
  *
452
- * All types are string literal unions NOT TypeScript enums so callers
453
- * do not need to import a runtime value and the values are transparent in
454
- * serialised output.
502
+ * Each option is exported as both a runtime `const` object (for enum-like
503
+ * access, e.g. `BookmarkRestrict.PUBLIC`) and a TypeScript `type` (so plain
504
+ * string literals such as `'public'` are also accepted).
505
+ *
506
+ * @example
507
+ * ```ts
508
+ * // Enum-like usage
509
+ * await client.illusts.bookmarkAdd({ illustId: 123, restrict: BookmarkRestrict.PUBLIC })
510
+ *
511
+ * // Plain string literal — also valid
512
+ * await client.illusts.bookmarkAdd({ illustId: 123, restrict: 'public' })
513
+ * ```
455
514
  */
456
515
  /**
457
516
  * Search match target for illust / novel searches.
@@ -461,7 +520,13 @@ declare function failedPaginated<TPage extends PagedResponse, TItem>(error: Pixi
461
520
  * - `title_and_caption` — title or caption contains the word
462
521
  * - `keyword` — general keyword search (novel only)
463
522
  */
464
- type SearchTarget = 'partial_match_for_tags' | 'exact_match_for_tags' | 'title_and_caption' | 'keyword';
523
+ declare const SearchTarget: {
524
+ readonly PARTIAL_MATCH_FOR_TAGS: "partial_match_for_tags";
525
+ readonly EXACT_MATCH_FOR_TAGS: "exact_match_for_tags";
526
+ readonly TITLE_AND_CAPTION: "title_and_caption";
527
+ readonly KEYWORD: "keyword";
528
+ };
529
+ type SearchTarget = (typeof SearchTarget)[keyof typeof SearchTarget];
465
530
  /**
466
531
  * Sort order for search results.
467
532
  *
@@ -469,7 +534,12 @@ type SearchTarget = 'partial_match_for_tags' | 'exact_match_for_tags' | 'title_a
469
534
  * - `date_asc` — oldest first
470
535
  * - `popular_desc` — most bookmarks first (premium only)
471
536
  */
472
- type SearchSort = 'date_desc' | 'date_asc' | 'popular_desc';
537
+ declare const SearchSort: {
538
+ readonly DATE_DESC: "date_desc";
539
+ readonly DATE_ASC: "date_asc";
540
+ readonly POPULAR_DESC: "popular_desc";
541
+ };
542
+ type SearchSort = (typeof SearchSort)[keyof typeof SearchSort];
473
543
  /**
474
544
  * Date range filter for search results.
475
545
  *
@@ -477,47 +547,93 @@ type SearchSort = 'date_desc' | 'date_asc' | 'popular_desc';
477
547
  * - `within_last_week` — past 7 days
478
548
  * - `within_last_month` — past 30 days
479
549
  */
480
- type SearchDuration = 'within_last_day' | 'within_last_week' | 'within_last_month';
550
+ declare const SearchDuration: {
551
+ readonly WITHIN_LAST_DAY: "within_last_day";
552
+ readonly WITHIN_LAST_WEEK: "within_last_week";
553
+ readonly WITHIN_LAST_MONTH: "within_last_month";
554
+ };
555
+ type SearchDuration = (typeof SearchDuration)[keyof typeof SearchDuration];
481
556
  /**
482
557
  * Ranking mode for illust rankings.
483
558
  *
484
559
  * R-18 modes require a premium account with R-18 content enabled.
485
560
  */
486
- type RankingMode = 'day' | 'day_male' | 'day_female' | 'week_original' | 'week_rookie' | 'week' | 'month' | 'day_ai' | 'day_r18' | 'week_r18' | 'day_male_r18' | 'day_female_r18' | 'day_r18_ai';
561
+ declare const RankingMode: {
562
+ readonly DAY: "day";
563
+ readonly DAY_MALE: "day_male";
564
+ readonly DAY_FEMALE: "day_female";
565
+ readonly WEEK_ORIGINAL: "week_original";
566
+ readonly WEEK_ROOKIE: "week_rookie";
567
+ readonly WEEK: "week";
568
+ readonly MONTH: "month";
569
+ readonly DAY_AI: "day_ai";
570
+ readonly DAY_R18: "day_r18";
571
+ readonly WEEK_R18: "week_r18";
572
+ readonly DAY_MALE_R18: "day_male_r18";
573
+ readonly DAY_FEMALE_R18: "day_female_r18";
574
+ readonly DAY_R18_AI: "day_r18_ai";
575
+ };
576
+ type RankingMode = (typeof RankingMode)[keyof typeof RankingMode];
487
577
  /**
488
578
  * Ranking mode for novel rankings.
489
579
  *
490
580
  * R-18 modes require a premium account with R-18 content enabled.
491
581
  */
492
- type NovelRankingMode = 'day' | 'week' | 'day_male' | 'day_female' | 'week_rookie' | 'day_r18' | 'week_r18' | 'day_r18_ai';
582
+ declare const NovelRankingMode: {
583
+ readonly DAY: "day";
584
+ readonly WEEK: "week";
585
+ readonly DAY_MALE: "day_male";
586
+ readonly DAY_FEMALE: "day_female";
587
+ readonly WEEK_ROOKIE: "week_rookie";
588
+ readonly DAY_R18: "day_r18";
589
+ readonly WEEK_R18: "week_r18";
590
+ readonly DAY_R18_AI: "day_r18_ai";
591
+ };
592
+ type NovelRankingMode = (typeof NovelRankingMode)[keyof typeof NovelRankingMode];
493
593
  /**
494
594
  * Visibility restriction for bookmarks.
495
595
  *
496
596
  * - `public` — publicly visible (default)
497
597
  * - `private` — visible only to the owner
498
598
  */
499
- type BookmarkRestrict = 'public' | 'private';
599
+ declare const BookmarkRestrict: {
600
+ readonly PUBLIC: "public";
601
+ readonly PRIVATE: "private";
602
+ };
603
+ type BookmarkRestrict = (typeof BookmarkRestrict)[keyof typeof BookmarkRestrict];
500
604
  /**
501
605
  * Visibility restriction for follows.
502
606
  *
503
607
  * - `public` — publicly visible (default)
504
608
  * - `private` — visible only to the owner
505
609
  */
506
- type FollowRestrict = 'public' | 'private';
610
+ declare const FollowRestrict: {
611
+ readonly PUBLIC: "public";
612
+ readonly PRIVATE: "private";
613
+ };
614
+ type FollowRestrict = (typeof FollowRestrict)[keyof typeof FollowRestrict];
507
615
  /**
508
616
  * OS filter used to request works compatible with the given platform.
509
617
  *
510
618
  * - `for_ios` — iOS-compatible works (default)
511
619
  * - `for_android` — Android-compatible works
512
620
  */
513
- type OSFilter = 'for_ios' | 'for_android';
621
+ declare const OSFilter: {
622
+ readonly FOR_IOS: "for_ios";
623
+ readonly FOR_ANDROID: "for_android";
624
+ };
625
+ type OSFilter = (typeof OSFilter)[keyof typeof OSFilter];
514
626
  /**
515
627
  * Work type filter for user illust listings.
516
628
  *
517
629
  * - `illust` — illustrations only
518
630
  * - `manga` — manga only
519
631
  */
520
- type UserIllustType = 'illust' | 'manga';
632
+ declare const UserIllustType: {
633
+ readonly ILLUST: "illust";
634
+ readonly MANGA: "manga";
635
+ };
636
+ type UserIllustType = (typeof UserIllustType)[keyof typeof UserIllustType];
521
637
 
522
638
  /**
523
639
  * Public types for @book000/pixivts.
@@ -1155,6 +1271,16 @@ interface IllustRecommendedParams {
1155
1271
  filter?: OSFilter;
1156
1272
  /** Zero-based offset for pagination. */
1157
1273
  offset?: number;
1274
+ /**
1275
+ * Cursor for resuming pagination: the `maxBookmarkIdForRecommend` value
1276
+ * extracted from a previous page's `next_url` via {@link parseNextUrl}.
1277
+ */
1278
+ maxBookmarkIdForRecommend?: number;
1279
+ /**
1280
+ * Secondary cursor for resuming pagination: the `minBookmarkIdForRecentIllust`
1281
+ * value extracted from a previous page's `next_url` via {@link parseNextUrl}.
1282
+ */
1283
+ minBookmarkIdForRecentIllust?: number;
1158
1284
  }
1159
1285
  /** Parameters for fetching an illust series. */
1160
1286
  interface IllustSeriesParams {
@@ -1186,6 +1312,16 @@ declare class IllustResource {
1186
1312
  * GET /v1/illust/detail
1187
1313
  *
1188
1314
  * @param params - Request parameters
1315
+ *
1316
+ * @example
1317
+ * ```ts
1318
+ * const result = await client.illusts.detail({ illustId: 12345 })
1319
+ * if (result.isOk) {
1320
+ * console.log(result.value.illust.title)
1321
+ * } else {
1322
+ * console.error(result.error)
1323
+ * }
1324
+ * ```
1189
1325
  */
1190
1326
  detail(params: IllustDetailParams): ResultAsync<IllustDetailResponse, PixivError>;
1191
1327
  /**
@@ -1200,6 +1336,20 @@ declare class IllustResource {
1200
1336
  * GET /v1/search/illust
1201
1337
  *
1202
1338
  * @param params - Request parameters
1339
+ *
1340
+ * @example
1341
+ * ```ts
1342
+ * // Iterate all results across pages
1343
+ * for await (const illust of client.illusts.search({ word: 'cat' }).items()) {
1344
+ * console.log(illust.title)
1345
+ * }
1346
+ *
1347
+ * // Fetch only the first page
1348
+ * const page = await client.illusts.search({ word: 'cat' })
1349
+ * if (page.isOk) {
1350
+ * console.log(page.value.illusts.length)
1351
+ * }
1352
+ * ```
1203
1353
  */
1204
1354
  search(params: IllustSearchParams): PaginatedResultAsync<IllustListPage, IllustListPage['illusts'][number]>;
1205
1355
  /**
@@ -1296,6 +1446,11 @@ interface NovelRecommendedParams {
1296
1446
  filter?: OSFilter;
1297
1447
  /** Zero-based offset for pagination. */
1298
1448
  offset?: number;
1449
+ /**
1450
+ * Cursor for resuming pagination: the `maxBookmarkIdForRecommend` value
1451
+ * extracted from a previous page's `next_url` via {@link parseNextUrl}.
1452
+ */
1453
+ maxBookmarkIdForRecommend?: number;
1299
1454
  }
1300
1455
  /** Parameters for fetching a novel series. */
1301
1456
  interface NovelSeriesParams {
@@ -1327,6 +1482,16 @@ declare class NovelResource {
1327
1482
  * GET /v2/novel/detail
1328
1483
  *
1329
1484
  * @param params - Request parameters
1485
+ *
1486
+ * @example
1487
+ * ```ts
1488
+ * const result = await client.novels.detail({ novelId: 67890 })
1489
+ * if (result.isOk) {
1490
+ * console.log(result.value.novel.title)
1491
+ * } else {
1492
+ * console.error(result.error)
1493
+ * }
1494
+ * ```
1330
1495
  */
1331
1496
  detail(params: NovelDetailParams): ResultAsync<NovelDetailResponse, PixivError>;
1332
1497
  /**
@@ -1351,6 +1516,20 @@ declare class NovelResource {
1351
1516
  * GET /v1/search/novel
1352
1517
  *
1353
1518
  * @param params - Request parameters
1519
+ *
1520
+ * @example
1521
+ * ```ts
1522
+ * // Iterate all results across pages
1523
+ * for await (const novel of client.novels.search({ word: 'fantasy' }).items()) {
1524
+ * console.log(novel.title)
1525
+ * }
1526
+ *
1527
+ * // Fetch only the first page
1528
+ * const page = await client.novels.search({ word: 'fantasy' })
1529
+ * if (page.isOk) {
1530
+ * console.log(page.value.novels.length)
1531
+ * }
1532
+ * ```
1354
1533
  */
1355
1534
  search(params: NovelSearchParams): PaginatedResultAsync<NovelListPage, PixivNovelItem>;
1356
1535
  /**
@@ -1481,6 +1660,25 @@ declare class UserBookmarksResource {
1481
1660
  * GET /v1/user/bookmarks/illust
1482
1661
  *
1483
1662
  * @param params - Request parameters
1663
+ *
1664
+ * @example
1665
+ * ```ts
1666
+ * // Iterate all bookmarked illusts across pages
1667
+ * for await (const illust of client.users.bookmarks.illusts({ userId: client.userId }).items()) {
1668
+ * console.log(illust.title)
1669
+ * }
1670
+ *
1671
+ * // Resume from a saved cursor
1672
+ * import { parseNextUrl } from '@book000/pixivts'
1673
+ * const page = await client.users.bookmarks.illusts({ userId: client.userId })
1674
+ * if (page.isOk && page.value.next_url) {
1675
+ * const cursor = parseNextUrl(page.value.next_url)
1676
+ * const next = await client.users.bookmarks.illusts({
1677
+ * userId: client.userId,
1678
+ * maxBookmarkId: cursor.maxBookmarkId,
1679
+ * })
1680
+ * }
1681
+ * ```
1484
1682
  */
1485
1683
  illusts(params: UserBookmarksIllustParams): PaginatedResultAsync<UserBookmarksIllustPage, PixivIllustItem>;
1486
1684
  /**
@@ -1488,6 +1686,14 @@ declare class UserBookmarksResource {
1488
1686
  * GET /v1/user/bookmarks/novel
1489
1687
  *
1490
1688
  * @param params - Request parameters
1689
+ *
1690
+ * @example
1691
+ * ```ts
1692
+ * // Iterate all bookmarked novels across pages
1693
+ * for await (const novel of client.users.bookmarks.novels({ userId: client.userId }).items()) {
1694
+ * console.log(novel.title)
1695
+ * }
1696
+ * ```
1491
1697
  */
1492
1698
  novels(params: UserBookmarksNovelParams): PaginatedResultAsync<UserBookmarksNovelPage, PixivNovelItem>;
1493
1699
  }
@@ -1646,11 +1852,20 @@ declare class PixivClient {
1646
1852
  readonly images: ImageResource;
1647
1853
  private constructor();
1648
1854
  /**
1649
- * Numeric user ID of the authenticated account, as a string.
1855
+ * Numeric user ID of the authenticated account.
1650
1856
  *
1651
1857
  * Available immediately after {@link PixivClient.of} resolves.
1858
+ * The pixiv OAuth endpoint returns the ID as a string; this getter
1859
+ * normalises it to `number` for consistency with resource method params
1860
+ * (e.g. `UserBookmarksIllustParams.userId`).
1861
+ *
1862
+ * @example
1863
+ * ```ts
1864
+ * const client = await PixivClient.of(refreshToken)
1865
+ * const bookmarks = await client.users.bookmarks.illusts({ userId: client.userId })
1866
+ * ```
1652
1867
  */
1653
- get userId(): string;
1868
+ get userId(): number;
1654
1869
  /**
1655
1870
  * Creates a PixivClient by refreshing the given token.
1656
1871
  *
@@ -1661,4 +1876,4 @@ declare class PixivClient {
1661
1876
  static of(refreshToken: string, options?: PixivClientOptions): Promise<PixivClient>;
1662
1877
  }
1663
1878
 
1664
- export { type BookmarkRestrict, type ErrResult, type FollowRestrict, type Frame, type HttpMethod, type IllustBookmarkAddParams, type IllustBookmarkDeleteParams, type IllustDetailParams, type IllustDetailResponse, type IllustListPage, type IllustRankingParams, type IllustRecommendedPage, type IllustRecommendedParams, type IllustRelatedParams, type IllustSearchParams, type IllustSeriesDetail, type IllustSeriesPage, type IllustSeriesParams, type ImageUrls, type MangaRecommendedPage, type MetaPages, type MetaSinglePage, type NovelBookmarkAddParams, type NovelBookmarkDeleteParams, type NovelDetailParams, type NovelDetailResponse, type NovelListPage, type NovelRankingMode, type NovelRankingParams, type NovelRecommendedPage, type NovelRecommendedParams, type NovelRelatedParams, type NovelSearchParams, type NovelSeriesDetail, type NovelSeriesPage, type NovelSeriesParams, type NovelTextParams, type OSFilter, type OkResult, type PagedResponse, PaginatedResultAsync, type PixivApiErrorBody, PixivClient, type PixivClientOptions, type PixivError, PixivFetchError, type PixivIllustItem, type PixivNovelItem, type PixivUgoiraItem, type PixivUser, type PixivUserItem, type PixivUserPreviewItem, type PixivUserProfile, type PixivUserProfilePublicity, type PixivUserProfileWorkspace, type PrivacyPolicy, type ProfileImageUrls, type RankingMode, type ResponseInterceptor, type ResponseRecord, type Result, ResultAsync, type SearchDuration, type SearchSort, type SearchTarget, type Series, type Tag, type UgoiraMetadataResponse, type UserBookmarksIllustPage, type UserBookmarksIllustParams, type UserBookmarksNovelPage, type UserBookmarksNovelParams, type UserDetailParams, type UserDetailResponse, type UserFollowAddParams, type UserFollowDeleteParams, type UserFollowingPage, type UserFollowingParams, type UserIllustType, type UserIllustsPage, type UserIllustsParams, type UserNovelsPage, type UserNovelsParams, type ZipUrls, apiError, authFailedError, err, failedPaginated, networkError, ok, rateLimitError };
1879
+ export { BookmarkRestrict, type ErrResult, FollowRestrict, type Frame, type HttpMethod, type IllustBookmarkAddParams, type IllustBookmarkDeleteParams, type IllustDetailParams, type IllustDetailResponse, type IllustListPage, type IllustRankingParams, type IllustRecommendedPage, type IllustRecommendedParams, type IllustRelatedParams, type IllustSearchParams, type IllustSeriesDetail, type IllustSeriesPage, type IllustSeriesParams, type ImageUrls, type MangaRecommendedPage, type MetaPages, type MetaSinglePage, type NovelBookmarkAddParams, type NovelBookmarkDeleteParams, type NovelDetailParams, type NovelDetailResponse, type NovelListPage, NovelRankingMode, type NovelRankingParams, type NovelRecommendedPage, type NovelRecommendedParams, type NovelRelatedParams, type NovelSearchParams, type NovelSeriesDetail, type NovelSeriesPage, type NovelSeriesParams, type NovelTextParams, OSFilter, type OkResult, type PagedResponse, PaginatedResultAsync, type ParsedNextUrl, type PixivApiErrorBody, PixivClient, type PixivClientOptions, type PixivError, PixivFetchError, type PixivIllustItem, type PixivNovelItem, type PixivUgoiraItem, type PixivUser, type PixivUserItem, type PixivUserPreviewItem, type PixivUserProfile, type PixivUserProfilePublicity, type PixivUserProfileWorkspace, type PrivacyPolicy, type ProfileImageUrls, RankingMode, type ResponseInterceptor, type ResponseRecord, type Result, ResultAsync, SearchDuration, SearchSort, SearchTarget, type Series, type Tag, type UgoiraMetadataResponse, type UserBookmarksIllustPage, type UserBookmarksIllustParams, type UserBookmarksNovelPage, type UserBookmarksNovelParams, type UserDetailParams, type UserDetailResponse, type UserFollowAddParams, type UserFollowDeleteParams, type UserFollowingPage, type UserFollowingParams, UserIllustType, type UserIllustsPage, type UserIllustsParams, type UserNovelsPage, type UserNovelsParams, type ZipUrls, apiError, authFailedError, err, failedPaginated, networkError, ok, parseNextUrl, rateLimitError };
package/dist/index.js CHANGED
@@ -255,6 +255,115 @@ function failedPaginated(error, http, getItems) {
255
255
  );
256
256
  }
257
257
 
258
+ // src/params.ts
259
+ function camelToSnake(key) {
260
+ return key.replaceAll(/([A-Z])/g, (m) => `_${m.toLowerCase()}`);
261
+ }
262
+ function toSnakeKeys(obj) {
263
+ const out = {};
264
+ for (const key of Object.keys(obj)) {
265
+ out[camelToSnake(key)] = obj[key];
266
+ }
267
+ return out;
268
+ }
269
+ function buildSearchParams(params) {
270
+ const usp = new URLSearchParams();
271
+ for (const [key, value] of Object.entries(params)) {
272
+ if (value === null || value === void 0) continue;
273
+ if (Array.isArray(value)) {
274
+ for (const item of value) usp.append(`${key}[]`, String(item));
275
+ } else {
276
+ usp.set(key, String(value));
277
+ }
278
+ }
279
+ return usp;
280
+ }
281
+ function buildParams(params) {
282
+ return buildSearchParams(toSnakeKeys(params));
283
+ }
284
+ function parseNextUrl(url) {
285
+ const usp = new URL(url).searchParams;
286
+ const result = {};
287
+ const toNum = (key) => {
288
+ const v = usp.get(key);
289
+ if (v === null || v === "") return void 0;
290
+ const n = Number(v);
291
+ return Number.isNaN(n) ? void 0 : n;
292
+ };
293
+ const maxBookmarkId = toNum("max_bookmark_id");
294
+ if (maxBookmarkId !== void 0) result.maxBookmarkId = maxBookmarkId;
295
+ const maxBookmarkIdForRecommend = toNum("max_bookmark_id_for_recommend");
296
+ if (maxBookmarkIdForRecommend !== void 0)
297
+ result.maxBookmarkIdForRecommend = maxBookmarkIdForRecommend;
298
+ const minBookmarkIdForRecentIllust = toNum("min_bookmark_id_for_recent_illust");
299
+ if (minBookmarkIdForRecentIllust !== void 0)
300
+ result.minBookmarkIdForRecentIllust = minBookmarkIdForRecentIllust;
301
+ const offset = toNum("offset");
302
+ if (offset !== void 0) result.offset = offset;
303
+ const lastOrder = toNum("last_order");
304
+ if (lastOrder !== void 0) result.lastOrder = lastOrder;
305
+ return result;
306
+ }
307
+
308
+ // src/options.ts
309
+ var SearchTarget = {
310
+ PARTIAL_MATCH_FOR_TAGS: "partial_match_for_tags",
311
+ EXACT_MATCH_FOR_TAGS: "exact_match_for_tags",
312
+ TITLE_AND_CAPTION: "title_and_caption",
313
+ KEYWORD: "keyword"
314
+ };
315
+ var SearchSort = {
316
+ DATE_DESC: "date_desc",
317
+ DATE_ASC: "date_asc",
318
+ POPULAR_DESC: "popular_desc"
319
+ };
320
+ var SearchDuration = {
321
+ WITHIN_LAST_DAY: "within_last_day",
322
+ WITHIN_LAST_WEEK: "within_last_week",
323
+ WITHIN_LAST_MONTH: "within_last_month"
324
+ };
325
+ var RankingMode = {
326
+ DAY: "day",
327
+ DAY_MALE: "day_male",
328
+ DAY_FEMALE: "day_female",
329
+ WEEK_ORIGINAL: "week_original",
330
+ WEEK_ROOKIE: "week_rookie",
331
+ WEEK: "week",
332
+ MONTH: "month",
333
+ DAY_AI: "day_ai",
334
+ DAY_R18: "day_r18",
335
+ WEEK_R18: "week_r18",
336
+ DAY_MALE_R18: "day_male_r18",
337
+ DAY_FEMALE_R18: "day_female_r18",
338
+ DAY_R18_AI: "day_r18_ai"
339
+ };
340
+ var NovelRankingMode = {
341
+ DAY: "day",
342
+ WEEK: "week",
343
+ DAY_MALE: "day_male",
344
+ DAY_FEMALE: "day_female",
345
+ WEEK_ROOKIE: "week_rookie",
346
+ DAY_R18: "day_r18",
347
+ WEEK_R18: "week_r18",
348
+ DAY_R18_AI: "day_r18_ai"
349
+ };
350
+ var BookmarkRestrict = {
351
+ PUBLIC: "public",
352
+ PRIVATE: "private"
353
+ };
354
+ var FollowRestrict = {
355
+ PUBLIC: "public",
356
+ PRIVATE: "private"
357
+ };
358
+ var OSFilter = {
359
+ FOR_IOS: "for_ios",
360
+ FOR_ANDROID: "for_android"
361
+ };
362
+ var UserIllustType = {
363
+ ILLUST: "illust",
364
+ MANGA: "manga"
365
+ };
366
+
258
367
  // src/auth.ts
259
368
  var T = Array.from(
260
369
  { length: 64 },
@@ -679,33 +788,6 @@ var HttpClient = class {
679
788
  }
680
789
  };
681
790
 
682
- // src/params.ts
683
- function camelToSnake(key) {
684
- return key.replaceAll(/([A-Z])/g, (m) => `_${m.toLowerCase()}`);
685
- }
686
- function toSnakeKeys(obj) {
687
- const out = {};
688
- for (const key of Object.keys(obj)) {
689
- out[camelToSnake(key)] = obj[key];
690
- }
691
- return out;
692
- }
693
- function buildSearchParams(params) {
694
- const usp = new URLSearchParams();
695
- for (const [key, value] of Object.entries(params)) {
696
- if (value === null || value === void 0) continue;
697
- if (Array.isArray(value)) {
698
- for (const item of value) usp.append(`${key}[]`, String(item));
699
- } else {
700
- usp.set(key, String(value));
701
- }
702
- }
703
- return usp;
704
- }
705
- function buildParams(params) {
706
- return buildSearchParams(toSnakeKeys(params));
707
- }
708
-
709
791
  // src/resources/illusts.ts
710
792
  var IllustResource = class {
711
793
  #http;
@@ -717,6 +799,16 @@ var IllustResource = class {
717
799
  * GET /v1/illust/detail
718
800
  *
719
801
  * @param params - Request parameters
802
+ *
803
+ * @example
804
+ * ```ts
805
+ * const result = await client.illusts.detail({ illustId: 12345 })
806
+ * if (result.isOk) {
807
+ * console.log(result.value.illust.title)
808
+ * } else {
809
+ * console.error(result.error)
810
+ * }
811
+ * ```
720
812
  */
721
813
  detail(params) {
722
814
  return this.#http.get(
@@ -749,6 +841,20 @@ var IllustResource = class {
749
841
  * GET /v1/search/illust
750
842
  *
751
843
  * @param params - Request parameters
844
+ *
845
+ * @example
846
+ * ```ts
847
+ * // Iterate all results across pages
848
+ * for await (const illust of client.illusts.search({ word: 'cat' }).items()) {
849
+ * console.log(illust.title)
850
+ * }
851
+ *
852
+ * // Fetch only the first page
853
+ * const page = await client.illusts.search({ word: 'cat' })
854
+ * if (page.isOk) {
855
+ * console.log(page.value.illusts.length)
856
+ * }
857
+ * ```
752
858
  */
753
859
  search(params) {
754
860
  return PaginatedResultAsync.fromResultAsync(
@@ -806,7 +912,9 @@ var IllustResource = class {
806
912
  includeRankingLabel: true,
807
913
  includeRankingIllusts: true,
808
914
  includePrivacyPolicy: true,
809
- offset: params.offset
915
+ offset: params.offset,
916
+ maxBookmarkIdForRecommend: params.maxBookmarkIdForRecommend,
917
+ minBookmarkIdForRecentIllust: params.minBookmarkIdForRecentIllust
810
918
  })
811
919
  ),
812
920
  this.#http,
@@ -875,6 +983,16 @@ var NovelResource = class {
875
983
  * GET /v2/novel/detail
876
984
  *
877
985
  * @param params - Request parameters
986
+ *
987
+ * @example
988
+ * ```ts
989
+ * const result = await client.novels.detail({ novelId: 67890 })
990
+ * if (result.isOk) {
991
+ * console.log(result.value.novel.title)
992
+ * } else {
993
+ * console.error(result.error)
994
+ * }
995
+ * ```
878
996
  */
879
997
  detail(params) {
880
998
  return this.#http.get(
@@ -919,6 +1037,20 @@ var NovelResource = class {
919
1037
  * GET /v1/search/novel
920
1038
  *
921
1039
  * @param params - Request parameters
1040
+ *
1041
+ * @example
1042
+ * ```ts
1043
+ * // Iterate all results across pages
1044
+ * for await (const novel of client.novels.search({ word: 'fantasy' }).items()) {
1045
+ * console.log(novel.title)
1046
+ * }
1047
+ *
1048
+ * // Fetch only the first page
1049
+ * const page = await client.novels.search({ word: 'fantasy' })
1050
+ * if (page.isOk) {
1051
+ * console.log(page.value.novels.length)
1052
+ * }
1053
+ * ```
922
1054
  */
923
1055
  search(params) {
924
1056
  return PaginatedResultAsync.fromResultAsync(
@@ -975,7 +1107,8 @@ var NovelResource = class {
975
1107
  filter: params.filter ?? "for_ios",
976
1108
  includeRankingNovels: true,
977
1109
  includePrivacyPolicy: true,
978
- offset: params.offset
1110
+ offset: params.offset,
1111
+ maxBookmarkIdForRecommend: params.maxBookmarkIdForRecommend
979
1112
  })
980
1113
  ),
981
1114
  this.#http,
@@ -1041,6 +1174,25 @@ var UserBookmarksResource = class {
1041
1174
  * GET /v1/user/bookmarks/illust
1042
1175
  *
1043
1176
  * @param params - Request parameters
1177
+ *
1178
+ * @example
1179
+ * ```ts
1180
+ * // Iterate all bookmarked illusts across pages
1181
+ * for await (const illust of client.users.bookmarks.illusts({ userId: client.userId }).items()) {
1182
+ * console.log(illust.title)
1183
+ * }
1184
+ *
1185
+ * // Resume from a saved cursor
1186
+ * import { parseNextUrl } from '@book000/pixivts'
1187
+ * const page = await client.users.bookmarks.illusts({ userId: client.userId })
1188
+ * if (page.isOk && page.value.next_url) {
1189
+ * const cursor = parseNextUrl(page.value.next_url)
1190
+ * const next = await client.users.bookmarks.illusts({
1191
+ * userId: client.userId,
1192
+ * maxBookmarkId: cursor.maxBookmarkId,
1193
+ * })
1194
+ * }
1195
+ * ```
1044
1196
  */
1045
1197
  illusts(params) {
1046
1198
  return PaginatedResultAsync.fromResultAsync(
@@ -1064,6 +1216,14 @@ var UserBookmarksResource = class {
1064
1216
  * GET /v1/user/bookmarks/novel
1065
1217
  *
1066
1218
  * @param params - Request parameters
1219
+ *
1220
+ * @example
1221
+ * ```ts
1222
+ * // Iterate all bookmarked novels across pages
1223
+ * for await (const novel of client.users.bookmarks.novels({ userId: client.userId }).items()) {
1224
+ * console.log(novel.title)
1225
+ * }
1226
+ * ```
1067
1227
  */
1068
1228
  novels(params) {
1069
1229
  return PaginatedResultAsync.fromResultAsync(
@@ -1288,12 +1448,25 @@ var PixivClient = class _PixivClient {
1288
1448
  this.images = new ImageResource(http);
1289
1449
  }
1290
1450
  /**
1291
- * Numeric user ID of the authenticated account, as a string.
1451
+ * Numeric user ID of the authenticated account.
1292
1452
  *
1293
1453
  * Available immediately after {@link PixivClient.of} resolves.
1454
+ * The pixiv OAuth endpoint returns the ID as a string; this getter
1455
+ * normalises it to `number` for consistency with resource method params
1456
+ * (e.g. `UserBookmarksIllustParams.userId`).
1457
+ *
1458
+ * @example
1459
+ * ```ts
1460
+ * const client = await PixivClient.of(refreshToken)
1461
+ * const bookmarks = await client.users.bookmarks.illusts({ userId: client.userId })
1462
+ * ```
1294
1463
  */
1295
1464
  get userId() {
1296
- return this.#auth.userId;
1465
+ const id = Number(this.#auth.userId);
1466
+ if (Number.isNaN(id)) {
1467
+ throw new TypeError(`Invalid userId: "${this.#auth.userId}"`);
1468
+ }
1469
+ return id;
1297
1470
  }
1298
1471
  /**
1299
1472
  * Creates a PixivClient by refreshing the given token.
@@ -1309,4 +1482,4 @@ var PixivClient = class _PixivClient {
1309
1482
  }
1310
1483
  };
1311
1484
 
1312
- export { PaginatedResultAsync, PixivClient, PixivFetchError, ResultAsync, apiError, authFailedError, err, failedPaginated, networkError, ok, rateLimitError };
1485
+ export { BookmarkRestrict, FollowRestrict, NovelRankingMode, OSFilter, PaginatedResultAsync, PixivClient, PixivFetchError, RankingMode, ResultAsync, SearchDuration, SearchSort, SearchTarget, UserIllustType, apiError, authFailedError, err, failedPaginated, networkError, ok, parseNextUrl, rateLimitError };