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