Contributing to MeshQL
Thank you for your interest in contributing to MeshQL.
Getting started
Section titled “Getting started”git clone https://github.com/meshql/meshql.gitcd meshqlpnpm installpnpm buildpnpm testRequirements: Node.js 22+ (CI uses 24), pnpm 11.
Development workflow
Section titled “Development workflow”- Fork the repository and create a branch from
main. - Make your changes in the appropriate package under
packages/. - Add or update tests in
@meshql/corewhen changing engine behavior. - Run checks before opening a PR:
pnpm buildpnpm testpnpm check-typespnpm formatProject structure
Section titled “Project structure”| Path | Purpose |
|---|---|
packages/core | Parser, planner, shaper, SQL builder |
packages/http | HTTP transport and framework adapters |
packages/client | Client SDK |
packages/upload | Upload extension |
packages/integrity | Request integrity / signing |
packages/access | Access control helpers |
examples/ | Runnable examples |
docs/ | Usage guides |
HTTP setup: docs/http-adapters.md.
Public API freeze
Section titled “Public API freeze”The intended 1.0 surface is documented in docs/PUBLIC_API.md (application vs adapter vs advanced exports). Rules:
- New optional exports and options are fine; mark unstable APIs
@experimentaluntil they are listed there. - Removing, renaming, or requiring new arguments is breaking — add a changeset and a migration note even before 1.0.
- Do not un-export SQL/ORM helpers from
@meshql/corewithout checking dialect packages; they are public adapter API. - Security-relevant behavior belongs in
docs/threat-model.mdandspecs/, not only in code comments.
Releasing packages
Section titled “Releasing packages”MeshQL is a pnpm + Turbo monorepo with independent per-package versions. Each library can ship on its own schedule. npm and JSR share the same semver per package (package.json is the source of truth; jsr.json is synced automatically).
Publishable packages
Section titled “Publishable packages”| Package | npm name | JSR name |
|---|---|---|
@meshql/core | @meshqljs/core | @meshql/core |
@meshql/postgres | @meshqljs/postgres | @meshql/postgres |
@meshql/sqlite | @meshqljs/sqlite | @meshql/sqlite |
@meshql/http | @meshqljs/http | @meshql/http |
@meshql/client | @meshqljs/client | @meshql/client |
@meshql/upload | @meshqljs/upload | @meshql/upload |
@meshql/integrity | @meshqljs/integrity | @meshql/integrity |
@meshql/access | @meshqljs/access | @meshql/access |
@meshql/prisma | @meshqljs/prisma | @meshql/prisma |
@meshql/drizzle | @meshqljs/drizzle | @meshql/drizzle |
@meshql/kysely | @meshqljs/kysely | @meshql/kysely |
@meshql/persisted-queries | @meshqljs/persisted-queries | @meshql/persisted-queries |
@meshql/access-cache | @meshqljs/access-cache | @meshql/access-cache |
@meshql/pubsub | @meshqljs/pubsub | @meshql/pubsub |
@meshql/sse | @meshqljs/sse | @meshql/sse |
@meshql/codemods | @meshqljs/codemods | @meshql/codemods |
@meshql/gateway | @meshqljs/gateway | @meshql/gateway |
@meshql/docs | @meshqljs/docs | @meshql/docs |
@meshql/typescript-config is private and never published.
Publish order (when multiple packages release together): core → postgres → sqlite → prisma → drizzle → kysely → http → upload → client → integrity → access → persisted-queries → access-cache → pubsub → sse → codemods → gateway → docs.
Standard release flow (Changesets)
Section titled “Standard release flow (Changesets)”- Add a changeset in your feature PR when you ship user-facing changes:
pnpm changesetChoose the affected package(s) and semver bump (patch / minor / major). This creates a file under .changeset/.
-
Merge your PR to
main. If changesets are pending, CI opens or updates a Version Packages PR (.github/workflows/release.yml).One-time repo setting (required): GitHub → Settings → Actions → General → enable Read and write permissions for workflows, and check Allow GitHub Actions to create and approve pull requests. Without this, the Version Packages PR cannot be opened automatically.
-
Merge the Version Packages PR. It bumps
package.json, syncsjsr.json, and updates per-packageCHANGELOG.mdfiles. -
CI creates per-package tags on
main. Tag push triggers publish.yml:- npm:
npm/{pkg}/v{version}→ publishes@meshqljs/{pkg} - JSR:
{pkg}/v{version}→ publishes@meshql/{pkg}
- npm:
Example: releasing only @meshql/core at 0.1.4 pushes npm/core/v0.1.4 and core/v0.1.4 — no umbrella tag required.
Manual release (fallback)
Section titled “Manual release (fallback)”Bump version in packages/{pkg}/package.json, run node scripts/sync-jsr-versions.mjs, update the package CHANGELOG.md, commit, then:
git tag npm/core/v0.1.4git tag core/v0.1.4git push origin npm/core/v0.1.4 core/v0.1.4Or use Actions → Publish → Run workflow to retry a package (choose registry: npm, jsr, or both) without retagging.
Optional umbrella tags (legacy)
Section titled “Optional umbrella tags (legacy)”These still work but are not the primary release path:
| Tag | Publishes |
|---|---|
npm/all/v* / all/v* | core, http, upload, client only |
npm/security/v* / security/v* | integrity + access together |
Publishing to JSR
Section titled “Publishing to JSR”JSR packages ship TypeScript source (jsr.json exports). CI rewrites workspace:* deps before publish.
One-time JSR setup (required for each package)
Section titled “One-time JSR setup (required for each package)”JSR OIDC only works for packages linked to your GitHub repo. Creating @meshql/core is not enough.
For each publishable package:
- Go to jsr.io/new and create it under the
@meshqlscope (if not created yet). - Open the package on JSR → Settings → GitHub repository.
- Enter
meshql/meshqland click Link. - Ensure
packages/{pkg}/README.mdexists (publish verify fails without it).
Setup checklist:
| Package | JSR created | GitHub linked |
|---|---|---|
@meshql/core | yes | yes |
@meshql/http | yes | yes |
@meshql/client | yes | yes |
@meshql/upload | yes | yes |
@meshql/integrity | yes | yes |
@meshql/access | yes | yes |
@meshql/persisted-queries | yes | yes |
@meshql/access-cache | yes | yes |
Without linking, CI fails with actorNotAuthorized or Following packages don't exist. See docs/jsr-settings.md for descriptions and runtime compatibility.
Tag-push publishes use GitHub OIDC (id-token: write) — no dispatch workaround. Keep RELEASE_PAT for the Changesets version PR only.
Local JSR publish
Section titled “Local JSR publish”pnpm buildpnpm publish:dry-run # npm pack + jsr --dry-run for all packagespnpm publish:local -- --registry jsr --package core --dry-runpnpm publish:jsr # all publishable packages, in workspace orderAlready-published versions are skipped automatically by jsr publish.
Publishing to npm
Section titled “Publishing to npm”Packages publish under the @meshqljs npm org as compiled ESM (dist/). Workspace and JSR names stay @meshql/*.
| Registry | Name | Example |
|---|---|---|
| npm | @meshqljs/<pkg> | npm i @meshqljs/core |
| JSR | @meshql/<pkg> | npx jsr add @meshql/core |
One-time npm setup
Section titled “One-time npm setup”- Create an npm access token with Automation or Publish scope (member of
@meshqljs). - Add it to the GitHub repo:
gh secret set NPM_TOKEN --repo meshql/meshql- First publish of each package claims
@meshqljs/<pkg>on npm.
npm tag reference
Section titled “npm tag reference”| Trigger | Example | Publishes |
|---|---|---|
| Per-package tag | npm/core/v0.1.3 | @meshqljs/core only |
| Per-package tag | npm/http/v0.1.3 | @meshqljs/http only |
| Umbrella tag | npm/all/v0.1.3 | core → … → client (see tag list) |
| Manual dispatch | Actions → Publish | selected package(s) / registry |
Each npm tag push creates a GitHub Release with .tgz assets.
Local pack (dry run)
Section titled “Local pack (dry run)”pnpm buildpnpm publish:dry-runpnpm publish:npm:pack # writes artifacts/*.tgz for all publishable packagesscripts/prepare-npm-publish.mjs rewrites package.json for publish (@meshqljs/* names, workspace:* → semver, strip devDeps) and rewrites @meshql/* imports in dist/ to @meshqljs/*. Workspace pnpm build keeps @meshql/* so tests resolve. CI restores manifests and dist after each package.
Pull requests
Section titled “Pull requests”- Keep PRs focused - one feature or fix per PR when possible.
- Add a changeset (
pnpm changeset) for user-facing package changes; CI opens a Version Packages PR. - For small doc-only changes with no release, a changeset is optional.
- Changing a frozen export in
docs/PUBLIC_API.mdneeds an explicit mention in the PR. - Write clear commit messages and PR descriptions explaining why, not just what.
- Ensure CI passes.
Reporting issues
Section titled “Reporting issues”- Bugs: include reproduction steps, expected vs actual behavior, and environment (Node version, OS).
- Features: describe the use case and how it fits MeshQL’s scope (REST + shaped queries, minimal ceremony).
Code style
Section titled “Code style”- TypeScript strict mode; match existing patterns in the package you’re editing.
- Prefer small, focused changes over large refactors unless discussed first.
- Comments only where behavior is non-obvious.
License
Section titled “License”By contributing, you agree that your contributions will be licensed under the MIT License.