fix(error): GraphQL-aligned GraphError type

Expand GraphError.path to accept strings and numbers for field names
and list indexes, rather than strings only. Constrain isRawError to
check this assumption. Add custom path formatter to use dot notation for
fields and brackets for indexes.
This commit is contained in:
2026-07-14 18:01:03 -07:00
parent d75b8333e5
commit 39e1f9e9ad
+30 -6
View File
@@ -3,7 +3,8 @@
*/
export interface GraphError {
message: string;
path?: string[];
/** GraphQL response path - field names and list indexes. */
path?: ReadonlyArray<string | number>;
}
/** RawError is an error that can be converted to a string by ErrorMessage */
@@ -15,14 +16,22 @@ export type RawError = ErrorMessage | Error | string | GraphError[];
* @returns true if the error is a RawError, false otherwise
*/
export const isRawError = (error: unknown): error is RawError => {
const isGraphError = (entry: unknown): entry is GraphError => {
if (!entry || typeof entry !== 'object') return false;
const candidate = entry as { message?: unknown; path?: unknown };
if (typeof candidate.message !== 'string') return false;
if (candidate.path === undefined) return true;
return (
Array.isArray(candidate.path) &&
candidate.path.every((p) => typeof p === 'string' || typeof p === 'number')
);
};
return (
error instanceof ErrorMessage ||
error instanceof Error ||
typeof error === 'string' ||
(Array.isArray(error) &&
error.every(
(e) => typeof e.message === 'string' && (e.path === undefined || Array.isArray(e.path))
))
(Array.isArray(error) && error.every(isGraphError))
);
};
@@ -73,6 +82,21 @@ export const checkGraphResponse = (
export class ErrorMessage {
private _lines: string[] = [];
/** formats a GraphQL path using dot notation for fields and brackets for indexes */
private static formatGraphPath(path: ReadonlyArray<string | number>): string {
let formatted = '';
for (const segment of path) {
if (typeof segment === 'number') {
formatted += `[${segment}]`;
} else if (formatted.length === 0) {
formatted = segment;
} else {
formatted += `.${segment}`;
}
}
return formatted;
}
/**
* Always creates a new ErrorMessage instance, even if there are no errors.
* @param errors The raw errors to convert and store, or null/undefined for no error.
@@ -136,7 +160,7 @@ export class ErrorMessage {
errorLines = raw.map((e) => {
const messageString = e.message || 'Unknown error';
if (e.path && e.path.length > 0) {
return `"${messageString}" at ${e.path.join('.')}`;
return `"${messageString}" at ${ErrorMessage.formatGraphPath(e.path)}`;
}
return messageString;
});