Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 4, 2026

Environment variables can now be defined as JSON objects with automatic parsing and nested type coercion.

Implementation

  • maybeParsedJSON keyword: Parses JSON strings (starting with { or [) into objects/arrays, returns original input on failure
  • Two-pass coercion: First parses JSON strings to objects, then coerces nested primitives (numbers, booleans) to target types
  • Schema detection: findCoercionPaths identifies object types requiring JSON parsing via JSON Schema introspection

Usage

import arkenv from 'arkenv';

const env = arkenv({
  DATABASE: {
    host: 'string',
    port: 'number',
    ssl: 'boolean'
  }
});

// Environment: DATABASE='{"host": "localhost", "port": "5432", "ssl": "true"}'
// Result: { host: "localhost", port: 5432, ssl: true }

Works with nested objects, arrays within objects, optional fields, and compiled type schemas. Invalid JSON or type mismatches produce clear validation errors.

Original prompt

This section details on the original issue you should resolve

<issue_title>Object parsing</issue_title>
<issue_description>Add the ability to define objects within environment variables, and for ArkEnv to be able to parse/coerce them into JavaScript objects.
</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Summary by CodeRabbit

New Features

  • ArkEnv now supports object coercion: JSON strings in environment variables are automatically parsed into nested, type-safe objects when the coerce option is enabled (default true). Inner properties are recursively coerced to their defined types.

Documentation

  • Updated coercion documentation with examples demonstrating object parsing from JSON strings and nested type coercion.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Jan 4, 2026

🦋 Changeset detected

Latest commit: 1b7231f

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

This PR includes changesets to release 6 packages
Name Type
@repo/keywords Patch
arkenv Patch
@repo/scope Patch
@arkenv/bun-plugin Patch
@arkenv/vite-plugin Patch
@repo/types 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 Jan 4, 2026

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR introduces object coercion to ArkEnv, enabling JSON strings in environment variables to be parsed into type-safe nested objects. A new maybeParsedJSON utility is added to the keywords module, the coercion engine is extended to recognize and handle object targets, and comprehensive tests and documentation are provided.

Changes

Cohort / File(s) Summary
Core Coercion Logic
packages/arkenv/src/utils/coerce.ts
Extended CoercionTarget.type to include "object", implemented JSON string parsing for object targets, added deduplication by path + type, introduced pre-walk sorting, enhanced walker to recognize and coerce object targets recursively, and updated root-level logic for object/array handling
Utility Functions
packages/internal/keywords/src/index.ts, packages/internal/keywords/src/index.test.ts
Added new public export maybeParsedJSON that conditionally parses JSON strings (objects/arrays) and returns the parsed result or original input on parse failure
Unit Tests
packages/arkenv/src/utils/coerce.test.ts
Added comprehensive test suite covering JSON object parsing, nested object coercion, invalid JSON error handling, and arrays of objects with arrayFormat: "json"
Integration Tests
packages/arkenv/src/object-parsing.integration.test.ts, packages/arkenv/src/create-env.test.ts
Added extensive integration tests for top-level and nested object parsing from JSON environment variables, type coercion within objects, mixed JSON and separate variable sources, optional properties, and whitespace tolerance
Documentation
apps/www/content/docs/arkenv/coercion.mdx
Added new "Objects" section describing JSON-to-object parsing, demonstrating nested object examples with recursive type coercion, and explaining recursive coercion behavior
UI & Metadata
apps/www/app/(home)/page.tsx, .changeset/moody-humans-knock.md
Updated AnnouncementBadge href and label from "Array coercion" to "Object & Array coercion"; added changelog entry documenting the new object coercion feature
VS Code Configuration
.vscode/tasks.json
Added new npm task entry for running the "changeset" script

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • PR #572: Both modify packages/arkenv/src/utils/coerce.ts, extending coercion logic and target type handling.
  • PR #565: Both touch the keywords module (packages/internal/keywords/src/index.ts) and implement schema-driven coercion enhancements.
  • PR #693: Both extend the coercion engine with new CoercionTarget types and traversal logic (array parsing vs. object/JSON parsing).

Poem

🐰 A JSON string, once flat and plain,
Now dances through our coerce domain!
Objects bloom where strings once lay,
Nested fields find their type-safe way. 🌱✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Object coercion' directly and clearly describes the main feature being introduced—support for parsing and coercing JSON strings into JavaScript objects with nested type handling.
Linked Issues check ✅ Passed The PR fully implements the linked issue #575 requirements: it adds object parsing capability and enables JSON string coercion into JavaScript objects with nested type coercion.
Out of Scope Changes check ✅ Passed All changes are directly scoped to object parsing: maybeParsedJSON utility, coerce function enhancements, schema detection, tests, and documentation updates align with the PR objectives.

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

Copilot AI and others added 3 commits January 4, 2026 17:35
Co-authored-by: yamcodes <2014360+yamcodes@users.noreply.github.com>
Co-authored-by: yamcodes <2014360+yamcodes@users.noreply.github.com>
Co-authored-by: yamcodes <2014360+yamcodes@users.noreply.github.com>
Copilot AI changed the title [WIP] Add object parsing for environment variables in ArkEnv Add object parsing for environment variables with JSON coercion Jan 4, 2026
Copilot AI requested a review from yamcodes January 4, 2026 17:49
@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 Jan 4, 2026
@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 4, 2026

Open in StackBlitz

npm i https://pkg.pr.new/arkenv@694
npm i https://pkg.pr.new/@arkenv/bun-plugin@694
npm i https://pkg.pr.new/@arkenv/vite-plugin@694

commit: 1b7231f

@arkenv-bot
Copy link
Contributor

arkenv-bot bot commented Jan 4, 2026

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

Project Deployment Review Updated (Asia/Almaty)
arkenv Ready Ready Preview, Comment Jan 6 2026, 1:28 AM (Asia/Almaty)

@yamcodes yamcodes changed the title Add object parsing for environment variables with JSON coercion Object coercion Jan 4, 2026
@github-actions github-actions bot added docs Improvements or additions to documentation www Improvements or additions to arkenv.js.org labels Jan 5, 2026
@yamcodes yamcodes marked this pull request as ready for review January 5, 2026 20:11
@yamcodes
Copy link
Owner

yamcodes commented Jan 5, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 5, 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.

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: 0

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/coerce.ts (1)

189-209: Unreachable dead code — duplicate condition block.

Lines 189-209 are unreachable. The condition if (typeof data !== "object" || data === null) at line 189 can never be true at this point because:

  1. Line 162 already checks if (typeof data !== "object" || data === null)
  2. If true, lines 163-181 handle it and return
  3. If false (data is an object), execution reaches line 184
  4. The condition at line 189 checks the same thing but will always be false

This appears to be a copy-paste error where the root coercion block was duplicated.

🔎 Proposed fix: Remove the unreachable code block
 	// Sort targets by path length to ensure parent objects/arrays are coerced before their children
 	const sortedTargets = [...targets].sort(
 		(a, b) => a.path.length - b.path.length,
 	);
-
-	if (typeof data !== "object" || data === null) {
-		// If root data needs coercion
-		if (targets.some((t) => t.path.length === 0)) {
-			const rootTarget = targets.find((t) => t.path.length === 0);
-
-			if (rootTarget?.type === "object" && typeof data === "string") {
-				return maybeParsedJSON(data);
-			}
-
-			if (rootTarget?.type === "array" && typeof data === "string") {
-				return splitString(data);
-			}
-
-			const asNumber = maybeNumber(data);
-			if (typeof asNumber === "number") {
-				return asNumber;
-			}
-			return maybeBoolean(data);
-		}
-		return data;
-	}

 	const walk = (
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a722ab and 35f6a35.

📒 Files selected for processing (10)
  • .changeset/moody-humans-knock.md
  • .vscode/tasks.json
  • apps/www/app/(home)/page.tsx
  • apps/www/content/docs/arkenv/coercion.mdx
  • packages/arkenv/src/create-env.test.ts
  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/arkenv/src/utils/coerce.test.ts
  • packages/arkenv/src/utils/coerce.ts
  • packages/internal/keywords/src/index.test.ts
  • packages/internal/keywords/src/index.ts
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{ts,tsx}: Prefer type over interface for type definitions in TypeScript
Use TypeScript 5.1+ features when appropriate
Leverage const type parameters for better inference in TypeScript
Use JSDoc comments for public APIs
Use tabs for indentation (configured in Biome)
Use double quotes for strings (configured in Biome)
Organize imports automatically (Biome handles this)
Avoid explicit types when TypeScript can infer them (noInferrableTypes error)
Use as const where appropriate for immutable values (useAsConstAssertion error)
Don't reassign function parameters (noParameterAssign error)
Place default parameters last in function signatures (useDefaultParameterLast error)
Always initialize enum values (useEnumInitializers error)
Declare one variable per statement (useSingleVarDeclarator error)
Avoid unnecessary template literals (noUnusedTemplateLiteral error)
Prefer Number.parseInt over global parseInt (useNumberNamespace error)
Use kebab-case for TypeScript filenames (e.g., create-env.ts)
Use camelCase for function names (e.g., createEnv)
Use PascalCase for type names (e.g., ArkEnvError)
Use UPPER_SNAKE_CASE for environment variables and constants
Include examples in JSDoc comments when helpful for public APIs
Document complex type logic with JSDoc comments
Use ArkEnvError for environment variable validation errors
Provide clear, actionable error messages that include the variable name and expected type

**/*.{ts,tsx}: Use createEnv(schema) function (or default import as arkenv) to create validated environment objects in TypeScript
Use built-in validators (host, port, url, email) from ArkEnv when available instead of custom ArkType schemas
Provide default values for optional environment variables using ArkType syntax (e.g., 'boolean = false')
Use ArkEnvError for environment variable errors instead of generic Error types
For environment schema definition, use ArkType string literal syntax for enumerated values (e.g., "'deve...

Files:

  • packages/internal/keywords/src/index.ts
  • packages/arkenv/src/utils/coerce.test.ts
  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/internal/keywords/src/index.test.ts
  • packages/arkenv/src/create-env.test.ts
  • apps/www/app/(home)/page.tsx
  • packages/arkenv/src/utils/coerce.ts
**/index.ts

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

Use barrel exports (index.ts) for package entry points

Files:

  • packages/internal/keywords/src/index.ts
packages/arkenv/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/arktype.mdc)

packages/arkenv/**/*.ts: Use ArkType's type() function to define schemas in environment variable definitions
Leverage ArkType's type inference for TypeScript types instead of manual type definitions
Use the scoped $ type system for custom types defined in scope.ts
Keep environment variable schemas readable and TypeScript-like using ArkType syntax
Use union types for enums in ArkType schemas (e.g., "'dev' | 'prod'") instead of separate enum definitions
Leverage ArkType's built-in types (e.g., string.host, number.port) where possible in environment schemas
Convert ArkType validation errors to ArkEnvError for user-friendly error messages that include variable name and expected type

Files:

  • packages/arkenv/src/utils/coerce.test.ts
  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/arkenv/src/create-env.test.ts
  • packages/arkenv/src/utils/coerce.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

Co-locate tests with components: Component.tsx next to Component.test.tsx

**/*.test.{ts,tsx}: Use Vitest for unit and integration tests
Test individual functions, components, and hooks in isolation with mocked dependencies in unit tests
Unit tests should focus on individual function logic and edge cases, component rendering and props, error handling and validation, and type checking
Unit tests should execute in less than 100ms per test
Mock external dependencies (clipboard, network, etc.) in unit tests
Co-locate unit test files with source files using naming convention: source file → test file (e.g., create-env.ts → create-env.test.ts)
Test component behavior, not aesthetics, and focus on what users can do and what the component guarantees through its API
Test component public API (props, events, and component contract), user behavior (clicks, typing, focus, keyboard, ARIA), state transitions, accessibility, and side effects in component tests
Do not test pure styling or CSS classes, library internals (Radix/shadcn), implementation details (hooks, setState, private variables), or visual variants in component tests
Use Testing Library with user-event for real user simulation in component tests
Query by role, name, label, and text (accessibility first) in component tests
Use beforeEach/afterEach for cleanup, not beforeAll/afterAll when possible
Keep tests fast, deterministic, and parallelizable
Mock at component boundaries (network, time, context)

Create unit tests with .test.ts or .test.tsx suffix located alongside source files, testing individual functions and components in isolation with mocked dependencies

Files:

  • packages/arkenv/src/utils/coerce.test.ts
  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/internal/keywords/src/index.test.ts
  • packages/arkenv/src/create-env.test.ts
**/*.{test,integration.test}.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{test,integration.test}.{ts,tsx}: Use Vitest's describe/it structure for all test files
Test both success and failure cases in unit and integration tests
Mock process.env in unit tests to test different environment variable scenarios

Files:

  • packages/arkenv/src/utils/coerce.test.ts
  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/internal/keywords/src/index.test.ts
  • packages/arkenv/src/create-env.test.ts
**/*.integration.test.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/test-patterns.mdc)

**/*.integration.test.{ts,tsx}: Test how multiple units (components, hooks, functions) work together without mocking their interactions in integration tests
Integration tests should focus on component and hook interactions, function composition and data flow, real dependencies between units, and state synchronization across boundaries
Integration tests should execute between 100ms - 2000ms per test
Use *.integration.test.ts suffix to distinguish integration tests from unit tests

Create integration tests with .integration.test.ts or .integration.test.tsx suffix, testing how multiple units work together without mocking their interactions (except external APIs)

Files:

  • packages/arkenv/src/object-parsing.integration.test.ts
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

Use self-closing JSX elements (useSelfClosingElements error)

Files:

  • apps/www/app/(home)/page.tsx
apps/www/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/www/.cursor/rules/posthog-integration.mdc)

apps/www/**/*.{ts,tsx,js,jsx}: If using TypeScript, use an enum to store feature flag names. If using JavaScript, store feature flag names as strings to an object declared as a constant to simulate an enum. Use UPPERCASE_WITH_UNDERSCORE naming convention for enum/const object members.
If a custom property for a person or event is referenced in two or more files or two or more callsites in the same file, use an enum or const object with UPPERCASE_WITH_UNDERSCORE naming convention, similar to feature flags.

Files:

  • apps/www/app/(home)/page.tsx
🧠 Learnings (31)
📓 Common learnings
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/arktype.mdc:0-0
Timestamp: 2025-11-24T16:03:45.295Z
Learning: Applies to packages/arkenv/**/*.ts : Keep environment variable schemas readable and TypeScript-like using ArkType syntax
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/arktype.mdc:0-0
Timestamp: 2025-11-24T16:03:45.295Z
Learning: Applies to packages/arkenv/**/*.ts : Leverage ArkType's built-in types (e.g., `string.host`, `number.port`) where possible in environment schemas
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/arktype.mdc:0-0
Timestamp: 2025-11-24T16:03:45.295Z
Learning: Applies to packages/arkenv/**/*.ts : Convert ArkType validation errors to `ArkEnvError` for user-friendly error messages that include variable name and expected type
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T07:09:57.130Z
Learning: Applies to **/*.{ts,tsx} : Use ArkEnvError for environment variable errors instead of generic Error types
📚 Learning: 2025-11-24T16:03:45.295Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/arktype.mdc:0-0
Timestamp: 2025-11-24T16:03:45.295Z
Learning: Applies to packages/arkenv/**/*.ts : Keep environment variable schemas readable and TypeScript-like using ArkType syntax

Applied to files:

  • apps/www/content/docs/arkenv/coercion.mdx
  • packages/arkenv/src/utils/coerce.test.ts
  • .changeset/moody-humans-knock.md
  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/arkenv/src/create-env.test.ts
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-12-23T07:09:57.130Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T07:09:57.130Z
Learning: Applies to **/*.{ts,tsx} : Use `createEnv(schema)` function (or default import as `arkenv`) to create validated environment objects in TypeScript

Applied to files:

  • apps/www/content/docs/arkenv/coercion.mdx
  • .changeset/moody-humans-knock.md
  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/arkenv/src/create-env.test.ts
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-11-24T16:03:45.295Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/arktype.mdc:0-0
Timestamp: 2025-11-24T16:03:45.295Z
Learning: Applies to packages/arkenv/**/*.ts : Leverage ArkType's built-in types (e.g., `string.host`, `number.port`) where possible in environment schemas

Applied to files:

  • apps/www/content/docs/arkenv/coercion.mdx
  • packages/arkenv/src/utils/coerce.test.ts
  • .changeset/moody-humans-knock.md
  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/arkenv/src/create-env.test.ts
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-11-24T16:03:45.295Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/arktype.mdc:0-0
Timestamp: 2025-11-24T16:03:45.295Z
Learning: Applies to packages/arkenv/**/*.ts : Use ArkType's `type()` function to define schemas in environment variable definitions

Applied to files:

  • apps/www/content/docs/arkenv/coercion.mdx
  • packages/arkenv/src/utils/coerce.test.ts
  • .changeset/moody-humans-knock.md
  • packages/arkenv/src/create-env.test.ts
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-11-24T16:04:00.957Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/coding-guidelines.mdc:0-0
Timestamp: 2025-11-24T16:04:00.957Z
Learning: Applies to **/*.{ts,tsx} : Use `ArkEnvError` for environment variable validation errors

Applied to files:

  • apps/www/content/docs/arkenv/coercion.mdx
  • .changeset/moody-humans-knock.md
  • packages/arkenv/src/create-env.test.ts
📚 Learning: 2025-11-24T16:03:45.295Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/arktype.mdc:0-0
Timestamp: 2025-11-24T16:03:45.295Z
Learning: Applies to packages/arkenv/**/*.ts : Convert ArkType validation errors to `ArkEnvError` for user-friendly error messages that include variable name and expected type

Applied to files:

  • apps/www/content/docs/arkenv/coercion.mdx
  • packages/arkenv/src/utils/coerce.test.ts
  • .changeset/moody-humans-knock.md
  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/arkenv/src/create-env.test.ts
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-12-23T07:09:57.130Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T07:09:57.130Z
Learning: Applies to **/*.{ts,tsx} : Provide default values for optional environment variables using ArkType syntax (e.g., 'boolean = false')

Applied to files:

  • apps/www/content/docs/arkenv/coercion.mdx
  • .changeset/moody-humans-knock.md
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-12-23T07:09:57.130Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T07:09:57.130Z
Learning: Applies to **/*.{ts,tsx} : For environment schema definition, use ArkType string literal syntax for enumerated values (e.g., "'development' | 'production' | 'test'")

Applied to files:

  • apps/www/content/docs/arkenv/coercion.mdx
  • packages/arkenv/src/utils/coerce.test.ts
  • .changeset/moody-humans-knock.md
  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/arkenv/src/create-env.test.ts
  • apps/www/app/(home)/page.tsx
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-11-24T16:03:45.295Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/arktype.mdc:0-0
Timestamp: 2025-11-24T16:03:45.295Z
Learning: ArkType validates environment variables at runtime and TypeScript types are inferred from the schema definition

Applied to files:

  • apps/www/content/docs/arkenv/coercion.mdx
  • .changeset/moody-humans-knock.md
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-12-23T07:09:57.130Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T07:09:57.130Z
Learning: Applies to **/*.{ts,tsx} : Use ArkEnvError for environment variable errors instead of generic Error types

Applied to files:

  • apps/www/content/docs/arkenv/coercion.mdx
  • .changeset/moody-humans-knock.md
📚 Learning: 2025-12-22T19:44:11.474Z
Learnt from: yamcodes
Repo: yamcodes/arkenv PR: 596
File: examples/basic/index.ts:4-5
Timestamp: 2025-12-22T19:44:11.474Z
Learning: In examples/basic/index.ts: Use explicit ArkType syntax (e.g., "string.ip | 'localhost'", "0 <= number.integer <= 65535") instead of built-in validators (string.host, number.port) to showcase ArkType's type system capabilities for educational purposes.

Applied to files:

  • packages/internal/keywords/src/index.ts
  • packages/arkenv/src/utils/coerce.test.ts
  • .changeset/moody-humans-knock.md
  • packages/internal/keywords/src/index.test.ts
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-11-24T16:03:45.295Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/arktype.mdc:0-0
Timestamp: 2025-11-24T16:03:45.295Z
Learning: Applies to packages/arkenv/**/*.ts : Use union types for enums in ArkType schemas (e.g., `"'dev' | 'prod'"`) instead of separate enum definitions

Applied to files:

  • packages/arkenv/src/utils/coerce.test.ts
  • .changeset/moody-humans-knock.md
  • packages/arkenv/src/create-env.test.ts
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-12-23T07:09:57.130Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T07:09:57.130Z
Learning: Applies to **/*.{test,integration.test}.{ts,tsx} : Test both success and failure cases in unit and integration tests

Applied to files:

  • packages/arkenv/src/utils/coerce.test.ts
  • packages/arkenv/src/object-parsing.integration.test.ts
📚 Learning: 2025-11-24T16:03:45.295Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/arktype.mdc:0-0
Timestamp: 2025-11-24T16:03:45.295Z
Learning: Applies to packages/arkenv/**/*.ts : Leverage ArkType's type inference for TypeScript types instead of manual type definitions

Applied to files:

  • packages/arkenv/src/utils/coerce.test.ts
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-11-24T16:04:11.901Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/monorepo.mdc:0-0
Timestamp: 2025-11-24T16:04:11.901Z
Learning: Applies to tooling/*/package.json : Tooling in tooling/ directory contains development and testing tools that are not published to npm and excluded from changesets

Applied to files:

  • .vscode/tasks.json
📚 Learning: 2025-11-24T16:04:11.901Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/monorepo.mdc:0-0
Timestamp: 2025-11-24T16:04:11.901Z
Learning: Applies to turbo.json : Turborepo tasks are defined in turbo.json with common tasks: build, dev, typecheck, test, and test:e2e

Applied to files:

  • .vscode/tasks.json
📚 Learning: 2025-12-23T07:09:57.130Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T07:09:57.130Z
Learning: Always run `pnpm changeset` for version bumps in published packages instead of manually modifying package.json versions

Applied to files:

  • .vscode/tasks.json
📚 Learning: 2025-12-12T13:20:01.954Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/test-patterns.mdc:0-0
Timestamp: 2025-12-12T13:20:01.954Z
Learning: Applies to **/*.integration.test.{ts,tsx} : Integration tests should focus on component and hook interactions, function composition and data flow, real dependencies between units, and state synchronization across boundaries

Applied to files:

  • packages/arkenv/src/object-parsing.integration.test.ts
📚 Learning: 2025-12-12T13:20:01.954Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/test-patterns.mdc:0-0
Timestamp: 2025-12-12T13:20:01.954Z
Learning: Applies to **/*.integration.test.{ts,tsx} : Test how multiple units (components, hooks, functions) work together without mocking their interactions in integration tests

Applied to files:

  • packages/arkenv/src/object-parsing.integration.test.ts
📚 Learning: 2025-12-23T07:09:57.130Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T07:09:57.130Z
Learning: Applies to **/*.integration.test.{ts,tsx} : Create integration tests with `.integration.test.ts` or `.integration.test.tsx` suffix, testing how multiple units work together without mocking their interactions (except external APIs)

Applied to files:

  • packages/arkenv/src/object-parsing.integration.test.ts
📚 Learning: 2025-12-23T07:09:57.130Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T07:09:57.130Z
Learning: Applies to **/*.{test,integration.test}.{ts,tsx} : Mock `process.env` in unit tests to test different environment variable scenarios

Applied to files:

  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/arkenv/src/create-env.test.ts
📚 Learning: 2025-12-26T19:27:11.710Z
Learnt from: danciudev
Repo: yamcodes/arkenv PR: 614
File: packages/vite-plugin/src/index.test.ts:641-654
Timestamp: 2025-12-26T19:27:11.710Z
Learning: In packages/vite-plugin/src/**/*.test.ts: The test suite uses `env.test` files (without leading dot) as test fixtures that are manually read by the `readTestConfig` helper function and stubbed into process.env with `vi.stubEnv`, not as files to be read by Vite's loadEnv during tests.

Applied to files:

  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/internal/keywords/src/index.test.ts
  • packages/arkenv/src/create-env.test.ts
📚 Learning: 2025-12-23T07:09:57.130Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T07:09:57.130Z
Learning: Applies to **/*.{test,integration.test}.{ts,tsx} : Use Vitest's `describe`/`it` structure for all test files

Applied to files:

  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/internal/keywords/src/index.test.ts
📚 Learning: 2025-12-12T13:20:01.954Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/test-patterns.mdc:0-0
Timestamp: 2025-12-12T13:20:01.954Z
Learning: Applies to packages/vite-plugin/src/**/*.test.ts : Test the Vite plugin using the with-vite-react example as a fixture and validate that the plugin works with real Vite projects

Applied to files:

  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/internal/keywords/src/index.test.ts
📚 Learning: 2025-12-12T13:20:01.954Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/test-patterns.mdc:0-0
Timestamp: 2025-12-12T13:20:01.954Z
Learning: Applies to **/*.test.{ts,tsx} : Use Vitest for unit and integration tests

Applied to files:

  • packages/arkenv/src/object-parsing.integration.test.ts
  • packages/internal/keywords/src/index.test.ts
📚 Learning: 2025-12-12T13:20:01.954Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/test-patterns.mdc:0-0
Timestamp: 2025-12-12T13:20:01.954Z
Learning: Applies to **/*.test.{ts,tsx} : Test component public API (props, events, and component contract), user behavior (clicks, typing, focus, keyboard, ARIA), state transitions, accessibility, and side effects in component tests

Applied to files:

  • packages/internal/keywords/src/index.test.ts
📚 Learning: 2025-12-12T13:20:01.954Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/test-patterns.mdc:0-0
Timestamp: 2025-12-12T13:20:01.954Z
Learning: Applies to **/*.test.{ts,tsx} : Co-locate unit test files with source files using naming convention: source file → test file (e.g., create-env.ts → create-env.test.ts)

Applied to files:

  • packages/arkenv/src/create-env.test.ts
📚 Learning: 2025-12-25T21:14:15.510Z
Learnt from: yamcodes
Repo: yamcodes/arkenv PR: 616
File: apps/www/app/(home)/page.tsx:18-18
Timestamp: 2025-12-25T21:14:15.510Z
Learning: In projects using fumadocs-ui's HomeLayout, do not render a top-level <main> inside page components because HomeLayout already provides a main landmark with id="nd-home-layout". Ensure that page.tsx (and other pages that compose HomeLayout) return content without wrapping a new <main>, and rely on HomeLayout's landmark for accessibility. If a page needs a landmark, use subregions like <section> or <div role="region"> inside the existing main, but avoid duplicate main elements. Verify that HomeLayout is used and that the id remains unique.

Applied to files:

  • apps/www/app/(home)/page.tsx
📚 Learning: 2025-11-24T16:03:45.295Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .cursor/rules/arktype.mdc:0-0
Timestamp: 2025-11-24T16:03:45.295Z
Learning: Applies to packages/arkenv/scope.ts : Define custom types in `scope.ts` using ArkType's scoped type system for reusability across schemas

Applied to files:

  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-12-23T07:09:57.130Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T07:09:57.130Z
Learning: Applies to **/*.{ts,tsx} : Use built-in validators (host, port, url, email) from ArkEnv when available instead of custom ArkType schemas

Applied to files:

  • packages/arkenv/src/utils/coerce.ts
🧬 Code graph analysis (6)
packages/internal/keywords/src/index.ts (1)
packages/arkenv/src/type.ts (1)
  • type (3-3)
packages/arkenv/src/utils/coerce.test.ts (2)
packages/arkenv/src/type.ts (1)
  • type (3-3)
packages/arkenv/src/utils/coerce.ts (1)
  • coerce (315-341)
packages/arkenv/src/object-parsing.integration.test.ts (1)
packages/arkenv/src/type.ts (1)
  • type (3-3)
packages/internal/keywords/src/index.test.ts (1)
packages/internal/keywords/src/index.ts (1)
  • maybeParsedJSON (56-66)
apps/www/app/(home)/page.tsx (1)
apps/www/components/announcement-badge.tsx (1)
  • AnnouncementBadge (7-48)
packages/arkenv/src/utils/coerce.ts (2)
packages/internal/keywords/src/index.ts (3)
  • maybeParsedJSON (56-66)
  • maybeNumber (12-20)
  • maybeBoolean (31-35)
packages/arkenv/src/type.ts (1)
  • type (3-3)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: test-e2e (a11y)
  • GitHub Check: test-e2e (e2e)
  • GitHub Check: test-typesafety
  • GitHub Check: test-build (lts/*)
  • GitHub Check: test-build (latest)
  • GitHub Check: Deploy-Preview
🔇 Additional comments (12)
.vscode/tasks.json (1)

21-27: LGTM! Useful developer task added.

The changeset task is properly configured and follows the established pattern. This makes it easier to run pnpm changeset directly from VS Code's task runner, aligning with the project's workflow for version management.

apps/www/app/(home)/page.tsx (1)

28-30: LGTM! Announcement badge correctly updated.

The href and label accurately reflect the new object coercion feature while maintaining backward compatibility by including "Array" in the label. The target anchor #objects exists in the coercion documentation.

.changeset/moody-humans-knock.md (1)

1-25: LGTM! Clear and well-structured changeset.

The changeset properly documents the new object coercion feature with:

  • Appropriate patch version bump
  • Clear description of the functionality
  • Practical example demonstrating JSON string parsing and nested type coercion (PORT as number)

The example effectively showcases both the dotenv and TypeScript usage patterns.

apps/www/content/docs/arkenv/coercion.mdx (1)

71-94: LGTM! Excellent documentation of object coercion.

The new Objects section clearly explains:

  • JSON string parsing into objects
  • Practical use case (grouping related configuration)
  • Recursive coercion behavior (PORT becoming a number)

The example follows ArkType syntax conventions and demonstrates the feature effectively. The documentation integrates seamlessly with existing content.

packages/arkenv/src/utils/coerce.test.ts (1)

159-254: LGTM! Comprehensive test coverage for object coercion.

The new test cases effectively cover the object coercion feature:

  • JSON parsing: Basic objects and nested structures
  • Type coercion: Numbers and booleans within parsed objects
  • Error handling: Invalid JSON strings producing ArkErrors
  • Integration: Mixed object and primitive coercion scenarios

The tests follow project conventions with clear names, proper assertions, and coverage of both success and failure cases. They align well with the existing test structure and coding guidelines.

packages/internal/keywords/src/index.ts (1)

46-66: LGTM! Well-implemented JSON parsing morph.

The implementation follows the established pattern of other loose morphs (maybeNumber, maybeBoolean), with appropriate trimming, prefix checking for JSON-like strings, and graceful fallback on parse failure. The JSDoc documentation is clear and consistent.

packages/internal/keywords/src/index.test.ts (1)

39-80: Comprehensive test coverage for the new maybeParsedJSON utility.

Tests cover the key scenarios: valid JSON objects/arrays, nested structures, invalid JSON, non-JSON strings, non-string inputs, object passthrough, and whitespace handling. This aligns well with the implementation behavior.

packages/arkenv/src/create-env.test.ts (1)

575-630: Well-structured object coercion test suite.

The tests effectively validate the new object coercion functionality with good coverage of:

  • Basic JSON string to object parsing with type coercion
  • Nested object structures
  • Error handling for invalid JSON
  • Arrays of objects with arrayFormat: "json"

The tests follow the project's testing conventions using Vitest's describe/it structure and properly mock environment variables.

packages/arkenv/src/object-parsing.integration.test.ts (1)

1-303: Excellent integration test coverage for object parsing.

This integration test file thoroughly validates the object coercion feature across multiple scenarios including string/number/boolean coercion, nested structures, optional properties, compiled schemas, arrays within objects, whitespace handling, and default values. The tests exercise the full integration between createEnv, type(), and the coercion system without mocking internal interactions, which is appropriate for integration tests per coding guidelines.

packages/arkenv/src/utils/coerce.ts (3)

63-84: Well-designed object detection and recursive property analysis.

The logic correctly identifies objects with defined properties for JSON parsing coercion, while also recursively checking nested properties for their own coercion requirements. This two-level approach ensures both the JSON string parsing and nested primitive coercion work together.


128-135: Good enhancement to deduplication logic.

Using path + type as the deduplication key prevents conflicts when the same path might be targeted by different coercion types (e.g., an object path and its nested primitive children).


184-187: Correct sorting strategy for parent-first coercion.

Sorting by path length ensures parent objects are parsed from JSON strings before attempting to coerce their nested children. This is essential for the two-pass coercion approach to work correctly.

@yamcodes yamcodes merged commit 01c1704 into main Jan 5, 2026
17 of 19 checks passed
@yamcodes yamcodes deleted the copilot/add-object-parsing-capability branch January 5, 2026 20:32
@arkenv-bot arkenv-bot bot mentioned this pull request Jan 5, 2026
yamcodes pushed a commit that referenced this pull request Jan 5, 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.8.3

### Patch Changes

- #### Object coercion
_[`#694`](#694)
[`01c1704`](01c1704)
[@copilot-swe-agent](https://github.com/apps/copilot-swe-agent)_

ArkEnv now coerces objects when the `coerce` option is enabled (true by
default).
Objects are parsed from JSON strings, allowing for nested typesafe
configuration.

    Example:

    ```dotenv
    DATABASE={"HOST": "localhost", "PORT": "5432"}
    ```

    ```ts
    const env = arkenv({
      DATABASE: {
        HOST: "string",
        PORT: "number",
      },
    });

    console.log(env.DATABASE.PORT); // 5432 (number)
    ```

## @arkenv/bun-plugin@0.0.8

### Patch Changes

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

<small>


[`01c1704`](01c1704)

</small>

-   `arkenv@0.8.3`

</details>

## @arkenv/vite-plugin@0.0.26

### Patch Changes

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

<small>


[`01c1704`](01c1704)

</small>

-   `arkenv@0.8.3`

</details>

## @repo/keywords@0.2.1

### Patch Changes

- #### Add `maybeJson` keyword
_[`#694`](#694)
[`01c1704`](01c1704)
[@copilot-swe-agent](https://github.com/apps/copilot-swe-agent)_

    JSDoc:

    ```ts
    /**
     * A loose JSON morph.
     *
     * **In**: `unknown`
     *
* **Out**: A parsed JSON object if the input is a valid JSON string;
otherwise the original input.
     *
* Useful for coercion in unions where failing on non-JSON strings would
block other branches.
     */
    ```

## @repo/scope@0.1.2

### Patch Changes

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

<small>


[`01c1704`](01c1704)

</small>

-   `@repo/keywords@0.2.1`

</details>

## @repo/types@0.0.6

### Patch Changes

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

<small>

</small>

-   `@repo/scope@0.1.2`

</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 Changes to the `arkenv` npm package. docs Improvements or additions to documentation 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.

Object parsing

2 participants