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

@everystack/cli

v0.4.2

Published

CLI and OTA updates for Expo apps on everystack

Readme

@everystack/cli

CLI and OTA updates for Expo apps on everystack. Database management (db:migrate, db:seed), OTA publishing, Expo Updates protocol (v0/v1), pluggable storage, RSA-SHA256 code signing, and channels.

Install

pnpm add @everystack/cli drizzle-orm structured-headers

For S3 storage:

pnpm add @aws-sdk/client-s3

Entry Points

| Import | Description | |--------|-------------| | @everystack/cli | Server: handler, storage adapters | | @everystack/cli/client | Client: UpdatesProvider, AppStateUpdateProvider | | @everystack/cli/schema | Drizzle tables (channels, releases, assets) |

Server: Handler

Creates a Web Standard handler implementing the Expo Updates manifest protocol:

import { createUpdatesHandler, createStorage } from '@everystack/cli';
import { db } from './db';

const storage = createStorage({
  type: 'filesystem',
  directory: './updates',
});

const handler = createUpdatesHandler({
  db,
  storage,
  baseUrl: 'https://myapp.com',
  basePath: '/api/updates',
  defaultChannel: 'production',
  auth: {
    verifyToken: async (token) => verifyJWT(token),
  },
  privateKey: process.env.CODE_SIGNING_PRIVATE_KEY, // PEM for RSA-SHA256
});

Mounting

// app/api/updates/[...path]+api.ts
export function GET(request: Request) { return handler(request); }
export function POST(request: Request) { return handler(request); }

Handler Endpoints

The handler serves:

  • Manifest requests — Expo Updates protocol v0/v1 manifest responses with multipart/mixed format
  • Asset downloads — Binary assets from your configured storage
  • Publish endpoint — Authenticated upload of new releases (used by the CLI)

Handler Options

interface UpdatesHandlerOptions {
  db: DrizzleDb;                // Drizzle instance
  storage: StorageAdapter;      // Filesystem or S3
  baseUrl: string;              // Public URL (for asset references in manifests)
  basePath?: string;            // URL prefix to strip
  auth?: {
    verifyToken: (token: string) => Promise<Record<string, unknown> | null>;
  };
  privateKey?: string;          // PEM for RSA-SHA256 code signing
  defaultChannel?: string;      // Default channel (default: 'production')
}

Storage Adapters

Filesystem

import { createStorage } from '@everystack/cli';

const storage = createStorage({
  type: 'filesystem',
  directory: './updates',
});

S3

const storage = createStorage({
  type: 's3',
  bucket: 'my-updates-bucket',
  region: 'us-east-1',
  endpoint: 'https://s3.us-east-1.amazonaws.com', // Optional
});

The S3 client is bounded by default so a single call can never hang: a connect timeout (3s), a per-request timeout (10s), bounded retries (3 attempts), and a capped list() (paged with a page backstop). This matters most for the observability rollup — an unbounded client under an S3 503 storm blocks until the Lambda's own timeout, and each hung invocation pins a concurrency slot, so it can drain the account pool and starve the app it observes.

Tune per-field via options, or override the timeouts/retries by env var without a code change (env is handy for turning a knob on a live Lambda):

const storage = createStorage({
  type: 's3',
  bucket: 'my-updates-bucket',
  region: 'us-east-1',
  options: {
    connectionTimeoutMs: 3000, // env: EVERYSTACK_S3_CONNECT_TIMEOUT_MS
    requestTimeoutMs: 10000,    // env: EVERYSTACK_S3_REQUEST_TIMEOUT_MS
    maxAttempts: 3,             // env: EVERYSTACK_S3_MAX_ATTEMPTS
    listMaxKeys: 1000,          // keys per ListObjectsV2 page
    listMaxPages: 1000,         // hard backstop before list() truncates (warns)
  },
});

Precedence per field: explicit options > env var > default.

Custom Adapter

Implement the StorageAdapter interface:

interface StorageAdapter {
  put(key: string, data: Buffer | Uint8Array, contentType?: string): Promise<void>;
  get(key: string): Promise<{ data: Buffer; contentType: string } | null>;
  exists(key: string): Promise<boolean>;
  list(prefix: string): Promise<string[]>;
  delete(key: string): Promise<void>;
}

CLI

Publish updates, manage certificates, and channels from the command line.

Publish an Update

everystack update \
  --channel production \
  --message "Fix login bug" \
  --platform ios          # ios | android | web | all (default: all)

This bundles your app, uploads assets to storage, creates a release record, and signs the manifest.

Media assets configured in app.config.js under extra.everystack.assets are fingerprinted with 8-char content hashes (e.g., logo.pnglogo-a1b2c3d4.png) and synced to the media bucket. A media-manifest.json mapping logical names to fingerprinted paths is written to the bucket root. Stale fingerprinted files from previous deploys are pruned automatically.

// app.config.js
extra: {
  everystack: {
    assets: [
      { from: 'server/email/assets', to: 'email/', include: '*.{png,jpg}' },
      { from: 'assets/og', to: 'og/' },
    ],
  },
}

Use @everystack/server/media to resolve fingerprinted URLs at runtime in SSR, email templates, and workers.

Code Signing

Generate RSA key pair for manifest signing:

everystack certs:generate --output ./certs
# Creates ./certs/private-key.pem and ./certs/certificate.pem

everystack certs:configure --input ./certs --keyid main
# Configures your app to use the generated certificates

Channel Management

everystack channels list
everystack channels create --name staging
everystack channels create --name production

Deploy

everystack deploy --stage dev           # provision infrastructure (wraps `sst deploy`)

A thin wrapper over SST so you never type sst directly. Infrastructure only — migrations and bundle publishing stay explicit (db:migrate, update).

Database Management

These commands invoke your Lambda directly via IAM (no database credentials ever leave AWS):

# Migrations
everystack db:migrate                   # apply Drizzle migrations
everystack db:seed                      # seed (dev only)
everystack db:psql --stage dev [-c SQL] # interactive psql / one-off query via Lambda

# Model-driven schema (@everystack/model)
everystack db:generate                  # diff Models vs the live DB → next migration
everystack db:pull                      # introspect a live DB → field() Models (brownfield on-ramp)
everystack db:reconcile                 # deploy the derived layer (functions/views/matviews/triggers) from the declared descriptors in db/models — no migrations
everystack db:fingerprint               # content-address the live base schema vs the Models — MATCH/MISMATCH
everystack db:sync                      # make the database match your checkout — state + compute, one verb (dev DBs)
everystack db:diff                      # the state edge between two declared states — no DB, CI-pure; reverse = computed rollback
everystack db:plan | db:apply           # mint a reviewable plan against a target, apply it fingerprint-verified at both ends + the fast-forward rule (checkout must descend from the commit declaring the target's state); destructive plans: --confirm + snapshot + approver set when declared
everystack db:check                     # the CI gate per PR: merged declared state composes + artifacts match regeneration (+ ephemeral from-scratch build w/ a scratch PG)
everystack db:approvers                 # declare who can DESTROY: the stage's destructive-approver set (SSM, STS-verified at apply)
everystack db:backfill                  # one-shot data jobs in db/backfills/*.sql — content-hash identity, own per-DB record, never a schema side effect
everystack db:template:refresh          # (re)build the dev template from the declared state + your db:seed script — never a data copy
everystack db:branch                    # per-branch dev DB minted from the template, keyed on the git branch (--list / --prune --confirm)
everystack db:fork                      # fork a deployed stage's DB into a feature stage (backup → presigned hand-off → restore); production is never a target

# Security
everystack db:doctor                    # is the DB least-privilege + RLS-subject?
everystack db:provision                 # create the least-privilege role chain (idempotent)
everystack db:authz:pull|diff|test|owner|report   # the authorization contract + red-team

# Backups (see docs/backups.md)
everystack db:backup | db:backups | db:restore --from <id> --confirm   # logical pg_dump → S3
everystack db:backup:download <id>      # presigned URL to a dump (1h)
everystack db:snapshot | db:snapshots   # physical RDS snapshots

The full command reference — including secrets, logs:*, status, cache:purge, branches, and every flag — is in docs/cli.md.

Interactive Console

everystack console --stage dev          # Connect to deployed database
everystack console --stage dev --sandbox  # Sandbox mode: rolls back after each expression

A REPL connected to your deployed database with db, schema, and eq/and/or helpers in scope. Supports multiline input, persistent history, and tab completion.

Dot commands:

  • .tables / .schema [table] — Inspect database schema
  • .login — Authenticate with email/password (hidden input)
  • .user — Show current auth context
  • .logout — Clear auth context
  • .sandbox — Toggle sandbox mode

After .login, queries run with RLS policies applied via pgSettings. The current_user variable is available in scope. Passwords are redacted from history.

Client: React Native

UpdatesProvider

Wraps your app to check for and apply OTA updates:

import { UpdatesProvider } from '@everystack/cli/client';

function App() {
  return (
    <UpdatesProvider
      url="https://myapp.com/api/updates"
      channel="production"
      checkInterval={60000} // Check every 60 seconds
      onUpdateAvailable={(update) => {
        // Optional: prompt user or auto-apply
        console.log('Update available:', update.message);
      }}
      onUpdateApplied={() => {
        console.log('Update applied, restarting...');
      }}
    >
      <MyApp />
    </UpdatesProvider>
  );
}

AppStateUpdateProvider

Checks for updates when the app returns from background:

import { AppStateUpdateProvider } from '@everystack/cli/client';

function App() {
  return (
    <AppStateUpdateProvider url="https://myapp.com/api/updates" channel="production">
      <MyApp />
    </AppStateUpdateProvider>
  );
}

Schema

Add the updates tables to your Drizzle migrations:

import { channels, releases, assets } from '@everystack/cli/schema';

Tables:

  • channels — Named release channels (production, staging, etc.)
  • releases — Published update bundles with metadata
  • assets — Individual asset files referenced by releases

Expo Updates Protocol

The handler implements the full Expo Updates manifest protocol:

  • Protocol v0: Legacy format for older Expo SDK versions
  • Protocol v1: Modern multipart/mixed response format
  • Code signing: RSA-SHA256 signatures on manifest directives
  • Platform filtering: Serves platform-specific bundles based on request headers
  • Channel routing: Multiple release channels with independent version tracks

How It Works

  1. The Expo app sends a manifest request with platform, runtime version, and current update ID
  2. The handler finds the latest release for the requested channel and platform
  3. If a newer release exists, it returns a signed manifest with asset URLs
  4. The Expo runtime downloads assets and applies the update

Peer Dependencies

| Package | Version | Required | |---------|---------|----------| | drizzle-orm | >=0.30.0 | Yes | | structured-headers | ^1.0.0 | Yes (runtime dep) | | @aws-sdk/client-s3 | >=3.0.0 | For S3 storage | | expo-updates | >=0.25.0 | Client SDK | | react | >=18.0.0 | Client SDK | | react-native | >=18.0.0 | Client SDK |

License

AGPL-3.0-only © Scalable Technology, Inc.

A commercial license is available for organizations that cannot or do not wish to comply with the AGPL-3.0 terms. For commercial licensing, contact [email protected].