@devbro/pashmak 0.1.48 → 0.1.51

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 (41) hide show
  1. package/dist/DatabaseServiceProvider.d.mts +5 -2
  2. package/dist/DatabaseServiceProvider.mjs +10 -3
  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 +12 -2
  12. package/dist/bin/app/console/DefaultCommand.cjs +136 -69
  13. package/dist/bin/app/console/KeyGenerateCommand.cjs +136 -69
  14. package/dist/bin/app/console/StartCommand.cjs +141 -71
  15. package/dist/bin/app/console/generate/GenerateApiDocsCommand.cjs +320 -170
  16. package/dist/bin/app/console/generate/GenerateControllerCommand.cjs +136 -69
  17. package/dist/bin/app/console/generate/index.cjs +320 -170
  18. package/dist/bin/app/console/index.cjs +325 -172
  19. package/dist/bin/app/console/migrate/GenerateMigrateCommand.cjs +136 -69
  20. package/dist/bin/app/console/migrate/MigrateCommand.cjs +136 -69
  21. package/dist/bin/app/console/migrate/MigrateRollbackCommand.cjs +136 -69
  22. package/dist/bin/app/console/migrate/index.cjs +136 -69
  23. package/dist/bin/app/console/queue/GenerateQueueMigrateCommand.cjs +136 -69
  24. package/dist/bin/cache.cjs +136 -69
  25. package/dist/bin/facades.cjs +136 -69
  26. package/dist/bin/factories.cjs +136 -69
  27. package/dist/bin/http.cjs +136 -69
  28. package/dist/bin/index.cjs +338 -178
  29. package/dist/bin/middlewares.cjs +136 -69
  30. package/dist/bin/queue.cjs +136 -69
  31. package/dist/bin/router.cjs +3 -5
  32. package/dist/facades.d.mts +6 -6
  33. package/dist/facades.mjs +97 -58
  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.mjs +1 -1
  38. package/dist/http.mjs.map +1 -1
  39. package/dist/queue.d.mts +1 -1
  40. package/dist/queue.mjs.map +1 -1
  41. 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,9 +108,6 @@ 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
112
  return await this.runMiddlewares(this.middlewares, this.request, this.response);
115
113
  }
@@ -245,7 +243,7 @@ var Route = class {
245
243
  i = start;
246
244
  } else if (char === "*") {
247
245
  let start = i + 1;
248
- while (start < path4.length && /[a-zA-Z0-9_\.]/.test(path4[start])) {
246
+ while (start < path4.length && /[a-zA-Z0-9_]/.test(path4[start])) {
249
247
  start++;
250
248
  }
251
249
  tokens.push({ type: "WILDCARD", value: path4.slice(i + 1, start) });
@@ -456,7 +454,7 @@ var Router = class {
456
454
 
457
455
  // src/facades.mts
458
456
  var import_neko_scheduler = require("@devbro/neko-scheduler");
459
- var import_neko_helper2 = require("@devbro/neko-helper");
457
+ var import_neko_helper3 = require("@devbro/neko-helper");
460
458
  var import_neko_context3 = require("@devbro/neko-context");
461
459
  var import_neko_storage2 = require("@devbro/neko-storage");
462
460
  var import_neko_mailer2 = require("@devbro/neko-mailer");
@@ -497,7 +495,7 @@ __export(queue_exports, {
497
495
  DatabaseTransport: () => DatabaseTransport
498
496
  });
499
497
  __reExport(queue_exports, require("@devbro/neko-queue"));
500
- var import_neko_helper = require("@devbro/neko-helper");
498
+ var import_neko_helper2 = require("@devbro/neko-helper");
501
499
  var import_neko_context2 = require("@devbro/neko-context");
502
500
  var DatabaseTransport = class {
503
501
  static {
@@ -558,7 +556,7 @@ var DatabaseTransport = class {
558
556
  }, "processMessage");
559
557
  constructor(config4 = {}) {
560
558
  this.config = { ...this.config, ...config4 };
561
- this.repeater = (0, import_neko_helper.createRepeater)(
559
+ this.repeater = (0, import_neko_helper2.createRepeater)(
562
560
  this.processMessage,
563
561
  this.config.listen_interval * 1e3
564
562
  );
@@ -626,6 +624,24 @@ import_neko_queue.QueueTransportFactory.register("database", (opt) => {
626
624
  import_neko_queue.QueueTransportFactory.register("memory", (opt) => {
627
625
  return new import_neko_queue.MemoryTransport(opt);
628
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
+ });
629
645
  var CacheProviderFactory = class _CacheProviderFactory {
630
646
  static {
631
647
  __name(this, "CacheProviderFactory");
@@ -656,32 +672,75 @@ import_neko_storage.StorageProviderFactory.register("local", (opt) => {
656
672
  import_neko_storage.StorageProviderFactory.register("s3", (opt) => {
657
673
  return new import_neko_storage.AWSS3StorageProvider(opt);
658
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
+ });
659
687
 
660
688
  // src/facades.mts
661
689
  var import_neko_cache2 = require("@devbro/neko-cache");
662
690
  var import_neko_queue2 = require("@devbro/neko-queue");
663
- var router = (0, import_neko_helper2.createSingleton)(() => new Router());
664
- var scheduler = (0, import_neko_helper2.createSingleton)(() => {
665
- const rc = new import_neko_scheduler.Scheduler();
666
- rc.setErrorHandler((err, job) => {
667
- logger().error({
668
- msg: "Scheduled job error",
669
- err,
670
- job_name: job.getName()
671
- });
691
+ function wrapSingletonWithAccessors(singletonFn) {
692
+ let methodsInitialized = false;
693
+ const initializeMethods = /* @__PURE__ */ __name(() => {
694
+ if (methodsInitialized) return;
695
+ const defaultInstance = singletonFn();
696
+ const prototype = Object.getPrototypeOf(defaultInstance);
697
+ const methodNames = Object.getOwnPropertyNames(prototype).filter(
698
+ (name) => name !== "constructor" && typeof prototype[name] === "function"
699
+ );
700
+ for (const methodName of methodNames) {
701
+ singletonFn[methodName] = (...args) => {
702
+ const instance = singletonFn();
703
+ return instance[methodName](...args);
704
+ };
705
+ }
706
+ methodsInitialized = true;
707
+ }, "initializeMethods");
708
+ return new Proxy(singletonFn, {
709
+ get(target, prop, receiver) {
710
+ if (typeof prop === "string" && !Reflect.has(target, prop)) {
711
+ initializeMethods();
712
+ }
713
+ return Reflect.get(target, prop, receiver);
714
+ }
672
715
  });
673
- return rc;
674
- });
716
+ }
717
+ __name(wrapSingletonWithAccessors, "wrapSingletonWithAccessors");
718
+ var router = (0, import_neko_helper3.createSingleton)(() => new Router());
719
+ var scheduler = wrapSingletonWithAccessors(
720
+ (0, import_neko_helper3.createSingleton)(() => {
721
+ const rc = new import_neko_scheduler.Scheduler();
722
+ rc.setErrorHandler((err, job) => {
723
+ logger().error({
724
+ msg: "Scheduled job error",
725
+ err,
726
+ job_name: job.getName()
727
+ });
728
+ });
729
+ return rc;
730
+ })
731
+ );
675
732
  var db = /* @__PURE__ */ __name((label = "default") => (0, import_neko_context3.ctx)().getOrThrow(["database", label]), "db");
676
- var storage = (0, import_neko_helper2.createSingleton)((label = "default") => {
677
- let storage_config = import_neko_config.config.get(["storages", label].join("."));
678
- const provider = import_neko_storage2.StorageProviderFactory.create(
679
- storage_config.provider,
680
- storage_config.config
681
- );
682
- return new import_neko_storage2.Storage(provider);
683
- });
684
- var cli = (0, import_neko_helper2.createSingleton)(() => {
733
+ var storage = wrapSingletonWithAccessors(
734
+ (0, import_neko_helper3.createSingleton)((label = "default") => {
735
+ let storage_config = import_neko_config.config.get(["storages", label].join("."));
736
+ const provider = import_neko_storage2.StorageProviderFactory.create(
737
+ storage_config.provider,
738
+ storage_config.config
739
+ );
740
+ return new import_neko_storage2.Storage(provider);
741
+ })
742
+ );
743
+ var cli = (0, import_neko_helper3.createSingleton)(() => {
685
744
  const [node, app, ...args] = process.argv;
686
745
  return new import_clipanion.Cli({
687
746
  binaryLabel: `My Application`,
@@ -689,52 +748,60 @@ var cli = (0, import_neko_helper2.createSingleton)(() => {
689
748
  binaryVersion: `1.0.0`
690
749
  });
691
750
  });
692
- var httpServer = (0, import_neko_helper2.createSingleton)(() => {
751
+ var httpServer = (0, import_neko_helper3.createSingleton)(() => {
693
752
  const server = new http_exports.HttpServer();
694
753
  server.setErrorHandler(handleHttpErrors);
695
754
  server.setRouter(router());
696
755
  return server;
697
756
  });
698
- var logger = (0, import_neko_helper2.createSingleton)((label) => {
699
- const logger_config = import_neko_config.config.get(["loggers", label].join("."));
700
- const rc = new import_neko_logger.Logger(logger_config);
701
- rc.setExtrasFunction((message) => {
702
- message.requestId = (0, import_neko_context3.ctxSafe)()?.get("requestId") || "N/A";
703
- return message;
704
- });
705
- return rc;
706
- });
707
- var mailer = (0, import_neko_helper2.createSingleton)((label) => {
708
- const mailer_config = import_neko_config.config.get(["mailer", label].join("."));
709
- const provider = import_neko_mailer2.MailerProviderFactory.create(
710
- mailer_config.provider,
711
- mailer_config.config
712
- );
713
- const rc = new import_neko_mailer2.Mailer(provider);
714
- return rc;
715
- });
716
- var queue = (0, import_neko_helper2.createSingleton)((label) => {
717
- const queue_config = import_neko_config.config.get(["queues", label].join("."));
718
- if (!queue_config) {
719
- throw new Error(`Queue configuration for '${label}' not found`);
720
- }
721
- const provider = import_neko_queue2.QueueTransportFactory.create(
722
- queue_config.provider,
723
- queue_config.config
724
- );
725
- return new import_neko_queue2.QueueConnection(provider);
726
- });
727
- var cache = (0, import_neko_helper2.createSingleton)((label) => {
728
- const cache_config = import_neko_config.config.get(["caches", label].join("."));
729
- if (!cache_config) {
730
- throw new Error(`Cache configuration for '${label}' not found`);
731
- }
732
- const provider = CacheProviderFactory.create(
733
- cache_config.provider,
734
- cache_config.config
735
- );
736
- return new import_neko_cache2.Cache(provider);
737
- });
757
+ var logger = wrapSingletonWithAccessors(
758
+ (0, import_neko_helper3.createSingleton)((label) => {
759
+ const logger_config = import_neko_config.config.get(["loggers", label].join("."));
760
+ const rc = new import_neko_logger.Logger(logger_config);
761
+ rc.setExtrasFunction((message) => {
762
+ message.requestId = (0, import_neko_context3.ctxSafe)()?.get("requestId") || "N/A";
763
+ return message;
764
+ });
765
+ return rc;
766
+ })
767
+ );
768
+ var mailer = wrapSingletonWithAccessors(
769
+ (0, import_neko_helper3.createSingleton)((label) => {
770
+ const mailer_config = import_neko_config.config.get(["mailer", label].join("."));
771
+ const provider = import_neko_mailer2.MailerProviderFactory.create(
772
+ mailer_config.provider,
773
+ mailer_config.config
774
+ );
775
+ const rc = new import_neko_mailer2.Mailer(provider);
776
+ return rc;
777
+ })
778
+ );
779
+ var queue = wrapSingletonWithAccessors(
780
+ (0, import_neko_helper3.createSingleton)((label) => {
781
+ const queue_config = import_neko_config.config.get(["queues", label].join("."));
782
+ if (!queue_config) {
783
+ throw new Error(`Queue configuration for '${label}' not found`);
784
+ }
785
+ const provider = import_neko_queue2.QueueTransportFactory.create(
786
+ queue_config.provider,
787
+ queue_config.config
788
+ );
789
+ return new import_neko_queue2.QueueConnection(provider);
790
+ })
791
+ );
792
+ var cache = wrapSingletonWithAccessors(
793
+ (0, import_neko_helper3.createSingleton)((label) => {
794
+ const cache_config = import_neko_config.config.get(["caches", label].join("."));
795
+ if (!cache_config) {
796
+ throw new Error(`Cache configuration for '${label}' not found`);
797
+ }
798
+ const provider = CacheProviderFactory.create(
799
+ cache_config.provider,
800
+ cache_config.config
801
+ );
802
+ return new import_neko_cache2.Cache(provider);
803
+ })
804
+ );
738
805
 
739
806
  // src/app/console/generate/GenerateControllerCommand.mts
740
807
  var import_clipanion2 = require("clipanion");
@@ -799,144 +866,155 @@ var config_exports = {};
799
866
  __reExport(config_exports, require("@devbro/neko-config"));
800
867
 
801
868
  // src/app/console/generate/GenerateApiDocsCommand.mts
802
- var import_neko_helper3 = require("@devbro/neko-helper");
869
+ var import_neko_helper4 = require("@devbro/neko-helper");
803
870
  var GenerateApiDocsCommand = class extends import_clipanion3.Command {
804
871
  static {
805
872
  __name(this, "GenerateApiDocsCommand");
806
873
  }
807
- static paths = [
808
- [`make`, `apidocs`],
809
- [`generate`, `apidocs`]
810
- ];
874
+ static paths = [[`generate`, `apidocs`]];
811
875
  static usage = import_clipanion3.Command.Usage({
812
876
  category: `Generate`,
813
877
  description: `Generate OpenAPI documentation from routes`,
814
878
  details: `
815
- This command generates OpenAPI 3.0 specification documentation by analyzing
879
+ This command utility generates OpenAPI 3.0 specification documentation by analyzing
816
880
  your application's routes and merging with example files.
817
881
 
818
- The generated documentation includes:
819
- - All registered routes with their HTTP methods
820
- - Path parameters extracted from route definitions
821
- - Request body schemas for POST, PUT, and PATCH methods
822
- - Response schemas
823
-
824
- The command will merge files specified in config.api_docs.merge_files
825
- and output the final documentation to config.api_docs.output.
882
+ Subcommands:
883
+ - generate-from-routes: Generate OpenAPI spec from registered routes
884
+ - generate-base: Generate base OpenAPI specification structure
885
+ - merge-files: Merge multiple OpenAPI files into final documentation
826
886
 
887
+
827
888
  This command depends on config data. make sure your default config contains the following:
828
- api_docs: {
829
- merge_files: [
830
- path.join(__dirname, '../..', 'private', 'openapi_examples.json'),
831
- path.join(__dirname, '../..', 'private', 'openapi_base.json'),
832
- path.join(__dirname, '../..', 'private', 'openapi_user_changes.json'),
833
- ],
834
- output: path.join(__dirname, '../..', 'private', 'openapi.json'),
835
- }
889
+
890
+ \`\`\`
891
+ api_docs: {
892
+
893
+ merge_files: [
894
+
895
+ path.join(__dirname, '../..', 'private', 'openapi_base.json'),
896
+
897
+ path.join(__dirname, '../..', 'private', 'openapi_from_routes.json'),
898
+
899
+ path.join(__dirname, '../..', 'private', 'openapi_from_tests.json'),
900
+
901
+ path.join(__dirname, '../..', 'private', 'openapi_other_user_changes.json'),
902
+
903
+ ],
904
+
905
+ output: path.join(__dirname, '../..', 'public', 'openapi.json'),
906
+
907
+ }
908
+
909
+ \`\`\`
836
910
  `,
837
- examples: [[`Generate API documentation`, `$0 generate apidocs`]]
911
+ examples: [
912
+ [
913
+ `Generate from routes`,
914
+ `$0 generate apidocs generate-from-routes --output path/to/output.json`
915
+ ],
916
+ [
917
+ `Generate base spec`,
918
+ `$0 generate apidocs generate-base --output path/to/output.json`
919
+ ],
920
+ [`Merge files`, `$0 generate apidocs merge-files`],
921
+ [`Show help`, `$0 generate apidocs --help`]
922
+ ]
923
+ });
924
+ subcommand = import_clipanion3.Option.String({ required: false });
925
+ output = import_clipanion3.Option.String(`--output,-o`, {
926
+ description: `Output file path for generated documentation`
838
927
  });
839
- help = import_clipanion3.Option.Boolean(`--help,-h`, false, {
840
- description: `Show help message for this command`
928
+ config = import_clipanion3.Option.String(`--config,-c`, {
929
+ description: `Path in config to get details from (default: api_docs)`,
930
+ required: false
841
931
  });
842
932
  async execute() {
843
- if (this.help) {
933
+ if (!this.subcommand) {
844
934
  this.context.stdout.write(
845
935
  this.constructor.usage?.toString() || "No help available\n"
846
936
  );
847
937
  return 0;
848
938
  }
849
- const rootDir = process.cwd();
850
- this.context.stdout.write(`Generating OpenAPI documentation...
939
+ switch (this.subcommand) {
940
+ case "generate-from-routes":
941
+ return await this.executeGenerateFromRoutes();
942
+ case "generate-base":
943
+ return await this.executeGenerateBase();
944
+ case "merge-files":
945
+ return await this.executeMergeFiles();
946
+ default:
947
+ this.context.stderr.write(`Unknown subcommand: ${this.subcommand}
851
948
  `);
852
- const routes = router().routes;
853
- const openApiSpec = {
854
- openapi: "3.0.0",
855
- info: {
856
- title: "API Documentation",
857
- version: "1.0.0",
858
- description: "Auto-generated API documentation"
859
- },
860
- servers: [
861
- {
862
- url: "/",
863
- description: "Local server"
864
- }
865
- ],
866
- paths: {}
867
- };
868
- for (const route of routes) {
869
- const routePath = route.path;
870
- const openApiPath = routePath.replace(/:([a-zA-Z0-9_]+)/g, "{$1}");
871
- if (!openApiSpec.paths[openApiPath]) {
872
- openApiSpec.paths[openApiPath] = {};
873
- }
874
- for (const method of route.methods) {
875
- const lowerMethod = method.toLowerCase();
876
- if (lowerMethod === "head") {
877
- continue;
878
- }
879
- openApiSpec.paths[openApiPath][lowerMethod] = {
880
- summary: `${method} ${routePath}`,
881
- description: `Endpoint for ${method} ${routePath}`,
882
- parameters: this.extractParameters(routePath),
883
- responses: {
884
- "200": {
885
- description: "Successful response",
886
- content: {
887
- "application/json": {
888
- schema: {
889
- type: "object"
890
- }
891
- }
892
- }
893
- },
894
- "500": {
895
- description: "Internal server error"
896
- }
897
- }
898
- };
899
- if (["post", "put", "patch"].includes(lowerMethod)) {
900
- openApiSpec.paths[openApiPath][lowerMethod].requestBody = {
901
- required: true,
902
- content: {
903
- "application/json": {
904
- schema: {
905
- type: "object"
906
- }
907
- }
908
- }
909
- };
910
- }
911
- }
949
+ this.context.stdout.write(
950
+ this.constructor.usage?.toString() || "No help available\n"
951
+ );
952
+ return 1;
912
953
  }
913
- await fs2.mkdir(config_exports.config.get("private_path"), { recursive: true });
914
- const outputPath = import_path3.default.join(config_exports.config.get("private_path"), "openapi.json");
954
+ }
955
+ async executeGenerateFromRoutes() {
956
+ this.context.stdout.write(
957
+ `Generating OpenAPI documentation from routes...
958
+ `
959
+ );
960
+ const openApiSpec = this.generateFromRoutes();
961
+ const outputPath = this.output || import_path3.default.join(config_exports.config.get("private_path"), "openapi_from_routes.json");
962
+ await fs2.mkdir(import_path3.default.dirname(outputPath), { recursive: true });
915
963
  await fs2.writeFile(
916
964
  outputPath,
917
965
  JSON.stringify(openApiSpec, null, 2),
918
966
  "utf-8"
919
967
  );
920
968
  this.context.stdout.write(
921
- `OpenAPI documentation generated at: ${outputPath}
969
+ `OpenAPI routes documentation generated at: ${outputPath}
970
+ `
971
+ );
972
+ this.context.stdout.write(
973
+ `Total routes documented: ${Object.keys(openApiSpec.paths).length}
974
+ `
975
+ );
976
+ return 0;
977
+ }
978
+ async executeGenerateBase() {
979
+ this.context.stdout.write(`Generating base OpenAPI specification...
980
+ `);
981
+ const baseSpec = this.getBaseOpenApiSpec();
982
+ const outputPath = this.output || import_path3.default.join(config_exports.config.get("private_path"), "openapi_base.json");
983
+ await fs2.mkdir(import_path3.default.dirname(outputPath), { recursive: true });
984
+ await fs2.writeFile(outputPath, JSON.stringify(baseSpec, null, 2), "utf-8");
985
+ this.context.stdout.write(
986
+ `Base OpenAPI specification generated at: ${outputPath}
922
987
  `
923
988
  );
924
- this.context.stdout.write(`Total routes documented: ${routes.length}
989
+ return 0;
990
+ }
991
+ async executeMergeFiles() {
992
+ this.context.stdout.write(`Merging OpenAPI files...
925
993
  `);
926
- let files_to_merge = config_exports.config.get("api_docs.merge_files");
994
+ let configPath = this.config || "api_docs";
995
+ const files_to_merge = config_exports.config.get(`${configPath}.merge_files`);
927
996
  let final_api_docs = {};
928
- for (let file_path of files_to_merge) {
929
- let file_json = JSON.parse(await fs2.readFile(file_path, "utf8"));
930
- final_api_docs = import_neko_helper3.Arr.deepMerge(final_api_docs, file_json);
997
+ for (const file_path of files_to_merge) {
998
+ try {
999
+ const file_json = JSON.parse(await fs2.readFile(file_path, "utf8"));
1000
+ final_api_docs = import_neko_helper4.Arr.deepMerge(final_api_docs, file_json);
1001
+ this.context.stdout.write(` Merged: ${file_path}
1002
+ `);
1003
+ } catch (error) {
1004
+ this.context.stderr.write(
1005
+ ` Warning: Could not read ${file_path}: ${error.message}
1006
+ `
1007
+ );
1008
+ }
931
1009
  }
932
- await fs2.writeFile(
933
- config_exports.config.get("api_docs.output"),
934
- JSON.stringify(final_api_docs, null, 2)
935
- );
1010
+ const outputPath = this.output || config_exports.config.get(`${configPath}.output`);
1011
+ await fs2.mkdir(import_path3.default.dirname(outputPath), { recursive: true });
1012
+ await fs2.writeFile(outputPath, JSON.stringify(final_api_docs, null, 2));
936
1013
  this.context.stdout.write(
937
- `wrote final open api document to : ${config_exports.config.get("api_docs.output")}
1014
+ `Final OpenAPI document written to: ${outputPath}
938
1015
  `
939
1016
  );
1017
+ return 0;
940
1018
  }
941
1019
  extractParameters(routePath) {
942
1020
  const paramRegex = /:([a-zA-Z0-9_]+)/g;
@@ -955,6 +1033,78 @@ var GenerateApiDocsCommand = class extends import_clipanion3.Command {
955
1033
  }
956
1034
  return parameters;
957
1035
  }
1036
+ generateFromRoutes() {
1037
+ const openApiSpec = {
1038
+ paths: {}
1039
+ };
1040
+ const routes = router().routes;
1041
+ for (const route of routes) {
1042
+ const routePath = route.path;
1043
+ const openApiPath = routePath.replace(/\/$/g, "");
1044
+ if (!openApiSpec.paths[openApiPath]) {
1045
+ openApiSpec.paths[openApiPath] = {};
1046
+ }
1047
+ for (const method of route.methods) {
1048
+ const lowerMethod = method.toLowerCase();
1049
+ if (lowerMethod === "head") {
1050
+ continue;
1051
+ }
1052
+ openApiSpec.paths[openApiPath][lowerMethod] = {
1053
+ summary: `${routePath}`,
1054
+ description: `Endpoint for ${method} ${routePath}`,
1055
+ security: [],
1056
+ parameters: this.extractParameters(routePath),
1057
+ responses: {}
1058
+ };
1059
+ if (["post", "put", "patch"].includes(lowerMethod)) {
1060
+ openApiSpec.paths[openApiPath][lowerMethod].requestBody = {
1061
+ required: true,
1062
+ content: {
1063
+ "application/json": {
1064
+ schema: {
1065
+ type: "object"
1066
+ }
1067
+ }
1068
+ }
1069
+ };
1070
+ }
1071
+ }
1072
+ }
1073
+ return openApiSpec;
1074
+ }
1075
+ getBaseOpenApiSpec() {
1076
+ const openApiSpec = {
1077
+ openapi: "3.0.0",
1078
+ info: {
1079
+ title: "API Documentation",
1080
+ version: "1.0.0",
1081
+ description: "Auto-generated API documentation"
1082
+ },
1083
+ servers: [
1084
+ {
1085
+ url: "/",
1086
+ description: "Local server"
1087
+ }
1088
+ ],
1089
+ components: {
1090
+ securitySchemes: {
1091
+ bearerAuth: {
1092
+ type: "http",
1093
+ scheme: "bearer",
1094
+ bearerFormat: "JWT",
1095
+ description: "JWT token authentication"
1096
+ }
1097
+ }
1098
+ },
1099
+ security: [
1100
+ {
1101
+ bearerAuth: []
1102
+ }
1103
+ ],
1104
+ paths: {}
1105
+ };
1106
+ return openApiSpec;
1107
+ }
958
1108
  };
959
1109
  cli().register(GenerateApiDocsCommand);
960
1110
  // Annotate the CommonJS export names for ESM import in node: