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

@tavora/sdk

v0.3.4

Published

Typed client for Tavora modules: talks to the Tavora Desktop host over its bridge.

Readme

@tavora/sdk

Typed client for Tavora modules. A module is a web page that runs in a sandboxed panel inside Tavora Desktop; this package is how it talks to the host.

Status: bridge protocol v1, still before 1.0: a minor version can change things, and the changelog says what and which Tavora a version needs.

Start a new module with npm create @tavora/module (or pnpm create @tavora/module), or add the SDK to your own project with npm install @tavora/sdk.

import { connect, TavoraError } from "@tavora/sdk";

const tavora = await connect();
const workspace = await tavora.workspace.getCurrent(); // { id: "poe2", displayName: "Path of Exile 2", kind: "game" }

// Small settings, private to this module in this workspace.
const count = (await tavora.storage.get<number>("count")) ?? 0;
await tavora.storage.set("count", count + 1);

// Documents are shared by all panels of this module in this workspace.
const plan = await tavora.documents.get<{ steps: string[] }>("plan");
try {
  await tavora.documents.put({ id: "plan", title: "Plan", data: { steps: ["Act 1"] }, ifRevision: plan?.revision ?? 0 });
} catch (error) {
  if (error instanceof TavoraError && error.code === "CONFLICT") {
    // Another panel saved first: reload instead of overwriting.
  }
}
tavora.documents.onChange(({ id }) => console.log(`${id} changed in another panel`));

// State for this one panel (open tab, scroll position). Removed when the panel closes.
await tavora.panel.setState({ tab: "gear" });

tavora.workspace.onResize(({ width, height }) => {});
tavora.ui.onThemeChange((theme) => {});
tavora.lifecycle.onSuspend(() => {}); // hidden: pause timers and animations
tavora.lifecycle.onResume(() => {});

connect() can be called any number of times and always returns the same connection. It rejects with a TavoraError when the page runs outside Tavora (HOST_UNAVAILABLE) or the host speaks another protocol version (INCOMPATIBLE).

Every failed call rejects with a TavoraError. Check error.code, not the message. The codes, limits and the wire format are in the bridge protocol reference; the manifest every module needs is in the manifest reference.

Values you store must be JSON. Type parameters such as get<number>() are not checked at runtime.

More from the host

| API | What it does | | --- | --- | | tavora.workspace.getCurrent() | The workspace the panel is in: { id, displayName, kind }, with kind "game" (a game workspace; the id is the game's) or "personal". tavora.game.getCurrent() still answers but is deprecated. | | tavora.storage.app | The same small settings, shared by every workspace: for choices the user expects to stay the same from workspace to workspace. | | tavora.ui.getLanguage(), onLanguageChange | The language of Tavora's own texts ("en", "de"). Show your module in it, or in English when you have no translation. | | tavora.blobs | Pictures too big for a document: up to 50 MB each and 500 MB per module and workspace. put a file, show it with url(id). See Files. | | tavora.clipboard.writeImage(png) | Puts a PNG on the system clipboard. Call it from the click or key press that asked for it. See Clipboard. | | tavora.pictures.save(png, name) | Saves a PNG in the user's Pictures\Tavora folder and resolves with the file name. Call it from the click or key press that asked for it. See Saving pictures. | | tavora.links.open(url) | Opens a web address in the workspace's Browser panel, from a click or key press. | | tavora.network.fetch(url, init) | A web request to a host listed in permissions.network, once the user granted it. See Network. | | tavora.network.readPage(url) | Capture a public page in an isolated temporary browser, using the same grants. No user sign-ins or module scripts; Tavora 0.19.3+. | | tavora.connectors.aiUsage | The usage of the user's Claude and Codex accounts, with the ai-usage connector. | | tavora.connectors.systemInfo | The PC's hardware and how busy it is, with the system-info connector: info(), watch(options), onSnapshot, onDrivesChange, and for the connection connectionLog(), clearConnectionLog(), connectionSettings(), setConnectionSettings(settings), onConnectionChange. | | tavora.connectors.media | What plays on the PC, with the media connector: watch(), unwatch(), onChange, control(session, action). | | tavora.connectors.discord | The Discord app on this PC through Tavora's Discord sign-in, with the discord connector: voice channels, mute and deafen, voice events. |

What needs the user's permission (permissions.network and permissions.connectors in the manifest) works only once the user has granted it: they are asked when they add the module and can take it back in Settings > Connections. Until then the call fails with PERMISSION_DENIED; tavora.host.capabilities lists what was granted when the module connected. See Capabilities and Connectors.

Examples

The same small module built three ways: vanilla TypeScript, React and Vue.

Building a module

  • Bundle everything. A module's page has no network access, so no CDN scripts, web fonts or remote images. To reach a web API, list its host in permissions.network and use tavora.network.fetch once the user has granted it.
  • Use relative asset paths (Vite: base: "./"). The page is served from tavora-module://<module-id>/.
  • Point the manifest's entry at the built HTML file.

Details over neighbouring panels

Requires Tavora 0.18.18. tavora.ui.tooltip.show accepts TooltipRequest, a bounded plain-data description, never HTML or CSS. Use a new id for each hover session, move while the pointer moves, and hide on pointer leave. A click may call show with pinned: true; hide then leaves it open. Call close before changing the source data, and use onClose to clear your local pin state. Escape and the host close button dismiss the current tooltip from anywhere in Tavora.

await tavora.ui.tooltip.show({
  id: 'item:42', at: { x: event.clientX, y: event.clientY },
  content: { title: 'Example spear', tone: 'rare',
    sections: [{ rows: [{ text: 'Adds 10 to 22 Physical Damage', badges: ['[P5]'] }] }] },
  pinned: false,
});