@t1mmen/srtd 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -118
- package/dist/package.json +97 -0
- package/dist/src/__tests__/apply.test.js +69 -0
- package/dist/src/__tests__/apply.test.js.map +1 -0
- package/dist/src/__tests__/build.test.js +67 -0
- package/dist/src/__tests__/build.test.js.map +1 -0
- package/dist/{__tests__ → src/__tests__}/vitest.setup.js +41 -25
- package/dist/src/__tests__/vitest.setup.js.map +1 -0
- package/dist/{__tests__ → src/__tests__}/watch.test.js +9 -3
- package/dist/src/__tests__/watch.test.js.map +1 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/commands/_app.js +29 -0
- package/dist/src/commands/_app.js.map +1 -0
- package/dist/{commands → src/commands}/apply.d.ts +2 -1
- package/dist/src/commands/apply.js +30 -0
- package/dist/src/commands/apply.js.map +1 -0
- package/dist/{commands → src/commands}/build.d.ts +5 -1
- package/dist/src/commands/build.js +41 -0
- package/dist/src/commands/build.js.map +1 -0
- package/dist/src/commands/clear.d.ts +2 -0
- package/dist/src/commands/clear.js +45 -0
- package/dist/src/commands/clear.js.map +1 -0
- package/dist/{commands → src/commands}/index.js +11 -5
- package/dist/src/commands/index.js.map +1 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/{commands → src/commands}/register.js +6 -5
- package/dist/src/commands/register.js.map +1 -0
- package/dist/{commands → src/commands}/watch.js +61 -33
- package/dist/src/commands/watch.js.map +1 -0
- package/dist/{components → src/components}/Branding.js +7 -7
- package/dist/src/components/Branding.js.map +1 -0
- package/dist/src/components/Debug.d.ts +2 -0
- package/dist/src/components/Debug.js +160 -0
- package/dist/src/components/Debug.js.map +1 -0
- package/dist/src/components/ProcessingResults.d.ts +9 -0
- package/dist/src/components/ProcessingResults.js +32 -0
- package/dist/src/components/ProcessingResults.js.map +1 -0
- package/dist/{components → src/components}/Quittable.js +1 -1
- package/dist/src/components/Quittable.js.map +1 -0
- package/dist/src/components/TimeSince.js.map +1 -0
- package/dist/src/components/customTheme.d.ts +10 -0
- package/dist/src/components/customTheme.js +66 -0
- package/dist/src/components/customTheme.js.map +1 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/hooks/useDatabaseConnection.js +57 -0
- package/dist/src/hooks/useDatabaseConnection.js.map +1 -0
- package/dist/{hooks → src/hooks}/useTemplateManager.d.ts +6 -8
- package/dist/src/hooks/useTemplateManager.js +124 -0
- package/dist/src/hooks/useTemplateManager.js.map +1 -0
- package/dist/src/hooks/useTemplateProcessor.d.ts +11 -0
- package/dist/src/hooks/useTemplateProcessor.js +71 -0
- package/dist/src/hooks/useTemplateProcessor.js.map +1 -0
- package/dist/src/hooks/useTemplateState.js.map +1 -0
- package/dist/{lib → src/lib}/templateManager.d.ts +13 -2
- package/dist/{lib → src/lib}/templateManager.js +172 -50
- package/dist/src/lib/templateManager.js.map +1 -0
- package/dist/src/lib/templateManager.test.js +729 -0
- package/dist/src/lib/templateManager.test.js.map +1 -0
- package/dist/{types.d.ts → src/types.d.ts} +3 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/applyMigration.js.map +1 -0
- package/dist/src/utils/applyMigrations.test.js.map +1 -0
- package/dist/src/utils/calculateMD5.js.map +1 -0
- package/dist/{utils → src/utils}/config.d.ts +2 -0
- package/dist/src/utils/config.js +65 -0
- package/dist/src/utils/config.js.map +1 -0
- package/dist/src/utils/config.test.js.map +1 -0
- package/dist/src/utils/createEmptyBuildLog.js.map +1 -0
- package/dist/{utils → src/utils}/databaseConnection.d.ts +5 -0
- package/dist/{utils → src/utils}/databaseConnection.js +40 -13
- package/dist/src/utils/databaseConnection.js.map +1 -0
- package/dist/src/utils/databaseConnection.test.d.ts +1 -0
- package/dist/src/utils/databaseConnection.test.js.map +1 -0
- package/dist/src/utils/ensureDirectories.js.map +1 -0
- package/dist/src/utils/fileExists.js.map +1 -0
- package/dist/src/utils/getNextTimestamp.js.map +1 -0
- package/dist/src/utils/isWipTemplate.js.map +1 -0
- package/dist/src/utils/loadBuildLog.js.map +1 -0
- package/dist/src/utils/loadBuildLog.test.d.ts +1 -0
- package/dist/src/utils/loadBuildLog.test.js.map +1 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/registerTemplate.js.map +1 -0
- package/dist/src/utils/safeCreate.js.map +1 -0
- package/dist/src/utils/saveBuildLog.js.map +1 -0
- package/dist/src/utils/store.d.ts +5 -0
- package/dist/src/utils/store.js +10 -0
- package/dist/src/utils/store.js.map +1 -0
- package/package.json +4 -3
- package/dist/__tests__/vitest.setup.js.map +0 -1
- package/dist/__tests__/watch.test.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/_app.js +0 -27
- package/dist/commands/_app.js.map +0 -1
- package/dist/commands/apply.js +0 -32
- package/dist/commands/apply.js.map +0 -1
- package/dist/commands/build.js +0 -25
- package/dist/commands/build.js.map +0 -1
- package/dist/commands/index.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/register.js.map +0 -1
- package/dist/commands/watch.js.map +0 -1
- package/dist/components/Branding.js.map +0 -1
- package/dist/components/Quittable.js.map +0 -1
- package/dist/components/TimeSince.js.map +0 -1
- package/dist/constants.js.map +0 -1
- package/dist/hooks/useDatabaseConnection.js +0 -68
- package/dist/hooks/useDatabaseConnection.js.map +0 -1
- package/dist/hooks/useTemplateManager.js +0 -141
- package/dist/hooks/useTemplateManager.js.map +0 -1
- package/dist/hooks/useTemplateState.js.map +0 -1
- package/dist/lib/templateManager.js.map +0 -1
- package/dist/lib/templateManager.test.js +0 -289
- package/dist/lib/templateManager.test.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils/applyMigration.js.map +0 -1
- package/dist/utils/applyMigrations.test.js.map +0 -1
- package/dist/utils/calculateMD5.js.map +0 -1
- package/dist/utils/config.js +0 -79
- package/dist/utils/config.js.map +0 -1
- package/dist/utils/config.test.js.map +0 -1
- package/dist/utils/createEmptyBuildLog.js.map +0 -1
- package/dist/utils/databaseConnection.js.map +0 -1
- package/dist/utils/databaseConnection.test.js.map +0 -1
- package/dist/utils/ensureDirectories.js.map +0 -1
- package/dist/utils/fileExists.js.map +0 -1
- package/dist/utils/getNextTimestamp.js.map +0 -1
- package/dist/utils/isWipTemplate.js.map +0 -1
- package/dist/utils/loadBuildLog.js.map +0 -1
- package/dist/utils/loadBuildLog.test.js.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/registerTemplate.js.map +0 -1
- package/dist/utils/safeCreate.js.map +0 -1
- package/dist/utils/saveBuildLog.js.map +0 -1
- /package/dist/{__tests__/watch.test.d.ts → src/__tests__/apply.test.d.ts} +0 -0
- /package/dist/{lib/templateManager.test.d.ts → src/__tests__/build.test.d.ts} +0 -0
- /package/dist/{__tests__ → src/__tests__}/vitest.setup.d.ts +0 -0
- /package/dist/{utils/applyMigrations.test.d.ts → src/__tests__/watch.test.d.ts} +0 -0
- /package/dist/{cli.d.ts → src/cli.d.ts} +0 -0
- /package/dist/{cli.js → src/cli.js} +0 -0
- /package/dist/{commands → src/commands}/_app.d.ts +0 -0
- /package/dist/{commands → src/commands}/index.d.ts +0 -0
- /package/dist/{commands → src/commands}/init.d.ts +0 -0
- /package/dist/{commands → src/commands}/init.js +0 -0
- /package/dist/{commands → src/commands}/register.d.ts +0 -0
- /package/dist/{commands → src/commands}/watch.d.ts +0 -0
- /package/dist/{components → src/components}/Branding.d.ts +0 -0
- /package/dist/{components → src/components}/Quittable.d.ts +0 -0
- /package/dist/{components → src/components}/TimeSince.d.ts +0 -0
- /package/dist/{components → src/components}/TimeSince.js +0 -0
- /package/dist/{constants.d.ts → src/constants.d.ts} +0 -0
- /package/dist/{constants.js → src/constants.js} +0 -0
- /package/dist/{hooks → src/hooks}/useDatabaseConnection.d.ts +0 -0
- /package/dist/{hooks → src/hooks}/useTemplateState.d.ts +0 -0
- /package/dist/{hooks → src/hooks}/useTemplateState.js +0 -0
- /package/dist/{utils/config.test.d.ts → src/lib/templateManager.test.d.ts} +0 -0
- /package/dist/{types.js → src/types.js} +0 -0
- /package/dist/{utils → src/utils}/applyMigration.d.ts +0 -0
- /package/dist/{utils → src/utils}/applyMigration.js +0 -0
- /package/dist/{utils/databaseConnection.test.d.ts → src/utils/applyMigrations.test.d.ts} +0 -0
- /package/dist/{utils → src/utils}/applyMigrations.test.js +0 -0
- /package/dist/{utils → src/utils}/calculateMD5.d.ts +0 -0
- /package/dist/{utils → src/utils}/calculateMD5.js +0 -0
- /package/dist/{utils/loadBuildLog.test.d.ts → src/utils/config.test.d.ts} +0 -0
- /package/dist/{utils → src/utils}/config.test.js +0 -0
- /package/dist/{utils → src/utils}/createEmptyBuildLog.d.ts +0 -0
- /package/dist/{utils → src/utils}/createEmptyBuildLog.js +0 -0
- /package/dist/{utils → src/utils}/databaseConnection.test.js +0 -0
- /package/dist/{utils → src/utils}/ensureDirectories.d.ts +0 -0
- /package/dist/{utils → src/utils}/ensureDirectories.js +0 -0
- /package/dist/{utils → src/utils}/fileExists.d.ts +0 -0
- /package/dist/{utils → src/utils}/fileExists.js +0 -0
- /package/dist/{utils → src/utils}/getNextTimestamp.d.ts +0 -0
- /package/dist/{utils → src/utils}/getNextTimestamp.js +0 -0
- /package/dist/{utils → src/utils}/isWipTemplate.d.ts +0 -0
- /package/dist/{utils → src/utils}/isWipTemplate.js +0 -0
- /package/dist/{utils → src/utils}/loadBuildLog.d.ts +0 -0
- /package/dist/{utils → src/utils}/loadBuildLog.js +0 -0
- /package/dist/{utils → src/utils}/loadBuildLog.test.js +0 -0
- /package/dist/{utils → src/utils}/logger.d.ts +0 -0
- /package/dist/{utils → src/utils}/logger.js +0 -0
- /package/dist/{utils → src/utils}/registerTemplate.d.ts +0 -0
- /package/dist/{utils → src/utils}/registerTemplate.js +0 -0
- /package/dist/{utils → src/utils}/safeCreate.d.ts +0 -0
- /package/dist/{utils → src/utils}/safeCreate.js +0 -0
- /package/dist/{utils → src/utils}/saveBuildLog.d.ts +0 -0
- /package/dist/{utils → src/utils}/saveBuildLog.js +0 -0
package/README.md
CHANGED
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
# `srtd` 🪄 Supabase Repeatable Template Definitions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
> Live-reloading SQL templates for [Supabase](https://supabase.com) projects. DX supercharged! 🚀
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/@t1mmen/srtd)
|
|
6
8
|
[](https://opensource.org/licenses/MIT)
|
|
7
9
|
[](https://github.com/t1mmen/srtd/actions/workflows/ci.yml)
|
|
8
10
|
[](https://codecov.io/gh/t1mmen/srtd)
|
|
9
11
|
|
|
12
|
+
|
|
13
|
+
[](./readme-screenshot.png)
|
|
14
|
+
|
|
10
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.
|
|
11
16
|
|
|
12
|
-
Built specifically for projects using the standard [Supabase](https://supabase.com) stack (but probably works alright for other Postgres-based projects, too).
|
|
13
17
|
|
|
14
|
-
|
|
18
|
+
📖 Blog: [Introducing `srtd`: Live-Reloading SQL Templates for Supabase](https://timm.stokke.me/blog/srtd-live-reloading-and-sql-templates-for-supabase)
|
|
15
19
|
|
|
16
20
|
## Why This Exists 🤔
|
|
17
21
|
|
|
18
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:
|
|
19
23
|
|
|
20
|
-
1. Code reviews were painful - function changes showed up as complete rewrites
|
|
21
|
-
2. Designing and iterating on database changes locally
|
|
24
|
+
1. Code reviews were painful - function changes showed up as complete rewrites, `git blame` was useless
|
|
25
|
+
2. Designing and iterating on database changes locally was full of friction, no matter which workflow we tried
|
|
26
|
+
|
|
27
|
+
I spent [nearly two years looking](https://news.ycombinator.com/item?id=37755076) for something pre-existing, to no avail. Sufficiently fed up, I paired with [Claude](https://claude.ai) to eliminate these annoyances.
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
Say hello to `srtd`.
|
|
24
30
|
|
|
25
31
|
## Key Features ✨
|
|
26
32
|
|
|
@@ -29,6 +35,8 @@ After over a year of looking-but-not-finding a better way, I paired up with [Cla
|
|
|
29
35
|
- **Just SQL**: Templates build as standard [Supabase](https://supabase.com) migrations when you're ready to deploy
|
|
30
36
|
- **Developer Friendly**: Interactive CLI with visual feedback for all operations
|
|
31
37
|
|
|
38
|
+
Built specifically for projects using the standard [Supabase](https://supabase.com) stack (but probably works alright for other Postgres-based projects, too).
|
|
39
|
+
|
|
32
40
|
## Requirements
|
|
33
41
|
|
|
34
42
|
- Node.js v20.x or higher
|
|
@@ -83,100 +91,82 @@ srtd build # Creates timestamped migration file
|
|
|
83
91
|
supabase migration up # Apply using Supabase CLI
|
|
84
92
|
```
|
|
85
93
|
|
|
86
|
-
## Commands 🎮
|
|
87
|
-
|
|
88
|
-
### Interactive Mode
|
|
89
|
-
|
|
90
|
-
Running `srtd` without arguments opens an interactive menu:
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
❯ 🏗️ build - Build Supabase migrations from templates
|
|
94
|
-
▶️ apply - Apply migration templates directly to database
|
|
95
|
-
✍️ register - Register templates as already built
|
|
96
|
-
👀 watch - Watch templates for changes, apply directly to database
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### CLI Mode
|
|
100
|
-
|
|
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
|
|
95
|
+
## The Power of Templates 💪
|
|
105
96
|
|
|
106
|
-
|
|
107
|
-
> `watch` and `apply` commands modify your local database directly and don't clean up after themselves. Use with caution!
|
|
97
|
+
Without templates, the smallest change to a function would show up as a complete rewrite in your version control system. With templates, the diff is clear and concise.
|
|
108
98
|
|
|
109
|
-
## Perfect For 🎯
|
|
110
99
|
|
|
111
|
-
###
|
|
100
|
+
### Perfect For 🎯
|
|
112
101
|
|
|
113
102
|
✅ Database functions:
|
|
114
|
-
```
|
|
115
|
-
--
|
|
116
|
-
CREATE OR REPLACE FUNCTION
|
|
117
|
-
RETURNS
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
json_build_object('table', TG_TABLE_NAME, 'id', NEW.id)::text
|
|
128
|
-
);
|
|
129
|
-
RETURN NEW;
|
|
130
|
-
END;
|
|
131
|
-
$$ LANGUAGE plpgsql;
|
|
103
|
+
```diff
|
|
104
|
+
-- Event notifications
|
|
105
|
+
CREATE OR REPLACE FUNCTION notify_changes()
|
|
106
|
+
RETURNS trigger AS $$
|
|
107
|
+
BEGIN
|
|
108
|
+
PERFORM pg_notify(
|
|
109
|
+
'changes',
|
|
110
|
+
json_build_object('table', TG_TABLE_NAME, 'id', NEW.id)::text
|
|
111
|
+
);
|
|
112
|
+
+ RAISE NOTICE 'Notified changes for %', TG_TABLE_NAME; -- Debug logging
|
|
113
|
+
RETURN NEW;
|
|
114
|
+
END;
|
|
115
|
+
$$ LANGUAGE plpgsql;
|
|
132
116
|
```
|
|
133
117
|
|
|
134
118
|
✅ Row-Level Security (RLS):
|
|
135
|
-
```
|
|
136
|
-
-- Replace/update policies safely
|
|
137
|
-
DROP POLICY IF EXISTS "workspace_access" ON resources;
|
|
138
|
-
CREATE POLICY "workspace_access" ON resources
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
119
|
+
```diff
|
|
120
|
+
-- Replace/update policies safely
|
|
121
|
+
DROP POLICY IF EXISTS "workspace_access" ON resources;
|
|
122
|
+
CREATE POLICY "workspace_access" ON resources
|
|
123
|
+
USING (workspace_id IN (
|
|
124
|
+
SELECT id FROM workspaces
|
|
125
|
+
WHERE organization_id = auth.organization_id()
|
|
126
|
+
+ AND auth.user_role() NOT IN ('pending')
|
|
127
|
+
));
|
|
143
128
|
```
|
|
144
129
|
|
|
145
130
|
✅ Views for data abstraction:
|
|
146
|
-
```
|
|
147
|
-
CREATE OR REPLACE VIEW active_subscriptions AS
|
|
148
|
-
SELECT
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
FROM subscriptions s
|
|
153
|
-
JOIN plans p ON p.id = s.plan_id
|
|
154
|
-
WHERE s.status = 'active'
|
|
155
|
-
|
|
131
|
+
```diff
|
|
132
|
+
CREATE OR REPLACE VIEW active_subscriptions AS
|
|
133
|
+
SELECT
|
|
134
|
+
s.*,
|
|
135
|
+
p.name as plan_name,
|
|
136
|
+
p.features
|
|
137
|
+
FROM subscriptions s
|
|
138
|
+
JOIN plans p ON p.id = s.plan_id
|
|
139
|
+
- WHERE s.status = 'active';
|
|
140
|
+
+ WHERE s.status = 'active'
|
|
141
|
+
+ AND s.expires_at > CURRENT_TIMESTAMP;
|
|
156
142
|
```
|
|
157
143
|
|
|
158
144
|
✅ Roles and Permissions:
|
|
159
|
-
```
|
|
160
|
-
-- Revoke all first for clean state
|
|
161
|
-
REVOKE ALL ON ALL TABLES IN SCHEMA public FROM public;
|
|
145
|
+
```diff
|
|
146
|
+
-- Revoke all first for clean state
|
|
147
|
+
REVOKE ALL ON ALL TABLES IN SCHEMA public FROM public;
|
|
162
148
|
|
|
163
|
-
-- Grant specific access
|
|
164
|
-
GRANT USAGE ON SCHEMA public TO authenticated;
|
|
165
|
-
GRANT SELECT ON ALL TABLES IN SCHEMA public TO authenticated;
|
|
149
|
+
-- Grant specific access
|
|
150
|
+
GRANT USAGE ON SCHEMA public TO authenticated;
|
|
151
|
+
GRANT SELECT ON ALL TABLES IN SCHEMA public TO authenticated;
|
|
152
|
+
+ GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO admin;
|
|
166
153
|
```
|
|
167
154
|
|
|
168
155
|
✅ Safe Type Extensions:
|
|
169
|
-
```
|
|
170
|
-
DO $$
|
|
171
|
-
BEGIN
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
156
|
+
```diff
|
|
157
|
+
DO $$
|
|
158
|
+
BEGIN
|
|
159
|
+
-- Add new enum values idempotently
|
|
160
|
+
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'notification_type') THEN
|
|
161
|
+
CREATE TYPE notification_type AS ENUM ('email', 'sms');
|
|
162
|
+
END IF;
|
|
163
|
+
|
|
164
|
+
-- Extend existing enum safely
|
|
165
|
+
ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'push';
|
|
166
|
+
ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'pusher';
|
|
167
|
+
ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'webhook';
|
|
168
|
+
+ ALTER TYPE notification_type ADD VALUE IF NOT EXISTS 'email';
|
|
169
|
+
END $$;
|
|
180
170
|
```
|
|
181
171
|
|
|
182
172
|
### Not Recommended For
|
|
@@ -188,46 +178,23 @@ END $$;
|
|
|
188
178
|
|
|
189
179
|
Use regular [Supabase](https://supabase.com) migrations for these cases.
|
|
190
180
|
|
|
191
|
-
## The Power of Templates 💪
|
|
192
181
|
|
|
193
|
-
|
|
182
|
+
## Commands 🎮
|
|
194
183
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
) RETURNS uuid AS $$
|
|
201
|
-
DECLARE
|
|
202
|
-
notification_id uuid;
|
|
203
|
-
user_settings jsonb;
|
|
204
|
-
BEGIN
|
|
205
|
-
-- Get user notification settings
|
|
206
|
-
SELECT settings INTO user_settings
|
|
207
|
-
FROM user_preferences
|
|
208
|
-
WHERE id = user_id;
|
|
209
|
-
|
|
210
|
-
-- Create notification record
|
|
211
|
-
+ -- Include priority based on notification type
|
|
212
|
-
INSERT INTO notifications (
|
|
213
|
-
id,
|
|
214
|
-
user_id,
|
|
215
|
-
type,
|
|
216
|
-
payload,
|
|
217
|
-
+ priority,
|
|
218
|
-
created_at
|
|
219
|
-
) VALUES (
|
|
220
|
-
gen_random_uuid(),
|
|
221
|
-
dispatch_notification.user_id,
|
|
222
|
-
type,
|
|
223
|
-
payload,
|
|
224
|
-
+ COALESCE((SELECT priority FROM notification_types WHERE name = type), 'normal'),
|
|
225
|
-
CURRENT_TIMESTAMP
|
|
226
|
-
)
|
|
227
|
-
RETURNING id INTO notification_id;
|
|
228
|
-
```
|
|
184
|
+
### Interactive Mode
|
|
185
|
+
|
|
186
|
+
Running `srtd` without arguments opens an interactive menu:
|
|
187
|
+
|
|
188
|
+
### CLI Mode
|
|
229
189
|
|
|
230
|
-
|
|
190
|
+
- 🏗️ `srtd build [--force]` - Generate migrations from templates
|
|
191
|
+
- ▶️ `srtd apply [--force]` - Apply templates directly to local database
|
|
192
|
+
- ✍️ `srtd register [file.sql]` - Mark templates as already built
|
|
193
|
+
- 👀 `srtd watch` - Watch and auto-apply changes
|
|
194
|
+
- 🧹 `srtd clean` - Remove all logs and reset config
|
|
195
|
+
|
|
196
|
+
> [!IMPORTANT]
|
|
197
|
+
> `watch` and `apply` commands modify your local database directly and don't clean up after themselves. Use with caution!
|
|
231
198
|
|
|
232
199
|
## Configuration 📝
|
|
233
200
|
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@t1mmen/srtd",
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Supabase Repeatable Template Definitions (srtd): 🪄 Live-reloading SQL templates for Supabase DX. Make your database changes reviewable and migrations maintainable! 🚀",
|
|
6
|
+
"bin": {
|
|
7
|
+
"srtd": "dist/src/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=20"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"clean": "rm -rf dist; rm -rf build; rm -rf coverage; npm run supabase:stop",
|
|
15
|
+
"changeset": "changeset",
|
|
16
|
+
"version": "changeset version",
|
|
17
|
+
"release": "npm run build && changeset publish",
|
|
18
|
+
"build": "rm -rf dist && tsc",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"lint": "biome lint . --write",
|
|
21
|
+
"format": "biome format . --write",
|
|
22
|
+
"dev": "tsc --watch",
|
|
23
|
+
"test": "vitest",
|
|
24
|
+
"test:coverage": "vitest run --coverage --reporter=junit --outputFile=test-report.junit.xml",
|
|
25
|
+
"start": "tsx src/cli.tsx",
|
|
26
|
+
"repomix": "mkdir build; npx repomix",
|
|
27
|
+
"supabase:start": "npx supabase start",
|
|
28
|
+
"supabase:stop": "npx supabase stop"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"author": {
|
|
34
|
+
"name": "Timm Stokke",
|
|
35
|
+
"email": "timm@stokke.me",
|
|
36
|
+
"url": "https://timm.stokke.me"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public",
|
|
40
|
+
"registry": "https://registry.npmjs.org/"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/t1mmen/srtd.git"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/t1mmen/srtd/issues"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/t1mmen/srtd#readme",
|
|
50
|
+
"os": [
|
|
51
|
+
"darwin",
|
|
52
|
+
"linux",
|
|
53
|
+
"win32"
|
|
54
|
+
],
|
|
55
|
+
"keywords": [
|
|
56
|
+
"cli",
|
|
57
|
+
"migrations",
|
|
58
|
+
"database",
|
|
59
|
+
"postgresql",
|
|
60
|
+
"supabase",
|
|
61
|
+
"sql-templates",
|
|
62
|
+
"live-reload",
|
|
63
|
+
"hot-reload",
|
|
64
|
+
"repeatable-migrations",
|
|
65
|
+
"database-migrations",
|
|
66
|
+
"supabase-cli"
|
|
67
|
+
],
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"@inkjs/ui": "^2.0.0",
|
|
70
|
+
"chokidar": "^4.0.3",
|
|
71
|
+
"conf": "^13.1.0",
|
|
72
|
+
"glob": "^10.0.0",
|
|
73
|
+
"ink": "^5.1.0",
|
|
74
|
+
"pastel": "^3.0.0",
|
|
75
|
+
"pg": "^8.13.1",
|
|
76
|
+
"react": "^18.3.0",
|
|
77
|
+
"update-notifier": "^7.3.1",
|
|
78
|
+
"zod": "^3.24.1"
|
|
79
|
+
},
|
|
80
|
+
"devDependencies": {
|
|
81
|
+
"@biomejs/biome": "1.9.4",
|
|
82
|
+
"@changesets/cli": "^2.27.11",
|
|
83
|
+
"@sindresorhus/tsconfig": "^7.0.0",
|
|
84
|
+
"@types/glob": "^8.1.0",
|
|
85
|
+
"@types/node": "^20.17.10",
|
|
86
|
+
"@types/pg": "^8.11.10",
|
|
87
|
+
"@types/react": "^18.3.0",
|
|
88
|
+
"@types/update-notifier": "^6.0.8",
|
|
89
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
90
|
+
"chalk": "^5.4.1",
|
|
91
|
+
"ink-testing-library": "^4.0.0",
|
|
92
|
+
"lefthook": "^1.10.1",
|
|
93
|
+
"tsx": "^4.19.2",
|
|
94
|
+
"typescript": "^5.7.2",
|
|
95
|
+
"vitest": "^2.1.8"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { render } from 'ink-testing-library';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
7
|
+
import Apply from '../commands/apply.js';
|
|
8
|
+
import { connect } from '../utils/databaseConnection.js';
|
|
9
|
+
import { TEST_FN_PREFIX } from './vitest.setup.js';
|
|
10
|
+
vi.mock('../hooks/useTemplateProcessor', () => ({
|
|
11
|
+
useTemplateProcessor: vi.fn().mockImplementation(() => ({
|
|
12
|
+
isProcessing: false,
|
|
13
|
+
result: {
|
|
14
|
+
built: [],
|
|
15
|
+
applied: ['test.sql'],
|
|
16
|
+
skipped: [],
|
|
17
|
+
errors: [],
|
|
18
|
+
},
|
|
19
|
+
})),
|
|
20
|
+
}));
|
|
21
|
+
describe('Apply Command', () => {
|
|
22
|
+
const testContext = {
|
|
23
|
+
timestamp: Date.now(),
|
|
24
|
+
testFunctionName: `${TEST_FN_PREFIX}${Date.now()}`,
|
|
25
|
+
testDir: path.join(tmpdir(), `test-apply-command-${Date.now()}`),
|
|
26
|
+
};
|
|
27
|
+
vi.mock('ink', async (importOriginal) => {
|
|
28
|
+
const actual = (await importOriginal());
|
|
29
|
+
const mockExit = vi.fn();
|
|
30
|
+
return {
|
|
31
|
+
...actual,
|
|
32
|
+
useApp: () => ({ exit: mockExit }),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
async function createTestTemplate(content) {
|
|
36
|
+
const templateDir = path.join(testContext.testDir, 'test-templates');
|
|
37
|
+
await fs.mkdir(templateDir, { recursive: true });
|
|
38
|
+
const templatePath = path.join(templateDir, `test-${testContext.timestamp}.sql`);
|
|
39
|
+
await fs.writeFile(templatePath, content);
|
|
40
|
+
}
|
|
41
|
+
beforeEach(async () => {
|
|
42
|
+
const validSQL = `
|
|
43
|
+
CREATE OR REPLACE FUNCTION ${testContext.testFunctionName}()
|
|
44
|
+
RETURNS void AS $$
|
|
45
|
+
BEGIN NULL; END;
|
|
46
|
+
$$ LANGUAGE plpgsql;
|
|
47
|
+
`;
|
|
48
|
+
await createTestTemplate(validSQL);
|
|
49
|
+
});
|
|
50
|
+
afterEach(async () => {
|
|
51
|
+
const client = await connect();
|
|
52
|
+
try {
|
|
53
|
+
await client.query(`DROP FUNCTION IF EXISTS ${testContext.testFunctionName}()`);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
client.release();
|
|
57
|
+
}
|
|
58
|
+
await fs.rm(testContext.testDir, { recursive: true, force: true });
|
|
59
|
+
});
|
|
60
|
+
test('shows progress and success', async () => {
|
|
61
|
+
const { lastFrame } = render(React.createElement(Apply, { options: { force: false } }));
|
|
62
|
+
expect(lastFrame()).toMatch(/✓ test\.sql/);
|
|
63
|
+
});
|
|
64
|
+
test('handles force flag', async () => {
|
|
65
|
+
const { lastFrame } = render(React.createElement(Apply, { options: { force: true } }));
|
|
66
|
+
expect(lastFrame()).toMatch(/✓ test\.sql/);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=apply.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.test.js","sourceRoot":"","sources":["../../../src/__tests__/apply.test.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { render } from 'ink-testing-library';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
7
|
+
import Build from '../commands/build.js';
|
|
8
|
+
import { TEST_FN_PREFIX } from './vitest.setup.js';
|
|
9
|
+
vi.mock('../hooks/useTemplateProcessor', () => ({
|
|
10
|
+
useTemplateProcessor: vi.fn().mockImplementation(({ apply }) => ({
|
|
11
|
+
isProcessing: false,
|
|
12
|
+
result: {
|
|
13
|
+
built: ['test.sql'],
|
|
14
|
+
applied: apply ? ['test.sql'] : [],
|
|
15
|
+
skipped: [],
|
|
16
|
+
errors: [],
|
|
17
|
+
},
|
|
18
|
+
})),
|
|
19
|
+
}));
|
|
20
|
+
describe('Build Command', () => {
|
|
21
|
+
const testContext = {
|
|
22
|
+
timestamp: Date.now(),
|
|
23
|
+
testFunctionName: `${TEST_FN_PREFIX}${Date.now()}`,
|
|
24
|
+
testDir: path.join(tmpdir(), `test-build-command-${Date.now()}`),
|
|
25
|
+
};
|
|
26
|
+
vi.mock('ink', async (importOriginal) => {
|
|
27
|
+
const actual = (await importOriginal());
|
|
28
|
+
return {
|
|
29
|
+
...actual,
|
|
30
|
+
useApp: () => ({ exit: vi.fn() }),
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
async function createTestTemplate(content) {
|
|
34
|
+
const templateDir = path.join(testContext.testDir, 'test-templates');
|
|
35
|
+
await fs.mkdir(templateDir, { recursive: true });
|
|
36
|
+
const templatePath = path.join(templateDir, `test-${testContext.timestamp}.sql`);
|
|
37
|
+
await fs.writeFile(templatePath, content);
|
|
38
|
+
return templatePath;
|
|
39
|
+
}
|
|
40
|
+
beforeEach(async () => {
|
|
41
|
+
const validSQL = `
|
|
42
|
+
CREATE OR REPLACE FUNCTION ${testContext.testFunctionName}()
|
|
43
|
+
RETURNS void AS $$
|
|
44
|
+
BEGIN NULL; END;
|
|
45
|
+
$$ LANGUAGE plpgsql;
|
|
46
|
+
`;
|
|
47
|
+
await createTestTemplate(validSQL);
|
|
48
|
+
});
|
|
49
|
+
afterEach(async () => {
|
|
50
|
+
await fs.rm(testContext.testDir, { recursive: true, force: true });
|
|
51
|
+
});
|
|
52
|
+
test('shows build progress and success', async () => {
|
|
53
|
+
const { lastFrame } = render(React.createElement(Build, { options: { force: false } }));
|
|
54
|
+
expect(lastFrame()).toMatch(/✓ test\.sql/);
|
|
55
|
+
});
|
|
56
|
+
test('handles force flag', async () => {
|
|
57
|
+
const { lastFrame } = render(React.createElement(Build, { options: { force: true } }));
|
|
58
|
+
expect(lastFrame()).toMatch(/✓ test\.sql/);
|
|
59
|
+
});
|
|
60
|
+
test('handles build and apply together', async () => {
|
|
61
|
+
const { lastFrame } = render(React.createElement(Build, { options: { force: false, apply: true } }));
|
|
62
|
+
// Use more precise matching that accounts for newlines
|
|
63
|
+
expect(lastFrame()).toMatch(/Built:\s*\n\s*✓ test\.sql/);
|
|
64
|
+
expect(lastFrame()).toMatch(/Applied:\s*\n\s*✓ test\.sql/);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=build.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.test.js","sourceRoot":"","sources":["../../../src/__tests__/build.test.tsx"],"names":[],"mappings":"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"}
|
|
@@ -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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest.setup.js","sourceRoot":"","sources":["../../../src/__tests__/vitest.setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,15 +9,21 @@ vi.mock('ink', async (importOriginal) => {
|
|
|
9
9
|
useApp: () => ({ exit: vi.fn() }),
|
|
10
10
|
};
|
|
11
11
|
});
|
|
12
|
+
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
12
13
|
describe('Watch Command', () => {
|
|
13
14
|
beforeEach(() => {
|
|
14
15
|
vi.clearAllMocks();
|
|
15
16
|
});
|
|
16
17
|
test('renders initial state with no templates', async () => {
|
|
17
18
|
const { lastFrame } = render(React.createElement(Watch, null));
|
|
18
|
-
await
|
|
19
|
-
|
|
20
|
-
expect(
|
|
19
|
+
await wait(100); // Allow time for UI to render and DB to initialize
|
|
20
|
+
expect(lastFrame()).toContain('Watch Mode');
|
|
21
|
+
expect(lastFrame()).toContain('No templates found');
|
|
22
|
+
});
|
|
23
|
+
test('renders with templates', async () => {
|
|
24
|
+
const { lastFrame } = render(React.createElement(Watch, null));
|
|
25
|
+
await wait(100); // Allow time for UI to render and DB to initialize
|
|
26
|
+
expect(lastFrame()).toContain('Watch Mode');
|
|
21
27
|
});
|
|
22
28
|
});
|
|
23
29
|
//# sourceMappingURL=watch.test.js.map
|
|
@@ -0,0 +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,MAAM,IAAI,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAE7E,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,CAAC,GAAG,CAAC,CAAC,CAAC,mDAAmD;QAEpE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QAC5C,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACxC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,oBAAC,KAAK,OAAG,CAAC,CAAC;QACxC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,mDAAmD;QAEpE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|