diff --git a/.changeset/tanstack-addon-rc-ranges.md b/.changeset/tanstack-addon-rc-ranges.md new file mode 100644 index 000000000..d93d9a257 --- /dev/null +++ b/.changeset/tanstack-addon-rc-ranges.md @@ -0,0 +1,13 @@ +--- +"@arkenv/tanstack-addon": patch +--- + +#### Point TanStack add-on dependencies at the current release + +Scaffolded TanStack Start apps now install `@arkenv/core`, `@arkenv/standard`, and `@arkenv/vite-plugin` at `^1.0.0-rc.2`, which matches the versions on npm `latest` and still accepts `1.0.0` when it publishes. + +Usage: + +```bash +npx @tanstack/cli create my-app --add-ons https://arkenv.js.org/tanstack/info.json +``` diff --git a/apps/www/content/docs/frameworks/tanstack-start.mdx b/apps/www/content/docs/frameworks/tanstack-start.mdx index 14581727c..10d5b2064 100644 --- a/apps/www/content/docs/frameworks/tanstack-start.mdx +++ b/apps/www/content/docs/frameworks/tanstack-start.mdx @@ -33,6 +33,11 @@ Generate a TanStack Start app with ArkEnv wired in: npx @tanstack/cli create my-app --add-ons https://arkenv.js.org/tanstack/info.json ``` + + `@tanstack/cli` does not include an `arkenv` add-on, so + `--add-ons arkenv` fails. Use the URL above. + + When the CLI runs, it asks a few setup questions — including which validator engine to use (ArkType, Zod, or Valibot) and whether to include the `/demo/arkenv` route. You can accept the defaults or pick diff --git a/apps/www/public/tanstack/add-on.json b/apps/www/public/tanstack/add-on.json index 5c2e17ac9..a2c57bd65 100644 --- a/apps/www/public/tanstack/add-on.json +++ b/apps/www/public/tanstack/add-on.json @@ -1,7 +1,7 @@ { "id": "arkenv", "name": "ArkEnv", - "version": "1.0.0-alpha.1", + "version": "1.0.0-rc.2", "description": "Typesafe environment variable validation with build-time validation and runtime leak protection.", "type": "add-on", "phase": "add-on", @@ -61,7 +61,7 @@ "code": "arkenv()" } ], - "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", + "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-rc.2\",\n \"zod\": \"^3.24.2\"\n<% } else if (validator === 'valibot') { -%>\n \"@arkenv/standard\": \"^1.0.0-rc.2\",\n \"valibot\": \"^1.0.0\"\n<% } else { -%>\n \"@arkenv/core\": \"^1.0.0-rc.2\",\n \"arktype\": \"^2.2.0\"\n<% } -%>\n },\n \"devDependencies\": {\n \"@arkenv/vite-plugin\": \"^1.0.0-rc.2\"\n }\n}\n", "files": { "_dot_env.example": "# Port for the dev/preview server\nPORT=3000\n\n# Public API URL (inlined into client bundle)\nVITE_API_URL=https://api.example.com\n\n# Server-only database connection URL (protected from client access)\nDATABASE_URL=postgresql://postgres:postgres@localhost:5432/db\n\n# Environment mode\nNODE_ENV=development\n", "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", diff --git a/apps/www/public/tanstack/info.json b/apps/www/public/tanstack/info.json index 5c2e17ac9..a2c57bd65 100644 --- a/apps/www/public/tanstack/info.json +++ b/apps/www/public/tanstack/info.json @@ -1,7 +1,7 @@ { "id": "arkenv", "name": "ArkEnv", - "version": "1.0.0-alpha.1", + "version": "1.0.0-rc.2", "description": "Typesafe environment variable validation with build-time validation and runtime leak protection.", "type": "add-on", "phase": "add-on", @@ -61,7 +61,7 @@ "code": "arkenv()" } ], - "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", + "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-rc.2\",\n \"zod\": \"^3.24.2\"\n<% } else if (validator === 'valibot') { -%>\n \"@arkenv/standard\": \"^1.0.0-rc.2\",\n \"valibot\": \"^1.0.0\"\n<% } else { -%>\n \"@arkenv/core\": \"^1.0.0-rc.2\",\n \"arktype\": \"^2.2.0\"\n<% } -%>\n },\n \"devDependencies\": {\n \"@arkenv/vite-plugin\": \"^1.0.0-rc.2\"\n }\n}\n", "files": { "_dot_env.example": "# Port for the dev/preview server\nPORT=3000\n\n# Public API URL (inlined into client bundle)\nVITE_API_URL=https://api.example.com\n\n# Server-only database connection URL (protected from client access)\nDATABASE_URL=postgresql://postgres:postgres@localhost:5432/db\n\n# Environment mode\nNODE_ENV=development\n", "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", diff --git a/apps/www/public/tanstack/package.json.ejs b/apps/www/public/tanstack/package.json.ejs index c54931f26..34bfd06dc 100644 --- a/apps/www/public/tanstack/package.json.ejs +++ b/apps/www/public/tanstack/package.json.ejs @@ -8,17 +8,17 @@ { "dependencies": { <% if (validator === 'zod') { -%> - "@arkenv/standard": "^1.0.0-alpha.1", + "@arkenv/standard": "^1.0.0-rc.2", "zod": "^3.24.2" <% } else if (validator === 'valibot') { -%> - "@arkenv/standard": "^1.0.0-alpha.1", + "@arkenv/standard": "^1.0.0-rc.2", "valibot": "^1.0.0" <% } else { -%> - "@arkenv/core": "^1.0.0-alpha.1", + "@arkenv/core": "^1.0.0-rc.2", "arktype": "^2.2.0" <% } -%> }, "devDependencies": { - "@arkenv/vite-plugin": "^1.0.0-alpha.1" + "@arkenv/vite-plugin": "^1.0.0-rc.2" } } diff --git a/packages/tanstack-addon/.add-on/README.md b/packages/tanstack-addon/.add-on/README.md new file mode 100644 index 000000000..be775a128 --- /dev/null +++ b/packages/tanstack-addon/.add-on/README.md @@ -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) diff --git a/packages/tanstack-addon/.add-on/info.json b/packages/tanstack-addon/.add-on/info.json index 5aadaf452..d5b9eac49 100644 --- a/packages/tanstack-addon/.add-on/info.json +++ b/packages/tanstack-addon/.add-on/info.json @@ -1,7 +1,7 @@ { "id": "arkenv", "name": "ArkEnv", - "version": "1.0.0-alpha.1", + "version": "1.0.0-rc.2", "description": "Typesafe environment variable validation with build-time validation and runtime leak protection.", "type": "add-on", "phase": "add-on", diff --git a/packages/tanstack-addon/.add-on/package.json.ejs b/packages/tanstack-addon/.add-on/package.json.ejs index c54931f26..34bfd06dc 100644 --- a/packages/tanstack-addon/.add-on/package.json.ejs +++ b/packages/tanstack-addon/.add-on/package.json.ejs @@ -8,17 +8,17 @@ { "dependencies": { <% if (validator === 'zod') { -%> - "@arkenv/standard": "^1.0.0-alpha.1", + "@arkenv/standard": "^1.0.0-rc.2", "zod": "^3.24.2" <% } else if (validator === 'valibot') { -%> - "@arkenv/standard": "^1.0.0-alpha.1", + "@arkenv/standard": "^1.0.0-rc.2", "valibot": "^1.0.0" <% } else { -%> - "@arkenv/core": "^1.0.0-alpha.1", + "@arkenv/core": "^1.0.0-rc.2", "arktype": "^2.2.0" <% } -%> }, "devDependencies": { - "@arkenv/vite-plugin": "^1.0.0-alpha.1" + "@arkenv/vite-plugin": "^1.0.0-rc.2" } } diff --git a/packages/tanstack-addon/README.md b/packages/tanstack-addon/README.md index f73e3cd3c..2e3708734 100644 --- a/packages/tanstack-addon/README.md +++ b/packages/tanstack-addon/README.md @@ -10,6 +10,8 @@ Create a new TanStack Start app with ArkEnv pre-configured: tanstack create my-app --add-ons https://arkenv.js.org/tanstack/info.json ``` +`@tanstack/cli` does not include an `arkenv` add-on yet, so `--add-ons arkenv` fails. + Or add to an existing project: ```bash diff --git a/packages/tanstack-addon/tests/templates.test.ts b/packages/tanstack-addon/tests/templates.test.ts index 8d4941454..fcc6f9680 100644 --- a/packages/tanstack-addon/tests/templates.test.ts +++ b/packages/tanstack-addon/tests/templates.test.ts @@ -21,6 +21,9 @@ const demoRouteTemplate = readFileSync( "utf-8", ); +/** Range emitted for `@arkenv/core`, `@arkenv/standard`, and `@arkenv/vite-plugin`. */ +const ARKENV_RANGE = "^1.0.0-rc.2"; + describe("Template Rendering", () => { describe("src/env.ts.ejs", () => { it("renders ArkType syntax by default when no options provided", () => { @@ -96,7 +99,8 @@ describe("Template Rendering", () => { expect(parsed.dependencies["@arkenv/core"]).toBeDefined(); expect(parsed.dependencies.arktype).toBeDefined(); expect(parsed.dependencies["@arkenv/standard"]).toBeUndefined(); - expect(parsed.devDependencies["@arkenv/vite-plugin"]).toBeDefined(); + expect(parsed.devDependencies["@arkenv/vite-plugin"]).toBe(ARKENV_RANGE); + expect(parsed.dependencies["@arkenv/core"]).toBe(ARKENV_RANGE); }); it("renders valid JSON with Zod dependencies", () => { @@ -105,10 +109,10 @@ describe("Template Rendering", () => { }); const parsed = JSON.parse(rendered); - expect(parsed.dependencies["@arkenv/standard"]).toBeDefined(); + expect(parsed.dependencies["@arkenv/standard"]).toBe(ARKENV_RANGE); expect(parsed.dependencies.zod).toBeDefined(); expect(parsed.dependencies["@arkenv/core"]).toBeUndefined(); - expect(parsed.devDependencies["@arkenv/vite-plugin"]).toBeDefined(); + expect(parsed.devDependencies["@arkenv/vite-plugin"]).toBe(ARKENV_RANGE); }); it("renders valid JSON with Valibot dependencies", () => { @@ -117,10 +121,10 @@ describe("Template Rendering", () => { }); const parsed = JSON.parse(rendered); - expect(parsed.dependencies["@arkenv/standard"]).toBeDefined(); + expect(parsed.dependencies["@arkenv/standard"]).toBe(ARKENV_RANGE); expect(parsed.dependencies.valibot).toBeDefined(); expect(parsed.dependencies["@arkenv/core"]).toBeUndefined(); - expect(parsed.devDependencies["@arkenv/vite-plugin"]).toBeDefined(); + expect(parsed.devDependencies["@arkenv/vite-plugin"]).toBe(ARKENV_RANGE); }); it("renders valid JSON when keyed by remote URL ID", () => { @@ -131,8 +135,9 @@ describe("Template Rendering", () => { }); const parsed = JSON.parse(rendered); - expect(parsed.dependencies["@arkenv/standard"]).toBeDefined(); + expect(parsed.dependencies["@arkenv/standard"]).toBe(ARKENV_RANGE); expect(parsed.dependencies.zod).toBeDefined(); + expect(parsed.devDependencies["@arkenv/vite-plugin"]).toBe(ARKENV_RANGE); }); });