Skip to main content

@ossl-dev/differens-tiers

The parsing pipeline. A content router picks a tier by file extension and magic bytes. The tier adapter parses the file into a Node tree for @ossl-dev/differens-core. If a tier fails to parse, or its diff falls back, the pipeline drops to the next lower tier. Raw line diff is the floor. Install:

Tiers

Exports

classifyFile

Extension-based routing, with a binary sniff (NUL bytes or failed UTF-8 decode) on top. Markdown goes to Tier.Raw, not prose: lines are structure in docs. CSV, TSV, GraphQL, and Dockerfiles are line-structured the same way, so they route to Tier.Raw too. Environment files (.env, .env.local) route to Tier.Data: KEY=VALUE lines parse as the TOML subset, so a changed value reports with its key path.

diffWithTier

Behavior worth knowing:
  • Entirely new or removed files become a single Insert/Delete of a kind: "file" node. That is one fact, not a tree diff.
  • Identical sources short-circuit with no changes.
  • A tier result is kept only if it parses and produces a usable tree diff; the core’s fallback verdict propagates instead of being reported as “no changes”.
  • On any failure the pipeline falls back to raw lines, with fallback: "lines".

LanguageExtractor

Each code language is a LanguageExtractor: a few dozen match arms that map tree-sitter CST node types to canonical concepts. It is not a parser.
getExtractors() reports them as ExtractorInfo { language, level: "L6" | "L5", extensions }, where L6 extractors have semantic mappings and L5 are generic. Sixteen languages register extractors: TypeScript/JavaScript, Python, Rust, Go, C, C++, Java, Ruby, PHP, Swift, Kotlin, C#, Scala, Lua, and shell. A grammar registered without an extractor reports as L5. parseCode keeps a content-addressed cache (64-entry LRU, keyed by content hash and extension), so the same file content reappearing in one run parses once. The cache is per-run and per-process by design; worker children build their own.

BinaryDiffPlugin

T0 format plugins upgrade binary files from the byte-delta report to a real diff:
Plugins register in-process with registerBinaryPlugin. The first non-declining answer wins; a throwing plugin is skipped; the default byte-delta report always remains as the floor. Dynamic plugin loading from disk is deliberately not included: that belongs to an ecosystem layer, not the diff pipeline.

Usage