import type { ProductFeatureOverrideMap } from "../contracts/product-features.ts"; import { runtimeConfigV1ArtifactSchema, runtimeConfigV2ArtifactSchema, type CapabilityOverrideArtifact, type RuntimeConfigV1Artifact, type RuntimeConfigV2Artifact, } from "../contracts/release-artifacts.ts"; export { isValidReleaseManifestUrl } from "../contracts/release-artifacts.ts"; export type CapabilityOverrides = CapabilityOverrideArtifact; /** * §6.1. Runtime Config V2 is deployment and browser operational setting only. * `API_CONTRACT_VERSION` is gone: a scalar cannot describe a multi-package * contract set, and Release Manifest V2 `contractSet` owns that meaning. */ export const runtimeConfigV2Schema = runtimeConfigV2ArtifactSchema; /** * §5.8 / §24.6 RC-2. The V1 reader is retained for one compatibility window so * a release never swaps source shape, manifest shape and runtime behaviour at * the same time. Only a V1 document may carry the scalar contract version. */ export const runtimeConfigV1Schema = runtimeConfigV1ArtifactSchema; export type RuntimeConfigV2 = RuntimeConfigV2Artifact; export type RuntimeConfigV1 = RuntimeConfigV1Artifact; /** * The composition-facing shape. V1 documents are normalized onto it so the rest * of the runtime never branches on config schema version. */ export type RuntimeConfig = Readonly<{ APP_ENV: RuntimeConfigV2["APP_ENV"]; API_BASE_URL: string; REQUEST_TIMEOUT_MS: number; MAX_RETRY_ATTEMPTS: number; TELEMETRY_ENABLED: boolean; TELEMETRY_ENDPOINT?: string; AUTH_MODE: RuntimeConfigV2["AUTH_MODE"]; CONFIG_SCHEMA_VERSION: string; RELEASE_MANIFEST_URL: string; RELEASE_ID?: string; BUILD_ID?: string; CAPABILITY_OVERRIDES: CapabilityOverrides; /** * §3.5. Runtime kill switch per installed feature. Subtractive only: a * feature the build did not install cannot be named into existence here. */ FEATURE_OVERRIDES: ProductFeatureOverrideMap; /** Present only while a V1 document is still accepted. */ LEGACY_API_CONTRACT_VERSION?: string; }>; export type RuntimeConfigValidation = | Readonly<{ success: true; data: RuntimeConfig; schema: "V1" | "V2" }> | Readonly<{ success: false; issues: readonly Readonly<{ path: string; code: string }>[]; }>; const DEFAULT_OVERRIDES: CapabilityOverrides = Object.freeze({ REALTIME: "DEFAULT" as const, WEB_WORKER: "DEFAULT" as const, SERVICE_WORKER: "DEFAULT" as const, OFFLINE_COMMANDS: "DEFAULT" as const, }); function canonicalUrl(value: string): string { return new URL(value).href; } export function validateRuntimeConfig(value: unknown): RuntimeConfigValidation { const declared = value && typeof value === "object" ? (value as Record).CONFIG_SCHEMA_VERSION : undefined; // §5.8: no precedence between V1 and V2. The declared version selects exactly // one parser, and a V2 document carrying the removed scalar is rejected. if (declared !== "1" && declared !== "2.0") { return Object.freeze({ success: false as const, issues: Object.freeze([ Object.freeze({ path: "CONFIG_SCHEMA_VERSION", code: "unsupported_value", }), ]), }); } const isV2 = declared === "2.0"; const result = isV2 ? runtimeConfigV2Schema.safeParse(value) : runtimeConfigV1Schema.safeParse(value); if (!result.success) { return Object.freeze({ success: false as const, issues: Object.freeze( result.error.issues.map((issue) => Object.freeze({ path: issue.path.join("."), code: issue.code }), ), ), }); } const parsed = result.data; const normalized: RuntimeConfig = Object.freeze({ APP_ENV: parsed.APP_ENV, API_BASE_URL: canonicalUrl(parsed.API_BASE_URL), REQUEST_TIMEOUT_MS: parsed.REQUEST_TIMEOUT_MS, MAX_RETRY_ATTEMPTS: parsed.MAX_RETRY_ATTEMPTS, TELEMETRY_ENABLED: parsed.TELEMETRY_ENABLED, ...(parsed.TELEMETRY_ENDPOINT ? { TELEMETRY_ENDPOINT: canonicalUrl(parsed.TELEMETRY_ENDPOINT) } : {}), AUTH_MODE: parsed.AUTH_MODE, CONFIG_SCHEMA_VERSION: parsed.CONFIG_SCHEMA_VERSION, RELEASE_MANIFEST_URL: parsed.RELEASE_MANIFEST_URL, ...(parsed.RELEASE_ID ? { RELEASE_ID: parsed.RELEASE_ID } : {}), ...(parsed.BUILD_ID ? { BUILD_ID: parsed.BUILD_ID } : {}), CAPABILITY_OVERRIDES: Object.freeze({ ...(isV2 ? (parsed as RuntimeConfigV2).CAPABILITY_OVERRIDES : DEFAULT_OVERRIDES), }), // A V1 document predates feature overrides, so it disables nothing. FEATURE_OVERRIDES: Object.freeze({ ...(isV2 ? (parsed as RuntimeConfigV2).FEATURE_OVERRIDES : {}), }), ...(isV2 ? {} : { LEGACY_API_CONTRACT_VERSION: (parsed as RuntimeConfigV1) .API_CONTRACT_VERSION, }), }); return Object.freeze({ success: true as const, data: normalized, schema: isV2 ? ("V2" as const) : ("V1" as const), }); }