@netlify/plugin-nextjs 5.14.0 → 5.14.1
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.
|
@@ -86,7 +86,7 @@ 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.14.
|
|
89
|
+
var version = "5.14.1";
|
|
90
90
|
|
|
91
91
|
// src/run/handlers/tags-handler.cts
|
|
92
92
|
var import_storage = require("../storage/storage.cjs");
|
|
@@ -206,9 +206,21 @@ async function doRevalidateTagAndPurgeEdgeCache(tags, durations) {
|
|
|
206
206
|
}
|
|
207
207
|
function markTagsAsStaleAndPurgeEdgeCache(tagOrTags, durations) {
|
|
208
208
|
const tags = getCacheTagsFromTagOrTags(tagOrTags);
|
|
209
|
-
const
|
|
209
|
+
const revalidationKey = JSON.stringify({ tags, durations });
|
|
210
210
|
const requestContext = (0, import_request_context.getRequestContext)();
|
|
211
211
|
if (requestContext) {
|
|
212
|
+
const ongoingRevalidation = requestContext.ongoingRevalidations?.get(revalidationKey);
|
|
213
|
+
if (ongoingRevalidation) {
|
|
214
|
+
return ongoingRevalidation;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const revalidateTagPromise = doRevalidateTagAndPurgeEdgeCache(tags, durations);
|
|
218
|
+
if (requestContext) {
|
|
219
|
+
requestContext.ongoingRevalidations ??= /* @__PURE__ */ new Map();
|
|
220
|
+
requestContext.ongoingRevalidations.set(revalidationKey, revalidateTagPromise);
|
|
221
|
+
process.nextTick(() => {
|
|
222
|
+
requestContext.ongoingRevalidations?.delete(revalidationKey);
|
|
223
|
+
});
|
|
212
224
|
requestContext.trackBackgroundWork(revalidateTagPromise);
|
|
213
225
|
}
|
|
214
226
|
return revalidateTagPromise;
|
|
@@ -1444,24 +1444,29 @@ var NetlifyDefaultUseCacheHandler = {
|
|
|
1444
1444
|
});
|
|
1445
1445
|
return void 0;
|
|
1446
1446
|
}
|
|
1447
|
-
const { stale } = await isAnyTagStaleOrExpired(entry.tags, entry.timestamp);
|
|
1448
|
-
if (
|
|
1449
|
-
getLogger().withFields({ cacheKey, ttl, status: "
|
|
1447
|
+
const { stale, expired } = await isAnyTagStaleOrExpired(entry.tags, entry.timestamp);
|
|
1448
|
+
if (expired) {
|
|
1449
|
+
getLogger().withFields({ cacheKey, ttl, status: "EXPIRED BY TAG" }).debug(`[NetlifyDefaultUseCacheHandler] get result`);
|
|
1450
1450
|
span.setAttributes({
|
|
1451
|
-
cacheStatus: "
|
|
1451
|
+
cacheStatus: "expired tag, discarded",
|
|
1452
1452
|
ttl
|
|
1453
1453
|
});
|
|
1454
1454
|
return void 0;
|
|
1455
1455
|
}
|
|
1456
|
-
|
|
1456
|
+
let { revalidate, value } = entry;
|
|
1457
|
+
if (stale) {
|
|
1458
|
+
revalidate = -1;
|
|
1459
|
+
}
|
|
1460
|
+
const [returnStream, newSaved] = value.tee();
|
|
1457
1461
|
entry.value = newSaved;
|
|
1458
|
-
getLogger().withFields({ cacheKey, ttl, status: "HIT" }).debug(`[NetlifyDefaultUseCacheHandler] get result`);
|
|
1462
|
+
getLogger().withFields({ cacheKey, ttl, status: stale ? "STALE" : "HIT" }).debug(`[NetlifyDefaultUseCacheHandler] get result`);
|
|
1459
1463
|
span.setAttributes({
|
|
1460
|
-
cacheStatus: "hit",
|
|
1464
|
+
cacheStatus: stale ? "stale" : "hit",
|
|
1461
1465
|
ttl
|
|
1462
1466
|
});
|
|
1463
1467
|
return {
|
|
1464
1468
|
...entry,
|
|
1469
|
+
revalidate,
|
|
1465
1470
|
value: returnStream
|
|
1466
1471
|
};
|
|
1467
1472
|
}
|
|
@@ -1514,10 +1519,11 @@ var NetlifyDefaultUseCacheHandler = {
|
|
|
1514
1519
|
},
|
|
1515
1520
|
async refreshTags() {
|
|
1516
1521
|
},
|
|
1517
|
-
getExpiration: function(...
|
|
1522
|
+
getExpiration: function(...notNormalizedTags) {
|
|
1518
1523
|
return getTracer().withActiveSpan(
|
|
1519
1524
|
"DefaultUseCacheHandler.getExpiration",
|
|
1520
1525
|
async (span) => {
|
|
1526
|
+
const tags = notNormalizedTags.flat();
|
|
1521
1527
|
span.setAttributes({
|
|
1522
1528
|
tags
|
|
1523
1529
|
});
|
|
@@ -1530,6 +1536,7 @@ var NetlifyDefaultUseCacheHandler = {
|
|
|
1530
1536
|
}
|
|
1531
1537
|
);
|
|
1532
1538
|
},
|
|
1539
|
+
// this is for CacheHandlerV2
|
|
1533
1540
|
expireTags(...tags) {
|
|
1534
1541
|
return getTracer().withActiveSpan(
|
|
1535
1542
|
"DefaultUseCacheHandler.expireTags",
|
|
@@ -1541,6 +1548,20 @@ var NetlifyDefaultUseCacheHandler = {
|
|
|
1541
1548
|
await markTagsAsStaleAndPurgeEdgeCache(tags);
|
|
1542
1549
|
}
|
|
1543
1550
|
);
|
|
1551
|
+
},
|
|
1552
|
+
// this is for CacheHandlerV3 / Next 16
|
|
1553
|
+
updateTags(tags, durations) {
|
|
1554
|
+
return getTracer().withActiveSpan(
|
|
1555
|
+
"DefaultUseCacheHandler.updateTags",
|
|
1556
|
+
async (span) => {
|
|
1557
|
+
getLogger().withFields({ tags, durations }).debug(`[NetlifyDefaultUseCacheHandler] updateTags`);
|
|
1558
|
+
span.setAttributes({
|
|
1559
|
+
tags,
|
|
1560
|
+
durations: JSON.stringify(durations)
|
|
1561
|
+
});
|
|
1562
|
+
await markTagsAsStaleAndPurgeEdgeCache(tags, durations);
|
|
1563
|
+
}
|
|
1564
|
+
);
|
|
1544
1565
|
}
|
|
1545
1566
|
};
|
|
1546
1567
|
function configureUseCacheHandlers() {
|
|
@@ -25,40 +25,12 @@ __export(regional_blob_store_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(regional_blob_store_exports);
|
|
27
27
|
|
|
28
|
-
// node_modules/@netlify/blobs/dist/
|
|
29
|
-
var
|
|
30
|
-
var
|
|
31
|
-
var
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (res.headers.has(NF_REQUEST_ID)) {
|
|
35
|
-
details += `, ID: ${res.headers.get(NF_REQUEST_ID)}`;
|
|
36
|
-
}
|
|
37
|
-
super(`Netlify Blobs has generated an internal error (${details})`);
|
|
38
|
-
this.name = "BlobsInternalError";
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
var collectIterator = async (iterator) => {
|
|
42
|
-
const result = [];
|
|
43
|
-
for await (const item of iterator) {
|
|
44
|
-
result.push(item);
|
|
45
|
-
}
|
|
46
|
-
return result;
|
|
47
|
-
};
|
|
48
|
-
var base64Decode = (input) => {
|
|
49
|
-
const { Buffer: Buffer2 } = globalThis;
|
|
50
|
-
if (Buffer2) {
|
|
51
|
-
return Buffer2.from(input, "base64").toString();
|
|
52
|
-
}
|
|
53
|
-
return atob(input);
|
|
54
|
-
};
|
|
55
|
-
var base64Encode = (input) => {
|
|
56
|
-
const { Buffer: Buffer2 } = globalThis;
|
|
57
|
-
if (Buffer2) {
|
|
58
|
-
return Buffer2.from(input).toString("base64");
|
|
59
|
-
}
|
|
60
|
-
return btoa(input);
|
|
61
|
-
};
|
|
28
|
+
// node_modules/@netlify/blobs/node_modules/@netlify/runtime-utils/dist/main.js
|
|
29
|
+
var getString = (input) => typeof input === "string" ? input : JSON.stringify(input);
|
|
30
|
+
var base64Decode = globalThis.Buffer ? (input) => Buffer.from(input, "base64").toString() : (input) => atob(input);
|
|
31
|
+
var base64Encode = globalThis.Buffer ? (input) => Buffer.from(getString(input)).toString("base64") : (input) => btoa(getString(input));
|
|
32
|
+
|
|
33
|
+
// node_modules/@netlify/blobs/dist/chunk-HN33TXZT.js
|
|
62
34
|
var getEnvironment = () => {
|
|
63
35
|
const { Deno, Netlify, process: process2 } = globalThis;
|
|
64
36
|
return Netlify?.env ?? Deno?.env ?? {
|
|
@@ -111,7 +83,7 @@ var encodeMetadata = (metadata) => {
|
|
|
111
83
|
return payload;
|
|
112
84
|
};
|
|
113
85
|
var decodeMetadata = (header) => {
|
|
114
|
-
if (!header
|
|
86
|
+
if (!header?.startsWith(BASE64_PREFIX)) {
|
|
115
87
|
return {};
|
|
116
88
|
}
|
|
117
89
|
const encodedData = header.slice(BASE64_PREFIX.length);
|
|
@@ -132,6 +104,25 @@ var getMetadataFromResponse = (response) => {
|
|
|
132
104
|
);
|
|
133
105
|
}
|
|
134
106
|
};
|
|
107
|
+
var NF_ERROR = "x-nf-error";
|
|
108
|
+
var NF_REQUEST_ID = "x-nf-request-id";
|
|
109
|
+
var BlobsInternalError = class extends Error {
|
|
110
|
+
constructor(res) {
|
|
111
|
+
let details = res.headers.get(NF_ERROR) || `${res.status} status code`;
|
|
112
|
+
if (res.headers.has(NF_REQUEST_ID)) {
|
|
113
|
+
details += `, ID: ${res.headers.get(NF_REQUEST_ID)}`;
|
|
114
|
+
}
|
|
115
|
+
super(`Netlify Blobs has generated an internal error (${details})`);
|
|
116
|
+
this.name = "BlobsInternalError";
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
var collectIterator = async (iterator) => {
|
|
120
|
+
const result = [];
|
|
121
|
+
for await (const item of iterator) {
|
|
122
|
+
result.push(item);
|
|
123
|
+
}
|
|
124
|
+
return result;
|
|
125
|
+
};
|
|
135
126
|
var BlobsConsistencyError = class extends Error {
|
|
136
127
|
constructor() {
|
|
137
128
|
super(
|
|
@@ -283,6 +274,7 @@ var Client = class {
|
|
|
283
274
|
}
|
|
284
275
|
async makeRequest({
|
|
285
276
|
body,
|
|
277
|
+
conditions = {},
|
|
286
278
|
consistency,
|
|
287
279
|
headers: extraHeaders,
|
|
288
280
|
key,
|
|
@@ -306,6 +298,11 @@ var Client = class {
|
|
|
306
298
|
if (method === "put") {
|
|
307
299
|
headers["cache-control"] = "max-age=0, stale-while-revalidate=60";
|
|
308
300
|
}
|
|
301
|
+
if ("onlyIfMatch" in conditions && conditions.onlyIfMatch) {
|
|
302
|
+
headers["if-match"] = conditions.onlyIfMatch;
|
|
303
|
+
} else if ("onlyIfNew" in conditions && conditions.onlyIfNew) {
|
|
304
|
+
headers["if-none-match"] = "*";
|
|
305
|
+
}
|
|
309
306
|
const options = {
|
|
310
307
|
body,
|
|
311
308
|
headers,
|
|
@@ -344,6 +341,8 @@ var getClientOptions = (options, contextOverride) => {
|
|
|
344
341
|
var DEPLOY_STORE_PREFIX = "deploy:";
|
|
345
342
|
var LEGACY_STORE_INTERNAL_PREFIX = "netlify-internal/legacy-namespace/";
|
|
346
343
|
var SITE_STORE_PREFIX = "site:";
|
|
344
|
+
var STATUS_OK = 200;
|
|
345
|
+
var STATUS_PRE_CONDITION_FAILED = 412;
|
|
347
346
|
var Store = class _Store {
|
|
348
347
|
constructor(options) {
|
|
349
348
|
this.client = options.client;
|
|
@@ -468,36 +467,56 @@ var Store = class _Store {
|
|
|
468
467
|
)
|
|
469
468
|
);
|
|
470
469
|
}
|
|
471
|
-
async set(key, data,
|
|
470
|
+
async set(key, data, options = {}) {
|
|
472
471
|
_Store.validateKey(key);
|
|
472
|
+
const conditions = _Store.getConditions(options);
|
|
473
473
|
const res = await this.client.makeRequest({
|
|
474
|
+
conditions,
|
|
474
475
|
body: data,
|
|
475
476
|
key,
|
|
476
|
-
metadata,
|
|
477
|
+
metadata: options.metadata,
|
|
477
478
|
method: "put",
|
|
478
479
|
storeName: this.name
|
|
479
480
|
});
|
|
480
|
-
|
|
481
|
-
|
|
481
|
+
const etag = res.headers.get("etag") ?? "";
|
|
482
|
+
if (conditions) {
|
|
483
|
+
return res.status === STATUS_PRE_CONDITION_FAILED ? { modified: false } : { etag, modified: true };
|
|
484
|
+
}
|
|
485
|
+
if (res.status === STATUS_OK) {
|
|
486
|
+
return {
|
|
487
|
+
etag,
|
|
488
|
+
modified: true
|
|
489
|
+
};
|
|
482
490
|
}
|
|
491
|
+
throw new BlobsInternalError(res);
|
|
483
492
|
}
|
|
484
|
-
async setJSON(key, data,
|
|
493
|
+
async setJSON(key, data, options = {}) {
|
|
485
494
|
_Store.validateKey(key);
|
|
495
|
+
const conditions = _Store.getConditions(options);
|
|
486
496
|
const payload = JSON.stringify(data);
|
|
487
497
|
const headers = {
|
|
488
498
|
"content-type": "application/json"
|
|
489
499
|
};
|
|
490
500
|
const res = await this.client.makeRequest({
|
|
501
|
+
...conditions,
|
|
491
502
|
body: payload,
|
|
492
503
|
headers,
|
|
493
504
|
key,
|
|
494
|
-
metadata,
|
|
505
|
+
metadata: options.metadata,
|
|
495
506
|
method: "put",
|
|
496
507
|
storeName: this.name
|
|
497
508
|
});
|
|
498
|
-
|
|
499
|
-
|
|
509
|
+
const etag = res.headers.get("etag") ?? "";
|
|
510
|
+
if (conditions) {
|
|
511
|
+
return res.status === STATUS_PRE_CONDITION_FAILED ? { modified: false } : { etag, modified: true };
|
|
500
512
|
}
|
|
513
|
+
if (res.status === STATUS_OK) {
|
|
514
|
+
return {
|
|
515
|
+
etag,
|
|
516
|
+
modified: true
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
throw new BlobsInternalError(res);
|
|
501
520
|
}
|
|
502
521
|
static formatListResultBlob(result) {
|
|
503
522
|
if (!result.key) {
|
|
@@ -508,6 +527,31 @@ var Store = class _Store {
|
|
|
508
527
|
key: result.key
|
|
509
528
|
};
|
|
510
529
|
}
|
|
530
|
+
static getConditions(options) {
|
|
531
|
+
if ("onlyIfMatch" in options && "onlyIfNew" in options) {
|
|
532
|
+
throw new Error(
|
|
533
|
+
`The 'onlyIfMatch' and 'onlyIfNew' options are mutually exclusive. Using 'onlyIfMatch' will make the write succeed only if there is an entry for the key with the given content, while 'onlyIfNew' will make the write succeed only if there is no entry for the key.`
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
if ("onlyIfMatch" in options && options.onlyIfMatch) {
|
|
537
|
+
if (typeof options.onlyIfMatch !== "string") {
|
|
538
|
+
throw new Error(`The 'onlyIfMatch' property expects a string representing an ETag.`);
|
|
539
|
+
}
|
|
540
|
+
return {
|
|
541
|
+
onlyIfMatch: options.onlyIfMatch
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
if ("onlyIfNew" in options && options.onlyIfNew) {
|
|
545
|
+
if (typeof options.onlyIfNew !== "boolean") {
|
|
546
|
+
throw new Error(
|
|
547
|
+
`The 'onlyIfNew' property expects a boolean indicating whether the write should fail if an entry for the key already exists.`
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
onlyIfNew: true
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
}
|
|
511
555
|
static validateKey(key) {
|
|
512
556
|
if (key === "") {
|
|
513
557
|
throw new Error("Blob key must not be empty.");
|