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