@devbro/pashmak 0.1.48 → 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 (38) 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 +45 -17
  13. package/dist/bin/app/console/KeyGenerateCommand.cjs +45 -17
  14. package/dist/bin/app/console/StartCommand.cjs +50 -19
  15. package/dist/bin/app/console/generate/GenerateApiDocsCommand.cjs +229 -118
  16. package/dist/bin/app/console/generate/GenerateControllerCommand.cjs +45 -17
  17. package/dist/bin/app/console/generate/index.cjs +229 -118
  18. package/dist/bin/app/console/index.cjs +234 -120
  19. package/dist/bin/app/console/migrate/GenerateMigrateCommand.cjs +45 -17
  20. package/dist/bin/app/console/migrate/MigrateCommand.cjs +45 -17
  21. package/dist/bin/app/console/migrate/MigrateRollbackCommand.cjs +45 -17
  22. package/dist/bin/app/console/migrate/index.cjs +45 -17
  23. package/dist/bin/app/console/queue/GenerateQueueMigrateCommand.cjs +45 -17
  24. package/dist/bin/cache.cjs +45 -17
  25. package/dist/bin/facades.cjs +45 -17
  26. package/dist/bin/factories.cjs +45 -17
  27. package/dist/bin/http.cjs +45 -17
  28. package/dist/bin/index.cjs +243 -126
  29. package/dist/bin/middlewares.cjs +45 -17
  30. package/dist/bin/queue.cjs +45 -17
  31. package/dist/bin/router.cjs +3 -5
  32. package/dist/factories.mjs +45 -2
  33. package/dist/factories.mjs.map +1 -1
  34. package/dist/http.mjs +1 -1
  35. package/dist/http.mjs.map +1 -1
  36. package/dist/queue.d.mts +1 -1
  37. package/dist/queue.mjs.map +1 -1
  38. 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,9 +1271,6 @@ 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
1275
  return await this.runMiddlewares(this.middlewares, this.request, this.response);
1278
1276
  }
@@ -1408,7 +1406,7 @@ var Route = class {
1408
1406
  i = start;
1409
1407
  } else if (char === "*") {
1410
1408
  let start = i + 1;
1411
- while (start < path10.length && /[a-zA-Z0-9_\.]/.test(path10[start])) {
1409
+ while (start < path10.length && /[a-zA-Z0-9_]/.test(path10[start])) {
1412
1410
  start++;
1413
1411
  }
1414
1412
  tokens.push({ type: "WILDCARD", value: path10.slice(i + 1, start) });
@@ -1619,7 +1617,7 @@ var Router = class {
1619
1617
 
1620
1618
  // src/facades.mts
1621
1619
  var import_neko_scheduler = require("@devbro/neko-scheduler");
1622
- var import_neko_helper2 = require("@devbro/neko-helper");
1620
+ var import_neko_helper3 = require("@devbro/neko-helper");
1623
1621
  var import_neko_context3 = require("@devbro/neko-context");
1624
1622
  var import_neko_storage2 = require("@devbro/neko-storage");
1625
1623
  var import_neko_mailer2 = require("@devbro/neko-mailer");
@@ -1660,7 +1658,7 @@ __export(queue_exports, {
1660
1658
  DatabaseTransport: () => DatabaseTransport
1661
1659
  });
1662
1660
  __reExport(queue_exports, require("@devbro/neko-queue"));
1663
- var import_neko_helper = require("@devbro/neko-helper");
1661
+ var import_neko_helper2 = require("@devbro/neko-helper");
1664
1662
  var import_neko_context2 = require("@devbro/neko-context");
1665
1663
  var DatabaseTransport = class {
1666
1664
  static {
@@ -1721,7 +1719,7 @@ var DatabaseTransport = class {
1721
1719
  }, "processMessage");
1722
1720
  constructor(config9 = {}) {
1723
1721
  this.config = { ...this.config, ...config9 };
1724
- this.repeater = (0, import_neko_helper.createRepeater)(
1722
+ this.repeater = (0, import_neko_helper2.createRepeater)(
1725
1723
  this.processMessage,
1726
1724
  this.config.listen_interval * 1e3
1727
1725
  );
@@ -1789,6 +1787,24 @@ import_neko_queue.QueueTransportFactory.register("database", (opt) => {
1789
1787
  import_neko_queue.QueueTransportFactory.register("memory", (opt) => {
1790
1788
  return new import_neko_queue.MemoryTransport(opt);
1791
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
+ });
1792
1808
  var CacheProviderFactory = class _CacheProviderFactory {
1793
1809
  static {
1794
1810
  __name(this, "CacheProviderFactory");
@@ -1819,12 +1835,24 @@ import_neko_storage.StorageProviderFactory.register("local", (opt) => {
1819
1835
  import_neko_storage.StorageProviderFactory.register("s3", (opt) => {
1820
1836
  return new import_neko_storage.AWSS3StorageProvider(opt);
1821
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
+ });
1822
1850
 
1823
1851
  // src/facades.mts
1824
1852
  var import_neko_cache2 = require("@devbro/neko-cache");
1825
1853
  var import_neko_queue2 = require("@devbro/neko-queue");
1826
- var router = (0, import_neko_helper2.createSingleton)(() => new Router());
1827
- 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)(() => {
1828
1856
  const rc = new import_neko_scheduler.Scheduler();
1829
1857
  rc.setErrorHandler((err, job) => {
1830
1858
  logger().error({
@@ -1836,7 +1864,7 @@ var scheduler = (0, import_neko_helper2.createSingleton)(() => {
1836
1864
  return rc;
1837
1865
  });
1838
1866
  var db = /* @__PURE__ */ __name((label = "default") => (0, import_neko_context3.ctx)().getOrThrow(["database", label]), "db");
1839
- var storage = (0, import_neko_helper2.createSingleton)((label = "default") => {
1867
+ var storage = (0, import_neko_helper3.createSingleton)((label = "default") => {
1840
1868
  let storage_config = import_neko_config.config.get(["storages", label].join("."));
1841
1869
  const provider = import_neko_storage2.StorageProviderFactory.create(
1842
1870
  storage_config.provider,
@@ -1844,7 +1872,7 @@ var storage = (0, import_neko_helper2.createSingleton)((label = "default") => {
1844
1872
  );
1845
1873
  return new import_neko_storage2.Storage(provider);
1846
1874
  });
1847
- var cli = (0, import_neko_helper2.createSingleton)(() => {
1875
+ var cli = (0, import_neko_helper3.createSingleton)(() => {
1848
1876
  const [node, app, ...args] = process.argv;
1849
1877
  return new import_clipanion.Cli({
1850
1878
  binaryLabel: `My Application`,
@@ -1852,13 +1880,13 @@ var cli = (0, import_neko_helper2.createSingleton)(() => {
1852
1880
  binaryVersion: `1.0.0`
1853
1881
  });
1854
1882
  });
1855
- var httpServer = (0, import_neko_helper2.createSingleton)(() => {
1883
+ var httpServer = (0, import_neko_helper3.createSingleton)(() => {
1856
1884
  const server = new http_exports.HttpServer();
1857
1885
  server.setErrorHandler(handleHttpErrors);
1858
1886
  server.setRouter(router());
1859
1887
  return server;
1860
1888
  });
1861
- var logger = (0, import_neko_helper2.createSingleton)((label) => {
1889
+ var logger = (0, import_neko_helper3.createSingleton)((label) => {
1862
1890
  const logger_config = import_neko_config.config.get(["loggers", label].join("."));
1863
1891
  const rc = new import_neko_logger.Logger(logger_config);
1864
1892
  rc.setExtrasFunction((message) => {
@@ -1867,7 +1895,7 @@ var logger = (0, import_neko_helper2.createSingleton)((label) => {
1867
1895
  });
1868
1896
  return rc;
1869
1897
  });
1870
- var mailer = (0, import_neko_helper2.createSingleton)((label) => {
1898
+ var mailer = (0, import_neko_helper3.createSingleton)((label) => {
1871
1899
  const mailer_config = import_neko_config.config.get(["mailer", label].join("."));
1872
1900
  const provider = import_neko_mailer2.MailerProviderFactory.create(
1873
1901
  mailer_config.provider,
@@ -1876,7 +1904,7 @@ var mailer = (0, import_neko_helper2.createSingleton)((label) => {
1876
1904
  const rc = new import_neko_mailer2.Mailer(provider);
1877
1905
  return rc;
1878
1906
  });
1879
- var queue = (0, import_neko_helper2.createSingleton)((label) => {
1907
+ var queue = (0, import_neko_helper3.createSingleton)((label) => {
1880
1908
  const queue_config = import_neko_config.config.get(["queues", label].join("."));
1881
1909
  if (!queue_config) {
1882
1910
  throw new Error(`Queue configuration for '${label}' not found`);
@@ -1887,7 +1915,7 @@ var queue = (0, import_neko_helper2.createSingleton)((label) => {
1887
1915
  );
1888
1916
  return new import_neko_queue2.QueueConnection(provider);
1889
1917
  });
1890
- var cache = (0, import_neko_helper2.createSingleton)((label) => {
1918
+ var cache = (0, import_neko_helper3.createSingleton)((label) => {
1891
1919
  const cache_config = import_neko_config.config.get(["caches", label].join("."));
1892
1920
  if (!cache_config) {
1893
1921
  throw new Error(`Cache configuration for '${label}' not found`);
@@ -2112,12 +2140,15 @@ var StartCommand = class extends import_clipanion5.Command {
2112
2140
  __name(this, "StartCommand");
2113
2141
  }
2114
2142
  scheduler = import_clipanion5.Option.Boolean(`--scheduler`, false);
2143
+ cron = import_clipanion5.Option.Boolean(`--cron`, false);
2115
2144
  http = import_clipanion5.Option.Boolean(`--http`, false);
2116
2145
  queue = import_clipanion5.Option.Boolean(`--queue`, false);
2117
2146
  all = import_clipanion5.Option.Boolean("--all", false);
2118
2147
  static paths = [[`start`]];
2119
2148
  async execute() {
2120
- 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) {
2121
2152
  this.context.stdout.write(
2122
2153
  `No service was selected. please check -h for details
2123
2154
  `
@@ -2127,7 +2158,7 @@ var StartCommand = class extends import_clipanion5.Command {
2127
2158
  logger().info(`Starting Server
2128
2159
  `);
2129
2160
  import_neko_sql.PostgresqlConnection.defaults.idleTimeoutMillis = 1e4;
2130
- if (this.scheduler || this.all) {
2161
+ if (this.scheduler || this.cron || this.all) {
2131
2162
  logger().info(`starting scheduler
2132
2163
  `);
2133
2164
  scheduler().start();
@@ -2325,144 +2356,155 @@ var config_exports = {};
2325
2356
  __reExport(config_exports, require("@devbro/neko-config"));
2326
2357
 
2327
2358
  // src/app/console/generate/GenerateApiDocsCommand.mts
2328
- var import_neko_helper3 = require("@devbro/neko-helper");
2359
+ var import_neko_helper4 = require("@devbro/neko-helper");
2329
2360
  var GenerateApiDocsCommand = class extends import_clipanion9.Command {
2330
2361
  static {
2331
2362
  __name(this, "GenerateApiDocsCommand");
2332
2363
  }
2333
- static paths = [
2334
- [`make`, `apidocs`],
2335
- [`generate`, `apidocs`]
2336
- ];
2364
+ static paths = [[`generate`, `apidocsv2`]];
2337
2365
  static usage = import_clipanion9.Command.Usage({
2338
2366
  category: `Generate`,
2339
2367
  description: `Generate OpenAPI documentation from routes`,
2340
2368
  details: `
2341
- This command generates OpenAPI 3.0 specification documentation by analyzing
2369
+ This command utility generates OpenAPI 3.0 specification documentation by analyzing
2342
2370
  your application's routes and merging with example files.
2343
2371
 
2344
- The generated documentation includes:
2345
- - All registered routes with their HTTP methods
2346
- - Path parameters extracted from route definitions
2347
- - Request body schemas for POST, PUT, and PATCH methods
2348
- - Response schemas
2349
-
2350
- The command will merge files specified in config.api_docs.merge_files
2351
- 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
2352
2376
 
2377
+
2353
2378
  This command depends on config data. make sure your default config contains the following:
2354
- api_docs: {
2355
- merge_files: [
2356
- path.join(__dirname, '../..', 'private', 'openapi_examples.json'),
2357
- path.join(__dirname, '../..', 'private', 'openapi_base.json'),
2358
- path.join(__dirname, '../..', 'private', 'openapi_user_changes.json'),
2359
- ],
2360
- output: path.join(__dirname, '../..', 'private', 'openapi.json'),
2361
- }
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
+ \`\`\`
2362
2400
  `,
2363
- 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
+ ]
2364
2413
  });
2365
- help = import_clipanion9.Option.Boolean(`--help,-h`, false, {
2366
- description: `Show help message for this command`
2414
+ subcommand = import_clipanion9.Option.String({ required: false });
2415
+ output = import_clipanion9.Option.String(`--output,-o`, {
2416
+ description: `Output file path for generated documentation`
2417
+ });
2418
+ config = import_clipanion9.Option.String(`--config,-c`, {
2419
+ description: `Path in config to get details from (default: api_docs)`,
2420
+ required: false
2367
2421
  });
2368
2422
  async execute() {
2369
- if (this.help) {
2423
+ if (!this.subcommand) {
2370
2424
  this.context.stdout.write(
2371
2425
  this.constructor.usage?.toString() || "No help available\n"
2372
2426
  );
2373
2427
  return 0;
2374
2428
  }
2375
- const rootDir = process.cwd();
2376
- 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}
2377
2438
  `);
2378
- const routes = router().routes;
2379
- const openApiSpec = {
2380
- openapi: "3.0.0",
2381
- info: {
2382
- title: "API Documentation",
2383
- version: "1.0.0",
2384
- description: "Auto-generated API documentation"
2385
- },
2386
- servers: [
2387
- {
2388
- url: "/",
2389
- description: "Local server"
2390
- }
2391
- ],
2392
- paths: {}
2393
- };
2394
- for (const route of routes) {
2395
- const routePath = route.path;
2396
- const openApiPath = routePath.replace(/:([a-zA-Z0-9_]+)/g, "{$1}");
2397
- if (!openApiSpec.paths[openApiPath]) {
2398
- openApiSpec.paths[openApiPath] = {};
2399
- }
2400
- for (const method of route.methods) {
2401
- const lowerMethod = method.toLowerCase();
2402
- if (lowerMethod === "head") {
2403
- continue;
2404
- }
2405
- openApiSpec.paths[openApiPath][lowerMethod] = {
2406
- summary: `${method} ${routePath}`,
2407
- description: `Endpoint for ${method} ${routePath}`,
2408
- parameters: this.extractParameters(routePath),
2409
- responses: {
2410
- "200": {
2411
- description: "Successful response",
2412
- content: {
2413
- "application/json": {
2414
- schema: {
2415
- type: "object"
2416
- }
2417
- }
2418
- }
2419
- },
2420
- "500": {
2421
- description: "Internal server error"
2422
- }
2423
- }
2424
- };
2425
- if (["post", "put", "patch"].includes(lowerMethod)) {
2426
- openApiSpec.paths[openApiPath][lowerMethod].requestBody = {
2427
- required: true,
2428
- content: {
2429
- "application/json": {
2430
- schema: {
2431
- type: "object"
2432
- }
2433
- }
2434
- }
2435
- };
2436
- }
2437
- }
2439
+ this.context.stdout.write(
2440
+ this.constructor.usage?.toString() || "No help available\n"
2441
+ );
2442
+ return 1;
2438
2443
  }
2439
- await fs6.mkdir(config_exports.config.get("private_path"), { recursive: true });
2440
- 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 });
2441
2453
  await fs6.writeFile(
2442
2454
  outputPath,
2443
2455
  JSON.stringify(openApiSpec, null, 2),
2444
2456
  "utf-8"
2445
2457
  );
2446
2458
  this.context.stdout.write(
2447
- `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}
2448
2477
  `
2449
2478
  );
2450
- this.context.stdout.write(`Total routes documented: ${routes.length}
2479
+ return 0;
2480
+ }
2481
+ async executeMergeFiles() {
2482
+ this.context.stdout.write(`Merging OpenAPI files...
2451
2483
  `);
2452
- 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`);
2453
2486
  let final_api_docs = {};
2454
- for (let file_path of files_to_merge) {
2455
- let file_json = JSON.parse(await fs6.readFile(file_path, "utf8"));
2456
- 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
+ }
2457
2499
  }
2458
- await fs6.writeFile(
2459
- config_exports.config.get("api_docs.output"),
2460
- JSON.stringify(final_api_docs, null, 2)
2461
- );
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));
2462
2503
  this.context.stdout.write(
2463
- `wrote final open api document to : ${config_exports.config.get("api_docs.output")}
2504
+ `Final OpenAPI document written to: ${outputPath}
2464
2505
  `
2465
2506
  );
2507
+ return 0;
2466
2508
  }
2467
2509
  extractParameters(routePath) {
2468
2510
  const paramRegex = /:([a-zA-Z0-9_]+)/g;
@@ -2481,6 +2523,78 @@ var GenerateApiDocsCommand = class extends import_clipanion9.Command {
2481
2523
  }
2482
2524
  return parameters;
2483
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
+ }
2484
2598
  };
2485
2599
  cli().register(GenerateApiDocsCommand);
2486
2600