react-query-lightbase-codegen 3.1.6 → 3.1.7

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.
@@ -18,10 +18,10 @@ function formatParamProperty(param, forceRequired = false) {
18
18
  function generateTypeDefinition(name, schema) {
19
19
  const description = !("$ref" in schema) && schema.description ? `/**\n * ${schema.description}\n */\n` : "";
20
20
  const typeValue = (0, utils_1.getTypeFromSchema)(schema);
21
- // Use 'type' for primitives, unions, and simple types
22
- // Use 'interface' only for complex objects with properties
23
- const isInterface = !("$ref" in schema) && schema.type === "object" && schema.properties;
24
- return isInterface
21
+ // Only emit `interface` when the body is a plain object literal. Composition
22
+ // (allOf/oneOf/anyOf) produces references or intersections that must use a type alias.
23
+ const canBeInterface = typeValue?.trimStart().startsWith("{");
24
+ return canBeInterface
25
25
  ? `${description}export interface ${(0, utils_1.sanitizeTypeName)(name)} ${typeValue}\n\n`
26
26
  : `${description}export type ${(0, utils_1.sanitizeTypeName)(name)} = ${typeValue}\n\n`;
27
27
  }
package/dist/utils.js CHANGED
@@ -145,6 +145,36 @@ function specTitle(spec) {
145
145
  }
146
146
  return camelCase(title.toLowerCase().replace(/\s+/g, "-"));
147
147
  }
148
+ /**
149
+ * Builds the object-literal fragment ({...} or Record<...>) from a schema's own
150
+ * `properties` or `additionalProperties`, ignoring composition keywords. Returns
151
+ * undefined when the schema contributes no own object shape.
152
+ */
153
+ function getOwnObjectFragment(schema) {
154
+ if (schema.properties && Object.keys(schema.properties).length > 0) {
155
+ const properties = Object.entries(schema.properties)
156
+ .map(([key, prop]) => {
157
+ const isRequired = schema.required?.includes(key);
158
+ const propertyType = getTypeFromSchema(prop);
159
+ const safeName = sanitizePropertyName(key);
160
+ const isDeprecated = "deprecated" in prop && prop.deprecated;
161
+ const hasDescription = "description" in prop && prop.description;
162
+ const desc = hasDescription || isDeprecated
163
+ ? `/**${hasDescription ? `\n * ${prop.description}` : ""}${isDeprecated ? "\n * @deprecated" : ""}\n */\n`
164
+ : "";
165
+ return `${desc}${safeName}${isRequired ? "" : "?"}: ${propertyType};`;
166
+ })
167
+ .join("\n");
168
+ return `{${properties}\n}`;
169
+ }
170
+ if (schema.additionalProperties) {
171
+ const valueType = typeof schema.additionalProperties === "boolean"
172
+ ? "unknown"
173
+ : getTypeFromSchema(schema.additionalProperties);
174
+ return `Record<string, ${valueType}>`;
175
+ }
176
+ return undefined;
177
+ }
148
178
  /**
149
179
  * Converts an OpenAPI schema object into a TypeScript type string.
150
180
  *
@@ -174,29 +204,36 @@ function getTypeFromSchema(schema) {
174
204
  }
175
205
  // Add "| null" for nullable types
176
206
  const nullable = "nullable" in schema && schema.nullable ? " | null" : "";
207
+ // Sibling properties / additionalProperties may appear next to allOf/oneOf/anyOf.
208
+ // Per the OpenAPI spec they constrain the parent and must be intersected with the composition.
209
+ const ownObjectFragment = getOwnObjectFragment(schema);
177
210
  // Handle allOf (intersection types)
178
211
  if ("allOf" in schema && schema.allOf) {
179
- const types = schema.allOf.map((s) => getTypeFromSchema(s)).filter(Boolean);
180
- if (types.length === 0)
212
+ const parts = schema.allOf.map((s) => getTypeFromSchema(s)).filter(Boolean);
213
+ if (ownObjectFragment)
214
+ parts.push(ownObjectFragment);
215
+ if (parts.length === 0)
181
216
  return "unknown";
182
- const result = types.length === 1 ? types[0] : `(${types.join(" & ")})`;
217
+ const result = parts.length === 1 ? parts[0] : `(${parts.join(" & ")})`;
183
218
  return `${result}${nullable}`;
184
219
  }
185
220
  // Handle oneOf (union types - exactly one)
186
221
  if ("oneOf" in schema && schema.oneOf) {
187
222
  const types = schema.oneOf.map((s) => getTypeFromSchema(s)).filter(Boolean);
188
- if (types.length === 0)
223
+ if (types.length === 0 && !ownObjectFragment)
189
224
  return "unknown";
190
- const result = types.length === 1 ? types[0] : `(${types.join(" | ")})`;
191
- return `${result}${nullable}`;
225
+ const union = types.length === 1 ? types[0] : `(${types.join(" | ")})`;
226
+ const composed = ownObjectFragment ? `(${union} & ${ownObjectFragment})` : union;
227
+ return `${composed}${nullable}`;
192
228
  }
193
229
  // Handle anyOf (union types - one or more)
194
230
  if ("anyOf" in schema && schema.anyOf) {
195
231
  const types = schema.anyOf.map((s) => getTypeFromSchema(s)).filter(Boolean);
196
- if (types.length === 0)
232
+ if (types.length === 0 && !ownObjectFragment)
197
233
  return "unknown";
198
- const result = types.length === 1 ? types[0] : `(${types.join(" | ")})`;
199
- return `${result}${nullable}`;
234
+ const union = types.length === 1 ? types[0] : `(${types.join(" | ")})`;
235
+ const composed = ownObjectFragment ? `(${union} & ${ownObjectFragment})` : union;
236
+ return `${composed}${nullable}`;
200
237
  }
201
238
  // Handle enums as union types
202
239
  if ("enum" in schema && schema.enum) {
@@ -242,33 +279,13 @@ function getTypeFromSchema(schema) {
242
279
  const itemType = getTypeFromSchema(schema.items);
243
280
  return `Array<${itemType}>${nullable}`;
244
281
  }
245
- case "object":
246
- // Handle objects with defined properties
247
- if (schema.properties) {
248
- const properties = Object.entries(schema.properties)
249
- .map(([key, prop]) => {
250
- const isRequired = schema.required?.includes(key);
251
- const propertyType = getTypeFromSchema(prop);
252
- const safeName = sanitizePropertyName(key);
253
- const isDeprecated = "deprecated" in prop && prop.deprecated;
254
- const hasDescription = "description" in prop && prop.description;
255
- const desc = hasDescription || isDeprecated
256
- ? `/**${hasDescription ? `\n * ${prop.description}` : ""}${isDeprecated ? "\n * @deprecated" : ""}\n */\n`
257
- : "";
258
- return `${desc}${safeName}${isRequired ? "" : "?"}: ${propertyType};`;
259
- })
260
- .join("\n");
261
- return `{${properties}\n}${nullable}`;
262
- }
263
- // Handle objects with additionalProperties
264
- if (schema.additionalProperties) {
265
- const valueType = typeof schema.additionalProperties === "boolean"
266
- ? "unknown"
267
- : getTypeFromSchema(schema.additionalProperties);
268
- return `Record<string, ${valueType}>${nullable}`;
269
- }
282
+ case "object": {
283
+ const fragment = getOwnObjectFragment(schema);
284
+ if (fragment)
285
+ return `${fragment}${nullable}`;
270
286
  // Default object type when no properties specified
271
287
  return `Record<string, unknown>${nullable}`;
288
+ }
272
289
  default:
273
290
  return `unknown${nullable}`;
274
291
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-query-lightbase-codegen",
3
- "version": "3.1.6",
3
+ "version": "3.1.7",
4
4
  "license": "MIT",
5
5
  "description": "Generate Axios API clients and React Query options from OpenAPI specifications",
6
6
  "exports": "./dist/index.js",
@@ -31,11 +31,11 @@ function generateTypeDefinition(
31
31
  const description = !("$ref" in schema) && schema.description ? `/**\n * ${schema.description}\n */\n` : "";
32
32
  const typeValue = getTypeFromSchema(schema);
33
33
 
34
- // Use 'type' for primitives, unions, and simple types
35
- // Use 'interface' only for complex objects with properties
36
- const isInterface = !("$ref" in schema) && schema.type === "object" && schema.properties;
34
+ // Only emit `interface` when the body is a plain object literal. Composition
35
+ // (allOf/oneOf/anyOf) produces references or intersections that must use a type alias.
36
+ const canBeInterface = typeValue?.trimStart().startsWith("{");
37
37
 
38
- return isInterface
38
+ return canBeInterface
39
39
  ? `${description}export interface ${sanitizeTypeName(name)} ${typeValue}\n\n`
40
40
  : `${description}export type ${sanitizeTypeName(name)} = ${typeValue}\n\n`;
41
41
  }
package/src/utils.ts CHANGED
@@ -170,6 +170,41 @@ export function specTitle(spec: OpenAPIV3.Document): string {
170
170
  return camelCase(title.toLowerCase().replace(/\s+/g, "-"));
171
171
  }
172
172
 
173
+ /**
174
+ * Builds the object-literal fragment ({...} or Record<...>) from a schema's own
175
+ * `properties` or `additionalProperties`, ignoring composition keywords. Returns
176
+ * undefined when the schema contributes no own object shape.
177
+ */
178
+ function getOwnObjectFragment(schema: OpenAPIV3.SchemaObject): string | undefined {
179
+ if (schema.properties && Object.keys(schema.properties).length > 0) {
180
+ const properties = Object.entries(schema.properties)
181
+ .map(([key, prop]) => {
182
+ const isRequired = schema.required?.includes(key);
183
+ const propertyType = getTypeFromSchema(prop);
184
+ const safeName = sanitizePropertyName(key);
185
+ const isDeprecated = "deprecated" in prop && prop.deprecated;
186
+ const hasDescription = "description" in prop && prop.description;
187
+ const desc =
188
+ hasDescription || isDeprecated
189
+ ? `/**${hasDescription ? `\n * ${prop.description}` : ""}${isDeprecated ? "\n * @deprecated" : ""}\n */\n`
190
+ : "";
191
+ return `${desc}${safeName}${isRequired ? "" : "?"}: ${propertyType};`;
192
+ })
193
+ .join("\n");
194
+ return `{${properties}\n}`;
195
+ }
196
+
197
+ if (schema.additionalProperties) {
198
+ const valueType =
199
+ typeof schema.additionalProperties === "boolean"
200
+ ? "unknown"
201
+ : getTypeFromSchema(schema.additionalProperties);
202
+ return `Record<string, ${valueType}>`;
203
+ }
204
+
205
+ return undefined;
206
+ }
207
+
173
208
  /**
174
209
  * Converts an OpenAPI schema object into a TypeScript type string.
175
210
  *
@@ -202,28 +237,35 @@ export function getTypeFromSchema(
202
237
  // Add "| null" for nullable types
203
238
  const nullable = "nullable" in schema && schema.nullable ? " | null" : "";
204
239
 
240
+ // Sibling properties / additionalProperties may appear next to allOf/oneOf/anyOf.
241
+ // Per the OpenAPI spec they constrain the parent and must be intersected with the composition.
242
+ const ownObjectFragment = getOwnObjectFragment(schema as OpenAPIV3.SchemaObject);
243
+
205
244
  // Handle allOf (intersection types)
206
245
  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(" & ")})`;
246
+ const parts = schema.allOf.map((s) => getTypeFromSchema(s)).filter(Boolean) as string[];
247
+ if (ownObjectFragment) parts.push(ownObjectFragment);
248
+ if (parts.length === 0) return "unknown";
249
+ const result = parts.length === 1 ? parts[0] : `(${parts.join(" & ")})`;
210
250
  return `${result}${nullable}`;
211
251
  }
212
252
 
213
253
  // Handle oneOf (union types - exactly one)
214
254
  if ("oneOf" in schema && schema.oneOf) {
215
255
  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}`;
256
+ if (types.length === 0 && !ownObjectFragment) return "unknown";
257
+ const union = types.length === 1 ? types[0] : `(${types.join(" | ")})`;
258
+ const composed = ownObjectFragment ? `(${union} & ${ownObjectFragment})` : union;
259
+ return `${composed}${nullable}`;
219
260
  }
220
261
 
221
262
  // Handle anyOf (union types - one or more)
222
263
  if ("anyOf" in schema && schema.anyOf) {
223
264
  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}`;
265
+ if (types.length === 0 && !ownObjectFragment) return "unknown";
266
+ const union = types.length === 1 ? types[0] : `(${types.join(" | ")})`;
267
+ const composed = ownObjectFragment ? `(${union} & ${ownObjectFragment})` : union;
268
+ return `${composed}${nullable}`;
227
269
  }
228
270
 
229
271
  // Handle enums as union types
@@ -274,37 +316,13 @@ export function getTypeFromSchema(
274
316
  return `Array<${itemType}>${nullable}`;
275
317
  }
276
318
 
277
- case "object":
278
- // Handle objects with defined properties
279
- if (schema.properties) {
280
- const properties = Object.entries(schema.properties)
281
- .map(([key, prop]) => {
282
- const isRequired = schema.required?.includes(key);
283
- const propertyType = getTypeFromSchema(prop);
284
- const safeName = sanitizePropertyName(key);
285
- const isDeprecated = "deprecated" in prop && prop.deprecated;
286
- const hasDescription = "description" in prop && prop.description;
287
- const desc =
288
- hasDescription || isDeprecated
289
- ? `/**${hasDescription ? `\n * ${prop.description}` : ""}${isDeprecated ? "\n * @deprecated" : ""}\n */\n`
290
- : "";
291
- return `${desc}${safeName}${isRequired ? "" : "?"}: ${propertyType};`;
292
- })
293
- .join("\n");
294
- return `{${properties}\n}${nullable}`;
295
- }
296
-
297
- // Handle objects with additionalProperties
298
- if (schema.additionalProperties) {
299
- const valueType =
300
- typeof schema.additionalProperties === "boolean"
301
- ? "unknown"
302
- : getTypeFromSchema(schema.additionalProperties);
303
- return `Record<string, ${valueType}>${nullable}`;
304
- }
319
+ case "object": {
320
+ const fragment = getOwnObjectFragment(schema);
321
+ if (fragment) return `${fragment}${nullable}`;
305
322
 
306
323
  // Default object type when no properties specified
307
324
  return `Record<string, unknown>${nullable}`;
325
+ }
308
326
 
309
327
  default:
310
328
  return `unknown${nullable}`;