AppbrewDevelopers
Concepts

The package ecosystem

The @gauntlet and @app-brew scopes and how versions move.

Appbrew code reaches your app as npm packages under two scopes. Knowing what each scope contains, and how versions move, saves you from the most common upgrade mistakes.

Two scopes

@gauntlet/* is the platform. These packages are the runtime an Appbrew app is built from:

  • @gauntlet/state — the store and hooks (State and data)
  • @gauntlet/types — shared TypeScript types, including BaseBlockProps
  • @gauntlet/ui-builder — the preferred building elements for new blocks
  • @gauntlet/components — the shared component library
  • @gauntlet/block-registry — the componentId → component registry
  • @gauntlet/brewery — the app shell and the app extension host (Extending app behavior)
  • per-integration packages for platform-maintained integrations

@app-brew/* is tooling and partner packages. Developer tooling lives here, and so do partner-built integration packages. When you publish an integration (The integrations model), it ships under this scope.

The private registry

Both scopes install from Appbrew's private registry at npm.appbrew.tech. Your repo's .npmrc points the scopes at it:

@gauntlet:registry=https://npm.appbrew.tech/
@app-brew:registry=https://npm.appbrew.tech/

The same .npmrc carries a read-only auth token for the registry host, committed in your repo, so a plain pnpm install works for everyone on the team and in CI. If installs fail with 401/403 errors on a @gauntlet or @app-brew package, the token line is the first thing to check.

Lockstep versioning

All @gauntlet/* packages version in lockstep: there is one platform version, and every package publishes at it. @gauntlet/state@0.27.0 is built against @gauntlet/types@0.27.0, and no other combination is tested or supported.

Every package also carries its own CHANGELOG.md, with a section for each release, including releases where that package did not change, so you never have to guess whether a missing entry means "nothing changed" or "nobody wrote it down". The consumer-facing summary of each release lives in the changelog.

Never mix @gauntlet versions

Upgrade every @gauntlet entry in package.json to the same version, as a set. A mixed tree — say, state ahead of types — produces type errors at best and subtle runtime breakage at worst.

@app-brew/* splits in two. The Appbrew-published native modules and build tooling are part of the same lockstep and carry the platform version. Upgrade them with the @gauntlet set. Developer tools like the milo CLI, and partner-published integration packages, version independently and declare which platform versions they support.

Packages ship TypeScript source

@gauntlet packages ship TypeScript source, not precompiled bundles. Your app's own bundler (Metro, or Re.Pack if your app uses it) compiles them along with your code. This has practical consequences:

  • There is no separate build output to stay in sync with; what you install is what compiles.
  • Your app's TypeScript and bundler configuration must be able to process the packages, which the app template already sets up.
  • Stack traces and go-to-definition land in real source, which makes platform behavior easy to inspect when debugging.

Upgrading is one atomic change

Bump every @gauntlet dependency in package.json to the new version together, reinstall, and let your bundler recompile everything. Because the packages are source, there are no stale prebuilt artifacts to chase. Native dependencies may still change between platform versions, though, so an upgrade can require a store release.

The step-by-step workflow, including verifying native changes, is at Upgrading.

On this page