@sapporta/rest-core 3.52.1 → 3.52.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +75 -10
- package/index.cjs.d.ts +1 -0
- package/index.cjs.default.js +1 -0
- package/index.cjs.js +807 -0
- package/index.cjs.mjs +2 -0
- package/index.esm.js +762 -0
- package/package.json +13 -3
- package/src/lib/client.d.ts +107 -0
- package/src/lib/dsl.d.ts +222 -0
- package/src/lib/infer-types.d.ts +78 -0
- package/src/lib/paths.d.ts +30 -0
- package/src/lib/query.d.ts +17 -0
- package/src/lib/response-error.d.ts +20 -0
- package/src/lib/response-validation-error.d.ts +14 -0
- package/src/lib/server.d.ts +18 -0
- package/src/lib/standard-schema-utils.d.ts +68 -0
- package/src/lib/standard-schema.d.ts +55 -0
- package/src/lib/status-codes.d.ts +6 -0
- package/src/lib/{test-helpers.ts → test-helpers.d.ts} +1 -6
- package/src/lib/type-guards.d.ts +12 -0
- package/src/lib/type-utils.d.ts +96 -0
- package/src/lib/unknown-status-error.d.ts +10 -0
- package/src/lib/validation-error.d.ts +11 -0
- package/.babelrc +0 -10
- package/.eslintrc.json +0 -21
- package/LICENCE +0 -21
- package/jest.config.ts +0 -16
- package/project.json +0 -51
- package/src/lib/client.spec.ts +0 -1330
- package/src/lib/client.ts +0 -481
- package/src/lib/dsl.spec.ts +0 -1308
- package/src/lib/dsl.ts +0 -472
- package/src/lib/fetch.spec.ts +0 -102
- package/src/lib/infer-types.spec.ts +0 -935
- package/src/lib/infer-types.ts +0 -282
- package/src/lib/paths.spec.ts +0 -138
- package/src/lib/paths.ts +0 -61
- package/src/lib/query.spec.ts +0 -329
- package/src/lib/query.ts +0 -114
- package/src/lib/response-error.spec.ts +0 -67
- package/src/lib/response-error.ts +0 -61
- package/src/lib/response-validation-error.ts +0 -24
- package/src/lib/server.spec.ts +0 -163
- package/src/lib/server.ts +0 -83
- package/src/lib/standard-schema-utils.spec.ts +0 -218
- package/src/lib/standard-schema-utils.ts +0 -280
- package/src/lib/standard-schema.ts +0 -71
- package/src/lib/status-codes.ts +0 -75
- package/src/lib/type-guards.spec.ts +0 -355
- package/src/lib/type-guards.ts +0 -99
- package/src/lib/type-utils.spec.ts +0 -59
- package/src/lib/type-utils.ts +0 -234
- package/src/lib/unknown-status-error.ts +0 -15
- package/src/lib/validation-error.ts +0 -36
- package/tsconfig.json +0 -22
- package/tsconfig.lib.json +0 -10
- package/tsconfig.spec.json +0 -9
- package/typedoc.json +0 -5
- /package/src/{index.ts → index.d.ts} +0 -0
package/src/lib/client.ts
DELETED
|
@@ -1,481 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AppRoute,
|
|
3
|
-
AppRouteMutation,
|
|
4
|
-
AppRouter,
|
|
5
|
-
ContractNoBody,
|
|
6
|
-
isAppRoute,
|
|
7
|
-
} from './dsl';
|
|
8
|
-
import { insertParamsIntoPath } from './paths';
|
|
9
|
-
import { convertQueryParamsToUrlString } from './query';
|
|
10
|
-
import { AreAllPropertiesOptional, Prettify } from './type-utils';
|
|
11
|
-
import { UnknownStatusError } from './unknown-status-error';
|
|
12
|
-
import {
|
|
13
|
-
ClientInferRequest,
|
|
14
|
-
ClientInferResponses,
|
|
15
|
-
PartialClientInferRequest,
|
|
16
|
-
} from './infer-types';
|
|
17
|
-
import { Equal, Expect } from './test-helpers';
|
|
18
|
-
import {
|
|
19
|
-
isStandardSchema,
|
|
20
|
-
parseAsStandardSchema,
|
|
21
|
-
validateAgainstStandardSchema,
|
|
22
|
-
} from './standard-schema-utils';
|
|
23
|
-
|
|
24
|
-
type RecursiveProxyObj<T extends AppRouter, TClientArgs extends ClientArgs> = {
|
|
25
|
-
[TKey in keyof T]: T[TKey] extends AppRoute
|
|
26
|
-
? AppRouteFunction<T[TKey], TClientArgs>
|
|
27
|
-
: T[TKey] extends AppRouter
|
|
28
|
-
? RecursiveProxyObj<T[TKey], TClientArgs>
|
|
29
|
-
: never;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @deprecated Only safe to use on the client-side. Use `ServerInferResponses`/`ClientInferResponses` instead.
|
|
34
|
-
*/
|
|
35
|
-
export type ApiResponseForRoute<T extends AppRoute> = ClientInferResponses<T>;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @deprecated Only safe to use on the client-side. Use `ServerInferResponses`/`ClientInferResponses` instead.
|
|
39
|
-
*/
|
|
40
|
-
export function getRouteResponses<T extends AppRouter>(router: T) {
|
|
41
|
-
return {} as ClientInferResponses<T>;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Returned from a mutation or query call
|
|
46
|
-
*/
|
|
47
|
-
export type AppRouteFunction<
|
|
48
|
-
TRoute extends AppRoute,
|
|
49
|
-
TClientArgs extends ClientArgs,
|
|
50
|
-
TArgs = PartialClientInferRequest<TRoute, TClientArgs>,
|
|
51
|
-
> = AreAllPropertiesOptional<TArgs> extends true
|
|
52
|
-
? (args?: Prettify<TArgs>) => Promise<Prettify<ClientInferResponses<TRoute>>>
|
|
53
|
-
: (args: Prettify<TArgs>) => Promise<Prettify<ClientInferResponses<TRoute>>>;
|
|
54
|
-
|
|
55
|
-
export type FetchOptions = typeof globalThis extends {
|
|
56
|
-
Request: infer T extends typeof Request;
|
|
57
|
-
}
|
|
58
|
-
? Omit<
|
|
59
|
-
NonNullable<ConstructorParameters<T>[1]>,
|
|
60
|
-
'method' | 'headers' | 'body'
|
|
61
|
-
>
|
|
62
|
-
: never;
|
|
63
|
-
|
|
64
|
-
export interface OverridableClientArgs {
|
|
65
|
-
baseUrl: string;
|
|
66
|
-
credentials?: FetchOptions['credentials'];
|
|
67
|
-
jsonQuery?: boolean;
|
|
68
|
-
validateResponse?: boolean;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface ClientArgs extends OverridableClientArgs {
|
|
72
|
-
baseHeaders?: Record<string, string | ((options: FetchApiOptions) => string)>;
|
|
73
|
-
api?: ApiFetcher;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export type ApiFetcherArgs<TFetchOptions extends FetchOptions = FetchOptions> =
|
|
77
|
-
{
|
|
78
|
-
route: AppRoute;
|
|
79
|
-
path: string;
|
|
80
|
-
method: string;
|
|
81
|
-
headers: Record<string, string>;
|
|
82
|
-
body: FormData | URLSearchParams | string | null | undefined;
|
|
83
|
-
rawBody: unknown;
|
|
84
|
-
rawQuery: unknown;
|
|
85
|
-
contentType: AppRouteMutation['contentType'];
|
|
86
|
-
fetchOptions?: FetchOptions;
|
|
87
|
-
validateResponse?: boolean;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* @deprecated Use `fetchOptions.credentials` instead
|
|
91
|
-
*/
|
|
92
|
-
credentials?: TFetchOptions['credentials'];
|
|
93
|
-
/**
|
|
94
|
-
* @deprecated Use `fetchOptions.signal` instead
|
|
95
|
-
*/
|
|
96
|
-
signal?: TFetchOptions['signal'];
|
|
97
|
-
/**
|
|
98
|
-
* @deprecated Use `fetchOptions.cache` instead
|
|
99
|
-
*/
|
|
100
|
-
cache?: 'cache' extends keyof TFetchOptions
|
|
101
|
-
? TFetchOptions['cache']
|
|
102
|
-
: never;
|
|
103
|
-
/**
|
|
104
|
-
* @deprecated Use `fetchOptions.next` instead
|
|
105
|
-
*/
|
|
106
|
-
next?: 'next' extends keyof TFetchOptions ? TFetchOptions['next'] : never;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
export type ApiFetcher = (args: ApiFetcherArgs) => Promise<{
|
|
110
|
-
status: number;
|
|
111
|
-
body: unknown;
|
|
112
|
-
headers: Headers;
|
|
113
|
-
}>;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Default fetch api implementation:
|
|
117
|
-
*
|
|
118
|
-
* Can be used as a reference for implementing your own fetcher,
|
|
119
|
-
* or used in the "api" field of ClientArgs to allow you to hook
|
|
120
|
-
* into the request to run custom logic
|
|
121
|
-
*/
|
|
122
|
-
export const tsRestFetchApi: ApiFetcher = async ({
|
|
123
|
-
route,
|
|
124
|
-
path,
|
|
125
|
-
method,
|
|
126
|
-
headers,
|
|
127
|
-
body,
|
|
128
|
-
validateResponse,
|
|
129
|
-
fetchOptions,
|
|
130
|
-
}) => {
|
|
131
|
-
const result = await fetch(path, {
|
|
132
|
-
...fetchOptions,
|
|
133
|
-
method,
|
|
134
|
-
headers,
|
|
135
|
-
body,
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
const contentType = result.headers.get('content-type');
|
|
139
|
-
|
|
140
|
-
if (contentType?.includes('application/') && contentType?.includes('json')) {
|
|
141
|
-
const responseSchema = route.responses[result.status];
|
|
142
|
-
|
|
143
|
-
const response = {
|
|
144
|
-
status: result.status,
|
|
145
|
-
body: responseSchema === ContractNoBody ? undefined : await result.json(),
|
|
146
|
-
headers: result.headers,
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const responseSchemaStandard = parseAsStandardSchema(responseSchema);
|
|
150
|
-
|
|
151
|
-
if (
|
|
152
|
-
responseSchemaStandard &&
|
|
153
|
-
(validateResponse ?? route.validateResponseOnClient)
|
|
154
|
-
) {
|
|
155
|
-
const result = validateAgainstStandardSchema(
|
|
156
|
-
response.body,
|
|
157
|
-
responseSchemaStandard,
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
if (result.error) {
|
|
161
|
-
throw result.error;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return {
|
|
165
|
-
...response,
|
|
166
|
-
body: result.value,
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return response;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (contentType?.includes('text/')) {
|
|
174
|
-
return {
|
|
175
|
-
status: result.status,
|
|
176
|
-
body: await result.text(),
|
|
177
|
-
headers: result.headers,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
status: result.status,
|
|
183
|
-
body: await result.blob(),
|
|
184
|
-
headers: result.headers,
|
|
185
|
-
};
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const createFormData = (body: unknown) => {
|
|
189
|
-
const formData = new FormData();
|
|
190
|
-
|
|
191
|
-
const appendToFormData = (key: string, value: unknown) => {
|
|
192
|
-
if (value instanceof File) {
|
|
193
|
-
formData.append(key, value);
|
|
194
|
-
} else {
|
|
195
|
-
formData.append(key, JSON.stringify(value));
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
Object.entries(body as Record<string, unknown>).forEach(([key, value]) => {
|
|
200
|
-
if (Array.isArray(value)) {
|
|
201
|
-
for (const item of value) {
|
|
202
|
-
appendToFormData(key, item);
|
|
203
|
-
}
|
|
204
|
-
} else {
|
|
205
|
-
appendToFormData(key, value);
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
return formData;
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const normalizeHeaders = (headers: Record<string, string | undefined>) => {
|
|
213
|
-
return Object.fromEntries(
|
|
214
|
-
Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v]),
|
|
215
|
-
);
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
export type FetchApiOptions = {
|
|
219
|
-
path: string;
|
|
220
|
-
clientArgs: ClientArgs;
|
|
221
|
-
route: AppRoute;
|
|
222
|
-
query: unknown;
|
|
223
|
-
body: unknown;
|
|
224
|
-
extraInputArgs: Record<string, unknown>;
|
|
225
|
-
headers: Record<string, string | undefined>;
|
|
226
|
-
fetchOptions?: FetchOptions;
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
export const fetchApi = (options: FetchApiOptions) => {
|
|
230
|
-
const {
|
|
231
|
-
path,
|
|
232
|
-
clientArgs,
|
|
233
|
-
route,
|
|
234
|
-
body,
|
|
235
|
-
query,
|
|
236
|
-
extraInputArgs,
|
|
237
|
-
headers,
|
|
238
|
-
fetchOptions,
|
|
239
|
-
} = options;
|
|
240
|
-
const apiFetcher = clientArgs.api || tsRestFetchApi;
|
|
241
|
-
|
|
242
|
-
const baseHeaders =
|
|
243
|
-
clientArgs.baseHeaders &&
|
|
244
|
-
Object.fromEntries(
|
|
245
|
-
Object.entries(clientArgs.baseHeaders).map(([name, valueOrFunction]) => {
|
|
246
|
-
if (typeof valueOrFunction === 'function') {
|
|
247
|
-
return [name, valueOrFunction(options)];
|
|
248
|
-
} else {
|
|
249
|
-
return [name, valueOrFunction];
|
|
250
|
-
}
|
|
251
|
-
}),
|
|
252
|
-
);
|
|
253
|
-
|
|
254
|
-
const combinedHeaders = {
|
|
255
|
-
...(baseHeaders && normalizeHeaders(baseHeaders)),
|
|
256
|
-
...normalizeHeaders(headers),
|
|
257
|
-
} as Record<string, string>;
|
|
258
|
-
|
|
259
|
-
// Remove any headers that are set to undefined
|
|
260
|
-
Object.keys(combinedHeaders).forEach((key) => {
|
|
261
|
-
if (combinedHeaders[key] === undefined) {
|
|
262
|
-
delete combinedHeaders[key];
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
let fetcherArgs: ApiFetcherArgs = {
|
|
267
|
-
route,
|
|
268
|
-
path,
|
|
269
|
-
method: route.method,
|
|
270
|
-
headers: combinedHeaders,
|
|
271
|
-
body: undefined,
|
|
272
|
-
rawBody: body,
|
|
273
|
-
rawQuery: query,
|
|
274
|
-
contentType: undefined,
|
|
275
|
-
validateResponse: clientArgs.validateResponse,
|
|
276
|
-
fetchOptions: {
|
|
277
|
-
...(clientArgs.credentials && { credentials: clientArgs.credentials }),
|
|
278
|
-
...fetchOptions,
|
|
279
|
-
},
|
|
280
|
-
...(fetchOptions?.signal && { signal: fetchOptions.signal }),
|
|
281
|
-
...(fetchOptions?.cache && { cache: fetchOptions.cache }),
|
|
282
|
-
...(fetchOptions &&
|
|
283
|
-
'next' in fetchOptions &&
|
|
284
|
-
!!fetchOptions?.next && { next: fetchOptions.next as any }),
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
if (route.method !== 'GET') {
|
|
288
|
-
if ('contentType' in route && route.contentType === 'multipart/form-data') {
|
|
289
|
-
fetcherArgs = {
|
|
290
|
-
...fetcherArgs,
|
|
291
|
-
contentType: 'multipart/form-data',
|
|
292
|
-
body: body instanceof FormData ? body : createFormData(body),
|
|
293
|
-
};
|
|
294
|
-
} else if (
|
|
295
|
-
'contentType' in route &&
|
|
296
|
-
route.contentType === 'application/x-www-form-urlencoded'
|
|
297
|
-
) {
|
|
298
|
-
fetcherArgs = {
|
|
299
|
-
...fetcherArgs,
|
|
300
|
-
contentType: 'application/x-www-form-urlencoded',
|
|
301
|
-
headers: {
|
|
302
|
-
'content-type': 'application/x-www-form-urlencoded',
|
|
303
|
-
...fetcherArgs.headers,
|
|
304
|
-
},
|
|
305
|
-
body:
|
|
306
|
-
typeof body === 'string'
|
|
307
|
-
? body
|
|
308
|
-
: new URLSearchParams(
|
|
309
|
-
body as Record<string, string> | URLSearchParams,
|
|
310
|
-
),
|
|
311
|
-
};
|
|
312
|
-
} else if (body !== null && body !== undefined) {
|
|
313
|
-
fetcherArgs = {
|
|
314
|
-
...fetcherArgs,
|
|
315
|
-
contentType: 'application/json',
|
|
316
|
-
headers: {
|
|
317
|
-
'content-type': 'application/json',
|
|
318
|
-
...fetcherArgs.headers,
|
|
319
|
-
},
|
|
320
|
-
body: JSON.stringify(body),
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return apiFetcher({
|
|
326
|
-
...fetcherArgs,
|
|
327
|
-
...extraInputArgs,
|
|
328
|
-
});
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
export const evaluateFetchApiArgs = <TAppRoute extends AppRoute>(
|
|
332
|
-
route: TAppRoute,
|
|
333
|
-
clientArgs: InitClientArgs,
|
|
334
|
-
inputArgs?: ClientInferRequest<AppRouteMutation, ClientArgs>,
|
|
335
|
-
) => {
|
|
336
|
-
const {
|
|
337
|
-
query,
|
|
338
|
-
params,
|
|
339
|
-
body,
|
|
340
|
-
headers,
|
|
341
|
-
extraHeaders,
|
|
342
|
-
overrideClientOptions,
|
|
343
|
-
fetchOptions,
|
|
344
|
-
|
|
345
|
-
// TODO: remove in 4.0
|
|
346
|
-
cache,
|
|
347
|
-
|
|
348
|
-
// TODO: remove in 4.0
|
|
349
|
-
next,
|
|
350
|
-
|
|
351
|
-
// extra input args
|
|
352
|
-
...extraInputArgs
|
|
353
|
-
} =
|
|
354
|
-
(inputArgs as ClientInferRequest<AppRouteMutation, ClientArgs> & {
|
|
355
|
-
next?: any;
|
|
356
|
-
}) || {};
|
|
357
|
-
|
|
358
|
-
// assert that we removed all non-extra args
|
|
359
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
360
|
-
type AssertExtraInputArgsEmpty = Expect<Equal<typeof extraInputArgs, {}>>;
|
|
361
|
-
|
|
362
|
-
const overriddenClientArgs = {
|
|
363
|
-
...clientArgs,
|
|
364
|
-
...overrideClientOptions,
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Coerce params to strings, allowing for numbers to be passed in (e.g. from using z.coerce.number())
|
|
369
|
-
*/
|
|
370
|
-
const parsedParams =
|
|
371
|
-
typeof params === 'object'
|
|
372
|
-
? Object.fromEntries(
|
|
373
|
-
Object.entries(params).map(([key, value]) => [key, String(value)]),
|
|
374
|
-
)
|
|
375
|
-
: {};
|
|
376
|
-
|
|
377
|
-
const completeUrl = getCompleteUrl(
|
|
378
|
-
query,
|
|
379
|
-
overriddenClientArgs.baseUrl,
|
|
380
|
-
parsedParams,
|
|
381
|
-
route,
|
|
382
|
-
!!overriddenClientArgs.jsonQuery,
|
|
383
|
-
);
|
|
384
|
-
|
|
385
|
-
return {
|
|
386
|
-
path: completeUrl,
|
|
387
|
-
clientArgs: overriddenClientArgs,
|
|
388
|
-
route,
|
|
389
|
-
body,
|
|
390
|
-
query,
|
|
391
|
-
extraInputArgs,
|
|
392
|
-
fetchOptions: {
|
|
393
|
-
...(cache && { cache }),
|
|
394
|
-
...(next && { next }),
|
|
395
|
-
...fetchOptions,
|
|
396
|
-
},
|
|
397
|
-
headers: {
|
|
398
|
-
...extraHeaders,
|
|
399
|
-
...headers,
|
|
400
|
-
},
|
|
401
|
-
} as Parameters<typeof fetchApi>[0];
|
|
402
|
-
};
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* @hidden
|
|
406
|
-
*/
|
|
407
|
-
export const getCompleteUrl = (
|
|
408
|
-
query: unknown,
|
|
409
|
-
baseUrl: string,
|
|
410
|
-
params: Record<string, string>,
|
|
411
|
-
route: AppRoute,
|
|
412
|
-
jsonQuery: boolean,
|
|
413
|
-
) => {
|
|
414
|
-
const path = insertParamsIntoPath({
|
|
415
|
-
path: route.path,
|
|
416
|
-
params: params,
|
|
417
|
-
});
|
|
418
|
-
const queryComponent = convertQueryParamsToUrlString(query, jsonQuery);
|
|
419
|
-
|
|
420
|
-
if (baseUrl.endsWith('/') && path.startsWith('/')) {
|
|
421
|
-
return `${baseUrl}${path.substring(1)}${queryComponent}`;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
return `${baseUrl}${path}${queryComponent}`;
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
export const getRouteQuery = <TAppRoute extends AppRoute>(
|
|
428
|
-
route: TAppRoute,
|
|
429
|
-
clientArgs: InitClientArgs,
|
|
430
|
-
) => {
|
|
431
|
-
const knownResponseStatuses = Object.keys(route.responses);
|
|
432
|
-
return async (
|
|
433
|
-
inputArgs?: ClientInferRequest<AppRouteMutation, ClientArgs>,
|
|
434
|
-
) => {
|
|
435
|
-
const fetchApiArgs = evaluateFetchApiArgs(route, clientArgs, inputArgs);
|
|
436
|
-
const response = await fetchApi(fetchApiArgs);
|
|
437
|
-
|
|
438
|
-
// TODO: in next major version, throw by default if `strictStatusCode` is enabled
|
|
439
|
-
if (!clientArgs.throwOnUnknownStatus) {
|
|
440
|
-
return response;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (knownResponseStatuses.includes(response.status.toString())) {
|
|
444
|
-
return response;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
throw new UnknownStatusError(response, knownResponseStatuses);
|
|
448
|
-
};
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
export type InitClientReturn<
|
|
452
|
-
T extends AppRouter,
|
|
453
|
-
TClientArgs extends ClientArgs,
|
|
454
|
-
> = RecursiveProxyObj<T, TClientArgs>;
|
|
455
|
-
|
|
456
|
-
// TODO: in next major version, turn on by default if `strictStatusCode` is enabled and remove `throwOnUnknownStatus`
|
|
457
|
-
export type InitClientArgs = ClientArgs & {
|
|
458
|
-
/**
|
|
459
|
-
* Ensures that the responses from the server match those defined in the
|
|
460
|
-
* contract.
|
|
461
|
-
*/
|
|
462
|
-
throwOnUnknownStatus?: boolean;
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
export const initClient = <
|
|
466
|
-
T extends AppRouter,
|
|
467
|
-
TClientArgs extends InitClientArgs,
|
|
468
|
-
>(
|
|
469
|
-
router: T,
|
|
470
|
-
args: TClientArgs,
|
|
471
|
-
): InitClientReturn<T, TClientArgs> => {
|
|
472
|
-
return Object.fromEntries(
|
|
473
|
-
Object.entries(router).map(([key, subRouter]) => {
|
|
474
|
-
if (isAppRoute(subRouter)) {
|
|
475
|
-
return [key, getRouteQuery(subRouter, args)];
|
|
476
|
-
} else {
|
|
477
|
-
return [key, initClient(subRouter, args)];
|
|
478
|
-
}
|
|
479
|
-
}),
|
|
480
|
-
);
|
|
481
|
-
};
|