react-query-lightbase-codegen 2.3.0 → 2.3.2

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.
@@ -1,5 +1,4 @@
1
1
  import type { OpenAPIV3 } from "openapi-types";
2
- import type { OpenAPIConfig } from "../types/config";
3
2
  export interface OperationInfo {
4
3
  method: string;
5
4
  path: string;
@@ -10,4 +9,4 @@ export interface OperationInfo {
10
9
  requestBody?: OpenAPIV3.RequestBodyObject;
11
10
  responses: OpenAPIV3.ResponsesObject;
12
11
  }
13
- export declare function generateApiClient(spec: OpenAPIV3.Document, config: OpenAPIConfig): string;
12
+ export declare function generateApiClient(spec: OpenAPIV3.Document): string;
@@ -65,11 +65,11 @@ function generateAxiosMethod(operation, spec) {
65
65
  // Add path and query parameters
66
66
  urlParams.forEach((p) => {
67
67
  const safeName = (0, utils_1.sanitizePropertyName)(p.name);
68
- dataProps.push(`${safeName}: ${getTypeFromParam(p)}`);
68
+ dataProps.push(`${safeName}: ${(0, utils_1.getTypeFromSchema)(p.schema)}`);
69
69
  });
70
70
  queryParams.forEach((p) => {
71
71
  const safeName = (0, utils_1.sanitizePropertyName)(p.name);
72
- dataProps.push(`${safeName}${p.required ? "" : "?"}: ${getTypeFromParam(p)}`);
72
+ dataProps.push(`${safeName}${p.required ? "" : "?"}: ${(0, utils_1.getTypeFromSchema)(p.schema)}`);
73
73
  });
74
74
  // Add request body type if it exists
75
75
  const hasData = (parameters && parameters.length > 0) || operation.requestBody;
@@ -134,35 +134,15 @@ function generateAxiosMethod(operation, spec) {
134
134
  .filter(Boolean)
135
135
  .join("\n ");
136
136
  const requestParms = hasData
137
- ? `props: ${(0, utils_1.pascalCase)(operationId)}Params & { axiosConfig?: AxiosRequestConfig; }`
137
+ ? `props: T.${(0, utils_1.pascalCase)(operationId)}Params & { axiosConfig?: AxiosRequestConfig; }`
138
138
  : "props?: { axiosConfig?: AxiosRequestConfig }";
139
139
  return `
140
- ${hasData ? `export type ${(0, utils_1.pascalCase)(operationId)}Params = ${dataType};` : ""}
141
140
  ${jsDocLines.join("\n ")}
142
141
  export async function ${(0, utils_1.camelCase)(operationId)}(${requestParms}): Promise<${responseType}> {
143
142
  ${methodBody}
144
143
  }`;
145
144
  }
146
- function getTypeFromParam(param) {
147
- if ("schema" in param) {
148
- const schema = param.schema;
149
- switch (schema.type) {
150
- case "string":
151
- return "string";
152
- case "integer":
153
- case "number":
154
- return "number";
155
- case "boolean":
156
- return "boolean";
157
- case "array":
158
- return "Array<any>"; // You might want to make this more specific
159
- default:
160
- return "any";
161
- }
162
- }
163
- return "any";
164
- }
165
- function generateApiClient(spec, config) {
145
+ function generateApiClient(spec) {
166
146
  const operations = [];
167
147
  const resolveParameters = (parameters) => {
168
148
  return parameters.map((p) => {
@@ -2,62 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateTypeDefinitions = generateTypeDefinitions;
4
4
  const utils_1 = require("../utils");
5
- /**
6
- * Converts OpenAPI schema type to TypeScript type
7
- */
8
- function getTypeFromSchema(schema, context) {
9
- if (!schema)
10
- return "any";
11
- if ("$ref" in schema) {
12
- const refType = schema.$ref.split("/").pop();
13
- return (0, utils_1.sanitizeTypeName)(refType);
14
- }
15
- const nullable = schema.nullable ? " | null" : "";
16
- // Handle enum types properly
17
- if (schema.enum) {
18
- return schema.enum.map((e) => (typeof e === "string" ? `'${e}'` : e)).join(" | ") + nullable;
19
- }
20
- switch (schema.type) {
21
- case "string":
22
- if ("format" in schema && schema.format === "binary") {
23
- return `string | { name?: string; type?: string; uri: string }${nullable}`;
24
- }
25
- return `string${nullable}`;
26
- case "number":
27
- case "integer":
28
- return `number${nullable}`;
29
- case "boolean":
30
- return `boolean${nullable}`;
31
- case "array": {
32
- const itemType = getTypeFromSchema(schema.items, context);
33
- return `Array<${itemType}>${nullable}`;
34
- }
35
- case "object":
36
- if (schema.properties) {
37
- const properties = Object.entries(schema.properties)
38
- .map(([key, prop]) => {
39
- const isRequired = schema.required?.includes(key);
40
- const propertyType = getTypeFromSchema(prop, context);
41
- const safeName = (0, utils_1.sanitizePropertyName)(key);
42
- return ` ${safeName}${isRequired ? "" : "?"}: ${propertyType};`;
43
- })
44
- .join("\n");
45
- return `{${properties}\n}${nullable}`;
46
- }
47
- if (schema.additionalProperties) {
48
- const valueType = typeof schema.additionalProperties === "boolean"
49
- ? "any"
50
- : getTypeFromSchema(schema.additionalProperties, context);
51
- return `Record<string, ${valueType}>${nullable}`;
52
- }
53
- return `Record<string, any>${nullable}`;
54
- default:
55
- return `any${nullable}`;
56
- }
57
- }
58
- function generateTypeDefinition(name, schema, context) {
5
+ function generateTypeDefinition(name, schema) {
59
6
  const description = !("$ref" in schema) && schema.description ? `/**\n * ${schema.description}\n */\n` : "";
60
- const typeValue = getTypeFromSchema(schema, context);
7
+ const typeValue = (0, utils_1.getTypeFromSchema)(schema);
61
8
  // Use 'type' for primitives, unions, and simple types
62
9
  // Use 'interface' only for complex objects with properties
63
10
  const isInterface = !("$ref" in schema) && schema.type === "object" && schema.properties;
@@ -78,7 +25,7 @@ function generateTypeDefinitions(spec) {
78
25
  for (const [name, schema] of Object.entries(context.schemas)) {
79
26
  if (context.generatedTypes.has(name))
80
27
  continue;
81
- output += generateTypeDefinition(name, schema, context);
28
+ output += generateTypeDefinition(name, schema);
82
29
  context.generatedTypes.add(name);
83
30
  }
84
31
  // Generate request/response types
@@ -90,32 +37,67 @@ function generateTypeDefinitions(spec) {
90
37
  const operationObject = operation;
91
38
  if (!operationObject)
92
39
  continue;
93
- const operationId = `${(0, utils_1.sanitizeTypeName)(operationObject.operationId || `${path.replace(/\W+/g, "_")}`)}`;
40
+ const { operationId: badOperationId, requestBody, responses, parameters } = operationObject;
41
+ const operationId = `${(0, utils_1.sanitizeTypeName)(badOperationId || `${path.replace(/\W+/g, "_")}`)}`;
94
42
  // Generate request body type
95
- if (operationObject.requestBody) {
96
- const content = operationObject.requestBody.content;
43
+ if (requestBody) {
44
+ const content = requestBody.content;
97
45
  const jsonContent = content["application/ld+json"] ??
98
46
  content["application/json"] ??
99
47
  content["multipart/form-data"] ??
100
48
  content["application/octet-stream"];
101
49
  if (jsonContent?.schema) {
102
50
  const typeName = `${operationId}Request`;
103
- output += generateTypeDefinition(typeName, jsonContent.schema, context);
51
+ output += generateTypeDefinition(typeName, jsonContent.schema);
104
52
  }
105
53
  }
106
54
  // Generate response types
107
- if (operationObject.responses) {
108
- for (const [code, response] of Object.entries(operationObject.responses)) {
55
+ if (responses) {
56
+ for (const [code, response] of Object.entries(responses)) {
109
57
  const responseObj = response;
110
58
  const content = responseObj.content?.["application/ld+json"] ??
111
59
  responseObj.content?.["application/json"] ??
112
60
  responseObj.content?.["application/octet-stream"];
113
61
  if (content?.schema) {
114
62
  const typeName = `${operationId}Response${code}`;
115
- output += generateTypeDefinition(typeName, content.schema, context);
63
+ output += generateTypeDefinition(typeName, content.schema);
116
64
  }
117
65
  }
118
66
  }
67
+ // Build data type parts
68
+ const dataProps = [];
69
+ const urlParams = (parameters?.filter((p) => "in" in p && p.in === "path") ||
70
+ []);
71
+ const queryParams = (parameters?.filter((p) => "in" in p && p.in === "query") ||
72
+ []);
73
+ // Add path and query parameters
74
+ urlParams.forEach((p) => {
75
+ const safeName = (0, utils_1.sanitizePropertyName)(p.name);
76
+ dataProps.push(`${safeName}: ${(0, utils_1.getTypeFromSchema)(p.schema)}`);
77
+ });
78
+ queryParams.forEach((p) => {
79
+ const safeName = (0, utils_1.sanitizePropertyName)(p.name);
80
+ dataProps.push(`${safeName}${p.required ? "" : "?"}: ${(0, utils_1.getTypeFromSchema)(p.schema)}`);
81
+ });
82
+ // Add request body type if it exists
83
+ const hasData = (parameters && parameters.length > 0) || requestBody;
84
+ let dataType = "undefined";
85
+ const namedType = (0, utils_1.pascalCase)(operationId);
86
+ if (hasData) {
87
+ if (requestBody && dataProps.length > 0) {
88
+ dataType = `${namedType}Request & { ${dataProps.join("; ")} }`;
89
+ }
90
+ else if (requestBody) {
91
+ dataType = `${namedType}Request`;
92
+ }
93
+ else if (dataProps.length > 0) {
94
+ dataType = `{ ${dataProps.join("; ")} }`;
95
+ }
96
+ else {
97
+ dataType = "Record<string, never>";
98
+ }
99
+ output += `\n\nexport type ${(0, utils_1.pascalCase)(operationId)}Params = ${dataType};\n\n`;
100
+ }
119
101
  }
120
102
  }
121
103
  }
package/dist/index.js CHANGED
@@ -90,7 +90,7 @@ async function codegenerate(config) {
90
90
  const typeDefinitions = (0, schemaGenerator_1.generateTypeDefinitions)(spec);
91
91
  await (0, promises_1.writeFile)((0, node_path_1.resolve)(config.exportDir, `${title}.schema.ts`), typeDefinitions, "utf-8");
92
92
  // Generate and write API client
93
- const clientCode = (0, clientGenerator_1.generateApiClient)(spec, config);
93
+ const clientCode = (0, clientGenerator_1.generateApiClient)(spec);
94
94
  await (0, promises_1.writeFile)((0, node_path_1.resolve)(config.exportDir, `${title}.client.ts`), clientCode, "utf-8");
95
95
  // Generate and write React Query options
96
96
  const queryCode = (0, reactQueryGenerator_1.generateReactQuery)(spec);
package/dist/utils.d.ts CHANGED
@@ -33,3 +33,21 @@ export declare function sanitizePropertyName(name: string): string;
33
33
  */
34
34
  export declare function sanitizeTypeName(name: string): string;
35
35
  export declare function specTitle(spec: OpenAPIV3.Document): string;
36
+ /**
37
+ * Converts an OpenAPI schema object into a TypeScript type string.
38
+ *
39
+ * Handles:
40
+ * - References ($ref) by extracting the type name
41
+ * - Nullable types by appending "| null"
42
+ * - Enums by creating union types of the values
43
+ * - Basic types (string, number, boolean)
44
+ * - Binary format strings as a union with file metadata object
45
+ * - Arrays by recursively getting the item type
46
+ * - Objects with properties by creating interfaces
47
+ * - Objects with additionalProperties as Records
48
+ * - Fallback to "any" for unknown types
49
+ *
50
+ * @param param - The OpenAPI schema/parameter object to convert
51
+ * @returns The TypeScript type as a string
52
+ */
53
+ export declare function getTypeFromSchema(schema: OpenAPIV3.ParameterObject | OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined): string | undefined;
package/dist/utils.js CHANGED
@@ -5,6 +5,7 @@ exports.pascalCase = pascalCase;
5
5
  exports.sanitizePropertyName = sanitizePropertyName;
6
6
  exports.sanitizeTypeName = sanitizeTypeName;
7
7
  exports.specTitle = specTitle;
8
+ exports.getTypeFromSchema = getTypeFromSchema;
8
9
  function camelCase(str) {
9
10
  return str
10
11
  .replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase())
@@ -53,3 +54,87 @@ function sanitizeTypeName(name) {
53
54
  function specTitle(spec) {
54
55
  return camelCase(spec.info.title.toLowerCase().replace(/\s+/g, "-"));
55
56
  }
57
+ /**
58
+ * Converts an OpenAPI schema object into a TypeScript type string.
59
+ *
60
+ * Handles:
61
+ * - References ($ref) by extracting the type name
62
+ * - Nullable types by appending "| null"
63
+ * - Enums by creating union types of the values
64
+ * - Basic types (string, number, boolean)
65
+ * - Binary format strings as a union with file metadata object
66
+ * - Arrays by recursively getting the item type
67
+ * - Objects with properties by creating interfaces
68
+ * - Objects with additionalProperties as Records
69
+ * - Fallback to "any" for unknown types
70
+ *
71
+ * @param param - The OpenAPI schema/parameter object to convert
72
+ * @returns The TypeScript type as a string
73
+ */
74
+ function getTypeFromSchema(schema) {
75
+ if (!schema)
76
+ return undefined;
77
+ // Handle $ref by extracting the referenced type name
78
+ if ("$ref" in schema) {
79
+ const refType = schema.$ref.split("/").pop();
80
+ return sanitizeTypeName(refType);
81
+ }
82
+ // Add "| null" for nullable types
83
+ const nullable = "nullable" in schema && schema.nullable ? " | null" : "";
84
+ // Handle enums as union types
85
+ if ("enum" in schema && schema.enum) {
86
+ if (Object.values(schema.enum)?.length > 0) {
87
+ return (Object.values(schema.enum)
88
+ .map((e) => (typeof e === "string" ? `'${e}'` : e))
89
+ .join(" | ") + nullable);
90
+ }
91
+ return schema.enum.map((e) => (typeof e === "string" ? `'${e}'` : e)).join(" | ") + nullable;
92
+ }
93
+ // Handle types based on the "type" property
94
+ if ("type" in schema) {
95
+ switch (schema.type) {
96
+ case "string":
97
+ // Special case for binary format strings
98
+ if ("format" in schema && schema.format === "binary") {
99
+ return `string | { name?: string; type?: string; uri: string }${nullable}`;
100
+ }
101
+ return `string${nullable}`;
102
+ case "number":
103
+ case "integer":
104
+ return `number${nullable}`;
105
+ case "boolean":
106
+ return `boolean${nullable}`;
107
+ case "array": {
108
+ // Recursively get the array item type
109
+ const itemType = getTypeFromSchema(schema.items);
110
+ return `Array<${itemType}>${nullable}`;
111
+ }
112
+ case "object":
113
+ // Handle objects with defined properties
114
+ if (schema.properties) {
115
+ const properties = Object.entries(schema.properties)
116
+ .map(([key, prop]) => {
117
+ const isRequired = schema.required?.includes(key);
118
+ const propertyType = getTypeFromSchema(prop);
119
+ const safeName = sanitizePropertyName(key);
120
+ return ` ${safeName}${isRequired ? "" : "?"}: ${propertyType};`;
121
+ })
122
+ .join("\n");
123
+ return `{${properties}\n}${nullable}`;
124
+ }
125
+ // Handle objects with additionalProperties
126
+ if (schema.additionalProperties) {
127
+ const valueType = typeof schema.additionalProperties === "boolean"
128
+ ? "any"
129
+ : getTypeFromSchema(schema.additionalProperties);
130
+ return `Record<string, ${valueType}>${nullable}`;
131
+ }
132
+ // Default object type when no properties specified
133
+ return `Record<string, any>${nullable}`;
134
+ default:
135
+ return `any${nullable}`;
136
+ }
137
+ }
138
+ // Fallback for schemas without a type
139
+ return "any";
140
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-query-lightbase-codegen",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "license": "MIT",
5
5
  "description": "Generate Axios API clients and React Query options from OpenAPI specifications",
6
6
  "exports": "./dist/index.js",
@@ -1,6 +1,12 @@
1
1
  import type { OpenAPIV3 } from "openapi-types";
2
- import type { OpenAPIConfig } from "../types/config";
3
- import { camelCase, pascalCase, sanitizePropertyName, sanitizeTypeName, specTitle } from "../utils";
2
+ import {
3
+ camelCase,
4
+ getTypeFromSchema,
5
+ pascalCase,
6
+ sanitizePropertyName,
7
+ sanitizeTypeName,
8
+ specTitle,
9
+ } from "../utils";
4
10
 
5
11
  export interface OperationInfo {
6
12
  method: string;
@@ -93,11 +99,11 @@ function generateAxiosMethod(operation: OperationInfo, spec: OpenAPIV3.Document)
93
99
  // Add path and query parameters
94
100
  urlParams.forEach((p) => {
95
101
  const safeName = sanitizePropertyName(p.name);
96
- dataProps.push(`${safeName}: ${getTypeFromParam(p)}`);
102
+ dataProps.push(`${safeName}: ${getTypeFromSchema(p.schema)}`);
97
103
  });
98
104
  queryParams.forEach((p) => {
99
105
  const safeName = sanitizePropertyName(p.name);
100
- dataProps.push(`${safeName}${p.required ? "" : "?"}: ${getTypeFromParam(p)}`);
106
+ dataProps.push(`${safeName}${p.required ? "" : "?"}: ${getTypeFromSchema(p.schema)}`);
101
107
  });
102
108
 
103
109
  // Add request body type if it exists
@@ -167,38 +173,17 @@ function generateAxiosMethod(operation: OperationInfo, spec: OpenAPIV3.Document)
167
173
  .join("\n ");
168
174
 
169
175
  const requestParms = hasData
170
- ? `props: ${pascalCase(operationId)}Params & { axiosConfig?: AxiosRequestConfig; }`
176
+ ? `props: T.${pascalCase(operationId)}Params & { axiosConfig?: AxiosRequestConfig; }`
171
177
  : "props?: { axiosConfig?: AxiosRequestConfig }";
172
178
 
173
179
  return `
174
- ${hasData ? `export type ${pascalCase(operationId)}Params = ${dataType};` : ""}
175
180
  ${jsDocLines.join("\n ")}
176
181
  export async function ${camelCase(operationId)}(${requestParms}): Promise<${responseType}> {
177
182
  ${methodBody}
178
183
  }`;
179
184
  }
180
185
 
181
- function getTypeFromParam(param: OpenAPIV3.ParameterObject): string {
182
- if ("schema" in param) {
183
- const schema = param.schema as OpenAPIV3.SchemaObject;
184
- switch (schema.type) {
185
- case "string":
186
- return "string";
187
- case "integer":
188
- case "number":
189
- return "number";
190
- case "boolean":
191
- return "boolean";
192
- case "array":
193
- return "Array<any>"; // You might want to make this more specific
194
- default:
195
- return "any";
196
- }
197
- }
198
- return "any";
199
- }
200
-
201
- export function generateApiClient(spec: OpenAPIV3.Document, config: OpenAPIConfig): string {
186
+ export function generateApiClient(spec: OpenAPIV3.Document): string {
202
187
  const operations: OperationInfo[] = [];
203
188
 
204
189
  const resolveParameters = (
@@ -1,79 +1,17 @@
1
1
  import type { OpenAPIV3 } from "openapi-types";
2
- import { sanitizePropertyName, sanitizeTypeName } from "../utils";
2
+ import { getTypeFromSchema, pascalCase, sanitizePropertyName, sanitizeTypeName } from "../utils";
3
3
 
4
4
  interface SchemaContext {
5
5
  schemas: { [key: string]: OpenAPIV3.SchemaObject };
6
6
  generatedTypes: Set<string>;
7
7
  }
8
8
 
9
- /**
10
- * Converts OpenAPI schema type to TypeScript type
11
- */
12
- function getTypeFromSchema(
13
- schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject,
14
- context: SchemaContext
15
- ): string {
16
- if (!schema) return "any";
17
-
18
- if ("$ref" in schema) {
19
- const refType = schema.$ref.split("/").pop();
20
- return sanitizeTypeName(refType as string);
21
- }
22
- const nullable = schema.nullable ? " | null" : "";
23
-
24
- // Handle enum types properly
25
- if (schema.enum) {
26
- return schema.enum.map((e) => (typeof e === "string" ? `'${e}'` : e)).join(" | ") + nullable;
27
- }
28
-
29
- switch (schema.type) {
30
- case "string":
31
- if ("format" in schema && schema.format === "binary") {
32
- return `string | { name?: string; type?: string; uri: string }${nullable}`;
33
- }
34
-
35
- return `string${nullable}`;
36
- case "number":
37
- case "integer":
38
- return `number${nullable}`;
39
- case "boolean":
40
- return `boolean${nullable}`;
41
- case "array": {
42
- const itemType = getTypeFromSchema(schema.items, context);
43
- return `Array<${itemType}>${nullable}`;
44
- }
45
- case "object":
46
- if (schema.properties) {
47
- const properties = Object.entries(schema.properties)
48
- .map(([key, prop]) => {
49
- const isRequired = schema.required?.includes(key);
50
- const propertyType = getTypeFromSchema(prop, context);
51
- const safeName = sanitizePropertyName(key);
52
- return ` ${safeName}${isRequired ? "" : "?"}: ${propertyType};`;
53
- })
54
- .join("\n");
55
- return `{${properties}\n}${nullable}`;
56
- }
57
- if (schema.additionalProperties) {
58
- const valueType =
59
- typeof schema.additionalProperties === "boolean"
60
- ? "any"
61
- : getTypeFromSchema(schema.additionalProperties, context);
62
- return `Record<string, ${valueType}>${nullable}`;
63
- }
64
- return `Record<string, any>${nullable}`;
65
- default:
66
- return `any${nullable}`;
67
- }
68
- }
69
-
70
9
  function generateTypeDefinition(
71
10
  name: string,
72
- schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject,
73
- context: SchemaContext
11
+ schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject
74
12
  ): string {
75
13
  const description = !("$ref" in schema) && schema.description ? `/**\n * ${schema.description}\n */\n` : "";
76
- const typeValue = getTypeFromSchema(schema, context);
14
+ const typeValue = getTypeFromSchema(schema);
77
15
 
78
16
  // Use 'type' for primitives, unions, and simple types
79
17
  // Use 'interface' only for complex objects with properties
@@ -98,7 +36,7 @@ export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
98
36
  // Generate types for all schema definitions
99
37
  for (const [name, schema] of Object.entries(context.schemas)) {
100
38
  if (context.generatedTypes.has(name)) continue;
101
- output += generateTypeDefinition(name, schema, context);
39
+ output += generateTypeDefinition(name, schema);
102
40
  context.generatedTypes.add(name);
103
41
  }
104
42
 
@@ -110,11 +48,12 @@ export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
110
48
 
111
49
  const operationObject = operation as OpenAPIV3.OperationObject;
112
50
  if (!operationObject) continue;
113
- const operationId = `${sanitizeTypeName(operationObject.operationId || `${path.replace(/\W+/g, "_")}`)}`;
51
+ const { operationId: badOperationId, requestBody, responses, parameters } = operationObject;
52
+ const operationId = `${sanitizeTypeName(badOperationId || `${path.replace(/\W+/g, "_")}`)}`;
114
53
 
115
54
  // Generate request body type
116
- if (operationObject.requestBody) {
117
- const content = (operationObject.requestBody as OpenAPIV3.RequestBodyObject).content;
55
+ if (requestBody) {
56
+ const content = (requestBody as OpenAPIV3.RequestBodyObject).content;
118
57
  const jsonContent =
119
58
  content["application/ld+json"] ??
120
59
  content["application/json"] ??
@@ -122,13 +61,13 @@ export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
122
61
  content["application/octet-stream"];
123
62
  if (jsonContent?.schema) {
124
63
  const typeName = `${operationId}Request`;
125
- output += generateTypeDefinition(typeName, jsonContent.schema as OpenAPIV3.SchemaObject, context);
64
+ output += generateTypeDefinition(typeName, jsonContent.schema as OpenAPIV3.SchemaObject);
126
65
  }
127
66
  }
128
67
 
129
68
  // Generate response types
130
- if (operationObject.responses) {
131
- for (const [code, response] of Object.entries(operationObject.responses)) {
69
+ if (responses) {
70
+ for (const [code, response] of Object.entries(responses)) {
132
71
  const responseObj = response as OpenAPIV3.ResponseObject;
133
72
  const content =
134
73
  responseObj.content?.["application/ld+json"] ??
@@ -136,10 +75,46 @@ export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
136
75
  responseObj.content?.["application/octet-stream"];
137
76
  if (content?.schema) {
138
77
  const typeName = `${operationId}Response${code}`;
139
- output += generateTypeDefinition(typeName, content.schema as OpenAPIV3.SchemaObject, context);
78
+ output += generateTypeDefinition(typeName, content.schema as OpenAPIV3.SchemaObject);
140
79
  }
141
80
  }
142
81
  }
82
+
83
+ // Build data type parts
84
+ const dataProps: string[] = [];
85
+
86
+ const urlParams = (parameters?.filter((p) => "in" in p && p.in === "path") ||
87
+ []) as OpenAPIV3.ParameterObject[];
88
+ const queryParams = (parameters?.filter((p) => "in" in p && p.in === "query") ||
89
+ []) as OpenAPIV3.ParameterObject[];
90
+
91
+ // Add path and query parameters
92
+ urlParams.forEach((p) => {
93
+ const safeName = sanitizePropertyName(p.name);
94
+ dataProps.push(`${safeName}: ${getTypeFromSchema(p.schema)}`);
95
+ });
96
+ queryParams.forEach((p) => {
97
+ const safeName = sanitizePropertyName(p.name);
98
+ dataProps.push(`${safeName}${p.required ? "" : "?"}: ${getTypeFromSchema(p.schema)}`);
99
+ });
100
+
101
+ // Add request body type if it exists
102
+ const hasData = (parameters && parameters.length > 0) || requestBody;
103
+
104
+ let dataType = "undefined";
105
+ const namedType = pascalCase(operationId);
106
+ if (hasData) {
107
+ if (requestBody && dataProps.length > 0) {
108
+ dataType = `${namedType}Request & { ${dataProps.join("; ")} }`;
109
+ } else if (requestBody) {
110
+ dataType = `${namedType}Request`;
111
+ } else if (dataProps.length > 0) {
112
+ dataType = `{ ${dataProps.join("; ")} }`;
113
+ } else {
114
+ dataType = "Record<string, never>";
115
+ }
116
+ output += `\n\nexport type ${pascalCase(operationId)}Params = ${dataType};\n\n`;
117
+ }
143
118
  }
144
119
  }
145
120
  }
package/src/index.ts CHANGED
@@ -62,7 +62,7 @@ export async function codegenerate(config: OpenAPIConfig): Promise<void> {
62
62
  await writeFile(resolve(config.exportDir, `${title}.schema.ts`), typeDefinitions, "utf-8");
63
63
 
64
64
  // Generate and write API client
65
- const clientCode = generateApiClient(spec, config);
65
+ const clientCode = generateApiClient(spec);
66
66
  await writeFile(resolve(config.exportDir, `${title}.client.ts`), clientCode, "utf-8");
67
67
 
68
68
  // Generate and write React Query options
package/src/utils.ts CHANGED
@@ -52,3 +52,103 @@ export function sanitizeTypeName(name: string): string {
52
52
  export function specTitle(spec: OpenAPIV3.Document): string {
53
53
  return camelCase(spec.info.title.toLowerCase().replace(/\s+/g, "-"));
54
54
  }
55
+
56
+ /**
57
+ * Converts an OpenAPI schema object into a TypeScript type string.
58
+ *
59
+ * Handles:
60
+ * - References ($ref) by extracting the type name
61
+ * - Nullable types by appending "| null"
62
+ * - Enums by creating union types of the values
63
+ * - Basic types (string, number, boolean)
64
+ * - Binary format strings as a union with file metadata object
65
+ * - Arrays by recursively getting the item type
66
+ * - Objects with properties by creating interfaces
67
+ * - Objects with additionalProperties as Records
68
+ * - Fallback to "any" for unknown types
69
+ *
70
+ * @param param - The OpenAPI schema/parameter object to convert
71
+ * @returns The TypeScript type as a string
72
+ */
73
+ export function getTypeFromSchema(
74
+ schema: OpenAPIV3.ParameterObject | OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined
75
+ ): string | undefined {
76
+ if (!schema) return undefined;
77
+ // Handle $ref by extracting the referenced type name
78
+ if ("$ref" in schema) {
79
+ const refType = schema.$ref.split("/").pop();
80
+ return sanitizeTypeName(refType as string);
81
+ }
82
+
83
+ // Add "| null" for nullable types
84
+ const nullable = "nullable" in schema && schema.nullable ? " | null" : "";
85
+
86
+ // Handle enums as union types
87
+ if ("enum" in schema && schema.enum) {
88
+ if (Object.values(schema.enum)?.length > 0) {
89
+ return (
90
+ Object.values(schema.enum)
91
+ .map((e) => (typeof e === "string" ? `'${e}'` : e))
92
+ .join(" | ") + nullable
93
+ );
94
+ }
95
+ return schema.enum.map((e) => (typeof e === "string" ? `'${e}'` : e)).join(" | ") + nullable;
96
+ }
97
+
98
+ // Handle types based on the "type" property
99
+ if ("type" in schema) {
100
+ switch (schema.type) {
101
+ case "string":
102
+ // Special case for binary format strings
103
+ if ("format" in schema && schema.format === "binary") {
104
+ return `string | { name?: string; type?: string; uri: string }${nullable}`;
105
+ }
106
+ return `string${nullable}`;
107
+
108
+ case "number":
109
+ case "integer":
110
+ return `number${nullable}`;
111
+
112
+ case "boolean":
113
+ return `boolean${nullable}`;
114
+
115
+ case "array": {
116
+ // Recursively get the array item type
117
+ const itemType = getTypeFromSchema(schema.items);
118
+ return `Array<${itemType}>${nullable}`;
119
+ }
120
+
121
+ case "object":
122
+ // Handle objects with defined properties
123
+ if (schema.properties) {
124
+ const properties = Object.entries(schema.properties)
125
+ .map(([key, prop]) => {
126
+ const isRequired = schema.required?.includes(key);
127
+ const propertyType = getTypeFromSchema(prop);
128
+ const safeName = sanitizePropertyName(key);
129
+ return ` ${safeName}${isRequired ? "" : "?"}: ${propertyType};`;
130
+ })
131
+ .join("\n");
132
+ return `{${properties}\n}${nullable}`;
133
+ }
134
+
135
+ // Handle objects with additionalProperties
136
+ if (schema.additionalProperties) {
137
+ const valueType =
138
+ typeof schema.additionalProperties === "boolean"
139
+ ? "any"
140
+ : getTypeFromSchema(schema.additionalProperties);
141
+ return `Record<string, ${valueType}>${nullable}`;
142
+ }
143
+
144
+ // Default object type when no properties specified
145
+ return `Record<string, any>${nullable}`;
146
+
147
+ default:
148
+ return `any${nullable}`;
149
+ }
150
+ }
151
+
152
+ // Fallback for schemas without a type
153
+ return "any";
154
+ }