This repo is a pnpm workspace for xndrjs: TypeScript libraries that help you model domains with explicit validation boundaries, optional schema adapters (Zod, Valibot, AJV), and small helpers for async work.
packages/<name>— libraries meant to be published to npm (scoped@xndrjs/*).apps/<name>— not published as libraries; things like the documentation site (xndrjs-documentation) and examples (e.g.oas-core-validator-demo).
Workspace-wide scripts (install, build, test, release) run from the repository root; each package documents its own API in its README.md or in the docs app.
Domain modeling: prefer @xndrjs/domain (validator-agnostic) and @xndrjs/domain-zod (Zod 4). @xndrjs/branded is deprecated and kept for historical compatibility. More validation adapters are on the roadmap (several likely in the near term).
- Node 25 (see
enginesinpackage.json) - pnpm 9.15 (matches
packageManager)
Use these docs in order:
- Core-only (
@xndrjs/domain): define primitives, shapes, proofs, capabilities with your ownValidator. - Zod adapter (
@xndrjs/domain-zod): usezodToValidatorandzodFromKitto keepdomainas source of truth and compose nested kits in schemas. - Valibot adapter (
@xndrjs/domain-valibot): usevalibotToValidatorandvalibotFromKitwith the samedomaincore model.
Quick links:
- Core docs and recipes:
packages/domain/README.md - Zod docs and recipes:
packages/domain-zod/README.md - Valibot docs and recipes:
packages/domain-valibot/README.md
Model kits once in domain, then choose an adapter per boundary:
- keep semantic model and capabilities in shared
domainmodules; - adapt external payload validation with
zodToValidatororvalibotToValidator; - compose nested domain kits in adapter schemas using
zodFromKit/valibotFromKit.
| Command | Description |
|---|---|
pnpm install |
Install dependencies for the whole workspace |
pnpm build:packages |
Run build in each packages/* package that defines the script |
pnpm test |
Run tests for all packages |
pnpm changeset |
Create or update a release note (files under .changeset/) |
We use Changesets to version and publish. Public scoped packages use access: public (see .changeset/config.json).
This is a repository secret that holds an npm automation token so CI can run pnpm changeset publish.
- Sign in at npmjs.com with an account that is allowed to publish under your scope (e.g. member of the
@xndrjsorg, or owner of the user scope). - Open Access Tokens:
- Classic token: choose type Automation (not Publish). Automation is meant for CI and can publish without a one-time password. A Publish token or a personal token often triggers
EOTP/ “This operation requires a one-time password” in GitHub Actions because npm still expects interactive 2FA. - Granular token: only if npm shows an option suitable for CI / automation (read and write on organization and the packages or scope you publish). If publish still fails with EOTP, switch to a classic Automation token for
NPM_TOKEN.
- Classic token: choose type Automation (not Publish). Automation is meant for CI and can publish without a one-time password. A Publish token or a personal token often triggers
- Copy the token once npm shows it (you will not see it again).
- In GitHub: open the repo → Settings → Secrets and variables → Actions → New repository secret.
- Name:
NPM_TOKEN, value: paste the token → save.
The workflows pass this to npm as NPM_TOKEN / NODE_AUTH_TOKEN (see .github/workflows/release-*.yml). If publish fails with 403 or 404, the token usually lacks publish rights for that scope or the package name is not created yet under your org. If it fails with EOTP, replace NPM_TOKEN with a classic Automation token (see above).
npm may print warnings like Unknown cli config "--git-checks" or Unknown user config "always-auth"; those come from the Actions runner / npm defaults and are unrelated to EOTP — fixing the token type is what resolves the publish failure.
GitHub Actions injects secrets.GITHUB_TOKEN automatically for each run. You do not need to add a secret named GITHUB_TOKEN in the UI unless you intentionally override it.
The stable workflow grants the job contents: write and pull-requests: write so the Changesets action can open/update the “Version Packages” PR and push commits. The alpha workflow uses contents: write so it can push the version bump after publish.
If PR creation fails with permission errors, check the repo’s Settings → Actions → General → Workflow permissions: enable Read and write permissions for workflows (or the default token will stay read-only and cannot open PRs or push).
- Develop on a feature branch.
- When the change should ship to npm, from the repo root run:
Pick the package and bump type (
pnpm changeset
patch/minor/major). - Commit the generated files under
.changeset/together with your code.
Alpha lines use prerelease semver (e.g. 0.1.1-alpha.0, 0.1.1-alpha.1), not snapshot versions like 0.0.0-alpha-<timestamp>. Consumers install with:
npm install @xndrjs/branded@alpha- Create the branch from
main(or your integration branch of choice):git checkout main git pull git checkout -b alpha
- Enter Changesets prerelease mode (creates
.changeset/pre.json):pnpm changeset pre enter alpha
- Commit and push the branch:
git add .changeset/pre.json git commit -m "chore: enter changesets prerelease (alpha)" git push -u origin alpha
Important: .changeset/pre.json should exist only on the alpha branch. Do not merge it to main (the stable workflow fails if it finds pre.json on main).
- Add a changeset (
pnpm changeset) and merge toalphalike any other change. - On push to
alpha, the Release alpha workflow (.github/workflows/release-alpha.yml):- checks
pre.jsonis inpremode with tagalpha; - runs tests and builds packages under
packages/*; - if there are changesets to apply, runs
pnpm changeset version, publishes withpnpm changeset publish(npm dist-tagalphacomes from.changeset/pre.json, not from--tagon the CLI), then commits the version bump (and changelog) with a message that includes[skip ci]to avoid CI loops.
- checks
If there are no new changesets, the workflow exits without publishing.
You can also run the workflow manually from the Actions tab (Run workflow).
- Ensure
maindoes not contain.changeset/pre.json. - Land your work (including
.changeset/files when a release is needed). - On every push to
main, the Release stable workflow (.github/workflows/release-stable.yml):- if there are open changesets, the changesets action opens or updates the “Version Packages” pull request;
- after merging that PR (which updates versions and changelogs), the next push runs
pnpm run changeset:publish(builds allpackages/*thenpnpm changeset publish) to the registry (default taglatest).
The root script changeset:publish wraps build + publish in one command so GitHub Actions does not split && into separate process arguments (which broke tsup in CI).
When you want to promote alpha versions to normal releases:
- On the
alphabranch, leave prerelease mode and align versions (follow Changesets prompts):Resolve any conflicts; verifygit checkout alpha pnpm changeset pre exit pnpm changeset versionpackage.jsonand changelogs. - Merge
alphaintomainwithout bringing.changeset/pre.jsonalong (afterpre exitit is usually no longer needed in the same form; if it still exists, do not merge it tomain). - On
main, follow the stable flow (Version Packages PR + publish) as above.
Official details: Prereleases.
After pre exit, if you want to keep publishing alphas from the alpha branch, run pnpm changeset pre enter alpha again and commit the updated pre.json (same as the initial setup).
In the branded package:
cd packages/branded
pnpm run packThis writes artifacts/*.tgz (folder is gitignored). Useful to inspect what would ship before publishing.
| Package | Description |
|---|---|
@xndrjs/domain |
Validator-agnostic shapes, primitives, proofs, capabilities (packages/domain) |
@xndrjs/domain-zod |
Zod 4 adapter; re-exports domain (packages/domain-zod) |
@xndrjs/domain-valibot |
Valibot adapter; re-exports domain (packages/domain-valibot) |
@xndrjs/branded |
Deprecated — use domain / domain-zod (packages/branded) |
@xndrjs/tasks |
Lazy async tasks with retry (packages/tasks) |
@xndrjs/orchestration |
Orchestration ports (packages/orchestration) |
@xndrjs/react-adapter |
React hooks for orchestration ports (packages/react-adapter) |