mirror of
https://github.com/stablyai/orca.git
synced 2026-09-24 16:02:41 +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.
48 lines
1.5 KiB
TypeScript
48 lines
1.5 KiB
TypeScript
import { expect, it, vi } from 'vitest'
|
|
import { parseGitHistoryLog } from './git-history-log-parser'
|
|
|
|
it('keeps a multiline commit body intact without materializing every message line', () => {
|
|
const message = `subject\n\n${'body line\n'.repeat(10000)}`
|
|
const record = [
|
|
'a'.repeat(40),
|
|
'Author',
|
|
'email',
|
|
'1700000000',
|
|
'1700000000',
|
|
'',
|
|
'',
|
|
'',
|
|
message
|
|
].join('\n')
|
|
// Method-shaped type: a call-signature capture would reject `split`'s splitter-object overload.
|
|
const original: { split(separator: unknown, limit?: number): string[] }['split'] =
|
|
String.prototype.split
|
|
let allocatedFields = 0
|
|
const spy = vi.spyOn(String.prototype, 'split').mockImplementation(function (
|
|
this: string,
|
|
separator: unknown,
|
|
limit?: number
|
|
) {
|
|
const result = original.call(this, separator, limit)
|
|
if (separator === '\n' && String(this).includes('body line')) {
|
|
allocatedFields += result.length
|
|
}
|
|
return result
|
|
})
|
|
let result: ReturnType<typeof parseGitHistoryLog>
|
|
try {
|
|
result = parseGitHistoryLog(`${record}\n\0`)
|
|
} finally {
|
|
spy.mockRestore()
|
|
}
|
|
expect(result![0].message).toBe(message)
|
|
expect(result![0].subject).toBe('subject')
|
|
expect(allocatedFields).toBe(0)
|
|
})
|
|
|
|
it('preserves incomplete header and empty body behavior', () => {
|
|
for (const suffix of ['', '\nAuthor', '\nAuthor\nemail\n0\n0\n\n\n']) {
|
|
expect(parseGitHistoryLog(`${'a'.repeat(40)}${suffix}\0`)[0].message).toBe('')
|
|
}
|
|
})
|