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

@tilery/client

v0.1.0

Published

Client library for Tilery.eu tile API with service worker caching

Readme

@tilery/client

Client library for Tilery.eu tile API with service worker caching and automatic token refresh.

Features

  • Token Management - Automatic token refresh before expiry
  • Service Worker Caching - Long-lived tile cache that survives token rotation
  • No CORS Preflight - Token passed as query parameter, not header
  • MapLibre Integration - Get ready-to-use style objects

The SDK implements only the JWT/token-exchange flow — that's the safe path for browsers (15-minute lifetime, origin-locked, plays well with the service-worker cache). The Tilery API also accepts an app-mode API key sent directly as Authorization: Bearer … for trusted backends, but that path skips the SDK entirely (and skips the cache). If you reach for it from a browser, you've left the safe envelope this SDK provides — see the security model before doing so.

Installation

npm install @tilery/client

The package has zero runtime dependencies. MapLibre style JSON (including all layer definitions) is fetched directly from the tile server.

Quick Start

import { TileryClient } from "@tilery/client";
import maplibregl from "maplibre-gl";

// Create client with your token fetcher
const client = new TileryClient({
  getToken: async () => {
    // Your backend exchanges API key for short-lived JWT
    const res = await fetch("/api/tile-token", { credentials: "include" });
    if (!res.ok) throw new Error("Failed to get token");
    return res.json(); // { token: string, exp: number }
  },
});

// Register service worker for tile caching
await client.registerServiceWorker();

// Create map with auto-generated style
const map = new maplibregl.Map({
  container: "map",
  style: await client.getMapStyle({ flavor: "dark" }),
  center: [10.75, 59.91],
  zoom: 12,
});

Service Worker Setup

The service worker code is bundled in the library. Create a file in your public directory:

Option 1: Copy from built library

# The library exports the compiled SW code
node -e "import('@tilery/client').then(m => console.log(m.SERVICE_WORKER_CODE))" > public/tilery-sw.js

Option 2: Build script

// scripts/build-sw.ts
import { SERVICE_WORKER_CODE } from "@tilery/client";
import { writeFileSync } from "fs";
writeFileSync("public/tilery-sw.js", SERVICE_WORKER_CODE);

Then register it:

await client.registerServiceWorker();

The service worker intercepts tile requests and:

  1. Strips the ?token= query parameter to create a normalized cache key
  2. Checks the cache using the normalized key
  3. On cache miss, fetches with the original URL (including token)
  4. Stores the response under the normalized key

This means tiles stay cached even when your token rotates!

API Reference

TileryClient

Constructor Options

interface TileryClientOptions {
  // Required: async function that returns { token, exp }
  getToken: () => Promise<{ token: string; exp: number }>;

  // Optional: tile server URL (default: 'https://api.tilery.eu').
  // Use 'https://api.test.tilery.eu' to point at the staging environment.
  tileServer?: string;

  // Optional: seconds before expiry to refresh (default: 60)
  refreshBuffer?: number;

  // Optional: path to service worker (default: '/tilery-sw.js')
  serviceWorkerPath?: string;
}

Methods

// Get current token (fetches if needed, cached otherwise)
const token = await client.getToken();

// Register service worker
await client.registerServiceWorker();

// Get MapLibre-compatible style (fetched from the tile server, token injected)
const style = await client.getMapStyle({
  flavor: "light", // see GET {tileServer}/map/styles for the full list
});

// Get URL templates
const tileUrl = await client.getTileUrlTemplate();
const glyphsUrl = client.getGlyphsUrl();
const spriteUrl = client.getSpriteUrl("dark");

// Clean up
client.destroy();

Events

client.on("token:refresh", (token) => {
  console.log("Token refreshed, expires at:", token.exp);
});

client.on("token:error", (error) => {
  console.error("Token refresh failed:", error);
});

client.on("sw:registered", (registration) => {
  console.log("Service worker registered");
});

client.on("sw:error", (error) => {
  console.error("Service worker failed:", error);
});

Backend Token Endpoint

Your backend should exchange an API key for a short-lived JWT. Example with the Tilery API:

// /api/tile-token.ts (your backend)
export async function GET(request: Request) {
  const res = await fetch("https://tilery.eu/api/tokens/exchange", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.TILERY_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ ttl: 900 }), // 15 minutes
  });

  return res.json(); // { token: string, exp: number }
}

Manual Usage (without MapLibre helper)

const client = new TileryClient({
  getToken: async () => fetch("/api/tile-token").then((r) => r.json()),
});

// Get token and build URLs manually
const token = await client.getToken();
const tileUrl = `https://api.tilery.eu/tiles/{z}/{x}/{y}?token=${token.token}`;

// Use with any mapping library
const map = new maplibregl.Map({
  container: "map",
  style: {
    version: 8,
    sources: {
      tiles: {
        type: "vector",
        tiles: [tileUrl],
        maxzoom: 14,
      },
    },
    layers: [
      /* your layers */
    ],
  },
});

Service Worker Messages

You can communicate with the service worker for cache management:

// Clear all cached tiles
navigator.serviceWorker.controller?.postMessage({ type: "CLEAR_CACHE" });

// Get cache size
const channel = new MessageChannel();
channel.port1.onmessage = (e) => console.log("Cache size:", e.data.size);
navigator.serviceWorker.controller?.postMessage({ type: "GET_CACHE_SIZE" }, [channel.port2]);

License

MIT