ani-client 1.4.1 → 1.4.3

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
@@ -127,6 +127,46 @@ var STAFF_FIELDS = `
127
127
  favourites
128
128
  siteUrl
129
129
  `;
130
+ var STAFF_MEDIA_FIELDS = `
131
+ staffMedia(perPage: $perPage, sort: [POPULARITY_DESC]) {
132
+ nodes {
133
+ id
134
+ title { romaji english native userPreferred }
135
+ type
136
+ format
137
+ status
138
+ coverImage { extraLarge large medium color }
139
+ bannerImage
140
+ genres
141
+ averageScore
142
+ meanScore
143
+ popularity
144
+ favourites
145
+ episodes
146
+ trending
147
+ hashtag
148
+ season
149
+ seasonYear
150
+ startDate { year month day }
151
+ endDate { year month day }
152
+ nextAiringEpisode {
153
+ id
154
+ airingAt
155
+ episode
156
+ mediaId
157
+ timeUntilAiring
158
+ }
159
+ studios {
160
+ edges {
161
+ node {
162
+ name
163
+ }
164
+ }
165
+ }
166
+ siteUrl
167
+ }
168
+ }
169
+ `;
130
170
  var USER_FIELDS = `
131
171
  id
132
172
  name
@@ -228,6 +268,13 @@ query ($id: Int!) {
228
268
  ${STAFF_FIELDS}
229
269
  }
230
270
  }`;
271
+ var QUERY_STAFF_BY_ID_WITH_MEDIA = `
272
+ query ($id: Int!, $perPage: Int) {
273
+ Staff(id: $id) {
274
+ ${STAFF_FIELDS}
275
+ ${STAFF_MEDIA_FIELDS}
276
+ }
277
+ }`;
231
278
  var QUERY_STAFF_SEARCH = `
232
279
  query ($search: String, $sort: [StaffSort], $page: Int, $perPage: Int) {
233
280
  Page(page: $page, perPage: $perPage) {
@@ -580,6 +627,7 @@ var AniListError = class _AniListError extends Error {
580
627
  this.name = "AniListError";
581
628
  this.status = status;
582
629
  this.errors = errors;
630
+ Object.setPrototypeOf(this, _AniListError.prototype);
583
631
  if (Error.captureStackTrace) {
584
632
  Error.captureStackTrace(this, _AniListError);
585
633
  }
@@ -681,7 +729,7 @@ function isNetworkError(err) {
681
729
  return false;
682
730
  }
683
731
 
684
- // src/types/index.ts
732
+ // src/types/media.ts
685
733
  var MediaType = /* @__PURE__ */ ((MediaType2) => {
686
734
  MediaType2["ANIME"] = "ANIME";
687
735
  MediaType2["MANGA"] = "MANGA";
@@ -762,22 +810,6 @@ var AiringSort = /* @__PURE__ */ ((AiringSort2) => {
762
810
  AiringSort2["EPISODE_DESC"] = "EPISODE_DESC";
763
811
  return AiringSort2;
764
812
  })(AiringSort || {});
765
- var CharacterSort = /* @__PURE__ */ ((CharacterSort2) => {
766
- CharacterSort2["ID"] = "ID";
767
- CharacterSort2["ID_DESC"] = "ID_DESC";
768
- CharacterSort2["ROLE"] = "ROLE";
769
- CharacterSort2["ROLE_DESC"] = "ROLE_DESC";
770
- CharacterSort2["SEARCH_MATCH"] = "SEARCH_MATCH";
771
- CharacterSort2["FAVOURITES"] = "FAVOURITES";
772
- CharacterSort2["FAVOURITES_DESC"] = "FAVOURITES_DESC";
773
- return CharacterSort2;
774
- })(CharacterSort || {});
775
- var CharacterRole = /* @__PURE__ */ ((CharacterRole2) => {
776
- CharacterRole2["MAIN"] = "MAIN";
777
- CharacterRole2["SUPPORTING"] = "SUPPORTING";
778
- CharacterRole2["BACKGROUND"] = "BACKGROUND";
779
- return CharacterRole2;
780
- })(CharacterRole || {});
781
813
  var MediaRelationType = /* @__PURE__ */ ((MediaRelationType2) => {
782
814
  MediaRelationType2["ADAPTATION"] = "ADAPTATION";
783
815
  MediaRelationType2["PREQUEL"] = "PREQUEL";
@@ -801,6 +833,26 @@ var RecommendationSort = /* @__PURE__ */ ((RecommendationSort2) => {
801
833
  RecommendationSort2["RATING_DESC"] = "RATING_DESC";
802
834
  return RecommendationSort2;
803
835
  })(RecommendationSort || {});
836
+
837
+ // src/types/character.ts
838
+ var CharacterSort = /* @__PURE__ */ ((CharacterSort2) => {
839
+ CharacterSort2["ID"] = "ID";
840
+ CharacterSort2["ID_DESC"] = "ID_DESC";
841
+ CharacterSort2["ROLE"] = "ROLE";
842
+ CharacterSort2["ROLE_DESC"] = "ROLE_DESC";
843
+ CharacterSort2["SEARCH_MATCH"] = "SEARCH_MATCH";
844
+ CharacterSort2["FAVOURITES"] = "FAVOURITES";
845
+ CharacterSort2["FAVOURITES_DESC"] = "FAVOURITES_DESC";
846
+ return CharacterSort2;
847
+ })(CharacterSort || {});
848
+ var CharacterRole = /* @__PURE__ */ ((CharacterRole2) => {
849
+ CharacterRole2["MAIN"] = "MAIN";
850
+ CharacterRole2["SUPPORTING"] = "SUPPORTING";
851
+ CharacterRole2["BACKGROUND"] = "BACKGROUND";
852
+ return CharacterRole2;
853
+ })(CharacterRole || {});
854
+
855
+ // src/types/lists.ts
804
856
  var MediaListStatus = /* @__PURE__ */ ((MediaListStatus2) => {
805
857
  MediaListStatus2["CURRENT"] = "CURRENT";
806
858
  MediaListStatus2["PLANNING"] = "PLANNING";
@@ -844,6 +896,18 @@ var MediaListSort = /* @__PURE__ */ ((MediaListSort2) => {
844
896
  return MediaListSort2;
845
897
  })(MediaListSort || {});
846
898
 
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
+
847
911
  // src/client/index.ts
848
912
  var DEFAULT_API_URL = "https://graphql.anilist.co";
849
913
  var AniListClient = class {
@@ -886,12 +950,13 @@ var AniListClient = class {
886
950
  async executeRequest(query, variables, cacheKey) {
887
951
  const start = Date.now();
888
952
  this.hooks.onRequest?.(query, variables);
953
+ const minifiedQuery = query.replace(/\s+/g, " ").trim();
889
954
  const res = await this.rateLimiter.fetchWithRetry(
890
955
  this.apiUrl,
891
956
  {
892
957
  method: "POST",
893
958
  headers: this.headers,
894
- body: JSON.stringify({ query, variables })
959
+ body: JSON.stringify({ query: minifiedQuery, variables })
895
960
  },
896
961
  { onRetry: this.hooks.onRetry, onRateLimit: this.hooks.onRateLimit }
897
962
  );
@@ -917,13 +982,6 @@ var AniListClient = class {
917
982
  }
918
983
  return { pageInfo: data.Page.pageInfo, results };
919
984
  }
920
- /**
921
- * @internal
922
- * Clamp perPage to AniList's maximum of 50.
923
- */
924
- clampPerPage(value) {
925
- return Math.min(Math.max(value, 1), 50);
926
- }
927
985
  /**
928
986
  * Fetch a single media entry by its AniList ID.
929
987
  *
@@ -983,7 +1041,7 @@ var AniListClient = class {
983
1041
  const { query: search, page = 1, perPage = 20, ...filters } = options;
984
1042
  return this.pagedRequest(
985
1043
  QUERY_MEDIA_SEARCH,
986
- { search, ...filters, page, perPage: this.clampPerPage(perPage) },
1044
+ { search, ...filters, page, perPage: clampPerPage(perPage) },
987
1045
  "media"
988
1046
  );
989
1047
  }
@@ -995,7 +1053,7 @@ var AniListClient = class {
995
1053
  * @param perPage - Results per page (default 20, max 50)
996
1054
  */
997
1055
  async getTrending(type = "ANIME" /* ANIME */, page = 1, perPage = 20) {
998
- return this.pagedRequest(QUERY_TRENDING, { type, page, perPage: this.clampPerPage(perPage) }, "media");
1056
+ return this.pagedRequest(QUERY_TRENDING, { type, page, perPage: clampPerPage(perPage) }, "media");
999
1057
  }
1000
1058
  /**
1001
1059
  * Fetch a character by AniList ID.
@@ -1041,7 +1099,7 @@ var AniListClient = class {
1041
1099
  const gqlQuery = voiceActors ? QUERY_CHARACTER_SEARCH_WITH_VA : QUERY_CHARACTER_SEARCH;
1042
1100
  return this.pagedRequest(
1043
1101
  gqlQuery,
1044
- { search, ...rest, page, perPage: this.clampPerPage(perPage) },
1102
+ { search, ...rest, page, perPage: clampPerPage(perPage) },
1045
1103
  "characters"
1046
1104
  );
1047
1105
  }
@@ -1049,15 +1107,26 @@ var AniListClient = class {
1049
1107
  * Fetch a staff member by AniList ID.
1050
1108
  *
1051
1109
  * @param id - The AniList staff ID
1110
+ * @param include - Optional include options to fetch related data (e.g. media)
1052
1111
  * @returns The staff object
1053
1112
  *
1054
1113
  * @example
1055
1114
  * ```ts
1056
1115
  * const staff = await client.getStaff(95001);
1057
1116
  * console.log(staff.name.full);
1117
+ *
1118
+ * // With media the staff worked on
1119
+ * const staff = await client.getStaff(95001, { media: true });
1120
+ * staff.staffMedia?.nodes.forEach((m) => console.log(m.title.romaji));
1058
1121
  * ```
1059
1122
  */
1060
- async getStaff(id) {
1123
+ async getStaff(id, include) {
1124
+ if (include?.media) {
1125
+ const opts = typeof include.media === "object" ? include.media : {};
1126
+ const perPage = opts.perPage ?? 25;
1127
+ const data2 = await this.request(QUERY_STAFF_BY_ID_WITH_MEDIA, { id, perPage });
1128
+ return data2.Staff;
1129
+ }
1061
1130
  const data = await this.request(QUERY_STAFF_BY_ID, { id });
1062
1131
  return data.Staff;
1063
1132
  }
@@ -1076,7 +1145,7 @@ var AniListClient = class {
1076
1145
  const { query: search, page = 1, perPage = 20, sort } = options;
1077
1146
  return this.pagedRequest(
1078
1147
  QUERY_STAFF_SEARCH,
1079
- { search, sort, page, perPage: this.clampPerPage(perPage) },
1148
+ { search, sort, page, perPage: clampPerPage(perPage) },
1080
1149
  "staff"
1081
1150
  );
1082
1151
  }
@@ -1145,7 +1214,7 @@ var AniListClient = class {
1145
1214
  airingAt_lesser: options.airingAtLesser ?? now,
1146
1215
  sort: options.sort ?? ["TIME_DESC"],
1147
1216
  page: options.page ?? 1,
1148
- perPage: this.clampPerPage(options.perPage ?? 20)
1217
+ perPage: clampPerPage(options.perPage ?? 20)
1149
1218
  };
1150
1219
  return this.pagedRequest(QUERY_AIRING_SCHEDULE, variables, "airingSchedules");
1151
1220
  }
@@ -1168,7 +1237,7 @@ var AniListClient = class {
1168
1237
  QUERY_RECENT_CHAPTERS,
1169
1238
  {
1170
1239
  page: options.page ?? 1,
1171
- perPage: this.clampPerPage(options.perPage ?? 20)
1240
+ perPage: clampPerPage(options.perPage ?? 20)
1172
1241
  },
1173
1242
  "media"
1174
1243
  );
@@ -1194,7 +1263,7 @@ var AniListClient = class {
1194
1263
  type: options.type,
1195
1264
  sort: options.sort ?? ["POPULARITY_DESC"],
1196
1265
  page: options.page ?? 1,
1197
- perPage: this.clampPerPage(options.perPage ?? 20)
1266
+ perPage: clampPerPage(options.perPage ?? 20)
1198
1267
  },
1199
1268
  "media"
1200
1269
  );
@@ -1256,7 +1325,7 @@ var AniListClient = class {
1256
1325
  type: options.type ?? "ANIME",
1257
1326
  sort: options.sort ?? ["POPULARITY_DESC"],
1258
1327
  page: options.page ?? 1,
1259
- perPage: this.clampPerPage(options.perPage ?? 20)
1328
+ perPage: clampPerPage(options.perPage ?? 20)
1260
1329
  },
1261
1330
  "media"
1262
1331
  );
@@ -1298,7 +1367,7 @@ var AniListClient = class {
1298
1367
  status: options.status,
1299
1368
  sort: options.sort,
1300
1369
  page: options.page ?? 1,
1301
- perPage: this.clampPerPage(options.perPage ?? 20)
1370
+ perPage: clampPerPage(options.perPage ?? 20)
1302
1371
  },
1303
1372
  "mediaList"
1304
1373
  );
@@ -1331,7 +1400,7 @@ var AniListClient = class {
1331
1400
  {
1332
1401
  search: options.query,
1333
1402
  page: options.page ?? 1,
1334
- perPage: this.clampPerPage(options.perPage ?? 20)
1403
+ perPage: clampPerPage(options.perPage ?? 20)
1335
1404
  },
1336
1405
  "studios"
1337
1406
  );
@@ -1431,23 +1500,14 @@ var AniListClient = class {
1431
1500
  }
1432
1501
  /** @internal */
1433
1502
  async executeBatch(ids, buildQuery, prefix) {
1434
- const chunks = this.chunk(ids, 50);
1435
- const chunkResults = await Promise.all(
1436
- chunks.map(async (chunk) => {
1437
- const query = buildQuery(chunk);
1438
- const data = await this.request(query);
1439
- return chunk.map((_, i) => data[`${prefix}${i}`]);
1440
- })
1441
- );
1442
- return chunkResults.flat();
1443
- }
1444
- /** @internal */
1445
- chunk(arr, size) {
1446
- const chunks = [];
1447
- for (let i = 0; i < arr.length; i += size) {
1448
- chunks.push(arr.slice(i, i + size));
1503
+ const chunks = chunk(ids, 50);
1504
+ const chunkResults = [];
1505
+ for (const idChunk of chunks) {
1506
+ const query = buildQuery(idChunk);
1507
+ const data = await this.request(query);
1508
+ chunkResults.push(idChunk.map((_, i) => data[`${prefix}${i}`]));
1449
1509
  }
1450
- return chunks;
1510
+ return chunkResults.flat();
1451
1511
  }
1452
1512
  // ── Cache management ──
1453
1513
  /**
@@ -1457,8 +1517,8 @@ var AniListClient = class {
1457
1517
  await this.cacheAdapter.clear();
1458
1518
  }
1459
1519
  /**
1460
- * Number of entries currently in the cache (sync).
1461
- * For async adapters like Redis, this may be approximate.
1520
+ * Number of entries currently in the cache.
1521
+ * For async adapters like Redis, this may return a Promise.
1462
1522
  */
1463
1523
  get cacheSize() {
1464
1524
  return this.cacheAdapter.size;
@@ -1536,15 +1596,14 @@ var RedisCache = class {
1536
1596
  }
1537
1597
  }
1538
1598
  /**
1539
- * Returns -1 because Redis keys can expire silently via TTL.
1540
- * Use `getSize()` for an accurate count.
1599
+ * Get the actual number of keys with this prefix in Redis.
1541
1600
  */
1542
1601
  get size() {
1543
- return -1;
1602
+ return this.getSize();
1544
1603
  }
1545
- /** Get the actual number of keys with this prefix in Redis. */
1604
+ /** @internal */
1546
1605
  async getSize() {
1547
- const keys = await this.client.keys(`${this.prefix}*`);
1606
+ const keys = await this.collectKeys(`${this.prefix}*`);
1548
1607
  return keys.length;
1549
1608
  }
1550
1609
  async keys() {