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

@inovus-medical/notifications

v0.3.0

Published

Typed client for the Inovus notification service — reliable by default (retries, timeouts, ETag polling), with optional React hooks

Readme

@inovus-medical/notifications

Typed client for the Inovus Notifications service — a persistent in-app inbox (not toasts).

  • Retries with backoff (publish retries only when you set a dedupeKey — see below)
  • Per-request timeouts
  • One-shot token refresh on 401
  • ETag polling (~30s) that returns 304 when nothing changed
  • Optional React hooks (@inovus-medical/notifications/react)
npm install @inovus-medical/notifications
# or: pnpm add @inovus-medical/notifications

| | | |---|---| | Production API | https://notifications.totumcloud.com | | OpenAPI / Swagger | https://notifications.totumcloud.com/docs | | This README | Humans integrating the npm package | | Agent brief | Copy-paste for coding agents: AGENT.md | | Full docs | docs index |


Who uses which credential

| App role | Credential | Use | |---|---|---| | SPA / browser | User Cognito access token | Inbox + notify self (publishSelf / notification.*) | | Backend / job / BFF | Publisher API key or M2M JWT | publish({ userGuid, … }) to any user | | SPA → another user | Your BFF only | Never put API keys or M2M secrets in the browser |

apiKey and getAccessToken are mutually exclusive — do not set both.


Quick start — inbox (browser)

import { NotificationsClient } from '@inovus-medical/notifications';

const client = new NotificationsClient({
  baseUrl: 'https://notifications.totumcloud.com',
  // Your existing Cognito wiring — called per request, and again after a 401.
  getAccessToken: () => auth.getValidAccessToken(),
});

const stop = client.subscribe(
  ({ items, unreadCount }) => renderBell(items, unreadCount),
  { intervalMs: 30_000 },
);
// ALWAYS call stop() on logout / unmount

const { items, nextCursor, unreadCount } = await client.list({ unreadOnly: true, limit: 50 });
await client.unreadCount();
await client.markRead(id);
await client.markUnread(id);
await client.markAllRead();
await client.delete(id);

Self-notify helpers (V1-shaped)

import { NotificationsClient, createNotificationService } from '@inovus-medical/notifications';

const client = new NotificationsClient({
  baseUrl: 'https://notifications.totumcloud.com',
  getAccessToken: () => auth.getValidAccessToken(),
});

export const notification = createNotificationService(client);

await notification.success('Export ready', 'Your file is ready.', '/exports/1');
await notification.error('Failed', error.message);
await notification.warning('Warning', 'Check this');
await notification.info('Info', 'Heads up');

Each helper calls POST /v2/notifications/self (recipient = token sub) and adds its own unique dedupeKey so a retried helper call won’t create two items.

Your app still owns: toast UI, bell markup, and navigating on actionUrl.

React

import { useNotifications } from '@inovus-medical/notifications/react';

function Bell() {
  const { notifications, unreadCount, loading, error, markRead, markUnread, markAllRead, remove } =
    useNotifications(client);
  // subscription + cleanup on unmount handled for you
}

Quick start — publish (backend)

API key (easiest)

const publisher = new NotificationsClient({
  baseUrl: 'https://notifications.totumcloud.com',
  apiKey: process.env.NOTIFICATIONS_API_KEY!, // never NEXT_PUBLIC_ / never browser
});

await publisher.publish({
  userGuid: 'cognito-sub-of-recipient', // MUST match the reader's Cognito `sub`
  title: 'Review complete',
  body: 'Your assessment feedback is ready.',
  severity: 'success', // 'info' | 'success' | 'warning' | 'error'
  actionUrl: '/video/abc/feedback',
  dedupeKey: 'review_case:8d2e', // ALWAYS set for jobs
  metadata: { caseId: '8d2e' }, // optional, ≤ 8 KB
});

M2M token

Use getAccessToken that returns a Cognito client-credentials token with scope notifications/publish.

dedupeKey — why you should always set one

Think of dedupeKey as a label for this specific notification, chosen by your app (e.g. review_case:8d2e or export:job:99).

What goes wrong without it

  1. Your server sends “review complete” and the network drops before you see the reply.
  2. You (or this client) send it again.
  3. The user gets two identical inbox items.

What happens with a stable key

  1. First send creates the notification.
  2. Same recipient + same app + same dedupeKey again → the API does not create a second row. You get success with duplicate: true and the original item.
  3. Safe to retry after timeouts, 429s, or crashes.

Rules of thumb

  • Backend / jobs: always set a key that means something in your domain (review_case:{id}, not a random UUID each attempt).
  • Same real-world event → same key. Different events → different keys.
  • Without a dedupeKey, this npm client will not automatically retry publish (on purpose — retries would risk duplicates).
  • Browser helpers (notification.success(...)) invent a unique key per call for you.

Errors

import { NotificationsApiError } from '@inovus-medical/notifications';

try {
  await client.markRead(id);
} catch (err) {
  if (err instanceof NotificationsApiError) {
    err.status; // e.g. 404, 429
    err.problem; // RFC 9457 { title, status, detail? }
  }
}

429 includes Retry-After; the client honours it when retrying.

Server caps (after auth): 300 req / 60s per user (inbox/self), 600 / 60s per publisher (cross-user publish).


Options

| Option | Default | Notes | |---|---|---| | baseUrl | — | Service origin (no trailing slash required) | | getAccessToken | — | Sync/async JWT getter. Required unless apiKey is set | | apiKey | — | Publisher secret. Backend only. Do not combine with getAccessToken | | timeoutMs | 10000 | Per attempt | | maxRetries | 3 | Extra attempts on 429/5xx/network; publish only if dedupeKey set | | retryBaseDelayMs | 250 | Exponential backoff + full jitter | | fetch | global | Injectable for tests / custom runtimes |

Works in browsers, Node ≥ 20, and edge runtimes (fetch / AbortController). React is an optional peer — only for /react.

Package exports

| Import | Exports | |---|---| | @inovus-medical/notifications | NotificationsClient, createNotificationService, NotificationsApiError, wire types | | @inovus-medical/notifications/react | useNotifications |


For coding agents

  1. Paste the full AGENT.md into the agent’s context (or save it as a rule in the consuming app).
  2. Do not invent endpoints, fields, or auth rules outside that brief.
  3. Prefer this package over raw fetch.
  4. SPA = user token + inbox/self only. Cross-user = server/BFF + API key or M2M + stable dedupeKey.

Human walkthroughs: Getting started · Publishing · App integration · Rate limiting

BFF samples in the monorepo: examples/publish-proxy-worker/, examples/publish-proxy-next/.


Releasing this package (maintainers)

Full guide: releasing.md (what a changeset is, why .changeset/ often only has config.json).

Short version: pnpm changeset → merge to master → merge Version Packages → GitHub Actions publishes to npm. Do not npm publish from your laptop.