@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,82 @@
1
+ import { MultiSelect } from '@inkjs/ui';
2
+ import { Box, Text } from 'ink';
3
+ import { argument } from 'pastel';
4
+ import React from 'react';
5
+ import zod from 'zod';
6
+ import Branding from '../components/Branding.js';
7
+ import { useTemplateState } from '../hooks/useTemplateState.js';
8
+ import { registerTemplate } from '../utils/registerTemplate.js';
9
+ // Support both array of filenames as arguments and interactive selection
10
+ export const args = zod
11
+ .array(zod.string())
12
+ .optional()
13
+ .describe(argument({
14
+ name: 'templates',
15
+ description: 'Template files to register (optional)',
16
+ }));
17
+ export default function Register({ args: templateArgs }) {
18
+ const { error, items } = useTemplateState();
19
+ const [selectedValues, setSelectedValues] = React.useState([]);
20
+ const [successMessage, setSuccessMessage] = React.useState('');
21
+ const [errorMessage, setErrorMessage] = React.useState('');
22
+ const handleTemplateRegistration = React.useCallback(async (templates) => {
23
+ setSuccessMessage('');
24
+ setErrorMessage('');
25
+ let successCount = 0;
26
+ let failCount = 0;
27
+ for (const path of templates) {
28
+ try {
29
+ await registerTemplate(path, process.cwd());
30
+ successCount++;
31
+ }
32
+ catch {
33
+ failCount++;
34
+ }
35
+ }
36
+ if (failCount > 0) {
37
+ setErrorMessage(`Failed to register ${failCount} template(s).`);
38
+ }
39
+ if (successCount > 0) {
40
+ setSuccessMessage(`Successfully registered ${successCount} template(s).`);
41
+ }
42
+ process.exit(failCount > 0 ? 1 : 0);
43
+ }, []);
44
+ React.useEffect(() => {
45
+ // If templates were provided as arguments, register them directly
46
+ if (templateArgs?.length) {
47
+ void handleTemplateRegistration(templateArgs);
48
+ }
49
+ }, [handleTemplateRegistration, templateArgs]);
50
+ if (error) {
51
+ return React.createElement(Text, { color: "red" },
52
+ "Error: ",
53
+ error);
54
+ }
55
+ // If no templates were provided as arguments, show interactive selection
56
+ if (!templateArgs?.length) {
57
+ const options = items.map(t => {
58
+ const status = t.buildState.lastMigrationFile ? 'registered' : 'new';
59
+ return {
60
+ label: `${t.name} (${status})`,
61
+ value: t.path,
62
+ };
63
+ });
64
+ return (React.createElement(Box, { flexDirection: "column" },
65
+ React.createElement(Branding, { subtitle: "Register templates" }),
66
+ React.createElement(Text, null, "Use arrow/space to select, then press Enter to register."),
67
+ React.createElement(Box, { marginTop: 1 },
68
+ React.createElement(Text, { color: "white" },
69
+ selectedValues.length,
70
+ " / ",
71
+ options.length,
72
+ " selected")),
73
+ React.createElement(Box, { marginTop: 1, marginBottom: 1 },
74
+ React.createElement(MultiSelect, { options: options, onChange: vals => setSelectedValues(vals), onSubmit: vals => void handleTemplateRegistration(vals) })),
75
+ !!errorMessage && React.createElement(Text, { color: "red" }, errorMessage),
76
+ !!successMessage && React.createElement(Text, { color: "green" }, successMessage)));
77
+ }
78
+ return (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
79
+ !!errorMessage && React.createElement(Text, { color: "red" }, errorMessage),
80
+ !!successMessage && React.createElement(Text, { color: "green" }, successMessage)));
81
+ }
82
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/commands/register.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export default function Watch(): React.JSX.Element;
@@ -0,0 +1,127 @@
1
+ import path from 'node:path';
2
+ import { Box, Text, useApp, useInput } from 'ink';
3
+ import React from 'react';
4
+ import Branding from '../components/Branding.js';
5
+ import { TimeSince } from '../components/TimeSince.js';
6
+ import { TemplateManager } from '../lib/templateManager.js';
7
+ export default function Watch() {
8
+ const { exit } = useApp();
9
+ const [templates, setTemplates] = React.useState([]);
10
+ const [error, setError] = React.useState();
11
+ const managerRef = React.useRef();
12
+ const mounted = React.useRef(true);
13
+ useInput((input, key) => {
14
+ if (input.toLowerCase() === 'q' || (key.ctrl && input === 'c')) {
15
+ mounted.current = false;
16
+ setTimeout(() => exit(), 0);
17
+ }
18
+ });
19
+ React.useEffect(() => {
20
+ let cleanup;
21
+ console.clear();
22
+ async function init() {
23
+ try {
24
+ managerRef.current = await TemplateManager.create(process.cwd(), { silent: true });
25
+ // Initial template load
26
+ const initialTemplates = await managerRef.current.findTemplates();
27
+ for (const templatePath of initialTemplates) {
28
+ const status = await managerRef.current.getTemplateStatus(templatePath);
29
+ if (mounted.current) {
30
+ setTemplates(prev => [...prev.filter(t => t.path !== status.path), status]);
31
+ }
32
+ }
33
+ // Watch and handle changes
34
+ const watcher = await managerRef.current.watch();
35
+ // Update UI on template changes
36
+ const updateTemplate = async (template) => {
37
+ if (!mounted.current)
38
+ return;
39
+ const status = await managerRef.current?.getTemplateStatus(template.path);
40
+ if (status) {
41
+ setTemplates(prev => [...prev.filter(t => t.path !== status.path), status]);
42
+ }
43
+ };
44
+ managerRef.current.on('templateChanged', updateTemplate);
45
+ managerRef.current.on('templateApplied', updateTemplate);
46
+ managerRef.current.on('templateError', ({ template }) => updateTemplate(template));
47
+ // Initial apply for any out-of-date templates
48
+ await managerRef.current.processTemplates({ apply: true });
49
+ return () => {
50
+ mounted.current = false;
51
+ watcher.close();
52
+ };
53
+ }
54
+ catch (err) {
55
+ if (mounted.current) {
56
+ setError(err instanceof Error ? err.message : String(err));
57
+ }
58
+ return () => {
59
+ mounted.current = false;
60
+ };
61
+ }
62
+ }
63
+ init().then(c => {
64
+ cleanup = c;
65
+ });
66
+ return () => cleanup?.();
67
+ }, []);
68
+ if (error) {
69
+ return React.createElement(Text, { color: "red" },
70
+ "Error: ",
71
+ error);
72
+ }
73
+ const templatesByDir = templates.reduce((acc, template) => {
74
+ const dir = path.dirname(path.relative(process.cwd(), template.path));
75
+ if (!acc[dir]) {
76
+ acc[dir] = [];
77
+ }
78
+ acc[dir].push(template);
79
+ return acc;
80
+ }, {});
81
+ const hasErrors = templates.some(t => t.buildState.lastAppliedError);
82
+ return (React.createElement(Box, { flexDirection: "column", marginBottom: 2, marginTop: 2 },
83
+ React.createElement(Branding, { subtitle: "Watch Mode" }),
84
+ Object.entries(templatesByDir).map(([dir, dirTemplates]) => (React.createElement(Box, { key: dir, flexDirection: "column", marginLeft: 1 },
85
+ React.createElement(Text, { dimColor: true }, dir),
86
+ dirTemplates.map(template => (React.createElement(Box, { key: template.path, marginLeft: 2 },
87
+ React.createElement(Box, { width: 2 },
88
+ React.createElement(Text, null, template.buildState.lastAppliedError
89
+ ? '❌'
90
+ : template === templates[templates.length - 1]
91
+ ? '✓'
92
+ : ' ')),
93
+ React.createElement(Box, { width: 20 },
94
+ React.createElement(Text, null, path.basename(template.path))),
95
+ React.createElement(Box, null,
96
+ React.createElement(Text, { dimColor: true },
97
+ "applied ",
98
+ React.createElement(TimeSince, { date: template.buildState.lastAppliedDate }),
99
+ " ago",
100
+ !template.buildState.lastBuildDate ||
101
+ template.currentHash !== template.buildState.lastBuildHash ? (React.createElement(React.Fragment, null, " \u2022 needs build")) : (React.createElement(React.Fragment, null,
102
+ ' ',
103
+ "\u2022 built ",
104
+ React.createElement(TimeSince, { date: template.buildState.lastBuildDate }),
105
+ " ago")))))))))),
106
+ hasErrors && (React.createElement(Box, { flexDirection: "column", marginY: 1 },
107
+ React.createElement(Text, { bold: true, color: "red" }, "Errors"),
108
+ templates
109
+ .filter(t => t.buildState.lastAppliedError)
110
+ .map(t => (React.createElement(Box, { key: t.name, marginLeft: 2 },
111
+ React.createElement(Text, { color: "red" },
112
+ "\u2022 ",
113
+ t.name,
114
+ ": ",
115
+ t.buildState.lastAppliedError)))))),
116
+ React.createElement(Box, { marginY: 1 },
117
+ React.createElement(Text, { bold: true, backgroundColor: hasErrors ? 'red' : 'green' }, hasErrors ? ' FAIL ' : ' OK '),
118
+ React.createElement(Text, null, " "),
119
+ React.createElement(Text, { dimColor: true }, "Watching for template changes...")),
120
+ React.createElement(Box, null,
121
+ React.createElement(Text, { dimColor: true }, "press "),
122
+ React.createElement(Text, null, "q"),
123
+ React.createElement(Text, { dimColor: true }, " or "),
124
+ React.createElement(Text, null, "Ctrl+c"),
125
+ React.createElement(Text, { dimColor: true }, " to quit"))));
126
+ }
127
+ //# sourceMappingURL=watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/commands/watch.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ type Props = {
3
+ subtitle?: string;
4
+ };
5
+ export default function Branding({ subtitle }: Props): React.JSX.Element;
6
+ export {};
@@ -0,0 +1,28 @@
1
+ import { Box, Text } from 'ink';
2
+ import React from 'react';
3
+ import packageJson from '../../package.json' assert { type: 'json' };
4
+ export default function Branding({ subtitle }) {
5
+ return (React.createElement(Box, { marginBottom: 1, marginTop: 1, gap: 1, flexDirection: "column" },
6
+ React.createElement(Box, { gap: 1 },
7
+ React.createElement(Text, { bold: true, backgroundColor: "#3ecf8e" },
8
+ ' ',
9
+ "srtd",
10
+ ' '),
11
+ subtitle ? (React.createElement(Text, null, subtitle)) : (React.createElement(Text, null,
12
+ "(",
13
+ React.createElement(Text, { bold: true, color: "#3ecf8e" }, "S"),
14
+ "upabase",
15
+ ' ',
16
+ React.createElement(Text, { bold: true, color: "#3ecf8e" }, "R"),
17
+ "epeatable",
18
+ ' ',
19
+ React.createElement(Text, { bold: true, color: "#3ecf8e" }, "T"),
20
+ "emplate",
21
+ ' ',
22
+ React.createElement(Text, { bold: true, color: "#3ecf8e" }, "D"),
23
+ "efinitions)")),
24
+ React.createElement(Text, { dimColor: true },
25
+ "v",
26
+ packageJson.version))));
27
+ }
28
+ //# sourceMappingURL=Branding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Branding.js","sourceRoot":"","sources":["../../src/components/Branding.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,WAAW,MAAM,oBAAoB,CAAC,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;AAMrE,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,QAAQ,EAAS;IAClD,OAAO,CACL,oBAAC,GAAG,IAAC,YAAY,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ;QAChE,oBAAC,GAAG,IAAC,GAAG,EAAE,CAAC;YACT,oBAAC,IAAI,IAAC,IAAI,QAAC,eAAe,EAAC,SAAS;gBACjC,GAAG;;gBACC,GAAG,CACH;YACN,QAAQ,CAAC,CAAC,CAAC,CACV,oBAAC,IAAI,QAAE,QAAQ,CAAQ,CACxB,CAAC,CAAC,CAAC,CACF,oBAAC,IAAI;;gBAEH,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,SAAS,QAEnB;;gBACC,GAAG;gBACX,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,SAAS,QAEnB;;gBACG,GAAG;gBACb,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,SAAS,QAEnB;;gBACC,GAAG;gBACX,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,SAAS,QAEnB;8BAEF,CACR;YACD,oBAAC,IAAI,IAAC,QAAQ;;gBAAG,WAAW,CAAC,OAAO,CAAQ,CACxC,CACF,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export declare const TimeSince: React.FC<{
3
+ date?: string;
4
+ }>;
@@ -0,0 +1,29 @@
1
+ import { Text } from 'ink';
2
+ import React from 'react';
3
+ export const TimeSince = ({ date }) => {
4
+ const [now, setNow] = React.useState(new Date());
5
+ React.useEffect(() => {
6
+ const timer = setInterval(() => setNow(new Date()), 1000);
7
+ return () => clearInterval(timer);
8
+ }, []);
9
+ if (!date)
10
+ return React.createElement(Text, null, "never");
11
+ const diff = now.getTime() - new Date(date).getTime();
12
+ const seconds = Math.floor(diff / 1000);
13
+ if (seconds < 60)
14
+ return React.createElement(Text, null,
15
+ seconds,
16
+ "s");
17
+ if (seconds < 3600)
18
+ return React.createElement(Text, null,
19
+ Math.floor(seconds / 60),
20
+ "m");
21
+ if (seconds < 86400)
22
+ return React.createElement(Text, null,
23
+ Math.floor(seconds / 3600),
24
+ "h");
25
+ return React.createElement(Text, null,
26
+ Math.floor(seconds / 86400),
27
+ "d");
28
+ };
29
+ //# sourceMappingURL=TimeSince.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimeSince.js","sourceRoot":"","sources":["../../src/components/TimeSince.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,SAAS,GAAgC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;IACjE,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAEjD,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1D,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,IAAI,CAAC,IAAI;QAAE,OAAO,oBAAC,IAAI,gBAAa,CAAC;IAErC,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;IACtD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAExC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,oBAAC,IAAI;YAAE,OAAO;gBAAS,CAAC;IACjD,IAAI,OAAO,GAAG,IAAI;QAAE,OAAO,oBAAC,IAAI;YAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;gBAAS,CAAC;IACpE,IAAI,OAAO,GAAG,KAAK;QAAE,OAAO,oBAAC,IAAI;YAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;gBAAS,CAAC;IACvE,OAAO,oBAAC,IAAI;QAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;YAAS,CAAC;AACrD,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare const CONFIG_FILE = "srtd.config.json";
@@ -0,0 +1,2 @@
1
+ export const CONFIG_FILE = 'srtd.config.json';
2
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { TemplateStatus } from '../types.js';
2
+ export declare function useTemplateState(): {
3
+ loading: boolean;
4
+ error: string | null;
5
+ items: TemplateStatus[];
6
+ };
@@ -0,0 +1,28 @@
1
+ // hooks/useTemplateState.ts
2
+ import { useEffect, useState } from 'react';
3
+ import { TemplateManager } from '../lib/templateManager.js';
4
+ export function useTemplateState() {
5
+ const [loading, setLoading] = useState(true);
6
+ const [error, setError] = useState(null);
7
+ const [items, setItems] = useState([]);
8
+ useEffect(() => {
9
+ async function fetchStatus() {
10
+ try {
11
+ const baseDir = process.cwd();
12
+ const manager = await TemplateManager.create(baseDir);
13
+ const templates = await manager.findTemplates();
14
+ const statuses = await Promise.all(templates.map(t => manager.getTemplateStatus(t)));
15
+ setItems(statuses);
16
+ }
17
+ catch (err) {
18
+ setError(err instanceof Error ? err.message : 'Unknown error');
19
+ }
20
+ finally {
21
+ setLoading(false);
22
+ }
23
+ }
24
+ fetchStatus();
25
+ }, []);
26
+ return { loading, error, items };
27
+ }
28
+ //# sourceMappingURL=useTemplateState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTemplateState.js","sourceRoot":"","sources":["../../src/hooks/useTemplateState.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAG5D,MAAM,UAAU,gBAAgB;IAC9B,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAmB,EAAE,CAAC,CAAC;IAEzD,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,UAAU,WAAW;YACxB,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;gBAC9B,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACtD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;gBAChD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACrF,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACrB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;YACjE,CAAC;oBAAS,CAAC;gBACT,UAAU,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;QACD,WAAW,EAAE,CAAC;IAChB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACnC,CAAC"}
@@ -0,0 +1,32 @@
1
+ import EventEmitter from 'node:events';
2
+ import type { CLIResult, TemplateStatus } from '../types.js';
3
+ export declare class TemplateManager extends EventEmitter {
4
+ private baseDir;
5
+ private buildLog;
6
+ private localBuildLog;
7
+ private config;
8
+ private templateCache;
9
+ private cacheTimeout;
10
+ private silent;
11
+ private constructor();
12
+ static create(baseDir: string, options?: {
13
+ silent?: boolean;
14
+ }): Promise<TemplateManager>;
15
+ private isCacheValid;
16
+ private invalidateCache;
17
+ findTemplates(): Promise<string[]>;
18
+ getTemplateStatus(templatePath: string): Promise<TemplateStatus>;
19
+ private saveBuildLogs;
20
+ private handleTemplateChange;
21
+ watch(): Promise<{
22
+ close: () => void;
23
+ }>;
24
+ applyTemplate(templatePath: string): Promise<CLIResult>;
25
+ buildTemplate(templatePath: string, force?: boolean): Promise<void>;
26
+ private log;
27
+ processTemplates(options: {
28
+ apply?: boolean;
29
+ generateFiles?: boolean;
30
+ force?: boolean;
31
+ }): Promise<CLIResult>;
32
+ }
@@ -0,0 +1,237 @@
1
+ import EventEmitter from 'node:events';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { glob } from 'glob';
5
+ import { applyMigration } from '../utils/applyMigration.js';
6
+ import { calculateMD5 } from '../utils/calculateMD5.js';
7
+ import { getConfig } from '../utils/config.js';
8
+ import { getNextTimestamp } from '../utils/getNextTimestamp.js';
9
+ import { isWipTemplate } from '../utils/isWipTemplate.js';
10
+ import { loadBuildLog } from '../utils/loadBuildLog.js';
11
+ import { logger } from '../utils/logger.js';
12
+ import { saveBuildLog } from '../utils/saveBuildLog.js';
13
+ export class TemplateManager extends EventEmitter {
14
+ baseDir;
15
+ buildLog;
16
+ localBuildLog;
17
+ config;
18
+ templateCache = new Map();
19
+ cacheTimeout = 1000;
20
+ silent;
21
+ // Constructor:
22
+ constructor(baseDir, buildLog, localBuildLog, config, options = {}) {
23
+ super();
24
+ this.silent = options.silent ?? false;
25
+ this.baseDir = baseDir;
26
+ this.buildLog = buildLog;
27
+ this.localBuildLog = localBuildLog;
28
+ this.config = config;
29
+ }
30
+ static async create(baseDir, options = {}) {
31
+ const config = await getConfig(baseDir);
32
+ const buildLog = await loadBuildLog(baseDir, 'common');
33
+ const localBuildLog = await loadBuildLog(baseDir, 'local');
34
+ return new TemplateManager(baseDir, buildLog, localBuildLog, config, options);
35
+ }
36
+ isCacheValid(cache) {
37
+ return Date.now() - cache.lastChecked < this.cacheTimeout;
38
+ }
39
+ invalidateCache(templatePath) {
40
+ this.templateCache.delete(templatePath);
41
+ }
42
+ async findTemplates() {
43
+ const templatePath = path.join(this.baseDir, this.config.templateDir, this.config.filter);
44
+ const matches = await glob(templatePath);
45
+ return matches;
46
+ }
47
+ async getTemplateStatus(templatePath) {
48
+ const cached = this.templateCache.get(templatePath);
49
+ if (cached && this.isCacheValid(cached)) {
50
+ return cached.status;
51
+ }
52
+ const content = await fs.readFile(templatePath, 'utf-8');
53
+ const currentHash = await calculateMD5(content);
54
+ const relPath = path.relative(this.baseDir, templatePath);
55
+ // Merge build and apply states
56
+ const buildState = {
57
+ ...this.buildLog.templates[relPath],
58
+ ...this.localBuildLog.templates[relPath],
59
+ };
60
+ const status = {
61
+ name: path.basename(templatePath, '.sql'),
62
+ path: templatePath,
63
+ currentHash,
64
+ migrationHash: null,
65
+ buildState,
66
+ };
67
+ this.templateCache.set(templatePath, {
68
+ status,
69
+ lastChecked: Date.now(),
70
+ });
71
+ return status;
72
+ }
73
+ async saveBuildLogs() {
74
+ await Promise.all([
75
+ saveBuildLog(this.baseDir, this.buildLog, 'common'),
76
+ saveBuildLog(this.baseDir, this.localBuildLog, 'local'),
77
+ ]);
78
+ }
79
+ async handleTemplateChange(templatePath) {
80
+ this.invalidateCache(templatePath);
81
+ const template = await this.getTemplateStatus(templatePath);
82
+ this.emit('templateChanged', template);
83
+ const result = await this.applyTemplate(templatePath);
84
+ if (result.errors.length) {
85
+ this.emit('templateError', {
86
+ template,
87
+ error: result.errors[0],
88
+ });
89
+ }
90
+ else {
91
+ const updatedTemplate = await this.getTemplateStatus(templatePath);
92
+ this.emit('templateApplied', updatedTemplate);
93
+ }
94
+ }
95
+ async watch() {
96
+ const chokidar = await import('chokidar');
97
+ const templatePath = path.join(this.baseDir, this.config.templateDir);
98
+ const watcher = chokidar.watch(templatePath, {
99
+ ignoreInitial: true,
100
+ depth: 0,
101
+ ignored: /(^|[\\])\../,
102
+ });
103
+ watcher.on('change', async (filepath) => {
104
+ if (path.extname(filepath) === '.sql') {
105
+ await this.handleTemplateChange(filepath);
106
+ }
107
+ });
108
+ return watcher;
109
+ }
110
+ async applyTemplate(templatePath) {
111
+ const template = await this.getTemplateStatus(templatePath);
112
+ const content = await fs.readFile(templatePath, 'utf-8');
113
+ const result = await applyMigration(content, template.name, this.silent);
114
+ const relPath = path.relative(this.baseDir, templatePath);
115
+ this.invalidateCache(templatePath);
116
+ if (result === true) {
117
+ this.localBuildLog.templates[relPath] = {
118
+ ...this.localBuildLog.templates[relPath],
119
+ lastAppliedHash: template.currentHash,
120
+ lastAppliedDate: new Date().toISOString(),
121
+ lastAppliedError: undefined,
122
+ };
123
+ await this.saveBuildLogs();
124
+ this.emit('templateApplied', template);
125
+ return { errors: [], applied: [template.name] };
126
+ }
127
+ this.localBuildLog.templates[relPath] = {
128
+ ...this.localBuildLog.templates[relPath],
129
+ lastAppliedError: result.error,
130
+ };
131
+ await this.saveBuildLogs();
132
+ this.emit('templateError', { template, error: result });
133
+ return { errors: [result], applied: [] };
134
+ }
135
+ async buildTemplate(templatePath, force = false) {
136
+ const template = await this.getTemplateStatus(templatePath);
137
+ const isWip = await isWipTemplate(templatePath);
138
+ const relPath = path.relative(this.baseDir, templatePath);
139
+ if (isWip) {
140
+ this.log(`Skipping WIP template: ${template.name}`, 'skip');
141
+ return;
142
+ }
143
+ const content = await fs.readFile(templatePath, 'utf-8');
144
+ const currentHash = await calculateMD5(content);
145
+ if (!force && this.buildLog.templates[relPath]?.lastBuildHash === currentHash) {
146
+ this.log(`Skipping unchanged template: ${template.name}`, 'skip');
147
+ return;
148
+ }
149
+ const timestamp = await getNextTimestamp(this.buildLog);
150
+ const migrationName = `${timestamp}_tmpl-${template.name}.sql`;
151
+ const migrationPath = path.join(this.config.migrationDir, migrationName);
152
+ const header = `-- Generated from template: ${this.config.templateDir}/${template.name}.sql\n`;
153
+ const banner = this.config.banner ? `-- ${this.config.banner}\n` : '\n';
154
+ const footer = `${this.config.footer}\n-- Last built: ${this.buildLog.templates[relPath]?.lastBuildDate || 'Never'}`;
155
+ const safeContent = this.config.wrapInTransaction ? `BEGIN;\n${content}\nCOMMIT;` : content;
156
+ const migrationContent = `${header}${banner}\n${safeContent}\n${footer}`;
157
+ await fs.writeFile(path.resolve(this.baseDir, migrationPath), migrationContent);
158
+ this.buildLog.templates[relPath] = {
159
+ ...this.buildLog.templates[relPath],
160
+ lastBuildHash: currentHash,
161
+ lastBuildDate: new Date().toISOString(),
162
+ lastMigrationFile: migrationName,
163
+ lastBuildError: undefined,
164
+ };
165
+ this.invalidateCache(templatePath);
166
+ await this.saveBuildLogs();
167
+ this.emit('templateBuilt', template);
168
+ }
169
+ log(msg, type = 'info') {
170
+ if (this.silent)
171
+ return;
172
+ if (type === 'error')
173
+ logger.error(msg);
174
+ else if (type === 'success')
175
+ logger.success(msg);
176
+ else if (type === 'skip')
177
+ logger.skip(msg);
178
+ else
179
+ logger.info(msg);
180
+ }
181
+ async processTemplates(options) {
182
+ const templates = await this.findTemplates();
183
+ const result = { errors: [], applied: [] };
184
+ if (options.apply) {
185
+ this.log('Applying changes...');
186
+ let hasChanges = false;
187
+ for (const templatePath of templates) {
188
+ const template = await this.getTemplateStatus(templatePath);
189
+ const needsApply = !template.buildState.lastAppliedHash ||
190
+ template.buildState.lastAppliedHash !== template.currentHash;
191
+ if (needsApply) {
192
+ hasChanges = true;
193
+ const applyResult = await this.applyTemplate(templatePath);
194
+ result.errors.push(...applyResult.errors);
195
+ result.applied.push(...applyResult.applied);
196
+ }
197
+ }
198
+ if (!hasChanges) {
199
+ this.log('No changes to apply');
200
+ }
201
+ else if (result.errors.length > 0) {
202
+ this.log(`${result.errors.length} template(s) failed to apply`, 'error');
203
+ for (const err of result.errors) {
204
+ this.log(`${err.file}: ${err.error}`, 'error');
205
+ }
206
+ }
207
+ else {
208
+ this.log(`Applied ${result.applied.length} template(s)`, 'success');
209
+ }
210
+ }
211
+ if (options.generateFiles) {
212
+ let built = 0;
213
+ let skipped = 0;
214
+ for (const templatePath of templates) {
215
+ const isWip = await isWipTemplate(templatePath);
216
+ if (!isWip) {
217
+ const template = await this.getTemplateStatus(templatePath);
218
+ if (options.force || template.currentHash !== template.buildState.lastBuildHash) {
219
+ await this.buildTemplate(templatePath, options.force);
220
+ built++;
221
+ }
222
+ else {
223
+ skipped++;
224
+ }
225
+ }
226
+ }
227
+ if (built > 0) {
228
+ this.log(`Generated ${built} migration file(s)`, 'success');
229
+ }
230
+ else if (skipped > 0) {
231
+ this.log('No changes to build');
232
+ }
233
+ }
234
+ return result;
235
+ }
236
+ }
237
+ //# sourceMappingURL=templateManager.js.map