@t1mmen/srtd 0.0.0-next-20251227000343
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/LICENSE +21 -0
- package/README.md +363 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +50 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/apply.d.ts +2 -0
- package/dist/commands/apply.js +105 -0
- package/dist/commands/apply.js.map +1 -0
- package/dist/commands/build.d.ts +2 -0
- package/dist/commands/build.js +134 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/clear.d.ts +2 -0
- package/dist/commands/clear.js +161 -0
- package/dist/commands/clear.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +91 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/menu.d.ts +4 -0
- package/dist/commands/menu.js +76 -0
- package/dist/commands/menu.js.map +1 -0
- package/dist/commands/promote.d.ts +2 -0
- package/dist/commands/promote.js +181 -0
- package/dist/commands/promote.js.map +1 -0
- package/dist/commands/register.d.ts +2 -0
- package/dist/commands/register.js +192 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/watch.d.ts +14 -0
- package/dist/commands/watch.js +190 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -0
- package/dist/services/DatabaseService.d.ts +113 -0
- package/dist/services/DatabaseService.js +343 -0
- package/dist/services/DatabaseService.js.map +1 -0
- package/dist/services/FileSystemService.d.ts +100 -0
- package/dist/services/FileSystemService.js +237 -0
- package/dist/services/FileSystemService.js.map +1 -0
- package/dist/services/MigrationBuilder.d.ts +106 -0
- package/dist/services/MigrationBuilder.js +193 -0
- package/dist/services/MigrationBuilder.js.map +1 -0
- package/dist/services/Orchestrator.d.ts +155 -0
- package/dist/services/Orchestrator.js +622 -0
- package/dist/services/Orchestrator.js.map +1 -0
- package/dist/services/StateService.d.ts +169 -0
- package/dist/services/StateService.js +463 -0
- package/dist/services/StateService.js.map +1 -0
- package/dist/types.d.ts +48 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/badge.d.ts +14 -0
- package/dist/ui/badge.js +28 -0
- package/dist/ui/badge.js.map +1 -0
- package/dist/ui/branding.d.ts +9 -0
- package/dist/ui/branding.js +35 -0
- package/dist/ui/branding.js.map +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +5 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/results.d.ts +10 -0
- package/dist/ui/results.js +62 -0
- package/dist/ui/results.js.map +1 -0
- package/dist/ui/spinner.d.ts +5 -0
- package/dist/ui/spinner.js +8 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/utils/config.d.ts +12 -0
- package/dist/utils/config.js +67 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/createEmptyBuildLog.d.ts +1 -0
- package/dist/utils/createEmptyBuildLog.js +10 -0
- package/dist/utils/createEmptyBuildLog.js.map +1 -0
- package/dist/utils/ensureDirectories.d.ts +4 -0
- package/dist/utils/ensureDirectories.js +23 -0
- package/dist/utils/ensureDirectories.js.map +1 -0
- package/dist/utils/fileExists.d.ts +1 -0
- package/dist/utils/fileExists.js +11 -0
- package/dist/utils/fileExists.js.map +1 -0
- package/dist/utils/findProjectRoot.d.ts +1 -0
- package/dist/utils/findProjectRoot.js +25 -0
- package/dist/utils/findProjectRoot.js.map +1 -0
- package/dist/utils/getErrorMessage.d.ts +9 -0
- package/dist/utils/getErrorMessage.js +14 -0
- package/dist/utils/getErrorMessage.js.map +1 -0
- package/dist/utils/getNextTimestamp.d.ts +2 -0
- package/dist/utils/getNextTimestamp.js +12 -0
- package/dist/utils/getNextTimestamp.js.map +1 -0
- package/dist/utils/isWipTemplate.d.ts +1 -0
- package/dist/utils/isWipTemplate.js +6 -0
- package/dist/utils/isWipTemplate.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.js +12 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/safeCreate.d.ts +1 -0
- package/dist/utils/safeCreate.js +16 -0
- package/dist/utils/safeCreate.js.map +1 -0
- package/package.json +106 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DatabaseService - Centralized database connection and SQL execution service
|
|
3
|
+
* Handles connection pooling, retry logic, and SQL execution with transaction management
|
|
4
|
+
*/
|
|
5
|
+
import { EventEmitter } from 'node:events';
|
|
6
|
+
import pg from 'pg';
|
|
7
|
+
import type { CLIConfig, MigrationError } from '../types.js';
|
|
8
|
+
export interface DatabaseServiceConfig {
|
|
9
|
+
connectionString: string;
|
|
10
|
+
connectionTimeoutMillis?: number;
|
|
11
|
+
maxConnections?: number;
|
|
12
|
+
idleTimeoutMillis?: number;
|
|
13
|
+
maxUses?: number;
|
|
14
|
+
maxRetries?: number;
|
|
15
|
+
retryDelayMs?: number;
|
|
16
|
+
wrapInTransaction?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface DatabaseConnection {
|
|
19
|
+
isConnected: boolean;
|
|
20
|
+
isChecking: boolean;
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ConnectionStats {
|
|
24
|
+
total: number;
|
|
25
|
+
idle: number;
|
|
26
|
+
active: number;
|
|
27
|
+
}
|
|
28
|
+
export declare enum DatabaseErrorType {
|
|
29
|
+
CONNECTION_ERROR = "CONNECTION_ERROR",
|
|
30
|
+
SYNTAX_ERROR = "SYNTAX_ERROR",
|
|
31
|
+
CONSTRAINT_VIOLATION = "CONSTRAINT_VIOLATION",
|
|
32
|
+
TIMEOUT_ERROR = "TIMEOUT_ERROR",
|
|
33
|
+
TRANSACTION_ERROR = "TRANSACTION_ERROR",
|
|
34
|
+
POOL_EXHAUSTED = "POOL_EXHAUSTED",
|
|
35
|
+
UNKNOWN_ERROR = "UNKNOWN_ERROR"
|
|
36
|
+
}
|
|
37
|
+
export interface DatabaseError {
|
|
38
|
+
type: DatabaseErrorType;
|
|
39
|
+
message: string;
|
|
40
|
+
originalError?: Error;
|
|
41
|
+
code?: string;
|
|
42
|
+
detail?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface SqlExecutionResult {
|
|
45
|
+
success: boolean;
|
|
46
|
+
error?: string;
|
|
47
|
+
databaseError?: DatabaseError;
|
|
48
|
+
rows?: unknown[];
|
|
49
|
+
rowCount?: number;
|
|
50
|
+
data?: unknown;
|
|
51
|
+
}
|
|
52
|
+
export interface ExecutionConfig {
|
|
53
|
+
templateName?: string;
|
|
54
|
+
useTransaction?: boolean;
|
|
55
|
+
silent?: boolean;
|
|
56
|
+
parameters?: unknown[];
|
|
57
|
+
isolationLevel?: 'READ UNCOMMITTED' | 'READ COMMITTED' | 'REPEATABLE READ' | 'SERIALIZABLE';
|
|
58
|
+
}
|
|
59
|
+
export declare class DatabaseService extends EventEmitter {
|
|
60
|
+
private config;
|
|
61
|
+
private pool;
|
|
62
|
+
private connectionAttempts;
|
|
63
|
+
private disposed;
|
|
64
|
+
constructor(config: DatabaseServiceConfig);
|
|
65
|
+
/**
|
|
66
|
+
* Categorize database errors for better error handling
|
|
67
|
+
*/
|
|
68
|
+
private categorizeError;
|
|
69
|
+
/**
|
|
70
|
+
* Create and configure database connection pool
|
|
71
|
+
*/
|
|
72
|
+
private createPool;
|
|
73
|
+
/**
|
|
74
|
+
* Establish database connection with retry logic
|
|
75
|
+
*/
|
|
76
|
+
private retryConnection;
|
|
77
|
+
/**
|
|
78
|
+
* Get a database connection with retry logic
|
|
79
|
+
*/
|
|
80
|
+
connect(params?: {
|
|
81
|
+
silent?: boolean;
|
|
82
|
+
}): Promise<pg.PoolClient>;
|
|
83
|
+
/**
|
|
84
|
+
* Test database connectivity
|
|
85
|
+
*/
|
|
86
|
+
testConnection(): Promise<boolean>;
|
|
87
|
+
/**
|
|
88
|
+
* Execute SQL with optional transaction wrapping and parameterized queries
|
|
89
|
+
* Core method for running template content or any SQL
|
|
90
|
+
*/
|
|
91
|
+
executeSQL(sql: string, config?: ExecutionConfig): Promise<SqlExecutionResult>;
|
|
92
|
+
/**
|
|
93
|
+
* Execute SQL and return migration-compatible result
|
|
94
|
+
* Used by migration/template application logic
|
|
95
|
+
*/
|
|
96
|
+
executeMigration(content: string, templateName: string, silent?: boolean): Promise<true | MigrationError>;
|
|
97
|
+
/**
|
|
98
|
+
* Get connection pool statistics
|
|
99
|
+
*/
|
|
100
|
+
getConnectionStats(): ConnectionStats | null;
|
|
101
|
+
/**
|
|
102
|
+
* Gracefully close all database connections
|
|
103
|
+
*/
|
|
104
|
+
dispose(): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Create DatabaseService from CLI config
|
|
107
|
+
*/
|
|
108
|
+
static fromConfig(config: CLIConfig): DatabaseService;
|
|
109
|
+
/**
|
|
110
|
+
* Setup process termination handlers for graceful shutdown
|
|
111
|
+
*/
|
|
112
|
+
private setupProcessHandlers;
|
|
113
|
+
}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DatabaseService - Centralized database connection and SQL execution service
|
|
3
|
+
* Handles connection pooling, retry logic, and SQL execution with transaction management
|
|
4
|
+
*/
|
|
5
|
+
import { EventEmitter } from 'node:events';
|
|
6
|
+
import pg from 'pg';
|
|
7
|
+
import { logger } from '../utils/logger.js';
|
|
8
|
+
export var DatabaseErrorType;
|
|
9
|
+
(function (DatabaseErrorType) {
|
|
10
|
+
DatabaseErrorType["CONNECTION_ERROR"] = "CONNECTION_ERROR";
|
|
11
|
+
DatabaseErrorType["SYNTAX_ERROR"] = "SYNTAX_ERROR";
|
|
12
|
+
DatabaseErrorType["CONSTRAINT_VIOLATION"] = "CONSTRAINT_VIOLATION";
|
|
13
|
+
DatabaseErrorType["TIMEOUT_ERROR"] = "TIMEOUT_ERROR";
|
|
14
|
+
DatabaseErrorType["TRANSACTION_ERROR"] = "TRANSACTION_ERROR";
|
|
15
|
+
DatabaseErrorType["POOL_EXHAUSTED"] = "POOL_EXHAUSTED";
|
|
16
|
+
DatabaseErrorType["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
17
|
+
})(DatabaseErrorType || (DatabaseErrorType = {}));
|
|
18
|
+
export class DatabaseService extends EventEmitter {
|
|
19
|
+
config;
|
|
20
|
+
pool;
|
|
21
|
+
connectionAttempts = 0;
|
|
22
|
+
disposed = false;
|
|
23
|
+
constructor(config) {
|
|
24
|
+
super();
|
|
25
|
+
this.config = {
|
|
26
|
+
connectionTimeoutMillis: 5000,
|
|
27
|
+
maxConnections: 6, // Default maxRetries * 2
|
|
28
|
+
idleTimeoutMillis: 2000,
|
|
29
|
+
maxUses: 500,
|
|
30
|
+
maxRetries: 3,
|
|
31
|
+
retryDelayMs: 500,
|
|
32
|
+
wrapInTransaction: false,
|
|
33
|
+
...config,
|
|
34
|
+
};
|
|
35
|
+
// Handle process termination
|
|
36
|
+
this.setupProcessHandlers();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Categorize database errors for better error handling
|
|
40
|
+
*/
|
|
41
|
+
categorizeError(error) {
|
|
42
|
+
const pgError = error;
|
|
43
|
+
const errorCode = pgError?.code;
|
|
44
|
+
const errorMessage = pgError?.message || String(error);
|
|
45
|
+
// Connection errors
|
|
46
|
+
if (errorCode === 'ECONNREFUSED' || errorCode === 'ENOTFOUND' || errorCode === 'ECONNRESET') {
|
|
47
|
+
return {
|
|
48
|
+
type: DatabaseErrorType.CONNECTION_ERROR,
|
|
49
|
+
message: 'Database connection failed',
|
|
50
|
+
originalError: error instanceof Error ? error : new Error(String(error)),
|
|
51
|
+
code: errorCode,
|
|
52
|
+
detail: errorMessage,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Pool exhaustion
|
|
56
|
+
if (errorMessage.includes('pool is exhausted') || errorMessage.includes('too many clients')) {
|
|
57
|
+
return {
|
|
58
|
+
type: DatabaseErrorType.POOL_EXHAUSTED,
|
|
59
|
+
message: 'Database connection pool exhausted',
|
|
60
|
+
originalError: error instanceof Error ? error : new Error(String(error)),
|
|
61
|
+
code: errorCode,
|
|
62
|
+
detail: errorMessage,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Timeout errors
|
|
66
|
+
if (errorCode === 'ETIMEOUT' || errorMessage.includes('timeout')) {
|
|
67
|
+
return {
|
|
68
|
+
type: DatabaseErrorType.TIMEOUT_ERROR,
|
|
69
|
+
message: 'Database operation timed out',
|
|
70
|
+
originalError: error instanceof Error ? error : new Error(String(error)),
|
|
71
|
+
code: errorCode,
|
|
72
|
+
detail: errorMessage,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// PostgreSQL specific error codes
|
|
76
|
+
if (errorCode) {
|
|
77
|
+
// Syntax errors (42xxx)
|
|
78
|
+
if (errorCode.startsWith('42')) {
|
|
79
|
+
return {
|
|
80
|
+
type: DatabaseErrorType.SYNTAX_ERROR,
|
|
81
|
+
message: 'SQL syntax error',
|
|
82
|
+
originalError: error instanceof Error ? error : new Error(String(error)),
|
|
83
|
+
code: errorCode,
|
|
84
|
+
detail: errorMessage,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// Constraint violations (23xxx)
|
|
88
|
+
if (errorCode.startsWith('23')) {
|
|
89
|
+
return {
|
|
90
|
+
type: DatabaseErrorType.CONSTRAINT_VIOLATION,
|
|
91
|
+
message: 'Database constraint violation',
|
|
92
|
+
originalError: error instanceof Error ? error : new Error(String(error)),
|
|
93
|
+
code: errorCode,
|
|
94
|
+
detail: errorMessage,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Transaction errors (25xxx, 40xxx)
|
|
98
|
+
if (errorCode.startsWith('25') || errorCode.startsWith('40')) {
|
|
99
|
+
return {
|
|
100
|
+
type: DatabaseErrorType.TRANSACTION_ERROR,
|
|
101
|
+
message: 'Transaction error',
|
|
102
|
+
originalError: error instanceof Error ? error : new Error(String(error)),
|
|
103
|
+
code: errorCode,
|
|
104
|
+
detail: errorMessage,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Default to unknown error
|
|
109
|
+
return {
|
|
110
|
+
type: DatabaseErrorType.UNKNOWN_ERROR,
|
|
111
|
+
message: errorMessage,
|
|
112
|
+
originalError: error instanceof Error ? error : new Error(String(error)),
|
|
113
|
+
code: errorCode,
|
|
114
|
+
detail: errorMessage,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Create and configure database connection pool
|
|
119
|
+
*/
|
|
120
|
+
async createPool() {
|
|
121
|
+
// Only create a new pool if one doesn't exist or has been ended
|
|
122
|
+
if (!this.pool || this.pool.ended) {
|
|
123
|
+
this.pool = new pg.Pool({
|
|
124
|
+
connectionString: this.config.connectionString,
|
|
125
|
+
connectionTimeoutMillis: this.config.connectionTimeoutMillis,
|
|
126
|
+
max: this.config.maxConnections,
|
|
127
|
+
idleTimeoutMillis: this.config.idleTimeoutMillis,
|
|
128
|
+
maxUses: this.config.maxUses,
|
|
129
|
+
});
|
|
130
|
+
// Handle pool errors
|
|
131
|
+
this.pool.on('error', err => {
|
|
132
|
+
const errorMessage = `Unexpected pool error: ${err}`;
|
|
133
|
+
logger.error(errorMessage);
|
|
134
|
+
this.emit('error', new Error(errorMessage));
|
|
135
|
+
});
|
|
136
|
+
this.emit('pool:created');
|
|
137
|
+
}
|
|
138
|
+
return this.pool;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Establish database connection with retry logic
|
|
142
|
+
*/
|
|
143
|
+
async retryConnection(params) {
|
|
144
|
+
const { silent = true } = params || {};
|
|
145
|
+
this.connectionAttempts++;
|
|
146
|
+
logger.debug(`Connection attempt ${this.connectionAttempts}`);
|
|
147
|
+
try {
|
|
148
|
+
const currentPool = await this.createPool();
|
|
149
|
+
const client = await currentPool.connect();
|
|
150
|
+
this.emit('connection:established');
|
|
151
|
+
return client;
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
if (this.connectionAttempts < (this.config.maxRetries ?? 3)) {
|
|
155
|
+
if (!silent) {
|
|
156
|
+
logger.warn(`Connection failed, retrying in ${this.config.retryDelayMs}ms...`);
|
|
157
|
+
}
|
|
158
|
+
await new Promise(resolve => setTimeout(resolve, this.config.retryDelayMs));
|
|
159
|
+
return this.retryConnection(params);
|
|
160
|
+
}
|
|
161
|
+
const databaseError = this.categorizeError(err);
|
|
162
|
+
const error = new Error(`Database connection failed after ${this.config.maxRetries} attempts: ${databaseError.message}`);
|
|
163
|
+
this.emit('connection:failed', { error, databaseError });
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get a database connection with retry logic
|
|
169
|
+
*/
|
|
170
|
+
async connect(params) {
|
|
171
|
+
this.connectionAttempts = 0;
|
|
172
|
+
return await this.retryConnection(params);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Test database connectivity
|
|
176
|
+
*/
|
|
177
|
+
async testConnection() {
|
|
178
|
+
try {
|
|
179
|
+
const client = await this.connect({ silent: true });
|
|
180
|
+
try {
|
|
181
|
+
await client.query('SELECT 1');
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
finally {
|
|
185
|
+
client.release();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Execute SQL with optional transaction wrapping and parameterized queries
|
|
194
|
+
* Core method for running template content or any SQL
|
|
195
|
+
*/
|
|
196
|
+
async executeSQL(sql, config) {
|
|
197
|
+
const { templateName = 'unknown', useTransaction = this.config.wrapInTransaction, silent = false, parameters = [], isolationLevel, } = config || {};
|
|
198
|
+
const client = await this.connect({ silent });
|
|
199
|
+
try {
|
|
200
|
+
if (useTransaction) {
|
|
201
|
+
await client.query('BEGIN');
|
|
202
|
+
// Set isolation level if specified
|
|
203
|
+
if (isolationLevel) {
|
|
204
|
+
await client.query(`SET TRANSACTION ISOLATION LEVEL ${isolationLevel}`);
|
|
205
|
+
}
|
|
206
|
+
// Use advisory lock for templates to prevent concurrent modifications
|
|
207
|
+
if (templateName !== 'unknown') {
|
|
208
|
+
const lockKey = Math.abs(Buffer.from(templateName).reduce((acc, byte) => acc + byte, 0));
|
|
209
|
+
await client.query(`SELECT pg_advisory_xact_lock(${lockKey}::bigint)`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// Execute with or without parameters for security
|
|
213
|
+
const result = parameters.length > 0 ? await client.query(sql, parameters) : await client.query(sql);
|
|
214
|
+
if (useTransaction) {
|
|
215
|
+
await client.query('COMMIT');
|
|
216
|
+
}
|
|
217
|
+
if (!silent) {
|
|
218
|
+
logger.success(`SQL executed successfully for ${templateName}`);
|
|
219
|
+
}
|
|
220
|
+
this.emit('sql:success', { templateName, rowCount: result.rowCount });
|
|
221
|
+
return {
|
|
222
|
+
success: true,
|
|
223
|
+
rows: result.rows,
|
|
224
|
+
rowCount: result.rowCount ?? 0,
|
|
225
|
+
data: result.rows,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
if (useTransaction) {
|
|
230
|
+
try {
|
|
231
|
+
await client.query('ROLLBACK');
|
|
232
|
+
}
|
|
233
|
+
catch (rollbackError) {
|
|
234
|
+
logger.error(`Rollback failed: ${rollbackError}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const databaseError = this.categorizeError(error);
|
|
238
|
+
const errorMessage = databaseError.message;
|
|
239
|
+
if (!silent) {
|
|
240
|
+
logger.error(`SQL execution failed for ${templateName}: ${errorMessage} (${databaseError.type})`);
|
|
241
|
+
}
|
|
242
|
+
this.emit('sql:error', { templateName, error: errorMessage, errorType: databaseError.type });
|
|
243
|
+
return {
|
|
244
|
+
success: false,
|
|
245
|
+
error: errorMessage,
|
|
246
|
+
databaseError,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
finally {
|
|
250
|
+
client.release();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Execute SQL and return migration-compatible result
|
|
255
|
+
* Used by migration/template application logic
|
|
256
|
+
*/
|
|
257
|
+
async executeMigration(content, templateName, silent = false) {
|
|
258
|
+
const result = await this.executeSQL(content, {
|
|
259
|
+
templateName,
|
|
260
|
+
useTransaction: true,
|
|
261
|
+
silent,
|
|
262
|
+
isolationLevel: 'READ COMMITTED', // Safe default for migrations
|
|
263
|
+
});
|
|
264
|
+
if (result.success) {
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
file: templateName,
|
|
269
|
+
error: result.error || 'Unknown error',
|
|
270
|
+
templateName,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Get connection pool statistics
|
|
275
|
+
*/
|
|
276
|
+
getConnectionStats() {
|
|
277
|
+
if (!this.pool)
|
|
278
|
+
return null;
|
|
279
|
+
return {
|
|
280
|
+
total: this.pool.totalCount,
|
|
281
|
+
idle: this.pool.idleCount,
|
|
282
|
+
active: this.pool.totalCount - this.pool.idleCount,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Gracefully close all database connections
|
|
287
|
+
*/
|
|
288
|
+
async dispose() {
|
|
289
|
+
if (this.disposed)
|
|
290
|
+
return;
|
|
291
|
+
this.disposed = true;
|
|
292
|
+
if (this.pool && !this.pool.ended) {
|
|
293
|
+
try {
|
|
294
|
+
// Suppress errors during shutdown
|
|
295
|
+
this.pool.on('error', () => {
|
|
296
|
+
// Empty handler on purpose
|
|
297
|
+
});
|
|
298
|
+
// End the pool with a timeout
|
|
299
|
+
const endPromise = this.pool.end();
|
|
300
|
+
const timeoutPromise = new Promise(resolve => {
|
|
301
|
+
setTimeout(() => {
|
|
302
|
+
logger.debug('Pool end timed out, proceeding anyway');
|
|
303
|
+
resolve();
|
|
304
|
+
}, 1000);
|
|
305
|
+
});
|
|
306
|
+
await Promise.race([endPromise, timeoutPromise]);
|
|
307
|
+
this.emit('pool:closed');
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
logger.error(`Pool end error: ${e}`);
|
|
311
|
+
// Don't re-throw disposal errors, just log them
|
|
312
|
+
}
|
|
313
|
+
finally {
|
|
314
|
+
this.pool = undefined; // Important: clear the pool reference
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Remove all listeners
|
|
318
|
+
this.removeAllListeners();
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Create DatabaseService from CLI config
|
|
322
|
+
*/
|
|
323
|
+
static fromConfig(config) {
|
|
324
|
+
return new DatabaseService({
|
|
325
|
+
connectionString: config.pgConnection,
|
|
326
|
+
wrapInTransaction: config.wrapInTransaction,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Setup process termination handlers for graceful shutdown
|
|
331
|
+
*/
|
|
332
|
+
setupProcessHandlers() {
|
|
333
|
+
// Only add handlers if we don't already have too many
|
|
334
|
+
if (process.listenerCount('SIGTERM') < 10 && process.listenerCount('SIGINT') < 10) {
|
|
335
|
+
const cleanup = () => {
|
|
336
|
+
void this.dispose().then(() => process.exit(0));
|
|
337
|
+
};
|
|
338
|
+
process.on('SIGTERM', cleanup);
|
|
339
|
+
process.on('SIGINT', cleanup);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
//# sourceMappingURL=DatabaseService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseService.js","sourceRoot":"","sources":["../../src/services/DatabaseService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileSystemService - Handles all file system operations for templates
|
|
3
|
+
* Decoupled from business logic, only handles raw file operations
|
|
4
|
+
*/
|
|
5
|
+
import { EventEmitter } from 'node:events';
|
|
6
|
+
import type { Stats } from 'node:fs';
|
|
7
|
+
export interface FileSystemConfig {
|
|
8
|
+
baseDir: string;
|
|
9
|
+
templateDir: string;
|
|
10
|
+
filter: string;
|
|
11
|
+
migrationDir: string;
|
|
12
|
+
watchOptions?: {
|
|
13
|
+
ignoreInitial?: boolean;
|
|
14
|
+
stabilityThreshold?: number;
|
|
15
|
+
pollInterval?: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface TemplateFile {
|
|
19
|
+
path: string;
|
|
20
|
+
name: string;
|
|
21
|
+
content: string;
|
|
22
|
+
hash: string;
|
|
23
|
+
relativePath: string;
|
|
24
|
+
}
|
|
25
|
+
export interface WatchEvent {
|
|
26
|
+
type: 'added' | 'changed' | 'removed';
|
|
27
|
+
path: string;
|
|
28
|
+
relativePath: string;
|
|
29
|
+
name: string;
|
|
30
|
+
}
|
|
31
|
+
export declare class FileSystemService extends EventEmitter {
|
|
32
|
+
private config;
|
|
33
|
+
private watcher;
|
|
34
|
+
private debouncedHandlers;
|
|
35
|
+
constructor(config: FileSystemConfig);
|
|
36
|
+
/**
|
|
37
|
+
* Find all template files matching the configured pattern
|
|
38
|
+
*/
|
|
39
|
+
findTemplates(): Promise<string[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Read a template file and return its content with metadata
|
|
42
|
+
*/
|
|
43
|
+
readTemplate(templatePath: string): Promise<TemplateFile>;
|
|
44
|
+
/**
|
|
45
|
+
* Check if a file exists
|
|
46
|
+
*/
|
|
47
|
+
fileExists(filePath: string): Promise<boolean>;
|
|
48
|
+
/**
|
|
49
|
+
* Write content to a file
|
|
50
|
+
*/
|
|
51
|
+
writeFile(filePath: string, content: string): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Delete a file
|
|
54
|
+
*/
|
|
55
|
+
deleteFile(filePath: string): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Rename a file
|
|
58
|
+
*/
|
|
59
|
+
renameFile(oldPath: string, newPath: string): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Get file stats
|
|
62
|
+
*/
|
|
63
|
+
getFileStats(filePath: string): Promise<Stats>;
|
|
64
|
+
/**
|
|
65
|
+
* Calculate MD5 hash of content
|
|
66
|
+
*/
|
|
67
|
+
private calculateHash;
|
|
68
|
+
/**
|
|
69
|
+
* Watch templates for changes
|
|
70
|
+
*/
|
|
71
|
+
watchTemplates(): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Stop watching templates
|
|
74
|
+
*/
|
|
75
|
+
stopWatching(): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Emit watch event with debouncing
|
|
78
|
+
*/
|
|
79
|
+
private debouncedEmit;
|
|
80
|
+
/**
|
|
81
|
+
* Emit a watch event
|
|
82
|
+
*/
|
|
83
|
+
private emitWatchEvent;
|
|
84
|
+
/**
|
|
85
|
+
* Clean up resources
|
|
86
|
+
*/
|
|
87
|
+
dispose(): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Get migration file path for a template
|
|
90
|
+
*/
|
|
91
|
+
getMigrationPath(templateName: string, timestamp: string): string;
|
|
92
|
+
/**
|
|
93
|
+
* List all migration files
|
|
94
|
+
*/
|
|
95
|
+
listMigrations(): Promise<string[]>;
|
|
96
|
+
/**
|
|
97
|
+
* Read a migration file
|
|
98
|
+
*/
|
|
99
|
+
readMigration(migrationPath: string): Promise<string>;
|
|
100
|
+
}
|