@serwist/strategies 9.0.0-preview.9 → 9.0.1
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/index.d.ts +2 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -725
- package/package.json +6 -7
- package/src/index.ts +2 -34
- package/dist/CacheFirst.d.ts +0 -23
- package/dist/CacheFirst.d.ts.map +0 -1
- package/dist/CacheOnly.d.ts +0 -20
- package/dist/CacheOnly.d.ts.map +0 -1
- package/dist/NetworkFirst.d.ts +0 -60
- package/dist/NetworkFirst.d.ts.map +0 -1
- package/dist/NetworkOnly.d.ts +0 -31
- package/dist/NetworkOnly.d.ts.map +0 -1
- package/dist/StaleWhileRevalidate.d.ts +0 -34
- package/dist/StaleWhileRevalidate.d.ts.map +0 -1
- package/dist/Strategy.d.ts +0 -83
- package/dist/Strategy.d.ts.map +0 -1
- package/dist/StrategyHandler.d.ts +0 -187
- package/dist/StrategyHandler.d.ts.map +0 -1
- package/dist/plugins/cacheOkAndOpaquePlugin.d.ts +0 -3
- package/dist/plugins/cacheOkAndOpaquePlugin.d.ts.map +0 -1
- package/dist/utils/messages.d.ts +0 -5
- package/dist/utils/messages.d.ts.map +0 -1
- package/src/CacheFirst.ts +0 -87
- package/src/CacheOnly.ts +0 -58
- package/src/NetworkFirst.ts +0 -228
- package/src/NetworkOnly.ts +0 -95
- package/src/StaleWhileRevalidate.ts +0 -108
- package/src/Strategy.ts +0 -203
- package/src/StrategyHandler.ts +0 -544
- package/src/plugins/cacheOkAndOpaquePlugin.ts +0 -26
- package/src/utils/messages.ts +0 -20
package/src/CacheOnly.ts
DELETED
|
@@ -1,58 +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 the [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
|
-
export 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
|
-
}
|
package/src/NetworkFirst.ts
DELETED
|
@@ -1,228 +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
|
-
export class NetworkFirst extends Strategy {
|
|
25
|
-
private readonly _networkTimeoutSeconds: number;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* An implementation of the [network first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-first-falling-back-to-cache)
|
|
29
|
-
* request strategy.
|
|
30
|
-
*
|
|
31
|
-
* By default, this strategy will cache responses with a 200 status code as
|
|
32
|
-
* well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
|
|
33
|
-
* Opaque responses are are cross-origin requests where the response doesn't
|
|
34
|
-
* support [CORS](https://enable-cors.org/).
|
|
35
|
-
*
|
|
36
|
-
* If the network request fails, and there is no cache match, this will throw
|
|
37
|
-
* a `SerwistError` exception.
|
|
38
|
-
*
|
|
39
|
-
* @param options
|
|
40
|
-
* This option can be used to combat
|
|
41
|
-
* "[lie-fi](https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi)"
|
|
42
|
-
* scenarios.
|
|
43
|
-
*/
|
|
44
|
-
constructor(options: NetworkFirstOptions = {}) {
|
|
45
|
-
super(options);
|
|
46
|
-
|
|
47
|
-
// If this instance contains no plugins with a 'cacheWillUpdate' callback,
|
|
48
|
-
// prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
|
|
49
|
-
if (!this.plugins.some((p) => "cacheWillUpdate" in p)) {
|
|
50
|
-
this.plugins.unshift(cacheOkAndOpaquePlugin);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
|
|
54
|
-
if (process.env.NODE_ENV !== "production") {
|
|
55
|
-
if (this._networkTimeoutSeconds) {
|
|
56
|
-
assert!.isType(this._networkTimeoutSeconds, "number", {
|
|
57
|
-
moduleName: "@serwist/strategies",
|
|
58
|
-
className: this.constructor.name,
|
|
59
|
-
funcName: "constructor",
|
|
60
|
-
paramName: "networkTimeoutSeconds",
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @private
|
|
68
|
-
* @param request A request to run this strategy for.
|
|
69
|
-
* @param handler The event that triggered the request.
|
|
70
|
-
* @returns
|
|
71
|
-
*/
|
|
72
|
-
async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
|
|
73
|
-
const logs: any[] = [];
|
|
74
|
-
|
|
75
|
-
if (process.env.NODE_ENV !== "production") {
|
|
76
|
-
assert!.isInstance(request, Request, {
|
|
77
|
-
moduleName: "@serwist/strategies",
|
|
78
|
-
className: this.constructor.name,
|
|
79
|
-
funcName: "handle",
|
|
80
|
-
paramName: "makeRequest",
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const promises: Promise<Response | undefined>[] = [];
|
|
85
|
-
let timeoutId: number | undefined;
|
|
86
|
-
|
|
87
|
-
if (this._networkTimeoutSeconds) {
|
|
88
|
-
const { id, promise } = this._getTimeoutPromise({
|
|
89
|
-
request,
|
|
90
|
-
logs,
|
|
91
|
-
handler,
|
|
92
|
-
});
|
|
93
|
-
timeoutId = id;
|
|
94
|
-
promises.push(promise);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const networkPromise = this._getNetworkPromise({
|
|
98
|
-
timeoutId,
|
|
99
|
-
request,
|
|
100
|
-
logs,
|
|
101
|
-
handler,
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
promises.push(networkPromise);
|
|
105
|
-
|
|
106
|
-
const response = await handler.waitUntil(
|
|
107
|
-
(async () => {
|
|
108
|
-
// Promise.race() will resolve as soon as the first promise resolves.
|
|
109
|
-
return (
|
|
110
|
-
(await handler.waitUntil(Promise.race(promises))) ||
|
|
111
|
-
// If Promise.race() resolved with null, it might be due to a network
|
|
112
|
-
// timeout + a cache miss. If that were to happen, we'd rather wait until
|
|
113
|
-
// the networkPromise resolves instead of returning null.
|
|
114
|
-
// Note that it's fine to await an already-resolved promise, so we don't
|
|
115
|
-
// have to check to see if it's still "in flight".
|
|
116
|
-
(await networkPromise)
|
|
117
|
-
);
|
|
118
|
-
})(),
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
if (process.env.NODE_ENV !== "production") {
|
|
122
|
-
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
123
|
-
for (const log of logs) {
|
|
124
|
-
logger.log(log);
|
|
125
|
-
}
|
|
126
|
-
messages.printFinalResponse(response);
|
|
127
|
-
logger.groupEnd();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (!response) {
|
|
131
|
-
throw new SerwistError("no-response", { url: request.url });
|
|
132
|
-
}
|
|
133
|
-
return response;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* @param options
|
|
138
|
-
* @returns
|
|
139
|
-
* @private
|
|
140
|
-
*/
|
|
141
|
-
private _getTimeoutPromise({
|
|
142
|
-
request,
|
|
143
|
-
logs,
|
|
144
|
-
handler,
|
|
145
|
-
}: {
|
|
146
|
-
request: Request;
|
|
147
|
-
/**
|
|
148
|
-
* A reference to the logs array.
|
|
149
|
-
*/
|
|
150
|
-
logs: any[];
|
|
151
|
-
handler: StrategyHandler;
|
|
152
|
-
}): { promise: Promise<Response | undefined>; id?: number } {
|
|
153
|
-
// biome-ignore lint/suspicious/noImplicitAnyLet: setTimeout is typed with Node.js's typings, so we can't use number | undefined here.
|
|
154
|
-
let timeoutId;
|
|
155
|
-
const timeoutPromise: Promise<Response | undefined> = new Promise((resolve) => {
|
|
156
|
-
const onNetworkTimeout = async () => {
|
|
157
|
-
if (process.env.NODE_ENV !== "production") {
|
|
158
|
-
logs.push(`Timing out the network response at ${this._networkTimeoutSeconds} seconds.`);
|
|
159
|
-
}
|
|
160
|
-
resolve(await handler.cacheMatch(request));
|
|
161
|
-
};
|
|
162
|
-
timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1000);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
promise: timeoutPromise,
|
|
167
|
-
id: timeoutId,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* @param options
|
|
173
|
-
* @param options.timeoutId
|
|
174
|
-
* @param options.request
|
|
175
|
-
* @param options.logs A reference to the logs Array.
|
|
176
|
-
* @param options.event
|
|
177
|
-
* @returns
|
|
178
|
-
*
|
|
179
|
-
* @private
|
|
180
|
-
*/
|
|
181
|
-
async _getNetworkPromise({
|
|
182
|
-
timeoutId,
|
|
183
|
-
request,
|
|
184
|
-
logs,
|
|
185
|
-
handler,
|
|
186
|
-
}: {
|
|
187
|
-
request: Request;
|
|
188
|
-
logs: any[];
|
|
189
|
-
timeoutId?: number;
|
|
190
|
-
handler: StrategyHandler;
|
|
191
|
-
}): Promise<Response | undefined> {
|
|
192
|
-
let error: Error | undefined = undefined;
|
|
193
|
-
let response: Response | undefined = undefined;
|
|
194
|
-
try {
|
|
195
|
-
response = await handler.fetchAndCachePut(request);
|
|
196
|
-
} catch (fetchError) {
|
|
197
|
-
if (fetchError instanceof Error) {
|
|
198
|
-
error = fetchError;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (timeoutId) {
|
|
203
|
-
clearTimeout(timeoutId);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (process.env.NODE_ENV !== "production") {
|
|
207
|
-
if (response) {
|
|
208
|
-
logs.push("Got response from network.");
|
|
209
|
-
} else {
|
|
210
|
-
logs.push("Unable to get a response from the network. Will respond " + "with a cached response.");
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (error || !response) {
|
|
215
|
-
response = await handler.cacheMatch(request);
|
|
216
|
-
|
|
217
|
-
if (process.env.NODE_ENV !== "production") {
|
|
218
|
-
if (response) {
|
|
219
|
-
logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
|
|
220
|
-
} else {
|
|
221
|
-
logs.push(`No response found in the '${this.cacheName}' cache.`);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return response;
|
|
227
|
-
}
|
|
228
|
-
}
|
package/src/NetworkOnly.ts
DELETED
|
@@ -1,95 +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
|
-
export 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
|
-
export class NetworkOnly extends Strategy {
|
|
24
|
-
private readonly _networkTimeoutSeconds: number;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* An implementation of the [network only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only)
|
|
28
|
-
* request strategy.
|
|
29
|
-
*
|
|
30
|
-
* This class is useful if you want to take advantage of any Serwist plugin.
|
|
31
|
-
*
|
|
32
|
-
* If the network request fails, this will throw a `SerwistError` exception.
|
|
33
|
-
*
|
|
34
|
-
* @param options
|
|
35
|
-
*/
|
|
36
|
-
constructor(options: NetworkOnlyOptions = {}) {
|
|
37
|
-
super(options);
|
|
38
|
-
|
|
39
|
-
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @private
|
|
44
|
-
* @param request A request to run this strategy for.
|
|
45
|
-
* @param handler The event that triggered the request.
|
|
46
|
-
* @returns
|
|
47
|
-
*/
|
|
48
|
-
async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
|
|
49
|
-
if (process.env.NODE_ENV !== "production") {
|
|
50
|
-
assert!.isInstance(request, Request, {
|
|
51
|
-
moduleName: "@serwist/strategies",
|
|
52
|
-
className: this.constructor.name,
|
|
53
|
-
funcName: "_handle",
|
|
54
|
-
paramName: "request",
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let error: Error | undefined = undefined;
|
|
59
|
-
let response: Response | undefined;
|
|
60
|
-
|
|
61
|
-
try {
|
|
62
|
-
const promises: Promise<Response | undefined>[] = [handler.fetch(request)];
|
|
63
|
-
|
|
64
|
-
if (this._networkTimeoutSeconds) {
|
|
65
|
-
const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000) as Promise<undefined>;
|
|
66
|
-
promises.push(timeoutPromise);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
response = await Promise.race(promises);
|
|
70
|
-
if (!response) {
|
|
71
|
-
throw new Error(`Timed out the network response after ${this._networkTimeoutSeconds} seconds.`);
|
|
72
|
-
}
|
|
73
|
-
} catch (err) {
|
|
74
|
-
if (err instanceof Error) {
|
|
75
|
-
error = err;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (process.env.NODE_ENV !== "production") {
|
|
80
|
-
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
81
|
-
if (response) {
|
|
82
|
-
logger.log("Got response from network.");
|
|
83
|
-
} else {
|
|
84
|
-
logger.log("Unable to get a response from the network.");
|
|
85
|
-
}
|
|
86
|
-
messages.printFinalResponse(response);
|
|
87
|
-
logger.groupEnd();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (!response) {
|
|
91
|
-
throw new SerwistError("no-response", { url: request.url, error });
|
|
92
|
-
}
|
|
93
|
-
return response;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,108 +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 class StaleWhileRevalidate extends Strategy {
|
|
18
|
-
/**
|
|
19
|
-
* An implementation of the
|
|
20
|
-
* [stale-while-revalidate](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#stale-while-revalidate)
|
|
21
|
-
* request strategy.
|
|
22
|
-
*
|
|
23
|
-
* Resources are requested from both the cache and the network in parallel.
|
|
24
|
-
* The strategy will respond with the cached version if available, otherwise
|
|
25
|
-
* wait for the network response. The cache is updated with the network response
|
|
26
|
-
* with each successful request.
|
|
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 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
|
-
* @param options
|
|
37
|
-
*/
|
|
38
|
-
constructor(options: StrategyOptions = {}) {
|
|
39
|
-
super(options);
|
|
40
|
-
|
|
41
|
-
// If this instance contains no plugins with a 'cacheWillUpdate' callback,
|
|
42
|
-
// prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
|
|
43
|
-
if (!this.plugins.some((p) => "cacheWillUpdate" in p)) {
|
|
44
|
-
this.plugins.unshift(cacheOkAndOpaquePlugin);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* @private
|
|
50
|
-
* @param request A request to run this strategy for.
|
|
51
|
-
* @param handler The event that triggered the request.
|
|
52
|
-
* @returns
|
|
53
|
-
*/
|
|
54
|
-
async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
|
|
55
|
-
const logs = [];
|
|
56
|
-
|
|
57
|
-
if (process.env.NODE_ENV !== "production") {
|
|
58
|
-
assert!.isInstance(request, Request, {
|
|
59
|
-
moduleName: "@serwist/strategies",
|
|
60
|
-
className: this.constructor.name,
|
|
61
|
-
funcName: "handle",
|
|
62
|
-
paramName: "request",
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const fetchAndCachePromise = handler.fetchAndCachePut(request).catch(() => {
|
|
67
|
-
// Swallow this error because a 'no-response' error will be thrown in
|
|
68
|
-
// main handler return flow. This will be in the `waitUntil()` flow.
|
|
69
|
-
});
|
|
70
|
-
void handler.waitUntil(fetchAndCachePromise);
|
|
71
|
-
|
|
72
|
-
let response = await handler.cacheMatch(request);
|
|
73
|
-
|
|
74
|
-
let error: Error | undefined = undefined;
|
|
75
|
-
if (response) {
|
|
76
|
-
if (process.env.NODE_ENV !== "production") {
|
|
77
|
-
logs.push(`Found a cached response in the '${this.cacheName}' cache. Will update with the network response in the background.`);
|
|
78
|
-
}
|
|
79
|
-
} else {
|
|
80
|
-
if (process.env.NODE_ENV !== "production") {
|
|
81
|
-
logs.push(`No response found in the '${this.cacheName}' cache. Will wait for the network response.`);
|
|
82
|
-
}
|
|
83
|
-
try {
|
|
84
|
-
// NOTE(philipwalton): Really annoying that we have to type cast here.
|
|
85
|
-
// https://github.com/microsoft/TypeScript/issues/20006
|
|
86
|
-
response = (await fetchAndCachePromise) as Response | undefined;
|
|
87
|
-
} catch (err) {
|
|
88
|
-
if (err instanceof Error) {
|
|
89
|
-
error = err;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (process.env.NODE_ENV !== "production") {
|
|
95
|
-
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
96
|
-
for (const log of logs) {
|
|
97
|
-
logger.log(log);
|
|
98
|
-
}
|
|
99
|
-
messages.printFinalResponse(response);
|
|
100
|
-
logger.groupEnd();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (!response) {
|
|
104
|
-
throw new SerwistError("no-response", { url: request.url, error });
|
|
105
|
-
}
|
|
106
|
-
return response;
|
|
107
|
-
}
|
|
108
|
-
}
|
package/src/Strategy.ts
DELETED
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020 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 type { HandlerCallbackOptions, RouteHandlerObject, SerwistPlugin } from "@serwist/core";
|
|
10
|
-
import { SerwistError, getFriendlyURL, logger, privateCacheNames } from "@serwist/core/internal";
|
|
11
|
-
|
|
12
|
-
import { StrategyHandler } from "./StrategyHandler.js";
|
|
13
|
-
|
|
14
|
-
export interface StrategyOptions {
|
|
15
|
-
/**
|
|
16
|
-
* Cache name to store and retrieve requests. Defaults to cache names provided by `@serwist/core`.
|
|
17
|
-
*/
|
|
18
|
-
cacheName?: string;
|
|
19
|
-
/**
|
|
20
|
-
* [Plugins](https://developers.google.com/web/tools/workbox/guides/using-plugins)
|
|
21
|
-
* to use in conjunction with this caching strategy.
|
|
22
|
-
*/
|
|
23
|
-
plugins?: SerwistPlugin[];
|
|
24
|
-
/**
|
|
25
|
-
* Values passed along to the [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
|
|
26
|
-
* of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796) `fetch()` requests made by this strategy.
|
|
27
|
-
*/
|
|
28
|
-
fetchOptions?: RequestInit;
|
|
29
|
-
/**
|
|
30
|
-
* The [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)
|
|
31
|
-
* for any `cache.match()` or `cache.put()` calls made by this strategy.
|
|
32
|
-
*/
|
|
33
|
-
matchOptions?: CacheQueryOptions;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Classes extending the `Strategy` based class should implement this method,
|
|
38
|
-
* and leverage `@serwist/strategies`'s `StrategyHandler` arg to perform all
|
|
39
|
-
* fetching and cache logic, which will ensure all relevant cache, cache options,
|
|
40
|
-
* fetch options and plugins are used (per the current strategy instance).
|
|
41
|
-
*/
|
|
42
|
-
export abstract class Strategy implements RouteHandlerObject {
|
|
43
|
-
cacheName: string;
|
|
44
|
-
plugins: SerwistPlugin[];
|
|
45
|
-
fetchOptions?: RequestInit;
|
|
46
|
-
matchOptions?: CacheQueryOptions;
|
|
47
|
-
|
|
48
|
-
protected abstract _handle(request: Request, handler: StrategyHandler): Promise<Response | undefined>;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Creates a new instance of the strategy and sets all documented option
|
|
52
|
-
* properties as public instance properties.
|
|
53
|
-
*
|
|
54
|
-
* Note: if a custom strategy class extends the base Strategy class and does
|
|
55
|
-
* not need more than these properties, it does not need to define its own
|
|
56
|
-
* constructor.
|
|
57
|
-
*
|
|
58
|
-
* @param options
|
|
59
|
-
*/
|
|
60
|
-
constructor(options: StrategyOptions = {}) {
|
|
61
|
-
this.cacheName = privateCacheNames.getRuntimeName(options.cacheName);
|
|
62
|
-
this.plugins = options.plugins || [];
|
|
63
|
-
this.fetchOptions = options.fetchOptions;
|
|
64
|
-
this.matchOptions = options.matchOptions;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Perform a request strategy and returns a `Promise` that will resolve with
|
|
69
|
-
* a `Response`, invoking all relevant plugin callbacks.
|
|
70
|
-
*
|
|
71
|
-
* When a strategy instance is registered with a `@serwist/routing` Route, this method is automatically
|
|
72
|
-
* called when the route matches.
|
|
73
|
-
*
|
|
74
|
-
* Alternatively, this method can be used in a standalone `FetchEvent`
|
|
75
|
-
* listener by passing it to `event.respondWith()`.
|
|
76
|
-
*
|
|
77
|
-
* @param options A `FetchEvent` or an object with the properties listed below.
|
|
78
|
-
* @param options.request A request to run this strategy for.
|
|
79
|
-
* @param options.event The event associated with the request.
|
|
80
|
-
* @param options.url
|
|
81
|
-
* @param options.params
|
|
82
|
-
*/
|
|
83
|
-
handle(options: FetchEvent | HandlerCallbackOptions): Promise<Response> {
|
|
84
|
-
const [responseDone] = this.handleAll(options);
|
|
85
|
-
return responseDone;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Similar to `@serwist/strategies`'s `Strategy.handle`, but
|
|
90
|
-
* instead of just returning a `Promise` that resolves to a `Response` it
|
|
91
|
-
* it will return an tuple of `[response, done]` promises, where the former
|
|
92
|
-
* (`response`) is equivalent to what `handle()` returns, and the latter is a
|
|
93
|
-
* Promise that will resolve once any promises that were added to
|
|
94
|
-
* `event.waitUntil()` as part of performing the strategy have completed.
|
|
95
|
-
*
|
|
96
|
-
* You can await the `done` promise to ensure any extra work performed by
|
|
97
|
-
* the strategy (usually caching responses) completes successfully.
|
|
98
|
-
*
|
|
99
|
-
* @param options A `FetchEvent` or `HandlerCallbackOptions` object.
|
|
100
|
-
* @returns A tuple of [response, done] promises that can be used to determine when the response resolves as
|
|
101
|
-
* well as when the handler has completed all its work.
|
|
102
|
-
*/
|
|
103
|
-
handleAll(options: FetchEvent | HandlerCallbackOptions): [Promise<Response>, Promise<void>] {
|
|
104
|
-
// Allow for flexible options to be passed.
|
|
105
|
-
if (options instanceof FetchEvent) {
|
|
106
|
-
options = {
|
|
107
|
-
event: options,
|
|
108
|
-
request: options.request,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const event = options.event;
|
|
113
|
-
const request = typeof options.request === "string" ? new Request(options.request) : options.request;
|
|
114
|
-
const params = "params" in options ? options.params : undefined;
|
|
115
|
-
|
|
116
|
-
const handler = new StrategyHandler(this, { event, request, params });
|
|
117
|
-
|
|
118
|
-
const responseDone = this._getResponse(handler, request, event);
|
|
119
|
-
const handlerDone = this._awaitComplete(responseDone, handler, request, event);
|
|
120
|
-
|
|
121
|
-
// Return an array of promises, suitable for use with Promise.all().
|
|
122
|
-
return [responseDone, handlerDone];
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async _getResponse(handler: StrategyHandler, request: Request, event: ExtendableEvent): Promise<Response> {
|
|
126
|
-
await handler.runCallbacks("handlerWillStart", { event, request });
|
|
127
|
-
|
|
128
|
-
let response: Response | undefined = undefined;
|
|
129
|
-
try {
|
|
130
|
-
response = await this._handle(request, handler);
|
|
131
|
-
// The "official" Strategy subclasses all throw this error automatically,
|
|
132
|
-
// but in case a third-party Strategy doesn't, ensure that we have a
|
|
133
|
-
// consistent failure when there's no response or an error response.
|
|
134
|
-
if (response === undefined || response.type === "error") {
|
|
135
|
-
throw new SerwistError("no-response", { url: request.url });
|
|
136
|
-
}
|
|
137
|
-
} catch (error) {
|
|
138
|
-
if (error instanceof Error) {
|
|
139
|
-
for (const callback of handler.iterateCallbacks("handlerDidError")) {
|
|
140
|
-
response = await callback({ error, event, request });
|
|
141
|
-
if (response !== undefined) {
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (!response) {
|
|
148
|
-
throw error;
|
|
149
|
-
}
|
|
150
|
-
if (process.env.NODE_ENV !== "production") {
|
|
151
|
-
throw logger.log(
|
|
152
|
-
`While responding to '${getFriendlyURL(request.url)}', an ${
|
|
153
|
-
error instanceof Error ? error.toString() : ""
|
|
154
|
-
} error occurred. Using a fallback response provided by a handlerDidError plugin.`,
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
for (const callback of handler.iterateCallbacks("handlerWillRespond")) {
|
|
160
|
-
response = (await callback({ event, request, response })) as Response;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return response;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
async _awaitComplete(responseDone: Promise<Response>, handler: StrategyHandler, request: Request, event: ExtendableEvent): Promise<void> {
|
|
167
|
-
let response: Response | undefined = undefined;
|
|
168
|
-
let error: Error | undefined = undefined;
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
response = await responseDone;
|
|
172
|
-
} catch (error) {
|
|
173
|
-
// Ignore errors, as response errors should be caught via the `response`
|
|
174
|
-
// promise above. The `done` promise will only throw for errors in
|
|
175
|
-
// promises passed to `handler.waitUntil()`.
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
try {
|
|
179
|
-
await handler.runCallbacks("handlerDidRespond", {
|
|
180
|
-
event,
|
|
181
|
-
request,
|
|
182
|
-
response,
|
|
183
|
-
});
|
|
184
|
-
await handler.doneWaiting();
|
|
185
|
-
} catch (waitUntilError) {
|
|
186
|
-
if (waitUntilError instanceof Error) {
|
|
187
|
-
error = waitUntilError;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
await handler.runCallbacks("handlerDidComplete", {
|
|
192
|
-
event,
|
|
193
|
-
request,
|
|
194
|
-
response,
|
|
195
|
-
error,
|
|
196
|
-
});
|
|
197
|
-
handler.destroy();
|
|
198
|
-
|
|
199
|
-
if (error) {
|
|
200
|
-
throw error;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|