ani-client 2.1.1 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +60 -1
- package/dist/index.d.ts +60 -1
- package/dist/index.js +223 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +223 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -122,6 +122,174 @@ function sortObjectKeys(obj) {
|
|
|
122
122
|
return sorted;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
// src/cache/normalized.ts
|
|
126
|
+
var NormalizedCache = class {
|
|
127
|
+
ttl;
|
|
128
|
+
maxSize;
|
|
129
|
+
enabled;
|
|
130
|
+
swrMs;
|
|
131
|
+
queryStore = /* @__PURE__ */ new Map();
|
|
132
|
+
entityStore = /* @__PURE__ */ new Map();
|
|
133
|
+
_hits = 0;
|
|
134
|
+
_misses = 0;
|
|
135
|
+
_stales = 0;
|
|
136
|
+
constructor(options = {}) {
|
|
137
|
+
this.ttl = options.ttl ?? 24 * 60 * 60 * 1e3;
|
|
138
|
+
this.maxSize = options.maxSize ?? 500;
|
|
139
|
+
this.enabled = options.enabled ?? true;
|
|
140
|
+
this.swrMs = options.staleWhileRevalidateMs ?? 0;
|
|
141
|
+
}
|
|
142
|
+
static key(query, variables) {
|
|
143
|
+
const normalized = normalizeQuery(query);
|
|
144
|
+
return `${normalized}|${JSON.stringify(sortObjectKeys(variables))}`;
|
|
145
|
+
}
|
|
146
|
+
/** Normalizes a GraphQL response, extracting entities and returning a tree of references. */
|
|
147
|
+
normalize(data, seen = /* @__PURE__ */ new WeakSet()) {
|
|
148
|
+
if (Array.isArray(data)) {
|
|
149
|
+
if (seen.has(data)) return null;
|
|
150
|
+
seen.add(data);
|
|
151
|
+
return data.map((item) => this.normalize(item, seen));
|
|
152
|
+
}
|
|
153
|
+
if (data !== null && typeof data === "object") {
|
|
154
|
+
if (seen.has(data)) return null;
|
|
155
|
+
seen.add(data);
|
|
156
|
+
const obj = data;
|
|
157
|
+
if (typeof obj.__typename === "string" && (typeof obj.id === "number" || typeof obj.id === "string")) {
|
|
158
|
+
const ref = `${obj.__typename}:${obj.id}`;
|
|
159
|
+
const normalizedObj = {};
|
|
160
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
161
|
+
normalizedObj[k] = this.normalize(v, seen);
|
|
162
|
+
}
|
|
163
|
+
const existing = this.entityStore.get(ref) || {};
|
|
164
|
+
this.entityStore.set(ref, { ...existing, ...normalizedObj });
|
|
165
|
+
return { __ref: ref };
|
|
166
|
+
}
|
|
167
|
+
const result = {};
|
|
168
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
169
|
+
result[k] = this.normalize(v, seen);
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
return data;
|
|
174
|
+
}
|
|
175
|
+
/** Reconstructs a GraphQL response from references. */
|
|
176
|
+
denormalize(data, seen = /* @__PURE__ */ new Set()) {
|
|
177
|
+
if (Array.isArray(data)) {
|
|
178
|
+
return data.map((item) => this.denormalize(item, seen));
|
|
179
|
+
}
|
|
180
|
+
if (data !== null && typeof data === "object") {
|
|
181
|
+
const obj = data;
|
|
182
|
+
if (typeof obj.__ref === "string") {
|
|
183
|
+
const ref = obj.__ref;
|
|
184
|
+
if (seen.has(ref)) {
|
|
185
|
+
return { __ref: ref };
|
|
186
|
+
}
|
|
187
|
+
seen.add(ref);
|
|
188
|
+
const entity = this.entityStore.get(ref);
|
|
189
|
+
if (!entity) return void 0;
|
|
190
|
+
const result2 = this.denormalize(entity, seen);
|
|
191
|
+
seen.delete(ref);
|
|
192
|
+
return result2;
|
|
193
|
+
}
|
|
194
|
+
const result = {};
|
|
195
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
196
|
+
const denormalized = this.denormalize(v, seen);
|
|
197
|
+
if (denormalized === void 0) return void 0;
|
|
198
|
+
result[k] = denormalized;
|
|
199
|
+
}
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
return data;
|
|
203
|
+
}
|
|
204
|
+
getWithMeta(key) {
|
|
205
|
+
if (!this.enabled) return void 0;
|
|
206
|
+
const entry = this.queryStore.get(key);
|
|
207
|
+
if (!entry) {
|
|
208
|
+
this._misses++;
|
|
209
|
+
return void 0;
|
|
210
|
+
}
|
|
211
|
+
const now = Date.now();
|
|
212
|
+
let isStale = false;
|
|
213
|
+
if (now > entry.expiresAt) {
|
|
214
|
+
if (this.swrMs > 0 && now <= entry.expiresAt + this.swrMs) {
|
|
215
|
+
isStale = true;
|
|
216
|
+
} else {
|
|
217
|
+
this.queryStore.delete(key);
|
|
218
|
+
this._misses++;
|
|
219
|
+
return void 0;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const denormalized = this.denormalize(entry.data);
|
|
223
|
+
if (denormalized === void 0) {
|
|
224
|
+
this.queryStore.delete(key);
|
|
225
|
+
this._misses++;
|
|
226
|
+
return void 0;
|
|
227
|
+
}
|
|
228
|
+
this.queryStore.delete(key);
|
|
229
|
+
this.queryStore.set(key, entry);
|
|
230
|
+
if (isStale) {
|
|
231
|
+
this._stales++;
|
|
232
|
+
} else {
|
|
233
|
+
this._hits++;
|
|
234
|
+
}
|
|
235
|
+
return { data: denormalized, stale: isStale };
|
|
236
|
+
}
|
|
237
|
+
get(key) {
|
|
238
|
+
const res = this.getWithMeta(key);
|
|
239
|
+
return res ? res.data : void 0;
|
|
240
|
+
}
|
|
241
|
+
set(key, data) {
|
|
242
|
+
if (!this.enabled) return;
|
|
243
|
+
const normalizedData = this.normalize(data);
|
|
244
|
+
this.queryStore.delete(key);
|
|
245
|
+
if (this.maxSize > 0 && this.queryStore.size >= this.maxSize) {
|
|
246
|
+
const firstKey = this.queryStore.keys().next().value;
|
|
247
|
+
if (firstKey !== void 0) this.queryStore.delete(firstKey);
|
|
248
|
+
}
|
|
249
|
+
this.queryStore.set(key, { data: normalizedData, expiresAt: Date.now() + this.ttl });
|
|
250
|
+
}
|
|
251
|
+
delete(key) {
|
|
252
|
+
return this.queryStore.delete(key);
|
|
253
|
+
}
|
|
254
|
+
clear() {
|
|
255
|
+
this.queryStore.clear();
|
|
256
|
+
this.entityStore.clear();
|
|
257
|
+
this._hits = 0;
|
|
258
|
+
this._misses = 0;
|
|
259
|
+
this._stales = 0;
|
|
260
|
+
}
|
|
261
|
+
get size() {
|
|
262
|
+
return this.queryStore.size;
|
|
263
|
+
}
|
|
264
|
+
keys() {
|
|
265
|
+
return [...this.queryStore.keys()];
|
|
266
|
+
}
|
|
267
|
+
invalidate(pattern) {
|
|
268
|
+
const test = typeof pattern === "string" ? (key) => key.includes(pattern) : (key) => pattern.test(key);
|
|
269
|
+
const toDelete = [];
|
|
270
|
+
for (const key of this.queryStore.keys()) {
|
|
271
|
+
if (test(key)) toDelete.push(key);
|
|
272
|
+
}
|
|
273
|
+
for (const key of toDelete) this.queryStore.delete(key);
|
|
274
|
+
return toDelete.length;
|
|
275
|
+
}
|
|
276
|
+
get stats() {
|
|
277
|
+
const total = this._hits + this._misses + this._stales;
|
|
278
|
+
return {
|
|
279
|
+
hits: this._hits,
|
|
280
|
+
misses: this._misses,
|
|
281
|
+
stales: this._stales,
|
|
282
|
+
hitRate: total === 0 ? Number.NaN : this._hits / total,
|
|
283
|
+
entitiesCount: this.entityStore.size
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
resetStats() {
|
|
287
|
+
this._hits = 0;
|
|
288
|
+
this._misses = 0;
|
|
289
|
+
this._stales = 0;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
125
293
|
// src/cache/index.ts
|
|
126
294
|
var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
|
|
127
295
|
var MemoryCache = class {
|
|
@@ -149,7 +317,10 @@ var MemoryCache = class {
|
|
|
149
317
|
* With stale-while-revalidate enabled, returns stale data within the grace window
|
|
150
318
|
* and flags it so the caller can refresh in the background.
|
|
151
319
|
*/
|
|
152
|
-
|
|
320
|
+
/**
|
|
321
|
+
* Retrieve a cached value and its stale status.
|
|
322
|
+
*/
|
|
323
|
+
getWithMeta(key) {
|
|
153
324
|
if (!this.enabled) return void 0;
|
|
154
325
|
const entry = this.store.get(key);
|
|
155
326
|
if (!entry) {
|
|
@@ -162,7 +333,7 @@ var MemoryCache = class {
|
|
|
162
333
|
this.store.delete(key);
|
|
163
334
|
this.store.set(key, entry);
|
|
164
335
|
this._stales++;
|
|
165
|
-
return entry.data;
|
|
336
|
+
return { data: entry.data, stale: true };
|
|
166
337
|
}
|
|
167
338
|
this.store.delete(key);
|
|
168
339
|
this._misses++;
|
|
@@ -171,7 +342,11 @@ var MemoryCache = class {
|
|
|
171
342
|
this.store.delete(key);
|
|
172
343
|
this.store.set(key, entry);
|
|
173
344
|
this._hits++;
|
|
174
|
-
return entry.data;
|
|
345
|
+
return { data: entry.data, stale: false };
|
|
346
|
+
}
|
|
347
|
+
get(key) {
|
|
348
|
+
const res = this.getWithMeta(key);
|
|
349
|
+
return res ? res.data : void 0;
|
|
175
350
|
}
|
|
176
351
|
/** Store a value in the cache. */
|
|
177
352
|
set(key, data) {
|
|
@@ -357,6 +532,7 @@ var AniListError = class _AniListError extends Error {
|
|
|
357
532
|
|
|
358
533
|
// src/queries/fragments.ts
|
|
359
534
|
var MEDIA_FIELDS_LIGHT = `
|
|
535
|
+
__typename
|
|
360
536
|
id
|
|
361
537
|
idMal
|
|
362
538
|
title { romaji english native userPreferred }
|
|
@@ -383,6 +559,7 @@ var MEDIA_FIELDS_LIGHT = `
|
|
|
383
559
|
}
|
|
384
560
|
`;
|
|
385
561
|
var MEDIA_FIELDS_BASE = `
|
|
562
|
+
__typename
|
|
386
563
|
id
|
|
387
564
|
idMal
|
|
388
565
|
title { romaji english native userPreferred }
|
|
@@ -429,6 +606,7 @@ var RELATIONS_FIELDS = `
|
|
|
429
606
|
edges {
|
|
430
607
|
relationType(version: 2)
|
|
431
608
|
node {
|
|
609
|
+
__typename
|
|
432
610
|
id
|
|
433
611
|
title { romaji english native userPreferred }
|
|
434
612
|
type
|
|
@@ -459,9 +637,11 @@ var RELATIONS_FIELDS = `
|
|
|
459
637
|
}
|
|
460
638
|
`;
|
|
461
639
|
var MEDIA_RECOMMENDATION_FIELDS = `
|
|
640
|
+
__typename
|
|
462
641
|
id
|
|
463
642
|
rating
|
|
464
643
|
mediaRecommendation {
|
|
644
|
+
__typename
|
|
465
645
|
id
|
|
466
646
|
title { romaji english native userPreferred }
|
|
467
647
|
type
|
|
@@ -493,6 +673,7 @@ var MEDIA_FIELDS = `
|
|
|
493
673
|
${RELATIONS_FIELDS}
|
|
494
674
|
`;
|
|
495
675
|
var CHARACTER_FIELDS_COMPACT = `
|
|
676
|
+
__typename
|
|
496
677
|
id
|
|
497
678
|
name { first middle last full native alternative }
|
|
498
679
|
image { large medium }
|
|
@@ -507,6 +688,7 @@ var CHARACTER_FIELDS_COMPACT = `
|
|
|
507
688
|
var CHARACTER_MEDIA_NODES = `
|
|
508
689
|
media(perPage: 10) {
|
|
509
690
|
nodes {
|
|
691
|
+
__typename
|
|
510
692
|
id
|
|
511
693
|
title { romaji english native userPreferred }
|
|
512
694
|
type
|
|
@@ -516,6 +698,7 @@ var CHARACTER_MEDIA_NODES = `
|
|
|
516
698
|
}
|
|
517
699
|
`;
|
|
518
700
|
var VOICE_ACTOR_FIELDS_COMPACT = `
|
|
701
|
+
__typename
|
|
519
702
|
id
|
|
520
703
|
name { first middle last full native userPreferred }
|
|
521
704
|
languageV2
|
|
@@ -531,6 +714,7 @@ var CHARACTER_MEDIA_EDGES_WITH_VA = `
|
|
|
531
714
|
${VOICE_ACTOR_FIELDS_COMPACT}
|
|
532
715
|
}
|
|
533
716
|
node {
|
|
717
|
+
__typename
|
|
534
718
|
id
|
|
535
719
|
title { romaji english native userPreferred }
|
|
536
720
|
type
|
|
@@ -549,6 +733,7 @@ var CHARACTER_FIELDS_WITH_VA = `
|
|
|
549
733
|
${CHARACTER_MEDIA_EDGES_WITH_VA}
|
|
550
734
|
`;
|
|
551
735
|
var STAFF_FIELDS = `
|
|
736
|
+
__typename
|
|
552
737
|
id
|
|
553
738
|
name { first middle last full native }
|
|
554
739
|
language
|
|
@@ -568,6 +753,7 @@ var STAFF_FIELDS = `
|
|
|
568
753
|
var STAFF_MEDIA_FIELDS = `
|
|
569
754
|
staffMedia(perPage: $perPage, sort: [POPULARITY_DESC]) {
|
|
570
755
|
nodes {
|
|
756
|
+
__typename
|
|
571
757
|
id
|
|
572
758
|
title { romaji english native userPreferred }
|
|
573
759
|
type
|
|
@@ -606,6 +792,7 @@ var STAFF_MEDIA_FIELDS = `
|
|
|
606
792
|
}
|
|
607
793
|
`;
|
|
608
794
|
var USER_FIELDS = `
|
|
795
|
+
__typename
|
|
609
796
|
id
|
|
610
797
|
name
|
|
611
798
|
about(asHtml: false)
|
|
@@ -626,6 +813,7 @@ var USER_FAVORITES_FIELDS = `
|
|
|
626
813
|
favourites {
|
|
627
814
|
anime(perPage: 25) {
|
|
628
815
|
nodes {
|
|
816
|
+
__typename
|
|
629
817
|
id
|
|
630
818
|
title { romaji english native userPreferred }
|
|
631
819
|
coverImage { large medium }
|
|
@@ -636,6 +824,7 @@ var USER_FAVORITES_FIELDS = `
|
|
|
636
824
|
}
|
|
637
825
|
manga(perPage: 25) {
|
|
638
826
|
nodes {
|
|
827
|
+
__typename
|
|
639
828
|
id
|
|
640
829
|
title { romaji english native userPreferred }
|
|
641
830
|
coverImage { large medium }
|
|
@@ -646,6 +835,7 @@ var USER_FAVORITES_FIELDS = `
|
|
|
646
835
|
}
|
|
647
836
|
characters(perPage: 25) {
|
|
648
837
|
nodes {
|
|
838
|
+
__typename
|
|
649
839
|
id
|
|
650
840
|
name { full native }
|
|
651
841
|
image { large medium }
|
|
@@ -654,6 +844,7 @@ var USER_FAVORITES_FIELDS = `
|
|
|
654
844
|
}
|
|
655
845
|
staff(perPage: 25) {
|
|
656
846
|
nodes {
|
|
847
|
+
__typename
|
|
657
848
|
id
|
|
658
849
|
name { full native }
|
|
659
850
|
image { large medium }
|
|
@@ -662,6 +853,7 @@ var USER_FAVORITES_FIELDS = `
|
|
|
662
853
|
}
|
|
663
854
|
studios(perPage: 25) {
|
|
664
855
|
nodes {
|
|
856
|
+
__typename
|
|
665
857
|
id
|
|
666
858
|
name
|
|
667
859
|
siteUrl
|
|
@@ -670,6 +862,7 @@ var USER_FAVORITES_FIELDS = `
|
|
|
670
862
|
}
|
|
671
863
|
`;
|
|
672
864
|
var MEDIA_LIST_FIELDS = `
|
|
865
|
+
__typename
|
|
673
866
|
id
|
|
674
867
|
mediaId
|
|
675
868
|
status
|
|
@@ -689,6 +882,7 @@ var MEDIA_LIST_FIELDS = `
|
|
|
689
882
|
}
|
|
690
883
|
`;
|
|
691
884
|
var STUDIO_FIELDS = `
|
|
885
|
+
__typename
|
|
692
886
|
id
|
|
693
887
|
name
|
|
694
888
|
isAnimationStudio
|
|
@@ -697,6 +891,7 @@ var STUDIO_FIELDS = `
|
|
|
697
891
|
media(page: 1, perPage: 25, sort: POPULARITY_DESC) {
|
|
698
892
|
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
699
893
|
nodes {
|
|
894
|
+
__typename
|
|
700
895
|
id
|
|
701
896
|
title { romaji english native userPreferred }
|
|
702
897
|
type
|
|
@@ -707,6 +902,7 @@ var STUDIO_FIELDS = `
|
|
|
707
902
|
}
|
|
708
903
|
`;
|
|
709
904
|
var THREAD_FIELDS = `
|
|
905
|
+
__typename
|
|
710
906
|
id
|
|
711
907
|
title
|
|
712
908
|
body(asHtml: false)
|
|
@@ -723,20 +919,24 @@ var THREAD_FIELDS = `
|
|
|
723
919
|
updatedAt
|
|
724
920
|
siteUrl
|
|
725
921
|
user {
|
|
922
|
+
__typename
|
|
726
923
|
id
|
|
727
924
|
name
|
|
728
925
|
avatar { large medium }
|
|
729
926
|
}
|
|
730
927
|
replyUser {
|
|
928
|
+
__typename
|
|
731
929
|
id
|
|
732
930
|
name
|
|
733
931
|
avatar { large medium }
|
|
734
932
|
}
|
|
735
933
|
categories {
|
|
934
|
+
__typename
|
|
736
935
|
id
|
|
737
936
|
name
|
|
738
937
|
}
|
|
739
938
|
mediaCategories {
|
|
939
|
+
__typename
|
|
740
940
|
id
|
|
741
941
|
title { romaji english native userPreferred }
|
|
742
942
|
type
|
|
@@ -744,6 +944,7 @@ var THREAD_FIELDS = `
|
|
|
744
944
|
siteUrl
|
|
745
945
|
}
|
|
746
946
|
likes {
|
|
947
|
+
__typename
|
|
747
948
|
id
|
|
748
949
|
name
|
|
749
950
|
}
|
|
@@ -2121,7 +2322,7 @@ function mapFavorites(fav) {
|
|
|
2121
2322
|
|
|
2122
2323
|
// src/client/index.ts
|
|
2123
2324
|
var DEFAULT_API_URL = "https://graphql.anilist.co";
|
|
2124
|
-
var LIB_VERSION = "2.1.
|
|
2325
|
+
var LIB_VERSION = "2.1.2" ;
|
|
2125
2326
|
var AniListClient = class {
|
|
2126
2327
|
apiUrl;
|
|
2127
2328
|
headers;
|
|
@@ -2166,10 +2367,25 @@ var AniListClient = class {
|
|
|
2166
2367
|
/** @internal */
|
|
2167
2368
|
async request(query, variables = {}) {
|
|
2168
2369
|
const cacheKey = MemoryCache.key(query, variables);
|
|
2169
|
-
|
|
2370
|
+
let cached;
|
|
2371
|
+
let isStale = false;
|
|
2372
|
+
if (this.cacheAdapter.getWithMeta) {
|
|
2373
|
+
const res = await this.cacheAdapter.getWithMeta(cacheKey);
|
|
2374
|
+
if (res) {
|
|
2375
|
+
cached = res.data;
|
|
2376
|
+
isStale = res.stale;
|
|
2377
|
+
}
|
|
2378
|
+
} else {
|
|
2379
|
+
cached = await this.cacheAdapter.get(cacheKey);
|
|
2380
|
+
}
|
|
2170
2381
|
if (cached !== void 0) {
|
|
2382
|
+
if (isStale) {
|
|
2383
|
+
this.executeRequest(query, variables, cacheKey).catch((err) => {
|
|
2384
|
+
this.logger?.error("Background revalidation failed", { error: err.message, cacheKey });
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2171
2387
|
this.hooks.onCacheHit?.(cacheKey);
|
|
2172
|
-
this.logger?.debug("Cache hit", { cacheKey });
|
|
2388
|
+
this.logger?.debug("Cache hit", { cacheKey, isStale });
|
|
2173
2389
|
const meta = { durationMs: 0, fromCache: true };
|
|
2174
2390
|
this._lastRequestMeta = meta;
|
|
2175
2391
|
this.hooks.onResponse?.(query, 0, true);
|
|
@@ -2544,6 +2760,6 @@ var AniListClient = class {
|
|
|
2544
2760
|
}
|
|
2545
2761
|
};
|
|
2546
2762
|
|
|
2547
|
-
export { AiringSort, AniListClient, AniListError, CharacterRole, CharacterSort, MediaFormat, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaSource, MediaStatus, MediaType, MemoryCache, RateLimiter, RecommendationSort, RedisCache, ReviewSort, StaffSort, StudioSort, ThreadSort, UserSort, parseAniListMarkdown };
|
|
2763
|
+
export { AiringSort, AniListClient, AniListError, CharacterRole, CharacterSort, MediaFormat, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaSource, MediaStatus, MediaType, MemoryCache, NormalizedCache, RateLimiter, RecommendationSort, RedisCache, ReviewSort, StaffSort, StudioSort, ThreadSort, UserSort, parseAniListMarkdown };
|
|
2548
2764
|
//# sourceMappingURL=index.mjs.map
|
|
2549
2765
|
//# sourceMappingURL=index.mjs.map
|