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

@bapp/web-component-sdk

v0.1.0

Published

Skeleton SDK for authoring BAPP web components (custom elements) loaded by the host frontend

Readme

@bapp/web-component-sdk

SDK for authoring BAPP web components — custom elements built as ES bundles, loaded by the host frontend via dynamic import, mounted into the page, and wired to the host's live API client (window.bappAutoApi), pub/sub bus (window.bappEvents), and an injected host context.

You write a class that extends BappElement, implement onMount, and register a tag. The host does the loading, context injection, and lifecycle.

Install

npm install @bapp/web-component-sdk
# @bapp/auto-api-client is an optional peer dep — install it for typed API access:
npm install @bapp/auto-api-client

The contract

The host owns the element's lifecycle. The order is always:

  1. Host creates the element and sets el.bappContext BEFORE appending it.
  2. Host appends it → the browser fires connectedCallback → the SDK calls your onMount().
  3. On any later change (route/subPath, theme, tenant…), the host re-assigns bappContext on the same element. Subscribers registered via onContextChange() are notified; the element is not re-mounted.
  4. Host removes the element → disconnectedCallback → the SDK calls your onUnmount() (if defined).

Because context is set before append, this.bappContext and the context getters are already populated inside onMount.

Quick start (vanilla)

import { BappElement } from "@bapp/web-component-sdk";

class MyWidget extends BappElement {
  async onMount() {
    // Initial context snapshot is available here:
    const items = await this.api.list("invoice", { tenantId: this.tenantId });

    this.innerHTML = `
      <h2 style="color:${this.theme?.colorPrimary}">Invoices</h2>
      <pre>${JSON.stringify(items, null, 2)}</pre>`;

    // React to later host context changes (route, theme, …):
    this._unsub = this.onContextChange((ctx) => {
      // re-render based on ctx.subPath / ctx.theme …
    });
  }

  onUnmount() {
    this._unsub?.(); // always unsubscribe to avoid leaks across re-mounts
  }
}

// register() is idempotent and returns the tag name.
export const tagName = MyWidget.register("bapp-my-widget");

API

class BappElement extends HTMLElement

| Member | Description | |---|---| | abstract onMount(): void | Your entry point. Called when the element connects; context is already present. | | onUnmount?(): void | Optional cleanup, called on disconnect. Unsubscribe here. | | static register(tag): string | Define the custom element (idempotent). Returns tag. | | get api: BappAutoApi | Host's live API client (window.bappAutoApi). | | get events: BappEventBus | Host's pub/sub bus (window.bappEvents). | | onContextChange(handler): () => void | Subscribe to context updates. Returns an unsubscribe fn. Does not fire with the initial value — read this.bappContext in onMount for that. A throwing handler is logged and skipped without affecting the others. | | get bappContext: BappHostContext \| undefined | Current host context. | | get tenantId / appSlug / basePath / subPath / theme / navigate | Convenience getters onto bappContext. |

type BappHostContext

type BappHostContext = {
  basePath: string;   // base route the widget is mounted under, e.g. "/conta/app"
  subPath: string;    // segments below basePath, e.g. "clients/42" — drives in-widget routing
  appSlug: string;    // host app this widget belongs to
  tenantId?: string;  // active tenant, when in a tenant context
  theme: { colorPrimary: string; isDark: boolean };
  navigate: (path: string) => void; // ask the host to navigate to an absolute in-app path
};

interface BappEventBus

interface BappEventBus {
  subscribe<T = unknown>(channel: string, handler: (data: T) => void): () => void;
  publish(channel: string, data: unknown): void;
}

The host bridges its realtime (websocket) layer into this bus, so subscribing to a channel yields realtime events published on it. Always call the returned unsubscribe function from onUnmount.

Examples

  • examples/conta-widget/ — a full React widget: mounts React in onMount, mirrors onContextChange into state for in-widget routing, subscribes to the events bus, and runs a background task. The canonical reference for a real-world widget. (Not part of the published package — see the repo.)
  • demo/main.ts — a minimal vanilla widget used by the local dev server.

Build for the host

Build your component project as an ES bundle (with build.manifest: true), upload dist/ to a CDN, and set the App's custom_frontend_url to that base URL. The entry must be named src/main.tsx (or emit a { entry, styles } manifest). The hosting origin must send CORS headers for the host origin.

Local demo

pnpm dev runs a Vite dev server (default http://localhost:5173) serving index.html + demo/main.ts. The demo registers a <bapp-hello> element, stubs window.bappAutoApi, and mounts it with a synthetic bappContext — useful for smoke-testing SDK changes without booting the host. The SDK source is imported from ../src, so HMR picks up edits to BappElement immediately.

Only dist/ (plus README.md, LICENSE, package.json) is published — the demo/, examples/, and root index.html are excluded, per files in package.json.

License

MIT