@devbro/pashmak 0.1.49 → 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.
- package/dist/DatabaseServiceProvider.d.mts +2 -2
- package/dist/DatabaseServiceProvider.mjs +5 -1
- package/dist/DatabaseServiceProvider.mjs.map +1 -1
- package/dist/app/console/generate/GenerateApiDocsCommand.mjs +5 -5
- package/dist/app/console/generate/GenerateApiDocsCommand.mjs.map +1 -1
- package/dist/bin/DatabaseServiceProvider.cjs +4 -0
- package/dist/bin/app/console/DefaultCommand.cjs +97 -58
- package/dist/bin/app/console/KeyGenerateCommand.cjs +97 -58
- package/dist/bin/app/console/StartCommand.cjs +97 -58
- package/dist/bin/app/console/generate/GenerateApiDocsCommand.cjs +102 -63
- package/dist/bin/app/console/generate/GenerateControllerCommand.cjs +97 -58
- package/dist/bin/app/console/generate/index.cjs +102 -63
- package/dist/bin/app/console/index.cjs +102 -63
- package/dist/bin/app/console/migrate/GenerateMigrateCommand.cjs +97 -58
- package/dist/bin/app/console/migrate/MigrateCommand.cjs +97 -58
- package/dist/bin/app/console/migrate/MigrateRollbackCommand.cjs +97 -58
- package/dist/bin/app/console/migrate/index.cjs +97 -58
- package/dist/bin/app/console/queue/GenerateQueueMigrateCommand.cjs +97 -58
- package/dist/bin/cache.cjs +97 -58
- package/dist/bin/facades.cjs +97 -58
- package/dist/bin/factories.cjs +97 -58
- package/dist/bin/http.cjs +97 -58
- package/dist/bin/index.cjs +106 -63
- package/dist/bin/middlewares.cjs +97 -58
- package/dist/bin/queue.cjs +97 -58
- package/dist/facades.d.mts +6 -6
- package/dist/facades.mjs +97 -58
- package/dist/facades.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Middleware, Request, Response } from '@devbro/neko-router';
|
|
2
|
+
import { SqliteConfig, Connection } from '@devbro/neko-sql';
|
|
2
3
|
import { PoolConfig } from 'pg';
|
|
3
|
-
import { Connection } from '@devbro/neko-sql';
|
|
4
4
|
|
|
5
5
|
declare class DatabaseServiceProvider extends Middleware {
|
|
6
6
|
call(req: Request, res: Response, next: () => Promise<void>): Promise<void>;
|
|
@@ -9,7 +9,7 @@ declare class DatabaseServiceProvider extends Middleware {
|
|
|
9
9
|
static getInstance(): DatabaseServiceProvider;
|
|
10
10
|
getConnection(db_config: {
|
|
11
11
|
provider: string;
|
|
12
|
-
config: PoolConfig;
|
|
12
|
+
config: PoolConfig | SqliteConfig;
|
|
13
13
|
}): Connection;
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
import { Middleware } from "@devbro/neko-router";
|
|
4
|
-
import { PostgresqlConnection } from "@devbro/neko-sql";
|
|
4
|
+
import { PostgresqlConnection, SqliteConnection } from "@devbro/neko-sql";
|
|
5
5
|
import { BaseModel } from "@devbro/neko-orm";
|
|
6
6
|
import { ctx } from "@devbro/neko-context";
|
|
7
7
|
import { config } from "@devbro/neko-config";
|
|
@@ -53,6 +53,10 @@ class DatabaseServiceProvider extends Middleware {
|
|
|
53
53
|
const conn = new PostgresqlConnection(db_config.config);
|
|
54
54
|
return conn;
|
|
55
55
|
}
|
|
56
|
+
if (db_config.provider === "sqlite") {
|
|
57
|
+
const conn = new SqliteConnection(db_config.config);
|
|
58
|
+
return conn;
|
|
59
|
+
}
|
|
56
60
|
throw new Error(`Unsupported database provider: ${db_config.provider}`);
|
|
57
61
|
}
|
|
58
62
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/DatabaseServiceProvider.mts"],"sourcesContent":["import { Middleware } from \"@devbro/neko-router\";\nimport { Request, Response } from \"@devbro/neko-router\";\nimport { PostgresqlConnection } from \"@devbro/neko-sql\";\nimport { PoolConfig } from \"pg\";\nimport { Connection } from \"@devbro/neko-sql\";\nimport { BaseModel } from \"@devbro/neko-orm\";\nimport { ctx } from \"@devbro/neko-context\";\nimport { config } from \"@devbro/neko-config\";\nimport { Global } from \"./global.mjs\";\n\nexport class DatabaseServiceProvider extends Middleware {\n async call(\n req: Request,\n res: Response,\n next: () => Promise<void>,\n ): Promise<void> {\n const db_configs: Record<string, { provider: string; config: PoolConfig }> =\n config.get(\"databases\");\n\n const conns = [];\n try {\n for (const [name, db_config] of Object.entries(db_configs)) {\n const conn = await this.getConnection(db_config);\n ctx().set([\"database\", name], conn);\n conns.push(conn);\n }\n BaseModel.setConnection(() => {\n const key = [\"database\", \"default\"];\n let rc: Connection | undefined;\n\n if (ctx.isActive()) {\n rc = ctx().get<Connection>(key);\n } else if (Global.has(key)) {\n rc = Global.get<Connection>(key);\n } else {\n rc = this.getConnection(db_configs[\"default\"]);\n Global.set(key, rc);\n }\n\n return rc!;\n });\n await next();\n } finally {\n for (const conn of conns) {\n await conn.disconnect();\n }\n }\n }\n\n private static instance: DatabaseServiceProvider;\n\n async register(): Promise<void> {}\n\n static getInstance(): DatabaseServiceProvider {\n if (!DatabaseServiceProvider.instance) {\n DatabaseServiceProvider.instance = new DatabaseServiceProvider();\n }\n return DatabaseServiceProvider.instance;\n }\n\n getConnection(db_config: {\n provider: string;\n config: PoolConfig;\n }): Connection {\n if (db_config.provider === \"postgresql\") {\n const conn = new PostgresqlConnection(db_config.config);\n return conn;\n }\n\n throw new Error(`Unsupported database provider: ${db_config.provider}`);\n }\n}\n"],"mappings":";;AAAA,SAAS,kBAAkB;AAE3B,SAAS,
|
|
1
|
+
{"version":3,"sources":["../src/DatabaseServiceProvider.mts"],"sourcesContent":["import { Middleware } from \"@devbro/neko-router\";\nimport { Request, Response } from \"@devbro/neko-router\";\nimport { PostgresqlConnection, SqliteConnection, SqliteConfig } from \"@devbro/neko-sql\";\nimport { PoolConfig } from \"pg\";\nimport { Connection } from \"@devbro/neko-sql\";\nimport { BaseModel } from \"@devbro/neko-orm\";\nimport { ctx } from \"@devbro/neko-context\";\nimport { config } from \"@devbro/neko-config\";\nimport { Global } from \"./global.mjs\";\n\nexport class DatabaseServiceProvider extends Middleware {\n async call(\n req: Request,\n res: Response,\n next: () => Promise<void>,\n ): Promise<void> {\n const db_configs: Record<string, { provider: string; config: PoolConfig | SqliteConfig }> =\n config.get(\"databases\");\n\n const conns = [];\n try {\n for (const [name, db_config] of Object.entries(db_configs)) {\n const conn = await this.getConnection(db_config);\n ctx().set([\"database\", name], conn);\n conns.push(conn);\n }\n BaseModel.setConnection(() => {\n const key = [\"database\", \"default\"];\n let rc: Connection | undefined;\n\n if (ctx.isActive()) {\n rc = ctx().get<Connection>(key);\n } else if (Global.has(key)) {\n rc = Global.get<Connection>(key);\n } else {\n rc = this.getConnection(db_configs[\"default\"]);\n Global.set(key, rc);\n }\n\n return rc!;\n });\n await next();\n } finally {\n for (const conn of conns) {\n await conn.disconnect();\n }\n }\n }\n\n private static instance: DatabaseServiceProvider;\n\n async register(): Promise<void> {}\n\n static getInstance(): DatabaseServiceProvider {\n if (!DatabaseServiceProvider.instance) {\n DatabaseServiceProvider.instance = new DatabaseServiceProvider();\n }\n return DatabaseServiceProvider.instance;\n }\n\n getConnection(db_config: {\n provider: string;\n config: PoolConfig | SqliteConfig;\n }): Connection {\n if (db_config.provider === \"postgresql\") {\n const conn = new PostgresqlConnection(db_config.config as PoolConfig);\n return conn;\n }\n\n if (db_config.provider === \"sqlite\") {\n const conn = new SqliteConnection(db_config.config as SqliteConfig);\n return conn;\n }\n\n throw new Error(`Unsupported database provider: ${db_config.provider}`);\n }\n}\n"],"mappings":";;AAAA,SAAS,kBAAkB;AAE3B,SAAS,sBAAsB,wBAAsC;AAGrE,SAAS,iBAAiB;AAC1B,SAAS,WAAW;AACpB,SAAS,cAAc;AACvB,SAAS,cAAc;AAEhB,MAAM,gCAAgC,WAAW;AAAA,EAVxD,OAUwD;AAAA;AAAA;AAAA,EACtD,MAAM,KACJ,KACA,KACA,MACe;AACf,UAAM,aACJ,OAAO,IAAI,WAAW;AAExB,UAAM,QAAQ,CAAC;AACf,QAAI;AACF,iBAAW,CAAC,MAAM,SAAS,KAAK,OAAO,QAAQ,UAAU,GAAG;AAC1D,cAAM,OAAO,MAAM,KAAK,cAAc,SAAS;AAC/C,YAAI,EAAE,IAAI,CAAC,YAAY,IAAI,GAAG,IAAI;AAClC,cAAM,KAAK,IAAI;AAAA,MACjB;AACA,gBAAU,cAAc,MAAM;AAC5B,cAAM,MAAM,CAAC,YAAY,SAAS;AAClC,YAAI;AAEJ,YAAI,IAAI,SAAS,GAAG;AAClB,eAAK,IAAI,EAAE,IAAgB,GAAG;AAAA,QAChC,WAAW,OAAO,IAAI,GAAG,GAAG;AAC1B,eAAK,OAAO,IAAgB,GAAG;AAAA,QACjC,OAAO;AACL,eAAK,KAAK,cAAc,WAAW,SAAS,CAAC;AAC7C,iBAAO,IAAI,KAAK,EAAE;AAAA,QACpB;AAEA,eAAO;AAAA,MACT,CAAC;AACD,YAAM,KAAK;AAAA,IACb,UAAE;AACA,iBAAW,QAAQ,OAAO;AACxB,cAAM,KAAK,WAAW;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAe;AAAA,EAEf,MAAM,WAA0B;AAAA,EAAC;AAAA,EAEjC,OAAO,cAAuC;AAC5C,QAAI,CAAC,wBAAwB,UAAU;AACrC,8BAAwB,WAAW,IAAI,wBAAwB;AAAA,IACjE;AACA,WAAO,wBAAwB;AAAA,EACjC;AAAA,EAEA,cAAc,WAGC;AACb,QAAI,UAAU,aAAa,cAAc;AACvC,YAAM,OAAO,IAAI,qBAAqB,UAAU,MAAoB;AACpE,aAAO;AAAA,IACT;AAEA,QAAI,UAAU,aAAa,UAAU;AACnC,YAAM,OAAO,IAAI,iBAAiB,UAAU,MAAsB;AAClE,aAAO;AAAA,IACT;AAEA,UAAM,IAAI,MAAM,kCAAkC,UAAU,QAAQ,EAAE;AAAA,EACxE;AACF;","names":[]}
|
|
@@ -10,7 +10,7 @@ class GenerateApiDocsCommand extends Command {
|
|
|
10
10
|
static {
|
|
11
11
|
__name(this, "GenerateApiDocsCommand");
|
|
12
12
|
}
|
|
13
|
-
static paths = [[`generate`, `
|
|
13
|
+
static paths = [[`generate`, `apidocs`]];
|
|
14
14
|
static usage = Command.Usage({
|
|
15
15
|
category: `Generate`,
|
|
16
16
|
description: `Generate OpenAPI documentation from routes`,
|
|
@@ -50,14 +50,14 @@ api_docs: {
|
|
|
50
50
|
examples: [
|
|
51
51
|
[
|
|
52
52
|
`Generate from routes`,
|
|
53
|
-
`$0 generate
|
|
53
|
+
`$0 generate apidocs generate-from-routes --output path/to/output.json`
|
|
54
54
|
],
|
|
55
55
|
[
|
|
56
56
|
`Generate base spec`,
|
|
57
|
-
`$0 generate
|
|
57
|
+
`$0 generate apidocs generate-base --output path/to/output.json`
|
|
58
58
|
],
|
|
59
|
-
[`Merge files`, `$0 generate
|
|
60
|
-
[`Show help`, `$0 generate
|
|
59
|
+
[`Merge files`, `$0 generate apidocs merge-files`],
|
|
60
|
+
[`Show help`, `$0 generate apidocs --help`]
|
|
61
61
|
]
|
|
62
62
|
});
|
|
63
63
|
subcommand = Option.String({ required: false });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/app/console/generate/GenerateApiDocsCommand.mts"],"sourcesContent":["/*\nhow this command should work:\n<command> generate-from-routes --output path/to/output.json\n<command> generate-base --output path/to/output.json\n<command> merge-files # file lists/details are in config\n<command> # will show help\n*/\nimport { cli, router } from \"../../../facades.mjs\";\nimport { Command, Option } from \"clipanion\";\nimport path from \"path\";\nimport * as fs from \"fs/promises\";\nimport { config } from \"../../../config.mjs\";\nimport { Arr } from \"@devbro/neko-helper\";\n\nexport class GenerateApiDocsCommand extends Command {\n static paths = [[`generate`, `apidocsv2`]];\n\n static usage = Command.Usage({\n category: `Generate`,\n description: `Generate OpenAPI documentation from routes`,\n details: `\n This command utility generates OpenAPI 3.0 specification documentation by analyzing\n your application's routes and merging with example files.\n \n Subcommands:\n - generate-from-routes: Generate OpenAPI spec from registered routes\n - generate-base: Generate base OpenAPI specification structure\n - merge-files: Merge multiple OpenAPI files into final documentation\n\n \n This command depends on config data. make sure your default config contains the following:\n\n\\`\\`\\`\napi_docs: {\n\n merge_files: [\n\n path.join(__dirname, '../..', 'private', 'openapi_base.json'),\n\n path.join(__dirname, '../..', 'private', 'openapi_from_routes.json'),\n\n path.join(__dirname, '../..', 'private', 'openapi_from_tests.json'),\n\n path.join(__dirname, '../..', 'private', 'openapi_other_user_changes.json'),\n\n ],\n\n output: path.join(__dirname, '../..', 'public', 'openapi.json'),\n\n}\n\n\\`\\`\\`\n `,\n examples: [\n [\n `Generate from routes`,\n `$0 generate apidocsv2 generate-from-routes --output path/to/output.json`,\n ],\n [\n `Generate base spec`,\n `$0 generate apidocsv2 generate-base --output path/to/output.json`,\n ],\n [`Merge files`, `$0 generate apidocsv2 merge-files`],\n [`Show help`, `$0 generate apidocsv2 --help`],\n ],\n });\n\n subcommand = Option.String({ required: false });\n\n output = Option.String(`--output,-o`, {\n description: `Output file path for generated documentation`,\n });\n\n config = Option.String(`--config,-c`, {\n description: `Path in config to get details from (default: api_docs)`,\n required: false,\n });\n\n async execute() {\n if (!this.subcommand) {\n this.context.stdout.write(\n this.constructor.usage?.toString() || \"No help available\\n\",\n );\n return 0;\n }\n\n switch (this.subcommand) {\n case \"generate-from-routes\":\n return await this.executeGenerateFromRoutes();\n case \"generate-base\":\n return await this.executeGenerateBase();\n case \"merge-files\":\n return await this.executeMergeFiles();\n default:\n this.context.stderr.write(`Unknown subcommand: ${this.subcommand}\\n`);\n this.context.stdout.write(\n this.constructor.usage?.toString() || \"No help available\\n\",\n );\n return 1;\n }\n }\n\n private async executeGenerateFromRoutes() {\n this.context.stdout.write(\n `Generating OpenAPI documentation from routes...\\n`,\n );\n\n const openApiSpec = this.generateFromRoutes();\n const outputPath =\n this.output ||\n path.join(config.get(\"private_path\"), \"openapi_from_routes.json\");\n\n // Ensure directory exists\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n\n await fs.writeFile(\n outputPath,\n JSON.stringify(openApiSpec, null, 2),\n \"utf-8\",\n );\n\n this.context.stdout.write(\n `OpenAPI routes documentation generated at: ${outputPath}\\n`,\n );\n this.context.stdout.write(\n `Total routes documented: ${Object.keys(openApiSpec.paths).length}\\n`,\n );\n return 0;\n }\n\n private async executeGenerateBase() {\n this.context.stdout.write(`Generating base OpenAPI specification...\\n`);\n\n const baseSpec = this.getBaseOpenApiSpec();\n const outputPath =\n this.output || path.join(config.get(\"private_path\"), \"openapi_base.json\");\n\n // Ensure directory exists\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n\n await fs.writeFile(outputPath, JSON.stringify(baseSpec, null, 2), \"utf-8\");\n\n this.context.stdout.write(\n `Base OpenAPI specification generated at: ${outputPath}\\n`,\n );\n return 0;\n }\n\n private async executeMergeFiles() {\n this.context.stdout.write(`Merging OpenAPI files...\\n`);\n let configPath = this.config || \"api_docs\";\n\n const files_to_merge: string[] = config.get(`${configPath}.merge_files`);\n let final_api_docs = {};\n\n for (const file_path of files_to_merge) {\n try {\n const file_json = JSON.parse(await fs.readFile(file_path, \"utf8\"));\n final_api_docs = Arr.deepMerge(final_api_docs, file_json);\n this.context.stdout.write(` Merged: ${file_path}\\n`);\n } catch (error) {\n this.context.stderr.write(\n ` Warning: Could not read ${file_path}: ${(error as Error).message}\\n`,\n );\n }\n }\n\n const outputPath = this.output || config.get(`${configPath}.output`);\n\n // Ensure directory exists\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n\n await fs.writeFile(outputPath, JSON.stringify(final_api_docs, null, 2));\n\n this.context.stdout.write(\n `Final OpenAPI document written to: ${outputPath}\\n`,\n );\n return 0;\n }\n\n private extractParameters(routePath: string): any[] {\n const paramRegex = /:([a-zA-Z0-9_]+)/g;\n const parameters: any[] = [];\n let match;\n\n while ((match = paramRegex.exec(routePath)) !== null) {\n parameters.push({\n name: match[1],\n in: \"path\",\n required: true,\n schema: {\n type: \"string\",\n },\n description: `Path parameter ${match[1]}`,\n });\n }\n\n return parameters;\n }\n\n private generateFromRoutes() {\n const openApiSpec = {\n paths: {} as any,\n };\n const routes = router().routes;\n\n // Process each route\n for (const route of routes) {\n const routePath = route.path;\n // Convert route path to OpenAPI format (e.g., /api/:id -> /api/{id})\n const openApiPath = routePath.replace(/\\/$/g, \"\"); //.replace(/:([a-zA-Z0-9_]+)/g, \"{$1}\");\n\n if (!openApiSpec.paths[openApiPath]) {\n openApiSpec.paths[openApiPath] = {};\n }\n\n // Add each HTTP method for this route\n for (const method of route.methods) {\n const lowerMethod = method.toLowerCase();\n\n // Skip HEAD as it's usually auto-generated\n if (lowerMethod === \"head\") {\n continue;\n }\n\n openApiSpec.paths[openApiPath][lowerMethod] = {\n summary: `${routePath}`,\n description: `Endpoint for ${method} ${routePath}`,\n security: [],\n parameters: this.extractParameters(routePath),\n responses: {},\n };\n\n // Add request body for POST, PUT, PATCH\n if ([\"post\", \"put\", \"patch\"].includes(lowerMethod)) {\n openApiSpec.paths[openApiPath][lowerMethod].requestBody = {\n required: true,\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n },\n },\n },\n };\n }\n }\n }\n\n return openApiSpec;\n }\n\n getBaseOpenApiSpec() {\n // Generate OpenAPI 3.0 specification\n const openApiSpec = {\n openapi: \"3.0.0\",\n info: {\n title: \"API Documentation\",\n version: \"1.0.0\",\n description: \"Auto-generated API documentation\",\n },\n servers: [\n {\n url: \"/\",\n description: \"Local server\",\n },\n ],\n components: {\n securitySchemes: {\n bearerAuth: {\n type: \"http\",\n scheme: \"bearer\",\n bearerFormat: \"JWT\",\n description: \"JWT token authentication\",\n },\n },\n },\n security: [\n {\n bearerAuth: [],\n },\n ],\n paths: {} as Record<string, any>,\n };\n\n return openApiSpec;\n }\n}\n\ncli().register(GenerateApiDocsCommand);\n"],"mappings":";;AAOA,SAAS,KAAK,cAAc;AAC5B,SAAS,SAAS,cAAc;AAChC,OAAO,UAAU;AACjB,YAAY,QAAQ;AACpB,SAAS,cAAc;AACvB,SAAS,WAAW;AAEb,MAAM,+BAA+B,QAAQ;AAAA,EAdpD,OAcoD;AAAA;AAAA;AAAA,EAClD,OAAO,QAAQ,CAAC,CAAC,YAAY,WAAW,CAAC;AAAA,EAEzC,OAAO,QAAQ,QAAQ,MAAM;AAAA,IAC3B,UAAU;AAAA,IACV,aAAa;AAAA,IACb,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAiCT,UAAU;AAAA,MACR;AAAA,QACE;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,MACF;AAAA,MACA,CAAC,eAAe,mCAAmC;AAAA,MACnD,CAAC,aAAa,8BAA8B;AAAA,IAC9C;AAAA,EACF,CAAC;AAAA,EAED,aAAa,OAAO,OAAO,EAAE,UAAU,MAAM,CAAC;AAAA,EAE9C,SAAS,OAAO,OAAO,eAAe;AAAA,IACpC,aAAa;AAAA,EACf,CAAC;AAAA,EAED,SAAS,OAAO,OAAO,eAAe;AAAA,IACpC,aAAa;AAAA,IACb,UAAU;AAAA,EACZ,CAAC;AAAA,EAED,MAAM,UAAU;AACd,QAAI,CAAC,KAAK,YAAY;AACpB,WAAK,QAAQ,OAAO;AAAA,QAClB,KAAK,YAAY,OAAO,SAAS,KAAK;AAAA,MACxC;AACA,aAAO;AAAA,IACT;AAEA,YAAQ,KAAK,YAAY;AAAA,MACvB,KAAK;AACH,eAAO,MAAM,KAAK,0BAA0B;AAAA,MAC9C,KAAK;AACH,eAAO,MAAM,KAAK,oBAAoB;AAAA,MACxC,KAAK;AACH,eAAO,MAAM,KAAK,kBAAkB;AAAA,MACtC;AACE,aAAK,QAAQ,OAAO,MAAM,uBAAuB,KAAK,UAAU;AAAA,CAAI;AACpE,aAAK,QAAQ,OAAO;AAAA,UAClB,KAAK,YAAY,OAAO,SAAS,KAAK;AAAA,QACxC;AACA,eAAO;AAAA,IACX;AAAA,EACF;AAAA,EAEA,MAAc,4BAA4B;AACxC,SAAK,QAAQ,OAAO;AAAA,MAClB;AAAA;AAAA,IACF;AAEA,UAAM,cAAc,KAAK,mBAAmB;AAC5C,UAAM,aACJ,KAAK,UACL,KAAK,KAAK,OAAO,IAAI,cAAc,GAAG,0BAA0B;AAGlE,UAAM,GAAG,MAAM,KAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAE5D,UAAM,GAAG;AAAA,MACP;AAAA,MACA,KAAK,UAAU,aAAa,MAAM,CAAC;AAAA,MACnC;AAAA,IACF;AAEA,SAAK,QAAQ,OAAO;AAAA,MAClB,8CAA8C,UAAU;AAAA;AAAA,IAC1D;AACA,SAAK,QAAQ,OAAO;AAAA,MAClB,4BAA4B,OAAO,KAAK,YAAY,KAAK,EAAE,MAAM;AAAA;AAAA,IACnE;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,sBAAsB;AAClC,SAAK,QAAQ,OAAO,MAAM;AAAA,CAA4C;AAEtE,UAAM,WAAW,KAAK,mBAAmB;AACzC,UAAM,aACJ,KAAK,UAAU,KAAK,KAAK,OAAO,IAAI,cAAc,GAAG,mBAAmB;AAG1E,UAAM,GAAG,MAAM,KAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAE5D,UAAM,GAAG,UAAU,YAAY,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AAEzE,SAAK,QAAQ,OAAO;AAAA,MAClB,4CAA4C,UAAU;AAAA;AAAA,IACxD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,oBAAoB;AAChC,SAAK,QAAQ,OAAO,MAAM;AAAA,CAA4B;AACtD,QAAI,aAAa,KAAK,UAAU;AAEhC,UAAM,iBAA2B,OAAO,IAAI,GAAG,UAAU,cAAc;AACvE,QAAI,iBAAiB,CAAC;AAEtB,eAAW,aAAa,gBAAgB;AACtC,UAAI;AACF,cAAM,YAAY,KAAK,MAAM,MAAM,GAAG,SAAS,WAAW,MAAM,CAAC;AACjE,yBAAiB,IAAI,UAAU,gBAAgB,SAAS;AACxD,aAAK,QAAQ,OAAO,MAAM,aAAa,SAAS;AAAA,CAAI;AAAA,MACtD,SAAS,OAAO;AACd,aAAK,QAAQ,OAAO;AAAA,UAClB,6BAA6B,SAAS,KAAM,MAAgB,OAAO;AAAA;AAAA,QACrE;AAAA,MACF;AAAA,IACF;AAEA,UAAM,aAAa,KAAK,UAAU,OAAO,IAAI,GAAG,UAAU,SAAS;AAGnE,UAAM,GAAG,MAAM,KAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAE5D,UAAM,GAAG,UAAU,YAAY,KAAK,UAAU,gBAAgB,MAAM,CAAC,CAAC;AAEtE,SAAK,QAAQ,OAAO;AAAA,MAClB,sCAAsC,UAAU;AAAA;AAAA,IAClD;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB,WAA0B;AAClD,UAAM,aAAa;AACnB,UAAM,aAAoB,CAAC;AAC3B,QAAI;AAEJ,YAAQ,QAAQ,WAAW,KAAK,SAAS,OAAO,MAAM;AACpD,iBAAW,KAAK;AAAA,QACd,MAAM,MAAM,CAAC;AAAA,QACb,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,QAAQ;AAAA,UACN,MAAM;AAAA,QACR;AAAA,QACA,aAAa,kBAAkB,MAAM,CAAC,CAAC;AAAA,MACzC,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,qBAAqB;AAC3B,UAAM,cAAc;AAAA,MAClB,OAAO,CAAC;AAAA,IACV;AACA,UAAM,SAAS,OAAO,EAAE;AAGxB,eAAW,SAAS,QAAQ;AAC1B,YAAM,YAAY,MAAM;AAExB,YAAM,cAAc,UAAU,QAAQ,QAAQ,EAAE;AAEhD,UAAI,CAAC,YAAY,MAAM,WAAW,GAAG;AACnC,oBAAY,MAAM,WAAW,IAAI,CAAC;AAAA,MACpC;AAGA,iBAAW,UAAU,MAAM,SAAS;AAClC,cAAM,cAAc,OAAO,YAAY;AAGvC,YAAI,gBAAgB,QAAQ;AAC1B;AAAA,QACF;AAEA,oBAAY,MAAM,WAAW,EAAE,WAAW,IAAI;AAAA,UAC5C,SAAS,GAAG,SAAS;AAAA,UACrB,aAAa,gBAAgB,MAAM,IAAI,SAAS;AAAA,UAChD,UAAU,CAAC;AAAA,UACX,YAAY,KAAK,kBAAkB,SAAS;AAAA,UAC5C,WAAW,CAAC;AAAA,QACd;AAGA,YAAI,CAAC,QAAQ,OAAO,OAAO,EAAE,SAAS,WAAW,GAAG;AAClD,sBAAY,MAAM,WAAW,EAAE,WAAW,EAAE,cAAc;AAAA,YACxD,UAAU;AAAA,YACV,SAAS;AAAA,cACP,oBAAoB;AAAA,gBAClB,QAAQ;AAAA,kBACN,MAAM;AAAA,gBACR;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,qBAAqB;AAEnB,UAAM,cAAc;AAAA,MAClB,SAAS;AAAA,MACT,MAAM;AAAA,QACJ,OAAO;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,MACA,SAAS;AAAA,QACP;AAAA,UACE,KAAK;AAAA,UACL,aAAa;AAAA,QACf;AAAA,MACF;AAAA,MACA,YAAY;AAAA,QACV,iBAAiB;AAAA,UACf,YAAY;AAAA,YACV,MAAM;AAAA,YACN,QAAQ;AAAA,YACR,cAAc;AAAA,YACd,aAAa;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,MACA,UAAU;AAAA,QACR;AAAA,UACE,YAAY,CAAC;AAAA,QACf;AAAA,MACF;AAAA,MACA,OAAO,CAAC;AAAA,IACV;AAEA,WAAO;AAAA,EACT;AACF;AAEA,IAAI,EAAE,SAAS,sBAAsB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/app/console/generate/GenerateApiDocsCommand.mts"],"sourcesContent":["/*\nhow this command should work:\n<command> generate-from-routes --output path/to/output.json\n<command> generate-base --output path/to/output.json\n<command> merge-files # file lists/details are in config\n<command> # will show help\n*/\nimport { cli, router } from \"../../../facades.mjs\";\nimport { Command, Option } from \"clipanion\";\nimport path from \"path\";\nimport * as fs from \"fs/promises\";\nimport { config } from \"../../../config.mjs\";\nimport { Arr } from \"@devbro/neko-helper\";\n\nexport class GenerateApiDocsCommand extends Command {\n static paths = [[`generate`, `apidocs`]];\n\n static usage = Command.Usage({\n category: `Generate`,\n description: `Generate OpenAPI documentation from routes`,\n details: `\n This command utility generates OpenAPI 3.0 specification documentation by analyzing\n your application's routes and merging with example files.\n \n Subcommands:\n - generate-from-routes: Generate OpenAPI spec from registered routes\n - generate-base: Generate base OpenAPI specification structure\n - merge-files: Merge multiple OpenAPI files into final documentation\n\n \n This command depends on config data. make sure your default config contains the following:\n\n\\`\\`\\`\napi_docs: {\n\n merge_files: [\n\n path.join(__dirname, '../..', 'private', 'openapi_base.json'),\n\n path.join(__dirname, '../..', 'private', 'openapi_from_routes.json'),\n\n path.join(__dirname, '../..', 'private', 'openapi_from_tests.json'),\n\n path.join(__dirname, '../..', 'private', 'openapi_other_user_changes.json'),\n\n ],\n\n output: path.join(__dirname, '../..', 'public', 'openapi.json'),\n\n}\n\n\\`\\`\\`\n `,\n examples: [\n [\n `Generate from routes`,\n `$0 generate apidocs generate-from-routes --output path/to/output.json`,\n ],\n [\n `Generate base spec`,\n `$0 generate apidocs generate-base --output path/to/output.json`,\n ],\n [`Merge files`, `$0 generate apidocs merge-files`],\n [`Show help`, `$0 generate apidocs --help`],\n ],\n });\n\n subcommand = Option.String({ required: false });\n\n output = Option.String(`--output,-o`, {\n description: `Output file path for generated documentation`,\n });\n\n config = Option.String(`--config,-c`, {\n description: `Path in config to get details from (default: api_docs)`,\n required: false,\n });\n\n async execute() {\n if (!this.subcommand) {\n this.context.stdout.write(\n this.constructor.usage?.toString() || \"No help available\\n\",\n );\n return 0;\n }\n\n switch (this.subcommand) {\n case \"generate-from-routes\":\n return await this.executeGenerateFromRoutes();\n case \"generate-base\":\n return await this.executeGenerateBase();\n case \"merge-files\":\n return await this.executeMergeFiles();\n default:\n this.context.stderr.write(`Unknown subcommand: ${this.subcommand}\\n`);\n this.context.stdout.write(\n this.constructor.usage?.toString() || \"No help available\\n\",\n );\n return 1;\n }\n }\n\n private async executeGenerateFromRoutes() {\n this.context.stdout.write(\n `Generating OpenAPI documentation from routes...\\n`,\n );\n\n const openApiSpec = this.generateFromRoutes();\n const outputPath =\n this.output ||\n path.join(config.get(\"private_path\"), \"openapi_from_routes.json\");\n\n // Ensure directory exists\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n\n await fs.writeFile(\n outputPath,\n JSON.stringify(openApiSpec, null, 2),\n \"utf-8\",\n );\n\n this.context.stdout.write(\n `OpenAPI routes documentation generated at: ${outputPath}\\n`,\n );\n this.context.stdout.write(\n `Total routes documented: ${Object.keys(openApiSpec.paths).length}\\n`,\n );\n return 0;\n }\n\n private async executeGenerateBase() {\n this.context.stdout.write(`Generating base OpenAPI specification...\\n`);\n\n const baseSpec = this.getBaseOpenApiSpec();\n const outputPath =\n this.output || path.join(config.get(\"private_path\"), \"openapi_base.json\");\n\n // Ensure directory exists\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n\n await fs.writeFile(outputPath, JSON.stringify(baseSpec, null, 2), \"utf-8\");\n\n this.context.stdout.write(\n `Base OpenAPI specification generated at: ${outputPath}\\n`,\n );\n return 0;\n }\n\n private async executeMergeFiles() {\n this.context.stdout.write(`Merging OpenAPI files...\\n`);\n let configPath = this.config || \"api_docs\";\n\n const files_to_merge: string[] = config.get(`${configPath}.merge_files`);\n let final_api_docs = {};\n\n for (const file_path of files_to_merge) {\n try {\n const file_json = JSON.parse(await fs.readFile(file_path, \"utf8\"));\n final_api_docs = Arr.deepMerge(final_api_docs, file_json);\n this.context.stdout.write(` Merged: ${file_path}\\n`);\n } catch (error) {\n this.context.stderr.write(\n ` Warning: Could not read ${file_path}: ${(error as Error).message}\\n`,\n );\n }\n }\n\n const outputPath = this.output || config.get(`${configPath}.output`);\n\n // Ensure directory exists\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n\n await fs.writeFile(outputPath, JSON.stringify(final_api_docs, null, 2));\n\n this.context.stdout.write(\n `Final OpenAPI document written to: ${outputPath}\\n`,\n );\n return 0;\n }\n\n private extractParameters(routePath: string): any[] {\n const paramRegex = /:([a-zA-Z0-9_]+)/g;\n const parameters: any[] = [];\n let match;\n\n while ((match = paramRegex.exec(routePath)) !== null) {\n parameters.push({\n name: match[1],\n in: \"path\",\n required: true,\n schema: {\n type: \"string\",\n },\n description: `Path parameter ${match[1]}`,\n });\n }\n\n return parameters;\n }\n\n private generateFromRoutes() {\n const openApiSpec = {\n paths: {} as any,\n };\n const routes = router().routes;\n\n // Process each route\n for (const route of routes) {\n const routePath = route.path;\n // Convert route path to OpenAPI format (e.g., /api/:id -> /api/{id})\n const openApiPath = routePath.replace(/\\/$/g, \"\"); //.replace(/:([a-zA-Z0-9_]+)/g, \"{$1}\");\n\n if (!openApiSpec.paths[openApiPath]) {\n openApiSpec.paths[openApiPath] = {};\n }\n\n // Add each HTTP method for this route\n for (const method of route.methods) {\n const lowerMethod = method.toLowerCase();\n\n // Skip HEAD as it's usually auto-generated\n if (lowerMethod === \"head\") {\n continue;\n }\n\n openApiSpec.paths[openApiPath][lowerMethod] = {\n summary: `${routePath}`,\n description: `Endpoint for ${method} ${routePath}`,\n security: [],\n parameters: this.extractParameters(routePath),\n responses: {},\n };\n\n // Add request body for POST, PUT, PATCH\n if ([\"post\", \"put\", \"patch\"].includes(lowerMethod)) {\n openApiSpec.paths[openApiPath][lowerMethod].requestBody = {\n required: true,\n content: {\n \"application/json\": {\n schema: {\n type: \"object\",\n },\n },\n },\n };\n }\n }\n }\n\n return openApiSpec;\n }\n\n getBaseOpenApiSpec() {\n // Generate OpenAPI 3.0 specification\n const openApiSpec = {\n openapi: \"3.0.0\",\n info: {\n title: \"API Documentation\",\n version: \"1.0.0\",\n description: \"Auto-generated API documentation\",\n },\n servers: [\n {\n url: \"/\",\n description: \"Local server\",\n },\n ],\n components: {\n securitySchemes: {\n bearerAuth: {\n type: \"http\",\n scheme: \"bearer\",\n bearerFormat: \"JWT\",\n description: \"JWT token authentication\",\n },\n },\n },\n security: [\n {\n bearerAuth: [],\n },\n ],\n paths: {} as Record<string, any>,\n };\n\n return openApiSpec;\n }\n}\n\ncli().register(GenerateApiDocsCommand);\n"],"mappings":"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","names":[]}
|
|
@@ -135,6 +135,10 @@ var DatabaseServiceProvider = class _DatabaseServiceProvider extends Middleware
|
|
|
135
135
|
const conn = new import_neko_sql.PostgresqlConnection(db_config.config);
|
|
136
136
|
return conn;
|
|
137
137
|
}
|
|
138
|
+
if (db_config.provider === "sqlite") {
|
|
139
|
+
const conn = new import_neko_sql.SqliteConnection(db_config.config);
|
|
140
|
+
return conn;
|
|
141
|
+
}
|
|
138
142
|
throw new Error(`Unsupported database provider: ${db_config.provider}`);
|
|
139
143
|
}
|
|
140
144
|
};
|
|
@@ -688,27 +688,58 @@ import_neko_storage.StorageProviderFactory.register("sftp", (opt) => {
|
|
|
688
688
|
// src/facades.mts
|
|
689
689
|
var import_neko_cache2 = require("@devbro/neko-cache");
|
|
690
690
|
var import_neko_queue2 = require("@devbro/neko-queue");
|
|
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
|
+
}
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
__name(wrapSingletonWithAccessors, "wrapSingletonWithAccessors");
|
|
691
718
|
var router = (0, import_neko_helper3.createSingleton)(() => new Router());
|
|
692
|
-
var scheduler = (
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
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
|
+
});
|
|
699
728
|
});
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
729
|
+
return rc;
|
|
730
|
+
})
|
|
731
|
+
);
|
|
703
732
|
var db = /* @__PURE__ */ __name((label = "default") => (0, import_neko_context3.ctx)().getOrThrow(["database", label]), "db");
|
|
704
|
-
var storage = (
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
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
|
+
);
|
|
712
743
|
var cli = (0, import_neko_helper3.createSingleton)(() => {
|
|
713
744
|
const [node, app, ...args] = process.argv;
|
|
714
745
|
return new import_clipanion.Cli({
|
|
@@ -723,46 +754,54 @@ var httpServer = (0, import_neko_helper3.createSingleton)(() => {
|
|
|
723
754
|
server.setRouter(router());
|
|
724
755
|
return server;
|
|
725
756
|
});
|
|
726
|
-
var logger = (
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
mailer_config.config
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
queue_config.
|
|
751
|
-
queue_config
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
});
|
|
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
|
+
);
|
|
766
805
|
|
|
767
806
|
// src/app/console/DefaultCommand.mts
|
|
768
807
|
var DefaultCommand = class extends import_clipanion2.Command {
|
|
@@ -691,27 +691,58 @@ import_neko_storage.StorageProviderFactory.register("sftp", (opt) => {
|
|
|
691
691
|
// src/facades.mts
|
|
692
692
|
var import_neko_cache2 = require("@devbro/neko-cache");
|
|
693
693
|
var import_neko_queue2 = require("@devbro/neko-queue");
|
|
694
|
+
function wrapSingletonWithAccessors(singletonFn) {
|
|
695
|
+
let methodsInitialized = false;
|
|
696
|
+
const initializeMethods = /* @__PURE__ */ __name(() => {
|
|
697
|
+
if (methodsInitialized) return;
|
|
698
|
+
const defaultInstance = singletonFn();
|
|
699
|
+
const prototype = Object.getPrototypeOf(defaultInstance);
|
|
700
|
+
const methodNames = Object.getOwnPropertyNames(prototype).filter(
|
|
701
|
+
(name) => name !== "constructor" && typeof prototype[name] === "function"
|
|
702
|
+
);
|
|
703
|
+
for (const methodName of methodNames) {
|
|
704
|
+
singletonFn[methodName] = (...args) => {
|
|
705
|
+
const instance = singletonFn();
|
|
706
|
+
return instance[methodName](...args);
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
methodsInitialized = true;
|
|
710
|
+
}, "initializeMethods");
|
|
711
|
+
return new Proxy(singletonFn, {
|
|
712
|
+
get(target, prop, receiver) {
|
|
713
|
+
if (typeof prop === "string" && !Reflect.has(target, prop)) {
|
|
714
|
+
initializeMethods();
|
|
715
|
+
}
|
|
716
|
+
return Reflect.get(target, prop, receiver);
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
__name(wrapSingletonWithAccessors, "wrapSingletonWithAccessors");
|
|
694
721
|
var router = (0, import_neko_helper3.createSingleton)(() => new Router());
|
|
695
|
-
var scheduler = (
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
722
|
+
var scheduler = wrapSingletonWithAccessors(
|
|
723
|
+
(0, import_neko_helper3.createSingleton)(() => {
|
|
724
|
+
const rc = new import_neko_scheduler.Scheduler();
|
|
725
|
+
rc.setErrorHandler((err, job) => {
|
|
726
|
+
logger().error({
|
|
727
|
+
msg: "Scheduled job error",
|
|
728
|
+
err,
|
|
729
|
+
job_name: job.getName()
|
|
730
|
+
});
|
|
702
731
|
});
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
732
|
+
return rc;
|
|
733
|
+
})
|
|
734
|
+
);
|
|
706
735
|
var db = /* @__PURE__ */ __name((label = "default") => (0, import_neko_context3.ctx)().getOrThrow(["database", label]), "db");
|
|
707
|
-
var storage = (
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
736
|
+
var storage = wrapSingletonWithAccessors(
|
|
737
|
+
(0, import_neko_helper3.createSingleton)((label = "default") => {
|
|
738
|
+
let storage_config = import_neko_config.config.get(["storages", label].join("."));
|
|
739
|
+
const provider = import_neko_storage2.StorageProviderFactory.create(
|
|
740
|
+
storage_config.provider,
|
|
741
|
+
storage_config.config
|
|
742
|
+
);
|
|
743
|
+
return new import_neko_storage2.Storage(provider);
|
|
744
|
+
})
|
|
745
|
+
);
|
|
715
746
|
var cli = (0, import_neko_helper3.createSingleton)(() => {
|
|
716
747
|
const [node, app, ...args] = process.argv;
|
|
717
748
|
return new import_clipanion.Cli({
|
|
@@ -726,46 +757,54 @@ var httpServer = (0, import_neko_helper3.createSingleton)(() => {
|
|
|
726
757
|
server.setRouter(router());
|
|
727
758
|
return server;
|
|
728
759
|
});
|
|
729
|
-
var logger = (
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
mailer_config.config
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
queue_config.
|
|
754
|
-
queue_config
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
});
|
|
760
|
+
var logger = wrapSingletonWithAccessors(
|
|
761
|
+
(0, import_neko_helper3.createSingleton)((label) => {
|
|
762
|
+
const logger_config = import_neko_config.config.get(["loggers", label].join("."));
|
|
763
|
+
const rc = new import_neko_logger.Logger(logger_config);
|
|
764
|
+
rc.setExtrasFunction((message) => {
|
|
765
|
+
message.requestId = (0, import_neko_context3.ctxSafe)()?.get("requestId") || "N/A";
|
|
766
|
+
return message;
|
|
767
|
+
});
|
|
768
|
+
return rc;
|
|
769
|
+
})
|
|
770
|
+
);
|
|
771
|
+
var mailer = wrapSingletonWithAccessors(
|
|
772
|
+
(0, import_neko_helper3.createSingleton)((label) => {
|
|
773
|
+
const mailer_config = import_neko_config.config.get(["mailer", label].join("."));
|
|
774
|
+
const provider = import_neko_mailer2.MailerProviderFactory.create(
|
|
775
|
+
mailer_config.provider,
|
|
776
|
+
mailer_config.config
|
|
777
|
+
);
|
|
778
|
+
const rc = new import_neko_mailer2.Mailer(provider);
|
|
779
|
+
return rc;
|
|
780
|
+
})
|
|
781
|
+
);
|
|
782
|
+
var queue = wrapSingletonWithAccessors(
|
|
783
|
+
(0, import_neko_helper3.createSingleton)((label) => {
|
|
784
|
+
const queue_config = import_neko_config.config.get(["queues", label].join("."));
|
|
785
|
+
if (!queue_config) {
|
|
786
|
+
throw new Error(`Queue configuration for '${label}' not found`);
|
|
787
|
+
}
|
|
788
|
+
const provider = import_neko_queue2.QueueTransportFactory.create(
|
|
789
|
+
queue_config.provider,
|
|
790
|
+
queue_config.config
|
|
791
|
+
);
|
|
792
|
+
return new import_neko_queue2.QueueConnection(provider);
|
|
793
|
+
})
|
|
794
|
+
);
|
|
795
|
+
var cache = wrapSingletonWithAccessors(
|
|
796
|
+
(0, import_neko_helper3.createSingleton)((label) => {
|
|
797
|
+
const cache_config = import_neko_config.config.get(["caches", label].join("."));
|
|
798
|
+
if (!cache_config) {
|
|
799
|
+
throw new Error(`Cache configuration for '${label}' not found`);
|
|
800
|
+
}
|
|
801
|
+
const provider = CacheProviderFactory.create(
|
|
802
|
+
cache_config.provider,
|
|
803
|
+
cache_config.config
|
|
804
|
+
);
|
|
805
|
+
return new import_neko_cache2.Cache(provider);
|
|
806
|
+
})
|
|
807
|
+
);
|
|
769
808
|
|
|
770
809
|
// src/app/console/KeyGenerateCommand.mts
|
|
771
810
|
var KeyGenerateCommand = class extends import_clipanion2.Command {
|