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/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 Object.keys(obj).sort()) {
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 [k, v] of Object.entries(obj)) {
162
- normalizedObj[k] = this.normalize(v, seen);
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 [k, v] of Object.entries(obj)) {
170
- result[k] = this.normalize(v, seen);
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 [k, v] of Object.entries(obj)) {
197
- const denormalized = this.denormalize(v, seen);
198
- if (denormalized === void 0) return void 0;
199
- result[k] = denormalized;
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.queryStore.delete(key);
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.queryStore.delete(key);
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 normalizedData = this.normalize(data);
245
- this.queryStore.delete(key);
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.delete(firstKey);
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
- return this.queryStore.delete(key);
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) this.queryStore.delete(key);
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.2.1" ;
2532
+ var LIB_VERSION = "2.3.0" ;
2543
2533
  var AniListClient = class {
2544
2534
  apiUrl;
2545
2535
  headers;