fix: serialize Map and Buffer in Redis ISR handler (fixes #15)#29
fix: serialize Map and Buffer in Redis ISR handler (fixes #15)#29mrjasonroy merged 3 commits intomainfrom
Conversation
Next.js 16 APP_PAGE entries store segmentData as a Map<string, Buffer>
and rscData as a Buffer. Plain JSON.stringify converts Maps to {} and
loses Buffer identity, causing "segmentData.get is not a function" at
runtime. Add custom JSON replacer/reviver to preserve these types
through the Redis serialization round-trip.
Also adds APP_PAGE, APP_ROUTE, PAGES, and REDIRECT to the CacheValue
type union to match the full Next.js 16 cache entry vocabulary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello @mrjasonroy, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses critical serialization issues within the Redis ISR cache handler, specifically for Next.js 16 applications. It ensures that complex data structures like Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request effectively addresses the serialization issue with Map and Buffer types when using the Redis ISR cache handler. The introduction of custom jsonReplacer and jsonReviver functions is a solid approach, and the implementation correctly handles serialization to and from a string format suitable for Redis. I appreciate the backward compatibility support for the old Buffer JSON format. The type definitions have been correctly expanded to include new Next.js 16 cache entry kinds, and the new test suite is comprehensive, covering the new functionality, edge cases, and regression testing. I have a couple of suggestions to improve the robustness of the jsonReviver function against malformed cache data.
| if (value && typeof value === "object") { | ||
| const obj = value as Record<string, unknown>; | ||
|
|
||
| if (obj.__serialized_type === "Map" && Array.isArray(obj.entries)) { |
There was a problem hiding this comment.
For improved robustness, you could add a check to ensure obj.entries contains valid key-value pairs before attempting to create a Map. This makes the reviver more defensive against malformed cache data, which could prevent runtime errors if the cache is ever manually edited or corrupted.
| if (obj.__serialized_type === "Map" && Array.isArray(obj.entries)) { | |
| if (obj.__serialized_type === "Map" && Array.isArray(obj.entries) && obj.entries.every(e => Array.isArray(e) && e.length === 2)) { |
| } | ||
|
|
||
| // Backward compat: Node's Buffer.toJSON() format | ||
| if (obj.type === "Buffer" && Array.isArray(obj.data)) { |
There was a problem hiding this comment.
Similar to the Map deserialization, you could make the backward-compatible Buffer revival more robust by checking if obj.data is an array of numbers before passing it to Buffer.from(). This would make the parsing more defensive against malformed data.
| if (obj.type === "Buffer" && Array.isArray(obj.data)) { | |
| if (obj.type === "Buffer" && Array.isArray(obj.data) && obj.data.every(n => typeof n === "number")) { |
Validate that deserialized Map entries are proper [key, value] pairs before constructing a Map, guarding against corrupted cache data. Addresses Gemini Code Assist review suggestion on PR #29. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
High priority:
- Force cache miss for old APP_PAGE entries with plain object segmentData
(entries stored before the Map serialization fix would crash Next.js)
- Add missing upstreamEtag field to IMAGE type to match Next.js 16
- Add IMAGE round-trip test through Redis handler
Medium priority:
- Tighten Buffer backward-compat check with number[] validation to
avoid false-positives on user data with { type: "Buffer", data: [...] }
- Fix memory handler size calculation to use jsonReplacer (Maps were
estimated as "{}" = 2 bytes regardless of actual content)
- Remove stale ROUTE and PAGE kinds from CacheValue (Next.js 16 uses
APP_PAGE, APP_ROUTE, PAGES, FETCH, REDIRECT, IMAGE)
- Add binary data Buffer test with non-UTF8 bytes (0x00, 0xff, etc.)
Also:
- Extract jsonReplacer/jsonReviver to shared helpers/serialization.ts
- Export serialization helpers from public API for custom handler reuse
- Add REDIRECT round-trip test
- Update all test files to use Next.js 16 cache value kinds
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
r.segmentData.get is not a functionwhen using the Redis ISR cache handler with Next.js 16 APP_PAGE entriessegmentDataas aMap<string, Buffer>andrscDataas aBufferon APP_PAGE cache entries. PlainJSON.stringifyconverts Maps to{}and loses Buffer identity, so on deserializationsegmentData.get()throwsjsonReplacer/jsonReviverto the Redis ISR handler that preserveMapandBuffertypes through the serialization round-tripAPP_PAGE,APP_ROUTE,PAGES, andREDIRECTto theCacheValuetype union to match the full Next.js 16 cache entry vocabularyChanges
packages/cache-handler/src/handlers/redis.ts—jsonReplacer/jsonReviverfunctions;set()uses replacer,get()uses reviver. Also handles backward-compat with Node's nativeBuffer.toJSON()format for entries stored before this fixpackages/cache-handler/src/types.ts— adds missing cache value kinds (APP_PAGE,APP_ROUTE,PAGES,REDIRECT)packages/cache-handler/src/handlers/redis.test.ts— new test suite covering APP_PAGE with segmentData Map + rscData Buffer round-trip, empty/undefined segmentData, APP_ROUTE with Buffer body, backward compat with old Buffer JSON format, and existing value typesTest plan
pnpm vitest run)pnpm lint)pnpm typecheck)🤖 Generated with Claude Code