@moccona/apicodegen 0.0.9 → 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/bin/cli.cjs +12 -12
- package/npm/index.cjs +609 -784
- package/npm/index.cjs.map +1 -1
- package/npm/index.d.cts +24 -5
- package/npm/index.d.cts.map +1 -1
- package/npm/index.d.mts +24 -5
- package/npm/index.d.mts.map +1 -1
- package/npm/index.mjs +609 -784
- package/npm/index.mjs.map +1 -1
- package/npm/vite/index.d.mts.map +1 -1
- package/npm/vite/index.mjs +371 -555
- package/npm/vite/index.mjs.map +1 -1
- package/package.json +1 -1
package/npm/index.cjs
CHANGED
|
@@ -440,6 +440,250 @@ var Provider = class {
|
|
|
440
440
|
}
|
|
441
441
|
};
|
|
442
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
|
|
443
687
|
//#region src/core/generator/index.ts
|
|
444
688
|
var Generator = class Generator {
|
|
445
689
|
/**
|
|
@@ -455,10 +699,19 @@ var Generator = class Generator {
|
|
|
455
699
|
return (0, typescript.createPrinter)().printFile(sourceFile);
|
|
456
700
|
}
|
|
457
701
|
static async write(code, filepath) {
|
|
702
|
+
const { mkdir } = await import("node:fs/promises");
|
|
703
|
+
const { dirname } = await import("node:path");
|
|
458
704
|
try {
|
|
705
|
+
await mkdir(dirname(filepath), { recursive: true });
|
|
459
706
|
await (0, node_fs_promises.writeFile)(filepath, code);
|
|
460
707
|
} catch (error) {
|
|
461
|
-
|
|
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
|
+
});
|
|
462
715
|
}
|
|
463
716
|
}
|
|
464
717
|
/**
|
|
@@ -938,20 +1191,6 @@ async function loadConfig(options = {}) {
|
|
|
938
1191
|
};
|
|
939
1192
|
}
|
|
940
1193
|
/**
|
|
941
|
-
* Create CLI options from config for commander
|
|
942
|
-
*/
|
|
943
|
-
function configToCLIOptions(config) {
|
|
944
|
-
const options = {};
|
|
945
|
-
if (config.spec) options.spec = config.spec;
|
|
946
|
-
if (config.output) options.output = config.output;
|
|
947
|
-
if (config.adaptor) options.adaptor = config.adaptor;
|
|
948
|
-
if (config.baseURL) options.baseURL = config.baseURL;
|
|
949
|
-
if (config.verbose) options.verbose = config.verbose;
|
|
950
|
-
if (config.watch) options.watch = config.watch;
|
|
951
|
-
if (config.importClientSource) options.importClientSource = config.importClientSource;
|
|
952
|
-
return options;
|
|
953
|
-
}
|
|
954
|
-
/**
|
|
955
1194
|
* Convert resolved config to provider options format
|
|
956
1195
|
*/
|
|
957
1196
|
function toProviderOptions(config) {
|
|
@@ -966,509 +1205,74 @@ function toProviderOptions(config) {
|
|
|
966
1205
|
};
|
|
967
1206
|
}
|
|
968
1207
|
//#endregion
|
|
969
|
-
//#region src/core/
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
}
|
|
1029
|
-
/**
|
|
1030
|
-
* Convert to JSON-serializable object
|
|
1031
|
-
*/
|
|
1032
|
-
toJSON() {
|
|
1033
|
-
return {
|
|
1034
|
-
name: this.name,
|
|
1035
|
-
code: this.code,
|
|
1036
|
-
message: this.message,
|
|
1037
|
-
location: this.location,
|
|
1038
|
-
line: this.line,
|
|
1039
|
-
column: this.column,
|
|
1040
|
-
path: this.path,
|
|
1041
|
-
suggestions: this.suggestions,
|
|
1042
|
-
cause: this.cause?.message
|
|
1043
|
-
};
|
|
1044
|
-
}
|
|
1045
|
-
};
|
|
1046
|
-
/**
|
|
1047
|
-
* ANSI color codes for terminal output
|
|
1048
|
-
*/
|
|
1049
|
-
const Colors = {
|
|
1050
|
-
reset: "\x1B[0m",
|
|
1051
|
-
bold: "\x1B[1m",
|
|
1052
|
-
red: "\x1B[31m",
|
|
1053
|
-
green: "\x1B[32m",
|
|
1054
|
-
yellow: "\x1B[33m",
|
|
1055
|
-
blue: "\x1B[34m",
|
|
1056
|
-
cyan: "\x1B[36m",
|
|
1057
|
-
gray: "\x1B[90m",
|
|
1058
|
-
brightRed: "\x1B[91m",
|
|
1059
|
-
brightGreen: "\x1B[92m"
|
|
1060
|
-
};
|
|
1061
|
-
/**
|
|
1062
|
-
* Format error for CLI output
|
|
1063
|
-
*/
|
|
1064
|
-
function formatError(error, verbose = false) {
|
|
1065
|
-
if (error instanceof ApicodegenError) return error.toString(verbose);
|
|
1066
|
-
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}` : ""}`;
|
|
1067
|
-
return `${Colors.red}${Colors.bold}Error${Colors.reset}: ${String(error)}`;
|
|
1068
|
-
}
|
|
1069
|
-
/**
|
|
1070
|
-
* Print error to console with formatting
|
|
1071
|
-
*/
|
|
1072
|
-
function printError(error, verbose = false, stream = process.stderr) {
|
|
1073
|
-
stream.write(formatError(error, verbose));
|
|
1074
|
-
stream.write("\n");
|
|
1075
|
-
}
|
|
1076
|
-
/**
|
|
1077
|
-
* Create error with common patterns
|
|
1078
|
-
*/
|
|
1079
|
-
const createErrors = {
|
|
1080
|
-
specNotFound(path, cause) {
|
|
1081
|
-
return new ApicodegenError({
|
|
1082
|
-
code: ErrorCodes.SPEC_NOT_FOUND,
|
|
1083
|
-
message: "OpenAPI spec file not found",
|
|
1084
|
-
location: path,
|
|
1085
|
-
suggestions: [
|
|
1086
|
-
"Check if the file exists using 'ls -la'",
|
|
1087
|
-
"Use --spec to provide the correct path",
|
|
1088
|
-
"For remote specs, ensure the URL is accessible"
|
|
1089
|
-
],
|
|
1090
|
-
cause
|
|
1091
|
-
});
|
|
1092
|
-
},
|
|
1093
|
-
specFetchFailed(url, statusCode, cause) {
|
|
1094
|
-
const message = statusCode ? `Failed to fetch OpenAPI spec (HTTP ${statusCode})` : "Failed to fetch OpenAPI spec from URL";
|
|
1095
|
-
return new ApicodegenError({
|
|
1096
|
-
code: ErrorCodes.SPEC_FETCH_FAILED,
|
|
1097
|
-
message,
|
|
1098
|
-
location: url,
|
|
1099
|
-
suggestions: [
|
|
1100
|
-
"Check if the URL is accessible in a browser",
|
|
1101
|
-
"Download the spec file locally and use the local path",
|
|
1102
|
-
"Verify CORS settings if fetching from a different origin"
|
|
1103
|
-
],
|
|
1104
|
-
cause
|
|
1105
|
-
});
|
|
1106
|
-
},
|
|
1107
|
-
specParseFailed(path, line, column, cause) {
|
|
1108
|
-
return new ApicodegenError({
|
|
1109
|
-
code: ErrorCodes.SPEC_PARSE_FAILED,
|
|
1110
|
-
message: "Failed to parse OpenAPI spec (invalid JSON or YAML)",
|
|
1111
|
-
location: path,
|
|
1112
|
-
line,
|
|
1113
|
-
column,
|
|
1114
|
-
suggestions: [
|
|
1115
|
-
"Validate JSON syntax using jsonlint.com",
|
|
1116
|
-
"For YAML specs, ensure proper indentation",
|
|
1117
|
-
"Check for trailing commas or unquoted special characters"
|
|
1118
|
-
],
|
|
1119
|
-
cause
|
|
1120
|
-
});
|
|
1121
|
-
},
|
|
1122
|
-
outputDirMissing(path, cause) {
|
|
1123
|
-
return new ApicodegenError({
|
|
1124
|
-
code: ErrorCodes.OUTPUT_DIR_MISSING,
|
|
1125
|
-
message: "Output directory does not exist",
|
|
1126
|
-
location: path,
|
|
1127
|
-
suggestions: ["Create the directory: mkdir -p $(dirname <output>)", "Check if the path is correct"],
|
|
1128
|
-
cause
|
|
1129
|
-
});
|
|
1130
|
-
},
|
|
1131
|
-
configInvalid(path, cause) {
|
|
1132
|
-
return new ApicodegenError({
|
|
1133
|
-
code: ErrorCodes.CONFIG_INVALID,
|
|
1134
|
-
message: "Invalid configuration file",
|
|
1135
|
-
location: path,
|
|
1136
|
-
suggestions: ["Validate JSON syntax in the config file", "Check for required fields (spec, output)"],
|
|
1137
|
-
cause
|
|
1138
|
-
});
|
|
1139
|
-
},
|
|
1140
|
-
validationFailed(path, details, cause) {
|
|
1141
|
-
return new ApicodegenError({
|
|
1142
|
-
code: ErrorCodes.VALIDATION_FAILED,
|
|
1143
|
-
message: "OpenAPI spec validation failed",
|
|
1144
|
-
location: path,
|
|
1145
|
-
path: details,
|
|
1146
|
-
suggestions: [
|
|
1147
|
-
"Check OpenAPI spec structure at the specified path",
|
|
1148
|
-
"Ensure all required fields are present",
|
|
1149
|
-
"Validate using swagger.io editor"
|
|
1150
|
-
],
|
|
1151
|
-
cause
|
|
1152
|
-
});
|
|
1153
|
-
},
|
|
1154
|
-
generationFailed(cause) {
|
|
1155
|
-
return new ApicodegenError({
|
|
1156
|
-
code: ErrorCodes.GENERATION_FAILED,
|
|
1157
|
-
message: "Code generation failed",
|
|
1158
|
-
suggestions: [
|
|
1159
|
-
"Check for unsupported OpenAPI features",
|
|
1160
|
-
"Ensure spec follows OpenAPI 2.0, 3.0, or 3.1 specification",
|
|
1161
|
-
"Use --verbose for more details"
|
|
1162
|
-
],
|
|
1163
|
-
cause
|
|
1164
|
-
});
|
|
1165
|
-
},
|
|
1166
|
-
typeCheckFailed(path, _errors, cause) {
|
|
1167
|
-
return new ApicodegenError({
|
|
1168
|
-
code: ErrorCodes.TYPE_CHECK_FAILED,
|
|
1169
|
-
message: "TypeScript type check failed",
|
|
1170
|
-
location: path,
|
|
1171
|
-
suggestions: [
|
|
1172
|
-
"Review type errors above",
|
|
1173
|
-
"Check for schema inconsistencies",
|
|
1174
|
-
"Update generated types or fix source schema"
|
|
1175
|
-
],
|
|
1176
|
-
cause
|
|
1177
|
-
});
|
|
1178
|
-
},
|
|
1179
|
-
missingRequiredField(field, context) {
|
|
1180
|
-
return new ApicodegenError({
|
|
1181
|
-
code: ErrorCodes.VALIDATION_FAILED,
|
|
1182
|
-
message: `Missing required field: ${field}`,
|
|
1183
|
-
path: context,
|
|
1184
|
-
suggestions: [`Add the '${field}' field to your configuration`]
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
};
|
|
1188
|
-
/**
|
|
1189
|
-
* Wrap unknown error in ApicodegenError if needed
|
|
1190
|
-
*/
|
|
1191
|
-
function wrapError(error, context) {
|
|
1192
|
-
if (error instanceof ApicodegenError) return error;
|
|
1193
|
-
if (error instanceof Error) return new ApicodegenError({
|
|
1194
|
-
code: context?.code || ErrorCodes.GENERATION_FAILED,
|
|
1195
|
-
message: context?.message || error.message,
|
|
1196
|
-
location: context?.location,
|
|
1197
|
-
suggestions: context?.suggestions,
|
|
1198
|
-
cause: error
|
|
1199
|
-
});
|
|
1200
|
-
return new ApicodegenError({
|
|
1201
|
-
code: context?.code || ErrorCodes.GENERATION_FAILED,
|
|
1202
|
-
message: String(error),
|
|
1203
|
-
suggestions: context?.suggestions
|
|
1204
|
-
});
|
|
1205
|
-
}
|
|
1206
|
-
/**
|
|
1207
|
-
* Check if error is an ApicodegenError
|
|
1208
|
-
*/
|
|
1209
|
-
function isApicodegenError(error) {
|
|
1210
|
-
return error instanceof ApicodegenError;
|
|
1211
|
-
}
|
|
1212
|
-
//#endregion
|
|
1213
|
-
//#region src/openapi/V2.ts
|
|
1214
|
-
var V2 = class {
|
|
1215
|
-
doc;
|
|
1216
|
-
constructor(doc) {
|
|
1217
|
-
this.doc = doc;
|
|
1218
|
-
}
|
|
1219
|
-
/**
|
|
1220
|
-
* Resolves a path $ref to the actual path object.
|
|
1221
|
-
*/
|
|
1222
|
-
resolvePathRef($ref) {
|
|
1223
|
-
const refName = Base.ref2name($ref, this.doc);
|
|
1224
|
-
return this.doc.paths?.[refName];
|
|
1225
|
-
}
|
|
1226
|
-
/**
|
|
1227
|
-
* Is array schema.
|
|
1228
|
-
*/
|
|
1229
|
-
isOpenAPIArraySchema(schema) {
|
|
1230
|
-
return typeof schema === "object" && schema.type === "array";
|
|
1231
|
-
}
|
|
1232
|
-
/**
|
|
1233
|
-
* OpenAPI schema to base schema.
|
|
1234
|
-
*/
|
|
1235
|
-
getSchemaByRef(schema, reserveRef = false, enums = [], upLevelSchemaKey = "") {
|
|
1236
|
-
let refName = "";
|
|
1237
|
-
if (Base.isRef(schema)) {
|
|
1238
|
-
refName = Base.upperCamelCase(Base.ref2name(schema.$ref));
|
|
1239
|
-
if (reserveRef) return { type: upLevelSchemaKey + refName };
|
|
1240
|
-
if (!this.doc.definitions) this.doc.definitions = {};
|
|
1241
|
-
schema = this.doc.definitions[Base.ref2name(schema.$ref, this.doc)];
|
|
1242
|
-
}
|
|
1243
|
-
return this.toBaseSchema(schema, enums, "", upLevelSchemaKey + refName);
|
|
1244
|
-
}
|
|
1245
|
-
/**
|
|
1246
|
-
* Transform all OpenAPI schema to Base Schema
|
|
1247
|
-
*/
|
|
1248
|
-
toBaseSchema(schema, enums = [], schemaKey = "", upLevelSchemaKey = "") {
|
|
1249
|
-
if (!schema) return { type: "unknown" };
|
|
1250
|
-
if (Base.isRef(schema)) return this.getSchemaByRef(schema, true);
|
|
1251
|
-
if (this.isOpenAPIArraySchema(schema)) {
|
|
1252
|
-
const { type, description, items, required } = schema;
|
|
1253
|
-
return {
|
|
1254
|
-
type,
|
|
1255
|
-
required: !!required,
|
|
1256
|
-
description,
|
|
1257
|
-
items: this.toBaseSchema(items, enums, schemaKey, upLevelSchemaKey)
|
|
1258
|
-
};
|
|
1259
|
-
} else {
|
|
1260
|
-
const { required = [], allOf, anyOf, description, enum: enum_, format, oneOf, properties = {} } = schema;
|
|
1261
|
-
let { type } = schema;
|
|
1262
|
-
if (enum_ && type !== "boolean") {
|
|
1263
|
-
const enumObject = {
|
|
1264
|
-
name: Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(schemaKey)),
|
|
1265
|
-
enum: [...new Set(enum_)]
|
|
1266
|
-
};
|
|
1267
|
-
const sameObject = Base.findSameSchema(enumObject, enums);
|
|
1268
|
-
if (!sameObject && Base.isValidEnumType(schema)) enums.push(enumObject);
|
|
1269
|
-
return {
|
|
1270
|
-
type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
|
|
1271
|
-
required,
|
|
1272
|
-
description
|
|
1273
|
-
};
|
|
1274
|
-
}
|
|
1275
|
-
if (type === void 0 && Object.keys(properties).length > 0) type = "object";
|
|
1276
|
-
return {
|
|
1277
|
-
type,
|
|
1278
|
-
required,
|
|
1279
|
-
description,
|
|
1280
|
-
enum: enum_,
|
|
1281
|
-
format,
|
|
1282
|
-
allOf: allOf?.map((s) => Base.isRef(s) ? {
|
|
1283
|
-
...s,
|
|
1284
|
-
ref: s.$ref,
|
|
1285
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1286
|
-
} : this.toBaseSchema(s, enums)),
|
|
1287
|
-
anyOf: anyOf?.map((s) => Base.isRef(s) ? {
|
|
1288
|
-
...s,
|
|
1289
|
-
ref: s.$ref,
|
|
1290
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1291
|
-
} : this.toBaseSchema(s, enums)),
|
|
1292
|
-
oneOf: oneOf?.map((s) => Base.isRef(s) ? {
|
|
1293
|
-
...s,
|
|
1294
|
-
ref: s.$ref,
|
|
1295
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1296
|
-
} : this.toBaseSchema(s, enums)),
|
|
1297
|
-
properties: Object.keys(properties).reduce((acc, p) => {
|
|
1298
|
-
const propSchema = properties[p];
|
|
1299
|
-
return {
|
|
1300
|
-
...acc,
|
|
1301
|
-
[p]: Base.isRef(propSchema) ? { type: Base.capitalize(Base.ref2name(propSchema.$ref, this.doc)) } : this.toBaseSchema(propSchema, enums, p, upLevelSchemaKey)
|
|
1302
|
-
};
|
|
1303
|
-
}, {})
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
/**
|
|
1308
|
-
* OpenAPI parameter to base parameter.
|
|
1309
|
-
*/
|
|
1310
|
-
getParameterByRef(parameter, enums = [], upLevelSchemaKey = "") {
|
|
1311
|
-
if (Base.isRef(parameter)) {
|
|
1312
|
-
const refName = Base.ref2name(parameter.$ref, this.doc);
|
|
1313
|
-
const resolved = this.doc.parameters?.[refName];
|
|
1314
|
-
if (!resolved) throw new Error(`Parameter reference not found: ${parameter.$ref}`);
|
|
1315
|
-
parameter = resolved;
|
|
1316
|
-
}
|
|
1317
|
-
const p = parameter;
|
|
1318
|
-
const { name, required, description, type, items, enum: enum_, properties, schema } = p;
|
|
1319
|
-
if (enum_) {
|
|
1320
|
-
const type = Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(name));
|
|
1321
|
-
const enumSchema = {
|
|
1322
|
-
name: type,
|
|
1323
|
-
enum: [...new Set(enum_)]
|
|
1324
|
-
};
|
|
1325
|
-
const sameEnum = Base.findSameSchema(enumSchema, enums);
|
|
1326
|
-
if (!sameEnum && Base.isValidEnumType({
|
|
1327
|
-
type,
|
|
1328
|
-
enum: enum_
|
|
1329
|
-
})) enums.push(enumSchema);
|
|
1330
|
-
return {
|
|
1331
|
-
name,
|
|
1332
|
-
required,
|
|
1333
|
-
description,
|
|
1334
|
-
in: p.in,
|
|
1335
|
-
schema: { type: sameEnum?.name ?? type }
|
|
1336
|
-
};
|
|
1337
|
-
}
|
|
1338
|
-
if (items) return {
|
|
1339
|
-
name,
|
|
1340
|
-
required,
|
|
1341
|
-
description,
|
|
1342
|
-
in: p.in,
|
|
1343
|
-
schema: {
|
|
1344
|
-
type,
|
|
1345
|
-
items
|
|
1346
|
-
}
|
|
1347
|
-
};
|
|
1348
|
-
if (schema && Base.isRef(schema)) return {
|
|
1349
|
-
name,
|
|
1350
|
-
required,
|
|
1351
|
-
description,
|
|
1352
|
-
in: p.in,
|
|
1353
|
-
schema: { type: Base.capitalize(Base.ref2name(schema.$ref)) }
|
|
1354
|
-
};
|
|
1355
|
-
return {
|
|
1356
|
-
name,
|
|
1357
|
-
required,
|
|
1358
|
-
description,
|
|
1359
|
-
in: p.in,
|
|
1360
|
-
schema: {
|
|
1361
|
-
type,
|
|
1362
|
-
properties
|
|
1363
|
-
}
|
|
1364
|
-
};
|
|
1365
|
-
}
|
|
1366
|
-
/**
|
|
1367
|
-
* OpenAPI schema to base response
|
|
1368
|
-
*/
|
|
1369
|
-
getResponseByRef(schema) {
|
|
1370
|
-
if (Base.isRef(schema)) schema = this.doc.responses[Base.ref2name(schema.$ref, this.doc)];
|
|
1371
|
-
const { schema: responseSchema } = schema;
|
|
1372
|
-
return [{
|
|
1373
|
-
type: "application/json",
|
|
1374
|
-
schema: responseSchema && this.getSchemaByRef(responseSchema, true)
|
|
1375
|
-
}];
|
|
1376
|
-
}
|
|
1377
|
-
init() {
|
|
1378
|
-
const { definitions = {}, responses = {}, paths = {} } = this.doc;
|
|
1379
|
-
const enums = [];
|
|
1380
|
-
const definitions_ = Object.keys(definitions).reduce((acc, key) => {
|
|
1381
|
-
const schema = definitions[key];
|
|
1382
|
-
return {
|
|
1383
|
-
...acc,
|
|
1384
|
-
[key]: this.getSchemaByRef(schema, false, enums, key)
|
|
1385
|
-
};
|
|
1386
|
-
}, {});
|
|
1387
|
-
const responses_ = Object.keys(responses).reduce((acc, key) => {
|
|
1388
|
-
const response = responses[key];
|
|
1389
|
-
return {
|
|
1390
|
-
...acc,
|
|
1391
|
-
[key]: this.getResponseByRef(response)
|
|
1392
|
-
};
|
|
1393
|
-
}, {});
|
|
1394
|
-
const apis = Object.keys(paths).reduce((acc, path) => {
|
|
1395
|
-
let pathObject = paths[path] ?? {};
|
|
1396
|
-
if (pathObject.$ref) {
|
|
1397
|
-
const resolved = this.resolvePathRef(pathObject.$ref);
|
|
1398
|
-
if (resolved) pathObject = resolved;
|
|
1399
|
-
}
|
|
1400
|
-
const { parameters = [] } = pathObject;
|
|
1401
|
-
const methodApis = [];
|
|
1402
|
-
Object.values(HttpMethods).forEach((method) => {
|
|
1403
|
-
const methodObject = pathObject[method];
|
|
1404
|
-
if (methodObject) {
|
|
1405
|
-
const { deprecated, operationId, summary: summary_, description: description_, responses = {} } = methodObject;
|
|
1406
|
-
const { parameters: parameters_ = [] } = methodObject;
|
|
1407
|
-
const baseParameters = [...parameters, ...parameters_].map((parameter) => this.getParameterByRef(parameter, enums));
|
|
1408
|
-
const uniqueParameterName = [...new Set(baseParameters.map((p) => p.name))];
|
|
1409
|
-
if (Object.keys(responses).length === 0) Object.assign(responses, { 200: { description: "Successful response" } });
|
|
1410
|
-
const inBody = baseParameters.filter((p) => p.in === "body" || p.in === "formData");
|
|
1411
|
-
const notInBody = baseParameters.filter((p) => p.in !== "body" && p.in !== "formData");
|
|
1412
|
-
const httpCodes = Object.keys(responses);
|
|
1413
|
-
for (const code of httpCodes) if (code in responses) {
|
|
1414
|
-
const response = responses[code];
|
|
1415
|
-
const responseSchema = this.getResponseByRef(response);
|
|
1416
|
-
const inBodyOnlyHasBody = inBody && inBody.length === 1 && inBody[0].in === "body" && inBody[0].name === "body";
|
|
1417
|
-
methodApis.push({
|
|
1418
|
-
method,
|
|
1419
|
-
operationId,
|
|
1420
|
-
summary: summary_,
|
|
1421
|
-
deprecated,
|
|
1422
|
-
description: description_,
|
|
1423
|
-
parameters: uniqueParameterName.map((name) => notInBody.find((p) => p.name === name)).filter(Boolean),
|
|
1424
|
-
responses: responseSchema,
|
|
1425
|
-
requestBody: inBody.length > 0 ? inBodyOnlyHasBody ? [{
|
|
1426
|
-
type: "application/json",
|
|
1427
|
-
schema: inBody[0].schema
|
|
1428
|
-
}] : [{
|
|
1429
|
-
type: "application/json",
|
|
1430
|
-
schema: {
|
|
1431
|
-
type: "object",
|
|
1432
|
-
properties: inBody.reduce((a, p) => {
|
|
1433
|
-
return {
|
|
1434
|
-
...a,
|
|
1435
|
-
[p.name]: {
|
|
1436
|
-
type: p.schema?.type ?? "unknown",
|
|
1437
|
-
required: p.schema?.required,
|
|
1438
|
-
items: p.schema?.items,
|
|
1439
|
-
description: p.schema?.description
|
|
1440
|
-
}
|
|
1441
|
-
};
|
|
1442
|
-
}, {})
|
|
1443
|
-
}
|
|
1444
|
-
}] : void 0
|
|
1445
|
-
});
|
|
1446
|
-
break;
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
});
|
|
1450
|
-
return {
|
|
1451
|
-
...acc,
|
|
1452
|
-
[path]: methodApis
|
|
1453
|
-
};
|
|
1454
|
-
}, {});
|
|
1455
|
-
return {
|
|
1456
|
-
enums: Base.uniqueEnums(enums),
|
|
1457
|
-
schemas: definitions_,
|
|
1458
|
-
responses: responses_,
|
|
1459
|
-
parameters: {},
|
|
1460
|
-
requestBodies: {},
|
|
1461
|
-
apis
|
|
1462
|
-
};
|
|
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}`);
|
|
1463
1267
|
}
|
|
1464
1268
|
};
|
|
1465
1269
|
//#endregion
|
|
1466
|
-
//#region src/openapi/
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
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 {
|
|
1472
1276
|
/**
|
|
1473
1277
|
* Resolves a path $ref to the actual path object.
|
|
1474
1278
|
*/
|
|
@@ -1480,28 +1284,37 @@ var V3 = class {
|
|
|
1480
1284
|
* Is array schema.
|
|
1481
1285
|
*/
|
|
1482
1286
|
isOpenAPIArraySchema(schema) {
|
|
1483
|
-
return typeof schema === "object" && schema.type === "array";
|
|
1287
|
+
return typeof schema === "object" && schema !== null && schema.type === "array";
|
|
1484
1288
|
}
|
|
1485
1289
|
/**
|
|
1486
|
-
* 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' }`).
|
|
1487
1292
|
*/
|
|
1488
1293
|
getSchemaByRef(schema, reserveRef = false, enums = [], upLevelSchemaKey = "") {
|
|
1489
1294
|
let refName = "";
|
|
1490
1295
|
if (Base.isRef(schema)) {
|
|
1491
|
-
refName =
|
|
1296
|
+
refName = this.formatRefName(Base.ref2name(schema.$ref));
|
|
1492
1297
|
if (reserveRef) return { type: upLevelSchemaKey + refName };
|
|
1493
|
-
const resolvedSchema = this.
|
|
1298
|
+
const resolvedSchema = this.getSchemaContainer()?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1494
1299
|
if (!resolvedSchema) return { type: "unknown" };
|
|
1495
1300
|
schema = resolvedSchema;
|
|
1496
1301
|
}
|
|
1497
1302
|
return this.toBaseSchema(schema, enums, "", upLevelSchemaKey + refName);
|
|
1498
1303
|
}
|
|
1499
1304
|
/**
|
|
1500
|
-
*
|
|
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`).
|
|
1501
1314
|
*/
|
|
1502
1315
|
getParameterByRef(schema, enums = [], upLevelSchemaKey = "") {
|
|
1503
1316
|
if (Base.isRef(schema)) {
|
|
1504
|
-
const resolvedSchema = this.
|
|
1317
|
+
const resolvedSchema = this.getParameterContainer()?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1505
1318
|
if (!resolvedSchema) return {
|
|
1506
1319
|
name: "unknown",
|
|
1507
1320
|
in: "query"
|
|
@@ -1532,37 +1345,38 @@ var V3 = class {
|
|
|
1532
1345
|
description,
|
|
1533
1346
|
deprecated,
|
|
1534
1347
|
in: schema.in,
|
|
1535
|
-
schema: schema.schema
|
|
1348
|
+
schema: schema.schema ? this.getSchemaByRef(schema.schema, false, enums, upLevelSchemaKey + Base.capitalize(name)) : void 0
|
|
1536
1349
|
};
|
|
1537
1350
|
}
|
|
1538
1351
|
/**
|
|
1539
|
-
* OpenAPI schema to base response
|
|
1352
|
+
* OpenAPI schema to base response. Override in V2 (its response shape
|
|
1353
|
+
* lacks the `content` wrapper).
|
|
1540
1354
|
*/
|
|
1541
1355
|
getResponseByRef(schema) {
|
|
1542
1356
|
if (Base.isRef(schema)) {
|
|
1543
|
-
const resolvedSchema = this.
|
|
1357
|
+
const resolvedSchema = this.getResponseContainer()?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1544
1358
|
if (!resolvedSchema) return [];
|
|
1545
1359
|
schema = resolvedSchema;
|
|
1546
1360
|
}
|
|
1547
1361
|
const { content = {} } = schema;
|
|
1548
1362
|
return Object.keys(content).map((c) => ({
|
|
1549
1363
|
type: c,
|
|
1550
|
-
schema: content[c].schema
|
|
1364
|
+
schema: content[c].schema ? this.getSchemaByRef(content[c].schema, true) : void 0
|
|
1551
1365
|
}));
|
|
1552
1366
|
}
|
|
1553
1367
|
/**
|
|
1554
|
-
* OpenAPI schema to requestBody.
|
|
1368
|
+
* OpenAPI schema to requestBody. V2 has no requestBody concept.
|
|
1555
1369
|
*/
|
|
1556
1370
|
getRequestBodyByRef(schema, enums = []) {
|
|
1557
1371
|
if (Base.isRef(schema)) {
|
|
1558
|
-
const resolvedSchema = this.
|
|
1372
|
+
const resolvedSchema = this.getRequestBodyContainer()?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1559
1373
|
if (!resolvedSchema) return [];
|
|
1560
1374
|
schema = resolvedSchema;
|
|
1561
1375
|
}
|
|
1562
1376
|
const { content = {} } = schema;
|
|
1563
1377
|
return Object.keys(content).map((c) => ({
|
|
1564
1378
|
type: c,
|
|
1565
|
-
schema: content[c].schema
|
|
1379
|
+
schema: content[c].schema ? this.getSchemaByRef(content[c].schema, false, enums) : void 0
|
|
1566
1380
|
}));
|
|
1567
1381
|
}
|
|
1568
1382
|
/**
|
|
@@ -1573,92 +1387,98 @@ var V3 = class {
|
|
|
1573
1387
|
if (Base.isRef(schema)) return this.getSchemaByRef(schema, true);
|
|
1574
1388
|
if (this.isOpenAPIArraySchema(schema)) {
|
|
1575
1389
|
const { type, description, items, required } = schema;
|
|
1390
|
+
const itemsSchema = items ? this.toBaseSchema(items, enums, schemaKey, upLevelSchemaKey) : { type: "unknown" };
|
|
1576
1391
|
return {
|
|
1577
1392
|
type,
|
|
1578
1393
|
required: !!required,
|
|
1579
1394
|
description,
|
|
1580
|
-
items:
|
|
1395
|
+
items: itemsSchema
|
|
1581
1396
|
};
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
return {
|
|
1593
|
-
type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
|
|
1594
|
-
required,
|
|
1595
|
-
description,
|
|
1596
|
-
deprecated
|
|
1597
|
-
};
|
|
1598
|
-
}
|
|
1599
|
-
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);
|
|
1600
1407
|
return {
|
|
1601
|
-
type,
|
|
1408
|
+
type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
|
|
1602
1409
|
required,
|
|
1603
1410
|
description,
|
|
1604
|
-
deprecated
|
|
1605
|
-
enum: enum_,
|
|
1606
|
-
format,
|
|
1607
|
-
allOf: allOf?.map((s) => Base.isRef(s) ? {
|
|
1608
|
-
...s,
|
|
1609
|
-
ref: s.$ref,
|
|
1610
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1611
|
-
} : this.toBaseSchema(s, enums)),
|
|
1612
|
-
anyOf: anyOf?.map((s) => Base.isRef(s) ? {
|
|
1613
|
-
...s,
|
|
1614
|
-
ref: s.$ref,
|
|
1615
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1616
|
-
} : this.toBaseSchema(s, enums)),
|
|
1617
|
-
oneOf: oneOf?.map((s) => Base.isRef(s) ? {
|
|
1618
|
-
...s,
|
|
1619
|
-
ref: s.$ref,
|
|
1620
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1621
|
-
} : this.toBaseSchema(s, enums)),
|
|
1622
|
-
properties: Object.keys(properties).reduce((acc, p) => {
|
|
1623
|
-
const propSchema = properties[p];
|
|
1624
|
-
return {
|
|
1625
|
-
...acc,
|
|
1626
|
-
[p]: Base.isRef(propSchema) ? {
|
|
1627
|
-
type: Base.capitalize(Base.ref2name(propSchema.$ref, this.doc)),
|
|
1628
|
-
isRef: true
|
|
1629
|
-
} : this.toBaseSchema(propSchema, enums, p, upLevelSchemaKey)
|
|
1630
|
-
};
|
|
1631
|
-
}, {})
|
|
1411
|
+
deprecated
|
|
1632
1412
|
};
|
|
1633
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
|
+
};
|
|
1634
1448
|
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Run the parsing pipeline and return a ProviderInitResult.
|
|
1451
|
+
*/
|
|
1635
1452
|
init() {
|
|
1636
|
-
const {
|
|
1453
|
+
const { paths = {} } = this.doc;
|
|
1637
1454
|
const enums = [];
|
|
1638
|
-
const
|
|
1639
|
-
const
|
|
1640
|
-
|
|
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];
|
|
1641
1461
|
return {
|
|
1642
1462
|
...acc,
|
|
1643
1463
|
[key]: this.getSchemaByRef(schema, false, enums, key)
|
|
1644
1464
|
};
|
|
1645
1465
|
}, {});
|
|
1646
|
-
const parameters_ = Object.keys(
|
|
1647
|
-
const parameter =
|
|
1466
|
+
const parameters_ = Object.keys(parameterContainer).reduce((acc, key) => {
|
|
1467
|
+
const parameter = parameterContainer[key];
|
|
1648
1468
|
return {
|
|
1649
1469
|
...acc,
|
|
1650
1470
|
[key]: this.getParameterByRef(parameter, enums, key)
|
|
1651
1471
|
};
|
|
1652
1472
|
}, {});
|
|
1653
|
-
const responses_ = Object.keys(
|
|
1654
|
-
const response =
|
|
1473
|
+
const responses_ = Object.keys(responseContainer).reduce((acc, key) => {
|
|
1474
|
+
const response = responseContainer[key];
|
|
1655
1475
|
return {
|
|
1656
1476
|
...acc,
|
|
1657
1477
|
[key]: this.getResponseByRef(response)
|
|
1658
1478
|
};
|
|
1659
1479
|
}, {});
|
|
1660
|
-
const requestBodies_ = Object.keys(
|
|
1661
|
-
const requestBody =
|
|
1480
|
+
const requestBodies_ = Object.keys(requestBodyContainer).reduce((acc, key) => {
|
|
1481
|
+
const requestBody = requestBodyContainer[key];
|
|
1662
1482
|
return {
|
|
1663
1483
|
...acc,
|
|
1664
1484
|
[key]: this.getRequestBodyByRef(requestBody, enums)
|
|
@@ -1675,15 +1495,16 @@ var V3 = class {
|
|
|
1675
1495
|
Object.values(HttpMethods).forEach((method) => {
|
|
1676
1496
|
const methodObject = pathObject[method];
|
|
1677
1497
|
if (methodObject) {
|
|
1678
|
-
const { deprecated, operationId, responses
|
|
1498
|
+
const { deprecated, operationId, responses, summary: summary_, description: description_, requestBody = { content: {} } } = methodObject;
|
|
1499
|
+
const responsesClone = responses ? { ...responses } : {};
|
|
1679
1500
|
const { parameters: parameters_ = [] } = methodObject;
|
|
1680
1501
|
const baseParameters = [...parameters, ...parameters_].map((parameter) => this.getParameterByRef(parameter, enums));
|
|
1681
1502
|
const baseRequestBody = this.getRequestBodyByRef(requestBody, enums);
|
|
1682
1503
|
const uniqueParameterName = [...new Set(baseParameters.map((p) => p.name))];
|
|
1683
|
-
if (Object.keys(
|
|
1684
|
-
const httpCodes = Object.keys(
|
|
1685
|
-
for (const code of httpCodes) if (code in
|
|
1686
|
-
const response =
|
|
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];
|
|
1687
1508
|
const responseSchema = this.getResponseByRef(response);
|
|
1688
1509
|
methodApis.push({
|
|
1689
1510
|
method,
|
|
@@ -1715,244 +1536,246 @@ var V3 = class {
|
|
|
1715
1536
|
}
|
|
1716
1537
|
};
|
|
1717
1538
|
//#endregion
|
|
1718
|
-
//#region src/openapi/
|
|
1719
|
-
var
|
|
1539
|
+
//#region src/openapi/V2.ts
|
|
1540
|
+
var V2 = class extends VersionedProvider {
|
|
1720
1541
|
doc;
|
|
1542
|
+
version = "v2";
|
|
1721
1543
|
constructor(doc) {
|
|
1544
|
+
super();
|
|
1722
1545
|
this.doc = doc;
|
|
1723
1546
|
}
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
*/
|
|
1727
|
-
resolvePathRef($ref) {
|
|
1728
|
-
const refName = Base.ref2name($ref, this.doc);
|
|
1729
|
-
return this.doc.paths?.[refName];
|
|
1547
|
+
getSchemaContainer() {
|
|
1548
|
+
return this.doc.definitions;
|
|
1730
1549
|
}
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
*/
|
|
1734
|
-
isOpenAPIArraySchema(schema) {
|
|
1735
|
-
return typeof schema === "object" && schema.type === "array";
|
|
1550
|
+
getParameterContainer() {
|
|
1551
|
+
return this.doc.parameters;
|
|
1736
1552
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
*/
|
|
1740
|
-
getSchemaByRef(schema, reserveRef = false, enums = [], upLevelSchemaKey = "") {
|
|
1741
|
-
let refName = "";
|
|
1742
|
-
if (Base.isRef(schema)) {
|
|
1743
|
-
refName = Base.upperCamelCase(Base.ref2name(schema.$ref));
|
|
1744
|
-
if (reserveRef) return { type: upLevelSchemaKey + refName };
|
|
1745
|
-
if (!this.doc.components) this.doc.components = { schemas: {} };
|
|
1746
|
-
const resolvedSchema = this.doc.components.schemas?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1747
|
-
if (!resolvedSchema) throw new Error(`Schema reference not found: ${refName}`);
|
|
1748
|
-
schema = resolvedSchema;
|
|
1749
|
-
}
|
|
1750
|
-
return this.toBaseSchema(schema, enums, "", upLevelSchemaKey + refName);
|
|
1553
|
+
getResponseContainer() {
|
|
1554
|
+
return this.doc.responses;
|
|
1751
1555
|
}
|
|
1556
|
+
getRequestBodyContainer() {}
|
|
1752
1557
|
/**
|
|
1753
|
-
*
|
|
1558
|
+
* V2 parameter shape differs from V3: top-level `items`/`properties`/`enum`
|
|
1559
|
+
* rather than nested under `schema`. Override accordingly.
|
|
1754
1560
|
*/
|
|
1755
|
-
getParameterByRef(
|
|
1756
|
-
if (Base.isRef(
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
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));
|
|
1760
1572
|
const enumSchema = {
|
|
1761
|
-
name:
|
|
1762
|
-
enum: [...new Set(
|
|
1573
|
+
name: enumType,
|
|
1574
|
+
enum: [...new Set(enum_)]
|
|
1763
1575
|
};
|
|
1764
1576
|
const sameEnum = Base.findSameSchema(enumSchema, enums);
|
|
1765
|
-
if (!sameEnum && Base.isValidEnumType(
|
|
1577
|
+
if (!sameEnum && Base.isValidEnumType({
|
|
1578
|
+
type: enumType,
|
|
1579
|
+
enum: enum_
|
|
1580
|
+
})) enums.push(enumSchema);
|
|
1766
1581
|
return {
|
|
1767
1582
|
name,
|
|
1768
1583
|
required,
|
|
1769
1584
|
description,
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
schema: { type: sameEnum?.name ?? type }
|
|
1585
|
+
in: p.in,
|
|
1586
|
+
schema: { type: sameEnum?.name ?? enumType }
|
|
1773
1587
|
};
|
|
1774
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
|
+
};
|
|
1775
1606
|
return {
|
|
1776
1607
|
name,
|
|
1777
1608
|
required,
|
|
1778
1609
|
description,
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1610
|
+
in: p.in,
|
|
1611
|
+
schema: {
|
|
1612
|
+
type,
|
|
1613
|
+
properties
|
|
1614
|
+
}
|
|
1782
1615
|
};
|
|
1783
1616
|
}
|
|
1784
1617
|
/**
|
|
1785
|
-
*
|
|
1618
|
+
* V2 response shape: a `schema` field directly, not `content`. V2 always
|
|
1619
|
+
* emits JSON.
|
|
1786
1620
|
*/
|
|
1787
1621
|
getResponseByRef(schema) {
|
|
1788
|
-
if (Base.isRef(schema)) schema = this.doc.
|
|
1789
|
-
const {
|
|
1790
|
-
return
|
|
1791
|
-
type:
|
|
1792
|
-
schema:
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
/**
|
|
1796
|
-
* OpenAPI schema to requestBody.
|
|
1797
|
-
*/
|
|
1798
|
-
getRequestBodyByRef(schema, enums = []) {
|
|
1799
|
-
if (Base.isRef(schema)) schema = this.doc.components?.requestBodies?.[Base.ref2name(schema.$ref, this.doc)];
|
|
1800
|
-
const { content = {} } = schema;
|
|
1801
|
-
return Object.keys(content).map((c) => ({
|
|
1802
|
-
type: c,
|
|
1803
|
-
schema: content[c].schema && this.getSchemaByRef(content[c].schema, true, enums)
|
|
1804
|
-
}));
|
|
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
|
+
}];
|
|
1805
1628
|
}
|
|
1806
1629
|
/**
|
|
1807
|
-
*
|
|
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.
|
|
1808
1634
|
*/
|
|
1809
|
-
toBaseSchema(schema, enums = [], schemaKey = "", upLevelSchemaKey = "") {
|
|
1810
|
-
if (!schema) return { type: "unknown" };
|
|
1811
|
-
if (Base.isRef(schema)) return this.getSchemaByRef(schema, true);
|
|
1812
|
-
if (this.isOpenAPIArraySchema(schema)) {
|
|
1813
|
-
const { type, description, items, required } = schema;
|
|
1814
|
-
return {
|
|
1815
|
-
type,
|
|
1816
|
-
required: !!required,
|
|
1817
|
-
description,
|
|
1818
|
-
items: this.toBaseSchema(items, enums, schemaKey, upLevelSchemaKey)
|
|
1819
|
-
};
|
|
1820
|
-
} else {
|
|
1821
|
-
const { required = [], allOf, anyOf, description, deprecated, enum: enum_, format, oneOf, properties = {} } = schema;
|
|
1822
|
-
let { type } = schema;
|
|
1823
|
-
if (enum_ && type !== "boolean") {
|
|
1824
|
-
const enumObject = {
|
|
1825
|
-
name: Base.upperCamelCase(Base.normalize(upLevelSchemaKey)) + Base.upperCamelCase(Base.normalize(schemaKey)),
|
|
1826
|
-
enum: [...new Set(enum_)]
|
|
1827
|
-
};
|
|
1828
|
-
const sameObject = Base.findSameSchema(enumObject, enums);
|
|
1829
|
-
if (!sameObject && Base.isValidEnumType(schema)) enums.push(enumObject);
|
|
1830
|
-
return {
|
|
1831
|
-
type: sameObject ? sameObject.name : Base.isBooleanEnum(schema) ? "boolean" : enumObject.name,
|
|
1832
|
-
required,
|
|
1833
|
-
description,
|
|
1834
|
-
deprecated
|
|
1835
|
-
};
|
|
1836
|
-
}
|
|
1837
|
-
if (type === void 0 && Object.keys(properties).length > 0) type = "object";
|
|
1838
|
-
return {
|
|
1839
|
-
type,
|
|
1840
|
-
required,
|
|
1841
|
-
description,
|
|
1842
|
-
deprecated,
|
|
1843
|
-
enum: enum_,
|
|
1844
|
-
format,
|
|
1845
|
-
allOf: allOf?.map((s) => Base.isRef(s) ? {
|
|
1846
|
-
...s,
|
|
1847
|
-
ref: s.$ref,
|
|
1848
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1849
|
-
} : this.toBaseSchema(s, enums)),
|
|
1850
|
-
anyOf: anyOf?.map((s) => Base.isRef(s) ? {
|
|
1851
|
-
...s,
|
|
1852
|
-
ref: s.$ref,
|
|
1853
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1854
|
-
} : this.toBaseSchema(s, enums)),
|
|
1855
|
-
oneOf: oneOf?.map((s) => Base.isRef(s) ? {
|
|
1856
|
-
...s,
|
|
1857
|
-
ref: s.$ref,
|
|
1858
|
-
type: Base.capitalize(Base.ref2name(s.$ref, this.doc))
|
|
1859
|
-
} : this.toBaseSchema(s, enums)),
|
|
1860
|
-
properties: Object.keys(properties).reduce((acc, p) => {
|
|
1861
|
-
const propSchema = properties[p];
|
|
1862
|
-
return {
|
|
1863
|
-
...acc,
|
|
1864
|
-
[p]: Base.isRef(propSchema) ? {
|
|
1865
|
-
type: Base.capitalize(Base.ref2name(propSchema.$ref, this.doc)),
|
|
1866
|
-
isRef: true
|
|
1867
|
-
} : this.toBaseSchema(propSchema, enums, p, upLevelSchemaKey)
|
|
1868
|
-
};
|
|
1869
|
-
}, {})
|
|
1870
|
-
};
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
1635
|
init() {
|
|
1874
|
-
const {
|
|
1636
|
+
const { paths = {} } = this.doc;
|
|
1875
1637
|
const enums = [];
|
|
1876
|
-
const
|
|
1877
|
-
const
|
|
1878
|
-
|
|
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];
|
|
1879
1643
|
return {
|
|
1880
1644
|
...acc,
|
|
1881
1645
|
[key]: this.getSchemaByRef(schema, false, enums, key)
|
|
1882
1646
|
};
|
|
1883
1647
|
}, {});
|
|
1884
|
-
const parameters_ = Object.keys(
|
|
1885
|
-
const parameter =
|
|
1648
|
+
const parameters_ = Object.keys(parameterContainer).reduce((acc, key) => {
|
|
1649
|
+
const parameter = parameterContainer[key];
|
|
1886
1650
|
return {
|
|
1887
1651
|
...acc,
|
|
1888
1652
|
[key]: this.getParameterByRef(parameter, enums, key)
|
|
1889
1653
|
};
|
|
1890
1654
|
}, {});
|
|
1891
|
-
const responses_ = Object.keys(
|
|
1892
|
-
const response =
|
|
1655
|
+
const responses_ = Object.keys(responseContainer).reduce((acc, key) => {
|
|
1656
|
+
const response = responseContainer[key];
|
|
1893
1657
|
return {
|
|
1894
1658
|
...acc,
|
|
1895
1659
|
[key]: this.getResponseByRef(response)
|
|
1896
1660
|
};
|
|
1897
1661
|
}, {});
|
|
1898
|
-
const
|
|
1899
|
-
|
|
1900
|
-
return {
|
|
1901
|
-
...acc,
|
|
1902
|
-
[key]: this.getRequestBodyByRef(requestBody, enums)
|
|
1903
|
-
};
|
|
1904
|
-
}, {});
|
|
1905
|
-
const apis = Object.keys(paths).reduce((acc, path) => {
|
|
1662
|
+
const apis = {};
|
|
1663
|
+
for (const path of Object.keys(paths)) {
|
|
1906
1664
|
let pathObject = paths[path] ?? {};
|
|
1907
1665
|
if (pathObject.$ref) {
|
|
1908
1666
|
const resolved = this.resolvePathRef(pathObject.$ref);
|
|
1909
1667
|
if (resolved) pathObject = resolved;
|
|
1910
1668
|
}
|
|
1911
|
-
const { parameters = []
|
|
1669
|
+
const { parameters = [] } = pathObject;
|
|
1912
1670
|
const methodApis = [];
|
|
1913
1671
|
Object.values(HttpMethods).forEach((method) => {
|
|
1914
1672
|
const methodObject = pathObject[method];
|
|
1915
|
-
if (methodObject)
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
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;
|
|
1938
1717
|
}
|
|
1939
1718
|
});
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
[path]: methodApis
|
|
1943
|
-
};
|
|
1944
|
-
}, {});
|
|
1719
|
+
apis[path] = methodApis;
|
|
1720
|
+
}
|
|
1945
1721
|
return {
|
|
1946
1722
|
enums: Base.uniqueEnums(enums),
|
|
1947
1723
|
schemas: schemas_,
|
|
1948
1724
|
responses: responses_,
|
|
1949
1725
|
parameters: parameters_,
|
|
1950
|
-
requestBodies:
|
|
1726
|
+
requestBodies: {},
|
|
1951
1727
|
apis
|
|
1952
1728
|
};
|
|
1953
1729
|
}
|
|
1954
1730
|
};
|
|
1955
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
|
|
1956
1779
|
//#region src/openapi/index.ts
|
|
1957
1780
|
const logger$1 = (0, _moccona_logger.createScopedLogger)("OpenAPI");
|
|
1958
1781
|
let OpenAPIVersion = /* @__PURE__ */ function(OpenAPIVersion) {
|
|
@@ -2018,7 +1841,7 @@ async function codeGen(initOptions) {
|
|
|
2018
1841
|
//#endregion
|
|
2019
1842
|
//#region src/vite-plugin/index.ts
|
|
2020
1843
|
const PLUGIN_NAME = "api-code-gen";
|
|
2021
|
-
const
|
|
1844
|
+
const pluginLogger = (0, _moccona_logger.createScopedLogger)("api-code-gen");
|
|
2022
1845
|
/**
|
|
2023
1846
|
* Run TypeScript type checking on generated file
|
|
2024
1847
|
*/
|
|
@@ -2047,7 +1870,7 @@ async function validateSpecPath(specPath) {
|
|
|
2047
1870
|
async function generateForOption(option) {
|
|
2048
1871
|
const { name, typeCheck = true, verbose, ...restOptions } = option;
|
|
2049
1872
|
try {
|
|
2050
|
-
|
|
1873
|
+
logger.info(`Generating ${name}...`);
|
|
2051
1874
|
const config = await loadConfig({
|
|
2052
1875
|
name,
|
|
2053
1876
|
cliOptions: {
|
|
@@ -2071,8 +1894,8 @@ async function generateForOption(option) {
|
|
|
2071
1894
|
if (typeCheck && config.output) {
|
|
2072
1895
|
const typeErrors = await runTypeCheck(config.output);
|
|
2073
1896
|
if (typeErrors.length > 0) {
|
|
2074
|
-
|
|
2075
|
-
if (verbose) for (const error of typeErrors)
|
|
1897
|
+
pluginLogger.warn(`Type check failed for ${config.output}`);
|
|
1898
|
+
if (verbose) for (const error of typeErrors) pluginLogger.warn(` ${error}`);
|
|
2076
1899
|
}
|
|
2077
1900
|
}
|
|
2078
1901
|
return {
|
|
@@ -2113,45 +1936,47 @@ async function generateForOption(option) {
|
|
|
2113
1936
|
*/
|
|
2114
1937
|
function apiCodeGenPlugin(options) {
|
|
2115
1938
|
if (!Array.isArray(options) || options.length === 0) {
|
|
2116
|
-
|
|
1939
|
+
pluginLogger.warn("No API configurations provided to apiCodeGenPlugin");
|
|
2117
1940
|
return { name: PLUGIN_NAME };
|
|
2118
1941
|
}
|
|
2119
1942
|
return {
|
|
2120
1943
|
name: PLUGIN_NAME,
|
|
2121
1944
|
async config(_config, env) {
|
|
2122
|
-
|
|
2123
|
-
console.log(`\x1b[1m\x1b[36mAPI Code Gen\x1b[0m`);
|
|
2124
|
-
console.log(`\x1b[90mMode:\x1b[0m ${env?.command || "unknown"}`);
|
|
2125
|
-
console.log(`\x1b[1m\x1b[36m${"─".repeat(50)}\x1b[0m`);
|
|
1945
|
+
logger.heading("API Code Gen", env?.command);
|
|
2126
1946
|
const results = await Promise.all(options.map(generateForOption));
|
|
2127
1947
|
const successCount = results.filter((r) => r.success).length;
|
|
2128
1948
|
const failCount = options.length - successCount;
|
|
2129
|
-
|
|
1949
|
+
logger.divider();
|
|
2130
1950
|
for (const result of results) if (result.success) {
|
|
2131
1951
|
const { name, output, stats } = result;
|
|
2132
|
-
if (stats)
|
|
2133
|
-
else
|
|
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");
|
|
2134
1954
|
} else {
|
|
2135
1955
|
const { name, error } = result;
|
|
2136
1956
|
if (isApicodegenError(error)) {
|
|
2137
|
-
|
|
2138
|
-
|
|
1957
|
+
logger.item(name, "red");
|
|
1958
|
+
logger.error(error, true);
|
|
2139
1959
|
} else {
|
|
2140
1960
|
const wrapped = wrapError(error, {
|
|
2141
|
-
code:
|
|
1961
|
+
code: ErrorCodes.GENERATION_FAILED,
|
|
2142
1962
|
message: `Failed to generate API "${name}"`
|
|
2143
1963
|
});
|
|
2144
|
-
|
|
2145
|
-
|
|
1964
|
+
logger.item(name, "red");
|
|
1965
|
+
logger.error(wrapped, true);
|
|
2146
1966
|
}
|
|
2147
1967
|
}
|
|
2148
|
-
|
|
1968
|
+
logger.divider();
|
|
2149
1969
|
const totalDuration = results.reduce((sum, r) => sum + (r.stats?.duration || 0), 0);
|
|
2150
1970
|
const totalEndpoints = results.reduce((sum, r) => sum + (r.stats?.endpoints || 0), 0);
|
|
2151
1971
|
const totalSchemas = results.reduce((sum, r) => sum + (r.stats?.schemas || 0), 0);
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
1972
|
+
logger.summary({
|
|
1973
|
+
succeeded: successCount,
|
|
1974
|
+
failed: failCount,
|
|
1975
|
+
endpoints: totalEndpoints,
|
|
1976
|
+
schemas: totalSchemas,
|
|
1977
|
+
duration: totalDuration
|
|
1978
|
+
});
|
|
1979
|
+
logger.divider();
|
|
2155
1980
|
return {};
|
|
2156
1981
|
}
|
|
2157
1982
|
};
|
|
@@ -2179,11 +2004,11 @@ exports.SchemaType = SchemaType;
|
|
|
2179
2004
|
exports.SuccessHttpStatusCode = SuccessHttpStatusCode;
|
|
2180
2005
|
exports.apiCodeGenPlugin = apiCodeGenPlugin;
|
|
2181
2006
|
exports.codeGen = codeGen;
|
|
2182
|
-
exports.configToCLIOptions = configToCLIOptions;
|
|
2183
2007
|
exports.createErrors = createErrors;
|
|
2184
2008
|
exports.formatError = formatError;
|
|
2185
2009
|
exports.isApicodegenError = isApicodegenError;
|
|
2186
2010
|
exports.loadConfig = loadConfig;
|
|
2011
|
+
exports.logger = logger;
|
|
2187
2012
|
exports.printError = printError;
|
|
2188
2013
|
exports.toProviderOptions = toProviderOptions;
|
|
2189
2014
|
exports.wrapError = wrapError;
|