create-swagger-client 0.1.5 → 0.1.7
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/README.md +21 -13
- package/index.mjs +202 -155
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Create Swagger Client
|
|
2
2
|
|
|
3
3
|
A TypeScript tool that generates a fully type-safe REST API client from OpenAPI/Swagger specifications. Built with `openapi-typescript` and `ts-morph`, it creates a strongly-typed client class with autocomplete and compile-time type checking for all API endpoints.
|
|
4
4
|
|
|
@@ -10,6 +10,7 @@ A TypeScript tool that generates a fully type-safe REST API client from OpenAPI/
|
|
|
10
10
|
- 📝 **OpenAPI Spec Support**: Works with OpenAPI 3.x specifications (JSON or YAML)
|
|
11
11
|
- 🌐 **URL or File Input**: Generate from remote URLs or local files
|
|
12
12
|
- 🎯 **Type Inference**: Automatic extraction of path params, query params, headers, and request/response types
|
|
13
|
+
- ⏱️ **Built-in Timeout**: Default 30s timeout per request (configurable)
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
15
16
|
|
|
@@ -38,7 +39,7 @@ npx create-swagger-client https://api.example.com/openapi.json my-api-client.ts
|
|
|
38
39
|
|
|
39
40
|
**Arguments:**
|
|
40
41
|
- `source` (required): URL or file path to your OpenAPI/Swagger specification
|
|
41
|
-
- `output` (optional): Output file name (default: `client
|
|
42
|
+
- `output` (optional): Output file name (default: `swagger-client.ts`)
|
|
42
43
|
|
|
43
44
|
This will generate a file with:
|
|
44
45
|
- All TypeScript types from your OpenAPI spec
|
|
@@ -50,7 +51,7 @@ This will generate a file with:
|
|
|
50
51
|
After generation, import and use the client:
|
|
51
52
|
|
|
52
53
|
```typescript
|
|
53
|
-
import { RestApiClient } from './client
|
|
54
|
+
import { RestApiClient } from './swagger-client';
|
|
54
55
|
|
|
55
56
|
// Initialize the client
|
|
56
57
|
const api = new RestApiClient('https://api.example.com', {
|
|
@@ -129,23 +130,30 @@ const result = await api.post('/projects/{projectId}/tasks', {
|
|
|
129
130
|
|
|
130
131
|
#### Custom Fetch Options
|
|
131
132
|
|
|
133
|
+
Each method accepts an optional `RequestInit` as the third argument:
|
|
134
|
+
|
|
132
135
|
```typescript
|
|
133
|
-
const
|
|
134
|
-
headers: {
|
|
135
|
-
'Authorization': 'Bearer TOKEN'
|
|
136
|
-
},
|
|
137
|
-
mode: 'cors',
|
|
136
|
+
const users = await api.get('/users', { query: { page: 1 } }, {
|
|
138
137
|
credentials: 'include',
|
|
139
|
-
|
|
138
|
+
mode: 'cors'
|
|
140
139
|
});
|
|
141
140
|
```
|
|
142
141
|
|
|
142
|
+
#### Timeout
|
|
143
|
+
|
|
144
|
+
The client uses a default timeout of 30 seconds. You can override it in the constructor:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const api = new RestApiClient('https://api.example.com', {}, 10_000);
|
|
148
|
+
```
|
|
149
|
+
|
|
143
150
|
## Generated Types
|
|
144
151
|
|
|
145
152
|
The generator creates several useful type utilities:
|
|
146
153
|
|
|
147
154
|
- `RestMethod`: Union of HTTP methods (`"get" | "post" | "put" | "delete" | "patch"`)
|
|
148
155
|
- `KeyPaths`: All available API paths
|
|
156
|
+
- `PathsForMethod<M>`: Paths that support method `M`
|
|
149
157
|
- `ExtractPathParams<Path, Method>`: Extract path parameters for an endpoint
|
|
150
158
|
- `ExtractQueryParams<Path, Method>`: Extract query parameters for an endpoint
|
|
151
159
|
- `ExtractHeaderParams<Path, Method>`: Extract header parameters for an endpoint
|
|
@@ -161,7 +169,7 @@ import {
|
|
|
161
169
|
RestApiClient,
|
|
162
170
|
ExtractBody,
|
|
163
171
|
APIResponse
|
|
164
|
-
} from './client
|
|
172
|
+
} from './swagger-client';
|
|
165
173
|
|
|
166
174
|
// Use types in your code
|
|
167
175
|
type CreateUserBody = ExtractBody<'/users', 'post'>;
|
|
@@ -174,7 +182,7 @@ const createUser = (data: CreateUserBody): Promise<UserResponse> => {
|
|
|
174
182
|
|
|
175
183
|
## Error Handling
|
|
176
184
|
|
|
177
|
-
The client throws
|
|
185
|
+
The client throws an `ApiError` for failed requests. The error includes `status`, `statusText`, and a parsed JSON `body` when available:
|
|
178
186
|
|
|
179
187
|
```typescript
|
|
180
188
|
try {
|
|
@@ -183,7 +191,7 @@ try {
|
|
|
183
191
|
});
|
|
184
192
|
} catch (error) {
|
|
185
193
|
console.error('API request failed:', error.message);
|
|
186
|
-
//
|
|
194
|
+
// error.status, error.statusText, error.body
|
|
187
195
|
}
|
|
188
196
|
```
|
|
189
197
|
|
|
@@ -198,4 +206,4 @@ MIT
|
|
|
198
206
|
|
|
199
207
|
## Contributing
|
|
200
208
|
|
|
201
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
209
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
package/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
2
3
|
import openapiTS, { astToString } from "openapi-typescript";
|
|
3
4
|
import ora from "ora";
|
|
4
5
|
import { resolve } from "path";
|
|
@@ -24,6 +25,11 @@ async function generate() {
|
|
|
24
25
|
if (isUrl(source) === false) {
|
|
25
26
|
source = resolve(process.cwd(), source);
|
|
26
27
|
}
|
|
28
|
+
|
|
29
|
+
if (existsSync(source)) {
|
|
30
|
+
source = readFileSync(source, "utf-8");
|
|
31
|
+
}
|
|
32
|
+
|
|
27
33
|
const spinner = ora(`Generating API client from ${source}...`).start();
|
|
28
34
|
const ast = await openapiTS(source);
|
|
29
35
|
const contents = astToString(ast);
|
|
@@ -78,45 +84,31 @@ async function generate() {
|
|
|
78
84
|
isExported: true,
|
|
79
85
|
typeParameters: ["K extends KeyPaths", "M extends RestMethod"],
|
|
80
86
|
type: `paths[K][M] extends { responses: infer R }
|
|
81
|
-
? R extends {
|
|
82
|
-
content: { "application/json": infer C };
|
|
83
|
-
}
|
|
87
|
+
? R extends { "200"?: { content: { "application/json": infer C } } }
|
|
84
88
|
? C
|
|
85
|
-
: R extends {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
: never
|
|
92
|
-
: never;`,
|
|
89
|
+
: R extends { content: { "application/json": infer C } }
|
|
90
|
+
? C
|
|
91
|
+
: R extends Record<number | string, { content: { "application/json": infer C } }>
|
|
92
|
+
? C
|
|
93
|
+
: never
|
|
94
|
+
: never`,
|
|
93
95
|
});
|
|
94
96
|
sourceFile.addTypeAlias({
|
|
95
97
|
name: "ApiPayload",
|
|
96
98
|
isExported: true,
|
|
97
|
-
typeParameters: ["T extends KeyPaths", "
|
|
99
|
+
typeParameters: ["T extends KeyPaths", "M extends RestMethod"],
|
|
98
100
|
type: `{
|
|
99
|
-
path?: ExtractPathParams<T,
|
|
100
|
-
query?: ExtractQueryParams<T,
|
|
101
|
-
body?:
|
|
102
|
-
headers?: ExtractHeaderParams<T,
|
|
101
|
+
path?: ExtractPathParams<T, M>;
|
|
102
|
+
query?: ExtractQueryParams<T, M>;
|
|
103
|
+
body?: M extends "post" | "put" | "patch" ? ExtractBody<T, M> : never;
|
|
104
|
+
headers?: ExtractHeaderParams<T, M> & Record<string, string>;
|
|
103
105
|
}`,
|
|
104
106
|
});
|
|
105
107
|
sourceFile.addTypeAlias({
|
|
106
|
-
name: "
|
|
107
|
-
|
|
108
|
-
type: `{
|
|
109
|
-
[K in RestMethod]: <T extends KeyPaths>(
|
|
110
|
-
path: T,
|
|
111
|
-
payload?: ApiPayload<T, K>,
|
|
112
|
-
) => Promise<APIResponse<T, K>>;
|
|
113
|
-
}`,
|
|
114
|
-
});
|
|
115
|
-
sourceFile.addTypeAlias({
|
|
116
|
-
name: "TypePaths",
|
|
117
|
-
typeParameters: ["T extends RestMethod"],
|
|
108
|
+
name: "PathsForMethod",
|
|
109
|
+
typeParameters: ["M extends RestMethod"],
|
|
118
110
|
type: `{
|
|
119
|
-
[K in KeyPaths]: paths[K] extends
|
|
111
|
+
[K in KeyPaths]: paths[K] extends Record<M, unknown> ? K : never;
|
|
120
112
|
}[KeyPaths]`,
|
|
121
113
|
});
|
|
122
114
|
sourceFile.addClass({
|
|
@@ -125,195 +117,250 @@ async function generate() {
|
|
|
125
117
|
ctors: [
|
|
126
118
|
{
|
|
127
119
|
parameters: [
|
|
128
|
-
{ name: "
|
|
120
|
+
{ name: "baseUrl", type: "string", scope: tsMorph.Scope.Private },
|
|
129
121
|
{
|
|
130
|
-
name: "
|
|
122
|
+
name: "defaultOptions",
|
|
131
123
|
type: "RequestInit",
|
|
132
|
-
|
|
124
|
+
initializer: "{}",
|
|
125
|
+
scope: tsMorph.Scope.Private,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "defaultTimeoutMs",
|
|
129
|
+
type: "number",
|
|
130
|
+
initializer: "30000",
|
|
133
131
|
scope: tsMorph.Scope.Private,
|
|
134
132
|
},
|
|
135
133
|
],
|
|
134
|
+
statements: `this.baseUrl = baseUrl.replace(/\\/+$/, "");`,
|
|
136
135
|
},
|
|
137
136
|
],
|
|
138
137
|
methods: [
|
|
139
138
|
{
|
|
140
|
-
name: "
|
|
141
|
-
scope: tsMorph.Scope.
|
|
139
|
+
name: "buildUrl",
|
|
140
|
+
scope: tsMorph.Scope.Private,
|
|
141
|
+
typeParameters: ["P extends KeyPaths", "M extends RestMethod"],
|
|
142
|
+
parameters: [
|
|
143
|
+
{ name: "pathTemplate", type: "P" },
|
|
144
|
+
{ name: "pathParams", type: "ExtractPathParams<P, M>", hasQuestionToken: true },
|
|
145
|
+
],
|
|
146
|
+
returnType: "URL",
|
|
147
|
+
statements: `let pathname = pathTemplate as string;
|
|
148
|
+
|
|
149
|
+
if (pathParams) {
|
|
150
|
+
pathname = pathname.replace(/\\{([^}]+)\\}/g, (_, paramName) => {
|
|
151
|
+
const value = (pathParams as Record<string, unknown>)[paramName];
|
|
152
|
+
if (value === undefined || value === null) {
|
|
153
|
+
throw new Error(\`Missing required path parameter: \${paramName}\`);
|
|
154
|
+
}
|
|
155
|
+
return encodeURIComponent(String(value));
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return new URL(this.baseUrl + pathname);`,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "appendQueryParams",
|
|
163
|
+
scope: tsMorph.Scope.Private,
|
|
164
|
+
parameters: [
|
|
165
|
+
{ name: "url", type: "URL" },
|
|
166
|
+
{ name: "query", type: "Record<string, unknown>", hasQuestionToken: true },
|
|
167
|
+
],
|
|
168
|
+
returnType: "void",
|
|
169
|
+
statements: `if (!query) return;
|
|
170
|
+
|
|
171
|
+
for (const [key, value] of Object.entries(query)) {
|
|
172
|
+
if (value === undefined || value === null) continue;
|
|
173
|
+
|
|
174
|
+
if (Array.isArray(value)) {
|
|
175
|
+
value.forEach((v) => {
|
|
176
|
+
if (v !== undefined && v !== null) {
|
|
177
|
+
url.searchParams.append(key, String(v));
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
} else {
|
|
181
|
+
url.searchParams.append(key, String(value));
|
|
182
|
+
}
|
|
183
|
+
}`,
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: "prepareBody",
|
|
187
|
+
scope: tsMorph.Scope.Private,
|
|
188
|
+
typeParameters: ["M extends RestMethod"],
|
|
189
|
+
parameters: [
|
|
190
|
+
{ name: "method", type: "M" },
|
|
191
|
+
{ name: "body", type: "unknown", hasQuestionToken: true },
|
|
192
|
+
],
|
|
193
|
+
returnType: "BodyInit | null",
|
|
194
|
+
statements: `if (!body || !["post", "put", "patch"].includes(method)) return null;
|
|
195
|
+
return JSON.stringify(body);`,
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "fetchWithTimeout",
|
|
199
|
+
scope: tsMorph.Scope.Private,
|
|
142
200
|
isAsync: true,
|
|
143
201
|
parameters: [
|
|
144
202
|
{ name: "input", type: "RequestInfo" },
|
|
145
|
-
{ name: "init", type: "RequestInit"
|
|
203
|
+
{ name: "init", type: "RequestInit" },
|
|
204
|
+
{ name: "timeoutMs", type: "number" },
|
|
146
205
|
],
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
};
|
|
206
|
+
returnType: "Promise<Response>",
|
|
207
|
+
statements: `const controller = new AbortController();
|
|
208
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
151
209
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
|
|
210
|
+
try {
|
|
211
|
+
const response = await fetch(input, {
|
|
212
|
+
...init,
|
|
213
|
+
signal: controller.signal,
|
|
214
|
+
});
|
|
215
|
+
clearTimeout(timeoutId);
|
|
216
|
+
return response;
|
|
217
|
+
} catch (err) {
|
|
218
|
+
clearTimeout(timeoutId);
|
|
219
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
220
|
+
throw new Error(\`Request timed out after \${timeoutMs}ms\`);
|
|
221
|
+
}
|
|
222
|
+
throw err;
|
|
223
|
+
}`,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: "createError",
|
|
227
|
+
scope: tsMorph.Scope.Private,
|
|
228
|
+
parameters: [
|
|
229
|
+
{ name: "response", type: "Response" },
|
|
230
|
+
{ name: "body", type: "string | null" },
|
|
231
|
+
],
|
|
232
|
+
statements: `return Object.assign(new Error(), {
|
|
233
|
+
name: "ApiError",
|
|
234
|
+
message: \`API request failed: \${response.status} \${response.statusText}\`,
|
|
235
|
+
status: response.status,
|
|
236
|
+
statusText: response.statusText,
|
|
237
|
+
body: body ? JSON.parse(body) : null,
|
|
238
|
+
});`,
|
|
160
239
|
},
|
|
161
240
|
{
|
|
162
241
|
name: "request",
|
|
163
|
-
|
|
242
|
+
isAsync: true,
|
|
243
|
+
typeParameters: ["M extends RestMethod", "P extends PathsForMethod<M>"],
|
|
164
244
|
parameters: [
|
|
165
245
|
{ name: "method", type: "M" },
|
|
166
246
|
{ name: "path", type: "P" },
|
|
167
247
|
{
|
|
168
|
-
name: "
|
|
248
|
+
name: "payload",
|
|
169
249
|
type: "ApiPayload<P, M>",
|
|
170
250
|
initializer: "{} as ApiPayload<P, M>",
|
|
171
251
|
},
|
|
252
|
+
{
|
|
253
|
+
name: "options",
|
|
254
|
+
type: "RequestInit",
|
|
255
|
+
initializer: "{}",
|
|
256
|
+
},
|
|
172
257
|
],
|
|
173
258
|
returnType: "Promise<APIResponse<P, M>>",
|
|
174
|
-
statements: `const url =
|
|
259
|
+
statements: `const url = this.buildUrl(path, payload.path);
|
|
260
|
+
this.appendQueryParams(url, payload.query ?? {});
|
|
175
261
|
|
|
176
|
-
|
|
177
|
-
|
|
262
|
+
const headers = {
|
|
263
|
+
"Content-Type": "application/json",
|
|
264
|
+
...this.defaultOptions.headers,
|
|
265
|
+
...payload.headers,
|
|
266
|
+
...options.headers,
|
|
267
|
+
};
|
|
178
268
|
|
|
179
269
|
const requestInit: RequestInit = {
|
|
180
270
|
method: method.toUpperCase(),
|
|
181
|
-
...this.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
},
|
|
186
|
-
body: this.prepareBody(method, init.body),
|
|
271
|
+
...this.defaultOptions,
|
|
272
|
+
...options,
|
|
273
|
+
headers,
|
|
274
|
+
body: this.prepareBody(method, payload.body),
|
|
187
275
|
};
|
|
188
276
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
277
|
+
const response = await this.fetchWithTimeout(
|
|
278
|
+
url.toString(),
|
|
279
|
+
requestInit,
|
|
280
|
+
this.defaultTimeoutMs,
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
let responseBody: unknown;
|
|
284
|
+
|
|
285
|
+
const contentType = response.headers.get("content-type") || "";
|
|
286
|
+
if (contentType.includes("application/json")) {
|
|
287
|
+
responseBody = await response.json();
|
|
288
|
+
} else {
|
|
289
|
+
responseBody = await response.text();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (!response.ok) {
|
|
293
|
+
throw this.createError(
|
|
294
|
+
response,
|
|
295
|
+
typeof responseBody === "string" ? responseBody : null,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return responseBody as APIResponse<P, M>;`,
|
|
192
300
|
},
|
|
193
301
|
{
|
|
194
302
|
name: "get",
|
|
195
303
|
scope: tsMorph.Scope.Public,
|
|
196
|
-
typeParameters: ['
|
|
304
|
+
typeParameters: ['P extends PathsForMethod<"get">'],
|
|
197
305
|
parameters: [
|
|
198
|
-
{ name: "path", type: "
|
|
199
|
-
{
|
|
200
|
-
|
|
201
|
-
type: 'ApiPayload<T, "get">',
|
|
202
|
-
hasQuestionToken: true,
|
|
203
|
-
},
|
|
306
|
+
{ name: "path", type: "P" },
|
|
307
|
+
{ name: "payload", type: 'ApiPayload<P, "get">', hasQuestionToken: true },
|
|
308
|
+
{ name: "options", type: "RequestInit", hasQuestionToken: true },
|
|
204
309
|
],
|
|
205
|
-
returnType: 'Promise<APIResponse<
|
|
206
|
-
statements: 'return this.request("get", path, payload);',
|
|
310
|
+
returnType: 'Promise<APIResponse<P, "get">>',
|
|
311
|
+
statements: 'return this.request("get", path, payload, options);',
|
|
207
312
|
},
|
|
208
313
|
{
|
|
209
314
|
name: "post",
|
|
210
315
|
scope: tsMorph.Scope.Public,
|
|
211
|
-
typeParameters: ['
|
|
316
|
+
typeParameters: ['P extends PathsForMethod<"post">'],
|
|
212
317
|
parameters: [
|
|
213
|
-
{ name: "path", type: "
|
|
214
|
-
{
|
|
215
|
-
|
|
216
|
-
type: 'ApiPayload<T, "post">',
|
|
217
|
-
hasQuestionToken: true,
|
|
218
|
-
},
|
|
318
|
+
{ name: "path", type: "P" },
|
|
319
|
+
{ name: "payload", type: 'ApiPayload<P, "post">', hasQuestionToken: true },
|
|
320
|
+
{ name: "options", type: "RequestInit", hasQuestionToken: true },
|
|
219
321
|
],
|
|
220
|
-
returnType: 'Promise<APIResponse<
|
|
221
|
-
statements: 'return this.request("post", path, payload);',
|
|
322
|
+
returnType: 'Promise<APIResponse<P, "post">>',
|
|
323
|
+
statements: 'return this.request("post", path, payload, options);',
|
|
222
324
|
},
|
|
223
325
|
{
|
|
224
326
|
name: "put",
|
|
225
327
|
scope: tsMorph.Scope.Public,
|
|
226
|
-
typeParameters: ['
|
|
227
|
-
parameters: [
|
|
228
|
-
{ name: "path", type: "T" },
|
|
229
|
-
{
|
|
230
|
-
name: "payload",
|
|
231
|
-
type: 'ApiPayload<T, "put">',
|
|
232
|
-
hasQuestionToken: true,
|
|
233
|
-
},
|
|
234
|
-
],
|
|
235
|
-
returnType: 'Promise<APIResponse<T, "put">>',
|
|
236
|
-
statements: 'return this.request("put", path, payload);',
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
name: "delete",
|
|
240
|
-
scope: tsMorph.Scope.Public,
|
|
241
|
-
typeParameters: ['T extends TypePaths<"delete">'],
|
|
328
|
+
typeParameters: ['P extends PathsForMethod<"put">'],
|
|
242
329
|
parameters: [
|
|
243
|
-
{ name: "path", type: "
|
|
244
|
-
{
|
|
245
|
-
|
|
246
|
-
type: 'ApiPayload<T, "delete">',
|
|
247
|
-
hasQuestionToken: true,
|
|
248
|
-
},
|
|
330
|
+
{ name: "path", type: "P" },
|
|
331
|
+
{ name: "payload", type: 'ApiPayload<P, "put">', hasQuestionToken: true },
|
|
332
|
+
{ name: "options", type: "RequestInit", hasQuestionToken: true },
|
|
249
333
|
],
|
|
250
|
-
returnType: 'Promise<APIResponse<
|
|
251
|
-
statements: 'return this.request("
|
|
334
|
+
returnType: 'Promise<APIResponse<P, "put">>',
|
|
335
|
+
statements: 'return this.request("put", path, payload, options);',
|
|
252
336
|
},
|
|
253
337
|
{
|
|
254
338
|
name: "patch",
|
|
255
339
|
scope: tsMorph.Scope.Public,
|
|
256
|
-
typeParameters: ['
|
|
257
|
-
parameters: [
|
|
258
|
-
{ name: "path", type: "T" },
|
|
259
|
-
{
|
|
260
|
-
name: "payload",
|
|
261
|
-
type: 'ApiPayload<T, "patch">',
|
|
262
|
-
hasQuestionToken: true,
|
|
263
|
-
},
|
|
264
|
-
],
|
|
265
|
-
returnType: 'Promise<APIResponse<T, "patch">>',
|
|
266
|
-
statements: 'return this.request("patch", path, payload);',
|
|
267
|
-
},
|
|
268
|
-
{
|
|
269
|
-
name: "buildPathUrl",
|
|
270
|
-
scope: tsMorph.Scope.Private,
|
|
340
|
+
typeParameters: ['P extends PathsForMethod<"patch">'],
|
|
271
341
|
parameters: [
|
|
272
|
-
{ name: "
|
|
273
|
-
{ name: "
|
|
274
|
-
|
|
275
|
-
returnType: "string",
|
|
276
|
-
statements: `let pathname = basePath;
|
|
277
|
-
if (pathParams != null) {
|
|
278
|
-
const params = pathParams as Record<string, unknown>;
|
|
279
|
-
pathname = decodeURIComponent(pathname).replace(/{(w+)}/g, (_, key) =>
|
|
280
|
-
encodeURIComponent(String(params[key])),
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
return pathname;`,
|
|
284
|
-
},
|
|
285
|
-
{
|
|
286
|
-
name: "prepareBody",
|
|
287
|
-
scope: tsMorph.Scope.Private,
|
|
288
|
-
parameters: [
|
|
289
|
-
{ name: "method", type: "RestMethod" },
|
|
290
|
-
{ name: "body", type: "unknown", hasQuestionToken: true },
|
|
342
|
+
{ name: "path", type: "P" },
|
|
343
|
+
{ name: "payload", type: 'ApiPayload<P, "patch">', hasQuestionToken: true },
|
|
344
|
+
{ name: "options", type: "RequestInit", hasQuestionToken: true },
|
|
291
345
|
],
|
|
292
|
-
returnType:
|
|
293
|
-
statements:
|
|
294
|
-
return JSON.stringify(body);
|
|
295
|
-
}
|
|
296
|
-
return undefined;`,
|
|
346
|
+
returnType: 'Promise<APIResponse<P, "patch">>',
|
|
347
|
+
statements: 'return this.request("patch", path, payload, options);',
|
|
297
348
|
},
|
|
298
349
|
{
|
|
299
|
-
name: "
|
|
300
|
-
scope: tsMorph.Scope.
|
|
350
|
+
name: "delete",
|
|
351
|
+
scope: tsMorph.Scope.Public,
|
|
352
|
+
typeParameters: ['P extends PathsForMethod<"delete">'],
|
|
301
353
|
parameters: [
|
|
302
|
-
{ name: "
|
|
303
|
-
{ name: "
|
|
354
|
+
{ name: "path", type: "P" },
|
|
355
|
+
{ name: "payload", type: 'ApiPayload<P, "delete">', hasQuestionToken: true },
|
|
356
|
+
{ name: "options", type: "RequestInit", hasQuestionToken: true },
|
|
304
357
|
],
|
|
305
|
-
returnType: "
|
|
306
|
-
statements:
|
|
307
|
-
const params = queryParams as Record<string, unknown>;
|
|
308
|
-
for (const [key, value] of Object.entries(params)) {
|
|
309
|
-
if (value !== undefined && value !== null) {
|
|
310
|
-
url.searchParams.append(key, String(value));
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}`,
|
|
358
|
+
returnType: 'Promise<APIResponse<P, "delete">>',
|
|
359
|
+
statements: 'return this.request("delete", path, payload, options);',
|
|
314
360
|
},
|
|
315
361
|
],
|
|
316
362
|
});
|
|
363
|
+
|
|
317
364
|
await sourceFile.formatText();
|
|
318
365
|
await project.save();
|
|
319
366
|
spinner.stopAndPersist({
|