-
-
Notifications
You must be signed in to change notification settings - Fork 187
feat(create): add ArkEnv add-on for environment variables #516
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
yamcodes
wants to merge
7
commits into
TanStack:main
Choose a base branch
from
yamcodes:feat/arkenv-addon
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
3d52566
feat(arkenv): introduce ArkEnv add-on for typesafe environment variab…
yamcodes cb1cf69
fix(types): add 'env' option to exclusive add-ons array
yamcodes 7fe8940
Add ArkEnv add-on for environment variable validation in React Start …
yamcodes 7fec7fa
Refactor ArkEnv add-on documentation for clarity
yamcodes 6e489c7
Refactor ArkEnv add-on configuration and demo route
yamcodes ed2ae67
Fix ArkEnv plugin entry, port bounds, and demo disclosure.
yamcodes c451a80
Precompile inline integration imports for edge rendering.
yamcodes File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| '@tanstack/create': minor | ||
| --- | ||
|
|
||
| Add an ArkEnv add-on for environment variable validation in React Start apps. | ||
|
|
||
| `tanstack add arkenv` (or `--add-ons arkenv` on `tanstack create`) | ||
| writes `src/env.ts`, registers `@arkenv/vite-plugin`, and can add a | ||
| `/demo/arkenv` route. ArkType, Zod, and Valibot are selectable. ArkEnv | ||
| and T3Env are mutually exclusive because both write `src/env.ts`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
packages/create/src/frameworks/react/add-ons/arkenv/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| ## ArkEnv | ||
|
|
||
| Typesafe environment variables for TanStack Start. The add-on installs | ||
| `@arkenv/vite-plugin`, writes `src/env.ts`, and can add a `/demo/arkenv` | ||
| route that shows server-only keys staying on the server. | ||
|
|
||
| Pick a validator when you scaffold: ArkType (`@arkenv/core`), Zod, or | ||
| Valibot (`@arkenv/standard`). | ||
|
|
||
| ### Usage | ||
|
|
||
| ```ts | ||
| import { env } from "#/env"; | ||
|
|
||
| console.log(env.VITE_API_URL); | ||
| ``` | ||
|
|
||
| Docs: [https://arkenv.js.org/docs/frameworks/tanstack-start](https://arkenv.js.org/docs/frameworks/tanstack-start) |
11 changes: 11 additions & 0 deletions
11
packages/create/src/frameworks/react/add-ons/arkenv/assets/_dot_env.example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| # Port for the dev/preview server | ||
| PORT=3000 | ||
|
|
||
| # Public API URL (inlined into client bundle) | ||
| VITE_API_URL=https://api.example.com | ||
|
|
||
| # Server-only database connection URL (protected from client access) | ||
| DATABASE_URL=postgresql://postgres:postgres@localhost:5432/db | ||
|
|
||
| # Environment mode | ||
| NODE_ENV=development |
55 changes: 55 additions & 0 deletions
55
packages/create/src/frameworks/react/add-ons/arkenv/assets/src/env.ts.ejs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| <% const validator = (addOnOption.arkenv && addOnOption.arkenv.validator) || 'arktype' -%> | ||
| <% if (validator === 'zod') { -%> | ||
| import arkenv from '@arkenv/standard' | ||
| import { z } from 'zod' | ||
|
|
||
| export const env = arkenv({ | ||
| PORT: z.coerce.number().int().min(1).max(65535).default(3000), | ||
| VITE_API_URL: z.string().url().default('https://api.example.com'), | ||
| DATABASE_URL: z | ||
| .string() | ||
| .url() | ||
| .default('postgresql://postgres:postgres@localhost:5432/db'), | ||
| NODE_ENV: z | ||
| .enum(['development', 'production', 'test']) | ||
| .default('development'), | ||
| }) | ||
| <% } else if (validator === 'valibot') { -%> | ||
| import arkenv from '@arkenv/standard' | ||
| import * as v from 'valibot' | ||
|
|
||
| export const env = arkenv({ | ||
| PORT: v.optional( | ||
| v.pipe( | ||
| v.unknown(), | ||
| v.transform(Number), | ||
| v.integer(), | ||
| v.minValue(1), | ||
| v.maxValue(65535), | ||
| ), | ||
| 3000, | ||
| ), | ||
| VITE_API_URL: v.optional( | ||
| v.pipe(v.string(), v.url()), | ||
| 'https://api.example.com', | ||
| ), | ||
| DATABASE_URL: v.optional( | ||
| v.pipe(v.string(), v.url()), | ||
| 'postgresql://postgres:postgres@localhost:5432/db', | ||
| ), | ||
| NODE_ENV: v.optional( | ||
| v.picklist(['development', 'production', 'test']), | ||
| 'development', | ||
| ), | ||
| }) | ||
| <% } else { -%> | ||
| import arkenv from '@arkenv/core' | ||
|
|
||
| export const env = arkenv({ | ||
| PORT: 'number.port = 3000', | ||
| VITE_API_URL: "string = 'https://api.example.com'", | ||
| DATABASE_URL: | ||
| "string = 'postgresql://postgres:postgres@localhost:5432/db'", | ||
| NODE_ENV: "'development' | 'production' | 'test' = 'development'", | ||
| }) | ||
| <% } -%> |
82 changes: 82 additions & 0 deletions
82
packages/create/src/frameworks/react/add-ons/arkenv/assets/src/routes/demo/arkenv.tsx.ejs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| <% if (!includeExamples) { ignoreFile(); return; } %> | ||
| import { useState } from 'react' | ||
| import { createFileRoute } from '@tanstack/react-router' | ||
| import { createServerFn } from '@tanstack/react-start' | ||
| import { env } from '../../env' | ||
|
|
||
| const getDatabaseConfig = createServerFn({ method: 'GET' }).handler(() => { | ||
| // Read the server-only key here. Return a fixed example so the deployed | ||
| // database endpoint is not sent to the browser. | ||
| if (!env.DATABASE_URL) { | ||
| throw new Error('DATABASE_URL is not set') | ||
| } | ||
|
|
||
| return { host: 'localhost:5432', protocol: 'postgresql:' } | ||
| }) | ||
|
|
||
| export const Route = createFileRoute('/demo/arkenv')({ | ||
| component: ArkEnvDemo, | ||
| loader: () => getDatabaseConfig(), | ||
| }) | ||
|
|
||
| function LeakedSecret() { | ||
| // Accessing server-only DATABASE_URL directly on the client throws at runtime | ||
| return <p>Server key leaked: {env.DATABASE_URL}</p> | ||
| } | ||
|
|
||
| function ArkEnvDemo() { | ||
| const dbConfig = Route.useLoaderData() | ||
| const [attemptLeak, setAttemptLeak] = useState(false) | ||
|
|
||
| return ( | ||
| <div className="p-6 max-w-xl mx-auto space-y-4 font-sans"> | ||
| <h1 className="text-2xl font-bold">ArkEnv Demo</h1> | ||
| <p className="text-sm text-gray-600"> | ||
| Typesafe environment variables with build-time validation and runtime | ||
| leak protection. | ||
| </p> | ||
|
|
||
| <div className="p-4 bg-gray-100 dark:bg-gray-800 rounded-md space-y-2"> | ||
| <h2 className="font-semibold text-lg">Public Client Variables</h2> | ||
| <p className="text-sm text-gray-600 dark:text-gray-300"> | ||
| Inlined safely into client bundles: | ||
| </p> | ||
| <code className="block p-2 bg-white dark:bg-black rounded border text-xs font-mono"> | ||
| env.VITE_API_URL: {env.VITE_API_URL} | ||
| </code> | ||
| </div> | ||
|
|
||
| <div className="p-4 bg-gray-100 dark:bg-gray-800 rounded-md space-y-2"> | ||
| <h2 className="font-semibold text-lg">Server-Only Variables</h2> | ||
| <p className="text-sm text-gray-600 dark:text-gray-300"> | ||
| Accessible inside createServerFn handlers. This page shows an example | ||
| endpoint, not the deployed database host: | ||
| </p> | ||
| <code className="block p-2 bg-white dark:bg-black rounded border text-xs font-mono"> | ||
| Example endpoint: {dbConfig.host} ({dbConfig.protocol}) | ||
| </code> | ||
| </div> | ||
|
|
||
| <div className="p-4 bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-900 rounded-md space-y-2"> | ||
| <h2 className="font-semibold text-red-800 dark:text-red-300 text-lg"> | ||
| Secret Leak Protection | ||
| </h2> | ||
| <p className="text-sm text-red-700 dark:text-red-400"> | ||
| Clicking the button below attempts to access the server secret{' '} | ||
| <code>env.DATABASE_URL</code> on the client, which ArkEnv blocks: | ||
| </p> | ||
| {attemptLeak ? ( | ||
| <LeakedSecret /> | ||
| ) : ( | ||
| <button | ||
| type="button" | ||
| className="px-3 py-1.5 bg-red-600 text-white rounded text-sm hover:bg-red-700 cursor-pointer" | ||
| onClick={() => setAttemptLeak(true)} | ||
| > | ||
| Attempt client access to DATABASE_URL | ||
| </button> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ) | ||
| } |
49 changes: 49 additions & 0 deletions
49
packages/create/src/frameworks/react/add-ons/arkenv/info.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| { | ||
| "id": "arkenv", | ||
| "name": "ArkEnv", | ||
| "description": "Typesafe environment variable validation with build-time validation and runtime leak protection.", | ||
| "type": "add-on", | ||
| "phase": "add-on", | ||
| "category": "tooling", | ||
| "exclusive": ["env"], | ||
| "color": "#06B6D4", | ||
| "priority": 28, | ||
| "link": "https://arkenv.js.org", | ||
| "modes": ["file-router", "code-router"], | ||
| "options": { | ||
| "validator": { | ||
| "type": "select", | ||
| "label": "Validator Engine", | ||
| "default": "arktype", | ||
| "options": [ | ||
| { | ||
| "value": "arktype", | ||
| "label": "ArkType (@arkenv/core) - Recommended" | ||
| }, | ||
| { | ||
| "value": "zod", | ||
| "label": "Zod (@arkenv/standard)" | ||
| }, | ||
| { | ||
| "value": "valibot", | ||
| "label": "Valibot (@arkenv/standard)" | ||
| } | ||
| ] | ||
| } | ||
| }, | ||
| "routes": [ | ||
| { | ||
| "url": "/demo/arkenv", | ||
| "name": "ArkEnv Demo", | ||
| "path": "src/routes/demo/arkenv.tsx", | ||
| "jsName": "ArkEnvDemo" | ||
| } | ||
| ], | ||
| "integrations": [ | ||
| { | ||
| "type": "vite-plugin", | ||
| "import": "import arkenv from '<%= (addOnOption.arkenv && (addOnOption.arkenv.validator === 'zod' || addOnOption.arkenv.validator === 'valibot')) ? '@arkenv/vite-plugin/standard' : '@arkenv/vite-plugin' %>'", | ||
| "code": "arkenv()" | ||
| } | ||
| ] | ||
| } |
18 changes: 18 additions & 0 deletions
18
packages/create/src/frameworks/react/add-ons/arkenv/package.json.ejs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <% const validator = (addOnOption.arkenv && addOnOption.arkenv.validator) || 'arktype' -%> | ||
| { | ||
| "dependencies": { | ||
| <% if (validator === 'zod') { -%> | ||
| "@arkenv/standard": "^1.0.0-rc.2", | ||
| "zod": "^4.4.1" | ||
| <% } else if (validator === 'valibot') { -%> | ||
| "@arkenv/standard": "^1.0.0-rc.2", | ||
| "valibot": "^1.0.0" | ||
| <% } else { -%> | ||
| "@arkenv/core": "^1.0.0-rc.2", | ||
| "arktype": "^2.2.0" | ||
| <% } -%> | ||
| }, | ||
| "devDependencies": { | ||
| "@arkenv/vite-plugin": "^1.0.0-rc.2" | ||
| } | ||
| } |
14 changes: 14 additions & 0 deletions
14
packages/create/src/frameworks/react/add-ons/arkenv/small-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.