Files
reverse-engineering-anything/src/hopper/LinuxPrivateDisplayDiagnostic.ts
T
morluto e7f54b3310 refactor(runtime): centralize safe JSON parsing and preserve error causes (#629)
* refactor(domain): consolidate canonical ordering and digest helpers (#624)

* refactor(domain): consolidate canonical ordering and digest helpers

Single owner for code-point ordering (domain/canonicalOrdering.ts)
and canonical JSON digests (domain/comparisonSemantics.ts). Delete
all local compareCodePoints/compareText/digestCanonical duplicates
and migrate every importer. No re-export shims.

* fix(ci): allow slower real browser startup

* chore(config): remove dead eslint, dedupe scripts, cacheable turbo tasks

Delete eslint.config.js (oxlint is the linter), drop lint:dead alias,
pin smol-toml, fold jscpd/scan:todos into check:pr, make test:run
cacheable, align oxlint ignores.

* fix(config): hash all test inputs for cached runs

* docs: fix architecture diagram and pretty-print tool catalog

Drop dangling REPLAY edges, add dotnet/reference/error/evaluation
nodes. Emit 2-space JSON from the catalog generator and regenerate.

* test: extract shared fixtures, drop knip carve-outs

Dedupe android/apple session tests, bundler adapter headers, and
reconstruction helpers into tests/support fixtures. Delete unused
type aliases and the knip ignoreIssues block. Lower jscpd threshold
2.7 to 2.5.

* refactor(app): split Setup and ProcessOwnership modules

Extract SetupTypes/SetupHost from Setup.ts and move observation
helpers into ProcessOwnershipObservation/ProcessTree. Migrate all
importers including CLI commands. No re-export shims.

* refactor(runtime): centralize safe JSON parsing and preserve error causes

Add domain/safeJson.safeParseJson and migrate all raw JSON.parse
call sites. Name catch causes and preserve failure reasons instead
of swallowing; annotate genuine best-effort cleanup.

* fix(runtime): retain JSON parse error causes
2026-10-06 05:00:54 +08:00

82 lines
2.7 KiB
TypeScript

import { z } from "zod";
import {
isHopperStartupFailureCode,
type HopperStartupDiagnostic,
} from "../domain/hopperStartupFailure.js";
import { safeParseJson } from "../domain/safeJson.js";
export const LINUX_PRIVATE_DISPLAY_DIAGNOSTIC_PREFIX = "REA_X11_DIAGNOSTIC=";
const diagnosticContextSchema = z.object({
component: z.literal("hopper_private_display"),
operation: z.enum(["probe", "launch"]),
reason: z.string().regex(/^[a-z0-9_]+$/u),
socket_directory: z.literal("/tmp/.X11-unix"),
socket_directory_mode: z
.string()
.regex(/^[0-7]{4}$/u)
.nullable(),
mount_read_only: z.boolean().nullable(),
effective_socket_directory_mode: z
.string()
.regex(/^[0-7]{4}$/u)
.nullable(),
effective_mount_read_only: z.boolean().nullable(),
wsl: z.boolean(),
strategy: z.enum(["direct", "user-mount-namespace", "unavailable"]),
fallback_reason: z
.string()
.regex(/^[a-z0-9_]+$/u)
.nullable(),
xvfb_stderr_bytes: z.number().int().nonnegative().safe(),
});
const failureCodeSchema = z.string().transform((value, context) => {
if (isHopperStartupFailureCode(value)) return value;
context.addIssue({
code: "custom",
message: "Unknown startup failure code",
});
return z.NEVER;
});
const diagnosticSchema = z.discriminatedUnion("status", [
diagnosticContextSchema
.extend({ status: z.literal("ready"), failure_code: z.null() })
.strict(),
diagnosticContextSchema
.extend({ status: z.literal("error"), failure_code: failureCodeSchema })
.strict(),
]);
export type LinuxPrivateDisplayDiagnosticParse =
| { readonly ok: true; readonly value: HopperStartupDiagnostic }
| {
readonly ok: false;
readonly reason:
| "diagnostic_missing"
| "diagnostic_multiple"
| "diagnostic_malformed";
};
/** Parse exactly one helper diagnostic from stderr. */
export const parseLinuxPrivateDisplayDiagnostic = (
stderr: string,
): LinuxPrivateDisplayDiagnosticParse => {
const records = stderr
.split("\n")
.filter((line) => line.startsWith(LINUX_PRIVATE_DISPLAY_DIAGNOSTIC_PREFIX));
if (records.length === 0) return { ok: false, reason: "diagnostic_missing" };
if (records.length !== 1) return { ok: false, reason: "diagnostic_multiple" };
const record = records[0];
if (record === undefined) return { ok: false, reason: "diagnostic_missing" };
const decoded = safeParseJson(
record.slice(LINUX_PRIVATE_DISPLAY_DIAGNOSTIC_PREFIX.length),
);
if (!decoded.ok) return { ok: false, reason: "diagnostic_malformed" };
const parsed = diagnosticSchema.safeParse(decoded.value);
if (!parsed.success) return { ok: false, reason: "diagnostic_malformed" };
return { ok: true, value: parsed.data };
};