mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-30 21:01:54 +08:00
Don't ship env-var conflict-throw in generated customer SDK
The generated env.ts getters ship inside @hexclave/stack / @hexclave/react and are read on a hot, side-effect-free path by SDK consumers. The conflict-detection helper added with the HEXCLAVE_* rename made those getters throw when both a HEXCLAVE_* and STACK_* spelling were set to different values — a breaking change to env-var reading for SDK users. Revert the generated getters to a plain || dual-read chain (prefer HEXCLAVE_*, fall back to legacy STACK_*, empty-as-unset), with no throw. Conflict detection stays in our own non-shipped infra only (packages/shared getEnvVariable/ getProcessEnv, dashboard inline env, CLI auth, docker entrypoint). Order-preserving dedup of the candidate list is kept so HEXCLAVE_API_URL no longer emits its STACK_URL aliases twice.
This commit is contained in:
parent
c9602352df
commit
1ede37281f
@ -63,76 +63,31 @@ const envVarsConfig: Record<string, { allowPublic?: boolean, deprecatedLegacyNam
|
||||
},
|
||||
};
|
||||
|
||||
function getHexclaveEnvVarName(name: string): string | undefined {
|
||||
if (!name.includes("STACK_")) {
|
||||
return undefined;
|
||||
}
|
||||
return name.replace("STACK_", "HEXCLAVE_");
|
||||
}
|
||||
|
||||
function getStackEnvVarName(name: string): string | undefined {
|
||||
if (!name.includes("HEXCLAVE_")) {
|
||||
return undefined;
|
||||
}
|
||||
return name.replace("HEXCLAVE_", "STACK_");
|
||||
}
|
||||
|
||||
function getEnvVarSnippet(variableName: string) {
|
||||
return deindent`
|
||||
((typeof process !== "undefined" ? process.env.${variableName} : undefined) || import.meta.env?.${variableName})
|
||||
`;
|
||||
}
|
||||
|
||||
function getEnvVarCandidateSnippets(allVariables: string[]) {
|
||||
const allVariablesSet = new Set(allVariables);
|
||||
const emittedVariables = new Set<string>();
|
||||
const snippets: string[] = [];
|
||||
|
||||
for (const variableName of allVariables) {
|
||||
if (emittedVariables.has(variableName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const stackName = getStackEnvVarName(variableName);
|
||||
if (stackName != null && allVariablesSet.has(stackName)) {
|
||||
emittedVariables.add(variableName);
|
||||
emittedVariables.add(stackName);
|
||||
snippets.push(deindent`
|
||||
resolveHexclaveStackEnvVar("${variableName}", "${stackName}", ${getEnvVarSnippet(variableName)}, ${getEnvVarSnippet(stackName)})
|
||||
`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const hexclaveName = getHexclaveEnvVarName(variableName);
|
||||
if (hexclaveName != null && allVariablesSet.has(hexclaveName)) {
|
||||
emittedVariables.add(hexclaveName);
|
||||
emittedVariables.add(variableName);
|
||||
snippets.push(deindent`
|
||||
resolveHexclaveStackEnvVar("${hexclaveName}", "${variableName}", ${getEnvVarSnippet(hexclaveName)}, ${getEnvVarSnippet(variableName)})
|
||||
`);
|
||||
continue;
|
||||
}
|
||||
|
||||
emittedVariables.add(variableName);
|
||||
snippets.push(getEnvVarSnippet(variableName));
|
||||
}
|
||||
|
||||
return snippets;
|
||||
}
|
||||
|
||||
function generateEnvVarsConstSnippet() {
|
||||
const getters: string[] = [];
|
||||
for (const [key, config] of Object.entries(envVarsConfig)) {
|
||||
const allVariables = [key, ...(config.deprecatedLegacyNames ?? [])]
|
||||
.flatMap(k => k.startsWith("HEXCLAVE_") ? [k, k.replace("HEXCLAVE_", "STACK_")] : [k])
|
||||
.flatMap(k => config.allowPublic ? [k, `NEXT_PUBLIC_${k}`, `VITE_${k}`] : [k]);
|
||||
const candidateSnippets = getEnvVarCandidateSnippets(allVariables);
|
||||
// Use || (not ??) between candidates: empty-string env vars (e.g. the empty
|
||||
// HEXCLAVE_* placeholders in checked-in .env templates) must not shadow a
|
||||
// real value under a legacy STACK_* name further down the chain.
|
||||
const allVariables = [...new Set(
|
||||
[key, ...(config.deprecatedLegacyNames ?? [])]
|
||||
.flatMap(k => k.startsWith("HEXCLAVE_") ? [k, k.replace("HEXCLAVE_", "STACK_")] : [k])
|
||||
.flatMap(k => config.allowPublic ? [k, `NEXT_PUBLIC_${k}`, `VITE_${k}`] : [k])
|
||||
)];
|
||||
// Prefer the canonical HEXCLAVE_* spelling, falling back to the legacy STACK_*
|
||||
// name. Use || (not ??) between candidates so an empty-string env var (e.g. an
|
||||
// empty HEXCLAVE_* placeholder) can't shadow a real value further down the chain.
|
||||
//
|
||||
// IMPORTANT: this getter ships in the customer SDK, so it must NOT throw when
|
||||
// both spellings are set — reading an env var is a hot, side-effect-free path
|
||||
// for SDK consumers. Conflict detection lives only in our own (non-shipped)
|
||||
// env helpers (packages/shared, dashboard inline env, CLI), never here.
|
||||
getters.push(deindent`
|
||||
get ${key}() {
|
||||
return ${candidateSnippets.join("\n || ")} || undefined;
|
||||
return ${allVariables.map(getEnvVarSnippet).join("\n || ")} || undefined;
|
||||
},
|
||||
`);
|
||||
}
|
||||
@ -141,13 +96,6 @@ function generateEnvVarsConstSnippet() {
|
||||
// DO NOT EDIT IT BY HAND.
|
||||
/* eslint-disable no-restricted-properties */
|
||||
|
||||
function resolveHexclaveStackEnvVar(hexclaveName: string, stackName: string, hexclaveValue: string | undefined, stackValue: string | undefined): string | undefined {
|
||||
if (hexclaveValue && stackValue && hexclaveValue !== stackValue) {
|
||||
throw new Error(\`Environment variables \${hexclaveName} and \${stackName} are both set to different values. Remove one of them or set them to the same value.\`);
|
||||
}
|
||||
return hexclaveValue || stackValue || undefined;
|
||||
}
|
||||
|
||||
export const envVars = {
|
||||
${getters.join("\n")}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user