mirror of
https://github.com/stablyai/orca.git
synced 2026-10-04 00:02:21 +00:00
#17230 added stripIpcInvokeEnvelope as a canonical home for Electron's IPC wrapper. It was the sixth implementation, not the first: nine other files carried ten hand-rolled copies, and they disagreed. Measured against a shared corpus, the copies split four ways. An envelope whose tail has no "Error: " prefix — Electron builds that tail from the main side's error.toString(), so a rejected non-Error produces one — was left fully visible by quick-open. A message-less handler failure rendered the bare word "Error" in the Linux recovery card, the voice download toast and the AI Vault scan row; an empty tail rendered an empty string in all three. Every copy was anchored at ^, so an envelope a caller had prefixed with its own context stayed on screen. The two AccountsPane copies were scoped to their own channel, so a rejection from any other channel kept its wrapper. All ten now route through one stripper, moved to src/shared because ai-vault-scan-error-message is imported by main and cannot reach a renderer-only module. The canonical regex additionally covers "Error occurred in handler for", which AccountsPane stripped and the canonical one did not, and a separate stripErrorClassPrefix keeps the bare "Error: " trim that three sites had — that prefix is Error.prototype.toString(), not the envelope, and an existing Linux card test caught its loss. Nothing is swallowed. The three sites that gained a null fallback now console.warn the original rejection, which none of them logged before, and Electron still logs the handler's original error with its stack in main. extractIpcErrorMessage keeps its own fail-open regex and its 18 call sites are untouched: it returns the tail verbatim and never returns null. A census test pins the envelope to the two files that own it, so a seventh copy fails CI. It lists nine offenders against the pre-fix tree.
53 lines
2.4 KiB
TypeScript
53 lines
2.4 KiB
TypeScript
import { readdirSync, readFileSync } from 'node:fs'
|
|
import { join, relative, sep } from 'node:path'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
// Why: the envelope is one wire format, and every hand-rolled copy of it is a chance to
|
|
// disagree. Six independent copies had already drifted — some kept a trailing `Error:` class
|
|
// name, some scoped themselves to a single channel, and three rendered "Error" or an empty
|
|
// string when the envelope carried no reason. This census is what stops a seventh appearing.
|
|
const ENVELOPE_OWNERS = [
|
|
// The canonical stripper. The only place the wire format is described.
|
|
'src/shared/ipc-invoke-envelope.ts',
|
|
// `extractIpcErrorMessage` keeps its own fail-open regex on purpose: it returns the tail
|
|
// verbatim (class name included) and never returns null, which 18 call sites rely on.
|
|
'src/renderer/src/lib/ipc-error.ts'
|
|
]
|
|
|
|
const REPO_ROOT = join(__dirname, '..', '..')
|
|
|
|
function listSourceFiles(dir: string): string[] {
|
|
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
const fullPath = join(dir, entry.name)
|
|
if (entry.isDirectory()) {
|
|
return listSourceFiles(fullPath)
|
|
}
|
|
if (!/\.(ts|tsx)$/.test(entry.name) || /\.test\.(ts|tsx)$/.test(entry.name)) {
|
|
return []
|
|
}
|
|
return [fullPath]
|
|
})
|
|
}
|
|
|
|
// Why: strip comments first, so a file that only *documents* the envelope (the terminal toast
|
|
// explains that its markers arrive IPC-wrapped) is not counted as a second implementation.
|
|
function stripComments(source: string): string {
|
|
return source.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|[^:])\/\/.*$/gm, '$1')
|
|
}
|
|
|
|
// Why: match the bare Electron names rather than a quoted-channel shape — the canonical file
|
|
// spells them as an alternation, and a future copy could too. Prose lives in comments, which
|
|
// are stripped above, so any surviving mention is code that knows the wire format.
|
|
const ENVELOPE_MATCHER = /Error invoking remote method|Error occurred in handler for/
|
|
|
|
describe('IPC invoke envelope has a single source of truth', () => {
|
|
it('is described in exactly the files that own it', () => {
|
|
const offenders = listSourceFiles(join(REPO_ROOT, 'src'))
|
|
.filter((file) => ENVELOPE_MATCHER.test(stripComments(readFileSync(file, 'utf8'))))
|
|
.map((file) => relative(REPO_ROOT, file).split(sep).join('/'))
|
|
.sort()
|
|
|
|
expect(offenders).toEqual([...ENVELOPE_OWNERS].sort())
|
|
})
|
|
})
|