From f87c9c92c15e80333506064c9cdeb62dbf4f0393 Mon Sep 17 00:00:00 2001 From: Bilal Godil Date: Thu, 11 Jun 2026 16:47:19 -0700 Subject: [PATCH] fix: address review findings for the HEXCLAVE_* env rename MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - e2e helpers: also expand the port-prefix placeholder in HEXCLAVE_*/ NEXT_PUBLIC_HEXCLAVE_* vars (the renamed .env.development keys no longer matched the STACK_-only prefix filter, leaving literal ${...} in every URL). - docker/local-emulator/generate-env-development.mjs: read source keys under the canonical HEXCLAVE_* name with STACK_* fallback and emit canonical keys (the exact-name lookups threw after the source env files were renamed). - prisma.config.ts: resolve the datasource URL from HEXCLAVE_DATABASE_CONNECTION_STRING with legacy fallback (Prisma's env() helper only knew the legacy name); same for the psql-inner script. - backend vitest: accept both env prefixes and dual-read the DB connection string in the auto-migration tests. - getProcessEnv: empty-as-unset fallback (||), consistent with getEnvVariable — an empty HEXCLAVE_* template placeholder must not shadow a real legacy value. - errors.tsx debugger flag and dashboard next.config emulator flag: dual-read the canonical name. - Vite examples and docs snippets: VITE_STACK_* → VITE_HEXCLAVE_* (the old names were dead after their .env.development files were renamed). --- apps/backend/package.json | 2 +- apps/backend/prisma.config.ts | 5 ++++- .../src/auto-migrations/auto-migration.tests.ts | 4 ++-- .../src/auto-migrations/migration-tests.test.ts | 2 +- apps/backend/vitest.config.ts | 2 +- apps/dashboard/next.config.mjs | 2 +- apps/e2e/tests/helpers.ts | 2 +- docker/local-emulator/generate-env-development.mjs | 13 +++++++++---- docs/code-examples/setup.ts | 8 ++++---- docs/code-examples/vite-example.ts | 12 ++++++------ examples/js-example/hexclave.ts | 6 +++--- .../lovable-react-18-example/src/hexclave/client.ts | 6 +++--- examples/react-example/src/hexclave.ts | 6 +++--- examples/tanstack-start-demo/src/hexclave.ts | 2 +- packages/shared/src/utils/env.tsx | 4 +++- packages/shared/src/utils/errors.tsx | 2 +- 16 files changed, 44 insertions(+), 34 deletions(-) diff --git a/apps/backend/package.json b/apps/backend/package.json index e16231901..ccf240188 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -27,7 +27,7 @@ "codegen-route-info:watch": "pnpm run with-env tsx watch --clear-screen=false scripts/generate-route-info.ts", "codegen": "pnpm run with-env pnpm run generate-migration-imports && pnpm run with-env bash -c 'if [ \"$STACK_ACCELERATE_ENABLED\" = \"true\" ]; then pnpm run prisma generate --no-engine; else pnpm run codegen-prisma; fi' && pnpm run generate-private-sign-up-risk-engine && pnpm run codegen-docs && pnpm run codegen-route-info", "codegen:watch": "pnpm run generate-private-sign-up-risk-engine && concurrently -n \"prisma,private-risk-engine,docs,route-info,migration-imports\" -k \"pnpm run codegen-prisma:watch\" \"pnpm run generate-private-sign-up-risk-engine:watch\" \"pnpm run codegen-docs:watch\" \"pnpm run codegen-route-info:watch\" \"pnpm run generate-migration-imports:watch\"", - "psql-inner": "psql $(echo $STACK_DATABASE_CONNECTION_STRING | sed 's/\\?.*$//')", + "psql-inner": "psql $(echo ${HEXCLAVE_DATABASE_CONNECTION_STRING:-$STACK_DATABASE_CONNECTION_STRING} | sed 's/\\?.*$//')", "clickhouse": "pnpm run with-env clickhouse-client --host localhost --port ${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}37 --user stackframe --password PASSWORD-PLACEHOLDER--9gKyMxJeMx", "psql": "pnpm run with-env:dev pnpm run psql-inner", "prisma-studio": "pnpm run with-env:dev prisma studio --port ${NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81}06 --browser none", diff --git a/apps/backend/prisma.config.ts b/apps/backend/prisma.config.ts index c80c21864..166cec3fc 100644 --- a/apps/backend/prisma.config.ts +++ b/apps/backend/prisma.config.ts @@ -8,7 +8,10 @@ export default defineConfig({ seed: 'pnpm run db-seed-script', }, datasource: { - url: env('STACK_DATABASE_CONNECTION_STRING'), + // Hexclave rebrand: prefer the canonical name, fall back to the legacy one + // (empty counts as unset — the checked-in .env templates define empty placeholders). + // eslint-disable-next-line no-restricted-properties + url: env(process.env.HEXCLAVE_DATABASE_CONNECTION_STRING ? 'HEXCLAVE_DATABASE_CONNECTION_STRING' : 'STACK_DATABASE_CONNECTION_STRING'), }, experimental: { externalTables: true, diff --git a/apps/backend/src/auto-migrations/auto-migration.tests.ts b/apps/backend/src/auto-migrations/auto-migration.tests.ts index 32d32f9e0..56f62adec 100644 --- a/apps/backend/src/auto-migrations/auto-migration.tests.ts +++ b/apps/backend/src/auto-migrations/auto-migration.tests.ts @@ -8,9 +8,9 @@ const TEST_DB_PREFIX = 'stack_auth_test_db'; const getTestDbURL = (testDbName: string) => { // @ts-ignore - ImportMeta.env is provided by Vite - const base = import.meta.env.STACK_DATABASE_CONNECTION_STRING.replace(/\/[^/]*$/, ''); + const base = (import.meta.env.HEXCLAVE_DATABASE_CONNECTION_STRING || import.meta.env.STACK_DATABASE_CONNECTION_STRING).replace(/\/[^/]*$/, ''); // @ts-ignore - ImportMeta.env is provided by Vite - const query = import.meta.env.STACK_DATABASE_CONNECTION_STRING.split('?')[1] ?? ''; + const query = (import.meta.env.HEXCLAVE_DATABASE_CONNECTION_STRING || import.meta.env.STACK_DATABASE_CONNECTION_STRING).split('?')[1] ?? ''; return { full: `${base}/${testDbName}`, base, diff --git a/apps/backend/src/auto-migrations/migration-tests.test.ts b/apps/backend/src/auto-migrations/migration-tests.test.ts index f572372d2..32d35485f 100644 --- a/apps/backend/src/auto-migrations/migration-tests.test.ts +++ b/apps/backend/src/auto-migrations/migration-tests.test.ts @@ -14,7 +14,7 @@ const TEST_DB_PREFIX = 'stack_migration_test'; const getTestDbURL = (testDbName: string) => { // @ts-ignore - ImportMeta.env is provided by Vite - const connString: string = import.meta.env.STACK_DATABASE_CONNECTION_STRING; + const connString: string = (import.meta.env.HEXCLAVE_DATABASE_CONNECTION_STRING || import.meta.env.STACK_DATABASE_CONNECTION_STRING); const base = connString.replace(/\/[^/]*(\?.*)?$/, ''); const query = connString.split('?')[1] ?? ''; return { full: `${base}/${testDbName}`, base, query }; diff --git a/apps/backend/vitest.config.ts b/apps/backend/vitest.config.ts index 49ed590ab..a1759118e 100644 --- a/apps/backend/vitest.config.ts +++ b/apps/backend/vitest.config.ts @@ -21,6 +21,6 @@ export default mergeConfig( } }, envDir: __dirname, - envPrefix: 'STACK_', + envPrefix: ['HEXCLAVE_', 'STACK_'], }) ) diff --git a/apps/dashboard/next.config.mjs b/apps/dashboard/next.config.mjs index 5f4f0a00b..eb8526961 100644 --- a/apps/dashboard/next.config.mjs +++ b/apps/dashboard/next.config.mjs @@ -100,7 +100,7 @@ const nextConfig = { }, async headers() { - const isLocalEmulator = process.env.NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR === "true"; + const isLocalEmulator = (process.env.NEXT_PUBLIC_HEXCLAVE_IS_LOCAL_EMULATOR || process.env.NEXT_PUBLIC_STACK_IS_LOCAL_EMULATOR) === "true"; return [ { source: "/(.*)", diff --git a/apps/e2e/tests/helpers.ts b/apps/e2e/tests/helpers.ts index 851046a5c..0a596e6a5 100644 --- a/apps/e2e/tests/helpers.ts +++ b/apps/e2e/tests/helpers.ts @@ -299,7 +299,7 @@ function expandHexclavePortPrefix(value?: string | null) { return prefix ? value.replace(/\$\{NEXT_PUBLIC_HEXCLAVE_PORT_PREFIX:-81\}/g, prefix) : value; } for (const [key, value] of Object.entries(process.env)) { - if (key.startsWith("STACK_") || key.startsWith("NEXT_PUBLIC_STACK_")) { + if (key.startsWith("STACK_") || key.startsWith("NEXT_PUBLIC_STACK_") || key.startsWith("HEXCLAVE_") || key.startsWith("NEXT_PUBLIC_HEXCLAVE_")) { const replaced = expandHexclavePortPrefix(value ?? undefined); if (replaced !== undefined) { // eslint-disable-next-line no-restricted-syntax diff --git a/docker/local-emulator/generate-env-development.mjs b/docker/local-emulator/generate-env-development.mjs index 5986a8bc3..7bb5db64d 100644 --- a/docker/local-emulator/generate-env-development.mjs +++ b/docker/local-emulator/generate-env-development.mjs @@ -41,23 +41,28 @@ const parseEnvFile = (filePath) => { const backendEnv = parseEnvFile(backendEnvPath); const dashboardEnv = parseEnvFile(dashboardEnvPath); +// Hexclave rebrand: the source env files use the canonical HEXCLAVE_* names, +// but accept the legacy STACK_* spelling as a fallback. Emitted keys are +// always canonical. +const toCanonicalKey = (key) => key.includes("STACK_") ? key.replace("STACK_", "HEXCLAVE_") : key; + const getRequiredEnvValue = (sourceName, envMap, key) => { - const value = envMap.get(key); + const value = envMap.get(toCanonicalKey(key)) ?? envMap.get(key); if (value == null) { - throw new Error(`Missing ${key} in ${sourceName}; update the generator or source env file.`); + throw new Error(`Missing ${toCanonicalKey(key)} in ${sourceName}; update the generator or source env file.`); } return value; }; const fromSource = (sourceName, envMap, key) => ({ type: "entry", - key, + key: toCanonicalKey(key), value: getRequiredEnvValue(sourceName, envMap, key), }); const literal = (key, value) => ({ type: "entry", - key, + key: toCanonicalKey(key), value, }); diff --git a/docs/code-examples/setup.ts b/docs/code-examples/setup.ts index 567b95e15..35d7a14be 100644 --- a/docs/code-examples/setup.ts +++ b/docs/code-examples/setup.ts @@ -80,8 +80,8 @@ STACK_SECRET_SERVER_KEY=`, language: 'JavaScript', framework: 'React', code: `# Store these in environment variables or directly in the client file during development -VITE_STACK_PROJECT_ID= -VITE_STACK_PUBLISHABLE_CLIENT_KEY=`, +VITE_HEXCLAVE_PROJECT_ID= +VITE_HEXCLAVE_PUBLISHABLE_CLIENT_KEY=`, highlightLanguage: 'bash', filename: '.env' }, @@ -172,8 +172,8 @@ export const stackClientApp = new StackClientApp({ // import { useNavigate } from "@tanstack/react-router"; // TanStack Router export const stackClientApp = new StackClientApp({ - projectId: process.env.VITE_STACK_PROJECT_ID || "your-project-id", - publishableClientKey: process.env.VITE_STACK_PUBLISHABLE_CLIENT_KEY || "your-publishable-client-key", + projectId: process.env.VITE_HEXCLAVE_PROJECT_ID || "your-project-id", + publishableClientKey: process.env.VITE_HEXCLAVE_PUBLISHABLE_CLIENT_KEY || "your-publishable-client-key", tokenStore: "cookie", // redirectMethod: { useNavigate }, // Set this for non-Next.js frameworks });`, diff --git a/docs/code-examples/vite-example.ts b/docs/code-examples/vite-example.ts index 2e528fb98..a7dc4e051 100644 --- a/docs/code-examples/vite-example.ts +++ b/docs/code-examples/vite-example.ts @@ -12,17 +12,17 @@ export const viteExamples = { declare global { interface ImportMeta { env: { - VITE_STACK_API_URL: string; - VITE_STACK_PROJECT_ID: string; - VITE_STACK_PUBLISHABLE_CLIENT_KEY: string; + VITE_HEXCLAVE_API_URL: string; + VITE_HEXCLAVE_PROJECT_ID: string; + VITE_HEXCLAVE_PUBLISHABLE_CLIENT_KEY: string; }; } } export const stackClientApp = new StackClientApp({ - baseUrl: import.meta.env.VITE_STACK_API_URL, - projectId: import.meta.env.VITE_STACK_PROJECT_ID, - publishableClientKey: import.meta.env.VITE_STACK_PUBLISHABLE_CLIENT_KEY, + baseUrl: import.meta.env.VITE_HEXCLAVE_API_URL, + projectId: import.meta.env.VITE_HEXCLAVE_PROJECT_ID, + publishableClientKey: import.meta.env.VITE_HEXCLAVE_PUBLISHABLE_CLIENT_KEY, tokenStore: "cookie", urls: { oauthCallback: window.location.origin + "/oauth", diff --git a/examples/js-example/hexclave.ts b/examples/js-example/hexclave.ts index f41a95520..14ca9197e 100644 --- a/examples/js-example/hexclave.ts +++ b/examples/js-example/hexclave.ts @@ -3,9 +3,9 @@ import { StackClientApp } from "@hexclave/js"; export const hexclaveClientApp = new StackClientApp({ - baseUrl: import.meta.env.VITE_STACK_API_URL, - projectId: import.meta.env.VITE_STACK_PROJECT_ID, - publishableClientKey: import.meta.env.VITE_STACK_PUBLISHABLE_CLIENT_KEY, + baseUrl: import.meta.env.VITE_HEXCLAVE_API_URL, + projectId: import.meta.env.VITE_HEXCLAVE_PROJECT_ID, + publishableClientKey: import.meta.env.VITE_HEXCLAVE_PUBLISHABLE_CLIENT_KEY, tokenStore: "cookie", urls: { oauthCallback: window.location.origin + "/oauth", diff --git a/examples/lovable-react-18-example/src/hexclave/client.ts b/examples/lovable-react-18-example/src/hexclave/client.ts index d29517733..2f47e522a 100644 --- a/examples/lovable-react-18-example/src/hexclave/client.ts +++ b/examples/lovable-react-18-example/src/hexclave/client.ts @@ -3,9 +3,9 @@ import { useNavigate } from "react-router-dom"; export const hexclaveClientApp = new StackClientApp({ tokenStore: "cookie", - baseUrl: import.meta.env.VITE_STACK_API_URL, - projectId: import.meta.env.VITE_STACK_PROJECT_ID, - publishableClientKey: import.meta.env.VITE_STACK_PUBLISHABLE_CLIENT_KEY, + baseUrl: import.meta.env.VITE_HEXCLAVE_API_URL, + projectId: import.meta.env.VITE_HEXCLAVE_PROJECT_ID, + publishableClientKey: import.meta.env.VITE_HEXCLAVE_PUBLISHABLE_CLIENT_KEY, redirectMethod: { useNavigate, }, diff --git a/examples/react-example/src/hexclave.ts b/examples/react-example/src/hexclave.ts index e6f292ee5..91608be3e 100644 --- a/examples/react-example/src/hexclave.ts +++ b/examples/react-example/src/hexclave.ts @@ -4,9 +4,9 @@ import { StackClientApp } from "@hexclave/react"; import { useNavigate } from "react-router-dom"; export const hexclaveClientApp = new StackClientApp({ - projectId: import.meta.env.VITE_STACK_PROJECT_ID, - publishableClientKey: import.meta.env.VITE_STACK_PUBLISHABLE_CLIENT_KEY, - baseUrl: import.meta.env.VITE_STACK_API_URL, + projectId: import.meta.env.VITE_HEXCLAVE_PROJECT_ID, + publishableClientKey: import.meta.env.VITE_HEXCLAVE_PUBLISHABLE_CLIENT_KEY, + baseUrl: import.meta.env.VITE_HEXCLAVE_API_URL, tokenStore: "cookie", redirectMethod: { useNavigate, diff --git a/examples/tanstack-start-demo/src/hexclave.ts b/examples/tanstack-start-demo/src/hexclave.ts index 8e36f8d45..09cf4fe90 100644 --- a/examples/tanstack-start-demo/src/hexclave.ts +++ b/examples/tanstack-start-demo/src/hexclave.ts @@ -9,7 +9,7 @@ function replaceHexclavePortPrefix(value: string): string { } function getStackApiUrl(): string { - const configured = import.meta.env.VITE_STACK_API_URL as string | undefined; + const configured = import.meta.env.VITE_HEXCLAVE_API_URL as string | undefined; return configured ? replaceHexclavePortPrefix(configured) : `http://localhost:${getPortPrefix()}02`; } diff --git a/packages/shared/src/utils/env.tsx b/packages/shared/src/utils/env.tsx index 900da02fc..9f5b81a7d 100644 --- a/packages/shared/src/utils/env.tsx +++ b/packages/shared/src/utils/env.tsx @@ -114,6 +114,8 @@ export function getProcessEnv(name: string): string | undefined { return undefined; } // Hexclave rebrand: prefer the HEXCLAVE_*-prefixed equivalent, fall back to the STACK_* name. + // Empty counts as unset — the checked-in .env templates define empty HEXCLAVE_* placeholders, + // which must not shadow a real value under the legacy name. const hexclaveName = getHexclaveEnvVarName(name); - return (hexclaveName ? process.env[hexclaveName] : undefined) ?? process.env[name]; + return (hexclaveName ? process.env[hexclaveName] : undefined) || process.env[name]; } diff --git a/packages/shared/src/utils/errors.tsx b/packages/shared/src/utils/errors.tsx index 57015efba..84d0550cd 100644 --- a/packages/shared/src/utils/errors.tsx +++ b/packages/shared/src/utils/errors.tsx @@ -80,7 +80,7 @@ export class HexclaveAssertionError extends Error { // Use literal dot-form (guarded with `typeof process`) so Next.js / webpack // DefinePlugin can inline the value at build time. See getProcessEnv in ./env. - if ((typeof process !== "undefined" ? process.env.NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR : undefined) === "true") { + if ((typeof process !== "undefined" ? (process.env.NEXT_PUBLIC_HEXCLAVE_DEBUGGER_ON_ASSERTION_ERROR || process.env.NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR) : undefined) === "true") { debugger; } }