# FX Standalone Mode **Make FX completely self-sufficient - no server required!** ## Overview FX Standalone Mode enables the FX framework to run entirely in the browser without any server dependencies. This is achieved through three core components: 1. **Service Worker Proxy** - Handles cross-origin module loading and caching 2. **IndexedDB Adapter** - Provides local database storage for fx-orm 3. **Web Worker Executor** - Executes fx-flow nodes in isolated workers ## Features ### πŸš€ Complete Browser Autonomy - No server endpoints required - Works offline after initial load - Cross-origin module loading - Local data persistence - Isolated code execution ### πŸ”’ Security & Isolation - Web Worker sandboxing for flow execution - CORS proxy with configurable origins - Structured cloning for safe data transfer - Error boundary isolation ### ⚑ Performance - Parallel execution with worker pools - Intelligent caching strategies - Lazy module loading - Query optimization with indexes ### πŸ”„ Compatibility - Drop-in replacement for server adapters - Same API surface as server mode - Gradual migration support - Backward compatible ## Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Browser Application β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ FX Core β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ fx-orm β”‚ fx-flow β”‚ fx-api β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚IndexedDB β”‚ Worker β”‚ Service Worker β”‚ β”‚ Adapter β”‚ Executor β”‚ Proxy β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Browser Native APIs β”‚ β”‚ IndexedDB | Web Workers | Cache API β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## Quick Start ### 1. Installation ```bash # Copy standalone adapters to your project cp -r fx/FX\ TypeScript/plugins/fx-* your-project/fx/plugins/ cp -r fx/FX\ TypeScript/workers/fx-service-worker.ts your-project/ ``` ### 2. HTML Setup ```html FX Standalone App
``` ## Component Details ### Service Worker Proxy The Service Worker intercepts and handles: - **Module Requests** - Loads cross-origin ES modules - **API Proxying** - Bypasses CORS restrictions - **Offline Caching** - Stores modules and data locally #### Features ```javascript // Automatic module proxying import React from 'https://esm.sh/react'; // Works even with CORS // Manual proxy requests fetch('/fx/proxy?url=https://api.example.com/data'); // Cache management await swManager.cacheModules(['/app.js', 'https://cdn.example.com/lib.js']); await swManager.getCacheStatus(); await swManager.clearCache(); ``` ### IndexedDB Adapter Full SQL-like query support with local storage: #### Features ```javascript // SQL queries await db.query('SELECT * FROM users WHERE age > 18 ORDER BY name'); await db.query('INSERT INTO users (name, email) VALUES ("John", "john@example.com")'); await db.query('UPDATE users SET active = true WHERE id = 1'); await db.query('DELETE FROM users WHERE active = false'); // ORM-style access const users = db.table('users'); await users.create({ name: 'Jane' }); await users.find(1); await users.where({ active: true }); await users.update(1, { name: 'Jane Doe' }); await users.delete(1); // Reactive updates users.onChange(event => { console.log('Change detected:', event); }); ``` ### Web Worker Executor Isolated execution environment for flow nodes: #### Features ```javascript // Execute single node const result = await executor.executeNode({ id: 'transform', code: 'return inputs.map(x => x * 2)' }, context); // Execute flow graph const flowResult = await executor.executeFlow(nodes, edges, { variables: { config: 'value' } }); // Handle suspension if (result.suspended) { // Resume later with state const resumed = await executor.resume(result.state); } // Monitor execution executor.onLog((level, message) => { console.log(`[Worker] ${level}: ${message}`); }); ``` ## Use Cases ### 1. Offline-First Applications ```javascript // App works without internet after initial load const app = FX.create({ offline: true, cache: { strategy: 'cache-first', maxAge: 86400000 // 24 hours } }); ``` ### 2. Client-Side Data Processing ```javascript // Process data entirely in browser const flow = { nodes: [ { id: 'load', code: 'return loadCSV(file)' }, { id: 'clean', code: 'return cleanData(inputs)' }, { id: 'analyze', code: 'return runStatistics(inputs)' }, { id: 'visualize', code: 'return createCharts(inputs)' } ] }; const results = await executor.executeFlow(flow.nodes, flow.edges); ``` ### 3. Privacy-First Applications ```javascript // All data stays in the browser const privateDb = createIndexedDBAdapter('encrypted-db'); // Store sensitive data locally await privateDb.table('secrets').create({ data: await encrypt(sensitiveData), timestamp: Date.now() }); ``` ### 4. Development and Testing ```javascript // Test without backend const mockDb = createIndexedDBAdapter('test-db'); // Seed test data await mockDb.import(testDataset); // Run tests const results = await runTests(mockDb); ``` ## Configuration ### Service Worker Options ```javascript registerFXServiceWorker({ scope: '/', // SW scope updateViaCache: 'none', // Update strategy autoUpdate: true, // Auto-check updates autoUpdateInterval: 60000, // Check interval (ms) onUpdateFound: () => { // Update callback showUpdateNotification(); } }); ``` ### Database Configuration ```javascript const db = createIndexedDBAdapter('my-db'); // Define schemas db.addSchema({ name: 'products', keyPath: 'id', autoIncrement: true, indexes: [ { name: 'sku', keyPath: 'sku', unique: true }, { name: 'category', keyPath: 'category' }, { name: 'price', keyPath: 'price' } ] }); ``` ### Worker Pool Configuration ```javascript const executor = createFlowExecutor( navigator.hardwareConcurrency || 4 // Number of workers ); // Monitor performance const stats = executor.getStats(); console.log('Worker pool:', stats); ``` ## Browser Requirements ### Minimum Requirements - Chrome 89+ / Edge 89+ - Firefox 89+ - Safari 15.4+ ### Required APIs - Service Workers - IndexedDB - Web Workers - Cache API - Structured Cloning ### Recommended Headers ``` Cross-Origin-Embedder-Policy: require-corp Cross-Origin-Opener-Policy: same-origin ``` ## Performance Tips ### 1. Pre-cache Critical Modules ```javascript // Cache on first load await swManager.cacheModules([ '/fx/fx.js', '/app/main.js', 'https://esm.sh/react', 'https://esm.sh/vue' ]); ``` ### 2. Optimize Database Queries ```javascript // Use indexes for frequent queries db.addSchema({ name: 'logs', indexes: [ { name: 'timestamp', keyPath: 'timestamp' }, { name: 'user_timestamp', keyPath: ['userId', 'timestamp'] } ] }); // Use appropriate queries const recent = await db.query( 'SELECT * FROM logs WHERE timestamp > ? ORDER BY timestamp DESC LIMIT 100' ); ``` ### 3. Manage Worker Pool ```javascript // Size pool based on workload const cpuIntensive = createFlowExecutor(navigator.hardwareConcurrency); const ioHeavy = createFlowExecutor(navigator.hardwareConcurrency * 2); const lightweight = createFlowExecutor(2); ``` ## Troubleshooting ### Service Worker Not Registering 1. Check HTTPS or localhost 2. Verify file location 3. Clear browser cache 4. Check console for errors ### IndexedDB Quota Issues ```javascript // Check available storage const estimate = await navigator.storage.estimate(); console.log(`Using ${estimate.usage} of ${estimate.quota} bytes`); // Request persistent storage await navigator.storage.persist(); ``` ### Worker Execution Errors ```javascript // Add error boundaries try { await executor.executeNode(node, context); } catch (error) { console.error('Execution failed:', error); // Handle error } ``` ## Examples ### Complete Standalone App ```javascript // main.js import FX from './fx/fx.js'; import { registerFXServiceWorker } from './fx/plugins/fx-service-worker-register.js'; import { createIndexedDBAdapter } from './fx/plugins/fx-orm-indexeddb.js'; import { createFlowExecutor } from './fx/plugins/fx-flow-worker.js'; async function createStandaloneApp() { // Initialize components const sw = registerFXServiceWorker(); const db = createIndexedDBAdapter('app-db'); const executor = createFlowExecutor(); await db.init(); // Create FX app const app = FX.create({ standalone: true, db: { adapter: db }, flow: { executor }, plugins: [ 'fx-router', 'fx-components', 'fx-jsx' ], routes: { '/': HomePage, '/users': UsersPage, '/settings': SettingsPage } }); // Define components const HomePage = () => (

Standalone FX App

Running entirely in your browser!

); const UsersPage = async () => { const users = await db.table('users').all(); return (

Users

); }; // Mount app app.mount('#app'); return app; } // Initialize on load createStandaloneApp().then(app => { console.log('App running in standalone mode'); }); ``` ## API Reference ### Service Worker Manager ```typescript class FXServiceWorkerManager { constructor(options?: FXServiceWorkerOptions); // Registration isActive(): boolean; getRegistration(): ServiceWorkerRegistration | null; // Cache management cacheModules(modules: string[]): Promise; clearCache(cacheName?: string): Promise; getCacheStatus(): Promise>; // Updates checkForUpdate(): Promise; stopAutoUpdate(): void; // Communication sendMessage(message: ServiceWorkerMessage): Promise; onMessage(type: string, handler: Function): () => void; // Lifecycle unregister(): Promise; } ``` ### IndexedDB Adapter ```typescript class IndexedDBAdapter { constructor(dbName?: string); // Initialization init(): Promise; // SQL queries query(sql: string, params?: any[]): Promise; // Table access table(name: string): TableWrapper; // Schema management addSchema(schema: TableSchema): void; // Events onChange(table: string, callback: Function): () => void; // Import/Export export(): Promise; import(data: any): Promise; // Lifecycle close(): void; deleteDatabase(): Promise; } ``` ### Worker Flow Executor ```typescript class WorkerFlowExecutor { constructor(maxWorkers?: number); // Execution executeNode(node: FlowNode, context: FlowContext): Promise; executeFlow(nodes: FlowNode[], edges: any[], context?: any): Promise; // Monitoring getStats(): WorkerPoolStats; onLog(handler: Function): () => void; // Lifecycle terminate(): void; } ``` ## Contributing We welcome contributions to improve standalone mode: 1. Fork the repository 2. Create a feature branch 3. Make your changes 4. Add tests 5. Submit a pull request ## License FX Standalone Mode is part of the FX Framework and is licensed under the MIT License. ## Support - Documentation: https://fx-framework.dev/docs/standalone - Issues: https://github.com/fx-framework/fx/issues - Discord: https://discord.gg/fx-framework - Twitter: @fx_framework --- **Built with ❀️ for the decentralized web**