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