11{
2- "id" : " arkenv" ,
3- "name" : " ArkEnv" ,
4- "version" : " 1.0.0-alpha.1" ,
5- "description" : " Typesafe environment variable validation with build-time validation and runtime leak protection." ,
6- "type" : " add-on" ,
7- "phase" : " add-on" ,
8- "category" : " tooling" ,
9- "color" : " #06B6D4" ,
10- "priority" : 28 ,
11- "link" : " https://arkenv.js.org" ,
12- "modes" : [
13- " file-router" ,
14- " code-router"
15- ],
16- "options" : {
17- "validator" : {
18- "type" : " select" ,
19- "label" : " Validator Engine" ,
20- "default" : " arktype" ,
21- "options" : [
22- {
23- "value" : " arktype" ,
24- "label" : " ArkType (@arkenv/core) - Recommended"
25- },
26- {
27- "value" : " zod" ,
28- "label" : " Zod (@arkenv/standard)"
29- },
30- {
31- "value" : " valibot" ,
32- "label" : " Valibot (@arkenv/standard)"
33- }
34- ]
35- }
36- },
37- "routes" : [
38- {
39- "url" : " /demo/arkenv" ,
40- "name" : " ArkEnv Demo" ,
41- "path" : " src/routes/demo/arkenv.tsx" ,
42- "jsName" : " ArkEnvDemo"
43- }
44- ],
45- "integrations" : [
46- {
47- "type" : " vite-plugin" ,
48- "import" : " import arkenv from '@arkenv/vite-plugin'" ,
49- "code" : " arkenv()"
50- }
51- ],
52- "packageTemplate" : " <%\n const arkenvOption = (typeof addOnOption !== 'undefined' && (\n addOnOption['arkenv'] ||\n Object.entries(addOnOption).find(([k]) => k.includes('arkenv') || k.includes('info.json'))?.[1]\n )) || {};\n const validator = arkenvOption.validator || 'arktype';\n -%>\n {\n \" dependencies\" : {\n <% if (validator === 'zod') { -%>\n \" @arkenv/standard\" : \" ^1.0.0-alpha.1\" ,\n \" zod\" : \" ^3.24.2\"\n <% } else if (validator === 'valibot') { -%>\n \" @arkenv/standard\" : \" ^1.0.0-alpha.1\" ,\n \" valibot\" : \" ^1.0.0\"\n <% } else { -%>\n \" @arkenv/core\" : \" ^1.0.0-alpha.1\" ,\n \" arktype\" : \" ^2.2.0\"\n <% } -%>\n },\n \" devDependencies\" : {\n \" @arkenv/vite-plugin\" : \" ^1.0.0-alpha.1\"\n }\n }\n " ,
53- "files" : {
54- "_dot_env.example" : " # Port for the dev/preview server\n PORT=3000\n\n # Public API URL (inlined into client bundle)\n VITE_API_URL=https://api.example.com\n\n # Server-only database connection URL (protected from client access)\n DATABASE_URL=postgresql://postgres:postgres@localhost:5432/db\n\n # Environment mode\n NODE_ENV=development\n " ,
55- "src/env.ts.ejs": "<%\n const arkenvOption = (typeof addOnOption !== 'undefined' && (\n addOnOption['arkenv'] ||\n Object.entries(addOnOption).find(([k]) => k.includes('arkenv') || k.includes('info.json'))?.[1]\n )) || {};\n const validator = arkenvOption.validator || 'arktype';\n-%>\n<% if (validator === 'zod') { -%>\nimport arkenv from \"@arkenv/standard\";\nimport { z } from \"zod\";\n\nexport const env = arkenv({\n PORT: z.coerce.number().int().min(1).max(65535).default(3000),\n VITE_API_URL: z.string().url().default(\"https://api.example.com\"),\n DATABASE_URL: z.string().url().default(\"postgresql://postgres:postgres@localhost:5432/db\"),\n NODE_ENV: z.enum([\"development\", \"production\", \"test\"]).default(\"development\"),\n});\n<% } else if (validator === 'valibot') { -%>\nimport arkenv from \"@arkenv/standard\";\nimport * as v from \"valibot\";\n\nexport const env = arkenv({\n PORT: v.optional(v.pipe(v.unknown(), v.transform(Number), v.integer()), 3000),\n VITE_API_URL: v.optional(v.pipe(v.string(), v.url()), \"https://api.example.com\"),\n DATABASE_URL: v.optional(v.pipe(v.string(), v.url()), \"postgresql://postgres:postgres@localhost:5432/db\"),\n NODE_ENV: v.optional(v.picklist([\"development\", \"production\", \"test\"]), \"development\"),\n});\n<% } else { -%>\nimport arkenv from \"@arkenv/core\";\n\nexport const env = arkenv({\n PORT: \"number.port = 3000\",\n VITE_API_URL: \"string = 'https://api.example.com'\",\n DATABASE_URL: \"string = 'postgresql://postgres:postgres@localhost:5432/db'\",\n NODE_ENV: \"'development' | 'production' | 'test' = 'development'\",\n});\n<% } -%>\n",
56- "src/routes/demo/arkenv.tsx": "import { createFileRoute } from \"@tanstack/react-router\";\nimport { createServerFn } from \"@tanstack/react-start\";\nimport { useState } from \"react\";\nimport { env } from \"../../env\";\n\nconst getDatabaseConfig = createServerFn({ method: \"GET\" }).handler(() => {\n\t// Server-only key: safely read on the server during SSR / RPC\n\ttry {\n\t\tconst url = new URL(env.DATABASE_URL);\n\t\treturn { host: url.host, protocol: url.protocol };\n\t} catch {\n\t\treturn { host: \"localhost:5432\", protocol: \"postgresql:\" };\n\t}\n});\n\nexport const Route = createFileRoute(\"/demo/arkenv\")({\n\tcomponent: ArkEnvDemo,\n\tloader: () => getDatabaseConfig(),\n});\n\nfunction LeakedSecret() {\n\t// Accessing server-only DATABASE_URL directly on the client throws at runtime\n\treturn <p>Server key leaked: {env.DATABASE_URL}</p>;\n}\n\nfunction ArkEnvDemo() {\n\tconst dbConfig = Route.useLoaderData();\n\tconst [attemptLeak, setAttemptLeak] = useState(false);\n\n\treturn (\n\t\t<div className=\"p-6 max-w-xl mx-auto space-y-4 font-sans\">\n\t\t\t<h1 className=\"text-2xl font-bold\">ArkEnv Demo</h1>\n\t\t\t<p className=\"text-sm text-gray-600\">\n\t\t\t\tTypesafe environment variables with build-time validation and runtime\n\t\t\t\tleak protection.\n\t\t\t</p>\n\n\t\t\t<div className=\"p-4 bg-gray-100 dark:bg-gray-800 rounded-md space-y-2\">\n\t\t\t\t<h2 className=\"font-semibold text-lg\">Public Client Variables</h2>\n\t\t\t\t<p className=\"text-sm text-gray-600 dark:text-gray-300\">\n\t\t\t\t\tInlined safely into client bundles:\n\t\t\t\t</p>\n\t\t\t\t<code className=\"block p-2 bg-white dark:bg-black rounded border text-xs font-mono\">\n\t\t\t\t\tenv.VITE_API_URL: {env.VITE_API_URL}\n\t\t\t\t</code>\n\t\t\t</div>\n\n\t\t\t<div className=\"p-4 bg-gray-100 dark:bg-gray-800 rounded-md space-y-2\">\n\t\t\t\t<h2 className=\"font-semibold text-lg\">Server-Only Variables</h2>\n\t\t\t\t<p className=\"text-sm text-gray-600 dark:text-gray-300\">\n\t\t\t\t\tAccessible inside createServerFn handlers:\n\t\t\t\t</p>\n\t\t\t\t<code className=\"block p-2 bg-white dark:bg-black rounded border text-xs font-mono\">\n\t\t\t\t\tDatabase Host: {dbConfig.host} ({dbConfig.protocol})\n\t\t\t\t</code>\n\t\t\t</div>\n\n\t\t\t<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\">\n\t\t\t\t<h2 className=\"font-semibold text-red-800 dark:text-red-300 text-lg\">\n\t\t\t\t\tSecret Leak Protection\n\t\t\t\t</h2>\n\t\t\t\t<p className=\"text-sm text-red-700 dark:text-red-400\">\n\t\t\t\t\tClicking the button below attempts to access the server secret{\" \"}\n\t\t\t\t\t<code>env.DATABASE_URL</code> on the client, which ArkEnv blocks:\n\t\t\t\t</p>\n\t\t\t\t{attemptLeak ? (\n\t\t\t\t\t<LeakedSecret />\n\t\t\t\t) : (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"px-3 py-1.5 bg-red-600 text-white rounded text-sm hover:bg-red-700 cursor-pointer\"\n\t\t\t\t\t\tonClick={() => setAttemptLeak(true)}\n\t\t\t\t\t>\n\t\t\t\t\t\tAttempt client access to DATABASE_URL\n\t\t\t\t\t</button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"
57- },
58- "deletedFiles" : []
59- }
2+ "id" : " arkenv" ,
3+ "name" : " ArkEnv" ,
4+ "version" : " 1.0.0-alpha.1" ,
5+ "description" : " Typesafe environment variable validation with build-time validation and runtime leak protection." ,
6+ "type" : " add-on" ,
7+ "phase" : " add-on" ,
8+ "category" : " tooling" ,
9+ "color" : " #06B6D4" ,
10+ "priority" : 28 ,
11+ "link" : " https://arkenv.js.org" ,
12+ "modes" : [" file-router" , " code-router" ],
13+ "options" : {
14+ "validator" : {
15+ "type" : " select" ,
16+ "label" : " Validator Engine" ,
17+ "default" : " arktype" ,
18+ "options" : [
19+ {
20+ "value" : " arktype" ,
21+ "label" : " ArkType (@arkenv/core) - Recommended"
22+ },
23+ {
24+ "value" : " zod" ,
25+ "label" : " Zod (@arkenv/standard)"
26+ },
27+ {
28+ "value" : " valibot" ,
29+ "label" : " Valibot (@arkenv/standard)"
30+ }
31+ ]
32+ }
33+ },
34+ "routes" : [
35+ {
36+ "url" : " /demo/arkenv" ,
37+ "name" : " ArkEnv Demo" ,
38+ "path" : " src/routes/demo/arkenv.tsx" ,
39+ "jsName" : " ArkEnvDemo"
40+ }
41+ ],
42+ "integrations" : [
43+ {
44+ "type" : " vite-plugin" ,
45+ "import" : " import arkenv from '@arkenv/vite-plugin'" ,
46+ "code" : " arkenv()"
47+ }
48+ ],
49+ "packageTemplate" : " <%\n const arkenvOption = (typeof addOnOption !== 'undefined' && (\n addOnOption['arkenv'] ||\n Object.entries(addOnOption).find(([k]) => k.includes('arkenv') || k.includes('info.json'))?.[1]\n )) || {};\n const validator = arkenvOption.validator || 'arktype';\n -%>\n {\n \" dependencies\" : {\n <% if (validator === 'zod') { -%>\n \" @arkenv/standard\" : \" ^1.0.0-alpha.1\" ,\n \" zod\" : \" ^3.24.2\"\n <% } else if (validator === 'valibot') { -%>\n \" @arkenv/standard\" : \" ^1.0.0-alpha.1\" ,\n \" valibot\" : \" ^1.0.0\"\n <% } else { -%>\n \" @arkenv/core\" : \" ^1.0.0-alpha.1\" ,\n \" arktype\" : \" ^2.2.0\"\n <% } -%>\n },\n \" devDependencies\" : {\n \" @arkenv/vite-plugin\" : \" ^1.0.0-alpha.1\"\n }\n }\n " ,
50+ "files" : {
51+ "_dot_env.example" : " # Port for the dev/preview server\n PORT=3000\n\n # Public API URL (inlined into client bundle)\n VITE_API_URL=https://api.example.com\n\n # Server-only database connection URL (protected from client access)\n DATABASE_URL=postgresql://postgres:postgres@localhost:5432/db\n\n # Environment mode\n NODE_ENV=development\n " ,
52+ "src/env.ts.ejs": "<%\n const arkenvOption = (typeof addOnOption !== 'undefined' && (\n addOnOption['arkenv'] ||\n Object.entries(addOnOption).find(([k]) => k.includes('arkenv') || k.includes('info.json'))?.[1]\n )) || {};\n const validator = arkenvOption.validator || 'arktype';\n-%>\n<% if (validator === 'zod') { -%>\nimport arkenv from \"@arkenv/standard\";\nimport { z } from \"zod\";\n\nexport const env = arkenv({\n PORT: z.coerce.number().int().min(1).max(65535).default(3000),\n VITE_API_URL: z.string().url().default(\"https://api.example.com\"),\n DATABASE_URL: z.string().url().default(\"postgresql://postgres:postgres@localhost:5432/db\"),\n NODE_ENV: z.enum([\"development\", \"production\", \"test\"]).default(\"development\"),\n});\n<% } else if (validator === 'valibot') { -%>\nimport arkenv from \"@arkenv/standard\";\nimport * as v from \"valibot\";\n\nexport const env = arkenv({\n PORT: v.optional(v.pipe(v.unknown(), v.transform(Number), v.integer()), 3000),\n VITE_API_URL: v.optional(v.pipe(v.string(), v.url()), \"https://api.example.com\"),\n DATABASE_URL: v.optional(v.pipe(v.string(), v.url()), \"postgresql://postgres:postgres@localhost:5432/db\"),\n NODE_ENV: v.optional(v.picklist([\"development\", \"production\", \"test\"]), \"development\"),\n});\n<% } else { -%>\nimport arkenv from \"@arkenv/core\";\n\nexport const env = arkenv({\n PORT: \"number.port = 3000\",\n VITE_API_URL: \"string = 'https://api.example.com'\",\n DATABASE_URL: \"string = 'postgresql://postgres:postgres@localhost:5432/db'\",\n NODE_ENV: \"'development' | 'production' | 'test' = 'development'\",\n});\n<% } -%>\n",
53+ "src/routes/demo/arkenv.tsx": "import { createFileRoute } from \"@tanstack/react-router\";\nimport { createServerFn } from \"@tanstack/react-start\";\nimport { useState } from \"react\";\nimport { env } from \"../../env\";\n\nconst getDatabaseConfig = createServerFn({ method: \"GET\" }).handler(() => {\n\t// Server-only key: safely read on the server during SSR / RPC\n\ttry {\n\t\tconst url = new URL(env.DATABASE_URL);\n\t\treturn { host: url.host, protocol: url.protocol };\n\t} catch {\n\t\treturn { host: \"localhost:5432\", protocol: \"postgresql:\" };\n\t}\n});\n\nexport const Route = createFileRoute(\"/demo/arkenv\")({\n\tcomponent: ArkEnvDemo,\n\tloader: () => getDatabaseConfig(),\n});\n\nfunction LeakedSecret() {\n\t// Accessing server-only DATABASE_URL directly on the client throws at runtime\n\treturn <p>Server key leaked: {env.DATABASE_URL}</p>;\n}\n\nfunction ArkEnvDemo() {\n\tconst dbConfig = Route.useLoaderData();\n\tconst [attemptLeak, setAttemptLeak] = useState(false);\n\n\treturn (\n\t\t<div className=\"p-6 max-w-xl mx-auto space-y-4 font-sans\">\n\t\t\t<h1 className=\"text-2xl font-bold\">ArkEnv Demo</h1>\n\t\t\t<p className=\"text-sm text-gray-600\">\n\t\t\t\tTypesafe environment variables with build-time validation and runtime\n\t\t\t\tleak protection.\n\t\t\t</p>\n\n\t\t\t<div className=\"p-4 bg-gray-100 dark:bg-gray-800 rounded-md space-y-2\">\n\t\t\t\t<h2 className=\"font-semibold text-lg\">Public Client Variables</h2>\n\t\t\t\t<p className=\"text-sm text-gray-600 dark:text-gray-300\">\n\t\t\t\t\tInlined safely into client bundles:\n\t\t\t\t</p>\n\t\t\t\t<code className=\"block p-2 bg-white dark:bg-black rounded border text-xs font-mono\">\n\t\t\t\t\tenv.VITE_API_URL: {env.VITE_API_URL}\n\t\t\t\t</code>\n\t\t\t</div>\n\n\t\t\t<div className=\"p-4 bg-gray-100 dark:bg-gray-800 rounded-md space-y-2\">\n\t\t\t\t<h2 className=\"font-semibold text-lg\">Server-Only Variables</h2>\n\t\t\t\t<p className=\"text-sm text-gray-600 dark:text-gray-300\">\n\t\t\t\t\tAccessible inside createServerFn handlers:\n\t\t\t\t</p>\n\t\t\t\t<code className=\"block p-2 bg-white dark:bg-black rounded border text-xs font-mono\">\n\t\t\t\t\tDatabase Host: {dbConfig.host} ({dbConfig.protocol})\n\t\t\t\t</code>\n\t\t\t</div>\n\n\t\t\t<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\">\n\t\t\t\t<h2 className=\"font-semibold text-red-800 dark:text-red-300 text-lg\">\n\t\t\t\t\tSecret Leak Protection\n\t\t\t\t</h2>\n\t\t\t\t<p className=\"text-sm text-red-700 dark:text-red-400\">\n\t\t\t\t\tClicking the button below attempts to access the server secret{\" \"}\n\t\t\t\t\t<code>env.DATABASE_URL</code> on the client, which ArkEnv blocks:\n\t\t\t\t</p>\n\t\t\t\t{attemptLeak ? (\n\t\t\t\t\t<LeakedSecret />\n\t\t\t\t) : (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"px-3 py-1.5 bg-red-600 text-white rounded text-sm hover:bg-red-700 cursor-pointer\"\n\t\t\t\t\t\tonClick={() => setAttemptLeak(true)}\n\t\t\t\t\t>\n\t\t\t\t\t\tAttempt client access to DATABASE_URL\n\t\t\t\t\t</button>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"
54+ },
55+ "deletedFiles" : []
56+ }
0 commit comments