@serwist/sw 9.0.0-preview.21 → 9.0.0-preview.24
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 +4 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -156
- package/package.json +5 -40
- package/src/index.ts +5 -22
- package/README.md +0 -1
- package/dist/abstractions/Serwist.d.ts +0 -82
- package/dist/abstractions/Serwist.d.ts.map +0 -1
- package/dist/abstractions/disableDevLogs.d.ts +0 -7
- package/dist/abstractions/disableDevLogs.d.ts.map +0 -1
- package/dist/abstractions/fallbacks.d.ts +0 -39
- package/dist/abstractions/fallbacks.d.ts.map +0 -1
- package/dist/abstractions/handlePrecaching.d.ts +0 -39
- package/dist/abstractions/handlePrecaching.d.ts.map +0 -1
- package/dist/abstractions/installSerwist.d.ts +0 -15
- package/dist/abstractions/installSerwist.d.ts.map +0 -1
- package/dist/abstractions/navigationPreload.d.ts +0 -20
- package/dist/abstractions/navigationPreload.d.ts.map +0 -1
- package/dist/abstractions/registerRuntimeCaching.d.ts +0 -11
- package/dist/abstractions/registerRuntimeCaching.d.ts.map +0 -1
- package/dist/abstractions/types.d.ts +0 -29
- package/dist/abstractions/types.d.ts.map +0 -1
- package/dist/chunks/NavigationRoute.js +0 -54
- package/dist/chunks/NetworkOnly.js +0 -193
- package/dist/chunks/PrecacheFallbackPlugin.js +0 -573
- package/dist/chunks/Strategy.js +0 -410
- package/dist/chunks/precacheAndRoute.js +0 -113
- package/dist/chunks/registerRoute.js +0 -7
- package/dist/chunks/singletonPrecacheController.js +0 -433
- package/dist/chunks/singletonRouter.js +0 -435
- package/dist/index.plugins.d.ts +0 -41
- package/dist/index.plugins.d.ts.map +0 -1
- package/dist/index.plugins.js +0 -671
- package/dist/index.precaching.d.ts +0 -25
- package/dist/index.precaching.d.ts.map +0 -1
- package/dist/index.precaching.js +0 -24
- package/dist/index.routing.d.ts +0 -15
- package/dist/index.routing.d.ts.map +0 -1
- package/dist/index.routing.js +0 -19
- package/dist/index.strategies.d.ts +0 -22
- package/dist/index.strategies.d.ts.map +0 -1
- package/dist/index.strategies.js +0 -146
- package/dist/plugins/backgroundSync/BackgroundSyncPlugin.d.ts +0 -23
- package/dist/plugins/backgroundSync/BackgroundSyncPlugin.d.ts.map +0 -1
- package/dist/plugins/backgroundSync/Queue.d.ts +0 -166
- package/dist/plugins/backgroundSync/Queue.d.ts.map +0 -1
- package/dist/plugins/backgroundSync/QueueDb.d.ts +0 -90
- package/dist/plugins/backgroundSync/QueueDb.d.ts.map +0 -1
- package/dist/plugins/backgroundSync/QueueStore.d.ts +0 -75
- package/dist/plugins/backgroundSync/QueueStore.d.ts.map +0 -1
- package/dist/plugins/backgroundSync/StorableRequest.d.ts +0 -51
- package/dist/plugins/backgroundSync/StorableRequest.d.ts.map +0 -1
- package/dist/plugins/broadcastUpdate/BroadcastCacheUpdate.d.ts +0 -45
- package/dist/plugins/broadcastUpdate/BroadcastCacheUpdate.d.ts.map +0 -1
- package/dist/plugins/broadcastUpdate/BroadcastUpdatePlugin.d.ts +0 -27
- package/dist/plugins/broadcastUpdate/BroadcastUpdatePlugin.d.ts.map +0 -1
- package/dist/plugins/broadcastUpdate/constants.d.ts +0 -5
- package/dist/plugins/broadcastUpdate/constants.d.ts.map +0 -1
- package/dist/plugins/broadcastUpdate/responsesAreSame.d.ts +0 -11
- package/dist/plugins/broadcastUpdate/responsesAreSame.d.ts.map +0 -1
- package/dist/plugins/broadcastUpdate/types.d.ts +0 -34
- package/dist/plugins/broadcastUpdate/types.d.ts.map +0 -1
- package/dist/plugins/cacheableResponse/CacheableResponse.d.ts +0 -40
- package/dist/plugins/cacheableResponse/CacheableResponse.d.ts.map +0 -1
- package/dist/plugins/cacheableResponse/CacheableResponsePlugin.d.ts +0 -27
- package/dist/plugins/cacheableResponse/CacheableResponsePlugin.d.ts.map +0 -1
- package/dist/plugins/expiration/CacheExpiration.d.ts +0 -66
- package/dist/plugins/expiration/CacheExpiration.d.ts.map +0 -1
- package/dist/plugins/expiration/ExpirationPlugin.d.ts +0 -116
- package/dist/plugins/expiration/ExpirationPlugin.d.ts.map +0 -1
- package/dist/plugins/expiration/models/CacheTimestampsModel.d.ts +0 -73
- package/dist/plugins/expiration/models/CacheTimestampsModel.d.ts.map +0 -1
- package/dist/plugins/googleAnalytics/constants.d.ts +0 -10
- package/dist/plugins/googleAnalytics/constants.d.ts.map +0 -1
- package/dist/plugins/googleAnalytics/initialize.d.ts +0 -34
- package/dist/plugins/googleAnalytics/initialize.d.ts.map +0 -1
- package/dist/plugins/precaching/PrecacheFallbackPlugin.d.ts +0 -54
- package/dist/plugins/precaching/PrecacheFallbackPlugin.d.ts.map +0 -1
- package/dist/plugins/rangeRequests/RangeRequestsPlugin.d.ts +0 -19
- package/dist/plugins/rangeRequests/RangeRequestsPlugin.d.ts.map +0 -1
- package/dist/plugins/rangeRequests/createPartialResponse.d.ts +0 -18
- package/dist/plugins/rangeRequests/createPartialResponse.d.ts.map +0 -1
- package/dist/plugins/rangeRequests/utils/calculateEffectiveBoundaries.d.ts +0 -14
- package/dist/plugins/rangeRequests/utils/calculateEffectiveBoundaries.d.ts.map +0 -1
- package/dist/plugins/rangeRequests/utils/parseRangeHeader.d.ts +0 -12
- package/dist/plugins/rangeRequests/utils/parseRangeHeader.d.ts.map +0 -1
- package/dist/precaching/PrecacheController.d.ts +0 -145
- package/dist/precaching/PrecacheController.d.ts.map +0 -1
- package/dist/precaching/PrecacheRoute.d.ts +0 -20
- package/dist/precaching/PrecacheRoute.d.ts.map +0 -1
- package/dist/precaching/PrecacheStrategy.d.ts +0 -68
- package/dist/precaching/PrecacheStrategy.d.ts.map +0 -1
- package/dist/precaching/addPlugins.d.ts +0 -8
- package/dist/precaching/addPlugins.d.ts.map +0 -1
- package/dist/precaching/addRoute.d.ts +0 -15
- package/dist/precaching/addRoute.d.ts.map +0 -1
- package/dist/precaching/cleanupOutdatedCaches.d.ts +0 -6
- package/dist/precaching/cleanupOutdatedCaches.d.ts.map +0 -1
- package/dist/precaching/createHandlerBoundToURL.d.ts +0 -17
- package/dist/precaching/createHandlerBoundToURL.d.ts.map +0 -1
- package/dist/precaching/getCacheKeyForURL.d.ts +0 -20
- package/dist/precaching/getCacheKeyForURL.d.ts.map +0 -1
- package/dist/precaching/matchPrecache.d.ts +0 -14
- package/dist/precaching/matchPrecache.d.ts.map +0 -1
- package/dist/precaching/precache.d.ts +0 -19
- package/dist/precaching/precache.d.ts.map +0 -1
- package/dist/precaching/precacheAndRoute.d.ts +0 -14
- package/dist/precaching/precacheAndRoute.d.ts.map +0 -1
- package/dist/precaching/singletonPrecacheController.d.ts +0 -38
- package/dist/precaching/singletonPrecacheController.d.ts.map +0 -1
- package/dist/precaching/types.d.ts +0 -37
- package/dist/precaching/types.d.ts.map +0 -1
- package/dist/precaching/utils/PrecacheCacheKeyPlugin.d.ts +0 -17
- package/dist/precaching/utils/PrecacheCacheKeyPlugin.d.ts.map +0 -1
- package/dist/precaching/utils/PrecacheInstallReportPlugin.d.ts +0 -15
- package/dist/precaching/utils/PrecacheInstallReportPlugin.d.ts.map +0 -1
- package/dist/precaching/utils/createCacheKey.d.ts +0 -16
- package/dist/precaching/utils/createCacheKey.d.ts.map +0 -1
- package/dist/precaching/utils/deleteOutdatedCaches.d.ts +0 -18
- package/dist/precaching/utils/deleteOutdatedCaches.d.ts.map +0 -1
- package/dist/precaching/utils/generateURLVariations.d.ts +0 -12
- package/dist/precaching/utils/generateURLVariations.d.ts.map +0 -1
- package/dist/precaching/utils/getCacheKeyForURL.d.ts +0 -14
- package/dist/precaching/utils/getCacheKeyForURL.d.ts.map +0 -1
- package/dist/precaching/utils/printCleanupDetails.d.ts +0 -6
- package/dist/precaching/utils/printCleanupDetails.d.ts.map +0 -1
- package/dist/precaching/utils/printInstallDetails.d.ts +0 -7
- package/dist/precaching/utils/printInstallDetails.d.ts.map +0 -1
- package/dist/precaching/utils/removeIgnoredSearchParams.d.ts +0 -12
- package/dist/precaching/utils/removeIgnoredSearchParams.d.ts.map +0 -1
- package/dist/routing/NavigationRoute.d.ts +0 -57
- package/dist/routing/NavigationRoute.d.ts.map +0 -1
- package/dist/routing/RegExpRoute.d.ts +0 -24
- package/dist/routing/RegExpRoute.d.ts.map +0 -1
- package/dist/routing/Route.d.ts +0 -33
- package/dist/routing/Route.d.ts.map +0 -1
- package/dist/routing/Router.d.ts +0 -150
- package/dist/routing/Router.d.ts.map +0 -1
- package/dist/routing/parseRoute.d.ts +0 -16
- package/dist/routing/parseRoute.d.ts.map +0 -1
- package/dist/routing/registerRoute.d.ts +0 -15
- package/dist/routing/registerRoute.d.ts.map +0 -1
- package/dist/routing/setCatchHandler.d.ts +0 -9
- package/dist/routing/setCatchHandler.d.ts.map +0 -1
- package/dist/routing/setDefaultHandler.d.ts +0 -12
- package/dist/routing/setDefaultHandler.d.ts.map +0 -1
- package/dist/routing/singletonRouter.d.ts +0 -47
- package/dist/routing/singletonRouter.d.ts.map +0 -1
- package/dist/routing/unregisterRoute.d.ts +0 -8
- package/dist/routing/unregisterRoute.d.ts.map +0 -1
- package/dist/routing/utils/constants.d.ts +0 -15
- package/dist/routing/utils/constants.d.ts.map +0 -1
- package/dist/routing/utils/normalizeHandler.d.ts +0 -10
- package/dist/routing/utils/normalizeHandler.d.ts.map +0 -1
- package/dist/strategies/CacheFirst.d.ts +0 -23
- package/dist/strategies/CacheFirst.d.ts.map +0 -1
- package/dist/strategies/CacheOnly.d.ts +0 -20
- package/dist/strategies/CacheOnly.d.ts.map +0 -1
- package/dist/strategies/NetworkFirst.d.ts +0 -61
- package/dist/strategies/NetworkFirst.d.ts.map +0 -1
- package/dist/strategies/NetworkOnly.d.ts +0 -32
- package/dist/strategies/NetworkOnly.d.ts.map +0 -1
- package/dist/strategies/StaleWhileRevalidate.d.ts +0 -35
- package/dist/strategies/StaleWhileRevalidate.d.ts.map +0 -1
- package/dist/strategies/Strategy.d.ts +0 -83
- package/dist/strategies/Strategy.d.ts.map +0 -1
- package/dist/strategies/StrategyHandler.d.ts +0 -189
- package/dist/strategies/StrategyHandler.d.ts.map +0 -1
- package/dist/strategies/plugins/cacheOkAndOpaquePlugin.d.ts +0 -3
- package/dist/strategies/plugins/cacheOkAndOpaquePlugin.d.ts.map +0 -1
- package/dist/strategies/utils/messages.d.ts +0 -5
- package/dist/strategies/utils/messages.d.ts.map +0 -1
- package/src/abstractions/Serwist.ts +0 -177
- package/src/abstractions/disableDevLogs.ts +0 -10
- package/src/abstractions/fallbacks.ts +0 -65
- package/src/abstractions/handlePrecaching.ts +0 -65
- package/src/abstractions/installSerwist.ts +0 -28
- package/src/abstractions/navigationPreload.ts +0 -64
- package/src/abstractions/registerRuntimeCaching.ts +0 -17
- package/src/abstractions/types.ts +0 -29
- package/src/index.plugins.ts +0 -95
- package/src/index.precaching.ts +0 -41
- package/src/index.routing.ts +0 -28
- package/src/index.strategies.ts +0 -26
- package/src/plugins/backgroundSync/BackgroundSyncPlugin.ts +0 -39
- package/src/plugins/backgroundSync/Queue.ts +0 -438
- package/src/plugins/backgroundSync/QueueDb.ts +0 -176
- package/src/plugins/backgroundSync/QueueStore.ts +0 -161
- package/src/plugins/backgroundSync/StorableRequest.ts +0 -142
- package/src/plugins/broadcastUpdate/BroadcastCacheUpdate.ts +0 -159
- package/src/plugins/broadcastUpdate/BroadcastUpdatePlugin.ts +0 -43
- package/src/plugins/broadcastUpdate/constants.ts +0 -12
- package/src/plugins/broadcastUpdate/responsesAreSame.ts +0 -48
- package/src/plugins/broadcastUpdate/types.ts +0 -37
- package/src/plugins/cacheableResponse/CacheableResponse.ts +0 -141
- package/src/plugins/cacheableResponse/CacheableResponsePlugin.ts +0 -46
- package/src/plugins/expiration/CacheExpiration.ts +0 -192
- package/src/plugins/expiration/ExpirationPlugin.ts +0 -297
- package/src/plugins/expiration/models/CacheTimestampsModel.ts +0 -184
- package/src/plugins/googleAnalytics/constants.ts +0 -22
- package/src/plugins/googleAnalytics/initialize.ts +0 -213
- package/src/plugins/precaching/PrecacheFallbackPlugin.ts +0 -86
- package/src/plugins/rangeRequests/RangeRequestsPlugin.ts +0 -39
- package/src/plugins/rangeRequests/createPartialResponse.ts +0 -92
- package/src/plugins/rangeRequests/utils/calculateEffectiveBoundaries.ts +0 -58
- package/src/plugins/rangeRequests/utils/parseRangeHeader.ts +0 -54
- package/src/precaching/PrecacheController.ts +0 -332
- package/src/precaching/PrecacheRoute.ts +0 -50
- package/src/precaching/PrecacheStrategy.ts +0 -238
- package/src/precaching/addPlugins.ts +0 -21
- package/src/precaching/addRoute.ts +0 -30
- package/src/precaching/cleanupOutdatedCaches.ts +0 -33
- package/src/precaching/createHandlerBoundToURL.ts +0 -30
- package/src/precaching/getCacheKeyForURL.ts +0 -33
- package/src/precaching/matchPrecache.ts +0 -25
- package/src/precaching/precache.ts +0 -31
- package/src/precaching/precacheAndRoute.ts +0 -27
- package/src/precaching/singletonPrecacheController.ts +0 -57
- package/src/precaching/types.ts +0 -46
- package/src/precaching/utils/PrecacheCacheKeyPlugin.ts +0 -36
- package/src/precaching/utils/PrecacheInstallReportPlugin.ts +0 -49
- package/src/precaching/utils/createCacheKey.ts +0 -68
- package/src/precaching/utils/deleteOutdatedCaches.ts +0 -40
- package/src/precaching/utils/generateURLVariations.ts +0 -55
- package/src/precaching/utils/getCacheKeyForURL.ts +0 -36
- package/src/precaching/utils/printCleanupDetails.ts +0 -38
- package/src/precaching/utils/printInstallDetails.ts +0 -53
- package/src/precaching/utils/removeIgnoredSearchParams.ts +0 -29
- package/src/routing/NavigationRoute.ts +0 -119
- package/src/routing/RegExpRoute.ts +0 -74
- package/src/routing/Route.ts +0 -68
- package/src/routing/Router.ts +0 -481
- package/src/routing/parseRoute.ts +0 -78
- package/src/routing/registerRoute.ts +0 -27
- package/src/routing/setCatchHandler.ts +0 -21
- package/src/routing/setDefaultHandler.ts +0 -24
- package/src/routing/singletonRouter.ts +0 -76
- package/src/routing/unregisterRoute.ts +0 -11
- package/src/routing/utils/constants.ts +0 -24
- package/src/routing/utils/normalizeHandler.ts +0 -40
- package/src/strategies/CacheFirst.ts +0 -87
- package/src/strategies/CacheOnly.ts +0 -58
- package/src/strategies/NetworkFirst.ts +0 -228
- package/src/strategies/NetworkOnly.ts +0 -96
- package/src/strategies/StaleWhileRevalidate.ts +0 -109
- package/src/strategies/Strategy.ts +0 -202
- package/src/strategies/StrategyHandler.ts +0 -557
- package/src/strategies/plugins/cacheOkAndOpaquePlugin.ts +0 -26
- package/src/strategies/utils/messages.ts +0 -20
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2019 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 { Router } from "./Router.js";
|
|
10
|
-
|
|
11
|
-
let defaultRouter: Router | undefined = undefined;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Creates a new, singleton `Router` if one does not exist. If one does
|
|
15
|
-
* already exist, that instance is returned. This instance is used by
|
|
16
|
-
* Serwist's `Router`-dependent functions and classes unless you provide
|
|
17
|
-
* a different `Router` to them.
|
|
18
|
-
*
|
|
19
|
-
* @returns The singleton `Router`.
|
|
20
|
-
*/
|
|
21
|
-
export const getSingletonRouter = (): Router => {
|
|
22
|
-
if (!defaultRouter) {
|
|
23
|
-
defaultRouter = new Router();
|
|
24
|
-
|
|
25
|
-
// The helpers that use the default Router assume these listeners exist.
|
|
26
|
-
defaultRouter.addFetchListener();
|
|
27
|
-
defaultRouter.addCacheListener();
|
|
28
|
-
}
|
|
29
|
-
return defaultRouter;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Changes the singleton `Router` to a different instance. This is meant for when you do not
|
|
34
|
-
* want to pass your own `Router` to every one of Serwist's `Router`-dependent functions and classes.
|
|
35
|
-
* If this or `getSingletonRouter` has been called before, it removes the listeners of the
|
|
36
|
-
* previous singleton `Router`. It also adds those of the new one, so you need not do that yourself.
|
|
37
|
-
*
|
|
38
|
-
* It is highly recommended that you call this before anything else, if you plan on doing so.
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* ```js
|
|
42
|
-
* import { Router, setSingletonRouter } from "@serwist/sw/routing";
|
|
43
|
-
*
|
|
44
|
-
* const router = new Router();
|
|
45
|
-
*
|
|
46
|
-
* setSingletonRouter(router);
|
|
47
|
-
*
|
|
48
|
-
* router.registerRoute(
|
|
49
|
-
* new Route(
|
|
50
|
-
* /\/api\/.*\/*.json/,
|
|
51
|
-
* new NetworkOnly({
|
|
52
|
-
* plugins: [backgroundSync],
|
|
53
|
-
* }),
|
|
54
|
-
* "POST",
|
|
55
|
-
* ),
|
|
56
|
-
* );
|
|
57
|
-
*
|
|
58
|
-
* // This class now automatically picks up your `Router`! Without `setSingletonRouter`, you'd need to
|
|
59
|
-
* // write this instead: `new Serwist({ router })`
|
|
60
|
-
* const serwist = new Serwist();
|
|
61
|
-
*
|
|
62
|
-
* serwist.install();
|
|
63
|
-
* ```
|
|
64
|
-
* @param router
|
|
65
|
-
* @returns The new singleton `Router`.
|
|
66
|
-
*/
|
|
67
|
-
export const setSingletonRouter = (router: Router): Router => {
|
|
68
|
-
if (defaultRouter) {
|
|
69
|
-
defaultRouter.removeFetchListener();
|
|
70
|
-
defaultRouter.removeCacheListener();
|
|
71
|
-
}
|
|
72
|
-
defaultRouter = router;
|
|
73
|
-
defaultRouter.addFetchListener();
|
|
74
|
-
defaultRouter.addCacheListener();
|
|
75
|
-
return defaultRouter;
|
|
76
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Route } from "./Route.js";
|
|
2
|
-
import { getSingletonRouter } from "./singletonRouter.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Unregisters a route from the singleton `Router` instance.
|
|
6
|
-
*
|
|
7
|
-
* @param route The route to unregister.
|
|
8
|
-
*/
|
|
9
|
-
export const unregisterRoute = (route: Route): void => {
|
|
10
|
-
getSingletonRouter().unregisterRoute(route);
|
|
11
|
-
};
|
|
@@ -1,24 +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
|
-
export type HTTPMethod = "DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT";
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The default HTTP method, 'GET', used when there's no specific method
|
|
13
|
-
* configured for a route.
|
|
14
|
-
*
|
|
15
|
-
* @private
|
|
16
|
-
*/
|
|
17
|
-
export const defaultMethod = "GET" satisfies HTTPMethod;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* The list of valid HTTP methods associated with requests that could be routed.
|
|
21
|
-
*
|
|
22
|
-
* @private
|
|
23
|
-
*/
|
|
24
|
-
export const validMethods = ["DELETE", "GET", "HEAD", "PATCH", "POST", "PUT"] satisfies HTTPMethod[];
|
|
@@ -1,40 +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 type { RouteHandler, RouteHandlerObject } from "@serwist/core";
|
|
10
|
-
import { assert } from "@serwist/core/internal";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @param handler Either a function, or an object with a
|
|
14
|
-
* 'handle' method.
|
|
15
|
-
* @returns An object with a handle method.
|
|
16
|
-
*
|
|
17
|
-
* @private
|
|
18
|
-
*/
|
|
19
|
-
export const normalizeHandler = (handler: RouteHandler): RouteHandlerObject => {
|
|
20
|
-
if (handler && typeof handler === "object") {
|
|
21
|
-
if (process.env.NODE_ENV !== "production") {
|
|
22
|
-
assert!.hasMethod(handler, "handle", {
|
|
23
|
-
moduleName: "@serwist/routing",
|
|
24
|
-
className: "Route",
|
|
25
|
-
funcName: "constructor",
|
|
26
|
-
paramName: "handler",
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
return handler;
|
|
30
|
-
}
|
|
31
|
-
if (process.env.NODE_ENV !== "production") {
|
|
32
|
-
assert!.isType(handler, "function", {
|
|
33
|
-
moduleName: "@serwist/routing",
|
|
34
|
-
className: "Route",
|
|
35
|
-
funcName: "constructor",
|
|
36
|
-
paramName: "handler",
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
return { handle: handler };
|
|
40
|
-
};
|
|
@@ -1,87 +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 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
|
-
export 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
|
-
}
|
|
@@ -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
|
-
}
|
|
@@ -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
|
-
/**
|
|
25
|
-
* An implementation of the [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
|
-
export class NetworkFirst extends Strategy {
|
|
37
|
-
private readonly _networkTimeoutSeconds: number;
|
|
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
|
-
}
|
|
@@ -1,96 +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
|
-
/**
|
|
24
|
-
* An implementation of the [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
|
-
export 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
|
-
}
|