typedapi-client-helpers 0.3.3 → 0.3.5

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 CHANGED
@@ -2,6 +2,53 @@
2
2
 
3
3
  All notable changes to `typedapi-client-helpers` are documented in this file.
4
4
 
5
+ ## [0.3.5] - 2026-07-24
6
+
7
+ ### Added
8
+
9
+ * Added support for `x-typedapi-filter-form` on non-paginated query endpoints.
10
+ * Added `buildFilterQuery<TQuery>()` for converting `FilterFormValues` without adding pagination or sorting.
11
+ * Generated filter-form methods now group normal query values and typed filter-form values inside a single `filterForm` argument.
12
+ * The `filterForm.query` values are merged with the output of `buildFilterQuery(filterForm.filters)` before sending the request.
13
+ * Optional-query endpoints default the complete `filterForm` argument to an empty object.
14
+
15
+ ### Compatibility notes
16
+
17
+ * Pair this package with `TypedApi.Swagger` 0.3.2 or newer to use `[TypedApiFilterForm]`.
18
+ * Existing paginated method signatures and `buildQuery()` behaviour are unchanged.
19
+ * Regenerate committed API files because filter-form endpoint signatures have changed.
20
+
21
+ ## [0.3.4] - 2026-07-22
22
+
23
+ ### Added
24
+
25
+ * Added support for `x-typedapi-generic` metadata and reusable TypeScript generic declarations.
26
+ * Added exact generic substitutions for direct properties, array/collection items, dictionary values, and inherited generic wrappers.
27
+ * Added discriminator mapping support with literal discriminator properties and frontend union types.
28
+ * Added readable closed-generic reference handling such as `ApiPaginationResponseOfProjectModel` to `ApiPaginationResponse<ProjectModel>`.
29
+ * Added generator coverage for custom generic envelopes, pagination, nullability combinations, polymorphism, schema IDs, typed errors, and TypeScript compilation.
30
+
31
+ ### Changed
32
+
33
+ * Requiredness and nullability now remain independent in generated property declarations.
34
+ * Polymorphic base schemas now generate a separate `Base` contract plus a union of concrete variants.
35
+ * Derived polymorphic schemas inherit from the base contract rather than from the union, preventing cyclic TypeScript types.
36
+ * Inline `allOf` object fragments are emitted directly in intersections instead of creating unnecessary numbered helper interfaces.
37
+ * Typed OpenAPI error responses are deduplicated into method-specific error unions.
38
+ * TypedApi contract version 2 is now supported.
39
+
40
+ ### Fixed
41
+
42
+ * Fixed flattened inherited generic wrappers accidentally capturing the first concrete schema, such as generating `data: OrderModel[]` inside `ApiPaginationSortResponse<T>`.
43
+ * Fixed flattened generic inheritance being emitted as a duplicated standalone interface; annotated derived wrappers now preserve their reusable generic base intersection.
44
+ * Invalid generic metadata with unused type parameters now fails with a clear error instead of emitting a misleading generic declaration.
45
+
46
+ ### Compatibility notes
47
+
48
+ * Pair this package with `TypedApi.Swagger` 0.3.1 or newer for all new features.
49
+ * Regenerate committed API files after upgrading.
50
+ * Generic reconstruction only occurs for schemas carrying `x-typedapi-generic`; ordinary closed generic schemas remain ordinary component types.
51
+
5
52
  ## [0.3.0] - 2026-07-10
6
53
 
7
54
  ### Added
package/README.md CHANGED
@@ -2,25 +2,184 @@
2
2
 
3
3
  A small Fetch runtime and OpenAPI 3 TypeScript generator designed to work with `TypedApi.Swagger` on ASP.NET Core.
4
4
 
5
- ## Version 0.3 highlights
6
-
7
- - Lowercases only the first character of generated TypeScript members.
8
- - Sanitizes operation IDs and rejects duplicates before writing output.
9
- - Can name frontend functions from either the unique OpenAPI operation ID or the original ASP.NET controller action name.
10
- - Supports path, query, header, cookie, and request-body inputs in the same operation.
11
- - Resolves local OpenAPI references for schemas, parameters, request bodies, and responses.
12
- - Supports `allOf` composition with local properties.
13
- - Generates typed unions for documented non-success responses.
14
- - Uses a structured `ApiHttpError` when an operation has no documented error schema.
15
- - Generates API contracts and controller method files without creating a root API barrel file.
16
- - Writes generated output transactionally, with a Windows-safe in-place fallback when directory renames are blocked by editors or file watchers.
17
- - Adds `--check`, `--strict`, `--offline`, and `--verbose` CLI modes.
18
- - Improves cancellation, timeouts, header merging, SSR safety, and malformed-response handling.
5
+ ## Version 0.3.5 highlights
6
+
7
+ * Supports `[TypedApiFilterForm]` endpoints through the `x-typedapi-filter-form` OpenAPI extension.
8
+ * Generates grouped filter-form arguments containing normal query values and `FilterFormValues`.
9
+ * Provides `buildFilterQuery<TQuery>()` for converting filters without adding pagination or sorting.
10
+ * Reconstructs opted-in closed .NET generic schemas as reusable TypeScript generics.
11
+ * Supports exact generic bindings for direct properties, arrays, collections, and dictionary values.
12
+ * Preserves required-but-nullable properties as required `T | null` instead of optional values.
13
+ * Generates non-cyclic discriminated unions with literal discriminator fields.
14
+ * Consumes readable generic schema IDs such as `ApiPaginationResponseOfProjectModel`.
15
+ * Generates method-specific unions from typed `ProblemDetails` error responses.
16
+ * Validates TypedApi OpenAPI contract version 2.
17
+
18
+ ## Filter-form endpoints
19
+
20
+ `TypedApi.Swagger` can mark a non-paginated query endpoint with `[TypedApiFilterForm]`.
21
+
22
+ The attribute can be placed on the query parameter:
23
+
24
+ ```csharp
25
+ [HttpGet("map-items")]
26
+ public async Task<ActionResult<IReadOnlyList<MapItemResponse>>> GetMapItems(
27
+ [FromQuery, TypedApiFilterForm] ChargePointMapFilter filter,
28
+ CancellationToken token)
29
+ {
30
+ return Ok(await service.GetMapItemsAsync(filter, token));
31
+ }
32
+ ```
33
+
34
+ It can also be placed on the endpoint:
35
+
36
+ ```csharp
37
+ [TypedApiFilterForm]
38
+ [HttpGet("map-items")]
39
+ public async Task<ActionResult<IReadOnlyList<MapItemResponse>>> GetMapItems(
40
+ [FromQuery] ChargePointMapFilter filter,
41
+ CancellationToken token)
42
+ {
43
+ return Ok(await service.GetMapItemsAsync(filter, token));
44
+ }
45
+ ```
46
+
47
+ Placing the attribute on the query parameter is preferred because it explicitly identifies which parameter represents the filter form.
48
+
49
+ The generated frontend method groups normal query values and dynamic filters inside one `filterForm` argument:
50
+
51
+ ```ts
52
+ export async function chargePointGetMapItems(
53
+ filterForm: {
54
+ query: ChargePointGetMapItemsQueryParams;
55
+ filters?: FilterFormValues<ChargePointGetMapItemsQueryParams>[];
56
+ },
57
+ options: ApiMethodOptions<
58
+ MapItemResponse[],
59
+ HttpValidationProblemDetails | ProblemDetails,
60
+ RequestParams
61
+ > = {},
62
+ ): Promise<
63
+ ApiResult<
64
+ MapItemResponse[],
65
+ HttpValidationProblemDetails | ProblemDetails
66
+ >
67
+ >;
68
+ ```
69
+
70
+ Call the generated endpoint with fixed query values and optional filters:
71
+
72
+ ```ts
73
+ await chargePointGetMapItems({
74
+ query: {
75
+ west,
76
+ south,
77
+ east,
78
+ north,
79
+ zoom,
80
+ },
81
+ filters: activeFilters,
82
+ });
83
+ ```
84
+
85
+ The generated implementation merges both sources before sending the request:
86
+
87
+ ```ts
88
+ const builtQuery = {
89
+ ...(filterForm.query ?? {}),
90
+ ...buildFilterQuery<ChargePointGetMapItemsQueryParams>(
91
+ filterForm.filters ?? [],
92
+ ),
93
+ };
94
+ ```
95
+
96
+ Values produced by `buildFilterQuery()` are applied after normal query values. This means an active filter with the same property name overrides the value from `filterForm.query`.
97
+
98
+ Filter-form endpoints do not automatically add pagination or sorting. Paginated endpoints continue to use `buildQuery()`.
99
+
100
+ For an endpoint where every query property is optional, the generated argument defaults to an empty object:
101
+
102
+ ```ts
103
+ export async function searchItems(
104
+ filterForm: {
105
+ query?: SearchItemsQueryParams;
106
+ filters?: FilterFormValues<SearchItemsQueryParams>[];
107
+ } = {},
108
+ options: ApiMethodOptions<
109
+ SearchResult[],
110
+ ProblemDetails,
111
+ RequestParams
112
+ > = {},
113
+ );
114
+ ```
115
+
116
+ For endpoints containing required query properties, `filterForm` and `filterForm.query` remain required.
117
+
118
+ ## Generic contracts
119
+
120
+ With `[TypedApiGeneric]` on a backend wrapper, a closed schema such as `ApiEnvelope<ProjectModel>` becomes one reusable declaration:
121
+
122
+ ```ts
123
+ export interface ApiEnvelope<T> {
124
+ data: T;
125
+ relatedItems: T[];
126
+ itemsByKey: Record<string, T>;
127
+ }
128
+ ```
129
+
130
+ Endpoints then use `ApiEnvelope<ProjectModel>` rather than a repeated `ApiEnvelopeOfProjectModel` interface.
131
+
132
+ Inherited generic wrappers are reconstructed whether Swagger uses `allOf` or emits a flattened schema:
133
+
134
+ ```ts
135
+ export type ApiPaginationSortResponse<T> = ApiPaginationResponse<T> & {
136
+ sortBy?: string | null;
137
+ sortDirection: SortDirection;
138
+ };
139
+ ```
140
+
141
+ ## Required and nullable properties
142
+
143
+ OpenAPI presence and nullability remain separate:
144
+
145
+ ```ts
146
+ export interface NullabilityContract {
147
+ requiredText: string;
148
+ requiredNullableText: string | null;
149
+ optionalNullableText?: string | null;
150
+ }
151
+ ```
152
+
153
+ ## Discriminated unions
154
+
155
+ Serializer discriminator mappings produce narrowing unions without cyclic inheritance:
156
+
157
+ ```ts
158
+ export type NotificationModel =
159
+ | EmailNotificationModel
160
+ | SmsNotificationModel;
161
+
162
+ export type EmailNotificationModel = NotificationModelBase & {
163
+ emailAddress: string;
164
+ kind: "email";
165
+ };
166
+ ```
167
+
168
+ ## Typed errors
169
+
170
+ When the OpenAPI operation contains typed error response bodies, generated methods expose the union directly:
171
+
172
+ ```ts
173
+ ApiResult<
174
+ ProjectModel,
175
+ HttpValidationProblemDetails | ProblemDetails
176
+ >
177
+ ```
19
178
 
20
179
  ## Install
21
180
 
22
181
  ```bash
23
- npm install typedapi-client-helpers@0.3.3
182
+ npm install typedapi-client-helpers@0.3.5
24
183
  ```
25
184
 
26
185
  The generator expects an OpenAPI 3.x document. Swagger 2.0 documents are rejected with a clear error.
@@ -65,14 +224,18 @@ src/api/
65
224
  └── Products.api.ts
66
225
  ```
67
226
 
68
- The generator intentionally does not create `src/api/index.ts`. Import generated methods and contracts directly from their files:
227
+ The generator intentionally does not create `src/api/index.ts`.
228
+
229
+ Import generated methods and contracts directly from their files:
69
230
 
70
231
  ```ts
71
232
  import { getProducts } from "./api/methods/Product.api";
72
233
  import type { ProductModel } from "./api/generated/data-contracts";
73
234
  ```
74
235
 
75
- When upgrading from an earlier 0.3.0 build, the next generation removes the old generated `src/api/index.ts`. The Swagger backup belongs to the consuming project, not this npm package.
236
+ When upgrading from an earlier 0.3.0 build, the next generation removes the old generated `src/api/index.ts`.
237
+
238
+ The Swagger backup belongs to the consuming project, not this npm package.
76
239
 
77
240
  ## Frontend method names
78
241
 
@@ -87,10 +250,10 @@ Choose the source of generated function names with `typedApiMethodNameStyle`:
87
250
  }
88
251
  ```
89
252
 
90
- - `"operationId"` uses the unique OpenAPI operation ID. The controller-prefix setting does not affect this mode.
91
- - `"action"` uses the original ASP.NET controller action name.
92
- - `typedApiPrefixMethodNamesWithController: true` is the default in action mode and prefixes the normalized controller name.
93
- - `typedApiPrefixMethodNamesWithController: false` removes the controller prefix.
253
+ * `"operationId"` uses the unique OpenAPI operation ID. The controller-prefix setting does not affect this mode.
254
+ * `"action"` uses the original ASP.NET controller action name.
255
+ * `typedApiPrefixMethodNamesWithController: true` is the default in action mode and prefixes the normalized controller name.
256
+ * `typedApiPrefixMethodNamesWithController: false` removes the controller prefix.
94
257
 
95
258
  For an `OrderController` action named `GetOrderById`:
96
259
 
@@ -99,15 +262,18 @@ typedApiPrefixMethodNamesWithController: true -> orderGetOrderById(...)
99
262
  typedApiPrefixMethodNamesWithController: false -> getOrderById(...)
100
263
  ```
101
264
 
102
- Operation-specific types follow the selected name too:
265
+ Operation-specific types follow the selected name:
103
266
 
104
267
  ```text
105
268
  true -> OrderGetOrderByIdParams
106
269
  false -> GetOrderByIdParams
107
270
  ```
108
271
 
109
- Action-name mode requires the matching `TypedApi.Swagger` package, which emits `x-typedapi-operation.actionName`. When the prefix is enabled, the controller name is read from `x-typedapi-operation.controllerName`, the first OpenAPI tag, or the route. If disabling the prefix creates duplicate names across controllers, generation stops with a clear collision error; enable the prefix, rename an action, or use `"operationId"`.
272
+ Action-name mode requires the matching `TypedApi.Swagger` package, which emits `x-typedapi-operation.actionName`.
273
+
274
+ When the prefix is enabled, the controller name is read from `x-typedapi-operation.controllerName`, the first OpenAPI tag, or the route.
110
275
 
276
+ If disabling the prefix creates duplicate names across controllers, generation stops with a clear collision error. Enable the prefix, rename an action, or use `"operationId"`.
111
277
 
112
278
  ## CI consistency check
113
279
 
@@ -120,15 +286,18 @@ This generates in memory and exits with an error when committed generated files
120
286
  Other modes:
121
287
 
122
288
  ```bash
123
- typedapi-generate --strict # fail instead of using a backup after a download error
124
- typedapi-generate --offline # intentionally generate from the backup
125
- typedapi-generate --verbose # print additional diagnostics
289
+ typedapi-generate --strict
290
+ typedapi-generate --offline
291
+ typedapi-generate --verbose
126
292
  ```
127
293
 
294
+ * `--strict` fails instead of using a backup after a Swagger download error.
295
+ * `--offline` intentionally generates from the configured Swagger backup.
296
+ * `--verbose` prints additional generator diagnostics.
128
297
 
129
298
  ## Generated property names
130
299
 
131
- Generated interface and parameter members lowercase **only the first character**:
300
+ Generated interface and parameter members lowercase only the first character:
132
301
 
133
302
  ```ts
134
303
  Files -> files
@@ -137,15 +306,33 @@ ProductID -> productID
137
306
  snake_case -> snake_case
138
307
  ```
139
308
 
140
- The rest of each name is left unchanged. Generated wire metadata converts these local names back to the exact OpenAPI names for JSON, multipart forms, query parameters, headers, cookies, and path parameters. Responses and documented error bodies are converted in the opposite direction.
309
+ The remainder of each property name is preserved.
310
+
311
+ Generated wire metadata converts local property names back to the exact OpenAPI names for:
312
+
313
+ * JSON bodies
314
+ * Multipart forms
315
+ * Query parameters
316
+ * Header parameters
317
+ * Cookie parameters
318
+ * Path parameters
319
+
320
+ Responses and documented error bodies are converted in the opposite direction.
141
321
 
142
322
  ## Generated method shape
143
323
 
144
- Generated methods keep parameters and request bodies as separate positional groups. Non-body parameters come first, the body comes second, and request options remain last:
324
+ Generated methods keep parameters and request bodies in separate positional groups.
325
+
326
+ Non-body parameters come first, the body comes second, and request options remain last:
145
327
 
146
328
  ```ts
147
329
  await updateWarehouse(
148
- { id: requireId(context.warehouseId, "Warehouse ID") },
330
+ {
331
+ id: requireId(
332
+ context.warehouseId,
333
+ "Warehouse ID",
334
+ ),
335
+ },
149
336
  warehouse,
150
337
  );
151
338
  ```
@@ -156,23 +343,69 @@ The generated signature is:
156
343
  export async function updateWarehouse(
157
344
  pathParams: UpdateWarehouseParams,
158
345
  data: WarehouseRequest,
159
- options: ApiMethodOptions<WarehouseModel, ApiHttpError, RequestParams> = {},
346
+ options: ApiMethodOptions<
347
+ WarehouseModel,
348
+ ApiHttpError,
349
+ RequestParams
350
+ > = {},
160
351
  ): Promise<ApiResult<WarehouseModel, ApiHttpError>>;
161
352
  ```
162
353
 
163
354
  Path operations without a body use only the params object:
164
355
 
165
356
  ```ts
166
- await deleteSupplier({ id: requireId(context.supplierId, "Supplier ID") });
357
+ await deleteSupplier({
358
+ id: requireId(context.supplierId, "Supplier ID"),
359
+ });
167
360
  ```
168
361
 
169
362
  Body-only operations accept their payload directly:
170
363
 
171
364
  ```ts
172
- await uploadProductFiles({ files: selectedFiles });
365
+ await uploadProductFiles({
366
+ files: selectedFiles,
367
+ });
368
+ ```
369
+
370
+ Regular query-only operations use a `query` argument:
371
+
372
+ ```ts
373
+ await searchProducts({
374
+ search: "charger",
375
+ limit: 25,
376
+ });
377
+ ```
378
+
379
+ Endpoints marked with `x-typedapi-filter-form` use a grouped `filterForm` argument containing `query` and optional `filters` properties:
380
+
381
+ ```ts
382
+ await searchMapItems({
383
+ query: {
384
+ west,
385
+ south,
386
+ east,
387
+ north,
388
+ zoom,
389
+ },
390
+ filters: activeFilters,
391
+ });
392
+ ```
393
+
394
+ Paginated endpoints use their existing filter, page, page-size, and sorting arguments:
395
+
396
+ ```ts
397
+ await getProducts(
398
+ activeFilters,
399
+ 1,
400
+ 100,
401
+ "name",
402
+ SortDirection.Ascending,
403
+ );
173
404
  ```
174
405
 
175
- Query-only operations use a `query` argument. Operations involving headers or cookies without path parameters use `requestParams`. All generated parameter interfaces are named `OperationNameParams`; nested `path`, `query`, `headers`, `cookies`, or `body` wrappers are not generated.
406
+ Operations involving headers or cookies without path parameters use `requestParams`.
407
+
408
+ All generated parameter interfaces are named `OperationNameParams`. Nested `path`, `query`, `headers`, `cookies`, or `body` wrappers are not generated for regular endpoint methods.
176
409
 
177
410
  Request options are always last:
178
411
 
@@ -181,50 +414,145 @@ await updateWarehouse(pathParams, data, {
181
414
  params: {
182
415
  signal: abortController.signal,
183
416
  timeoutMs: 10_000,
184
- headers: new Headers({ Authorization: "Bearer ..." }),
417
+ headers: new Headers({
418
+ Authorization: "Bearer ...",
419
+ }),
420
+ },
421
+ onSuccess: result => {
422
+ console.log(result.response);
423
+ },
424
+ onError: result => {
425
+ console.error(result.error);
185
426
  },
186
- onSuccess: result => console.log(result.response),
187
- onError: result => console.error(result.error),
188
427
  });
189
428
  ```
190
429
 
430
+ ## Filter conversion
431
+
432
+ Use `buildFilterQuery()` when filters must be converted without adding pagination or sorting:
433
+
434
+ ```ts
435
+ const query = buildFilterQuery<ProductQueryParams>(
436
+ activeFilters,
437
+ );
438
+ ```
439
+
440
+ Use `buildQuery()` for paginated endpoints:
441
+
442
+ ```ts
443
+ const query = buildQuery<
444
+ ProductQueryParams,
445
+ ProductTableRow
446
+ >(
447
+ activeFilters,
448
+ page,
449
+ pageSize,
450
+ sortBy,
451
+ sortDirection,
452
+ );
453
+ ```
454
+
455
+ `buildFilterQuery()` supports the same `FilterFormValues` conversion behavior as `buildQuery()`, including:
456
+
457
+ * Primitive values
458
+ * Minimum and maximum values
459
+ * Lists
460
+ * `OptionValue` values
461
+ * Booleans
462
+ * Numbers
463
+ * Dates
464
+ * Strings
465
+
466
+ It does not add:
467
+
468
+ * `pageNumber`
469
+ * `pageSize`
470
+ * `sortBy`
471
+ * `sortDirection`
472
+
191
473
  ## Typed errors
192
474
 
193
- When the OpenAPI document describes error response bodies, generated methods expose their generated type or union. When no error schema is documented, the method uses `ApiHttpError` instead of `unknown`:
475
+ When the OpenAPI document describes error response bodies, generated methods expose their generated type or union.
476
+
477
+ When no error schema is documented, the method uses `ApiHttpError` instead of `unknown`:
194
478
 
195
479
  ```ts
196
- const result = await cancelOrder({ id, reason });
480
+ const result = await cancelOrder({
481
+ id,
482
+ reason,
483
+ });
197
484
 
198
485
  if (!result.ok) {
199
486
  if (result.error.kind === "http") {
200
- console.error(result.error.status, result.error.body);
487
+ console.error(
488
+ result.error.status,
489
+ result.error.body,
490
+ );
201
491
  } else {
202
- // Structured TypedApi client error: network, aborted, or parse.
203
492
  console.error(result.error);
204
493
  }
205
494
  }
206
495
  ```
207
496
 
208
- Malformed JSON is reported as a structured parse error instead of being cast to the expected response type. Consumer callback exceptions are allowed to propagate and are not disguised as API failures.
497
+ Structured client errors include:
498
+
499
+ * Network errors
500
+ * Aborted requests
501
+ * Timeout errors
502
+ * Malformed response errors
503
+
504
+ Malformed JSON is reported as a structured parse error instead of being cast to the expected response type.
505
+
506
+ Exceptions thrown by consumer `onSuccess` and `onError` callbacks propagate normally.
209
507
 
210
508
  ## Runtime configuration
211
509
 
212
510
  ```ts
213
- import { configureApiClient, setSecurityData } from "typedapi-client-helpers";
511
+ import {
512
+ configureApiClient,
513
+ setSecurityData,
514
+ } from "typedapi-client-helpers";
214
515
 
215
516
  configureApiClient({
216
517
  baseUrl: "https://api.example.com",
217
518
  timeoutMs: 15_000,
218
- securityWorker: token => token
219
- ? { headers: { Authorization: `Bearer ${token}` } }
220
- : undefined
519
+ securityWorker: token =>
520
+ token
521
+ ? {
522
+ headers: {
523
+ Authorization: `Bearer ${token}`,
524
+ },
525
+ }
526
+ : undefined,
221
527
  });
222
528
 
223
529
  setSecurityData("access-token");
224
530
  ```
225
531
 
226
- The runtime also exports `createApiClient`, `request`, `abortRequest`, `mergeHeaders`, `toRequestHeaders`, `toCookieHeader`, `handleApiResponse`, `buildQuery`, `toFormData`.
532
+ The runtime also exports:
533
+
534
+ * `createApiClient`
535
+ * `request`
536
+ * `abortRequest`
537
+ * `mergeHeaders`
538
+ * `toRequestHeaders`
539
+ * `toCookieHeader`
540
+ * `handleApiResponse`
541
+ * `buildFilterQuery`
542
+ * `buildQuery`
543
+ * `toFormData`
227
544
 
228
545
  ## Backend pairing
229
546
 
230
- Use `TypedApi.Swagger` 0.3 or newer on the backend. It emits `x-typedapi.contractVersion = 1`, unique operation IDs, serializer-aware property names, and explicit pagination metadata. The generator validates the contract version before replacing generated files.
547
+ Use `TypedApi.Swagger` 0.3.2 or newer on the backend for:
548
+
549
+ * `[TypedApiFilterForm]`
550
+ * Generic reconstruction
551
+ * Corrected nullability
552
+ * Discriminators
553
+ * Readable schema IDs
554
+ * Typed default errors
555
+
556
+ The backend package emits `x-typedapi-filter-form` for opted-in filter endpoints.
557
+
558
+ It also emits `x-typedapi.contractVersion = 2` for contract compatibility. The generator validates the contract version before replacing generated files.
@@ -1,15 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildFilterQuery = buildFilterQuery;
3
4
  exports.buildQuery = buildQuery;
4
5
  /**
5
- * Builds a typed query object from filter form values, pagination, and sorting.
6
+ * Builds a typed query object from filter form values without adding pagination or sorting.
6
7
  *
7
8
  * Empty filter values are ignored.
8
9
  * List filters are converted item by item.
9
10
  * `OptionValue` lists send their `value` fields instead of the display names.
10
11
  */
11
- function buildQuery(filters, page = 1, pageSize = 100, sortBy = null, sortDirection) {
12
- const query = filters.reduce((queryResult, filter) => {
12
+ function buildFilterQuery(filters) {
13
+ return filters.reduce((queryResult, filter) => {
13
14
  const { filterName, filterNameMax, type, value, maxValue, isAList } = filter;
14
15
  if (isEmptyValue(value) && isEmptyValue(maxValue)) {
15
16
  return queryResult;
@@ -31,11 +32,22 @@ function buildQuery(filters, page = 1, pageSize = 100, sortBy = null, sortDirect
31
32
  if (filterNameMax && !isEmptyValue(maxValue)) {
32
33
  const convertedMaxValue = convertType(type, maxValue);
33
34
  if (convertedMaxValue !== null) {
34
- queryResult[filterNameMax] = convertedMaxValue;
35
+ queryResult[filterNameMax] =
36
+ convertedMaxValue;
35
37
  }
36
38
  }
37
39
  return queryResult;
38
40
  }, {});
41
+ }
42
+ /**
43
+ * Builds a typed query object from filter form values, pagination, and sorting.
44
+ *
45
+ * Empty filter values are ignored.
46
+ * List filters are converted item by item.
47
+ * `OptionValue` lists send their `value` fields instead of the display names.
48
+ */
49
+ function buildQuery(filters, page = 1, pageSize = 100, sortBy = null, sortDirection) {
50
+ const query = buildFilterQuery(filters);
39
51
  query.pageNumber = page;
40
52
  if (pageSize > 0) {
41
53
  query.pageSize = pageSize;
@@ -1 +1 @@
1
- {"version":3,"file":"buildQuery.js","sourceRoot":"","sources":["../../../src/utils/buildQuery.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"buildQuery.js","sourceRoot":"","sources":["../../../src/utils/buildQuery.ts"],"names":[],"mappings":"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"}