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

@yuno-payments/dashboard-embed-sdk

v1.11.0

Published

Lightweight SDK for embedding the Yuno Dashboard via iframe

Readme

@yuno-payments/dashboard-embed-sdk

Lightweight SDK for embedding the Yuno Dashboard via iframe. Zero dependencies — uses only DOM APIs.

Installation

npm install @yuno-payments/dashboard-embed-sdk

Quick Start

import { initDashboard } from "@yuno-payments/dashboard-embed-sdk";

const sdk = initDashboard({
  baseUrl: "https://dashboard.y.uno",
  container: document.getElementById("dashboard")!,
  token: "your-jwt-token",
  theme: {
    // Simple: flat tokens apply to both light mode and dark mode;
    tokens: { primary: "#134AC3", surface: "#FFFFFF" },
    // Per-mode: full control over both light and dark colors
    // tokens: {
    //   light: { primary: "#134AC3", surface: "#FFFFFF" },
    //   dark: { primary: "#5B7BFF", surface: "#0A0A0A" },
    // },
    typography: {
      fontFamily: "'Inter', sans-serif",
      fontUrl: "https://fonts.googleapis.com/css2?family=Inter",
    },
    mode: "light",
    styles: ".yuno-card { border-radius: 8px; }",
  },
  lang: "en",
  path: "/connections",
  onReady: () => console.log("Dashboard is ready"),
});

API

Singleton helpers

The recommended way to manage the SDK instance:

import {
  initDashboard,
  getDashboard,
  destroyDashboard,
} from "@yuno-payments/dashboard-embed-sdk";

// Create the single dashboard instance
const sdk = initDashboard(config);

// Retrieve the current instance from anywhere
const sdk = getDashboard();

// Destroy the instance and clean up
destroyDashboard();

The SDK runs a single instance. Call initDashboard() once, then drive that instance — navigate() to change pages, setTheme()/setLang()/setToken() for runtime updates. Calling initDashboard() again while an instance already exists throws: re-initializing reloads the iframe and, with syncUrl on, the new path is overridden by the previously synced route. To genuinely tear down and start over, call destroyDashboard() first.

// ❌ Don't re-init to change pages — this throws on the 2nd call:
initDashboard({ ...config, path: "/connections" });
initDashboard({ ...config, path: "/checkout-builder" });

// ✅ Init once, then navigate:
const sdk = initDashboard({ ...config, path: "/connections" });
sdk.navigate("/checkout-builder");

// ✅ Or explicitly tear down first if you really need a fresh instance:
destroyDashboard();
initDashboard({ ...config, path: "/checkout-builder" });

initDashboard(config)

| Option | Type | Required | Description | |---|---|---|---| | baseUrl | string | Yes | Dashboard base URL | | container | HTMLElement | Yes | Element to mount the iframe | | token | string | No | JWT auth token — sent via PostMessage after the iframe is ready | | theme | DashboardTheme | No | Initial theme configuration | | lang | string | No | Language code (default: "en") | | path | string | No | Initial navigation path (default: "/"). Any query string you pass is stripped — only the SDK sets the iframe query params (embed/theme/lang/test) | | testMode | boolean | No | When set, the dashboard mounts in test/sandbox mode (true) or live mode (false). Omit to inherit the dashboard's own default. See Test mode | | onReady | () => void | No | Callback invoked when the dashboard is fully loaded and authenticated | | onSessionExpired | () => void | No | Callback invoked when the embedded session has expired. The host should re-authenticate and call setToken(newToken) to resume. | | onNavigationChange | (event: NavigationChangedEvent) => void | No | Fires on every in-app navigation inside the dashboard, delivering the new route via event.path. A cross-cutting notification (like onReady), not a domain event. Optional — with syncUrl on (default) the SDK already keeps the host URL in sync. See Navigation. | | events | DashboardEvents | No | Notifications the dashboard sends the host, grouped by domain and action — e.g. events.connections.created, events.checkout.published. Pure callbacks: providing a handler does not change what the dashboard does. To make the dashboard step aside (e.g. skip its connection success screen), use ui. See Embed events. | | loading | HTMLElement | No | Custom loading overlay element. If omitted, a default spinner is shown | | autoHeight | boolean | No | When true, the iframe is resized to match the dashboard content height (no inner scroll). Default false (iframe fills its container at 100%). See Auto height | | syncUrl | boolean | No | Keep the host's browser URL in sync with the dashboard's in-app route automatically, via the URL hash (e.g. #/payments/abc) — no host code required. Default true; set false to opt out (e.g. if the host owns routing). See Navigation | | ui | EmbedUi | No | Declarative UI config announced to the dashboard on init — hide/show dashboard UI elements for this embed (e.g. the checkout publish button). See Commands & UI config |

Methods

  • setTheme(theme) — Update colors, typography, mode, or external styles
  • setLang(lang) — Change display language
  • setToken(token) — Update the auth token via PostMessage
  • navigate(path) — Navigate to a dashboard route
  • dispatch(command) — Send an imperative command into the dashboard (e.g. publish the open checkout). See Commands & UI config
  • setTestMode(enabled) — Toggle test/sandbox mode at runtime via PostMessage. See Test mode
  • destroy() — Remove iframe and clean up event listeners

Types

interface ThemeColors {
  primary: string;
  primaryForeground: string;
  secondary: string;
  secondaryForeground: string;
  background: string;
  foreground: string;
  muted: string;
  mutedForeground: string;
  accent: string;
  accentForeground: string;
  destructive: string;
  destructiveForeground: string;
  border: string;
  input: string;
  ring: string;
  surface: string;
  card: string;
  cardForeground: string;
  popover: string;
  popoverForeground: string;
  success: string;
  warning: string;
  info: string;
}

interface ThemeTypography {
  fontFamily: string;
  fontUrl: string;
}

interface ModeTokens {
  light?: Partial<ThemeColors>;
  dark?: Partial<ThemeColors>;
}

interface DashboardTheme {
  tokens?: Partial<ThemeColors> | ModeTokens; // flat OR per-mode
  typography?: Partial<ThemeTypography>;
  mode?: "light" | "dark";
  styles?: string; // Custom CSS injected into the dashboard
}

type EmbedEventStatus = "loading" | "success" | "error";

interface ConnectionCreatedPayload {
  connectionCode: string;
  providerId: string;
}

interface ConnectionCreatedEvent {
  status: EmbedEventStatus;
  payload?: ConnectionCreatedPayload;
}

interface CheckoutPublishedEvent {
  status: EmbedEventStatus;
  payload?: unknown; // The configuration object that was published
}

interface NavigationChangedEvent {
  path: string; // The new in-app route, e.g. "/payments/abc?status=approved"
}

interface DashboardEvents {
  connections?: {
    created?: (event: ConnectionCreatedEvent) => void | Promise<void>;
  };
  checkout?: {
    published?: (event: CheckoutPublishedEvent) => void | Promise<void>;
  };
}

// Imperative command sent into the dashboard via dashboard.dispatch().
type EmbedCommand = {
  domain: "checkout";
  action: "publish";
  payload?: unknown;
};

// Declarative UI config passed via the `ui` option.
interface EmbedUi {
  checkout?: {
    hidePublishButton?: boolean;
  };
  connections?: {
    hideSuccessScreen?: boolean;
  };
}

onNavigationChange is a top-level lifecycle callback (like onReady), not a DashboardEvents entry: navigation is a cross-cutting notification with no domain ownership, so it does not announce a host capability. See Navigation.

Session timeouts

By default, sessions issued by POST /v1/external/authenticate are valid for 24 hours. You can issue a shorter session by passing timeout_seconds (between 60 and 86400):

curl -X POST https://api.y.uno/v1/external/authenticate \
  -H "x-organization-code: <your-org-uuid>" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"<user-uuid>", "timeout_seconds": 1800}'

When the embedded session expires, the iframe paints a "Session expired" overlay and emits a message to the host. Subscribe via onSessionExpired:

const sdk = initDashboard({
  baseUrl: "https://dashboard.y.uno",
  container: document.getElementById("dashboard")!,
  token: initialToken,
  onSessionExpired: async () => {
    const newToken = await myBackend.requestEmbedToken({ timeout_seconds: 1800 })
    sdk.setToken(newToken)
  },
})

Embed events

The embedded dashboard sends business events to the host through the events config, grouped by MFE (domain) and action. Register a handler only for what you care about:

const sdk = initDashboard({
  baseUrl: "https://dashboard.y.uno",
  container: document.getElementById("dashboard")!,
  token: initialToken,
  events: {
    connections: {
      created: (event) => {
        // event.status is "loading" | "success" | "error"
        // event.payload is { connectionCode, providerId } on success
        if (event.status === "success") {
          router.push(`/integrations/${event.payload!.connectionCode}/done`)
        }
      },
    },
    checkout: {
      published: (event) => {
        if (event.status === "success") {
          console.log("Checkout published", event.payload)
        }
      },
    },
  },
})

Events are notifications; ui makes the dashboard step aside

Events are pure callbacks — providing a handler tells the dashboard nothing and does not change its behavior. Making the dashboard skip its own UI is a separate, explicit decision via the ui option.

For connection creation the two compose: register events.connections.created to be notified when a connection is created, and set ui: { connections: { hideSuccessScreen: true } } to stop the dashboard from showing its own success screen so you can route the user yourself. Each works independently — notify without suppressing, or suppress without notifying.

Status lifecycle

Handlers fire once per state transition: first status: "loading" when the action starts, then "success" or "error" once the dashboard's call resolves. event.payload is only meaningful on "success".

Navigation (keeping the host URL in sync)

When the user navigates inside the iframe (e.g. Payment Detail → Routing), the SDK keeps the host's browser URL in sync automatically — you write no code. This is on by default (syncUrl: true) and works in both directions via the URL hash:

  • iframe → host: on every in-app route change the SDK writes the route to the host URL hash, e.g. https://your-app.com/dashboard#/payments/abc. The address bar stays shareable and bookmarkable, and each navigation is a history entry.
  • host → iframe (reload / shared link): on load the SDK reads the route from the hash and boots the iframe straight into that view, so a reload or a shared bookmark reopens where the user was.
  • host → iframe (back / forward): the SDK listens for the host URL changing (browser back/forward) and steers the dashboard to match.
// That's it — bookmarking, sharing, and back/forward just work:
const sdk = initDashboard({
  baseUrl: "https://dashboard.y.uno",
  container: document.getElementById("dashboard")!,
  token: initialToken,
});

Set syncUrl: false to opt out — for example if the host owns routing and prefers to manage the URL itself. In that case (or alongside the automatic sync) you can register the optional top-level onNavigationChange callback to run custom logic on each navigation:

const sdk = initDashboard({
  baseUrl: "https://dashboard.y.uno",
  container: document.getElementById("dashboard")!,
  token: initialToken,
  syncUrl: false, // SDK won't touch the host URL
  onNavigationChange: (event) => {
    // event.path is the dashboard's new in-app route
    window.history.replaceState(null, "", `/embed${event.path}`);
  },
});

onNavigationChange is a pure notification — like onReady it is a top-level lifecycle callback (not a domain event), it has no status lifecycle, and the dashboard does not change its own behavior; it just reports the new route via event.path.

Host/iframe contract: the embedded dashboard posts { type: "yuno-dashboard:embed-event", domain: "navigation", action: "changed", payload: { path } } to the host on each internal navigation; the SDK reflects path into the host URL hash (when syncUrl) and routes it to the optional callback. Messages are accepted only from the configured baseUrl origin.

Commands & UI config

The previous sections cover the dashboard handing off to the host. These two channels go the other way — the host drives the dashboard:

  • Commands (imperative): trigger an action inside the dashboard, e.g. publish the checkout the user is editing.
  • UI config (declarative): hide/show dashboard UI elements for this embed, e.g. hide the dashboard's own publish button so the host can render its own.

Commands — dispatch(command)

Navigate to the relevant view first, then dispatch. A command for an MFE that is not currently mounted is a no-op (nothing is queued for it), so make sure the target view is open. The result of the action comes back through the matching embed event — e.g. publishing reports via events.checkout.published.

const sdk = initDashboard({
  baseUrl: "https://dashboard.y.uno",
  container: document.getElementById("dashboard")!,
  token,
  path: "/checkout/editor/abc",
  events: {
    checkout: {
      published: (event) => {
        if (event.status === "success") console.log("Published!", event.payload);
      },
    },
  },
});

// Host's own "Publish" button:
publishBtn.addEventListener("click", () => {
  sdk.dispatch({ domain: "checkout", action: "publish" });
});

UI config — ui

Announced once to the dashboard when it becomes ready. Use it to suppress dashboard UI that the host replaces with its own:

initDashboard({
  baseUrl: "https://dashboard.y.uno",
  container: document.getElementById("dashboard")!,
  token,
  ui: {
    checkout: {
      hidePublishButton: true, // host renders its own publish button
    },
    connections: {
      hideSuccessScreen: true, // host owns the post-create moment
    },
  },
});

Host/iframe contract: dispatch() posts { type: "yuno-dashboard:command", domain, action, payload } to the iframe; the ui config is announced as { type: "yuno-dashboard:ui", ui } on ready. The dashboard validates both come from the host (parent) origin.

Test mode

The dashboard can run in test/sandbox mode or live mode. Set it at mount time via the testMode option, or toggle it at runtime via setTestMode(enabled):

const sdk = initDashboard({
  baseUrl: "https://dashboard.y.uno",
  container: document.getElementById("dashboard")!,
  token,
  path: "/connections", // clean route — no query string
  testMode: false,      // mount in live mode
});

// Later — flip to test mode without remounting
sdk.setTestMode(true);

Do not pass test mode through path. The SDK builds the iframe URL by appending its own query string (embed, theme, lang, and test when testMode is set) to the route: ${baseUrl}${path}?embed=true&…. A path that already carries a query (e.g. "/connections?test=false") produces a malformed double-? URL that swallows embed and breaks the embed (the dashboard never mounts). Always pass a query-free path and use the testMode option / setTestMode() method instead.

Host/iframe contract: when testMode is provided, the SDK appends test=<bool> to the initial iframe URL — the embedded dashboard reads it from its own URL on load. For the runtime toggle, setTestMode(enabled) sends postMessage({ action: "setTestMode", testMode }) (accepted only from the configured baseUrl origin); the embedded dashboard must handle that action to switch mode without a reload.

Loading overlay

A loading overlay covers the iframe while initialization completes. You can provide a custom element via the loading config option, or the SDK renders a default spinner. The overlay fades out automatically (300ms transition) once the dashboard is authenticated and ready.

Auto height

By default the iframe fills its container (height: 100%) and the dashboard content scrolls inside it. Pass autoHeight: true to size the iframe to the dashboard's content height instead, so the whole host page scrolls naturally with no inner scrollbar:

initDashboard({
  baseUrl: "https://dashboard.y.uno",
  container: document.getElementById("dashboard")!,
  token,
  autoHeight: true,
});

Host/iframe contract: when autoHeight is enabled, the embedded dashboard reports its content height to the host via postMessage({ action: "resize", height }) whenever the content resizes; the SDK applies that height to the iframe and its wrapper. Messages are accepted only from the configured baseUrl origin. Your container must allow vertical growth (avoid a fixed height / overflow: hidden) for the effect to be visible.

Overlays (modals & drawers)

An embedded overlay is positioned relative to its iframe, not the host page — so a modal would appear centered to the iframe panel, not the browser viewport. To fix this, the embedded dashboard signals the SDK when an overlay opens or closes (embed-event with domain: "ui", action: "overlay-open" | "overlay-close"), and the SDK expands the iframe to the full viewport (position: fixed; inset: 0, top z-index) for as long as any overlay is open, then restores it. This is automatic — no host code required. Signals are ref-counted, so stacked overlays are handled correctly.

While expanded the iframe is transparent, and the SDK captures the iframe's prior panel position (getBoundingClientRect) and sends it to the dashboard as an offset (postMessage({ action: "overlay-mode", active, offset })). The dashboard uses that offset to leave the host's chrome regions unpainted, so your surrounding UI stays visible through the iframe while the overlay is centered to the page. No host code required.

Host/iframe contract: while an overlay is open the iframe covers the whole viewport (transparent) at a maximal z-index. Host chrome behind the iframe is visible but not interactive during the overlay (the iframe captures pointer events) — expected for a modal. Messages are accepted only from the configured baseUrl origin.

Migrating from 0.x to 1.0

1.0.0 drops the Yuno brand prefix from the public API so the SDK reads cleanly in white-labelled integrations. The old names were removed — there is no compatibility shim, so update every import and call site:

| 0.x (removed) | 1.0 (new) | |---|---| | initYunoDashboard(config) | initDashboard(config) | | getYunoDashboard() | getDashboard() | | destroyYunoDashboard() | destroyDashboard() | | YunoDashboard (class) | Dashboard | | YunoDashboardConfig (type) | DashboardConfig |

- import { initYunoDashboard, getYunoDashboard, destroyYunoDashboard } from "@yuno-payments/dashboard-embed-sdk";
- import type { YunoDashboardConfig } from "@yuno-payments/dashboard-embed-sdk";
+ import { initDashboard, getDashboard, destroyDashboard } from "@yuno-payments/dashboard-embed-sdk";
+ import type { DashboardConfig } from "@yuno-payments/dashboard-embed-sdk";

- const sdk = initYunoDashboard(config);
+ const sdk = initDashboard(config);

Behavior, config options, methods, callbacks, and the PostMessage host/iframe contract are unchanged — this release renames the API surface only.

Development

npm install
npm run build      # Build with tsup
npm run dev        # Watch mode
npm run type-check # TypeScript check