Skip to content

feat(arkenv): Fix coercion crashes for predicates#595

Merged
yamcodes merged 2 commits intocoercionfrom
594-coercion-fix-stringurl
Dec 22, 2025
Merged

feat(arkenv): Fix coercion crashes for predicates#595
yamcodes merged 2 commits intocoercionfrom
594-coercion-fix-stringurl

Conversation

@yamcodes
Copy link
Owner

@yamcodes yamcodes commented Dec 22, 2025

  • Use toJsonSchema fallback
  • Handle unjsonifiable types
  • Preserve base schema structure
  • Use public API for introspection

Closes #594

Summary by CodeRabbit

  • Improvements

    • Enhanced schema introspection robustness with a fallback mechanism that preserves base schema structure for types that cannot be fully represented in standard formats, enabling more resilient coercion handling.
  • Documentation

    • Updated design and proposal documentation for schema introspection workflows.

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

- Use `toJsonSchema` fallback
- Handle unjsonifiable types
- Preserve base schema structure
- Use public API for introspection
@changeset-bot
Copy link

changeset-bot bot commented Dec 22, 2025

⚠️ No Changeset found

Latest commit: 0727670

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@vercel
Copy link

vercel bot commented Dec 22, 2025

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

Project Deployment Review Updated (UTC)
arkenv Ready Ready Preview, Comment Dec 22, 2025 7:26pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. 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

The changes introduce a fallback mechanism to schema.in.toJsonSchema() that preserves base schema information when encountering unjsonifiable constraints like custom predicates. The fallback parameter { fallback: (ctx) => ctx.base } enables graceful handling of types such as string.url during coercion, preventing errors from predicates that cannot be represented in JSON Schema.

Changes

Cohort / File(s) Summary
Schema Introspection Fallback
packages/arkenv/src/utils/coerce.ts
Updated coerce() to call schema.in.toJsonSchema() with a fallback option that preserves ctx.base for unjsonifiable types, allowing partial schema representation instead of throwing errors.
Design & Proposal Documentation
openspec/changes/coercion-public-api/design.md, openspec/changes/coercion-public-api/proposal.md
Reworked design documentation to frame the fallback mechanism and JSON Schema-based path traversal; updated proposal to specify the exact fallback signature { fallback: (ctx) => ctx.base }; expanded rules for type mapping and trade-offs rationale.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10-15 minutes

  • Implementation change is minimal: Single parameter addition to existing function call in coerce.ts
  • Straightforward fallback approach: No complex new logic; leverages existing ctx.base mechanism
  • Documentation largely narrative: Restructuring of design rationale rather than new concepts
  • Focus area for review: Verify that the fallback preserves base schema correctly for types with predicates (e.g., string.url) and that coercion paths remain valid for representable portions

Possibly related PRs

Poem

🐰 Through schemas that shimmer and dance,
A fallback now grants second chance,
When predicates hide from JSON's view,
The base shines bright, forever true—
Coercion hops forward without a glance! ✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(arkenv): Fix coercion crashes for predicates' clearly and specifically identifies the main change—fixing crashes caused by predicates in the coercion system, which aligns with the changeset focused on handling unjsonifiable types like string.url.
Linked Issues check ✅ Passed The pull request fully addresses the requirements in issue #594: it prevents crashes when using predicates like string.url in createEnv by implementing a toJsonSchema fallback mechanism that preserves base schema structure while skipping unjsonifiable constraints.
Out of Scope Changes check ✅ Passed All changes are in-scope: the design documentation updates explain the fallback approach, the proposal details the implementation strategy, and the coerce.ts modification implements the fix for handling unjsonifiable types—all directly supporting the objective to fix predicate-related coercion crashes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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 docs Improvements or additions to documentation arkenv Changes to the `arkenv` npm package. labels Dec 22, 2025
@yamcodes yamcodes changed the title feat(arkenv): improve coercion robustness feat(arkenv): Fix coercion crashes for predicates Dec 22, 2025
@yamcodes
Copy link
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

✅ 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: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32522e3 and 5880f50.

📒 Files selected for processing (3)
  • openspec/changes/coercion-public-api/design.md
  • openspec/changes/coercion-public-api/proposal.md
  • packages/arkenv/src/utils/coerce.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,json,md}

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

Use Biome for linting and formatting instead of ESLint and Prettier

Files:

  • openspec/changes/coercion-public-api/design.md
  • openspec/changes/coercion-public-api/proposal.md
  • packages/arkenv/src/utils/coerce.ts
openspec/changes/*/proposal.md

📄 CodeRabbit inference engine (openspec/AGENTS.md)

Create proposal.md with sections: Why, What Changes (with BREAKING markers), and Impact

Files:

  • openspec/changes/coercion-public-api/proposal.md
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.ts
**/*.{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) as the main function for validated environment objects, available as the default export
Use built-in validators (host, port, url, email) from src/types.ts when available instead of custom validation
Use ArkEnvError for environment variable errors, not generic errors
Environment schema definitions should use built-in validators, ArkType string literals, and support default values in the schema pattern
Use logical grouping for related environment variables in schemas
Use descriptive env...

Files:

  • packages/arkenv/src/utils/coerce.ts
packages/arkenv/src/**/*.ts

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

Main library implementation should be in src/create-env.ts, built-in validators in src/types.ts, error handling in src/errors.ts, and utilities in src/utils.ts

Files:

  • packages/arkenv/src/utils/coerce.ts
🧠 Learnings (10)
📚 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:

  • openspec/changes/coercion-public-api/design.md
  • openspec/changes/coercion-public-api/proposal.md
  • 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 : Keep environment variable schemas readable and TypeScript-like using ArkType syntax

Applied to files:

  • openspec/changes/coercion-public-api/design.md
  • openspec/changes/coercion-public-api/proposal.md
  • packages/arkenv/src/utils/coerce.ts
📚 Learning: 2025-11-29T22:11:39.931Z
Learnt from: CR
Repo: yamcodes/arkenv PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-29T22:11:39.931Z
Learning: Applies to **/*.{ts,tsx} : Environment schema definitions should use built-in validators, ArkType string literals, and support default values in the schema pattern

Applied to files:

  • openspec/changes/coercion-public-api/proposal.md
  • 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:

  • openspec/changes/coercion-public-api/proposal.md
  • 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:

  • openspec/changes/coercion-public-api/proposal.md
  • 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 type inference for TypeScript types instead of manual type definitions

Applied to files:

  • openspec/changes/coercion-public-api/proposal.md
  • 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:

  • openspec/changes/coercion-public-api/proposal.md
📚 Learning: 2025-12-05T20:33:10.676Z
Learnt from: yamcodes
Repo: yamcodes/arkenv PR: 498
File: apps/playgrounds/node/index.ts:2-2
Timestamp: 2025-12-05T20:33:10.676Z
Learning: ArkType 2.1.28+ supports Standard Schema specification, allowing interoperability with other Standard Schema-compliant validation libraries (e.g., Zod, Valibot) within the same schema definition

Applied to files:

  • openspec/changes/coercion-public-api/proposal.md
📚 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:

  • openspec/changes/coercion-public-api/proposal.md
📚 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:

  • openspec/changes/coercion-public-api/proposal.md
  • packages/arkenv/src/utils/coerce.ts
🪛 LanguageTool
openspec/changes/coercion-public-api/design.md

[grammar] ~9-~9: Ensure spelling is correct
Context: ...hs*. To ensure 100% stability even when encountereing types that are not representable in JSO...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (3)
openspec/changes/coercion-public-api/proposal.md (1)

12-12: LGTM! Clear technical description.

The explanation accurately describes the fallback mechanism and its role in handling unjsonifiable types while preserving base schema information.

openspec/changes/coercion-public-api/design.md (1)

11-26: Excellent technical explanation.

The code snippet and rationale clearly demonstrate how the fallback mechanism provides resilience while maintaining standards compliance. The identification rules effectively map to JSON Schema concepts.

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

216-222: The implementation correctly uses ArkType's documented fallback API.

ArkType's toJsonSchema() method supports the fallback option where ctx.base represents the schema being generated and can be used to effectively ignore incompatible constraints. The code properly handles schema parts that cannot be fully represented in JSON Schema by preserving the base schema through the fallback mechanism.

### 1. Introspection via `schema.in.toJsonSchema()` with Fallback

We use `schema.in` to get a representation of the schema's input *without morphs* and then call `.toJsonSchema()` to get a standard JSON Schema representation for traversal. This ensures compatibility with schemas that use `.pipe()` or other morphs, which would otherwise cause `toJsonSchema()` to throw.
We use `schema.in` to get a representation of the schema's input *without morphs*. To ensure 100% stability even when encountereing types that are not representable in JSON Schema (like those with customized predicates or narrows), we call `.toJsonSchema()` with a base-preserving fallback:
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix typo in documentation.

The word "encountereing" should be "encountering".

🔎 Proposed fix
-We use `schema.in` to get a representation of the schema's input *without morphs*. To ensure 100% stability even when encountereing types that are not representable in JSON Schema (like those with customized predicates or narrows), we call `.toJsonSchema()` with a base-preserving fallback:
+We use `schema.in` to get a representation of the schema's input *without morphs*. To ensure 100% stability even when encountering types that are not representable in JSON Schema (like those with customized predicates or narrows), we call `.toJsonSchema()` with a base-preserving fallback:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
We use `schema.in` to get a representation of the schema's input *without morphs*. To ensure 100% stability even when encountereing types that are not representable in JSON Schema (like those with customized predicates or narrows), we call `.toJsonSchema()` with a base-preserving fallback:
We use `schema.in` to get a representation of the schema's input *without morphs*. To ensure 100% stability even when encountering types that are not representable in JSON Schema (like those with customized predicates or narrows), we call `.toJsonSchema()` with a base-preserving fallback:
🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Ensure spelling is correct
Context: ...hs*. To ensure 100% stability even when encountereing types that are not representable in JSO...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
In openspec/changes/coercion-public-api/design.md around line 9, fix the typo
"encountereing" to "encountering" in the sentence describing calling
`.toJsonSchema()` with a base-preserving fallback so the documentation reads
correctly.

- Corrected "encountereing" to "encountering"
@yamcodes yamcodes merged commit 78936c3 into coercion Dec 22, 2025
3 checks passed
@yamcodes yamcodes deleted the 594-coercion-fix-stringurl branch December 22, 2025 19:25
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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant