@cloudflare/pages-shared 0.13.71 → 0.13.72
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/__tests__/asset-server/handler.test.d.ts +2 -0
- package/dist/__tests__/asset-server/handler.test.d.ts.map +1 -0
- package/dist/__tests__/asset-server/responses.test.d.ts +2 -0
- package/dist/__tests__/asset-server/responses.test.d.ts.map +1 -0
- package/dist/__tests__/metadata-generator/createMetadataObject.test.d.ts +2 -0
- package/dist/__tests__/metadata-generator/createMetadataObject.test.d.ts.map +1 -0
- package/dist/asset-server/handler.d.ts +62 -0
- package/dist/asset-server/handler.d.ts.map +1 -0
- package/dist/asset-server/handler.js +890 -0
- package/dist/asset-server/handler.js.map +7 -0
- package/dist/asset-server/metadata.d.ts +52 -0
- package/dist/asset-server/metadata.d.ts.map +1 -0
- package/dist/asset-server/metadata.js +0 -0
- package/dist/asset-server/metadata.js.map +7 -0
- package/dist/asset-server/patchUrl.d.ts +2 -0
- package/dist/asset-server/patchUrl.d.ts.map +1 -0
- package/dist/asset-server/patchUrl.js +15 -0
- package/dist/asset-server/patchUrl.js.map +7 -0
- package/dist/asset-server/responses.d.ts +45 -0
- package/dist/asset-server/responses.d.ts.map +1 -0
- package/dist/asset-server/responses.js +166 -0
- package/dist/asset-server/responses.js.map +7 -0
- package/dist/environment-polyfills/html-rewriter.d.ts +9 -0
- package/dist/environment-polyfills/html-rewriter.d.ts.map +1 -0
- package/dist/environment-polyfills/index.d.ts +3 -0
- package/dist/environment-polyfills/index.d.ts.map +1 -0
- package/dist/environment-polyfills/miniflare.d.ts +3 -0
- package/dist/environment-polyfills/miniflare.d.ts.map +1 -0
- package/dist/environment-polyfills/types.d.ts +11 -0
- package/dist/environment-polyfills/types.d.ts.map +1 -0
- package/dist/metadata-generator/constants.d.ts +14 -0
- package/dist/metadata-generator/constants.d.ts.map +1 -0
- package/dist/metadata-generator/constants.js +29 -0
- package/dist/metadata-generator/constants.js.map +7 -0
- package/dist/metadata-generator/createMetadataObject.d.ts +13 -0
- package/dist/metadata-generator/createMetadataObject.d.ts.map +1 -0
- package/dist/metadata-generator/createMetadataObject.js +174 -0
- package/dist/metadata-generator/createMetadataObject.js.map +7 -0
- package/dist/metadata-generator/types.d.ts +42 -0
- package/dist/metadata-generator/types.d.ts.map +1 -0
- package/dist/metadata-generator/types.js +0 -0
- package/dist/metadata-generator/types.js.map +7 -0
- package/dist/scripts/build.d.ts +2 -0
- package/dist/scripts/build.d.ts.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +10 -5
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
// ../workers-shared/utils/responses.ts
|
|
2
|
+
var OkResponse = class _OkResponse extends Response {
|
|
3
|
+
static {
|
|
4
|
+
this.status = 200;
|
|
5
|
+
}
|
|
6
|
+
constructor(body, init) {
|
|
7
|
+
super(body, {
|
|
8
|
+
...init,
|
|
9
|
+
status: _OkResponse.status
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var NotFoundResponse = class _NotFoundResponse extends Response {
|
|
14
|
+
static {
|
|
15
|
+
this.status = 404;
|
|
16
|
+
}
|
|
17
|
+
constructor(...[body, init]) {
|
|
18
|
+
super(body, {
|
|
19
|
+
...init,
|
|
20
|
+
status: _NotFoundResponse.status,
|
|
21
|
+
statusText: "Not Found"
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var MethodNotAllowedResponse = class _MethodNotAllowedResponse extends Response {
|
|
26
|
+
static {
|
|
27
|
+
this.status = 405;
|
|
28
|
+
}
|
|
29
|
+
constructor(...[body, init]) {
|
|
30
|
+
super(body, {
|
|
31
|
+
...init,
|
|
32
|
+
status: _MethodNotAllowedResponse.status,
|
|
33
|
+
statusText: "Method Not Allowed"
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var InternalServerErrorResponse = class _InternalServerErrorResponse extends Response {
|
|
38
|
+
static {
|
|
39
|
+
this.status = 500;
|
|
40
|
+
}
|
|
41
|
+
constructor(_, init) {
|
|
42
|
+
super(null, {
|
|
43
|
+
...init,
|
|
44
|
+
status: _InternalServerErrorResponse.status
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var NotModifiedResponse = class _NotModifiedResponse extends Response {
|
|
49
|
+
static {
|
|
50
|
+
this.status = 304;
|
|
51
|
+
}
|
|
52
|
+
constructor(...[_body, init]) {
|
|
53
|
+
super(null, {
|
|
54
|
+
...init,
|
|
55
|
+
status: _NotModifiedResponse.status,
|
|
56
|
+
statusText: "Not Modified"
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var MovedPermanentlyResponse = class _MovedPermanentlyResponse extends Response {
|
|
61
|
+
static {
|
|
62
|
+
this.status = 301;
|
|
63
|
+
}
|
|
64
|
+
constructor(location, init) {
|
|
65
|
+
super(null, {
|
|
66
|
+
...init,
|
|
67
|
+
status: _MovedPermanentlyResponse.status,
|
|
68
|
+
statusText: "Moved Permanently",
|
|
69
|
+
headers: {
|
|
70
|
+
...init?.headers,
|
|
71
|
+
Location: location
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var FoundResponse = class _FoundResponse extends Response {
|
|
77
|
+
static {
|
|
78
|
+
this.status = 302;
|
|
79
|
+
}
|
|
80
|
+
constructor(location, init) {
|
|
81
|
+
super(null, {
|
|
82
|
+
...init,
|
|
83
|
+
status: _FoundResponse.status,
|
|
84
|
+
statusText: "Found",
|
|
85
|
+
headers: {
|
|
86
|
+
...init?.headers,
|
|
87
|
+
Location: location
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
var SeeOtherResponse = class _SeeOtherResponse extends Response {
|
|
93
|
+
static {
|
|
94
|
+
this.status = 303;
|
|
95
|
+
}
|
|
96
|
+
constructor(location, init) {
|
|
97
|
+
super(null, {
|
|
98
|
+
...init,
|
|
99
|
+
status: _SeeOtherResponse.status,
|
|
100
|
+
statusText: "See Other",
|
|
101
|
+
headers: {
|
|
102
|
+
...init?.headers,
|
|
103
|
+
Location: location
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var TemporaryRedirectResponse = class _TemporaryRedirectResponse extends Response {
|
|
109
|
+
static {
|
|
110
|
+
this.status = 307;
|
|
111
|
+
}
|
|
112
|
+
constructor(location, init) {
|
|
113
|
+
super(null, {
|
|
114
|
+
...init,
|
|
115
|
+
status: _TemporaryRedirectResponse.status,
|
|
116
|
+
statusText: "Temporary Redirect",
|
|
117
|
+
headers: {
|
|
118
|
+
...init?.headers,
|
|
119
|
+
Location: location
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
var PermanentRedirectResponse = class _PermanentRedirectResponse extends Response {
|
|
125
|
+
static {
|
|
126
|
+
this.status = 308;
|
|
127
|
+
}
|
|
128
|
+
constructor(location, init) {
|
|
129
|
+
super(null, {
|
|
130
|
+
...init,
|
|
131
|
+
status: _PermanentRedirectResponse.status,
|
|
132
|
+
statusText: "Permanent Redirect",
|
|
133
|
+
headers: {
|
|
134
|
+
...init?.headers,
|
|
135
|
+
Location: location
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// ../workers-shared/asset-worker/src/utils/rules-engine.ts
|
|
142
|
+
var ESCAPE_REGEX_CHARACTERS = /[-/\\^$*+?.()|[\]{}]/g;
|
|
143
|
+
var escapeRegex = (str) => {
|
|
144
|
+
return str.replace(ESCAPE_REGEX_CHARACTERS, "\\$&");
|
|
145
|
+
};
|
|
146
|
+
var HOST_PLACEHOLDER_REGEX = /(?<=^https:\\\/\\\/[^/]*?):([A-Za-z]\w*)(?=\\)/g;
|
|
147
|
+
var PLACEHOLDER_REGEX = /:([A-Za-z]\w*)/g;
|
|
148
|
+
var replacer = (str, replacements) => {
|
|
149
|
+
for (const [replacement, value] of Object.entries(replacements)) {
|
|
150
|
+
str = str.replaceAll(`:${replacement}`, value);
|
|
151
|
+
}
|
|
152
|
+
return str;
|
|
153
|
+
};
|
|
154
|
+
var generateRuleRegExp = (rule) => {
|
|
155
|
+
rule = rule.split("*").map(escapeRegex).join("(?<splat>.*)");
|
|
156
|
+
const host_matches = rule.matchAll(HOST_PLACEHOLDER_REGEX);
|
|
157
|
+
for (const host_match of host_matches) {
|
|
158
|
+
rule = rule.split(host_match[0]).join(`(?<${host_match[1]}>[^/.]+)`);
|
|
159
|
+
}
|
|
160
|
+
const path_matches = rule.matchAll(PLACEHOLDER_REGEX);
|
|
161
|
+
for (const path_match of path_matches) {
|
|
162
|
+
rule = rule.split(path_match[0]).join(`(?<${path_match[1]}>[^/]+)`);
|
|
163
|
+
}
|
|
164
|
+
rule = "^" + rule + "$";
|
|
165
|
+
return RegExp(rule);
|
|
166
|
+
};
|
|
167
|
+
var generateRulesMatcher = (rules, replacerFn = (match) => match) => {
|
|
168
|
+
if (!rules) {
|
|
169
|
+
return () => [];
|
|
170
|
+
}
|
|
171
|
+
const compiledRules = Object.entries(rules).map(([rule, match]) => {
|
|
172
|
+
const crossHost = rule.startsWith("https://");
|
|
173
|
+
try {
|
|
174
|
+
const regExp = generateRuleRegExp(rule);
|
|
175
|
+
return [{ crossHost, regExp }, match];
|
|
176
|
+
} catch {
|
|
177
|
+
}
|
|
178
|
+
}).filter((value) => value !== void 0);
|
|
179
|
+
return ({ request }) => {
|
|
180
|
+
const { pathname, hostname } = new URL(request.url);
|
|
181
|
+
return compiledRules.map(([{ crossHost, regExp }, match]) => {
|
|
182
|
+
const test = crossHost ? `https://${hostname}${pathname}` : pathname;
|
|
183
|
+
const result = regExp.exec(test);
|
|
184
|
+
if (result) {
|
|
185
|
+
return replacerFn(match, result.groups || {});
|
|
186
|
+
}
|
|
187
|
+
}).filter((value) => value !== void 0);
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// asset-server/responses.ts
|
|
192
|
+
function mergeHeaders(base, extra) {
|
|
193
|
+
const baseHeaders = new Headers(base ?? {});
|
|
194
|
+
const extraHeaders = new Headers(extra ?? {});
|
|
195
|
+
return new Headers({
|
|
196
|
+
...Object.fromEntries(baseHeaders.entries()),
|
|
197
|
+
...Object.fromEntries(extraHeaders.entries())
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
function stripLeadingDoubleSlashes(location) {
|
|
201
|
+
return location.replace(/^(\/|%2F|%2f|%5C|%5c|%09|\s|\\)+(.*)/, "/$2");
|
|
202
|
+
}
|
|
203
|
+
var OkResponse2 = class extends Response {
|
|
204
|
+
constructor(...[body, init]) {
|
|
205
|
+
super(body, {
|
|
206
|
+
...init,
|
|
207
|
+
status: 200,
|
|
208
|
+
statusText: "OK"
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var MovedPermanentlyResponse2 = class extends Response {
|
|
213
|
+
constructor(location, init, {
|
|
214
|
+
preventLeadingDoubleSlash = true
|
|
215
|
+
} = {
|
|
216
|
+
preventLeadingDoubleSlash: true
|
|
217
|
+
}) {
|
|
218
|
+
location = preventLeadingDoubleSlash ? stripLeadingDoubleSlashes(location) : location;
|
|
219
|
+
super(`Redirecting to ${location}`, {
|
|
220
|
+
...init,
|
|
221
|
+
status: 301,
|
|
222
|
+
statusText: "Moved Permanently",
|
|
223
|
+
headers: mergeHeaders(init?.headers, {
|
|
224
|
+
location
|
|
225
|
+
})
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
var FoundResponse2 = class extends Response {
|
|
230
|
+
constructor(location, init, {
|
|
231
|
+
preventLeadingDoubleSlash = true
|
|
232
|
+
} = {
|
|
233
|
+
preventLeadingDoubleSlash: true
|
|
234
|
+
}) {
|
|
235
|
+
location = preventLeadingDoubleSlash ? stripLeadingDoubleSlashes(location) : location;
|
|
236
|
+
super(`Redirecting to ${location}`, {
|
|
237
|
+
...init,
|
|
238
|
+
status: 302,
|
|
239
|
+
statusText: "Found",
|
|
240
|
+
headers: mergeHeaders(init?.headers, {
|
|
241
|
+
location
|
|
242
|
+
})
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
var NotModifiedResponse2 = class extends Response {
|
|
247
|
+
constructor(...[_body, _init]) {
|
|
248
|
+
super(void 0, {
|
|
249
|
+
status: 304,
|
|
250
|
+
statusText: "Not Modified"
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
var PermanentRedirectResponse2 = class extends Response {
|
|
255
|
+
constructor(location, init, {
|
|
256
|
+
preventLeadingDoubleSlash = true
|
|
257
|
+
} = {
|
|
258
|
+
preventLeadingDoubleSlash: true
|
|
259
|
+
}) {
|
|
260
|
+
location = preventLeadingDoubleSlash ? stripLeadingDoubleSlashes(location) : location;
|
|
261
|
+
super(void 0, {
|
|
262
|
+
...init,
|
|
263
|
+
status: 308,
|
|
264
|
+
statusText: "Permanent Redirect",
|
|
265
|
+
headers: mergeHeaders(init?.headers, {
|
|
266
|
+
location
|
|
267
|
+
})
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
var NotFoundResponse2 = class extends Response {
|
|
272
|
+
constructor(...[body, init]) {
|
|
273
|
+
super(body, {
|
|
274
|
+
...init,
|
|
275
|
+
status: 404,
|
|
276
|
+
statusText: "Not Found"
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
var MethodNotAllowedResponse2 = class extends Response {
|
|
281
|
+
constructor(...[body, init]) {
|
|
282
|
+
super(body, {
|
|
283
|
+
...init,
|
|
284
|
+
status: 405,
|
|
285
|
+
statusText: "Method Not Allowed"
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
var NotAcceptableResponse = class extends Response {
|
|
290
|
+
constructor(...[body, init]) {
|
|
291
|
+
super(body, {
|
|
292
|
+
...init,
|
|
293
|
+
status: 406,
|
|
294
|
+
statusText: "Not Acceptable"
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
var InternalServerErrorResponse2 = class extends Response {
|
|
299
|
+
constructor(err, init) {
|
|
300
|
+
let body = void 0;
|
|
301
|
+
if (globalThis.DEBUG) {
|
|
302
|
+
body = `${err.message}
|
|
303
|
+
|
|
304
|
+
${err.stack}`;
|
|
305
|
+
}
|
|
306
|
+
super(body, {
|
|
307
|
+
...init,
|
|
308
|
+
status: 500,
|
|
309
|
+
statusText: "Internal Server Error"
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
var SeeOtherResponse2 = class extends Response {
|
|
314
|
+
constructor(location, init, {
|
|
315
|
+
preventLeadingDoubleSlash = true
|
|
316
|
+
} = {
|
|
317
|
+
preventLeadingDoubleSlash: true
|
|
318
|
+
}) {
|
|
319
|
+
location = preventLeadingDoubleSlash ? stripLeadingDoubleSlashes(location) : location;
|
|
320
|
+
super(`Redirecting to ${location}`, {
|
|
321
|
+
...init,
|
|
322
|
+
status: 303,
|
|
323
|
+
statusText: "See Other",
|
|
324
|
+
headers: mergeHeaders(init?.headers, { location })
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
var TemporaryRedirectResponse2 = class extends Response {
|
|
329
|
+
constructor(location, init, {
|
|
330
|
+
preventLeadingDoubleSlash = true
|
|
331
|
+
} = {
|
|
332
|
+
preventLeadingDoubleSlash: true
|
|
333
|
+
}) {
|
|
334
|
+
location = preventLeadingDoubleSlash ? stripLeadingDoubleSlashes(location) : location;
|
|
335
|
+
super(`Redirecting to ${location}`, {
|
|
336
|
+
...init,
|
|
337
|
+
status: 307,
|
|
338
|
+
statusText: "Temporary Redirect",
|
|
339
|
+
headers: mergeHeaders(init?.headers, { location })
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// asset-server/handler.ts
|
|
345
|
+
var ASSET_PRESERVATION_CACHE = "assetPreservationCacheV2";
|
|
346
|
+
var CACHE_CONTROL_PRESERVATION = "public, s-maxage=604800";
|
|
347
|
+
var CACHE_PRESERVATION_WRITE_FREQUENCY = 86400;
|
|
348
|
+
var CACHE_CONTROL_BROWSER2 = "public, max-age=0, must-revalidate";
|
|
349
|
+
var REDIRECTS_VERSION2 = 1;
|
|
350
|
+
var HEADERS_VERSION2 = 2;
|
|
351
|
+
var HEADERS_VERSION_V1 = 1;
|
|
352
|
+
var ANALYTICS_VERSION = 1;
|
|
353
|
+
var ALLOWED_EARLY_HINT_LINK_ATTRIBUTES = ["rel", "as", "href"];
|
|
354
|
+
function normaliseHeaders(headers) {
|
|
355
|
+
if (headers.version === HEADERS_VERSION2) {
|
|
356
|
+
return headers.rules;
|
|
357
|
+
} else if (headers.version === HEADERS_VERSION_V1) {
|
|
358
|
+
return Object.keys(headers.rules).reduce(
|
|
359
|
+
(acc, key) => {
|
|
360
|
+
acc[key] = {
|
|
361
|
+
set: headers.rules[key]
|
|
362
|
+
};
|
|
363
|
+
return acc;
|
|
364
|
+
},
|
|
365
|
+
{}
|
|
366
|
+
);
|
|
367
|
+
} else {
|
|
368
|
+
return {};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function generateETagHeader(assetKey) {
|
|
372
|
+
const strongETag = `"${assetKey}"`;
|
|
373
|
+
const weakETag = `W/"${assetKey}"`;
|
|
374
|
+
return { strongETag, weakETag };
|
|
375
|
+
}
|
|
376
|
+
function checkIfNoneMatch(request, strongETag, weakETag) {
|
|
377
|
+
const ifNoneMatch = request.headers.get("if-none-match");
|
|
378
|
+
return ifNoneMatch === weakETag || ifNoneMatch === strongETag;
|
|
379
|
+
}
|
|
380
|
+
async function generateHandler({
|
|
381
|
+
request,
|
|
382
|
+
metadata,
|
|
383
|
+
xServerEnvHeader,
|
|
384
|
+
xDeploymentIdHeader,
|
|
385
|
+
xWebAnalyticsHeader,
|
|
386
|
+
logError,
|
|
387
|
+
setMetrics,
|
|
388
|
+
findAssetEntryForPath,
|
|
389
|
+
getAssetKey,
|
|
390
|
+
negotiateContent,
|
|
391
|
+
fetchAsset,
|
|
392
|
+
generateNotFoundResponse = async (notFoundRequest, notFoundFindAssetEntryForPath, notFoundServeAsset) => {
|
|
393
|
+
let assetEntry;
|
|
394
|
+
if (assetEntry = await notFoundFindAssetEntryForPath("/index.html")) {
|
|
395
|
+
return notFoundServeAsset(assetEntry, { preserve: false });
|
|
396
|
+
}
|
|
397
|
+
return new NotFoundResponse2();
|
|
398
|
+
},
|
|
399
|
+
attachAdditionalHeaders = () => {
|
|
400
|
+
},
|
|
401
|
+
caches,
|
|
402
|
+
waitUntil
|
|
403
|
+
}) {
|
|
404
|
+
const url = new URL(request.url);
|
|
405
|
+
const { protocol, host, search } = url;
|
|
406
|
+
let { pathname } = url;
|
|
407
|
+
const earlyHintsCache = metadata.deploymentId ? await caches?.open(`eh:${metadata.deploymentId}`) : void 0;
|
|
408
|
+
const headerRules = metadata.headers ? normaliseHeaders(metadata.headers) : {};
|
|
409
|
+
const staticRules = metadata.redirects?.version === REDIRECTS_VERSION2 ? metadata.redirects.staticRules || {} : {};
|
|
410
|
+
const staticRedirectsMatcher2 = () => {
|
|
411
|
+
const withHostMatch = staticRules[`https://${host}${pathname}`];
|
|
412
|
+
const withoutHostMatch = staticRules[pathname];
|
|
413
|
+
if (withHostMatch && withoutHostMatch) {
|
|
414
|
+
if (withHostMatch.lineNumber < withoutHostMatch.lineNumber) {
|
|
415
|
+
return withHostMatch;
|
|
416
|
+
} else {
|
|
417
|
+
return withoutHostMatch;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return withHostMatch || withoutHostMatch;
|
|
421
|
+
};
|
|
422
|
+
const generateRedirectsMatcher2 = () => generateRulesMatcher(
|
|
423
|
+
metadata.redirects?.version === REDIRECTS_VERSION2 ? metadata.redirects.rules : {},
|
|
424
|
+
({ status, to }, replacements) => ({
|
|
425
|
+
status,
|
|
426
|
+
to: replacer(to, replacements)
|
|
427
|
+
})
|
|
428
|
+
);
|
|
429
|
+
let assetEntry;
|
|
430
|
+
async function generateResponse() {
|
|
431
|
+
const match = staticRedirectsMatcher2() || generateRedirectsMatcher2()({ request })[0];
|
|
432
|
+
if (match) {
|
|
433
|
+
if (match.status === 200) {
|
|
434
|
+
pathname = new URL(match.to, request.url).pathname;
|
|
435
|
+
} else {
|
|
436
|
+
const { status, to } = match;
|
|
437
|
+
const destination = new URL(to, request.url);
|
|
438
|
+
const location = destination.origin === new URL(request.url).origin ? `${destination.pathname}${destination.search || search}${destination.hash}` : `${destination.href.slice(0, destination.href.length - (destination.search.length + destination.hash.length))}${destination.search ? destination.search : search}${destination.hash}`;
|
|
439
|
+
switch (status) {
|
|
440
|
+
case 301:
|
|
441
|
+
return new MovedPermanentlyResponse2(location, void 0, {
|
|
442
|
+
preventLeadingDoubleSlash: false
|
|
443
|
+
});
|
|
444
|
+
case 303:
|
|
445
|
+
return new SeeOtherResponse2(location, void 0, {
|
|
446
|
+
preventLeadingDoubleSlash: false
|
|
447
|
+
});
|
|
448
|
+
case 307:
|
|
449
|
+
return new TemporaryRedirectResponse2(location, void 0, {
|
|
450
|
+
preventLeadingDoubleSlash: false
|
|
451
|
+
});
|
|
452
|
+
case 308:
|
|
453
|
+
return new PermanentRedirectResponse2(location, void 0, {
|
|
454
|
+
preventLeadingDoubleSlash: false
|
|
455
|
+
});
|
|
456
|
+
case 302:
|
|
457
|
+
default:
|
|
458
|
+
return new FoundResponse2(location, void 0, {
|
|
459
|
+
preventLeadingDoubleSlash: false
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (!request.method.match(/^(get|head)$/i)) {
|
|
465
|
+
return new MethodNotAllowedResponse2();
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
pathname = globalThis.decodeURIComponent(pathname);
|
|
469
|
+
} catch {
|
|
470
|
+
}
|
|
471
|
+
if (pathname.endsWith("/")) {
|
|
472
|
+
if (assetEntry = await findAssetEntryForPath(`${pathname}index.html`)) {
|
|
473
|
+
return serveAsset(assetEntry);
|
|
474
|
+
} else if (pathname.endsWith("/index/")) {
|
|
475
|
+
return new PermanentRedirectResponse2(
|
|
476
|
+
`/${pathname.slice(1, -"index/".length)}${search}`
|
|
477
|
+
);
|
|
478
|
+
} else if (assetEntry = await findAssetEntryForPath(
|
|
479
|
+
`${pathname.replace(/\/$/, ".html")}`
|
|
480
|
+
)) {
|
|
481
|
+
return new PermanentRedirectResponse2(
|
|
482
|
+
`/${pathname.slice(1, -1)}${search}`
|
|
483
|
+
);
|
|
484
|
+
} else {
|
|
485
|
+
return notFound();
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (assetEntry = await findAssetEntryForPath(pathname)) {
|
|
489
|
+
if (pathname.endsWith(".html")) {
|
|
490
|
+
const extensionlessPath = pathname.slice(0, -".html".length);
|
|
491
|
+
if (extensionlessPath.endsWith("/index")) {
|
|
492
|
+
return new PermanentRedirectResponse2(
|
|
493
|
+
`${extensionlessPath.replace(/\/index$/, "/")}${search}`
|
|
494
|
+
);
|
|
495
|
+
} else if (await findAssetEntryForPath(extensionlessPath) || extensionlessPath === "/") {
|
|
496
|
+
return serveAsset(assetEntry);
|
|
497
|
+
} else {
|
|
498
|
+
return new PermanentRedirectResponse2(`${extensionlessPath}${search}`);
|
|
499
|
+
}
|
|
500
|
+
} else {
|
|
501
|
+
return serveAsset(assetEntry);
|
|
502
|
+
}
|
|
503
|
+
} else if (pathname.endsWith("/index")) {
|
|
504
|
+
return new PermanentRedirectResponse2(
|
|
505
|
+
`/${pathname.slice(1, -"index".length)}${search}`
|
|
506
|
+
);
|
|
507
|
+
} else if (assetEntry = await findAssetEntryForPath(`${pathname}.html`)) {
|
|
508
|
+
return serveAsset(assetEntry);
|
|
509
|
+
}
|
|
510
|
+
if (assetEntry = await findAssetEntryForPath(`${pathname}/index.html`)) {
|
|
511
|
+
return new PermanentRedirectResponse2(`${pathname}/${search}`);
|
|
512
|
+
} else {
|
|
513
|
+
return notFound();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
function isNullBodyStatus(status) {
|
|
517
|
+
return [101, 204, 205, 304].includes(status);
|
|
518
|
+
}
|
|
519
|
+
async function attachHeaders(response) {
|
|
520
|
+
const existingHeaders = new Headers(response.headers);
|
|
521
|
+
const eTag = existingHeaders.get("eTag")?.match(/^"(.*)"$/)?.[1];
|
|
522
|
+
const extraHeaders = new Headers({
|
|
523
|
+
"access-control-allow-origin": "*",
|
|
524
|
+
"referrer-policy": "strict-origin-when-cross-origin",
|
|
525
|
+
...existingHeaders.has("content-type") ? { "x-content-type-options": "nosniff" } : {}
|
|
526
|
+
});
|
|
527
|
+
const headers = new Headers({
|
|
528
|
+
// But we intentionally override existing headers
|
|
529
|
+
...Object.fromEntries(existingHeaders.entries()),
|
|
530
|
+
...Object.fromEntries(extraHeaders.entries())
|
|
531
|
+
});
|
|
532
|
+
if (earlyHintsCache && isHTMLContentType(response.headers.get("Content-Type")) && eTag) {
|
|
533
|
+
const preEarlyHintsHeaders = new Headers(headers);
|
|
534
|
+
const earlyHintsCacheKey = `${protocol}//${host}/${eTag}`;
|
|
535
|
+
const earlyHintsResponse = await earlyHintsCache.match(earlyHintsCacheKey);
|
|
536
|
+
if (earlyHintsResponse) {
|
|
537
|
+
const earlyHintsLinkHeader = earlyHintsResponse.headers.get("Link");
|
|
538
|
+
if (earlyHintsLinkHeader) {
|
|
539
|
+
headers.set("Link", earlyHintsLinkHeader);
|
|
540
|
+
if (setMetrics) {
|
|
541
|
+
setMetrics({ earlyHintsResult: "used-hit" });
|
|
542
|
+
}
|
|
543
|
+
} else {
|
|
544
|
+
if (setMetrics) {
|
|
545
|
+
setMetrics({ earlyHintsResult: "notused-hit" });
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
} else {
|
|
549
|
+
if (setMetrics) {
|
|
550
|
+
setMetrics({ earlyHintsResult: "notused-miss" });
|
|
551
|
+
}
|
|
552
|
+
const clonedResponse = response.clone();
|
|
553
|
+
if (waitUntil) {
|
|
554
|
+
waitUntil(
|
|
555
|
+
(async () => {
|
|
556
|
+
try {
|
|
557
|
+
const links = [];
|
|
558
|
+
const transformedResponse = new HTMLRewriter().on(
|
|
559
|
+
"link[rel~=preconnect],link[rel~=preload],link[rel~=modulepreload]",
|
|
560
|
+
{
|
|
561
|
+
element(element) {
|
|
562
|
+
for (const [attributeName] of element.attributes) {
|
|
563
|
+
if (!ALLOWED_EARLY_HINT_LINK_ATTRIBUTES.includes(
|
|
564
|
+
attributeName.toLowerCase()
|
|
565
|
+
)) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
const href = element.getAttribute("href") || void 0;
|
|
570
|
+
const rel = element.getAttribute("rel") || void 0;
|
|
571
|
+
const as = element.getAttribute("as") || void 0;
|
|
572
|
+
if (href && !href.startsWith("data:") && rel) {
|
|
573
|
+
links.push({ href, rel, as });
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
).transform(clonedResponse);
|
|
578
|
+
await transformedResponse.text();
|
|
579
|
+
links.forEach(({ href, rel, as }) => {
|
|
580
|
+
let link = `<${href}>; rel="${rel}"`;
|
|
581
|
+
if (as) {
|
|
582
|
+
link += `; as=${as}`;
|
|
583
|
+
}
|
|
584
|
+
preEarlyHintsHeaders.append("Link", link);
|
|
585
|
+
});
|
|
586
|
+
const linkHeader = preEarlyHintsHeaders.get("Link");
|
|
587
|
+
const earlyHintsHeaders = new Headers({
|
|
588
|
+
"Cache-Control": "max-age=2592000"
|
|
589
|
+
// 30 days
|
|
590
|
+
});
|
|
591
|
+
if (linkHeader) {
|
|
592
|
+
earlyHintsHeaders.append("Link", linkHeader);
|
|
593
|
+
}
|
|
594
|
+
await earlyHintsCache.put(
|
|
595
|
+
earlyHintsCacheKey,
|
|
596
|
+
new Response(null, { headers: earlyHintsHeaders })
|
|
597
|
+
);
|
|
598
|
+
} catch {
|
|
599
|
+
await earlyHintsCache.put(
|
|
600
|
+
earlyHintsCacheKey,
|
|
601
|
+
new Response(null, {
|
|
602
|
+
headers: {
|
|
603
|
+
"Cache-Control": "max-age=86400"
|
|
604
|
+
// 1 day
|
|
605
|
+
}
|
|
606
|
+
})
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
})()
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
} else {
|
|
614
|
+
if (setMetrics) {
|
|
615
|
+
setMetrics({ earlyHintsResult: "disabled" });
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
const headersMatcher = generateRulesMatcher(
|
|
619
|
+
headerRules,
|
|
620
|
+
({ set = {}, unset = [] }, replacements) => {
|
|
621
|
+
const replacedSet = {};
|
|
622
|
+
Object.keys(set).forEach((key) => {
|
|
623
|
+
replacedSet[key] = replacer(set[key], replacements);
|
|
624
|
+
});
|
|
625
|
+
return {
|
|
626
|
+
set: replacedSet,
|
|
627
|
+
unset
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
);
|
|
631
|
+
const matches = headersMatcher({ request });
|
|
632
|
+
const setMap = /* @__PURE__ */ new Set();
|
|
633
|
+
matches.forEach(({ set = {}, unset = [] }) => {
|
|
634
|
+
unset.forEach((key) => {
|
|
635
|
+
headers.delete(key);
|
|
636
|
+
});
|
|
637
|
+
Object.keys(set).forEach((key) => {
|
|
638
|
+
if (setMap.has(key.toLowerCase())) {
|
|
639
|
+
headers.append(key, set[key]);
|
|
640
|
+
} else {
|
|
641
|
+
headers.set(key, set[key]);
|
|
642
|
+
setMap.add(key.toLowerCase());
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
});
|
|
646
|
+
return new Response(
|
|
647
|
+
isNullBodyStatus(response.status) ? null : response.body,
|
|
648
|
+
{
|
|
649
|
+
headers,
|
|
650
|
+
status: response.status,
|
|
651
|
+
statusText: response.statusText
|
|
652
|
+
}
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
const responseWithoutHeaders = await generateResponse();
|
|
656
|
+
if (responseWithoutHeaders.status >= 500) {
|
|
657
|
+
return responseWithoutHeaders;
|
|
658
|
+
}
|
|
659
|
+
const responseWithHeaders = await attachHeaders(responseWithoutHeaders);
|
|
660
|
+
if (responseWithHeaders.status === 404) {
|
|
661
|
+
if (responseWithHeaders.headers.has("cache-control")) {
|
|
662
|
+
responseWithHeaders.headers.delete("cache-control");
|
|
663
|
+
}
|
|
664
|
+
responseWithHeaders.headers.append("cache-control", "no-store");
|
|
665
|
+
}
|
|
666
|
+
return responseWithHeaders;
|
|
667
|
+
async function serveAsset(servingAssetEntry, options = { preserve: true }) {
|
|
668
|
+
let content;
|
|
669
|
+
try {
|
|
670
|
+
content = negotiateContent(request, servingAssetEntry);
|
|
671
|
+
} catch {
|
|
672
|
+
return new NotAcceptableResponse();
|
|
673
|
+
}
|
|
674
|
+
const assetKey = getAssetKey(servingAssetEntry, content);
|
|
675
|
+
const { strongETag, weakETag } = generateETagHeader(assetKey);
|
|
676
|
+
const isIfNoneMatch = checkIfNoneMatch(request, strongETag, weakETag);
|
|
677
|
+
if (isIfNoneMatch) {
|
|
678
|
+
return new NotModifiedResponse2();
|
|
679
|
+
}
|
|
680
|
+
try {
|
|
681
|
+
const asset = await fetchAsset(assetKey);
|
|
682
|
+
const headers = {
|
|
683
|
+
etag: strongETag,
|
|
684
|
+
"content-type": asset.contentType
|
|
685
|
+
};
|
|
686
|
+
let encodeBody = "automatic";
|
|
687
|
+
if (xServerEnvHeader) {
|
|
688
|
+
headers["x-server-env"] = xServerEnvHeader;
|
|
689
|
+
}
|
|
690
|
+
if (xDeploymentIdHeader && metadata.deploymentId) {
|
|
691
|
+
headers["x-deployment-id"] = metadata.deploymentId;
|
|
692
|
+
}
|
|
693
|
+
if (content.encoding) {
|
|
694
|
+
encodeBody = "manual";
|
|
695
|
+
headers["cache-control"] = "no-transform";
|
|
696
|
+
headers["content-encoding"] = content.encoding;
|
|
697
|
+
}
|
|
698
|
+
const response = new OkResponse2(
|
|
699
|
+
request.method === "HEAD" ? null : asset.body,
|
|
700
|
+
{
|
|
701
|
+
headers,
|
|
702
|
+
encodeBody
|
|
703
|
+
}
|
|
704
|
+
);
|
|
705
|
+
if (isCacheable(request)) {
|
|
706
|
+
response.headers.append("cache-control", CACHE_CONTROL_BROWSER2);
|
|
707
|
+
}
|
|
708
|
+
attachAdditionalHeaders(response, content, servingAssetEntry, asset);
|
|
709
|
+
if (isPreview(new URL(request.url))) {
|
|
710
|
+
response.headers.set("x-robots-tag", "noindex");
|
|
711
|
+
}
|
|
712
|
+
if (options.preserve && waitUntil && caches) {
|
|
713
|
+
waitUntil(
|
|
714
|
+
(async () => {
|
|
715
|
+
try {
|
|
716
|
+
const assetPreservationCache = await caches.open(
|
|
717
|
+
ASSET_PRESERVATION_CACHE
|
|
718
|
+
);
|
|
719
|
+
const match = await assetPreservationCache.match(request);
|
|
720
|
+
if (!match || assetKey !== await match.text() || isPreservationCacheResponseExpiring(match)) {
|
|
721
|
+
const preservedResponse = new Response(assetKey, response);
|
|
722
|
+
preservedResponse.headers.set(
|
|
723
|
+
"cache-control",
|
|
724
|
+
CACHE_CONTROL_PRESERVATION
|
|
725
|
+
);
|
|
726
|
+
preservedResponse.headers.set("x-robots-tag", "noindex");
|
|
727
|
+
await assetPreservationCache.put(
|
|
728
|
+
request.url,
|
|
729
|
+
preservedResponse
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
} catch (err) {
|
|
733
|
+
logError(err);
|
|
734
|
+
}
|
|
735
|
+
})()
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
if (isHTMLContentType(asset.contentType) && metadata.analytics?.version === ANALYTICS_VERSION) {
|
|
739
|
+
if (xWebAnalyticsHeader) {
|
|
740
|
+
response.headers.set("x-cf-pages-analytics", "1");
|
|
741
|
+
}
|
|
742
|
+
return new HTMLRewriter().on("body", {
|
|
743
|
+
element(e) {
|
|
744
|
+
e.append(
|
|
745
|
+
`<!-- Cloudflare Pages Analytics --><script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "${metadata.analytics?.token}"}'><\/script><!-- Cloudflare Pages Analytics -->`,
|
|
746
|
+
{ html: true }
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
}).transform(response);
|
|
750
|
+
}
|
|
751
|
+
return response;
|
|
752
|
+
} catch (err) {
|
|
753
|
+
logError(err);
|
|
754
|
+
return new InternalServerErrorResponse2(err);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
async function notFound() {
|
|
758
|
+
if (caches) {
|
|
759
|
+
try {
|
|
760
|
+
const assetPreservationCache = await caches.open(
|
|
761
|
+
ASSET_PRESERVATION_CACHE
|
|
762
|
+
);
|
|
763
|
+
const preservedResponse = await assetPreservationCache.match(
|
|
764
|
+
request.url
|
|
765
|
+
);
|
|
766
|
+
if (preservedResponse) {
|
|
767
|
+
if (setMetrics) {
|
|
768
|
+
setMetrics({ preservationCacheResult: "checked-hit" });
|
|
769
|
+
}
|
|
770
|
+
const assetKey = await preservedResponse.text();
|
|
771
|
+
if (isNullBodyStatus(preservedResponse.status)) {
|
|
772
|
+
return new Response(null, preservedResponse);
|
|
773
|
+
}
|
|
774
|
+
if (assetKey) {
|
|
775
|
+
const { strongETag, weakETag } = generateETagHeader(assetKey);
|
|
776
|
+
const isIfNoneMatch = checkIfNoneMatch(
|
|
777
|
+
request,
|
|
778
|
+
strongETag,
|
|
779
|
+
weakETag
|
|
780
|
+
);
|
|
781
|
+
if (isIfNoneMatch) {
|
|
782
|
+
if (setMetrics) {
|
|
783
|
+
setMetrics({ preservationCacheResult: "not-modified" });
|
|
784
|
+
}
|
|
785
|
+
return new NotModifiedResponse2();
|
|
786
|
+
}
|
|
787
|
+
const asset = await fetchAsset(assetKey);
|
|
788
|
+
if (asset) {
|
|
789
|
+
return new Response(asset.body, preservedResponse);
|
|
790
|
+
} else {
|
|
791
|
+
logError(
|
|
792
|
+
new Error(
|
|
793
|
+
`preservation cache contained assetKey that does not exist in storage: ${assetKey}`
|
|
794
|
+
)
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
} else {
|
|
798
|
+
logError(new Error(`cached response had no assetKey: ${assetKey}`));
|
|
799
|
+
}
|
|
800
|
+
} else {
|
|
801
|
+
if (setMetrics) {
|
|
802
|
+
setMetrics({ preservationCacheResult: "checked-miss" });
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
} catch (err) {
|
|
806
|
+
logError(err);
|
|
807
|
+
}
|
|
808
|
+
} else {
|
|
809
|
+
if (setMetrics) {
|
|
810
|
+
setMetrics({ preservationCacheResult: "disabled" });
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
let cwd = pathname;
|
|
814
|
+
while (cwd) {
|
|
815
|
+
cwd = cwd.slice(0, cwd.lastIndexOf("/"));
|
|
816
|
+
if (assetEntry = await findAssetEntryForPath(`${cwd}/404.html`)) {
|
|
817
|
+
let content;
|
|
818
|
+
try {
|
|
819
|
+
content = negotiateContent(request, assetEntry);
|
|
820
|
+
} catch {
|
|
821
|
+
return new NotAcceptableResponse();
|
|
822
|
+
}
|
|
823
|
+
const assetKey = getAssetKey(assetEntry, content);
|
|
824
|
+
try {
|
|
825
|
+
const { body, contentType } = await fetchAsset(assetKey);
|
|
826
|
+
const response = new NotFoundResponse2(body);
|
|
827
|
+
response.headers.set("content-type", contentType);
|
|
828
|
+
return response;
|
|
829
|
+
} catch (err) {
|
|
830
|
+
logError(err);
|
|
831
|
+
return new InternalServerErrorResponse2(err);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
return await generateNotFoundResponse(
|
|
836
|
+
request,
|
|
837
|
+
findAssetEntryForPath,
|
|
838
|
+
serveAsset
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
function parseQualityWeightedList(list = "") {
|
|
843
|
+
const items = {};
|
|
844
|
+
list.replace(/\s/g, "").split(",").forEach((el) => {
|
|
845
|
+
const [item, weight] = el.split(";q=");
|
|
846
|
+
items[item] = weight ? parseFloat(weight) : 1;
|
|
847
|
+
});
|
|
848
|
+
return items;
|
|
849
|
+
}
|
|
850
|
+
function isCacheable(request) {
|
|
851
|
+
return !request.headers.has("authorization") && !request.headers.has("range");
|
|
852
|
+
}
|
|
853
|
+
function isPreview(url) {
|
|
854
|
+
if (url.hostname.endsWith(".pages.dev")) {
|
|
855
|
+
return url.hostname.split(".").length > 3 ? true : false;
|
|
856
|
+
}
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
function isPreservationCacheResponseExpiring(response) {
|
|
860
|
+
const ageHeader = response.headers.get("age");
|
|
861
|
+
if (!ageHeader) {
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
try {
|
|
865
|
+
const age = parseInt(ageHeader);
|
|
866
|
+
const jitter = Math.floor(Math.random() * 43200);
|
|
867
|
+
if (age > CACHE_PRESERVATION_WRITE_FREQUENCY + jitter) {
|
|
868
|
+
return true;
|
|
869
|
+
}
|
|
870
|
+
} catch {
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
return false;
|
|
874
|
+
}
|
|
875
|
+
function isHTMLContentType(contentType) {
|
|
876
|
+
return contentType?.toLowerCase().startsWith("text/html") || false;
|
|
877
|
+
}
|
|
878
|
+
export {
|
|
879
|
+
ANALYTICS_VERSION,
|
|
880
|
+
ASSET_PRESERVATION_CACHE,
|
|
881
|
+
CACHE_CONTROL_BROWSER2 as CACHE_CONTROL_BROWSER,
|
|
882
|
+
CACHE_PRESERVATION_WRITE_FREQUENCY,
|
|
883
|
+
HEADERS_VERSION2 as HEADERS_VERSION,
|
|
884
|
+
HEADERS_VERSION_V1,
|
|
885
|
+
REDIRECTS_VERSION2 as REDIRECTS_VERSION,
|
|
886
|
+
generateHandler,
|
|
887
|
+
isPreservationCacheResponseExpiring,
|
|
888
|
+
normaliseHeaders,
|
|
889
|
+
parseQualityWeightedList
|
|
890
|
+
};
|