@netlify/plugin-nextjs 5.14.3 → 5.14.5

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.
@@ -534,7 +534,7 @@ var copyHandlerDependenciesForNodeMiddleware = async (ctx) => {
534
534
  const parts = [shim];
535
535
  const entry = "server/middleware.js";
536
536
  const nft = `${entry}.nft.json`;
537
- const nftFilesPath = join(process.cwd(), ctx.nextDistDir, nft);
537
+ const nftFilesPath = join(process.cwd(), ctx.distDir, nft);
538
538
  const nftManifest = JSON.parse(await readFile(nftFilesPath, "utf8"));
539
539
  const files = nftManifest.files.map((file) => join("server", file));
540
540
  files.push(entry);
@@ -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.3";
89
+ var version = "5.14.5";
90
90
 
91
91
  // src/run/handlers/tags-handler.cts
92
92
  var import_storage = require("../storage/storage.cjs");
package/dist/run/next.cjs CHANGED
@@ -498,6 +498,7 @@ var import_request_context = require("./handlers/request-context.cjs");
498
498
  var import_tracer = require("./handlers/tracer.cjs");
499
499
  var import_storage = require("./storage/storage.cjs");
500
500
  process.env.NODE_ENV = "production";
501
+ process.env.NEXT_OTEL_FETCH_DISABLED = "1";
501
502
  var { getRequestHandlers } = require("next/dist/server/lib/start-server.js");
502
503
  var ResponseCache = require("next/dist/server/response-cache/index.js").default;
503
504
  var originalGet = ResponseCache.prototype.get;
@@ -25,7 +25,7 @@ __export(regional_blob_store_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(regional_blob_store_exports);
27
27
 
28
- // node_modules/@netlify/blobs/node_modules/@netlify/runtime-utils/dist/main.js
28
+ // node_modules/@netlify/runtime-utils/dist/main.js
29
29
  var getString = (input) => typeof input === "string" ? input : JSON.stringify(input);
30
30
  var base64Decode = globalThis.Buffer ? (input) => Buffer.from(input, "base64").toString() : (input) => atob(input);
31
31
  var base64Encode = globalThis.Buffer ? (input) => Buffer.from(getString(input)).toString("base64") : (input) => btoa(getString(input));
@@ -337,6 +337,22 @@ var getClientOptions = (options, contextOverride) => {
337
337
  return clientOptions;
338
338
  };
339
339
 
340
+ // node_modules/@netlify/otel/dist/main.js
341
+ var GET_TRACER = "__netlify__getTracer";
342
+ var getTracer = (name, version) => {
343
+ return globalThis[GET_TRACER]?.(name, version);
344
+ };
345
+ function withActiveSpan(tracer, name, optionsOrFn, contextOrFn, fn) {
346
+ const func = typeof contextOrFn === "function" ? contextOrFn : typeof optionsOrFn === "function" ? optionsOrFn : fn;
347
+ if (!func) {
348
+ throw new Error("function to execute with active span is missing");
349
+ }
350
+ if (!tracer) {
351
+ return func();
352
+ }
353
+ return tracer.withActiveSpan(name, optionsOrFn, contextOrFn, func);
354
+ }
355
+
340
356
  // node_modules/@netlify/blobs/dist/main.js
341
357
  var DEPLOY_STORE_PREFIX = "deploy:";
342
358
  var LEGACY_STORE_INTERNAL_PREFIX = "netlify-internal/legacy-namespace/";
@@ -368,155 +384,245 @@ var Store = class _Store {
368
384
  throw new BlobsInternalError(res);
369
385
  }
370
386
  }
371
- async get(key, options) {
372
- const { consistency, type } = options ?? {};
373
- const res = await this.client.makeRequest({ consistency, key, method: "get", storeName: this.name });
374
- if (res.status === 404) {
375
- return null;
376
- }
387
+ async deleteAll() {
388
+ const res = await this.client.makeRequest({ method: "delete", storeName: this.name });
377
389
  if (res.status !== 200) {
378
390
  throw new BlobsInternalError(res);
379
391
  }
380
- if (type === void 0 || type === "text") {
381
- return res.text();
382
- }
383
- if (type === "arrayBuffer") {
384
- return res.arrayBuffer();
385
- }
386
- if (type === "blob") {
387
- return res.blob();
388
- }
389
- if (type === "json") {
390
- return res.json();
391
- }
392
- if (type === "stream") {
393
- return res.body;
394
- }
395
- throw new BlobsInternalError(res);
396
- }
397
- async getMetadata(key, { consistency } = {}) {
398
- const res = await this.client.makeRequest({ consistency, key, method: "head", storeName: this.name });
399
- if (res.status === 404) {
400
- return null;
401
- }
402
- if (res.status !== 200 && res.status !== 304) {
392
+ const data = await res.json();
393
+ if (typeof data.blobs_deleted !== "number") {
403
394
  throw new BlobsInternalError(res);
404
395
  }
405
- const etag = res?.headers.get("etag") ?? void 0;
406
- const metadata = getMetadataFromResponse(res);
407
- const result = {
408
- etag,
409
- metadata
396
+ return {
397
+ deletedBlobs: data.blobs_deleted
410
398
  };
411
- return result;
399
+ }
400
+ async get(key, options) {
401
+ return withActiveSpan(getTracer(), "blobs.get", async (span) => {
402
+ const { consistency, type } = options ?? {};
403
+ span?.setAttributes({
404
+ "blobs.store": this.name,
405
+ "blobs.key": key,
406
+ "blobs.type": type,
407
+ "blobs.method": "GET",
408
+ "blobs.consistency": consistency
409
+ });
410
+ const res = await this.client.makeRequest({
411
+ consistency,
412
+ key,
413
+ method: "get",
414
+ storeName: this.name
415
+ });
416
+ span?.setAttributes({
417
+ "blobs.response.body.size": res.headers.get("content-length") ?? void 0,
418
+ "blobs.response.status": res.status
419
+ });
420
+ if (res.status === 404) {
421
+ return null;
422
+ }
423
+ if (res.status !== 200) {
424
+ throw new BlobsInternalError(res);
425
+ }
426
+ if (type === void 0 || type === "text") {
427
+ return res.text();
428
+ }
429
+ if (type === "arrayBuffer") {
430
+ return res.arrayBuffer();
431
+ }
432
+ if (type === "blob") {
433
+ return res.blob();
434
+ }
435
+ if (type === "json") {
436
+ return res.json();
437
+ }
438
+ if (type === "stream") {
439
+ return res.body;
440
+ }
441
+ throw new BlobsInternalError(res);
442
+ });
443
+ }
444
+ async getMetadata(key, { consistency } = {}) {
445
+ return withActiveSpan(getTracer(), "blobs.getMetadata", async (span) => {
446
+ span?.setAttributes({
447
+ "blobs.store": this.name,
448
+ "blobs.key": key,
449
+ "blobs.method": "HEAD",
450
+ "blobs.consistency": consistency
451
+ });
452
+ const res = await this.client.makeRequest({ consistency, key, method: "head", storeName: this.name });
453
+ span?.setAttributes({
454
+ "blobs.response.status": res.status
455
+ });
456
+ if (res.status === 404) {
457
+ return null;
458
+ }
459
+ if (res.status !== 200 && res.status !== 304) {
460
+ throw new BlobsInternalError(res);
461
+ }
462
+ const etag = res?.headers.get("etag") ?? void 0;
463
+ const metadata = getMetadataFromResponse(res);
464
+ const result = {
465
+ etag,
466
+ metadata
467
+ };
468
+ return result;
469
+ });
412
470
  }
413
471
  async getWithMetadata(key, options) {
414
- const { consistency, etag: requestETag, type } = options ?? {};
415
- const headers = requestETag ? { "if-none-match": requestETag } : void 0;
416
- const res = await this.client.makeRequest({
417
- consistency,
418
- headers,
419
- key,
420
- method: "get",
421
- storeName: this.name
472
+ return withActiveSpan(getTracer(), "blobs.getWithMetadata", async (span) => {
473
+ const { consistency, etag: requestETag, type } = options ?? {};
474
+ const headers = requestETag ? { "if-none-match": requestETag } : void 0;
475
+ span?.setAttributes({
476
+ "blobs.store": this.name,
477
+ "blobs.key": key,
478
+ "blobs.method": "GET",
479
+ "blobs.consistency": options?.consistency,
480
+ "blobs.type": type,
481
+ "blobs.request.etag": requestETag
482
+ });
483
+ const res = await this.client.makeRequest({
484
+ consistency,
485
+ headers,
486
+ key,
487
+ method: "get",
488
+ storeName: this.name
489
+ });
490
+ const responseETag = res?.headers.get("etag") ?? void 0;
491
+ span?.setAttributes({
492
+ "blobs.response.body.size": res.headers.get("content-length") ?? void 0,
493
+ "blobs.response.etag": responseETag,
494
+ "blobs.response.status": res.status
495
+ });
496
+ if (res.status === 404) {
497
+ return null;
498
+ }
499
+ if (res.status !== 200 && res.status !== 304) {
500
+ throw new BlobsInternalError(res);
501
+ }
502
+ const metadata = getMetadataFromResponse(res);
503
+ const result = {
504
+ etag: responseETag,
505
+ metadata
506
+ };
507
+ if (res.status === 304 && requestETag) {
508
+ return { data: null, ...result };
509
+ }
510
+ if (type === void 0 || type === "text") {
511
+ return { data: await res.text(), ...result };
512
+ }
513
+ if (type === "arrayBuffer") {
514
+ return { data: await res.arrayBuffer(), ...result };
515
+ }
516
+ if (type === "blob") {
517
+ return { data: await res.blob(), ...result };
518
+ }
519
+ if (type === "json") {
520
+ return { data: await res.json(), ...result };
521
+ }
522
+ if (type === "stream") {
523
+ return { data: res.body, ...result };
524
+ }
525
+ throw new Error(`Invalid 'type' property: ${type}. Expected: arrayBuffer, blob, json, stream, or text.`);
422
526
  });
423
- if (res.status === 404) {
424
- return null;
425
- }
426
- if (res.status !== 200 && res.status !== 304) {
427
- throw new BlobsInternalError(res);
428
- }
429
- const responseETag = res?.headers.get("etag") ?? void 0;
430
- const metadata = getMetadataFromResponse(res);
431
- const result = {
432
- etag: responseETag,
433
- metadata
434
- };
435
- if (res.status === 304 && requestETag) {
436
- return { data: null, ...result };
437
- }
438
- if (type === void 0 || type === "text") {
439
- return { data: await res.text(), ...result };
440
- }
441
- if (type === "arrayBuffer") {
442
- return { data: await res.arrayBuffer(), ...result };
443
- }
444
- if (type === "blob") {
445
- return { data: await res.blob(), ...result };
446
- }
447
- if (type === "json") {
448
- return { data: await res.json(), ...result };
449
- }
450
- if (type === "stream") {
451
- return { data: res.body, ...result };
452
- }
453
- throw new Error(`Invalid 'type' property: ${type}. Expected: arrayBuffer, blob, json, stream, or text.`);
454
527
  }
455
528
  list(options = {}) {
456
- const iterator = this.getListIterator(options);
457
- if (options.paginate) {
458
- return iterator;
459
- }
460
- return collectIterator(iterator).then(
461
- (items) => items.reduce(
462
- (acc, item) => ({
463
- blobs: [...acc.blobs, ...item.blobs],
464
- directories: [...acc.directories, ...item.directories]
465
- }),
466
- { blobs: [], directories: [] }
467
- )
468
- );
529
+ return withActiveSpan(getTracer(), "blobs.list", (span) => {
530
+ span?.setAttributes({
531
+ "blobs.store": this.name,
532
+ "blobs.method": "GET",
533
+ "blobs.list.paginate": options.paginate ?? false
534
+ });
535
+ const iterator = this.getListIterator(options);
536
+ if (options.paginate) {
537
+ return iterator;
538
+ }
539
+ return collectIterator(iterator).then(
540
+ (items) => items.reduce(
541
+ (acc, item) => ({
542
+ blobs: [...acc.blobs, ...item.blobs],
543
+ directories: [...acc.directories, ...item.directories]
544
+ }),
545
+ { blobs: [], directories: [] }
546
+ )
547
+ );
548
+ });
469
549
  }
470
550
  async set(key, data, options = {}) {
471
- _Store.validateKey(key);
472
- const conditions = _Store.getConditions(options);
473
- const res = await this.client.makeRequest({
474
- conditions,
475
- body: data,
476
- key,
477
- metadata: options.metadata,
478
- method: "put",
479
- storeName: this.name
551
+ return withActiveSpan(getTracer(), "blobs.set", async (span) => {
552
+ span?.setAttributes({
553
+ "blobs.store": this.name,
554
+ "blobs.key": key,
555
+ "blobs.method": "PUT",
556
+ "blobs.data.size": typeof data == "string" ? data.length : data instanceof Blob ? data.size : data.byteLength,
557
+ "blobs.data.type": typeof data == "string" ? "string" : data instanceof Blob ? "blob" : "arrayBuffer",
558
+ "blobs.atomic": Boolean(options.onlyIfMatch ?? options.onlyIfNew)
559
+ });
560
+ _Store.validateKey(key);
561
+ const conditions = _Store.getConditions(options);
562
+ const res = await this.client.makeRequest({
563
+ conditions,
564
+ body: data,
565
+ key,
566
+ metadata: options.metadata,
567
+ method: "put",
568
+ storeName: this.name
569
+ });
570
+ const etag = res.headers.get("etag") ?? "";
571
+ span?.setAttributes({
572
+ "blobs.response.etag": etag,
573
+ "blobs.response.status": res.status
574
+ });
575
+ if (conditions) {
576
+ return res.status === STATUS_PRE_CONDITION_FAILED ? { modified: false } : { etag, modified: true };
577
+ }
578
+ if (res.status === STATUS_OK) {
579
+ return {
580
+ etag,
581
+ modified: true
582
+ };
583
+ }
584
+ throw new BlobsInternalError(res);
480
585
  });
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
- };
490
- }
491
- throw new BlobsInternalError(res);
492
586
  }
493
587
  async setJSON(key, data, options = {}) {
494
- _Store.validateKey(key);
495
- const conditions = _Store.getConditions(options);
496
- const payload = JSON.stringify(data);
497
- const headers = {
498
- "content-type": "application/json"
499
- };
500
- const res = await this.client.makeRequest({
501
- ...conditions,
502
- body: payload,
503
- headers,
504
- key,
505
- metadata: options.metadata,
506
- method: "put",
507
- storeName: this.name
508
- });
509
- const etag = res.headers.get("etag") ?? "";
510
- if (conditions) {
511
- return res.status === STATUS_PRE_CONDITION_FAILED ? { modified: false } : { etag, modified: true };
512
- }
513
- if (res.status === STATUS_OK) {
514
- return {
515
- etag,
516
- modified: true
588
+ return withActiveSpan(getTracer(), "blobs.setJSON", async (span) => {
589
+ span?.setAttributes({
590
+ "blobs.store": this.name,
591
+ "blobs.key": key,
592
+ "blobs.method": "PUT",
593
+ "blobs.data.type": "json"
594
+ });
595
+ _Store.validateKey(key);
596
+ const conditions = _Store.getConditions(options);
597
+ const payload = JSON.stringify(data);
598
+ const headers = {
599
+ "content-type": "application/json"
517
600
  };
518
- }
519
- throw new BlobsInternalError(res);
601
+ const res = await this.client.makeRequest({
602
+ ...conditions,
603
+ body: payload,
604
+ headers,
605
+ key,
606
+ metadata: options.metadata,
607
+ method: "put",
608
+ storeName: this.name
609
+ });
610
+ const etag = res.headers.get("etag") ?? "";
611
+ span?.setAttributes({
612
+ "blobs.response.etag": etag,
613
+ "blobs.response.status": res.status
614
+ });
615
+ if (conditions) {
616
+ return res.status === STATUS_PRE_CONDITION_FAILED ? { modified: false } : { etag, modified: true };
617
+ }
618
+ if (res.status === STATUS_OK) {
619
+ return {
620
+ etag,
621
+ modified: true
622
+ };
623
+ }
624
+ throw new BlobsInternalError(res);
625
+ });
520
626
  }
521
627
  static formatListResultBlob(result) {
522
628
  if (!result.key) {
@@ -595,42 +701,54 @@ var Store = class _Store {
595
701
  let done = false;
596
702
  return {
597
703
  async next() {
598
- if (done) {
599
- return { done: true, value: void 0 };
600
- }
601
- const nextParameters = { ...parameters };
602
- if (currentCursor !== null) {
603
- nextParameters.cursor = currentCursor;
604
- }
605
- const res = await client.makeRequest({
606
- method: "get",
607
- parameters: nextParameters,
608
- storeName
609
- });
610
- let blobs = [];
611
- let directories = [];
612
- if (![200, 204, 404].includes(res.status)) {
613
- throw new BlobsInternalError(res);
614
- }
615
- if (res.status === 404) {
616
- done = true;
617
- } else {
618
- const page = await res.json();
619
- if (page.next_cursor) {
620
- currentCursor = page.next_cursor;
621
- } else {
622
- done = true;
704
+ return withActiveSpan(getTracer(), "blobs.list.next", async (span) => {
705
+ span?.setAttributes({
706
+ "blobs.store": storeName,
707
+ "blobs.method": "GET",
708
+ "blobs.list.paginate": options?.paginate ?? false,
709
+ "blobs.list.done": done,
710
+ "blobs.list.cursor": currentCursor ?? void 0
711
+ });
712
+ if (done) {
713
+ return { done: true, value: void 0 };
714
+ }
715
+ const nextParameters = { ...parameters };
716
+ if (currentCursor !== null) {
717
+ nextParameters.cursor = currentCursor;
718
+ }
719
+ const res = await client.makeRequest({
720
+ method: "get",
721
+ parameters: nextParameters,
722
+ storeName
723
+ });
724
+ span?.setAttributes({
725
+ "blobs.response.status": res.status
726
+ });
727
+ let blobs = [];
728
+ let directories = [];
729
+ if (![200, 204, 404].includes(res.status)) {
730
+ throw new BlobsInternalError(res);
623
731
  }
624
- blobs = (page.blobs ?? []).map(_Store.formatListResultBlob).filter(Boolean);
625
- directories = page.directories ?? [];
626
- }
627
- return {
628
- done: false,
629
- value: {
630
- blobs,
631
- directories
732
+ if (res.status === 404) {
733
+ done = true;
734
+ } else {
735
+ const page = await res.json();
736
+ if (page.next_cursor) {
737
+ currentCursor = page.next_cursor;
738
+ } else {
739
+ done = true;
740
+ }
741
+ blobs = (page.blobs ?? []).map(_Store.formatListResultBlob).filter(Boolean);
742
+ directories = page.directories ?? [];
632
743
  }
633
- };
744
+ return {
745
+ done: false,
746
+ value: {
747
+ blobs,
748
+ directories
749
+ }
750
+ };
751
+ });
634
752
  }
635
753
  };
636
754
  }
@@ -1400,6 +1400,9 @@ var import_blob_types = require("../../shared/blob-types.cjs");
1400
1400
  var import_request_context = require("../handlers/request-context.cjs");
1401
1401
  var import_tracer = require("../handlers/tracer.cjs");
1402
1402
  var NullValue = Symbol.for("null-value");
1403
+ var isDataWithEtag = (value) => {
1404
+ return typeof value === "object" && value !== null && "data" in value && "etag" in value;
1405
+ };
1403
1406
  var IN_MEMORY_CACHE_MAX_SIZE = Symbol.for("nf-in-memory-cache-max-size");
1404
1407
  var IN_MEMORY_LRU_CACHE = Symbol.for("nf-in-memory-lru-cache");
1405
1408
  var extendedGlobalThis = globalThis;
@@ -1413,24 +1416,29 @@ var isPositiveNumber = (value) => {
1413
1416
  return typeof value === "number" && value > 0;
1414
1417
  };
1415
1418
  var BASE_BLOB_SIZE = 25;
1419
+ var BASE_BLOB_WITH_ETAG_SIZE = BASE_BLOB_SIZE + 34;
1416
1420
  var estimateBlobKnownTypeSize = (valueToStore) => {
1417
- if (valueToStore === null || (0, import_types.isPromise)(valueToStore) || (0, import_blob_types.isTagManifest)(valueToStore)) {
1421
+ if (valueToStore === null || (0, import_types.isPromise)(valueToStore)) {
1418
1422
  return BASE_BLOB_SIZE;
1419
1423
  }
1420
- if ((0, import_blob_types.isHtmlBlob)(valueToStore)) {
1421
- return BASE_BLOB_SIZE + valueToStore.html.length;
1424
+ const { data, baseSize } = isDataWithEtag(valueToStore) ? { data: valueToStore.data, baseSize: BASE_BLOB_WITH_ETAG_SIZE } : { data: valueToStore, baseSize: BASE_BLOB_SIZE };
1425
+ if ((0, import_blob_types.isTagManifest)(data)) {
1426
+ return baseSize;
1427
+ }
1428
+ if ((0, import_blob_types.isHtmlBlob)(data)) {
1429
+ return baseSize + data.html.length;
1422
1430
  }
1423
- if (valueToStore.value?.kind === "FETCH") {
1424
- return BASE_BLOB_SIZE + valueToStore.value.data.body.length;
1431
+ if (data.value?.kind === "FETCH") {
1432
+ return baseSize + data.value.data.body.length;
1425
1433
  }
1426
- if (valueToStore.value?.kind === "APP_PAGE") {
1427
- return BASE_BLOB_SIZE + valueToStore.value.html.length + (valueToStore.value.rscData?.length ?? 0);
1434
+ if (data.value?.kind === "APP_PAGE") {
1435
+ return baseSize + data.value.html.length + (data.value.rscData?.length ?? 0);
1428
1436
  }
1429
- if (valueToStore.value?.kind === "PAGE" || valueToStore.value?.kind === "PAGES") {
1430
- return BASE_BLOB_SIZE + valueToStore.value.html.length + JSON.stringify(valueToStore.value.pageData).length;
1437
+ if (data.value?.kind === "PAGE" || data.value?.kind === "PAGES") {
1438
+ return baseSize + data.value.html.length + JSON.stringify(data.value.pageData).length;
1431
1439
  }
1432
- if (valueToStore.value?.kind === "ROUTE" || valueToStore.value?.kind === "APP_ROUTE") {
1433
- return BASE_BLOB_SIZE + valueToStore.value.body.length;
1440
+ if (data.value?.kind === "ROUTE" || data.value?.kind === "APP_ROUTE") {
1441
+ return baseSize + data.value.body.length;
1434
1442
  }
1435
1443
  };
1436
1444
  var estimateBlobSize = (valueToStore) => {
@@ -1456,13 +1464,22 @@ var estimateBlobSize = (valueToStore) => {
1456
1464
  function getInMemoryLRUCache() {
1457
1465
  if (typeof extendedGlobalThis[IN_MEMORY_LRU_CACHE] === "undefined") {
1458
1466
  const maxSize = typeof extendedGlobalThis[IN_MEMORY_CACHE_MAX_SIZE] === "number" ? extendedGlobalThis[IN_MEMORY_CACHE_MAX_SIZE] : DEFAULT_FALLBACK_MAX_SIZE;
1459
- extendedGlobalThis[IN_MEMORY_LRU_CACHE] = maxSize === 0 ? null : new LRUCache({
1460
- max: 1e3,
1461
- maxSize,
1462
- sizeCalculation: (valueToStore) => {
1463
- return estimateBlobSize(valueToStore === NullValue ? null : valueToStore);
1464
- }
1465
- });
1467
+ if (maxSize === 0) {
1468
+ extendedGlobalThis[IN_MEMORY_LRU_CACHE] = null;
1469
+ } else {
1470
+ const global = /* @__PURE__ */ new Map();
1471
+ const perRequest = new LRUCache({
1472
+ max: 1e3,
1473
+ maxSize,
1474
+ sizeCalculation: (valueToStore) => {
1475
+ return estimateBlobSize(valueToStore === NullValue ? null : valueToStore);
1476
+ }
1477
+ });
1478
+ extendedGlobalThis[IN_MEMORY_LRU_CACHE] = {
1479
+ perRequest,
1480
+ global
1481
+ };
1482
+ }
1466
1483
  }
1467
1484
  return extendedGlobalThis[IN_MEMORY_LRU_CACHE];
1468
1485
  }
@@ -1473,8 +1490,27 @@ var getRequestScopedInMemoryCache = () => {
1473
1490
  get(key) {
1474
1491
  if (!requestContext) return;
1475
1492
  try {
1476
- const value = inMemoryLRUCache?.get(`${requestContext.requestID}:${key}`);
1477
- return value === NullValue ? null : value;
1493
+ const currentRequestValue = inMemoryLRUCache?.perRequest.get(
1494
+ `${requestContext.requestID}:${key}`
1495
+ );
1496
+ if (currentRequestValue) {
1497
+ return {
1498
+ conditional: false,
1499
+ currentRequestValue: currentRequestValue === NullValue ? null : isDataWithEtag(currentRequestValue) ? currentRequestValue.data : currentRequestValue
1500
+ };
1501
+ }
1502
+ const globalEntry = inMemoryLRUCache?.global.get(key);
1503
+ if (globalEntry) {
1504
+ const derefencedGlobalEntry = globalEntry.deref();
1505
+ if (derefencedGlobalEntry) {
1506
+ return {
1507
+ conditional: true,
1508
+ globalValue: derefencedGlobalEntry.data,
1509
+ etag: derefencedGlobalEntry.etag
1510
+ };
1511
+ }
1512
+ inMemoryLRUCache?.global.delete(key);
1513
+ }
1478
1514
  } catch (error) {
1479
1515
  (0, import_tracer.recordWarning)(new Error("Failed to get value from memory cache", { cause: error }));
1480
1516
  }
@@ -1482,7 +1518,10 @@ var getRequestScopedInMemoryCache = () => {
1482
1518
  set(key, value) {
1483
1519
  if (!requestContext) return;
1484
1520
  try {
1485
- inMemoryLRUCache?.set(`${requestContext?.requestID}:${key}`, value ?? NullValue);
1521
+ if (isDataWithEtag(value)) {
1522
+ inMemoryLRUCache?.global.set(key, new WeakRef(value));
1523
+ }
1524
+ inMemoryLRUCache?.perRequest.set(`${requestContext.requestID}:${key}`, value ?? NullValue);
1486
1525
  } catch (error) {
1487
1526
  (0, import_tracer.recordWarning)(new Error("Failed to store value in memory cache", { cause: error }));
1488
1527
  }
@@ -51,15 +51,32 @@ var getMemoizedKeyValueStoreBackedByRegionalBlobStore = (...args) => {
51
51
  async get(key, otelSpanTitle) {
52
52
  const inMemoryCache = (0, import_request_scoped_in_memory_cache.getRequestScopedInMemoryCache)();
53
53
  const memoizedValue = inMemoryCache.get(key);
54
- if (typeof memoizedValue !== "undefined") {
55
- return memoizedValue;
54
+ if (memoizedValue?.conditional === false && typeof memoizedValue?.currentRequestValue !== "undefined") {
55
+ return memoizedValue.currentRequestValue;
56
56
  }
57
57
  const blobKey = await encodeBlobKey(key);
58
58
  const getPromise = (0, import_tracer.withActiveSpan)(tracer, otelSpanTitle, async (span) => {
59
- span?.setAttributes({ key, blobKey });
60
- const blob = await store.get(blobKey, { type: "json" });
61
- inMemoryCache.set(key, blob);
62
- span?.addEvent(blob ? "Hit" : "Miss");
59
+ const { etag: previousEtag, globalValue: previousBlob } = memoizedValue?.conditional ? memoizedValue : {};
60
+ span?.setAttributes({ key, blobKey, previousEtag });
61
+ const result = await store.getWithMetadata(blobKey, {
62
+ type: "json",
63
+ etag: previousEtag
64
+ });
65
+ const shouldReuseMemoizedBlob = result?.etag && previousEtag === result?.etag;
66
+ const blob = shouldReuseMemoizedBlob ? previousBlob : result?.data;
67
+ if (result?.etag && blob) {
68
+ inMemoryCache.set(key, {
69
+ data: blob,
70
+ etag: result?.etag
71
+ });
72
+ } else {
73
+ inMemoryCache.set(key, blob);
74
+ }
75
+ span?.setAttributes({
76
+ etag: result?.etag,
77
+ reusingPreviouslyFetchedBlob: shouldReuseMemoizedBlob,
78
+ status: blob ? shouldReuseMemoizedBlob ? "Hit, no change" : "Hit" : "Miss"
79
+ });
63
80
  return blob;
64
81
  });
65
82
  inMemoryCache.set(key, getPromise);
@@ -71,7 +88,14 @@ var getMemoizedKeyValueStoreBackedByRegionalBlobStore = (...args) => {
71
88
  const blobKey = await encodeBlobKey(key);
72
89
  return (0, import_tracer.withActiveSpan)(tracer, otelSpanTitle, async (span) => {
73
90
  span?.setAttributes({ key, blobKey });
74
- return await store.setJSON(blobKey, value);
91
+ const writeResult = await store.setJSON(blobKey, value);
92
+ if (writeResult?.etag) {
93
+ inMemoryCache.set(key, {
94
+ data: value,
95
+ etag: writeResult.etag
96
+ });
97
+ }
98
+ return writeResult;
75
99
  });
76
100
  }
77
101
  };
@@ -1,16 +1,21 @@
1
1
  // NOTE: This is a fragment of a JavaScript program that will be inlined with
2
2
  // a Webpack bundle. You should not import this file from anywhere in the
3
3
  // application.
4
- import { AsyncLocalStorage } from 'node:async_hooks'
5
-
6
4
  import { createRequire } from 'node:module' // used in dynamically generated part
7
- import process from 'node:process'
8
5
 
9
6
  import { registerCJSModules } from '../edge-runtime/lib/cjs.ts' // used in dynamically generated part
10
7
 
11
- globalThis.process = process
8
+ if (typeof process === 'undefined') {
9
+ globalThis.process = (await import('node:process')).default
10
+ }
11
+
12
+ if (typeof AsyncLocalStorage === 'undefined') {
13
+ globalThis.AsyncLocalStorage = (await import('node:async_hooks')).AsyncLocalStorage
14
+ }
12
15
 
13
- globalThis.AsyncLocalStorage = AsyncLocalStorage
16
+ if (typeof Buffer === 'undefined') {
17
+ globalThis.Buffer = (await import('node:buffer')).Buffer
18
+ }
14
19
 
15
20
  // needed for path.relative and path.resolve to work
16
21
  Deno.cwd = () => ''
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "5.14.3",
3
+ "version": "5.14.5",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",