SEOmator SEO Audit Tool

repository·main·Indexed 43 days ago

https://github.com/seo-skills/seo-audit-skill

A comprehensive SEO audit CLI tool, version 5.0.0, that scans websites against 332 rules across 20 categories. It features multi-page crawling, Core Web Vitals measurement, and a local dashboard. SEOmator provides various output formats including JSON, HTML, and an AI-optimized LLM format for agents like Claude Code. It can be integrated into CI/CD pipelines via GitHub Actions and GitLab CI, or used programmatically via the @seomator/seo-audit API.

Tokens
104.6K
Snippets
235
Records
395
Agent score
60%

What's inside SEOmator

  1. Identify the six audit output surfaces

    main

    The SEOmator SEO Audit tool provides six different ways to view audit results, each with different default behaviors and use cases:

    1. Terminal: The default output when running seomator audit <url>. It sorts categories worst-first and ranks issues by severity.
    2. Static HTML report: Generated using the --format html flag. It is a highly visual, single-file report (includes inline CSS).
    3. Web dashboard: Accessed via seomator serve. A functional web UI for viewing audits.
    4. Electron app: The desktop application, which inherits the Web dashboard UI.
    5. Markdown report: Generated using the --format markdown flag.
    6. LLM report: Generated using the --format llm. This format is specifically designed for use with the Claude Code skill.
    # Default (Terminal)
    seomator audit <url>
    
    # HTML Report
    seomator audit <url> --format html
    
    # Markdown Report
    seomator audit <url> --format markdown
    
    # LLM Report
    seomator audit <url> --format llm
    
    # Web Dashboard
    seomator serve
  2. Directory Structure of the SEOmator Desktop App

    main

    The project is organized into three main areas:

    • electron/: Contains the Electron-specific logic.
      • main/: Node.js logic, IPC bridges (audit-bridge.ts, db-bridge.ts), and window management.
      • preload/: The security bridge exposing electronAPI.
      • electron-vite.config.ts: Build configuration for main, preload, and renderer targets.
    • ui/: The React application (Renderer process).
      • components/: UI elements like AuditRunner.tsx, ScoreCircle.tsx, and IssuesTable.tsx.
      • hooks/: React hooks for interacting with the app (useAudit.ts, useAuditHistory.ts).
      • lib/: Utilities, including ipc-client.ts for accessing window.electronAPI.
      • stores/: State management using Zustand (audit-store.ts).
    • shared/: Contains ipc-types.ts which defines IPC channel names and payload types used by both Main and Renderer processes.
    • src/: The core audit engine (not part of the Electron layer, but used by the Main process).
    electron/
    ├── electron-vite.config.ts       # Triple-target Vite build (main + preload + renderer)
    ├── tsconfig.json                 # TypeScript config with @core/* and @renderer/* aliases
    ├── main/                         # Main process (Node.js context — full system access)
    │   ├── index.ts                  # BrowserWindow creation, IPC handler registration
    │   ├── audit-bridge.ts           # Wraps Auditor class → IPC events
    │   └── db-bridge.ts              # Wraps AuditsDatabase → IPC invoke/handle
    ├── preload/                      # Preload script (security boundary)
    │   └── index.ts                  # contextBridge — exposes typed electronAPI
    └── (the renderer lives in `ui/`, one level up)
    
    ui/                               # Renderer process (browser context — React app)
    │   ├── index.html                # HTML shell
    │   ├── main.tsx                  # React entry point
    │   ├── App.tsx                  # Root component — header + page routing
    │   ├── components/               # 15 React components
    │   ├── pages/                   # AuditPage.tsx, HistoryPage.tsx
    │   ├── hooks/                   # useAudit.ts, useAuditHistory.ts, useTheme.ts
    │   ├── stores/                   # audit-store.ts (Zustand)
    │   ├── lib/                     # ipc-client.ts, format.ts, fix-suggestions.ts
    │   └── styles/                  # tailwind.css, globals.css
    
    shared/                           # Shared between main and renderer
    └── ipc-types.ts                  # IPC channel names + payload types
  3. Handle 'not-measured' rule statuses

    main

    A not-measured status indicates a rule was not executed (e.g., when Core Web Vitals are disabled). This is treated as a prompt block rather than a finding.

    Data Encoding: Because of database constraints (audit_results.score is INTEGER NOT NULL), not-measured rules are assigned a score of 50. The status is determined by checking both the status string and the weight:

    • In SQL: weight IS NOT NULL AND (weight = 0 OR status = 'not-measured')
    • In TypeScript: r.status === 'not-measured' || r.weight === 0

    Important: Do not rely on the score of 50 to identify unmeasured rules; always check the status property.

  4. Performance considerations for large audits (1,000+ pages)

    main

    When working with or optimizing the audit engine, be aware of performance bottlenecks that emerge at scale (e.g., 1,000+ pages):

    • Terminal Grouping: The current implementation in terminal.ts:134 is $O(n \cdot m)$ due to regex applications. For large audits, use a Map keyed once per result.
    • HTML Report Size: The html-reporter can emit tens of MBs because data-urls are emitted twice per rule. To mitigate this, cap sample pages and drop the data-urls attribute.
    • Aggregation Latency: getRuleSummaries in aggregate.ts is linear relative to the number of rows (e.g., 332,000 rows). Consider persisting a summary row per rule at save time to avoid full table scans on every read.
  5. Understand the SEOmator Three-Process Model

    main

    The application is split into three isolated processes to ensure security and separation of concerns:

    1. Main Process (electron/main/)

      • Runs in a Node.js context with full system access (file system, native modules).
      • Manages the BrowserWindow and app lifecycle.
      • Hosts the IPC bridges: audit-bridge.ts (for running audits via AuditSession) and db-bridge.ts (for database queries via src/dashboard/queries.ts).
      • Note: sandbox: false must be configured in electron/main/index.ts to support the better-sqlite3 native C++ addon.
    2. Preload Script (electron/preload/)

      • Acts as a secure boundary between the Main and Renderer processes.
      • Uses contextBridge.exposeInMainWorld() to expose a typed window.electronAPI to the UI.
      • This is the only layer that can access both Node.js APIs and the DOM.
    3. Renderer Process (ui/)

      • Runs the React application in a standard browser context.
      • Has no direct Node.js access.
      • Communicates with the Main process exclusively through the window.electronAPI provided by the preload script.
      • The UI is decoupled from Electron so it can also be served as a web dashboard.
  6. Identify Audit Result Encodings and Migration Logic

    main

    The AuditResult data can exist in three different encodings depending on when it was written to the database. Understanding these is critical for performing comparisons and migrations without breaking CI builds.

    EncodingVersion Rangeweight valuestatus valueMeaning
    Apre-3.4.0NULLN/AMeasured (Legacy)
    B3.4.0–3.5.00'warn'Not Measured
    CPost-Plan0'not-measured'Not Measured

    Critical Migration Rule: To avoid false positives in seomator compare --fail-on-regression, the system uses weight as the authoritative field. A change that keys only on status would incorrectly treat legacy 'not measured' checks as real warnings. An audit in Encoding B compared against the same site in Encoding C must produce an empty RuleDiff.

  7. Understand the database type system

    main

    The project uses a tiered type system to distinguish between raw database rows and application-ready objects. All types are defined in src/storage/types.ts:

    • Db*: Raw database records that match SQLite column names exactly (e.g., crawl_id, html_compressed as 0|1).
    • Hydrated*: Enriched records used in application logic. These feature camelCase keys and parsed types (e.g., crawlId, htmlCompressed as boolean, and Date objects instead of strings).
    • *Options: Configuration/filter objects for query operations.
    • *Input: Types used for creating or inserting new records.
    // Raw database record
    interface DbPage {
      id: number;
      crawl_id: number;
      url: string;
      html_compressed: number;  // 0 or 1
    }
    
    // Hydrated for application use
    interface HydratedPage {
      id: number;
      crawlId: number;         // camelCase
      url: string;
      htmlCompressed: boolean; // boolean, not 0/1
      crawledAt: Date;         // Date, not string
    }
  8. Security considerations for the Local Web Dashboard

    main

    When running the local dashboard via seomator serve, be aware of the following security model and mitigations:

    Request Security

    • Origin Validation: The server uses Origin and Sec-Fetch-Site checks to prevent malicious websites from driving the local server via cross-site fetch or form POSTs to /api/runs.
    • DNS Rebinding: A Host allow-list is used to mitigate DNS rebinding attacks.
    • Authentication: Every /api request requires a per-launch token (provided via cookie or header) to prevent unauthorized access from other processes on the same machine.

    Data & Privacy

    • Data at Rest: By default, audited URLs, rule details (e.g., cookie names, headers), and run options are persisted in audits.db. To prevent this, use the --no-save flag or set save = false in your configuration.
    • Credential Handling: Userinfo in URLs (e.g., https://user:pass@host) is stripped before storage and display. However, secrets passed in query strings remain the user's responsibility.
    • XSS Protection: React is used for automatic escaping; dangerouslySetInnerHTML is prohibited to prevent stored XSS from audited page text.

    Resource Limits

    • Oversized Bodies: POST requests are capped at 64 kB; larger bodies return a 413 error.
    • DoS Protection: Only a single run is allowed at a time (returns 409 if a run is in progress).
    • Stream Exhaustion: Server-Sent Events (SSE) connections are capped at 8 to prevent exhaustion.
  9. Understand the SEOmator Local Web Dashboard architecture

    main

    The Local Web Dashboard is a multi-surface tool designed to provide a visual interface for SEO audits.

    Core Components:

    • Engine: Performs the actual crawling, rendering, and rule execution. It supports aborting mid-crawl and handles browser lifecycle management.
    • Storage: Uses a SQLite database to persist audit results, allowing for historical comparisons and trend analysis. It handles schema migrations and concurrent access.
    • Session Management: Manages active audit runs. It ensures only one auditor is running at a time (using a synchronous reservation slot) and handles graceful shutdowns.
    • Server: A local HTTP server that exposes an API for running audits, streaming real-time events via SSE, and retrieving historical data. It includes security features like CSP/XFO headers and token-based authentication.
    • Renderer (UI): A web-based interface (built with vitest workspace jsdom for testing) that consumes the API to display audit details, comparison lists, and trend charts.
  10. Understand the SEOmator Desktop App architecture

    main

    The SEOmator Desktop App is built using an Electron-based three-process model designed for security and performance. It is purely additive, meaning it shares the same core logic (the Auditor class, rule registry, and scoring engine) as the CLI via the @core alias. This ensures that bug fixes in the core logic automatically apply to both the CLI and the desktop app without synchronization issues.

    The architecture consists of three distinct layers:

    1. Main Process (electron/main/): Handles system-level operations and heavy lifting.
    2. Preload Script (electron/preload/): Acts as a secure bridge between the Main and Renderer processes.
    3. Renderer Process (ui/): The user interface layer.

    Key design patterns include:

    • IPC Communication: Uses send/on for long-running audit operations to allow real-time progress streaming, while using invoke/handle for simple request-response database queries.
    • Security: Employs contextBridge to expose only specific functions to the renderer, preventing XSS vulnerabilities from gaining full Node.js access.
    • State Management: Uses Zustand for a lightweight, zero-boilerplate state machine to manage audit states.
    • Styling: Uses CSS custom properties derived from the HTML reporter to ensure visual consistency between the desktop app and generated reports.
  11. Understand the SEOmator SEO Audit Design System Approach

    main

    The project uses Approach B for its design system to ensure consistency across different output surfaces (HTML, Terminal, React UI, LLM/Markdown).

    Instead of restyling each surface in place, the project uses shared tokens and shared pure TypeScript logic with renderer-native components. This approach prevents 'drift' in grades, statuses, and colors. Each renderer remains idiomatic:

    • HTML: Uses a string builder.
    • React UI: Uses React components.
    • Terminal: Uses chalk.

    This ensures that the core logic (like scoring and status) is defined once and reused by all reporters.

  12. Avoid UI bundle bloat by restricting imports

    main

    When extending the UI or dashboard components, follow these import rules to prevent the entire rule engine from being pulled into the browser bundle:

    1. Allowed: You may import scoreToVerdict from @core. It is a pure leaf function with no heavy dependencies.
    2. Forbidden: Do not import rulePriority. This function depends on rules/loader.ts which statically imports all 332 rule modules. Importing it in the UI will cause the browser to download the entire engine.