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

@nominalso/vibe-host

v0.2.0

Published

Host-side SDK for embedding Nominal Vibe Apps — receives bridge requests over a typed postMessage protocol and dispatches them to Nominal APIs (used by nom-ui).

Readme

@nominalso/vibe-host

Host-side SDK for embedding Nominal Vibe Apps. Used by the Nominal app (nom-ui) to receive requests from an embedded Vibe App over a typed postMessage protocol and dispatch them to Nominal APIs. The host ships a complete, type-checked handler map covering every protocol operation — you supply a clientConfig pointing at the Nominal API, and the SDK wires up and dispatches all operations for you.

For AI agents: you do not pass a handlers map. Provide clientConfig (where the Nominal API lives) and the SDK builds every handler. The iframe-side counterpart is @nominalso/vibe-bridge.

Install

npm install @nominalso/vibe-host

Externalizes only @hey-api/client-fetch; TypeScript types are self-contained.

Quickstart

import { VibeAppHost } from '@nominalso/vibe-host'

const host = new VibeAppHost({
  // Iframe origin(s) allowed to talk to this host — must match exactly.
  trustedOrigins: ['https://my-vibe-app.lovable.app'],
  // This Vibe App's base path in nom-ui (used to sync the browser URL).
  appBasePath: `/${tenant}/${subsidiary}/apps/${slug}`,
  // Context pushed to the iframe on connect.
  getContext: () => ({ tenant, subsidiaryId, subsidiaries, user, lastClosedPeriodSlug }),
  // Points the built-in handler map at the Nominal API (e.g. nom-ui's proxy route).
  clientConfig: { baseUrl: '/api/proxy', stripApiPrefix: true },
})

// Start listening BEFORE the iframe loads, then push context on its `load` event.
const unmount = host.mount()
// once the iframe has loaded: host.pushContextTo(iframe.contentWindow)

API

new VibeAppHost(options)

| Option | Type | Description | | ---------------- | ----------------------- | --------------------------------------------------------------------------------------------------------- | | trustedOrigins | string[] | Iframe origins allowed to talk to this host. Messages from other origins are ignored. | | getContext | () => HostContext | Returns the current context to send to the iframe (hostVersion is injected automatically). | | appBasePath | string | Base URL path of this Vibe App in nom-ui, e.g. /acme/1/apps/fixed-assets. | | clientConfig | VibeApiClientOptions? | Points the built-in handler map at the Nominal API. Defaults to { baseUrl: '', stripApiPrefix: false }. |

VibeApiClientOptions is { baseUrl: string; stripApiPrefix?: boolean }. Set stripApiPrefix: true when routing through a proxy whose convention expects paths without the /api prefix.

mount(iframeWindow?): () => void

Starts listening for bridge messages and sets up browser back/forward sync. Returns a cleanup function. Pass iframeWindow to immediately push context; if omitted, the host answers GET_CONTEXT polls but won't proactively push until you call pushContextTo.

pushContextTo(iframeWindow): void

Pushes the current context to the iframe and stores the window reference for future pushes (e.g. subroute sync). Call once the iframe has loaded if you called mount() with no argument.

Exports

VibeAppHost, and the types VibeAppHostOptions, HostContext, HostHandlers, RequestHandlers, ContextPayload, VibeApiClientOptions.

Mounting inside Next.js (nom-ui)

The iframe is server-rendered, so it may start loading before React hydrates, and cross-origin contentDocument is always null. The robust pattern: mount() (no arg) immediately to start listening, then pushContextTo(iframe.contentWindow) from the iframe's load event.

'use client'
import { useEffect, useRef } from 'react'
import { VibeAppHost } from '@nominalso/vibe-host'

function VibeAppFrame({ host, src }: { host: VibeAppHost; src: string }) {
  const iframeRef = useRef<HTMLIFrameElement>(null)

  useEffect(() => {
    const iframe = iframeRef.current
    if (!iframe) return
    const unmount = host.mount() // listen before the iframe loads
    const onLoad = () => iframe.contentWindow && host.pushContextTo(iframe.contentWindow)
    iframe.addEventListener('load', onLoad)
    return () => {
      iframe.removeEventListener('load', onLoad)
      unmount()
    }
  }, [host])

  return (
    <iframe
      ref={iframeRef}
      src={src}
      sandbox="allow-scripts allow-forms allow-same-origin allow-popups"
    />
  )
}

Calling contentWindow.postMessage while the iframe is still at about:blank throws a DOMException — that's why context is pushed from the load event, not on effect setup.

How it fits together

The iframe side is @nominalso/vibe-bridge. See the repository for the full protocol and architecture.

License

UNLICENSED — proprietary. © Nominal. All rights reserved.