@netlify/plugin-nextjs 5.13.5 → 5.14.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/build/content/prerendered.js +1 -1
- package/dist/build/content/server.js +1 -1
- package/dist/build/plugin-context.js +5 -1
- package/dist/build/skew-protection.js +106 -0
- package/dist/build/verification.js +1 -1
- package/dist/esm-chunks/{chunk-TLQCAGE2.js → chunk-TVEBGDAB.js} +23 -1
- package/dist/index.js +3 -1
- package/dist/run/handlers/cache.cjs +18 -8
- package/dist/run/handlers/tags-handler.cjs +62 -32
- package/dist/run/handlers/use-cache-handler.js +5 -4
- package/dist/shared/blob-types.cjs +1 -1
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
require_semver
|
|
9
|
-
} from "../esm-chunks/chunk-
|
|
9
|
+
} from "../esm-chunks/chunk-TVEBGDAB.js";
|
|
10
10
|
import {
|
|
11
11
|
__toESM
|
|
12
12
|
} from "../esm-chunks/chunk-6BT4RYQJ.js";
|
|
@@ -150,6 +150,10 @@ var PluginContext = class {
|
|
|
150
150
|
get edgeHandlerDir() {
|
|
151
151
|
return join(this.edgeFunctionsDir, EDGE_HANDLER_NAME);
|
|
152
152
|
}
|
|
153
|
+
/** Absolute path to the skew protection config */
|
|
154
|
+
get skewProtectionConfigPath() {
|
|
155
|
+
return this.resolveFromPackagePath(".netlify/v1/skew-protection.json");
|
|
156
|
+
}
|
|
153
157
|
constructor(options) {
|
|
154
158
|
this.constants = options.constants;
|
|
155
159
|
this.featureFlags = options.featureFlags;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
|
|
2
|
+
var require = await (async () => {
|
|
3
|
+
var { createRequire } = await import("node:module");
|
|
4
|
+
return createRequire(import.meta.url);
|
|
5
|
+
})();
|
|
6
|
+
|
|
7
|
+
import "../esm-chunks/chunk-6BT4RYQJ.js";
|
|
8
|
+
|
|
9
|
+
// src/build/skew-protection.ts
|
|
10
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
var EnabledOrDisabledReason = /* @__PURE__ */ ((EnabledOrDisabledReason2) => {
|
|
13
|
+
EnabledOrDisabledReason2["OPT_OUT_DEFAULT"] = "off-default";
|
|
14
|
+
EnabledOrDisabledReason2["OPT_OUT_NO_VALID_DEPLOY_ID"] = "off-no-valid-deploy-id";
|
|
15
|
+
EnabledOrDisabledReason2["OPT_OUT_NO_VALID_DEPLOY_ID_ENV_VAR"] = "off-no-valid-deploy-id-env-var";
|
|
16
|
+
EnabledOrDisabledReason2["OPT_IN_FF"] = "on-ff";
|
|
17
|
+
EnabledOrDisabledReason2["OPT_IN_ENV_VAR"] = "on-env-var";
|
|
18
|
+
EnabledOrDisabledReason2["OPT_OUT_ENV_VAR"] = "off-env-var";
|
|
19
|
+
return EnabledOrDisabledReason2;
|
|
20
|
+
})(EnabledOrDisabledReason || {});
|
|
21
|
+
var optInOptions = /* @__PURE__ */ new Set([
|
|
22
|
+
"on-ff" /* OPT_IN_FF */,
|
|
23
|
+
"on-env-var" /* OPT_IN_ENV_VAR */
|
|
24
|
+
]);
|
|
25
|
+
var skewProtectionConfig = {
|
|
26
|
+
patterns: [".*"],
|
|
27
|
+
sources: [
|
|
28
|
+
{
|
|
29
|
+
type: "cookie",
|
|
30
|
+
name: "__vdpl"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: "header",
|
|
34
|
+
name: "X-Deployment-Id"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: "query",
|
|
38
|
+
name: "dpl"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
};
|
|
42
|
+
function shouldEnableSkewProtection(ctx) {
|
|
43
|
+
let enabledOrDisabledReason = "off-default" /* OPT_OUT_DEFAULT */;
|
|
44
|
+
if (process.env.NETLIFY_NEXT_SKEW_PROTECTION === "true" || process.env.NETLIFY_NEXT_SKEW_PROTECTION === "1") {
|
|
45
|
+
enabledOrDisabledReason = "on-env-var" /* OPT_IN_ENV_VAR */;
|
|
46
|
+
} else if (process.env.NETLIFY_NEXT_SKEW_PROTECTION === "false" || process.env.NETLIFY_NEXT_SKEW_PROTECTION === "0") {
|
|
47
|
+
return {
|
|
48
|
+
enabled: false,
|
|
49
|
+
enabledOrDisabledReason: "off-env-var" /* OPT_OUT_ENV_VAR */
|
|
50
|
+
};
|
|
51
|
+
} else if (ctx.featureFlags?.["next-runtime-skew-protection"]) {
|
|
52
|
+
enabledOrDisabledReason = "on-ff" /* OPT_IN_FF */;
|
|
53
|
+
} else {
|
|
54
|
+
return {
|
|
55
|
+
enabled: false,
|
|
56
|
+
enabledOrDisabledReason: "off-default" /* OPT_OUT_DEFAULT */
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if ((!process.env.DEPLOY_ID || process.env.DEPLOY_ID === "0") && optInOptions.has(enabledOrDisabledReason)) {
|
|
60
|
+
return {
|
|
61
|
+
enabled: false,
|
|
62
|
+
enabledOrDisabledReason: enabledOrDisabledReason === "on-env-var" /* OPT_IN_ENV_VAR */ && ctx.constants.IS_LOCAL ? (
|
|
63
|
+
// this case is singled out to provide visible feedback to users that env var has no effect
|
|
64
|
+
"off-no-valid-deploy-id-env-var" /* OPT_OUT_NO_VALID_DEPLOY_ID_ENV_VAR */
|
|
65
|
+
) : (
|
|
66
|
+
// this is silent disablement to avoid spam logs for users opted in via feature flag
|
|
67
|
+
// that don't explicitly opt in via env var
|
|
68
|
+
"off-no-valid-deploy-id" /* OPT_OUT_NO_VALID_DEPLOY_ID */
|
|
69
|
+
)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
enabled: optInOptions.has(enabledOrDisabledReason),
|
|
74
|
+
enabledOrDisabledReason
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
var setSkewProtection = async (ctx, span) => {
|
|
78
|
+
const { enabled, enabledOrDisabledReason } = shouldEnableSkewProtection(ctx);
|
|
79
|
+
span.setAttribute("skewProtection", enabledOrDisabledReason);
|
|
80
|
+
if (!enabled) {
|
|
81
|
+
if (enabledOrDisabledReason === "off-no-valid-deploy-id-env-var" /* OPT_OUT_NO_VALID_DEPLOY_ID_ENV_VAR */) {
|
|
82
|
+
console.warn(
|
|
83
|
+
`NETLIFY_NEXT_SKEW_PROTECTION environment variable is set to ${process.env.NETLIFY_NEXT_SKEW_PROTECTION}, but skew protection is currently unavailable for CLI deploys. Skew protection will not be enabled.`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (enabledOrDisabledReason === "on-env-var" /* OPT_IN_ENV_VAR */) {
|
|
89
|
+
console.log(
|
|
90
|
+
`Setting up Next.js Skew Protection due to NETLIFY_NEXT_SKEW_PROTECTION=${process.env.NETLIFY_NEXT_SKEW_PROTECTION} environment variable.`
|
|
91
|
+
);
|
|
92
|
+
} else {
|
|
93
|
+
console.log("Setting up Next.js Skew Protection.");
|
|
94
|
+
}
|
|
95
|
+
process.env.NEXT_DEPLOYMENT_ID = process.env.DEPLOY_ID;
|
|
96
|
+
await mkdir(dirname(ctx.skewProtectionConfigPath), {
|
|
97
|
+
recursive: true
|
|
98
|
+
});
|
|
99
|
+
await writeFile(ctx.skewProtectionConfigPath, JSON.stringify(skewProtectionConfig));
|
|
100
|
+
};
|
|
101
|
+
export {
|
|
102
|
+
EnabledOrDisabledReason,
|
|
103
|
+
setSkewProtection,
|
|
104
|
+
shouldEnableSkewProtection,
|
|
105
|
+
skewProtectionConfig
|
|
106
|
+
};
|
|
@@ -163,6 +163,9 @@ var require_identifiers = __commonJS({
|
|
|
163
163
|
"use strict";
|
|
164
164
|
var numeric = /^[0-9]+$/;
|
|
165
165
|
var compareIdentifiers = (a, b) => {
|
|
166
|
+
if (typeof a === "number" && typeof b === "number") {
|
|
167
|
+
return a === b ? 0 : a < b ? -1 : 1;
|
|
168
|
+
}
|
|
166
169
|
const anum = numeric.test(a);
|
|
167
170
|
const bnum = numeric.test(b);
|
|
168
171
|
if (anum && bnum) {
|
|
@@ -269,7 +272,25 @@ var require_semver = __commonJS({
|
|
|
269
272
|
if (!(other instanceof _SemVer)) {
|
|
270
273
|
other = new _SemVer(other, this.options);
|
|
271
274
|
}
|
|
272
|
-
|
|
275
|
+
if (this.major < other.major) {
|
|
276
|
+
return -1;
|
|
277
|
+
}
|
|
278
|
+
if (this.major > other.major) {
|
|
279
|
+
return 1;
|
|
280
|
+
}
|
|
281
|
+
if (this.minor < other.minor) {
|
|
282
|
+
return -1;
|
|
283
|
+
}
|
|
284
|
+
if (this.minor > other.minor) {
|
|
285
|
+
return 1;
|
|
286
|
+
}
|
|
287
|
+
if (this.patch < other.patch) {
|
|
288
|
+
return -1;
|
|
289
|
+
}
|
|
290
|
+
if (this.patch > other.patch) {
|
|
291
|
+
return 1;
|
|
292
|
+
}
|
|
293
|
+
return 0;
|
|
273
294
|
}
|
|
274
295
|
comparePre(other) {
|
|
275
296
|
if (!(other instanceof _SemVer)) {
|
|
@@ -1030,6 +1051,7 @@ var require_range = __commonJS({
|
|
|
1030
1051
|
return result;
|
|
1031
1052
|
};
|
|
1032
1053
|
var parseComparator = (comp, options) => {
|
|
1054
|
+
comp = comp.replace(re[t.BUILD], "");
|
|
1033
1055
|
debug("comp", comp, options);
|
|
1034
1056
|
comp = replaceCarets(comp, options);
|
|
1035
1057
|
debug("caret", comp);
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,7 @@ import { clearStaleEdgeHandlers, createEdgeHandlers } from "./build/functions/ed
|
|
|
26
26
|
import { clearStaleServerHandlers, createServerHandler } from "./build/functions/server.js";
|
|
27
27
|
import { setImageConfig } from "./build/image-cdn.js";
|
|
28
28
|
import { PluginContext } from "./build/plugin-context.js";
|
|
29
|
+
import { setSkewProtection } from "./build/skew-protection.js";
|
|
29
30
|
import {
|
|
30
31
|
verifyAdvancedAPIRoutes,
|
|
31
32
|
verifyNetlifyFormsWorkaround,
|
|
@@ -49,7 +50,7 @@ var onPreBuild = async (options) => {
|
|
|
49
50
|
console.warn(skipText);
|
|
50
51
|
return;
|
|
51
52
|
}
|
|
52
|
-
await tracer.withActiveSpan("onPreBuild", async () => {
|
|
53
|
+
await tracer.withActiveSpan("onPreBuild", async (span) => {
|
|
53
54
|
process.env.NEXT_PRIVATE_STANDALONE = "true";
|
|
54
55
|
const ctx = new PluginContext(options);
|
|
55
56
|
if (options.constants.IS_LOCAL) {
|
|
@@ -58,6 +59,7 @@ var onPreBuild = async (options) => {
|
|
|
58
59
|
} else {
|
|
59
60
|
await restoreBuildCache(ctx);
|
|
60
61
|
}
|
|
62
|
+
await setSkewProtection(ctx, span);
|
|
61
63
|
});
|
|
62
64
|
};
|
|
63
65
|
var onBuild = async (options) => {
|
|
@@ -204,16 +204,20 @@ var NetlifyCacheHandler = class {
|
|
|
204
204
|
);
|
|
205
205
|
return null;
|
|
206
206
|
}
|
|
207
|
-
const staleByTags = await this.checkCacheEntryStaleByTags(
|
|
207
|
+
const { stale: staleByTags, expired: expiredByTags } = await this.checkCacheEntryStaleByTags(
|
|
208
208
|
blob,
|
|
209
209
|
context.tags,
|
|
210
210
|
context.softTags
|
|
211
211
|
);
|
|
212
|
-
if (
|
|
213
|
-
span.addEvent("
|
|
212
|
+
if (expiredByTags) {
|
|
213
|
+
span.addEvent("Expired", { expiredByTags, key, ttl });
|
|
214
214
|
return null;
|
|
215
215
|
}
|
|
216
216
|
this.captureResponseCacheLastModified(blob, key, span);
|
|
217
|
+
if (staleByTags) {
|
|
218
|
+
span.addEvent("Stale", { staleByTags, key, ttl });
|
|
219
|
+
blob.lastModified = -1;
|
|
220
|
+
}
|
|
217
221
|
const isDataRequest = Boolean(context.fetchUrl);
|
|
218
222
|
if (!isDataRequest) {
|
|
219
223
|
this.captureCacheTags(blob.value, key);
|
|
@@ -350,8 +354,8 @@ var NetlifyCacheHandler = class {
|
|
|
350
354
|
}
|
|
351
355
|
});
|
|
352
356
|
}
|
|
353
|
-
async revalidateTag(tagOrTags) {
|
|
354
|
-
return (0, import_tags_handler.markTagsAsStaleAndPurgeEdgeCache)(tagOrTags);
|
|
357
|
+
async revalidateTag(tagOrTags, durations) {
|
|
358
|
+
return (0, import_tags_handler.markTagsAsStaleAndPurgeEdgeCache)(tagOrTags, durations);
|
|
355
359
|
}
|
|
356
360
|
resetRequestCache() {
|
|
357
361
|
}
|
|
@@ -365,16 +369,22 @@ var NetlifyCacheHandler = class {
|
|
|
365
369
|
} else if (cacheEntry.value?.kind === "PAGE" || cacheEntry.value?.kind === "PAGES" || cacheEntry.value?.kind === "APP_PAGE" || cacheEntry.value?.kind === "ROUTE" || cacheEntry.value?.kind === "APP_ROUTE") {
|
|
366
370
|
cacheTags = cacheEntry.value.headers?.[import_constants.NEXT_CACHE_TAGS_HEADER]?.split(/,|%2c/gi) || [];
|
|
367
371
|
} else {
|
|
368
|
-
return
|
|
372
|
+
return {
|
|
373
|
+
stale: false,
|
|
374
|
+
expired: false
|
|
375
|
+
};
|
|
369
376
|
}
|
|
370
377
|
if (this.revalidatedTags && this.revalidatedTags.length !== 0) {
|
|
371
378
|
for (const tag of this.revalidatedTags) {
|
|
372
379
|
if (cacheTags.includes(tag)) {
|
|
373
|
-
return
|
|
380
|
+
return {
|
|
381
|
+
stale: true,
|
|
382
|
+
expired: true
|
|
383
|
+
};
|
|
374
384
|
}
|
|
375
385
|
}
|
|
376
386
|
}
|
|
377
|
-
return (0, import_tags_handler.
|
|
387
|
+
return (0, import_tags_handler.isAnyTagStaleOrExpired)(cacheTags, cacheEntry.lastModified);
|
|
378
388
|
}
|
|
379
389
|
};
|
|
380
390
|
var cache_default = NetlifyCacheHandler;
|
|
@@ -20,8 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/run/handlers/tags-handler.cts
|
|
21
21
|
var tags_handler_exports = {};
|
|
22
22
|
__export(tags_handler_exports, {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
getMostRecentTagExpirationTimestamp: () => getMostRecentTagExpirationTimestamp,
|
|
24
|
+
isAnyTagStaleOrExpired: () => isAnyTagStaleOrExpired,
|
|
25
25
|
markTagsAsStaleAndPurgeEdgeCache: () => markTagsAsStaleAndPurgeEdgeCache,
|
|
26
26
|
purgeEdgeCache: () => purgeEdgeCache
|
|
27
27
|
});
|
|
@@ -86,58 +86,86 @@ var pipeline = (0, import_util.promisify)(import_stream.pipeline);
|
|
|
86
86
|
|
|
87
87
|
// package.json
|
|
88
88
|
var name = "@netlify/plugin-nextjs";
|
|
89
|
-
var version = "5.
|
|
89
|
+
var version = "5.14.0";
|
|
90
90
|
|
|
91
91
|
// src/run/handlers/tags-handler.cts
|
|
92
92
|
var import_storage = require("../storage/storage.cjs");
|
|
93
93
|
var import_request_context = require("./request-context.cjs");
|
|
94
94
|
var purgeCacheUserAgent = `${name}@${version}`;
|
|
95
|
-
async function
|
|
95
|
+
async function getTagManifest(tag, cacheStore) {
|
|
96
96
|
const tagManifest = await cacheStore.get(tag, "tagManifest.get");
|
|
97
97
|
if (!tagManifest) {
|
|
98
98
|
return null;
|
|
99
99
|
}
|
|
100
|
-
return tagManifest
|
|
100
|
+
return tagManifest;
|
|
101
101
|
}
|
|
102
|
-
async function
|
|
102
|
+
async function getMostRecentTagExpirationTimestamp(tags) {
|
|
103
103
|
if (tags.length === 0) {
|
|
104
104
|
return 0;
|
|
105
105
|
}
|
|
106
106
|
const cacheStore = (0, import_storage.getMemoizedKeyValueStoreBackedByRegionalBlobStore)({ consistency: "strong" });
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
)
|
|
110
|
-
const timestamps = timestampsOrNulls.filter((timestamp) => timestamp !== null);
|
|
111
|
-
if (timestamps.length === 0) {
|
|
107
|
+
const manifestsOrNulls = await Promise.all(tags.map((tag) => getTagManifest(tag, cacheStore)));
|
|
108
|
+
const expirationTimestamps = manifestsOrNulls.filter((manifest) => manifest !== null).map((manifest) => manifest.expireAt);
|
|
109
|
+
if (expirationTimestamps.length === 0) {
|
|
112
110
|
return 0;
|
|
113
111
|
}
|
|
114
|
-
return Math.max(...
|
|
112
|
+
return Math.max(...expirationTimestamps);
|
|
115
113
|
}
|
|
116
|
-
function
|
|
114
|
+
function isAnyTagStaleOrExpired(tags, timestamp) {
|
|
117
115
|
if (tags.length === 0 || !timestamp) {
|
|
118
|
-
return Promise.resolve(false);
|
|
116
|
+
return Promise.resolve({ stale: false, expired: false });
|
|
119
117
|
}
|
|
120
118
|
const cacheStore = (0, import_storage.getMemoizedKeyValueStoreBackedByRegionalBlobStore)({ consistency: "strong" });
|
|
121
119
|
return new Promise((resolve, reject) => {
|
|
122
120
|
const tagManifestPromises = [];
|
|
123
121
|
for (const tag of tags) {
|
|
124
|
-
const
|
|
122
|
+
const tagManifestPromise = getTagManifest(tag, cacheStore);
|
|
125
123
|
tagManifestPromises.push(
|
|
126
|
-
|
|
127
|
-
if (!
|
|
128
|
-
return false;
|
|
124
|
+
tagManifestPromise.then((tagManifest) => {
|
|
125
|
+
if (!tagManifest) {
|
|
126
|
+
return { stale: false, expired: false };
|
|
127
|
+
}
|
|
128
|
+
const stale = tagManifest.staleAt >= timestamp;
|
|
129
|
+
const expired = tagManifest.expireAt >= timestamp && tagManifest.expireAt <= Date.now();
|
|
130
|
+
if (expired && stale) {
|
|
131
|
+
const expiredResult = {
|
|
132
|
+
stale,
|
|
133
|
+
expired
|
|
134
|
+
};
|
|
135
|
+
resolve(expiredResult);
|
|
136
|
+
return expiredResult;
|
|
129
137
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
138
|
+
if (stale) {
|
|
139
|
+
const staleResult = {
|
|
140
|
+
stale,
|
|
141
|
+
expired,
|
|
142
|
+
expireAt: tagManifest.expireAt
|
|
143
|
+
};
|
|
144
|
+
return staleResult;
|
|
134
145
|
}
|
|
135
|
-
return false;
|
|
146
|
+
return { stale: false, expired: false };
|
|
136
147
|
})
|
|
137
148
|
);
|
|
138
149
|
}
|
|
139
|
-
Promise.all(tagManifestPromises).then((
|
|
140
|
-
|
|
150
|
+
Promise.all(tagManifestPromises).then((tagManifestsAreStaleOrExpired) => {
|
|
151
|
+
let result = { stale: false, expired: false };
|
|
152
|
+
for (const tagResult of tagManifestsAreStaleOrExpired) {
|
|
153
|
+
if (tagResult.expired) {
|
|
154
|
+
result = tagResult;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
if (tagResult.stale) {
|
|
158
|
+
result = {
|
|
159
|
+
stale: true,
|
|
160
|
+
expired: false,
|
|
161
|
+
expireAt: (
|
|
162
|
+
// make sure to use expireAt that is lowest of all tags
|
|
163
|
+
result.stale && !result.expired && typeof result.expireAt === "number" ? Math.min(result.expireAt, tagResult.expireAt) : tagResult.expireAt
|
|
164
|
+
)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
resolve(result);
|
|
141
169
|
}).catch(reject);
|
|
142
170
|
});
|
|
143
171
|
}
|
|
@@ -154,13 +182,15 @@ function purgeEdgeCache(tagOrTags) {
|
|
|
154
182
|
(0, import_request_context.getLogger)().withError(error).error(`[NextRuntime] Purging the cache for tags [${tags.join(",")}] failed`);
|
|
155
183
|
});
|
|
156
184
|
}
|
|
157
|
-
async function doRevalidateTagAndPurgeEdgeCache(tags) {
|
|
158
|
-
(0, import_request_context.getLogger)().withFields({ tags }).debug("doRevalidateTagAndPurgeEdgeCache");
|
|
185
|
+
async function doRevalidateTagAndPurgeEdgeCache(tags, durations) {
|
|
186
|
+
(0, import_request_context.getLogger)().withFields({ tags, durations }).debug("doRevalidateTagAndPurgeEdgeCache");
|
|
159
187
|
if (tags.length === 0) {
|
|
160
188
|
return;
|
|
161
189
|
}
|
|
190
|
+
const now = Date.now();
|
|
162
191
|
const tagManifest = {
|
|
163
|
-
|
|
192
|
+
staleAt: now,
|
|
193
|
+
expireAt: now + (durations?.expire ? durations.expire * 1e3 : 0)
|
|
164
194
|
};
|
|
165
195
|
const cacheStore = (0, import_storage.getMemoizedKeyValueStoreBackedByRegionalBlobStore)({ consistency: "strong" });
|
|
166
196
|
await Promise.all(
|
|
@@ -174,9 +204,9 @@ async function doRevalidateTagAndPurgeEdgeCache(tags) {
|
|
|
174
204
|
);
|
|
175
205
|
await purgeEdgeCache(tags);
|
|
176
206
|
}
|
|
177
|
-
function markTagsAsStaleAndPurgeEdgeCache(tagOrTags) {
|
|
207
|
+
function markTagsAsStaleAndPurgeEdgeCache(tagOrTags, durations) {
|
|
178
208
|
const tags = getCacheTagsFromTagOrTags(tagOrTags);
|
|
179
|
-
const revalidateTagPromise = doRevalidateTagAndPurgeEdgeCache(tags);
|
|
209
|
+
const revalidateTagPromise = doRevalidateTagAndPurgeEdgeCache(tags, durations);
|
|
180
210
|
const requestContext = (0, import_request_context.getRequestContext)();
|
|
181
211
|
if (requestContext) {
|
|
182
212
|
requestContext.trackBackgroundWork(revalidateTagPromise);
|
|
@@ -185,8 +215,8 @@ function markTagsAsStaleAndPurgeEdgeCache(tagOrTags) {
|
|
|
185
215
|
}
|
|
186
216
|
// Annotate the CommonJS export names for ESM import in node:
|
|
187
217
|
0 && (module.exports = {
|
|
188
|
-
|
|
189
|
-
|
|
218
|
+
getMostRecentTagExpirationTimestamp,
|
|
219
|
+
isAnyTagStaleOrExpired,
|
|
190
220
|
markTagsAsStaleAndPurgeEdgeCache,
|
|
191
221
|
purgeEdgeCache
|
|
192
222
|
});
|
|
@@ -1381,8 +1381,8 @@ var LRUCache = class _LRUCache {
|
|
|
1381
1381
|
// src/run/handlers/use-cache-handler.ts
|
|
1382
1382
|
import { getLogger } from "./request-context.cjs";
|
|
1383
1383
|
import {
|
|
1384
|
-
|
|
1385
|
-
|
|
1384
|
+
getMostRecentTagExpirationTimestamp,
|
|
1385
|
+
isAnyTagStaleOrExpired,
|
|
1386
1386
|
markTagsAsStaleAndPurgeEdgeCache
|
|
1387
1387
|
} from "./tags-handler.cjs";
|
|
1388
1388
|
import { getTracer } from "./tracer.cjs";
|
|
@@ -1444,7 +1444,8 @@ var NetlifyDefaultUseCacheHandler = {
|
|
|
1444
1444
|
});
|
|
1445
1445
|
return void 0;
|
|
1446
1446
|
}
|
|
1447
|
-
|
|
1447
|
+
const { stale } = await isAnyTagStaleOrExpired(entry.tags, entry.timestamp);
|
|
1448
|
+
if (stale) {
|
|
1448
1449
|
getLogger().withFields({ cacheKey, ttl, status: "STALE BY TAG" }).debug(`[NetlifyDefaultUseCacheHandler] get result`);
|
|
1449
1450
|
span.setAttributes({
|
|
1450
1451
|
cacheStatus: "stale tag, discarded",
|
|
@@ -1520,7 +1521,7 @@ var NetlifyDefaultUseCacheHandler = {
|
|
|
1520
1521
|
span.setAttributes({
|
|
1521
1522
|
tags
|
|
1522
1523
|
});
|
|
1523
|
-
const expiration = await
|
|
1524
|
+
const expiration = await getMostRecentTagExpirationTimestamp(tags);
|
|
1524
1525
|
getLogger().withFields({ tags, expiration }).debug(`[NetlifyDefaultUseCacheHandler] getExpiration`);
|
|
1525
1526
|
span.setAttributes({
|
|
1526
1527
|
expiration
|
|
@@ -25,7 +25,7 @@ __export(blob_types_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(blob_types_exports);
|
|
27
27
|
var isTagManifest = (value) => {
|
|
28
|
-
return typeof value === "object" && value !== null && "
|
|
28
|
+
return typeof value === "object" && value !== null && "staleAt" in value && typeof value.staleAt === "number" && "expiredAt" in value && typeof value.expiredAt === "number" && Object.keys(value).length === 2;
|
|
29
29
|
};
|
|
30
30
|
var isHtmlBlob = (value) => {
|
|
31
31
|
return typeof value === "object" && value !== null && "html" in value && "isFullyStaticPage" in value && typeof value.html === "string" && typeof value.isFullyStaticPage === "boolean" && Object.keys(value).length === 2;
|