22
33** Branch:** ` feat-demo-1-vertical-slice `
44
5- ** Goal:** Implement minimal end-to-end flow execution - from UI button click through Edge Function to real-time status updates. Validates entire integration stack with ** client-side auth only ** .
5+ ** Goal:** Implement minimal end-to-end flow execution - from UI button click through Edge Function to real-time status updates. Validates entire integration stack with ** no authentication required ** - just public anon key access .
66
77** Success Criteria:**
88- ✅ Supabase initialized in demo app
9- - ✅ Client-side anonymous auth working
109- ✅ Edge Function with 1-step test flow executes
1110- ✅ pgflow packages vendored correctly
12- - ✅ pgflow client connects from UI
11+ - ✅ pgflow client connects from UI (anon key only)
1312- ✅ Button click starts flow
1413- ✅ Status updates in real-time
1514- ✅ No console errors
1615
17- ** Philosophy:** Build the thinnest possible slice through the entire stack. UI will be ugly - that's fine. Goal is to prove integration works. ** No server-side auth needed - demo is public !**
16+ ** Philosophy:** Build the thinnest possible slice through the entire stack. UI will be ugly - that's fine. Goal is to prove integration works. ** No authentication - just public demo with anon key !**
1817
1918---
2019
2120## Tasks
2221
2322### 1. Add pgflow Client Dependency
2423
25- Edit ` apps/demo/package.json ` - add ` "@pgflow/client": "workspace:*" ` to dependencies:
26-
2724``` bash
28- pnpm install
25+ cd apps/demo
26+ pnpm add @pgflow/client
27+ cd ../..
2928```
3029
30+ This will add ` "@pgflow/client": "workspace:*" ` to dependencies automatically.
31+
3132### 2. Initialize Supabase
3233
3334``` bash
34- cd apps/demo && supabase init && cd ../..
35+ cd apps/demo && npx -y supabase@latest init && cd ../..
3536```
3637
3738---
3839
39- ### 3. Configure Supabase for pgflow
40+ ### 3. Install pgflow
4041
41- Edit ` apps/demo/supabase/config.toml ` - add ` " pgflow" ` to ` [api] ` schemas :
42+ Run the pgflow installer :
4243
43- ``` toml
44- schemas = [" public" , " pgflow" ]
44+ ``` bash
45+ npx pgflow@latest install
46+ ```
47+
48+ This will:
49+ - Update ` supabase/config.toml ` (adds pgflow schema, connection pooling)
50+ - Copy pgflow migrations to ` supabase/migrations/ `
51+
52+ ### 4. Create Anon Permissions Migration
53+
54+ Create ` apps/demo/supabase/migrations/<timestamp>_demo_anon_permissions.sql ` :
55+
56+ ``` sql
57+ -- Grant anon role access to start flows
58+ GRANT USAGE ON SCHEMA pgflow TO anon;
59+ GRANT EXECUTE ON FUNCTION pgflow .start_flow TO anon;
60+
61+ -- Grant anon role read access to pgflow tables for real-time updates
62+ GRANT SELECT ON pgflow .flows TO anon;
63+ GRANT SELECT ON pgflow .runs TO anon;
64+ GRANT SELECT ON pgflow .steps TO anon;
65+ GRANT SELECT ON pgflow .step_states TO anon;
66+ GRANT SELECT ON pgflow .deps TO anon;
67+
68+ -- Enable real-time for anon role
69+ ALTER PUBLICATION supabase_realtime ADD TABLE pgflow .runs ;
70+ ALTER PUBLICATION supabase_realtime ADD TABLE pgflow .step_states ;
71+ ```
72+
73+ ### 5. Restart Supabase and Apply Migrations
74+
75+ ``` bash
76+ npx -y supabase@latest stop
77+ npx -y supabase@latest start
78+ npx -y supabase@latest migrations up
4579```
4680
4781---
4882
49- ### 4 . Copy Vendoring Script
83+ ### 6 . Copy Vendoring Script
5084
5185``` bash
5286mkdir -p apps/demo/scripts
5387cp examples/playground/scripts/sync-edge-deps.sh apps/demo/scripts/
5488chmod +x apps/demo/scripts/sync-edge-deps.sh
5589```
5690
57- ### 5 . Update Vendoring Script Paths
91+ ### 7 . Update Vendoring Script Paths
5892
5993Edit ` apps/demo/scripts/sync-edge-deps.sh ` - replace ` PLAYGROUND_DIR ` with ` DEMO_DIR ` :
6094
@@ -65,7 +99,7 @@ VENDOR_DIR="$DEMO_DIR/supabase/functions/_vendor"
6599
66100---
67101
68- ### 6 . Add Nx Target for Vendoring
102+ ### 8 . Add Nx Target for Vendoring
69103
70104Edit ` apps/demo/project.json ` - add ` sync-edge-deps ` target:
71105
@@ -77,7 +111,7 @@ Edit `apps/demo/project.json` - add `sync-edge-deps` target:
77111}
78112```
79113
80- ### 7 . Build Dependencies and Vendor
114+ ### 9 . Build Dependencies and Vendor
81115
82116``` bash
83117pnpm nx build core dsl
@@ -86,93 +120,233 @@ pnpm nx sync-edge-deps demo # Verify: ls apps/demo/supabase/functions/_vendor/@
86120
87121---
88122
89- ### 8. Create Test Flow Definition
123+ ### 10. Create Test Flow Worker Directory
124+
125+ Create worker directory with flow definition inside:
126+
127+ ``` bash
128+ mkdir -p apps/demo/supabase/functions/test_flow_worker
129+ ```
90130
91- Create ` apps/demo/supabase/functions/_flows/test-flow.ts ` :
92- - Import Flow from ` @pgflow/dsl `
93- - Create simple 1-step flow with slug 'test-flow'
94- - Handler returns: ` Hello, ${input.run.message}! `
95- - Note: Access run input via ` input.run.* ` pattern
131+ ### 11. Create Test Flow Definition
96132
97- ### 9. Create Edge Function Worker
133+ Create ` apps/demo/supabase/functions/test_flow_worker/test_flow.ts ` :
134+
135+ ``` typescript
136+ import { Flow } from ' @pgflow/dsl' ;
137+
138+ export default new Flow <{ message: string }>({ slug: ' test_flow' }).step (
139+ { slug: ' greet' },
140+ (input ) => ` Hello, ${input .run .message }! `
141+ );
142+ ```
143+
144+ ** Note:** Flow slug is ` test_flow ` (with underscore), matching the worker directory name.
145+
146+ ### 12. Create Edge Function Worker
147+
148+ Create ` apps/demo/supabase/functions/test_flow_worker/index.ts ` :
98149
99- Create ` apps/demo/supabase/functions/demo-worker/index.ts ` :
100150``` typescript
101151import { EdgeWorker } from ' @pgflow/edge-worker' ;
102- import TestFlow from ' ../_flows/test-flow .ts' ;
152+ import TestFlow from ' ./test_flow .ts' ;
103153
104154EdgeWorker .start (TestFlow );
105155```
156+
106157** This 3-line pattern is critical - it's how all pgflow workers are set up!**
107158
108- ### 10. Test Edge Function Locally
159+ ### 13. Create Deno Import Map
160+
161+ Create ` apps/demo/supabase/functions/test_flow_worker/deno.json ` :
162+
163+ ``` json
164+ {
165+ "imports" : {
166+ "@pgflow/core" : " ../_vendor/@pgflow/core/index.ts" ,
167+ "@pgflow/core/" : " ../_vendor/@pgflow/core/" ,
168+ "@pgflow/dsl" : " ../_vendor/@pgflow/dsl/index.ts" ,
169+ "@pgflow/dsl/" : " ../_vendor/@pgflow/dsl/" ,
170+ "@pgflow/dsl/supabase" : " ../_vendor/@pgflow/dsl/src/supabase.ts" ,
171+ "@pgflow/edge-worker" : " ../_vendor/@pgflow/edge-worker/index.ts" ,
172+ "@pgflow/edge-worker/" : " ../_vendor/@pgflow/edge-worker/" ,
173+ "@pgflow/edge-worker/_internal" : " ../_vendor/@pgflow/edge-worker/_internal.ts" ,
174+ "postgres" : " npm:postgres@3.4.5" ,
175+ "@henrygd/queue" : " jsr:@henrygd/queue@^1.0.7" ,
176+ "@supabase/supabase-js" : " jsr:@supabase/supabase-js@^2.49.4"
177+ }
178+ }
179+ ```
180+
181+ ** Critical:** This maps all ` @pgflow/* ` imports to the vendored packages (one level up), including subpaths and required dependencies!
182+
183+ ---
184+
185+ ### 14. Configure Edge Function in config.toml
186+
187+ Edit ` apps/demo/supabase/config.toml ` , add at the end:
188+
189+ ``` toml
190+ [functions .test_flow_worker ]
191+ enabled = true
192+ verify_jwt = false
193+ import_map = " ./functions/test_flow_worker/deno.json"
194+ entrypoint = " ./functions/test_flow_worker/index.ts"
195+ ```
196+
197+ ** Critical:** ` verify_jwt = false ` allows public demo access without authentication.
198+
199+ ---
200+
201+ ### 15. Build Client Package
202+
203+ ``` bash
204+ pnpm nx build client
205+ ```
206+
207+ ### 16. Test Edge Function Locally
109208
110209``` bash
111210cd apps/demo
112- supabase start # Then in another terminal:
113- supabase functions serve demo-worker
211+ npx -y supabase@latest start # Then in another terminal:
212+ npx -y supabase@latest functions serve test_flow_worker
114213```
115214
116- ### 11 . Create Client-Side Supabase Configuration
215+ ### 17 . Create Client-Side Supabase Configuration
117216
118217Create ` apps/demo/src/lib/supabase.ts ` :
119- - Create Supabase client with URL and anon key (use env vars or local defaults)
120- - Create PgflowClient wrapping the Supabase client
121- - Export both for use in components
122- - ** Key point:** Pure client-side - no server hooks, no cookies!
123218
124- ---
219+ ``` typescript
220+ import { createClient } from ' @supabase/supabase-js' ;
221+ import { PgflowClient } from ' @pgflow/client' ;
125222
126- ### 12. Create Minimal Test UI
223+ // Hardcoded local Supabase defaults (Phase 1 - production config in Phase 6)
224+ const SUPABASE_URL = ' http://127.0.0.1:54321' ;
225+ const SUPABASE_ANON_KEY = ' sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH' ;
127226
128- Replace ` apps/demo/src/routes/+page.svelte ` with basic test interface.
227+ export const supabase = createClient (SUPABASE_URL , SUPABASE_ANON_KEY );
228+ export const pgflow = new PgflowClient (supabase );
229+ ```
129230
130- ** Key patterns to implement:**
131- - Anonymous auth: ` await supabase.auth.signInAnonymously() ` in onMount
132- - Start flow: ` pgflow.startFlow('test-flow', { message: 'World' }) `
133- - Listen to events: ` run.on('*', (event) => { ... }) `
134- - Svelte 5 state: ` let status = $state<string>('idle') `
135- - Display status updates and output
231+ ** Note:** Get the anon key from ` npx -y supabase@latest status ` . Environment variables for production will be added in Phase 6 (Deploy).
232+
233+ ---
136234
137- ** Remember:** Use ` onclick={handler} ` not ` on:click ` (Svelte 5 syntax)
235+ ### 18. Create Minimal Test UI
236+
237+ Replace ` apps/demo/src/routes/+page.svelte ` with basic test interface:
238+
239+ ``` svelte
240+ <script lang="ts">
241+ import { pgflow } from '$lib/supabase';
242+
243+ let status = $state<string>('idle');
244+ let output = $state<string>('');
245+ let events = $state<string[]>([]);
246+
247+ async function startTestFlow() {
248+ status = 'starting...';
249+ events = [];
250+ output = '';
251+
252+ try {
253+ const run = await pgflow.startFlow('test_flow', { message: 'World' });
254+
255+ run.on('*', (event) => {
256+ events = [...events, JSON.stringify(event, null, 2)];
257+ status = event.status || status;
258+
259+ if (event.status === 'completed' && event.output) {
260+ output = JSON.stringify(event.output, null, 2);
261+ }
262+ });
263+ } catch (error) {
264+ status = 'error';
265+ output = error instanceof Error ? error.message : String(error);
266+ }
267+ }
268+ </script>
269+
270+ <div class="container">
271+ <h1>pgflow Demo - Phase 1 Vertical Slice</h1>
272+
273+ <div class="controls">
274+ <button onclick={startTestFlow}>Start Test Flow</button>
275+ </div>
276+
277+ <div class="status">
278+ <h2>Status</h2>
279+ <p>{status}</p>
280+ </div>
281+
282+ {#if output}
283+ <div class="output">
284+ <h2>Output</h2>
285+ <pre>{output}</pre>
286+ </div>
287+ {/if}
288+
289+ {#if events.length > 0}
290+ <div class="events">
291+ <h2>Events</h2>
292+ {#each events as event}
293+ <pre>{event}</pre>
294+ {/each}
295+ </div>
296+ {/if}
297+ </div>
298+
299+ <style>
300+ /* Add basic styling for layout */
301+ </style>
302+ ```
303+
304+ ** Key patterns:**
305+ - Use ` onclick={handler} ` not ` on:click ` (Svelte 5 syntax)
306+ - Svelte 5 state: ` let status = $state<string>('idle') `
307+ - Start flow: ` pgflow.startFlow('test_flow', { message: 'World' }) `
308+ - Listen to events: ` run.on('*', (event) => { ... }) `
138309
139310---
140311
141- ### 13 . Start Dev Server
312+ ### 19 . Start Dev Server
142313
143314``` bash
144- pnpm nx dev demo # Ensure supabase start running
315+ pnpm nx dev demo # Ensure npx -y supabase@latest start running
145316```
146317
147- ### 14 . Test End-to-End
318+ ### 20 . Test End-to-End
148319
149320Open http://localhost:5173/ , click button, verify:
150321- Status: ` idle ` → ` starting... ` → ` running ` → ` completed `
151322- Output shows ` "Hello, World!" `
152- - Console shows event stream
323+ - Events section shows real-time event stream
153324
154325---
155326
156327## Validation Checklist
157328
158329- [ ] Supabase initialized, pgflow packages vendored
159- - [ ] Test flow + worker created
160- - [ ] Anonymous auth working (check Network tab for auth.signInAnonymously)
161- - [ ] ` supabase start ` and ` functions serve demo-worker ` running
330+ - [ ] Test flow + worker created in ` test_flow_worker/ ` directory
331+ - [ ] Worker configured in ` config.toml ` with ` verify_jwt = false `
332+ - [ ] Deno import map created with all dependencies
333+ - [ ] ` npx -y supabase@latest start ` and ` functions serve test_flow_worker ` running
162334- [ ] Dev server starts, button click starts flow
163- - [ ] Status updates real-time, output appears, no console errors
335+ - [ ] Status updates real-time, output appears, events stream visible
336+ - [ ] No authentication needed - works immediately on page load
337+ - [ ] Flow slug is ` test_flow ` (with underscore)
338+ - [ ] No console errors
164339
165340---
166341
167342## Troubleshooting
168343
169344- ** Vendoring fails:** Check ` ls pkgs/core/dist ` , rebuild with ` pnpm nx build core dsl `
170- - ** Edge Function won't start:** Check ` supabase status ` , verify vendored files exist
171- - ** Anonymous auth fails:** Check browser console, ensure Supabase anon key is valid
345+ - ** Edge Function won't start:** Check ` npx -y supabase@latest status ` , verify vendored files exist
172346- ** Flow doesn't start:** Check browser console - Supabase connection, pgflow schema in config.toml, flow slug matches
173347- ** No real-time updates:** Check Realtime enabled, Edge Function logs, Svelte ` $state ` reactivity
174348- ** TypeScript errors:** Verify Svelte 5 syntax (` $state ` , ` onclick ` )
175- - ** Auth issues:** Remember - this is all client-side! No server hooks needed
349+ - ** Anon key issues:** Get correct key from ` npx -y supabase@latest status ` , ensure hardcoded in ` lib/supabase.ts `
176350
177351** Rollback:** Mock pgflow client to debug Edge Function separately
178352
0 commit comments