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

@molecule/api-push-notifications

v1.0.3

Published

Push notification provider interface for molecule.dev

Downloads

0

Readme

@molecule/api-push-notifications

Push notification provider interface for molecule.dev.

Provides an abstract push notification interface that can be backed by any push notification library. Use setProvider to bond a concrete implementation such as @molecule/api-push-notifications-web-push.

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Quick Start

import { configure, sendMany, getPublicKey } from '@molecule/api-push-notifications'
configure({ email: '[email protected]', publicKey: VAPID_PUBLIC, privateKey: VAPID_PRIVATE })

// Browser subscribes with the PUBLIC key only; the API stores the subscription per user.
router.post('/push/subscribe', async (req, res) => {
  await saveSubscription(getUserId(res), req.body.subscription) // scoped to the user
  res.json({ ok: true })
})

// Server sends, then prunes subscriptions the push service reports as gone.
// A dead endpoint REJECTS (it lands in r.error with a statusCode), it does not
// resolve into r.result — checking r.result for 404/410 would never prune anything.
const rows = await subscriptionsFor(userId)
const results = await sendMany(
  rows.map((r) => r.subscription),
  { title: 'Hi', options: { body: '…' } },
)
results.forEach((r, i) => {
  const gone = (r.error as { statusCode?: number } | undefined)?.statusCode
  if (gone === 404 || gone === 410) deleteSubscription(rows[i].id)
})

Type

core

Installation

npm install @molecule/api-push-notifications @molecule/api-bond

API

Interfaces

PushSubscription

A push subscription representing a client endpoint.

interface PushSubscription {
  endpoint: string
  keys: {
    p256dh: string
    auth: string
  }
}

NotificationPayload

Notification payload structure.

interface NotificationPayload {
  title: string
  options?: {
    body?: string
    icon?: string
    badge?: string
    image?: string
    tag?: string
    data?: Record<string, unknown>
    actions?: Array<{
      action: string
      title: string
      icon?: string
    }>
    requireInteraction?: boolean
    silent?: boolean
  }
}

SendResult

Result of sending a single push notification.

interface SendResult {
  statusCode: number
  headers: Record<string, string>
  body: string
}

SendManyResult

Result entry when sending to multiple subscriptions.

Exactly one of result / error is set per entry. A dead subscription (HTTP 404/410 from the push service) is a FAILURE: it arrives in error (provider errors such as web-push's WebPushError carry a statusCode), never as a resolved result — prune stored subscriptions by inspecting error, not result.

interface SendManyResult {
  subscription: PushSubscription
  result?: SendResult
  error?: Error
}

VapidConfig

VAPID configuration for push notification authentication.

interface VapidConfig {
  email: string
  publicKey: string
  privateKey: string
}

VapidKeys

VAPID key pair returned by key generation.

interface VapidKeys {
  publicKey: string
  privateKey: string
}

PushNotificationProvider

Push notification provider interface that all implementations must satisfy.

interface PushNotificationProvider {
  /**
   * Configure VAPID credentials for push notifications.
   */
  configure(config?: VapidConfig): void

  /**
   * Sends a push notification to a single subscription endpoint.
   */
  send(subscription: PushSubscription, payload: NotificationPayload): Promise<SendResult>

  /**
   * Sends a push notification to multiple subscriptions, handling
   * partial failures gracefully.
   */
  sendMany(
    subscriptions: PushSubscription[],
    payload: NotificationPayload,
  ): Promise<SendManyResult[]>

  /**
   * Generates a new VAPID key pair for push notification authentication.
   */
  generateVapidKeys(): VapidKeys

  /**
   * Returns the public VAPID key for client-side subscription requests,
   * or `undefined` if VAPID is not configured.
   */
  getPublicKey(): string | undefined
}

Functions

setProvider(provider)

Registers a push notification provider as the active singleton. Called by bond packages during application startup.

function setProvider(provider: PushNotificationProvider): void
  • provider — The push notification provider implementation to bond.

getProvider()

Retrieves the bonded push notification provider, or undefined if none is bonded.

function getProvider(): PushNotificationProvider | undefined

Returns: The bonded push notification provider, or undefined.

hasProvider()

Checks whether a push notification provider is currently bonded.

function hasProvider(): boolean

Returns: true if a push notification provider is bonded.

requireProvider()

Retrieves the bonded push notification provider, throwing if none is configured.

function requireProvider(): PushNotificationProvider

Returns: The bonded push notification provider.

configure(config)

Configures VAPID credentials on the bonded push notification provider.

function configure(config?: VapidConfig): void
  • config — The VAPID configuration containing email, public key, and private key.

send(subscription, payload)

Sends a push notification to a single subscription endpoint.

function send(subscription: PushSubscription, payload: NotificationPayload): Promise<SendResult>
  • subscription — The push subscription containing the client endpoint and encryption keys.
  • payload — The notification payload with title and optional display options.

Returns: The send result containing status code, headers, and response body.

sendMany(subscriptions, payload)

Sends a push notification to multiple subscription endpoints. Handles partial failures gracefully — each result includes either the send result or the error for that subscription.

function sendMany(
  subscriptions: PushSubscription[],
  payload: NotificationPayload,
): Promise<SendManyResult[]>
  • subscriptions — The push subscriptions to send to.
  • payload — The notification payload with title and optional display options.

Returns: One result entry per subscription, each containing either result or error.

generateVapidKeys()

Generates a new VAPID key pair for push notification authentication.

function generateVapidKeys(): VapidKeys

Returns: An object containing publicKey and privateKey strings.

getPublicKey()

Returns the public VAPID key for client-side push subscription requests.

function getPublicKey(): string | undefined

Returns: The public VAPID key string, or undefined if not configured.

Available Providers

| Provider | Package | | ----------------- | ------------------------------------------- | | Capture | @molecule/api-push-capture | | Molecule (hosted) | @molecule/api-push-notifications-molecule | | Web Push (VAPID) | @molecule/api-push-notifications-web-push |

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-bond ^1.0.1

Runtime Dependencies

  • @molecule/api-bond

  • The VAPID private key is a server secret — it stays in the API (config/secrets). Only the public key ({@link getPublicKey}) goes to the browser for subscribe(). Generate the pair ONCE with {@link generateVapidKeys} and persist it in env; do NOT regenerate per boot — new keys invalidate every existing subscription.

  • Store each {@link PushSubscription} server-side, scoped to its user, and send from the server with {@link send}/{@link sendMany}. The browser never sends notifications and never holds another user's subscription.

  • Prune dead endpoints: a gone subscription (HTTP 404/410 from the push service) surfaces as a failed send — with the web-push bond the per-subscription error in {@link SendManyResult} (a WebPushError carrying statusCode), NOT a resolved {@link SendResult}. Check r.error for 404/410 and delete that row so you stop pushing to it. (send() for a single subscription throws in that case.)

  • configure() once at startup with your {@link VapidConfig} (email + keys).

E2E Tests

Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:

  • [ ] The UI offers an enable-notifications control; activating it triggers the browser permission prompt and, once granted, the subscription is stored (the UI still shows "enabled" after a full reload).
  • [ ] An event this app notifies about actually delivers a push to the subscribed session, with a readable title/body (not raw JSON). The sandbox CAPTURES outbound pushes instead of delivering — read the captured message with the read_activity tool (filter type 'push'); never mock the flow or modify production code to expose it.
  • [ ] Clicking the delivered notification opens/focuses the relevant screen (when the app claims deep-linking).
  • [ ] Denying the permission leaves the app fully usable and truthful about the state (no crash, no false "enabled").
  • [ ] Disabling/unsubscribing stops deliveries, and the disabled state persists across a reload.