New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@serwist/expiration

Package Overview
Dependencies
Maintainers
1
Versions
54
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@serwist/expiration - npm Package Compare versions

Comparing version 9.0.0-preview.17 to 9.0.0-preview.18

7

dist/index.d.ts

@@ -1,6 +0,3 @@

import { CacheExpiration } from "./CacheExpiration.js";
import type { ExpirationPluginOptions } from "./ExpirationPlugin.js";
import { ExpirationPlugin } from "./ExpirationPlugin.js";
export { CacheExpiration, ExpirationPlugin };
export type { ExpirationPluginOptions };
export { CacheExpiration, ExpirationPlugin } from "@serwist/sw/plugins";
export type { ExpirationPluginOptions } from "@serwist/sw/plugins";
//# sourceMappingURL=index.d.ts.map

@@ -1,330 +0,1 @@

import { assert, SerwistError, logger, privateCacheNames, getFriendlyURL } from '@serwist/core/internal';
import { deleteDB, openDB } from 'idb';
import { registerQuotaErrorCallback } from '@serwist/core';
const DB_NAME = "serwist-expiration";
const CACHE_OBJECT_STORE = "cache-entries";
const normalizeURL = (unNormalizedUrl)=>{
const url = new URL(unNormalizedUrl, location.href);
url.hash = "";
return url.href;
};
class CacheTimestampsModel {
_cacheName;
_db = null;
constructor(cacheName){
this._cacheName = cacheName;
}
_getId(url) {
return `${this._cacheName}|${normalizeURL(url)}`;
}
_upgradeDb(db) {
const objStore = db.createObjectStore(CACHE_OBJECT_STORE, {
keyPath: "id"
});
objStore.createIndex("cacheName", "cacheName", {
unique: false
});
objStore.createIndex("timestamp", "timestamp", {
unique: false
});
}
_upgradeDbAndDeleteOldDbs(db) {
this._upgradeDb(db);
if (this._cacheName) {
void deleteDB(this._cacheName);
}
}
async setTimestamp(url, timestamp) {
url = normalizeURL(url);
const entry = {
id: this._getId(url),
cacheName: this._cacheName,
url,
timestamp
};
const db = await this.getDb();
const tx = db.transaction(CACHE_OBJECT_STORE, "readwrite", {
durability: "relaxed"
});
await tx.store.put(entry);
await tx.done;
}
async getTimestamp(url) {
const db = await this.getDb();
const entry = await db.get(CACHE_OBJECT_STORE, this._getId(url));
return entry?.timestamp;
}
async expireEntries(minTimestamp, maxCount) {
const db = await this.getDb();
let cursor = await db.transaction(CACHE_OBJECT_STORE, "readwrite").store.index("timestamp").openCursor(null, "prev");
const urlsDeleted = [];
let entriesNotDeletedCount = 0;
while(cursor){
const result = cursor.value;
if (result.cacheName === this._cacheName) {
if (minTimestamp && result.timestamp < minTimestamp || maxCount && entriesNotDeletedCount >= maxCount) {
cursor.delete();
urlsDeleted.push(result.url);
} else {
entriesNotDeletedCount++;
}
}
cursor = await cursor.continue();
}
return urlsDeleted;
}
async getDb() {
if (!this._db) {
this._db = await openDB(DB_NAME, 1, {
upgrade: this._upgradeDbAndDeleteOldDbs.bind(this)
});
}
return this._db;
}
}
class CacheExpiration {
_isRunning = false;
_rerunRequested = false;
_maxEntries;
_maxAgeSeconds;
_matchOptions;
_cacheName;
_timestampModel;
constructor(cacheName, config = {}){
if (process.env.NODE_ENV !== "production") {
assert.isType(cacheName, "string", {
moduleName: "@serwist/expiration",
className: "CacheExpiration",
funcName: "constructor",
paramName: "cacheName"
});
if (!(config.maxEntries || config.maxAgeSeconds)) {
throw new SerwistError("max-entries-or-age-required", {
moduleName: "@serwist/expiration",
className: "CacheExpiration",
funcName: "constructor"
});
}
if (config.maxEntries) {
assert.isType(config.maxEntries, "number", {
moduleName: "@serwist/expiration",
className: "CacheExpiration",
funcName: "constructor",
paramName: "config.maxEntries"
});
}
if (config.maxAgeSeconds) {
assert.isType(config.maxAgeSeconds, "number", {
moduleName: "@serwist/expiration",
className: "CacheExpiration",
funcName: "constructor",
paramName: "config.maxAgeSeconds"
});
}
}
this._maxEntries = config.maxEntries;
this._maxAgeSeconds = config.maxAgeSeconds;
this._matchOptions = config.matchOptions;
this._cacheName = cacheName;
this._timestampModel = new CacheTimestampsModel(cacheName);
}
async expireEntries() {
if (this._isRunning) {
this._rerunRequested = true;
return;
}
this._isRunning = true;
const minTimestamp = this._maxAgeSeconds ? Date.now() - this._maxAgeSeconds * 1000 : 0;
const urlsExpired = await this._timestampModel.expireEntries(minTimestamp, this._maxEntries);
const cache = await self.caches.open(this._cacheName);
for (const url of urlsExpired){
await cache.delete(url, this._matchOptions);
}
if (process.env.NODE_ENV !== "production") {
if (urlsExpired.length > 0) {
logger.groupCollapsed(`Expired ${urlsExpired.length} ` + `${urlsExpired.length === 1 ? "entry" : "entries"} and removed ` + `${urlsExpired.length === 1 ? "it" : "them"} from the ` + `'${this._cacheName}' cache.`);
logger.log(`Expired the following ${urlsExpired.length === 1 ? "URL" : "URLs"}:`);
for (const url of urlsExpired){
logger.log(` ${url}`);
}
logger.groupEnd();
} else {
logger.debug("Cache expiration ran and found no entries to remove.");
}
}
this._isRunning = false;
if (this._rerunRequested) {
this._rerunRequested = false;
void this.expireEntries();
}
}
async updateTimestamp(url) {
if (process.env.NODE_ENV !== "production") {
assert.isType(url, "string", {
moduleName: "@serwist/expiration",
className: "CacheExpiration",
funcName: "updateTimestamp",
paramName: "url"
});
}
await this._timestampModel.setTimestamp(url, Date.now());
}
async isURLExpired(url) {
if (!this._maxAgeSeconds) {
if (process.env.NODE_ENV !== "production") {
throw new SerwistError("expired-test-without-max-age", {
methodName: "isURLExpired",
paramName: "maxAgeSeconds"
});
}
return false;
}
const timestamp = await this._timestampModel.getTimestamp(url);
const expireOlderThan = Date.now() - this._maxAgeSeconds * 1000;
return timestamp !== undefined ? timestamp < expireOlderThan : true;
}
async delete() {
this._rerunRequested = false;
await this._timestampModel.expireEntries(Number.POSITIVE_INFINITY);
}
}
class ExpirationPlugin {
_config;
_cacheExpirations;
constructor(config = {}){
if (process.env.NODE_ENV !== "production") {
if (!(config.maxEntries || config.maxAgeSeconds)) {
throw new SerwistError("max-entries-or-age-required", {
moduleName: "@serwist/expiration",
className: "ExpirationPlugin",
funcName: "constructor"
});
}
if (config.maxEntries) {
assert.isType(config.maxEntries, "number", {
moduleName: "@serwist/expiration",
className: "ExpirationPlugin",
funcName: "constructor",
paramName: "config.maxEntries"
});
}
if (config.maxAgeSeconds) {
assert.isType(config.maxAgeSeconds, "number", {
moduleName: "@serwist/expiration",
className: "ExpirationPlugin",
funcName: "constructor",
paramName: "config.maxAgeSeconds"
});
}
if (config.maxAgeFrom) {
assert.isType(config.maxAgeFrom, "string", {
moduleName: "@serwist/expiration",
className: "ExpirationPlugin",
funcName: "constructor",
paramName: "config.maxAgeFrom"
});
}
}
this._config = config;
this._cacheExpirations = new Map();
if (!this._config.maxAgeFrom) {
this._config.maxAgeFrom = "last-fetched";
}
if (this._config.purgeOnQuotaError) {
registerQuotaErrorCallback(()=>this.deleteCacheAndMetadata());
}
}
_getCacheExpiration(cacheName) {
if (cacheName === privateCacheNames.getRuntimeName()) {
throw new SerwistError("expire-custom-caches-only");
}
let cacheExpiration = this._cacheExpirations.get(cacheName);
if (!cacheExpiration) {
cacheExpiration = new CacheExpiration(cacheName, this._config);
this._cacheExpirations.set(cacheName, cacheExpiration);
}
return cacheExpiration;
}
cachedResponseWillBeUsed({ event, cacheName, request, cachedResponse }) {
if (!cachedResponse) {
return null;
}
const isFresh = this._isResponseDateFresh(cachedResponse);
const cacheExpiration = this._getCacheExpiration(cacheName);
const isMaxAgeFromLastUsed = this._config.maxAgeFrom === "last-used";
const done = (async ()=>{
if (isMaxAgeFromLastUsed) {
await cacheExpiration.updateTimestamp(request.url);
}
await cacheExpiration.expireEntries();
})();
try {
event.waitUntil(done);
} catch (error) {
if (process.env.NODE_ENV !== "production") {
if (event instanceof FetchEvent) {
logger.warn(`Unable to ensure service worker stays alive when updating cache entry for '${getFriendlyURL(event.request.url)}'.`);
}
}
}
return isFresh ? cachedResponse : null;
}
_isResponseDateFresh(cachedResponse) {
const isMaxAgeFromLastUsed = this._config.maxAgeFrom === "last-used";
if (isMaxAgeFromLastUsed) {
return true;
}
const now = Date.now();
if (!this._config.maxAgeSeconds) {
return true;
}
const dateHeaderTimestamp = this._getDateHeaderTimestamp(cachedResponse);
if (dateHeaderTimestamp === null) {
return true;
}
return dateHeaderTimestamp >= now - this._config.maxAgeSeconds * 1000;
}
_getDateHeaderTimestamp(cachedResponse) {
if (!cachedResponse.headers.has("date")) {
return null;
}
const dateHeader = cachedResponse.headers.get("date");
const parsedDate = new Date(dateHeader);
const headerTime = parsedDate.getTime();
if (Number.isNaN(headerTime)) {
return null;
}
return headerTime;
}
async cacheDidUpdate({ cacheName, request }) {
if (process.env.NODE_ENV !== "production") {
assert.isType(cacheName, "string", {
moduleName: "@serwist/expiration",
className: "Plugin",
funcName: "cacheDidUpdate",
paramName: "cacheName"
});
assert.isInstance(request, Request, {
moduleName: "@serwist/expiration",
className: "Plugin",
funcName: "cacheDidUpdate",
paramName: "request"
});
}
const cacheExpiration = this._getCacheExpiration(cacheName);
await cacheExpiration.updateTimestamp(request.url);
await cacheExpiration.expireEntries();
}
async deleteCacheAndMetadata() {
for (const [cacheName, cacheExpiration] of this._cacheExpirations){
await self.caches.delete(cacheName);
await cacheExpiration.delete();
}
this._cacheExpirations = new Map();
}
}
export { CacheExpiration, ExpirationPlugin };
export { CacheExpiration, ExpirationPlugin } from '@serwist/sw/plugins';
{
"name": "@serwist/expiration",
"version": "9.0.0-preview.17",
"version": "9.0.0-preview.18",
"type": "module",

@@ -32,4 +32,3 @@ "description": "A module that expires cached responses based on age or maximum number of entries.",

"dependencies": {
"idb": "8.0.0",
"@serwist/core": "9.0.0-preview.17"
"@serwist/sw": "9.0.0-preview.18"
},

@@ -39,3 +38,3 @@ "devDependencies": {

"typescript": "5.5.0-dev.20240323",
"@serwist/constants": "9.0.0-preview.17"
"@serwist/constants": "9.0.0-preview.18"
},

@@ -42,0 +41,0 @@ "peerDependencies": {

@@ -1,15 +0,2 @@

/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
import { CacheExpiration } from "./CacheExpiration.js";
import type { ExpirationPluginOptions } from "./ExpirationPlugin.js";
import { ExpirationPlugin } from "./ExpirationPlugin.js";
export { CacheExpiration, ExpirationPlugin };
export type { ExpirationPluginOptions };
export { CacheExpiration, ExpirationPlugin } from "@serwist/sw/plugins";
export type { ExpirationPluginOptions } from "@serwist/sw/plugins";

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc