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

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/src/CacheFirst.ts DELETED
@@ -1,89 +0,0 @@
1
- /*
2
- Copyright 2018 Google LLC
3
-
4
- Use of this source code is governed by an MIT-style
5
- license that can be found in the LICENSE file or at
6
- https://opensource.org/licenses/MIT.
7
- */
8
-
9
- import { assert, SerwistError, logger } from "@serwist/core/internal";
10
-
11
- import { Strategy } from "./Strategy.js";
12
- import type { StrategyHandler } from "./StrategyHandler.js";
13
- import { messages } from "./utils/messages.js";
14
-
15
- /**
16
- * An implementation of a [cache first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-falling-back-to-network)
17
- * request strategy.
18
- *
19
- * A cache first strategy is useful for assets that have been revisioned,
20
- * such as URLs like `/styles/example.a8f5f1.css`, since they
21
- * can be cached for long periods of time.
22
- *
23
- * If the network request fails, and there is no cache match, this will throw
24
- * a `SerwistError` exception.
25
- */
26
- class CacheFirst extends Strategy {
27
- /**
28
- * @private
29
- * @param request A request to run this strategy for.
30
- * @param handler The event that triggered the request.
31
- * @returns
32
- */
33
- async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
34
- const logs = [];
35
-
36
- if (process.env.NODE_ENV !== "production") {
37
- assert!.isInstance(request, Request, {
38
- moduleName: "@serwist/strategies",
39
- className: this.constructor.name,
40
- funcName: "makeRequest",
41
- paramName: "request",
42
- });
43
- }
44
-
45
- let response = await handler.cacheMatch(request);
46
-
47
- let error: Error | undefined = undefined;
48
- if (!response) {
49
- if (process.env.NODE_ENV !== "production") {
50
- logs.push(`No response found in the '${this.cacheName}' cache. Will respond with a network request.`);
51
- }
52
- try {
53
- response = await handler.fetchAndCachePut(request);
54
- } catch (err) {
55
- if (err instanceof Error) {
56
- error = err;
57
- }
58
- }
59
-
60
- if (process.env.NODE_ENV !== "production") {
61
- if (response) {
62
- logs.push("Got response from network.");
63
- } else {
64
- logs.push("Unable to get a response from the network.");
65
- }
66
- }
67
- } else {
68
- if (process.env.NODE_ENV !== "production") {
69
- logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
70
- }
71
- }
72
-
73
- if (process.env.NODE_ENV !== "production") {
74
- logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
75
- for (const log of logs) {
76
- logger.log(log);
77
- }
78
- messages.printFinalResponse(response);
79
- logger.groupEnd();
80
- }
81
-
82
- if (!response) {
83
- throw new SerwistError("no-response", { url: request.url, error });
84
- }
85
- return response;
86
- }
87
- }
88
-
89
- export { CacheFirst };
package/src/CacheOnly.ts DELETED
@@ -1,60 +0,0 @@
1
- /*
2
- Copyright 2018 Google LLC
3
-
4
- Use of this source code is governed by an MIT-style
5
- license that can be found in the LICENSE file or at
6
- https://opensource.org/licenses/MIT.
7
- */
8
-
9
- import { assert, SerwistError, logger } from "@serwist/core/internal";
10
-
11
- import { Strategy } from "./Strategy.js";
12
- import type { StrategyHandler } from "./StrategyHandler.js";
13
- import { messages } from "./utils/messages.js";
14
-
15
- /**
16
- * An implementation of a [cache only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-only)
17
- * request strategy.
18
- *
19
- * This class is useful if you want to take advantage of any Serwist plugin.
20
- *
21
- * If there is no cache match, this will throw a `SerwistError` exception.
22
- */
23
- class CacheOnly extends Strategy {
24
- /**
25
- * @private
26
- * @param request A request to run this strategy for.
27
- * @param handler The event that triggered the request.
28
- * @returns
29
- */
30
- async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
31
- if (process.env.NODE_ENV !== "production") {
32
- assert!.isInstance(request, Request, {
33
- moduleName: "@serwist/strategies",
34
- className: this.constructor.name,
35
- funcName: "makeRequest",
36
- paramName: "request",
37
- });
38
- }
39
-
40
- const response = await handler.cacheMatch(request);
41
-
42
- if (process.env.NODE_ENV !== "production") {
43
- logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
44
- if (response) {
45
- logger.log(`Found a cached response in the '${this.cacheName}' cache.`);
46
- messages.printFinalResponse(response);
47
- } else {
48
- logger.log(`No response found in the '${this.cacheName}' cache.`);
49
- }
50
- logger.groupEnd();
51
- }
52
-
53
- if (!response) {
54
- throw new SerwistError("no-response", { url: request.url });
55
- }
56
- return response;
57
- }
58
- }
59
-
60
- export { CacheOnly };
@@ -1,231 +0,0 @@
1
- /*
2
- Copyright 2018 Google LLC
3
-
4
- Use of this source code is governed by an MIT-style
5
- license that can be found in the LICENSE file or at
6
- https://opensource.org/licenses/MIT.
7
- */
8
-
9
- import { assert, SerwistError, logger } from "@serwist/core/internal";
10
-
11
- import type { StrategyOptions } from "./Strategy.js";
12
- import { Strategy } from "./Strategy.js";
13
- import type { StrategyHandler } from "./StrategyHandler.js";
14
- import { cacheOkAndOpaquePlugin } from "./plugins/cacheOkAndOpaquePlugin.js";
15
- import { messages } from "./utils/messages.js";
16
-
17
- export interface NetworkFirstOptions extends StrategyOptions {
18
- /**
19
- * If set, any network requests that fail to respond within the timeout will fallback to the cache.
20
- */
21
- networkTimeoutSeconds?: number;
22
- }
23
-
24
- /**
25
- * An implementation of a [network first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-first-falling-back-to-cache)
26
- * request strategy.
27
- *
28
- * By default, this strategy will cache responses with a 200 status code as
29
- * well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
30
- * Opaque responses are are cross-origin requests where the response doesn't
31
- * support [CORS](https://enable-cors.org/).
32
- *
33
- * If the network request fails, and there is no cache match, this will throw
34
- * a `SerwistError` exception.
35
- */
36
- class NetworkFirst extends Strategy {
37
- private readonly _networkTimeoutSeconds: number;
38
-
39
- /**
40
- * @param options
41
- * This option can be used to combat
42
- * "[lie-fi](https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi)"
43
- * scenarios.
44
- */
45
- constructor(options: NetworkFirstOptions = {}) {
46
- super(options);
47
-
48
- // If this instance contains no plugins with a 'cacheWillUpdate' callback,
49
- // prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
50
- if (!this.plugins.some((p) => "cacheWillUpdate" in p)) {
51
- this.plugins.unshift(cacheOkAndOpaquePlugin);
52
- }
53
-
54
- this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
55
- if (process.env.NODE_ENV !== "production") {
56
- if (this._networkTimeoutSeconds) {
57
- assert!.isType(this._networkTimeoutSeconds, "number", {
58
- moduleName: "@serwist/strategies",
59
- className: this.constructor.name,
60
- funcName: "constructor",
61
- paramName: "networkTimeoutSeconds",
62
- });
63
- }
64
- }
65
- }
66
-
67
- /**
68
- * @private
69
- * @param request A request to run this strategy for.
70
- * @param handler The event that triggered the request.
71
- * @returns
72
- */
73
- async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
74
- const logs: any[] = [];
75
-
76
- if (process.env.NODE_ENV !== "production") {
77
- assert!.isInstance(request, Request, {
78
- moduleName: "@serwist/strategies",
79
- className: this.constructor.name,
80
- funcName: "handle",
81
- paramName: "makeRequest",
82
- });
83
- }
84
-
85
- const promises: Promise<Response | undefined>[] = [];
86
- let timeoutId: number | undefined;
87
-
88
- if (this._networkTimeoutSeconds) {
89
- const { id, promise } = this._getTimeoutPromise({
90
- request,
91
- logs,
92
- handler,
93
- });
94
- timeoutId = id;
95
- promises.push(promise);
96
- }
97
-
98
- const networkPromise = this._getNetworkPromise({
99
- timeoutId,
100
- request,
101
- logs,
102
- handler,
103
- });
104
-
105
- promises.push(networkPromise);
106
-
107
- const response = await handler.waitUntil(
108
- (async () => {
109
- // Promise.race() will resolve as soon as the first promise resolves.
110
- return (
111
- (await handler.waitUntil(Promise.race(promises))) ||
112
- // If Promise.race() resolved with null, it might be due to a network
113
- // timeout + a cache miss. If that were to happen, we'd rather wait until
114
- // the networkPromise resolves instead of returning null.
115
- // Note that it's fine to await an already-resolved promise, so we don't
116
- // have to check to see if it's still "in flight".
117
- (await networkPromise)
118
- );
119
- })(),
120
- );
121
-
122
- if (process.env.NODE_ENV !== "production") {
123
- logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
124
- for (const log of logs) {
125
- logger.log(log);
126
- }
127
- messages.printFinalResponse(response);
128
- logger.groupEnd();
129
- }
130
-
131
- if (!response) {
132
- throw new SerwistError("no-response", { url: request.url });
133
- }
134
- return response;
135
- }
136
-
137
- /**
138
- * @param options
139
- * @returns
140
- * @private
141
- */
142
- private _getTimeoutPromise({
143
- request,
144
- logs,
145
- handler,
146
- }: {
147
- request: Request;
148
- /**
149
- * A reference to the logs array.
150
- */
151
- logs: any[];
152
- handler: StrategyHandler;
153
- }): { promise: Promise<Response | undefined>; id?: number } {
154
- // biome-ignore lint/suspicious/noImplicitAnyLet: setTimeout is typed with Node.js's typings, so we can't use number | undefined here.
155
- let timeoutId;
156
- const timeoutPromise: Promise<Response | undefined> = new Promise((resolve) => {
157
- const onNetworkTimeout = async () => {
158
- if (process.env.NODE_ENV !== "production") {
159
- logs.push(`Timing out the network response at ${this._networkTimeoutSeconds} seconds.`);
160
- }
161
- resolve(await handler.cacheMatch(request));
162
- };
163
- timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1000);
164
- });
165
-
166
- return {
167
- promise: timeoutPromise,
168
- id: timeoutId,
169
- };
170
- }
171
-
172
- /**
173
- * @param options
174
- * @param options.timeoutId
175
- * @param options.request
176
- * @param options.logs A reference to the logs Array.
177
- * @param options.event
178
- * @returns
179
- *
180
- * @private
181
- */
182
- async _getNetworkPromise({
183
- timeoutId,
184
- request,
185
- logs,
186
- handler,
187
- }: {
188
- request: Request;
189
- logs: any[];
190
- timeoutId?: number;
191
- handler: StrategyHandler;
192
- }): Promise<Response | undefined> {
193
- let error: Error | undefined = undefined;
194
- let response: Response | undefined = undefined;
195
- try {
196
- response = await handler.fetchAndCachePut(request);
197
- } catch (fetchError) {
198
- if (fetchError instanceof Error) {
199
- error = fetchError;
200
- }
201
- }
202
-
203
- if (timeoutId) {
204
- clearTimeout(timeoutId);
205
- }
206
-
207
- if (process.env.NODE_ENV !== "production") {
208
- if (response) {
209
- logs.push("Got response from network.");
210
- } else {
211
- logs.push("Unable to get a response from the network. Will respond " + "with a cached response.");
212
- }
213
- }
214
-
215
- if (error || !response) {
216
- response = await handler.cacheMatch(request);
217
-
218
- if (process.env.NODE_ENV !== "production") {
219
- if (response) {
220
- logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
221
- } else {
222
- logs.push(`No response found in the '${this.cacheName}' cache.`);
223
- }
224
- }
225
- }
226
-
227
- return response;
228
- }
229
- }
230
-
231
- export { NetworkFirst };
@@ -1,99 +0,0 @@
1
- /*
2
- Copyright 2018 Google LLC
3
-
4
- Use of this source code is governed by an MIT-style
5
- license that can be found in the LICENSE file or at
6
- https://opensource.org/licenses/MIT.
7
- */
8
-
9
- import { assert, SerwistError, logger, timeout } from "@serwist/core/internal";
10
-
11
- import type { StrategyOptions } from "./Strategy.js";
12
- import { Strategy } from "./Strategy.js";
13
- import type { StrategyHandler } from "./StrategyHandler.js";
14
- import { messages } from "./utils/messages.js";
15
-
16
- interface NetworkOnlyOptions extends Omit<StrategyOptions, "cacheName" | "matchOptions"> {
17
- /**
18
- * If set, any network requests that fail to respond within the timeout will result in a network error.
19
- */
20
- networkTimeoutSeconds?: number;
21
- }
22
-
23
- /**
24
- * An implementation of a [network only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only)
25
- * request strategy.
26
- *
27
- * This class is useful if you want to take advantage of any Serwist plugin.
28
- *
29
- * If the network request fails, this will throw a `SerwistError` exception.
30
- */
31
- class NetworkOnly extends Strategy {
32
- private readonly _networkTimeoutSeconds: number;
33
-
34
- /**
35
- * @param options
36
- */
37
- constructor(options: NetworkOnlyOptions = {}) {
38
- super(options);
39
-
40
- this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
41
- }
42
-
43
- /**
44
- * @private
45
- * @param request A request to run this strategy for.
46
- * @param handler The event that triggered the request.
47
- * @returns
48
- */
49
- async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
50
- if (process.env.NODE_ENV !== "production") {
51
- assert!.isInstance(request, Request, {
52
- moduleName: "@serwist/strategies",
53
- className: this.constructor.name,
54
- funcName: "_handle",
55
- paramName: "request",
56
- });
57
- }
58
-
59
- let error: Error | undefined = undefined;
60
- let response: Response | undefined;
61
-
62
- try {
63
- const promises: Promise<Response | undefined>[] = [handler.fetch(request)];
64
-
65
- if (this._networkTimeoutSeconds) {
66
- const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000) as Promise<undefined>;
67
- promises.push(timeoutPromise);
68
- }
69
-
70
- response = await Promise.race(promises);
71
- if (!response) {
72
- throw new Error(`Timed out the network response after ${this._networkTimeoutSeconds} seconds.`);
73
- }
74
- } catch (err) {
75
- if (err instanceof Error) {
76
- error = err;
77
- }
78
- }
79
-
80
- if (process.env.NODE_ENV !== "production") {
81
- logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
82
- if (response) {
83
- logger.log("Got response from network.");
84
- } else {
85
- logger.log("Unable to get a response from the network.");
86
- }
87
- messages.printFinalResponse(response);
88
- logger.groupEnd();
89
- }
90
-
91
- if (!response) {
92
- throw new SerwistError("no-response", { url: request.url, error });
93
- }
94
- return response;
95
- }
96
- }
97
-
98
- export { NetworkOnly };
99
- export type { NetworkOnlyOptions };
@@ -1,111 +0,0 @@
1
- /*
2
- Copyright 2018 Google LLC
3
-
4
- Use of this source code is governed by an MIT-style
5
- license that can be found in the LICENSE file or at
6
- https://opensource.org/licenses/MIT.
7
- */
8
-
9
- import { assert, SerwistError, logger } from "@serwist/core/internal";
10
-
11
- import type { StrategyOptions } from "./Strategy.js";
12
- import { Strategy } from "./Strategy.js";
13
- import type { StrategyHandler } from "./StrategyHandler.js";
14
- import { cacheOkAndOpaquePlugin } from "./plugins/cacheOkAndOpaquePlugin.js";
15
- import { messages } from "./utils/messages.js";
16
-
17
- /**
18
- * An implementation of a
19
- * [stale-while-revalidate](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#stale-while-revalidate)
20
- * request strategy.
21
- *
22
- * Resources are requested from both the cache and the network in parallel.
23
- * The strategy will respond with the cached version if available, otherwise
24
- * wait for the network response. The cache is updated with the network response
25
- * with each successful request.
26
- *
27
- * By default, this strategy will cache responses with a 200 status code as
28
- * well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
29
- * Opaque responses are cross-origin requests where the response doesn't
30
- * support [CORS](https://enable-cors.org/).
31
- *
32
- * If the network request fails, and there is no cache match, this will throw
33
- * a `SerwistError` exception.
34
- */
35
- class StaleWhileRevalidate extends Strategy {
36
- /**
37
- * @param options
38
- */
39
- constructor(options: StrategyOptions = {}) {
40
- super(options);
41
-
42
- // If this instance contains no plugins with a 'cacheWillUpdate' callback,
43
- // prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
44
- if (!this.plugins.some((p) => "cacheWillUpdate" in p)) {
45
- this.plugins.unshift(cacheOkAndOpaquePlugin);
46
- }
47
- }
48
-
49
- /**
50
- * @private
51
- * @param request A request to run this strategy for.
52
- * @param handler The event that triggered the request.
53
- * @returns
54
- */
55
- async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
56
- const logs = [];
57
-
58
- if (process.env.NODE_ENV !== "production") {
59
- assert!.isInstance(request, Request, {
60
- moduleName: "@serwist/strategies",
61
- className: this.constructor.name,
62
- funcName: "handle",
63
- paramName: "request",
64
- });
65
- }
66
-
67
- const fetchAndCachePromise = handler.fetchAndCachePut(request).catch(() => {
68
- // Swallow this error because a 'no-response' error will be thrown in
69
- // main handler return flow. This will be in the `waitUntil()` flow.
70
- });
71
- void handler.waitUntil(fetchAndCachePromise);
72
-
73
- let response = await handler.cacheMatch(request);
74
-
75
- let error: Error | undefined = undefined;
76
- if (response) {
77
- if (process.env.NODE_ENV !== "production") {
78
- logs.push(`Found a cached response in the '${this.cacheName}' cache. Will update with the network response in the background.`);
79
- }
80
- } else {
81
- if (process.env.NODE_ENV !== "production") {
82
- logs.push(`No response found in the '${this.cacheName}' cache. Will wait for the network response.`);
83
- }
84
- try {
85
- // NOTE(philipwalton): Really annoying that we have to type cast here.
86
- // https://github.com/microsoft/TypeScript/issues/20006
87
- response = (await fetchAndCachePromise) as Response | undefined;
88
- } catch (err) {
89
- if (err instanceof Error) {
90
- error = err;
91
- }
92
- }
93
- }
94
-
95
- if (process.env.NODE_ENV !== "production") {
96
- logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
97
- for (const log of logs) {
98
- logger.log(log);
99
- }
100
- messages.printFinalResponse(response);
101
- logger.groupEnd();
102
- }
103
-
104
- if (!response) {
105
- throw new SerwistError("no-response", { url: request.url, error });
106
- }
107
- return response;
108
- }
109
- }
110
-
111
- export { StaleWhileRevalidate };