The discord CLI's top-level handler prints a native Node error that is not already a labelled refusal as 'cannot <syscall> <label> (<CODE>)'. The journal and approvals readers and writers name the file by label at the source, so conversation, control-board and webui callers get the label too. The session directory check moves to engine-pi.mjs as sessionContinues(). Exit codes and refusal codes are unchanged.
54 lines
2.9 KiB
JavaScript
54 lines
2.9 KiB
JavaScript
// One error class for the package. exitCode follows docs/TOOLS.md: 1 operation
|
|
// failed, 2 invalid data or configuration, 3 refused by a brake (STOP or a
|
|
// held binding; a supervisor must not retry), 4 usage.
|
|
// A refusal that names an unknown key quotes it only when it is a plain name;
|
|
// a key that could carry a host path or a secret is described, never echoed (#1873).
|
|
export const plainKey = (k) => (/^[A-Za-z_][A-Za-z0-9_-]{0,63}$/.test(String(k)) ? JSON.stringify(k) : "that is not a plain name");
|
|
// A binding (and seat) name. Defined here, not in binding.mjs, so journal.mjs can label a
|
|
// journal directory without importing binding.mjs and its config readers; copies of
|
|
// journal.mjs + errors.mjs (the queue test repo) stay self-contained. binding.mjs re-exports it.
|
|
export const BINDING_NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
|
|
// A filesystem call that fails names the file by label, the call and the
|
|
// error code, never the path Node puts in the message. The same error object
|
|
// is rethrown, so its type, code and the exit it maps to are unchanged, and
|
|
// it is marked so the CLI's top-level handler prints it as it is (#1873).
|
|
const QUIET = Symbol("discord.quiet");
|
|
const SYSCALL = /^[a-z][a-z0-9_]{0,31}$/;
|
|
const CODE = /^[A-Z][A-Z0-9_]{0,63}$/;
|
|
const callOf = (e) => {
|
|
const word = typeof e.syscall === "string" ? e.syscall.split(" ")[0] : "";
|
|
return SYSCALL.test(word) ? word : "access";
|
|
};
|
|
const codeOf = (e) => (typeof e.code === "string" && CODE.test(e.code) ? e.code : "UNKNOWN");
|
|
export function quietFs(e, label, verb = null) {
|
|
if (e && typeof e === "object" && typeof e.code === "string") {
|
|
e.message = `cannot ${verb ?? callOf(e)} ${label} (${codeOf(e)})`;
|
|
e[QUIET] = true;
|
|
}
|
|
return e;
|
|
}
|
|
export const quietRead = (e, label) => quietFs(e, label, "read");
|
|
|
|
// What a top-level handler prints for an error (#1873). Our refusals and an
|
|
// error already labelled at its source print their own message. A native
|
|
// Node error (it carries a syscall, or a code and a path) that slipped past
|
|
// every source label prints only its call and code, never its message,
|
|
// which carries the path; `label` names the file when the error has one.
|
|
export function printable(err, label = "a discord file") {
|
|
if (err instanceof DiscordError || (err && err[QUIET] === true)) return String(err.message);
|
|
if (err && typeof err === "object" && (typeof err.syscall === "string" || (typeof err.code === "string" && typeof err.path === "string"))) {
|
|
return typeof err.path === "string" ? `cannot ${callOf(err)} ${label} (${codeOf(err)})` : `cannot ${callOf(err)} (${codeOf(err)})`;
|
|
}
|
|
return err && typeof err.message === "string" ? err.message : String(err);
|
|
}
|
|
|
|
export class DiscordError extends Error {
|
|
constructor(message, exitCode = 2, details = undefined) {
|
|
super(message);
|
|
this.name = "DiscordError";
|
|
this.exitCode = exitCode;
|
|
if (details !== undefined) this.details = details;
|
|
}
|
|
}
|