@amazon/vinyl-cache-manager 1.0.0
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.cjs +271 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.js +251 -0
- package/dist/index.js.map +7 -0
- package/dist/types/current/cacheManager/CacheControl.d.ts +67 -0
- package/dist/types/current/cacheManager/CacheControl.d.ts.map +1 -0
- package/dist/types/current/cacheManager/CacheControl.js +47 -0
- package/dist/types/current/cacheManager/CacheManager.d.ts +119 -0
- package/dist/types/current/cacheManager/CacheManager.d.ts.map +1 -0
- package/dist/types/current/cacheManager/CacheManager.js +221 -0
- package/dist/types/current/index.d.ts +4 -0
- package/dist/types/current/index.d.ts.map +1 -0
- package/dist/types/current/index.js +3 -0
- package/dist/types/current/tsconfig.tsbuildinfo +1 -0
- package/dist/types/ts3.7/cacheManager/CacheControl.d.ts +67 -0
- package/dist/types/ts3.7/cacheManager/CacheManager.d.ts +119 -0
- package/dist/types/ts3.7/index.d.ts +4 -0
- package/dist/types/ts4.1/cacheManager/CacheControl.d.ts +67 -0
- package/dist/types/ts4.1/cacheManager/CacheManager.d.ts +119 -0
- package/dist/types/ts4.1/index.d.ts +4 -0
- package/dist/types/ts4.7/cacheManager/CacheControl.d.ts +67 -0
- package/dist/types/ts4.7/cacheManager/CacheManager.d.ts +119 -0
- package/dist/types/ts4.7/index.d.ts +4 -0
- package/package.json +81 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __spreadValues = (a, b) => {
|
|
10
|
+
for (var prop in b || (b = {}))
|
|
11
|
+
if (__hasOwnProp.call(b, prop))
|
|
12
|
+
__defNormalProp(a, prop, b[prop]);
|
|
13
|
+
if (__getOwnPropSymbols)
|
|
14
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
+
if (__propIsEnum.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
}
|
|
18
|
+
return a;
|
|
19
|
+
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
23
|
+
};
|
|
24
|
+
var __copyProps = (to, from, except, desc) => {
|
|
25
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
26
|
+
for (let key of __getOwnPropNames(from))
|
|
27
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
28
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
29
|
+
}
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
34
|
+
|
|
35
|
+
// src/index.ts
|
|
36
|
+
var index_exports = {};
|
|
37
|
+
__export(index_exports, {
|
|
38
|
+
CacheManager: () => CacheManager,
|
|
39
|
+
DEFAULT_CACHE_NAME: () => DEFAULT_CACHE_NAME,
|
|
40
|
+
DEFAULT_PURGE_AFTER_STALE: () => DEFAULT_PURGE_AFTER_STALE,
|
|
41
|
+
NOT_CACHED_ERROR: () => NOT_CACHED_ERROR,
|
|
42
|
+
parseCacheControl: () => parseCacheControl
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(index_exports);
|
|
45
|
+
|
|
46
|
+
// src/cacheManager/CacheControl.ts
|
|
47
|
+
var knownDirectives = {
|
|
48
|
+
"max-age": "maxAge",
|
|
49
|
+
"no-cache": "noCache",
|
|
50
|
+
"no-store": "noStore",
|
|
51
|
+
"must-revalidate": "mustRevalidate",
|
|
52
|
+
public: "public",
|
|
53
|
+
private: "private",
|
|
54
|
+
immutable: "immutable",
|
|
55
|
+
"stale-while-revalidate": "staleWhileRevalidate",
|
|
56
|
+
"stale-if-error": "staleIfError",
|
|
57
|
+
"only-if-cached": "onlyIfCached"
|
|
58
|
+
};
|
|
59
|
+
function parseCacheControl(str) {
|
|
60
|
+
if (!str) return {};
|
|
61
|
+
const directives = {};
|
|
62
|
+
str.split(",").forEach((part) => {
|
|
63
|
+
const [key, value] = part.trim().split("=");
|
|
64
|
+
const directiveKey = key.trim().toLowerCase();
|
|
65
|
+
const typedKey = knownDirectives[directiveKey];
|
|
66
|
+
if (typedKey) {
|
|
67
|
+
if (value === void 0) {
|
|
68
|
+
directives[typedKey] = true;
|
|
69
|
+
} else {
|
|
70
|
+
const numericValue = parseInt(value, 10);
|
|
71
|
+
directives[typedKey] = isNaN(numericValue) ? value : numericValue;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return directives;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/cacheManager/CacheManager.ts
|
|
79
|
+
var DEFAULT_CACHE_NAME = "offlineCache";
|
|
80
|
+
var DEFAULT_PURGE_AFTER_STALE = 7 * 24 * 60 * 60;
|
|
81
|
+
var NOT_CACHED_ERROR = "Not cached";
|
|
82
|
+
var CACHE_KEY_PREFIX = "https://cache/";
|
|
83
|
+
var CACHED_TIMESTAMP_HEADER = "X-Cached-Timestamp";
|
|
84
|
+
var CacheManager = class {
|
|
85
|
+
constructor(options, deps) {
|
|
86
|
+
__publicField(this, "name");
|
|
87
|
+
__publicField(this, "caches");
|
|
88
|
+
__publicField(this, "cache");
|
|
89
|
+
__publicField(this, "fetch");
|
|
90
|
+
/**
|
|
91
|
+
* If set, when asynchronous caching has failed, this callback will be invoked.
|
|
92
|
+
* This is not invoked when a promise rejects during an operation.
|
|
93
|
+
*/
|
|
94
|
+
__publicField(this, "onError");
|
|
95
|
+
__publicField(this, "errorHandler", (error) => {
|
|
96
|
+
var _a;
|
|
97
|
+
(_a = this.onError) == null ? void 0 : _a.call(this, error);
|
|
98
|
+
});
|
|
99
|
+
var _a, _b, _c;
|
|
100
|
+
this.caches = (_a = deps == null ? void 0 : deps.caches) != null ? _a : caches;
|
|
101
|
+
this.name = (_b = options == null ? void 0 : options.name) != null ? _b : DEFAULT_CACHE_NAME;
|
|
102
|
+
this.cache = this.caches.open(this.name);
|
|
103
|
+
this.fetch = (_c = options == null ? void 0 : options.requestFromNetwork) != null ? _c : ((request) => fetch(request));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Caches a response with added expiry headers.
|
|
107
|
+
*/
|
|
108
|
+
async put(cacheId, response) {
|
|
109
|
+
if (response.bodyUsed)
|
|
110
|
+
throw new Error(`cannot cache request, body already used`);
|
|
111
|
+
const clonedResponse = response.clone();
|
|
112
|
+
const headers = new Headers(clonedResponse.headers);
|
|
113
|
+
headers.append(CACHED_TIMESTAMP_HEADER, Date.now().toString());
|
|
114
|
+
const responseWithHeader = new Response(clonedResponse.body, {
|
|
115
|
+
status: clonedResponse.status,
|
|
116
|
+
statusText: clonedResponse.statusText,
|
|
117
|
+
headers
|
|
118
|
+
});
|
|
119
|
+
const cache = await this.cache;
|
|
120
|
+
await cache.put(cacheId, responseWithHeader);
|
|
121
|
+
return response;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Deletes the request from this cache.
|
|
125
|
+
*
|
|
126
|
+
* @param cacheId The request or cache id used as the cache key.
|
|
127
|
+
*/
|
|
128
|
+
async delete(cacheId) {
|
|
129
|
+
const cache = await this.cache;
|
|
130
|
+
return await cache.delete(cacheId);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Resolves to true if the request is cached.
|
|
134
|
+
*
|
|
135
|
+
* @param cacheId
|
|
136
|
+
*/
|
|
137
|
+
async has(cacheId) {
|
|
138
|
+
return await this.getCached(cacheId) != null;
|
|
139
|
+
}
|
|
140
|
+
async get(request, options) {
|
|
141
|
+
var _a;
|
|
142
|
+
const cache = await this.cache;
|
|
143
|
+
const cacheId = (options == null ? void 0 : options.cacheKey) ? CACHE_KEY_PREFIX + options.cacheKey : request;
|
|
144
|
+
let cached;
|
|
145
|
+
if ((_a = options == null ? void 0 : options.cacheControlOverrides) == null ? void 0 : _a.noStore) {
|
|
146
|
+
this.delete(cacheId).catch(this.errorHandler);
|
|
147
|
+
cached = void 0;
|
|
148
|
+
} else {
|
|
149
|
+
cached = await cache.match(cacheId);
|
|
150
|
+
}
|
|
151
|
+
const cacheControlOptions = {
|
|
152
|
+
defaults: options == null ? void 0 : options.cacheControlDefaults,
|
|
153
|
+
overrides: options == null ? void 0 : options.cacheControlOverrides
|
|
154
|
+
};
|
|
155
|
+
const responseIsValid = async (response) => {
|
|
156
|
+
return response.ok && ((options == null ? void 0 : options.responseIsValid) == null || await options.responseIsValid(response.clone()));
|
|
157
|
+
};
|
|
158
|
+
const fetchAndMaybeCache = async () => {
|
|
159
|
+
return this.fetch(request).then(async (response) => {
|
|
160
|
+
const cacheControl2 = calculateCacheControl(
|
|
161
|
+
response,
|
|
162
|
+
cacheControlOptions
|
|
163
|
+
);
|
|
164
|
+
if (!cacheControl2.noStore && isCacheableRequestKey(cacheId) && await responseIsValid(response)) {
|
|
165
|
+
this.put(cacheId, response).catch(this.errorHandler);
|
|
166
|
+
}
|
|
167
|
+
return response;
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
const cacheControl = calculateCacheControl(cached, cacheControlOptions);
|
|
171
|
+
const cacheBehavior = calculateCacheBehavior(
|
|
172
|
+
cacheControl,
|
|
173
|
+
getCachedTimestamp(cached)
|
|
174
|
+
);
|
|
175
|
+
if (cacheBehavior.preferCache) {
|
|
176
|
+
if (cacheBehavior.reevaluate) {
|
|
177
|
+
fetchAndMaybeCache().catch(this.errorHandler);
|
|
178
|
+
}
|
|
179
|
+
return cached;
|
|
180
|
+
} else {
|
|
181
|
+
if (!cacheBehavior.allowNetwork && !cached) {
|
|
182
|
+
throw new Error(NOT_CACHED_ERROR);
|
|
183
|
+
}
|
|
184
|
+
const response = await fetchAndMaybeCache().catch((error) => {
|
|
185
|
+
if (!cacheBehavior.allowCache || !cached) throw error;
|
|
186
|
+
else return cached;
|
|
187
|
+
});
|
|
188
|
+
const ok = await responseIsValid(response);
|
|
189
|
+
return !ok ? cached != null ? cached : response : response;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Returns a Response if it exists in cache.
|
|
194
|
+
*
|
|
195
|
+
* @param request
|
|
196
|
+
*/
|
|
197
|
+
async getCached(request) {
|
|
198
|
+
const cache = await this.cache;
|
|
199
|
+
return await cache.match(request);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* @returns a promise that resolves to true if the Cache object is found and deleted, and false otherwise.
|
|
203
|
+
*/
|
|
204
|
+
async clear() {
|
|
205
|
+
const deleted = await this.caches.delete(this.name);
|
|
206
|
+
this.cache = this.caches.open(this.name);
|
|
207
|
+
return deleted;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Cleans stale items from the cache.
|
|
211
|
+
*/
|
|
212
|
+
async clean(options) {
|
|
213
|
+
var _a;
|
|
214
|
+
const cache = await this.cache;
|
|
215
|
+
const cacheControlOptions = {
|
|
216
|
+
defaults: {
|
|
217
|
+
// Unless the cached entry has a stale-while-revalidate directive, use this default.
|
|
218
|
+
staleWhileRevalidate: (_a = options == null ? void 0 : options.purgeAfterStale) != null ? _a : DEFAULT_PURGE_AFTER_STALE
|
|
219
|
+
},
|
|
220
|
+
overrides: {
|
|
221
|
+
noStore: false
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
for (const key of await cache.keys()) {
|
|
225
|
+
const cached = await cache.match(key);
|
|
226
|
+
const cacheControl = calculateCacheControl(
|
|
227
|
+
cached,
|
|
228
|
+
cacheControlOptions
|
|
229
|
+
);
|
|
230
|
+
const { preferCache } = calculateCacheBehavior(
|
|
231
|
+
cacheControl,
|
|
232
|
+
getCachedTimestamp(cached)
|
|
233
|
+
);
|
|
234
|
+
if (!preferCache) {
|
|
235
|
+
await cache.delete(key);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
function calculateCacheControl(cached, options) {
|
|
241
|
+
const cacheControl = cached ? parseCacheControl(cached.headers.get("Cache-Control")) : {};
|
|
242
|
+
return __spreadValues(__spreadValues(__spreadValues({}, options.defaults), cacheControl), options.overrides);
|
|
243
|
+
}
|
|
244
|
+
function calculateCacheBehavior(cacheControl, cachedTimestamp) {
|
|
245
|
+
var _a, _b, _c, _d;
|
|
246
|
+
const allowCache = !((_a = cacheControl.noStore) != null ? _a : false);
|
|
247
|
+
const maxAge = (_b = cacheControl.maxAge) != null ? _b : 0;
|
|
248
|
+
const staleAt = maxAge * 1e3 + (cachedTimestamp != null ? cachedTimestamp : Number.NEGATIVE_INFINITY);
|
|
249
|
+
const now = Date.now();
|
|
250
|
+
const isFresh = staleAt > now;
|
|
251
|
+
const immutable = (_c = cacheControl.immutable) != null ? _c : false;
|
|
252
|
+
const allowNetwork = !cacheControl.onlyIfCached;
|
|
253
|
+
const reevaluate = allowNetwork && !immutable && (cacheControl.mustRevalidate || !isFresh);
|
|
254
|
+
const staleWhileRevalidate = (_d = cacheControl.staleWhileRevalidate) != null ? _d : 0;
|
|
255
|
+
const preferCache = allowCache && (isFresh || staleAt + staleWhileRevalidate * 1e3 > now || immutable);
|
|
256
|
+
return {
|
|
257
|
+
preferCache,
|
|
258
|
+
reevaluate,
|
|
259
|
+
allowCache,
|
|
260
|
+
allowNetwork
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function isCacheableRequestKey(request) {
|
|
264
|
+
return typeof request === "string" || request.method === "GET";
|
|
265
|
+
}
|
|
266
|
+
function getCachedTimestamp(cached) {
|
|
267
|
+
const timestampHeader = cached == null ? void 0 : cached.headers.get(CACHED_TIMESTAMP_HEADER);
|
|
268
|
+
if (!timestampHeader) return void 0;
|
|
269
|
+
return parseInt(timestampHeader, 10);
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts", "../src/cacheManager/CacheControl.ts", "../src/cacheManager/CacheManager.ts"],
|
|
4
|
+
"sourcesContent": ["/** Auto-generated from build scripts */\nexport * from './cacheManager/CacheManager'\nexport * from './cacheManager/CacheControl'\n", "/*\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Represents all known Cache-Control directives.\n *\n * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control\n */\nexport type CacheControl = {\n /**\n * The maximum amount of time (in seconds) a resource is considered fresh.\n * Example: `max-age=3600` (1 hour)\n */\n readonly maxAge?: number\n\n /**\n * Requires the resource to be revalidated with the origin server before serving it from the cache.\n * Example: `no-cache`\n */\n readonly noCache?: boolean\n\n /**\n * Prevents the resource from being cached by any mechanism.\n * Example: `no-store`\n */\n readonly noStore?: boolean\n\n /**\n * Ensures that stale responses cannot be used without successful revalidation with the origin server.\n * Example: `must-revalidate`\n */\n readonly mustRevalidate?: boolean\n\n /**\n * Indicates the resource is cacheable by both shared and private caches.\n * Example: `public`\n */\n readonly public?: boolean\n\n /**\n * Indicates the resource is cacheable only by the user's browser or private caches, not shared caches.\n * Example: `private`\n */\n readonly private?: boolean\n\n /**\n * Indicates that the resource will not change, so it does not need to be revalidated.\n * Example: `immutable`\n */\n readonly immutable?: boolean\n\n /**\n * Allows serving stale responses for the specified duration (in seconds) while asynchronously revalidating the\n * cache.\n * Example: `stale-while-revalidate=60`\n */\n readonly staleWhileRevalidate?: number\n\n /**\n * Allows serving stale responses (in seconds) if the origin server is unavailable or returns an error.\n * Example: `stale-if-error=86400` (1 day)\n */\n readonly staleIfError?: number\n\n /**\n * Indicates the client only wants a response from the cache and will not make a network request.\n * Example: `only-if-cached`\n */\n readonly onlyIfCached?: boolean\n}\n\n// Map for known directive names to their typed keys\nconst knownDirectives: Record<string, keyof CacheControl | undefined> = {\n 'max-age': 'maxAge',\n 'no-cache': 'noCache',\n 'no-store': 'noStore',\n 'must-revalidate': 'mustRevalidate',\n public: 'public',\n private: 'private',\n immutable: 'immutable',\n 'stale-while-revalidate': 'staleWhileRevalidate',\n 'stale-if-error': 'staleIfError',\n 'only-if-cached': 'onlyIfCached',\n} as const\n\n/**\n * Parses a Cache-Control header string into a structured object.\n * Handles known directives with optional numeric values.\n *\n * @param str - The Cache-Control header string to parse.\n * @returns A CacheControl object representing the parsed directives.\n */\nexport function parseCacheControl(\n str: string | null | undefined\n): CacheControl {\n if (!str) return {}\n const directives: any = {}\n str.split(',').forEach((part) => {\n const [key, value] = part.trim().split('=') as [string, string?]\n const directiveKey = key.trim().toLowerCase() // Ensure case-insensitivity\n\n const typedKey = knownDirectives[directiveKey]\n if (typedKey) {\n // Parse the value or set to true for boolean directives\n if (value === undefined) {\n directives[typedKey] = true\n } else {\n const numericValue = parseInt(value, 10)\n directives[typedKey] = isNaN(numericValue)\n ? value\n : numericValue\n }\n }\n })\n return directives\n}\n", "/*\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * Offline utility that can be shared between the service worker and application.\n * Note - this should not use any dependencies; the bundled service worker must\n * be lightweight.\n */\n\nimport { type CacheControl, parseCacheControl } from './CacheControl'\n\nexport const DEFAULT_CACHE_NAME = 'offlineCache'\nexport const DEFAULT_PURGE_AFTER_STALE = 7 * 24 * 60 * 60\n\n/**\n * If the cache control options set the onlyIfCached directive and the request is not cached, an error will be thrown.\n */\nexport const NOT_CACHED_ERROR = 'Not cached'\n\nexport type CacheManagerOptions = {\n /**\n * The name of the cache key.\n */\n readonly name?: string\n\n /**\n * The Fetch from network method. Defaults to window.fetch.\n */\n readonly requestFromNetwork?: Fetch\n}\n\ntype Fetch = (request: RequestInfo) => Promise<Response>\n\n/**\n * A prefix for cache keys in order to guarantee they can be used as cache keys.\n * WebKit browsers only allow http/https cache keys.\n */\nconst CACHE_KEY_PREFIX = 'https://cache/'\n\n/**\n * A custom header for storing the timestamp at which the entry was cached.\n */\nconst CACHED_TIMESTAMP_HEADER = 'X-Cached-Timestamp'\n\nexport type CacheEntryOptions = {\n /**\n * If set, the response will be cached under this identifier, not based on the request.\n * This is required for caching POST requests.\n */\n readonly cacheKey?: string | null | undefined\n\n /**\n * Cache control directive defaults.\n * These will be used unless overridden by the response headers or the cache control overrides.\n *\n * CacheControl directives will be overwritten in the order:\n * - cacheControlDefaults\n * - response headers\n * - cacheControlOverrides\n */\n readonly cacheControlDefaults?: CacheControl\n\n /**\n * Allows the client to set cache control overrides. This object is merged with the actual cache control header.\n *\n * For example, to allow cached items to be used 1 day after they are stale while asynchronously revalidating\n * the cache, set `{ staleWhileRevalidate: 24 * 60 * 60 }`\n *\n * These overrides do not change the header cached, only how the cached header is read.\n * If noStore is true, the entry will not be cached.\n */\n readonly cacheControlOverrides?: CacheControl\n\n /**\n * If set, checks the response if it should be used. This affects whether to cache and whether to use the\n * cached fallback.\n */\n readonly responseIsValid?: (response: Response) => Promise<boolean>\n}\n\nexport type CacheCleanOptions = {\n /**\n * The number of seconds after an entry becomes stale before it should be removed from the cache.\n * Default: 7 * 24 * 60 * 60 (7 days).\n * If the cached entry has a stale-while-revalidate cache control directive, this will be ignored.\n */\n readonly purgeAfterStale?: number\n}\n\nexport interface CacheManagerDeps {\n /**\n * The cache storages api.\n * Default: window.caches\n */\n readonly caches?: CacheStorage\n\n /**\n * Creates a Headers object\n */\n readonly headersFactory?: (headersInit?: HeadersInit) => Headers\n\n /**\n * Creates a Response object\n */\n readonly responseFactory?: (\n body?: BodyInit | null,\n init?: ResponseInit\n ) => Response\n}\n\nexport class CacheManager {\n readonly name: string\n\n private readonly caches: CacheStorage\n private cache: Promise<Cache>\n private readonly fetch: Fetch\n\n /**\n * If set, when asynchronous caching has failed, this callback will be invoked.\n * This is not invoked when a promise rejects during an operation.\n */\n onError?: (error: Error) => void\n\n constructor(options?: CacheManagerOptions, deps?: CacheManagerDeps) {\n this.caches = deps?.caches ?? caches\n this.name = options?.name ?? DEFAULT_CACHE_NAME\n this.cache = this.caches.open(this.name)\n this.fetch =\n options?.requestFromNetwork ?? ((request) => fetch(request))\n }\n\n private readonly errorHandler = (error: Error) => {\n this.onError?.(error)\n }\n\n /**\n * Caches a response with added expiry headers.\n */\n async put(cacheId: RequestInfo, response: Response): Promise<Response> {\n if (response.bodyUsed)\n throw new Error(`cannot cache request, body already used`)\n\n const clonedResponse = response.clone()\n\n // Add a timestamp header\n const headers = new Headers(clonedResponse.headers)\n headers.append(CACHED_TIMESTAMP_HEADER, Date.now().toString())\n\n const responseWithHeader = new Response(clonedResponse.body, {\n status: clonedResponse.status,\n statusText: clonedResponse.statusText,\n headers,\n })\n\n const cache = await this.cache\n await cache.put(cacheId, responseWithHeader)\n return response\n }\n\n /**\n * Deletes the request from this cache.\n *\n * @param cacheId The request or cache id used as the cache key.\n */\n async delete(cacheId: RequestInfo): Promise<boolean> {\n const cache = await this.cache\n return await cache.delete(cacheId)\n }\n\n /**\n * Resolves to true if the request is cached.\n *\n * @param cacheId\n */\n async has(cacheId: RequestInfo): Promise<boolean> {\n return (await this.getCached(cacheId)) != null\n }\n\n async get(\n request: RequestInfo,\n options?: CacheEntryOptions\n ): Promise<Response> {\n const cache = await this.cache\n\n const cacheId = options?.cacheKey\n ? CACHE_KEY_PREFIX + options.cacheKey\n : request\n\n let cached: Response | undefined\n if (options?.cacheControlOverrides?.noStore) {\n this.delete(cacheId).catch(this.errorHandler)\n cached = undefined\n } else {\n cached = await cache.match(cacheId)\n }\n\n const cacheControlOptions: CacheControlOptions = {\n defaults: options?.cacheControlDefaults,\n overrides: options?.cacheControlOverrides,\n }\n\n const responseIsValid = async (\n response: Response\n ): Promise<boolean> => {\n return (\n response.ok &&\n (options?.responseIsValid == null ||\n (await options.responseIsValid(response.clone())))\n )\n }\n\n const fetchAndMaybeCache = async (): Promise<Response> => {\n return this.fetch(request).then(async (response) => {\n const cacheControl = calculateCacheControl(\n response,\n cacheControlOptions\n )\n if (\n !cacheControl.noStore &&\n isCacheableRequestKey(cacheId) &&\n (await responseIsValid(response))\n ) {\n this.put(cacheId, response).catch(this.errorHandler)\n }\n return response\n })\n }\n const cacheControl = calculateCacheControl(cached, cacheControlOptions)\n const cacheBehavior = calculateCacheBehavior(\n cacheControl,\n getCachedTimestamp(cached)\n )\n if (cacheBehavior.preferCache) {\n if (cacheBehavior.reevaluate) {\n fetchAndMaybeCache().catch(this.errorHandler)\n }\n return cached!\n } else {\n if (!cacheBehavior.allowNetwork && !cached) {\n throw new Error(NOT_CACHED_ERROR)\n }\n // Prefer a network response. If the network response isn't valid, return the cached value.\n const response = await fetchAndMaybeCache().catch((error) => {\n if (!cacheBehavior.allowCache || !cached) throw error\n else return cached\n })\n const ok = await responseIsValid(response)\n return !ok ? (cached ?? response) : response\n }\n }\n\n /**\n * Returns a Response if it exists in cache.\n *\n * @param request\n */\n private async getCached(\n request: RequestInfo\n ): Promise<Response | undefined> {\n const cache = await this.cache\n return await cache.match(request)\n }\n\n /**\n * @returns a promise that resolves to true if the Cache object is found and deleted, and false otherwise.\n */\n async clear(): Promise<boolean> {\n const deleted = await this.caches.delete(this.name)\n this.cache = this.caches.open(this.name)\n return deleted\n }\n\n /**\n * Cleans stale items from the cache.\n */\n async clean(options?: CacheCleanOptions) {\n const cache = await this.cache\n const cacheControlOptions: CacheControlOptions = {\n defaults: {\n // Unless the cached entry has a stale-while-revalidate directive, use this default.\n staleWhileRevalidate:\n options?.purgeAfterStale ?? DEFAULT_PURGE_AFTER_STALE,\n },\n overrides: {\n noStore: false,\n },\n }\n for (const key of await cache.keys()) {\n const cached = await cache.match(key)\n const cacheControl = calculateCacheControl(\n cached,\n cacheControlOptions\n )\n const { preferCache } = calculateCacheBehavior(\n cacheControl,\n getCachedTimestamp(cached)\n )\n if (!preferCache) {\n await cache.delete(key)\n }\n }\n }\n}\n\ntype CacheControlOptions = {\n readonly defaults?: CacheControl | undefined\n readonly overrides?: CacheControl | undefined\n}\n\nfunction calculateCacheControl(\n cached: Response | undefined,\n options: CacheControlOptions\n): CacheControl {\n const cacheControl = cached\n ? parseCacheControl(cached.headers.get('Cache-Control'))\n : {}\n return {\n ...options.defaults,\n ...cacheControl,\n ...options.overrides,\n }\n}\n\ntype CacheBehavior = {\n /**\n * If false, do not use the cached entry. If it exists, delete it.\n */\n readonly allowCache: boolean\n\n /**\n * Prefer the cached result.\n * If false, the cache may be used if `allowCache` is true and the request fails.\n */\n readonly preferCache: boolean\n\n /**\n * If false, do not request from network, even if there is not a cached resource.\n */\n readonly allowNetwork: boolean\n\n /**\n * Load the resource from the network.\n * If preferCache is true, the cached response will be used and a new request will be made asynchronously.\n */\n readonly reevaluate: boolean\n}\n\nfunction calculateCacheBehavior(\n cacheControl: CacheControl,\n cachedTimestamp: number | undefined\n): CacheBehavior {\n const allowCache = !(cacheControl.noStore ?? false)\n const maxAge = cacheControl.maxAge ?? 0\n const staleAt =\n maxAge * 1000 + (cachedTimestamp ?? Number.NEGATIVE_INFINITY)\n\n const now = Date.now()\n const isFresh = staleAt > now\n const immutable = cacheControl.immutable ?? false\n const allowNetwork = !cacheControl.onlyIfCached\n const reevaluate =\n allowNetwork && !immutable && (cacheControl.mustRevalidate || !isFresh)\n // If the entry is past its max age and there is staleWhileRevalidate set, allow the cached entry to be used\n // staleWhileRevalidate seconds after maxAge.\n const staleWhileRevalidate = cacheControl.staleWhileRevalidate ?? 0\n const preferCache =\n allowCache &&\n (isFresh || staleAt + staleWhileRevalidate * 1000 > now || immutable)\n return {\n preferCache,\n reevaluate,\n allowCache,\n allowNetwork,\n }\n}\n\n/**\n * Returns true if the request can be used as a Cache key.\n */\nfunction isCacheableRequestKey(request: RequestInfo): boolean {\n return typeof request === 'string' || request.method === 'GET'\n}\n\nfunction getCachedTimestamp(cached: Response | undefined): number | undefined {\n const timestampHeader = cached?.headers.get(CACHED_TIMESTAMP_HEADER)\n if (!timestampHeader) return undefined\n return parseInt(timestampHeader, 10)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["cacheControl"]
|
|
7
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __spreadValues = (a, b) => {
|
|
7
|
+
for (var prop in b || (b = {}))
|
|
8
|
+
if (__hasOwnProp.call(b, prop))
|
|
9
|
+
__defNormalProp(a, prop, b[prop]);
|
|
10
|
+
if (__getOwnPropSymbols)
|
|
11
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
+
if (__propIsEnum.call(b, prop))
|
|
13
|
+
__defNormalProp(a, prop, b[prop]);
|
|
14
|
+
}
|
|
15
|
+
return a;
|
|
16
|
+
};
|
|
17
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
18
|
+
|
|
19
|
+
// src/cacheManager/CacheControl.ts
|
|
20
|
+
var knownDirectives = {
|
|
21
|
+
"max-age": "maxAge",
|
|
22
|
+
"no-cache": "noCache",
|
|
23
|
+
"no-store": "noStore",
|
|
24
|
+
"must-revalidate": "mustRevalidate",
|
|
25
|
+
public: "public",
|
|
26
|
+
private: "private",
|
|
27
|
+
immutable: "immutable",
|
|
28
|
+
"stale-while-revalidate": "staleWhileRevalidate",
|
|
29
|
+
"stale-if-error": "staleIfError",
|
|
30
|
+
"only-if-cached": "onlyIfCached"
|
|
31
|
+
};
|
|
32
|
+
function parseCacheControl(str) {
|
|
33
|
+
if (!str) return {};
|
|
34
|
+
const directives = {};
|
|
35
|
+
str.split(",").forEach((part) => {
|
|
36
|
+
const [key, value] = part.trim().split("=");
|
|
37
|
+
const directiveKey = key.trim().toLowerCase();
|
|
38
|
+
const typedKey = knownDirectives[directiveKey];
|
|
39
|
+
if (typedKey) {
|
|
40
|
+
if (value === void 0) {
|
|
41
|
+
directives[typedKey] = true;
|
|
42
|
+
} else {
|
|
43
|
+
const numericValue = parseInt(value, 10);
|
|
44
|
+
directives[typedKey] = isNaN(numericValue) ? value : numericValue;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return directives;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/cacheManager/CacheManager.ts
|
|
52
|
+
var DEFAULT_CACHE_NAME = "offlineCache";
|
|
53
|
+
var DEFAULT_PURGE_AFTER_STALE = 7 * 24 * 60 * 60;
|
|
54
|
+
var NOT_CACHED_ERROR = "Not cached";
|
|
55
|
+
var CACHE_KEY_PREFIX = "https://cache/";
|
|
56
|
+
var CACHED_TIMESTAMP_HEADER = "X-Cached-Timestamp";
|
|
57
|
+
var CacheManager = class {
|
|
58
|
+
constructor(options, deps) {
|
|
59
|
+
__publicField(this, "name");
|
|
60
|
+
__publicField(this, "caches");
|
|
61
|
+
__publicField(this, "cache");
|
|
62
|
+
__publicField(this, "fetch");
|
|
63
|
+
/**
|
|
64
|
+
* If set, when asynchronous caching has failed, this callback will be invoked.
|
|
65
|
+
* This is not invoked when a promise rejects during an operation.
|
|
66
|
+
*/
|
|
67
|
+
__publicField(this, "onError");
|
|
68
|
+
__publicField(this, "errorHandler", (error) => {
|
|
69
|
+
var _a;
|
|
70
|
+
(_a = this.onError) == null ? void 0 : _a.call(this, error);
|
|
71
|
+
});
|
|
72
|
+
var _a, _b, _c;
|
|
73
|
+
this.caches = (_a = deps == null ? void 0 : deps.caches) != null ? _a : caches;
|
|
74
|
+
this.name = (_b = options == null ? void 0 : options.name) != null ? _b : DEFAULT_CACHE_NAME;
|
|
75
|
+
this.cache = this.caches.open(this.name);
|
|
76
|
+
this.fetch = (_c = options == null ? void 0 : options.requestFromNetwork) != null ? _c : ((request) => fetch(request));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Caches a response with added expiry headers.
|
|
80
|
+
*/
|
|
81
|
+
async put(cacheId, response) {
|
|
82
|
+
if (response.bodyUsed)
|
|
83
|
+
throw new Error(`cannot cache request, body already used`);
|
|
84
|
+
const clonedResponse = response.clone();
|
|
85
|
+
const headers = new Headers(clonedResponse.headers);
|
|
86
|
+
headers.append(CACHED_TIMESTAMP_HEADER, Date.now().toString());
|
|
87
|
+
const responseWithHeader = new Response(clonedResponse.body, {
|
|
88
|
+
status: clonedResponse.status,
|
|
89
|
+
statusText: clonedResponse.statusText,
|
|
90
|
+
headers
|
|
91
|
+
});
|
|
92
|
+
const cache = await this.cache;
|
|
93
|
+
await cache.put(cacheId, responseWithHeader);
|
|
94
|
+
return response;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Deletes the request from this cache.
|
|
98
|
+
*
|
|
99
|
+
* @param cacheId The request or cache id used as the cache key.
|
|
100
|
+
*/
|
|
101
|
+
async delete(cacheId) {
|
|
102
|
+
const cache = await this.cache;
|
|
103
|
+
return await cache.delete(cacheId);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Resolves to true if the request is cached.
|
|
107
|
+
*
|
|
108
|
+
* @param cacheId
|
|
109
|
+
*/
|
|
110
|
+
async has(cacheId) {
|
|
111
|
+
return await this.getCached(cacheId) != null;
|
|
112
|
+
}
|
|
113
|
+
async get(request, options) {
|
|
114
|
+
var _a;
|
|
115
|
+
const cache = await this.cache;
|
|
116
|
+
const cacheId = (options == null ? void 0 : options.cacheKey) ? CACHE_KEY_PREFIX + options.cacheKey : request;
|
|
117
|
+
let cached;
|
|
118
|
+
if ((_a = options == null ? void 0 : options.cacheControlOverrides) == null ? void 0 : _a.noStore) {
|
|
119
|
+
this.delete(cacheId).catch(this.errorHandler);
|
|
120
|
+
cached = void 0;
|
|
121
|
+
} else {
|
|
122
|
+
cached = await cache.match(cacheId);
|
|
123
|
+
}
|
|
124
|
+
const cacheControlOptions = {
|
|
125
|
+
defaults: options == null ? void 0 : options.cacheControlDefaults,
|
|
126
|
+
overrides: options == null ? void 0 : options.cacheControlOverrides
|
|
127
|
+
};
|
|
128
|
+
const responseIsValid = async (response) => {
|
|
129
|
+
return response.ok && ((options == null ? void 0 : options.responseIsValid) == null || await options.responseIsValid(response.clone()));
|
|
130
|
+
};
|
|
131
|
+
const fetchAndMaybeCache = async () => {
|
|
132
|
+
return this.fetch(request).then(async (response) => {
|
|
133
|
+
const cacheControl2 = calculateCacheControl(
|
|
134
|
+
response,
|
|
135
|
+
cacheControlOptions
|
|
136
|
+
);
|
|
137
|
+
if (!cacheControl2.noStore && isCacheableRequestKey(cacheId) && await responseIsValid(response)) {
|
|
138
|
+
this.put(cacheId, response).catch(this.errorHandler);
|
|
139
|
+
}
|
|
140
|
+
return response;
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
const cacheControl = calculateCacheControl(cached, cacheControlOptions);
|
|
144
|
+
const cacheBehavior = calculateCacheBehavior(
|
|
145
|
+
cacheControl,
|
|
146
|
+
getCachedTimestamp(cached)
|
|
147
|
+
);
|
|
148
|
+
if (cacheBehavior.preferCache) {
|
|
149
|
+
if (cacheBehavior.reevaluate) {
|
|
150
|
+
fetchAndMaybeCache().catch(this.errorHandler);
|
|
151
|
+
}
|
|
152
|
+
return cached;
|
|
153
|
+
} else {
|
|
154
|
+
if (!cacheBehavior.allowNetwork && !cached) {
|
|
155
|
+
throw new Error(NOT_CACHED_ERROR);
|
|
156
|
+
}
|
|
157
|
+
const response = await fetchAndMaybeCache().catch((error) => {
|
|
158
|
+
if (!cacheBehavior.allowCache || !cached) throw error;
|
|
159
|
+
else return cached;
|
|
160
|
+
});
|
|
161
|
+
const ok = await responseIsValid(response);
|
|
162
|
+
return !ok ? cached != null ? cached : response : response;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Returns a Response if it exists in cache.
|
|
167
|
+
*
|
|
168
|
+
* @param request
|
|
169
|
+
*/
|
|
170
|
+
async getCached(request) {
|
|
171
|
+
const cache = await this.cache;
|
|
172
|
+
return await cache.match(request);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* @returns a promise that resolves to true if the Cache object is found and deleted, and false otherwise.
|
|
176
|
+
*/
|
|
177
|
+
async clear() {
|
|
178
|
+
const deleted = await this.caches.delete(this.name);
|
|
179
|
+
this.cache = this.caches.open(this.name);
|
|
180
|
+
return deleted;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Cleans stale items from the cache.
|
|
184
|
+
*/
|
|
185
|
+
async clean(options) {
|
|
186
|
+
var _a;
|
|
187
|
+
const cache = await this.cache;
|
|
188
|
+
const cacheControlOptions = {
|
|
189
|
+
defaults: {
|
|
190
|
+
// Unless the cached entry has a stale-while-revalidate directive, use this default.
|
|
191
|
+
staleWhileRevalidate: (_a = options == null ? void 0 : options.purgeAfterStale) != null ? _a : DEFAULT_PURGE_AFTER_STALE
|
|
192
|
+
},
|
|
193
|
+
overrides: {
|
|
194
|
+
noStore: false
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
for (const key of await cache.keys()) {
|
|
198
|
+
const cached = await cache.match(key);
|
|
199
|
+
const cacheControl = calculateCacheControl(
|
|
200
|
+
cached,
|
|
201
|
+
cacheControlOptions
|
|
202
|
+
);
|
|
203
|
+
const { preferCache } = calculateCacheBehavior(
|
|
204
|
+
cacheControl,
|
|
205
|
+
getCachedTimestamp(cached)
|
|
206
|
+
);
|
|
207
|
+
if (!preferCache) {
|
|
208
|
+
await cache.delete(key);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
function calculateCacheControl(cached, options) {
|
|
214
|
+
const cacheControl = cached ? parseCacheControl(cached.headers.get("Cache-Control")) : {};
|
|
215
|
+
return __spreadValues(__spreadValues(__spreadValues({}, options.defaults), cacheControl), options.overrides);
|
|
216
|
+
}
|
|
217
|
+
function calculateCacheBehavior(cacheControl, cachedTimestamp) {
|
|
218
|
+
var _a, _b, _c, _d;
|
|
219
|
+
const allowCache = !((_a = cacheControl.noStore) != null ? _a : false);
|
|
220
|
+
const maxAge = (_b = cacheControl.maxAge) != null ? _b : 0;
|
|
221
|
+
const staleAt = maxAge * 1e3 + (cachedTimestamp != null ? cachedTimestamp : Number.NEGATIVE_INFINITY);
|
|
222
|
+
const now = Date.now();
|
|
223
|
+
const isFresh = staleAt > now;
|
|
224
|
+
const immutable = (_c = cacheControl.immutable) != null ? _c : false;
|
|
225
|
+
const allowNetwork = !cacheControl.onlyIfCached;
|
|
226
|
+
const reevaluate = allowNetwork && !immutable && (cacheControl.mustRevalidate || !isFresh);
|
|
227
|
+
const staleWhileRevalidate = (_d = cacheControl.staleWhileRevalidate) != null ? _d : 0;
|
|
228
|
+
const preferCache = allowCache && (isFresh || staleAt + staleWhileRevalidate * 1e3 > now || immutable);
|
|
229
|
+
return {
|
|
230
|
+
preferCache,
|
|
231
|
+
reevaluate,
|
|
232
|
+
allowCache,
|
|
233
|
+
allowNetwork
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function isCacheableRequestKey(request) {
|
|
237
|
+
return typeof request === "string" || request.method === "GET";
|
|
238
|
+
}
|
|
239
|
+
function getCachedTimestamp(cached) {
|
|
240
|
+
const timestampHeader = cached == null ? void 0 : cached.headers.get(CACHED_TIMESTAMP_HEADER);
|
|
241
|
+
if (!timestampHeader) return void 0;
|
|
242
|
+
return parseInt(timestampHeader, 10);
|
|
243
|
+
}
|
|
244
|
+
export {
|
|
245
|
+
CacheManager,
|
|
246
|
+
DEFAULT_CACHE_NAME,
|
|
247
|
+
DEFAULT_PURGE_AFTER_STALE,
|
|
248
|
+
NOT_CACHED_ERROR,
|
|
249
|
+
parseCacheControl
|
|
250
|
+
};
|
|
251
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cacheManager/CacheControl.ts", "../src/cacheManager/CacheManager.ts"],
|
|
4
|
+
"sourcesContent": ["/*\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Represents all known Cache-Control directives.\n *\n * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control\n */\nexport type CacheControl = {\n /**\n * The maximum amount of time (in seconds) a resource is considered fresh.\n * Example: `max-age=3600` (1 hour)\n */\n readonly maxAge?: number\n\n /**\n * Requires the resource to be revalidated with the origin server before serving it from the cache.\n * Example: `no-cache`\n */\n readonly noCache?: boolean\n\n /**\n * Prevents the resource from being cached by any mechanism.\n * Example: `no-store`\n */\n readonly noStore?: boolean\n\n /**\n * Ensures that stale responses cannot be used without successful revalidation with the origin server.\n * Example: `must-revalidate`\n */\n readonly mustRevalidate?: boolean\n\n /**\n * Indicates the resource is cacheable by both shared and private caches.\n * Example: `public`\n */\n readonly public?: boolean\n\n /**\n * Indicates the resource is cacheable only by the user's browser or private caches, not shared caches.\n * Example: `private`\n */\n readonly private?: boolean\n\n /**\n * Indicates that the resource will not change, so it does not need to be revalidated.\n * Example: `immutable`\n */\n readonly immutable?: boolean\n\n /**\n * Allows serving stale responses for the specified duration (in seconds) while asynchronously revalidating the\n * cache.\n * Example: `stale-while-revalidate=60`\n */\n readonly staleWhileRevalidate?: number\n\n /**\n * Allows serving stale responses (in seconds) if the origin server is unavailable or returns an error.\n * Example: `stale-if-error=86400` (1 day)\n */\n readonly staleIfError?: number\n\n /**\n * Indicates the client only wants a response from the cache and will not make a network request.\n * Example: `only-if-cached`\n */\n readonly onlyIfCached?: boolean\n}\n\n// Map for known directive names to their typed keys\nconst knownDirectives: Record<string, keyof CacheControl | undefined> = {\n 'max-age': 'maxAge',\n 'no-cache': 'noCache',\n 'no-store': 'noStore',\n 'must-revalidate': 'mustRevalidate',\n public: 'public',\n private: 'private',\n immutable: 'immutable',\n 'stale-while-revalidate': 'staleWhileRevalidate',\n 'stale-if-error': 'staleIfError',\n 'only-if-cached': 'onlyIfCached',\n} as const\n\n/**\n * Parses a Cache-Control header string into a structured object.\n * Handles known directives with optional numeric values.\n *\n * @param str - The Cache-Control header string to parse.\n * @returns A CacheControl object representing the parsed directives.\n */\nexport function parseCacheControl(\n str: string | null | undefined\n): CacheControl {\n if (!str) return {}\n const directives: any = {}\n str.split(',').forEach((part) => {\n const [key, value] = part.trim().split('=') as [string, string?]\n const directiveKey = key.trim().toLowerCase() // Ensure case-insensitivity\n\n const typedKey = knownDirectives[directiveKey]\n if (typedKey) {\n // Parse the value or set to true for boolean directives\n if (value === undefined) {\n directives[typedKey] = true\n } else {\n const numericValue = parseInt(value, 10)\n directives[typedKey] = isNaN(numericValue)\n ? value\n : numericValue\n }\n }\n })\n return directives\n}\n", "/*\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/*\n * Offline utility that can be shared between the service worker and application.\n * Note - this should not use any dependencies; the bundled service worker must\n * be lightweight.\n */\n\nimport { type CacheControl, parseCacheControl } from './CacheControl'\n\nexport const DEFAULT_CACHE_NAME = 'offlineCache'\nexport const DEFAULT_PURGE_AFTER_STALE = 7 * 24 * 60 * 60\n\n/**\n * If the cache control options set the onlyIfCached directive and the request is not cached, an error will be thrown.\n */\nexport const NOT_CACHED_ERROR = 'Not cached'\n\nexport type CacheManagerOptions = {\n /**\n * The name of the cache key.\n */\n readonly name?: string\n\n /**\n * The Fetch from network method. Defaults to window.fetch.\n */\n readonly requestFromNetwork?: Fetch\n}\n\ntype Fetch = (request: RequestInfo) => Promise<Response>\n\n/**\n * A prefix for cache keys in order to guarantee they can be used as cache keys.\n * WebKit browsers only allow http/https cache keys.\n */\nconst CACHE_KEY_PREFIX = 'https://cache/'\n\n/**\n * A custom header for storing the timestamp at which the entry was cached.\n */\nconst CACHED_TIMESTAMP_HEADER = 'X-Cached-Timestamp'\n\nexport type CacheEntryOptions = {\n /**\n * If set, the response will be cached under this identifier, not based on the request.\n * This is required for caching POST requests.\n */\n readonly cacheKey?: string | null | undefined\n\n /**\n * Cache control directive defaults.\n * These will be used unless overridden by the response headers or the cache control overrides.\n *\n * CacheControl directives will be overwritten in the order:\n * - cacheControlDefaults\n * - response headers\n * - cacheControlOverrides\n */\n readonly cacheControlDefaults?: CacheControl\n\n /**\n * Allows the client to set cache control overrides. This object is merged with the actual cache control header.\n *\n * For example, to allow cached items to be used 1 day after they are stale while asynchronously revalidating\n * the cache, set `{ staleWhileRevalidate: 24 * 60 * 60 }`\n *\n * These overrides do not change the header cached, only how the cached header is read.\n * If noStore is true, the entry will not be cached.\n */\n readonly cacheControlOverrides?: CacheControl\n\n /**\n * If set, checks the response if it should be used. This affects whether to cache and whether to use the\n * cached fallback.\n */\n readonly responseIsValid?: (response: Response) => Promise<boolean>\n}\n\nexport type CacheCleanOptions = {\n /**\n * The number of seconds after an entry becomes stale before it should be removed from the cache.\n * Default: 7 * 24 * 60 * 60 (7 days).\n * If the cached entry has a stale-while-revalidate cache control directive, this will be ignored.\n */\n readonly purgeAfterStale?: number\n}\n\nexport interface CacheManagerDeps {\n /**\n * The cache storages api.\n * Default: window.caches\n */\n readonly caches?: CacheStorage\n\n /**\n * Creates a Headers object\n */\n readonly headersFactory?: (headersInit?: HeadersInit) => Headers\n\n /**\n * Creates a Response object\n */\n readonly responseFactory?: (\n body?: BodyInit | null,\n init?: ResponseInit\n ) => Response\n}\n\nexport class CacheManager {\n readonly name: string\n\n private readonly caches: CacheStorage\n private cache: Promise<Cache>\n private readonly fetch: Fetch\n\n /**\n * If set, when asynchronous caching has failed, this callback will be invoked.\n * This is not invoked when a promise rejects during an operation.\n */\n onError?: (error: Error) => void\n\n constructor(options?: CacheManagerOptions, deps?: CacheManagerDeps) {\n this.caches = deps?.caches ?? caches\n this.name = options?.name ?? DEFAULT_CACHE_NAME\n this.cache = this.caches.open(this.name)\n this.fetch =\n options?.requestFromNetwork ?? ((request) => fetch(request))\n }\n\n private readonly errorHandler = (error: Error) => {\n this.onError?.(error)\n }\n\n /**\n * Caches a response with added expiry headers.\n */\n async put(cacheId: RequestInfo, response: Response): Promise<Response> {\n if (response.bodyUsed)\n throw new Error(`cannot cache request, body already used`)\n\n const clonedResponse = response.clone()\n\n // Add a timestamp header\n const headers = new Headers(clonedResponse.headers)\n headers.append(CACHED_TIMESTAMP_HEADER, Date.now().toString())\n\n const responseWithHeader = new Response(clonedResponse.body, {\n status: clonedResponse.status,\n statusText: clonedResponse.statusText,\n headers,\n })\n\n const cache = await this.cache\n await cache.put(cacheId, responseWithHeader)\n return response\n }\n\n /**\n * Deletes the request from this cache.\n *\n * @param cacheId The request or cache id used as the cache key.\n */\n async delete(cacheId: RequestInfo): Promise<boolean> {\n const cache = await this.cache\n return await cache.delete(cacheId)\n }\n\n /**\n * Resolves to true if the request is cached.\n *\n * @param cacheId\n */\n async has(cacheId: RequestInfo): Promise<boolean> {\n return (await this.getCached(cacheId)) != null\n }\n\n async get(\n request: RequestInfo,\n options?: CacheEntryOptions\n ): Promise<Response> {\n const cache = await this.cache\n\n const cacheId = options?.cacheKey\n ? CACHE_KEY_PREFIX + options.cacheKey\n : request\n\n let cached: Response | undefined\n if (options?.cacheControlOverrides?.noStore) {\n this.delete(cacheId).catch(this.errorHandler)\n cached = undefined\n } else {\n cached = await cache.match(cacheId)\n }\n\n const cacheControlOptions: CacheControlOptions = {\n defaults: options?.cacheControlDefaults,\n overrides: options?.cacheControlOverrides,\n }\n\n const responseIsValid = async (\n response: Response\n ): Promise<boolean> => {\n return (\n response.ok &&\n (options?.responseIsValid == null ||\n (await options.responseIsValid(response.clone())))\n )\n }\n\n const fetchAndMaybeCache = async (): Promise<Response> => {\n return this.fetch(request).then(async (response) => {\n const cacheControl = calculateCacheControl(\n response,\n cacheControlOptions\n )\n if (\n !cacheControl.noStore &&\n isCacheableRequestKey(cacheId) &&\n (await responseIsValid(response))\n ) {\n this.put(cacheId, response).catch(this.errorHandler)\n }\n return response\n })\n }\n const cacheControl = calculateCacheControl(cached, cacheControlOptions)\n const cacheBehavior = calculateCacheBehavior(\n cacheControl,\n getCachedTimestamp(cached)\n )\n if (cacheBehavior.preferCache) {\n if (cacheBehavior.reevaluate) {\n fetchAndMaybeCache().catch(this.errorHandler)\n }\n return cached!\n } else {\n if (!cacheBehavior.allowNetwork && !cached) {\n throw new Error(NOT_CACHED_ERROR)\n }\n // Prefer a network response. If the network response isn't valid, return the cached value.\n const response = await fetchAndMaybeCache().catch((error) => {\n if (!cacheBehavior.allowCache || !cached) throw error\n else return cached\n })\n const ok = await responseIsValid(response)\n return !ok ? (cached ?? response) : response\n }\n }\n\n /**\n * Returns a Response if it exists in cache.\n *\n * @param request\n */\n private async getCached(\n request: RequestInfo\n ): Promise<Response | undefined> {\n const cache = await this.cache\n return await cache.match(request)\n }\n\n /**\n * @returns a promise that resolves to true if the Cache object is found and deleted, and false otherwise.\n */\n async clear(): Promise<boolean> {\n const deleted = await this.caches.delete(this.name)\n this.cache = this.caches.open(this.name)\n return deleted\n }\n\n /**\n * Cleans stale items from the cache.\n */\n async clean(options?: CacheCleanOptions) {\n const cache = await this.cache\n const cacheControlOptions: CacheControlOptions = {\n defaults: {\n // Unless the cached entry has a stale-while-revalidate directive, use this default.\n staleWhileRevalidate:\n options?.purgeAfterStale ?? DEFAULT_PURGE_AFTER_STALE,\n },\n overrides: {\n noStore: false,\n },\n }\n for (const key of await cache.keys()) {\n const cached = await cache.match(key)\n const cacheControl = calculateCacheControl(\n cached,\n cacheControlOptions\n )\n const { preferCache } = calculateCacheBehavior(\n cacheControl,\n getCachedTimestamp(cached)\n )\n if (!preferCache) {\n await cache.delete(key)\n }\n }\n }\n}\n\ntype CacheControlOptions = {\n readonly defaults?: CacheControl | undefined\n readonly overrides?: CacheControl | undefined\n}\n\nfunction calculateCacheControl(\n cached: Response | undefined,\n options: CacheControlOptions\n): CacheControl {\n const cacheControl = cached\n ? parseCacheControl(cached.headers.get('Cache-Control'))\n : {}\n return {\n ...options.defaults,\n ...cacheControl,\n ...options.overrides,\n }\n}\n\ntype CacheBehavior = {\n /**\n * If false, do not use the cached entry. If it exists, delete it.\n */\n readonly allowCache: boolean\n\n /**\n * Prefer the cached result.\n * If false, the cache may be used if `allowCache` is true and the request fails.\n */\n readonly preferCache: boolean\n\n /**\n * If false, do not request from network, even if there is not a cached resource.\n */\n readonly allowNetwork: boolean\n\n /**\n * Load the resource from the network.\n * If preferCache is true, the cached response will be used and a new request will be made asynchronously.\n */\n readonly reevaluate: boolean\n}\n\nfunction calculateCacheBehavior(\n cacheControl: CacheControl,\n cachedTimestamp: number | undefined\n): CacheBehavior {\n const allowCache = !(cacheControl.noStore ?? false)\n const maxAge = cacheControl.maxAge ?? 0\n const staleAt =\n maxAge * 1000 + (cachedTimestamp ?? Number.NEGATIVE_INFINITY)\n\n const now = Date.now()\n const isFresh = staleAt > now\n const immutable = cacheControl.immutable ?? false\n const allowNetwork = !cacheControl.onlyIfCached\n const reevaluate =\n allowNetwork && !immutable && (cacheControl.mustRevalidate || !isFresh)\n // If the entry is past its max age and there is staleWhileRevalidate set, allow the cached entry to be used\n // staleWhileRevalidate seconds after maxAge.\n const staleWhileRevalidate = cacheControl.staleWhileRevalidate ?? 0\n const preferCache =\n allowCache &&\n (isFresh || staleAt + staleWhileRevalidate * 1000 > now || immutable)\n return {\n preferCache,\n reevaluate,\n allowCache,\n allowNetwork,\n }\n}\n\n/**\n * Returns true if the request can be used as a Cache key.\n */\nfunction isCacheableRequestKey(request: RequestInfo): boolean {\n return typeof request === 'string' || request.method === 'GET'\n}\n\nfunction getCachedTimestamp(cached: Response | undefined): number | undefined {\n const timestampHeader = cached?.headers.get(CACHED_TIMESTAMP_HEADER)\n if (!timestampHeader) return undefined\n return parseInt(timestampHeader, 10)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["cacheControl"]
|
|
7
|
+
}
|