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

@alter-ai/connect

v0.12.0

Published

JavaScript SDK for Alter Connect - Secure OAuth integration UI

Readme

Alter Connect SDK

A lightweight JavaScript SDK for embedding OAuth integrations into an application. The SDK opens Alter Connect in a desktop popup, uses full-page navigation on mobile-classified devices, and validates completion data before invoking typed callbacks.

Typed OAuth callbacks | Zod-validated payloads | TypeScript included

Quick Start

1. Install

npm install @alter-ai/connect

The package metadata declares Node.js 20.19 or later for npm tooling. Browser execution does not require Node.js. Or use the UMD bundle via CDN:

<script src="https://cdn.jsdelivr.net/npm/@alter-ai/connect@latest/dist/alter-connect.umd.js"></script>

2. Get a Session Token from the Application Backend

The application backend creates a short-lived session token using the Alter SDK:

// Application backend (Node.js example using @alter-ai/alter-sdk)
import { App, CallerType } from "@alter-ai/alter-sdk";

const alterApp = new App({
  apiKey: process.env.ALTER_API_KEY!,
  caller: "my-backend",
  callerType: CallerType.SERVICE,
});

const session = await alterApp.createConnectSession({
  allowedProviders: ["provider-id"],
  allowedOrigin: "https://app.example.com",
  // Optional. When the user approves access, the Connect UI also lets them set
  // their own usage limits on the connection being created — deny rules,
  // human approval, time windows, request quotas, and operation/parameter
  // rules. They can only narrow what they are granting, never widen it, and
  // they can change or remove the limits later in their wallet.
  //
  // This step is enabled by default. To hide it, pass:
  // allowUserPolicyRules: false,
});

const sessionToken = session.sessionToken;

3. Open the Connect UI

import AlterConnect from '@alter-ai/connect';

// Initialize SDK (no API key needed!)
const alterConnect = AlterConnect.create();

const connectButton = document.querySelector('#connect-button');
if (!(connectButton instanceof HTMLButtonElement)) {
  throw new Error('Connect button is missing');
}
connectButton.disabled = true;

// Prefetch before the user interacts. Browsers may block a popup opened only
// after an awaited network request has detached it from the click gesture.
let sessionToken = null;
fetch('/api/alter/session')
  .then(response => response.json())
  .then(({ session_token }) => {
    sessionToken = session_token;
    connectButton.disabled = false;
  })
  .catch(() => {
    console.error('Unable to prepare the Connect session');
  });

connectButton.addEventListener('click', () => {
  if (!sessionToken) return;
  void alterConnect.open({
    token: sessionToken,
    onSuccess: (grants, completion) => {
      console.log('Connected!', grants);
      grants.forEach(grant => console.log(grant.provider, grant.grant_id));
      completion.failedGrants.forEach(failure => {
        console.warn(failure.providerId, failure.reason, failure.message);
      });
    },
    onError: (error) => {
      console.error('Failed:', error);
    },
    onExit: () => {
      console.log('User closed the window');
    }
  });
});

The package launches the hosted OAuth Connect UI and validates desktop popup results before callbacks receive them.

Framework Examples

React, Vue, Angular, Svelte, and vanilla applications use the same pattern:

  1. Create one reusable AlterConnect instance.
  2. Fetch a session token before enabling the Connect button.
  3. Call open() directly in the click handler so popup activation remains attached to the user gesture.
  4. Destroy the instance when the owning component unmounts.

The CDN bundle exposes the same class as window.AlterConnect. CommonJS consumers read the default export with require('@alter-ai/connect').default.

API Reference

AlterConnect.create(config?)

Creates a new SDK instance.

const alterConnect = AlterConnect.create({
  debug: true  // Enable console logging (default: false)
});

| Option | Type | Description | Default | |--------|------|-------------|---------| | debug | boolean | Enable debug logging | false | | baseURL | string | Reserved and unsupported. Passing any value throws. | — |

Note: Visual customization (colors, fonts, logo) is configured via the Developer Portal branding settings. Alter Connect applies the configured branding automatically.


alterConnect.open(options)

Attempts to open Connect. Desktop uses a centered popup window (500×700 px). Mobile-classified devices use full-page navigation, with the limitation documented under Mobile behavior.

await alterConnect.open({
  token: 'sess_abc123...',
  onSuccess: (grants, completion) => { /* ... */ },
  onError: (error) => { /* ... */ },
  onExit: () => { /* ... */ },
  onEvent: (eventName, metadata) => { /* ... */ }
});

| Parameter | Type | Required | Description | |-----------|------|----------|-------------| | token | string | Yes | Short-lived session token created by the application backend with an Alter SDK | | onSuccess | (grants, completion) => void | Yes | Called with the legacy grants array and a typed completion object. completion.failedGrants identifies partial failures. | | onError | (error) => void | No | Called for failures delivered to the browser SDK, such as a blocked popup, a malformed result, or a total usage-limit application failure. Hosted-page-only failures require backend polling. | | onExit | function | No | Called when the user closes the desktop popup; this does not mark the backend session denied | | onEvent | (eventName, metadata) => void | No | Called once with connect_opened after each launch attempt, including a blocked popup |

The promise resolves after the launch attempt, not after user authorization. Missing or invalid token or onSuccess values reject the returned promise with code: "invalid_options". baseURL is not an OpenOptions field: TypeScript rejects it, while plain JavaScript ignores it.

onSuccess, onError, and onExit run through the event emitter's exception guard. If one throws, the SDK logs the exception and does not perform that callback's final state/listener cleanup; callback code should not throw. onEvent is invoked directly, so an exception from it rejects open() after the launch attempt.

Completion data (onSuccess):

For compatibility, the first argument remains an array of Grant objects. The second argument makes partial completion explicit:

interface ConnectCompletion {
  grants: Grant[];
  failedGrants: Array<{
    providerId: string;
    reason: string;
    message: string;
  }>;
}

failedGrants is empty for full success. When at least one provider succeeds, it identifies any providers whose grants were revoked because the selected usage limits could not be applied. Unknown reason strings are preserved.

Error Object (onError):

{
  code: string;
  message: string;
  details?: Record<string, unknown>;
  failedGrants?: Array<{
    providerId: string;
    reason: string;
    message: string;
  }>;
}

A completion with no successful grants and one or more failed grants invokes onError with code: "grant_policy_application_failed" instead of reporting bare success.


alterConnect.close()

Closes an active popup if one exists, stops the current flow handler, removes the current per-open callbacks, resets isOpen(), and emits close.

alterConnect.close();

alterConnect.destroy()

Destroys the SDK instance and cleans up resources.

alterConnect.destroy();

alterConnect.on(event, handler)

Register an event listener. Returns an unsubscribe function.

const unsubscribe = alterConnect.on('success', (grants, completion) => {
  console.log('Connected:', grants);
  console.log('Providers not kept:', completion.failedGrants);
});

// Later: unsubscribe();

Emitted bus events: success, error, exit, close. The success handler receives (grants, completion). Analytics does not emit an event bus event; use the per-open onEvent callback.


alterConnect.off(event, handler)

Removes a previously registered event handler. Pass the same handler reference that was supplied to on().

const handleError = error => console.error(error);
alterConnect.on('error', handleError);
alterConnect.off('error', handleError);

alterConnect.isOpen()

Returns the SDK's open-state flag. It becomes true when a launch starts and returns to false when close() runs. A manually closed popup resets it automatically only when the supplied onExit callback returns normally; if onExit is omitted or throws, call close() explicitly before opening again.

if (alterConnect.isOpen()) {
  console.log('Connect is open');
}

alterConnect.getVersion()

Gets the SDK version.

console.log(alterConnect.getVersion()); // "0.2.0" in package 0.11.0

getVersion() reads a runtime constant embedded in the bundle. It does not currently match the package metadata version.

Mobile behavior

The SDK chooses the flow using user-agent, touch, viewport, and orientation checks:

| Device | Flow | How It Works | |--------|------|-------------| | Desktop | Popup | Opens a centered popup (500x700px) and reports completion through callbacks | | Mobile-classified viewport ≤480 px | Redirect | Navigates the full page to Connect | | Mobile-classified viewport 481–1024 px in portrait | Redirect | Navigates the full page to Connect | | Other devices | Popup | Uses the desktop callback flow |

Redirect completion: full-page navigation destroys the callbacks and listeners registered by the original page. Set returnUrl on the session and Connect navigates back to it with the outcome, which is delivered to the callbacks registered on the instance constructed by the returning page. Callback parameters that do not carry the per-flow value the SDK minted are ignored. Sessions created without a returnUrl, and returns arriving more than five minutes after the flow started, complete through createConnectSession() plus pollConnectSession() on the application backend.

Closing the desktop popup invokes onExit locally but sends no denial transition to the backend. A server-side poll remains pending until its timeout or the session expires.

Security

  • API keys stay on the backend. The frontend only receives a short-lived session token minted by the application backend.
  • No API keys or provider credentials in the browser. The frontend receives only the short-lived Connect session token.

Provider selection

The hosted UI renders the providers permitted by the session. When that provider is not already connected, a one-provider allowlist still renders one available provider. A verified user with an existing healthy connection can use the consent fast path; requestedGrant, when present, controls the sibling grant requested on that connection. Set switchAccount: true while creating the session to force full provider authorization.

Session-controlled behavior

Provider allowlists, per-provider requested scopes, origin binding, user identity, requested sibling grants, delegated agents, onward-delegation permission, delegation scope constraints, account switching, grant-expiry bounds, and policy authoring are session-creation concerns. They are not browser OpenOptions. Grant-expiry bounds add a duration picker to the hosted confirmation screen.

A recovery session from the server SDKs' createConnectSessionForError() is opened like any other session by passing its sessionToken to open(). This browser package exports none of the server-side exception classes, including ReAuthRequiredError, NoDelegatedGrantError, GrantNotFoundError, CredentialRevokedError, or the headless-flow ConnectFlowError family (ConnectDeniedError, ConnectConfigError, ConnectTimeoutError).

With allowUserPolicyRules enabled, the hosted UI can author narrowing deny, human-approval, time-window, quota, and operation/parameter content rules. Human approval occurs on later API calls; Connect only authors the rule. If selected rules fail to apply for some providers, completion.failedGrants reports the partial failure. If no grant remains, onError receives grant_policy_application_failed.

TypeScript Support

Full TypeScript definitions included:

import AlterConnect, {
  type AlterConnectConfig,
  type OpenOptions,
  type ConnectCompletion,
  type ConnectFailedGrant,
  type Provider,
  type Grant,
  type AlterError
} from '@alter-ai/connect';

const alterConnect = AlterConnect.create({ debug: true });

await alterConnect.open({
  token: sessionToken,
  onSuccess: (grants: Grant[], completion: ConnectCompletion) => {
    for (const grant of grants) {
      console.log(grant.grant_id);  // Store this in the application database
      console.log(grant.provider);
      console.log(grant.scopes);
    }
    for (const failure of completion.failedGrants) {
      console.warn(failure.providerId, failure.message);
    }
  },
  onError: (error: AlterError) => {
    console.error(error.code, error.message);
  }
});

The package has one runtime dependency: Zod 4 validates complete cross-window result payloads before callback data is consumed.

Browser Support

The CJS, ESM, and UMD bundles target ES2020 and use modern browser APIs including popup or full-page navigation, postMessage, URL parsing, and sessionStorage. Importing the package is side-effect free, but AlterConnect.create() must run in a browser because it checks redirect state during construction. The repository's product E2E suite exercises Chromium; it does not establish a cross-browser version matrix. The package metadata declares Node.js 20.19 or later for npm tooling; CDN browser use does not require Node.js.

Troubleshooting

Popup Blocked

Problem: Browser blocks the OAuth popup

Solution: Ensure alterConnect.open() is called directly from a user interaction (click event):

// May be blocked: the network request runs after the click.
button.addEventListener('click', async () => {
  const token = await fetchToken();
  void alterConnect.open({ token, onSuccess, onError });
});

// Reliable: fetch before enabling the button.
const token = await fetchToken();
button.disabled = false;

button.addEventListener('click', () => {
  void alterConnect.open({ token, onSuccess, onError });
});

Session Token Expired

Problem: The hosted Connect page reports that the session expired or is invalid

Solution: Session tokens have a 10-minute default lifetime. Create a new session and open its token. An expiry detected while binding or loading the hosted page is rendered there; it is not a reliable browser onError callback.

CORS Errors

Problem: Session creation was attempted from browser code

Solution: Create sessions on the application backend with an Alter server SDK, then return only the short-lived session token to the browser.

Support

License

MIT