Thanks for taking the time to contribute. This document covers the conventions we follow so your PR can land smoothly. For local setup, the per-task reference, and how the monorepo is wired, see DEVELOPMENT.md.
- Report a bug. Open an issue with a minimal repro and the actual vs. expected behavior. Include runtime (Node / Bun / Deno / Workers / Edge),
@vlandoss/envversion, and the schema validator you use. - Propose a feature. Open an issue first so we can align on scope before you spend time on a PR. We bias toward small, composable APIs.
- Improve the docs. Even one-paragraph corrections are welcome. Docs live in
docsite/content/docs/. - Add an example. Demos that exercise a runtime or framework we don't cover yet are great PRs. See adding a new example below.
- Send a fix or feature PR. Read the rest of this document first.
- An issue exists for non-trivial changes. For typo fixes, dead-code removal, or a single-file bug fix, just open the PR. For anything that adds API, changes behavior, or touches public types, open an issue first.
- Your branch is up to date with
main. mise run test:e2epasses locally (or at least the suites your change touches).- You added a changeset if the change affects the published
@vlandoss/env. See Changesets.
Use a short prefix that mirrors the change type:
feat/<short-slug>— new feature or new public APIfix/<short-slug>— bug fixdocs/<short-slug>— docs-only changeschore/<short-slug>— tooling, CI, dependencies, refactors with no behavior changetest/<short-slug>— test-only changes
The CI release workflow uses the feat/* and fix/* prefixes to publish preview releases under the pr-<number> dist-tag (see Preview releases).
We follow Conventional Commits, kept terse:
feat(zod): add zEnum primitive for case-insensitive enums
fix(react): EnvScript no longer double-encodes the JSON payload
docs: clarify resolution order for runtimeEnv
chore(ci): bump jdx/mise-action to v2
The PR title follows the same format — that title becomes the squash-merge commit.
We version and release with Changesets. Every PR that affects what gets shipped to npm needs a changeset.
Add one as part of your PR:
pnpm changesetThat command walks you through:
- Which packages changed (almost always just
@vlandoss/env). - The bump type —
patch(bug fix),minor(new feature, backwards-compatible),major(breaking change). - A user-facing description (it lands in the changelog).
It writes a markdown file under .changeset/ — commit it with the rest of your changes.
You don't need a changeset when:
- Only
docsite/,examples/, CI files, or other non-published files changed. - The PR is a pure
chore/*that doesn't touchpackage/src/.
Don't bump versions or edit CHANGELOG.md by hand — Changesets does both during release.
- Branch named
feat/*,fix/*,docs/*,chore/*, ortest/* - PR title follows Conventional Commits
- Tests cover the change (unit in
package/src/__tests__/, e2e inexamples/<name>/test/e2e/if relevant) -
mise run lib:testandmise run checkpass locally (JS & TS check) -
mise run test:e2epasses locally for affected examples - Changeset added (if the published
@vlandoss/envchanged) - Docs updated (if public API or behavior changed)
Two workflows run on every PR:
- CI (
.github/workflows/ci.yml) — static checks, unit tests, and library build inside the small pnpm workspace (package+docsite). - E2E (
.github/workflows/e2e.yml) — a matrix job per example, each provisioning its own runtime viajdx/mise-action.
Both must be green to merge.
Examples are runtime-isolated demos. Each one is a real consumer of the published @vlandoss/env tarball — no shared lockfile, no workspace cheating. To add one:
- Create the directory under
examples/<name>/with the source for the demo. - Add a
package.jsonthat consumes@vlandoss/envfrom the local tarball: - Add a
mise.tomlwith the runtime tools (node/bun/deno/pnpm) and the standard tasks (setup,start,test:e2e,check). Use any of the existing examples as a template — pick the one with the closest runtime. - Add a
biome.jsonextending@rrlab/biome-config(skip this for Deno-only examples). - Add the path to
[monorepo].config_rootsin the rootmise.toml. - Add a row to the matrix in
.github/workflows/e2e.yml. - Update the table in
examples/README.md. - Run the install + e2e and commit the generated lockfile.
The detailed setup mechanics — which package manager fits which runtime, why Deno needs nodeModulesDir: "manual", the depends-on-//:env:pack rule — are documented in DEVELOPMENT.md.
PRs from branches named feat/* or fix/* publish a preview build of @vlandoss/env to npm under the pr-<PR_NUMBER> dist-tag, so reviewers can install the unmerged version:
pnpm install @vlandoss/env@pr-123The preview is republished on every push to the PR.
When PRs with changesets land on main, the Changesets bot opens a "Version Packages" PR. Merging that PR triggers the release job to publish the new versions to npm and tag the release on GitHub.
Maintainers don't need to manually bump versions or edit CHANGELOG.md — both are generated.
- We aim to leave a first review within a few business days. If you don't hear back in a week, ping the PR.
- Reviews focus on: correctness, type safety at API boundaries, test coverage of failure modes, runtime portability (does this break Deno / Workers?), and docs alignment.
- We're conservative about new public surface area. Smaller, composable additions are easier to land than big new namespaces.
Be kind and assume good faith. We don't have a formal CoC document yet — if your interaction wouldn't fly in a respectful workplace, don't post it here.
Questions? Open a discussion or ping us in the PR.