@t1mmen/srtd 0.2.2 → 0.4.0

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 (52) hide show
  1. package/README.md +170 -146
  2. package/dist/__tests__/vitest.setup.js +41 -25
  3. package/dist/__tests__/vitest.setup.js.map +1 -1
  4. package/dist/__tests__/watch.test.js +0 -1
  5. package/dist/__tests__/watch.test.js.map +1 -1
  6. package/dist/cli.js +3 -0
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/_app.js +31 -3
  9. package/dist/commands/_app.js.map +1 -1
  10. package/dist/commands/apply.d.ts +13 -1
  11. package/dist/commands/apply.js +19 -6
  12. package/dist/commands/apply.js.map +1 -1
  13. package/dist/commands/build.d.ts +13 -1
  14. package/dist/commands/build.js +14 -4
  15. package/dist/commands/build.js.map +1 -1
  16. package/dist/commands/clear.d.ts +2 -0
  17. package/dist/commands/clear.js +45 -0
  18. package/dist/commands/clear.js.map +1 -0
  19. package/dist/commands/index.js +25 -10
  20. package/dist/commands/index.js.map +1 -1
  21. package/dist/commands/register.js +9 -6
  22. package/dist/commands/register.js.map +1 -1
  23. package/dist/commands/watch.js +121 -117
  24. package/dist/commands/watch.js.map +1 -1
  25. package/dist/components/Branding.js +5 -4
  26. package/dist/components/Branding.js.map +1 -1
  27. package/dist/components/Quittable.d.ts +6 -0
  28. package/dist/components/Quittable.js +36 -0
  29. package/dist/components/Quittable.js.map +1 -0
  30. package/dist/hooks/useDatabaseConnection.d.ts +7 -0
  31. package/dist/hooks/useDatabaseConnection.js +68 -0
  32. package/dist/hooks/useDatabaseConnection.js.map +1 -0
  33. package/dist/hooks/useTemplateManager.d.ts +22 -0
  34. package/dist/hooks/useTemplateManager.js +124 -0
  35. package/dist/hooks/useTemplateManager.js.map +1 -0
  36. package/dist/lib/templateManager.d.ts +12 -5
  37. package/dist/lib/templateManager.js +166 -60
  38. package/dist/lib/templateManager.js.map +1 -1
  39. package/dist/lib/templateManager.test.js +452 -15
  40. package/dist/lib/templateManager.test.js.map +1 -1
  41. package/dist/types.d.ts +2 -1
  42. package/dist/utils/config.d.ts +2 -0
  43. package/dist/utils/config.js +27 -40
  44. package/dist/utils/config.js.map +1 -1
  45. package/dist/utils/databaseConnection.d.ts +5 -1
  46. package/dist/utils/databaseConnection.js +40 -9
  47. package/dist/utils/databaseConnection.js.map +1 -1
  48. package/dist/utils/logger.d.ts +1 -0
  49. package/package.json +12 -7
  50. package/dist/commands/help.d.ts +0 -1
  51. package/dist/commands/help.js +0 -2
  52. package/dist/commands/help.js.map +0 -1
package/README.md CHANGED
@@ -1,17 +1,27 @@
1
1
  # `srtd` 🪄 Supabase Repeatable Template Definitions
2
2
 
3
- Live-reloading SQL templates for [Supabase](https://supabase.com) projects. DX supercharged! 🚀
4
3
 
5
4
 
6
- `srtd` enhances the [Supabase](https://supabase.com) development workflow by adding live-reloading SQL templates, and a single-source-of-truth template migrations system for your database functions, RLS policies, etc. Drastically simplify code reviews 💪
5
+ > Live-reloading SQL templates for [Supabase](https://supabase.com) projects. DX supercharged! 🚀
7
6
 
8
- Built specifically for projects using the standard [Supabase](https://supabase.com) stack (but probably works alright for other Postgres-based projects, too).
7
+ [![npm version](https://badge.fury.io/js/@t1mmen%2Fsrtd.svg)](https://www.npmjs.com/package/@t1mmen/srtd)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ [![CI/CD](https://github.com/t1mmen/srtd/actions/workflows/ci.yml/badge.svg)](https://github.com/t1mmen/srtd/actions/workflows/ci.yml)
10
+ [![codecov](https://codecov.io/gh/t1mmen/srtd/graph/badge.svg?token=CIMAZ55KCJ)](https://codecov.io/gh/t1mmen/srtd)
11
+
12
+
13
+ [![screenshot of srtd](./readme-screenshot.png)](./readme-screenshot.png)
14
+
15
+ `srtd` enhances the [Supabase](https://supabase.com) DX by adding live-reloading SQL templates into local db. The single-source-of-truth template ➡️ migrations system brings sanity to code reviews, making `git blame` useful.
16
+
17
+
18
+ **Read the introductory blog post: [Introducing `srtd`: Live-Reloading SQL Templates for Supabase](https://timm.stokke.me/blog/srtd-live-reloading-and-sql-templates-for-supabase)**
9
19
 
10
20
  ## Why This Exists 🤔
11
21
 
12
22
  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:
13
23
 
14
- 1. Code reviews were painful - function changes showed up as complete rewrites rather than helpful diffs
24
+ 1. Code reviews were painful - function changes showed up as complete rewrites, `git blame` was useless
15
25
  2. Designing and iterating on database changes locally meant constant friction, like the dance around copy-pasting into SQL console
16
26
 
17
27
  After over a year of looking-but-not-finding a better way, I paired up with [Claude](https://claude.ai) to eliminate these annoyances. Say hello to `srtd`.
@@ -19,10 +29,12 @@ After over a year of looking-but-not-finding a better way, I paired up with [Cla
19
29
  ## Key Features ✨
20
30
 
21
31
  - **Live Reload**: Changes to your SQL templates instantly update your local database
22
- - **Single Source of Truth**: Templates are the source of all (non-mutable) database objects, making code reviews a breeze
23
- - **Clean Migrations**: Generate standard [Supabase](https://supabase.com) migrations when you're ready to deploy
32
+ - **Single Source of Truth**: Templates are the source of all (non-mutable) database objects, improving code-review clarity
33
+ - **Just SQL**: Templates build as standard [Supabase](https://supabase.com) migrations when you're ready to deploy
24
34
  - **Developer Friendly**: Interactive CLI with visual feedback for all operations
25
35
 
36
+ Built specifically for projects using the standard [Supabase](https://supabase.com) stack (but probably works alright for other Postgres-based projects, too).
37
+
26
38
  ## Requirements
27
39
 
28
40
  - Node.js v20.x or higher
@@ -31,24 +43,29 @@ After over a year of looking-but-not-finding a better way, I paired up with [Cla
31
43
 
32
44
  ## Quick Start 🚀
33
45
 
34
- First, install `srtd` globally or in your project:
46
+ ### Installation
35
47
 
36
48
  ```bash
37
- npm install -g @t1mmen/srtd # Global installation
38
- # or
39
- npm install --save-dev @t1mmen/srtd # Project installation
40
- # or
41
- npx @t1mmen/srtd init # Run directly
49
+ # Global installation
50
+ npm install -g @t1mmen/srtd
51
+
52
+ # Project installation
53
+ npm install --save-dev @t1mmen/srtd
54
+
55
+ # Or run directly
56
+ npx @t1mmen/srtd
42
57
  ```
43
58
 
44
- Then set up in your [Supabase](https://supabase.com) project:
59
+ ### Setup
45
60
 
46
61
  ```bash
47
62
  cd your-supabase-project
48
63
  srtd init
49
64
  ```
50
65
 
51
- Create a template (e.g., `supabase/migrations-templates/my_function.sql`):
66
+ ### Create Your First Template
67
+
68
+ Create `supabase/migrations-templates/my_function.sql`:
52
69
 
53
70
  ```sql
54
71
  CREATE OR REPLACE FUNCTION my_function()
@@ -59,145 +76,153 @@ END;
59
76
  $$ LANGUAGE plpgsql;
60
77
  ```
61
78
 
62
- Start development mode:
79
+ ### Development Workflow
63
80
 
81
+ 1. Start watch mode:
64
82
  ```bash
65
83
  srtd watch # Changes auto-apply to local database
66
84
  ```
67
85
 
68
- When ready to deploy:
69
-
86
+ 2. When ready to deploy:
70
87
  ```bash
71
- srtd build # Creates timestamped migration file
72
- supabase migrate up # Apply using Supabase CLI
88
+ srtd build # Creates timestamped migration file
89
+ supabase migration up # Apply using Supabase CLI
73
90
  ```
74
91
 
75
92
  ## Commands 🎮
76
93
 
77
- Running `srtd` without arguments opens an interactive menu:
94
+ ### Interactive Mode
78
95
 
79
- ```
80
- ❯ 🏗️ build - Build Supabase migrations from templates
81
- ▶️ apply - Apply migration templates directly to database
82
- ✍️ register - Register templates as already built
83
- 👀 watch - Watch templates for changes, apply directly to database
84
- ```
96
+ Running `srtd` without arguments opens an interactive menu:
85
97
 
86
- Or use these commands directly:
98
+ ### CLI Mode
87
99
 
88
- - 🏗️ `build` - Generate [Supabase](https://supabase.com) migrations from templates
89
- - ▶️ `apply` - Apply templates directly to local database
90
- - ✍️ `register [file.sql]` - Mark templates as already built (interactive UI if no file specified)
91
- - 👀 `watch` - Watch templates and apply changes instantly
100
+ - 🏗️ `srtd build [--force]` - Generate migrations from templates
101
+ - ▶️ `srtd apply [--force]` - Apply templates directly to local database
102
+ - ✍️ `srtd register [file.sql]` - Mark templates as already built
103
+ - 👀 `srtd watch` - Watch and auto-apply changes
104
+ - 🧹 `srtd clean` - Remove all logs and reset config
92
105
 
93
- ## Perfect For 🎯
106
+ > [!IMPORTANT]
107
+ > `watch` and `apply` commands modify your local database directly and don't clean up after themselves. Use with caution!
94
108
 
95
- Ideal for [Supabase](https://supabase.com) database objects that need full redefinition:
109
+ ## The Power of Templates 💪
96
110
 
97
- ✅ Functions and stored procedures:
98
- ```sql
99
- CREATE OR REPLACE FUNCTION search_products(query text, category_id uuid DEFAULT NULL)
100
- RETURNS SETOF products AS $$
101
- BEGIN
102
- RETURN QUERY
103
- SELECT p.* FROM products p
104
- LEFT JOIN product_categories pc ON pc.product_id = p.id
105
- WHERE to_tsvector('english',
106
- p.name || ' ' ||
107
- p.description || ' ' ||
108
- p.tags || ' ' ||
109
- COALESCE((
110
- SELECT string_agg(c.name, ' ')
111
- FROM categories c
112
- WHERE c.id = ANY(p.category_ids)
113
- ), '')
114
- ) @@ plainto_tsquery('english', query)
115
- AND (category_id IS NULL OR pc.category_id = category_id);
116
- END;
117
- $$ LANGUAGE plpgsql;
118
- ```
119
111
 
120
- Row-Level Security (RLS) policies:
121
- ```sql
122
- CREATE POLICY "users can view own data"
123
- ON profiles FOR SELECT
124
- USING (auth.uid() = user_id);
125
- ```
112
+ Templates make code reviews meaningful. Consider this PR adding priority to a notification function:
126
113
 
127
- Roles and permissions:
128
- ```sql
129
- CREATE ROLE authenticated;
130
- GRANT USAGE ON SCHEMA public TO authenticated;
131
- GRANT SELECT ON ALL TABLES IN SCHEMA public TO authenticated;
132
- ```
114
+ Without templates, this would appear as a complete rewrite in your PR.
133
115
 
134
- **Not recommended for:**
116
+ ### Perfect For 🎯
135
117
 
136
- * Table structures
137
- * ❌ Indexes
138
- * Data modifications
139
- * Anything that is not fully re-definable, really.
118
+ Database functions:
119
+ ```diff
120
+ -- Event notifications
121
+ CREATE OR REPLACE FUNCTION notify_changes()
122
+ RETURNS trigger AS $$
123
+ BEGIN
124
+ PERFORM pg_notify(
125
+ 'changes',
126
+ json_build_object('table', TG_TABLE_NAME, 'id', NEW.id)::text
127
+ );
128
+ + RAISE NOTICE 'Notified changes for %', TG_TABLE_NAME; -- Debug logging
129
+ RETURN NEW;
130
+ END;
131
+ $$ LANGUAGE plpgsql;
132
+ ```
140
133
 
141
- In these cases, use regular [Supabase](https://supabase.com) migrations.
134
+ Row-Level Security (RLS):
135
+ ```diff
136
+ -- Replace/update policies safely
137
+ DROP POLICY IF EXISTS "workspace_access" ON resources;
138
+ CREATE POLICY "workspace_access" ON resources
139
+ USING (workspace_id IN (
140
+ SELECT id FROM workspaces
141
+ WHERE organization_id = auth.organization_id()
142
+ + AND auth.user_role() NOT IN ('pending')
143
+ ));
144
+ ```
142
145
 
143
- ## The Power of Templates 💪
146
+ Views for data abstraction:
147
+ ```diff
148
+ CREATE OR REPLACE VIEW active_subscriptions AS
149
+ SELECT
150
+ s.*,
151
+ p.name as plan_name,
152
+ p.features
153
+ FROM subscriptions s
154
+ JOIN plans p ON p.id = s.plan_id
155
+ - WHERE s.status = 'active';
156
+ + WHERE s.status = 'active'
157
+ + AND s.expires_at > CURRENT_TIMESTAMP;
158
+ ```
144
159
 
145
- Here's why templates make your life easier. Consider a PR that adds priority to our notification dispatch function.
160
+ Roles and Permissions:
161
+ ```diff
162
+ -- Revoke all first for clean state
163
+ REVOKE ALL ON ALL TABLES IN SCHEMA public FROM public;
146
164
 
147
- With templates, the change is clear and reviewable:
165
+ -- Grant specific access
166
+ GRANT USAGE ON SCHEMA public TO authenticated;
167
+ GRANT SELECT ON ALL TABLES IN SCHEMA public TO authenticated;
168
+ + GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO admin;
169
+ ```
148
170
 
171
+ ✅ Safe Type Extensions:
149
172
  ```diff
150
- CREATE OR REPLACE FUNCTION dispatch_notification(
151
- user_id uuid,
152
- type text,
153
- payload jsonb
154
- ) RETURNS uuid AS $$
155
- DECLARE
156
- notification_id uuid;
157
- user_settings jsonb;
158
- BEGIN
159
- -- Get user notification settings
160
- SELECT settings INTO user_settings
161
- FROM user_preferences
162
- WHERE id = user_id;
163
-
164
- -- Create notification record
165
- + -- Include priority based on notification type
166
- INSERT INTO notifications (
167
- id,
168
- user_id,
169
- type,
170
- payload,
171
- + priority,
172
- created_at
173
- ) VALUES (
174
- gen_random_uuid(),
175
- dispatch_notification.user_id,
176
- type,
177
- payload,
178
- + COALESCE((SELECT priority FROM notification_types WHERE name = type), 'normal'),
179
- CURRENT_TIMESTAMP
180
- )
181
- RETURNING id INTO notification_id;
173
+ DO $$
174
+ BEGIN
175
+ -- Add new enum values idempotently
176
+ IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'notification_type') THEN
177
+ CREATE TYPE notification_type AS ENUM ('email', 'sms');
178
+ END IF;
179
+
180
+ -- Extend existing enum safely
181
+ ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'push';
182
+ ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'pusher';
183
+ ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'webhook';
184
+ + ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'email';
185
+ END $$;
182
186
  ```
183
187
 
184
- Without templates, the same change appears as a complete new file in your PR.
188
+ ### Not Recommended For
189
+
190
+ * ❌ Table structures
191
+ * ❌ Indexes
192
+ * ❌ Data modifications
193
+ * ❌ Non-idempotent operations
194
+
195
+ Use regular [Supabase](https://supabase.com) migrations for these cases.
185
196
 
186
197
  ## Configuration 📝
187
198
 
188
- During initialization, `srtd` creates a `srtd.config.json`:
199
+ `srtd.config.json` created during initialization:
189
200
 
190
- ```json
201
+ ```jsonc
191
202
  {
203
+ // Prevents building templates with this extension
192
204
  "wipIndicator": ".wip",
205
+
206
+ // Migration file naming: 20211001000000_srtd-my_function.sql
207
+ "migrationPrefix": "srtd",
208
+
209
+ // Template discovery
193
210
  "filter": "**/*.sql",
211
+
212
+ // Migration file comments
194
213
  "banner": "You very likely **DO NOT** want to manually edit this generated file.",
195
214
  "footer": "",
215
+
216
+ // Wrap migrations in transaction
196
217
  "wrapInTransaction": true,
218
+
219
+ // File paths
197
220
  "templateDir": "supabase/migrations-templates",
198
221
  "migrationDir": "supabase/migrations",
199
222
  "buildLog": "supabase/migrations-templates/.buildlog.json",
200
223
  "localBuildLog": "supabase/migrations-templates/.buildlog.local.json",
224
+
225
+ // Database connection
201
226
  "pgConnection": "postgresql://postgres:postgres@localhost:54322/postgres"
202
227
  }
203
228
  ```
@@ -206,74 +231,73 @@ During initialization, `srtd` creates a `srtd.config.json`:
206
231
 
207
232
  ### Work in Progress Templates
208
233
 
209
- Add `.wip.sql` extension to templates under development to prevent accidental migration generation:
210
-
234
+ Add `.wip.sql` extension to prevent migration generation:
211
235
  ```bash
212
- my_function.wip.sql # Won't generate migrations during build
236
+ my_function.wip.sql # Only applied locally, never built
213
237
  ```
214
238
 
215
239
  ### Template State Management
216
240
 
217
- `srtd` maintains two logs:
218
-
219
- - `.buildlog.json` - Tracks which templates have been built into migrations (commit this)
220
- - `.buildlog.local.json` - Tracks local database state (add to .gitignore)
241
+ Two state tracking files:
242
+ - `.buildlog.json` - Migration build state (commit this)
243
+ - `.buildlog.local.json` - Local database state (add to `.gitignore`)
221
244
 
222
245
  ### Register Existing Objects
223
246
 
224
- Import existing database objects into the template system:
225
-
247
+ Import existing database objects:
226
248
  ```bash
227
- srtd register my_function.sql # Won't generate new migration until changed
228
- # or
229
- srtd register # Opens interactive UI for selecting multiple templates
249
+ # Register specific template
250
+ srtd register my_function.sql
251
+
252
+ # Interactive multi-select UI
253
+ srtd register
230
254
  ```
231
255
 
232
256
  ## Development 🛠️
233
257
 
234
- This project uses TypeScript and modern Node.js features. To contribute:
258
+ ### Local Setup
235
259
 
236
- 1. Set up the development environment:
237
260
  ```bash
261
+ # Clone and install
238
262
  git clone https://github.com/stokke/srtd.git
239
263
  cd srtd
240
264
  npm install
241
- ```
242
265
 
243
- 2. Start development:
244
- ```bash
245
- npm run dev # Watches for changes
246
- npm test # Runs tests
247
- npm start # Builds, links, and runs CLI
248
- ```
266
+ # Development
267
+ npm run dev # Watch mode
268
+ npm test # Run tests
269
+ npm start # Build, link, run
249
270
 
250
- 3. Other useful commands:
251
- ```bash
252
- npm run typecheck # Type checking
253
- npm run lint # Lint and fix
254
- npm run test:coverage # Test coverage
271
+ # Quality Checks
272
+ npm run typecheck # Type checking
273
+ npm run lint # Lint and fix
274
+ npm run test:coverage # Test coverage
255
275
  ```
256
276
 
257
277
  ## Contributing 🤝
258
278
 
259
- This tool was built to solve specific problems in our [Supabase](https://supabase.com) development workflow. While it's considered feature-complete for our needs, we welcome improvements through pull requests, especially for:
279
+ While feature-complete for our needs, we welcome:
260
280
 
261
- - Bug fixes and reliability improvements
262
- - Documentation improvements and examples
263
- - Test coverage
264
- - Performance optimizations
281
+ - 🐛 Bug fixes and reliability improvements
282
+ - 📚 Documentation improvements
283
+ - Test coverage enhancements
284
+ - ⚡️ Performance optimizations
265
285
 
266
- ### Contribution Guidelines
286
+ ### Contribution Process
267
287
 
268
288
  1. Create a [changeset](https://github.com/changesets/changesets) (`npm run changeset`)
269
289
  2. Ensure tests pass (`npm test`)
270
290
  3. Follow existing code style
271
- 4. Update documentation as needed
291
+ 4. Update documentation
272
292
 
273
- Note that new features may or may not be accepted depending on whether they align with the project's focused scope. However, improvements to existing functionality, documentation, and tests are always welcome!
293
+ Note: New features are evaluated based on alignment with project scope.
274
294
 
275
295
  ## License
276
296
 
277
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
297
+ MIT License - see [LICENSE](LICENSE) file.
298
+
299
+ ---
278
300
 
279
301
  Made with 🪄 by [Timm Stokke](https://timm.stokke.me) & [Claude Sonnet](https://claude.ai)
302
+
303
+ [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/t1mmen)
@@ -5,19 +5,35 @@ import { afterAll, beforeAll, vi } from 'vitest';
5
5
  import { connect, disconnect } from '../utils/databaseConnection.js';
6
6
  export const TEST_FN_PREFIX = 'srtd_scoped_test_func_';
7
7
  export const TEST_ROOT = join(tmpdir(), `srtd-test-${Date.now()}`);
8
- if (process.env.CI) {
9
- let consoleLogMock;
10
- beforeAll(() => {
11
- consoleLogMock = vi.spyOn(console, 'log').mockImplementation(() => {
12
- // Do nothing
13
- });
14
- });
15
- afterAll(() => {
16
- consoleLogMock.mockRestore();
17
- });
18
- }
8
+ vi.mock('../utils/logger', () => ({
9
+ logger: {
10
+ info: () => {
11
+ /** noop */
12
+ },
13
+ success: () => {
14
+ /** noop */
15
+ },
16
+ warn: () => {
17
+ /** noop */
18
+ },
19
+ error: () => {
20
+ /** noop */
21
+ },
22
+ skip: () => {
23
+ /** noop */
24
+ },
25
+ debug: () => {
26
+ /** noop */
27
+ },
28
+ },
29
+ }));
19
30
  beforeAll(async () => {
20
- await fs.mkdir(TEST_ROOT, { recursive: true });
31
+ try {
32
+ await fs.mkdir(TEST_ROOT, { recursive: true });
33
+ }
34
+ catch (error) {
35
+ console.error('Error creating test root:', error, ', retrying once.');
36
+ }
21
37
  });
22
38
  afterAll(async () => {
23
39
  await fs.rm(TEST_ROOT, { recursive: true, force: true });
@@ -26,19 +42,19 @@ afterAll(async () => {
26
42
  try {
27
43
  await client.query('BEGIN');
28
44
  await client.query(`
29
- DO $$
30
- DECLARE
31
- r record;
32
- BEGIN
33
- FOR r IN
34
- SELECT quote_ident(proname) AS func_name
35
- FROM pg_proc
36
- WHERE proname LIKE '${TEST_FN_PREFIX}%'
37
- LOOP
38
- EXECUTE 'DROP FUNCTION IF EXISTS ' || r.func_name;
39
- END LOOP;
40
- END;
41
- $$
45
+ DO $$
46
+ DECLARE
47
+ r record;
48
+ BEGIN
49
+ FOR r IN
50
+ SELECT quote_ident(proname) AS func_name
51
+ FROM pg_proc
52
+ WHERE proname LIKE '${TEST_FN_PREFIX}%'
53
+ LOOP
54
+ EXECUTE 'DROP FUNCTION IF EXISTS ' || r.func_name;
55
+ END LOOP;
56
+ END;
57
+ $$;
42
58
  `);
43
59
  await client.query('COMMIT');
44
60
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.setup.js","sourceRoot":"","sources":["../../src/__tests__/vitest.setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAErE,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AACvD,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,aAAa,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AAEnE,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;IACnB,IAAI,cAA2C,CAAC;IAEhD,SAAS,CAAC,GAAG,EAAE;QACb,cAAc,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE;YAChE,aAAa;QACf,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,GAAG,EAAE;QACZ,cAAc,CAAC,WAAW,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;IAClB,MAAM,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAEzD,uDAAuD;IACvD,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,MAAM,MAAM,CAAC,KAAK,CAAC;;;;;;;;gCAQS,cAAc;;;;;;KAMzC,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC/B,MAAM,CAAC,CAAC;IACV,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC;IAED,UAAU,EAAE,CAAC;AACf,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAC,cAAc,EAAC,EAAE;IAChD,MAAM,MAAM,GAAG,CAAC,MAAM,cAAc,EAAE,CAAwC,CAAC;IAC/E,OAAO;QACL,GAAG,MAAM;QACT,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC;YACnC,YAAY,EAAE,MAAM;YACpB,MAAM,EAAE,UAAU;YAClB,WAAW,EAAE,gBAAgB;YAC7B,YAAY,EAAE,iBAAiB;YAC/B,QAAQ,EAAE,qBAAqB;YAC/B,aAAa,EAAE,2BAA2B;YAC1C,YAAY,EACV,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,yDAAyD;YACvF,MAAM,EAAE,aAAa;YACrB,MAAM,EAAE,aAAa;YACrB,iBAAiB,EAAE,IAAI;SACxB,CAAC;KACH,CAAC;AACJ,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"vitest.setup.js","sourceRoot":"","sources":["../../src/__tests__/vitest.setup.ts"],"names":[],"mappings":"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"}
@@ -18,7 +18,6 @@ describe('Watch Command', () => {
18
18
  await new Promise(resolve => setTimeout(resolve, 100));
19
19
  const output = lastFrame();
20
20
  expect(output).toContain('Watch Mode');
21
- expect(output).toContain('Watching for template changes');
22
21
  });
23
22
  });
24
23
  //# sourceMappingURL=watch.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"watch.test.js","sourceRoot":"","sources":["../../src/__tests__/watch.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAChE,OAAO,KAAK,MAAM,sBAAsB,CAAC;AAEzC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAC,cAAc,EAAC,EAAE;IACpC,MAAM,MAAM,GAAG,CAAC,MAAM,cAAc,EAAE,CAAyB,CAAC;IAChE,OAAO;QACL,GAAG,MAAM;QACT,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;KAClC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,oBAAC,KAAK,OAAG,CAAC,CAAC;QACxC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;QAEvD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"watch.test.js","sourceRoot":"","sources":["../../src/__tests__/watch.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAChE,OAAO,KAAK,MAAM,sBAAsB,CAAC;AAEzC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAC,cAAc,EAAC,EAAE;IACpC,MAAM,MAAM,GAAG,CAAC,MAAM,cAAc,EAAE,CAAyB,CAAC;IAChE,OAAO;QACL,GAAG,MAAM;QACT,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;KAClC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,oBAAC,KAAK,OAAG,CAAC,CAAC;QACxC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;QAEvD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/dist/cli.js CHANGED
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env -S node --no-warnings
2
2
  import Pastel from 'pastel';
3
+ import updateNotifier from 'update-notifier';
4
+ import packageJson from '../package.json' assert { type: 'json' };
5
+ updateNotifier({ pkg: packageJson }).notify();
3
6
  const app = new Pastel({
4
7
  importMeta: import.meta,
5
8
  });
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":";AACA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,IAAI;CACxB,CAAC,CAAC;AAEH,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":";AACA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,cAAc,MAAM,iBAAiB,CAAC;AAC7C,OAAO,WAAW,MAAM,iBAAiB,CAAC,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;AAElE,cAAc,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AAE9C,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,IAAI;CACxB,CAAC,CAAC;AAEH,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC"}
@@ -1,7 +1,35 @@
1
- import { Box } from 'ink';
1
+ import { Alert, ThemeProvider, defaultTheme, extendTheme } from '@inkjs/ui';
2
+ import { Box, Static, Text } from 'ink';
2
3
  import React from 'react';
4
+ import { useDatabaseConnection } from '../hooks/useDatabaseConnection.js';
5
+ const customTheme = extendTheme(defaultTheme, {
6
+ components: {
7
+ Spinner: {
8
+ styles: {
9
+ frame: () => ({
10
+ color: 'magenta',
11
+ }),
12
+ },
13
+ },
14
+ },
15
+ });
3
16
  export default function App({ Component, commandProps }) {
4
- return (React.createElement(Box, { flexDirection: "column" },
5
- React.createElement(Component, { ...commandProps })));
17
+ const { error } = useDatabaseConnection();
18
+ return (React.createElement(ThemeProvider, { theme: customTheme },
19
+ React.createElement(Box, { flexDirection: "column", padding: 1 },
20
+ !!error && (React.createElement(Static, { items: [error] }, error => (React.createElement(Box, { key: error },
21
+ React.createElement(Alert, { variant: "error" },
22
+ React.createElement(Text, { bold: true, color: "red" },
23
+ "Error:",
24
+ ' '),
25
+ error))))),
26
+ React.createElement(Component, { ...commandProps }))));
6
27
  }
28
+ // Mimick fullscreen behavior
29
+ const enterAltScreenCommand = '\x1b[?1049h';
30
+ const leaveAltScreenCommand = '\x1b[?1049l';
31
+ process.stdout.write(enterAltScreenCommand);
32
+ process.on('exit', () => {
33
+ process.stdout.write(leaveAltScreenCommand);
34
+ });
7
35
  //# sourceMappingURL=_app.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_app.js","sourceRoot":"","sources":["../../src/commands/_app.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAE1B,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,EAAE,SAAS,EAAE,YAAY,EAAY;IAC/D,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ;QACzB,oBAAC,SAAS,OAAK,YAAY,GAAI,CAC3B,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"_app.js","sourceRoot":"","sources":["../../src/commands/_app.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAkB,MAAM,KAAK,CAAC;AAExD,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAE1E,MAAM,WAAW,GAAG,WAAW,CAAC,YAAY,EAAE;IAC5C,UAAU,EAAE;QACV,OAAO,EAAE;YACP,MAAM,EAAE;gBACN,KAAK,EAAE,GAAc,EAAE,CAAC,CAAC;oBACvB,KAAK,EAAE,SAAS;iBACjB,CAAC;aACH;SACF;KACF;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,EAAE,SAAS,EAAE,YAAY,EAAY;IAC/D,MAAM,EAAE,KAAK,EAAE,GAAG,qBAAqB,EAAE,CAAC;IAE1C,OAAO,CACL,oBAAC,aAAa,IAAC,KAAK,EAAE,WAAW;QAC/B,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,OAAO,EAAE,CAAC;YACnC,CAAC,CAAC,KAAK,IAAI,CACV,oBAAC,MAAM,IAAC,KAAK,EAAE,CAAC,KAAK,CAAC,IACnB,KAAK,CAAC,EAAE,CAAC,CACR,oBAAC,GAAG,IAAC,GAAG,EAAE,KAAK;gBACb,oBAAC,KAAK,IAAC,OAAO,EAAC,OAAO;oBACpB,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,KAAK;;wBACb,GAAG,CACL;oBACN,KAAK,CACA,CACJ,CACP,CACM,CACV;YACD,oBAAC,SAAS,OAAK,YAAY,GAAI,CAC3B,CACQ,CACjB,CAAC;AACJ,CAAC;AAED,6BAA6B;AAC7B,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAC5C,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;AAC5C,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;IACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC"}
@@ -1 +1,13 @@
1
- export default function Apply(): null;
1
+ import zod from 'zod';
2
+ export declare const options: zod.ZodObject<{
3
+ force: zod.ZodBoolean;
4
+ }, "strip", zod.ZodTypeAny, {
5
+ force: boolean;
6
+ }, {
7
+ force: boolean;
8
+ }>;
9
+ type Props = {
10
+ options: zod.infer<typeof options>;
11
+ };
12
+ export default function Apply({ options }: Props): null;
13
+ export {};