Skip to content

Releases: hyperb1iss/next-dynenv

v4.1.0

Choose a tag to compare

@github-actions github-actions released this 04 Nov 01:51

Release Notes 4.1.0

Released: 2026-11-04

This release fixes edge cases in envParsers, makeEnvPublic, and EnvScript. It also lets CommonJS consumers such as a compiled next.config.ts load the package, and the npm tarball now contains only the build output. Behind that, the toolchain moves to pnpm 12, TypeScript 7, Vitest 5, and Next 16.4. The example apps are rebuilt for Next.js 16, and the docs were rewritten to match the current code.

🌟 Highlights

🐛 nextScriptProps no longer drops beforeInteractive

Before this release, nextScriptProps defaulted to { strategy: 'beforeInteractive' }, so passing any props replaced the whole object. Passing only an id was enough to lose the strategy. Next then fell back to afterInteractive, and client components hydrated before window.__ENV existed, so they read undefined. EnvScript now sets strategy="beforeInteractive" on <Script> and spreads nextScriptProps over it. <EnvScript nextScriptProps={{ id: 'runtime-env' }} /> keeps the correct strategy, and a regression test covers this case.

📦 require() support and a smaller tarball

The exports map had only an import condition, so require('next-dynenv') threw ERR_PACKAGE_PATH_NOT_EXPORTED. Next compiles next.config.ts to CommonJS, so importing the library there failed. A default condition now lets require(esm) load it on Node 20.19+ and 22.12+. A files: ["build"] allowlist and a new tsconfig.build.json keep spec files, test-setup.js, and inline source maps out of the tarball, which drops it from 59 files to 45. The package also declares "sideEffects": false, so bundlers can drop the server components from client bundles that only import env().

🐛 Parser and makeEnvPublic fixes

  • envParsers.number returns the default for empty or whitespace-only values. It used to return 0 because Number('') is 0. It now throws for non-finite values such as Infinity.
  • envParsers.boolean trims surrounding whitespace before matching, so ' true ' is true. Any other defined value, including an empty string, is still false. The default only applies when the variable is unset.
  • makeEnvPublic now prefixes variables that are set to an empty string. It used to skip them and warn that they were missing.

🐛 Prerender bailouts are no longer swallowed

When EnvScript read the nonce from a header, it caught any error containing Dynamic server usage. That error is how Next.js marks a route as dynamic during prerendering, so it now propagates. The only error that still means "render without a nonce" is outside a request scope (CLI scripts, tests).

♻️ Example apps rebuilt for Next.js 16

examples/with-app-router-context and examples/with-app-router-script are now small runnable demos. Each has a client component, a server component, and a route handler.

  • The script example moves its CSP nonce from middleware.ts to proxy.ts, following the Next CSP guide.
  • Server pages call connection(), and makeEnvPublic runs in instrumentation.ts, so it also works in standalone builds.
  • next.config.js is replaced by next.config.ts. Removed experimental flags and next lint are gone.
  • The .env.example files now match the variables the apps read.

📝 Documentation

  • Every guide, API page, example page, and the README was checked against src and rewritten where it had drifted. Fixed problems include:
    • samples that imported the unexported getPublicEnv
    • makeEnvPublic documented as a build-time next.config call
    • wrong parser semantics
    • env() listed as never throwing
    • afterInteractive/onLoad examples passed through nextScriptProps
  • New pages: docs/api/server-only.md, docs/api/make-env-public.md, and docs/guide/troubleshooting.md.
  • The security guide and JSDoc now explain that client components also render on the server. There, env() and serverOnly() return the real value of a non-public key, which would end up in the HTML.
  • JSDoc for makeEnvPublic says to call it from register() in instrumentation.ts. Standalone builds (output: 'standalone') never run next.config at runtime.
  • JSDoc for disableNextScript explains when to use it: code in instrumentation-client.ts (for example a Sentry init) runs before beforeInteractive scripts. When this option is on, pass nonce explicitly under a CSP.
  • The old note that NonceConfig was blocked by Next.js PR #58129 is gone. nonce={{ headerKey: 'x-nonce' }} is documented with a header set in proxy.ts.
  • The env() browser error now suggests makeEnvPublic in instrumentation.ts instead of next.config.js.
  • The VitePress theme uses the SilkCircuit Neon and Dawn palettes and code themes, adds a hero mark, defaults to dark mode, and fixes broken favicon and og-image links.
  • CHANGELOG.md now points to GitHub Releases.

⬆️ Toolchain and Dependencies

  • The workspace moves to pnpm 12.16. pnpm 11 stopped reading the pnpm field in package.json, so the old glob and brace-expansion overrides had been silently ignored. Overrides now live in pnpm-workspace.yaml.
  • Catalog bumps: Next 16.4.3, React 19.3.1, TypeScript 7.0.2, Vitest 5.0.5, jsdom 30, @types/node 26, Biome 2.6.4, Prettier 3.9.10.
  • semantic-release and audit-ci are removed. Their dependency trees carried most of the 184 advisories that pnpm audit reported, and it now reports none.
  • docs/ and both examples are now workspace members that use one root lockfile. Their separate pnpm-lock.yaml files are deleted.
  • tsconfig.json is now typecheck-only and covers all of src, including the six spec files that pnpm typecheck previously skipped.
  • Removed: the unused .npmignore, scripts/resolve-catalog.mjs, scripts/upgrade_deps.sh, and the TypeScript 6 SDK path.

👷 CI and Release Pipeline

  • CI runs on Node 26, and every GitHub Action moves to its latest major version. A local composite action (.github/actions/setup/action.yml) replaces the setup steps each job used to repeat.
  • The security job runs pnpm audit across all dependencies, dev included. Advisories of any severity fail CI.
  • A new Examples job builds both example apps against the freshly built package.
  • Releases are packed with pnpm pack, which resolves catalog: and workspace: natively, and published to npm with provenance. Packing runs before the version bump is committed or tagged, so a pack failure no longer leaves an orphaned tag.
  • The publish job no longer caches dependencies, following setup-node's trusted publishing guidance.
  • Release notes are generated with git-iris.

✅ Testing

  • The integration test no longer marks next-dynenv as a serverExternalPackages entry, so it now renders the library through Next's bundler. That is how most apps consume it and the path that sideEffects: false affects.
  • The integration test now polls for server readiness instead of assuming a fixed startup time.
  • New regression tests cover the nextScriptProps strategy merge, the propagated Dynamic server usage error, and the parser and makeEnvPublic edge cases.

Behavior Changes to Review

No public APIs were removed, but some edge cases now behave differently:

  • envParsers.number:
    • An empty or whitespace-only variable now returns defaultValue instead of 0.
    • Infinity and -Infinity now throw.
  • envParsers.boolean: values with surrounding whitespace are trimmed before matching, so ' true ' now returns true.
  • makeEnvPublic: a variable set to '' is now copied to NEXT_PUBLIC_* instead of being skipped with a warning.
  • EnvScript / PublicEnvScript with nonce={{ headerKey }}: Dynamic server usage errors are no longer swallowed during prerendering. Next.js now treats the route as dynamic instead of rendering it statically without a nonce.
  • nextScriptProps: these props are now merged over strategy: 'beforeInteractive' instead of replacing it. If you passed props without a strategy, the script now loads beforeInteractive.

Upgrade Notes

  • Move makeEnvPublic calls from next.config into register() in instrumentation.ts if you deploy with output: 'standalone'.
  • If you set nextScriptProps.strategy to something other than beforeInteractive, remove it. Client components can read undefined with a later strategy.
  • If you use disableNextScript under a CSP, pass nonce explicitly.
  • CommonJS consumers, including a compiled next.config.ts, can now require('next-dynenv') on Node 20.19+ or 22.12+.
  • The peer dependency range is unchanged: next ^15 || ^16, react ^19, and Node >=20.9.0.

v4.0.6

Choose a tag to compare

@github-actions github-actions released this 07 Apr 07:49

Released: 2026-04-07

This release focuses on dependency modernization and npm publishing reliability. TypeScript is upgraded to 6.0, all CI infrastructure moves to Node 24 with the latest GitHub Actions, and a new prepack script fixes a packaging bug where raw catalog: protocol references were shipped to npm.

✨ Highlights

npm Compatibility Fix for pnpm Catalog Users

Published packages previously contained raw catalog: protocol references in package.json that npm cannot resolve, breaking npm install next-dynenv. A new scripts/resolve-catalog.mjs prepack script now resolves catalog references from pnpm-workspace.yaml before publishing and restores the original file via postpack. This closes #1.

TypeScript 6.0 Upgrade

The project now builds with TypeScript ^6.0.2, up from 5.9. This brings the latest type-checking improvements and language features to the codebase.

Node 24 and GitHub Actions Refresh

CI, docs, and release workflows all move to Node 24 and the latest action versions. The release workflow also switches to OIDC trusted publishing with npm publish --provenance, replacing the previous npm token-based approach and removing a broken npm install -g npm@latest self-upgrade step.

🔧 Packaging & Publishing

  • Added prepack / postpack lifecycle scripts in package.json that back up package.json, resolve all catalog: references to concrete version specifiers, then restore the original after packing
  • New scripts/resolve-catalog.mjs parses pnpm-workspace.yaml and rewrites dependencies, devDependencies, peerDependencies, and optionalDependencies in-place
  • Moved react from dependencies to devDependencies — it was already declared as a peerDependency, so shipping it as a direct dependency was unnecessary
  • Updated .npmignore to exclude /scripts, package.json.bak, and .claude from the published tarball

⬆️ Dependency Updates

Package Previous Current
typescript ^5.9.3 ^6.0.2
next ^16.0.10 ^16.2.2
react / react-dom ^19.2.0 ^19.2.4
vitest / @vitest/coverage-v8 ^3.2.4 ^4.1.2
@biomejs/biome ^2.2.6 ^2.4.10
prettier ^3.5.3 ^3.8.1
jsdom ^26.1.0 ^29.0.2
@types/node ^24.7.2 ^25.5.2

The next update to ^16.2.2 includes a fix for CVE-2025-59471. The Biome config schema in biome.jsonc is updated to match the new 2.4.10 version.

👷 CI & Infrastructure

  • All workflows now target Node 24 (was 22)
  • GitHub Actions version bumps across all workflows:
    • actions/checkout v4 → v6
    • actions/setup-node v4 → v6
    • pnpm/action-setup v4 → v5
    • actions/upload-artifact v4 → v6
    • actions/download-artifact v4 → v7
    • actions/upload-pages-artifact v3 → v4
  • Release workflow: removed the npm install -g npm@latest step that was failing with a MODULE_NOT_FOUND error for promise-retry on newer runners
  • Release workflow: npm publish now includes the --provenance flag for OIDC trusted publishing, providing cryptographic proof of build origin without relying on stored npm tokens

📝 Documentation

  • Minor formatting fix in README.md — removed a stray blank line in the server-side contexts section

Upgrade Notes

  • No API changes — this is a drop-in upgrade from v4.0.5
  • If you vendor or pin the TypeScript version in your project, note that next-dynenv now targets TypeScript 6.0 for its own build; your project can still use its own TypeScript version
  • The react peer dependency requirement is unchanged; the move from dependencies to devDependencies only affects the published package metadata

v4.0.5

Choose a tag to compare

@github-actions github-actions released this 23 Dec 19:52

Full Changelog: v4.0.4...v4.0.5

v4.0.4

Choose a tag to compare

@github-actions github-actions released this 23 Dec 19:44

Full Changelog: v4.0.3...v4.0.4

v4.0.3

Choose a tag to compare

@github-actions github-actions released this 23 Dec 19:38

Full Changelog: v4.0.2...v4.0.3

v4.0.2

Choose a tag to compare

@github-actions github-actions released this 17 Dec 03:53

Release Notes v4.0.2

This release introduces serverOnly() for safe isomorphic access to server secrets and modernizes dependency management with pnpm catalog.

🌟 Highlights

✨ New serverOnly() Function for Isomorphic Server Secrets

Access server-only environment variables without throwing in browser contexts. Unlike env(), the new serverOnly() function gracefully returns a fallback value when called from client code, enabling cleaner isomorphic patterns.

import { serverOnly } from 'next-dynenv'

// Returns actual value on server, fallback on client
const dbUrl = serverOnly('DATABASE_URL', 'postgresql://localhost:5432/dev')
const apiSecret = serverOnly('API_SECRET_KEY') // undefined on client

This is particularly useful for shared configuration modules with lazy evaluation patterns using Zod schemas.

📦 Centralized Dependency Versions with pnpm Catalog

Dependency versions are now centralized in pnpm-workspace.yaml using the catalog: protocol. This ensures consistent versions across the workspace and simplifies version bumps.

✨ Features

  • serverOnly() function — New export in src/script/env.ts that reads non-public environment variables without throwing in browser contexts. Returns the fallback value when window exists, reads process.env on the server.

  • pnpm catalog support — All dependencies in package.json and test-app/package.json now use catalog: references pointing to versions defined in pnpm-workspace.yaml.

  • next moved to devDependencies — The next package is only needed for development and testing, not as a runtime dependency of the published package.

🔧 Improvements

  • pnpm version via packageManager field — Upgraded to pnpm 10.26.0 and removed explicit PNPM_VERSION env variables from CI workflows. The pnpm/action-setup@v4 action automatically detects the version from the packageManager field in package.json.

  • Security override cleanup — Removed the js-yaml security override from pnpm config as the vulnerability in transitive dependencies has been resolved upstream.

  • Security overrides for transitive deps — Added pnpm overrides for glob (>=10.5.0) and brace-expansion (>=2.0.2) to address security vulnerabilities.

v4.0.1

Choose a tag to compare

@github-actions github-actions released this 10 Dec 08:41

next-dynenv v4.0.1 ✨

Build once, deploy anywhere. This major release brings full Next.js 15/16 support, a complete rebrand, and powerful new features for type-safe runtime environment variables.


🎯 Highlights

  • Next.js 15 & 16 Ready — Full compatibility with the latest Next.js and React 19
  • New Identity — Rebranded from next-runtime-env to next-dynenv
  • Type-Safe Parsers — Boolean, number, array, JSON, URL, and enum parsers with full TypeScript inference
  • Enhanced Security — XSS protection and frozen window.__ENV object
  • Modern Tooling — ESM-first, Biome + Prettier, Vitest test framework
  • Trusted Publishing — OIDC-based npm publishing with provenance

⬆️ Next.js 15/16 Migration

New Dynamic Rendering APIs

Replaced deprecated unstable_noStore with modern Next.js APIs:

// PublicEnvScript & PublicEnvProvider now use:
import { connection } from 'next/server'
await connection()

// env() uses headers() as a side-effect for dynamic rendering
import { headers } from 'next/headers'

Async Server Components

All server components are now properly async for React 19:

// Before (Next.js 14)
export function MyComponent() {
  const apiUrl = env('API_URL')
  return <div>{apiUrl}</div>
}

// After (Next.js 15+)
export async function MyComponent() {
  const apiUrl = env('API_URL')
  return <div>{apiUrl}</div>
}

ESM Package

The package is now native ESM with "type": "module":

{
  "type": "module",
  "exports": {
    ".": {
      "types": "./build/index.d.ts",
      "import": "./build/index.js"
    }
  }
}

🔒 Type-Safe Environment Parsers

New envParsers module for validated, type-safe environment variables:

import { env, envParsers } from 'next-dynenv'

// Boolean parsing
const debug = envParsers.boolean(env('DEBUG'))  // true/false

// Number parsing
const port = envParsers.number(env('PORT'))  // number

// Array parsing
const hosts = envParsers.array(env('ALLOWED_HOSTS'))  // string[]

// JSON parsing with type inference
const config = envParsers.json<AppConfig>(env('CONFIG'))

// URL validation
const apiUrl = envParsers.url(env('API_URL'))  // URL object

// ✨ NEW: Enum validation
type Env = 'development' | 'staging' | 'production'
const nodeEnv = envParsers.enum<Env>(env('NODE_ENV'), [
  'development', 'staging', 'production'
])

Required Variables with requireEnv()

import { requireEnv } from 'next-dynenv'

// Throws if undefined
const apiKey = requireEnv('API_KEY')

Default Values

import { env } from 'next-dynenv'

// Returns default if undefined
const timeout = env('TIMEOUT', '5000')

🛡️ Security Enhancements

XSS Protection

Script injection is now protected against </script> attacks:

// Malicious input: "</script><script>alert('xss')</script>"
// Safely escaped in the injected script tag

Frozen Environment Object

window.__ENV is now frozen to prevent tampering:

// Attempting to modify throws in strict mode
window.__ENV.API_KEY = 'hacked'  // ❌ TypeError

📦 Package Rebrand

The package has been renamed for clarity and discoverability:

- npm install next-runtime-env
+ npm install next-dynenv
- import { env } from 'next-runtime-env'
+ import { env } from 'next-dynenv'

🧪 Testing Infrastructure

Vitest Migration

Replaced Jest with Vitest for faster, ESM-native testing:

pnpm test          # Run tests
pnpm test:watch    # Watch mode
pnpm test:coverage # Coverage report

Integration Tests

New end-to-end integration test validates the core promise:

pnpm test:integration

Verifies:

  • ✓ Runtime values override build-time values
  • ✓ window.__ENV injection works correctly
  • ✓ Standalone mode fully functional
  • ✓ Both script and context provider approaches work

🔧 Modern Tooling

Biome + Prettier

  • Biome — Fast Rust-based linting and formatting for TypeScript/JavaScript
  • Prettier — Markdown, YAML, and JSON formatting only
  • 10x faster linting, unified tooling, 12 fewer dependencies
pnpm lint      # Check
pnpm lint:fix  # Auto-fix

CI/CD Overhaul

  • Parallel job execution for faster builds
  • Matrix testing across Node 18, 20, 22
  • Codecov integration
  • GitHub Actions summaries
  • Trusted npm publishing with OIDC provenance

📚 Documentation

New VitePress Site

Beautiful documentation with:

  • Getting Started guide
  • API reference
  • Deployment examples (Docker, Kubernetes, Vercel, Netlify)
  • Troubleshooting guide

AI-Ready

Added llms.txt generation for AI/LLM contextual understanding.


💔 Breaking Changes

Node.js Requirement

{
  "engines": {
    "node": ">=20.9.0"
  }
}

Peer Dependencies

{
  "peerDependencies": {
    "next": "^15 || ^16",
    "react": "^19"
  }
}

Package Name

Update your imports:

- import { env, PublicEnvScript } from 'next-runtime-env'
+ import { env, PublicEnvScript } from 'next-dynenv'

🙏 Attribution

This project is a fork of next-runtime-env by Expatfile.tax LLC. All credit for the original implementation goes to them. This fork exists to provide continued maintenance and Next.js 15+ support.


📋 Full Changelog

Features

  • ✨ Migrate to Next.js 15 dynamic rendering APIs
  • ✨ Add type-safe env parsers (boolean, number, array, json, url)
  • ✨ Add enum parser for validated environment variables
  • ✨ Add requireEnv() for required variables
  • ✨ Add default value support to env()
  • 🔒 Add XSS protection for script injection
  • 🔒 Freeze window.__ENV with Object.freeze()

Infrastructure

  • ⬆️ Upgrade to Next.js 16 with ESM module system
  • 📦 Rename package to next-dynenv
  • ✅ Migrate from Jest to Vitest
  • ⚡ Modernize tooling with Biome + Prettier
  • ♻️ Refactor CI workflows for parallel execution
  • 🔧 Add pnpm workspace configuration
  • 🧪 Add integration test with real Next.js app

Documentation

  • 📚 Add VitePress documentation site
  • 📝 Comprehensive documentation overhaul
  • 📝 Add llms.txt plugin for AI context
  • 📝 Improve SEO metadata

Fixes

  • 🐛 Fix client-side bundling issue in env-script component
  • 🐛 Improve error handling for headers() outside request scope
  • 🐛 Fix makeEnvPublic() to prevent double-prefixing

Full Commit History: de82ca4...HEAD