ani-client 1.8.0 → 1.9.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.mjs CHANGED
@@ -248,6 +248,95 @@ var MemoryCache = class {
248
248
  }
249
249
  };
250
250
 
251
+ // src/cache/redis.ts
252
+ var RedisCache = class {
253
+ client;
254
+ prefix;
255
+ ttl;
256
+ constructor(options) {
257
+ this.client = options.client;
258
+ this.prefix = options.prefix ?? "ani:";
259
+ this.ttl = options.ttl ?? 86400;
260
+ }
261
+ prefixedKey(key) {
262
+ return `${this.prefix}${key}`;
263
+ }
264
+ async get(key) {
265
+ const raw = await this.client.get(this.prefixedKey(key));
266
+ if (raw === null) return void 0;
267
+ try {
268
+ return JSON.parse(raw);
269
+ } catch {
270
+ return void 0;
271
+ }
272
+ }
273
+ async set(key, data) {
274
+ await this.client.set(this.prefixedKey(key), JSON.stringify(data), "EX", this.ttl);
275
+ }
276
+ async delete(key) {
277
+ const count = await this.client.del(this.prefixedKey(key));
278
+ return count > 0;
279
+ }
280
+ /**
281
+ * Collect keys matching a pattern. Uses SCAN when available, falls back to KEYS.
282
+ *
283
+ * **Warning:** The `KEYS` fallback is O(N) and blocks the Redis server.
284
+ * Provide a client with `scanIterator` support for production use.
285
+ * @internal
286
+ */
287
+ async collectKeys(pattern) {
288
+ if (this.client.scanIterator) {
289
+ const keys = [];
290
+ for await (const key of this.client.scanIterator({ MATCH: pattern, COUNT: 100 })) {
291
+ keys.push(key);
292
+ }
293
+ return keys;
294
+ }
295
+ return this.client.keys(pattern);
296
+ }
297
+ async clear() {
298
+ const keys = await this.collectKeys(`${this.prefix}*`);
299
+ if (keys.length > 0) {
300
+ await this.client.del(...keys);
301
+ }
302
+ }
303
+ /**
304
+ * Get the actual number of keys with this prefix in Redis.
305
+ */
306
+ get size() {
307
+ return this.getSize();
308
+ }
309
+ /** @internal */
310
+ async getSize() {
311
+ const keys = await this.collectKeys(`${this.prefix}*`);
312
+ return keys.length;
313
+ }
314
+ async keys() {
315
+ const raw = await this.collectKeys(`${this.prefix}*`);
316
+ return raw.map((k) => k.slice(this.prefix.length));
317
+ }
318
+ /**
319
+ * Remove all entries whose key matches the given pattern.
320
+ *
321
+ * - **String**: treated as a substring match (e.g. `"Media"` removes all keys containing `"Media"`).
322
+ * - **RegExp**: tested against each key directly.
323
+ *
324
+ * @param pattern — A string (substring match) or RegExp.
325
+ * @returns Number of entries removed.
326
+ */
327
+ async invalidate(pattern) {
328
+ if (typeof pattern === "string") {
329
+ const keys = await this.collectKeys(`${this.prefix}*${pattern}*`);
330
+ if (keys.length === 0) return 0;
331
+ return this.client.del(...keys);
332
+ }
333
+ const allKeys = await this.collectKeys(`${this.prefix}*`);
334
+ const matching = allKeys.filter((k) => pattern.test(k.slice(this.prefix.length)));
335
+ if (matching.length === 0) return 0;
336
+ return this.client.del(...matching);
337
+ }
338
+ };
339
+
251
340
  // src/errors/index.ts
252
341
  var AniListError = class _AniListError extends Error {
253
342
  /** HTTP status code returned by the API */
@@ -622,8 +711,10 @@ query ($id: Int!) {
622
711
  var QUERY_MEDIA_SEARCH = `
623
712
  query (
624
713
  $search: String,
714
+ $countryOfOrigin: CountryCode,
625
715
  $type: MediaType,
626
716
  $format: MediaFormat,
717
+ $format_in: [MediaFormat],
627
718
  $status: MediaStatus,
628
719
  $season: MediaSeason,
629
720
  $seasonYear: Int,
@@ -642,8 +733,10 @@ query (
642
733
  pageInfo { total perPage currentPage lastPage hasNextPage }
643
734
  media(
644
735
  search: $search,
736
+ countryOfOrigin: $countryOfOrigin,
645
737
  type: $type,
646
738
  format: $format,
739
+ format_in: $format_in,
647
740
  status: $status,
648
741
  season: $season,
649
742
  seasonYear: $seasonYear,
@@ -753,6 +846,111 @@ query ($mediaId: Int!, $page: Int, $perPage: Int, $sort: [RecommendationSort]) {
753
846
  }
754
847
  }`;
755
848
 
849
+ // src/queries/builders.ts
850
+ function buildMediaByIdQuery(include) {
851
+ if (!include) return QUERY_MEDIA_BY_ID;
852
+ const extra = [];
853
+ if (include.relations !== false) {
854
+ extra.push(RELATIONS_FIELDS);
855
+ }
856
+ if (include.characters) {
857
+ const opts = typeof include.characters === "object" ? include.characters : {};
858
+ const perPage = clampPerPage(opts.perPage ?? 25);
859
+ const sortClause = opts.sort !== false ? ", sort: [ROLE, RELEVANCE, ID]" : "";
860
+ const voiceActorBlock = opts.voiceActors ? `
861
+ voiceActors {
862
+ ${VOICE_ACTOR_FIELDS_COMPACT}
863
+ }` : "";
864
+ extra.push(`
865
+ characters(perPage: ${perPage}${sortClause}) {
866
+ edges {
867
+ role
868
+ node {
869
+ ${CHARACTER_FIELDS_COMPACT}
870
+ }${voiceActorBlock}
871
+ }
872
+ }`);
873
+ }
874
+ if (include.staff) {
875
+ const opts = typeof include.staff === "object" ? include.staff : {};
876
+ const perPage = clampPerPage(opts.perPage ?? 25);
877
+ const sortClause = opts.sort !== false ? ", sort: [RELEVANCE, ID]" : "";
878
+ extra.push(`
879
+ staff(perPage: ${perPage}${sortClause}) {
880
+ edges {
881
+ role
882
+ node {
883
+ ${STAFF_FIELDS}
884
+ }
885
+ }
886
+ }`);
887
+ }
888
+ if (include.recommendations) {
889
+ const perPage = clampPerPage(
890
+ typeof include.recommendations === "object" ? include.recommendations.perPage ?? 10 : 10
891
+ );
892
+ extra.push(`
893
+ recommendations(perPage: ${perPage}, sort: [RATING_DESC]) {
894
+ nodes {
895
+ id
896
+ rating
897
+ mediaRecommendation {
898
+ id
899
+ title { romaji english native userPreferred }
900
+ type
901
+ format
902
+ coverImage { large medium }
903
+ averageScore
904
+ siteUrl
905
+ }
906
+ }
907
+ }`);
908
+ }
909
+ if (include.streamingEpisodes) {
910
+ extra.push(`
911
+ streamingEpisodes {
912
+ title
913
+ thumbnail
914
+ url
915
+ site
916
+ }`);
917
+ }
918
+ if (include.externalLinks) {
919
+ extra.push(`
920
+ externalLinks {
921
+ id
922
+ url
923
+ site
924
+ type
925
+ icon
926
+ color
927
+ }`);
928
+ }
929
+ if (include.stats) {
930
+ extra.push(`
931
+ stats {
932
+ scoreDistribution { score amount }
933
+ statusDistribution { status amount }
934
+ }`);
935
+ }
936
+ return `
937
+ query ($id: Int!) {
938
+ Media(id: $id) {
939
+ ${MEDIA_FIELDS_BASE}
940
+ ${extra.join("\n")}
941
+ }
942
+ }`;
943
+ }
944
+ function buildBatchQuery(ids, typeName, fields, prefix) {
945
+ const aliases = ids.map((id, i) => `${prefix}${i}: ${typeName}(id: ${id}) { ${fields} }`).join("\n ");
946
+ return `query {
947
+ ${aliases}
948
+ }`;
949
+ }
950
+ var buildBatchMediaQuery = (ids) => buildBatchQuery(ids, "Media", MEDIA_FIELDS, "m");
951
+ var buildBatchCharacterQuery = (ids) => buildBatchQuery(ids, "Character", CHARACTER_FIELDS, "c");
952
+ var buildBatchStaffQuery = (ids) => buildBatchQuery(ids, "Staff", STAFF_FIELDS, "s");
953
+
756
954
  // src/queries/character.ts
757
955
  var QUERY_CHARACTER_BY_ID = `
758
956
  query ($id: Int!) {
@@ -785,6 +983,22 @@ query ($search: String, $sort: [CharacterSort], $page: Int, $perPage: Int) {
785
983
  }
786
984
  }`;
787
985
 
986
+ // src/queries/metadata.ts
987
+ var QUERY_GENRES = `
988
+ query {
989
+ GenreCollection
990
+ }`;
991
+ var QUERY_TAGS = `
992
+ query {
993
+ MediaTagCollection {
994
+ id
995
+ name
996
+ description
997
+ category
998
+ isAdult
999
+ }
1000
+ }`;
1001
+
788
1002
  // src/queries/staff.ts
789
1003
  var QUERY_STAFF_BY_ID = `
790
1004
  query ($id: Int!) {
@@ -809,6 +1023,60 @@ query ($search: String, $sort: [StaffSort], $page: Int, $perPage: Int) {
809
1023
  }
810
1024
  }`;
811
1025
 
1026
+ // src/queries/studio.ts
1027
+ var QUERY_STUDIO_BY_ID = `
1028
+ query ($id: Int!) {
1029
+ Studio(id: $id) {
1030
+ ${STUDIO_FIELDS}
1031
+ }
1032
+ }`;
1033
+ function buildStudioByIdQuery(mediaPerPage) {
1034
+ if (mediaPerPage === void 0) return QUERY_STUDIO_BY_ID;
1035
+ const pp = clampPerPage(mediaPerPage);
1036
+ return `
1037
+ query ($id: Int!) {
1038
+ Studio(id: $id) {
1039
+ id
1040
+ name
1041
+ isAnimationStudio
1042
+ siteUrl
1043
+ favourites
1044
+ media(page: 1, perPage: ${pp}, sort: POPULARITY_DESC) {
1045
+ pageInfo { total perPage currentPage lastPage hasNextPage }
1046
+ nodes {
1047
+ ${MEDIA_FIELDS_LIGHT}
1048
+ }
1049
+ }
1050
+ }
1051
+ }`;
1052
+ }
1053
+ var QUERY_STUDIO_SEARCH = `
1054
+ query ($search: String, $sort: [StudioSort], $page: Int, $perPage: Int) {
1055
+ Page(page: $page, perPage: $perPage) {
1056
+ pageInfo { total perPage currentPage lastPage hasNextPage }
1057
+ studios(search: $search, sort: $sort) {
1058
+ ${STUDIO_FIELDS}
1059
+ }
1060
+ }
1061
+ }`;
1062
+
1063
+ // src/queries/thread.ts
1064
+ var QUERY_THREAD_BY_ID = `
1065
+ query ($id: Int!) {
1066
+ Thread(id: $id) {
1067
+ ${THREAD_FIELDS}
1068
+ }
1069
+ }`;
1070
+ var QUERY_THREAD_SEARCH = `
1071
+ query ($search: String, $mediaCategoryId: Int, $categoryId: Int, $sort: [ThreadSort], $page: Int, $perPage: Int) {
1072
+ Page(page: $page, perPage: $perPage) {
1073
+ pageInfo { total perPage currentPage lastPage hasNextPage }
1074
+ threads(search: $search, mediaCategoryId: $mediaCategoryId, categoryId: $categoryId, sort: $sort) {
1075
+ ${THREAD_FIELDS}
1076
+ }
1077
+ }
1078
+ }`;
1079
+
812
1080
  // src/queries/user.ts
813
1081
  var QUERY_USER_BY_ID = `
814
1082
  query ($id: Int!) {
@@ -895,199 +1163,24 @@ query (${varDecl}) {
895
1163
  }
896
1164
  }
897
1165
  staff(perPage: ${pp}) {
898
- nodes {
899
- id
900
- name { full native }
901
- image { large medium }
902
- siteUrl
903
- }
904
- }
905
- studios(perPage: ${pp}) {
906
- nodes {
907
- id
908
- name
909
- siteUrl
910
- }
911
- }
912
- }
913
- }
914
- }`;
915
- }
916
-
917
- // src/queries/studio.ts
918
- var QUERY_STUDIO_BY_ID = `
919
- query ($id: Int!) {
920
- Studio(id: $id) {
921
- ${STUDIO_FIELDS}
922
- }
923
- }`;
924
- function buildStudioByIdQuery(mediaPerPage) {
925
- if (mediaPerPage === void 0) return QUERY_STUDIO_BY_ID;
926
- const pp = clampPerPage(mediaPerPage);
927
- return `
928
- query ($id: Int!) {
929
- Studio(id: $id) {
930
- id
931
- name
932
- isAnimationStudio
933
- siteUrl
934
- favourites
935
- media(page: 1, perPage: ${pp}, sort: POPULARITY_DESC) {
936
- pageInfo { total perPage currentPage lastPage hasNextPage }
937
- nodes {
938
- ${MEDIA_FIELDS_LIGHT}
939
- }
940
- }
941
- }
942
- }`;
943
- }
944
- var QUERY_STUDIO_SEARCH = `
945
- query ($search: String, $sort: [StudioSort], $page: Int, $perPage: Int) {
946
- Page(page: $page, perPage: $perPage) {
947
- pageInfo { total perPage currentPage lastPage hasNextPage }
948
- studios(search: $search, sort: $sort) {
949
- ${STUDIO_FIELDS}
950
- }
951
- }
952
- }`;
953
-
954
- // src/queries/metadata.ts
955
- var QUERY_GENRES = `
956
- query {
957
- GenreCollection
958
- }`;
959
- var QUERY_TAGS = `
960
- query {
961
- MediaTagCollection {
962
- id
963
- name
964
- description
965
- category
966
- isAdult
967
- }
968
- }`;
969
-
970
- // src/queries/builders.ts
971
- function buildMediaByIdQuery(include) {
972
- if (!include) return QUERY_MEDIA_BY_ID;
973
- const extra = [];
974
- if (include.relations !== false) {
975
- extra.push(RELATIONS_FIELDS);
976
- }
977
- if (include.characters) {
978
- const opts = typeof include.characters === "object" ? include.characters : {};
979
- const perPage = clampPerPage(opts.perPage ?? 25);
980
- const sortClause = opts.sort !== false ? ", sort: [ROLE, RELEVANCE, ID]" : "";
981
- const voiceActorBlock = opts.voiceActors ? `
982
- voiceActors {
983
- ${VOICE_ACTOR_FIELDS_COMPACT}
984
- }` : "";
985
- extra.push(`
986
- characters(perPage: ${perPage}${sortClause}) {
987
- edges {
988
- role
989
- node {
990
- ${CHARACTER_FIELDS_COMPACT}
991
- }${voiceActorBlock}
992
- }
993
- }`);
994
- }
995
- if (include.staff) {
996
- const opts = typeof include.staff === "object" ? include.staff : {};
997
- const perPage = clampPerPage(opts.perPage ?? 25);
998
- const sortClause = opts.sort !== false ? ", sort: [RELEVANCE, ID]" : "";
999
- extra.push(`
1000
- staff(perPage: ${perPage}${sortClause}) {
1001
- edges {
1002
- role
1003
- node {
1004
- ${STAFF_FIELDS}
1005
- }
1006
- }
1007
- }`);
1008
- }
1009
- if (include.recommendations) {
1010
- const perPage = clampPerPage(
1011
- typeof include.recommendations === "object" ? include.recommendations.perPage ?? 10 : 10
1012
- );
1013
- extra.push(`
1014
- recommendations(perPage: ${perPage}, sort: [RATING_DESC]) {
1015
- nodes {
1016
- id
1017
- rating
1018
- mediaRecommendation {
1019
- id
1020
- title { romaji english native userPreferred }
1021
- type
1022
- format
1023
- coverImage { large medium }
1024
- averageScore
1025
- siteUrl
1026
- }
1027
- }
1028
- }`);
1029
- }
1030
- if (include.streamingEpisodes) {
1031
- extra.push(`
1032
- streamingEpisodes {
1033
- title
1034
- thumbnail
1035
- url
1036
- site
1037
- }`);
1038
- }
1039
- if (include.externalLinks) {
1040
- extra.push(`
1041
- externalLinks {
1042
- id
1043
- url
1044
- site
1045
- type
1046
- icon
1047
- color
1048
- }`);
1049
- }
1050
- if (include.stats) {
1051
- extra.push(`
1052
- stats {
1053
- scoreDistribution { score amount }
1054
- statusDistribution { status amount }
1055
- }`);
1056
- }
1057
- return `
1058
- query ($id: Int!) {
1059
- Media(id: $id) {
1060
- ${MEDIA_FIELDS_BASE}
1061
- ${extra.join("\n")}
1062
- }
1063
- }`;
1064
- }
1065
- function buildBatchQuery(ids, typeName, fields, prefix) {
1066
- const aliases = ids.map((id, i) => `${prefix}${i}: ${typeName}(id: ${id}) { ${fields} }`).join("\n ");
1067
- return `query {
1068
- ${aliases}
1069
- }`;
1070
- }
1071
- var buildBatchMediaQuery = (ids) => buildBatchQuery(ids, "Media", MEDIA_FIELDS, "m");
1072
- var buildBatchCharacterQuery = (ids) => buildBatchQuery(ids, "Character", CHARACTER_FIELDS, "c");
1073
- var buildBatchStaffQuery = (ids) => buildBatchQuery(ids, "Staff", STAFF_FIELDS, "s");
1074
-
1075
- // src/queries/thread.ts
1076
- var QUERY_THREAD_BY_ID = `
1077
- query ($id: Int!) {
1078
- Thread(id: $id) {
1079
- ${THREAD_FIELDS}
1080
- }
1081
- }`;
1082
- var QUERY_THREAD_SEARCH = `
1083
- query ($search: String, $mediaCategoryId: Int, $categoryId: Int, $sort: [ThreadSort], $page: Int, $perPage: Int) {
1084
- Page(page: $page, perPage: $perPage) {
1085
- pageInfo { total perPage currentPage lastPage hasNextPage }
1086
- threads(search: $search, mediaCategoryId: $mediaCategoryId, categoryId: $categoryId, sort: $sort) {
1087
- ${THREAD_FIELDS}
1166
+ nodes {
1167
+ id
1168
+ name { full native }
1169
+ image { large medium }
1170
+ siteUrl
1171
+ }
1172
+ }
1173
+ studios(perPage: ${pp}) {
1174
+ nodes {
1175
+ id
1176
+ name
1177
+ siteUrl
1178
+ }
1179
+ }
1088
1180
  }
1089
1181
  }
1090
1182
  }`;
1183
+ }
1091
1184
 
1092
1185
  // src/rate-limiter/index.ts
1093
1186
  var RateLimiter = class {
@@ -1171,7 +1264,7 @@ var RateLimiter = class {
1171
1264
  }
1172
1265
  }
1173
1266
  if (lastResponse) return lastResponse;
1174
- throw lastError;
1267
+ throw lastError ?? new Error(`Request failed after ${this.maxRetries} retries`);
1175
1268
  }
1176
1269
  /** @internal — Exponential backoff with jitter, capped at 30s (or custom strategy) */
1177
1270
  exponentialDelay(attempt) {
@@ -1246,6 +1339,68 @@ async function searchCharacters(client, options = {}) {
1246
1339
  return client.pagedRequest(gqlQuery, { search, sort, page, perPage: clampPerPage(perPage) }, "characters");
1247
1340
  }
1248
1341
 
1342
+ // src/types/character.ts
1343
+ var CharacterSort = /* @__PURE__ */ ((CharacterSort2) => {
1344
+ CharacterSort2["ID"] = "ID";
1345
+ CharacterSort2["ID_DESC"] = "ID_DESC";
1346
+ CharacterSort2["ROLE"] = "ROLE";
1347
+ CharacterSort2["ROLE_DESC"] = "ROLE_DESC";
1348
+ CharacterSort2["SEARCH_MATCH"] = "SEARCH_MATCH";
1349
+ CharacterSort2["FAVOURITES"] = "FAVOURITES";
1350
+ CharacterSort2["FAVOURITES_DESC"] = "FAVOURITES_DESC";
1351
+ return CharacterSort2;
1352
+ })(CharacterSort || {});
1353
+ var CharacterRole = /* @__PURE__ */ ((CharacterRole2) => {
1354
+ CharacterRole2["MAIN"] = "MAIN";
1355
+ CharacterRole2["SUPPORTING"] = "SUPPORTING";
1356
+ CharacterRole2["BACKGROUND"] = "BACKGROUND";
1357
+ return CharacterRole2;
1358
+ })(CharacterRole || {});
1359
+
1360
+ // src/types/lists.ts
1361
+ var MediaListStatus = /* @__PURE__ */ ((MediaListStatus2) => {
1362
+ MediaListStatus2["CURRENT"] = "CURRENT";
1363
+ MediaListStatus2["PLANNING"] = "PLANNING";
1364
+ MediaListStatus2["COMPLETED"] = "COMPLETED";
1365
+ MediaListStatus2["DROPPED"] = "DROPPED";
1366
+ MediaListStatus2["PAUSED"] = "PAUSED";
1367
+ MediaListStatus2["REPEATING"] = "REPEATING";
1368
+ return MediaListStatus2;
1369
+ })(MediaListStatus || {});
1370
+ var MediaListSort = /* @__PURE__ */ ((MediaListSort2) => {
1371
+ MediaListSort2["MEDIA_ID"] = "MEDIA_ID";
1372
+ MediaListSort2["MEDIA_ID_DESC"] = "MEDIA_ID_DESC";
1373
+ MediaListSort2["SCORE"] = "SCORE";
1374
+ MediaListSort2["SCORE_DESC"] = "SCORE_DESC";
1375
+ MediaListSort2["STATUS"] = "STATUS";
1376
+ MediaListSort2["STATUS_DESC"] = "STATUS_DESC";
1377
+ MediaListSort2["PROGRESS"] = "PROGRESS";
1378
+ MediaListSort2["PROGRESS_DESC"] = "PROGRESS_DESC";
1379
+ MediaListSort2["PROGRESS_VOLUMES"] = "PROGRESS_VOLUMES";
1380
+ MediaListSort2["PROGRESS_VOLUMES_DESC"] = "PROGRESS_VOLUMES_DESC";
1381
+ MediaListSort2["REPEAT"] = "REPEAT";
1382
+ MediaListSort2["REPEAT_DESC"] = "REPEAT_DESC";
1383
+ MediaListSort2["PRIORITY"] = "PRIORITY";
1384
+ MediaListSort2["PRIORITY_DESC"] = "PRIORITY_DESC";
1385
+ MediaListSort2["STARTED_ON"] = "STARTED_ON";
1386
+ MediaListSort2["STARTED_ON_DESC"] = "STARTED_ON_DESC";
1387
+ MediaListSort2["FINISHED_ON"] = "FINISHED_ON";
1388
+ MediaListSort2["FINISHED_ON_DESC"] = "FINISHED_ON_DESC";
1389
+ MediaListSort2["ADDED_TIME"] = "ADDED_TIME";
1390
+ MediaListSort2["ADDED_TIME_DESC"] = "ADDED_TIME_DESC";
1391
+ MediaListSort2["UPDATED_TIME"] = "UPDATED_TIME";
1392
+ MediaListSort2["UPDATED_TIME_DESC"] = "UPDATED_TIME_DESC";
1393
+ MediaListSort2["MEDIA_TITLE_ROMAJI"] = "MEDIA_TITLE_ROMAJI";
1394
+ MediaListSort2["MEDIA_TITLE_ROMAJI_DESC"] = "MEDIA_TITLE_ROMAJI_DESC";
1395
+ MediaListSort2["MEDIA_TITLE_ENGLISH"] = "MEDIA_TITLE_ENGLISH";
1396
+ MediaListSort2["MEDIA_TITLE_ENGLISH_DESC"] = "MEDIA_TITLE_ENGLISH_DESC";
1397
+ MediaListSort2["MEDIA_TITLE_NATIVE"] = "MEDIA_TITLE_NATIVE";
1398
+ MediaListSort2["MEDIA_TITLE_NATIVE_DESC"] = "MEDIA_TITLE_NATIVE_DESC";
1399
+ MediaListSort2["MEDIA_POPULARITY"] = "MEDIA_POPULARITY";
1400
+ MediaListSort2["MEDIA_POPULARITY_DESC"] = "MEDIA_POPULARITY_DESC";
1401
+ return MediaListSort2;
1402
+ })(MediaListSort || {});
1403
+
1249
1404
  // src/types/media.ts
1250
1405
  var MediaType = /* @__PURE__ */ ((MediaType2) => {
1251
1406
  MediaType2["ANIME"] = "ANIME";
@@ -1369,24 +1524,6 @@ var RecommendationSort = /* @__PURE__ */ ((RecommendationSort2) => {
1369
1524
  return RecommendationSort2;
1370
1525
  })(RecommendationSort || {});
1371
1526
 
1372
- // src/types/character.ts
1373
- var CharacterSort = /* @__PURE__ */ ((CharacterSort2) => {
1374
- CharacterSort2["ID"] = "ID";
1375
- CharacterSort2["ID_DESC"] = "ID_DESC";
1376
- CharacterSort2["ROLE"] = "ROLE";
1377
- CharacterSort2["ROLE_DESC"] = "ROLE_DESC";
1378
- CharacterSort2["SEARCH_MATCH"] = "SEARCH_MATCH";
1379
- CharacterSort2["FAVOURITES"] = "FAVOURITES";
1380
- CharacterSort2["FAVOURITES_DESC"] = "FAVOURITES_DESC";
1381
- return CharacterSort2;
1382
- })(CharacterSort || {});
1383
- var CharacterRole = /* @__PURE__ */ ((CharacterRole2) => {
1384
- CharacterRole2["MAIN"] = "MAIN";
1385
- CharacterRole2["SUPPORTING"] = "SUPPORTING";
1386
- CharacterRole2["BACKGROUND"] = "BACKGROUND";
1387
- return CharacterRole2;
1388
- })(CharacterRole || {});
1389
-
1390
1527
  // src/types/staff.ts
1391
1528
  var StaffSort = /* @__PURE__ */ ((StaffSort2) => {
1392
1529
  StaffSort2["ID"] = "ID";
@@ -1402,20 +1539,6 @@ var StaffSort = /* @__PURE__ */ ((StaffSort2) => {
1402
1539
  return StaffSort2;
1403
1540
  })(StaffSort || {});
1404
1541
 
1405
- // src/types/user.ts
1406
- var UserSort = /* @__PURE__ */ ((UserSort2) => {
1407
- UserSort2["ID"] = "ID";
1408
- UserSort2["ID_DESC"] = "ID_DESC";
1409
- UserSort2["USERNAME"] = "USERNAME";
1410
- UserSort2["USERNAME_DESC"] = "USERNAME_DESC";
1411
- UserSort2["WATCHED_TIME"] = "WATCHED_TIME";
1412
- UserSort2["WATCHED_TIME_DESC"] = "WATCHED_TIME_DESC";
1413
- UserSort2["CHAPTERS_READ"] = "CHAPTERS_READ";
1414
- UserSort2["CHAPTERS_READ_DESC"] = "CHAPTERS_READ_DESC";
1415
- UserSort2["SEARCH_MATCH"] = "SEARCH_MATCH";
1416
- return UserSort2;
1417
- })(UserSort || {});
1418
-
1419
1542
  // src/types/studio.ts
1420
1543
  var StudioSort = /* @__PURE__ */ ((StudioSort2) => {
1421
1544
  StudioSort2["ID"] = "ID";
@@ -1428,50 +1551,6 @@ var StudioSort = /* @__PURE__ */ ((StudioSort2) => {
1428
1551
  return StudioSort2;
1429
1552
  })(StudioSort || {});
1430
1553
 
1431
- // src/types/lists.ts
1432
- var MediaListStatus = /* @__PURE__ */ ((MediaListStatus2) => {
1433
- MediaListStatus2["CURRENT"] = "CURRENT";
1434
- MediaListStatus2["PLANNING"] = "PLANNING";
1435
- MediaListStatus2["COMPLETED"] = "COMPLETED";
1436
- MediaListStatus2["DROPPED"] = "DROPPED";
1437
- MediaListStatus2["PAUSED"] = "PAUSED";
1438
- MediaListStatus2["REPEATING"] = "REPEATING";
1439
- return MediaListStatus2;
1440
- })(MediaListStatus || {});
1441
- var MediaListSort = /* @__PURE__ */ ((MediaListSort2) => {
1442
- MediaListSort2["MEDIA_ID"] = "MEDIA_ID";
1443
- MediaListSort2["MEDIA_ID_DESC"] = "MEDIA_ID_DESC";
1444
- MediaListSort2["SCORE"] = "SCORE";
1445
- MediaListSort2["SCORE_DESC"] = "SCORE_DESC";
1446
- MediaListSort2["STATUS"] = "STATUS";
1447
- MediaListSort2["STATUS_DESC"] = "STATUS_DESC";
1448
- MediaListSort2["PROGRESS"] = "PROGRESS";
1449
- MediaListSort2["PROGRESS_DESC"] = "PROGRESS_DESC";
1450
- MediaListSort2["PROGRESS_VOLUMES"] = "PROGRESS_VOLUMES";
1451
- MediaListSort2["PROGRESS_VOLUMES_DESC"] = "PROGRESS_VOLUMES_DESC";
1452
- MediaListSort2["REPEAT"] = "REPEAT";
1453
- MediaListSort2["REPEAT_DESC"] = "REPEAT_DESC";
1454
- MediaListSort2["PRIORITY"] = "PRIORITY";
1455
- MediaListSort2["PRIORITY_DESC"] = "PRIORITY_DESC";
1456
- MediaListSort2["STARTED_ON"] = "STARTED_ON";
1457
- MediaListSort2["STARTED_ON_DESC"] = "STARTED_ON_DESC";
1458
- MediaListSort2["FINISHED_ON"] = "FINISHED_ON";
1459
- MediaListSort2["FINISHED_ON_DESC"] = "FINISHED_ON_DESC";
1460
- MediaListSort2["ADDED_TIME"] = "ADDED_TIME";
1461
- MediaListSort2["ADDED_TIME_DESC"] = "ADDED_TIME_DESC";
1462
- MediaListSort2["UPDATED_TIME"] = "UPDATED_TIME";
1463
- MediaListSort2["UPDATED_TIME_DESC"] = "UPDATED_TIME_DESC";
1464
- MediaListSort2["MEDIA_TITLE_ROMAJI"] = "MEDIA_TITLE_ROMAJI";
1465
- MediaListSort2["MEDIA_TITLE_ROMAJI_DESC"] = "MEDIA_TITLE_ROMAJI_DESC";
1466
- MediaListSort2["MEDIA_TITLE_ENGLISH"] = "MEDIA_TITLE_ENGLISH";
1467
- MediaListSort2["MEDIA_TITLE_ENGLISH_DESC"] = "MEDIA_TITLE_ENGLISH_DESC";
1468
- MediaListSort2["MEDIA_TITLE_NATIVE"] = "MEDIA_TITLE_NATIVE";
1469
- MediaListSort2["MEDIA_TITLE_NATIVE_DESC"] = "MEDIA_TITLE_NATIVE_DESC";
1470
- MediaListSort2["MEDIA_POPULARITY"] = "MEDIA_POPULARITY";
1471
- MediaListSort2["MEDIA_POPULARITY_DESC"] = "MEDIA_POPULARITY_DESC";
1472
- return MediaListSort2;
1473
- })(MediaListSort || {});
1474
-
1475
1554
  // src/types/thread.ts
1476
1555
  var ThreadSort = /* @__PURE__ */ ((ThreadSort2) => {
1477
1556
  ThreadSort2["ID"] = "ID";
@@ -1493,6 +1572,20 @@ var ThreadSort = /* @__PURE__ */ ((ThreadSort2) => {
1493
1572
  return ThreadSort2;
1494
1573
  })(ThreadSort || {});
1495
1574
 
1575
+ // src/types/user.ts
1576
+ var UserSort = /* @__PURE__ */ ((UserSort2) => {
1577
+ UserSort2["ID"] = "ID";
1578
+ UserSort2["ID_DESC"] = "ID_DESC";
1579
+ UserSort2["USERNAME"] = "USERNAME";
1580
+ UserSort2["USERNAME_DESC"] = "USERNAME_DESC";
1581
+ UserSort2["WATCHED_TIME"] = "WATCHED_TIME";
1582
+ UserSort2["WATCHED_TIME_DESC"] = "WATCHED_TIME_DESC";
1583
+ UserSort2["CHAPTERS_READ"] = "CHAPTERS_READ";
1584
+ UserSort2["CHAPTERS_READ_DESC"] = "CHAPTERS_READ_DESC";
1585
+ UserSort2["SEARCH_MATCH"] = "SEARCH_MATCH";
1586
+ return UserSort2;
1587
+ })(UserSort || {});
1588
+
1496
1589
  // src/client/media.ts
1497
1590
  async function getMedia(client, id, include) {
1498
1591
  validateId(id, "mediaId");
@@ -1509,12 +1602,24 @@ async function getMediaByMalId(client, malId, type) {
1509
1602
  return data.Media;
1510
1603
  }
1511
1604
  async function searchMedia(client, options = {}) {
1512
- const { query: search, page = 1, perPage = 20, genres, tags, genresExclude, tagsExclude, ...filters } = options;
1605
+ const {
1606
+ query: search,
1607
+ page = 1,
1608
+ perPage = 20,
1609
+ genres,
1610
+ tags,
1611
+ genresExclude,
1612
+ tagsExclude,
1613
+ format,
1614
+ ...filters
1615
+ } = options;
1513
1616
  return client.pagedRequest(
1514
1617
  QUERY_MEDIA_SEARCH,
1515
1618
  {
1516
1619
  search,
1517
1620
  ...filters,
1621
+ format: Array.isArray(format) ? void 0 : format,
1622
+ format_in: Array.isArray(format) ? format : void 0,
1518
1623
  genre_in: genres,
1519
1624
  tag_in: tags,
1520
1625
  genre_not_in: genresExclude,
@@ -1717,7 +1822,7 @@ async function searchUsers(client, options = {}) {
1717
1822
  }
1718
1823
  async function getUserMediaList(client, options) {
1719
1824
  if (!options.userId && !options.userName) {
1720
- throw new AniListError("getUserMediaList requires either userId or userName", 0, []);
1825
+ throw new TypeError("getUserMediaList requires either userId or userName");
1721
1826
  }
1722
1827
  if (options.userId) {
1723
1828
  validateId(options.userId, "userId");
@@ -1764,7 +1869,7 @@ function mapFavorites(fav) {
1764
1869
 
1765
1870
  // src/client/index.ts
1766
1871
  var DEFAULT_API_URL = "https://graphql.anilist.co";
1767
- var LIB_VERSION = "1.8.0" ;
1872
+ var LIB_VERSION = "1.9.0" ;
1768
1873
  var AniListClient = class {
1769
1874
  apiUrl;
1770
1875
  headers;
@@ -1852,7 +1957,15 @@ var AniListClient = class {
1852
1957
  this.hooks.onError?.(error, query, variables);
1853
1958
  throw error;
1854
1959
  }
1855
- const json = await res.json();
1960
+ let json;
1961
+ try {
1962
+ json = await res.json();
1963
+ } catch {
1964
+ const error = new AniListError(`Non-JSON response from AniList (HTTP ${res.status})`, res.status, []);
1965
+ this.logger?.error("Request failed", { error: error.message, status: error.status });
1966
+ this.hooks.onError?.(error, query, variables);
1967
+ throw error;
1968
+ }
1856
1969
  if (!res.ok || json.errors) {
1857
1970
  const message = json.errors?.[0]?.message ?? `AniList API error (HTTP ${res.status})`;
1858
1971
  const error = new AniListError(message, res.status, json.errors ?? []);
@@ -2156,92 +2269,6 @@ var AniListClient = class {
2156
2269
  }
2157
2270
  };
2158
2271
 
2159
- // src/cache/redis.ts
2160
- var RedisCache = class {
2161
- client;
2162
- prefix;
2163
- ttl;
2164
- constructor(options) {
2165
- this.client = options.client;
2166
- this.prefix = options.prefix ?? "ani:";
2167
- this.ttl = options.ttl ?? 86400;
2168
- }
2169
- prefixedKey(key) {
2170
- return `${this.prefix}${key}`;
2171
- }
2172
- async get(key) {
2173
- const raw = await this.client.get(this.prefixedKey(key));
2174
- if (raw === null) return void 0;
2175
- try {
2176
- return JSON.parse(raw);
2177
- } catch {
2178
- return void 0;
2179
- }
2180
- }
2181
- async set(key, data) {
2182
- await this.client.set(this.prefixedKey(key), JSON.stringify(data), "EX", this.ttl);
2183
- }
2184
- async delete(key) {
2185
- const count = await this.client.del(this.prefixedKey(key));
2186
- return count > 0;
2187
- }
2188
- /**
2189
- * Collect keys matching a pattern. Uses SCAN when available, falls back to KEYS.
2190
- *
2191
- * **Warning:** The `KEYS` fallback is O(N) and blocks the Redis server.
2192
- * Provide a client with `scanIterator` support for production use.
2193
- * @internal
2194
- */
2195
- async collectKeys(pattern) {
2196
- if (this.client.scanIterator) {
2197
- const keys = [];
2198
- for await (const key of this.client.scanIterator({ MATCH: pattern, COUNT: 100 })) {
2199
- keys.push(key);
2200
- }
2201
- return keys;
2202
- }
2203
- return this.client.keys(pattern);
2204
- }
2205
- async clear() {
2206
- const keys = await this.collectKeys(`${this.prefix}*`);
2207
- if (keys.length > 0) {
2208
- await this.client.del(...keys);
2209
- }
2210
- }
2211
- /**
2212
- * Get the actual number of keys with this prefix in Redis.
2213
- */
2214
- get size() {
2215
- return this.getSize();
2216
- }
2217
- /** @internal */
2218
- async getSize() {
2219
- const keys = await this.collectKeys(`${this.prefix}*`);
2220
- return keys.length;
2221
- }
2222
- async keys() {
2223
- const raw = await this.collectKeys(`${this.prefix}*`);
2224
- return raw.map((k) => k.slice(this.prefix.length));
2225
- }
2226
- /**
2227
- * Remove all entries whose key matches the given glob pattern.
2228
- *
2229
- * @param pattern — A glob pattern (e.g. `"*Media*"`)
2230
- * @returns Number of entries removed.
2231
- */
2232
- async invalidate(pattern) {
2233
- if (typeof pattern === "string") {
2234
- const keys = await this.collectKeys(`${this.prefix}${pattern}`);
2235
- if (keys.length === 0) return 0;
2236
- return this.client.del(...keys);
2237
- }
2238
- const allKeys = await this.collectKeys(`${this.prefix}*`);
2239
- const matching = allKeys.filter((k) => pattern.test(k.slice(this.prefix.length)));
2240
- if (matching.length === 0) return 0;
2241
- return this.client.del(...matching);
2242
- }
2243
- };
2244
-
2245
2272
  export { AiringSort, AniListClient, AniListError, CharacterRole, CharacterSort, MediaFormat, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaSource, MediaStatus, MediaType, MemoryCache, RateLimiter, RecommendationSort, RedisCache, StaffSort, StudioSort, ThreadSort, UserSort, parseAniListMarkdown };
2246
2273
  //# sourceMappingURL=index.mjs.map
2247
2274
  //# sourceMappingURL=index.mjs.map