Files
clean-architecture-frontend…/src/bootstrap/load-runtime-config.js

105 lines
2.9 KiB
JavaScript

import { assertSafeConfigNames, getBuildConfig } from "../contracts/env.js";
import { validateRuntimeConfig } from "./runtime-config-schema.js";
export class BootConfigError extends Error {
/**
* @param {string} code
* @param {{ buildId: string, configSchemaVersion?: string, releaseId?: string }} safe
*/
constructor(code, safe) {
super("Runtime configuration could not be loaded");
this.name = "BootConfigError";
this.kind = "BOOT_CONFIG_FAILURE";
this.code = code;
this.safe = Object.freeze({
kind: this.kind,
code,
buildId: safe.buildId,
configSchemaVersion: safe.configSchemaVersion,
releaseId: safe.releaseId,
supportReference: `${safe.buildId}:${code}`,
});
}
}
/**
* @param {{
* fetcher?: typeof fetch,
* buildConfig?: ReturnType<typeof getBuildConfig>,
* now?: () => number
* }} [options]
*/
export async function loadRuntimeConfig(options = {}) {
const fetcher = options.fetcher ?? fetch;
const buildConfig = options.buildConfig ?? getBuildConfig();
const now = options.now ?? performance.now.bind(performance);
const startedAt = now();
let response;
try {
response = await fetcher(buildConfig.runtimeConfigUrl, {
cache: "no-store",
headers: { Accept: "application/json" },
});
} catch {
throw new BootConfigError("CONFIG_FETCH_FAILED", {
buildId: buildConfig.buildId,
});
}
if (!response.ok) {
throw new BootConfigError("CONFIG_HTTP_FAILED", {
buildId: buildConfig.buildId,
});
}
let rawConfig;
try {
rawConfig = await response.json();
} catch {
throw new BootConfigError("CONFIG_JSON_INVALID", {
buildId: buildConfig.buildId,
});
}
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
throw new BootConfigError("CONFIG_SHAPE_INVALID", {
buildId: buildConfig.buildId,
});
}
try {
assertSafeConfigNames(rawConfig);
} catch {
throw new BootConfigError("CONFIG_SECRET_NAME_REJECTED", {
buildId: buildConfig.buildId,
});
}
const validated = validateRuntimeConfig(rawConfig);
if (!validated.success) {
throw new BootConfigError("CONFIG_SCHEMA_INVALID", {
buildId: buildConfig.buildId,
configSchemaVersion:
typeof rawConfig.CONFIG_SCHEMA_VERSION === "string"
? rawConfig.CONFIG_SCHEMA_VERSION
: undefined,
releaseId: typeof rawConfig.RELEASE_ID === "string" ? rawConfig.RELEASE_ID : undefined,
});
}
if (validated.data.BUILD_ID && validated.data.BUILD_ID !== buildConfig.buildId) {
throw new BootConfigError("CONFIG_BUILD_MISMATCH", {
buildId: buildConfig.buildId,
configSchemaVersion: validated.data.CONFIG_SCHEMA_VERSION,
releaseId: validated.data.RELEASE_ID,
});
}
return Object.freeze({
config: validated.data,
build: buildConfig,
validationDurationMs: now() - startedAt,
});
}