From 9be223a58d066c5d1518a7a31ee7d58e17846354 Mon Sep 17 00:00:00 2001 From: Darsh Patel Date: Sun, 19 Oct 2025 17:12:10 -0700 Subject: [PATCH 1/3] feat: allow string enums for errors --- __tests__/typescript-stress.test.ts | 53 +++++++++++++++++++++++++++++ router/errors.ts | 14 ++++++-- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/__tests__/typescript-stress.test.ts b/__tests__/typescript-stress.test.ts index c1a5cd7b..c20cccf4 100644 --- a/__tests__/typescript-stress.test.ts +++ b/__tests__/typescript-stress.test.ts @@ -25,6 +25,12 @@ import { flattenErrorType, ProcedureErrorSchemaType } from '../router/errors'; import { ReadableImpl } from '../router/streams'; import { createMockTransportNetwork } from '../testUtil/fixtures/mockTransport'; +enum TestErrorCodes { + ERROR_ONE = 'ERROR_ONE', + ERROR_TWO = 'ERROR_TWO', + ERROR_THREE = 'ERROR_THREE', +} + const requestData = Type.Union([ Type.Object({ a: Type.Number() }), Type.Object({ c: Type.String() }), @@ -479,6 +485,53 @@ describe('Procedure error schema', () => { ); }); + test('object with enum code', () => { + acceptErrorSchema( + Type.Object({ + code: Type.Enum(TestErrorCodes), + message: Type.String(), + }), + ); + }); + + test('union containing enum-based error schemas', () => { + acceptErrorSchema( + Type.Union([ + Type.Object({ + code: Type.Enum(TestErrorCodes), + message: Type.String(), + }), + Type.Object({ + code: Type.Literal('OTHER_ERROR'), + message: Type.String(), + }), + ]), + ); + }); + + test('flattenErrorType with enum-based error schemas', () => { + acceptErrorSchema( + flattenErrorType( + Type.Union([ + Type.Object({ + code: Type.Enum(TestErrorCodes), + message: Type.String(), + }), + Type.Union([ + Type.Object({ + code: Type.Literal('ERROR_4'), + message: Type.String(), + }), + Type.Object({ + code: Type.Literal('ERROR_5'), + message: Type.String(), + }), + ]), + ]), + ), + ); + }); + test('union of union', () => { acceptErrorSchema( flattenErrorType( diff --git a/router/errors.ts b/router/errors.ts index 836d9411..62186319 100644 --- a/router/errors.ts +++ b/router/errors.ts @@ -1,6 +1,7 @@ import { Kind, Static, + TEnum, TLiteral, TNever, TObject, @@ -32,13 +33,18 @@ export const CANCEL_CODE = 'CANCEL'; type TLiteralString = TLiteral; +/** + * String literals or string enums for error codes. + */ +type TStringCode = TLiteralString | TEnum>; + export type BaseErrorSchemaType = | TObject<{ - code: TLiteralString; + code: TStringCode; message: TLiteralString | TString; }> | TObject<{ - code: TLiteralString; + code: TStringCode; message: TLiteralString | TString; extras: TSchema; }>; @@ -139,7 +145,9 @@ function isUnion(schema: TSchema): schema is TUnion { type Flatten = T extends BaseErrorSchemaType ? T : T extends TUnion> - ? Flatten + ? U extends BaseErrorSchemaType + ? TUnion> + : Flatten : unknown; /** From a8fa52f72cc492ea89433160b39525a392f16436 Mon Sep 17 00:00:00 2001 From: Darsh Patel Date: Sun, 19 Oct 2025 17:17:54 -0700 Subject: [PATCH 2/3] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7394f97f..3f2d5736 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@replit/river", "description": "It's like tRPC but... with JSON Schema Support, duplex streaming and support for service multiplexing. Transport agnostic!", - "version": "0.209.8", + "version": "0.210.0", "type": "module", "exports": { ".": { From 05466f98b98999c8d6a827a4c4880b3f2f5cbae3 Mon Sep 17 00:00:00 2001 From: Darsh Patel Date: Sun, 19 Oct 2025 17:22:38 -0700 Subject: [PATCH 3/3] chore: match patterns --- router/errors.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/router/errors.ts b/router/errors.ts index 62186319..32e66060 100644 --- a/router/errors.ts +++ b/router/errors.ts @@ -33,18 +33,15 @@ export const CANCEL_CODE = 'CANCEL'; type TLiteralString = TLiteral; -/** - * String literals or string enums for error codes. - */ -type TStringCode = TLiteralString | TEnum>; +type TEnumString = TEnum>; export type BaseErrorSchemaType = | TObject<{ - code: TStringCode; + code: TLiteralString | TEnumString; message: TLiteralString | TString; }> | TObject<{ - code: TStringCode; + code: TLiteralString | TEnumString; message: TLiteralString | TString; extras: TSchema; }>;