@t1mmen/srtd 0.2.1 → 0.3.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 (44) hide show
  1. package/README.md +148 -87
  2. package/dist/__tests__/watch.test.js +0 -1
  3. package/dist/__tests__/watch.test.js.map +1 -1
  4. package/dist/cli.js +3 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/_app.js +22 -2
  7. package/dist/commands/_app.js.map +1 -1
  8. package/dist/commands/apply.d.ts +13 -1
  9. package/dist/commands/apply.js +18 -6
  10. package/dist/commands/apply.js.map +1 -1
  11. package/dist/commands/build.d.ts +13 -1
  12. package/dist/commands/build.js +14 -4
  13. package/dist/commands/build.js.map +1 -1
  14. package/dist/commands/index.js +20 -10
  15. package/dist/commands/index.js.map +1 -1
  16. package/dist/commands/register.js +9 -6
  17. package/dist/commands/register.js.map +1 -1
  18. package/dist/commands/watch.js +105 -117
  19. package/dist/commands/watch.js.map +1 -1
  20. package/dist/components/Branding.js +5 -4
  21. package/dist/components/Branding.js.map +1 -1
  22. package/dist/components/Quittable.d.ts +6 -0
  23. package/dist/components/Quittable.js +36 -0
  24. package/dist/components/Quittable.js.map +1 -0
  25. package/dist/hooks/useDatabaseConnection.d.ts +7 -0
  26. package/dist/hooks/useDatabaseConnection.js +68 -0
  27. package/dist/hooks/useDatabaseConnection.js.map +1 -0
  28. package/dist/hooks/useTemplateManager.d.ts +24 -0
  29. package/dist/hooks/useTemplateManager.js +141 -0
  30. package/dist/hooks/useTemplateManager.js.map +1 -0
  31. package/dist/lib/templateManager.d.ts +3 -3
  32. package/dist/lib/templateManager.js +24 -17
  33. package/dist/lib/templateManager.js.map +1 -1
  34. package/dist/types.d.ts +2 -1
  35. package/dist/utils/config.js +1 -0
  36. package/dist/utils/config.js.map +1 -1
  37. package/dist/utils/databaseConnection.d.ts +5 -1
  38. package/dist/utils/databaseConnection.js +27 -8
  39. package/dist/utils/databaseConnection.js.map +1 -1
  40. package/dist/utils/logger.d.ts +1 -0
  41. package/package.json +13 -8
  42. package/dist/commands/help.d.ts +0 -1
  43. package/dist/commands/help.js +0 -2
  44. package/dist/commands/help.js.map +0 -1
package/README.md CHANGED
@@ -2,11 +2,17 @@
2
2
 
3
3
  Live-reloading SQL templates for [Supabase](https://supabase.com) projects. DX supercharged! 🚀
4
4
 
5
+ [![npm version](https://badge.fury.io/js/@t1mmen%2Fsrtd.svg)](https://www.npmjs.com/package/@t1mmen/srtd)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![CI/CD](https://github.com/t1mmen/srtd/actions/workflows/ci.yml/badge.svg)](https://github.com/t1mmen/srtd/actions/workflows/ci.yml)
8
+ [![codecov](https://codecov.io/gh/t1mmen/srtd/graph/badge.svg?token=CIMAZ55KCJ)](https://codecov.io/gh/t1mmen/srtd)
5
9
 
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 💪
10
+ `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.
7
11
 
8
12
  Built specifically for projects using the standard [Supabase](https://supabase.com) stack (but probably works alright for other Postgres-based projects, too).
9
13
 
14
+ **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)**
15
+
10
16
  ## Why This Exists 🤔
11
17
 
12
18
  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:
@@ -19,8 +25,8 @@ After over a year of looking-but-not-finding a better way, I paired up with [Cla
19
25
  ## Key Features ✨
20
26
 
21
27
  - **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
28
+ - **Single Source of Truth**: Templates are the source of all (non-mutable) database objects, improving code-review clarity
29
+ - **Just SQL**: Templates build as standard [Supabase](https://supabase.com) migrations when you're ready to deploy
24
30
  - **Developer Friendly**: Interactive CLI with visual feedback for all operations
25
31
 
26
32
  ## Requirements
@@ -31,22 +37,29 @@ After over a year of looking-but-not-finding a better way, I paired up with [Cla
31
37
 
32
38
  ## Quick Start 🚀
33
39
 
34
- First, install `srtd` globally or in your project:
40
+ ### Installation
35
41
 
36
42
  ```bash
37
- npm install -g srtd # Global installation
38
- # or
39
- npm install --save-dev srtd # Project installation
43
+ # Global installation
44
+ npm install -g @t1mmen/srtd
45
+
46
+ # Project installation
47
+ npm install --save-dev @t1mmen/srtd
48
+
49
+ # Or run directly
50
+ npx @t1mmen/srtd
40
51
  ```
41
52
 
42
- Then set up in your [Supabase](https://supabase.com) project:
53
+ ### Setup
43
54
 
44
55
  ```bash
45
56
  cd your-supabase-project
46
57
  srtd init
47
58
  ```
48
59
 
49
- Create a template (e.g., `supabase/migrations-templates/my_function.sql`):
60
+ ### Create Your First Template
61
+
62
+ Create `supabase/migrations-templates/my_function.sql`:
50
63
 
51
64
  ```sql
52
65
  CREATE OR REPLACE FUNCTION my_function()
@@ -57,21 +70,23 @@ END;
57
70
  $$ LANGUAGE plpgsql;
58
71
  ```
59
72
 
60
- Start development mode:
73
+ ### Development Workflow
61
74
 
75
+ 1. Start watch mode:
62
76
  ```bash
63
77
  srtd watch # Changes auto-apply to local database
64
78
  ```
65
79
 
66
- When ready to deploy:
67
-
80
+ 2. When ready to deploy:
68
81
  ```bash
69
- srtd build # Creates timestamped migration file
70
- supabase migrate up # Apply using Supabase CLI
82
+ srtd build # Creates timestamped migration file
83
+ supabase migration up # Apply using Supabase CLI
71
84
  ```
72
85
 
73
86
  ## Commands 🎮
74
87
 
88
+ ### Interactive Mode
89
+
75
90
  Running `srtd` without arguments opens an interactive menu:
76
91
 
77
92
  ```
@@ -81,68 +96,101 @@ Running `srtd` without arguments opens an interactive menu:
81
96
  👀 watch - Watch templates for changes, apply directly to database
82
97
  ```
83
98
 
84
- Or use these commands directly:
99
+ ### CLI Mode
85
100
 
86
- - 🏗️ `build` - Generate [Supabase](https://supabase.com) migrations from templates
87
- - ▶️ `apply` - Apply templates directly to local database
88
- - ✍️ `register [file.sql]` - Mark templates as already built (interactive UI if no file specified)
89
- - 👀 `watch` - Watch templates and apply changes instantly
101
+ - 🏗️ `build [--force]` - Generate migrations from templates
102
+ - ▶️ `apply [--force]` - Apply templates directly to local database
103
+ - ✍️ `register [file.sql]` - Mark templates as already built
104
+ - 👀 `watch` - Watch and auto-apply changes
105
+
106
+ > [!IMPORTANT]
107
+ > `watch` and `apply` commands modify your local database directly and don't clean up after themselves. Use with caution!
90
108
 
91
109
  ## Perfect For 🎯
92
110
 
93
- Ideal for [Supabase](https://supabase.com) database objects that need full redefinition:
111
+ ### Ideal Use Cases
94
112
 
95
- Functions and stored procedures:
113
+ Database functions:
96
114
  ```sql
97
- CREATE OR REPLACE FUNCTION search_products(query text, category_id uuid DEFAULT NULL)
98
- RETURNS SETOF products AS $$
115
+ -- Reusable auth helper
116
+ CREATE OR REPLACE FUNCTION auth.user_id()
117
+ RETURNS uuid AS $$
118
+ SELECT auth.uid()::uuid;
119
+ $$ LANGUAGE sql SECURITY DEFINER;
120
+
121
+ -- Event notifications
122
+ CREATE OR REPLACE FUNCTION notify_changes()
123
+ RETURNS trigger AS $$
99
124
  BEGIN
100
- RETURN QUERY
101
- SELECT p.* FROM products p
102
- LEFT JOIN product_categories pc ON pc.product_id = p.id
103
- WHERE to_tsvector('english',
104
- p.name || ' ' ||
105
- p.description || ' ' ||
106
- p.tags || ' ' ||
107
- COALESCE((
108
- SELECT string_agg(c.name, ' ')
109
- FROM categories c
110
- WHERE c.id = ANY(p.category_ids)
111
- ), '')
112
- ) @@ plainto_tsquery('english', query)
113
- AND (category_id IS NULL OR pc.category_id = category_id);
125
+ PERFORM pg_notify(
126
+ 'changes',
127
+ json_build_object('table', TG_TABLE_NAME, 'id', NEW.id)::text
128
+ );
129
+ RETURN NEW;
114
130
  END;
115
131
  $$ LANGUAGE plpgsql;
116
132
  ```
117
133
 
118
- ✅ Row-Level Security (RLS) policies:
134
+ ✅ Row-Level Security (RLS):
135
+ ```sql
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
+ ));
143
+ ```
144
+
145
+ ✅ Views for data abstraction:
119
146
  ```sql
120
- CREATE POLICY "users can view own data"
121
- ON profiles FOR SELECT
122
- USING (auth.uid() = user_id);
147
+ CREATE OR REPLACE VIEW active_subscriptions AS
148
+ SELECT
149
+ s.*,
150
+ p.name as plan_name,
151
+ p.features
152
+ FROM subscriptions s
153
+ JOIN plans p ON p.id = s.plan_id
154
+ WHERE s.status = 'active'
155
+ AND s.expires_at > CURRENT_TIMESTAMP;
123
156
  ```
124
157
 
125
- ✅ Roles and permissions:
158
+ ✅ Roles and Permissions:
126
159
  ```sql
127
- CREATE ROLE authenticated;
160
+ -- Revoke all first for clean state
161
+ REVOKE ALL ON ALL TABLES IN SCHEMA public FROM public;
162
+
163
+ -- Grant specific access
128
164
  GRANT USAGE ON SCHEMA public TO authenticated;
129
165
  GRANT SELECT ON ALL TABLES IN SCHEMA public TO authenticated;
130
166
  ```
131
167
 
132
- **Not recommended for:**
168
+ Safe Type Extensions:
169
+ ```sql
170
+ DO $$
171
+ BEGIN
172
+ -- Add new enum values idempotently
173
+ IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'notification_type') THEN
174
+ CREATE TYPE notification_type AS ENUM ('email', 'sms');
175
+ END IF;
176
+
177
+ -- Extend existing enum safely
178
+ ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'push';
179
+ END $$;
180
+ ```
181
+
182
+ ### Not Recommended For
133
183
 
134
184
  * ❌ Table structures
135
185
  * ❌ Indexes
136
186
  * ❌ Data modifications
137
- * ❌ Anything that is not fully re-definable, really.
187
+ * ❌ Non-idempotent operations
138
188
 
139
- In these cases, use regular [Supabase](https://supabase.com) migrations.
189
+ Use regular [Supabase](https://supabase.com) migrations for these cases.
140
190
 
141
191
  ## The Power of Templates 💪
142
192
 
143
- Here's why templates make your life easier. Consider a PR that adds priority to our notification dispatch function.
144
-
145
- With templates, the change is clear and reviewable:
193
+ Templates make code reviews meaningful. Consider this PR adding priority to a notification function:
146
194
 
147
195
  ```diff
148
196
  CREATE OR REPLACE FUNCTION dispatch_notification(
@@ -179,23 +227,37 @@ CREATE OR REPLACE FUNCTION dispatch_notification(
179
227
  RETURNING id INTO notification_id;
180
228
  ```
181
229
 
182
- Without templates, the same change appears as a complete new file in your PR.
230
+ Without templates, this would appear as a complete rewrite in your PR.
183
231
 
184
232
  ## Configuration 📝
185
233
 
186
- During initialization, `srtd` creates a `srtd.config.json`:
234
+ `srtd.config.json` created during initialization:
187
235
 
188
- ```json
236
+ ```jsonc
189
237
  {
238
+ // Prevents building templates with this extension
190
239
  "wipIndicator": ".wip",
240
+
241
+ // Migration file naming: 20211001000000_srtd-my_function.sql
242
+ "migrationPrefix": "srtd",
243
+
244
+ // Template discovery
191
245
  "filter": "**/*.sql",
246
+
247
+ // Migration file comments
192
248
  "banner": "You very likely **DO NOT** want to manually edit this generated file.",
193
249
  "footer": "",
250
+
251
+ // Wrap migrations in transaction
194
252
  "wrapInTransaction": true,
253
+
254
+ // File paths
195
255
  "templateDir": "supabase/migrations-templates",
196
256
  "migrationDir": "supabase/migrations",
197
257
  "buildLog": "supabase/migrations-templates/.buildlog.json",
198
258
  "localBuildLog": "supabase/migrations-templates/.buildlog.local.json",
259
+
260
+ // Database connection
199
261
  "pgConnection": "postgresql://postgres:postgres@localhost:54322/postgres"
200
262
  }
201
263
  ```
@@ -204,74 +266,73 @@ During initialization, `srtd` creates a `srtd.config.json`:
204
266
 
205
267
  ### Work in Progress Templates
206
268
 
207
- Add `.wip.sql` extension to templates under development to prevent accidental migration generation:
208
-
269
+ Add `.wip.sql` extension to prevent migration generation:
209
270
  ```bash
210
- my_function.wip.sql # Won't generate migrations during build
271
+ my_function.wip.sql # Only applied locally, never built
211
272
  ```
212
273
 
213
274
  ### Template State Management
214
275
 
215
- `srtd` maintains two logs:
216
-
217
- - `.buildlog.json` - Tracks which templates have been built into migrations (commit this)
218
- - `.buildlog.local.json` - Tracks local database state (add to .gitignore)
276
+ Two state tracking files:
277
+ - `.buildlog.json` - Migration build state (commit this)
278
+ - `.buildlog.local.json` - Local database state (add to `.gitignore`)
219
279
 
220
280
  ### Register Existing Objects
221
281
 
222
- Import existing database objects into the template system:
223
-
282
+ Import existing database objects:
224
283
  ```bash
225
- srtd register my_function.sql # Won't generate new migration until changed
226
- # or
227
- srtd register # Opens interactive UI for selecting multiple templates
284
+ # Register specific template
285
+ srtd register my_function.sql
286
+
287
+ # Interactive multi-select UI
288
+ srtd register
228
289
  ```
229
290
 
230
291
  ## Development 🛠️
231
292
 
232
- This project uses TypeScript and modern Node.js features. To contribute:
293
+ ### Local Setup
233
294
 
234
- 1. Set up the development environment:
235
295
  ```bash
296
+ # Clone and install
236
297
  git clone https://github.com/stokke/srtd.git
237
298
  cd srtd
238
299
  npm install
239
- ```
240
300
 
241
- 2. Start development:
242
- ```bash
243
- npm run dev # Watches for changes
244
- npm test # Runs tests
245
- npm start # Builds, links, and runs CLI
246
- ```
301
+ # Development
302
+ npm run dev # Watch mode
303
+ npm test # Run tests
304
+ npm start # Build, link, run
247
305
 
248
- 3. Other useful commands:
249
- ```bash
250
- npm run typecheck # Type checking
251
- npm run lint # Lint and fix
252
- npm run test:coverage # Test coverage
306
+ # Quality Checks
307
+ npm run typecheck # Type checking
308
+ npm run lint # Lint and fix
309
+ npm run test:coverage # Test coverage
253
310
  ```
254
311
 
255
312
  ## Contributing 🤝
256
313
 
257
- 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:
314
+ While feature-complete for our needs, we welcome:
258
315
 
259
- - Bug fixes and reliability improvements
260
- - Documentation improvements and examples
261
- - Test coverage
262
- - Performance optimizations
316
+ - 🐛 Bug fixes and reliability improvements
317
+ - 📚 Documentation improvements
318
+ - Test coverage enhancements
319
+ - ⚡️ Performance optimizations
263
320
 
264
- ### Contribution Guidelines
321
+ ### Contribution Process
265
322
 
266
323
  1. Create a [changeset](https://github.com/changesets/changesets) (`npm run changeset`)
267
324
  2. Ensure tests pass (`npm test`)
268
325
  3. Follow existing code style
269
- 4. Update documentation as needed
326
+ 4. Update documentation
270
327
 
271
- 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!
328
+ Note: New features are evaluated based on alignment with project scope.
272
329
 
273
330
  ## License
274
331
 
275
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
332
+ MIT License - see [LICENSE](LICENSE) file.
333
+
334
+ ---
276
335
 
277
336
  Made with 🪄 by [Timm Stokke](https://timm.stokke.me) & [Claude Sonnet](https://claude.ai)
337
+
338
+ [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/t1mmen)
@@ -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,27 @@
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" },
17
+ const { error } = useDatabaseConnection();
18
+ return (React.createElement(ThemeProvider, { theme: customTheme },
19
+ !!error && (React.createElement(Static, { items: [error] }, error => (React.createElement(Box, { key: error },
20
+ React.createElement(Alert, { variant: "error" },
21
+ React.createElement(Text, { bold: true, color: "red" },
22
+ "Error:",
23
+ ' '),
24
+ error))))),
5
25
  React.createElement(Component, { ...commandProps })));
6
26
  }
7
27
  //# 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;QAC9B,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;YACb,oBAAC,KAAK,IAAC,OAAO,EAAC,OAAO;gBACpB,oBAAC,IAAI,IAAC,IAAI,QAAC,KAAK,EAAC,KAAK;;oBACb,GAAG,CACL;gBACN,KAAK,CACA,CACJ,CACP,CACM,CACV;QACD,oBAAC,SAAS,OAAK,YAAY,GAAI,CACjB,CACjB,CAAC;AACJ,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 {};
@@ -1,20 +1,32 @@
1
+ // commands/build.tsx
2
+ import { useApp } from 'ink';
3
+ import { option } from 'pastel';
1
4
  import React from 'react';
5
+ import zod from 'zod';
2
6
  import { TemplateManager } from '../lib/templateManager.js';
3
- export default function Apply() {
7
+ export const options = zod.object({
8
+ force: zod.boolean().describe(option({
9
+ description: 'Force apply of all templates, irrespective of changes',
10
+ alias: 'f',
11
+ })),
12
+ });
13
+ export default function Apply({ options }) {
14
+ const { exit } = useApp();
4
15
  React.useEffect(() => {
5
16
  async function doApply() {
6
17
  try {
7
18
  const manager = await TemplateManager.create(process.cwd());
8
- await manager.processTemplates({ apply: true });
9
- process.exit(0);
19
+ await manager.processTemplates({ apply: true, force: options.force });
20
+ exit();
10
21
  }
11
22
  catch (err) {
12
- console.error('Error:', err instanceof Error ? err.message : String(err));
13
- process.exit(1);
23
+ if (err instanceof Error) {
24
+ exit(err);
25
+ }
14
26
  }
15
27
  }
16
28
  void doApply();
17
- }, []);
29
+ }, [exit, options]);
18
30
  return null;
19
31
  }
20
32
  //# sourceMappingURL=apply.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/commands/apply.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,CAAC,OAAO,UAAU,KAAK;IAC3B,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,KAAK,UAAU,OAAO;YACpB,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC5D,MAAM,OAAO,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QACD,KAAK,OAAO,EAAE,CAAC;IACjB,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/commands/apply.tsx"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC;IAChC,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,QAAQ,CAC3B,MAAM,CAAC;QACL,WAAW,EAAE,uDAAuD;QACpE,KAAK,EAAE,GAAG;KACX,CAAC,CACH;CACF,CAAC,CAAC;AAMH,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,EAAE,OAAO,EAAS;IAC9C,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,KAAK,UAAU,OAAO;YACpB,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;gBAC5D,MAAM,OAAO,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;gBACtE,IAAI,EAAE,CAAC;YACT,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;oBACzB,IAAI,CAAC,GAAG,CAAC,CAAC;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;QACD,KAAK,OAAO,EAAE,CAAC;IACjB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACpB,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1 +1,13 @@
1
- export default function Build(): 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 Build({ options }: Props): null;
13
+ export {};
@@ -1,15 +1,25 @@
1
1
  // commands/build.tsx
2
+ import { useApp } from 'ink';
3
+ import { option } from 'pastel';
2
4
  import React from 'react';
5
+ import zod from 'zod';
3
6
  import { TemplateManager } from '../lib/templateManager.js';
4
- export default function Build() {
7
+ export const options = zod.object({
8
+ force: zod.boolean().describe(option({
9
+ description: 'Force building of all templates, irrespective of changes',
10
+ alias: 'f',
11
+ })),
12
+ });
13
+ export default function Build({ options }) {
14
+ const { exit } = useApp();
5
15
  React.useEffect(() => {
6
16
  async function doBuild() {
7
17
  const manager = await TemplateManager.create(process.cwd());
8
- await manager.processTemplates({ generateFiles: true });
9
- process.exit(0);
18
+ await manager.processTemplates({ generateFiles: true, force: options.force });
19
+ exit();
10
20
  }
11
21
  doBuild();
12
- }, []);
22
+ }, [options, exit]);
13
23
  return null;
14
24
  }
15
25
  //# sourceMappingURL=build.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.tsx"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,CAAC,OAAO,UAAU,KAAK;IAC3B,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,KAAK,UAAU,OAAO;YACpB,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;YAC5D,MAAM,OAAO,CAAC,gBAAgB,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.tsx"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC;IAChC,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,QAAQ,CAC3B,MAAM,CAAC;QACL,WAAW,EAAE,0DAA0D;QACvE,KAAK,EAAE,GAAG;KACX,CAAC,CACH;CACF,CAAC,CAAC;AAMH,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,EAAE,OAAO,EAAS;IAC9C,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,KAAK,UAAU,OAAO;YACpB,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;YAC5D,MAAM,OAAO,CAAC,gBAAgB,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;YAC9E,IAAI,EAAE,CAAC;QACT,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IAEpB,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,37 +1,47 @@
1
- import { Select } from '@inkjs/ui';
2
- import { Box } from 'ink';
1
+ // commands/index.tsx
2
+ import { Select, Spinner } from '@inkjs/ui';
3
+ import { Box, Text } from 'ink';
3
4
  import React from 'react';
4
5
  import Branding from '../components/Branding.js';
6
+ import Quittable from '../components/Quittable.js';
7
+ import { useDatabaseConnection } from '../hooks/useDatabaseConnection.js';
5
8
  import Apply from './apply.js';
6
9
  import Build from './build.js';
7
10
  import Register from './register.js';
8
11
  import Watch from './watch.js';
9
12
  export default function UI() {
13
+ const { error, isChecking, isConnected } = useDatabaseConnection();
10
14
  const [selectedCommand, setSelectedCommand] = React.useState(null);
11
- // For now, we only demonstrate navigation to "register"
15
+ const handleOnChange = async (value) => {
16
+ setSelectedCommand(value);
17
+ };
12
18
  if (selectedCommand === 'register') {
13
19
  return React.createElement(Register, { args: undefined });
14
20
  }
15
21
  if (selectedCommand === 'apply') {
16
- return React.createElement(Apply, null);
22
+ return React.createElement(Apply, { options: { force: false } });
17
23
  }
18
24
  if (selectedCommand === 'build') {
19
- return React.createElement(Build, null);
25
+ return React.createElement(Build, { options: { force: false } });
20
26
  }
21
27
  if (selectedCommand === 'watch') {
22
28
  return React.createElement(Watch, null);
23
29
  }
24
30
  const menuItems = [
25
- { label: '🏗️ build - Build Supabase migrations from templates', value: 'build' },
26
- { label: '▶️ apply - Apply migration templates directly to database', value: 'apply' },
27
- { label: '✍️ register - Register templates as already built', value: 'register' },
31
+ { label: '🏗️ build - Build Supabase migrations from templates', value: 'build' },
32
+ { label: '▶️ apply - Apply migration templates directly to database', value: 'apply' },
33
+ { label: '✍️ register - Register templates as already built', value: 'register' },
28
34
  {
29
- label: '👀 watch - Watch templates for changes and apply directly to database',
35
+ label: '👀 watch - Watch templates for changes and apply directly to database',
30
36
  value: 'watch',
31
37
  },
32
38
  ];
33
39
  return (React.createElement(Box, { flexDirection: "column" },
34
40
  React.createElement(Branding, null),
35
- React.createElement(Select, { options: menuItems, onChange: value => setSelectedCommand(value) })));
41
+ error ? (React.createElement(Box, { gap: 1 },
42
+ React.createElement(Text, { color: "red", bold: true }, "Error"),
43
+ React.createElement(Text, null, "Check your database connection and try again."))) : (React.createElement(Select, { options: menuItems, isDisabled: !isConnected, onChange: handleOnChange })),
44
+ isChecking ? (React.createElement(Box, { marginTop: 1 },
45
+ React.createElement(Spinner, { label: "Checking database connection..." }))) : (React.createElement(Quittable, null))));
36
46
  }
37
47
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,QAAQ,MAAM,2BAA2B,CAAC;AACjD,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,MAAM,CAAC,OAAO,UAAU,EAAE;IACxB,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAElF,wDAAwD;IACxD,IAAI,eAAe,KAAK,UAAU,EAAE,CAAC;QACnC,OAAO,oBAAC,QAAQ,IAAC,IAAI,EAAE,SAAS,GAAI,CAAC;IACvC,CAAC;IAED,IAAI,eAAe,KAAK,OAAO,EAAE,CAAC;QAChC,OAAO,oBAAC,KAAK,OAAG,CAAC;IACnB,CAAC;IAED,IAAI,eAAe,KAAK,OAAO,EAAE,CAAC;QAChC,OAAO,oBAAC,KAAK,OAAG,CAAC;IACnB,CAAC;IAED,IAAI,eAAe,KAAK,OAAO,EAAE,CAAC;QAChC,OAAO,oBAAC,KAAK,OAAG,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG;QAChB,EAAE,KAAK,EAAE,uDAAuD,EAAE,KAAK,EAAE,OAAO,EAAE;QAClF,EAAE,KAAK,EAAE,4DAA4D,EAAE,KAAK,EAAE,OAAO,EAAE;QACvF,EAAE,KAAK,EAAE,oDAAoD,EAAE,KAAK,EAAE,UAAU,EAAE;QAClF;YACE,KAAK,EAAE,wEAAwE;YAC/E,KAAK,EAAE,OAAO;SACf;KACF,CAAC;IAEF,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ;QACzB,oBAAC,QAAQ,OAAG;QACZ,oBAAC,MAAM,IAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC,GAAI,CACxE,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.tsx"],"names":[],"mappings":"AAAA,qBAAqB;AACrB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,QAAQ,MAAM,2BAA2B,CAAC;AACjD,OAAO,SAAS,MAAM,4BAA4B,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,MAAM,CAAC,OAAO,UAAU,EAAE;IACxB,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,qBAAqB,EAAE,CAAC;IACnE,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAElF,MAAM,cAAc,GAAG,KAAK,EAAE,KAAa,EAAE,EAAE;QAC7C,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC,CAAC;IAEF,IAAI,eAAe,KAAK,UAAU,EAAE,CAAC;QACnC,OAAO,oBAAC,QAAQ,IAAC,IAAI,EAAE,SAAS,GAAI,CAAC;IACvC,CAAC;IAED,IAAI,eAAe,KAAK,OAAO,EAAE,CAAC;QAChC,OAAO,oBAAC,KAAK,IAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAI,CAAC;IAC9C,CAAC;IAED,IAAI,eAAe,KAAK,OAAO,EAAE,CAAC;QAChC,OAAO,oBAAC,KAAK,IAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAI,CAAC;IAC9C,CAAC;IAED,IAAI,eAAe,KAAK,OAAO,EAAE,CAAC;QAChC,OAAO,oBAAC,KAAK,OAAG,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG;QAChB,EAAE,KAAK,EAAE,sDAAsD,EAAE,KAAK,EAAE,OAAO,EAAE;QACjF,EAAE,KAAK,EAAE,2DAA2D,EAAE,KAAK,EAAE,OAAO,EAAE;QACtF,EAAE,KAAK,EAAE,mDAAmD,EAAE,KAAK,EAAE,UAAU,EAAE;QACjF;YACE,KAAK,EAAE,uEAAuE;YAC9E,KAAK,EAAE,OAAO;SACf;KACF,CAAC;IAEF,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ;QACzB,oBAAC,QAAQ,OAAG;QACX,KAAK,CAAC,CAAC,CAAC,CACP,oBAAC,GAAG,IAAC,GAAG,EAAE,CAAC;YACT,oBAAC,IAAI,IAAC,KAAK,EAAC,KAAK,EAAC,IAAI,kBAEf;YACP,oBAAC,IAAI,wDAAqD,CACtD,CACP,CAAC,CAAC,CAAC,CACF,oBAAC,MAAM,IAAC,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,cAAc,GAAI,CACnF;QAEA,UAAU,CAAC,CAAC,CAAC,CACZ,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC;YACf,oBAAC,OAAO,IAAC,KAAK,EAAC,iCAAiC,GAAG,CAC/C,CACP,CAAC,CAAC,CAAC,CACF,oBAAC,SAAS,OAAG,CACd,CACG,CACP,CAAC;AACJ,CAAC"}