Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/defu-merge-isolate-safe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@vlandoss/env": patch
---

Replace `lodash.merge` with [`defu`](https://github.com/unjs/defu) for the internal merge that combines `defaults + config + envOverride`. The precedence (env > config > defaults) is unchanged.

This fixes a silent shallow-merge regression on V8 isolate runtimes (Cloudflare Workers, Vercel Edge, Next.js Edge): `lodash.merge` relies on `isPlainObject`, which returns `false` when the object originates from a different realm than the merger. The fallback path then assigns by reference instead of deep-merging, which caused leaf values present in `config` (e.g. `db.LOGGING: true`) to be dropped whenever `envOverride` touched the same parent branch.

defu is realm-agnostic and reliably deep-merges in all the runtimes supported by the core.
5 changes: 5 additions & 0 deletions .changeset/docs-feedback-polish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@vlandoss/env": patch
---

Polish the package README to match the new docs site language — soften technical references to `process.env`, separate the optional `zod` install, and align the tagline with the rest of the documentation.
13 changes: 13 additions & 0 deletions .changeset/envscript-rename.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@vlandoss/env": minor
---

**BREAKING** Rename `<ClientEnv />` (in `@vlandoss/env/react`) to `<EnvScript />`. The new name stops suggesting client-side use and reflects what the component actually does: emit a `<script>` tag with the server-resolved env so the browser can read it during SSR or SSG hydration. The entrypoint path (`@vlandoss/env/react`) is unchanged. The wire format is also unchanged: the DOM `id="env"` and `window.__env` global stay identical, so HTML served by an older build continues to hydrate correctly.

Renames included:

- Component: `ClientEnv` → `EnvScript`
- Props: `ClientEnvProps` → `EnvScriptProps`
- Constants: `CLIENT_ENV_SCRIPT_ID` → `ENV_SCRIPT_ID`, `CLIENT_ENV_GLOBAL_ID` → `ENV_GLOBAL_ID`

Migration: replace `<ClientEnv …>` with `<EnvScript …>`. If you imported the constants from the core entrypoint, update those imports too.
7 changes: 7 additions & 0 deletions .changeset/fs-entrypoint-rename.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@vlandoss/env": minor
---

**BREAKING** Rename `@vlandoss/env/node` to `@vlandoss/env/fs`. The entrypoint has always been about file-system access on a Node-compatible runtime — the new name reflects that it works on Node, Bun, and Deno (not just Node), and that it does not work on Workers/Edge. The dynamic-import path resolution now uses `pathToFileURL` so Deno can load absolute paths correctly.

Migration: replace `from "@vlandoss/env/node"` with `from "@vlandoss/env/fs"`.
5 changes: 5 additions & 0 deletions .changeset/readenv-defensive-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@vlandoss/env": patch
---

`readEnv()` no longer throws `ReferenceError` on runtimes where `process` is not defined (e.g. Cloudflare Workers without `nodejs_compat`). It now returns `{}` so callers can supply the environment explicitly via `defineEnv({ runtimeEnv })` — the idiomatic pattern in Worker `fetch(req, env, ctx)` handlers. Behavior on Node, Bun, Deno, browsers, and Edge runtimes that polyfill `process.env` is unchanged.
74 changes: 25 additions & 49 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, labeled]
types: [opened, synchronize, reopened]
branches:
- main

Expand All @@ -16,32 +16,33 @@ concurrency:
jobs:
test:
runs-on: blacksmith-4vcpu-ubuntu-2404
env:
MISE_EXPERIMENTAL: "1"
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: 🟢 Setup Node.js
uses: actions/setup-node@v6
- name: 🛠️ Setup mise
uses: jdx/mise-action@v2
with:
node-version-file: .node-version
install: true
cache: true
experimental: true

- name: ⌛ Setup pnpm
uses: variableland/gh-actions/actions/setup-pnpm@main
- name: 📦 Install root workspace
run: pnpm install --frozen-lockfile

- name: 💅 Test static
run: pnpm rr test:static
- name: 💅 Test static (biome + tsc)
run: mise run test:static

- name: 🎭 Install Playwright browsers
run: pnpm exec playwright install --with-deps chromium
- name: 🎭 Install Playwright browser (lib browser tests)
run: mise run playwright:install

- name: 🧪 Test unit
run: pnpm test
- name: 🧪 Test unit (lib)
run: mise run lib:test

- name: 🧩 Test e2e
run: pnpm test:e2e

- name: 🔨 Build
run: pnpm build
- name: 🔨 Build (lib)
run: mise run lib:build

release:
runs-on: ubuntu-latest
Expand All @@ -65,13 +66,15 @@ jobs:
with:
token: ${{ steps.app-token.outputs.token }}

- name: 🟢 Setup Node.js
uses: actions/setup-node@v6
- name: 🛠️ Setup mise
uses: jdx/mise-action@v2
with:
node-version-file: .node-version
install: true
cache: true
experimental: true

- name: ⌛ Setup pnpm
uses: variableland/gh-actions/actions/setup-pnpm@main
- name: 📦 Install root workspace
run: pnpm install --frozen-lockfile

- name: 🧑‍💻 Configure git user
run: |
Expand All @@ -88,30 +91,3 @@ jobs:
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
LEFTHOOK: 0

preview:
runs-on: ubuntu-latest
needs: test
if: ${{ startsWith(github.head_ref, 'feat') || startsWith(github.head_ref, 'fix') || contains(github.event.pull_request.labels.*.name, 'preview') }}
permissions:
id-token: write
contents: write
packages: write
pull-requests: write
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: 🟢 Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: .node-version

- name: ⌛ Setup CI
uses: variableland/gh-actions/actions/setup-pnpm-bun@main

- name: 🚀 Preview release
uses: variableland/gh-actions/actions/monorepo-preview-release@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
npm_token: ${{ secrets.NPM_TOKEN }}
57 changes: 57 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: E2E

on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
branches:
- main

jobs:
test:
name: ${{ matrix.example }}
runs-on: blacksmith-4vcpu-ubuntu-2404
env:
MISE_EXPERIMENTAL: "1"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.example }}
cancel-in-progress: true
strategy:
fail-fast: false
matrix:
include:
- { example: backend-node, browser: false }
- { example: backend-bun, browser: false }
- { example: backend-deno, browser: false }
- { example: worker-cloudflare, browser: false }
- { example: edge-nextjs, browser: false }
- { example: spa-vite-dynamic, browser: true }
- { example: spa-vite-plugin, browser: true }
- { example: ssr-react-router, browser: true }
- { example: ssr-tanstack-start, browser: true }
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: 🛠️ Setup mise (root + example tools)
uses: jdx/mise-action@v2
with:
install: true
cache: true
experimental: true
working_directory: examples/${{ matrix.example }}

- name: 📦 Install root workspace (for env:pack)
run: pnpm install --frozen-lockfile

- name: 🎁 Pack @vlandoss/env
run: mise run env:pack

- name: 🎭 Install Playwright browsers
if: matrix.browser
run: mise run playwright:install

- name: 🧩 Test e2e
run: mise run //examples/${{ matrix.example }}:test:e2e
41 changes: 41 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Preview

on:
pull_request:
types: [opened, synchronize, reopened, labeled]
branches:
- main
paths:
- "package/**"
- ".github/workflows/preview.yml"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
preview:
runs-on: ubuntu-latest
if: ${{ startsWith(github.head_ref, 'feat') || startsWith(github.head_ref, 'fix') || contains(github.event.pull_request.labels.*.name, 'preview') }}
permissions:
id-token: write
contents: write
packages: write
pull-requests: write
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v4

- name: 🟢 Setup Node.js
uses: actions/setup-node@v6
with:
node-version-file: .node-version

- name: ⌛ Setup CI
uses: variableland/gh-actions/actions/setup-pnpm-bun@main

- name: 🚀 Preview release
uses: variableland/gh-actions/actions/monorepo-preview-release@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
npm_token: ${{ secrets.NPM_TOKEN }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ web_modules/

*.tgz

# Local pack output (used by mise env:pack to feed examples)

package/.local/

# Yarn Integrity file

.yarn-integrity
Expand Down
1 change: 0 additions & 1 deletion .npmrc

This file was deleted.

3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"vitest.configSearchPatternInclude": "**/vitest.config.{ts,js,mjs,cjs,cts,mts}"
}
17 changes: 17 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# @vlandoss/env

Contract-first environment configuration with typed schemas. Runtime-agnostic core (Node, Bun, Deno, browser, Workers, Edge) with opt-in adapters for Node, Vite, React (SSR), and a curated set of Zod-based environment schema primitives.

See [README.md](./README.md) for setup, tooling, and commands.

## Repository structure

Monorepo managed with pnpm workspaces + Turborepo:

- [`package/`](./package/README.md) — the `@vlandoss/env` npm package (source & tests)
- [`docs/`](./docs/README.md) — Fumadocs; published at [env.oss.variable.land](https://env.oss.variable.land)
- [`examples/`](./examples/README.md) — runnable example apps with Playwright e2e suites

## Release

- This project use changesets to manage releases, never touch the `CHANGELOG.md` or bump `package.json` file directly, it is generated by changesets.
Loading
Loading