@t1mmen/srtd 0.4.3 → 0.4.4

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 (55) hide show
  1. package/dist/__tests__/apply.test.js +2 -2
  2. package/dist/__tests__/build.test.js +4 -4
  3. package/dist/__tests__/build.test.js.map +1 -1
  4. package/dist/__tests__/vitest.setup.js +2 -1
  5. package/dist/__tests__/vitest.setup.js.map +1 -1
  6. package/dist/__tests__/watch.test.js +2 -0
  7. package/dist/__tests__/watch.test.js.map +1 -1
  8. package/dist/commands/_app.js +0 -8
  9. package/dist/commands/_app.js.map +1 -1
  10. package/dist/commands/apply.js +3 -5
  11. package/dist/commands/apply.js.map +1 -1
  12. package/dist/commands/build.js +5 -6
  13. package/dist/commands/build.js.map +1 -1
  14. package/dist/commands/clear.js +10 -2
  15. package/dist/commands/clear.js.map +1 -1
  16. package/dist/commands/index.js +6 -12
  17. package/dist/commands/index.js.map +1 -1
  18. package/dist/commands/register.d.ts +1 -1
  19. package/dist/commands/register.js +62 -16
  20. package/dist/commands/register.js.map +1 -1
  21. package/dist/commands/watch.js +25 -28
  22. package/dist/commands/watch.js.map +1 -1
  23. package/dist/components/Branding.js +1 -1
  24. package/dist/components/Branding.js.map +1 -1
  25. package/dist/components/ProcessingResults.js +26 -14
  26. package/dist/components/ProcessingResults.js.map +1 -1
  27. package/dist/components/Quittable.js +6 -4
  28. package/dist/components/Quittable.js.map +1 -1
  29. package/dist/components/StatBadge.d.ts +6 -0
  30. package/dist/components/StatBadge.js +14 -0
  31. package/dist/components/StatBadge.js.map +1 -0
  32. package/dist/components/TimeSince.js +9 -5
  33. package/dist/components/TimeSince.js.map +1 -1
  34. package/dist/hooks/useDatabaseConnection.js +2 -1
  35. package/dist/hooks/useDatabaseConnection.js.map +1 -1
  36. package/dist/hooks/useFullscreen.d.ts +1 -0
  37. package/dist/hooks/useFullscreen.js +18 -0
  38. package/dist/hooks/useFullscreen.js.map +1 -0
  39. package/dist/hooks/useTemplateProcessor.js +0 -6
  40. package/dist/hooks/useTemplateProcessor.js.map +1 -1
  41. package/dist/lib/templateManager.js +41 -26
  42. package/dist/lib/templateManager.js.map +1 -1
  43. package/dist/lib/templateManager.test.js +791 -488
  44. package/dist/lib/templateManager.test.js.map +1 -1
  45. package/dist/utils/applyMigrations.test.js +4 -1
  46. package/dist/utils/applyMigrations.test.js.map +1 -1
  47. package/dist/utils/databaseConnection.js +21 -19
  48. package/dist/utils/databaseConnection.js.map +1 -1
  49. package/dist/utils/databaseConnection.test.js +1 -4
  50. package/dist/utils/databaseConnection.test.js.map +1 -1
  51. package/dist/utils/logger.js +6 -5
  52. package/dist/utils/logger.js.map +1 -1
  53. package/dist/utils/registerTemplate.js +1 -2
  54. package/dist/utils/registerTemplate.js.map +1 -1
  55. package/package.json +2 -1
@@ -56,14 +56,15 @@ import { default as path, join, relative } from 'node:path';
56
56
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
57
57
  import { TEST_FN_PREFIX } from '../__tests__/vitest.setup.js';
58
58
  import { calculateMD5 } from '../utils/calculateMD5.js';
59
- import { connect, disconnect } from '../utils/databaseConnection.js';
59
+ import { connect } from '../utils/databaseConnection.js';
60
60
  import { ensureDirectories } from '../utils/ensureDirectories.js';
61
61
  import { TemplateManager } from './templateManager.js';
62
+ const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
62
63
  describe('TemplateManager', () => {
63
64
  const testContext = {
64
65
  testId: 0,
65
- testDir: '',
66
- testFunctionName: '',
66
+ testDir: tmpdir(),
67
+ testFunctionName: TEST_FN_PREFIX,
67
68
  templateCounter: 0,
68
69
  };
69
70
  beforeEach(async () => {
@@ -100,7 +101,6 @@ describe('TemplateManager', () => {
100
101
  client.release();
101
102
  }
102
103
  await fs.rm(testContext.testDir, { recursive: true, force: true });
103
- disconnect();
104
104
  });
105
105
  // Helper to generate unique template names
106
106
  const getNextTemplateName = (prefix = 'template') => {
@@ -128,167 +128,289 @@ describe('TemplateManager', () => {
128
128
  return createTemplate(name, content, dir);
129
129
  };
130
130
  it('should create migration file when template changes', async () => {
131
- await createTemplateWithFunc('basic', '_file_change');
132
- const manager = await TemplateManager.create(testContext.testDir);
133
- await manager.processTemplates({ generateFiles: true });
134
- const migrations = await fs.readdir(join(testContext.testDir, 'test-migrations'));
135
- expect(migrations.length).toBe(1);
131
+ const env_1 = { stack: [], error: void 0, hasError: false };
132
+ try {
133
+ await createTemplateWithFunc('basic', '_file_change');
134
+ const manager = __addDisposableResource(env_1, await TemplateManager.create(testContext.testDir), false);
135
+ await manager.processTemplates({ generateFiles: true });
136
+ const migrations = await fs.readdir(join(testContext.testDir, 'test-migrations'));
137
+ expect(migrations.length).toBe(1);
138
+ }
139
+ catch (e_1) {
140
+ env_1.error = e_1;
141
+ env_1.hasError = true;
142
+ }
143
+ finally {
144
+ __disposeResources(env_1);
145
+ }
136
146
  });
137
147
  it('should not allow building WIP templates', async () => {
138
- await createTemplateWithFunc('file.wip', '_wip_wont_build');
139
- const manager = await TemplateManager.create(testContext.testDir);
140
- await manager.processTemplates({ generateFiles: true });
141
- const migrations = await fs.readdir(join(testContext.testDir, 'test-migrations'));
142
- expect(migrations.filter(m => m.includes(`wip`))).toHaveLength(0);
148
+ const env_2 = { stack: [], error: void 0, hasError: false };
149
+ try {
150
+ await createTemplateWithFunc('file.wip', '_wip_wont_build');
151
+ const manager = __addDisposableResource(env_2, await TemplateManager.create(testContext.testDir), false);
152
+ await manager.processTemplates({ generateFiles: true });
153
+ const migrations = await fs.readdir(join(testContext.testDir, 'test-migrations'));
154
+ expect(migrations.filter(m => m.includes(`wip`))).toHaveLength(0);
155
+ }
156
+ catch (e_2) {
157
+ env_2.error = e_2;
158
+ env_2.hasError = true;
159
+ }
160
+ finally {
161
+ __disposeResources(env_2);
162
+ }
143
163
  });
144
164
  it('should maintain separate build and local logs', async () => {
145
- const templatePath = join(testContext.testDir, 'test-templates', `template_${testContext.testId}_1.sql`);
146
- const templateContent = `CREATE FUNCTION ${testContext.testFunctionName}() RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;`;
147
- await fs.writeFile(templatePath, templateContent);
148
- const manager = await TemplateManager.create(testContext.testDir);
149
- // Build writes to build log
150
- await manager.processTemplates({ generateFiles: true });
151
- const buildLog = JSON.parse(await fs.readFile(join(testContext.testDir, '.buildlog-test.json'), 'utf-8'));
152
- const relPath = relative(testContext.testDir, templatePath);
153
- expect(buildLog.templates[relPath].lastBuildHash).toBeDefined();
154
- // Apply writes to local log
155
- await manager.processTemplates({ apply: true });
156
- const localLog = JSON.parse(await fs.readFile(join(testContext.testDir, '.buildlog-test.local.json'), 'utf-8'));
157
- expect(localLog.templates[relPath].lastAppliedHash).toBeDefined();
165
+ const env_3 = { stack: [], error: void 0, hasError: false };
166
+ try {
167
+ const templatePath = join(testContext.testDir, 'test-templates', `template_${testContext.testId}_1.sql`);
168
+ const templateContent = `CREATE FUNCTION ${testContext.testFunctionName}() RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;`;
169
+ await fs.writeFile(templatePath, templateContent);
170
+ const manager = __addDisposableResource(env_3, await TemplateManager.create(testContext.testDir), false);
171
+ // Build writes to build log
172
+ await manager.processTemplates({ generateFiles: true });
173
+ const buildLog = JSON.parse(await fs.readFile(join(testContext.testDir, '.buildlog-test.json'), 'utf-8'));
174
+ const relPath = relative(testContext.testDir, templatePath);
175
+ expect(buildLog.templates[relPath].lastBuildHash).toBeDefined();
176
+ // Apply writes to local log
177
+ await manager.processTemplates({ apply: true });
178
+ const localLog = JSON.parse(await fs.readFile(join(testContext.testDir, '.buildlog-test.local.json'), 'utf-8'));
179
+ expect(localLog.templates[relPath].lastAppliedHash).toBeDefined();
180
+ }
181
+ catch (e_3) {
182
+ env_3.error = e_3;
183
+ env_3.hasError = true;
184
+ }
185
+ finally {
186
+ __disposeResources(env_3);
187
+ }
158
188
  });
159
189
  it('should track template state correctly', async () => {
160
- const templatePath = join(testContext.testDir, 'test-templates', `template_${testContext.testId}_1.sql`);
161
- const templateContent = `CREATE FUNCTION ${testContext.testFunctionName}() RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;`;
162
- await fs.writeFile(templatePath, templateContent);
163
- const manager = await TemplateManager.create(testContext.testDir);
164
- // Initially no state
165
- let status = await manager.getTemplateStatus(templatePath);
166
- expect(status.buildState.lastBuildHash).toBeUndefined();
167
- expect(status.buildState.lastAppliedHash).toBeUndefined();
168
- // After build
169
- await manager.processTemplates({ generateFiles: true });
170
- status = await manager.getTemplateStatus(templatePath);
171
- expect(status.buildState.lastBuildHash).toBeDefined();
172
- expect(status.buildState.lastBuildDate).toBeDefined();
173
- // After apply
174
- await manager.processTemplates({ apply: true });
175
- status = await manager.getTemplateStatus(templatePath);
176
- expect(status.buildState.lastAppliedHash).toBeDefined();
177
- expect(status.buildState.lastAppliedDate).toBeDefined();
190
+ const env_4 = { stack: [], error: void 0, hasError: false };
191
+ try {
192
+ const templatePath = join(testContext.testDir, 'test-templates', `template_${testContext.testId}_1.sql`);
193
+ const templateContent = `CREATE FUNCTION ${testContext.testFunctionName}() RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;`;
194
+ await fs.writeFile(templatePath, templateContent);
195
+ const manager = __addDisposableResource(env_4, await TemplateManager.create(testContext.testDir), false);
196
+ // Initially no state
197
+ let status = await manager.getTemplateStatus(templatePath);
198
+ expect(status.buildState.lastBuildHash).toBeUndefined();
199
+ expect(status.buildState.lastAppliedHash).toBeUndefined();
200
+ // After build
201
+ await manager.processTemplates({ generateFiles: true });
202
+ status = await manager.getTemplateStatus(templatePath);
203
+ expect(status.buildState.lastBuildHash).toBeDefined();
204
+ expect(status.buildState.lastBuildDate).toBeDefined();
205
+ // After apply
206
+ await manager.processTemplates({ apply: true });
207
+ status = await manager.getTemplateStatus(templatePath);
208
+ expect(status.buildState.lastAppliedHash).toBeDefined();
209
+ expect(status.buildState.lastAppliedDate).toBeDefined();
210
+ }
211
+ catch (e_4) {
212
+ env_4.error = e_4;
213
+ env_4.hasError = true;
214
+ }
215
+ finally {
216
+ __disposeResources(env_4);
217
+ }
178
218
  });
179
219
  it('should handle rapid template changes', async () => {
180
- const templatePath = join(testContext.testDir, 'test-templates', `template_${testContext.testId}_1.sql`);
181
- const baseContent = `CREATE OR REPLACE FUNCTION ${testContext.testFunctionName}() RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;`;
182
- await fs.writeFile(templatePath, baseContent);
183
- const manager = await TemplateManager.create(testContext.testDir);
184
- const changes = [];
185
- manager.on('templateChanged', async (template) => {
186
- changes.push(template.currentHash);
187
- });
188
- const watcher = await manager.watch();
189
- await new Promise(resolve => setTimeout(resolve, 100));
190
- // Make rapid changes
191
- for (let i = 0; i < 5; i++) {
192
- await fs.writeFile(templatePath, `${baseContent}\n-- Change ${i}`);
193
- await new Promise(resolve => setTimeout(resolve, 51));
194
- }
195
- await new Promise(resolve => setTimeout(resolve, 500));
196
- watcher.close();
197
- expect(changes.length).toBeGreaterThanOrEqual(1);
198
- expect(new Set(changes).size).toBe(changes.length); // All changes should be unique
220
+ const env_5 = { stack: [], error: void 0, hasError: false };
221
+ try {
222
+ const templatePath = join(testContext.testDir, 'test-templates', `template_${testContext.testId}_1.sql`);
223
+ const baseContent = `CREATE OR REPLACE FUNCTION ${testContext.testFunctionName}() RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;`;
224
+ await fs.writeFile(templatePath, baseContent);
225
+ const manager = __addDisposableResource(env_5, await TemplateManager.create(testContext.testDir), false);
226
+ const changes = [];
227
+ manager.on('templateChanged', async (template) => {
228
+ changes.push(template.currentHash);
229
+ });
230
+ const watcher = await manager.watch();
231
+ await wait(100);
232
+ // Make rapid changes
233
+ for (let i = 0; i < 5; i++) {
234
+ await fs.writeFile(templatePath, `${baseContent}\n-- Change ${i}`);
235
+ await wait(100);
236
+ }
237
+ await wait(500);
238
+ watcher.close();
239
+ expect(changes.length).toBeGreaterThanOrEqual(1);
240
+ expect(new Set(changes).size).toBe(changes.length); // All changes should be unique
241
+ }
242
+ catch (e_5) {
243
+ env_5.error = e_5;
244
+ env_5.hasError = true;
245
+ }
246
+ finally {
247
+ __disposeResources(env_5);
248
+ }
199
249
  }, 10000);
200
250
  it('should apply WIP templates directly to database', async () => {
201
- const templatePath = join(testContext.testDir, 'test-templates', `template_${testContext.testId}_1.wip.sql`);
202
- const templateContent = `CREATE FUNCTION ${testContext.testFunctionName}() RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;`;
203
- await fs.writeFile(templatePath, templateContent);
204
- const manager = await TemplateManager.create(testContext.testDir);
205
- const result = await manager.processTemplates({ apply: true });
206
- expect(result.errors).toHaveLength(0);
207
- const client = await connect();
251
+ const env_6 = { stack: [], error: void 0, hasError: false };
208
252
  try {
209
- const res = await client.query(`SELECT COUNT(*) FROM pg_proc WHERE proname = $1`, [
210
- testContext.testFunctionName,
211
- ]);
212
- expect(Number.parseInt(res.rows[0].count)).toBe(1);
253
+ const templatePath = join(testContext.testDir, 'test-templates', `template_${testContext.testId}_1.wip.sql`);
254
+ const templateContent = `CREATE FUNCTION ${testContext.testFunctionName}() RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;`;
255
+ await fs.writeFile(templatePath, templateContent);
256
+ const manager = __addDisposableResource(env_6, await TemplateManager.create(testContext.testDir), false);
257
+ const result = await manager.processTemplates({ apply: true });
258
+ expect(result.errors).toHaveLength(0);
259
+ const client = await connect();
260
+ try {
261
+ const res = await client.query(`SELECT COUNT(*) FROM pg_proc WHERE proname = $1`, [
262
+ testContext.testFunctionName,
263
+ ]);
264
+ expect(Number.parseInt(res.rows[0].count)).toBe(1);
265
+ }
266
+ finally {
267
+ client.release();
268
+ }
269
+ }
270
+ catch (e_6) {
271
+ env_6.error = e_6;
272
+ env_6.hasError = true;
213
273
  }
214
274
  finally {
215
- client.release();
275
+ __disposeResources(env_6);
216
276
  }
217
277
  });
218
278
  it('should handle sequential template operations', async () => {
219
- await Promise.all([...Array(5)].map((_, i) => createTemplateWithFunc(`sequencetest_${i}`, `_sequence_test_${i}`)));
220
- const manager = await TemplateManager.create(testContext.testDir);
221
- const client = await connect();
279
+ const env_7 = { stack: [], error: void 0, hasError: false };
222
280
  try {
223
- // Start transaction for isolation
224
- await client.query('BEGIN');
225
- const result = await manager.processTemplates({ apply: true });
226
- expect(result.errors).toHaveLength(0);
227
- // Verify final state
228
- const allFunctions = await client.query(`SELECT proname FROM pg_proc WHERE proname LIKE $1`, [
229
- `${testContext.testFunctionName}_sequence_test_%`,
230
- ]);
231
- expect(allFunctions.rows).toHaveLength(5);
281
+ const tmpls = await Promise.all([...Array(5)].map((_, i) => createTemplateWithFunc(`sequencetest_${i}`, `_sequence_test_${i}`)));
282
+ expect(tmpls).toHaveLength(5);
283
+ const manager = __addDisposableResource(env_7, await TemplateManager.create(testContext.testDir), false);
284
+ const client = await connect();
285
+ await wait(100);
286
+ try {
287
+ // Start transaction
288
+ await client.query('BEGIN');
289
+ const result = await manager.processTemplates({ apply: true, force: true });
290
+ // Add retry logic for verification
291
+ const verifyFunctions = async (retries = 3, delay = 200) => {
292
+ try {
293
+ const allFunctions = await client.query(`SELECT proname FROM pg_proc WHERE proname LIKE $1`, [`${testContext.testFunctionName}_sequence_test_%`]);
294
+ expect(allFunctions.rows).toHaveLength(5);
295
+ }
296
+ catch (error) {
297
+ console.log('Flakey test failed verifying functions:', error, 'retries', retries);
298
+ if (retries === 0)
299
+ throw error;
300
+ await wait(delay);
301
+ await verifyFunctions(retries - 1, delay * 2);
302
+ }
303
+ };
304
+ await verifyFunctions();
305
+ await client.query('COMMIT');
306
+ expect(result.errors).toHaveLength(0);
307
+ expect(result.applied).toHaveLength(5);
308
+ }
309
+ catch (error) {
310
+ await client.query('ROLLBACK');
311
+ throw error;
312
+ }
313
+ finally {
314
+ client.release();
315
+ }
232
316
  }
233
- catch (error) {
234
- console.error('Test failed:', error);
235
- throw error;
317
+ catch (e_7) {
318
+ env_7.error = e_7;
319
+ env_7.hasError = true;
236
320
  }
237
321
  finally {
238
- // Cleanup
239
- await client.query('ROLLBACK');
240
- client.release();
322
+ __disposeResources(env_7);
241
323
  }
242
324
  });
243
325
  it('should generate unique timestamps for multiple templates', async () => {
244
- const templates = await Promise.all([...Array(10)].map((_, i) => createTemplateWithFunc(`timestamptest_${i}`, `_unique_timestamps_${i}`)));
245
- const manager = await TemplateManager.create(testContext.testDir);
246
- await manager.processTemplates({ generateFiles: true });
247
- const migrations = await fs.readdir(join(testContext.testDir, 'test-migrations'));
248
- const timestamps = migrations.map(m => m.split('_')[0]);
249
- const uniqueTimestamps = new Set(timestamps);
250
- expect(uniqueTimestamps.size).toBe(templates.length);
251
- expect(timestamps).toEqual([...timestamps].sort());
326
+ const env_8 = { stack: [], error: void 0, hasError: false };
327
+ try {
328
+ const templates = await Promise.all([...Array(10)].map((_, i) => createTemplateWithFunc(`timestamptest_${i}`, `_unique_timestamps_${i}`)));
329
+ const manager = __addDisposableResource(env_8, await TemplateManager.create(testContext.testDir), false);
330
+ await manager.processTemplates({ generateFiles: true });
331
+ const migrations = await fs.readdir(join(testContext.testDir, 'test-migrations'));
332
+ const timestamps = migrations.map(m => m.split('_')[0]);
333
+ const uniqueTimestamps = new Set(timestamps);
334
+ expect(uniqueTimestamps.size).toBe(templates.length);
335
+ expect(timestamps).toEqual([...timestamps].sort());
336
+ }
337
+ catch (e_8) {
338
+ env_8.error = e_8;
339
+ env_8.hasError = true;
340
+ }
341
+ finally {
342
+ __disposeResources(env_8);
343
+ }
252
344
  });
253
345
  it('should handle mix of working and broken templates', async () => {
254
- await createTemplateWithFunc(`a-test-good`, '_good_and_broken_mix');
255
- await createTemplate(`a-test-bad.sql`, 'INVALID SQL SYNTAX;');
256
- const manager = await TemplateManager.create(testContext.testDir);
257
- const result = await manager.processTemplates({ apply: true });
258
- expect(result.errors).toHaveLength(1);
259
- expect(result.applied).toHaveLength(1);
260
- const client = await connect();
346
+ const env_9 = { stack: [], error: void 0, hasError: false };
261
347
  try {
262
- const res = await client.query(`SELECT COUNT(*) FROM pg_proc WHERE proname = $1`, [
263
- `${testContext.testFunctionName}_good_and_broken_mix`,
264
- ]);
265
- expect(Number.parseInt(res.rows[0].count)).toBe(1);
348
+ await createTemplateWithFunc(`a-test-good`, '_good_and_broken_mix');
349
+ await createTemplate(`a-test-bad.sql`, 'INVALID SQL SYNTAX;');
350
+ const manager = __addDisposableResource(env_9, await TemplateManager.create(testContext.testDir), false);
351
+ const result = await manager.processTemplates({ apply: true });
352
+ expect(result.errors).toHaveLength(1);
353
+ expect(result.applied).toHaveLength(1);
354
+ const client = await connect();
355
+ try {
356
+ const res = await client.query(`SELECT COUNT(*) FROM pg_proc WHERE proname = $1`, [
357
+ `${testContext.testFunctionName}_good_and_broken_mix`,
358
+ ]);
359
+ expect(Number.parseInt(res.rows[0].count)).toBe(1);
360
+ }
361
+ finally {
362
+ client.release();
363
+ }
364
+ }
365
+ catch (e_9) {
366
+ env_9.error = e_9;
367
+ env_9.hasError = true;
266
368
  }
267
369
  finally {
268
- client.release();
370
+ __disposeResources(env_9);
269
371
  }
270
372
  });
271
373
  it('should handle database errors gracefully', async () => {
272
- const manager = await TemplateManager.create(testContext.testDir);
273
- await createTemplate(`test-error.sql`, 'SELECT 1/0;'); // Division by zero error
274
- const result = await manager.processTemplates({ apply: true });
275
- expect(result.errors).toHaveLength(1);
276
- expect(result.errors[0]?.error).toMatch(/division by zero/i);
374
+ const env_10 = { stack: [], error: void 0, hasError: false };
375
+ try {
376
+ const manager = __addDisposableResource(env_10, await TemplateManager.create(testContext.testDir), false);
377
+ await createTemplate(`test-error.sql`, 'SELECT 1/0;'); // Division by zero error
378
+ const result = await manager.processTemplates({ apply: true });
379
+ expect(result.errors).toHaveLength(1);
380
+ expect(result.errors[0]?.error).toMatch(/division by zero/i);
381
+ }
382
+ catch (e_10) {
383
+ env_10.error = e_10;
384
+ env_10.hasError = true;
385
+ }
386
+ finally {
387
+ __disposeResources(env_10);
388
+ }
277
389
  });
278
390
  it('should handle file system errors', async () => {
279
391
  const errorPath = join(testContext.testDir, 'test-templates', `test-error.sql`);
280
392
  try {
281
- await createTemplate(`test-error.sql`, 'SELECT 1;');
282
- await fs.chmod(errorPath, 0o000);
283
- const manager = await TemplateManager.create(testContext.testDir);
393
+ const env_11 = { stack: [], error: void 0, hasError: false };
284
394
  try {
285
- await manager.processTemplates({ generateFiles: true });
395
+ await createTemplate(`test-error.sql`, 'SELECT 1;');
396
+ await fs.chmod(errorPath, 0o000);
397
+ const manager = __addDisposableResource(env_11, await TemplateManager.create(testContext.testDir), false);
398
+ try {
399
+ await manager.processTemplates({ generateFiles: true });
400
+ }
401
+ catch (error) {
402
+ expect(error).toBeDefined();
403
+ }
404
+ // Cleanup for afterEach
405
+ await fs.chmod(errorPath, 0o644);
286
406
  }
287
- catch (error) {
288
- expect(error).toBeDefined();
407
+ catch (e_11) {
408
+ env_11.error = e_11;
409
+ env_11.hasError = true;
410
+ }
411
+ finally {
412
+ __disposeResources(env_11);
289
413
  }
290
- // Cleanup for afterEach
291
- await fs.chmod(errorPath, 0o644);
292
414
  }
293
415
  catch (error) {
294
416
  expect(error).toBeDefined();
@@ -302,17 +424,29 @@ describe('TemplateManager', () => {
302
424
  }
303
425
  });
304
426
  it('should handle large batches of templates', async () => {
305
- // Create 50 templates
306
- await Promise.all([...Array(50)].map((_, i) => createTemplateWithFunc(`test_${i}`, `_large_batch_${i}`)));
307
- const manager = await TemplateManager.create(testContext.testDir);
308
- const result = await manager.processTemplates({ generateFiles: true });
309
- expect(result.errors).toHaveLength(0);
310
- const migrations = await fs.readdir(join(testContext.testDir, 'test-migrations'));
311
- expect(migrations.length).toBe(50);
427
+ const env_12 = { stack: [], error: void 0, hasError: false };
428
+ try {
429
+ // Create 50 templates
430
+ await Promise.all([...Array(50)].map((_, i) => createTemplateWithFunc(`test_${i}`, `_large_batch_${i}`)));
431
+ const manager = __addDisposableResource(env_12, await TemplateManager.create(testContext.testDir), false);
432
+ const result = await manager.processTemplates({ generateFiles: true });
433
+ expect(result.errors).toHaveLength(0);
434
+ const migrations = await fs.readdir(join(testContext.testDir, 'test-migrations'));
435
+ expect(migrations.length).toBe(50);
436
+ }
437
+ catch (e_12) {
438
+ env_12.error = e_12;
439
+ env_12.hasError = true;
440
+ }
441
+ finally {
442
+ __disposeResources(env_12);
443
+ }
312
444
  });
313
445
  it('should handle templates with complex SQL', async () => {
314
- const testFunctionName = `${testContext.testFunctionName}_complex`;
315
- const complexSQL = `
446
+ const env_13 = { stack: [], error: void 0, hasError: false };
447
+ try {
448
+ const testFunctionName = `${testContext.testFunctionName}_complex`;
449
+ const complexSQL = `
316
450
  CREATE OR REPLACE FUNCTION ${testFunctionName}(
317
451
  param1 integer DEFAULT 100,
318
452
  OUT result1 integer,
@@ -337,426 +471,595 @@ describe('TemplateManager', () => {
337
471
  END;
338
472
  $$ LANGUAGE plpgsql;
339
473
  `;
340
- await createTemplate(`test-complex.sql`, complexSQL);
341
- const manager = await TemplateManager.create(testContext.testDir);
342
- const result = await manager.processTemplates({ apply: true });
343
- expect(result.errors).toHaveLength(0);
344
- const client = await connect();
345
- try {
346
- const res = await client.query(`
474
+ await createTemplate(`test-complex.sql`, complexSQL);
475
+ const manager = __addDisposableResource(env_13, await TemplateManager.create(testContext.testDir), false);
476
+ const result = await manager.processTemplates({ apply: true });
477
+ expect(result.errors).toHaveLength(0);
478
+ const client = await connect();
479
+ try {
480
+ const res = await client.query(`
347
481
  SELECT proname, pronargs, prorettype::regtype::text as return_type
348
482
  FROM pg_proc
349
483
  WHERE proname = $1
350
484
  `, [testFunctionName]);
351
- expect(res.rows).toHaveLength(1);
352
- expect(res.rows[0].return_type).toBe('record');
485
+ expect(res.rows).toHaveLength(1);
486
+ expect(res.rows[0].return_type).toBe('record');
487
+ }
488
+ finally {
489
+ client.release();
490
+ }
491
+ }
492
+ catch (e_13) {
493
+ env_13.error = e_13;
494
+ env_13.hasError = true;
353
495
  }
354
496
  finally {
355
- client.release();
497
+ __disposeResources(env_13);
356
498
  }
357
499
  });
358
500
  it('should maintain template state across manager instances', async () => {
359
- const template = await createTemplateWithFunc(`test`, 'maintain_state');
360
- // First manager instance
361
- const manager1 = await TemplateManager.create(testContext.testDir);
362
- await manager1.processTemplates({ generateFiles: true });
363
- // Second manager instance should see the state
364
- const manager2 = await TemplateManager.create(testContext.testDir);
365
- const status = await manager2.getTemplateStatus(template);
366
- expect(status.buildState.lastBuildHash).toBeDefined();
501
+ const env_14 = { stack: [], error: void 0, hasError: false };
502
+ try {
503
+ const template = await createTemplateWithFunc(`test`, 'maintain_state');
504
+ // First manager instance
505
+ const manager1 = __addDisposableResource(env_14, await TemplateManager.create(testContext.testDir), false);
506
+ await manager1.processTemplates({ generateFiles: true });
507
+ // Second manager instance should see the state
508
+ const manager2 = __addDisposableResource(env_14, await TemplateManager.create(testContext.testDir), false);
509
+ const status = await manager2.getTemplateStatus(template);
510
+ expect(status.buildState.lastBuildHash).toBeDefined();
511
+ }
512
+ catch (e_14) {
513
+ env_14.error = e_14;
514
+ env_14.hasError = true;
515
+ }
516
+ finally {
517
+ __disposeResources(env_14);
518
+ }
367
519
  });
368
520
  it('should handle template additions in watch mode', async () => {
369
- const manager = await TemplateManager.create(testContext.testDir);
370
- const changes = [];
371
- manager.on('templateChanged', template => {
372
- changes.push(template.name);
373
- });
374
- const watcher = await manager.watch();
375
- // Add new template after watch started
376
- await createTemplateWithFunc('new', '_watch_addition');
377
- await new Promise(resolve => setTimeout(resolve, 150));
378
- watcher.close();
379
- expect(changes).toContain(`new_${testContext.testId}_1`);
521
+ const env_15 = { stack: [], error: void 0, hasError: false };
522
+ try {
523
+ const manager = __addDisposableResource(env_15, await TemplateManager.create(testContext.testDir), false);
524
+ const changes = [];
525
+ manager.on('templateChanged', template => {
526
+ changes.push(template.name);
527
+ });
528
+ const watcher = await manager.watch();
529
+ // Add new template after watch started
530
+ await createTemplateWithFunc('new', '_watch_addition');
531
+ await wait(150);
532
+ watcher.close();
533
+ expect(changes).toContain(`new_${testContext.testId}_1`);
534
+ }
535
+ catch (e_15) {
536
+ env_15.error = e_15;
537
+ env_15.hasError = true;
538
+ }
539
+ finally {
540
+ __disposeResources(env_15);
541
+ }
380
542
  });
381
543
  it('should handle templates in deep subdirectories', async () => {
382
- // Create nested directory structure
383
- const depth = 5;
384
- const templatePaths = [];
385
- for (let i = 1; i <= depth; i++) {
386
- const dir = [...Array(i)].map((_, idx) => `level${idx + 1}`).join('/');
387
- const templatePath = await createTemplateWithFunc(`depth-test_${i}`, `_depth_${i}`, dir);
388
- templatePaths.push(templatePath);
389
- }
390
- const manager = await TemplateManager.create(testContext.testDir);
391
- const changes = [];
392
- manager.on('templateChanged', template => {
393
- changes.push(template.name);
394
- });
395
- const watcher = await manager.watch();
396
- await new Promise(resolve => setTimeout(resolve, depth * 100 * 1.1));
397
- watcher.close();
398
- expect(changes.length).toBe(depth);
399
- // Verify each template was detected
400
- for (let i = 1; i <= depth; i++) {
401
- expect(changes).toContain(`depth-test_${i}_${testContext.testId}_${i}`);
544
+ const env_16 = { stack: [], error: void 0, hasError: false };
545
+ try {
546
+ // Create nested directory structure
547
+ const depth = 5;
548
+ const templatePaths = [];
549
+ for (let i = 1; i <= depth; i++) {
550
+ const dir = [...Array(i)].map((_, idx) => `level${idx + 1}`).join('/');
551
+ const templatePath = await createTemplateWithFunc(`depth-test_${i}`, `_depth_${i}`, dir);
552
+ templatePaths.push(templatePath);
553
+ }
554
+ const manager = __addDisposableResource(env_16, await TemplateManager.create(testContext.testDir), false);
555
+ const changes = [];
556
+ manager.on('templateChanged', template => {
557
+ changes.push(template.name);
558
+ });
559
+ const watcher = await manager.watch();
560
+ await wait(depth * 100 * 1.1);
561
+ watcher.close();
562
+ expect(changes.length).toBe(depth);
563
+ // Verify each template was detected
564
+ for (let i = 1; i <= depth; i++) {
565
+ expect(changes).toContain(`depth-test_${i}_${testContext.testId}_${i}`);
566
+ }
567
+ }
568
+ catch (e_16) {
569
+ env_16.error = e_16;
570
+ env_16.hasError = true;
571
+ }
572
+ finally {
573
+ __disposeResources(env_16);
402
574
  }
403
575
  });
404
576
  it('should only watch SQL files', async () => {
405
- const manager = await TemplateManager.create(testContext.testDir);
406
- const changes = [];
407
- manager.on('templateChanged', template => {
408
- changes.push(template.name);
409
- });
410
- const watcher = await manager.watch();
411
- await new Promise(resolve => setTimeout(resolve, 100));
412
- // Create various file types
413
- await fs.writeFile(join(testContext.testDir, 'test-templates/test.txt'), 'not sql');
414
- await fs.writeFile(join(testContext.testDir, 'test-templates/test.md'), 'not sql');
415
- await createTemplateWithFunc(`sql`, '_watch_sql_only');
416
- await new Promise(resolve => setTimeout(resolve, 500));
417
- watcher.close();
418
- expect(changes).toHaveLength(1);
419
- expect(changes[0]).toBe(`sql_${testContext.testId}_1`);
420
- });
421
- it('should handle multiple template changes simultaneously', async () => {
422
- const client = await connect();
423
- const manager = await TemplateManager.create(testContext.testDir);
424
- const changes = new Set();
425
- const count = 5;
426
- const watcher = await manager.watch();
427
- await new Promise(resolve => setTimeout(resolve, 100));
428
- manager.on('templateChanged', template => {
429
- changes.add(template.name);
430
- });
431
- // Create multiple templates simultaneously
432
- try {
433
- await createTemplateWithFunc(`rapid_test_1`, '_batch_changes_1');
434
- await createTemplateWithFunc(`rapid_test_2`, '_batch_changes_2');
435
- await createTemplateWithFunc(`rapid_test_3`, '_batch_changes_3');
436
- await createTemplateWithFunc(`rapid_test_4`, '_batch_changes_4', 'deep');
437
- await createTemplateWithFunc(`rapid_test_5`, '_batch_changes_5', 'deep/nested');
577
+ const env_17 = { stack: [], error: void 0, hasError: false };
578
+ try {
579
+ const manager = __addDisposableResource(env_17, await TemplateManager.create(testContext.testDir), false);
580
+ const changes = [];
581
+ manager.on('templateChanged', template => {
582
+ changes.push(template.name);
583
+ });
584
+ const watcher = await manager.watch();
585
+ await wait(100);
586
+ // Create various file types
587
+ await fs.writeFile(join(testContext.testDir, 'test-templates/test.txt'), 'not sql');
588
+ await fs.writeFile(join(testContext.testDir, 'test-templates/test.md'), 'not sql');
589
+ await createTemplateWithFunc(`sql`, '_watch_sql_only');
590
+ await wait(500);
591
+ watcher.close();
592
+ expect(changes).toHaveLength(1);
593
+ expect(changes[0]).toBe(`sql_${testContext.testId}_1`);
438
594
  }
439
- catch (error) {
440
- console.error('Error creating templates:', error);
441
- throw error;
595
+ catch (e_17) {
596
+ env_17.error = e_17;
597
+ env_17.hasError = true;
442
598
  }
443
- // Give enough time for all changes to be detected
444
- await new Promise(resolve => setTimeout(resolve, count * 100 * 1.1));
445
- watcher.close();
446
- expect(changes.size).toBe(count); // Should detect all 5 templates
447
- for (let i = 1; i <= count; i++) {
448
- expect(changes.has(`rapid_test_${i}_${testContext.testId}_${i}`)).toBe(true);
599
+ finally {
600
+ __disposeResources(env_17);
449
601
  }
450
- // Verify all templates were processed
451
- await new Promise(resolve => setTimeout(resolve, 50));
602
+ });
603
+ it('should handle multiple template changes simultaneously', async () => {
604
+ const env_18 = { stack: [], error: void 0, hasError: false };
452
605
  try {
453
- const res = await client.query(`SELECT proname FROM pg_proc WHERE proname LIKE $1`, [
454
- `${testContext.testFunctionName}_batch_changes_%`,
455
- ]);
456
- // expect(res).toBe('');
457
- expect(res.rows).toHaveLength(count);
606
+ const client = await connect();
607
+ const manager = __addDisposableResource(env_18, await TemplateManager.create(testContext.testDir), false);
608
+ const changes = new Set();
609
+ const count = 5;
610
+ const watcher = await manager.watch();
611
+ await wait(100);
612
+ manager.on('templateChanged', template => {
613
+ changes.add(template.name);
614
+ });
615
+ // Create multiple templates simultaneously
616
+ try {
617
+ await createTemplateWithFunc(`rapid_test_1`, '_batch_changes_1');
618
+ await createTemplateWithFunc(`rapid_test_2`, '_batch_changes_2');
619
+ await createTemplateWithFunc(`rapid_test_3`, '_batch_changes_3');
620
+ await createTemplateWithFunc(`rapid_test_4`, '_batch_changes_4', 'deep');
621
+ await createTemplateWithFunc(`rapid_test_5`, '_batch_changes_5', 'deep/nested');
622
+ }
623
+ catch (error) {
624
+ console.error('Error creating templates:', error);
625
+ throw error;
626
+ }
627
+ // Give enough time for all changes to be detected
628
+ await wait(count * 100 * 1.1);
629
+ watcher.close();
630
+ expect(changes.size).toBe(count); // Should detect all 5 templates
631
+ for (let i = 1; i <= count; i++) {
632
+ expect(changes.has(`rapid_test_${i}_${testContext.testId}_${i}`)).toBe(true);
633
+ }
634
+ // Verify all templates were processed
635
+ await wait(100);
636
+ try {
637
+ const res = await client.query(`SELECT proname FROM pg_proc WHERE proname LIKE $1`, [
638
+ `${testContext.testFunctionName}_batch_changes_%`,
639
+ ]);
640
+ // expect(res).toBe('');
641
+ expect(res.rows).toHaveLength(count);
642
+ }
643
+ catch (error) {
644
+ console.error('Error querying functions:', error);
645
+ }
646
+ finally {
647
+ client.release();
648
+ }
458
649
  }
459
- catch (error) {
460
- console.error('Error querying functions:', error);
650
+ catch (e_18) {
651
+ env_18.error = e_18;
652
+ env_18.hasError = true;
461
653
  }
462
654
  finally {
463
- client.release();
655
+ __disposeResources(env_18);
464
656
  }
465
657
  }, 15000);
466
658
  it('should handle rapid bulk template creation realistically', async () => {
467
- const TEMPLATE_COUNT = 50;
468
- const manager = await TemplateManager.create(testContext.testDir);
469
- const processed = new Set();
470
- const failed = new Set();
471
- const inProgress = new Set();
472
- const events = [];
473
- let resolveProcessing;
474
- const processingComplete = new Promise(resolve => {
475
- resolveProcessing = resolve;
476
- });
477
- manager.on('templateChanged', ({ name }) => {
478
- events.push({ event: 'changed', template: name, time: Date.now() });
479
- inProgress.add(name);
480
- });
481
- manager.on('templateApplied', ({ name }) => {
482
- events.push({ event: 'applied', template: name, time: Date.now() });
483
- processed.add(name);
484
- inProgress.delete(name);
485
- if (processed.size + failed.size === TEMPLATE_COUNT) {
486
- resolveProcessing();
487
- }
488
- });
489
- manager.on('templateError', ({ template: { name }, error }) => {
490
- events.push({ event: 'error', template: name, time: Date.now() });
491
- failed.add(name);
492
- inProgress.delete(name);
493
- console.error('Template error:', { name, error });
494
- if (processed.size + failed.size === TEMPLATE_COUNT) {
495
- resolveProcessing();
496
- }
497
- });
498
- const watcher = await manager.watch();
499
- // Create all templates
500
- await Promise.all(Array.from({ length: TEMPLATE_COUNT }, (_, i) => createTemplateWithFunc(`bulk_${i + 1}`, `_bulk_${i + 1}`)));
501
- await processingComplete;
502
- watcher.close();
503
- expect(processed.size + failed.size).toBe(TEMPLATE_COUNT);
504
- expect(inProgress.size).toBe(0);
505
- expect(failed.size).toBe(0);
659
+ const env_19 = { stack: [], error: void 0, hasError: false };
660
+ try {
661
+ const TEMPLATE_COUNT = 50;
662
+ const manager = __addDisposableResource(env_19, await TemplateManager.create(testContext.testDir), false);
663
+ const processed = new Set();
664
+ const failed = new Set();
665
+ const inProgress = new Set();
666
+ const events = [];
667
+ let resolveProcessing;
668
+ const processingComplete = new Promise(resolve => {
669
+ resolveProcessing = resolve;
670
+ });
671
+ manager.on('templateChanged', ({ name }) => {
672
+ events.push({ event: 'changed', template: name, time: Date.now() });
673
+ inProgress.add(name);
674
+ });
675
+ manager.on('templateApplied', ({ name }) => {
676
+ events.push({ event: 'applied', template: name, time: Date.now() });
677
+ processed.add(name);
678
+ inProgress.delete(name);
679
+ if (processed.size + failed.size === TEMPLATE_COUNT) {
680
+ resolveProcessing();
681
+ }
682
+ });
683
+ manager.on('templateError', ({ template: { name }, error }) => {
684
+ events.push({ event: 'error', template: name, time: Date.now() });
685
+ failed.add(name);
686
+ inProgress.delete(name);
687
+ console.error('Template error:', { name, error });
688
+ if (processed.size + failed.size === TEMPLATE_COUNT) {
689
+ resolveProcessing();
690
+ }
691
+ });
692
+ const watcher = await manager.watch();
693
+ // Create all templates
694
+ await Promise.all(Array.from({ length: TEMPLATE_COUNT }, (_, i) => createTemplateWithFunc(`bulk_${i + 1}`, `_bulk_${i + 1}`)));
695
+ await processingComplete;
696
+ watcher.close();
697
+ expect(processed.size + failed.size).toBe(TEMPLATE_COUNT);
698
+ expect(inProgress.size).toBe(0);
699
+ expect(failed.size).toBe(0);
700
+ }
701
+ catch (e_19) {
702
+ env_19.error = e_19;
703
+ env_19.hasError = true;
704
+ }
705
+ finally {
706
+ __disposeResources(env_19);
707
+ }
506
708
  });
507
709
  it('should cleanup resources when disposed', async () => {
508
- const manager = await TemplateManager.create(testContext.testDir);
509
- const changes = [];
510
- manager.on('templateChanged', template => {
511
- changes.push(template.name);
512
- });
513
- await manager.watch();
514
- // Create template before disposal
515
- await createTemplateWithFunc(`before-dispose`, 'before_dispose');
516
- await new Promise(resolve => setTimeout(resolve, 100));
517
- // Dispose and verify cleanup
518
- manager[Symbol.dispose]();
519
- // Try creating template after disposal
520
- await createTemplateWithFunc(`after-dispose`, 'after_dispose');
521
- await new Promise(resolve => setTimeout(resolve, 100));
522
- expect(changes).toHaveLength(1);
523
- expect(changes[0]).toBe(`before-dispose_${testContext.testId}_1`);
710
+ const env_20 = { stack: [], error: void 0, hasError: false };
711
+ try {
712
+ const manager = __addDisposableResource(env_20, await TemplateManager.create(testContext.testDir), false);
713
+ const changes = [];
714
+ manager.on('templateChanged', template => {
715
+ changes.push(template.name);
716
+ });
717
+ await manager.watch();
718
+ // Create template before disposal
719
+ await createTemplateWithFunc(`before-dispose`, 'before_dispose');
720
+ await wait(100);
721
+ // Dispose and verify cleanup
722
+ manager[Symbol.dispose]();
723
+ // Try creating template after disposal
724
+ await createTemplateWithFunc(`after-dispose`, 'after_dispose');
725
+ await wait(100);
726
+ expect(changes).toHaveLength(1);
727
+ expect(changes[0]).toBe(`before-dispose_${testContext.testId}_1`);
728
+ }
729
+ catch (e_20) {
730
+ env_20.error = e_20;
731
+ env_20.hasError = true;
732
+ }
733
+ finally {
734
+ __disposeResources(env_20);
735
+ }
524
736
  });
525
737
  it('should auto-cleanup with using statement', async () => {
526
738
  const changes = [];
527
739
  await (async () => {
528
- const env_1 = { stack: [], error: void 0, hasError: false };
740
+ const env_21 = { stack: [], error: void 0, hasError: false };
529
741
  try {
530
- const manager = __addDisposableResource(env_1, await TemplateManager.create(testContext.testDir), false);
742
+ const manager = __addDisposableResource(env_21, await TemplateManager.create(testContext.testDir), false);
531
743
  manager.on('templateChanged', template => {
532
744
  changes.push(template.name);
533
745
  });
534
746
  await manager.watch();
535
747
  await createTemplateWithFunc(`during-scope`, 'during_scope');
536
- await new Promise(resolve => setTimeout(resolve, 100));
748
+ await wait(100);
537
749
  }
538
- catch (e_1) {
539
- env_1.error = e_1;
540
- env_1.hasError = true;
750
+ catch (e_21) {
751
+ env_21.error = e_21;
752
+ env_21.hasError = true;
541
753
  }
542
754
  finally {
543
- __disposeResources(env_1);
755
+ __disposeResources(env_21);
544
756
  }
545
757
  })();
546
758
  // After scope exit, create another template
547
759
  await createTemplateWithFunc(`after-scope`, 'after_scope');
548
- await new Promise(resolve => setTimeout(resolve, 100));
549
- expect(changes).toHaveLength(1);
760
+ await wait(100);
550
761
  expect(changes[0]).toBe(`during-scope_${testContext.testId}_1`);
762
+ expect(changes).toHaveLength(1);
551
763
  });
552
764
  it('should not process unchanged templates', async () => {
553
- const templatePath = await createTemplateWithFunc(`initial_will_remain_unchanged`, 'unchanged_tmpl');
554
- const manager = await TemplateManager.create(testContext.testDir);
555
- await manager.watch();
556
- // First processing
557
- await manager.processTemplates({ apply: true });
558
- // Get the status after first processing
559
- const statusAfterFirstRun = await manager.getTemplateStatus(templatePath);
560
- const changes = [];
561
- manager.on('templateChanged', template => {
562
- changes.push(template.name);
563
- });
564
- // Process again without changes
565
- await manager.processTemplates({ apply: true });
566
- // Get status after second run
567
- const statusAfterSecondRun = await manager.getTemplateStatus(templatePath);
568
- expect(changes).toHaveLength(0);
569
- expect(statusAfterSecondRun.buildState.lastBuildHash).toBe(statusAfterFirstRun.buildState.lastBuildHash);
570
- expect(statusAfterSecondRun.buildState.lastAppliedHash).toBe(statusAfterFirstRun.buildState.lastAppliedHash);
765
+ const env_22 = { stack: [], error: void 0, hasError: false };
766
+ try {
767
+ const templatePath = await createTemplateWithFunc(`initial_will_remain_unchanged`, 'unchanged_tmpl');
768
+ const manager = __addDisposableResource(env_22, await TemplateManager.create(testContext.testDir), false);
769
+ await manager.watch();
770
+ // First processing
771
+ await manager.processTemplates({ apply: true });
772
+ // Get the status after first processing
773
+ const statusAfterFirstRun = await manager.getTemplateStatus(templatePath);
774
+ const changes = [];
775
+ manager.on('templateChanged', template => {
776
+ changes.push(template.name);
777
+ });
778
+ // Process again without changes
779
+ await manager.processTemplates({ apply: true });
780
+ // Get status after second run
781
+ const statusAfterSecondRun = await manager.getTemplateStatus(templatePath);
782
+ expect(changes).toHaveLength(0);
783
+ expect(statusAfterSecondRun.buildState.lastBuildHash).toBe(statusAfterFirstRun.buildState.lastBuildHash);
784
+ expect(statusAfterSecondRun.buildState.lastAppliedHash).toBe(statusAfterFirstRun.buildState.lastAppliedHash);
785
+ }
786
+ catch (e_22) {
787
+ env_22.error = e_22;
788
+ env_22.hasError = true;
789
+ }
790
+ finally {
791
+ __disposeResources(env_22);
792
+ }
571
793
  });
572
794
  it('should only process modified templates in batch', async () => {
573
- // Create two templates
574
- const template1 = await createTemplateWithFunc(`modified_tmpl_1`, 'mod_1');
575
- await createTemplateWithFunc(`modified_tmpl_2`, 'mod_2');
576
- const manager = await TemplateManager.create(testContext.testDir);
577
- // First processing of both
578
- await manager.processTemplates({ apply: true });
579
- const changes = [];
580
- manager.on('templateChanged', template => {
581
- changes.push(template.name);
582
- });
583
- // Modify only template1
795
+ const env_23 = { stack: [], error: void 0, hasError: false };
584
796
  try {
585
- await fs.writeFile(template1, `${await fs.readFile(template1, 'utf-8')}\n-- Modified`);
797
+ // Create two templates
798
+ const template1 = await createTemplateWithFunc(`modified_tmpl_1`, 'mod_1');
799
+ await createTemplateWithFunc(`modified_tmpl_2`, 'mod_2');
800
+ const manager = __addDisposableResource(env_23, await TemplateManager.create(testContext.testDir), false);
801
+ // First processing of both
802
+ await manager.processTemplates({ apply: true });
803
+ const changes = [];
804
+ manager.on('templateChanged', template => {
805
+ changes.push(template.name);
806
+ });
807
+ // Modify only template1
808
+ try {
809
+ const tmpl1content = await fs.readFile(template1, 'utf-8');
810
+ await fs.writeFile(template1, `${tmpl1content}\n-- Modified`);
811
+ }
812
+ catch (error) {
813
+ console.error('Test: Error modifying template:', error);
814
+ throw error;
815
+ }
816
+ // Process both templates again
817
+ await manager.processTemplates({ apply: true });
818
+ expect(changes).toHaveLength(1);
819
+ expect(changes[0]).toBe(`modified_tmpl_1_${testContext.testId}_1`);
586
820
  }
587
- catch (error) {
588
- console.error('Test: Error modifying template:', error);
589
- throw error;
821
+ catch (e_23) {
822
+ env_23.error = e_23;
823
+ env_23.hasError = true;
824
+ }
825
+ finally {
826
+ __disposeResources(env_23);
590
827
  }
591
- // Process both templates again
592
- await manager.processTemplates({ apply: true });
593
- expect(changes).toHaveLength(1);
594
- expect(changes[0]).toBe(`modified_tmpl_1_${testContext.testId}_1`);
595
828
  });
596
829
  it('should correctly update local buildlog on apply', async () => {
597
- const templatePath = await createTemplateWithFunc(`buildlog`, '_buildlog');
598
- const manager = await TemplateManager.create(testContext.testDir);
599
- const localBuildlogPath = join(testContext.testDir, '.buildlog-test.local.json');
600
- // Initial apply
601
- await manager.processTemplates({ apply: true });
602
- const initialLog = JSON.parse(await fs.readFile(localBuildlogPath, 'utf-8'));
603
- const relPath = relative(testContext.testDir, templatePath);
604
- const initialHash = initialLog.templates[relPath].lastAppliedHash;
605
- const initialContent = await fs.readFile(templatePath, 'utf-8');
606
- expect(initialHash).toBeDefined();
607
- // Modify template
608
- await fs.writeFile(templatePath, `${initialContent}\n-- Modified`);
609
- await new Promise(resolve => setTimeout(resolve, 100));
610
- const changedContent = await fs.readFile(templatePath, 'utf-8');
611
- // Second apply
612
- await manager.processTemplates({ apply: true });
613
- await new Promise(resolve => setTimeout(resolve, 100));
614
- const updatedLog = JSON.parse(await fs.readFile(localBuildlogPath, 'utf-8'));
615
- const newHash = updatedLog.templates[relPath].lastAppliedHash;
616
- const manualMd5 = await calculateMD5(changedContent);
617
- expect(newHash).toBeDefined();
618
- expect(newHash).toBe(manualMd5);
619
- expect(newHash).not.toBe(initialHash);
830
+ const env_24 = { stack: [], error: void 0, hasError: false };
831
+ try {
832
+ const templatePath = await createTemplateWithFunc(`buildlog`, '_buildlog');
833
+ const manager = __addDisposableResource(env_24, await TemplateManager.create(testContext.testDir), false);
834
+ const localBuildlogPath = join(testContext.testDir, '.buildlog-test.local.json');
835
+ // Initial apply
836
+ await manager.processTemplates({ apply: true });
837
+ const initialLog = JSON.parse(await fs.readFile(localBuildlogPath, 'utf-8'));
838
+ const relPath = relative(testContext.testDir, templatePath);
839
+ const initialHash = initialLog.templates[relPath].lastAppliedHash;
840
+ const initialContent = await fs.readFile(templatePath, 'utf-8');
841
+ expect(initialHash).toBeDefined();
842
+ // Modify template
843
+ await fs.writeFile(templatePath, `${initialContent}\n-- Modified`);
844
+ await wait(100);
845
+ const changedContent = await fs.readFile(templatePath, 'utf-8');
846
+ // Second apply
847
+ await manager.processTemplates({ apply: true });
848
+ await wait(100);
849
+ const updatedLog = JSON.parse(await fs.readFile(localBuildlogPath, 'utf-8'));
850
+ const newHash = updatedLog.templates[relPath].lastAppliedHash;
851
+ const manualMd5 = await calculateMD5(changedContent);
852
+ expect(newHash).toBeDefined();
853
+ expect(newHash).toBe(manualMd5);
854
+ expect(newHash).not.toBe(initialHash);
855
+ }
856
+ catch (e_24) {
857
+ env_24.error = e_24;
858
+ env_24.hasError = true;
859
+ }
860
+ finally {
861
+ __disposeResources(env_24);
862
+ }
620
863
  });
621
864
  it('should skip apply if template hash matches local buildlog', async () => {
622
- const templatePath = await createTemplateWithFunc(`skip`, '_skip_apply');
623
- const manager = await TemplateManager.create(testContext.testDir);
624
- const localBuildlogPath = join(testContext.testDir, '.buildlog-test.local.json');
625
- // Initial apply
626
- await manager.processTemplates({ apply: true });
627
- const initialLog = JSON.parse(await fs.readFile(localBuildlogPath, 'utf-8'));
628
- const relPath = relative(testContext.testDir, templatePath);
629
- const initialHash = initialLog.templates[relPath].lastAppliedHash;
630
- const initialDate = initialLog.templates[relPath].lastAppliedDate;
631
- // Wait a bit to ensure timestamp would be different
632
- await new Promise(resolve => setTimeout(resolve, 100));
633
- // Apply again without changes
634
- await manager.processTemplates({ apply: true });
635
- const updatedLog = JSON.parse(await fs.readFile(localBuildlogPath, 'utf-8'));
636
- // Hash and date should remain exactly the same since no changes were made
637
- expect(updatedLog.templates[relPath].lastAppliedHash).toBe(initialHash);
638
- expect(updatedLog.templates[relPath].lastAppliedDate).toBe(initialDate);
865
+ const env_25 = { stack: [], error: void 0, hasError: false };
866
+ try {
867
+ const templatePath = await createTemplateWithFunc(`skip`, '_skip_apply');
868
+ const manager = __addDisposableResource(env_25, await TemplateManager.create(testContext.testDir), false);
869
+ const localBuildlogPath = join(testContext.testDir, '.buildlog-test.local.json');
870
+ // Initial apply
871
+ await manager.processTemplates({ apply: true });
872
+ const initialLog = JSON.parse(await fs.readFile(localBuildlogPath, 'utf-8'));
873
+ const relPath = relative(testContext.testDir, templatePath);
874
+ const initialHash = initialLog.templates[relPath].lastAppliedHash;
875
+ const initialDate = initialLog.templates[relPath].lastAppliedDate;
876
+ // Wait a bit to ensure timestamp would be different
877
+ await wait(100);
878
+ // Apply again without changes
879
+ await manager.processTemplates({ apply: true });
880
+ const updatedLog = JSON.parse(await fs.readFile(localBuildlogPath, 'utf-8'));
881
+ // Hash and date should remain exactly the same since no changes were made
882
+ expect(updatedLog.templates[relPath].lastAppliedHash).toBe(initialHash);
883
+ expect(updatedLog.templates[relPath].lastAppliedDate).toBe(initialDate);
884
+ }
885
+ catch (e_25) {
886
+ env_25.error = e_25;
887
+ env_25.hasError = true;
888
+ }
889
+ finally {
890
+ __disposeResources(env_25);
891
+ }
639
892
  });
640
893
  it('should not reapply unchanged templates in watch mode', async () => {
641
- // Create multiple templates
642
- const templates = await Promise.all([
643
- createTemplateWithFunc(`watch-stable_1`, '_watch_1'),
644
- createTemplateWithFunc(`watch-stable_2`, '_watch_2'),
645
- ]);
646
- const manager = await TemplateManager.create(testContext.testDir);
647
- const applied = [];
648
- const changed = [];
649
- manager.on('templateChanged', template => {
650
- changed.push(template.name);
651
- });
652
- manager.on('templateApplied', template => {
653
- applied.push(template.name);
654
- });
655
- // First watch session
656
- const watcher1 = await manager.watch();
657
- await new Promise(resolve => setTimeout(resolve, 100));
658
- await watcher1.close();
659
- // Record initial state
660
- const initialApplied = [...applied];
661
- const initialChanged = [...changed];
662
- applied.length = 0;
663
- changed.length = 0;
664
- // Second watch session without any changes
665
- const watcher2 = await manager.watch();
666
- await new Promise(resolve => setTimeout(resolve, 100));
667
- await watcher2.close();
668
- expect(initialApplied).toHaveLength(2); // First run should apply both
669
- expect(initialChanged).toHaveLength(2); // First run should detect both
670
- expect(applied).toHaveLength(0); // Second run should apply none
671
- expect(changed).toHaveLength(0); // Second run should detect none
672
- // Verify the buildlog state
673
- const localBuildlogPath = join(testContext.testDir, '.buildlog-test.local.json');
674
- const buildLog = JSON.parse(await fs.readFile(localBuildlogPath, 'utf-8'));
675
- for (const templatePath of templates) {
676
- const relPath = relative(testContext.testDir, templatePath);
677
- const content = await fs.readFile(templatePath, 'utf-8');
678
- const hash = await calculateMD5(content);
679
- expect(buildLog.templates[relPath].lastAppliedHash).toBe(hash);
894
+ const env_26 = { stack: [], error: void 0, hasError: false };
895
+ try {
896
+ // Create multiple templates
897
+ const templates = await Promise.all([
898
+ createTemplateWithFunc(`watch-stable_1`, '_watch_1'),
899
+ createTemplateWithFunc(`watch-stable_2`, '_watch_2'),
900
+ ]);
901
+ const manager = __addDisposableResource(env_26, await TemplateManager.create(testContext.testDir), false);
902
+ const applied = [];
903
+ const changed = [];
904
+ manager.on('templateChanged', template => {
905
+ changed.push(template.name);
906
+ });
907
+ manager.on('templateApplied', template => {
908
+ applied.push(template.name);
909
+ });
910
+ // First watch session
911
+ const watcher1 = await manager.watch();
912
+ await wait(100);
913
+ await watcher1.close();
914
+ // Record initial state
915
+ const initialApplied = [...applied];
916
+ const initialChanged = [...changed];
917
+ applied.length = 0;
918
+ changed.length = 0;
919
+ // Second watch session without any changes
920
+ const watcher2 = await manager.watch();
921
+ await wait(100);
922
+ await watcher2.close();
923
+ expect(initialApplied).toHaveLength(2); // First run should apply both
924
+ expect(initialChanged).toHaveLength(2); // First run should detect both
925
+ expect(applied).toHaveLength(0); // Second run should apply none
926
+ expect(changed).toHaveLength(0); // Second run should detect none
927
+ // Verify the buildlog state
928
+ const localBuildlogPath = join(testContext.testDir, '.buildlog-test.local.json');
929
+ const buildLog = JSON.parse(await fs.readFile(localBuildlogPath, 'utf-8'));
930
+ for (const templatePath of templates) {
931
+ const relPath = relative(testContext.testDir, templatePath);
932
+ const content = await fs.readFile(templatePath, 'utf-8');
933
+ const hash = await calculateMD5(content);
934
+ expect(buildLog.templates[relPath].lastAppliedHash).toBe(hash);
935
+ }
936
+ }
937
+ catch (e_26) {
938
+ env_26.error = e_26;
939
+ env_26.hasError = true;
940
+ }
941
+ finally {
942
+ __disposeResources(env_26);
680
943
  }
681
944
  });
682
945
  it('should process unapplied templates on startup', async () => {
683
- // Create template but don't process it
684
- await createTemplateWithFunc(`startup-test`, '_startup_test');
685
- // Create a new manager instance
686
- const manager = await TemplateManager.create(testContext.testDir);
687
- const changes = [];
688
- const applied = [];
689
- manager.on('templateChanged', t => changes.push(t.name));
690
- manager.on('templateApplied', t => applied.push(t.name));
691
- // Start watching - this should process the template
692
- await manager.watch();
693
- await new Promise(resolve => setTimeout(resolve, 100));
694
- expect(changes).toHaveLength(1);
695
- expect(applied).toHaveLength(1);
696
- expect(changes[0]).toBe(`startup-test_${testContext.testId}_1`);
697
- expect(applied[0]).toBe(`startup-test_${testContext.testId}_1`);
946
+ const env_27 = { stack: [], error: void 0, hasError: false };
947
+ try {
948
+ // Create template but don't process it
949
+ await createTemplateWithFunc(`startup-test`, '_startup_test');
950
+ // Create a new manager instance
951
+ const manager = __addDisposableResource(env_27, await TemplateManager.create(testContext.testDir), false);
952
+ const changes = [];
953
+ const applied = [];
954
+ manager.on('templateChanged', t => changes.push(t.name));
955
+ manager.on('templateApplied', t => applied.push(t.name));
956
+ // Start watching - this should process the template
957
+ await manager.watch();
958
+ await wait(100);
959
+ expect(changes).toHaveLength(1);
960
+ expect(applied).toHaveLength(1);
961
+ expect(changes[0]).toBe(`startup-test_${testContext.testId}_1`);
962
+ expect(applied[0]).toBe(`startup-test_${testContext.testId}_1`);
963
+ }
964
+ catch (e_27) {
965
+ env_27.error = e_27;
966
+ env_27.hasError = true;
967
+ }
968
+ finally {
969
+ __disposeResources(env_27);
970
+ }
698
971
  });
699
972
  it('should handle error state transitions correctly', async () => {
700
- const templatePath = await createTemplateWithFunc(`error-state`, '_error_test');
701
- const manager = await TemplateManager.create(testContext.testDir);
702
- const states = [];
703
- manager.on('templateChanged', () => states.push({ type: 'changed' }));
704
- manager.on('templateApplied', () => states.push({ type: 'applied' }));
705
- manager.on('templateError', ({ error }) => states.push({ type: 'error', error: String(error) }));
706
- // First apply should succeed
707
- await manager.processTemplates({ apply: true });
708
- // Modify template to be invalid
709
- await fs.writeFile(templatePath, 'INVALID SQL;');
710
- await manager.processTemplates({ apply: true });
711
- // Fix template with valid SQL
712
- await fs.writeFile(templatePath, `CREATE OR REPLACE FUNCTION ${testContext.testFunctionName}() RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;`);
713
- await manager.processTemplates({ apply: true });
714
- expect(states).toEqual([
715
- { type: 'changed' },
716
- { type: 'applied' },
717
- { type: 'changed' },
718
- { type: 'error', error: expect.stringMatching(/syntax error/) },
719
- { type: 'changed' },
720
- { type: 'applied' },
721
- ]);
973
+ const env_28 = { stack: [], error: void 0, hasError: false };
974
+ try {
975
+ const templatePath = await createTemplateWithFunc(`error-state`, '_error_test');
976
+ const manager = __addDisposableResource(env_28, await TemplateManager.create(testContext.testDir), false);
977
+ const states = [];
978
+ manager.on('templateChanged', () => states.push({ type: 'changed' }));
979
+ manager.on('templateApplied', () => states.push({ type: 'applied' }));
980
+ manager.on('templateError', ({ error }) => states.push({ type: 'error', error: String(error) }));
981
+ // First apply should succeed
982
+ await manager.processTemplates({ apply: true });
983
+ // Modify template to be invalid
984
+ await fs.writeFile(templatePath, 'INVALID SQL;');
985
+ await manager.processTemplates({ apply: true });
986
+ // Fix template with valid SQL
987
+ await fs.writeFile(templatePath, `CREATE OR REPLACE FUNCTION ${testContext.testFunctionName}() RETURNS void AS $$ BEGIN NULL; END; $$ LANGUAGE plpgsql;`);
988
+ await manager.processTemplates({ apply: true });
989
+ expect(states).toEqual([
990
+ { type: 'changed' },
991
+ { type: 'applied' },
992
+ { type: 'changed' },
993
+ { type: 'error', error: expect.stringMatching(/syntax error/) },
994
+ { type: 'changed' },
995
+ { type: 'applied' },
996
+ ]);
997
+ }
998
+ catch (e_28) {
999
+ env_28.error = e_28;
1000
+ env_28.hasError = true;
1001
+ }
1002
+ finally {
1003
+ __disposeResources(env_28);
1004
+ }
722
1005
  });
723
1006
  it('should maintain correct state through manager restarts', async () => {
724
- const templatePath = await createTemplateWithFunc(`restart-test`, 'restart_test');
725
- // First manager instance
726
- const manager1 = await TemplateManager.create(testContext.testDir);
727
- await manager1.processTemplates({ apply: true });
728
- // Get initial state
729
- const status1 = await manager1.getTemplateStatus(templatePath);
730
- const initialHash = status1.buildState.lastAppliedHash;
731
- // Modify template
732
- await fs.writeFile(templatePath, `${await fs.readFile(templatePath, 'utf-8')}\n-- Modified`);
733
- // Create new manager instance
734
- const manager2 = await TemplateManager.create(testContext.testDir);
735
- const changes = [];
736
- const applied = [];
737
- manager2.on('templateChanged', t => changes.push(t.name));
738
- manager2.on('templateApplied', t => applied.push(t.name));
739
- await manager2.watch();
740
- await new Promise(resolve => setTimeout(resolve, 100));
741
- // Verify state was maintained and change was detected
742
- const status2 = await manager2.getTemplateStatus(templatePath);
743
- expect(status2.buildState.lastAppliedHash).not.toBe(initialHash);
744
- expect(changes).toContain(`restart-test_${testContext.testId}_1`);
745
- expect(applied).toContain(`restart-test_${testContext.testId}_1`);
1007
+ const env_29 = { stack: [], error: void 0, hasError: false };
1008
+ try {
1009
+ const templatePath = await createTemplateWithFunc(`restart-test`, 'restart_test');
1010
+ // First manager instance
1011
+ const manager1 = __addDisposableResource(env_29, await TemplateManager.create(testContext.testDir), false);
1012
+ await manager1.processTemplates({ apply: true });
1013
+ // Get initial state
1014
+ const status1 = await manager1.getTemplateStatus(templatePath);
1015
+ const initialHash = status1.buildState.lastAppliedHash;
1016
+ // Modify template
1017
+ await fs.writeFile(templatePath, `${await fs.readFile(templatePath, 'utf-8')}\n-- Modified`);
1018
+ // Create new manager instance
1019
+ const manager2 = __addDisposableResource(env_29, await TemplateManager.create(testContext.testDir), false);
1020
+ const changes = [];
1021
+ const applied = [];
1022
+ manager2.on('templateChanged', t => changes.push(t.name));
1023
+ manager2.on('templateApplied', t => applied.push(t.name));
1024
+ await manager2.watch();
1025
+ await new Promise(resolve => setTimeout(resolve, 100));
1026
+ // Verify state was maintained and change was detected
1027
+ const status2 = await manager2.getTemplateStatus(templatePath);
1028
+ expect(status2.buildState.lastAppliedHash).not.toBe(initialHash);
1029
+ expect(changes).toContain(`restart-test_${testContext.testId}_1`);
1030
+ expect(applied).toContain(`restart-test_${testContext.testId}_1`);
1031
+ }
1032
+ catch (e_29) {
1033
+ env_29.error = e_29;
1034
+ env_29.hasError = true;
1035
+ }
1036
+ finally {
1037
+ __disposeResources(env_29);
1038
+ }
746
1039
  });
747
1040
  it('should properly format and propagate error messages', async () => {
748
- const templatePath = await createTemplateWithFunc(`error-format`, 'error_format');
749
- const manager = await TemplateManager.create(testContext.testDir);
750
- const errors = [];
751
- manager.on('templateError', err => errors.push(err));
752
- // Create invalid SQL
753
- await fs.writeFile(templatePath, 'SELECT * FROM nonexistent_table;');
754
- await manager.processTemplates({ apply: true });
755
- expect(errors).toHaveLength(1);
756
- const error = errors[0]?.error;
757
- expect(typeof error).toBe('string');
758
- expect(error).not.toMatch(/\[object Object\]/);
759
- expect(error).toMatch(/relation.*does not exist/i);
1041
+ const env_30 = { stack: [], error: void 0, hasError: false };
1042
+ try {
1043
+ const templatePath = await createTemplateWithFunc(`error-format`, 'error_format');
1044
+ const manager = __addDisposableResource(env_30, await TemplateManager.create(testContext.testDir), false);
1045
+ const errors = [];
1046
+ manager.on('templateError', err => errors.push(err));
1047
+ // Create invalid SQL
1048
+ await fs.writeFile(templatePath, 'SELECT * FROM nonexistent_table;');
1049
+ await manager.processTemplates({ apply: true });
1050
+ expect(errors).toHaveLength(1);
1051
+ const error = errors[0]?.error;
1052
+ expect(typeof error).toBe('string');
1053
+ expect(error).not.toMatch(/\[object Object\]/);
1054
+ expect(error).toMatch(/relation.*does not exist/i);
1055
+ }
1056
+ catch (e_30) {
1057
+ env_30.error = e_30;
1058
+ env_30.hasError = true;
1059
+ }
1060
+ finally {
1061
+ __disposeResources(env_30);
1062
+ }
760
1063
  });
761
1064
  });
762
1065
  //# sourceMappingURL=templateManager.test.js.map