How to Trace Upstream and Downstream Reachability from a Focused Node in Archify
Archify exposes a focus.reachability() API that returns all ancestor (upstream) or descendant (downstream) nodes reachable from any focused node through the diagram's directed edges.
Archify treats every diagram as a directed graph where nodes represent visual elements and edges represent relationships. When you focus a node, you can programmatically query which other nodes are reachable in either direction—essential for understanding dependencies, call chains, or data flow. This guide covers the core API, the underlying graph traversal algorithm, and practical code patterns based on the official source code in tt-a1i/archify.
Core Reachability API
Archify's reachability system centers on three interconnected components: the focus object, the relationship map, and the computation engine.
The Focus Object
Archify.focus holds state for the currently selected node and exposes helper methods. In examples/web-app.html at line 5967, the focus object provides:
reachabilitySnapshot()– Returns a serializable state object for deep-linkingreachability(direction?)– Initiates reachability computation and UI highlighting
The focus object is automatically updated when a user clicks any node, or you can set it programmatically.
The Relationship Map
Before computing reachability, Archify builds a directed adjacency map. The function reachabilityRelationships() (line 7003 in examples/web-app.html) constructs:
// Returns: { nodeId: [adjacentNodeId, ...], ... }
This map is cached per render and represents the graph structure derived from the diagram's connections.
The Computation Engine
The core algorithm lives in computeReachability(originId, direction, relationships) at line 7016. It performs depth-first search with directional edge following:
- Upstream: Follows incoming edges using a reverse-lookup map
- Downstream: Follows outgoing edges from the standard adjacency list
The implementation is pure—no side effects—making it safe to run repeatedly without mutating diagram state.
How to Compute Reachability Programmatically
Triggering Upstream Reachability
To find all ancestor nodes from a focused node:
// Set focus on a specific node
Archify.focus = { id: 'order-123' };
// Compute upstream reachability
const upstream = Archify.focus.reachability('upstream');
// Returns: { direction: 'upstream', nodeIds: ['customer-789', 'account-456', ...] }
// Highlight reachable nodes in the UI
Archify.view.reveal(upstream.nodeIds, {
includeNeighbors: false,
reason: 'reachability'
});
The reachability() method internally calls reachabilityFor(id, 'upstream') as implemented at line 7113-7114 of examples/web-app.html.
Triggering Downstream Reachability
Swap the direction parameter:
const downstream = Archify.focus.reachability('downstream');
// Returns: { direction: 'downstream', nodeIds: ['shipment-456', 'invoice-789', ...] }
Archify.view.reveal(downstream.nodeIds, {
includeNeighbors: false,
reason: 'reachability'
});
Toggle Behavior
The UI implements toggle semantics: calling reachability('upstream') when upstream mode is already active collapses the view. This matches the button behavior at line 6988 where reachabilityMode is checked and cleared if unchanged.
Creating Deep Links with Snapshots
The snapshot API enables bookmarkable views of reachability explorations:
// After computing reachability
const snap = Archify.focus.reachabilitySnapshot();
// Returns: { direction: 'downstream', nodeIds: ['order-123', 'shipment-456', 'invoice-789'] }
// Build a shareable URL
const url = `${location.origin}${location.pathname}#focus=${encodeURIComponent(Archify.focus.id)}&reach=${snap.direction}`;
// Result: https://example.com/diagram#focus=order-123&reach=downstream
The snapshot implementation at line 7228-7233 in examples/web-app.html serializes only the essential state—direction and node IDs—keeping URLs compact while preserving full context.
Wiring UI Controls
For interactive applications, bind reachability to button controls:
// Upstream button handler
document.getElementById('btn-reach-upstream').addEventListener('click', () => {
Archify.focus.reachability('upstream');
});
// Downstream button handler
document.getElementById('btn-reach-downstream').addEventListener('click', () => {
Archify.focus.reachability('downstream');
});
The button markup at line 4863-4867 in examples/web-app.html includes ARIA labels for accessibility:
<button id="btn-reach-upstream" aria-label="Show upstream dependencies">← Upstream</button>
<button id="btn-reach-downstream" aria-label="Show downstream dependencies">Downstream →</button>
Under the Hood: archify/assets/template.html
The base template at archify/assets/template.html contains a shared computeReachability implementation (line 6949) used by all rendered diagrams. This ensures consistent behavior across:
- Static exports
- Interactive web views
- Embedded diagrams
The template defines the core traversal without UI dependencies, allowing custom interfaces to leverage the same algorithm.
Testing Reachability Logic
The test suite at archify/test/authored-reachability.test.mjs validates the algorithm against known graph fixtures. Key test patterns include:
- Diamond graphs: Confirming upstream from a bottom node returns both top ancestors
- Cycles: Verifying termination despite circular references
- Isolated nodes: Ensuring empty results for disconnected elements
Running these tests guarantees that reachability behaves predictably across diagram types.
Summary
Archify.focus.reachability(direction)– Primary API for upstream/downstream queries; returns node IDs reachable from the focused nodecomputeReachability(originId, direction, relationships)– Pure DFS implementation inexamples/web-app.htmlline 7016reachabilitySnapshot()– Serializes state for deep-linking at line 7228reachabilityRelationships()– Builds the directed adjacency map at line 7003- Toggle behavior – Calling the same direction twice collapses the highlight
- Performance – Pure algorithm on cached relationship map enables instant browser-side computation
Frequently Asked Questions
How does Archify handle cycles in the graph when computing reachability?
Archify's computeReachability prevents infinite loops by tracking visited nodes. During DFS traversal, each node ID is added to an internal visited Set before recursion. Duplicate encounters skip processing. This is validated in archify/test/authored-reachability.test.mjs against cyclic graph fixtures.
Can I compute reachability without triggering the UI highlight?
Yes—call reachabilityFor(id, direction) directly instead of Archify.focus.reachability(). The wrapper at line 7113 returns raw results without invoking Archify.view.reveal(). Use this for background analysis or custom rendering pipelines.
What's the difference between reachability() and reachabilitySnapshot()?
reachability(direction) actively computes and visualizes reachability, returning the result and updating the diagram highlight. reachabilitySnapshot() returns the last computed state without recalculation—useful for URL generation or state persistence. The snapshot contains the same structure ({ direction, nodeIds }) but reads from cached focus state.
Where is the reachability algorithm defined if I need to customize it?
The canonical implementation lives in two places: archify/assets/template.html at line 6949 (used by all rendered diagrams) and examples/web-app.html at line 7016 (full UI example). Both use the same computeReachability signature. Override in template.html for global changes, or shadow in specific examples for localized behavior.
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 →