Nx monorepo for the Rule.io TypeScript SDK. Detailed usage docs live in packages/sdk and the per-package READMEs; this README covers package selection, contributor workflow, and the release process.
| Package | Purpose | Status |
|---|---|---|
@rule/rcml |
RCML email-template builders, types, and validators | Released |
@rule/client |
HTTP wrapper around the Rule.io v2/v3 API | Released |
@rule/sdk |
Meta-package re-exporting @rule/rcml + @rule/client |
Released |
@rule/template-engine |
XML template engine powering vendor email templates | Under development |
@rule/vendor |
Shared vendor-preset infrastructure | Under development |
@rule/vendor-shopify |
Shopify preset — e-commerce automation flows | Under development |
@rule/vendor-bookzen |
Bookzen preset — hospitality automation flows | Under development |
@rule/vendor-samfora |
Samfora preset — Swedish donation flows | Under development |
Most consumers install one package — npm pulls in everything else as transitive dependencies.
| If you want to… | Install | Notes |
|---|---|---|
| Call the Rule.io HTTP API | @rule/client |
The 90% case. Brings in @rule/rcml automatically. |
| Compose custom RCML templates from primitives | @rule/rcml |
Low-level builders only — pair with @rule/client to send. |
| Try everything in one install (prototypes, demos) | @rule/sdk |
Meta-package re-exporting the libraries above. |
Just calling the API:
npm install @rule/clientimport { RuleClient } from '@rule/client';
const client = new RuleClient({ apiKey: process.env.RULE_API_KEY! });
await client.addSubscriberTagsV3('user@example.com', { tags: ['welcome'] });Kitchen sink for prototyping:
npm install @rule/sdkimport { RuleClient, createBrandTemplate } from '@rule/sdk';
@rule/template-engineis an infrastructure package — it almost never appears in a consumer'spackage.json. It arrives as a transitive dependency of the vendor preset packages (not yet released).
Clean DAG, no cycles — ← reads as "depends on"; workspace edges only, external runtime deps live in each package's package.json:
template-engine
rcml ← template-engine
client ← rcml
vendor ← rcml
vendor-bookzen ← client, rcml, template-engine, vendor
vendor-samfora ← rcml, template-engine, vendor
vendor-shopify ← rcml, template-engine, vendor
sdk (meta) ← rcml, client [v0.3.0 release scope]
Node >=20 required (the Nx plugins used by this workspace rely on node:util.styleText, added in Node 20.12 / 22). If you use nvm: nvm use 22.
npm install # install + link workspace packages
npx nx show projects # sanity-check: list all packages in the workspace
npm run build # build all publishable packages → dist/packages/<pkg>/
npm run test # run every package's tests
npm run lint # lint every package
npm run graph # visualise the project graph in a browserBuilding a single package (and its deps, in topological order):
npx nx build client # builds rcml, then clientReleases are orchestrated by nx release with version calculation driven by Conventional Commits.
Fixed/locked versioning — all published packages share one version number. A release always versions the entire public SDK together.
| Branch | Purpose |
|---|---|
develop |
Primary active development branch. All feature work targets here. Never published directly. |
main |
Stable, releasable state. Merging to main triggers the automated release workflow. |
0.4.x, 1.0.x, … |
Maintenance branches — critical fixes for older release lines. |
feature branches → develop → release PR → main → automated release workflow → (approval) → npm publish
Merging to main triggers CI. npm publishing is gated by GitHub Environment approval (npm-production) — the workflow auto-prepares the release but a human must approve before anything lands on the registry.
Always use merge commit when landing develop → main.
nx release version calculates the version bump by reading Conventional Commits directly from the git log. The merge strategy determines what Nx sees:
| Strategy | What Nx Release sees | Risk |
|---|---|---|
| Merge commit ✓ | Every feat:, fix:, BREAKING CHANGE: intact (Nx traverses through merge commits) |
None — correct bump every time |
| Rebase merge | Every commit intact, but new SHAs on main mean develop requires a reset --hard to re-sync |
Extra friction keeping develop in sync |
| Squash merge | One commit with the squash message | Minor bump if message is feat: — no release if message is chore: or generic. Also leaves develop with ghost commits that conflict on the next PR. |
After each merge to main, sync develop:
git fetch origin main
git checkout develop
git merge --ff-only origin/main--ff-only fails loudly if develop has diverged unexpectedly, preventing an accidental extra merge commit.
Beta stage (0.x):
| Commit type | Result |
|---|---|
fix:, perf: |
beta patch increment — e.g. 0.3.0-beta.1 → 0.3.0-beta.2 |
feat: |
next beta minor line — e.g. 0.3.0-beta.2 → 0.4.0-beta.0 |
feat!: / BREAKING CHANGE: footer |
next beta minor line (same as feat during 0.x) |
chore:, docs:, test:, style:, ci:, refactor: |
no bump |
Stable (1.0.0+):
| Commit type | Result |
|---|---|
fix:, perf: |
patch |
feat: |
minor |
feat!: / BREAKING CHANGE: footer |
major |
chore:, docs:, test:, style:, ci:, refactor: |
no bump |
Scope your commits when a change is package-specific: fix(client): …, feat(rcml): ….
- Beta releases publish under the
betadist-tag →npm install @rule/client@beta - Stable releases publish under
latest
Not all packages in this monorepo are published — see the Packages table above. For each published package, Nx runs npm publish from dist/packages/<pkg>/. Contents of each tarball:
.js+.d.tsoutput underdist/packages/<pkg>/src/- the package-specific
README.md - the package-specific
CHANGELOG.md(auto-generated by Nx Release) - a
package.jsonwith the bumped version, rewritten paths, and nodevDependencies
Nx Release generates both a root CHANGELOG.md (SDK-level) and a per-package CHANGELOG.md for each published package.
Published versions are never unpublished or rewritten. Roll back by publishing a new fix or revert version.
The SDK documentation site lives in apps/docs/ and is built with VitePress. It is deployed to GitHub Pages from the gh-pages branch on every push to main.
| Content | Location |
|---|---|
| Cross-package guides (Getting Started, etc.) | apps/docs/src/guide/ |
| Package-specific conceptual docs | packages/{name}/docs/ |
| API reference (JSDoc) | packages/{name}/src/ — generated by TypeDoc |
| Homepage | apps/docs/src/index.md |
npm run docs:generate # generate API reference + sync package docs
npm run docs:dev # start dev server → http://localhost:5173/sdk/To preview the production build:
npm run docs:build # build static site
npm run docs:preview # serve the build locallynpm run docs:check # verify all public symbols are documented (TypeDoc)
npm run docs:build # also runs VitePress's built-in dead-link checkerBoth checks run automatically in CI on every PR.
- Guide content: edit Markdown files in
apps/docs/src/guide/orpackages/{name}/docs/. Seeapps/docs/src/guide/contributing.mdfor authoring guidelines. - API reference: edit JSDoc in
packages/{name}/src/and re-rundocs:generate. - Generated files (
apps/docs/src/api/,apps/docs/src/packages/) are gitignored — never commit them.
packages/
├── rcml/
├── client/
├── template-engine/
├── vendor/
├── vendor-shopify/
├── vendor-bookzen/
├── vendor-samfora/
└── sdk/ # meta-package
apps/
└── docs/ # VitePress documentation site
.github/workflows/ # CI + docs deployment
nx.json # Nx config incl. `release`
tsconfig.json # path mappings for IDE autocompletion
Before creating a feature branch, make sure your local
developis up to date withmain. Release commits land onmainfirst and are fast-forwarded intodevelopafterwards — branching from a staledevelopwill pull those already-merged commits into your PR diff.git fetch origin main git checkout develop git merge --ff-only origin/mainThen create your branch from the updated
develop.
- Start a branch from
develop. - Use conventional commits (
feat(client): …,fix(rcml): …, etc.) — they drive the version bumps. - Open a PR targeting
develop; CI runsnx affected -t eslint:lint test-ci build --parallel=3on your changes. - After merging into
develop, open a release PR tomain. Merging tomaintriggers the automated release workflow; a maintainer approves the npm publish step in the GitHubnpm-productionenvironment.