Skip to content

fix(arkenv): prevent in-place mutation of config objects during coercion#1206

Merged
yamcodes merged 16 commits into
devfrom
fix/coercion-safety
Jun 15, 2026
Merged

fix(arkenv): prevent in-place mutation of config objects during coercion#1206
yamcodes merged 16 commits into
devfrom
fix/coercion-safety

Conversation

@yamcodes

Copy link
Copy Markdown
Owner

Description

This PR refactors applyCoercion to perform non-mutating updates on environment configuration data by recursively cloning/structurally sharing objects and arrays along the validation target path instead of modifying the existing ones in-place.

This fixes:

  1. Runtime crashes when parsing frozen or read-only configuration objects (such as Nuxt's runtimeConfig).
  2. Silent side-effects caused by in-place mutations on reused configuration objects.
  3. Restores configuration safety when using Record<string, unknown> environments.

Testing Done

Added comprehensive unit tests in packages/arkenv/src/coercion/shared.test.ts to assert:

  • Successful coercion of frozen/read-only objects without crashes.
  • Reference integrity (non-mutation) of original objects.
  • Correct structural sharing on unmodified sibling/nested paths.
  • Clean array coercion without in-place mutation.

All 69 test files (701 tests) in the workspace pass successfully.

@pullfrog

pullfrog Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Run failed. View the logs →

Pullfrog  | Rerun failed job ➔View workflow run | via Pullfrog | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@github-actions github-actions Bot added docs Adds or changes documentation, or acts as documentation in and of itself arkenv Changes to the `arkenv` npm package. tests This issue or PR is about adding, removing or changing tests labels Jun 14, 2026
@changeset-bot

changeset-bot Bot commented Jun 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: e8bc799

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
arkenv Patch
@arkenv/nuxt Patch
@arkenv/cli Patch
@arkenv/build Patch
@arkenv/nextjs Minor
@arkenv/bun-plugin Patch
@arkenv/vite-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@arkenv-bot

arkenv-bot Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (Asia/Almaty)
arkenv Ready Ready Preview, Comment Jun 15 2026, 9:59 AM (Asia/Almaty)

@pkg-pr-new

pkg-pr-new Bot commented Jun 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

arkenv

npm i https://pkg.pr.new/yamcodes/arkenv@1206

@arkenv/build

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/build@1206

@arkenv/bun-plugin

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/bun-plugin@1206

@arkenv/cli

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/cli@1206

@arkenv/fumadocs-ui

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/fumadocs-ui@1206

@arkenv/nextjs

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/nextjs@1206

@arkenv/nuxt

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/nuxt@1206

@arkenv/vite-plugin

npm i https://pkg.pr.new/yamcodes/arkenv/@arkenv/vite-plugin@1206

commit: e8bc799

@arkenv-bot

arkenv-bot Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Size Report

Package Size Limit Diff Status
arkenv 2.02 kB 2.05 kB +3.6%
arkenv/standard 2.19 kB 2.25 kB +3.3%
arkenv/core 441 B 500 B 0.0%
@arkenv/nextjs 1002 B 2.93 kB <0.1%
@arkenv/nextjs/shared 1018 B 1.46 kB -0.4%
@arkenv/nextjs/server 1.05 kB 1.95 kB -0.9%
@arkenv/nextjs/client 1.04 kB 1.95 kB -1.0%
@arkenv/nextjs/config 2.42 kB 2.93 kB 0.0%

All size limits passed!

@yamcodes
yamcodes force-pushed the fix/coercion-safety branch from bd132eb to f07b397 Compare June 14, 2026 19:05
@yamcodes
yamcodes force-pushed the fix/coercion-safety branch from 532df49 to 910dc5e Compare June 14, 2026 19:07

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

Object.hasOwn breaks TypeScript compilation at the current es2020 target. The old code explicitly handled this with the prototype-based call and a biome suppression comment noting the limitation.

Reviewed changes — this PR refactors applyCoercion from in-place mutation to a non-mutating functional pattern, preventing crashes on frozen/read-only config objects and eliminating side-effects on reusable configs.

  • Replace walk with updateAtPath — the new pure function clones objects and arrays along validated paths only when a descendant value changes, otherwise returns the same reference for structural sharing.
  • Rewrite coerceValue primitive-array coercion.map() replaces the in-place for loop so frozen arrays are safe.
  • Add safety and non-mutation tests — covers frozen objects, reference integrity, structural sharing of untouched subtrees, and array coercion without mutation.
  • Bump size-limit from 2 kB to 2.1 kB to accommodate the non-mutating coercion logic.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

Comment thread packages/arkenv/src/coercion/shared.ts

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — the new commit adds "lib": ["es2020", "es2022.object"] to tsconfig.json, resolving the Object.hasOwn TS2550 error. es2022.object is the minimal lib needed — it provides Object.hasOwn without pulling in unrelated ES2022 features.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@github-actions github-actions Bot added @arkenv/nextjs Issues or Pull Requests involving the Next.js integration for ArkEnv @arkenv/nuxt Issues or Pull Requests involving the Nuxt integration for ArkEnv labels Jun 15, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed changes — since the last approved review, this PR expanded to include Dict<string> type standardization across all packages and a substantial Nuxt runtimeEnv refactor.

  • Standardized environment variable types to Dict<string> — replaced Record<string, string | undefined> and Record<string, unknown> with Dict<string> in core create-env.ts, parse-standard.ts, infer-type.ts, and all Next.js/Nuxt plugin files
  • Removed runtimeEnv option from Nuxt createEnv — replaced with sourceEnv auto-detection from process.env and Nuxt's globalConfig; removed skipDestructureCheck validation; removed runtimeEnv from all three type-safety overloads (client.ts, server.ts, shared.ts)
  • Removed String() and as string casts in Next.js createEnvInternalruntimeEnv, extendedEnvValues, and combinedEnv are now typed as Dict<string>, making explicit string conversion redundant
  • Removed runtimeEnv from Nuxt legacy detectionisLegacy now only checks for "client" or "shared" in the options, not runtimeEnv
  • Widened stripEmptyStrings signature — from Record<string, string | undefined> to Record<string, unknown>
  • Bumped size limit — from 2 kB to 2.1 kB in packages/arkenv/package.json

⚠️ Nuxt runtimeEnv removal is a breaking change with stale tests

The runtimeEnv option was removed from all three Nuxt type-safety overloads (client.ts, server.ts, shared.ts) and from the createEnvInternal implementation. Callers that were explicitly passing runtimeEnv to Nuxt's createEnv will get type errors and the provided runtimeEnv values are silently ignored (replaced by sourceEnv auto-detection).

The Nuxt test suite still references runtimeEnv in every test case (packages/nuxt/src/index.test.ts lines 10, 25, 43, 61, 82, 111, 159) — these tests are now stale and will fail because runtimeEnv` is no longer read from the options object.

The changeset (.changeset/fix-coercion-in-place-mutation.md) only covers the coercion fix — there is no changeset for this breaking change.

Technical details
# Nuxt `runtimeEnv` removal — breaking change and stale tests

## Affected sites
- `packages/nuxt/src/client.ts:24-26``runtimeEnv` removed from 1st overload signature
- `packages/nuxt/src/client.ts:37-38``runtimeEnv` removed from 2nd overload signature
- `packages/nuxt/src/client.ts:47``isLegacy` check no longer considers `runtimeEnv`
- `packages/nuxt/src/server.ts:23,35``runtimeEnv` removed from both overloads
- `packages/nuxt/src/shared.ts:22``runtimeEnv` removed from overload
- `packages/nuxt/src/create-env.ts:34-37``sourceEnv` auto-detection replaces `runtimeEnv`
- `packages/nuxt/src/create-env.ts:107-110` — extend loop reads from `sourceEnv` instead of `runtimeEnv`
- `packages/nuxt/src/create-env.ts:171-174` — final combinedEnv reads from `sourceEnv`
- `packages/nuxt/src/index.test.ts:10,25,43,61,82,111,159` — tests still pass `runtimeEnv`

## Required outcome
- Add a changeset documenting the `runtimeEnv` removal as a breaking change for `@arkenv/nuxt`
- Update the Nuxt test suite to use `sourceEnv`-compatible test data (e.g., mutate `process.env` before `createEnv` calls)
- Remove the stale `runtimeEnv` references from test cases

## Suggested approach
The Nuxt module already populates `nuxt.options.runtimeConfig`, so `sourceEnv` auto-detection is the correct direction. Tests should either:
1. Set `process.env` keys before calling `createEnv` to simulate `sourceEnv` detection, or
2. Restore a `runtimeEnv` override parameter for testing (lower priority than `sourceEnv`)

## Open questions for the human
- Is the removal of `runtimeEnv` from Nuxt intended to be a breaking change, or should it be restored as an override option?
- Should the Nuxt entry points expose a testing-only API for injecting runtime env values?

Pullfrog  | Fix it ➔View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@yamcodes
yamcodes enabled auto-merge (squash) June 15, 2026 04:58
@yamcodes
yamcodes merged commit 12ed4f3 into dev Jun 15, 2026
22 checks passed
@yamcodes
yamcodes deleted the fix/coercion-safety branch June 15, 2026 05:01

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ One minor nit — otherwise the changes cleanly address the prior review feedback.

Reviewed changes — since the last review, runtimeEnv was fully removed from the Nuxt adapter and tests were updated to use process.env / window.__NUXT__.

  • Removed runtimeEnv from server.ts and shared.ts overloads — matching the earlier client.ts removal, all Nuxt entry points now omit runtimeEnv from their type signatures.
  • Introduced server/client-aware sourceEnv — on the server, only process.env is consulted; on the client, window.__NUXT__.config.public takes priority with a process.env fallback.
  • Removed skipDestructureCheck validation block — the runtime check that verified all client/shared keys were present in runtimeEnv is gone, since sourceEnv auto-detection replaces it.
  • Updated Nuxt test suite — all tests now set process.env keys or mock window.__NUXT__ instead of passing runtimeEnv; removed the now-irrelevant "missing key in runtimeEnv" and "fallback to process.env" tests; added try/finally window restoration in client-side tests.

Note: 1 inline comment(s) dropped because they did not anchor to lines inside the PR diff:

  • packages/nuxt/src/server.ts:42 (RIGHT) — line 42 (RIGHT) is not inside a diff hunk

Pullfrog  | Fix it ➔View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@arkenv-bot arkenv-bot Bot mentioned this pull request Jun 15, 2026
yamcodes pushed a commit that referenced this pull request Jun 15, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to dev, this PR will
be updated.


# Releases
## @arkenv/nextjs@0.1.0

### Minor Changes

- #### Enforce strict intersection typing on `runtimeEnv` and reject
legacy configs _[`#1206`](#1206)
[`12ed4f3`](12ed4f3)
[@yamcodes](https://github.com/yamcodes)_

Restored strict intersection types (`Record<RequiredKeys, unknown> &
Record<string, unknown>`) on the Next.js `createEnv` adapter to
guarantee compile-time enforcement of required schema keys.
Additionally, narrowed the accepted `runtimeEnv` record value type to
`string | undefined` to actively reject invalid configurations.

**BREAKING CHANGE**: If you were using the legacy Next.js `env` object
configuration (e.g., passing a nested object to `runtimeEnv`), or if you
were failing to explicitly map all required keys into `runtimeEnv`, your
build will now fail with a TypeScript error. You must explicitly map all
variables referenced in your schema as `string | undefined`.

  Usage:

  ```ts
  import { createEnv } from "@arkenv/nextjs";

  export const env = createEnv({
    client: { NEXT_PUBLIC_API: "string" },
    runtimeEnv: {
      // TypeScript will error if NEXT_PUBLIC_API is missing,
      // and will also error if you try to pass an object or array.
      NEXT_PUBLIC_API: process.env.NEXT_PUBLIC_API,
    },
  });
  ```

### Patch Changes

<details><summary>Updated 1 dependency</summary>

<small>


[`a3e32db`](a3e32db)
[`12ed4f3`](12ed4f3)
[`12ed4f3`](12ed4f3)

</small>

- `arkenv@0.12.2`

</details>
## arkenv@0.12.2

### Patch Changes

- #### Expose `getSchemaKeys` helper
_[`#1191`](#1191)
[`a3e32db`](a3e32db)
[@yamcodes](https://github.com/yamcodes)_

Expose `getSchemaKeys` helper and widen `RuntimeEnvironment` type to
support typed framework runtime configurations.

- #### Handle pre-parsed primitives gracefully in coercion logic
_[`#1206`](#1206)
[`12ed4f3`](12ed4f3)
[@yamcodes](https://github.com/yamcodes)_

Updated the core coercion logic to defensively skip values that are not
strings. This ensures ArkEnv won't crash or behave unpredictably when
passed configuration objects that have already been parsed into numbers,
booleans, or complex objects by other systems (such as Nuxt's
`runtimeConfig`).

- #### Fix coercion to prevent in-place mutation of environment config
objects _[`#1206`](#1206)
[`12ed4f3`](12ed4f3)
[@yamcodes](https://github.com/yamcodes)_

Refactor `applyCoercion` to perform non-mutating updates on environment
configuration data. Clone objects and arrays along the validation path
instead of modifying them in-place, preventing runtime crashes on frozen
configuration objects (e.g., framework runtime configurations) and
avoiding side-effects on reusable config objects.

  Usage:

  ```ts
  import { createEnv } from "arkenv";

  const env = createEnv(
    { DATABASE: { port: "number" } },
{ env: { DATABASE: Object.freeze({ port: "3000" }) } } // Frozen object
is now safely parsed without crashes!
  );
  ```
## @arkenv/build@0.0.1

### Patch Changes

- #### Introduce Nuxt support
_[`#1191`](#1191)
[`a3e32db`](a3e32db)
[@yamcodes](https://github.com/yamcodes)_

Introduce `@arkenv/nuxt` integration package providing a Nuxt module for
automatic environment variable validation and runtimeConfig mapping, and
add Nuxt support to the CLI scaffold wizard. The Nuxt adapter elegantly
embraces Nuxt's native configuration exposure by automatically reading
from `window.__NUXT__.config.public`, eliminating the need for
developers to manually pass a `runtimeEnv` or `useRuntimeConfig()` map.
## @arkenv/bun-plugin@0.1.10

### Patch Changes

<details><summary>Updated 1 dependency</summary>

<small>


[`a3e32db`](a3e32db)
[`12ed4f3`](12ed4f3)
[`12ed4f3`](12ed4f3)

</small>

- `arkenv@0.12.2`

</details>
## @arkenv/cli@0.2.11

### Patch Changes

- #### Introduce Nuxt support
_[`#1191`](#1191)
[`a3e32db`](a3e32db)
[@yamcodes](https://github.com/yamcodes)_

Introduce `@arkenv/nuxt` integration package providing a Nuxt module for
automatic environment variable validation and runtimeConfig mapping, and
add Nuxt support to the CLI scaffold wizard. The Nuxt adapter elegantly
embraces Nuxt's native configuration exposure by automatically reading
from `window.__NUXT__.config.public`, eliminating the need for
developers to manually pass a `runtimeEnv` or `useRuntimeConfig()` map.
## @arkenv/nuxt@0.0.1

### Patch Changes

- #### Introduce Nuxt support
_[`#1191`](#1191)
[`a3e32db`](a3e32db)
[@yamcodes](https://github.com/yamcodes)_

Introduce `@arkenv/nuxt` integration package providing a Nuxt module for
automatic environment variable validation and runtimeConfig mapping, and
add Nuxt support to the CLI scaffold wizard. The Nuxt adapter elegantly
embraces Nuxt's native configuration exposure by automatically reading
from `window.__NUXT__.config.public`, eliminating the need for
developers to manually pass a `runtimeEnv` or `useRuntimeConfig()` map.

<details><summary>Updated 2 dependencies</summary>

<small>


[`a3e32db`](a3e32db)
[`12ed4f3`](12ed4f3)
[`12ed4f3`](12ed4f3)
[`a3e32db`](a3e32db)

</small>

- `arkenv@0.12.2`
- `@arkenv/build@0.0.1`

</details>
## @arkenv/vite-plugin@0.1.4

### Patch Changes

<details><summary>Updated 1 dependency</summary>

<small>


[`a3e32db`](a3e32db)
[`12ed4f3`](12ed4f3)
[`12ed4f3`](12ed4f3)

</small>

- `arkenv@0.12.2`

</details>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
renovate Bot added a commit to gwennlbh/pleye that referenced this pull request Jul 20, 2026
##### [v0.12.2](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.12.2)

##### Patch Changes

- #### Expose `getSchemaKeys` helper *[`#1191`](yamcodes/arkenv#1191) [`a3e32db`](yamcodes/arkenv@a3e32db) [@yamcodes](https://github.com/yamcodes)*

  Expose `getSchemaKeys` helper and widen `RuntimeEnvironment` type to support typed framework runtime configurations.

- #### Handle pre-parsed primitives gracefully in coercion logic *[`#1206`](yamcodes/arkenv#1206) [`12ed4f3`](yamcodes/arkenv@12ed4f3) [@yamcodes](https://github.com/yamcodes)*

  Updated the core coercion logic to defensively skip values that are not strings. This ensures ArkEnv won't crash or behave unpredictably when passed configuration objects that have already been parsed into numbers, booleans, or complex objects by other systems (such as Nuxt's `runtimeConfig`).

- #### Fix coercion to prevent in-place mutation of environment config objects *[`#1206`](yamcodes/arkenv#1206) [`12ed4f3`](yamcodes/arkenv@12ed4f3) [@yamcodes](https://github.com/yamcodes)*

  Refactor `applyCoercion` to perform non-mutating updates on environment configuration data. Clone objects and arrays along the validation path instead of modifying them in-place, preventing runtime crashes on frozen configuration objects (e.g., framework runtime configurations) and avoiding side-effects on reusable config objects.

  Usage:

  ```ts
  import { createEnv } from "arkenv";

  const env = createEnv(
    { DATABASE: { port: "number" } },
    { env: { DATABASE: Object.freeze({ port: "3000" }) } } // Frozen object is now safely parsed without crashes!
  );
  ```
##### [v0.12.1](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.12.1)

##### Patch Changes

- #### Add `emptyAsUndefined` option to treat empty env values as missing *[`#1188`](yamcodes/arkenv#1188) [`3bfbcb7`](yamcodes/arkenv@3bfbcb7) [@yamcodes](https://github.com/yamcodes)*

  Add a new `emptyAsUndefined` configuration option to both `createEnv` (ArkType mode) and `arkenv/standard` (Standard Schema mode). When enabled, environment variables set to empty strings (e.g., `PORT=` in a `.env` file) are treated as if they were missing, allowing defaults to apply and preventing unnecessary validation errors for numeric, boolean, or array types.

  **Usage:**

  ```ts
  import arkenv from "arkenv";

  const env = arkenv(
    { PORT: "number = 3000", DEBUG: "boolean = false" },
    { emptyAsUndefined: true }
  );

  // Given PORT= and DEBUG= in the environment:
  // env.PORT  → 3000
  // env.DEBUG → false
  ```

  ```ts
  import arkenv from "arkenv/standard";
  import { z } from "zod";

  const env = arkenv(
    { PORT: z.coerce.number().default(3000) },
    { emptyAsUndefined: true }
  );
  ```

  - The default behavior remains unchanged (`emptyAsUndefined: false`).
  - Keys with empty values are removed from the input record before validation so that ArkType defaults and optional types work correctly.
##### [v0.12.0](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.12.0)

##### Minor Changes

- #### Add Standard JSON Schema coercion to `arkenv/standard` *[`#1154`](yamcodes/arkenv#1154) [`88b0eee`](yamcodes/arkenv@88b0eee) [@yamcodes](https://github.com/yamcodes)*

  Introduce opt-out type coercion for standard mode (`arkenv/standard`). This coercion only works if the validator is a standard JSON Schema compliant validator (e.g., Zod, Valibot, or custom schemas that implement the `StandardJSONSchemaV1` interface). This automatically enables coercion for environment variables without relying on ArkType's runtime footprint.

  If you need to disable coercion, explicitly pass `{ coerce: false }` in your configuration:

  ```ts
  import arkenv from "arkenv/standard";
  import { z } from "zod";

  const env = arkenv({ PORT: z.number() }, { coerce: false });
  ```

  BREAKING CHANGE: Coercion is now enabled by default in `arkenv/standard`. This will automatically coerce environment variables to their expected types (e.g., strings containing numbers, booleans, or dates will be converted to their respective types) based on the JSON Schema of your validators. It's unlikely to affect you unless you were relying on validation failing for uncoerced string inputs, or have custom schemas that expect raw strings instead of coerced values.
##### [v0.11.1](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.11.1)

##### Patch Changes

- #### Add `Infer<T>` helper to resolve environment variable types *[`#1092`](yamcodes/arkenv#1092) [`c6c30ab`](yamcodes/arkenv@c6c30ab) [@yamcodes](https://github.com/yamcodes)*

  Introduce the `Infer<T>` type helper, allowing developers to extract the inferred output types of their environment schemas. It supports both declarative schema shapes and compiled schemas (like Zod or ArkType types).

  Usage:

  ```ts
  import { createEnv, type Infer } from "arkenv";
  import { type } from "arktype";

  const schema = {
    PORT: type.number,
  };

  export type Env = Infer<typeof schema>;
  ```
##### [v0.11.0](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.11.0)

##### Minor Changes

- #### Remove `ArkEnvError` import from "arkenv" *[`#815`](yamcodes/arkenv#815) [`5e8025f`](yamcodes/arkenv@5e8025f) [@yamcodes](https://github.com/yamcodes)*

  The `ArkEnvError` class is now only available via:

  ```ts
  import { ArkEnvError } from "arkenv/core";
  ```
##### [v0.10.0](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.10.0)

##### Minor Changes

- #### `arkenv/standard` import *[`#806`](yamcodes/arkenv#806) [`f9010d0`](yamcodes/arkenv@f9010d0) [@yamcodes](https://github.com/yamcodes)*

  `arkenv` now ships three separate entry points:

  - **`arkenv`** (main): ArkType-first. Includes `createEnv` (aliased to `arkenv` default import), `type`, and `ArkEnvError`. Importing from this entry requires you to have ArkType installed.
  - **`arkenv/standard`**: ArkType-free. A standalone `createEnv` (aliased to `arkenv` default import) for Standard Schema validators (Zod, Valibot, etc.), not requiring ArkType.
  - **`arkenv/core`**: Mode-agnostic primitives - `ArkEnvError` and `ValidationIssue`.

##### Breaking changes

````
**1. `validator: "standard"` option removed; `arkenv` now statically requires ArkType.**
The `validator` config option has been removed - ArkType is now always required when importing from `arkenv`. For a zero-ArkType bundle, use `arkenv/standard`:

```ts
// ❌ Before
import arkenv from "arkenv";
import { z } from "zod";

const env = arkenv({ PORT: z.coerce.number() }, { validator: "standard" });

// ✅ After
import arkenv from "arkenv/standard";
import { z } from "zod";

const env = arkenv({ PORT: z.coerce.number() });
```

**2. `type` moved from `arkenv/arktype` to `arkenv`.**
The `type` helper is now exported from the main entry. The `arkenv/arktype` sub-path is no longer public:

```ts
// ❌ Before
import { type } from "arkenv/arktype";

// ✅ After
import { type } from "arkenv"; // 'type' is the ArkEnv helper, not a TS type modifier
```
````
renovate Bot added a commit to gwennlbh/pleye that referenced this pull request Jul 24, 2026
##### [v0.12.3](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.12.3)

##### Patch Changes

- #### Improve npm keywords across published packages for discoverability *[`#1383`](yamcodes/arkenv#1383) [`bf60ab2`](yamcodes/arkenv@bf60ab2) [@yamcodes](https://github.com/yamcodes)*

  Clean up and extend the `keywords` field of every published package so npm search, aggregators, and LLM-powered package discovery surface ArkEnv for the terms users actually search for.

  - Remove the misleading `pnpm` keyword from `arkenv` and add `env`, `environment-variables`, `dotenv`, `config`, `standard-schema`, and the supported validators `zod` and `valibot`.
  - Deduplicate the repeated `arkenv` keyword in `@arkenv/vite-plugin`.
  - Give every env-related package a shared baseline (`env`, `environment-variables`, `dotenv`, `config`, `validation`, `typesafe`, `standard-schema`) alongside their integration-specific terms.
  - Add a keyword set to `@arkenv/fumadocs-ui`, which previously had none.
##### [v0.12.2](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.12.2)

##### Patch Changes

- #### Expose `getSchemaKeys` helper *[`#1191`](yamcodes/arkenv#1191) [`a3e32db`](yamcodes/arkenv@a3e32db) [@yamcodes](https://github.com/yamcodes)*

  Expose `getSchemaKeys` helper and widen `RuntimeEnvironment` type to support typed framework runtime configurations.

- #### Handle pre-parsed primitives gracefully in coercion logic *[`#1206`](yamcodes/arkenv#1206) [`12ed4f3`](yamcodes/arkenv@12ed4f3) [@yamcodes](https://github.com/yamcodes)*

  Updated the core coercion logic to defensively skip values that are not strings. This ensures ArkEnv won't crash or behave unpredictably when passed configuration objects that have already been parsed into numbers, booleans, or complex objects by other systems (such as Nuxt's `runtimeConfig`).

- #### Fix coercion to prevent in-place mutation of environment config objects *[`#1206`](yamcodes/arkenv#1206) [`12ed4f3`](yamcodes/arkenv@12ed4f3) [@yamcodes](https://github.com/yamcodes)*

  Refactor `applyCoercion` to perform non-mutating updates on environment configuration data. Clone objects and arrays along the validation path instead of modifying them in-place, preventing runtime crashes on frozen configuration objects (e.g., framework runtime configurations) and avoiding side-effects on reusable config objects.

  Usage:

  ```ts
  import { createEnv } from "arkenv";

  const env = createEnv(
    { DATABASE: { port: "number" } },
    { env: { DATABASE: Object.freeze({ port: "3000" }) } } // Frozen object is now safely parsed without crashes!
  );
  ```
##### [v0.12.1](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.12.1)

##### Patch Changes

- #### Add `emptyAsUndefined` option to treat empty env values as missing *[`#1188`](yamcodes/arkenv#1188) [`3bfbcb7`](yamcodes/arkenv@3bfbcb7) [@yamcodes](https://github.com/yamcodes)*

  Add a new `emptyAsUndefined` configuration option to both `createEnv` (ArkType mode) and `arkenv/standard` (Standard Schema mode). When enabled, environment variables set to empty strings (e.g., `PORT=` in a `.env` file) are treated as if they were missing, allowing defaults to apply and preventing unnecessary validation errors for numeric, boolean, or array types.

  **Usage:**

  ```ts
  import arkenv from "arkenv";

  const env = arkenv(
    { PORT: "number = 3000", DEBUG: "boolean = false" },
    { emptyAsUndefined: true }
  );

  // Given PORT= and DEBUG= in the environment:
  // env.PORT  → 3000
  // env.DEBUG → false
  ```

  ```ts
  import arkenv from "arkenv/standard";
  import { z } from "zod";

  const env = arkenv(
    { PORT: z.coerce.number().default(3000) },
    { emptyAsUndefined: true }
  );
  ```

  - The default behavior remains unchanged (`emptyAsUndefined: false`).
  - Keys with empty values are removed from the input record before validation so that ArkType defaults and optional types work correctly.
##### [v0.12.0](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.12.0)

##### Minor Changes

- #### Add Standard JSON Schema coercion to `arkenv/standard` *[`#1154`](yamcodes/arkenv#1154) [`88b0eee`](yamcodes/arkenv@88b0eee) [@yamcodes](https://github.com/yamcodes)*

  Introduce opt-out type coercion for standard mode (`arkenv/standard`). This coercion only works if the validator is a standard JSON Schema compliant validator (e.g., Zod, Valibot, or custom schemas that implement the `StandardJSONSchemaV1` interface). This automatically enables coercion for environment variables without relying on ArkType's runtime footprint.

  If you need to disable coercion, explicitly pass `{ coerce: false }` in your configuration:

  ```ts
  import arkenv from "arkenv/standard";
  import { z } from "zod";

  const env = arkenv({ PORT: z.number() }, { coerce: false });
  ```

  BREAKING CHANGE: Coercion is now enabled by default in `arkenv/standard`. This will automatically coerce environment variables to their expected types (e.g., strings containing numbers, booleans, or dates will be converted to their respective types) based on the JSON Schema of your validators. It's unlikely to affect you unless you were relying on validation failing for uncoerced string inputs, or have custom schemas that expect raw strings instead of coerced values.
##### [v0.11.1](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.11.1)

##### Patch Changes

- #### Add `Infer<T>` helper to resolve environment variable types *[`#1092`](yamcodes/arkenv#1092) [`c6c30ab`](yamcodes/arkenv@c6c30ab) [@yamcodes](https://github.com/yamcodes)*

  Introduce the `Infer<T>` type helper, allowing developers to extract the inferred output types of their environment schemas. It supports both declarative schema shapes and compiled schemas (like Zod or ArkType types).

  Usage:

  ```ts
  import { createEnv, type Infer } from "arkenv";
  import { type } from "arktype";

  const schema = {
    PORT: type.number,
  };

  export type Env = Infer<typeof schema>;
  ```
##### [v0.11.0](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.11.0)

##### Minor Changes

- #### Remove `ArkEnvError` import from "arkenv" *[`#815`](yamcodes/arkenv#815) [`5e8025f`](yamcodes/arkenv@5e8025f) [@yamcodes](https://github.com/yamcodes)*

  The `ArkEnvError` class is now only available via:

  ```ts
  import { ArkEnvError } from "arkenv/core";
  ```
##### [v0.10.0](https://github.com/yamcodes/arkenv/releases/tag/arkenv%400.10.0)

##### Minor Changes

- #### `arkenv/standard` import *[`#806`](yamcodes/arkenv#806) [`f9010d0`](yamcodes/arkenv@f9010d0) [@yamcodes](https://github.com/yamcodes)*

  `arkenv` now ships three separate entry points:

  - **`arkenv`** (main): ArkType-first. Includes `createEnv` (aliased to `arkenv` default import), `type`, and `ArkEnvError`. Importing from this entry requires you to have ArkType installed.
  - **`arkenv/standard`**: ArkType-free. A standalone `createEnv` (aliased to `arkenv` default import) for Standard Schema validators (Zod, Valibot, etc.), not requiring ArkType.
  - **`arkenv/core`**: Mode-agnostic primitives - `ArkEnvError` and `ValidationIssue`.

##### Breaking changes

````
**1. `validator: "standard"` option removed; `arkenv` now statically requires ArkType.**
The `validator` config option has been removed - ArkType is now always required when importing from `arkenv`. For a zero-ArkType bundle, use `arkenv/standard`:

```ts
// ❌ Before
import arkenv from "arkenv";
import { z } from "zod";

const env = arkenv({ PORT: z.coerce.number() }, { validator: "standard" });

// ✅ After
import arkenv from "arkenv/standard";
import { z } from "zod";

const env = arkenv({ PORT: z.coerce.number() });
```

**2. `type` moved from `arkenv/arktype` to `arkenv`.**
The `type` helper is now exported from the main entry. The `arkenv/arktype` sub-path is no longer public:

```ts
// ❌ Before
import { type } from "arkenv/arktype";

// ✅ After
import { type } from "arkenv"; // 'type' is the ArkEnv helper, not a TS type modifier
```
````
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

@arkenv/nextjs Issues or Pull Requests involving the Next.js integration for ArkEnv @arkenv/nuxt Issues or Pull Requests involving the Nuxt integration for ArkEnv arkenv Changes to the `arkenv` npm package. docs Adds or changes documentation, or acts as documentation in and of itself tests This issue or PR is about adding, removing or changing tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant