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

@napplet/sdk

v0.2.1

Published

Typed named exports wrapping window.napplet for bundler-consuming napplet developers

Readme

@napplet/sdk

Named TypeScript exports for napplet developers using a bundler. Wraps window.napplet at call time.

Getting Started

Prerequisites

  • @napplet/shim must be imported (side-effect) to install window.napplet before SDK methods are called
  • A shell host running a napplet protocol shell implementation

How It Works

  1. Import @napplet/shim in your entry point to install the window.napplet global
  2. Import named exports from @napplet/sdk -- relay, ipc, storage, keys
  3. Each SDK method delegates to its window.napplet.* counterpart at call time
  4. If window.napplet is not installed when a method is called, a descriptive error is thrown

Installation

npm install @napplet/sdk @napplet/shim

Quick Start

import '@napplet/shim';
import { relay, ipc, storage, keys, media, notify, config, type NostrEvent } from '@napplet/sdk';

// Subscribe to kind 1 notes
const sub = relay.subscribe(
  { kinds: [1], limit: 20 },
  (event) => console.log('New note:', event.content),
  () => console.log('End of stored events'),
);

// Publish a signed note
const signed = await relay.publish({
  kind: 1,
  content: 'Hello from my napplet!',
  tags: [],
  created_at: Math.floor(Date.now() / 1000),
});

// Inter-pane messaging
ipc.emit('chat:message', [], JSON.stringify({ text: 'hi' }));
const ipcSub = ipc.on('bot:response', (payload) => {
  console.log('Bot says:', payload);
});

// Scoped storage
await storage.setItem('theme', 'dark');
const theme = await storage.getItem('theme'); // 'dark'

// Register keyboard action
const result = await keys.registerAction({
  id: 'editor.save', label: 'Save', defaultKey: 'Ctrl+S',
});

// Listen for bound key locally
const keySub = keys.onAction('editor.save', () => {
  console.log('Save triggered!');
});

// Create a media session
const { sessionId } = await media.createSession({
  title: 'My Song', artist: 'The Artist',
});
media.reportState(sessionId, { status: 'playing', position: 42.5, duration: 240 });

// Send a notification
const { notificationId } = await notify.send({
  title: 'Task complete', body: 'Build succeeded', priority: 'normal',
});
notify.badge(1);

// Read per-napplet config (shell-validated + defaulted)
const values = await config.get();
console.log('Current theme:', values.theme);

// Subscribe to live config updates
const configSub = config.subscribe((v) => {
  applyTheme(v.theme);
});

// Deep-link settings UI
config.openSettings({ section: 'appearance' });

// Clean up
sub.close();
ipcSub.close();
keySub.close();
configSub.close();

API Reference

relay

Relay operations through the shell's relay pool. Mirrors window.napplet.relay.

| Method | Returns | Description | |--------|---------|-------------| | subscribe(filters, onEvent, onEose, options?) | Subscription | Open a live relay subscription through the shell's relay pool | | publish(template, options?) | Promise<NostrEvent> | Send event template to the shell for signing and broadcast | | publishEncrypted(template, recipient, encryption?) | Promise<NostrEvent> | Send event template for encryption, signing, and broadcast | | query(filters) | Promise<NostrEvent[]> | One-shot query: subscribe, collect until EOSE, resolve |

ipc

Inter-napplet communication between napplets. Mirrors window.napplet.ipc.

Messages are sent as JSON envelope objects ({ type: 'ifc.emit', topic, payload }) and received as ({ type: 'ifc.event', topic, payload, sender }).

| Method | Returns | Description | |--------|---------|-------------| | emit(topic, extraTags?, content?) | void | Broadcast an IFC event to other napplets via the shell | | on(topic, callback) | { close(): void } | Subscribe to IFC events on a topic |

storage

Sandboxed key-value storage. Mirrors window.napplet.storage. 512 KB quota per napplet.

| Method | Returns | Description | |--------|---------|-------------| | getItem(key) | Promise<string \| null> | Retrieve a stored value | | setItem(key, value) | Promise<void> | Store a key-value pair | | removeItem(key) | Promise<void> | Remove a stored key | | keys() | Promise<string[]> | List all stored keys |

media

Media session control. Mirrors window.napplet.media.

| Method | Returns | Description | |--------|---------|-------------| | createSession(metadata?) | Promise<{ sessionId }> | Create a new media session with optional metadata | | updateSession(sessionId, metadata) | void | Update metadata for an existing session | | destroySession(sessionId) | void | Destroy a session | | reportState(sessionId, state) | void | Report playback state | | reportCapabilities(sessionId, actions) | void | Declare supported media actions | | onCommand(sessionId, callback) | { close(): void } | Listen for shell media commands | | onControls(sessionId, callback) | { close(): void } | Listen for the shell's supported control list |

notify

Shell-rendered notifications. Mirrors window.napplet.notify.

| Method | Returns | Description | |--------|---------|-------------| | send(notification) | Promise<{ notificationId }> | Send a notification to the shell | | dismiss(notificationId) | void | Dismiss a notification | | badge(count) | void | Set badge count (0 to clear) | | registerChannel(channel) | void | Register a notification channel | | requestPermission(channel?) | Promise<{ granted }> | Request permission to send notifications | | onAction(callback) | { close(): void } | Listen for action button clicks | | onClicked(callback) | { close(): void } | Listen for notification body clicks | | onDismissed(callback) | { close(): void } | Listen for dismissals | | onControls(callback) | { close(): void } | Listen for shell's notification capabilities |

config

Per-napplet declarative configuration (NUB-CONFIG). Mirrors window.napplet.config.

| Method | Returns | Description | |--------|---------|-------------| | get() | Promise<Record<string, unknown>> | One-shot snapshot of validated + defaulted config values | | subscribe(callback) | { close(): void } | Live push stream (initial snapshot + updates on change) | | openSettings(options?) | void | Open shell's settings UI, optionally deep-linked to x-napplet-section | | registerSchema(schema, version?) | Promise<void> | Runtime schema registration (escape hatch; prefer vite-plugin configSchema) | | onSchemaError(callback) | () => void | Listen for config.schemaError pushes (returns plain teardown fn) | | schema (accessor) | Record<string, unknown> \| null | Readonly current schema |

FromSchema type inference (NUB-CONFIG)

json-schema-to-ts is declared as an optional peerDependency of @napplet/nub-config. Install it in your napplet to get FromSchema<typeof schema> typing for your config.subscribe callback -- the values parameter is inferred directly from your schema (enums, required fields, defaults all flow through). Authors who skip json-schema-to-ts pay no install cost and config.subscribe still works with the default Record<string, unknown> typing.

import '@napplet/shim';
import { config } from '@napplet/sdk';
import type { FromSchema } from 'json-schema-to-ts';

const schema = {
  type: 'object',
  properties: {
    theme: { type: 'string', enum: ['light', 'dark'], default: 'dark' },
  },
  required: ['theme'],
} as const;

type MyConfig = FromSchema<typeof schema>;

const sub = config.subscribe((values: MyConfig) => {
  // values.theme is typed 'light' | 'dark'
});

Install the peer when you want typed callbacks:

npm install --save-dev json-schema-to-ts

keys

Keyboard forwarding and action keybindings. Mirrors window.napplet.keys.

| Method | Returns | Description | |--------|---------|-------------| | registerAction(action) | Promise<{ actionId, binding? }> | Declare a named action the shell can bind to a key | | unregisterAction(actionId) | void | Remove a previously registered action | | onAction(actionId, callback) | { close(): void } | Register a local handler for a bound key (zero-latency, not a wire message) |

shell

Namespaced capability query. Access via window.napplet.shell.supports() after importing @napplet/shim.

Note: The SDK does not export a top-level shell object. Use window.napplet.shell.supports() directly.

| Method | Returns | Description | |--------|---------|-------------| | supports(capability) | boolean | Check shell support for a NUB (nub:relay) or permission (perm:popups). Bare NUB names also accepted (relay). |

Example:

import '@napplet/shim';

// NUB domains (bare shorthand or nub: prefix)
if (window.napplet.shell.supports('relay')) { /* ... */ }
if (window.napplet.shell.supports('nub:identity')) { /* ... */ }

// Permissions
if (window.napplet.shell.supports('perm:popups')) { /* ... */ }

Namespace Import

import * as napplet from '@napplet/sdk' produces an object structurally identical to window.napplet:

import * as napplet from '@napplet/sdk';

napplet.relay.subscribe({ kinds: [1] }, (e) => console.log(e));
napplet.storage.setItem('key', 'value');
napplet.config.subscribe((v) => console.log(v));

Types

All protocol types are re-exported from @napplet/core and the NUB packages:

import type {
  // Protocol types (from @napplet/core)
  NostrEvent,
  NostrFilter,
  Subscription,
  EventTemplate,
  NappletMessage,
  NubDomain,
  NamespacedCapability,
  ShellSupports,
  // NUB message types (re-exported from NUB packages)
  RelayNubMessage,
  IdentityNubMessage,
  StorageNubMessage,
  IfcNubMessage,
  KeysNubMessage,
  Action,
} from '@napplet/sdk';

Core Protocol Types

| Type | Description | |------|-------------| | NostrEvent | Standard Nostr event object | | NostrFilter | Relay subscription filter | | Subscription | Handle with close() method | | EventTemplate | Unsigned event for relay.publish() | | NappletMessage | Base JSON envelope type for all protocol messages | | NubDomain | String literal union of NUB domain names | | NamespacedCapability | Union of NubDomain \| nub:* \| perm:* for supports() | | ShellSupports | Interface for the shell capability query API |

NUB Message Types

These are discriminated union types covering all messages in each NUB domain. Useful for writing typed message handlers in shell implementations or protocol-aware code.

| Type | NUB Package | Description | |------|-------------|-------------| | RelayNubMessage | @napplet/nub-relay | Discriminated union of all relay domain messages | | IdentityNubMessage | @napplet/nub-identity | Discriminated union of all identity domain messages | | StorageNubMessage | @napplet/nub-storage | Discriminated union of all storage domain messages | | IfcNubMessage | @napplet/nub-ifc | Discriminated union of all IFC domain messages | | KeysNubMessage | @napplet/nub-keys | Discriminated union of all keys domain messages | | MediaNubMessage | @napplet/nub-media | Discriminated union of all media domain messages | | NotifyNubMessage | @napplet/nub-notify | Discriminated union of all notify domain messages | | ConfigNubMessage | @napplet/nub-config | Discriminated union of all config domain messages |

Individual message types (e.g., RelaySubscribeMessage, IdentityGetPublicKeyMessage) are also re-exported from @napplet/sdk for fine-grained typing.

NUB Domain Constants

Each NUB domain has a string constant re-exported from its package:

import { RELAY_DOMAIN, IDENTITY_DOMAIN, STORAGE_DOMAIN, IFC_DOMAIN, THEME_DOMAIN, KEYS_DOMAIN, MEDIA_DOMAIN, NOTIFY_DOMAIN, CONFIG_DOMAIN } from '@napplet/sdk';
// Values: 'relay', 'identity', 'storage', 'ifc', 'theme', 'keys', 'media', 'notify', 'config'

These constants are re-exported from the individual NUB packages. Use them with the shell capability query API for type-safe conditional logic:

if (window.napplet.shell.supports('nub:relay')) {
  // relay operations are available
}

if (window.napplet.shell.supports('nub:identity')) {
  // identity queries are available
}

if (window.napplet.shell.supports('nub:config')) {
  // NUB-CONFIG is available -- schema registration and subscribe()
}

Runtime Guard

If window.napplet is not installed when an SDK method is called, a clear error is thrown:

Error: window.napplet not installed -- import @napplet/shim first

This protects against importing @napplet/sdk without the side-effect shim import.

SDK vs Shim

| | @napplet/sdk | @napplet/shim | |---|---|---| | Import style | import { relay } from '@napplet/sdk' | import '@napplet/shim' (side-effect) | | What it does | Named exports wrapping window.napplet | Installs window.napplet + shell registration | | Dependencies | @napplet/core (types only) | None (types from @napplet/core) | | Side effects | None | Yes -- installs globals, registers with shell | | Required | Optional convenience | Required in every napplet |

Typical usage: Import both -- shim for runtime, SDK for developer API:

import '@napplet/shim';                                                  // required: installs window.napplet
import { relay, ipc, storage, keys, media, notify } from '@napplet/sdk';  // optional: typed API

If you are writing a vanilla napplet with no build step, use window.napplet.* directly after importing the shim -- the SDK is not required.

Protocol Reference

License

MIT