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

@alphayard/appkit

v1.7.0

Published

Reusable AppKit SDK client for Boundary and UniApps services

Readme

@alphayard/appkit

Reusable AppKit SDK client for mobile apps, web apps, and service-to-service integrations.

Features

  • hosted or fully client-owned authentication UI
  • typed authentication continuation states for MFA, verification, and required factor enrollment
  • auth token storage helpers
  • branding and Color Studio config helpers
  • localization helpers
  • identity endpoints
  • CMS collection helpers
  • file storage helpers
  • communication helpers for email and SMS
  • organization and invite helpers for admin contexts
  • application registry, Personal/Business purpose, and company-website visibility helpers
  • AI Studio helpers for published apps, flows, and agents
  • emergency contacts, safety alerts, and circle member status/location helpers
  • legal acceptance and developer-document feedback helpers
  • storage listing, metadata, search, quota, and analytics helpers
  • configured social-provider access-token verification

Install

npm install @alphayard/appkit

Standard SDK usage

import { AppKit } from '@alphayard/appkit';

const client = new AppKit({
  clientId: 'client-id',
  domain: 'https://your-appkit-domain.com',
  baseURL: 'https://your-appkit-domain.com',
  storage: 'memory',
});

Authentication UI: hosted or your own

AppKit supports two UI models over the same authentication backend and application policy.

Option A — AppKit hosted UI

import { createHeadlessAppKit } from '@alphayard/appkit/headless-auth';

const appkit = createHeadlessAppKit({
  clientId: 'public-client-id',
  appId: 'application-uuid',
  domain: 'https://auth.example.com',
});

window.location.assign(appkit.auth.getHostedLoginUrl({
  redirect: '/oauth/authorize?client_id=public-client-id',
  locale: 'en',
  mode: 'login',
}));

Option B — render your own UI

Use the headless entrypoint when your web/mobile application owns all login screens. It returns authentication continuations as data instead of forcing AppKit UI.

import { createHeadlessAppKit } from '@alphayard/appkit/headless-auth';

const appkit = createHeadlessAppKit({
  clientId: 'public-client-id',
  appId: 'application-uuid',
  domain: 'https://auth.example.com',
  // Use your secure platform storage adapter in mobile apps.
  storage: 'memory',
});

const result = await appkit.auth.loginWithCredentials({
  email,
  password,
  rememberMe: true,
});

switch (result.status) {
  case 'authenticated':
    // Token storage has already been updated by the SDK.
    navigateToApp();
    break;

  case 'mfa_required':
    showMfaScreen({
      challengeToken: result.challengeToken,
      channels: result.availableChannels,
    });
    break;

  case 'email_verification_required':
    showEmailVerificationScreen(result.verificationToken);
    break;

  case 'mfa_enrollment_required':
    showRequiredSecuritySetup({
      enrollmentToken: result.enrollmentToken,
      methods: result.enrollmentMethods,
    });
    break;

  default:
    // `complete`, `passwordless_challenge`, and `recovery_codes`
    // are handled by the matching journey that created them.
    break;
}

The client does not decide whether MFA, email verification, signup, legal acceptance, password rules, or factor enrollment are required. Those rules come from the registered application's effective AppKit policy.

Load policy and branding for your UI

const config = await appkit.auth.getConfig();

console.log(config.authPolicy); // password/MFA/signup/legal rules
console.log(config.branding);   // safe public branding
console.log(config.providers);  // production-ready configured methods only

Do not embed an AppKit client secret in a browser or mobile application. Public headless auth uses the registered clientId/appId; confidential client secrets remain server-only.

Passwordless

const requested = await appkit.auth.requestPasswordless({
  method: 'sms-otp',
  identifier: '+66812345678',
  rememberMe: true,
});

if (requested.status === 'passwordless_challenge') {
  const completed = await appkit.auth.verifyPasswordless({
    challengeToken: requested.challengeToken,
    code: otp,
  });
  // `completed` can still be mfa_required / email_verification_required /
  // mfa_enrollment_required. Render the returned continuation.
}

MFA

if (result.status === 'mfa_required') {
  const request = await appkit.auth.requestMfa({
    challengeToken: result.challengeToken,
    channel: 'email',
  });

  const completed = await appkit.auth.verifyMfa({
    challengeToken: result.challengeToken,
    channel: 'email',
    otpChallengeId: request.otpChallengeId || undefined,
    code: otp,
    trustDevice: false,
  });
}

For passkey MFA, requestMfa() returns publicKey options and a passkeyChallengeToken. Your web/native layer performs the WebAuthn ceremony and passes the serialized credential to verifyMfa().

Required TOTP/passkey enrollment

if (result.status === 'mfa_enrollment_required') {
  const setup = await appkit.auth.startMfaEnrollment({
    enrollmentToken: result.enrollmentToken,
    method: 'totp',
  });

  if (setup.method === 'totp') {
    // Show setup.otpauthUri as a QR code or setup.secret as a manual key.
    const verified = await appkit.auth.verifyTotpEnrollment({
      setupToken: setup.setupToken,
      code: authenticatorCode,
    });

    if (verified.status === 'recovery_codes') {
      // Display these once and require the user to save them.
      console.log(verified.backupCodes);
      // Continue using verified.next.
    }
  }
}

Passkeys with client-owned UI

The SDK core intentionally does not call navigator.credentials; it exposes raw WebAuthn options so web and native clients can use their platform-native ceremony.

const options = await appkit.auth.getPasskeyAuthenticationOptions();
const credential = await yourPlatformWebAuthnGet(options.publicKey);

const result = await appkit.auth.verifyPasskeyAuthentication({
  challengeToken: options.challengeToken,
  credential,
  rememberMe: true,
});

For browser deployments on a different site from the AppKit RP domain, WebAuthn RP/origin rules still apply. Use an RP/domain configuration compatible with your deployment or use the AppKit-hosted passkey ceremony; do not relax WebAuthn origin verification.

Password recovery

Password reset requires both the signed reset challenge and its OTP. A reset token is never accepted as the OTP itself.

const reset = await appkit.auth.resetPassword({
  resetToken,
  otp,
  password: newPassword,
});

Browser CORS

When a browser UI calls a different AppKit origin directly, that UI origin must be included in the deployment's CORS_ORIGIN allowlist. Server-to-server and native clients do not depend on browser CORS. A same-origin backend proxy is also supported.

Application registry, purpose, and company website visibility

Purpose is application-level metadata with two values: personal or business. Company website visibility is a separate global boolean and does not change access mode, activation, lifecycle stage, or environment configuration.

import { ApplicationRegistryClient } from '@alphayard/appkit/applications';

const applications = new ApplicationRegistryClient({
  baseURL: 'https://your-appkit-domain.com',
  // Optional for admin metadata reads/writes in service-to-service usage.
  accessToken: process.env.APPKIT_ADMIN_TOKEN,
});

// Normal public registry. Planning-stage applications remain excluded by AppKit.
const registry = await applications.listPublic({
  organizationId: 'organization-id',
});

console.log(registry.applications[0]?.purposeType); // personal | business
console.log(registry.applications[0]?.showOnCompanyWebsite); // boolean

// Recommended for a company website: only return applications enabled for that surface.
const companyWebsiteApps = await applications.listPublic({
  organizationId: 'organization-id',
  companyWebsiteOnly: true,
});

const purpose = await applications.getPurpose('application-id');
await applications.setPurpose('application-id', 'personal');

const visibility = await applications.getCompanyWebsiteVisibility('application-id');
await applications.setCompanyWebsiteVisibility('application-id', true);

In browser admin contexts, cookie authentication is used by default. Legacy applications without an explicit company-website visibility value keep their prior visible behavior; newly created applications default to hidden until enabled.

AI Studio

const { aiConfig, apps } = await client.ai.list();

const events = await client.ai.runFlow({
  flowId: 'flow-id',
  inputVariables: { message: 'Summarize my account' },
});

for await (const event of events) {
  console.log(event.data);
}

Color Studio

const colorConfig = await client.getColorStudioConfig();

const updated = await client.updateColorStudioConfig({
  ...colorConfig.branding,
  tokens: {
    ...colorConfig.branding.tokens,
    activeColorThemeId: 'core-theme',
  },
});

console.log(updated.colorStudio.activeTheme);
await client.updateColorStudioVariable({
  key: 'chat.accent',
  value: '#06B6D4',
});

await client.updateColorStudioGroup({
  groupId: 'chat',
  group: { name: 'Chat', description: 'Conversation colors.' },
});

await client.setActiveColorTheme({ themeId: 'core-theme', makeDefault: true });
await client.duplicateColorTheme({ themeId: 'core-theme', name: 'Spring Theme' });

Color Studio writes go through the admin application API, so the SDK must be configured with appId and an admin-capable authenticated context.

Localized application description

The public application description is registered under application.description in the application's active Localization Studio package. The SDK resolves exact and regional language codes and falls back through the application's configured fallback language:

const thaiDescription = await client.getApplicationDescription('th-TH');

// Equivalent namespaced API:
const description = await client.localization.getApplicationDescription('en');

Application previews

Branding responses include typed desktop and mobile preview galleries:

const branding = await client.branding.getMobileBranding();

console.log(branding.previewImages?.desktop);
console.log(branding.previewImages?.mobile);

Each gallery contains up to 10 uploaded image URLs.

Organizations

Organization helpers use the authenticated admin API, so the SDK needs an admin-capable token.

const { organizations } = await client.organizations.getMine();

const organization = await client.organizations.create({
  name: 'Acme Studio',
  website: 'https://acme.example',
});

await client.organizations.updateProfile(organization.id, {
  logoUrl: '/uploads/acme-logo.png',
  contact: { email: '[email protected]' },
});

const invite = await client.organizations.createInvite({
  organizationId: organization.id,
  role: 'member',
});

Mobile Splash

const splash = await client.getSplashConfig();

console.log(splash?.logoUrl);
console.log(splash?.variants.dark.backgroundImageUrl);

Build tooling can fetch the same unauthenticated config directly:

curl "https://your-appkit-domain.com/api/v1/mobile/splash?app_slug=my-app"