Examples & Demos
Discover the power of Coherent.js through interactive playground examples. From basic components to advanced patterns, see how pure object syntax makes building UIs intuitive and performant.
Getting Started
Basic Usage
JSCoherentGreeting components with conditional rendering and user cards.
▶️ Run:node examples/basic-usage.js/** * @name Basic Usage * @category Getting Started * @description Greeting components with conditional rendering and user cards. */ export const Greeting = ({ name = 'World', mood = 'happy' }) => ({ div: { className: `greeting greeting--${mood}`, children: [ { h2: { text: `Hello, ${name}!` } }, { p: { text: `You seem ${mood} today` } }, mood === 'fantastic' ? { div: { className: 'celebration', children: [ { span: { text: '🎉 Amazing! 🎉' } } ] } } : null ].filter(Boolean) } }); // User profile component with styling export const UserCard = ({ user }) => ({ div: { className: 'user-card', style: 'border: 1px solid #ccc; padding: 10px; margin: 10px;', children: [ { h3: { text: user.name } }, { p: { text: `Email: ${user.email}` } }, { p: { text: `Role: ${user.role}` } } ] } }); // List component rendering multiple user cards export const UserList = ({ users = [] }) => ({ div: { className: 'user-list', children: [ { h2: { text: 'User List' } }, users.length > 0 ? { ul: { children: users.map(user => ({ li: { key: user.id, children: [UserCard({ user })] } })) } } : { p: { text: 'No users found' } } ] } }); // Sample data for demonstration const sampleUsers = [ { id: 1, name: 'Alice', email: 'alice@example.com', role: 'Admin' }, { id: 2, name: 'Bob', email: 'bob@example.com', role: 'User' }, { id: 3, name: 'Charlie', email: 'charlie@example.com', role: 'User' } ]; // Demo component combining everything const Demo = () => ({ div: { style: 'font-family: system-ui, sans-serif; max-width: 800px; padding: 20px;', children: [ { h1: { text: 'Coherent Framework Demo' } }, { p: { text: 'This page demonstrates basic component usage, composition, and styling.' } }, Greeting({ name: 'Coherent User', mood: 'fantastic' }), UserList({ users: sampleUsers }) ] } }); // Export for playground preview export default Demo();
Components
Component Composition
JSCoherentHOCs, state management, and advanced component patterns.
▶️ Run:node examples/component-composition.js/** * @name Component Composition * @category Components * @description HOCs, state management, and advanced component patterns. */ import { withState } from '@coherent.js/core'; import { makeHydratable, autoHydrate } from '@coherent.js/client'; // Example 1: Basic component composition export const Header = ({ title, subtitle }) => ({ header: { className: 'app-header', children: [ { h1: { text: title } }, subtitle ? { p: { text: subtitle } } : null ].filter(Boolean) } }); export const Footer = ({ copyright }) => ({ footer: { className: 'app-footer', children: [ { p: { text: ` ${new Date().getFullYear()} ${copyright}` } } ] } }); export const Layout = ({ header, footer, children }) => ({ div: { className: 'app-layout', children: [ header, { main: { className: 'app-main', children: Array.isArray(children) ? children : [children] } }, footer ] } }); // Example 2: Higher-order component for loading states export const withLoading = (WrappedComponent) => withState({ loading: false, error: null })(({ state, setState, ...props }) => { if (state.loading) { return { div: { className: 'loading-container', children: [ { h3: { text: 'Loading...' } }, { div: { className: 'spinner', text: '' } } ] } }; } if (state.error) { return { div: { className: 'error-container', children: [ { h3: { text: 'Error Occurred' } }, { p: { text: state.error.message || 'An unknown error occurred' } }, { button: { text: 'Retry', onclick: () => setState({ error: null }) }} ] } }; } // Add loading controls to props const propsWithLoading = { ...props, setLoading: (loading) => setState({ loading }), setError: (error) => setState({ error }) }; return WrappedComponent(propsWithLoading); }); // Example 3: Component composition with mixins export const withTimestamp = (Component) => (props) => ({ div: { className: 'timestamp-wrapper', children: [ Component(props), { small: { text: `Last updated: ${new Date().toLocaleTimeString()}`, className: 'timestamp' }} ] } }); export const withBorder = (Component) => (props) => ({ div: { className: 'border-wrapper', style: 'border: 2px solid #ccc; padding: 10px; margin: 10px 0; border-radius: 4px;', children: [Component(props)] } }); export const SimpleCard = ({ title, content }) => ({ div: { className: 'simple-card', children: [ { h3: { text: title } }, { p: { text: content } } ] } }); // Compose multiple HOCs export const EnhancedCard = withBorder(withTimestamp(SimpleCard)); // Example 4: Form component with state management and hydration support const ContactFormComponent = withState({ name: '', email: '', message: '', submitted: false })(({ state, stateUtils }) => { const { setState } = stateUtils; const handleSubmit = (event) => { event.preventDefault(); console.log('Form submitted:', { name: state.name, email: state.email, message: state.message }); setState({ submitted: true }); // Reset form after 2 seconds setTimeout(() => { setState({ name: '', email: '', message: '', submitted: false }); }, 2000); }; const updateField = (field) => (event) => { setState({ [field]: event.target.value }); }; return { div: { 'data-coherent-component': 'contact-form', children: [ { form: { className: 'contact-form', onsubmit: handleSubmit, children: [ { h2: { text: 'Contact Us' } }, state.submitted ? { div: { className: 'success-message', children: [ { p: { text: '✓ Message sent successfully!' } } ] } } : null, { div: { className: 'form-field', children: [ { label: { text: 'Name', htmlFor: 'contact-name' } }, { input: { id: 'contact-name', type: 'text', value: state.name, placeholder: 'Your name', oninput: updateField('name'), required: true } } ] } }, { div: { className: 'form-field', children: [ { label: { text: 'Email', htmlFor: 'contact-email' } }, { input: { id: 'contact-email', type: 'email', value: state.email, placeholder: 'your@email.com', oninput: updateField('email'), required: true } } ] } }, { div: { className: 'form-field', children: [ { label: { text: 'Message', htmlFor: 'contact-message' } }, { textarea: { id: 'contact-message', value: state.message, placeholder: 'Your message here...', oninput: updateField('message'), rows: 4, required: true } } ] } }, { button: { className: 'btn btn--primary', type: 'submit', text: state.submitted ? 'Sent!' : 'Send Message', disabled: state.submitted } } ].filter(Boolean) } } ] } }; }); export const ContactForm = ContactFormComponent; // Make the contact form hydratable export const HydratableContactForm = makeHydratable(ContactForm, { componentName: 'contact-form' }); // Complete page demonstrating all composition patterns export const demoPage = { html: { children: [ { head: { children: [ { title: { text: 'Component Composition Demo' } }, { style: { text: ` body { font-family: Arial, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; line-height: 1.6; } .app-header { background: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 20px; } .app-footer { background: #f8f9fa; padding: 15px; border-radius: 8px; margin-top: 20px; text-align: center; } .app-main { min-height: 400px; } .simple-card { background: white; padding: 15px; margin: 10px 0; } .timestamp { color: #666; font-style: italic; margin-top: 10px; display: block; } .contact-form { background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0; } .form-field { margin-bottom: 15px; } .form-field label { display: block; margin-bottom: 5px; font-weight: bold; } .form-field input, .form-field textarea { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; } .btn { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; } .btn--primary { background: #007bff; color: white; } .btn--primary:hover { background: #0056b3; } .btn:disabled { background: #6c757d; cursor: not-allowed; opacity: 0.6; } .success-message { background: #d4edda; color: #155724; padding: 10px; border-radius: 4px; margin: 10px 0; border: 1px solid #c3e6cb; } ` } } ] } }, { body: { children: [ Layout({ header: Header({ title: 'Component Composition Demo', subtitle: 'Exploring different composition patterns in Coherent.js' }), footer: Footer({ copyright: 'Coherent.js Examples' }), children: [ { h2: { text: 'Enhanced Card with Multiple HOCs' } }, { p: { text: 'This card demonstrates composition using withBorder and withTimestamp HOCs:' } }, EnhancedCard({ title: 'Enhanced Card Example', content: 'This card has a border and timestamp automatically added through composition.' }), { h2: { text: 'Interactive Form with State' } }, { p: { text: 'This form demonstrates state management, event handling, and client-side hydration:' } }, HydratableContactForm.renderWithHydration() ] }) ] } } ] } }; // Set up client-side hydration (browser only) if (typeof window !== 'undefined') { // Component registry for hydration window.componentRegistry = { 'contact-form': HydratableContactForm }; // Auto-hydrate when DOM is ready document.addEventListener('DOMContentLoaded', () => { setTimeout(() => { autoHydrate(window.componentRegistry); console.log('✅ Component composition hydration complete!'); }, 100); }); } // Export the demo page as default for live preview export default demoPage;Error Boundaries
JSCoherentGraceful error handling with fallback components.
▶️ Run:node examples/error-boundary-demo.js/** * @name Error Boundaries * @category Components * @description Graceful error handling with fallback components. */ import { render, createErrorBoundary, createErrorFallback, withErrorBoundary, createAsyncErrorBoundary, createGlobalErrorHandler } from '@coherent.js/core'; console.log('\n=== Coherent.js Error Boundary Demo ===\n'); // Example 1: Basic Error Boundary console.log('--- Example 1: Basic Error Boundary ---\n'); const BuggyComponent = () => { throw new Error('Oops! Something went wrong'); }; const basicBoundary = createErrorBoundary({ fallback: { div: { className: 'error', text: 'An error occurred. Please try again.' } }, onError: (error, errorInfo) => { console.log('Error caught:', error.message); console.log('Error info:', errorInfo); } }); const SafeBuggyComponent = basicBoundary(BuggyComponent); try { const result = SafeBuggyComponent(); console.log('Rendered fallback:', render(result)); } catch (error) { console.log('Error was not caught!'); } // Example 2: Custom Error Fallback console.log('\n--- Example 2: Custom Error Fallback ---\n'); const customFallback = createErrorFallback({ title: 'Oops! Something went wrong', showError: true, showStack: false, showReset: true, className: 'custom-error' }); const customBoundary = createErrorBoundary({ fallback: customFallback, onError: (error) => console.log('Custom boundary caught:', error.message) }); const AnotherBuggyComponent = () => { throw new Error('Custom error message'); }; const SafeCustomComponent = customBoundary(AnotherBuggyComponent); const customResult = SafeCustomComponent(); console.log('Custom fallback rendered'); // Example 3: Error Boundary with Reset console.log('\n--- Example 3: Error Boundary with Reset ---\n'); let shouldFail = true; const SometimesBuggyComponent = () => { if (shouldFail) { throw new Error('Component failed!'); } return { div: { text: 'Component rendered successfully!' } }; }; const resetBoundary = createErrorBoundary({ fallback: (error, errorInfo, context) => ({ div: { children: [ { p: { text: `Error: ${error.message}` } }, { button: { text: 'Reset and Try Again', onclick: () => { console.log('Resetting error boundary...'); shouldFail = false; context.reset(); } } } ] } }), onReset: () => console.log('Error boundary reset!') }); const SafeSometimesBuggyComponent = resetBoundary(SometimesBuggyComponent); console.log('First render (will fail):'); const result1 = SafeSometimesBuggyComponent(); console.log(render(result1)); console.log('\nAfter reset (should succeed):'); shouldFail = false; const result2 = SafeSometimesBuggyComponent(); console.log(render(result2)); // Example 4: Error Boundary with Reset Keys console.log('\n--- Example 4: Error Boundary with Reset Keys ---\n'); const UserComponent = ({ userId }) => { if (userId === 'bad-user') { throw new Error('Invalid user!'); } return { div: { text: `User: ${userId}` } }; }; const userBoundary = createErrorBoundary({ fallback: { div: { text: 'Failed to load user' } }, resetKeys: ['userId'], resetOnPropsChange: true, onReset: () => console.log('User changed, resetting error boundary') }); const SafeUserComponent = userBoundary(UserComponent); console.log('Rendering with bad user:'); const badUserResult = SafeUserComponent({ userId: 'bad-user' }); console.log(render(badUserResult)); console.log('\nRendering with good user (auto-reset):'); const goodUserResult = SafeUserComponent({ userId: 'good-user' }); console.log(render(goodUserResult)); // Example 5: Max Errors console.log('\n--- Example 5: Max Errors ---\n'); let attemptCount = 0; const AlwaysBuggyComponent = () => { attemptCount++; throw new Error(`Attempt ${attemptCount} failed`); }; const maxErrorsBoundary = createErrorBoundary({ fallback: (error, errorInfo, context) => { if (context.permanent) { return { div: { className: 'permanent-error', text: 'Too many errors. Component disabled.' } }; } return { div: { text: `Error ${context.errorCount}/3: ${error.message}` } }; }, maxErrors: 3, onError: (error) => console.log('Error caught:', error.message) }); const SafeAlwaysBuggyComponent = maxErrorsBoundary(AlwaysBuggyComponent); console.log('Attempt 1:'); console.log(render(SafeAlwaysBuggyComponent())); console.log('\nAttempt 2:'); console.log(render(SafeAlwaysBuggyComponent())); console.log('\nAttempt 3:'); console.log(render(SafeAlwaysBuggyComponent())); console.log('\nAttempt 4 (permanent fallback):'); console.log(render(SafeAlwaysBuggyComponent())); // Example 6: Auto-Reset Timeout console.log('\n--- Example 6: Auto-Reset Timeout ---\n'); const timeoutBoundary = createErrorBoundary({ fallback: { div: { text: 'Error! Will auto-reset in 2 seconds...' } }, resetTimeout: 2000, onReset: () => console.log('Auto-reset triggered!'), onError: (error) => console.log('Error caught, timer started') }); // Example 7: Wrapping Multiple Components console.log('\n--- Example 7: Wrapping Multiple Components ---\n'); const Header = () => ({ header: { text: 'Header' } }); const Content = () => { throw new Error('Content failed'); }; const Footer = () => ({ footer: { text: 'Footer' } }); const safeComponents = withErrorBoundary( { fallback: (error) => ({ div: { className: 'component-error', text: `Component error: ${error.message}` } }) }, { Header, Content, Footer } ); console.log('Safe Header:', render(safeComponents.Header())); console.log('Safe Content:', render(safeComponents.Content())); console.log('Safe Footer:', render(safeComponents.Footer())); // Example 8: Async Error Boundary console.log('\n--- Example 8: Async Error Boundary ---\n'); const AsyncComponent = async () => { await new Promise(resolve => setTimeout(resolve, 100)); return { div: { text: 'Async content loaded!' } }; }; const FailingAsyncComponent = async () => { await new Promise(resolve => setTimeout(resolve, 100)); throw new Error('Async load failed!'); }; const asyncBoundary = createAsyncErrorBoundary({ fallback: { div: { text: 'Loading...' } }, errorFallback: { div: { text: 'Failed to load async content' } }, timeout: 5000, onError: (error) => console.log('Async error:', error.message) }); const SafeAsyncComponent = asyncBoundary(AsyncComponent); const SafeFailingAsyncComponent = asyncBoundary(FailingAsyncComponent); console.log('Loading async component...'); SafeAsyncComponent().then(result => { console.log('Success:', render(result)); }); console.log('Loading failing async component...'); SafeFailingAsyncComponent().then(result => { console.log('Fallback:', render(result)); }); // Example 9: Global Error Handler console.log('\n--- Example 9: Global Error Handler ---\n'); const globalHandler = createGlobalErrorHandler({ maxErrors: 50, onError: (error, context) => { console.log(`Global handler caught: ${error.message}`); console.log(`Context:`, context); } }); // Simulate some errors globalHandler.captureError(new Error('Error 1'), { component: 'Header' }); globalHandler.captureError(new Error('Error 2'), { component: 'Content' }); globalHandler.captureError(new Error('Error 3'), { component: 'Footer' }); console.log('\nGlobal error stats:', globalHandler.getStats()); console.log('Captured errors:', globalHandler.getErrors().length); // Example 10: Nested Error Boundaries console.log('\n--- Example 10: Nested Error Boundaries ---\n'); const InnerBuggyComponent = () => { throw new Error('Inner component failed'); }; const OuterComponent = () => { const innerBoundary = createErrorBoundary({ fallback: { div: { className: 'inner-error', text: 'Inner error caught' } } }); const SafeInner = innerBoundary(InnerBuggyComponent); return { div: { className: 'outer', children: [ { h2: { text: 'Outer Component' } }, SafeInner() ] } }; }; const outerBoundary = createErrorBoundary({ fallback: { div: { text: 'Outer error caught' } } }); const SafeOuterComponent = outerBoundary(OuterComponent); console.log('Nested boundaries result:'); console.log(render(SafeOuterComponent())); // Example 11: Error Boundary with Component Tree console.log('\n--- Example 11: Error Boundary with Component Tree ---\n'); const Page = () => ({ html: { children: [ { head: { children: [ { title: { text: 'Error Boundary Demo' } } ] } }, { body: { children: [ { h1: { text: 'My Application' } }, (() => { throw new Error('Body content failed'); })() ] } } ] } }); const pageBoundary = createErrorBoundary({ fallback: createErrorFallback({ title: 'Page Error', showError: true, showReset: true }) }); const SafePage = pageBoundary(Page); console.log('Page with error boundary:'); const pageResult = SafePage(); console.log(render(pageResult).substring(0, 200) + '...'); console.log('\n=== Demo Complete ===\n'); console.log('Error boundaries provide robust error handling for your components!'); console.log('Use them to prevent entire application crashes from single component failures.');State Management
JSCoherentReactive state with observables, computed properties, and persistence.
▶️ Run:node examples/state-management-demo.js/** * @name State Management * @category Components * @description Reactive state with observables, computed properties, and persistence. */ import { render } from '@coherent.js/core'; import { observable, computed, createReactiveState, withLocalStorage, createValidatedState, validators, provideContext, useContext } from '@coherent.js/state'; // ============================================================================= // Example 1: Basic Observables // ============================================================================= console.log('\n📦 Example 1: Basic Observables'); const count = observable(0); const doubled = computed(() => count.value * 2); // Watch for changes count.watch((newValue, oldValue) => { console.log(`Count changed from ${oldValue} to ${newValue}`); console.log(`Doubled is now: ${doubled.value}`); }); count.value = 5; // Logs: "Count changed from 0 to 5", "Doubled is now: 10" count.value = 10; // Logs: "Count changed from 5 to 10", "Doubled is now: 20" // ============================================================================= // Example 2: Reactive State Object // ============================================================================= console.log('\n📦 Example 2: Reactive State Object'); const appState = createReactiveState({ user: { name: 'Guest', isLoggedIn: false }, theme: 'dark', notifications: [] }); // Watch specific paths appState.watch('user.name', (newName, oldName) => { console.log(`User name changed from "${oldName}" to "${newName}"`); }); appState.watch('theme', (newTheme) => { console.log(`Theme changed to: ${newTheme}`); }); // Update state appState.set('user.name', 'John Doe'); appState.set('user.isLoggedIn', true); appState.set('theme', 'light'); // Get values console.log('Current user:', appState.get('user')); console.log('Current theme:', appState.get('theme')); // ============================================================================= // Example 3: State Persistence with LocalStorage // ============================================================================= console.log('\n📦 Example 3: State Persistence'); // Note: This example is for browser environments // In Node.js, this will use a mock localStorage const userPrefs = withLocalStorage( { theme: 'dark', language: 'en', fontSize: 14 }, 'user-preferences' ); console.log('Loaded preferences:', userPrefs.toObject()); // Updates are automatically persisted userPrefs.set('theme', 'light'); userPrefs.set('fontSize', 16); console.log('Updated preferences:', userPrefs.toObject()); // These changes are now saved to localStorage! // ============================================================================= // Example 4: State Validation // ============================================================================= console.log('\n📦 Example 4: State Validation'); const userForm = createValidatedState( { email: '', age: 0, username: '' }, { validators: { email: validators.email('Invalid email format'), age: validators.range(18, 120, 'Age must be between 18 and 120'), username: validators.minLength(3, 'Username must be at least 3 characters') } } ); // Valid updates try { userForm.set('email', 'user@example.com'); userForm.set('age', 25); userForm.set('username', 'johndoe'); console.log('✓ All validations passed!'); console.log('Form data:', userForm.toObject()); } catch (error) { console.error('✗ Validation error:', error.message); } // Invalid update try { userForm.set('email', 'invalid-email'); } catch (error) { console.error('✗ Validation error:', error.message); } // ============================================================================= // Example 5: SSR-Compatible State (Context API) // ============================================================================= console.log('\n📦 Example 5: SSR Context API'); // Simulate a request handler function handleRequest(userId) { const requestState = { userId, timestamp: new Date().toISOString(), theme: 'dark' }; // Provide context for this request provideContext('request', requestState); // Render components that use the context const html = render(UserDashboard()); console.log('Rendered HTML:', html); return html; } function UserDashboard() { const requestState = useContext('request'); return { div: { className: `dashboard theme-${requestState.theme}`, children: [ { h1: { text: 'User Dashboard' } }, { p: { text: `User ID: ${requestState.userId}` } }, { p: { text: `Request time: ${requestState.timestamp}` } } ] } }; } // Simulate requests handleRequest(123); handleRequest(456); // ============================================================================= // Example 6: Complete Application State // ============================================================================= console.log('\n📦 Example 6: Complete Application State'); // Combining features: reactive + persistent + validated const appConfig = createValidatedState( { user: { id: null, name: '', email: '' }, settings: { theme: 'dark', notifications: true }, cart: [] }, { validators: { 'user.email': validators.email(), 'settings.theme': validators.custom((value) => { return ['dark', 'light', 'auto'].includes(value) ? null : 'Invalid theme'; }) } } ); // Make it persistent const persistentConfig = withLocalStorage(appConfig, 'app-config'); // Watch for changes persistentConfig.watch('user', (newUser) => { console.log('User updated:', newUser); }); persistentConfig.watch('settings.theme', (newTheme) => { console.log('Theme changed to:', newTheme); }); // Update state (validated and persisted) try { persistentConfig.set('user', { id: 123, name: 'John Doe', email: 'john@example.com' }); persistentConfig.set('settings.theme', 'light'); persistentConfig.set('cart', [ { id: 1, name: 'Product A', price: 29.99 }, { id: 2, name: 'Product B', price: 39.99 } ]); console.log('\n✓ All state updated successfully!'); console.log('Final state:', JSON.stringify(persistentConfig.toObject(), null, 2)); } catch (error) { console.error('✗ Error:', error.message); } // ============================================================================= // Example 7: Reactive UI Component // ============================================================================= console.log('\n📦 Example 7: Reactive UI Component'); // Create reactive counter const counter = observable(0); function CounterComponent() { // This would normally re-render automatically on changes return { div: { className: 'counter', children: [ { h2: { text: 'Counter Example' } }, { p: { text: `Count: ${counter.value}` } }, { button: { text: 'Increment', onclick: () => { counter.value++; console.log(`Counter incremented to: ${counter.value}`); } } } ] } }; } // Render initial state console.log('Initial render:', render(CounterComponent())); // Simulate clicks counter.value++; counter.value++; counter.value++; console.log('After increments:', render(CounterComponent())); // ============================================================================= // Summary // ============================================================================= console.log('\n' + '='.repeat(80)); console.log('📝 Summary of @coherent.js/state Features:'); console.log('='.repeat(80)); console.log(''); console.log('✓ Observable values with automatic dependency tracking'); console.log('✓ Computed properties that auto-update'); console.log('✓ Reactive state objects with path-based watching'); console.log('✓ LocalStorage/SessionStorage/IndexedDB persistence'); console.log('✓ Built-in validation with customizable validators'); console.log('✓ SSR-compatible context API'); console.log('✓ Type-safe with TypeScript definitions'); console.log('✓ Zero dependencies (except @coherent.js/core peer)'); console.log(''); console.log('🚀 Ready for production use in v1.0.0-beta.2!'); console.log('='.repeat(80));
Features
Event Bus
JSCoherentPublish-subscribe event system for component communication.
▶️ Run:node examples/event-bus-demo.js/** * @name Event Bus * @category Features * @description Publish-subscribe event system for component communication. */ import { render, eventSystem, withEventBus, createActionHandlers } from '@coherent.js/core'; // Example: Todo Application with Event-Driven Architecture function createTodoApp() { // Register action handlers for todo operations eventSystem.registerActions({ 'add-todo': ({ data, state, setState, emit }) => { const text = data.text || data.todoText; if (!text || !text.trim()) return; const newTodo = { id: Date.now(), text: text.trim(), completed: false }; setState({ todos: [...(state.todos || []), newTodo], newTodoText: '' }); emit('notification:success', { message: `Todo "${text}" added!` }); }, 'toggle-todo': ({ data, state, setState, emit }) => { const todoId = parseInt(data.todoId); setState({ todos: state.todos.map(todo => todo.id === todoId ? { ...todo, completed: !todo.completed } : todo ) }); emit('todo:toggled', { todoId, completed: !state.todos.find(t => t.id === todoId)?.completed }); }, 'delete-todo': ({ data, state, setState, emit }) => { const todoId = parseInt(data.todoId); const todo = state.todos.find(t => t.id === todoId); setState({ todos: state.todos.filter(todo => todo.id !== todoId) }); emit('notification:info', { message: `Todo "${todo?.text}" deleted` }); }, 'clear-completed': ({ state, setState, emit }) => { const completedCount = state.todos.filter(todo => todo.completed).length; setState({ todos: state.todos.filter(todo => !todo.completed) }); emit('notification:info', { message: `${completedCount} completed todos cleared` }); }, 'filter-todos': ({ data, state, setState }) => { setState({ filter: data.filter || 'all' }); } }); // Register modal actions eventSystem.registerActions(createActionHandlers.modal('todo-help')); // Register notification handlers eventSystem.on('notification:*', (data, eventName) => { console.log(`[Notification] ${eventName}:`, data.message); // In a real app, this would update a notification component }); // Register todo event handlers eventSystem.on('todo:toggled', (data) => { console.log(`[Todo] Toggled todo ${data.todoId} to ${data.completed ? 'completed' : 'pending'}`); }); // Component using event bus integration const TodoApp = withEventBus({ scope: 'todo-app', events: { 'app:mounted': () => { console.log('[TodoApp] Application mounted'); } }, debug: true })(({ eventBus, eventUtils, ...props }) => { const state = props.state || { todos: [ { id: 1, text: 'Learn Coherent.js Event Bus', completed: false }, { id: 2, text: 'Build an awesome app', completed: false } ], filter: 'all', newTodoText: '' }; const filteredTodos = state.todos.filter(todo => { switch (state.filter) { case 'active': return !todo.completed; case 'completed': return todo.completed; default: return true; } }); const stats = { total: state.todos.length, completed: state.todos.filter(todo => todo.completed).length, active: state.todos.filter(todo => !todo.completed).length }; return { div: { className: 'todo-app', 'data-coherent-component': 'TodoApp', 'data-coherent-state': JSON.stringify(state), children: [ // Header { header: { className: 'todo-header', children: [ { h1: { text: 'Event-Driven Todos' } }, { button: { 'data-action': 'open-modal', 'data-modal-id': 'todo-help', className: 'help-btn', text: '?' } } ] } }, // Add todo form { form: { className: 'add-todo-form', 'data-action': 'add-todo', children: [ { input: { type: 'text', name: 'todoText', placeholder: 'What needs to be done?', value: state.newTodoText, 'data-action': 'update-input', required: true } }, { button: { type: 'submit', text: 'Add Todo' } } ] } }, // Filter buttons { div: { className: 'filter-controls', children: ['all', 'active', 'completed'].map(filter => ({ button: { 'data-action': 'filter-todos', 'data-filter': filter, className: `filter-btn ${state.filter === filter ? 'active' : ''}`, text: filter.charAt(0).toUpperCase() + filter.slice(1) } })) } }, // Todo list { ul: { className: 'todo-list', children: filteredTodos.map(todo => ({ li: { className: `todo-item ${todo.completed ? 'completed' : ''}`, 'data-todo-id': todo.id, children: [ { input: { type: 'checkbox', checked: todo.completed, 'data-action': 'toggle-todo', 'data-todo-id': todo.id } }, { span: { className: 'todo-text', text: todo.text } }, { button: { 'data-action': 'delete-todo', 'data-todo-id': todo.id, className: 'delete-btn', text: '×' } } ] } })) } }, // Stats and actions { footer: { className: 'todo-footer', children: [ { div: { className: 'todo-stats', children: [ { span: { text: `${stats.total} total, ${stats.active} active, ${stats.completed} completed` } } ] } }, stats.completed > 0 ? { button: { 'data-action': 'clear-completed', className: 'clear-completed-btn', text: 'Clear Completed' } } : null ].filter(Boolean) } }, // Help modal (hidden by default) { div: { id: 'todo-help', className: 'modal hidden', children: [ { div: { className: 'modal-content', children: [ { h3: { text: 'Event-Driven Todo Help' } }, { p: { text: 'This todo app demonstrates declarative event handling:' } }, { ul: { children: [ { li: { text: 'Click checkboxes to toggle completion' } }, { li: { text: 'Click × to delete todos' } }, { li: { text: 'Use filter buttons to view different states' } }, { li: { text: 'All actions use data-action attributes' } } ] } }, { button: { 'data-action': 'close-modal', 'data-modal-id': 'todo-help', text: 'Close' } } ] } } ] } } ] } }; }); return TodoApp; } // Example: Product Catalog with CRUD operations function createProductCatalog() { // Register CRUD actions for products eventSystem.registerActions(createActionHandlers.crud({ entityName: 'product', onCreate: (data) => console.log('Creating product:', data), onUpdate: (data) => console.log('Updating product:', data), onDelete: (data) => console.log('Deleting product:', data), onRead: (data) => console.log('Reading product:', data) })); // Register form actions eventSystem.registerActions(createActionHandlers.form({ onSubmit: (formData) => { eventSystem.emit('product:create', formData); }, onValidate: (formData) => { return formData.name && formData.name.trim().length > 0; } })); const ProductCatalog = ({ products = [] }) => ({ div: { className: 'product-catalog', children: [ { h2: { text: 'Product Catalog' } }, // Add product form { form: { className: 'add-product-form', 'data-action': 'submit-form', children: [ { input: { type: 'text', name: 'name', placeholder: 'Product name', required: true } }, { input: { type: 'number', name: 'price', placeholder: 'Price', min: '0', step: '0.01' } }, { button: { type: 'submit', text: 'Add Product' } } ] } }, // Product list { div: { className: 'product-list', children: products.map(product => ({ div: { className: 'product-card', 'data-product-id': product.id, children: [ { h3: { text: product.name } }, { p: { text: `${product.price}` } }, { button: { 'data-action': 'update-product', 'data-product-id': product.id, text: 'Edit' } }, { button: { 'data-action': 'delete-product', 'data-product-id': product.id, className: 'danger', text: 'Delete' } } ] } })) } } ] } }); return ProductCatalog; } // Demo runner function runDemo() { console.log('🚀 Coherent.js Event Bus Demo'); console.log('===============================\n'); // Create components const TodoApp = createTodoApp(); const ProductCatalog = createProductCatalog(); // Render todo app console.log('📝 Todo Application:'); const todoHTML = render(TodoApp()); console.log('Rendered HTML length:', todoHTML.length, 'characters'); // Render product catalog console.log('\n🛍️ Product Catalog:'); const catalogHTML = render(ProductCatalog({ products: [ { id: 1, name: 'Coherent.js Book', price: 29.99 }, { id: 2, name: 'Event Bus Guide', price: 19.99 } ] })); console.log('Rendered HTML length:', catalogHTML.length, 'characters'); // Demonstrate event system stats console.log('\n📊 Event Bus Statistics:'); console.log(eventSystem.getStats()); // Demonstrate scoped events console.log('\n🎯 Scoped Events Demo:'); const userScope = eventSystem.createScope('user'); const adminScope = eventSystem.createScope('admin'); userScope.on('action', (data) => { console.log('[User Scope] Action:', data); }); adminScope.on('action', (data) => { console.log('[Admin Scope] Action:', data); }); userScope.emitSync('action', { type: 'user-click' }); adminScope.emitSync('action', { type: 'admin-action' }); // Test wildcard events console.log('\n🌟 Wildcard Events Demo:'); eventSystem.on('demo:*', (data, event) => { console.log(`[Wildcard] Caught ${event}:`, data); }); eventSystem.emitSync('demo:test1', { message: 'First test' }); eventSystem.emitSync('demo:test2', { message: 'Second test' }); console.log('\n✅ Demo completed successfully!'); console.log('\nIn a real application, you would:'); console.log('1. Include the event system in your main app'); console.log('2. Use data-action attributes in your HTML'); console.log('3. Register action handlers for your use cases'); console.log('4. Leverage event communication between components'); } // HTML/CSS for complete demo const demoCSS = ` <style> .todo-app { max-width: 600px; margin: 0 auto; padding: 20px; font-family: Arial, sans-serif; } .todo-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .help-btn { width: 30px; height: 30px; border-radius: 50%; border: 1px solid #ccc; background: #f0f0f0; cursor: pointer; } .add-todo-form { display: flex; margin-bottom: 20px; } .add-todo-form input { flex: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px 0 0 4px; } .add-todo-form button { padding: 10px 20px; border: 1px solid #007bff; background: #007bff; color: white; border-radius: 0 4px 4px 0; cursor: pointer; } .filter-controls { margin-bottom: 20px; } .filter-btn { margin-right: 10px; padding: 5px 15px; border: 1px solid #ccc; background: white; cursor: pointer; } .filter-btn.active { background: #007bff; color: white; } .todo-list { list-style: none; padding: 0; } .todo-item { display: flex; align-items: center; padding: 10px; border-bottom: 1px solid #eee; } .todo-item.completed .todo-text { text-decoration: line-through; color: #888; } .todo-text { flex: 1; margin: 0 10px; } .delete-btn { background: #dc3545; color: white; border: none; width: 25px; height: 25px; border-radius: 50%; cursor: pointer; } .todo-footer { margin-top: 20px; display: flex; justify-content: space-between; align-items: center; } .clear-completed-btn { padding: 5px 15px; border: 1px solid #dc3545; background: #dc3545; color: white; border-radius: 4px; cursor: pointer; } .modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); display: flex; justify-content: center; align-items: center; } .modal.hidden { display: none; } .modal-content { background: white; padding: 20px; border-radius: 8px; max-width: 500px; } .product-catalog { max-width: 800px; margin: 0 auto; padding: 20px; } .add-product-form { display: flex; gap: 10px; margin-bottom: 20px; } .add-product-form input { padding: 10px; border: 1px solid #ccc; border-radius: 4px; } .product-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 20px; } .product-card { border: 1px solid #ccc; padding: 15px; border-radius: 8px; } .product-card button { margin-right: 10px; padding: 5px 10px; border: 1px solid #ccc; border-radius: 4px; cursor: pointer; } .product-card button.danger { background: #dc3545; color: white; border-color: #dc3545; } </style> `; export { runDemo, demoCSS, createTodoApp, createProductCatalog }; // Run the demo if this file is executed directly if (import.meta.main) { runDemo(); }Plugin System
JSCoherentExtensible plugin architecture with lifecycle hooks.
▶️ Run:node examples/plugin-system-demo.js/** * @name Plugin System * @category Features * @description Extensible plugin architecture with lifecycle hooks. */ import { render, createPluginManager, createPlugin, PluginHooks, createPerformancePlugin, createDevLoggerPlugin, createCachePlugin } from '@coherent.js/core'; // Create plugin manager const pluginManager = createPluginManager({ debug: true, silentErrors: false }); // Example 1: Using built-in plugins console.log('\n=== Example 1: Built-in Plugins ===\n'); // Add performance monitoring const perfPlugin = createPerformancePlugin({ threshold: 10, logSlowRenders: true }); pluginManager.use(perfPlugin); // Add development logger const loggerPlugin = createDevLoggerPlugin({ logRenders: true, logStateChanges: true }); pluginManager.use(loggerPlugin); // Add caching const cachePlugin = createCachePlugin({ maxSize: 50, ttl: 30000 }); pluginManager.use(cachePlugin); // Example 2: Creating a custom plugin console.log('\n=== Example 2: Custom Plugin ===\n'); const customPlugin = createPlugin({ name: 'custom-transformer', version: '1.0.0', hooks: { [PluginHooks.BEFORE_RENDER]: (component) => { console.log('[Custom Plugin] Transforming component...'); // Add a custom class to all divs if (component.div) { component.div.className = (component.div.className || '') + ' custom-class'; } return component; }, [PluginHooks.AFTER_RENDER]: (result) => { console.log('[Custom Plugin] Render complete!'); return result; } }, setup(manager) { console.log('[Custom Plugin] Setup complete'); }, cleanup() { console.log('[Custom Plugin] Cleanup complete'); } }); pluginManager.use(customPlugin); // Example 3: Plugin with dependencies console.log('\n=== Example 3: Plugin Dependencies ===\n'); const dependentPlugin = createPlugin({ name: 'dependent-plugin', version: '1.0.0', dependencies: ['custom-transformer'], // Requires custom-transformer hooks: { [PluginHooks.AFTER_RENDER]: (result) => { console.log('[Dependent Plugin] Processing after custom-transformer'); return result; } } }); pluginManager.use(dependentPlugin); // Example 4: Using hooks console.log('\n=== Example 4: Hook Execution ===\n'); // Test component const testComponent = { div: { className: 'test', children: [ { h1: { text: 'Plugin System Demo' } }, { p: { text: 'This component is processed by multiple plugins' } } ] } }; // Execute hooks async function demonstrateHooks() { console.log('\n--- Before Render Hook ---'); const transformed = await pluginManager.callHook( PluginHooks.BEFORE_RENDER, testComponent ); console.log('\n--- Rendering Component ---'); const html = render(transformed); console.log('\n--- After Render Hook ---'); await pluginManager.callHook(PluginHooks.AFTER_RENDER, html); console.log('\n--- Rendered HTML ---'); console.log(html); } await demonstrateHooks(); // Example 5: Plugin statistics console.log('\n=== Example 5: Plugin Statistics ===\n'); const stats = pluginManager.getStats(); console.log('Plugin Statistics:'); console.log(`- Total Plugins: ${stats.pluginCount}`); console.log(`- Total Hooks: ${stats.hookCount}`); console.log(`- Enabled: ${stats.enabled}`); console.log('\nInstalled Plugins:'); stats.plugins.forEach(plugin => { console.log(` • ${plugin.name} v${plugin.version}`); if (plugin.dependencies.length > 0) { console.log(` Dependencies: ${plugin.dependencies.join(', ')}`); } }); console.log('\nRegistered Hooks:'); stats.hooks.forEach(hook => { console.log(` • ${hook.name}: ${hook.handlerCount} handler(s)`); }); // Example 6: Performance metrics console.log('\n=== Example 6: Performance Metrics ===\n'); if (perfPlugin.getMetrics) { const metrics = perfPlugin.getMetrics(); console.log('Performance Metrics:'); console.log(`- Total Renders: ${metrics.renders}`); console.log(`- Average Time: ${metrics.averageTime.toFixed(2)}ms`); console.log(`- Slow Renders: ${metrics.slowRenders}`); } // Example 7: Cache statistics console.log('\n=== Example 7: Cache Statistics ===\n'); if (cachePlugin.getStats) { const cacheStats = cachePlugin.getStats(); console.log('Cache Statistics:'); console.log(`- Current Size: ${cacheStats.size}`); console.log(`- Max Size: ${cacheStats.maxSize}`); console.log(`- TTL: ${cacheStats.ttl}ms`); } // Example 8: Uninstalling plugins console.log('\n=== Example 8: Plugin Lifecycle ===\n'); console.log('Uninstalling custom-transformer...'); try { pluginManager.unuse('custom-transformer'); } catch (error) { console.error('Cannot uninstall:', error.message); console.log('(This is expected because dependent-plugin requires it)'); } console.log('\nUninstalling dependent-plugin first...'); pluginManager.unuse('dependent-plugin'); console.log('Now uninstalling custom-transformer...'); pluginManager.unuse('custom-transformer'); console.log('\nRemaining plugins:', pluginManager.getPlugins().map(p => p.name)); // Example 9: Disabling plugin system console.log('\n=== Example 9: Enabling/Disabling ===\n'); console.log('Disabling plugin system...'); pluginManager.disable(); console.log('Plugin system enabled:', pluginManager.enabled); console.log('\nRe-enabling plugin system...'); pluginManager.enable(); console.log('Plugin system enabled:', pluginManager.enabled); // Example 10: Creating an advanced plugin console.log('\n=== Example 10: Advanced Plugin ===\n'); const advancedPlugin = createPlugin({ name: 'seo-enhancer', version: '1.0.0', hooks: { [PluginHooks.BEFORE_RENDER]: (component, context) => { // Add SEO meta tags if (component.html && component.html.children) { const head = component.html.children.find(child => child.head); if (head && head.head) { if (!head.head.children) { head.head.children = []; } // Add meta description head.head.children.push({ meta: { name: 'description', content: 'Enhanced by SEO plugin' } }); console.log('[SEO Plugin] Added meta tags'); } } return component; } } }); pluginManager.use(advancedPlugin); // Test SEO enhancement const pageComponent = { html: { children: [ { head: { children: [ { title: { text: 'My Page' } } ] } }, { body: { children: [ { h1: { text: 'Welcome' } } ] } } ] } }; const enhancedComponent = await pluginManager.callHook( PluginHooks.BEFORE_RENDER, pageComponent ); console.log('\nEnhanced HTML:'); console.log(render(enhancedComponent)); console.log('\n=== Demo Complete ===\n'); console.log('The plugin system provides a powerful way to extend Coherent.js!'); console.log('Create your own plugins to add custom functionality.');Runtime Features
JSCoherentCore runtime utilities including rendering, caching, and monitoring.
▶️ Run:node examples/runtime-features-demo.js/** * @name Runtime Features * @category Features * @description Core runtime utilities including rendering, caching, and monitoring. */ import { createRuntime, RuntimeEnvironment } from '@coherent.js/runtime'; console.log('\n=== Coherent.js Runtime Features Demo ===\n'); // Example 1: Edge Runtime with Streaming console.log('--- Example 1: Edge Runtime with Streaming ---\n'); const edgeRuntime = await createRuntime(RuntimeEnvironment.EDGE, { streaming: true, streamChunkSize: 512 }); const edgeApp = edgeRuntime.createApp(); edgeApp.get('/stream', async () => { const largeComponent = { html: { children: [ { head: { children: [ { title: { text: 'Streaming Demo' } } ] } }, { body: { children: Array.from({ length: 100 }, (_, i) => ({ div: { className: 'item', text: `Item ${i + 1}: ${'Lorem ipsum '.repeat(10)}` } })) } } ] } }; return await edgeRuntime.renderStream(largeComponent); }); console.log('✅ Edge runtime with streaming configured'); console.log(' Route: /stream'); console.log(' Chunk size: 512 bytes'); // Example 2: Middleware in Edge Runtime console.log('\n--- Example 2: Middleware in Edge Runtime ---\n'); const middlewareRuntime = await createRuntime(RuntimeEnvironment.EDGE); const middlewareApp = middlewareRuntime.createApp(); // Logging middleware middlewareApp.use(async (context, next) => { const start = Date.now(); console.log(`[Middleware] ${context.method} ${context.pathname}`); await next(); const duration = Date.now() - start; console.log(`[Middleware] Completed in ${duration}ms`); }); // Auth middleware middlewareApp.use(async (context, next) => { const authHeader = context.headers['authorization']; if (!authHeader && context.pathname.startsWith('/protected')) { context.response = new Response('Unauthorized', { status: 401 }); return; } context.state.user = authHeader ? { id: 1, name: 'User' } : null; await next(); }); // CORS middleware middlewareApp.use(async (context, next) => { context.state.cors = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE', 'Access-Control-Allow-Headers': 'Content-Type, Authorization' }; await next(); }); middlewareApp.get('/public', async (context) => { return { component: () => ({ div: { text: 'Public page - no auth required' } }) }; }); middlewareApp.get('/protected', async (context) => { return { component: () => ({ div: { text: `Welcome ${context.state.user?.name || 'Guest'}` } }) }; }); console.log('✅ Middleware configured:'); console.log(' - Logging middleware'); console.log(' - Auth middleware'); console.log(' - CORS middleware'); // Simulate requests console.log('\nSimulating requests:'); const publicRequest = new Request('http://localhost/public'); const publicResponse = await middlewareApp.fetch(publicRequest); console.log(`Public route: ${publicResponse.status} ${publicResponse.statusText}`); const protectedRequest = new Request('http://localhost/protected'); const protectedResponse = await middlewareApp.fetch(protectedRequest); console.log(`Protected route (no auth): ${protectedResponse.status} ${protectedResponse.statusText}`); const authedRequest = new Request('http://localhost/protected', { headers: { 'Authorization': 'Bearer token123' } }); const authedResponse = await middlewareApp.fetch(authedRequest); console.log(`Protected route (with auth): ${authedResponse.status} ${authedResponse.statusText}`); // Example 3: Node.js Runtime console.log('\n--- Example 3: Node.js Runtime ---\n'); const nodeRuntime = await createRuntime(RuntimeEnvironment.NODE, { port: 3001, host: 'localhost', caching: true }); const nodeApp = nodeRuntime.createApp(); // Add middleware nodeApp.use(async (context, next) => { console.log(`[Node] ${context.method} ${context.pathname}`); await next(); }); // Register components nodeApp.component('HomePage', (props) => ({ html: { children: [ { head: { children: [ { title: { text: props.title || 'Home' } } ] } }, { body: { children: [ { h1: { text: 'Welcome to Coherent.js' } }, { p: { text: 'Running on Node.js runtime' } } ] } } ] } })); nodeApp.component('UserProfile', (props) => ({ div: { className: 'profile', children: [ { h2: { text: `User: ${props.name}` } }, { p: { text: `ID: ${props.id}` } } ] } })); // Add routes nodeApp.get('/', async () => ({ component: 'HomePage', props: { title: 'Home Page' } })); nodeApp.get('/user/:id', async (context) => ({ component: 'UserProfile', props: { id: context.params.id, name: `User ${context.params.id}` } })); nodeApp.get('/api/data', async () => ({ json: { message: 'Hello from API', timestamp: Date.now() } })); console.log('✅ Node.js runtime configured'); console.log(' Port: 3001'); console.log(' Components: HomePage, UserProfile'); console.log(' Routes: /, /user/:id, /api/data'); // Example 4: Framework Integration Helpers console.log('\n--- Example 4: Framework Integration Helpers ---\n'); console.log('Express middleware:'); console.log('```javascript'); console.log('const express = require("express");'); console.log('const app = express();'); console.log(''); console.log('app.use(nodeRuntime.expressMiddleware());'); console.log(''); console.log('app.get("/", async (req, res) => {'); console.log(' await req.coherent.render(HomePage, { title: "Home" });'); console.log('});'); console.log('```'); console.log('\nFastify plugin:'); console.log('```javascript'); console.log('const fastify = require("fastify")();'); console.log(''); console.log('fastify.register(nodeRuntime.fastifyPlugin());'); console.log(''); console.log('fastify.get("/", async (request, reply) => {'); console.log(' const html = await fastify.coherent.render(HomePage);'); console.log(' reply.type("text/html").send(html);'); console.log('});'); console.log('```'); console.log('\nKoa middleware:'); console.log('```javascript'); console.log('const Koa = require("koa");'); console.log('const app = new Koa();'); console.log(''); console.log('app.use(nodeRuntime.koaMiddleware());'); console.log(''); console.log('app.use(async (ctx) => {'); console.log(' await ctx.coherent.render(HomePage, { title: "Home" });'); console.log('});'); console.log('```'); // Example 5: Runtime Statistics console.log('\n--- Example 5: Runtime Statistics ---\n'); const stats = nodeApp.getStats(); console.log('Node.js Runtime Stats:'); console.log(`- Render Count: ${stats.renderCount}`); console.log(`- Cache Size: ${stats.cacheSize}`); console.log(`- Component Count: ${stats.componentCount}`); console.log(`- Route Count: ${stats.routeCount}`); console.log(`- Middleware Count: ${stats.middlewareCount}`); // Example 6: Middleware Chain console.log('\n--- Example 6: Advanced Middleware Chain ---\n'); const advancedRuntime = await createRuntime(RuntimeEnvironment.EDGE); const advancedApp = advancedRuntime.createApp(); // Request timing middleware advancedApp.use(async (context, next) => { context.state.startTime = Date.now(); await next(); context.state.duration = Date.now() - context.state.startTime; }); // Request ID middleware advancedApp.use(async (context, next) => { context.state.requestId = Math.random().toString(36).substring(7); await next(); }); // Error handling middleware advancedApp.use(async (context, next) => { try { await next(); } catch (error) { console.error(`[Error] Request ${context.state.requestId}:`, error.message); context.response = new Response( JSON.stringify({ error: error.message }), { status: 500, headers: { 'Content-Type': 'application/json' } } ); } }); // Response headers middleware advancedApp.use(async (context, next) => { await next(); if (context.response) { const headers = new Headers(context.response.headers); headers.set('X-Request-ID', context.state.requestId); headers.set('X-Response-Time', `${context.state.duration}ms`); context.response = new Response(context.response.body, { status: context.response.status, headers }); } }); advancedApp.get('/test', async (context) => { return { component: () => ({ div: { text: `Request ID: ${context.state.requestId}` } }) }; }); console.log('✅ Advanced middleware chain configured:'); console.log(' 1. Request timing'); console.log(' 2. Request ID generation'); console.log(' 3. Error handling'); console.log(' 4. Response headers'); const testRequest = new Request('http://localhost/test'); const testResponse = await advancedApp.fetch(testRequest); console.log('\nTest request headers:'); console.log(` X-Request-ID: ${testResponse.headers.get('X-Request-ID')}`); console.log(` X-Response-Time: ${testResponse.headers.get('X-Response-Time')}`); console.log('\n=== Demo Complete ===\n'); console.log('Runtime features implemented:'); console.log('✅ Streaming rendering in Edge runtime'); console.log('✅ Middleware support in Edge runtime'); console.log('✅ Complete Node.js runtime'); console.log('✅ Framework integration helpers (Express, Fastify, Koa)'); console.log('✅ Advanced middleware chains'); console.log('✅ Runtime statistics');Streaming SSR
JSCoherentStream HTML responses for faster time-to-first-byte.
▶️ Run:node examples/streaming.js/** * @name Streaming SSR * @category Features * @description Stream HTML responses for faster time-to-first-byte. */ import { render } from '@coherent.js/core'; // Note: Streaming renderer is a separate feature - using render for now // For true streaming, use the streaming-renderer package when available // Large list component optimized for streaming const StreamingList = ({ itemCount = 100, title = 'Streaming List' }) => ({ div: { class: 'streaming-list', children: [ { h4: { text: title } }, { p: { text: `${itemCount} items rendered progressively` } }, { div: { class: 'list-container', children: Array.from({ length: itemCount }, (_, i) => ({ div: { key: i, class: 'list-item', children: [ { span: { text: `Item ${i + 1}` } }, { small: { text: ` (Batch ${Math.floor(i / 10) + 1})` } } ] } })) } } ] } }); // Streaming data table component const StreamingDataTable = ({ rows = [], showProgress = false }) => ({ div: { class: 'streaming-table-container', children: [ { h4: { text: 'Data Table Streaming' } }, showProgress && { div: { class: 'progress-info', children: [ { p: { text: `Streaming ${rows.length} records` } }, { div: { class: 'progress-bar', text: '████████████ 100%' } } ] } }, { table: { class: 'streaming-table', children: [ { thead: { children: [{ tr: { children: [ { th: { text: 'ID' } }, { th: { text: 'Name' } }, { th: { text: 'Department' } }, { th: { text: 'Status' } } ] } }] } }, { tbody: { children: rows.map(row => ({ tr: { key: row.id, class: row.status === 'active' ? 'active-row' : '', children: [ { td: { text: row.id } }, { td: { text: row.name } }, { td: { text: row.department } }, { td: { text: row.status, class: `status-${row.status}` } } ] } })) } } ] } } ].filter(Boolean) } }); // Progressive content streaming component const ProgressiveContent = ({ sections = [] }) => ({ div: { class: 'progressive-content', children: [ { h4: { text: 'Progressive Content Streaming' } }, { p: { text: 'Content sections loaded incrementally' } }, ...sections.map((section, index) => ({ div: { key: index, class: `content-section section-${index}`, children: [ { h5: { text: section.title } }, { p: { text: section.content } }, section.highlight && { div: { class: 'highlight', text: section.highlight } } ].filter(Boolean) } })) ] } }); // Generate sample data for streaming demos const generateStreamingData = (count = 50) => Array.from({ length: count }, (_, i) => ({ id: i + 1, name: `Employee ${i + 1}`, department: ['Engineering', 'Marketing', 'Sales', 'Support'][i % 4], status: ['active', 'pending', 'inactive'][i % 3] })); // Generate progressive content sections const generateContentSections = () => [ { title: 'Introduction', content: 'This section introduces the streaming capabilities of Coherent.js.', highlight: 'Streams render content progressively for better performance.' }, { title: 'Benefits', content: 'Streaming provides improved perceived performance and reduced memory usage.', highlight: 'Large datasets can be processed without blocking the main thread.' }, { title: 'Implementation', content: 'Use renderToStream() to enable streaming for any component.', highlight: 'Works seamlessly with existing component architecture.' } ]; // Real-time streaming feed component const StreamingFeed = ({ items = [], isLive = false }) => ({ div: { class: 'streaming-feed', children: [ { h4: { text: '📡 Live Data Stream' } }, { div: { class: 'feed-status', children: [ { span: { text: isLive ? '🟢 Live' : '🔴 Offline', class: 'status-indicator' } }, { span: { text: `${items.length} items` } } ] } }, { div: { class: 'feed-container', children: items.map(item => ({ div: { key: item.id, class: 'feed-item', children: [ { h6: { text: item.title } }, { p: { text: item.content } }, { small: { text: `${item.timestamp}ms ago` } } ] } })) } } ] } }); // Streaming utilities const createStreamingDemo = async (component, options = {}) => { const { delay = 0 } = options; const stream = renderToStream(component); const chunks = []; for await (const chunk of stream) { chunks.push(chunk); if (delay > 0) { await new Promise(resolve => setTimeout(resolve, delay)); } } return { totalChunks: chunks.length, totalSize: chunks.join('').length, averageChunkSize: chunks.reduce((sum, chunk) => sum + chunk.length, 0) / chunks.length }; }; // Streaming demo component const StreamingDemo = () => { const styles = ` .demo { max-width: 1200px; margin: 0 auto; padding: 20px; font-family: system-ui, sans-serif; } .demo h2 { color: #333; border-bottom: 2px solid #667eea; padding-bottom: 10px; } .demo .section { margin: 30px 0; padding: 20px; background: #f8f9fa; border-radius: 8px; } .demo .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 20px; } .streaming-list, .streaming-table-container, .progressive-content, .streaming-feed { background: white; padding: 15px; border-radius: 5px; border: 1px solid #ddd; height: 100%; } .list-container { max-height: 200px; overflow-y: auto; } .list-item { padding: 5px; border-bottom: 1px solid #eee; } .streaming-table { width: 100%; border-collapse: collapse; font-size: 0.9em; } .streaming-table th, .streaming-table td { border: 1px solid #ddd; padding: 6px; text-align: left; } .streaming-table th { background: #f8f9fa; } .active-row { background: #d4edda; } .status-active { color: #155724; } .status-pending { color: #856404; } .status-inactive { color: #721c24; } .progress-info { margin-bottom: 10px; } .progress-bar { background: #e9ecef; padding: 5px; border-radius: 3px; font-family: monospace; } .content-section { margin: 15px 0; padding: 10px; border-left: 3px solid #667eea; } .highlight { background: #fff3cd; padding: 8px; border-radius: 3px; margin-top: 5px; font-style: italic; } .feed-status { margin-bottom: 10px; } .status-indicator { margin-right: 10px; } .feed-item { margin: 10px 0; padding: 10px; background: #f8f9fa; border-radius: 3px; } .api-example { background: #e3f2fd; padding: 15px; border-radius: 5px; margin: 20px 0; } .api-example pre { background: #263238; color: #eee; padding: 10px; border-radius: 3px; overflow-x: auto; } `; const sampleData = generateStreamingData(25); const contentSections = generateContentSections(); const feedItems = [ { id: 1, title: 'System Update', content: 'New streaming features deployed', timestamp: 1200 }, { id: 2, title: 'Performance Alert', content: 'Render time improved by 40%', timestamp: 800 }, { id: 3, title: 'Cache Status', content: 'Cache hit rate: 95%', timestamp: 400 } ]; return { html: { children: [ { head: { children: [ { title: { text: 'Streaming Rendering Demo' } }, { style: { text: styles } } ] } }, { body: { children: [ { div: { class: 'demo', children: [ { h2: { text: '🌊 Streaming Rendering in Coherent.js' } }, { div: { class: 'section', children: [ { h3: { text: 'Live Streaming Examples' } }, { p: { text: 'These components demonstrate progressive rendering and real-time data streaming:' } }, { div: { class: 'grid', children: [ StreamingList({ itemCount: 1000, title: 'Progressive List' }), StreamingDataTable({ rows: sampleData.slice(0, 15), showProgress: true }) ] } } ] } }, { div: { class: 'section', children: [ { h3: { text: 'Content & Data Streams' } }, { div: { class: 'grid', children: [ ProgressiveContent({ sections: contentSections }), StreamingFeed({ items: feedItems, isLive: true }) ] } } ] } }, { div: { class: 'api-example', children: [ { h3: { text: '🔧 Streaming API Usage' } }, { ul: { children: [ { li: { text: 'Use renderToStream() for progressive rendering' } }, { li: { text: 'Automatic chunking for optimal performance' } }, { li: { text: 'Compatible with Express, Fastify, and Node.js HTTP' } }, { li: { text: 'Real-time updates with WebSocket integration' } } ] } }, { h4: { text: 'Example Implementation:' } }, { pre: { text: `import { renderToStream } from '@coherent/core'; // Express route with streaming app.get('/data', async (req, res) => { const stream = renderToStream(LargeDataComponent()); res.setHeader('Transfer-Encoding', 'chunked'); for await (const chunk of stream) { res.write(chunk); } res.end(); });` } } ] } } ] } } ] } } ] } }; }; export default StreamingDemo; export { StreamingList, StreamingDataTable, ProgressiveContent, StreamingFeed, generateStreamingData, createStreamingDemo };
Client-Side
Client Router
JSCoherentClient-side routing with prefetching, transitions, and guards.
▶️ Run:node examples/client-router-demo.js/** * @name Client Router * @category Client-Side * @description Client-side routing with prefetching, transitions, and guards. */ import { createRouter } from '@coherent.js/client/router'; import { render } from '@coherent.js/core'; // ============================================================================= // Page Components // ============================================================================= function HomePage() { return { div: { className: 'page home-page', children: [ { h1: { text: 'Home Page' } }, { p: { text: 'Welcome to the Coherent.js router demo!' } }, { nav: { children: [ { a: { href: '/about', text: 'About', className: 'nav-link' } }, { a: { href: '/products', text: 'Products', className: 'nav-link' } }, { a: { href: '/users/123', text: 'User Profile', className: 'nav-link' } } ] } } ] } }; } function AboutPage() { return { div: { className: 'page about-page', children: [ { h1: { text: 'About Us' } }, { p: { text: 'Learn more about our application' } }, { a: { href: '/', text: '← Back to Home' } } ] } }; } function ProductsPage() { const products = [ { id: 1, name: 'Product A', price: 29.99 }, { id: 2, name: 'Product B', price: 39.99 }, { id: 3, name: 'Product C', price: 49.99 } ]; return { div: { className: 'page products-page', children: [ { h1: { text: 'Products' } }, { ul: { children: products.map(product => ({ li: { children: [ { a: { href: `/products/${product.id}`, text: `${product.name} - ${product.price}` } } ] } })) } }, { a: { href: '/', text: '← Back to Home' } } ] } }; } function ProductDetailPage({ params }) { const productId = params.id; return { div: { className: 'page product-detail-page', children: [ { h1: { text: `Product #${productId}` } }, { p: { text: `Details for product ${productId}` } }, { a: { href: '/products', text: '← Back to Products' } } ] } }; } function UserProfilePage({ params }) { const userId = params.id; return { div: { className: 'page user-profile-page', children: [ { h1: { text: `User Profile` } }, { p: { text: `User ID: ${userId}` } }, { a: { href: '/', text: '← Back to Home' } } ] } }; } function NotFoundPage() { return { div: { className: 'page not-found-page', children: [ { h1: { text: '404 - Not Found' } }, { p: { text: 'The page you are looking for does not exist.' } }, { a: { href: '/', text: '← Back to Home' } } ] } }; } function LoadingPage() { return { div: { className: 'page loading-page', children: [ { div: { className: 'spinner' } }, { p: { text: 'Loading...' } } ] } }; } // ============================================================================= // Router Configuration // ============================================================================= console.log('📦 Creating Router...\n'); const router = createRouter({ // Router mode mode: 'history', // or 'hash' for hash-based routing // Base path base: '/', // Route prefetching prefetch: { enabled: true, strategy: 'hover', // Prefetch on hover delay: 100, // 100ms delay maxConcurrent: 3, // Max 3 concurrent prefetches priority: { critical: 100, high: 50, normal: 0, low: -50 } }, // Page transitions transitions: { enabled: true, default: { enter: 'fade-in', leave: 'fade-out', duration: 300 }, routes: { '/products': { enter: 'slide-left', leave: 'slide-right', duration: 400 } } }, // Code splitting codeSplitting: { enabled: true, chunkStrategy: 'route', preload: ['/', '/about'], loadingComponent: LoadingPage }, // Scroll behavior scrollBehavior: { behavior: 'smooth', top: 0, preserveScroll: false, scrollToHash: true }, // Routes routes: { '/': { component: HomePage, prefetch: 'eager' // Prefetch immediately }, '/about': { component: AboutPage, prefetch: 'high' }, '/products': { component: ProductsPage, prefetch: 'normal' }, '/products/:id': { component: ProductDetailPage, prefetch: 'low' }, '/users/:id': { component: UserProfilePage, beforeEnter: (to, from, next) => { console.log(`🔒 Checking access to user ${to.params.id}...`); // Simulate auth check const isAuthenticated = true; // Change to false to test guard if (isAuthenticated) { console.log('✓ Access granted'); next(); } else { console.log('✗ Access denied, redirecting to home'); next('/'); } } }, '*': { component: NotFoundPage } } }); // ============================================================================= // Global Navigation Guards // ============================================================================= console.log('🔐 Setting up navigation guards...\n'); // Before navigation router.beforeEach((to, from, next) => { console.log(`📍 Navigating from ${from?.path || '(initial)'} to ${to.path}`); // Log navigation console.log(` Params: ${JSON.stringify(to.params)}`); console.log(` Query: ${JSON.stringify(to.query)}`); // Simulate analytics trackPageView(to.path); // Continue navigation next(); }); // After navigation router.afterEach((to, from) => { console.log(`✓ Navigation complete to ${to.path}\n`); // Update page title const pageTitles = { '/': 'Home', '/about': 'About Us', '/products': 'Products', '/users/:id': 'User Profile' }; const title = pageTitles[to.path] || 'Page'; console.log(`📄 Page title: ${title}`); }); // ============================================================================= // Router Events // ============================================================================= console.log('📢 Setting up router events...\n'); router.on('navigate', (to, from) => { console.log(`🚀 Navigate event: ${from?.path} → ${to.path}`); }); router.on('error', (error) => { console.error('❌ Router error:', error); }); router.on('prefetch:start', (path) => { console.log(`⏳ Prefetching started: ${path}`); }); router.on('prefetch:complete', (path) => { console.log(`✓ Prefetching complete: ${path}`); }); // ============================================================================= // Helper Functions // ============================================================================= function trackPageView(path) { console.log(`📊 Analytics: Page view tracked for ${path}`); } // ============================================================================= // Navigation Examples // ============================================================================= console.log('=' .repeat(80)); console.log('🧭 Router Navigation Examples'); console.log('='.repeat(80)); console.log(''); // Example 1: Basic navigation console.log('Example 1: Basic Navigation\n'); router.push('/about'); setTimeout(() => { // Example 2: Navigation with dynamic route console.log('\nExample 2: Dynamic Route Navigation\n'); router.push('/users/456'); setTimeout(() => { // Example 3: Navigation with query parameters console.log('\nExample 3: Query Parameters\n'); router.push({ path: '/products', query: { category: 'electronics', sort: 'price' } }); setTimeout(() => { // Example 4: Navigation with hash console.log('\nExample 4: Hash Navigation\n'); router.push({ path: '/about', hash: '#team' }); setTimeout(() => { // Example 5: Back navigation console.log('\nExample 5: Back Navigation\n'); router.back(); setTimeout(() => { // Example 6: Forward navigation console.log('\nExample 6: Forward Navigation\n'); router.forward(); setTimeout(() => { // Example 7: Replace current route console.log('\nExample 7: Replace Route\n'); router.replace('/products/123'); setTimeout(() => { // Example 8: Check active route console.log('\nExample 8: Check Active Route\n'); console.log('Is /products/123 active?', router.isActive('/products/123')); console.log('Is /about active?', router.isActive('/about')); setTimeout(() => { // Example 9: Get current route console.log('\nExample 9: Current Route Info\n'); const current = router.currentRoute; console.log('Current path:', current.path); console.log('Current params:', current.params); console.log('Current query:', current.query); setTimeout(() => { // Example 10: Manual prefetch console.log('\nExample 10: Manual Prefetch\n'); router.prefetch('/users/789'); router.prefetch(['/about', '/products']); setTimeout(() => { // Final summary console.log('\n' + '='.repeat(80)); console.log('📝 Router Demo Complete!'); console.log('='.repeat(80)); console.log(''); console.log('Features demonstrated:'); console.log('✓ Basic navigation (push, back, forward, replace)'); console.log('✓ Dynamic routes with parameters'); console.log('✓ Query parameters and hash'); console.log('✓ Navigation guards (beforeEach, afterEach, beforeEnter)'); console.log('✓ Route prefetching (automatic and manual)'); console.log('✓ Page transitions'); console.log('✓ Router events'); console.log('✓ Active route checking'); console.log(''); console.log('🚀 Ready to use in your application!'); console.log('='.repeat(80)); }, 1000); }, 1000); }, 1000); }, 1000); }, 1000); }, 1000); }, 1000); }, 1000); }, 1000); }, 1000); // ============================================================================= // Usage in HTML // ============================================================================= /* <!DOCTYPE html> <html> <head> <title>Coherent.js Router Demo</title> <style> .page { padding: 20px; } .nav-link { margin-right: 10px; } .spinner { width: 40px; height: 40px; border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } </style> </head> <body> <div id="app"></div> <script type="module" src="./client-router-demo.js"></script> </body> </html> */Hydration
JSCoherentClient-side hydration with Islands Architecture.
▶️ Run:node examples/hydration-demo.js/** * @name Hydration * @category Client-Side * @description Client-side hydration with Islands Architecture. */ import { makeHydratable, autoHydrate } from '@coherent.js/client'; import { withState } from '@coherent.js/core'; // Interactive counter with hydration support // Create a simple counter component that works with hydration const CounterComponent = withState({ count: 0, step: 1 })(({ state, props = {} }) => { // Extract initial values from props with defaults const initialCount = props.initialCount !== undefined ? props.initialCount : 0; const initialStep = props.initialStep !== undefined ? props.initialStep : 1; // Initialize state with initial values if not already set if (state.count === undefined) { state.count = initialCount; } if (state.step === undefined) { state.step = initialStep; } return { div: { class: 'counter-widget', 'data-coherent-component': 'counter', // Add data attribute to identify component children: [ { h4: { text: 'Interactive Counter', class: 'widget-title' } }, { div: { class: 'counter-display', children: [ { span: { text: `Count: ${state.count}`, class: 'count-value', 'data-ref': 'count' } }, { span: { text: `Step: ${state.step}`, class: 'step-value', 'data-ref': 'step' } } ] } }, { div: { class: 'counter-controls', children: [ { button: { text: '-', class: 'btn btn-secondary', onclick: (event, state, setState) => setState({ count: state.count - state.step }) } }, { button: { text: '+', class: 'btn btn-primary', onclick: (event, state, setState) => setState({ count: state.count + state.step }) } }, { button: { text: 'Reset Test', class: 'btn btn-outline', onclick: (event, state, setState) => setState({ count: initialCount }) } } ] } }, { div: { class: 'step-controls', children: [ { label: { text: 'Step size:', class: 'step-label' } }, { input: { type: 'number', value: state.step, min: 1, max: 10, class: 'step-input', oninput: (event, state, setState) => setState({ step: parseInt(event.target.value) || 1 }) } } ] } } ] } }; }); const HydratableCounter = makeHydratable(CounterComponent, { componentName: 'HydratableCounter' }); // Interactive todo list with hydration support // Interactive user profile form with hydration support const HydratableUserProfile = makeHydratable( withState({ firstName: 'John', lastName: 'Doe', age: 30, email: 'john.doe@example.com', bio: 'Software developer passionate about web technologies.', newsletter: true })(({ state }) => { return { div: { class: 'profile-widget', 'data-coherent-component': 'user-profile', children: [ { h4: { text: 'Interactive User Profile', class: 'widget-title' } }, { div: { class: 'profile-display', children: [ { p: { text: `Name: ${state.firstName} ${state.lastName}`, class: 'profile-info' } }, { p: { text: `Age: ${state.age}`, class: 'profile-info' } }, { p: { text: `Email: ${state.email}`, class: 'profile-info' } }, { p: { text: `Status: ${state.age >= 18 ? 'Adult' : 'Minor'}`, class: `profile-status ${state.age >= 18 ? 'adult' : 'minor'}` }}, { p: { text: `Newsletter: ${state.newsletter ? 'Subscribed' : 'Not subscribed'}`, class: 'profile-info' } } ] } }, { div: { class: 'profile-form', children: [ { div: { class: 'form-row', children: [ { input: { type: 'text', placeholder: 'First Name', value: state.firstName, class: 'form-input', oninput: (event, state, setState) => setState({ firstName: event.target.value }) } }, { input: { type: 'text', placeholder: 'Last Name', value: state.lastName, class: 'form-input', oninput: (event, state, setState) => setState({ lastName: event.target.value }) } } ] } }, { div: { class: 'form-row', children: [ { input: { type: 'number', placeholder: 'Age', value: state.age, min: 1, max: 120, class: 'form-input', oninput: (event, state, setState) => setState({ age: parseInt(event.target.value) || 0 }) } }, { input: { type: 'email', placeholder: 'Email', value: state.email, class: 'form-input', oninput: (event, state, setState) => setState({ email: event.target.value }) } } ] } }, { div: { class: 'form-row full-width', children: [ { textarea: { placeholder: 'Bio', value: state.bio, class: 'form-textarea', rows: 3, oninput: (event, state, setState) => setState({ bio: event.target.value }) } } ] } }, { div: { class: 'form-row checkbox-row', children: [ { label: { class: 'checkbox-label', children: [ { input: { type: 'checkbox', checked: state.newsletter, class: 'form-checkbox', onchange: (event, state, setState) => setState({ newsletter: event.target.checked }) } }, { span: { text: 'Subscribe to newsletter', class: 'checkbox-text' } } ] } } ] } }, { div: { class: 'form-actions', children: [ { button: { text: 'Reset Profile', class: 'btn btn-outline', onclick: (event, state, setState) => setState({ firstName: 'John', lastName: 'Doe', age: 30, email: 'john.doe@example.com', bio: 'Software developer passionate about web technologies.', newsletter: true }) } } ] } } ] } } ] } }; }), { componentName: 'HydratableUserProfile' } ); const HydratableTodoList = makeHydratable( withState({ todos: [], newTodo: '', filter: 'all' })(({ state }) => { // Define functions that accept setState as parameter for hydration compatibility const addTodo = (event, state, setState) => { if (state.newTodo.trim()) { setState({ todos: [...state.todos, { id: Date.now(), text: state.newTodo.trim(), completed: false }], newTodo: '' }); } }; const toggleTodo = (id) => (event, state, setState) => { setState({ todos: state.todos.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo ) }); }; const removeTodo = (id) => (event, state, setState) => { setState({ todos: state.todos.filter(todo => todo.id !== id) }); }; const setFilter = (filter) => (event, state, setState) => setState({ filter }); const filteredTodos = state.todos.filter(todo => { if (state.filter === 'active') return !todo.completed; if (state.filter === 'completed') return todo.completed; return true; }); const stats = { total: state.todos.length, completed: state.todos.filter(t => t.completed).length, active: state.todos.filter(t => !t.completed).length }; return { div: { class: 'todo-widget', 'data-coherent-component': 'todo-list', // Add data attribute to identify component children: [ { h4: { text: 'Interactive Todo List', class: 'widget-title' } }, { div: { class: 'todo-stats', children: [ { span: { text: `Total: ${stats.total}`, class: 'stat-item' } }, { span: { text: `Active: ${stats.active}`, class: 'stat-item' } }, { span: { text: `Completed: ${stats.completed}`, class: 'stat-item' } } ] } }, { div: { class: 'todo-input', children: [ { input: { type: 'text', value: state.newTodo, placeholder: 'Add new todo...', class: 'todo-input-field', oninput: (e, state, setState) => setState({ newTodo: e.target.value }), onkeypress: (e, state, setState) => { if (e.key === 'Enter') addTodo(e, state, setState); } } }, { button: { text: 'Add', class: 'btn btn-primary', onclick: addTodo } } ] } }, { div: { class: 'todo-filters', children: [ { button: { text: 'All', class: `filter-btn ${state.filter === 'all' ? 'active' : ''}`, onclick: setFilter('all') } }, { button: { text: 'Active', class: `filter-btn ${state.filter === 'active' ? 'active' : ''}`, onclick: setFilter('active') } }, { button: { text: 'Completed', class: `filter-btn ${state.filter === 'completed' ? 'active' : ''}`, onclick: setFilter('completed') } } ] } }, { ul: { class: 'todo-list', children: filteredTodos.map(todo => ({ li: { key: todo.id, class: `todo-item ${todo.completed ? 'completed' : ''}`, children: [ { input: { type: 'checkbox', checked: todo.completed, class: 'todo-checkbox', onchange: toggleTodo(todo.id) } }, { span: { text: todo.text, class: 'todo-text' } }, { button: { text: '×', class: 'btn btn-danger btn-small', onclick: removeTodo(todo.id) } } ] } })) } } ] } }; }), { componentName: 'HydratableTodoList' } ); // Complete hydration demo page export const hydrationDemo = { html: { children: [ { head: { children: [ { title: { text: 'Hydration Demo - Coherent.js' } }, { style: { text: ` body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100dvh; color: #2d3748; } .demo-container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); overflow: hidden; } .demo-header { background: linear-gradient(135deg, #4299e1 0%, #3182ce 100%); color: white; padding: 30px; text-align: center; } .demo-header h1 { margin: 0 0 10px 0; font-size: 2.5rem; font-weight: 700; } .demo-header p { margin: 0; font-size: 1.1rem; opacity: 0.9; } .demo-content { padding: 40px; } .demo-section { margin-bottom: 40px; padding: 30px; background: #f7fafc; border-radius: 8px; border-left: 4px solid #4299e1; } .section-title { color: #2b6cb0; margin: 0 0 15px 0; font-size: 1.5rem; font-weight: 600; } .section-description { color: #4a5568; margin-bottom: 25px; line-height: 1.6; } .widget-title { color: #2d3748; margin: 0 0 20px 0; font-size: 1.25rem; font-weight: 600; } .counter-widget, .todo-widget, .profile-widget { background: white; padding: 25px; border-radius: 8px; border: 1px solid #e2e8f0; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } .counter-display { text-align: center; margin: 20px 0; padding: 15px; background: #f7fafc; border-radius: 6px; display: flex; justify-content: center; gap: 20px; } .count-value, .step-value { font-size: 1.25rem; font-weight: 600; color: #2b6cb0; } .counter-controls, .step-controls { display: flex; justify-content: center; gap: 10px; margin: 15px 0; flex-wrap: wrap; } .step-controls { align-items: center; } .step-label { margin-right: 10px; font-weight: 500; } .step-input { width: 60px; padding: 5px 8px; border: 1px solid #e2e8f0; border-radius: 4px; text-align: center; } .btn { padding: 10px 20px; border: none; border-radius: 6px; font-weight: 500; cursor: pointer; transition: all 0.2s ease; font-size: 14px; } .btn:hover { transform: translateY(-1px); box-shadow: 0 4px 8px rgba(0,0,0,0.15); } .btn-primary { background: #4299e1; color: white; } .btn-primary:hover { background: #3182ce; } .btn-secondary { background: #718096; color: white; } .btn-secondary:hover { background: #4a5568; } .btn-outline { background: transparent; color: #4299e1; border: 2px solid #4299e1; } .btn-outline:hover { background: #4299e1; color: white; } .btn-danger { background: #e53e3e; color: white; } .btn-danger:hover { background: #c53030; } .btn-small { padding: 5px 8px; font-size: 12px; min-width: 24px; } .todo-stats { display: flex; gap: 15px; margin-bottom: 20px; padding: 10px; background: #f7fafc; border-radius: 6px; } .stat-item { font-weight: 500; color: #4a5568; } .todo-input { display: flex; gap: 10px; margin-bottom: 20px; } .todo-input-field { flex: 1; padding: 10px; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 14px; } .todo-input-field:focus { outline: none; border-color: #4299e1; box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1); } .todo-filters { display: flex; gap: 5px; margin-bottom: 20px; } .filter-btn { padding: 8px 16px; border: 1px solid #e2e8f0; background: white; color: #4a5568; border-radius: 4px; cursor: pointer; transition: all 0.2s ease; font-size: 13px; } .filter-btn:hover { border-color: #4299e1; color: #4299e1; } .filter-btn.active { background: #4299e1; color: white; border-color: #4299e1; } .todo-list { list-style: none; padding: 0; margin: 0; } .todo-item { display: flex; align-items: center; gap: 10px; padding: 12px; margin: 8px 0; background: white; border: 1px solid #e2e8f0; border-radius: 6px; transition: all 0.2s ease; } .todo-item:hover { border-color: #cbd5e0; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } .todo-item.completed { opacity: 0.7; background: #f7fafc; } .todo-item.completed .todo-text { text-decoration: line-through; color: #718096; } .todo-checkbox { width: 16px; height: 16px; cursor: pointer; } .todo-text { flex: 1; color: #2d3748; } .profile-display { background: #f7fafc; padding: 20px; border-radius: 6px; margin-bottom: 25px; } .profile-info { margin: 8px 0; color: #4a5568; font-weight: 500; } .profile-status.adult { color: #38a169; font-weight: 600; } .profile-status.minor { color: #ed8936; font-weight: 600; } .profile-form { display: flex; flex-direction: column; gap: 15px; } .form-row { display: flex; gap: 15px; align-items: center; } .form-row.full-width { flex-direction: column; align-items: stretch; } .form-row.checkbox-row { justify-content: flex-start; } .form-input { padding: 10px 12px; border: 1px solid #e2e8f0; border-radius: 6px; flex: 1; min-width: 0; font-size: 14px; transition: border-color 0.2s ease; } .form-input:focus { outline: none; border-color: #4299e1; box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1); } .form-textarea { padding: 10px 12px; border: 1px solid #e2e8f0; border-radius: 6px; width: 100%; font-size: 14px; font-family: inherit; resize: vertical; transition: border-color 0.2s ease; } .form-textarea:focus { outline: none; border-color: #4299e1; box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1); } .checkbox-label { display: flex; align-items: center; gap: 8px; cursor: pointer; } .form-checkbox { width: 16px; height: 16px; cursor: pointer; } .checkbox-text { color: #4a5568; font-size: 14px; } .form-actions { display: flex; justify-content: flex-end; margin-top: 10px; } .hydration-info { background: #e6fffa; border: 1px solid #81e6d9; border-radius: 8px; padding: 20px; margin-top: 30px; } .hydration-info h3 { color: #234e52; margin: 0 0 10px 0; } .hydration-info p { color: #285e61; margin: 0; line-height: 1.6; } ` } } ] } }, { body: { children: [ { div: { class: 'demo-container', children: [ { div: { class: 'demo-header', children: [ { h1: { text: 'Hydration Demo' } }, { p: { text: 'Interactive components with server-side rendering and client-side hydration' } } ] } }, { div: { class: 'demo-content', children: [ { div: { class: 'demo-section', children: [ { h2: { text: 'Interactive Counter', class: 'section-title' } }, { p: { text: 'A stateful counter with step control. State is preserved during hydration and updates are reactive.', class: 'section-description' }}, HydratableCounter.renderWithHydration({ initialCount: 5 }) ] } }, { div: { class: 'demo-section', children: [ { h2: { text: 'Interactive User Profile', class: 'section-title' } }, { p: { text: 'A form component with various input types, computed properties, and real-time validation.', class: 'section-description' }}, HydratableUserProfile.renderWithHydration() ] } }, { div: { class: 'demo-section', children: [ { h2: { text: 'Interactive Todo List', class: 'section-title' } }, { p: { text: 'A complex stateful component with filtering, statistics, and real-time interactions.', class: 'section-description' }}, HydratableTodoList.renderWithHydration() ] } }, { div: { class: 'hydration-info', children: [ { h3: { text: 'About Hydration' } }, { p: { text: 'This page demonstrates client-side hydration where server-rendered HTML becomes interactive on the client. Components maintain their state and event handlers are attached seamlessly.' } } ] } } ] } } ] } } ] } } ] } }; // Make the demo page hydratable const HydrationDemoPage = makeHydratable(() => hydrationDemo); // Export component registry for dev server (only in browser environment) if (typeof window !== 'undefined') { // Initialize component registry window.componentRegistry = { HydratableCounter, HydratableUserProfile, HydratableTodoList }; // Auto-hydrate all components with explicit registry autoHydrate(window.componentRegistry); } // Export the demo page as default for live preview export default HydrationDemoPage;
Integrations
Express Integration
JSCoherentUsing Coherent.js with Express.js for SSR.
▶️ Run:node examples/express-integration.js/** * @name Express Integration * @category Integrations * @description Using Coherent.js with Express.js for SSR. */ import express from 'express'; import { createCoherentHandler, setupCoherent } from '@coherent.js/express'; const app = express(); const PORT = process.env.PORT || 3000; // Setup Coherent.js with Express setupCoherent(app, { useMiddleware: true, useEngine: false, enablePerformanceMonitoring: true }); // Express integration home page component function ExpressHomePage({ name = 'Express Developer' }) { const styles = ` body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 900px; margin: 0 auto; padding: 40px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100dvh; color: white; } .container { background: rgba(255,255,255,0.1); border-radius: 20px; padding: 40px; backdrop-filter: blur(10px); } .header { text-align: center; margin-bottom: 40px; } .header h1 { font-size: 3em; margin: 0; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); } .header p { font-size: 1.2em; margin: 10px 0; opacity: 0.9; } .features { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin: 30px 0; } .feature { background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px; } .feature h3 { margin: 0 0 10px 0; color: #ffd700; } .links { text-align: center; margin-top: 30px; } .links a { color: #ffd700; text-decoration: none; margin: 0 15px; font-weight: bold; } .links a:hover { text-decoration: underline; } `; return { html: { children: [ { head: { children: [ { title: { text: 'Coherent.js + Express Integration' } }, { style: { text: styles } } ] } }, { body: { children: [ { div: { class: 'container', children: [ { div: { class: 'header', children: [ { h1: { text: '🚀 Express + Coherent.js' } }, { p: { text: `Welcome, ${name}!` } }, { p: { text: 'Server-side rendering made simple' } } ] } }, { div: { class: 'features', children: [ { div: { class: 'feature', children: [ { h3: { text: '⚡ Fast Rendering' } }, { p: { text: 'Server-side rendering with automatic optimization' } } ] } }, { div: { class: 'feature', children: [ { h3: { text: '🔧 Easy Integration' } }, { p: { text: 'Drop-in middleware for existing Express apps' } } ] } }, { div: { class: 'feature', children: [ { h3: { text: '📊 Performance Monitoring' } }, { p: { text: 'Built-in performance tracking and metrics' } } ] } }, { div: { class: 'feature', children: [ { h3: { text: '🛡️ Type Safety' } }, { p: { text: 'Full TypeScript support and type safety' } } ] } } ] } }, { div: { class: 'links', children: [ { a: { href: '/user/demo', text: '👤 User Profile Demo' } }, { a: { href: '/api/status', text: '📡 API Status' } } ] } } ] } } ] } } ] } }; } // User profile component using Express request data function ExpressUserPage(req) { const { username } = req.params; const userAgent = req.get('User-Agent') || 'Unknown'; const timestamp = new Date().toLocaleString(); const styles = ` body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 800px; margin: 0 auto; padding: 40px 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100dvh; color: white; } .profile { background: rgba(255,255,255,0.1); border-radius: 20px; padding: 40px; backdrop-filter: blur(10px); } .profile h1 { text-align: center; margin-bottom: 30px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); } .info { background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px; margin: 20px 0; } .info h3 { margin: 0 0 10px 0; color: #ffd700; } .info p { margin: 5px 0; opacity: 0.9; } .back-link { display: inline-block; margin-top: 20px; color: #ffd700; text-decoration: none; font-weight: bold; } .back-link:hover { text-decoration: underline; } `; return { html: { children: [ { head: { children: [ { title: { text: `User Profile - ${username}` } }, { style: { text: styles } } ] } }, { body: { children: [ { div: { class: 'profile', children: [ { h1: { text: `👤 ${username}` } }, { div: { class: 'info', children: [ { h3: { text: '🔗 Request Info' } }, { p: { text: `Path: ${req.path}` } }, { p: { text: `Method: ${req.method}` } }, { p: { text: `Timestamp: ${timestamp}` } } ] } }, { div: { class: 'info', children: [ { h3: { text: '🌐 Browser Info' } }, { p: { text: `User Agent: ${userAgent.substring(0, 80)}${userAgent.length > 80 ? '...' : ''}` } }, { p: { text: `IP: ${req.ip || 'Unknown'}` } } ] } }, { a: { href: '/', text: '← Back to Home', class: 'back-link' } } ] } } ] } } ] } }; } // Routes app.get('/', (req, res) => { res.send(ExpressHomePage({ name: 'Express Developer' })); }); app.get('/user/:username', createCoherentHandler((req) => { return ExpressUserPage(req); })); app.get('/api/status', (req, res) => { res.json({ status: 'ok', framework: 'Coherent.js with Express', timestamp: new Date().toISOString(), uptime: process.uptime() }); }); // Error handling app.use((err, req, res) => { res.status(500).send({ error: 'Internal Server Error', message: err.message }); }); // Start server only if run directly (not imported as module) if (import.meta.url === `file://${process.argv[1]}`) { app.listen(PORT, () => { if (process.env.NODE_ENV !== 'production') { console.log(`Express + Coherent.js server: http://localhost:${PORT}`); } }); } // Demo component for live preview const ExpressIntegrationDemo = () => { const styles = ` .demo { max-width: 800px; margin: 0 auto; padding: 20px; font-family: system-ui, sans-serif; } .demo h2 { color: #333; border-bottom: 2px solid #667eea; padding-bottom: 10px; } .demo .section { margin: 30px 0; padding: 20px; background: #f8f9fa; border-radius: 8px; } .demo pre { background: #2d3748; color: #e2e8f0; padding: 15px; border-radius: 5px; overflow-x: auto; } .demo .highlight { background: #ffd700; padding: 2px 4px; border-radius: 3px; } `; return { html: { children: [ { head: { children: [ { title: { text: 'Express.js Integration Demo' } }, { style: { text: styles } } ] } }, { body: { children: [ { div: { class: 'demo', children: [ { h2: { text: '🚀 Express.js Integration with Coherent.js' } }, { div: { class: 'section', children: [ { h3: { text: 'Setup' } }, { p: { text: 'Install the Express integration:' } }, { pre: { text: 'npm install @coherent/express' } }, { p: { text: 'Configure your Express app:' } }, { pre: { text: `import { setupCoherent } from '@coherent/express'; setupCoherent(app, { useMiddleware: true, enablePerformanceMonitoring: true });` } } ] } }, { div: { class: 'section', children: [ { h3: { text: 'Features' } }, { ul: { children: [ { li: { text: 'Automatic component rendering with Express middleware' } }, { li: { text: 'Request data injection into components' } }, { li: { text: 'Performance monitoring and metrics' } }, { li: { text: 'Error handling and debugging support' } } ] } } ] } }, { div: { class: 'section', children: [ { h3: { text: 'Usage Example' } }, { p: { text: 'Create routes that return Coherent.js components:' } }, { pre: { text: `app.get('/', (req, res) => { res.send(HomePage({ user: req.user })); }); app.get('/user/:id', createCoherentHandler((req) => { return UserProfile(req.params.id); }));` } } ] } } ] } } ] } } ] } }; }; export default ExpressIntegrationDemo; export { app };Next.js Integration
JSCoherentUsing Coherent.js components inside Next.js applications.
▶️ Run:node examples/nextjs-integration.js/** * @name Next.js Integration * @category Integrations * @description Using Coherent.js components inside Next.js applications. */ import { createCoherentNextHandler } from '@coherent.js/nextjs'; // Enhanced Next.js home page component export const NextHomePage = ({ name = 'World', timestamp = new Date().toISOString() }) => ({ html: { children: [ { head: { children: [ { title: { text: 'Coherent.js + Next.js Integration' } }, { style: { text: ` body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 900px; margin: 0 auto; padding: 20px; background: #f8fafc; line-height: 1.6; } .container { background: white; padding: 40px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); } .header { text-align: center; margin-bottom: 40px; padding-bottom: 20px; border-bottom: 2px solid #e2e8f0; } .header h1 { color: #1a202c; margin-bottom: 10px; font-size: 2.5em; font-weight: 300; } .header p { color: #4a5568; font-size: 1.2em; } .content { margin: 30px 0; } .features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin: 30px 0; } .feature-card { padding: 20px; background: #f7fafc; border-radius: 8px; border-left: 4px solid #3182ce; } .feature-card h3 { color: #2d3748; margin-bottom: 10px; } .feature-card p { color: #4a5568; margin: 0; } .info-section { background: #edf2f7; padding: 20px; border-radius: 8px; margin: 30px 0; } .info-item { margin: 10px 0; padding: 10px; background: white; border-radius: 6px; border-left: 4px solid #38a169; } .footer { margin-top: 40px; text-align: center; padding-top: 20px; border-top: 1px solid #e2e8f0; color: #718096; font-size: 0.9em; } ` } } ] } }, { body: { children: [ { div: { className: 'container', children: [ { div: { className: 'header', children: [ { h1: { text: 'Coherent.js + Next.js' } }, { p: { text: `Welcome, ${name}! Experience seamless Next.js integration with server-side rendering.` } } ] } }, { div: { className: 'content', children: [ { h2: { text: 'Integration Features' } }, { div: { className: 'features-grid', children: [ { div: { className: 'feature-card', children: [ { h3: { text: '🚀 API Route Integration' } }, { p: { text: 'Seamless integration with Next.js API routes for server-side rendering.' } } ] } }, { div: { className: 'feature-card', children: [ { h3: { text: '📊 Performance Monitoring' } }, { p: { text: 'Built-in performance tracking and optimization for Next.js applications.' } } ] } }, { div: { className: 'feature-card', children: [ { h3: { text: '🔧 Zero Configuration' } }, { p: { text: 'Drop-in integration with existing Next.js projects without configuration.' } } ] } }, { div: { className: 'feature-card', children: [ { h3: { text: '⚡ Static Generation' } }, { p: { text: 'Compatible with Next.js static generation and incremental static regeneration.' } } ] } } ] } }, { div: { className: 'info-section', children: [ { h3: { text: 'Request Information' } }, { div: { className: 'info-item', children: [ { strong: { text: 'Rendered At: ' } }, { span: { text: new Date(timestamp).toLocaleString() } } ] } }, { div: { className: 'info-item', children: [ { strong: { text: 'Framework: ' } }, { span: { text: 'Next.js with Coherent.js' } } ] } }, { div: { className: 'info-item', children: [ { strong: { text: 'Rendering: ' } }, { span: { text: 'Server-Side Rendering (SSR)' } } ] } } ] } } ] } }, { div: { className: 'footer', children: [ { p: { text: 'Powered by Coherent.js and Next.js • Built for modern web applications' } } ] } } ] } } ] } } ] } }); // Enhanced user profile component for Next.js integration export async function getServerSideProps(_context) { const { username } = _context.query; const userAgent = _context.req.headers['user-agent'] || 'Unknown'; const timestamp = new Date().toLocaleString(); const method = _context.req.method || 'GET'; return { html: { children: [ { head: { children: [ { title: { text: `${username}'s Profile - Next.js + Coherent.js` } }, { style: { text: ` body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background: #f8fafc; } .profile-container { background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); } .profile-header { text-align: center; margin-bottom: 30px; padding-bottom: 20px; border-bottom: 2px solid #e2e8f0; } .profile-info { background: #f7fafc; padding: 20px; border-radius: 8px; margin: 20px 0; } .info-item { margin: 10px 0; padding: 10px; background: white; border-radius: 6px; border-left: 4px solid #3182ce; } .back-link { display: inline-block; padding: 10px 20px; background: #3182ce; color: white; text-decoration: none; border-radius: 6px; margin-top: 20px; } .next-badge { background: #000; color: white; padding: 4px 8px; border-radius: 4px; font-size: 0.8em; margin-left: 10px; } ` } } ] } }, { body: { children: [ { div: { className: 'profile-container', children: [ { div: { className: 'profile-header', children: [ { h1: { children: [ { span: { text: `👤 ${username}'s Profile` } }, { span: { text: 'Next.js', className: 'next-badge' } } ] } }, { p: { text: 'User profile rendered with Next.js API routes and Coherent.js integration' } } ] } }, { div: { className: 'profile-info', children: [ { h3: { text: 'Request Information' } }, { div: { className: 'info-item', children: [ { strong: { text: 'Username: ' } }, { span: { text: username } } ] } }, { div: { className: 'info-item', children: [ { strong: { text: 'Request Method: ' } }, { span: { text: method } } ] } }, { div: { className: 'info-item', children: [ { strong: { text: 'Request Path: ' } }, { span: { text: _context.req.url || '/api/user/[username]' } } ] } }, { div: { className: 'info-item', children: [ { strong: { text: 'User Agent: ' } }, { span: { text: userAgent.substring(0, 100) + (userAgent.length > 100 ? '...' : '') } } ] } }, { div: { className: 'info-item', children: [ { strong: { text: 'Rendered At: ' } }, { span: { text: timestamp } } ] } }, { div: { className: 'info-item', children: [ { strong: { text: 'Framework: ' } }, { span: { text: 'Next.js + Coherent.js' } } ] } } ] } }, { a: { href: '/api/home', text: '← Back to Home', className: 'back-link' } } ] } } ] } } ] } }; }; // Create optimized Next.js API route handlers export const homeHandler = createCoherentNextHandler(() => { return NextHomePage({ name: 'Next.js Developer', timestamp: new Date().toISOString() }); }, { enablePerformanceMonitoring: true, cacheComponents: true }); export const userHandler = createCoherentNextHandler((req) => { return NextUserPage(req); }, { enablePerformanceMonitoring: true }); // API status handler for health checks export const statusHandler = createCoherentNextHandler((req, res) => { // Return JSON response for API status res.status(200).json({ status: 'ok', framework: 'Next.js + Coherent.js', timestamp: new Date().toISOString(), version: '1.0.0' }); }); // Export as default for Next.js API routes export default function handler(_req, _res) { // Return JSON response for API status _res.status(200).json({ status: 'ok', framework: 'Next.js + Coherent.js', timestamp: new Date().toISOString(), version: '1.0.0' }); } /** * Usage Instructions: * * To use these handlers in your Next.js application: * * 1. Create pages/api/home.js: * export { homeHandler as default } from '../../examples/nextjs-integration.js'; * * 2. Create pages/api/user/[username].js: * export { userHandler as default } from '../../../examples/nextjs-integration.js'; * * 3. Create pages/api/status.js: * export { statusHandler as default } from '../../examples/nextjs-integration.js'; */
Full Apps
DevTools
JSCoherentDeveloper tools with component inspector, profiler, and logger.
▶️ Run:node examples/devtools-demo.js/** * @name DevTools * @category Full Apps * @description Developer tools with component inspector, profiler, and logger. */ import { createInspector, inspect, validateComponent, createProfiler, measure, createLogger, LogLevel } from '@coherent.js/devtools'; console.log('\n=== Coherent.js DevTools Demo ===\n'); // Example 1: Component Inspector console.log('--- Example 1: Component Inspector ---\n'); const testComponent = { div: { className: 'container', id: 'main', children: [ { h1: { text: 'Hello World' } }, { p: { className: 'description', text: 'This is a test component' } }, { ul: { children: [ { li: { text: 'Item 1' } }, { li: { text: 'Item 2' } }, { li: { text: 'Item 3' } } ] } } ] } }; const inspector = createInspector({ verbose: true }); const inspection = inspector.inspect(testComponent, { name: 'TestComponent' }); console.log('Inspection Results:'); console.log(`- ID: ${inspection.id}`); console.log(`- Valid: ${inspection.analysis.valid}`); console.log(`- Root Elements: ${inspection.analysis.rootElements.join(', ')}`); console.log(`- Element Count: ${inspection.stats.elementCount}`); console.log(`- Tree Depth: ${inspection.stats.depth}`); console.log(`- Text Nodes: ${inspection.stats.textNodes}`); console.log(`- Has Styles: ${inspection.stats.hasStyles}`); console.log(`- Has Classes: ${inspection.stats.hasClasses}`); if (inspection.analysis.issues.length > 0) { console.log('\nIssues:'); inspection.analysis.issues.forEach(issue => console.log(` - ${issue}`)); } if (inspection.analysis.warnings.length > 0) { console.log('\nWarnings:'); inspection.analysis.warnings.forEach(warning => console.log(` - ${warning}`)); } // Example 2: Component Validation console.log('\n--- Example 2: Component Validation ---\n'); const validComponent = { div: { children: [ { h1: { text: 'Valid' } } ] } }; const invalidComponent = { div: { children: 'Should be array' // Invalid! } }; console.log('Validating valid component:'); const validation1 = validateComponent(validComponent); console.log(`- Valid: ${validation1.valid}`); console.log(`- Issues: ${validation1.issues.length}`); console.log('\nValidating invalid component:'); const validation2 = validateComponent(invalidComponent); console.log(`- Valid: ${validation2.valid}`); console.log(`- Issues: ${validation2.issues.join(', ')}`); // Example 3: Performance Profiler console.log('\n--- Example 3: Performance Profiler ---\n'); const profiler = createProfiler({ slowThreshold: 10, trackMemory: true }); const sessionId = profiler.startSession('demo-session'); console.log(`Started profiling session: ${sessionId}`); // Simulate some renders for (let i = 0; i < 5; i++) { const measureId = profiler.startRender(`Component${i}`); // Simulate work const start = Date.now(); while (Date.now() - start < Math.random() * 20) { // Busy wait } profiler.endRender(measureId); } const sessionAnalysis = profiler.endSession(sessionId); console.log('\nSession Analysis:'); console.log(`- Duration: ${sessionAnalysis.duration}ms`); console.log(`- Measurements: ${sessionAnalysis.measurements}`); console.log(`- Average: ${sessionAnalysis.analysis.average.toFixed(2)}ms`); console.log(`- Median: ${sessionAnalysis.analysis.median.toFixed(2)}ms`); console.log(`- Min: ${sessionAnalysis.analysis.min.toFixed(2)}ms`); console.log(`- Max: ${sessionAnalysis.analysis.max.toFixed(2)}ms`); console.log(`- P95: ${sessionAnalysis.analysis.p95.toFixed(2)}ms`); console.log(`- Slow Renders: ${sessionAnalysis.analysis.slowRenders}`); console.log(`- Slow %: ${sessionAnalysis.analysis.slowPercentage.toFixed(1)}%`); console.log('\nBy Component:'); Object.entries(sessionAnalysis.byComponent).forEach(([name, stats]) => { console.log(` ${name}: ${stats.count} renders, avg ${stats.average.toFixed(2)}ms`); }); // Example 4: Measure Function console.log('\n--- Example 4: Measure Function ---\n'); const slowFunction = async () => { return new Promise(resolve => { setTimeout(() => resolve('Done'), 50); }); }; console.log('Measuring async function...'); await measure('SlowFunction', slowFunction, profiler); const summary = profiler.getSummary(); console.log(`\nTotal measurements: ${summary.totalMeasurements}`); console.log(`Slow renders: ${summary.slowRenders}`); // Example 5: Development Logger console.log('\n--- Example 5: Development Logger ---\n'); const logger = createLogger({ level: LogLevel.DEBUG, prefix: '[Demo]', timestamp: true, colors: true }); logger.debug('Debug message', { detail: 'This is debug info' }); logger.info('Info message', { status: 'ok' }); logger.warn('Warning message', { code: 'WARN_001' }); logger.error('Error message', { error: 'Something failed' }); // Example 6: Log Grouping console.log('\n--- Example 6: Log Grouping ---\n'); logger.group('Rendering Phase'); logger.info('Starting render'); logger.debug('Processing components'); logger.info('Render complete'); logger.groupEnd(); logger.group('API Calls'); logger.info('Fetching data'); logger.debug('Request sent', { url: '/api/data' }); logger.info('Data received'); logger.groupEnd(); // Example 7: Log Filtering console.log('\n--- Example 7: Log Filtering ---\n'); const filteredLogger = createLogger({ level: LogLevel.WARN // Only warnings and errors }); filteredLogger.debug('This will not be logged'); filteredLogger.info('This will not be logged'); filteredLogger.warn('This WILL be logged'); filteredLogger.error('This WILL be logged'); // Example 8: Custom Log Handler console.log('\n--- Example 8: Custom Log Handler ---\n'); const handlerLogger = createLogger(); const errorLogs = []; handlerLogger.addHandler((logEntry) => { if (logEntry.level >= LogLevel.ERROR) { errorLogs.push(logEntry); } }); handlerLogger.info('Normal log'); handlerLogger.error('Error 1'); handlerLogger.error('Error 2'); console.log(`Captured ${errorLogs.length} error logs`); // Example 9: Log Statistics console.log('\n--- Example 9: Log Statistics ---\n'); const stats = logger.getStats(); console.log('Logger Statistics:'); console.log(`- Total Logs: ${stats.total}`); console.log('- By Level:'); Object.entries(stats.byLevel).forEach(([level, count]) => { if (count > 0) { console.log(` ${level}: ${count}`); } }); // Example 10: Inspector Report console.log('\n--- Example 10: Inspector Report ---\n'); // Inspect multiple components inspector.inspect({ div: { text: 'Component 1' } }); inspector.inspect({ div: { text: 'Component 2' } }); inspector.inspect({ div: { children: [] } }); // Empty children const report = inspector.generateReport(); console.log('Inspector Report:'); console.log(`- Total Inspections: ${report.totalInspections}`); console.log(`- Components Tracked: ${report.componentsTracked}`); console.log(`- History Size: ${report.historySize}`); console.log(`- Total Elements: ${report.summary.totalElements}`); console.log(`- Average Depth: ${report.summary.averageDepth.toFixed(2)}`); console.log(`- With Issues: ${report.summary.componentsWithIssues}`); console.log(`- With Warnings: ${report.summary.componentsWithWarnings}`); // Example 11: Component Comparison console.log('\n--- Example 11: Component Comparison ---\n'); const componentA = { div: { children: [ { h1: { text: 'Title' } }, { p: { text: 'Content' } } ] } }; const componentB = { div: { children: [ { h1: { text: 'Title' } }, { p: { text: 'Content' } }, { footer: { text: 'Footer' } } ] } }; const comparison = inspector.compare(componentA, componentB); console.log('Component Comparison:'); console.log(`- Element Count Diff: ${comparison.statsComparison.elementCount.diff}`); console.log(`- Depth Diff: ${comparison.statsComparison.depth.diff}`); console.log(`- Structure Match: ${comparison.structureMatch}`); // Example 12: Search Components console.log('\n--- Example 12: Search Components ---\n'); const searchResults = inspector.search({ minElements: 2, hasWarnings: false }); console.log(`Found ${searchResults.length} components matching criteria`); // Example 13: Export Logs console.log('\n--- Example 13: Export Logs ---\n'); const jsonExport = logger.export('json'); console.log(`Exported ${jsonExport.length} characters as JSON`); const textExport = logger.export('text'); console.log(`Exported ${textExport.split('\n').length} lines as text`); console.log('\n=== Demo Complete ===\n'); console.log('DevTools Features:'); console.log('✅ Component Inspector - Analyze component structure'); console.log('✅ Component Validation - Check for issues'); console.log('✅ Performance Profiler - Measure render times'); console.log('✅ Session Analysis - Track profiling sessions'); console.log('✅ Development Logger - Structured logging'); console.log('✅ Log Grouping - Organize logs'); console.log('✅ Log Filtering - Control verbosity'); console.log('✅ Custom Handlers - Extend functionality'); console.log('✅ Statistics & Reports - Analyze data'); console.log('✅ Export Capabilities - Save logs');Forms & Validation
JSCoherentComplete user registration form with validation and state management.
▶️ Run:node examples/forms-complete-example.js/** * @name Forms & Validation * @category Full Apps * @description Complete user registration form with validation and state management. */ import { html, render } from '@coherent.js/core'; import { createFormValidator, validators, FormBuilder } from '@coherent.js/forms'; // ============================================================================ // Custom Validators // ============================================================================ /** * Check if username is available (simulated async check) */ async function usernameAvailable(value) { // Simulate API call await new Promise(resolve => setTimeout(resolve, 500)); const takenUsernames = ['admin', 'root', 'user', 'test']; if (takenUsernames.includes(value.toLowerCase())) { return 'This username is already taken'; } return null; } /** * Password strength validator */ function passwordStrength(value) { if (!value) return null; const hasUpperCase = /[A-Z]/.test(value); const hasLowerCase = /[a-z]/.test(value); const hasNumbers = /\d/.test(value); const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value); const strength = [hasUpperCase, hasLowerCase, hasNumbers, hasSpecialChar] .filter(Boolean).length; if (strength < 3) { return 'Password must contain uppercase, lowercase, numbers, and special characters'; } return null; } /** * Confirm password matches */ function confirmPassword(confirmValue, formData) { if (confirmValue !== formData.password) { return 'Passwords do not match'; } return null; } /** * Age validator */ function ageValidator(value) { const age = parseInt(value, 10); if (age < 18) { return 'You must be at least 18 years old'; } if (age > 120) { return 'Please enter a valid age'; } return null; } /** * Phone number validator */ function phoneValidator(value) { const phoneRegex = /^\+?[\d\s\-()]+$/; if (!phoneRegex.test(value)) { return 'Please enter a valid phone number'; } if (value.replace(/\D/g, '').length < 10) { return 'Phone number must be at least 10 digits'; } return null; } // ============================================================================ // Form Schemas // ============================================================================ /** * Step 1: Basic Information */ const basicInfoSchema = { username: [ validators.required('Username is required'), validators.minLength(3, 'Username must be at least 3 characters'), validators.maxLength(20, 'Username must not exceed 20 characters'), validators.pattern(/^[a-zA-Z0-9_]+$/, 'Username can only contain letters, numbers, and underscores'), usernameAvailable, ], email: [ validators.required('Email is required'), validators.email('Please enter a valid email address'), ], password: [ validators.required('Password is required'), validators.minLength(8, 'Password must be at least 8 characters'), passwordStrength, ], confirmPassword: [ validators.required('Please confirm your password'), confirmPassword, ], }; /** * Step 2: Personal Information */ const personalInfoSchema = { firstName: [ validators.required('First name is required'), validators.minLength(2, 'First name must be at least 2 characters'), ], lastName: [ validators.required('Last name is required'), validators.minLength(2, 'Last name must be at least 2 characters'), ], age: [ validators.required('Age is required'), validators.number('Age must be a number'), ageValidator, ], phone: [ validators.required('Phone number is required'), phoneValidator, ], }; /** * Step 3: Preferences */ const preferencesSchema = { newsletter: [], notifications: [], theme: [ validators.required('Please select a theme'), ], bio: [ validators.maxLength(500, 'Bio must not exceed 500 characters'), ], }; // ============================================================================ // Components // ============================================================================ /** * Input Field Component with Validation */ function InputField({ label, name, type = 'text', value = '', error = null, required = false, placeholder = '', disabled = false, }) { return html` <div class="form-group ${error ? 'has-error' : ''}"> <label for="${name}"> ${label} ${required ? html`<span class="required">*</span>` : ''} </label> <input type="${type}" id="${name}" name="${name}" value="${value}" placeholder="${placeholder}" ${required ? 'required' : ''} ${disabled ? 'disabled' : ''} class="${error ? 'error' : ''}" /> ${error ? html`<span class="error-message">${error}</span>` : ''} </div> `; } /** * Textarea Field Component */ function TextareaField({ label, name, value = '', error = null, required = false, placeholder = '', rows = 4, }) { return html` <div class="form-group ${error ? 'has-error' : ''}"> <label for="${name}"> ${label} ${required ? html`<span class="required">*</span>` : ''} </label> <textarea id="${name}" name="${name}" placeholder="${placeholder}" rows="${rows}" ${required ? 'required' : ''} class="${error ? 'error' : ''}" >${value}</textarea> ${error ? html`<span class="error-message">${error}</span>` : ''} <span class="char-count">${value.length}/500</span> </div> `; } /** * Select Field Component */ function SelectField({ label, name, value = '', options = [], error = null, required = false, }) { return html` <div class="form-group ${error ? 'has-error' : ''}"> <label for="${name}"> ${label} ${required ? html`<span class="required">*</span>` : ''} </label> <select id="${name}" name="${name}" ${required ? 'required' : ''} class="${error ? 'error' : ''}" > <option value="">Select ${label}</option> ${options.map(opt => html` <option value="${opt.value}" ${opt.value === value ? 'selected' : ''}> ${opt.label} </option> `)} </select> ${error ? html`<span class="error-message">${error}</span>` : ''} </div> `; } /** * Checkbox Field Component */ function CheckboxField({ label, name, checked = false, description = '' }) { return html` <div class="form-group checkbox-group"> <label class="checkbox-label"> <input type="checkbox" name="${name}" ${checked ? 'checked' : ''} /> <span>${label}</span> </label> ${description ? html`<p class="field-description">${description}</p>` : ''} </div> `; } /** * Progress Indicator */ function ProgressIndicator({ currentStep, totalSteps }) { const percentage = (currentStep / totalSteps) * 100; return html` <div class="progress-indicator"> <div class="progress-steps"> ${Array.from({ length: totalSteps }, (_, i) => { const step = i + 1; const status = step < currentStep ? 'completed' : step === currentStep ? 'active' : 'pending'; return html` <div class="step ${status}"> <div class="step-number">${step}</div> <div class="step-label">Step ${step}</div> </div> `; })} </div> <div class="progress-bar"> <div class="progress-fill" style="width: ${percentage}%"></div> </div> </div> `; } /** * Step 1: Basic Information Form */ function BasicInfoForm({ formData, errors }) { return html` <div class="form-step"> <h2>Basic Information</h2> <p class="step-description">Create your account credentials</p> ${InputField({ label: 'Username', name: 'username', value: formData.username || '', error: errors.username, required: true, placeholder: 'Choose a unique username', })} ${InputField({ label: 'Email', name: 'email', type: 'email', value: formData.email || '', error: errors.email, required: true, placeholder: 'your.email@example.com', })} ${InputField({ label: 'Password', name: 'password', type: 'password', value: formData.password || '', error: errors.password, required: true, placeholder: 'Create a strong password', })} ${InputField({ label: 'Confirm Password', name: 'confirmPassword', type: 'password', value: formData.confirmPassword || '', error: errors.confirmPassword, required: true, placeholder: 'Re-enter your password', })} </div> `; } /** * Step 2: Personal Information Form */ function PersonalInfoForm({ formData, errors }) { return html` <div class="form-step"> <h2>Personal Information</h2> <p class="step-description">Tell us about yourself</p> <div class="form-row"> ${InputField({ label: 'First Name', name: 'firstName', value: formData.firstName || '', error: errors.firstName, required: true, placeholder: 'John', })} ${InputField({ label: 'Last Name', name: 'lastName', value: formData.lastName || '', error: errors.lastName, required: true, placeholder: 'Doe', })} </div> <div class="form-row"> ${InputField({ label: 'Age', name: 'age', type: 'number', value: formData.age || '', error: errors.age, required: true, placeholder: '25', })} ${InputField({ label: 'Phone Number', name: 'phone', type: 'tel', value: formData.phone || '', error: errors.phone, required: true, placeholder: '+1 (555) 123-4567', })} </div> </div> `; } /** * Step 3: Preferences Form */ function PreferencesForm({ formData, errors }) { const themeOptions = [ { value: 'light', label: 'Light' }, { value: 'dark', label: 'Dark' }, { value: 'auto', label: 'Auto (System)' }, ]; return html` <div class="form-step"> <h2>Preferences</h2> <p class="step-description">Customize your experience</p> ${SelectField({ label: 'Theme', name: 'theme', value: formData.theme || '', options: themeOptions, error: errors.theme, required: true, })} ${TextareaField({ label: 'Bio', name: 'bio', value: formData.bio || '', error: errors.bio, placeholder: 'Tell us about yourself...', rows: 4, })} ${CheckboxField({ label: 'Subscribe to newsletter', name: 'newsletter', checked: formData.newsletter || false, description: 'Receive updates about new features and content', })} ${CheckboxField({ label: 'Enable notifications', name: 'notifications', checked: formData.notifications || false, description: 'Get notified about important updates', })} </div> `; } /** * Review Step */ function ReviewStep({ formData }) { return html` <div class="form-step review-step"> <h2>Review Your Information</h2> <p class="step-description">Please review your information before submitting</p> <div class="review-section"> <h3>Account Information</h3> <dl> <dt>Username:</dt> <dd>${formData.username}</dd> <dt>Email:</dt> <dd>${formData.email}</dd> </dl> </div> <div class="review-section"> <h3>Personal Information</h3> <dl> <dt>Name:</dt> <dd>${formData.firstName} ${formData.lastName}</dd> <dt>Age:</dt> <dd>${formData.age}</dd> <dt>Phone:</dt> <dd>${formData.phone}</dd> </dl> </div> <div class="review-section"> <h3>Preferences</h3> <dl> <dt>Theme:</dt> <dd>${formData.theme}</dd> <dt>Newsletter:</dt> <dd>${formData.newsletter ? 'Yes' : 'No'}</dd> <dt>Notifications:</dt> <dd>${formData.notifications ? 'Yes' : 'No'}</dd> ${formData.bio ? html` <dt>Bio:</dt> <dd>${formData.bio}</dd> ` : ''} </dl> </div> </div> `; } /** * Success Message */ function SuccessMessage({ formData }) { return html` <div class="success-message"> <div class="success-icon">✓</div> <h2>Registration Successful!</h2> <p>Welcome, ${formData.firstName}! Your account has been created.</p> <p class="success-details"> We've sent a confirmation email to <strong>${formData.email}</strong> </p> <button class="btn-primary">Go to Dashboard</button> </div> `; } /** * Multi-Step Registration Form */ function RegistrationForm({ currentStep = 1, formData = {}, errors = {}, isSubmitting = false, isSuccess = false, }) { const totalSteps = 4; // 3 form steps + 1 review step if (isSuccess) { return html` <div class="registration-container"> ${SuccessMessage({ formData })} </div> `; } return html` <div class="registration-container"> <div class="registration-header"> <h1>Create Your Account</h1> <p>Join our community today</p> </div> ${ProgressIndicator({ currentStep, totalSteps })} <form class="registration-form" onsubmit="return handleSubmit(event)"> ${currentStep === 1 ? BasicInfoForm({ formData, errors }) : ''} ${currentStep === 2 ? PersonalInfoForm({ formData, errors }) : ''} ${currentStep === 3 ? PreferencesForm({ formData, errors }) : ''} ${currentStep === 4 ? ReviewStep({ formData }) : ''} <div class="form-actions"> ${currentStep > 1 ? html` <button type="button" class="btn-secondary" onclick="previousStep()"> Previous </button> ` : ''} ${currentStep < totalSteps ? html` <button type="button" class="btn-primary" onclick="nextStep()"> Next </button> ` : html` <button type="submit" class="btn-primary" ${isSubmitting ? 'disabled' : ''}> ${isSubmitting ? 'Submitting...' : 'Create Account'} </button> `} </div> </form> </div> `; } /** * Main App Component */ function App() { // Simulated form state const formData = { username: 'johndoe', email: 'john@example.com', password: 'SecurePass123!', confirmPassword: 'SecurePass123!', firstName: 'John', lastName: 'Doe', age: '28', phone: '+1 (555) 123-4567', theme: 'dark', bio: 'Full-stack developer passionate about web technologies.', newsletter: true, notifications: true, }; const errors = {}; return html` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>User Registration - Coherent.js Forms Example</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 40px 20px; } .registration-container { max-width: 700px; margin: 0 auto; background: white; border-radius: 12px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); overflow: hidden; } .registration-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 40px; text-align: center; } .registration-header h1 { font-size: 2em; margin-bottom: 10px; } .progress-indicator { padding: 30px 40px; background: #f8f9fa; border-bottom: 1px solid #e9ecef; } .progress-steps { display: flex; justify-content: space-between; margin-bottom: 20px; } .step { display: flex; flex-direction: column; align-items: center; flex: 1; } .step-number { width: 40px; height: 40px; border-radius: 50%; background: #e9ecef; color: #6c757d; display: flex; align-items: center; justify-content: center; font-weight: bold; margin-bottom: 8px; } .step.active .step-number { background: #667eea; color: white; } .step.completed .step-number { background: #28a745; color: white; } .step-label { font-size: 0.85em; color: #6c757d; } .progress-bar { height: 8px; background: #e9ecef; border-radius: 4px; overflow: hidden; } .progress-fill { height: 100%; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); transition: width 0.3s ease; } .registration-form { padding: 40px; } .form-step h2 { color: #2c3e50; margin-bottom: 10px; } .step-description { color: #6c757d; margin-bottom: 30px; } .form-group { margin-bottom: 25px; } .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } label { display: block; font-weight: 600; margin-bottom: 8px; color: #2c3e50; } .required { color: #dc3545; margin-left: 4px; } input, select, textarea { width: 100%; padding: 12px; border: 2px solid #e9ecef; border-radius: 6px; font-size: 14px; transition: border-color 0.2s; } input:focus, select:focus, textarea:focus { outline: none; border-color: #667eea; } input.error, select.error, textarea.error { border-color: #dc3545; } .error-message { display: block; color: #dc3545; font-size: 0.85em; margin-top: 6px; } .char-count { display: block; text-align: right; font-size: 0.85em; color: #6c757d; margin-top: 4px; } .checkbox-group { display: flex; flex-direction: column; } .checkbox-label { display: flex; align-items: center; cursor: pointer; font-weight: normal; } .checkbox-label input[type="checkbox"] { width: auto; margin-right: 10px; } .field-description { font-size: 0.85em; color: #6c757d; margin-top: 6px; margin-left: 30px; } .review-section { background: #f8f9fa; padding: 20px; border-radius: 6px; margin-bottom: 20px; } .review-section h3 { color: #2c3e50; margin-bottom: 15px; font-size: 1.1em; } .review-section dl { display: grid; grid-template-columns: 150px 1fr; gap: 10px; } .review-section dt { font-weight: 600; color: #6c757d; } .review-section dd { color: #2c3e50; } .form-actions { display: flex; justify-content: space-between; gap: 15px; margin-top: 30px; padding-top: 30px; border-top: 1px solid #e9ecef; } button { padding: 12px 30px; border: none; border-radius: 6px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.2s; } .btn-primary { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; flex: 1; } .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); } .btn-primary:disabled { opacity: 0.6; cursor: not-allowed; transform: none; } .btn-secondary { background: #e9ecef; color: #2c3e50; } .btn-secondary:hover { background: #dee2e6; } .success-message { padding: 60px 40px; text-align: center; } .success-icon { width: 80px; height: 80px; border-radius: 50%; background: #28a745; color: white; font-size: 48px; display: flex; align-items: center; justify-content: center; margin: 0 auto 30px; } .success-message h2 { color: #2c3e50; margin-bottom: 15px; } .success-details { color: #6c757d; margin: 20px 0 30px; } @media (max-width: 768px) { .form-row { grid-template-columns: 1fr; } .progress-steps { flex-wrap: wrap; } .step { flex-basis: 50%; margin-bottom: 15px; } } </style> </head> <body> ${RegistrationForm({ currentStep: 4, formData, errors, isSubmitting: false, isSuccess: false, })} </body> </html> `; } // ============================================================================ // Demo: Form Validation // ============================================================================ console.log('='.repeat(80)); console.log('Coherent.js Forms Complete Example'); console.log('='.repeat(80)); // Test validation console.log('\n🧪 Testing Form Validation...\n'); const validator = createFormValidator(basicInfoSchema); // Test valid data const validData = { username: 'johndoe123', email: 'john@example.com', password: 'SecurePass123!', confirmPassword: 'SecurePass123!', }; const validResult = validator.validate(validData); console.log('✅ Valid data:', validResult.isValid ? 'PASSED' : 'FAILED'); // Test invalid data const invalidData = { username: 'ab', // Too short email: 'invalid-email', // Invalid format password: 'weak', // Too weak confirmPassword: 'different', // Doesn't match }; const invalidResult = validator.validate(invalidData); console.log('❌ Invalid data:', !invalidResult.isValid ? 'PASSED' : 'FAILED'); console.log(' Errors found:', Object.keys(invalidResult.errors).length); // Render the form console.log('\n📝 Rendering Registration Form...\n'); const renderedForm = render(App()); console.log('✅ Form rendered successfully'); console.log(` Length: ${renderedForm.length} characters`); console.log('\n' + '='.repeat(80)); console.log('✅ Forms example complete!'); console.log('='.repeat(80)); export { App, RegistrationForm, basicInfoSchema, personalInfoSchema, preferencesSchema };Internationalization
JSCoherentMulti-language blog application with locale switching.
▶️ Run:node examples/i18n-complete-example.js/** * @name Internationalization * @category Full Apps * @description Multi-language blog application with locale switching. */ import { html, render } from '@coherent.js/core'; import { createTranslator, formatters } from '@coherent.js/i18n'; // ============================================================================ // Translation Files // ============================================================================ const translations = { en: { app: { title: 'My Blog', tagline: 'Thoughts on web development', }, nav: { home: 'Home', about: 'About', contact: 'Contact', language: 'Language', }, blog: { readMore: 'Read more', publishedOn: 'Published on {date}', author: 'By {author}', comments: { zero: 'No comments', one: '1 comment', other: '{count} comments', }, tags: 'Tags', share: 'Share this post', }, footer: { copyright: '© {year} My Blog. All rights reserved.', followUs: 'Follow us', }, stats: { views: '{count, number} views', likes: '{count, number} likes', shares: '{count, number} shares', }, price: { amount: '{amount, currency}', from: 'From {amount, currency}', }, }, fr: { app: { title: 'Mon Blog', tagline: 'Réflexions sur le développement web', }, nav: { home: 'Accueil', about: 'À propos', contact: 'Contact', language: 'Langue', }, blog: { readMore: 'Lire la suite', publishedOn: 'Publié le {date}', author: 'Par {author}', comments: { zero: 'Aucun commentaire', one: '1 commentaire', other: '{count} commentaires', }, tags: 'Étiquettes', share: 'Partager cet article', }, footer: { copyright: '© {year} Mon Blog. Tous droits réservés.', followUs: 'Suivez-nous', }, stats: { views: '{count, number} vues', likes: '{count, number} j\'aime', shares: '{count, number} partages', }, price: { amount: '{amount, currency}', from: 'À partir de {amount, currency}', }, }, es: { app: { title: 'Mi Blog', tagline: 'Reflexiones sobre desarrollo web', }, nav: { home: 'Inicio', about: 'Acerca de', contact: 'Contacto', language: 'Idioma', }, blog: { readMore: 'Leer más', publishedOn: 'Publicado el {date}', author: 'Por {author}', comments: { zero: 'Sin comentarios', one: '1 comentario', other: '{count} comentarios', }, tags: 'Etiquetas', share: 'Compartir esta publicación', }, footer: { copyright: '© {year} Mi Blog. Todos los derechos reservados.', followUs: 'Síguenos', }, stats: { views: '{count, number} vistas', likes: '{count, number} me gusta', shares: '{count, number} compartidos', }, price: { amount: '{amount, currency}', from: 'Desde {amount, currency}', }, }, ar: { app: { title: 'مدونتي', tagline: 'أفكار حول تطوير الويب', }, nav: { home: 'الرئيسية', about: 'حول', contact: 'اتصل', language: 'اللغة', }, blog: { readMore: 'اقرأ المزيد', publishedOn: 'نُشر في {date}', author: 'بواسطة {author}', comments: { zero: 'لا توجد تعليقات', one: 'تعليق واحد', other: '{count} تعليقات', }, tags: 'الوسوم', share: 'شارك هذا المنشور', }, footer: { copyright: '© {year} مدونتي. جميع الحقوق محفوظة.', followUs: 'تابعنا', }, stats: { views: '{count, number} مشاهدة', likes: '{count, number} إعجاب', shares: '{count, number} مشاركة', }, price: { amount: '{amount, currency}', from: 'من {amount, currency}', }, }, }; // ============================================================================ // Locale Configuration // ============================================================================ const localeConfig = { en: { name: 'English', direction: 'ltr', dateFormat: 'MM/DD/YYYY', currency: 'USD', }, fr: { name: 'Français', direction: 'ltr', dateFormat: 'DD/MM/YYYY', currency: 'EUR', }, es: { name: 'Español', direction: 'ltr', dateFormat: 'DD/MM/YYYY', currency: 'EUR', }, ar: { name: 'العربية', direction: 'rtl', dateFormat: 'DD/MM/YYYY', currency: 'SAR', }, }; // ============================================================================ // Sample Blog Data // ============================================================================ const blogPosts = [ { id: 1, title: { en: 'Getting Started with Web Components', fr: 'Débuter avec les Web Components', es: 'Comenzando con Web Components', ar: 'البدء مع مكونات الويب', }, excerpt: { en: 'Learn how to build reusable web components...', fr: 'Apprenez à créer des composants web réutilisables...', es: 'Aprende a construir componentes web reutilizables...', ar: 'تعلم كيفية بناء مكونات ويب قابلة لإعادة الاستخدام...', }, author: 'John Doe', publishedDate: new Date('2024-01-15'), views: 1234, likes: 56, shares: 12, comments: 8, tags: ['web-components', 'javascript', 'tutorial'], price: 29.99, }, { id: 2, title: { en: 'Advanced State Management', fr: 'Gestion d\'état avancée', es: 'Gestión de estado avanzada', ar: 'إدارة الحالة المتقدمة', }, excerpt: { en: 'Deep dive into state management patterns...', fr: 'Plongée profonde dans les modèles de gestion d\'état...', es: 'Inmersión profunda en patrones de gestión de estado...', ar: 'غوص عميق في أنماط إدارة الحالة...', }, author: 'Jane Smith', publishedDate: new Date('2024-01-10'), views: 2345, likes: 89, shares: 23, comments: 15, tags: ['state-management', 'architecture', 'advanced'], price: 39.99, }, { id: 3, title: { en: 'Performance Optimization Tips', fr: 'Conseils d\'optimisation des performances', es: 'Consejos de optimización de rendimiento', ar: 'نصائح تحسين الأداء', }, excerpt: { en: 'Make your applications faster and more efficient...', fr: 'Rendez vos applications plus rapides et plus efficaces...', es: 'Haz tus aplicaciones más rápidas y eficientes...', ar: 'اجعل تطبيقاتك أسرع وأكثر كفاءة...', }, author: 'Bob Johnson', publishedDate: new Date('2024-01-05'), views: 3456, likes: 123, shares: 45, comments: 0, tags: ['performance', 'optimization', 'best-practices'], price: 49.99, }, ]; // ============================================================================ // Components // ============================================================================ /** * Language Selector Component */ function LanguageSelector({ currentLocale, onLanguageChange, t }) { const languages = Object.keys(translations); return html` <div class="language-selector"> <label>${t('nav.language')}:</label> <select onchange="${(e) => onLanguageChange(e.target.value)}" value="${currentLocale}"> ${languages.map(lang => html` <option value="${lang}" ${lang === currentLocale ? 'selected' : ''}> ${localeConfig[lang].name} </option> `)} </select> </div> `; } /** * Navigation Component */ function Navigation({ locale, t }) { return html` <nav class="navigation"> <ul> <li><a href="/">${t('nav.home')}</a></li> <li><a href="/about">${t('nav.about')}</a></li> <li><a href="/contact">${t('nav.contact')}</a></li> </ul> </nav> `; } /** * Blog Post Card Component */ function BlogPostCard({ post, locale, t, formatNumber, formatCurrency, formatDate }) { const config = localeConfig[locale]; // Get pluralized comment count const commentCount = post.comments; const commentKey = commentCount === 0 ? 'zero' : commentCount === 1 ? 'one' : 'other'; const commentsText = t(`blog.comments.${commentKey}`, { count: commentCount }); return html` <article class="blog-post-card" dir="${config.direction}"> <h2>${post.title[locale]}</h2> <div class="post-meta"> <span class="author">${t('blog.author', { author: post.author })}</span> <span class="date"> ${t('blog.publishedOn', { date: formatDate(post.publishedDate, { year: 'numeric', month: 'long', day: 'numeric' }) })} </span> </div> <p class="excerpt">${post.excerpt[locale]}</p> <div class="post-stats"> <span class="views">${t('stats.views', { count: formatNumber(post.views) })}</span> <span class="likes">${t('stats.likes', { count: formatNumber(post.likes) })}</span> <span class="shares">${t('stats.shares', { count: formatNumber(post.shares) })}</span> <span class="comments">${commentsText}</span> </div> <div class="post-tags"> <strong>${t('blog.tags')}:</strong> ${post.tags.map(tag => html`<span class="tag">${tag}</span>`)} </div> <div class="post-price"> ${t('price.from', { amount: formatCurrency(post.price, config.currency) })} </div> <div class="post-actions"> <button class="btn-primary">${t('blog.readMore')}</button> <button class="btn-secondary">${t('blog.share')}</button> </div> </article> `; } /** * Footer Component */ function Footer({ t, formatNumber }) { const currentYear = new Date().getFullYear(); return html` <footer class="footer"> <p>${t('footer.copyright', { year: currentYear })}</p> <div class="social-links"> <p>${t('footer.followUs')}:</p> <a href="https://twitter.com">Twitter</a> <a href="https://github.com">GitHub</a> <a href="https://linkedin.com">LinkedIn</a> </div> </footer> `; } /** * Main App Component */ function App({ locale = 'en' }) { // Create translator const translator = createTranslator(translations, locale); const t = translator.t.bind(translator); // Get locale configuration const config = localeConfig[locale]; // Create formatters const formatNumber = (num) => formatters.number(num, locale); const formatCurrency = (amount, currency) => formatters.currency(amount, locale, currency); const formatDate = (date, options) => formatters.date(date, locale, options); return html` <!DOCTYPE html> <html lang="${locale}" dir="${config.direction}"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>${t('app.title')}</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; background: #f5f5f5; } [dir="rtl"] { text-align: right; } .container { max-width: 1200px; margin: 0 auto; padding: 20px; } header { background: white; padding: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1); margin-bottom: 30px; } .header-content { max-width: 1200px; margin: 0 auto; padding: 0 20px; display: flex; justify-content: space-between; align-items: center; } h1 { color: #2c3e50; margin-bottom: 5px; } .tagline { color: #7f8c8d; font-size: 0.9em; } .language-selector { display: flex; align-items: center; gap: 10px; } .language-selector select { padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; } .navigation { background: #34495e; margin-bottom: 30px; } .navigation ul { max-width: 1200px; margin: 0 auto; padding: 0 20px; list-style: none; display: flex; gap: 30px; } .navigation a { color: white; text-decoration: none; padding: 15px 0; display: block; } .navigation a:hover { color: #3498db; } .blog-posts { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 30px; margin-bottom: 50px; } .blog-post-card { background: white; border-radius: 8px; padding: 25px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); transition: transform 0.2s; } .blog-post-card:hover { transform: translateY(-4px); box-shadow: 0 4px 12px rgba(0,0,0,0.15); } .blog-post-card h2 { color: #2c3e50; margin-bottom: 15px; font-size: 1.5em; } .post-meta { display: flex; gap: 15px; margin-bottom: 15px; font-size: 0.9em; color: #7f8c8d; } .excerpt { margin-bottom: 20px; color: #555; } .post-stats { display: flex; gap: 15px; margin-bottom: 15px; font-size: 0.9em; color: #7f8c8d; flex-wrap: wrap; } .post-tags { margin-bottom: 15px; } .tag { display: inline-block; background: #ecf0f1; padding: 4px 12px; border-radius: 12px; font-size: 0.85em; margin: 0 5px; } .post-price { font-size: 1.2em; font-weight: bold; color: #27ae60; margin-bottom: 15px; } .post-actions { display: flex; gap: 10px; } button { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; font-size: 14px; transition: background 0.2s; } .btn-primary { background: #3498db; color: white; } .btn-primary:hover { background: #2980b9; } .btn-secondary { background: #ecf0f1; color: #2c3e50; } .btn-secondary:hover { background: #bdc3c7; } .footer { background: #2c3e50; color: white; padding: 30px 0; text-align: center; } .social-links { margin-top: 15px; } .social-links a { color: #3498db; margin: 0 10px; text-decoration: none; } .social-links a:hover { text-decoration: underline; } </style> </head> <body> <header> <div class="header-content"> <div> <h1>${t('app.title')}</h1> <p class="tagline">${t('app.tagline')}</p> </div> ${LanguageSelector({ currentLocale: locale, onLanguageChange: (newLocale) => { // In a real app, this would update the URL or state console.log('Language changed to:', newLocale); }, t })} </div> </header> ${Navigation({ locale, t })} <div class="container"> <div class="blog-posts"> ${blogPosts.map(post => BlogPostCard({ post, locale, t, formatNumber, formatCurrency, formatDate }))} </div> </div> ${Footer({ t, formatNumber })} </body> </html> `; } // ============================================================================ // Demo: Render in Different Languages // ============================================================================ console.log('='.repeat(80)); console.log('Coherent.js i18n Complete Example'); console.log('='.repeat(80)); // Render in English console.log('\n📝 Rendering in English...\n'); const htmlEn = render(App({ locale: 'en' })); console.log('✅ English version rendered successfully'); console.log(` Length: ${htmlEn.length} characters`); // Render in French console.log('\n📝 Rendering in French...\n'); const htmlFr = render(App({ locale: 'fr' })); console.log('✅ French version rendered successfully'); console.log(` Length: ${htmlFr.length} characters`); // Render in Spanish console.log('\n📝 Rendering in Spanish...\n'); const htmlEs = render(App({ locale: 'es' })); console.log('✅ Spanish version rendered successfully'); console.log(` Length: ${htmlEs.length} characters`); // Render in Arabic (RTL) console.log('\n📝 Rendering in Arabic (RTL)...\n'); const htmlAr = render(App({ locale: 'ar' })); console.log('✅ Arabic version rendered successfully'); console.log(` Length: ${htmlAr.length} characters`); console.log(' Direction: RTL (Right-to-Left)'); console.log('\n' + '='.repeat(80)); console.log('✅ All languages rendered successfully!'); console.log('='.repeat(80)); // Export for use in other files export { App, translations, localeConfig };