@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.
- package/LICENSE +21 -0
- package/README.md +363 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +50 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/apply.d.ts +2 -0
- package/dist/commands/apply.js +105 -0
- package/dist/commands/apply.js.map +1 -0
- package/dist/commands/build.d.ts +2 -0
- package/dist/commands/build.js +134 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/clear.d.ts +2 -0
- package/dist/commands/clear.js +161 -0
- package/dist/commands/clear.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +91 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/menu.d.ts +4 -0
- package/dist/commands/menu.js +76 -0
- package/dist/commands/menu.js.map +1 -0
- package/dist/commands/promote.d.ts +2 -0
- package/dist/commands/promote.js +181 -0
- package/dist/commands/promote.js.map +1 -0
- package/dist/commands/register.d.ts +2 -0
- package/dist/commands/register.js +192 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/watch.d.ts +14 -0
- package/dist/commands/watch.js +190 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -0
- package/dist/services/DatabaseService.d.ts +113 -0
- package/dist/services/DatabaseService.js +343 -0
- package/dist/services/DatabaseService.js.map +1 -0
- package/dist/services/FileSystemService.d.ts +100 -0
- package/dist/services/FileSystemService.js +237 -0
- package/dist/services/FileSystemService.js.map +1 -0
- package/dist/services/MigrationBuilder.d.ts +106 -0
- package/dist/services/MigrationBuilder.js +193 -0
- package/dist/services/MigrationBuilder.js.map +1 -0
- package/dist/services/Orchestrator.d.ts +155 -0
- package/dist/services/Orchestrator.js +622 -0
- package/dist/services/Orchestrator.js.map +1 -0
- package/dist/services/StateService.d.ts +169 -0
- package/dist/services/StateService.js +463 -0
- package/dist/services/StateService.js.map +1 -0
- package/dist/types.d.ts +48 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/badge.d.ts +14 -0
- package/dist/ui/badge.js +28 -0
- package/dist/ui/badge.js.map +1 -0
- package/dist/ui/branding.d.ts +9 -0
- package/dist/ui/branding.js +35 -0
- package/dist/ui/branding.js.map +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +5 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/results.d.ts +10 -0
- package/dist/ui/results.js +62 -0
- package/dist/ui/results.js.map +1 -0
- package/dist/ui/spinner.d.ts +5 -0
- package/dist/ui/spinner.js +8 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/utils/config.d.ts +12 -0
- package/dist/utils/config.js +67 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/createEmptyBuildLog.d.ts +1 -0
- package/dist/utils/createEmptyBuildLog.js +10 -0
- package/dist/utils/createEmptyBuildLog.js.map +1 -0
- package/dist/utils/ensureDirectories.d.ts +4 -0
- package/dist/utils/ensureDirectories.js +23 -0
- package/dist/utils/ensureDirectories.js.map +1 -0
- package/dist/utils/fileExists.d.ts +1 -0
- package/dist/utils/fileExists.js +11 -0
- package/dist/utils/fileExists.js.map +1 -0
- package/dist/utils/findProjectRoot.d.ts +1 -0
- package/dist/utils/findProjectRoot.js +25 -0
- package/dist/utils/findProjectRoot.js.map +1 -0
- package/dist/utils/getErrorMessage.d.ts +9 -0
- package/dist/utils/getErrorMessage.js +14 -0
- package/dist/utils/getErrorMessage.js.map +1 -0
- package/dist/utils/getNextTimestamp.d.ts +2 -0
- package/dist/utils/getNextTimestamp.js +12 -0
- package/dist/utils/getNextTimestamp.js.map +1 -0
- package/dist/utils/isWipTemplate.d.ts +1 -0
- package/dist/utils/isWipTemplate.js +6 -0
- package/dist/utils/isWipTemplate.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.js +12 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/safeCreate.d.ts +1 -0
- package/dist/utils/safeCreate.js +16 -0
- package/dist/utils/safeCreate.js.map +1 -0
- 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
|
+
[](https://www.npmjs.com/package/@t1mmen/srtd)
|
|
8
|
+
[](https://www.npmjs.com/package/@t1mmen/srtd)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
[](https://github.com/t1mmen/srtd/actions/workflows/ci.yml)
|
|
11
|
+
[](https://codecov.io/gh/t1mmen/srtd)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
[](./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
|
+
[](./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
|
+
[](https://www.buymeacoffee.com/t1mmen)
|
package/dist/cli.d.ts
ADDED
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
|
package/dist/cli.js.map
ADDED
|
@@ -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,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"}
|