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

@mavericklaunch/launchkit-sdk

v0.4.0

Published

TypeScript SDK client for the MaverickLaunchKit API

Readme

@mavericklaunch/launchkit-sdk

npm version

Typed TypeScript client for the MaverickLaunchKit API (https://api.mavericklaunch.ai). Auth, billing, tenants, workflows, entitlements, and more — all as typed HTTP calls. The SDK itself is a thin client; the logic runs server-side behind your API key.

Install

npm install @mavericklaunch/launchkit-sdk

Building the frontend too? The UI and component packages pair with the SDK:

npm install @mavericklaunch/launchkit-ui @mavericklaunch/launchkit-components

Get an API key

The SDK is inert without one. Sign in at launchkit.mavericklaunch.ai, create a tenant, and generate an API key. Every request is scoped to that tenant.

Quickstart

import { MaverickLaunch } from '@mavericklaunch/launchkit-sdk';

const client = new MaverickLaunch({
  apiKey: process.env.LAUNCHKIT_API_KEY!,
  // baseUrl defaults to https://api.mavericklaunch.ai
});

const tenant = await client.tenants.getCurrent();
console.log(tenant.name, tenant.plan);

Usage examples

Auth — register users, issue/verify/refresh JWTs, manage API keys:

const user = await client.auth.register({ email: '[email protected]', password: 'Secure123!', name: 'Alice' });
const tokens = await client.auth.getToken('[email protected]', 'Secure123!');
const verified = await client.auth.verifyToken(tokens.token);

Tenants — read and update the current tenant:

const tenant = await client.tenants.getCurrent();
await client.tenants.updateCurrent({ name: 'New Co' });

Users — manage users within a tenant:

const users = await client.users.list();
const created = await client.users.create({ email: '[email protected]', name: 'Bob', role: 'member' });
await client.users.updateStatus(created.id, 'active');

Billing / Payments — subscriptions, invoices, hosted checkout:

const subs = await client.billing.listSubscriptions();
const invoices = await client.billing.listInvoices();

const checkout = await client.payments.checkout({
  endUserId: 'user_123',
  planKey: 'pro',
  returnUrl: 'https://app.example.com/billing/success',
  cancelUrl: 'https://app.example.com/billing/cancel',
});

Entitlements — plans, feature catalog, per-user feature checks:

const plans = await client.entitlements.listPlans();
const { allowed } = await client.entitlements.check('user_123', 'api_calls', 42);

Workflows — create and run automations:

const workflow = await client.workflows.create({ name: 'Welcome email', triggerType: 'user.created' });
const run = await client.workflows.execute(workflow.id, { triggerData: { userId: 'user_123' } });

Settings — tenant-scoped key/value config:

await client.settings.set('theme.primary', '#3b82f6');
const setting = await client.settings.get('theme.primary');

Analytics / Dashboard — track events, read aggregated stats:

await client.analytics.trackEvent({ event: 'signup', properties: { plan: 'pro' } });
const stats = await client.dashboard.getStats({ days: 30 });

Notifications — send and list notifications:

await client.notifications.send({ recipient: '[email protected]', subject: 'Welcome', body: 'Hi Alice!' });
const notifications = await client.notifications.list({ limit: 20 });

File storage — register and manage uploaded files:

const file = await client.fileStorage.upload({ filename: 'invoice.pdf', contentType: 'application/pdf' });
const files = await client.fileStorage.list();

RBAC — create app roles, assign users, manage permissions:

const role = await client.rbac.createRole({ name: 'Editor', slug: 'myapp-editor', permissions: ['posts.create', 'posts.edit'] });
await client.rbac.assignRole(userId, role.id);
const users = await client.rbac.listRoleUsers(role.id);

RBAC Guide

Roles are per-app — create your own for every tier

The platform's built-in roles (admin, owner, superadmin) are is_system: they carry platform permissions (tenant/billing/user management), not your app's permissions, and cannot be edited or deleted. Create your own prefixed role for every tier your app needs — including admin — and assign those, never the built-ins:

const adminRole = await client.rbac.createRole({ name: 'App Admin', slug: 'myapp-admin', permissions: ['listing.create', 'users.manage'] });
await client.rbac.assignRole(userId, adminRole.id);

Assigning a built-in is_system role to an app user leaves them with platform permissions and zero app permissions — a silent under-privilege.

User ids are per (email, environment)

The platform maintains a separate user id per environment for the same email (a dev user and a uat user with the same address are different ids). Any app that syncs ML user ids into a local table MUST key on (email, environment), not email alone. To resolve a user's real id for a given environment deterministically (e.g. when seeding an admin), look them up by email instead of guessing a UUID:

const user = await client.users.findByEmail('[email protected]'); // resolves the current key's environment
if (user) await client.rbac.assignRole(user.id, adminRole.id);

The * wildcard

A role whose permissions contains "*" grants all permissions. getUserPermissions returns * literally, so a naive perms.includes('listing.create') would DENY a superadmin. Either handle * yourself, or call the server-side resolver:

if (await client.rbac.hasPermission(userId, 'listing.create')) { /* allowed (also true for a `*` role) */ }

Multi-role and delegated governance

A user can hold several roles; effective permissions are the union across all of them. One role is the primary (client.rbac.assignRole(userId, roleId) sets it; client.users.list() shows it as the scalar role); add more with client.rbac.addRole(userId, roleId) and inspect with client.rbac.listUserRoles(userId).

Every tenant is provisioned with a superadmin role carrying ["*"]. rbac write endpoints (createRole, updateRole, deleteRole, assignRole, addRole, unassignRole) require an API key with the rbac.write scope (or *) as a baseline gate, regardless of any acting-user delegation below.

Note: client.users.create() no longer auto-assigns a role by default — omit role and the new user gets no role assignment, and therefore zero effective permissions. client.users.list() will still display role: 'member' for that user (the server derives the scalar role via COALESCE(r.slug, 'member') for display purposes) — this is a display default only, not an actual grant. Don't infer permission state from the role string; check permissions directly (e.g. client.rbac.hasPermission(userId, 'listing.create')). Pass an explicit role slug (e.g. 'member') if you want the platform default role actually assigned; unassigning a user's only app role does not fall back to member anymore.

Server-enforced acting-user delegation (X-Acting-User-Token)

By default (Phase 2), rbac writes are authorized purely by the tenant API key's rbac.write scope — any holder of that key can perform any rbac write, regardless of which human triggered it. Phase 3 adds an optional, backward-compatible delegation layer: pass a verified end-user JWT as actingUserToken and the SDK forwards it as the X-Acting-User-Token header. The server then authorizes the write against that user's own effective permissions, not just the API key's scope:

// Backward-compatible — no acting user, authorized purely by the API key's rbac.write scope:
await client.rbac.createRole({ name: 'Editor', slug: 'myapp-editor', permissions: ['posts.create'] });

// Phase 3 delegation — authorized against the acting user's own permissions:
await client.rbac.createRole(
  { name: 'Editor', slug: 'myapp-editor', permissions: ['posts.create'] },
  { actingUserToken: endUserJwt },
);

actingUserToken is accepted as the last (optional) argument on every rbac-write method: createRole(params, opts?), updateRole(slug, params, opts?), deleteRole(slug, opts?), assignRole(userId, roleId, opts?), addRole(userId, roleId, opts?), unassignRole(userId, roleId, opts?). It is a per-call argument, not client-level state — only the calls that need delegation carry it, and it never touches the Authorization header (your tenant API key keeps authorizing the request as always).

Behavior when the token is present:

  • The token must be a valid, unexpired JWT for a user in the same tenant as the request — otherwise the API returns 403 INVALID_ACTING_USER.
  • The acting user's effective permissions (union across their roles, resolved server-side) must include the permission required for that specific write — otherwise 403 FORBIDDEN. The required permission per method is one of: rbac.roles.create, rbac.roles.update, rbac.roles.delete, rbac.users.assign, rbac.users.unassign.
  • Anti-escalation (exact-string subset, not hierarchical): an acting user without * may only create/update a role, or assign a role, whose permissions are all already held by that user. This is an exact-string subset check in v1 — users.* does not expand to cover users.read; if your app wants a wildcard-holder to grant users.read, the wildcard-holder needs users.read (or *) literally in their own permission set.
  • Wildcard-laundering block: an acting user who does not hold * can never create, update, or assign a role whose permissions array contains "*" — even if every other individual permission they'd need is present. Only an actor holding * may grant *.
  • Omitting actingUserToken (or omitting the header) preserves the Phase-2 behavior: the write is authorized by the API key's rbac.write scope alone, with no per-user check — unless the tenant has opted into strict mode (next section).

Per-tenant strict mode (strict_rbac)

A tenant can require every rbac write to carry a valid acting-user token by opting into strict mode:

await client.tenants.update(tenantId, { strict_rbac: true }); // PATCH /v1/tenants/:tenantId

Note: toggling strict_rbac is itself an admin operation — it requires an API key with the tenants.admin scope, not tenant self-service.

With strict_rbac: true, any rbac-write call that omits actingUserToken is rejected with 403 ACTING_USER_REQUIRED — the Phase-2 API-key-only path is no longer accepted for that tenant. Default is false for all existing and new tenants (fully backward-compatible; opt-in only).

Resource reference

Every resource lives on the client as client.<name>:

| Resource | Purpose | |---|---| | auth | Register users, JWT issue/verify/refresh, API key management | | tenants | Read/create/update tenants | | users | List/create users, roles, status, invites | | invitations | Create, list, accept, revoke, resend invitations | | billing | Subscriptions, invoices, Stripe webhook handling | | payments | Hosted checkout, subscription management, payment gateways | | entitlements | Plans, feature catalog, per-user entitlement checks | | licensing | License keys, validation, plan listing | | featureGates | Feature flags: list, create, evaluate, update | | rbac | Roles, role assignment, user permissions | | authz | Fine-grained resource access checks and grants | | workflows | Create, list, run, and delete automations | | jobs | Background job creation, listing, cancellation | | cron | Scheduled job creation, listing, removal | | settings | Tenant-scoped key/value settings | | analytics | Event tracking (single + batch), aggregation | | dashboard | Aggregated dashboard stats (signups, revenue, retention) | | timeSeries | Write/query/aggregate time-series metrics | | usageTracking | Usage records, quotas, quota checks | | notifications | Send and list notifications | | fileStorage | Register, list, fetch, delete stored files | | alerts | List/evaluate/acknowledge/resolve alerts | | escalation | Escalation policies, triggering, on-call lookup | | healthMonitor | Health check status | | retention | Data retention policies and manual runs | | contentModeration | Content checks, moderation queue, review | | rateLimiting | Rate limit configuration | | encryption | Encrypt/decrypt/hash utilities, key generation | | audit | Audit log entries: list and create | | reports | Generate and list reports | | resources | Generic resource graph upsert/delete | | provisioning | Tenant provisioning workflows | | totp | TOTP (2FA) setup, verification, backup codes | | designTokens | Design token get/save, presets | | theme | Theme config, shadow presets | | whiteLabel | White-label theming and generated CSS |

Error handling

All non-2xx responses throw MaverickLaunchError (message, code, HTTP status, optional details):

import { MaverickLaunchError } from '@mavericklaunch/launchkit-sdk';

try {
  await client.tenants.get('missing-id');
} catch (err) {
  if (err instanceof MaverickLaunchError) {
    console.error(err.status, err.code, err.message);
  } else {
    throw err;
  }
}

Requirements

  • Node.js >= 20
  • ESM ("type": "module") — this package ships as ESM only

License

MIT