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

@dasl/tiles

v1.0.0

Published

All-purpose web tiles library

Readme

Web Tiles

Web Tiles are a DASL technology. They basically make content addressed resource work well in a web context, and make web content available safely and in privacy-friendly ways in non-web contexts such as social, commercial, or agentic protocols.

This library is a toolbox for tiles.

Tile Loader

In order to load a tile into a web page you need to use the tile loader with a specific loading configuration. It typically looks like this:

import { TileMothership } from '@dasl/tiles/loader';
import { ATTileLoader } from '@dasl/tiles/loader/at';

// AT Tiles!
const at = new ATTileLoader();
const tl = new TileMothership({ loadDomain: 'load.webtiles.bast' });
tl.init();
tl.addLoader(at);

const parent = document.createElement('div');
parent.style.display = 'inline-block';
parent.style.verticalAlign = 'top';
parent.style.padding = '50px';
parent.style.width = '570px';
document.body.append(parent);

const tile = await tl.loadTile(`at://did:plc:izttpdp3l6vss5crelt5kcux/ing.dasl.masl/3mcjwwoqjqs2v`); // Minesweeper
console.warn(`Tile`, tile);
parent.append(await tile.renderCard());

The tile-loading architecture has three levels that all communicate together:

  • At the top, the MOTHERSHIP. This has access to things in the real world like fetching from the internet or reading from the file system. It's the interface to tile loading, it gets configured in ways that are appropriate for its context. This is the entry point: you give it a URL and it'll instantiate that tile. To the extent possible, this should contain all the intelligence and all the configurability so that the other components can be deployed in entirely generic ways.
  • The mothership instantiates tiles by creating insulated contexts (a protected iframe, an incognito window…) and loading a SHUTTLE in it. The role of the shuttle is to set up a service worker and an iframe to load the root of the tile into. It only exists because you need something to carry a service worker in. The only other thing that it does is (drumroll) shuttle messages back and forth between the worker and the mothership.
  • The WORKER is dispatched on a shuttle to handle resource loading for a tile. Apart from allow-listing some paths for itself and the shuttle, it passes all requests up, which the shuttle then hands over to the mothership.

First, you create a mothership using new TileMothership(). That mothership is then initialised and configured with one or more loaders that support getting tiles from AT Proto (ATTileLoader), CAR Tiles (CARTileLoader), WebXDC (WebXDCTileLoader), or memory (MemoryTileLoader). If you add more than one loader, the mothership will try them each in turn until one returns a tile (most match on URL scheme).

Once the mothership is configured, you call loadTile() on it with a tile URL. And after the tile has been loaded, you can get a DOM element from either tile.renderCard() (for the card overview) or tile.renderContent() that will render the actual tile.

Rendering a card only uses tile metadata and so is safe if you wish to render hundreds or even thoughts. Rendering content is heavier, and may lead to resource contention. When content is loaded, an intermediary iframe is created by the mothership, known as the shuttle. The shuttle then sets up a service worker and a nested iframe into which the tile will render. Due to browser limitations, the shuttle needs to be loaded from a website (see the Tiles Loading Server section below), but that site doesn't get to see or know anything about the tiles.

TileMothership

  • constructor: accepts { loadDomain: 'load.webtiles.example' } which is the domain from which to hit the tile loading server.
  • init(): must be called once, wires the mothership to make sure that it is ready to operate
  • addLoader(someTileLoader): adds a specific class that knows how to load a specific kind of tile. You need at least one.
  • await loadTile(url): returns either a Tile object or false if no loader matched.

Tile

This is the tile object produced when a tile is loaded.

  • renderCard(): renders the card for the tile, returning a DOM element that you can put wherever.
  • renderContent(height?): renders the actual tile, returning a DOM element for you to insert. You can override the tile's height by passing your own.

atile tool

The atile tool is used to publish tiles on AT proto. Most operations revolve around a tile source contained in a directory, with a manifest, that you want to publish to AT.

Usage: atile [options] [command]

Manipulating tiles on the AT protocol

Options:
  -V, --version                  output the version number
  -h, --help                     display help for command

Commands:
  login <handle> <appPassword>   log a handle into AT so that you can post
  logout <handle>                log a specific handle out
  default-user <handle>          set the default handle to use when unspecified
  list-users                     list all logged in handles you have
  publish [options] <dir>        publish a tile to the Atmosphere
  delete [options] <dirOrATURL>  delete a tile from the Atmosphere
  help [command]                 display help for command

In order to publish, you must be logged in. For that, you use the atile login command, with an app password. Passwords are stored using keytar, which should map to a safe option on your platform. Every time that you log in with a different handle, it gets saved separately (and the latest one becomes the default handle). You can then use a specific handle for other commands with the --user option.

You can then use atile logout to log a specific handle out, atile list-users to list all the logged in handles, and atile default user to change the default one.

To publish, use atile publish and give it the path to a directory that contains a manifest.json, an index.html, and all the other files in the tile. It will automatically populate the resources entry of the manifest with the correct CID and guess the media type. If there is already an entry for that path in resources, it will keep it and just update the CID. This makes it possible to set your own HTTP headers there. You can change the handle you use with --user and ask that atile saves the AT URL of the published tile with -s. In the latter case, it will automatically reuse that URL whenever it's given the same path to publish from.

To delete a tile, use atile delete. It acccepts either the AT URL of the tile, or if you've saved the published URL with -s it will find it given the directory.

Tiles Loading Server

Due to limitations in browser technology, supporting tiles in browsers requires a very minimalistic server set up so as to isolate tiles in their own origin, while maintaining the ability to run a service worker (otherwise it's either one or the other).

The tiles loader is expected to load from https://load.server/.well-known/web-tiles/. The loading server handles that and redirects to https://random-subdomain.server/.well-known/web-tiles/ with the right headers. That's it!

You can use it either as a library or as a CLI tool.

As a library, there's a function that takes a domain and returns an Express router configured correctly. Note that if you provide example.site for the base host, it will set itself up to listen on load.example.site and redirect to twenty-random-letters.example.site.

import express from 'express';
import { createTileLoadingRouter } from '@dasl/tiles/loading-server';

const app = express();
app.set('trust proxy', 'loopback'); // need this
app.use(createTileLoadingRouter('example.site'));

Or just install the global tool, or run it with npx:

tiles-loading-server example.site 8080

Writing

import TileWrite from '@dasl/tiles/writer';

const tw = new TileWriter({
  name: `My Cat`,
  description: `This basic tile is a picture of my cat.`,
});
tw.addResource('/', { 'content-type': 'text/html' }, { path: '/path/to/index.html') });
tw.addResource('/img/kitsu.jpg', { 'content-type': 'image/jpeg' }, { path: '/path/to/rick.jpg') });
await tw.write('/path/to/output.tile');

Reading from a CAR

The CAR reader indexes the whole tile and resolves paths to return the headers and the means of creating a stream that reads just that segment of the CAR. Note that the path resolution will correctly ignore query strings, hashes, etc.

import CarTileReader from '@dasl/tiles/car-reader';

const ctr = new CarTileReader('/path/to/a/car-based.tile');
await ctr.open();
const nf = ctr.resolvePath('/not/exists');
// nf.ok = false, nf.status = 404
const root = ctr.resolvePath('/');
// root.ok = true, root.status = 200
sendHeaders(root.headers); // { 'content-type': 'text/html' }
root.createReadStream().pipe(res); // sends the content