import { AnyRouter, inferRouterError } from '@trpc/server'; import { TRPCResponse, TRPCResponseMessage, TRPCResultMessage, } from '@trpc/server/rpc'; import { TRPCClientRuntime } from '..'; import { TRPCClientError } from '../../TRPCClientError'; // FIXME: // - the generics here are probably unnecessary // - the RPC-spec could probably be simplified to combine HTTP + WS /** @internal */ function transformResultInner( response: | TRPCResponseMessage> | TRPCResponse>, runtime: TRPCClientRuntime, ) { if ('error' in response) { const error = runtime.transformer.deserialize( response.error, ) as inferRouterError; return { ok: false, error: { ...response, error, }, } as const; } const result = { ...response.result, ...((!response.result.type || response.result.type === 'data') && { type: 'data', data: runtime.transformer.deserialize(response.result.data), }), } as TRPCResultMessage['result']; return { ok: true, result } as const; } function isObject(value: unknown): value is Record { // check that value is object return !!value && !Array.isArray(value) && typeof value === 'object'; } /** * Transforms and validates that the result is a valid TRPCResponse * @internal */ export function transformResult( response: | TRPCResponseMessage> | TRPCResponse>, runtime: TRPCClientRuntime, ): ReturnType { let result: ReturnType; try { // Use the data transformers on the JSON-response result = transformResultInner(response, runtime); } catch (err) { throw new TRPCClientError('Unable to transform response from server'); } // check that output of the transformers is a valid TRPCResponse if ( !result.ok && (!isObject(result.error.error) || typeof result.error.error.code !== 'number') ) { throw new TRPCClientError('Badly formatted response from server'); } if (result.ok && !isObject(result.result)) { throw new TRPCClientError('Badly formatted response from server'); } return result; }