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

@proappstore/sdk

v1.7.0

Published

Browser SDK for paid apps on proappstore.online — subscriptions, license keys, premium modules.

Downloads

2,101

Readme

@proappstore/sdk

Unified SDK for paid apps on proappstore.online. Includes everything from @freeappstore/sdk (auth, kv, counters, rooms, proxy) plus subscription management, license keys, and a per-app SQL database.

Installation

npm i @proappstore/sdk
# or
pnpm add @proappstore/sdk

Usage

import { initPro } from '@proappstore/sdk'

const app = initPro({ appId: 'my-app' })

Options:

| Option | Default | Description | |--------|---------|-------------| | appId | (required) | Your app's unique identifier | | fasApiBase | https://api.freeappstore.online | Free-tier backend URL | | proApiBase | https://api.proappstore.online | Pro-tier backend URL | | dataApiBase | https://data-{appId}.proappstore.online | Per-app data worker URL |

Modules

Auth

GitHub OAuth — shared identity across all FreeAppStore and ProAppStore apps.

await app.auth.init()
app.auth.onChange((user) => console.log(user))
app.auth.signIn()
app.auth.signOut()

KV (Per-user key-value storage)

await app.kv.set('profile', { name: 'Alice' })
const profile = await app.kv.get('profile')
const keys = await app.kv.list({ prefix: 'note:' })
await app.kv.delete('profile')

Counters (Shared atomic counters)

Cross-user counters for votes, views, leaderboards.

await app.counters.increment('views')
await app.counters.decrement('likes')
const all = await app.counters.list()

Rooms (Real-time WebSocket)

const room = app.rooms.join('lobby')
room.send({ text: 'hello' })
room.onMessage((msg) => console.log(msg))
room.onPeers((peers) => console.log(peers))
room.leave()

Proxy (Secret-injecting API proxy)

Call third-party APIs without exposing keys to the client.

const response = await app.proxy.fetch('/openai/chat/completions', {
  method: 'POST',
  body: JSON.stringify({ model: 'gpt-4', messages: [...] }),
})

Database (Per-app SQL)

Each Pro app gets its own D1 SQL database accessed through a dedicated data worker at data-{appId}.proappstore.online.

// Query rows
const { rows } = await app.db.query('SELECT * FROM users WHERE active = ?', [true])

// Execute writes
const { meta } = await app.db.execute('INSERT INTO users (name) VALUES (?)', ['Alice'])
console.log(meta.last_row_id) // auto-increment id

// Batch (transactional)
const results = await app.db.batch([
  { sql: 'INSERT INTO orders (user_id, total) VALUES (?, ?)', params: [1, 99.99] },
  { sql: 'UPDATE users SET order_count = order_count + 1 WHERE id = ?', params: [1] },
])

// List tables
const tables = await app.db.tables()

Subscription (Stripe-powered)

// Check subscription status
const sub = await app.subscription.status()
// Returns: { status, tier, priceId, currentPeriodEnd, cancelAtPeriodEnd } | null

// Open Stripe checkout (navigates away)
await app.subscription.openCheckout({
  priceId: 'price_pro_monthly',
  successUrl: 'https://my-app.proappstore.online/success',
  cancelUrl: 'https://my-app.proappstore.online/',
})

// Open Stripe billing portal (navigates away)
await app.subscription.openPortal('https://my-app.proappstore.online/')

License

Per-app license key validation.

// Get current user's license (requires auth)
const license = await app.license.current()
// Returns: { key, appId, issuedAt, expiresAt } | null

// Validate any key (no auth required)
const valid = await app.license.validate('LIC-ABC-123')

Maps (Geocoding + Embeds)

Address-to-coordinates and map embeds. Powered by OpenStreetMap/Nominatim. No Google API keys needed.

// Geocode an address
const results = await app.maps.geocode('Times Square, New York')
// [{lat: 40.758, lng: -73.985, displayName: "Times Square...", address: {...}}]

// Reverse geocode
const place = await app.maps.reverseGeocode(40.758, -73.985)

// Embed map in iframe
<iframe src={app.maps.embedUrl(40.758, -73.985)} />

// Static tile image
<img src={app.maps.staticUrl(40.758, -73.985)} />

Storage (File Upload)

Upload images, videos, documents. Public files get URLs usable in <img src> without auth.

// Private upload (owner-only access)
await app.storage.upload('docs/resume.pdf', file, 'application/pdf')

// Public upload (anyone can view)
await app.storage.uploadPublic('avatar.jpg', file, 'image/jpeg')
const url = app.storage.publicUrl('avatar.jpg')  // works in <img src>

// List, download, delete
const files = await app.storage.list()
const response = await app.storage.download('docs/resume.pdf')
await app.storage.delete('docs/resume.pdf')

React Hooks (recommended)

Hooks give you full control over your UI while the platform handles auth, subscriptions, and gating. Import from @proappstore/sdk/hooks.

useProAuth

Auth state + actions. The primary way apps interact with platform identity.

import { initPro } from '@proappstore/sdk'
import { useProAuth } from '@proappstore/sdk/hooks'

const app = initPro({ appId: 'my-app' })

function App() {
  const { user, loading, signIn, signOut, deleteAccount } = useProAuth(app)
  if (loading) return <p>Loading...</p>
  if (!user) return <button onClick={signIn}>Sign in with GitHub</button>
  return <p>Welcome, {user.login}! <button onClick={signOut}>Sign out</button></p>
}

useProSubscription

Subscription state + actions. Check if user is subscribed, upgrade, manage billing.

import { useProSubscription } from '@proappstore/sdk/hooks'

function Billing() {
  const { subscription, isPro, loading, upgrade, manageBilling } = useProSubscription(app)
  if (loading) return <p>Loading...</p>
  if (!isPro) return <button onClick={() => upgrade()}>Upgrade to Pro</button>
  return <button onClick={manageBilling}>Manage billing</button>
}

useProGate

Combined auth + subscription gate. Returns a single gate state for easy conditional rendering.

import { initPro } from '@proappstore/sdk'
import { useProGate } from '@proappstore/sdk/hooks'

const app = initPro({ appId: 'my-app' })

function App() {
  const { gate, user, signIn, upgrade } = useProGate(app, { allowFree: true })

  if (gate === 'loading') return <p>Loading...</p>
  if (gate === 'signed-out') return <button onClick={signIn}>Sign in</button>
  if (gate === 'no-subscription') return <button onClick={() => upgrade()}>Upgrade</button>
  return <p>Welcome, {user?.login}!</p>
}

Gate states: 'loading' | 'signed-out' | 'no-subscription' | 'ready'

Pass { allowFree: true } to skip the subscription check (lets free users through).

ProShell Component

A React component that handles auth gates, subscription checks, and renders a platform-level shell with topbar and user menu.

import { initPro } from '@proappstore/sdk'
import { ProShell } from '@proappstore/sdk/shell'

const app = initPro({ appId: 'meetup' })

export default function App() {
  return (
    <ProShell app={app} appName="Meetup">
      <MeetupApp />
    </ProShell>
  )
}

Props:

| Prop | Type | Description | |------|------|-------------| | app | ProAppStore | SDK instance from initPro() | | children | ReactNode | App content (rendered only when gates pass) | | appName | string? | Name shown in the topbar | | allowFree | boolean? | Skip subscription gate (default: false) |

ProShell handles:

  • Auth initialization and sign-in gate
  • Subscription check and upgrade wall (unless allowFree=true)
  • Topbar with avatar, app name, and user menu (sign out, manage billing, delete account)

Per-app SQL Database

Each Pro app is provisioned with a dedicated Cloudflare D1 database fronted by a data worker (data-{appId}.proappstore.online). The SDK's db module provides a typed client for this worker.

The database is per-user isolated at the auth layer — all requests require a valid Bearer token. The data worker validates the token against the FAS auth API before executing queries.

Tables are user-defined (create them via db.execute('CREATE TABLE IF NOT EXISTS ...')). The schema is entirely up to the app developer.

License

MIT.