ani-client 2.1.3 → 2.2.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
@@ -133,6 +133,7 @@ var NormalizedCache = class {
133
133
  _hits = 0;
134
134
  _misses = 0;
135
135
  _stales = 0;
136
+ gcTimeout;
136
137
  constructor(options = {}) {
137
138
  this.ttl = options.ttl ?? 24 * 60 * 60 * 1e3;
138
139
  this.maxSize = options.maxSize ?? 500;
@@ -244,8 +245,10 @@ var NormalizedCache = class {
244
245
  this.queryStore.delete(key);
245
246
  if (this.maxSize > 0 && this.queryStore.size >= this.maxSize) {
246
247
  const firstKey = this.queryStore.keys().next().value;
247
- if (firstKey !== void 0) this.queryStore.delete(firstKey);
248
- this.gc();
248
+ if (firstKey !== void 0) {
249
+ this.queryStore.delete(firstKey);
250
+ }
251
+ this.scheduleGc();
249
252
  }
250
253
  this.queryStore.set(key, { data: normalizedData, expiresAt: Date.now() + this.ttl });
251
254
  }
@@ -253,6 +256,10 @@ var NormalizedCache = class {
253
256
  return this.queryStore.delete(key);
254
257
  }
255
258
  clear() {
259
+ if (this.gcTimeout) {
260
+ clearTimeout(this.gcTimeout);
261
+ this.gcTimeout = void 0;
262
+ }
256
263
  this.queryStore.clear();
257
264
  this.entityStore.clear();
258
265
  this._hits = 0;
@@ -289,6 +296,16 @@ var NormalizedCache = class {
289
296
  this._misses = 0;
290
297
  this._stales = 0;
291
298
  }
299
+ scheduleGc() {
300
+ if (this.gcTimeout) return;
301
+ this.gcTimeout = setTimeout(() => {
302
+ this.gc();
303
+ this.gcTimeout = void 0;
304
+ }, 500);
305
+ if (typeof this.gcTimeout.unref === "function") {
306
+ this.gcTimeout.unref();
307
+ }
308
+ }
292
309
  /**
293
310
  * Garbage-collect orphaned entities that are no longer referenced by any query.
294
311
  * Called automatically on LRU eviction to prevent unbounded entity store growth.
@@ -647,6 +664,7 @@ var RELATIONS_FIELDS = `
647
664
  type
648
665
  format
649
666
  status
667
+ description(asHtml: false)
650
668
  startDate { year month day }
651
669
  endDate { year month day }
652
670
  season
@@ -681,6 +699,8 @@ var MEDIA_RECOMMENDATION_FIELDS = `
681
699
  title { romaji english native userPreferred }
682
700
  type
683
701
  format
702
+ status
703
+ description(asHtml: false)
684
704
  coverImage { extraLarge large medium color }
685
705
  averageScore
686
706
  meanScore
@@ -1178,8 +1198,7 @@ query ($mediaId: Int!, $page: Int, $perPage: Int, $sort: [RecommendationSort]) {
1178
1198
  }`;
1179
1199
 
1180
1200
  // src/queries/builders.ts
1181
- function buildMediaByIdQuery(include) {
1182
- if (!include) return QUERY_MEDIA_BY_ID;
1201
+ function buildMediaIncludeQuery(include) {
1183
1202
  const extra = [];
1184
1203
  if (include.relations !== false) {
1185
1204
  extra.push(RELATIONS_FIELDS);
@@ -1254,14 +1273,172 @@ function buildMediaByIdQuery(include) {
1254
1273
  statusDistribution { status amount }
1255
1274
  }`);
1256
1275
  }
1276
+ return extra;
1277
+ }
1278
+ function buildMediaByIdQuery(include) {
1279
+ if (!include) return QUERY_MEDIA_BY_ID;
1257
1280
  return `
1258
1281
  query ($id: Int!) {
1259
1282
  Media(id: $id) {
1260
1283
  ${MEDIA_FIELDS_BASE}
1261
- ${extra.join("\n")}
1284
+ ${buildMediaIncludeQuery(include).join("\n")}
1262
1285
  }
1263
1286
  }`;
1264
1287
  }
1288
+ function buildSearchMediaQuery(include) {
1289
+ if (!include) return QUERY_MEDIA_SEARCH;
1290
+ return `
1291
+ query (
1292
+ $search: String,
1293
+ $countryOfOrigin: CountryCode,
1294
+ $type: MediaType,
1295
+ $format: MediaFormat,
1296
+ $format_in: [MediaFormat],
1297
+ $status: MediaStatus,
1298
+ $season: MediaSeason,
1299
+ $seasonYear: Int,
1300
+ $genre: String,
1301
+ $tag: String,
1302
+ $genre_in: [String],
1303
+ $tag_in: [String],
1304
+ $genre_not_in: [String],
1305
+ $tag_not_in: [String],
1306
+ $isAdult: Boolean,
1307
+ $idNotIn: [Int],
1308
+ $sort: [MediaSort],
1309
+ $page: Int,
1310
+ $perPage: Int
1311
+ ) {
1312
+ Page(page: $page, perPage: $perPage) {
1313
+ pageInfo { total perPage currentPage lastPage hasNextPage }
1314
+ media(
1315
+ search: $search,
1316
+ countryOfOrigin: $countryOfOrigin,
1317
+ type: $type,
1318
+ format: $format,
1319
+ format_in: $format_in,
1320
+ status: $status,
1321
+ season: $season,
1322
+ seasonYear: $seasonYear,
1323
+ genre: $genre,
1324
+ tag: $tag,
1325
+ genre_in: $genre_in,
1326
+ tag_in: $tag_in,
1327
+ genre_not_in: $genre_not_in,
1328
+ tag_not_in: $tag_not_in,
1329
+ isAdult: $isAdult,
1330
+ id_not_in: $idNotIn,
1331
+ sort: $sort
1332
+ ) {
1333
+ ${MEDIA_FIELDS_BASE}
1334
+ ${buildMediaIncludeQuery(include).join("\n")}
1335
+ }
1336
+ }
1337
+ }`;
1338
+ }
1339
+ function buildGetTrendingQuery(include) {
1340
+ if (!include) return QUERY_TRENDING;
1341
+ return `
1342
+ query (
1343
+ $type: MediaType,
1344
+ $isAdult: Boolean,
1345
+ $idNotIn: [Int],
1346
+ $page: Int,
1347
+ $perPage: Int
1348
+ ) {
1349
+ Page(page: $page, perPage: $perPage) {
1350
+ pageInfo { total perPage currentPage lastPage hasNextPage }
1351
+ media(
1352
+ type: $type,
1353
+ isAdult: $isAdult,
1354
+ id_not_in: $idNotIn,
1355
+ sort: TRENDING_DESC
1356
+ ) {
1357
+ ${MEDIA_FIELDS_BASE}
1358
+ ${buildMediaIncludeQuery(include).join("\n")}
1359
+ }
1360
+ }
1361
+ }`;
1362
+ }
1363
+ function buildGetMediaBySeasonQuery(include) {
1364
+ if (!include) return QUERY_MEDIA_BY_SEASON;
1365
+ return `
1366
+ query (
1367
+ $season: MediaSeason!,
1368
+ $seasonYear: Int!,
1369
+ $type: MediaType,
1370
+ $isAdult: Boolean,
1371
+ $idNotIn: [Int],
1372
+ $sort: [MediaSort],
1373
+ $page: Int,
1374
+ $perPage: Int
1375
+ ) {
1376
+ Page(page: $page, perPage: $perPage) {
1377
+ pageInfo { total perPage currentPage lastPage hasNextPage }
1378
+ media(
1379
+ season: $season,
1380
+ seasonYear: $seasonYear,
1381
+ type: $type,
1382
+ isAdult: $isAdult,
1383
+ id_not_in: $idNotIn,
1384
+ sort: $sort
1385
+ ) {
1386
+ ${MEDIA_FIELDS_BASE}
1387
+ ${buildMediaIncludeQuery(include).join("\n")}
1388
+ }
1389
+ }
1390
+ }`;
1391
+ }
1392
+ function buildGetRecentlyUpdatedMangaQuery(include) {
1393
+ if (!include) return QUERY_RECENT_CHAPTERS;
1394
+ return `
1395
+ query (
1396
+ $isAdult: Boolean,
1397
+ $idNotIn: [Int],
1398
+ $page: Int,
1399
+ $perPage: Int
1400
+ ) {
1401
+ Page(page: $page, perPage: $perPage) {
1402
+ pageInfo { total perPage currentPage lastPage hasNextPage }
1403
+ media(
1404
+ type: MANGA,
1405
+ isAdult: $isAdult,
1406
+ id_not_in: $idNotIn,
1407
+ status: RELEASING,
1408
+ sort: UPDATED_AT_DESC
1409
+ ) {
1410
+ ${MEDIA_FIELDS_BASE}
1411
+ ${buildMediaIncludeQuery(include).join("\n")}
1412
+ }
1413
+ }
1414
+ }`;
1415
+ }
1416
+ function buildGetPlanningQuery(include) {
1417
+ if (!include) return QUERY_PLANNING;
1418
+ return `
1419
+ query (
1420
+ $type: MediaType,
1421
+ $isAdult: Boolean,
1422
+ $idNotIn: [Int],
1423
+ $sort: [MediaSort],
1424
+ $page: Int,
1425
+ $perPage: Int
1426
+ ) {
1427
+ Page(page: $page, perPage: $perPage) {
1428
+ pageInfo { total perPage currentPage lastPage hasNextPage }
1429
+ media(
1430
+ type: $type,
1431
+ isAdult: $isAdult,
1432
+ id_not_in: $idNotIn,
1433
+ status: NOT_YET_RELEASED,
1434
+ sort: $sort
1435
+ ) {
1436
+ ${MEDIA_FIELDS_BASE}
1437
+ ${buildMediaIncludeQuery(include).join("\n")}
1438
+ }
1439
+ }
1440
+ }`;
1441
+ }
1265
1442
  function buildMediaCharactersQuery(options = {}) {
1266
1443
  const sortClause = options.sort === false ? "" : ", sort: [ROLE, RELEVANCE, ID]";
1267
1444
  const voiceActorBlock = options.voiceActors ? `
@@ -2063,7 +2240,7 @@ async function getMediaByMalId(client, malId, type) {
2063
2240
  });
2064
2241
  return data.Media;
2065
2242
  }
2066
- async function searchMedia(client, options = {}) {
2243
+ async function searchMedia(client, options = {}, include) {
2067
2244
  const {
2068
2245
  query: search,
2069
2246
  page = 1,
@@ -2075,8 +2252,9 @@ async function searchMedia(client, options = {}) {
2075
2252
  format,
2076
2253
  ...filters
2077
2254
  } = options;
2255
+ const query = buildSearchMediaQuery(include);
2078
2256
  return client.pagedRequest(
2079
- QUERY_MEDIA_SEARCH,
2257
+ query,
2080
2258
  {
2081
2259
  search,
2082
2260
  ...filters,
@@ -2092,21 +2270,18 @@ async function searchMedia(client, options = {}) {
2092
2270
  "media"
2093
2271
  );
2094
2272
  }
2095
- async function getTrending(client, options) {
2273
+ async function getTrending(client, options, include) {
2096
2274
  const { type = "ANIME" /* ANIME */, isAdult = false, idNotIn = [], page = 1, perPage = 20 } = options;
2097
- return client.pagedRequest(
2098
- QUERY_TRENDING,
2099
- { type, isAdult, idNotIn, page, perPage: clampPerPage(perPage) },
2100
- "media"
2101
- );
2275
+ const query = buildGetTrendingQuery(include);
2276
+ return client.pagedRequest(query, { type, isAdult, idNotIn, page, perPage: clampPerPage(perPage) }, "media");
2102
2277
  }
2103
- async function getPopular(client, options) {
2278
+ async function getPopular(client, options, include) {
2104
2279
  const { type = "ANIME" /* ANIME */, isAdult = false, idNotIn = [], page = 1, perPage = 20 } = options;
2105
- return searchMedia(client, { type, isAdult, idNotIn, sort: ["POPULARITY_DESC" /* POPULARITY_DESC */], page, perPage });
2280
+ return searchMedia(client, { type, isAdult, idNotIn, sort: ["POPULARITY_DESC" /* POPULARITY_DESC */], page, perPage }, include);
2106
2281
  }
2107
- async function getTopRated(client, options) {
2282
+ async function getTopRated(client, options, include) {
2108
2283
  const { type = "ANIME" /* ANIME */, isAdult = false, idNotIn = [], page = 1, perPage = 20 } = options;
2109
- return searchMedia(client, { type, isAdult, idNotIn, sort: ["SCORE_DESC" /* SCORE_DESC */], page, perPage });
2284
+ return searchMedia(client, { type, isAdult, idNotIn, sort: ["SCORE_DESC" /* SCORE_DESC */], page, perPage }, include);
2110
2285
  }
2111
2286
  async function getAiredEpisodes(client, options = {}) {
2112
2287
  const now = Math.floor(Date.now() / 1e3);
@@ -2123,9 +2298,10 @@ async function getAiredEpisodes(client, options = {}) {
2123
2298
  "airingSchedules"
2124
2299
  );
2125
2300
  }
2126
- async function getRecentlyUpdatedManga(client, options = {}) {
2301
+ async function getRecentlyUpdatedManga(client, options = {}, include) {
2302
+ const query = buildGetRecentlyUpdatedMangaQuery(include);
2127
2303
  return client.pagedRequest(
2128
- QUERY_RECENT_CHAPTERS,
2304
+ query,
2129
2305
  {
2130
2306
  isAdult: options.isAdult ?? false,
2131
2307
  idNotIn: options.idNotIn ?? [],
@@ -2135,9 +2311,10 @@ async function getRecentlyUpdatedManga(client, options = {}) {
2135
2311
  "media"
2136
2312
  );
2137
2313
  }
2138
- async function getPlanning(client, options = {}) {
2314
+ async function getPlanning(client, options = {}, include) {
2315
+ const query = buildGetPlanningQuery(include);
2139
2316
  return client.pagedRequest(
2140
- QUERY_PLANNING,
2317
+ query,
2141
2318
  {
2142
2319
  type: options.type,
2143
2320
  isAdult: options.isAdult ?? false,
@@ -2162,9 +2339,10 @@ async function getRecommendations(client, mediaId, options = {}) {
2162
2339
  results: data.Media.recommendations.nodes
2163
2340
  };
2164
2341
  }
2165
- async function getMediaBySeason(client, options) {
2342
+ async function getMediaBySeason(client, options, include) {
2343
+ const query = buildGetMediaBySeasonQuery(include);
2166
2344
  return client.pagedRequest(
2167
- QUERY_MEDIA_BY_SEASON,
2345
+ query,
2168
2346
  {
2169
2347
  season: options.season,
2170
2348
  seasonYear: options.seasonYear,
@@ -2361,7 +2539,7 @@ function mapFavorites(fav) {
2361
2539
 
2362
2540
  // src/client/index.ts
2363
2541
  var DEFAULT_API_URL = "https://graphql.anilist.co";
2364
- var LIB_VERSION = "2.1.3" ;
2542
+ var LIB_VERSION = "2.2.0" ;
2365
2543
  var AniListClient = class {
2366
2544
  apiUrl;
2367
2545
  headers;
@@ -2531,20 +2709,20 @@ var AniListClient = class {
2531
2709
  * @param options - Search / filter parameters
2532
2710
  * @returns Paginated results with matching media
2533
2711
  */
2534
- async searchMedia(options = {}) {
2535
- return searchMedia(this, options);
2712
+ async searchMedia(options = {}, include) {
2713
+ return searchMedia(this, options, include);
2536
2714
  }
2537
2715
  /** Get currently trending anime or manga. */
2538
- async getTrending(options = {}) {
2539
- return getTrending(this, options);
2716
+ async getTrending(options = {}, include) {
2717
+ return getTrending(this, options, include);
2540
2718
  }
2541
2719
  /** Get the most popular anime or manga. */
2542
- async getPopular(options = {}) {
2543
- return getPopular(this, options);
2720
+ async getPopular(options = {}, include) {
2721
+ return getPopular(this, options, include);
2544
2722
  }
2545
2723
  /** Get the highest-rated anime or manga. */
2546
- async getTopRated(options = {}) {
2547
- return getTopRated(this, options);
2724
+ async getTopRated(options = {}, include) {
2725
+ return getTopRated(this, options, include);
2548
2726
  }
2549
2727
  /** Get recently aired anime episodes. */
2550
2728
  async getAiredEpisodes(options = {}) {
@@ -2555,14 +2733,8 @@ var AniListClient = class {
2555
2733
  *
2556
2734
  * @param options - Pagination parameters
2557
2735
  */
2558
- async getRecentlyUpdatedManga(options = {}) {
2559
- return getRecentlyUpdatedManga(this, options);
2560
- }
2561
- /**
2562
- * @deprecated Use `getRecentlyUpdatedManga()` instead. This alias will be removed in v3.
2563
- */
2564
- async getAiredChapters(options = {}) {
2565
- return this.getRecentlyUpdatedManga(options);
2736
+ async getRecentlyUpdatedManga(options = {}, include) {
2737
+ return getRecentlyUpdatedManga(this, options, include);
2566
2738
  }
2567
2739
  /**
2568
2740
  * Fetch a media entry by its MyAnimeList (MAL) ID.
@@ -2578,16 +2750,16 @@ var AniListClient = class {
2578
2750
  return getWeeklySchedule(this, date, idNotIn);
2579
2751
  }
2580
2752
  /** Get upcoming (not yet released) media. */
2581
- async getPlanning(options = {}) {
2582
- return getPlanning(this, options);
2753
+ async getPlanning(options = {}, include) {
2754
+ return getPlanning(this, options, include);
2583
2755
  }
2584
2756
  /** Get recommendations for a specific media. */
2585
2757
  async getRecommendations(mediaId, options = {}) {
2586
2758
  return getRecommendations(this, mediaId, options);
2587
2759
  }
2588
2760
  /** Get anime (or manga) for a specific season and year. */
2589
- async getMediaBySeason(options) {
2590
- return getMediaBySeason(this, options);
2761
+ async getMediaBySeason(options, include) {
2762
+ return getMediaBySeason(this, options, include);
2591
2763
  }
2592
2764
  /** Fetch a character by AniList ID. Pass `{ voiceActors: true }` to include VA data. */
2593
2765
  async getCharacter(id, include) {