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