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

@appthriveio/sdk

v0.3.0

Published

AppThrive JS SDK — push merchant data + events from your Shopify app to AppThrive. Zero deps. Web Crypto HMAC. Runs in Node 20+, Bun, Deno, Cloudflare Workers, browsers.

Readme

@appthriveio/sdk

Push merchant data, events, and metrics from your Shopify app into AppThrive. Zero dependencies. Web Crypto HMAC. Runs in Node 20+, Bun, Deno, Cloudflare Workers, modern browsers.

Install

npm install @appthriveio/sdk

Quick start (one-liner — recommended)

import { createClient } from '@appthriveio/sdk'

const appthrive = createClient({
  orgId: process.env.APPTHRIVE_ORG_ID!,
  appId: process.env.APPTHRIVE_APP_ID!,
  webhookSecret: process.env.APPTHRIVE_WEBHOOK_SECRET!,
})

// In your existing Shopify OAuth callback:
await appthrive.bootstrap({
  shopDomain,
  accessToken,
  shopifyClientSecret: process.env.SHOPIFY_API_SECRET!, // ⚠️ required for webhook HMAC
})

That single call:
1. Reads the shop's data via Shopify Admin GraphQL
2. Posts owner email / plan / address / etc. to AppThrive
3. Uploads your Shopify Client Secret (encrypted at rest) so AppThrive can verify the HMAC on inbound webhooks
4. Registers 6 Shopify webhooks (shop/update, app/uninstalled, app/scopes_update, app_subscriptions/update,
   app_subscriptions/approaching_capped_amount, app_purchases_one_time/update)

The access token is used in-memory only — never persisted on AppThrive.

⚠️ shopifyClientSecret is the **Client secret** from Partner Dashboard → Apps → <your app> → Configuration →
App credentials. Without it, AppThrive can't verify HMAC on inbound Shopify webhooks and uninstalls/plan
changes won't propagate. Pass once on first bootstrap; re-passing rotates the stored value.

Commerce data (orders, products, customers, carts, checkouts, refunds)

AppThrive returns HTTP 410 Gone for the Shopify commerce topics — they're intentionally NOT in the default
registration set. Apply your own business logic in your existing webhook handlers and push only the merchant-
success-relevant metrics into AppThrive via `track()`:

// inside your own orders/paid handler:
await appthrive.track({
  event: 'order_paid',
  shopId: shopDomain,
  metrics: [
    { name: 'orders_generated', op: 'increment', value: 1 },
    { name: 'gmv_cents', op: 'increment', value: totalPriceCents },
  ],
})

This keeps AppThrive a merchant-success store (rollups + scores), not a commerce data store.

Customising the topic list

Pass webhookTopics to bootstrap() to override the default. To EXTEND rather than replace:

import { createClient, defaultBootstrapTopics } from '@appthriveio/sdk'

await appthrive.bootstrap({
  shopDomain,
  accessToken,
  shopifyClientSecret: process.env.SHOPIFY_API_SECRET!,
  webhookTopics: [...defaultBootstrapTopics, 'inventory_levels/update', 'fulfillments/create'],
})

On-demand re-enrichment (0.2.0+)

When the AppThrive dashboard's **Re-enrich** button is clicked on a merchant, AppThrive can ask your app to
refetch fresh shop data from Shopify Admin GraphQL — without ever holding a per-merchant Shopify token.
Two-line setup:

// 1. Tell the SDK where your handler lives
const appthrive = createClient({
  orgId, appId, webhookSecret,
  enrichmentCallbackUrl: 'https://yourapp.example/appthrive/enrich',
})

// 2. Mount the handler — Next.js example, same shape works in Hono, Bun.serve, Deno, CF Workers
// app/appthrive/enrich/route.ts
export const POST = appthrive.createEnrichmentHandler({
  getAccessToken: async (shopDomain) => {
    const [s] = await shopifySession.findSessionsByShop(shopDomain)
    return s?.accessToken ?? null
  },
})

The SDK auto-registers the URL with AppThrive on the next signed call (via an `X-AppThrive-Enrichment-Url`
header on existing ingest traffic — zero extra round-trips). The handler verifies AppThrive's HMAC, looks up
the merchant's Shopify access token via your callback, fetches `/shop` GraphQL, and forwards the result
through the same `/merchant` ingest endpoint `bootstrap()` uses. Tokens never leave your process.

Skip it if you don't need on-demand re-enrichment — the Re-enrich button gracefully falls back to a
Partner-API sync (limited fields).

For Express, wrap the handler with a `Request` adapter:

const handle = appthrive.createEnrichmentHandler({ getAccessToken })
app.post('/appthrive/enrich', async (req, res) => {
  const webReq = new Request(`http://x${req.originalUrl}`, {
    method: 'POST',
    headers: req.headers as Record<string, string>,
    body: JSON.stringify(req.body),
  })
  const webRes = await handle(webReq)
  res.status(webRes.status)
  webRes.headers.forEach((v, k) => res.setHeader(k, v))
  res.send(await webRes.text())
})

Other methods

- client.upsertMerchant({ shopId, ...fields }) — explicit per-field control
- client.bulkUpsertMerchants([...]) — up to 100 at a time
- client.track({ shopId, eventType, payload }) — send custom events
- client.incrementMetric({ shopId, metric, value }) — push named metric observations
- client.createEnrichmentHandler({ getAccessToken }) — on-demand re-enrichment (see above)

Where do I get my credentials?

Visit /connect-your-app in your AppThrive dashboard. Quick-start tab has copy-paste env vars + a one-click reveal for the webhook secret.

Full API reference

/docs/api — search for the Ingest (HMAC) tag.

License

MIT