innodb-utils 5.1.0

InnoDB file analysis toolkit
Documentation
// Checksum validation — mirrors `inno checksum`
import { getWasm } from '../wasm.js';
import { esc } from '../utils/html.js';
import { createExportBar } from '../utils/export.js';
import { trackFeatureUse } from '../utils/analytics.js';

export function createChecksums(container, fileData) {
  const wasm = getWasm();
  let report;
  try {
    report = JSON.parse(wasm.validate_checksums(fileData));
  } catch (e) {
    container.innerHTML = `<div class="p-6 text-red-400">Error validating checksums: ${esc(e)}</div>`;
    return;
  }

  const total = report.total_pages;
  const validPct = total > 0 ? ((report.valid_pages / total) * 100).toFixed(1) : '0';
  const allValid = report.invalid_pages === 0;
  trackFeatureUse('checksum_validation', { total, valid: report.valid_pages, invalid: report.invalid_pages });

  container.innerHTML = `
    <div class="p-6 space-y-6 overflow-auto max-h-full">
      <div class="flex items-center gap-3">
        <h2 class="text-lg font-bold text-innodb-cyan">Checksum Validation</h2>
        <span id="checksums-export"></span>
        <span class="${allValid ? 'badge-valid' : 'badge-invalid'} px-2 py-0.5 rounded text-xs font-bold">
          ${allValid ? 'ALL VALID' : `${report.invalid_pages} INVALID`}
        </span>
      </div>

      <div class="grid grid-cols-2 md:grid-cols-5 gap-4">
        ${statCard('Total Pages', total)}
        ${statCard('Valid', report.valid_pages, 'text-innodb-green')}
        ${statCard('Invalid', report.invalid_pages, report.invalid_pages > 0 ? 'text-innodb-red' : '')}
        ${statCard('Empty', report.empty_pages, 'text-gray-500')}
        ${statCard('LSN Mismatches', report.lsn_mismatches, report.lsn_mismatches > 0 ? 'text-innodb-amber' : '')}
      </div>

      <div class="bg-surface-2 rounded-lg p-4">
        <div class="flex items-center gap-2 mb-2">
          <span class="text-sm text-gray-400">Integrity</span>
          <span class="text-sm font-bold ${allValid ? 'text-innodb-green' : 'text-innodb-red'}">${validPct}%</span>
        </div>
        <div class="w-full bg-gray-800 rounded-full h-3 overflow-hidden">
          <div class="h-full rounded-full ${allValid ? 'bg-innodb-green' : 'bg-innodb-red'}" style="width:${validPct}%"></div>
        </div>
      </div>

      <div class="flex items-center gap-2 mb-2">
        <h3 class="text-md font-semibold text-gray-300">Per-Page Results</h3>
        <label class="text-xs text-gray-600 flex items-center gap-1">
          <input type="checkbox" id="show-invalid-only" class="rounded bg-gray-800 border-gray-600" />
          Show invalid only
        </label>
      </div>
      <div id="checksum-table-wrap" class="overflow-x-auto max-h-96">
        ${renderTable(report.pages, false)}
      </div>
    </div>
  `;

  const exportSlot = container.querySelector('#checksums-export');
  if (exportSlot) {
    exportSlot.appendChild(createExportBar(() => report, 'checksums'));
  }

  const checkbox = container.querySelector('#show-invalid-only');
  const wrap = container.querySelector('#checksum-table-wrap');
  checkbox.addEventListener('change', () => {
    wrap.innerHTML = renderTable(report.pages, checkbox.checked);
  });
}

function renderTable(pages, invalidOnly) {
  const filtered = invalidOnly ? pages.filter((p) => p.status !== 'valid') : pages;
  if (filtered.length === 0) {
    return `<div class="text-gray-500 text-sm py-4">No pages to display.</div>`;
  }
  return `
    <table class="w-full text-xs font-mono">
      <thead class="sticky top-0 bg-gray-950">
        <tr class="text-left text-gray-500 border-b border-gray-800">
          <th scope="col" class="py-1 pr-3">#</th>
          <th scope="col" class="py-1 pr-3">Status</th>
          <th scope="col" class="py-1 pr-3">Algorithm</th>
          <th scope="col" class="py-1 pr-3">Stored</th>
          <th scope="col" class="py-1 pr-3">Calculated</th>
          <th scope="col" class="py-1 pr-3">LSN</th>
        </tr>
      </thead>
      <tbody>
        ${filtered.map(checksumRow).join('')}
      </tbody>
    </table>`;
}

function checksumRow(p) {
  const statusClass = p.status === 'valid' ? 'text-innodb-green' : 'text-innodb-red';
  const lsnClass = p.lsn_valid ? 'text-innodb-green' : 'text-innodb-amber';
  return `
    <tr class="border-b border-gray-800/30 hover:bg-surface-2/50">
      <td class="py-1 pr-3 text-gray-400">${p.page_number}</td>
      <td class="py-1 pr-3 ${statusClass} font-bold">${p.status}</td>
      <td class="py-1 pr-3 text-gray-400">${esc(p.algorithm)}</td>
      <td class="py-1 pr-3">0x${p.stored_checksum.toString(16).padStart(8, '0')}</td>
      <td class="py-1 pr-3">0x${p.calculated_checksum.toString(16).padStart(8, '0')}</td>
      <td class="py-1 pr-3 ${lsnClass}">${p.lsn_valid ? 'OK' : 'MISMATCH'}</td>
    </tr>`;
}

function statCard(label, value, colorClass = '') {
  return `
    <div class="bg-surface-2 rounded-lg p-4">
      <div class="text-xs text-gray-500 uppercase tracking-wide">${esc(label)}</div>
      <div class="text-lg font-bold ${colorClass || 'text-gray-100'} mt-1">${value}</div>
    </div>`;
}