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

tillefeuille

v0.1.4

Published

Merge multiple MVT sources into a multi-layer vector tile using Web-standard APIs.

Readme

tillefeuille

CI npm version

Merge multiple Mapbox Vector Tile (MVT) sources into a single multi-layer vector tile.

tillefeuille is a small, pure TypeScript library for composing vector tiles at request time. Give it one tile coordinate and a set of source definitions, and it fetches each source tile and returns one merged MVT payload. Layer names are preserved by default and can be customized from the source key.

flowchart LR
  A[z/x/y.pbf] --> T((tillefeuille))
  B[z/x/y.pbf] --> T
  C[PMTiles] --> T
  T --> O[1 MVT]

Its primary purpose is to run behind a server-side tile endpoint, such as a Cloudflare Worker. The endpoint can fetch from multiple tile sources and return one MVT to the client, so clients use a single tile source while the composition stays on the server. tillefeuille is designed to be embedded in that service; it does not ship an HTTP server.

Features

  • Merge multiple MVT sources into one MVT response.
  • Optionally derive output layer names from each source key and original layer name.
  • Exclude original layer names from individual sources before merging.
  • Restrict individual sources to an inclusive tile zoom range.
  • Fetch tiles from HTTP(S) URL templates using {z}, {x}, and {y} tokens.
  • Read PMTiles v3 archives over HTTP range requests.
  • Accept gzip-compressed source tiles.
  • Use Web-standard APIs such as fetch, AbortSignal, and DecompressionStream.

Installation

pnpm add tillefeuille

Usage

import { mergeVectorTiles } from "tillefeuille";

const tile = await mergeVectorTiles({
  z: 14,
  x: 14553,
  y: 6451,
  sources: {
    basemap: {
      url: "https://example.com/basemap/{z}/{x}/{y}.mvt",
      minzoom: 8,
      maxzoom: 16,
      layers: {
        include: ["transportation", "water"],
        exclude: ["building"]
      }
    },
    roads: { url: "https://example.com/roads/{z}/{x}/{y}.mvt" },
    poi: { url: "https://example.com/poi/{z}/{x}/{y}.mvt" },
    admin: { url: "pmtiles://https://example.com/admin.pmtiles" }
  }
});

return new Response(tile, {
  headers: {
    "content-type": "application/vnd.mapbox-vector-tile"
  }
});

The returned Uint8Array is an uncompressed MVT. Apply response compression in the calling service when appropriate.

API

mergeVectorTiles(options)

import { mergeVectorTiles } from "tillefeuille";

const tile: Uint8Array = await mergeVectorTiles(options);

Options:

| Option | Type | Description | | --- | --- | --- | | z | number | Tile zoom level. | | x | number | Tile column. | | y | number | Tile row. | | sources | Record<string, VectorTileSource> | Source ids mapped to { url, minzoom?, maxzoom?, layers?: { include?, exclude? } } definitions. | | fetch | typeof fetch | Optional custom fetch implementation. Useful for tests and runtimes with wrapped fetch behavior. | | signal | AbortSignal | Optional abort signal passed to source requests. | | skipMissing | boolean | Whether to ignore missing source tiles. Defaults to true. | | getLayerName | (key: string, layerName: string) => string | Optional function that returns an output layer name from the source key and original name. | | logger | { warn(warning): void } | Optional logger notified when a same-named layer is skipped because its MVT version or extent cannot be merged. |

Missing source tiles are HTTP 404, HTTP 204, or absent PMTiles entries. When skipMissing is true, they are omitted from the merged tile. When skipMissing is false, a missing source tile throws an error.

Source URLs

Each source has a url. HTTP URLs must start with http:// or https:// and include all three tile coordinate tokens:

{
  roads: { url: "https://tiles.example.com/roads/{z}/{x}/{y}.mvt" }
}

PMTiles sources use the pmtiles:// prefix followed by the archive URL. layers.include retains selected original layer names, and layers.exclude removes them, before they are renamed and merged. These filters apply to both HTTP and PMTiles sources. If both specify a name, layers.exclude wins:

{
  admin: {
    url: "pmtiles://https://tiles.example.com/admin.pmtiles",
    layers: {
      include: ["boundary", "building", "poi"],
      exclude: ["building", "poi"]
    }
  }
}

minzoom and maxzoom limit a source to that inclusive zoom range. A source outside its range is not fetched.

For PMTiles sources, the archive server must support HTTP range requests.

Layer Naming

Layer names are preserved as they appear in the source tile by default. Set getLayerName to customize them. For example, source-prefixed names can be created with:

const tile = await mergeVectorTiles({
  // ...
  getLayerName: (key, layerName) => `${key}:${layerName}`
});

For example, if the roads source contains a layer named transportation, the merged tile contains that layer as:

roads:transportation

Layers with the same output name are merged when they use the same MVT version and coordinate extent; their features and attribute dictionaries are combined. When those values differ, the first layer in sources insertion order is kept. Set logger to receive an incompatible-layer warning with the skipped and retained source keys, their MVT versions and extents, and the mismatched fields:

const tile = await mergeVectorTiles({
  // ...
  logger: console
});

Runtime Requirements

tillefeuille targets modern JavaScript runtimes with Web-standard APIs:

  • fetch
  • Response
  • Headers
  • AbortSignal
  • DecompressionStream for gzip source tiles

It is intended for runtimes such as modern Node.js, Cloudflare Workers, and other Fetch-compatible environments.

Local Demo

pnpm run demo

The demo builds the library and starts a Vite app that uses MapLibre GL JS. It registers a custom protocol that calls mergeVectorTiles, inspects the merged tile, and renders layers by geometry type. It exists for debugging and explanation only; production deployments should compose tiles in a server-side endpoint such as a Cloudflare Worker.

The same demo is deployed to GitHub Pages when main is pushed:

https://kanahiro.github.io/tillefeuille/demo/

Development

pnpm install
pnpm run build
pnpm test

Before publishing, pnpm pack and pnpm publish run the prepack script, which builds the package and runs the test suite.

Releases

Publishing is performed by GitHub Actions when a GitHub release is published. Create an annotated tag named v<package-version> (for example, v0.1.1), then create and publish the corresponding GitHub release.

The workflow verifies that the tag matches package.json, runs the prepack checks, and publishes with npm provenance. Configure npm Trusted Publishing once for the Kanahiro/tillefeuille repository and the publish.yml workflow, allowing npm publish and setting its environment to npm-publish. The workflow uses an OIDC identity; do not add an npm access token as a GitHub Actions secret.

For an approval gate, create the npm-publish environment in the repository settings and require a reviewer before deployment. The workflow targets that environment before it can publish.

For an approval gate, create the npm-publish environment in the repository settings and require a reviewer before deployment. The workflow targets that environment before it can publish.

License

MIT