@workflow/web-shared 5.0.0-beta.40 → 5.0.0-beta.42
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 +10 -2
- package/dist/components/event-list-view.d.ts.map +1 -1
- package/dist/components/event-list-view.js +52 -26
- package/dist/components/sidebar/attributes-block.d.ts.map +1 -1
- package/dist/components/sidebar/attributes-block.js +3 -6
- package/dist/components/sidebar/conversation-view.d.ts.map +1 -1
- package/dist/components/sidebar/conversation-view.js +19 -36
- package/dist/components/sidebar/entity-detail-panel.d.ts +5 -0
- package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -1
- package/dist/components/sidebar/entity-detail-panel.js +2 -2
- package/dist/components/sidebar/events-list.d.ts +7 -1
- package/dist/components/sidebar/events-list.d.ts.map +1 -1
- package/dist/components/sidebar/events-list.js +6 -5
- package/dist/components/sidebar/resolve-hook-modal.d.ts.map +1 -1
- package/dist/components/sidebar/resolve-hook-modal.js +4 -145
- package/dist/components/sidebar/sidebar-data-context.d.ts +6 -0
- package/dist/components/sidebar/sidebar-data-context.d.ts.map +1 -1
- package/dist/components/sidebar/sidebar-data-context.js +1 -1
- package/dist/components/stream-viewer-skeleton.js +6 -6
- package/dist/components/stream-viewer.js +2 -2
- package/dist/components/trace-viewer/components/detail-panel.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/detail-panel.js +2 -1
- package/dist/components/trace-viewer/components/timeline.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/timeline.js +13 -22
- package/dist/components/trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -1
- package/dist/components/trace-viewer/components/trace-viewer-skeleton.js +58 -18
- package/dist/components/trace-viewer/icons.js +5 -5
- package/dist/components/trace-viewer/utils.d.ts +6 -10
- package/dist/components/trace-viewer/utils.d.ts.map +1 -1
- package/dist/components/trace-viewer/utils.js +14 -24
- package/dist/components/trace-viewer.d.ts.map +1 -1
- package/dist/components/trace-viewer.js +12 -4
- package/dist/components/ui/context-card.d.ts.map +1 -1
- package/dist/components/ui/context-card.js +9 -21
- package/dist/components/ui/data-inspector.d.ts.map +1 -1
- package/dist/components/ui/data-inspector.js +8 -35
- package/dist/components/ui/duplicate-event-tooltip.d.ts +15 -0
- package/dist/components/ui/duplicate-event-tooltip.d.ts.map +1 -0
- package/dist/components/ui/duplicate-event-tooltip.js +19 -0
- package/dist/components/ui/menu-dropdown.d.ts.map +1 -1
- package/dist/components/ui/menu-dropdown.js +3 -19
- package/dist/components/ui/skeleton.js +2 -2
- package/dist/components/ui/spinner.d.ts.map +1 -1
- package/dist/components/ui/spinner.js +5 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/lib/duplicate-events.d.ts +27 -0
- package/dist/lib/duplicate-events.d.ts.map +1 -0
- package/dist/lib/duplicate-events.js +166 -0
- package/dist/lib/event-materialization.d.ts +15 -2
- package/dist/lib/event-materialization.d.ts.map +1 -1
- package/dist/lib/event-materialization.js +19 -4
- package/dist/lib/trace-builder.d.ts +16 -1
- package/dist/lib/trace-builder.d.ts.map +1 -1
- package/dist/lib/trace-builder.js +23 -4
- package/package.json +4 -4
- package/src/components/event-list-view.tsx +70 -32
- package/src/components/sidebar/attributes-block.tsx +4 -11
- package/src/components/sidebar/conversation-view.tsx +25 -79
- package/src/components/sidebar/entity-detail-panel.tsx +6 -0
- package/src/components/sidebar/events-list.tsx +21 -3
- package/src/components/sidebar/resolve-hook-modal.tsx +23 -186
- package/src/components/sidebar/sidebar-data-context.tsx +6 -0
- package/src/components/stream-viewer-skeleton.tsx +5 -5
- package/src/components/stream-viewer.tsx +1 -1
- package/src/components/trace-viewer/components/detail-panel.tsx +1 -0
- package/src/components/trace-viewer/components/timeline.tsx +21 -39
- package/src/components/trace-viewer/components/trace-viewer-skeleton.tsx +63 -35
- package/src/components/trace-viewer/icons.tsx +4 -4
- package/src/components/trace-viewer/utils.test.ts +5 -7
- package/src/components/trace-viewer/utils.ts +17 -31
- package/src/components/trace-viewer.tsx +15 -3
- package/src/components/ui/context-card.tsx +16 -26
- package/src/components/ui/data-inspector.tsx +20 -65
- package/src/components/ui/duplicate-event-tooltip.tsx +44 -0
- package/src/components/ui/menu-dropdown.tsx +8 -22
- package/src/components/ui/skeleton.tsx +2 -2
- package/src/components/ui/spinner.tsx +4 -8
- package/src/index.ts +4 -0
- package/src/lib/duplicate-events.test.ts +339 -0
- package/src/lib/duplicate-events.ts +188 -0
- package/src/lib/event-materialization.test.ts +117 -0
- package/src/lib/event-materialization.ts +23 -3
- package/src/lib/trace-builder.test.ts +74 -0
- package/src/lib/trace-builder.ts +30 -3
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { isHookLifecycleEventType, isStepEventType, isWaitEventType, } from '@workflow/world';
|
|
9
9
|
import { getEventTimestamp, hookToSpan, runToSpan, stepToSpan, WORKFLOW_LIBRARY, waitToSpan, } from '../components/workflow-traces/trace-span-construction';
|
|
10
10
|
import { otelTimeToMs } from '../components/workflow-traces/trace-time-utils';
|
|
11
|
+
import { findDuplicateEventIds } from './duplicate-events';
|
|
11
12
|
/**
|
|
12
13
|
* Events that belong to the run root span rather than a child entity span.
|
|
13
14
|
* Mirrors the fallthrough logic in {@link groupEventsByCorrelation}: events
|
|
@@ -122,9 +123,26 @@ function cascadeSpans(runSpan, spans) {
|
|
|
122
123
|
};
|
|
123
124
|
});
|
|
124
125
|
}
|
|
125
|
-
export function buildTrace(run, events, now
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
export function buildTrace(run, events, now,
|
|
127
|
+
/**
|
|
128
|
+
* Whether `events` is the run's whole log. Defaults to false, which builds
|
|
129
|
+
* the trace from every event: on a subset there is no way to tell a repeat
|
|
130
|
+
* from the only copy the caller was given, and dropping the wrong one moves
|
|
131
|
+
* a span. See {@link findDuplicateEventIds}.
|
|
132
|
+
*/
|
|
133
|
+
{ isCompleteHistory = false } = {}) {
|
|
134
|
+
// Span geometry comes from what the run acted on. A repeat of a class the
|
|
135
|
+
// log already records is read past by every replay, and letting one through
|
|
136
|
+
// here would stretch a span to whenever a concurrent replay committed it.
|
|
137
|
+
// The event lists still show them, marked as repeats.
|
|
138
|
+
const duplicateEventIds = findDuplicateEventIds(events, {
|
|
139
|
+
isCompleteHistory,
|
|
140
|
+
});
|
|
141
|
+
const actedOnEvents = duplicateEventIds.size === 0
|
|
142
|
+
? events
|
|
143
|
+
: events.filter((event) => !duplicateEventIds.has(event.eventId));
|
|
144
|
+
const groupedEvents = groupEventsByCorrelation(actedOnEvents);
|
|
145
|
+
const latestKnownTime = computeLatestKnownTime(actedOnEvents, run);
|
|
128
146
|
const { runSpan, spans } = buildSpans(run, groupedEvents, now, latestKnownTime);
|
|
129
147
|
const sortedCascadingSpans = cascadeSpans(runSpan, spans);
|
|
130
148
|
// Compute known duration relative to trace start
|
|
@@ -143,6 +161,7 @@ export function buildTrace(run, events, now) {
|
|
|
143
161
|
},
|
|
144
162
|
],
|
|
145
163
|
knownDurationMs: Math.max(0, knownDurationMs),
|
|
164
|
+
duplicateEventIds,
|
|
146
165
|
};
|
|
147
166
|
}
|
|
148
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhY2UtYnVpbGRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9saWIvdHJhY2UtYnVpbGRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFFSCxPQUFPLEVBRUwsd0JBQXdCLEVBQ3hCLGVBQWUsRUFDZixlQUFlLEdBRWhCLE1BQU0saUJBQWlCLENBQUM7QUFDekIsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixVQUFVLEVBQ1YsU0FBUyxFQUNULFVBQVUsRUFDVixnQkFBZ0IsRUFDaEIsVUFBVSxHQUNYLE1BQU0sdURBQXVELENBQUM7QUFDL0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGdEQUFnRCxDQUFDO0FBRzlFOzs7Ozs7R0FNRztBQUNILE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxDQUFDLEtBQVksRUFBVyxFQUFFLENBQ3ZELENBQUMsS0FBSyxDQUFDLGFBQWE7SUFDcEIsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDO1FBQ2hDLENBQUMsd0JBQXdCLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQztRQUMxQyxDQUFDLGVBQWUsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztBQUV2Qzs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FDakMsTUFBZSxFQUNmLFFBQTRCLEVBQzVCLE1BQTBCO0lBRTFCLElBQUksUUFBUSxLQUFLLEtBQUssRUFBRSxDQUFDO1FBQ3ZCLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUN4QyxDQUFDO0lBQ0QsSUFBSSxDQUFDLE1BQU07UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUN2QixPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxhQUFhLEtBQUssTUFBTSxDQUFDLENBQUM7QUFDMUQsQ0FBQztBQWFELFNBQVMsU0FBUyxDQUNoQixHQUF5QixFQUN6QixhQUFxQixFQUNyQixLQUFZO0lBRVosTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUN4QyxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBQ2IsUUFBUSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNyQixPQUFPO0lBQ1QsQ0FBQztJQUNELEdBQUcsQ0FBQyxHQUFHLENBQUMsYUFBYSxFQUFFLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUNsQyxDQUFDO0FBRUQsTUFBTSxVQUFVLHdCQUF3QixDQUFDLE1BQWU7SUFDdEQsTUFBTSxjQUFjLEdBQUcsSUFBSSxHQUFHLEVBQW1CLENBQUM7SUFDbEQsTUFBTSxjQUFjLEdBQVksRUFBRSxDQUFDO0lBQ25DLE1BQU0sV0FBVyxHQUFHLElBQUksR0FBRyxFQUFtQixDQUFDO0lBQy9DLE1BQU0sVUFBVSxHQUFHLElBQUksR0FBRyxFQUFtQixDQUFDO0lBRTlDLEtBQUssTUFBTSxLQUFLLElBQUksTUFBTSxFQUFFLENBQUM7UUFDM0IsTUFBTSxhQUFhLEdBQUcsS0FBSyxDQUFDLGFBQWEsQ0FBQztRQUMxQyxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDbkIsY0FBYyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUMzQixTQUFTO1FBQ1gsQ0FBQztRQUVELElBQUksZUFBZSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDO1lBQ3JDLFNBQVMsQ0FBQyxXQUFXLEVBQUUsYUFBYSxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQzdDLFNBQVM7UUFDWCxDQUFDO1FBRUQsSUFBSSx3QkFBd0IsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztZQUM5QyxTQUFTLENBQUMsVUFBVSxFQUFFLGFBQWEsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUM1QyxTQUFTO1FBQ1gsQ0FBQztRQUVELElBQUksZUFBZSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDO1lBQ3JDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsYUFBYSxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ2hELFNBQVM7UUFDWCxDQUFDO1FBRUQsY0FBYyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM3QixDQUFDO0lBRUQsT0FBTyxFQUFFLGNBQWMsRUFBRSxjQUFjLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxDQUFDO0FBQ3JFLENBQUM7QUFFRCw4RUFBOEU7QUFDOUUscUJBQXFCO0FBQ3JCLDhFQUE4RTtBQUU5RTs7OztHQUlHO0FBQ0gsU0FBUyxzQkFBc0IsQ0FBQyxNQUFlLEVBQUUsR0FBZ0I7SUFDL0QsSUFBSSxNQUFNLEdBQUcsSUFBSSxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQy9DLEtBQUssTUFBTSxLQUFLLElBQUksTUFBTSxFQUFFLENBQUM7UUFDM0IsTUFBTSxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUM1RSxJQUFJLENBQUMsR0FBRyxNQUFNO1lBQUUsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUM3QixDQUFDO0lBQ0QsT0FBTyxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUMxQixDQUFDO0FBRUQsU0FBUyxVQUFVLENBQ2pCLEdBQWdCLEVBQ2hCLGFBQTRCLEVBQzVCLEdBQVMsRUFDVCxlQUFxQjtJQUVyQixzRUFBc0U7SUFDdEUsNEVBQTRFO0lBQzVFLHNFQUFzRTtJQUN0RSxNQUFNLFdBQVcsR0FBRyxlQUFlLENBQUM7SUFDcEMsTUFBTSxTQUFTLEdBQUcsR0FBRyxDQUFDLFdBQVcsSUFBSSxHQUFHLENBQUM7SUFFekMsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsY0FBYyxDQUFDLE1BQU0sRUFBRSxDQUFDO1NBQ2hFLEdBQUcsQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxXQUFXLENBQUMsQ0FBQztTQUNoRCxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQWdCLEVBQUUsQ0FBQyxJQUFJLEtBQUssSUFBSSxDQUFDLENBQUM7SUFFakQsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDO1NBQzVELEdBQUcsQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxXQUFXLENBQUMsQ0FBQztTQUNoRCxNQUFNLENBQUMsQ0FBQyxJQUFJLEVBQWdCLEVBQUUsQ0FBQyxJQUFJLEtBQUssSUFBSSxDQUFDLENBQUM7SUFFakQsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsV0FBVyxDQUFDLE1BQU0sRUFBRSxDQUFDO1NBQzdELEdBQUcsQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxXQUFXLEVBQUUsU0FBUyxDQUFDLENBQUM7U0FDM0QsTUFBTSxDQUFDLENBQUMsSUFBSSxFQUFnQixFQUFFLENBQUMsSUFBSSxLQUFLLElBQUksQ0FBQyxDQUFDO0lBRWpELE9BQU87UUFDTCxPQUFPLEVBQUUsU0FBUyxDQUFDLEdBQUcsRUFBRSxhQUFhLENBQUMsY0FBYyxFQUFFLEdBQUcsQ0FBQztRQUMxRCxLQUFLLEVBQUUsQ0FBQyxHQUFHLFNBQVMsRUFBRSxHQUFHLFNBQVMsRUFBRSxHQUFHLFNBQVMsQ0FBQztLQUNsRCxDQUFDO0FBQ0osQ0FBQztBQUVELFNBQVMsWUFBWSxDQUFDLE9BQWEsRUFBRSxLQUFhO0lBQ2hELE1BQU0sV0FBVyxHQUFHO1FBQ2xCLE9BQU87UUFDUCxHQUFHLEtBQUssQ0FBQyxLQUFLLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7WUFDN0IsTUFBTSxNQUFNLEdBQUcsWUFBWSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUN6QyxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1lBQ3pDLE9BQU8sTUFBTSxHQUFHLE1BQU0sQ0FBQztRQUN6QixDQUFDLENBQUM7S0FDSCxDQUFDO0lBRUYsT0FBTyxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFO1FBQ3JDLE1BQU0sWUFBWSxHQUNoQixLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ2xFLE9BQU87WUFDTCxHQUFHLElBQUk7WUFDUCxZQUFZO1NBQ2IsQ0FBQztJQUNKLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQVdELE1BQU0sVUFBVSxVQUFVLENBQ3hCLEdBQWdCLEVBQ2hCLE1BQWUsRUFDZixHQUFTO0lBRVQsTUFBTSxhQUFhLEdBQUcsd0JBQXdCLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDdkQsTUFBTSxlQUFlLEdBQUcsc0JBQXNCLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQzVELE1BQU0sRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEdBQUcsVUFBVSxDQUNuQyxHQUFHLEVBQ0gsYUFBYSxFQUNiLEdBQUcsRUFDSCxlQUFlLENBQ2hCLENBQUM7SUFDRixNQUFNLG9CQUFvQixHQUFHLFlBQVksQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFFMUQsaURBQWlEO0lBQ2pELE1BQU0sWUFBWSxHQUFHLFlBQVksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDckQsTUFBTSxlQUFlLEdBQUcsZUFBZSxDQUFDLE9BQU8sRUFBRSxHQUFHLFlBQVksQ0FBQztJQUVqRSxPQUFPO1FBQ0wsT0FBTyxFQUFFLEdBQUcsQ0FBQyxLQUFLO1FBQ2xCLFVBQVUsRUFBRSxHQUFHLENBQUMsS0FBSztRQUNyQixLQUFLLEVBQUUsb0JBQW9CO1FBQzNCLFNBQVMsRUFBRTtZQUNUO2dCQUNFLElBQUksRUFBRSxVQUFVO2dCQUNoQixVQUFVLEVBQUU7b0JBQ1YsY0FBYyxFQUFFLGdCQUFnQixDQUFDLElBQUk7aUJBQ3RDO2FBQ0Y7U0FDRjtRQUNELGVBQWUsRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxlQUFlLENBQUM7S0FDOUMsQ0FBQztBQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEJ1aWxkcyBhIGNvbXBsZXRlIHRyYWNlIGZyb20gYSBXb3JrZmxvd1J1biBhbmQgaXRzIEV2ZW50cy5cbiAqXG4gKiBUaGlzIG1vZHVsZSBncm91cHMgcmF3IGV2ZW50cyBieSBjb3JyZWxhdGlvbiBJRCBhbmQgZW50aXR5IHR5cGUsXG4gKiBjb252ZXJ0cyBlYWNoIGdyb3VwIGludG8gYW4gT3BlblRlbGVtZXRyeS1zdHlsZSBTcGFuLCBhbmQgcmV0dXJuc1xuICogYSBmdWxseS1mb3JtZWQgVHJhY2UgcmVhZHkgZm9yIHRoZSB0cmFjZSB2aWV3ZXIuXG4gKi9cblxuaW1wb3J0IHtcbiAgdHlwZSBFdmVudCxcbiAgaXNIb29rTGlmZWN5Y2xlRXZlbnRUeXBlLFxuICBpc1N0ZXBFdmVudFR5cGUsXG4gIGlzV2FpdEV2ZW50VHlwZSxcbiAgdHlwZSBXb3JrZmxvd1J1bixcbn0gZnJvbSAnQHdvcmtmbG93L3dvcmxkJztcbmltcG9ydCB7XG4gIGdldEV2ZW50VGltZXN0YW1wLFxuICBob29rVG9TcGFuLFxuICBydW5Ub1NwYW4sXG4gIHN0ZXBUb1NwYW4sXG4gIFdPUktGTE9XX0xJQlJBUlksXG4gIHdhaXRUb1NwYW4sXG59IGZyb20gJy4uL2NvbXBvbmVudHMvd29ya2Zsb3ctdHJhY2VzL3RyYWNlLXNwYW4tY29uc3RydWN0aW9uJztcbmltcG9ydCB7IG90ZWxUaW1lVG9NcyB9IGZyb20gJy4uL2NvbXBvbmVudHMvd29ya2Zsb3ctdHJhY2VzL3RyYWNlLXRpbWUtdXRpbHMnO1xuaW1wb3J0IHR5cGUgeyBTcGFuIH0gZnJvbSAnLi90cmFjZS10eXBlcyc7XG5cbi8qKlxuICogRXZlbnRzIHRoYXQgYmVsb25nIHRvIHRoZSBydW4gcm9vdCBzcGFuIHJhdGhlciB0aGFuIGEgY2hpbGQgZW50aXR5IHNwYW4uXG4gKiBNaXJyb3JzIHRoZSBmYWxsdGhyb3VnaCBsb2dpYyBpbiB7QGxpbmsgZ3JvdXBFdmVudHNCeUNvcnJlbGF0aW9ufTogZXZlbnRzXG4gKiB3aXRob3V0IGEgY29ycmVsYXRpb25JZCAocnVuIGxpZmVjeWNsZSkgcGx1cyBjb3JyZWxhdGVkIGV2ZW50cyB0aGF0IGFyZW4ndFxuICogc3RlcC90aW1lci9ob29rIGV2ZW50cyAoZS5nLiBgYXR0cl9zZXRgLCB3aG9zZSBjb3JyZWxhdGlvbklkIGlzIGEgZGVkdXBcbiAqIHRva2VuIHJhdGhlciB0aGFuIGEgY2hpbGQgZW50aXR5IElEKS5cbiAqL1xuZXhwb3J0IGNvbnN0IGlzUnVuTGV2ZWxFdmVudCA9IChldmVudDogRXZlbnQpOiBib29sZWFuID0+XG4gICFldmVudC5jb3JyZWxhdGlvbklkIHx8XG4gICghaXNXYWl0RXZlbnRUeXBlKGV2ZW50LmV2ZW50VHlwZSkgJiZcbiAgICAhaXNIb29rTGlmZWN5Y2xlRXZlbnRUeXBlKGV2ZW50LmV2ZW50VHlwZSkgJiZcbiAgICAhaXNTdGVwRXZlbnRUeXBlKGV2ZW50LmV2ZW50VHlwZSkpO1xuXG4vKipcbiAqIEZpbHRlciB0aGUgcmF3IGV2ZW50IGxpc3QgZG93biB0byB0aGUgZXZlbnRzIGZvciBhIHNlbGVjdGVkIHNwYW4uXG4gKiBGb3IgdGhlIHJ1biByb290IHNwYW4gdGhpcyByZXR1cm5zIHJ1bi1sZXZlbCBldmVudHMgKHNlZVxuICoge0BsaW5rIGlzUnVuTGV2ZWxFdmVudH0pOyBmb3IgY2hpbGQgc3BhbnMgaXQgcmV0dXJucyB0aGUgZXZlbnRzXG4gKiBjb3JyZWxhdGVkIHRvIHRoYXQgc3BhbidzIElELlxuICovXG5leHBvcnQgZnVuY3Rpb24gZmlsdGVyU3BhblJhd0V2ZW50cyhcbiAgZXZlbnRzOiBFdmVudFtdLFxuICByZXNvdXJjZTogc3RyaW5nIHwgdW5kZWZpbmVkLFxuICBzcGFuSWQ6IHN0cmluZyB8IHVuZGVmaW5lZFxuKTogRXZlbnRbXSB7XG4gIGlmIChyZXNvdXJjZSA9PT0gJ3J1bicpIHtcbiAgICByZXR1cm4gZXZlbnRzLmZpbHRlcihpc1J1bkxldmVsRXZlbnQpO1xuICB9XG4gIGlmICghc3BhbklkKSByZXR1cm4gW107XG4gIHJldHVybiBldmVudHMuZmlsdGVyKChlKSA9PiBlLmNvcnJlbGF0aW9uSWQgPT09IHNwYW5JZCk7XG59XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gRXZlbnQgZ3JvdXBpbmdcbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG5leHBvcnQgdHlwZSBHcm91cGVkRXZlbnRzID0ge1xuICBldmVudHNCeVN0ZXBJZDogTWFwPHN0cmluZywgRXZlbnRbXT47XG4gIHJ1bkxldmVsRXZlbnRzOiBFdmVudFtdO1xuICB0aW1lckV2ZW50czogTWFwPHN0cmluZywgRXZlbnRbXT47XG4gIGhvb2tFdmVudHM6IE1hcDxzdHJpbmcsIEV2ZW50W10+O1xufTtcblxuZnVuY3Rpb24gcHVzaEV2ZW50KFxuICBtYXA6IE1hcDxzdHJpbmcsIEV2ZW50W10+LFxuICBjb3JyZWxhdGlvbklkOiBzdHJpbmcsXG4gIGV2ZW50OiBFdmVudFxuKSB7XG4gIGNvbnN0IGV4aXN0aW5nID0gbWFwLmdldChjb3JyZWxhdGlvbklkKTtcbiAgaWYgKGV4aXN0aW5nKSB7XG4gICAgZXhpc3RpbmcucHVzaChldmVudCk7XG4gICAgcmV0dXJuO1xuICB9XG4gIG1hcC5zZXQoY29ycmVsYXRpb25JZCwgW2V2ZW50XSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBncm91cEV2ZW50c0J5Q29ycmVsYXRpb24oZXZlbnRzOiBFdmVudFtdKTogR3JvdXBlZEV2ZW50cyB7XG4gIGNvbnN0IGV2ZW50c0J5U3RlcElkID0gbmV3IE1hcDxzdHJpbmcsIEV2ZW50W10+KCk7XG4gIGNvbnN0IHJ1bkxldmVsRXZlbnRzOiBFdmVudFtdID0gW107XG4gIGNvbnN0IHRpbWVyRXZlbnRzID0gbmV3IE1hcDxzdHJpbmcsIEV2ZW50W10+KCk7XG4gIGNvbnN0IGhvb2tFdmVudHMgPSBuZXcgTWFwPHN0cmluZywgRXZlbnRbXT4oKTtcblxuICBmb3IgKGNvbnN0IGV2ZW50IG9mIGV2ZW50cykge1xuICAgIGNvbnN0IGNvcnJlbGF0aW9uSWQgPSBldmVudC5jb3JyZWxhdGlvbklkO1xuICAgIGlmICghY29ycmVsYXRpb25JZCkge1xuICAgICAgcnVuTGV2ZWxFdmVudHMucHVzaChldmVudCk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBpZiAoaXNXYWl0RXZlbnRUeXBlKGV2ZW50LmV2ZW50VHlwZSkpIHtcbiAgICAgIHB1c2hFdmVudCh0aW1lckV2ZW50cywgY29ycmVsYXRpb25JZCwgZXZlbnQpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgaWYgKGlzSG9va0xpZmVjeWNsZUV2ZW50VHlwZShldmVudC5ldmVudFR5cGUpKSB7XG4gICAgICBwdXNoRXZlbnQoaG9va0V2ZW50cywgY29ycmVsYXRpb25JZCwgZXZlbnQpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgaWYgKGlzU3RlcEV2ZW50VHlwZShldmVudC5ldmVudFR5cGUpKSB7XG4gICAgICBwdXNoRXZlbnQoZXZlbnRzQnlTdGVwSWQsIGNvcnJlbGF0aW9uSWQsIGV2ZW50KTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIHJ1bkxldmVsRXZlbnRzLnB1c2goZXZlbnQpO1xuICB9XG5cbiAgcmV0dXJuIHsgZXZlbnRzQnlTdGVwSWQsIHJ1bkxldmVsRXZlbnRzLCB0aW1lckV2ZW50cywgaG9va0V2ZW50cyB9O1xufVxuXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbi8vIFRyYWNlIGNvbnN0cnVjdGlvblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbi8qKlxuICogQ29tcHV0ZXMgdGhlIGxhdGVzdCBrbm93biBldmVudCB0aW1lIGZyb20gdGhlIGV2ZW50cyBsaXN0LlxuICogQWN0aXZlIChpbi1wcm9ncmVzcykgY2hpbGQgc3BhbnMgYXJlIGNhcHBlZCBhdCB0aGlzIHRpbWUgaW5zdGVhZCBvZiBgbm93YCxcbiAqIHNvIHRoZXkgb25seSBleHRlbmQgYXMgZmFyIGFzIG91ciBkYXRhIGdvZXMuXG4gKi9cbmZ1bmN0aW9uIGNvbXB1dGVMYXRlc3RLbm93blRpbWUoZXZlbnRzOiBFdmVudFtdLCBydW46IFdvcmtmbG93UnVuKTogRGF0ZSB7XG4gIGxldCBsYXRlc3QgPSBuZXcgRGF0ZShydW4uY3JlYXRlZEF0KS5nZXRUaW1lKCk7XG4gIGZvciAoY29uc3QgZXZlbnQgb2YgZXZlbnRzKSB7XG4gICAgY29uc3QgdCA9IChnZXRFdmVudFRpbWVzdGFtcChldmVudCkgPz8gbmV3IERhdGUoZXZlbnQuY3JlYXRlZEF0KSkuZ2V0VGltZSgpO1xuICAgIGlmICh0ID4gbGF0ZXN0KSBsYXRlc3QgPSB0O1xuICB9XG4gIHJldHVybiBuZXcgRGF0ZShsYXRlc3QpO1xufVxuXG5mdW5jdGlvbiBidWlsZFNwYW5zKFxuICBydW46IFdvcmtmbG93UnVuLFxuICBncm91cGVkRXZlbnRzOiBHcm91cGVkRXZlbnRzLFxuICBub3c6IERhdGUsXG4gIGxhdGVzdEtub3duVGltZTogRGF0ZVxuKSB7XG4gIC8vIEFjdGl2ZSBjaGlsZCBzcGFucyBjYXAgYXQgbGF0ZXN0S25vd25UaW1lIHNvIHRoZXkgZG9uJ3QgZXh0ZW5kIGludG9cbiAgLy8gdW5rbm93biB0ZXJyaXRvcnkuIEV2ZW4gd2hlbiB0aGUgcnVuIGlzIGNvbXBsZXRlZCwgd2UgbWF5IG5vdCBoYXZlIGxvYWRlZFxuICAvLyBhbGwgZXZlbnRzIHlldCwgc28gYWx3YXlzIHVzZSB0aGUgbGF0ZXN0IGV2ZW50IHdlJ3ZlIGFjdHVhbGx5IHNlZW4uXG4gIGNvbnN0IGNoaWxkTWF4RW5kID0gbGF0ZXN0S25vd25UaW1lO1xuICBjb25zdCBydW5NYXhFbmQgPSBydW4uY29tcGxldGVkQXQgPz8gbm93O1xuXG4gIGNvbnN0IHN0ZXBTcGFucyA9IEFycmF5LmZyb20oZ3JvdXBlZEV2ZW50cy5ldmVudHNCeVN0ZXBJZC52YWx1ZXMoKSlcbiAgICAubWFwKChldmVudHMpID0+IHN0ZXBUb1NwYW4oZXZlbnRzLCBjaGlsZE1heEVuZCkpXG4gICAgLmZpbHRlcigoc3Bhbik6IHNwYW4gaXMgU3BhbiA9PiBzcGFuICE9PSBudWxsKTtcblxuICBjb25zdCBob29rU3BhbnMgPSBBcnJheS5mcm9tKGdyb3VwZWRFdmVudHMuaG9va0V2ZW50cy52YWx1ZXMoKSlcbiAgICAubWFwKChldmVudHMpID0+IGhvb2tUb1NwYW4oZXZlbnRzLCBjaGlsZE1heEVuZCkpXG4gICAgLmZpbHRlcigoc3Bhbik6IHNwYW4gaXMgU3BhbiA9PiBzcGFuICE9PSBudWxsKTtcblxuICBjb25zdCB3YWl0U3BhbnMgPSBBcnJheS5mcm9tKGdyb3VwZWRFdmVudHMudGltZXJFdmVudHMudmFsdWVzKCkpXG4gICAgLm1hcCgoZXZlbnRzKSA9PiB3YWl0VG9TcGFuKGV2ZW50cywgY2hpbGRNYXhFbmQsIHJ1bk1heEVuZCkpXG4gICAgLmZpbHRlcigoc3Bhbik6IHNwYW4gaXMgU3BhbiA9PiBzcGFuICE9PSBudWxsKTtcblxuICByZXR1cm4ge1xuICAgIHJ1blNwYW46IHJ1blRvU3BhbihydW4sIGdyb3VwZWRFdmVudHMucnVuTGV2ZWxFdmVudHMsIG5vdyksXG4gICAgc3BhbnM6IFsuLi5zdGVwU3BhbnMsIC4uLmhvb2tTcGFucywgLi4ud2FpdFNwYW5zXSxcbiAgfTtcbn1cblxuZnVuY3Rpb24gY2FzY2FkZVNwYW5zKHJ1blNwYW46IFNwYW4sIHNwYW5zOiBTcGFuW10pIHtcbiAgY29uc3Qgc29ydGVkU3BhbnMgPSBbXG4gICAgcnVuU3BhbixcbiAgICAuLi5zcGFucy5zbGljZSgpLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgIGNvbnN0IGFTdGFydCA9IG90ZWxUaW1lVG9NcyhhLnN0YXJ0VGltZSk7XG4gICAgICBjb25zdCBiU3RhcnQgPSBvdGVsVGltZVRvTXMoYi5zdGFydFRpbWUpO1xuICAgICAgcmV0dXJuIGFTdGFydCAtIGJTdGFydDtcbiAgICB9KSxcbiAgXTtcblxuICByZXR1cm4gc29ydGVkU3BhbnMubWFwKChzcGFuLCBpbmRleCkgPT4ge1xuICAgIGNvbnN0IHBhcmVudFNwYW5JZCA9XG4gICAgICBpbmRleCA9PT0gMCA/IHVuZGVmaW5lZCA6IFN0cmluZyhzb3J0ZWRTcGFuc1tpbmRleCAtIDFdLnNwYW5JZCk7XG4gICAgcmV0dXJuIHtcbiAgICAgIC4uLnNwYW4sXG4gICAgICBwYXJlbnRTcGFuSWQsXG4gICAgfTtcbiAgfSk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgVHJhY2VXaXRoTWV0YSB7XG4gIHRyYWNlSWQ6IHN0cmluZztcbiAgcm9vdFNwYW5JZDogc3RyaW5nO1xuICBzcGFuczogU3BhbltdO1xuICByZXNvdXJjZXM6IHsgbmFtZTogc3RyaW5nOyBhdHRyaWJ1dGVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IH1bXTtcbiAgLyoqIER1cmF0aW9uIGluIG1zIGZyb20gdHJhY2Ugc3RhcnQgdG8gdGhlIGxhdGVzdCBrbm93biBldmVudC4gKi9cbiAga25vd25EdXJhdGlvbk1zOiBudW1iZXI7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBidWlsZFRyYWNlKFxuICBydW46IFdvcmtmbG93UnVuLFxuICBldmVudHM6IEV2ZW50W10sXG4gIG5vdzogRGF0ZVxuKTogVHJhY2VXaXRoTWV0YSB7XG4gIGNvbnN0IGdyb3VwZWRFdmVudHMgPSBncm91cEV2ZW50c0J5Q29ycmVsYXRpb24oZXZlbnRzKTtcbiAgY29uc3QgbGF0ZXN0S25vd25UaW1lID0gY29tcHV0ZUxhdGVzdEtub3duVGltZShldmVudHMsIHJ1bik7XG4gIGNvbnN0IHsgcnVuU3Bhbiwgc3BhbnMgfSA9IGJ1aWxkU3BhbnMoXG4gICAgcnVuLFxuICAgIGdyb3VwZWRFdmVudHMsXG4gICAgbm93LFxuICAgIGxhdGVzdEtub3duVGltZVxuICApO1xuICBjb25zdCBzb3J0ZWRDYXNjYWRpbmdTcGFucyA9IGNhc2NhZGVTcGFucyhydW5TcGFuLCBzcGFucyk7XG5cbiAgLy8gQ29tcHV0ZSBrbm93biBkdXJhdGlvbiByZWxhdGl2ZSB0byB0cmFjZSBzdGFydFxuICBjb25zdCB0cmFjZVN0YXJ0TXMgPSBvdGVsVGltZVRvTXMocnVuU3Bhbi5zdGFydFRpbWUpO1xuICBjb25zdCBrbm93bkR1cmF0aW9uTXMgPSBsYXRlc3RLbm93blRpbWUuZ2V0VGltZSgpIC0gdHJhY2VTdGFydE1zO1xuXG4gIHJldHVybiB7XG4gICAgdHJhY2VJZDogcnVuLnJ1bklkLFxuICAgIHJvb3RTcGFuSWQ6IHJ1bi5ydW5JZCxcbiAgICBzcGFuczogc29ydGVkQ2FzY2FkaW5nU3BhbnMsXG4gICAgcmVzb3VyY2VzOiBbXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICd3b3JrZmxvdycsXG4gICAgICAgIGF0dHJpYnV0ZXM6IHtcbiAgICAgICAgICAnc2VydmljZS5uYW1lJzogV09SS0ZMT1dfTElCUkFSWS5uYW1lLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICBdLFxuICAgIGtub3duRHVyYXRpb25NczogTWF0aC5tYXgoMCwga25vd25EdXJhdGlvbk1zKSxcbiAgfTtcbn1cbiJdfQ==
|
|
167
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhY2UtYnVpbGRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9saWIvdHJhY2UtYnVpbGRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7O0dBTUc7QUFFSCxPQUFPLEVBRUwsd0JBQXdCLEVBQ3hCLGVBQWUsRUFDZixlQUFlLEdBRWhCLE1BQU0saUJBQWlCLENBQUM7QUFDekIsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixVQUFVLEVBQ1YsU0FBUyxFQUNULFVBQVUsRUFDVixnQkFBZ0IsRUFDaEIsVUFBVSxHQUNYLE1BQU0sdURBQXVELENBQUM7QUFDL0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGdEQUFnRCxDQUFDO0FBQzlFLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRzNEOzs7Ozs7R0FNRztBQUNILE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxDQUFDLEtBQVksRUFBVyxFQUFFLENBQ3ZELENBQUMsS0FBSyxDQUFDLGFBQWE7SUFDcEIsQ0FBQyxDQUFDLGVBQWUsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDO1FBQ2hDLENBQUMsd0JBQXdCLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQztRQUMxQyxDQUFDLGVBQWUsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztBQUV2Qzs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FDakMsTUFBZSxFQUNmLFFBQTRCLEVBQzVCLE1BQTBCO0lBRTFCLElBQUksUUFBUSxLQUFLLEtBQUssRUFBRSxDQUFDO1FBQ3ZCLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUN4QyxDQUFDO0lBQ0QsSUFBSSxDQUFDLE1BQU07UUFBRSxPQUFPLEVBQUUsQ0FBQztJQUN2QixPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxhQUFhLEtB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QUFhLEdBQUcsd0JBQXdCLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDOUQsTUFBTSxlQUFlLEdBQUcsc0JBQXNCLENBQUMsYUFBYSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ25FLE1BQU0sRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEdBQUcsVUFBVSxDQUNuQyxHQUFHLEVBQ0gsYUFBYSxFQUNiLEdBQUcsRUFDSCxlQUFlLENBQ2hCLENBQUM7SUFDRixNQUFNLG9CQUFvQixHQUFHLFlBQVksQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFFMUQsaURBQWlEO0lBQ2pELE1BQU0sWUFBWSxHQUFHLFlBQVksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDckQsTUFBTSxlQUFlLEdBQUcsZUFBZSxDQUFDLE9BQU8sRUFBRSxHQUFHLFlBQVksQ0FBQztJQUVqRSxPQUFPO1FBQ0wsT0FBTyxFQUFFLEdBQUcsQ0FBQyxLQUFLO1FBQ2xCLFVBQVUsRUFBRSxHQUFHLENBQUMsS0FBSztRQUNyQixLQUFLLEVBQUUsb0JBQW9CO1FBQzNCLFNBQVMsRUFBRTtZQUNUO2dCQUNFLElBQUksRUFBRSxVQUFVO2dCQUNoQixVQUFVLEVBQUU7b0JBQ1YsY0FBYyxFQUFFLGdCQUFnQixDQUFDLElBQUk7aUJBQ3RDO2FBQ0Y7U0FDRjtRQUNELGVBQWUsRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxlQUFlLENBQUM7UUFDN0MsaUJBQWlCO0tBQ2xCLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBCdWlsZHMgYSBjb21wbGV0ZSB0cmFjZSBmcm9tIGEgV29ya2Zsb3dSdW4gYW5kIGl0cyBFdmVudHMuXG4gKlxuICogVGhpcyBtb2R1bGUgZ3JvdXBzIHJhdyBldmVudHMgYnkgY29ycmVsYXRpb24gSUQgYW5kIGVudGl0eSB0eXBlLFxuICogY29udmVydHMgZWFjaCBncm91cCBpbnRvIGFuIE9wZW5UZWxlbWV0cnktc3R5bGUgU3BhbiwgYW5kIHJldHVybnNcbiAqIGEgZnVsbHktZm9ybWVkIFRyYWNlIHJlYWR5IGZvciB0aGUgdHJhY2Ugdmlld2VyLlxuICovXG5cbmltcG9ydCB7XG4gIHR5cGUgRXZlbnQsXG4gIGlzSG9va0xpZmVjeWNsZUV2ZW50VHlwZSxcbiAgaXNTdGVwRXZlbnRUeXBlLFxuICBpc1dhaXRFdmVudFR5cGUsXG4gIHR5cGUgV29ya2Zsb3dSdW4sXG59IGZyb20gJ0B3b3JrZmxvdy93b3JsZCc7XG5pbXBvcnQge1xuICBnZXRFdmVudFRpbWVzdGFtcCxcbiAgaG9va1RvU3BhbixcbiAgcnVuVG9TcGFuLFxuICBzdGVwVG9TcGFuLFxuICBXT1JLRkxPV19MSUJSQVJZLFxuICB3YWl0VG9TcGFuLFxufSBmcm9tICcuLi9jb21wb25lbnRzL3dvcmtmbG93LXRyYWNlcy90cmFjZS1zcGFuLWNvbnN0cnVjdGlvbic7XG5pbXBvcnQgeyBvdGVsVGltZVRvTXMgfSBmcm9tICcuLi9jb21wb25lbnRzL3dvcmtmbG93LXRyYWNlcy90cmFjZS10aW1lLXV0aWxzJztcbmltcG9ydCB7IGZpbmREdXBsaWNhdGVFdmVudElkcyB9IGZyb20gJy4vZHVwbGljYXRlLWV2ZW50cyc7XG5pbXBvcnQgdHlwZSB7IFNwYW4gfSBmcm9tICcuL3RyYWNlLXR5cGVzJztcblxuLyoqXG4gKiBFdmVudHMgdGhhdCBiZWxvbmcgdG8gdGhlIHJ1biByb290IHNwYW4gcmF0aGVyIHRoYW4gYSBjaGlsZCBlbnRpdHkgc3Bhbi5cbiAqIE1pcnJvcnMgdGhlIGZhbGx0aHJvdWdoIGxvZ2ljIGluIHtAbGluayBncm91cEV2ZW50c0J5Q29ycmVsYXRpb259OiBldmVudHNcbiAqIHdpdGhvdXQgYSBjb3JyZWxhdGlvbklkIChydW4gbGlmZWN5Y2xlKSBwbHVzIGNvcnJlbGF0ZWQgZXZlbnRzIHRoYXQgYXJlbid0XG4gKiBzdGVwL3RpbWVyL2hvb2sgZXZlbnRzIChlLmcuIGBhdHRyX3NldGAsIHdob3NlIGNvcnJlbGF0aW9uSWQgaXMgYSBkZWR1cFxuICogdG9rZW4gcmF0aGVyIHRoYW4gYSBjaGlsZCBlbnRpdHkgSUQpLlxuICovXG5leHBvcnQgY29uc3QgaXNSdW5MZXZlbEV2ZW50ID0gKGV2ZW50OiBFdmVudCk6IGJvb2xlYW4gPT5cbiAgIWV2ZW50LmNvcnJlbGF0aW9uSWQgfHxcbiAgKCFpc1dhaXRFdmVudFR5cGUoZXZlbnQuZXZlbnRUeXBlKSAmJlxuICAgICFpc0hvb2tMaWZlY3ljbGVFdmVudFR5cGUoZXZlbnQuZXZlbnRUeXBlKSAmJlxuICAgICFpc1N0ZXBFdmVudFR5cGUoZXZlbnQuZXZlbnRUeXBlKSk7XG5cbi8qKlxuICogRmlsdGVyIHRoZSByYXcgZXZlbnQgbGlzdCBkb3duIHRvIHRoZSBldmVudHMgZm9yIGEgc2VsZWN0ZWQgc3Bhbi5cbiAqIEZvciB0aGUgcnVuIHJvb3Qgc3BhbiB0aGlzIHJldHVybnMgcnVuLWxldmVsIGV2ZW50cyAoc2VlXG4gKiB7QGxpbmsgaXNSdW5MZXZlbEV2ZW50fSk7IGZvciBjaGlsZCBzcGFucyBpdCByZXR1cm5zIHRoZSBldmVudHNcbiAqIGNvcnJlbGF0ZWQgdG8gdGhhdCBzcGFuJ3MgSUQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmaWx0ZXJTcGFuUmF3RXZlbnRzKFxuICBldmVudHM6IEV2ZW50W10sXG4gIHJlc291cmNlOiBzdHJpbmcgfCB1bmRlZmluZWQsXG4gIHNwYW5JZDogc3RyaW5nIHwgdW5kZWZpbmVkXG4pOiBFdmVudFtdIHtcbiAgaWYgKHJlc291cmNlID09PSAncnVuJykge1xuICAgIHJldHVybiBldmVudHMuZmlsdGVyKGlzUnVuTGV2ZWxFdmVudCk7XG4gIH1cbiAgaWYgKCFzcGFuSWQpIHJldHVybiBbXTtcbiAgcmV0dXJuIGV2ZW50cy5maWx0ZXIoKGUpID0+IGUuY29ycmVsYXRpb25JZCA9PT0gc3BhbklkKTtcbn1cblxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4vLyBFdmVudCBncm91cGluZ1xuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbmV4cG9ydCB0eXBlIEdyb3VwZWRFdmVudHMgPSB7XG4gIGV2ZW50c0J5U3RlcElkOiBNYXA8c3RyaW5nLCBFdmVudFtdPjtcbiAgcnVuTGV2ZWxFdmVudHM6IEV2ZW50W107XG4gIHRpbWVyRXZlbnRzOiBNYXA8c3RyaW5nLCBFdmVudFtdPjtcbiAgaG9va0V2ZW50czogTWFwPHN0cmluZywgRXZlbnRbXT47XG59O1xuXG5mdW5jdGlvbiBwdXNoRXZlbnQoXG4gIG1hcDogTWFwPHN0cmluZywgRXZlbnRbXT4sXG4gIGNvcnJlbGF0aW9uSWQ6IHN0cmluZyxcbiAgZXZlbnQ6IEV2ZW50XG4pIHtcbiAgY29uc3QgZXhpc3RpbmcgPSBtYXAuZ2V0KGNvcnJlbGF0aW9uSWQpO1xuICBpZiAoZXhpc3RpbmcpIHtcbiAgICBleGlzdGluZy5wdXNoKGV2ZW50KTtcbiAgICByZXR1cm47XG4gIH1cbiAgbWFwLnNldChjb3JyZWxhdGlvbklkLCBbZXZlbnRdKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdyb3VwRXZlbnRzQnlDb3JyZWxhdGlvbihldmVudHM6IEV2ZW50W10pOiBHcm91cGVkRXZlbnRzIHtcbiAgY29uc3QgZXZlbnRzQnlTdGVwSWQgPSBuZXcgTWFwPHN0cmluZywgRXZlbnRbXT4oKTtcbiAgY29uc3QgcnVuTGV2ZWxFdmVudHM6IEV2ZW50W10gPSBbXTtcbiAgY29uc3QgdGltZXJFdmVudHMgPSBuZXcgTWFwPHN0cmluZywgRXZlbnRbXT4oKTtcbiAgY29uc3QgaG9va0V2ZW50cyA9IG5ldyBNYXA8c3RyaW5nLCBFdmVudFtdPigpO1xuXG4gIGZvciAoY29uc3QgZXZlbnQgb2YgZXZlbnRzKSB7XG4gICAgY29uc3QgY29ycmVsYXRpb25JZCA9IGV2ZW50LmNvcnJlbGF0aW9uSWQ7XG4gICAgaWYgKCFjb3JyZWxhdGlvbklkKSB7XG4gICAgICBydW5MZXZlbEV2ZW50cy5wdXNoKGV2ZW50KTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGlmIChpc1dhaXRFdmVudFR5cGUoZXZlbnQuZXZlbnRUeXBlKSkge1xuICAgICAgcHVzaEV2ZW50KHRpbWVyRXZlbnRzLCBjb3JyZWxhdGlvbklkLCBldmVudCk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBpZiAoaXNIb29rTGlmZWN5Y2xlRXZlbnRUeXBlKGV2ZW50LmV2ZW50VHlwZSkpIHtcbiAgICAgIHB1c2hFdmVudChob29rRXZlbnRzLCBjb3JyZWxhdGlvbklkLCBldmVudCk7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBpZiAoaXNTdGVwRXZlbnRUeXBlKGV2ZW50LmV2ZW50VHlwZSkpIHtcbiAgICAgIHB1c2hFdmVudChldmVudHNCeVN0ZXBJZCwgY29ycmVsYXRpb25JZCwgZXZlbnQpO1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgcnVuTGV2ZWxFdmVudHMucHVzaChldmVudCk7XG4gIH1cblxuICByZXR1cm4geyBldmVudHNCeVN0ZXBJZCwgcnVuTGV2ZWxFdmVudHMsIHRpbWVyRXZlbnRzLCBob29rRXZlbnRzIH07XG59XG5cbi8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuLy8gVHJhY2UgY29uc3RydWN0aW9uXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuLyoqXG4gKiBDb21wdXRlcyB0aGUgbGF0ZXN0IGtub3duIGV2ZW50IHRpbWUgZnJvbSB0aGUgZXZlbnRzIGxpc3QuXG4gKiBBY3RpdmUgKGluLXByb2dyZXNzKSBjaGlsZCBzcGFucyBhcmUgY2FwcGVkIGF0IHRoaXMgdGltZSBpbnN0ZWFkIG9mIGBub3dgLFxuICogc28gdGhleSBvbmx5IGV4dGVuZCBhcyBmYXIgYXMgb3VyIGRhdGEgZ29lcy5cbiAqL1xuZnVuY3Rpb24gY29tcHV0ZUxhdGVzdEtub3duVGltZShldmVudHM6IEV2ZW50W10sIHJ1bjogV29ya2Zsb3dSdW4pOiBEYXRlIHtcbiAgbGV0IGxhdGVzdCA9IG5ldyBEYXRlKHJ1bi5jcmVhdGVkQXQpLmdldFRpbWUoKTtcbiAgZm9yIChjb25zdCBldmVudCBvZiBldmVudHMpIHtcbiAgICBjb25zdCB0ID0gKGdldEV2ZW50VGltZXN0YW1wKGV2ZW50KSA/PyBuZXcgRGF0ZShldmVudC5jcmVhdGVkQXQpKS5nZXRUaW1lKCk7XG4gICAgaWYgKHQgPiBsYXRlc3QpIGxhdGVzdCA9IHQ7XG4gIH1cbiAgcmV0dXJuIG5ldyBEYXRlKGxhdGVzdCk7XG59XG5cbmZ1bmN0aW9uIGJ1aWxkU3BhbnMoXG4gIHJ1bjogV29ya2Zsb3dSdW4sXG4gIGdyb3VwZWRFdmVudHM6IEdyb3VwZWRFdmVudHMsXG4gIG5vdzogRGF0ZSxcbiAgbGF0ZXN0S25vd25UaW1lOiBEYXRlXG4pIHtcbiAgLy8gQWN0aXZlIGNoaWxkIHNwYW5zIGNhcCBhdCBsYXRlc3RLbm93blRpbWUgc28gdGhleSBkb24ndCBleHRlbmQgaW50b1xuICAvLyB1bmtub3duIHRlcnJpdG9yeS4gRXZlbiB3aGVuIHRoZSBydW4gaXMgY29tcGxldGVkLCB3ZSBtYXkgbm90IGhhdmUgbG9hZGVkXG4gIC8vIGFsbCBldmVudHMgeWV0LCBzbyBhbHdheXMgdXNlIHRoZSBsYXRlc3QgZXZlbnQgd2UndmUgYWN0dWFsbHkgc2Vlbi5cbiAgY29uc3QgY2hpbGRNYXhFbmQgPSBsYXRlc3RLbm93blRpbWU7XG4gIGNvbnN0IHJ1bk1heEVuZCA9IHJ1bi5jb21wbGV0ZWRBdCA/PyBub3c7XG5cbiAgY29uc3Qgc3RlcFNwYW5zID0gQXJyYXkuZnJvbShncm91cGVkRXZlbnRzLmV2ZW50c0J5U3RlcElkLnZhbHVlcygpKVxuICAgIC5tYXAoKGV2ZW50cykgPT4gc3RlcFRvU3BhbihldmVudHMsIGNoaWxkTWF4RW5kKSlcbiAgICAuZmlsdGVyKChzcGFuKTogc3BhbiBpcyBTcGFuID0+IHNwYW4gIT09IG51bGwpO1xuXG4gIGNvbnN0IGhvb2tTcGFucyA9IEFycmF5LmZyb20oZ3JvdXBlZEV2ZW50cy5ob29rRXZlbnRzLnZhbHVlcygpKVxuICAgIC5tYXAoKGV2ZW50cykgPT4gaG9va1RvU3BhbihldmVudHMsIGNoaWxkTWF4RW5kKSlcbiAgICAuZmlsdGVyKChzcGFuKTogc3BhbiBpcyBTcGFuID0+IHNwYW4gIT09IG51bGwpO1xuXG4gIGNvbnN0IHdhaXRTcGFucyA9IEFycmF5LmZyb20oZ3JvdXBlZEV2ZW50cy50aW1lckV2ZW50cy52YWx1ZXMoKSlcbiAgICAubWFwKChldmVudHMpID0+IHdhaXRUb1NwYW4oZXZlbnRzLCBjaGlsZE1heEVuZCwgcnVuTWF4RW5kKSlcbiAgICAuZmlsdGVyKChzcGFuKTogc3BhbiBpcyBTcGFuID0+IHNwYW4gIT09IG51bGwpO1xuXG4gIHJldHVybiB7XG4gICAgcnVuU3BhbjogcnVuVG9TcGFuKHJ1biwgZ3JvdXBlZEV2ZW50cy5ydW5MZXZlbEV2ZW50cywgbm93KSxcbiAgICBzcGFuczogWy4uLnN0ZXBTcGFucywgLi4uaG9va1NwYW5zLCAuLi53YWl0U3BhbnNdLFxuICB9O1xufVxuXG5mdW5jdGlvbiBjYXNjYWRlU3BhbnMocnVuU3BhbjogU3Bhbiwgc3BhbnM6IFNwYW5bXSkge1xuICBjb25zdCBzb3J0ZWRTcGFucyA9IFtcbiAgICBydW5TcGFuLFxuICAgIC4uLnNwYW5zLnNsaWNlKCkuc29ydCgoYSwgYikgPT4ge1xuICAgICAgY29uc3QgYVN0YXJ0ID0gb3RlbFRpbWVUb01zKGEuc3RhcnRUaW1lKTtcbiAgICAgIGNvbnN0IGJTdGFydCA9IG90ZWxUaW1lVG9NcyhiLnN0YXJ0VGltZSk7XG4gICAgICByZXR1cm4gYVN0YXJ0IC0gYlN0YXJ0O1xuICAgIH0pLFxuICBdO1xuXG4gIHJldHVybiBzb3J0ZWRTcGFucy5tYXAoKHNwYW4sIGluZGV4KSA9PiB7XG4gICAgY29uc3QgcGFyZW50U3BhbklkID1cbiAgICAgIGluZGV4ID09PSAwID8gdW5kZWZpbmVkIDogU3RyaW5nKHNvcnRlZFNwYW5zW2luZGV4IC0gMV0uc3BhbklkKTtcbiAgICByZXR1cm4ge1xuICAgICAgLi4uc3BhbixcbiAgICAgIHBhcmVudFNwYW5JZCxcbiAgICB9O1xuICB9KTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBUcmFjZVdpdGhNZXRhIHtcbiAgdHJhY2VJZDogc3RyaW5nO1xuICByb290U3BhbklkOiBzdHJpbmc7XG4gIHNwYW5zOiBTcGFuW107XG4gIHJlc291cmNlczogeyBuYW1lOiBzdHJpbmc7IGF0dHJpYnV0ZXM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gfVtdO1xuICAvKiogRHVyYXRpb24gaW4gbXMgZnJvbSB0cmFjZSBzdGFydCB0byB0aGUgbGF0ZXN0IGtub3duIGV2ZW50LiAqL1xuICBrbm93bkR1cmF0aW9uTXM6IG51bWJlcjtcbiAgLyoqXG4gICAqIFRoZSBldmVudHMgbGVmdCBvdXQgb2YgdGhlIHNwYW4gZ2VvbWV0cnkgYXMgcmVwZWF0cy4gRW1wdHkgdW5sZXNzIHRoZVxuICAgKiBjYWxsZXIgdm91Y2hlZCBmb3IgdGhlIGxvZyBiZWluZyBjb21wbGV0ZS4gU2VlXG4gICAqIHtAbGluayBmaW5kRHVwbGljYXRlRXZlbnRJZHN9LlxuICAgKi9cbiAgZHVwbGljYXRlRXZlbnRJZHM6IFJlYWRvbmx5U2V0PHN0cmluZz47XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBidWlsZFRyYWNlKFxuICBydW46IFdvcmtmbG93UnVuLFxuICBldmVudHM6IEV2ZW50W10sXG4gIG5vdzogRGF0ZSxcbiAgLyoqXG4gICAqIFdoZXRoZXIgYGV2ZW50c2AgaXMgdGhlIHJ1bidzIHdob2xlIGxvZy4gRGVmYXVsdHMgdG8gZmFsc2UsIHdoaWNoIGJ1aWxkc1xuICAgKiB0aGUgdHJhY2UgZnJvbSBldmVyeSBldmVudDogb24gYSBzdWJzZXQgdGhlcmUgaXMgbm8gd2F5IHRvIHRlbGwgYSByZXBlYXRcbiAgICogZnJvbSB0aGUgb25seSBjb3B5IHRoZSBjYWxsZXIgd2FzIGdpdmVuLCBhbmQgZHJvcHBpbmcgdGhlIHdyb25nIG9uZSBtb3Zlc1xuICAgKiBhIHNwYW4uIFNlZSB7QGxpbmsgZmluZER1cGxpY2F0ZUV2ZW50SWRzfS5cbiAgICovXG4gIHsgaXNDb21wbGV0ZUhpc3RvcnkgPSBmYWxzZSB9OiB7IGlzQ29tcGxldGVIaXN0b3J5PzogYm9vbGVhbiB9ID0ge31cbik6IFRyYWNlV2l0aE1ldGEge1xuICAvLyBTcGFuIGdlb21ldHJ5IGNvbWVzIGZyb20gd2hhdCB0aGUgcnVuIGFjdGVkIG9uLiBBIHJlcGVhdCBvZiBhIGNsYXNzIHRoZVxuICAvLyBsb2cgYWxyZWFkeSByZWNvcmRzIGlzIHJlYWQgcGFzdCBieSBldmVyeSByZXBsYXksIGFuZCBsZXR0aW5nIG9uZSB0aHJvdWdoXG4gIC8vIGhlcmUgd291bGQgc3RyZXRjaCBhIHNwYW4gdG8gd2hlbmV2ZXIgYSBjb25jdXJyZW50IHJlcGxheSBjb21taXR0ZWQgaXQuXG4gIC8vIFRoZSBldmVudCBsaXN0cyBzdGlsbCBzaG93IHRoZW0sIG1hcmtlZCBhcyByZXBlYXRzLlxuICBjb25zdCBkdXBsaWNhdGVFdmVudElkcyA9IGZpbmREdXBsaWNhdGVFdmVudElkcyhldmVudHMsIHtcbiAgICBpc0NvbXBsZXRlSGlzdG9yeSxcbiAgfSk7XG4gIGNvbnN0IGFjdGVkT25FdmVudHMgPVxuICAgIGR1cGxpY2F0ZUV2ZW50SWRzLnNpemUgPT09IDBcbiAgICAgID8gZXZlbnRzXG4gICAgICA6IGV2ZW50cy5maWx0ZXIoKGV2ZW50KSA9PiAhZHVwbGljYXRlRXZlbnRJZHMuaGFzKGV2ZW50LmV2ZW50SWQpKTtcblxuICBjb25zdCBncm91cGVkRXZlbnRzID0gZ3JvdXBFdmVudHNCeUNvcnJlbGF0aW9uKGFjdGVkT25FdmVudHMpO1xuICBjb25zdCBsYXRlc3RLbm93blRpbWUgPSBjb21wdXRlTGF0ZXN0S25vd25UaW1lKGFjdGVkT25FdmVudHMsIHJ1bik7XG4gIGNvbnN0IHsgcnVuU3Bhbiwgc3BhbnMgfSA9IGJ1aWxkU3BhbnMoXG4gICAgcnVuLFxuICAgIGdyb3VwZWRFdmVudHMsXG4gICAgbm93LFxuICAgIGxhdGVzdEtub3duVGltZVxuICApO1xuICBjb25zdCBzb3J0ZWRDYXNjYWRpbmdTcGFucyA9IGNhc2NhZGVTcGFucyhydW5TcGFuLCBzcGFucyk7XG5cbiAgLy8gQ29tcHV0ZSBrbm93biBkdXJhdGlvbiByZWxhdGl2ZSB0byB0cmFjZSBzdGFydFxuICBjb25zdCB0cmFjZVN0YXJ0TXMgPSBvdGVsVGltZVRvTXMocnVuU3Bhbi5zdGFydFRpbWUpO1xuICBjb25zdCBrbm93bkR1cmF0aW9uTXMgPSBsYXRlc3RLbm93blRpbWUuZ2V0VGltZSgpIC0gdHJhY2VTdGFydE1zO1xuXG4gIHJldHVybiB7XG4gICAgdHJhY2VJZDogcnVuLnJ1bklkLFxuICAgIHJvb3RTcGFuSWQ6IHJ1bi5ydW5JZCxcbiAgICBzcGFuczogc29ydGVkQ2FzY2FkaW5nU3BhbnMsXG4gICAgcmVzb3VyY2VzOiBbXG4gICAgICB7XG4gICAgICAgIG5hbWU6ICd3b3JrZmxvdycsXG4gICAgICAgIGF0dHJpYnV0ZXM6IHtcbiAgICAgICAgICAnc2VydmljZS5uYW1lJzogV09SS0ZMT1dfTElCUkFSWS5uYW1lLFxuICAgICAgICB9LFxuICAgICAgfSxcbiAgICBdLFxuICAgIGtub3duRHVyYXRpb25NczogTWF0aC5tYXgoMCwga25vd25EdXJhdGlvbk1zKSxcbiAgICBkdXBsaWNhdGVFdmVudElkcyxcbiAgfTtcbn1cbiJdfQ==
|
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.42",
|
|
5
5
|
"private": false,
|
|
6
6
|
"files": [
|
|
7
7
|
"dist",
|
|
@@ -55,9 +55,9 @@
|
|
|
55
55
|
"streamdown": "2.3.0",
|
|
56
56
|
"tailwind-merge": "3.5.0",
|
|
57
57
|
"tailwindcss": "4",
|
|
58
|
-
"@workflow/core": "5.0.0-beta.
|
|
58
|
+
"@workflow/core": "5.0.0-beta.42",
|
|
59
59
|
"@workflow/utils": "5.0.0-beta.8",
|
|
60
|
-
"@workflow/world": "5.0.0-beta.
|
|
60
|
+
"@workflow/world": "5.0.0-beta.27"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
63
|
"@biomejs/biome": "^2.4.4",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"ai": "6.0.116",
|
|
68
68
|
"typescript": "^6.0.3",
|
|
69
69
|
"vitest": "^4.1.10",
|
|
70
|
-
"@workflow/errors": "5.0.0-beta.
|
|
70
|
+
"@workflow/errors": "5.0.0-beta.17",
|
|
71
71
|
"@workflow/tsconfig": "5.0.0-beta.0"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
@@ -10,6 +10,7 @@ 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 { findDuplicateEventIds } from '../lib/duplicate-events';
|
|
13
14
|
import {
|
|
14
15
|
type ExactIdSearchResult,
|
|
15
16
|
type ExactWorkflowSearchIdKind,
|
|
@@ -23,6 +24,7 @@ import { AttrSetEventBlock } from './sidebar/attributes-block';
|
|
|
23
24
|
import { ContextCardProvider } from './ui/context-card';
|
|
24
25
|
import { DataInspector, DecryptClickContext } from './ui/data-inspector';
|
|
25
26
|
import { DecryptButton } from './ui/decrypt-button';
|
|
27
|
+
import { DuplicateEventTooltip } from './ui/duplicate-event-tooltip';
|
|
26
28
|
import {
|
|
27
29
|
ErrorStackBlock,
|
|
28
30
|
isStructuredError,
|
|
@@ -190,15 +192,24 @@ export interface DurationInfo {
|
|
|
190
192
|
* Build a map from correlationId → duration info by diffing
|
|
191
193
|
* created ↔ started (queued) and started ↔ completed/failed/cancelled (ran).
|
|
192
194
|
* Also computes run-level durations under the key '__run__'.
|
|
195
|
+
*
|
|
196
|
+
* Events every replay reads past as repeats are excluded: a second
|
|
197
|
+
* `step_completed` written by a concurrent replay would otherwise stretch the
|
|
198
|
+
* step's measured runtime to whenever that replay happened to commit. The
|
|
199
|
+
* caller supplies them, because whether an event is a repeat is a property of
|
|
200
|
+
* the whole log and this function may be handed a page of it.
|
|
193
201
|
*/
|
|
194
|
-
export function buildDurationMap(
|
|
202
|
+
export function buildDurationMap(
|
|
203
|
+
events: Event[],
|
|
204
|
+
duplicateEventIds: ReadonlySet<string> = new Set()
|
|
205
|
+
): Map<string, DurationInfo> {
|
|
195
206
|
// Process events in chronological order so the result doesn't depend on
|
|
196
207
|
// the caller's sort direction. Retried steps emit multiple `step_started`
|
|
197
208
|
// events for the same correlationId; the queued duration must be measured
|
|
198
209
|
// against the first one, not the last.
|
|
199
|
-
const chronological = [...events]
|
|
200
|
-
(
|
|
201
|
-
|
|
210
|
+
const chronological = [...events]
|
|
211
|
+
.filter((event) => !duplicateEventIds.has(event.eventId))
|
|
212
|
+
.sort((a, b) => getEffectiveEventTime(a) - getEffectiveEventTime(b));
|
|
202
213
|
|
|
203
214
|
const createdTimes = new Map<string, number>();
|
|
204
215
|
const firstStartedTimes = new Map<string, number>();
|
|
@@ -832,6 +843,7 @@ export function EventRow({
|
|
|
832
843
|
onEncryptedDataDetected,
|
|
833
844
|
suppressGroupDimming = false,
|
|
834
845
|
showSeparateEventOccurrenceTimestamps = false,
|
|
846
|
+
isDuplicate = false,
|
|
835
847
|
}: {
|
|
836
848
|
event: Event;
|
|
837
849
|
index: number;
|
|
@@ -856,6 +868,8 @@ export function EventRow({
|
|
|
856
868
|
suppressGroupDimming?: boolean;
|
|
857
869
|
/** Show occurredAt separately instead of folding it into the Created timestamp. */
|
|
858
870
|
showSeparateEventOccurrenceTimestamps?: boolean;
|
|
871
|
+
/** The event repeats a class already in the log, so the runtime ignored it. */
|
|
872
|
+
isDuplicate?: boolean;
|
|
859
873
|
}) {
|
|
860
874
|
const [isLoading, setIsLoading] = useState(false);
|
|
861
875
|
const [loadedEventData, setLoadedEventData] = useState<unknown | null>(
|
|
@@ -1095,43 +1109,49 @@ export function EventRow({
|
|
|
1095
1109
|
|
|
1096
1110
|
{/* Event Type */}
|
|
1097
1111
|
<div className="font-medium min-w-0 px-4" style={{ flex: '2 1 0%' }}>
|
|
1098
|
-
<
|
|
1099
|
-
className="inline-flex items-center gap-1.5"
|
|
1100
|
-
style={{ color: 'var(--ds-gray-900)' }}
|
|
1101
|
-
>
|
|
1112
|
+
<DuplicateEventTooltip isDuplicate={isDuplicate}>
|
|
1102
1113
|
<span
|
|
1114
|
+
className="inline-flex items-center gap-1.5"
|
|
1103
1115
|
style={{
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
height: 6,
|
|
1108
|
-
flexShrink: 0,
|
|
1116
|
+
color: isDuplicate
|
|
1117
|
+
? 'var(--ds-gray-700)'
|
|
1118
|
+
: 'var(--ds-gray-900)',
|
|
1109
1119
|
}}
|
|
1110
1120
|
>
|
|
1111
|
-
{isPulsing && (
|
|
1112
|
-
<span
|
|
1113
|
-
style={{
|
|
1114
|
-
position: 'absolute',
|
|
1115
|
-
inset: 0,
|
|
1116
|
-
borderRadius: '50%',
|
|
1117
|
-
backgroundColor: statusDotColor,
|
|
1118
|
-
opacity: 0.75,
|
|
1119
|
-
animation: DOT_PULSE_ANIMATION,
|
|
1120
|
-
}}
|
|
1121
|
-
/>
|
|
1122
|
-
)}
|
|
1123
1121
|
<span
|
|
1124
1122
|
style={{
|
|
1125
1123
|
position: 'relative',
|
|
1124
|
+
display: 'inline-flex',
|
|
1126
1125
|
width: 6,
|
|
1127
1126
|
height: 6,
|
|
1128
|
-
|
|
1129
|
-
backgroundColor: statusDotColor,
|
|
1127
|
+
flexShrink: 0,
|
|
1130
1128
|
}}
|
|
1131
|
-
|
|
1129
|
+
>
|
|
1130
|
+
{isPulsing && (
|
|
1131
|
+
<span
|
|
1132
|
+
style={{
|
|
1133
|
+
position: 'absolute',
|
|
1134
|
+
inset: 0,
|
|
1135
|
+
borderRadius: '50%',
|
|
1136
|
+
backgroundColor: statusDotColor,
|
|
1137
|
+
opacity: 0.75,
|
|
1138
|
+
animation: DOT_PULSE_ANIMATION,
|
|
1139
|
+
}}
|
|
1140
|
+
/>
|
|
1141
|
+
)}
|
|
1142
|
+
<span
|
|
1143
|
+
style={{
|
|
1144
|
+
position: 'relative',
|
|
1145
|
+
width: 6,
|
|
1146
|
+
height: 6,
|
|
1147
|
+
borderRadius: '50%',
|
|
1148
|
+
backgroundColor: statusDotColor,
|
|
1149
|
+
}}
|
|
1150
|
+
/>
|
|
1151
|
+
</span>
|
|
1152
|
+
{formatEventType(event.eventType)}
|
|
1132
1153
|
</span>
|
|
1133
|
-
|
|
1134
|
-
</span>
|
|
1154
|
+
</DuplicateEventTooltip>
|
|
1135
1155
|
</div>
|
|
1136
1156
|
|
|
1137
1157
|
{/* Name */}
|
|
@@ -1310,6 +1330,23 @@ function EventListViewInner({
|
|
|
1310
1330
|
);
|
|
1311
1331
|
}, [events, effectiveSortOrder, isExactSearchActive, searchResults]);
|
|
1312
1332
|
|
|
1333
|
+
// Events every replay reads past as repeats. Computed from the source list
|
|
1334
|
+
// rather than `sortedEvents` because which occurrence counted is a property
|
|
1335
|
+
// of the log, not of the direction the table happens to be sorted in.
|
|
1336
|
+
//
|
|
1337
|
+
// A page short of the whole log, or an exact-ID search that returns one
|
|
1338
|
+
// event, cannot answer that question: the event a repeat lost to may be
|
|
1339
|
+
// outside the window, and reading it the other way round would mark the
|
|
1340
|
+
// event the run acted on and drop it from the durations. Both cases classify
|
|
1341
|
+
// nothing.
|
|
1342
|
+
const duplicateEventIds = useMemo(
|
|
1343
|
+
() =>
|
|
1344
|
+
findDuplicateEventIds(events ?? [], {
|
|
1345
|
+
isCompleteHistory: !hasMoreEvents && !isExactSearchActive,
|
|
1346
|
+
}),
|
|
1347
|
+
[events, hasMoreEvents, isExactSearchActive]
|
|
1348
|
+
);
|
|
1349
|
+
|
|
1313
1350
|
// Detect encrypted fields across all loaded events (inline eventData).
|
|
1314
1351
|
const hasEncryptedInlineData = useMemo(() => {
|
|
1315
1352
|
const sourceEvents = isExactSearchActive ? searchResults : events;
|
|
@@ -1342,8 +1379,8 @@ function EventListViewInner({
|
|
|
1342
1379
|
);
|
|
1343
1380
|
|
|
1344
1381
|
const durationMap = useMemo(
|
|
1345
|
-
() => buildDurationMap(sortedEvents),
|
|
1346
|
-
[sortedEvents]
|
|
1382
|
+
() => buildDurationMap(sortedEvents, duplicateEventIds),
|
|
1383
|
+
[sortedEvents, duplicateEventIds]
|
|
1347
1384
|
);
|
|
1348
1385
|
|
|
1349
1386
|
const [selectedGroupKey, setSelectedGroupKey] = useState<string | undefined>(
|
|
@@ -1804,6 +1841,7 @@ function EventListViewInner({
|
|
|
1804
1841
|
encryptionKey={encryptionKey}
|
|
1805
1842
|
onEncryptedDataDetected={handleEncryptedDataDetected}
|
|
1806
1843
|
suppressGroupDimming={isExactSearchActive}
|
|
1844
|
+
isDuplicate={duplicateEventIds.has(ev.eventId)}
|
|
1807
1845
|
showSeparateEventOccurrenceTimestamps={
|
|
1808
1846
|
showSeparateEventOccurrenceTimestamps
|
|
1809
1847
|
}
|
|
@@ -92,8 +92,7 @@ function DetailKeyValueRowBase({
|
|
|
92
92
|
>
|
|
93
93
|
<MiddleTruncate
|
|
94
94
|
value={copyText}
|
|
95
|
-
className="min-w-0 text-right"
|
|
96
|
-
style={{ gridTemplateColumns: 'minmax(0, 1fr)' }}
|
|
95
|
+
className="min-w-0 grid-cols-[minmax(0,1fr)] text-right"
|
|
97
96
|
/>
|
|
98
97
|
<ArrowUpRight aria-hidden className="h-3 w-3 shrink-0" />
|
|
99
98
|
</a>
|
|
@@ -101,9 +100,9 @@ function DetailKeyValueRowBase({
|
|
|
101
100
|
<MiddleTruncate
|
|
102
101
|
value={copyText}
|
|
103
102
|
className={cn(
|
|
104
|
-
rowValueVariants({ variant, className: 'text-right' })
|
|
103
|
+
rowValueVariants({ variant, className: 'text-right' }),
|
|
104
|
+
'grid-cols-[minmax(0,1fr)]'
|
|
105
105
|
)}
|
|
106
|
-
style={{ gridTemplateColumns: 'minmax(0, 1fr)' }}
|
|
107
106
|
/>
|
|
108
107
|
)}
|
|
109
108
|
<CopyButton
|
|
@@ -311,13 +310,7 @@ export function AttrSetEventBlock({ data }: { data: unknown }) {
|
|
|
311
310
|
))}
|
|
312
311
|
</div>
|
|
313
312
|
{writerLabel && (
|
|
314
|
-
<div
|
|
315
|
-
className="border-t py-1.5 text-label-12"
|
|
316
|
-
style={{
|
|
317
|
-
borderColor: 'var(--ds-gray-alpha-400)',
|
|
318
|
-
color: 'var(--ds-gray-700)',
|
|
319
|
-
}}
|
|
320
|
-
>
|
|
313
|
+
<div className="border-gray-alpha-400 border-t py-1.5 text-gray-700 text-label-12">
|
|
321
314
|
{writerLabel}
|
|
322
315
|
</div>
|
|
323
316
|
)}
|
|
@@ -9,10 +9,7 @@ interface ConversationViewProps {
|
|
|
9
9
|
export function ConversationView({ messages }: ConversationViewProps) {
|
|
10
10
|
if (messages.length === 0) {
|
|
11
11
|
return (
|
|
12
|
-
<div
|
|
13
|
-
className="text-center py-8 text-[11px]"
|
|
14
|
-
style={{ color: 'var(--ds-gray-600)' }}
|
|
15
|
-
>
|
|
12
|
+
<div className="py-8 text-center text-[11px] text-gray-600">
|
|
16
13
|
No messages
|
|
17
14
|
</div>
|
|
18
15
|
);
|
|
@@ -30,23 +27,13 @@ export function ConversationView({ messages }: ConversationViewProps) {
|
|
|
30
27
|
function MessageBubble({ message }: { message: ModelMessage }) {
|
|
31
28
|
const role = message.role;
|
|
32
29
|
const parts = parseContent(message.content);
|
|
33
|
-
const
|
|
30
|
+
const roleClasses = getRoleClasses(role);
|
|
34
31
|
|
|
35
32
|
return (
|
|
36
|
-
<div
|
|
37
|
-
className="rounded-md border text-[11px]"
|
|
38
|
-
style={{
|
|
39
|
-
backgroundColor: style.bg,
|
|
40
|
-
borderColor: style.border,
|
|
41
|
-
}}
|
|
42
|
-
>
|
|
33
|
+
<div className={`rounded-md border text-[11px] ${roleClasses.bubble}`}>
|
|
43
34
|
{/* Role header */}
|
|
44
35
|
<div
|
|
45
|
-
className=
|
|
46
|
-
style={{
|
|
47
|
-
borderColor: style.border,
|
|
48
|
-
color: style.label,
|
|
49
|
-
}}
|
|
36
|
+
className={`border-b px-2.5 py-1 font-medium text-[10px] uppercase tracking-wide ${roleClasses.label}`}
|
|
50
37
|
>
|
|
51
38
|
{role}
|
|
52
39
|
</div>
|
|
@@ -76,20 +63,14 @@ function ContentPart({ part, role }: { part: ParsedPart; role: string }) {
|
|
|
76
63
|
// Use Streamdown for assistant messages (they often contain markdown)
|
|
77
64
|
if (role === 'assistant') {
|
|
78
65
|
return (
|
|
79
|
-
<div
|
|
80
|
-
className="prose prose-sm max-w-none text-[11px] [&>*:first-child]:mt-0 [&>*:last-child]:mb-0"
|
|
81
|
-
style={{ color: 'var(--ds-gray-1000)' }}
|
|
82
|
-
>
|
|
66
|
+
<div className="prose prose-sm max-w-none text-[11px] text-gray-1000 [&>*:first-child]:mt-0 [&>*:last-child]:mb-0">
|
|
83
67
|
<Streamdown>{part.text}</Streamdown>
|
|
84
68
|
</div>
|
|
85
69
|
);
|
|
86
70
|
}
|
|
87
71
|
|
|
88
72
|
return (
|
|
89
|
-
<div
|
|
90
|
-
className="whitespace-pre-wrap break-words"
|
|
91
|
-
style={{ color: 'var(--ds-gray-1000)' }}
|
|
92
|
-
>
|
|
73
|
+
<div className="whitespace-pre-wrap break-words text-gray-1000">
|
|
93
74
|
{part.text}
|
|
94
75
|
</div>
|
|
95
76
|
);
|
|
@@ -97,29 +78,15 @@ function ContentPart({ part, role }: { part: ParsedPart; role: string }) {
|
|
|
97
78
|
|
|
98
79
|
if (part.type === 'tool-call') {
|
|
99
80
|
return (
|
|
100
|
-
<div
|
|
101
|
-
className="rounded border px-2 py-1.5"
|
|
102
|
-
style={{
|
|
103
|
-
backgroundColor: 'var(--ds-purple-100)',
|
|
104
|
-
borderColor: 'var(--ds-purple-300)',
|
|
105
|
-
}}
|
|
106
|
-
>
|
|
81
|
+
<div className="rounded border border-purple-300 bg-purple-100 px-2 py-1.5">
|
|
107
82
|
<div className="flex items-center gap-1.5 text-[10px] font-medium">
|
|
108
83
|
<span>🔧</span>
|
|
109
|
-
<span
|
|
84
|
+
<span className="text-purple-900">{part.toolName}</span>
|
|
110
85
|
</div>
|
|
111
86
|
{part.input != null && (
|
|
112
|
-
<div
|
|
113
|
-
className="mt-1.5 overflow-x-auto p-1.5 rounded"
|
|
114
|
-
style={{ backgroundColor: 'var(--ds-gray-100)' }}
|
|
115
|
-
>
|
|
87
|
+
<div className="mt-1.5 overflow-x-auto rounded bg-gray-100 p-1.5">
|
|
116
88
|
{typeof part.input === 'string' ? (
|
|
117
|
-
<pre
|
|
118
|
-
className="text-[10px]"
|
|
119
|
-
style={{ color: 'var(--ds-gray-800)' }}
|
|
120
|
-
>
|
|
121
|
-
{part.input}
|
|
122
|
-
</pre>
|
|
89
|
+
<pre className="text-[10px] text-gray-800">{part.input}</pre>
|
|
123
90
|
) : (
|
|
124
91
|
<DataInspector data={part.input} />
|
|
125
92
|
)}
|
|
@@ -131,31 +98,15 @@ function ContentPart({ part, role }: { part: ParsedPart; role: string }) {
|
|
|
131
98
|
|
|
132
99
|
if (part.type === 'tool-result') {
|
|
133
100
|
return (
|
|
134
|
-
<div
|
|
135
|
-
className="rounded border px-2 py-1.5"
|
|
136
|
-
style={{
|
|
137
|
-
backgroundColor: 'var(--ds-green-100)',
|
|
138
|
-
borderColor: 'var(--ds-green-300)',
|
|
139
|
-
}}
|
|
140
|
-
>
|
|
101
|
+
<div className="rounded border border-green-300 bg-green-100 px-2 py-1.5">
|
|
141
102
|
<div className="flex items-center gap-1.5 text-[10px] font-medium">
|
|
142
103
|
<span>✓</span>
|
|
143
|
-
<span
|
|
144
|
-
{part.toolName} result
|
|
145
|
-
</span>
|
|
104
|
+
<span className="text-green-900">{part.toolName} result</span>
|
|
146
105
|
</div>
|
|
147
106
|
{part.output != null && (
|
|
148
|
-
<div
|
|
149
|
-
className="mt-1.5 overflow-x-auto max-h-[200px] overflow-y-auto p-1.5 rounded"
|
|
150
|
-
style={{ backgroundColor: 'var(--ds-gray-100)' }}
|
|
151
|
-
>
|
|
107
|
+
<div className="mt-1.5 max-h-[200px] overflow-x-auto overflow-y-auto rounded bg-gray-100 p-1.5">
|
|
152
108
|
{typeof part.output === 'string' ? (
|
|
153
|
-
<pre
|
|
154
|
-
className="text-[10px]"
|
|
155
|
-
style={{ color: 'var(--ds-gray-800)' }}
|
|
156
|
-
>
|
|
157
|
-
{part.output}
|
|
158
|
-
</pre>
|
|
109
|
+
<pre className="text-[10px] text-gray-800">{part.output}</pre>
|
|
159
110
|
) : (
|
|
160
111
|
<DataInspector data={part.output} expandLevel={1} />
|
|
161
112
|
)}
|
|
@@ -168,37 +119,32 @@ function ContentPart({ part, role }: { part: ParsedPart; role: string }) {
|
|
|
168
119
|
return null;
|
|
169
120
|
}
|
|
170
121
|
|
|
171
|
-
function
|
|
122
|
+
function getRoleClasses(role: string): { bubble: string; label: string } {
|
|
172
123
|
switch (role) {
|
|
173
124
|
case 'user':
|
|
174
125
|
return {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
label: 'var(--ds-blue-700)',
|
|
126
|
+
bubble: 'border-blue-300 bg-blue-100',
|
|
127
|
+
label: 'border-blue-300 text-blue-700',
|
|
178
128
|
};
|
|
179
129
|
case 'assistant':
|
|
180
130
|
return {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
label: 'var(--ds-gray-700)',
|
|
131
|
+
bubble: 'border-gray-300 bg-gray-100',
|
|
132
|
+
label: 'border-gray-300 text-gray-700',
|
|
184
133
|
};
|
|
185
134
|
case 'system':
|
|
186
135
|
return {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
label: 'var(--ds-amber-700)',
|
|
136
|
+
bubble: 'border-amber-300 bg-amber-100',
|
|
137
|
+
label: 'border-amber-300 text-amber-700',
|
|
190
138
|
};
|
|
191
139
|
case 'tool':
|
|
192
140
|
return {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
label: 'var(--ds-green-700)',
|
|
141
|
+
bubble: 'border-green-300 bg-green-100',
|
|
142
|
+
label: 'border-green-300 text-green-700',
|
|
196
143
|
};
|
|
197
144
|
default:
|
|
198
145
|
return {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
label: 'var(--ds-gray-700)',
|
|
146
|
+
bubble: 'border-gray-300 bg-gray-100',
|
|
147
|
+
label: 'border-gray-300 text-gray-700',
|
|
202
148
|
};
|
|
203
149
|
}
|
|
204
150
|
}
|
|
@@ -47,6 +47,11 @@ export interface SelectedSpanInfo {
|
|
|
47
47
|
spanId?: string;
|
|
48
48
|
/** Raw correlated events from the store (NOT from the trace worker pipeline) */
|
|
49
49
|
rawEvents?: Event[];
|
|
50
|
+
/**
|
|
51
|
+
* Events every replay reads past as repeats, computed from the whole log.
|
|
52
|
+
* `rawEvents` is one entity's slice, which cannot answer that on its own.
|
|
53
|
+
*/
|
|
54
|
+
duplicateEventIds?: ReadonlySet<string>;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
/**
|
|
@@ -402,6 +407,7 @@ export function EntityDetailPanel({
|
|
|
402
407
|
showSeparateEventOccurrenceTimestamps={
|
|
403
408
|
showSeparateEventOccurrenceTimestamps
|
|
404
409
|
}
|
|
410
|
+
duplicateEventIds={selectedSpan?.duplicateEventIds}
|
|
405
411
|
/>
|
|
406
412
|
)}
|
|
407
413
|
</div>
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
CollapsibleTrigger,
|
|
11
11
|
} from '../ui/collapsible';
|
|
12
12
|
import { RunClickContext, StreamClickContext } from '../ui/data-inspector';
|
|
13
|
+
import { DuplicateEventTooltip } from '../ui/duplicate-event-tooltip';
|
|
13
14
|
import { ErrorCard } from '../ui/error-card';
|
|
14
15
|
import { ErrorStackBlock, isStructuredError } from '../ui/error-stack-block';
|
|
15
16
|
import { Skeleton } from '../ui/skeleton';
|
|
@@ -47,6 +48,7 @@ function EventItem({
|
|
|
47
48
|
onLoadEventData,
|
|
48
49
|
encryptionKey,
|
|
49
50
|
showSeparateEventOccurrenceTimestamps = false,
|
|
51
|
+
isDuplicate = false,
|
|
50
52
|
}: {
|
|
51
53
|
event: Event;
|
|
52
54
|
onLoadEventData?: (event: Event) => Promise<unknown | null>;
|
|
@@ -54,6 +56,8 @@ function EventItem({
|
|
|
54
56
|
encryptionKey?: Uint8Array;
|
|
55
57
|
/** Show occurredAt separately instead of folding it into the Created timestamp. */
|
|
56
58
|
showSeparateEventOccurrenceTimestamps?: boolean;
|
|
59
|
+
/** The event repeats a class already in the log, so the runtime ignored it. */
|
|
60
|
+
isDuplicate?: boolean;
|
|
57
61
|
}) {
|
|
58
62
|
const [loadedData, setLoadedData] = useState<unknown | null>(null);
|
|
59
63
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -135,9 +139,15 @@ function EventItem({
|
|
|
135
139
|
>
|
|
136
140
|
<CollapsibleTrigger className="px-3 py-2">
|
|
137
141
|
<div className="flex w-full items-center justify-between gap-3">
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
<DuplicateEventTooltip isDuplicate={isDuplicate}>
|
|
143
|
+
<span
|
|
144
|
+
className={`text-label-12 font-mono ${
|
|
145
|
+
isDuplicate ? 'text-gray-700' : 'text-gray-1000'
|
|
146
|
+
}`}
|
|
147
|
+
>
|
|
148
|
+
{event.eventType}
|
|
149
|
+
</span>
|
|
150
|
+
</DuplicateEventTooltip>
|
|
141
151
|
<span className="shrink-0 text-label-13 text-gray-900">
|
|
142
152
|
{displayedCreatedAtTime}
|
|
143
153
|
</span>
|
|
@@ -294,6 +304,7 @@ export function EventsList({
|
|
|
294
304
|
onRunClick,
|
|
295
305
|
encryptionKey,
|
|
296
306
|
showSeparateEventOccurrenceTimestamps = false,
|
|
307
|
+
duplicateEventIds,
|
|
297
308
|
}: {
|
|
298
309
|
events: Event[];
|
|
299
310
|
isLoading?: boolean;
|
|
@@ -305,6 +316,12 @@ export function EventsList({
|
|
|
305
316
|
encryptionKey?: Uint8Array;
|
|
306
317
|
/** Show occurredAt separately instead of folding it into the Created timestamp. */
|
|
307
318
|
showSeparateEventOccurrenceTimestamps?: boolean;
|
|
319
|
+
/**
|
|
320
|
+
* Events every replay reads past as repeats, from the caller that holds the
|
|
321
|
+
* whole log. `events` here is one entity's slice of it, which cannot answer
|
|
322
|
+
* the question on its own.
|
|
323
|
+
*/
|
|
324
|
+
duplicateEventIds?: ReadonlySet<string>;
|
|
308
325
|
}) {
|
|
309
326
|
// Sort by the timestamp shown as Created by default.
|
|
310
327
|
const sortedEvents = useMemo(
|
|
@@ -352,6 +369,7 @@ export function EventsList({
|
|
|
352
369
|
showSeparateEventOccurrenceTimestamps={
|
|
353
370
|
showSeparateEventOccurrenceTimestamps
|
|
354
371
|
}
|
|
372
|
+
isDuplicate={duplicateEventIds?.has(event.eventId)}
|
|
355
373
|
/>
|
|
356
374
|
))}
|
|
357
375
|
</div>
|