@t1mmen/srtd 0.2.2 → 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 -89
  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 +12 -7
  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,24 +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 @t1mmen/srtd # Global installation
38
- # or
39
- npm install --save-dev @t1mmen/srtd # Project installation
40
- # or
41
- npx @t1mmen/srtd init # Run directly
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
42
51
  ```
43
52
 
44
- Then set up in your [Supabase](https://supabase.com) project:
53
+ ### Setup
45
54
 
46
55
  ```bash
47
56
  cd your-supabase-project
48
57
  srtd init
49
58
  ```
50
59
 
51
- 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`:
52
63
 
53
64
  ```sql
54
65
  CREATE OR REPLACE FUNCTION my_function()
@@ -59,21 +70,23 @@ END;
59
70
  $$ LANGUAGE plpgsql;
60
71
  ```
61
72
 
62
- Start development mode:
73
+ ### Development Workflow
63
74
 
75
+ 1. Start watch mode:
64
76
  ```bash
65
77
  srtd watch # Changes auto-apply to local database
66
78
  ```
67
79
 
68
- When ready to deploy:
69
-
80
+ 2. When ready to deploy:
70
81
  ```bash
71
- srtd build # Creates timestamped migration file
72
- supabase migrate up # Apply using Supabase CLI
82
+ srtd build # Creates timestamped migration file
83
+ supabase migration up # Apply using Supabase CLI
73
84
  ```
74
85
 
75
86
  ## Commands 🎮
76
87
 
88
+ ### Interactive Mode
89
+
77
90
  Running `srtd` without arguments opens an interactive menu:
78
91
 
79
92
  ```
@@ -83,68 +96,101 @@ Running `srtd` without arguments opens an interactive menu:
83
96
  👀 watch - Watch templates for changes, apply directly to database
84
97
  ```
85
98
 
86
- Or use these commands directly:
99
+ ### CLI Mode
87
100
 
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
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!
92
108
 
93
109
  ## Perfect For 🎯
94
110
 
95
- Ideal for [Supabase](https://supabase.com) database objects that need full redefinition:
111
+ ### Ideal Use Cases
96
112
 
97
- Functions and stored procedures:
113
+ Database functions:
98
114
  ```sql
99
- CREATE OR REPLACE FUNCTION search_products(query text, category_id uuid DEFAULT NULL)
100
- 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 $$
101
124
  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);
125
+ PERFORM pg_notify(
126
+ 'changes',
127
+ json_build_object('table', TG_TABLE_NAME, 'id', NEW.id)::text
128
+ );
129
+ RETURN NEW;
116
130
  END;
117
131
  $$ LANGUAGE plpgsql;
118
132
  ```
119
133
 
120
- ✅ 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:
121
146
  ```sql
122
- CREATE POLICY "users can view own data"
123
- ON profiles FOR SELECT
124
- 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;
125
156
  ```
126
157
 
127
- ✅ Roles and permissions:
158
+ ✅ Roles and Permissions:
128
159
  ```sql
129
- 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
130
164
  GRANT USAGE ON SCHEMA public TO authenticated;
131
165
  GRANT SELECT ON ALL TABLES IN SCHEMA public TO authenticated;
132
166
  ```
133
167
 
134
- **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
135
183
 
136
184
  * ❌ Table structures
137
185
  * ❌ Indexes
138
186
  * ❌ Data modifications
139
- * ❌ Anything that is not fully re-definable, really.
187
+ * ❌ Non-idempotent operations
140
188
 
141
- In these cases, use regular [Supabase](https://supabase.com) migrations.
189
+ Use regular [Supabase](https://supabase.com) migrations for these cases.
142
190
 
143
191
  ## The Power of Templates 💪
144
192
 
145
- Here's why templates make your life easier. Consider a PR that adds priority to our notification dispatch function.
146
-
147
- With templates, the change is clear and reviewable:
193
+ Templates make code reviews meaningful. Consider this PR adding priority to a notification function:
148
194
 
149
195
  ```diff
150
196
  CREATE OR REPLACE FUNCTION dispatch_notification(
@@ -181,23 +227,37 @@ CREATE OR REPLACE FUNCTION dispatch_notification(
181
227
  RETURNING id INTO notification_id;
182
228
  ```
183
229
 
184
- 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.
185
231
 
186
232
  ## Configuration 📝
187
233
 
188
- During initialization, `srtd` creates a `srtd.config.json`:
234
+ `srtd.config.json` created during initialization:
189
235
 
190
- ```json
236
+ ```jsonc
191
237
  {
238
+ // Prevents building templates with this extension
192
239
  "wipIndicator": ".wip",
240
+
241
+ // Migration file naming: 20211001000000_srtd-my_function.sql
242
+ "migrationPrefix": "srtd",
243
+
244
+ // Template discovery
193
245
  "filter": "**/*.sql",
246
+
247
+ // Migration file comments
194
248
  "banner": "You very likely **DO NOT** want to manually edit this generated file.",
195
249
  "footer": "",
250
+
251
+ // Wrap migrations in transaction
196
252
  "wrapInTransaction": true,
253
+
254
+ // File paths
197
255
  "templateDir": "supabase/migrations-templates",
198
256
  "migrationDir": "supabase/migrations",
199
257
  "buildLog": "supabase/migrations-templates/.buildlog.json",
200
258
  "localBuildLog": "supabase/migrations-templates/.buildlog.local.json",
259
+
260
+ // Database connection
201
261
  "pgConnection": "postgresql://postgres:postgres@localhost:54322/postgres"
202
262
  }
203
263
  ```
@@ -206,74 +266,73 @@ During initialization, `srtd` creates a `srtd.config.json`:
206
266
 
207
267
  ### Work in Progress Templates
208
268
 
209
- Add `.wip.sql` extension to templates under development to prevent accidental migration generation:
210
-
269
+ Add `.wip.sql` extension to prevent migration generation:
211
270
  ```bash
212
- my_function.wip.sql # Won't generate migrations during build
271
+ my_function.wip.sql # Only applied locally, never built
213
272
  ```
214
273
 
215
274
  ### Template State Management
216
275
 
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)
276
+ Two state tracking files:
277
+ - `.buildlog.json` - Migration build state (commit this)
278
+ - `.buildlog.local.json` - Local database state (add to `.gitignore`)
221
279
 
222
280
  ### Register Existing Objects
223
281
 
224
- Import existing database objects into the template system:
225
-
282
+ Import existing database objects:
226
283
  ```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
284
+ # Register specific template
285
+ srtd register my_function.sql
286
+
287
+ # Interactive multi-select UI
288
+ srtd register
230
289
  ```
231
290
 
232
291
  ## Development 🛠️
233
292
 
234
- This project uses TypeScript and modern Node.js features. To contribute:
293
+ ### Local Setup
235
294
 
236
- 1. Set up the development environment:
237
295
  ```bash
296
+ # Clone and install
238
297
  git clone https://github.com/stokke/srtd.git
239
298
  cd srtd
240
299
  npm install
241
- ```
242
300
 
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
- ```
301
+ # Development
302
+ npm run dev # Watch mode
303
+ npm test # Run tests
304
+ npm start # Build, link, run
249
305
 
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
306
+ # Quality Checks
307
+ npm run typecheck # Type checking
308
+ npm run lint # Lint and fix
309
+ npm run test:coverage # Test coverage
255
310
  ```
256
311
 
257
312
  ## Contributing 🤝
258
313
 
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:
314
+ While feature-complete for our needs, we welcome:
260
315
 
261
- - Bug fixes and reliability improvements
262
- - Documentation improvements and examples
263
- - Test coverage
264
- - Performance optimizations
316
+ - 🐛 Bug fixes and reliability improvements
317
+ - 📚 Documentation improvements
318
+ - Test coverage enhancements
319
+ - ⚡️ Performance optimizations
265
320
 
266
- ### Contribution Guidelines
321
+ ### Contribution Process
267
322
 
268
323
  1. Create a [changeset](https://github.com/changesets/changesets) (`npm run changeset`)
269
324
  2. Ensure tests pass (`npm test`)
270
325
  3. Follow existing code style
271
- 4. Update documentation as needed
326
+ 4. Update documentation
272
327
 
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!
328
+ Note: New features are evaluated based on alignment with project scope.
274
329
 
275
330
  ## License
276
331
 
277
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
332
+ MIT License - see [LICENSE](LICENSE) file.
333
+
334
+ ---
278
335
 
279
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"}