typedapi-client-helpers 0.1.15 → 0.2.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 (137) hide show
  1. package/README.md +220 -490
  2. package/dist/index.cjs +274 -47
  3. package/dist/index.d.cts +79 -95
  4. package/dist/index.d.ts +79 -95
  5. package/dist/index.js +267 -47
  6. package/package.json +54 -12
  7. package/scripts/generate-api-minimal.mjs +476 -959
  8. package/scripts/openapi-generator/controller-template.mjs +246 -0
  9. package/scripts/openapi-generator/http-client-template.mjs +34 -0
  10. package/scripts/openapi-generator/index.mjs +113 -0
  11. package/scripts/openapi-generator/method-template.mjs +440 -0
  12. package/scripts/openapi-generator/names.mjs +145 -0
  13. package/scripts/openapi-generator/openapi-utils.mjs +147 -0
  14. package/scripts/openapi-generator/schema.mjs +455 -0
  15. package/bin/typedapi-generate.js +0 -3
  16. package/dist/api/generated/Import.d.ts +0 -22
  17. package/dist/api/generated/Import.d.ts.map +0 -1
  18. package/dist/api/generated/Import.js +0 -48
  19. package/dist/api/generated/Import.js.map +0 -1
  20. package/dist/api/generated/ImportRoute.d.ts +0 -30
  21. package/dist/api/generated/ImportRoute.d.ts.map +0 -1
  22. package/dist/api/generated/ImportRoute.js +0 -13
  23. package/dist/api/generated/ImportRoute.js.map +0 -1
  24. package/dist/api/generated/Order.d.ts +0 -38
  25. package/dist/api/generated/Order.d.ts.map +0 -1
  26. package/dist/api/generated/Order.js +0 -75
  27. package/dist/api/generated/Order.js.map +0 -1
  28. package/dist/api/generated/OrderRoute.d.ts +0 -83
  29. package/dist/api/generated/OrderRoute.d.ts.map +0 -1
  30. package/dist/api/generated/OrderRoute.js +0 -13
  31. package/dist/api/generated/OrderRoute.js.map +0 -1
  32. package/dist/api/generated/Product.d.ts +0 -62
  33. package/dist/api/generated/Product.d.ts.map +0 -1
  34. package/dist/api/generated/Product.js +0 -114
  35. package/dist/api/generated/Product.js.map +0 -1
  36. package/dist/api/generated/ProductRoute.d.ts +0 -140
  37. package/dist/api/generated/ProductRoute.d.ts.map +0 -1
  38. package/dist/api/generated/ProductRoute.js +0 -13
  39. package/dist/api/generated/ProductRoute.js.map +0 -1
  40. package/dist/api/generated/Report.d.ts +0 -14
  41. package/dist/api/generated/Report.d.ts.map +0 -1
  42. package/dist/api/generated/Report.js +0 -32
  43. package/dist/api/generated/Report.js.map +0 -1
  44. package/dist/api/generated/ReportRoute.d.ts +0 -23
  45. package/dist/api/generated/ReportRoute.d.ts.map +0 -1
  46. package/dist/api/generated/ReportRoute.js +0 -13
  47. package/dist/api/generated/ReportRoute.js.map +0 -1
  48. package/dist/api/generated/Supplier.d.ts +0 -46
  49. package/dist/api/generated/Supplier.d.ts.map +0 -1
  50. package/dist/api/generated/Supplier.js +0 -87
  51. package/dist/api/generated/Supplier.js.map +0 -1
  52. package/dist/api/generated/SupplierRoute.d.ts +0 -101
  53. package/dist/api/generated/SupplierRoute.d.ts.map +0 -1
  54. package/dist/api/generated/SupplierRoute.js +0 -13
  55. package/dist/api/generated/SupplierRoute.js.map +0 -1
  56. package/dist/api/generated/data-contracts.d.ts +0 -277
  57. package/dist/api/generated/data-contracts.d.ts.map +0 -1
  58. package/dist/api/generated/data-contracts.js +0 -26
  59. package/dist/api/generated/data-contracts.js.map +0 -1
  60. package/dist/api/generated/http-client.d.ts +0 -61
  61. package/dist/api/generated/http-client.d.ts.map +0 -1
  62. package/dist/api/generated/http-client.js +0 -171
  63. package/dist/api/generated/http-client.js.map +0 -1
  64. package/dist/api/index.d.ts +0 -8
  65. package/dist/api/index.d.ts.map +0 -1
  66. package/dist/api/index.js +0 -8
  67. package/dist/api/index.js.map +0 -1
  68. package/dist/api/methods/Import.api.d.ts +0 -6
  69. package/dist/api/methods/Import.api.d.ts.map +0 -1
  70. package/dist/api/methods/Import.api.js +0 -27
  71. package/dist/api/methods/Import.api.js.map +0 -1
  72. package/dist/api/methods/Order.api.d.ts +0 -29
  73. package/dist/api/methods/Order.api.d.ts.map +0 -1
  74. package/dist/api/methods/Order.api.js +0 -52
  75. package/dist/api/methods/Order.api.js.map +0 -1
  76. package/dist/api/methods/Product.api.d.ts +0 -39
  77. package/dist/api/methods/Product.api.d.ts.map +0 -1
  78. package/dist/api/methods/Product.api.js +0 -69
  79. package/dist/api/methods/Product.api.js.map +0 -1
  80. package/dist/api/methods/Report.api.d.ts +0 -6
  81. package/dist/api/methods/Report.api.d.ts.map +0 -1
  82. package/dist/api/methods/Report.api.js +0 -19
  83. package/dist/api/methods/Report.api.js.map +0 -1
  84. package/dist/api/methods/Supplier.api.d.ts +0 -35
  85. package/dist/api/methods/Supplier.api.d.ts.map +0 -1
  86. package/dist/api/methods/Supplier.api.js +0 -63
  87. package/dist/api/methods/Supplier.api.js.map +0 -1
  88. package/dist/index.d.ts.map +0 -1
  89. package/dist/index.js.map +0 -1
  90. package/dist/interfaces/ApiResult.d.ts +0 -17
  91. package/dist/interfaces/ApiResult.d.ts.map +0 -1
  92. package/dist/interfaces/ApiResult.js +0 -2
  93. package/dist/interfaces/ApiResult.js.map +0 -1
  94. package/dist/interfaces/Filter.d.ts +0 -31
  95. package/dist/interfaces/Filter.d.ts.map +0 -1
  96. package/dist/interfaces/Filter.js +0 -2
  97. package/dist/interfaces/Filter.js.map +0 -1
  98. package/dist/interfaces/SortType.d.ts +0 -7
  99. package/dist/interfaces/SortType.d.ts.map +0 -1
  100. package/dist/interfaces/SortType.js +0 -2
  101. package/dist/interfaces/SortType.js.map +0 -1
  102. package/dist/types/ApiCallOptions.d.ts +0 -9
  103. package/dist/types/ApiCallOptions.d.ts.map +0 -1
  104. package/dist/types/ApiCallOptions.js +0 -2
  105. package/dist/types/ApiCallOptions.js.map +0 -1
  106. package/dist/types/ApiMethodTypes.d.ts +0 -24
  107. package/dist/types/ApiMethodTypes.d.ts.map +0 -1
  108. package/dist/types/ApiMethodTypes.js +0 -2
  109. package/dist/types/ApiMethodTypes.js.map +0 -1
  110. package/dist/types/HttpResponse.d.ts +0 -16
  111. package/dist/types/HttpResponse.d.ts.map +0 -1
  112. package/dist/types/HttpResponse.js +0 -2
  113. package/dist/types/HttpResponse.js.map +0 -1
  114. package/dist/utils/SortTypeFromSortDirection.d.ts +0 -22
  115. package/dist/utils/SortTypeFromSortDirection.d.ts.map +0 -1
  116. package/dist/utils/SortTypeFromSortDirection.js +0 -45
  117. package/dist/utils/SortTypeFromSortDirection.js.map +0 -1
  118. package/dist/utils/buildQuery.d.ts +0 -11
  119. package/dist/utils/buildQuery.d.ts.map +0 -1
  120. package/dist/utils/buildQuery.js +0 -94
  121. package/dist/utils/buildQuery.js.map +0 -1
  122. package/dist/utils/extractArgsCallbacksAndParams.d.ts +0 -22
  123. package/dist/utils/extractArgsCallbacksAndParams.d.ts.map +0 -1
  124. package/dist/utils/extractArgsCallbacksAndParams.js +0 -44
  125. package/dist/utils/extractArgsCallbacksAndParams.js.map +0 -1
  126. package/dist/utils/extractArgsToastsAndParams.d.ts +0 -18
  127. package/dist/utils/extractArgsToastsAndParams.d.ts.map +0 -1
  128. package/dist/utils/extractArgsToastsAndParams.js +0 -40
  129. package/dist/utils/extractArgsToastsAndParams.js.map +0 -1
  130. package/dist/utils/handleApiResponse.d.ts +0 -9
  131. package/dist/utils/handleApiResponse.d.ts.map +0 -1
  132. package/dist/utils/handleApiResponse.js +0 -49
  133. package/dist/utils/handleApiResponse.js.map +0 -1
  134. package/dist/utils/toFormData.d.ts +0 -8
  135. package/dist/utils/toFormData.d.ts.map +0 -1
  136. package/dist/utils/toFormData.js +0 -68
  137. package/dist/utils/toFormData.js.map +0 -1
@@ -0,0 +1,440 @@
1
+ import { getOperationTypes, generatedFileHeader } from "./schema.mjs";
2
+ import { pascalCase, sanitizeIdentifier } from "./names.mjs";
3
+
4
+ function unique(values) {
5
+ return [...new Set(values.filter(Boolean))];
6
+ }
7
+
8
+ function createImport(names, from, typeOnly = false) {
9
+ const cleanNames = unique(names).sort((a, b) => a.localeCompare(b));
10
+ if (cleanNames.length === 0) return "";
11
+ const keyword = typeOnly ? "import type" : "import";
12
+ if (cleanNames.length <= 3) {
13
+ return `${keyword} { ${cleanNames.join(", ")} } from ${JSON.stringify(from)};`;
14
+ }
15
+ return `${keyword} {\n${cleanNames.map((name) => ` ${name},`).join("\n")}\n} from ${JSON.stringify(from)};`;
16
+ }
17
+
18
+ function isPrimitiveType(type) {
19
+ return /^(void|any|unknown|string|number|boolean|Blob|File|Record<)/.test(
20
+ type,
21
+ );
22
+ }
23
+
24
+ function extractTopLevelTypeNames(type) {
25
+ const withoutLiterals = String(type ?? "").replace(
26
+ /"(?:\\.|[^"])*"|'(?:\\.|[^'])*'/g,
27
+ "",
28
+ );
29
+ const matches = withoutLiterals.match(/\b[A-Z][A-Za-z0-9_]*\b/g) ?? [];
30
+ return matches.filter(
31
+ (name) =>
32
+ ![
33
+ "Array",
34
+ "Record",
35
+ "Promise",
36
+ "HttpResponse",
37
+ "Blob",
38
+ "File",
39
+ "ApiResult",
40
+ "RequestParams",
41
+ "FormData",
42
+ ].includes(name),
43
+ );
44
+ }
45
+
46
+ function pathExpression(
47
+ routePath,
48
+ pathParams,
49
+ paramsObjectName = "pathParams",
50
+ ) {
51
+ const parts = [];
52
+ let cursor = 0;
53
+ const regex = /\{([^}]+)\}/g;
54
+ let match;
55
+
56
+ while ((match = regex.exec(routePath))) {
57
+ const [placeholder, paramName] = match;
58
+ const before = routePath.slice(cursor, match.index);
59
+ if (before) parts.push(JSON.stringify(before));
60
+ parts.push(
61
+ `encodeURIComponent(String(${paramsObjectName}.${sanitizeIdentifier(paramName)}))`,
62
+ );
63
+ cursor = match.index + placeholder.length;
64
+ }
65
+
66
+ const after = routePath.slice(cursor);
67
+ if (after) parts.push(JSON.stringify(after));
68
+
69
+ if (parts.length === 0) return JSON.stringify(routePath);
70
+
71
+ return parts
72
+ .map((part) =>
73
+ /^"/.test(part) ? part.slice(1, -1).replace(/`/g, "\\`") : `\${${part}}`,
74
+ )
75
+ .join("")
76
+ .replace(/^/, "`")
77
+ .replace(/$/, "`");
78
+ }
79
+
80
+ function requestFormat(responseContentType, responseType) {
81
+ if (responseType === "void") return undefined;
82
+ if (!responseContentType) return undefined;
83
+ if (responseContentType.includes("json")) return "json";
84
+ if (responseContentType.startsWith("text/")) return "text";
85
+ if (
86
+ responseContentType === "application/octet-stream" ||
87
+ responseType === "Blob"
88
+ )
89
+ return "blob";
90
+ return undefined;
91
+ }
92
+
93
+ function contentTypeEnum(contentType) {
94
+ switch (contentType) {
95
+ case "application/json":
96
+ return "ContentType.Json";
97
+ case "application/vnd.api+json":
98
+ return "ContentType.JsonApi";
99
+ case "multipart/form-data":
100
+ return "ContentType.FormData";
101
+ case "application/x-www-form-urlencoded":
102
+ return "ContentType.UrlEncoded";
103
+ case "text/plain":
104
+ return "ContentType.Text";
105
+ default:
106
+ return contentType?.includes("json") ? "ContentType.Json" : undefined;
107
+ }
108
+ }
109
+
110
+ function queryExpression(operationTypes, queryValueName = "query") {
111
+ if (!operationTypes.hasQueryParams) return undefined;
112
+
113
+ if (!operationTypes.hasPathParams) {
114
+ return `${queryValueName} ?? {}`;
115
+ }
116
+
117
+ const entries = operationTypes.queryParams.map((parameter) => {
118
+ const identifier = sanitizeIdentifier(parameter.name);
119
+ return `${JSON.stringify(parameter.name)}: pathParams.${identifier}`;
120
+ });
121
+
122
+ return `{ ${entries.join(", ")} }`;
123
+ }
124
+
125
+ function requestItems(operationInfo, operationTypes, options = {}) {
126
+ const items = [
127
+ `path: ${pathExpression(operationInfo.routePath, operationTypes.pathParams)}`,
128
+ `method: ${JSON.stringify(operationInfo.method)}`,
129
+ ];
130
+
131
+ const query = queryExpression(
132
+ operationTypes,
133
+ options.queryValueName ?? "query",
134
+ );
135
+ if (query) items.push(`query: ${query}`);
136
+
137
+ if (operationTypes.bodyType) {
138
+ items.push(`body: ${options.bodyExpression ?? "data"}`);
139
+ }
140
+
141
+ const type = contentTypeEnum(operationTypes.contentType);
142
+ if (type) items.push(`type: ${type}`);
143
+
144
+ const format = requestFormat(
145
+ operationTypes.responseContentType,
146
+ operationTypes.responseType,
147
+ );
148
+ if (format) items.push(`format: ${JSON.stringify(format)}`);
149
+
150
+ items.push("...params");
151
+ return items;
152
+ }
153
+
154
+ function callbackOptions(defaultHandlers) {
155
+ if (!defaultHandlers) return "{ onSuccess, onError }";
156
+
157
+ return "{ onSuccess: onSuccess ?? typedApiDefaultSuccessHandler, onError: onError ?? typedApiDefaultErrorHandler }";
158
+ }
159
+
160
+ function optionsType(operationTypes) {
161
+ return `ApiMethodOptions<${operationTypes.responseType}, any, RequestParams>`;
162
+ }
163
+
164
+ function promiseType(operationTypes) {
165
+ return `Promise<ApiResult<${operationTypes.responseType}>>`;
166
+ }
167
+
168
+ function isPaginated(operationTypes) {
169
+ return /(?:Api)?Pagination(?:Sort)?Response\b|Paged(?:Response|Result)?\b|Paginated(?:Response|Result)?\b/.test(
170
+ operationTypes.responseType,
171
+ );
172
+ }
173
+
174
+ function methodDescription(operationInfo, operationTypes) {
175
+ const description =
176
+ operationInfo.operation.description ||
177
+ operationInfo.operation.summary ||
178
+ "No description";
179
+ const tags = (operationInfo.operation.tags ?? []).join(", ");
180
+
181
+ return `/**\n * ${description.replace(/\n/g, "\n * ")}\n *\n * @tags ${tags}\n * @name ${operationTypes.operationId}\n * @request ${operationInfo.method}:${operationInfo.routePath}\n */`;
182
+ }
183
+
184
+ function createRequestCall(operationInfo, operationTypes, options = {}) {
185
+ const items = requestItems(operationInfo, operationTypes, options)
186
+ .map((item) => ` ${item},`)
187
+ .join("\n");
188
+
189
+ return `request<${operationTypes.responseType}, any>({\n${items}\n })`;
190
+ }
191
+
192
+ function createPaginatedMethod(operationInfo, operationTypes, defaultHandlers) {
193
+ const queryTypeName = `${pascalCase(operationTypes.methodName)}Query`;
194
+ const requestCall = createRequestCall(operationInfo, operationTypes, {
195
+ queryValueName: "builtQuery",
196
+ });
197
+
198
+ return `${methodDescription(operationInfo, operationTypes)}
199
+ export async function ${operationTypes.methodName}(
200
+ filters: FilterFormValues<${queryTypeName}>[] = [],
201
+ page = 1,
202
+ pageSize = 100,
203
+ sortBy: SortableKeys<${operationTypes.responseType}> | null = null,
204
+ sortDirection?: SortDirection,
205
+ options: ${optionsType(operationTypes)} = {},
206
+ ): ${promiseType(operationTypes)} {
207
+ const { onSuccess, onError, params } = options;
208
+ const builtQuery = buildQuery<
209
+ ${queryTypeName},
210
+ UnwrapArray<ExtractDataIfPaginated<${operationTypes.responseType}>>
211
+ >(filters, page, pageSize, sortBy, sortDirection);
212
+
213
+ return handleApiResponse<${operationTypes.responseType}, any>(
214
+ () => ${requestCall},
215
+ ${callbackOptions(defaultHandlers)},
216
+ );
217
+ }`;
218
+ }
219
+
220
+ function createSimpleQueryMethod(
221
+ operationInfo,
222
+ operationTypes,
223
+ defaultHandlers,
224
+ ) {
225
+ const queryTypeName = `${pascalCase(operationTypes.methodName)}Query`;
226
+ const requestCall = createRequestCall(operationInfo, operationTypes, {
227
+ queryValueName: "query",
228
+ });
229
+
230
+ return `${methodDescription(operationInfo, operationTypes)}
231
+ export async function ${operationTypes.methodName}(
232
+ query?: ${queryTypeName},
233
+ options: ${optionsType(operationTypes)} = {},
234
+ ): ${promiseType(operationTypes)} {
235
+ const { onSuccess, onError, params } = options;
236
+
237
+ return handleApiResponse<${operationTypes.responseType}, any>(
238
+ () => ${requestCall},
239
+ ${callbackOptions(defaultHandlers)},
240
+ );
241
+ }`;
242
+ }
243
+
244
+ function wrapperArguments(operationTypes) {
245
+ const args = [];
246
+
247
+ if (operationTypes.paramsTypeName && operationTypes.hasPathParams) {
248
+ args.push(`pathParams: ${operationTypes.paramsTypeName}`);
249
+ }
250
+
251
+ if (operationTypes.bodyType) {
252
+ args.push(`data: ${operationTypes.bodyType}`);
253
+ }
254
+
255
+ args.push(`options: ${optionsType(operationTypes)} = {}`);
256
+ return args.join(",\n ");
257
+ }
258
+
259
+ function createRegularMethod(operationInfo, operationTypes, defaultHandlers) {
260
+ const isFormData = operationTypes.contentType === "multipart/form-data";
261
+ const requestCall = createRequestCall(operationInfo, operationTypes, {
262
+ bodyExpression: isFormData ? "formData" : "data",
263
+ });
264
+
265
+ const formDataLine = isFormData
266
+ ? "\n const formData = toFormData(data);\n"
267
+ : "";
268
+
269
+ return `${methodDescription(operationInfo, operationTypes)}
270
+ export async function ${operationTypes.methodName}(
271
+ ${wrapperArguments(operationTypes)}
272
+ ): ${promiseType(operationTypes)} {
273
+ const { onSuccess, onError, params } = options;${formDataLine}
274
+ return handleApiResponse<${operationTypes.responseType}, any>(
275
+ () => ${requestCall},
276
+ ${callbackOptions(defaultHandlers)},
277
+ );
278
+ }`;
279
+ }
280
+
281
+ function createMethod(operationInfo, operationTypes, defaultHandlers, options) {
282
+ if (
283
+ operationTypes.hasQueryParams &&
284
+ !operationTypes.hasPathParams &&
285
+ options.useFilterFormValues !== false &&
286
+ isPaginated(operationTypes)
287
+ ) {
288
+ return createPaginatedMethod(
289
+ operationInfo,
290
+ operationTypes,
291
+ defaultHandlers,
292
+ );
293
+ }
294
+
295
+ if (
296
+ operationTypes.hasQueryParams &&
297
+ !operationTypes.hasPathParams &&
298
+ !operationTypes.bodyType
299
+ ) {
300
+ return createSimpleQueryMethod(
301
+ operationInfo,
302
+ operationTypes,
303
+ defaultHandlers,
304
+ );
305
+ }
306
+
307
+ return createRegularMethod(operationInfo, operationTypes, defaultHandlers);
308
+ }
309
+
310
+ export function methodFileNameForController(controllerName) {
311
+ return `${controllerName}.api.ts`;
312
+ }
313
+
314
+ export function controllerNameForOperation(operationInfo, options = {}) {
315
+ if (options.moduleNameFirstTag !== false) {
316
+ const [tag] = operationInfo.operation.tags ?? [];
317
+ if (tag) return pascalCase(tag, "Default");
318
+ }
319
+
320
+ const firstPathPart =
321
+ operationInfo.routePath.split("/").filter(Boolean)[1] ??
322
+ operationInfo.routePath.split("/").filter(Boolean)[0] ??
323
+ "Default";
324
+
325
+ return pascalCase(firstPathPart, "Default");
326
+ }
327
+
328
+ export function generateMethodFile(
329
+ openApi,
330
+ controllerName,
331
+ operations,
332
+ options = {},
333
+ ) {
334
+ const dataContractImports = [];
335
+ const helperTypeImports = ["ApiResult", "ApiMethodOptions"];
336
+ const helperValueImports = ["handleApiResponse"];
337
+ const methods = [];
338
+ const queryAliases = [];
339
+ let needsContentType = false;
340
+ let needsToFormData = false;
341
+ let needsPaginationTypes = false;
342
+
343
+ for (const operationInfo of operations) {
344
+ const operationTypes = getOperationTypes(openApi, operationInfo, options);
345
+ if (!operationTypes.operationId || !operationTypes.methodName) continue;
346
+
347
+ if (operationTypes.paramsTypeName)
348
+ dataContractImports.push(operationTypes.paramsTypeName);
349
+ if (
350
+ operationTypes.bodyType &&
351
+ !isPrimitiveType(operationTypes.bodyType)
352
+ ) {
353
+ dataContractImports.push(
354
+ ...extractTopLevelTypeNames(operationTypes.bodyType),
355
+ );
356
+ }
357
+ if (!isPrimitiveType(operationTypes.responseType)) {
358
+ dataContractImports.push(
359
+ ...extractTopLevelTypeNames(operationTypes.responseType),
360
+ );
361
+ }
362
+
363
+ if (operationTypes.hasQueryParams && !operationTypes.hasPathParams) {
364
+ const queryTypeName = `${pascalCase(operationTypes.methodName)}Query`;
365
+ queryAliases.push(
366
+ `export type ${queryTypeName} = NonNullable<${operationTypes.paramsTypeName}>;`,
367
+ );
368
+ }
369
+
370
+ if (contentTypeEnum(operationTypes.contentType)) needsContentType = true;
371
+ if (operationTypes.contentType === "multipart/form-data")
372
+ needsToFormData = true;
373
+ if (
374
+ operationTypes.hasQueryParams &&
375
+ !operationTypes.hasPathParams &&
376
+ options.useFilterFormValues !== false &&
377
+ isPaginated(operationTypes)
378
+ ) {
379
+ needsPaginationTypes = true;
380
+ }
381
+
382
+ methods.push(
383
+ createMethod(
384
+ operationInfo,
385
+ operationTypes,
386
+ options.defaultHandlers,
387
+ options,
388
+ ),
389
+ );
390
+ }
391
+
392
+ if (needsToFormData) helperValueImports.push("toFormData");
393
+ if (needsPaginationTypes) {
394
+ helperValueImports.push("buildQuery");
395
+ helperTypeImports.push(
396
+ "ExtractDataIfPaginated",
397
+ "FilterFormValues",
398
+ "SortableKeys",
399
+ "SortDirection",
400
+ "UnwrapArray",
401
+ );
402
+ }
403
+
404
+ const defaultHandlers = options.defaultHandlers;
405
+ const useTypeOnlyImports = options.useTypeOnlyImports !== false;
406
+ const imports = [
407
+ generatedFileHeader(),
408
+ createImport(
409
+ ["request", ...(needsContentType ? ["ContentType"] : [])],
410
+ "../generated/http-client",
411
+ ),
412
+ createImport(
413
+ ["RequestParams"],
414
+ "../generated/http-client",
415
+ useTypeOnlyImports,
416
+ ),
417
+ createImport(
418
+ dataContractImports,
419
+ "../generated/data-contracts",
420
+ useTypeOnlyImports,
421
+ ),
422
+ createImport(
423
+ helperValueImports,
424
+ options.runtimePackageName ?? "typedapi-client-helpers",
425
+ ),
426
+ createImport(
427
+ helperTypeImports,
428
+ options.runtimePackageName ?? "typedapi-client-helpers",
429
+ useTypeOnlyImports,
430
+ ),
431
+ defaultHandlers
432
+ ? `import { ${defaultHandlers.success} as typedApiDefaultSuccessHandler, ${defaultHandlers.error} as typedApiDefaultErrorHandler } from ${JSON.stringify(defaultHandlers.path)};`
433
+ : "",
434
+ ].filter(Boolean);
435
+
436
+ const aliases = unique(queryAliases).join("\n");
437
+ const aliasBlock = aliases ? `${aliases}\n\n` : "";
438
+
439
+ return `${imports.join("\n")}\n\n${aliasBlock}${methods.join("\n\n")}\n`;
440
+ }
@@ -0,0 +1,145 @@
1
+ const reservedWords = new Set([
2
+ "abstract",
3
+ "any",
4
+ "as",
5
+ "asserts",
6
+ "async",
7
+ "await",
8
+ "boolean",
9
+ "break",
10
+ "case",
11
+ "catch",
12
+ "class",
13
+ "const",
14
+ "constructor",
15
+ "continue",
16
+ "debugger",
17
+ "declare",
18
+ "default",
19
+ "delete",
20
+ "do",
21
+ "else",
22
+ "enum",
23
+ "export",
24
+ "extends",
25
+ "false",
26
+ "finally",
27
+ "for",
28
+ "from",
29
+ "function",
30
+ "get",
31
+ "if",
32
+ "implements",
33
+ "import",
34
+ "in",
35
+ "infer",
36
+ "instanceof",
37
+ "interface",
38
+ "is",
39
+ "keyof",
40
+ "let",
41
+ "module",
42
+ "namespace",
43
+ "never",
44
+ "new",
45
+ "null",
46
+ "number",
47
+ "object",
48
+ "of",
49
+ "package",
50
+ "private",
51
+ "protected",
52
+ "public",
53
+ "readonly",
54
+ "require",
55
+ "return",
56
+ "set",
57
+ "static",
58
+ "string",
59
+ "super",
60
+ "switch",
61
+ "symbol",
62
+ "this",
63
+ "throw",
64
+ "true",
65
+ "try",
66
+ "type",
67
+ "typeof",
68
+ "undefined",
69
+ "unique",
70
+ "unknown",
71
+ "var",
72
+ "void",
73
+ "while",
74
+ "with",
75
+ "yield",
76
+ ]);
77
+
78
+ export function pascalCase(value, fallback = "Anonymous") {
79
+ const text = String(value ?? "").trim();
80
+ const parts = text
81
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
82
+ .split(/[^A-Za-z0-9]+/)
83
+ .filter(Boolean);
84
+
85
+ const name = parts
86
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
87
+ .join("");
88
+
89
+ return name || fallback;
90
+ }
91
+
92
+ export function camelCase(value, fallback = "value") {
93
+ const pascal = pascalCase(value, pascalCase(fallback));
94
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
95
+ }
96
+
97
+ export function lowerFirst(value) {
98
+ const text = String(value ?? "");
99
+ return text.charAt(0).toLowerCase() + text.slice(1);
100
+ }
101
+
102
+ export function sanitizeIdentifier(value, fallback = "value") {
103
+ let name = String(value ?? "").replace(/[^A-Za-z0-9_$]/g, "_");
104
+
105
+ if (!name) {
106
+ name = fallback;
107
+ }
108
+
109
+ if (/^[0-9]/.test(name)) {
110
+ name = `_${name}`;
111
+ }
112
+
113
+ if (reservedWords.has(name)) {
114
+ name = `${name}_`;
115
+ }
116
+
117
+ return name;
118
+ }
119
+
120
+ export function propertyKey(value) {
121
+ const name = String(value ?? "");
122
+
123
+ if (/^[$A-Z_a-z][$\w]*$/.test(name) && !reservedWords.has(name)) {
124
+ return name;
125
+ }
126
+
127
+ return JSON.stringify(name);
128
+ }
129
+
130
+ export function refName(ref) {
131
+ return decodeURIComponent(String(ref).split("/").pop() ?? "");
132
+ }
133
+
134
+ export function uniqueName(baseName, usedNames) {
135
+ let name = baseName;
136
+ let counter = 2;
137
+
138
+ while (usedNames.has(name)) {
139
+ name = `${baseName}${counter}`;
140
+ counter += 1;
141
+ }
142
+
143
+ usedNames.add(name);
144
+ return name;
145
+ }