• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

graphty-org / graphty-element / 20390753610

20 Dec 2025 06:53AM UTC coverage: 82.423% (-1.2%) from 83.666%
20390753610

push

github

apowers313
Merge branch 'master' of https://github.com/graphty-org/graphty-element

5162 of 6088 branches covered (84.79%)

Branch coverage included in aggregate %.

24775 of 30233 relevant lines covered (81.95%)

6480.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.2
/src/algorithms/BFSAlgorithm.ts
1
import {breadthFirstSearch} from "@graphty/algorithms";
3✔
2
import {z} from "zod/v4";
3✔
3

4
import {defineOptions, type OptionsSchema as ZodOptionsSchema, type SuggestedStylesConfig} from "../config";
3✔
5
import type {Graph} from "../Graph";
6
import {Algorithm} from "./Algorithm";
3✔
7
import {type OptionsSchema} from "./types/OptionSchema";
8
import {toAlgorithmGraph} from "./utils/graphConverter";
3✔
9

10
/**
11
 * Zod-based options schema for BFS algorithm
12
 */
13
export const bfsOptionsSchema = defineOptions({
3✔
14
    source: {
3✔
15
        schema: z.union([z.string(), z.number()]).nullable().default(null),
3✔
16
        meta: {
3✔
17
            label: "Source Node",
3✔
18
            description: "Starting node for BFS traversal (uses first node if not set)",
3✔
19
        },
3✔
20
    },
3✔
21
    targetNode: {
3✔
22
        schema: z.union([z.string(), z.number()]).nullable().default(null),
3✔
23
        meta: {
3✔
24
            label: "Target Node",
3✔
25
            description: "Target node for early termination (optional - searches all nodes if not set)",
3✔
26
            advanced: true,
3✔
27
        },
3✔
28
    },
3✔
29
});
3✔
30

31
/**
32
 * Options for BFS algorithm
33
 */
34
export interface BFSOptions extends Record<string, unknown> {
35
    /** Starting node for traversal (defaults to first node if not provided) */
36
    source: number | string | null;
37
    /** Target node for early termination (optional) */
38
    targetNode: number | string | null;
39
}
40

41
export class BFSAlgorithm extends Algorithm<BFSOptions> {
3✔
42
    static namespace = "graphty";
17✔
43
    static type = "bfs";
17✔
44

45
    static zodOptionsSchema: ZodOptionsSchema = bfsOptionsSchema;
17✔
46

47
    /**
48
     * Options schema for BFS algorithm
49
     */
50
    static optionsSchema: OptionsSchema = {
17✔
51
        source: {
17✔
52
            type: "nodeId",
17✔
53
            default: null,
17✔
54
            label: "Source Node",
17✔
55
            description: "Starting node for BFS traversal (uses first node if not set)",
17✔
56
            required: false,
17✔
57
        },
17✔
58
        targetNode: {
17✔
59
            type: "nodeId",
17✔
60
            default: null,
17✔
61
            label: "Target Node",
17✔
62
            description: "Target node for early termination (optional - searches all nodes if not set)",
17✔
63
            required: false,
17✔
64
            advanced: true,
17✔
65
        },
17✔
66
    };
17✔
67

68
    /**
69
     * Legacy options set via configure() for backward compatibility
70
     */
71
    private legacyOptions: {source: number | string} | null = null;
17✔
72

73
    static suggestedStyles = (): SuggestedStylesConfig => ({
108✔
74
        layers: [
10✔
75
            {
10✔
76
                node: {
10✔
77
                    selector: "",
10✔
78
                    style: {enabled: true},
10✔
79
                    calculatedStyle: {
10✔
80
                        inputs: ["algorithmResults.graphty.bfs.levelPct"],
10✔
81
                        output: "style.texture.color",
10✔
82
                        expr: "{ return StyleHelpers.color.sequential.viridis(arguments[0] ?? 0) }",
10✔
83
                    },
10✔
84
                },
10✔
85
                metadata: {
10✔
86
                    name: "BFS - Level Colors",
10✔
87
                    description: "Colors nodes by BFS level from source (viridis gradient)",
10✔
88
                },
10✔
89
            },
10✔
90
        ],
10✔
91
        description: "Visualizes breadth-first traversal levels from source node",
10✔
92
        category: "hierarchy",
10✔
93
    });
10✔
94

95
    /**
96
     * Configure the algorithm with source node
97
     *
98
     * @deprecated Use constructor options instead. This method is kept for backward compatibility.
99
     */
100
    configure(options: {source: number | string}): this {
7✔
101
        this.legacyOptions = options;
8✔
102
        return this;
8✔
103
    }
8✔
104

105
    // eslint-disable-next-line @typescript-eslint/require-await
106
    async run(): Promise<void> {
7✔
107
        const g = this.graph;
13✔
108
        const dm = g.getDataManager();
13✔
109
        const nodes = Array.from(dm.nodes.keys());
13✔
110
        const n = nodes.length;
13✔
111

112
        if (n === 0) {
13✔
113
            return;
1✔
114
        }
1✔
115

116
        // Get source from legacy options, schema options, or use first node as default
117
        // Legacy configure() takes precedence for backward compatibility
118
        const source = this.legacyOptions?.source ?? this._schemaOptions.source ?? nodes[0];
13✔
119
        const targetNode = this._schemaOptions.targetNode ?? undefined;
13✔
120

121
        // Convert to @graphty/algorithms format
122
        // Using directed=false so the converter adds reverse edges for undirected traversal
123
        const graphData = toAlgorithmGraph(g as unknown as Graph, {directed: false});
13✔
124

125
        // Check if source exists
126
        if (!graphData.hasNode(source)) {
13!
127
            // Source not in graph - nothing to do
128
            return;
×
129
        }
✔
130

131
        // Track levels and visit order manually since breadthFirstSearch doesn't return them directly
132
        const levels = new Map<string | number, number>();
12✔
133
        const visitOrders = new Map<string | number, number>();
12✔
134
        let visitOrder = 0;
12✔
135
        let maxLevel = 0;
12✔
136
        let foundTarget = false;
12✔
137

138
        // Use visitCallback to track levels
139
        breadthFirstSearch(graphData, source, {
12✔
140
            targetNode,
12✔
141
            visitCallback: (node, level) => {
12✔
142
                levels.set(node, level);
569✔
143
                visitOrders.set(node, visitOrder);
569✔
144
                visitOrder++;
569✔
145
                if (level > maxLevel) {
569✔
146
                    maxLevel = level;
31✔
147
                }
31✔
148

149
                if (targetNode !== undefined && node === targetNode) {
569!
150
                    foundTarget = true;
×
151
                }
×
152
            },
569✔
153
        });
12✔
154

155
        // Store results on nodes
156
        for (const nodeId of nodes) {
13✔
157
            const level = levels.get(nodeId);
588✔
158
            const order = visitOrders.get(nodeId);
588✔
159

160
            if (level !== undefined) {
588✔
161
                this.addNodeResult(nodeId, "level", level);
569✔
162
                // Normalize level to percentage
163
                const levelPct = maxLevel > 0 ? level / maxLevel : 0;
569✔
164
                this.addNodeResult(nodeId, "levelPct", levelPct);
569✔
165
            }
569✔
166

167
            if (order !== undefined) {
588✔
168
                this.addNodeResult(nodeId, "visitOrder", order);
569✔
169
            }
569✔
170
        }
588✔
171

172
        // Store graph-level results
173
        this.addGraphResult("maxLevel", maxLevel);
12✔
174
        this.addGraphResult("visitedCount", levels.size);
12✔
175
        if (targetNode !== undefined) {
13!
176
            this.addGraphResult("targetFound", foundTarget);
×
177
        }
×
178
    }
13✔
179
}
7✔
180

181
// Auto-register this algorithm when the module is imported
182
Algorithm.register(BFSAlgorithm);
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc