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

graphty-org / graphty-element / 20514590651

26 Dec 2025 02:37AM UTC coverage: 70.559% (-0.3%) from 70.836%
20514590651

push

github

apowers313
ci: fix npm ci

9591 of 13363 branches covered (71.77%)

Branch coverage included in aggregate %.

25136 of 35854 relevant lines covered (70.11%)

6233.71 hits per line

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

98.03
/src/data/ErrorAggregator.ts
1
/**
2
 * Represents a single error that occurred during data loading
3
 */
4
export interface DataLoadingError {
5
    /** The error message */
6
    message: string;
7
    /** Optional row/line number where the error occurred */
8
    line?: number;
9
    /** Optional error category for grouping */
10
    category?: string;
11
    /** Optional field/column name related to the error */
12
    field?: string;
13
}
14

15
/**
16
 * Summary of errors aggregated by category
17
 */
18
export interface ErrorSummary {
19
    /** Total number of errors */
20
    totalErrors: number;
21
    /** Errors grouped by category */
22
    categories: Map<string, DataLoadingError[]>;
23
    /** Most common error category */
24
    primaryCategory?: string;
25
    /** User-friendly summary message */
26
    message: string;
27
    /** Actionable suggestion for fixing the errors */
28
    suggestion?: string;
29
}
30

31
/**
32
 * Aggregates and groups errors during data loading to provide user-friendly error messages
33
 */
34
export class ErrorAggregator {
15✔
35
    private errors: DataLoadingError[] = [];
15✔
36
    private maxErrors: number;
37
    private errorsByCategory = new Map<string, DataLoadingError[]>();
15✔
38
    private errorsByField = new Map<string, DataLoadingError[]>();
15✔
39

40
    /**
41
     * Creates a new ErrorAggregator instance.
42
     * @param maxErrors - Maximum number of errors to collect before stopping
43
     */
44
    constructor(maxErrors = 100) {
15✔
45
        this.maxErrors = maxErrors;
356✔
46
    }
356✔
47

48
    /**
49
     * Add an error to the aggregator
50
     * @param error The error to add
51
     * @returns true if error was added, false if error limit reached
52
     */
53
    addError(error: DataLoadingError): boolean {
15✔
54
        if (this.errors.length >= this.maxErrors) {
367✔
55
            return false;
51✔
56
        }
51✔
57

58
        this.errors.push(error);
316✔
59

60
        // Group by category
61
        const category = error.category ?? "unknown";
367✔
62

63
        if (!this.errorsByCategory.has(category)) {
367✔
64
            this.errorsByCategory.set(category, []);
57✔
65
        }
57✔
66

67
        const categoryErrors = this.errorsByCategory.get(category);
316✔
68
        if (categoryErrors) {
316✔
69
            categoryErrors.push(error);
316✔
70
        }
316✔
71

72
        // Group by field
73
        if (error.field) {
367✔
74
            if (!this.errorsByField.has(error.field)) {
59✔
75
                this.errorsByField.set(error.field, []);
31✔
76
            }
31✔
77

78
            const fieldErrors = this.errorsByField.get(error.field);
59✔
79
            if (fieldErrors) {
59✔
80
                fieldErrors.push(error);
59✔
81
            }
59✔
82
        }
59✔
83

84
        return true;
316✔
85
    }
367✔
86

87
    /**
88
     * Get the total number of errors
89
     * @returns The count of accumulated errors
90
     */
91
    getErrorCount(): number {
15✔
92
        return this.errors.length;
227✔
93
    }
227✔
94

95
    /**
96
     * Check if error limit has been reached
97
     * @returns True if the maximum error count has been reached
98
     */
99
    hasReachedLimit(): boolean {
15✔
100
        return this.errors.length >= this.maxErrors;
298✔
101
    }
298✔
102

103
    /**
104
     * Get all errors
105
     * @returns Array of all accumulated errors
106
     */
107
    getErrors(): DataLoadingError[] {
15✔
108
        return [... this.errors];
9✔
109
    }
9✔
110

111
    /**
112
     * Get a summary of all errors
113
     * @returns Error summary with categorization and suggestions
114
     */
115
    getSummary(): ErrorSummary {
15✔
116
        const totalErrors = this.errors.length;
21✔
117

118
        // Find the most common category
119
        let primaryCategory: string | undefined;
21✔
120
        let maxCategoryCount = 0;
21✔
121
        for (const [category, errors] of this.errorsByCategory.entries()) {
21✔
122
            if (errors.length > maxCategoryCount) {
24✔
123
                maxCategoryCount = errors.length;
18✔
124
                primaryCategory = category;
18✔
125
            }
18✔
126
        }
24✔
127

128
        // Generate user-friendly message and suggestion
129
        const {message, suggestion} = this.generateUserFriendlyMessage(
21✔
130
            totalErrors,
21✔
131
            primaryCategory,
21✔
132
            this.errorsByCategory,
21✔
133
            this.errorsByField,
21✔
134
        );
21✔
135

136
        return {
21✔
137
            totalErrors,
21✔
138
            categories: new Map(this.errorsByCategory),
21✔
139
            primaryCategory,
21✔
140
            message,
21✔
141
            suggestion,
21✔
142
        };
21✔
143
    }
21✔
144

145
    /**
146
     * Get a user-friendly error message
147
     * @returns Formatted error message with suggestions
148
     */
149
    getUserFriendlyMessage(): string {
15✔
150
        const summary = this.getSummary();
3✔
151
        let msg = summary.message;
3✔
152

153
        if (summary.suggestion) {
3✔
154
            msg += `\nSuggestion: ${summary.suggestion}`;
2✔
155
        }
2✔
156

157
        return msg;
3✔
158
    }
3✔
159

160
    /**
161
     * Get a detailed error report with all error messages
162
     * @returns Comprehensive report with categorized errors
163
     */
164
    getDetailedReport(): string {
15✔
165
        const summary = this.getSummary();
4✔
166
        let report = `${summary.message}\n\n`;
4✔
167

168
        if (summary.suggestion) {
4✔
169
            report += `Suggestion: ${summary.suggestion}\n\n`;
4✔
170
        }
4✔
171

172
        report += `Detailed Errors (${summary.totalErrors} total):\n`;
4✔
173
        report += `${"=".repeat(50)}\n\n`;
4✔
174

175
        // Group errors by category
176
        for (const [category, errors] of this.errorsByCategory.entries()) {
4✔
177
            report += `${category.toUpperCase()} (${errors.length} errors):\n`;
5✔
178

179
            // Show first 10 errors from this category
180
            const sampleErrors = errors.slice(0, 10);
5✔
181
            for (const error of sampleErrors) {
5✔
182
                const lineInfo = error.line !== undefined ? ` [line ${error.line}]` : "";
16✔
183
                const fieldInfo = error.field ? ` [field: ${error.field}]` : "";
16✔
184
                report += `  - ${error.message}${lineInfo}${fieldInfo}\n`;
16✔
185
            }
16✔
186

187
            if (errors.length > 10) {
5✔
188
                report += `  ... and ${errors.length - 10} more\n`;
1✔
189
            }
1✔
190

191
            report += "\n";
5✔
192
        }
5✔
193

194
        return report;
4✔
195
    }
4✔
196

197
    /**
198
     * Clear all errors
199
     */
200
    clear(): void {
15✔
201
        this.errors = [];
1✔
202

203
        this.errorsByCategory.clear();
1✔
204
        this.errorsByField.clear();
1✔
205
    }
1✔
206

207
    /**
208
     * Generate user-friendly message based on error patterns
209
     * @param totalErrors - Total number of errors
210
     * @param primaryCategory - Most common error category
211
     * @param errorsByCategory - Errors grouped by category
212
     * @param errorsByField - Errors grouped by field name
213
     * @returns Object containing user-friendly message and optional suggestion
214
     */
215
    private generateUserFriendlyMessage(
15✔
216
        totalErrors: number,
21✔
217
        primaryCategory: string | undefined,
21✔
218
        errorsByCategory: Map<string, DataLoadingError[]>,
21✔
219
        errorsByField: Map<string, DataLoadingError[]>,
21✔
220
    ): {message: string, suggestion?: string} {
21✔
221
        if (totalErrors === 0) {
21✔
222
            return {message: "No errors"};
3✔
223
        }
3✔
224

225
        // Check for common error patterns
226
        const missingColumnErrors = errorsByCategory.get("missing-column")?.length ?? 0;
21✔
227
        const parseErrors = errorsByCategory.get("parse-error")?.length ?? 0;
21✔
228
        const validationErrors = errorsByCategory.get("validation-error")?.length ?? 0;
21✔
229
        const missingValueErrors = errorsByCategory.get("missing-value")?.length ?? 0;
21✔
230

231
        // Pattern: Missing required columns
232
        if (missingColumnErrors > 0) {
21✔
233
            const fields = Array.from(errorsByField.keys());
3✔
234
            const message = `Found ${totalErrors} errors (${missingColumnErrors} missing column errors)`;
3✔
235
            const suggestion = fields.length > 0 ?
3✔
236
                `Check that your data has the required columns: ${fields.join(", ")}` :
3!
237
                "Check that your data has the required columns (e.g., 'source', 'target' for edges)";
×
238
            return {message, suggestion};
3✔
239
        }
3✔
240

241
        // Pattern: Parse errors
242
        if (parseErrors > totalErrors * 0.5) {
21✔
243
            const message = `Found ${totalErrors} errors (mostly parsing errors)`;
5✔
244
            const suggestion = "Check your file format and encoding. Common issues: incorrect delimiter, malformed quotes, or encoding problems (try UTF-8)";
5✔
245
            return {message, suggestion};
5✔
246
        }
5✔
247

248
        // Pattern: Missing values
249
        if (missingValueErrors > 0) {
21✔
250
            const fields = Array.from(errorsByField.keys());
2✔
251
            const message = `Found ${totalErrors} errors (${missingValueErrors} missing required values)`;
2✔
252
            const suggestion = fields.length > 0 ?
2✔
253
                `Check that rows have values for: ${fields.join(", ")}` :
2!
254
                "Check that rows have all required values";
×
255
            return {message, suggestion};
2✔
256
        }
2✔
257

258
        // Pattern: Validation errors
259
        if (validationErrors > 0) {
21✔
260
            const message = `Found ${totalErrors} errors (${validationErrors} validation errors)`;
2✔
261
            const suggestion = "Check that your data values match the expected format and types";
2✔
262
            return {message, suggestion};
2✔
263
        }
2✔
264

265
        // Generic message based on primary category
266
        if (primaryCategory && primaryCategory !== "unknown") {
21✔
267
            return {
4✔
268
                message: `Found ${totalErrors} errors (mostly ${primaryCategory} errors)`,
4✔
269
                suggestion: "See detailed error report for more information",
4✔
270
            };
4✔
271
        }
4✔
272

273
        // Fallback generic message
274
        return {
2✔
275
            message: `Found ${totalErrors} errors during data loading`,
2✔
276
            suggestion: "See detailed error report for more information",
2✔
277
        };
2✔
278
    }
21✔
279
}
15✔
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