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

@sitepong/fastnode

v0.2.2

Published

A complete Node.js/TypeScript port of Fastlane — iOS & Android build automation, code signing, and app distribution

Downloads

94

Readme

@sitepong/fastnode

A Node.js/TypeScript toolkit for iOS & Android build automation, code signing, and App Store / TestFlight distribution. Inspired by Fastlane, rewritten in TypeScript with no Ruby dependency.

Install

npm install @sitepong/fastnode

Quick start

As a library

import { AppStoreConnectClient } from "@sitepong/fastnode/client";
import { createCertificate } from "@sitepong/fastnode/cert";

const client = new AppStoreConnectClient({
  keyId: process.env.APP_STORE_CONNECT_API_KEY_ID!,
  issuerId: process.env.APP_STORE_CONNECT_API_KEY_ISSUER_ID!,
  privateKey: process.env.APP_STORE_CONNECT_API_KEY!,
});

const cert = await createCertificate({
  client,
  type: "IOS_DISTRIBUTION",
  outputDir: "./certs",
});

console.log("Issued cert:", cert.certificate.id);

CLI

npx @sitepong/fastnode --help
npx @sitepong/fastnode cert --development
npx @sitepong/fastnode sigh --bundle-id com.example.app

Modules

Each module can be imported separately:

| Module | Purpose | |---|---| | @sitepong/fastnode/client | App Store Connect REST client (JWT) + Apple Developer Portal client (session) | | @sitepong/fastnode/cert | Create, list, revoke signing certificates | | @sitepong/fastnode/sigh | Create and download provisioning profiles | | @sitepong/fastnode/match | Sync certs + profiles across machines via git/S3/local storage | | @sitepong/fastnode/produce | Create App Store Connect apps and bundle IDs, manage capabilities | | @sitepong/fastnode/pilot | Upload builds to TestFlight, manage beta groups, set changelog | | @sitepong/fastnode/deliver | Upload metadata, screenshots, submit to App Review | | @sitepong/fastnode/pem | Create APNS push notification certificates | | @sitepong/fastnode/gym | Build and archive iOS apps via xcodebuild | | @sitepong/fastnode/scan | Run tests via xcodebuild, parse xcresult | | @sitepong/fastnode/snapshot | Capture localized screenshots across devices | | @sitepong/fastnode/trainer | Parse .xcresult / .plist test output → JUnit XML | | @sitepong/fastnode/precheck | Static-analyze App Store metadata against review rules | | @sitepong/fastnode/frameit | Composite screenshots into device frames |

Apple sign-in & one-click setup

fastnode ships a complete "sign in with your Apple Developer Apple ID and provision everything" flow built on top of the same SIRP+2FA login fastlane's spaceship uses. Given an email + password and a 2FA code, it produces a fully-populated credential set without ever touching the Apple Developer Portal UI.

Resumable login (HTTP-friendly 2FA)

import {
  beginLogin,
  submitTwoFactorCode,
  requestSmsCode,
} from "@sitepong/fastnode/client";

// Step 1: SIRP. Returns either a logged-in session or a pending state
// describing the 2FA challenge.
const result = await beginLogin({ email, password });

if (result.kind === "logged_in") {
  // No 2FA on this account — go straight to provisioning
  console.log("Logged in:", result.session.userEmail);
} else {
  // Prompt the user for the 6-digit code Apple pushed to their device
  // (or call requestSmsCode() to fall back to SMS)
  const session = await submitTwoFactorCode({
    pending: result.pending,
    code: "123456",
    source: "device", // or "phone" if the user opted into SMS
  });
}

beginLogin and submitTwoFactorCode are split so the 2FA prompt can be served by an HTTP request loop (e.g. a wizard in a web app), not just an interactive CLI. The legacy loginWithAppleId(...) is still available for one-shot CLI use.

One-click orchestration

import {
  oneClickListTeams,
  oneClickInventory,
  oneClickSetup,
} from "@sitepong/fastnode";

// 1. List developer-portal teams (the alphanumeric teamIds, e.g. "W44274RVJ2")
const teams = await oneClickListTeams(session);

// 2. Inspect the team's current state — bundle ids, dist cert capacity,
// existing push certs, profiles. Used to pre-populate UI / detect the
// 3-distribution-cert limit before trying to provision.
const inventory = await oneClickInventory(session, teams[0].teamId);

// 3. Provision everything in one call
const result = await oneClickSetup({
  session,
  teamId: teams[0].teamId,
  bundleIdentifier: "com.example.myapp",
  appName: "My App",
  options: {
    generateApnsAuthKey: true,    // .p8 APNs auth key (modern push)
    generateAscApiKey: true,      // .p8 ASC API key (TestFlight uploads)
    enableSignInWithApple: true,  // bundle capability via REST API
    enablePushNotifications: true,
    revokeCertIds: [],            // dist cert ids to revoke first
  },
  onProgress: (event) => {
    // structured step events for live UI feedback
    if (event.type === "step_done") console.log("✓", event.label);
  },
});

console.log({
  bundleId: result.bundle_identifier,
  ascKeyId: result.asc_key_id,           // e.g. "MRA2DX6Y6W"
  ascIssuerId: result.asc_issuer_id,     // e.g. "cfadaf94-44ca-4c2c-..."
  ascP8: result.asc_api_key_base64,      // base64 .p8 PEM
  distP12: result.dist_cert_base64,      // base64 PKCS#12
  certPwd: result.cert_password,
  profile: result.provisioning_profile_base64,
  apnsKey: result.additional_certs.find(c => c.cert_type === "apns_key"),
});

Under the hood oneClickSetup runs:

  1. Initialize the developer-portal CSRF context
  2. (Optional) revoke the user's existing distribution cert(s) — Apple caps a team at 3 active distribution certs and 1 per user
  3. Create or reuse the bundle id
  4. Generate an RSA CSR + create a fresh distribution certificate, bundle it with the private key into a PKCS#12 (.p12) using node-forge
  5. Create an App Store provisioning profile, picking a non-colliding date-stamped name (e.g. My App App Store 2026-04-14, … (1), … (2) if the same day collides)
  6. (Optional) create an APNs Authentication Key (.p8) via the developer portal's account/auth/key/v2/create endpoint and download the PEM immediately via account/auth/key/download
  7. (Optional) create an App Store Connect API key (.p8) via the iris admin API at iris/v1/apiKeys, then download the .p8 + issuer id via the JSON-API sparse fieldset trick (?fields[apiKeys]=privateKey&include=provider) reverse-engineered from @expo/apple-utils
  8. (Optional) flip Push Notifications and Sign in with Apple capabilities on the bundle id via the JWT-authenticated REST API (the legacy portal capability endpoint Apple removed)

Failures at any optional step are converted into structured warnings rather than halting the orchestration, so the credential is always saved with whatever could be obtained.

PortalClient and IrisClient

Two lower-level clients are exported alongside the high-level orchestrator for direct use:

import { PortalClient, IrisClient } from "@sitepong/fastnode/client";

// Apple Developer Portal — session-cookie auth, used by the legacy
// account/* endpoints (bundle ids, certs, profiles, devices, key
// management for service-tied auth keys like APNs / DeviceCheck).
const portal = new PortalClient(session, "W44274RVJ2");
await portal.init();
const apnsKey = await portal.createApnsAuthKey("My App APNs");
console.log(apnsKey.pem); // -----BEGIN PRIVATE KEY-----...

// App Store Connect iris admin API — same session cookies, used for
// the modern PUBLIC_API (App Store Connect) keys that show up under
// "Users and Access > Integrations > Keys".
const iris = new IrisClient(session);
const ascKey = await iris.createApiKey({
  nickname: "My App SitePong",
  roles: ["APP_MANAGER"],
  allAppsVisible: true,
  keyType: "PUBLIC_API",
});
const downloaded = await iris.fetchApiKeyPrivateKey(ascKey.id);
console.log(downloaded?.pem, downloaded?.issuerId);

Both clients deliberately stay close to the wire format — they do not hide the JSON-API envelope or Apple's per-section CSRF quirks. The high-level oneClickSetup is where the ergonomics live.

Environment

The library pulls App Store Connect credentials from:

  • APP_STORE_CONNECT_API_KEY_ID — the 10-character key ID
  • APP_STORE_CONNECT_API_KEY_ISSUER_ID — the issuer UUID (omit for individual keys)
  • APP_STORE_CONNECT_API_KEY — the -----BEGIN PRIVATE KEY----- PEM contents

Requirements

  • Node.js ≥ 20
  • macOS for Xcode-driven operations (gym, scan, snapshot, trainer)
  • xcodebuild, security, and codesign on PATH for code-signing flows

License

MIT