# How to Trace Upstream and Downstream Reach in Archify

> Learn to trace upstream and downstream reach in Archify. Discover how Archify uses BFS and traversal paths to visualize code relationships effectively.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: how-to-guide
- Published: 2026-09-11

---

**Archify traces upstream and downstream reach by performing a breadth-first search (BFS) through SVG edge relationships, using [`viewer/focus.js`](https://github.com/tt-a1i/archify/blob/main/viewer/focus.js) to compute traversal paths and [`viewer/route-probe.js`](https://github.com/tt-a1i/archify/blob/main/viewer/route-probe.js) for low-level graph utilities, then applies visual indicators via data attributes on DOM elements.**

Archify is an open-source diagram visualization tool that enables developers to analyze node dependencies through bidirectional reachability tracing. Understanding how to trace upstream and downstream reach in Archify allows you to identify all ancestors or descendants of a selected node within directed graphs. The implementation relies on two core JavaScript modules that parse SVG edge data and execute deterministic graph traversal algorithms.

## Core Architecture for Reachability Tracing

### viewer/focus.js - Primary Reachability Controller

Located in [`viewer/focus.js`](https://github.com/tt-a1i/archify/blob/main/viewer/focus.js), this module contains the main entry points for reachability analysis. The `computeReachability(originId, direction, relationships)` function initiates a breadth-first traversal from a specified origin node, while `reachabilityFor(id, direction)` provides a standalone computation interface without UI side effects. The `applyReachability(direction, options)` method handles the visual layer, painting traversal results onto the SVG canvas and updating interface counters.

### viewer/route-probe.js - Graph Algorithms

The [`viewer/route-probe.js`](https://github.com/tt-a1i/archify/blob/main/viewer/route-probe.js) module supplies the underlying graph theory implementations reused by the focus controller. Key exports include `reachableFrom(source)`, which returns a Boolean map of all downstream nodes; `hopDistancesFrom(source)`, which calculates hop counts from the origin; and `shortestDirectedPath(source, target)`, which computes optimal routes between specific nodes.

## The Reachability Traversal Process

### Relationship Discovery

The tracing begins when `reachabilityRelationships()` scans the DOM for all SVG edges marked with `[data-edge-from]` and `[data-edge-to]` attributes. This generates a flat array of relationship objects containing `from`, `to`, and `key` properties that define the directed graph structure.

### BFS Execution Logic

The `computeReachability` function implements a queue-based breadth-first search. It initializes a `depths` Map with the origin node set to depth `0`, then processes nodes iteratively:

- For **downstream** traversal, the algorithm follows edges where `relationship.from === current`
- For **upstream** traversal, the algorithm follows edges where `relationship.to === current`

Each discovered node receives a depth value of `depths[current] + 1`, and the process continues until the queue empties.

### Result Data Structure

Upon completion, the function returns a structured object:

```javascript
{
  direction,           // 'upstream' or 'downstream'
  originId,           // Starting node identifier
  nodeIds: order,     // BFS-ordered array of visited IDs
  edgeKeys,           // Array of traversed edge identifiers
  depths,             // Map of node ID to hop count
  maxDepth            // Integer representing longest path
}

```

### Visual Application and State Management

The `applyReachability` method consumes this result object to update the DOM. Nodes within the reachability set receive `data-reach-match` and `data-reach-depth` attributes, while the origin node gets `data-reach-origin`. Corresponding edges are tagged with `data-reach-match` attributes. The UI counters update via `Math.max(0, result.nodeIds.length - 1)` to exclude the origin from dependency counts.

The reachability state persists in the URL hash (e.g., `#focus=nodeId&reach=upstream`) and integrates with [`viewer/export.js`](https://github.com/tt-a1i/archify/blob/main/viewer/export.js) to include traversal context in exported artifacts.

## Practical Implementation Examples

### Trace Downstream Reach from Current Selection

To visualize all descendants of the currently selected node:

```javascript
// Retrieve active selection from Archify focus state
const nodeId = Archify.focus.activeIds[0];

// Apply downstream reachability visualization
Archify.focus.applyReachability('downstream');

// Capture shareable snapshot for URL persistence or export
const snapshot = Archify.focus.reachabilitySnapshot();
console.log(snapshot);
// Outputs: { direction: 'downstream', origin: {...}, nodeIds: [...], depths: {...}, maxDepth: n, edges: [...] }

```

### Compute Upstream Reach Programmatically

For headless computation without DOM updates:

```javascript
const reach = Archify.focus.reachabilityFor('myNodeId', 'upstream');

console.log(reach.nodeIds);  // Array of ancestor IDs
console.log(reach.depths);   // Map of hop distances from origin

```

### Export Diagrams with Reach Overlay

To generate an SVG export that includes the reach visualization:

```javascript
Archify.focus.applyReachability('upstream');
Archify.exportMenu.exportSvg({ 
  reachSnapshot: Archify.focus.reachabilitySnapshot() 
})
.then(blob => {
  // Process exported SVG file containing visual reach indicators
});

```

### Access Low-Level Graph Utilities

Import functions directly from [`viewer/route-probe.js`](https://github.com/tt-a1i/archify/blob/main/viewer/route-probe.js) for custom analysis:

```javascript
import { reachableFrom, hopDistancesFrom } from './viewer/route-probe.js';

const downstreamMap = reachableFrom('myNodeId');  // { nodeId: true, ... }
const hopCounts = hopDistancesFrom('myNodeId');    // { nodeId: 0, ... }

```

## Summary

- **Bidirectional BFS**: Archify uses breadth-first search in [`viewer/focus.js`](https://github.com/tt-a1i/archify/blob/main/viewer/focus.js) to trace both upstream (incoming edges) and downstream (outgoing edges) relationships from any origin node
- **Deterministic results**: The `computeReachability` function returns structured data including node IDs, edge keys, and hop depths for every traversal
- **Visual integration**: The `applyReachability` method synchronizes graph state with DOM attributes (`data-reach-match`, `data-reach-depth`) and updates UI counters excluding the origin node
- **Export support**: Reachability snapshots serialize via `reachabilitySnapshot()` for URL persistence (`#focus=...&reach=upstream`) and integration with [`viewer/export.js`](https://github.com/tt-a1i/archify/blob/main/viewer/export.js)
- **Low-level access**: [`viewer/route-probe.js`](https://github.com/tt-a1i/archify/blob/main/viewer/route-probe.js) provides reusable graph algorithms including `reachableFrom` and `hopDistancesFrom` for custom tooling and testing scenarios

## Frequently Asked Questions

### How does Archify determine which nodes are reachable upstream versus downstream?

Archify distinguishes direction by inspecting edge relationships during BFS queue processing. For downstream reach, it follows edges where the current node matches the `from` property; for upstream reach, it follows edges where the current node matches the `to` property. This logic resides in the `computeReachability` function within [`viewer/focus.js`](https://github.com/tt-a1i/archify/blob/main/viewer/focus.js).

### Can I export the reachability analysis along with the diagram?

Yes. After calling `Archify.focus.applyReachability()`, invoke `Archify.focus.reachabilitySnapshot()` to capture the current state, then pass this object to `Archify.exportMenu.exportSvg({ reachSnapshot: ... })`. The exported SVG includes visual reach indicators and traversal metadata according to the implementation in [`viewer/export.js`](https://github.com/tt-a1i/archify/blob/main/viewer/export.js).

### What is the maximum depth limit for reachability tracing?

The analysis uses standard BFS without artificial depth limits, constrained only by graph size and JavaScript execution context. The `maxDepth` property in the result object indicates the longest hop count found during traversal, representing the furthest dependency layer from the origin node.

### How can I compute reachability without updating the UI?

Use the `reachabilityFor(id, direction)` method in [`viewer/focus.js`](https://github.com/tt-a1i/archify/blob/main/viewer/focus.js) instead of `applyReachability()`. This returns the raw traversal data—including node IDs, depths, and edge keys—without modifying DOM attributes or triggering visual updates, suitable for background analysis or automated testing scenarios.