@workflow/web-shared 5.0.0-beta.12 → 5.0.0-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/event-list-view.d.ts.map +1 -1
- package/dist/components/event-list-view.js +9 -9
- package/dist/components/new-trace-viewer/components/event-list.d.ts +1 -1
- package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/event-list.js +4 -2
- package/dist/components/new-trace-viewer/components/timeline.d.ts +1 -1
- package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/timeline.js +4 -4
- package/dist/components/new-trace-viewer/context.d.ts +1 -1
- package/dist/components/new-trace-viewer/context.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/context.js +1 -1
- package/dist/components/new-trace-viewer/icons.js +3 -3
- package/dist/components/new-trace-viewer/search.d.ts +1 -1
- package/dist/components/new-trace-viewer/search.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/search.js +1 -1
- package/dist/components/new-trace-viewer/trace-viewer.d.ts +2 -2
- package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/trace-viewer.js +6 -7
- package/dist/components/new-trace-viewer/types.d.ts +2 -0
- package/dist/components/new-trace-viewer/types.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/types.js +2 -2
- package/dist/components/new-trace-viewer/utils.d.ts +1 -1
- package/dist/components/new-trace-viewer/utils.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/utils.js +1 -1
- package/dist/components/sidebar/attribute-panel.d.ts.map +1 -1
- package/dist/components/sidebar/attribute-panel.js +7 -7
- package/dist/components/sidebar/events-list.d.ts.map +1 -1
- package/dist/components/sidebar/events-list.js +7 -7
- package/dist/components/trace-viewer-new.d.ts.map +1 -1
- package/dist/components/trace-viewer-new.js +2 -3
- package/dist/components/ui/error-stack-block.d.ts +17 -10
- package/dist/components/ui/error-stack-block.d.ts.map +1 -1
- package/dist/components/ui/error-stack-block.js +30 -19
- package/package.json +2 -2
- package/src/components/event-list-view.tsx +12 -11
- package/src/components/new-trace-viewer/components/event-list.tsx +4 -3
- package/src/components/new-trace-viewer/components/timeline.tsx +4 -4
- package/src/components/new-trace-viewer/context.tsx +1 -1
- package/src/components/new-trace-viewer/icons.tsx +2 -2
- package/src/components/new-trace-viewer/search.ts +1 -1
- package/src/components/new-trace-viewer/trace-viewer.tsx +7 -14
- package/src/components/new-trace-viewer/types.ts +2 -0
- package/src/components/new-trace-viewer/utils.ts +1 -1
- package/src/components/sidebar/attribute-panel.tsx +6 -9
- package/src/components/sidebar/events-list.tsx +6 -9
- package/src/components/trace-viewer-new.tsx +2 -4
- package/src/components/ui/error-stack-block.tsx +51 -31
- package/dist/components/new-trace-viewer/detail-panel.d.ts +0 -10
- package/dist/components/new-trace-viewer/detail-panel.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/detail-panel.js +0 -13
- package/dist/components/new-trace-viewer/util/constants.d.ts +0 -1
- package/dist/components/new-trace-viewer/util/constants.d.ts.map +0 -1
- package/dist/components/new-trace-viewer/util/constants.js +0 -2
- package/src/components/new-trace-viewer/detail-panel.tsx +0 -55
- package/src/components/new-trace-viewer/util/constants.ts +0 -0
|
@@ -3,13 +3,21 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { AlertCircle } from 'lucide-react';
|
|
4
4
|
import { CopyButton } from '../new-trace-viewer/components/copy-button';
|
|
5
5
|
/**
|
|
6
|
-
* Check whether `value` looks like a structured error object
|
|
7
|
-
*
|
|
6
|
+
* Check whether `value` looks like a structured error object we can render
|
|
7
|
+
* with the error block. Some persisted workflow errors only include a
|
|
8
|
+
* `message`, while runtime errors usually also include `stack`.
|
|
8
9
|
*/
|
|
9
|
-
export function
|
|
10
|
+
export function isStructuredError(value) {
|
|
10
11
|
return (value != null &&
|
|
11
12
|
typeof value === 'object' &&
|
|
12
|
-
'
|
|
13
|
+
(typeof value.message === 'string' ||
|
|
14
|
+
typeof value.stack === 'string'));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Narrower guard kept for callers that specifically need a stack trace.
|
|
18
|
+
*/
|
|
19
|
+
export function isStructuredErrorWithStack(value) {
|
|
20
|
+
return (isStructuredError(value) &&
|
|
13
21
|
typeof value.stack === 'string');
|
|
14
22
|
}
|
|
15
23
|
/**
|
|
@@ -26,18 +34,21 @@ function deriveTitle(message) {
|
|
|
26
34
|
return firstLine.trim();
|
|
27
35
|
}
|
|
28
36
|
/**
|
|
29
|
-
* Renders
|
|
30
|
-
*
|
|
31
|
-
*
|
|
37
|
+
* Renders a structured error as a visually distinct error block. Shows the
|
|
38
|
+
* error message with an alert icon at the top, separated from the stack trace
|
|
39
|
+
* or full message below.
|
|
32
40
|
*/
|
|
33
|
-
export function ErrorStackBlock({ value
|
|
34
|
-
const stack = value.stack;
|
|
41
|
+
export function ErrorStackBlock({ value }) {
|
|
42
|
+
const stack = typeof value.stack === 'string' ? value.stack : undefined;
|
|
35
43
|
const message = typeof value.message === 'string' ? value.message : undefined;
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
const
|
|
44
|
+
const body = stack ?? message ?? '';
|
|
45
|
+
// V8's `Error.stack` already starts with `Name: message`; message-only
|
|
46
|
+
// errors use the message as the body so long single-line failures remain
|
|
47
|
+
// readable even when the header truncates.
|
|
48
|
+
const title = message ? deriveTitle(message) : deriveTitle(body);
|
|
49
|
+
const copyText = message && stack && !stack.includes(message)
|
|
50
|
+
? `${message}\n\n${stack}`
|
|
51
|
+
: body;
|
|
41
52
|
return (_jsxs("div", { className: "relative overflow-hidden rounded-md border", style: {
|
|
42
53
|
borderColor: 'var(--ds-red-400)',
|
|
43
54
|
background: 'var(--ds-red-100)',
|
|
@@ -45,13 +56,13 @@ export function ErrorStackBlock({ value, }) {
|
|
|
45
56
|
color: 'var(--ds-red-900)',
|
|
46
57
|
borderBottom: '1px solid var(--ds-red-400)',
|
|
47
58
|
}, children: [_jsx(AlertCircle, { className: "h-4 w-4 shrink-0" }), _jsx("p", { className: "text-xs font-semibold m-0 truncate",
|
|
48
|
-
// The full
|
|
49
|
-
//
|
|
59
|
+
// The full message or stack is in the body below; the header just
|
|
60
|
+
// shows the first line, single-line, with overflow
|
|
50
61
|
// ellipsised so a long title doesn't push the copy button or
|
|
51
62
|
// wrap into the framed hint/docs lines.
|
|
52
|
-
title: message, children: title })] })), _jsx("pre", { className: "px-3 py-2.5 text-xs font-mono whitespace-pre-wrap break-words overflow-auto m-0", style: {
|
|
63
|
+
title: message, children: title })] })), body && (_jsx("pre", { className: "px-3 py-2.5 text-xs font-mono whitespace-pre-wrap break-words overflow-auto m-0", style: {
|
|
53
64
|
color: 'var(--ds-red-900)',
|
|
54
65
|
background: 'var(--ds-red-200)',
|
|
55
|
-
}, children:
|
|
66
|
+
}, children: body }))] }));
|
|
56
67
|
}
|
|
57
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
68
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3Itc3RhY2stYmxvY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29tcG9uZW50cy91aS9lcnJvci1zdGFjay1ibG9jay50c3giXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWSxDQUFDOztBQUViLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDM0MsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDRDQUE0QyxDQUFDO0FBT3hFOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsaUJBQWlCLENBQy9CLEtBQWM7SUFFZCxPQUFPLENBQ0wsS0FBSyxJQUFJLElBQUk7UUFDYixPQUFPLEtBQUssS0FBSyxRQUFRO1FBQ3pCLENBQUMsT0FBUSxLQUFpQyxDQUFDLE9BQU8sS0FBSyxRQUFRO1lBQzdELE9BQVEsS0FBaUMsQ0FBQyxLQUFLLEtBQUssUUFBUSxDQUFDLENBQ2hFLENBQUM7QUFDSixDQUFDO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLFVBQVUsMEJBQTBCLENBQ3hDLEtBQWM7SUFFZCxPQUFPLENBQ0wsaUJBQWlCLENBQUMsS0FBSyxDQUFDO1FBQ3hCLE9BQVEsS0FBK0IsQ0FBQyxLQUFLLEtBQUssUUFBUSxDQUMzRCxDQUFDO0FBQ0osQ0FBQztBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsU0FBUyxXQUFXLENBQUMsT0FBZTtJQUNsQyxNQUFNLFNBQVMsR0FDYixPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsSUFBSSxPQUFPLENBQUM7SUFDeEUsT0FBTyxTQUFTLENBQUMsSUFBSSxFQUFFLENBQUM7QUFDMUIsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsZUFBZSxDQUFDLEVBQUUsS0FBSyxFQUFvQztJQUN6RSxNQUFNLEtBQUssR0FBRyxPQUFPLEtBQUssQ0FBQyxLQUFLLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDeEUsTUFBTSxPQUFPLEdBQUcsT0FBTyxLQUFLLENBQUMsT0FBTyxLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0lBQzlFLE1BQU0sSUFBSSxHQUFHLEtBQUssSUFBSSxPQUFPLElBQUksRUFBRSxDQUFDO0lBQ3BDLHVFQUF1RTtJQUN2RSx5RUFBeUU7SUFDekUsMkNBQTJDO0lBQzNDLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDakUsTUFBTSxRQUFRLEdBQ1osT0FBTyxJQUFJLEtBQUssSUFBSSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDO1FBQzFDLENBQUMsQ0FBQyxHQUFHLE9BQU8sT0FBTyxLQUFLLEVBQUU7UUFDMUIsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUVYLE9BQU8sQ0FDTCxlQUNFLFNBQVMsRUFBQyw0Q0FBNEMsRUFDdEQsS0FBSyxFQUFFO1lBQ0wsV0FBVyxFQUFFLG1CQUFtQjtZQUNoQyxVQUFVLEVBQUUsbUJBQW1CO1NBQ2hDLGFBRUQsS0FBQyxVQUFVLElBQ1QsUUFBUSxFQUFFLFFBQVEsRUFDbEIsU0FBUyxFQUFDLFlBQVksRUFDdEIsU0FBUyxFQUFDLG1OQUFtTixHQUM3TixFQUVELEtBQUssSUFBSSxDQUNSLGVBQ0UsU0FBUyxFQUFDLDJDQUEyQyxFQUNyRCxLQUFLLEVBQUU7b0JBQ0wsS0FBSyxFQUFFLG1CQUFtQjtvQkFDMUIsWUFBWSxFQUFFLDZCQUE2QjtpQkFDNUMsYUFFRCxLQUFDLFdBQVcsSUFBQyxTQUFTLEVBQUMsa0JBQWtCLEdBQUcsRUFDNUMsWUFDRSxTQUFTLEVBQUMsb0NBQW9DO3dCQUM5QyxrRUFBa0U7d0JBQ2xFLG1EQUFtRDt3QkFDbkQsNkRBQTZEO3dCQUM3RCx3Q0FBd0M7d0JBQ3hDLEtBQUssRUFBRSxPQUFPLFlBRWIsS0FBSyxHQUNKLElBQ0EsQ0FDUCxFQUNBLElBQUksSUFBSSxDQUNQLGNBQ0UsU0FBUyxFQUFDLGlGQUFpRixFQUMzRixLQUFLLEVBQUU7b0JBQ0wsS0FBSyxFQUFFLG1CQUFtQjtvQkFDMUIsVUFBVSxFQUFFLG1CQUFtQjtpQkFDaEMsWUFFQSxJQUFJLEdBQ0QsQ0FDUCxJQUNHLENBQ1AsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIGNsaWVudCc7XG5cbmltcG9ydCB7IEFsZXJ0Q2lyY2xlIH0gZnJvbSAnbHVjaWRlLXJlYWN0JztcbmltcG9ydCB7IENvcHlCdXR0b24gfSBmcm9tICcuLi9uZXctdHJhY2Utdmlld2VyL2NvbXBvbmVudHMvY29weS1idXR0b24nO1xuXG5leHBvcnQgdHlwZSBTdHJ1Y3R1cmVkRXJyb3JSZWNvcmQgPSBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiAmIHtcbiAgbWVzc2FnZT86IHN0cmluZztcbiAgc3RhY2s/OiBzdHJpbmc7XG59O1xuXG4vKipcbiAqIENoZWNrIHdoZXRoZXIgYHZhbHVlYCBsb29rcyBsaWtlIGEgc3RydWN0dXJlZCBlcnJvciBvYmplY3Qgd2UgY2FuIHJlbmRlclxuICogd2l0aCB0aGUgZXJyb3IgYmxvY2suIFNvbWUgcGVyc2lzdGVkIHdvcmtmbG93IGVycm9ycyBvbmx5IGluY2x1ZGUgYVxuICogYG1lc3NhZ2VgLCB3aGlsZSBydW50aW1lIGVycm9ycyB1c3VhbGx5IGFsc28gaW5jbHVkZSBgc3RhY2tgLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNTdHJ1Y3R1cmVkRXJyb3IoXG4gIHZhbHVlOiB1bmtub3duXG4pOiB2YWx1ZSBpcyBTdHJ1Y3R1cmVkRXJyb3JSZWNvcmQge1xuICByZXR1cm4gKFxuICAgIHZhbHVlICE9IG51bGwgJiZcbiAgICB0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnICYmXG4gICAgKHR5cGVvZiAodmFsdWUgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pLm1lc3NhZ2UgPT09ICdzdHJpbmcnIHx8XG4gICAgICB0eXBlb2YgKHZhbHVlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KS5zdGFjayA9PT0gJ3N0cmluZycpXG4gICk7XG59XG5cbi8qKlxuICogTmFycm93ZXIgZ3VhcmQga2VwdCBmb3IgY2FsbGVycyB0aGF0IHNwZWNpZmljYWxseSBuZWVkIGEgc3RhY2sgdHJhY2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1N0cnVjdHVyZWRFcnJvcldpdGhTdGFjayhcbiAgdmFsdWU6IHVua25vd25cbik6IHZhbHVlIGlzIFN0cnVjdHVyZWRFcnJvclJlY29yZCAmIHsgc3RhY2s6IHN0cmluZyB9IHtcbiAgcmV0dXJuIChcbiAgICBpc1N0cnVjdHVyZWRFcnJvcih2YWx1ZSkgJiZcbiAgICB0eXBlb2YgKHZhbHVlIGFzIFN0cnVjdHVyZWRFcnJvclJlY29yZCkuc3RhY2sgPT09ICdzdHJpbmcnXG4gICk7XG59XG5cbi8qKlxuICogUHVsbCBhIHNob3J0LCBzaW5nbGUtbGluZSB0aXRsZSBvdXQgb2YgYW4gZXJyb3IgbWVzc2FnZS5cbiAqXG4gKiBXb3JrZmxvdydzIHN0cnVjdHVyZWQgZXJyb3IgbWVzc2FnZXMgYXJlIG11bHRpLWxpbmUg4oCUIHRoZSBmaXJzdCBsaW5lIGlzXG4gKiB0aGUgaGVhZGxpbmUgKGBGYWlsZWQgdG8gc2VyaWFsaXplIHN0ZXAgcmV0dXJuIHZhbHVlYCkgYW5kIHRoZSByZXN0IGFyZVxuICogYOKVsOKWtiBoaW50OmAgLyBg4pWw4pa2IGRvY3M6YCBmcmFtZWQgZGV0YWlscy4gVGhlIGZ1bGwgbWVzc2FnZSBiZWxvbmdzIGluIHRoZVxuICogYm9keSBvZiB0aGUgZXJyb3IgYmxvY2s7IHRoZSB0aXRsZSBzaG91bGQganVzdCBiZSB0aGUgaGVhZGxpbmUgc28gdGhlXG4gKiBjYXJkIHN0YXlzIHNjYW5uYWJsZS5cbiAqL1xuZnVuY3Rpb24gZGVyaXZlVGl0bGUobWVzc2FnZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgZmlyc3RMaW5lID1cbiAgICBtZXNzYWdlLnNwbGl0KCdcXG4nKS5maW5kKChsaW5lKSA9PiBsaW5lLnRyaW0oKS5sZW5ndGggPiAwKSA/PyBtZXNzYWdlO1xuICByZXR1cm4gZmlyc3RMaW5lLnRyaW0oKTtcbn1cblxuLyoqXG4gKiBSZW5kZXJzIGEgc3RydWN0dXJlZCBlcnJvciBhcyBhIHZpc3VhbGx5IGRpc3RpbmN0IGVycm9yIGJsb2NrLiBTaG93cyB0aGVcbiAqIGVycm9yIG1lc3NhZ2Ugd2l0aCBhbiBhbGVydCBpY29uIGF0IHRoZSB0b3AsIHNlcGFyYXRlZCBmcm9tIHRoZSBzdGFjayB0cmFjZVxuICogb3IgZnVsbCBtZXNzYWdlIGJlbG93LlxuICovXG5leHBvcnQgZnVuY3Rpb24gRXJyb3JTdGFja0Jsb2NrKHsgdmFsdWUgfTogeyB2YWx1ZTogU3RydWN0dXJlZEVycm9yUmVjb3JkIH0pIHtcbiAgY29uc3Qgc3RhY2sgPSB0eXBlb2YgdmFsdWUuc3RhY2sgPT09ICdzdHJpbmcnID8gdmFsdWUuc3RhY2sgOiB1bmRlZmluZWQ7XG4gIGNvbnN0IG1lc3NhZ2UgPSB0eXBlb2YgdmFsdWUubWVzc2FnZSA9PT0gJ3N0cmluZycgPyB2YWx1ZS5tZXNzYWdlIDogdW5kZWZpbmVkO1xuICBjb25zdCBib2R5ID0gc3RhY2sgPz8gbWVzc2FnZSA/PyAnJztcbiAgLy8gVjgncyBgRXJyb3Iuc3RhY2tgIGFscmVhZHkgc3RhcnRzIHdpdGggYE5hbWU6IG1lc3NhZ2VgOyBtZXNzYWdlLW9ubHlcbiAgLy8gZXJyb3JzIHVzZSB0aGUgbWVzc2FnZSBhcyB0aGUgYm9keSBzbyBsb25nIHNpbmdsZS1saW5lIGZhaWx1cmVzIHJlbWFpblxuICAvLyByZWFkYWJsZSBldmVuIHdoZW4gdGhlIGhlYWRlciB0cnVuY2F0ZXMuXG4gIGNvbnN0IHRpdGxlID0gbWVzc2FnZSA/IGRlcml2ZVRpdGxlKG1lc3NhZ2UpIDogZGVyaXZlVGl0bGUoYm9keSk7XG4gIGNvbnN0IGNvcHlUZXh0ID1cbiAgICBtZXNzYWdlICYmIHN0YWNrICYmICFzdGFjay5pbmNsdWRlcyhtZXNzYWdlKVxuICAgICAgPyBgJHttZXNzYWdlfVxcblxcbiR7c3RhY2t9YFxuICAgICAgOiBib2R5O1xuXG4gIHJldHVybiAoXG4gICAgPGRpdlxuICAgICAgY2xhc3NOYW1lPVwicmVsYXRpdmUgb3ZlcmZsb3ctaGlkZGVuIHJvdW5kZWQtbWQgYm9yZGVyXCJcbiAgICAgIHN0eWxlPXt7XG4gICAgICAgIGJvcmRlckNvbG9yOiAndmFyKC0tZHMtcmVkLTQwMCknLFxuICAgICAgICBiYWNrZ3JvdW5kOiAndmFyKC0tZHMtcmVkLTEwMCknLFxuICAgICAgfX1cbiAgICA+XG4gICAgICA8Q29weUJ1dHRvblxuICAgICAgICBjb3B5VGV4dD17Y29weVRleHR9XG4gICAgICAgIGFyaWFMYWJlbD1cIkNvcHkgZXJyb3JcIlxuICAgICAgICBjbGFzc05hbWU9XCJhYnNvbHV0ZSByaWdodC0yIHRvcC0yIGZsZXggaC02IHctNiBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgcm91bmRlZC1tZCBib3JkZXIgYm9yZGVyLXJlZC00MDAgYmctcmVkLTEwMCBwLTAgdGV4dC1yZWQtOTAwIHRyYW5zaXRpb24tdHJhbnNmb3JtIHRyYW5zaXRpb24tY29sb3JzIGR1cmF0aW9uLTEwMCBob3ZlcjpiZy1yZWQtMjAwIGFjdGl2ZTpzY2FsZS05NVwiXG4gICAgICAvPlxuXG4gICAgICB7dGl0bGUgJiYgKFxuICAgICAgICA8ZGl2XG4gICAgICAgICAgY2xhc3NOYW1lPVwiZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTIgcHgtMyBweS0yLjUgcHItMTBcIlxuICAgICAgICAgIHN0eWxlPXt7XG4gICAgICAgICAgICBjb2xvcjogJ3ZhcigtLWRzLXJlZC05MDApJyxcbiAgICAgICAgICAgIGJvcmRlckJvdHRvbTogJzFweCBzb2xpZCB2YXIoLS1kcy1yZWQtNDAwKScsXG4gICAgICAgICAgfX1cbiAgICAgICAgPlxuICAgICAgICAgIDxBbGVydENpcmNsZSBjbGFzc05hbWU9XCJoLTQgdy00IHNocmluay0wXCIgLz5cbiAgICAgICAgICA8cFxuICAgICAgICAgICAgY2xhc3NOYW1lPVwidGV4dC14cyBmb250LXNlbWlib2xkIG0tMCB0cnVuY2F0ZVwiXG4gICAgICAgICAgICAvLyBUaGUgZnVsbCBtZXNzYWdlIG9yIHN0YWNrIGlzIGluIHRoZSBib2R5IGJlbG93OyB0aGUgaGVhZGVyIGp1c3RcbiAgICAgICAgICAgIC8vIHNob3dzIHRoZSBmaXJzdCBsaW5lLCBzaW5nbGUtbGluZSwgd2l0aCBvdmVyZmxvd1xuICAgICAgICAgICAgLy8gZWxsaXBzaXNlZCBzbyBhIGxvbmcgdGl0bGUgZG9lc24ndCBwdXNoIHRoZSBjb3B5IGJ1dHRvbiBvclxuICAgICAgICAgICAgLy8gd3JhcCBpbnRvIHRoZSBmcmFtZWQgaGludC9kb2NzIGxpbmVzLlxuICAgICAgICAgICAgdGl0bGU9e21lc3NhZ2V9XG4gICAgICAgICAgPlxuICAgICAgICAgICAge3RpdGxlfVxuICAgICAgICAgIDwvcD5cbiAgICAgICAgPC9kaXY+XG4gICAgICApfVxuICAgICAge2JvZHkgJiYgKFxuICAgICAgICA8cHJlXG4gICAgICAgICAgY2xhc3NOYW1lPVwicHgtMyBweS0yLjUgdGV4dC14cyBmb250LW1vbm8gd2hpdGVzcGFjZS1wcmUtd3JhcCBicmVhay13b3JkcyBvdmVyZmxvdy1hdXRvIG0tMFwiXG4gICAgICAgICAgc3R5bGU9e3tcbiAgICAgICAgICAgIGNvbG9yOiAndmFyKC0tZHMtcmVkLTkwMCknLFxuICAgICAgICAgICAgYmFja2dyb3VuZDogJ3ZhcigtLWRzLXJlZC0yMDApJyxcbiAgICAgICAgICB9fVxuICAgICAgICA+XG4gICAgICAgICAge2JvZHl9XG4gICAgICAgIDwvcHJlPlxuICAgICAgKX1cbiAgICA8L2Rpdj5cbiAgKTtcbn1cbiJdfQ==
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workflow/web-shared",
|
|
3
3
|
"description": "Shared components for Workflow Observability UI",
|
|
4
|
-
"version": "5.0.0-beta.
|
|
4
|
+
"version": "5.0.0-beta.13",
|
|
5
5
|
"private": false,
|
|
6
6
|
"files": [
|
|
7
7
|
"dist",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"streamdown": "2.3.0",
|
|
54
54
|
"tailwind-merge": "3.5.0",
|
|
55
55
|
"tailwindcss": "4",
|
|
56
|
-
"@workflow/core": "5.0.0-beta.
|
|
56
|
+
"@workflow/core": "5.0.0-beta.13",
|
|
57
57
|
"@workflow/utils": "5.0.0-beta.3",
|
|
58
58
|
"@workflow/world": "5.0.0-beta.7"
|
|
59
59
|
},
|
|
@@ -10,20 +10,21 @@ import type {
|
|
|
10
10
|
} from 'react';
|
|
11
11
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
12
12
|
import { Virtuoso, type VirtuosoHandle } from 'react-virtuoso';
|
|
13
|
-
import { isEncryptedMarker } from '../lib/hydration';
|
|
14
13
|
import {
|
|
15
|
-
parseExactWorkflowSearchId,
|
|
16
|
-
looksLikeWorkflowIdSearchInput,
|
|
17
14
|
type ExactIdSearchResult,
|
|
18
15
|
type ExactWorkflowSearchIdKind,
|
|
16
|
+
looksLikeWorkflowIdSearchInput,
|
|
17
|
+
parseExactWorkflowSearchId,
|
|
19
18
|
} from '../lib/exact-event-search-id';
|
|
20
|
-
import {
|
|
21
|
-
import { formatDuration } from '../lib/utils';
|
|
19
|
+
import { isEncryptedMarker } from '../lib/hydration';
|
|
22
20
|
import { useToast } from '../lib/toast';
|
|
21
|
+
import { formatDuration } from '../lib/utils';
|
|
23
22
|
import { DataInspector, DecryptClickContext } from './ui/data-inspector';
|
|
23
|
+
import { DecryptButton } from './ui/decrypt-button';
|
|
24
24
|
import {
|
|
25
25
|
ErrorStackBlock,
|
|
26
|
-
|
|
26
|
+
isStructuredError,
|
|
27
|
+
type StructuredErrorRecord,
|
|
27
28
|
} from './ui/error-stack-block';
|
|
28
29
|
import { LoadMoreButton } from './ui/load-more-button';
|
|
29
30
|
import { MenuDropdown } from './ui/menu-dropdown';
|
|
@@ -519,20 +520,20 @@ function deepParseJson(value: unknown): unknown {
|
|
|
519
520
|
}
|
|
520
521
|
|
|
521
522
|
/**
|
|
522
|
-
* Extracts a structured error
|
|
523
|
+
* Extracts a structured error from event data, if present.
|
|
523
524
|
* Returns the error object to render with ErrorStackBlock, or null if not applicable.
|
|
524
525
|
*/
|
|
525
526
|
function extractStructuredError(
|
|
526
527
|
data: unknown,
|
|
527
528
|
eventType?: string
|
|
528
|
-
):
|
|
529
|
+
): StructuredErrorRecord | null {
|
|
529
530
|
if (!eventType || !ERROR_EVENT_TYPES.has(eventType)) return null;
|
|
530
531
|
if (data == null || typeof data !== 'object') return null;
|
|
531
532
|
const record = data as Record<string, unknown>;
|
|
532
533
|
// Check the nested `error` field first (the StructuredError)
|
|
533
|
-
if (
|
|
534
|
-
// Some error formats put the stack at the top level of eventData
|
|
535
|
-
if (
|
|
534
|
+
if (isStructuredError(record.error)) return record.error;
|
|
535
|
+
// Some error formats put the message/stack at the top level of eventData.
|
|
536
|
+
if (isStructuredError(record)) return record;
|
|
536
537
|
return null;
|
|
537
538
|
}
|
|
538
539
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Circle } from 'lucide-react';
|
|
2
2
|
import { useRef } from 'react';
|
|
3
3
|
import { cn } from '../../../lib/utils';
|
|
4
|
-
import type { Span } from '
|
|
4
|
+
import type { Span } from '../types';
|
|
5
5
|
import { formatDuration } from '../../trace-viewer/util/timing';
|
|
6
6
|
import {
|
|
7
7
|
SleepIcon,
|
|
@@ -53,8 +53,9 @@ const EventRow = ({
|
|
|
53
53
|
onSelectSpan: (spanId: string) => void;
|
|
54
54
|
}) => {
|
|
55
55
|
const durationMs = getSpanDurationMs(span);
|
|
56
|
-
const
|
|
57
|
-
|
|
56
|
+
const workflowStatus = (span.attributes.data as Record<string, unknown>)
|
|
57
|
+
?.status as string | undefined;
|
|
58
|
+
const isErrored = span.status.code === 2 || workflowStatus === 'failed';
|
|
58
59
|
const { icon: Icon, className: tagClassName } = getEventStyle(
|
|
59
60
|
span.resource,
|
|
60
61
|
isErrored
|
|
@@ -4,7 +4,7 @@ import { ArrowLeft, ArrowRight } from 'lucide-react';
|
|
|
4
4
|
import type { CSSProperties, ReactNode } from 'react';
|
|
5
5
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
6
6
|
import { cn } from '../../../lib/utils';
|
|
7
|
-
import type { Span } from '
|
|
7
|
+
import type { Span } from '../types';
|
|
8
8
|
import { formatDuration, getHighResInMs } from '../../trace-viewer/util/timing';
|
|
9
9
|
import { isSpanDimmedBySearch, type SpanSearchResult } from '../search';
|
|
10
10
|
import type { Segment, SegmentStatus, TimeMarker } from '../utils';
|
|
@@ -234,7 +234,7 @@ function SegmentBar({ segments }: { segments: VisibleSegment[] }): ReactNode {
|
|
|
234
234
|
|
|
235
235
|
return (
|
|
236
236
|
<div
|
|
237
|
-
key={
|
|
237
|
+
key={i}
|
|
238
238
|
className={cn(
|
|
239
239
|
'absolute h-full rounded-[0.25rem]',
|
|
240
240
|
SEGMENT_CLASSES[seg.status]
|
|
@@ -421,7 +421,7 @@ export function TimelineHeader({
|
|
|
421
421
|
<div className="relative h-full flex-1">
|
|
422
422
|
{markers.map((m) => (
|
|
423
423
|
<span
|
|
424
|
-
key={
|
|
424
|
+
key={String(m.value)}
|
|
425
425
|
className="absolute bottom-1 font-mono text-xs font-normal leading-4 text-gray-900 whitespace-nowrap"
|
|
426
426
|
style={{ left: `${m.position * 100}%` }}
|
|
427
427
|
>
|
|
@@ -506,7 +506,7 @@ export function Timeline({
|
|
|
506
506
|
// Skip the "0s" origin marker since the left edge already implies it.
|
|
507
507
|
Math.abs(marker.value) > 0.000001 ? (
|
|
508
508
|
<div
|
|
509
|
-
key={
|
|
509
|
+
key={String(marker.value)}
|
|
510
510
|
className="absolute top-0 bottom-0 w-px bg-gray-alpha-300"
|
|
511
511
|
style={{ left: `${marker.position * 100}%` }}
|
|
512
512
|
/>
|
|
@@ -3,7 +3,7 @@ const WorkflowIcon = () => {
|
|
|
3
3
|
<svg
|
|
4
4
|
data-testid="geist-icon"
|
|
5
5
|
height="16"
|
|
6
|
-
|
|
6
|
+
strokeLinejoin="round"
|
|
7
7
|
style={{ color: 'currentColor' }}
|
|
8
8
|
viewBox="0 0 16 16"
|
|
9
9
|
width="16"
|
|
@@ -21,7 +21,7 @@ const WebhookIcon = () => {
|
|
|
21
21
|
<svg
|
|
22
22
|
data-testid="geist-icon"
|
|
23
23
|
height="16"
|
|
24
|
-
|
|
24
|
+
strokeLinejoin="round"
|
|
25
25
|
viewBox="0 0 16 16"
|
|
26
26
|
width="16"
|
|
27
27
|
style={{ color: 'currentColor' }}
|
|
@@ -26,8 +26,8 @@ import {
|
|
|
26
26
|
EntityDetailPanel,
|
|
27
27
|
type SelectedSpanInfo,
|
|
28
28
|
} from '../sidebar/entity-detail-panel';
|
|
29
|
-
import {
|
|
30
|
-
import type {
|
|
29
|
+
import { useSidebarData } from '../sidebar/sidebar-data-context';
|
|
30
|
+
import type { TraceWithMeta } from './types';
|
|
31
31
|
import { formatDuration, getHighResInMs } from '../trace-viewer/util/timing';
|
|
32
32
|
import { IconButton } from '../ui/icon-button';
|
|
33
33
|
import { Spinner } from '../ui/spinner';
|
|
@@ -46,12 +46,11 @@ import {
|
|
|
46
46
|
TooltipTrigger,
|
|
47
47
|
} from '../ui/tooltip';
|
|
48
48
|
import { ActiveSpanProvider, useActiveSpan } from './context';
|
|
49
|
-
import { DetailPanel } from './detail-panel';
|
|
50
49
|
import { searchSpans } from './search';
|
|
51
50
|
import { computeRootBounds, computeTimeMarkers } from './utils';
|
|
52
51
|
|
|
53
52
|
interface NewTraceViewerProps {
|
|
54
|
-
trace:
|
|
53
|
+
trace: TraceWithMeta;
|
|
55
54
|
onLoadMore?: () => void | Promise<void>;
|
|
56
55
|
hasMore?: boolean;
|
|
57
56
|
isLoadingMore?: boolean;
|
|
@@ -141,10 +140,10 @@ function useAnimatedViewport(initial: Viewport) {
|
|
|
141
140
|
|
|
142
141
|
function useSelectedSpanInfo(): SelectedSpanInfo | null {
|
|
143
142
|
const { activeSpan } = useActiveSpan();
|
|
144
|
-
const sidebar =
|
|
143
|
+
const sidebar = useSidebarData();
|
|
145
144
|
|
|
146
145
|
return useMemo(() => {
|
|
147
|
-
if (!activeSpan
|
|
146
|
+
if (!activeSpan) return null;
|
|
148
147
|
|
|
149
148
|
const correlationId = activeSpan.spanId;
|
|
150
149
|
const rawEvents = correlationId
|
|
@@ -193,7 +192,7 @@ function NewTraceViewerContent({
|
|
|
193
192
|
const { activeSpan, activeSpanId, setActiveSpan, clearActiveSpan } =
|
|
194
193
|
useActiveSpan();
|
|
195
194
|
|
|
196
|
-
const sidebar =
|
|
195
|
+
const sidebar = useSidebarData();
|
|
197
196
|
const selectedSpan = useSelectedSpanInfo();
|
|
198
197
|
|
|
199
198
|
const [searchQuery, setSearchQuery] = useState('');
|
|
@@ -674,7 +673,7 @@ function NewTraceViewerContent({
|
|
|
674
673
|
</div>
|
|
675
674
|
|
|
676
675
|
{/* Detail panel */}
|
|
677
|
-
{activeSpan
|
|
676
|
+
{activeSpan ? (
|
|
678
677
|
<aside className="flex flex-col h-full max-h-full bg-background-100 border-l border-gray-alpha-400 overflow-auto">
|
|
679
678
|
{/* Panel header */}
|
|
680
679
|
<div className="flex items-center justify-between gap-2 shrink-0 px-4 pt-3 pb-3">
|
|
@@ -750,12 +749,6 @@ function NewTraceViewerContent({
|
|
|
750
749
|
</ErrorBoundary>
|
|
751
750
|
</div>
|
|
752
751
|
</aside>
|
|
753
|
-
) : activeSpan ? (
|
|
754
|
-
<DetailPanel
|
|
755
|
-
span={activeSpan}
|
|
756
|
-
rootStart={root.startTime}
|
|
757
|
-
onClose={clearActiveSpan}
|
|
758
|
-
/>
|
|
759
752
|
) : null}
|
|
760
753
|
</div>
|
|
761
754
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Span, SpanEvent } from '
|
|
1
|
+
import type { Span, SpanEvent } from './types';
|
|
2
2
|
import { formatDuration, getHighResInMs } from '../trace-viewer/util/timing';
|
|
3
3
|
|
|
4
4
|
// ---------------------------------------------------------------------------
|
|
@@ -8,20 +8,17 @@ import { useCallback, useContext, useMemo, useState } from 'react';
|
|
|
8
8
|
import { isEncryptedMarker, isExpiredMarker } from '../../lib/hydration';
|
|
9
9
|
import { useToast } from '../../lib/toast';
|
|
10
10
|
import { extractConversation, isDoStreamStep } from '../../lib/utils';
|
|
11
|
+
import { CopyButton } from '../new-trace-viewer/components/copy-button';
|
|
12
|
+
import { MiddleTruncate } from '../new-trace-viewer/components/middle-truncate/middle-truncate';
|
|
11
13
|
import {
|
|
12
14
|
DecryptClickContext,
|
|
13
15
|
RunClickContext,
|
|
14
16
|
StreamClickContext,
|
|
15
17
|
} from '../ui/data-inspector';
|
|
16
18
|
import { ErrorCard } from '../ui/error-card';
|
|
17
|
-
import {
|
|
18
|
-
ErrorStackBlock,
|
|
19
|
-
isStructuredErrorWithStack,
|
|
20
|
-
} from '../ui/error-stack-block';
|
|
19
|
+
import { ErrorStackBlock, isStructuredError } from '../ui/error-stack-block';
|
|
21
20
|
import { Skeleton } from '../ui/skeleton';
|
|
22
21
|
import { TimestampTooltip } from '../ui/timestamp-tooltip';
|
|
23
|
-
import { CopyButton } from '../new-trace-viewer/components/copy-button';
|
|
24
|
-
import { MiddleTruncate } from '../new-trace-viewer/components/middle-truncate/middle-truncate';
|
|
25
22
|
import { ConversationView } from './conversation-view';
|
|
26
23
|
import { CopyableDataBlock, EncryptedDataBlock } from './copyable-data-block';
|
|
27
24
|
import { DetailCard } from './detail-card';
|
|
@@ -548,9 +545,9 @@ const attributeToDisplayFn: Record<
|
|
|
548
545
|
if (isExpiredMarker(value)) return <ExpiredFieldBlock />;
|
|
549
546
|
if (!hasDisplayContent(value)) return null;
|
|
550
547
|
|
|
551
|
-
//
|
|
552
|
-
//
|
|
553
|
-
if (
|
|
548
|
+
// Structured workflow errors may be persisted as either `{ message }` or
|
|
549
|
+
// `{ message, stack }`. Render both with the dedicated error block.
|
|
550
|
+
if (isStructuredError(value)) {
|
|
554
551
|
return (
|
|
555
552
|
<DetailCard summary="Error" defaultOpen>
|
|
556
553
|
<ErrorStackBlock value={value} />
|
|
@@ -4,10 +4,7 @@ import { EVENT_DATA_REF_FIELDS, type Event } from '@workflow/world';
|
|
|
4
4
|
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
5
5
|
import { hasEncryptedFields, isExpiredMarker } from '../../lib/hydration';
|
|
6
6
|
import { ErrorCard } from '../ui/error-card';
|
|
7
|
-
import {
|
|
8
|
-
ErrorStackBlock,
|
|
9
|
-
isStructuredErrorWithStack,
|
|
10
|
-
} from '../ui/error-stack-block';
|
|
7
|
+
import { ErrorStackBlock, isStructuredError } from '../ui/error-stack-block';
|
|
11
8
|
import { Skeleton } from '../ui/skeleton';
|
|
12
9
|
import { CopyableDataBlock, EncryptedDataBlock } from './copyable-data-block';
|
|
13
10
|
import { DetailCard } from './detail-card';
|
|
@@ -196,8 +193,8 @@ function hasOnlyExpiredFields(data: unknown, eventType: string): boolean {
|
|
|
196
193
|
}
|
|
197
194
|
|
|
198
195
|
/**
|
|
199
|
-
* Renders event data, using ErrorStackBlock for error events that contain
|
|
200
|
-
*
|
|
196
|
+
* Renders event data, using ErrorStackBlock for error events that contain a
|
|
197
|
+
* structured error, and CopyableDataBlock otherwise.
|
|
201
198
|
*/
|
|
202
199
|
function EventDataBlock({
|
|
203
200
|
eventType,
|
|
@@ -239,12 +236,12 @@ function EventDataBlock({
|
|
|
239
236
|
const record = data as Record<string, unknown>;
|
|
240
237
|
|
|
241
238
|
// Check the nested `error` field first (the StructuredError)
|
|
242
|
-
if (
|
|
239
|
+
if (isStructuredError(record.error)) {
|
|
243
240
|
return <ErrorStackBlock value={record.error} />;
|
|
244
241
|
}
|
|
245
242
|
|
|
246
|
-
// Some error formats put the stack at the top level of eventData
|
|
247
|
-
if (
|
|
243
|
+
// Some error formats put the message/stack at the top level of eventData.
|
|
244
|
+
if (isStructuredError(record)) {
|
|
248
245
|
return <ErrorStackBlock value={record} />;
|
|
249
246
|
}
|
|
250
247
|
}
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
type SidebarDataContextValue,
|
|
9
9
|
SidebarDataProvider,
|
|
10
10
|
} from './sidebar/sidebar-data-context';
|
|
11
|
-
import type { Trace } from './trace-viewer/types';
|
|
12
11
|
|
|
13
12
|
const NewTraceViewer = ({
|
|
14
13
|
run,
|
|
@@ -27,14 +26,13 @@ const NewTraceViewer = ({
|
|
|
27
26
|
isLoadingMore?: boolean;
|
|
28
27
|
loading?: boolean;
|
|
29
28
|
}) => {
|
|
30
|
-
const
|
|
29
|
+
const trace: TraceWithMeta | undefined = useMemo(() => {
|
|
31
30
|
if (!run?.runId) {
|
|
32
31
|
return undefined;
|
|
33
32
|
}
|
|
34
33
|
return buildTrace(run, events, new Date());
|
|
35
34
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- `new Date()` is intentionally not a dep
|
|
36
35
|
}, [run, events]);
|
|
37
|
-
const trace = traceWithMeta;
|
|
38
36
|
|
|
39
37
|
if (!trace || (loading && events.length === 0)) {
|
|
40
38
|
return <TraceViewerSkeleton />;
|
|
@@ -44,7 +42,7 @@ const NewTraceViewer = ({
|
|
|
44
42
|
<SidebarDataProvider value={sidebarData}>
|
|
45
43
|
<div className="relative w-full h-full flex">
|
|
46
44
|
<NewTraceViewerComponent
|
|
47
|
-
trace={trace
|
|
45
|
+
trace={trace}
|
|
48
46
|
onLoadMore={onLoadMore}
|
|
49
47
|
hasMore={hasMore}
|
|
50
48
|
isLoadingMore={isLoadingMore}
|
|
@@ -3,18 +3,36 @@
|
|
|
3
3
|
import { AlertCircle } from 'lucide-react';
|
|
4
4
|
import { CopyButton } from '../new-trace-viewer/components/copy-button';
|
|
5
5
|
|
|
6
|
+
export type StructuredErrorRecord = Record<string, unknown> & {
|
|
7
|
+
message?: string;
|
|
8
|
+
stack?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
6
11
|
/**
|
|
7
|
-
* Check whether `value` looks like a structured error object
|
|
8
|
-
*
|
|
12
|
+
* Check whether `value` looks like a structured error object we can render
|
|
13
|
+
* with the error block. Some persisted workflow errors only include a
|
|
14
|
+
* `message`, while runtime errors usually also include `stack`.
|
|
9
15
|
*/
|
|
10
|
-
export function
|
|
16
|
+
export function isStructuredError(
|
|
11
17
|
value: unknown
|
|
12
|
-
): value is
|
|
18
|
+
): value is StructuredErrorRecord {
|
|
13
19
|
return (
|
|
14
20
|
value != null &&
|
|
15
21
|
typeof value === 'object' &&
|
|
16
|
-
'
|
|
17
|
-
|
|
22
|
+
(typeof (value as Record<string, unknown>).message === 'string' ||
|
|
23
|
+
typeof (value as Record<string, unknown>).stack === 'string')
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Narrower guard kept for callers that specifically need a stack trace.
|
|
29
|
+
*/
|
|
30
|
+
export function isStructuredErrorWithStack(
|
|
31
|
+
value: unknown
|
|
32
|
+
): value is StructuredErrorRecord & { stack: string } {
|
|
33
|
+
return (
|
|
34
|
+
isStructuredError(value) &&
|
|
35
|
+
typeof (value as StructuredErrorRecord).stack === 'string'
|
|
18
36
|
);
|
|
19
37
|
}
|
|
20
38
|
|
|
@@ -34,22 +52,22 @@ function deriveTitle(message: string): string {
|
|
|
34
52
|
}
|
|
35
53
|
|
|
36
54
|
/**
|
|
37
|
-
* Renders
|
|
38
|
-
*
|
|
39
|
-
*
|
|
55
|
+
* Renders a structured error as a visually distinct error block. Shows the
|
|
56
|
+
* error message with an alert icon at the top, separated from the stack trace
|
|
57
|
+
* or full message below.
|
|
40
58
|
*/
|
|
41
|
-
export function ErrorStackBlock({
|
|
42
|
-
value
|
|
43
|
-
}: {
|
|
44
|
-
value: Record<string, unknown> & { stack: string };
|
|
45
|
-
}) {
|
|
46
|
-
const stack = value.stack;
|
|
59
|
+
export function ErrorStackBlock({ value }: { value: StructuredErrorRecord }) {
|
|
60
|
+
const stack = typeof value.stack === 'string' ? value.stack : undefined;
|
|
47
61
|
const message = typeof value.message === 'string' ? value.message : undefined;
|
|
48
|
-
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
const
|
|
62
|
+
const body = stack ?? message ?? '';
|
|
63
|
+
// V8's `Error.stack` already starts with `Name: message`; message-only
|
|
64
|
+
// errors use the message as the body so long single-line failures remain
|
|
65
|
+
// readable even when the header truncates.
|
|
66
|
+
const title = message ? deriveTitle(message) : deriveTitle(body);
|
|
67
|
+
const copyText =
|
|
68
|
+
message && stack && !stack.includes(message)
|
|
69
|
+
? `${message}\n\n${stack}`
|
|
70
|
+
: body;
|
|
53
71
|
|
|
54
72
|
return (
|
|
55
73
|
<div
|
|
@@ -76,8 +94,8 @@ export function ErrorStackBlock({
|
|
|
76
94
|
<AlertCircle className="h-4 w-4 shrink-0" />
|
|
77
95
|
<p
|
|
78
96
|
className="text-xs font-semibold m-0 truncate"
|
|
79
|
-
// The full
|
|
80
|
-
//
|
|
97
|
+
// The full message or stack is in the body below; the header just
|
|
98
|
+
// shows the first line, single-line, with overflow
|
|
81
99
|
// ellipsised so a long title doesn't push the copy button or
|
|
82
100
|
// wrap into the framed hint/docs lines.
|
|
83
101
|
title={message}
|
|
@@ -86,15 +104,17 @@ export function ErrorStackBlock({
|
|
|
86
104
|
</p>
|
|
87
105
|
</div>
|
|
88
106
|
)}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
107
|
+
{body && (
|
|
108
|
+
<pre
|
|
109
|
+
className="px-3 py-2.5 text-xs font-mono whitespace-pre-wrap break-words overflow-auto m-0"
|
|
110
|
+
style={{
|
|
111
|
+
color: 'var(--ds-red-900)',
|
|
112
|
+
background: 'var(--ds-red-200)',
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
{body}
|
|
116
|
+
</pre>
|
|
117
|
+
)}
|
|
98
118
|
</div>
|
|
99
119
|
);
|
|
100
120
|
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import type { Span } from '../trace-viewer/types';
|
|
3
|
-
interface DetailPanelProps {
|
|
4
|
-
span: Span;
|
|
5
|
-
rootStart: number;
|
|
6
|
-
onClose: () => void;
|
|
7
|
-
}
|
|
8
|
-
export declare function DetailPanel({ span, rootStart, onClose, }: DetailPanelProps): ReactNode;
|
|
9
|
-
export {};
|
|
10
|
-
//# sourceMappingURL=detail-panel.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"detail-panel.d.ts","sourceRoot":"","sources":["../../../src/components/new-trace-viewer/detail-panel.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAKlD,UAAU,gBAAgB;IACxB,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,SAAS,EACT,OAAO,GACR,EAAE,gBAAgB,GAAG,SAAS,CAmC9B"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { X } from 'lucide-react';
|
|
4
|
-
import { formatDuration, getHighResInMs } from '../trace-viewer/util/timing';
|
|
5
|
-
import { IconButton } from '../ui/icon-button';
|
|
6
|
-
import { getSpanDurationMs } from './utils';
|
|
7
|
-
export function DetailPanel({ span, rootStart, onClose, }) {
|
|
8
|
-
const startMs = getHighResInMs(span.startTime);
|
|
9
|
-
const durationMs = getSpanDurationMs(span);
|
|
10
|
-
const offsetMs = startMs - rootStart;
|
|
11
|
-
return (_jsxs("aside", { className: "grid h-full max-h-full grid-rows-[2.5rem_1fr] bg-background-200", children: [_jsxs("div", { className: "flex items-center justify-between px-3 border-b border-gray-alpha-400", children: [_jsx("span", { className: "text-sm font-medium text-gray-1000 truncate", children: span.name }), _jsx(IconButton, { "aria-label": "Close span details", onClick: onClose, children: _jsx(X, { className: "w-4 h-4" }) })] }), _jsx("div", { className: "overflow-y-auto p-3 space-y-3", children: _jsxs("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1.5 text-sm", children: [_jsx("dt", { className: "text-gray-900", children: "Resource" }), _jsx("dd", { className: "text-gray-1000 font-mono", children: span.resource }), _jsx("dt", { className: "text-gray-900", children: "Duration" }), _jsx("dd", { className: "text-gray-1000 tabular-nums font-mono", children: formatDuration(durationMs) }), _jsx("dt", { className: "text-gray-900", children: "Offset" }), _jsxs("dd", { className: "text-gray-1000 tabular-nums font-mono", children: ["+", formatDuration(offsetMs)] }), _jsx("dt", { className: "text-gray-900", children: "Status" }), _jsx("dd", { className: "text-gray-1000 font-mono", children: span.status.code === 2 ? 'Error' : 'OK' })] }) })] }));
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGV0YWlsLXBhbmVsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbmV3LXRyYWNlLXZpZXdlci9kZXRhaWwtcGFuZWwudHN4Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVksQ0FBQzs7QUFFYixPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBR2pDLE9BQU8sRUFBRSxjQUFjLEVBQUUsY0FBYyxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDN0UsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQy9DLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQVE1QyxNQUFNLFVBQVUsV0FBVyxDQUFDLEVBQzFCLElBQUksRUFDSixTQUFTLEVBQ1QsT0FBTyxHQUNVO0lBQ2pCLE1BQU0sT0FBTyxHQUFHLGNBQWMsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDL0MsTUFBTSxVQUFVLEdBQUcsaUJBQWlCLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDM0MsTUFBTSxRQUFRLEdBQUcsT0FBTyxHQUFHLFNBQVMsQ0FBQztJQUVyQyxPQUFPLENBQ0wsaUJBQU8sU0FBUyxFQUFDLGlFQUFpRSxhQUNoRixlQUFLLFNBQVMsRUFBQyx1RUFBdUUsYUFDcEYsZUFBTSxTQUFTLEVBQUMsNkNBQTZDLFlBQzFELElBQUksQ0FBQyxJQUFJLEdBQ0wsRUFDUCxLQUFDLFVBQVUsa0JBQVksb0JBQW9CLEVBQUMsT0FBTyxFQUFFLE9BQU8sWUFDMUQsS0FBQyxDQUFDLElBQUMsU0FBUyxFQUFDLFNBQVMsR0FBRyxHQUNkLElBQ1QsRUFDTixjQUFLLFNBQVMsRUFBQywrQkFBK0IsWUFDNUMsY0FBSSxTQUFTLEVBQUMscURBQXFELGFBQ2pFLGFBQUksU0FBUyxFQUFDLGVBQWUseUJBQWMsRUFDM0MsYUFBSSxTQUFTLEVBQUMsMEJBQTBCLFlBQUUsSUFBSSxDQUFDLFFBQVEsR0FBTSxFQUM3RCxhQUFJLFNBQVMsRUFBQyxlQUFlLHlCQUFjLEVBQzNDLGFBQUksU0FBUyxFQUFDLHVDQUF1QyxZQUNsRCxjQUFjLENBQUMsVUFBVSxDQUFDLEdBQ3hCLEVBQ0wsYUFBSSxTQUFTLEVBQUMsZUFBZSx1QkFBWSxFQUN6QyxjQUFJLFNBQVMsRUFBQyx1Q0FBdUMsa0JBQ2pELGNBQWMsQ0FBQyxRQUFRLENBQUMsSUFDdkIsRUFDTCxhQUFJLFNBQVMsRUFBQyxlQUFlLHVCQUFZLEVBQ3pDLGFBQUksU0FBUyxFQUFDLDBCQUEwQixZQUNyQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxHQUNyQyxJQUNGLEdBQ0QsSUFDQSxDQUNULENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBjbGllbnQnO1xuXG5pbXBvcnQgeyBYIH0gZnJvbSAnbHVjaWRlLXJlYWN0JztcbmltcG9ydCB0eXBlIHsgUmVhY3ROb2RlIH0gZnJvbSAncmVhY3QnO1xuaW1wb3J0IHR5cGUgeyBTcGFuIH0gZnJvbSAnLi4vdHJhY2Utdmlld2VyL3R5cGVzJztcbmltcG9ydCB7IGZvcm1hdER1cmF0aW9uLCBnZXRIaWdoUmVzSW5NcyB9IGZyb20gJy4uL3RyYWNlLXZpZXdlci91dGlsL3RpbWluZyc7XG5pbXBvcnQgeyBJY29uQnV0dG9uIH0gZnJvbSAnLi4vdWkvaWNvbi1idXR0b24nO1xuaW1wb3J0IHsgZ2V0U3BhbkR1cmF0aW9uTXMgfSBmcm9tICcuL3V0aWxzJztcblxuaW50ZXJmYWNlIERldGFpbFBhbmVsUHJvcHMge1xuICBzcGFuOiBTcGFuO1xuICByb290U3RhcnQ6IG51bWJlcjtcbiAgb25DbG9zZTogKCkgPT4gdm9pZDtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIERldGFpbFBhbmVsKHtcbiAgc3BhbixcbiAgcm9vdFN0YXJ0LFxuICBvbkNsb3NlLFxufTogRGV0YWlsUGFuZWxQcm9wcyk6IFJlYWN0Tm9kZSB7XG4gIGNvbnN0IHN0YXJ0TXMgPSBnZXRIaWdoUmVzSW5NcyhzcGFuLnN0YXJ0VGltZSk7XG4gIGNvbnN0IGR1cmF0aW9uTXMgPSBnZXRTcGFuRHVyYXRpb25NcyhzcGFuKTtcbiAgY29uc3Qgb2Zmc2V0TXMgPSBzdGFydE1zIC0gcm9vdFN0YXJ0O1xuXG4gIHJldHVybiAoXG4gICAgPGFzaWRlIGNsYXNzTmFtZT1cImdyaWQgaC1mdWxsIG1heC1oLWZ1bGwgZ3JpZC1yb3dzLVsyLjVyZW1fMWZyXSBiZy1iYWNrZ3JvdW5kLTIwMFwiPlxuICAgICAgPGRpdiBjbGFzc05hbWU9XCJmbGV4IGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWJldHdlZW4gcHgtMyBib3JkZXItYiBib3JkZXItZ3JheS1hbHBoYS00MDBcIj5cbiAgICAgICAgPHNwYW4gY2xhc3NOYW1lPVwidGV4dC1zbSBmb250LW1lZGl1bSB0ZXh0LWdyYXktMTAwMCB0cnVuY2F0ZVwiPlxuICAgICAgICAgIHtzcGFuLm5hbWV9XG4gICAgICAgIDwvc3Bhbj5cbiAgICAgICAgPEljb25CdXR0b24gYXJpYS1sYWJlbD1cIkNsb3NlIHNwYW4gZGV0YWlsc1wiIG9uQ2xpY2s9e29uQ2xvc2V9PlxuICAgICAgICAgIDxYIGNsYXNzTmFtZT1cInctNCBoLTRcIiAvPlxuICAgICAgICA8L0ljb25CdXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICAgIDxkaXYgY2xhc3NOYW1lPVwib3ZlcmZsb3cteS1hdXRvIHAtMyBzcGFjZS15LTNcIj5cbiAgICAgICAgPGRsIGNsYXNzTmFtZT1cImdyaWQgZ3JpZC1jb2xzLVthdXRvXzFmcl0gZ2FwLXgtMyBnYXAteS0xLjUgdGV4dC1zbVwiPlxuICAgICAgICAgIDxkdCBjbGFzc05hbWU9XCJ0ZXh0LWdyYXktOTAwXCI+UmVzb3VyY2U8L2R0PlxuICAgICAgICAgIDxkZCBjbGFzc05hbWU9XCJ0ZXh0LWdyYXktMTAwMCBmb250LW1vbm9cIj57c3Bhbi5yZXNvdXJjZX08L2RkPlxuICAgICAgICAgIDxkdCBjbGFzc05hbWU9XCJ0ZXh0LWdyYXktOTAwXCI+RHVyYXRpb248L2R0PlxuICAgICAgICAgIDxkZCBjbGFzc05hbWU9XCJ0ZXh0LWdyYXktMTAwMCB0YWJ1bGFyLW51bXMgZm9udC1tb25vXCI+XG4gICAgICAgICAgICB7Zm9ybWF0RHVyYXRpb24oZHVyYXRpb25Ncyl9XG4gICAgICAgICAgPC9kZD5cbiAgICAgICAgICA8ZHQgY2xhc3NOYW1lPVwidGV4dC1ncmF5LTkwMFwiPk9mZnNldDwvZHQ+XG4gICAgICAgICAgPGRkIGNsYXNzTmFtZT1cInRleHQtZ3JheS0xMDAwIHRhYnVsYXItbnVtcyBmb250LW1vbm9cIj5cbiAgICAgICAgICAgICt7Zm9ybWF0RHVyYXRpb24ob2Zmc2V0TXMpfVxuICAgICAgICAgIDwvZGQ+XG4gICAgICAgICAgPGR0IGNsYXNzTmFtZT1cInRleHQtZ3JheS05MDBcIj5TdGF0dXM8L2R0PlxuICAgICAgICAgIDxkZCBjbGFzc05hbWU9XCJ0ZXh0LWdyYXktMTAwMCBmb250LW1vbm9cIj5cbiAgICAgICAgICAgIHtzcGFuLnN0YXR1cy5jb2RlID09PSAyID8gJ0Vycm9yJyA6ICdPSyd9XG4gICAgICAgICAgPC9kZD5cbiAgICAgICAgPC9kbD5cbiAgICAgIDwvZGl2PlxuICAgIDwvYXNpZGU+XG4gICk7XG59XG4iXX0=
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/components/new-trace-viewer/util/constants.ts"],"names":[],"mappings":""}
|