react-query-lightbase-codegen 2.5.11 → 3.1.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.
@@ -1,9 +1,27 @@
1
1
  import type { OpenAPIV3 } from "openapi-types";
2
- import { getTypeFromSchema, pascalCase, sanitizePropertyName, sanitizeTypeName } from "../utils";
2
+ import {
3
+ getContentSchema,
4
+ getTypeFromSchema,
5
+ pascalCase,
6
+ sanitizePropertyName,
7
+ sanitizeTypeName,
8
+ } from "../utils";
3
9
 
4
- interface SchemaContext {
5
- schemas: { [key: string]: OpenAPIV3.SchemaObject };
6
- generatedTypes: Set<string>;
10
+ /**
11
+ * Formats a parameter as a TypeScript property string with optional JSDoc.
12
+ */
13
+ function formatParamProperty(param: OpenAPIV3.ParameterObject, forceRequired = false): string {
14
+ const safeName = sanitizePropertyName(param.name);
15
+ const isDeprecated = param.deprecated;
16
+ const hasDescription = param.description;
17
+ const isOptional = forceRequired ? false : !param.required;
18
+
19
+ const desc =
20
+ hasDescription || isDeprecated
21
+ ? `/**${hasDescription ? `\n * ${param.description}` : ""}${isDeprecated ? "\n * @deprecated" : ""}\n */\n`
22
+ : "";
23
+
24
+ return `${desc}${safeName}${isOptional ? "?" : ""}: ${getTypeFromSchema(param.schema)}`;
7
25
  }
8
26
 
9
27
  function generateTypeDefinition(
@@ -26,18 +44,16 @@ function generateTypeDefinition(
26
44
  * Generates TypeScript interface definitions from OpenAPI schemas
27
45
  */
28
46
  export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
29
- const context: SchemaContext = {
30
- schemas: (spec.components?.schemas as { [key: string]: OpenAPIV3.SchemaObject }) || {},
31
- generatedTypes: new Set(),
32
- };
47
+ const schemas = (spec.components?.schemas as { [key: string]: OpenAPIV3.SchemaObject }) || {};
48
+ const generatedTypes = new Set<string>();
33
49
 
34
50
  let output = "/* Generated TypeScript Definitions */\n\n";
35
51
 
36
52
  // Generate types for all schema definitions
37
- for (const [name, schema] of Object.entries(context.schemas)) {
38
- if (context.generatedTypes.has(name)) continue;
53
+ for (const [name, schema] of Object.entries(schemas)) {
54
+ if (generatedTypes.has(name)) continue;
39
55
  output += generateTypeDefinition(name, schema);
40
- context.generatedTypes.add(name);
56
+ generatedTypes.add(name);
41
57
  }
42
58
 
43
59
  // Generate request/response types
@@ -54,34 +70,36 @@ export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
54
70
  // Generate request body type
55
71
  if (requestBody) {
56
72
  const content = (requestBody as OpenAPIV3.RequestBodyObject).content;
57
- const jsonContent =
58
- content["application/ld+json"] ??
59
- content["application/json"] ??
60
- content["multipart/form-data"] ??
61
- content["application/octet-stream"] ??
62
- content["application/json;charset=UTF-8"];
63
- if (jsonContent?.schema) {
73
+ const requestSchema = getContentSchema(content);
74
+ if (requestSchema) {
64
75
  const typeName = `${operationId}Request`;
65
- output += generateTypeDefinition(typeName, jsonContent.schema as OpenAPIV3.SchemaObject);
76
+ output += generateTypeDefinition(typeName, requestSchema as OpenAPIV3.SchemaObject);
66
77
  }
67
78
  }
68
79
 
69
80
  // Generate response types
81
+ const errorTypes: string[] = [];
70
82
  if (responses) {
71
83
  for (const [code, response] of Object.entries(responses)) {
72
84
  const responseObj = response as OpenAPIV3.ResponseObject;
73
- const content =
74
- responseObj.content?.["application/ld+json"] ??
75
- responseObj.content?.["application/json"] ??
76
- responseObj.content?.["application/octet-stream"] ??
77
- responseObj.content?.["application/json;charset=UTF-8"];
78
- if (content?.schema) {
85
+ const responseSchema = getContentSchema(responseObj.content);
86
+ if (responseSchema) {
79
87
  const typeName = `${operationId}Response${code}`;
80
- output += generateTypeDefinition(typeName, content.schema as OpenAPIV3.SchemaObject);
88
+ output += generateTypeDefinition(typeName, responseSchema as OpenAPIV3.SchemaObject);
89
+
90
+ // Track non-2xx responses for error union type
91
+ if (!code.startsWith("2")) {
92
+ errorTypes.push(typeName);
93
+ }
81
94
  }
82
95
  }
83
96
  }
84
97
 
98
+ // Generate error union type if there are error responses
99
+ if (errorTypes.length > 0) {
100
+ output += `export type ${pascalCase(operationId)}Error = ${errorTypes.join(" | ")};\n\n`;
101
+ }
102
+
85
103
  // Build data type parts
86
104
  const dataProps: string[] = [];
87
105
 
@@ -91,43 +109,14 @@ export function generateTypeDefinitions(spec: OpenAPIV3.Document): string {
91
109
  []) as OpenAPIV3.ParameterObject[];
92
110
  const headerParams = (parameters?.filter((p) => "in" in p && p.in === "header") ||
93
111
  []) as OpenAPIV3.ParameterObject[];
112
+ const cookieParams = (parameters?.filter((p) => "in" in p && p.in === "cookie") ||
113
+ []) as OpenAPIV3.ParameterObject[];
94
114
 
95
- // Add path and query parameters
96
- urlParams.forEach((p) => {
97
- const safeName = sanitizePropertyName(p.name);
98
- const isDeprecated = "deprecated" in p && p.deprecated;
99
- const hasDescription = "description" in p && p.description;
100
- const desc =
101
- hasDescription || isDeprecated
102
- ? `/**${hasDescription ? `\n* ${p.description}` : ""}${isDeprecated ? "\n* @deprecated" : ""}
103
- */\n`
104
- : "";
105
- dataProps.push(`${desc}${safeName}: ${getTypeFromSchema(p.schema)}`);
106
- });
107
-
108
- queryParams.forEach((p) => {
109
- const safeName = sanitizePropertyName(p.name);
110
- const isDeprecated = "deprecated" in p && p.deprecated;
111
- const hasDescription = "description" in p && p.description;
112
- const desc =
113
- hasDescription || isDeprecated
114
- ? `\n/**${hasDescription ? `\n* ${p.description}` : ""}${isDeprecated ? "\n* @deprecated" : ""}
115
- */\n`
116
- : "";
117
- dataProps.push(`${desc}${safeName}${p.required ? "" : "?"}: ${getTypeFromSchema(p.schema)}`);
118
- });
119
-
120
- headerParams.forEach((p) => {
121
- const safeName = sanitizePropertyName(p.name);
122
- const isDeprecated = "deprecated" in p && p.deprecated;
123
- const hasDescription = "description" in p && p.description;
124
- const desc =
125
- hasDescription || isDeprecated
126
- ? `\n/**${hasDescription ? `\n* ${p.description}` : ""}${isDeprecated ? "\n* @deprecated" : ""}
127
- */\n`
128
- : "";
129
- dataProps.push(`${desc}${safeName}${p.required ? "" : "?"}: ${getTypeFromSchema(p.schema)}`);
130
- });
115
+ // Add path, query, header, and cookie parameters
116
+ urlParams.forEach((p) => dataProps.push(formatParamProperty(p, true))); // Path params always required
117
+ queryParams.forEach((p) => dataProps.push(formatParamProperty(p)));
118
+ headerParams.forEach((p) => dataProps.push(formatParamProperty(p)));
119
+ cookieParams.forEach((p) => dataProps.push(formatParamProperty(p)));
131
120
 
132
121
  // Add request body type if it exists
133
122
  const hasData = (parameters && parameters.length > 0) || requestBody;
package/src/utils.ts CHANGED
@@ -1,5 +1,118 @@
1
1
  import type { OpenAPIV3 } from "openapi-types";
2
2
 
3
+ /**
4
+ * Supported content types in order of preference
5
+ */
6
+ export const CONTENT_TYPES = [
7
+ "application/ld+json",
8
+ "application/json",
9
+ "text/plain",
10
+ "multipart/form-data",
11
+ "application/octet-stream",
12
+ "application/json;charset=UTF-8",
13
+ ] as const;
14
+
15
+ /**
16
+ * Resolves a schema reference to its actual schema object.
17
+ * If the schema is already a concrete schema (not a $ref), returns it as-is.
18
+ */
19
+ export function resolveSchema(
20
+ schema: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject | undefined,
21
+ spec: OpenAPIV3.Document
22
+ ): OpenAPIV3.SchemaObject | undefined {
23
+ if (!schema) return undefined;
24
+ if ("$ref" in schema) {
25
+ const index = schema.$ref.split("/").pop();
26
+ return spec.components?.schemas?.[index as string] as OpenAPIV3.SchemaObject;
27
+ }
28
+ return schema;
29
+ }
30
+
31
+ /**
32
+ * Extracts the schema from a content object, checking supported content types in order.
33
+ */
34
+ export function getContentSchema(
35
+ content: { [media: string]: OpenAPIV3.MediaTypeObject } | undefined
36
+ ): OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined {
37
+ if (!content) return undefined;
38
+ for (const type of CONTENT_TYPES) {
39
+ if (content[type]?.schema) {
40
+ return content[type].schema;
41
+ }
42
+ }
43
+ return undefined;
44
+ }
45
+
46
+ /**
47
+ * Operation info extracted from OpenAPI paths
48
+ */
49
+ export interface OperationInfo {
50
+ method: string;
51
+ path: string;
52
+ operationId: string;
53
+ summary?: string;
54
+ description?: string;
55
+ deprecated?: boolean;
56
+ parameters: OpenAPIV3.ParameterObject[];
57
+ requestBody?: OpenAPIV3.RequestBodyObject;
58
+ responses: OpenAPIV3.ResponsesObject;
59
+ }
60
+
61
+ const HTTP_METHODS = ["get", "post", "put", "delete", "patch"] as const;
62
+
63
+ /**
64
+ * Collects all operations from an OpenAPI spec, resolving parameter and request body references.
65
+ */
66
+ export function collectOperations(spec: OpenAPIV3.Document): OperationInfo[] {
67
+ const operations: OperationInfo[] = [];
68
+
69
+ const resolveParameters = (
70
+ parameters: (OpenAPIV3.ParameterObject | OpenAPIV3.ReferenceObject)[]
71
+ ): OpenAPIV3.ParameterObject[] => {
72
+ return parameters.map((p) => {
73
+ if ("$ref" in p) {
74
+ const index = p.$ref.split("/").pop();
75
+ return spec.components?.parameters?.[index as string] as OpenAPIV3.ParameterObject;
76
+ }
77
+ return p;
78
+ });
79
+ };
80
+
81
+ const resolveRequestBody = (
82
+ requestBody: OpenAPIV3.RequestBodyObject | OpenAPIV3.ReferenceObject | undefined
83
+ ): OpenAPIV3.RequestBodyObject | undefined => {
84
+ if (!requestBody) return undefined;
85
+ if ("$ref" in requestBody) {
86
+ const index = requestBody.$ref.split("/").pop();
87
+ return spec.components?.requestBodies?.[index as string] as OpenAPIV3.RequestBodyObject;
88
+ }
89
+ return requestBody;
90
+ };
91
+
92
+ Object.entries(spec.paths || {}).forEach(([path, pathItem]) => {
93
+ if (!pathItem) return;
94
+
95
+ HTTP_METHODS.forEach((method) => {
96
+ const operation = pathItem[method] as OpenAPIV3.OperationObject | undefined;
97
+ if (!operation) return;
98
+
99
+ operations.push({
100
+ method,
101
+ path,
102
+ operationId: sanitizeTypeName(operation.operationId || path.replace(/\W+/g, "_")),
103
+ summary: operation.summary,
104
+ description: operation.description,
105
+ deprecated: operation.deprecated,
106
+ parameters: resolveParameters([...(pathItem.parameters || []), ...(operation.parameters || [])]),
107
+ requestBody: resolveRequestBody(operation.requestBody),
108
+ responses: operation.responses,
109
+ });
110
+ });
111
+ });
112
+
113
+ return operations;
114
+ }
115
+
3
116
  export function camelCase(str: string): string {
4
117
  return str
5
118
  .replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase())
@@ -63,6 +176,7 @@ export function specTitle(spec: OpenAPIV3.Document): string {
63
176
  * Handles:
64
177
  * - References ($ref) by extracting the type name
65
178
  * - Nullable types by appending "| null"
179
+ * - Composition types (allOf → intersection, oneOf/anyOf → union)
66
180
  * - Enums by creating union types of the values
67
181
  * - OneOf schemas as union types
68
182
  * - Basic types (string, number, boolean)
@@ -72,7 +186,7 @@ export function specTitle(spec: OpenAPIV3.Document): string {
72
186
  * - Objects with additionalProperties as Records
73
187
  * - Fallback to "any" for unknown types
74
188
  *
75
- * @param param - The OpenAPI schema/parameter object to convert
189
+ * @param schema - The OpenAPI schema/parameter object to convert
76
190
  * @returns The TypeScript type as a string
77
191
  */
78
192
  export function getTypeFromSchema(
@@ -88,6 +202,30 @@ export function getTypeFromSchema(
88
202
  // Add "| null" for nullable types
89
203
  const nullable = "nullable" in schema && schema.nullable ? " | null" : "";
90
204
 
205
+ // Handle allOf (intersection types)
206
+ if ("allOf" in schema && schema.allOf) {
207
+ const types = schema.allOf.map((s) => getTypeFromSchema(s)).filter(Boolean) as string[];
208
+ if (types.length === 0) return "unknown";
209
+ const result = types.length === 1 ? types[0] : `(${types.join(" & ")})`;
210
+ return `${result}${nullable}`;
211
+ }
212
+
213
+ // Handle oneOf (union types - exactly one)
214
+ if ("oneOf" in schema && schema.oneOf) {
215
+ const types = schema.oneOf.map((s) => getTypeFromSchema(s)).filter(Boolean) as string[];
216
+ if (types.length === 0) return "unknown";
217
+ const result = types.length === 1 ? types[0] : `(${types.join(" | ")})`;
218
+ return `${result}${nullable}`;
219
+ }
220
+
221
+ // Handle anyOf (union types - one or more)
222
+ if ("anyOf" in schema && schema.anyOf) {
223
+ const types = schema.anyOf.map((s) => getTypeFromSchema(s)).filter(Boolean) as string[];
224
+ if (types.length === 0) return "unknown";
225
+ const result = types.length === 1 ? types[0] : `(${types.join(" | ")})`;
226
+ return `${result}${nullable}`;
227
+ }
228
+
91
229
  // Handle enums as union types
92
230
  if ("enum" in schema && schema.enum) {
93
231
  if (Object.values(schema.enum)?.length > 0) {
@@ -100,14 +238,6 @@ export function getTypeFromSchema(
100
238
  return schema.enum.map((e) => (typeof e === "string" ? `'${e}'` : e)).join(" | ") + nullable;
101
239
  }
102
240
 
103
- // Handle oneOf as union types
104
- if ("oneOf" in schema && schema.oneOf) {
105
- const unionTypes = schema.oneOf
106
- .map((subSchema) => getTypeFromSchema(subSchema))
107
- .filter((type): type is string => type !== undefined);
108
- return unionTypes.length > 0 ? `${unionTypes.join(" | ")}${nullable}` : `any${nullable}`;
109
- }
110
-
111
241
  // Handle types based on the "type" property
112
242
  if ("type" in schema) {
113
243
  switch (schema.type) {
@@ -143,8 +273,7 @@ export function getTypeFromSchema(
143
273
  const hasDescription = "description" in prop && prop.description;
144
274
  const desc =
145
275
  hasDescription || isDeprecated
146
- ? `/**${hasDescription ? `\n* ${prop.description}` : ""}${isDeprecated ? "\n* @deprecated" : ""}
147
- */\n`
276
+ ? `/**${hasDescription ? `\n * ${prop.description}` : ""}${isDeprecated ? "\n * @deprecated" : ""}\n */\n`
148
277
  : "";
149
278
  return `${desc}${safeName}${isRequired ? "" : "?"}: ${propertyType};`;
150
279
  })
@@ -156,19 +285,19 @@ export function getTypeFromSchema(
156
285
  if (schema.additionalProperties) {
157
286
  const valueType =
158
287
  typeof schema.additionalProperties === "boolean"
159
- ? "any"
288
+ ? "unknown"
160
289
  : getTypeFromSchema(schema.additionalProperties);
161
290
  return `Record<string, ${valueType}>${nullable}`;
162
291
  }
163
292
 
164
293
  // Default object type when no properties specified
165
- return `Record<string, any>${nullable}`;
294
+ return `Record<string, unknown>${nullable}`;
166
295
 
167
296
  default:
168
- return `any${nullable}`;
297
+ return `unknown${nullable}`;
169
298
  }
170
299
  }
171
300
 
172
301
  // Fallback for schemas without a type
173
- return "any";
302
+ return "unknown";
174
303
  }