@netlify/cache 1.1.0 → 1.3.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/main.cjs CHANGED
@@ -3,6 +3,10 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
6
10
  var __copyProps = (to, from, except, desc) => {
7
11
  if (from && typeof from === "object" || typeof from === "function") {
8
12
  for (let key of __getOwnPropNames(from))
@@ -15,4 +19,280 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
15
19
 
16
20
  // src/main.ts
17
21
  var main_exports = {};
22
+ __export(main_exports, {
23
+ fetchWithCache: () => fetchWithCache,
24
+ getCacheStatus: () => getCacheStatus,
25
+ setCacheHeaders: () => setCacheHeaders
26
+ });
18
27
  module.exports = __toCommonJS(main_exports);
28
+
29
+ // src/headers.ts
30
+ var CacheStatus = "cache-status";
31
+ var NetlifyCacheId = "netlify-cache-id";
32
+ var NetlifyCacheTag = "netlify-cache-tag";
33
+ var NetlifyCdnCacheControl = "netlify-cdn-cache-control";
34
+ var NetlifyVary = "netlify-vary";
35
+
36
+ // src/cache-headers/validation.ts
37
+ var ensureArray = (value) => Array.isArray(value) ? value : [value];
38
+ var requireArrayOfStrings = (name, value) => {
39
+ if (!Array.isArray(value) || value.some((part) => typeof part !== "string" || part.length === 0)) {
40
+ throw new TypeError(`'${name}' must be an array of non-empty strings.`);
41
+ }
42
+ return value;
43
+ };
44
+ var requireArrayOfStringsWithNesting = (name, value, joiner) => {
45
+ if (!Array.isArray(value)) {
46
+ throw new TypeError(`'${name}' must be an array.`);
47
+ }
48
+ return value.map((part, index) => {
49
+ if (typeof part === "string") {
50
+ return part;
51
+ }
52
+ return requireArrayOfStrings(`${name}[${index}]`, part).join(joiner);
53
+ });
54
+ };
55
+ var requirePositiveInteger = (name, value) => {
56
+ const number = Number.parseFloat(value);
57
+ if (Number.isNaN(number) || !Number.isInteger(number) || number < 0 || number === Number.POSITIVE_INFINITY) {
58
+ throw new TypeError(`'${name}' must be a positive integer number.`);
59
+ }
60
+ return number;
61
+ };
62
+
63
+ // src/cache-headers/cache-headers.ts
64
+ var ONE_YEAR = 60 * 24 * 365;
65
+ var cacheHeaders = (cacheSettings) => {
66
+ const { durable, overrideDeployRevalidation: id, tags, ttl, swr, vary } = cacheSettings;
67
+ const headers = {};
68
+ const cacheControlDirectives = [];
69
+ if (ttl) {
70
+ const ttlValue = requirePositiveInteger("ttl", ttl);
71
+ if (ttlValue > 0) {
72
+ cacheControlDirectives.push(`s-maxage=${ttlValue}`);
73
+ }
74
+ }
75
+ if (swr) {
76
+ const swrValue = swr === true ? ONE_YEAR : requirePositiveInteger("swr", swr);
77
+ if (swrValue > 0) {
78
+ cacheControlDirectives.push(`stale-while-revalidate=${swrValue}`);
79
+ }
80
+ }
81
+ if (cacheControlDirectives.length > 0) {
82
+ if (durable) {
83
+ cacheControlDirectives.push("durable");
84
+ }
85
+ headers[NetlifyCdnCacheControl] = cacheControlDirectives.join(",");
86
+ }
87
+ if (tags) {
88
+ headers[NetlifyCacheTag] = requireArrayOfStrings("tags", tags).join(",");
89
+ }
90
+ const netlifyVary = getNetlifyVary(vary);
91
+ if (netlifyVary) {
92
+ headers[NetlifyVary] = netlifyVary;
93
+ }
94
+ if (id) {
95
+ headers[NetlifyCacheId] = requireArrayOfStrings("id", ensureArray(id)).join(",");
96
+ }
97
+ return headers;
98
+ };
99
+ var getNetlifyVary = (varyOptions) => {
100
+ if (!varyOptions) {
101
+ return null;
102
+ }
103
+ const { cookie, country, header, language, query } = varyOptions;
104
+ const directives = [];
105
+ if (cookie) {
106
+ directives.push(`cookie=${requireArrayOfStrings("cookie", ensureArray(cookie)).join("|")}`);
107
+ }
108
+ if (country) {
109
+ directives.push(`country=${requireArrayOfStringsWithNesting("country", ensureArray(country), "+").join("|")}`);
110
+ }
111
+ if (header) {
112
+ directives.push(`header=${requireArrayOfStrings("header", ensureArray(header)).join("|")}`);
113
+ }
114
+ if (language) {
115
+ directives.push(`language=${requireArrayOfStringsWithNesting("language", ensureArray(language), "+").join("|")}`);
116
+ }
117
+ if (query) {
118
+ if (query === true) {
119
+ directives.push(`query`);
120
+ } else {
121
+ directives.push(`query=${requireArrayOfStrings("query", ensureArray(query)).join("|")}`);
122
+ }
123
+ }
124
+ if (directives.length === 0) {
125
+ return null;
126
+ }
127
+ return directives.join(",");
128
+ };
129
+ var applyHeaders = (subject, headersObject) => {
130
+ for (const name in headersObject) {
131
+ if (name === NetlifyCdnCacheControl) {
132
+ subject.set(name, headersObject[name]);
133
+ } else {
134
+ subject.append(name, headersObject[name]);
135
+ }
136
+ }
137
+ };
138
+ var setCacheHeaders = (response, cacheSettings) => {
139
+ if (!(response instanceof Response)) {
140
+ throw new TypeError("Input must be a Response object.");
141
+ }
142
+ const newResponse = new Response(response.body, response);
143
+ applyHeaders(newResponse.headers, cacheHeaders(cacheSettings));
144
+ return newResponse;
145
+ };
146
+
147
+ // src/cache-status/cache-status.ts
148
+ var CACHE_DURABLE = "netlify durable";
149
+ var CACHE_EDGE = "netlify edge";
150
+ var parseCacheStatusValue = (value) => {
151
+ const parts = value.split(";").map((part) => part.trim());
152
+ const [namePart, ...attributeParts] = parts;
153
+ const name = (namePart ?? "").replace(/"/g, "").toLowerCase();
154
+ const attributes = attributeParts.reduce((acc, part) => {
155
+ const [key, value2 = ""] = part.split("=");
156
+ return {
157
+ ...acc,
158
+ [key]: value2
159
+ };
160
+ }, {});
161
+ return {
162
+ attributes,
163
+ name
164
+ };
165
+ };
166
+ var parseCacheStatusValues = (cacheStatusValues) => {
167
+ const cacheStatus = {
168
+ hit: false,
169
+ caches: {}
170
+ };
171
+ for (const value of cacheStatusValues.split(",")) {
172
+ const { attributes, name } = parseCacheStatusValue(value);
173
+ if (name === CACHE_EDGE) {
174
+ const hit = attributes.hit !== void 0;
175
+ cacheStatus.caches.edge = {
176
+ hit,
177
+ fresh: hit && attributes.fwd !== "stale"
178
+ };
179
+ cacheStatus.hit = cacheStatus.hit || hit;
180
+ continue;
181
+ }
182
+ if (name === CACHE_DURABLE) {
183
+ let ttl = 0;
184
+ if (attributes.ttl !== void 0) {
185
+ const parsedTTL = Number.parseInt(attributes.ttl);
186
+ if (!Number.isNaN(parsedTTL)) {
187
+ ttl = parsedTTL;
188
+ }
189
+ }
190
+ const hit = attributes.hit !== void 0;
191
+ cacheStatus.caches.durable = {
192
+ hit,
193
+ fresh: hit && attributes.fwd !== "stale",
194
+ stored: attributes.stored === "true",
195
+ ttl
196
+ };
197
+ cacheStatus.hit = cacheStatus.hit || hit;
198
+ continue;
199
+ }
200
+ }
201
+ if (Object.keys(cacheStatus.caches).length === 0) {
202
+ return null;
203
+ }
204
+ return cacheStatus;
205
+ };
206
+ var getCacheStatus = (input) => {
207
+ if (typeof input === "string") {
208
+ return parseCacheStatusValues(input);
209
+ }
210
+ if (input instanceof Headers) {
211
+ return parseCacheStatusValues(input.get(CacheStatus) ?? "");
212
+ }
213
+ if (input instanceof Response) {
214
+ return parseCacheStatusValues(input.headers.get(CacheStatus) ?? "");
215
+ }
216
+ throw new TypeError("`getCacheStatus` expects a string, a `Headers` object or a `Response` object.");
217
+ };
218
+
219
+ // src/fetchwithcache.ts
220
+ var requestInitOptions = [
221
+ "method",
222
+ "keepalive",
223
+ "headers",
224
+ "body",
225
+ "redirect",
226
+ "integrity",
227
+ "signal",
228
+ "credentials",
229
+ "mode",
230
+ "referrer",
231
+ "referrerPolicy",
232
+ "window",
233
+ "dispatcher",
234
+ "duplex"
235
+ ];
236
+ var isRequestInit = (input) => {
237
+ if (typeof input !== "object") {
238
+ return false;
239
+ }
240
+ for (const property of requestInitOptions) {
241
+ if (property in input) {
242
+ return true;
243
+ }
244
+ }
245
+ return false;
246
+ };
247
+ var fetchWithCache = async (request, optionsOrCacheSettings, cacheOptionsParam) => {
248
+ let cacheOptions;
249
+ let requestInit;
250
+ if (isRequestInit(optionsOrCacheSettings)) {
251
+ cacheOptions = cacheOptionsParam || {};
252
+ requestInit = optionsOrCacheSettings;
253
+ } else {
254
+ cacheOptions = optionsOrCacheSettings || {};
255
+ requestInit = {};
256
+ }
257
+ let method;
258
+ if (request instanceof Request) {
259
+ method = request.method;
260
+ } else {
261
+ method = requestInit?.method;
262
+ }
263
+ if (method && method?.toLowerCase() !== "get") {
264
+ throw new TypeError("`fetchWithCache` only supports GET requests.");
265
+ }
266
+ let cache;
267
+ const { cache: cacheParam, onCachePut, ...cacheSettings } = cacheOptions;
268
+ if (cacheParam) {
269
+ if (typeof cacheParam === "string") {
270
+ cache = await caches.open(cacheParam);
271
+ } else if (cacheParam instanceof Cache) {
272
+ cache = cacheParam;
273
+ } else {
274
+ throw new TypeError("`cache` must be a string representing the cache name or an instance of `Cache`.");
275
+ }
276
+ } else {
277
+ cache = await caches.open("");
278
+ }
279
+ const cached = await cache.match(request);
280
+ if (cached) {
281
+ return cached;
282
+ }
283
+ const fresh = await fetch(request, requestInit);
284
+ const responseForCache = setCacheHeaders(fresh.clone(), cacheSettings);
285
+ const cachePut = cache.put(request, responseForCache);
286
+ if (onCachePut) {
287
+ await onCachePut(cachePut);
288
+ } else {
289
+ await cachePut;
290
+ }
291
+ return fresh;
292
+ };
293
+ // Annotate the CommonJS export names for ESM import in node:
294
+ 0 && (module.exports = {
295
+ fetchWithCache,
296
+ getCacheStatus,
297
+ setCacheHeaders
298
+ });
package/dist/main.d.cts CHANGED
@@ -1,2 +1,156 @@
1
+ import { N as NetlifyCache } from './cache-854474ad.js';
1
2
 
2
- export { }
3
+ interface CacheSettings {
4
+ /**
5
+ * Persist the response in the durable cache, shared across all CDN nodes.
6
+ *
7
+ * {@link} https://ntl.fyi/durable
8
+ */
9
+ durable?: boolean;
10
+ /**
11
+ * Override the default behavior of revalidating cached responses with new
12
+ * deploys. You must provide one or more values that will be registered as
13
+ * cache tags for you to purge the responses on-demand.
14
+ *
15
+ * {@link} https://ntl.fyi/cache-id
16
+ */
17
+ overrideDeployRevalidation?: string | string[];
18
+ /**
19
+ * Provide one or more cache tags to associate with the response. You can use
20
+ * these tags to revalidate responses on-demand, making sure you target the
21
+ * specific responses you want based on your application logic.
22
+ *
23
+ * {@link} https://ntl.fyi/cache-tags
24
+ */
25
+ tags?: string[];
26
+ /**
27
+ * Define the period of time (in seconds) during which the response can be
28
+ * considered fresh. After this period, the response will revalidated in
29
+ * the background if used with the `stale-while-revalidate` directive,
30
+ * otherwise the response will be discarded.
31
+ *
32
+ * {@link} https://ntl.fyi/cache-ttl
33
+ */
34
+ ttl?: number;
35
+ /**
36
+ * Define the period of time (in seconds) after the response is considered
37
+ * stale (set by the `ttl` property) and during which it can still
38
+ * be served, while starting a revalidation in the background.
39
+ *
40
+ * {@link} https://ntl.fyi/cache-swr
41
+ */
42
+ swr?: boolean | number;
43
+ /**
44
+ * Defines how cache key variations are created for the response, giving you
45
+ * fine-grained control over which parts of a request are taken into
46
+ * consideration for matching cache objects.
47
+ *
48
+ * {@link} https://ntl.fyi/cache-vary
49
+ */
50
+ vary?: VaryOptions;
51
+ }
52
+ interface VaryOptions {
53
+ /**
54
+ * Define cache key variations based on a subset of cookie keys.
55
+ */
56
+ cookie?: string | string[];
57
+ /**
58
+ * Define cache key variations based on the geographical origin of the request.
59
+ */
60
+ country?: string | (string | string[])[];
61
+ /**
62
+ * Define cache key variations based on your custom request headers and most
63
+ * standard request headers.
64
+ */
65
+ header?: string | string[];
66
+ /**
67
+ * Define cache key variations for one or more individual languages or custom
68
+ * language groups.
69
+ */
70
+ language?: string | (string | string[])[];
71
+ /**
72
+ * Define cache key variations based on a specific subset of query parameters
73
+ * included with a request or all request query parameters.
74
+ */
75
+ query?: boolean | string | string[];
76
+ }
77
+
78
+ declare const setCacheHeaders: (response: Response, cacheSettings: CacheSettings) => Response;
79
+
80
+ interface CacheStatus {
81
+ /**
82
+ * Whether the response was served from a Netlify cache.
83
+ */
84
+ hit: boolean;
85
+ /**
86
+ * Granular information about how the different Netlify caches have
87
+ * contributed to the delivery of the response.
88
+ */
89
+ caches: {
90
+ /**
91
+ * How the response has interacted with the durable cache.
92
+ *
93
+ * {@link} https://ntl.fyi/durable
94
+ */
95
+ durable?: {
96
+ hit: boolean;
97
+ fresh: boolean;
98
+ stored?: boolean;
99
+ ttl: number;
100
+ };
101
+ /**
102
+ * How the response has interacted with the edge cache.
103
+ *
104
+ * {@link} https://ntl.fyi/edge-cache
105
+ */
106
+ edge?: {
107
+ hit: boolean;
108
+ fresh: boolean;
109
+ };
110
+ };
111
+ }
112
+ type ParseCacheStatus = {
113
+ (header: string): CacheStatus | null;
114
+ (headers: Headers): CacheStatus | null;
115
+ (response: Response): CacheStatus | null;
116
+ };
117
+ /**
118
+ * Retrieves information about how a response has interacted with Netlify's
119
+ * global caching infrastructure, including whether the response has been
120
+ * served by a cache and whether it's fresh or stale.
121
+ */
122
+ declare const getCacheStatus: ParseCacheStatus;
123
+
124
+ type CacheOptions = CacheSettings & {
125
+ /**
126
+ * A `Cache` instance or the name of the cache that should be used. If not
127
+ * set, a cache without a name (i.e. `""`) will be used.
128
+ */
129
+ cache?: NetlifyCache | string;
130
+ /**
131
+ * When `fetchWithCache` fetches a new response and adds it to the cache, the
132
+ * `Promise` it returns waits for both the network call to finish and for the
133
+ * response to be cached. Customize this behavior by setting a `onCachePut`
134
+ * handler that receives the cache write `Promise`, giving you the option to
135
+ * handle it as you like. This lets you remove the cache write from the "hot
136
+ * path" and run it in the background.
137
+ */
138
+ onCachePut?: (cachePut: Promise<void>) => void | Promise<void>;
139
+ };
140
+ type FetchWithCache = {
141
+ (request: string | URL | Request, init?: RequestInit): Promise<Response>;
142
+ (request: string | URL | Request, cacheSettings?: CacheOptions): Promise<Response>;
143
+ (request: string | URL | Request, init: RequestInit, cacheSettings?: CacheOptions): Promise<Response>;
144
+ };
145
+ /**
146
+ * Serves a resource from the Cache API if available, otherwise it's fetched
147
+ * from the network and added to the cache. It's a drop-in replacement for
148
+ * `fetch`, supporting the same arguments and return value. A third (optional)
149
+ * argument makes it possible to set the caching configuration of the response
150
+ * as it's added to the cache, overridding any cache control settings it sets.
151
+ * It returns a `Promise` that resolves with the resulting `Response` object,
152
+ * whether it comes from the cache or from the network.
153
+ */
154
+ declare const fetchWithCache: FetchWithCache;
155
+
156
+ export { fetchWithCache, getCacheStatus, setCacheHeaders };
package/dist/main.d.ts CHANGED
@@ -1,2 +1,156 @@
1
+ import { N as NetlifyCache } from './cache-854474ad.js';
1
2
 
2
- export { }
3
+ interface CacheSettings {
4
+ /**
5
+ * Persist the response in the durable cache, shared across all CDN nodes.
6
+ *
7
+ * {@link} https://ntl.fyi/durable
8
+ */
9
+ durable?: boolean;
10
+ /**
11
+ * Override the default behavior of revalidating cached responses with new
12
+ * deploys. You must provide one or more values that will be registered as
13
+ * cache tags for you to purge the responses on-demand.
14
+ *
15
+ * {@link} https://ntl.fyi/cache-id
16
+ */
17
+ overrideDeployRevalidation?: string | string[];
18
+ /**
19
+ * Provide one or more cache tags to associate with the response. You can use
20
+ * these tags to revalidate responses on-demand, making sure you target the
21
+ * specific responses you want based on your application logic.
22
+ *
23
+ * {@link} https://ntl.fyi/cache-tags
24
+ */
25
+ tags?: string[];
26
+ /**
27
+ * Define the period of time (in seconds) during which the response can be
28
+ * considered fresh. After this period, the response will revalidated in
29
+ * the background if used with the `stale-while-revalidate` directive,
30
+ * otherwise the response will be discarded.
31
+ *
32
+ * {@link} https://ntl.fyi/cache-ttl
33
+ */
34
+ ttl?: number;
35
+ /**
36
+ * Define the period of time (in seconds) after the response is considered
37
+ * stale (set by the `ttl` property) and during which it can still
38
+ * be served, while starting a revalidation in the background.
39
+ *
40
+ * {@link} https://ntl.fyi/cache-swr
41
+ */
42
+ swr?: boolean | number;
43
+ /**
44
+ * Defines how cache key variations are created for the response, giving you
45
+ * fine-grained control over which parts of a request are taken into
46
+ * consideration for matching cache objects.
47
+ *
48
+ * {@link} https://ntl.fyi/cache-vary
49
+ */
50
+ vary?: VaryOptions;
51
+ }
52
+ interface VaryOptions {
53
+ /**
54
+ * Define cache key variations based on a subset of cookie keys.
55
+ */
56
+ cookie?: string | string[];
57
+ /**
58
+ * Define cache key variations based on the geographical origin of the request.
59
+ */
60
+ country?: string | (string | string[])[];
61
+ /**
62
+ * Define cache key variations based on your custom request headers and most
63
+ * standard request headers.
64
+ */
65
+ header?: string | string[];
66
+ /**
67
+ * Define cache key variations for one or more individual languages or custom
68
+ * language groups.
69
+ */
70
+ language?: string | (string | string[])[];
71
+ /**
72
+ * Define cache key variations based on a specific subset of query parameters
73
+ * included with a request or all request query parameters.
74
+ */
75
+ query?: boolean | string | string[];
76
+ }
77
+
78
+ declare const setCacheHeaders: (response: Response, cacheSettings: CacheSettings) => Response;
79
+
80
+ interface CacheStatus {
81
+ /**
82
+ * Whether the response was served from a Netlify cache.
83
+ */
84
+ hit: boolean;
85
+ /**
86
+ * Granular information about how the different Netlify caches have
87
+ * contributed to the delivery of the response.
88
+ */
89
+ caches: {
90
+ /**
91
+ * How the response has interacted with the durable cache.
92
+ *
93
+ * {@link} https://ntl.fyi/durable
94
+ */
95
+ durable?: {
96
+ hit: boolean;
97
+ fresh: boolean;
98
+ stored?: boolean;
99
+ ttl: number;
100
+ };
101
+ /**
102
+ * How the response has interacted with the edge cache.
103
+ *
104
+ * {@link} https://ntl.fyi/edge-cache
105
+ */
106
+ edge?: {
107
+ hit: boolean;
108
+ fresh: boolean;
109
+ };
110
+ };
111
+ }
112
+ type ParseCacheStatus = {
113
+ (header: string): CacheStatus | null;
114
+ (headers: Headers): CacheStatus | null;
115
+ (response: Response): CacheStatus | null;
116
+ };
117
+ /**
118
+ * Retrieves information about how a response has interacted with Netlify's
119
+ * global caching infrastructure, including whether the response has been
120
+ * served by a cache and whether it's fresh or stale.
121
+ */
122
+ declare const getCacheStatus: ParseCacheStatus;
123
+
124
+ type CacheOptions = CacheSettings & {
125
+ /**
126
+ * A `Cache` instance or the name of the cache that should be used. If not
127
+ * set, a cache without a name (i.e. `""`) will be used.
128
+ */
129
+ cache?: NetlifyCache | string;
130
+ /**
131
+ * When `fetchWithCache` fetches a new response and adds it to the cache, the
132
+ * `Promise` it returns waits for both the network call to finish and for the
133
+ * response to be cached. Customize this behavior by setting a `onCachePut`
134
+ * handler that receives the cache write `Promise`, giving you the option to
135
+ * handle it as you like. This lets you remove the cache write from the "hot
136
+ * path" and run it in the background.
137
+ */
138
+ onCachePut?: (cachePut: Promise<void>) => void | Promise<void>;
139
+ };
140
+ type FetchWithCache = {
141
+ (request: string | URL | Request, init?: RequestInit): Promise<Response>;
142
+ (request: string | URL | Request, cacheSettings?: CacheOptions): Promise<Response>;
143
+ (request: string | URL | Request, init: RequestInit, cacheSettings?: CacheOptions): Promise<Response>;
144
+ };
145
+ /**
146
+ * Serves a resource from the Cache API if available, otherwise it's fetched
147
+ * from the network and added to the cache. It's a drop-in replacement for
148
+ * `fetch`, supporting the same arguments and return value. A third (optional)
149
+ * argument makes it possible to set the caching configuration of the response
150
+ * as it's added to the cache, overridding any cache control settings it sets.
151
+ * It returns a `Promise` that resolves with the resulting `Response` object,
152
+ * whether it comes from the cache or from the network.
153
+ */
154
+ declare const fetchWithCache: FetchWithCache;
155
+
156
+ export { fetchWithCache, getCacheStatus, setCacheHeaders };