@workflow/web-shared 5.0.0-beta.12 → 5.0.0-beta.14
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 +6 -4
- 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 +7 -7
- 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 +14 -10
- package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.js +12 -11
- package/dist/components/sidebar/events-list.d.ts +3 -1
- package/dist/components/sidebar/events-list.d.ts.map +1 -1
- package/dist/components/sidebar/events-list.js +10 -9
- package/dist/components/sidebar/span-detail-merge.d.ts +16 -0
- package/dist/components/sidebar/span-detail-merge.d.ts.map +1 -0
- package/dist/components/sidebar/span-detail-merge.js +53 -0
- package/dist/components/trace-viewer/util/timing.d.ts +2 -2
- package/dist/components/trace-viewer/util/timing.d.ts.map +1 -1
- package/dist/components/trace-viewer/util/timing.js +3 -3
- 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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/lib/utils.d.ts +11 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +49 -1
- package/package.json +3 -3
- package/src/components/event-list-view.tsx +12 -11
- package/src/components/new-trace-viewer/components/event-list.tsx +6 -5
- package/src/components/new-trace-viewer/components/timeline.tsx +11 -7
- 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 +13 -12
- package/src/components/sidebar/entity-detail-panel.tsx +28 -13
- package/src/components/sidebar/events-list.tsx +40 -34
- package/src/components/sidebar/span-detail-merge.ts +65 -0
- package/src/components/trace-viewer/util/timing.ts +2 -2
- package/src/components/trace-viewer-new.tsx +2 -4
- package/src/components/ui/error-stack-block.tsx +51 -31
- package/src/index.ts +1 -0
- package/src/lib/utils.ts +57 -0
- 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
|
@@ -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';
|
|
@@ -264,6 +261,7 @@ const attributeOrder: AttributeKey[] = [
|
|
|
264
261
|
'eventData',
|
|
265
262
|
'input',
|
|
266
263
|
'output',
|
|
264
|
+
'attributes',
|
|
267
265
|
'resumeAt',
|
|
268
266
|
];
|
|
269
267
|
|
|
@@ -287,6 +285,7 @@ const attributeDisplayNames: Partial<Record<AttributeKey, string>> = {
|
|
|
287
285
|
runId: 'Run ID',
|
|
288
286
|
token: 'Token',
|
|
289
287
|
eventType: 'Event Type',
|
|
288
|
+
errorCode: 'Error Code',
|
|
290
289
|
correlationId: 'Correlation ID',
|
|
291
290
|
deploymentId: 'Deployment ID',
|
|
292
291
|
specVersion: 'Spec Version',
|
|
@@ -418,11 +417,11 @@ const attributeToDisplayFn: Record<
|
|
|
418
417
|
environment: (_value: unknown) => null,
|
|
419
418
|
executionContext: (_value: unknown) => null,
|
|
420
419
|
// Attributes MVP — string-string metadata attached to the run.
|
|
421
|
-
// Rendered
|
|
422
|
-
// hasDisplayContent gate
|
|
420
|
+
// Rendered in its own collapsible DetailCard; if empty/missing, hidden
|
|
421
|
+
// by the hasDisplayContent gate.
|
|
423
422
|
attributes: (value: unknown) => {
|
|
424
423
|
if (!hasDisplayContent(value)) return null;
|
|
425
|
-
return JsonBlock(value)
|
|
424
|
+
return <DetailCard summary="Attributes">{JsonBlock(value)}</DetailCard>;
|
|
426
425
|
},
|
|
427
426
|
// Dates — wrapped with TimestampTooltip showing UTC/local + relative time
|
|
428
427
|
createdAt: timestampWithTooltipOrNull,
|
|
@@ -548,9 +547,9 @@ const attributeToDisplayFn: Record<
|
|
|
548
547
|
if (isExpiredMarker(value)) return <ExpiredFieldBlock />;
|
|
549
548
|
if (!hasDisplayContent(value)) return null;
|
|
550
549
|
|
|
551
|
-
//
|
|
552
|
-
//
|
|
553
|
-
if (
|
|
550
|
+
// Structured workflow errors may be persisted as either `{ message }` or
|
|
551
|
+
// `{ message, stack }`. Render both with the dedicated error block.
|
|
552
|
+
if (isStructuredError(value)) {
|
|
554
553
|
return (
|
|
555
554
|
<DetailCard summary="Error" defaultOpen>
|
|
556
555
|
<ErrorStackBlock value={value} />
|
|
@@ -591,6 +590,7 @@ const resolvableAttributes = [
|
|
|
591
590
|
'output',
|
|
592
591
|
'error',
|
|
593
592
|
'metadata',
|
|
593
|
+
'attributes',
|
|
594
594
|
'eventData',
|
|
595
595
|
];
|
|
596
596
|
|
|
@@ -600,6 +600,7 @@ const selfHeaderedAttributes = new Set([
|
|
|
600
600
|
'input',
|
|
601
601
|
'output',
|
|
602
602
|
'error',
|
|
603
|
+
'attributes',
|
|
603
604
|
'eventData',
|
|
604
605
|
]);
|
|
605
606
|
|
|
@@ -10,6 +10,10 @@ import { AttributePanel } from './attribute-panel';
|
|
|
10
10
|
import { EventsList } from './events-list';
|
|
11
11
|
import { ResolveHookModal } from './resolve-hook-modal';
|
|
12
12
|
import { useSidebarDataOptional } from './sidebar-data-context';
|
|
13
|
+
import {
|
|
14
|
+
mergeSpanDetail,
|
|
15
|
+
spanDetailMatchesSelection,
|
|
16
|
+
} from './span-detail-merge';
|
|
13
17
|
|
|
14
18
|
// Type guards for runtime validation of span attribute data
|
|
15
19
|
function isStep(data: unknown): data is Step {
|
|
@@ -215,19 +219,27 @@ export function EntityDetailPanel({
|
|
|
215
219
|
const error = spanDetailError ?? undefined;
|
|
216
220
|
const loading = spanDetailLoading ?? false;
|
|
217
221
|
|
|
222
|
+
const matchedSpanDetailData = useMemo(
|
|
223
|
+
() =>
|
|
224
|
+
spanDetailMatchesSelection(spanDetailData, resource, resourceId)
|
|
225
|
+
? spanDetailData
|
|
226
|
+
: null,
|
|
227
|
+
[spanDetailData, resource, resourceId]
|
|
228
|
+
);
|
|
229
|
+
|
|
218
230
|
// Get the hook token for resolving (prefer fetched data, then hooks array fallback)
|
|
219
231
|
const hookToken = useMemo(() => {
|
|
220
232
|
if (resource !== 'hook' || !resourceId) return undefined;
|
|
221
233
|
// 1. Try the externally-fetched detail data first
|
|
222
|
-
if (isHook(
|
|
223
|
-
return
|
|
234
|
+
if (isHook(matchedSpanDetailData) && matchedSpanDetailData.token) {
|
|
235
|
+
return matchedSpanDetailData.token;
|
|
224
236
|
}
|
|
225
237
|
// 2. Try the span's inline data (reconstructed from hook_created event)
|
|
226
238
|
if (isHook(data) && (data as Hook).token) {
|
|
227
239
|
return (data as Hook).token;
|
|
228
240
|
}
|
|
229
241
|
return undefined;
|
|
230
|
-
}, [resource, resourceId,
|
|
242
|
+
}, [resource, resourceId, matchedSpanDetailData, data]);
|
|
231
243
|
|
|
232
244
|
useEffect(() => {
|
|
233
245
|
if (error && selectedSpan && resource) {
|
|
@@ -289,7 +301,7 @@ export function EntityDetailPanel({
|
|
|
289
301
|
|
|
290
302
|
try {
|
|
291
303
|
setResolvingHook(true);
|
|
292
|
-
const candidate =
|
|
304
|
+
const candidate = matchedSpanDetailData ?? data;
|
|
293
305
|
const hook = isHook(candidate) ? candidate : undefined;
|
|
294
306
|
await onResolveHook(hookToken, payload, hook);
|
|
295
307
|
toast.success('Hook resolved', {
|
|
@@ -310,17 +322,18 @@ export function EntityDetailPanel({
|
|
|
310
322
|
setResolvingHook(false);
|
|
311
323
|
}
|
|
312
324
|
},
|
|
313
|
-
[onResolveHook, hookToken, resolvingHook,
|
|
325
|
+
[onResolveHook, hookToken, resolvingHook, matchedSpanDetailData, data]
|
|
314
326
|
);
|
|
315
327
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
328
|
+
const displayData = useMemo(
|
|
329
|
+
() =>
|
|
330
|
+
mergeSpanDetail(data, matchedSpanDetailData) as
|
|
331
|
+
| WorkflowRun
|
|
332
|
+
| Step
|
|
333
|
+
| Hook
|
|
334
|
+
| Event,
|
|
335
|
+
[data, matchedSpanDetailData]
|
|
336
|
+
);
|
|
324
337
|
|
|
325
338
|
const moduleSpecifier = useMemo(() => {
|
|
326
339
|
const displayRecord = displayData as Record<string, unknown>;
|
|
@@ -435,6 +448,8 @@ export function EntityDetailPanel({
|
|
|
435
448
|
<EventsList
|
|
436
449
|
events={rawEvents}
|
|
437
450
|
onLoadEventData={onLoadEventData}
|
|
451
|
+
onStreamClick={onStreamClick}
|
|
452
|
+
onRunClick={onRunClick}
|
|
438
453
|
encryptionKey={encryptionKey}
|
|
439
454
|
/>
|
|
440
455
|
)}
|
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
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
|
+
import { RunClickContext, StreamClickContext } from '../ui/data-inspector';
|
|
6
7
|
import { ErrorCard } from '../ui/error-card';
|
|
7
|
-
import {
|
|
8
|
-
ErrorStackBlock,
|
|
9
|
-
isStructuredErrorWithStack,
|
|
10
|
-
} from '../ui/error-stack-block';
|
|
8
|
+
import { ErrorStackBlock, isStructuredError } from '../ui/error-stack-block';
|
|
11
9
|
import { Skeleton } from '../ui/skeleton';
|
|
12
10
|
import { CopyableDataBlock, EncryptedDataBlock } from './copyable-data-block';
|
|
13
11
|
import { DetailCard } from './detail-card';
|
|
@@ -196,8 +194,8 @@ function hasOnlyExpiredFields(data: unknown, eventType: string): boolean {
|
|
|
196
194
|
}
|
|
197
195
|
|
|
198
196
|
/**
|
|
199
|
-
* Renders event data, using ErrorStackBlock for error events that contain
|
|
200
|
-
*
|
|
197
|
+
* Renders event data, using ErrorStackBlock for error events that contain a
|
|
198
|
+
* structured error, and CopyableDataBlock otherwise.
|
|
201
199
|
*/
|
|
202
200
|
function EventDataBlock({
|
|
203
201
|
eventType,
|
|
@@ -239,12 +237,12 @@ function EventDataBlock({
|
|
|
239
237
|
const record = data as Record<string, unknown>;
|
|
240
238
|
|
|
241
239
|
// Check the nested `error` field first (the StructuredError)
|
|
242
|
-
if (
|
|
240
|
+
if (isStructuredError(record.error)) {
|
|
243
241
|
return <ErrorStackBlock value={record.error} />;
|
|
244
242
|
}
|
|
245
243
|
|
|
246
|
-
// Some error formats put the stack at the top level of eventData
|
|
247
|
-
if (
|
|
244
|
+
// Some error formats put the message/stack at the top level of eventData.
|
|
245
|
+
if (isStructuredError(record)) {
|
|
248
246
|
return <ErrorStackBlock value={record} />;
|
|
249
247
|
}
|
|
250
248
|
}
|
|
@@ -259,6 +257,8 @@ export function EventsList({
|
|
|
259
257
|
isLoading = false,
|
|
260
258
|
error,
|
|
261
259
|
onLoadEventData,
|
|
260
|
+
onStreamClick,
|
|
261
|
+
onRunClick,
|
|
262
262
|
encryptionKey,
|
|
263
263
|
}: {
|
|
264
264
|
events: Event[];
|
|
@@ -268,6 +268,8 @@ export function EventsList({
|
|
|
268
268
|
correlationId: string,
|
|
269
269
|
eventId: string
|
|
270
270
|
) => Promise<unknown | null>;
|
|
271
|
+
onStreamClick?: (streamId: string) => void;
|
|
272
|
+
onRunClick?: (runId: string) => void;
|
|
271
273
|
/** When provided, signals that decryption is active (triggers re-load of expanded events) */
|
|
272
274
|
encryptionKey?: Uint8Array;
|
|
273
275
|
}) {
|
|
@@ -288,31 +290,35 @@ export function EventsList({
|
|
|
288
290
|
}
|
|
289
291
|
|
|
290
292
|
return (
|
|
291
|
-
<
|
|
292
|
-
{
|
|
293
|
-
<
|
|
294
|
-
{
|
|
295
|
-
<div
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
293
|
+
<RunClickContext.Provider value={onRunClick}>
|
|
294
|
+
<StreamClickContext.Provider value={onStreamClick}>
|
|
295
|
+
<DetailCard summary="Events" contentClassName="mb-0" defaultOpen>
|
|
296
|
+
{isLoading ? (
|
|
297
|
+
<div className="flex flex-col -mx-4">
|
|
298
|
+
{[0, 1, 2].map((i) => (
|
|
299
|
+
<div
|
|
300
|
+
key={i}
|
|
301
|
+
className="flex items-center justify-between gap-3 bg-background-200 px-4 py-2"
|
|
302
|
+
>
|
|
303
|
+
<Skeleton className="h-4 w-32 rounded" />
|
|
304
|
+
<Skeleton className="h-3 w-16 rounded" />
|
|
305
|
+
</div>
|
|
306
|
+
))}
|
|
301
307
|
</div>
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
)
|
|
314
|
-
</
|
|
315
|
-
|
|
316
|
-
</
|
|
308
|
+
) : (
|
|
309
|
+
<div className="flex flex-col -mx-4">
|
|
310
|
+
{sortedEvents.map((event) => (
|
|
311
|
+
<EventItem
|
|
312
|
+
key={event.eventId}
|
|
313
|
+
event={event}
|
|
314
|
+
onLoadEventData={onLoadEventData}
|
|
315
|
+
encryptionKey={encryptionKey}
|
|
316
|
+
/>
|
|
317
|
+
))}
|
|
318
|
+
</div>
|
|
319
|
+
)}
|
|
320
|
+
</DetailCard>
|
|
321
|
+
</StreamClickContext.Provider>
|
|
322
|
+
</RunClickContext.Provider>
|
|
317
323
|
);
|
|
318
324
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const hasField = (
|
|
2
|
+
value: object,
|
|
3
|
+
key: string
|
|
4
|
+
): value is Record<string, unknown> => key in value;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns true when the fetched `detail` belongs to the current selection.
|
|
8
|
+
* The fetch lags selection, so it can briefly hold a previously selected span
|
|
9
|
+
* (even a different resource type); reject it before merging or its fields
|
|
10
|
+
* union into the wrong panel. Steps/hooks/waits carry their parent `runId`, so
|
|
11
|
+
* a `run` selection excludes objects identifiable as a child resource.
|
|
12
|
+
*/
|
|
13
|
+
export function spanDetailMatchesSelection(
|
|
14
|
+
detail: unknown,
|
|
15
|
+
resource: string | undefined,
|
|
16
|
+
resourceId: string | undefined
|
|
17
|
+
): boolean {
|
|
18
|
+
if (!detail || typeof detail !== 'object' || !resource || !resourceId) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
switch (resource) {
|
|
22
|
+
case 'step':
|
|
23
|
+
return hasField(detail, 'stepId') && detail.stepId === resourceId;
|
|
24
|
+
case 'hook':
|
|
25
|
+
return hasField(detail, 'hookId') && detail.hookId === resourceId;
|
|
26
|
+
case 'sleep':
|
|
27
|
+
return hasField(detail, 'waitId') && detail.waitId === resourceId;
|
|
28
|
+
case 'run':
|
|
29
|
+
return (
|
|
30
|
+
hasField(detail, 'runId') &&
|
|
31
|
+
!('stepId' in detail) &&
|
|
32
|
+
!('hookId' in detail) &&
|
|
33
|
+
!('waitId' in detail) &&
|
|
34
|
+
detail.runId === resourceId
|
|
35
|
+
);
|
|
36
|
+
default:
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Merges fetched `detail` over the span's own data. The detail supplies the
|
|
43
|
+
* heavy fields the trace strips (input/output/error/metadata, sleep resumeAt);
|
|
44
|
+
* the span's identity, status, and event-derived timestamps stay authoritative
|
|
45
|
+
* so they don't flicker to the backend row's millisecond-different values.
|
|
46
|
+
*/
|
|
47
|
+
export function mergeSpanDetail(spanData: unknown, detail: unknown): unknown {
|
|
48
|
+
if (!detail || typeof detail !== 'object') {
|
|
49
|
+
return spanData;
|
|
50
|
+
}
|
|
51
|
+
if (!spanData || typeof spanData !== 'object') {
|
|
52
|
+
return detail;
|
|
53
|
+
}
|
|
54
|
+
// Skip `undefined` span fields so they don't clobber a value the detail
|
|
55
|
+
// legitimately provides (e.g. a step's optional startedAt).
|
|
56
|
+
const merged: Record<string, unknown> = { ...detail };
|
|
57
|
+
for (const [key, value] of Object.entries(
|
|
58
|
+
spanData as Record<string, unknown>
|
|
59
|
+
)) {
|
|
60
|
+
if (value !== undefined) {
|
|
61
|
+
merged[key] = value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return merged;
|
|
65
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { formatDuration } from '../../../lib/utils';
|
|
1
|
+
import { formatDuration, formatDurationPrecise } from '../../../lib/utils';
|
|
2
2
|
|
|
3
|
-
export { formatDuration };
|
|
3
|
+
export { formatDuration, formatDurationPrecise };
|
|
4
4
|
|
|
5
5
|
export function getHighResInMs([seconds, nanoseconds]: [
|
|
6
6
|
number,
|
|
@@ -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
|
}
|
package/src/index.ts
CHANGED
package/src/lib/utils.ts
CHANGED
|
@@ -80,6 +80,63 @@ export function formatDuration(ms: number, compact = false): string {
|
|
|
80
80
|
return parts.join(' ');
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
// Locale-aware formatter that always renders exactly two fraction digits.
|
|
84
|
+
// Used for the seconds component of precise durations so values are never
|
|
85
|
+
// snapped to a whole second (and thousands separators stay correct).
|
|
86
|
+
const preciseSecondsFormatter = new Intl.NumberFormat(undefined, {
|
|
87
|
+
minimumFractionDigits: 2,
|
|
88
|
+
maximumFractionDigits: 2,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Formats a duration in milliseconds without snapping to whole seconds.
|
|
93
|
+
*
|
|
94
|
+
* Unlike {@link formatDuration}, this never rounds a sub-minute value up to
|
|
95
|
+
* the next whole second (e.g. 1626ms renders as "1.63s", not "2s").
|
|
96
|
+
*
|
|
97
|
+
* - < 1s: shows whole milliseconds (e.g. "626ms")
|
|
98
|
+
* - 1s – 1m: shows seconds with two decimals (e.g. "1.63s", "45.20s")
|
|
99
|
+
* - >= 1m: decomposes into d/h/m with two-decimal seconds (e.g. "1m 13.45s")
|
|
100
|
+
*/
|
|
101
|
+
export function formatDurationPrecise(ms: number): string {
|
|
102
|
+
if (ms === 0) {
|
|
103
|
+
return '0s';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (ms < MS_IN_SECOND) {
|
|
107
|
+
const roundedMs = Math.round(ms);
|
|
108
|
+
if (roundedMs < MS_IN_SECOND) {
|
|
109
|
+
return `${roundedMs}ms`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const normalizedMs = Math.round(ms / 10) * 10;
|
|
114
|
+
|
|
115
|
+
if (normalizedMs < MS_IN_MINUTE) {
|
|
116
|
+
return `${preciseSecondsFormatter.format(normalizedMs / MS_IN_SECOND)}s`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const days = Math.floor(normalizedMs / MS_IN_DAY);
|
|
120
|
+
const hours = Math.floor((normalizedMs % MS_IN_DAY) / MS_IN_HOUR);
|
|
121
|
+
const minutes = Math.floor((normalizedMs % MS_IN_HOUR) / MS_IN_MINUTE);
|
|
122
|
+
const seconds = (normalizedMs % MS_IN_MINUTE) / MS_IN_SECOND;
|
|
123
|
+
|
|
124
|
+
const parts: string[] = [];
|
|
125
|
+
|
|
126
|
+
if (days > 0) {
|
|
127
|
+
parts.push(`${days}d`);
|
|
128
|
+
}
|
|
129
|
+
if (hours > 0) {
|
|
130
|
+
parts.push(`${hours}h`);
|
|
131
|
+
}
|
|
132
|
+
if (minutes > 0) {
|
|
133
|
+
parts.push(`${minutes}m`);
|
|
134
|
+
}
|
|
135
|
+
parts.push(`${preciseSecondsFormatter.format(seconds)}s`);
|
|
136
|
+
|
|
137
|
+
return parts.join(' ');
|
|
138
|
+
}
|
|
139
|
+
|
|
83
140
|
/**
|
|
84
141
|
* Returns a formatted pagination display string
|
|
85
142
|
* @param currentPage - The current page number
|
|
@@ -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":""}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvbmV3LXRyYWNlLXZpZXdlci91dGlsL2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIiwic291cmNlc0NvbnRlbnQiOlsiIl19
|