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

@mythryl/app-sdk

v0.1.11

Published

The browser SDK for Mythryl mini apps.

Readme

@mythryl/app-sdk

The browser SDK for Mythryl mini apps. It connects an app to the Mythryl window that embeds it.

npm install @mythryl/app-sdk
import sdk from "@mythryl/app-sdk";

await sdk.ready();

const context = await sdk.getContext();
console.log(context.user.handle);

The default export is created on first use, so importing it has no side effects and is safe in code that also renders on the server. Call it from effects and event handlers, which run in the browser.

API

| Call | Result | | --- | --- | | sdk.ready() | Signals that the app has loaded. The host removes its loading screen. | | sdk.close() | Closes the app. | | sdk.getCapabilities() | The capabilities the host supports. Check before calling optional features. | | sdk.getContext() | The signed-in user, how the app was launched, and the client environment. | | sdk.auth.signin({ force? }) | { token }: a JWT that authenticates the user to the app's own server. | | sdk.openProfile(actor) | Opens a profile (handle or DID) in the host and minimises the app. | | sdk.openPost(uri) | Opens a post, by its at:// URI, and minimises the app. | | sdk.openTopic(slug) | Opens a topic, by name without the slash, and minimises the app. | | sdk.openUrl(url) | Opens an https address in the browser once the user confirms. Rejects with cancelled if they decline. | | sdk.openStorePage(domain?) | Opens an app's store page in the host and minimises this app. Without a domain, it is this app's own page. | | sdk.openApp({ domain, url? }) | Opens another mini app and closes this one. url is where to start, on that app's domain. | | sdk.compose({ text?, embedUrl? }) | Opens the composer with a draft. { uri } if the user posted, {} if they did not. | | sdk.notifications.request() | Asks the user to let the app notify them, adding it to their library too. On yes, Mythryl sends your server a notification token for the user (the notifications.enabled webhook). Resolves at once if they are already on; rejects with cancelled if they decline. | | sdk.storage.get(key) | The JSON value saved under a key for this user, or undefined if there is none. | | sdk.storage.set(key, value) | Saves a JSON value under a key, replacing what was there. | | sdk.storage.delete(key) | Removes a key. Resolves if there is no such key. | | sdk.storage.list() | Every key the user has saved for this app, as { key, size } with the size in bytes. | | sdk.review.request() | Asks the user to review the app, in a sheet Mythryl shows. Resolves once they submit; rejects with cancelled if they dismiss it, and also when Mythryl does not show it (not used enough, already reviewed, asked recently). You never learn which, and never see the rating. Ask after something good happened, never at launch, and never reward or require a review. | | sdk.install() | Asks the user to add this app to their library. Resolves at once if it is already there; rejects with cancelled if they decline. | | sdk.follow(did) | Asks the user to follow an account. Rejects with cancelled if they decline. | | sdk.on(event, listener) | Subscribes to a host event. Returns a function that removes the listener. | | sdk.dispose() | Removes listeners and rejects pending calls with cancelled. |

openProfile, openPost, openTopic, openUrl and openApp need the navigation capability. compose, follow, install, notifications.request and review.request each have a capability of the same name. Older hosts have none of them: check sdk.getCapabilities() first, or handle the unsupported rejection. Anything that writes (compose, follow, install, notifications.request, review.request) is confirmed by the user. A target the host cannot open rejects with unavailable.

Storage

sdk.storage is the user's saves for your app, kept by Mythryl, so a game's state follows the player across devices without a backend of your own. It needs the storage capability. The values are private to your app and that user.

  • Limits: a key is up to 128 characters, a value up to 256 KB as JSON, and one user can keep 1 MB in all. A write over a limit rejects with too_large or quota_exceeded.
  • Write rate: a key can be written about every 2 seconds, and more slowly the larger the value is (a 256 KB value every 16 seconds). A write sooner rejects with rate_limited, and its message says how long to wait. Save when something changes, such as the end of a level, not on a timer.
  • Last write wins, per key. Writing a value that has not changed costs nothing.
  • Do not trust it: the player can edit their own saves. Keep scores and rewards on your server.
const progress = (await sdk.storage.get<{ level: number }>("progress")) ?? { level: 1 };
await sdk.storage.set("progress", { level: progress.level + 1 });

Events

minimised and maximised are sent when the user hides the app and brings it back. colorSchemeChanged is sent with "light" or "dark" when the host switches mode. closing and resized are defined by the protocol; hosts do not send them yet. closing is best effort and must not be relied on to save state.

Light and dark mode

sdk.getContext() returns the host's current mode in client.colorScheme, and the colorSchemeChanged event reports each change. What the app does with it is up to the app.

const { client } = await sdk.getContext();
setMode(client.colorScheme);

sdk.on("colorSchemeChanged", setMode);

Signing in to your server

sdk.auth.signin() returns a token valid for 10 minutes, issued for the app's domain. Send it to your server as a bearer token and verify it there with @mythryl/server-sdk. The SDK caches the token and requests a new one when less than a minute of validity remains; signin({ force: true }) always requests a new one.

const { token } = await sdk.auth.signin();

const headers = { authorization: `Bearer ${token}` };
const response = await fetch("/api/me", { headers });

Errors

A call that fails rejects with a ProtocolError. Its code is cancelled, unsupported, not_consented, rate_limited, too_large, quota_exceeded or unavailable. A call the host answers by itself that gets no answer within 30 seconds rejects with unavailable. A call that waits on the user (openUrl, openApp, compose, follow, install, notifications.request, review.request) has no time limit: it ends when they answer.

Outside a host

Outside a Mythryl window there is no host to answer, and calls time out. isEmbedded() returns whether the page is framed (and false on a server, where there is no window):

import sdk, { isEmbedded } from "@mythryl/app-sdk";

if (isEmbedded()) await sdk.ready();

Other transports

createSdk(transport) builds an SDK over any Transport, which is how the SDK runs in a webview or a test. The default export uses windowTransport(), which talks to the embedding window.

License

MIT