mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-24 14:55:37 +00:00
Repair stale arm64 uiohook binary
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("electron", () => ({
|
||||
app: {
|
||||
getPath: vi.fn(() => "/tmp"),
|
||||
setPath: vi.fn(),
|
||||
isReady: vi.fn(() => true),
|
||||
},
|
||||
}));
|
||||
|
||||
import { repairBundledUiohookBinaryForCurrentArch } from "./interaction";
|
||||
|
||||
describe("repairBundledUiohookBinaryForCurrentArch", () => {
|
||||
const tempRoots: string[] = [];
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(
|
||||
tempRoots.splice(0).map((tempRoot) => fs.rm(tempRoot, { recursive: true, force: true })),
|
||||
);
|
||||
});
|
||||
|
||||
it("promotes the bundled darwin-arm64 prebuild over a stale incompatible build", async () => {
|
||||
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "recordly-uiohook-"));
|
||||
tempRoots.push(tempRoot);
|
||||
|
||||
const packageRoot = path.join(tempRoot, "uiohook-napi");
|
||||
const prebuildPath = path.join(packageRoot, "prebuilds", "darwin-arm64", "node.napi.node");
|
||||
const buildPath = path.join(packageRoot, "build", "Release", "uiohook_napi.node");
|
||||
await fs.mkdir(path.dirname(prebuildPath), { recursive: true });
|
||||
await fs.mkdir(path.dirname(buildPath), { recursive: true });
|
||||
await fs.writeFile(prebuildPath, "arm64-prebuild");
|
||||
await fs.writeFile(buildPath, "x64-build");
|
||||
|
||||
const log = vi.fn();
|
||||
const repaired = repairBundledUiohookBinaryForCurrentArch(
|
||||
Object.assign(new Error("mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64')"), {
|
||||
code: "ERR_DLOPEN_FAILED",
|
||||
}),
|
||||
{ packageRoot, platform: "darwin", arch: "arm64", log },
|
||||
);
|
||||
|
||||
expect(repaired).toBe(true);
|
||||
expect(await fs.readFile(buildPath, "utf8")).toBe("arm64-prebuild");
|
||||
expect(log).toHaveBeenCalledWith(
|
||||
"[CursorTelemetry] Repaired stale uiohook-napi binary using bundled darwin-arm64 prebuild.",
|
||||
);
|
||||
});
|
||||
|
||||
it("does not rewrite binaries for unrelated load failures", async () => {
|
||||
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "recordly-uiohook-"));
|
||||
tempRoots.push(tempRoot);
|
||||
|
||||
const packageRoot = path.join(tempRoot, "uiohook-napi");
|
||||
const buildPath = path.join(packageRoot, "build", "Release", "uiohook_napi.node");
|
||||
await fs.mkdir(path.dirname(buildPath), { recursive: true });
|
||||
await fs.writeFile(buildPath, "existing-build");
|
||||
|
||||
const repaired = repairBundledUiohookBinaryForCurrentArch(
|
||||
Object.assign(new Error("some other dlopen failure"), {
|
||||
code: "ERR_DLOPEN_FAILED",
|
||||
}),
|
||||
{ packageRoot, platform: "darwin", arch: "arm64" },
|
||||
);
|
||||
|
||||
expect(repaired).toBe(false);
|
||||
expect(await fs.readFile(buildPath, "utf8")).toBe("existing-build");
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
import type { HookMouseEvent, UiohookLike, UiohookModuleNamespace, CursorInteractionType } from "../types";
|
||||
import {
|
||||
isCursorCaptureActive,
|
||||
@@ -54,8 +56,7 @@ function isUiohookLike(value: unknown): value is UiohookLike {
|
||||
return typeof candidate?.on === "function" && typeof candidate?.start === "function";
|
||||
}
|
||||
|
||||
function loadUiohookModule() {
|
||||
const moduleExports = nodeRequire("uiohook-napi") as UiohookModuleNamespace;
|
||||
function resolveUiohookModule(moduleExports: UiohookModuleNamespace) {
|
||||
const defaultExport = moduleExports.default;
|
||||
|
||||
if (moduleExports.uIOhook) {
|
||||
@@ -89,6 +90,88 @@ function loadUiohookModule() {
|
||||
return null;
|
||||
}
|
||||
|
||||
function shouldRepairBundledUiohookBinary(error: unknown): error is NodeJS.ErrnoException {
|
||||
if (process.platform !== "darwin") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (process.arch !== "arm64") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const candidate = error as NodeJS.ErrnoException | null;
|
||||
return (
|
||||
candidate?.code === "ERR_DLOPEN_FAILED" &&
|
||||
typeof candidate.message === "string" &&
|
||||
candidate.message.includes("incompatible architecture")
|
||||
);
|
||||
}
|
||||
|
||||
export function repairBundledUiohookBinaryForCurrentArch(
|
||||
error: unknown,
|
||||
options?: {
|
||||
packageRoot?: string;
|
||||
platform?: NodeJS.Platform;
|
||||
arch?: string;
|
||||
log?: (message: string) => void;
|
||||
},
|
||||
) {
|
||||
const platform = options?.platform ?? process.platform;
|
||||
const arch = options?.arch ?? process.arch;
|
||||
|
||||
if (platform !== "darwin" || arch !== "arm64") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const candidate = error as NodeJS.ErrnoException | null;
|
||||
if (
|
||||
candidate?.code !== "ERR_DLOPEN_FAILED" ||
|
||||
typeof candidate.message !== "string" ||
|
||||
!candidate.message.includes("incompatible architecture")
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const packageRoot =
|
||||
options?.packageRoot ?? path.dirname(nodeRequire.resolve("uiohook-napi/package.json"));
|
||||
const prebuildPath = path.join(packageRoot, "prebuilds", `darwin-${arch}`, "node.napi.node");
|
||||
const buildPath = path.join(packageRoot, "build", "Release", "uiohook_napi.node");
|
||||
|
||||
if (!fs.existsSync(prebuildPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
fs.mkdirSync(path.dirname(buildPath), { recursive: true });
|
||||
fs.copyFileSync(prebuildPath, buildPath);
|
||||
(options?.log ?? console.warn)(
|
||||
"[CursorTelemetry] Repaired stale uiohook-napi binary using bundled darwin-arm64 prebuild.",
|
||||
);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function loadUiohookModule() {
|
||||
try {
|
||||
const moduleExports = nodeRequire("uiohook-napi") as UiohookModuleNamespace;
|
||||
return resolveUiohookModule(moduleExports);
|
||||
} catch (error) {
|
||||
if (!shouldRepairBundledUiohookBinary(error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!repairBundledUiohookBinaryForCurrentArch(error)) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
delete nodeRequire.cache[nodeRequire.resolve("uiohook-napi")];
|
||||
const moduleExports = nodeRequire("uiohook-napi") as UiohookModuleNamespace;
|
||||
return resolveUiohookModule(moduleExports);
|
||||
}
|
||||
}
|
||||
|
||||
export async function startInteractionCapture() {
|
||||
if (!isCursorCaptureActive) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user