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

@raybase/vaportrail

v0.2.0

Published

Worker-compatible runtime Tailwind scanning and compilation for Cloudflare Workers.

Readme

Vaportrail

Worker-compatible runtime Tailwind scanning and compilation for Cloudflare Workers.

@raybase/vaportrail turns runtime-authored strings or explicit Tailwind candidates into a stable candidate list, and can optionally compile those candidates into CSS from a caller-provided Tailwind v4 CSS entry.

It is intended for Workers apps that render or publish runtime content, including CMS pages, user-authored templates, AI-generated HTML, white-label pages, and template publishing flows.

Features

  • Worker-safe Tailwind candidate extraction through a custom WASM wrapper around Tailwind v4's Rust extractor.
  • Tailwind v4 compile().build(candidates) orchestration from raw strings or explicit candidates.
  • Caller-provided Tailwind CSS entry, with optional stylesheet import resolution.
  • emit: "full", emit: "runtime", and custom layer-aware emit policies.
  • Stable SHA-256 hash and semantic cacheKey values.
  • Optional compiler cache hooks.
  • Cloudflare Cache API, KV, R2, and in-memory cache helpers.
  • Optional reference Worker with /scan, /compile, /css/:hash, and /health endpoints.

Install

npm install @raybase/vaportrail

Quick Start

import { compile } from "@raybase/vaportrail/cloudflare";

export default {
  async fetch(request: Request) {
    const html = await request.text();

    const result = await compile({
      css: "@theme { --color-red-500: oklch(63.7% 0.237 25.331); } @tailwind utilities;",
      sources: [{ kind: "text", content: html, extension: "html" }],
      emit: "runtime",
    });

    return new Response(result.css, {
      headers: {
        "content-type": "text/css; charset=utf-8",
        etag: result.hash,
      },
    });
  },
};

Runtime compilation should be cached. Large CSS entries or large candidate sets can exceed normal Worker CPU budgets.

Entry Points

@raybase/vaportrail/extractor

Scan a single string with the WASM extractor.

import { scan } from "@raybase/vaportrail/extractor";

const candidates = scan('<section class="grid md:grid-cols-2"></section>', {
  extension: "html",
});

@raybase/vaportrail/compiler

Scan multiple sources and optionally compile CSS.

import { compile, scan } from "@raybase/vaportrail/compiler";

const scanned = await scan({
  sources: [{ kind: "text", content: '<div class="text-red-500"></div>' }],
  candidates: ["hover:bg-blue-100"],
});

const result = await compile({
  css: "@theme { --spacing: 0.25rem; } @tailwind utilities;",
  candidates: ["p-4"],
  emit: "full",
});

@raybase/vaportrail/cloudflare

Worker-friendly entrypoint. Re-exports scan and compile, plus Cloudflare cache and storage helpers.

import { compile, composeCaches, kvCache, memoryCache } from "@raybase/vaportrail/cloudflare";

export interface Env {
  CSS_CACHE: KVNamespace;
}

export default {
  async fetch(request: Request, env: Env) {
    const result = await compile({
      css: "@theme { --color-brand: oklch(62% 0.18 240); } @tailwind utilities;",
      sources: [{ kind: "text", content: await request.text() }],
      emit: "runtime",
      cache: composeCaches(memoryCache(), kvCache(env.CSS_CACHE)),
    });

    return new Response(result.css, {
      headers: { "content-type": "text/css; charset=utf-8" },
    });
  },
};

@raybase/vaportrail/worker

Reference Worker implementation. It is provided as a deployment pattern, not as a required integration path.

export { default } from "@raybase/vaportrail/worker";

The reference Worker exposes:

  • GET /health
  • POST /scan
  • POST /compile
  • GET /css/:hash

POST /compile requires Authorization: Bearer <VAPORTRAIL_SECRET>.

API

type Source =
  | { kind: "text"; content: string; extension?: string }
  | { kind: "candidates"; candidates: string[] };

type EmitPolicy =
  | "full"
  | "runtime"
  | {
      theme?: "all" | false;
      base?: boolean;
      components?: boolean;
      utilities?: boolean;
      properties?: boolean;
    };

type CompileInput = {
  sources?: Source[];
  candidates?: string[];
  css: string;
  base?: string;
  loadStylesheet?: (
    id: string,
    base: string,
  ) => Promise<string | null> | string | null;
  emit?: EmitPolicy;
  cache?: {
    get(key: string): Promise<CachedResult | null>;
    put(key: string, value: CachedResult): Promise<void>;
  };
};

compile(input) returns:

{
  css: string;
  candidates: string[];
  hash: string;
  cacheKey: string;
}

hash is derived from the emitted CSS. cacheKey is derived from semantic inputs including Tailwind version, extractor version, CSS/import content, emit policy, and sorted candidates.

CSS Source And Imports

Pass the normal Tailwind CSS source you want Vaportrail to compile from:

await compile({
  css: "@theme { --color-accent: oklch(70% 0.2 160); } @tailwind utilities;",
  candidates: ["text-accent"],
  emit: "runtime",
});

If your CSS uses @import, provide loadStylesheet. Vaportrail adapts the string resolver to Tailwind's { path, base, content } loader shape and includes imported content hashes in cacheKey.

await compile({
  css: '@import "./theme.css" layer(theme); @tailwind utilities;',
  base: "/styles/app.css",
  candidates: ["text-brand"],
  loadStylesheet(id, base) {
    return env.CSS_SOURCES.get(`${base}/${id}`);
  },
});

Emit Policies

emit: "full" returns Tailwind's compiled output unchanged.

emit: "runtime" is intended for runtime-authored template CSS when the host app already serves global CSS. It keeps:

  • full theme variables
  • generated utilities for the scanned candidates
  • top-level @property rules

It drops:

  • base/preflight reset CSS
  • component/global CSS
  • unlayered global rules

Custom policies can toggle theme, base, components, utilities, and properties.

await compile({
  css,
  sources,
  emit: {
    theme: "all",
    base: false,
    components: false,
    utilities: true,
    properties: true,
  },
});

theme: "used" is not implemented.

Cloudflare Helpers

@raybase/vaportrail/cloudflare exports:

  • memoryCache()
  • cacheApiCache(caches.default)
  • kvCache(namespace)
  • r2Cache(bucket)
  • composeCaches(...caches)
  • loadCssFromKV(namespace, key)
  • kvStylesheetLoader(namespace, options?)
  • loadCssFromR2(bucket, key)
  • r2StylesheetLoader(bucket, options?)

The Cache API is regional. KV and R2 are better for reusable content-hashed CSS across regions.

Worker WASM Packaging

The default ESM import embeds WASM bytes for Node and bundler compatibility.

The package export map also provides workerd conditions that import the .wasm module statically. This is required by workerd because runtime WASM code generation is blocked in Cloudflare Workers.

When deploying the reference Worker from this repository, build first and deploy the generated workerd entrypoint:

{
  "main": "dist/workerd/worker/index.js",
  "rules": [
    {
      "type": "CompiledWasm",
      "globs": ["**/*.wasm"],
      "fallthrough": false
    }
  ]
}

Reference Worker Deployment

npm run build
npx wrangler secret put VAPORTRAIL_SECRET
npx wrangler deploy

Smoke test:

curl https://<worker>.workers.dev/health

curl -X POST https://<worker>.workers.dev/scan \
  -H 'content-type: application/json' \
  --data '{"sources":[{"kind":"text","content":"<div class=\"text-red-500\"></div>"}]}'

Authenticated compile:

curl -X POST https://<worker>.workers.dev/compile \
  -H 'content-type: application/json' \
  -H "authorization: Bearer $VAPORTRAIL_SECRET" \
  --data '{
    "css": "@theme { --color-red-500: oklch(63.7% 0.237 25.331); } @tailwind utilities;",
    "sources": [{"kind": "text", "content": "<div class=\"text-red-500\"></div>"}],
    "emit": "full"
  }'

Limitations

  • Dynamic class construction has the same constraints as Tailwind scanning generally: complete class strings must appear somewhere, or callers must pass explicit candidates.
  • Runtime Tailwind compilation can be CPU-heavy for large candidate sets or large CSS entries.
  • Vaportrail does not load arbitrary Tailwind config or plugin modules from public requests.
  • CSS entries that depend on Node/Vite-only plugins or filesystem behavior should be compiled elsewhere or resolved into plain CSS/import strings before reaching Vaportrail.
  • Deployed compile endpoints should be authenticated and rate-limited.

Development

npm install
npm run check:vendor
npm run build
npm test
npm run pack:dry

The Rust extractor vendors Tailwind v4.3.1 and builds to wasm32-unknown-unknown.

License

MIT