Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
engine-strict=true
@buf:registry=https://buf.build/gen/npm/v1/
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@
"devDependencies": {
"@axe-core/playwright": "^4.10.1",
"@babel/core": "^7.20.12",
"@babel/preset-typescript": "^7.18.6",
"@buf/temporalio_api.bufbuild_es": "2.12.0-20260602195324-874c8d65f615.1",
"@bufbuild/protobuf": "^2.12.0",
"@chromatic-com/storybook": "^3.2.6",
"@eslint/js": "^9.39.2",
"@playwright/test": "^1.55.1",
Expand Down
1,668 changes: 885 additions & 783 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

50 changes: 41 additions & 9 deletions src/lib/components/event/event-card.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import PayloadCodeBlock from '$lib/components/payload/payload-code-block.svelte';
import PayloadSummary from '$lib/components/payload/payload-summary.svelte';
import SystemNexusOperationRenderer from '$lib/components/payload/system-nexus-operation-renderer.svelte';
import Timestamp from '$lib/components/timestamp.svelte';
import CodeBlock from '$lib/holocene/code-block.svelte';
import Copyable from '$lib/holocene/copyable/index.svelte';
Expand All @@ -11,6 +12,7 @@
import type { EventLink as ELink } from '$lib/types';
import { type Payload as RawPayload } from '$lib/types';
import type { WorkflowEvent } from '$lib/types/events';
import { isRawPayload } from '$lib/utilities/decode-payload';
import { getEventLinkHref } from '$lib/utilities/event-link-href';
import {
format,
Expand All @@ -23,7 +25,9 @@
getStackTrace,
shouldDisplayAsTime,
} from '$lib/utilities/get-single-attribute-for-event';
import { getSystemNexusEventDisplay } from '$lib/utilities/get-system-nexus-event-display';
import { isLocalActivityMarkerEvent } from '$lib/utilities/is-event-type';
import { describeNexusOperation } from '$lib/utilities/nexus-operation-registry';
import {
routeForEventHistoryEvent,
routeForNamespace,
Expand All @@ -34,17 +38,26 @@
let { event }: { event: WorkflowEvent } = $props();
const { namespace, workflow, run } = $derived(page.params);

const displayName = $derived(
isLocalActivityMarkerEvent(event)
? translate('events.category.local-activity')
: spaceBetweenCapitalLetters(event.name),
);
const systemNexus = $derived(getSystemNexusEventDisplay(event));

const attributes = $derived.by(() => {
const attrs = formatAttributes(event);
if (event?.principal?.name) attrs.principalName = event.principal.name;
if (event?.principal?.type) attrs.principalType = event.principal.type;
if (systemNexus?.extraAttributes) {
const extra = attrs as Record<string, unknown>;
Object.assign(extra, systemNexus.extraAttributes);
}
return attrs;
});

const displayName = $derived(
systemNexus?.displayName ??
(isLocalActivityMarkerEvent(event)
? translate('events.category.local-activity')
: spaceBetweenCapitalLetters(event.name)),
);

const fields = $derived(Object.entries(attributes));
const payloadFields = $derived(
fields.filter(
Expand All @@ -59,11 +72,22 @@
);

const hiddenDetailFields = $derived.by(() => {
const systemNexusFields = systemNexus?.hiddenFields ?? [];
if (event.category === 'activity')
return ['scheduledEventId', 'startedEventId', 'namespaceId'];
return [
...systemNexusFields,
'scheduledEventId',
'startedEventId',
'namespaceId',
];
if (event.category === 'child-workflow')
return ['initiatedEventId', 'startedEventId', 'namespaceId'];
return ['namespaceId'];
return [
...systemNexusFields,
'initiatedEventId',
'startedEventId',
'namespaceId',
];
return [...systemNexusFields, 'namespaceId'];
});
const detailFields = $derived(
fields.filter(
Expand Down Expand Up @@ -177,11 +201,19 @@
{#snippet payloads(key, value)}
{@const codeBlockValue = getCodeBlockValue(value)}
{@const stackTrace = getStackTrace(codeBlockValue)}
{@const nexusDescriptor = isRawPayload(codeBlockValue)
? describeNexusOperation(codeBlockValue as RawPayload)
: null}
<div>
<p class="mb-1 min-w-56 text-sm text-secondary/80">
{format(key)}
</p>
{#if value?.payloads}
{#if nexusDescriptor}
<SystemNexusOperationRenderer
payload={codeBlockValue as RawPayload}
maxHeight={384}
/>
{:else if value?.payloads}
<PayloadCodeBlock
filenameData={{
workflowId: workflow,
Expand Down
45 changes: 31 additions & 14 deletions src/lib/components/event/event-summary-row.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import { isCloud } from '$lib/stores/advanced-visibility';
import type { IterableEvent, WorkflowEvent } from '$lib/types/events';
import { decodeLocalActivity } from '$lib/utilities/decode-local-activity';
import { isRawPayload } from '$lib/utilities/decode-payload';

Check warning on line 25 in src/lib/components/event/event-summary-row.svelte

View workflow job for this annotation

GitHub Actions / lint

'isRawPayload' is defined but never used. Allowed unused vars must match /^_|^\$\$(Props|Events|Slots)$/u
import { formatEventGroupDuration } from '$lib/utilities/event-group-duration';
import { spaceBetweenCapitalLetters } from '$lib/utilities/format-camel-case';
import { formatAttributes } from '$lib/utilities/format-event-attributes';
Expand All @@ -31,6 +32,7 @@
getPrimaryAttributeForEvent,
getSecondaryAttributeForEvent,
} from '$lib/utilities/get-single-attribute-for-event';
import { getSystemNexusEventDisplay } from '$lib/utilities/get-system-nexus-event-display';
import {
isActivityTaskStartedEvent,
isLocalActivityMarkerEvent,
Expand Down Expand Up @@ -109,26 +111,41 @@
const canceled = $derived(eventOrGroupIsCanceled(event));
const terminated = $derived(eventOrGroupIsTerminated(event));

const displayName = $derived(
isEventGroup(event)
? event.pendingActivity
? translate('workflows.pending-activity')
: event.pendingNexusOperation
? translate('workflows.pending-nexus-operation')
: event.label
: isLocalActivityMarkerEvent(event)
? translate('events.category.local-activity')
: spaceBetweenCapitalLetters(event.name),
const systemNexus = $derived(
!isEventGroup(event)
? getSystemNexusEventDisplay(event as WorkflowEvent)
: null,
);

const displayName = $derived.by(() => {
if (isEventGroup(event)) {
if (event.pendingActivity) return translate('workflows.pending-activity');
if (event.pendingNexusOperation)
return translate('workflows.pending-nexus-operation');
return event.label;
}
if (isLocalActivityMarkerEvent(event))
return translate('events.category.local-activity');
if (systemNexus?.displayName) return systemNexus.displayName;
return spaceBetweenCapitalLetters(event.name);
});

const primaryAttribute = $derived(
!isLocalActivityMarkerEvent(event)
!isLocalActivityMarkerEvent(event) && !systemNexus
? getPrimaryAttributeForEvent(
isEventGroup(event) ? event.initialEvent : event,
)
: undefined,
);

const effectiveCategory = $derived(
systemNexus
? 'signal'
: isEventGroup(event)
? event.category
: event.category,
);

const secondaryAttribute = $derived(
getSecondaryAttributeForEvent(
isEventGroup(event) ? event.lastEvent : event,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ⚠️ Argument of type 'string | undefined' is not assignable to parameter of type 'string'.

Expand Down Expand Up @@ -215,7 +232,7 @@
{#if isEventGroup(event)}
<td class="font-mono">
<div class="flex items-center gap-0.5">
{#each event.eventList as groupEvent}

Check warning on line 235 in src/lib/components/event/event-summary-row.svelte

View workflow job for this annotation

GitHub Actions / lint

Each block should have a key
<Link
data-testid="link"
href={routeForEventHistoryEvent({
Expand Down Expand Up @@ -270,10 +287,10 @@
</Tooltip>
</td>
<td class="truncate">
<p class={eventTypeStyle({ category: event.category })}>
<p class={eventTypeStyle({ category: effectiveCategory })}>
<Icon
name={CategoryIcon[event.category].name}
title={CategoryIcon[event.category].title}
name={CategoryIcon[effectiveCategory].name}
title={CategoryIcon[effectiveCategory].title}
class={merge(
'mr-1 inline',
isEventGroup(event) && event.isPending && 'animate-pulse',
Expand Down
20 changes: 16 additions & 4 deletions src/lib/components/lines-and-dots/svg/timeline-graph-row.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import {
isActivityTaskScheduledEvent,
isActivityTaskStartedEvent,
isNexusOperationScheduledEvent,
} from '$lib/utilities/is-event-type';

import {
Expand Down Expand Up @@ -58,6 +59,16 @@
pendingActivity && pendingActivity.pauseInfo?.pauseTime,
);

const systemNexusCategory = $derived.by((): typeof group.category | null => {
if (!isNexusOperationScheduledEvent(group.initialEvent)) return null;
const attrs = group.initialEvent.nexusOperationScheduledEventAttributes;
if (String(attrs.endpoint ?? '') !== '__temporal_system') return null;
if (attrs.operation === 'SignalWithStartWorkflowExecution') return 'signal';
return null;
});

const effectiveCategory = $derived(systemNexusCategory ?? group.category);

let decodedLocalActivity: SummaryAttribute | undefined = $state(undefined);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ⚠️ 'attrs' is possibly 'null' or 'undefined'.

onMount(async () => {
Expand Down Expand Up @@ -189,7 +200,7 @@
>
<div
class={groupHover({
category: group ? group.category : 'default',
category: group ? effectiveCategory : 'default',
})}
></div>
</foreignObject>
Expand Down Expand Up @@ -224,7 +235,7 @@
<Line
startPoint={[x, y]}
endPoint={[nextPoint, y]}
category={group.category}
category={effectiveCategory}
classification={group.lastEvent.classification}
pending={!!pauseTime}
paused={!!pauseTime}
Expand All @@ -242,7 +253,7 @@
? pendingActivity.attempt > 1
? 'retry'
: 'pending'
: group.category}
: effectiveCategory}
classification={group.lastEvent.classification}
pending
paused={!!pauseTime}
Expand Down Expand Up @@ -295,12 +306,13 @@
{/if}
<Dot
point={[x, y]}
category={effectiveCategory}
classification={group.eventList[index]?.classification}
icon={pauseTime && index !== 0
? 'pause'
: decodedLocalActivity
? CategoryIcon['local-activity'].name
: CategoryIcon[group.category].name}
: CategoryIcon[effectiveCategory].name}
r={radius}
/>
{/each}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<script lang="ts">
import type { Component } from 'svelte';

import type { Payload } from '$lib/types';
import {
describeNexusOperation,
type NexusEmbeddedOperationKind,
type NexusOperationDescriptor,
} from '$lib/utilities/nexus-operation-registry';

import DefaultRenderer from './system-nexus/default-renderer.svelte';
import SignalWithStartRenderer from './system-nexus/signal-with-start-renderer.svelte';

interface Props {
payload: Payload;
maxHeight?: number;
}

let { payload, maxHeight }: Props = $props();

type RendererProps = {
descriptor: NexusOperationDescriptor;
maxHeight?: number;
};

const RENDERERS: Partial<
Record<NexusEmbeddedOperationKind, Component<RendererProps>>
> = {
'signal-with-start-workflow': SignalWithStartRenderer,
};

const descriptor: NexusOperationDescriptor | null = $derived(
describeNexusOperation(payload),
);

const Renderer = $derived(
descriptor ? (RENDERERS[descriptor.kind] ?? DefaultRenderer) : null,
);
</script>

{#if descriptor && Renderer}
<Renderer {descriptor} {maxHeight} />
{/if}
13 changes: 13 additions & 0 deletions src/lib/components/payload/system-nexus/default-renderer.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts">
import PayloadCodeBlock from '$lib/components/payload/payload-code-block.svelte';
import type { NexusOperationDescriptor } from '$lib/utilities/nexus-operation-registry';

interface Props {
descriptor: NexusOperationDescriptor;
maxHeight?: number;
}

let { descriptor, maxHeight }: Props = $props();
</script>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ⚠️ Type 'IPayload[] | null | undefined' is not assignable to type 'object | IPayloads | IPayload'.

<PayloadCodeBlock value={descriptor.embeddedInput} {maxHeight} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts">
import PayloadCodeBlock from '$lib/components/payload/payload-code-block.svelte';
import type { NexusOperationDescriptor } from '$lib/utilities/nexus-operation-registry';

interface Props {
descriptor: NexusOperationDescriptor;
maxHeight?: number;
}

let { descriptor, maxHeight }: Props = $props();
</script>

<div class="flex flex-col gap-2">
{#if descriptor.embeddedInput != null || descriptor.workflowInput != null}
{#if descriptor.embeddedInput != null}
{#if descriptor.workflowInput != null}
<p class="text-xs text-secondary/70">Signal Input</p>
{/if}
<PayloadCodeBlock value={descriptor.embeddedInput} {maxHeight} />
{/if}
{#if descriptor.workflowInput != null}
<p class="text-xs text-secondary/70">Workflow Input</p>
<PayloadCodeBlock value={descriptor.workflowInput} {maxHeight} />
{/if}
{:else}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • ⚠️ Type 'null' is not assignable to type 'object | IPayloads | IPayload'.

<PayloadCodeBlock value={null} {maxHeight} />
{/if}
</div>
Loading
Loading