@devbro/pashmak 0.1.47 → 0.1.49
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/dist/DatabaseServiceProvider.d.mts +4 -1
- package/dist/DatabaseServiceProvider.mjs +5 -2
- package/dist/DatabaseServiceProvider.mjs.map +1 -1
- package/dist/app/console/StartCommand.d.mts +1 -0
- package/dist/app/console/StartCommand.mjs +5 -2
- package/dist/app/console/StartCommand.mjs.map +1 -1
- package/dist/app/console/generate/GenerateApiDocsCommand.d.mts +34 -2
- package/dist/app/console/generate/GenerateApiDocsCommand.mjs +183 -100
- package/dist/app/console/generate/GenerateApiDocsCommand.mjs.map +1 -1
- package/dist/app/console/project/base_project/src/config/storages.ts.tpl +2 -2
- package/dist/bin/DatabaseServiceProvider.cjs +8 -2
- package/dist/bin/app/console/DefaultCommand.cjs +64 -32
- package/dist/bin/app/console/KeyGenerateCommand.cjs +64 -32
- package/dist/bin/app/console/StartCommand.cjs +69 -34
- package/dist/bin/app/console/generate/GenerateApiDocsCommand.cjs +248 -133
- package/dist/bin/app/console/generate/GenerateControllerCommand.cjs +64 -32
- package/dist/bin/app/console/generate/index.cjs +248 -133
- package/dist/bin/app/console/index.cjs +253 -135
- package/dist/bin/app/console/migrate/GenerateMigrateCommand.cjs +64 -32
- package/dist/bin/app/console/migrate/MigrateCommand.cjs +64 -32
- package/dist/bin/app/console/migrate/MigrateRollbackCommand.cjs +64 -32
- package/dist/bin/app/console/migrate/index.cjs +64 -32
- package/dist/bin/app/console/queue/GenerateQueueMigrateCommand.cjs +64 -32
- package/dist/bin/cache.cjs +64 -32
- package/dist/bin/facades.cjs +64 -32
- package/dist/bin/factories.cjs +64 -32
- package/dist/bin/http.cjs +739 -0
- package/dist/bin/index.cjs +264 -141
- package/dist/bin/middlewares.cjs +66 -34
- package/dist/bin/queue.cjs +64 -32
- package/dist/bin/router.cjs +4 -8
- package/dist/config.d.mts +0 -1
- package/dist/facades.mjs +2 -13
- package/dist/facades.mjs.map +1 -1
- package/dist/factories.mjs +45 -2
- package/dist/factories.mjs.map +1 -1
- package/dist/http.d.mts +4 -0
- package/dist/http.mjs +20 -0
- package/dist/http.mjs.map +1 -1
- package/dist/queue.d.mts +1 -1
- package/dist/queue.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -77,6 +77,7 @@ var MiddlewareFactory = class {
|
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
// ../neko-router/dist/CompiledRoute.mjs
|
|
80
|
+
var import_neko_helper = require("@devbro/neko-helper");
|
|
80
81
|
var CompiledRoute = class {
|
|
81
82
|
static {
|
|
82
83
|
__name(this, "CompiledRoute");
|
|
@@ -97,7 +98,7 @@ var CompiledRoute = class {
|
|
|
97
98
|
for (const middleware of [...this.globalMiddlewares, ...this.route.getMiddlewares()]) {
|
|
98
99
|
if (middleware instanceof Middleware) {
|
|
99
100
|
this.middlewares.push(middleware);
|
|
100
|
-
} else if (
|
|
101
|
+
} else if ((0, import_neko_helper.isClass)(middleware)) {
|
|
101
102
|
this.middlewares.push(middleware.getInstance({}));
|
|
102
103
|
} else if (typeof middleware === "function") {
|
|
103
104
|
this.middlewares.push(MiddlewareFactory.create(middleware));
|
|
@@ -106,13 +107,8 @@ var CompiledRoute = class {
|
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
|
-
isClass(func) {
|
|
110
|
-
return typeof func === "function" && /^class\s/.test(Function.prototype.toString.call(func));
|
|
111
|
-
}
|
|
112
110
|
async run() {
|
|
113
|
-
|
|
114
|
-
this.response.end();
|
|
115
|
-
return rc;
|
|
111
|
+
return await this.runMiddlewares(this.middlewares, this.request, this.response);
|
|
116
112
|
}
|
|
117
113
|
prepareOutputJsonFormat(obj) {
|
|
118
114
|
function traverse(value) {
|
|
@@ -246,7 +242,7 @@ var Route = class {
|
|
|
246
242
|
i = start;
|
|
247
243
|
} else if (char === "*") {
|
|
248
244
|
let start = i + 1;
|
|
249
|
-
while (start < path3.length && /[a-zA-Z0-9_
|
|
245
|
+
while (start < path3.length && /[a-zA-Z0-9_]/.test(path3[start])) {
|
|
250
246
|
start++;
|
|
251
247
|
}
|
|
252
248
|
tokens.push({ type: "WILDCARD", value: path3.slice(i + 1, start) });
|
|
@@ -457,7 +453,7 @@ var Router = class {
|
|
|
457
453
|
|
|
458
454
|
// src/facades.mts
|
|
459
455
|
var import_neko_scheduler = require("@devbro/neko-scheduler");
|
|
460
|
-
var
|
|
456
|
+
var import_neko_helper3 = require("@devbro/neko-helper");
|
|
461
457
|
var import_neko_context3 = require("@devbro/neko-context");
|
|
462
458
|
var import_neko_storage2 = require("@devbro/neko-storage");
|
|
463
459
|
var import_neko_mailer2 = require("@devbro/neko-mailer");
|
|
@@ -466,7 +462,24 @@ var import_clipanion = require("clipanion");
|
|
|
466
462
|
|
|
467
463
|
// src/http.mts
|
|
468
464
|
var http_exports = {};
|
|
465
|
+
__export(http_exports, {
|
|
466
|
+
handleHttpErrors: () => handleHttpErrors
|
|
467
|
+
});
|
|
468
|
+
var import_neko_http = require("@devbro/neko-http");
|
|
469
469
|
__reExport(http_exports, require("@devbro/neko-http"));
|
|
470
|
+
async function handleHttpErrors(err, req, res) {
|
|
471
|
+
if (err instanceof import_neko_http.HttpError) {
|
|
472
|
+
res.writeHead(err.statusCode, { "Content-Type": "application/json" });
|
|
473
|
+
res.write(JSON.stringify({ message: err.message, error: err.code }));
|
|
474
|
+
logger().warn({ msg: "HttpError: " + err.message, err });
|
|
475
|
+
return;
|
|
476
|
+
} else {
|
|
477
|
+
logger().error({ msg: "Error: " + err.message, err });
|
|
478
|
+
}
|
|
479
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
480
|
+
res.write(JSON.stringify({ error: "Internal Server Error" }));
|
|
481
|
+
}
|
|
482
|
+
__name(handleHttpErrors, "handleHttpErrors");
|
|
470
483
|
|
|
471
484
|
// src/facades.mts
|
|
472
485
|
var import_neko_logger = require("@devbro/neko-logger");
|
|
@@ -481,7 +494,7 @@ __export(queue_exports, {
|
|
|
481
494
|
DatabaseTransport: () => DatabaseTransport
|
|
482
495
|
});
|
|
483
496
|
__reExport(queue_exports, require("@devbro/neko-queue"));
|
|
484
|
-
var
|
|
497
|
+
var import_neko_helper2 = require("@devbro/neko-helper");
|
|
485
498
|
var import_neko_context2 = require("@devbro/neko-context");
|
|
486
499
|
var DatabaseTransport = class {
|
|
487
500
|
static {
|
|
@@ -542,7 +555,7 @@ var DatabaseTransport = class {
|
|
|
542
555
|
}, "processMessage");
|
|
543
556
|
constructor(config3 = {}) {
|
|
544
557
|
this.config = { ...this.config, ...config3 };
|
|
545
|
-
this.repeater = (0,
|
|
558
|
+
this.repeater = (0, import_neko_helper2.createRepeater)(
|
|
546
559
|
this.processMessage,
|
|
547
560
|
this.config.listen_interval * 1e3
|
|
548
561
|
);
|
|
@@ -610,6 +623,24 @@ import_neko_queue.QueueTransportFactory.register("database", (opt) => {
|
|
|
610
623
|
import_neko_queue.QueueTransportFactory.register("memory", (opt) => {
|
|
611
624
|
return new import_neko_queue.MemoryTransport(opt);
|
|
612
625
|
});
|
|
626
|
+
import_neko_queue.QueueTransportFactory.register("sqs", (opt) => {
|
|
627
|
+
return new import_neko_queue.AwsSqsTransport(opt);
|
|
628
|
+
});
|
|
629
|
+
import_neko_queue.QueueTransportFactory.register("amqp", (opt) => {
|
|
630
|
+
return new import_neko_queue.AmqpTransport(opt);
|
|
631
|
+
});
|
|
632
|
+
import_neko_queue.QueueTransportFactory.register("redis", (opt) => {
|
|
633
|
+
return new import_neko_queue.RedisTransport(opt);
|
|
634
|
+
});
|
|
635
|
+
import_neko_queue.QueueTransportFactory.register("async", (opt) => {
|
|
636
|
+
return new import_neko_queue.AsyncTransport();
|
|
637
|
+
});
|
|
638
|
+
import_neko_queue.QueueTransportFactory.register("azure_service_bus", (opt) => {
|
|
639
|
+
return new import_neko_queue.AzureServiceBusTransport(opt);
|
|
640
|
+
});
|
|
641
|
+
import_neko_queue.QueueTransportFactory.register("google_pubsub", (opt) => {
|
|
642
|
+
return new import_neko_queue.GooglePubSubTransport(opt);
|
|
643
|
+
});
|
|
613
644
|
var CacheProviderFactory = class _CacheProviderFactory {
|
|
614
645
|
static {
|
|
615
646
|
__name(this, "CacheProviderFactory");
|
|
@@ -640,12 +671,24 @@ import_neko_storage.StorageProviderFactory.register("local", (opt) => {
|
|
|
640
671
|
import_neko_storage.StorageProviderFactory.register("s3", (opt) => {
|
|
641
672
|
return new import_neko_storage.AWSS3StorageProvider(opt);
|
|
642
673
|
});
|
|
674
|
+
import_neko_storage.StorageProviderFactory.register("gcp", (opt) => {
|
|
675
|
+
return new import_neko_storage.GCPStorageProvider(opt);
|
|
676
|
+
});
|
|
677
|
+
import_neko_storage.StorageProviderFactory.register("azure", (opt) => {
|
|
678
|
+
return new import_neko_storage.AzureBlobStorageProvider(opt);
|
|
679
|
+
});
|
|
680
|
+
import_neko_storage.StorageProviderFactory.register("ftp", (opt) => {
|
|
681
|
+
return new import_neko_storage.FTPStorageProvider(opt);
|
|
682
|
+
});
|
|
683
|
+
import_neko_storage.StorageProviderFactory.register("sftp", (opt) => {
|
|
684
|
+
return new import_neko_storage.SFTPStorageProvider(opt);
|
|
685
|
+
});
|
|
643
686
|
|
|
644
687
|
// src/facades.mts
|
|
645
688
|
var import_neko_cache2 = require("@devbro/neko-cache");
|
|
646
689
|
var import_neko_queue2 = require("@devbro/neko-queue");
|
|
647
|
-
var router = (0,
|
|
648
|
-
var scheduler = (0,
|
|
690
|
+
var router = (0, import_neko_helper3.createSingleton)(() => new Router());
|
|
691
|
+
var scheduler = (0, import_neko_helper3.createSingleton)(() => {
|
|
649
692
|
const rc = new import_neko_scheduler.Scheduler();
|
|
650
693
|
rc.setErrorHandler((err, job) => {
|
|
651
694
|
logger().error({
|
|
@@ -657,7 +700,7 @@ var scheduler = (0, import_neko_helper2.createSingleton)(() => {
|
|
|
657
700
|
return rc;
|
|
658
701
|
});
|
|
659
702
|
var db = /* @__PURE__ */ __name((label = "default") => (0, import_neko_context3.ctx)().getOrThrow(["database", label]), "db");
|
|
660
|
-
var storage = (0,
|
|
703
|
+
var storage = (0, import_neko_helper3.createSingleton)((label = "default") => {
|
|
661
704
|
let storage_config = import_neko_config.config.get(["storages", label].join("."));
|
|
662
705
|
const provider = import_neko_storage2.StorageProviderFactory.create(
|
|
663
706
|
storage_config.provider,
|
|
@@ -665,7 +708,7 @@ var storage = (0, import_neko_helper2.createSingleton)((label = "default") => {
|
|
|
665
708
|
);
|
|
666
709
|
return new import_neko_storage2.Storage(provider);
|
|
667
710
|
});
|
|
668
|
-
var cli = (0,
|
|
711
|
+
var cli = (0, import_neko_helper3.createSingleton)(() => {
|
|
669
712
|
const [node, app, ...args] = process.argv;
|
|
670
713
|
return new import_clipanion.Cli({
|
|
671
714
|
binaryLabel: `My Application`,
|
|
@@ -673,24 +716,13 @@ var cli = (0, import_neko_helper2.createSingleton)(() => {
|
|
|
673
716
|
binaryVersion: `1.0.0`
|
|
674
717
|
});
|
|
675
718
|
});
|
|
676
|
-
var httpServer = (0,
|
|
719
|
+
var httpServer = (0, import_neko_helper3.createSingleton)(() => {
|
|
677
720
|
const server = new http_exports.HttpServer();
|
|
678
|
-
server.setErrorHandler(
|
|
679
|
-
if (err instanceof http_exports.HttpError) {
|
|
680
|
-
res.writeHead(err.statusCode, { "Content-Type": "application/json" });
|
|
681
|
-
res.end(JSON.stringify({ message: err.message, error: err.code }));
|
|
682
|
-
logger().warn({ msg: "HttpError: " + err.message, err });
|
|
683
|
-
return;
|
|
684
|
-
} else {
|
|
685
|
-
logger().error({ msg: "Error: " + err.message, err });
|
|
686
|
-
}
|
|
687
|
-
res.writeHead(500, { "Content-Type": "" });
|
|
688
|
-
res.end(JSON.stringify({ error: "Internal Server Error" }));
|
|
689
|
-
});
|
|
721
|
+
server.setErrorHandler(handleHttpErrors);
|
|
690
722
|
server.setRouter(router());
|
|
691
723
|
return server;
|
|
692
724
|
});
|
|
693
|
-
var logger = (0,
|
|
725
|
+
var logger = (0, import_neko_helper3.createSingleton)((label) => {
|
|
694
726
|
const logger_config = import_neko_config.config.get(["loggers", label].join("."));
|
|
695
727
|
const rc = new import_neko_logger.Logger(logger_config);
|
|
696
728
|
rc.setExtrasFunction((message) => {
|
|
@@ -699,7 +731,7 @@ var logger = (0, import_neko_helper2.createSingleton)((label) => {
|
|
|
699
731
|
});
|
|
700
732
|
return rc;
|
|
701
733
|
});
|
|
702
|
-
var mailer = (0,
|
|
734
|
+
var mailer = (0, import_neko_helper3.createSingleton)((label) => {
|
|
703
735
|
const mailer_config = import_neko_config.config.get(["mailer", label].join("."));
|
|
704
736
|
const provider = import_neko_mailer2.MailerProviderFactory.create(
|
|
705
737
|
mailer_config.provider,
|
|
@@ -708,7 +740,7 @@ var mailer = (0, import_neko_helper2.createSingleton)((label) => {
|
|
|
708
740
|
const rc = new import_neko_mailer2.Mailer(provider);
|
|
709
741
|
return rc;
|
|
710
742
|
});
|
|
711
|
-
var queue = (0,
|
|
743
|
+
var queue = (0, import_neko_helper3.createSingleton)((label) => {
|
|
712
744
|
const queue_config = import_neko_config.config.get(["queues", label].join("."));
|
|
713
745
|
if (!queue_config) {
|
|
714
746
|
throw new Error(`Queue configuration for '${label}' not found`);
|
|
@@ -719,7 +751,7 @@ var queue = (0, import_neko_helper2.createSingleton)((label) => {
|
|
|
719
751
|
);
|
|
720
752
|
return new import_neko_queue2.QueueConnection(provider);
|
|
721
753
|
});
|
|
722
|
-
var cache = (0,
|
|
754
|
+
var cache = (0, import_neko_helper3.createSingleton)((label) => {
|
|
723
755
|
const cache_config = import_neko_config.config.get(["caches", label].join("."));
|
|
724
756
|
if (!cache_config) {
|
|
725
757
|
throw new Error(`Cache configuration for '${label}' not found`);
|
|
@@ -741,144 +773,155 @@ var config_exports = {};
|
|
|
741
773
|
__reExport(config_exports, require("@devbro/neko-config"));
|
|
742
774
|
|
|
743
775
|
// src/app/console/generate/GenerateApiDocsCommand.mts
|
|
744
|
-
var
|
|
776
|
+
var import_neko_helper4 = require("@devbro/neko-helper");
|
|
745
777
|
var GenerateApiDocsCommand = class extends import_clipanion2.Command {
|
|
746
778
|
static {
|
|
747
779
|
__name(this, "GenerateApiDocsCommand");
|
|
748
780
|
}
|
|
749
|
-
static paths = [
|
|
750
|
-
[`make`, `apidocs`],
|
|
751
|
-
[`generate`, `apidocs`]
|
|
752
|
-
];
|
|
781
|
+
static paths = [[`generate`, `apidocsv2`]];
|
|
753
782
|
static usage = import_clipanion2.Command.Usage({
|
|
754
783
|
category: `Generate`,
|
|
755
784
|
description: `Generate OpenAPI documentation from routes`,
|
|
756
785
|
details: `
|
|
757
|
-
This command generates OpenAPI 3.0 specification documentation by analyzing
|
|
786
|
+
This command utility generates OpenAPI 3.0 specification documentation by analyzing
|
|
758
787
|
your application's routes and merging with example files.
|
|
759
788
|
|
|
760
|
-
|
|
761
|
-
-
|
|
762
|
-
-
|
|
763
|
-
-
|
|
764
|
-
- Response schemas
|
|
765
|
-
|
|
766
|
-
The command will merge files specified in config.api_docs.merge_files
|
|
767
|
-
and output the final documentation to config.api_docs.output.
|
|
789
|
+
Subcommands:
|
|
790
|
+
- generate-from-routes: Generate OpenAPI spec from registered routes
|
|
791
|
+
- generate-base: Generate base OpenAPI specification structure
|
|
792
|
+
- merge-files: Merge multiple OpenAPI files into final documentation
|
|
768
793
|
|
|
794
|
+
|
|
769
795
|
This command depends on config data. make sure your default config contains the following:
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
796
|
+
|
|
797
|
+
\`\`\`
|
|
798
|
+
api_docs: {
|
|
799
|
+
|
|
800
|
+
merge_files: [
|
|
801
|
+
|
|
802
|
+
path.join(__dirname, '../..', 'private', 'openapi_base.json'),
|
|
803
|
+
|
|
804
|
+
path.join(__dirname, '../..', 'private', 'openapi_from_routes.json'),
|
|
805
|
+
|
|
806
|
+
path.join(__dirname, '../..', 'private', 'openapi_from_tests.json'),
|
|
807
|
+
|
|
808
|
+
path.join(__dirname, '../..', 'private', 'openapi_other_user_changes.json'),
|
|
809
|
+
|
|
810
|
+
],
|
|
811
|
+
|
|
812
|
+
output: path.join(__dirname, '../..', 'public', 'openapi.json'),
|
|
813
|
+
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
\`\`\`
|
|
778
817
|
`,
|
|
779
|
-
examples: [
|
|
818
|
+
examples: [
|
|
819
|
+
[
|
|
820
|
+
`Generate from routes`,
|
|
821
|
+
`$0 generate apidocsv2 generate-from-routes --output path/to/output.json`
|
|
822
|
+
],
|
|
823
|
+
[
|
|
824
|
+
`Generate base spec`,
|
|
825
|
+
`$0 generate apidocsv2 generate-base --output path/to/output.json`
|
|
826
|
+
],
|
|
827
|
+
[`Merge files`, `$0 generate apidocsv2 merge-files`],
|
|
828
|
+
[`Show help`, `$0 generate apidocsv2 --help`]
|
|
829
|
+
]
|
|
830
|
+
});
|
|
831
|
+
subcommand = import_clipanion2.Option.String({ required: false });
|
|
832
|
+
output = import_clipanion2.Option.String(`--output,-o`, {
|
|
833
|
+
description: `Output file path for generated documentation`
|
|
780
834
|
});
|
|
781
|
-
|
|
782
|
-
description: `
|
|
835
|
+
config = import_clipanion2.Option.String(`--config,-c`, {
|
|
836
|
+
description: `Path in config to get details from (default: api_docs)`,
|
|
837
|
+
required: false
|
|
783
838
|
});
|
|
784
839
|
async execute() {
|
|
785
|
-
if (this.
|
|
840
|
+
if (!this.subcommand) {
|
|
786
841
|
this.context.stdout.write(
|
|
787
842
|
this.constructor.usage?.toString() || "No help available\n"
|
|
788
843
|
);
|
|
789
844
|
return 0;
|
|
790
845
|
}
|
|
791
|
-
|
|
792
|
-
|
|
846
|
+
switch (this.subcommand) {
|
|
847
|
+
case "generate-from-routes":
|
|
848
|
+
return await this.executeGenerateFromRoutes();
|
|
849
|
+
case "generate-base":
|
|
850
|
+
return await this.executeGenerateBase();
|
|
851
|
+
case "merge-files":
|
|
852
|
+
return await this.executeMergeFiles();
|
|
853
|
+
default:
|
|
854
|
+
this.context.stderr.write(`Unknown subcommand: ${this.subcommand}
|
|
793
855
|
`);
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
title: "API Documentation",
|
|
799
|
-
version: "1.0.0",
|
|
800
|
-
description: "Auto-generated API documentation"
|
|
801
|
-
},
|
|
802
|
-
servers: [
|
|
803
|
-
{
|
|
804
|
-
url: "/",
|
|
805
|
-
description: "Local server"
|
|
806
|
-
}
|
|
807
|
-
],
|
|
808
|
-
paths: {}
|
|
809
|
-
};
|
|
810
|
-
for (const route of routes) {
|
|
811
|
-
const routePath = route.path;
|
|
812
|
-
const openApiPath = routePath.replace(/:([a-zA-Z0-9_]+)/g, "{$1}");
|
|
813
|
-
if (!openApiSpec.paths[openApiPath]) {
|
|
814
|
-
openApiSpec.paths[openApiPath] = {};
|
|
815
|
-
}
|
|
816
|
-
for (const method of route.methods) {
|
|
817
|
-
const lowerMethod = method.toLowerCase();
|
|
818
|
-
if (lowerMethod === "head") {
|
|
819
|
-
continue;
|
|
820
|
-
}
|
|
821
|
-
openApiSpec.paths[openApiPath][lowerMethod] = {
|
|
822
|
-
summary: `${method} ${routePath}`,
|
|
823
|
-
description: `Endpoint for ${method} ${routePath}`,
|
|
824
|
-
parameters: this.extractParameters(routePath),
|
|
825
|
-
responses: {
|
|
826
|
-
"200": {
|
|
827
|
-
description: "Successful response",
|
|
828
|
-
content: {
|
|
829
|
-
"application/json": {
|
|
830
|
-
schema: {
|
|
831
|
-
type: "object"
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
},
|
|
836
|
-
"500": {
|
|
837
|
-
description: "Internal server error"
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
|
-
if (["post", "put", "patch"].includes(lowerMethod)) {
|
|
842
|
-
openApiSpec.paths[openApiPath][lowerMethod].requestBody = {
|
|
843
|
-
required: true,
|
|
844
|
-
content: {
|
|
845
|
-
"application/json": {
|
|
846
|
-
schema: {
|
|
847
|
-
type: "object"
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
};
|
|
852
|
-
}
|
|
853
|
-
}
|
|
856
|
+
this.context.stdout.write(
|
|
857
|
+
this.constructor.usage?.toString() || "No help available\n"
|
|
858
|
+
);
|
|
859
|
+
return 1;
|
|
854
860
|
}
|
|
855
|
-
|
|
856
|
-
|
|
861
|
+
}
|
|
862
|
+
async executeGenerateFromRoutes() {
|
|
863
|
+
this.context.stdout.write(
|
|
864
|
+
`Generating OpenAPI documentation from routes...
|
|
865
|
+
`
|
|
866
|
+
);
|
|
867
|
+
const openApiSpec = this.generateFromRoutes();
|
|
868
|
+
const outputPath = this.output || import_path2.default.join(config_exports.config.get("private_path"), "openapi_from_routes.json");
|
|
869
|
+
await fs.mkdir(import_path2.default.dirname(outputPath), { recursive: true });
|
|
857
870
|
await fs.writeFile(
|
|
858
871
|
outputPath,
|
|
859
872
|
JSON.stringify(openApiSpec, null, 2),
|
|
860
873
|
"utf-8"
|
|
861
874
|
);
|
|
862
875
|
this.context.stdout.write(
|
|
863
|
-
`OpenAPI documentation generated at: ${outputPath}
|
|
876
|
+
`OpenAPI routes documentation generated at: ${outputPath}
|
|
877
|
+
`
|
|
878
|
+
);
|
|
879
|
+
this.context.stdout.write(
|
|
880
|
+
`Total routes documented: ${Object.keys(openApiSpec.paths).length}
|
|
881
|
+
`
|
|
882
|
+
);
|
|
883
|
+
return 0;
|
|
884
|
+
}
|
|
885
|
+
async executeGenerateBase() {
|
|
886
|
+
this.context.stdout.write(`Generating base OpenAPI specification...
|
|
887
|
+
`);
|
|
888
|
+
const baseSpec = this.getBaseOpenApiSpec();
|
|
889
|
+
const outputPath = this.output || import_path2.default.join(config_exports.config.get("private_path"), "openapi_base.json");
|
|
890
|
+
await fs.mkdir(import_path2.default.dirname(outputPath), { recursive: true });
|
|
891
|
+
await fs.writeFile(outputPath, JSON.stringify(baseSpec, null, 2), "utf-8");
|
|
892
|
+
this.context.stdout.write(
|
|
893
|
+
`Base OpenAPI specification generated at: ${outputPath}
|
|
864
894
|
`
|
|
865
895
|
);
|
|
866
|
-
|
|
896
|
+
return 0;
|
|
897
|
+
}
|
|
898
|
+
async executeMergeFiles() {
|
|
899
|
+
this.context.stdout.write(`Merging OpenAPI files...
|
|
867
900
|
`);
|
|
868
|
-
let
|
|
901
|
+
let configPath = this.config || "api_docs";
|
|
902
|
+
const files_to_merge = config_exports.config.get(`${configPath}.merge_files`);
|
|
869
903
|
let final_api_docs = {};
|
|
870
|
-
for (
|
|
871
|
-
|
|
872
|
-
|
|
904
|
+
for (const file_path of files_to_merge) {
|
|
905
|
+
try {
|
|
906
|
+
const file_json = JSON.parse(await fs.readFile(file_path, "utf8"));
|
|
907
|
+
final_api_docs = import_neko_helper4.Arr.deepMerge(final_api_docs, file_json);
|
|
908
|
+
this.context.stdout.write(` Merged: ${file_path}
|
|
909
|
+
`);
|
|
910
|
+
} catch (error) {
|
|
911
|
+
this.context.stderr.write(
|
|
912
|
+
` Warning: Could not read ${file_path}: ${error.message}
|
|
913
|
+
`
|
|
914
|
+
);
|
|
915
|
+
}
|
|
873
916
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
);
|
|
917
|
+
const outputPath = this.output || config_exports.config.get(`${configPath}.output`);
|
|
918
|
+
await fs.mkdir(import_path2.default.dirname(outputPath), { recursive: true });
|
|
919
|
+
await fs.writeFile(outputPath, JSON.stringify(final_api_docs, null, 2));
|
|
878
920
|
this.context.stdout.write(
|
|
879
|
-
`
|
|
921
|
+
`Final OpenAPI document written to: ${outputPath}
|
|
880
922
|
`
|
|
881
923
|
);
|
|
924
|
+
return 0;
|
|
882
925
|
}
|
|
883
926
|
extractParameters(routePath) {
|
|
884
927
|
const paramRegex = /:([a-zA-Z0-9_]+)/g;
|
|
@@ -897,6 +940,78 @@ var GenerateApiDocsCommand = class extends import_clipanion2.Command {
|
|
|
897
940
|
}
|
|
898
941
|
return parameters;
|
|
899
942
|
}
|
|
943
|
+
generateFromRoutes() {
|
|
944
|
+
const openApiSpec = {
|
|
945
|
+
paths: {}
|
|
946
|
+
};
|
|
947
|
+
const routes = router().routes;
|
|
948
|
+
for (const route of routes) {
|
|
949
|
+
const routePath = route.path;
|
|
950
|
+
const openApiPath = routePath.replace(/\/$/g, "");
|
|
951
|
+
if (!openApiSpec.paths[openApiPath]) {
|
|
952
|
+
openApiSpec.paths[openApiPath] = {};
|
|
953
|
+
}
|
|
954
|
+
for (const method of route.methods) {
|
|
955
|
+
const lowerMethod = method.toLowerCase();
|
|
956
|
+
if (lowerMethod === "head") {
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
959
|
+
openApiSpec.paths[openApiPath][lowerMethod] = {
|
|
960
|
+
summary: `${routePath}`,
|
|
961
|
+
description: `Endpoint for ${method} ${routePath}`,
|
|
962
|
+
security: [],
|
|
963
|
+
parameters: this.extractParameters(routePath),
|
|
964
|
+
responses: {}
|
|
965
|
+
};
|
|
966
|
+
if (["post", "put", "patch"].includes(lowerMethod)) {
|
|
967
|
+
openApiSpec.paths[openApiPath][lowerMethod].requestBody = {
|
|
968
|
+
required: true,
|
|
969
|
+
content: {
|
|
970
|
+
"application/json": {
|
|
971
|
+
schema: {
|
|
972
|
+
type: "object"
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return openApiSpec;
|
|
981
|
+
}
|
|
982
|
+
getBaseOpenApiSpec() {
|
|
983
|
+
const openApiSpec = {
|
|
984
|
+
openapi: "3.0.0",
|
|
985
|
+
info: {
|
|
986
|
+
title: "API Documentation",
|
|
987
|
+
version: "1.0.0",
|
|
988
|
+
description: "Auto-generated API documentation"
|
|
989
|
+
},
|
|
990
|
+
servers: [
|
|
991
|
+
{
|
|
992
|
+
url: "/",
|
|
993
|
+
description: "Local server"
|
|
994
|
+
}
|
|
995
|
+
],
|
|
996
|
+
components: {
|
|
997
|
+
securitySchemes: {
|
|
998
|
+
bearerAuth: {
|
|
999
|
+
type: "http",
|
|
1000
|
+
scheme: "bearer",
|
|
1001
|
+
bearerFormat: "JWT",
|
|
1002
|
+
description: "JWT token authentication"
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
security: [
|
|
1007
|
+
{
|
|
1008
|
+
bearerAuth: []
|
|
1009
|
+
}
|
|
1010
|
+
],
|
|
1011
|
+
paths: {}
|
|
1012
|
+
};
|
|
1013
|
+
return openApiSpec;
|
|
1014
|
+
}
|
|
900
1015
|
};
|
|
901
1016
|
cli().register(GenerateApiDocsCommand);
|
|
902
1017
|
// Annotate the CommonJS export names for ESM import in node:
|