1const http = require('http');
  2const fs = require('fs').promises;
  3const path = require('path');
  4
  5// This file is used for testing wasm build from emscripten
  6// Example build command:
  7// emcmake cmake -B build-wasm -DGGML_WEBGPU=ON -DLLAMA_OPENSSL=OFF
  8// cmake --build build-wasm --target test-backend-ops -j
  9
 10const PORT = 8080;
 11const STATIC_DIR = path.join(__dirname, '../build-wasm/bin');
 12console.log(`Serving static files from: ${STATIC_DIR}`);
 13
 14const mimeTypes = {
 15  '.html': 'text/html',
 16  '.js': 'text/javascript',
 17  '.css': 'text/css',
 18  '.png': 'image/png',
 19  '.jpg': 'image/jpeg',
 20  '.gif': 'image/gif',
 21  '.svg': 'image/svg+xml',
 22  '.json': 'application/json',
 23  '.woff': 'font/woff',
 24  '.woff2': 'font/woff2',
 25};
 26
 27async function generateDirListing(dirPath, reqUrl) {
 28  const files = await fs.readdir(dirPath);
 29  let html = `
 30    <!DOCTYPE html>
 31    <html>
 32    <head>
 33      <title>Directory Listing</title>
 34      <style>
 35        body { font-family: Arial, sans-serif; padding: 20px; }
 36        ul { list-style: none; padding: 0; }
 37        li { margin: 5px 0; }
 38        a { text-decoration: none; color: #0066cc; }
 39        a:hover { text-decoration: underline; }
 40      </style>
 41    </head>
 42    <body>
 43      <h1>Directory: ${reqUrl}</h1>
 44      <ul>
 45  `;
 46
 47  if (reqUrl !== '/') {
 48    html += `<li><a href="../">../ (Parent Directory)</a></li>`;
 49  }
 50
 51  for (const file of files) {
 52    const filePath = path.join(dirPath, file);
 53    const stats = await fs.stat(filePath);
 54    const link = encodeURIComponent(file) + (stats.isDirectory() ? '/' : '');
 55    html += `<li><a href="${link}">${file}${stats.isDirectory() ? '/' : ''}</a></li>`;
 56  }
 57
 58  html += `
 59      </ul>
 60    </body>
 61    </html>
 62  `;
 63  return html;
 64}
 65
 66const server = http.createServer(async (req, res) => {
 67  try {
 68    // Set COOP and COEP headers
 69    res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
 70    res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
 71    res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
 72    res.setHeader('Pragma', 'no-cache');
 73    res.setHeader('Expires', '0');
 74
 75    const filePath = path.join(STATIC_DIR, decodeURIComponent(req.url));
 76    const stats = await fs.stat(filePath);
 77
 78    if (stats.isDirectory()) {
 79      const indexPath = path.join(filePath, 'index.html');
 80      try {
 81        const indexData = await fs.readFile(indexPath);
 82        res.writeHeader(200, { 'Content-Type': 'text/html' });
 83        res.end(indexData);
 84      } catch {
 85        // No index.html, generate directory listing
 86        const dirListing = await generateDirListing(filePath, req.url);
 87        res.writeHeader(200, { 'Content-Type': 'text/html' });
 88        res.end(dirListing);
 89      }
 90    } else {
 91      const ext = path.extname(filePath).toLowerCase();
 92      const contentType = mimeTypes[ext] || 'application/octet-stream';
 93      const data = await fs.readFile(filePath);
 94      res.writeHeader(200, { 'Content-Type': contentType });
 95      res.end(data);
 96    }
 97  } catch (err) {
 98    if (err.code === 'ENOENT') {
 99      res.writeHeader(404, { 'Content-Type': 'text/plain' });
100      res.end('404 Not Found');
101    } else {
102      res.writeHeader(500, { 'Content-Type': 'text/plain' });
103      res.end('500 Internal Server Error');
104    }
105  }
106});
107
108server.listen(PORT, () => {
109  console.log(`Server running at http://localhost:${PORT}/`);
110});