next-openapi-gen 0.6.1 → 0.6.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.
- package/README.md +29 -0
- package/dist/lib/route-processor.js +1 -1
- package/dist/lib/schema-processor.js +50 -6
- package/dist/lib/utils.js +9 -0
- package/dist/lib/zod-converter.js +16 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -154,6 +154,7 @@ export async function GET(
|
|
|
154
154
|
| `@bodyDescription` | Request body description |
|
|
155
155
|
| `@response` | Response type/schema |
|
|
156
156
|
| `@responseDescription` | Response description |
|
|
157
|
+
| `@contentType` | Request body content type (`application/json`, `multipart/form-data`) |
|
|
157
158
|
| `@auth` | Authorization type (`bearer`, `basic`, `apikey`) |
|
|
158
159
|
| `@tag` | Custom tag |
|
|
159
160
|
| `@deprecated` | Marks the route as deprecated |
|
|
@@ -344,6 +345,34 @@ export async function GET() {
|
|
|
344
345
|
}
|
|
345
346
|
```
|
|
346
347
|
|
|
348
|
+
### File Uploads / Multipart Form Data
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
// src/app/api/upload/route.ts
|
|
352
|
+
|
|
353
|
+
// TypeScript
|
|
354
|
+
type FileUploadFormData = {
|
|
355
|
+
file: File;
|
|
356
|
+
description?: string;
|
|
357
|
+
category: string;
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// Or Zod
|
|
361
|
+
const FileUploadSchema = z.object({
|
|
362
|
+
file: z.custom<File>().describe("Image file (PNG/JPG)"),
|
|
363
|
+
description: z.string().optional().describe("File description"),
|
|
364
|
+
category: z.string().describe("File category"),
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* @body FileUploadSchema
|
|
369
|
+
* @contentType multipart/form-data
|
|
370
|
+
*/
|
|
371
|
+
export async function POST() {
|
|
372
|
+
// ...
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
347
376
|
## Advanced Usage
|
|
348
377
|
|
|
349
378
|
### Automatic Path Parameter Detection
|
|
@@ -160,7 +160,7 @@ export class RouteProcessor {
|
|
|
160
160
|
}
|
|
161
161
|
// Add request body
|
|
162
162
|
if (MUTATION_HTTP_METHODS.includes(method.toUpperCase())) {
|
|
163
|
-
definition.requestBody = this.schemaProcessor.createRequestBodySchema(body, bodyDescription);
|
|
163
|
+
definition.requestBody = this.schemaProcessor.createRequestBodySchema(body, bodyDescription, dataTypes.contentType);
|
|
164
164
|
}
|
|
165
165
|
// Add responses
|
|
166
166
|
definition.responses = responses
|
|
@@ -501,6 +501,44 @@ export class SchemaProcessor {
|
|
|
501
501
|
return "example";
|
|
502
502
|
}
|
|
503
503
|
}
|
|
504
|
+
detectContentType(bodyType, explicitContentType) {
|
|
505
|
+
if (explicitContentType) {
|
|
506
|
+
return explicitContentType;
|
|
507
|
+
}
|
|
508
|
+
// Automatic detection based on type name
|
|
509
|
+
if (bodyType &&
|
|
510
|
+
(bodyType.toLowerCase().includes("formdata") ||
|
|
511
|
+
bodyType.toLowerCase().includes("fileupload") ||
|
|
512
|
+
bodyType.toLowerCase().includes("multipart"))) {
|
|
513
|
+
return "multipart/form-data";
|
|
514
|
+
}
|
|
515
|
+
return "application/json";
|
|
516
|
+
}
|
|
517
|
+
createFormDataSchema(body) {
|
|
518
|
+
if (!body.properties) {
|
|
519
|
+
return body;
|
|
520
|
+
}
|
|
521
|
+
const formDataProperties = {};
|
|
522
|
+
Object.entries(body.properties).forEach(([key, value]) => {
|
|
523
|
+
// Convert File types to binary format
|
|
524
|
+
if (value.type === "object" &&
|
|
525
|
+
(key.toLowerCase().includes("file") ||
|
|
526
|
+
value.description?.toLowerCase().includes("file"))) {
|
|
527
|
+
formDataProperties[key] = {
|
|
528
|
+
type: "string",
|
|
529
|
+
format: "binary",
|
|
530
|
+
description: value.description,
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
formDataProperties[key] = value;
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
return {
|
|
538
|
+
...body,
|
|
539
|
+
properties: formDataProperties,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
504
542
|
/**
|
|
505
543
|
* Create a default schema for path parameters when no schema is defined
|
|
506
544
|
*/
|
|
@@ -558,18 +596,24 @@ export class SchemaProcessor {
|
|
|
558
596
|
}
|
|
559
597
|
return queryParams;
|
|
560
598
|
}
|
|
561
|
-
createRequestBodySchema(body, description) {
|
|
562
|
-
const
|
|
599
|
+
createRequestBodySchema(body, description, contentType) {
|
|
600
|
+
const detectedContentType = this.detectContentType(body?.type || "", contentType);
|
|
601
|
+
let schema = body;
|
|
602
|
+
// If it is multipart/form-data, convert schema
|
|
603
|
+
if (detectedContentType === "multipart/form-data") {
|
|
604
|
+
schema = this.createFormDataSchema(body);
|
|
605
|
+
}
|
|
606
|
+
const requestBody = {
|
|
563
607
|
content: {
|
|
564
|
-
|
|
565
|
-
schema:
|
|
608
|
+
[detectedContentType]: {
|
|
609
|
+
schema: schema,
|
|
566
610
|
},
|
|
567
611
|
},
|
|
568
612
|
};
|
|
569
613
|
if (description) {
|
|
570
|
-
|
|
614
|
+
requestBody.description = description;
|
|
571
615
|
}
|
|
572
|
-
return
|
|
616
|
+
return requestBody;
|
|
573
617
|
}
|
|
574
618
|
createResponseSchema(responses, description) {
|
|
575
619
|
return {
|
package/dist/lib/utils.js
CHANGED
|
@@ -28,6 +28,7 @@ export function extractJSDocComments(path) {
|
|
|
28
28
|
let deprecated = false;
|
|
29
29
|
let bodyDescription = "";
|
|
30
30
|
let responseDescription = "";
|
|
31
|
+
let contentType = "";
|
|
31
32
|
if (comments) {
|
|
32
33
|
comments.forEach((comment) => {
|
|
33
34
|
const commentValue = cleanComment(comment.value);
|
|
@@ -90,6 +91,13 @@ export function extractJSDocComments(path) {
|
|
|
90
91
|
if (commentValue.includes("@response")) {
|
|
91
92
|
responseType = extractTypeFromComment(commentValue, "@response");
|
|
92
93
|
}
|
|
94
|
+
if (commentValue.includes("@contentType")) {
|
|
95
|
+
const regex = /@contentType\s*(.*)/;
|
|
96
|
+
const match = commentValue.match(regex);
|
|
97
|
+
if (match && match[1]) {
|
|
98
|
+
contentType = match[1].trim();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
93
101
|
});
|
|
94
102
|
}
|
|
95
103
|
return {
|
|
@@ -105,6 +113,7 @@ export function extractJSDocComments(path) {
|
|
|
105
113
|
deprecated,
|
|
106
114
|
bodyDescription,
|
|
107
115
|
responseDescription,
|
|
116
|
+
contentType,
|
|
108
117
|
};
|
|
109
118
|
}
|
|
110
119
|
export function extractTypeFromComment(commentValue, tag) {
|
|
@@ -846,6 +846,22 @@ export class ZodSchemaConverter {
|
|
|
846
846
|
!t.isIdentifier(node.callee.property)) {
|
|
847
847
|
return { type: "string" };
|
|
848
848
|
}
|
|
849
|
+
if (t.isMemberExpression(node.callee) &&
|
|
850
|
+
t.isIdentifier(node.callee.property)) {
|
|
851
|
+
const zodType = node.callee.property.name;
|
|
852
|
+
// Custom() support for FormData
|
|
853
|
+
if (zodType === "custom" && node.arguments.length > 0) {
|
|
854
|
+
// Check if it is FormData
|
|
855
|
+
if (t.isArrowFunctionExpression(node.arguments[0])) {
|
|
856
|
+
// Assume custom FormData validation
|
|
857
|
+
return {
|
|
858
|
+
type: "object",
|
|
859
|
+
additionalProperties: true,
|
|
860
|
+
description: "Form data object",
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
849
865
|
const zodType = node.callee.property.name;
|
|
850
866
|
let schema = {};
|
|
851
867
|
// Basic type mapping
|
package/package.json
CHANGED