Skip to content

arkenv/standard dedicated import#806

Merged
yamcodes merged 41 commits intomainfrom
arkenv-standard-import
Feb 22, 2026
Merged

arkenv/standard dedicated import#806
yamcodes merged 41 commits intomainfrom
arkenv-standard-import

Conversation

@yamcodes
Copy link
Owner

@yamcodes yamcodes commented Feb 21, 2026

Summary by CodeRabbit

  • New Features

    • New public entry points: arkenv/standard (ArkType-free createEnv) and arkenv/core (core primitives including ArkEnvError and ValidationIssue); main arkenv now exports the type helper.
  • Breaking Changes

    • Removed arkenv/arktype sub-path. Import the type from arkenv and use arkenv/standard for Standard Schema flows; createEnv signature simplified (validator option removed).
  • Documentation

    • Added ARCHITECTURE.md and updated docs/examples to show the new entry points and migration guidance.

@changeset-bot
Copy link

changeset-bot bot commented Feb 21, 2026

🦋 Changeset detected

Latest commit: 02704b4

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

This PR includes changesets to release 3 packages
Name Type
arkenv Minor
@arkenv/vite-plugin Patch
@arkenv/bun-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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 21, 2026

Warning

Rate limit exceeded

@yamcodes has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 53 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

Refactors ArkEnv export surface into three tiers: root arkenv (ArkType-first), arkenv/standard (ArkType-free createEnv), and arkenv/core (mode-agnostic primitives). Removes lazy ArkType loader and arkenv/arktype public subpath, extracts runtime guards, updates package exports, tests, and docs; adds ARCHITECTURE.md.

Changes

Cohort / File(s) Summary
Core export surface
packages/arkenv/src/index.ts, packages/arkenv/src/core.ts, packages/arkenv/src/arktype/index.ts, packages/arkenv/package.json, packages/arkenv/tsdown.config.ts
Adds arkenv/core, re-exports type from root, moves error/ValidationIssue primitives to core, removes ./arktype public subpath, and updates package exports and public entries.
Standard mode entry
packages/arkenv/src/standard.ts, packages/arkenv/src/parse-standard.ts
Adds ArkType-free createEnv and StandardEnvConfig/ParseStandardConfig; parse-standard uses ValidationIssue[] and throws ArkEnvError with structured issues.
Guards & create flow
packages/arkenv/src/guards.ts, packages/arkenv/src/create-env.ts
Extracts runtime guards (assertNotArkTypeDsl, assertStandardSchema*); create-env now statically imports ArkType parse, removes dynamic loader and inline standard-mode branch.
Loader removal & tests
packages/arkenv/src/utils/load-arktype.ts, packages/arkenv/src/utils/load-arktype.test.ts, packages/arkenv/src/isolation.test.ts, packages/arkenv/src/utils/bundling-interop.test.ts
Deletes dynamic load-arktype and its tests; tests updated to use arkenv/standard and assert dist/standard.mjs contains no arktype references.
Errors & error plumbing
packages/arkenv/src/errors.ts, packages/arkenv/src/arktype/*, packages/arkenv/src/errors.test.ts
Removes legacy errors.ts; introduces ValidationIssue and ArkEnvError in core; arktype parse converts ArkErrors → ValidationIssue and constructs ArkEnvError; tests updated.
Build, tooling & packaging
packages/arkenv/tsdown.config.ts, packages/arkenv/package.json, .github/actions/size-limit/src/*, packages/vite-plugin/package.json
Adds src/standard.ts/src/core.ts to build entries, updates exports map (./standard, ./core), adjusts size-limit entries and minor size-limit output formatting.
Clients, examples & docs
apps/*, examples/*, apps/www/content/docs/*, apps/www/lib/twoslash-options.ts, packages/vite-plugin/src/__fixtures__/*
Updates imports/usages to import { type } from "arkenv" and createEnv from arkenv/standard; refreshes docs, examples, and twoslash aliases for the new API.
Specs, design & ARCHITECTURE
openspec/changes/refactor-export-surface/*, packages/arkenv/ARCHITECTURE.md, openspec/changes/refactor-export-surface/specs/*
Adds design, proposal and spec docs describing three‑tier export surface, delegation rules, invariants, migration guidance, and scenarios.
Minor edits
various MD/MDX and workflow files
Multiple punctuation/typography tweaks (em-dash → hyphen) and minor import path/test updates across the repo.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Consumer
    participant Root as "arkenv (root)"
    participant Standard as "arkenv/standard"
    participant ArkType as "src/arktype/index.ts"
    participant Core as "arkenv/core"

    Consumer->>Root: import { createEnv, type } from "arkenv"
    Root->>ArkType: static parse(def, config)
    Consumer->>Standard: import { createEnv } from "arkenv/standard"
    Standard->>Standard: run guards (assertNotArkTypeDsl, assertStandardSchema)
    Standard->>Standard: parseStandard(def, config) -> validated output
    Any->>Core: import { ArkEnvError, ValidationIssue } from "arkenv/core"
    Core-->>Any: structured error primitives / formatted messages
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

github_actions

Poem

🐇 I hopped through exports, tidy and spry,
split lanes for standard, core, and sky,
guards in my pouch and static imports in tow,
lazy loaders vanished — the pathways now flow,
a tiny rabbit cheers the refactor's glow.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: introducing a dedicated arkenv/standard import path as a separate entry point for ArkType-free Standard Schema validation.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch arkenv-standard-import

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the docs Improvements or additions to documentation label Feb 21, 2026
@github-actions github-actions bot added arkenv Changes to the `arkenv` npm package. tests This issue or PR is about adding, removing or changing tests labels Feb 21, 2026
@yamcodes yamcodes marked this pull request as ready for review February 21, 2026 18:16
@dosubot
Copy link

dosubot bot commented Feb 21, 2026

Documentation Updates

3 document(s) were updated by changes in this PR:

CHANGELOG
View Changes
@@ -1,4 +1,51 @@
 # ArkEnv
+
+## 0.10.0
+
+### Minor Changes
+
+- #### Add `arkenv/standard` import for non-ArkType consumers _[`#806`](https://github.com/yamcodes/arkenv/pull/806) [@yamcodes](https://github.com/yamcodes)_
+
+  ArkEnv now provides three separate entry points to better serve different use cases:
+
+  - **`arkenv`** (main): ArkType-first. Includes `createEnv`, `type`, and `ArkEnvError`. The `type` helper has moved here from `arkenv/arktype`.
+  - **`arkenv/standard`**: ArkType-free. A standalone `createEnv` for Standard Schema validators (Zod, Valibot, etc.) with zero ArkType in the bundle.
+  - **`arkenv/core`**: Mode-agnostic primitives - `ArkEnvError` and `ValidationIssue`.
+
+  **For Standard Schema users** (Zod, Valibot, etc.), import from `arkenv/standard` directly, without passing `{ validator: "standard" }` on every call:
+
+  ```ts
+  // ❌ Before
+  import { createEnv } from "arkenv";
+  import { z } from "zod";
+
+  const env = createEnv(
+    { PORT: z.coerce.number() },
+    { validator: "standard" }
+  );
+
+  // ✅ After
+  import { createEnv } from "arkenv/standard";
+  import { z } from "zod";
+
+  const env = createEnv({ PORT: z.coerce.number() });
+  ```
+
+  **For `arkenv/arktype` users**, the `type` helper has moved to the main entry:
+
+  ```ts
+  // ❌ Before
+  import { type } from "arkenv/arktype";
+
+  // ✅ After
+  import { type } from "arkenv";
+  ```
+
+  **Breaking Changes:**
+
+  - Removed `arkenv/arktype` sub-path. Import the `type` helper from the main `arkenv` entry point instead.
+  - The `validator` config option has been removed. For Standard Schema flows, use the new `arkenv/standard` entry point instead.
+  - For access to core primitives like `ArkEnvError`, use `import { ArkEnvError } from "arkenv/core"`.
 
 ## 0.9.3
 
copilot-instructions
View Changes
@@ -51,47 +51,124 @@
 
 ### Main Package (`packages/arkenv`)
 
-The core package provides:
-
-1. **`createEnv(schema)`** - Main function to create validated environment objects. Also available as the default export, typically imported as `arkenv`.
-2. **Built-in validators** - Common validators like `host`, `port`, `url`, etc.
-3. **ArkType integration** - Uses ArkType for schema definition and validation
-4. **Type inference** - Full TypeScript type inference from schemas
+The core package provides three entry points:
+
+1. **`arkenv`** (main entry) - ArkType-first `createEnv` with built-in validators and `type` helper
+2. **`arkenv/standard`** - ArkType-free `createEnv` for Standard Schema validators (Zod, Valibot, etc.)
+3. **`arkenv/core`** - Core primitives like `ArkEnvError` and `ValidationIssue`, usable without ArkType
+
+#### Main Entry (`arkenv`)
+- **`createEnv(schema)`** - Main function to create validated environment objects (also available as default export `arkenv`)
+- **`type`** - Helper for ArkType schemas, re-exported from ArkType with ArkEnv's built-in validators
+- **Built-in validators** - Common validators like `host`, `port`, `url`, etc.
+- **Type inference** - Full TypeScript type inference from schemas
+
+#### Standard Entry (`arkenv/standard`)
+- **`createEnv(schema)`** - ArkType-free validator for Standard Schema implementations
+- No ArkType dependency - perfect for projects using only Zod, Valibot, or other Standard Schema validators
+
+#### Core Entry (`arkenv/core`)
+- **`ArkEnvError`** - Error class for validation failures
+- **`ValidationIssue`** - Type for individual validation errors
+- Mode-agnostic primitives usable in any context
 
 ### Key Files
+- `src/index.ts` - Main ArkType entry point exporting `createEnv` and `type` helper
+- `src/standard.ts` - Standard Schema entry point with ArkType-free `createEnv`
+- `src/core.ts` - Core primitives (`ArkEnvError`, `ValidationIssue`)
 - `src/create-env.ts` - Core `createEnv` implementation
-- `src/types.ts` - Built-in type validators (host, port, url, etc.)
-- `src/errors.ts` - Error handling and formatting
+- `src/arktype/index.ts` - ArkType-specific validation logic and `parse` function
+- `src/parse-standard.ts` - Standard Schema validation logic
+- `src/guards.ts` - Type guards for runtime validation
 - `src/utils.ts` - Utility functions
 
 ## Coding Patterns
 
-### Environment Schema Definition
-```typescript
-import arkenv from 'arkenv';
+### Import Structure
+
+ArkEnv has three main entry points for different use cases:
+
+#### Using ArkType (Default)
+```typescript
+import arkenv, { type } from 'arkenv';
 
 const env = arkenv({
   HOST: "string.host",                                    // Built-in validator
   PORT: "number.port",                                    // Built-in validator  
-  NODE_ENV: "'development' | 'production' | 'test'", // ArkType string literal
-  DATABASE_URL: "string",                        // Simple ArkType schema
-  FEATURE_FLAG: "boolean = false",               // Default value
-});
-```
+  NODE_ENV: "'development' | 'production' | 'test'",     // ArkType string literal
+  DATABASE_URL: "string",                                 // Simple ArkType schema
+  FEATURE_FLAG: "boolean = false",                        // Default value
+});
+
+// Using the type helper for reusable schemas
+const portSchema = type("number.port");
+const env2 = arkenv({
+  API_PORT: portSchema,
+  DB_PORT: portSchema,
+});
+```
+
+#### Using Standard Schema Only (Zod, Valibot, etc.)
+```typescript
+import arkenv from 'arkenv/standard';
+import { z } from 'zod';
+
+const env = arkenv({
+  PORT: z.coerce.number().int().min(0).max(65535),
+  DATABASE_URL: z.string().url(),
+  DEBUG: z.coerce.boolean().default(false),
+});
+```
+
+#### Importing Core Primitives
+```typescript
+import { ArkEnvError } from 'arkenv/core';
+
+try {
+  // validation logic
+} catch (error) {
+  if (error instanceof ArkEnvError) {
+    console.error('Environment validation failed:', error.message);
+  }
+}
+```
+
+### Environment Schema Definition
+
+The main entry (`arkenv`) uses ArkType's DSL for schema definition with built-in validators:
 
 ### Built-in Validators
-The library provides common validators in `src/types.ts`:
-- `host` - Valid IP address or hostname
-- `port` - Valid port number (0-65535)
-- `url` - Valid URL
-- `email` - Valid email address
+
+The library provides common validators accessible via the ArkType DSL:
+- `string.host` - Valid IP address or hostname
+- `number.port` - Valid port number (0-65535)
+- `string.url` - Valid URL
+- `string.email` - Valid email address
+
+These validators are available when using the main `arkenv` entry with ArkType.
 
 ### Error Handling
-Environment validation errors are thrown early with descriptive messages showing:
+
+Validation errors are thrown as `ArkEnvError` (importable from `arkenv` or `arkenv/core`) with descriptive messages showing:
 - Which variables are missing or invalid
 - Expected vs actual values
 - Helpful suggestions for fixing issues
 
+```typescript
+import arkenv, { ArkEnvError } from 'arkenv';
+
+try {
+  const env = arkenv({
+    PORT: "number.port",
+    HOST: "string.host",
+  });
+} catch (error) {
+  if (error instanceof ArkEnvError) {
+    console.error('Environment validation failed:', error.message);
+  }
+}
+```
+
 ## Testing Guidelines
 
 ### Test Structure
@@ -102,9 +179,11 @@
 **What:** Test individual functions, components, and hooks in isolation with mocked dependencies.
 
 Tests are located alongside source files with `.test.ts` suffix:
-- `create-env.test.ts` - Tests for main `createEnv` functionality
-- `types.test.ts` - Tests for built-in validators
+- `create-env.test.ts` - Tests for `createEnv` functionality
+- `arktype/create-env.test.ts` - Tests for ArkType-specific parsing
+- `standard-mode.test.ts` - Tests for Standard Schema mode
 - `errors.test.ts` - Tests for error handling
+- `guards.test.ts` - Tests for type guards
 - `utils.test.ts` - Tests for utility functions
 - `copy-button.test.tsx` - Tests `CopyButton` component with mocked dependencies
 
@@ -146,6 +225,9 @@
 ### Unit Test Example
 ```typescript
 import { beforeEach, afterEach, it, expect } from 'vitest';
+import arkenv from 'arkenv';
+
+let originalEnv: NodeJS.ProcessEnv;
 
 beforeEach(() => {
   // Save original env
@@ -258,18 +340,77 @@
 
 ## Best Practices
 
-1. **Use built-in validators** when available (host, port, url, etc.)
-2. **Provide defaults** for optional environment variables
-3. **Group related variables** in logical schemas
-4. **Document environment requirements** in README files
-5. **Test edge cases** including invalid and missing values
-6. **Use descriptive variable names** that indicate purpose and format
+1. **Choose the right entry point**:
+   - Use `arkenv` for ArkType DSL and built-in validators
+   - Use `arkenv/standard` for Standard Schema-only workflows (Zod, Valibot)
+   - Use `arkenv/core` for importing error types without validation logic
+2. **Use built-in validators** when available (`string.host`, `number.port`, `string.url`, etc.)
+3. **Provide defaults** for optional environment variables
+4. **Group related variables** in logical schemas
+5. **Document environment requirements** in README files
+6. **Test edge cases** including invalid and missing values
+7. **Use descriptive variable names** that indicate purpose and format
+
+## Migration Guide
+
+### From arkenv < 1.0 (arkenv/arktype sub-path)
+
+The `arkenv/arktype` sub-path has been removed. Update your imports:
+
+**Before:**
+```typescript
+import { type } from 'arkenv/arktype';
+import arkenv from 'arkenv';
+
+const env = arkenv({
+  PORT: type("number.port"),
+});
+```
+
+**After:**
+```typescript
+import arkenv, { type } from 'arkenv';
+
+const env = arkenv({
+  PORT: type("number.port"),
+});
+```
+
+### From validator option to entry point
+
+The `validator` configuration option has been removed. Use the appropriate entry point instead:
+
+**Before:**
+```typescript
+import arkenv from 'arkenv';
+import { z } from 'zod';
+
+const env = arkenv(
+  {
+    PORT: z.coerce.number(),
+  },
+  { validator: "standard" }
+);
+```
+
+**After:**
+```typescript
+import arkenv from 'arkenv/standard';
+import { z } from 'zod';
+
+const env = arkenv({
+  PORT: z.coerce.number(),
+});
+```
 
 ## Examples Reference
 
 Check the `examples/` directory for practical usage patterns:
-- `basic/` - Simple Node.js application
+- `basic/` - Simple Node.js application with ArkType
 - `with-bun/` - Bun runtime integration
+- `with-standard-schema/` - Using Standard Schema validators without ArkType
+- `with-vite-react/` - Vite + React integration
+- `with-solid-start/` - SolidStart integration
 
 These examples demonstrate real-world usage and can serve as templates for new integrations.
 
@@ -285,7 +426,7 @@
 
 ### Security Considerations
 - Always validate user input in examples and documentation
-- Use `ArkEnvError` for environment variable errors, not generic errors
+- Use `ArkEnvError` (from `arkenv` or `arkenv/core`) for environment variable errors, not generic errors
 - Never log environment variable values in production code
 - Ensure examples demonstrate secure default practices
 
README
View Changes
@@ -1,4 +1,6 @@
 # ArkType Land
+
+> **⚠️ Breaking Change:** The `arkenv/arktype` sub-path has been **removed**. The `type` helper is now exported from the main **`arkenv`** entry point. Import from `"arkenv"` instead of `"arkenv/arktype"`.
 
 This module **assumes that the user has `arktype` installed** in their project. It is intentionally isolated from the core of `arkenv` to allow for optional and lazy loading of the ArkType library.
 
@@ -8,7 +10,7 @@
 
 This directory contains the ArkType-specific validator logic for `arkenv`.
 
-- `index.ts`: The main entry point for the ArkType validator, which integrates with `@repo/scope`.
+- `index.ts`: The implementation of the ArkType validator, including the `parse` function and `type` helper. While this was previously exposed as `arkenv/arktype`, it is now an internal module. The `type` helper is re-exported from the main `arkenv` entry.
 - `coercion/`: Logic for introspecting ArkType schemas and applying automatic type coercion (e.g., parsing "3000" as a number).
 
 ## How it works
@@ -16,3 +18,25 @@
 When `createEnv` is called in ArkType-mode (the default), `arkenv` dynamically loads this module. This ensures that:
 1. `arkenv` can be used without `arktype` (e.g., for Standard Schema only projects).
 2. `arktype` is only loaded if and when environment validation is actually performed.
+
+## Public API
+
+`arkenv` now has **three public entry points**:
+
+- **`arkenv`** (main): ArkType-first. Exports `createEnv` (with ArkType support), `type` (the ArkEnv helper with extra keywords like `string.host`), and `ArkEnvError`.
+- **`arkenv/standard`**: ArkType-free. A standalone `createEnv` for Standard Schema validators (Zod, Valibot, etc.) with zero ArkType in the bundle.
+- **`arkenv/core`**: Mode-agnostic primitives (`ArkEnvError`, `ValidationIssue`).
+
+The `arkenv/arktype` sub-path no longer exists. This module (`src/arktype/`) is now an internal implementation detail, and its exports are re-exported from the main `arkenv` entry.
+
+## Migration
+
+For developers referencing this internal module:
+
+```ts
+// ❌ Before
+import { type } from "arkenv/arktype";
+
+// ✅ After
+import { type } from "arkenv";
+```

How did I do? Any feedback?  Join Discord

@arkenv-bot
Copy link
Contributor

arkenv-bot bot commented Feb 21, 2026

📦 Bundle Size Report

Package Size Limit Diff Status
@arkenv/vite-plugin 1.91 kB 2.93 kB +12.4%
arkenv 441 B 2.93 kB -71.7%
@arkenv/bun-plugin 2.39 kB 2.44 kB +10.1%

All size limits passed!

@arkenv-bot
Copy link
Contributor

arkenv-bot bot commented Feb 21, 2026

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

Project Deployment Review Updated (Asia/Almaty)
arkenv Ready Ready Preview, Comment Feb 23 2026, 12:28 AM (Asia/Almaty)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/arkenv/src/utils/bundling-interop.test.ts (1)

83-141: ⚠️ Potential issue | 🟡 Minor

The test doesn't actually enforce the "no ArkType needed" guarantee.

The comment on Line 124 says "arktype must NOT be required", but the test only checks output.trim() === "SUCCESS". If arkenv/standard's dist were to import arktype, esbuild would resolve it via the workspace's own node_modules (the temp dir is a subdirectory of the workspace), bundle it transparently, and the test would still pass. The ArkType-free invariant is never enforced.

The simplest fix is to assert the bundle output contains no arktype reference:

🧪 Proposed verification step
+		// Verify the bundle does not reference arktype (ArkType-free guarantee)
+		const { readFileSync } = await import("node:fs");
+		const bundleSource = readFileSync(outFile, "utf-8");
+		expect(bundleSource).not.toContain("arktype");

 		// Run the bundled output — arktype must NOT be required for this to work
 		try {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/arkenv/src/utils/bundling-interop.test.ts` around lines 83 - 141,
The test "should work when arkenv/standard is external in an ESM bundle (no
ArkType needed)" does not assert that arktype is not referenced; after bundling
(the execSync call using esbuildPath that writes outFile), read the generated
bundle file (outFile) and assert it does not contain any "arktype" references
(e.g., import "arktype", require("arktype"), or the string 'arktype') before
running the bundled output; update the test to fail if such a reference is found
so the ArkType-free invariant is actually enforced.
🧹 Nitpick comments (3)
openspec/changes/refactor-export-surface/specs/validator-mode/spec.md (1)

1-20: Consider adding a scenario for the ArkType-free runtime invariant.

The three scenarios cover happy-path validation and the two error modes, but the headline requirement — "without requiring ArkType" — has no corresponding #### Scenario. Consumers adopting arkenv/standard specifically for ArkType-free bundles would benefit from a normative scenario capturing this guarantee (e.g., WHEN imported in a bundle without ArkType installed, THEN it MUST function without errors). This would also drive better test coverage (see note on bundling-interop.test.ts).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openspec/changes/refactor-export-surface/specs/validator-mode/spec.md` around
lines 1 - 20, Add a fourth Scenario under the "Dedicated Standard Entry Point"
requirement that captures the ArkType-free runtime invariant: when createEnv is
imported from "arkenv/standard" and run in a runtime/bundle where ArkType is not
present or installed, it MUST still validate and return the Standard Schema
output without throwing errors; reference the arkenv/standard createEnv symbol
and mention that no ArkType runtime/DLS strings may be required, and add a
corresponding test note (e.g., in bundling-interop.test.ts) to exercise this
case to ensure the bundle works without ArkType.
packages/arkenv/src/standard.ts (1)

55-63: Outer schema-shape validation is duplicated between standard.ts and create-env.ts.

The identical !def || typeof def !== "object" || Array.isArray(def) guard + ArkEnvError throw block (including the same error message) appears in both src/standard.ts (lines 55–62) and src/create-env.ts (lines 117–126). The assertNotArkTypeDsl/assertStandardSchema guards were correctly extracted to guards.ts, but this outer check was not. Consider extracting it into guards.ts as well:

♻️ Suggested refactor

In src/guards.ts, add:

export function assertStandardSchemaMap(def: unknown): asserts def is Record<string, unknown> {
	if (!def || typeof def !== "object" || Array.isArray(def)) {
		throw new ArkEnvError([
			{
				path: "",
				message: 'Invalid schema: expected an object mapping in "standard" mode.',
			},
		]);
	}
}

Then in both standard.ts and create-env.ts, replace the duplicated block with a single call:

-if (!def || typeof def !== "object" || Array.isArray(def)) {
-  throw new ArkEnvError([{ path: "", message: 'Invalid schema: expected an object mapping in "standard" mode.' }]);
-}
+assertStandardSchemaMap(def);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/arkenv/src/standard.ts` around lines 55 - 63, Extract the duplicated
outer-schema check into a new guard in guards.ts named
assertStandardSchemaMap(def: unknown): asserts def is Record<string, unknown>
that throws the existing ArkEnvError with message 'Invalid schema: expected an
object mapping in "standard" mode.' when !def || typeof def !== "object" ||
Array.isArray(def); then replace the duplicated if-blocks in standard.ts (the
current guard before using def) and create-env.ts (the identical guard around
schema handling) with a single call to assertStandardSchemaMap(def) so both
files use the shared guard.
packages/arkenv/package.json (1)

84-93: Consider adding size-limit entries for the two new sub-paths.

dist/standard.mjs and dist/core.mjs are new public entries that will contribute to install weight but are not currently measured. The design doc notes this as "worth doing but out of scope" — worth tracking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/arkenv/package.json` around lines 84 - 93, Update the "size-limit"
configuration in package.json to add entries for the two new public build
outputs so they are measured: add objects analogous to the existing entry for
"dist/index.mjs" for "dist/standard.mjs" and "dist/core.mjs" (using the same
"limit", "import" and "ignore" values as the current size-limit entry) so the
new sub-paths are tracked by size-limit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@openspec/changes/refactor-export-surface/design.md`:
- Line 63: The sentence currently implies a dependency chain "create-env.ts (via
src/standard.ts) → parseStandard" which is incorrect; update the phrasing in
design.md so it clearly states that both create-env.ts and src/standard.ts
independently call parseStandard (or simply drop the parenthetical), and ensure
the line names create-env.ts, src/standard.ts and parseStandard are mentioned to
make it explicit there is no create-env.ts → src/standard.ts dependency.

In `@packages/arkenv/package.json`:
- Around line 18-33: The package arkenv removed the ./arktype export which is a
breaking change; add a changeset for the arkenv package that documents this as a
major bump so releases are versioned correctly. Create a new file under
.changeset (e.g., a .md or .mdx file named uniquely) that lists the package name
"arkenv" and sets the bump type to "major", include a brief note referencing the
refactor/export-surface change, and commit it alongside the package.json change
so the release tooling will pick up the breaking change.

In `@packages/arkenv/src/core.ts`:
- Line 1: Rename the exported type InternalValidationError to a public-friendly
name (e.g., ValidationIssue or EnvValidationError) and update all internal
references and public re-exports to use the new identifier; specifically, change
the export statement that currently exports InternalValidationError and adjust
any usages in types, function signatures, error handling code, and other export
lists so they import/export the new name consistently (ensure any tests or docs
referencing InternalValidationError are updated or aliased if backwards
compatibility is required).

In `@packages/arkenv/src/guards.ts`:
- Around line 23-32: assertStandardSchema currently only checks that the
"~standard" property exists but not that its validate field is a function, so
malformed validators like { "~standard": null } escape and later cause
TypeError; update assertStandardSchema to verify that value["~standard"] is an
object and that ("validate" in value["~standard"]) and typeof
value["~standard"].validate === "function", and if not throw an ArkEnvError
(same shape as existing error) so parse-standard.ts will receive a structured
error instead of a runtime TypeError.

In `@packages/arkenv/src/isolation.test.ts`:
- Around line 35-39: The test "loads ArkType statically when importing from
arktype/index.ts" currently only does an import with no assertions; update it to
explicitly assert that the expected export from "./arktype/index.ts" is present
(e.g., import the module as const mod = await import("./arktype/index.ts") and
add an expect that mod.ArkType or mod.default is defined or matches the expected
shape), so the test documents the contract and fails if the export isn't
available.

---

Outside diff comments:
In `@packages/arkenv/src/utils/bundling-interop.test.ts`:
- Around line 83-141: The test "should work when arkenv/standard is external in
an ESM bundle (no ArkType needed)" does not assert that arktype is not
referenced; after bundling (the execSync call using esbuildPath that writes
outFile), read the generated bundle file (outFile) and assert it does not
contain any "arktype" references (e.g., import "arktype", require("arktype"), or
the string 'arktype') before running the bundled output; update the test to fail
if such a reference is found so the ArkType-free invariant is actually enforced.

---

Nitpick comments:
In `@openspec/changes/refactor-export-surface/specs/validator-mode/spec.md`:
- Around line 1-20: Add a fourth Scenario under the "Dedicated Standard Entry
Point" requirement that captures the ArkType-free runtime invariant: when
createEnv is imported from "arkenv/standard" and run in a runtime/bundle where
ArkType is not present or installed, it MUST still validate and return the
Standard Schema output without throwing errors; reference the arkenv/standard
createEnv symbol and mention that no ArkType runtime/DLS strings may be
required, and add a corresponding test note (e.g., in bundling-interop.test.ts)
to exercise this case to ensure the bundle works without ArkType.

In `@packages/arkenv/package.json`:
- Around line 84-93: Update the "size-limit" configuration in package.json to
add entries for the two new public build outputs so they are measured: add
objects analogous to the existing entry for "dist/index.mjs" for
"dist/standard.mjs" and "dist/core.mjs" (using the same "limit", "import" and
"ignore" values as the current size-limit entry) so the new sub-paths are
tracked by size-limit.

In `@packages/arkenv/src/standard.ts`:
- Around line 55-63: Extract the duplicated outer-schema check into a new guard
in guards.ts named assertStandardSchemaMap(def: unknown): asserts def is
Record<string, unknown> that throws the existing ArkEnvError with message
'Invalid schema: expected an object mapping in "standard" mode.' when !def ||
typeof def !== "object" || Array.isArray(def); then replace the duplicated
if-blocks in standard.ts (the current guard before using def) and create-env.ts
(the identical guard around schema handling) with a single call to
assertStandardSchemaMap(def) so both files use the shared guard.

@github-actions github-actions bot added @arkenv/vite-plugin Issues or Pull Requests involving the Vite plugin for ArkEnv example Issues or Pull Requests concerning at least one ArkEnv example. (Found in the `examples/` directory) www Improvements or additions to arkenv.js.org labels Feb 21, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/www/content/docs/arkenv/quickstart.mdx (1)

59-60: Merge the two imports from 'arkenv' into one statement.

Lines 59–60 both import from the same specifier; combining them avoids redundancy in the doc sample.

♻️ Proposed merge
-import arkenv from 'arkenv';
-import { type } from 'arkenv';
+import arkenv, { type } from 'arkenv';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/content/docs/arkenv/quickstart.mdx` around lines 59 - 60, Merge the
two import statements that both import from 'arkenv' into a single import
declaration by combining the default import arkenv and the named import type
(e.g., import arkenv, { type } from 'arkenv';) so the example no longer has
redundant separate imports for arkenv and type.
apps/www/content/docs/arkenv/how-to/reuse-schemas.mdx (1)

27-28: Merge the two 'arkenv' imports into one statement.

Having separate import declarations for the same module is non-idiomatic and adds cognitive overhead for readers copying this example.

♻️ Suggested consolidation
-import arkenv from 'arkenv';
-import { type } from 'arkenv';
+import arkenv, { type } from 'arkenv';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/content/docs/arkenv/how-to/reuse-schemas.mdx` around lines 27 - 28,
The file has two imports from the same module ("import arkenv from 'arkenv';"
and "import { type } from 'arkenv';"); consolidate them into a single import
statement that imports the default export and the named export together
(reference the symbols arkenv and type) so there's only one import declaration
from 'arkenv'.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@apps/playgrounds/bun-react/src/env.ts`:
- Line 1: The import statement currently reads 'import { type } from "arkenv";'
and the reviewer approved the change (no modifications required); leave the
import as-is in env.ts (the existing symbol 'type' from the arkenv module should
remain unchanged).

In `@apps/playgrounds/solid-start/README.md`:
- Line 16: Remove the stray/incorrect import statement "import { type } from
\"arkenv\";" found in apps/playgrounds/solid-start/README.md; locate the literal
"import { type } from \"arkenv\";" and delete it (or replace it with the
intended example/code snippet) so the README no longer contains a dangling or
invalid import reference to the symbol "type".

In `@apps/playgrounds/vite-legacy/vite.config.ts`:
- Line 3: The import statement currently reads "import arkenv, { type } from
'arkenv';" and there is no change needed—leave the import as-is (keeping the
default export arkenv and the named export type) since the reviewer approved the
code; no edits to the import line or symbols (arkenv, type) are required.

---

Nitpick comments:
In `@apps/www/content/docs/arkenv/how-to/reuse-schemas.mdx`:
- Around line 27-28: The file has two imports from the same module ("import
arkenv from 'arkenv';" and "import { type } from 'arkenv';"); consolidate them
into a single import statement that imports the default export and the named
export together (reference the symbols arkenv and type) so there's only one
import declaration from 'arkenv'.

In `@apps/www/content/docs/arkenv/quickstart.mdx`:
- Around line 59-60: Merge the two import statements that both import from
'arkenv' into a single import declaration by combining the default import arkenv
and the named import type (e.g., import arkenv, { type } from 'arkenv';) so the
example no longer has redundant separate imports for arkenv and type.

- Clarify `./arktype` sub-path removal
- Promote `type` export to root entry
- Document hard ArkType dependency
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/www/content/docs/vite-plugin/arkenv-in-viteconfig.mdx (1)

15-15: ⚠️ Potential issue | 🟡 Minor

Missing space before the backtick causes a rendering artifact.

ArkEnv's\type()`should beArkEnv's `type()`` — the missing space before the backtick fuses the apostrophe into the inline-code span.

✏️ Proposed fix
-Here's how to validate those variables with ArkEnv. The key is defining your schema *once* with ArkEnv's`type()` and reusing it for both server-side config variables and client-exposed `VITE_*` variables:
+Here's how to validate those variables with ArkEnv. The key is defining your schema *once* with ArkEnv's `type()` and reusing it for both server-side config variables and client-exposed `VITE_*` variables:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/content/docs/vite-plugin/arkenv-in-viteconfig.mdx` at line 15, The
inline code snippet "ArkEnv's`type()`" is missing a space before the backtick
causing a rendering artifact; update the MDX text so there is a space between
the apostrophe and the inline code (i.e., change to "ArkEnv's `type()`") where
the phrase referencing ArkEnv and its type() helper appears to fix the
inline-code rendering.
🧹 Nitpick comments (1)
apps/www/content/docs/arkenv/quickstart.mdx (1)

17-27: Consider surfacing arkenv/standard import path in the Standard Schema accordion.

The PR introduces arkenv/standard as the dedicated ArkType-free entry point, but the "Install ArkEnv only" accordion only links to the Standard Schema guide without mentioning which import path to use. A Standard Schema user who doesn't follow the link might default to import from 'arkenv' (the ArkType-dependent entry). A one-liner like:

  Use this if you already rely on a [Standard Schema](https://standardschema.dev) validator like Zod or Valibot.
+
+  ```ts
+  import { createEnv } from 'arkenv/standard';
+  ```

  > The examples in this guide use ArkType. See the [Standard Schema guide](/docs/arkenv/integrations/standard-schema) for alternative setups.

…would make the distinction immediately clear at the install step.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/content/docs/arkenv/quickstart.mdx` around lines 17 - 27, Add a
one-line example showing the ArkType-free import path so Standard Schema users
know to use the dedicated entry point: insert a short code snippet inside the
"Install ArkEnv only" Accordion demonstrating "import { createEnv } from
'arkenv/standard';" (or similar named export used elsewhere) directly above or
below the existing install snippet, and keep the existing note about ArkType and
the link to the Standard Schema guide unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@apps/www/content/docs/vite-plugin/arkenv-in-viteconfig.mdx`:
- Line 15: The inline code snippet "ArkEnv's`type()`" is missing a space before
the backtick causing a rendering artifact; update the MDX text so there is a
space between the apostrophe and the inline code (i.e., change to "ArkEnv's
`type()`") where the phrase referencing ArkEnv and its type() helper appears to
fix the inline-code rendering.

---

Nitpick comments:
In `@apps/www/content/docs/arkenv/quickstart.mdx`:
- Around line 17-27: Add a one-line example showing the ArkType-free import path
so Standard Schema users know to use the dedicated entry point: insert a short
code snippet inside the "Install ArkEnv only" Accordion demonstrating "import {
createEnv } from 'arkenv/standard';" (or similar named export used elsewhere)
directly above or below the existing install snippet, and keep the existing note
about ArkType and the link to the Standard Schema guide unchanged.

@yamcodes yamcodes added this to the v1 milestone Feb 21, 2026
@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 22, 2026

Open in StackBlitz

npm i https://pkg.pr.new/arkenv@806
npm i https://pkg.pr.new/@arkenv/bun-plugin@806
npm i https://pkg.pr.new/@arkenv/fumadocs-ui@806
npm i https://pkg.pr.new/@arkenv/vite-plugin@806

commit: 02704b4

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/www/content/docs/arkenv/validator-mode.mdx (1)

6-7: ⚠️ Potential issue | 🟡 Minor

Stale introduction: validator mode is no longer selected via a validator option.

Line 7 says "You select the mode via the validator option in the arkenv configuration." With the new three-tier export surface, Standard mode is selected by importing from arkenv/standard—not via an option. Consider updating the intro paragraph to reflect this.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/content/docs/arkenv/validator-mode.mdx` around lines 6 - 7, Update
the stale intro sentence that says "You select the mode via the `validator`
option in the `arkenv` configuration": instead explain that validator modes are
selected by importing the appropriate export (e.g., import from
`arkenv/standard` to use Standard mode) as part of the new three-tier export
surface; replace or reword the line in validator-mode.mdx to reference importing
`arkenv/standard` (and the equivalent import names for other modes) instead of
using a `validator` option.
packages/arkenv/src/arktype/index.ts (1)

1-1: ⚠️ Potential issue | 🟠 Major

CI failure: sync the with-standard-schema example.

The sync-examples pipeline reports with-standard-schema is out of sync (Modified: index.ts). The example needs to be updated to use the new arkenv/standard import path introduced by this PR.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/arkenv/src/arktype/index.ts` at line 1, Update the import in the
arktype module: replace the existing import statement that reads import { $ }
from "@repo/scope"; with the new package path import { $ } from
"arkenv/standard"; so the example uses the new arkenv/standard export. Ensure
the symbol $ remains imported from "arkenv/standard" exactly as shown.
🧹 Nitpick comments (4)
apps/playgrounds/standard-schema/index.ts (1)

19-19: Redundant str: string type annotation — TypeScript infers it from the z.string() chain.

Per coding guidelines, explicit types inferrable by TypeScript should be omitted.

♻️ Remove the redundant type annotation
-		.transform((str: string) => str.split(","))
+		.transform((str) => str.split(","))

As per coding guidelines: "Avoid explicit types when TypeScript can infer them (noInferrableTypes error)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/playgrounds/standard-schema/index.ts` at line 19, The transform callback
on the z.string() schema includes a redundant parameter type annotation "str:
string"; remove the explicit ": string" so the transform reads (.transform((str)
=> ...)) to rely on TypeScript's inferred type and satisfy the noInferrableTypes
guideline in the transform call on the z.string() chain.
packages/arkenv/src/standard-mode.test.ts (1)

36-36: Move vi.unstubAllEnvs() to afterEach to prevent env leaks on assertion failures.

If any assertion throws before the inline vi.unstubAllEnvs() call, stubs accumulate and can cause false positives or failures in subsequent tests. As per coding guidelines, use afterEach for cleanup.

♻️ Proposed refactor
+import { afterEach, describe, expect, expectTypeOf, it, vi } from "vitest";
-import { describe, expect, expectTypeOf, it, vi } from "vitest";

 describe("Standard Mode Type Inference", () => {
+  afterEach(() => {
+    vi.unstubAllEnvs();
+  });
+
   it("should infer correct types from Standard Schema validators", () => {
     vi.stubEnv("STRING_VAR", "test");
     vi.stubEnv("NUMBER_VAR", "42");
     vi.stubEnv("BOOLEAN_VAR", "true");
     // ...
-    vi.unstubAllEnvs();
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/arkenv/src/standard-mode.test.ts` at line 36, The test currently
calls vi.unstubAllEnvs() inline which can be skipped if an assertion throws;
move the cleanup into a global teardown by adding an afterEach hook that calls
vi.unstubAllEnvs(), ensuring it runs after every test; update the test file
(standard-mode.test.ts) to remove the inline vi.unstubAllEnvs() call and add
afterEach(() => vi.unstubAllEnvs()) near the other test setup/teardown code so
environment stubs are always cleared even on failures.
packages/arkenv/src/arktype/index.ts (1)

7-7: Explicit .ts extension on style-text.ts import is inconsistent with the rest of the file.

Lines 6 and 8 use extension-less imports (../create-env, ./coercion/coerce), while line 7 retains ../utils/style-text.ts. This is a nit.

🔧 Proposed fix
-import { styleText } from "../utils/style-text.ts";
+import { styleText } from "../utils/style-text";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/arkenv/src/arktype/index.ts` at line 7, The import for styleText
uses an explicit .ts extension while the surrounding imports (e.g.,
../create-env and ./coercion/coerce) are extension-less; change the import of
styleText (symbol: styleText) to the extension-less form (../utils/style-text)
so it matches the project's import style and remains consistent with the other
imports in this module (arktype/index.ts).
openspec/changes/refactor-export-surface/design.md (1)

61-61: $.type.raw() is documented in ArkType's FAQ — no action needed, but worth a stabilization note.

The ArkType FAQ explicitly demonstrates type.raw() for generic wrappers, confirming this is a semi-public, intentionally exposed API rather than a private internal. Calling it on a custom scope instance ($.type.raw()) is the expected extension of that pattern.

Since this design doc hard-codes the call signature, consider adding a one-line note along the lines of "verified against ArkType vX.Y" so future maintainers know which version the assumption was validated against, without needing to re-investigate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openspec/changes/refactor-export-surface/design.md` at line 61, Add a
one-line stabilization note stating which ArkType version the `$.type.raw()`
call signature was verified against (e.g., "Verified against ArkType vX.Y") to
the design doc so future maintainers know the assumption backing `createEnv`'s
use of `$.type.raw()` is validated; mention `createEnv`, the `$.type.raw()`
call, and that parsing/validation is delegated to `parse` in
`src/arktype/index.ts` so the note clearly ties the verification to the relevant
symbols.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/playgrounds/standard-schema/index.ts`:
- Around line 1-4: The example file is out of sync: replace its current env
import and call with the updated pattern by importing createEnv as arkenv from
"arkenv/standard" and updating the createEnv call-site to the single-argument
form arkenv({...}); ensure the zod import (import * as z from "zod") remains and
that the symbol arkenv is used where the previous createEnv call was so the
example matches the new playground usage.

In `@apps/www/content/docs/arkenv/how-to/load-environment-variables.mdx`:
- Around line 218-221: The schema uses z.url() which exists only in Zod v4;
update the DATABASE_URL validator in the createEnv call (symbol: env2 and
function createEnv) to use z.string().url() instead of z.url(), leaving PORT as
z.coerce.number().int().min(0).max(65535); this ensures compatibility with the
current Zod version while preserving URL validation.

In `@apps/www/content/docs/arkenv/integrations/standard-schema.mdx`:
- Line 57: Update the prose to stop referencing the deprecated validator:
"standard" option and instead describe the new import-based pattern: explain
that to run ArkEnv in Standard-only mode you import from 'arkenv/standard'
(e.g., using the dedicated import) and remove or replace the phrase `validator:
"standard"` so the text matches the example that uses the import-based approach;
update the sentence around the example to mention the dedicated import (from
'arkenv/standard') and that no validator option is required.
- Around line 64-66: The schema uses z.url() which is a Zod v4 API and will
break for zod@^3.24.0; replace the DATABASE_URL schema definition from using
z.url() to the Zod v3-compatible form z.string().url() so the constant
DATABASE_URL in the object (near PORT and DEBUG) validates as a URL with Zod v3.

In `@apps/www/content/docs/arkenv/validator-mode.mdx`:
- Around line 67-70: The schema in createEnv uses z.url() (for DATABASE_URL)
which is Zod v4-only; update the DATABASE_URL entry in the createEnv call to use
z.string().url() instead (leave the PORT definition as-is) so the validator
works with Zod v3—look for the createEnv invocation that defines PORT and
DATABASE_URL and replace z.url() with z.string().url().

In `@packages/arkenv/src/arktype/index.ts`:
- Around line 34-37: Replace the regex-based path stripping (variables
escapedPath and pathRegex) with deterministic string operations to avoid ReDoS
and RegExp overhead: trim leading whitespace from the input, optionally strip a
single leading separator character if it's one of ':', '.', or '-', then trim
any following whitespace, and perform a case-insensitive startsWith check
against the path string (use .toLowerCase() on both sides) and slice off the
matched prefix; update the code around escapedPath/pathRegex in arktype/index.ts
accordingly.

In `@packages/arkenv/src/index.ts`:
- Line 1: The examples check failed because
examples/with-standard-schema/index.ts is out of sync with the updated
packages/arkenv/src/index.ts; open packages/arkenv/src/index.ts to see the
intended change (the import of $ from "@repo/scope") then update
examples/with-standard-schema/index.ts to match that change (ensure the same
imports/exports and any API usage are identical), run the repo's sync or
formatting script (or re-run the fd/rg check) to validate, then commit and push
the updated examples/with-standard-schema/index.ts so the sync-examples CI gate
passes.

---

Outside diff comments:
In `@apps/www/content/docs/arkenv/validator-mode.mdx`:
- Around line 6-7: Update the stale intro sentence that says "You select the
mode via the `validator` option in the `arkenv` configuration": instead explain
that validator modes are selected by importing the appropriate export (e.g.,
import from `arkenv/standard` to use Standard mode) as part of the new
three-tier export surface; replace or reword the line in validator-mode.mdx to
reference importing `arkenv/standard` (and the equivalent import names for other
modes) instead of using a `validator` option.

In `@packages/arkenv/src/arktype/index.ts`:
- Line 1: Update the import in the arktype module: replace the existing import
statement that reads import { $ } from "@repo/scope"; with the new package path
import { $ } from "arkenv/standard"; so the example uses the new arkenv/standard
export. Ensure the symbol $ remains imported from "arkenv/standard" exactly as
shown.

---

Duplicate comments:
In `@packages/arkenv/package.json`:
- Around line 18-33: The package removed the ./arktype export but no changeset
was added; create a major-bump changeset documenting the breaking change
(removal of the ./arktype export) and include it in the repository's changesets
folder so CI/release tooling will publish a major version; reference the removed
export name ("./arktype") and the affected package ("packages/arkenv" / its
package.json exports) in the changeset description.

---

Nitpick comments:
In `@apps/playgrounds/standard-schema/index.ts`:
- Line 19: The transform callback on the z.string() schema includes a redundant
parameter type annotation "str: string"; remove the explicit ": string" so the
transform reads (.transform((str) => ...)) to rely on TypeScript's inferred type
and satisfy the noInferrableTypes guideline in the transform call on the
z.string() chain.

In `@openspec/changes/refactor-export-surface/design.md`:
- Line 61: Add a one-line stabilization note stating which ArkType version the
`$.type.raw()` call signature was verified against (e.g., "Verified against
ArkType vX.Y") to the design doc so future maintainers know the assumption
backing `createEnv`'s use of `$.type.raw()` is validated; mention `createEnv`,
the `$.type.raw()` call, and that parsing/validation is delegated to `parse` in
`src/arktype/index.ts` so the note clearly ties the verification to the relevant
symbols.

In `@packages/arkenv/src/arktype/index.ts`:
- Line 7: The import for styleText uses an explicit .ts extension while the
surrounding imports (e.g., ../create-env and ./coercion/coerce) are
extension-less; change the import of styleText (symbol: styleText) to the
extension-less form (../utils/style-text) so it matches the project's import
style and remains consistent with the other imports in this module
(arktype/index.ts).

In `@packages/arkenv/src/standard-mode.test.ts`:
- Line 36: The test currently calls vi.unstubAllEnvs() inline which can be
skipped if an assertion throws; move the cleanup into a global teardown by
adding an afterEach hook that calls vi.unstubAllEnvs(), ensuring it runs after
every test; update the test file (standard-mode.test.ts) to remove the inline
vi.unstubAllEnvs() call and add afterEach(() => vi.unstubAllEnvs()) near the
other test setup/teardown code so environment stubs are always cleared even on
failures.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
examples/with-standard-schema/index.ts (2)

17-20: Redundant explicit type annotation on transform parameter.

TypeScript can infer str as string from the preceding z.string() schema; the : string annotation is superfluous.

As per coding guidelines, "Avoid explicit types when TypeScript can infer them (noInferrableTypes error)."

♻️ Proposed fix
-	ALLOWED_ORIGINS: z
-		.string()
-		.transform((str: string) => str.split(","))
-		.pipe(z.array(z.url())),
+	ALLOWED_ORIGINS: z
+		.string()
+		.transform((str) => str.split(","))
+		.pipe(z.array(z.url())),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/with-standard-schema/index.ts` around lines 17 - 20, Remove the
redundant explicit type annotation from the transform callback on
ALLOWED_ORIGINS: locate the ALLOWED_ORIGINS schema where you call
z.string().transform((str: string) => str.split(",")) and change the callback to
use an unannotated parameter (e.g. (str) => str.split(",")) so TypeScript can
infer the type from z.string(); keep the subsequent .pipe(z.array(z.url()))
unchanged.

25-28: z.boolean() branch in the DEBUG union is unreachable for env var inputs.

Environment variables are always strings, so the z.boolean() arm of the union will never match at runtime — the z.enum(["true", "false", "1", "0"]) branch handles all real cases. If this example is meant to be illustrative of programmatic (non-env) usage, a short comment would help; otherwise, consider dropping the dead branch to avoid confusion.

♻️ Proposed simplification (env-var-only path)
-	DEBUG: z
-		.union([z.boolean(), z.enum(["true", "false", "1", "0"])])
-		.transform((v) => v === true || v === "true" || v === "1")
-		.default(false),
+	DEBUG: z
+		.enum(["true", "false", "1", "0"])
+		.transform((v) => v === "true" || v === "1")
+		.default("false"),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/with-standard-schema/index.ts` around lines 25 - 28, The DEBUG
schema's union includes z.boolean() which is unreachable for env-var inputs (env
vars are strings); update the DEBUG definition (the union containing
z.boolean(), z.enum(["true","false","1","0"]), .transform(...), .default(false))
by either removing the z.boolean() branch to avoid dead code for env-var
parsing, or add a concise comment above DEBUG stating the union includes
z.boolean() only for programmatic (non-env) usage so readers understand intent;
keep the existing .transform(...) and .default(false) logic unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@examples/with-standard-schema/index.ts`:
- Around line 17-20: Remove the redundant explicit type annotation from the
transform callback on ALLOWED_ORIGINS: locate the ALLOWED_ORIGINS schema where
you call z.string().transform((str: string) => str.split(",")) and change the
callback to use an unannotated parameter (e.g. (str) => str.split(",")) so
TypeScript can infer the type from z.string(); keep the subsequent
.pipe(z.array(z.url())) unchanged.
- Around line 25-28: The DEBUG schema's union includes z.boolean() which is
unreachable for env-var inputs (env vars are strings); update the DEBUG
definition (the union containing z.boolean(), z.enum(["true","false","1","0"]),
.transform(...), .default(false)) by either removing the z.boolean() branch to
avoid dead code for env-var parsing, or add a concise comment above DEBUG
stating the union includes z.boolean() only for programmatic (non-env) usage so
readers understand intent; keep the existing .transform(...) and .default(false)
logic unchanged.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/bun-plugin/src/plugin.test.ts (1)

24-27: ⚠️ Potential issue | 🟠 Major

Replace mockReset() with mockClear() in afterEach to preserve the wrapped createEnv implementation across test runs.

When vi.fn(actual.createEnv) wraps the real implementation, mockReset() removes that implementation entirely, causing all subsequent tests to receive undefined. Since beforeEach only calls mockClear(), the implementation is never restored. This causes tests 2–5 (which invoke arkenv() and trigger createEnv) to fail with TypeError when processEnvSchema tries to call Object.entries() on undefined.

Proposed fix
 afterEach(() => {
   process.env = originalEnv;
-  mockCreateEnv.mockReset();
+  mockCreateEnv.mockClear();
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bun-plugin/src/plugin.test.ts` around lines 24 - 27, The test
teardown currently calls mockCreateEnv.mockReset() which clears the wrapped real
implementation of createEnv and causes later tests to get undefined; change
mockCreateEnv.mockReset() to mockCreateEnv.mockClear() in the afterEach so the
vi.fn wrapper around actual.createEnv is preserved across tests; ensure this
change is made in the afterEach block that mirrors the beforeEach (which already
uses mockClear), so arkenv()/processEnvSchema callers receive the real createEnv
implementation instead of undefined.
apps/www/content/docs/arkenv/validator-mode.mdx (1)

37-41: ⚠️ Potential issue | 🟡 Minor

z.url() is Zod v4-only; the requirements section references zod@^3.24.0.

Line 74 lists zod@^3.24.0 as a supported version, but z.url() was introduced in Zod v4. Use z.string().url() for v3 compatibility.

🐛 Proposed fix
-  DATABASE_URL: z.url(),
+  DATABASE_URL: z.string().url(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/content/docs/arkenv/validator-mode.mdx` around lines 37 - 41, The
example uses z.url() (Zod v4-only) inside the arkenv schema; replace that call
with the v3-compatible z.string().url() expression so the example and the listed
requirement zod@^3.24.0 are consistent—update the const env = arkenv({ ...
DATABASE_URL: ... }) example to use z.string().url() instead of z.url() (ensure
any references to z.url() in the surrounding docs/examples are changed
similarly).
apps/www/content/docs/vite-plugin/index.mdx (1)

36-49: ⚠️ Potential issue | 🟡 Minor

z.url() is Zod v4-only — use z.string().url() for v3 compatibility.

The documentation in validator-mode.mdx references zod@^3.24.0, which uses z.string().url() syntax. The z.url() shorthand was introduced in Zod v4. For compatibility with both versions, use z.string().url().

Proposed fix
-      VITE_API_URL: z.url(),
+      VITE_API_URL: z.string().url(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/content/docs/vite-plugin/index.mdx` around lines 36 - 49, The
example uses Zod's v4-only shorthand z.url(); update the arkenv plugin config so
the VITE_API_URL schema uses z.string().url() instead: locate the arkenv({...})
plugin block and replace z.url() for the VITE_API_URL entry with
z.string().url() (leave VITE_API_KEY and VITE_DEBUG as-is) to restore
compatibility with Zod v3.
🧹 Nitpick comments (2)
packages/vite-plugin/src/index.test.ts (1)

122-134: Optional: extract shared mockContext into a factory to eliminate duplication.

The mockContext object (with meta, error, warn, info, debug) is constructed identically in 10+ test cases. Extracting it reduces noise and makes future Vite API changes a single-point fix.

♻️ Proposed refactor
+const createMockContext = () =>
+	({
+		meta: {
+			framework: "vite",
+			version: "1.0.0",
+			rollupVersion: "4.0.0",
+			viteVersion: "5.0.0",
+		},
+		error: vi.fn(),
+		warn: vi.fn(),
+		info: vi.fn(),
+		debug: vi.fn(),
+	}) as any;

Then replace each inline object with createMockContext(), e.g.:

-			const mockContext = {
-				meta: {
-					framework: "vite",
-					version: "1.0.0",
-					rollupVersion: "4.0.0",
-					viteVersion: "5.0.0",
-				},
-				error: vi.fn(),
-				warn: vi.fn(),
-				info: vi.fn(),
-				debug: vi.fn(),
-			} as any;
+			const mockContext = createMockContext();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/vite-plugin/src/index.test.ts` around lines 122 - 134, Extract the
repeated mockContext construction into a factory function (e.g.,
createMockContext) that returns the object with meta and stubbed methods (error,
warn, info, debug) used across tests; replace each inline instantiation (the
object passed into pluginInstance.config checks and other places) with calls to
createMockContext(), keep the same shape and typing (cast as any or appropriate
type) and place the factory at the top of the test file (or export from a shared
test-helpers file) so all 10+ tests reuse it and a single change to meta or
method names updates all tests.
examples/without-arktype/src/index.ts (1)

7-7: Consider z.hostname() instead of z.url() for the HOST field.

z.url() uses the new URL() constructor and is permissive — it accepts full URI strings with a scheme (e.g., "mailto:[email protected]", "https://example.com"). For a HOST environment variable, z.hostname() more accurately validates bare hostnames (e.g., "example.com", "192.168.1.1").

♻️ Suggested refinement
-	HOST: z.literal("localhost").or(z.url()),
+	HOST: z.literal("localhost").or(z.hostname()),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/without-arktype/src/index.ts` at line 7, The HOST schema currently
allows z.url() which accepts full URIs; change it to use z.hostname() (keeping
z.literal("localhost") or) so HOST validates bare hostnames/IPs rather than full
URLs—update the HOST entry that currently reads HOST:
z.literal("localhost").or(z.url()) to use z.hostname() instead of z.url() and
ensure the combined schema still permits "localhost" and valid hostnames/IPs via
the HOST symbol.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/www/content/docs/bun-plugin/index.mdx`:
- Line 93: Update the closing sentence that still uses the deprecated phrase
"validator mode" to match the renamed section "Using Standard Schema validators
(Zod, Valibot)"; replace the phrase with something like "default validator" or
"built-in validator implementation" and keep the guidance about installing
arktype (i.e., change the sentence that currently reads 'If you intend to use
the default **validator mode**, you should also install `arktype`:' to a wording
such as 'If you intend to use the default validator, you should also install
`arktype`:'), ensuring the wording no longer references "validator mode" but
preserves the arktype installation recommendation.

In `@openspec/specs/validator-mode/spec.md`:
- Around line 12-13: Lines stating "**THEN** it SHOULD use ArkType for
validation" and "**AND** it SHOULD support ArkType-specific features like string
DSL and `type()`" downgrade mandatory behavior; update these two normative
statements to use MUST (or SHALL) instead of SHOULD so the arkenv default entry
is required to use ArkType for validation and must expose ArkType features
(string DSL and `type()`); edit the two phrases referencing "ArkType", "string
DSL", and "`type()`" accordingly to maintain consistency with the rest of the
spec.

In `@packages/arkenv/src/parse-standard.ts`:
- Around line 54-70: The error messages in parse-standard.ts duplicate the key
name because formatInternalErrors already prepends the path; update the
ArkEnvError messages thrown in the StandardSchemaV1 path so they do not include
the key string. Specifically, in the block that throws new ArkEnvError when the
validator is invalid and in the block that throws for async validation, remove
the `"${key}"` inclusion from the message text (leave a generic, descriptive
message like "Invalid schema: expected a Standard Schema 1.0 validator in
'standard' mode." and "Async validation is not supported. ArkEnv is
synchronous."). Keep the path field as-is so formatInternalErrors can add the
key.

---

Outside diff comments:
In `@apps/www/content/docs/arkenv/validator-mode.mdx`:
- Around line 37-41: The example uses z.url() (Zod v4-only) inside the arkenv
schema; replace that call with the v3-compatible z.string().url() expression so
the example and the listed requirement zod@^3.24.0 are consistent—update the
const env = arkenv({ ... DATABASE_URL: ... }) example to use z.string().url()
instead of z.url() (ensure any references to z.url() in the surrounding
docs/examples are changed similarly).

In `@apps/www/content/docs/vite-plugin/index.mdx`:
- Around line 36-49: The example uses Zod's v4-only shorthand z.url(); update
the arkenv plugin config so the VITE_API_URL schema uses z.string().url()
instead: locate the arkenv({...}) plugin block and replace z.url() for the
VITE_API_URL entry with z.string().url() (leave VITE_API_KEY and VITE_DEBUG
as-is) to restore compatibility with Zod v3.

In `@packages/bun-plugin/src/plugin.test.ts`:
- Around line 24-27: The test teardown currently calls mockCreateEnv.mockReset()
which clears the wrapped real implementation of createEnv and causes later tests
to get undefined; change mockCreateEnv.mockReset() to mockCreateEnv.mockClear()
in the afterEach so the vi.fn wrapper around actual.createEnv is preserved
across tests; ensure this change is made in the afterEach block that mirrors the
beforeEach (which already uses mockClear), so arkenv()/processEnvSchema callers
receive the real createEnv implementation instead of undefined.

---

Duplicate comments:
In `@apps/www/content/docs/arkenv/validator-mode.mdx`:
- Around line 67-70: The docs use z.url() which is only available in Zod v4
whereas the project pins zod@^3.24.0; update the validator example to be
compatible with Zod v3 by replacing z.url() with a v3-safe alternative (e.g.,
z.string().url() or a custom validator) in the createEnv call so the
DATABASE_URL schema uses z.string().url() (or equivalent) alongside the existing
PORT definition; ensure the symbols referenced are createEnv, PORT,
DATABASE_URL, and z.url() when making the change.

---

Nitpick comments:
In `@examples/without-arktype/src/index.ts`:
- Line 7: The HOST schema currently allows z.url() which accepts full URIs;
change it to use z.hostname() (keeping z.literal("localhost") or) so HOST
validates bare hostnames/IPs rather than full URLs—update the HOST entry that
currently reads HOST: z.literal("localhost").or(z.url()) to use z.hostname()
instead of z.url() and ensure the combined schema still permits "localhost" and
valid hostnames/IPs via the HOST symbol.

In `@packages/vite-plugin/src/index.test.ts`:
- Around line 122-134: Extract the repeated mockContext construction into a
factory function (e.g., createMockContext) that returns the object with meta and
stubbed methods (error, warn, info, debug) used across tests; replace each
inline instantiation (the object passed into pluginInstance.config checks and
other places) with calls to createMockContext(), keep the same shape and typing
(cast as any or appropriate type) and place the factory at the top of the test
file (or export from a shared test-helpers file) so all 10+ tests reuse it and a
single change to meta or method names updates all tests.

@yamcodes
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@yamcodes yamcodes merged commit f9010d0 into main Feb 22, 2026
20 checks passed
@yamcodes yamcodes deleted the arkenv-standard-import branch February 22, 2026 19:42
@arkenv-bot arkenv-bot bot mentioned this pull request Feb 22, 2026
yamcodes pushed a commit that referenced this pull request Feb 22, 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 main, this PR will
be updated.


# Releases
## arkenv@0.10.0

### Minor Changes

- #### `arkenv/standard` import
_[`#806`](#806)
[`f9010d0`](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
    ```

## @arkenv/bun-plugin@0.1.4

### Patch Changes

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

<small>


[`f9010d0`](f9010d0)

</small>

-   `arkenv@0.10.0`

</details>

## @arkenv/vite-plugin@0.0.31

### Patch Changes

- #### `arkenv/standard` import
_[`#806`](#806)
[`f9010d0`](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
    ```

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

<small>


[`f9010d0`](f9010d0)

</small>

-   `arkenv@0.10.0`

</details>

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

@arkenv/vite-plugin Issues or Pull Requests involving the Vite plugin for ArkEnv arkenv Changes to the `arkenv` npm package. docs Improvements or additions to documentation example Issues or Pull Requests concerning at least one ArkEnv example. (Found in the `examples/` directory) github actions Pull requests that update GitHub Actions code tests This issue or PR is about adding, removing or changing tests www Improvements or additions to arkenv.js.org

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant