typedapi-client-helpers 0.1.11 → 0.1.12
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 +189 -57
- package/package.json +1 -1
- package/scripts/generate-api-minimal.mjs +132 -154
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ Configuration can be supplied in the `config` section of your `package.json`, wi
|
|
|
68
68
|
| `apiOutput` | `src/api` | Directory where the generated API files are written. |
|
|
69
69
|
| `typedApiUseTypeOnlyImports` | `false` | Generates `import type` statements for imports that are only used as types. |
|
|
70
70
|
| `typedApiUseFilterFormValues` | `false` | Generates paginated methods that accept `FilterFormValues[]`, page values, and sort values instead of a prebuilt query object. |
|
|
71
|
-
| `typedApiDefaultFunctionsPath` | `
|
|
71
|
+
| `typedApiDefaultFunctionsPath` | `defaultApiFunctions` | Import path used by generated method files for shared success and error handlers. |
|
|
72
72
|
| `typedApiDefaultSuccessHandler` | `handleGoodResult` | Name of the default success handler function imported into generated methods. |
|
|
73
73
|
| `typedApiDefaultErrorHandler` | `handleErrors` | Name of the default error handler function imported into generated methods. |
|
|
74
74
|
|
|
@@ -118,12 +118,12 @@ src/
|
|
|
118
118
|
└── index.ts
|
|
119
119
|
```
|
|
120
120
|
|
|
121
|
-
| Folder or file | Description
|
|
122
|
-
| ------------------------ |
|
|
123
|
-
| `generated/` | Raw client, models,
|
|
124
|
-
| `methods/` | Typed wrapper methods generated by this package. These call the raw generated client and return `ApiResult<T>`.
|
|
125
|
-
| `index.ts` | Barrel file that exports generated contracts, HTTP client types, and wrapper methods.
|
|
126
|
-
| `defaultApiFunctions.ts` | Optional shared success and error handlers created when the configured default handler file does not exist.
|
|
121
|
+
| Folder or file | Description |
|
|
122
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
|
|
123
|
+
| `generated/` | Raw client, models, and HTTP client generated by `swagger-typescript-api`. Route type files are removed after generation. |
|
|
124
|
+
| `methods/` | Typed wrapper methods generated by this package. These call the raw generated client and return `ApiResult<T>`. |
|
|
125
|
+
| `index.ts` | Barrel file that exports generated contracts, HTTP client types, and wrapper methods. |
|
|
126
|
+
| `defaultApiFunctions.ts` | Optional shared success and error handlers created when the configured default handler file does not exist. |
|
|
127
127
|
|
|
128
128
|
## Calling generated methods
|
|
129
129
|
|
|
@@ -148,36 +148,84 @@ Checking `result.ok` narrows the result automatically.
|
|
|
148
148
|
|
|
149
149
|
## Generated method argument order
|
|
150
150
|
|
|
151
|
-
Generated wrappers preserve the original generated API arguments and then add optional
|
|
151
|
+
Generated wrappers preserve the original generated API arguments and then add an optional method options object.
|
|
152
152
|
|
|
153
153
|
For query methods:
|
|
154
154
|
|
|
155
155
|
```ts
|
|
156
|
-
await getSuppliers(query,
|
|
156
|
+
await getSuppliers(query, {
|
|
157
|
+
onSuccess,
|
|
158
|
+
onError,
|
|
159
|
+
params,
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
For query methods without a query object, pass `undefined` as the first argument when you want to provide options:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
await exportPartners(undefined, {
|
|
167
|
+
onError: handleError,
|
|
168
|
+
});
|
|
157
169
|
```
|
|
158
170
|
|
|
159
171
|
For non-query methods:
|
|
160
172
|
|
|
161
173
|
```ts
|
|
162
|
-
await updateSupplier(id, body,
|
|
174
|
+
await updateSupplier(id, body, {
|
|
175
|
+
onSuccess,
|
|
176
|
+
onError,
|
|
177
|
+
params,
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
For multipart/form-data methods:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
await uploadSupplierLogo(data, {
|
|
185
|
+
onSuccess,
|
|
186
|
+
onError,
|
|
187
|
+
params,
|
|
188
|
+
});
|
|
163
189
|
```
|
|
164
190
|
|
|
165
191
|
For filter-form paginated methods when `typedApiUseFilterFormValues` is enabled:
|
|
166
192
|
|
|
167
193
|
```ts
|
|
168
|
-
await getSuppliers(
|
|
169
|
-
filters,
|
|
170
|
-
page,
|
|
171
|
-
pageSize,
|
|
172
|
-
sortBy,
|
|
173
|
-
sortDirection,
|
|
194
|
+
await getSuppliers(filters, page, pageSize, sortBy, sortDirection, {
|
|
174
195
|
onSuccess,
|
|
175
196
|
onError,
|
|
176
197
|
params,
|
|
177
|
-
);
|
|
198
|
+
});
|
|
178
199
|
```
|
|
179
200
|
|
|
180
|
-
The
|
|
201
|
+
The `params` property is forwarded to the generated HTTP client and can contain values such as headers or other request options.
|
|
202
|
+
|
|
203
|
+
Every generated wrapper method gets its own options type. The type name is based on the method name.
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
export type GetSuppliersOptions = {
|
|
207
|
+
onSuccess?: ApiSuccessHandler<GetSuppliersResponse>;
|
|
208
|
+
onError?: ApiErrorHandler<GetSuppliersError>;
|
|
209
|
+
params?: RequestParams;
|
|
210
|
+
};
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Examples of generated option type names:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
GetSuppliersOptions;
|
|
217
|
+
CreateSupplierOptions;
|
|
218
|
+
UpdateSupplierOptions;
|
|
219
|
+
ExportPartnersOptions;
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
This makes it possible to pass only `onError` without also passing `onSuccess`.
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
await getSuppliers(undefined, {
|
|
226
|
+
onError: handleError,
|
|
227
|
+
});
|
|
228
|
+
```
|
|
181
229
|
|
|
182
230
|
## Success and error callbacks
|
|
183
231
|
|
|
@@ -195,28 +243,98 @@ function onError(result: ApiErrorResult<SupplierListResponse>) {
|
|
|
195
243
|
console.error(result.status, result.error);
|
|
196
244
|
}
|
|
197
245
|
|
|
198
|
-
await getSuppliers(
|
|
246
|
+
await getSuppliers(
|
|
247
|
+
{ pageNumber: 1, pageSize: 25 },
|
|
248
|
+
{
|
|
249
|
+
onSuccess,
|
|
250
|
+
onError,
|
|
251
|
+
},
|
|
252
|
+
);
|
|
199
253
|
```
|
|
200
254
|
|
|
201
255
|
The generated method still returns `ApiResult<T>` after callbacks run.
|
|
202
256
|
|
|
257
|
+
You can also pass only one callback.
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
await getSuppliers(
|
|
261
|
+
{ pageNumber: 1, pageSize: 25 },
|
|
262
|
+
{
|
|
263
|
+
onError,
|
|
264
|
+
},
|
|
265
|
+
);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
When default handlers are configured, generated methods use them when no method-specific callback is supplied.
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
await getSuppliers(
|
|
272
|
+
{ pageNumber: 1, pageSize: 25 },
|
|
273
|
+
{
|
|
274
|
+
onSuccess: customSuccessHandler,
|
|
275
|
+
},
|
|
276
|
+
);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
In this example, the custom success handler is used for success results, and the configured default error handler is still used for error results.
|
|
280
|
+
|
|
203
281
|
## Custom request parameters
|
|
204
282
|
|
|
205
|
-
The
|
|
283
|
+
The method options object can contain request parameters supported by the generated HTTP client.
|
|
206
284
|
|
|
207
285
|
```ts
|
|
208
286
|
const result = await getSuppliers(
|
|
209
287
|
{ pageNumber: 1, pageSize: 25 },
|
|
210
|
-
undefined,
|
|
211
|
-
undefined,
|
|
212
288
|
{
|
|
213
|
-
|
|
214
|
-
|
|
289
|
+
params: {
|
|
290
|
+
headers: {
|
|
291
|
+
Authorization: `Bearer ${token}`,
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Request parameters can also be combined with callbacks.
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
const result = await getSuppliers(
|
|
302
|
+
{ pageNumber: 1, pageSize: 25 },
|
|
303
|
+
{
|
|
304
|
+
onError: handleError,
|
|
305
|
+
params: {
|
|
306
|
+
headers: {
|
|
307
|
+
Authorization: `Bearer ${token}`,
|
|
308
|
+
},
|
|
215
309
|
},
|
|
216
310
|
},
|
|
217
311
|
);
|
|
218
312
|
```
|
|
219
313
|
|
|
314
|
+
## Query methods without required filters
|
|
315
|
+
|
|
316
|
+
Some generated API methods require a query object at the raw generated client level, even when all query properties are optional.
|
|
317
|
+
|
|
318
|
+
The wrapper methods allow the query argument to be omitted. Internally, they pass an empty object to the raw generated method.
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
await exportPartners();
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
is handled like:
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
await exportPartners({});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
When you want to pass options but no query, pass `undefined` as the first argument.
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
await exportPartners(undefined, {
|
|
334
|
+
onError: handleError,
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
220
338
|
## Filter-form generation
|
|
221
339
|
|
|
222
340
|
Set `typedApiUseFilterFormValues` to `true` to generate paginated methods that accept filter form values.
|
|
@@ -248,6 +366,19 @@ const filters: FilterFormValues<GetSuppliersQuery>[] = [
|
|
|
248
366
|
const result = await getSuppliers(filters, 1, 25, "name", "Ascending");
|
|
249
367
|
```
|
|
250
368
|
|
|
369
|
+
You can also pass method options as the final argument.
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
const result = await getSuppliers(filters, 1, 25, "name", "Ascending", {
|
|
373
|
+
onError: handleError,
|
|
374
|
+
params: {
|
|
375
|
+
headers: {
|
|
376
|
+
Authorization: `Bearer ${token}`,
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
```
|
|
381
|
+
|
|
251
382
|
Empty filter values are ignored by `buildQuery`.
|
|
252
383
|
|
|
253
384
|
## Multipart uploads
|
|
@@ -278,29 +409,30 @@ Arrays are appended as repeated fields, files keep their filename, primitives be
|
|
|
278
409
|
|
|
279
410
|
### Filter and sort types
|
|
280
411
|
|
|
281
|
-
| Export | Description
|
|
282
|
-
| -------------------------- |
|
|
283
|
-
| `FilterType` | Union of supported filter input types
|
|
284
|
-
| `OptionValue` | Option object with a display `name` and submitted `value`.
|
|
285
|
-
| `FilterFormValues<TQuery>` | Configuration object for one filter field mapped to a query property.
|
|
286
|
-
| `SortType` | UI sort state: Default, Neutral, Ascending, or Descending.
|
|
287
|
-
| `SortDirection` | Sort direction value accepted by generated query objects. Supports string or number values.
|
|
288
|
-
| `ApiSortDirection` | Sort direction values accepted by the generated API: Default, Ascending, or Descending.
|
|
289
|
-
| `sortTypes` | Constant list of all supported UI sort states.
|
|
412
|
+
| Export | Description |
|
|
413
|
+
| -------------------------- | ------------------------------------------------------------------------------------------- |
|
|
414
|
+
| `FilterType` | Union of supported filter input types used by generated filter-form methods. |
|
|
415
|
+
| `OptionValue` | Option object with a display `name` and submitted `value`. |
|
|
416
|
+
| `FilterFormValues<TQuery>` | Configuration object for one filter field mapped to a query property. |
|
|
417
|
+
| `SortType` | UI sort state: Default, Neutral, Ascending, or Descending. |
|
|
418
|
+
| `SortDirection` | Sort direction value accepted by generated query objects. Supports string or number values. |
|
|
419
|
+
| `ApiSortDirection` | Sort direction values accepted by the generated API: Default, Ascending, or Descending. |
|
|
420
|
+
| `sortTypes` | Constant list of all supported UI sort states. |
|
|
290
421
|
|
|
291
422
|
### Callback and method types
|
|
292
423
|
|
|
293
|
-
| Export
|
|
294
|
-
|
|
|
295
|
-
| `ApiSuccessHandler<TResponse>`
|
|
296
|
-
| `ApiErrorHandler<TResponse>`
|
|
297
|
-
| `ApiMethodCallbacks<TResponse>`
|
|
298
|
-
| `
|
|
299
|
-
| `
|
|
300
|
-
| `
|
|
301
|
-
| `
|
|
302
|
-
| `
|
|
303
|
-
| `
|
|
424
|
+
| Export | Description |
|
|
425
|
+
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
426
|
+
| `ApiSuccessHandler<TResponse>` | Callback type for successful API results. |
|
|
427
|
+
| `ApiErrorHandler<TResponse>` | Callback type for failed API results. |
|
|
428
|
+
| `ApiMethodCallbacks<TResponse>` | Object shape containing optional `onSuccess`, `onError`, and `params` properties. |
|
|
429
|
+
| Generated method options, such as `GetSuppliersOptions` | Per-method options object generated for wrapper methods. Contains optional `onSuccess`, `onError`, and `params`. |
|
|
430
|
+
| `ExtractResponse<T>` | Extracts the response body type from a generated method promise. |
|
|
431
|
+
| `ExtractError<T>` | Extracts the error body type from a generated method promise. |
|
|
432
|
+
| `UnwrapArray<T>` | Extracts the item type from an array, otherwise keeps the original type. |
|
|
433
|
+
| `ExtractDataIfPaginated<T>` | Extracts the item type from a `{ data?: T[] }` paginated response. |
|
|
434
|
+
| `SortableKeys<T>` | Produces keys that can be used as sortable fields for a response type. |
|
|
435
|
+
| `WithoutRequestParams<T>` | Removes a trailing optional `RequestParams` argument from a generated method argument tuple. |
|
|
304
436
|
|
|
305
437
|
### HTTP types
|
|
306
438
|
|
|
@@ -311,16 +443,16 @@ Arrays are appended as repeated fields, files keep their filename, primitives be
|
|
|
311
443
|
|
|
312
444
|
### Utility functions
|
|
313
445
|
|
|
314
|
-
| Export | Description
|
|
315
|
-
| ---------------------------------- |
|
|
316
|
-
| `buildQuery<TQuery, TSortModel>()` | Builds a query object from filter form values, page values, and sort values.
|
|
317
|
-
| `extractArgsCallbacksAndParams()` |
|
|
318
|
-
| `extractArgsToastsAndParams()` | Backwards-compatible extractor for wrapper arguments with success/error handlers and request params.
|
|
319
|
-
| `handleApiResponse()` | Executes a generated API call and converts the response or thrown error into `ApiResult<T>`.
|
|
320
|
-
| `HandleApiResponseOptions<T>` | Options object for passing success and error callbacks to `handleApiResponse`.
|
|
321
|
-
| `getSortTypeFromSortDirection()` | Converts an API sort direction into a UI `SortType`.
|
|
322
|
-
| `getSortDirectionFromSortType()` | Converts a UI `SortType` into an API sort direction.
|
|
323
|
-
| `toFormData()` | Converts an object payload into `FormData`.
|
|
446
|
+
| Export | Description |
|
|
447
|
+
| ---------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
448
|
+
| `buildQuery<TQuery, TSortModel>()` | Builds a query object from filter form values, page values, and sort values. |
|
|
449
|
+
| `extractArgsCallbacksAndParams()` | Backwards-compatible helper for older generated files that used positional callbacks. |
|
|
450
|
+
| `extractArgsToastsAndParams()` | Backwards-compatible extractor for wrapper arguments with success/error handlers and request params. |
|
|
451
|
+
| `handleApiResponse()` | Executes a generated API call and converts the response or thrown error into `ApiResult<T>`. |
|
|
452
|
+
| `HandleApiResponseOptions<T>` | Options object for passing success and error callbacks to `handleApiResponse`. |
|
|
453
|
+
| `getSortTypeFromSortDirection()` | Converts an API sort direction into a UI `SortType`. |
|
|
454
|
+
| `getSortDirectionFromSortType()` | Converts a UI `SortType` into an API sort direction. |
|
|
455
|
+
| `toFormData()` | Converts an object payload into `FormData`. |
|
|
324
456
|
|
|
325
457
|
### Default callback functions
|
|
326
458
|
|
|
@@ -338,8 +470,6 @@ Additional README files are included in these folders:
|
|
|
338
470
|
| `src/interfaces/README.md` | Documents exported interfaces and result/filter/sort types. |
|
|
339
471
|
| `src/types/README.md` | Documents helper types used by generated wrapper methods. |
|
|
340
472
|
| `src/utils/README.md` | Documents runtime helper functions. |
|
|
341
|
-
| `scripts/README.md` | Documents the API generator script. |
|
|
342
|
-
| `bin/README.md` | Documents the command-line entry point. |
|
|
343
473
|
|
|
344
474
|
## Build the package
|
|
345
475
|
|
|
@@ -355,4 +485,6 @@ npm run generate:api
|
|
|
355
485
|
|
|
356
486
|
## Notes
|
|
357
487
|
|
|
358
|
-
Generated files inside `src/api` and `dist/api` depend on the Swagger document used during generation. Their exact method names
|
|
488
|
+
Generated files inside `src/api` and `dist/api` depend on the Swagger document used during generation. Their exact method names and model names will change when the OpenAPI document changes.
|
|
489
|
+
|
|
490
|
+
Generated route type files ending in `Route.ts` are removed from the generated output.
|
package/package.json
CHANGED
|
@@ -33,7 +33,8 @@ const config = {
|
|
|
33
33
|
const swaggerFile =
|
|
34
34
|
process.env.SWAGGER_FILE ??
|
|
35
35
|
process.env.npm_config_swagger_file ??
|
|
36
|
-
config.swaggerFile
|
|
36
|
+
config.swaggerFile ??
|
|
37
|
+
"swagger/swagger.json";
|
|
37
38
|
|
|
38
39
|
const swaggerUrl =
|
|
39
40
|
process.env.SWAGGER_URL ??
|
|
@@ -50,6 +51,7 @@ const apiRoot = path.resolve(
|
|
|
50
51
|
config.apiOutput ??
|
|
51
52
|
"src/api",
|
|
52
53
|
);
|
|
54
|
+
|
|
53
55
|
const generatedDir = path.join(apiRoot, "generated");
|
|
54
56
|
const methodsDir = path.join(apiRoot, "methods");
|
|
55
57
|
|
|
@@ -110,6 +112,10 @@ function toQueryName(methodName) {
|
|
|
110
112
|
return `${pascalCase(methodName)}Query`;
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
function toOptionsName(methodName) {
|
|
116
|
+
return `${pascalCase(methodName)}Options`;
|
|
117
|
+
}
|
|
118
|
+
|
|
113
119
|
function cleanDirectory(directory) {
|
|
114
120
|
if (fs.existsSync(directory)) {
|
|
115
121
|
fs.rmSync(directory, { recursive: true, force: true });
|
|
@@ -157,6 +163,7 @@ async function generateOpenApiClient() {
|
|
|
157
163
|
}
|
|
158
164
|
|
|
159
165
|
await generateApi(generateOptions);
|
|
166
|
+
|
|
160
167
|
removeGeneratedRouteFiles();
|
|
161
168
|
}
|
|
162
169
|
|
|
@@ -226,7 +233,6 @@ ${createImportStatement({
|
|
|
226
233
|
typeOnly: true,
|
|
227
234
|
})}
|
|
228
235
|
|
|
229
|
-
|
|
230
236
|
export function ${defaultSuccessHandlerName}<T>(
|
|
231
237
|
_response: ApiSuccessResult<T>,
|
|
232
238
|
): void | Promise<void> {
|
|
@@ -374,102 +380,97 @@ function getAllTsFiles(dir) {
|
|
|
374
380
|
return results;
|
|
375
381
|
}
|
|
376
382
|
|
|
377
|
-
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
console.log(
|
|
382
|
-
`Checking ${tsFiles.length} generated TypeScript files for multipart methods...`,
|
|
383
|
-
);
|
|
383
|
+
function getGeneratedMethods(source) {
|
|
384
|
+
const methodRegex = /^\s*(\w+)\s*=\s*\(/gm;
|
|
385
|
+
const matches = [...source.matchAll(methodRegex)];
|
|
384
386
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
387
|
+
return matches.map((match, index) => {
|
|
388
|
+
const methodName = match[1];
|
|
389
|
+
const startIndex = match.index ?? 0;
|
|
390
|
+
const endIndex =
|
|
391
|
+
index + 1 < matches.length ? matches[index + 1].index : source.length;
|
|
392
392
|
|
|
393
|
-
const
|
|
393
|
+
const methodSource = source.slice(startIndex, endIndex);
|
|
394
394
|
|
|
395
|
-
|
|
396
|
-
|
|
395
|
+
return {
|
|
396
|
+
methodName,
|
|
397
|
+
methodSource,
|
|
398
|
+
isFormData: methodSource.includes("type: ContentType.FormData"),
|
|
399
|
+
};
|
|
400
|
+
});
|
|
401
|
+
}
|
|
397
402
|
|
|
398
|
-
|
|
403
|
+
function splitTopLevelArguments(value) {
|
|
404
|
+
const args = [];
|
|
405
|
+
let current = "";
|
|
406
|
+
let depth = 0;
|
|
407
|
+
let quote = "";
|
|
399
408
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
console.log(
|
|
404
|
-
` Replacing multipart payload type "${currentType.trim()}" with FormData`,
|
|
405
|
-
);
|
|
409
|
+
for (let index = 0; index < value.length; index++) {
|
|
410
|
+
const char = value[index];
|
|
411
|
+
const previousChar = value[index - 1];
|
|
406
412
|
|
|
407
|
-
|
|
408
|
-
|
|
413
|
+
if (quote) {
|
|
414
|
+
current += char;
|
|
409
415
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
416
|
+
if (char === quote && previousChar !== "\\") {
|
|
417
|
+
quote = "";
|
|
418
|
+
}
|
|
413
419
|
|
|
414
|
-
|
|
415
|
-
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
416
422
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
)}`,
|
|
422
|
-
);
|
|
423
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
424
|
+
quote = char;
|
|
425
|
+
current += char;
|
|
426
|
+
continue;
|
|
423
427
|
}
|
|
424
|
-
}
|
|
425
428
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
429
|
+
if (char === "(" || char === "{" || char === "[" || char === "<") {
|
|
430
|
+
depth++;
|
|
431
|
+
current += char;
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
431
434
|
|
|
432
|
-
|
|
433
|
-
|
|
435
|
+
if (char === ")" || char === "}" || char === "]" || char === ">") {
|
|
436
|
+
depth--;
|
|
437
|
+
current += char;
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
434
440
|
|
|
435
|
-
|
|
436
|
-
|
|
441
|
+
if (char === "," && depth === 0) {
|
|
442
|
+
if (current.trim()) {
|
|
443
|
+
args.push(current.trim());
|
|
444
|
+
}
|
|
437
445
|
|
|
438
|
-
|
|
446
|
+
current = "";
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
439
449
|
|
|
440
|
-
|
|
441
|
-
return false;
|
|
450
|
+
current += char;
|
|
442
451
|
}
|
|
443
452
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
const nextMatch = nextMethodPattern.exec(source);
|
|
448
|
-
const endIndex = nextMatch?.index ?? source.length;
|
|
449
|
-
|
|
450
|
-
const methodSource = source.slice(startMatch.index, endIndex);
|
|
453
|
+
if (current.trim()) {
|
|
454
|
+
args.push(current.trim());
|
|
455
|
+
}
|
|
451
456
|
|
|
452
|
-
return
|
|
457
|
+
return args;
|
|
453
458
|
}
|
|
454
459
|
|
|
455
|
-
function
|
|
456
|
-
const
|
|
457
|
-
const matches = [...source.matchAll(methodRegex)];
|
|
460
|
+
function getGeneratedMethodArgumentCount(signatureParams) {
|
|
461
|
+
const args = splitTopLevelArguments(signatureParams);
|
|
458
462
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const endIndex =
|
|
463
|
-
index + 1 < matches.length ? matches[index + 1].index : source.length;
|
|
463
|
+
if (args.length === 0) {
|
|
464
|
+
return 0;
|
|
465
|
+
}
|
|
464
466
|
|
|
465
|
-
|
|
467
|
+
const lastArg = args[args.length - 1];
|
|
466
468
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
});
|
|
469
|
+
if (/\bparams\s*:\s*RequestParams\b/.test(lastArg)) {
|
|
470
|
+
return args.length - 1;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return args.length;
|
|
473
474
|
}
|
|
474
475
|
|
|
475
476
|
async function createMethodFiles() {
|
|
@@ -518,9 +519,9 @@ async function createMethodFiles() {
|
|
|
518
519
|
}
|
|
519
520
|
|
|
520
521
|
const params = signatureMatch[1].trim();
|
|
522
|
+
const methodArgumentCount = getGeneratedMethodArgumentCount(params);
|
|
521
523
|
|
|
522
524
|
const returnTypeMatch = methodSource.match(/this\.request<([^,>]+)/m);
|
|
523
|
-
|
|
524
525
|
const returnType = returnTypeMatch?.[1] ?? "";
|
|
525
526
|
|
|
526
527
|
const isPaginated =
|
|
@@ -540,6 +541,7 @@ async function createMethodFiles() {
|
|
|
540
541
|
nonQueryMethods.push({
|
|
541
542
|
methodName,
|
|
542
543
|
isFormData,
|
|
544
|
+
methodArgumentCount,
|
|
543
545
|
});
|
|
544
546
|
}
|
|
545
547
|
|
|
@@ -554,9 +556,23 @@ export type ${toQueryName(methodName)} =
|
|
|
554
556
|
)
|
|
555
557
|
.join("\n\n");
|
|
556
558
|
|
|
557
|
-
const
|
|
559
|
+
const methodOptionTypes = generatedMethods
|
|
560
|
+
.map(({ methodName }) =>
|
|
561
|
+
`
|
|
562
|
+
export type ${toOptionsName(methodName)} = {
|
|
563
|
+
onSuccess?: ApiSuccessHandler<
|
|
564
|
+
ExtractResponse<ReturnType<${className}["${methodName}"]>>
|
|
565
|
+
>;
|
|
566
|
+
onError?: ApiErrorHandler<
|
|
567
|
+
ExtractError<ReturnType<${className}["${methodName}"]>>
|
|
568
|
+
>;
|
|
569
|
+
params?: RequestParams;
|
|
570
|
+
};
|
|
571
|
+
`.trim(),
|
|
572
|
+
)
|
|
573
|
+
.join("\n\n");
|
|
558
574
|
|
|
559
|
-
const
|
|
575
|
+
const hasPaginatedMethods = paginatedMethods.length > 0;
|
|
560
576
|
|
|
561
577
|
const hasFormDataMethods = nonQueryMethods.some(
|
|
562
578
|
({ isFormData }) => isFormData,
|
|
@@ -578,14 +594,10 @@ export async function ${methodName}(
|
|
|
578
594
|
ExtractResponse<ReturnType<${className}["${methodName}"]>>
|
|
579
595
|
> | null = null,
|
|
580
596
|
sortDirection?: SortDirection,
|
|
581
|
-
|
|
582
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>
|
|
583
|
-
>,
|
|
584
|
-
onError?: ApiErrorHandler<
|
|
585
|
-
ExtractError<ReturnType<${className}["${methodName}"]>>
|
|
586
|
-
>,
|
|
587
|
-
params?: RequestParams
|
|
597
|
+
options: ${toOptionsName(methodName)} = {}
|
|
588
598
|
): Promise<ApiResult<ExtractResponse<ReturnType<${className}["${methodName}"]>>>> {
|
|
599
|
+
const { onSuccess, onError, params } = options;
|
|
600
|
+
|
|
589
601
|
return handleApiResponse(
|
|
590
602
|
() =>
|
|
591
603
|
${instanceName}.${methodName}(
|
|
@@ -608,14 +620,10 @@ export async function ${methodName}(
|
|
|
608
620
|
return `
|
|
609
621
|
export async function ${methodName}(
|
|
610
622
|
query?: ${toQueryName(methodName)},
|
|
611
|
-
|
|
612
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>
|
|
613
|
-
>,
|
|
614
|
-
onError?: ApiErrorHandler<
|
|
615
|
-
ExtractError<ReturnType<${className}["${methodName}"]>>
|
|
616
|
-
>,
|
|
617
|
-
params?: RequestParams
|
|
623
|
+
options: ${toOptionsName(methodName)} = {}
|
|
618
624
|
): Promise<ApiResult<ExtractResponse<ReturnType<${className}["${methodName}"]>>>> {
|
|
625
|
+
const { onSuccess, onError, params } = options;
|
|
626
|
+
|
|
619
627
|
return handleApiResponse(
|
|
620
628
|
() =>
|
|
621
629
|
${instanceName}.${methodName}(
|
|
@@ -634,13 +642,7 @@ export async function ${methodName}(
|
|
|
634
642
|
`
|
|
635
643
|
export async function ${methodName}(
|
|
636
644
|
query?: ${toQueryName(methodName)},
|
|
637
|
-
|
|
638
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>
|
|
639
|
-
>,
|
|
640
|
-
onError?: ApiErrorHandler<
|
|
641
|
-
ExtractError<ReturnType<${className}["${methodName}"]>>
|
|
642
|
-
>,
|
|
643
|
-
params?: RequestParams
|
|
645
|
+
options: ${toOptionsName(methodName)} = {}
|
|
644
646
|
): Promise<
|
|
645
647
|
ApiResult<
|
|
646
648
|
ExtractResponse<
|
|
@@ -650,6 +652,8 @@ export async function ${methodName}(
|
|
|
650
652
|
>
|
|
651
653
|
>
|
|
652
654
|
> {
|
|
655
|
+
const { onSuccess, onError, params } = options;
|
|
656
|
+
|
|
653
657
|
return handleApiResponse(
|
|
654
658
|
() =>
|
|
655
659
|
${instanceName}.${methodName}(
|
|
@@ -664,20 +668,14 @@ export async function ${methodName}(
|
|
|
664
668
|
.join("\n\n");
|
|
665
669
|
|
|
666
670
|
const nonQueryMethodWrappers = nonQueryMethods
|
|
667
|
-
.map(({ methodName, isFormData }) => {
|
|
671
|
+
.map(({ methodName, isFormData, methodArgumentCount }) => {
|
|
668
672
|
if (isFormData) {
|
|
669
673
|
return `
|
|
670
674
|
export async function ${methodName}(
|
|
671
675
|
data: Parameters<
|
|
672
676
|
${className}["${methodName}"]
|
|
673
677
|
>[0],
|
|
674
|
-
|
|
675
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>
|
|
676
|
-
>,
|
|
677
|
-
onError?: ApiErrorHandler<
|
|
678
|
-
ExtractError<ReturnType<${className}["${methodName}"]>>
|
|
679
|
-
>,
|
|
680
|
-
params?: RequestParams
|
|
678
|
+
options: ${toOptionsName(methodName)} = {}
|
|
681
679
|
): Promise<
|
|
682
680
|
ApiResult<
|
|
683
681
|
ExtractResponse<
|
|
@@ -687,6 +685,7 @@ export async function ${methodName}(
|
|
|
687
685
|
>
|
|
688
686
|
>
|
|
689
687
|
> {
|
|
688
|
+
const { onSuccess, onError, params } = options;
|
|
690
689
|
const formData = toFormData(data);
|
|
691
690
|
|
|
692
691
|
return handleApiResponse(
|
|
@@ -705,25 +704,11 @@ export async function ${methodName}(
|
|
|
705
704
|
|
|
706
705
|
return `
|
|
707
706
|
export async function ${methodName}(
|
|
708
|
-
...
|
|
707
|
+
...argsWithOptions: [
|
|
709
708
|
...ApiMethodArguments<
|
|
710
709
|
${className}["${methodName}"]
|
|
711
710
|
>,
|
|
712
|
-
|
|
713
|
-
ExtractResponse<
|
|
714
|
-
ReturnType<
|
|
715
|
-
${className}["${methodName}"]
|
|
716
|
-
>
|
|
717
|
-
>
|
|
718
|
-
>?,
|
|
719
|
-
ApiErrorHandler<
|
|
720
|
-
ExtractError<
|
|
721
|
-
ReturnType<
|
|
722
|
-
${className}["${methodName}"]
|
|
723
|
-
>
|
|
724
|
-
>
|
|
725
|
-
>?,
|
|
726
|
-
RequestParams?
|
|
711
|
+
${toOptionsName(methodName)}?
|
|
727
712
|
]
|
|
728
713
|
): Promise<
|
|
729
714
|
ApiResult<
|
|
@@ -734,26 +719,14 @@ export async function ${methodName}(
|
|
|
734
719
|
>
|
|
735
720
|
>
|
|
736
721
|
> {
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
>,
|
|
746
|
-
ExtractResponse<
|
|
747
|
-
ReturnType<
|
|
748
|
-
${className}["${methodName}"]
|
|
749
|
-
>
|
|
750
|
-
>,
|
|
751
|
-
ExtractError<
|
|
752
|
-
ReturnType<
|
|
753
|
-
${className}["${methodName}"]
|
|
754
|
-
>
|
|
755
|
-
>
|
|
756
|
-
>(argsWithCallbacks);
|
|
722
|
+
const args = [...argsWithOptions] as unknown[];
|
|
723
|
+
|
|
724
|
+
const options =
|
|
725
|
+
args.length > ${methodArgumentCount}
|
|
726
|
+
? (args.pop() as ${toOptionsName(methodName)})
|
|
727
|
+
: {};
|
|
728
|
+
|
|
729
|
+
const { onSuccess, onError, params } = options ?? {};
|
|
757
730
|
|
|
758
731
|
const requestArgs = [
|
|
759
732
|
...args,
|
|
@@ -776,10 +749,10 @@ export async function ${methodName}(
|
|
|
776
749
|
|
|
777
750
|
const runtimeValueImportNames = [
|
|
778
751
|
...(hasPaginatedMethods && useFilterFormValues ? ["buildQuery"] : []),
|
|
779
|
-
...(hasRegularNonQueryMethods ? ["extractArgsCallbacksAndParams"] : []),
|
|
780
752
|
...(hasFormDataMethods ? ["toFormData"] : []),
|
|
781
753
|
"handleApiResponse",
|
|
782
754
|
];
|
|
755
|
+
|
|
783
756
|
const runtimeTypeImportNames = [
|
|
784
757
|
"ApiResult",
|
|
785
758
|
"ApiSuccessHandler",
|
|
@@ -841,16 +814,16 @@ export async function ${methodName}(
|
|
|
841
814
|
|
|
842
815
|
const apiMethodArgumentsType = hasRegularNonQueryMethods
|
|
843
816
|
? `
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
817
|
+
type ApiMethodArguments<
|
|
818
|
+
TMethod extends (...args: any[]) => unknown
|
|
819
|
+
> =
|
|
820
|
+
Parameters<TMethod> extends [
|
|
821
|
+
...infer Arguments,
|
|
822
|
+
RequestParams?
|
|
823
|
+
]
|
|
824
|
+
? Arguments
|
|
825
|
+
: Parameters<TMethod>;
|
|
826
|
+
`.trim()
|
|
854
827
|
: "";
|
|
855
828
|
|
|
856
829
|
const content = `
|
|
@@ -871,6 +844,11 @@ ${apiMethodArgumentsType}
|
|
|
871
844
|
======================= */
|
|
872
845
|
${queryTypes}
|
|
873
846
|
|
|
847
|
+
/* =======================
|
|
848
|
+
Method Option Types
|
|
849
|
+
======================= */
|
|
850
|
+
${methodOptionTypes}
|
|
851
|
+
|
|
874
852
|
/* =======================
|
|
875
853
|
API Instance
|
|
876
854
|
======================= */
|