ani-client 2.2.1 → 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/README.md +7 -190
- package/dist/index.d.mts +2 -7
- package/dist/index.d.ts +2 -7
- package/dist/index.js +63 -73
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +63 -73
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -115,8 +115,17 @@ function validateIds(ids, label = "id") {
|
|
|
115
115
|
function sortObjectKeys(obj) {
|
|
116
116
|
if (obj === null || typeof obj !== "object") return obj;
|
|
117
117
|
if (Array.isArray(obj)) return obj.map(sortObjectKeys);
|
|
118
|
+
const keys = Object.keys(obj);
|
|
119
|
+
if (keys.length === 0) return obj;
|
|
120
|
+
if (keys.length === 1) {
|
|
121
|
+
const key = keys[0];
|
|
122
|
+
const val = obj[key];
|
|
123
|
+
const sortedVal = sortObjectKeys(val);
|
|
124
|
+
if (val === sortedVal) return obj;
|
|
125
|
+
return { [key]: sortedVal };
|
|
126
|
+
}
|
|
118
127
|
const sorted = {};
|
|
119
|
-
for (const key of
|
|
128
|
+
for (const key of keys.sort()) {
|
|
120
129
|
sorted[key] = sortObjectKeys(obj[key]);
|
|
121
130
|
}
|
|
122
131
|
return sorted;
|
|
@@ -130,10 +139,10 @@ var NormalizedCache = class {
|
|
|
130
139
|
swrMs;
|
|
131
140
|
queryStore = /* @__PURE__ */ new Map();
|
|
132
141
|
entityStore = /* @__PURE__ */ new Map();
|
|
142
|
+
refCount = /* @__PURE__ */ new Map();
|
|
133
143
|
_hits = 0;
|
|
134
144
|
_misses = 0;
|
|
135
145
|
_stales = 0;
|
|
136
|
-
gcTimeout;
|
|
137
146
|
constructor(options = {}) {
|
|
138
147
|
this.ttl = options.ttl ?? 24 * 60 * 60 * 1e3;
|
|
139
148
|
this.maxSize = options.maxSize ?? 500;
|
|
@@ -145,11 +154,11 @@ var NormalizedCache = class {
|
|
|
145
154
|
return `${normalized}|${JSON.stringify(sortObjectKeys(variables))}`;
|
|
146
155
|
}
|
|
147
156
|
/** Normalizes a GraphQL response, extracting entities and returning a tree of references. */
|
|
148
|
-
normalize(data, seen = /* @__PURE__ */ new WeakSet()) {
|
|
157
|
+
normalize(data, refsOut, seen = /* @__PURE__ */ new WeakSet()) {
|
|
149
158
|
if (Array.isArray(data)) {
|
|
150
159
|
if (seen.has(data)) return null;
|
|
151
160
|
seen.add(data);
|
|
152
|
-
return data.map((item) => this.normalize(item, seen));
|
|
161
|
+
return data.map((item) => this.normalize(item, refsOut, seen));
|
|
153
162
|
}
|
|
154
163
|
if (data !== null && typeof data === "object") {
|
|
155
164
|
if (seen.has(data)) return null;
|
|
@@ -157,17 +166,22 @@ var NormalizedCache = class {
|
|
|
157
166
|
const obj = data;
|
|
158
167
|
if (typeof obj.__typename === "string" && (typeof obj.id === "number" || typeof obj.id === "string")) {
|
|
159
168
|
const ref = `${obj.__typename}:${obj.id}`;
|
|
169
|
+
refsOut.add(ref);
|
|
160
170
|
const normalizedObj = {};
|
|
161
|
-
for (const
|
|
162
|
-
|
|
171
|
+
for (const k in obj) {
|
|
172
|
+
if (Object.hasOwn(obj, k)) {
|
|
173
|
+
normalizedObj[k] = this.normalize(obj[k], refsOut, seen);
|
|
174
|
+
}
|
|
163
175
|
}
|
|
164
176
|
const existing = this.entityStore.get(ref) || {};
|
|
165
177
|
this.entityStore.set(ref, { ...existing, ...normalizedObj });
|
|
166
178
|
return { __ref: ref };
|
|
167
179
|
}
|
|
168
180
|
const result = {};
|
|
169
|
-
for (const
|
|
170
|
-
|
|
181
|
+
for (const k in obj) {
|
|
182
|
+
if (Object.hasOwn(obj, k)) {
|
|
183
|
+
result[k] = this.normalize(obj[k], refsOut, seen);
|
|
184
|
+
}
|
|
171
185
|
}
|
|
172
186
|
return result;
|
|
173
187
|
}
|
|
@@ -193,10 +207,12 @@ var NormalizedCache = class {
|
|
|
193
207
|
return result2;
|
|
194
208
|
}
|
|
195
209
|
const result = {};
|
|
196
|
-
for (const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
210
|
+
for (const k in obj) {
|
|
211
|
+
if (Object.hasOwn(obj, k)) {
|
|
212
|
+
const denormalized = this.denormalize(obj[k], seen);
|
|
213
|
+
if (denormalized === void 0) return void 0;
|
|
214
|
+
result[k] = denormalized;
|
|
215
|
+
}
|
|
200
216
|
}
|
|
201
217
|
return result;
|
|
202
218
|
}
|
|
@@ -215,14 +231,14 @@ var NormalizedCache = class {
|
|
|
215
231
|
if (this.swrMs > 0 && now <= entry.expiresAt + this.swrMs) {
|
|
216
232
|
isStale = true;
|
|
217
233
|
} else {
|
|
218
|
-
this.
|
|
234
|
+
this.deleteQueryEntry(key, entry);
|
|
219
235
|
this._misses++;
|
|
220
236
|
return void 0;
|
|
221
237
|
}
|
|
222
238
|
}
|
|
223
239
|
const denormalized = this.denormalize(entry.data);
|
|
224
240
|
if (denormalized === void 0) {
|
|
225
|
-
this.
|
|
241
|
+
this.deleteQueryEntry(key, entry);
|
|
226
242
|
this._misses++;
|
|
227
243
|
return void 0;
|
|
228
244
|
}
|
|
@@ -241,27 +257,46 @@ var NormalizedCache = class {
|
|
|
241
257
|
}
|
|
242
258
|
set(key, data) {
|
|
243
259
|
if (!this.enabled) return;
|
|
244
|
-
const
|
|
245
|
-
this.
|
|
260
|
+
const refs = /* @__PURE__ */ new Set();
|
|
261
|
+
const normalizedData = this.normalize(data, refs);
|
|
262
|
+
const existing = this.queryStore.get(key);
|
|
263
|
+
if (existing) {
|
|
264
|
+
this.deleteQueryEntry(key, existing);
|
|
265
|
+
}
|
|
246
266
|
if (this.maxSize > 0 && this.queryStore.size >= this.maxSize) {
|
|
247
267
|
const firstKey = this.queryStore.keys().next().value;
|
|
248
268
|
if (firstKey !== void 0) {
|
|
249
|
-
this.queryStore.
|
|
269
|
+
const firstEntry = this.queryStore.get(firstKey);
|
|
270
|
+
if (firstEntry) this.deleteQueryEntry(firstKey, firstEntry);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
for (const ref of refs) {
|
|
274
|
+
this.refCount.set(ref, (this.refCount.get(ref) ?? 0) + 1);
|
|
275
|
+
}
|
|
276
|
+
this.queryStore.set(key, { data: normalizedData, refs, expiresAt: Date.now() + this.ttl });
|
|
277
|
+
}
|
|
278
|
+
deleteQueryEntry(key, entry) {
|
|
279
|
+
this.queryStore.delete(key);
|
|
280
|
+
for (const ref of entry.refs) {
|
|
281
|
+
const count = (this.refCount.get(ref) ?? 0) - 1;
|
|
282
|
+
if (count <= 0) {
|
|
283
|
+
this.refCount.delete(ref);
|
|
284
|
+
this.entityStore.delete(ref);
|
|
285
|
+
} else {
|
|
286
|
+
this.refCount.set(ref, count);
|
|
250
287
|
}
|
|
251
|
-
this.scheduleGc();
|
|
252
288
|
}
|
|
253
|
-
this.queryStore.set(key, { data: normalizedData, expiresAt: Date.now() + this.ttl });
|
|
254
289
|
}
|
|
255
290
|
delete(key) {
|
|
256
|
-
|
|
291
|
+
const entry = this.queryStore.get(key);
|
|
292
|
+
if (!entry) return false;
|
|
293
|
+
this.deleteQueryEntry(key, entry);
|
|
294
|
+
return true;
|
|
257
295
|
}
|
|
258
296
|
clear() {
|
|
259
|
-
if (this.gcTimeout) {
|
|
260
|
-
clearTimeout(this.gcTimeout);
|
|
261
|
-
this.gcTimeout = void 0;
|
|
262
|
-
}
|
|
263
297
|
this.queryStore.clear();
|
|
264
298
|
this.entityStore.clear();
|
|
299
|
+
this.refCount.clear();
|
|
265
300
|
this._hits = 0;
|
|
266
301
|
this._misses = 0;
|
|
267
302
|
this._stales = 0;
|
|
@@ -278,7 +313,9 @@ var NormalizedCache = class {
|
|
|
278
313
|
for (const key of this.queryStore.keys()) {
|
|
279
314
|
if (test(key)) toDelete.push(key);
|
|
280
315
|
}
|
|
281
|
-
for (const key of toDelete)
|
|
316
|
+
for (const key of toDelete) {
|
|
317
|
+
this.delete(key);
|
|
318
|
+
}
|
|
282
319
|
return toDelete.length;
|
|
283
320
|
}
|
|
284
321
|
get stats() {
|
|
@@ -296,53 +333,6 @@ var NormalizedCache = class {
|
|
|
296
333
|
this._misses = 0;
|
|
297
334
|
this._stales = 0;
|
|
298
335
|
}
|
|
299
|
-
scheduleGc() {
|
|
300
|
-
if (this.gcTimeout) return;
|
|
301
|
-
this.gcTimeout = setTimeout(() => {
|
|
302
|
-
this.gc();
|
|
303
|
-
this.gcTimeout = void 0;
|
|
304
|
-
}, 500);
|
|
305
|
-
if (typeof this.gcTimeout.unref === "function") {
|
|
306
|
-
this.gcTimeout.unref();
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Garbage-collect orphaned entities that are no longer referenced by any query.
|
|
311
|
-
* Called automatically on LRU eviction to prevent unbounded entity store growth.
|
|
312
|
-
*/
|
|
313
|
-
gc() {
|
|
314
|
-
const referencedRefs = /* @__PURE__ */ new Set();
|
|
315
|
-
const collectRefs = (data) => {
|
|
316
|
-
if (Array.isArray(data)) {
|
|
317
|
-
for (const item of data) collectRefs(item);
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
if (data !== null && typeof data === "object") {
|
|
321
|
-
const obj = data;
|
|
322
|
-
if (typeof obj.__ref === "string") {
|
|
323
|
-
referencedRefs.add(obj.__ref);
|
|
324
|
-
const entity = this.entityStore.get(obj.__ref);
|
|
325
|
-
if (entity && !referencedRefs.has(`_visited:${obj.__ref}`)) {
|
|
326
|
-
referencedRefs.add(`_visited:${obj.__ref}`);
|
|
327
|
-
collectRefs(entity);
|
|
328
|
-
}
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
for (const v of Object.values(obj)) collectRefs(v);
|
|
332
|
-
}
|
|
333
|
-
};
|
|
334
|
-
for (const entry of this.queryStore.values()) {
|
|
335
|
-
collectRefs(entry.data);
|
|
336
|
-
}
|
|
337
|
-
let removed = 0;
|
|
338
|
-
for (const ref of this.entityStore.keys()) {
|
|
339
|
-
if (!referencedRefs.has(ref)) {
|
|
340
|
-
this.entityStore.delete(ref);
|
|
341
|
-
removed++;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return removed;
|
|
345
|
-
}
|
|
346
336
|
};
|
|
347
337
|
|
|
348
338
|
// src/cache/index.ts
|
|
@@ -2539,7 +2529,7 @@ function mapFavorites(fav) {
|
|
|
2539
2529
|
|
|
2540
2530
|
// src/client/index.ts
|
|
2541
2531
|
var DEFAULT_API_URL = "https://graphql.anilist.co";
|
|
2542
|
-
var LIB_VERSION = "2.
|
|
2532
|
+
var LIB_VERSION = "2.3.0" ;
|
|
2543
2533
|
var AniListClient = class {
|
|
2544
2534
|
apiUrl;
|
|
2545
2535
|
headers;
|