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

@meister1/app-bridge

v0.2.2

Published

Host↔app bridge contract and runtime for embedding MX apps into a MeisterSystems host (shadow-DOM custom elements, token relay, capability gating).

Downloads

539

Readme

@msys/app-bridge

The shared contract and runtime for embedding first-party MX apps into Meister Systems via shadow DOM. One rule: an MX app is a custom element that renders into its own shadow root and talks to whoever mounted it through a single injected MsysHost object. The app must not care whether that host is Meister Systems, the mx-shell dev shell, or a test harness.

Entry points

| Import | Contents | Consumers | | --------------------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | | @msys/app-bridge/contract | MsysHost, MsysContext, MsEntityRef, AppManifest + Zod schema, AppLoadError, Result — types + zod only | everyone | | @msys/app-bridge/app | MsAppElement, dual-mode auth session, React hooks, in-shadow portals, axios interceptors | MX apps | | @msys/app-bridge/host | loadApp, mountApp, createMsysHost | ms-platform, dev shell | | @msys/app-bridge/vite | createAppConfig — single-file IIFE bundle + mx-app.manifest.json | app build configs (monorepo-internal, not published) |

Private package scope. The published package ships only ./contract, ./app, and ./host (bundled to dist/). The ./vite build helper and the examples/ at the end of this file are monorepo-internal and excluded. A host integrator needs only ./host ("Hosting an app" below); the app-authoring sections are for building apps inside this repo.

Installing it outside this repo

The package is published to the private AWS CodeArtifact registry (domain msys, repository msys-npm) — the same registry the services already resolve @msys packages from. It is never published to npmjs.

npm run npm:login   # aws codeartifact login --namespace=@msys --tool npm \
                    #   --repository msys-npm --domain msys --domain-owner 579466223539 --region eu-central-1
npm i @msys/app-bridge

dist/ is self-contained: @mx/auth and @msys/process-shared are bundled in at build time (build.mjs), because both are private, unpublished workspace packages that do not exist off-workspace — @msys/process-shared has no build at all. Only zod (a real dependency) and the optional peers react, react-dom, axios, oidc-client-ts are resolved from the consumer's own tree. scripts/verify-package.mjs fails the build if that ever stops being true; releases run it before publishing.

Versions in CodeArtifact are immutable — bump version in packages/app-bridge/package.json before releasing. The package version is independent of MSYS_HOST_CONTRACT_VERSION, which governs host↔app compatibility.

Building an embeddable app (monorepo-internal)

// src/msEntry.tsx — the bundle entry
import { MsAppElement } from "@msys/app-bridge/app";
import styles from "./styles.css?inline";
import { App } from "./App";

class MxMyAppElement extends MsAppElement {
  protected getStyles() {
    return `${super.getStyles()}\n${styles}`;
  }
  protected renderApp() {
    return <App />;
  }
}
customElements.define("mx-my-app", MxMyAppElement);
// vite.ms-entry.config.ts
import { createAppConfig } from "@msys/app-bridge/vite";
export default createAppConfig({
  appRoot: __dirname,
  entry: "src/msEntry.tsx",
  elementTag: "mx-my-app",
  fileName: "mx-my-app",
});

Inside the app, everything comes from hooks:

const host = useHost(); // navigation, environment
const { organisationId, locale } = useMsysContext(); // re-renders on live changes (locale)
const { session, state } = useAuthSession(); // dual-mode: host-backed or standalone OIDC
const portalContainer = usePortalContainer(); // ALL overlays render here

API clients attach auth with attachAuthInterceptors(axiosInstance, session) — this sets Authorization + X-Msys-Graphql-Token on API-origin requests only and owns the 401-refresh-replay loop with a circuit breaker.

Standalone mode (running the app at its own URL with a direct Keycloak login) needs no host: createAuthSession({ standalone: { authority, clientId } }). Mode selection is total — a session created with neither host nor standalone options fails fast; frame sniffing (window.self !== window.top) is banned in this package.

Styles: inline sheet vs. manifest css

Two ways an app's CSS reaches its shadow root, and they compose:

  • getStyles() — the app inlines its sheet as a string (import css from "./x.css?inline"). Works with any host.
  • manifest csscreateAppConfig writes the build's extracted CSS asset into mx-app.manifest.json; loadApp resolves it against the manifest URL (same allow-list and origin rule as js) and mountApp/mountWidget prepend a <link rel="stylesheet"> into the shadow root. The host owns this injection — an app bundle needs no code for it, which is what makes already-shipped bundles styleable. See docs/decisions/2026-07-30-manifest-css-host-injection.md.

Doing both is harmless (identical rules, and the inline sheet stays last so it wins ties).

Widgets

An app's manifest may declare inline widgets — extra custom elements its bundle registers, mounted at code locations via mountWidget (inert navigation; auth/context identical to page mounts). Declare them in createAppConfig({ widgets: [{ name, elementTag }] }); hosts look the element tag up by name from manifest.widgets. First consumer: billing's mx-billing-status / mx-billing-plan-picker / mx-billing-usage-meter inside mx-call-dashboard.

Tabs / multiple surfaces

A host tab bar is just several surfaces mounted side by side — one per container, each in its own shadow root with its own MsysHost. Two ways a host obtains the surfaces to mount:

  • Declared widgets (above) — one manifest lists many widgets; the host mounts each by name via mountWidget.
  • Per-element manifests — one bundle registers several tags, each published as its own <tag>.manifest.json; the host loadApps each manifest URL and mounts by tag.

Several manifests can point at one shared bundle. loadApp injects that bundle only once, but every call returns the requested manifest's own elementTag/css — so mountApp(container, loaded.value.elementTag, host) always mounts the surface you asked for (a manifest tag the bundle never defines settles as a clean timeout). To render one app's internal pages as tabs in a single mount, switch the active view with pushPath and reflect the app's own route changes via reportRouteChange (no remount needed).

Hosting an app

import { createMsysHost, loadApp, mountApp } from "@msys/app-bridge/host";
import { isErr } from "@msys/app-bridge/contract";

const loaded = await loadApp("https://app.example/mx-app.manifest.json");
if (isErr(loaded))
  renderErrorPanel(loaded.error.kind); // network | invalid-manifest | contract-mismatch | origin-mismatch | script-error | timeout
else {
  const { host, updateContext } = createMsysHost({
    getAuthHeaders: async () => ({ authorization: `Bearer ${await getAccessToken()}` }),
    onAuthDead: () => redirectToLogin(),
    context: { organisationId, userId, locale, msysBaseUrl } /* current MsysContext */,
    navigation: { basePath: "/apps/my-app", initialPath: "/" },
    navigate: (target) => router.push(target) /* { path } | MsEntityRef */,
    reportRouteChange: (path) => router.replace(joinBasePath("/apps/my-app", path)),
    environment: { isNative: false, hostVersion: "1.0.0", deployEnv: "prod" },
  });
  const mounted = mountApp(container, loaded.value.elementTag, host);
  if (isErr(mounted)) renderErrorPanel(mounted.error.message);
  // else: locale changes: updateContext({ locale }); org/user changes: remount the element.
}

Host rules: the bundle origin must match the manifest origin (opt-outs via allowedBundleOrigins only); navigate({ path }) values are route paths — join them under basePath, never feed them to window.location; remount the element when organisationId or userId changes.

Shadow-app rules (what an embedded app must never do)

  • No document.body portals. MUI/Radix overlays default to body — always pass usePortalContainer(). Styles do not exist outside your shadow root.
  • No window.history. The host owns the URL. Run a memory router internally, call host.navigation.reportRouteChange(path) on route changes, and start from host.navigation.initialPath.
  • No token persistence. Embedded tokens are memory-only; never write them to storage, attributes, or logs. The session enforces this — don't work around it.
  • Clean up everything in effects: document/window listeners, timers, observers. disconnectedCallback unmounts your React root, but globals you registered survive it.
  • Bundle your own dependencies. The IIFE build ships your React/UI libs; never assume host globals.

Example & harness

examples/hello-app exercises the full contract (context display, entity navigation, authenticated fetch, in-shadow portal), and examples/hello-app/harness is a hand-built host page with a fake token issuer, log sinks, locale switcher, and org-switch remount:

pnpm run example:build   # dist/hello-app.js + mx-app.manifest.json
pnpm run example:serve   # harness on http://localhost:5470