@hkdigital/lib-sveltekit 0.1.98 → 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 +101 -112
- 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
|
-
|
345
|
+
if (!entry) {
|
346
|
+
resolve(null);
|
347
|
+
return;
|
348
|
+
}
|
349
|
+
|
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);
|
362
355
|
});
|
356
|
+
resolve(null);
|
357
|
+
return;
|
358
|
+
}
|
363
359
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
// );
|
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
|
+
});
|
369
364
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
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
|
+
// );
|
375
370
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
);
|
382
|
-
});
|
383
|
-
}
|
371
|
+
// Clone the entry for migration
|
372
|
+
const migratedEntry = {
|
373
|
+
...entry,
|
374
|
+
cacheVersion: this.cacheVersion
|
375
|
+
};
|
384
376
|
|
385
|
-
//
|
386
|
-
|
387
|
-
|
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
|
+
}
|
388
385
|
|
389
|
-
|
386
|
+
// Deserialize the response
|
387
|
+
try {
|
388
|
+
let responseHeaders = new Headers(entry.headers);
|
390
389
|
|
391
|
-
|
392
|
-
// Clone the blob to ensure it's not consumed
|
393
|
-
responseBody = entry.body.slice(0, entry.body.size, entry.body.type);
|
394
|
-
}
|
390
|
+
let responseBody = entry.body;
|
395
391
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
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
|
-
}
|
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
|
+
// }
|
417
396
|
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
lastModified: entry.lastModified,
|
426
|
-
cacheVersion: entry.cacheVersion
|
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
|
|