Skip to content

DT-3751 - download external payloads#3345

Merged
rossedfort merged 54 commits into
mainfrom
DT-3751-external-payload-codeblocks
May 15, 2026
Merged

DT-3751 - download external payloads#3345
rossedfort merged 54 commits into
mainfrom
DT-3751-external-payload-codeblocks

Conversation

@rossedfort

@rossedfort rossedfort commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Description & motivation 💭

Adds download functionality for externally stored payloads (note: only for payloads that display in a Code Block). Other payload touch points will be addressed in a following PR.

WIP
This PR is a work in progress. Currently the download button shows, but the download functionality doesn't work.

Screenshots (if applicable) 📸

Generic error (when codec server not configured or download fails)

Screenshot 2026-05-05 at 11 22 48 AM

Error when codec server is configured, but no /download endpoint

Screenshot 2026-05-05 at 11 23 21 AM

Disable download button and show tooltip when codec server not configured

Screenshot 2026-05-05 at 11 23 43 AM

Sample download w/ semantic filename

Screenshot 2026-05-12 at 10 54 00 AM

Event History Row w/ External Payload

Screenshot 2026-05-12 at 12 30 52 PM

Design Considerations 🎨

Testing 🧪

How was this tested 👻

  • Manual testing
  • E2E tests added
  • Unit tests added

Steps for others to test: 🚶🏽‍♂️🚶🏽‍♀️

  1. Check out this branch and run pnpm i and pnpm build:server and pnpm dev
  2. Clone Justin's fork of sdk-go which contains an external payload workflow and codec server
  3. Follow the instructions in the notion doc titled: External Storage Bug Bash 04/17
  4. Set the AWS_PROFILE and S3_BUCKET environment variables
  5. In your local version of Justin's fork of temporalio/sdk-go, run go run demos/worker/main.go, and in a separate shell session, run go run demos/codecserver/main.go
  6. Run temporal workflow start --task-queue "echo-task-queue" --type "GenerateDocument" --workflow-id "generate-doc" --input '1000000' to run a workflow with an externally stored payload.

Checklists

Draft Checklist

Merge Checklist

Issue(s) closed

Docs

Any docs updates needed?

…load component

Consolidates two divergent payload display components into a single
`<Payload />` component at `src/lib/components/payload.svelte`.

Previously, `payload-decoder.svelte` (Svelte 5 runes, required a
`children` snippet, always decoded full attribute trees) and
`metadata-decoder.svelte` (Svelte 4 options API with slot syntax,
decoded single payloads to truncated summary strings) served different
display purposes but used inconsistent APIs and decoding paths across
65+ call sites.

The new component unifies both under a single `mode` prop:
- `code-block` (default): renders a CodeBlock with the decoded value,
  replacing all PayloadDecoder + CodeBlock snippet patterns
- `summary`: truncates to 120 chars and renders a Badge, replacing all
  MetadataDecoder usages
- `inline-truncated`: renders the compact .payload pre block used in
  event detail rows
- `children` snippet: escape hatch for callers that need custom
  rendering (schedule input, timeline SVG text elements)

Both old components used a single decoding path internally
(cloneAllPotentialPayloadsWithCodec -> decodePayloadAttributes ->
stringifyWithBigInt). MetadataDecoder previously used a separate
decodeSingleReadablePayloadWithCodec path; the new component uses the
same unified path for consistency.

The component is written in Svelte 5 runes throughout and imports from
$app/state instead of $app/stores, matching the newer direction of the
codebase.

Migrated files:
- src/lib/components/event/event-card.svelte
- src/lib/components/event/event-details-row.svelte (+ moved .payload CSS)
- src/lib/components/event/event-metadata-expanded.svelte
- src/lib/components/event/event-summary-row.svelte
- src/lib/components/lines-and-dots/svg/group-details-text.svelte
- src/lib/components/lines-and-dots/svg/timeline-graph-row.svelte
- src/lib/components/schedule/schedule-form/schedule-input-payload.svelte
- src/lib/components/standalone-activities/activity-input-and-outcome.svelte
- src/lib/components/workflow/client-actions/update-confirmation-modal.svelte
- src/lib/components/workflow/input-and-results-payload.svelte
- src/lib/components/workflow/metadata/metadata-events.svelte
- src/lib/components/workflow/pending-activity/pending-activity-card.svelte
- src/lib/components/workflow/workflow-json-navigator.svelte
- src/lib/pages/standalone-activity-details.svelte
- src/lib/pages/standalone-activity-search-attributes.svelte
- src/lib/pages/workflow-memo.svelte
- src/lib/pages/workflow-metadata.svelte
- src/lib/pages/workflow-search-attributes.svelte

All 1730 tests pass.
Moves payload.svelte into src/lib/components/payload/ and adds a
USAGE.md report documenting all 31 call sites grouped by feature area.
Updates all 17 import paths accordingly.
… components

Replace the 14-prop multi-mode payload.svelte with four single-purpose
components: PayloadCodeBlock, PayloadSummary, PayloadDecoder, and
PayloadInline. Each component carries only the props relevant to its
render mode, eliminating dead props at call sites.

Also updates the decode paths to use the renamed decode-payload.ts API
(decodeEventAttributes, parsePayloadAttributes) following the #3302
cleanup.
Replace decodeEventAttributes with decodeUserMetadata — the correct
decode path for a single raw Payload (Phase 2 codec only, no attribute
tree walking). Also add onDecode callback prop, called after a
successful decode, consistent with PayloadCodeBlock.
…lue utility

Remove 3-way duplication of getInitialValue/onMount decode logic from
payload-code-block, payload-decoder, and payload-inline by extracting
getInitialPayloadValue and decodePayloadValue into
src/lib/utilities/decode-payload-value.ts.
Components now re-decode when value or fieldName props change after
mount, fixing the reactivity gap that caused stale decoded content
when parent components updated their payload bindings.
@vercel

vercel Bot commented Apr 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
holocene Ready Ready Preview, Comment May 15, 2026 4:07pm

Request Review

downloadLoading = true;
let data: Payloads | undefined = undefined;
try {
data = await downloadExternalPayloadWithCodec(payload);

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.

  • ⚠️ 'data.payloads' is possibly 'null' or 'undefined'.

</div>
<div class="flex w-full flex-col gap-4 xl:w-1/3">
<ScheduleInput
{scheduleId}

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 'IPayloads | null | undefined' is not assignable to type 'IPayloads'.

<ScheduleInput
{scheduleId}
input={schedule?.schedule?.action?.startWorkflow?.input}
/>

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 'IScheduleSpec | null | undefined' is not assignable to type 'IScheduleSpec'.

): Promise<unknown[]> => {
if (!payloads) return null;
returnDataOnly: boolean = true,
): Promise<unknown[]> {

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 'IPayload[] | null | undefined' is not assignable to parameter of type 'unknown[]'.

Comment thread src/lib/components/payload/payload-code-block.svelte
Comment thread src/lib/components/schedule/schedule-form/schedule-input-payload.svelte Outdated
{@render children(decodedValue)}
{:then decodeResult}
{@render children(decodeResult)}
{/await}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I feel like we could really easily put the error state in here and pass it a retry snippet as well so we can get per request errors instead of the "last used" errors

{testId}
language="json"
/>
{/if}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I feel like there's a lot of stuff going on with these files that could be weird or error, maybe we should throw down an error boundary somewhere just in case to prevent app crashes

payload.metadata.messageType ===
'temporal.api.sdk.v1.ExternalStorageReference'
);
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just spitballing on these, I wonder if instead of using typeGuards (which are a typesystem escape hatch) we could use a discriminated union on like a data container so you could easily identify which phase we're in for decoding.

Just thinking out loud don't need to act on it.

@rossedfort
rossedfort merged commit 41fd4f1 into main May 15, 2026
17 checks passed
@rossedfort
rossedfort deleted the DT-3751-external-payload-codeblocks branch May 15, 2026 18:24
tegan-temporal pushed a commit that referenced this pull request May 18, 2026
* refactor: replace PayloadDecoder and MetadataDecoder with unified Payload component

Consolidates two divergent payload display components into a single
`<Payload />` component at `src/lib/components/payload.svelte`.

Previously, `payload-decoder.svelte` (Svelte 5 runes, required a
`children` snippet, always decoded full attribute trees) and
`metadata-decoder.svelte` (Svelte 4 options API with slot syntax,
decoded single payloads to truncated summary strings) served different
display purposes but used inconsistent APIs and decoding paths across
65+ call sites.

The new component unifies both under a single `mode` prop:
- `code-block` (default): renders a CodeBlock with the decoded value,
  replacing all PayloadDecoder + CodeBlock snippet patterns
- `summary`: truncates to 120 chars and renders a Badge, replacing all
  MetadataDecoder usages
- `inline-truncated`: renders the compact .payload pre block used in
  event detail rows
- `children` snippet: escape hatch for callers that need custom
  rendering (schedule input, timeline SVG text elements)

Both old components used a single decoding path internally
(cloneAllPotentialPayloadsWithCodec -> decodePayloadAttributes ->
stringifyWithBigInt). MetadataDecoder previously used a separate
decodeSingleReadablePayloadWithCodec path; the new component uses the
same unified path for consistency.

The component is written in Svelte 5 runes throughout and imports from
$app/state instead of $app/stores, matching the newer direction of the
codebase.

Migrated files:
- src/lib/components/event/event-card.svelte
- src/lib/components/event/event-details-row.svelte (+ moved .payload CSS)
- src/lib/components/event/event-metadata-expanded.svelte
- src/lib/components/event/event-summary-row.svelte
- src/lib/components/lines-and-dots/svg/group-details-text.svelte
- src/lib/components/lines-and-dots/svg/timeline-graph-row.svelte
- src/lib/components/schedule/schedule-form/schedule-input-payload.svelte
- src/lib/components/standalone-activities/activity-input-and-outcome.svelte
- src/lib/components/workflow/client-actions/update-confirmation-modal.svelte
- src/lib/components/workflow/input-and-results-payload.svelte
- src/lib/components/workflow/metadata/metadata-events.svelte
- src/lib/components/workflow/pending-activity/pending-activity-card.svelte
- src/lib/components/workflow/workflow-json-navigator.svelte
- src/lib/pages/standalone-activity-details.svelte
- src/lib/pages/standalone-activity-search-attributes.svelte
- src/lib/pages/workflow-memo.svelte
- src/lib/pages/workflow-metadata.svelte
- src/lib/pages/workflow-search-attributes.svelte

All 1730 tests pass.

* remove unused component, fix css

* refactor: move Payload component into payload/ directory

Moves payload.svelte into src/lib/components/payload/ and adds a
USAGE.md report documenting all 31 call sites grouped by feature area.
Updates all 17 import paths accordingly.

* docs: add Payload component improvement suggestions

* docs: add decode-payload usage report and refactoring recommendations

* refactor(payload): split Payload component into focused mode-specific components

Replace the 14-prop multi-mode payload.svelte with four single-purpose
components: PayloadCodeBlock, PayloadSummary, PayloadDecoder, and
PayloadInline. Each component carries only the props relevant to its
render mode, eliminating dead props at call sites.

Also updates the decode paths to use the renamed decode-payload.ts API
(decodeEventAttributes, parsePayloadAttributes) following the #3302
cleanup.

* fix(payload-summary): use decodeUserMetadata and add onDecode prop

Replace decodeEventAttributes with decodeUserMetadata — the correct
decode path for a single raw Payload (Phase 2 codec only, no attribute
tree walking). Also add onDecode callback prop, called after a
successful decode, consistent with PayloadCodeBlock.

* refactor(payload): extract shared decode logic into decode-payload-value utility

Remove 3-way duplication of getInitialValue/onMount decode logic from
payload-code-block, payload-decoder, and payload-inline by extracting
getInitialPayloadValue and decodePayloadValue into
src/lib/utilities/decode-payload-value.ts.

* fix(payload): replace onMount with \$effect for reactive value updates

Components now re-decode when value or fieldName props change after
mount, fixing the reactivity gap that caused stale decoded content
when parent components updated their payload bindings.

* add workflow with user metadata

* remove unnecessary props from payload code block

* fix summary display

* add improvements

* fix heading levels

* add multi input workflow

* get rid of fieldName prop and fix most of the call sites

* fix remaining type errors

* fix tests

* rm inspect

* rm console.log

* WIP - download external payloads

* add support for downloading externally stored payloads

* add some error handling to downloads

* add support for filename

* fix CR feedback

* remove bad key

* fix export

* remove unused type

* cleanup unused stuff

* fix possibly duplicate key

* add language prop to PayloadCodeBlock

* add copyIconTitle and copySuccessIconTitle to codeblock instance

* refactor(payload): fix reactivity and race condition in payload-summary

Replace $effect+$state async pattern with $derived promise + $effect
cleanup to ensure all reactive deps (value, fallback, prefix,
maxSummaryLength) are tracked synchronously. Use $effect cleanup function
to cancel stale promise callbacks on re-run, preventing race conditions
when value changes rapidly. Remove unused onDecode prop.

* early return if no payloads in decode utils

* add type guards

* support inline external payloads

* fix download error and payload codeblock types

* fix schedule input

* rm console.log

* fix tests and strict mode

* fix spec

* fix typo

* add some optional chaining
laurakwhit added a commit that referenced this pull request May 21, 2026
Auto-generated version bump from 2.49.1 to 2.50.0

Bump type: minor

Changes included:
- [`29832bec`](29832be) Use initiatedEvent for startChildFailed event grouping (#3342)
- [`91a01560`](91a0156) rm slash (#3343)
- [`2e7b88d0`](2e7b88d) feat: Set Current Version action for worker deployment versions (#3319)
- [`b2685f3b`](b2685f3) Add relative path prefix support to routeFor utilities (#3292)
- [`9c888c0c`](9c888c0) fix: validate connection modal status, retry button, and copy nits (#3347)
- [`38989c48`](38989c4) revert: restore original create worker deployment copy (#3348)
- [`90e1fe58`](90e1fe5) Common Errors for Event History (#3306)
- [`f479e4e2`](f479e4e) Show current duration for pending timeline events (#3346)
- [`65a7ff0d`](65a7ff0) Add refresh button to workers list view (#3349)
- [`219cfee4`](219cfee) Fix null conditionals in search attribute filter (#3351)
- [`08bd2f01`](08bd2f0) Add support for adding caller Namespace even if it's not in list of allowed Namespace options for Nexus endpoint (#3167)
- [`d424a78a`](d424a78) refactor(DT-3906): Add knip (#3350)
- [`e828b14f`](e828b14) Remove icon (#3357)
- [`252a755c`](252a755) Add Java to list of support versions for worker heartbeats (#3362)
- [`0f446c7a`](0f446c7) refactor(DT-3906): Simple Svelte 5 migrations (#3359)
- [`63db5b72`](63db5b7) feat(DT-3657): Support shift click for bulk selection in workflow table (#3344)
- [`389d57bf`](389d57b) Enable Svelte 5 runes on files not using legacy features (#3363)
- [`f4b87e1d`](f4b87e1) Update components/workflow to Svelte 5 syntax (#3361)
- [`3e2cce43`](3e2cce4) refactor(DT-3906): More Svelte 5 Migrations (trivial ones) (#3364)
- [`15adcd73`](15adcd7) refactor(DT-3906): UI svelte 5 migrate components/events (#3365)
- [`031fb867`](031fb86) refactor(DT-3906): Migrate stories to Svelte 5 syntax (#3366)
- [`4eac9d2a`](4eac9d2) Scope group hover to tooltip component only (#3379)
- [`eeace5c3`](eeace5c) chore: upgrade TypeScript to v6.0.3 (#3371)
- [`cb80efdd`](cb80efd) refactor(DT-3906): More trivial migrations (#3367)
- [`6cc395bf`](6cc395b) refactor(DT-3906): migrate holocene primitives + layout components to Svelte 5 runes (#3377)
- [`40a029ea`](40a029e) refactor(DT-3906): UI Svelte 5 Migrations medium (#3368)
- [`7dc29ba6`](7dc29ba) refactor(DT-3906): Delete unused Svelte 4 scaffolding (carved from #3370 - Part 1) (#3372)
- [`34a8547e`](34a8547) refactor(DT-3906): Migrate workflow client-action modals to runes (carved from #3370 - Part 4) (#3375)
- [`61090d7e`](61090d7) refactor(DT-3906): Migrate schedule view components to runes (carved from #3370 - Part 2) (#3373)
- [`988e0479`](988e047) refactor(DT-3906): Migrate filter and input components to runes (carved from #3370 - Part 3) (#3374)
- [`d8b78496`](d8b7849) refactor(DT-3906): Migrate workflows-summary table + relationships to runes (carved from #3370 - Part 5) (#3376)
- [`ff86c004`](ff86c00) fix(DT-3968): Make Workflow Table Tooltips render in portal (#3383)
- [`5e02642a`](5e02642) fix(DT-3967): Visibly toggle the view children button even with a parent has 0 (#3382)
- [`1f2b1031`](1f2b103) Remove capability guard from Set Current Version menu item (#3386)
- [`0d54dcb4`](0d54dcb) fix: portal maximizable to body to escape stacking context (#3385)
- [`abf45438`](abf4543) chore(security): patch Dependabot alerts for axios, protobufjs, fast-uri, uuid, postcss, gomarkdown (#3388)
- [`4500f5ea`](4500f5e) Upgrade GitHub actions (#3389)
- [`4a8c0c5b`](4a8c0c5) fix(timeline): stabilize child workflow timeline width with scrollbar-gutter (#3329)
- [`ead14c61`](ead14c6) Move Create Schedule button to header row (#3390)
- [`e8638a2b`](e8638a2) Add top margin (#3392)
- [`ef03dfce`](ef03dfc) refactor: replace PayloadDecoder and MetadataDecoder with unified Payload component (#3299)
- [`89b9eafa`](89b9eaf) add loading state to payload code block (#3397)
- [`ee3ae138`](ee3ae13) Update Schedules search attributes filter (#3396)
- [`41fd4f1f`](41fd4f1) DT-3751 - download external payloads (#3345)
- [`e4fee0b6`](e4fee0b) Fix codec server request URL (#3400)
- [`966b3d0a`](966b3d0) Make input from schedule result actually a string and update tests (#3403)
- [`48a014db`](48a014d) feat(history): show Nexus operation name in compact view  (#3394)
- [`9127c768`](9127c76) Refactor status counts and refresh button (#3402)
- [`747ec109`](747ec10) Fix double loader button (#3407)
- [`6ae961fc`](6ae961f) Payload rendering and error optimizations (#3401)
- [`32c74a48`](32c74a4) add codec server error banner back to workflow layout (#3408)
- [`f73c7ec9`](f73c7ec) Nxs operation/kt (#3406)
- [`672bb04a`](672bb04) Bump Go 1.26.2→1.26.3, x/net v0.54.0, remove curl from runtime image (#3409)
- [`1ee09fe3`](1ee09fe) chore(deps-dev): bump svelte from 5.55.1 to 5.55.7 (#3395)
- [`565bb071`](565bb07) VLN-1352: remediate missing-dependency-cooldown (#3398)
- [`25794ed8`](25794ed) Bump devalue to 5.8.1 (#3410)
- [`b18d0060`](b18d006) Set color-scheme explicitly (#3358)
- [`e7995b81`](e7995b8) chore: ensure the empty app.html respects the user's ligh/dark settings (#3353)
- [`01d64038`](01d6403) fix: enable external payload download button with namespace level codec endpoint (#3420)
- [`4cc44311`](4cc4431) Fix publicPath URL duplication when prefix appears as substring (#3393)

Co-authored-by: laurakwhit <[email protected]>
tegan-temporal pushed a commit that referenced this pull request May 29, 2026
* refactor: replace PayloadDecoder and MetadataDecoder with unified Payload component

Consolidates two divergent payload display components into a single
`<Payload />` component at `src/lib/components/payload.svelte`.

Previously, `payload-decoder.svelte` (Svelte 5 runes, required a
`children` snippet, always decoded full attribute trees) and
`metadata-decoder.svelte` (Svelte 4 options API with slot syntax,
decoded single payloads to truncated summary strings) served different
display purposes but used inconsistent APIs and decoding paths across
65+ call sites.

The new component unifies both under a single `mode` prop:
- `code-block` (default): renders a CodeBlock with the decoded value,
  replacing all PayloadDecoder + CodeBlock snippet patterns
- `summary`: truncates to 120 chars and renders a Badge, replacing all
  MetadataDecoder usages
- `inline-truncated`: renders the compact .payload pre block used in
  event detail rows
- `children` snippet: escape hatch for callers that need custom
  rendering (schedule input, timeline SVG text elements)

Both old components used a single decoding path internally
(cloneAllPotentialPayloadsWithCodec -> decodePayloadAttributes ->
stringifyWithBigInt). MetadataDecoder previously used a separate
decodeSingleReadablePayloadWithCodec path; the new component uses the
same unified path for consistency.

The component is written in Svelte 5 runes throughout and imports from
$app/state instead of $app/stores, matching the newer direction of the
codebase.

Migrated files:
- src/lib/components/event/event-card.svelte
- src/lib/components/event/event-details-row.svelte (+ moved .payload CSS)
- src/lib/components/event/event-metadata-expanded.svelte
- src/lib/components/event/event-summary-row.svelte
- src/lib/components/lines-and-dots/svg/group-details-text.svelte
- src/lib/components/lines-and-dots/svg/timeline-graph-row.svelte
- src/lib/components/schedule/schedule-form/schedule-input-payload.svelte
- src/lib/components/standalone-activities/activity-input-and-outcome.svelte
- src/lib/components/workflow/client-actions/update-confirmation-modal.svelte
- src/lib/components/workflow/input-and-results-payload.svelte
- src/lib/components/workflow/metadata/metadata-events.svelte
- src/lib/components/workflow/pending-activity/pending-activity-card.svelte
- src/lib/components/workflow/workflow-json-navigator.svelte
- src/lib/pages/standalone-activity-details.svelte
- src/lib/pages/standalone-activity-search-attributes.svelte
- src/lib/pages/workflow-memo.svelte
- src/lib/pages/workflow-metadata.svelte
- src/lib/pages/workflow-search-attributes.svelte

All 1730 tests pass.

* remove unused component, fix css

* refactor: move Payload component into payload/ directory

Moves payload.svelte into src/lib/components/payload/ and adds a
USAGE.md report documenting all 31 call sites grouped by feature area.
Updates all 17 import paths accordingly.

* docs: add Payload component improvement suggestions

* docs: add decode-payload usage report and refactoring recommendations

* refactor(payload): split Payload component into focused mode-specific components

Replace the 14-prop multi-mode payload.svelte with four single-purpose
components: PayloadCodeBlock, PayloadSummary, PayloadDecoder, and
PayloadInline. Each component carries only the props relevant to its
render mode, eliminating dead props at call sites.

Also updates the decode paths to use the renamed decode-payload.ts API
(decodeEventAttributes, parsePayloadAttributes) following the #3302
cleanup.

* fix(payload-summary): use decodeUserMetadata and add onDecode prop

Replace decodeEventAttributes with decodeUserMetadata — the correct
decode path for a single raw Payload (Phase 2 codec only, no attribute
tree walking). Also add onDecode callback prop, called after a
successful decode, consistent with PayloadCodeBlock.

* refactor(payload): extract shared decode logic into decode-payload-value utility

Remove 3-way duplication of getInitialValue/onMount decode logic from
payload-code-block, payload-decoder, and payload-inline by extracting
getInitialPayloadValue and decodePayloadValue into
src/lib/utilities/decode-payload-value.ts.

* fix(payload): replace onMount with \$effect for reactive value updates

Components now re-decode when value or fieldName props change after
mount, fixing the reactivity gap that caused stale decoded content
when parent components updated their payload bindings.

* add workflow with user metadata

* remove unnecessary props from payload code block

* fix summary display

* add improvements

* fix heading levels

* add multi input workflow

* get rid of fieldName prop and fix most of the call sites

* fix remaining type errors

* fix tests

* rm inspect

* rm console.log

* WIP - download external payloads

* add support for downloading externally stored payloads

* add some error handling to downloads

* add support for filename

* fix CR feedback

* remove bad key

* fix export

* remove unused type

* cleanup unused stuff

* fix possibly duplicate key

* add language prop to PayloadCodeBlock

* add copyIconTitle and copySuccessIconTitle to codeblock instance

* refactor(payload): fix reactivity and race condition in payload-summary

Replace $effect+$state async pattern with $derived promise + $effect
cleanup to ensure all reactive deps (value, fallback, prefix,
maxSummaryLength) are tracked synchronously. Use $effect cleanup function
to cancel stale promise callbacks on re-run, preventing race conditions
when value changes rapidly. Remove unused onDecode prop.

* early return if no payloads in decode utils

* add type guards

* support inline external payloads

* fix download error and payload codeblock types

* fix schedule input

* rm console.log

* fix tests and strict mode

* fix spec

* fix typo

* add some optional chaining
tegan-temporal pushed a commit that referenced this pull request Jun 3, 2026
* refactor: replace PayloadDecoder and MetadataDecoder with unified Payload component

Consolidates two divergent payload display components into a single
`<Payload />` component at `src/lib/components/payload.svelte`.

Previously, `payload-decoder.svelte` (Svelte 5 runes, required a
`children` snippet, always decoded full attribute trees) and
`metadata-decoder.svelte` (Svelte 4 options API with slot syntax,
decoded single payloads to truncated summary strings) served different
display purposes but used inconsistent APIs and decoding paths across
65+ call sites.

The new component unifies both under a single `mode` prop:
- `code-block` (default): renders a CodeBlock with the decoded value,
  replacing all PayloadDecoder + CodeBlock snippet patterns
- `summary`: truncates to 120 chars and renders a Badge, replacing all
  MetadataDecoder usages
- `inline-truncated`: renders the compact .payload pre block used in
  event detail rows
- `children` snippet: escape hatch for callers that need custom
  rendering (schedule input, timeline SVG text elements)

Both old components used a single decoding path internally
(cloneAllPotentialPayloadsWithCodec -> decodePayloadAttributes ->
stringifyWithBigInt). MetadataDecoder previously used a separate
decodeSingleReadablePayloadWithCodec path; the new component uses the
same unified path for consistency.

The component is written in Svelte 5 runes throughout and imports from
$app/state instead of $app/stores, matching the newer direction of the
codebase.

Migrated files:
- src/lib/components/event/event-card.svelte
- src/lib/components/event/event-details-row.svelte (+ moved .payload CSS)
- src/lib/components/event/event-metadata-expanded.svelte
- src/lib/components/event/event-summary-row.svelte
- src/lib/components/lines-and-dots/svg/group-details-text.svelte
- src/lib/components/lines-and-dots/svg/timeline-graph-row.svelte
- src/lib/components/schedule/schedule-form/schedule-input-payload.svelte
- src/lib/components/standalone-activities/activity-input-and-outcome.svelte
- src/lib/components/workflow/client-actions/update-confirmation-modal.svelte
- src/lib/components/workflow/input-and-results-payload.svelte
- src/lib/components/workflow/metadata/metadata-events.svelte
- src/lib/components/workflow/pending-activity/pending-activity-card.svelte
- src/lib/components/workflow/workflow-json-navigator.svelte
- src/lib/pages/standalone-activity-details.svelte
- src/lib/pages/standalone-activity-search-attributes.svelte
- src/lib/pages/workflow-memo.svelte
- src/lib/pages/workflow-metadata.svelte
- src/lib/pages/workflow-search-attributes.svelte

All 1730 tests pass.

* remove unused component, fix css

* refactor: move Payload component into payload/ directory

Moves payload.svelte into src/lib/components/payload/ and adds a
USAGE.md report documenting all 31 call sites grouped by feature area.
Updates all 17 import paths accordingly.

* docs: add Payload component improvement suggestions

* docs: add decode-payload usage report and refactoring recommendations

* refactor(payload): split Payload component into focused mode-specific components

Replace the 14-prop multi-mode payload.svelte with four single-purpose
components: PayloadCodeBlock, PayloadSummary, PayloadDecoder, and
PayloadInline. Each component carries only the props relevant to its
render mode, eliminating dead props at call sites.

Also updates the decode paths to use the renamed decode-payload.ts API
(decodeEventAttributes, parsePayloadAttributes) following the #3302
cleanup.

* fix(payload-summary): use decodeUserMetadata and add onDecode prop

Replace decodeEventAttributes with decodeUserMetadata — the correct
decode path for a single raw Payload (Phase 2 codec only, no attribute
tree walking). Also add onDecode callback prop, called after a
successful decode, consistent with PayloadCodeBlock.

* refactor(payload): extract shared decode logic into decode-payload-value utility

Remove 3-way duplication of getInitialValue/onMount decode logic from
payload-code-block, payload-decoder, and payload-inline by extracting
getInitialPayloadValue and decodePayloadValue into
src/lib/utilities/decode-payload-value.ts.

* fix(payload): replace onMount with \$effect for reactive value updates

Components now re-decode when value or fieldName props change after
mount, fixing the reactivity gap that caused stale decoded content
when parent components updated their payload bindings.

* add workflow with user metadata

* remove unnecessary props from payload code block

* fix summary display

* add improvements

* fix heading levels

* add multi input workflow

* get rid of fieldName prop and fix most of the call sites

* fix remaining type errors

* fix tests

* rm inspect

* rm console.log

* WIP - download external payloads

* add support for downloading externally stored payloads

* add some error handling to downloads

* add support for filename

* fix CR feedback

* remove bad key

* fix export

* remove unused type

* cleanup unused stuff

* fix possibly duplicate key

* add language prop to PayloadCodeBlock

* add copyIconTitle and copySuccessIconTitle to codeblock instance

* refactor(payload): fix reactivity and race condition in payload-summary

Replace $effect+$state async pattern with $derived promise + $effect
cleanup to ensure all reactive deps (value, fallback, prefix,
maxSummaryLength) are tracked synchronously. Use $effect cleanup function
to cancel stale promise callbacks on re-run, preventing race conditions
when value changes rapidly. Remove unused onDecode prop.

* early return if no payloads in decode utils

* add type guards

* support inline external payloads

* fix download error and payload codeblock types

* fix schedule input

* rm console.log

* fix tests and strict mode

* fix spec

* fix typo

* add some optional chaining
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants