All files / src/koa coherent-koa.js

42.67% Statements 67/157
100% Branches 2/2
40% Functions 2/5
42.67% Lines 67/157

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 1681x 1x 1x 1x           1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                     1x 1x   1x 1x 1x 1x 1x 1x 1x                   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                             1x 1x   1x 1x 1x 1x 1x 1x 1x                       1x 1x 1x 1x 1x 1x 1x 1x                                   1x 1x 1x 1x 1x 1x 1x  
/**
 * Koa.js integration for Coherent.js
 * Provides middleware and utilities for using Coherent.js with Koa
 */
 
import { renderToString } from '../rendering/html-renderer.js';
import { performanceMonitor } from '../performance/monitor.js';
import { importPeerDependency } from '../utils/dependency-utils.js';
 
/**
 * Coherent.js Koa middleware
 * Automatically renders Coherent.js components and handles errors
 * 
 * @param {Object} options - Configuration options
 * @param {boolean} options.enablePerformanceMonitoring - Enable performance monitoring
 * @param {string} options.template - HTML template with {{content}} placeholder
 * @returns {Function} Koa middleware function
 */
export function coherentKoaMiddleware(options = {}) {
  const {
    enablePerformanceMonitoring = false,
    template = '<!DOCTYPE html>\n{{content}}'
  } = options;
  
  return async (ctx, next) => {
    await next();
    
    // If response body is a Coherent.js object, render it
    if (isCoherentObject(ctx.body)) {
      try {
        let html;
        
        if (enablePerformanceMonitoring) {
          const renderId = performanceMonitor.startRender();
          html = renderToString(ctx.body);
          performanceMonitor.endRender(renderId);
        } else {
          html = renderToString(ctx.body);
        }
        
        // Apply template
        const finalHtml = template.replace('{{content}}', html);
        
        // Set content type and body
        ctx.type = 'text/html';
        ctx.body = finalHtml;
      } catch (error) {
        console.error('Coherent.js rendering error:', error);
        throw error;
      }
    }
  };
}
 
/**
 * Check if an object is a Coherent.js component object
 * A Coherent.js component is a plain object with a single key
 * 
 * @param {any} obj - Object to check
 * @returns {boolean} True if object is a Coherent.js component
 */
function isCoherentObject(obj) {
  if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
    return false;
  }
  
  const keys = Object.keys(obj);
  return keys.length === 1;
}
 
/**
 * Create a Koa route handler for Coherent.js components
 * 
 * @param {Function} componentFactory - Function that returns a Coherent.js component
 * @param {Object} options - Handler options
 * @returns {Function} Koa route handler
 */
export function createCoherentKoaHandler(componentFactory, options = {}) {
  const {
    enablePerformanceMonitoring = false,
    template = '<!DOCTYPE html>\n{{content}}'
  } = options;
  
  return async (ctx, next) => {
    try {
      // Create component with context data
      const component = await Promise.resolve(
        componentFactory(ctx, next)
      );
      
      if (!component) {
        throw new Error('Component factory returned null/undefined');
      }
      
      // Render component
      let html;
      if (enablePerformanceMonitoring) {
        const renderId = performanceMonitor.startRender();
        html = renderToString(component);
        performanceMonitor.endRender(renderId);
      } else {
        html = renderToString(component);
      }
      
      // Apply template
      const finalHtml = template.replace('{{content}}', html);
      
      // Set response
      ctx.type = 'text/html';
      ctx.body = finalHtml;
    } catch (error) {
      console.error('Coherent.js handler error:', error);
      throw error;
    }
  };
}
 
/**
 * Setup Coherent.js with Koa app
 * 
 * @param {Object} app - Koa app instance
 * @param {Object} options - Setup options
 */
export function setupCoherentKoa(app, options = {}) {
  const {
    useMiddleware = true,
    enablePerformanceMonitoring = false
  } = options;
  
  // Use middleware for automatic rendering
  if (useMiddleware) {
    app.use(coherentKoaMiddleware({ enablePerformanceMonitoring }));
  }
}
 
/**
 * Create Koa integration with dependency checking
 * This function ensures Koa is available before setting up the integration
 * 
 * @param {Object} options - Setup options
 * @returns {Promise<Function>} - Function to setup Koa integration
 */
export async function createKoaIntegration(options = {}) {
  try {
    // Verify Koa is available
    await importPeerDependency('koa', 'Koa.js');
    
    return function(app) {
      if (!app || typeof app.use !== 'function') {
        throw new Error('Invalid Koa app instance provided');
      }
      
      setupCoherentKoa(app, options);
      return app;
    };
  } catch (error) {
    throw error;
  }
}
 
// Export all utilities
export default {
  coherentKoaMiddleware,
  createCoherentKoaHandler,
  setupCoherentKoa,
  createKoaIntegration
};