typedapi-client-helpers 0.2.1 → 0.3.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +221 -220
  3. package/dist/cjs/httpClient.js +258 -0
  4. package/dist/cjs/httpClient.js.map +1 -0
  5. package/dist/cjs/index.js +32 -0
  6. package/dist/cjs/index.js.map +1 -0
  7. package/dist/cjs/interfaces/ApiResult.js +3 -0
  8. package/dist/cjs/interfaces/ApiResult.js.map +1 -0
  9. package/dist/cjs/interfaces/Filter.js +3 -0
  10. package/dist/cjs/interfaces/Filter.js.map +1 -0
  11. package/dist/cjs/interfaces/SortType.js +3 -0
  12. package/dist/cjs/interfaces/SortType.js.map +1 -0
  13. package/dist/cjs/package.json +3 -0
  14. package/dist/cjs/types/ApiCallOptions.js +3 -0
  15. package/dist/cjs/types/ApiCallOptions.js.map +1 -0
  16. package/dist/cjs/types/ApiMethodArguments.js +3 -0
  17. package/dist/cjs/types/ApiMethodArguments.js.map +1 -0
  18. package/dist/cjs/types/ApiMethodOptions.js +3 -0
  19. package/dist/cjs/types/ApiMethodOptions.js.map +1 -0
  20. package/dist/cjs/types/ApiMethodTypes.js +3 -0
  21. package/dist/cjs/types/ApiMethodTypes.js.map +1 -0
  22. package/dist/cjs/types/HttpResponse.js +3 -0
  23. package/dist/cjs/types/HttpResponse.js.map +1 -0
  24. package/dist/cjs/types/httpClientTypes.js +3 -0
  25. package/dist/cjs/types/httpClientTypes.js.map +1 -0
  26. package/dist/cjs/utils/SortTypeFromSortDirection.js +43 -0
  27. package/dist/cjs/utils/SortTypeFromSortDirection.js.map +1 -0
  28. package/dist/cjs/utils/buildQuery.js +96 -0
  29. package/dist/cjs/utils/buildQuery.js.map +1 -0
  30. package/dist/cjs/utils/extractArgsCallbacksAndParams.js +51 -0
  31. package/dist/cjs/utils/extractArgsCallbacksAndParams.js.map +1 -0
  32. package/dist/cjs/utils/extractArgsToastsAndParams.js +49 -0
  33. package/dist/cjs/utils/extractArgsToastsAndParams.js.map +1 -0
  34. package/dist/cjs/utils/handleApiResponse.js +124 -0
  35. package/dist/cjs/utils/handleApiResponse.js.map +1 -0
  36. package/dist/cjs/utils/toFormData.js +58 -0
  37. package/dist/cjs/utils/toFormData.js.map +1 -0
  38. package/dist/cjs/utils/wireMapping.js +123 -0
  39. package/dist/cjs/utils/wireMapping.js.map +1 -0
  40. package/dist/esm/httpClient.js +251 -0
  41. package/dist/esm/httpClient.js.map +1 -0
  42. package/dist/esm/index.js +16 -0
  43. package/dist/esm/index.js.map +1 -0
  44. package/dist/esm/interfaces/ApiResult.js +2 -0
  45. package/dist/esm/interfaces/ApiResult.js.map +1 -0
  46. package/dist/esm/interfaces/Filter.js +2 -0
  47. package/dist/esm/interfaces/Filter.js.map +1 -0
  48. package/dist/esm/interfaces/SortType.js +2 -0
  49. package/dist/esm/interfaces/SortType.js.map +1 -0
  50. package/dist/esm/types/ApiCallOptions.js +2 -0
  51. package/dist/esm/types/ApiCallOptions.js.map +1 -0
  52. package/dist/esm/types/ApiMethodArguments.js +2 -0
  53. package/dist/esm/types/ApiMethodArguments.js.map +1 -0
  54. package/dist/esm/types/ApiMethodOptions.js +2 -0
  55. package/dist/esm/types/ApiMethodOptions.js.map +1 -0
  56. package/dist/esm/types/ApiMethodTypes.js +2 -0
  57. package/dist/esm/types/ApiMethodTypes.js.map +1 -0
  58. package/dist/esm/types/HttpResponse.js +2 -0
  59. package/dist/esm/types/HttpResponse.js.map +1 -0
  60. package/dist/esm/types/httpClientTypes.js +2 -0
  61. package/dist/esm/types/httpClientTypes.js.map +1 -0
  62. package/dist/esm/utils/SortTypeFromSortDirection.js +38 -0
  63. package/dist/esm/utils/SortTypeFromSortDirection.js.map +1 -0
  64. package/dist/esm/utils/buildQuery.js +93 -0
  65. package/dist/esm/utils/buildQuery.js.map +1 -0
  66. package/dist/esm/utils/extractArgsCallbacksAndParams.js +48 -0
  67. package/dist/esm/utils/extractArgsCallbacksAndParams.js.map +1 -0
  68. package/dist/esm/utils/extractArgsToastsAndParams.js +46 -0
  69. package/dist/esm/utils/extractArgsToastsAndParams.js.map +1 -0
  70. package/dist/esm/utils/handleApiResponse.js +121 -0
  71. package/dist/esm/utils/handleApiResponse.js.map +1 -0
  72. package/dist/esm/utils/toFormData.js +55 -0
  73. package/dist/esm/utils/toFormData.js.map +1 -0
  74. package/dist/esm/utils/wireMapping.js +119 -0
  75. package/dist/esm/utils/wireMapping.js.map +1 -0
  76. package/dist/types/httpClient.d.ts +63 -0
  77. package/dist/types/httpClient.d.ts.map +1 -0
  78. package/dist/types/index.d.ts +16 -0
  79. package/dist/types/index.d.ts.map +1 -0
  80. package/dist/types/interfaces/ApiResult.d.ts +32 -0
  81. package/dist/types/interfaces/ApiResult.d.ts.map +1 -0
  82. package/dist/types/interfaces/Filter.d.ts +31 -0
  83. package/dist/types/interfaces/Filter.d.ts.map +1 -0
  84. package/dist/types/interfaces/SortType.d.ts +7 -0
  85. package/dist/types/interfaces/SortType.d.ts.map +1 -0
  86. package/dist/types/types/ApiCallOptions.d.ts +26 -0
  87. package/dist/types/types/ApiCallOptions.d.ts.map +1 -0
  88. package/dist/types/types/ApiMethodArguments.d.ts +6 -0
  89. package/dist/types/types/ApiMethodArguments.d.ts.map +1 -0
  90. package/dist/types/types/ApiMethodOptions.d.ts +8 -0
  91. package/dist/types/types/ApiMethodOptions.d.ts.map +1 -0
  92. package/dist/types/types/ApiMethodTypes.d.ts +47 -0
  93. package/dist/types/types/ApiMethodTypes.d.ts.map +1 -0
  94. package/dist/types/types/HttpResponse.d.ts +2 -0
  95. package/dist/types/types/HttpResponse.d.ts.map +1 -0
  96. package/dist/types/types/httpClientTypes.d.ts +2 -0
  97. package/dist/types/types/httpClientTypes.d.ts.map +1 -0
  98. package/dist/types/utils/SortTypeFromSortDirection.d.ts +13 -0
  99. package/dist/types/utils/SortTypeFromSortDirection.d.ts.map +1 -0
  100. package/dist/types/utils/buildQuery.d.ts +16 -0
  101. package/dist/types/utils/buildQuery.d.ts.map +1 -0
  102. package/dist/types/utils/extractArgsCallbacksAndParams.d.ts +22 -0
  103. package/dist/types/utils/extractArgsCallbacksAndParams.d.ts.map +1 -0
  104. package/dist/types/utils/extractArgsToastsAndParams.d.ts +20 -0
  105. package/dist/types/utils/extractArgsToastsAndParams.d.ts.map +1 -0
  106. package/dist/types/utils/handleApiResponse.d.ts +11 -0
  107. package/dist/types/utils/handleApiResponse.d.ts.map +1 -0
  108. package/dist/types/utils/toFormData.d.ts +10 -0
  109. package/dist/types/utils/toFormData.d.ts.map +1 -0
  110. package/dist/types/utils/wireMapping.d.ts +30 -0
  111. package/dist/types/utils/wireMapping.d.ts.map +1 -0
  112. package/package.json +90 -84
  113. package/scripts/build.mjs +74 -0
  114. package/scripts/generate-api-minimal.mjs +568 -476
  115. package/scripts/openapi-generator/controller-template.mjs +1 -1
  116. package/scripts/openapi-generator/index.mjs +357 -113
  117. package/scripts/openapi-generator/method-template.mjs +201 -244
  118. package/scripts/openapi-generator/names.mjs +37 -86
  119. package/scripts/openapi-generator/openapi-utils.mjs +411 -147
  120. package/scripts/openapi-generator/schema.mjs +518 -238
  121. package/dist/index.cjs +0 -536
  122. package/dist/index.d.cts +0 -285
  123. package/dist/index.d.ts +0 -285
  124. package/dist/index.js +0 -495
package/CHANGELOG.md ADDED
@@ -0,0 +1,68 @@
1
+ # Changelog
2
+
3
+ All notable changes to `typedapi-client-helpers` are documented in this file.
4
+
5
+ ## [0.3.0] - 2026-07-10
6
+
7
+ ### Added
8
+
9
+ * Added serializer-aware wire mapping through `toWireValue()` and `fromWireValue()`.
10
+ * Added support for exact OpenAPI property names in JSON bodies, multipart forms, query parameters, headers, cookies, path parameters, responses, and documented error bodies.
11
+ * Added support for path, query, header, cookie, and request-body inputs in the same operation.
12
+ * Added typed unions for documented non-success response bodies.
13
+ * Added structured client errors for network failures, aborted requests, and malformed responses.
14
+ * Added support for local OpenAPI `$ref` values in schemas, parameters, path items, request bodies, and responses.
15
+ * Added support for `allOf` schemas with additional local properties.
16
+ * Added configurable frontend method naming through `typedApiMethodNameStyle`.
17
+ * Added `typedApiPrefixMethodNamesWithController` for controller-prefixed action names.
18
+ * Added controller-name fallback detection from TypedApi metadata, OpenAPI tags, and route paths.
19
+ * Added generated root `index.ts` barrel exports.
20
+ * Added `typedapi.manifest.json` containing generator, contract, source, and naming information.
21
+ * Added `--check`, `--strict`, `--offline`, and `--verbose` generator modes.
22
+ * Added configurable Swagger download timeouts.
23
+ * Added global and per-request timeout support.
24
+ * Added support for external `AbortSignal` values and combined cancellation signals.
25
+ * Added header and cookie utilities through `mergeHeaders()`, `toRequestHeaders()`, and `toCookieHeader()`.
26
+ * Added automated generator and runtime tests.
27
+
28
+ ### Changed
29
+
30
+ * Generated TypeScript property names now lowercase only the first character while preserving the remainder of the OpenAPI name.
31
+ * Generated operation IDs and TypeScript identifiers are validated and sanitized before files are written.
32
+ * Missing operation IDs now cause a clear error by default.
33
+ * Missing operation IDs can optionally be generated with `typedApiGenerateMissingOperationIds`.
34
+ * Action-based names can include the controller name by default, preventing collisions between actions with the same name in different controllers.
35
+ * Controller prefixes are not duplicated when an action name already begins with the controller name.
36
+ * Generated method signatures now keep non-body parameters, request bodies, and request options in predictable argument groups.
37
+ * Body-only operations now accept their payload directly without an additional request wrapper.
38
+ * Query-only and paginated operations now use more specific generated query types.
39
+ * Documented backend errors are returned alongside structured TypedApi client errors.
40
+ * Malformed JSON is now returned as a structured parse error instead of being cast to the expected response type.
41
+ * Exceptions thrown by consumer `onSuccess` and `onError` callbacks now propagate normally.
42
+ * Fetch header merging now uses the native `Headers` implementation.
43
+ * File, Blob, FormData, and multipart handling is safer in SSR and Node.js environments.
44
+ * Binary image, PDF, and octet-stream responses are handled as blobs.
45
+ * The package build now uses a deterministic TypeScript-only ESM, CommonJS, and declaration build instead of a bundler-dependent build.
46
+
47
+ ### Fixed
48
+
49
+ * Fixed generated-output replacement failures caused by transient Windows filesystem locks.
50
+ * Added retries for filesystem errors encountered while replacing generated output.
51
+ * Added rollback-protected managed-file synchronization when `src/api` cannot be renamed because an editor, TypeScript watcher, antivirus scanner, or development server holds a directory handle.
52
+ * Preserved unmanaged files during fallback synchronization.
53
+ * Removed stale TypedApi-generated files during fallback synchronization.
54
+ * Fixed handling of cancel token `0`.
55
+ * Fixed abort-controller cleanup after completed requests.
56
+ * Fixed URLs that already contain query strings.
57
+ * Fixed absolute URL handling.
58
+ * Fixed duplicate generated method and type names after TypeScript normalization.
59
+ * Fixed property-name collisions caused by first-character normalization.
60
+
61
+ ### Migration notes
62
+
63
+ * OpenAPI 3.x is now required. Swagger 2.0 documents are rejected.
64
+ * Regenerated method names, parameter types, property names, and method signatures may differ from version `0.2.1`.
65
+ * Projects using `typedApiMethodNameStyle: "action"` receive controller-prefixed names by default.
66
+ * Set `typedApiPrefixMethodNamesWithController` to `false` to generate action-only names.
67
+ * Set `typedApiGenerateMissingOperationIds` to `true` only when operation IDs cannot be supplied by the backend.
68
+ * Regenerate and review committed API files after upgrading.
package/README.md CHANGED
@@ -1,220 +1,221 @@
1
- # typedapi-client-helpers
2
-
3
- `typedapi-client-helpers` generates a typed TypeScript fetch client from an OpenAPI/Swagger document and adds reusable helper types and functions for API results, callbacks, filtering, sorting, request parameters, and multipart uploads.
4
-
5
- The package is intended for frontend projects that want a generated API client while still keeping generated files inside their own source tree.
6
-
7
- ## Installation
8
-
9
- ```bash
10
- npm install typedapi-client-helpers
11
- ```
12
-
13
- ## Basic usage
14
-
15
- Add a generator script to your project:
16
-
17
- ```json
18
- {
19
- "scripts": {
20
- "generate:api": "typedapi-generate"
21
- }
22
- }
23
- ```
24
-
25
- Run the generator:
26
-
27
- ```bash
28
- npm run generate:api
29
- ```
30
-
31
- By default, the generator first looks for a local `swagger/swagger.json` file in the project where the command is run. If that file is not available, it tries `https://localhost:7000/swagger/v1/swagger.json` and stores a validated backup copy inside this package for later runs.
32
-
33
- You can also run the command directly:
34
-
35
- ```bash
36
- npx typedapi-generate
37
- ```
38
-
39
- ## Configuration
40
-
41
- Configuration can be supplied in the `config` section of your `package.json`, with environment variables, or with npm command-line arguments.
42
-
43
- Configuration is resolved in this order:
44
-
45
- 1. environment variables;
46
- 2. npm command-line config values;
47
- 3. the consuming project's `package.json` config;
48
- 4. this package's default config values;
49
- 5. built-in fallbacks.
50
-
51
- ### Package configuration
52
-
53
- ```json
54
- {
55
- "config": {
56
- "swaggerUrl": "https://localhost:7000/swagger/v1/swagger.json",
57
- "swaggerFile": "swagger/openapi.json",
58
- "typedApiSwaggerBackupFile": "swagger/swagger.backup.json",
59
- "apiOutput": "src/api",
60
- "typedApiCleanOutput": true,
61
- "typedApiModuleNameFirstTag": true,
62
- "typedApiUseTypeOnlyImports": true,
63
- "typedApiDefaultFunctionsPath": "src/defaultApiFunctions",
64
- "typedApiDefaultSuccessHandler": "handleGoodResult",
65
- "typedApiDefaultErrorHandler": "handleErrors",
66
- "typedApiDefaultResponseAsSuccess": false,
67
- "typedApiGenerateUnionEnums": false,
68
- "typedApiEnumNamesAsValues": false,
69
- "typedApiBaseUrl": "https://localhost:7000"
70
- }
71
- }
72
- ```
73
-
74
- `swaggerFile` takes precedence over `swaggerUrl` when both are configured.
75
-
76
- ### OpenAPI input and backup behavior
77
-
78
- The generator resolves the OpenAPI/Swagger input as follows:
79
-
80
- 1. If `swaggerFile` is configured and the file exists, that file is used and copied to the backup file.
81
- 2. If `swaggerFile` is configured but missing, the generator uses the backup file when it exists. If no backup exists, generation fails.
82
- 3. If `swaggerUrl` is configured, the generator downloads it, validates that the response is JSON, writes it to the backup file, and generates from that backup copy.
83
- 4. If `swaggerUrl` is configured but unavailable, the generator uses the backup file when it exists. If no backup exists, generation fails.
84
- 5. If neither `swaggerFile` nor `swaggerUrl` is configured, the generator tries `swagger/swagger.json` in the current project.
85
- 6. If no local default file exists, the generator tries `https://localhost:7000/swagger/v1/swagger.json` and falls back to the backup file if the URL is unavailable.
86
-
87
- The backup prevents a temporary unavailable Swagger endpoint from breaking generation when a valid backup was created earlier.
88
-
89
- The backup file is always package-owned: `swagger/swagger.backup.json` inside `typedapi-client-helpers`. Consumers do not need to add or configure a backup file in their own project.
90
-
91
- ### Configuration options
92
-
93
- | Option | Default | Description |
94
- | ---------------------------------- | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
95
- | `swaggerUrl` | `https://localhost:7000/swagger/v1/swagger.json` | URL of the OpenAPI/Swagger document. Used when no configured or default local file is available. |
96
- | `swaggerFile` | none | Local path to an OpenAPI/Swagger document. When set, this is used before `swaggerUrl`. |
97
- | `apiOutput` | `src/api` | Directory where the generated API files are written. |
98
- | `typedApiCleanOutput` | `true` | Cleans generated API output before writing the new files. |
99
- | `typedApiModuleNameFirstTag` | `true` | Groups operations by their first OpenAPI tag, creating one controller file per tag. |
100
- | `typedApiUseTypeOnlyImports` | `true` | Generates `import type` statements for imports that are only used as types. Set to `false` for normal imports. |
101
- | `typedApiDefaultFunctionsPath` | `src/defaultApiFunctions` | Path to the project file that exports the shared default API success and error handlers. Created when missing. |
102
- | `typedApiDefaultSuccessHandler` | `handleGoodResult` | Export name of the default success handler imported into generated method files. |
103
- | `typedApiDefaultErrorHandler` | `handleErrors` | Export name of the default error handler imported into generated method files. |
104
- | `typedApiDefaultResponseAsSuccess` | `false` | Uses the OpenAPI `default` response as a success response when no 2xx response is available. |
105
- | `typedApiGenerateUnionEnums` | `false` | Generates enum schemas as string-literal union types instead of `as const` enum-like objects. |
106
- | `typedApiEnumNamesAsValues` | `false` | When OpenAPI uses `x-enumNames`, uses those names as enum/union values instead of only as object keys. |
107
- | `typedApiBaseUrl` | first OpenAPI server URL | Overrides the generated HTTP client's default `baseUrl`. |
108
-
109
- ### Environment variables
110
-
111
- | Environment variable | Description |
112
- | --------------------------------------- | ------------------------------------------------------------ |
113
- | `SWAGGER_URL` | URL of the OpenAPI/Swagger document. |
114
- | `SWAGGER_FILE` | Local path to an OpenAPI/Swagger document. |
115
- | `API_OUTPUT` | Directory where generated API files are written. |
116
- | `TYPED_API_CLEAN_OUTPUT` | Cleans generated API output when set to `true`. |
117
- | `TYPED_API_MODULE_NAME_FIRST_TAG` | Groups operations by first OpenAPI tag when set to `true`. |
118
- | `TYPED_API_USE_TYPE_ONLY_IMPORTS` | Enables type-only imports when set to `true`. |
119
- | `TYPED_API_DEFAULT_FUNCTIONS_PATH` | Path to the shared default handler file. |
120
- | `TYPED_API_DEFAULT_SUCCESS_HANDLER` | Export name of the default success handler. |
121
- | `TYPED_API_DEFAULT_ERROR_HANDLER` | Export name of the default error handler. |
122
- | `TYPED_API_DEFAULT_RESPONSE_AS_SUCCESS` | Allows the OpenAPI `default` response to be used as success. |
123
- | `TYPED_API_GENERATE_UNION_ENUMS` | Generates enum schemas as union types when set to `true`. |
124
- | `TYPED_API_ENUM_NAMES_AS_VALUES` | Generates enum-like literal values when set to `true`. |
125
- | `TYPED_API_BASE_URL` | Overrides the generated HTTP client's default base URL. |
126
-
127
- Example:
128
-
129
- ```bash
130
- SWAGGER_URL=https://localhost:7000/swagger/v1/swagger.json npm run generate:api
131
- ```
132
-
133
- ### Command-line npm config
134
-
135
- ```bash
136
- npm run generate:api --swagger-url=https://localhost:7000/swagger/v1/swagger.json
137
- npm run generate:api --swagger-file=swagger/openapi.json
138
- npm run generate:api --typed-api-swagger-backup-file=swagger/swagger.backup.json
139
- npm run generate:api --api-output=src/api
140
- npm run generate:api --typed-api-use-type-only-imports=true
141
- npm run generate:api --typed-api-module-name-first-tag=true
142
- npm run generate:api --typed-api-generate-union-enums=true
143
- ```
144
-
145
- ## Generated structure
146
-
147
- Running the generator creates a structure similar to this:
148
-
149
- ```text
150
- src/
151
- └── api/
152
- ├── generated/
153
- │ ├── data-contracts.ts
154
- │ └── http-client.ts
155
- ├── methods/
156
- │ ├── Product.api.ts
157
- │ └── Supplier.api.ts
158
- └── index.ts
159
- ```
160
-
161
- | Folder or file | Description |
162
- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
163
- | `generated/data-contracts.ts` | TypeScript models, request parameter types, const enum-like objects, and multipart payload interfaces generated from the OpenAPI schema. |
164
- | `generated/http-client.ts` | Function-based fetch client with request params, security worker support, custom fetch support, cancellation tokens, and content formatting. It does not generate a class. |
165
- | `methods/*.api.ts` | One generated file per controller/tag. These files export plain functions such as `getProducts`, `createProduct`, and `uploadProductFiles`; there are no generated controller classes. |
166
- | `index.ts` | Barrel file that exports contracts, HTTP client utilities, and callable methods. |
167
-
168
- The generated `generated/` folder, `methods/` folder, old `controllers/` folder, and `index.ts` are cleaned each time the generator runs.
169
-
170
- The `generated/` and `methods/` folders are cleaned and recreated each time the generator runs.
171
-
172
- ## Calling generated methods
173
-
174
- Generated method files export plain functions that can be called directly. The wrapper return type stays `ApiResult<T>`, so existing UI code can keep using `result.ok`, `result.status`, `result.response`, and `result.error`.
175
-
176
- ```ts
177
- import {
178
- getProducts,
179
- getProductById,
180
- updateProduct,
181
- } from "./api/methods/Product.api";
182
-
183
- const products = await getProducts([], 1, 25);
184
- const product = await getProductById({ id: "..." });
185
- await updateProduct(
186
- { id: "..." },
187
- { name: "Updated", sku: "SKU-1", price: 10, stock: 5, active: true },
188
- );
189
- ```
190
-
191
- The generated method signatures stay close to the existing callable wrappers:
192
-
193
- ```ts
194
- getProducts(filters?, page?, pageSize?, sortBy?, sortDirection?, options?);
195
- getProductById({ id }, options?);
196
- updateProduct({ id }, data, options?);
197
- uploadProductFile(data, options?);
198
- ```
199
-
200
- `options.params` is the generated `RequestParams` type and is forwarded directly to the fetch client.
201
-
202
- ### Default success and error handlers
203
-
204
- Generated method files import default handlers from `typedApiDefaultFunctionsPath` when that file exports the configured handler names. With the package defaults, the generator uses `src/defaultApiFunctions.ts` and creates it automatically when it is missing.
205
-
206
- ```ts
207
- export function handleGoodResult<T>(
208
- response: ApiSuccessResult<T>,
209
- ): void | Promise<void> {
210
- // default success behavior
211
- }
212
-
213
- export function handleErrors<T>(
214
- error: ApiErrorResult<T>,
215
- ): void | Promise<void> {
216
- // default error behavior
217
- }
218
- ```
219
-
220
- You can override the path or export names from the consuming project's `package.json` without changing generated method signatures.
1
+ # typedapi-client-helpers
2
+
3
+ A small Fetch runtime and OpenAPI 3 TypeScript generator designed to work with `TypedApi.Swagger` on ASP.NET Core.
4
+
5
+ ## Version 0.3 highlights
6
+
7
+ - Lowercases only the first character of generated TypeScript members while preserving exact OpenAPI names on the wire.
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
+ - Produces `index.ts` and `typedapi.manifest.json`.
15
+ - Writes generated output transactionally, with a Windows-safe in-place fallback when directory renames are blocked by editors or file watchers.
16
+ - Adds `--check`, `--strict`, `--offline`, and `--verbose` CLI modes.
17
+ - Improves cancellation, timeouts, header merging, SSR safety, and malformed-response handling.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npm install typedapi-client-helpers@0.3.0
23
+ ```
24
+
25
+ The generator expects an OpenAPI 3.x document. Swagger 2.0 documents are rejected with a clear error.
26
+
27
+ ## Configure generation
28
+
29
+ Add settings to the consuming application's `package.json`:
30
+
31
+ ```json
32
+ {
33
+ "scripts": {
34
+ "generate:api": "typedapi-generate",
35
+ "check:api": "typedapi-generate --check"
36
+ },
37
+ "config": {
38
+ "swaggerUrl": "https://localhost:7000/swagger/v1/swagger.json",
39
+ "apiOutput": "src/api",
40
+ "typedApiSwaggerBackupFile": "swagger/swagger.backup.json",
41
+ "typedApiDownloadTimeoutMs": 15000,
42
+ "typedApiCleanOutput": true,
43
+ "typedApiGenerateMissingOperationIds": false,
44
+ "typedApiMethodNameStyle": "operationId",
45
+ "typedApiPrefixMethodNamesWithController": true
46
+ }
47
+ }
48
+ ```
49
+
50
+ Run:
51
+
52
+ ```bash
53
+ npm run generate:api
54
+ ```
55
+
56
+ Generated output contains:
57
+
58
+ ```text
59
+ src/api/
60
+ ├── generated/
61
+ │ ├── data-contracts.ts
62
+ │ └── http-client.ts
63
+ ├── methods/
64
+ │ └── Products.api.ts
65
+ ├── index.ts
66
+ └── typedapi.manifest.json
67
+ ```
68
+
69
+ The Swagger backup belongs to the consuming project, not this npm package.
70
+
71
+ ## Frontend method names
72
+
73
+ Choose the source of generated function names with `typedApiMethodNameStyle`:
74
+
75
+ ```json
76
+ {
77
+ "config": {
78
+ "typedApiMethodNameStyle": "action",
79
+ "typedApiPrefixMethodNamesWithController": true
80
+ }
81
+ }
82
+ ```
83
+
84
+ - `"operationId"` uses the unique OpenAPI operation ID. The controller-prefix setting does not affect this mode.
85
+ - `"action"` uses the original ASP.NET controller action name.
86
+ - `typedApiPrefixMethodNamesWithController: true` is the default in action mode and prefixes the normalized controller name.
87
+ - `typedApiPrefixMethodNamesWithController: false` removes the controller prefix.
88
+
89
+ For an `OrderController` action named `GetOrderById`:
90
+
91
+ ```text
92
+ typedApiPrefixMethodNamesWithController: true -> orderGetOrderById(...)
93
+ typedApiPrefixMethodNamesWithController: false -> getOrderById(...)
94
+ ```
95
+
96
+ Operation-specific types follow the selected name too:
97
+
98
+ ```text
99
+ true -> OrderGetOrderByIdParams
100
+ false -> GetOrderByIdParams
101
+ ```
102
+
103
+ 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"`.
104
+
105
+
106
+ ## CI consistency check
107
+
108
+ ```bash
109
+ typedapi-generate --check
110
+ ```
111
+
112
+ This generates in memory and exits with an error when committed generated files are missing, stale, or different.
113
+
114
+ Other modes:
115
+
116
+ ```bash
117
+ typedapi-generate --strict # fail instead of using a backup after a download error
118
+ typedapi-generate --offline # intentionally generate from the backup
119
+ typedapi-generate --verbose # print additional diagnostics
120
+ ```
121
+
122
+
123
+ ## Generated property names
124
+
125
+ Generated interface and parameter members lowercase **only the first character**:
126
+
127
+ ```ts
128
+ Files -> files
129
+ URLValue -> uRLValue
130
+ ProductID -> productID
131
+ snake_case -> snake_case
132
+ ```
133
+
134
+ 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.
135
+
136
+ ## Generated method shape
137
+
138
+ 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:
139
+
140
+ ```ts
141
+ await updateWarehouse(
142
+ { id: requireId(context.warehouseId, "Warehouse ID") },
143
+ warehouse,
144
+ );
145
+ ```
146
+
147
+ The generated signature is:
148
+
149
+ ```ts
150
+ export async function updateWarehouse(
151
+ pathParams: UpdateWarehouseParams,
152
+ data: WarehouseRequest,
153
+ options: ApiMethodOptions<WarehouseModel, unknown, RequestParams> = {},
154
+ ): Promise<ApiResult<WarehouseModel, unknown>>;
155
+ ```
156
+
157
+ Path operations without a body use only the params object:
158
+
159
+ ```ts
160
+ await deleteSupplier({ id: requireId(context.supplierId, "Supplier ID") });
161
+ ```
162
+
163
+ Body-only operations accept their payload directly:
164
+
165
+ ```ts
166
+ await uploadProductFiles({ files: selectedFiles });
167
+ ```
168
+
169
+ 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.
170
+
171
+ Request options are always last:
172
+
173
+ ```ts
174
+ await updateWarehouse(pathParams, data, {
175
+ params: {
176
+ signal: abortController.signal,
177
+ timeoutMs: 10_000,
178
+ headers: new Headers({ Authorization: "Bearer ..." }),
179
+ },
180
+ onSuccess: result => console.log(result.response),
181
+ onError: result => console.error(result.error),
182
+ });
183
+ ```
184
+
185
+ ## Typed errors
186
+
187
+ When the OpenAPI document describes error response bodies, generated methods expose their union:
188
+
189
+ ```ts
190
+ const result = await createProduct(body);
191
+
192
+ if (!result.ok) {
193
+ // Documented backend error type or a TypedApi client error:
194
+ // network, aborted, or parse.
195
+ console.error(result.error);
196
+ }
197
+ ```
198
+
199
+ 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.
200
+
201
+ ## Runtime configuration
202
+
203
+ ```ts
204
+ import { configureApiClient, setSecurityData } from "typedapi-client-helpers";
205
+
206
+ configureApiClient({
207
+ baseUrl: "https://api.example.com",
208
+ timeoutMs: 15_000,
209
+ securityWorker: token => token
210
+ ? { headers: { Authorization: `Bearer ${token}` } }
211
+ : undefined
212
+ });
213
+
214
+ setSecurityData("access-token");
215
+ ```
216
+
217
+ The runtime also exports `createApiClient`, `request`, `abortRequest`, `mergeHeaders`, `toRequestHeaders`, `toCookieHeader`, `handleApiResponse`, `buildQuery`, `toFormData`, `toWireValue`, and `fromWireValue`.
218
+
219
+ ## Backend pairing
220
+
221
+ 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.