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

@dxj.jp/md-embed

v0.2.0

Published

Streaming <md-embed> shortcode renderer for Cloudflare Workers: embeds live, R2-backed Markdown into HTML via HTMLRewriter.

Readme

md-embed (@dxj.jp/md-embed)

Live demo: md-embed.dxj.jp — every block of prose on that page is Markdown in R2, rendered at request time by this package. Tested on real workerd via @cloudflare/vitest-pool-workers. The unscoped md-embed npm name is held by DXJ and points here.

Embed live Markdown into any HTML page at the edge.

md-embed is a streaming shortcode renderer for Cloudflare Workers. Drop a custom element into your HTML:

<md-embed src="docs/intro.md"></md-embed>

and the Worker resolves it at request time with HTMLRewriter — fetching the Markdown from R2, rendering it to clean HTML (## → <h2>, GFM tables, and more), and streaming it into place. Update the Markdown in R2 and every embedding page reflects it instantly, with no redeploy.

Why

  • Live content: Markdown lives in R2 and is edited by humans and AI agents. Provenance attributes (data-md-etag) make cache invalidation trivial.
  • Streaming: HTMLRewriter transforms the page as it streams — no full-page buffering.
  • One tag: the <md-embed> custom element is the whole integration surface.

Quickstart (Hono)

npm i @dxj.jp/md-embed
import { Hono } from "hono";
import { mdEmbed, r2Resolver } from "@dxj.jp/md-embed";

type Env = { CONTENT: R2Bucket; ASSETS: Fetcher };

const app = new Hono<{ Bindings: Env }>();

app.use(mdEmbed((env) => r2Resolver((env as Env).CONTENT)));
app.get("*", (c) => c.env.ASSETS.fetch(c.req.raw));

export default app;

Any text/html response passing through the middleware gets its <md-embed> elements resolved. Non-HTML responses are untouched.

API

render(markdown, options?)            // Markdown → HTML string (wrapped in <article class="prose">)
r2Resolver(bucket, { prefix? })       // MarkdownResolver backed by an R2 bucket
urlResolver({ allow, ... })           // opt-in: fetch Markdown from allowlisted external URLs
combineResolvers(...resolvers)        // try resolvers in order; first non-null wins
transform(response, resolver, opts?)  // stream-resolve <md-embed> in a Response
mdEmbed(env => resolver, opts?)       // Hono-compatible middleware (no hono dependency)

Embedding from other sites (opt-in)

By default src is resolved locally only (an R2 key) — external URLs are never fetched. To allow <md-embed src="https://…">, opt in with urlResolver, which is default-deny: it fetches nothing until you supply an allow rule, and only ever touches absolute URLs, so it composes with r2Resolver:

app.use(mdEmbed<Env>((env) => combineResolvers(
  urlResolver({ allow: "https://raw.githubusercontent.com/DXJ-LLC/" }),
  r2Resolver(env.CONTENT),
)));

Now <md-embed src="https://raw.githubusercontent.com/DXJ-LLC/md-embed/main/README.md"> renders, while a URL outside the allowlist falls through to R2 and ends up not-found.

allow accepts a string (matched by origin + path prefix, so "https://host" and "https://host/dir/" are both host-boundary-safe — a bare prefix can't be spoofed by https://host.evil.com), a RegExp (tested against href — anchor it), a predicate (url: URL) => boolean, or an array of these. Only https: is fetched by default (protocols to change), and init is forwarded to fetch.

Two independent gates guard resolution: the element-level allowSrc option runs first and shows a visible data-md-error="not-allowed"; the fetch-level urlResolver allow silently falls through (so a rejected URL ends up not-found via the next resolver, revealing nothing about the allowlist).

Security. External Markdown is untrusted, unsanitized third-party input — an allowlisted host that gets compromised can inject HTML into your page. urlResolver is default-deny and does not follow redirects by default (redirect: "manual", so a 3xx resolves to nothing) so a host can't 302 off-allowlist; if you opt back in, the landing URL is re-checked. Keep allow tight and prefer hosts you control. On Workers the edge has no private network, so this is an XSS/abuse surface rather than a classic internal-SSRF one — but treat it as a real trust boundary regardless.

Options

| Option | Default | Meaning | |---|---|---| | wrapperClass | "prose" | Class on the <article> wrapper; null disables wrapping | | markedOptions | GFM on | Forwarded to marked (async is always forced to false) | | tagName | "md-embed" | Element to resolve (should contain a hyphen — custom-element convention) | | allowSrc | allow all | Allowlist gate for src values; rejected embeds get data-md-error="not-allowed" |

Element contract

The <md-embed> element stays in the DOM after rendering — its inner content is replaced and attributes are stamped:

| Attribute | Meaning | |---|---| | rendered | Present when server-side rendering succeeded (a future client-side Web Component skips hydration when set) | | data-md-src | The resolved source path | | data-md-etag | Version identifier from the resolver (R2 httpEtag) | | data-md-error | missing-src / not-allowed / not-found / resolver-error / render-error when rendering failed (element and its fallback content left as-is) |

Elements that already carry rendered are skipped, so layered rendering (edge cache → origin) is safe. Failures are contained per element — one broken embed never breaks the page.

Closing tag required. HTML has no self-closing custom elements: <md-embed src="…" /> is parsed as an open tag and will swallow everything up to its parent's close. Always write <md-embed src="…"></md-embed>.

Headers. Transformed responses have upstream ETag, Last-Modified, and Content-Length stripped — those validators describe the original asset, not the composed page, and would let browsers revalidate into stale embeds.

Conventions

  • Raw Markdown should be reachable at a .md alias URL (serve text/markdown for the same path); content negotiation via Accept headers is treated as sugar on top, never the canonical route.

Security

Rendered Markdown is not sanitized — embed only content you control (your own R2 bucket, your repo). Do not point a resolver at untrusted third-party input.

The src attribute is a server-side include surface: the embedding HTML must be fully trusted too. Do not run the middleware over pages containing user-generated markup — anyone who can inject a tag can inline any object your resolver reaches (e.g. unpublished drafts). Use allowSrc to restrict resolvable sources, and note that r2Resolver treats keys as literals (no ../ traversal), but custom HTTP/filesystem resolvers must enforce that themselves.

TypeScript consumers: @cloudflare/workers-types is an optional peer dependency — install it (or use wrangler types) so R2Bucket and friends resolve.

Development

pnpm install
pnpm test    # vitest on real workerd (HTMLRewriter included)
pnpm build   # tsc → dist/

Roadmap

  • 0.1.0 — core (render / r2Resolver / transform / mdEmbed) — shipped, dogfooded on md-embed.dxj.jp
  • Cache API integration (ETag-keyed, stale-while-revalidate)
  • Client-side <md-embed> Web Component sharing this element contract
  • llms.txt generation from embedded sources

License

MIT — made by DXJ