@moccona/apicodegen 0.0.8 → 0.0.10

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/npm/index.mjs CHANGED
@@ -238,7 +238,9 @@ var Base = class Base {
238
238
  */
239
239
  static camelCase(text) {
240
240
  text = text.trim();
241
- return text.split("_").filter(Boolean).map((t, index) => index === 0 ? t : Base.capitalize(t)).join("");
241
+ const parts = text.split("_").filter(Boolean);
242
+ while (parts[0]?.match(/^\d/)) parts.shift();
243
+ return parts.map((t, index) => index === 0 ? t : Base.capitalize(t)).join("");
242
244
  }
243
245
  /**
244
246
  * Converts a string to UpperCamelCase.
@@ -413,6 +415,250 @@ var Provider = class {
413
415
  }
414
416
  };
415
417
  //#endregion
418
+ //#region src/core/errors.ts
419
+ /**
420
+ * Error handling utilities for api-codegen
421
+ */
422
+ const ErrorCodes = {
423
+ SPEC_NOT_FOUND: "E_SPEC_NOT_FOUND",
424
+ SPEC_FETCH_FAILED: "E_SPEC_FETCH_FAILED",
425
+ SPEC_PARSE_FAILED: "E_SPEC_PARSE_FAILED",
426
+ OUTPUT_DIR_MISSING: "E_OUTPUT_DIR_MISSING",
427
+ CONFIG_INVALID: "E_CONFIG_INVALID",
428
+ VALIDATION_FAILED: "E_VALIDATION_FAILED",
429
+ GENERATION_FAILED: "E_GENERATION_FAILED",
430
+ TYPE_CHECK_FAILED: "E_TYPE_CHECK_FAILED"
431
+ };
432
+ /**
433
+ * Custom error class for api-codegen with rich context
434
+ */
435
+ var ApicodegenError = class ApicodegenError extends Error {
436
+ code;
437
+ location;
438
+ line;
439
+ column;
440
+ path;
441
+ suggestions;
442
+ cause;
443
+ constructor(context) {
444
+ super(context.message);
445
+ this.name = "ApicodegenError";
446
+ this.code = context.code;
447
+ this.location = context.location;
448
+ this.line = context.line;
449
+ this.column = context.column;
450
+ this.path = context.path;
451
+ this.suggestions = context.suggestions || [];
452
+ this.cause = context.cause;
453
+ if (Error.captureStackTrace) Error.captureStackTrace(this, ApicodegenError);
454
+ }
455
+ /**
456
+ * Convert error to formatted string for CLI output
457
+ */
458
+ toString(verbose = false) {
459
+ const lines = [];
460
+ lines.push(`\x1b[1;31mError [${this.code}]\x1b[0m ${this.message}`);
461
+ if (this.location) lines.push(` \x1b[36m→ Location:\x1b[0m ${this.location}`);
462
+ if (this.path) lines.push(` \x1b[36m→ Path:\x1b[0m ${this.path}`);
463
+ if (this.line !== void 0) {
464
+ let lineInfo = ` \x1b[36m→ Line:\x1b[0m ${this.line}`;
465
+ if (this.column !== void 0) lineInfo += `, Column: ${this.column}`;
466
+ lines.push(lineInfo);
467
+ }
468
+ if (this.suggestions.length > 0) for (const suggestion of this.suggestions) lines.push(` \x1b[32m→ Suggestion:\x1b[0m ${suggestion}`);
469
+ if (verbose && this.cause) {
470
+ lines.push(`\n \x1b[90mOriginal Error:\x1b[0m ${this.cause.message}`);
471
+ if (this.stack) {
472
+ const stackLines = this.stack.split("\n").slice(1).join("\n");
473
+ lines.push(`\x1b[90m${stackLines}\x1b[0m`);
474
+ }
475
+ }
476
+ return lines.join("\n");
477
+ }
478
+ /**
479
+ * Convert to JSON-serializable object
480
+ */
481
+ toJSON() {
482
+ return {
483
+ name: this.name,
484
+ code: this.code,
485
+ message: this.message,
486
+ location: this.location,
487
+ line: this.line,
488
+ column: this.column,
489
+ path: this.path,
490
+ suggestions: this.suggestions,
491
+ cause: this.cause?.message
492
+ };
493
+ }
494
+ };
495
+ /**
496
+ * ANSI color codes for terminal output
497
+ */
498
+ const Colors = {
499
+ reset: "\x1B[0m",
500
+ bold: "\x1B[1m",
501
+ red: "\x1B[31m",
502
+ green: "\x1B[32m",
503
+ yellow: "\x1B[33m",
504
+ blue: "\x1B[34m",
505
+ cyan: "\x1B[36m",
506
+ gray: "\x1B[90m",
507
+ brightRed: "\x1B[91m",
508
+ brightGreen: "\x1B[92m"
509
+ };
510
+ /**
511
+ * Format error for CLI output
512
+ */
513
+ function formatError(error, verbose = false) {
514
+ if (error instanceof ApicodegenError) return error.toString(verbose);
515
+ if (error instanceof Error) return `${Colors.red}${Colors.bold}Error${Colors.reset}: ${error.message}${verbose && error.stack ? `\n\n${Colors.gray}${error.stack}${Colors.reset}` : ""}`;
516
+ return `${Colors.red}${Colors.bold}Error${Colors.reset}: ${String(error)}`;
517
+ }
518
+ /**
519
+ * Print error to console with formatting
520
+ */
521
+ function printError(error, verbose = false, stream = process.stderr) {
522
+ stream.write(formatError(error, verbose));
523
+ stream.write("\n");
524
+ }
525
+ /**
526
+ * Create error with common patterns
527
+ */
528
+ const createErrors = {
529
+ specNotFound(path, cause) {
530
+ return new ApicodegenError({
531
+ code: ErrorCodes.SPEC_NOT_FOUND,
532
+ message: "OpenAPI spec file not found",
533
+ location: path,
534
+ suggestions: [
535
+ "Check if the file exists using 'ls -la'",
536
+ "Use --spec to provide the correct path",
537
+ "For remote specs, ensure the URL is accessible"
538
+ ],
539
+ cause
540
+ });
541
+ },
542
+ specFetchFailed(url, statusCode, cause) {
543
+ const message = statusCode ? `Failed to fetch OpenAPI spec (HTTP ${statusCode})` : "Failed to fetch OpenAPI spec from URL";
544
+ return new ApicodegenError({
545
+ code: ErrorCodes.SPEC_FETCH_FAILED,
546
+ message,
547
+ location: url,
548
+ suggestions: [
549
+ "Check if the URL is accessible in a browser",
550
+ "Download the spec file locally and use the local path",
551
+ "Verify CORS settings if fetching from a different origin"
552
+ ],
553
+ cause
554
+ });
555
+ },
556
+ specParseFailed(path, line, column, cause) {
557
+ return new ApicodegenError({
558
+ code: ErrorCodes.SPEC_PARSE_FAILED,
559
+ message: "Failed to parse OpenAPI spec (invalid JSON or YAML)",
560
+ location: path,
561
+ line,
562
+ column,
563
+ suggestions: [
564
+ "Validate JSON syntax using jsonlint.com",
565
+ "For YAML specs, ensure proper indentation",
566
+ "Check for trailing commas or unquoted special characters"
567
+ ],
568
+ cause
569
+ });
570
+ },
571
+ outputDirMissing(path, cause) {
572
+ return new ApicodegenError({
573
+ code: ErrorCodes.OUTPUT_DIR_MISSING,
574
+ message: "Output directory does not exist",
575
+ location: path,
576
+ suggestions: ["Create the directory: mkdir -p $(dirname <output>)", "Check if the path is correct"],
577
+ cause
578
+ });
579
+ },
580
+ configInvalid(path, cause) {
581
+ return new ApicodegenError({
582
+ code: ErrorCodes.CONFIG_INVALID,
583
+ message: "Invalid configuration file",
584
+ location: path,
585
+ suggestions: ["Validate JSON syntax in the config file", "Check for required fields (spec, output)"],
586
+ cause
587
+ });
588
+ },
589
+ validationFailed(path, details, cause) {
590
+ return new ApicodegenError({
591
+ code: ErrorCodes.VALIDATION_FAILED,
592
+ message: "OpenAPI spec validation failed",
593
+ location: path,
594
+ path: details,
595
+ suggestions: [
596
+ "Check OpenAPI spec structure at the specified path",
597
+ "Ensure all required fields are present",
598
+ "Validate using swagger.io editor"
599
+ ],
600
+ cause
601
+ });
602
+ },
603
+ generationFailed(cause) {
604
+ return new ApicodegenError({
605
+ code: ErrorCodes.GENERATION_FAILED,
606
+ message: "Code generation failed",
607
+ suggestions: [
608
+ "Check for unsupported OpenAPI features",
609
+ "Ensure spec follows OpenAPI 2.0, 3.0, or 3.1 specification",
610
+ "Use --verbose for more details"
611
+ ],
612
+ cause
613
+ });
614
+ },
615
+ typeCheckFailed(path, _errors, cause) {
616
+ return new ApicodegenError({
617
+ code: ErrorCodes.TYPE_CHECK_FAILED,
618
+ message: "TypeScript type check failed",
619
+ location: path,
620
+ suggestions: [
621
+ "Review type errors above",
622
+ "Check for schema inconsistencies",
623
+ "Update generated types or fix source schema"
624
+ ],
625
+ cause
626
+ });
627
+ },
628
+ missingRequiredField(field, context) {
629
+ return new ApicodegenError({
630
+ code: ErrorCodes.VALIDATION_FAILED,
631
+ message: `Missing required field: ${field}`,
632
+ path: context,
633
+ suggestions: [`Add the '${field}' field to your configuration`]
634
+ });
635
+ }
636
+ };
637
+ /**
638
+ * Wrap unknown error in ApicodegenError if needed
639
+ */
640
+ function wrapError(error, context) {
641
+ if (error instanceof ApicodegenError) return error;
642
+ if (error instanceof Error) return new ApicodegenError({
643
+ code: context?.code || ErrorCodes.GENERATION_FAILED,
644
+ message: context?.message || error.message,
645
+ location: context?.location,
646
+ suggestions: context?.suggestions,
647
+ cause: error
648
+ });
649
+ return new ApicodegenError({
650
+ code: context?.code || ErrorCodes.GENERATION_FAILED,
651
+ message: String(error),
652
+ suggestions: context?.suggestions
653
+ });
654
+ }
655
+ /**
656
+ * Check if error is an ApicodegenError
657
+ */
658
+ function isApicodegenError(error) {
659
+ return error instanceof ApicodegenError;
660
+ }
661
+ //#endregion
416
662
  //#region src/core/generator/index.ts
417
663
  var Generator = class Generator {
418
664
  /**
@@ -428,10 +674,19 @@ var Generator = class Generator {
428
674
  return createPrinter().printFile(sourceFile);
429
675
  }
430
676
  static async write(code, filepath) {
677
+ const { mkdir } = await import("node:fs/promises");
678
+ const { dirname } = await import("node:path");
431
679
  try {
680
+ await mkdir(dirname(filepath), { recursive: true });
432
681
  await writeFile(filepath, code);
433
682
  } catch (error) {
434
- console.error(error);
683
+ throw new ApicodegenError({
684
+ code: ErrorCodes.OUTPUT_DIR_MISSING,
685
+ message: "Failed to write generated code to output file",
686
+ location: filepath,
687
+ cause: error instanceof Error ? error : new Error(String(error)),
688
+ suggestions: ["Verify the output directory path is writable", "Check that the parent directory exists or can be created"]
689
+ });
435
690
  }
436
691
  }
437
692
  /**
@@ -467,9 +722,12 @@ var Generator = class Generator {
467
722
  static addComments(node, comments) {
468
723
  if (!Array.isArray(comments) || comments.filter(Boolean).length === 0) return;
469
724
  const formatComment = (comment) => {
470
- return comment.tag ? ` @${comment.tag} ${comment.comment ?? ""}` : ` ${comment.comment}`;
725
+ if (comment.tag === "returns") return `* @returns {${comment.type}} ${comment.comment ?? ""}`;
726
+ if (comment.tag === "param") return comment.comment ? `* @param ${comment.paramName} - ${comment.comment}` : `* @param ${comment.paramName}`;
727
+ if (comment.tag) return `* @${comment.tag} ${comment.comment ?? ""}`;
728
+ return `* ${comment.comment}`;
471
729
  };
472
- const formattedComments = "*\n" + comments.map(formatComment).join("\n").trim() + "\n";
730
+ const formattedComments = comments.map(formatComment).join("\n").trim() + "\n";
473
731
  addSyntheticLeadingComment(node, SyntaxKind.MultiLineCommentTrivia, formattedComments, true);
474
732
  }
475
733
  /**
@@ -486,6 +744,53 @@ var Generator = class Generator {
486
744
  const nonArraySchema = schema;
487
745
  return nonArraySchema.format === "blob" || nonArraySchema.format === "binary" || nonArraySchema.type === "file";
488
746
  }
747
+ static schemaToTypeString(schema) {
748
+ if (schema.type === "array") {
749
+ const arraySchema = schema;
750
+ return arraySchema.items ? `${Generator.schemaToTypeString(arraySchema.items)}[]` : "unknown";
751
+ }
752
+ const singleSchema = schema;
753
+ if (schema.type === "string") return "string";
754
+ if (schema.type === "number" || schema.type === "integer") return "number";
755
+ if (schema.type === "boolean") return "boolean";
756
+ if (schema.type === "object" || schema.properties) return "object";
757
+ if (singleSchema.format === "binary" || singleSchema.type === "file") return "Blob";
758
+ if (singleSchema.format === "blob") return "Blob";
759
+ if (singleSchema.ref) return singleSchema.ref;
760
+ return "unknown";
761
+ }
762
+ static generateParamTags(parameters, requestBody) {
763
+ const tags = [];
764
+ for (const p of parameters) {
765
+ const paramName = Base.camelCase(Base.normalize(p.name));
766
+ let paramType = "unknown";
767
+ if (p.schema) paramType = Generator.schemaToTypeString(p.schema);
768
+ const isOptional = p.required === false;
769
+ tags.push({
770
+ tag: "param",
771
+ paramName,
772
+ type: `${paramType}${isOptional ? " | undefined" : ""}`,
773
+ comment: p.description ?? ""
774
+ });
775
+ }
776
+ if (requestBody?.schema && "properties" in requestBody.schema) {
777
+ const properties = requestBody.schema.properties;
778
+ const required = requestBody.schema.required;
779
+ const requiredArray = Array.isArray(required) ? required : [];
780
+ for (const [key, schema] of Object.entries(properties ?? {})) {
781
+ const paramName = `req.${key}`;
782
+ const paramType = Generator.schemaToTypeString(schema);
783
+ const isOptional = !requiredArray.includes(key);
784
+ tags.push({
785
+ tag: "param",
786
+ paramName,
787
+ type: `${paramType}${isOptional ? " | undefined" : ""}`,
788
+ comment: schema.description ?? ""
789
+ });
790
+ }
791
+ }
792
+ return tags;
793
+ }
489
794
  static toRequestBodyTypeNode(schema) {
490
795
  return factory.createParameterDeclaration(void 0, void 0, factory.createIdentifier("req"), void 0, Generator.toTypeNode(schema));
491
796
  }
@@ -564,8 +869,8 @@ var Generator = class Generator {
564
869
  factory.createIdentifier("file"),
565
870
  factory.createPropertyAccessExpression(factory.createAsExpression(factory.createIdentifier("file"), factory.createTypeReferenceNode(factory.createIdentifier("File"), void 0)), factory.createIdentifier("name"))
566
871
  ]))])));
567
- else if (schemaByKey.required) statements.push(factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("fd"), factory.createIdentifier("append")), void 0, [factory.createStringLiteral(key), schemaByKey.type === "string" ? factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key)) : factory.createCallExpression(factory.createIdentifier("String"), void 0, [factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key))])])));
568
- else statements.push(factory.createExpressionStatement(factory.createBinaryExpression(factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key)), factory.createToken(SyntaxKind.AmpersandAmpersandToken), factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("fd"), factory.createIdentifier("append")), void 0, [factory.createStringLiteral(key), schemaByKey.type === "string" ? factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key)) : factory.createCallExpression(factory.createIdentifier("String"), void 0, [factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key))])]))));
872
+ else if (schemaByKey.required) statements.push(factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("fd"), factory.createIdentifier("append")), void 0, [factory.createStringLiteral(key), schemaByKey.type === "string" || Generator.isBinarySchema(schemaByKey) || schemaByKey.isRef ? factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key)) : schemaByKey.type === "array" || schemaByKey.type === "object" ? factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("JSON"), factory.createIdentifier("stringify")), void 0, [factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key))]) : factory.createCallExpression(factory.createIdentifier("String"), void 0, [factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key))])])));
873
+ else statements.push(factory.createExpressionStatement(factory.createBinaryExpression(factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key)), factory.createToken(SyntaxKind.AmpersandAmpersandToken), factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("fd"), factory.createIdentifier("append")), void 0, [factory.createStringLiteral(key), schemaByKey.type === "string" || Generator.isBinarySchema(schemaByKey) || schemaByKey.isRef ? factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key)) : schemaByKey.type === "array" || schemaByKey.type === "object" ? factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("JSON"), factory.createIdentifier("stringify")), void 0, [factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key))]) : factory.createCallExpression(factory.createIdentifier("String"), void 0, [factory.createElementAccessExpression(factory.createIdentifier("req"), factory.createStringLiteral(key))])]))));
569
874
  });
570
875
  return statements;
571
876
  }
@@ -577,7 +882,7 @@ var Generator = class Generator {
577
882
  const parametersShouldNotPutInFormData = parameters.filter((p) => !parametersShouldPutInFormData.includes(p));
578
883
  const isRequestBodyContainsBinary = requestBody?.schema && "properties" in requestBody.schema && Object.values(requestBody.schema?.properties ?? {}).some((p) => Generator.isBinarySchema(p));
579
884
  const hasBinaryInParameters = parameters.some((p) => p?.schema && Generator.isBinarySchema(p.schema));
580
- const shouldPutParametersOrBodyInFormData = isFormDataRequest || isRequestBodyBinary || hasBinaryInParameters || isRequestBodyContainsBinary || parametersShouldPutInFormData.length > 0;
885
+ const shouldPutParametersOrBodyInFormData = !!isFormDataRequest && (isRequestBodyBinary || hasBinaryInParameters || isRequestBodyContainsBinary || parametersShouldPutInFormData.length > 0);
581
886
  return factory.createBlock([...shouldPutParametersOrBodyInFormData ? Generator.toFormDataStatement(parametersShouldPutInFormData, requestBody?.schema) : [], ...adapter.client(uri, method, parametersShouldNotPutInFormData, requestBody, response, adapter, shouldPutParametersOrBodyInFormData, shouldParseResponseToJSON)]);
582
887
  }
583
888
  static schemaToStatemets(parsedDoc, adaptor, options) {
@@ -604,10 +909,11 @@ var Generator = class Generator {
604
909
  const shouldAddExtraMethodNameSuffix = requestBody.length > 1;
605
910
  for (const req of requestBody) {
606
911
  const statement = factory.createFunctionDeclaration([factory.createModifier(SyntaxKind.ExportKeyword), factory.createModifier(SyntaxKind.AsyncKeyword)], void 0, Base.pathToFnName(uri, method, operationId) + (shouldAddExtraMethodNameSuffix ? Base.capitalize(req.type.split("/")[1]) : ""), void 0, [...parameters.length > 0 ? Generator.toDeclarationNodes(parameters) : [], ...req?.schema ? [Generator.toRequestBodyTypeNode(req.schema)] : []].filter(Boolean), void 0, Generator.bodyBlock(options.baseURL + uri, method, parameters, req, responses[0], adaptor));
912
+ const mergedDescription = [description, summary].filter(Boolean).join(". ");
607
913
  Generator.addComments(statement, [
608
- description && { comment: description },
609
- summary && { comment: summary },
610
- deprecated && { tag: "deprecated" }
914
+ mergedDescription && { comment: mergedDescription },
915
+ deprecated && { tag: "deprecated" },
916
+ ...Generator.generateParamTags(parameters, req)
611
917
  ].filter(Boolean));
612
918
  statements.push(statement);
613
919
  }
@@ -860,20 +1166,6 @@ async function loadConfig(options = {}) {
860
1166
  };
861
1167
  }
862
1168
  /**
863
- * Create CLI options from config for commander
864
- */
865
- function configToCLIOptions(config) {
866
- const options = {};
867
- if (config.spec) options.spec = config.spec;
868
- if (config.output) options.output = config.output;
869
- if (config.adaptor) options.adaptor = config.adaptor;
870
- if (config.baseURL) options.baseURL = config.baseURL;
871
- if (config.verbose) options.verbose = config.verbose;
872
- if (config.watch) options.watch = config.watch;
873
- if (config.importClientSource) options.importClientSource = config.importClientSource;
874
- return options;
875
- }
876
- /**
877
1169
  * Convert resolved config to provider options format
878
1170
  */
879
1171
  function toProviderOptions(config) {
@@ -888,509 +1180,74 @@ function toProviderOptions(config) {
888
1180
  };
889
1181
  }
890
1182
  //#endregion
891
- //#region src/core/errors.ts
892
- /**
893
- * Error handling utilities for api-codegen
894
- */
895
- const ErrorCodes = {
896
- SPEC_NOT_FOUND: "E_SPEC_NOT_FOUND",
897
- SPEC_FETCH_FAILED: "E_SPEC_FETCH_FAILED",
898
- SPEC_PARSE_FAILED: "E_SPEC_PARSE_FAILED",
899
- OUTPUT_DIR_MISSING: "E_OUTPUT_DIR_MISSING",
900
- CONFIG_INVALID: "E_CONFIG_INVALID",
901
- VALIDATION_FAILED: "E_VALIDATION_FAILED",
902
- GENERATION_FAILED: "E_GENERATION_FAILED",
903
- TYPE_CHECK_FAILED: "E_TYPE_CHECK_FAILED"
904
- };
905
- /**
906
- * Custom error class for api-codegen with rich context
907
- */
908
- var ApicodegenError = class ApicodegenError extends Error {
909
- code;
910
- location;
911
- line;
912
- column;
913
- path;
914
- suggestions;
915
- cause;
916
- constructor(context) {
917
- super(context.message);
918
- this.name = "ApicodegenError";
919
- this.code = context.code;
920
- this.location = context.location;
921
- this.line = context.line;
922
- this.column = context.column;
923
- this.path = context.path;
924
- this.suggestions = context.suggestions || [];
925
- this.cause = context.cause;
926
- if (Error.captureStackTrace) Error.captureStackTrace(this, ApicodegenError);
927
- }
928
- /**
929
- * Convert error to formatted string for CLI output
930
- */
931
- toString(verbose = false) {
932
- const lines = [];
933
- lines.push(`\x1b[1;31mError [${this.code}]\x1b[0m ${this.message}`);
934
- if (this.location) lines.push(` \x1b[36m→ Location:\x1b[0m ${this.location}`);
935
- if (this.path) lines.push(` \x1b[36m→ Path:\x1b[0m ${this.path}`);
936
- if (this.line !== void 0) {
937
- let lineInfo = ` \x1b[36m→ Line:\x1b[0m ${this.line}`;
938
- if (this.column !== void 0) lineInfo += `, Column: ${this.column}`;
939
- lines.push(lineInfo);
940
- }
941
- if (this.suggestions.length > 0) for (const suggestion of this.suggestions) lines.push(` \x1b[32m→ Suggestion:\x1b[0m ${suggestion}`);
942
- if (verbose && this.cause) {
943
- lines.push(`\n \x1b[90mOriginal Error:\x1b[0m ${this.cause.message}`);
944
- if (this.stack) {
945
- const stackLines = this.stack.split("\n").slice(1).join("\n");
946
- lines.push(`\x1b[90m${stackLines}\x1b[0m`);
947
- }
948
- }
949
- return lines.join("\n");
950
- }
951
- /**
952
- * Convert to JSON-serializable object
953
- */
954
- toJSON() {
955
- return {
956
- name: this.name,
957
- code: this.code,
958
- message: this.message,
959
- location: this.location,
960
- line: this.line,
961
- column: this.column,
962
- path: this.path,
963
- suggestions: this.suggestions,
964
- cause: this.cause?.message
965
- };
966
- }
967
- };
968
- /**
969
- * ANSI color codes for terminal output
970
- */
971
- const Colors = {
972
- reset: "\x1B[0m",
973
- bold: "\x1B[1m",
974
- red: "\x1B[31m",
975
- green: "\x1B[32m",
976
- yellow: "\x1B[33m",
977
- blue: "\x1B[34m",
978
- cyan: "\x1B[36m",
979
- gray: "\x1B[90m",
980
- brightRed: "\x1B[91m",
981
- brightGreen: "\x1B[92m"
982
- };
983
- /**
984
- * Format error for CLI output
985
- */
986
- function formatError(error, verbose = false) {
987
- if (error instanceof ApicodegenError) return error.toString(verbose);
988
- if (error instanceof Error) return `${Colors.red}${Colors.bold}Error${Colors.reset}: ${error.message}${verbose && error.stack ? `\n\n${Colors.gray}${error.stack}${Colors.reset}` : ""}`;
989
- return `${Colors.red}${Colors.bold}Error${Colors.reset}: ${String(error)}`;
990
- }
991
- /**
992
- * Print error to console with formatting
993
- */
994
- function printError(error, verbose = false, stream = process.stderr) {
995
- stream.write(formatError(error, verbose));
996
- stream.write("\n");
997
- }
998
- /**
999
- * Create error with common patterns
1000
- */
1001
- const createErrors = {
1002
- specNotFound(path, cause) {
1003
- return new ApicodegenError({
1004
- code: ErrorCodes.SPEC_NOT_FOUND,
1005
- message: "OpenAPI spec file not found",
1006
- location: path,
1007
- suggestions: [
1008
- "Check if the file exists using 'ls -la'",
1009
- "Use --spec to provide the correct path",
1010
- "For remote specs, ensure the URL is accessible"
1011
- ],
1012
- cause
1013
- });
1014
- },
1015
- specFetchFailed(url, statusCode, cause) {
1016
- const message = statusCode ? `Failed to fetch OpenAPI spec (HTTP ${statusCode})` : "Failed to fetch OpenAPI spec from URL";
1017
- return new ApicodegenError({
1018
- code: ErrorCodes.SPEC_FETCH_FAILED,
1019
- message,
1020
- location: url,
1021
- suggestions: [
1022
- "Check if the URL is accessible in a browser",
1023
- "Download the spec file locally and use the local path",
1024
- "Verify CORS settings if fetching from a different origin"
1025
- ],
1026
- cause
1027
- });
1028
- },
1029
- specParseFailed(path, line, column, cause) {
1030
- return new ApicodegenError({
1031
- code: ErrorCodes.SPEC_PARSE_FAILED,
1032
- message: "Failed to parse OpenAPI spec (invalid JSON or YAML)",
1033
- location: path,
1034
- line,
1035
- column,
1036
- suggestions: [
1037
- "Validate JSON syntax using jsonlint.com",
1038
- "For YAML specs, ensure proper indentation",
1039
- "Check for trailing commas or unquoted special characters"
1040
- ],
1041
- cause
1042
- });
1043
- },
1044
- outputDirMissing(path, cause) {
1045
- return new ApicodegenError({
1046
- code: ErrorCodes.OUTPUT_DIR_MISSING,
1047
- message: "Output directory does not exist",
1048
- location: path,
1049
- suggestions: ["Create the directory: mkdir -p $(dirname <output>)", "Check if the path is correct"],
1050
- cause
1051
- });
1052
- },
1053
- configInvalid(path, cause) {
1054
- return new ApicodegenError({
1055
- code: ErrorCodes.CONFIG_INVALID,
1056
- message: "Invalid configuration file",
1057
- location: path,
1058
- suggestions: ["Validate JSON syntax in the config file", "Check for required fields (spec, output)"],
1059
- cause
1060
- });
1061
- },
1062
- validationFailed(path, details, cause) {
1063
- return new ApicodegenError({
1064
- code: ErrorCodes.VALIDATION_FAILED,
1065
- message: "OpenAPI spec validation failed",
1066
- location: path,
1067
- path: details,
1068
- suggestions: [
1069
- "Check OpenAPI spec structure at the specified path",
1070
- "Ensure all required fields are present",
1071
- "Validate using swagger.io editor"
1072
- ],
1073
- cause
1074
- });
1075
- },
1076
- generationFailed(cause) {
1077
- return new ApicodegenError({
1078
- code: ErrorCodes.GENERATION_FAILED,
1079
- message: "Code generation failed",
1080
- suggestions: [
1081
- "Check for unsupported OpenAPI features",
1082
- "Ensure spec follows OpenAPI 2.0, 3.0, or 3.1 specification",
1083
- "Use --verbose for more details"
1084
- ],
1085
- cause
1086
- });
1087
- },
1088
- typeCheckFailed(path, _errors, cause) {
1089
- return new ApicodegenError({
1090
- code: ErrorCodes.TYPE_CHECK_FAILED,
1091
- message: "TypeScript type check failed",
1092
- location: path,
1093
- suggestions: [
1094
- "Review type errors above",
1095
- "Check for schema inconsistencies",
1096
- "Update generated types or fix source schema"
1097
- ],
1098
- cause
1099
- });
1100
- },
1101
- missingRequiredField(field, context) {
1102
- return new ApicodegenError({
1103
- code: ErrorCodes.VALIDATION_FAILED,
1104
- message: `Missing required field: ${field}`,
1105
- path: context,
1106
- suggestions: [`Add the '${field}' field to your configuration`]
1107
- });
1108
- }
1109
- };
1110
- /**
1111
- * Wrap unknown error in ApicodegenError if needed
1112
- */
1113
- function wrapError(error, context) {
1114
- if (error instanceof ApicodegenError) return error;
1115
- if (error instanceof Error) return new ApicodegenError({
1116
- code: context?.code || ErrorCodes.GENERATION_FAILED,
1117
- message: context?.message || error.message,
1118
- location: context?.location,
1119
- suggestions: context?.suggestions,
1120
- cause: error
1121
- });
1122
- return new ApicodegenError({
1123
- code: context?.code || ErrorCodes.GENERATION_FAILED,
1124
- message: String(error),
1125
- suggestions: context?.suggestions
1126
- });
1127
- }
1128
- /**
1129
- * Check if error is an ApicodegenError
1130
- */
1131
- function isApicodegenError(error) {
1132
- return error instanceof ApicodegenError;
1133
- }
1134
- //#endregion
1135
- //#region src/openapi/V2.ts
1136
- var V2 = class {
1137
- doc;
1138
- constructor(doc) {
1139
- this.doc = doc;
1140
- }
1141
- /**
1142
- * Resolves a path $ref to the actual path object.
1143
- */
1144
- resolvePathRef($ref) {
1145
- const refName = Base.ref2name($ref, this.doc);
1146
- return this.doc.paths?.[refName];
1147
- }
1148
- /**
1149
- * Is array schema.
1150
- */
1151
- isOpenAPIArraySchema(schema) {
1152
- return typeof schema === "object" && schema.type === "array";
1153
- }
1154
- /**
1155
- * OpenAPI schema to base schema.
1156
- */
1157
- getSchemaByRef(schema, reserveRef = false, enums = [], upLevelSchemaKey = "") {
1158
- let refName = "";
1159
- if (Base.isRef(schema)) {
1160
- refName = Base.upperCamelCase(Base.ref2name(schema.$ref));
1161
- if (reserveRef) return { type: upLevelSchemaKey + refName };
1162
- if (!this.doc.definitions) this.doc.definitions = {};
1163
- schema = this.doc.definitions[Base.ref2name(schema.$ref, this.doc)];
1164
- }
1165
- return this.toBaseSchema(schema, enums, "", upLevelSchemaKey + refName);
1166
- }
1167
- /**
1168
- * Transform all OpenAPI schema to Base Schema
1169
- */
1170
- toBaseSchema(schema, enums = [], schemaKey = "", upLevelSchemaKey = "") {
1171
- if (!schema) return { type: "unknown" };
1172
- if (Base.isRef(schema)) return this.getSchemaByRef(schema, true);
1173
- if (this.isOpenAPIArraySchema(schema)) {
1174
- const { type, description, items, required } = schema;
1175
- return {
1176
- type,
1177
- required: !!required,
1178
- description,
1179
- items: this.toBaseSchema(items, enums, schemaKey, upLevelSchemaKey)
1180
- };
1181
- } else {
1182
- const { required = [], allOf, anyOf, description, enum: enum_, format, oneOf, properties = {} } = schema;
1183
- let { type } = schema;
1184
- if (enum_ && type !== "boolean") {
1185
- const enumObject = {
1186
- name: Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(schemaKey)),
1187
- enum: [...new Set(enum_)]
1188
- };
1189
- const sameObject = Base.findSameSchema(enumObject, enums);
1190
- if (!sameObject && Base.isValidEnumType(schema)) enums.push(enumObject);
1191
- return {
1192
- type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
1193
- required,
1194
- description
1195
- };
1196
- }
1197
- if (type === void 0 && Object.keys(properties).length > 0) type = "object";
1198
- return {
1199
- type,
1200
- required,
1201
- description,
1202
- enum: enum_,
1203
- format,
1204
- allOf: allOf?.map((s) => Base.isRef(s) ? {
1205
- ...s,
1206
- ref: s.$ref,
1207
- type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1208
- } : this.toBaseSchema(s, enums)),
1209
- anyOf: anyOf?.map((s) => Base.isRef(s) ? {
1210
- ...s,
1211
- ref: s.$ref,
1212
- type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1213
- } : this.toBaseSchema(s, enums)),
1214
- oneOf: oneOf?.map((s) => Base.isRef(s) ? {
1215
- ...s,
1216
- ref: s.$ref,
1217
- type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1218
- } : this.toBaseSchema(s, enums)),
1219
- properties: Object.keys(properties).reduce((acc, p) => {
1220
- const propSchema = properties[p];
1221
- return {
1222
- ...acc,
1223
- [p]: Base.isRef(propSchema) ? { type: Base.capitalize(Base.ref2name(propSchema.$ref, this.doc)) } : this.toBaseSchema(propSchema, enums, p, upLevelSchemaKey)
1224
- };
1225
- }, {})
1226
- };
1227
- }
1228
- }
1229
- /**
1230
- * OpenAPI parameter to base parameter.
1231
- */
1232
- getParameterByRef(parameter, enums = [], upLevelSchemaKey = "") {
1233
- if (Base.isRef(parameter)) {
1234
- const refName = Base.ref2name(parameter.$ref, this.doc);
1235
- const resolved = this.doc.parameters?.[refName];
1236
- if (!resolved) throw new Error(`Parameter reference not found: ${parameter.$ref}`);
1237
- parameter = resolved;
1238
- }
1239
- const p = parameter;
1240
- const { name, required, description, type, items, enum: enum_, properties, schema } = p;
1241
- if (enum_) {
1242
- const type = Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(name));
1243
- const enumSchema = {
1244
- name: type,
1245
- enum: [...new Set(enum_)]
1246
- };
1247
- const sameEnum = Base.findSameSchema(enumSchema, enums);
1248
- if (!sameEnum && Base.isValidEnumType({
1249
- type,
1250
- enum: enum_
1251
- })) enums.push(enumSchema);
1252
- return {
1253
- name,
1254
- required,
1255
- description,
1256
- in: p.in,
1257
- schema: { type: sameEnum?.name ?? type }
1258
- };
1259
- }
1260
- if (items) return {
1261
- name,
1262
- required,
1263
- description,
1264
- in: p.in,
1265
- schema: {
1266
- type,
1267
- items
1268
- }
1269
- };
1270
- if (schema && Base.isRef(schema)) return {
1271
- name,
1272
- required,
1273
- description,
1274
- in: p.in,
1275
- schema: { type: Base.capitalize(Base.ref2name(schema.$ref)) }
1276
- };
1277
- return {
1278
- name,
1279
- required,
1280
- description,
1281
- in: p.in,
1282
- schema: {
1283
- type,
1284
- properties
1285
- }
1286
- };
1287
- }
1288
- /**
1289
- * OpenAPI schema to base response
1290
- */
1291
- getResponseByRef(schema) {
1292
- if (Base.isRef(schema)) schema = this.doc.responses[Base.ref2name(schema.$ref, this.doc)];
1293
- const { schema: responseSchema } = schema;
1294
- return [{
1295
- type: "application/json",
1296
- schema: responseSchema && this.getSchemaByRef(responseSchema, true)
1297
- }];
1298
- }
1299
- init() {
1300
- const { definitions = {}, responses = {}, paths = {} } = this.doc;
1301
- const enums = [];
1302
- const definitions_ = Object.keys(definitions).reduce((acc, key) => {
1303
- const schema = definitions[key];
1304
- return {
1305
- ...acc,
1306
- [key]: this.getSchemaByRef(schema, false, enums, key)
1307
- };
1308
- }, {});
1309
- const responses_ = Object.keys(responses).reduce((acc, key) => {
1310
- const response = responses[key];
1311
- return {
1312
- ...acc,
1313
- [key]: this.getResponseByRef(response)
1314
- };
1315
- }, {});
1316
- const apis = Object.keys(paths).reduce((acc, path) => {
1317
- let pathObject = paths[path] ?? {};
1318
- if (pathObject.$ref) {
1319
- const resolved = this.resolvePathRef(pathObject.$ref);
1320
- if (resolved) pathObject = resolved;
1321
- }
1322
- const { parameters = [] } = pathObject;
1323
- const methodApis = [];
1324
- Object.values(HttpMethods).forEach((method) => {
1325
- const methodObject = pathObject[method];
1326
- if (methodObject) {
1327
- const { deprecated, operationId, summary: summary_, description: description_, responses = {} } = methodObject;
1328
- const { parameters: parameters_ = [] } = methodObject;
1329
- const baseParameters = [...parameters, ...parameters_].map((parameter) => this.getParameterByRef(parameter, enums));
1330
- const uniqueParameterName = [...new Set(baseParameters.map((p) => p.name))];
1331
- if (Object.keys(responses).length === 0) Object.assign(responses, { 200: { description: "Successful response" } });
1332
- const inBody = baseParameters.filter((p) => p.in === "body" || p.in === "formData");
1333
- const notInBody = baseParameters.filter((p) => p.in !== "body" && p.in !== "formData");
1334
- const httpCodes = Object.keys(responses);
1335
- for (const code of httpCodes) if (code in responses) {
1336
- const response = responses[code];
1337
- const responseSchema = this.getResponseByRef(response);
1338
- const inBodyOnlyHasBody = inBody && inBody.length === 1 && inBody[0].in === "body" && inBody[0].name === "body";
1339
- methodApis.push({
1340
- method,
1341
- operationId,
1342
- summary: summary_,
1343
- deprecated,
1344
- description: description_,
1345
- parameters: uniqueParameterName.map((name) => notInBody.find((p) => p.name === name)).filter(Boolean),
1346
- responses: responseSchema,
1347
- requestBody: inBody.length > 0 ? inBodyOnlyHasBody ? [{
1348
- type: "application/json",
1349
- schema: inBody[0].schema
1350
- }] : [{
1351
- type: "application/json",
1352
- schema: {
1353
- type: "object",
1354
- properties: inBody.reduce((a, p) => {
1355
- return {
1356
- ...a,
1357
- [p.name]: {
1358
- type: p.schema?.type ?? "unknown",
1359
- required: p.schema?.required,
1360
- items: p.schema?.items,
1361
- description: p.schema?.description
1362
- }
1363
- };
1364
- }, {})
1365
- }
1366
- }] : void 0
1367
- });
1368
- break;
1369
- }
1370
- }
1371
- });
1372
- return {
1373
- ...acc,
1374
- [path]: methodApis
1375
- };
1376
- }, {});
1377
- return {
1378
- enums: Base.uniqueEnums(enums),
1379
- schemas: definitions_,
1380
- responses: responses_,
1381
- parameters: {},
1382
- requestBodies: {},
1383
- apis
1384
- };
1183
+ //#region src/core/logger.ts
1184
+ const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
1185
+ const green = (s) => `\x1b[32m${s}\x1b[0m`;
1186
+ const red = (s) => `\x1b[31m${s}\x1b[0m`;
1187
+ const blue = (s) => `\x1b[34m${s}\x1b[0m`;
1188
+ const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
1189
+ const magenta = (s) => `\x1b[35m${s}\x1b[0m`;
1190
+ const gray = (s) => `\x1b[90m${s}\x1b[0m`;
1191
+ const bold = (s) => `\x1b[1m${s}\x1b[0m`;
1192
+ const logger = {
1193
+ success(msg) {
1194
+ console.log(`${green("")} ${msg}`);
1195
+ },
1196
+ error(err, verbose = false) {
1197
+ if (isApicodegenError(err)) console.error(`${red("✗")} ${err.toString(verbose)}`);
1198
+ else if (err instanceof Error) {
1199
+ const msg = `Error: ${err.message}`;
1200
+ console.error(`${red("✗")} ${msg}${verbose && err.stack ? `\n${gray(err.stack)}` : ""}`);
1201
+ } else console.error(`${red("✗")} ${String(err)}`);
1202
+ },
1203
+ info(msg) {
1204
+ console.log(`${blue("ℹ")} ${msg}`);
1205
+ },
1206
+ warn(msg) {
1207
+ console.log(`${yellow("⚠")} ${msg}`);
1208
+ },
1209
+ loading(msg) {
1210
+ console.log(`${yellow("🔄")} ${msg}`);
1211
+ },
1212
+ watching(msg) {
1213
+ console.log(`${magenta("⟳")} ${msg}`);
1214
+ },
1215
+ fileChange(filePath) {
1216
+ console.log(`${yellow("↓")} ${filePath}`);
1217
+ },
1218
+ fileAdd(filePath) {
1219
+ console.log(`${green("+")} ${filePath}`);
1220
+ },
1221
+ shutdown() {
1222
+ console.log(`\n${gray("👋 Shutting down...")}`);
1223
+ },
1224
+ divider(width = 50) {
1225
+ console.log(`${bold(cyan("─".repeat(width)))}`);
1226
+ },
1227
+ heading(text, mode, width = 50) {
1228
+ console.log(`${bold(cyan("─".repeat(width)))}`);
1229
+ console.log(`${bold(cyan(text))}`);
1230
+ console.log(`${gray("Mode:")} ${mode || "unknown"}`);
1231
+ console.log(`${bold(cyan("─".repeat(width)))}`);
1232
+ },
1233
+ item(label, color = "green") {
1234
+ const icon = color === "green" ? "✓" : color === "red" ? "✗" : "⚠";
1235
+ console.log(`${color === "green" ? green(icon) : color === "red" ? red(icon) : yellow(icon)} ${label}`);
1236
+ },
1237
+ summary(stats) {
1238
+ const { succeeded, failed, endpoints, schemas, duration } = stats;
1239
+ const label = `API Code Gen - Complete (${succeeded} succeeded${failed > 0 ? `, ${failed} failed` : ""}, ${endpoints} endpoints, ${schemas} schemas, ${duration}ms)`;
1240
+ if (failed === 0) console.log(`${green("✓")} ${label}`);
1241
+ else console.log(`${yellow("")} ${label}`);
1385
1242
  }
1386
1243
  };
1387
1244
  //#endregion
1388
- //#region src/openapi/V3.ts
1389
- var V3 = class {
1390
- doc;
1391
- constructor(doc) {
1392
- this.doc = doc;
1393
- }
1245
+ //#region src/openapi/VersionedProvider.ts
1246
+ /**
1247
+ * Abstract base class. Subclasses implement the four container accessors
1248
+ * and a version tag. The shared logic lives here.
1249
+ */
1250
+ var VersionedProvider = class {
1394
1251
  /**
1395
1252
  * Resolves a path $ref to the actual path object.
1396
1253
  */
@@ -1402,28 +1259,37 @@ var V3 = class {
1402
1259
  * Is array schema.
1403
1260
  */
1404
1261
  isOpenAPIArraySchema(schema) {
1405
- return typeof schema === "object" && schema.type === "array";
1262
+ return typeof schema === "object" && schema !== null && schema.type === "array";
1406
1263
  }
1407
1264
  /**
1408
- * OpenAPI schema to base schema.
1265
+ * OpenAPI schema to base schema. Override for version-specific quirks
1266
+ * (V3_1 throws on missing ref; V3 returns `{ type: 'unknown' }`).
1409
1267
  */
1410
1268
  getSchemaByRef(schema, reserveRef = false, enums = [], upLevelSchemaKey = "") {
1411
1269
  let refName = "";
1412
1270
  if (Base.isRef(schema)) {
1413
- refName = Base.capitalize(Base.ref2name(schema.$ref));
1271
+ refName = this.formatRefName(Base.ref2name(schema.$ref));
1414
1272
  if (reserveRef) return { type: upLevelSchemaKey + refName };
1415
- const resolvedSchema = this.doc.components?.schemas?.[Base.ref2name(schema.$ref, this.doc)];
1273
+ const resolvedSchema = this.getSchemaContainer()?.[Base.ref2name(schema.$ref, this.doc)];
1416
1274
  if (!resolvedSchema) return { type: "unknown" };
1417
1275
  schema = resolvedSchema;
1418
1276
  }
1419
1277
  return this.toBaseSchema(schema, enums, "", upLevelSchemaKey + refName);
1420
1278
  }
1421
1279
  /**
1422
- * OpenAPI parameter to base parameter.
1280
+ * Format a ref name for the schema's `type` field.
1281
+ * V3 uses `capitalize`; V3_1 uses `upperCamelCase` (preserved for compat).
1282
+ */
1283
+ formatRefName(name) {
1284
+ return Base.capitalize(name);
1285
+ }
1286
+ /**
1287
+ * OpenAPI parameter to base parameter. Override in V2 — its parameter
1288
+ * shape is structurally different (top-level `items`/`properties`/`enum`).
1423
1289
  */
1424
1290
  getParameterByRef(schema, enums = [], upLevelSchemaKey = "") {
1425
1291
  if (Base.isRef(schema)) {
1426
- const resolvedSchema = this.doc.components?.parameters?.[Base.ref2name(schema.$ref, this.doc)];
1292
+ const resolvedSchema = this.getParameterContainer()?.[Base.ref2name(schema.$ref, this.doc)];
1427
1293
  if (!resolvedSchema) return {
1428
1294
  name: "unknown",
1429
1295
  in: "query"
@@ -1454,37 +1320,38 @@ var V3 = class {
1454
1320
  description,
1455
1321
  deprecated,
1456
1322
  in: schema.in,
1457
- schema: schema.schema && this.getSchemaByRef(schema.schema, false, enums, upLevelSchemaKey + Base.capitalize(name))
1323
+ schema: schema.schema ? this.getSchemaByRef(schema.schema, false, enums, upLevelSchemaKey + Base.capitalize(name)) : void 0
1458
1324
  };
1459
1325
  }
1460
1326
  /**
1461
- * OpenAPI schema to base response
1327
+ * OpenAPI schema to base response. Override in V2 (its response shape
1328
+ * lacks the `content` wrapper).
1462
1329
  */
1463
1330
  getResponseByRef(schema) {
1464
1331
  if (Base.isRef(schema)) {
1465
- const resolvedSchema = this.doc.components?.responses?.[Base.ref2name(schema.$ref, this.doc)];
1332
+ const resolvedSchema = this.getResponseContainer()?.[Base.ref2name(schema.$ref, this.doc)];
1466
1333
  if (!resolvedSchema) return [];
1467
1334
  schema = resolvedSchema;
1468
1335
  }
1469
1336
  const { content = {} } = schema;
1470
1337
  return Object.keys(content).map((c) => ({
1471
1338
  type: c,
1472
- schema: content[c].schema && this.getSchemaByRef(content[c].schema, true)
1339
+ schema: content[c].schema ? this.getSchemaByRef(content[c].schema, true) : void 0
1473
1340
  }));
1474
1341
  }
1475
1342
  /**
1476
- * OpenAPI schema to requestBody.
1343
+ * OpenAPI schema to requestBody. V2 has no requestBody concept.
1477
1344
  */
1478
1345
  getRequestBodyByRef(schema, enums = []) {
1479
1346
  if (Base.isRef(schema)) {
1480
- const resolvedSchema = this.doc.components?.requestBodies?.[Base.ref2name(schema.$ref, this.doc)];
1347
+ const resolvedSchema = this.getRequestBodyContainer()?.[Base.ref2name(schema.$ref, this.doc)];
1481
1348
  if (!resolvedSchema) return [];
1482
1349
  schema = resolvedSchema;
1483
1350
  }
1484
1351
  const { content = {} } = schema;
1485
1352
  return Object.keys(content).map((c) => ({
1486
1353
  type: c,
1487
- schema: content[c].schema && this.getSchemaByRef(content[c].schema, false, enums)
1354
+ schema: content[c].schema ? this.getSchemaByRef(content[c].schema, false, enums) : void 0
1488
1355
  }));
1489
1356
  }
1490
1357
  /**
@@ -1495,89 +1362,98 @@ var V3 = class {
1495
1362
  if (Base.isRef(schema)) return this.getSchemaByRef(schema, true);
1496
1363
  if (this.isOpenAPIArraySchema(schema)) {
1497
1364
  const { type, description, items, required } = schema;
1365
+ const itemsSchema = items ? this.toBaseSchema(items, enums, schemaKey, upLevelSchemaKey) : { type: "unknown" };
1498
1366
  return {
1499
1367
  type,
1500
1368
  required: !!required,
1501
1369
  description,
1502
- items: this.toBaseSchema(items, enums, schemaKey, upLevelSchemaKey)
1370
+ items: itemsSchema
1503
1371
  };
1504
- } else {
1505
- const { required = [], allOf, anyOf, description, deprecated, enum: enum_, format, oneOf, properties = {} } = schema;
1506
- let { type } = schema;
1507
- if (enum_ && type !== "boolean") {
1508
- const enumObject = {
1509
- name: Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(schemaKey)),
1510
- enum: [...new Set(enum_)]
1511
- };
1512
- const sameObject = Base.findSameSchema(enumObject, enums);
1513
- if (!sameObject && Base.isValidEnumType(schema)) enums.push(enumObject);
1514
- return {
1515
- type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
1516
- required,
1517
- description,
1518
- deprecated
1519
- };
1520
- }
1521
- if (type === void 0 && Object.keys(properties).length > 0) type = "object";
1372
+ }
1373
+ const { required = [], allOf, anyOf, description, deprecated, enum: enum_, format, oneOf, properties = {} } = schema;
1374
+ let { type } = schema;
1375
+ if (enum_ && type !== "boolean") {
1376
+ const enumObject = {
1377
+ name: Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(schemaKey)),
1378
+ enum: [...new Set(enum_)]
1379
+ };
1380
+ const sameObject = Base.findSameSchema(enumObject, enums);
1381
+ if (!sameObject && Base.isValidEnumType(schema)) enums.push(enumObject);
1522
1382
  return {
1523
- type,
1383
+ type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
1524
1384
  required,
1525
1385
  description,
1526
- deprecated,
1527
- enum: enum_,
1528
- format,
1529
- allOf: allOf?.map((s) => Base.isRef(s) ? {
1530
- ...s,
1531
- ref: s.$ref,
1532
- type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1533
- } : this.toBaseSchema(s, enums)),
1534
- anyOf: anyOf?.map((s) => Base.isRef(s) ? {
1535
- ...s,
1536
- ref: s.$ref,
1537
- type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1538
- } : this.toBaseSchema(s, enums)),
1539
- oneOf: oneOf?.map((s) => Base.isRef(s) ? {
1540
- ...s,
1541
- ref: s.$ref,
1542
- type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1543
- } : this.toBaseSchema(s, enums)),
1544
- properties: Object.keys(properties).reduce((acc, p) => {
1545
- const propSchema = properties[p];
1546
- return {
1547
- ...acc,
1548
- [p]: Base.isRef(propSchema) ? { type: Base.capitalize(Base.ref2name(propSchema.$ref, this.doc)) } : this.toBaseSchema(propSchema, enums, p, upLevelSchemaKey)
1549
- };
1550
- }, {})
1386
+ deprecated
1551
1387
  };
1552
1388
  }
1389
+ if (type === void 0 && Object.keys(properties).length > 0) type = "object";
1390
+ return {
1391
+ type,
1392
+ required,
1393
+ description,
1394
+ deprecated,
1395
+ enum: enum_,
1396
+ format,
1397
+ allOf: allOf?.map((s) => Base.isRef(s) ? {
1398
+ ...s,
1399
+ ref: s.$ref,
1400
+ type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1401
+ } : this.toBaseSchema(s, enums)),
1402
+ anyOf: anyOf?.map((s) => Base.isRef(s) ? {
1403
+ ...s,
1404
+ ref: s.$ref,
1405
+ type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1406
+ } : this.toBaseSchema(s, enums)),
1407
+ oneOf: oneOf?.map((s) => Base.isRef(s) ? {
1408
+ ...s,
1409
+ ref: s.$ref,
1410
+ type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1411
+ } : this.toBaseSchema(s, enums)),
1412
+ properties: Object.keys(properties).reduce((acc, p) => {
1413
+ const propSchema = properties[p];
1414
+ return {
1415
+ ...acc,
1416
+ [p]: Base.isRef(propSchema) ? {
1417
+ type: Base.capitalize(Base.ref2name(propSchema.$ref, this.doc)),
1418
+ isRef: true
1419
+ } : this.toBaseSchema(propSchema, enums, p, upLevelSchemaKey)
1420
+ };
1421
+ }, {})
1422
+ };
1553
1423
  }
1424
+ /**
1425
+ * Run the parsing pipeline and return a ProviderInitResult.
1426
+ */
1554
1427
  init() {
1555
- const { components = {}, paths = {} } = this.doc;
1428
+ const { paths = {} } = this.doc;
1556
1429
  const enums = [];
1557
- const { requestBodies = {}, responses = {}, parameters = {}, schemas = {} } = components;
1558
- const schemas_ = Object.keys(schemas).reduce((acc, key) => {
1559
- const schema = schemas[key];
1430
+ const schemaContainer = this.getSchemaContainer() ?? {};
1431
+ const parameterContainer = this.getParameterContainer() ?? {};
1432
+ const responseContainer = this.getResponseContainer() ?? {};
1433
+ const requestBodyContainer = this.getRequestBodyContainer() ?? {};
1434
+ const schemas_ = Object.keys(schemaContainer).reduce((acc, key) => {
1435
+ const schema = schemaContainer[key];
1560
1436
  return {
1561
1437
  ...acc,
1562
1438
  [key]: this.getSchemaByRef(schema, false, enums, key)
1563
1439
  };
1564
1440
  }, {});
1565
- const parameters_ = Object.keys(parameters).reduce((acc, key) => {
1566
- const parameter = parameters[key];
1441
+ const parameters_ = Object.keys(parameterContainer).reduce((acc, key) => {
1442
+ const parameter = parameterContainer[key];
1567
1443
  return {
1568
1444
  ...acc,
1569
1445
  [key]: this.getParameterByRef(parameter, enums, key)
1570
1446
  };
1571
1447
  }, {});
1572
- const responses_ = Object.keys(responses).reduce((acc, key) => {
1573
- const response = responses[key];
1448
+ const responses_ = Object.keys(responseContainer).reduce((acc, key) => {
1449
+ const response = responseContainer[key];
1574
1450
  return {
1575
1451
  ...acc,
1576
1452
  [key]: this.getResponseByRef(response)
1577
1453
  };
1578
1454
  }, {});
1579
- const requestBodies_ = Object.keys(requestBodies).reduce((acc, key) => {
1580
- const requestBody = requestBodies[key];
1455
+ const requestBodies_ = Object.keys(requestBodyContainer).reduce((acc, key) => {
1456
+ const requestBody = requestBodyContainer[key];
1581
1457
  return {
1582
1458
  ...acc,
1583
1459
  [key]: this.getRequestBodyByRef(requestBody, enums)
@@ -1594,15 +1470,16 @@ var V3 = class {
1594
1470
  Object.values(HttpMethods).forEach((method) => {
1595
1471
  const methodObject = pathObject[method];
1596
1472
  if (methodObject) {
1597
- const { deprecated, operationId, responses = {}, summary: summary_, description: description_, requestBody = { content: {} } } = methodObject;
1473
+ const { deprecated, operationId, responses, summary: summary_, description: description_, requestBody = { content: {} } } = methodObject;
1474
+ const responsesClone = responses ? { ...responses } : {};
1598
1475
  const { parameters: parameters_ = [] } = methodObject;
1599
1476
  const baseParameters = [...parameters, ...parameters_].map((parameter) => this.getParameterByRef(parameter, enums));
1600
1477
  const baseRequestBody = this.getRequestBodyByRef(requestBody, enums);
1601
1478
  const uniqueParameterName = [...new Set(baseParameters.map((p) => p.name))];
1602
- if (Object.keys(responses).length === 0) Object.assign(responses, { 200: { description: "Successful response" } });
1603
- const httpCodes = Object.keys(responses);
1604
- for (const code of httpCodes) if (code in responses) {
1605
- const response = responses[code];
1479
+ if (Object.keys(responsesClone).length === 0) responsesClone[200] = { description: "Successful response" };
1480
+ const httpCodes = Object.keys(responsesClone);
1481
+ for (const code of httpCodes) if (code in responsesClone) {
1482
+ const response = responsesClone[code];
1606
1483
  const responseSchema = this.getResponseByRef(response);
1607
1484
  methodApis.push({
1608
1485
  method,
@@ -1634,241 +1511,246 @@ var V3 = class {
1634
1511
  }
1635
1512
  };
1636
1513
  //#endregion
1637
- //#region src/openapi/V3_1.ts
1638
- var V3_1 = class {
1514
+ //#region src/openapi/V2.ts
1515
+ var V2 = class extends VersionedProvider {
1639
1516
  doc;
1517
+ version = "v2";
1640
1518
  constructor(doc) {
1519
+ super();
1641
1520
  this.doc = doc;
1642
1521
  }
1643
- /**
1644
- * Resolves a path $ref to the actual path object.
1645
- */
1646
- resolvePathRef($ref) {
1647
- const refName = Base.ref2name($ref, this.doc);
1648
- return this.doc.paths?.[refName];
1522
+ getSchemaContainer() {
1523
+ return this.doc.definitions;
1649
1524
  }
1650
- /**
1651
- * Is array schema.
1652
- */
1653
- isOpenAPIArraySchema(schema) {
1654
- return typeof schema === "object" && schema.type === "array";
1525
+ getParameterContainer() {
1526
+ return this.doc.parameters;
1655
1527
  }
1656
- /**
1657
- * OpenAPI schema to base schema.
1658
- */
1659
- getSchemaByRef(schema, reserveRef = false, enums = [], upLevelSchemaKey = "") {
1660
- let refName = "";
1661
- if (Base.isRef(schema)) {
1662
- refName = Base.upperCamelCase(Base.ref2name(schema.$ref));
1663
- if (reserveRef) return { type: upLevelSchemaKey + refName };
1664
- if (!this.doc.components) this.doc.components = { schemas: {} };
1665
- const resolvedSchema = this.doc.components.schemas?.[Base.ref2name(schema.$ref, this.doc)];
1666
- if (!resolvedSchema) throw new Error(`Schema reference not found: ${refName}`);
1667
- schema = resolvedSchema;
1668
- }
1669
- return this.toBaseSchema(schema, enums, "", upLevelSchemaKey + refName);
1528
+ getResponseContainer() {
1529
+ return this.doc.responses;
1670
1530
  }
1531
+ getRequestBodyContainer() {}
1671
1532
  /**
1672
- * OpenAPI parameter to base parameter.
1533
+ * V2 parameter shape differs from V3: top-level `items`/`properties`/`enum`
1534
+ * rather than nested under `schema`. Override accordingly.
1673
1535
  */
1674
- getParameterByRef(schema, enums = [], upLevelSchemaKey = "") {
1675
- if (Base.isRef(schema)) schema = this.doc.components?.parameters?.[Base.ref2name(schema.$ref, this.doc)];
1676
- const { name, required, deprecated, description, schema: parameterSchema } = schema;
1677
- if (parameterSchema && !Base.isRef(parameterSchema) && parameterSchema.enum) {
1678
- const type = Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(name));
1536
+ getParameterByRef(parameter, enums = [], upLevelSchemaKey = "") {
1537
+ if (Base.isRef(parameter)) {
1538
+ const refName = Base.ref2name(parameter.$ref, this.doc);
1539
+ const resolved = this.doc.parameters?.[refName];
1540
+ if (!resolved) throw new Error(`Parameter reference not found: ${parameter.$ref}`);
1541
+ parameter = resolved;
1542
+ }
1543
+ const p = parameter;
1544
+ const { name, required, description, type, items, enum: enum_, properties, schema } = p;
1545
+ if (enum_) {
1546
+ const enumType = Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(name));
1679
1547
  const enumSchema = {
1680
- name: type,
1681
- enum: [...new Set(parameterSchema.enum)]
1548
+ name: enumType,
1549
+ enum: [...new Set(enum_)]
1682
1550
  };
1683
1551
  const sameEnum = Base.findSameSchema(enumSchema, enums);
1684
- if (!sameEnum && Base.isValidEnumType(parameterSchema)) enums.push(enumSchema);
1552
+ if (!sameEnum && Base.isValidEnumType({
1553
+ type: enumType,
1554
+ enum: enum_
1555
+ })) enums.push(enumSchema);
1685
1556
  return {
1686
1557
  name,
1687
1558
  required,
1688
1559
  description,
1689
- deprecated,
1690
- in: schema.in,
1691
- schema: { type: sameEnum?.name ?? type }
1560
+ in: p.in,
1561
+ schema: { type: sameEnum?.name ?? enumType }
1692
1562
  };
1693
1563
  }
1564
+ if (items) return {
1565
+ name,
1566
+ required,
1567
+ description,
1568
+ in: p.in,
1569
+ schema: {
1570
+ type,
1571
+ items
1572
+ }
1573
+ };
1574
+ if (schema && Base.isRef(schema)) return {
1575
+ name,
1576
+ required,
1577
+ description,
1578
+ in: p.in,
1579
+ schema: { type: Base.capitalize(Base.ref2name(schema.$ref)) }
1580
+ };
1694
1581
  return {
1695
1582
  name,
1696
1583
  required,
1697
1584
  description,
1698
- deprecated,
1699
- in: schema.in,
1700
- schema: schema.schema && this.getSchemaByRef(schema.schema, false, enums, upLevelSchemaKey + Base.capitalize(name))
1585
+ in: p.in,
1586
+ schema: {
1587
+ type,
1588
+ properties
1589
+ }
1701
1590
  };
1702
1591
  }
1703
1592
  /**
1704
- * OpenAPI schema to base response
1593
+ * V2 response shape: a `schema` field directly, not `content`. V2 always
1594
+ * emits JSON.
1705
1595
  */
1706
1596
  getResponseByRef(schema) {
1707
- if (Base.isRef(schema)) schema = this.doc.components?.responses?.[Base.ref2name(schema.$ref, this.doc)];
1708
- const { content = {} } = schema;
1709
- return Object.keys(content).map((c) => ({
1710
- type: c,
1711
- schema: content[c].schema && this.getSchemaByRef(content[c].schema, true)
1712
- }));
1713
- }
1714
- /**
1715
- * OpenAPI schema to requestBody.
1716
- */
1717
- getRequestBodyByRef(schema, enums = []) {
1718
- if (Base.isRef(schema)) schema = this.doc.components?.requestBodies?.[Base.ref2name(schema.$ref, this.doc)];
1719
- const { content = {} } = schema;
1720
- return Object.keys(content).map((c) => ({
1721
- type: c,
1722
- schema: content[c].schema && this.getSchemaByRef(content[c].schema, true, enums)
1723
- }));
1597
+ if (Base.isRef(schema)) schema = this.doc.responses[Base.ref2name(schema.$ref, this.doc)];
1598
+ const { schema: responseSchema } = schema;
1599
+ return [{
1600
+ type: "application/json",
1601
+ schema: responseSchema ? this.getSchemaByRef(responseSchema, true) : void 0
1602
+ }];
1724
1603
  }
1725
1604
  /**
1726
- * Transform all OpenAPI schema to Base Schema
1605
+ * V2 has no `requestBody` concept; parameters with `in: body` or
1606
+ * `in: formData` are split out and turned into a synthetic requestBody
1607
+ * here. A single body param named `body` is used directly; otherwise
1608
+ * the body / formData params are wrapped in a synthetic object.
1727
1609
  */
1728
- toBaseSchema(schema, enums = [], schemaKey = "", upLevelSchemaKey = "") {
1729
- if (!schema) return { type: "unknown" };
1730
- if (Base.isRef(schema)) return this.getSchemaByRef(schema, true);
1731
- if (this.isOpenAPIArraySchema(schema)) {
1732
- const { type, description, items, required } = schema;
1733
- return {
1734
- type,
1735
- required: !!required,
1736
- description,
1737
- items: this.toBaseSchema(items, enums, schemaKey, upLevelSchemaKey)
1738
- };
1739
- } else {
1740
- const { required = [], allOf, anyOf, description, deprecated, enum: enum_, format, oneOf, properties = {} } = schema;
1741
- let { type } = schema;
1742
- if (enum_ && type !== "boolean") {
1743
- const enumObject = {
1744
- name: Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(schemaKey)),
1745
- enum: [...new Set(enum_)]
1746
- };
1747
- const sameObject = Base.findSameSchema(enumObject, enums);
1748
- if (!sameObject && Base.isValidEnumType(schema)) enums.push(enumObject);
1749
- return {
1750
- type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
1751
- required,
1752
- description,
1753
- deprecated
1754
- };
1755
- }
1756
- if (type === void 0 && Object.keys(properties).length > 0) type = "object";
1757
- return {
1758
- type,
1759
- required,
1760
- description,
1761
- deprecated,
1762
- enum: enum_,
1763
- format,
1764
- allOf: allOf?.map((s) => Base.isRef(s) ? {
1765
- ...s,
1766
- ref: s.$ref,
1767
- type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1768
- } : this.toBaseSchema(s, enums)),
1769
- anyOf: anyOf?.map((s) => Base.isRef(s) ? {
1770
- ...s,
1771
- ref: s.$ref,
1772
- type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1773
- } : this.toBaseSchema(s, enums)),
1774
- oneOf: oneOf?.map((s) => Base.isRef(s) ? {
1775
- ...s,
1776
- ref: s.$ref,
1777
- type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
1778
- } : this.toBaseSchema(s, enums)),
1779
- properties: Object.keys(properties).reduce((acc, p) => {
1780
- const propSchema = properties[p];
1781
- return {
1782
- ...acc,
1783
- [p]: Base.isRef(propSchema) ? { type: Base.capitalize(Base.ref2name(propSchema.$ref, this.doc)) } : this.toBaseSchema(propSchema, enums, p, upLevelSchemaKey)
1784
- };
1785
- }, {})
1786
- };
1787
- }
1788
- }
1789
1610
  init() {
1790
- const { components = {}, paths = {} } = this.doc;
1611
+ const { paths = {} } = this.doc;
1791
1612
  const enums = [];
1792
- const { requestBodies = {}, responses = {}, parameters = {}, schemas = {} } = components;
1793
- const schemas_ = Object.keys(schemas).reduce((acc, key) => {
1794
- const schema = schemas[key];
1613
+ const schemaContainer = this.getSchemaContainer() ?? {};
1614
+ const parameterContainer = this.getParameterContainer() ?? {};
1615
+ const responseContainer = this.getResponseContainer() ?? {};
1616
+ const schemas_ = Object.keys(schemaContainer).reduce((acc, key) => {
1617
+ const schema = schemaContainer[key];
1795
1618
  return {
1796
1619
  ...acc,
1797
1620
  [key]: this.getSchemaByRef(schema, false, enums, key)
1798
1621
  };
1799
1622
  }, {});
1800
- const parameters_ = Object.keys(parameters).reduce((acc, key) => {
1801
- const parameter = parameters[key];
1623
+ const parameters_ = Object.keys(parameterContainer).reduce((acc, key) => {
1624
+ const parameter = parameterContainer[key];
1802
1625
  return {
1803
1626
  ...acc,
1804
1627
  [key]: this.getParameterByRef(parameter, enums, key)
1805
1628
  };
1806
1629
  }, {});
1807
- const responses_ = Object.keys(responses).reduce((acc, key) => {
1808
- const response = responses[key];
1630
+ const responses_ = Object.keys(responseContainer).reduce((acc, key) => {
1631
+ const response = responseContainer[key];
1809
1632
  return {
1810
1633
  ...acc,
1811
1634
  [key]: this.getResponseByRef(response)
1812
1635
  };
1813
1636
  }, {});
1814
- const requestBodies_ = Object.keys(requestBodies).reduce((acc, key) => {
1815
- const requestBody = requestBodies[key];
1816
- return {
1817
- ...acc,
1818
- [key]: this.getRequestBodyByRef(requestBody, enums)
1819
- };
1820
- }, {});
1821
- const apis = Object.keys(paths).reduce((acc, path) => {
1637
+ const apis = {};
1638
+ for (const path of Object.keys(paths)) {
1822
1639
  let pathObject = paths[path] ?? {};
1823
1640
  if (pathObject.$ref) {
1824
1641
  const resolved = this.resolvePathRef(pathObject.$ref);
1825
1642
  if (resolved) pathObject = resolved;
1826
1643
  }
1827
- const { parameters = [], description, summary } = pathObject;
1644
+ const { parameters = [] } = pathObject;
1828
1645
  const methodApis = [];
1829
1646
  Object.values(HttpMethods).forEach((method) => {
1830
1647
  const methodObject = pathObject[method];
1831
- if (methodObject) {
1832
- const { deprecated, operationId, summary: summary_, description: description_, responses = {}, requestBody = { content: {} } } = methodObject;
1833
- const { parameters: parameters_ = [] } = methodObject;
1834
- const baseParameters = [...parameters, ...parameters_].map((parameter) => this.getParameterByRef(parameter, enums));
1835
- const baseRequestBody = this.getRequestBodyByRef(requestBody, enums);
1836
- const uniqueParameterName = [...new Set(baseParameters.map((p) => p.name))];
1837
- if (Object.keys(responses).length === 0) Object.assign(responses, { 200: { description: "Successful response" } });
1838
- const httpCodes = Object.keys(responses);
1839
- for (const code of httpCodes) if (code in responses) {
1840
- const response = responses[code];
1841
- const responseSchema = this.getResponseByRef(response);
1842
- methodApis.push({
1843
- method,
1844
- operationId,
1845
- summary: summary_ ?? summary,
1846
- description: description_ ?? description,
1847
- deprecated,
1848
- parameters: uniqueParameterName.map((name) => baseParameters.find((p) => p.name === name)).filter((p) => p !== void 0),
1849
- responses: responseSchema,
1850
- requestBody: baseRequestBody
1851
- });
1852
- break;
1853
- }
1648
+ if (!methodObject) return;
1649
+ const { deprecated, operationId, summary: summary_, description: description_, responses } = methodObject;
1650
+ const { parameters: parameters_ = [] } = methodObject;
1651
+ const baseParameters = [...parameters, ...parameters_].map((p) => this.getParameterByRef(p, enums));
1652
+ const uniqueParameterName = [...new Set(baseParameters.map((p) => p.name))];
1653
+ const responsesClone = responses ? { ...responses } : {};
1654
+ if (Object.keys(responsesClone).length === 0) responsesClone[200] = { description: "Successful response" };
1655
+ const inBody = baseParameters.filter((p) => p.in === "body" || p.in === "formData");
1656
+ const notInBody = baseParameters.filter((p) => p.in !== "body" && p.in !== "formData");
1657
+ const httpCodes = Object.keys(responsesClone);
1658
+ for (const code of httpCodes) if (code in responsesClone) {
1659
+ const response = responsesClone[code];
1660
+ const responseSchema = this.getResponseByRef(response);
1661
+ const inBodyOnlyHasBody = inBody.length === 1 && inBody[0].in === "body" && inBody[0].name === "body";
1662
+ methodApis.push({
1663
+ method,
1664
+ operationId,
1665
+ summary: summary_,
1666
+ deprecated,
1667
+ description: description_,
1668
+ parameters: uniqueParameterName.map((name) => notInBody.find((p) => p.name === name)).filter((p) => p !== void 0),
1669
+ responses: responseSchema,
1670
+ requestBody: inBody.length > 0 ? inBodyOnlyHasBody ? [{
1671
+ type: "application/json",
1672
+ schema: inBody[0].schema
1673
+ }] : [{
1674
+ type: "application/json",
1675
+ schema: {
1676
+ type: "object",
1677
+ properties: inBody.reduce((a, p) => {
1678
+ return {
1679
+ ...a,
1680
+ [p.name]: {
1681
+ type: p.schema?.type ?? "unknown",
1682
+ required: p.schema?.required,
1683
+ items: p.schema?.items,
1684
+ description: p.schema?.description
1685
+ }
1686
+ };
1687
+ }, {})
1688
+ }
1689
+ }] : void 0
1690
+ });
1691
+ break;
1854
1692
  }
1855
1693
  });
1856
- return {
1857
- ...acc,
1858
- [path]: methodApis
1859
- };
1860
- }, {});
1694
+ apis[path] = methodApis;
1695
+ }
1861
1696
  return {
1862
1697
  enums: Base.uniqueEnums(enums),
1863
1698
  schemas: schemas_,
1864
1699
  responses: responses_,
1865
1700
  parameters: parameters_,
1866
- requestBodies: requestBodies_,
1701
+ requestBodies: {},
1867
1702
  apis
1868
1703
  };
1869
1704
  }
1870
1705
  };
1871
1706
  //#endregion
1707
+ //#region src/openapi/V3.ts
1708
+ var V3 = class extends VersionedProvider {
1709
+ doc;
1710
+ version = "v3";
1711
+ constructor(doc) {
1712
+ super();
1713
+ this.doc = doc;
1714
+ }
1715
+ getSchemaContainer() {
1716
+ return this.doc.components?.schemas;
1717
+ }
1718
+ getParameterContainer() {
1719
+ return this.doc.components?.parameters;
1720
+ }
1721
+ getResponseContainer() {
1722
+ return this.doc.components?.responses;
1723
+ }
1724
+ getRequestBodyContainer() {
1725
+ return this.doc.components?.requestBodies;
1726
+ }
1727
+ };
1728
+ //#endregion
1729
+ //#region src/openapi/V3_1.ts
1730
+ var V3_1 = class extends VersionedProvider {
1731
+ doc;
1732
+ version = "v3_1";
1733
+ constructor(doc) {
1734
+ super();
1735
+ this.doc = doc;
1736
+ }
1737
+ formatRefName(name) {
1738
+ return Base.upperCamelCase(name);
1739
+ }
1740
+ getSchemaContainer() {
1741
+ return this.doc.components?.schemas;
1742
+ }
1743
+ getParameterContainer() {
1744
+ return this.doc.components?.parameters;
1745
+ }
1746
+ getResponseContainer() {
1747
+ return this.doc.components?.responses;
1748
+ }
1749
+ getRequestBodyContainer() {
1750
+ return this.doc.components?.requestBodies;
1751
+ }
1752
+ };
1753
+ //#endregion
1872
1754
  //#region src/openapi/index.ts
1873
1755
  const logger$1 = createScopedLogger("OpenAPI");
1874
1756
  let OpenAPIVersion = /* @__PURE__ */ function(OpenAPIVersion) {
@@ -1934,7 +1816,7 @@ async function codeGen(initOptions) {
1934
1816
  //#endregion
1935
1817
  //#region src/vite-plugin/index.ts
1936
1818
  const PLUGIN_NAME = "api-code-gen";
1937
- const logger = createScopedLogger("api-code-gen");
1819
+ const pluginLogger = createScopedLogger("api-code-gen");
1938
1820
  /**
1939
1821
  * Run TypeScript type checking on generated file
1940
1822
  */
@@ -1963,7 +1845,7 @@ async function validateSpecPath(specPath) {
1963
1845
  async function generateForOption(option) {
1964
1846
  const { name, typeCheck = true, verbose, ...restOptions } = option;
1965
1847
  try {
1966
- console.log(`\x1b[36m├─\x1b[0m ${name}`);
1848
+ logger.info(`Generating ${name}...`);
1967
1849
  const config = await loadConfig({
1968
1850
  name,
1969
1851
  cliOptions: {
@@ -1987,8 +1869,8 @@ async function generateForOption(option) {
1987
1869
  if (typeCheck && config.output) {
1988
1870
  const typeErrors = await runTypeCheck(config.output);
1989
1871
  if (typeErrors.length > 0) {
1990
- logger.warn(`Type check failed for ${config.output}`);
1991
- if (verbose) for (const error of typeErrors) logger.warn(` ${error}`);
1872
+ pluginLogger.warn(`Type check failed for ${config.output}`);
1873
+ if (verbose) for (const error of typeErrors) pluginLogger.warn(` ${error}`);
1992
1874
  }
1993
1875
  }
1994
1876
  return {
@@ -2029,50 +1911,52 @@ async function generateForOption(option) {
2029
1911
  */
2030
1912
  function apiCodeGenPlugin(options) {
2031
1913
  if (!Array.isArray(options) || options.length === 0) {
2032
- logger.warn("No API configurations provided to apiCodeGenPlugin");
1914
+ pluginLogger.warn("No API configurations provided to apiCodeGenPlugin");
2033
1915
  return { name: PLUGIN_NAME };
2034
1916
  }
2035
1917
  return {
2036
1918
  name: PLUGIN_NAME,
2037
1919
  async config(_config, env) {
2038
- console.log(`\x1b[1m\x1b[36m${"".repeat(50)}\x1b[0m`);
2039
- console.log(`\x1b[1m\x1b[36mAPI Code Gen\x1b[0m`);
2040
- console.log(`\x1b[90mMode:\x1b[0m ${env?.command || "unknown"}`);
2041
- console.log(`\x1b[1m\x1b[36m${"─".repeat(50)}\x1b[0m`);
1920
+ logger.heading("API Code Gen", env?.command);
2042
1921
  const results = await Promise.all(options.map(generateForOption));
2043
1922
  const successCount = results.filter((r) => r.success).length;
2044
1923
  const failCount = options.length - successCount;
2045
- console.log(`\x1b[1m\x1b[36m${"─".repeat(50)}\x1b[0m`);
1924
+ logger.divider();
2046
1925
  for (const result of results) if (result.success) {
2047
1926
  const { name, output, stats } = result;
2048
- if (stats) console.log(`\x1b[32m✓\x1b[0m ${name} → ${output} (${stats.endpoints} endpoints, ${stats.schemas} schemas) ${stats.duration}ms`);
2049
- else console.log(`\x1b[32m✓\x1b[0m ${name} → ${output || "N/A"}`);
1927
+ if (stats) logger.item(`${name} → ${output} (${stats.endpoints} endpoints, ${stats.schemas} schemas) ${stats.duration}ms`, "green");
1928
+ else logger.item(`${name} → ${output || "N/A"}`, "green");
2050
1929
  } else {
2051
1930
  const { name, error } = result;
2052
1931
  if (isApicodegenError(error)) {
2053
- console.log(`\x1b[31m✗\x1b[0m ${name}`);
2054
- console.log(`\x1b[90m${formatError(error, true)}\x1b[0m`);
1932
+ logger.item(name, "red");
1933
+ logger.error(error, true);
2055
1934
  } else {
2056
1935
  const wrapped = wrapError(error, {
2057
- code: "E_GENERATION_FAILED",
1936
+ code: ErrorCodes.GENERATION_FAILED,
2058
1937
  message: `Failed to generate API "${name}"`
2059
1938
  });
2060
- console.log(`\x1b[31m✗\x1b[0m ${name}`);
2061
- console.log(`\x1b[90m${formatError(wrapped, true)}\x1b[0m`);
1939
+ logger.item(name, "red");
1940
+ logger.error(wrapped, true);
2062
1941
  }
2063
1942
  }
2064
- console.log(`\x1b[1m\x1b[36m${"─".repeat(50)}\x1b[0m`);
1943
+ logger.divider();
2065
1944
  const totalDuration = results.reduce((sum, r) => sum + (r.stats?.duration || 0), 0);
2066
1945
  const totalEndpoints = results.reduce((sum, r) => sum + (r.stats?.endpoints || 0), 0);
2067
1946
  const totalSchemas = results.reduce((sum, r) => sum + (r.stats?.schemas || 0), 0);
2068
- if (failCount === 0) console.log(`\x1b[32m✓\x1b[0m API Code Gen - Complete (\x1b[90m${successCount}/${options.length} succeeded\x1b[0m, ${totalEndpoints} endpoints, ${totalSchemas} schemas, ${totalDuration}ms\x1b[0m)`);
2069
- else console.log(`\x1b[33m⚠\x1b[0m API Code Gen - Complete (\x1b[90m${successCount} succeeded, ${failCount} failed\x1b[0m, ${totalEndpoints} endpoints, ${totalSchemas} schemas, ${totalDuration}ms\x1b[0m)`);
2070
- console.log(`\x1b[1m\x1b[36m${"─".repeat(50)}\x1b[0m`);
1947
+ logger.summary({
1948
+ succeeded: successCount,
1949
+ failed: failCount,
1950
+ endpoints: totalEndpoints,
1951
+ schemas: totalSchemas,
1952
+ duration: totalDuration
1953
+ });
1954
+ logger.divider();
2071
1955
  return {};
2072
1956
  }
2073
1957
  };
2074
1958
  }
2075
1959
  //#endregion
2076
- export { Adapter, Adaptors, ApicodegenError, ArraySchemaType, AxiosAdapter, Base, Colors, ErrorCodes, FetchAdapter, Generator, HttpMethods, MediaTypes, NonArraySchemaType, OpenAPIProvider, OpenAPIVersion, ParameterIn, Provider, SchemaFormatType, SchemaType, SuccessHttpStatusCode, apiCodeGenPlugin, codeGen, configToCLIOptions, createErrors, formatError, isApicodegenError, loadConfig, printError, toProviderOptions, wrapError };
1960
+ export { Adapter, Adaptors, ApicodegenError, ArraySchemaType, AxiosAdapter, Base, Colors, ErrorCodes, FetchAdapter, Generator, HttpMethods, MediaTypes, NonArraySchemaType, OpenAPIProvider, OpenAPIVersion, ParameterIn, Provider, SchemaFormatType, SchemaType, SuccessHttpStatusCode, apiCodeGenPlugin, codeGen, createErrors, formatError, isApicodegenError, loadConfig, logger, printError, toProviderOptions, wrapError };
2077
1961
 
2078
1962
  //# sourceMappingURL=index.mjs.map