diff --git a/.changeset/eight-clowns-own.md b/.changeset/eight-clowns-own.md new file mode 100644 index 000000000..89b9d3e6e --- /dev/null +++ b/.changeset/eight-clowns-own.md @@ -0,0 +1,6 @@ +--- +"@workflow/web-shared": patch +"@workflow/core": patch +--- + +Fix hydration of eventData for sleep calls diff --git a/packages/core/src/observability.ts b/packages/core/src/observability.ts index 9422e6f1a..1a7e7b3fb 100644 --- a/packages/core/src/observability.ts +++ b/packages/core/src/observability.ts @@ -41,6 +41,7 @@ const streamPrintRevivers: Record any> = { ReadableStream: streamToStreamId, WritableStream: streamToStreamId, TransformStream: streamToStreamId, + StepFunction: (value) => ``, }; const hydrateStepIO = < @@ -101,22 +102,24 @@ const hydrateEventData = < if (!event.eventData) { return event; } + const eventData = { ...event.eventData }; + // Events can have various eventData with non-devalued keys. + // So far, only eventData.result is devalued (though this may change), + // so we need to hydrate it specifically. + try { + if ('result' in eventData && typeof eventData.result === 'object') { + eventData.result = hydrateStepReturnValue( + eventData.result, + globalThis, + streamPrintRevivers + ); + } + } catch (error) { + console.error('Error hydrating event data', error); + } return { ...event, - // Events have various top-level non-devalued keys, so we need to - // hydrate each value individually. - eventData: Object.fromEntries( - Object.entries(event.eventData).map(([key, value]) => [ - key, - hydrateStepArguments( - value as any, - [], - event.runId as string, - globalThis, - streamPrintRevivers - ), - ]) - ), + eventData, }; }; diff --git a/packages/web-shared/src/api/workflow-server-actions.ts b/packages/web-shared/src/api/workflow-server-actions.ts index fc4ca1d18..66230d452 100644 --- a/packages/web-shared/src/api/workflow-server-actions.ts +++ b/packages/web-shared/src/api/workflow-server-actions.ts @@ -134,7 +134,15 @@ function getUserFacingMessage(error: Error): string { return error.message || 'An unexpected error occurred'; } +const toJSONCompatible = (data: T): T => { + if (data && typeof data === 'object') { + return JSON.parse(JSON.stringify(data)) as T; + } + return data; +}; + const hydrate = (data: T): T => { + data = toJSONCompatible(data); try { return hydrateResourceIO(data as any) as T; } catch (error) { @@ -148,16 +156,7 @@ const hydrate = (data: T): T => { * @returns ServerActionResult with success=true and the data */ function createResponse(data: T): ServerActionResult { - if (data && typeof data === 'object') { - // We can't pass non-serializable objects from client to server. To ensure - // we don't accidentally do this, we convert the object to JSON. - try { - data = JSON.parse(JSON.stringify(data)) as T; - } catch { - // If we can't serialize the data, we'll leave it to the API layer to - // throw an error if the data isn't transportable. - } - } + data = toJSONCompatible(data); return { success: true, data, diff --git a/workbench/nitro-v2/server/api/trigger.post.ts b/workbench/nitro-v2/server/api/trigger.post.ts index cb0700811..b5d85fe2a 100644 --- a/workbench/nitro-v2/server/api/trigger.post.ts +++ b/workbench/nitro-v2/server/api/trigger.post.ts @@ -1,7 +1,7 @@ import { defineEventHandler, getRequestURL, readRawBody } from 'h3'; import { start } from 'workflow/api'; import { hydrateWorkflowArguments } from 'workflow/internal/serialization'; -import { allWorkflows } from '../_workflows.js'; +import { allWorkflows } from '../../_workflows.js'; export default defineEventHandler(async (event) => { const url = getRequestURL(event);