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

tanship-cli

v0.0.2

Published

Bootstrap TANSHIP template projects: Cloudflare D1/R2 resources, identity rewrite, .env generation, migrations, first production deploy, and a private GitHub repo.

Readme

tanship-cli

npx tanship-cli create — one guided run that turns a fresh TANSHIP template clone into a deployed, privately-owned project:

  1. preflight toolcheck (node ≥22, pnpm, git, gh auth, Cloudflare credentials)
  2. guided questionnaire + single overview confirm
  3. clone TANSHIP-app/tanship_template_1 (remote renamed to upstream)
  4. pnpm install + cloudflare.vars.json {} placeholder (fresh-clone guard)
  5. Cloudflare D1 + R2 creation with read-back (reuses existing names; a globally-taken R2 name forces a rename)
  6. identity rewrite across 13 files + residue scan (must be 0 matches)
  7. .env generation (3 generated secrets, pasted keys, neutral placeholders for inactive providers / Turnstile / Chatwoot) + pnpm cloudflare:sync-env
  8. D1 migrations (--local, then --remote) — the command that never exits is handled: completion markers, quiet-window settle, SIGTERM/SIGKILL, exit-0-without-marker is treated as failure
  9. first pnpm deploy:production + origin health poll
  10. commit + private GitHub repo (gh repo create --private --source . --remote origin --push)
  11. final report (names only — secret values are never echoed)

Requirements

  • Node ≥22, pnpm, git
  • GitHub CLI authenticated (gh auth login) with access to the private template repo
  • Shell exports:
    • CLOUDFLARE_ACCOUNT_ID
    • CLOUDFLARE_API_TOKEN (template: “Edit Cloudflare Workers”)

Every spawned cf command gets these variables injected explicitly — cf does not read .env by itself. The token intentionally lives only in the shell + .env (never written by the CLI into state).

Usage

npx tanship-cli create                     # interactive from scratch
npx tanship-cli create --name my-app       # pre-fill the project name
npx tanship-cli create --dry-run           # Steps 0–1 only, zero creation
npx tanship-cli create --resume            # continue an interrupted run

Progress lives in <project>/.tanship/state.json — non-secret answers and created-resource ids only. Pasted credentials go into <project>/.env (gitignored) and are never persisted to state.

Replayable automation mode (--answers)

For E2E reproducibility (“one command, from zero to deployed, rerunnable”):

npx tanship-cli create --answers ./e2e-answers.json

Automation replays still pause at the overview confirm (one Enter / one expect send) before anything is created — a human stays on the trigger. The JSON file shape (validated strictly):

{
  "answers": {
    "projectName": "acme",      // also dir, worker, db, repo defaults
    "dirName": "acme",
    "workerName": "acme",
    "dbName": "acme",
    "bucketName": "acme-assets",
    "ghOwner": "acme-org",
    "repoName": "acme",
    "provider": "creem",         // creem | stripe | paypal
    "googleEnabled": false,
    "contactToEmail": "[email protected]",
    "resendFromEmail": "Acme <[email protected]>"
  },
  "providerInput": { /* every key for the chosen provider */ },
  "resendApiKey": "re_...",
  "googleInput": { "GOOGLE_CLIENT_ID": "", "GOOGLE_CLIENT_SECRET": "" }
}

The overview confirm step stays interactive on purpose: automation replays are explicitly confirmed by a human (or expect) before anything is created.

Contract points (template ↔ CLI)

Changing any of these in the template means a CLI release:

  • Template repo: TANSHIP-app/tanship_template_1 (gh repo clone, HTTPS)
  • Script names: cloudflare:sync-env, deploy:check, deploy:production, db:migrate:local, db:migrate:remote, build
  • Env names (full list from .env.example):
    • public: VITE_*, PAYMENT_PROVIDER, CREEM_MODE, CREEM_PRODUCT_*_ID, STRIPE_PRICE_*_ID, PAYPAL_MODE, PAYPAL_CLIENT_ID, PAYPAL_WEBHOOK_ID, PAYPAL_PLAN_*_ID, GOOGLE_CLIENT_ID, VITE_GOOGLE_CLIENT_ID, RESEND_FROM_EMAIL, CONTACT_TO_EMAIL, VITE_TURNSTILE_SITE_KEY, VITE_ENABLE_TWITTER_TESTIMONIALS, VITE_TESTIMONIAL_TWEET_IDS, ADMIN_EMAILS
    • secrets: BETTER_AUTH_SECRET, NEWSLETTER_TOKEN_SECRET, CONTACT_FORM_SECRET, TURNSTILE_SECRET_KEY, RESEND_API_KEY, GOOGLE_CLIENT_SECRET, CREEM_API_KEY, CREEM_WEBHOOK_SECRET, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, PAYPAL_CLIENT_SECRET
    • tooling: CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_DATABASE_ID
  • Config structure: cloudflare.vars.json (local-only, imported by cloudflare.config.ts), D1 binding under worker.env.DB (name, id), R2 binding ASSETS_R2 (name)
  • Identity rewrite checklist (plan §3 Step 5 — 13 files):

| # | File | Rewritten | |---|------|-----------| | 1 | cloudflare.config.ts | worker name, DB name+id, R2 bucket | | 2 | scripts/cloudflare-env.mjs | PRODUCTION_BASE_URL | | 3 | scripts/build-cloudflare.mjs | PRODUCTION_BASE_URL | | 4 | scripts/deploy-check.mjs | worker/db name constants, db id constant | | 5 | package.json | db:migrate:* D1 ids | | 6 | .env | generated in Step 6 — not rewritten in Step 5 | | 7 | src/tests/cloudflare-build.test.ts | origin assertions | | 8 | src/tests/cloudflare-env-sync.test.ts | origin fixture | | 9 | src/tests/deploy-preflight.test.ts | worker/db name + id fixtures | | 10 | README.md | worker/db name, origin host, tail name | | 11 | src/server/AGENTS.md | bucket example, tail name | | 12 | src/tests/analytics-host.test.tsx, src/tests/auth-handler-identity.test.ts | origin fixtures | | 13 | scripts/deploy-production.mjs | secrets tmpdir prefix |

Residue scan patterns are precise literals: template worker name, template D1 id, template workers.dev origin host. Bare account handles are deliberately not scanned (they would mis-hit the dependency name @pang3fan-creator/analytics-kit).

Development

pnpm install
pnpm exec tsc --noEmit          # typecheck
pnpm build                      # emit dist/
pnpm exec vitest run test/<name>.test.ts   # targeted suites

Test suites: identity (rewrite engine + residue scan + resume mode), migrate (hang → marker → SIGTERM handling), envfile (rendering + placeholder rules), state (no secrets persisted), proc (credential injection), resources (D1/R2 parse + reuse logic + subdomain), preflight (Step 0 gates), report (names-only guarantee).

E2E (stays out of unit runs): real Cloudflare resources, strictly serial, one round-trip per round; artifacts under test/e2e/.