@gpc-cli/config 0.9.5 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +23 -1
- package/dist/index.js +40 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
declare class ConfigError extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
readonly suggestion?: string | undefined;
|
|
4
|
+
readonly exitCode = 1;
|
|
5
|
+
constructor(message: string, code: string, suggestion?: string | undefined);
|
|
6
|
+
toJSON(): {
|
|
7
|
+
success: boolean;
|
|
8
|
+
error: {
|
|
9
|
+
code: string;
|
|
10
|
+
message: string;
|
|
11
|
+
suggestion: string | undefined;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface WebhookConfig {
|
|
17
|
+
slack?: string;
|
|
18
|
+
discord?: string;
|
|
19
|
+
custom?: string;
|
|
20
|
+
}
|
|
1
21
|
interface GpcConfig {
|
|
2
22
|
app?: string;
|
|
3
23
|
output?: OutputFormat;
|
|
@@ -7,6 +27,7 @@ interface GpcConfig {
|
|
|
7
27
|
plugins?: string[];
|
|
8
28
|
profiles?: Record<string, ProfileConfig>;
|
|
9
29
|
approvedPlugins?: string[];
|
|
30
|
+
webhooks?: WebhookConfig;
|
|
10
31
|
}
|
|
11
32
|
interface AuthConfig {
|
|
12
33
|
serviceAccount?: string;
|
|
@@ -26,6 +47,7 @@ interface ResolvedConfig extends Required<Pick<GpcConfig, "output">> {
|
|
|
26
47
|
plugins?: string[];
|
|
27
48
|
profiles?: Record<string, ProfileConfig>;
|
|
28
49
|
approvedPlugins?: string[];
|
|
50
|
+
webhooks?: WebhookConfig;
|
|
29
51
|
}
|
|
30
52
|
|
|
31
53
|
declare function loadEnvConfig(): Partial<GpcConfig>;
|
|
@@ -47,4 +69,4 @@ declare function initConfig(config: GpcConfig): Promise<string>;
|
|
|
47
69
|
|
|
48
70
|
declare const DEFAULT_CONFIG: ResolvedConfig;
|
|
49
71
|
|
|
50
|
-
export { type AuthConfig, DEFAULT_CONFIG, type GpcConfig, type OutputFormat, type ProfileConfig, type ResolvedConfig, approvePlugin, deleteProfile, findConfigFile, getCacheDir, getConfigDir, getDataDir, getUserConfigPath, initConfig, listProfiles, loadConfig, loadEnvConfig, revokePluginApproval, setConfigValue, setProfileConfig };
|
|
72
|
+
export { type AuthConfig, ConfigError, DEFAULT_CONFIG, type GpcConfig, type OutputFormat, type ProfileConfig, type ResolvedConfig, type WebhookConfig, approvePlugin, deleteProfile, findConfigFile, getCacheDir, getConfigDir, getDataDir, getUserConfigPath, initConfig, listProfiles, loadConfig, loadEnvConfig, revokePluginApproval, setConfigValue, setProfileConfig };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var ConfigError = class extends Error {
|
|
3
|
+
constructor(message, code, suggestion) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.suggestion = suggestion;
|
|
7
|
+
this.name = "ConfigError";
|
|
8
|
+
}
|
|
9
|
+
exitCode = 1;
|
|
10
|
+
toJSON() {
|
|
11
|
+
return {
|
|
12
|
+
success: false,
|
|
13
|
+
error: {
|
|
14
|
+
code: this.code,
|
|
15
|
+
message: this.message,
|
|
16
|
+
suggestion: this.suggestion
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
1
22
|
// src/loader.ts
|
|
2
23
|
import { readFile, stat } from "fs/promises";
|
|
3
24
|
import { dirname, join as join2 } from "path";
|
|
@@ -77,8 +98,10 @@ async function readConfigFile(filePath) {
|
|
|
77
98
|
const parsed = JSON.parse(content);
|
|
78
99
|
sanitizeObject(parsed);
|
|
79
100
|
if (parsed.output !== void 0 && !isValidOutputFormat(parsed.output)) {
|
|
80
|
-
throw new
|
|
81
|
-
`Invalid output format "${String(parsed.output)}" in ${filePath}. Must be one of: ${[...VALID_OUTPUT_FORMATS].join(", ")}
|
|
101
|
+
throw new ConfigError(
|
|
102
|
+
`Invalid output format "${String(parsed.output)}" in ${filePath}. Must be one of: ${[...VALID_OUTPUT_FORMATS].join(", ")}`,
|
|
103
|
+
"CONFIG_INVALID_VALUE",
|
|
104
|
+
`Set the "output" field to one of: ${[...VALID_OUTPUT_FORMATS].join(", ")}.`
|
|
82
105
|
);
|
|
83
106
|
}
|
|
84
107
|
return parsed;
|
|
@@ -127,8 +150,10 @@ async function loadConfig(overrides) {
|
|
|
127
150
|
if (p?.developerId) result.developerId = p.developerId;
|
|
128
151
|
} else if (result.profile && result.profiles && !(result.profile in result.profiles)) {
|
|
129
152
|
const available = Object.keys(result.profiles).join(", ");
|
|
130
|
-
throw new
|
|
131
|
-
`Profile "${result.profile}" not found. Available profiles: ${available || "(none)"}
|
|
153
|
+
throw new ConfigError(
|
|
154
|
+
`Profile "${result.profile}" not found. Available profiles: ${available || "(none)"}`,
|
|
155
|
+
"CONFIG_PROFILE_NOT_FOUND",
|
|
156
|
+
`Check the profile name in your config or use one of: ${available || "none defined yet. Create one with: gpc config profile set <name>"}`
|
|
132
157
|
);
|
|
133
158
|
}
|
|
134
159
|
if (!isValidOutputFormat(result.output)) {
|
|
@@ -157,12 +182,20 @@ async function writeSecureFile(filePath, content) {
|
|
|
157
182
|
var DANGEROUS_KEYS2 = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
158
183
|
function validateConfigKey(key) {
|
|
159
184
|
if (!key || key.startsWith(".") || key.endsWith(".") || key.includes("..")) {
|
|
160
|
-
throw new
|
|
185
|
+
throw new ConfigError(
|
|
186
|
+
`Invalid config key: "${key}"`,
|
|
187
|
+
"CONFIG_INVALID_KEY",
|
|
188
|
+
"Config keys must be non-empty, cannot start or end with a dot, and cannot contain consecutive dots. Example: auth.serviceAccount"
|
|
189
|
+
);
|
|
161
190
|
}
|
|
162
191
|
const parts = key.split(".");
|
|
163
192
|
for (const part of parts) {
|
|
164
193
|
if (DANGEROUS_KEYS2.has(part)) {
|
|
165
|
-
throw new
|
|
194
|
+
throw new ConfigError(
|
|
195
|
+
`Unsafe config key: "${key}" contains forbidden key "${part}"`,
|
|
196
|
+
"CONFIG_INVALID_KEY",
|
|
197
|
+
`The key "${part}" is reserved and cannot be used in config paths.`
|
|
198
|
+
);
|
|
166
199
|
}
|
|
167
200
|
}
|
|
168
201
|
}
|
|
@@ -273,6 +306,7 @@ async function initConfig(config) {
|
|
|
273
306
|
return configPath;
|
|
274
307
|
}
|
|
275
308
|
export {
|
|
309
|
+
ConfigError,
|
|
276
310
|
DEFAULT_CONFIG,
|
|
277
311
|
approvePlugin,
|
|
278
312
|
deleteProfile,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/loader.ts","../src/defaults.ts","../src/paths.ts","../src/writer.ts"],"sourcesContent":["import { readFile, stat } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { DEFAULT_CONFIG } from \"./defaults.js\";\nimport { getUserConfigPath } from \"./paths.js\";\nimport type { GpcConfig, OutputFormat, ResolvedConfig } from \"./types.js\";\n\nconst VALID_OUTPUT_FORMATS: ReadonlySet<string> = new Set([\"table\", \"json\", \"yaml\", \"markdown\"]);\n\nfunction isValidOutputFormat(value: string): value is OutputFormat {\n return VALID_OUTPUT_FORMATS.has(value);\n}\n\nexport function loadEnvConfig(): Partial<GpcConfig> {\n const config: Partial<GpcConfig> = {};\n\n const app = process.env[\"GPC_APP\"];\n if (app) config.app = app;\n\n const output = process.env[\"GPC_OUTPUT\"];\n if (output) {\n if (isValidOutputFormat(output)) {\n config.output = output;\n }\n }\n\n const profile = process.env[\"GPC_PROFILE\"];\n if (profile) config.profile = profile;\n\n const serviceAccount = process.env[\"GPC_SERVICE_ACCOUNT\"];\n if (serviceAccount) {\n config.auth = { serviceAccount };\n }\n\n const developerId = process.env[\"GPC_DEVELOPER_ID\"];\n if (developerId) config.developerId = developerId;\n\n return config;\n}\n\nexport async function findConfigFile(startDir?: string): Promise<string | undefined> {\n let dir = startDir || process.cwd();\n\n while (true) {\n const candidate = join(dir, \".gpcrc.json\");\n try {\n await stat(candidate);\n return candidate;\n } catch {\n // file does not exist, keep walking up\n }\n\n const parent = dirname(dir);\n if (parent === dir) break; // reached root\n dir = parent;\n }\n\n return undefined;\n}\n\nexport async function readConfigFile(filePath: string): Promise<GpcConfig> {\n const content = await readFile(filePath, \"utf-8\");\n const parsed = JSON.parse(content) as GpcConfig;\n\n // Guard against prototype pollution\n sanitizeObject(parsed);\n\n // Validate output format if present\n if (parsed.output !== undefined && !isValidOutputFormat(parsed.output)) {\n throw new Error(\n `Invalid output format \"${String(parsed.output)}\" in ${filePath}. Must be one of: ${[...VALID_OUTPUT_FORMATS].join(\", \")}`,\n );\n }\n\n return parsed;\n}\n\nconst DANGEROUS_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n/** Remove prototype pollution vectors from parsed JSON objects recursively. */\nfunction sanitizeObject(obj: unknown): void {\n if (!obj || typeof obj !== \"object\") return;\n if (Array.isArray(obj)) {\n for (const item of obj) sanitizeObject(item);\n return;\n }\n const record = obj as Record<string, unknown>;\n for (const key of Object.keys(record)) {\n if (DANGEROUS_KEYS.has(key)) {\n delete record[key];\n } else if (typeof record[key] === \"object\" && record[key] !== null) {\n sanitizeObject(record[key]);\n }\n }\n}\n\nexport async function loadConfig(overrides?: Partial<GpcConfig>): Promise<ResolvedConfig> {\n // Layer 5: defaults\n const result: ResolvedConfig = { ...DEFAULT_CONFIG };\n\n // Layer 4: user config file (~/.config/gpc/config.json)\n try {\n const userConfig = await readConfigFile(getUserConfigPath());\n Object.assign(result, stripUndefined(userConfig));\n } catch {\n // User config doesn't exist or is invalid — skip\n }\n\n // Layer 3: project config file (.gpcrc.json walking up)\n const projectConfigPath = await findConfigFile();\n if (projectConfigPath) {\n try {\n const projectConfig = await readConfigFile(projectConfigPath);\n Object.assign(result, stripUndefined(projectConfig));\n result.configPath = projectConfigPath;\n } catch {\n // Project config is invalid — skip\n }\n }\n\n // Layer 2: environment variables\n const envConfig = loadEnvConfig();\n Object.assign(result, stripUndefined(envConfig));\n\n // Layer 1: CLI flag overrides\n if (overrides) {\n Object.assign(result, stripUndefined(overrides));\n }\n\n // Resolve profile overrides\n if (result.profile && result.profiles?.[result.profile]) {\n const p = result.profiles[result.profile];\n if (p?.auth) result.auth = p.auth;\n if (p?.app) result.app = p.app;\n if (p?.developerId) result.developerId = p.developerId;\n } else if (result.profile && result.profiles && !(result.profile in result.profiles)) {\n const available = Object.keys(result.profiles).join(\", \");\n throw new Error(\n `Profile \"${result.profile}\" not found. Available profiles: ${available || \"(none)\"}`,\n );\n }\n\n // Validate final output format\n if (!isValidOutputFormat(result.output)) {\n result.output = DEFAULT_CONFIG.output;\n }\n\n return result;\n}\n\nfunction stripUndefined<T extends object>(obj: T): Partial<T> {\n const cleaned: Partial<T> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) {\n (cleaned as Record<string, unknown>)[key] = value;\n }\n }\n return cleaned;\n}\n","import type { ResolvedConfig } from \"./types.js\";\n\nexport const DEFAULT_CONFIG: ResolvedConfig = {\n output: \"table\",\n};\n","import { homedir } from \"node:os\";\nimport { join, isAbsolute } from \"node:path\";\n\n// Cache homedir() — it calls an OS function, no need to repeat per call\nconst HOME = homedir();\n\nfunction resolveXdg(envVar: string, fallback: string): string {\n const xdg = process.env[envVar];\n if (xdg && isAbsolute(xdg)) return xdg;\n return fallback;\n}\n\nexport function getConfigDir(): string {\n const base = resolveXdg(\"XDG_CONFIG_HOME\", join(HOME, \".config\"));\n return join(base, \"gpc\");\n}\n\nexport function getUserConfigPath(): string {\n return join(getConfigDir(), \"config.json\");\n}\n\nexport function getDataDir(): string {\n const base = resolveXdg(\"XDG_DATA_HOME\", join(HOME, \".local\", \"share\"));\n return join(base, \"gpc\");\n}\n\nexport function getCacheDir(): string {\n const base = resolveXdg(\"XDG_CACHE_HOME\", join(HOME, \".cache\"));\n return join(base, \"gpc\");\n}\n","import { chmod, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { getConfigDir } from \"./paths.js\";\nimport type { GpcConfig, ProfileConfig } from \"./types.js\";\n\nasync function writeSecureFile(filePath: string, content: string): Promise<void> {\n await writeFile(filePath, content, { encoding: \"utf-8\", mode: 0o600 });\n await chmod(filePath, 0o600).catch(() => {});\n}\n\nconst DANGEROUS_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nfunction validateConfigKey(key: string): void {\n if (!key || key.startsWith(\".\") || key.endsWith(\".\") || key.includes(\"..\")) {\n throw new Error(`Invalid config key: \"${key}\"`);\n }\n const parts = key.split(\".\");\n for (const part of parts) {\n if (DANGEROUS_KEYS.has(part)) {\n throw new Error(`Unsafe config key: \"${key}\" contains forbidden key \"${part}\"`);\n }\n }\n}\n\nexport async function setConfigValue(key: string, value: string): Promise<void> {\n validateConfigKey(key);\n const configPath = join(getConfigDir(), \"config.json\");\n\n let existing: Record<string, unknown> = {};\n try {\n const content = await readFile(configPath, \"utf-8\");\n existing = JSON.parse(content) as Record<string, unknown>;\n } catch {\n // File doesn't exist yet — start fresh\n }\n\n // Support dotted keys like \"auth.serviceAccount\"\n const keys = key.split(\".\");\n let target = existing;\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i] as string;\n if (typeof target[k] !== \"object\" || target[k] === null) {\n target[k] = {};\n }\n target = target[k] as Record<string, unknown>;\n }\n const lastKey = keys[keys.length - 1] as string;\n target[lastKey] = value;\n\n await mkdir(dirname(configPath), { recursive: true, mode: 0o700 });\n await writeSecureFile(configPath, JSON.stringify(existing, null, 2) + \"\\n\");\n}\n\nexport async function setProfileConfig(profileName: string, config: ProfileConfig): Promise<void> {\n const configPath = join(getConfigDir(), \"config.json\");\n\n let existing: Record<string, unknown> = {};\n try {\n const content = await readFile(configPath, \"utf-8\");\n existing = JSON.parse(content) as Record<string, unknown>;\n } catch {\n // start fresh\n }\n\n if (typeof existing[\"profiles\"] !== \"object\" || existing[\"profiles\"] === null) {\n existing[\"profiles\"] = {};\n }\n (existing[\"profiles\"] as Record<string, unknown>)[profileName] = config;\n\n await mkdir(dirname(configPath), { recursive: true, mode: 0o700 });\n await writeSecureFile(configPath, JSON.stringify(existing, null, 2) + \"\\n\");\n}\n\nexport async function deleteProfile(profileName: string): Promise<boolean> {\n const configPath = join(getConfigDir(), \"config.json\");\n\n let existing: Record<string, unknown>;\n try {\n const content = await readFile(configPath, \"utf-8\");\n existing = JSON.parse(content) as Record<string, unknown>;\n } catch {\n return false;\n }\n\n const profiles = existing[\"profiles\"] as Record<string, unknown> | undefined;\n if (!profiles || !(profileName in profiles)) return false;\n\n existing[\"profiles\"] = Object.fromEntries(\n Object.entries(profiles).filter(([key]) => key !== profileName),\n );\n await writeSecureFile(configPath, JSON.stringify(existing, null, 2) + \"\\n\");\n return true;\n}\n\nexport async function listProfiles(): Promise<string[]> {\n const configPath = join(getConfigDir(), \"config.json\");\n\n try {\n const content = await readFile(configPath, \"utf-8\");\n const config = JSON.parse(content) as Record<string, unknown>;\n const profiles = config[\"profiles\"] as Record<string, unknown> | undefined;\n return profiles ? Object.keys(profiles) : [];\n } catch {\n return [];\n }\n}\n\nexport async function approvePlugin(pluginName: string): Promise<void> {\n const configPath = join(getConfigDir(), \"config.json\");\n\n let existing: Record<string, unknown> = {};\n try {\n const content = await readFile(configPath, \"utf-8\");\n existing = JSON.parse(content) as Record<string, unknown>;\n } catch {\n // start fresh\n }\n\n const approved = (existing[\"approvedPlugins\"] as string[] | undefined) ?? [];\n if (!approved.includes(pluginName)) {\n approved.push(pluginName);\n }\n existing[\"approvedPlugins\"] = approved;\n\n await mkdir(dirname(configPath), { recursive: true, mode: 0o700 });\n await writeSecureFile(configPath, JSON.stringify(existing, null, 2) + \"\\n\");\n}\n\nexport async function revokePluginApproval(pluginName: string): Promise<boolean> {\n const configPath = join(getConfigDir(), \"config.json\");\n\n let existing: Record<string, unknown>;\n try {\n const content = await readFile(configPath, \"utf-8\");\n existing = JSON.parse(content) as Record<string, unknown>;\n } catch {\n return false;\n }\n\n const approved = (existing[\"approvedPlugins\"] as string[] | undefined) ?? [];\n const index = approved.indexOf(pluginName);\n if (index === -1) return false;\n\n approved.splice(index, 1);\n existing[\"approvedPlugins\"] = approved;\n await writeSecureFile(configPath, JSON.stringify(existing, null, 2) + \"\\n\");\n return true;\n}\n\nexport async function initConfig(config: GpcConfig): Promise<string> {\n const configDir = getConfigDir();\n const configPath = join(configDir, \"config.json\");\n\n await mkdir(configDir, { recursive: true, mode: 0o700 });\n await writeSecureFile(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n\n return configPath;\n}\n"],"mappings":"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","names":["join","join","readFile","dirname","join","DANGEROUS_KEYS","join","readFile","dirname"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/loader.ts","../src/defaults.ts","../src/paths.ts","../src/writer.ts"],"sourcesContent":["export class ConfigError extends Error {\n public readonly exitCode = 1;\n constructor(\n message: string,\n public readonly code: string,\n public readonly suggestion?: string,\n ) {\n super(message);\n this.name = \"ConfigError\";\n }\n toJSON() {\n return {\n success: false,\n error: {\n code: this.code,\n message: this.message,\n suggestion: this.suggestion,\n },\n };\n }\n}\n","import { readFile, stat } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { DEFAULT_CONFIG } from \"./defaults.js\";\nimport { ConfigError } from \"./errors.js\";\nimport { getUserConfigPath } from \"./paths.js\";\nimport type { GpcConfig, OutputFormat, ResolvedConfig } from \"./types.js\";\n\nconst VALID_OUTPUT_FORMATS: ReadonlySet<string> = new Set([\"table\", \"json\", \"yaml\", \"markdown\"]);\n\nfunction isValidOutputFormat(value: string): value is OutputFormat {\n return VALID_OUTPUT_FORMATS.has(value);\n}\n\nexport function loadEnvConfig(): Partial<GpcConfig> {\n const config: Partial<GpcConfig> = {};\n\n const app = process.env[\"GPC_APP\"];\n if (app) config.app = app;\n\n const output = process.env[\"GPC_OUTPUT\"];\n if (output) {\n if (isValidOutputFormat(output)) {\n config.output = output;\n }\n }\n\n const profile = process.env[\"GPC_PROFILE\"];\n if (profile) config.profile = profile;\n\n const serviceAccount = process.env[\"GPC_SERVICE_ACCOUNT\"];\n if (serviceAccount) {\n config.auth = { serviceAccount };\n }\n\n const developerId = process.env[\"GPC_DEVELOPER_ID\"];\n if (developerId) config.developerId = developerId;\n\n return config;\n}\n\nexport async function findConfigFile(startDir?: string): Promise<string | undefined> {\n let dir = startDir || process.cwd();\n\n while (true) {\n const candidate = join(dir, \".gpcrc.json\");\n try {\n await stat(candidate);\n return candidate;\n } catch {\n // file does not exist, keep walking up\n }\n\n const parent = dirname(dir);\n if (parent === dir) break; // reached root\n dir = parent;\n }\n\n return undefined;\n}\n\nexport async function readConfigFile(filePath: string): Promise<GpcConfig> {\n const content = await readFile(filePath, \"utf-8\");\n const parsed = JSON.parse(content) as GpcConfig;\n\n // Guard against prototype pollution\n sanitizeObject(parsed);\n\n // Validate output format if present\n if (parsed.output !== undefined && !isValidOutputFormat(parsed.output)) {\n throw new ConfigError(\n `Invalid output format \"${String(parsed.output)}\" in ${filePath}. Must be one of: ${[...VALID_OUTPUT_FORMATS].join(\", \")}`,\n \"CONFIG_INVALID_VALUE\",\n `Set the \"output\" field to one of: ${[...VALID_OUTPUT_FORMATS].join(\", \")}.`,\n );\n }\n\n return parsed;\n}\n\nconst DANGEROUS_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\n/** Remove prototype pollution vectors from parsed JSON objects recursively. */\nfunction sanitizeObject(obj: unknown): void {\n if (!obj || typeof obj !== \"object\") return;\n if (Array.isArray(obj)) {\n for (const item of obj) sanitizeObject(item);\n return;\n }\n const record = obj as Record<string, unknown>;\n for (const key of Object.keys(record)) {\n if (DANGEROUS_KEYS.has(key)) {\n delete record[key];\n } else if (typeof record[key] === \"object\" && record[key] !== null) {\n sanitizeObject(record[key]);\n }\n }\n}\n\nexport async function loadConfig(overrides?: Partial<GpcConfig>): Promise<ResolvedConfig> {\n // Layer 5: defaults\n const result: ResolvedConfig = { ...DEFAULT_CONFIG };\n\n // Layer 4: user config file (~/.config/gpc/config.json)\n try {\n const userConfig = await readConfigFile(getUserConfigPath());\n Object.assign(result, stripUndefined(userConfig));\n } catch {\n // User config doesn't exist or is invalid — skip\n }\n\n // Layer 3: project config file (.gpcrc.json walking up)\n const projectConfigPath = await findConfigFile();\n if (projectConfigPath) {\n try {\n const projectConfig = await readConfigFile(projectConfigPath);\n Object.assign(result, stripUndefined(projectConfig));\n result.configPath = projectConfigPath;\n } catch {\n // Project config is invalid — skip\n }\n }\n\n // Layer 2: environment variables\n const envConfig = loadEnvConfig();\n Object.assign(result, stripUndefined(envConfig));\n\n // Layer 1: CLI flag overrides\n if (overrides) {\n Object.assign(result, stripUndefined(overrides));\n }\n\n // Resolve profile overrides\n if (result.profile && result.profiles?.[result.profile]) {\n const p = result.profiles[result.profile];\n if (p?.auth) result.auth = p.auth;\n if (p?.app) result.app = p.app;\n if (p?.developerId) result.developerId = p.developerId;\n } else if (result.profile && result.profiles && !(result.profile in result.profiles)) {\n const available = Object.keys(result.profiles).join(\", \");\n throw new ConfigError(\n `Profile \"${result.profile}\" not found. Available profiles: ${available || \"(none)\"}`,\n \"CONFIG_PROFILE_NOT_FOUND\",\n `Check the profile name in your config or use one of: ${available || \"none defined yet. Create one with: gpc config profile set <name>\"}`,\n );\n }\n\n // Validate final output format\n if (!isValidOutputFormat(result.output)) {\n result.output = DEFAULT_CONFIG.output;\n }\n\n return result;\n}\n\nfunction stripUndefined<T extends object>(obj: T): Partial<T> {\n const cleaned: Partial<T> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) {\n (cleaned as Record<string, unknown>)[key] = value;\n }\n }\n return cleaned;\n}\n","import type { ResolvedConfig } from \"./types.js\";\n\nexport const DEFAULT_CONFIG: ResolvedConfig = {\n output: \"table\",\n};\n","import { homedir } from \"node:os\";\nimport { join, isAbsolute } from \"node:path\";\n\n// Cache homedir() — it calls an OS function, no need to repeat per call\nconst HOME = homedir();\n\nfunction resolveXdg(envVar: string, fallback: string): string {\n const xdg = process.env[envVar];\n if (xdg && isAbsolute(xdg)) return xdg;\n return fallback;\n}\n\nexport function getConfigDir(): string {\n const base = resolveXdg(\"XDG_CONFIG_HOME\", join(HOME, \".config\"));\n return join(base, \"gpc\");\n}\n\nexport function getUserConfigPath(): string {\n return join(getConfigDir(), \"config.json\");\n}\n\nexport function getDataDir(): string {\n const base = resolveXdg(\"XDG_DATA_HOME\", join(HOME, \".local\", \"share\"));\n return join(base, \"gpc\");\n}\n\nexport function getCacheDir(): string {\n const base = resolveXdg(\"XDG_CACHE_HOME\", join(HOME, \".cache\"));\n return join(base, \"gpc\");\n}\n","import { chmod, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { ConfigError } from \"./errors.js\";\nimport { getConfigDir } from \"./paths.js\";\nimport type { GpcConfig, ProfileConfig } from \"./types.js\";\n\nasync function writeSecureFile(filePath: string, content: string): Promise<void> {\n await writeFile(filePath, content, { encoding: \"utf-8\", mode: 0o600 });\n await chmod(filePath, 0o600).catch(() => {});\n}\n\nconst DANGEROUS_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nfunction validateConfigKey(key: string): void {\n if (!key || key.startsWith(\".\") || key.endsWith(\".\") || key.includes(\"..\")) {\n throw new ConfigError(\n `Invalid config key: \"${key}\"`,\n \"CONFIG_INVALID_KEY\",\n \"Config keys must be non-empty, cannot start or end with a dot, and cannot contain consecutive dots. Example: auth.serviceAccount\",\n );\n }\n const parts = key.split(\".\");\n for (const part of parts) {\n if (DANGEROUS_KEYS.has(part)) {\n throw new ConfigError(\n `Unsafe config key: \"${key}\" contains forbidden key \"${part}\"`,\n \"CONFIG_INVALID_KEY\",\n `The key \"${part}\" is reserved and cannot be used in config paths.`,\n );\n }\n }\n}\n\nexport async function setConfigValue(key: string, value: string): Promise<void> {\n validateConfigKey(key);\n const configPath = join(getConfigDir(), \"config.json\");\n\n let existing: Record<string, unknown> = {};\n try {\n const content = await readFile(configPath, \"utf-8\");\n existing = JSON.parse(content) as Record<string, unknown>;\n } catch {\n // File doesn't exist yet — start fresh\n }\n\n // Support dotted keys like \"auth.serviceAccount\"\n const keys = key.split(\".\");\n let target = existing;\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i] as string;\n if (typeof target[k] !== \"object\" || target[k] === null) {\n target[k] = {};\n }\n target = target[k] as Record<string, unknown>;\n }\n const lastKey = keys[keys.length - 1] as string;\n target[lastKey] = value;\n\n await mkdir(dirname(configPath), { recursive: true, mode: 0o700 });\n await writeSecureFile(configPath, JSON.stringify(existing, null, 2) + \"\\n\");\n}\n\nexport async function setProfileConfig(profileName: string, config: ProfileConfig): Promise<void> {\n const configPath = join(getConfigDir(), \"config.json\");\n\n let existing: Record<string, unknown> = {};\n try {\n const content = await readFile(configPath, \"utf-8\");\n existing = JSON.parse(content) as Record<string, unknown>;\n } catch {\n // start fresh\n }\n\n if (typeof existing[\"profiles\"] !== \"object\" || existing[\"profiles\"] === null) {\n existing[\"profiles\"] = {};\n }\n (existing[\"profiles\"] as Record<string, unknown>)[profileName] = config;\n\n await mkdir(dirname(configPath), { recursive: true, mode: 0o700 });\n await writeSecureFile(configPath, JSON.stringify(existing, null, 2) + \"\\n\");\n}\n\nexport async function deleteProfile(profileName: string): Promise<boolean> {\n const configPath = join(getConfigDir(), \"config.json\");\n\n let existing: Record<string, unknown>;\n try {\n const content = await readFile(configPath, \"utf-8\");\n existing = JSON.parse(content) as Record<string, unknown>;\n } catch {\n return false;\n }\n\n const profiles = existing[\"profiles\"] as Record<string, unknown> | undefined;\n if (!profiles || !(profileName in profiles)) return false;\n\n existing[\"profiles\"] = Object.fromEntries(\n Object.entries(profiles).filter(([key]) => key !== profileName),\n );\n await writeSecureFile(configPath, JSON.stringify(existing, null, 2) + \"\\n\");\n return true;\n}\n\nexport async function listProfiles(): Promise<string[]> {\n const configPath = join(getConfigDir(), \"config.json\");\n\n try {\n const content = await readFile(configPath, \"utf-8\");\n const config = JSON.parse(content) as Record<string, unknown>;\n const profiles = config[\"profiles\"] as Record<string, unknown> | undefined;\n return profiles ? Object.keys(profiles) : [];\n } catch {\n return [];\n }\n}\n\nexport async function approvePlugin(pluginName: string): Promise<void> {\n const configPath = join(getConfigDir(), \"config.json\");\n\n let existing: Record<string, unknown> = {};\n try {\n const content = await readFile(configPath, \"utf-8\");\n existing = JSON.parse(content) as Record<string, unknown>;\n } catch {\n // start fresh\n }\n\n const approved = (existing[\"approvedPlugins\"] as string[] | undefined) ?? [];\n if (!approved.includes(pluginName)) {\n approved.push(pluginName);\n }\n existing[\"approvedPlugins\"] = approved;\n\n await mkdir(dirname(configPath), { recursive: true, mode: 0o700 });\n await writeSecureFile(configPath, JSON.stringify(existing, null, 2) + \"\\n\");\n}\n\nexport async function revokePluginApproval(pluginName: string): Promise<boolean> {\n const configPath = join(getConfigDir(), \"config.json\");\n\n let existing: Record<string, unknown>;\n try {\n const content = await readFile(configPath, \"utf-8\");\n existing = JSON.parse(content) as Record<string, unknown>;\n } catch {\n return false;\n }\n\n const approved = (existing[\"approvedPlugins\"] as string[] | undefined) ?? [];\n const index = approved.indexOf(pluginName);\n if (index === -1) return false;\n\n approved.splice(index, 1);\n existing[\"approvedPlugins\"] = approved;\n await writeSecureFile(configPath, JSON.stringify(existing, null, 2) + \"\\n\");\n return true;\n}\n\nexport async function initConfig(config: GpcConfig): Promise<string> {\n const configDir = getConfigDir();\n const configPath = join(configDir, \"config.json\");\n\n await mkdir(configDir, { recursive: true, mode: 0o700 });\n await writeSecureFile(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n\n return configPath;\n}\n"],"mappings":"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","names":["join","join","readFile","dirname","join","DANGEROUS_KEYS","join","readFile","dirname"]}
|