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" : [" 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- }
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+ "demo" : {
37+ "type" : " select" ,
38+ "label" : " Interactive Demo Route" ,
39+ "default" : " true" ,
40+ "options" : [
41+ {
42+ "value" : " true" ,
43+ "label" : " Include /demo/arkenv (recommended)"
44+ },
45+ {
46+ "value" : " false" ,
47+ "label" : " Skip demo route"
48+ }
49+ ]
50+ }
51+ },
52+ "routes" : [
53+ {
54+ "url" : " /demo/arkenv" ,
55+ "name" : " ArkEnv Demo" ,
56+ "path" : " src/routes/demo/arkenv.tsx" ,
57+ "jsName" : " ArkEnvDemo"
58+ }
59+ ],
60+ "integrations" : [
61+ {
62+ "type" : " vite-plugin" ,
63+ "import" : " import arkenv from '@arkenv/vite-plugin'" ,
64+ "code" : " arkenv()"
65+ }
66+ ],
67+ "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 " ,
68+ "files" : {
69+ "_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 " ,
70+ "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",
71+ "src/routes/demo/arkenv.tsx.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 includeDemo = typeof includeExamples !== 'undefined' ? includeExamples : true;\n if (arkenvOption.demo === 'false' || !includeDemo) {\n if (typeof ignoreFile === 'function') {\n ignoreFile();\n return;\n }\n }\n-%>\nimport { 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"
72+ },
73+ "deletedFiles" : []
74+ }
0 commit comments