# 🔄 FX Flow Plugin - Complete Cheat Sheet ## 🎯 **Plugin Overview** **FX Flow** provides dynamic, reactive, cross-realm procedure graphs for complex business logic and multi-step workflows that can execute on client, server, or both seamlessly. ### **Core Features** - ✅ **Cross-Realm Execution** - Run nodes on client, server, or both - ✅ **Dynamic Flow Creation** - Build flows programmatically - ✅ **Reactive Integration** - Flows integrate with FX reactive system - ✅ **Zero Latency** - Uses FX's sync surface with background execution - ✅ **Error Handling** - Built-in retry, timeout, and error recovery - ✅ **State Management** - Flow state stored in FX nodes --- ## 📦 **Installation & Setup** ### **Auto-Loading (Recommended)** ```javascript // Just use $flow - auto-loads fx-flow plugin const userFlow = $flow('userRegistration') .node('validate', { runsOn: 'client', effect: validateUser }) .node('save', { runsOn: 'server', effect: saveUser }) .start('validate', userData); ``` ### **Manual Loading** ```html ``` --- ## 🚀 **Core API Reference** ### **Flow Creation** ```typescript $flow(flowName: string) ``` | Method | Description | Example | |--------|-------------|---------| | `.node(name, config)` | Add flow node | `.node('validate', { runsOn: 'client', effect: fn })` | | `.connect(from, to)` | Connect nodes | `.connect('validate', 'save')` | | `.start(nodeName, data)` | Start flow execution | `.start('validate', userData)` | | `.status()` | Get flow status | `.status()` | | `.result()` | Get flow result | `.result()` | ### **Node Configuration** ```typescript { runsOn: 'client' | 'server' | 'both', effect: (ctx) => { /* node logic */ }, timeout?: number, retries?: number, guards?: (ctx) => boolean } ``` --- ## 🎪 **Basic Usage Patterns** ### **Simple Linear Flow** ```typescript // User registration workflow const registrationFlow = $flow('userRegistration') .node('validateInput', { runsOn: 'client', effect: (ctx) => { const { email, password } = ctx.in; if (!email.includes('@')) { ctx.error('Invalid email format'); return; } if (password.length < 8) { ctx.error('Password too short'); return; } ctx.next('checkExisting', { email, password }); } }) .node('checkExisting', { runsOn: 'server', effect: (ctx) => { const existingUser = $db.users.where(`[email="${ctx.in.email}"]`).first(); if (existingUser) { ctx.error('Email already registered'); return; } ctx.next('createUser', ctx.in); } }) .node('createUser', { runsOn: 'server', effect: (ctx) => { const userId = $db.users.create({ email: ctx.in.email, password: hashPassword(ctx.in.password), createdAt: Date.now() }); ctx.set({ userId, success: true }); } }); // Execute the flow const result = registrationFlow.start('validateInput', { email: 'user@example.com', password: 'securepass123' }); // Watch for completion result.watch(data => { if (data.success) { console.log('User registered with ID:', data.userId); } }); ``` ### **Branching Flow with Conditions** ```typescript // Order processing with multiple paths const orderFlow = $flow('orderProcessing') .node('validateOrder', { runsOn: 'both', effect: (ctx) => { const order = ctx.in; if (!order.items?.length) { ctx.error('Empty order'); return; } // Branch based on order type if (order.type === 'subscription') { ctx.next('processSubscription', order); } else { ctx.next('processOneTime', order); } } }) .node('processSubscription', { runsOn: 'server', effect: (ctx) => { // Handle subscription logic const subscription = $db.subscriptions.create(ctx.in); ctx.next('sendConfirmation', { type: 'subscription', id: subscription.id }); } }) .node('processOneTime', { runsOn: 'server', effect: (ctx) => { // Handle one-time purchase const order = $db.orders.create(ctx.in); ctx.next('sendConfirmation', { type: 'order', id: order.id }); } }) .node('sendConfirmation', { runsOn: 'both', effect: (ctx) => { const emailResult = $api.post('/emails/confirmation', ctx.in); emailResult.watch(result => { ctx.set({ emailSent: result.success, ...ctx.in }); }); } }); ``` ### **Parallel Execution Flow** ```typescript // Data processing with parallel steps const dataProcessingFlow = $flow('dataProcessing') .node('splitData', { runsOn: 'server', effect: (ctx) => { const data = ctx.in; const chunks = chunkArray(data, 100); // Start parallel processing chunks.forEach((chunk, index) => { ctx.next(`processChunk${index}`, chunk); }); } }); // Add dynamic processing nodes for (let i = 0; i < maxChunks; i++) { dataProcessingFlow.node(`processChunk${i}`, { runsOn: 'server', effect: (ctx) => { const processedChunk = processData(ctx.in); ctx.next('collectResults', { chunkIndex: i, data: processedChunk }); } }); } dataProcessingFlow.node('collectResults', { runsOn: 'server', effect: (ctx) => { // Collect all processed chunks const results = $$('flows.dataProcessing.results').get() || []; results.push(ctx.in); $$('flows.dataProcessing.results').set(results); // Check if all chunks processed if (results.length >= expectedChunks) { ctx.set({ allChunks: results, completed: true }); } } }); ``` --- ## 🛡️ **Error Handling & Resilience** ### **Retry and Timeout** ```typescript const resilientFlow = $flow('resilientAPI') .node('callExternalAPI', { runsOn: 'server', timeout: 10000, // 10 second timeout retries: 3, // Retry 3 times effect: (ctx) => { const response = $api.get('https://external-api.com/data'); response.watch((data, error) => { if (error) { ctx.retry(error); // Triggers automatic retry } else { ctx.next('processResponse', data); } }); } }) .node('processResponse', { runsOn: 'both', effect: (ctx) => { const processedData = processAPIResponse(ctx.in); ctx.set(processedData); } }); ``` ### **Error Recovery** ```typescript const recoveryFlow = $flow('withRecovery') .node('riskyOperation', { runsOn: 'server', effect: (ctx) => { try { const result = performRiskyOperation(ctx.in); ctx.next('success', result); } catch (error) { ctx.next('handleError', { error: error.message, input: ctx.in }); } } }) .node('handleError', { runsOn: 'server', effect: (ctx) => { // Log error to FX $$('flows.errors').set({ flow: 'withRecovery', error: ctx.in.error, timestamp: Date.now() }); // Attempt recovery ctx.next('recovery', ctx.in.input); } }) .node('recovery', { runsOn: 'server', effect: (ctx) => { const recoveredResult = performRecoveryOperation(ctx.in); ctx.set({ recovered: true, result: recoveredResult }); } }) .node('success', { runsOn: 'both', effect: (ctx) => { ctx.set({ success: true, result: ctx.in }); } }); ``` --- ## 🎯 **Real-World Examples** ### **E-commerce Checkout Flow** ```typescript const checkoutFlow = $flow('ecommerceCheckout') .node('validateCart', { runsOn: 'client', effect: (ctx) => { const cart = $$('cart.items').get(); if (!cart?.length) { ctx.error('Cart is empty'); return; } ctx.next('calculateTotal', cart); } }) .node('calculateTotal', { runsOn: 'both', effect: (ctx) => { const items = ctx.in; const subtotal = items.reduce((sum, item) => sum + (item.price * item.quantity), 0); const tax = subtotal * 0.1; const total = subtotal + tax; ctx.next('processPayment', { items, subtotal, tax, total }); } }) .node('processPayment', { runsOn: 'server', timeout: 30000, retries: 2, effect: (ctx) => { const paymentData = ctx.in; $safe.circuit('payment-processor').execute(() => { const payment = $api.post('/payments/process', paymentData); payment.watch((result, error) => { if (error) { ctx.error('Payment failed: ' + error.message); } else { ctx.next('createOrder', { ...paymentData, paymentId: result.id }); } }); }); } }) .node('createOrder', { runsOn: 'server', effect: (ctx) => { const orderId = $db.orders.create({ items: ctx.in.items, total: ctx.in.total, paymentId: ctx.in.paymentId, status: 'confirmed', createdAt: Date.now() }); ctx.next('sendConfirmation', { orderId }); } }) .node('sendConfirmation', { runsOn: 'both', effect: (ctx) => { // Send email confirmation $api.post('/emails/order-confirmation', ctx.in); // Clear cart $$('cart.items').set([]); // Update UI $$('checkout.status').set('completed'); $$('checkout.orderId').set(ctx.in.orderId); ctx.set({ success: true, orderId: ctx.in.orderId }); } }); // Usage in checkout component function processCheckout(cartData) { const result = checkoutFlow.start('validateCart', cartData); result.watch(data => { if (data.success) { showSuccessMessage(`Order ${data.orderId} confirmed!`); redirectToOrderPage(data.orderId); } }); } ``` ### **User Onboarding Flow** ```typescript const onboardingFlow = $flow('userOnboarding') .node('collectInfo', { runsOn: 'client', effect: (ctx) => { // Show onboarding form jsx.render(`