@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.
Files changed (60) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +75 -10
  3. package/index.cjs.d.ts +1 -0
  4. package/index.cjs.default.js +1 -0
  5. package/index.cjs.js +807 -0
  6. package/index.cjs.mjs +2 -0
  7. package/index.esm.js +762 -0
  8. package/package.json +13 -3
  9. package/src/lib/client.d.ts +107 -0
  10. package/src/lib/dsl.d.ts +222 -0
  11. package/src/lib/infer-types.d.ts +78 -0
  12. package/src/lib/paths.d.ts +30 -0
  13. package/src/lib/query.d.ts +17 -0
  14. package/src/lib/response-error.d.ts +20 -0
  15. package/src/lib/response-validation-error.d.ts +14 -0
  16. package/src/lib/server.d.ts +18 -0
  17. package/src/lib/standard-schema-utils.d.ts +68 -0
  18. package/src/lib/standard-schema.d.ts +55 -0
  19. package/src/lib/status-codes.d.ts +6 -0
  20. package/src/lib/{test-helpers.ts → test-helpers.d.ts} +1 -6
  21. package/src/lib/type-guards.d.ts +12 -0
  22. package/src/lib/type-utils.d.ts +96 -0
  23. package/src/lib/unknown-status-error.d.ts +10 -0
  24. package/src/lib/validation-error.d.ts +11 -0
  25. package/.babelrc +0 -10
  26. package/.eslintrc.json +0 -21
  27. package/LICENCE +0 -21
  28. package/jest.config.ts +0 -16
  29. package/project.json +0 -51
  30. package/src/lib/client.spec.ts +0 -1330
  31. package/src/lib/client.ts +0 -481
  32. package/src/lib/dsl.spec.ts +0 -1308
  33. package/src/lib/dsl.ts +0 -472
  34. package/src/lib/fetch.spec.ts +0 -102
  35. package/src/lib/infer-types.spec.ts +0 -935
  36. package/src/lib/infer-types.ts +0 -282
  37. package/src/lib/paths.spec.ts +0 -138
  38. package/src/lib/paths.ts +0 -61
  39. package/src/lib/query.spec.ts +0 -329
  40. package/src/lib/query.ts +0 -114
  41. package/src/lib/response-error.spec.ts +0 -67
  42. package/src/lib/response-error.ts +0 -61
  43. package/src/lib/response-validation-error.ts +0 -24
  44. package/src/lib/server.spec.ts +0 -163
  45. package/src/lib/server.ts +0 -83
  46. package/src/lib/standard-schema-utils.spec.ts +0 -218
  47. package/src/lib/standard-schema-utils.ts +0 -280
  48. package/src/lib/standard-schema.ts +0 -71
  49. package/src/lib/status-codes.ts +0 -75
  50. package/src/lib/type-guards.spec.ts +0 -355
  51. package/src/lib/type-guards.ts +0 -99
  52. package/src/lib/type-utils.spec.ts +0 -59
  53. package/src/lib/type-utils.ts +0 -234
  54. package/src/lib/unknown-status-error.ts +0 -15
  55. package/src/lib/validation-error.ts +0 -36
  56. package/tsconfig.json +0 -22
  57. package/tsconfig.lib.json +0 -10
  58. package/tsconfig.spec.json +0 -9
  59. package/typedoc.json +0 -5
  60. /package/src/{index.ts → index.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sapporta/rest-core",
3
- "version": "3.52.1",
3
+ "version": "3.52.2",
4
4
  "private": false,
5
5
  "description": "Sapporta-maintained ts-rest core fork for Zod 4 contracts and inference",
6
6
  "keywords": [
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "repository": {
16
16
  "type": "git",
17
- "url": "https://github.com/sapporta/sapporta-rest.git",
17
+ "url": "https://github.com/jasim/sapporta-rest.git",
18
18
  "directory": "libs/ts-rest/core"
19
19
  },
20
20
  "license": "MIT",
@@ -29,5 +29,15 @@
29
29
  },
30
30
  "devDependencies": {
31
31
  "zod": "^4.0.0"
32
- }
32
+ },
33
+ "exports": {
34
+ "./package.json": "./package.json",
35
+ ".": {
36
+ "module": "./index.esm.js",
37
+ "import": "./index.cjs.mjs",
38
+ "default": "./index.cjs.js"
39
+ }
40
+ },
41
+ "module": "./index.esm.js",
42
+ "main": "./index.cjs.js"
33
43
  }
@@ -0,0 +1,107 @@
1
+ import { AppRoute, AppRouteMutation, AppRouter } from './dsl';
2
+ import { AreAllPropertiesOptional, Prettify } from './type-utils';
3
+ import { ClientInferRequest, ClientInferResponses, PartialClientInferRequest } from './infer-types';
4
+ type RecursiveProxyObj<T extends AppRouter, TClientArgs extends ClientArgs> = {
5
+ [TKey in keyof T]: T[TKey] extends AppRoute ? AppRouteFunction<T[TKey], TClientArgs> : T[TKey] extends AppRouter ? RecursiveProxyObj<T[TKey], TClientArgs> : never;
6
+ };
7
+ /**
8
+ * @deprecated Only safe to use on the client-side. Use `ServerInferResponses`/`ClientInferResponses` instead.
9
+ */
10
+ export type ApiResponseForRoute<T extends AppRoute> = ClientInferResponses<T>;
11
+ /**
12
+ * @deprecated Only safe to use on the client-side. Use `ServerInferResponses`/`ClientInferResponses` instead.
13
+ */
14
+ export declare function getRouteResponses<T extends AppRouter>(router: T): ClientInferResponses<T>;
15
+ /**
16
+ * Returned from a mutation or query call
17
+ */
18
+ export type AppRouteFunction<TRoute extends AppRoute, TClientArgs extends ClientArgs, TArgs = PartialClientInferRequest<TRoute, TClientArgs>> = AreAllPropertiesOptional<TArgs> extends true ? (args?: Prettify<TArgs>) => Promise<Prettify<ClientInferResponses<TRoute>>> : (args: Prettify<TArgs>) => Promise<Prettify<ClientInferResponses<TRoute>>>;
19
+ export type FetchOptions = typeof globalThis extends {
20
+ Request: infer T extends typeof Request;
21
+ } ? Omit<NonNullable<ConstructorParameters<T>[1]>, 'method' | 'headers' | 'body'> : never;
22
+ export interface OverridableClientArgs {
23
+ baseUrl: string;
24
+ credentials?: FetchOptions['credentials'];
25
+ jsonQuery?: boolean;
26
+ validateResponse?: boolean;
27
+ }
28
+ export interface ClientArgs extends OverridableClientArgs {
29
+ baseHeaders?: Record<string, string | ((options: FetchApiOptions) => string)>;
30
+ api?: ApiFetcher;
31
+ }
32
+ export type ApiFetcherArgs<TFetchOptions extends FetchOptions = FetchOptions> = {
33
+ route: AppRoute;
34
+ path: string;
35
+ method: string;
36
+ headers: Record<string, string>;
37
+ body: FormData | URLSearchParams | string | null | undefined;
38
+ rawBody: unknown;
39
+ rawQuery: unknown;
40
+ contentType: AppRouteMutation['contentType'];
41
+ fetchOptions?: FetchOptions;
42
+ validateResponse?: boolean;
43
+ /**
44
+ * @deprecated Use `fetchOptions.credentials` instead
45
+ */
46
+ credentials?: TFetchOptions['credentials'];
47
+ /**
48
+ * @deprecated Use `fetchOptions.signal` instead
49
+ */
50
+ signal?: TFetchOptions['signal'];
51
+ /**
52
+ * @deprecated Use `fetchOptions.cache` instead
53
+ */
54
+ cache?: 'cache' extends keyof TFetchOptions ? TFetchOptions['cache'] : never;
55
+ /**
56
+ * @deprecated Use `fetchOptions.next` instead
57
+ */
58
+ next?: 'next' extends keyof TFetchOptions ? TFetchOptions['next'] : never;
59
+ };
60
+ export type ApiFetcher = (args: ApiFetcherArgs) => Promise<{
61
+ status: number;
62
+ body: unknown;
63
+ headers: Headers;
64
+ }>;
65
+ /**
66
+ * Default fetch api implementation:
67
+ *
68
+ * Can be used as a reference for implementing your own fetcher,
69
+ * or used in the "api" field of ClientArgs to allow you to hook
70
+ * into the request to run custom logic
71
+ */
72
+ export declare const tsRestFetchApi: ApiFetcher;
73
+ export type FetchApiOptions = {
74
+ path: string;
75
+ clientArgs: ClientArgs;
76
+ route: AppRoute;
77
+ query: unknown;
78
+ body: unknown;
79
+ extraInputArgs: Record<string, unknown>;
80
+ headers: Record<string, string | undefined>;
81
+ fetchOptions?: FetchOptions;
82
+ };
83
+ export declare const fetchApi: (options: FetchApiOptions) => Promise<{
84
+ status: number;
85
+ body: unknown;
86
+ headers: Headers;
87
+ }>;
88
+ export declare const evaluateFetchApiArgs: <TAppRoute extends AppRoute>(route: TAppRoute, clientArgs: InitClientArgs, inputArgs?: ClientInferRequest<AppRouteMutation, ClientArgs>) => FetchApiOptions;
89
+ /**
90
+ * @hidden
91
+ */
92
+ export declare const getCompleteUrl: (query: unknown, baseUrl: string, params: Record<string, string>, route: AppRoute, jsonQuery: boolean) => string;
93
+ export declare const getRouteQuery: <TAppRoute extends AppRoute>(route: TAppRoute, clientArgs: InitClientArgs) => (inputArgs?: ClientInferRequest<AppRouteMutation, ClientArgs>) => Promise<{
94
+ status: number;
95
+ body: unknown;
96
+ headers: Headers;
97
+ }>;
98
+ export type InitClientReturn<T extends AppRouter, TClientArgs extends ClientArgs> = RecursiveProxyObj<T, TClientArgs>;
99
+ export type InitClientArgs = ClientArgs & {
100
+ /**
101
+ * Ensures that the responses from the server match those defined in the
102
+ * contract.
103
+ */
104
+ throwOnUnknownStatus?: boolean;
105
+ };
106
+ export declare const initClient: <T extends AppRouter, TClientArgs extends InitClientArgs>(router: T, args: TClientArgs) => RecursiveProxyObj<T, TClientArgs>;
107
+ export {};
@@ -0,0 +1,222 @@
1
+ import { StandardSchemaV1 } from './standard-schema';
2
+ import { LowercaseKeys, Merge, Opaque, Prettify, SchemaInputOrType, SchemaOutputOrType, WithoutUnknown } from './type-utils';
3
+ type MixedSchemaError<A, B> = Opaque<{
4
+ a: A;
5
+ b: B;
6
+ }, 'MixedSchemaError'>;
7
+ /**
8
+ * The path with colon-prefixed parameters
9
+ * e.g. "/posts/:id".
10
+ */
11
+ type Path = string;
12
+ declare const NullSymbol: unique symbol;
13
+ export declare const ContractNoBody: unique symbol;
14
+ export type ContractPlainType<T> = Opaque<T, 'ContractPlainType'>;
15
+ export type ContractNullType = Opaque<typeof NullSymbol, 'ContractNullType'>;
16
+ export type ContractNoBodyType = typeof ContractNoBody;
17
+ export type ContractAnyType = StandardSchemaV1<any> | ContractPlainType<unknown> | ContractNullType | null;
18
+ export type ContractOtherResponse<T extends ContractAnyType> = Opaque<{
19
+ contentType: string;
20
+ body: T;
21
+ }, 'ContractOtherResponse'>;
22
+ export type AppRouteResponse = ContractAnyType | ContractNoBodyType | ContractOtherResponse<ContractAnyType>;
23
+ type AppRouteCommon = {
24
+ path: Path;
25
+ pathParams?: ContractAnyType;
26
+ query?: ContractAnyType;
27
+ headers?: Record<string, ContractAnyType>;
28
+ summary?: string;
29
+ description?: string;
30
+ deprecated?: boolean;
31
+ responses: Record<number, AppRouteResponse>;
32
+ strictStatusCodes?: boolean;
33
+ metadata?: unknown;
34
+ /**
35
+ * @deprecated Use `validateResponse` on the client options
36
+ */
37
+ validateResponseOnClient?: boolean;
38
+ };
39
+ /**
40
+ * A query endpoint. In REST terms, one using GET.
41
+ */
42
+ export type AppRouteQuery = AppRouteCommon & {
43
+ method: 'GET';
44
+ };
45
+ /**
46
+ * A mutation endpoint. In REST terms, one using POST, PUT,
47
+ * PATCH, or DELETE.
48
+ */
49
+ export type AppRouteMutation = AppRouteCommon & {
50
+ method: 'POST' | 'DELETE' | 'PUT' | 'PATCH';
51
+ contentType?: 'application/json' | 'multipart/form-data' | 'application/x-www-form-urlencoded';
52
+ body: ContractAnyType | ContractNoBodyType;
53
+ };
54
+ /**
55
+ * A mutation endpoint. In REST terms, one using POST, PUT,
56
+ * PATCH, or DELETE.
57
+ */
58
+ export type AppRouteDeleteNoBody = AppRouteCommon & {
59
+ method: 'DELETE';
60
+ };
61
+ type ValidatedHeaders<T extends AppRoute, TOptions extends RouterOptions, TOptionsApplied = ApplyOptions<T, TOptions>> = 'headers' extends keyof TOptionsApplied ? TOptionsApplied['headers'] extends MixedSchemaError<infer A, infer B> ? {
62
+ _error: 'Cannot mix plain object types with StandardSchemaV1 objects for headers';
63
+ a: A;
64
+ b: B;
65
+ } : T : T;
66
+ /**
67
+ * Recursively process a router, allowing for you to define nested routers.
68
+ *
69
+ * The main purpose of this is to convert all path strings into string constants so we can infer the path
70
+ */
71
+ type RecursivelyProcessAppRouter<T extends AppRouter, TOptions extends RouterOptions> = {
72
+ [K in keyof T]: T[K] extends AppRoute ? ValidatedHeaders<T[K], TOptions> : T[K] extends AppRouter ? RecursivelyProcessAppRouter<T[K], TOptions> : T[K];
73
+ };
74
+ type RecursivelyApplyOptions<TRouter extends AppRouter, TOptions extends RouterOptions> = {
75
+ [TRouterKey in keyof TRouter]: TRouter[TRouterKey] extends AppRoute ? Prettify<ApplyOptions<TRouter[TRouterKey], TOptions>> : TRouter[TRouterKey] extends AppRouter ? RecursivelyApplyOptions<TRouter[TRouterKey], TOptions> : TRouter[TRouterKey];
76
+ };
77
+ /**
78
+ * Merge headers together
79
+ */
80
+ export type MergeHeaders<A extends AppRouteCommon['headers'], B extends AppRouteCommon['headers']> = [A, B] extends [undefined, undefined] ? unknown : A extends undefined ? B : B extends undefined ? A : A extends Record<string, ContractAnyType> ? B extends Record<string, ContractAnyType> ? MergeObjectBasedHeaders<A, B> : unknown : unknown;
81
+ /**
82
+ * Headers are typed as a Record<string, ContractAnyType>
83
+ *
84
+ * We need to be able to merge together base headers and route headers, this smushes them together taking precedence to route headers
85
+ */
86
+ type MergeObjectBasedHeaders<T extends Record<string, ContractAnyType>, U extends Record<string, ContractAnyType>> = {
87
+ [K in keyof T | keyof U]: K extends keyof U ? U[K] : K extends keyof T ? T[K] : never;
88
+ } extends infer M ? {
89
+ [K in keyof M as M[K] extends null ? never : K]: M[K];
90
+ } : never;
91
+ type IsEmptyObject<T> = keyof T extends never ? {} extends T ? true : false : false;
92
+ /**
93
+ * For a given app route, infer the headers input type
94
+ */
95
+ export type InferHeadersInput<T extends AppRoute, THeaders = T['headers']> = unknown extends THeaders ? undefined : IsEmptyObject<THeaders> extends true ? {} : THeaders extends Record<string, ContractAnyType> ? LowercaseKeys<UnknownOrUndefinedObjectValuesToOptionalKeys<{
96
+ [K in keyof THeaders]: THeaders[K] extends ContractAnyType ? SchemaInputOrType<THeaders[K]> : never;
97
+ }>> : undefined;
98
+ /**
99
+ * { foo: string | undefined } => { foo?: string | undefined }
100
+ * { foo: unknown } => { foo?: unknown }
101
+ *
102
+ * @internal
103
+ */
104
+ export type UnknownOrUndefinedObjectValuesToOptionalKeys<T> = {
105
+ [K in keyof T as undefined extends T[K] ? K : unknown extends T[K] ? K : never]?: T[K];
106
+ } & {
107
+ [K in keyof T as undefined extends T[K] ? never : unknown extends T[K] ? never : K]: T[K];
108
+ };
109
+ /**
110
+ * For a given app route, infer the headers output type
111
+ */
112
+ export type InferHeadersOutput<T extends AppRoute, THeaders = T['headers']> = unknown extends THeaders ? '1' : IsEmptyObject<THeaders> extends true ? {} : THeaders extends Record<string, ContractAnyType> ? {
113
+ [K in keyof THeaders]: THeaders[K] extends ContractAnyType ? LowercaseKeys<SchemaOutputOrType<THeaders[K]>> : never;
114
+ } : '3';
115
+ type ApplyOptions<TRoute extends AppRoute, TOptions extends RouterOptions> = Omit<TRoute, 'headers' | 'path' | 'responses'> & WithoutUnknown<{
116
+ path: TOptions['pathPrefix'] extends string ? `${TOptions['pathPrefix']}${TRoute['path']}` : TRoute['path'];
117
+ headers: MergeHeaders<UnknownToUndefined<TOptions['baseHeaders']>, UnknownToUndefined<TRoute['headers']>>;
118
+ strictStatusCodes: TRoute['strictStatusCodes'] extends boolean ? TRoute['strictStatusCodes'] : TOptions['strictStatusCodes'] extends boolean ? TOptions['strictStatusCodes'] : unknown;
119
+ responses: 'commonResponses' extends keyof TOptions ? Prettify<Merge<TOptions['commonResponses'], TRoute['responses']>> : TRoute['responses'];
120
+ metadata: 'metadata' extends keyof TOptions ? Prettify<Merge<TOptions['metadata'], TRoute['metadata']>> : TRoute['metadata'];
121
+ }>;
122
+ /**
123
+ * This was needed as **for some reason** headers above end up being `unknown`, our
124
+ * `MergeHeadersWithLegacySupport` function expends undefined, so we need to normalize it
125
+ *
126
+ * Can be moved in V4 when the legacy polyfill is removed
127
+ */
128
+ export type UnknownToUndefined<T> = unknown extends T ? T extends unknown ? undefined : T : T;
129
+ /**
130
+ * A union of all possible endpoint types.
131
+ */
132
+ export type AppRoute = AppRouteQuery | AppRouteMutation | AppRouteDeleteNoBody;
133
+ export type AppRouteStrictStatusCodes = Omit<AppRoute, 'strictStatusCodes'> & {
134
+ strictStatusCodes: true;
135
+ };
136
+ /**
137
+ * A router (or contract) in @ts-rest is a collection of more routers or
138
+ * individual routes
139
+ */
140
+ export type AppRouter = {
141
+ [key: string]: AppRouter | AppRoute;
142
+ };
143
+ export type FlattenAppRouter<T extends AppRouter | AppRoute> = T extends AppRoute ? T : {
144
+ [TKey in keyof T]: T[TKey] extends AppRoute ? T[TKey] : T[TKey] extends AppRouter ? FlattenAppRouter<T[TKey]> : never;
145
+ }[keyof T];
146
+ export type RouterOptions<TPrefix extends string = string> = {
147
+ baseHeaders?: Record<string, ContractAnyType>;
148
+ strictStatusCodes?: boolean;
149
+ pathPrefix?: TPrefix;
150
+ commonResponses?: Record<number, AppRouteResponse>;
151
+ metadata?: unknown;
152
+ /**
153
+ * @deprecated Use `validateResponse` on the client options
154
+ */
155
+ validateResponseOnClient?: boolean;
156
+ };
157
+ /**
158
+ * Differentiate between a route and a router
159
+ *
160
+ * @param obj
161
+ * @returns
162
+ */
163
+ export declare const isAppRoute: (obj: AppRoute | AppRouter) => obj is AppRoute;
164
+ export declare const isAppRouteQuery: (route: AppRoute) => route is AppRouteQuery;
165
+ export declare const isAppRouteMutation: (route: AppRoute) => route is AppRouteMutation;
166
+ type NarrowObject<T> = {
167
+ [K in keyof T]: T[K];
168
+ };
169
+ /**
170
+ * The instantiated ts-rest client
171
+ */
172
+ type ContractInstance = {
173
+ /**
174
+ * A collection of routes or routers
175
+ */
176
+ router: <TRouter extends AppRouter, TPrefix extends string, TOptions extends RouterOptions<TPrefix> = {}>(endpoints: RecursivelyProcessAppRouter<TRouter, TOptions>, options?: TOptions) => RecursivelyApplyOptions<TRouter, TOptions>;
177
+ /**
178
+ * A single query route, should exist within
179
+ * a {@link AppRouter}
180
+ */
181
+ query: <T extends AppRouteQuery>(query: NarrowObject<T>) => T;
182
+ /**
183
+ * A single mutation route, should exist within
184
+ * a {@link AppRouter}
185
+ */
186
+ mutation: <T extends AppRouteMutation>(mutation: NarrowObject<T>) => T;
187
+ responses: <TResponses extends Record<number, AppRouteResponse>>(responses: TResponses) => TResponses;
188
+ /**
189
+ * @deprecated Please use type() instead.
190
+ */
191
+ response: <T>() => T extends null ? ContractNullType : ContractPlainType<T>;
192
+ /**
193
+ * @deprecated Please use type() instead.
194
+ */
195
+ body: <T>() => T extends null ? ContractNullType : ContractPlainType<T>;
196
+ /**
197
+ * Exists to allow storing a Type in the contract (at compile time only)
198
+ */
199
+ type: <T>() => T extends null ? ContractNullType : ContractPlainType<T>;
200
+ /**
201
+ * Define a custom response type
202
+ */
203
+ otherResponse: <T extends ContractAnyType>({ contentType, body, }: {
204
+ contentType: string;
205
+ body: T;
206
+ }) => ContractOtherResponse<T>;
207
+ /** Use to indicate that a route takes no body or responds with no body */
208
+ noBody: () => ContractNoBodyType;
209
+ };
210
+ /**
211
+ *
212
+ * @deprecated Please use {@link initContract} instead.
213
+ */
214
+ export declare const initTsRest: () => ContractInstance;
215
+ export declare const ContractPlainTypeRuntimeSymbol: any;
216
+ /**
217
+ * Instantiate a ts-rest client, primarily to access `router`, `response`, and `body`
218
+ *
219
+ * @returns {ContractInstance}
220
+ */
221
+ export declare const initContract: () => ContractInstance;
222
+ export {};
@@ -0,0 +1,78 @@
1
+ import { AppRoute, AppRouteMutation, AppRouter, AppRouteStrictStatusCodes, ContractAnyType, ContractNoBodyType, ContractOtherResponse, InferHeadersInput, InferHeadersOutput } from './dsl';
2
+ import { HTTPStatusCode } from './status-codes';
3
+ import { And, Extends, LowercaseKeys, Merge, Not, OptionalIfAllOptional, Or, PartialByLooseKeys, Prettify, Without, SchemaOutputOrType, SchemaInputOrType } from './type-utils';
4
+ import { ApiFetcher, ClientArgs, OverridableClientArgs, FetchOptions } from './client';
5
+ import { ParamsFromUrl } from './paths';
6
+ type ExtractExtraParametersFromClientArgs<TClientArgs extends Pick<ClientArgs, 'api'>> = TClientArgs['api'] extends ApiFetcher ? Omit<Parameters<TClientArgs['api']>[0], keyof Parameters<ApiFetcher>[0]> : {};
7
+ /**
8
+ * Extract the path params from the path in the contract
9
+ */
10
+ type PathParamsFromUrl<T extends AppRoute> = ParamsFromUrl<T['path']> extends infer U ? U : never;
11
+ /**
12
+ * Merge `PathParamsFromUrl<T>` with pathParams schema if it exists
13
+ */
14
+ type PathParamsWithCustomValidators<T extends AppRoute, TClientOrServer extends 'client' | 'server' = 'server'> = 'pathParams' extends keyof T ? Merge<PathParamsFromUrl<T>, TClientOrServer extends 'server' ? SchemaOutputOrType<T['pathParams']> : SchemaInputOrType<T['pathParams']>> : PathParamsFromUrl<T>;
15
+ export type ResolveResponseType<T extends ContractAnyType | ContractNoBodyType | ContractOtherResponse<ContractAnyType>> = T extends ContractOtherResponse<infer U> ? U : T;
16
+ export type InferResponseDefinedStatusCodes<T extends AppRoute, TStatus extends HTTPStatusCode = HTTPStatusCode> = {
17
+ [K in keyof T['responses'] & TStatus]: K;
18
+ }[keyof T['responses'] & TStatus];
19
+ export type InferResponseUndefinedStatusCodes<T extends AppRoute, TStatus extends HTTPStatusCode = HTTPStatusCode> = Exclude<TStatus, InferResponseDefinedStatusCodes<T, TStatus>>;
20
+ type AppRouteResponses<T extends AppRoute, TStatus extends HTTPStatusCode, TClientOrServer extends 'client' | 'server', TStrictStatusCodes extends 'default' | 'ignore' | 'force' = 'default'> = {
21
+ [K in keyof T['responses'] & TStatus]: {
22
+ status: K;
23
+ body: TClientOrServer extends 'server' ? SchemaInputOrType<ResolveResponseType<T['responses'][K]>> : SchemaOutputOrType<ResolveResponseType<T['responses'][K]>>;
24
+ } & (TClientOrServer extends 'client' ? {
25
+ headers: Headers;
26
+ } : {});
27
+ }[keyof T['responses'] & TStatus] | (Or<Extends<TStrictStatusCodes, 'force'>, And<Extends<T, AppRouteStrictStatusCodes>, Not<Extends<TStrictStatusCodes, 'ignore'>>>> extends true ? never : Exclude<TStatus, keyof T['responses']> extends never ? never : {
28
+ status: Exclude<TStatus, keyof T['responses']>;
29
+ body: unknown;
30
+ } & (TClientOrServer extends 'client' ? {
31
+ headers: Headers;
32
+ } : {}));
33
+ export type ServerInferResponses<T extends AppRoute | AppRouter, TStatus extends HTTPStatusCode = HTTPStatusCode, TStrictStatusCodes extends 'default' | 'ignore' | 'force' = 'default'> = T extends AppRoute ? Prettify<AppRouteResponses<T, TStatus, 'server', TStrictStatusCodes>> : T extends AppRouter ? {
34
+ [TKey in keyof T]: ServerInferResponses<T[TKey], TStatus, TStrictStatusCodes>;
35
+ } : never;
36
+ export type ClientInferResponses<T extends AppRoute | AppRouter, TStatus extends HTTPStatusCode = HTTPStatusCode, TStrictStatusCodes extends 'default' | 'ignore' | 'force' = 'default'> = T extends AppRoute ? Prettify<AppRouteResponses<T, TStatus, 'client', TStrictStatusCodes>> : T extends AppRouter ? {
37
+ [TKey in keyof T]: ClientInferResponses<T[TKey], TStatus, TStrictStatusCodes>;
38
+ } : never;
39
+ export type ServerInferResponseBody<T extends AppRoute, TStatus extends keyof T['responses'] = keyof T['responses']> = Prettify<AppRouteResponses<T, TStatus & HTTPStatusCode, 'server'>['body']>;
40
+ export type ClientInferResponseBody<T extends AppRoute, TStatus extends keyof T['responses'] = keyof T['responses']> = Prettify<AppRouteResponses<T, TStatus & HTTPStatusCode, 'client'>['body']>;
41
+ type BodyWithoutFileIfMultiPart<T extends AppRouteMutation> = T['contentType'] extends 'multipart/form-data' ? Without<SchemaOutputOrType<T['body']>, File | File[]> : SchemaOutputOrType<T['body']>;
42
+ export type ServerInferRequest<T extends AppRoute | AppRouter, TServerHeaders = never> = T extends AppRoute ? Prettify<Without<{
43
+ params: [keyof PathParamsWithCustomValidators<T>] extends [never] ? never : Prettify<PathParamsWithCustomValidators<T>>;
44
+ body: T extends AppRouteMutation ? BodyWithoutFileIfMultiPart<T> : never;
45
+ query: 'query' extends keyof T ? SchemaOutputOrType<T['query']> : never;
46
+ headers: 'headers' extends keyof T ? Prettify<InferHeadersOutput<T> & ([TServerHeaders] extends [never] ? {} : Omit<TServerHeaders, keyof LowercaseKeys<InferHeadersOutput<T>>>)> : TServerHeaders;
47
+ }, never>> : T extends AppRouter ? {
48
+ [TKey in keyof T]: ServerInferRequest<T[TKey], TServerHeaders>;
49
+ } : never;
50
+ type ClientInferRequestBase<T extends AppRoute, TClientArgs extends Omit<ClientArgs, 'baseUrl'> = {}, THeaders = 'headers' extends keyof T ? Prettify<PartialByLooseKeys<LowercaseKeys<InferHeadersInput<T>>, keyof LowercaseKeys<TClientArgs['baseHeaders']>>> : never, TFetchOptions extends FetchOptions = FetchOptions> = Prettify<Without<{
51
+ params: [keyof PathParamsWithCustomValidators<T, 'client'>] extends [
52
+ never
53
+ ] ? never : Prettify<PathParamsWithCustomValidators<T, 'client'>>;
54
+ body: T extends AppRouteMutation ? T['body'] extends null ? never : T['contentType'] extends 'multipart/form-data' ? FormData | SchemaInputOrType<T['body']> : T['contentType'] extends 'application/x-www-form-urlencoded' ? string | SchemaInputOrType<T['body']> : SchemaInputOrType<T['body']> : never;
55
+ query: 'query' extends keyof T ? T['query'] extends null ? never : SchemaInputOrType<T['query']> : never;
56
+ headers: THeaders;
57
+ extraHeaders?: [THeaders] extends [never] ? Record<string, string> : {
58
+ [K in keyof RequiredKeys<InferHeadersInput<T>>]?: never;
59
+ } & Record<string, string>;
60
+ fetchOptions?: FetchOptions;
61
+ overrideClientOptions?: Partial<OverridableClientArgs>;
62
+ /**
63
+ * @deprecated Use `fetchOptions.cache` instead
64
+ */
65
+ cache?: 'cache' extends keyof TFetchOptions ? TFetchOptions['cache'] : never;
66
+ /**
67
+ * @deprecated Use `fetchOptions.next` instead
68
+ */
69
+ next?: 'next' extends keyof TFetchOptions ? TFetchOptions['next'] : never;
70
+ } & ExtractExtraParametersFromClientArgs<TClientArgs>, never>>;
71
+ type RequiredKeys<T> = {
72
+ [K in keyof T]-?: T[K];
73
+ };
74
+ export type ClientInferRequest<T extends AppRoute | AppRouter, TClientArgs extends Omit<ClientArgs, 'baseUrl'> = {}> = T extends AppRoute ? ClientInferRequestBase<T, TClientArgs> : T extends AppRouter ? {
75
+ [TKey in keyof T]: ClientInferRequest<T[TKey]>;
76
+ } : never;
77
+ export type PartialClientInferRequest<TRoute extends AppRoute, TClientArgs extends Omit<ClientArgs, 'baseUrl'> = {}> = OptionalIfAllOptional<ClientInferRequest<TRoute, TClientArgs>>;
78
+ export {};
@@ -0,0 +1,30 @@
1
+ type ResolveOptionalPathParam<T extends string> = T extends `${infer PathParam}?` ? {
2
+ [key in PathParam]?: string | undefined;
3
+ } : {
4
+ [key in T]: string;
5
+ };
6
+ /**
7
+ * @params T - The URL e.g. /posts/:id
8
+ * @params TAcc - Accumulator object
9
+ */
10
+ type RecursivelyExtractPathParams<T extends string> = T extends '' ? Record<never, never> : T extends `${infer Left}/:${infer PathParam}/${infer Right}` ? ResolveOptionalPathParam<PathParam> & RecursivelyExtractPathParams<Left> & RecursivelyExtractPathParams<Right> : T extends `:${infer PathParam}/${infer Right}` ? ResolveOptionalPathParam<PathParam> & RecursivelyExtractPathParams<Right> : T extends `${infer Left}/:${infer PathParam}` ? ResolveOptionalPathParam<PathParam> & RecursivelyExtractPathParams<Left> : T extends `:${infer PathParam}` ? ResolveOptionalPathParam<PathParam> : Record<never, never>;
11
+ /**
12
+ * Extract path params from path function
13
+ *
14
+ * `{ id: string, commentId: string }`
15
+ *
16
+ * @params T - The URL e.g. /posts/:id
17
+ */
18
+ export type ParamsFromUrl<T extends string> = RecursivelyExtractPathParams<T> extends infer U ? {
19
+ [key in keyof U]: U[key];
20
+ } : never;
21
+ /**
22
+ * @param path - The URL e.g. /posts/:id
23
+ * @param params - The params e.g. `{ id: string }`
24
+ * @returns - The URL with the params e.g. /posts/123
25
+ */
26
+ export declare const insertParamsIntoPath: <T extends string>({ path, params, }: {
27
+ path: T;
28
+ params: ParamsFromUrl<T>;
29
+ }) => string;
30
+ export {};
@@ -0,0 +1,17 @@
1
+ /**
2
+ *
3
+ * @param query - Any JSON object
4
+ * @param json - Use JSON.stringify to encode the query values
5
+ * @returns - The query url segment, using explode array syntax, and deep object syntax
6
+ */
7
+ export declare const convertQueryParamsToUrlString: (query: unknown, json?: boolean) => string;
8
+ export declare const encodeQueryParamsJson: (query: unknown) => string;
9
+ export declare const encodeQueryParams: (query: unknown) => string;
10
+ /**
11
+ *
12
+ * @param query - A server-side query object where values have been encoded as JSON strings
13
+ * @returns - The same object with the JSON strings decoded. Objects that were encoded using toJSON such as Dates will remain as strings
14
+ */
15
+ export declare const parseJsonQueryObject: (query: Record<string, string>) => {
16
+ [k: string]: any;
17
+ };
@@ -0,0 +1,20 @@
1
+ import { AppRoute, AppRouter, AppRouteResponse } from './dsl';
2
+ import { ResolveResponseType, ServerInferResponses } from './infer-types';
3
+ import { HTTPStatusCode } from './status-codes';
4
+ import { CommonAndEqual, SchemaInputOrType } from './type-utils';
5
+ export declare class TsRestResponseError<T extends AppRoute | AppRouter> extends Error {
6
+ statusCode: HTTPStatusCode;
7
+ body: any;
8
+ constructor(route: T, response: T extends AppRouter ? ServerCommonResponses<T> : ServerInferResponses<T>);
9
+ }
10
+ type FlattenAppRouter<T extends AppRouter | AppRoute> = T extends AppRoute ? T : {
11
+ [TKey in keyof T]: T[TKey] extends AppRoute ? T[TKey] : T[TKey] extends AppRouter ? FlattenAppRouter<T[TKey]> : never;
12
+ }[keyof T];
13
+ type AppRouterCommonResponses<T extends AppRouter> = CommonAndEqual<FlattenAppRouter<T>['responses']>;
14
+ type ServerCommonResponses<T extends AppRouter, TResponses = AppRouterCommonResponses<T>> = {
15
+ [K in keyof TResponses]: {
16
+ status: K;
17
+ body: TResponses[K] extends AppRouteResponse ? SchemaInputOrType<ResolveResponseType<TResponses[K]>> : never;
18
+ };
19
+ }[keyof TResponses];
20
+ export {};
@@ -0,0 +1,14 @@
1
+ import { AppRoute } from './dsl';
2
+ import { StandardSchemaError } from './validation-error';
3
+ export declare class TsRestResponseValidationError extends Error {
4
+ appRoute: AppRoute;
5
+ cause: StandardSchemaError;
6
+ constructor(appRoute: AppRoute, cause: StandardSchemaError);
7
+ }
8
+ export declare class TsRestRequestValidationError extends Error {
9
+ pathParams: StandardSchemaError | null;
10
+ headers: StandardSchemaError | null;
11
+ query: StandardSchemaError | null;
12
+ body: StandardSchemaError | null;
13
+ constructor(pathParams: StandardSchemaError | null, headers: StandardSchemaError | null, query: StandardSchemaError | null, body: StandardSchemaError | null);
14
+ }
@@ -0,0 +1,18 @@
1
+ import { HTTPStatusCode } from './status-codes';
2
+ import { AppRoute, ContractAnyType, ContractNoBody, ContractNoBodyType, ContractOtherResponse } from './dsl';
3
+ export declare const isAppRouteResponse: (value: unknown) => value is {
4
+ status: HTTPStatusCode;
5
+ body?: any;
6
+ };
7
+ export declare const isAppRouteOtherResponse: (response: ContractAnyType | ContractNoBodyType | ContractOtherResponse<ContractAnyType>) => response is ContractOtherResponse<ContractAnyType>;
8
+ export declare const isAppRouteNoBody: (response: ContractAnyType | ContractNoBodyType | ContractOtherResponse<ContractAnyType>) => response is typeof ContractNoBody;
9
+ export declare const validateResponse: ({ appRoute, response, }: {
10
+ appRoute: AppRoute;
11
+ response: {
12
+ status: number;
13
+ body?: unknown;
14
+ };
15
+ }) => {
16
+ status: number;
17
+ body?: unknown;
18
+ };
@@ -0,0 +1,68 @@
1
+ import { ZodError } from 'zod';
2
+ import { StandardSchemaV1 } from './standard-schema';
3
+ import { StandardSchemaError } from './validation-error';
4
+ import { ContractAnyType } from './dsl';
5
+ /**
6
+ * Type guard to check if the schema is a standard schema.
7
+ *
8
+ * @param schema - unknown
9
+ * @returns boolean
10
+ */
11
+ export declare const isStandardSchema: (schema: unknown) => schema is StandardSchemaV1<unknown, unknown>;
12
+ /**
13
+ * Takes in an unknown object and returns either a standard schema or null
14
+ *
15
+ * @param schema - unknown
16
+ * @returns StandardSchemaV1<unknown, unknown> | null
17
+ */
18
+ export declare const parseAsStandardSchema: (schema: unknown) => StandardSchemaV1<unknown, unknown> | null;
19
+ /**
20
+ * Since 3.53.0 we've moved to headers using an object with schemas inside it, rather than a top level schema.
21
+ *
22
+ * This makes it easier to merge schemas together.
23
+ *
24
+ * @param data - Data to validate e.g. headers
25
+ * @param schemaObject - Schema object to validate against e.g. { 'x-foo': v.string() }
26
+ * @returns
27
+ */
28
+ export declare const validateMultiSchemaObject: (data: unknown, schemaObject: Record<string, ContractAnyType> | undefined) => {
29
+ value?: unknown;
30
+ error?: StandardSchemaError | ZodError;
31
+ schemasUsed: Array<StandardSchemaV1<unknown, unknown>>;
32
+ };
33
+ /**
34
+ * Combines two standard schemas into a single standard schema.
35
+ *
36
+ * The combined schema will run the validation of both schemas and return the result of the first schema that
37
+ * succeeds.
38
+ *
39
+ * If either schema fails, the combined schema will return the issues from both schemas.
40
+ *
41
+ * @param a - StandardSchemaV1<unknown, unknown>
42
+ * @param b - StandardSchemaV1<unknown, unknown>
43
+ * @returns StandardSchemaV1<unknown, unknown>
44
+ */
45
+ export declare const combineStandardSchemas: (a: StandardSchemaV1<unknown, unknown>, b: StandardSchemaV1<unknown, unknown>) => StandardSchemaV1<unknown, unknown>;
46
+ /**
47
+ * Merges two header schemas together, these can either be legacy zod objects or objects containing standard schemas.
48
+ */
49
+ export declare const mergeHeaderSchemasForRoute: (baseSchema: unknown, routeSchema: unknown) => unknown;
50
+ /**
51
+ * Similar to validateAgainstStandardSchema, but it takes an unknown schema, it will not validate if no schema provided and will check the schema is
52
+ * valid before validating the data.
53
+ *
54
+ * This is super handy for validating request bodies, headers, etc. as it passes through the data if no schema is provided.
55
+ */
56
+ export declare const validateIfSchema: (data: unknown, schema: unknown, { passThroughExtraKeys, }?: {
57
+ passThroughExtraKeys?: boolean | undefined;
58
+ }) => {
59
+ value?: unknown;
60
+ error?: StandardSchemaError;
61
+ schemasUsed: Array<StandardSchemaV1<unknown, unknown>>;
62
+ };
63
+ export declare const validateAgainstStandardSchema: (data: unknown, schema: StandardSchemaV1<unknown, unknown>, { passThroughExtraKeys, }?: {
64
+ passThroughExtraKeys?: boolean | undefined;
65
+ }) => {
66
+ value?: unknown;
67
+ error?: StandardSchemaError | ZodError;
68
+ };