chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* §6.3. Case-insensitive key fragments that can never appear in a client
|
||||
* configuration document.
|
||||
*/
|
||||
const FORBIDDEN_CONFIG_NAME_FRAGMENTS = Object.freeze([
|
||||
"PASSWORD",
|
||||
"SECRET",
|
||||
"TOKEN",
|
||||
"PRIVATE_KEY",
|
||||
"CLIENT_SECRET",
|
||||
"ACCESS_KEY",
|
||||
"REFRESH_TOKEN",
|
||||
"COOKIE",
|
||||
"AUTHORIZATION",
|
||||
]);
|
||||
|
||||
/**
|
||||
* Exact top-level keys whose fragment match is a semantic enum name, not a
|
||||
* credential. The allowlist is exact-key only; it is never applied to arbitrary
|
||||
* nested keys.
|
||||
*/
|
||||
const SEMANTIC_KEY_ALLOWLIST = Object.freeze(
|
||||
new Set(["AUTH_MODE", "TELEMETRY_ENABLED"]),
|
||||
);
|
||||
|
||||
export type EnvironmentPhase = "build" | "runtime";
|
||||
export type EnvironmentDefinition = Readonly<{
|
||||
phase: EnvironmentPhase;
|
||||
classification: string;
|
||||
required: boolean;
|
||||
defaultValue: unknown;
|
||||
}>;
|
||||
|
||||
export const ENV_REGISTRY = Object.freeze({
|
||||
VITE_BUILD_ID: build("public-metadata", true, null),
|
||||
VITE_COMMIT_SHA: build("public-metadata", false, "local"),
|
||||
VITE_ROUTER_BASE_PATH: build("compile-time", true, "/"),
|
||||
VITE_RUNTIME_CONFIG_URL: build("compile-time", true, "/config.json"),
|
||||
APP_ENV: runtime("public", true, null),
|
||||
API_BASE_URL: runtime("public-sensitive", true, null),
|
||||
REQUEST_TIMEOUT_MS: runtime("public", false, 10_000),
|
||||
MAX_RETRY_ATTEMPTS: runtime("public", false, 2),
|
||||
TELEMETRY_ENABLED: runtime("public", true, false),
|
||||
TELEMETRY_ENDPOINT: runtime("public-sensitive", false, null),
|
||||
AUTH_MODE: runtime("public", true, "external"),
|
||||
CONFIG_SCHEMA_VERSION: runtime("public", true, null),
|
||||
RELEASE_MANIFEST_URL: runtime("public", true, "/release-manifest.json"),
|
||||
// §3.5: overrides may only disable an installed capability, never enable one.
|
||||
CAPABILITY_OVERRIDES: runtime("public", false, null),
|
||||
});
|
||||
|
||||
function build(
|
||||
classification: string,
|
||||
required: boolean,
|
||||
defaultValue: unknown,
|
||||
): EnvironmentDefinition {
|
||||
return Object.freeze({ phase: "build", classification, required, defaultValue });
|
||||
}
|
||||
|
||||
function runtime(
|
||||
classification: string,
|
||||
required: boolean,
|
||||
defaultValue: unknown,
|
||||
): EnvironmentDefinition {
|
||||
return Object.freeze({ phase: "runtime", classification, required, defaultValue });
|
||||
}
|
||||
|
||||
export function assertSafeConfigNames(
|
||||
config: Readonly<Record<string, unknown>>,
|
||||
depth = 0,
|
||||
): void {
|
||||
if (depth > 4) {
|
||||
throw new Error("Client configuration nesting exceeds its bound");
|
||||
}
|
||||
for (const [name, value] of Object.entries(config)) {
|
||||
const allowlisted = depth === 0 && SEMANTIC_KEY_ALLOWLIST.has(name);
|
||||
if (!allowlisted && isForbiddenConfigName(name)) {
|
||||
throw new Error(`Forbidden client configuration key: ${name}`);
|
||||
}
|
||||
if (value && typeof value === "object" && !Array.isArray(value)) {
|
||||
assertSafeConfigNames(value as Record<string, unknown>, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isForbiddenConfigName(name: string): boolean {
|
||||
const upper = name.toUpperCase();
|
||||
return FORBIDDEN_CONFIG_NAME_FRAGMENTS.some((fragment) =>
|
||||
upper.includes(fragment),
|
||||
);
|
||||
}
|
||||
|
||||
export type BuildEnvironment = Readonly<{
|
||||
VITE_BUILD_ID?: string;
|
||||
VITE_COMMIT_SHA?: string;
|
||||
VITE_ROUTER_BASE_PATH?: string;
|
||||
VITE_RUNTIME_CONFIG_URL?: string;
|
||||
}>;
|
||||
|
||||
export function getBuildConfig(
|
||||
environment: BuildEnvironment = import.meta.env as BuildEnvironment,
|
||||
) {
|
||||
const buildId = environment.VITE_BUILD_ID || "local-build";
|
||||
const commitSha = environment.VITE_COMMIT_SHA || "local";
|
||||
const routerBasePath = environment.VITE_ROUTER_BASE_PATH || "/";
|
||||
const runtimeConfigUrl = environment.VITE_RUNTIME_CONFIG_URL || "/config.json";
|
||||
|
||||
return Object.freeze({ buildId, commitSha, routerBasePath, runtimeConfigUrl });
|
||||
}
|
||||
Reference in New Issue
Block a user