76 lines
3.1 KiB
Markdown
76 lines
3.1 KiB
Markdown
# BlastRadius
|
|
|
|
Detects when copies of the same function in different codebases drift apart,
|
|
and names which copies disagree. It is used in Hitt Hosting products.
|
|
|
|
## What it does and why
|
|
|
|
When the same logic lives in more than one place (two services, a vendored
|
|
copy, a CommonJS and a TypeScript version), a fix tends to land in only one of
|
|
them. Each copy looks fine on its own. Only a comparison shows that they no
|
|
longer agree, and by then the unfixed copy has been running for a while.
|
|
|
|
BlastRadius takes the source text of each copy and the name of the function to
|
|
compare. It extracts the function by brace matching, strips comments and
|
|
normalises whitespace, fingerprints the result, and groups the copies by
|
|
fingerprint. More than one group means drift. The largest group comes first,
|
|
which usually tells you which side has the fix and which side missed it.
|
|
|
|
Comments and formatting are ignored because copies may be documented
|
|
differently but must behave the same. Comparing raw text would fire on a typo
|
|
fix and train people to update the pinned value without reading, which brings
|
|
back the drift the check exists to catch. The extraction is not a parser, so
|
|
it works across CommonJS, ESM and TypeScript without a toolchain per dialect.
|
|
|
|
**A copy where the function cannot be found is a failure, not a pass.** It may
|
|
have been renamed, split or deleted, and a guard that cannot find its subject
|
|
is not passing. This holds even when only one copy was located.
|
|
|
|
Fewer than two located copies, with none missing, is reported as not
|
|
assessed: one copy cannot disagree with itself, and calling that "in sync"
|
|
would let a deleted sibling read as a clean result. An in-sync pass cites the
|
|
copies it compared, because a pass that cannot name what it compared is only
|
|
asserted.
|
|
|
|
The library is pure and has no filesystem access; you read the files. Reports
|
|
use the `@extant2000/evidence-record` format, and every copy is cited by its
|
|
path when you give one, or by its label.
|
|
|
|
## Usage
|
|
|
|
```ts
|
|
import { compareImplementations, toReport, formatDrift } from '@extant2000/blast-radius'
|
|
|
|
const withCheck = 'function canSend(c) {\n if (!c.postalAddress) return false\n return true\n}'
|
|
const withoutCheck = 'function canSend(c) {\n return true\n}'
|
|
|
|
const result = compareImplementations([
|
|
{ label: 'web', path: 'web/lib/send.ts', source: withCheck },
|
|
{ label: 'worker', path: 'worker/lib/send.ts', source: withCheck },
|
|
{ label: 'cli', path: 'cli/lib/send.ts', source: withoutCheck },
|
|
], 'canSend')
|
|
|
|
result.inSync // false
|
|
result.groups.map(g => g.labels) // [['web', 'worker'], ['cli']]
|
|
result.missing // []
|
|
|
|
const report = toReport(result) // evidence-record report
|
|
console.log(formatDrift(result, { evidence: 'full' }))
|
|
```
|
|
|
|
The report has one critical finding, "canSend has drifted across 2 variants",
|
|
citing all three files with the variant each belongs to (2 of 3 and 1 of 3).
|
|
|
|
`extractFunction()`, `codeOnly()` and `fingerprint()` (FNV-1a) are exported
|
|
for use on their own.
|
|
|
|
## Install
|
|
|
|
```
|
|
npm install @extant2000/blast-radius
|
|
```
|
|
|
|
## License
|
|
|
|
MIT. See [LICENSE](LICENSE).
|