@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
@@ -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 (this.isClass(middleware)) {
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
- let rc = await this.runMiddlewares(this.middlewares, this.request, this.response);
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_\.]/.test(path3[start])) {
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 import_neko_helper2 = require("@devbro/neko-helper");
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 import_neko_helper = require("@devbro/neko-helper");
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, import_neko_helper.createRepeater)(
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, import_neko_helper2.createSingleton)(() => new Router());
648
- var scheduler = (0, import_neko_helper2.createSingleton)(() => {
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, import_neko_helper2.createSingleton)((label = "default") => {
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, import_neko_helper2.createSingleton)(() => {
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, import_neko_helper2.createSingleton)(() => {
719
+ var httpServer = (0, import_neko_helper3.createSingleton)(() => {
677
720
  const server = new http_exports.HttpServer();
678
- server.setErrorHandler(async (err, req, res) => {
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, import_neko_helper2.createSingleton)((label) => {
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, import_neko_helper2.createSingleton)((label) => {
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, import_neko_helper2.createSingleton)((label) => {
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, import_neko_helper2.createSingleton)((label) => {
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 import_neko_helper3 = require("@devbro/neko-helper");
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
- The generated documentation includes:
761
- - All registered routes with their HTTP methods
762
- - Path parameters extracted from route definitions
763
- - Request body schemas for POST, PUT, and PATCH methods
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
- api_docs: {
771
- merge_files: [
772
- path.join(__dirname, '../..', 'private', 'openapi_examples.json'),
773
- path.join(__dirname, '../..', 'private', 'openapi_base.json'),
774
- path.join(__dirname, '../..', 'private', 'openapi_user_changes.json'),
775
- ],
776
- output: path.join(__dirname, '../..', 'private', 'openapi.json'),
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: [[`Generate API documentation`, `$0 generate apidocs`]]
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
- help = import_clipanion2.Option.Boolean(`--help,-h`, false, {
782
- description: `Show help message for this command`
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.help) {
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
- const rootDir = process.cwd();
792
- this.context.stdout.write(`Generating OpenAPI documentation...
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
- const routes = router().routes;
795
- const openApiSpec = {
796
- openapi: "3.0.0",
797
- info: {
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
- await fs.mkdir(config_exports.config.get("private_path"), { recursive: true });
856
- const outputPath = import_path2.default.join(config_exports.config.get("private_path"), "openapi.json");
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
- this.context.stdout.write(`Total routes documented: ${routes.length}
896
+ return 0;
897
+ }
898
+ async executeMergeFiles() {
899
+ this.context.stdout.write(`Merging OpenAPI files...
867
900
  `);
868
- let files_to_merge = config_exports.config.get("api_docs.merge_files");
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 (let file_path of files_to_merge) {
871
- let file_json = JSON.parse(await fs.readFile(file_path, "utf8"));
872
- final_api_docs = import_neko_helper3.Arr.deepMerge(final_api_docs, file_json);
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
- await fs.writeFile(
875
- config_exports.config.get("api_docs.output"),
876
- JSON.stringify(final_api_docs, null, 2)
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
- `wrote final open api document to : ${config_exports.config.get("api_docs.output")}
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: