te.js 2.1.0 → 2.1.2

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 (70) hide show
  1. package/README.md +197 -196
  2. package/auto-docs/analysis/handler-analyzer.js +58 -58
  3. package/auto-docs/analysis/source-resolver.js +101 -101
  4. package/auto-docs/constants.js +37 -37
  5. package/auto-docs/docs-llm/index.js +7 -7
  6. package/auto-docs/docs-llm/prompts.js +222 -222
  7. package/auto-docs/docs-llm/provider.js +132 -132
  8. package/auto-docs/index.js +146 -146
  9. package/auto-docs/openapi/endpoint-processor.js +277 -277
  10. package/auto-docs/openapi/generator.js +107 -107
  11. package/auto-docs/openapi/level3.js +131 -131
  12. package/auto-docs/openapi/spec-builders.js +244 -244
  13. package/auto-docs/ui/docs-ui.js +186 -186
  14. package/auto-docs/utils/logger.js +17 -17
  15. package/auto-docs/utils/strip-usage.js +10 -10
  16. package/cli/docs-command.js +315 -315
  17. package/cli/fly-command.js +71 -71
  18. package/cli/index.js +56 -56
  19. package/cors/index.js +71 -0
  20. package/database/index.js +165 -165
  21. package/database/mongodb.js +146 -146
  22. package/database/redis.js +201 -201
  23. package/docs/README.md +36 -36
  24. package/docs/ammo.md +362 -362
  25. package/docs/api-reference.md +490 -490
  26. package/docs/auto-docs.md +216 -216
  27. package/docs/cli.md +152 -152
  28. package/docs/configuration.md +275 -275
  29. package/docs/database.md +390 -390
  30. package/docs/error-handling.md +438 -438
  31. package/docs/file-uploads.md +333 -333
  32. package/docs/getting-started.md +214 -214
  33. package/docs/middleware.md +355 -355
  34. package/docs/rate-limiting.md +393 -393
  35. package/docs/routing.md +302 -302
  36. package/lib/llm/client.js +73 -0
  37. package/lib/llm/index.js +7 -0
  38. package/lib/llm/parse.js +89 -0
  39. package/package.json +64 -62
  40. package/rate-limit/algorithms/fixed-window.js +141 -141
  41. package/rate-limit/algorithms/sliding-window.js +147 -147
  42. package/rate-limit/algorithms/token-bucket.js +115 -115
  43. package/rate-limit/base.js +165 -165
  44. package/rate-limit/index.js +147 -147
  45. package/rate-limit/storage/base.js +104 -104
  46. package/rate-limit/storage/memory.js +101 -101
  47. package/rate-limit/storage/redis.js +88 -88
  48. package/server/ammo/body-parser.js +220 -220
  49. package/server/ammo/dispatch-helper.js +103 -103
  50. package/server/ammo/enhancer.js +57 -57
  51. package/server/ammo.js +454 -415
  52. package/server/endpoint.js +97 -74
  53. package/server/error.js +9 -9
  54. package/server/errors/code-context.js +125 -125
  55. package/server/errors/llm-error-service.js +140 -140
  56. package/server/files/helper.js +33 -33
  57. package/server/files/uploader.js +143 -143
  58. package/server/handler.js +158 -119
  59. package/server/target.js +185 -175
  60. package/server/targets/middleware-validator.js +22 -22
  61. package/server/targets/path-validator.js +21 -21
  62. package/server/targets/registry.js +160 -160
  63. package/server/targets/shoot-validator.js +21 -21
  64. package/te.js +428 -402
  65. package/utils/auto-register.js +17 -17
  66. package/utils/configuration.js +64 -64
  67. package/utils/errors-llm-config.js +84 -84
  68. package/utils/request-logger.js +43 -43
  69. package/utils/status-codes.js +82 -82
  70. package/utils/tejas-entrypoint-html.js +18 -18
@@ -1,71 +1,71 @@
1
- /**
2
- * `tejas fly` — start the Tejas server by running the user's entry point.
3
- * Entry point is resolved in order: CLI arg → tejas.config.json "entry" → package.json "main" → index.js → app.js → server.js
4
- */
5
-
6
- import path from 'node:path';
7
- import fs from 'node:fs';
8
- import { spawn } from 'node:child_process';
9
- import { loadConfigFile } from '../utils/configuration.js';
10
-
11
- const CONVENTION_FILES = ['index.js', 'app.js', 'server.js'];
12
-
13
- function resolveEntryPoint(cliArg) {
14
- const cwd = process.cwd();
15
-
16
- if (cliArg) {
17
- const candidate = path.isAbsolute(cliArg) ? cliArg : path.join(cwd, cliArg);
18
- if (fs.existsSync(candidate)) return candidate;
19
- throw new Error(`Entry file not found: ${cliArg}`);
20
- }
21
-
22
- const config = loadConfigFile();
23
- if (config.entry) {
24
- const candidate = path.join(cwd, config.entry);
25
- if (fs.existsSync(candidate)) return candidate;
26
- throw new Error(`Entry file from tejas.config.json not found: ${config.entry}`);
27
- }
28
-
29
- try {
30
- const pkgPath = path.join(cwd, 'package.json');
31
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
32
- if (pkg.main) {
33
- const candidate = path.join(cwd, pkg.main);
34
- if (fs.existsSync(candidate)) return candidate;
35
- throw new Error(`Entry file from package.json "main" not found: ${pkg.main}`);
36
- }
37
- } catch (err) {
38
- if (err.code === 'ENOENT') {
39
- // no package.json, continue to convention
40
- } else {
41
- throw err;
42
- }
43
- }
44
-
45
- for (const name of CONVENTION_FILES) {
46
- const candidate = path.join(cwd, name);
47
- if (fs.existsSync(candidate)) return candidate;
48
- }
49
-
50
- throw new Error(
51
- `Could not resolve entry point. Set "entry" in tejas.config.json, "main" in package.json, pass a file (tejas fly <file>), or add index.js, app.js, or server.js in ${cwd}`,
52
- );
53
- }
54
-
55
- /**
56
- * Resolves the entry point and spawns the server process. Exits with the child's exit code.
57
- */
58
- export function runFlyCommand() {
59
- const cliArg = process.argv[3]; // tejas fly [file]
60
- const entryFile = resolveEntryPoint(cliArg);
61
-
62
- const child = spawn(process.execPath, [entryFile], {
63
- stdio: 'inherit',
64
- cwd: process.cwd(),
65
- env: process.env,
66
- });
67
-
68
- child.on('exit', (code, signal) => {
69
- process.exit(code ?? (signal ? 1 : 0));
70
- });
71
- }
1
+ /**
2
+ * `tejas fly` — start the Tejas server by running the user's entry point.
3
+ * Entry point is resolved in order: CLI arg → tejas.config.json "entry" → package.json "main" → index.js → app.js → server.js
4
+ */
5
+
6
+ import path from 'node:path';
7
+ import fs from 'node:fs';
8
+ import { spawn } from 'node:child_process';
9
+ import { loadConfigFile } from '../utils/configuration.js';
10
+
11
+ const CONVENTION_FILES = ['index.js', 'app.js', 'server.js'];
12
+
13
+ function resolveEntryPoint(cliArg) {
14
+ const cwd = process.cwd();
15
+
16
+ if (cliArg) {
17
+ const candidate = path.isAbsolute(cliArg) ? cliArg : path.join(cwd, cliArg);
18
+ if (fs.existsSync(candidate)) return candidate;
19
+ throw new Error(`Entry file not found: ${cliArg}`);
20
+ }
21
+
22
+ const config = loadConfigFile();
23
+ if (config.entry) {
24
+ const candidate = path.join(cwd, config.entry);
25
+ if (fs.existsSync(candidate)) return candidate;
26
+ throw new Error(`Entry file from tejas.config.json not found: ${config.entry}`);
27
+ }
28
+
29
+ try {
30
+ const pkgPath = path.join(cwd, 'package.json');
31
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
32
+ if (pkg.main) {
33
+ const candidate = path.join(cwd, pkg.main);
34
+ if (fs.existsSync(candidate)) return candidate;
35
+ throw new Error(`Entry file from package.json "main" not found: ${pkg.main}`);
36
+ }
37
+ } catch (err) {
38
+ if (err.code === 'ENOENT') {
39
+ // no package.json, continue to convention
40
+ } else {
41
+ throw err;
42
+ }
43
+ }
44
+
45
+ for (const name of CONVENTION_FILES) {
46
+ const candidate = path.join(cwd, name);
47
+ if (fs.existsSync(candidate)) return candidate;
48
+ }
49
+
50
+ throw new Error(
51
+ `Could not resolve entry point. Set "entry" in tejas.config.json, "main" in package.json, pass a file (tejas fly <file>), or add index.js, app.js, or server.js in ${cwd}`,
52
+ );
53
+ }
54
+
55
+ /**
56
+ * Resolves the entry point and spawns the server process. Exits with the child's exit code.
57
+ */
58
+ export function runFlyCommand() {
59
+ const cliArg = process.argv[3]; // tejas fly [file]
60
+ const entryFile = resolveEntryPoint(cliArg);
61
+
62
+ const child = spawn(process.execPath, [entryFile], {
63
+ stdio: 'inherit',
64
+ cwd: process.cwd(),
65
+ env: process.env,
66
+ });
67
+
68
+ child.on('exit', (code, signal) => {
69
+ process.exit(code ?? (signal ? 1 : 0));
70
+ });
71
+ }
package/cli/index.js CHANGED
@@ -1,57 +1,57 @@
1
1
  #!/usr/bin/env node
2
-
3
- /**
4
- * CLI entry point for te.js (tejas).
5
- * Usage: tejas fly [file] | tejas generate:docs [--ci] | tejas docs:on-push
6
- */
7
-
8
- import { runDocsCommand, runDocsCommandCI, runDocsOnPush } from './docs-command.js';
9
- import { runFlyCommand } from './fly-command.js';
10
-
11
- const command = process.argv[2];
12
- const ciFlag = process.argv.includes('--ci');
13
-
14
- if (command === 'fly') {
15
- try {
16
- runFlyCommand();
17
- } catch (err) {
18
- console.error(err?.message ?? err);
19
- process.exit(1);
20
- }
21
- } else if (command === 'docs:on-push') {
22
- runDocsOnPush().catch((err) => {
23
- console.error(err?.message ?? err);
24
- process.exit(1);
25
- });
26
- } else if (command === 'generate:docs') {
27
- if (ciFlag) {
28
- runDocsCommandCI().catch((err) => {
29
- console.error(err?.message ?? err);
30
- process.exit(1);
31
- });
32
- } else {
33
- runDocsCommand().catch((err) => {
34
- console.error(err?.message ?? err);
35
- process.exit(1);
36
- });
37
- }
38
- } else {
39
- console.log(`
40
- tejas - te.js framework CLI
41
-
42
- Usage: tejas <command> [options]
43
-
44
- Commands:
45
- fly [file] Start the Tejas server
46
- generate:docs [--ci] OpenAPI documentation generator (interactive or CI mode)
47
- docs:on-push Generate docs when pushing to production branch (use in pre-push hook)
48
-
49
- Examples:
50
- tejas fly
51
- tejas fly index.js
52
- tejas generate:docs
53
- tejas generate:docs --ci
54
- tejas docs:on-push
55
- `);
56
- process.exit(command ? 1 : 0);
57
- }
2
+
3
+ /**
4
+ * CLI entry point for te.js (tejas).
5
+ * Usage: tejas fly [file] | tejas generate:docs [--ci] | tejas docs:on-push
6
+ */
7
+
8
+ import { runDocsCommand, runDocsCommandCI, runDocsOnPush } from './docs-command.js';
9
+ import { runFlyCommand } from './fly-command.js';
10
+
11
+ const command = process.argv[2];
12
+ const ciFlag = process.argv.includes('--ci');
13
+
14
+ if (command === 'fly') {
15
+ try {
16
+ runFlyCommand();
17
+ } catch (err) {
18
+ console.error(err?.message ?? err);
19
+ process.exit(1);
20
+ }
21
+ } else if (command === 'docs:on-push') {
22
+ runDocsOnPush().catch((err) => {
23
+ console.error(err?.message ?? err);
24
+ process.exit(1);
25
+ });
26
+ } else if (command === 'generate:docs') {
27
+ if (ciFlag) {
28
+ runDocsCommandCI().catch((err) => {
29
+ console.error(err?.message ?? err);
30
+ process.exit(1);
31
+ });
32
+ } else {
33
+ runDocsCommand().catch((err) => {
34
+ console.error(err?.message ?? err);
35
+ process.exit(1);
36
+ });
37
+ }
38
+ } else {
39
+ console.log(`
40
+ tejas - te.js framework CLI
41
+
42
+ Usage: tejas <command> [options]
43
+
44
+ Commands:
45
+ fly [file] Start the Tejas server
46
+ generate:docs [--ci] OpenAPI documentation generator (interactive or CI mode)
47
+ docs:on-push Generate docs when pushing to production branch (use in pre-push hook)
48
+
49
+ Examples:
50
+ tejas fly
51
+ tejas fly index.js
52
+ tejas generate:docs
53
+ tejas generate:docs --ci
54
+ tejas docs:on-push
55
+ `);
56
+ process.exit(command ? 1 : 0);
57
+ }
package/cors/index.js ADDED
@@ -0,0 +1,71 @@
1
+ /**
2
+ * CORS middleware factory. Handles OPTIONS preflight with 204 and sets CORS response headers.
3
+ *
4
+ * @param {Object} config - CORS configuration
5
+ * @param {string|string[]|((origin: string) => boolean)} [config.origin='*'] - Allowed origin(s): '*' or array of origins or function
6
+ * @param {string[]} [config.methods=['GET','POST','PUT','DELETE','PATCH','HEAD','OPTIONS']] - Allowed methods for Access-Control-Allow-Methods
7
+ * @param {string[]} [config.allowedHeaders=['Content-Type','Authorization']] - Allowed request headers for Access-Control-Allow-Headers
8
+ * @param {boolean} [config.credentials=false] - Access-Control-Allow-Credentials (use with specific origin, not '*')
9
+ * @param {number} [config.maxAge] - Access-Control-Max-Age in seconds for preflight cache
10
+ * @returns {Function} Middleware (ammo, next)
11
+ */
12
+ function corsMiddleware(config = {}) {
13
+ const {
14
+ origin = '*',
15
+ methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],
16
+ allowedHeaders = ['Content-Type', 'Authorization'],
17
+ credentials = false,
18
+ maxAge,
19
+ } = config;
20
+
21
+ const methodsList = Array.isArray(methods)
22
+ ? methods.map((m) => String(m).toUpperCase()).join(', ')
23
+ : String(methods);
24
+ const headersList = Array.isArray(allowedHeaders)
25
+ ? allowedHeaders.join(', ')
26
+ : String(allowedHeaders);
27
+
28
+ const resolveOrigin = (requestOrigin) => {
29
+ if (typeof origin === 'function') {
30
+ return origin(requestOrigin) ? requestOrigin || '*' : null;
31
+ }
32
+ if (origin === '*') {
33
+ return credentials ? (requestOrigin || '*') : '*';
34
+ }
35
+ if (Array.isArray(origin)) {
36
+ const normalized = (requestOrigin || '').toLowerCase();
37
+ const allowed = origin.some(
38
+ (o) => String(o).toLowerCase() === normalized,
39
+ );
40
+ return allowed ? requestOrigin : null;
41
+ }
42
+ return String(origin) === (requestOrigin || '') ? requestOrigin : null;
43
+ };
44
+
45
+ return async (ammo, next) => {
46
+ const requestOrigin = ammo.req.headers.origin;
47
+
48
+ const allowOrigin = resolveOrigin(requestOrigin);
49
+ if (allowOrigin != null) {
50
+ ammo.res.setHeader('Access-Control-Allow-Origin', allowOrigin);
51
+ }
52
+ ammo.res.setHeader('Access-Control-Allow-Methods', methodsList);
53
+ ammo.res.setHeader('Access-Control-Allow-Headers', headersList);
54
+ if (credentials) {
55
+ ammo.res.setHeader('Access-Control-Allow-Credentials', 'true');
56
+ }
57
+ if (maxAge != null && Number.isFinite(maxAge)) {
58
+ ammo.res.setHeader('Access-Control-Max-Age', String(maxAge));
59
+ }
60
+
61
+ if (ammo.req.method === 'OPTIONS') {
62
+ ammo.res.writeHead(204);
63
+ ammo.res.end();
64
+ return;
65
+ }
66
+
67
+ await next();
68
+ };
69
+ }
70
+
71
+ export default corsMiddleware;
package/database/index.js CHANGED
@@ -1,165 +1,165 @@
1
- import redis from './redis.js';
2
- import mongodb from './mongodb.js';
3
- import TejError from '../server/error.js';
4
- import TejLogger from 'tej-logger';
5
-
6
- const logger = new TejLogger('DatabaseManager');
7
-
8
- class DatabaseManager {
9
- static #instance = null;
10
- static #isInitializing = false;
11
-
12
- // Enhanced connection tracking with metadata
13
- #connections = new Map();
14
- #initializingConnections = new Map();
15
-
16
- // Helper method for sleeping
17
- async #sleep(ms) {
18
- return new Promise((resolve) => setTimeout(resolve, ms));
19
- }
20
-
21
- constructor() {
22
- if (DatabaseManager.#instance) {
23
- return DatabaseManager.#instance;
24
- }
25
-
26
- if (!DatabaseManager.#isInitializing) {
27
- throw new TejError(
28
- 500,
29
- 'Use DatabaseManager.getInstance() to get the instance',
30
- );
31
- }
32
-
33
- DatabaseManager.#isInitializing = false;
34
- DatabaseManager.#instance = this;
35
- }
36
-
37
- static getInstance() {
38
- if (!DatabaseManager.#instance) {
39
- DatabaseManager.#isInitializing = true;
40
- DatabaseManager.#instance = new DatabaseManager();
41
- }
42
- return DatabaseManager.#instance;
43
- }
44
-
45
- async initializeConnection(dbType, config) {
46
- const key = dbType.toLowerCase();
47
-
48
- // If a connection already exists for this config, return it
49
- if (this.#connections.has(key)) {
50
- return this.#connections.get(key).client;
51
- }
52
-
53
- // Set initializing flag
54
- this.#initializingConnections.set(key, true);
55
-
56
- let client;
57
- try {
58
- switch (key) {
59
- case 'redis':
60
- client = await redis.createConnection({
61
- isCluster: config.isCluster || false,
62
- options: config || {},
63
- });
64
- break;
65
- case 'mongodb':
66
- client = await mongodb.createConnection(config);
67
- break;
68
- default:
69
- throw new TejError(400, `Unsupported database type: ${dbType}`);
70
- }
71
-
72
- this.#connections.set(key, {
73
- type: dbType,
74
- client,
75
- config,
76
- });
77
-
78
- // Clear initializing flag
79
- this.#initializingConnections.delete(key);
80
-
81
- return client;
82
- } catch (error) {
83
- // Clear initializing flag on error
84
- this.#initializingConnections.delete(key);
85
- logger.error(`Failed to initialize ${dbType} connection:`, error);
86
- throw error;
87
- }
88
- }
89
-
90
- getConnection(dbType) {
91
- const key = dbType.toLowerCase();
92
- const connection = this.#connections.get(key);
93
- if (!connection) {
94
- throw new TejError(
95
- 404,
96
- `No connection found for ${dbType} with given config`,
97
- );
98
- }
99
- return connection.client;
100
- }
101
-
102
- async closeConnection(dbType, config) {
103
- const key = dbType.toLowerCase();
104
- if (!this.#connections.has(key)) {
105
- return;
106
- }
107
-
108
- try {
109
- const connection = this.#connections.get(key);
110
- switch (key) {
111
- case 'redis':
112
- await redis.closeConnection(connection.client);
113
- break;
114
- case 'mongodb':
115
- await mongodb.closeConnection(connection.client);
116
- break;
117
- }
118
-
119
- this.#connections.delete(key);
120
- } catch (error) {
121
- logger.error(`Error closing ${dbType} connection:`, error);
122
- throw error;
123
- }
124
- }
125
-
126
- /**
127
- * Close all database connections
128
- * @returns {Promise<void>}
129
- */
130
- async closeAllConnections() {
131
- const closePromises = [];
132
- for (const [key, connection] of this.#connections) {
133
- closePromises.push(
134
- this.closeConnection(connection.type, connection.config),
135
- );
136
- }
137
- await Promise.all(closePromises);
138
- this.#connections.clear();
139
- }
140
-
141
- /**
142
- * Get all active connections
143
- * @returns {Map<string, {type: string, client: any, config: Object}>}
144
- */
145
- getActiveConnections() {
146
- return new Map(this.#connections);
147
- }
148
-
149
- /**
150
- * Check if a connection exists or is being initialized
151
- * @param {string} dbType - Type of database
152
- * @param {Object} config - Database configuration
153
- * @returns {{ exists: boolean, initializing: boolean }}
154
- */
155
- hasConnection(dbType, config) {
156
- const key = dbType.toLowerCase();
157
- return {
158
- exists: this.#connections.has(key),
159
- initializing: this.#initializingConnections.has(key),
160
- };
161
- }
162
- }
163
-
164
- const dbManager = DatabaseManager.getInstance();
165
- export default dbManager;
1
+ import redis from './redis.js';
2
+ import mongodb from './mongodb.js';
3
+ import TejError from '../server/error.js';
4
+ import TejLogger from 'tej-logger';
5
+
6
+ const logger = new TejLogger('DatabaseManager');
7
+
8
+ class DatabaseManager {
9
+ static #instance = null;
10
+ static #isInitializing = false;
11
+
12
+ // Enhanced connection tracking with metadata
13
+ #connections = new Map();
14
+ #initializingConnections = new Map();
15
+
16
+ // Helper method for sleeping
17
+ async #sleep(ms) {
18
+ return new Promise((resolve) => setTimeout(resolve, ms));
19
+ }
20
+
21
+ constructor() {
22
+ if (DatabaseManager.#instance) {
23
+ return DatabaseManager.#instance;
24
+ }
25
+
26
+ if (!DatabaseManager.#isInitializing) {
27
+ throw new TejError(
28
+ 500,
29
+ 'Use DatabaseManager.getInstance() to get the instance',
30
+ );
31
+ }
32
+
33
+ DatabaseManager.#isInitializing = false;
34
+ DatabaseManager.#instance = this;
35
+ }
36
+
37
+ static getInstance() {
38
+ if (!DatabaseManager.#instance) {
39
+ DatabaseManager.#isInitializing = true;
40
+ DatabaseManager.#instance = new DatabaseManager();
41
+ }
42
+ return DatabaseManager.#instance;
43
+ }
44
+
45
+ async initializeConnection(dbType, config) {
46
+ const key = dbType.toLowerCase();
47
+
48
+ // If a connection already exists for this config, return it
49
+ if (this.#connections.has(key)) {
50
+ return this.#connections.get(key).client;
51
+ }
52
+
53
+ // Set initializing flag
54
+ this.#initializingConnections.set(key, true);
55
+
56
+ let client;
57
+ try {
58
+ switch (key) {
59
+ case 'redis':
60
+ client = await redis.createConnection({
61
+ isCluster: config.isCluster || false,
62
+ options: config || {},
63
+ });
64
+ break;
65
+ case 'mongodb':
66
+ client = await mongodb.createConnection(config);
67
+ break;
68
+ default:
69
+ throw new TejError(400, `Unsupported database type: ${dbType}`);
70
+ }
71
+
72
+ this.#connections.set(key, {
73
+ type: dbType,
74
+ client,
75
+ config,
76
+ });
77
+
78
+ // Clear initializing flag
79
+ this.#initializingConnections.delete(key);
80
+
81
+ return client;
82
+ } catch (error) {
83
+ // Clear initializing flag on error
84
+ this.#initializingConnections.delete(key);
85
+ logger.error(`Failed to initialize ${dbType} connection:`, error);
86
+ throw error;
87
+ }
88
+ }
89
+
90
+ getConnection(dbType) {
91
+ const key = dbType.toLowerCase();
92
+ const connection = this.#connections.get(key);
93
+ if (!connection) {
94
+ throw new TejError(
95
+ 404,
96
+ `No connection found for ${dbType} with given config`,
97
+ );
98
+ }
99
+ return connection.client;
100
+ }
101
+
102
+ async closeConnection(dbType, config) {
103
+ const key = dbType.toLowerCase();
104
+ if (!this.#connections.has(key)) {
105
+ return;
106
+ }
107
+
108
+ try {
109
+ const connection = this.#connections.get(key);
110
+ switch (key) {
111
+ case 'redis':
112
+ await redis.closeConnection(connection.client);
113
+ break;
114
+ case 'mongodb':
115
+ await mongodb.closeConnection(connection.client);
116
+ break;
117
+ }
118
+
119
+ this.#connections.delete(key);
120
+ } catch (error) {
121
+ logger.error(`Error closing ${dbType} connection:`, error);
122
+ throw error;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Close all database connections
128
+ * @returns {Promise<void>}
129
+ */
130
+ async closeAllConnections() {
131
+ const closePromises = [];
132
+ for (const [key, connection] of this.#connections) {
133
+ closePromises.push(
134
+ this.closeConnection(connection.type, connection.config),
135
+ );
136
+ }
137
+ await Promise.all(closePromises);
138
+ this.#connections.clear();
139
+ }
140
+
141
+ /**
142
+ * Get all active connections
143
+ * @returns {Map<string, {type: string, client: any, config: Object}>}
144
+ */
145
+ getActiveConnections() {
146
+ return new Map(this.#connections);
147
+ }
148
+
149
+ /**
150
+ * Check if a connection exists or is being initialized
151
+ * @param {string} dbType - Type of database
152
+ * @param {Object} config - Database configuration
153
+ * @returns {{ exists: boolean, initializing: boolean }}
154
+ */
155
+ hasConnection(dbType, config) {
156
+ const key = dbType.toLowerCase();
157
+ return {
158
+ exists: this.#connections.has(key),
159
+ initializing: this.#initializingConnections.has(key),
160
+ };
161
+ }
162
+ }
163
+
164
+ const dbManager = DatabaseManager.getInstance();
165
+ export default dbManager;