Skip to content

Commit 1ac6588

Browse files
authored
Merge pull request #1169 from yamcodes/dev
2 parents 8a4e77b + cb80ae8 commit 1ac6588

40 files changed

Lines changed: 735 additions & 406 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
"arkenv": minor
3+
---
4+
5+
#### Add Standard JSON Schema coercion to `arkenv/standard`
6+
7+
Introduce opt-out type coercion for standard mode (`arkenv/standard`). This coercion only works if the validator is a standard JSON Schema compliant validator (e.g., Zod, Valibot, or custom schemas that implement the `StandardJSONSchemaV1` interface). This automatically enables coercion for environment variables without relying on ArkType's runtime footprint.
8+
9+
If you need to disable coercion, explicitly pass `{ coerce: false }` in your configuration:
10+
11+
```ts
12+
import arkenv from "arkenv/standard";
13+
import { z } from "zod";
14+
15+
const env = arkenv(
16+
{ PORT: z.number() },
17+
{ coerce: false }
18+
);
19+
```
20+
21+
BREAKING CHANGE: Coercion is now enabled by default in `arkenv/standard`. This will automatically coerce environment variables to their expected types (e.g., strings containing numbers, booleans, or dates will be converted to their respective types) based on the JSON Schema of your validators. It's unlikely to affect you unless you were relying on validation failing for uncoerced string inputs, or have custom schemas that expect raw strings instead of coerced values.

.github/workflows/sync-pr.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ jobs:
3939
4040
It tracks all commits on `dev` that have not yet been merged into `main`, keeping the two branches in sync after every push to `dev`.
4141
42+
🌍 **Preview the latest dev documentation**: https://arkenv-dev.vercel.app
43+
4244
> [!NOTE]
4345
> This PR is opened and managed by **arkenv-bot**. It is skipped by Pullfrog and other bot-aware workflows.
4446
run: |
@@ -67,7 +69,7 @@ jobs:
6769
gh pr create \
6870
--base main \
6971
--head dev \
70-
--title "chore: sync dev → main" \
72+
--title "Sync `dev``main`" \
7173
--body "$PR_BODY" \
7274
--label "maintenance"
7375
echo "✅ Sync PR created."

apps/www/components/page/cli-command.test.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { render, screen, waitFor } from "@testing-library/react";
22
import userEvent from "@testing-library/user-event";
3-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3+
import { beforeEach, describe, expect, it, vi } from "vitest";
44
import { Toaster } from "~/components/ui/toaster";
55
import { CLICommand } from "./cli-command";
66

@@ -19,10 +19,6 @@ describe("CLICommand", () => {
1919
});
2020
});
2121

22-
afterEach(() => {
23-
vi.unstubAllGlobals();
24-
});
25-
2622
it("should render with syntax highlighting spans", () => {
2723
render(<CLICommand />);
2824
expect(screen.getByText("npx")).toBeInTheDocument();

apps/www/components/page/copy-button.integration.test.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { render, screen, waitFor } from "@testing-library/react";
22
import userEvent from "@testing-library/user-event";
3-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3+
import { beforeEach, describe, expect, it, vi } from "vitest";
44
import { Toaster } from "~/components/ui/toaster";
55
import { CopyButton } from "./copy-button";
66

@@ -22,10 +22,6 @@ describe("CopyButton + useToast + Toaster integration", () => {
2222
});
2323
});
2424

25-
afterEach(() => {
26-
vi.unstubAllGlobals();
27-
});
28-
2925
it("should show toast when copy succeeds", async () => {
3026
const user = userEvent.setup();
3127
mockWriteText.mockResolvedValue(undefined);

apps/www/components/ui/search-toggle.test.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ vi.mock("fumadocs-ui/contexts/search", () => ({
1111
describe("SearchToggle", () => {
1212
afterEach(() => {
1313
cleanup();
14-
vi.restoreAllMocks();
1514
vi.unstubAllGlobals();
1615
});
1716

apps/www/content/docs/arkenv/coercion.mdx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@ description: Environment variables are auto-morphed into typesafe numbers, boole
55

66
Traditionally, environment variables are always strings by default. ArkEnv **coerces** these strings into their target types based on your schema, so you only have to think about the types you want.
77

8-
:::important
9-
Coercion is not available in `arkenv/standard` - use your validator's native coercion instead.
10-
:::
8+
:::note
9+
Coercion in [arkenv/standard](/docs/arkenv/standard) only works with:
10+
11+
- [Zod](https://zod.dev) (v4.2+)
12+
- [Valibot](https://valibot.dev) (v1.2+ via `@valibot/to-json-schema`)
13+
- [Zod Mini](https://zod.dev)
14+
- [stnl](https://github.com/re-utils/stnl)
15+
- Any validator compliant with [Standard JSON Schema v1](https://standardschema.dev/json-schema#what-schema-libraries-support-this-spec)
16+
:::
1117

1218
## Numbers
1319

apps/www/content/docs/arkenv/standard.mdx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ Already using ArkType? You can still use Zod, Valibot, and other Standard Schema
1414
| Feature | `arkenv` | `arkenv/standard` |
1515
| :------------------------ | :------: | :---------------: |
1616
| **ArkType DSL** |||
17-
| **ArkEnv keywords**\* |||
17+
| **ArkEnv keywords**¹ |||
1818
| **ArkEnv plugins** |||
19-
| **Automatic coercion** || |
19+
| **Automatic coercion** || ✅² |
2020
| **Standard Schema** |||
2121
| **Works without ArkType** |||
2222

23-
\* ArkEnv extends ArkType with custom keywords like `string.host` and `number.port`
23+
¹ ArkEnv extends ArkType with custom keywords like `string.host` and `number.port`
24+
25+
² Only works with Standard JSON Schema validators like Zod 4, Zod Mini and Valibot - see [Coercion](/docs/arkenv/coercion)
2426

2527
✅ Built-in / first-class support
2628

apps/www/hooks/use-copy-command.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ describe("useCopyCommand", () => {
2626
});
2727

2828
afterEach(() => {
29-
vi.unstubAllGlobals();
3029
vi.clearAllMocks();
3130
vi.useRealTimers();
3231
});

apps/www/lib/twoslash-options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const arktypeTwoslashOptions: ArkTypeTwoslashOptions = {
3434
arkenv: [path.join(root, "packages/arkenv/src/index.ts")],
3535
"arkenv/standard": [path.join(root, "packages/arkenv/src/standard.ts")],
3636
"arkenv/core": [path.join(root, "packages/arkenv/src/core.ts")],
37+
"@/*": [path.join(root, "packages/arkenv/src/*")],
3738
"@arkenv/nextjs": [path.join(root, "packages/nextjs/src/index.ts")],
3839
"@arkenv/nextjs/server": [
3940
path.join(root, "packages/nextjs/src/server.ts"),

apps/www/lib/utils/github.test.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ describe("github utilities", () => {
5656
});
5757

5858
it("should use fallback URL when no URL is configured", () => {
59-
vi.unstubAllEnvs();
60-
6159
const result = breakDownGithubUrl();
6260

6361
expect(result).toEqual({
@@ -147,8 +145,6 @@ describe("github utilities", () => {
147145
});
148146

149147
it("should use fallback URL when no URL is configured", () => {
150-
vi.unstubAllEnvs();
151-
152148
const result = getLinkTitleAndHref("test.md");
153149

154150
expect(result).toEqual({

0 commit comments

Comments
 (0)