16. Releases
How you pack and publish @avelon/* from this monorepo. The first public line is a human action. Nothing here publishes on push.
What you publish
These packages are public (private is false) and share one version:
| Package | What it is |
|---|---|
@avelon/core | Frozen contracts, errors, and capability types |
@avelon/orm | Model layer and QueryIR builder |
@avelon/cli | reeve CLI and TUI |
@avelon/next | Next.js adapter |
@avelon/bailiff | Architecture linter |
@avelon/assay | Test harness |
@avelon/supabase | Supabase drivers |
@avelon/postgres | Postgres database driver |
@avelon/resend | Resend mail driver |
@avelon/conformance | Contract suites and fakes |
These stay private. You do not publish them:
| Package | Why |
|---|---|
avelon (repo root) | Workspace root |
@avelon/docs-site | Site builder for this repository |
@avelon/s4 | Integration slice |
@avelon/gazette | Example app under examples/gazette |
TypeScript source, not a compiled dist
The first public line publishes TypeScript source. Package exports point at ./src/index.ts. There is no dist/, no dual CJS/ESM build, and no tsc emit step. That is intentional for this prep, not a stand-in for a later compiled release.
You consume these packages with Bun, which runs TypeScript directly. @avelon/core and @avelon/orm still must not import bun:*. engines.bun is >=1.3.14 because that is the toolchain this repo pins (docs/11-runtime.md). A compiled dist is a later change. Do not advertise Node-native install until that lands.
Version policy
Every publishable package is 0.0.0 until you cut the first public release. That first public version is 0.1.0. You bump every publishable package together. You do not ship @avelon/orm@0.2.0 against @avelon/core@0.1.0.
After you change versions, run bun install so bun.lock matches the on-disk manifests, then dry-run pack. Bun 1.3.14 rewrites workspace:* from the workspace package versions it can see; a stale lockfile is how you publish the wrong number.
You may put a canary on the next or dev tag before you put 0.1.0 on latest. The publish workflow defaults the tag to next so a tired click does not land on latest.
License
The repo root LICENSE is MIT, copyright Ryan Yannelli. The pack and publish scripts copy that file into each publishable package directory for the tarball, then remove the copy if it was not already checked in. You do not maintain ten LICENSE files by hand.
Dry-run pack
From the repo root:
bun run release:pack
That runs scripts/release/pack.ts. For each publishable package it:
1. Copies the root LICENSE into the package if needed. 2. Runs bun pm pack --destination <temp> --ignore-scripts from the package directory. Bun writes <name>-<version>.tgz. It rejects --filename together with --destination. 3. Prints every path in the tarball. 4. Fails if LICENSE or README.md is missing, if src/ is missing, if tests/, fixtures/, tsconfig, or *.test.ts leaked, or if the packed package.json still contains workspace:.
You do not need NPM_TOKEN for this. CI runs the same command.
Workspace protocol
Workspace packages depend on each other with workspace:*. That protocol is invalid on npm.
You do not rewrite package.json on disk. Bun 1.3.14 does it in the tarball:
| Command | What it does to workspace:* |
|---|---|
bun pm pack --destination <dir> --ignore-scripts | Writes @avelon/core as 0.0.0 (the dependency's package.json version) |
bun publish --access public --tag <tag> --ignore-scripts | Same rewrite, then uploads |
workspace:^ becomes ^<version> and workspace:~ becomes ~<version>. This repo only uses workspace:*, which becomes the exact version.
scripts/release/pack.ts reads package/package.json out of each tarball and fails if any workspace: spec remains. scripts/release/publish.ts runs this from each package directory, in dependency order:
bun publish --access public --tag "$RELEASE_TAG" --ignore-scripts
RELEASE_TAG defaults to next.
Publish through workflow_dispatch
.github/workflows/publish.yml is workflow_dispatch only. It does not run on push.
1. Put an npm automation token in the repository secret NPM_TOKEN. Do not commit it. Do not put it in .npmrc. 2. Open the Publish workflow in GitHub Actions and run it. 3. Leave dry_run at true the first time. That only runs bun run release:pack. 4. When you mean it, set dry_run to false and choose a tag (next, dev, or latest). 5. The workflow then runs bun scripts/release/publish.ts with NPM_TOKEN and RELEASE_TAG.
A real publish also needs the packages to exist on npm under the avelon org, and you to be logged in as someone who can publish @avelon/*. The script refuses to run without NPM_TOKEN.
Publish order is core, conformance, bailiff, orm, postgres, resend, next, supabase, assay, cli.
What you never commit
.envor.env.*- A service role key
- A real project ref
NPM_TOKENor any other registry credential
.gitignore already drops .env. The publish workflow reads NPM_TOKEN from GitHub Actions secrets and only injects it when dry_run is false.
Provenance
bun publish in Bun 1.3.14 has no --provenance flag. This prep does not claim npm provenance. If you later publish with npm publish --provenance, add id-token: write to the workflow and document that change here.
CI
.github/workflows/ci.yml runs on push and pull request: bun install, bun run test:unit, bun run typecheck, bun run release:pack.
test:unit is every *.test.ts under packages/, scripts/, and examples/ except *.live.test.ts. Live driver suites need Postgres and PostgREST, and they fail on purpose when those services are down. Run bun test locally when the stack is up. Do not put that command in CI until the workflow starts those services.