How to Trace Upstream and Downstream Reach in Archify Viewer: A Step-by-Step Guide
Archify's interactive viewer lets you explore the authored graph of a system by tracing upstream (all ancestors) or downstream (all descendants) of any selected node.
Tracing upstream and downstream reachability is essential for understanding dependency flow in complex software systems. The Archify viewer provides built-in controls to instantly highlight all reachable nodes from any selected component. This article explains how the feature works according to the tt-a1i/archify source code, with practical examples you can use immediately.
Understanding Upstream vs. Downstream Reachability
- Upstream reachability — All ancestors of a node: components that influence or are required by your selected node.
- Downstream reachability — All descendants of a node: components that depend on or are influenced by your selected node.
These directions map directly to the direction parameter used throughout the reachability system in archify/assets/template.html.
Using the UI Controls to Trace Reachability
When you select any node in the Archify viewer, two toolbar buttons appear automatically:
| Button ID | Label | Function |
|---|---|---|
#btn-reach-upstream |
Upstream | Shows count of reachable ancestors; toggles upstream tracing |
#btn-reach-downstream |
Downstream | Shows count of reachable descendants; toggles downstream tracing |
Clicking either button activates the corresponding reachability mode. Clicking the same button again clears the view and returns to normal display.
The button state is synchronized with:
- ARIA attributes (
aria-pressed) for accessibility - URL hash (
#focus=…&reach=upstream) for bookmarkable views - Status line showing the count of reachable nodes
The Core Algorithm: computeReachability and reachabilityFor
The reachability calculation is implemented in archify/assets/template.html through two key functions.
reachabilityFor(id, direction)
This entry point forwards requests to the core algorithm:
// From archify/assets/template.html (line 6999-7001)
function reachabilityFor(id, direction) {
return computeReachability(id, direction);
}
Valid direction values are 'upstream' or 'downstream'.
computeReachability(startId, direction)
This function performs a breadth-first search over the JSON IR relationships:
// Simplified structure based on archify/assets/template.html (lines 6950-6979)
function computeReachability(startId, direction) {
const queue = [{ id: startId, depth: 0 }];
const visited = new Map(); // id → depth
const edgeKeys = new Set();
while (queue.length > 0) {
const { id, depth } = queue.shift();
if (visited.has(id)) continue;
visited.set(id, depth);
// Get neighbors based on direction
const neighbors = direction === 'upstream'
? getPredecessors(id) // who points TO this node
: getSuccessors(id); // who this node points TO
for (const neighbor of neighbors) {
if (!visited.has(neighbor.id)) {
queue.push({ id: neighbor.id, depth: depth + 1 });
edgeKeys.add(neighbor.edgeKey);
}
}
}
return {
direction,
originId: startId,
nodeIds: Array.from(visited.keys()),
edgeKeys: Array.from(edgeKeys),
depths: Object.fromEntries(visited),
maxDepth: Math.max(...visited.values())
};
}
The result object contains everything needed to visualize the reachable subgraph.
Applying Reachability: The applyReachability Function
When a toolbar button is pressed, applyReachability(direction, options) executes the following steps (lines 7050-7072):
- Validates that a node is currently selected
- Obtains the reachability snapshot via
reachabilityFor - Clears any prior reachability highlights
- Marks matching nodes and edges with data attributes:
data-reach-match— indicates this element is in the reachable setdata-reach-depth— the distance from the origin nodedata-reach-origin— the starting node ID
URL Synchronization and Deep Linking
The viewer automatically updates the URL hash (lines 7089-7110):
// Example generated URL
https://example.com/viewer#focus=service-api&reach=upstream
This enables sharing specific reachability views via copy-paste or bookmark.
Auto-Revealing the Subgraph
Finally, if Archify.view.reveal is available, the diagram pans and zooms to show only the reachable nodes (lines 7110-7111):
if (Archify.view && Archify.view.reveal) {
Archify.view.reveal(result.nodeIds);
}
Clearing the Reachability View
To reset the display, the viewer calls clearReachability (lines 7026-7032):
function clearReachability() {
// Remove all reachability data attributes
document.querySelectorAll('[data-reach-match]')
.forEach(el => {
delete el.dataset.reachMatch;
delete el.dataset.reachDepth;
delete el.dataset.reachOrigin;
});
// Reset toolbar state and URL hash
updateToolbarState(null);
updateUrlHash({ focus: currentFocusId }); // removes 'reach' param
}
Clicking an active upstream/downstream button triggers this automatically.
Manual Invocation from Browser Console
For debugging or custom integrations, you can invoke reachability directly:
// Trace upstream from a specific node
const upstream = reachabilityFor('service-api', 'upstream');
console.log('Upstream nodes:', upstream.nodeIds);
console.log('Max depth:', upstream.maxDepth);
// Trace downstream
const downstream = reachabilityFor('service-api', 'downstream');
// Apply manually to update the visual state
applyReachability('upstream', { reveal: true });
Relevant Source Files in tt-a1i/archify
| File | Purpose |
|---|---|
archify/assets/template.html |
Core implementation: computeReachability, reachabilityFor, applyReachability, UI wiring, URL handling |
examples/web-app.html |
Demonstration page with upstream/downstream buttons in a live diagram |
archify/test/authored-reachability.test.mjs |
Automated test suite validating reachability calculations against known graph structures |
Summary
- Trace upstream reachability in Archify by clicking the
#btn-reach-upstreamtoolbar button or callingapplyReachability('upstream') - Trace downstream reachability by clicking
#btn-reach-downstreamor callingapplyReachability('downstream') - The BFS-based algorithm in
computeReachabilitytraverses the JSON IR, recording depths and edge keys - Data attributes (
data-reach-match,data-reach-depth,data-reach-origin) drive the visual highlighting - URL hash synchronization makes reachability views bookmarkable and shareable
- Click the active button again — or call
clearReachability()— to reset the view
Frequently Asked Questions
How do I programmatically check if a node has upstream dependencies without changing the view?
Call reachabilityFor(nodeId, 'upstream') directly. This returns the full result object including nodeIds, depths, and maxDepth without triggering any visual updates.
Can I trace reachability for multiple nodes simultaneously?
The current implementation in archify/assets/template.html focuses on a single originId. For multi-node reachability, you would need to call reachabilityFor for each node and merge the results manually, or extend the computeReachability function to accept an array of start IDs.
What happens if the graph contains cycles?
The computeReachability function uses a visited Map to track seen nodes, so cycles are automatically handled — the BFS will not revisit nodes, preventing infinite loops while still correctly recording the shortest path depth to each reachable node.
Is there a way to filter reachability by relationship type?
The current implementation traverses all relationships in the JSON IR. To filter by type, you would modify the neighbor iteration logic inside computeReachability (lines 6950-6979) to check edge properties before adding to the queue.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →