mirror of
https://github.com/stablyai/orca.git
synced 2026-09-21 16:02:20 +00:00
`anti-slop/no-reflect-apply` rejects `Reflect.apply(fn, thisArg, argsArray)`.
It defeats the call-signature checks TypeScript applies to an ordinary call:
the args array is checked as an array, not positionally against the callee's
parameters, so arity and type errors pass silently. Dynamic dispatch belongs
behind a named interface, not behind a reflective call.
Flipped the rule from "off" to "error" and cleared all 17 baseline violations
across `src config tests mobile` (16 sites; one file had two).
Fix pattern: `Reflect.apply(fn, recv, args)` becomes `fn.call(recv, ...args)`,
or a direct method call when the implicit receiver is already the right object.
The receiver is preserved at every site.
Where the callee is a captured built-in whose overloads split on an argument's
shape (`String.prototype.split`, `JSON.stringify`), a call-signature capture no
longer compiles once the args are passed positionally. Those three sites capture
the function through a method-shaped type
(`{ split(separator: unknown, limit?: number): string[] }['split']`), which keeps
the forwarding call checked rather than asserted.
Behaviour notes:
- `diff-section-layout.test.ts` drops a `limit === undefined ? [sep] : [sep, limit]`
conditional. Equivalent: `String.prototype.split` maps an undefined limit to
2^32-1, and the `Symbol.split` path forwards undefined either way.
- `workspace-space-compaction.test.ts` forwards `reduce`'s two arguments unchanged,
so the `arguments.length >= 2` initial-value branch is unaffected.
- `agent-session-history-byte-accounting.test.ts` is the one site where the receiver
is not literally preserved (`JSON` -> undefined). `JSON.stringify` never reads
`this` per spec, and restoring `.call(JSON, ...)` would reintroduce the overload
failure under strictBindCallApply.
No suppression comments added — the rule has zero `oxlint-disable` sites.
`Reflect.apply` still appears at electron.vite.config.ts:159, inside a template
literal of generated bootstrap source. That is string content, not lintable code.
121 lines
3.5 KiB
JavaScript
121 lines
3.5 KiB
JavaScript
export function installMainBlockingProbe() {
|
|
if (globalThis.__orcaMainBlockingProbe) {
|
|
throw new Error('Main blocking probe already exists')
|
|
}
|
|
const events = []
|
|
const cleanup = []
|
|
const startedAt = Date.now()
|
|
function wrap(object, name, label, sizeOf) {
|
|
const original = object[name]
|
|
const wrapped = function (...args) {
|
|
const start = performance.now()
|
|
const epoch = Date.now()
|
|
let result
|
|
try {
|
|
result = original.call(this, ...args)
|
|
return result
|
|
} finally {
|
|
const durationMs = performance.now() - start
|
|
if (durationMs >= 8 && events.length < 2000) {
|
|
events.push({
|
|
epoch,
|
|
durationMs,
|
|
label,
|
|
size: sizeOf?.(args, result) ?? null,
|
|
stack: new Error('Main blocking call').stack?.split('\n').slice(2, 10)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
object[name] = wrapped
|
|
cleanup.push(() => {
|
|
if (object[name] === wrapped) {
|
|
object[name] = original
|
|
}
|
|
})
|
|
}
|
|
wrap(JSON, 'stringify', 'JSON.stringify', (_args, result) => result?.length)
|
|
wrap(globalThis, 'structuredClone', 'structuredClone')
|
|
wrap(Buffer, 'from', 'Buffer.from', (args) => args[0]?.length)
|
|
const hashPrototype = Object.getPrototypeOf(
|
|
process.getBuiltinModule('crypto').createHash('sha256')
|
|
)
|
|
wrap(hashPrototype, 'update', 'hash.update', (args) => args[0]?.length)
|
|
const fs = process.getBuiltinModule('fs')
|
|
for (const name of ['existsSync', 'accessSync', 'writeFileSync', 'fsyncSync', 'renameSync']) {
|
|
wrap(fs, name, name)
|
|
}
|
|
const timerGaps = []
|
|
let previous = performance.now()
|
|
const timer = setInterval(() => {
|
|
const now = performance.now()
|
|
const gap = now - previous - 25
|
|
previous = now
|
|
if (gap > 20 && timerGaps.length < 2000) {
|
|
timerGaps.push({ epoch: Date.now(), gapMs: gap })
|
|
}
|
|
}, 25)
|
|
timer.unref()
|
|
globalThis.__orcaMainBlockingProbe = {
|
|
stop() {
|
|
clearInterval(timer)
|
|
for (const restore of cleanup.toReversed()) {
|
|
restore()
|
|
}
|
|
delete globalThis.__orcaMainBlockingProbe
|
|
return { startedAt, endedAt: Date.now(), events, timerGaps }
|
|
}
|
|
}
|
|
return { startedAt }
|
|
}
|
|
|
|
export function installRendererIpcProbe() {
|
|
if (window.__orcaIpcTimingProbe) {
|
|
throw new Error('Renderer IPC probe already exists')
|
|
}
|
|
const requests = []
|
|
const keys = []
|
|
let pending = false
|
|
let stopped = false
|
|
const timer = setInterval(async () => {
|
|
if (pending || stopped) {
|
|
return
|
|
}
|
|
pending = true
|
|
const start = performance.now()
|
|
const epoch = Date.now()
|
|
try {
|
|
await window.api.app.getIdentity()
|
|
if (requests.length < 2000) {
|
|
requests.push({ epoch, durationMs: performance.now() - start })
|
|
}
|
|
} catch (error) {
|
|
if (requests.length < 2000) {
|
|
requests.push({ epoch, durationMs: performance.now() - start, failed: String(error) })
|
|
}
|
|
} finally {
|
|
pending = false
|
|
}
|
|
}, 100)
|
|
const keydown = (event) => {
|
|
if (keys.length < 1000) {
|
|
keys.push({
|
|
epoch: Date.now(),
|
|
queueMs: performance.now() - event.timeStamp,
|
|
terminal: !!event.target?.closest?.('.xterm'),
|
|
trusted: event.isTrusted
|
|
})
|
|
}
|
|
}
|
|
document.addEventListener('keydown', keydown, true)
|
|
window.__orcaIpcTimingProbe = {
|
|
stop() {
|
|
stopped = true
|
|
clearInterval(timer)
|
|
document.removeEventListener('keydown', keydown, true)
|
|
delete window.__orcaIpcTimingProbe
|
|
return { requests, keys }
|
|
}
|
|
}
|
|
}
|