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