@hkdigital/lib-sveltekit 0.1.99 → 0.1.100
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/classes/cache/IndexedDbCache.js +102 -113
- package/package.json +1 -1
@@ -32,6 +32,8 @@
|
|
32
32
|
* }
|
33
33
|
*/
|
34
34
|
|
35
|
+
import { HkPromise } from '../promise';
|
36
|
+
|
35
37
|
/** @typedef {import('./typedef').CacheEntry} CacheEntry */
|
36
38
|
|
37
39
|
/** @typedef {import('./typedef').IDBRequestEvent} IDBRequestEvent */
|
@@ -48,9 +50,6 @@ const DEFAULT_CLEANUP_INTERVAL = 5 * 60 * 1000; // 5 minutes;
|
|
48
50
|
|
49
51
|
const DEFAULT_CLEANUP_POSTPONE_MS = 5000; // 5 seconds
|
50
52
|
|
51
|
-
// Add logging to track concurrent access
|
52
|
-
const concurrentReadsByKey = new Map();
|
53
|
-
|
54
53
|
/**
|
55
54
|
* IndexedDbCache with automatic background cleanup
|
56
55
|
*/
|
@@ -323,138 +322,128 @@ export default class IndexedDbCache {
|
|
323
322
|
* @returns {Promise<CacheEntry|null>} Cache entry or null if not found/expired
|
324
323
|
*/
|
325
324
|
async get(key) {
|
326
|
-
// Track concurrent reads per key
|
327
|
-
const current = concurrentReadsByKey.get(key) || 0;
|
328
|
-
concurrentReadsByKey.set(key, current + 1);
|
329
|
-
// console.log(`Concurrent reads for ${key}: ${current + 1}`);
|
330
|
-
|
331
325
|
try {
|
332
326
|
const db = await this.dbPromise;
|
333
327
|
|
334
|
-
|
335
|
-
|
336
|
-
const transaction = db.transaction(this.storeName, 'readonly');
|
337
|
-
const store = transaction.objectStore(this.storeName);
|
338
|
-
const request = store.get(key);
|
328
|
+
let resolve;
|
329
|
+
let reject;
|
339
330
|
|
340
|
-
|
341
|
-
|
342
|
-
|
331
|
+
let promise = new Promise((_resolve, _reject) => {
|
332
|
+
resolve = _resolve;
|
333
|
+
reject = _reject;
|
334
|
+
});
|
343
335
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
336
|
+
try {
|
337
|
+
const transaction = db.transaction(this.storeName, 'readonly');
|
338
|
+
const store = transaction.objectStore(this.storeName);
|
339
|
+
const request = store.get(key);
|
348
340
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
this._deleteEntry(key).catch((err) => {
|
353
|
-
console.error('Failed to delete expired entry:', err);
|
354
|
-
});
|
355
|
-
resolve(null);
|
356
|
-
return;
|
357
|
-
}
|
341
|
+
request.onerror = () => reject(request.error);
|
342
|
+
request.onsuccess = async () => {
|
343
|
+
const entry = request.result;
|
358
344
|
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
345
|
+
if (!entry) {
|
346
|
+
resolve(null);
|
347
|
+
return;
|
348
|
+
}
|
363
349
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
);
|
350
|
+
// Check if expired
|
351
|
+
if (entry.expires && Date.now() > entry.expires) {
|
352
|
+
// Delete expired entry (but don't block)
|
353
|
+
this._deleteEntry(key).catch((err) => {
|
354
|
+
console.error('Failed to delete expired entry:', err);
|
355
|
+
});
|
356
|
+
resolve(null);
|
357
|
+
return;
|
358
|
+
}
|
369
359
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
};
|
360
|
+
// Update access timestamp (should finish before result is returned)
|
361
|
+
await this._updateAccessTime(key).catch((err) => {
|
362
|
+
console.error('Failed to update access time:', err);
|
363
|
+
});
|
375
364
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
);
|
382
|
-
});
|
383
|
-
}
|
365
|
+
// Check if from a different cache version
|
366
|
+
if (entry.cacheVersion !== this.cacheVersion) {
|
367
|
+
// console.log(
|
368
|
+
// `Migrating entry ${key} from version ${entry.cacheVersion} to ${this.cacheVersion}`
|
369
|
+
// );
|
384
370
|
|
385
|
-
//
|
386
|
-
|
387
|
-
|
371
|
+
// Clone the entry for migration
|
372
|
+
const migratedEntry = {
|
373
|
+
...entry,
|
374
|
+
cacheVersion: this.cacheVersion
|
375
|
+
};
|
388
376
|
|
389
|
-
|
377
|
+
// Store the migrated entry (don't block)
|
378
|
+
this._updateEntry(migratedEntry).catch((err) => {
|
379
|
+
console.error(
|
380
|
+
'Failed to migrate entry to current cache version:',
|
381
|
+
err
|
382
|
+
);
|
383
|
+
});
|
384
|
+
}
|
390
385
|
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
}
|
386
|
+
// Deserialize the response
|
387
|
+
try {
|
388
|
+
let responseHeaders = new Headers(entry.headers);
|
395
389
|
|
396
|
-
|
397
|
-
let response;
|
398
|
-
try {
|
399
|
-
response = new Response(responseBody, {
|
400
|
-
status: entry.status,
|
401
|
-
statusText: entry.statusText,
|
402
|
-
headers: responseHeaders
|
403
|
-
});
|
404
|
-
} catch (err) {
|
405
|
-
// Simplified mock response for test environments
|
406
|
-
response = /** @type {Response} */ ({
|
407
|
-
status: entry.status,
|
408
|
-
statusText: entry.statusText,
|
409
|
-
headers: responseHeaders,
|
410
|
-
body: entry.body,
|
411
|
-
url: entry.url,
|
412
|
-
clone() {
|
413
|
-
return this;
|
414
|
-
}
|
415
|
-
});
|
416
|
-
}
|
390
|
+
let responseBody = entry.body;
|
417
391
|
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
392
|
+
// if (responseBody instanceof Blob) {
|
393
|
+
// // Clone the blob to ensure it's not consumed
|
394
|
+
// responseBody = entry.body.slice(0, entry.body.size, entry.body.type);
|
395
|
+
// }
|
396
|
+
|
397
|
+
// Create Response safely
|
398
|
+
let response;
|
399
|
+
try {
|
400
|
+
response = new Response(responseBody, {
|
401
|
+
status: entry.status,
|
402
|
+
statusText: entry.statusText,
|
403
|
+
headers: responseHeaders
|
427
404
|
});
|
405
|
+
// eslint-disable-next-line no-unused-vars
|
428
406
|
} catch (err) {
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
407
|
+
// Simplified mock response for test environments
|
408
|
+
response = /** @type {Response} */ ({
|
409
|
+
status: entry.status,
|
410
|
+
statusText: entry.statusText,
|
411
|
+
headers: responseHeaders,
|
412
|
+
body: entry.body,
|
413
|
+
url: entry.url,
|
414
|
+
clone() {
|
415
|
+
return this;
|
416
|
+
}
|
417
|
+
});
|
434
418
|
}
|
435
|
-
};
|
436
|
-
} catch (err) {
|
437
|
-
console.error('Error in get transaction:', err);
|
438
|
-
resolve(null);
|
439
|
-
}
|
440
|
-
});
|
441
419
|
|
442
|
-
|
420
|
+
resolve({
|
421
|
+
response,
|
422
|
+
metadata: entry.metadata,
|
423
|
+
url: entry.url,
|
424
|
+
timestamp: entry.timestamp,
|
425
|
+
expires: entry.expires,
|
426
|
+
etag: entry.etag,
|
427
|
+
lastModified: entry.lastModified,
|
428
|
+
cacheVersion: entry.cacheVersion
|
429
|
+
});
|
430
|
+
} catch (err) {
|
431
|
+
console.error('Failed to deserialize cached response:', err);
|
432
|
+
|
433
|
+
// Delete corrupted entry
|
434
|
+
this._deleteEntry(key).catch(console.error);
|
435
|
+
resolve(null);
|
436
|
+
}
|
437
|
+
};
|
438
|
+
} catch (err) {
|
439
|
+
console.error('Error in get transaction:', err);
|
440
|
+
return null;
|
441
|
+
}
|
442
|
+
|
443
|
+
return promise;
|
443
444
|
} catch (err) {
|
444
445
|
console.error('Cache get error:', err);
|
445
446
|
return null;
|
446
|
-
} finally {
|
447
|
-
// Always decrement
|
448
|
-
const current = concurrentReadsByKey.get(key) || 1;
|
449
|
-
if (current <= 1) {
|
450
|
-
concurrentReadsByKey.delete(key);
|
451
|
-
} else {
|
452
|
-
concurrentReadsByKey.set(key, current - 1);
|
453
|
-
}
|
454
|
-
|
455
|
-
// console.debug(
|
456
|
-
// `Concurrent reads for ${key} after decrement: ${current - 1}`
|
457
|
-
// );
|
458
447
|
}
|
459
448
|
}
|
460
449
|
|