@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
@@ -1241,6 +1241,7 @@ var MiddlewareFactory = class {
1241
1241
  };
1242
1242
 
1243
1243
  // ../neko-router/dist/CompiledRoute.mjs
1244
+ var import_neko_helper = require("@devbro/neko-helper");
1244
1245
  var CompiledRoute = class {
1245
1246
  static {
1246
1247
  __name(this, "CompiledRoute");
@@ -1261,7 +1262,7 @@ var CompiledRoute = class {
1261
1262
  for (const middleware of [...this.globalMiddlewares, ...this.route.getMiddlewares()]) {
1262
1263
  if (middleware instanceof Middleware) {
1263
1264
  this.middlewares.push(middleware);
1264
- } else if (this.isClass(middleware)) {
1265
+ } else if ((0, import_neko_helper.isClass)(middleware)) {
1265
1266
  this.middlewares.push(middleware.getInstance({}));
1266
1267
  } else if (typeof middleware === "function") {
1267
1268
  this.middlewares.push(MiddlewareFactory.create(middleware));
@@ -1270,13 +1271,8 @@ var CompiledRoute = class {
1270
1271
  }
1271
1272
  }
1272
1273
  }
1273
- isClass(func) {
1274
- return typeof func === "function" && /^class\s/.test(Function.prototype.toString.call(func));
1275
- }
1276
1274
  async run() {
1277
- let rc = await this.runMiddlewares(this.middlewares, this.request, this.response);
1278
- this.response.end();
1279
- return rc;
1275
+ return await this.runMiddlewares(this.middlewares, this.request, this.response);
1280
1276
  }
1281
1277
  prepareOutputJsonFormat(obj) {
1282
1278
  function traverse(value) {
@@ -1410,7 +1406,7 @@ var Route = class {
1410
1406
  i = start;
1411
1407
  } else if (char === "*") {
1412
1408
  let start = i + 1;
1413
- while (start < path10.length && /[a-zA-Z0-9_\.]/.test(path10[start])) {
1409
+ while (start < path10.length && /[a-zA-Z0-9_]/.test(path10[start])) {
1414
1410
  start++;
1415
1411
  }
1416
1412
  tokens.push({ type: "WILDCARD", value: path10.slice(i + 1, start) });
@@ -1621,7 +1617,7 @@ var Router = class {
1621
1617
 
1622
1618
  // src/facades.mts
1623
1619
  var import_neko_scheduler = require("@devbro/neko-scheduler");
1624
- var import_neko_helper2 = require("@devbro/neko-helper");
1620
+ var import_neko_helper3 = require("@devbro/neko-helper");
1625
1621
  var import_neko_context3 = require("@devbro/neko-context");
1626
1622
  var import_neko_storage2 = require("@devbro/neko-storage");
1627
1623
  var import_neko_mailer2 = require("@devbro/neko-mailer");
@@ -1630,7 +1626,24 @@ var import_clipanion = require("clipanion");
1630
1626
 
1631
1627
  // src/http.mts
1632
1628
  var http_exports = {};
1629
+ __export(http_exports, {
1630
+ handleHttpErrors: () => handleHttpErrors
1631
+ });
1632
+ var import_neko_http = require("@devbro/neko-http");
1633
1633
  __reExport(http_exports, require("@devbro/neko-http"));
1634
+ async function handleHttpErrors(err, req, res) {
1635
+ if (err instanceof import_neko_http.HttpError) {
1636
+ res.writeHead(err.statusCode, { "Content-Type": "application/json" });
1637
+ res.write(JSON.stringify({ message: err.message, error: err.code }));
1638
+ logger().warn({ msg: "HttpError: " + err.message, err });
1639
+ return;
1640
+ } else {
1641
+ logger().error({ msg: "Error: " + err.message, err });
1642
+ }
1643
+ res.writeHead(500, { "Content-Type": "application/json" });
1644
+ res.write(JSON.stringify({ error: "Internal Server Error" }));
1645
+ }
1646
+ __name(handleHttpErrors, "handleHttpErrors");
1634
1647
 
1635
1648
  // src/facades.mts
1636
1649
  var import_neko_logger = require("@devbro/neko-logger");
@@ -1645,7 +1658,7 @@ __export(queue_exports, {
1645
1658
  DatabaseTransport: () => DatabaseTransport
1646
1659
  });
1647
1660
  __reExport(queue_exports, require("@devbro/neko-queue"));
1648
- var import_neko_helper = require("@devbro/neko-helper");
1661
+ var import_neko_helper2 = require("@devbro/neko-helper");
1649
1662
  var import_neko_context2 = require("@devbro/neko-context");
1650
1663
  var DatabaseTransport = class {
1651
1664
  static {
@@ -1706,7 +1719,7 @@ var DatabaseTransport = class {
1706
1719
  }, "processMessage");
1707
1720
  constructor(config9 = {}) {
1708
1721
  this.config = { ...this.config, ...config9 };
1709
- this.repeater = (0, import_neko_helper.createRepeater)(
1722
+ this.repeater = (0, import_neko_helper2.createRepeater)(
1710
1723
  this.processMessage,
1711
1724
  this.config.listen_interval * 1e3
1712
1725
  );
@@ -1774,6 +1787,24 @@ import_neko_queue.QueueTransportFactory.register("database", (opt) => {
1774
1787
  import_neko_queue.QueueTransportFactory.register("memory", (opt) => {
1775
1788
  return new import_neko_queue.MemoryTransport(opt);
1776
1789
  });
1790
+ import_neko_queue.QueueTransportFactory.register("sqs", (opt) => {
1791
+ return new import_neko_queue.AwsSqsTransport(opt);
1792
+ });
1793
+ import_neko_queue.QueueTransportFactory.register("amqp", (opt) => {
1794
+ return new import_neko_queue.AmqpTransport(opt);
1795
+ });
1796
+ import_neko_queue.QueueTransportFactory.register("redis", (opt) => {
1797
+ return new import_neko_queue.RedisTransport(opt);
1798
+ });
1799
+ import_neko_queue.QueueTransportFactory.register("async", (opt) => {
1800
+ return new import_neko_queue.AsyncTransport();
1801
+ });
1802
+ import_neko_queue.QueueTransportFactory.register("azure_service_bus", (opt) => {
1803
+ return new import_neko_queue.AzureServiceBusTransport(opt);
1804
+ });
1805
+ import_neko_queue.QueueTransportFactory.register("google_pubsub", (opt) => {
1806
+ return new import_neko_queue.GooglePubSubTransport(opt);
1807
+ });
1777
1808
  var CacheProviderFactory = class _CacheProviderFactory {
1778
1809
  static {
1779
1810
  __name(this, "CacheProviderFactory");
@@ -1804,12 +1835,24 @@ import_neko_storage.StorageProviderFactory.register("local", (opt) => {
1804
1835
  import_neko_storage.StorageProviderFactory.register("s3", (opt) => {
1805
1836
  return new import_neko_storage.AWSS3StorageProvider(opt);
1806
1837
  });
1838
+ import_neko_storage.StorageProviderFactory.register("gcp", (opt) => {
1839
+ return new import_neko_storage.GCPStorageProvider(opt);
1840
+ });
1841
+ import_neko_storage.StorageProviderFactory.register("azure", (opt) => {
1842
+ return new import_neko_storage.AzureBlobStorageProvider(opt);
1843
+ });
1844
+ import_neko_storage.StorageProviderFactory.register("ftp", (opt) => {
1845
+ return new import_neko_storage.FTPStorageProvider(opt);
1846
+ });
1847
+ import_neko_storage.StorageProviderFactory.register("sftp", (opt) => {
1848
+ return new import_neko_storage.SFTPStorageProvider(opt);
1849
+ });
1807
1850
 
1808
1851
  // src/facades.mts
1809
1852
  var import_neko_cache2 = require("@devbro/neko-cache");
1810
1853
  var import_neko_queue2 = require("@devbro/neko-queue");
1811
- var router = (0, import_neko_helper2.createSingleton)(() => new Router());
1812
- var scheduler = (0, import_neko_helper2.createSingleton)(() => {
1854
+ var router = (0, import_neko_helper3.createSingleton)(() => new Router());
1855
+ var scheduler = (0, import_neko_helper3.createSingleton)(() => {
1813
1856
  const rc = new import_neko_scheduler.Scheduler();
1814
1857
  rc.setErrorHandler((err, job) => {
1815
1858
  logger().error({
@@ -1821,7 +1864,7 @@ var scheduler = (0, import_neko_helper2.createSingleton)(() => {
1821
1864
  return rc;
1822
1865
  });
1823
1866
  var db = /* @__PURE__ */ __name((label = "default") => (0, import_neko_context3.ctx)().getOrThrow(["database", label]), "db");
1824
- var storage = (0, import_neko_helper2.createSingleton)((label = "default") => {
1867
+ var storage = (0, import_neko_helper3.createSingleton)((label = "default") => {
1825
1868
  let storage_config = import_neko_config.config.get(["storages", label].join("."));
1826
1869
  const provider = import_neko_storage2.StorageProviderFactory.create(
1827
1870
  storage_config.provider,
@@ -1829,7 +1872,7 @@ var storage = (0, import_neko_helper2.createSingleton)((label = "default") => {
1829
1872
  );
1830
1873
  return new import_neko_storage2.Storage(provider);
1831
1874
  });
1832
- var cli = (0, import_neko_helper2.createSingleton)(() => {
1875
+ var cli = (0, import_neko_helper3.createSingleton)(() => {
1833
1876
  const [node, app, ...args] = process.argv;
1834
1877
  return new import_clipanion.Cli({
1835
1878
  binaryLabel: `My Application`,
@@ -1837,24 +1880,13 @@ var cli = (0, import_neko_helper2.createSingleton)(() => {
1837
1880
  binaryVersion: `1.0.0`
1838
1881
  });
1839
1882
  });
1840
- var httpServer = (0, import_neko_helper2.createSingleton)(() => {
1883
+ var httpServer = (0, import_neko_helper3.createSingleton)(() => {
1841
1884
  const server = new http_exports.HttpServer();
1842
- server.setErrorHandler(async (err, req, res) => {
1843
- if (err instanceof http_exports.HttpError) {
1844
- res.writeHead(err.statusCode, { "Content-Type": "application/json" });
1845
- res.end(JSON.stringify({ message: err.message, error: err.code }));
1846
- logger().warn({ msg: "HttpError: " + err.message, err });
1847
- return;
1848
- } else {
1849
- logger().error({ msg: "Error: " + err.message, err });
1850
- }
1851
- res.writeHead(500, { "Content-Type": "" });
1852
- res.end(JSON.stringify({ error: "Internal Server Error" }));
1853
- });
1885
+ server.setErrorHandler(handleHttpErrors);
1854
1886
  server.setRouter(router());
1855
1887
  return server;
1856
1888
  });
1857
- var logger = (0, import_neko_helper2.createSingleton)((label) => {
1889
+ var logger = (0, import_neko_helper3.createSingleton)((label) => {
1858
1890
  const logger_config = import_neko_config.config.get(["loggers", label].join("."));
1859
1891
  const rc = new import_neko_logger.Logger(logger_config);
1860
1892
  rc.setExtrasFunction((message) => {
@@ -1863,7 +1895,7 @@ var logger = (0, import_neko_helper2.createSingleton)((label) => {
1863
1895
  });
1864
1896
  return rc;
1865
1897
  });
1866
- var mailer = (0, import_neko_helper2.createSingleton)((label) => {
1898
+ var mailer = (0, import_neko_helper3.createSingleton)((label) => {
1867
1899
  const mailer_config = import_neko_config.config.get(["mailer", label].join("."));
1868
1900
  const provider = import_neko_mailer2.MailerProviderFactory.create(
1869
1901
  mailer_config.provider,
@@ -1872,7 +1904,7 @@ var mailer = (0, import_neko_helper2.createSingleton)((label) => {
1872
1904
  const rc = new import_neko_mailer2.Mailer(provider);
1873
1905
  return rc;
1874
1906
  });
1875
- var queue = (0, import_neko_helper2.createSingleton)((label) => {
1907
+ var queue = (0, import_neko_helper3.createSingleton)((label) => {
1876
1908
  const queue_config = import_neko_config.config.get(["queues", label].join("."));
1877
1909
  if (!queue_config) {
1878
1910
  throw new Error(`Queue configuration for '${label}' not found`);
@@ -1883,7 +1915,7 @@ var queue = (0, import_neko_helper2.createSingleton)((label) => {
1883
1915
  );
1884
1916
  return new import_neko_queue2.QueueConnection(provider);
1885
1917
  });
1886
- var cache = (0, import_neko_helper2.createSingleton)((label) => {
1918
+ var cache = (0, import_neko_helper3.createSingleton)((label) => {
1887
1919
  const cache_config = import_neko_config.config.get(["caches", label].join("."));
1888
1920
  if (!cache_config) {
1889
1921
  throw new Error(`Cache configuration for '${label}' not found`);
@@ -2108,12 +2140,15 @@ var StartCommand = class extends import_clipanion5.Command {
2108
2140
  __name(this, "StartCommand");
2109
2141
  }
2110
2142
  scheduler = import_clipanion5.Option.Boolean(`--scheduler`, false);
2143
+ cron = import_clipanion5.Option.Boolean(`--cron`, false);
2111
2144
  http = import_clipanion5.Option.Boolean(`--http`, false);
2112
2145
  queue = import_clipanion5.Option.Boolean(`--queue`, false);
2113
2146
  all = import_clipanion5.Option.Boolean("--all", false);
2114
2147
  static paths = [[`start`]];
2115
2148
  async execute() {
2116
- if ([this.all, this.http, this.scheduler, this.queue].filter((x) => x).length == 0) {
2149
+ if ([this.all, this.http, this.scheduler || this.cron, this.queue].filter(
2150
+ (x) => x
2151
+ ).length == 0) {
2117
2152
  this.context.stdout.write(
2118
2153
  `No service was selected. please check -h for details
2119
2154
  `
@@ -2123,7 +2158,7 @@ var StartCommand = class extends import_clipanion5.Command {
2123
2158
  logger().info(`Starting Server
2124
2159
  `);
2125
2160
  import_neko_sql.PostgresqlConnection.defaults.idleTimeoutMillis = 1e4;
2126
- if (this.scheduler || this.all) {
2161
+ if (this.scheduler || this.cron || this.all) {
2127
2162
  logger().info(`starting scheduler
2128
2163
  `);
2129
2164
  scheduler().start();
@@ -2321,144 +2356,155 @@ var config_exports = {};
2321
2356
  __reExport(config_exports, require("@devbro/neko-config"));
2322
2357
 
2323
2358
  // src/app/console/generate/GenerateApiDocsCommand.mts
2324
- var import_neko_helper3 = require("@devbro/neko-helper");
2359
+ var import_neko_helper4 = require("@devbro/neko-helper");
2325
2360
  var GenerateApiDocsCommand = class extends import_clipanion9.Command {
2326
2361
  static {
2327
2362
  __name(this, "GenerateApiDocsCommand");
2328
2363
  }
2329
- static paths = [
2330
- [`make`, `apidocs`],
2331
- [`generate`, `apidocs`]
2332
- ];
2364
+ static paths = [[`generate`, `apidocsv2`]];
2333
2365
  static usage = import_clipanion9.Command.Usage({
2334
2366
  category: `Generate`,
2335
2367
  description: `Generate OpenAPI documentation from routes`,
2336
2368
  details: `
2337
- This command generates OpenAPI 3.0 specification documentation by analyzing
2369
+ This command utility generates OpenAPI 3.0 specification documentation by analyzing
2338
2370
  your application's routes and merging with example files.
2339
2371
 
2340
- The generated documentation includes:
2341
- - All registered routes with their HTTP methods
2342
- - Path parameters extracted from route definitions
2343
- - Request body schemas for POST, PUT, and PATCH methods
2344
- - Response schemas
2345
-
2346
- The command will merge files specified in config.api_docs.merge_files
2347
- and output the final documentation to config.api_docs.output.
2372
+ Subcommands:
2373
+ - generate-from-routes: Generate OpenAPI spec from registered routes
2374
+ - generate-base: Generate base OpenAPI specification structure
2375
+ - merge-files: Merge multiple OpenAPI files into final documentation
2348
2376
 
2377
+
2349
2378
  This command depends on config data. make sure your default config contains the following:
2350
- api_docs: {
2351
- merge_files: [
2352
- path.join(__dirname, '../..', 'private', 'openapi_examples.json'),
2353
- path.join(__dirname, '../..', 'private', 'openapi_base.json'),
2354
- path.join(__dirname, '../..', 'private', 'openapi_user_changes.json'),
2355
- ],
2356
- output: path.join(__dirname, '../..', 'private', 'openapi.json'),
2357
- }
2379
+
2380
+ \`\`\`
2381
+ api_docs: {
2382
+
2383
+ merge_files: [
2384
+
2385
+ path.join(__dirname, '../..', 'private', 'openapi_base.json'),
2386
+
2387
+ path.join(__dirname, '../..', 'private', 'openapi_from_routes.json'),
2388
+
2389
+ path.join(__dirname, '../..', 'private', 'openapi_from_tests.json'),
2390
+
2391
+ path.join(__dirname, '../..', 'private', 'openapi_other_user_changes.json'),
2392
+
2393
+ ],
2394
+
2395
+ output: path.join(__dirname, '../..', 'public', 'openapi.json'),
2396
+
2397
+ }
2398
+
2399
+ \`\`\`
2358
2400
  `,
2359
- examples: [[`Generate API documentation`, `$0 generate apidocs`]]
2401
+ examples: [
2402
+ [
2403
+ `Generate from routes`,
2404
+ `$0 generate apidocsv2 generate-from-routes --output path/to/output.json`
2405
+ ],
2406
+ [
2407
+ `Generate base spec`,
2408
+ `$0 generate apidocsv2 generate-base --output path/to/output.json`
2409
+ ],
2410
+ [`Merge files`, `$0 generate apidocsv2 merge-files`],
2411
+ [`Show help`, `$0 generate apidocsv2 --help`]
2412
+ ]
2413
+ });
2414
+ subcommand = import_clipanion9.Option.String({ required: false });
2415
+ output = import_clipanion9.Option.String(`--output,-o`, {
2416
+ description: `Output file path for generated documentation`
2360
2417
  });
2361
- help = import_clipanion9.Option.Boolean(`--help,-h`, false, {
2362
- description: `Show help message for this command`
2418
+ config = import_clipanion9.Option.String(`--config,-c`, {
2419
+ description: `Path in config to get details from (default: api_docs)`,
2420
+ required: false
2363
2421
  });
2364
2422
  async execute() {
2365
- if (this.help) {
2423
+ if (!this.subcommand) {
2366
2424
  this.context.stdout.write(
2367
2425
  this.constructor.usage?.toString() || "No help available\n"
2368
2426
  );
2369
2427
  return 0;
2370
2428
  }
2371
- const rootDir = process.cwd();
2372
- this.context.stdout.write(`Generating OpenAPI documentation...
2429
+ switch (this.subcommand) {
2430
+ case "generate-from-routes":
2431
+ return await this.executeGenerateFromRoutes();
2432
+ case "generate-base":
2433
+ return await this.executeGenerateBase();
2434
+ case "merge-files":
2435
+ return await this.executeMergeFiles();
2436
+ default:
2437
+ this.context.stderr.write(`Unknown subcommand: ${this.subcommand}
2373
2438
  `);
2374
- const routes = router().routes;
2375
- const openApiSpec = {
2376
- openapi: "3.0.0",
2377
- info: {
2378
- title: "API Documentation",
2379
- version: "1.0.0",
2380
- description: "Auto-generated API documentation"
2381
- },
2382
- servers: [
2383
- {
2384
- url: "/",
2385
- description: "Local server"
2386
- }
2387
- ],
2388
- paths: {}
2389
- };
2390
- for (const route of routes) {
2391
- const routePath = route.path;
2392
- const openApiPath = routePath.replace(/:([a-zA-Z0-9_]+)/g, "{$1}");
2393
- if (!openApiSpec.paths[openApiPath]) {
2394
- openApiSpec.paths[openApiPath] = {};
2395
- }
2396
- for (const method of route.methods) {
2397
- const lowerMethod = method.toLowerCase();
2398
- if (lowerMethod === "head") {
2399
- continue;
2400
- }
2401
- openApiSpec.paths[openApiPath][lowerMethod] = {
2402
- summary: `${method} ${routePath}`,
2403
- description: `Endpoint for ${method} ${routePath}`,
2404
- parameters: this.extractParameters(routePath),
2405
- responses: {
2406
- "200": {
2407
- description: "Successful response",
2408
- content: {
2409
- "application/json": {
2410
- schema: {
2411
- type: "object"
2412
- }
2413
- }
2414
- }
2415
- },
2416
- "500": {
2417
- description: "Internal server error"
2418
- }
2419
- }
2420
- };
2421
- if (["post", "put", "patch"].includes(lowerMethod)) {
2422
- openApiSpec.paths[openApiPath][lowerMethod].requestBody = {
2423
- required: true,
2424
- content: {
2425
- "application/json": {
2426
- schema: {
2427
- type: "object"
2428
- }
2429
- }
2430
- }
2431
- };
2432
- }
2433
- }
2439
+ this.context.stdout.write(
2440
+ this.constructor.usage?.toString() || "No help available\n"
2441
+ );
2442
+ return 1;
2434
2443
  }
2435
- await fs6.mkdir(config_exports.config.get("private_path"), { recursive: true });
2436
- const outputPath = import_path7.default.join(config_exports.config.get("private_path"), "openapi.json");
2444
+ }
2445
+ async executeGenerateFromRoutes() {
2446
+ this.context.stdout.write(
2447
+ `Generating OpenAPI documentation from routes...
2448
+ `
2449
+ );
2450
+ const openApiSpec = this.generateFromRoutes();
2451
+ const outputPath = this.output || import_path7.default.join(config_exports.config.get("private_path"), "openapi_from_routes.json");
2452
+ await fs6.mkdir(import_path7.default.dirname(outputPath), { recursive: true });
2437
2453
  await fs6.writeFile(
2438
2454
  outputPath,
2439
2455
  JSON.stringify(openApiSpec, null, 2),
2440
2456
  "utf-8"
2441
2457
  );
2442
2458
  this.context.stdout.write(
2443
- `OpenAPI documentation generated at: ${outputPath}
2459
+ `OpenAPI routes documentation generated at: ${outputPath}
2460
+ `
2461
+ );
2462
+ this.context.stdout.write(
2463
+ `Total routes documented: ${Object.keys(openApiSpec.paths).length}
2464
+ `
2465
+ );
2466
+ return 0;
2467
+ }
2468
+ async executeGenerateBase() {
2469
+ this.context.stdout.write(`Generating base OpenAPI specification...
2470
+ `);
2471
+ const baseSpec = this.getBaseOpenApiSpec();
2472
+ const outputPath = this.output || import_path7.default.join(config_exports.config.get("private_path"), "openapi_base.json");
2473
+ await fs6.mkdir(import_path7.default.dirname(outputPath), { recursive: true });
2474
+ await fs6.writeFile(outputPath, JSON.stringify(baseSpec, null, 2), "utf-8");
2475
+ this.context.stdout.write(
2476
+ `Base OpenAPI specification generated at: ${outputPath}
2444
2477
  `
2445
2478
  );
2446
- this.context.stdout.write(`Total routes documented: ${routes.length}
2479
+ return 0;
2480
+ }
2481
+ async executeMergeFiles() {
2482
+ this.context.stdout.write(`Merging OpenAPI files...
2447
2483
  `);
2448
- let files_to_merge = config_exports.config.get("api_docs.merge_files");
2484
+ let configPath = this.config || "api_docs";
2485
+ const files_to_merge = config_exports.config.get(`${configPath}.merge_files`);
2449
2486
  let final_api_docs = {};
2450
- for (let file_path of files_to_merge) {
2451
- let file_json = JSON.parse(await fs6.readFile(file_path, "utf8"));
2452
- final_api_docs = import_neko_helper3.Arr.deepMerge(final_api_docs, file_json);
2487
+ for (const file_path of files_to_merge) {
2488
+ try {
2489
+ const file_json = JSON.parse(await fs6.readFile(file_path, "utf8"));
2490
+ final_api_docs = import_neko_helper4.Arr.deepMerge(final_api_docs, file_json);
2491
+ this.context.stdout.write(` Merged: ${file_path}
2492
+ `);
2493
+ } catch (error) {
2494
+ this.context.stderr.write(
2495
+ ` Warning: Could not read ${file_path}: ${error.message}
2496
+ `
2497
+ );
2498
+ }
2453
2499
  }
2454
- await fs6.writeFile(
2455
- config_exports.config.get("api_docs.output"),
2456
- JSON.stringify(final_api_docs, null, 2)
2457
- );
2500
+ const outputPath = this.output || config_exports.config.get(`${configPath}.output`);
2501
+ await fs6.mkdir(import_path7.default.dirname(outputPath), { recursive: true });
2502
+ await fs6.writeFile(outputPath, JSON.stringify(final_api_docs, null, 2));
2458
2503
  this.context.stdout.write(
2459
- `wrote final open api document to : ${config_exports.config.get("api_docs.output")}
2504
+ `Final OpenAPI document written to: ${outputPath}
2460
2505
  `
2461
2506
  );
2507
+ return 0;
2462
2508
  }
2463
2509
  extractParameters(routePath) {
2464
2510
  const paramRegex = /:([a-zA-Z0-9_]+)/g;
@@ -2477,6 +2523,78 @@ var GenerateApiDocsCommand = class extends import_clipanion9.Command {
2477
2523
  }
2478
2524
  return parameters;
2479
2525
  }
2526
+ generateFromRoutes() {
2527
+ const openApiSpec = {
2528
+ paths: {}
2529
+ };
2530
+ const routes = router().routes;
2531
+ for (const route of routes) {
2532
+ const routePath = route.path;
2533
+ const openApiPath = routePath.replace(/\/$/g, "");
2534
+ if (!openApiSpec.paths[openApiPath]) {
2535
+ openApiSpec.paths[openApiPath] = {};
2536
+ }
2537
+ for (const method of route.methods) {
2538
+ const lowerMethod = method.toLowerCase();
2539
+ if (lowerMethod === "head") {
2540
+ continue;
2541
+ }
2542
+ openApiSpec.paths[openApiPath][lowerMethod] = {
2543
+ summary: `${routePath}`,
2544
+ description: `Endpoint for ${method} ${routePath}`,
2545
+ security: [],
2546
+ parameters: this.extractParameters(routePath),
2547
+ responses: {}
2548
+ };
2549
+ if (["post", "put", "patch"].includes(lowerMethod)) {
2550
+ openApiSpec.paths[openApiPath][lowerMethod].requestBody = {
2551
+ required: true,
2552
+ content: {
2553
+ "application/json": {
2554
+ schema: {
2555
+ type: "object"
2556
+ }
2557
+ }
2558
+ }
2559
+ };
2560
+ }
2561
+ }
2562
+ }
2563
+ return openApiSpec;
2564
+ }
2565
+ getBaseOpenApiSpec() {
2566
+ const openApiSpec = {
2567
+ openapi: "3.0.0",
2568
+ info: {
2569
+ title: "API Documentation",
2570
+ version: "1.0.0",
2571
+ description: "Auto-generated API documentation"
2572
+ },
2573
+ servers: [
2574
+ {
2575
+ url: "/",
2576
+ description: "Local server"
2577
+ }
2578
+ ],
2579
+ components: {
2580
+ securitySchemes: {
2581
+ bearerAuth: {
2582
+ type: "http",
2583
+ scheme: "bearer",
2584
+ bearerFormat: "JWT",
2585
+ description: "JWT token authentication"
2586
+ }
2587
+ }
2588
+ },
2589
+ security: [
2590
+ {
2591
+ bearerAuth: []
2592
+ }
2593
+ ],
2594
+ paths: {}
2595
+ };
2596
+ return openApiSpec;
2597
+ }
2480
2598
  };
2481
2599
  cli().register(GenerateApiDocsCommand);
2482
2600