@serwist/strategies 9.0.0-preview.0 → 9.0.0-preview.2

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.
Files changed (2) hide show
  1. package/dist/index.js +43 -387
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -3,48 +3,18 @@ import { assert, Deferred, logger, getFriendlyURL, SerwistError, timeout, cacheM
3
3
  function toRequest(input) {
4
4
  return typeof input === "string" ? new Request(input) : input;
5
5
  }
6
- /**
7
- * A class created every time a Strategy instance instance calls `Strategy.handle` or
8
- * `Strategy.handleAll` that wraps all fetch and cache actions around plugin callbacks
9
- * and keeps track of when the strategy is "done" (i.e. all added `event.waitUntil()` promises
10
- * have resolved).
11
- */ class StrategyHandler {
12
- /**
13
- * The request the strategy is performing (passed to the strategy's
14
- * `handle()` or `handleAll()` method).
15
- */ request;
16
- /**
17
- * A `URL` instance of `request.url` (if passed to the strategy's
18
- * `handle()` or `handleAll()` method).
19
- * Note: the `url` param will be present if the strategy was invoked
20
- * from a `@serwist/routing.Route` object.
21
- */ url;
22
- /**
23
- * The event associated with this request.
24
- */ event;
25
- /**
26
- * A `param` value (if passed to the strategy's
27
- * `handle()` or `handleAll()` method).
28
- * Note: the `param` param will be present if the strategy was invoked
29
- * from a `@serwist/routing.Route` object and the `@serwist/strategies.matchCallback`
30
- * returned a truthy value (it will be that value).
31
- */ params;
6
+ class StrategyHandler {
7
+ request;
8
+ url;
9
+ event;
10
+ params;
32
11
  _cacheKeys = {};
33
12
  _strategy;
34
13
  _extendLifetimePromises;
35
14
  _handlerDeferred;
36
15
  _plugins;
37
16
  _pluginStateMap;
38
- /**
39
- * Creates a new instance associated with the passed strategy and event
40
- * that's handling the request.
41
- *
42
- * The constructor also initializes the state that will be passed to each of
43
- * the plugins handling this request.
44
- *
45
- * @param strategy
46
- * @param options
47
- */ constructor(strategy, options){
17
+ constructor(strategy, options){
48
18
  if (process.env.NODE_ENV !== "production") {
49
19
  assert.isInstance(options.event, ExtendableEvent, {
50
20
  moduleName: "@serwist/strategies",
@@ -58,8 +28,6 @@ function toRequest(input) {
58
28
  this._strategy = strategy;
59
29
  this._handlerDeferred = new Deferred();
60
30
  this._extendLifetimePromises = [];
61
- // Copy the plugins list (since it's mutable on the strategy),
62
- // so any mutations don't affect this handler instance.
63
31
  this._plugins = [
64
32
  ...strategy.plugins
65
33
  ];
@@ -69,19 +37,7 @@ function toRequest(input) {
69
37
  }
70
38
  this.event.waitUntil(this._handlerDeferred.promise);
71
39
  }
72
- /**
73
- * Fetches a given request (and invokes any applicable plugin callback
74
- * methods) using the `fetchOptions` (for non-navigation requests) and
75
- * `plugins` defined on the `Strategy` object.
76
- *
77
- * The following plugin lifecycle methods are invoked when using this method:
78
- * - `requestWillFetch()`
79
- * - `fetchDidSucceed()`
80
- * - `fetchDidFail()`
81
- *
82
- * @param input The URL or request to fetch.
83
- * @returns
84
- */ async fetch(input) {
40
+ async fetch(input) {
85
41
  const { event } = this;
86
42
  let request = toRequest(input);
87
43
  if (request.mode === "navigate" && event instanceof FetchEvent && event.preloadResponse) {
@@ -93,9 +49,6 @@ function toRequest(input) {
93
49
  return possiblePreloadResponse;
94
50
  }
95
51
  }
96
- // If there is a fetchDidFail plugin, we need to save a clone of the
97
- // original request before it's either modified by a requestWillFetch
98
- // plugin or before the original request's body is consumed via fetch().
99
52
  const originalRequest = this.hasCallback("fetchDidFail") ? request.clone() : null;
100
53
  try {
101
54
  for (const cb of this.iterateCallbacks("requestWillFetch")){
@@ -111,13 +64,9 @@ function toRequest(input) {
111
64
  });
112
65
  }
113
66
  }
114
- // The request can be altered by plugins with `requestWillFetch` making
115
- // the original request (most likely from a `fetch` event) different
116
- // from the Request we make. Pass both to `fetchDidFail` to aid debugging.
117
67
  const pluginFilteredRequest = request.clone();
118
68
  try {
119
69
  let fetchResponse;
120
- // See https://github.com/GoogleChrome/workbox/issues/1796
121
70
  fetchResponse = await fetch(request, request.mode === "navigate" ? undefined : this._strategy.fetchOptions);
122
71
  if (process.env.NODE_ENV !== "production") {
123
72
  logger.debug(`Network request for '${getFriendlyURL(request.url)}' returned a response with status '${fetchResponse.status}'.`);
@@ -134,8 +83,6 @@ function toRequest(input) {
134
83
  if (process.env.NODE_ENV !== "production") {
135
84
  logger.log(`Network request for '${getFriendlyURL(request.url)}' threw an error.`, error);
136
85
  }
137
- // `originalRequest` will only exist if a `fetchDidFail` callback
138
- // is being used (see above).
139
86
  if (originalRequest) {
140
87
  await this.runCallbacks("fetchDidFail", {
141
88
  error: error,
@@ -147,33 +94,13 @@ function toRequest(input) {
147
94
  throw error;
148
95
  }
149
96
  }
150
- /**
151
- * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on
152
- * the response generated by `this.fetch()`.
153
- *
154
- * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,
155
- * so you do not have to manually call `waitUntil()` on the event.
156
- *
157
- * @param input The request or URL to fetch and cache.
158
- * @returns
159
- */ async fetchAndCachePut(input) {
97
+ async fetchAndCachePut(input) {
160
98
  const response = await this.fetch(input);
161
99
  const responseClone = response.clone();
162
100
  void this.waitUntil(this.cachePut(input, responseClone));
163
101
  return response;
164
102
  }
165
- /**
166
- * Matches a request from the cache (and invokes any applicable plugin
167
- * callback methods) using the `cacheName`, `matchOptions`, and `plugins`
168
- * defined on the strategy object.
169
- *
170
- * The following plugin lifecycle methods are invoked when using this method:
171
- * - cacheKeyWillByUsed()
172
- * - cachedResponseWillByUsed()
173
- *
174
- * @param key The Request or URL to use as the cache key.
175
- * @returns A matching response, if found.
176
- */ async cacheMatch(key) {
103
+ async cacheMatch(key) {
177
104
  const request = toRequest(key);
178
105
  let cachedResponse;
179
106
  const { cacheName, matchOptions } = this._strategy;
@@ -203,24 +130,8 @@ function toRequest(input) {
203
130
  }
204
131
  return cachedResponse;
205
132
  }
206
- /**
207
- * Puts a request/response pair in the cache (and invokes any applicable
208
- * plugin callback methods) using the `cacheName` and `plugins` defined on
209
- * the strategy object.
210
- *
211
- * The following plugin lifecycle methods are invoked when using this method:
212
- * - cacheKeyWillByUsed()
213
- * - cacheWillUpdate()
214
- * - cacheDidUpdate()
215
- *
216
- * @param key The request or URL to use as the cache key.
217
- * @param response The response to cache.
218
- * @returns `false` if a cacheWillUpdate caused the response
219
- * not be cached, and `true` otherwise.
220
- */ async cachePut(key, response) {
133
+ async cachePut(key, response) {
221
134
  const request = toRequest(key);
222
- // Run in the next task to avoid blocking other cache reads.
223
- // https://github.com/w3c/ServiceWorker/issues/1397
224
135
  await timeout(0);
225
136
  const effectiveRequest = await this.getCacheKey(request, "write");
226
137
  if (process.env.NODE_ENV !== "production") {
@@ -230,7 +141,6 @@ function toRequest(input) {
230
141
  method: effectiveRequest.method
231
142
  });
232
143
  }
233
- // See https://github.com/GoogleChrome/workbox/issues/2818
234
144
  const vary = response.headers.get("Vary");
235
145
  if (vary) {
236
146
  logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} has a 'Vary: ${vary}' header. Consider setting the {ignoreVary: true} option on your strategy to ensure cache matching and deletion works as expected.`);
@@ -254,10 +164,7 @@ function toRequest(input) {
254
164
  const { cacheName, matchOptions } = this._strategy;
255
165
  const cache = await self.caches.open(cacheName);
256
166
  const hasCacheUpdateCallback = this.hasCallback("cacheDidUpdate");
257
- const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(// TODO(philipwalton): the `__WB_REVISION__` param is a precaching
258
- // feature. Consider into ways to only add this behavior if using
259
- // precaching.
260
- cache, effectiveRequest.clone(), [
167
+ const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(cache, effectiveRequest.clone(), [
261
168
  "__WB_REVISION__"
262
169
  ], matchOptions) : null;
263
170
  if (process.env.NODE_ENV !== "production") {
@@ -267,7 +174,6 @@ function toRequest(input) {
267
174
  await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);
268
175
  } catch (error) {
269
176
  if (error instanceof Error) {
270
- // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError
271
177
  if (error.name === "QuotaExceededError") {
272
178
  await executeQuotaErrorCallbacks();
273
179
  }
@@ -285,17 +191,7 @@ function toRequest(input) {
285
191
  }
286
192
  return true;
287
193
  }
288
- /**
289
- * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and
290
- * executes any of those callbacks found in sequence. The final `Request`
291
- * object returned by the last plugin is treated as the cache key for cache
292
- * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have
293
- * been registered, the passed request is returned unmodified
294
- *
295
- * @param request
296
- * @param mode
297
- * @returns
298
- */ async getCacheKey(request, mode) {
194
+ async getCacheKey(request, mode) {
299
195
  const key = `${request.url} | ${mode}`;
300
196
  if (!this._cacheKeys[key]) {
301
197
  let effectiveRequest = request;
@@ -304,7 +200,6 @@ function toRequest(input) {
304
200
  mode,
305
201
  request: effectiveRequest,
306
202
  event: this.event,
307
- // params has a type any can't change right now.
308
203
  params: this.params
309
204
  }));
310
205
  }
@@ -312,13 +207,7 @@ function toRequest(input) {
312
207
  }
313
208
  return this._cacheKeys[key];
314
209
  }
315
- /**
316
- * Returns true if the strategy has at least one plugin with the given
317
- * callback.
318
- *
319
- * @param name The name of the callback to check for.
320
- * @returns
321
- */ hasCallback(name) {
210
+ hasCallback(name) {
322
211
  for (const plugin of this._strategy.plugins){
323
212
  if (name in plugin) {
324
213
  return true;
@@ -326,34 +215,12 @@ function toRequest(input) {
326
215
  }
327
216
  return false;
328
217
  }
329
- /**
330
- * Runs all plugin callbacks matching the given name, in order, passing the
331
- * given param object (merged ith the current plugin state) as the only
332
- * argument.
333
- *
334
- * Note: since this method runs all plugins, it's not suitable for cases
335
- * where the return value of a callback needs to be applied prior to calling
336
- * the next callback. See `@serwist/strategies.iterateCallbacks` for how to handle that case.
337
- *
338
- * @param name The name of the callback to run within each plugin.
339
- * @param param The object to pass as the first (and only) param when executing each callback. This object will be merged with the
340
- * current plugin state prior to callback execution.
341
- */ async runCallbacks(name, param) {
218
+ async runCallbacks(name, param) {
342
219
  for (const callback of this.iterateCallbacks(name)){
343
- // TODO(philipwalton): not sure why `any` is needed. It seems like
344
- // this should work with `as SerwistPluginCallbackParam[C]`.
345
220
  await callback(param);
346
221
  }
347
222
  }
348
- /**
349
- * Accepts a callback and returns an iterable of matching plugin callbacks,
350
- * where each callback is wrapped with the current handler state (i.e. when
351
- * you call each callback, whatever object parameter you pass it will
352
- * be merged with the plugin's current state).
353
- *
354
- * @param name The name fo the callback to run
355
- * @returns
356
- */ *iterateCallbacks(name) {
223
+ *iterateCallbacks(name) {
357
224
  for (const plugin of this._strategy.plugins){
358
225
  if (typeof plugin[name] === "function") {
359
226
  const state = this._pluginStateMap.get(plugin);
@@ -362,57 +229,26 @@ function toRequest(input) {
362
229
  ...param,
363
230
  state
364
231
  };
365
- // TODO(philipwalton): not sure why `any` is needed. It seems like
366
- // this should work with `as WorkboxPluginCallbackParam[C]`.
367
232
  return plugin[name](statefulParam);
368
233
  };
369
234
  yield statefulCallback;
370
235
  }
371
236
  }
372
237
  }
373
- /**
374
- * Adds a promise to the
375
- * [extend lifetime promises](https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises)
376
- * of the event event associated with the request being handled (usually a `FetchEvent`).
377
- *
378
- * Note: you can await
379
- * `@serwist/strategies.StrategyHandler.doneWaiting`
380
- * to know when all added promises have settled.
381
- *
382
- * @param promise A promise to add to the extend lifetime promises of
383
- * the event that triggered the request.
384
- */ waitUntil(promise) {
238
+ waitUntil(promise) {
385
239
  this._extendLifetimePromises.push(promise);
386
240
  return promise;
387
241
  }
388
- /**
389
- * Returns a promise that resolves once all promises passed to
390
- * `@serwist/strategies.StrategyHandler.waitUntil` have settled.
391
- *
392
- * Note: any work done after `doneWaiting()` settles should be manually
393
- * passed to an event's `waitUntil()` method (not this handler's
394
- * `waitUntil()` method), otherwise the service worker thread my be killed
395
- * prior to your work completing.
396
- */ async doneWaiting() {
242
+ async doneWaiting() {
397
243
  let promise = undefined;
398
244
  while(promise = this._extendLifetimePromises.shift()){
399
245
  await promise;
400
246
  }
401
247
  }
402
- /**
403
- * Stops running the strategy and immediately resolves any pending
404
- * `waitUntil()` promises.
405
- */ destroy() {
248
+ destroy() {
406
249
  this._handlerDeferred.resolve(null);
407
250
  }
408
- /**
409
- * This method will call `cacheWillUpdate` on the available plugins (or use
410
- * status === 200) to determine if the response is safe and valid to cache.
411
- *
412
- * @param response
413
- * @returns
414
- * @private
415
- */ async _ensureResponseSafeToCache(response) {
251
+ async _ensureResponseSafeToCache(response) {
416
252
  let responseToCache = response;
417
253
  let pluginsUsed = false;
418
254
  for (const callback of this.iterateCallbacks("cacheWillUpdate")){
@@ -446,66 +282,22 @@ function toRequest(input) {
446
282
  }
447
283
  }
448
284
 
449
- /**
450
- * Classes extending the `Strategy` based class should implement this method,
451
- * and leverage `@serwist/strategies`'s `StrategyHandler` arg to perform all
452
- * fetching and cache logic, which will ensure all relevant cache, cache options,
453
- * fetch options and plugins are used (per the current strategy instance).
454
- */ class Strategy {
285
+ class Strategy {
455
286
  cacheName;
456
287
  plugins;
457
288
  fetchOptions;
458
289
  matchOptions;
459
- /**
460
- * Creates a new instance of the strategy and sets all documented option
461
- * properties as public instance properties.
462
- *
463
- * Note: if a custom strategy class extends the base Strategy class and does
464
- * not need more than these properties, it does not need to define its own
465
- * constructor.
466
- *
467
- * @param options
468
- */ constructor(options = {}){
290
+ constructor(options = {}){
469
291
  this.cacheName = privateCacheNames.getRuntimeName(options.cacheName);
470
292
  this.plugins = options.plugins || [];
471
293
  this.fetchOptions = options.fetchOptions;
472
294
  this.matchOptions = options.matchOptions;
473
295
  }
474
- /**
475
- * Perform a request strategy and returns a `Promise` that will resolve with
476
- * a `Response`, invoking all relevant plugin callbacks.
477
- *
478
- * When a strategy instance is registered with a `@serwist/routing` Route, this method is automatically
479
- * called when the route matches.
480
- *
481
- * Alternatively, this method can be used in a standalone `FetchEvent`
482
- * listener by passing it to `event.respondWith()`.
483
- *
484
- * @param options A `FetchEvent` or an object with the properties listed below.
485
- * @param options.request A request to run this strategy for.
486
- * @param options.event The event associated with the request.
487
- * @param options.url
488
- * @param options.params
489
- */ handle(options) {
296
+ handle(options) {
490
297
  const [responseDone] = this.handleAll(options);
491
298
  return responseDone;
492
299
  }
493
- /**
494
- * Similar to `@serwist/strategies`'s `Strategy.handle`, but
495
- * instead of just returning a `Promise` that resolves to a `Response` it
496
- * it will return an tuple of `[response, done]` promises, where the former
497
- * (`response`) is equivalent to what `handle()` returns, and the latter is a
498
- * Promise that will resolve once any promises that were added to
499
- * `event.waitUntil()` as part of performing the strategy have completed.
500
- *
501
- * You can await the `done` promise to ensure any extra work performed by
502
- * the strategy (usually caching responses) completes successfully.
503
- *
504
- * @param options A `FetchEvent` or `HandlerCallbackOptions` object.
505
- * @returns A tuple of [response, done] promises that can be used to determine when the response resolves as
506
- * well as when the handler has completed all its work.
507
- */ handleAll(options) {
508
- // Allow for flexible options to be passed.
300
+ handleAll(options) {
509
301
  if (options instanceof FetchEvent) {
510
302
  options = {
511
303
  event: options,
@@ -522,7 +314,6 @@ function toRequest(input) {
522
314
  });
523
315
  const responseDone = this._getResponse(handler, request, event);
524
316
  const handlerDone = this._awaitComplete(responseDone, handler, request, event);
525
- // Return an array of promises, suitable for use with Promise.all().
526
317
  return [
527
318
  responseDone,
528
319
  handlerDone
@@ -536,9 +327,6 @@ function toRequest(input) {
536
327
  let response = undefined;
537
328
  try {
538
329
  response = await this._handle(request, handler);
539
- // The "official" Strategy subclasses all throw this error automatically,
540
- // but in case a third-party Strategy doesn't, ensure that we have a
541
- // consistent failure when there's no response or an error response.
542
330
  if (response === undefined || response.type === "error") {
543
331
  throw new SerwistError("no-response", {
544
332
  url: request.url
@@ -578,11 +366,7 @@ function toRequest(input) {
578
366
  let error = undefined;
579
367
  try {
580
368
  response = await responseDone;
581
- } catch (error) {
582
- // Ignore errors, as response errors should be caught via the `response`
583
- // promise above. The `done` promise will only throw for errors in
584
- // promises passed to `handler.waitUntil()`.
585
- }
369
+ } catch (error) {}
586
370
  try {
587
371
  await handler.runCallbacks("handlerDidRespond", {
588
372
  event,
@@ -619,23 +403,8 @@ const messages = {
619
403
  }
620
404
  };
621
405
 
622
- /**
623
- * An implementation of a [cache first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-falling-back-to-network)
624
- * request strategy.
625
- *
626
- * A cache first strategy is useful for assets that have been revisioned,
627
- * such as URLs like `/styles/example.a8f5f1.css`, since they
628
- * can be cached for long periods of time.
629
- *
630
- * If the network request fails, and there is no cache match, this will throw
631
- * a `SerwistError` exception.
632
- */ class CacheFirst extends Strategy {
633
- /**
634
- * @private
635
- * @param request A request to run this strategy for.
636
- * @param handler The event that triggered the request.
637
- * @returns
638
- */ async _handle(request, handler) {
406
+ class CacheFirst extends Strategy {
407
+ async _handle(request, handler) {
639
408
  const logs = [];
640
409
  if (process.env.NODE_ENV !== "production") {
641
410
  assert.isInstance(request, Request, {
@@ -688,20 +457,8 @@ const messages = {
688
457
  }
689
458
  }
690
459
 
691
- /**
692
- * An implementation of a [cache only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-only)
693
- * request strategy.
694
- *
695
- * This class is useful if you want to take advantage of any Serwist plugin.
696
- *
697
- * If there is no cache match, this will throw a `SerwistError` exception.
698
- */ class CacheOnly extends Strategy {
699
- /**
700
- * @private
701
- * @param request A request to run this strategy for.
702
- * @param handler The event that triggered the request.
703
- * @returns
704
- */ async _handle(request, handler) {
460
+ class CacheOnly extends Strategy {
461
+ async _handle(request, handler) {
705
462
  if (process.env.NODE_ENV !== "production") {
706
463
  assert.isInstance(request, Request, {
707
464
  moduleName: "@serwist/strategies",
@@ -730,21 +487,8 @@ const messages = {
730
487
  }
731
488
  }
732
489
 
733
- /*
734
- Copyright 2018 Google LLC
735
-
736
- Use of this source code is governed by an MIT-style
737
- license that can be found in the LICENSE file or at
738
- https://opensource.org/licenses/MIT.
739
- */ const cacheOkAndOpaquePlugin = {
740
- /**
741
- * Returns a valid response (to allow caching) if the status is 200 (OK) or
742
- * 0 (opaque).
743
- *
744
- * @param options
745
- * @returns
746
- * @private
747
- */ cacheWillUpdate: async ({ response })=>{
490
+ const cacheOkAndOpaquePlugin = {
491
+ cacheWillUpdate: async ({ response })=>{
748
492
  if (response.status === 200 || response.status === 0) {
749
493
  return response;
750
494
  }
@@ -752,28 +496,10 @@ const messages = {
752
496
  }
753
497
  };
754
498
 
755
- /**
756
- * An implementation of a [network first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-first-falling-back-to-cache)
757
- * request strategy.
758
- *
759
- * By default, this strategy will cache responses with a 200 status code as
760
- * well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
761
- * Opaque responses are are cross-origin requests where the response doesn't
762
- * support [CORS](https://enable-cors.org/).
763
- *
764
- * If the network request fails, and there is no cache match, this will throw
765
- * a `SerwistError` exception.
766
- */ class NetworkFirst extends Strategy {
499
+ class NetworkFirst extends Strategy {
767
500
  _networkTimeoutSeconds;
768
- /**
769
- * @param options
770
- * This option can be used to combat
771
- * "[lie-fi](https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi)"
772
- * scenarios.
773
- */ constructor(options = {}){
501
+ constructor(options = {}){
774
502
  super(options);
775
- // If this instance contains no plugins with a 'cacheWillUpdate' callback,
776
- // prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
777
503
  if (!this.plugins.some((p)=>"cacheWillUpdate" in p)) {
778
504
  this.plugins.unshift(cacheOkAndOpaquePlugin);
779
505
  }
@@ -789,12 +515,7 @@ const messages = {
789
515
  }
790
516
  }
791
517
  }
792
- /**
793
- * @private
794
- * @param request A request to run this strategy for.
795
- * @param handler The event that triggered the request.
796
- * @returns
797
- */ async _handle(request, handler) {
518
+ async _handle(request, handler) {
798
519
  const logs = [];
799
520
  if (process.env.NODE_ENV !== "production") {
800
521
  assert.isInstance(request, Request, {
@@ -823,13 +544,7 @@ const messages = {
823
544
  });
824
545
  promises.push(networkPromise);
825
546
  const response = await handler.waitUntil((async ()=>{
826
- // Promise.race() will resolve as soon as the first promise resolves.
827
- return await handler.waitUntil(Promise.race(promises)) || // If Promise.race() resolved with null, it might be due to a network
828
- // timeout + a cache miss. If that were to happen, we'd rather wait until
829
- // the networkPromise resolves instead of returning null.
830
- // Note that it's fine to await an already-resolved promise, so we don't
831
- // have to check to see if it's still "in flight".
832
- await networkPromise;
547
+ return await handler.waitUntil(Promise.race(promises)) || await networkPromise;
833
548
  })());
834
549
  if (process.env.NODE_ENV !== "production") {
835
550
  logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
@@ -846,12 +561,7 @@ const messages = {
846
561
  }
847
562
  return response;
848
563
  }
849
- /**
850
- * @param options
851
- * @returns
852
- * @private
853
- */ _getTimeoutPromise({ request, logs, handler }) {
854
- // biome-ignore lint/suspicious/noImplicitAnyLet: setTimeout is typed with Node.js's typings, so we can't use number | undefined here.
564
+ _getTimeoutPromise({ request, logs, handler }) {
855
565
  let timeoutId;
856
566
  const timeoutPromise = new Promise((resolve)=>{
857
567
  const onNetworkTimeout = async ()=>{
@@ -867,16 +577,7 @@ const messages = {
867
577
  id: timeoutId
868
578
  };
869
579
  }
870
- /**
871
- * @param options
872
- * @param options.timeoutId
873
- * @param options.request
874
- * @param options.logs A reference to the logs Array.
875
- * @param options.event
876
- * @returns
877
- *
878
- * @private
879
- */ async _getNetworkPromise({ timeoutId, request, logs, handler }) {
580
+ async _getNetworkPromise({ timeoutId, request, logs, handler }) {
880
581
  let error = undefined;
881
582
  let response = undefined;
882
583
  try {
@@ -910,27 +611,13 @@ const messages = {
910
611
  }
911
612
  }
912
613
 
913
- /**
914
- * An implementation of a [network only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only)
915
- * request strategy.
916
- *
917
- * This class is useful if you want to take advantage of any Serwist plugin.
918
- *
919
- * If the network request fails, this will throw a `SerwistError` exception.
920
- */ class NetworkOnly extends Strategy {
614
+ class NetworkOnly extends Strategy {
921
615
  _networkTimeoutSeconds;
922
- /**
923
- * @param options
924
- */ constructor(options = {}){
616
+ constructor(options = {}){
925
617
  super(options);
926
618
  this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
927
619
  }
928
- /**
929
- * @private
930
- * @param request A request to run this strategy for.
931
- * @param handler The event that triggered the request.
932
- * @returns
933
- */ async _handle(request, handler) {
620
+ async _handle(request, handler) {
934
621
  if (process.env.NODE_ENV !== "production") {
935
622
  assert.isInstance(request, Request, {
936
623
  moduleName: "@serwist/strategies",
@@ -978,40 +665,14 @@ const messages = {
978
665
  }
979
666
  }
980
667
 
981
- /**
982
- * An implementation of a
983
- * [stale-while-revalidate](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#stale-while-revalidate)
984
- * request strategy.
985
- *
986
- * Resources are requested from both the cache and the network in parallel.
987
- * The strategy will respond with the cached version if available, otherwise
988
- * wait for the network response. The cache is updated with the network response
989
- * with each successful request.
990
- *
991
- * By default, this strategy will cache responses with a 200 status code as
992
- * well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
993
- * Opaque responses are cross-origin requests where the response doesn't
994
- * support [CORS](https://enable-cors.org/).
995
- *
996
- * If the network request fails, and there is no cache match, this will throw
997
- * a `SerwistError` exception.
998
- */ class StaleWhileRevalidate extends Strategy {
999
- /**
1000
- * @param options
1001
- */ constructor(options = {}){
668
+ class StaleWhileRevalidate extends Strategy {
669
+ constructor(options = {}){
1002
670
  super(options);
1003
- // If this instance contains no plugins with a 'cacheWillUpdate' callback,
1004
- // prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
1005
671
  if (!this.plugins.some((p)=>"cacheWillUpdate" in p)) {
1006
672
  this.plugins.unshift(cacheOkAndOpaquePlugin);
1007
673
  }
1008
674
  }
1009
- /**
1010
- * @private
1011
- * @param request A request to run this strategy for.
1012
- * @param handler The event that triggered the request.
1013
- * @returns
1014
- */ async _handle(request, handler) {
675
+ async _handle(request, handler) {
1015
676
  const logs = [];
1016
677
  if (process.env.NODE_ENV !== "production") {
1017
678
  assert.isInstance(request, Request, {
@@ -1021,10 +682,7 @@ const messages = {
1021
682
  paramName: "request"
1022
683
  });
1023
684
  }
1024
- const fetchAndCachePromise = handler.fetchAndCachePut(request).catch(()=>{
1025
- // Swallow this error because a 'no-response' error will be thrown in
1026
- // main handler return flow. This will be in the `waitUntil()` flow.
1027
- });
685
+ const fetchAndCachePromise = handler.fetchAndCachePut(request).catch(()=>{});
1028
686
  void handler.waitUntil(fetchAndCachePromise);
1029
687
  let response = await handler.cacheMatch(request);
1030
688
  let error = undefined;
@@ -1037,8 +695,6 @@ const messages = {
1037
695
  logs.push(`No response found in the '${this.cacheName}' cache. Will wait for the network response.`);
1038
696
  }
1039
697
  try {
1040
- // NOTE(philipwalton): Really annoying that we have to type cast here.
1041
- // https://github.com/microsoft/TypeScript/issues/20006
1042
698
  response = await fetchAndCachePromise;
1043
699
  } catch (err) {
1044
700
  if (err instanceof Error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serwist/strategies",
3
- "version": "9.0.0-preview.0",
3
+ "version": "9.0.0-preview.2",
4
4
  "type": "module",
5
5
  "description": "A service worker helper library implementing common caching strategies.",
6
6
  "files": [
@@ -30,12 +30,12 @@
30
30
  "./package.json": "./package.json"
31
31
  },
32
32
  "dependencies": {
33
- "@serwist/core": "9.0.0-preview.0"
33
+ "@serwist/core": "9.0.0-preview.2"
34
34
  },
35
35
  "devDependencies": {
36
36
  "rollup": "4.9.6",
37
- "typescript": "5.4.0-dev.20240203",
38
- "@serwist/constants": "9.0.0-preview.0"
37
+ "typescript": "5.4.0-dev.20240206",
38
+ "@serwist/constants": "9.0.0-preview.2"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "typescript": ">=5.0.0"