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