@@ -13,6 +13,7 @@ import {
1313 ZodInferOrType ,
1414 ZodInputOrType ,
1515} from './type-utils' ;
16+ import { UnknownStatusError } from './unknown-status-error' ;
1617
1718type RecursiveProxyObj < T extends AppRouter , TClientArgs extends ClientArgs > = {
1819 [ TKey in keyof T ] : T [ TKey ] extends AppRoute
@@ -22,6 +23,17 @@ type RecursiveProxyObj<T extends AppRouter, TClientArgs extends ClientArgs> = {
2223 : never ;
2324} ;
2425
26+ type RecursiveProxyObjNoUnknownStatus <
27+ T extends AppRouter ,
28+ TClientArgs extends ClientArgs & { throwOnUnknownStatus : true }
29+ > = {
30+ [ TKey in keyof T ] : T [ TKey ] extends AppRoute
31+ ? AppRouteFunctionNoUnknownStatus < T [ TKey ] , TClientArgs >
32+ : T [ TKey ] extends AppRouter
33+ ? RecursiveProxyObjNoUnknownStatus < T [ TKey ] , TClientArgs >
34+ : never ;
35+ } ;
36+
2537type AppRouteMutationType < T > = ZodInputOrType < T > ;
2638
2739/**
@@ -96,13 +108,16 @@ type DataReturnArgs<
96108 Without < DataReturnArgsBase < TRoute , TClientArgs > , never >
97109> ;
98110
99- export type ApiRouteResponse < T > =
111+ export type ApiRouteResponseNoUnknownStatus < T > =
100112 | {
101113 [ K in keyof T ] : {
102114 status : K ;
103115 body : ZodInferOrType < T [ K ] > ;
104116 } ;
105- } [ keyof T ]
117+ } [ keyof T ] ;
118+
119+ export type ApiRouteResponse < T > =
120+ | ApiRouteResponseNoUnknownStatus < T >
106121 | {
107122 status : Exclude < HTTPStatusCode , keyof T > ;
108123 body : unknown ;
@@ -140,6 +155,22 @@ export type AppRouteFunction<
140155 args : Prettify < DataReturnArgs < TRoute , TClientArgs > >
141156 ) => Promise < Prettify < ApiRouteResponse < TRoute [ 'responses' ] > > > ;
142157
158+ /**
159+ * Returned from a mutation or query call when NoUnknownStatus mode is enabled
160+ */
161+ export type AppRouteFunctionNoUnknownStatus <
162+ TRoute extends AppRoute ,
163+ TClientArgs extends ClientArgs
164+ > = AreAllPropertiesOptional < DataReturnArgs < TRoute , TClientArgs > > extends true
165+ ? (
166+ args ?: Prettify < DataReturnArgs < TRoute , TClientArgs > >
167+ ) => Promise < Prettify < ApiRouteResponseNoUnknownStatus < TRoute [ 'responses' ] > > >
168+ : (
169+ args : Prettify < DataReturnArgs < TRoute , TClientArgs > >
170+ ) => Promise <
171+ Prettify < ApiRouteResponseNoUnknownStatus < TRoute [ 'responses' ] > >
172+ > ;
173+
143174export interface ClientArgs {
144175 baseUrl : string ;
145176 baseHeaders : Record < string , string > ;
@@ -293,8 +324,9 @@ export const getCompleteUrl = (
293324
294325export const getRouteQuery = < TAppRoute extends AppRoute > (
295326 route : TAppRoute ,
296- clientArgs : ClientArgs
327+ clientArgs : InitClientArgs
297328) => {
329+ const knownResponseStatuses = Object . keys ( route . responses ) ;
298330 return async ( inputArgs ?: DataReturnArgsBase < any , ClientArgs > ) => {
299331 const { query, params, body, headers, extraHeaders, ...extraInputArgs } =
300332 inputArgs || { } ;
@@ -307,7 +339,7 @@ export const getRouteQuery = <TAppRoute extends AppRoute>(
307339 ! ! clientArgs . jsonQuery
308340 ) ;
309341
310- return await fetchApi ( {
342+ const response = await fetchApi ( {
311343 path : completeUrl ,
312344 clientArgs,
313345 route,
@@ -319,6 +351,15 @@ export const getRouteQuery = <TAppRoute extends AppRoute>(
319351 ...headers ,
320352 } ,
321353 } ) ;
354+
355+ if ( ! clientArgs . throwOnUnknownStatus ) {
356+ return response ;
357+ }
358+
359+ if ( knownResponseStatuses . includes ( response . status . toString ( ) ) ) {
360+ return response ;
361+ }
362+ throw new UnknownStatusError ( response , knownResponseStatuses ) ;
322363 } ;
323364} ;
324365
@@ -327,10 +368,28 @@ export type InitClientReturn<
327368 TClientArgs extends ClientArgs
328369> = RecursiveProxyObj < T , TClientArgs > ;
329370
330- export const initClient = < T extends AppRouter , TClientArgs extends ClientArgs > (
371+ export type InitClientReturnNoUnknownStatus <
372+ T extends AppRouter ,
373+ TClientArgs extends ClientArgs & { throwOnUnknownStatus : true }
374+ > = RecursiveProxyObjNoUnknownStatus < T , TClientArgs > ;
375+
376+ export type InitClientArgs = ClientArgs & {
377+ /**
378+ * Ensures that the responses from the server match those defined in the
379+ * contract.
380+ */
381+ throwOnUnknownStatus ?: boolean ;
382+ } ;
383+
384+ export const initClient = <
385+ T extends AppRouter ,
386+ TClientArgs extends InitClientArgs
387+ > (
331388 router : T ,
332389 args : TClientArgs
333- ) : InitClientReturn < T , TClientArgs > => {
390+ ) : TClientArgs extends { throwOnUnknownStatus : true }
391+ ? InitClientReturnNoUnknownStatus < T , TClientArgs >
392+ : InitClientReturn < T , TClientArgs > => {
334393 return Object . fromEntries (
335394 Object . entries ( router ) . map ( ( [ key , subRouter ] ) => {
336395 if ( isAppRoute ( subRouter ) ) {
0 commit comments