@connorbritain/mssql-mcp-server 0.1.0

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 +61 -0
  2. package/dist/audit/AuditLogger.d.ts +25 -0
  3. package/dist/audit/AuditLogger.d.ts.map +1 -0
  4. package/dist/audit/AuditLogger.js +91 -0
  5. package/dist/audit/AuditLogger.js.map +1 -0
  6. package/dist/config/EnvironmentManager.d.ts +36 -0
  7. package/dist/config/EnvironmentManager.d.ts.map +1 -0
  8. package/dist/config/EnvironmentManager.js +185 -0
  9. package/dist/config/EnvironmentManager.js.map +1 -0
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +615 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/tools/CreateIndexTool.d.ts +24 -0
  15. package/dist/tools/CreateIndexTool.d.ts.map +1 -0
  16. package/dist/tools/CreateIndexTool.js +64 -0
  17. package/dist/tools/CreateIndexTool.js.map +1 -0
  18. package/dist/tools/CreateTableTool.d.ts +12 -0
  19. package/dist/tools/CreateTableTool.d.ts.map +1 -0
  20. package/dist/tools/CreateTableTool.js +49 -0
  21. package/dist/tools/CreateTableTool.js.map +1 -0
  22. package/dist/tools/DeleteDataTool.d.ts +56 -0
  23. package/dist/tools/DeleteDataTool.d.ts.map +1 -0
  24. package/dist/tools/DeleteDataTool.js +103 -0
  25. package/dist/tools/DeleteDataTool.js.map +1 -0
  26. package/dist/tools/DescribeTableTool.d.ts +20 -0
  27. package/dist/tools/DescribeTableTool.d.ts.map +1 -0
  28. package/dist/tools/DescribeTableTool.js +34 -0
  29. package/dist/tools/DescribeTableTool.js.map +1 -0
  30. package/dist/tools/DropTableTool.d.ts +12 -0
  31. package/dist/tools/DropTableTool.d.ts.map +1 -0
  32. package/dist/tools/DropTableTool.js +37 -0
  33. package/dist/tools/DropTableTool.js.map +1 -0
  34. package/dist/tools/ExplainQueryTool.d.ts +24 -0
  35. package/dist/tools/ExplainQueryTool.d.ts.map +1 -0
  36. package/dist/tools/ExplainQueryTool.js +98 -0
  37. package/dist/tools/ExplainQueryTool.js.map +1 -0
  38. package/dist/tools/InsertDataTool.d.ts +17 -0
  39. package/dist/tools/InsertDataTool.d.ts.map +1 -0
  40. package/dist/tools/InsertDataTool.js +144 -0
  41. package/dist/tools/InsertDataTool.js.map +1 -0
  42. package/dist/tools/ListTableTool.d.ts +18 -0
  43. package/dist/tools/ListTableTool.d.ts.map +1 -0
  44. package/dist/tools/ListTableTool.js +43 -0
  45. package/dist/tools/ListTableTool.js.map +1 -0
  46. package/dist/tools/ProfileTableTool.d.ts +78 -0
  47. package/dist/tools/ProfileTableTool.d.ts.map +1 -0
  48. package/dist/tools/ProfileTableTool.js +372 -0
  49. package/dist/tools/ProfileTableTool.js.map +1 -0
  50. package/dist/tools/ReadDataTool.d.ts +58 -0
  51. package/dist/tools/ReadDataTool.d.ts.map +1 -0
  52. package/dist/tools/ReadDataTool.js +265 -0
  53. package/dist/tools/ReadDataTool.js.map +1 -0
  54. package/dist/tools/RelationshipInspectorTool.d.ts +46 -0
  55. package/dist/tools/RelationshipInspectorTool.d.ts.map +1 -0
  56. package/dist/tools/RelationshipInspectorTool.js +155 -0
  57. package/dist/tools/RelationshipInspectorTool.js.map +1 -0
  58. package/dist/tools/SearchSchemaTool.d.ts +88 -0
  59. package/dist/tools/SearchSchemaTool.d.ts.map +1 -0
  60. package/dist/tools/SearchSchemaTool.js +236 -0
  61. package/dist/tools/SearchSchemaTool.js.map +1 -0
  62. package/dist/tools/TestConnectionTool.d.ts +36 -0
  63. package/dist/tools/TestConnectionTool.d.ts.map +1 -0
  64. package/dist/tools/TestConnectionTool.js +155 -0
  65. package/dist/tools/TestConnectionTool.js.map +1 -0
  66. package/dist/tools/UpdateDataTool.d.ts +61 -0
  67. package/dist/tools/UpdateDataTool.d.ts.map +1 -0
  68. package/dist/tools/UpdateDataTool.js +117 -0
  69. package/dist/tools/UpdateDataTool.js.map +1 -0
  70. package/package.json +42 -0
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # @connorbritain/mssql-mcp-server
2
+
3
+ Model Context Protocol server for SQL Server. This is the Node.js implementation.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @connorbritain/mssql-mcp-server@latest
9
+ # or run ad-hoc
10
+ npx @connorbritain/mssql-mcp-server@latest
11
+ ```
12
+
13
+ ## CLI
14
+
15
+ ```
16
+ mssql-mcp-server
17
+ ```
18
+
19
+ The entrypoint expects env vars:
20
+
21
+ | Variable | Required | Notes |
22
+ | --- | --- | --- |
23
+ | `SERVER_NAME` | ✅ | SQL Server hostname/IP |
24
+ | `DATABASE_NAME` | ✅ | Database to target |
25
+ | `SQL_AUTH_MODE` | | `sql`, `windows`, or `aad` (default `aad`) |
26
+ | `SQL_USERNAME`/`SQL_PASSWORD` | | Required for `sql`/`windows` modes |
27
+ | `SQL_DOMAIN` | | Optional for NTLM |
28
+ | `SQL_PORT` | | Defaults to `1433` |
29
+ | `TRUST_SERVER_CERTIFICATE` | | Set `true` for dev/self-signed |
30
+ | `CONNECTION_TIMEOUT` | | Seconds, default `30` |
31
+ | `READONLY` | | `true` disables write tools |
32
+ | `PROFILE_SAMPLE_SIZE_DEFAULT` | | Default profiling sample size (default `50`) |
33
+ | `PROFILE_SAMPLE_RETURN_LIMIT` | | Max sample rows returned (default `10`) |
34
+ | `SEARCH_SCHEMA_DEFAULT_LIMIT` | | Default pagination size for `search_schema` (default `50`) |
35
+
36
+ ## Example MCP config
37
+
38
+ ```json
39
+ {
40
+ "mcpServers": {
41
+ "mssql": {
42
+ "command": "npx",
43
+ "args": ["@connorbritain/mssql-mcp-server@latest"],
44
+ "env": {
45
+ "SERVER_NAME": "127.0.0.1",
46
+ "DATABASE_NAME": "sampledb",
47
+ "SQL_AUTH_MODE": "sql",
48
+ "SQL_USERNAME": "sa",
49
+ "SQL_PASSWORD": "YourPassword123",
50
+ "READONLY": "false"
51
+ }
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ This package ships precompiled JS under `dist/`.
58
+
59
+ **Full documentation:** See the [main README](../../README.md) for complete feature list, configuration options, and safety guidance.
60
+
61
+ **Repository:** https://github.com/ConnorBritain/mssql-mcp-server
@@ -0,0 +1,25 @@
1
+ export interface AuditLogEntry {
2
+ timestamp: string;
3
+ toolName: string;
4
+ arguments?: Record<string, any>;
5
+ result?: {
6
+ success: boolean;
7
+ recordCount?: number;
8
+ error?: string;
9
+ };
10
+ durationMs?: number;
11
+ sessionId?: string;
12
+ userId?: string;
13
+ }
14
+ export declare class AuditLogger {
15
+ private readonly logFilePath;
16
+ private readonly enabled;
17
+ private readonly redactSensitiveData;
18
+ constructor();
19
+ private ensureLogDirectory;
20
+ private redactArguments;
21
+ log(entry: AuditLogEntry): void;
22
+ logToolInvocation(toolName: string, args: any, result: any, durationMs: number, sessionId?: string, userId?: string): void;
23
+ }
24
+ export declare const auditLogger: AuditLogger;
25
+ //# sourceMappingURL=AuditLogger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuditLogger.d.ts","sourceRoot":"","sources":["../../src/audit/AuditLogger.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE;QACP,OAAO,EAAE,OAAO,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAU;;IAoB9C,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,eAAe;IA6BvB,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAkB/B,iBAAiB,CACf,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,GAAG,EACX,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,IAAI;CAiBR;AAGD,eAAO,MAAM,WAAW,aAAoB,CAAC"}
@@ -0,0 +1,91 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ export class AuditLogger {
4
+ constructor() {
5
+ // Read config from env vars
6
+ const logPath = process.env.AUDIT_LOG_PATH;
7
+ this.enabled = process.env.AUDIT_LOGGING !== "false"; // Enabled by default
8
+ this.redactSensitiveData = process.env.AUDIT_REDACT_SENSITIVE !== "false"; // Redact by default
9
+ if (this.enabled && logPath) {
10
+ this.logFilePath = path.resolve(logPath);
11
+ this.ensureLogDirectory();
12
+ }
13
+ else if (this.enabled) {
14
+ // Default to logs/audit.jsonl in the project root
15
+ this.logFilePath = path.resolve(process.cwd(), "logs", "audit.jsonl");
16
+ this.ensureLogDirectory();
17
+ }
18
+ else {
19
+ this.logFilePath = "";
20
+ }
21
+ }
22
+ ensureLogDirectory() {
23
+ if (!this.logFilePath)
24
+ return;
25
+ const dir = path.dirname(this.logFilePath);
26
+ if (!fs.existsSync(dir)) {
27
+ fs.mkdirSync(dir, { recursive: true });
28
+ }
29
+ }
30
+ redactArguments(args) {
31
+ if (!this.redactSensitiveData) {
32
+ return args;
33
+ }
34
+ const redacted = { ...args };
35
+ const sensitiveKeys = [
36
+ "password",
37
+ "secret",
38
+ "token",
39
+ "key",
40
+ "authorization",
41
+ "auth",
42
+ "credential",
43
+ ];
44
+ for (const [key, value] of Object.entries(redacted)) {
45
+ const lowerKey = key.toLowerCase();
46
+ if (sensitiveKeys.some((sensitive) => lowerKey.includes(sensitive))) {
47
+ redacted[key] = "[REDACTED]";
48
+ }
49
+ else if (typeof value === "string" && value.length > 500) {
50
+ // Truncate very long strings (likely large query results)
51
+ redacted[key] = value.substring(0, 500) + "... [TRUNCATED]";
52
+ }
53
+ }
54
+ return redacted;
55
+ }
56
+ log(entry) {
57
+ if (!this.enabled || !this.logFilePath) {
58
+ return;
59
+ }
60
+ try {
61
+ const logEntry = {
62
+ ...entry,
63
+ arguments: entry.arguments ? this.redactArguments(entry.arguments) : undefined,
64
+ };
65
+ const logLine = JSON.stringify(logEntry) + "\n";
66
+ fs.appendFileSync(this.logFilePath, logLine, { encoding: "utf-8" });
67
+ }
68
+ catch (error) {
69
+ console.error("Failed to write audit log:", error);
70
+ }
71
+ }
72
+ logToolInvocation(toolName, args, result, durationMs, sessionId, userId) {
73
+ const entry = {
74
+ timestamp: new Date().toISOString(),
75
+ toolName,
76
+ arguments: args || {},
77
+ result: {
78
+ success: result?.success ?? false,
79
+ recordCount: result?.recordCount ?? result?.rowsAffected,
80
+ error: result?.error,
81
+ },
82
+ durationMs: Math.round(durationMs),
83
+ sessionId,
84
+ userId,
85
+ };
86
+ this.log(entry);
87
+ }
88
+ }
89
+ // Singleton instance
90
+ export const auditLogger = new AuditLogger();
91
+ //# sourceMappingURL=AuditLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuditLogger.js","sourceRoot":"","sources":["../../src/audit/AuditLogger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import sql from "mssql";
2
+ export interface EnvironmentConfig {
3
+ name: string;
4
+ server: string;
5
+ database: string;
6
+ port?: number;
7
+ authMode: "sql" | "windows" | "aad";
8
+ username?: string;
9
+ password?: string;
10
+ domain?: string;
11
+ trustServerCertificate?: boolean;
12
+ connectionTimeout?: number;
13
+ readonly?: boolean;
14
+ allowedTools?: string[];
15
+ maxRowsDefault?: number;
16
+ description?: string;
17
+ }
18
+ export interface EnvironmentsConfig {
19
+ defaultEnvironment?: string;
20
+ environments: EnvironmentConfig[];
21
+ }
22
+ export declare class EnvironmentManager {
23
+ private readonly environments;
24
+ private defaultEnvironment?;
25
+ private readonly connections;
26
+ constructor(configPath?: string);
27
+ private loadFromFile;
28
+ private loadFromEnvVars;
29
+ getEnvironment(name?: string): EnvironmentConfig;
30
+ listEnvironments(): EnvironmentConfig[];
31
+ getConnection(environmentName?: string): Promise<sql.ConnectionPool>;
32
+ private createSqlConfig;
33
+ closeAll(): Promise<void>;
34
+ }
35
+ export declare function getEnvironmentManager(): EnvironmentManager;
36
+ //# sourceMappingURL=EnvironmentManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnvironmentManager.d.ts","sourceRoot":"","sources":["../../src/config/EnvironmentManager.ts"],"names":[],"mappings":"AAGA,OAAO,GAAG,MAAM,OAAO,CAAC;AAExB,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,KAAK,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,iBAAiB,EAAE,CAAC;CACnC;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiC;IAC9D,OAAO,CAAC,kBAAkB,CAAC,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8D;gBAE9E,UAAU,CAAC,EAAE,MAAM;IAa/B,OAAO,CAAC,YAAY;IAyBpB,OAAO,CAAC,eAAe;IA+BvB,cAAc,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,iBAAiB;IAahD,gBAAgB,IAAI,iBAAiB,EAAE;IAIjC,aAAa,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;YA2B5D,eAAe;IAoFvB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAShC;AAKD,wBAAgB,qBAAqB,IAAI,kBAAkB,CAM1D"}
@@ -0,0 +1,185 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { InteractiveBrowserCredential } from "@azure/identity";
4
+ import sql from "mssql";
5
+ export class EnvironmentManager {
6
+ constructor(configPath) {
7
+ this.environments = new Map();
8
+ this.connections = new Map();
9
+ // Try to load from config file first
10
+ if (configPath) {
11
+ this.loadFromFile(configPath);
12
+ }
13
+ else {
14
+ // Fall back to environment variables for single environment
15
+ this.loadFromEnvVars();
16
+ }
17
+ }
18
+ loadFromFile(configPath) {
19
+ try {
20
+ const resolvedPath = path.resolve(configPath);
21
+ if (!fs.existsSync(resolvedPath)) {
22
+ console.warn(`Environment config file not found at ${resolvedPath}, falling back to env vars`);
23
+ this.loadFromEnvVars();
24
+ return;
25
+ }
26
+ const configContent = fs.readFileSync(resolvedPath, "utf-8");
27
+ const config = JSON.parse(configContent);
28
+ this.defaultEnvironment = config.defaultEnvironment;
29
+ for (const env of config.environments) {
30
+ this.environments.set(env.name, env);
31
+ }
32
+ console.log(`Loaded ${this.environments.size} environment(s) from ${resolvedPath}`);
33
+ }
34
+ catch (error) {
35
+ console.error(`Failed to load environment config: ${error}`);
36
+ this.loadFromEnvVars();
37
+ }
38
+ }
39
+ loadFromEnvVars() {
40
+ const server = process.env.SERVER_NAME;
41
+ const database = process.env.DATABASE_NAME;
42
+ if (!server || !database) {
43
+ throw new Error("No environment config file provided and SERVER_NAME/DATABASE_NAME env vars not set");
44
+ }
45
+ const defaultEnv = {
46
+ name: "default",
47
+ server,
48
+ database,
49
+ port: process.env.SQL_PORT ? parseInt(process.env.SQL_PORT, 10) : undefined,
50
+ authMode: process.env.SQL_AUTH_MODE?.toLowerCase() ?? "aad",
51
+ username: process.env.SQL_USERNAME,
52
+ password: process.env.SQL_PASSWORD,
53
+ domain: process.env.SQL_DOMAIN,
54
+ trustServerCertificate: process.env.TRUST_SERVER_CERTIFICATE?.toLowerCase() === "true",
55
+ connectionTimeout: process.env.CONNECTION_TIMEOUT
56
+ ? parseInt(process.env.CONNECTION_TIMEOUT, 10)
57
+ : 30,
58
+ readonly: process.env.READONLY === "true",
59
+ };
60
+ this.environments.set("default", defaultEnv);
61
+ this.defaultEnvironment = "default";
62
+ console.log("Loaded default environment from environment variables");
63
+ }
64
+ getEnvironment(name) {
65
+ const targetName = name || this.defaultEnvironment || "default";
66
+ const env = this.environments.get(targetName);
67
+ if (!env) {
68
+ throw new Error(`Environment '${targetName}' not found. Available: ${Array.from(this.environments.keys()).join(", ")}`);
69
+ }
70
+ return env;
71
+ }
72
+ listEnvironments() {
73
+ return Array.from(this.environments.values());
74
+ }
75
+ async getConnection(environmentName) {
76
+ const env = this.getEnvironment(environmentName);
77
+ const cached = this.connections.get(env.name);
78
+ // Check if we have a valid cached connection
79
+ if (cached &&
80
+ cached.pool.connected &&
81
+ (!cached.expiresOn || cached.expiresOn > new Date(Date.now() + 2 * 60 * 1000))) {
82
+ return cached.pool;
83
+ }
84
+ // Create new connection
85
+ const { config, expiresOn } = await this.createSqlConfig(env);
86
+ // Close old connection if exists
87
+ if (cached?.pool && cached.pool.connected) {
88
+ await cached.pool.close();
89
+ }
90
+ const pool = await sql.connect(config);
91
+ this.connections.set(env.name, { pool, expiresOn });
92
+ return pool;
93
+ }
94
+ async createSqlConfig(env) {
95
+ const baseConfig = {
96
+ server: env.server,
97
+ database: env.database,
98
+ port: env.port,
99
+ connectionTimeout: (env.connectionTimeout || 30) * 1000,
100
+ };
101
+ if (env.authMode === "sql") {
102
+ if (!env.username || !env.password) {
103
+ throw new Error(`Environment '${env.name}' requires username and password for SQL auth`);
104
+ }
105
+ return {
106
+ config: {
107
+ ...baseConfig,
108
+ user: env.username,
109
+ password: env.password,
110
+ options: {
111
+ encrypt: false,
112
+ trustServerCertificate: env.trustServerCertificate ?? false,
113
+ },
114
+ },
115
+ };
116
+ }
117
+ if (env.authMode === "windows") {
118
+ if (!env.username || !env.password) {
119
+ throw new Error(`Environment '${env.name}' requires username and password for Windows auth`);
120
+ }
121
+ return {
122
+ config: {
123
+ ...baseConfig,
124
+ options: {
125
+ encrypt: false,
126
+ trustServerCertificate: env.trustServerCertificate ?? false,
127
+ },
128
+ authentication: {
129
+ type: "ntlm",
130
+ options: {
131
+ userName: env.username,
132
+ password: env.password,
133
+ domain: env.domain || "",
134
+ },
135
+ },
136
+ },
137
+ };
138
+ }
139
+ // Azure AD auth
140
+ const credential = new InteractiveBrowserCredential({
141
+ redirectUri: "http://localhost",
142
+ });
143
+ const accessToken = await credential.getToken("https://database.windows.net/.default");
144
+ if (!accessToken?.token) {
145
+ throw new Error(`Failed to acquire Azure AD token for environment '${env.name}'`);
146
+ }
147
+ return {
148
+ config: {
149
+ ...baseConfig,
150
+ options: {
151
+ encrypt: true,
152
+ trustServerCertificate: env.trustServerCertificate ?? false,
153
+ },
154
+ authentication: {
155
+ type: "azure-active-directory-access-token",
156
+ options: {
157
+ token: accessToken.token,
158
+ },
159
+ },
160
+ },
161
+ expiresOn: accessToken?.expiresOnTimestamp
162
+ ? new Date(accessToken.expiresOnTimestamp)
163
+ : new Date(Date.now() + 30 * 60 * 1000),
164
+ };
165
+ }
166
+ async closeAll() {
167
+ for (const [name, { pool }] of this.connections.entries()) {
168
+ if (pool.connected) {
169
+ await pool.close();
170
+ console.log(`Closed connection for environment '${name}'`);
171
+ }
172
+ }
173
+ this.connections.clear();
174
+ }
175
+ }
176
+ // Singleton instance
177
+ let environmentManager;
178
+ export function getEnvironmentManager() {
179
+ if (!environmentManager) {
180
+ const configPath = process.env.ENVIRONMENTS_CONFIG_PATH;
181
+ environmentManager = new EnvironmentManager(configPath);
182
+ }
183
+ return environmentManager;
184
+ }
185
+ //# sourceMappingURL=EnvironmentManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnvironmentManager.js","sourceRoot":"","sources":["../../src/config/EnvironmentManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}