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

@vendodev/connect-portal

v0.4.1

Published

React component package for Vendo connections — drop-in connect buttons, cards, and full portal grid.

Readme

@vendodev/connect-portal

CI npm version License: MIT

Embeddable React connection management UI for Vendo-powered apps.

Browser-only. This package has no SSR helpers. All components and hooks rely on browser APIs (window, postMessage, EventSource). If you use Next.js or another SSR framework, render these components client-side only (e.g., "use client" directive or dynamic import with ssr: false).

Requirements

  • React 18+
  • @vendodev/sdk 0.1.0+ (peer dependency)
  • Node 18+ (build/dev only)

Install

npm install @vendodev/connect-portal

Then import the stylesheet once near your app root:

import "@vendodev/connect-portal/styles.css";

Quick start

import { VendoProvider, ConnectPortal } from "@vendodev/connect-portal";
import { createClient } from "@vendodev/sdk";

const client = createClient({ apiKey: "vendo_sk_..." });

export function App() {
  return (
    <VendoProvider client={client}>
      <ConnectPortal />
    </VendoProvider>
  );
}

That renders the full integration grid grouped by category with search. See ConnectPortal props for filtering and layout options.

Just one card

import { VendoProvider, ConnectionCard } from "@vendodev/connect-portal";

export function App() {
  return (
    <VendoProvider client={client}>
      <ConnectionCard slug="telegram" />
    </VendoProvider>
  );
}

<ConnectionCard> reads state from the provider and renders the correct CTA automatically:

| Connection status | Badge | Primary button | |---|---|---| | none / available | — | Connect | | connecting | Connecting… + spinner | Cancel | | pending_setup | Setup incomplete | Continue setup | | connected | ● Connected | Manage | | needs_reauth | ● Reauth needed | Reconnect + Manage | | error | ● Error: reason | Retry + Manage |

Components

<ConnectionCard slug>

Full self-contained card. Optional props:

<ConnectionCard
  slug="telegram"
  compact                          // 80px tall vs 120px default
  returnTo="/dashboard/connections"
  onConnected={(conn) => console.log("connected", conn)}
  onDisconnected={(conn) => console.log("disconnected", conn)}
  customLogo={<MyLogo />}
  className="my-card"
/>

<ConnectButton slug>

Bare CTA — host owns the surrounding layout.

<ConnectButton
  slug="telegram"
  returnTo="/dashboard"
  onConnected={(id) => console.log("connection id:", id)}
  onError={(err) => console.error(err)}
>
  Connect Telegram
</ConnectButton>

<ManageButton slug>

Opens the hosted management dashboard in a popup.

<ManageButton slug="telegram" returnTo="/dashboard" />

Headless hooks

const { connect, disconnect } = useConnect();

// Opens a popup; falls back to full-page redirect if popup is blocked.
const result = await connect("telegram", { returnTo: "/dashboard" });
// result.status: "connected" | "cancelled" | "timeout" | "redirect_initiated"

// disconnect requires client.disconnect() to be implemented.
await disconnect("conn_id_123");

disconnect support

useConnect.disconnect(connectionId) requires the client to implement a disconnect method:

interface ConnectPortalClient {
  disconnect?(connectionId: string): Promise<void>;
  // ...
}

If disconnect is not implemented on the client, disconnect() throws a clear error.

Popup flow

  1. connect(slug) calls window.open(connectUrl, ...).
  2. If window.open returns null (popup blocked), falls back to window.location.assign(url) and returns { status: "redirect_initiated" }.
  3. The popup runs the OAuth dance on vendo.run. On completion it posts:
    window.opener.postMessage({ type: "vendo:connection-completed", slug, connectionId }, origin)
  4. The bridge validates origin + slug strictly before resolving.

<ConnectPortal> — full grid embed

Drop in the full integration grid with one component:

<VendoProvider client={vendo}>
  <ConnectPortal />
</VendoProvider>

Optional props:

<ConnectPortal
  categories={["messaging", "ai"]}   // only show these groups
  showSearch={false}                  // hide search input (default: true)
  layout="list"                       // "grid" (default) or "list"
  returnTo="/dashboard/connections"
  onConnected={(conn) => console.log("connected", conn)}
  onDisconnected={(conn) => console.log("disconnected", conn)}
/>

Behavior:

  • Groups integrations by category with a section header per group.
  • Within each group: non-available connections sort above available ones, then featured-first, then alphabetically.
  • Search filters across slug, name, and description with a 150ms debounce.
  • Empty search shows a friendly message with a Clear button.
  • layout="grid" uses responsive CSS grid (minmax(280px, 1fr)); layout="list" uses compact single-column.
  • Single column forced below 600px regardless of layout.

Styling

All class names use vendo-connect-* / vendo-portal__* BEM prefix. Override any design token via CSS custom properties:

:root {
  --vendo-color-brand: #6B4FFF;
  --vendo-color-bg: white;
  --vendo-color-bg-muted: #f8f8fa;
  --vendo-color-border: #e5e5ea;
  --vendo-color-text: #1a1a1a;
  --vendo-color-text-muted: #666;
  --vendo-color-success: #10b981;
  --vendo-color-warning: #f59e0b;
  --vendo-color-error: #ef4444;
  --vendo-radius: 12px;
  --vendo-radius-sm: 8px;
  --vendo-font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  --vendo-card-padding: 16px;
  --vendo-card-padding-compact: 12px;
  --vendo-spacing-xs: 4px;
  --vendo-spacing-sm: 8px;
  --vendo-spacing-md: 12px;
  --vendo-spacing-lg: 16px;
  --vendo-shadow-sm: 0 1px 2px rgba(0,0,0,0.04);
}

Testing helpers

The package ships a /testing sub-entry with mock utilities for unit tests. These are never included in production bundles.

import { MockClient, MockSseTransport } from "@vendodev/connect-portal/testing";

CI

CI runs on every push and pull request via .github/workflows/ci.yml (lint, test, build, size gate, Storybook smoke). A tagged release (v*) triggers an additional publish job that runs npm publish --access public. Requires an NPM_TOKEN secret configured at repo settings.