next-shared-cache icon indicating copy to clipboard operation
next-shared-cache copied to clipboard

Add Next.js 15 support

Open better-salmon opened this issue 1 year ago • 32 comments

  • [x] Old tests are passing
  • [ ] New e2e tests for App router are passing
  • [ ] New e2e tests for Pages router are passing
  • [ ] New unit tests for testing backward compatibility
  • [ ] All @ts-expect-error removed

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new API endpoint for cache revalidation, allowing users to revalidate data based on path or tag.
    • Added a CacheStateWatcher component to monitor and display cache states dynamically.
    • Implemented a RestartButton component for restarting the application from the UI.
  • Improvements

    • Enhanced global styles for better responsiveness and theming.
    • Improved error handling and logging in cache operations for better debugging.
  • Tests

    • Added comprehensive tests for cache revalidation mechanisms using Playwright.

better-salmon avatar Oct 30 '24 14:10 better-salmon

⚠️ No Changeset found

Latest commit: 31d3d435fd2a1f8c605868621f50d9d5b906584d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

💥 An error occurred when fetching the changed packages and changesets in this PR
Some errors occurred when validating the changesets config:
The package "@repo/cache-testing-15-app" depends on the ignored package "@repo/eslint-config", but "@repo/cache-testing-15-app" is not being ignored. Please add "@repo/cache-testing-15-app" to the `ignore` option.

changeset-bot[bot] avatar Oct 30 '24 14:10 changeset-bot[bot]

Walkthrough

The pull request introduces a series of changes across multiple files, primarily focusing on enhancing the caching mechanisms and configuration for a cache-testing application. Key modifications include updates to workflow commands, the addition of new configuration files, new components for handling caching and revalidation, and enhancements to existing cache handling logic. New environment variables and types are defined to improve type safety, and a comprehensive suite of tests is introduced to validate cache behaviors.

Changes

File Path Change Summary
.github/workflows/tests.yml Updated e2e test command to specify working directory.
.vscode/extensions.json Added three new recommended extensions for VSCode.
.vscode/settings.json Added setting for local Turbo use and fixed JSON syntax.
apps/cache-testing-15-app/.env.example Introduced new environment configuration file with essential variables for the application.
apps/cache-testing-15-app/.eslintrc.js Added ESLint configuration for the application.
apps/cache-testing-15-app/cache-handler-local.mjs Created a local cache handler integrating with existing CacheHandler framework.
apps/cache-testing-15-app/cache-handler-next-example.js Introduced a CacheHandler class for managing in-memory cache.
apps/cache-testing-15-app/cache-handler-redis-stack.js Implemented Redis caching with fallback to local LRU cache.
apps/cache-testing-15-app/cache-handler-redis-stack.mjs Similar to above, with support for Redis and local caching.
apps/cache-testing-15-app/cache-handler-redis-strings.mjs Added Redis cache handler with connection management and error handling.
apps/cache-testing-15-app/cluster.config.js New configuration for application running in cluster mode.
apps/cache-testing-15-app/create-instances.sh Script for setting up standalone application environment with directory management.
apps/cache-testing-15-app/next.config.mjs New Next.js configuration file with various settings for the application.
apps/cache-testing-15-app/package.json Introduced package.json with scripts and dependencies for the application.
apps/cache-testing-15-app/playwright.config.ts New Playwright configuration for testing parameters.
apps/cache-testing-15-app/run-app-instances.ts Script for managing application instances using PM2 and Fastify.
apps/cache-testing-15-app/src/app/api/revalidate-app/route.ts New API endpoint for handling cache revalidation requests.
apps/cache-testing-15-app/src/app/app/no-params/dynamic-false/[slug]/page.tsx New React component for handling dynamic routing without parameters.
apps/cache-testing-15-app/src/app/app/no-params/dynamic-true/[slug]/page.tsx New component for dynamic routing with parameters.
apps/cache-testing-15-app/src/app/app/no-params/ssr/200/page.tsx New SSR page for the specified route.
apps/cache-testing-15-app/src/app/app/ppr/page.tsx New component utilizing partial prerendering with loading states.
apps/cache-testing-15-app/src/app/app/randomHex/[length]/page.tsx New page for displaying random hexadecimal values based on length.
apps/cache-testing-15-app/src/app/app/static/route.ts New static route returning a basic HTTP response.
apps/cache-testing-15-app/src/app/app/with-params/dynamic-false/[slug]/page.tsx Component for handling dynamic routing with specific parameters.
apps/cache-testing-15-app/src/app/app/with-params/dynamic-true/[slug]/page.tsx Dynamic page component for routing based on URL parameters.
apps/cache-testing-15-app/src/app/app/with-params/nesh-cache/[slug]/page.tsx New component for handling dynamic routing with nesh cache.
apps/cache-testing-15-app/src/app/app/with-params/unstable-cache/[slug]/page.tsx New component for unstable cache handling.
apps/cache-testing-15-app/src/app/layout.tsx New root layout component for the application.
apps/cache-testing-15-app/src/components/cache-state-watcher.tsx New component for monitoring cache state.
apps/cache-testing-15-app/src/components/pre-rendered-at.tsx New component for displaying pre-rendered time.
apps/cache-testing-15-app/src/components/restart-button.tsx New button component for restarting the application.
apps/cache-testing-15-app/src/components/revalidate-button.tsx New button component for triggering revalidation processes.
apps/cache-testing-15-app/src/globals.css New CSS file defining global styles and custom properties.
apps/cache-testing-15-app/src/instrumentation.ts New function for registering initial cache based on environment settings.
apps/cache-testing-15-app/src/utils/common-app-page.tsx New component for rendering common application pages.
apps/cache-testing-15-app/src/utils/create-get-data.ts New utility function for fetching and caching data from an API.
apps/cache-testing-15-app/src/utils/format-time.ts New utility function for formatting time.
apps/cache-testing-15-app/src/utils/normalize-slug.ts New function for normalizing slugs based on predefined cases.
apps/cache-testing-15-app/src/utils/types.ts New TypeScript type definitions for various response structures.
apps/cache-testing-15-app/tests/app.spec.ts New test suite for validating cache revalidation mechanisms.
apps/cache-testing-15-app/tests/test-helpers.ts New utility functions for cache revalidation in tests.
apps/cache-testing-15-app/tsconfig.json New TypeScript configuration file for the application.
apps/cache-testing/package.json Updated dependencies in package.json.
docs/cache-handler-docs/package.json Updated dependencies in documentation package.json.
internal/next-common/package.json Updated dependency version in next-common package.json.
internal/next-common/src/next-common.ts Enhanced type definitions and imports related to caching.
internal/next-lru-cache/src/cache-types/next-cache-handler-value.ts Improved object size calculation logic for different cache types.
packages/cache-handler/src/cache-handler.ts Enhanced cache handling functionality with new types and properties.
packages/cache-handler/src/functions/nesh-cache.ts Updated caching functions to include new properties and error handling.
packages/cache-handler/src/functions/nesh-classic-cache.ts Modified cache handling logic to improve metadata handling.
packages/cache-handler/src/handlers/local-lru.ts Improved type safety for implicit tag handling.
packages/cache-handler/src/helpers/is-implicit-tag.ts Enhanced type safety for implicit tag checking.
packages/cache-handler/src/instrumentation/register-initial-cache.ts Improved error handling and logging in cache registration logic.
packages/server/src/server.ts Enhanced type safety and cache management in server route handlers.
package.json Added scripts for building and starting the cache-testing application.

Poem

In the burrows deep, where the cache does gleam,
A rabbit hops forth, with a joyful dream.
New scripts and handlers, oh what a delight,
With caching and testing, we'll hop through the night.
So raise up your carrots, let the code flow,
For changes are here, and we're ready to grow! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

coderabbitai[bot] avatar Oct 30 '24 14:10 coderabbitai[bot]

Can we include in this breaking change an export for export type NextCacheImplicitTagId = '_N_T_'; for the type or a constant, available to custom driver creator ?

ScreamZ avatar Oct 31 '24 08:10 ScreamZ

Can we include in this breaking change an export for export type NextCacheImplicitTagId = '_N_T_'; for the type or a constant, available to custom driver creator ?

I will do this in this PR

better-salmon avatar Oct 31 '24 12:10 better-salmon

Hey @better-salmon - could you tell me what is the status of this PR? Is it ready to use for testing in non-production environments? Or do you still expect significant changes other than tests and typing?

delemeator avatar Nov 16 '24 07:11 delemeator

Tested it quickly with cache-handler/redis-strings and next 15.0.3 stable

Got the error 500

⨯ TypeError: result.toUnchunkedString is not a function at sendRenderResult (/node_modules/next/dist/server/send-payload.js:66:54)

Happens only on the static pages

node: v18.18.2 production next build

https://github.com/vercel/next.js/blob/ef9d0969a050263403dd59573a09b345e75583a8/packages/next/src/server/send-payload.ts#L73

const payload = result.isDynamic ? null : result.toUnchunkedString();

result typeof "string" here, it seems

stupid fix fixes this problem, added it to cache-handler.mjs

/** fix for cache-handler and next 15 bug */
String.prototype.toUnchunkedString = function () {
  return this;
};

the cache keys are simple strings as expected, probably the cache-handler/redis-strings should return RenderResult not string. Haven't dive into how it works...

Please publish this PR to npm so we can play with it easily. Had to publish my own package in npm with this branch, will remove it later

maslowivan avatar Nov 17 '24 13:11 maslowivan

Hey y'all, any update on this? I'm assuming this resolves the following error

⨯ TypeError: Cannot read properties of undefined (reading 'isDynamic')

Is there any workaround in the meantime, or does this PR need to be merged?

GioLogist avatar Nov 26 '24 23:11 GioLogist

Following up on this? Anything I can do to help, or are we just waiting on official approval?

GioLogist avatar Dec 06 '24 07:12 GioLogist

Hello, any timeline for full next 15 support? (This PR merged @better-salmon @justinadkins

astrodomas avatar Dec 10 '24 13:12 astrodomas

Based on what I have investigated, there is still a lot to do in this PR. Especially as it is supposed to be backward compatible. There has been really a lot of changes in next 15 in terms of cache. I think better-salomon will need more time to complete this. My approach was to create a simpler version of cache handler that has the minimum features I need. Works for me well, so far, thus I would suggest to bridge the gap in your own capacity and give better-salomon time to do this right

delemeator avatar Dec 10 '24 13:12 delemeator

Based on what I have investigated, there is still a lot to do in this PR. Especially as it is supposed to be backward compatible. There has been really a lot of changes in next 15 in terms of cache. I think better-salomon will need more time to complete this. My approach was to create a simpler version of cache handler that has the minimum features I need. Works for me well, so far, thus I would suggest to bridge the gap in your own capacity and give better-salomon time to do this right

I believe it is not supposed to be backwards compatible? As per the official docs page This is the final release leading up to version 2.0.0. The upcoming version 2 will bring significant changes and will no longer support Next.js versions 13 and 14. While version 1 will still receive ongoing maintenance, it won't get any new features.

astrodomas avatar Dec 10 '24 15:12 astrodomas

@astrodomas I'm not a contributor, just another dev looking for Next 15 support. I tinkered with this PR a bit though and have been running a patched version of this lib on a low traffic environment without any issues yet. The only changes I made to this PR can be seen here.

I also added this to the top of my cache-handler.mjs file based on previous feedback.

// @see https://github.com/caching-tools/next-shared-cache/pull/846#issuecomment-2481276786
// eslint-disable-next-line @typescript-eslint/no-explicit-any
String.prototype.toUnchunkedString = function () {
  return this;
};

justinadkins avatar Dec 10 '24 23:12 justinadkins

For anyone following along, I discovered an issue with my fork of this PR today and addressed it with this commit. It came to light when I noticed that an intercepting route wasn't working.

justinadkins avatar Dec 18 '24 00:12 justinadkins

hi 👋

I see this PR has been stalled for a while. We're are currently implementing shared caching with next 15 and would love this update 👀

Netail avatar Jan 15 '25 17:01 Netail

Same for us - we are waiting for this missing part to upgrade our apps to next15 - is there anything one can do to help with the development of this change? 😊

ichichich3011 avatar Jan 16 '25 08:01 ichichich3011

@better-salmon I'd be happy to help get this over the finish line as well. I've been running a forked and patched version of this PR for a little over a month now. I can make a PR against this branch if that would be helpful.

justinadkins avatar Jan 21 '25 22:01 justinadkins

@justinadkins @better-salmon (if you still here) seems like the best solution for node.js + redis (strings) should look like this

/**
 * @param {string} key 
 * @param {string[]} [tags]
 * @returns {string}
 */
const generateCacheKey = (key, tags = []) => {
  const tagsStr = tags.length > 0 ? `:t_${tags.join("t_")}` : "";
  return `${key}${tagsStr}:${buildId}`;
};

// set
async set(key, data, ctx) {
  const generatedKey = generateCacheKey(key, ctx.tags);
  // ...
  if (data.revalidate) {
    await client.setEx(generatedKey, data.revalidate, value);
  } else {
    await client.set(generatedKey, value);
  }
   // ...
}

// get
async get(key, ctx) {
  const generatedKey = generateCacheKey(key, ctx.tags);
  // ...
  if (data.revalidate) {
    await client.setEx(generatedKey, data.revalidate, value);
  } else {
    await client.set(generatedKey, value);
  }
   // ...
}

/**
 * @param {string} pattern 
 * @param {ReturnType<typeof createClient>} client 
 */
export async function deleteKeysWithPattern(pattern, client) {
  const script = `
  local key = tostring(KEYS[1]);
  local delsCount = 0;
  local cursor = 0;
  repeat
    local result = redis.call('SCAN', cursor, 'MATCH', key)
    for _, key in ipairs(result[2]) do
      redis.call('UNLINK', key);
      delsCount = delsCount + 1;
    end
    cursor = tonumber(result[1]);
  until cursor == 0;
  return delsCount;`;
  try {
    const delsCount = await client.eval(script, { keys: [pattern] });
  } catch (error) {
    console.error(`Error deleting keys matching pattern '${pattern}':`, error);
  }
}

async revalidateTag(tags) {
  // ...
  for await (let tag of tags) {
      await deleteKeysWithPattern(`*t_${tag}*`, client);
   }
   // ...
}

great read/write performance, no complexity with sets/hashes/etc, also AWS Elasticache supports EVAL

I wrote it today, tested and all works well

And do we really need to diff (kinds) APP_ROUTE, FETCH, etc?

maslowivan avatar Jan 27 '25 10:01 maslowivan

Yes, because you have many different things you can cache: fetch, rendered html etc.

@justinadkins @better-salmon (if you still here) seems like the best solution for node.js + redis (strings) should look like this

/**
 * @param {string} key 
 * @param {string[]} [tags]
 * @returns {string}
 */
const generateCacheKey = (key, tags = []) => {
  const tagsStr = tags.length > 0 ? `:t_${tags.join("t_")}` : "";
  return `${key}${tagsStr}:${buildId}`;
};

// set
async set(key, data, ctx) {
  const generatedKey = generateCacheKey(key, ctx.tags);
  // ...
  if (data.revalidate) {
    await client.setEx(generatedKey, data.revalidate, value);
  } else {
    await client.set(generatedKey, value);
  }
   // ...
}

// get
async get(key, ctx) {
  const generatedKey = generateCacheKey(key, ctx.tags);
  // ...
  if (data.revalidate) {
    await client.setEx(generatedKey, data.revalidate, value);
  } else {
    await client.set(generatedKey, value);
  }
   // ...
}

/**
 * @param {string} pattern 
 * @param {ReturnType<typeof createClient>} client 
 */
export async function deleteKeysWithPattern(pattern, client) {
  const script = `
  local key = tostring(KEYS[1]);
  local delsCount = 0;
  local cursor = 0;
  repeat
    local result = redis.call('SCAN', cursor, 'MATCH', key)
    for _, key in ipairs(result[2]) do
      redis.call('UNLINK', key);
      delsCount = delsCount + 1;
    end
    cursor = tonumber(result[1]);
  until cursor == 0;
  return delsCount;`;
  try {
    const delsCount = await client.eval(script, { keys: [pattern] });
  } catch (error) {
    console.error(`Error deleting keys matching pattern '${pattern}':`, error);
  }
}

async revalidateTag(tags) {
  // ...
  for await (let tag of tags) {
      await deleteKeysWithPattern(`*t_${tag}*`, client);
   }
   // ...
}

great read/write performance, no complexity with sets/hashes/etc, also AWS Elasticache supports EVAL

I wrote it today, tested and all works well

And do we really need to diff (kinds) APP_ROUTE, FETCH, etc?

KraKeN-47 avatar Jan 29 '25 23:01 KraKeN-47

@better-salmon I'd be happy to help get this over the finish line as well. I've been running a forked and patched version of this PR for a little over a month now. I can make a PR against this branch if that would be helpful.

@justinadkins Would you mind publishing the fork as a temporary measure?

jdpnielsen avatar Jan 30 '25 15:01 jdpnielsen

Yes, because you have many different things you can cache: fetch, rendered html etc.

what is the difference? just store the value, is always string @KraKeN-47

maslowivan avatar Jan 30 '25 21:01 maslowivan

@jdpnielsen I can get it published soon. Will report back here when I do.

@studentIvan I'd need to revisit the source code to see if the additional complexity in this repo is beneficial / necessary compared to your approach. I was just focused on getting this repo working when I was tinkering with it as opposed to home brewing a solution.

justinadkins avatar Jan 30 '25 21:01 justinadkins

@justinadkins it seems like any fix can't fix the pipeTo problem. Because some keys (technically it's ?_rsc=xxx pages cache prefetched from desktop Link component) have rscData Buffer with too much weight now, like 550kb+. pipeTo fails because it has a data read/write timeout it seems (not sure from which library exactly) How are going to solve this problem?

First Idea I got is simple store big rscData values in LRU cache and check it first, before redis, but here we getting another problem - revalidation of these values.

Maybe next.js devs left this "bug" or "feature" for us, to move us to vercel...

maslowivan avatar Feb 01 '25 12:02 maslowivan

@justinadkins it seems like any fix can't fix the pipeTo problem. Because some keys (technically it's ?_rsc=xxx pages cache prefetched from desktop Link component) have rscData Buffer with too much weight now, like 550kb+. pipeTo fails because it has a data read/write timeout it seems (not sure from which library exactly) How are going to solve this problem?

First Idea I got is simple store big rscData values in LRU cache and check it first, before redis, but here we getting another problem - revalidation of these values.

Maybe next.js devs left this "bug" or "feature" for us, to move us to vercel...

maybe compression is needed?

astrodomas avatar Feb 03 '25 08:02 astrodomas

@justinadkins it seems like any fix can't fix the pipeTo problem. Because some keys (technically it's ?_rsc=xxx pages cache prefetched from desktop Link component) have rscData Buffer with too much weight now, like 550kb+. pipeTo fails because it has a data read/write timeout it seems (not sure from which library exactly) How are going to solve this problem?

First Idea I got is simple store big rscData values in LRU cache and check it first, before redis, but here we getting another problem - revalidation of these values.

Maybe next.js devs left this "bug" or "feature" for us, to move us to vercel...

How about relying on LRU for some cache values, but have the cache manifest in Redis (shared) so revalidation will apply to all instances.

tleguijt avatar Feb 05 '25 14:02 tleguijt

@justinadkins it seems like any fix can't fix the pipeTo problem. Because some keys (technically it's ?_rsc=xxx pages cache prefetched from desktop Link component) have rscData Buffer with too much weight now, like 550kb+. pipeTo fails because it has a data read/write timeout it seems (not sure from which library exactly) How are going to solve this problem? First Idea I got is simple store big rscData values in LRU cache and check it first, before redis, but here we getting another problem - revalidation of these values. Maybe next.js devs left this "bug" or "feature" for us, to move us to vercel...

maybe compression is needed?

Yes can help but we can lost some time and cpu in this case, any idea how to implement it quickly? For now I've implemented my own cache handler with LRU + redis together

maslowivan avatar Feb 06 '25 04:02 maslowivan

How about relying on LRU for some cache values, but have the cache manifest in Redis (shared) so revalidation will apply to all instances.

Didn't get you. Can you explain or add details pls?

maslowivan avatar Feb 06 '25 05:02 maslowivan

How about relying on LRU for some cache values, but have the cache manifest in Redis (shared) so revalidation will apply to all instances.

Didn't get you. Can you explain or add details pls?

I don't know how best to solve the size issue for certain cache values in Redis. But I'm also looking how we can implement an hybrid approach. For example we have the following setup since upgrading to NextJS (and therefore having to abandon our Redis cache solution for now);

We are running Cloud Run instances with a Cloud Storage bucket volume mount on .next/cache/fetch-cache for example so we at least have a shared cache for all instances

Having this shared cache is great since when scaling up, the new instances can use the same shared data cache. However, invalidation is still an issue because invalidation doesn't happen by deleting a file, but marking a certain tag as invalidated in the (in-memory) tagsManifest in https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/incremental-cache/tags-manifest.external.ts

If that tagsManifest could be shared as well (in Redis for example) at least you have a shared invalidation source.

At that point it wouldn't matter if you are getting your cache value from Redis, disk or in-memory; Invalidations will be shared anyway.

tleguijt avatar Feb 06 '25 10:02 tleguijt

I found this repo while looking for alternatives:

  • https://github.com/fortedigital/nextjs-cache-handler

@AyronK is your fork running OK with Next.js 15? I see you made couple of patches in there.

taraspos avatar Feb 07 '25 14:02 taraspos

@taraspos yes, we're using it with Next 15 with a bit of workaround https://github.com/fortedigital/nextjs-cache-handler/blob/ce977bd5c45f6f117ec0dad4cc21263ec3b9fae0/packages/nextjs-cache-handler/src/handlers/buffer-string-decorator.ts. However I haven't seen this PR before, not sure how it's gonna work in the next version of this package.

AyronK avatar Feb 07 '25 14:02 AyronK

Hey y'all, sorry for the delay in following up. I've published my patch here if you need something temporarily (use at your own discretion). It uses Buffer for routes and pages similarly to what is described above.

If you'd like to see the diff from this PR to what is published, you can find it here.

justinadkins avatar Feb 14 '25 20:02 justinadkins