@t1mmen/srtd 0.2.1

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 (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +277 -0
  3. package/dist/__tests__/vitest.setup.d.ts +2 -0
  4. package/dist/__tests__/vitest.setup.js +72 -0
  5. package/dist/__tests__/vitest.setup.js.map +1 -0
  6. package/dist/__tests__/watch.test.d.ts +1 -0
  7. package/dist/__tests__/watch.test.js +24 -0
  8. package/dist/__tests__/watch.test.js.map +1 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.js +7 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/commands/_app.d.ts +3 -0
  13. package/dist/commands/_app.js +7 -0
  14. package/dist/commands/_app.js.map +1 -0
  15. package/dist/commands/apply.d.ts +1 -0
  16. package/dist/commands/apply.js +20 -0
  17. package/dist/commands/apply.js.map +1 -0
  18. package/dist/commands/build.d.ts +1 -0
  19. package/dist/commands/build.js +15 -0
  20. package/dist/commands/build.js.map +1 -0
  21. package/dist/commands/help.d.ts +1 -0
  22. package/dist/commands/help.js +2 -0
  23. package/dist/commands/help.js.map +1 -0
  24. package/dist/commands/index.d.ts +2 -0
  25. package/dist/commands/index.js +37 -0
  26. package/dist/commands/index.js.map +1 -0
  27. package/dist/commands/init.d.ts +1 -0
  28. package/dist/commands/init.js +80 -0
  29. package/dist/commands/init.js.map +1 -0
  30. package/dist/commands/register.d.ts +8 -0
  31. package/dist/commands/register.js +82 -0
  32. package/dist/commands/register.js.map +1 -0
  33. package/dist/commands/watch.d.ts +2 -0
  34. package/dist/commands/watch.js +127 -0
  35. package/dist/commands/watch.js.map +1 -0
  36. package/dist/components/Branding.d.ts +6 -0
  37. package/dist/components/Branding.js +28 -0
  38. package/dist/components/Branding.js.map +1 -0
  39. package/dist/components/TimeSince.d.ts +4 -0
  40. package/dist/components/TimeSince.js +29 -0
  41. package/dist/components/TimeSince.js.map +1 -0
  42. package/dist/constants.d.ts +1 -0
  43. package/dist/constants.js +2 -0
  44. package/dist/constants.js.map +1 -0
  45. package/dist/hooks/useTemplateState.d.ts +6 -0
  46. package/dist/hooks/useTemplateState.js +28 -0
  47. package/dist/hooks/useTemplateState.js.map +1 -0
  48. package/dist/lib/templateManager.d.ts +32 -0
  49. package/dist/lib/templateManager.js +237 -0
  50. package/dist/lib/templateManager.js.map +1 -0
  51. package/dist/lib/templateManager.test.d.ts +1 -0
  52. package/dist/lib/templateManager.test.js +289 -0
  53. package/dist/lib/templateManager.test.js.map +1 -0
  54. package/dist/types.d.ts +44 -0
  55. package/dist/types.js +2 -0
  56. package/dist/types.js.map +1 -0
  57. package/dist/utils/applyMigration.d.ts +2 -0
  58. package/dist/utils/applyMigration.js +29 -0
  59. package/dist/utils/applyMigration.js.map +1 -0
  60. package/dist/utils/applyMigrations.test.d.ts +1 -0
  61. package/dist/utils/applyMigrations.test.js +112 -0
  62. package/dist/utils/applyMigrations.test.js.map +1 -0
  63. package/dist/utils/calculateMD5.d.ts +1 -0
  64. package/dist/utils/calculateMD5.js +5 -0
  65. package/dist/utils/calculateMD5.js.map +1 -0
  66. package/dist/utils/config.d.ts +3 -0
  67. package/dist/utils/config.js +78 -0
  68. package/dist/utils/config.js.map +1 -0
  69. package/dist/utils/config.test.d.ts +1 -0
  70. package/dist/utils/config.test.js +61 -0
  71. package/dist/utils/config.test.js.map +1 -0
  72. package/dist/utils/createEmptyBuildLog.d.ts +1 -0
  73. package/dist/utils/createEmptyBuildLog.js +10 -0
  74. package/dist/utils/createEmptyBuildLog.js.map +1 -0
  75. package/dist/utils/databaseConnection.d.ts +3 -0
  76. package/dist/utils/databaseConnection.js +45 -0
  77. package/dist/utils/databaseConnection.js.map +1 -0
  78. package/dist/utils/databaseConnection.test.d.ts +1 -0
  79. package/dist/utils/databaseConnection.test.js +45 -0
  80. package/dist/utils/databaseConnection.test.js.map +1 -0
  81. package/dist/utils/ensureDirectories.d.ts +4 -0
  82. package/dist/utils/ensureDirectories.js +23 -0
  83. package/dist/utils/ensureDirectories.js.map +1 -0
  84. package/dist/utils/fileExists.d.ts +1 -0
  85. package/dist/utils/fileExists.js +11 -0
  86. package/dist/utils/fileExists.js.map +1 -0
  87. package/dist/utils/getNextTimestamp.d.ts +2 -0
  88. package/dist/utils/getNextTimestamp.js +12 -0
  89. package/dist/utils/getNextTimestamp.js.map +1 -0
  90. package/dist/utils/isWipTemplate.d.ts +1 -0
  91. package/dist/utils/isWipTemplate.js +6 -0
  92. package/dist/utils/isWipTemplate.js.map +1 -0
  93. package/dist/utils/loadBuildLog.d.ts +2 -0
  94. package/dist/utils/loadBuildLog.js +21 -0
  95. package/dist/utils/loadBuildLog.js.map +1 -0
  96. package/dist/utils/loadBuildLog.test.d.ts +1 -0
  97. package/dist/utils/loadBuildLog.test.js +62 -0
  98. package/dist/utils/loadBuildLog.test.js.map +1 -0
  99. package/dist/utils/logger.d.ts +8 -0
  100. package/dist/utils/logger.js +11 -0
  101. package/dist/utils/logger.js.map +1 -0
  102. package/dist/utils/registerTemplate.d.ts +1 -0
  103. package/dist/utils/registerTemplate.js +44 -0
  104. package/dist/utils/registerTemplate.js.map +1 -0
  105. package/dist/utils/safeCreate.d.ts +1 -0
  106. package/dist/utils/safeCreate.js +10 -0
  107. package/dist/utils/safeCreate.js.map +1 -0
  108. package/dist/utils/saveBuildLog.d.ts +2 -0
  109. package/dist/utils/saveBuildLog.js +9 -0
  110. package/dist/utils/saveBuildLog.js.map +1 -0
  111. package/package.json +91 -0
@@ -0,0 +1,78 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { CONFIG_FILE } from '../constants.js';
4
+ const defaultConfig = {
5
+ wipIndicator: '.wip',
6
+ filter: '**/*.sql',
7
+ banner: 'You very likely **DO NOT** want to manually edit this generated file.',
8
+ footer: '',
9
+ wrapInTransaction: true,
10
+ templateDir: 'supabase/migrations-templates',
11
+ migrationDir: 'supabase/migrations',
12
+ buildLog: 'supabase/migrations-templates/.srtd.buildlog.json',
13
+ localBuildLog: 'supabase/migrations-templates/.srtd.buildlog.local.json',
14
+ pgConnection: 'postgresql://postgres:postgres@localhost:54322/postgres',
15
+ };
16
+ let cachedConfig;
17
+ export async function getConfig(dir = process.cwd()) {
18
+ if (cachedConfig)
19
+ return cachedConfig;
20
+ try {
21
+ const configPath = path.join(dir, CONFIG_FILE);
22
+ const content = await fs.readFile(configPath, 'utf-8');
23
+ cachedConfig = { ...defaultConfig, ...JSON.parse(content) };
24
+ }
25
+ catch {
26
+ cachedConfig = defaultConfig;
27
+ }
28
+ if (!cachedConfig) {
29
+ throw new Error('Config not initialized');
30
+ }
31
+ return cachedConfig;
32
+ }
33
+ export async function saveConfig(baseDir, config) {
34
+ const configPath = path.join(baseDir, CONFIG_FILE);
35
+ const finalConfig = { ...defaultConfig, ...config };
36
+ await fs.writeFile(configPath, JSON.stringify(finalConfig, null, 2));
37
+ cachedConfig = finalConfig;
38
+ }
39
+ // import { CLIConfig } from '../types.js';
40
+ // import path from 'path';
41
+ // import fs from 'fs/promises';
42
+ // import { CONFIG_FILE } from '../constants.js';
43
+ // let config: CLIConfig;
44
+ // export async function getConfig(baseDir: string): Promise<CLIConfig> {
45
+ // if (!config) {
46
+ // config = await getConfig(baseDir);
47
+ // }
48
+ // return config;
49
+ // }
50
+ // const defaultConfig: CLIConfig = {
51
+ // wipIndicator: '.wip',
52
+ // filter: '**/*.sql',
53
+ // banner: 'You very likely **DO NOT** want to manually edit this generated file.',
54
+ // footer: '',
55
+ // wrapInTransaction: true,
56
+ // templateDir: 'supabase/migrations-templates',
57
+ // migrationDir: 'supabase/migrations',
58
+ // buildLog: 'supabase/migrations-templates/.buildlog.json',
59
+ // localBuildLog: 'supabase/migrations-templates/.buildlog.local.json',
60
+ // pgConnection: 'postgresql://postgres:postgres@localhost:54322/postgres',
61
+ // };
62
+ // export async function getConfig(dir?: string): Promise<CLIConfig> {
63
+ // const baseDir = dir || process.cwd();
64
+ // const configPath = path.join(baseDir, CONFIG_FILE);
65
+ // try {
66
+ // const content = await fs.readFile(configPath, 'utf-8');
67
+ // const userConfig = JSON.parse(content);
68
+ // return { ...defaultConfig, ...userConfig };
69
+ // } catch {
70
+ // return defaultConfig;
71
+ // }
72
+ // }
73
+ // export async function saveConfig(baseDir: string, config: Partial<CLIConfig>): Promise<void> {
74
+ // const configPath = path.join(baseDir, CONFIG_FILE);
75
+ // const finalConfig = { ...defaultConfig, ...config };
76
+ // await fs.writeFile(configPath, JSON.stringify(finalConfig, null, 2));
77
+ // }
78
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C,MAAM,aAAa,GAAc;IAC/B,YAAY,EAAE,MAAM;IACpB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,uEAAuE;IAC/E,MAAM,EAAE,EAAE;IACV,iBAAiB,EAAE,IAAI;IACvB,WAAW,EAAE,+BAA+B;IAC5C,YAAY,EAAE,qBAAqB;IACnC,QAAQ,EAAE,mDAAmD;IAC7D,aAAa,EAAE,yDAAyD;IACxE,YAAY,EAAE,yDAAyD;CACxE,CAAC;AAEF,IAAI,YAAmC,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IACzD,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IAEtC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACvD,YAAY,GAAG,EAAE,GAAG,aAAa,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,YAAY,GAAG,aAAa,CAAC;IAC/B,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAe,EAAE,MAA0B;IAC1E,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACnD,MAAM,WAAW,GAAG,EAAE,GAAG,aAAa,EAAE,GAAG,MAAM,EAAE,CAAC;IACpD,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACrE,YAAY,GAAG,WAAW,CAAC;AAC7B,CAAC;AAED,2CAA2C;AAC3C,2BAA2B;AAC3B,gCAAgC;AAChC,iDAAiD;AAEjD,yBAAyB;AAEzB,yEAAyE;AACzE,mBAAmB;AACnB,yCAAyC;AACzC,MAAM;AACN,mBAAmB;AACnB,IAAI;AAEJ,qCAAqC;AACrC,0BAA0B;AAC1B,wBAAwB;AACxB,qFAAqF;AACrF,gBAAgB;AAChB,6BAA6B;AAC7B,kDAAkD;AAClD,yCAAyC;AACzC,8DAA8D;AAC9D,yEAAyE;AACzE,6EAA6E;AAC7E,KAAK;AAEL,sEAAsE;AACtE,0CAA0C;AAC1C,wDAAwD;AACxD,UAAU;AACV,8DAA8D;AAC9D,8CAA8C;AAC9C,kDAAkD;AAClD,cAAc;AACd,4BAA4B;AAC5B,MAAM;AACN,IAAI;AAEJ,iGAAiG;AACjG,wDAAwD;AACxD,yDAAyD;AACzD,0EAA0E;AAC1E,IAAI"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,61 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import { TEST_ROOT } from '../__tests__/vitest.setup.js';
5
+ import { getConfig, saveConfig } from './config.js';
6
+ describe('config', () => {
7
+ beforeEach(() => {
8
+ vi.clearAllMocks();
9
+ });
10
+ it('should call getConfig with the correct path', async () => {
11
+ const mockGetConfig = vi.mocked(getConfig);
12
+ await getConfig(TEST_ROOT);
13
+ expect(mockGetConfig).toHaveBeenCalledWith(TEST_ROOT);
14
+ });
15
+ it('should return mocked test config values', async () => {
16
+ const config = await getConfig(TEST_ROOT);
17
+ expect(config).toEqual({
18
+ wipIndicator: '.wip',
19
+ filter: '**/*.sql',
20
+ templateDir: 'test-templates',
21
+ migrationDir: 'test-migrations',
22
+ buildLog: '.buildlog-test.json',
23
+ localBuildLog: '.buildlog-test.local.json',
24
+ pgConnection: process.env.POSTGRES_URL || 'postgresql://postgres:postgres@localhost:54322/postgres',
25
+ banner: 'Test banner',
26
+ footer: 'Test footer',
27
+ wrapInTransaction: true,
28
+ });
29
+ });
30
+ it('should save and merge with default config', async () => {
31
+ const userConfig = {
32
+ templateDir: 'custom/templates',
33
+ migrationDir: 'custom/migrations',
34
+ };
35
+ await saveConfig(TEST_ROOT, userConfig);
36
+ const savedContent = JSON.parse(await fs.readFile(path.join(TEST_ROOT, 'srtd.config.json'), 'utf-8'));
37
+ expect(savedContent.templateDir).toBe('custom/templates');
38
+ expect(savedContent.migrationDir).toBe('custom/migrations');
39
+ expect(savedContent.wrapInTransaction).toBe(true);
40
+ });
41
+ it('should handle empty config object', async () => {
42
+ await saveConfig(TEST_ROOT, {});
43
+ const config = await getConfig(TEST_ROOT);
44
+ expect(config.templateDir).toBe('test-templates');
45
+ });
46
+ it('should preserve unknown fields', async () => {
47
+ const customConfig = {
48
+ templateDir: 'custom',
49
+ unknownField: 'value',
50
+ };
51
+ await saveConfig(TEST_ROOT, customConfig);
52
+ const savedContent = JSON.parse(await fs.readFile(path.join(TEST_ROOT, 'srtd.config.json'), 'utf-8'));
53
+ expect(savedContent.unknownField).toBe('value');
54
+ });
55
+ it('should handle nested paths correctly', async () => {
56
+ const nestedPath = path.join(TEST_ROOT, 'nested', 'config');
57
+ const config = await getConfig(nestedPath);
58
+ expect(config.templateDir).toBe('test-templates');
59
+ });
60
+ });
61
+ //# sourceMappingURL=config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/utils/config.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function createEmptyBuildLog(filepath: string): Promise<boolean>;
@@ -0,0 +1,10 @@
1
+ import { safeCreate } from './safeCreate.js';
2
+ export async function createEmptyBuildLog(filepath) {
3
+ const initial = {
4
+ version: '1.0',
5
+ lastTimestamp: '',
6
+ templates: {},
7
+ };
8
+ return safeCreate(filepath, JSON.stringify(initial, null, 2));
9
+ }
10
+ //# sourceMappingURL=createEmptyBuildLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createEmptyBuildLog.js","sourceRoot":"","sources":["../../src/utils/createEmptyBuildLog.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAAgB;IACxD,MAAM,OAAO,GAAG;QACd,OAAO,EAAE,KAAK;QACd,aAAa,EAAE,EAAE;QACjB,SAAS,EAAE,EAAE;KACK,CAAC;IACrB,OAAO,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import pg from 'pg';
2
+ export declare function connect(): Promise<pg.PoolClient>;
3
+ export declare function disconnect(): Promise<void>;
@@ -0,0 +1,45 @@
1
+ // utils/db.connection.ts
2
+ import pg from 'pg';
3
+ import { getConfig } from './config.js';
4
+ import { logger } from './logger.js';
5
+ let pool;
6
+ let connectionAttempts = 0;
7
+ const MAX_RETRIES = 3;
8
+ const RETRY_DELAY = 1000;
9
+ async function createPool() {
10
+ const config = await getConfig(process.cwd());
11
+ return new pg.Pool({
12
+ connectionString: config.pgConnection,
13
+ connectionTimeoutMillis: 5000,
14
+ });
15
+ }
16
+ async function retryConnection() {
17
+ connectionAttempts++;
18
+ logger.debug(`Connection attempt ${connectionAttempts}`);
19
+ try {
20
+ if (!pool)
21
+ pool = await createPool();
22
+ return await pool.connect();
23
+ }
24
+ catch (err) {
25
+ if (connectionAttempts < MAX_RETRIES) {
26
+ logger.warn(`Connection failed, retrying in ${RETRY_DELAY}ms...`);
27
+ await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
28
+ return retryConnection();
29
+ }
30
+ throw new Error(`Database connection failed after ${MAX_RETRIES} attempts: ${err}`);
31
+ }
32
+ }
33
+ export async function connect() {
34
+ connectionAttempts = 0;
35
+ return retryConnection();
36
+ }
37
+ export async function disconnect() {
38
+ if (pool) {
39
+ await pool.end();
40
+ pool = undefined;
41
+ }
42
+ return;
43
+ }
44
+ process.on('exit', async () => await disconnect());
45
+ //# sourceMappingURL=databaseConnection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"databaseConnection.js","sourceRoot":"","sources":["../../src/utils/databaseConnection.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,IAAI,IAAyB,CAAC;AAC9B,IAAI,kBAAkB,GAAG,CAAC,CAAC;AAC3B,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,WAAW,GAAG,IAAI,CAAC;AAEzB,KAAK,UAAU,UAAU;IACvB,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9C,OAAO,IAAI,EAAE,CAAC,IAAI,CAAC;QACjB,gBAAgB,EAAE,MAAM,CAAC,YAAY;QACrC,uBAAuB,EAAE,IAAI;KAC9B,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,eAAe;IAC5B,kBAAkB,EAAE,CAAC;IACrB,MAAM,CAAC,KAAK,CAAC,sBAAsB,kBAAkB,EAAE,CAAC,CAAC;IAEzD,IAAI,CAAC;QACH,IAAI,CAAC,IAAI;YAAE,IAAI,GAAG,MAAM,UAAU,EAAE,CAAC;QACrC,OAAO,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,kBAAkB,GAAG,WAAW,EAAE,CAAC;YACrC,MAAM,CAAC,IAAI,CAAC,kCAAkC,WAAW,OAAO,CAAC,CAAC;YAClE,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;YAC/D,OAAO,eAAe,EAAE,CAAC;QAC3B,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,oCAAoC,WAAW,cAAc,GAAG,EAAE,CAAC,CAAC;IACtF,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO;IAC3B,kBAAkB,GAAG,CAAC,CAAC;IACvB,OAAO,eAAe,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACjB,IAAI,GAAG,SAAS,CAAC;IACnB,CAAC;IACD,OAAO;AACT,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE,CAAC,MAAM,UAAU,EAAE,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import { connect, disconnect } from './databaseConnection.js';
3
+ describe('db.connection', () => {
4
+ afterEach(async () => {
5
+ await disconnect();
6
+ });
7
+ it('should connect and execute queries', async () => {
8
+ const client = await connect();
9
+ const result = await client.query('SELECT 1 as num');
10
+ expect(result.rows[0].num).toBe(1);
11
+ client.release();
12
+ });
13
+ it('should reuse the same pool for multiple connections', async () => {
14
+ const client1 = await connect();
15
+ const client2 = await connect();
16
+ await client1.query('SELECT 1');
17
+ await client2.query('SELECT 1');
18
+ client1.release();
19
+ client2.release();
20
+ });
21
+ it('should handle connection release properly', async () => {
22
+ const client1 = await connect();
23
+ client1.release();
24
+ const client2 = await connect();
25
+ await client2.query('SELECT 1');
26
+ client2.release();
27
+ });
28
+ it('should handle disconnection properly', async () => {
29
+ const client = await connect();
30
+ await client.query('SELECT 1');
31
+ client.release();
32
+ await disconnect();
33
+ // Should be able to connect again
34
+ const newClient = await connect();
35
+ await newClient.query('SELECT 1');
36
+ newClient.release();
37
+ });
38
+ it('should handle multiple disconnects gracefully', async () => {
39
+ const client = await connect();
40
+ client.release();
41
+ await disconnect();
42
+ await disconnect(); // Should not error
43
+ });
44
+ });
45
+ //# sourceMappingURL=databaseConnection.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"databaseConnection.test.js","sourceRoot":"","sources":["../../src/utils/databaseConnection.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE9D,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,UAAU,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QAClD,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACrD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAEhC,MAAM,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAChC,MAAM,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAEhC,OAAO,CAAC,OAAO,EAAE,CAAC;QAClB,OAAO,CAAC,OAAO,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,OAAO,CAAC,OAAO,EAAE,CAAC;QAElB,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAC;QAChC,MAAM,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAChC,OAAO,CAAC,OAAO,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,CAAC;QAC/B,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC/B,MAAM,CAAC,OAAO,EAAE,CAAC;QAEjB,MAAM,UAAU,EAAE,CAAC;QAEnB,kCAAkC;QAClC,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,CAAC;QAClC,MAAM,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAClC,SAAS,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,CAAC;QAC/B,MAAM,CAAC,OAAO,EAAE,CAAC;QAEjB,MAAM,UAAU,EAAE,CAAC;QACnB,MAAM,UAAU,EAAE,CAAC,CAAC,mBAAmB;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function ensureDirectories(baseDir: string): Promise<{
2
+ templateDir: boolean;
3
+ migrationDir: boolean;
4
+ }>;
@@ -0,0 +1,23 @@
1
+ // utils/ensureDirectories.ts
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { getConfig } from './config.js';
5
+ import { fileExists } from './fileExists.js';
6
+ export async function ensureDirectories(baseDir) {
7
+ const config = await getConfig(baseDir);
8
+ const templatePath = path.join(baseDir, config.templateDir);
9
+ const migrationPath = path.join(baseDir, config.migrationDir);
10
+ const templateExists = await fileExists(templatePath);
11
+ const migrationExists = await fileExists(migrationPath);
12
+ if (!templateExists) {
13
+ await fs.mkdir(templatePath, { recursive: true });
14
+ }
15
+ if (!migrationExists) {
16
+ await fs.mkdir(migrationPath, { recursive: true });
17
+ }
18
+ return {
19
+ templateDir: !templateExists,
20
+ migrationDir: !migrationExists,
21
+ };
22
+ }
23
+ //# sourceMappingURL=ensureDirectories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensureDirectories.js","sourceRoot":"","sources":["../../src/utils/ensureDirectories.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAe;IAEf,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5D,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAE9D,MAAM,cAAc,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC,CAAC;IACtD,MAAM,eAAe,GAAG,MAAM,UAAU,CAAC,aAAa,CAAC,CAAC;IAExD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,EAAE,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,OAAO;QACL,WAAW,EAAE,CAAC,cAAc;QAC5B,YAAY,EAAE,CAAC,eAAe;KAC/B,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function fileExists(filepath: string): Promise<boolean>;
@@ -0,0 +1,11 @@
1
+ import fs from 'node:fs/promises';
2
+ export async function fileExists(filepath) {
3
+ try {
4
+ await fs.access(filepath);
5
+ return true;
6
+ }
7
+ catch {
8
+ return false;
9
+ }
10
+ }
11
+ //# sourceMappingURL=fileExists.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileExists.js","sourceRoot":"","sources":["../../src/utils/fileExists.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC/C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { BuildLog } from '../types.js';
2
+ export declare function getNextTimestamp(buildLog: BuildLog): Promise<string>;
@@ -0,0 +1,12 @@
1
+ export async function getNextTimestamp(buildLog) {
2
+ const now = new Date();
3
+ const timestamp = now.toISOString().replace(/\D/g, '').slice(0, 14);
4
+ if (timestamp <= buildLog.lastTimestamp) {
5
+ const nextTimestamp = (BigInt(buildLog.lastTimestamp) + 1n).toString();
6
+ buildLog.lastTimestamp = nextTimestamp;
7
+ return nextTimestamp;
8
+ }
9
+ buildLog.lastTimestamp = timestamp;
10
+ return timestamp;
11
+ }
12
+ //# sourceMappingURL=getNextTimestamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getNextTimestamp.js","sourceRoot":"","sources":["../../src/utils/getNextTimestamp.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,QAAkB;IACvD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEpE,IAAI,SAAS,IAAI,QAAQ,CAAC,aAAa,EAAE,CAAC;QACxC,MAAM,aAAa,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;QACvE,QAAQ,CAAC,aAAa,GAAG,aAAa,CAAC;QACvC,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,QAAQ,CAAC,aAAa,GAAG,SAAS,CAAC;IACnC,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function isWipTemplate(templatePath: string): Promise<boolean>;
@@ -0,0 +1,6 @@
1
+ import { getConfig } from './config.js';
2
+ export async function isWipTemplate(templatePath) {
3
+ const config = await getConfig();
4
+ return templatePath.includes(config.wipIndicator);
5
+ }
6
+ //# sourceMappingURL=isWipTemplate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isWipTemplate.js","sourceRoot":"","sources":["../../src/utils/isWipTemplate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,YAAoB;IACtD,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IACjC,OAAO,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { BuildLog } from '../types.js';
2
+ export declare function loadBuildLog(dirname: string, which: 'local' | 'common'): Promise<BuildLog>;
@@ -0,0 +1,21 @@
1
+ //utils/loadBuildLog.ts
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { getConfig } from './config.js';
5
+ export async function loadBuildLog(dirname, which) {
6
+ try {
7
+ const config = await getConfig(dirname);
8
+ const logPath = which === 'local' ? config.localBuildLog : config.buildLog;
9
+ const content = await fs.readFile(path.resolve(dirname, logPath), 'utf-8');
10
+ const log = JSON.parse(content);
11
+ return {
12
+ version: log.version || '1.0',
13
+ lastTimestamp: log.lastTimestamp || '',
14
+ templates: log.templates || {},
15
+ };
16
+ }
17
+ catch {
18
+ return { version: '1.0', templates: {}, lastTimestamp: '' };
19
+ }
20
+ }
21
+ //# sourceMappingURL=loadBuildLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadBuildLog.js","sourceRoot":"","sources":["../../src/utils/loadBuildLog.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe,EAAE,KAAyB;IAC3E,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,OAAO,GAAG,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC3E,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;QAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAChC,OAAO;YACL,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,KAAK;YAC7B,aAAa,EAAE,GAAG,CAAC,aAAa,IAAI,EAAE;YACtC,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,EAAE;SAC/B,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC;IAC9D,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,62 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { TEST_ROOT } from '../__tests__/vitest.setup.js';
5
+ import { loadBuildLog } from './loadBuildLog.js';
6
+ describe('loadBuildLog', () => {
7
+ it('should load existing build log', async () => {
8
+ const content = {
9
+ version: '1.0',
10
+ lastTimestamp: '20240101120000',
11
+ templates: {
12
+ 'test.sql': {
13
+ lastBuildHash: 'abc123',
14
+ lastBuildDate: '2024-01-01T12:00:00Z',
15
+ },
16
+ },
17
+ };
18
+ await fs.writeFile(path.join(TEST_ROOT, '.buildlog-test.json'), JSON.stringify(content));
19
+ const log = await loadBuildLog(TEST_ROOT, 'common');
20
+ expect(log).toEqual(content);
21
+ });
22
+ it('should return empty build log when file does not exist', async () => {
23
+ const log = await loadBuildLog(path.join(TEST_ROOT, 'nonexistent'), 'common');
24
+ expect(log).toEqual({
25
+ version: '1.0',
26
+ templates: {},
27
+ lastTimestamp: '',
28
+ });
29
+ });
30
+ it('should handle invalid JSON', async () => {
31
+ await fs.writeFile(path.join(TEST_ROOT, '.buildlog-test.local.json'), 'invalid json');
32
+ const log = await loadBuildLog(TEST_ROOT, 'local');
33
+ expect(log).toEqual({
34
+ version: '1.0',
35
+ templates: {},
36
+ lastTimestamp: '',
37
+ });
38
+ });
39
+ it('should handle missing fields', async () => {
40
+ const incompleteContent = {
41
+ version: '1.0',
42
+ };
43
+ await fs.writeFile(path.join(TEST_ROOT, '.buildlog-test.json'), JSON.stringify(incompleteContent));
44
+ const log = await loadBuildLog(TEST_ROOT, 'common');
45
+ expect(log).toEqual({
46
+ version: '1.0',
47
+ templates: {},
48
+ lastTimestamp: '',
49
+ });
50
+ });
51
+ it('should load correct file based on type parameter', async () => {
52
+ const commonContent = { version: '1.0', templates: { common: true } };
53
+ const localContent = { version: '1.0', templates: { local: true } };
54
+ await fs.writeFile(path.join(TEST_ROOT, '.buildlog-test.json'), JSON.stringify(commonContent));
55
+ await fs.writeFile(path.join(TEST_ROOT, '.buildlog-test.local.json'), JSON.stringify(localContent));
56
+ const commonLog = await loadBuildLog(TEST_ROOT, 'common');
57
+ const localLog = await loadBuildLog(TEST_ROOT, 'local');
58
+ expect(commonLog.templates).toEqual({ common: true });
59
+ expect(localLog.templates).toEqual({ local: true });
60
+ });
61
+ });
62
+ //# sourceMappingURL=loadBuildLog.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadBuildLog.test.js","sourceRoot":"","sources":["../../src/utils/loadBuildLog.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export declare const logger: {
2
+ info: (msg: string) => void;
3
+ success: (msg: string) => void;
4
+ warn: (msg: string) => void;
5
+ error: (msg: string) => void;
6
+ skip: (msg: string) => void;
7
+ debug: (msg: string) => false | void | "" | undefined;
8
+ };
@@ -0,0 +1,11 @@
1
+ // utils/logger.ts
2
+ import chalk from 'chalk';
3
+ export const logger = {
4
+ info: (msg) => console.log(` ${msg}`),
5
+ success: (msg) => console.log(` ✅ ${chalk.green(msg)}`),
6
+ warn: (msg) => console.log(` ⚠️ ${chalk.yellow(msg)}`),
7
+ error: (msg) => console.log(` ❌ ${chalk.red(msg)}`),
8
+ skip: (msg) => console.log(` ↪️ ${chalk.dim(msg)}`),
9
+ debug: (msg) => process.env.DEBUG && process.env.DEBUG === 'true' && console.log(` 🔍 ${chalk.white(msg)}`),
10
+ };
11
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,kBAAkB;AAClB,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC;IAC9C,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAChE,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAChE,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;IAC5D,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;IAC5D,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CACrB,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;CAC/F,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function registerTemplate(templatePath: string, baseDir: string): Promise<void>;
@@ -0,0 +1,44 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import chalk from 'chalk';
4
+ import { calculateMD5 } from './calculateMD5.js';
5
+ import { getConfig } from './config.js';
6
+ import { fileExists } from './fileExists.js';
7
+ import { loadBuildLog } from './loadBuildLog.js';
8
+ import { saveBuildLog } from './saveBuildLog.js';
9
+ export async function registerTemplate(templatePath, baseDir) {
10
+ const config = await getConfig(baseDir);
11
+ // Try multiple path resolutions
12
+ const pathsToTry = [
13
+ templatePath, // As provided
14
+ path.join(baseDir, config.templateDir, templatePath), // In templates dir
15
+ ];
16
+ let resolvedPath = null;
17
+ for (const p of pathsToTry) {
18
+ if (await fileExists(p)) {
19
+ resolvedPath = p;
20
+ break;
21
+ }
22
+ }
23
+ if (!resolvedPath) {
24
+ console.log(chalk.red('Error:'), `Template file not found. Tried:`);
25
+ for (const p of pathsToTry) {
26
+ console.log(chalk.dim(` - ${p}`));
27
+ }
28
+ throw new Error(`Template ${templatePath} not found`);
29
+ }
30
+ const content = await fs.readFile(resolvedPath, 'utf-8');
31
+ const hash = await calculateMD5(content);
32
+ const relativePath = path.relative(baseDir, resolvedPath);
33
+ const now = new Date().toISOString();
34
+ // Update build log
35
+ const buildLog = await loadBuildLog(baseDir, 'common');
36
+ buildLog.templates[relativePath] = {
37
+ lastBuildHash: hash,
38
+ lastBuildDate: now,
39
+ lastMigrationFile: ``, // Unknown, may want to allow entering this?
40
+ };
41
+ await saveBuildLog(baseDir, buildLog, 'common');
42
+ console.log(chalk.green(`✓ Registered template:`), relativePath);
43
+ }
44
+ //# sourceMappingURL=registerTemplate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registerTemplate.js","sourceRoot":"","sources":["../../src/utils/registerTemplate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,YAAoB,EAAE,OAAe;IAC1E,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;IAExC,gCAAgC;IAChC,MAAM,UAAU,GAAG;QACjB,YAAY,EAAE,cAAc;QAC5B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,EAAE,mBAAmB;KAC1E,CAAC;IAEF,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;YACxB,YAAY,GAAG,CAAC,CAAC;YACjB,MAAM;QACR,CAAC;IACH,CAAC;IAED,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,iCAAiC,CAAC,CAAC;QACpE,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,YAAY,YAAY,YAAY,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAErC,mBAAmB;IACnB,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACvD,QAAQ,CAAC,SAAS,CAAC,YAAY,CAAC,GAAG;QACjC,aAAa,EAAE,IAAI;QACnB,aAAa,EAAE,GAAG;QAClB,iBAAiB,EAAE,EAAE,EAAE,4CAA4C;KACpE,CAAC;IAEF,MAAM,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAChD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,EAAE,YAAY,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function safeCreate(filepath: string, content: string): Promise<boolean>;
@@ -0,0 +1,10 @@
1
+ import fs from 'node:fs/promises';
2
+ import { fileExists } from './fileExists.js';
3
+ export async function safeCreate(filepath, content) {
4
+ if (await fileExists(filepath)) {
5
+ return false;
6
+ }
7
+ await fs.writeFile(filepath, content);
8
+ return true;
9
+ }
10
+ //# sourceMappingURL=safeCreate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safeCreate.js","sourceRoot":"","sources":["../../src/utils/safeCreate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAAgB,EAAE,OAAe;IAChE,IAAI,MAAM,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { BuildLog } from '../types.js';
2
+ export declare function saveBuildLog(dirname: string, log: BuildLog, which: 'local' | 'common'): Promise<void>;
@@ -0,0 +1,9 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { getConfig } from './config.js';
4
+ export async function saveBuildLog(dirname, log, which) {
5
+ const config = await getConfig();
6
+ const useLog = which === 'local' ? config.localBuildLog : config.buildLog;
7
+ await fs.writeFile(path.resolve(dirname, useLog), JSON.stringify(log, null, 2));
8
+ }
9
+ //# sourceMappingURL=saveBuildLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saveBuildLog.js","sourceRoot":"","sources":["../../src/utils/saveBuildLog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,GAAa,EACb,KAAyB;IAEzB,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC1E,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAClF,CAAC"}