ani-client 1.4.3 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -548
- package/dist/index.d.mts +202 -96
- package/dist/index.d.ts +202 -96
- package/dist/index.js +194 -40
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +192 -41
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -6
package/dist/index.mjs
CHANGED
|
@@ -33,6 +33,13 @@ var MEDIA_FIELDS_BASE = `
|
|
|
33
33
|
studios { nodes { id name isAnimationStudio siteUrl } }
|
|
34
34
|
isAdult
|
|
35
35
|
siteUrl
|
|
36
|
+
nextAiringEpisode {
|
|
37
|
+
id
|
|
38
|
+
airingAt
|
|
39
|
+
episode
|
|
40
|
+
mediaId
|
|
41
|
+
timeUntilAiring
|
|
42
|
+
}
|
|
36
43
|
`;
|
|
37
44
|
var RELATIONS_FIELDS = `
|
|
38
45
|
relations {
|
|
@@ -200,6 +207,10 @@ query (
|
|
|
200
207
|
$seasonYear: Int,
|
|
201
208
|
$genre: String,
|
|
202
209
|
$tag: String,
|
|
210
|
+
$genre_in: [String],
|
|
211
|
+
$tag_in: [String],
|
|
212
|
+
$genre_not_in: [String],
|
|
213
|
+
$tag_not_in: [String],
|
|
203
214
|
$isAdult: Boolean,
|
|
204
215
|
$sort: [MediaSort],
|
|
205
216
|
$page: Int,
|
|
@@ -216,6 +227,10 @@ query (
|
|
|
216
227
|
seasonYear: $seasonYear,
|
|
217
228
|
genre: $genre,
|
|
218
229
|
tag: $tag,
|
|
230
|
+
genre_in: $genre_in,
|
|
231
|
+
tag_in: $tag_in,
|
|
232
|
+
genre_not_in: $genre_not_in,
|
|
233
|
+
tag_not_in: $tag_not_in,
|
|
219
234
|
isAdult: $isAdult,
|
|
220
235
|
sort: $sort
|
|
221
236
|
) {
|
|
@@ -296,6 +311,15 @@ query ($name: String!) {
|
|
|
296
311
|
${USER_FIELDS}
|
|
297
312
|
}
|
|
298
313
|
}`;
|
|
314
|
+
var QUERY_USER_SEARCH = `
|
|
315
|
+
query ($search: String, $sort: [UserSort], $page: Int, $perPage: Int) {
|
|
316
|
+
Page(page: $page, perPage: $perPage) {
|
|
317
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
318
|
+
users(search: $search, sort: $sort) {
|
|
319
|
+
${USER_FIELDS}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}`;
|
|
299
323
|
var QUERY_AIRING_SCHEDULE = `
|
|
300
324
|
query ($airingAt_greater: Int, $airingAt_lesser: Int, $sort: [AiringSort], $page: Int, $perPage: Int) {
|
|
301
325
|
Page(page: $page, perPage: $perPage) {
|
|
@@ -550,6 +574,21 @@ var buildBatchMediaQuery = (ids) => buildBatchQuery(ids, "Media", MEDIA_FIELDS_B
|
|
|
550
574
|
var buildBatchCharacterQuery = (ids) => buildBatchQuery(ids, "Character", CHARACTER_FIELDS, "c");
|
|
551
575
|
var buildBatchStaffQuery = (ids) => buildBatchQuery(ids, "Staff", STAFF_FIELDS, "s");
|
|
552
576
|
|
|
577
|
+
// src/utils/index.ts
|
|
578
|
+
function normalizeQuery(query) {
|
|
579
|
+
return query.replace(/\s+/g, " ").trim();
|
|
580
|
+
}
|
|
581
|
+
function clampPerPage(value) {
|
|
582
|
+
return Math.min(Math.max(value, 1), 50);
|
|
583
|
+
}
|
|
584
|
+
function chunk(arr, size) {
|
|
585
|
+
const chunks = [];
|
|
586
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
587
|
+
chunks.push(arr.slice(i, i + size));
|
|
588
|
+
}
|
|
589
|
+
return chunks;
|
|
590
|
+
}
|
|
591
|
+
|
|
553
592
|
// src/cache/index.ts
|
|
554
593
|
var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
|
|
555
594
|
var MemoryCache = class {
|
|
@@ -561,7 +600,7 @@ var MemoryCache = class {
|
|
|
561
600
|
}
|
|
562
601
|
/** Build a deterministic cache key from a query + variables pair. */
|
|
563
602
|
static key(query, variables) {
|
|
564
|
-
const normalized = query
|
|
603
|
+
const normalized = normalizeQuery(query);
|
|
565
604
|
return `${normalized}|${JSON.stringify(variables, Object.keys(variables).sort())}`;
|
|
566
605
|
}
|
|
567
606
|
/** Retrieve a cached value, or `undefined` if missing / expired. */
|
|
@@ -606,14 +645,17 @@ var MemoryCache = class {
|
|
|
606
645
|
/**
|
|
607
646
|
* Remove all entries whose key matches the given pattern.
|
|
608
647
|
*
|
|
609
|
-
*
|
|
648
|
+
* - **String**: treated as a substring match (e.g. `"Media"` removes all keys containing `"Media"`).
|
|
649
|
+
* - **RegExp**: tested against each key directly.
|
|
650
|
+
*
|
|
651
|
+
* @param pattern — A string (substring match) or RegExp.
|
|
610
652
|
* @returns Number of entries removed.
|
|
611
653
|
*/
|
|
612
654
|
invalidate(pattern) {
|
|
613
|
-
const
|
|
655
|
+
const test = typeof pattern === "string" ? (key) => key.includes(pattern) : (key) => pattern.test(key);
|
|
614
656
|
const toDelete = [];
|
|
615
657
|
for (const key of this.store.keys()) {
|
|
616
|
-
if (
|
|
658
|
+
if (test(key)) toDelete.push(key);
|
|
617
659
|
}
|
|
618
660
|
for (const key of toDelete) this.store.delete(key);
|
|
619
661
|
return toDelete.length;
|
|
@@ -637,8 +679,8 @@ var AniListError = class _AniListError extends Error {
|
|
|
637
679
|
// src/rate-limiter/index.ts
|
|
638
680
|
var RateLimiter = class {
|
|
639
681
|
constructor(options = {}) {
|
|
640
|
-
|
|
641
|
-
this.
|
|
682
|
+
this.head = 0;
|
|
683
|
+
this.count = 0;
|
|
642
684
|
this.maxRequests = options.maxRequests ?? 85;
|
|
643
685
|
this.windowMs = options.windowMs ?? 6e4;
|
|
644
686
|
this.maxRetries = options.maxRetries ?? 3;
|
|
@@ -646,24 +688,34 @@ var RateLimiter = class {
|
|
|
646
688
|
this.enabled = options.enabled ?? true;
|
|
647
689
|
this.timeoutMs = options.timeoutMs ?? 3e4;
|
|
648
690
|
this.retryOnNetworkError = options.retryOnNetworkError ?? true;
|
|
691
|
+
this.timestamps = new Array(this.maxRequests).fill(0);
|
|
649
692
|
}
|
|
650
693
|
/**
|
|
651
694
|
* Wait until it's safe to make a request (respects rate limit window).
|
|
652
695
|
*/
|
|
653
696
|
async acquire() {
|
|
654
697
|
if (!this.enabled) return;
|
|
698
|
+
if (this.count >= this.maxRequests) {
|
|
699
|
+
const oldest = this.timestamps[this.head];
|
|
700
|
+
const now2 = Date.now();
|
|
701
|
+
const elapsed = now2 - oldest;
|
|
702
|
+
if (elapsed < this.windowMs) {
|
|
703
|
+
const waitMs = this.windowMs - elapsed + 50;
|
|
704
|
+
await this.sleep(waitMs);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
655
707
|
const now = Date.now();
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
708
|
+
if (this.count < this.maxRequests) {
|
|
709
|
+
this.timestamps[(this.head + this.count) % this.maxRequests] = now;
|
|
710
|
+
this.count++;
|
|
711
|
+
} else {
|
|
712
|
+
this.timestamps[this.head] = now;
|
|
713
|
+
this.head = (this.head + 1) % this.maxRequests;
|
|
662
714
|
}
|
|
663
|
-
this.timestamps.push(Date.now());
|
|
664
715
|
}
|
|
665
716
|
/**
|
|
666
717
|
* Execute a fetch with automatic retry on 429 responses and network errors.
|
|
718
|
+
* Uses exponential backoff with jitter for retry delays.
|
|
667
719
|
*/
|
|
668
720
|
async fetchWithRetry(url, init, hooks) {
|
|
669
721
|
await this.acquire();
|
|
@@ -676,7 +728,7 @@ var RateLimiter = class {
|
|
|
676
728
|
lastResponse = res;
|
|
677
729
|
if (attempt === this.maxRetries) break;
|
|
678
730
|
const retryAfter = res.headers.get("Retry-After");
|
|
679
|
-
const delayMs = retryAfter ? Number.parseInt(retryAfter, 10) * 1e3 : this.
|
|
731
|
+
const delayMs = retryAfter ? Number.parseInt(retryAfter, 10) * 1e3 : this.exponentialDelay(attempt);
|
|
680
732
|
hooks?.onRateLimit?.(delayMs);
|
|
681
733
|
hooks?.onRetry?.(attempt + 1, "HTTP 429", delayMs);
|
|
682
734
|
await this.sleep(delayMs);
|
|
@@ -684,7 +736,7 @@ var RateLimiter = class {
|
|
|
684
736
|
} catch (err) {
|
|
685
737
|
lastError = err;
|
|
686
738
|
if (this.retryOnNetworkError && isNetworkError(err) && attempt < this.maxRetries) {
|
|
687
|
-
const delayMs = this.
|
|
739
|
+
const delayMs = this.exponentialDelay(attempt);
|
|
688
740
|
hooks?.onRetry?.(attempt + 1, `Network error: ${err.message}`, delayMs);
|
|
689
741
|
await this.sleep(delayMs);
|
|
690
742
|
await this.acquire();
|
|
@@ -696,6 +748,12 @@ var RateLimiter = class {
|
|
|
696
748
|
if (lastResponse) return lastResponse;
|
|
697
749
|
throw lastError;
|
|
698
750
|
}
|
|
751
|
+
/** @internal — Exponential backoff with jitter, capped at 30s */
|
|
752
|
+
exponentialDelay(attempt) {
|
|
753
|
+
const base = this.retryDelayMs * 2 ** attempt;
|
|
754
|
+
const jitter = Math.random() * 1e3;
|
|
755
|
+
return Math.min(base + jitter, 3e4);
|
|
756
|
+
}
|
|
699
757
|
/** @internal */
|
|
700
758
|
async fetchWithTimeout(url, init) {
|
|
701
759
|
if (this.timeoutMs <= 0) return fetch(url, init);
|
|
@@ -735,6 +793,24 @@ var MediaType = /* @__PURE__ */ ((MediaType2) => {
|
|
|
735
793
|
MediaType2["MANGA"] = "MANGA";
|
|
736
794
|
return MediaType2;
|
|
737
795
|
})(MediaType || {});
|
|
796
|
+
var MediaSource = /* @__PURE__ */ ((MediaSource2) => {
|
|
797
|
+
MediaSource2["ORIGINAL"] = "ORIGINAL";
|
|
798
|
+
MediaSource2["MANGA"] = "MANGA";
|
|
799
|
+
MediaSource2["LIGHT_NOVEL"] = "LIGHT_NOVEL";
|
|
800
|
+
MediaSource2["VISUAL_NOVEL"] = "VISUAL_NOVEL";
|
|
801
|
+
MediaSource2["VIDEO_GAME"] = "VIDEO_GAME";
|
|
802
|
+
MediaSource2["OTHER"] = "OTHER";
|
|
803
|
+
MediaSource2["NOVEL"] = "NOVEL";
|
|
804
|
+
MediaSource2["DOUJINSHI"] = "DOUJINSHI";
|
|
805
|
+
MediaSource2["ANIME"] = "ANIME";
|
|
806
|
+
MediaSource2["WEB_NOVEL"] = "WEB_NOVEL";
|
|
807
|
+
MediaSource2["LIVE_ACTION"] = "LIVE_ACTION";
|
|
808
|
+
MediaSource2["GAME"] = "GAME";
|
|
809
|
+
MediaSource2["COMIC"] = "COMIC";
|
|
810
|
+
MediaSource2["MULTIMEDIA_PROJECT"] = "MULTIMEDIA_PROJECT";
|
|
811
|
+
MediaSource2["PICTURE_BOOK"] = "PICTURE_BOOK";
|
|
812
|
+
return MediaSource2;
|
|
813
|
+
})(MediaSource || {});
|
|
738
814
|
var MediaFormat = /* @__PURE__ */ ((MediaFormat2) => {
|
|
739
815
|
MediaFormat2["TV"] = "TV";
|
|
740
816
|
MediaFormat2["TV_SHORT"] = "TV_SHORT";
|
|
@@ -852,6 +928,35 @@ var CharacterRole = /* @__PURE__ */ ((CharacterRole2) => {
|
|
|
852
928
|
return CharacterRole2;
|
|
853
929
|
})(CharacterRole || {});
|
|
854
930
|
|
|
931
|
+
// src/types/staff.ts
|
|
932
|
+
var StaffSort = /* @__PURE__ */ ((StaffSort2) => {
|
|
933
|
+
StaffSort2["ID"] = "ID";
|
|
934
|
+
StaffSort2["ID_DESC"] = "ID_DESC";
|
|
935
|
+
StaffSort2["ROLE"] = "ROLE";
|
|
936
|
+
StaffSort2["ROLE_DESC"] = "ROLE_DESC";
|
|
937
|
+
StaffSort2["LANGUAGE"] = "LANGUAGE";
|
|
938
|
+
StaffSort2["LANGUAGE_DESC"] = "LANGUAGE_DESC";
|
|
939
|
+
StaffSort2["SEARCH_MATCH"] = "SEARCH_MATCH";
|
|
940
|
+
StaffSort2["FAVOURITES"] = "FAVOURITES";
|
|
941
|
+
StaffSort2["FAVOURITES_DESC"] = "FAVOURITES_DESC";
|
|
942
|
+
StaffSort2["RELEVANCE"] = "RELEVANCE";
|
|
943
|
+
return StaffSort2;
|
|
944
|
+
})(StaffSort || {});
|
|
945
|
+
|
|
946
|
+
// src/types/user.ts
|
|
947
|
+
var UserSort = /* @__PURE__ */ ((UserSort2) => {
|
|
948
|
+
UserSort2["ID"] = "ID";
|
|
949
|
+
UserSort2["ID_DESC"] = "ID_DESC";
|
|
950
|
+
UserSort2["USERNAME"] = "USERNAME";
|
|
951
|
+
UserSort2["USERNAME_DESC"] = "USERNAME_DESC";
|
|
952
|
+
UserSort2["WATCHED_TIME"] = "WATCHED_TIME";
|
|
953
|
+
UserSort2["WATCHED_TIME_DESC"] = "WATCHED_TIME_DESC";
|
|
954
|
+
UserSort2["CHAPTERS_READ"] = "CHAPTERS_READ";
|
|
955
|
+
UserSort2["CHAPTERS_READ_DESC"] = "CHAPTERS_READ_DESC";
|
|
956
|
+
UserSort2["SEARCH_MATCH"] = "SEARCH_MATCH";
|
|
957
|
+
return UserSort2;
|
|
958
|
+
})(UserSort || {});
|
|
959
|
+
|
|
855
960
|
// src/types/lists.ts
|
|
856
961
|
var MediaListStatus = /* @__PURE__ */ ((MediaListStatus2) => {
|
|
857
962
|
MediaListStatus2["CURRENT"] = "CURRENT";
|
|
@@ -896,18 +1001,6 @@ var MediaListSort = /* @__PURE__ */ ((MediaListSort2) => {
|
|
|
896
1001
|
return MediaListSort2;
|
|
897
1002
|
})(MediaListSort || {});
|
|
898
1003
|
|
|
899
|
-
// src/utils/index.ts
|
|
900
|
-
function clampPerPage(value) {
|
|
901
|
-
return Math.min(Math.max(value, 1), 50);
|
|
902
|
-
}
|
|
903
|
-
function chunk(arr, size) {
|
|
904
|
-
const chunks = [];
|
|
905
|
-
for (let i = 0; i < arr.length; i += size) {
|
|
906
|
-
chunks.push(arr.slice(i, i + size));
|
|
907
|
-
}
|
|
908
|
-
return chunks;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
1004
|
// src/client/index.ts
|
|
912
1005
|
var DEFAULT_API_URL = "https://graphql.anilist.co";
|
|
913
1006
|
var AniListClient = class {
|
|
@@ -950,7 +1043,7 @@ var AniListClient = class {
|
|
|
950
1043
|
async executeRequest(query, variables, cacheKey) {
|
|
951
1044
|
const start = Date.now();
|
|
952
1045
|
this.hooks.onRequest?.(query, variables);
|
|
953
|
-
const minifiedQuery = query
|
|
1046
|
+
const minifiedQuery = normalizeQuery(query);
|
|
954
1047
|
const res = await this.rateLimiter.fetchWithRetry(
|
|
955
1048
|
this.apiUrl,
|
|
956
1049
|
{
|
|
@@ -1038,10 +1131,19 @@ var AniListClient = class {
|
|
|
1038
1131
|
* ```
|
|
1039
1132
|
*/
|
|
1040
1133
|
async searchMedia(options = {}) {
|
|
1041
|
-
const { query: search, page = 1, perPage = 20, ...filters } = options;
|
|
1134
|
+
const { query: search, page = 1, perPage = 20, genres, tags, genresExclude, tagsExclude, ...filters } = options;
|
|
1042
1135
|
return this.pagedRequest(
|
|
1043
1136
|
QUERY_MEDIA_SEARCH,
|
|
1044
|
-
{
|
|
1137
|
+
{
|
|
1138
|
+
search,
|
|
1139
|
+
...filters,
|
|
1140
|
+
genre_in: genres,
|
|
1141
|
+
tag_in: tags,
|
|
1142
|
+
genre_not_in: genresExclude,
|
|
1143
|
+
tag_not_in: tagsExclude,
|
|
1144
|
+
page,
|
|
1145
|
+
perPage: clampPerPage(perPage)
|
|
1146
|
+
},
|
|
1045
1147
|
"media"
|
|
1046
1148
|
);
|
|
1047
1149
|
}
|
|
@@ -1055,6 +1157,30 @@ var AniListClient = class {
|
|
|
1055
1157
|
async getTrending(type = "ANIME" /* ANIME */, page = 1, perPage = 20) {
|
|
1056
1158
|
return this.pagedRequest(QUERY_TRENDING, { type, page, perPage: clampPerPage(perPage) }, "media");
|
|
1057
1159
|
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Get the most popular anime or manga.
|
|
1162
|
+
*
|
|
1163
|
+
* Convenience wrapper around `searchMedia` with `sort: POPULARITY_DESC`.
|
|
1164
|
+
*
|
|
1165
|
+
* @param type - `MediaType.ANIME` or `MediaType.MANGA` (defaults to ANIME)
|
|
1166
|
+
* @param page - Page number (default 1)
|
|
1167
|
+
* @param perPage - Results per page (default 20, max 50)
|
|
1168
|
+
*/
|
|
1169
|
+
async getPopular(type = "ANIME" /* ANIME */, page = 1, perPage = 20) {
|
|
1170
|
+
return this.searchMedia({ type, sort: ["POPULARITY_DESC" /* POPULARITY_DESC */], page, perPage });
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Get the highest-rated anime or manga.
|
|
1174
|
+
*
|
|
1175
|
+
* Convenience wrapper around `searchMedia` with `sort: SCORE_DESC`.
|
|
1176
|
+
*
|
|
1177
|
+
* @param type - `MediaType.ANIME` or `MediaType.MANGA` (defaults to ANIME)
|
|
1178
|
+
* @param page - Page number (default 1)
|
|
1179
|
+
* @param perPage - Results per page (default 20, max 50)
|
|
1180
|
+
*/
|
|
1181
|
+
async getTopRated(type = "ANIME" /* ANIME */, page = 1, perPage = 20) {
|
|
1182
|
+
return this.searchMedia({ type, sort: ["SCORE_DESC" /* SCORE_DESC */], page, perPage });
|
|
1183
|
+
}
|
|
1058
1184
|
/**
|
|
1059
1185
|
* Fetch a character by AniList ID.
|
|
1060
1186
|
*
|
|
@@ -1150,36 +1276,50 @@ var AniListClient = class {
|
|
|
1150
1276
|
);
|
|
1151
1277
|
}
|
|
1152
1278
|
/**
|
|
1153
|
-
* Fetch a user by AniList ID.
|
|
1279
|
+
* Fetch a user by AniList ID or username.
|
|
1154
1280
|
*
|
|
1155
|
-
* @param
|
|
1281
|
+
* @param idOrName - The AniList user ID (number) or username (string)
|
|
1156
1282
|
* @returns The user object
|
|
1157
1283
|
*
|
|
1158
1284
|
* @example
|
|
1159
1285
|
* ```ts
|
|
1160
1286
|
* const user = await client.getUser(1);
|
|
1287
|
+
* const user2 = await client.getUser("AniList");
|
|
1161
1288
|
* console.log(user.name);
|
|
1162
1289
|
* ```
|
|
1163
1290
|
*/
|
|
1164
|
-
async getUser(
|
|
1165
|
-
|
|
1291
|
+
async getUser(idOrName) {
|
|
1292
|
+
if (typeof idOrName === "number") {
|
|
1293
|
+
const data2 = await this.request(QUERY_USER_BY_ID, { id: idOrName });
|
|
1294
|
+
return data2.User;
|
|
1295
|
+
}
|
|
1296
|
+
const data = await this.request(QUERY_USER_BY_NAME, { name: idOrName });
|
|
1166
1297
|
return data.User;
|
|
1167
1298
|
}
|
|
1168
1299
|
/**
|
|
1169
1300
|
* Fetch a user by username.
|
|
1170
1301
|
*
|
|
1302
|
+
* @deprecated Use `getUser(name)` instead.
|
|
1171
1303
|
* @param name - The AniList username
|
|
1172
1304
|
* @returns The user object
|
|
1305
|
+
*/
|
|
1306
|
+
async getUserByName(name) {
|
|
1307
|
+
return this.getUser(name);
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Search for users by name.
|
|
1311
|
+
*
|
|
1312
|
+
* @param options - Search / pagination parameters
|
|
1313
|
+
* @returns Paginated results with matching users
|
|
1173
1314
|
*
|
|
1174
1315
|
* @example
|
|
1175
1316
|
* ```ts
|
|
1176
|
-
* const
|
|
1177
|
-
* console.log(user.statistics);
|
|
1317
|
+
* const result = await client.searchUsers({ query: "AniList", perPage: 5 });
|
|
1178
1318
|
* ```
|
|
1179
1319
|
*/
|
|
1180
|
-
async
|
|
1181
|
-
const
|
|
1182
|
-
return
|
|
1320
|
+
async searchUsers(options = {}) {
|
|
1321
|
+
const { query: search, page = 1, perPage = 20, sort } = options;
|
|
1322
|
+
return this.pagedRequest(QUERY_USER_SEARCH, { search, sort, page, perPage: clampPerPage(perPage) }, "users");
|
|
1183
1323
|
}
|
|
1184
1324
|
/**
|
|
1185
1325
|
* Execute an arbitrary GraphQL query against the AniList API.
|
|
@@ -1291,7 +1431,7 @@ var AniListClient = class {
|
|
|
1291
1431
|
mediaId,
|
|
1292
1432
|
sort: options.sort ?? ["RATING_DESC"],
|
|
1293
1433
|
page: options.page ?? 1,
|
|
1294
|
-
perPage: options.perPage ?? 20
|
|
1434
|
+
perPage: clampPerPage(options.perPage ?? 20)
|
|
1295
1435
|
};
|
|
1296
1436
|
const data = await this.request(QUERY_RECOMMENDATIONS, variables);
|
|
1297
1437
|
return {
|
|
@@ -1544,6 +1684,17 @@ var AniListClient = class {
|
|
|
1544
1684
|
}
|
|
1545
1685
|
return count;
|
|
1546
1686
|
}
|
|
1687
|
+
/**
|
|
1688
|
+
* Clean up resources held by the client.
|
|
1689
|
+
*
|
|
1690
|
+
* Clears the in-memory cache and aborts any pending in-flight requests.
|
|
1691
|
+
* If using a custom cache adapter (e.g. Redis), call its close/disconnect
|
|
1692
|
+
* method separately.
|
|
1693
|
+
*/
|
|
1694
|
+
async destroy() {
|
|
1695
|
+
await this.cacheAdapter.clear();
|
|
1696
|
+
this.inFlight.clear();
|
|
1697
|
+
}
|
|
1547
1698
|
};
|
|
1548
1699
|
|
|
1549
1700
|
// src/cache/redis.ts
|
|
@@ -1629,6 +1780,6 @@ var RedisCache = class {
|
|
|
1629
1780
|
}
|
|
1630
1781
|
};
|
|
1631
1782
|
|
|
1632
|
-
export { AiringSort, AniListClient, AniListError, CharacterRole, CharacterSort, MediaFormat, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaStatus, MediaType, MemoryCache, RateLimiter, RecommendationSort, RedisCache };
|
|
1783
|
+
export { AiringSort, AniListClient, AniListError, CharacterRole, CharacterSort, MediaFormat, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaSource, MediaStatus, MediaType, MemoryCache, RateLimiter, RecommendationSort, RedisCache, StaffSort, UserSort };
|
|
1633
1784
|
//# sourceMappingURL=index.mjs.map
|
|
1634
1785
|
//# sourceMappingURL=index.mjs.map
|