# 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
{users.map(user => (
- {user.name}
))}
);
};
// 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**