npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@soeditor/html-tools

v1.6.0

Published

Extensible HTML diagnostics and formatting for SoEditor.

Readme

@soeditor/html-tools

UI-independent HTML diagnostic providers, Problems aggregation, and explicit guarded Prettier formatting for SoEditor. Formatting is command-driven and refuses parser-invalid source; unknown elements and CMS markers are not diagnostic errors merely because they are unfamiliar.

Accessibility and SEO diagnostics

AccessibilityDiagnosticsPlugin and SeoDiagnosticsPlugin are independently selectable plugins. Each automatically loads DiagnosticsPlugin, registers a source-only provider, and contributes to the existing document.validate command and DiagnosticsService result. They parse canonical HTML into SoEditor-owned trees; they never render, execute, sanitize, fetch, or mutate the source.

import {
    AccessibilityDiagnosticsPlugin,
    SeoDiagnosticsPlugin,
} from '@soeditor/html-tools';

const editor = await Editor.create({
    data: '<!doctype html><html><head></head><body></body></html>',
    plugins: [AccessibilityDiagnosticsPlugin, SeoDiagnosticsPlugin],
    config: {
        htmlTools: {
            accessibility: {
                rules: {
                    'a11y.heading-order': false,
                    'a11y.iframe-title': 'error',
                },
            },
            seo: {
                rules: {
                    'seo.h1': 'warning',
                },
            },
        },
    },
});

const problems = await editor.execute('document.validate');

A rule setting is either false or error | warning | info | hint. Unknown rule codes and malformed values fail editor initialization with an actionable configuration error.

| Provider | Rule | Default | Scope | | -------------------- | ----------------------- | ------- | ------------------------------------------------------------ | | html.accessibility | a11y.form-label | warning | Visible HTML form controls without a native/ARIA label path. | | html.accessibility | a11y.heading-order | warning | Heading-level jumps in complete documents only. | | html.accessibility | a11y.iframe-title | warning | Iframes with a missing or empty title. | | html.accessibility | a11y.interactive-name | warning | Buttons/button-like inputs without a detectable name. | | html.seo | seo.document-title | warning | Missing, empty, or duplicate complete-document title. | | html.seo | seo.h1 | info | Missing or multiple complete-document h1 elements. | | html.seo | seo.meta-description | info | Missing or empty complete-document meta description. |

The base html.structure provider continues to report duplicate IDs, missing image alt, and missing complete-document root lang. Template descendants are treated as inert template data by the Phase 17 quality providers.

These rules are deliberately bounded source-analysis aids. They cannot inspect computed styles, contrast, focus order, screen-reader behavior, rendered scripts, application context, remote pages, search indexing, or ranking. Their results are not a claim of WCAG, legal, Lighthouse, or search-engine compliance.

Validation workflow

Validation remains manual by default through document.validate or DiagnosticsService.validate(). Applications may opt into per-editor debounced validation:

const editor = await Editor.create({
    plugins: [DiagnosticsPlugin],
    config: {
        htmlTools: {
            diagnostics: {
                validation: { mode: 'debounced', delay: 300 },
            },
        },
    },
});

Each run snapshots the registered providers, runs them independently, and retains registration order in the final Problems list. A rejected or malformed provider is recorded in service.failures; it does not prevent other providers from completing. Overlapping, unregistered, changed-document, and destroyed runs cannot publish stale results.

service.snapshot exposes immutable idle | validating | ready state, Problems, provider failures, and counts. service.subscribe() observes published workflow transitions. service.getProblems(filter) and service.getCounts(filter) accept optional provider and severity filters. The original service.problems, register(), and validate() entry points remain available.

Formatting worker and CSP

Formatting loads only when requested. The built package ships dist/assets/formatting-worker-*.js; its URL is relative to the formatting module, so nested deployments do not depend on a root /assets directory. Keep the complete dist directory when publishing or copying the package. Bundlers must retain the asset referenced by new URL(..., import.meta.url).

The worker uses a normal HTTP(S) URL, not blob: or data:. Same-origin deployments work with worker-src 'self', or with a script-src 'self' fallback when worker-src and child-src are absent. A separately hosted package requires its resource origin to be allowed by the host policy and worker origin rules. Worker startup failures retain the existing main-thread formatter fallback; this does not bypass CSP or suppress browser policy diagnostics. Size limits, parser protection, stale-result checks and undo behavior remain unchanged.