Skip to content

fix: support ArrayBuffer serialization#358

Open
xianjianlf2 wants to merge 1 commit into
flightcontrolhq:mainfrom
xianjianlf2:fix/arraybuffer-serialization
Open

fix: support ArrayBuffer serialization#358
xianjianlf2 wants to merge 1 commit into
flightcontrolhq:mainfrom
xianjianlf2:fix/arraybuffer-serialization

Conversation

@xianjianlf2

@xianjianlf2 xianjianlf2 commented Jul 15, 2026

Copy link
Copy Markdown

Problem

A plain ArrayBuffer currently round-trips to an empty object, silently losing all bytes and the type:

const buf = new Uint8Array([1, 2, 3, 4]).buffer;
SuperJSON.parse(SuperJSON.stringify({ buf }));
// => { buf: {} }

SuperJSON already supports every TypedArray (including the recently added BigInt64Array/BigUint64Array, #353), but a bare ArrayBuffer isn't a plain object, array, Map/Set, Error, or registered class, so it falls through untransformed and JSON.stringify reduces it to {} — with no error or warning.

Changes

  • Added an ArrayBuffer leaf transformer in src/transformer.ts (with an isArrayBuffer guard in src/is.ts), following the same convention as the existing typed-array support: the buffer is serialized as an array of byte values (JSON can't represent raw bytes) and reconstructed via new Uint8Array(bytes).buffer.
  • Added a stringify & parse test case covering a non-empty and an empty ArrayBuffer, asserting the serialized form, the annotation, and byte-exact reconstruction (+24 lines in src/index.test.ts).
  • Added ArrayBuffer to the README support table.

npm test (vitest): 7 files, 87 passed, 1 skipped (pre-existing), 1 todo.

Greptile Summary

This PR adds native ArrayBuffer serialization support to SuperJSON by following the existing typed-array convention: the buffer is serialized as a plain JSON array of byte values and reconstructed via new Uint8Array(bytes).buffer. Before this change, a bare ArrayBuffer would silently serialize to {}, losing all data.

  • src/is.ts / src/transformer.ts: Adds isArrayBuffer guard and registers a new simpleTransformation entry that converts ArrayBuffer ↔ number[]. The rule is correctly placed after all other simple rules and does not conflict with the existing isTypedArray composite rule (since ArrayBuffer.isView(anArrayBuffer) is false).
  • src/index.test.ts: Covers non-empty and empty ArrayBuffer round-trips, verifying serialized form, annotation key, instance type, and byte-exact reconstruction.

Confidence Score: 4/5

Safe to merge — the change is self-contained and the round-trip is byte-exact.

The implementation correctly follows the existing simple-transformation pattern, does not conflict with the TypedArray composite rule (since ArrayBuffer.isView returns false for a bare ArrayBuffer), and the test covers both empty and non-empty buffers with byte-exact assertions. One minor style inconsistency was noted but does not affect correctness.

No files require special attention.

Important Files Changed

Filename Overview
src/transformer.ts Adds ArrayBuffer to LeafTypeAnnotation union and appends a simpleTransformation rule; logic is correct and consistent with surrounding patterns
src/is.ts Adds isArrayBuffer guard using instanceof ArrayBuffer, idiomatic and consistent with all other type guards in the file
src/index.test.ts Adds a stringify/parse test case covering non-empty and empty ArrayBuffers with byte-exact assertions; follows the existing dontExpectEquality + customExpectations pattern correctly
README.md Adds ArrayBuffer row to the supported-types table; accurate and complete

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant C as Caller
    participant SJ as SuperJSON
    participant T as transformer.ts
    participant IS as is.ts

    Note over C,IS: Serialization (stringify)
    C->>SJ: "stringify({ buf: ArrayBuffer })"
    SJ->>T: transformValue(ArrayBuffer)
    T->>IS: isArrayBuffer(value)
    IS-->>T: true
    T->>T: Array.from(new Uint8Array(buf))
    T-->>SJ: "{ value: [1,2,3,4], type: ArrayBuffer }"
    SJ-->>C: json string with annotation

    Note over C,IS: Deserialization (parse)
    C->>SJ: parse(jsonString)
    SJ->>T: untransformValue([1,2,3,4], ArrayBuffer)
    T->>T: new Uint8Array([1,2,3,4]).buffer
    T-->>SJ: ArrayBuffer(4)
    SJ-->>C: "{ buf: ArrayBuffer }"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant C as Caller
    participant SJ as SuperJSON
    participant T as transformer.ts
    participant IS as is.ts

    Note over C,IS: Serialization (stringify)
    C->>SJ: "stringify({ buf: ArrayBuffer })"
    SJ->>T: transformValue(ArrayBuffer)
    T->>IS: isArrayBuffer(value)
    IS-->>T: true
    T->>T: Array.from(new Uint8Array(buf))
    T-->>SJ: "{ value: [1,2,3,4], type: ArrayBuffer }"
    SJ-->>C: json string with annotation

    Note over C,IS: Deserialization (parse)
    C->>SJ: parse(jsonString)
    SJ->>T: untransformValue([1,2,3,4], ArrayBuffer)
    T->>T: new Uint8Array([1,2,3,4]).buffer
    T-->>SJ: ArrayBuffer(4)
    SJ-->>C: "{ buf: ArrayBuffer }"
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
src/transformer.ts:186
Minor style inconsistency: the existing `typedArrayRule` spreads its view with `[...v]` (line 256), while the new rule uses `Array.from(new Uint8Array(v))`. Both produce identical results, but using the spread form matches the established pattern in this file.

```suggestion
    v => [...new Uint8Array(v)],
```

Reviews (1): Last reviewed commit: "fix: preserve ArrayBuffer instead of sil..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

SuperJSON supports every TypedArray (Int8Array … BigInt64Array) and
DataView-adjacent binary types, but a plain `ArrayBuffer` was not handled
at all. Because it isn't a plain object, array, Map/Set, Error, or
registered class, it fell through untransformed: `stringify` emitted `{}`
and `parse` returned an empty plain object, silently losing all bytes and
the type.

Add an `ArrayBuffer` leaf transformer that serializes the buffer as an
array of byte values (JSON can't represent raw bytes) and reconstructs it
via `new Uint8Array(bytes).buffer`, mirroring the existing typed-array
convention.
@xianjianlf2 xianjianlf2 requested a review from Skn0tt as a code owner July 15, 2026 12:48
Comment thread src/transformer.ts
isArrayBuffer,
'ArrayBuffer',
// Raw bytes aren't valid JSON, so they're stored as an array of byte values.
v => Array.from(new Uint8Array(v)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Minor style inconsistency: the existing typedArrayRule spreads its view with [...v] (line 256), while the new rule uses Array.from(new Uint8Array(v)). Both produce identical results, but using the spread form matches the established pattern in this file.

Suggested change
v => Array.from(new Uint8Array(v)),
v => [...new Uint8Array(v)],
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/transformer.ts
Line: 186

Comment:
Minor style inconsistency: the existing `typedArrayRule` spreads its view with `[...v]` (line 256), while the new rule uses `Array.from(new Uint8Array(v))`. Both produce identical results, but using the spread form matches the established pattern in this file.

```suggestion
    v => [...new Uint8Array(v)],
```

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant