ani-client 1.1.0 → 1.3.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 +363 -127
- package/dist/index.d.mts +440 -7
- package/dist/index.d.ts +440 -7
- package/dist/index.js +703 -81
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +699 -82
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -15
package/dist/index.mjs
CHANGED
|
@@ -31,6 +31,20 @@ var MEDIA_FIELDS = `
|
|
|
31
31
|
trending
|
|
32
32
|
tags { id name description category rank isMediaSpoiler }
|
|
33
33
|
studios { nodes { id name isAnimationStudio siteUrl } }
|
|
34
|
+
relations {
|
|
35
|
+
edges {
|
|
36
|
+
relationType(version: 2)
|
|
37
|
+
node {
|
|
38
|
+
id
|
|
39
|
+
title { romaji english native userPreferred }
|
|
40
|
+
type
|
|
41
|
+
format
|
|
42
|
+
status
|
|
43
|
+
coverImage { large medium }
|
|
44
|
+
siteUrl
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
34
48
|
isAdult
|
|
35
49
|
siteUrl
|
|
36
50
|
`;
|
|
@@ -213,16 +227,133 @@ query ($type: MediaType, $sort: [MediaSort], $page: Int, $perPage: Int) {
|
|
|
213
227
|
}
|
|
214
228
|
}
|
|
215
229
|
}`;
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
this.errors = errors;
|
|
230
|
+
var QUERY_MEDIA_BY_SEASON = `
|
|
231
|
+
query ($season: MediaSeason!, $seasonYear: Int!, $type: MediaType, $sort: [MediaSort], $page: Int, $perPage: Int) {
|
|
232
|
+
Page(page: $page, perPage: $perPage) {
|
|
233
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
234
|
+
media(season: $season, seasonYear: $seasonYear, type: $type, sort: $sort) {
|
|
235
|
+
${MEDIA_FIELDS}
|
|
236
|
+
}
|
|
224
237
|
}
|
|
225
|
-
}
|
|
238
|
+
}`;
|
|
239
|
+
var MEDIA_LIST_FIELDS = `
|
|
240
|
+
id
|
|
241
|
+
mediaId
|
|
242
|
+
status
|
|
243
|
+
score(format: POINT_100)
|
|
244
|
+
progress
|
|
245
|
+
progressVolumes
|
|
246
|
+
repeat
|
|
247
|
+
priority
|
|
248
|
+
private
|
|
249
|
+
notes
|
|
250
|
+
startedAt { year month day }
|
|
251
|
+
completedAt { year month day }
|
|
252
|
+
updatedAt
|
|
253
|
+
createdAt
|
|
254
|
+
media {
|
|
255
|
+
${MEDIA_FIELDS}
|
|
256
|
+
}
|
|
257
|
+
`;
|
|
258
|
+
var QUERY_RECOMMENDATIONS = `
|
|
259
|
+
query ($mediaId: Int!, $page: Int, $perPage: Int, $sort: [RecommendationSort]) {
|
|
260
|
+
Media(id: $mediaId) {
|
|
261
|
+
recommendations(page: $page, perPage: $perPage, sort: $sort) {
|
|
262
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
263
|
+
nodes {
|
|
264
|
+
id
|
|
265
|
+
rating
|
|
266
|
+
userRating
|
|
267
|
+
mediaRecommendation {
|
|
268
|
+
id
|
|
269
|
+
idMal
|
|
270
|
+
title { romaji english native userPreferred }
|
|
271
|
+
type
|
|
272
|
+
format
|
|
273
|
+
status
|
|
274
|
+
coverImage { extraLarge large medium color }
|
|
275
|
+
bannerImage
|
|
276
|
+
genres
|
|
277
|
+
averageScore
|
|
278
|
+
meanScore
|
|
279
|
+
popularity
|
|
280
|
+
favourites
|
|
281
|
+
siteUrl
|
|
282
|
+
}
|
|
283
|
+
user {
|
|
284
|
+
id
|
|
285
|
+
name
|
|
286
|
+
avatar { large medium }
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}`;
|
|
292
|
+
var QUERY_USER_MEDIA_LIST = `
|
|
293
|
+
query ($userId: Int, $userName: String, $type: MediaType!, $status: MediaListStatus, $sort: [MediaListSort], $page: Int, $perPage: Int) {
|
|
294
|
+
Page(page: $page, perPage: $perPage) {
|
|
295
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
296
|
+
mediaList(userId: $userId, userName: $userName, type: $type, status: $status, sort: $sort) {
|
|
297
|
+
${MEDIA_LIST_FIELDS}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}`;
|
|
301
|
+
var STUDIO_FIELDS = `
|
|
302
|
+
id
|
|
303
|
+
name
|
|
304
|
+
isAnimationStudio
|
|
305
|
+
siteUrl
|
|
306
|
+
favourites
|
|
307
|
+
media(page: 1, perPage: 25, sort: POPULARITY_DESC) {
|
|
308
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
309
|
+
nodes {
|
|
310
|
+
id
|
|
311
|
+
title { romaji english native userPreferred }
|
|
312
|
+
type
|
|
313
|
+
format
|
|
314
|
+
coverImage { large medium }
|
|
315
|
+
siteUrl
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
`;
|
|
319
|
+
var QUERY_STUDIO_BY_ID = `
|
|
320
|
+
query ($id: Int!) {
|
|
321
|
+
Studio(id: $id) {
|
|
322
|
+
${STUDIO_FIELDS}
|
|
323
|
+
}
|
|
324
|
+
}`;
|
|
325
|
+
var QUERY_STUDIO_SEARCH = `
|
|
326
|
+
query ($search: String, $page: Int, $perPage: Int) {
|
|
327
|
+
Page(page: $page, perPage: $perPage) {
|
|
328
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
329
|
+
studios(search: $search) {
|
|
330
|
+
${STUDIO_FIELDS}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}`;
|
|
334
|
+
var QUERY_GENRES = `
|
|
335
|
+
query {
|
|
336
|
+
GenreCollection
|
|
337
|
+
}`;
|
|
338
|
+
var QUERY_TAGS = `
|
|
339
|
+
query {
|
|
340
|
+
MediaTagCollection {
|
|
341
|
+
id
|
|
342
|
+
name
|
|
343
|
+
description
|
|
344
|
+
category
|
|
345
|
+
isAdult
|
|
346
|
+
}
|
|
347
|
+
}`;
|
|
348
|
+
function buildBatchQuery(ids, typeName, fields, prefix) {
|
|
349
|
+
const aliases = ids.map((id, i) => `${prefix}${i}: ${typeName}(id: ${id}) { ${fields} }`).join("\n ");
|
|
350
|
+
return `query {
|
|
351
|
+
${aliases}
|
|
352
|
+
}`;
|
|
353
|
+
}
|
|
354
|
+
var buildBatchMediaQuery = (ids) => buildBatchQuery(ids, "Media", MEDIA_FIELDS, "m");
|
|
355
|
+
var buildBatchCharacterQuery = (ids) => buildBatchQuery(ids, "Character", CHARACTER_FIELDS, "c");
|
|
356
|
+
var buildBatchStaffQuery = (ids) => buildBatchQuery(ids, "Staff", STAFF_FIELDS, "s");
|
|
226
357
|
|
|
227
358
|
// src/cache/index.ts
|
|
228
359
|
var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -235,7 +366,7 @@ var MemoryCache = class {
|
|
|
235
366
|
}
|
|
236
367
|
/** Build a deterministic cache key from a query + variables pair. */
|
|
237
368
|
static key(query, variables) {
|
|
238
|
-
return query.trim()
|
|
369
|
+
return `${query.trim()}|${JSON.stringify(variables, Object.keys(variables).sort())}`;
|
|
239
370
|
}
|
|
240
371
|
/** Retrieve a cached value, or `undefined` if missing / expired. */
|
|
241
372
|
get(key) {
|
|
@@ -246,11 +377,14 @@ var MemoryCache = class {
|
|
|
246
377
|
this.store.delete(key);
|
|
247
378
|
return void 0;
|
|
248
379
|
}
|
|
380
|
+
this.store.delete(key);
|
|
381
|
+
this.store.set(key, entry);
|
|
249
382
|
return entry.data;
|
|
250
383
|
}
|
|
251
384
|
/** Store a value in the cache. */
|
|
252
385
|
set(key, data) {
|
|
253
386
|
if (!this.enabled) return;
|
|
387
|
+
this.store.delete(key);
|
|
254
388
|
if (this.maxSize > 0 && this.store.size >= this.maxSize) {
|
|
255
389
|
const firstKey = this.store.keys().next().value;
|
|
256
390
|
if (firstKey !== void 0) this.store.delete(firstKey);
|
|
@@ -269,6 +403,37 @@ var MemoryCache = class {
|
|
|
269
403
|
get size() {
|
|
270
404
|
return this.store.size;
|
|
271
405
|
}
|
|
406
|
+
/** Return an iterator over all cache keys. */
|
|
407
|
+
keys() {
|
|
408
|
+
return this.store.keys();
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Remove all entries whose key matches the given pattern.
|
|
412
|
+
*
|
|
413
|
+
* @param pattern — A string (converted to RegExp) or RegExp.
|
|
414
|
+
* @returns Number of entries removed.
|
|
415
|
+
*/
|
|
416
|
+
invalidate(pattern) {
|
|
417
|
+
const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
|
|
418
|
+
let count = 0;
|
|
419
|
+
for (const key of [...this.store.keys()]) {
|
|
420
|
+
if (regex.test(key)) {
|
|
421
|
+
this.store.delete(key);
|
|
422
|
+
count++;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return count;
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
// src/errors/index.ts
|
|
430
|
+
var AniListError = class extends Error {
|
|
431
|
+
constructor(message, status, errors = []) {
|
|
432
|
+
super(message);
|
|
433
|
+
this.name = "AniListError";
|
|
434
|
+
this.status = status;
|
|
435
|
+
this.errors = errors;
|
|
436
|
+
}
|
|
272
437
|
};
|
|
273
438
|
|
|
274
439
|
// src/rate-limiter/index.ts
|
|
@@ -281,6 +446,8 @@ var RateLimiter = class {
|
|
|
281
446
|
this.maxRetries = options.maxRetries ?? 3;
|
|
282
447
|
this.retryDelayMs = options.retryDelayMs ?? 2e3;
|
|
283
448
|
this.enabled = options.enabled ?? true;
|
|
449
|
+
this.timeoutMs = options.timeoutMs ?? 3e4;
|
|
450
|
+
this.retryOnNetworkError = options.retryOnNetworkError ?? true;
|
|
284
451
|
}
|
|
285
452
|
/**
|
|
286
453
|
* Wait until it's safe to make a request (respects rate limit window).
|
|
@@ -298,66 +465,141 @@ var RateLimiter = class {
|
|
|
298
465
|
this.timestamps.push(Date.now());
|
|
299
466
|
}
|
|
300
467
|
/**
|
|
301
|
-
* Execute a fetch with automatic retry on 429 responses.
|
|
468
|
+
* Execute a fetch with automatic retry on 429 responses and network errors.
|
|
302
469
|
*/
|
|
303
|
-
async fetchWithRetry(url, init) {
|
|
470
|
+
async fetchWithRetry(url, init, hooks) {
|
|
304
471
|
await this.acquire();
|
|
305
472
|
let lastResponse;
|
|
473
|
+
let lastError;
|
|
306
474
|
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
return res;
|
|
475
|
+
try {
|
|
476
|
+
const res = await this.fetchWithTimeout(url, init);
|
|
477
|
+
if (res.status !== 429) return res;
|
|
478
|
+
lastResponse = res;
|
|
479
|
+
if (attempt === this.maxRetries) break;
|
|
480
|
+
const retryAfter = res.headers.get("Retry-After");
|
|
481
|
+
const delayMs = retryAfter ? Number.parseInt(retryAfter, 10) * 1e3 : this.retryDelayMs * (attempt + 1);
|
|
482
|
+
hooks?.onRateLimit?.(delayMs);
|
|
483
|
+
hooks?.onRetry?.(attempt + 1, "HTTP 429", delayMs);
|
|
484
|
+
await this.sleep(delayMs);
|
|
485
|
+
await this.acquire();
|
|
486
|
+
} catch (err) {
|
|
487
|
+
lastError = err;
|
|
488
|
+
if (this.retryOnNetworkError && isNetworkError(err) && attempt < this.maxRetries) {
|
|
489
|
+
const delayMs = this.retryDelayMs * (attempt + 1);
|
|
490
|
+
hooks?.onRetry?.(attempt + 1, `Network error: ${err.message}`, delayMs);
|
|
491
|
+
await this.sleep(delayMs);
|
|
492
|
+
await this.acquire();
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
throw err;
|
|
310
496
|
}
|
|
311
|
-
lastResponse = res;
|
|
312
|
-
if (attempt === this.maxRetries) break;
|
|
313
|
-
const retryAfter = res.headers.get("Retry-After");
|
|
314
|
-
const delayMs = retryAfter ? parseInt(retryAfter, 10) * 1e3 : this.retryDelayMs * (attempt + 1);
|
|
315
|
-
await this.sleep(delayMs);
|
|
316
|
-
await this.acquire();
|
|
317
497
|
}
|
|
318
|
-
return lastResponse;
|
|
498
|
+
if (lastResponse) return lastResponse;
|
|
499
|
+
throw lastError;
|
|
500
|
+
}
|
|
501
|
+
/** @internal */
|
|
502
|
+
async fetchWithTimeout(url, init) {
|
|
503
|
+
if (this.timeoutMs <= 0) return fetch(url, init);
|
|
504
|
+
const controller = new AbortController();
|
|
505
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
506
|
+
try {
|
|
507
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
508
|
+
} finally {
|
|
509
|
+
clearTimeout(timer);
|
|
510
|
+
}
|
|
319
511
|
}
|
|
320
512
|
sleep(ms) {
|
|
321
513
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
322
514
|
}
|
|
323
515
|
};
|
|
516
|
+
var RETRYABLE_NETWORK_CODES = /* @__PURE__ */ new Set([
|
|
517
|
+
"ECONNRESET",
|
|
518
|
+
"ECONNREFUSED",
|
|
519
|
+
"ETIMEDOUT",
|
|
520
|
+
"ENOTFOUND",
|
|
521
|
+
"EAI_AGAIN",
|
|
522
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
523
|
+
"UND_ERR_SOCKET"
|
|
524
|
+
]);
|
|
525
|
+
function isNetworkError(err) {
|
|
526
|
+
if (err instanceof TypeError && err.message === "fetch failed") return true;
|
|
527
|
+
const code = err?.code;
|
|
528
|
+
if (code && RETRYABLE_NETWORK_CODES.has(code)) return true;
|
|
529
|
+
const cause = err?.cause?.code;
|
|
530
|
+
if (cause && RETRYABLE_NETWORK_CODES.has(cause)) return true;
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
324
533
|
|
|
325
534
|
// src/client/index.ts
|
|
326
535
|
var DEFAULT_API_URL = "https://graphql.anilist.co";
|
|
327
536
|
var AniListClient = class {
|
|
328
537
|
constructor(options = {}) {
|
|
538
|
+
this.inFlight = /* @__PURE__ */ new Map();
|
|
329
539
|
this.apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
330
540
|
this.headers = {
|
|
331
541
|
"Content-Type": "application/json",
|
|
332
542
|
Accept: "application/json"
|
|
333
543
|
};
|
|
334
544
|
if (options.token) {
|
|
335
|
-
this.headers
|
|
545
|
+
this.headers.Authorization = `Bearer ${options.token}`;
|
|
336
546
|
}
|
|
337
|
-
this.
|
|
547
|
+
this.cacheAdapter = options.cacheAdapter ?? new MemoryCache(options.cache);
|
|
338
548
|
this.rateLimiter = new RateLimiter(options.rateLimit);
|
|
549
|
+
this.hooks = options.hooks ?? {};
|
|
339
550
|
}
|
|
340
551
|
/**
|
|
341
552
|
* @internal
|
|
342
553
|
*/
|
|
343
554
|
async request(query, variables = {}) {
|
|
344
555
|
const cacheKey = MemoryCache.key(query, variables);
|
|
345
|
-
const cached = this.
|
|
346
|
-
if (cached !== void 0)
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
556
|
+
const cached = await this.cacheAdapter.get(cacheKey);
|
|
557
|
+
if (cached !== void 0) {
|
|
558
|
+
this.hooks.onCacheHit?.(cacheKey);
|
|
559
|
+
this.hooks.onResponse?.(query, 0, true);
|
|
560
|
+
return cached;
|
|
561
|
+
}
|
|
562
|
+
const existing = this.inFlight.get(cacheKey);
|
|
563
|
+
if (existing) return existing;
|
|
564
|
+
const promise = this.executeRequest(query, variables, cacheKey);
|
|
565
|
+
this.inFlight.set(cacheKey, promise);
|
|
566
|
+
try {
|
|
567
|
+
return await promise;
|
|
568
|
+
} finally {
|
|
569
|
+
this.inFlight.delete(cacheKey);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
/** @internal */
|
|
573
|
+
async executeRequest(query, variables, cacheKey) {
|
|
574
|
+
const start = Date.now();
|
|
575
|
+
this.hooks.onRequest?.(query, variables);
|
|
576
|
+
const res = await this.rateLimiter.fetchWithRetry(
|
|
577
|
+
this.apiUrl,
|
|
578
|
+
{
|
|
579
|
+
method: "POST",
|
|
580
|
+
headers: this.headers,
|
|
581
|
+
body: JSON.stringify({ query, variables })
|
|
582
|
+
},
|
|
583
|
+
{ onRetry: this.hooks.onRetry, onRateLimit: this.hooks.onRateLimit }
|
|
584
|
+
);
|
|
352
585
|
const json = await res.json();
|
|
353
586
|
if (!res.ok || json.errors) {
|
|
354
587
|
const message = json.errors?.[0]?.message ?? `AniList API error (HTTP ${res.status})`;
|
|
355
588
|
throw new AniListError(message, res.status, json.errors ?? []);
|
|
356
589
|
}
|
|
357
590
|
const data = json.data;
|
|
358
|
-
this.
|
|
591
|
+
await this.cacheAdapter.set(cacheKey, data);
|
|
592
|
+
this.hooks.onResponse?.(query, Date.now() - start, false);
|
|
359
593
|
return data;
|
|
360
594
|
}
|
|
595
|
+
/**
|
|
596
|
+
* @internal
|
|
597
|
+
* Shorthand for paginated queries that follow the `Page { pageInfo, <field>[] }` pattern.
|
|
598
|
+
*/
|
|
599
|
+
async pagedRequest(query, variables, field) {
|
|
600
|
+
const data = await this.request(query, variables);
|
|
601
|
+
return { pageInfo: data.Page.pageInfo, results: data.Page[field] };
|
|
602
|
+
}
|
|
361
603
|
/**
|
|
362
604
|
* Fetch a single media entry by its AniList ID.
|
|
363
605
|
*
|
|
@@ -384,22 +626,8 @@ var AniListClient = class {
|
|
|
384
626
|
* ```
|
|
385
627
|
*/
|
|
386
628
|
async searchMedia(options = {}) {
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
type: options.type,
|
|
390
|
-
format: options.format,
|
|
391
|
-
status: options.status,
|
|
392
|
-
season: options.season,
|
|
393
|
-
seasonYear: options.seasonYear,
|
|
394
|
-
genre: options.genre,
|
|
395
|
-
tag: options.tag,
|
|
396
|
-
isAdult: options.isAdult,
|
|
397
|
-
sort: options.sort,
|
|
398
|
-
page: options.page ?? 1,
|
|
399
|
-
perPage: options.perPage ?? 20
|
|
400
|
-
};
|
|
401
|
-
const data = await this.request(QUERY_MEDIA_SEARCH, variables);
|
|
402
|
-
return { pageInfo: data.Page.pageInfo, results: data.Page.media };
|
|
629
|
+
const { query: search, page = 1, perPage = 20, ...filters } = options;
|
|
630
|
+
return this.pagedRequest(QUERY_MEDIA_SEARCH, { search, ...filters, page, perPage }, "media");
|
|
403
631
|
}
|
|
404
632
|
/**
|
|
405
633
|
* Get currently trending anime or manga.
|
|
@@ -409,8 +637,7 @@ var AniListClient = class {
|
|
|
409
637
|
* @param perPage - Results per page (default 20, max 50)
|
|
410
638
|
*/
|
|
411
639
|
async getTrending(type = "ANIME", page = 1, perPage = 20) {
|
|
412
|
-
|
|
413
|
-
return { pageInfo: data.Page.pageInfo, results: data.Page.media };
|
|
640
|
+
return this.pagedRequest(QUERY_TRENDING, { type, page, perPage }, "media");
|
|
414
641
|
}
|
|
415
642
|
/**
|
|
416
643
|
* Fetch a character by AniList ID.
|
|
@@ -423,14 +650,8 @@ var AniListClient = class {
|
|
|
423
650
|
* Search for characters by name.
|
|
424
651
|
*/
|
|
425
652
|
async searchCharacters(options = {}) {
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
sort: options.sort,
|
|
429
|
-
page: options.page ?? 1,
|
|
430
|
-
perPage: options.perPage ?? 20
|
|
431
|
-
};
|
|
432
|
-
const data = await this.request(QUERY_CHARACTER_SEARCH, variables);
|
|
433
|
-
return { pageInfo: data.Page.pageInfo, results: data.Page.characters };
|
|
653
|
+
const { query: search, page = 1, perPage = 20, ...rest } = options;
|
|
654
|
+
return this.pagedRequest(QUERY_CHARACTER_SEARCH, { search, ...rest, page, perPage }, "characters");
|
|
434
655
|
}
|
|
435
656
|
/**
|
|
436
657
|
* Fetch a staff member by AniList ID.
|
|
@@ -443,13 +664,8 @@ var AniListClient = class {
|
|
|
443
664
|
* Search for staff (voice actors, directors, etc.).
|
|
444
665
|
*/
|
|
445
666
|
async searchStaff(options = {}) {
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
page: options.page ?? 1,
|
|
449
|
-
perPage: options.perPage ?? 20
|
|
450
|
-
};
|
|
451
|
-
const data = await this.request(QUERY_STAFF_SEARCH, variables);
|
|
452
|
-
return { pageInfo: data.Page.pageInfo, results: data.Page.staff };
|
|
667
|
+
const { query: search, page = 1, perPage = 20 } = options;
|
|
668
|
+
return this.pagedRequest(QUERY_STAFF_SEARCH, { search, page, perPage }, "staff");
|
|
453
669
|
}
|
|
454
670
|
/**
|
|
455
671
|
* Fetch a user by AniList ID.
|
|
@@ -500,8 +716,7 @@ var AniListClient = class {
|
|
|
500
716
|
page: options.page ?? 1,
|
|
501
717
|
perPage: options.perPage ?? 20
|
|
502
718
|
};
|
|
503
|
-
|
|
504
|
-
return { pageInfo: data.Page.pageInfo, results: data.Page.airingSchedules };
|
|
719
|
+
return this.pagedRequest(QUERY_AIRING_SCHEDULE, variables, "airingSchedules");
|
|
505
720
|
}
|
|
506
721
|
/**
|
|
507
722
|
* Get manga that are currently releasing, sorted by most recently updated.
|
|
@@ -518,12 +733,14 @@ var AniListClient = class {
|
|
|
518
733
|
* ```
|
|
519
734
|
*/
|
|
520
735
|
async getAiredChapters(options = {}) {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
736
|
+
return this.pagedRequest(
|
|
737
|
+
QUERY_RECENT_CHAPTERS,
|
|
738
|
+
{
|
|
739
|
+
page: options.page ?? 1,
|
|
740
|
+
perPage: options.perPage ?? 20
|
|
741
|
+
},
|
|
742
|
+
"media"
|
|
743
|
+
);
|
|
527
744
|
}
|
|
528
745
|
/**
|
|
529
746
|
* Get upcoming (not yet released) anime and/or manga, sorted by popularity.
|
|
@@ -540,26 +757,361 @@ var AniListClient = class {
|
|
|
540
757
|
* ```
|
|
541
758
|
*/
|
|
542
759
|
async getPlanning(options = {}) {
|
|
760
|
+
return this.pagedRequest(
|
|
761
|
+
QUERY_PLANNING,
|
|
762
|
+
{
|
|
763
|
+
type: options.type,
|
|
764
|
+
sort: options.sort ?? ["POPULARITY_DESC"],
|
|
765
|
+
page: options.page ?? 1,
|
|
766
|
+
perPage: options.perPage ?? 20
|
|
767
|
+
},
|
|
768
|
+
"media"
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Get recommendations for a specific media.
|
|
773
|
+
*
|
|
774
|
+
* Returns other anime/manga that users have recommended based on the given media.
|
|
775
|
+
*
|
|
776
|
+
* @param mediaId - The AniList media ID
|
|
777
|
+
* @param options - Optional sort / pagination parameters
|
|
778
|
+
* @returns Paginated list of recommendations
|
|
779
|
+
*
|
|
780
|
+
* @example
|
|
781
|
+
* ```ts
|
|
782
|
+
* // Get recommendations for Cowboy Bebop
|
|
783
|
+
* const recs = await client.getRecommendations(1);
|
|
784
|
+
* recs.results.forEach((r) =>
|
|
785
|
+
* console.log(`${r.mediaRecommendation.title.romaji} (rating: ${r.rating})`)
|
|
786
|
+
* );
|
|
787
|
+
* ```
|
|
788
|
+
*/
|
|
789
|
+
async getRecommendations(mediaId, options = {}) {
|
|
543
790
|
const variables = {
|
|
544
|
-
|
|
545
|
-
sort: options.sort ?? ["
|
|
791
|
+
mediaId,
|
|
792
|
+
sort: options.sort ?? ["RATING_DESC"],
|
|
546
793
|
page: options.page ?? 1,
|
|
547
794
|
perPage: options.perPage ?? 20
|
|
548
795
|
};
|
|
549
|
-
const data = await this.request(
|
|
550
|
-
return {
|
|
796
|
+
const data = await this.request(QUERY_RECOMMENDATIONS, variables);
|
|
797
|
+
return {
|
|
798
|
+
pageInfo: data.Media.recommendations.pageInfo,
|
|
799
|
+
results: data.Media.recommendations.nodes
|
|
800
|
+
};
|
|
551
801
|
}
|
|
802
|
+
/**
|
|
803
|
+
* Get anime (or manga) for a specific season and year.
|
|
804
|
+
*
|
|
805
|
+
* @param options - Season, year and optional filter / pagination parameters
|
|
806
|
+
* @returns Paginated list of media for the given season
|
|
807
|
+
*
|
|
808
|
+
* @example
|
|
809
|
+
* ```ts
|
|
810
|
+
* import { MediaSeason } from "ani-client";
|
|
811
|
+
*
|
|
812
|
+
* const winter2026 = await client.getMediaBySeason({
|
|
813
|
+
* season: MediaSeason.WINTER,
|
|
814
|
+
* seasonYear: 2026,
|
|
815
|
+
* perPage: 10,
|
|
816
|
+
* });
|
|
817
|
+
* ```
|
|
818
|
+
*/
|
|
819
|
+
async getMediaBySeason(options) {
|
|
820
|
+
return this.pagedRequest(
|
|
821
|
+
QUERY_MEDIA_BY_SEASON,
|
|
822
|
+
{
|
|
823
|
+
season: options.season,
|
|
824
|
+
seasonYear: options.seasonYear,
|
|
825
|
+
type: options.type ?? "ANIME",
|
|
826
|
+
sort: options.sort ?? ["POPULARITY_DESC"],
|
|
827
|
+
page: options.page ?? 1,
|
|
828
|
+
perPage: options.perPage ?? 20
|
|
829
|
+
},
|
|
830
|
+
"media"
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Get a user's anime or manga list.
|
|
835
|
+
*
|
|
836
|
+
* Provide either `userId` or `userName` to identify the user.
|
|
837
|
+
* Requires `type` (ANIME or MANGA). Optionally filter by list status.
|
|
838
|
+
*
|
|
839
|
+
* @param options - User identifier, media type, and optional filters
|
|
840
|
+
* @returns Paginated list of media list entries
|
|
841
|
+
*
|
|
842
|
+
* @example
|
|
843
|
+
* ```ts
|
|
844
|
+
* import { MediaType, MediaListStatus } from "ani-client";
|
|
845
|
+
*
|
|
846
|
+
* // Get a user's completed anime list
|
|
847
|
+
* const list = await client.getUserMediaList({
|
|
848
|
+
* userName: "AniList",
|
|
849
|
+
* type: MediaType.ANIME,
|
|
850
|
+
* status: MediaListStatus.COMPLETED,
|
|
851
|
+
* });
|
|
852
|
+
* list.results.forEach((entry) =>
|
|
853
|
+
* console.log(`${entry.media.title.romaji} — ${entry.score}/100`)
|
|
854
|
+
* );
|
|
855
|
+
* ```
|
|
856
|
+
*/
|
|
857
|
+
async getUserMediaList(options) {
|
|
858
|
+
if (!options.userId && !options.userName) {
|
|
859
|
+
throw new Error("Either userId or userName must be provided");
|
|
860
|
+
}
|
|
861
|
+
return this.pagedRequest(
|
|
862
|
+
QUERY_USER_MEDIA_LIST,
|
|
863
|
+
{
|
|
864
|
+
userId: options.userId,
|
|
865
|
+
userName: options.userName,
|
|
866
|
+
type: options.type,
|
|
867
|
+
status: options.status,
|
|
868
|
+
sort: options.sort,
|
|
869
|
+
page: options.page ?? 1,
|
|
870
|
+
perPage: options.perPage ?? 20
|
|
871
|
+
},
|
|
872
|
+
"mediaList"
|
|
873
|
+
);
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Fetch a studio by its AniList ID.
|
|
877
|
+
*
|
|
878
|
+
* Returns studio details along with its most popular productions.
|
|
879
|
+
*
|
|
880
|
+
* @param id - The AniList studio ID
|
|
881
|
+
*/
|
|
882
|
+
async getStudio(id) {
|
|
883
|
+
const data = await this.request(QUERY_STUDIO_BY_ID, { id });
|
|
884
|
+
return data.Studio;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* Search for studios by name.
|
|
888
|
+
*
|
|
889
|
+
* @param options - Search / pagination parameters
|
|
890
|
+
* @returns Paginated list of studios
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* ```ts
|
|
894
|
+
* const studios = await client.searchStudios({ query: "MAPPA" });
|
|
895
|
+
* ```
|
|
896
|
+
*/
|
|
897
|
+
async searchStudios(options = {}) {
|
|
898
|
+
return this.pagedRequest(
|
|
899
|
+
QUERY_STUDIO_SEARCH,
|
|
900
|
+
{
|
|
901
|
+
search: options.query,
|
|
902
|
+
page: options.page ?? 1,
|
|
903
|
+
perPage: options.perPage ?? 20
|
|
904
|
+
},
|
|
905
|
+
"studios"
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Get all available genres on AniList.
|
|
910
|
+
*
|
|
911
|
+
* @returns Array of genre strings (e.g. "Action", "Adventure", ...)
|
|
912
|
+
*/
|
|
913
|
+
async getGenres() {
|
|
914
|
+
const data = await this.request(QUERY_GENRES);
|
|
915
|
+
return data.GenreCollection;
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Get all available media tags on AniList.
|
|
919
|
+
*
|
|
920
|
+
* @returns Array of tag objects with id, name, description, category, isAdult
|
|
921
|
+
*/
|
|
922
|
+
async getTags() {
|
|
923
|
+
const data = await this.request(QUERY_TAGS);
|
|
924
|
+
return data.MediaTagCollection;
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* Auto-paginating async iterator.
|
|
928
|
+
*
|
|
929
|
+
* Wraps any paginated method and yields individual items across all pages.
|
|
930
|
+
* Stops when `hasNextPage` is `false` or `maxPages` is reached.
|
|
931
|
+
*
|
|
932
|
+
* @param fetchPage - A function that takes a page number and returns a `PagedResult<T>`
|
|
933
|
+
* @param maxPages - Maximum number of pages to fetch (default: Infinity)
|
|
934
|
+
* @returns An async iterable iterator of individual items
|
|
935
|
+
*
|
|
936
|
+
* @example
|
|
937
|
+
* ```ts
|
|
938
|
+
* // Iterate over all search results
|
|
939
|
+
* for await (const anime of client.paginate((page) =>
|
|
940
|
+
* client.searchMedia({ query: "Naruto", page, perPage: 10 })
|
|
941
|
+
* )) {
|
|
942
|
+
* console.log(anime.title.romaji);
|
|
943
|
+
* }
|
|
944
|
+
*
|
|
945
|
+
* // Limit to 3 pages
|
|
946
|
+
* for await (const anime of client.paginate(
|
|
947
|
+
* (page) => client.getTrending(MediaType.ANIME, page, 20),
|
|
948
|
+
* 3,
|
|
949
|
+
* )) {
|
|
950
|
+
* console.log(anime.title.romaji);
|
|
951
|
+
* }
|
|
952
|
+
* ```
|
|
953
|
+
*/
|
|
954
|
+
async *paginate(fetchPage, maxPages = Number.POSITIVE_INFINITY) {
|
|
955
|
+
let page = 1;
|
|
956
|
+
let hasNext = true;
|
|
957
|
+
while (hasNext && page <= maxPages) {
|
|
958
|
+
const result = await fetchPage(page);
|
|
959
|
+
for (const item of result.results) {
|
|
960
|
+
yield item;
|
|
961
|
+
}
|
|
962
|
+
hasNext = result.pageInfo.hasNextPage === true;
|
|
963
|
+
page++;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
// ── Batch queries ──
|
|
967
|
+
/**
|
|
968
|
+
* Fetch multiple media entries in a single API request.
|
|
969
|
+
* Uses GraphQL aliases to batch up to 50 IDs per call.
|
|
970
|
+
*
|
|
971
|
+
* @param ids - Array of AniList media IDs
|
|
972
|
+
* @returns Array of media objects (same order as input IDs)
|
|
973
|
+
*/
|
|
974
|
+
async getMediaBatch(ids) {
|
|
975
|
+
if (ids.length === 0) return [];
|
|
976
|
+
if (ids.length === 1) return [await this.getMedia(ids[0])];
|
|
977
|
+
return this.executeBatch(ids, buildBatchMediaQuery, "m");
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Fetch multiple characters in a single API request.
|
|
981
|
+
*
|
|
982
|
+
* @param ids - Array of AniList character IDs
|
|
983
|
+
* @returns Array of character objects (same order as input IDs)
|
|
984
|
+
*/
|
|
985
|
+
async getCharacterBatch(ids) {
|
|
986
|
+
if (ids.length === 0) return [];
|
|
987
|
+
if (ids.length === 1) return [await this.getCharacter(ids[0])];
|
|
988
|
+
return this.executeBatch(ids, buildBatchCharacterQuery, "c");
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Fetch multiple staff members in a single API request.
|
|
992
|
+
*
|
|
993
|
+
* @param ids - Array of AniList staff IDs
|
|
994
|
+
* @returns Array of staff objects (same order as input IDs)
|
|
995
|
+
*/
|
|
996
|
+
async getStaffBatch(ids) {
|
|
997
|
+
if (ids.length === 0) return [];
|
|
998
|
+
if (ids.length === 1) return [await this.getStaff(ids[0])];
|
|
999
|
+
return this.executeBatch(ids, buildBatchStaffQuery, "s");
|
|
1000
|
+
}
|
|
1001
|
+
/** @internal */
|
|
1002
|
+
async executeBatch(ids, buildQuery, prefix) {
|
|
1003
|
+
const chunks = this.chunk(ids, 50);
|
|
1004
|
+
const results = [];
|
|
1005
|
+
for (const chunk of chunks) {
|
|
1006
|
+
const query = buildQuery(chunk);
|
|
1007
|
+
const data = await this.request(query);
|
|
1008
|
+
results.push(...chunk.map((_, i) => data[`${prefix}${i}`]));
|
|
1009
|
+
}
|
|
1010
|
+
return results;
|
|
1011
|
+
}
|
|
1012
|
+
/** @internal */
|
|
1013
|
+
chunk(arr, size) {
|
|
1014
|
+
const chunks = [];
|
|
1015
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
1016
|
+
chunks.push(arr.slice(i, i + size));
|
|
1017
|
+
}
|
|
1018
|
+
return chunks;
|
|
1019
|
+
}
|
|
1020
|
+
// ── Cache management ──
|
|
552
1021
|
/**
|
|
553
1022
|
* Clear the entire response cache.
|
|
554
1023
|
*/
|
|
555
|
-
clearCache() {
|
|
556
|
-
this.
|
|
1024
|
+
async clearCache() {
|
|
1025
|
+
await this.cacheAdapter.clear();
|
|
557
1026
|
}
|
|
558
1027
|
/**
|
|
559
|
-
* Number of entries currently in the cache.
|
|
1028
|
+
* Number of entries currently in the cache (sync).
|
|
1029
|
+
* For async adapters like Redis, this may be approximate.
|
|
560
1030
|
*/
|
|
561
1031
|
get cacheSize() {
|
|
562
|
-
return this.
|
|
1032
|
+
return this.cacheAdapter.size;
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Remove cache entries whose key matches the given pattern.
|
|
1036
|
+
*
|
|
1037
|
+
* @param pattern — A string (converted to RegExp) or RegExp
|
|
1038
|
+
* @returns Number of entries removed
|
|
1039
|
+
*/
|
|
1040
|
+
async invalidateCache(pattern) {
|
|
1041
|
+
if (this.cacheAdapter.invalidate) {
|
|
1042
|
+
return this.cacheAdapter.invalidate(pattern);
|
|
1043
|
+
}
|
|
1044
|
+
const allKeys = await this.cacheAdapter.keys();
|
|
1045
|
+
const regex = typeof pattern === "string" ? new RegExp(pattern) : pattern;
|
|
1046
|
+
let count = 0;
|
|
1047
|
+
for (const key of allKeys) {
|
|
1048
|
+
if (regex.test(key)) {
|
|
1049
|
+
await this.cacheAdapter.delete(key);
|
|
1050
|
+
count++;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
return count;
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
// src/cache/redis.ts
|
|
1058
|
+
var RedisCache = class {
|
|
1059
|
+
constructor(options) {
|
|
1060
|
+
this.client = options.client;
|
|
1061
|
+
this.prefix = options.prefix ?? "ani:";
|
|
1062
|
+
this.ttl = options.ttl ?? 86400;
|
|
1063
|
+
}
|
|
1064
|
+
prefixedKey(key) {
|
|
1065
|
+
return `${this.prefix}${key}`;
|
|
1066
|
+
}
|
|
1067
|
+
async get(key) {
|
|
1068
|
+
const raw = await this.client.get(this.prefixedKey(key));
|
|
1069
|
+
if (raw === null) return void 0;
|
|
1070
|
+
try {
|
|
1071
|
+
return JSON.parse(raw);
|
|
1072
|
+
} catch {
|
|
1073
|
+
return void 0;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
async set(key, data) {
|
|
1077
|
+
await this.client.set(this.prefixedKey(key), JSON.stringify(data), "EX", this.ttl);
|
|
1078
|
+
}
|
|
1079
|
+
async delete(key) {
|
|
1080
|
+
const count = await this.client.del(this.prefixedKey(key));
|
|
1081
|
+
return count > 0;
|
|
1082
|
+
}
|
|
1083
|
+
async clear() {
|
|
1084
|
+
const keys = await this.client.keys(`${this.prefix}*`);
|
|
1085
|
+
if (keys.length > 0) {
|
|
1086
|
+
await this.client.del(...keys);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Returns -1 because Redis keys can expire silently via TTL.
|
|
1091
|
+
* Use `getSize()` for an accurate count.
|
|
1092
|
+
*/
|
|
1093
|
+
get size() {
|
|
1094
|
+
return -1;
|
|
1095
|
+
}
|
|
1096
|
+
/** Get the actual number of keys with this prefix in Redis. */
|
|
1097
|
+
async getSize() {
|
|
1098
|
+
const keys = await this.client.keys(`${this.prefix}*`);
|
|
1099
|
+
return keys.length;
|
|
1100
|
+
}
|
|
1101
|
+
async keys() {
|
|
1102
|
+
const raw = await this.client.keys(`${this.prefix}*`);
|
|
1103
|
+
return raw.map((k) => k.slice(this.prefix.length));
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Remove all entries whose key matches the given glob pattern.
|
|
1107
|
+
*
|
|
1108
|
+
* @param pattern — A glob pattern (e.g. `"*Media*"`)
|
|
1109
|
+
* @returns Number of entries removed.
|
|
1110
|
+
*/
|
|
1111
|
+
async invalidate(pattern) {
|
|
1112
|
+
const keys = await this.client.keys(`${this.prefix}${pattern}`);
|
|
1113
|
+
if (keys.length === 0) return 0;
|
|
1114
|
+
return this.client.del(...keys);
|
|
563
1115
|
}
|
|
564
1116
|
};
|
|
565
1117
|
|
|
@@ -635,7 +1187,72 @@ var CharacterSort = /* @__PURE__ */ ((CharacterSort2) => {
|
|
|
635
1187
|
CharacterSort2["FAVOURITES"] = "FAVOURITES";
|
|
636
1188
|
return CharacterSort2;
|
|
637
1189
|
})(CharacterSort || {});
|
|
1190
|
+
var MediaRelationType = /* @__PURE__ */ ((MediaRelationType2) => {
|
|
1191
|
+
MediaRelationType2["ADAPTATION"] = "ADAPTATION";
|
|
1192
|
+
MediaRelationType2["PREQUEL"] = "PREQUEL";
|
|
1193
|
+
MediaRelationType2["SEQUEL"] = "SEQUEL";
|
|
1194
|
+
MediaRelationType2["PARENT"] = "PARENT";
|
|
1195
|
+
MediaRelationType2["SIDE_STORY"] = "SIDE_STORY";
|
|
1196
|
+
MediaRelationType2["CHARACTER"] = "CHARACTER";
|
|
1197
|
+
MediaRelationType2["SUMMARY"] = "SUMMARY";
|
|
1198
|
+
MediaRelationType2["ALTERNATIVE"] = "ALTERNATIVE";
|
|
1199
|
+
MediaRelationType2["SPIN_OFF"] = "SPIN_OFF";
|
|
1200
|
+
MediaRelationType2["OTHER"] = "OTHER";
|
|
1201
|
+
MediaRelationType2["SOURCE"] = "SOURCE";
|
|
1202
|
+
MediaRelationType2["COMPILATION"] = "COMPILATION";
|
|
1203
|
+
MediaRelationType2["CONTAINS"] = "CONTAINS";
|
|
1204
|
+
return MediaRelationType2;
|
|
1205
|
+
})(MediaRelationType || {});
|
|
1206
|
+
var RecommendationSort = /* @__PURE__ */ ((RecommendationSort2) => {
|
|
1207
|
+
RecommendationSort2["ID"] = "ID";
|
|
1208
|
+
RecommendationSort2["ID_DESC"] = "ID_DESC";
|
|
1209
|
+
RecommendationSort2["RATING"] = "RATING";
|
|
1210
|
+
RecommendationSort2["RATING_DESC"] = "RATING_DESC";
|
|
1211
|
+
return RecommendationSort2;
|
|
1212
|
+
})(RecommendationSort || {});
|
|
1213
|
+
var MediaListStatus = /* @__PURE__ */ ((MediaListStatus2) => {
|
|
1214
|
+
MediaListStatus2["CURRENT"] = "CURRENT";
|
|
1215
|
+
MediaListStatus2["PLANNING"] = "PLANNING";
|
|
1216
|
+
MediaListStatus2["COMPLETED"] = "COMPLETED";
|
|
1217
|
+
MediaListStatus2["DROPPED"] = "DROPPED";
|
|
1218
|
+
MediaListStatus2["PAUSED"] = "PAUSED";
|
|
1219
|
+
MediaListStatus2["REPEATING"] = "REPEATING";
|
|
1220
|
+
return MediaListStatus2;
|
|
1221
|
+
})(MediaListStatus || {});
|
|
1222
|
+
var MediaListSort = /* @__PURE__ */ ((MediaListSort2) => {
|
|
1223
|
+
MediaListSort2["MEDIA_ID"] = "MEDIA_ID";
|
|
1224
|
+
MediaListSort2["MEDIA_ID_DESC"] = "MEDIA_ID_DESC";
|
|
1225
|
+
MediaListSort2["SCORE"] = "SCORE";
|
|
1226
|
+
MediaListSort2["SCORE_DESC"] = "SCORE_DESC";
|
|
1227
|
+
MediaListSort2["STATUS"] = "STATUS";
|
|
1228
|
+
MediaListSort2["STATUS_DESC"] = "STATUS_DESC";
|
|
1229
|
+
MediaListSort2["PROGRESS"] = "PROGRESS";
|
|
1230
|
+
MediaListSort2["PROGRESS_DESC"] = "PROGRESS_DESC";
|
|
1231
|
+
MediaListSort2["PROGRESS_VOLUMES"] = "PROGRESS_VOLUMES";
|
|
1232
|
+
MediaListSort2["PROGRESS_VOLUMES_DESC"] = "PROGRESS_VOLUMES_DESC";
|
|
1233
|
+
MediaListSort2["REPEAT"] = "REPEAT";
|
|
1234
|
+
MediaListSort2["REPEAT_DESC"] = "REPEAT_DESC";
|
|
1235
|
+
MediaListSort2["PRIORITY"] = "PRIORITY";
|
|
1236
|
+
MediaListSort2["PRIORITY_DESC"] = "PRIORITY_DESC";
|
|
1237
|
+
MediaListSort2["STARTED_ON"] = "STARTED_ON";
|
|
1238
|
+
MediaListSort2["STARTED_ON_DESC"] = "STARTED_ON_DESC";
|
|
1239
|
+
MediaListSort2["FINISHED_ON"] = "FINISHED_ON";
|
|
1240
|
+
MediaListSort2["FINISHED_ON_DESC"] = "FINISHED_ON_DESC";
|
|
1241
|
+
MediaListSort2["ADDED_TIME"] = "ADDED_TIME";
|
|
1242
|
+
MediaListSort2["ADDED_TIME_DESC"] = "ADDED_TIME_DESC";
|
|
1243
|
+
MediaListSort2["UPDATED_TIME"] = "UPDATED_TIME";
|
|
1244
|
+
MediaListSort2["UPDATED_TIME_DESC"] = "UPDATED_TIME_DESC";
|
|
1245
|
+
MediaListSort2["MEDIA_TITLE_ROMAJI"] = "MEDIA_TITLE_ROMAJI";
|
|
1246
|
+
MediaListSort2["MEDIA_TITLE_ROMAJI_DESC"] = "MEDIA_TITLE_ROMAJI_DESC";
|
|
1247
|
+
MediaListSort2["MEDIA_TITLE_ENGLISH"] = "MEDIA_TITLE_ENGLISH";
|
|
1248
|
+
MediaListSort2["MEDIA_TITLE_ENGLISH_DESC"] = "MEDIA_TITLE_ENGLISH_DESC";
|
|
1249
|
+
MediaListSort2["MEDIA_TITLE_NATIVE"] = "MEDIA_TITLE_NATIVE";
|
|
1250
|
+
MediaListSort2["MEDIA_TITLE_NATIVE_DESC"] = "MEDIA_TITLE_NATIVE_DESC";
|
|
1251
|
+
MediaListSort2["MEDIA_POPULARITY"] = "MEDIA_POPULARITY";
|
|
1252
|
+
MediaListSort2["MEDIA_POPULARITY_DESC"] = "MEDIA_POPULARITY_DESC";
|
|
1253
|
+
return MediaListSort2;
|
|
1254
|
+
})(MediaListSort || {});
|
|
638
1255
|
|
|
639
|
-
export { AiringSort, AniListClient, AniListError, CharacterSort, MediaFormat, MediaSeason, MediaSort, MediaStatus, MediaType, MemoryCache, RateLimiter };
|
|
1256
|
+
export { AiringSort, AniListClient, AniListError, CharacterSort, MediaFormat, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaStatus, MediaType, MemoryCache, RateLimiter, RecommendationSort, RedisCache };
|
|
640
1257
|
//# sourceMappingURL=index.mjs.map
|
|
641
1258
|
//# sourceMappingURL=index.mjs.map
|