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

@sylphx/sdk

v0.20.1

Published

Sylphx SDK - State-of-the-art platform SDK with pure functions

Readme

@sylphx/sdk

npm version License: MIT TypeScript docs

Auth, billing, analytics, AI, storage, and more — in one SDK.

📖 Full docs: sylphx.com/docs


Installation

npm install @sylphx/sdk
# or
pnpm add @sylphx/sdk
bun add @sylphx/sdk

Quick Start (Next.js)

1. Environment Variables

Your server and browser connection URLs from your Platform Console. Sylphx deploys inject these automatically; local development can pull the same values into .env.local.

# .env.local
SYLPHX_SECRET_URL=sylphx://sk_dev_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@bold-river-a1b2c3.api.sylphx.com
SYLPHX_URL=sylphx://pk_dev_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@bold-river-a1b2c3.api.sylphx.com
NEXT_PUBLIC_SYLPHX_URL=sylphx://pk_dev_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@bold-river-a1b2c3.api.sylphx.com

That's it. No other config needed.

SYLPHX_SECRET_URL is used only on the server for confidential token exchange and HttpOnly cookie handling. SYLPHX_URL / NEXT_PUBLIC_SYLPHX_URL carry the publishable credential used by browser-safe auth routes such as /auth/login, /auth/oauth-providers, passkeys, and OAuth authorize.

Key formats

  • sylphx://sk_*@<tenant-slug>.api.sylphx.com — Server connection URL (server only, never expose)
  • sylphx://pk_*@<tenant-slug>.api.sylphx.com — Browser connection URL (publishable)

Get both from Console → Your App → API Keys. The hosted BaaS API is always addressed as <tenant-slug>.api.sylphx.com.


2. Middleware

Handles auth routes (/auth/callback, /auth/signout) and route protection automatically.

// middleware.ts
import { createSylphxMiddleware } from '@sylphx/sdk/nextjs'

export default createSylphxMiddleware({
  publicRoutes: ['/', '/about', '/pricing', '/login'],
})

export const config = {
  matcher: ['/((?!_next|.*\\..*).*)', '/'],
}

No manual /api/auth/* routes needed — the middleware handles everything.

Organization context is part of the same contract. Calling the mounted POST /auth/switch-org route stores the active org in SDK-owned HttpOnly cookies; subsequent refresh-token rotations restore the org-scoped access token automatically. If a user belongs to exactly one organization, the middleware auto-scopes the refreshed session without app-specific glue code.


3. Root Layout

Fetch config server-side once, pass to the provider:

// app/layout.tsx
import { getAppConfig } from '@sylphx/sdk/server'
import { SylphxProvider } from '@sylphx/sdk/react'
import { createServerClient } from '@sylphx/sdk'

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)
  const apiUrl = sylphx.baseUrl.replace(/\/v[0-9]+$/, '')

  const config = await getAppConfig({
    secretKey: sylphx.secretKey!,
    appId: process.env.NEXT_PUBLIC_SYLPHX_APP_ID!,
    platformUrl: apiUrl,
  })

  return (
    <html>
      <body>
        <SylphxProvider
          config={config}
          appId={process.env.NEXT_PUBLIC_SYLPHX_APP_ID!}
          platformUrl={apiUrl}
        >
          {children}
        </SylphxProvider>
      </body>
    </html>
  )
}

4. Protect Pages (Server Components)

// app/dashboard/page.tsx
import { currentUser } from '@sylphx/sdk/nextjs'
import { redirect } from 'next/navigation'

export default async function Dashboard() {
  const user = await currentUser()
  if (!user) redirect('/login')

  return <h1>Hello, {user.name}</h1>
}

5. Auth UI (Client Components)

'use client'
import { useUser, SignedIn, SignedOut, UserButton } from '@sylphx/sdk/react'

export default function Header() {
  const { user } = useUser()

  return (
    <header>
      <SignedOut>
        <a href="/login">Sign in</a>
      </SignedOut>
      <SignedIn>
        <span>Hello, {user?.name}</span>
        <UserButton afterSignOutUrl="/" />
      </SignedIn>
    </header>
  )
}

Server-Side

Get Current User

import { auth, currentUser, currentUserId } from '@sylphx/sdk/nextjs'

// Full auth state
const { userId, user, sessionToken } = await auth()

// Just the user object (null if not signed in)
const user = await currentUser()

// Just the user ID
const userId = await currentUserId()

Server API Client

import { createServerClient, getPlans, track } from '@sylphx/sdk'

const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)

// Billing
const plans = await getPlans(sylphx)

// Analytics
await track(sylphx, { event: 'purchase', properties: { amount: 99 } })

CI Runner Profile Helpers

@sylphx/sdk also exports browser-safe CI runner profile helpers for tools that generate GitHub Actions workflows:

import { formatCiRunnerWorkflowRunsOn, getCiRunnerProfileById } from '@sylphx/sdk'

const profile = getCiRunnerProfileById('macos-standard')
const runsOn = formatCiRunnerWorkflowRunsOn(profile)
// [self-hosted, sylphx, macos, standard]

These helpers expose the public workflow label contract only. Applications do not need to know the backing scheduler, cluster, runner group, capacity formula, or placement details.

Sandbox Capability Catalog

Sandbox clients expose the authenticated capability report served by the exec-server:

import { SandboxClient } from '@sylphx/sdk'

const sandbox = await SandboxClient.create(sylphx)
const report = await sandbox.capabilities.get()

const storage = report.capabilities.find((capability) => capability.id === 'workspace-storage')
if (storage?.workspaceStorage?.pressure === 'critical') {
  // Clean caches/artifacts, fork from a clean workspace, or offload heavy work.
}

const mobile = report.capabilities.find((capability) => capability.id === 'mobile-device-test')
if (mobile?.status === 'not-local') {
  // Build artifacts in the workspace or CI, then run device tests through the
  // managed mobile device runtime when available.
}

The report is tenant-neutral and product-level. It tells agents what is local to the sandbox (exec, files, browser, desktop) versus another Platform execution plane (ci-runner, managed-device-runtime) without exposing Kubernetes placement or storage internals.

Prefetch App Config

import {
  getAppConfig,    // All config in one call (recommended)
  getPlans,        // Billing plans
  getFeatureFlags, // Feature flag definitions
  getConsentTypes, // GDPR consent config
} from '@sylphx/sdk/server'
import { createServerClient } from '@sylphx/sdk'

const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)

const config = await getAppConfig({
  secretKey: sylphx.secretKey!,
  appId: process.env.NEXT_PUBLIC_SYLPHX_APP_ID!,
  platformUrl: sylphx.baseUrl.replace(/\/v[0-9]+$/, ''),
})
// config.plans, config.featureFlags, config.oauthProviders, config.consentTypes

Verify Webhooks

import { verifyWebhook } from '@sylphx/sdk/server'

export async function POST(request: Request) {
  const body = await request.text()

  const result = await verifyWebhook({
    payload: body,
    signatureHeader: request.headers.get('x-webhook-signature'),
    secret: process.env.SYLPHX_WEBHOOK_SECRET!,
  })

  if (!result.valid) {
    return new Response('Unauthorized', { status: 401 })
  }

  const { event, data } = result.payload!
  // handle event...
  return Response.json({ received: true })
}

Or use the handler factory:

import { createWebhookHandler } from '@sylphx/sdk/server'

export const POST = createWebhookHandler({
  secret: process.env.SYLPHX_WEBHOOK_SECRET!,
  handlers: {
    'user.created': async (data) => { /* ... */ },
    'subscription.updated': async (data) => { /* ... */ },
  },
})

JWT Verification

import { createServerClient } from '@sylphx/sdk'
import { verifyAccessToken } from '@sylphx/sdk/server'

const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)

const payload = await verifyAccessToken(token, {
  platformUrl: sylphx.baseUrl.replace(/\/v[0-9]+$/, ''),
  audience: process.env.NEXT_PUBLIC_SYLPHX_APP_ID!,
})
// payload.sub, payload.email, payload.role, payload.app_id
// Org-scoped tokens also preserve payload.org_id, payload.org_slug, payload.org_role
// DPoP-bound tokens preserve payload.cnf?.jkt for proof validation.

Always pass audience on a resource server. The server verifier rejects a validly-signed token when its aud claim is for another app or API. Omitting audience is kept only for backward compatibility and skips audience validation. If you authorize organization resources, compare payload.org_id with the addressed resource's org and check payload.org_role before granting access.


Service Accounts (machine-to-machine)

Authenticate as a service account with the OAuth 2.0 client-credentials grant (RFC 6749 §4.4, ADR-2062). Returns a short-lived org-scoped access token carrying org_id / org_slug / org_role claims — verify it on the resource server with verifyAccessToken (above).

import { createServerClient, signInAsService, withToken } from '@sylphx/sdk'

const sylphx = createServerClient(process.env.SYLPHX_SECRET_URL!)

const { token, expiresIn } = await signInAsService(sylphx, {
  clientId: process.env.SYLPHX_SERVICE_CLIENT_ID!,     // `sa_…`, stable across rotations
  clientSecret: process.env.SYLPHX_SERVICE_CLIENT_SECRET!,
  // scope: 'read:foo write:bar',                       // optional scope downgrade
})

const authed = withToken(sylphx, token)

No refresh token is issued — re-mint before expiresIn (seconds) elapses. Secrets support rotation with a grace window, so a rotated secret keeps authenticating running consumers until its deadline. org_role is one of admin / developer / viewer (never owner / super_admin — those stay human-only).


React Hooks

Auth

import { useUser, useAuth } from '@sylphx/sdk/react'

const { user, isLoading, isSignedIn } = useUser()
const { signIn, signUp, signOut, forgotPassword } = useAuth()

await signIn({ email: '[email protected]', password: '...' })
await signOut()

Billing

import { useBilling } from '@sylphx/sdk/react'

const { subscription, isPremium, plans, createCheckout, openPortal } = useBilling()

// Check access
if (!isPremium) return <UpgradePrompt />

// Start checkout
const url = await createCheckout('pro', 'monthly')
window.location.href = url

// Manage subscription
await openPortal()

Analytics

import { useAnalytics } from '@sylphx/sdk/react'

const { track, identify, page } = useAnalytics()

track('button_clicked', { button: 'upgrade' })
identify({ name: 'John', email: '[email protected]' })

Feature Flags

import { useFeatureFlag } from '@sylphx/sdk/react'

const { isEnabled } = useFeatureFlag('new-dashboard')
if (isEnabled) return <NewDashboard />

AI

import { useChat, useCompletion } from '@sylphx/sdk/react'

const { messages, send, isLoading } = useChat({
  model: 'anthropic/claude-3.5-sonnet',
})

await send('What is the meaning of life?')

Storage

import { useStorage } from '@sylphx/sdk/react'

const { upload, uploadAvatar, isUploading, progress } = useStorage()

const url = await upload(file, { path: 'documents/' })
const avatarUrl = await uploadAvatar(imageFile)

More Hooks

import { useConsent }       from '@sylphx/sdk/react' // GDPR consent
import { useFeatureFlags }  from '@sylphx/sdk/react' // All flags at once
import { useNotifications } from '@sylphx/sdk/react' // In-app notifications
import { useReferral }      from '@sylphx/sdk/react' // Referral program
import { useOrganization }  from '@sylphx/sdk/react' // Multi-tenant orgs
import { useTasks }         from '@sylphx/sdk/react' // Background tasks
import { useErrorTracking } from '@sylphx/sdk/react' // Error capture

UI Components

Auth

import { SignIn, SignUp, UserButton, SignedIn, SignedOut } from '@sylphx/sdk/react'

<SignedOut><SignIn mode="embedded" afterSignInUrl="/dashboard" /></SignedOut>
<SignedIn><UserButton afterSignOutUrl="/" /></SignedIn>

Billing

import { PricingTable, CheckoutButton } from '@sylphx/sdk/react'

<PricingTable plans={plans} />
<CheckoutButton planSlug="pro" interval="monthly">Upgrade</CheckoutButton>

Route Protection

import { Protect } from '@sylphx/sdk/react'

<Protect role="admin">
  <AdminPanel />
</Protect>

Pure Functions (Server or Client)

For non-React environments or maximum control:

import { createClient, signIn, track, getPlans } from '@sylphx/sdk'

const config = createClient(process.env.NEXT_PUBLIC_SYLPHX_URL!)

// Auth
const tokens = await signIn(config, { email, password })
const authedConfig = withToken(config, tokens.accessToken)

// Analytics
await track(config, { event: 'purchase', properties: { amount: 99 } })

// Billing
const plans = await getPlans(config)

Entry Points

| Import path | Use for | |---|---| | @sylphx/sdk | Pure functions (server or client, no React) | | @sylphx/sdk/react | React hooks, components, SylphxProvider | | @sylphx/sdk/server | JWT verification, webhook verification, server client | | @sylphx/sdk/nextjs | createSylphxMiddleware, auth(), currentUser() | | @sylphx/sdk/web-analytics | Standalone web-analytics tracker (rrweb + web-vitals) | | @sylphx/sdk/health | Multi-signal health score for the sylphx-health-agent sidecar (ADR-111 Phase B) |

@sylphx/sdk/health — Phase B health score (ADR-111)

Apps register signals (event-loop lag, queue depth, error rate, memory pressure) and the SDK folds them into a continuous score in [0, 1]. The sylphx-health-agent sidecar polls the score and decides liveness / readiness / drain via the three-tier gate. See src/health/README.md for the full guide.

import { sylphxHealth, eventLoopLagSignal, queueDepthSignal } from '@sylphx/sdk/health'

const health = sylphxHealth({
  signals: [
    eventLoopLagSignal({ degradedMs: 5000, deadMs: 30000 }),
    queueDepthSignal({ getter: () => queue.size, fullThreshold: 1000 }),
  ],
})

app.get('/healthz', health.handler())
// Or — Unix-socket transport for the sidecar:
health.serveUnixSocket() // → /var/run/sylphx/health.sock

TypeScript

All types are fully inferred. Import them directly:

import type { User, Plan, Subscription } from '@sylphx/sdk'
import type { AppConfig } from '@sylphx/sdk/server'
import type { AuthResult } from '@sylphx/sdk/nextjs'

Self-Hosting / Custom Deployment

If you're running your own Sylphx Platform deployment, configure the base URL via the CLI:

SYLPHX_API_URL=https://platform.your-domain.com sylphx deploy

Or in the SDK via an explicit custom-domain connection URL:

import { createServerClient } from '@sylphx/sdk'

const config = createServerClient(
  'sylphx://sk_prod_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@acme.api.example.com',
)

Note: hosted Sylphx uses <tenant-slug>.api.sylphx.com. Only use a custom host for self-hosted deployments or a documented legacy migration.


Architecture — contract-first

This SDK is part of the two-plane architecture (ADR-083) with a single source of truth (ADR-084):

  • @sylphx/contract (Effect Schema) drives the types you see here. The types are not hand-written and not separately maintained — add an endpoint to the contract and both this SDK and the sibling @sylphx/management inherit it. Hand-written shims are a bug.
  • Promise surface, Effect-free. Internally @sylphx/contract is Effect Schema, but the published .d.ts is stripped of Effect imports (strip-at-publish CI guard). You get clean Promise<User> / Promise<Plan[]> signatures with zero Effect dependency in your bundle.
  • Standard Schema compliant. Every input schema in the contract exposes the ~standard interface — compatible with Zod, Valibot, ArkType, TypeBox, Effect Schema, and any future Standard-Schema-compliant validator.
  • Agent surface via the CLI. The sylphx CLI is the first-class agent entry point (Claude Code, Cursor, ChatGPT Apps). If you want a custom MCP, compose it on top of @sylphx/management yourself — Sylphx does not ship a standalone stdio MCP binary.

See ADR-084 for the full design rationale.