From 39923a272499e654551c6c65d10e784fa1e08059 Mon Sep 17 00:00:00 2001 From: captain-mirage <241807724+captain-mirage@users.noreply.github.com> Date: Mon, 21 Sep 2026 15:00:30 -0400 Subject: [PATCH] Fix TS Result serializer using the ok payload type for err `SumType.makeSerializer`'s Result fast path built both the `ok` and the `err` serializer from `ty.variants[0].algebraicType`, so a `Result` whose `Err` payload type differs from `Ok` was written with the `ok` encoder. `makeDeserializer` already reads `err` with `variants[1]`, so the two halves disagree: the value is encoded as `Ok` and decoded as `Err`. The failure is silent whenever the wrong encoder still produces bytes the `err` decoder accepts. For `Result`, `{ err: 'boom' }` encodes as the single byte `0x00` (a `string` coerced through `writeU8`); for a `Result`, `{ err: { code: 42 } }` encodes as the UTF-8 string `"[object Object]"`. Nothing throws at encode time; the corruption surfaces later as a wrong value or a confusing decode error. Use `variants[1].algebraicType` for `serializeErr`, matching the deserializer. --- .../src/lib/algebraic_type.ts | 2 +- .../bindings-typescript/tests/serde.test.ts | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/crates/bindings-typescript/src/lib/algebraic_type.ts b/crates/bindings-typescript/src/lib/algebraic_type.ts index c325797af75..2009dab02e5 100644 --- a/crates/bindings-typescript/src/lib/algebraic_type.ts +++ b/crates/bindings-typescript/src/lib/algebraic_type.ts @@ -657,7 +657,7 @@ export const SumType = { typespace ); const serializeErr = AlgebraicType.makeSerializer( - ty.variants[0].algebraicType, + ty.variants[1].algebraicType, typespace ); diff --git a/crates/bindings-typescript/tests/serde.test.ts b/crates/bindings-typescript/tests/serde.test.ts index 84f2c70f6e0..b2695d6b2ef 100644 --- a/crates/bindings-typescript/tests/serde.test.ts +++ b/crates/bindings-typescript/tests/serde.test.ts @@ -5,6 +5,7 @@ import { BinaryWriter, ConnectionId, Identity, + Result, ScheduleAt, Uuid, } from '../src'; @@ -184,4 +185,72 @@ describe('it correctly serializes and deserializes algebraic values', () => { expect(deserializedValue).toEqual(value); }); + + test('when it serializes and deserializes a Result with an ok payload', () => { + const value = { ok: 7 }; + + const algebraic_type = Result.getAlgebraicType( + AlgebraicType.U8, + AlgebraicType.String + ); + const binaryWriter = new BinaryWriter(1024); + AlgebraicType.serializeValue(binaryWriter, algebraic_type, value); + + const buffer = binaryWriter.getBuffer(); + // Tag 0 (`ok`), then the `u8` payload. + expect(buffer).toEqual(new Uint8Array([0, 7])); + + const deserializedValue = AlgebraicType.deserializeValue( + new BinaryReader(buffer), + algebraic_type + ); + + expect(deserializedValue).toEqual(value); + }); + + test('when it serializes and deserializes a Result with an err payload', () => { + const value = { err: 'boom' }; + + const algebraic_type = Result.getAlgebraicType( + AlgebraicType.U8, + AlgebraicType.String + ); + const binaryWriter = new BinaryWriter(1024); + AlgebraicType.serializeValue(binaryWriter, algebraic_type, value); + + const buffer = binaryWriter.getBuffer(); + // Tag 1 (`err`), then the `string` payload: a u32 length and its bytes. + expect(buffer).toEqual(new Uint8Array([1, 4, 0, 0, 0, 98, 111, 111, 109])); + + const deserializedValue = AlgebraicType.deserializeValue( + new BinaryReader(buffer), + algebraic_type + ); + + expect(deserializedValue).toEqual(value); + }); + + test('when it serializes and deserializes a Result with a product err payload', () => { + const value = { err: { code: 42 } }; + + const algebraic_type = Result.getAlgebraicType( + AlgebraicType.String, + AlgebraicType.Product({ + elements: [{ name: 'code', algebraicType: AlgebraicType.I32 }], + }) + ); + const binaryWriter = new BinaryWriter(1024); + AlgebraicType.serializeValue(binaryWriter, algebraic_type, value); + + const buffer = binaryWriter.getBuffer(); + // Tag 1 (`err`), then the product payload: 42 as a little-endian i32. + expect(buffer).toEqual(new Uint8Array([1, 42, 0, 0, 0])); + + const deserializedValue = AlgebraicType.deserializeValue( + new BinaryReader(buffer), + algebraic_type + ); + + expect(deserializedValue).toEqual(value); + }); });