ani-client 2.1.4 → 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.
@@ -1181,8 +1198,7 @@ query ($mediaId: Int!, $page: Int, $perPage: Int, $sort: [RecommendationSort]) {
1181
1198
  }`;
1182
1199
 
1183
1200
  // src/queries/builders.ts
1184
- function buildMediaByIdQuery(include) {
1185
- if (!include) return QUERY_MEDIA_BY_ID;
1201
+ function buildMediaIncludeQuery(include) {
1186
1202
  const extra = [];
1187
1203
  if (include.relations !== false) {
1188
1204
  extra.push(RELATIONS_FIELDS);
@@ -1257,14 +1273,172 @@ function buildMediaByIdQuery(include) {
1257
1273
  statusDistribution { status amount }
1258
1274
  }`);
1259
1275
  }
1276
+ return extra;
1277
+ }
1278
+ function buildMediaByIdQuery(include) {
1279
+ if (!include) return QUERY_MEDIA_BY_ID;
1260
1280
  return `
1261
1281
  query ($id: Int!) {
1262
1282
  Media(id: $id) {
1263
1283
  ${MEDIA_FIELDS_BASE}
1264
- ${extra.join("\n")}
1284
+ ${buildMediaIncludeQuery(include).join("\n")}
1265
1285
  }
1266
1286
  }`;
1267
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
+ }
1268
1442
  function buildMediaCharactersQuery(options = {}) {
1269
1443
  const sortClause = options.sort === false ? "" : ", sort: [ROLE, RELEVANCE, ID]";
1270
1444
  const voiceActorBlock = options.voiceActors ? `
@@ -2066,7 +2240,7 @@ async function getMediaByMalId(client, malId, type) {
2066
2240
  });
2067
2241
  return data.Media;
2068
2242
  }
2069
- async function searchMedia(client, options = {}) {
2243
+ async function searchMedia(client, options = {}, include) {
2070
2244
  const {
2071
2245
  query: search,
2072
2246
  page = 1,
@@ -2078,8 +2252,9 @@ async function searchMedia(client, options = {}) {
2078
2252
  format,
2079
2253
  ...filters
2080
2254
  } = options;
2255
+ const query = buildSearchMediaQuery(include);
2081
2256
  return client.pagedRequest(
2082
- QUERY_MEDIA_SEARCH,
2257
+ query,
2083
2258
  {
2084
2259
  search,
2085
2260
  ...filters,
@@ -2095,21 +2270,18 @@ async function searchMedia(client, options = {}) {
2095
2270
  "media"
2096
2271
  );
2097
2272
  }
2098
- async function getTrending(client, options) {
2273
+ async function getTrending(client, options, include) {
2099
2274
  const { type = "ANIME" /* ANIME */, isAdult = false, idNotIn = [], page = 1, perPage = 20 } = options;
2100
- return client.pagedRequest(
2101
- QUERY_TRENDING,
2102
- { type, isAdult, idNotIn, page, perPage: clampPerPage(perPage) },
2103
- "media"
2104
- );
2275
+ const query = buildGetTrendingQuery(include);
2276
+ return client.pagedRequest(query, { type, isAdult, idNotIn, page, perPage: clampPerPage(perPage) }, "media");
2105
2277
  }
2106
- async function getPopular(client, options) {
2278
+ async function getPopular(client, options, include) {
2107
2279
  const { type = "ANIME" /* ANIME */, isAdult = false, idNotIn = [], page = 1, perPage = 20 } = options;
2108
- 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);
2109
2281
  }
2110
- async function getTopRated(client, options) {
2282
+ async function getTopRated(client, options, include) {
2111
2283
  const { type = "ANIME" /* ANIME */, isAdult = false, idNotIn = [], page = 1, perPage = 20 } = options;
2112
- 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);
2113
2285
  }
2114
2286
  async function getAiredEpisodes(client, options = {}) {
2115
2287
  const now = Math.floor(Date.now() / 1e3);
@@ -2126,9 +2298,10 @@ async function getAiredEpisodes(client, options = {}) {
2126
2298
  "airingSchedules"
2127
2299
  );
2128
2300
  }
2129
- async function getRecentlyUpdatedManga(client, options = {}) {
2301
+ async function getRecentlyUpdatedManga(client, options = {}, include) {
2302
+ const query = buildGetRecentlyUpdatedMangaQuery(include);
2130
2303
  return client.pagedRequest(
2131
- QUERY_RECENT_CHAPTERS,
2304
+ query,
2132
2305
  {
2133
2306
  isAdult: options.isAdult ?? false,
2134
2307
  idNotIn: options.idNotIn ?? [],
@@ -2138,9 +2311,10 @@ async function getRecentlyUpdatedManga(client, options = {}) {
2138
2311
  "media"
2139
2312
  );
2140
2313
  }
2141
- async function getPlanning(client, options = {}) {
2314
+ async function getPlanning(client, options = {}, include) {
2315
+ const query = buildGetPlanningQuery(include);
2142
2316
  return client.pagedRequest(
2143
- QUERY_PLANNING,
2317
+ query,
2144
2318
  {
2145
2319
  type: options.type,
2146
2320
  isAdult: options.isAdult ?? false,
@@ -2165,9 +2339,10 @@ async function getRecommendations(client, mediaId, options = {}) {
2165
2339
  results: data.Media.recommendations.nodes
2166
2340
  };
2167
2341
  }
2168
- async function getMediaBySeason(client, options) {
2342
+ async function getMediaBySeason(client, options, include) {
2343
+ const query = buildGetMediaBySeasonQuery(include);
2169
2344
  return client.pagedRequest(
2170
- QUERY_MEDIA_BY_SEASON,
2345
+ query,
2171
2346
  {
2172
2347
  season: options.season,
2173
2348
  seasonYear: options.seasonYear,
@@ -2364,7 +2539,7 @@ function mapFavorites(fav) {
2364
2539
 
2365
2540
  // src/client/index.ts
2366
2541
  var DEFAULT_API_URL = "https://graphql.anilist.co";
2367
- var LIB_VERSION = "2.1.4" ;
2542
+ var LIB_VERSION = "2.2.0" ;
2368
2543
  var AniListClient = class {
2369
2544
  apiUrl;
2370
2545
  headers;
@@ -2534,20 +2709,20 @@ var AniListClient = class {
2534
2709
  * @param options - Search / filter parameters
2535
2710
  * @returns Paginated results with matching media
2536
2711
  */
2537
- async searchMedia(options = {}) {
2538
- return searchMedia(this, options);
2712
+ async searchMedia(options = {}, include) {
2713
+ return searchMedia(this, options, include);
2539
2714
  }
2540
2715
  /** Get currently trending anime or manga. */
2541
- async getTrending(options = {}) {
2542
- return getTrending(this, options);
2716
+ async getTrending(options = {}, include) {
2717
+ return getTrending(this, options, include);
2543
2718
  }
2544
2719
  /** Get the most popular anime or manga. */
2545
- async getPopular(options = {}) {
2546
- return getPopular(this, options);
2720
+ async getPopular(options = {}, include) {
2721
+ return getPopular(this, options, include);
2547
2722
  }
2548
2723
  /** Get the highest-rated anime or manga. */
2549
- async getTopRated(options = {}) {
2550
- return getTopRated(this, options);
2724
+ async getTopRated(options = {}, include) {
2725
+ return getTopRated(this, options, include);
2551
2726
  }
2552
2727
  /** Get recently aired anime episodes. */
2553
2728
  async getAiredEpisodes(options = {}) {
@@ -2558,14 +2733,8 @@ var AniListClient = class {
2558
2733
  *
2559
2734
  * @param options - Pagination parameters
2560
2735
  */
2561
- async getRecentlyUpdatedManga(options = {}) {
2562
- return getRecentlyUpdatedManga(this, options);
2563
- }
2564
- /**
2565
- * @deprecated Use `getRecentlyUpdatedManga()` instead. This alias will be removed in v3.
2566
- */
2567
- async getAiredChapters(options = {}) {
2568
- return this.getRecentlyUpdatedManga(options);
2736
+ async getRecentlyUpdatedManga(options = {}, include) {
2737
+ return getRecentlyUpdatedManga(this, options, include);
2569
2738
  }
2570
2739
  /**
2571
2740
  * Fetch a media entry by its MyAnimeList (MAL) ID.
@@ -2581,16 +2750,16 @@ var AniListClient = class {
2581
2750
  return getWeeklySchedule(this, date, idNotIn);
2582
2751
  }
2583
2752
  /** Get upcoming (not yet released) media. */
2584
- async getPlanning(options = {}) {
2585
- return getPlanning(this, options);
2753
+ async getPlanning(options = {}, include) {
2754
+ return getPlanning(this, options, include);
2586
2755
  }
2587
2756
  /** Get recommendations for a specific media. */
2588
2757
  async getRecommendations(mediaId, options = {}) {
2589
2758
  return getRecommendations(this, mediaId, options);
2590
2759
  }
2591
2760
  /** Get anime (or manga) for a specific season and year. */
2592
- async getMediaBySeason(options) {
2593
- return getMediaBySeason(this, options);
2761
+ async getMediaBySeason(options, include) {
2762
+ return getMediaBySeason(this, options, include);
2594
2763
  }
2595
2764
  /** Fetch a character by AniList ID. Pass `{ voiceActors: true }` to include VA data. */
2596
2765
  async getCharacter(id, include) {