Files
orca/src/main/ipc/filesystem-create-path-guards.ts
T
Neil 15e1ba3f84 refactor(ipc): split main-process IPC modules under the max-lines budget (#14703)
The six oversized src/main/ipc modules each carried a file-level
`eslint-disable max-lines` and ran 427-671 counted lines against a 300-line
budget. AGENTS.md calls for splitting rather than suppressing, and
config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all six
suppressions and prunes their entries (341 -> 335).

Pure move, no behavior change. Each file is cut along the seams it already had:
pet splits into format allowlist / storage paths / symlink-safe copy / bundle
manifest + import; filesystem-auth into path-containment primitives, the
config-derived allow-list, and the git-registered root cache; notifications into
sound selection, native lifecycle, permission probe, and burst cooldown;
crash-reporting into renderer error reports, breadcrumbs, and sender.

The IPC surface is proved intact rather than assumed: comparing (method,
channel) multisets between HEAD and the split gives 49 registrations across 49
distinct channels on both sides. filesystem-auth's security boundary keeps its
acyclic layering -- containment primitives, then allow-list, then root cache,
then path-resolution orchestration -- with no layer gaining a back-edge.

Also keeps clipboard-ipc-handlers.test.ts under the 800-line test budget. The
split had briefly added a redundant vi.mock for isENOENT (byte-identical to the
real implementation) that pushed it to 801; the mock is dropped in favor of the
real function, with realpath added to the existing node:fs/promises mock.

Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green
(the one remaining failure is a pre-existing load flake in an untouched file,
green when re-run serially), no new runtime import cycles among 617 modules,
and no lint suppression added anywhere.
2026-08-15 18:08:54 -07:00

44 lines
1.5 KiB
TypeScript

import { lstat } from 'node:fs/promises'
import { basename } from 'node:path'
import { isENOENT } from './filesystem-path-containment'
/**
* Re-throw filesystem errors with user-friendly messages.
* The `wx` flag on writeFile throws a raw EEXIST with no helpful message,
* so we catch it here and provide context the renderer can display directly.
*/
export function rethrowWithUserMessage(error: unknown, targetPath: string): never {
const name = basename(targetPath)
if (error instanceof Error && 'code' in error) {
const code = (error as NodeJS.ErrnoException).code
if (code === 'EEXIST') {
throw new Error(`A file or folder named '${name}' already exists in this location`)
}
if (code === 'EACCES' || code === 'EPERM') {
throw new Error(`Permission denied: unable to create '${name}'`)
}
}
throw error
}
/**
* Ensure `targetPath` does not already exist. Throws if it does.
*
* Note: this is a non-atomic check — a concurrent operation could create the
* path between `lstat` and the caller's next action. Acceptable for a desktop
* app with low concurrency; `createFile` uses the `wx` flag for an atomic
* alternative where possible.
*/
export async function assertNotExists(targetPath: string): Promise<void> {
try {
await lstat(targetPath)
throw new Error(
`A file or folder named '${basename(targetPath)}' already exists in this location`
)
} catch (error) {
if (!isENOENT(error)) {
throw error
}
}
}