@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/infer-types.ts
DELETED
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AppRoute,
|
|
3
|
-
AppRouteMutation,
|
|
4
|
-
AppRouter,
|
|
5
|
-
AppRouteStrictStatusCodes,
|
|
6
|
-
ContractAnyType,
|
|
7
|
-
ContractNoBodyType,
|
|
8
|
-
ContractOtherResponse,
|
|
9
|
-
InferHeadersInput,
|
|
10
|
-
InferHeadersOutput,
|
|
11
|
-
} from './dsl';
|
|
12
|
-
import { HTTPStatusCode } from './status-codes';
|
|
13
|
-
import {
|
|
14
|
-
And,
|
|
15
|
-
Extends,
|
|
16
|
-
LowercaseKeys,
|
|
17
|
-
Merge,
|
|
18
|
-
Not,
|
|
19
|
-
OptionalIfAllOptional,
|
|
20
|
-
Or,
|
|
21
|
-
PartialByLooseKeys,
|
|
22
|
-
Prettify,
|
|
23
|
-
Without,
|
|
24
|
-
SchemaOutputOrType,
|
|
25
|
-
SchemaInputOrType,
|
|
26
|
-
} from './type-utils';
|
|
27
|
-
import {
|
|
28
|
-
ApiFetcher,
|
|
29
|
-
ClientArgs,
|
|
30
|
-
OverridableClientArgs,
|
|
31
|
-
FetchOptions,
|
|
32
|
-
} from './client';
|
|
33
|
-
import { ParamsFromUrl } from './paths';
|
|
34
|
-
|
|
35
|
-
type ExtractExtraParametersFromClientArgs<
|
|
36
|
-
TClientArgs extends Pick<ClientArgs, 'api'>,
|
|
37
|
-
> = TClientArgs['api'] extends ApiFetcher
|
|
38
|
-
? Omit<Parameters<TClientArgs['api']>[0], keyof Parameters<ApiFetcher>[0]>
|
|
39
|
-
: {};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Extract the path params from the path in the contract
|
|
43
|
-
*/
|
|
44
|
-
type PathParamsFromUrl<T extends AppRoute> = ParamsFromUrl<
|
|
45
|
-
T['path']
|
|
46
|
-
> extends infer U
|
|
47
|
-
? U
|
|
48
|
-
: never;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Merge `PathParamsFromUrl<T>` with pathParams schema if it exists
|
|
52
|
-
*/
|
|
53
|
-
type PathParamsWithCustomValidators<
|
|
54
|
-
T extends AppRoute,
|
|
55
|
-
TClientOrServer extends 'client' | 'server' = 'server',
|
|
56
|
-
> = 'pathParams' extends keyof T
|
|
57
|
-
? Merge<
|
|
58
|
-
PathParamsFromUrl<T>,
|
|
59
|
-
TClientOrServer extends 'server'
|
|
60
|
-
? SchemaOutputOrType<T['pathParams']>
|
|
61
|
-
: SchemaInputOrType<T['pathParams']>
|
|
62
|
-
>
|
|
63
|
-
: PathParamsFromUrl<T>;
|
|
64
|
-
|
|
65
|
-
export type ResolveResponseType<
|
|
66
|
-
T extends
|
|
67
|
-
| ContractAnyType
|
|
68
|
-
| ContractNoBodyType
|
|
69
|
-
| ContractOtherResponse<ContractAnyType>,
|
|
70
|
-
> = T extends ContractOtherResponse<infer U> ? U : T;
|
|
71
|
-
|
|
72
|
-
export type InferResponseDefinedStatusCodes<
|
|
73
|
-
T extends AppRoute,
|
|
74
|
-
TStatus extends HTTPStatusCode = HTTPStatusCode,
|
|
75
|
-
> = {
|
|
76
|
-
[K in keyof T['responses'] & TStatus]: K;
|
|
77
|
-
}[keyof T['responses'] & TStatus];
|
|
78
|
-
|
|
79
|
-
export type InferResponseUndefinedStatusCodes<
|
|
80
|
-
T extends AppRoute,
|
|
81
|
-
TStatus extends HTTPStatusCode = HTTPStatusCode,
|
|
82
|
-
> = Exclude<TStatus, InferResponseDefinedStatusCodes<T, TStatus>>;
|
|
83
|
-
|
|
84
|
-
type AppRouteResponses<
|
|
85
|
-
T extends AppRoute,
|
|
86
|
-
TStatus extends HTTPStatusCode,
|
|
87
|
-
TClientOrServer extends 'client' | 'server',
|
|
88
|
-
TStrictStatusCodes extends 'default' | 'ignore' | 'force' = 'default',
|
|
89
|
-
> =
|
|
90
|
-
| {
|
|
91
|
-
[K in keyof T['responses'] & TStatus]: {
|
|
92
|
-
status: K;
|
|
93
|
-
body: TClientOrServer extends 'server'
|
|
94
|
-
? SchemaInputOrType<ResolveResponseType<T['responses'][K]>>
|
|
95
|
-
: SchemaOutputOrType<ResolveResponseType<T['responses'][K]>>;
|
|
96
|
-
} & (TClientOrServer extends 'client'
|
|
97
|
-
? {
|
|
98
|
-
headers: Headers;
|
|
99
|
-
}
|
|
100
|
-
: {});
|
|
101
|
-
}[keyof T['responses'] & TStatus]
|
|
102
|
-
| (Or<
|
|
103
|
-
Extends<TStrictStatusCodes, 'force'>,
|
|
104
|
-
And<
|
|
105
|
-
Extends<T, AppRouteStrictStatusCodes>,
|
|
106
|
-
Not<Extends<TStrictStatusCodes, 'ignore'>>
|
|
107
|
-
>
|
|
108
|
-
> extends true
|
|
109
|
-
? never
|
|
110
|
-
: Exclude<TStatus, keyof T['responses']> extends never
|
|
111
|
-
? never
|
|
112
|
-
: {
|
|
113
|
-
status: Exclude<TStatus, keyof T['responses']>;
|
|
114
|
-
body: unknown;
|
|
115
|
-
} & (TClientOrServer extends 'client'
|
|
116
|
-
? {
|
|
117
|
-
headers: Headers;
|
|
118
|
-
}
|
|
119
|
-
: {}));
|
|
120
|
-
|
|
121
|
-
export type ServerInferResponses<
|
|
122
|
-
T extends AppRoute | AppRouter,
|
|
123
|
-
TStatus extends HTTPStatusCode = HTTPStatusCode,
|
|
124
|
-
TStrictStatusCodes extends 'default' | 'ignore' | 'force' = 'default',
|
|
125
|
-
> = T extends AppRoute
|
|
126
|
-
? Prettify<AppRouteResponses<T, TStatus, 'server', TStrictStatusCodes>>
|
|
127
|
-
: T extends AppRouter
|
|
128
|
-
? {
|
|
129
|
-
[TKey in keyof T]: ServerInferResponses<
|
|
130
|
-
T[TKey],
|
|
131
|
-
TStatus,
|
|
132
|
-
TStrictStatusCodes
|
|
133
|
-
>;
|
|
134
|
-
}
|
|
135
|
-
: never;
|
|
136
|
-
|
|
137
|
-
export type ClientInferResponses<
|
|
138
|
-
T extends AppRoute | AppRouter,
|
|
139
|
-
TStatus extends HTTPStatusCode = HTTPStatusCode,
|
|
140
|
-
TStrictStatusCodes extends 'default' | 'ignore' | 'force' = 'default',
|
|
141
|
-
> = T extends AppRoute
|
|
142
|
-
? Prettify<AppRouteResponses<T, TStatus, 'client', TStrictStatusCodes>>
|
|
143
|
-
: T extends AppRouter
|
|
144
|
-
? {
|
|
145
|
-
[TKey in keyof T]: ClientInferResponses<
|
|
146
|
-
T[TKey],
|
|
147
|
-
TStatus,
|
|
148
|
-
TStrictStatusCodes
|
|
149
|
-
>;
|
|
150
|
-
}
|
|
151
|
-
: never;
|
|
152
|
-
|
|
153
|
-
export type ServerInferResponseBody<
|
|
154
|
-
T extends AppRoute,
|
|
155
|
-
TStatus extends keyof T['responses'] = keyof T['responses'],
|
|
156
|
-
> = Prettify<AppRouteResponses<T, TStatus & HTTPStatusCode, 'server'>['body']>;
|
|
157
|
-
|
|
158
|
-
export type ClientInferResponseBody<
|
|
159
|
-
T extends AppRoute,
|
|
160
|
-
TStatus extends keyof T['responses'] = keyof T['responses'],
|
|
161
|
-
> = Prettify<AppRouteResponses<T, TStatus & HTTPStatusCode, 'client'>['body']>;
|
|
162
|
-
|
|
163
|
-
type BodyWithoutFileIfMultiPart<T extends AppRouteMutation> =
|
|
164
|
-
T['contentType'] extends 'multipart/form-data'
|
|
165
|
-
? Without<SchemaOutputOrType<T['body']>, File | File[]>
|
|
166
|
-
: SchemaOutputOrType<T['body']>;
|
|
167
|
-
|
|
168
|
-
export type ServerInferRequest<
|
|
169
|
-
T extends AppRoute | AppRouter,
|
|
170
|
-
TServerHeaders = never,
|
|
171
|
-
> = T extends AppRoute
|
|
172
|
-
? Prettify<
|
|
173
|
-
Without<
|
|
174
|
-
{
|
|
175
|
-
params: [keyof PathParamsWithCustomValidators<T>] extends [never]
|
|
176
|
-
? never
|
|
177
|
-
: Prettify<PathParamsWithCustomValidators<T>>;
|
|
178
|
-
body: T extends AppRouteMutation
|
|
179
|
-
? BodyWithoutFileIfMultiPart<T>
|
|
180
|
-
: never;
|
|
181
|
-
query: 'query' extends keyof T
|
|
182
|
-
? SchemaOutputOrType<T['query']>
|
|
183
|
-
: never;
|
|
184
|
-
headers: 'headers' extends keyof T
|
|
185
|
-
? Prettify<
|
|
186
|
-
InferHeadersOutput<T> &
|
|
187
|
-
([TServerHeaders] extends [never]
|
|
188
|
-
? {}
|
|
189
|
-
: Omit<
|
|
190
|
-
TServerHeaders,
|
|
191
|
-
keyof LowercaseKeys<InferHeadersOutput<T>>
|
|
192
|
-
>)
|
|
193
|
-
>
|
|
194
|
-
: TServerHeaders;
|
|
195
|
-
},
|
|
196
|
-
never
|
|
197
|
-
>
|
|
198
|
-
>
|
|
199
|
-
: T extends AppRouter
|
|
200
|
-
? { [TKey in keyof T]: ServerInferRequest<T[TKey], TServerHeaders> }
|
|
201
|
-
: never;
|
|
202
|
-
|
|
203
|
-
type ClientInferRequestBase<
|
|
204
|
-
T extends AppRoute,
|
|
205
|
-
TClientArgs extends Omit<ClientArgs, 'baseUrl'> = {},
|
|
206
|
-
THeaders = 'headers' extends keyof T
|
|
207
|
-
? /**
|
|
208
|
-
* We want to only require headers that the user did not provide
|
|
209
|
-
* in the base headers of the client
|
|
210
|
-
*/
|
|
211
|
-
Prettify<
|
|
212
|
-
PartialByLooseKeys<
|
|
213
|
-
LowercaseKeys<InferHeadersInput<T>>,
|
|
214
|
-
keyof LowercaseKeys<TClientArgs['baseHeaders']>
|
|
215
|
-
>
|
|
216
|
-
>
|
|
217
|
-
: never,
|
|
218
|
-
TFetchOptions extends FetchOptions = FetchOptions,
|
|
219
|
-
> = Prettify<
|
|
220
|
-
Without<
|
|
221
|
-
{
|
|
222
|
-
params: [keyof PathParamsWithCustomValidators<T, 'client'>] extends [
|
|
223
|
-
never,
|
|
224
|
-
]
|
|
225
|
-
? never
|
|
226
|
-
: Prettify<PathParamsWithCustomValidators<T, 'client'>>;
|
|
227
|
-
body: T extends AppRouteMutation
|
|
228
|
-
? T['body'] extends null
|
|
229
|
-
? never
|
|
230
|
-
: T['contentType'] extends 'multipart/form-data'
|
|
231
|
-
? FormData | SchemaInputOrType<T['body']>
|
|
232
|
-
: T['contentType'] extends 'application/x-www-form-urlencoded'
|
|
233
|
-
? string | SchemaInputOrType<T['body']>
|
|
234
|
-
: SchemaInputOrType<T['body']>
|
|
235
|
-
: never;
|
|
236
|
-
query: 'query' extends keyof T
|
|
237
|
-
? T['query'] extends null
|
|
238
|
-
? never
|
|
239
|
-
: SchemaInputOrType<T['query']>
|
|
240
|
-
: never;
|
|
241
|
-
headers: THeaders;
|
|
242
|
-
extraHeaders?: [THeaders] extends [never]
|
|
243
|
-
? Record<string, string>
|
|
244
|
-
: {
|
|
245
|
-
[K in keyof RequiredKeys<InferHeadersInput<T>>]?: never;
|
|
246
|
-
} & Record<string, string>;
|
|
247
|
-
fetchOptions?: FetchOptions;
|
|
248
|
-
overrideClientOptions?: Partial<OverridableClientArgs>;
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* @deprecated Use `fetchOptions.cache` instead
|
|
252
|
-
*/
|
|
253
|
-
cache?: 'cache' extends keyof TFetchOptions
|
|
254
|
-
? TFetchOptions['cache']
|
|
255
|
-
: never;
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* @deprecated Use `fetchOptions.next` instead
|
|
259
|
-
*/
|
|
260
|
-
next?: 'next' extends keyof TFetchOptions ? TFetchOptions['next'] : never;
|
|
261
|
-
} & ExtractExtraParametersFromClientArgs<TClientArgs>,
|
|
262
|
-
never
|
|
263
|
-
>
|
|
264
|
-
>;
|
|
265
|
-
|
|
266
|
-
type RequiredKeys<T> = {
|
|
267
|
-
[K in keyof T]-?: T[K];
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
export type ClientInferRequest<
|
|
271
|
-
T extends AppRoute | AppRouter,
|
|
272
|
-
TClientArgs extends Omit<ClientArgs, 'baseUrl'> = {},
|
|
273
|
-
> = T extends AppRoute
|
|
274
|
-
? ClientInferRequestBase<T, TClientArgs>
|
|
275
|
-
: T extends AppRouter
|
|
276
|
-
? { [TKey in keyof T]: ClientInferRequest<T[TKey]> }
|
|
277
|
-
: never;
|
|
278
|
-
|
|
279
|
-
export type PartialClientInferRequest<
|
|
280
|
-
TRoute extends AppRoute,
|
|
281
|
-
TClientArgs extends Omit<ClientArgs, 'baseUrl'> = {},
|
|
282
|
-
> = OptionalIfAllOptional<ClientInferRequest<TRoute, TClientArgs>>;
|
package/src/lib/paths.spec.ts
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { expectType } from 'tsd';
|
|
2
|
-
import { insertParamsIntoPath, ParamsFromUrl } from './paths';
|
|
3
|
-
|
|
4
|
-
const type = <T>() => '' as unknown as T;
|
|
5
|
-
|
|
6
|
-
const url = '/post/:id/comments/:commentId';
|
|
7
|
-
expectType<{ id: string; commentId: string }>(
|
|
8
|
-
type<ParamsFromUrl<typeof url>>(),
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
const url2 = '/post/:id/comments';
|
|
12
|
-
expectType<{ id: string }>(type<ParamsFromUrl<typeof url2>>());
|
|
13
|
-
|
|
14
|
-
const url3 = '/post/:id';
|
|
15
|
-
expectType<{ id: string }>(type<ParamsFromUrl<typeof url3>>());
|
|
16
|
-
|
|
17
|
-
const urlNoParams = '/posts';
|
|
18
|
-
expectType<{}>(type<ParamsFromUrl<typeof urlNoParams>>());
|
|
19
|
-
|
|
20
|
-
const urlManyParams = '/post/:id/comments/:commentId/:commentId2';
|
|
21
|
-
expectType<{ id: string; commentId: string; commentId2: string }>(
|
|
22
|
-
type<ParamsFromUrl<typeof urlManyParams>>(),
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
const urlOptional = '/post/:id?';
|
|
26
|
-
expectType<{
|
|
27
|
-
id?: string;
|
|
28
|
-
}>(type<ParamsFromUrl<typeof urlOptional>>());
|
|
29
|
-
|
|
30
|
-
const urlManyOptional = '/post/:id?/comments/:commentId?';
|
|
31
|
-
expectType<{
|
|
32
|
-
id?: string;
|
|
33
|
-
commentId?: string;
|
|
34
|
-
}>(type<ParamsFromUrl<typeof urlManyOptional>>());
|
|
35
|
-
|
|
36
|
-
const urlMixedOptional = '/post/:id/comments/:commentId?';
|
|
37
|
-
expectType<{
|
|
38
|
-
id: string;
|
|
39
|
-
commentId?: string;
|
|
40
|
-
}>(type<ParamsFromUrl<typeof urlMixedOptional>>());
|
|
41
|
-
|
|
42
|
-
const urlMixedOptional2 = '/post/:id?/comments/:commentId';
|
|
43
|
-
expectType<{
|
|
44
|
-
id?: string;
|
|
45
|
-
commentId: string;
|
|
46
|
-
}>(type<ParamsFromUrl<typeof urlMixedOptional2>>());
|
|
47
|
-
|
|
48
|
-
describe('insertParamsIntoPath', () => {
|
|
49
|
-
it('should insert params into path', () => {
|
|
50
|
-
expect(
|
|
51
|
-
insertParamsIntoPath({
|
|
52
|
-
path: '/post/:id/comments/:commentId',
|
|
53
|
-
params: { commentId: '2', id: '1' },
|
|
54
|
-
}),
|
|
55
|
-
).toBe('/post/1/comments/2');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should insert params into path with no params', () => {
|
|
59
|
-
expect(
|
|
60
|
-
insertParamsIntoPath({
|
|
61
|
-
path: '/posts',
|
|
62
|
-
params: { a: '1' },
|
|
63
|
-
}),
|
|
64
|
-
).toBe('/posts');
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('should insert params into path with many params', () => {
|
|
68
|
-
expect(
|
|
69
|
-
insertParamsIntoPath({
|
|
70
|
-
path: '/post/:id/comments/:commentId/:commentId2',
|
|
71
|
-
params: { commentId: '2', commentId2: '3', id: '1' },
|
|
72
|
-
}),
|
|
73
|
-
).toBe('/post/1/comments/2/3');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should insert into paths with only one param', () => {
|
|
77
|
-
expect(
|
|
78
|
-
insertParamsIntoPath({
|
|
79
|
-
path: '/:id',
|
|
80
|
-
params: { id: '1' },
|
|
81
|
-
}),
|
|
82
|
-
).toBe('/1');
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should insert optional params into path with many params', () => {
|
|
86
|
-
expect(
|
|
87
|
-
insertParamsIntoPath({
|
|
88
|
-
path: '/post/:id?/comments/:commentId?/:commentId2?',
|
|
89
|
-
params: { commentId: '2', commentId2: '3', id: '1' },
|
|
90
|
-
}),
|
|
91
|
-
).toBe('/post/1/comments/2/3');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('should insert optional params into path with no params', () => {
|
|
95
|
-
expect(
|
|
96
|
-
insertParamsIntoPath({
|
|
97
|
-
path: '/post/:id?/comments/:commentId?/:commentId2?',
|
|
98
|
-
params: {},
|
|
99
|
-
}),
|
|
100
|
-
).toBe('/post/comments');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should insert not have trailing slashes', () => {
|
|
104
|
-
expect(
|
|
105
|
-
insertParamsIntoPath({
|
|
106
|
-
path: '/post/:id?/comments/:commentId?/:commentId2?/:commentId3?',
|
|
107
|
-
params: {},
|
|
108
|
-
}),
|
|
109
|
-
).toBe('/post/comments');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('should insert optional params into paths with only one param', () => {
|
|
113
|
-
expect(
|
|
114
|
-
insertParamsIntoPath({
|
|
115
|
-
path: '/:id?',
|
|
116
|
-
params: { id: '1' },
|
|
117
|
-
}),
|
|
118
|
-
).toBe('/1');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should preserve trailing slashes in path', () => {
|
|
122
|
-
expect(
|
|
123
|
-
insertParamsIntoPath({
|
|
124
|
-
path: '/:id/',
|
|
125
|
-
params: { id: '1' },
|
|
126
|
-
}),
|
|
127
|
-
).toBe('/1/');
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should preserve trailing slashes in path with optional params', () => {
|
|
131
|
-
expect(
|
|
132
|
-
insertParamsIntoPath({
|
|
133
|
-
path: '/post/:id?/comments/:commentId?/:commentId2?/:commentId3?/',
|
|
134
|
-
params: {},
|
|
135
|
-
}),
|
|
136
|
-
).toBe('/post/comments/');
|
|
137
|
-
});
|
|
138
|
-
});
|
package/src/lib/paths.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
type ResolveOptionalPathParam<T extends string> =
|
|
2
|
-
T extends `${infer PathParam}?`
|
|
3
|
-
? {
|
|
4
|
-
[key in PathParam]?: string | undefined;
|
|
5
|
-
}
|
|
6
|
-
: {
|
|
7
|
-
[key in T]: string;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @params T - The URL e.g. /posts/:id
|
|
12
|
-
* @params TAcc - Accumulator object
|
|
13
|
-
*/
|
|
14
|
-
type RecursivelyExtractPathParams<T extends string> = T extends ''
|
|
15
|
-
? Record<never, never>
|
|
16
|
-
: T extends `${infer Left}/:${infer PathParam}/${infer Right}`
|
|
17
|
-
? ResolveOptionalPathParam<PathParam> &
|
|
18
|
-
RecursivelyExtractPathParams<Left> &
|
|
19
|
-
RecursivelyExtractPathParams<Right>
|
|
20
|
-
: T extends `:${infer PathParam}/${infer Right}`
|
|
21
|
-
? ResolveOptionalPathParam<PathParam> & RecursivelyExtractPathParams<Right>
|
|
22
|
-
: T extends `${infer Left}/:${infer PathParam}`
|
|
23
|
-
? ResolveOptionalPathParam<PathParam> & RecursivelyExtractPathParams<Left>
|
|
24
|
-
: T extends `:${infer PathParam}`
|
|
25
|
-
? ResolveOptionalPathParam<PathParam>
|
|
26
|
-
: Record<never, never>;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Extract path params from path function
|
|
30
|
-
*
|
|
31
|
-
* `{ id: string, commentId: string }`
|
|
32
|
-
*
|
|
33
|
-
* @params T - The URL e.g. /posts/:id
|
|
34
|
-
*/
|
|
35
|
-
export type ParamsFromUrl<T extends string> =
|
|
36
|
-
RecursivelyExtractPathParams<T> extends infer U
|
|
37
|
-
? {
|
|
38
|
-
[key in keyof U]: U[key];
|
|
39
|
-
}
|
|
40
|
-
: never;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* @param path - The URL e.g. /posts/:id
|
|
44
|
-
* @param params - The params e.g. `{ id: string }`
|
|
45
|
-
* @returns - The URL with the params e.g. /posts/123
|
|
46
|
-
*/
|
|
47
|
-
export const insertParamsIntoPath = <T extends string>({
|
|
48
|
-
path,
|
|
49
|
-
params,
|
|
50
|
-
}: {
|
|
51
|
-
path: T;
|
|
52
|
-
params: ParamsFromUrl<T>;
|
|
53
|
-
}) => {
|
|
54
|
-
const pathParams: Record<string, string> = params;
|
|
55
|
-
|
|
56
|
-
return path.replace(/\/?:([^/?]+)\??/g, (matched, p) =>
|
|
57
|
-
pathParams[p]
|
|
58
|
-
? `${matched.startsWith('/') ? '/' : ''}${pathParams[p]}`
|
|
59
|
-
: '',
|
|
60
|
-
);
|
|
61
|
-
};
|