Skip to content
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
427ced6
fix: tackle issue #709
yamcodes Jun 3, 2026
ca7c067
fix: resolve biome formatting and sync examples issues
yamcodes Jun 4, 2026
df34eed
Merge branch 'dev' into 709-unify-error-normalization-and-formatting
yamcodes Jun 5, 2026
94f275f
fix: address PR review comments on error formatters and sensitive key…
yamcodes Jun 5, 2026
083837c
fix: resolve size-limit failures by splitting redact utilities and op…
yamcodes Jun 5, 2026
b2b2e85
fix: resolve exactOptionalPropertyTypes errors in parse-standard.ts
yamcodes Jun 5, 2026
c0a5415
Merge branch 'v1' into 709-unify-error-normalization-and-formatting
yamcodes Jun 5, 2026
99c91e5
Merge branch 'v1' into 709-unify-error-normalization-and-formatting
yamcodes Jun 19, 2026
2321b1c
refactor: reorder internal imports and exports for improved consistency
yamcodes Jun 19, 2026
6a8da0c
fix: validate rawVal type in parse-standard and update bundle size li…
yamcodes Jun 19, 2026
dd03fe5
refactor: extract getProp helper and add code-review skill definition
yamcodes Jun 19, 2026
160e78f
refactor: centralize error mapping and metadata extraction for ArkTyp…
yamcodes Jun 19, 2026
f187548
fix(review): remove test file .ts extensions, restructure changeset, …
yamcodes Jun 19, 2026
2aa8a93
docs(skill): clarify that gh pr view completely omits inline code rev…
yamcodes Jun 19, 2026
52ff00c
refactor: remove redundant index file references from import paths ac…
yamcodes Jun 19, 2026
e51c73b
docs(changeset): elaborate on breaking change error message details
yamcodes Jun 19, 2026
0658e96
refactor: migrate root entrypoint imports from @/ to @ and update tsc…
yamcodes Jun 19, 2026
9b4eb34
refactor: use @ instead of @/index in dynamic import in isolation test
yamcodes Jun 19, 2026
fe83263
refactor: resolve second round of PR comments on error formatting and…
yamcodes Jun 19, 2026
40432cf
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 19, 2026
df87424
refactor: use imperative verb form and remove implementation details …
yamcodes Jun 19, 2026
e6ffea9
refactor: consolidate environment coercion logic and standardize path…
yamcodes Jun 19, 2026
4539b0a
refactor: replace Record<string, unknown> with Dict<string> in enviro…
yamcodes Jun 19, 2026
c90f532
refactor: move coerceEnvironment to dedicated module and update error…
yamcodes Jun 19, 2026
ad54a52
feat: upgrade arkenv to major version and document breaking API chang…
yamcodes Jun 19, 2026
53ba7dc
feat: import Dict type from repo types for shared coercion utilities
yamcodes Jun 20, 2026
c3fc46a
chore: add job execution logs and update package dependencies
yamcodes Jun 20, 2026
979c137
docs(review): resolve PR #1157 review comments, document safeArkEnv
yamcodes Jun 20, 2026
5d4e876
docs: document architectural design decisions regarding module isolat…
yamcodes Jun 20, 2026
aefb0d7
feat: add documentation for hiding internal comments from .d.ts files…
yamcodes Jun 20, 2026
63fa480
docs: address review comments
yamcodes Jun 21, 2026
101fbc9
refactor: update safeArkEnv return type to provide issues directly in…
yamcodes Jun 21, 2026
da4c544
refactor: replace safeArkEnv utility with a safe option in the arkenv…
yamcodes Jun 21, 2026
f922185
[autofix.ci] apply automated fixes
autofix-ci[bot] Jun 21, 2026
16e4d27
feat: add { safe: true } configuration option to arkenv for non-throw…
yamcodes Jun 21, 2026
6737534
refactor: remove engine-specific metadata from EnvIssue and deprecate…
yamcodes Jun 21, 2026
5066f2a
style: fix minor whitespace inconsistencies and clean up code formatting
yamcodes Jun 21, 2026
de18bbc
feat: disable strict validation by setting safe: false in plugin conf…
yamcodes Jun 21, 2026
43c7858
refactor: omit safe property from ArkEnvConfig in vite and bun plugin…
yamcodes Jun 21, 2026
3dd42d7
docs: clarify that the safe option is unsupported in bun and vite plu…
yamcodes Jun 21, 2026
ceb0496
docs: document the safe option and its limitations in framework integ…
yamcodes Jun 21, 2026
10a2103
refactor: enforce strict type safety for error code mappings using sa…
yamcodes Jun 21, 2026
befc6c9
fix: ensure type safety when accessing error maps by using the 'in' o…
yamcodes Jun 21, 2026
43206f2
fix: update error code mappings to support comprehensive ArkType vali…
yamcodes Jun 21, 2026
0ca680b
docs: remove safe parsing documentation from quickstart guide
yamcodes Jun 21, 2026
c9532d0
refactor: rename executeSafe to safeExecute for consistent naming con…
yamcodes Jun 21, 2026
11999c9
feat: remove safe option from integrations and enforce safe: false in…
yamcodes Jun 21, 2026
df221c2
chore: ignore job_logs.txt and clear existing log entries
yamcodes Jun 21, 2026
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
18 changes: 18 additions & 0 deletions .changeset/unified-error-normalization.md
Comment thread
yamcodes marked this conversation as resolved.
Comment thread
yamcodes marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
"arkenv": major
---

#### Refactor error system to use normalized `EnvIssue` and add `{ safe: true }` API

Introduce a unified `EnvIssue` type for programmatic access to validation issues via `ArkEnvError.issues`, and add the non-throwing `{ safe: true }` configuration option to `arkenv`.

Error messages now use ANSI colors instead of a bullet-point prefix:

```diff
- - [PORT] must be a valid port number (was "invalid-port")
+ PORT must be a valid port number (was "invalid-port")
```

Note: Header (red), variable path (yellow), and received value (cyan) are now styled with ANSI escape codes. Update any test suites asserting on exact error text.

**BREAKING CHANGE**: `ValidationIssue` and `formatInternalErrors` removed. Use `EnvIssue` and `formatIssues` instead.
2 changes: 1 addition & 1 deletion apps/www/bin/twoslash-mdx.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// biome-ignore-all lint/suspicious/noConsole: This is a CLI debugging script
import fs from "node:fs";
import { createTwoslasher } from "twoslash";
import { arktypeTwoslashOptions } from "../lib/twoslash-options";
import { arktypeTwoslashOptions } from "~/lib/twoslash-options";

const mdxPath = process.argv[2];
if (!mdxPath) {
Expand Down
12 changes: 6 additions & 6 deletions apps/www/components/page/compatibility-rails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import {
} from "@icons-pack/react-simple-icons";
import { Square } from "lucide-react";
import type { JSX } from "react";
import { ArkTypeIcon } from "~/components/icons/arktype-icon";
import { JoiIcon } from "~/components/icons/joi-icon";
import { SolidStartIcon } from "~/components/icons/solid-start-icon";
import { TypiaIcon } from "~/components/icons/typia-icon";
import { ValibotIcon } from "~/components/icons/valibot-icon";
import { VinxiIcon } from "~/components/icons/vinxi-icon";
import { cn } from "~/lib/utils";
import { ArkTypeIcon } from "../icons/arktype-icon";
import { JoiIcon } from "../icons/joi-icon";
import { SolidStartIcon } from "../icons/solid-start-icon";
import { TypiaIcon } from "../icons/typia-icon";
import { ValibotIcon } from "../icons/valibot-icon";
import { VinxiIcon } from "../icons/vinxi-icon";

type RailItem = {
name: string;
Expand Down
2 changes: 1 addition & 1 deletion apps/www/components/ui/popover.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import * as PopoverPrimitive from "@radix-ui/react-popover";
import * as React from "react";
import { cn } from "../../lib/cn";
import { cn } from "~/lib/cn";

const Popover = PopoverPrimitive.Root;

Expand Down
29 changes: 29 additions & 0 deletions apps/www/content/docs/arkenv/options.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,32 @@ const env = arkenv(
// env.PORT → 3000 (default applied)
// env.DEBUG → false (default applied)
```

## `safe`

Whether to return a safe result object instead of throwing an error on validation failure. Defaults to `false`.

When enabled, the function returns an object with `{ success: true, data }` or `{ success: false, issues }`.

```ts twoslash
import arkenv from "arkenv";
// ---cut---
const result = arkenv(
{ PORT: "number" },
{ safe: true, env: { PORT: "invalid" } }
);

if (!result.success) {
console.error("Validation failed:", result.issues);
} else {
console.log("Port is:", result.data.PORT);
}
```

:::warn Not supported in Integrations
The `safe: true` option is purposefully omitted from the configuration options of framework integrations (like `@arkenv/vite-plugin`, `@arkenv/nextjs`, etc.).

Framework integrations rely on injecting the raw, validated environment variables into the build process. If `safe: true` were enabled, a validation failure would result in the plugin silently injecting the `{ success: false, issues }` wrapper object into your application bundle instead of crashing the build, leading to broken downstream behavior.

If you need to handle validation failures programmatically without failing the build, you should invoke `arkenv()` manually rather than using an automated bundler plugin.
:::
19 changes: 19 additions & 0 deletions apps/www/content/docs/arkenv/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,25 @@ const dbConfig = {
console.log(`Connecting to ${dbConfig.host}:${dbConfig.port}...`);
```

## Safe parsing (Non-throwing)

By default, calling `arkenv()` will throw an `ArkEnvError` if validation fails. If you prefer to handle validation issues without catching thrown exceptions, you can pass the `{ safe: true }` option instead. It returns a result object containing either the successfully parsed data or an array of validation issues.

```ts title="env.ts" twoslash
import { arkenv } from 'arkenv';

const result = arkenv({
PORT: "number = 3000",
}, { safe: true });

if (result.success) {
console.log("Validated config:", result.data);
Comment thread
yamcodes marked this conversation as resolved.
Outdated
// result.data.PORT is guaranteed to be a number here
} else {
console.error("Validation failed:", result.issues);
}
```

## Next steps

<Cards>
Expand Down
15 changes: 14 additions & 1 deletion docs/CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,15 @@ pnpm run test:e2e # E2E tests
- Run `pnpm release` after merging PRs to publish packages
- Only packages in `packages/` are published to npm

## Design Decisions

**Split Parsing Engines (ArkType vs Standard Schema):**

- ArkEnv maintains two distinct parsing engines: `src/arktype/index.ts` and `src/parse-standard.ts`.
- Despite visual similarities, they are strictly isolated to guarantee the `arkenv/standard` module boundary remains "ArkType-free".
- Unifying them would force bundlers to trace static imports and drag ArkType into the dependency tree of Standard Schema users, violating the zero-dependency goal.
- We prioritize optimal tree-shaking, bundle size isolation, and decoupling over dogmatic DRYness.

## Domain context

**Environment Variable Validation:**
Expand Down Expand Up @@ -225,8 +234,12 @@ pnpm run test:e2e # E2E tests
- Leverages ArkType's `type.infer` and `type.validate` utilities
- Typesafe environment object returned from `arkenv`

**Error Handling:**
**Error Handling & Vocabulary:**

- **Issue vs. Error Distinction**: ArkEnv strictly differentiates between an "Issue" and an "Error".
- **Issue (`EnvIssue`)**: A single, isolated validation failure on a specific environment variable.
- **Error (`ArkEnvError`)**: The overarching runtime exception that is thrown when validation fails. It contains an array of `EnvIssue`s.
- Functions dealing with individual failures should use "Issue" (e.g., `formatIssues`), while functions dealing with the final halting exception should use "Error" (e.g., `ArkEnvError`).
- `ArkEnvError` extends `Error` and formats ArkType validation errors
- Errors include variable names and expected types
- Fail-fast approach: app won't start if validation fails
Expand Down
27 changes: 27 additions & 0 deletions docs/adr/0010-bundle-isolation-over-dryness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 10. Bundle Isolation trumps DRYness

Date: 2026-06-21

## Status

Accepted

## Context

ArkEnv operates as a zero-dependency environment variable parser. It uses ArkType as its primary validation engine (`arkenv`), while also offering a completely separate entry point (`arkenv/standard`) that leverages Standard Schema 1.0 (for users preferring Zod, Valibot, etc.).

During the refactoring to a single core repository with multiple internal exports, a code review raised concerns about code duplication between `src/arktype/index.ts` and `src/parse-standard.ts`. Both files implement similar logic for parsing objects, extracting issue metadata, and formatting validation results. The suggestion was to "aggressively unify or codeshare" these implementations to abide by the DRY (Don't Repeat Yourself) principle.

However, sharing utilities across the core ArkType engine and the Standard Schema engine introduces hidden module graph entanglements. Bundlers like Webpack, Rollup, and esbuild often rely on static imports for tree-shaking. A single shared `utils.ts` file imported by both entry points can easily trick the bundler's heuristics into statically tracing the dependency tree back to `arktype`. This would drag the entire 50kb+ ArkType AST engine into the production bundle of users who only wanted to use `arkenv/standard` with Zod.

## Decision

We intentionally duplicate parsing, formatting, and issue-mapping logic across the `arktype` and `standard` engine implementations to maintain an airtight module boundary. **Bundle isolation strictly trumps DRYness across core/standard boundaries.**

We will not create shared abstractions or utility files that bridge these two domains. The small maintenance cost of duplicated internal logic is a worthwhile trade-off to guarantee that `arkenv/standard` users never incur a bundle size penalty from ArkType.

## Consequences

- The footprint of `@arkenv/standard` remains strictly minimal and fully decoupled from ArkType.
- Contributors must be aware that fixing a bug in the error extraction logic for ArkType may require a mirrored fix in the Standard Schema logic.
- Future code reviews raising concerns about DRYness between these files should be directed to this ADR.
Loading
Loading