How to release Regium to every JavaScript package registry so anyone can install it with their favourite tool — npm, pnpm, yarn, bun, deno, JSR, GitHub Packages, or via CDN (jsDelivr, esm.sh).
- What gets published
- Prerequisites
- Step 1 — Choose a scope
- Step 2 — Generate an npm token
- Step 3 — Add the token to GitHub
- Step 4 — First release (automated)
- Manual release fallback
- Subsequent releases
- Distribution channels
- Verify a published package
- Troubleshooting
Every package whose package.json has "private": false is published. In Regium that's everything in packages/ (22 packages):
@regium/core @regium/types @regium/utils
@regium/localization @regium/validators @regium/forms
@regium/banking @regium/tax @regium/payroll
@regium/labor @regium/countries @regium/react
@regium/country-in @regium/country-us @regium/country-uk
@regium/country-de @regium/country-fr @regium/country-sg
@regium/country-ae @regium/country-br @regium/country-au
@regium/country-ca
The two apps (@regium/playground, @regium/docs) are marked "private": true and stay local.
You need:
- An npm.com account (signup)
- An npm organization matching the scope (
regium) - A GitHub repository for the project
- Local Node ≥ 18.17 and pnpm ≥ 9 (see
RUNNING.md)
Sign in to https://www.npmjs.com/ → click your avatar → Add Organization:
- Org name:
regium - Visibility: Public (free)
⚠️ Ifregiumis taken, pick another scope (e.g. your handle), and rename the packages:# quick rename across the repo npx replace-in-files-cli --string "@regium/" --replacement "@your-scope/" "package*.json" "**/*.{ts,tsx,md,json}"
- https://www.npmjs.com/ → avatar → Access Tokens → Generate New Token → Granular Access Token.
- Settings:
- Name:
regium-ci - Expiration: 365 days
- Permissions: Read and write
- Packages and scopes: select
@regium
- Name:
- Click Generate Token. Copy it now — npm only shows it once.
In your GitHub repo:
Settings → Secrets and variables → Actions → New repository secret
- Name:
NPM_TOKEN - Value: the token from step 2
The release workflow (.github/workflows/release.yml) reads it automatically.
Optional but recommended: also enable 2FA on npm (
npm profile enable-2fa auth-and-writes) and use an automation token instead of a publish token, so CI can publish without prompting.
The repo is wired with Changesets + GitHub Actions. The flow:
# 1. Push to GitHub
git init
git add .
git commit -m "feat: initial Regium implementation"
git branch -M main
git remote add origin https://github.com/<you>/regium.git
git push -u origin mainWhat happens next:
- The push triggers
release.yml. - Because
.changeset/initial.mdalready exists, GitHub Actions opens a PR titled "chore: release" that bumps every package version and updates changelogs. - Review and merge that PR.
- The merge re-runs the workflow, which now publishes every package to npm with provenance.
- Verify on npm:
If you'd rather publish from your laptop (no CI), you need to be logged in:
npm login # username, password, OTP from your authenticator
npm whoami # verifyThen:
pnpm install
pnpm build
pnpm changeset version # bumps versions based on .changeset/*.md
git add .
git commit -m "chore: release"
pnpm release # turbo run build && changeset publishpnpm release skips packages whose version is already on the registry, so it's safe to re-run if it stops mid-way.
For every change after the first release:
git checkout -b feat/add-country-jp
# ... make changes ...
pnpm changeset # interactive: pick packages + severity
git add .
git commit -m "feat: add Japan country pack"
git push -u origin feat/add-country-jpOpen a PR. When it merges to main, the GitHub Actions workflow opens / updates the Version PR automatically. Merging the Version PR triggers the actual npm publish.
After a successful npm release, your packages are automatically reachable from every major JavaScript ecosystem.
The default. No configuration needed.
npm install @regium/core @regium/country-in
npm install @regium/core @regium/country-in --save-exactReads from npm by default.
pnpm add @regium/core @regium/country-in
pnpm add @regium/core @regium/country-in --save-exactReads from npm by default. Works for both Yarn 1 (Classic) and Yarn 2/3/4 (Berry).
yarn add @regium/core @regium/country-inFor Yarn Berry with PnP, ensure your .yarnrc.yml has:
nodeLinker: node-modules # or pnpReads from npm.
bun add @regium/core @regium/country-inUseful for private mirrors or enterprise distribution.
Add a workflow .github/workflows/publish-gh.yml:
name: Publish to GitHub Packages
on:
workflow_dispatch:
permissions:
contents: read
packages: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
registry-url: https://npm.pkg.github.com
scope: '@regium'
- run: pnpm install --frozen-lockfile
- run: pnpm build
- run: pnpm -r publish --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}In the consumer project, add a .npmrc:
@regium:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}Then:
npm install @regium/coreJSR is the modern registry by the Deno team that supports TypeScript natively.
For each public package add a jsr.json next to package.json:
{
"name": "@regium/core",
"version": "0.1.0",
"exports": "./src/index.ts",
"publish": {
"include": ["src/**/*.ts", "README.md", "LICENSE"]
}
}Then run:
# install the JSR CLI
npm install -g @jsr/cli
# log in
deno publish --token=<your-jsr-token> # or
npx jsr publish# Deno
deno add @regium/core
# Node + npm/pnpm/yarn (JSR provides an npm shim)
npx jsr add @regium/coreimport { createRegium } from "@regium/core";Deno can install directly from JSR (preferred), npm via the npm: specifier, or any HTTPS URL.
// JSR (recommended)
import { createRegium } from "jsr:@regium/core@^0.1";
// npm specifier
import { createRegium } from "npm:@regium/core@^0.1";
// CDN (esm.sh)
import { createRegium } from "https://esm.sh/@regium/core@0.1";Once published to npm, every package is automatically mirrored to all major CDNs. No extra steps needed.
<script type="module">
import { createRegium } from "https://cdn.jsdelivr.net/npm/@regium/core@0.1/+esm";
import india from "https://cdn.jsdelivr.net/npm/@regium/country-in@0.1/+esm";
const r = createRegium({ plugins: [india] });
</script><script type="module">
import { createRegium } from "https://unpkg.com/@regium/core@0.1/dist/index.js";
</script>import { createRegium } from "https://esm.sh/@regium/core@0.1";After releasing, smoke-test from a clean directory:
mkdir /tmp/regium-test && cd /tmp/regium-test
npm init -y
npm install @regium/core @regium/country-in
node -e "const { createRegium } = require('@regium/core'); const india = require('@regium/country-in').default; const r = createRegium({ plugins: [india] }); console.log(r.getCountryConfig('IN').name);"
# → "India"Or with ESM:
node --input-type=module -e "import('@regium/core').then(async ({ createRegium }) => { const india = (await import('@regium/country-in')).default; const r = createRegium({ plugins: [india] }); console.log(r.getCountryConfig('IN').name); });"The package name is taken or scope is set to private. Either:
- Add
"publishConfig": { "access": "public" }(already done in this repo), or - Run
npm publish --access publicfor a manual publish.
Your NPM_TOKEN is missing, expired, or scoped wrong. Regenerate the token (Step 2) and re-add it as a GitHub secret (Step 3).
Run pnpm install locally and commit the updated pnpm-lock.yaml.
Make sure the workflow has id-token: write permission (already set in release.yml) and the runner is GitHub-hosted.
Changesets publishes packages one at a time. If it stops mid-way, just re-run pnpm release — already-published packages are skipped automatically.
You probably renamed in only some files. Search-and-replace one more time:
grep -r "@regium/" --include="package.json" .Every result should match your chosen scope.
npm unpublish @regium/<pkg>@<version> # within 72 hours of publish
npm deprecate @regium/<pkg>@<version> "use 0.1.x instead" # afterwards
⚠️ npm only allows full unpublish within 72 hours of release. After that, deprecate instead.
Once on npm, the ecosystem mirrors automatically:
| Channel | URL pattern |
|---|---|
| npm | https://registry.npmjs.org/@regium/core |
| jsDelivr | https://cdn.jsdelivr.net/npm/@regium/core |
| unpkg | https://unpkg.com/@regium/core |
| esm.sh | https://esm.sh/@regium/core |
| skypack | https://cdn.skypack.dev/@regium/core |
| Deno via npm | npm:@regium/core |
You don't have to do anything for these — they sync from npm automatically.
You're done. Push to main, merge the release PR, and your packages are live worldwide.