@hey-api/openapi-ts 0.80.18 → 0.81.0
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/dist/chunk-Z7FL5BGA.js +43 -0
- package/dist/chunk-Z7FL5BGA.js.map +1 -0
- package/dist/clients/angular/client.ts +37 -14
- package/dist/clients/angular/types.ts +50 -17
- package/dist/clients/axios/client.ts +36 -10
- package/dist/clients/axios/types.ts +51 -18
- package/dist/clients/axios/utils.ts +5 -112
- package/dist/clients/core/serverSentEvents.ts +235 -0
- package/dist/clients/core/utils.ts +112 -0
- package/dist/clients/fetch/client.ts +40 -12
- package/dist/clients/fetch/types.ts +49 -15
- package/dist/clients/fetch/utils.ts +4 -117
- package/dist/clients/next/client.ts +43 -14
- package/dist/clients/next/types.ts +47 -14
- package/dist/clients/nuxt/client.ts +65 -13
- package/dist/clients/nuxt/types.ts +27 -2
- package/dist/clients/nuxt/utils.ts +1 -1
- package/dist/index.cjs +65 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.cjs +12 -12
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-4ELE5AM4.js +0 -43
- package/dist/chunk-4ELE5AM4.js.map +0 -1
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import type { Config } from './types';
|
|
2
|
+
|
|
3
|
+
export type ServerSentEventsOptions<TData = unknown> = Omit<
|
|
4
|
+
RequestInit,
|
|
5
|
+
'method'
|
|
6
|
+
> &
|
|
7
|
+
Pick<Config, 'method' | 'responseTransformer' | 'responseValidator'> & {
|
|
8
|
+
/**
|
|
9
|
+
* Callback invoked when a network or parsing error occurs during streaming.
|
|
10
|
+
*
|
|
11
|
+
* This option applies only if the endpoint returns a stream of events.
|
|
12
|
+
*
|
|
13
|
+
* @param error The error that occurred.
|
|
14
|
+
*/
|
|
15
|
+
onSseError?: (error: unknown) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Callback invoked when an event is streamed from the server.
|
|
18
|
+
*
|
|
19
|
+
* This option applies only if the endpoint returns a stream of events.
|
|
20
|
+
*
|
|
21
|
+
* @param event Event streamed from the server.
|
|
22
|
+
* @returns Nothing (void).
|
|
23
|
+
*/
|
|
24
|
+
onSseEvent?: (event: StreamEvent<TData>) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Default retry delay in milliseconds.
|
|
27
|
+
*
|
|
28
|
+
* This option applies only if the endpoint returns a stream of events.
|
|
29
|
+
*
|
|
30
|
+
* @default 3000
|
|
31
|
+
*/
|
|
32
|
+
sseDefaultRetryDelay?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Maximum number of retry attempts before giving up.
|
|
35
|
+
*/
|
|
36
|
+
sseMaxRetryAttempts?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Maximum retry delay in milliseconds.
|
|
39
|
+
*
|
|
40
|
+
* Applies only when exponential backoff is used.
|
|
41
|
+
*
|
|
42
|
+
* This option applies only if the endpoint returns a stream of events.
|
|
43
|
+
*
|
|
44
|
+
* @default 30000
|
|
45
|
+
*/
|
|
46
|
+
sseMaxRetryDelay?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Optional sleep function for retry backoff.
|
|
49
|
+
*
|
|
50
|
+
* Defaults to using `setTimeout`.
|
|
51
|
+
*/
|
|
52
|
+
sseSleepFn?: (ms: number) => Promise<void>;
|
|
53
|
+
url: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export interface StreamEvent<TData = unknown> {
|
|
57
|
+
data: TData;
|
|
58
|
+
event?: string;
|
|
59
|
+
id?: string;
|
|
60
|
+
retry?: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type ServerSentEventsResult<
|
|
64
|
+
TData = unknown,
|
|
65
|
+
TReturn = void,
|
|
66
|
+
TNext = unknown,
|
|
67
|
+
> = {
|
|
68
|
+
stream: AsyncGenerator<
|
|
69
|
+
TData extends Record<string, unknown> ? TData[keyof TData] : TData,
|
|
70
|
+
TReturn,
|
|
71
|
+
TNext
|
|
72
|
+
>;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const createSseClient = <TData = unknown>({
|
|
76
|
+
onSseError,
|
|
77
|
+
onSseEvent,
|
|
78
|
+
responseTransformer,
|
|
79
|
+
responseValidator,
|
|
80
|
+
sseDefaultRetryDelay,
|
|
81
|
+
sseMaxRetryAttempts,
|
|
82
|
+
sseMaxRetryDelay,
|
|
83
|
+
sseSleepFn,
|
|
84
|
+
url,
|
|
85
|
+
...options
|
|
86
|
+
}: ServerSentEventsOptions): ServerSentEventsResult<TData> => {
|
|
87
|
+
let lastEventId: string | undefined;
|
|
88
|
+
|
|
89
|
+
const sleep =
|
|
90
|
+
sseSleepFn ??
|
|
91
|
+
((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
92
|
+
|
|
93
|
+
const createStream = async function* () {
|
|
94
|
+
let retryDelay: number = sseDefaultRetryDelay ?? 3000;
|
|
95
|
+
let attempt = 0;
|
|
96
|
+
const signal = options.signal ?? new AbortController().signal;
|
|
97
|
+
|
|
98
|
+
while (true) {
|
|
99
|
+
if (signal.aborted) break;
|
|
100
|
+
|
|
101
|
+
attempt++;
|
|
102
|
+
|
|
103
|
+
const headers =
|
|
104
|
+
options.headers instanceof Headers
|
|
105
|
+
? options.headers
|
|
106
|
+
: new Headers(options.headers as Record<string, string> | undefined);
|
|
107
|
+
|
|
108
|
+
if (lastEventId !== undefined) {
|
|
109
|
+
headers.set('Last-Event-ID', lastEventId);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const response = await fetch(url, { ...options, headers, signal });
|
|
114
|
+
|
|
115
|
+
if (!response.ok)
|
|
116
|
+
throw new Error(
|
|
117
|
+
`SSE failed: ${response.status} ${response.statusText}`,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
if (!response.body) throw new Error('No body in SSE response');
|
|
121
|
+
|
|
122
|
+
const reader = response.body
|
|
123
|
+
.pipeThrough(new TextDecoderStream())
|
|
124
|
+
.getReader();
|
|
125
|
+
|
|
126
|
+
let buffer = '';
|
|
127
|
+
|
|
128
|
+
const abortHandler = () => {
|
|
129
|
+
try {
|
|
130
|
+
reader.cancel();
|
|
131
|
+
} catch {
|
|
132
|
+
// noop
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
signal.addEventListener('abort', abortHandler);
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
while (true) {
|
|
140
|
+
const { done, value } = await reader.read();
|
|
141
|
+
if (done) break;
|
|
142
|
+
buffer += value;
|
|
143
|
+
|
|
144
|
+
const chunks = buffer.split('\n\n');
|
|
145
|
+
buffer = chunks.pop() ?? '';
|
|
146
|
+
|
|
147
|
+
for (const chunk of chunks) {
|
|
148
|
+
const lines = chunk.split('\n');
|
|
149
|
+
const dataLines: Array<string> = [];
|
|
150
|
+
let eventName: string | undefined;
|
|
151
|
+
|
|
152
|
+
for (const line of lines) {
|
|
153
|
+
if (line.startsWith('data:')) {
|
|
154
|
+
dataLines.push(line.replace(/^data:\s*/, ''));
|
|
155
|
+
} else if (line.startsWith('event:')) {
|
|
156
|
+
eventName = line.replace(/^event:\s*/, '');
|
|
157
|
+
} else if (line.startsWith('id:')) {
|
|
158
|
+
lastEventId = line.replace(/^id:\s*/, '');
|
|
159
|
+
} else if (line.startsWith('retry:')) {
|
|
160
|
+
const parsed = Number.parseInt(
|
|
161
|
+
line.replace(/^retry:\s*/, ''),
|
|
162
|
+
10,
|
|
163
|
+
);
|
|
164
|
+
if (!Number.isNaN(parsed)) {
|
|
165
|
+
retryDelay = parsed;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
let data: unknown;
|
|
171
|
+
let parsedJson = false;
|
|
172
|
+
|
|
173
|
+
if (dataLines.length) {
|
|
174
|
+
const rawData = dataLines.join('\n');
|
|
175
|
+
try {
|
|
176
|
+
data = JSON.parse(rawData);
|
|
177
|
+
parsedJson = true;
|
|
178
|
+
} catch {
|
|
179
|
+
data = rawData;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (parsedJson) {
|
|
184
|
+
if (responseValidator) {
|
|
185
|
+
await responseValidator(data);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (responseTransformer) {
|
|
189
|
+
data = await responseTransformer(data);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
onSseEvent?.({
|
|
194
|
+
data,
|
|
195
|
+
event: eventName,
|
|
196
|
+
id: lastEventId,
|
|
197
|
+
retry: retryDelay,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
if (dataLines.length) {
|
|
201
|
+
yield data as any;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
} finally {
|
|
206
|
+
signal.removeEventListener('abort', abortHandler);
|
|
207
|
+
reader.releaseLock();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
break; // exit loop on normal completion
|
|
211
|
+
} catch (error) {
|
|
212
|
+
// connection failed or aborted; retry after delay
|
|
213
|
+
onSseError?.(error);
|
|
214
|
+
|
|
215
|
+
if (
|
|
216
|
+
sseMaxRetryAttempts !== undefined &&
|
|
217
|
+
attempt >= sseMaxRetryAttempts
|
|
218
|
+
) {
|
|
219
|
+
break; // stop after firing error
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// exponential backoff: double retry each attempt, cap at 30s
|
|
223
|
+
const backoff = Math.min(
|
|
224
|
+
retryDelay * 2 ** (attempt - 1),
|
|
225
|
+
sseMaxRetryDelay ?? 30000,
|
|
226
|
+
);
|
|
227
|
+
await sleep(backoff);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const stream = createStream();
|
|
233
|
+
|
|
234
|
+
return { stream };
|
|
235
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { QuerySerializer } from './bodySerializer';
|
|
2
|
+
import {
|
|
3
|
+
type ArraySeparatorStyle,
|
|
4
|
+
serializeArrayParam,
|
|
5
|
+
serializeObjectParam,
|
|
6
|
+
serializePrimitiveParam,
|
|
7
|
+
} from './pathSerializer';
|
|
8
|
+
|
|
9
|
+
export interface PathSerializer {
|
|
10
|
+
path: Record<string, unknown>;
|
|
11
|
+
url: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
15
|
+
|
|
16
|
+
export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => {
|
|
17
|
+
let url = _url;
|
|
18
|
+
const matches = _url.match(PATH_PARAM_RE);
|
|
19
|
+
if (matches) {
|
|
20
|
+
for (const match of matches) {
|
|
21
|
+
let explode = false;
|
|
22
|
+
let name = match.substring(1, match.length - 1);
|
|
23
|
+
let style: ArraySeparatorStyle = 'simple';
|
|
24
|
+
|
|
25
|
+
if (name.endsWith('*')) {
|
|
26
|
+
explode = true;
|
|
27
|
+
name = name.substring(0, name.length - 1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (name.startsWith('.')) {
|
|
31
|
+
name = name.substring(1);
|
|
32
|
+
style = 'label';
|
|
33
|
+
} else if (name.startsWith(';')) {
|
|
34
|
+
name = name.substring(1);
|
|
35
|
+
style = 'matrix';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const value = path[name];
|
|
39
|
+
|
|
40
|
+
if (value === undefined || value === null) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (Array.isArray(value)) {
|
|
45
|
+
url = url.replace(
|
|
46
|
+
match,
|
|
47
|
+
serializeArrayParam({ explode, name, style, value }),
|
|
48
|
+
);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (typeof value === 'object') {
|
|
53
|
+
url = url.replace(
|
|
54
|
+
match,
|
|
55
|
+
serializeObjectParam({
|
|
56
|
+
explode,
|
|
57
|
+
name,
|
|
58
|
+
style,
|
|
59
|
+
value: value as Record<string, unknown>,
|
|
60
|
+
valueOnly: true,
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (style === 'matrix') {
|
|
67
|
+
url = url.replace(
|
|
68
|
+
match,
|
|
69
|
+
`;${serializePrimitiveParam({
|
|
70
|
+
name,
|
|
71
|
+
value: value as string,
|
|
72
|
+
})}`,
|
|
73
|
+
);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const replaceValue = encodeURIComponent(
|
|
78
|
+
style === 'label' ? `.${value as string}` : (value as string),
|
|
79
|
+
);
|
|
80
|
+
url = url.replace(match, replaceValue);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return url;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const getUrl = ({
|
|
87
|
+
baseUrl,
|
|
88
|
+
path,
|
|
89
|
+
query,
|
|
90
|
+
querySerializer,
|
|
91
|
+
url: _url,
|
|
92
|
+
}: {
|
|
93
|
+
baseUrl?: string;
|
|
94
|
+
path?: Record<string, unknown>;
|
|
95
|
+
query?: Record<string, unknown>;
|
|
96
|
+
querySerializer: QuerySerializer;
|
|
97
|
+
url: string;
|
|
98
|
+
}) => {
|
|
99
|
+
const pathUrl = _url.startsWith('/') ? _url : `/${_url}`;
|
|
100
|
+
let url = (baseUrl ?? '') + pathUrl;
|
|
101
|
+
if (path) {
|
|
102
|
+
url = defaultPathSerializer({ path, url });
|
|
103
|
+
}
|
|
104
|
+
let search = query ? querySerializer(query) : '';
|
|
105
|
+
if (search.startsWith('?')) {
|
|
106
|
+
search = search.substring(1);
|
|
107
|
+
}
|
|
108
|
+
if (search) {
|
|
109
|
+
url += `?${search}`;
|
|
110
|
+
}
|
|
111
|
+
return url;
|
|
112
|
+
};
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createSseClient } from '../core/serverSentEvents';
|
|
2
|
+
import type {
|
|
3
|
+
Client,
|
|
4
|
+
Config,
|
|
5
|
+
RequestOptions,
|
|
6
|
+
ResolvedRequestOptions,
|
|
7
|
+
} from './types';
|
|
2
8
|
import {
|
|
3
9
|
buildUrl,
|
|
4
10
|
createConfig,
|
|
@@ -31,7 +37,7 @@ export const createClient = (config: Config = {}): Client => {
|
|
|
31
37
|
ResolvedRequestOptions
|
|
32
38
|
>();
|
|
33
39
|
|
|
34
|
-
const
|
|
40
|
+
const beforeRequest = async (options: RequestOptions) => {
|
|
35
41
|
const opts = {
|
|
36
42
|
..._config,
|
|
37
43
|
...options,
|
|
@@ -61,6 +67,13 @@ export const createClient = (config: Config = {}): Client => {
|
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
const url = buildUrl(opts);
|
|
70
|
+
|
|
71
|
+
return { opts, url };
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const request: Client['request'] = async (options) => {
|
|
75
|
+
// @ts-expect-error
|
|
76
|
+
const { opts, url } = await beforeRequest(options);
|
|
64
77
|
const requestInit: ReqInit = {
|
|
65
78
|
redirect: 'follow',
|
|
66
79
|
...opts,
|
|
@@ -178,20 +191,35 @@ export const createClient = (config: Config = {}): Client => {
|
|
|
178
191
|
};
|
|
179
192
|
};
|
|
180
193
|
|
|
194
|
+
const makeMethod = (method: Required<Config>['method']) => {
|
|
195
|
+
const fn = (options: RequestOptions) => request({ ...options, method });
|
|
196
|
+
fn.sse = async (options: RequestOptions) => {
|
|
197
|
+
const { opts, url } = await beforeRequest(options);
|
|
198
|
+
return createSseClient({
|
|
199
|
+
...opts,
|
|
200
|
+
body: opts.body as BodyInit | null | undefined,
|
|
201
|
+
headers: opts.headers as unknown as Record<string, string>,
|
|
202
|
+
method,
|
|
203
|
+
url,
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
return fn;
|
|
207
|
+
};
|
|
208
|
+
|
|
181
209
|
return {
|
|
182
210
|
buildUrl,
|
|
183
|
-
connect: (
|
|
184
|
-
delete: (
|
|
185
|
-
get: (
|
|
211
|
+
connect: makeMethod('CONNECT'),
|
|
212
|
+
delete: makeMethod('DELETE'),
|
|
213
|
+
get: makeMethod('GET'),
|
|
186
214
|
getConfig,
|
|
187
|
-
head: (
|
|
215
|
+
head: makeMethod('HEAD'),
|
|
188
216
|
interceptors,
|
|
189
|
-
options: (
|
|
190
|
-
patch: (
|
|
191
|
-
post: (
|
|
192
|
-
put: (
|
|
217
|
+
options: makeMethod('OPTIONS'),
|
|
218
|
+
patch: makeMethod('PATCH'),
|
|
219
|
+
post: makeMethod('POST'),
|
|
220
|
+
put: makeMethod('PUT'),
|
|
193
221
|
request,
|
|
194
222
|
setConfig,
|
|
195
|
-
trace: (
|
|
196
|
-
};
|
|
223
|
+
trace: makeMethod('TRACE'),
|
|
224
|
+
} as Client;
|
|
197
225
|
};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { Auth } from '../core/auth';
|
|
2
|
+
import type {
|
|
3
|
+
ServerSentEventsOptions,
|
|
4
|
+
ServerSentEventsResult,
|
|
5
|
+
} from '../core/serverSentEvents';
|
|
2
6
|
import type {
|
|
3
7
|
Client as CoreClient,
|
|
4
8
|
Config as CoreConfig,
|
|
@@ -59,13 +63,22 @@ export interface Config<T extends ClientOptions = ClientOptions>
|
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
export interface RequestOptions<
|
|
66
|
+
TData = unknown,
|
|
62
67
|
TResponseStyle extends ResponseStyle = 'fields',
|
|
63
68
|
ThrowOnError extends boolean = boolean,
|
|
64
69
|
Url extends string = string,
|
|
65
70
|
> extends Config<{
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
responseStyle: TResponseStyle;
|
|
72
|
+
throwOnError: ThrowOnError;
|
|
73
|
+
}>,
|
|
74
|
+
Pick<
|
|
75
|
+
ServerSentEventsOptions<TData>,
|
|
76
|
+
| 'onSseError'
|
|
77
|
+
| 'onSseEvent'
|
|
78
|
+
| 'sseDefaultRetryDelay'
|
|
79
|
+
| 'sseMaxRetryAttempts'
|
|
80
|
+
| 'sseMaxRetryDelay'
|
|
81
|
+
> {
|
|
69
82
|
/**
|
|
70
83
|
* Any body that you want to add to your request.
|
|
71
84
|
*
|
|
@@ -85,7 +98,7 @@ export interface ResolvedRequestOptions<
|
|
|
85
98
|
TResponseStyle extends ResponseStyle = 'fields',
|
|
86
99
|
ThrowOnError extends boolean = boolean,
|
|
87
100
|
Url extends string = string,
|
|
88
|
-
> extends RequestOptions<TResponseStyle, ThrowOnError, Url> {
|
|
101
|
+
> extends RequestOptions<unknown, TResponseStyle, ThrowOnError, Url> {
|
|
89
102
|
serializedBody?: string;
|
|
90
103
|
}
|
|
91
104
|
|
|
@@ -140,23 +153,39 @@ export interface ClientOptions {
|
|
|
140
153
|
throwOnError?: boolean;
|
|
141
154
|
}
|
|
142
155
|
|
|
143
|
-
type
|
|
156
|
+
type MethodFnBase = <
|
|
144
157
|
TData = unknown,
|
|
145
158
|
TError = unknown,
|
|
146
159
|
ThrowOnError extends boolean = false,
|
|
147
160
|
TResponseStyle extends ResponseStyle = 'fields',
|
|
148
161
|
>(
|
|
149
|
-
options: Omit<RequestOptions<TResponseStyle, ThrowOnError>, 'method'>,
|
|
162
|
+
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, 'method'>,
|
|
150
163
|
) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>;
|
|
151
164
|
|
|
165
|
+
type MethodFnServerSentEvents = <
|
|
166
|
+
TData = unknown,
|
|
167
|
+
TError = unknown,
|
|
168
|
+
ThrowOnError extends boolean = false,
|
|
169
|
+
TResponseStyle extends ResponseStyle = 'fields',
|
|
170
|
+
>(
|
|
171
|
+
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, 'method'>,
|
|
172
|
+
) => Promise<ServerSentEventsResult<TData, TError>>;
|
|
173
|
+
|
|
174
|
+
type MethodFn = MethodFnBase & {
|
|
175
|
+
sse: MethodFnServerSentEvents;
|
|
176
|
+
};
|
|
177
|
+
|
|
152
178
|
type RequestFn = <
|
|
153
179
|
TData = unknown,
|
|
154
180
|
TError = unknown,
|
|
155
181
|
ThrowOnError extends boolean = false,
|
|
156
182
|
TResponseStyle extends ResponseStyle = 'fields',
|
|
157
183
|
>(
|
|
158
|
-
options: Omit<RequestOptions<TResponseStyle, ThrowOnError>, 'method'> &
|
|
159
|
-
Pick<
|
|
184
|
+
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, 'method'> &
|
|
185
|
+
Pick<
|
|
186
|
+
Required<RequestOptions<TData, TResponseStyle, ThrowOnError>>,
|
|
187
|
+
'method'
|
|
188
|
+
>,
|
|
160
189
|
) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>;
|
|
161
190
|
|
|
162
191
|
type BuildUrlFn = <
|
|
@@ -199,9 +228,10 @@ type OmitKeys<T, K> = Pick<T, Exclude<keyof T, K>>;
|
|
|
199
228
|
export type Options<
|
|
200
229
|
TData extends TDataShape = TDataShape,
|
|
201
230
|
ThrowOnError extends boolean = boolean,
|
|
231
|
+
TResponse = unknown,
|
|
202
232
|
TResponseStyle extends ResponseStyle = 'fields',
|
|
203
233
|
> = OmitKeys<
|
|
204
|
-
RequestOptions<TResponseStyle, ThrowOnError>,
|
|
234
|
+
RequestOptions<TResponse, TResponseStyle, ThrowOnError>,
|
|
205
235
|
'body' | 'path' | 'query' | 'url'
|
|
206
236
|
> &
|
|
207
237
|
Omit<TData, 'url'>;
|
|
@@ -213,18 +243,22 @@ export type OptionsLegacyParser<
|
|
|
213
243
|
> = TData extends { body?: any }
|
|
214
244
|
? TData extends { headers?: any }
|
|
215
245
|
? OmitKeys<
|
|
216
|
-
RequestOptions<TResponseStyle, ThrowOnError>,
|
|
246
|
+
RequestOptions<unknown, TResponseStyle, ThrowOnError>,
|
|
217
247
|
'body' | 'headers' | 'url'
|
|
218
248
|
> &
|
|
219
249
|
TData
|
|
220
|
-
: OmitKeys<
|
|
250
|
+
: OmitKeys<
|
|
251
|
+
RequestOptions<unknown, TResponseStyle, ThrowOnError>,
|
|
252
|
+
'body' | 'url'
|
|
253
|
+
> &
|
|
221
254
|
TData &
|
|
222
|
-
Pick<RequestOptions<TResponseStyle, ThrowOnError>, 'headers'>
|
|
255
|
+
Pick<RequestOptions<unknown, TResponseStyle, ThrowOnError>, 'headers'>
|
|
223
256
|
: TData extends { headers?: any }
|
|
224
257
|
? OmitKeys<
|
|
225
|
-
RequestOptions<TResponseStyle, ThrowOnError>,
|
|
258
|
+
RequestOptions<unknown, TResponseStyle, ThrowOnError>,
|
|
226
259
|
'headers' | 'url'
|
|
227
260
|
> &
|
|
228
261
|
TData &
|
|
229
|
-
Pick<RequestOptions<TResponseStyle, ThrowOnError>, 'body'>
|
|
230
|
-
: OmitKeys<RequestOptions<TResponseStyle, ThrowOnError>, 'url'> &
|
|
262
|
+
Pick<RequestOptions<unknown, TResponseStyle, ThrowOnError>, 'body'>
|
|
263
|
+
: OmitKeys<RequestOptions<unknown, TResponseStyle, ThrowOnError>, 'url'> &
|
|
264
|
+
TData;
|
|
@@ -1,97 +1,14 @@
|
|
|
1
1
|
import { getAuthToken } from '../core/auth';
|
|
2
|
-
import type {
|
|
3
|
-
QuerySerializer,
|
|
4
|
-
QuerySerializerOptions,
|
|
5
|
-
} from '../core/bodySerializer';
|
|
2
|
+
import type { QuerySerializerOptions } from '../core/bodySerializer';
|
|
6
3
|
import { jsonBodySerializer } from '../core/bodySerializer';
|
|
7
4
|
import {
|
|
8
5
|
serializeArrayParam,
|
|
9
6
|
serializeObjectParam,
|
|
10
7
|
serializePrimitiveParam,
|
|
11
8
|
} from '../core/pathSerializer';
|
|
9
|
+
import { getUrl } from '../core/utils';
|
|
12
10
|
import type { Client, ClientOptions, Config, RequestOptions } from './types';
|
|
13
11
|
|
|
14
|
-
interface PathSerializer {
|
|
15
|
-
path: Record<string, unknown>;
|
|
16
|
-
url: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
20
|
-
|
|
21
|
-
type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited';
|
|
22
|
-
type MatrixStyle = 'label' | 'matrix' | 'simple';
|
|
23
|
-
type ArraySeparatorStyle = ArrayStyle | MatrixStyle;
|
|
24
|
-
|
|
25
|
-
const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => {
|
|
26
|
-
let url = _url;
|
|
27
|
-
const matches = _url.match(PATH_PARAM_RE);
|
|
28
|
-
if (matches) {
|
|
29
|
-
for (const match of matches) {
|
|
30
|
-
let explode = false;
|
|
31
|
-
let name = match.substring(1, match.length - 1);
|
|
32
|
-
let style: ArraySeparatorStyle = 'simple';
|
|
33
|
-
|
|
34
|
-
if (name.endsWith('*')) {
|
|
35
|
-
explode = true;
|
|
36
|
-
name = name.substring(0, name.length - 1);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (name.startsWith('.')) {
|
|
40
|
-
name = name.substring(1);
|
|
41
|
-
style = 'label';
|
|
42
|
-
} else if (name.startsWith(';')) {
|
|
43
|
-
name = name.substring(1);
|
|
44
|
-
style = 'matrix';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const value = path[name];
|
|
48
|
-
|
|
49
|
-
if (value === undefined || value === null) {
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (Array.isArray(value)) {
|
|
54
|
-
url = url.replace(
|
|
55
|
-
match,
|
|
56
|
-
serializeArrayParam({ explode, name, style, value }),
|
|
57
|
-
);
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (typeof value === 'object') {
|
|
62
|
-
url = url.replace(
|
|
63
|
-
match,
|
|
64
|
-
serializeObjectParam({
|
|
65
|
-
explode,
|
|
66
|
-
name,
|
|
67
|
-
style,
|
|
68
|
-
value: value as Record<string, unknown>,
|
|
69
|
-
valueOnly: true,
|
|
70
|
-
}),
|
|
71
|
-
);
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (style === 'matrix') {
|
|
76
|
-
url = url.replace(
|
|
77
|
-
match,
|
|
78
|
-
`;${serializePrimitiveParam({
|
|
79
|
-
name,
|
|
80
|
-
value: value as string,
|
|
81
|
-
})}`,
|
|
82
|
-
);
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const replaceValue = encodeURIComponent(
|
|
87
|
-
style === 'label' ? `.${value as string}` : (value as string),
|
|
88
|
-
);
|
|
89
|
-
url = url.replace(match, replaceValue);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return url;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
12
|
export const createQuerySerializer = <T = unknown>({
|
|
96
13
|
allowReserved,
|
|
97
14
|
array,
|
|
@@ -243,8 +160,8 @@ export const setAuthParams = async ({
|
|
|
243
160
|
}
|
|
244
161
|
};
|
|
245
162
|
|
|
246
|
-
export const buildUrl: Client['buildUrl'] = (options) =>
|
|
247
|
-
|
|
163
|
+
export const buildUrl: Client['buildUrl'] = (options) =>
|
|
164
|
+
getUrl({
|
|
248
165
|
baseUrl: options.baseUrl as string,
|
|
249
166
|
path: options.path,
|
|
250
167
|
query: options.query,
|
|
@@ -254,36 +171,6 @@ export const buildUrl: Client['buildUrl'] = (options) => {
|
|
|
254
171
|
: createQuerySerializer(options.querySerializer),
|
|
255
172
|
url: options.url,
|
|
256
173
|
});
|
|
257
|
-
return url;
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
export const getUrl = ({
|
|
261
|
-
baseUrl,
|
|
262
|
-
path,
|
|
263
|
-
query,
|
|
264
|
-
querySerializer,
|
|
265
|
-
url: _url,
|
|
266
|
-
}: {
|
|
267
|
-
baseUrl?: string;
|
|
268
|
-
path?: Record<string, unknown>;
|
|
269
|
-
query?: Record<string, unknown>;
|
|
270
|
-
querySerializer: QuerySerializer;
|
|
271
|
-
url: string;
|
|
272
|
-
}) => {
|
|
273
|
-
const pathUrl = _url.startsWith('/') ? _url : `/${_url}`;
|
|
274
|
-
let url = (baseUrl ?? '') + pathUrl;
|
|
275
|
-
if (path) {
|
|
276
|
-
url = defaultPathSerializer({ path, url });
|
|
277
|
-
}
|
|
278
|
-
let search = query ? querySerializer(query) : '';
|
|
279
|
-
if (search.startsWith('?')) {
|
|
280
|
-
search = search.substring(1);
|
|
281
|
-
}
|
|
282
|
-
if (search) {
|
|
283
|
-
url += `?${search}`;
|
|
284
|
-
}
|
|
285
|
-
return url;
|
|
286
|
-
};
|
|
287
174
|
|
|
288
175
|
export const mergeConfigs = (a: Config, b: Config): Config => {
|
|
289
176
|
const config = { ...a, ...b };
|