@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.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +363 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +50 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/apply.d.ts +2 -0
  7. package/dist/commands/apply.js +105 -0
  8. package/dist/commands/apply.js.map +1 -0
  9. package/dist/commands/build.d.ts +2 -0
  10. package/dist/commands/build.js +134 -0
  11. package/dist/commands/build.js.map +1 -0
  12. package/dist/commands/clear.d.ts +2 -0
  13. package/dist/commands/clear.js +161 -0
  14. package/dist/commands/clear.js.map +1 -0
  15. package/dist/commands/init.d.ts +2 -0
  16. package/dist/commands/init.js +91 -0
  17. package/dist/commands/init.js.map +1 -0
  18. package/dist/commands/menu.d.ts +4 -0
  19. package/dist/commands/menu.js +76 -0
  20. package/dist/commands/menu.js.map +1 -0
  21. package/dist/commands/promote.d.ts +2 -0
  22. package/dist/commands/promote.js +181 -0
  23. package/dist/commands/promote.js.map +1 -0
  24. package/dist/commands/register.d.ts +2 -0
  25. package/dist/commands/register.js +192 -0
  26. package/dist/commands/register.js.map +1 -0
  27. package/dist/commands/watch.d.ts +14 -0
  28. package/dist/commands/watch.js +190 -0
  29. package/dist/commands/watch.js.map +1 -0
  30. package/dist/constants.d.ts +1 -0
  31. package/dist/constants.js +2 -0
  32. package/dist/constants.js.map +1 -0
  33. package/dist/services/DatabaseService.d.ts +113 -0
  34. package/dist/services/DatabaseService.js +343 -0
  35. package/dist/services/DatabaseService.js.map +1 -0
  36. package/dist/services/FileSystemService.d.ts +100 -0
  37. package/dist/services/FileSystemService.js +237 -0
  38. package/dist/services/FileSystemService.js.map +1 -0
  39. package/dist/services/MigrationBuilder.d.ts +106 -0
  40. package/dist/services/MigrationBuilder.js +193 -0
  41. package/dist/services/MigrationBuilder.js.map +1 -0
  42. package/dist/services/Orchestrator.d.ts +155 -0
  43. package/dist/services/Orchestrator.js +622 -0
  44. package/dist/services/Orchestrator.js.map +1 -0
  45. package/dist/services/StateService.d.ts +169 -0
  46. package/dist/services/StateService.js +463 -0
  47. package/dist/services/StateService.js.map +1 -0
  48. package/dist/types.d.ts +48 -0
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/ui/badge.d.ts +14 -0
  52. package/dist/ui/badge.js +28 -0
  53. package/dist/ui/badge.js.map +1 -0
  54. package/dist/ui/branding.d.ts +9 -0
  55. package/dist/ui/branding.js +35 -0
  56. package/dist/ui/branding.js.map +1 -0
  57. package/dist/ui/index.d.ts +4 -0
  58. package/dist/ui/index.js +5 -0
  59. package/dist/ui/index.js.map +1 -0
  60. package/dist/ui/results.d.ts +10 -0
  61. package/dist/ui/results.js +62 -0
  62. package/dist/ui/results.js.map +1 -0
  63. package/dist/ui/spinner.d.ts +5 -0
  64. package/dist/ui/spinner.js +8 -0
  65. package/dist/ui/spinner.js.map +1 -0
  66. package/dist/utils/config.d.ts +12 -0
  67. package/dist/utils/config.js +67 -0
  68. package/dist/utils/config.js.map +1 -0
  69. package/dist/utils/createEmptyBuildLog.d.ts +1 -0
  70. package/dist/utils/createEmptyBuildLog.js +10 -0
  71. package/dist/utils/createEmptyBuildLog.js.map +1 -0
  72. package/dist/utils/ensureDirectories.d.ts +4 -0
  73. package/dist/utils/ensureDirectories.js +23 -0
  74. package/dist/utils/ensureDirectories.js.map +1 -0
  75. package/dist/utils/fileExists.d.ts +1 -0
  76. package/dist/utils/fileExists.js +11 -0
  77. package/dist/utils/fileExists.js.map +1 -0
  78. package/dist/utils/findProjectRoot.d.ts +1 -0
  79. package/dist/utils/findProjectRoot.js +25 -0
  80. package/dist/utils/findProjectRoot.js.map +1 -0
  81. package/dist/utils/getErrorMessage.d.ts +9 -0
  82. package/dist/utils/getErrorMessage.js +14 -0
  83. package/dist/utils/getErrorMessage.js.map +1 -0
  84. package/dist/utils/getNextTimestamp.d.ts +2 -0
  85. package/dist/utils/getNextTimestamp.js +12 -0
  86. package/dist/utils/getNextTimestamp.js.map +1 -0
  87. package/dist/utils/isWipTemplate.d.ts +1 -0
  88. package/dist/utils/isWipTemplate.js +6 -0
  89. package/dist/utils/isWipTemplate.js.map +1 -0
  90. package/dist/utils/logger.d.ts +9 -0
  91. package/dist/utils/logger.js +12 -0
  92. package/dist/utils/logger.js.map +1 -0
  93. package/dist/utils/safeCreate.d.ts +1 -0
  94. package/dist/utils/safeCreate.js +16 -0
  95. package/dist/utils/safeCreate.js.map +1 -0
  96. 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
+ }