@t1mmen/srtd 0.0.0-next-20251227000343

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +363 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +50 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/apply.d.ts +2 -0
  7. package/dist/commands/apply.js +105 -0
  8. package/dist/commands/apply.js.map +1 -0
  9. package/dist/commands/build.d.ts +2 -0
  10. package/dist/commands/build.js +134 -0
  11. package/dist/commands/build.js.map +1 -0
  12. package/dist/commands/clear.d.ts +2 -0
  13. package/dist/commands/clear.js +161 -0
  14. package/dist/commands/clear.js.map +1 -0
  15. package/dist/commands/init.d.ts +2 -0
  16. package/dist/commands/init.js +91 -0
  17. package/dist/commands/init.js.map +1 -0
  18. package/dist/commands/menu.d.ts +4 -0
  19. package/dist/commands/menu.js +76 -0
  20. package/dist/commands/menu.js.map +1 -0
  21. package/dist/commands/promote.d.ts +2 -0
  22. package/dist/commands/promote.js +181 -0
  23. package/dist/commands/promote.js.map +1 -0
  24. package/dist/commands/register.d.ts +2 -0
  25. package/dist/commands/register.js +192 -0
  26. package/dist/commands/register.js.map +1 -0
  27. package/dist/commands/watch.d.ts +14 -0
  28. package/dist/commands/watch.js +190 -0
  29. package/dist/commands/watch.js.map +1 -0
  30. package/dist/constants.d.ts +1 -0
  31. package/dist/constants.js +2 -0
  32. package/dist/constants.js.map +1 -0
  33. package/dist/services/DatabaseService.d.ts +113 -0
  34. package/dist/services/DatabaseService.js +343 -0
  35. package/dist/services/DatabaseService.js.map +1 -0
  36. package/dist/services/FileSystemService.d.ts +100 -0
  37. package/dist/services/FileSystemService.js +237 -0
  38. package/dist/services/FileSystemService.js.map +1 -0
  39. package/dist/services/MigrationBuilder.d.ts +106 -0
  40. package/dist/services/MigrationBuilder.js +193 -0
  41. package/dist/services/MigrationBuilder.js.map +1 -0
  42. package/dist/services/Orchestrator.d.ts +155 -0
  43. package/dist/services/Orchestrator.js +622 -0
  44. package/dist/services/Orchestrator.js.map +1 -0
  45. package/dist/services/StateService.d.ts +169 -0
  46. package/dist/services/StateService.js +463 -0
  47. package/dist/services/StateService.js.map +1 -0
  48. package/dist/types.d.ts +48 -0
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -0
  51. package/dist/ui/badge.d.ts +14 -0
  52. package/dist/ui/badge.js +28 -0
  53. package/dist/ui/badge.js.map +1 -0
  54. package/dist/ui/branding.d.ts +9 -0
  55. package/dist/ui/branding.js +35 -0
  56. package/dist/ui/branding.js.map +1 -0
  57. package/dist/ui/index.d.ts +4 -0
  58. package/dist/ui/index.js +5 -0
  59. package/dist/ui/index.js.map +1 -0
  60. package/dist/ui/results.d.ts +10 -0
  61. package/dist/ui/results.js +62 -0
  62. package/dist/ui/results.js.map +1 -0
  63. package/dist/ui/spinner.d.ts +5 -0
  64. package/dist/ui/spinner.js +8 -0
  65. package/dist/ui/spinner.js.map +1 -0
  66. package/dist/utils/config.d.ts +12 -0
  67. package/dist/utils/config.js +67 -0
  68. package/dist/utils/config.js.map +1 -0
  69. package/dist/utils/createEmptyBuildLog.d.ts +1 -0
  70. package/dist/utils/createEmptyBuildLog.js +10 -0
  71. package/dist/utils/createEmptyBuildLog.js.map +1 -0
  72. package/dist/utils/ensureDirectories.d.ts +4 -0
  73. package/dist/utils/ensureDirectories.js +23 -0
  74. package/dist/utils/ensureDirectories.js.map +1 -0
  75. package/dist/utils/fileExists.d.ts +1 -0
  76. package/dist/utils/fileExists.js +11 -0
  77. package/dist/utils/fileExists.js.map +1 -0
  78. package/dist/utils/findProjectRoot.d.ts +1 -0
  79. package/dist/utils/findProjectRoot.js +25 -0
  80. package/dist/utils/findProjectRoot.js.map +1 -0
  81. package/dist/utils/getErrorMessage.d.ts +9 -0
  82. package/dist/utils/getErrorMessage.js +14 -0
  83. package/dist/utils/getErrorMessage.js.map +1 -0
  84. package/dist/utils/getNextTimestamp.d.ts +2 -0
  85. package/dist/utils/getNextTimestamp.js +12 -0
  86. package/dist/utils/getNextTimestamp.js.map +1 -0
  87. package/dist/utils/isWipTemplate.d.ts +1 -0
  88. package/dist/utils/isWipTemplate.js +6 -0
  89. package/dist/utils/isWipTemplate.js.map +1 -0
  90. package/dist/utils/logger.d.ts +9 -0
  91. package/dist/utils/logger.js +12 -0
  92. package/dist/utils/logger.js.map +1 -0
  93. package/dist/utils/safeCreate.d.ts +1 -0
  94. package/dist/utils/safeCreate.js +16 -0
  95. package/dist/utils/safeCreate.js.map +1 -0
  96. package/package.json +106 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Timm Stokke
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,363 @@
1
+ # `srtd` 🪄 Supabase Repeatable Template Definitions
2
+
3
+
4
+
5
+ > Live-reloading SQL templates for [Supabase](https://supabase.com) projects. DX supercharged! 🚀
6
+
7
+ [![NPM Version](https://img.shields.io/npm/v/%40t1mmen%2Fsrtd)](https://www.npmjs.com/package/@t1mmen/srtd)
8
+ [![Downloads](https://img.shields.io/npm/dt/%40t1mmen%2Fsrtd)](https://www.npmjs.com/package/@t1mmen/srtd)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+ [![CI/CD](https://github.com/t1mmen/srtd/actions/workflows/ci.yml/badge.svg)](https://github.com/t1mmen/srtd/actions/workflows/ci.yml)
11
+ [![codecov](https://codecov.io/gh/t1mmen/srtd/graph/badge.svg?token=CIMAZ55KCJ)](https://codecov.io/gh/t1mmen/srtd)
12
+
13
+
14
+ [![video demo](./readme-demo.gif)](./readme-demo.gif)
15
+
16
+
17
+ `srtd` enhances the [Supabase](https://supabase.com) DX by adding **live-reloading SQL** from templates into local db.
18
+
19
+ **Templates act as single-source-of-truth of your database objects**, that `build` to regular SQL migrations; This makes for sane code reviews and functional change history, (e.g `git blame` works as expected).
20
+
21
+
22
+ 📖 Blog: [Introducing `srtd`: Live-Reloading SQL Templates for Supabase](https://timm.stokke.me/blog/srtd-live-reloading-and-sql-templates-for-supabase)
23
+
24
+ ### Why This Exists 🤔
25
+
26
+ While building [Timely](https://www.timely.com)'s next-generation [Memory Engine](https://www.timely.com/memory-app) on [Supabase](https://supabase.com), we found ourselves facing two major annoyances:
27
+
28
+ 1. Code reviews were painful - function changes showed up as complete rewrites, `git blame` was useless
29
+ 2. Designing and iterating on database changes locally was full of friction, no matter which workflow we tried
30
+
31
+ I spent [nearly two](https://news.ycombinator.com/item?id=37755076) [years looking](https://news.ycombinator.com/item?id=36007640) for something pre-existing, to no avail. Sufficiently fed up, I paired with [Claude](https://claude.ai) to eliminate these annoyances.
32
+
33
+ Say hello to `srtd`.
34
+
35
+ [![screenshot of srtd](./readme-screenshot.png)](./readme-screenshot.png)
36
+
37
+ ## Key Features ✨
38
+
39
+ - **Live Reload**: Changes to your SQL templates instantly update your local database
40
+ - **Templates as source of truth**: Templates are the source of (non-mutable) database objects
41
+ - **Just SQL**: Templates as just SQL, and `build` to standard [Supabase](https://supabase.com) migrations when you're ready to ship
42
+ - **Sane code reviews**: Templates evolve like regular code, with diffs in PR's working `git blame`.
43
+ - **Developer Friendly**: Interactive CLI with visual feedback for all operations.
44
+
45
+ Built specifically for projects using the standard [Supabase](https://supabase.com) stack (but probably works alright for other Postgres-based projects, too).
46
+
47
+ ## Quick Start 🚀
48
+
49
+ ### Requirements
50
+
51
+ - Node.js v20.18.1 or higher
52
+ - [Supabase](https://supabase.com) project initialized (in `/supabase`).
53
+
54
+ ### Installation
55
+
56
+
57
+ ```bash
58
+ # Global installation
59
+ npm install -g @t1mmen/srtd
60
+
61
+ # Project installation
62
+ npm install --save-dev @t1mmen/srtd
63
+
64
+ # Or run directly
65
+ npx @t1mmen/srtd
66
+ ```
67
+
68
+ ### Setup
69
+
70
+ ```bash
71
+ cd your-supabase-project
72
+ npx @t1mmen/srtd init # Creates srtd.config.json, not required
73
+ ```
74
+
75
+ ### Create Your First Template
76
+
77
+ Create `supabase/migrations-templates/my_function.sql`:
78
+
79
+ ```sql
80
+ DROP FUNCTION IF EXISTS public.my_function; -- Makes it easier to change args later
81
+ CREATE FUNCTION my_function()
82
+ RETURNS void AS $$
83
+ BEGIN
84
+ -- Your function logic here
85
+ END;
86
+ $$ LANGUAGE plpgsql;
87
+ ```
88
+
89
+ ### Development Workflow
90
+
91
+ 1. Start watch mode:
92
+ ```bash
93
+ npx @t1mmen/srtd watch # Changes auto-apply to local database
94
+ ```
95
+
96
+ 2. When ready to deploy:
97
+ ```bash
98
+ npx @t1mmen/srtd build # Creates timestamped migration file
99
+ supabase migration up # Apply using Supabase CLI
100
+ ```
101
+
102
+ > [!TIP]
103
+ > To reduce noise in PR's, consider adding `supabase/migrations/*srtd*.sql linguist-generated=true` to your [`.gitattributes` file.](https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github) (unless you manually edit the generated files)
104
+
105
+
106
+ ## The Power of Templates 💪
107
+
108
+ Without templates, the smallest change to a function would show up as a complete rewrite in your version control system. With templates, the diff is clear and concise.
109
+
110
+
111
+ ### Perfect For 🎯
112
+
113
+ ✅ Database functions:
114
+ ```diff
115
+ -- Event notifications
116
+ DROP FUNCTION IF EXISTS notify_changes;
117
+ CREATE FUNCTION notify_changes()
118
+ RETURNS trigger AS $$
119
+ BEGIN
120
+ PERFORM pg_notify(
121
+ 'changes',
122
+ json_build_object('table', TG_TABLE_NAME, 'id', NEW.id)::text
123
+ );
124
+ + RAISE NOTICE 'Notified changes for %', TG_TABLE_NAME; -- Debug logging
125
+ RETURN NEW;
126
+ END;
127
+ $$ LANGUAGE plpgsql;
128
+ ```
129
+
130
+ ✅ Row-Level Security (RLS):
131
+ ```diff
132
+ -- Replace/update policies safely
133
+ DROP POLICY IF EXISTS "workspace_access" ON resources;
134
+ CREATE POLICY "workspace_access" ON resources
135
+ USING (workspace_id IN (
136
+ SELECT id FROM workspaces
137
+ WHERE organization_id = auth.organization_id()
138
+ + AND auth.user_role() NOT IN ('pending')
139
+ ));
140
+ ```
141
+
142
+ ✅ Views for data abstraction:
143
+ ```diff
144
+ CREATE OR REPLACE VIEW active_subscriptions AS
145
+ SELECT
146
+ s.*,
147
+ p.name as plan_name,
148
+ p.features
149
+ FROM subscriptions s
150
+ JOIN plans p ON p.id = s.plan_id
151
+ - WHERE s.status = 'active';
152
+ + WHERE s.status = 'active'
153
+ + AND s.expires_at > CURRENT_TIMESTAMP;
154
+ ```
155
+
156
+ ✅ Roles and Permissions:
157
+ ```diff
158
+ -- Revoke all first for clean state
159
+ REVOKE ALL ON ALL TABLES IN SCHEMA public FROM public;
160
+
161
+ -- Grant specific access
162
+ GRANT USAGE ON SCHEMA public TO authenticated;
163
+ GRANT SELECT ON ALL TABLES IN SCHEMA public TO authenticated;
164
+ + GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO admin;
165
+ ```
166
+
167
+ ✅ Safe Type Extensions:
168
+ ```diff
169
+ DO $$
170
+ BEGIN
171
+ -- Add new enum values idempotently
172
+ IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'notification_type') THEN
173
+ CREATE TYPE notification_type AS ENUM ('email', 'sms');
174
+ END IF;
175
+
176
+ -- Extend existing enum safely
177
+ ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'push';
178
+ ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'pusher';
179
+ ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'webhook';
180
+ + ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'whatsapp';
181
+ END $$;
182
+ ```
183
+
184
+ ✅ Triggers
185
+ ```diff
186
+ DROP TRIGGER IF EXISTS on_new_user ON auth.users;
187
+ DROP FUNCTION IF EXISTS public.setup_new_user;
188
+
189
+ CREATE FUNCTION public.setup_new_user() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER
190
+ SET search_path = public AS $$
191
+ BEGIN
192
+ -- Existing logic for new users
193
+
194
+ + -- Your new changes go here..
195
+ END;
196
+ $$;
197
+
198
+ CREATE TRIGGER on_new_user AFTER INSERT ON auth.users FOR EACH ROW EXECUTE PROCEDURE public.setup_new_user ();
199
+ ```
200
+
201
+ > [!TIP]
202
+ > You don't need to specifying parameters in drop functions. E.g `DROP FUNCTION IF EXISTS public.my_function;`. This ensures you don't end up with multiple functions with the same name, but different parameters.
203
+
204
+ ### Not Recommended For:
205
+ * ❌ Table structures
206
+ * ❌ Indexes
207
+ * ❌ Data modifications
208
+ * ❌ Non-idempotent operations
209
+
210
+ Use regular [Supabase](https://supabase.com) migrations for these cases.
211
+
212
+
213
+ ## Commands 🎮
214
+
215
+ ### Interactive Mode
216
+
217
+ Running `npx @t1mmen/srtd` without arguments opens an interactive menu. All commands can also be run directly:
218
+
219
+ - 👀 `srtd watch` - Watch and auto-apply changes
220
+ - 🏗️ `srtd build [--force] [--bundle]` - Generate migrations from templates
221
+ - ▶️ `srtd apply [--force]` - Apply templates directly to local database
222
+ - ✍️ `srtd register [file.sql...]` - Mark templates as already built
223
+ - 🚀 `srtd promote - [file.sql ...]` - Promote WIP template to buildable templates
224
+ - 🧹 `srtd clear` - Clear build logs or reset configuration
225
+
226
+ > [!IMPORTANT]
227
+ > `watch` and `apply` commands modify your local database directly and don't clean up after themselves. Use with caution!
228
+
229
+ ## Configuration 📝
230
+
231
+ `srtd.config.json` can be created with `init` command. It is not necessary, if the defaults suit your needs.
232
+
233
+ ```jsonc
234
+ {
235
+ // Prevents building templates with this extension
236
+ "wipIndicator": ".wip",
237
+
238
+ // Migration file naming: 20211001000000_srtd-my_function.sql
239
+ "migrationPrefix": "srtd",
240
+
241
+ // Template discovery
242
+ "filter": "**/*.sql",
243
+
244
+ // Migration file comments
245
+ "banner": "You very likely **DO NOT** want to manually edit this generated file.",
246
+ "footer": "",
247
+
248
+ // Wrap migrations in transaction
249
+ "wrapInTransaction": true,
250
+
251
+ // File paths
252
+ "templateDir": "supabase/migrations-templates",
253
+ "migrationDir": "supabase/migrations",
254
+ "buildLog": "supabase/migrations-templates/.buildlog.json",
255
+ "localBuildLog": "supabase/migrations-templates/.buildlog.local.json",
256
+
257
+ // Database connection
258
+ "pgConnection": "postgresql://postgres:postgres@localhost:54322/postgres"
259
+ }
260
+ ```
261
+
262
+ ## Other Features 🔧
263
+
264
+ ### Work in Progress Templates
265
+
266
+ Add `.wip.sql` extension to prevent migration generation:
267
+ ```bash
268
+ my_function.wip.sql # Only applied locally, never built
269
+ ```
270
+
271
+ Make a WIP template buildable as migration by renaming it, or using the `promote` command:
272
+ ```bash
273
+ npx @t1mmen/srtd promote my_function.wip.sql
274
+ ```
275
+
276
+ ### Register Existing Objects
277
+
278
+ Registering a template is useful when you're creating templates for what is already in your database. This avoids generating migrations on `build` (until they're changed)
279
+
280
+ ```bash
281
+ # Register specific template
282
+ npx @t1mmen/srtd register my_function.sql another_fn.sql
283
+
284
+ # Interactive multi-select UI
285
+ npx @t1mmen/srtd register
286
+ ```
287
+
288
+ This can be useful when setting up `srtd` for an existing project, where you may have hundreds of existing functions, views, etc that you want as templates, but don't want to generate migrations until changed later.
289
+
290
+ ### Template State Management
291
+
292
+ The state of templates are stored to..
293
+
294
+ - [`.buildlog.json`](https://github.com/t1mmen/srtd/blob/main/supabase/migrations-templates/.srtd.buildlog.json) - Migration build state (commit this)
295
+ - `.buildlog.local.json` - Local database state (add to `.gitignore`)
296
+
297
+ This helps `srtd` identify when templates are changed, to only `build` (as migrations) or `apply` the necessary changes (directly to local db).
298
+
299
+ ## Development 🛠️
300
+
301
+ ### Local Setup
302
+
303
+ ```bash
304
+ # Clone and install
305
+ git clone https://github.com/stokke/srtd.git
306
+ cd srtd
307
+ npm install
308
+
309
+ # Development
310
+ npm run dev # Watch mode
311
+ npm test # Run tests
312
+ npm start # Run CLI
313
+ npm start:link # Build, npm link, and run CLI
314
+
315
+ # Quality Checks
316
+ npm run typecheck # Type checking
317
+ npm run lint # Lint and fix
318
+ npm run format # Format code
319
+ npm run test:coverage # Test coverage
320
+ ```
321
+
322
+ ## Contributing 🤝
323
+
324
+ While feature-complete for our needs, we welcome:
325
+
326
+ - 🐛 Bug fixes and reliability improvements
327
+ - 📚 Documentation improvements
328
+ - ✅ Test coverage enhancements
329
+ - ⚡️ Performance optimizations
330
+
331
+ ### Contribution Process
332
+
333
+ 1. Create a [changeset](https://github.com/changesets/changesets) (`npm run changeset`)
334
+ 2. Ensure tests pass (`npm test`)
335
+ 3. Follow existing code style
336
+ 4. Update documentation
337
+
338
+ Note: New features are evaluated based on alignment with project scope.
339
+
340
+ ## Built With 🛠️
341
+
342
+ ### CLI
343
+ - [Commander.js](https://github.com/tj/commander.js) - CLI framework
344
+ - [Inquirer](https://github.com/SBoudrias/Inquirer.js) - Interactive prompts
345
+ - [Ora](https://github.com/sindresorhus/ora) - Terminal spinners
346
+ - [Chalk](https://github.com/chalk/chalk) - Terminal styling
347
+ - [Figures](https://github.com/sindresorhus/figures) - Unicode symbols
348
+
349
+ ### Core
350
+ - [Chokidar](https://github.com/paulmillr/chokidar) - File watcher
351
+ - [Zod](https://zod.dev/) - Schema validation
352
+ - [update-notifier](https://github.com/sindresorhus/update-notifier) - Version checks
353
+
354
+
355
+ ## License
356
+
357
+ MIT License - see [LICENSE](LICENSE) file.
358
+
359
+ ---
360
+
361
+ Made with 🪄 by [Timm Stokke](https://timm.stokke.me) & [Claude Sonnet](https://claude.ai)
362
+
363
+ [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/t1mmen)
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import updateNotifier from 'update-notifier';
4
+ import packageJson from '../package.json' with { type: 'json' };
5
+ // Check for test environment
6
+ const isTestMode = process.env.SRTD_TEST_MODE === 'true';
7
+ const nonInteractiveFlag = process.argv.includes('--non-interactive');
8
+ // Only show update notifications in non-test mode
9
+ if (!isTestMode) {
10
+ updateNotifier({ pkg: packageJson }).notify();
11
+ }
12
+ import { applyCommand } from './commands/apply.js';
13
+ import { buildCommand } from './commands/build.js';
14
+ import { clearCommand } from './commands/clear.js';
15
+ // Import commands
16
+ import { initCommand } from './commands/init.js';
17
+ import { showMenu } from './commands/menu.js';
18
+ import { promoteCommand } from './commands/promote.js';
19
+ import { registerCommand } from './commands/register.js';
20
+ import { watchCommand } from './commands/watch.js';
21
+ // Create the main program
22
+ const program = new Command();
23
+ program
24
+ .name('srtd')
25
+ .description('Supabase Repeatable Template Definitions - Live-reloading SQL templates')
26
+ .version(packageJson.version);
27
+ // Register all commands
28
+ program.addCommand(initCommand);
29
+ program.addCommand(applyCommand);
30
+ program.addCommand(buildCommand);
31
+ program.addCommand(clearCommand);
32
+ program.addCommand(promoteCommand);
33
+ program.addCommand(registerCommand);
34
+ program.addCommand(watchCommand);
35
+ // Check if no arguments were provided (flags like --version should still be parsed)
36
+ const args = process.argv.slice(2);
37
+ const hasArgs = args.length > 0;
38
+ // If no args provided and we're in TTY, show interactive menu
39
+ if (!hasArgs && process.stdin.isTTY && !isTestMode && !nonInteractiveFlag) {
40
+ await showMenu();
41
+ }
42
+ else {
43
+ // Parse command line arguments (Commander handles --help, --version, and commands)
44
+ await program.parseAsync(process.argv);
45
+ }
46
+ // For testing purposes, ensure process exits cleanly
47
+ if (isTestMode || nonInteractiveFlag) {
48
+ process.exit(0);
49
+ }
50
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,cAAc,MAAM,iBAAiB,CAAC;AAC7C,OAAO,WAAW,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAEhE,6BAA6B;AAC7B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,MAAM,CAAC;AACzD,MAAM,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;AAEtE,kDAAkD;AAClD,IAAI,CAAC,UAAU,EAAE,CAAC;IAChB,cAAc,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AAChD,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,kBAAkB;AAClB,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,0BAA0B;AAC1B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,MAAM,CAAC;KACZ,WAAW,CAAC,yEAAyE,CAAC;KACtF,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AAEhC,wBAAwB;AACxB,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AAEjC,oFAAoF;AACpF,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;AAEhC,8DAA8D;AAC9D,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,UAAU,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC1E,MAAM,QAAQ,EAAE,CAAC;AACnB,CAAC;KAAM,CAAC;IACN,mFAAmF;IACnF,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC;AAED,qDAAqD;AACrD,IAAI,UAAU,IAAI,kBAAkB,EAAE,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const applyCommand: Command;
@@ -0,0 +1,105 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
53
+ import chalk from 'chalk';
54
+ // src/commands/apply.ts
55
+ import { Command } from 'commander';
56
+ import figures from 'figures';
57
+ import { Orchestrator } from '../services/Orchestrator.js';
58
+ import { createSpinner, renderBranding, renderResults } from '../ui/index.js';
59
+ import { getConfig } from '../utils/config.js';
60
+ import { findProjectRoot } from '../utils/findProjectRoot.js';
61
+ import { getErrorMessage } from '../utils/getErrorMessage.js';
62
+ export const applyCommand = new Command('apply')
63
+ .description('Apply built migrations to the database')
64
+ .option('-f, --force', 'Force apply of all templates, irrespective of changes')
65
+ .action(async (options) => {
66
+ let exitCode = 0;
67
+ try {
68
+ const env_1 = { stack: [], error: void 0, hasError: false };
69
+ try {
70
+ await renderBranding({ subtitle: 'Apply migrations' });
71
+ const spinner = createSpinner('Applying templates...').start();
72
+ // Initialize Orchestrator
73
+ const projectRoot = await findProjectRoot();
74
+ const config = await getConfig(projectRoot);
75
+ const orchestrator = __addDisposableResource(env_1, await Orchestrator.create(projectRoot, config, { silent: true }), true);
76
+ // Execute apply operation
77
+ const result = await orchestrator.apply({
78
+ force: options.force,
79
+ silent: true,
80
+ });
81
+ spinner.stop();
82
+ // Show results
83
+ renderResults(result, { showApply: true });
84
+ exitCode = result.errors.length > 0 ? 1 : 0;
85
+ }
86
+ catch (e_1) {
87
+ env_1.error = e_1;
88
+ env_1.hasError = true;
89
+ }
90
+ finally {
91
+ const result_1 = __disposeResources(env_1);
92
+ if (result_1)
93
+ await result_1;
94
+ }
95
+ }
96
+ catch (error) {
97
+ console.log();
98
+ console.log(chalk.red(`${figures.cross} Error applying templates:`));
99
+ console.log(chalk.red(getErrorMessage(error)));
100
+ exitCode = 1;
101
+ }
102
+ // Exit AFTER the await using block has completed, ensuring dispose() runs
103
+ process.exit(exitCode);
104
+ });
105
+ //# sourceMappingURL=apply.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/commands/apply.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,wBAAwB;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,wCAAwC,CAAC;KACrD,MAAM,CAAC,aAAa,EAAE,uDAAuD,CAAC;KAC9E,MAAM,CAAC,KAAK,EAAE,OAA4B,EAAE,EAAE;IAC7C,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,IAAI,CAAC;;;YACH,MAAM,cAAc,CAAC,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAEvD,MAAM,OAAO,GAAG,aAAa,CAAC,uBAAuB,CAAC,CAAC,KAAK,EAAE,CAAC;YAE/D,0BAA0B;YAC1B,MAAM,WAAW,GAAG,MAAM,eAAe,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;YAC5C,MAAY,YAAY,kCAAG,MAAM,YAAY,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,OAAA,CAAC;YAE5F,0BAA0B;YAC1B,MAAM,MAAM,GAA4B,MAAM,YAAY,CAAC,KAAK,CAAC;gBAC/D,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,MAAM,EAAE,IAAI;aACb,CAAC,CAAC;YAEH,OAAO,CAAC,IAAI,EAAE,CAAC;YAEf,eAAe;YACf,aAAa,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAE3C,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;;;;;;;;;;;KAC7C;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,KAAK,4BAA4B,CAAC,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/C,QAAQ,GAAG,CAAC,CAAC;IACf,CAAC;IAED,0EAA0E;IAC1E,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACzB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const buildCommand: Command;