next-openapi-gen 0.8.8 → 0.8.9

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 CHANGED
@@ -784,6 +784,51 @@ Factory functions work with any naming convention and support:
784
784
  - Imported schemas
785
785
  - Multiple factory patterns in the same project
786
786
 
787
+ ### Schema Composition with `.extend()`
788
+
789
+ Zod's `.extend()` method allows you to build upon existing schemas:
790
+
791
+ ```typescript
792
+ // src/schemas/user.ts
793
+
794
+ // Base user schema
795
+ export const BaseUserSchema = z.object({
796
+ id: z.string().uuid().describe("User ID"),
797
+ email: z.string().email().describe("Email address"),
798
+ });
799
+
800
+ // Extend with additional fields
801
+ export const UserProfileSchema = BaseUserSchema.extend({
802
+ name: z.string().describe("Full name"),
803
+ bio: z.string().optional().describe("User biography"),
804
+ });
805
+
806
+ // Multiple levels of extension
807
+ export const AdminUserSchema = UserProfileSchema.extend({
808
+ role: z.enum(["admin", "moderator"]).describe("Admin role"),
809
+ permissions: z.array(z.string()).describe("Permission list"),
810
+ });
811
+
812
+ export const UserIdParams = z.object({
813
+ id: z.string().uuid().describe("User ID"),
814
+ });
815
+
816
+ // src/app/api/users/[id]/route.ts
817
+
818
+ /**
819
+ * Get user profile
820
+ * @pathParams UserIdParams
821
+ * @response UserProfileSchema
822
+ * @openapi
823
+ */
824
+ export async function GET(
825
+ request: NextRequest,
826
+ { params }: { params: { id: string } }
827
+ ) {
828
+ // Returns: { id, email, name, bio? }
829
+ }
830
+ ```
831
+
787
832
  ### Drizzle-Zod Support
788
833
 
789
834
  The library fully supports **drizzle-zod** for generating Zod schemas from Drizzle ORM table definitions. This provides a single source of truth for your database schema, validation, and API documentation.
@@ -400,6 +400,54 @@ export class ZodSchemaConverter {
400
400
  });
401
401
  }
402
402
  break;
403
+ case "extend":
404
+ // Extend the schema with new properties
405
+ if (node.arguments.length > 0 &&
406
+ t.isObjectExpression(node.arguments[0])) {
407
+ const extensionProperties = {};
408
+ const extensionRequired = [];
409
+ node.arguments[0].properties.forEach((prop) => {
410
+ if (t.isObjectProperty(prop)) {
411
+ const key = t.isIdentifier(prop.key)
412
+ ? prop.key.name
413
+ : t.isStringLiteral(prop.key)
414
+ ? prop.key.value
415
+ : null;
416
+ if (key) {
417
+ // Process the Zod type for this property
418
+ const propSchema = this.processZodNode(prop.value);
419
+ if (propSchema) {
420
+ extensionProperties[key] = propSchema;
421
+ // Check if the schema itself has nullable set (which processZodNode sets for optional fields)
422
+ const isOptional = propSchema.nullable === true;
423
+ if (!isOptional) {
424
+ extensionRequired.push(key);
425
+ }
426
+ }
427
+ }
428
+ }
429
+ });
430
+ // Merge with existing schema
431
+ if (schema.properties) {
432
+ schema.properties = {
433
+ ...schema.properties,
434
+ ...extensionProperties,
435
+ };
436
+ }
437
+ else {
438
+ schema.properties = extensionProperties;
439
+ }
440
+ // Merge required arrays
441
+ if (extensionRequired.length > 0) {
442
+ schema.required = [
443
+ ...(schema.required || []),
444
+ ...extensionRequired,
445
+ ];
446
+ // Deduplicate
447
+ schema.required = [...new Set(schema.required)];
448
+ }
449
+ }
450
+ break;
403
451
  }
404
452
  return schema;
405
453
  };
@@ -989,8 +1037,9 @@ export class ZodSchemaConverter {
989
1037
  properties,
990
1038
  };
991
1039
  if (required.length > 0) {
1040
+ // Deduplicate required array using Set
992
1041
  // @ts-ignore
993
- schema.required = required;
1042
+ schema.required = [...new Set(required)];
994
1043
  }
995
1044
  return schema;
996
1045
  }
@@ -1371,7 +1420,24 @@ export class ZodSchemaConverter {
1371
1420
  if (node.arguments.length > 0 &&
1372
1421
  t.isObjectExpression(node.arguments[0])) {
1373
1422
  // Get the base schema by processing the object that extend is called on
1374
- const baseSchema = this.processZodNode(node.callee.object);
1423
+ const baseSchemaResult = this.processZodNode(node.callee.object);
1424
+ // If it's a reference, resolve it to the actual schema
1425
+ let baseSchema = baseSchemaResult;
1426
+ if (baseSchemaResult && baseSchemaResult.$ref) {
1427
+ const schemaName = baseSchemaResult.$ref.replace("#/components/schemas/", "");
1428
+ // Try to convert the base schema if not already processed
1429
+ if (!this.zodSchemas[schemaName]) {
1430
+ logger.debug(`[extend] Base schema ${schemaName} not found, attempting to convert it`);
1431
+ this.convertZodSchemaToOpenApi(schemaName);
1432
+ }
1433
+ // Now retrieve the converted schema
1434
+ if (this.zodSchemas[schemaName]) {
1435
+ baseSchema = this.zodSchemas[schemaName];
1436
+ }
1437
+ else {
1438
+ logger.debug(`Could not resolve reference for extend: ${schemaName}`);
1439
+ }
1440
+ }
1375
1441
  // Process the extension object
1376
1442
  const extendNode = {
1377
1443
  type: "CallExpression",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-openapi-gen",
3
- "version": "0.8.8",
3
+ "version": "0.8.9",
4
4
  "description": "Automatically generate OpenAPI 3.0 documentation from Next.js projects, with support for Zod schemas and TypeScript types.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",