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

inframaps

v0.0.1

Published

A self-contained slice of the world as vector tiles plus terrain

Readme

inframaps

The whole world as vector tiles plus terrain, from zoom 0 to 7, in one npm package that runs straight from a CDN: no server, no API key.

  • Try it: the viewer and the examples, one capability each, run straight from the package on unpkg.
  • Embed it: load it from jsDelivr, as below.

Use each CDN for its own job. jsDelivr serves HTML as plain text, so the pages only open on unpkg or your own server. unpkg answers a missing file without a CORS header, so a page on another site cannot tell a tile the package leaves out from a network failure, and those tiles fail instead of being drawn from their parent. Every path in the package is relative, so any copy of this folder on your own server does both jobs.

Use it from a CDN

<link href="https://cdn.jsdelivr.net/npm/[email protected]/renderer/[email protected]/maplibre.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/renderer/[email protected]/maplibre.js"></script>
<div id="map" style="height: 100vh"></div>
<script type="module">
	const root = "https://cdn.jsdelivr.net/npm/[email protected]/";
	const { registerInframapsProtocol } = await import(root + "protocol.mjs");
	registerInframapsProtocol(maplibregl);

	const style = await fetch(root + "themes/avenue.json").then((r) => r.json());
	style.glyphs = root + style.glyphs.slice(2);
	style.sprite = root + style.sprite.slice(2);
	style.sources.__bmk_bm_src = {
		type: "vector",
		tiles: ["inframaps://vector/{z}/{x}/{y}"],
		maxzoom: 7,
		attribution: "&copy; OpenStreetMap"
	};
	style.sources.__bmk_tr_src = {
		type: "raster-dem",
		tiles: [root + "terrain/{z}/{x}/{y}.webp"],
		tileSize: 512,
		maxzoom: 7,
		encoding: "terrarium"
	};
	// The themes use one MapLibre 6 paint key that MapLibre 5 rejects.
	for (const layer of style.layers) {
		if (layer.paint?.["line-layer-opacity"] !== undefined) {
			layer.paint["line-opacity"] = layer.paint["line-layer-opacity"];
			delete layer.paint["line-layer-opacity"];
		}
	}

	new maplibregl.Map({ container: "map", style, center: [10, 45], zoom: 3 });
</script>

Pin the version in every URL. Without one, jsDelivr resolves each file on its own, and just after a release a page can mix files from two versions.

The protocol finds its tiles next to itself, so the same code works from a CDN, from node_modules, or from a copy of this folder on your own server: change root. Every theme in themes/index.json loads the same way; the two source ids are listed there too.

The protocol

registerInframapsProtocol(maplibregl, options); // returns a function that unregisters

| Option | Default | Meaning | | --------- | ------------------------------ | ---------------------------------------- | | base | vector/ beside protocol.mjs | where the tile tree lives | | levels | 4 | ancestors to try when a tile is missing | | scheme | "inframaps" | protocol name | | onEvent | none | called once per tile, see below |

Some deep tiles over open sea and empty land are left out to keep the package small. When one is missing, the protocol cuts its nearest ancestor to the right quarter and scales it up, so the map shows less detail rather than a hole.

onEvent receives one of:

{ type: "decoded",   z, x, y, stored, plain, ms }
{ type: "stretched", z, x, y, from }
{ type: "missing",   z, x, y }
{ type: "failed",    z, x, y, error }

What is inside

| Path | Contents | | ------------- | ------------------------------------------------- | | vector/ | gzip Mapbox Vector Tiles, z0 to z7 | | terrain/ | Terrarium-encoded WebP elevation tiles, z0 to z7 | | themes/ | MapLibre styles; themes/index.json lists them | | fonts/ | glyph ranges for six scripts | | sprites/ | sprite sheets | | renderer/ | a patched MapLibre GL JS 5.24.0 | | examples/ | showcase pages; examples/index.html lists them | | typefaces/ | the viewer panel's Noto Sans, woff2, OFL licence | | processors/ | the Node programs that built the tile trees |

Rebuilding the tiles

The processors need Node 24 and sharp:

node processors/vector.mjs --input ./vector.pmtiles --output ./vector
node processors/terrain.mjs --input ./terrain.pmtiles --output ./terrain

Both require --input and --output and have no defaults.