anilink-api-wrapper 2.2.0 → 2.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/dist/AniLink.mjs CHANGED
@@ -1034,6 +1034,10 @@ class BaseOperation {
1034
1034
  stateOwner = {};
1035
1035
  /**
1036
1036
  * The authentication token shared by all operations of an instance.
1037
+ *
1038
+ * Mutable only through {@link BaseOperation.updateAuth} so a provider
1039
+ * wiring seam can swap in refreshed auth material (for example the MAL
1040
+ * automatic token-refresh lifecycle) without rebuilding operations.
1037
1041
  */
1038
1042
  requestAuth;
1039
1043
  /**
@@ -1074,6 +1078,19 @@ class BaseOperation {
1074
1078
  get instanceOptions() {
1075
1079
  return this.resolvedOptions;
1076
1080
  }
1081
+ /**
1082
+ * Swaps the instance authentication material in place.
1083
+ *
1084
+ * @internal This mutator exists for provider wiring seams that refresh
1085
+ * credentials mid-flight (the MAL automatic token-refresh lifecycle swaps
1086
+ * the stored auth on the operation instances before replaying a 401'd
1087
+ * request). It is not part of the public API surface.
1088
+ *
1089
+ * @param auth - The replacement authentication material, or `undefined` to clear it.
1090
+ */
1091
+ updateAuth(auth) {
1092
+ this.requestAuth = auth;
1093
+ }
1077
1094
  /**
1078
1095
  * Dispatches one HTTP call through the shared transport pipeline.
1079
1096
  *
@@ -1404,6 +1421,21 @@ function mergeEntry(byId, entry, listName, isCustomList, isSplitCompletedList) {
1404
1421
  });
1405
1422
  }
1406
1423
 
1424
+ function crossLink(media) {
1425
+ const anilistToMal = /* @__PURE__ */ new Map();
1426
+ const malToAnilist = /* @__PURE__ */ new Map();
1427
+ const unmapped = [];
1428
+ for (const entry of media) {
1429
+ if (typeof entry.idMal !== "number") {
1430
+ unmapped.push(entry);
1431
+ continue;
1432
+ }
1433
+ anilistToMal.set(entry.id, entry.idMal);
1434
+ malToAnilist.set(entry.idMal, entry.id);
1435
+ }
1436
+ return { anilistToMal, malToAnilist, unmapped };
1437
+ }
1438
+
1407
1439
  function resolvePositiveInt(value, fallback) {
1408
1440
  if (value === void 0) return fallback;
1409
1441
  if (!Number.isFinite(value) || value <= 0) return fallback;
@@ -1578,7 +1610,13 @@ async function paginate(fetchPage, itemsKey, options) {
1578
1610
  const items = [];
1579
1611
  const pages = [];
1580
1612
  for (const response of responses) {
1581
- const pageItems = response[itemsKey];
1613
+ const raw = response[itemsKey];
1614
+ if (raw === void 0) {
1615
+ throw new AniLinkValidationError([
1616
+ `paginate: the page response has no "${itemsKey}" key. Check the itemsKey argument.`
1617
+ ]);
1618
+ }
1619
+ const pageItems = Array.isArray(raw) ? raw : [];
1582
1620
  pages.push({ pageInfo: response.pageInfo, items: pageItems });
1583
1621
  items.push(...pageItems);
1584
1622
  safeCallback(options?.onPage, "onPage", {
@@ -1672,7 +1710,13 @@ async function paginateChunks(fetchChunk, itemsKey, options) {
1672
1710
  const items = [];
1673
1711
  const chunks = [];
1674
1712
  for (const response of responses) {
1675
- const chunkItems = response[itemsKey];
1713
+ const raw = response[itemsKey];
1714
+ if (raw === void 0) {
1715
+ throw new AniLinkValidationError([
1716
+ `paginateChunks: the chunk response has no "${itemsKey}" key. Check the itemsKey argument.`
1717
+ ]);
1718
+ }
1719
+ const chunkItems = Array.isArray(raw) ? raw : [];
1676
1720
  chunks.push({ hasNextChunk: response.hasNextChunk, items: chunkItems });
1677
1721
  items.push(...chunkItems);
1678
1722
  safeCallback(options?.onChunk, "onChunk", {
@@ -2425,6 +2469,229 @@ class ActivityReplyQuery extends AniListOperation {
2425
2469
  }
2426
2470
  }
2427
2471
 
2472
+ const parseCache = /* @__PURE__ */ new Map();
2473
+ function parseSelection(body) {
2474
+ const cached = parseCache.get(body);
2475
+ if (cached !== void 0) return cached;
2476
+ const parsed = parseSelectionUncached(body);
2477
+ parseCache.set(body, parsed);
2478
+ return parsed;
2479
+ }
2480
+ function parseSelectionUncached(body) {
2481
+ const roots = [];
2482
+ const stack = [];
2483
+ for (const rawLine of body.split("\n")) {
2484
+ const line = rawLine.trim();
2485
+ if (line === "") continue;
2486
+ const closeCount = (line.match(/\}/g) ?? []).length;
2487
+ if (closeCount > 0 && line.includes("{")) {
2488
+ throw new Error(
2489
+ `composeSelection cannot parse the document line: "${line}". The maximal document must use one field per line, without aliases, comments, or inline fragments.`
2490
+ );
2491
+ }
2492
+ for (let i = 0; i < closeCount; i++) stack.pop();
2493
+ const fieldMatch = /^([A-Za-z_][A-Za-z0-9_]*)\s*(\([^()]*\))?\s*(\{)?$/.exec(line);
2494
+ if (fieldMatch) {
2495
+ const node = {
2496
+ name: fieldMatch[1],
2497
+ args: fieldMatch[2] ?? "",
2498
+ children: []
2499
+ };
2500
+ if (stack.length === 0) roots.push(node);
2501
+ else stack[stack.length - 1].children.push(node);
2502
+ if (fieldMatch[3]) stack.push(node);
2503
+ continue;
2504
+ }
2505
+ if (/^\}+$/.test(line)) continue;
2506
+ throw new Error(
2507
+ `composeSelection cannot parse the document line: "${line}". The maximal document must use one field per line, without aliases, comments, or inline fragments.`
2508
+ );
2509
+ }
2510
+ return roots;
2511
+ }
2512
+ function renderSelection(nodes, indent) {
2513
+ const lines = [];
2514
+ for (const node of nodes) {
2515
+ if (node.children.length === 0) {
2516
+ lines.push(`${indent}${node.name}${node.args}`);
2517
+ } else {
2518
+ lines.push(`${indent}${node.name}${node.args} {`);
2519
+ lines.push(renderSelection(node.children, `${indent} `));
2520
+ lines.push(`${indent}}`);
2521
+ }
2522
+ }
2523
+ return lines.join("\n");
2524
+ }
2525
+ function pruneSelection(nodes, paths, always, prefix) {
2526
+ const byName = /* @__PURE__ */ new Map();
2527
+ for (const node of nodes) byName.set(node.name, node);
2528
+ const unknown = [];
2529
+ const subPaths = /* @__PURE__ */ new Map();
2530
+ const wholeHeads = /* @__PURE__ */ new Set();
2531
+ for (const path of always) {
2532
+ const segments = path.split(".");
2533
+ if (!byName.has(segments[0])) {
2534
+ throw new Error(
2535
+ `composeSelection: invalid always-selected key "${path}" \u2014 not a field of the maximal document. This is a library bug in the operation's alwaysSelected registry entry, not a caller error.`
2536
+ );
2537
+ }
2538
+ }
2539
+ const requested = [...always, ...paths];
2540
+ for (const path of requested) {
2541
+ const segments = path.split(".");
2542
+ const head = segments[0];
2543
+ if (!byName.has(head)) {
2544
+ unknown.push(path);
2545
+ continue;
2546
+ }
2547
+ if (segments.length === 1) {
2548
+ wholeHeads.add(head);
2549
+ } else {
2550
+ const rest = segments.slice(1).join(".");
2551
+ const existing = subPaths.get(head);
2552
+ if (existing) existing.push(rest);
2553
+ else subPaths.set(head, [rest]);
2554
+ }
2555
+ }
2556
+ if (unknown.length > 0) {
2557
+ throw new AniLinkValidationError([
2558
+ `Unknown field(s): ${unknown.join(", ")}. Valid fields are the response type's keys, at any nesting depth.`
2559
+ ]);
2560
+ }
2561
+ const out = [];
2562
+ for (const node of nodes) {
2563
+ const isWhole = wholeHeads.has(node.name);
2564
+ const subs = subPaths.get(node.name);
2565
+ if (isWhole) {
2566
+ out.push(node);
2567
+ continue;
2568
+ }
2569
+ if (subs === void 0) continue;
2570
+ if (node.children.length === 0) {
2571
+ const full = (s) => prefix === "" ? `${node.name}.${s}` : `${prefix}.${node.name}.${s}`;
2572
+ throw new AniLinkValidationError([
2573
+ `Unknown field(s): ${subs.map(full).join(", ")}. "${node.name}" is a scalar and cannot be drilled into.`
2574
+ ]);
2575
+ }
2576
+ out.push({
2577
+ name: node.name,
2578
+ args: node.args,
2579
+ children: pruneSelection(
2580
+ node.children,
2581
+ subs,
2582
+ [],
2583
+ prefix === "" ? node.name : `${prefix}.${node.name}`
2584
+ )
2585
+ });
2586
+ }
2587
+ return out;
2588
+ }
2589
+ function isVariableUsed(usageScope, name) {
2590
+ const token = `$${name}`;
2591
+ let at = usageScope.indexOf(token);
2592
+ while (at !== -1) {
2593
+ const after = usageScope[at + token.length];
2594
+ if (after === void 0 || !/[A-Za-z0-9_]/.test(after)) return true;
2595
+ at = usageScope.indexOf(token, at + token.length);
2596
+ }
2597
+ return false;
2598
+ }
2599
+ function pruneVariableDeclarations(header, document) {
2600
+ const opBrace = header.indexOf("{");
2601
+ const openParen = header.indexOf("(");
2602
+ if (openParen === -1 || opBrace === -1 || openParen > opBrace) return header;
2603
+ let parenDepth = 0;
2604
+ let closeParen = -1;
2605
+ for (let i = openParen; i < header.length; i++) {
2606
+ const char = header[i];
2607
+ if (char === "(") parenDepth++;
2608
+ else if (char === ")") {
2609
+ parenDepth--;
2610
+ if (parenDepth === 0) {
2611
+ closeParen = i;
2612
+ break;
2613
+ }
2614
+ }
2615
+ }
2616
+ if (closeParen === -1) return header;
2617
+ const declarations = header.slice(openParen + 1, closeParen).split(",");
2618
+ const usageScope = document.slice(0, openParen) + document.slice(closeParen + 1);
2619
+ const kept = declarations.filter((declaration) => {
2620
+ const nameMatch = /\$([A-Za-z0-9_]+)/.exec(declaration);
2621
+ if (!nameMatch) return true;
2622
+ return isVariableUsed(usageScope, nameMatch[1]);
2623
+ });
2624
+ if (kept.length === declarations.length) return header;
2625
+ const prefix = header.slice(0, openParen).trimEnd();
2626
+ const suffix = header.slice(closeParen + 1);
2627
+ if (kept.length === 0) {
2628
+ return `${prefix}${suffix}`;
2629
+ }
2630
+ return `${prefix}(${kept.map((d) => d.trim()).join(", ")})${suffix}`;
2631
+ }
2632
+ function composeDocument(maximalDocument, fields, always) {
2633
+ if (fields === void 0) return maximalDocument;
2634
+ if (fields === null) {
2635
+ throw new AniLinkValidationError([
2636
+ "`fields` must be an array of response paths, not `null`. Omit it for the maximal selection."
2637
+ ]);
2638
+ }
2639
+ if (!Array.isArray(fields)) {
2640
+ throw new AniLinkValidationError([
2641
+ "`fields` must be an array of response paths. Omit it for the maximal selection."
2642
+ ]);
2643
+ }
2644
+ const opOpen = maximalDocument.indexOf("{");
2645
+ if (opOpen === -1) {
2646
+ throw new Error("composeSelection cannot locate the operation definition's opening brace.");
2647
+ }
2648
+ const rootOpen = maximalDocument.indexOf("{", opOpen + 1);
2649
+ if (rootOpen === -1) {
2650
+ throw new Error("composeSelection cannot locate the root field's selection.");
2651
+ }
2652
+ let depth = 0;
2653
+ let rootClose = -1;
2654
+ for (let i = rootOpen; i < maximalDocument.length; i++) {
2655
+ const char = maximalDocument[i];
2656
+ if (char === "{") depth++;
2657
+ else if (char === "}") {
2658
+ depth--;
2659
+ if (depth === 0) {
2660
+ rootClose = i;
2661
+ break;
2662
+ }
2663
+ }
2664
+ }
2665
+ if (rootClose === -1) {
2666
+ throw new Error("composeSelection cannot locate the root field's closing brace.");
2667
+ }
2668
+ const tree = parseSelection(maximalDocument.slice(rootOpen + 1, rootClose));
2669
+ const pruned = pruneSelection(tree, fields, always, "");
2670
+ if (pruned.length === 0) {
2671
+ throw new AniLinkValidationError([
2672
+ "`fields` must name at least one field of the response."
2673
+ ]);
2674
+ }
2675
+ const rendered = renderSelection(pruned, " ");
2676
+ const header = maximalDocument.slice(0, rootOpen + 1);
2677
+ const tail = maximalDocument.slice(rootClose);
2678
+ const body = `${rendered}
2679
+ ${tail}`;
2680
+ const composed = `${header}
2681
+ ${body}`;
2682
+ return `${pruneVariableDeclarations(header, composed)}
2683
+ ${body}`;
2684
+ }
2685
+
2686
+ const PAGE_ALWAYS = ["pageInfo"];
2687
+ function splitFieldsOption(options) {
2688
+ if (options === void 0 || options === null) {
2689
+ return { fields: void 0, transportOptions: {} };
2690
+ }
2691
+ const { fields, ...transportOptions } = options;
2692
+ return { fields, transportOptions };
2693
+ }
2694
+
2428
2695
  const ActivityRepliesMappings = {
2429
2696
  page: "number",
2430
2697
  perPage: "number",
@@ -2433,18 +2700,6 @@ const ActivityRepliesMappings = {
2433
2700
  asHtml: "boolean"
2434
2701
  };
2435
2702
  class ActivityRepliesQuery extends AniListOperation {
2436
- /**
2437
- * `activityReplies` is a method that sends a query request to get activity replies.
2438
- *
2439
- * @param variables - Values from {@link ActivityRepliesVariables} for the query.
2440
- * @returns The {@link ActivityRepliesPageResponse} for the requested page, with pagination metadata.
2441
- * @see https://docs.anilist.co/reference/object/activityreply
2442
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
2443
- * @example
2444
- * ```typescript
2445
- * const result = await new ActivityRepliesQuery().activityReplies({ page: 1, perPage: 10 });
2446
- * ```
2447
- */
2448
2703
  async activityReplies(variables, options) {
2449
2704
  const query = `
2450
2705
  query ($page: Int, $perPage: Int, $id: Int, $activityId: Int, $asHtml: Boolean) {
@@ -2462,10 +2717,15 @@ class ActivityRepliesQuery extends AniListOperation {
2462
2717
  }
2463
2718
  }
2464
2719
  `;
2465
- return await this.execute(query, variables, {
2466
- mappings: ActivityRepliesMappings,
2467
- transportOptions: options
2468
- });
2720
+ const { fields, transportOptions } = splitFieldsOption(options);
2721
+ return await this.execute(
2722
+ composeDocument(query, fields, PAGE_ALWAYS),
2723
+ variables,
2724
+ {
2725
+ mappings: ActivityRepliesMappings,
2726
+ transportOptions
2727
+ }
2728
+ );
2469
2729
  }
2470
2730
  }
2471
2731
 
@@ -2851,6 +3111,7 @@ const AiringScheduleSchema = `
2851
3111
  }
2852
3112
  `;
2853
3113
 
3114
+ const AIRING_SCHEDULE_ALWAYS = ["id"];
2854
3115
  const AiringScheduleMappings = {
2855
3116
  id: "number",
2856
3117
  mediaId: "number",
@@ -2874,18 +3135,6 @@ const AiringScheduleMappings = {
2874
3135
  asHtml: "boolean"
2875
3136
  };
2876
3137
  class AiringScheduleQuery extends AniListOperation {
2877
- /**
2878
- * {@link AiringScheduleQuery.airingSchedule} sends a query request to get airing schedules.
2879
- *
2880
- * @param variables - Values from {@link AiringScheduleVariables} for the query.
2881
- * @returns The {@link AiringScheduleResponse} returned by the query.
2882
- * @see https://docs.anilist.co/reference/object/airingschedule
2883
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
2884
- * @example
2885
- * ```typescript
2886
- * const result = await new AiringScheduleQuery().airingSchedule({ mediaId: 1 });
2887
- * ```
2888
- */
2889
3138
  async airingSchedule(variables, options) {
2890
3139
  const query = `
2891
3140
  query ($id: Int, $mediaId: Int, $episode: Int, $airingAt: Int, $notYetAired: Boolean, $id_not: Int, $id_in: [Int], $id_not_in: [Int], $mediaId_not: Int, $mediaId_in: [Int], $mediaId_not_in: [Int], $episode_not: Int, $episode_in: [Int], $episode_not_in: [Int], $episode_greater: Int, $episode_lesser: Int, $airingAt_greater: Int, $airingAt_lesser: Int, $sort: [AiringSort], $asHtml: Boolean) {
@@ -2894,17 +3143,22 @@ class AiringScheduleQuery extends AniListOperation {
2894
3143
  }
2895
3144
  }
2896
3145
  `;
2897
- return await this.execute(query, variables, {
2898
- requirements: [
2899
- {
2900
- kind: "notOnly",
2901
- names: ["asHtml"],
2902
- message: "The AiringSchedule query requires at least one filter variable."
2903
- }
2904
- ],
2905
- mappings: AiringScheduleMappings,
2906
- transportOptions: options
2907
- });
3146
+ const { fields, transportOptions } = splitFieldsOption(options);
3147
+ return await this.execute(
3148
+ composeDocument(query, fields, AIRING_SCHEDULE_ALWAYS),
3149
+ variables,
3150
+ {
3151
+ requirements: [
3152
+ {
3153
+ kind: "notOnly",
3154
+ names: ["asHtml"],
3155
+ message: "The AiringSchedule query requires at least one filter variable."
3156
+ }
3157
+ ],
3158
+ mappings: AiringScheduleMappings,
3159
+ transportOptions
3160
+ }
3161
+ );
2908
3162
  }
2909
3163
  }
2910
3164
 
@@ -2933,18 +3187,6 @@ const AiringSchedulesMappings = {
2933
3187
  asHtml: "boolean"
2934
3188
  };
2935
3189
  class AiringSchedulesQuery extends AniListOperation {
2936
- /**
2937
- * `airingSchedules` is a method that sends a query request to get airing schedules.
2938
- *
2939
- * @param variables - Values from {@link AiringSchedulesVariables} for the query.
2940
- * @returns The {@link AiringSchedulesPageResponse} returned by the query.
2941
- * @see https://docs.anilist.co/reference/object/airingschedule
2942
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
2943
- * @example
2944
- * ```typescript
2945
- * const result = await new AiringSchedulesQuery().airingSchedules({ page: 1, perPage: 10 });
2946
- * ```
2947
- */
2948
3190
  async airingSchedules(variables, options) {
2949
3191
  const query = `
2950
3192
  query ($page: Int, $perPage: Int, $id: Int, $mediaId: Int, $episode: Int, $airingAt: Int, $notYetAired: Boolean, $id_not: Int, $id_in: [Int], $id_not_in: [Int], $mediaId_not: Int, $mediaId_in: [Int], $mediaId_not_in: [Int], $episode_not: Int, $episode_in: [Int], $episode_not_in: [Int], $episode_greater: Int, $episode_lesser: Int, $airingAt_greater: Int, $airingAt_lesser: Int, $sort: [AiringSort], $asHtml: Boolean) {
@@ -2962,10 +3204,15 @@ class AiringSchedulesQuery extends AniListOperation {
2962
3204
  }
2963
3205
  }
2964
3206
  `;
2965
- return await this.execute(query, variables, {
2966
- mappings: AiringSchedulesMappings,
2967
- transportOptions: options
2968
- });
3207
+ const { fields, transportOptions } = splitFieldsOption(options);
3208
+ return await this.execute(
3209
+ composeDocument(query, fields, PAGE_ALWAYS),
3210
+ variables,
3211
+ {
3212
+ mappings: AiringSchedulesMappings,
3213
+ transportOptions
3214
+ }
3215
+ );
2969
3216
  }
2970
3217
  }
2971
3218
 
@@ -3024,6 +3271,7 @@ const CharacterSchema = `
3024
3271
  modNotes
3025
3272
  `;
3026
3273
 
3274
+ const CHARACTER_ALWAYS = ["id"];
3027
3275
  const CharacterMappings = {
3028
3276
  id: "number",
3029
3277
  isBirthday: "boolean",
@@ -3039,18 +3287,6 @@ const CharacterMappings = {
3039
3287
  mediaPerPage: "number"
3040
3288
  };
3041
3289
  class CharacterQuery extends AniListOperation {
3042
- /**
3043
- * {@link CharacterQuery.character} sends a query request to get characters.
3044
- *
3045
- * @param variables - Values from {@link CharacterVariables} for the query.
3046
- * @returns The {@link CharacterResponse} returned by the query.
3047
- * @see https://docs.anilist.co/reference/object/character
3048
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
3049
- * @example
3050
- * ```typescript
3051
- * const result = await new CharacterQuery().character({ id: 1 });
3052
- * ```
3053
- */
3054
3290
  async character(variables, options) {
3055
3291
  const query = `
3056
3292
  query ($id: Int, $isBirthday: Boolean, $search: String, $id_not: Int, $id_in: [Int], $id_not_in: [Int], $sort: [CharacterSort], $asHtml: Boolean, $mediaSort: [MediaSort], $mediaOnList: Boolean, $mediaPage: Int, $mediaPerPage: Int) {
@@ -3059,10 +3295,15 @@ class CharacterQuery extends AniListOperation {
3059
3295
  }
3060
3296
  }
3061
3297
  `;
3062
- return await this.execute(query, variables, {
3063
- mappings: CharacterMappings,
3064
- transportOptions: options
3065
- });
3298
+ const { fields, transportOptions } = splitFieldsOption(options);
3299
+ return await this.execute(
3300
+ composeDocument(query, fields, CHARACTER_ALWAYS),
3301
+ variables,
3302
+ {
3303
+ mappings: CharacterMappings,
3304
+ transportOptions
3305
+ }
3306
+ );
3066
3307
  }
3067
3308
  }
3068
3309
 
@@ -3083,18 +3324,6 @@ const CharactersMappings = {
3083
3324
  mediaPerPage: "number"
3084
3325
  };
3085
3326
  class CharactersQuery extends AniListOperation {
3086
- /**
3087
- * `characters` is a method that sends a query request to get characters.
3088
- *
3089
- * @param variables - Values from {@link CharactersVariables} for the query.
3090
- * @returns The {@link CharactersPageResponse} returned by the query.
3091
- * @see https://docs.anilist.co/reference/object/character
3092
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
3093
- * @example
3094
- * ```typescript
3095
- * const result = await new CharactersQuery().characters({ page: 1, perPage: 10 });
3096
- * ```
3097
- */
3098
3327
  async characters(variables, options) {
3099
3328
  const query = `
3100
3329
  query ($page: Int, $perPage: Int, $id: Int, $isBirthday: Boolean, $search: String, $id_not: Int, $id_in: [Int], $id_not_in: [Int], $sort: [CharacterSort], $asHtml: Boolean, $mediaSort: [MediaSort], $mediaOnList: Boolean, $mediaPage: Int, $mediaPerPage: Int) {
@@ -3112,10 +3341,15 @@ class CharactersQuery extends AniListOperation {
3112
3341
  }
3113
3342
  }
3114
3343
  `;
3115
- return await this.execute(query, variables, {
3116
- mappings: CharactersMappings,
3117
- transportOptions: options
3118
- });
3344
+ const { fields, transportOptions } = splitFieldsOption(options);
3345
+ return await this.execute(
3346
+ composeDocument(query, fields, PAGE_ALWAYS),
3347
+ variables,
3348
+ {
3349
+ mappings: CharactersMappings,
3350
+ transportOptions
3351
+ }
3352
+ );
3119
3353
  }
3120
3354
  }
3121
3355
 
@@ -3620,18 +3854,6 @@ const FollowersMappings = {
3620
3854
  mangaStatSort: UserStatisticSortMappings
3621
3855
  };
3622
3856
  class FollowersQuery extends AniListOperation {
3623
- /**
3624
- * `followers` is a method that sends a query request to get followers.
3625
- *
3626
- * @param variables - Values from {@link FollowersVariables} for the query.
3627
- * @returns The {@link FollowersPageResponse} returned by the query.
3628
- * @see https://docs.anilist.co/reference/object/user
3629
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
3630
- * @example
3631
- * ```typescript
3632
- * const result = await new FollowersQuery().followers({ userId: 1, page: 1, perPage: 10 });
3633
- * ```
3634
- */
3635
3857
  async followers(variables, options) {
3636
3858
  const query = `
3637
3859
  query ($page: Int, $perPage: Int, $userId: Int!, $sort: [UserSort], $asHtml: Boolean, $animeStatLimit: Int, $mangaStatLimit: Int, $animeStatSort: [UserStatisticsSort], $mangaStatSort: [UserStatisticsSort]) {
@@ -3649,17 +3871,22 @@ class FollowersQuery extends AniListOperation {
3649
3871
  }
3650
3872
  }
3651
3873
  `;
3652
- return await this.execute(query, variables, {
3653
- requirements: [
3654
- {
3655
- kind: "all",
3656
- names: ["userId"],
3657
- message: "The Page.followers query requires a userId."
3658
- }
3659
- ],
3660
- mappings: FollowersMappings,
3661
- transportOptions: options
3662
- });
3874
+ const { fields, transportOptions } = splitFieldsOption(options);
3875
+ return await this.execute(
3876
+ composeDocument(query, fields, PAGE_ALWAYS),
3877
+ variables,
3878
+ {
3879
+ requirements: [
3880
+ {
3881
+ kind: "all",
3882
+ names: ["userId"],
3883
+ message: "The Page.followers query requires a userId."
3884
+ }
3885
+ ],
3886
+ mappings: FollowersMappings,
3887
+ transportOptions
3888
+ }
3889
+ );
3663
3890
  }
3664
3891
  }
3665
3892
 
@@ -3719,18 +3946,6 @@ const FollowingsMappings = {
3719
3946
  mangaStatSort: UserStatisticSortMappings
3720
3947
  };
3721
3948
  class FollowingsQuery extends AniListOperation {
3722
- /**
3723
- * `followings` is a method that sends a query request to get followings.
3724
- *
3725
- * @param variables - Values from {@link FollowingsVariables} for the query.
3726
- * @returns The {@link FollowingsPageResponse} returned by the query.
3727
- * @see https://docs.anilist.co/reference/object/user
3728
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
3729
- * @example
3730
- * ```typescript
3731
- * const result = await new FollowingsQuery().followings({ userId: 1, page: 1, perPage: 10 });
3732
- * ```
3733
- */
3734
3949
  async followings(variables, options) {
3735
3950
  const query = `
3736
3951
  query ($page: Int, $perPage: Int, $userId: Int!, $sort: [UserSort], $asHtml: Boolean, $animeStatLimit: Int, $mangaStatLimit: Int, $animeStatSort: [UserStatisticsSort], $mangaStatSort: [UserStatisticsSort]) {
@@ -3748,17 +3963,22 @@ class FollowingsQuery extends AniListOperation {
3748
3963
  }
3749
3964
  }
3750
3965
  `;
3751
- return await this.execute(query, variables, {
3752
- requirements: [
3753
- {
3754
- kind: "all",
3755
- names: ["userId"],
3756
- message: "The Page.following query requires a userId."
3757
- }
3758
- ],
3759
- mappings: FollowingsMappings,
3760
- transportOptions: options
3761
- });
3966
+ const { fields, transportOptions } = splitFieldsOption(options);
3967
+ return await this.execute(
3968
+ composeDocument(query, fields, PAGE_ALWAYS),
3969
+ variables,
3970
+ {
3971
+ requirements: [
3972
+ {
3973
+ kind: "all",
3974
+ names: ["userId"],
3975
+ message: "The Page.following query requires a userId."
3976
+ }
3977
+ ],
3978
+ mappings: FollowingsMappings,
3979
+ transportOptions
3980
+ }
3981
+ );
3762
3982
  }
3763
3983
  }
3764
3984
 
@@ -3791,18 +4011,6 @@ const LikesMappings = {
3791
4011
  perPage: "number"
3792
4012
  };
3793
4013
  class LikesQuery extends AniListOperation {
3794
- /**
3795
- * `likes` is a method that sends a query request to get likes.
3796
- *
3797
- * @param variables - Values from {@link LikesVariables} for the query.
3798
- * @returns The {@link LikesPageResponse} for the requested page, with pagination metadata.
3799
- * @see https://docs.anilist.co/reference/union/likeableunion
3800
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
3801
- * @example
3802
- * ```typescript
3803
- * const result = await new LikesQuery().likes({ likeableId: 1, type: "ACTIVITY" });
3804
- * ```
3805
- */
3806
4014
  async likes(variables, options) {
3807
4015
  const query = `
3808
4016
  query ($likeableId: Int, $type: LikeableType, $page: Int, $perPage: Int) {
@@ -3820,17 +4028,22 @@ class LikesQuery extends AniListOperation {
3820
4028
  }
3821
4029
  }
3822
4030
  `;
3823
- return await this.execute(query, variables, {
3824
- requirements: [
3825
- {
3826
- kind: "all",
3827
- names: ["likeableId", "type"],
3828
- message: "The Page.likes query requires both a likeableId and a type."
3829
- }
3830
- ],
3831
- mappings: LikesMappings,
3832
- transportOptions: options
3833
- });
4031
+ const { fields, transportOptions } = splitFieldsOption(options);
4032
+ return await this.execute(
4033
+ composeDocument(query, fields, PAGE_ALWAYS),
4034
+ variables,
4035
+ {
4036
+ requirements: [
4037
+ {
4038
+ kind: "all",
4039
+ names: ["likeableId", "type"],
4040
+ message: "The Page.likes query requires both a likeableId and a type."
4041
+ }
4042
+ ],
4043
+ mappings: LikesMappings,
4044
+ transportOptions
4045
+ }
4046
+ );
3834
4047
  }
3835
4048
  }
3836
4049
 
@@ -3951,6 +4164,7 @@ const MediaListCollectionQuerySchema = `
3951
4164
  }
3952
4165
  `;
3953
4166
 
4167
+ const MEDIA_LIST_COLLECTION_ALWAYS = ["hasNextChunk"];
3954
4168
  const MediaListCollectionMappings = {
3955
4169
  userId: "number",
3956
4170
  userName: "string",
@@ -3978,55 +4192,29 @@ const MediaListCollectionMappings = {
3978
4192
  asHtml: "boolean"
3979
4193
  };
3980
4194
  class MediaListCollectionQuery extends AniListOperation {
3981
- /**
3982
- * {@link MediaListCollectionQuery.mediaListCollection} sends a query request to get media list collection data.
3983
- *
3984
- * Chunk semantics: AniList returns large user lists in chunks. Set `chunk` (1-based) and
3985
- * `perChunk` (entries per chunk) to fetch a single chunk; the response's `hasNextChunk` flag
3986
- * indicates whether more chunks remain. To retrieve an entire list, advance `chunk` from 1
3987
- * while `hasNextChunk` is `true`. Use the shared `paginateChunks` helper (see `src/apis/graphql/anilist/Paginator.ts`)
3988
- * to walk chunks with a `maxChunks` guard instead of hand-rolling the loop:
3989
- *
3990
- * ```typescript
3991
- * const result = await paginateChunks(
3992
- * (chunk, perChunk) => aniLink.anilist.query.mediaListCollection(
3993
- * { userId: 542244, type: "ANIME", chunk, perChunk }
3994
- * ),
3995
- * "lists",
3996
- * { perChunk: 500, maxChunks: 20 }
3997
- * );
3998
- * ```
3999
- *
4000
- * @param variables - Values from {@link MediaListCollectionVariables} for the query.
4001
- * @returns The {@link MediaListCollectionResponse} from the query request, including `lists` and `hasNextChunk`.
4002
- * @see https://docs.anilist.co/reference/object/medialistcollection
4003
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
4004
- * @example
4005
- * ```typescript
4006
- * const result = await new MediaListCollectionQuery().mediaListCollection({
4007
- * type: "ANIME",
4008
- * userId: 1,
4009
- * });
4010
- * ```
4011
- */
4012
4195
  async mediaListCollection(variables, options) {
4013
4196
  const query = MediaListCollectionQuerySchema;
4014
- return await this.execute(query, variables, {
4015
- requirements: [
4016
- {
4017
- kind: "all",
4018
- names: ["type"],
4019
- message: "The MediaListCollection query requires a type variable."
4020
- },
4021
- {
4022
- kind: "any",
4023
- names: ["userId", "userName"],
4024
- message: "The MediaListCollection query requires a userId or a userName."
4025
- }
4026
- ],
4027
- mappings: MediaListCollectionMappings,
4028
- transportOptions: options
4029
- });
4197
+ const { fields, transportOptions } = splitFieldsOption(options);
4198
+ return await this.execute(
4199
+ composeDocument(query, fields, MEDIA_LIST_COLLECTION_ALWAYS),
4200
+ variables,
4201
+ {
4202
+ requirements: [
4203
+ {
4204
+ kind: "all",
4205
+ names: ["type"],
4206
+ message: "The MediaListCollection query requires a type variable."
4207
+ },
4208
+ {
4209
+ kind: "any",
4210
+ names: ["userId", "userName"],
4211
+ message: "The MediaListCollection query requires a userId or a userName."
4212
+ }
4213
+ ],
4214
+ mappings: MediaListCollectionMappings,
4215
+ transportOptions
4216
+ }
4217
+ );
4030
4218
  }
4031
4219
  }
4032
4220
 
@@ -4058,6 +4246,7 @@ const MediaListSchema = `
4058
4246
  }
4059
4247
  `;
4060
4248
 
4249
+ const MEDIA_LIST_ALWAYS = ["id"];
4061
4250
  const MediaListMappings = {
4062
4251
  id: "number",
4063
4252
  userId: "number",
@@ -4089,18 +4278,6 @@ const MediaListMappings = {
4089
4278
  asHtml: "boolean"
4090
4279
  };
4091
4280
  class MediaListQuery extends AniListOperation {
4092
- /**
4093
- * {@link MediaListQuery.mediaList} sends a query request to get media list data.
4094
- *
4095
- * @param variables - Values from {@link MediaListVariables} for the query.
4096
- * @returns The {@link MediaListResponse} returned by the query.
4097
- * @see https://docs.anilist.co/reference/object/medialist
4098
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
4099
- * @example
4100
- * ```typescript
4101
- * const result = await new MediaListQuery().mediaList({ id: 1 });
4102
- * ```
4103
- */
4104
4281
  async mediaList(variables, options) {
4105
4282
  const query = `
4106
4283
  query ($id: Int, $userId: Int, $userName: String, $type: MediaType, $status: MediaListStatus, $mediaId: Int, $isFollowing: Boolean, $notes: String, $startedAt: FuzzyDateInt, $completedAt: FuzzyDateInt, $compareWithAuthList: Boolean, $userId_in: [Int], $status_in: [MediaListStatus], $status_not_in: [MediaListStatus], $status_not: MediaListStatus, $mediaId_in: [Int], $mediaId_not_in: [Int], $notes_like: String, $startedAt_greater: FuzzyDateInt, $startedAt_lesser: FuzzyDateInt, $startedAt_like: String, $completedAt_greater: FuzzyDateInt, $completedAt_lesser: FuzzyDateInt, $completedAt_like: String, $sort: [MediaListSort], $scoreFormat: ScoreFormat, $asArray: Boolean, $asHtml: Boolean) {
@@ -4109,10 +4286,15 @@ class MediaListQuery extends AniListOperation {
4109
4286
  }
4110
4287
  }
4111
4288
  `;
4112
- return await this.execute(query, variables, {
4113
- mappings: MediaListMappings,
4114
- transportOptions: options
4115
- });
4289
+ const { fields, transportOptions } = splitFieldsOption(options);
4290
+ return await this.execute(
4291
+ composeDocument(query, fields, MEDIA_LIST_ALWAYS),
4292
+ variables,
4293
+ {
4294
+ mappings: MediaListMappings,
4295
+ transportOptions
4296
+ }
4297
+ );
4116
4298
  }
4117
4299
  }
4118
4300
 
@@ -4149,18 +4331,6 @@ const MediaListsMappings = {
4149
4331
  asHtml: "boolean"
4150
4332
  };
4151
4333
  class MediaListsQuery extends AniListOperation {
4152
- /**
4153
- * `mediaLists` is a method that sends a query request to get media lists.
4154
- *
4155
- * @param variables - Values from {@link MediaListsVariables} for the query.
4156
- * @returns The {@link MediaListsPageResponse} returned by the query.
4157
- * @see https://docs.anilist.co/reference/object/medialist
4158
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
4159
- * @example
4160
- * ```typescript
4161
- * const result = await new MediaListsQuery().mediaLists({ userId: 1, page: 1, perPage: 10 });
4162
- * ```
4163
- */
4164
4334
  async mediaLists(variables, options) {
4165
4335
  const query = `
4166
4336
  query ($page: Int, $perPage: Int, $id: Int, $userId: Int, $userName: String, $type: MediaType, $status: MediaListStatus, $mediaId: Int, $isFollowing: Boolean, $notes: String, $startedAt: FuzzyDateInt, $completedAt: FuzzyDateInt, $compareWithAuthList: Boolean, $userId_in: [Int], $status_in: [MediaListStatus], $status_not_in: [MediaListStatus], $status_not: MediaListStatus, $mediaId_in: [Int], $mediaId_not_in: [Int], $notes_like: String, $startedAt_greater: FuzzyDateInt, $startedAt_lesser: FuzzyDateInt, $startedAt_like: String, $completedAt_greater: FuzzyDateInt, $completedAt_lesser: FuzzyDateInt, $completedAt_like: String, $sort: [MediaListSort], $scoreFormat: ScoreFormat, $asArray: Boolean, $asHtml: Boolean) {
@@ -4178,17 +4348,22 @@ class MediaListsQuery extends AniListOperation {
4178
4348
  }
4179
4349
  }
4180
4350
  `;
4181
- return await this.execute(query, variables, {
4182
- requirements: [
4183
- {
4184
- kind: "any",
4185
- names: ["userId", "userName"],
4186
- message: "The Page.mediaList query requires either a userId or a userName."
4187
- }
4188
- ],
4189
- mappings: MediaListsMappings,
4190
- transportOptions: options
4191
- });
4351
+ const { fields, transportOptions } = splitFieldsOption(options);
4352
+ return await this.execute(
4353
+ composeDocument(query, fields, PAGE_ALWAYS),
4354
+ variables,
4355
+ {
4356
+ requirements: [
4357
+ {
4358
+ kind: "any",
4359
+ names: ["userId", "userName"],
4360
+ message: "The Page.mediaList query requires either a userId or a userName."
4361
+ }
4362
+ ],
4363
+ mappings: MediaListsMappings,
4364
+ transportOptions
4365
+ }
4366
+ );
4192
4367
  }
4193
4368
  }
4194
4369
 
@@ -4213,6 +4388,7 @@ const MediaSourceMappings = [
4213
4388
  "PICTURE_BOOK"
4214
4389
  ];
4215
4390
 
4391
+ const MEDIA_ALWAYS = ["id", "idMal"];
4216
4392
  const MediaMappings = {
4217
4393
  id: "number",
4218
4394
  idMal: "number",
@@ -4286,18 +4462,6 @@ const MediaMappings = {
4286
4462
  asHtml: "boolean"
4287
4463
  };
4288
4464
  class MediaQuery extends AniListOperation {
4289
- /**
4290
- * {@link MediaQuery.media} sends a query request to get media data.
4291
- *
4292
- * @param variables - Values from {@link MediaVariables} for the query.
4293
- * @returns The {@link MediaResponse} returned by the query.
4294
- * @see https://docs.anilist.co/reference/object/media
4295
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
4296
- * @example
4297
- * ```typescript
4298
- * const result = await new MediaQuery().media({ id: 1 });
4299
- * ```
4300
- */
4301
4465
  async media(variables, options) {
4302
4466
  const query = `
4303
4467
  query ($id: Int, $idMal: Int, $startDate: FuzzyDateInt, $endDate: FuzzyDateInt, $season: MediaSeason, $seasonYear: Int, $type: MediaType, $format: MediaFormat, $status: MediaStatus, $episodes: Int, $duration: Int, $chapters: Int, $volumes: Int, $isAdult: Boolean, $genre: String, $tag: String, $minimumTagRank: Int, $tagCategory: String, $onList: Boolean, $licensedBy: String, $licensedById: Int, $averageScore: Int, $popularity: Int, $source: MediaSource, $countryOfOrigin: CountryCode, $isLicensed: Boolean, $search: String, $id_not: Int, $id_in: [Int], $id_not_in: [Int], $idMal_not: Int, $idMal_in: [Int], $idMal_not_in: [Int], $startDate_greater: FuzzyDateInt, $startDate_lesser: FuzzyDateInt, $startDate_like: String, $endDate_greater: FuzzyDateInt, $endDate_lesser: FuzzyDateInt, $endDate_like: String, $format_in: [MediaFormat], $format_not: MediaFormat, $format_not_in: [MediaFormat], $status_in: [MediaStatus], $status_not: MediaStatus, $status_not_in: [MediaStatus], $episodes_greater: Int, $episodes_lesser: Int, $duration_greater: Int, $duration_lesser: Int, $chapters_greater: Int, $chapters_lesser: Int, $volumes_greater: Int, $volumes_lesser: Int, $genre_in: [String], $genre_not_in: [String], $tag_in: [String], $tag_not_in: [String], $tagCategory_in: [String], $tagCategory_not_in: [String], $licensedBy_in: [String], $licensedById_in: [Int], $averageScore_not: Int, $averageScore_greater: Int, $averageScore_lesser: Int, $popularity_not: Int, $popularity_greater: Int, $popularity_lesser: Int, $source_in: [MediaSource], $sort: [MediaSort], $asHtml: Boolean) {
@@ -4306,17 +4470,22 @@ class MediaQuery extends AniListOperation {
4306
4470
  }
4307
4471
  }
4308
4472
  `;
4309
- return await this.execute(query, variables, {
4310
- requirements: [
4311
- {
4312
- kind: "notOnly",
4313
- names: ["asHtml"],
4314
- message: "The Media query requires at least one filter variable."
4315
- }
4316
- ],
4317
- mappings: MediaMappings,
4318
- transportOptions: options
4319
- });
4473
+ const { fields, transportOptions } = splitFieldsOption(options);
4474
+ return await this.execute(
4475
+ composeDocument(query, fields, MEDIA_ALWAYS),
4476
+ variables,
4477
+ {
4478
+ requirements: [
4479
+ {
4480
+ kind: "notOnly",
4481
+ names: ["asHtml"],
4482
+ message: "The Media query requires at least one filter variable."
4483
+ }
4484
+ ],
4485
+ mappings: MediaMappings,
4486
+ transportOptions
4487
+ }
4488
+ );
4320
4489
  }
4321
4490
  }
4322
4491
 
@@ -4365,6 +4534,7 @@ const MediaTrendSchema = `
4365
4534
  }
4366
4535
  `;
4367
4536
 
4537
+ const MEDIA_TREND_ALWAYS = [];
4368
4538
  const MediaTrendMappings = {
4369
4539
  mediaId: "number",
4370
4540
  date: "number",
@@ -4394,18 +4564,6 @@ const MediaTrendMappings = {
4394
4564
  asHtml: "boolean"
4395
4565
  };
4396
4566
  class MediaTrendQuery extends AniListOperation {
4397
- /**
4398
- * {@link MediaTrendQuery.mediaTrend} sends a query request to get media trend data.
4399
- *
4400
- * @param variables - Values from {@link MediaTrendVariables} for the query.
4401
- * @returns The {@link MediaTrendResponse} returned by the query.
4402
- * @see https://docs.anilist.co/reference/object/mediatrend
4403
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
4404
- * @example
4405
- * ```typescript
4406
- * const result = await new MediaTrendQuery().mediaTrend({ mediaId: 1 });
4407
- * ```
4408
- */
4409
4567
  async mediaTrend(variables, options) {
4410
4568
  const query = `
4411
4569
  query ($mediaId: Int, $date: Int, $trending: Int, $averageScore: Int, $popularity: Int, $episode: Int, $releasing: Boolean, $mediaId_not: Int, $mediaId_in: [Int], $mediaId_not_in: [Int], $date_greater: Int, $date_lesser: Int, $trending_greater: Int, $trending_lesser: Int, $trending_not: Int, $averageScore_greater: Int, $averageScore_lesser: Int, $averageScore_not: Int, $popularity_greater: Int, $popularity_lesser: Int, $popularity_not: Int, $episode_greater: Int, $episode_lesser: Int, $episode_not: Int, $sort: [MediaTrendSort], $asHtml: Boolean) {
@@ -4414,17 +4572,22 @@ class MediaTrendQuery extends AniListOperation {
4414
4572
  }
4415
4573
  }
4416
4574
  `;
4417
- return await this.execute(query, variables, {
4418
- requirements: [
4419
- {
4420
- kind: "notOnly",
4421
- names: ["asHtml"],
4422
- message: "The MediaTrend query requires at least one filter variable."
4423
- }
4424
- ],
4425
- mappings: MediaTrendMappings,
4426
- transportOptions: options
4427
- });
4575
+ const { fields, transportOptions } = splitFieldsOption(options);
4576
+ return await this.execute(
4577
+ composeDocument(query, fields, MEDIA_TREND_ALWAYS),
4578
+ variables,
4579
+ {
4580
+ requirements: [
4581
+ {
4582
+ kind: "notOnly",
4583
+ names: ["asHtml"],
4584
+ message: "The MediaTrend query requires at least one filter variable."
4585
+ }
4586
+ ],
4587
+ mappings: MediaTrendMappings,
4588
+ transportOptions
4589
+ }
4590
+ );
4428
4591
  }
4429
4592
  }
4430
4593
 
@@ -4459,18 +4622,6 @@ const MediaTrendsMappings = {
4459
4622
  asHtml: "boolean"
4460
4623
  };
4461
4624
  class MediaTrendsQuery extends AniListOperation {
4462
- /**
4463
- * `mediaTrends` is a method that sends a query request to get media trends.
4464
- *
4465
- * @param variables - Values from {@link MediaTrendsVariables} for the query.
4466
- * @returns The {@link MediaTrendsPageResponse} returned by the query.
4467
- * @see https://docs.anilist.co/reference/object/mediatrend
4468
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
4469
- * @example
4470
- * ```typescript
4471
- * const result = await new MediaTrendsQuery().mediaTrends({ mediaId: 1, page: 1, perPage: 10 });
4472
- * ```
4473
- */
4474
4625
  async mediaTrends(variables, options) {
4475
4626
  const query = `
4476
4627
  query ($page: Int, $perPage: Int, $mediaId: Int, $date: Int, $trending: Int, $averageScore: Int, $popularity: Int, $episode: Int, $releasing: Boolean, $mediaId_not: Int, $mediaId_in: [Int], $mediaId_not_in: [Int], $date_greater: Int, $date_lesser: Int, $trending_greater: Int, $trending_lesser: Int, $trending_not: Int, $averageScore_greater: Int, $averageScore_lesser: Int, $averageScore_not: Int, $popularity_greater: Int, $popularity_lesser: Int, $popularity_not: Int, $episode_greater: Int, $episode_lesser: Int, $episode_not: Int, $sort: [MediaTrendSort], $asHtml: Boolean) {
@@ -4488,10 +4639,15 @@ class MediaTrendsQuery extends AniListOperation {
4488
4639
  }
4489
4640
  }
4490
4641
  `;
4491
- return await this.execute(query, variables, {
4492
- mappings: MediaTrendsMappings,
4493
- transportOptions: options
4494
- });
4642
+ const { fields, transportOptions } = splitFieldsOption(options);
4643
+ return await this.execute(
4644
+ composeDocument(query, fields, PAGE_ALWAYS),
4645
+ variables,
4646
+ {
4647
+ mappings: MediaTrendsMappings,
4648
+ transportOptions
4649
+ }
4650
+ );
4495
4651
  }
4496
4652
  }
4497
4653
 
@@ -4570,17 +4726,6 @@ const MediasMappings = {
4570
4726
  asHtml: "boolean"
4571
4727
  };
4572
4728
  class MediasQuery extends AniListOperation {
4573
- /**
4574
- * Returns a {@link MediasPageResponse} object.
4575
- * @param variables - Values from {@link MediasVariables} for the query.
4576
- * @returns The {@link MediasPageResponse} returned by the query.
4577
- * @see https://docs.anilist.co/reference/object/media
4578
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
4579
- * @example
4580
- * ```typescript
4581
- * const result = await new MediasQuery().medias({ search: "Cowboy Bebop", page: 1 });
4582
- * ```
4583
- */
4584
4729
  async medias(variables, options) {
4585
4730
  const query = `
4586
4731
  query ($page: Int, $perPage: Int, $id: Int, $idMal: Int, $startDate: FuzzyDateInt, $endDate: FuzzyDateInt, $season: MediaSeason, $seasonYear: Int, $type: MediaType, $format: MediaFormat, $status: MediaStatus, $episodes: Int, $duration: Int, $chapters: Int, $volumes: Int, $isAdult: Boolean, $genre: String, $tag: String, $minimumTagRank: Int, $tagCategory: String, $onList: Boolean, $licensedBy: String, $licensedById: Int, $averageScore: Int, $popularity: Int, $source: MediaSource, $countryOfOrigin: CountryCode, $isLicensed: Boolean, $search: String, $id_not: Int, $id_in: [Int], $id_not_in: [Int], $idMal_not: Int, $idMal_in: [Int], $idMal_not_in: [Int], $startDate_greater: FuzzyDateInt, $startDate_lesser: FuzzyDateInt, $startDate_like: String, $endDate_greater: FuzzyDateInt, $endDate_lesser: FuzzyDateInt, $endDate_like: String, $format_in: [MediaFormat], $format_not: MediaFormat, $format_not_in: [MediaFormat], $status_in: [MediaStatus], $status_not: MediaStatus, $status_not_in: [MediaStatus], $episodes_greater: Int, $episodes_lesser: Int, $duration_greater: Int, $duration_lesser: Int, $chapters_greater: Int, $chapters_lesser: Int, $volumes_greater: Int, $volumes_lesser: Int, $genre_in: [String], $genre_not_in: [String], $tag_in: [String], $tag_not_in: [String], $tagCategory_in: [String], $tagCategory_not_in: [String], $licensedBy_in: [String], $licensedById_in: [Int], $averageScore_not: Int, $averageScore_greater: Int, $averageScore_lesser: Int, $popularity_not: Int, $popularity_greater: Int, $popularity_lesser: Int, $source_in: [MediaSource], $sort: [MediaSort], $asHtml: Boolean) {
@@ -4598,10 +4743,15 @@ class MediasQuery extends AniListOperation {
4598
4743
  }
4599
4744
  }
4600
4745
  `;
4601
- return await this.execute(query, variables, {
4602
- mappings: MediasMappings,
4603
- transportOptions: options
4604
- });
4746
+ const { fields, transportOptions } = splitFieldsOption(options);
4747
+ return await this.execute(
4748
+ composeDocument(query, fields, PAGE_ALWAYS),
4749
+ variables,
4750
+ {
4751
+ mappings: MediasMappings,
4752
+ transportOptions
4753
+ }
4754
+ );
4605
4755
  }
4606
4756
  }
4607
4757
 
@@ -4874,6 +5024,7 @@ const RecommendationSchema = `
4874
5024
  }
4875
5025
  `;
4876
5026
 
5027
+ const RECOMMENDATION_ALWAYS = ["id"];
4877
5028
  const RecommendationMappings = {
4878
5029
  id: "number",
4879
5030
  mediaId: "number",
@@ -4887,18 +5038,6 @@ const RecommendationMappings = {
4887
5038
  asHtml: "boolean"
4888
5039
  };
4889
5040
  class RecommendationQuery extends AniListOperation {
4890
- /**
4891
- * {@link RecommendationQuery.recommendation} sends a query request to get recommendation data.
4892
- *
4893
- * @param variables - Values from {@link RecommendationVariables} for the query.
4894
- * @returns The {@link RecommendationResponse} returned by the query.
4895
- * @see https://docs.anilist.co/reference/object/recommendation
4896
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
4897
- * @example
4898
- * ```typescript
4899
- * const result = await new RecommendationQuery().recommendation({ mediaId: 1 });
4900
- * ```
4901
- */
4902
5041
  async recommendation(variables, options) {
4903
5042
  const query = `
4904
5043
  query ($id: Int, $mediaId: Int, $mediaRecommendationId: Int, $userId: Int, $rating: Int, $onList: Boolean, $rating_greater: Int, $rating_lesser: Int, $sort: [RecommendationSort], $asHtml: Boolean) {
@@ -4907,17 +5046,22 @@ class RecommendationQuery extends AniListOperation {
4907
5046
  }
4908
5047
  }
4909
5048
  `;
4910
- return await this.execute(query, variables, {
4911
- requirements: [
4912
- {
4913
- kind: "notOnly",
4914
- names: ["asHtml"],
4915
- message: "The Recommendation query requires at least one filter variable."
4916
- }
4917
- ],
4918
- mappings: RecommendationMappings,
4919
- transportOptions: options
4920
- });
5049
+ const { fields, transportOptions } = splitFieldsOption(options);
5050
+ return await this.execute(
5051
+ composeDocument(query, fields, RECOMMENDATION_ALWAYS),
5052
+ variables,
5053
+ {
5054
+ requirements: [
5055
+ {
5056
+ kind: "notOnly",
5057
+ names: ["asHtml"],
5058
+ message: "The Recommendation query requires at least one filter variable."
5059
+ }
5060
+ ],
5061
+ mappings: RecommendationMappings,
5062
+ transportOptions
5063
+ }
5064
+ );
4921
5065
  }
4922
5066
  }
4923
5067
 
@@ -4936,18 +5080,6 @@ const RecommendationsMappings = {
4936
5080
  asHtml: "boolean"
4937
5081
  };
4938
5082
  class RecommendationsQuery extends AniListOperation {
4939
- /**
4940
- * `recommendations` is a method that sends a query request to get recommendations.
4941
- *
4942
- * @param variables - Values from {@link RecommendationsVariables} for the query.
4943
- * @returns The {@link RecommendationsPageResponse} returned by the query.
4944
- * @see https://docs.anilist.co/reference/object/recommendation
4945
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
4946
- * @example
4947
- * ```typescript
4948
- * const result = await new RecommendationsQuery().recommendations({ mediaId: 1, page: 1 });
4949
- * ```
4950
- */
4951
5083
  async recommendations(variables, options) {
4952
5084
  const query = `
4953
5085
  query ($page: Int, $perPage: Int, $id: Int, $mediaId: Int, $mediaRecommendationId: Int, $userId: Int, $rating: Int, $onList: Boolean, $rating_greater: Int, $rating_lesser: Int, $sort: [RecommendationSort], $asHtml: Boolean) {
@@ -4965,10 +5097,15 @@ class RecommendationsQuery extends AniListOperation {
4965
5097
  }
4966
5098
  }
4967
5099
  `;
4968
- return await this.execute(query, variables, {
4969
- mappings: RecommendationsMappings,
4970
- transportOptions: options
4971
- });
5100
+ const { fields, transportOptions } = splitFieldsOption(options);
5101
+ return await this.execute(
5102
+ composeDocument(query, fields, PAGE_ALWAYS),
5103
+ variables,
5104
+ {
5105
+ mappings: RecommendationsMappings,
5106
+ transportOptions
5107
+ }
5108
+ );
4972
5109
  }
4973
5110
  }
4974
5111
 
@@ -4994,6 +5131,7 @@ const ReviewSchema = `
4994
5131
  }
4995
5132
  `;
4996
5133
 
5134
+ const REVIEW_ALWAYS = ["id"];
4997
5135
  const ReviewMappings = {
4998
5136
  id: "number",
4999
5137
  mediaId: "number",
@@ -5003,18 +5141,6 @@ const ReviewMappings = {
5003
5141
  asHtml: "boolean"
5004
5142
  };
5005
5143
  class ReviewQuery extends AniListOperation {
5006
- /**
5007
- * {@link ReviewQuery.review} sends a query request to get review data.
5008
- *
5009
- * @param variables - Values from {@link ReviewVariables} for the query.
5010
- * @returns The {@link ReviewResponse} returned by the query.
5011
- * @see https://docs.anilist.co/reference/object/review
5012
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5013
- * @example
5014
- * ```typescript
5015
- * const result = await new ReviewQuery().review({ mediaId: 1 });
5016
- * ```
5017
- */
5018
5144
  async review(variables, options) {
5019
5145
  const query = `
5020
5146
  query ($id: Int, $mediaId: Int, $userId: Int, $mediaType: MediaType, $sort: [ReviewSort], $asHtml: Boolean) {
@@ -5023,17 +5149,22 @@ class ReviewQuery extends AniListOperation {
5023
5149
  }
5024
5150
  }
5025
5151
  `;
5026
- return await this.execute(query, variables, {
5027
- requirements: [
5028
- {
5029
- kind: "notOnly",
5030
- names: ["asHtml"],
5031
- message: "The Review query requires at least one filter variable."
5032
- }
5033
- ],
5034
- mappings: ReviewMappings,
5035
- transportOptions: options
5036
- });
5152
+ const { fields, transportOptions } = splitFieldsOption(options);
5153
+ return await this.execute(
5154
+ composeDocument(query, fields, REVIEW_ALWAYS),
5155
+ variables,
5156
+ {
5157
+ requirements: [
5158
+ {
5159
+ kind: "notOnly",
5160
+ names: ["asHtml"],
5161
+ message: "The Review query requires at least one filter variable."
5162
+ }
5163
+ ],
5164
+ mappings: ReviewMappings,
5165
+ transportOptions
5166
+ }
5167
+ );
5037
5168
  }
5038
5169
  }
5039
5170
 
@@ -5048,18 +5179,6 @@ const ReviewsMappings = {
5048
5179
  asHtml: "boolean"
5049
5180
  };
5050
5181
  class ReviewsQuery extends AniListOperation {
5051
- /**
5052
- * `reviews` is a method that sends a query request to get reviews.
5053
- *
5054
- * @param variables - Values from {@link ReviewsVariables} for the query.
5055
- * @returns The {@link ReviewsPageResponse} returned by the query.
5056
- * @see https://docs.anilist.co/reference/object/review
5057
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5058
- * @example
5059
- * ```typescript
5060
- * const result = await new ReviewsQuery().reviews({ mediaId: 1, page: 1 });
5061
- * ```
5062
- */
5063
5182
  async reviews(variables, options) {
5064
5183
  const query = `
5065
5184
  query ($page: Int, $perPage: Int, $id: Int, $mediaId: Int, $userId: Int, $mediaType: MediaType, $sort: [ReviewSort], $asHtml: Boolean) {
@@ -5077,10 +5196,15 @@ class ReviewsQuery extends AniListOperation {
5077
5196
  }
5078
5197
  }
5079
5198
  `;
5080
- return await this.execute(query, variables, {
5081
- mappings: ReviewsMappings,
5082
- transportOptions: options
5083
- });
5199
+ const { fields, transportOptions } = splitFieldsOption(options);
5200
+ return await this.execute(
5201
+ composeDocument(query, fields, PAGE_ALWAYS),
5202
+ variables,
5203
+ {
5204
+ mappings: ReviewsMappings,
5205
+ transportOptions
5206
+ }
5207
+ );
5084
5208
  }
5085
5209
  }
5086
5210
 
@@ -5131,6 +5255,7 @@ const SiteStatisticsSchema = `
5131
5255
  }
5132
5256
  `;
5133
5257
 
5258
+ const SITE_STATISTICS_ALWAYS = [];
5134
5259
  const SiteStatisticsMappings = {
5135
5260
  usersSort: SiteTrendSortMappings,
5136
5261
  usersPage: "number",
@@ -5155,19 +5280,7 @@ const SiteStatisticsMappings = {
5155
5280
  reviewsPerPage: "number"
5156
5281
  };
5157
5282
  class SiteStatisticsQuery extends AniListOperation {
5158
- /**
5159
- * {@link SiteStatisticsQuery.siteStatistics} sends a query request to get site statistics data.
5160
- *
5161
- * @param variables - Optional values from {@link SiteStatisticsVariables}; defaults to an empty object.
5162
- * @returns The {@link SiteStatisticsResponse} returned by the query.
5163
- * @see https://docs.anilist.co/reference/object/sitestatistics
5164
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5165
- * @example
5166
- * ```typescript
5167
- * const result = await new SiteStatisticsQuery().siteStatistics({});
5168
- * ```
5169
- */
5170
- async siteStatistics(variables = {}, options) {
5283
+ async siteStatistics(variables = {}, options = {}) {
5171
5284
  const query = `
5172
5285
  query ($usersSort: [SiteTrendSort], $usersPage: Int, $usersPerPage: Int, $animeSort: [SiteTrendSort], $animePage: Int, $animePerPage: Int, $mangaSort: [SiteTrendSort], $mangaPage: Int, $mangaPerPage: Int, $charactersSort: [SiteTrendSort], $charactersPage: Int, $charactersPerPage: Int, $staffSort: [SiteTrendSort], $staffPage: Int, $staffPerPage: Int, $studiosSort: [SiteTrendSort], $studiosPage: Int, $studiosPerPage: Int, $reviewsSort: [SiteTrendSort], $reviewsPage: Int, $reviewsPerPage: Int) {
5173
5286
  SiteStatistics {
@@ -5175,10 +5288,15 @@ class SiteStatisticsQuery extends AniListOperation {
5175
5288
  }
5176
5289
  }
5177
5290
  `;
5178
- return await this.execute(query, variables, {
5179
- mappings: SiteStatisticsMappings,
5180
- transportOptions: options
5181
- });
5291
+ const { fields, transportOptions } = splitFieldsOption(options);
5292
+ return await this.execute(
5293
+ composeDocument(query, fields, SITE_STATISTICS_ALWAYS),
5294
+ variables,
5295
+ {
5296
+ mappings: SiteStatisticsMappings,
5297
+ transportOptions
5298
+ }
5299
+ );
5182
5300
  }
5183
5301
  }
5184
5302
 
@@ -5231,6 +5349,7 @@ const StaffSchema = `
5231
5349
  modNotes
5232
5350
  `;
5233
5351
 
5352
+ const STAFF_ALWAYS = ["id"];
5234
5353
  const StaffMappings = {
5235
5354
  id: "number",
5236
5355
  isBirthday: "boolean",
@@ -5254,18 +5373,6 @@ const StaffMappings = {
5254
5373
  characterMediaPerPage: "number"
5255
5374
  };
5256
5375
  class StaffQuery extends AniListOperation {
5257
- /**
5258
- * {@link StaffQuery.staff} sends a query request to get staff data.
5259
- *
5260
- * @param variables - Values from {@link StaffVariables} for the query.
5261
- * @returns The {@link StaffResponse} returned by the query.
5262
- * @see https://docs.anilist.co/reference/object/staff
5263
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5264
- * @example
5265
- * ```typescript
5266
- * const result = await new StaffQuery().staff({ id: 1 });
5267
- * ```
5268
- */
5269
5376
  async staff(variables, options) {
5270
5377
  const query = `
5271
5378
  query ($id: Int, $isBirthday: Boolean, $search: String, $id_not: Int, $id_in: [Int], $id_not_in: [Int], $sort: [StaffSort], $asHtml: Boolean, $staffMediaSort: [MediaSort], $staffMediaType: MediaType, $staffMediaOnList: Boolean, $staffMediaPage: Int, $staffMediaPerPage: Int, $charactersSort: [CharacterSort], $charactersPage: Int, $charactersPerPage: Int, $characterMediaSort: [MediaSort], $characterMediaOnList: Boolean, $characterMediaPage: Int, $characterMediaPerPage: Int) {
@@ -5274,10 +5381,15 @@ class StaffQuery extends AniListOperation {
5274
5381
  }
5275
5382
  }
5276
5383
  `;
5277
- return await this.execute(query, variables, {
5278
- mappings: StaffMappings,
5279
- transportOptions: options
5280
- });
5384
+ const { fields, transportOptions } = splitFieldsOption(options);
5385
+ return await this.execute(
5386
+ composeDocument(query, fields, STAFF_ALWAYS),
5387
+ variables,
5388
+ {
5389
+ mappings: StaffMappings,
5390
+ transportOptions
5391
+ }
5392
+ );
5281
5393
  }
5282
5394
  }
5283
5395
 
@@ -5306,18 +5418,6 @@ const StaffsMappings = {
5306
5418
  characterMediaPerPage: "number"
5307
5419
  };
5308
5420
  class StaffsQuery extends AniListOperation {
5309
- /**
5310
- * `staffs` is a method that sends a query request to get staffs.
5311
- *
5312
- * @param variables - Values from {@link StaffsVariables} for the query.
5313
- * @returns The {@link StaffsPageResponse} returned by the query.
5314
- * @see https://docs.anilist.co/reference/object/staff
5315
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5316
- * @example
5317
- * ```typescript
5318
- * const result = await new StaffsQuery().staffs({ search: "Hayao Miyazaki", page: 1 });
5319
- * ```
5320
- */
5321
5421
  async staffs(variables, options) {
5322
5422
  const query = `
5323
5423
  query ($page: Int, $perPage: Int, $id: Int, $isBirthday: Boolean, $search: String, $id_not: Int, $id_in: [Int], $id_not_in: [Int], $sort: [StaffSort], $asHtml: Boolean, $staffMediaSort: [MediaSort], $staffMediaType: MediaType, $staffMediaOnList: Boolean, $staffMediaPage: Int, $staffMediaPerPage: Int, $charactersSort: [CharacterSort], $charactersPage: Int, $charactersPerPage: Int, $characterMediaSort: [MediaSort], $characterMediaOnList: Boolean, $characterMediaPage: Int, $characterMediaPerPage: Int) {
@@ -5335,10 +5435,15 @@ class StaffsQuery extends AniListOperation {
5335
5435
  }
5336
5436
  }
5337
5437
  `;
5338
- return await this.execute(query, variables, {
5339
- mappings: StaffsMappings,
5340
- transportOptions: options
5341
- });
5438
+ const { fields, transportOptions } = splitFieldsOption(options);
5439
+ return await this.execute(
5440
+ composeDocument(query, fields, PAGE_ALWAYS),
5441
+ variables,
5442
+ {
5443
+ mappings: StaffsMappings,
5444
+ transportOptions
5445
+ }
5446
+ );
5342
5447
  }
5343
5448
  }
5344
5449
 
@@ -5391,6 +5496,7 @@ const StudioSchema = `
5391
5496
  favourites
5392
5497
  `;
5393
5498
 
5499
+ const STUDIO_ALWAYS = ["id"];
5394
5500
  const StudioMappings = {
5395
5501
  id: "number",
5396
5502
  search: "string",
@@ -5418,18 +5524,6 @@ const StudioMappings = {
5418
5524
  characterMediaPerPage: "number"
5419
5525
  };
5420
5526
  class StudioQuery extends AniListOperation {
5421
- /**
5422
- * {@link StudioQuery.studio} sends a query request to get studio data.
5423
- *
5424
- * @param variables - Values from {@link StudioVariables} for the query.
5425
- * @returns The {@link StudioResponse} returned by the query.
5426
- * @see https://docs.anilist.co/reference/object/studio
5427
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5428
- * @example
5429
- * ```typescript
5430
- * const result = await new StudioQuery().studio({ id: 1 });
5431
- * ```
5432
- */
5433
5527
  async studio(variables, options) {
5434
5528
  const query = `
5435
5529
  query ($id: Int, $search: String, $id_not: Int, $id_in: [Int], $id_not_in: [Int], $sort: [StudioSort], $asHtml: Boolean, $mediaSort: [MediaSort], $mediaIsMain: Boolean, $mediaOnList: Boolean, $mediaPage: Int, $mediaPerPage: Int, $staffMediaSort: [MediaSort], $staffMediaType: MediaType, $staffMediaOnList: Boolean, $staffMediaPage: Int, $staffMediaPerPage: Int, $charactersSort: [CharacterSort], $charactersPage: Int, $charactersPerPage: Int, $characterMediaSort: [MediaSort], $characterMediaOnList: Boolean, $characterMediaPage: Int, $characterMediaPerPage: Int) {
@@ -5438,10 +5532,15 @@ class StudioQuery extends AniListOperation {
5438
5532
  }
5439
5533
  }
5440
5534
  `;
5441
- return await this.execute(query, variables, {
5442
- mappings: StudioMappings,
5443
- transportOptions: options
5444
- });
5535
+ const { fields, transportOptions } = splitFieldsOption(options);
5536
+ return await this.execute(
5537
+ composeDocument(query, fields, STUDIO_ALWAYS),
5538
+ variables,
5539
+ {
5540
+ mappings: StudioMappings,
5541
+ transportOptions
5542
+ }
5543
+ );
5445
5544
  }
5446
5545
  }
5447
5546
 
@@ -5474,18 +5573,6 @@ const StudiosMappings = {
5474
5573
  characterMediaPerPage: "number"
5475
5574
  };
5476
5575
  class StudiosQuery extends AniListOperation {
5477
- /**
5478
- * `studios` is a method that sends a query request to get studios.
5479
- *
5480
- * @param variables - Values from {@link StudiosVariables} for the query.
5481
- * @returns The {@link StudiosPageResponse} returned by the query.
5482
- * @see https://docs.anilist.co/reference/object/studio
5483
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5484
- * @example
5485
- * ```typescript
5486
- * const result = await new StudiosQuery().studios({ search: "Bones", page: 1 });
5487
- * ```
5488
- */
5489
5576
  async studios(variables, options) {
5490
5577
  const query = `
5491
5578
  query ($page: Int, $perPage: Int, $id: Int, $search: String, $id_not: Int, $id_in: [Int], $id_not_in: [Int], $sort: [StudioSort], $asHtml: Boolean, $mediaSort: [MediaSort], $mediaIsMain: Boolean, $mediaOnList: Boolean, $mediaPage: Int, $mediaPerPage: Int, $staffMediaSort: [MediaSort], $staffMediaType: MediaType, $staffMediaOnList: Boolean, $staffMediaPage: Int, $staffMediaPerPage: Int, $charactersSort: [CharacterSort], $charactersPage: Int, $charactersPerPage: Int, $characterMediaSort: [MediaSort], $characterMediaOnList: Boolean, $characterMediaPage: Int, $characterMediaPerPage: Int) {
@@ -5503,10 +5590,15 @@ class StudiosQuery extends AniListOperation {
5503
5590
  }
5504
5591
  }
5505
5592
  `;
5506
- return await this.execute(query, variables, {
5507
- mappings: StudiosMappings,
5508
- transportOptions: options
5509
- });
5593
+ const { fields, transportOptions } = splitFieldsOption(options);
5594
+ return await this.execute(
5595
+ composeDocument(query, fields, PAGE_ALWAYS),
5596
+ variables,
5597
+ {
5598
+ mappings: StudiosMappings,
5599
+ transportOptions
5600
+ }
5601
+ );
5510
5602
  }
5511
5603
  }
5512
5604
 
@@ -5569,6 +5661,7 @@ const ThreadCommentSchema = `
5569
5661
  isLocked
5570
5662
  `;
5571
5663
 
5664
+ const THREAD_COMMENT_ALWAYS = ["id"];
5572
5665
  const ThreadCommentMappings = {
5573
5666
  id: "number",
5574
5667
  threadId: "number",
@@ -5577,18 +5670,6 @@ const ThreadCommentMappings = {
5577
5670
  asHtml: "boolean"
5578
5671
  };
5579
5672
  class ThreadCommentQuery extends AniListOperation {
5580
- /**
5581
- * {@link ThreadCommentQuery.threadComment} sends a query request to get thread comment data.
5582
- *
5583
- * @param variables - Values from {@link ThreadCommentVariables} for the query.
5584
- * @returns The {@link ThreadCommentResponse} returned by the query.
5585
- * @see https://docs.anilist.co/reference/object/threadcomment
5586
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5587
- * @example
5588
- * ```typescript
5589
- * const result = await new ThreadCommentQuery().threadComment({ threadId: 1 });
5590
- * ```
5591
- */
5592
5673
  async threadComment(variables, options) {
5593
5674
  const query = `
5594
5675
  query ($id: Int, $threadId: Int, $userId: Int, $sort: [ThreadCommentSort], $asHtml: Boolean) {
@@ -5597,17 +5678,22 @@ class ThreadCommentQuery extends AniListOperation {
5597
5678
  }
5598
5679
  }
5599
5680
  `;
5600
- return await this.execute(query, variables, {
5601
- requirements: [
5602
- {
5603
- kind: "notOnly",
5604
- names: ["asHtml"],
5605
- message: "The ThreadComment query requires at least one filter variable."
5606
- }
5607
- ],
5608
- mappings: ThreadCommentMappings,
5609
- transportOptions: options
5610
- });
5681
+ const { fields, transportOptions } = splitFieldsOption(options);
5682
+ return await this.execute(
5683
+ composeDocument(query, fields, THREAD_COMMENT_ALWAYS),
5684
+ variables,
5685
+ {
5686
+ requirements: [
5687
+ {
5688
+ kind: "notOnly",
5689
+ names: ["asHtml"],
5690
+ message: "The ThreadComment query requires at least one filter variable."
5691
+ }
5692
+ ],
5693
+ mappings: ThreadCommentMappings,
5694
+ transportOptions
5695
+ }
5696
+ );
5611
5697
  }
5612
5698
  }
5613
5699
 
@@ -5621,18 +5707,6 @@ const ThreadCommentsMappings = {
5621
5707
  asHtml: "boolean"
5622
5708
  };
5623
5709
  class ThreadCommentsQuery extends AniListOperation {
5624
- /**
5625
- * `threadComments` is a method that sends a query request to get thread comments.
5626
- *
5627
- * @param variables - Values from {@link ThreadCommentsVariables} for the query.
5628
- * @returns The {@link ThreadCommentsPageResponse} returned by the query.
5629
- * @see https://docs.anilist.co/reference/object/threadcomment
5630
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5631
- * @example
5632
- * ```typescript
5633
- * const result = await new ThreadCommentsQuery().threadComments({ threadId: 1, page: 1 });
5634
- * ```
5635
- */
5636
5710
  async threadComments(variables, options) {
5637
5711
  const query = `
5638
5712
  query ($page: Int, $perPage: Int, $id: Int, $threadId: Int, $userId: Int, $sort: [ThreadCommentSort], $asHtml: Boolean) {
@@ -5650,20 +5724,26 @@ class ThreadCommentsQuery extends AniListOperation {
5650
5724
  }
5651
5725
  }
5652
5726
  `;
5653
- return await this.execute(query, variables, {
5654
- requirements: [
5655
- {
5656
- kind: "any",
5657
- names: ["threadId", "userId"],
5658
- message: "The Page.threadComments query requires a threadId or a userId."
5659
- }
5660
- ],
5661
- mappings: ThreadCommentsMappings,
5662
- transportOptions: options
5663
- });
5727
+ const { fields, transportOptions } = splitFieldsOption(options);
5728
+ return await this.execute(
5729
+ composeDocument(query, fields, PAGE_ALWAYS),
5730
+ variables,
5731
+ {
5732
+ requirements: [
5733
+ {
5734
+ kind: "any",
5735
+ names: ["threadId", "userId"],
5736
+ message: "The Page.threadComments query requires a threadId or a userId."
5737
+ }
5738
+ ],
5739
+ mappings: ThreadCommentsMappings,
5740
+ transportOptions
5741
+ }
5742
+ );
5664
5743
  }
5665
5744
  }
5666
5745
 
5746
+ const THREAD_ALWAYS = ["id"];
5667
5747
  const ThreadMappings = {
5668
5748
  id: "number",
5669
5749
  userId: "number",
@@ -5677,18 +5757,6 @@ const ThreadMappings = {
5677
5757
  asHtml: "boolean"
5678
5758
  };
5679
5759
  class ThreadQuery extends AniListOperation {
5680
- /**
5681
- * {@link ThreadQuery.thread} sends a query request to get thread data.
5682
- *
5683
- * @param variables - Values from {@link ThreadVariables} for the query.
5684
- * @returns The {@link ThreadResponse} returned by the query.
5685
- * @see https://docs.anilist.co/reference/object/thread
5686
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5687
- * @example
5688
- * ```typescript
5689
- * const result = await new ThreadQuery().thread({ id: 1 });
5690
- * ```
5691
- */
5692
5760
  async thread(variables, options) {
5693
5761
  const query = `
5694
5762
  query ($id: Int, $userId: Int, $replyUserId: Int, $subscribed: Boolean, $categoryId: Int, $mediaCategoryId: Int, $search: String, $id_in: [Int], $sort: [ThreadSort], $asHtml: Boolean) {
@@ -5697,17 +5765,22 @@ class ThreadQuery extends AniListOperation {
5697
5765
  }
5698
5766
  }
5699
5767
  `;
5700
- return await this.execute(query, variables, {
5701
- requirements: [
5702
- {
5703
- kind: "notOnly",
5704
- names: ["asHtml"],
5705
- message: "The Thread query requires at least one filter variable."
5706
- }
5707
- ],
5708
- mappings: ThreadMappings,
5709
- transportOptions: options
5710
- });
5768
+ const { fields, transportOptions } = splitFieldsOption(options);
5769
+ return await this.execute(
5770
+ composeDocument(query, fields, THREAD_ALWAYS),
5771
+ variables,
5772
+ {
5773
+ requirements: [
5774
+ {
5775
+ kind: "notOnly",
5776
+ names: ["asHtml"],
5777
+ message: "The Thread query requires at least one filter variable."
5778
+ }
5779
+ ],
5780
+ mappings: ThreadMappings,
5781
+ transportOptions
5782
+ }
5783
+ );
5711
5784
  }
5712
5785
  }
5713
5786
 
@@ -5726,18 +5799,6 @@ const ThreadsMappings = {
5726
5799
  asHtml: "boolean"
5727
5800
  };
5728
5801
  class ThreadsQuery extends AniListOperation {
5729
- /**
5730
- * `threads` is a method that sends a query request to get threads.
5731
- *
5732
- * @param variables - Values from {@link ThreadsVariables} for the query.
5733
- * @returns The {@link ThreadsPageResponse} returned by the query.
5734
- * @see https://docs.anilist.co/reference/object/thread
5735
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5736
- * @example
5737
- * ```typescript
5738
- * const result = await new ThreadsQuery().threads({ page: 1, perPage: 10 });
5739
- * ```
5740
- */
5741
5802
  async threads(variables, options) {
5742
5803
  const query = `
5743
5804
  query ($page: Int, $perPage: Int, $id: Int, $userId: Int, $replyUserId: Int, $subscribed: Boolean, $categoryId: Int, $mediaCategoryId: Int, $search: String, $id_in: [Int], $sort: [ThreadSort], $asHtml: Boolean) {
@@ -5755,13 +5816,19 @@ class ThreadsQuery extends AniListOperation {
5755
5816
  }
5756
5817
  }
5757
5818
  `;
5758
- return await this.execute(query, variables, {
5759
- mappings: ThreadsMappings,
5760
- transportOptions: options
5761
- });
5819
+ const { fields, transportOptions } = splitFieldsOption(options);
5820
+ return await this.execute(
5821
+ composeDocument(query, fields, PAGE_ALWAYS),
5822
+ variables,
5823
+ {
5824
+ mappings: ThreadsMappings,
5825
+ transportOptions
5826
+ }
5827
+ );
5762
5828
  }
5763
5829
  }
5764
5830
 
5831
+ const USER_ALWAYS = ["id"];
5765
5832
  const UserMappings = {
5766
5833
  id: "number",
5767
5834
  name: "string",
@@ -5775,18 +5842,6 @@ const UserMappings = {
5775
5842
  mangaStatSort: UserStatisticSortMappings
5776
5843
  };
5777
5844
  class UserQuery extends AniListOperation {
5778
- /**
5779
- * {@link UserQuery.user} sends a query request to get user data.
5780
- *
5781
- * @param variables - Values from {@link UserVariables} for the query.
5782
- * @returns The {@link UserResponse} returned by the query.
5783
- * @see https://docs.anilist.co/reference/object/user
5784
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5785
- * @example
5786
- * ```typescript
5787
- * const result = await new UserQuery().user({ id: 1 });
5788
- * ```
5789
- */
5790
5845
  async user(variables, options) {
5791
5846
  const query = `
5792
5847
  query ($id: Int, $name: String, $isModerator: Boolean, $search: String, $sort: [UserSort], $asHtml: Boolean, $animeStatLimit: Int, $mangaStatLimit: Int, $animeStatSort: [UserStatisticsSort], $mangaStatSort: [UserStatisticsSort]) {
@@ -5795,10 +5850,15 @@ class UserQuery extends AniListOperation {
5795
5850
  }
5796
5851
  }
5797
5852
  `;
5798
- return await this.execute(query, variables, {
5799
- mappings: UserMappings,
5800
- transportOptions: options
5801
- });
5853
+ const { fields, transportOptions } = splitFieldsOption(options);
5854
+ return await this.execute(
5855
+ composeDocument(query, fields, USER_ALWAYS),
5856
+ variables,
5857
+ {
5858
+ mappings: UserMappings,
5859
+ transportOptions
5860
+ }
5861
+ );
5802
5862
  }
5803
5863
  }
5804
5864
 
@@ -5817,18 +5877,6 @@ const UsersMappings = {
5817
5877
  mangaStatSort: UserStatisticSortMappings
5818
5878
  };
5819
5879
  class UsersQuery extends AniListOperation {
5820
- /**
5821
- * `users` is a method that sends a query request to get users.
5822
- *
5823
- * @param variables - Values from {@link UsersVariables} for the query.
5824
- * @returns The {@link UsersPageResponse} returned by the query.
5825
- * @see https://docs.anilist.co/reference/object/user
5826
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5827
- * @example
5828
- * ```typescript
5829
- * const result = await new UsersQuery().users({ search: "AniList", page: 1 });
5830
- * ```
5831
- */
5832
5880
  async users(variables, options) {
5833
5881
  const query = `
5834
5882
  query ($page: Int, $perPage: Int, $id: Int, $name: String, $isModerator: Boolean, $search: String, $sort: [UserSort], $asHtml: Boolean, $animeStatLimit: Int, $mangaStatLimit: Int, $animeStatSort: [UserStatisticsSort], $mangaStatSort: [UserStatisticsSort]) {
@@ -5846,10 +5894,15 @@ class UsersQuery extends AniListOperation {
5846
5894
  }
5847
5895
  }
5848
5896
  `;
5849
- return await this.execute(query, variables, {
5850
- mappings: UsersMappings,
5851
- transportOptions: options
5852
- });
5897
+ const { fields, transportOptions } = splitFieldsOption(options);
5898
+ return await this.execute(
5899
+ composeDocument(query, fields, PAGE_ALWAYS),
5900
+ variables,
5901
+ {
5902
+ mappings: UsersMappings,
5903
+ transportOptions
5904
+ }
5905
+ );
5853
5906
  }
5854
5907
  }
5855
5908
 
@@ -5893,24 +5946,6 @@ const DeleteMediaListEntryMappings = {
5893
5946
  id: "number"
5894
5947
  };
5895
5948
  class DeleteMediaListEntryMutation extends AniListOperation {
5896
- /**
5897
- * {@link DeleteMediaListEntryMutation.deleteMediaListEntry} sends a mutation request to delete a media list entry.
5898
- *
5899
- * The response is `{ deleted: boolean }`. A `true` value means the entry was deleted by this
5900
- * call; a `false` value means the entry was not present (already deleted or never existed).
5901
- * The mutation is therefore safe to retry after a partial failure: a `false` result confirms
5902
- * the target is gone rather than reporting an error.
5903
- *
5904
- * @param variables - Values from {@link DeleteMediaListEntryVariables} for the mutation.
5905
- * @returns The {@link DeleteMediaListEntryResponse} returned by the mutation.
5906
- * @throws Throws if no authentication token is configured, `id` is missing or invalid, or the mutation request fails.
5907
- * @see https://docs.anilist.co/reference/object/deleted
5908
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5909
- * @example
5910
- * ```typescript
5911
- * const result = await new DeleteMediaListEntryMutation("your-token").deleteMediaListEntry({ id: 1 });
5912
- * ```
5913
- */
5914
5949
  async deleteMediaListEntry(variables, options) {
5915
5950
  const mutation = `
5916
5951
  mutation ($id: Int) {
@@ -5919,18 +5954,23 @@ class DeleteMediaListEntryMutation extends AniListOperation {
5919
5954
  }
5920
5955
  }
5921
5956
  `;
5922
- return await this.execute(mutation, variables, {
5923
- requirements: [
5924
- {
5925
- kind: "all",
5926
- names: ["id"],
5927
- message: "The DeleteMediaListEntry mutation requires an id variable."
5928
- }
5929
- ],
5930
- mappings: DeleteMediaListEntryMappings,
5931
- requiresAuth: true,
5932
- transportOptions: options
5933
- });
5957
+ const { fields, transportOptions } = splitFieldsOption(options);
5958
+ return await this.execute(
5959
+ composeDocument(mutation, fields, []),
5960
+ variables,
5961
+ {
5962
+ requirements: [
5963
+ {
5964
+ kind: "all",
5965
+ names: ["id"],
5966
+ message: "The DeleteMediaListEntry mutation requires an id variable."
5967
+ }
5968
+ ],
5969
+ mappings: DeleteMediaListEntryMappings,
5970
+ requiresAuth: true,
5971
+ transportOptions
5972
+ }
5973
+ );
5934
5974
  }
5935
5975
  }
5936
5976
 
@@ -5939,24 +5979,6 @@ const DeleteCustomListMappings = {
5939
5979
  type: MediaTypeMappings
5940
5980
  };
5941
5981
  class DeleteCustomListMutation extends AniListOperation {
5942
- /**
5943
- * {@link DeleteCustomListMutation.deleteCustomList} sends a mutation request to delete a custom list.
5944
- *
5945
- * The response is `{ deleted: boolean }`. A `true` value means the custom list was deleted by
5946
- * this call; a `false` value means the list was not present (already deleted or never existed).
5947
- * The mutation is therefore safe to retry after a partial failure: a `false` result confirms
5948
- * the target is gone rather than reporting an error.
5949
- *
5950
- * @param variables - Values from {@link DeleteCustomListVariables} for the mutation.
5951
- * @returns The {@link DeleteResult} returned by the mutation.
5952
- * @throws Throws if no authentication token is configured, `customList` or `type` is missing or invalid, or the mutation request fails.
5953
- * @see https://docs.anilist.co/reference/object/deleted
5954
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5955
- * @example
5956
- * ```typescript
5957
- * const result = await new DeleteCustomListMutation("your-token").deleteCustomList({ customList: "watching", type: "ANIME" });
5958
- * ```
5959
- */
5960
5982
  async deleteCustomList(variables, options) {
5961
5983
  const mutation = `
5962
5984
  mutation ($customList: String, $type: MediaType) {
@@ -5965,7 +5987,8 @@ class DeleteCustomListMutation extends AniListOperation {
5965
5987
  }
5966
5988
  }
5967
5989
  `;
5968
- return await this.execute(mutation, variables, {
5990
+ const { fields, transportOptions } = splitFieldsOption(options);
5991
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
5969
5992
  requirements: [
5970
5993
  {
5971
5994
  kind: "all",
@@ -5975,7 +5998,7 @@ class DeleteCustomListMutation extends AniListOperation {
5975
5998
  ],
5976
5999
  mappings: DeleteCustomListMappings,
5977
6000
  requiresAuth: true,
5978
- transportOptions: options
6001
+ transportOptions
5979
6002
  });
5980
6003
  }
5981
6004
  }
@@ -5987,19 +6010,6 @@ const SaveTextActivityMappings = {
5987
6010
  asHtml: "boolean"
5988
6011
  };
5989
6012
  class SaveTextActivityMutation extends AniListOperation {
5990
- /**
5991
- * {@link SaveTextActivityMutation.saveTextActivity} sends a mutation request to save a text activity.
5992
- *
5993
- * @param variables - Values from {@link SaveTextActivityVariables} for the mutation.
5994
- * @returns The {@link Activity} returned by the mutation.
5995
- * @throws Throws if no authentication token is configured, `id` or `text` is missing or invalid, or the mutation request fails.
5996
- * @see https://docs.anilist.co/reference/union/activityunion
5997
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
5998
- * @example
5999
- * ```typescript
6000
- * const result = await new SaveTextActivityMutation("your-token").saveTextActivity({ id: 1, text: "Hello, world!" });
6001
- * ```
6002
- */
6003
6013
  async saveTextActivity(variables, options) {
6004
6014
  const mutation = `
6005
6015
  mutation ($id: Int, $text: String, $locked: Boolean, $asHtml: Boolean) {
@@ -6008,7 +6018,8 @@ class SaveTextActivityMutation extends AniListOperation {
6008
6018
  }
6009
6019
  }
6010
6020
  `;
6011
- return await this.execute(mutation, variables, {
6021
+ const { fields, transportOptions } = splitFieldsOption(options);
6022
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6012
6023
  requirements: [
6013
6024
  {
6014
6025
  kind: "any",
@@ -6018,7 +6029,7 @@ class SaveTextActivityMutation extends AniListOperation {
6018
6029
  ],
6019
6030
  mappings: SaveTextActivityMappings,
6020
6031
  requiresAuth: true,
6021
- transportOptions: options
6032
+ transportOptions
6022
6033
  });
6023
6034
  }
6024
6035
  }
@@ -6033,19 +6044,6 @@ const SaveMessageActivityMappings = {
6033
6044
  asHtml: "boolean"
6034
6045
  };
6035
6046
  class SaveMessageActivityMutation extends AniListOperation {
6036
- /**
6037
- * {@link SaveMessageActivityMutation.saveMessageActivity} sends a mutation request to save a message activity.
6038
- *
6039
- * @param variables - Values from {@link SaveMessageActivityVariables} for the mutation.
6040
- * @returns The {@link Activity} returned by the mutation.
6041
- * @throws Throws if no authentication token is configured, `id` or `message` is missing or invalid, or the mutation request fails.
6042
- * @see https://docs.anilist.co/reference/union/activityunion
6043
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6044
- * @example
6045
- * ```typescript
6046
- * const result = await new SaveMessageActivityMutation("your-token").saveMessageActivity({ id: 1, message: "Hello, world!" });
6047
- * ```
6048
- */
6049
6047
  async saveMessageActivity(variables, options) {
6050
6048
  const mutation = `
6051
6049
  mutation ($id: Int, $message: String, $recipientId: Int, $private: Boolean, $locked: Boolean, $asMod: Boolean, $asHtml: Boolean) {
@@ -6054,7 +6052,8 @@ class SaveMessageActivityMutation extends AniListOperation {
6054
6052
  }
6055
6053
  }
6056
6054
  `;
6057
- return await this.execute(mutation, variables, {
6055
+ const { fields, transportOptions } = splitFieldsOption(options);
6056
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6058
6057
  requirements: [
6059
6058
  {
6060
6059
  kind: "any",
@@ -6064,7 +6063,7 @@ class SaveMessageActivityMutation extends AniListOperation {
6064
6063
  ],
6065
6064
  mappings: SaveMessageActivityMappings,
6066
6065
  requiresAuth: true,
6067
- transportOptions: options
6066
+ transportOptions
6068
6067
  });
6069
6068
  }
6070
6069
  }
@@ -6075,19 +6074,6 @@ const SaveListActivityMappings = {
6075
6074
  asHtml: "boolean"
6076
6075
  };
6077
6076
  class SaveListActivityMutation extends AniListOperation {
6078
- /**
6079
- * {@link SaveListActivityMutation.saveListActivity} sends a mutation request to save a list activity.
6080
- *
6081
- * @param variables - Values from {@link SaveListActivityVariables} for the mutation.
6082
- * @returns The {@link Activity} returned by the mutation.
6083
- * @throws Throws if no authentication token is configured, `id` is missing or invalid, or the mutation request fails.
6084
- * @see https://docs.anilist.co/reference/union/activityunion
6085
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6086
- * @example
6087
- * ```typescript
6088
- * const result = await new SaveListActivityMutation("your-token").saveListActivity({ id: 1 });
6089
- * ```
6090
- */
6091
6077
  async saveListActivity(variables, options) {
6092
6078
  const mutation = `
6093
6079
  mutation ($id: Int, $locked: Boolean, $asHtml: Boolean) {
@@ -6095,7 +6081,8 @@ class SaveListActivityMutation extends AniListOperation {
6095
6081
  ${ListActivitySchema}
6096
6082
  }
6097
6083
  `;
6098
- return await this.execute(mutation, variables, {
6084
+ const { fields, transportOptions } = splitFieldsOption(options);
6085
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6099
6086
  requirements: [
6100
6087
  {
6101
6088
  kind: "all",
@@ -6105,7 +6092,7 @@ class SaveListActivityMutation extends AniListOperation {
6105
6092
  ],
6106
6093
  mappings: SaveListActivityMappings,
6107
6094
  requiresAuth: true,
6108
- transportOptions: options
6095
+ transportOptions
6109
6096
  });
6110
6097
  }
6111
6098
  }
@@ -6114,24 +6101,6 @@ const DeleteActivityMappings = {
6114
6101
  id: "number"
6115
6102
  };
6116
6103
  class DeleteActivityMutation extends AniListOperation {
6117
- /**
6118
- * {@link DeleteActivityMutation.deleteActivity} sends a mutation request to delete an activity.
6119
- *
6120
- * The response is `{ deleted: boolean }`. A `true` value means the activity was deleted by
6121
- * this call; a `false` value means the activity was not present (already deleted or never
6122
- * existed). The mutation is therefore safe to retry after a partial failure: a `false` result
6123
- * confirms the target is gone rather than reporting an error.
6124
- *
6125
- * @param variables - Values from {@link DeleteActivityVariables} for the mutation.
6126
- * @returns The {@link DeleteResult} returned by the mutation.
6127
- * @throws Throws if no authentication token is configured, `id` is missing or invalid, or the mutation request fails.
6128
- * @see https://docs.anilist.co/reference/object/deleted
6129
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6130
- * @example
6131
- * ```typescript
6132
- * const result = await new DeleteActivityMutation("your-token").deleteActivity({ id: 1 });
6133
- * ```
6134
- */
6135
6104
  async deleteActivity(variables, options) {
6136
6105
  const mutation = `
6137
6106
  mutation ($id: Int) {
@@ -6140,7 +6109,8 @@ class DeleteActivityMutation extends AniListOperation {
6140
6109
  }
6141
6110
  }
6142
6111
  `;
6143
- return await this.execute(mutation, variables, {
6112
+ const { fields, transportOptions } = splitFieldsOption(options);
6113
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6144
6114
  requirements: [
6145
6115
  {
6146
6116
  kind: "all",
@@ -6150,7 +6120,7 @@ class DeleteActivityMutation extends AniListOperation {
6150
6120
  ],
6151
6121
  mappings: DeleteActivityMappings,
6152
6122
  requiresAuth: true,
6153
- transportOptions: options
6123
+ transportOptions
6154
6124
  });
6155
6125
  }
6156
6126
  }
@@ -6247,19 +6217,6 @@ const SaveActivityReplyMappings = {
6247
6217
  asHtml: "boolean"
6248
6218
  };
6249
6219
  class SaveActivityReplyMutation extends AniListOperation {
6250
- /**
6251
- * {@link SaveActivityReplyMutation.saveActivityReply} sends a mutation request to save an activity reply.
6252
- *
6253
- * @param variables - Values from {@link SaveActivityReplyVariables} for the mutation.
6254
- * @returns The {@link ActivityReply} returned by the mutation.
6255
- * @throws Throws if no authentication token is configured, `id` or `text` is missing or invalid, or the mutation request fails.
6256
- * @see https://docs.anilist.co/reference/object/activityreply
6257
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6258
- * @example
6259
- * ```typescript
6260
- * const result = await new SaveActivityReplyMutation("your-token").saveActivityReply({ id: 1, text: "Hello, world!" });
6261
- * ```
6262
- */
6263
6220
  async saveActivityReply(variables, options) {
6264
6221
  const mutation = `
6265
6222
  mutation ($id: Int, $activityId: Int, $text: String, $asMod: Boolean, $asHtml: Boolean) {
@@ -6268,7 +6225,8 @@ class SaveActivityReplyMutation extends AniListOperation {
6268
6225
  }
6269
6226
  }
6270
6227
  `;
6271
- return await this.execute(mutation, variables, {
6228
+ const { fields, transportOptions } = splitFieldsOption(options);
6229
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6272
6230
  requirements: [
6273
6231
  {
6274
6232
  kind: "any",
@@ -6278,7 +6236,7 @@ class SaveActivityReplyMutation extends AniListOperation {
6278
6236
  ],
6279
6237
  mappings: SaveActivityReplyMappings,
6280
6238
  requiresAuth: true,
6281
- transportOptions: options
6239
+ transportOptions
6282
6240
  });
6283
6241
  }
6284
6242
  }
@@ -6287,24 +6245,6 @@ const DeleteActivityReplyMappings = {
6287
6245
  id: "number"
6288
6246
  };
6289
6247
  class DeleteActivityReplyMutation extends AniListOperation {
6290
- /**
6291
- * {@link DeleteActivityReplyMutation.deleteActivityReply} sends a mutation request to delete an activity reply.
6292
- *
6293
- * The response is `{ deleted: boolean }`. A `true` value means the reply was deleted by this
6294
- * call; a `false` value means the reply was not present (already deleted or never existed).
6295
- * The mutation is therefore safe to retry after a partial failure: a `false` result confirms
6296
- * the target is gone rather than reporting an error.
6297
- *
6298
- * @param variables - Values from {@link DeleteActivityReplyVariables} for the mutation.
6299
- * @returns The {@link DeleteResult} returned by the mutation.
6300
- * @throws Throws if no authentication token is configured, `id` is missing or invalid, or the mutation request fails.
6301
- * @see https://docs.anilist.co/reference/object/deleted
6302
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6303
- * @example
6304
- * ```typescript
6305
- * const result = await new DeleteActivityReplyMutation("your-token").deleteActivityReply({ id: 1 });
6306
- * ```
6307
- */
6308
6248
  async deleteActivityReply(variables, options) {
6309
6249
  const mutation = `
6310
6250
  mutation ($id: Int) {
@@ -6313,7 +6253,8 @@ class DeleteActivityReplyMutation extends AniListOperation {
6313
6253
  }
6314
6254
  }
6315
6255
  `;
6316
- return await this.execute(mutation, variables, {
6256
+ const { fields, transportOptions } = splitFieldsOption(options);
6257
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6317
6258
  requirements: [
6318
6259
  {
6319
6260
  kind: "all",
@@ -6323,7 +6264,7 @@ class DeleteActivityReplyMutation extends AniListOperation {
6323
6264
  ],
6324
6265
  mappings: DeleteActivityReplyMappings,
6325
6266
  requiresAuth: true,
6326
- transportOptions: options
6267
+ transportOptions
6327
6268
  });
6328
6269
  }
6329
6270
  }
@@ -6333,20 +6274,6 @@ const ToggleLikeMappings = {
6333
6274
  type: LikeableTypeMappings
6334
6275
  };
6335
6276
  class ToggleLikeMutation extends AniListOperation {
6336
- /**
6337
- * {@link ToggleLikeMutation.toggleLike} sends a mutation request to toggle a like.
6338
- *
6339
- * @deprecated Prefer `ToggleLikeV2Mutation.toggleLikeV2`, which returns the richer `Likeable` union (activity, activity reply, thread, or thread comment) instead of a bare user.
6340
- * @param variables - Values from {@link ToggleLikeVariables} for the mutation.
6341
- * @returns The {@link BasicUser} returned by the mutation.
6342
- * @throws Throws if no authentication token is configured, `id` or `type` is missing or invalid, or the mutation request fails.
6343
- * @see https://docs.anilist.co/reference/object/user
6344
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6345
- * @example
6346
- * ```typescript
6347
- * const result = await new ToggleLikeMutation("your-token").toggleLike({ id: 1, type: "ACTIVITY" });
6348
- * ```
6349
- */
6350
6277
  async toggleLike(variables, options) {
6351
6278
  const mutation = `
6352
6279
  mutation ($id: Int, $type: LikeableType) {
@@ -6355,7 +6282,8 @@ class ToggleLikeMutation extends AniListOperation {
6355
6282
  }
6356
6283
  }
6357
6284
  `;
6358
- return await this.execute(mutation, variables, {
6285
+ const { fields, transportOptions } = splitFieldsOption(options);
6286
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6359
6287
  requirements: [
6360
6288
  {
6361
6289
  kind: "all",
@@ -6365,7 +6293,7 @@ class ToggleLikeMutation extends AniListOperation {
6365
6293
  ],
6366
6294
  mappings: ToggleLikeMappings,
6367
6295
  requiresAuth: true,
6368
- transportOptions: options
6296
+ transportOptions
6369
6297
  });
6370
6298
  }
6371
6299
  }
@@ -6416,19 +6344,6 @@ const ToggleFollowMappings = {
6416
6344
  userId: "number"
6417
6345
  };
6418
6346
  class ToggleFollowMutation extends AniListOperation {
6419
- /**
6420
- * {@link ToggleFollowMutation.toggleFollow} sends a mutation request to toggle a follow.
6421
- *
6422
- * @param variables - Values from {@link ToggleFollowVariables} for the mutation.
6423
- * @returns The {@link UserResponse} returned by the mutation.
6424
- * @throws Throws if no authentication token is configured, `userId` is missing or invalid, or the mutation request fails.
6425
- * @see https://docs.anilist.co/reference/object/user
6426
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6427
- * @example
6428
- * ```typescript
6429
- * const result = await new ToggleFollowMutation("your-token").toggleFollow({ userId: 1 });
6430
- * ```
6431
- */
6432
6347
  async toggleFollow(variables, options) {
6433
6348
  const mutation = `
6434
6349
  mutation ($userId: Int) {
@@ -6437,7 +6352,8 @@ class ToggleFollowMutation extends AniListOperation {
6437
6352
  }
6438
6353
  }
6439
6354
  `;
6440
- return await this.execute(mutation, variables, {
6355
+ const { fields, transportOptions } = splitFieldsOption(options);
6356
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6441
6357
  requirements: [
6442
6358
  {
6443
6359
  kind: "all",
@@ -6447,7 +6363,7 @@ class ToggleFollowMutation extends AniListOperation {
6447
6363
  ],
6448
6364
  mappings: ToggleFollowMappings,
6449
6365
  requiresAuth: true,
6450
- transportOptions: options
6366
+ transportOptions
6451
6367
  });
6452
6368
  }
6453
6369
  }
@@ -6536,19 +6452,6 @@ const ToggleFavouriteMappings = {
6536
6452
  studioId: "number"
6537
6453
  };
6538
6454
  class ToggleFavouriteMutation extends AniListOperation {
6539
- /**
6540
- * {@link ToggleFavouriteMutation.toggleFavourite} sends a mutation request to toggle a favourite.
6541
- *
6542
- * @param variables - Values from {@link ToggleFavouriteVariables} for the mutation.
6543
- * @returns The {@link Favourites} returned by the mutation.
6544
- * @throws Throws if no authentication token is configured, at least one favourite ID is missing or invalid, or the mutation request fails.
6545
- * @see https://docs.anilist.co/reference/object/favourites
6546
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6547
- * @example
6548
- * ```typescript
6549
- * const result = await new ToggleFavouriteMutation("your-token").toggleFavourite({ animeId: 1, mangaId: 1, characterId: 1, staffId: 1, studioId: 1 });
6550
- * ```
6551
- */
6552
6455
  async toggleFavourite(variables, options) {
6553
6456
  const mutation = `
6554
6457
  mutation ($animeId: Int, $mangaId: Int, $characterId: Int, $staffId: Int, $studioId: Int) {
@@ -6557,7 +6460,8 @@ class ToggleFavouriteMutation extends AniListOperation {
6557
6460
  }
6558
6461
  }
6559
6462
  `;
6560
- return await this.execute(mutation, variables, {
6463
+ const { fields, transportOptions } = splitFieldsOption(options);
6464
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6561
6465
  requirements: [
6562
6466
  {
6563
6467
  kind: "any",
@@ -6567,7 +6471,7 @@ class ToggleFavouriteMutation extends AniListOperation {
6567
6471
  ],
6568
6472
  mappings: ToggleFavouriteMappings,
6569
6473
  requiresAuth: true,
6570
- transportOptions: options
6474
+ transportOptions
6571
6475
  });
6572
6476
  }
6573
6477
  }
@@ -6585,19 +6489,6 @@ const UpdateFavouriteOrderMappings = {
6585
6489
  studioOrder: "number[]"
6586
6490
  };
6587
6491
  class UpdateFavouriteOrderMutation extends AniListOperation {
6588
- /**
6589
- * {@link UpdateFavouriteOrderMutation.updateFavouriteOrder} sends a mutation request to update the order of favourites.
6590
- *
6591
- * @param variables - Values from {@link UpdateFavouriteOrderVariables} for the mutation.
6592
- * @returns The {@link Favourites} returned by the mutation.
6593
- * @throws Throws if no authentication token is configured, an order array lacks its corresponding ID array, a variable has an invalid type, or the mutation request fails.
6594
- * @see https://docs.anilist.co/reference/object/favourites
6595
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6596
- * @example
6597
- * ```typescript
6598
- * const result = await new UpdateFavouriteOrderMutation("your-token").updateFavouriteOrder({ animeIds: [1], mangaIds: [], characterIds: [], staffIds: [], studioIds: [], animeOrder: [1], mangaOrder: [], characterOrder: [], staffOrder: [], studioOrder: [] });
6599
- * ```
6600
- */
6601
6492
  async updateFavouriteOrder(variables, options) {
6602
6493
  if (!variables.animeIds && variables.animeOrder || !variables.mangaIds && variables.mangaOrder || !variables.characterIds && variables.characterOrder || !variables.staffIds && variables.staffOrder || !variables.studioIds && variables.studioOrder) {
6603
6494
  throw new AniLinkValidationError([
@@ -6611,10 +6502,11 @@ class UpdateFavouriteOrderMutation extends AniListOperation {
6611
6502
  }
6612
6503
  }
6613
6504
  `;
6614
- return await this.execute(mutation, variables, {
6505
+ const { fields, transportOptions } = splitFieldsOption(options);
6506
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6615
6507
  mappings: UpdateFavouriteOrderMappings,
6616
6508
  requiresAuth: true,
6617
- transportOptions: options
6509
+ transportOptions
6618
6510
  });
6619
6511
  }
6620
6512
  }
@@ -6629,19 +6521,6 @@ const SaveReviewMappings = {
6629
6521
  asHtml: "boolean"
6630
6522
  };
6631
6523
  class SaveReviewMutation extends AniListOperation {
6632
- /**
6633
- * {@link SaveReviewMutation.saveReview} sends a mutation request to save a review.
6634
- *
6635
- * @param variables - Values from {@link SaveReviewVariables} for the mutation.
6636
- * @returns The {@link ReviewResponse} returned by the mutation.
6637
- * @throws Throws if no authentication token is configured, `id` or `mediaId` is missing, a variable has an invalid type, or the mutation request fails.
6638
- * @see https://docs.anilist.co/reference/object/review
6639
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6640
- * @example
6641
- * ```typescript
6642
- * const result = await new SaveReviewMutation("your-token").saveReview({ id: 1, mediaId: 1, body: "Example review", summary: "Example", score: 8, private: false });
6643
- * ```
6644
- */
6645
6524
  async saveReview(variables, options) {
6646
6525
  const mutation = `
6647
6526
  mutation ($id: Int, $mediaId: Int, $body: String, $summary: String, $score: Int, $private: Boolean, $asHtml: Boolean) {
@@ -6650,18 +6529,23 @@ class SaveReviewMutation extends AniListOperation {
6650
6529
  }
6651
6530
  }
6652
6531
  `;
6653
- return await this.execute(mutation, variables, {
6654
- requirements: [
6655
- {
6656
- kind: "any",
6657
- names: ["id", "mediaId"],
6658
- message: "The SaveReview mutation requires an id or a mediaId variable."
6659
- }
6660
- ],
6661
- mappings: SaveReviewMappings,
6662
- requiresAuth: true,
6663
- transportOptions: options
6664
- });
6532
+ const { fields, transportOptions } = splitFieldsOption(options);
6533
+ return await this.execute(
6534
+ composeDocument(mutation, fields, []),
6535
+ variables,
6536
+ {
6537
+ requirements: [
6538
+ {
6539
+ kind: "any",
6540
+ names: ["id", "mediaId"],
6541
+ message: "The SaveReview mutation requires an id or a mediaId variable."
6542
+ }
6543
+ ],
6544
+ mappings: SaveReviewMappings,
6545
+ requiresAuth: true,
6546
+ transportOptions
6547
+ }
6548
+ );
6665
6549
  }
6666
6550
  }
6667
6551
 
@@ -6672,19 +6556,6 @@ const RateReviewMappings = {
6672
6556
  rating: ReviewRatingMappings
6673
6557
  };
6674
6558
  class RateReviewMutation extends AniListOperation {
6675
- /**
6676
- * {@link RateReviewMutation.rateReview} sends a mutation request to rate a review.
6677
- *
6678
- * @param variables - Values from {@link RateReviewVariables} for the mutation.
6679
- * @returns The {@link ReviewResponse} returned by the mutation.
6680
- * @throws Throws if no authentication token is configured, `reviewId` or `rating` is missing or invalid, or the mutation request fails.
6681
- * @see https://docs.anilist.co/reference/object/review
6682
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6683
- * @example
6684
- * ```typescript
6685
- * const result = await new RateReviewMutation("your-token").rateReview({ reviewId: 1, rating: "UP_VOTE" });
6686
- * ```
6687
- */
6688
6559
  async rateReview(variables, options) {
6689
6560
  const mutation = `
6690
6561
  mutation ($reviewId: Int, $rating: ReviewRating) {
@@ -6693,18 +6564,23 @@ class RateReviewMutation extends AniListOperation {
6693
6564
  }
6694
6565
  }
6695
6566
  `;
6696
- return await this.execute(mutation, variables, {
6697
- requirements: [
6698
- {
6699
- kind: "all",
6700
- names: ["reviewId", "rating"],
6701
- message: "The RateReview mutation requires reviewId and rating variables."
6702
- }
6703
- ],
6704
- mappings: RateReviewMappings,
6705
- requiresAuth: true,
6706
- transportOptions: options
6707
- });
6567
+ const { fields, transportOptions } = splitFieldsOption(options);
6568
+ return await this.execute(
6569
+ composeDocument(mutation, fields, []),
6570
+ variables,
6571
+ {
6572
+ requirements: [
6573
+ {
6574
+ kind: "all",
6575
+ names: ["reviewId", "rating"],
6576
+ message: "The RateReview mutation requires reviewId and rating variables."
6577
+ }
6578
+ ],
6579
+ mappings: RateReviewMappings,
6580
+ requiresAuth: true,
6581
+ transportOptions
6582
+ }
6583
+ );
6708
6584
  }
6709
6585
  }
6710
6586
 
@@ -6712,24 +6588,6 @@ const DeleteReviewMappings = {
6712
6588
  id: "number"
6713
6589
  };
6714
6590
  class DeleteReviewMutation extends AniListOperation {
6715
- /**
6716
- * {@link DeleteReviewMutation.deleteReview} sends a mutation request to delete a review.
6717
- *
6718
- * The response is `{ deleted: boolean }`. A `true` value means the review was deleted by this
6719
- * call; a `false` value means the review was not present (already deleted or never existed).
6720
- * The mutation is therefore safe to retry after a partial failure: a `false` result confirms
6721
- * the target is gone rather than reporting an error.
6722
- *
6723
- * @param variables - Values from {@link DeleteReviewVariables} for the mutation.
6724
- * @returns The {@link DeleteResult} returned by the mutation.
6725
- * @throws Throws if no authentication token is configured, `id` is missing or invalid, or the mutation request fails.
6726
- * @see https://docs.anilist.co/reference/object/deleted
6727
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6728
- * @example
6729
- * ```typescript
6730
- * const result = await new DeleteReviewMutation("your-token").deleteReview({ id: 1 });
6731
- * ```
6732
- */
6733
6591
  async deleteReview(variables, options) {
6734
6592
  const mutation = `
6735
6593
  mutation ($id: Int) {
@@ -6738,7 +6596,8 @@ class DeleteReviewMutation extends AniListOperation {
6738
6596
  }
6739
6597
  }
6740
6598
  `;
6741
- return await this.execute(mutation, variables, {
6599
+ const { fields, transportOptions } = splitFieldsOption(options);
6600
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6742
6601
  requirements: [
6743
6602
  {
6744
6603
  kind: "all",
@@ -6748,7 +6607,7 @@ class DeleteReviewMutation extends AniListOperation {
6748
6607
  ],
6749
6608
  mappings: DeleteReviewMappings,
6750
6609
  requiresAuth: true,
6751
- transportOptions: options
6610
+ transportOptions
6752
6611
  });
6753
6612
  }
6754
6613
  }
@@ -6766,19 +6625,6 @@ const SaveRecommendationMappings = {
6766
6625
  asHtml: "boolean"
6767
6626
  };
6768
6627
  class SaveRecommendationMutation extends AniListOperation {
6769
- /**
6770
- * {@link SaveRecommendationMutation.saveRecommendation} sends a mutation request to save a recommendation.
6771
- *
6772
- * @param variables - Values from {@link SaveRecommendationVariables} for the mutation.
6773
- * @returns The {@link RecommendationResponse} returned by the mutation.
6774
- * @throws Throws if no authentication token is configured, `mediaId`, `mediaRecommendationId`, or `rating` is missing or invalid, or the mutation request fails.
6775
- * @see https://docs.anilist.co/reference/object/recommendation
6776
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6777
- * @example
6778
- * ```typescript
6779
- * const result = await new SaveRecommendationMutation("your-token").saveRecommendation({ mediaId: 1, mediaRecommendationId: 2, rating: "RATE_UP" });
6780
- * ```
6781
- */
6782
6628
  async saveRecommendation(variables, options) {
6783
6629
  const mutation = `
6784
6630
  mutation ($mediaId: Int, $mediaRecommendationId: Int, $rating: RecommendationRating, $asHtml: Boolean) {
@@ -6787,18 +6633,23 @@ class SaveRecommendationMutation extends AniListOperation {
6787
6633
  }
6788
6634
  }
6789
6635
  `;
6790
- return await this.execute(mutation, variables, {
6791
- requirements: [
6792
- {
6793
- kind: "all",
6794
- names: ["mediaId", "mediaRecommendationId", "rating"],
6795
- message: "The SaveRecommendation mutation requires mediaId, mediaRecommendationId, and rating variables."
6796
- }
6797
- ],
6798
- mappings: SaveRecommendationMappings,
6799
- requiresAuth: true,
6800
- transportOptions: options
6801
- });
6636
+ const { fields, transportOptions } = splitFieldsOption(options);
6637
+ return await this.execute(
6638
+ composeDocument(mutation, fields, []),
6639
+ variables,
6640
+ {
6641
+ requirements: [
6642
+ {
6643
+ kind: "all",
6644
+ names: ["mediaId", "mediaRecommendationId", "rating"],
6645
+ message: "The SaveRecommendation mutation requires mediaId, mediaRecommendationId, and rating variables."
6646
+ }
6647
+ ],
6648
+ mappings: SaveRecommendationMappings,
6649
+ requiresAuth: true,
6650
+ transportOptions
6651
+ }
6652
+ );
6802
6653
  }
6803
6654
  }
6804
6655
 
@@ -6813,19 +6664,6 @@ const SaveThreadMappings = {
6813
6664
  asHtml: "boolean"
6814
6665
  };
6815
6666
  class SaveThreadMutation extends AniListOperation {
6816
- /**
6817
- * {@link SaveThreadMutation.saveThread} sends a mutation request to save a thread.
6818
- *
6819
- * @param variables - Values from {@link SaveThreadVariables} for the mutation.
6820
- * @returns The {@link ThreadResponse} returned by the mutation.
6821
- * @throws Throws if no authentication token is configured, `id` or `title` is missing, a variable has an invalid type, or the mutation request fails.
6822
- * @see https://docs.anilist.co/reference/object/thread
6823
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6824
- * @example
6825
- * ```typescript
6826
- * const result = await new SaveThreadMutation("your-token").saveThread({ id: 1, title: "Example thread", body: "Hello, world!", categories: [], mediaCategories: [], sticky: false, locked: false, asHtml: true });
6827
- * ```
6828
- */
6829
6667
  async saveThread(variables, options) {
6830
6668
  const mutation = `
6831
6669
  mutation ($id: Int, $title: String, $body: String, $categories: [Int], $mediaCategories: [Int], $sticky: Boolean, $locked: Boolean, $asHtml: Boolean) {
@@ -6834,18 +6672,23 @@ class SaveThreadMutation extends AniListOperation {
6834
6672
  }
6835
6673
  }
6836
6674
  `;
6837
- return await this.execute(mutation, variables, {
6838
- requirements: [
6839
- {
6840
- kind: "any",
6841
- names: ["id", "title"],
6842
- message: "The SaveThread mutation requires an id or a title variable."
6843
- }
6844
- ],
6845
- mappings: SaveThreadMappings,
6846
- requiresAuth: true,
6847
- transportOptions: options
6848
- });
6675
+ const { fields, transportOptions } = splitFieldsOption(options);
6676
+ return await this.execute(
6677
+ composeDocument(mutation, fields, []),
6678
+ variables,
6679
+ {
6680
+ requirements: [
6681
+ {
6682
+ kind: "any",
6683
+ names: ["id", "title"],
6684
+ message: "The SaveThread mutation requires an id or a title variable."
6685
+ }
6686
+ ],
6687
+ mappings: SaveThreadMappings,
6688
+ requiresAuth: true,
6689
+ transportOptions
6690
+ }
6691
+ );
6849
6692
  }
6850
6693
  }
6851
6694
 
@@ -6853,24 +6696,6 @@ const DeleteThreadMappings = {
6853
6696
  id: "number"
6854
6697
  };
6855
6698
  class DeleteThreadMutation extends AniListOperation {
6856
- /**
6857
- * {@link DeleteThreadMutation.deleteThread} sends a mutation request to delete a thread.
6858
- *
6859
- * The response is `{ deleted: boolean }`. A `true` value means the thread was deleted by this
6860
- * call; a `false` value means the thread was not present (already deleted or never existed).
6861
- * The mutation is therefore safe to retry after a partial failure: a `false` result confirms
6862
- * the target is gone rather than reporting an error.
6863
- *
6864
- * @param variables - Values from {@link DeleteThreadVariables} for the mutation.
6865
- * @returns The {@link DeleteResult} returned by the mutation.
6866
- * @throws Throws if no authentication token is configured, `id` is missing or invalid, or the mutation request fails.
6867
- * @see https://docs.anilist.co/reference/object/deleted
6868
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6869
- * @example
6870
- * ```typescript
6871
- * const result = await new DeleteThreadMutation("your-token").deleteThread({ id: 1 });
6872
- * ```
6873
- */
6874
6699
  async deleteThread(variables, options) {
6875
6700
  const mutation = `
6876
6701
  mutation ($id: Int) {
@@ -6879,7 +6704,8 @@ class DeleteThreadMutation extends AniListOperation {
6879
6704
  }
6880
6705
  }
6881
6706
  `;
6882
- return await this.execute(mutation, variables, {
6707
+ const { fields, transportOptions } = splitFieldsOption(options);
6708
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
6883
6709
  requirements: [
6884
6710
  {
6885
6711
  kind: "all",
@@ -6889,7 +6715,7 @@ class DeleteThreadMutation extends AniListOperation {
6889
6715
  ],
6890
6716
  mappings: DeleteThreadMappings,
6891
6717
  requiresAuth: true,
6892
- transportOptions: options
6718
+ transportOptions
6893
6719
  });
6894
6720
  }
6895
6721
  }
@@ -6900,19 +6726,6 @@ const ToggleThreadSubscriptionMappings = {
6900
6726
  asHtml: "boolean"
6901
6727
  };
6902
6728
  class ToggleThreadSubscriptionMutation extends AniListOperation {
6903
- /**
6904
- * {@link ToggleThreadSubscriptionMutation.toggleThreadSubscription} sends a mutation request to subscribe to a thread.
6905
- *
6906
- * @param variables - Values from {@link ToggleThreadSubscriptionVariables} for the mutation.
6907
- * @returns The {@link ThreadResponse} returned by the mutation.
6908
- * @throws Throws if no authentication token is configured, `threadId` or `subscribe` is missing or invalid, or the mutation request fails.
6909
- * @see https://docs.anilist.co/reference/object/thread
6910
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6911
- * @example
6912
- * ```typescript
6913
- * const result = await new ToggleThreadSubscriptionMutation("your-token").toggleThreadSubscription({ threadId: 1, subscribe: true });
6914
- * ```
6915
- */
6916
6729
  async toggleThreadSubscription(variables, options) {
6917
6730
  const mutation = `
6918
6731
  mutation ($threadId: Int, $subscribe: Boolean, $asHtml: Boolean) {
@@ -6921,18 +6734,23 @@ class ToggleThreadSubscriptionMutation extends AniListOperation {
6921
6734
  }
6922
6735
  }
6923
6736
  `;
6924
- return await this.execute(mutation, variables, {
6925
- requirements: [
6926
- {
6927
- kind: "all",
6928
- names: ["threadId", "subscribe"],
6929
- message: "The ToggleThreadSubscription mutation requires threadId and subscribe variables."
6930
- }
6931
- ],
6932
- mappings: ToggleThreadSubscriptionMappings,
6933
- requiresAuth: true,
6934
- transportOptions: options
6935
- });
6737
+ const { fields, transportOptions } = splitFieldsOption(options);
6738
+ return await this.execute(
6739
+ composeDocument(mutation, fields, []),
6740
+ variables,
6741
+ {
6742
+ requirements: [
6743
+ {
6744
+ kind: "all",
6745
+ names: ["threadId", "subscribe"],
6746
+ message: "The ToggleThreadSubscription mutation requires threadId and subscribe variables."
6747
+ }
6748
+ ],
6749
+ mappings: ToggleThreadSubscriptionMappings,
6750
+ requiresAuth: true,
6751
+ transportOptions
6752
+ }
6753
+ );
6936
6754
  }
6937
6755
  }
6938
6756
 
@@ -6945,19 +6763,6 @@ const SaveThreadCommentMappings = {
6945
6763
  asHtml: "boolean"
6946
6764
  };
6947
6765
  class SaveThreadCommentMutation extends AniListOperation {
6948
- /**
6949
- * {@link SaveThreadCommentMutation.saveThreadComment} sends a mutation request to save a thread comment.
6950
- *
6951
- * @param variables - Values from {@link SaveThreadCommentVariables} for the mutation.
6952
- * @returns The {@link ThreadCommentResponse} returned by the mutation.
6953
- * @throws Throws if no authentication token is configured, `id` or `threadId` is missing, a variable has an invalid type, or the mutation request fails.
6954
- * @see https://docs.anilist.co/reference/object/threadcomment
6955
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
6956
- * @example
6957
- * ```typescript
6958
- * const result = await new SaveThreadCommentMutation("your-token").saveThreadComment({ id: 1, threadId: 1, parentCommentId: 0, comment: "Hello, world!", locked: false, asHtml: true });
6959
- * ```
6960
- */
6961
6766
  async saveThreadComment(variables, options) {
6962
6767
  const mutation = `
6963
6768
  mutation ($id: Int, $threadId: Int, $parentCommentId: Int, $comment: String, $locked: Boolean, $asHtml: Boolean) {
@@ -6966,18 +6771,23 @@ class SaveThreadCommentMutation extends AniListOperation {
6966
6771
  }
6967
6772
  }
6968
6773
  `;
6969
- return await this.execute(mutation, variables, {
6970
- requirements: [
6971
- {
6972
- kind: "any",
6973
- names: ["id", "threadId"],
6974
- message: "The SaveThreadComment mutation requires an id or a threadId variable."
6975
- }
6976
- ],
6977
- mappings: SaveThreadCommentMappings,
6978
- requiresAuth: true,
6979
- transportOptions: options
6980
- });
6774
+ const { fields, transportOptions } = splitFieldsOption(options);
6775
+ return await this.execute(
6776
+ composeDocument(mutation, fields, []),
6777
+ variables,
6778
+ {
6779
+ requirements: [
6780
+ {
6781
+ kind: "any",
6782
+ names: ["id", "threadId"],
6783
+ message: "The SaveThreadComment mutation requires an id or a threadId variable."
6784
+ }
6785
+ ],
6786
+ mappings: SaveThreadCommentMappings,
6787
+ requiresAuth: true,
6788
+ transportOptions
6789
+ }
6790
+ );
6981
6791
  }
6982
6792
  }
6983
6793
 
@@ -6985,24 +6795,6 @@ const DeleteThreadCommentMappings = {
6985
6795
  id: "number"
6986
6796
  };
6987
6797
  class DeleteThreadCommentMutation extends AniListOperation {
6988
- /**
6989
- * {@link DeleteThreadCommentMutation.deleteThreadComment} sends a mutation request to delete a thread comment.
6990
- *
6991
- * The response is `{ deleted: boolean }`. A `true` value means the comment was deleted by this
6992
- * call; a `false` value means the comment was not present (already deleted or never existed).
6993
- * The mutation is therefore safe to retry after a partial failure: a `false` result confirms
6994
- * the target is gone rather than reporting an error.
6995
- *
6996
- * @param variables - Values from {@link DeleteThreadCommentVariables} for the mutation.
6997
- * @returns The {@link DeleteResult} returned by the mutation.
6998
- * @throws Throws if no authentication token is configured, `id` is missing or invalid, or the mutation request fails.
6999
- * @see https://docs.anilist.co/reference/object/deleted
7000
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
7001
- * @example
7002
- * ```typescript
7003
- * const result = await new DeleteThreadCommentMutation("your-token").deleteThreadComment({ id: 1 });
7004
- * ```
7005
- */
7006
6798
  async deleteThreadComment(variables, options) {
7007
6799
  const mutation = `
7008
6800
  mutation ($id: Int) {
@@ -7011,7 +6803,8 @@ class DeleteThreadCommentMutation extends AniListOperation {
7011
6803
  }
7012
6804
  }
7013
6805
  `;
7014
- return await this.execute(mutation, variables, {
6806
+ const { fields, transportOptions } = splitFieldsOption(options);
6807
+ return await this.execute(composeDocument(mutation, fields, []), variables, {
7015
6808
  requirements: [
7016
6809
  {
7017
6810
  kind: "all",
@@ -7021,7 +6814,7 @@ class DeleteThreadCommentMutation extends AniListOperation {
7021
6814
  ],
7022
6815
  mappings: DeleteThreadCommentMappings,
7023
6816
  requiresAuth: true,
7024
- transportOptions: options
6817
+ transportOptions
7025
6818
  });
7026
6819
  }
7027
6820
  }
@@ -7118,19 +6911,6 @@ const UpdateMediaListEntriesMappings = {
7118
6911
  ids: "number[]"
7119
6912
  };
7120
6913
  class UpdateMediaListEntriesMutation extends AniListOperation {
7121
- /**
7122
- * {@link UpdateMediaListEntriesMutation.updateMediaListEntries} sends a mutation request to update media list entries.
7123
- *
7124
- * @param variables - Values from {@link UpdateMediaListEntriesVariables} for the mutation.
7125
- * @returns The updated {@link MediaListResponse} entries returned by the mutation.
7126
- * @throws Throws if no authentication token is configured, `ids` is missing or invalid, or the mutation request fails.
7127
- * @see https://docs.anilist.co/reference/object/medialist
7128
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
7129
- * @example
7130
- * ```typescript
7131
- * const result = await new UpdateMediaListEntriesMutation("your-token").updateMediaListEntries({ ids: [1], status: "CURRENT", progress: 1 });
7132
- * ```
7133
- */
7134
6914
  async updateMediaListEntries(variables, options) {
7135
6915
  const mutation = `
7136
6916
  mutation ($status: MediaListStatus, $score: Float, $scoreRaw: Int, $progress: Int, $progressVolumes: Int, $repeat: Int, $priority: Int, $private: Boolean, $notes: String, $hiddenFromStatusLists: Boolean, $advancedScores: [Float], $startedAt: FuzzyDateInput, $completedAt: FuzzyDateInput, $ids: [Int]) {
@@ -7155,18 +6935,23 @@ class UpdateMediaListEntriesMutation extends AniListOperation {
7155
6935
  }
7156
6936
  }
7157
6937
  `;
7158
- return await this.execute(mutation, variables, {
7159
- requirements: [
7160
- {
7161
- kind: "all",
7162
- names: ["ids"],
7163
- message: "The UpdateMediaListEntries mutation requires an ids variable."
7164
- }
7165
- ],
7166
- mappings: UpdateMediaListEntriesMappings,
7167
- requiresAuth: true,
7168
- transportOptions: options
7169
- });
6938
+ const { fields, transportOptions } = splitFieldsOption(options);
6939
+ return await this.execute(
6940
+ composeDocument(mutation, fields, []),
6941
+ variables,
6942
+ {
6943
+ requirements: [
6944
+ {
6945
+ kind: "all",
6946
+ names: ["ids"],
6947
+ message: "The UpdateMediaListEntries mutation requires an ids variable."
6948
+ }
6949
+ ],
6950
+ mappings: UpdateMediaListEntriesMappings,
6951
+ requiresAuth: true,
6952
+ transportOptions
6953
+ }
6954
+ );
7170
6955
  }
7171
6956
  }
7172
6957
 
@@ -7244,19 +7029,6 @@ const UpdateUserMappings = {
7244
7029
  disabledListActivity: DisabledListActivityMapping
7245
7030
  };
7246
7031
  class UpdateUserMutation extends AniListOperation {
7247
- /**
7248
- * {@link UpdateUserMutation.updateUser} sends a mutation request to update a user.
7249
- *
7250
- * @param variables - Values from {@link UpdateUserVariables} for the mutation.
7251
- * @returns The {@link UpdateUserResponse} returned by the mutation.
7252
- * @throws Throws if no authentication token is configured, a variable has an invalid type, or the mutation request fails.
7253
- * @see https://docs.anilist.co/reference/object/user
7254
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
7255
- * @example
7256
- * ```typescript
7257
- * const result = await new UpdateUserMutation("your-token").updateUser({ about: "Updated profile" });
7258
- * ```
7259
- */
7260
7032
  async updateUser(variables, options) {
7261
7033
  const mutation = `
7262
7034
  mutation ($about: String, $titleLanguage: UserTitleLanguage, $displayAdultContent: Boolean, $airingNotifications: Boolean, $scoreFormat: ScoreFormat, $rowOrder: String, $profileColor: String, $donatorBadge: String, $notificationOptions: [NotificationOptionInput], $timezone: String, $activityMergeTime: Int, $animeListOptions: MediaListOptionsInput, $mangaListOptions: MediaListOptionsInput, $staffNameLanguage: UserStaffNameLanguage, $restrictMessagesToFollowing: Boolean, $disabledListActivity: [ListActivityOptionInput]) {
@@ -7313,11 +7085,16 @@ class UpdateUserMutation extends AniListOperation {
7313
7085
  }
7314
7086
  }
7315
7087
  `;
7316
- return await this.execute(mutation, variables, {
7317
- mappings: UpdateUserMappings,
7318
- requiresAuth: true,
7319
- transportOptions: options
7320
- });
7088
+ const { fields, transportOptions } = splitFieldsOption(options);
7089
+ return await this.execute(
7090
+ composeDocument(mutation, fields, []),
7091
+ variables,
7092
+ {
7093
+ mappings: UpdateUserMappings,
7094
+ requiresAuth: true,
7095
+ transportOptions
7096
+ }
7097
+ );
7321
7098
  }
7322
7099
  }
7323
7100
 
@@ -7340,19 +7117,6 @@ const SaveMediaListEntryMappings = {
7340
7117
  completedAt: FuzzyDateMappings
7341
7118
  };
7342
7119
  class SaveMediaListEntryMutation extends AniListOperation {
7343
- /**
7344
- * {@link SaveMediaListEntryMutation.saveMediaListEntry} sends a mutation request to save a media list entry.
7345
- *
7346
- * @param variables - Values from {@link SaveMediaListEntryVariables} for the mutation.
7347
- * @returns The {@link MediaListResponse} returned by the mutation.
7348
- * @throws Throws if no authentication token is configured, `mediaId` is missing or invalid, or the mutation request fails.
7349
- * @see https://docs.anilist.co/reference/object/medialist
7350
- * @param options - Optional {@link RequestOptions} merged over the instance-level settings for this call only.
7351
- * @example
7352
- * ```typescript
7353
- * const result = await new SaveMediaListEntryMutation("your-token").saveMediaListEntry({ mediaId: 1, status: "COMPLETED" });
7354
- * ```
7355
- */
7356
7120
  async saveMediaListEntry(variables, options) {
7357
7121
  const mutation = `
7358
7122
  mutation ($id: Int, $mediaId: Int, $status: MediaListStatus, $score: Float, $scoreRaw: Int, $progress: Int, $progressVolumes: Int, $repeat: Int, $priority: Int, $private: Boolean, $notes: String, $hiddenFromStatusLists: Boolean, $customLists: [String], $advancedScores: [Float], $startedAt: FuzzyDateInput, $completedAt: FuzzyDateInput) {
@@ -7379,74 +7143,79 @@ class SaveMediaListEntryMutation extends AniListOperation {
7379
7143
  }
7380
7144
  }
7381
7145
  `;
7382
- return await this.execute(mutation, variables, {
7383
- requirements: [
7384
- {
7385
- kind: "all",
7386
- names: ["mediaId"],
7387
- message: "The SaveMediaListEntry mutation requires a mediaId variable."
7388
- }
7389
- ],
7390
- mappings: SaveMediaListEntryMappings,
7391
- requiresAuth: true,
7392
- transportOptions: options
7393
- });
7146
+ const { fields, transportOptions } = splitFieldsOption(options);
7147
+ return await this.execute(
7148
+ composeDocument(mutation, fields, []),
7149
+ variables,
7150
+ {
7151
+ requirements: [
7152
+ {
7153
+ kind: "all",
7154
+ names: ["mediaId"],
7155
+ message: "The SaveMediaListEntry mutation requires a mediaId variable."
7156
+ }
7157
+ ],
7158
+ mappings: SaveMediaListEntryMappings,
7159
+ requiresAuth: true,
7160
+ transportOptions
7161
+ }
7162
+ );
7394
7163
  }
7395
7164
  }
7396
7165
 
7397
- function op(name, operationClass) {
7398
- return { name, operationClass, methodName: name };
7166
+ function op(name, operationClass, alwaysSelected) {
7167
+ return { name, operationClass, methodName: name, alwaysSelected };
7399
7168
  }
7400
- function opAs(name, operationClass, methodName) {
7401
- return { name, operationClass, methodName };
7169
+ function opAs(name, operationClass, methodName, alwaysSelected) {
7170
+ return { name, operationClass, methodName, alwaysSelected };
7402
7171
  }
7403
7172
  const ANILIST_OPERATION_REGISTRY = {
7404
7173
  query: [
7405
- op("user", UserQuery),
7406
- op("media", MediaQuery),
7174
+ op("user", UserQuery, USER_ALWAYS),
7175
+ op("media", MediaQuery, MEDIA_ALWAYS),
7407
7176
  op("mediaTrend", MediaTrendQuery),
7408
- op("airingSchedule", AiringScheduleQuery),
7409
- op("character", CharacterQuery),
7410
- op("staff", StaffQuery),
7411
- op("mediaList", MediaListQuery),
7412
- op("mediaListCollection", MediaListCollectionQuery),
7177
+ op("airingSchedule", AiringScheduleQuery, AIRING_SCHEDULE_ALWAYS),
7178
+ op("character", CharacterQuery, CHARACTER_ALWAYS),
7179
+ op("staff", StaffQuery, STAFF_ALWAYS),
7180
+ op("mediaList", MediaListQuery, MEDIA_LIST_ALWAYS),
7181
+ op("mediaListCollection", MediaListCollectionQuery, MEDIA_LIST_COLLECTION_ALWAYS),
7413
7182
  op("genreCollection", GenreCollectionQuery),
7414
7183
  op("mediaTagCollection", MediaTagCollectionQuery),
7415
7184
  op("viewer", ViewerQuery),
7416
7185
  op("notification", NotificationQuery),
7417
- op("studio", StudioQuery),
7418
- op("review", ReviewQuery),
7186
+ op("studio", StudioQuery, STUDIO_ALWAYS),
7187
+ op("review", ReviewQuery, REVIEW_ALWAYS),
7419
7188
  op("activity", ActivityQuery),
7420
7189
  op("activityReply", ActivityReplyQuery),
7421
7190
  op("following", FollowingQuery),
7422
7191
  op("follower", FollowerQuery),
7423
- op("thread", ThreadQuery),
7424
- op("threadComment", ThreadCommentQuery),
7425
- op("recommendation", RecommendationQuery),
7192
+ op("thread", ThreadQuery, THREAD_ALWAYS),
7193
+ op("threadComment", ThreadCommentQuery, THREAD_COMMENT_ALWAYS),
7194
+ op("recommendation", RecommendationQuery, RECOMMENDATION_ALWAYS),
7426
7195
  op("markdown", MarkdownQuery),
7427
7196
  op("aniChartUser", AniChartUserQuery),
7428
7197
  op("siteStatistics", SiteStatisticsQuery),
7429
7198
  op("externalLinkSourceCollection", ExternalLinkSourceCollectionQuery)
7430
7199
  ],
7431
7200
  page: [
7432
- op("users", UsersQuery),
7433
- op("medias", MediasQuery),
7434
- op("characters", CharactersQuery),
7435
- op("staffs", StaffsQuery),
7436
- op("studios", StudiosQuery),
7437
- op("mediaLists", MediaListsQuery),
7438
- op("airingSchedules", AiringSchedulesQuery),
7439
- op("mediaTrends", MediaTrendsQuery),
7201
+ op("users", UsersQuery, PAGE_ALWAYS),
7202
+ op("medias", MediasQuery, PAGE_ALWAYS),
7203
+ op("characters", CharactersQuery, PAGE_ALWAYS),
7204
+ op("staffs", StaffsQuery, PAGE_ALWAYS),
7205
+ op("studios", StudiosQuery, PAGE_ALWAYS),
7206
+ op("mediaLists", MediaListsQuery, PAGE_ALWAYS),
7207
+ op("airingSchedules", AiringSchedulesQuery, PAGE_ALWAYS),
7208
+ op("mediaTrends", MediaTrendsQuery, PAGE_ALWAYS),
7440
7209
  op("notifications", NotificationsQuery),
7441
- op("followers", FollowersQuery),
7442
- opAs("following", FollowingsQuery, "followings"),
7210
+ op("followers", FollowersQuery, PAGE_ALWAYS),
7211
+ opAs("following", FollowingsQuery, "followings", PAGE_ALWAYS),
7443
7212
  op("activities", ActivitiesQuery),
7444
- opAs("activityReplies", ActivityRepliesQuery, "activityReplies"),
7445
- op("threads", ThreadsQuery),
7446
- opAs("threadComments", ThreadCommentsQuery, "threadComments"),
7447
- op("reviews", ReviewsQuery),
7448
- opAs("recommendations", RecommendationsQuery, "recommendations"),
7449
- op("likes", LikesQuery)
7213
+ opAs("activityReplies", ActivityRepliesQuery, "activityReplies", PAGE_ALWAYS),
7214
+ op("threads", ThreadsQuery, PAGE_ALWAYS),
7215
+ opAs("threadComments", ThreadCommentsQuery, "threadComments", PAGE_ALWAYS),
7216
+ op("reviews", ReviewsQuery, PAGE_ALWAYS),
7217
+ opAs("recommendations", RecommendationsQuery, "recommendations", PAGE_ALWAYS),
7218
+ op("likes", LikesQuery, PAGE_ALWAYS)
7450
7219
  ],
7451
7220
  mutation: [
7452
7221
  op("updateUser", UpdateUserMutation),
@@ -7541,7 +7310,8 @@ function buildAniListWiring(authToken, options) {
7541
7310
  paginatePages,
7542
7311
  paginateChunks,
7543
7312
  fuzzyDate,
7544
- flattenMediaListCollection
7313
+ flattenMediaListCollection,
7314
+ crossLink
7545
7315
  },
7546
7316
  {
7547
7317
  custom: {
@@ -7618,7 +7388,8 @@ function resolveMalCredentials(credentials) {
7618
7388
  "accessToken",
7619
7389
  "refreshToken",
7620
7390
  "clientId",
7621
- "clientSecret"
7391
+ "clientSecret",
7392
+ "onTokenRefresh"
7622
7393
  ])
7623
7394
  };
7624
7395
  }
@@ -7687,6 +7458,19 @@ const MAL_API_BASE_URL = "https://api.myanimelist.net/v2";
7687
7458
  const MAL_AUTHORIZE_URL = "https://myanimelist.net/v1/oauth2/authorize";
7688
7459
  const MAL_TOKEN_URL = "https://myanimelist.net/v1/oauth2/token";
7689
7460
  const MAL_API_REFERENCE = "https://myanimelist.net/apiconfig/references/api/v2";
7461
+ const DEFAULT_MAL_ANIME_FIELDS = [
7462
+ "id",
7463
+ "title",
7464
+ "main_picture",
7465
+ "synopsis",
7466
+ "status",
7467
+ "mean",
7468
+ "num_episodes",
7469
+ "media_type",
7470
+ "start_date",
7471
+ "broadcast",
7472
+ "average_episode_duration"
7473
+ ];
7690
7474
 
7691
7475
  class MalAnimeOperation extends RestOperation {
7692
7476
  /** The base URL for MyAnimeList API v2, from {@link MAL_API_BASE_URL}. */
@@ -7694,7 +7478,7 @@ class MalAnimeOperation extends RestOperation {
7694
7478
  /**
7695
7479
  * {@link MalAnimeOperation.get} gets one anime by its MyAnimeList ID.
7696
7480
  *
7697
- * It calls `GET /anime/{id}` through `RestOperation.execute` and returns a {@link MalAnime} shaped by {@link MalRequestOptions.fields}. The facade alias is `MyAnimeListAnimeApi.get`.
7481
+ * It calls `GET /anime/{id}` through `RestOperation.execute` and returns a {@link MalAnime} shaped by {@link MalRequestOptions.fields}; when `fields` is omitted it falls back to {@link DEFAULT_MAL_ANIME_FIELDS}. The facade alias is `MyAnimeListAnimeApi.get`.
7698
7482
  *
7699
7483
  * @param id - The MyAnimeList anime ID.
7700
7484
  * @param options - Optional field selection and transport settings; a {@link MalRequestOptions} merged over the instance defaults.
@@ -7709,12 +7493,98 @@ class MalAnimeOperation extends RestOperation {
7709
7493
  */
7710
7494
  async get(id, options = {}) {
7711
7495
  const { fields, ...transportOptions } = options;
7496
+ const selectedFields = fields ?? DEFAULT_MAL_ANIME_FIELDS;
7712
7497
  return await this.execute("/anime/{id}", {
7713
7498
  transportOptions,
7714
- query: fields === void 0 ? void 0 : { fields: Array.isArray(fields) ? fields.join(",") : fields },
7499
+ query: {
7500
+ fields: Array.isArray(selectedFields) ? selectedFields.join(",") : selectedFields
7501
+ },
7715
7502
  pathParams: { id }
7716
7503
  });
7717
7504
  }
7505
+ /**
7506
+ * {@link MalAnimeOperation.seasonal} gets the anime of one broadcast season.
7507
+ *
7508
+ * It calls `GET /anime/season/{year}/{season}` through `RestOperation.execute` and returns a {@link MalSeasonalAnimeResponse} page of {@link MalSeasonalAnime} entries shaped by {@link MalRequestOptions.fields}. The facade alias is `MyAnimeListAnimeApi.seasonal` and it is a public read.
7509
+ *
7510
+ * @param year - The season's year.
7511
+ * @param season - The season's broadcast window; one of {@link MalSeason}.
7512
+ * @param options - Optional field selection and transport settings; a {@link MalRequestOptions} merged over the instance defaults.
7513
+ * @returns The seasonal anime page, a {@link MalSeasonalAnimeResponse}.
7514
+ * @throws A normalized `AniLinkError` when the request fails.
7515
+ * @example
7516
+ * ```typescript
7517
+ * const api = new AniLink({ mal: { accessToken: "mal-token" } }).mal;
7518
+ * const season = await api.anime.seasonal(2024, "winter", {
7519
+ * fields: ["id", "title", "main_picture"],
7520
+ * });
7521
+ * console.log(season.data[0]?.node.title);
7522
+ * ```
7523
+ * @see https://myanimelist.net/apiconfig/references/api/v2#tag/anime/operation/anime_season_year_season_get
7524
+ */
7525
+ async seasonal(year, season, options = {}) {
7526
+ const { fields, ...transportOptions } = options;
7527
+ return await this.execute("/anime/season/{year}/{season}", {
7528
+ transportOptions,
7529
+ query: fields === void 0 ? void 0 : { fields: Array.isArray(fields) ? fields.join(",") : fields },
7530
+ pathParams: { year, season }
7531
+ });
7532
+ }
7533
+ /**
7534
+ * {@link MalAnimeOperation.ranking} gets one of MyAnimeList's anime ranking lists.
7535
+ *
7536
+ * It calls `GET /anime/ranking` through `RestOperation.execute` with the `ranking_type` query parameter and returns a {@link MalAnimeRankingResponse} page of {@link MalRankingEntry} entries shaped by {@link MalRequestOptions.fields}. The facade alias is `MyAnimeListAnimeApi.ranking` and it is a public read.
7537
+ *
7538
+ * @param rankingType - The ranking list to fetch; one of {@link MalRankingType}.
7539
+ * @param options - Optional field selection and transport settings; a {@link MalRequestOptions} merged over the instance defaults.
7540
+ * @returns The ranking page, a {@link MalAnimeRankingResponse}.
7541
+ * @throws A normalized `AniLinkError` when the request fails.
7542
+ * @example
7543
+ * ```typescript
7544
+ * const api = new AniLink({ mal: { accessToken: "mal-token" } }).mal;
7545
+ * const top = await api.anime.ranking("airing", {
7546
+ * fields: ["id", "title", "mean"],
7547
+ * });
7548
+ * console.log(top.data[0]?.node.title, top.data[0]?.ranking.rank);
7549
+ * ```
7550
+ * @see https://myanimelist.net/apiconfig/references/api/v2#tag/anime/operation/anime_ranking_get
7551
+ */
7552
+ async ranking(rankingType, options = {}) {
7553
+ const { fields, ...transportOptions } = options;
7554
+ return await this.execute("/anime/ranking", {
7555
+ transportOptions,
7556
+ query: {
7557
+ ranking_type: rankingType,
7558
+ ...fields === void 0 ? {} : { fields: Array.isArray(fields) ? fields.join(",") : fields }
7559
+ }
7560
+ });
7561
+ }
7562
+ /**
7563
+ * {@link MalAnimeOperation.suggestions} gets MyAnimeList's anime suggestions for the authenticated user.
7564
+ *
7565
+ * It calls `GET /anime/suggestions` through `RestOperation.execute` with `requiresAuth` and returns a {@link MalAnimeSuggestionsResponse} page of {@link MalSuggestion} entries shaped by {@link MalRequestOptions.fields}. The facade alias is `MyAnimeListAnimeApi.suggestions` and it requires `MalCredentials.accessToken`.
7566
+ *
7567
+ * @param options - Optional field selection and transport settings; a {@link MalRequestOptions} merged over the instance defaults.
7568
+ * @returns The suggestions page, a {@link MalAnimeSuggestionsResponse}.
7569
+ * @throws An `AniLinkAuthError` without an access token, or a normalized request error.
7570
+ * @example
7571
+ * ```typescript
7572
+ * const api = new AniLink({ mal: { accessToken: "mal-token" } }).mal;
7573
+ * const suggestions = await api.anime.suggestions({
7574
+ * fields: ["id", "title", "main_picture"],
7575
+ * });
7576
+ * console.log(suggestions.data[0]?.node.title);
7577
+ * ```
7578
+ * @see https://myanimelist.net/apiconfig/references/api/v2#tag/anime/operation/anime_suggestions_get
7579
+ */
7580
+ async suggestions(options = {}) {
7581
+ const { fields, ...transportOptions } = options;
7582
+ return await this.execute("/anime/suggestions", {
7583
+ requiresAuth: true,
7584
+ transportOptions,
7585
+ query: fields === void 0 ? void 0 : { fields: Array.isArray(fields) ? fields.join(",") : fields }
7586
+ });
7587
+ }
7718
7588
  /**
7719
7589
  * Encodes a {@link MalAnimeListStatusUpdate} as the form-urlencoded body
7720
7590
  * MAL's list-status endpoints require.
@@ -7913,6 +7783,13 @@ class MalMangaOperation extends RestOperation {
7913
7783
  class MalUserOperation extends RestOperation {
7914
7784
  /** The base URL for MyAnimeList API v2, from {@link MAL_API_BASE_URL}. */
7915
7785
  baseUrl = MAL_API_BASE_URL;
7786
+ /**
7787
+ * Normalizes a `username` argument for the `@me` check: trimmed and
7788
+ * lowercased, so `@ME` and `" @me "` resolve the authenticated user too.
7789
+ */
7790
+ static normalizeUsername(username) {
7791
+ return username.trim().toLowerCase();
7792
+ }
7916
7793
  /**
7917
7794
  * {@link MalUserOperation.me} gets the currently authenticated MyAnimeList user.
7918
7795
  *
@@ -7936,45 +7813,94 @@ class MalUserOperation extends RestOperation {
7936
7813
  query: fields === void 0 ? void 0 : { fields: Array.isArray(fields) ? fields.join(",") : fields }
7937
7814
  });
7938
7815
  }
7939
- }
7940
-
7941
- function buildMyAnimeListApi(credentials) {
7942
- const { auth, options } = resolveMalCredentials(credentials);
7943
- const anime = new MalAnimeOperation(auth, options);
7944
- const manga = new MalMangaOperation(auth, options);
7945
- const user = new MalUserOperation(auth, options);
7946
- return {
7947
- anime: {
7948
- get: anime.get.bind(anime),
7949
- updateMyListStatus: anime.updateMyListStatus.bind(anime),
7950
- deleteFromList: anime.deleteFromList.bind(anime)
7951
- },
7952
- manga: {
7953
- get: manga.get.bind(manga),
7954
- updateMyListStatus: manga.updateMyListStatus.bind(manga),
7955
- deleteFromList: manga.deleteFromList.bind(manga)
7956
- },
7957
- user: { me: user.me.bind(user) }
7958
- };
7959
- }
7960
-
7961
- const buildAniListClient = (credentials, legacyOptions) => {
7962
- const resolved = resolveAniListCredentials(credentials);
7963
- return buildAniListApi(resolved.auth, resolved.options ?? legacyOptions);
7964
- };
7965
- const buildMalClient = (credentials) => buildMyAnimeListApi(credentials);
7966
- const PROVIDER_FACTORIES = {
7967
- anilist: buildAniListClient,
7968
- mal: buildMalClient
7969
- };
7970
- function buildProviderClients(credentials = {}, legacyOptions) {
7971
- const clientHookError = credentials.onHookError;
7972
- const anilistSlot = clientHookError !== void 0 && credentials.anilist?.onHookError === void 0 ? { ...credentials.anilist, onHookError: clientHookError } : credentials.anilist;
7973
- const malSlot = clientHookError !== void 0 && credentials.mal?.onHookError === void 0 ? { ...credentials.mal, onHookError: clientHookError } : credentials.mal;
7974
- return {
7975
- anilist: PROVIDER_FACTORIES.anilist(anilistSlot, legacyOptions),
7976
- mal: PROVIDER_FACTORIES.mal(malSlot)
7977
- };
7816
+ /**
7817
+ * {@link MalUserOperation.animeList} gets a user's anime list, one page at a time.
7818
+ *
7819
+ * It calls `GET /users/{username}/animelist` through `RestOperation.execute` and returns a {@link MalUserAnimeListResponse} page of {@link MalUserAnimeListEntry} entries shaped by {@link MalUserAnimeListOptions.fields}. The facade alias is `MyAnimeListUserApi.animeList` and it is a public read: `username` accepts a user name or `@me`, with `@me` and private lists requiring an access token (a client ID alone cannot resolve `@me`). The `@me` check is case-insensitive and ignores surrounding whitespace.
7820
+ *
7821
+ * @param username - The MyAnimeList user name, or `@me` for the authenticated user (case-insensitive, surrounding whitespace ignored).
7822
+ * @param options - Optional status, sort, paging, field selection, and transport settings; a {@link MalUserAnimeListOptions} merged over the instance defaults.
7823
+ * @returns The anime list page, a {@link MalUserAnimeListResponse}.
7824
+ * @throws An `AniLinkAuthError` when `username` is `@me` and no access token is configured.
7825
+ * @throws A normalized `AniLinkError` when the request fails.
7826
+ * @example
7827
+ * ```typescript
7828
+ * const api = new AniLink({ mal: { accessToken: "mal-token" } }).mal;
7829
+ * const list = await api.user.animeList("@me", {
7830
+ * status: "watching",
7831
+ * fields: ["id", "title", "list_status"],
7832
+ * });
7833
+ * console.log(list.data[0]?.node.title);
7834
+ * ```
7835
+ * @see https://myanimelist.net/apiconfig/references/api/v2#tag/user-animelist/operation/users_user_id_animelist_get
7836
+ */
7837
+ async animeList(username, options = {}) {
7838
+ const { fields, status, sort, limit, offset, ...transportOptions } = options;
7839
+ const normalized = MalUserOperation.normalizeUsername(username);
7840
+ return await this.execute(
7841
+ normalized === "@me" ? "/users/@me/animelist" : "/users/{username}/animelist",
7842
+ {
7843
+ // `@me` resolves the authenticated user, which only a bearer token
7844
+ // can identify — a client ID alone cannot — so fail fast like `me`.
7845
+ requiresAuth: normalized === "@me",
7846
+ transportOptions,
7847
+ // `buildQueryString` skips undefined/null values, so the
7848
+ // optional filters can be passed straight through.
7849
+ query: {
7850
+ fields: Array.isArray(fields) ? fields.join(",") : fields,
7851
+ status,
7852
+ sort,
7853
+ limit,
7854
+ offset
7855
+ },
7856
+ pathParams: normalized === "@me" ? void 0 : { username }
7857
+ }
7858
+ );
7859
+ }
7860
+ /**
7861
+ * {@link MalUserOperation.mangaList} gets a user's manga list, one page at a time.
7862
+ *
7863
+ * It calls `GET /users/{username}/mangalist` through `RestOperation.execute` and returns a {@link MalUserMangaListResponse} page of {@link MalUserMangaListEntry} entries shaped by {@link MalUserMangaListOptions.fields}. The facade alias is `MyAnimeListUserApi.mangaList` and it is a public read: `username` accepts a user name or `@me`, with `@me` and private lists requiring an access token (a client ID alone cannot resolve `@me`). The `@me` check is case-insensitive and ignores surrounding whitespace.
7864
+ *
7865
+ * @param username - The MyAnimeList user name, or `@me` for the authenticated user (case-insensitive, surrounding whitespace ignored).
7866
+ * @param options - Optional status, sort, paging, field selection, and transport settings; a {@link MalUserMangaListOptions} merged over the instance defaults.
7867
+ * @returns The manga list page, a {@link MalUserMangaListResponse}.
7868
+ * @throws An `AniLinkAuthError` when `username` is `@me` and no access token is configured.
7869
+ * @throws A normalized `AniLinkError` when the request fails.
7870
+ * @example
7871
+ * ```typescript
7872
+ * const api = new AniLink({ mal: { accessToken: "mal-token" } }).mal;
7873
+ * const list = await api.user.mangaList("@me", {
7874
+ * status: "reading",
7875
+ * fields: ["id", "title", "list_status"],
7876
+ * });
7877
+ * console.log(list.data[0]?.node.title);
7878
+ * ```
7879
+ * @see https://myanimelist.net/apiconfig/references/api/v2#tag/user-mangalist/operation/users_user_id_mangalist_get
7880
+ */
7881
+ async mangaList(username, options = {}) {
7882
+ const { fields, status, sort, limit, offset, ...transportOptions } = options;
7883
+ const normalized = MalUserOperation.normalizeUsername(username);
7884
+ return await this.execute(
7885
+ normalized === "@me" ? "/users/@me/mangalist" : "/users/{username}/mangalist",
7886
+ {
7887
+ // `@me` resolves the authenticated user, which only a bearer token
7888
+ // can identify — a client ID alone cannot — so fail fast like `me`.
7889
+ requiresAuth: normalized === "@me",
7890
+ transportOptions,
7891
+ // `buildQueryString` skips undefined/null values, so the
7892
+ // optional filters can be passed straight through.
7893
+ query: {
7894
+ fields: Array.isArray(fields) ? fields.join(",") : fields,
7895
+ status,
7896
+ sort,
7897
+ limit,
7898
+ offset
7899
+ },
7900
+ pathParams: normalized === "@me" ? void 0 : { username }
7901
+ }
7902
+ );
7903
+ }
7978
7904
  }
7979
7905
 
7980
7906
  const sanitizeTokenError = (error, label) => {
@@ -8020,6 +7946,230 @@ const sanitizeTokenError = (error, label) => {
8020
7946
  return new AniLinkError(`${label} failed.`, AniLinkErrorCodes.UNKNOWN);
8021
7947
  };
8022
7948
 
7949
+ const MAL_AUTH_TIMEOUT_MS = 1e4;
7950
+ const buildMalAuthorizationUrl = (clientId, codeChallenge, state) => {
7951
+ const params = new URLSearchParams({
7952
+ response_type: "code",
7953
+ client_id: clientId,
7954
+ code_challenge: codeChallenge,
7955
+ code_challenge_method: "plain"
7956
+ });
7957
+ if (state !== void 0) params.set("state", state);
7958
+ return `${MAL_AUTHORIZE_URL}?${params.toString().replaceAll("+", "%20")}`;
7959
+ };
7960
+ const normalizeMalTokenError = (error) => sanitizeTokenError(error, "MAL token request");
7961
+ const requestMalToken = async (params, options) => {
7962
+ try {
7963
+ const body = new URLSearchParams(params).toString();
7964
+ return await sendRequest(MAL_TOKEN_URL, "POST", body, void 0, {
7965
+ requiresAuth: false,
7966
+ options: {
7967
+ ...options,
7968
+ timeout: options?.timeout ?? MAL_AUTH_TIMEOUT_MS,
7969
+ exposeRawAxiosError: false
7970
+ },
7971
+ contentType: "application/x-www-form-urlencoded"
7972
+ });
7973
+ } catch (error) {
7974
+ throw normalizeMalTokenError(error);
7975
+ }
7976
+ };
7977
+ const getMalAccessToken = (request) => requestMalToken(
7978
+ {
7979
+ client_id: request.clientId,
7980
+ code: request.code,
7981
+ code_verifier: request.codeVerifier,
7982
+ grant_type: "authorization_code",
7983
+ ...request.clientSecret === void 0 ? {} : { client_secret: request.clientSecret }
7984
+ },
7985
+ request.options
7986
+ );
7987
+ const refreshMalAccessToken = (request) => requestMalToken(
7988
+ {
7989
+ client_id: request.clientId,
7990
+ grant_type: "refresh_token",
7991
+ refresh_token: request.refreshToken,
7992
+ ...request.clientSecret === void 0 ? {} : { client_secret: request.clientSecret }
7993
+ },
7994
+ request.options
7995
+ );
7996
+ const getMalTokenExpiry = (response, now = Date.now()) => new Date(now + response.expires_in * 1e3);
7997
+
7998
+ class MalTokenRefresher {
7999
+ clientId;
8000
+ clientSecret;
8001
+ refreshToken;
8002
+ onTokenRefresh;
8003
+ onHookError;
8004
+ applyAccessToken;
8005
+ refreshInFlight;
8006
+ /**
8007
+ * Constructs a refresh coordinator from the credential slot fields.
8008
+ *
8009
+ * @param options - The refresh grant fields, the auth-swap callback, and the optional persistence callback.
8010
+ */
8011
+ constructor(options) {
8012
+ this.clientId = options.clientId;
8013
+ this.clientSecret = options.clientSecret;
8014
+ this.refreshToken = options.refreshToken;
8015
+ this.onTokenRefresh = options.onTokenRefresh;
8016
+ this.onHookError = options.onHookError;
8017
+ this.applyAccessToken = options.applyAccessToken;
8018
+ }
8019
+ /**
8020
+ * Runs one operation attempt under the automatic refresh lifecycle.
8021
+ *
8022
+ * A 401 from the first attempt — or an {@link AniLinkAuthError} raised
8023
+ * before any request because no access token is configured, which lets a
8024
+ * persisted refresh token bootstrap the client — triggers exactly one
8025
+ * refresh (concurrent failures share the in-flight refresh) followed by
8026
+ * a single replay with the new auth material. Any other failure — a
8027
+ * non-401 first attempt, a failed refresh, or a replay that fails again
8028
+ * — surfaces unchanged.
8029
+ *
8030
+ * @param operation - A closure performing one request attempt; called at most twice.
8031
+ * @returns The first successful attempt's result.
8032
+ * @throws The sanitized refresh error when the token endpoint rejects the grant, or the operation's own error otherwise.
8033
+ */
8034
+ async executeWithRefresh(operation) {
8035
+ try {
8036
+ return await operation();
8037
+ } catch (error) {
8038
+ const isExpiredToken = error instanceof AniLinkApiError && error.status === 401;
8039
+ const isMissingToken = error instanceof AniLinkAuthError;
8040
+ if (!isExpiredToken && !isMissingToken) {
8041
+ throw error;
8042
+ }
8043
+ await this.refresh();
8044
+ return await operation();
8045
+ }
8046
+ }
8047
+ /**
8048
+ * Performs one deduplicated refresh grant, applies the fresh access
8049
+ * token exactly once per grant, notifies the persistence callback, and
8050
+ * applies the rotation semantics: a response without `refresh_token`
8051
+ * keeps the stored one.
8052
+ *
8053
+ * The auth swap and the callback run inside the shared in-flight promise
8054
+ * so concurrent 401s observe one grant, one swap, and one callback
8055
+ * invocation — and so the swap always lands in grant order.
8056
+ *
8057
+ * @returns The effective token response, with `refresh_token` filled in when MAL omitted it.
8058
+ */
8059
+ async refresh() {
8060
+ if (this.refreshInFlight === void 0) {
8061
+ this.refreshInFlight = this.performRefresh().then((response) => {
8062
+ this.applyAccessToken(response.access_token);
8063
+ safeInvoke(this.onTokenRefresh, "onTokenRefresh", this.onHookError, response);
8064
+ return response;
8065
+ }).finally(() => {
8066
+ this.refreshInFlight = void 0;
8067
+ });
8068
+ }
8069
+ return await this.refreshInFlight;
8070
+ }
8071
+ /**
8072
+ * Runs the refresh grant and stores the rotated refresh token.
8073
+ *
8074
+ * @returns The effective token response.
8075
+ */
8076
+ async performRefresh() {
8077
+ const response = await refreshMalAccessToken({
8078
+ clientId: this.clientId,
8079
+ refreshToken: this.refreshToken,
8080
+ clientSecret: this.clientSecret
8081
+ });
8082
+ this.refreshToken = response.refresh_token ?? this.refreshToken;
8083
+ return { ...response, refresh_token: this.refreshToken };
8084
+ }
8085
+ }
8086
+ const buildRefreshedAuth = (auth, accessToken) => ({
8087
+ token: accessToken,
8088
+ headers: typeof auth === "object" && auth?.headers !== void 0 ? { ...auth.headers } : void 0
8089
+ });
8090
+
8091
+ function buildMyAnimeListApi(credentials) {
8092
+ const { auth, options } = resolveMalCredentials(credentials);
8093
+ const anime = new MalAnimeOperation(auth, options);
8094
+ const manga = new MalMangaOperation(auth, options);
8095
+ const user = new MalUserOperation(auth, options);
8096
+ const refresher = credentials?.refreshToken !== void 0 && credentials.refreshToken !== "" && credentials.clientId !== void 0 && credentials.clientId !== "" ? new MalTokenRefresher({
8097
+ clientId: credentials.clientId,
8098
+ refreshToken: credentials.refreshToken,
8099
+ clientSecret: credentials.clientSecret,
8100
+ onTokenRefresh: credentials.onTokenRefresh,
8101
+ onHookError: credentials.onHookError,
8102
+ applyAccessToken: (accessToken) => {
8103
+ const refreshed = buildRefreshedAuth(auth, accessToken);
8104
+ for (const operation of [anime, manga, user]) {
8105
+ operation.updateAuth(refreshed);
8106
+ }
8107
+ }
8108
+ }) : void 0;
8109
+ if (refresher === void 0) {
8110
+ return {
8111
+ anime: {
8112
+ get: anime.get.bind(anime),
8113
+ seasonal: anime.seasonal.bind(anime),
8114
+ ranking: anime.ranking.bind(anime),
8115
+ suggestions: anime.suggestions.bind(anime),
8116
+ updateMyListStatus: anime.updateMyListStatus.bind(anime),
8117
+ deleteFromList: anime.deleteFromList.bind(anime)
8118
+ },
8119
+ manga: {
8120
+ get: manga.get.bind(manga),
8121
+ updateMyListStatus: manga.updateMyListStatus.bind(manga),
8122
+ deleteFromList: manga.deleteFromList.bind(manga)
8123
+ },
8124
+ user: {
8125
+ me: user.me.bind(user),
8126
+ animeList: user.animeList.bind(user),
8127
+ mangaList: user.mangaList.bind(user)
8128
+ }
8129
+ };
8130
+ }
8131
+ const wrap = (method) => (...args) => refresher.executeWithRefresh(() => method(...args));
8132
+ return {
8133
+ anime: {
8134
+ get: wrap(anime.get.bind(anime)),
8135
+ seasonal: wrap(anime.seasonal.bind(anime)),
8136
+ ranking: wrap(anime.ranking.bind(anime)),
8137
+ suggestions: wrap(anime.suggestions.bind(anime)),
8138
+ updateMyListStatus: wrap(anime.updateMyListStatus.bind(anime)),
8139
+ deleteFromList: wrap(anime.deleteFromList.bind(anime))
8140
+ },
8141
+ manga: {
8142
+ get: wrap(manga.get.bind(manga)),
8143
+ updateMyListStatus: wrap(manga.updateMyListStatus.bind(manga)),
8144
+ deleteFromList: wrap(manga.deleteFromList.bind(manga))
8145
+ },
8146
+ user: {
8147
+ me: wrap(user.me.bind(user)),
8148
+ animeList: wrap(user.animeList.bind(user)),
8149
+ mangaList: wrap(user.mangaList.bind(user))
8150
+ }
8151
+ };
8152
+ }
8153
+
8154
+ const buildAniListClient = (credentials, legacyOptions) => {
8155
+ const resolved = resolveAniListCredentials(credentials);
8156
+ return buildAniListApi(resolved.auth, resolved.options ?? legacyOptions);
8157
+ };
8158
+ const buildMalClient = (credentials) => buildMyAnimeListApi(credentials);
8159
+ const PROVIDER_FACTORIES = {
8160
+ anilist: buildAniListClient,
8161
+ mal: buildMalClient
8162
+ };
8163
+ function buildProviderClients(credentials = {}, legacyOptions) {
8164
+ const clientHookError = credentials.onHookError;
8165
+ const anilistSlot = clientHookError !== void 0 && credentials.anilist?.onHookError === void 0 ? { ...credentials.anilist, onHookError: clientHookError } : credentials.anilist;
8166
+ const malSlot = clientHookError !== void 0 && credentials.mal?.onHookError === void 0 ? { ...credentials.mal, onHookError: clientHookError } : credentials.mal;
8167
+ return {
8168
+ anilist: PROVIDER_FACTORIES.anilist(anilistSlot, legacyOptions),
8169
+ mal: PROVIDER_FACTORIES.mal(malSlot)
8170
+ };
8171
+ }
8172
+
8023
8173
  const AUTH_TOKEN_TIMEOUT_MS = 1e4;
8024
8174
  const ANILIST_TOKEN_URL = "https://anilist.co/api/v2/oauth/token";
8025
8175
  const ANILIST_AUTHORIZE_URL = "https://anilist.co/api/v2/oauth/authorize";
@@ -8236,55 +8386,6 @@ class ResponseCache {
8236
8386
  }
8237
8387
  }
8238
8388
 
8239
- const MAL_AUTH_TIMEOUT_MS = 1e4;
8240
- const buildMalAuthorizationUrl = (clientId, codeChallenge, state) => {
8241
- const params = new URLSearchParams({
8242
- response_type: "code",
8243
- client_id: clientId,
8244
- code_challenge: codeChallenge,
8245
- code_challenge_method: "S256"
8246
- });
8247
- if (state !== void 0) params.set("state", state);
8248
- return `${MAL_AUTHORIZE_URL}?${params.toString().replaceAll("+", "%20")}`;
8249
- };
8250
- const normalizeMalTokenError = (error) => sanitizeTokenError(error, "MAL token request");
8251
- const requestMalToken = async (params, options) => {
8252
- try {
8253
- const body = new URLSearchParams(params).toString();
8254
- return await sendRequest(MAL_TOKEN_URL, "POST", body, void 0, {
8255
- requiresAuth: false,
8256
- options: {
8257
- ...options,
8258
- timeout: options?.timeout ?? MAL_AUTH_TIMEOUT_MS,
8259
- exposeRawAxiosError: false
8260
- },
8261
- contentType: "application/x-www-form-urlencoded"
8262
- });
8263
- } catch (error) {
8264
- throw normalizeMalTokenError(error);
8265
- }
8266
- };
8267
- const getMalAccessToken = (request) => requestMalToken(
8268
- {
8269
- client_id: request.clientId,
8270
- code: request.code,
8271
- code_verifier: request.codeVerifier,
8272
- grant_type: "authorization_code",
8273
- ...request.clientSecret === void 0 ? {} : { client_secret: request.clientSecret }
8274
- },
8275
- request.options
8276
- );
8277
- const refreshMalAccessToken = (request) => requestMalToken(
8278
- {
8279
- client_id: request.clientId,
8280
- grant_type: "refresh_token",
8281
- refresh_token: request.refreshToken,
8282
- ...request.clientSecret === void 0 ? {} : { client_secret: request.clientSecret }
8283
- },
8284
- request.options
8285
- );
8286
- const getMalTokenExpiry = (response, now = Date.now()) => new Date(now + response.expires_in * 1e3);
8287
-
8288
8389
  class AniLink {
8289
8390
  /**
8290
8391
  * The AniList GraphQL API surface, a {@link AniListApi} composed from the
@@ -8340,4 +8441,4 @@ class AniLink {
8340
8441
  }
8341
8442
  }
8342
8443
 
8343
- export { ANILIST_AUTHORIZE_URL, ANILIST_TOKEN_URL, AniLink, AniLinkApiError, AniLinkAuthError, AniLinkError, AniLinkErrorCodes, AniLinkGraphQLError, AniLinkNetworkError, AniLinkRestError, AniLinkValidationError, MAL_API_BASE_URL, MAL_API_REFERENCE, MAL_AUTHORIZE_URL, MAL_TOKEN_URL, ResponseCache, buildAuthorizationUrl, buildMalAuthorizationUrl, buildMyAnimeListApi, buildProviderClients, destroyCachedAgents, getAccessToken, getMalAccessToken, getMalTokenExpiry, getTokenExpiry, paginate, paginateChunks, paginatePages, refreshAccessToken, refreshMalAccessToken };
8444
+ export { ANILIST_AUTHORIZE_URL, ANILIST_TOKEN_URL, AniLink, AniLinkApiError, AniLinkAuthError, AniLinkError, AniLinkErrorCodes, AniLinkGraphQLError, AniLinkNetworkError, AniLinkRestError, AniLinkValidationError, MAL_API_BASE_URL, MAL_API_REFERENCE, MAL_AUTHORIZE_URL, MAL_TOKEN_URL, ResponseCache, buildAuthorizationUrl, buildMalAuthorizationUrl, buildMyAnimeListApi, buildProviderClients, crossLink, destroyCachedAgents, getAccessToken, getMalAccessToken, getMalTokenExpiry, getTokenExpiry, paginate, paginateChunks, paginatePages, refreshAccessToken, refreshMalAccessToken };