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

@shyguy_studio/shipkit

v0.2.5

Published

Programmatic App Store submission. One YAML, one command.

Readme

@shyguy_studio/shipkit

Programmatic App Store submission. One YAML config, one command to ship.

npm install -g @shyguy_studio/shipkit
shipkit init
shipkit ship

What it does

ShipKit wraps Apple's App Store Connect API into a single CLI. Build, upload, push metadata, and submit (or re-submit) for review — without opening Xcode or App Store Connect. One YAML config replaces Fastlane's 5+ files.

Install

npm install -g @shyguy_studio/shipkit

# Verify
shipkit --version  # 0.2.4

Or use without installing:

npx @shyguy_studio/shipkit@latest init

Quick Start

# 1. Generate config (detects Xcode, XcodeGen, workspaces)
shipkit init

# 2. Edit shipkit.yml — metadata, review notes, etc.

# 3. Full pipeline: build → upload → metadata → submit
shipkit ship

Commands

| Command | What it does | |---------|------| | shipkit init | Interactive setup, generates shipkit.yml | | shipkit auth | Validate API credentials | | shipkit build | Build and archive (xcodebuild) | | shipkit upload | Upload .ipa to App Store Connect | | shipkit metadata push | Push name, subtitle, description, keywords, URLs, categories, review notes | | shipkit metadata pull | Pull current version-level metadata from ASC | | shipkit submit | Submit (or re-submit) for App Store review | | shipkit submissions list | List every reviewSubmission with state | | shipkit submissions cancel <id> | Cancel a stuck non-terminal submission | | shipkit status | Check review status | | shipkit ship | Full pipeline: build → upload → metadata → submit | | shipkit telemetry status/enable/disable | Manage anonymous usage analytics |

Config (shipkit.yml)

version: 1

auth:
  key_id: "${ASC_KEY_ID}"          # Env var interpolation works
  issuer_id: "${ASC_ISSUER_ID}"
  key_path: ./keys/AuthKey.p8

app:
  bundle_id: com.example.myapp
  apple_id: "6761797247"           # Optional, ASC app ID
  team_id: 5UF3Q334K6
  name: "My App"                   # Internal display only (see note below)

build:
  scheme: MyApp
  configuration: Release
  export:
    method: app-store
  version:
    marketing: "1.0.0"
    build: auto                    # Queries ASC, increments latest

metadata:
  default_locale: en-US
  locales:
    en-US:
      # App-level (pushed via appInfoLocalizations)
      name: "My App"                 # 30 char limit
      subtitle: "Short tagline"      # 30 char limit
      privacy_url: "https://..."     # Moved to app-level by Apple

      # Version-level (pushed via appStoreVersionLocalizations)
      description: |
        Multi-line description...    # 4000 char limit
      keywords: "kw1,kw2,kw3"        # 100 char limit, no spaces after commas
      promotional_text: "..."        # 170 char limit
      marketing_url: "https://..."
      support_url: "https://..."
      whats_new: "Bug fixes."        # 4000 char limit

  # Optional categories
  categories:
    primary: "REFERENCE"
    secondary: "BOOKS"

  # Optional App Review Information
  review:
    first_name: "Jane"
    last_name: "Doe"
    email: "[email protected]"
    notes: |
      Notes for the App Reviewer. Explain
      what changed, provide demo credentials, etc.

release:
  type: manual                     # manual | automatic | phased

hooks:
  pre_build:
    - "xcodegen generate"

SDK

Every operation is available as a TypeScript import:

import { ShipKit } from "@shyguy_studio/shipkit";

const kit = new ShipKit({
  keyId: process.env.ASC_KEY_ID!,
  issuerId: process.env.ASC_ISSUER_ID!,
  keyPath: "./keys/AuthKey.p8",
});

// Modules: apps, appInfo, builds, metadata, submissions
const app = await kit.apps.find("com.example.myapp");
const status = await kit.submissions.getStatus(app.id);

// Push app-level metadata (name, subtitle, privacy URL, categories)
const appInfo = await kit.appInfo.getEditable(app.id);
await kit.appInfo.pushLocale(appInfo!.id, "en-US", {
  name: "My App",
  subtitle: "Short tagline",
  privacyPolicyUrl: "https://example.com/privacy",
});

// List reviewSubmissions (for diagnosing stuck state)
const submissions = await kit.submissions.listForApp(app.id);

// Submit or resubmit — auto-cleans up stuck submissions
await kit.submissions.submit(app.id, versionId);

After a Rejection

ShipKit handles the full rejection-to-resubmission loop. Typical flow:

# 1. Fix the issue (edit shipkit.yml metadata, or fix code + bump build)

# 2. Push the new metadata (version-level + app-level + review notes)
shipkit metadata push

# 3. Inspect stuck submissions (optional)
shipkit submissions list

# 4. Re-submit — auto-releases the version from the rejected/orphaned submission
shipkit submit

Don't forget: Reply in App Store Connect → Resolution Center. That messaging system is not in Apple's public API so it must be done via the web UI.

See shyguy.studio/shipkit/docs#rejection for the full playbook.

Requirements

  • App Store Connect API Key — must be a Team Key, not Individual Key. Generated at ASC → Users and Access → Integrations → Team Keys. Role must be Admin or App Manager.
  • macOS for build and upload commands (metadata, submit, status, submissions work anywhere)
  • Node.js 18+
  • Xcode if you're using shipkit build

Gotchas

These are the non-obvious things we learned building and using ShipKit. Most are documented in detail at shyguy.studio/shipkit/docs#troubleshooting.

  • Team Keys only. Individual keys return 401 on the ASC REST API. No workaround — regenerate as Team Key.
  • privacyPolicyUrl is app-level, not version-level. Apple moved it in 2024. ShipKit v0.2.1+ handles this — leave the field where it is in your YAML.
  • App name lives on appInfoLocalizations.name, NOT on the apps resource. Setting app.name in YAML only affects internal ShipKit logging. Use metadata.locales.<locale>.name to change the App Store display name.
  • POST /appStoreVersionSubmissions is deprecated. ShipKit uses the 3-step reviewSubmissions flow.
  • READY_FOR_SALE is not editable. To push new metadata, bump build.version.marketing — ShipKit will auto-create a new version.
  • sort param is rejected on /apps/{id}/appStoreVersions. If calling the SDK directly, filter client-side instead.
  • Rejected versions hold the version hostage. After a 4.1(a) or 1.5 rejection, the UNRESOLVED_ISSUES reviewSubmission keeps your version "owned" and you can't create a new submission for it. ShipKit v0.2.4+ handles this by DELETEing the old submission's items, which releases the version. Terminal-state submissions (UNRESOLVED_ISSUES, COMPLETE) can't be canceled — Apple rejects PATCH canceled=true — but the item-deletion trick works.
  • READY_FOR_REVIEW orphans from partial submits. If your shipkit submit fails mid-flow, you may leave a half-created submission. v0.2.4+ finds and cancels these automatically on the next attempt.
  • Resolution Center messaging is web-only. Apple hasn't exposed it via the public ASC API. ShipKit puts your metadata.review.notes in the right place for the next reviewer, but if you need to reply to an existing rejection thread, you have to paste it at appstoreconnect.apple.com manually.
  • 4.1(a) "Copycats" doesn't accept disclaimers. If your metadata names a specific IP, franchise, or character — even with a "fan-made, unaffiliated" disclaimer — it gets rejected. Rewrite the metadata to describe the genre without naming the source. Your app's actual content (writing, artwork) can still be inspired by the genre.
  • Run from the project directory. ShipKit's config loader searches cwd for shipkit.yml. If you have multiple projects, cd into each before running.
  • Expo: appVersionSource: "remote" in eas.json does not override hardcoded buildNumber in app.json. Remove the hardcoded value so ShipKit's auto-increment can take over.
  • WAITING_FOR_REVIEW and IN_REVIEW submissions can't be double-submitted. ShipKit will bail with a clear error. Cancel manually in ASC or wait for the current review to complete.

Known Limitations

Works today: metadata push/pull, submit/resubmit with lifecycle cleanup, build, upload, status, per-app insights via the dashboard.

Not yet supported (pre-1.0):

  • Screenshot upload (roadmap: v0.3)
  • In-app purchase management (v0.3)
  • TestFlight distribution (v0.3)
  • Code signing / profile management (v0.4)
  • Resolution Center messaging (not in Apple's public API)
  • App-level metadata pull (only version-level fields come back)

Dashboard

Web dashboard at shyguy.studio/shipkit/dashboard — connect your ASC API key (encrypted storage), view all your apps with Domino's-style pipeline trackers, submission history, and aggregate insights across all ShipKit users (Solo+ plan).

Docs

Full documentation at shyguy.studio/shipkit/docs.

Telemetry

ShipKit collects anonymous usage analytics (command name, success/fail, duration, OS) to improve the tool. No app names, API keys, bundle IDs, or file paths are ever collected.

shipkit telemetry status    # check if enabled
shipkit telemetry disable   # opt out
shipkit telemetry enable    # opt back in

License

MIT — Shy Guy Studio