// SPDX-License-Identifier: MIT import { readFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; type Config = { tenantId?: string; clientId?: string; }; type ConfigCandidate = { tenantId?: unknown; clientId?: unknown; }; export function getUserConfigDir(): string { if (process.platform === "win32") { return process.env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local"); } return process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"); } async function loadConfig(configFileName: string): Promise { if (typeof configFileName !== "string" || configFileName.trim() === "") { throw new Error( 'Invalid config file name. Expected a non-empty string like "public-config.json" or "confidential-config.json".', ); } const envConfig: Config = { tenantId: process.env.AZURE_TENANT_ID, clientId: process.env.AZURE_CLIENT_ID, }; const configPath = path.join(getUserConfigDir(), "sk-az-tools", configFileName); return readFile(configPath, "utf8") .then((configJson) => JSON.parse(configJson) as ConfigCandidate) .catch((err: unknown) => { if ((err as { code?: string } | null)?.code === "ENOENT") { return {} as ConfigCandidate; } throw err; }) .then((json) => ({ tenantId: typeof json.tenantId === "string" && json.tenantId ? json.tenantId : envConfig.tenantId, clientId: typeof json.clientId === "string" && json.clientId ? json.clientId : envConfig.clientId, })); } export function loadPublicConfig(): Promise { return loadConfig("public-config.json"); } export function loadConfidentialConfig(): Promise { return loadConfig("confidential-config.json"); }