mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-27 19:52:49 +00:00
Name config.json when the CLI cannot parse it (#5337)
readPersistedConfig, used by paseo daemon config, status, pair, and set-password, parsed and validated config.json without the error wrapping loadPersistedConfig applies, so an invalid file surfaced as a bare SyntaxError or ZodError. Both readers now share one parse step that names the file and, for schema errors, the field. Closes #5328 Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -765,6 +765,34 @@ describe("config.json saved with a UTF-8 byte order mark", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("readPersistedConfig with an unreadable config.json", () => {
|
||||
test("names the file when it is not valid JSON", () => {
|
||||
const home = createTempHome();
|
||||
const configPath = path.join(home, "config.json");
|
||||
try {
|
||||
writeFileSync(configPath, '{"version":1,');
|
||||
|
||||
expect(() => readPersistedConfig(home)).toThrow(`[Config] Invalid JSON in ${configPath}: `);
|
||||
} finally {
|
||||
rmSync(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("names the file and the field when it does not match the schema", () => {
|
||||
const home = createTempHome();
|
||||
const configPath = path.join(home, "config.json");
|
||||
try {
|
||||
writeFileSync(configPath, '{"daemon":{"listen":5}}');
|
||||
|
||||
expect(() => readPersistedConfig(home)).toThrow(
|
||||
`[Config] Invalid config in ${configPath}:\n - daemon.listen: `,
|
||||
);
|
||||
} finally {
|
||||
rmSync(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe.skipIf(process.platform === "win32")("persisted config file permissions", () => {
|
||||
test("initializes config.json with private permissions", () => {
|
||||
const home = createTempHome();
|
||||
|
||||
@@ -436,6 +436,29 @@ export function loadPersistedConfig(paseoHome: string, logger?: LoggerLike): Per
|
||||
});
|
||||
}
|
||||
|
||||
const config = parseConfigFile(configPath, raw);
|
||||
log?.info(`Loaded from ${configPath}`);
|
||||
return config;
|
||||
}
|
||||
|
||||
/** Observe the file without initializing a home, identity, or default configuration. */
|
||||
export function readPersistedConfig(
|
||||
paseoHome: string,
|
||||
options: { defaultsIfMissing?: boolean } = {},
|
||||
): PersistedConfig {
|
||||
const configPath = getConfigPath(paseoHome);
|
||||
let raw: string;
|
||||
try {
|
||||
raw = readFileSync(configPath, "utf8");
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === "ENOENT")
|
||||
return options.defaultsIfMissing ? structuredClone(DEFAULT_PERSISTED_CONFIG) : {};
|
||||
throw error;
|
||||
}
|
||||
return parseConfigFile(configPath, raw);
|
||||
}
|
||||
|
||||
function parseConfigFile(configPath: string, raw: string): PersistedConfig {
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = parseConfigText(raw);
|
||||
@@ -446,37 +469,16 @@ export function loadPersistedConfig(paseoHome: string, logger?: LoggerLike): Per
|
||||
});
|
||||
}
|
||||
|
||||
const migrated = stripRemovedConfigFields(parsed);
|
||||
const result = PersistedConfigSchema.safeParse(migrated);
|
||||
const result = PersistedConfigSchema.safeParse(stripRemovedConfigFields(parsed));
|
||||
if (!result.success) {
|
||||
const issues = result.error.issues
|
||||
.map((i) => ` - ${i.path.join(".")}: ${i.message}`)
|
||||
.join("\n");
|
||||
throw new Error(`[Config] Invalid config in ${configPath}:\n${issues}`);
|
||||
}
|
||||
|
||||
log?.info(`Loaded from ${configPath}`);
|
||||
return result.data as PersistedConfig;
|
||||
}
|
||||
|
||||
/** Observe the file without initializing a home, identity, or default configuration. */
|
||||
export function readPersistedConfig(
|
||||
paseoHome: string,
|
||||
options: { defaultsIfMissing?: boolean } = {},
|
||||
): PersistedConfig {
|
||||
let raw: string;
|
||||
try {
|
||||
raw = readFileSync(getConfigPath(paseoHome), "utf8");
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === "ENOENT")
|
||||
return options.defaultsIfMissing ? structuredClone(DEFAULT_PERSISTED_CONFIG) : {};
|
||||
throw error;
|
||||
}
|
||||
return PersistedConfigSchema.parse(
|
||||
stripRemovedConfigFields(parseConfigText(raw)),
|
||||
) as PersistedConfig;
|
||||
}
|
||||
|
||||
/** Editors such as Windows Notepad save UTF-8 with a byte order mark, which JSON.parse rejects. */
|
||||
function parseConfigText(raw: string): unknown {
|
||||
return JSON.parse(raw.replace(/^\uFEFF/, ""));
|
||||
|
||||
Reference in New Issue
Block a user