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

@serwist/strategies

Package Overview
Dependencies
Maintainers
1
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@serwist/strategies - npm Package Compare versions

Comparing version 9.0.0-preview.0 to 9.0.0-preview.1

430

dist/index.js

@@ -6,28 +6,7 @@ import { assert, Deferred, logger, getFriendlyURL, SerwistError, timeout, cacheMatchIgnoreParams, executeQuotaErrorCallbacks, privateCacheNames } from '@serwist/core/internal';

}
/**
* A class created every time a Strategy instance instance calls `Strategy.handle` or
* `Strategy.handleAll` that wraps all fetch and cache actions around plugin callbacks
* and keeps track of when the strategy is "done" (i.e. all added `event.waitUntil()` promises
* have resolved).
*/ class StrategyHandler {
/**
* The request the strategy is performing (passed to the strategy's
* `handle()` or `handleAll()` method).
*/ request;
/**
* A `URL` instance of `request.url` (if passed to the strategy's
* `handle()` or `handleAll()` method).
* Note: the `url` param will be present if the strategy was invoked
* from a `@serwist/routing.Route` object.
*/ url;
/**
* The event associated with this request.
*/ event;
/**
* A `param` value (if passed to the strategy's
* `handle()` or `handleAll()` method).
* Note: the `param` param will be present if the strategy was invoked
* from a `@serwist/routing.Route` object and the `@serwist/strategies.matchCallback`
* returned a truthy value (it will be that value).
*/ params;
class StrategyHandler {
request;
url;
event;
params;
_cacheKeys = {};

@@ -39,12 +18,3 @@ _strategy;

_pluginStateMap;
/**
* Creates a new instance associated with the passed strategy and event
* that's handling the request.
*
* The constructor also initializes the state that will be passed to each of
* the plugins handling this request.
*
* @param strategy
* @param options
*/ constructor(strategy, options){
constructor(strategy, options){
if (process.env.NODE_ENV !== "production") {

@@ -63,4 +33,2 @@ assert.isInstance(options.event, ExtendableEvent, {

this._extendLifetimePromises = [];
// Copy the plugins list (since it's mutable on the strategy),
// so any mutations don't affect this handler instance.
this._plugins = [

@@ -75,15 +43,3 @@ ...strategy.plugins

}
/**
* Fetches a given request (and invokes any applicable plugin callback
* methods) using the `fetchOptions` (for non-navigation requests) and
* `plugins` defined on the `Strategy` object.
*
* The following plugin lifecycle methods are invoked when using this method:
* - `requestWillFetch()`
* - `fetchDidSucceed()`
* - `fetchDidFail()`
*
* @param input The URL or request to fetch.
* @returns
*/ async fetch(input) {
async fetch(input) {
const { event } = this;

@@ -100,5 +56,2 @@ let request = toRequest(input);

}
// If there is a fetchDidFail plugin, we need to save a clone of the
// original request before it's either modified by a requestWillFetch
// plugin or before the original request's body is consumed via fetch().
const originalRequest = this.hasCallback("fetchDidFail") ? request.clone() : null;

@@ -119,9 +72,5 @@ try {

}
// The request can be altered by plugins with `requestWillFetch` making
// the original request (most likely from a `fetch` event) different
// from the Request we make. Pass both to `fetchDidFail` to aid debugging.
const pluginFilteredRequest = request.clone();
try {
let fetchResponse;
// See https://github.com/GoogleChrome/workbox/issues/1796
fetchResponse = await fetch(request, request.mode === "navigate" ? undefined : this._strategy.fetchOptions);

@@ -143,4 +92,2 @@ if (process.env.NODE_ENV !== "production") {

}
// `originalRequest` will only exist if a `fetchDidFail` callback
// is being used (see above).
if (originalRequest) {

@@ -157,12 +104,3 @@ await this.runCallbacks("fetchDidFail", {

}
/**
* Calls `this.fetch()` and (in the background) runs `this.cachePut()` on
* the response generated by `this.fetch()`.
*
* The call to `this.cachePut()` automatically invokes `this.waitUntil()`,
* so you do not have to manually call `waitUntil()` on the event.
*
* @param input The request or URL to fetch and cache.
* @returns
*/ async fetchAndCachePut(input) {
async fetchAndCachePut(input) {
const response = await this.fetch(input);

@@ -173,14 +111,3 @@ const responseClone = response.clone();

}
/**
* Matches a request from the cache (and invokes any applicable plugin
* callback methods) using the `cacheName`, `matchOptions`, and `plugins`
* defined on the strategy object.
*
* The following plugin lifecycle methods are invoked when using this method:
* - cacheKeyWillByUsed()
* - cachedResponseWillByUsed()
*
* @param key The Request or URL to use as the cache key.
* @returns A matching response, if found.
*/ async cacheMatch(key) {
async cacheMatch(key) {
const request = toRequest(key);

@@ -215,20 +142,4 @@ let cachedResponse;

}
/**
* Puts a request/response pair in the cache (and invokes any applicable
* plugin callback methods) using the `cacheName` and `plugins` defined on
* the strategy object.
*
* The following plugin lifecycle methods are invoked when using this method:
* - cacheKeyWillByUsed()
* - cacheWillUpdate()
* - cacheDidUpdate()
*
* @param key The request or URL to use as the cache key.
* @param response The response to cache.
* @returns `false` if a cacheWillUpdate caused the response
* not be cached, and `true` otherwise.
*/ async cachePut(key, response) {
async cachePut(key, response) {
const request = toRequest(key);
// Run in the next task to avoid blocking other cache reads.
// https://github.com/w3c/ServiceWorker/issues/1397
await timeout(0);

@@ -243,3 +154,2 @@ const effectiveRequest = await this.getCacheKey(request, "write");

}
// See https://github.com/GoogleChrome/workbox/issues/2818
const vary = response.headers.get("Vary");

@@ -268,6 +178,3 @@ if (vary) {

const hasCacheUpdateCallback = this.hasCallback("cacheDidUpdate");
const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(// TODO(philipwalton): the `__WB_REVISION__` param is a precaching
// feature. Consider into ways to only add this behavior if using
// precaching.
cache, effectiveRequest.clone(), [
const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(cache, effectiveRequest.clone(), [
"__WB_REVISION__"

@@ -282,3 +189,2 @@ ], matchOptions) : null;

if (error instanceof Error) {
// See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError
if (error.name === "QuotaExceededError") {

@@ -301,13 +207,3 @@ await executeQuotaErrorCallbacks();

}
/**
* Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and
* executes any of those callbacks found in sequence. The final `Request`
* object returned by the last plugin is treated as the cache key for cache
* reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have
* been registered, the passed request is returned unmodified
*
* @param request
* @param mode
* @returns
*/ async getCacheKey(request, mode) {
async getCacheKey(request, mode) {
const key = `${request.url} | ${mode}`;

@@ -321,3 +217,2 @@ if (!this._cacheKeys[key]) {

event: this.event,
// params has a type any can't change right now.
params: this.params

@@ -330,9 +225,3 @@ }));

}
/**
* Returns true if the strategy has at least one plugin with the given
* callback.
*
* @param name The name of the callback to check for.
* @returns
*/ hasCallback(name) {
hasCallback(name) {
for (const plugin of this._strategy.plugins){

@@ -345,30 +234,8 @@ if (name in plugin) {

}
/**
* Runs all plugin callbacks matching the given name, in order, passing the
* given param object (merged ith the current plugin state) as the only
* argument.
*
* Note: since this method runs all plugins, it's not suitable for cases
* where the return value of a callback needs to be applied prior to calling
* the next callback. See `@serwist/strategies.iterateCallbacks` for how to handle that case.
*
* @param name The name of the callback to run within each plugin.
* @param param The object to pass as the first (and only) param when executing each callback. This object will be merged with the
* current plugin state prior to callback execution.
*/ async runCallbacks(name, param) {
async runCallbacks(name, param) {
for (const callback of this.iterateCallbacks(name)){
// TODO(philipwalton): not sure why `any` is needed. It seems like
// this should work with `as SerwistPluginCallbackParam[C]`.
await callback(param);
}
}
/**
* Accepts a callback and returns an iterable of matching plugin callbacks,
* where each callback is wrapped with the current handler state (i.e. when
* you call each callback, whatever object parameter you pass it will
* be merged with the plugin's current state).
*
* @param name The name fo the callback to run
* @returns
*/ *iterateCallbacks(name) {
*iterateCallbacks(name) {
for (const plugin of this._strategy.plugins){

@@ -382,4 +249,2 @@ if (typeof plugin[name] === "function") {

};
// TODO(philipwalton): not sure why `any` is needed. It seems like
// this should work with `as WorkboxPluginCallbackParam[C]`.
return plugin[name](statefulParam);

@@ -391,26 +256,7 @@ };

}
/**
* Adds a promise to the
* [extend lifetime promises](https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises)
* of the event event associated with the request being handled (usually a `FetchEvent`).
*
* Note: you can await
* `@serwist/strategies.StrategyHandler.doneWaiting`
* to know when all added promises have settled.
*
* @param promise A promise to add to the extend lifetime promises of
* the event that triggered the request.
*/ waitUntil(promise) {
waitUntil(promise) {
this._extendLifetimePromises.push(promise);
return promise;
}
/**
* Returns a promise that resolves once all promises passed to
* `@serwist/strategies.StrategyHandler.waitUntil` have settled.
*
* Note: any work done after `doneWaiting()` settles should be manually
* passed to an event's `waitUntil()` method (not this handler's
* `waitUntil()` method), otherwise the service worker thread my be killed
* prior to your work completing.
*/ async doneWaiting() {
async doneWaiting() {
let promise = undefined;

@@ -421,16 +267,6 @@ while(promise = this._extendLifetimePromises.shift()){

}
/**
* Stops running the strategy and immediately resolves any pending
* `waitUntil()` promises.
*/ destroy() {
destroy() {
this._handlerDeferred.resolve(null);
}
/**
* This method will call `cacheWillUpdate` on the available plugins (or use
* status === 200) to determine if the response is safe and valid to cache.
*
* @param response
* @returns
* @private
*/ async _ensureResponseSafeToCache(response) {
async _ensureResponseSafeToCache(response) {
let responseToCache = response;

@@ -469,8 +305,3 @@ let pluginsUsed = false;

/**
* Classes extending the `Strategy` based class should implement this method,
* and leverage `@serwist/strategies`'s `StrategyHandler` arg to perform all
* fetching and cache logic, which will ensure all relevant cache, cache options,
* fetch options and plugins are used (per the current strategy instance).
*/ class Strategy {
class Strategy {
cacheName;

@@ -480,12 +311,3 @@ plugins;

matchOptions;
/**
* Creates a new instance of the strategy and sets all documented option
* properties as public instance properties.
*
* Note: if a custom strategy class extends the base Strategy class and does
* not need more than these properties, it does not need to define its own
* constructor.
*
* @param options
*/ constructor(options = {}){
constructor(options = {}){
this.cacheName = privateCacheNames.getRuntimeName(options.cacheName);

@@ -496,37 +318,7 @@ this.plugins = options.plugins || [];

}
/**
* Perform a request strategy and returns a `Promise` that will resolve with
* a `Response`, invoking all relevant plugin callbacks.
*
* When a strategy instance is registered with a `@serwist/routing` Route, this method is automatically
* called when the route matches.
*
* Alternatively, this method can be used in a standalone `FetchEvent`
* listener by passing it to `event.respondWith()`.
*
* @param options A `FetchEvent` or an object with the properties listed below.
* @param options.request A request to run this strategy for.
* @param options.event The event associated with the request.
* @param options.url
* @param options.params
*/ handle(options) {
handle(options) {
const [responseDone] = this.handleAll(options);
return responseDone;
}
/**
* Similar to `@serwist/strategies`'s `Strategy.handle`, but
* instead of just returning a `Promise` that resolves to a `Response` it
* it will return an tuple of `[response, done]` promises, where the former
* (`response`) is equivalent to what `handle()` returns, and the latter is a
* Promise that will resolve once any promises that were added to
* `event.waitUntil()` as part of performing the strategy have completed.
*
* You can await the `done` promise to ensure any extra work performed by
* the strategy (usually caching responses) completes successfully.
*
* @param options A `FetchEvent` or `HandlerCallbackOptions` object.
* @returns A tuple of [response, done] promises that can be used to determine when the response resolves as
* well as when the handler has completed all its work.
*/ handleAll(options) {
// Allow for flexible options to be passed.
handleAll(options) {
if (options instanceof FetchEvent) {

@@ -548,3 +340,2 @@ options = {

const handlerDone = this._awaitComplete(responseDone, handler, request, event);
// Return an array of promises, suitable for use with Promise.all().
return [

@@ -563,5 +354,2 @@ responseDone,

response = await this._handle(request, handler);
// The "official" Strategy subclasses all throw this error automatically,
// but in case a third-party Strategy doesn't, ensure that we have a
// consistent failure when there's no response or an error response.
if (response === undefined || response.type === "error") {

@@ -606,7 +394,3 @@ throw new SerwistError("no-response", {

response = await responseDone;
} catch (error) {
// Ignore errors, as response errors should be caught via the `response`
// promise above. The `done` promise will only throw for errors in
// promises passed to `handler.waitUntil()`.
}
} catch (error) {}
try {

@@ -648,19 +432,4 @@ await handler.runCallbacks("handlerDidRespond", {

/**
* An implementation of a [cache first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-falling-back-to-network)
* request strategy.
*
* A cache first strategy is useful for assets that have been revisioned,
* such as URLs like `/styles/example.a8f5f1.css`, since they
* can be cached for long periods of time.
*
* If the network request fails, and there is no cache match, this will throw
* a `SerwistError` exception.
*/ class CacheFirst extends Strategy {
/**
* @private
* @param request A request to run this strategy for.
* @param handler The event that triggered the request.
* @returns
*/ async _handle(request, handler) {
class CacheFirst extends Strategy {
async _handle(request, handler) {
const logs = [];

@@ -718,16 +487,4 @@ if (process.env.NODE_ENV !== "production") {

/**
* An implementation of a [cache only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-only)
* request strategy.
*
* This class is useful if you want to take advantage of any Serwist plugin.
*
* If there is no cache match, this will throw a `SerwistError` exception.
*/ class CacheOnly extends Strategy {
/**
* @private
* @param request A request to run this strategy for.
* @param handler The event that triggered the request.
* @returns
*/ async _handle(request, handler) {
class CacheOnly extends Strategy {
async _handle(request, handler) {
if (process.env.NODE_ENV !== "production") {

@@ -761,17 +518,4 @@ assert.isInstance(request, Request, {

/*
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.
*/ const cacheOkAndOpaquePlugin = {
/**
* Returns a valid response (to allow caching) if the status is 200 (OK) or
* 0 (opaque).
*
* @param options
* @returns
* @private
*/ cacheWillUpdate: async ({ response })=>{
const cacheOkAndOpaquePlugin = {
cacheWillUpdate: async ({ response })=>{
if (response.status === 200 || response.status === 0) {

@@ -784,24 +528,6 @@ return response;

/**
* An implementation of a [network first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-first-falling-back-to-cache)
* request strategy.
*
* By default, this strategy will cache responses with a 200 status code as
* well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
* Opaque responses are are cross-origin requests where the response doesn't
* support [CORS](https://enable-cors.org/).
*
* If the network request fails, and there is no cache match, this will throw
* a `SerwistError` exception.
*/ class NetworkFirst extends Strategy {
class NetworkFirst extends Strategy {
_networkTimeoutSeconds;
/**
* @param options
* This option can be used to combat
* "[lie-fi](https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi)"
* scenarios.
*/ constructor(options = {}){
constructor(options = {}){
super(options);
// If this instance contains no plugins with a 'cacheWillUpdate' callback,
// prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
if (!this.plugins.some((p)=>"cacheWillUpdate" in p)) {

@@ -822,8 +548,3 @@ this.plugins.unshift(cacheOkAndOpaquePlugin);

}
/**
* @private
* @param request A request to run this strategy for.
* @param handler The event that triggered the request.
* @returns
*/ async _handle(request, handler) {
async _handle(request, handler) {
const logs = [];

@@ -857,9 +578,3 @@ if (process.env.NODE_ENV !== "production") {

const response = await handler.waitUntil((async ()=>{
// Promise.race() will resolve as soon as the first promise resolves.
return await handler.waitUntil(Promise.race(promises)) || // If Promise.race() resolved with null, it might be due to a network
// timeout + a cache miss. If that were to happen, we'd rather wait until
// the networkPromise resolves instead of returning null.
// Note that it's fine to await an already-resolved promise, so we don't
// have to check to see if it's still "in flight".
await networkPromise;
return await handler.waitUntil(Promise.race(promises)) || await networkPromise;
})());

@@ -881,8 +596,3 @@ if (process.env.NODE_ENV !== "production") {

}
/**
* @param options
* @returns
* @private
*/ _getTimeoutPromise({ request, logs, handler }) {
// biome-ignore lint/suspicious/noImplicitAnyLet: setTimeout is typed with Node.js's typings, so we can't use number | undefined here.
_getTimeoutPromise({ request, logs, handler }) {
let timeoutId;

@@ -903,12 +613,3 @@ const timeoutPromise = new Promise((resolve)=>{

}
/**
* @param options
* @param options.timeoutId
* @param options.request
* @param options.logs A reference to the logs Array.
* @param options.event
* @returns
*
* @private
*/ async _getNetworkPromise({ timeoutId, request, logs, handler }) {
async _getNetworkPromise({ timeoutId, request, logs, handler }) {
let error = undefined;

@@ -947,23 +648,9 @@ let response = undefined;

/**
* An implementation of a [network only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only)
* request strategy.
*
* This class is useful if you want to take advantage of any Serwist plugin.
*
* If the network request fails, this will throw a `SerwistError` exception.
*/ class NetworkOnly extends Strategy {
class NetworkOnly extends Strategy {
_networkTimeoutSeconds;
/**
* @param options
*/ constructor(options = {}){
constructor(options = {}){
super(options);
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
}
/**
* @private
* @param request A request to run this strategy for.
* @param handler The event that triggered the request.
* @returns
*/ async _handle(request, handler) {
async _handle(request, handler) {
if (process.env.NODE_ENV !== "production") {

@@ -1016,26 +703,5 @@ assert.isInstance(request, Request, {

/**
* An implementation of a
* [stale-while-revalidate](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#stale-while-revalidate)
* request strategy.
*
* Resources are requested from both the cache and the network in parallel.
* The strategy will respond with the cached version if available, otherwise
* wait for the network response. The cache is updated with the network response
* with each successful request.
*
* By default, this strategy will cache responses with a 200 status code as
* well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
* Opaque responses are cross-origin requests where the response doesn't
* support [CORS](https://enable-cors.org/).
*
* If the network request fails, and there is no cache match, this will throw
* a `SerwistError` exception.
*/ class StaleWhileRevalidate extends Strategy {
/**
* @param options
*/ constructor(options = {}){
class StaleWhileRevalidate extends Strategy {
constructor(options = {}){
super(options);
// If this instance contains no plugins with a 'cacheWillUpdate' callback,
// prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
if (!this.plugins.some((p)=>"cacheWillUpdate" in p)) {

@@ -1045,8 +711,3 @@ this.plugins.unshift(cacheOkAndOpaquePlugin);

}
/**
* @private
* @param request A request to run this strategy for.
* @param handler The event that triggered the request.
* @returns
*/ async _handle(request, handler) {
async _handle(request, handler) {
const logs = [];

@@ -1061,6 +722,3 @@ if (process.env.NODE_ENV !== "production") {

}
const fetchAndCachePromise = handler.fetchAndCachePut(request).catch(()=>{
// Swallow this error because a 'no-response' error will be thrown in
// main handler return flow. This will be in the `waitUntil()` flow.
});
const fetchAndCachePromise = handler.fetchAndCachePut(request).catch(()=>{});
void handler.waitUntil(fetchAndCachePromise);

@@ -1078,4 +736,2 @@ let response = await handler.cacheMatch(request);

try {
// NOTE(philipwalton): Really annoying that we have to type cast here.
// https://github.com/microsoft/TypeScript/issues/20006
response = await fetchAndCachePromise;

@@ -1082,0 +738,0 @@ } catch (err) {

6

package.json
{
"name": "@serwist/strategies",
"version": "9.0.0-preview.0",
"version": "9.0.0-preview.1",
"type": "module",

@@ -33,3 +33,3 @@ "description": "A service worker helper library implementing common caching strategies.",

"dependencies": {
"@serwist/core": "9.0.0-preview.0"
"@serwist/core": "9.0.0-preview.1"
},

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

"typescript": "5.4.0-dev.20240203",
"@serwist/constants": "9.0.0-preview.0"
"@serwist/constants": "9.0.0-preview.1"
},

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

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