@lostgradient/environmentalist
v0.0.1
Published
Type-safe environment variable loading, validation, and configuration resolution for Node, Bun, and the browser.
Maintainers
Readme
Project Name
Prerequisites
- Bun installed on your machine.
Installation
Create a new project based on this template:
# From the local template in ~/.bun-create/basic
bun create basic $PROJECT_DIRECTORY
# Skip installing dependencies (useful for CI or offline work)
bun create basic $PROJECT_DIRECTORY --no-installIf you publish this template to a GitHub repository, you can also create from
it directly — replace <owner>/<repo> with your repository:
bun create github.com/<owner>/<repo> $PROJECT_DIRECTORYThe --no-install flag is helpful when:
- Working in offline environments
- Using CI pipelines with cached dependencies
- You plan to modify dependencies before installation
Core Tools
- Bun: runtime, bundler, test runner, and package manager
- TypeScript: strict type checking
- Oxlint: fast Rust-based linter
- Prettier: formatting
- Lefthook: Git hooks
Development
Start the development server:
bun run devGit Hooks (Lefthook)
Lefthook is installed via the prepare script on bun install. Hook implementations live in scripts/hooks/ and are configured in lefthook.yml.
pre-commit: formats staged files with Prettier, runs oxlint --fix on staged files, blocks staged conflict markers, and checks thatbun.lockis staged whenpackage.jsonchanges. Fast by design — typecheck and tests are intentionally deferred to pre-push. Skipped during merge/rebase.pre-push: runsbun run validate(format check, lint, typecheck, tests, build, and package validation). This is the full gate before code leaves your machine. Skipped in CI.post-checkout: installs deps whenbun.lockchanged; surfaces config changes.post-merge: installs/cleans when dependencies or config changed; flags leftover conflict markers.
Hooks print only when something fails, so clean commits and pushes stay quiet. Use --no-verify to bypass hooks (not recommended; CI will catch you anyway).
Running Tests
This template uses Bun's built-in test runner with a preloaded setup file at test/setup.ts that resets mocks and system time after each test.
bun test # run all tests
bun test --watch # watch mode
bun test --coverage # coverage reportCoverage thresholds are configured in bunfig.toml under [test]. The default is 100% for src/.
For mocking, clock control, and module mocking see the bun:test docs.
Continuous Integration
A CI workflow at .github/workflows/ci.yaml runs bun run validate on every push and pull request against Node 22 (LTS) and Node 24 (latest). This includes linting, typechecking, tests, build, and package validation (publint + @arethetypeswrong/cli).
Understanding bun run vs bunx
- bun run: Executes scripts defined in
package.jsonor runs local TypeScript/JavaScript files directly. - bun x: Executes binaries from installed packages. For packages already in
devDependencies, preferbun run <script>or calling the binary directly rather thanbunx, which can pull a remote version.
Project Structure
src/— Source codetest/— Test setup (test/setup.tsis preloaded by bun:test)scripts/hooks/— Git hook implementations (TypeScript + Bun)scripts/setup/— One-timebun createsetup scripts (self-remove after first install)lefthook.yml— Git hook configuration
Library Output
When built, the package emits two ESM bundles:
dist/index.node.js— Node-family build (Bun.build target: 'node')dist/index.browser.js— browser build (Bun.build target: 'browser')dist/cli.js,dist/react.js, anddist/svelte.js— CLI and optional framework entriesdist/index.d.ts— Shared TypeScript declarations
The package.json exports map routes Bun consumers to the Bun build and Node/bundler consumers to the Node build automatically.
Published src/ code must not use Bun-only runtime APIs (Bun.file, Bun.serve, etc.) — those belong in scripts/ and tests only.
Releasing
Releases are tag-driven and tokenless. .github/workflows/release.yaml publishes to npm
using trusted publishing — GitHub Actions mints
a short-lived OIDC token, so there is no NPM_TOKEN secret in this repository.
To cut a release:
npm version patch # or minor / major — commits and tags vX.Y.Z
git push --follow-tagsPushing the tag triggers release.yaml, which verifies the tag matches package.json,
runs bun run validate, and publishes with npm provenance.
One-time trusted publisher setup
Trusted publishers are configured per package on npmjs.com, so the package has to exist first. Bootstrap it once from a local machine with 2FA, then hand publishing over to CI:
npm publishlocally (thepublishConfig.access: "public"inpackage.jsonmakes the scoped package public). OmitNPM_CONFIG_PROVENANCE— provenance only works from CI. This consumes the current version, so runnpm version patchbefore the first tag-driven release or npm will reject the tag as an already-published version.- On npmjs.com → the package → Settings → Trusted Publisher, add a GitHub Actions
publisher: owner
stevekinney, repositoryenvironmentalist, workflow filenamerelease.yaml(exact, case-sensitive — notrelease.yml), environment left blank. - Under Publishing access, select Require two-factor authentication and disallow tokens so the OIDC workflow is the only automated path in. This also closes off local emergency publishes — after this, every release goes through a tag.
NPM_CONFIG_PROVENANCE is set in the workflow rather than in publishConfig on purpose —
provenance requires a supported CI, so putting it in package.json would break the local
bootstrap publish above.
Customization
TypeScript Configuration
The base tsconfig.json targets ESNext with strict settings tuned for a Bun library. To add a frontend app layer:
- Extend
tsconfig.jsonin a newtsconfig.frontend.json - Add
"lib": ["ESNext","DOM","DOM.Iterable"]and"jsx": "react-jsx"(or your framework equivalent)
Template Setup (bun-create)
When using bun create with this template, a postinstall sequence runs once to bootstrap the project:
- Sets
package.json:namefrom the folder name - Copies
.env.exampleto.env(or appends missing keys) - Writes
OPENAI_API_KEY,ANTHROPIC_API_KEY, andGEMINI_API_KEYfrom your shell into.envif the values are currently empty or placeholder - Runs
bun run prepareto install Lefthook hooks - Removes
scripts/setup/and thebun-createentry frompackage.json
These steps are idempotent — safe to re-run if something fails partway through.
