From ec88dcd52d2ccd90ab35f34fbc1457641db47dd2 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Thu, 24 Jul 2025 14:53:58 +0300 Subject: [PATCH 01/10] feat: add feature set for checking if endpoint is workflow --- src/context/steps.ts | 4 +++- src/qstash/headers.ts | 4 ++-- src/serve/options.ts | 9 ++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/context/steps.ts b/src/context/steps.ts index 2a939c4..e178122 100644 --- a/src/context/steps.ts +++ b/src/context/steps.ts @@ -429,6 +429,8 @@ export class LazyCallStep extends BaseLazySt const { headers, contentType } = super.getHeaders({ context, telemetry, invokeCount, step }); headers["Upstash-Retries"] = this.retries.toString(); + + // WF_DetectTrigger is not included because these requests are going to external endpoints headers[WORKFLOW_FEATURE_HEADER] = "WF_NoDelete,InitialBody"; if (this.flowControl) { @@ -456,7 +458,7 @@ export class LazyCallStep extends BaseLazySt "Upstash-Callback-Workflow-CallType": "fromCallback", "Upstash-Callback-Workflow-Init": "false", "Upstash-Callback-Workflow-Url": context.url, - "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody", + "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger", "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true", "Upstash-Callback-Forward-Upstash-Workflow-StepId": step.stepId.toString(), diff --git a/src/qstash/headers.ts b/src/qstash/headers.ts index 3ab0c6b..f06a0d6 100644 --- a/src/qstash/headers.ts +++ b/src/qstash/headers.ts @@ -107,7 +107,7 @@ class WorkflowHeaders { [WORKFLOW_INIT_HEADER]: this.initHeaderValue, [WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId, [WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl, - [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody", + [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger", [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION, ...(this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}), ...(this.workflowConfig.telemetry && @@ -197,7 +197,7 @@ class WorkflowHeaders { this.headers.failureHeaders["Workflow-Init"] = "false"; this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl; this.headers.failureHeaders["Workflow-Calltype"] = "failureCall"; - this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody"; + this.headers.failureHeaders["Feature-Set"] = "LazyFetch,InitialBody,WF_DetectTrigger"; if ( this.workflowConfig.retries !== undefined && this.workflowConfig.retries !== DEFAULT_RETRIES diff --git a/src/serve/options.ts b/src/serve/options.ts index af825aa..0ae0ee3 100644 --- a/src/serve/options.ts +++ b/src/serve/options.ts @@ -1,6 +1,6 @@ import { Receiver } from "@upstash/qstash"; import { Client } from "@upstash/qstash"; -import { DEFAULT_RETRIES } from "../constants"; +import { DEFAULT_RETRIES, WORKFLOW_PROTOCOL_VERSION, WORKFLOW_PROTOCOL_VERSION_HEADER } from "../constants"; import type { FinishCondition, RequiredExceptFields, WorkflowServeOptions } from "../types"; import { WorkflowLogger } from "../logger"; import { formatWorkflowError, WorkflowError, WorkflowNonRetryableError } from "../error"; @@ -53,18 +53,25 @@ export const processOptions = { From ac7a0a5b2e2efdb588f661d3b061c341fee7b9ae Mon Sep 17 00:00:00 2001 From: CahidArda Date: Thu, 24 Jul 2025 14:54:23 +0300 Subject: [PATCH 02/10] fix: fmt --- src/context/steps.ts | 5 ++++- src/serve/options.ts | 12 ++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/context/steps.ts b/src/context/steps.ts index e178122..ae9facc 100644 --- a/src/context/steps.ts +++ b/src/context/steps.ts @@ -512,7 +512,10 @@ export class LazyWaitForEventStep extends BaseLazyStep>> { + public async getResultStep( + concurrent: number, + stepId: number + ): Promise>> { return await Promise.resolve({ stepId, stepName: this.stepName, diff --git a/src/serve/options.ts b/src/serve/options.ts index 0ae0ee3..d87ca48 100644 --- a/src/serve/options.ts +++ b/src/serve/options.ts @@ -1,6 +1,10 @@ import { Receiver } from "@upstash/qstash"; import { Client } from "@upstash/qstash"; -import { DEFAULT_RETRIES, WORKFLOW_PROTOCOL_VERSION, WORKFLOW_PROTOCOL_VERSION_HEADER } from "../constants"; +import { + DEFAULT_RETRIES, + WORKFLOW_PROTOCOL_VERSION, + WORKFLOW_PROTOCOL_VERSION_HEADER, +} from "../constants"; import type { FinishCondition, RequiredExceptFields, WorkflowServeOptions } from "../types"; import { WorkflowLogger } from "../logger"; import { formatWorkflowError, WorkflowError, WorkflowNonRetryableError } from "../error"; @@ -55,14 +59,14 @@ export const processOptions = { From 582abffeed8aa6f14d02cc6a0bf32e4952b875b7 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Tue, 29 Jul 2025 13:01:20 +0300 Subject: [PATCH 03/10] fix: add body field in errors --- src/client/index.test.ts | 1 + src/client/types.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/client/index.test.ts b/src/client/index.test.ts index 0b30762..4b280a9 100644 --- a/src/client/index.test.ts +++ b/src/client/index.test.ts @@ -663,6 +663,7 @@ describe("workflow client", () => { errors: [ { error: "400 Bad Request", + body: expect.any(String), headers: expect.any(Object), status: 400, time: expect.any(Number), diff --git a/src/client/types.ts b/src/client/types.ts index 95f7c89..476ce3a 100644 --- a/src/client/types.ts +++ b/src/client/types.ts @@ -177,7 +177,20 @@ type StepLogGroup = * errors which occured in the step */ errors?: { + /** + * error message associated with the request + * + * example: + * ``` + * detected a non-workflow destination for trigger/invoke. + * make sure you are sending the request to the correct endpoint + * ``` + */ error: string; + /** + * response body returned in the request which resulted in an error + */ + body: string; headers: Record; status: number; time: number; From e5e1e6ce546b7503a571e23d47b1b08c97b1bf02 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Tue, 29 Jul 2025 14:17:38 +0300 Subject: [PATCH 04/10] fix: tests --- src/agents/adapters.test.ts | 8 ++++---- src/agents/agent.test.ts | 6 +++--- src/agents/task.test.ts | 6 +++--- src/client/index.test.ts | 12 +++++------ src/context/auto-executor.test.ts | 14 ++++++------- src/context/context.test.ts | 20 +++++++++---------- src/receiver.test.ts | 2 +- src/serve/authorization.test.ts | 6 +++--- src/serve/serve-many.test.ts | 6 +++--- src/serve/serve.test.ts | 33 ++++++++++++++++--------------- src/workflow-requests.test.ts | 18 ++++++++--------- 11 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/agents/adapters.test.ts b/src/agents/adapters.test.ts index 2e877b5..72d0992 100644 --- a/src/agents/adapters.test.ts +++ b/src/agents/adapters.test.ts @@ -81,7 +81,7 @@ describe("wrapTools", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", "upstash-workflow-init": "false", @@ -129,7 +129,7 @@ describe("wrapTools", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", "upstash-workflow-init": "false", @@ -198,7 +198,7 @@ describe("wrapTools", () => { headers: { "content-type": "application/json", "upstash-delay": "1000s", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", "upstash-workflow-init": "false", @@ -246,7 +246,7 @@ describe("wrapTools", () => { destination: WORKFLOW_ENDPOINT, headers: { "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", "upstash-workflow-init": "false", diff --git a/src/agents/agent.test.ts b/src/agents/agent.test.ts index 14b40c9..d147aff 100644 --- a/src/agents/agent.test.ts +++ b/src/agents/agent.test.ts @@ -98,7 +98,7 @@ describe("agents", () => { "upstash-workflow-sdk-version": "1", "content-type": "application/json", "upstash-callback": "https://requestcatcher.com/api", - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": "application/json", @@ -165,7 +165,7 @@ describe("agents", () => { "upstash-workflow-sdk-version": "1", "content-type": "application/json", "upstash-callback": "https://requestcatcher.com/api", - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": "application/json", @@ -231,7 +231,7 @@ describe("agents", () => { "upstash-workflow-sdk-version": "1", "content-type": "application/json", "upstash-callback": "https://requestcatcher.com/api", - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": "application/json", diff --git a/src/agents/task.test.ts b/src/agents/task.test.ts index 4cea1ba..b26edc0 100644 --- a/src/agents/task.test.ts +++ b/src/agents/task.test.ts @@ -115,7 +115,7 @@ describe("tasks", () => { "upstash-workflow-sdk-version": "1", "content-type": "application/json", "upstash-callback": "https://requestcatcher.com/api", - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": "application/json", @@ -198,7 +198,7 @@ describe("tasks", () => { "upstash-workflow-sdk-version": "1", "content-type": "application/json", "upstash-callback": "https://requestcatcher.com/api", - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": "application/json", @@ -273,7 +273,7 @@ describe("tasks", () => { "upstash-workflow-sdk-version": "1", "content-type": "application/json", "upstash-callback": "https://requestcatcher.com/api", - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": "application/json", diff --git a/src/client/index.test.ts b/src/client/index.test.ts index 8e0de3b..ea73006 100644 --- a/src/client/index.test.ts +++ b/src/client/index.test.ts @@ -234,7 +234,7 @@ describe("workflow client", () => { "upstash-workflow-url": "https://requestcatcher.com/api", "upstash-delay": "1s", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-telemetry-framework": "unknown", "upstash-telemetry-runtime": expect.stringMatching(/bun@/), "upstash-telemetry-sdk": expect.stringContaining("@upstash/workflow"), @@ -299,7 +299,7 @@ describe("workflow client", () => { "upstash-workflow-url": "https://requestcatcher.com/api", "upstash-delay": "1s", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-telemetry-framework": "unknown", "upstash-telemetry-runtime": expect.stringMatching(/bun@/), "upstash-telemetry-sdk": expect.stringContaining("@upstash/workflow"), @@ -319,13 +319,13 @@ describe("workflow client", () => { "upstash-workflow-url": "https://requestcatcher.com/api", "upstash-delay": "1s", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-telemetry-framework": "unknown", "upstash-telemetry-runtime": expect.stringMatching(/bun@/), "upstash-telemetry-sdk": expect.stringContaining("@upstash/workflow"), "upstash-workflow-sdk-version": "1", "upstash-failure-callback": "https://requestcatcher.com/api", - "upstash-failure-callback-feature-set": "LazyFetch,InitialBody", + "upstash-failure-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-failure-callback-forward-upstash-workflow-failure-callback": "true", "upstash-failure-callback-forward-upstash-workflow-is-failure": "true", "upstash-failure-callback-forward-user-header": "user-header-value", @@ -379,8 +379,8 @@ describe("workflow client", () => { "upstash-delay": "1s", "upstash-failure-callback": "https://requestcatcher.com/some-failure-callback", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", - "upstash-failure-callback-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", + "upstash-failure-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-failure-callback-forward-upstash-workflow-failure-callback": "true", "upstash-failure-callback-forward-upstash-workflow-is-failure": "true", "upstash-failure-callback-forward-user-header": "user-header-value", diff --git a/src/context/auto-executor.test.ts b/src/context/auto-executor.test.ts index c5eede6..207d625 100644 --- a/src/context/auto-executor.test.ts +++ b/src/context/auto-executor.test.ts @@ -128,7 +128,7 @@ describe("auto-executor", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", "upstash-workflow-runid": workflowRunId, @@ -224,7 +224,7 @@ describe("auto-executor", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-delay": "123s", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", @@ -243,7 +243,7 @@ describe("auto-executor", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-delay": "10m", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", @@ -262,7 +262,7 @@ describe("auto-executor", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", "upstash-not-before": "123123", @@ -281,7 +281,7 @@ describe("auto-executor", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", "upstash-workflow-runid": workflowRunId, @@ -342,7 +342,7 @@ describe("auto-executor", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", "upstash-workflow-runid": workflowRunId, @@ -399,7 +399,7 @@ describe("auto-executor", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", "upstash-workflow-runid": workflowRunId, diff --git a/src/context/context.test.ts b/src/context/context.test.ts index 539f4d0..398ec50 100644 --- a/src/context/context.test.ts +++ b/src/context/context.test.ts @@ -146,7 +146,7 @@ describe("context tests", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-forward-upstash-workflow-invoke-count": "5", "upstash-method": "POST", @@ -197,7 +197,7 @@ describe("context tests", () => { timeout: "7d", // default timeout timeoutHeaders: { "content-type": ["application/json"], - "Upstash-Feature-Set": ["LazyFetch,InitialBody"], + "Upstash-Feature-Set": ["LazyFetch,InitialBody,WF_DetectTrigger"], [`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`]: ["1"], [WORKFLOW_PROTOCOL_VERSION_HEADER]: [WORKFLOW_PROTOCOL_VERSION], "Upstash-Workflow-CallType": ["step"], @@ -250,7 +250,7 @@ describe("context tests", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-forward-upstash-workflow-invoke-count": "1", "upstash-method": "POST", @@ -265,7 +265,7 @@ describe("context tests", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-forward-upstash-workflow-invoke-count": "1", "upstash-method": "POST", @@ -327,7 +327,7 @@ describe("context tests", () => { "content-type": "application/x-www-form-urlencoded", "upstash-forward-content-type": "application/x-www-form-urlencoded", "upstash-callback": WORKFLOW_ENDPOINT, - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": @@ -393,7 +393,7 @@ describe("context tests", () => { "upstash-workflow-sdk-version": "1", "content-type": "application/json", "upstash-callback": WORKFLOW_ENDPOINT, - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": "application/json", @@ -557,7 +557,7 @@ describe("context tests", () => { "upstash-timeout": timeout, "content-type": "application/json", "upstash-callback": WORKFLOW_ENDPOINT, - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": "application/json", @@ -640,7 +640,7 @@ describe("context tests", () => { "upstash-timeout": timeout, "content-type": "application/json", "upstash-callback": WORKFLOW_ENDPOINT, - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": "application/json", @@ -721,7 +721,7 @@ describe("context tests", () => { "upstash-timeout": timeout, "content-type": "application/json", "upstash-callback": WORKFLOW_ENDPOINT, - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": "application/json", @@ -811,7 +811,7 @@ describe("context tests", () => { [`upstash-forward-${header}`]: headerValue, "content-type": "application/json", "upstash-callback": WORKFLOW_ENDPOINT, - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-forward-upstash-workflow-callback": "true", "upstash-callback-forward-upstash-workflow-concurrent": "1", "upstash-callback-forward-upstash-workflow-contenttype": "application/json", diff --git a/src/receiver.test.ts b/src/receiver.test.ts index 39161bd..925e1e2 100644 --- a/src/receiver.test.ts +++ b/src/receiver.test.ts @@ -206,7 +206,7 @@ describe("receiver", () => { destination: WORKFLOW_ENDPOINT, headers: { "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-authorization": expect.stringMatching(/Bearer /), "upstash-forward-upstash-signature": expect.any(String), "upstash-forward-upstash-workflow-sdk-version": "1", diff --git a/src/serve/authorization.test.ts b/src/serve/authorization.test.ts index e20eded..8443945 100644 --- a/src/serve/authorization.test.ts +++ b/src/serve/authorization.test.ts @@ -227,7 +227,7 @@ describe("disabled workflow context", () => { destination: WORKFLOW_ENDPOINT, headers: { "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-workflow-sdk-version": "1", "upstash-method": "POST", @@ -283,7 +283,7 @@ describe("disabled workflow context", () => { destination: WORKFLOW_ENDPOINT, headers: { "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-workflow-sdk-version": "1", "upstash-method": "POST", @@ -340,7 +340,7 @@ describe("disabled workflow context", () => { destination: WORKFLOW_ENDPOINT, headers: { "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-forward-upstash-workflow-invoke-count": "4", "upstash-workflow-sdk-version": "1", diff --git a/src/serve/serve-many.test.ts b/src/serve/serve-many.test.ts index b6e6895..317933f 100644 --- a/src/serve/serve-many.test.ts +++ b/src/serve/serve-many.test.ts @@ -158,7 +158,7 @@ describe("serveMany", () => { body: { body: "2", headers: { - "Upstash-Feature-Set": ["LazyFetch,InitialBody"], + "Upstash-Feature-Set": ["LazyFetch,InitialBody,WF_DetectTrigger"], "Upstash-Flow-Control-Key": ["workflowTwoFlowControl"], "Upstash-Flow-Control-Value": ["parallelism=4, rate=6"], "Upstash-Forward-Upstash-Workflow-Sdk-Version": ["1"], @@ -212,7 +212,7 @@ describe("serveMany", () => { body: { body: "2", headers: { - "Upstash-Feature-Set": ["LazyFetch,InitialBody"], + "Upstash-Feature-Set": ["LazyFetch,InitialBody,WF_DetectTrigger"], "Upstash-Forward-Upstash-Workflow-Invoke-Count": ["1"], "Upstash-Flow-Control-Key": ["workflowTwoFlowControl"], "Upstash-Flow-Control-Value": ["parallelism=4, rate=6"], @@ -271,7 +271,7 @@ describe("serveMany", () => { headers: { "content-type": "application/json", "upstash-callback": "https://requestcatcher.com/api/workflow-three", - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-flow-control-key": "workflowThreeFlowControl", "upstash-callback-flow-control-value": "parallelism=4, rate=6", "upstash-flow-control-key": "workflowOneFlowControl", diff --git a/src/serve/serve.test.ts b/src/serve/serve.test.ts index 820dbf5..18b377c 100644 --- a/src/serve/serve.test.ts +++ b/src/serve/serve.test.ts @@ -78,7 +78,7 @@ describe("serve", () => { destination: WORKFLOW_ENDPOINT, headers: { "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-flow-control-key": "my-key", "upstash-flow-control-value": "parallelism=1", "upstash-forward-upstash-workflow-sdk-version": "1", @@ -177,7 +177,7 @@ describe("serve", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-forward-upstash-workflow-invoke-count": "2", "upstash-method": "POST", @@ -209,7 +209,7 @@ describe("serve", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-forward-upstash-workflow-invoke-count": "2", "upstash-method": "POST", @@ -239,7 +239,7 @@ describe("serve", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-forward-upstash-workflow-invoke-count": "2", "upstash-method": "POST", @@ -424,7 +424,7 @@ describe("serve", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", "upstash-workflow-init": "false", @@ -472,7 +472,7 @@ describe("serve", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-delay": "1s", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", @@ -517,8 +517,8 @@ describe("serve", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", - "upstash-failure-callback-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", + "upstash-failure-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-delay": "1s", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", @@ -572,8 +572,8 @@ describe("serve", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", - "upstash-failure-callback-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", + "upstash-failure-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-delay": "1s", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", @@ -643,7 +643,8 @@ describe("serve", () => { headers: { "content-type": "application/json", "upstash-callback": "https://requestcatcher.com/api", - "upstash-callback-failure-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-failure-callback-feature-set": + "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-callback-failure-callback": "some-failure-url", "upstash-callback-failure-callback-forward-upstash-workflow-failure-callback": "true", @@ -654,7 +655,7 @@ describe("serve", () => { "upstash-callback-failure-callback-workflow-runid": "wfr-foo", "upstash-callback-failure-callback-workflow-url": "https://requestcatcher.com/api", "upstash-callback-forward-upstash-workflow-callback": "true", - "upstash-callback-feature-set": "LazyFetch,InitialBody", + "upstash-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", // invoke counts: "upstash-callback-failure-callback-forward-upstash-workflow-invoke-count": "2", "upstash-failure-callback-forward-upstash-workflow-invoke-count": "2", @@ -670,7 +671,7 @@ describe("serve", () => { "upstash-callback-workflow-init": "false", "upstash-callback-workflow-runid": "wfr-foo", "upstash-callback-workflow-url": "https://requestcatcher.com/api", - "upstash-failure-callback-feature-set": "LazyFetch,InitialBody", + "upstash-failure-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-failure-callback-forward-upstash-workflow-failure-callback": "true", "upstash-failure-callback-forward-upstash-workflow-is-failure": "true", "upstash-failure-callback-retries": "2", @@ -920,7 +921,7 @@ describe("serve", () => { timeoutHeaders: { "Upstash-Workflow-Sdk-Version": ["1"], "content-type": ["application/json"], - "Upstash-Feature-Set": ["LazyFetch,InitialBody"], + "Upstash-Feature-Set": ["LazyFetch,InitialBody,WF_DetectTrigger"], "Upstash-Forward-Upstash-Workflow-Sdk-Version": ["1"], "Upstash-Retries": ["2"], "Upstash-Flow-Control-Key": ["fc-key"], @@ -1248,8 +1249,8 @@ describe("serve", () => { headers: { "upstash-workflow-sdk-version": "1", "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", - "upstash-failure-callback-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", + "upstash-failure-callback-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-delay": "1s", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", diff --git a/src/workflow-requests.test.ts b/src/workflow-requests.test.ts index 2a695b0..2e6372c 100644 --- a/src/workflow-requests.test.ts +++ b/src/workflow-requests.test.ts @@ -68,7 +68,7 @@ describe("Workflow Requests", () => { destination: WORKFLOW_ENDPOINT, headers: { "content-type": "application/json", - "upstash-feature-set": "LazyFetch,InitialBody", + "upstash-feature-set": "LazyFetch,InitialBody,WF_DetectTrigger", "upstash-forward-upstash-workflow-sdk-version": "1", "upstash-method": "POST", "upstash-retries": "0", @@ -490,7 +490,7 @@ describe("Workflow Requests", () => { [WORKFLOW_INIT_HEADER]: "true", [WORKFLOW_ID_HEADER]: workflowRunId, [WORKFLOW_URL_HEADER]: WORKFLOW_ENDPOINT, - [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody", + [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger", [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION, [`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`]: WORKFLOW_PROTOCOL_VERSION, "content-type": "application/json", @@ -525,7 +525,7 @@ describe("Workflow Requests", () => { [WORKFLOW_INIT_HEADER]: "false", [WORKFLOW_ID_HEADER]: workflowRunId, [WORKFLOW_URL_HEADER]: WORKFLOW_ENDPOINT, - [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody", + [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger", [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION, [`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`]: WORKFLOW_PROTOCOL_VERSION, "content-type": "application/json", @@ -585,7 +585,7 @@ describe("Workflow Requests", () => { [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION, "Upstash-Callback-Forward-Upstash-Workflow-Invoke-Count": "3", "Upstash-Forward-Upstash-Workflow-Invoke-Count": "3", - "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody", + "Upstash-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger", "Upstash-Retries": "0", "Upstash-Callback": WORKFLOW_ENDPOINT, "Upstash-Callback-Forward-Upstash-Workflow-Callback": "true", @@ -629,8 +629,8 @@ describe("Workflow Requests", () => { [WORKFLOW_INIT_HEADER]: "true", [WORKFLOW_ID_HEADER]: workflowRunId, [WORKFLOW_URL_HEADER]: WORKFLOW_ENDPOINT, - [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody", - "Upstash-Failure-Callback-Feature-Set": "LazyFetch,InitialBody", + [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger", + "Upstash-Failure-Callback-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger", [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION, [`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`]: WORKFLOW_PROTOCOL_VERSION, [`Upstash-Failure-Callback-Forward-${WORKFLOW_FAILURE_HEADER}`]: "true", @@ -682,7 +682,7 @@ describe("Workflow Requests", () => { "Upstash-Workflow-RunId": workflowRunId, "Upstash-Workflow-Url": WORKFLOW_ENDPOINT, [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION, - [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody", + [WORKFLOW_FEATURE_HEADER]: "LazyFetch,InitialBody,WF_DetectTrigger", "Upstash-Forward-Upstash-Workflow-Sdk-Version": "1", "Upstash-Workflow-CallType": "step", "content-type": "application/json", @@ -698,7 +698,7 @@ describe("Workflow Requests", () => { "Upstash-Workflow-Init": ["false"], "Upstash-Workflow-RunId": [workflowRunId], "Upstash-Workflow-Url": [WORKFLOW_ENDPOINT], - [WORKFLOW_FEATURE_HEADER]: ["LazyFetch,InitialBody"], + [WORKFLOW_FEATURE_HEADER]: ["LazyFetch,InitialBody,WF_DetectTrigger"], [WORKFLOW_PROTOCOL_VERSION_HEADER]: [WORKFLOW_PROTOCOL_VERSION], "Upstash-Forward-Upstash-Workflow-Sdk-Version": ["1"], "content-type": ["application/json"], @@ -981,7 +981,7 @@ describe("Workflow Requests", () => { "Upstash-Workflow-Init": "true", "Upstash-Workflow-RunId": workflowRunId, "Upstash-Workflow-Url": WORKFLOW_ENDPOINT, - "Upstash-Feature-Set": "LazyFetch,InitialBody", + "Upstash-Feature-Set": "LazyFetch,InitialBody,WF_DetectTrigger", [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION, "Upstash-Forward-Upstash-Workflow-Sdk-Version": "1", "Upstash-Retries": "0", From f2af66f40fe55df0510d81968a16fc9846c27b27 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Thu, 31 Jul 2025 15:32:08 +0300 Subject: [PATCH 05/10] fix: return headers in all serve handlers --- platforms/express.ts | 5 +++++ platforms/nextjs.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/platforms/express.ts b/platforms/express.ts index 2c41e24..a67e9c0 100644 --- a/platforms/express.ts +++ b/platforms/express.ts @@ -66,6 +66,11 @@ function createExpressHandler( const response = await serveHandler(webRequest); res.status(response.status).json(await response.json()); + + // set headers + for (const [key, value] of response.headers.entries()) { + res.setHeader(key, value); + } }; } diff --git a/platforms/nextjs.ts b/platforms/nextjs.ts index cfe629d..a74f41e 100644 --- a/platforms/nextjs.ts +++ b/platforms/nextjs.ts @@ -106,6 +106,11 @@ export const servePagesRouter = ( }); const response = await serveHandler(request); res.status(response.status).json(await response.json()); + + // set headers + for (const [key, value] of response.headers.entries()) { + res.setHeader(key, value); + } }; return { From 961f4376fa7bc3256f949cd264c443bcd1bdbe4e Mon Sep 17 00:00:00 2001 From: CahidArda Date: Fri, 1 Aug 2025 13:25:47 +0300 Subject: [PATCH 06/10] fix: add protocol header to error responses too --- src/serve/index.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/serve/index.ts b/src/serve/index.ts index 2483ed3..225b810 100644 --- a/src/serve/index.ts +++ b/src/serve/index.ts @@ -1,5 +1,10 @@ import { makeCancelRequest } from "../client/utils"; -import { SDK_TELEMETRY, WORKFLOW_INVOKE_COUNT_HEADER } from "../constants"; +import { + SDK_TELEMETRY, + WORKFLOW_INVOKE_COUNT_HEADER, + WORKFLOW_PROTOCOL_VERSION, + WORKFLOW_PROTOCOL_VERSION_HEADER, +} from "../constants"; import { WorkflowContext } from "../context"; import { formatWorkflowError, WorkflowNonRetryableError } from "../error"; import { WorkflowLogger } from "../logger"; @@ -268,10 +273,16 @@ export const serveBase = < console.error(errorMessage); return new Response(errorMessage, { status: 500, + headers: { + [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION, + }, }) as TResponse; } return new Response(JSON.stringify(formattedError), { status: 500, + headers: { + [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION, + }, }) as TResponse; } }; From 586bcbcbf94554fa769d7fa1829e6e1a71c2a1b7 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Fri, 1 Aug 2025 13:26:55 +0300 Subject: [PATCH 07/10] fix: add header to serveMany error responses --- src/serve/serve-many.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/serve/serve-many.ts b/src/serve/serve-many.ts index a111af3..4aab6bb 100644 --- a/src/serve/serve-many.ts +++ b/src/serve/serve-many.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { WORKFLOW_PROTOCOL_VERSION, WORKFLOW_PROTOCOL_VERSION_HEADER } from "../constants"; import { WorkflowError } from "../error"; import { InvokableWorkflow, PublicServeOptions, RouteFunction } from "../types"; @@ -74,6 +75,9 @@ export const serveManyBase = < `Unexpected request in serveMany. workflowId not set. Please update the URL of your request.`, { status: 404, + headers: { + [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION, + }, } ); } @@ -83,6 +87,9 @@ export const serveManyBase = < `No workflows in serveMany found for '${pickedWorkflowId}'. Please update the URL of your request.`, { status: 404, + headers: { + [WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION, + }, } ); } From 2e986e68c0488099094c774b7f5750aaf8a87720 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Mon, 4 Aug 2025 11:00:32 +0300 Subject: [PATCH 08/10] fix: set headers before sending response and fix express issues in https://github.com/upstash/workflow-js/pull/118, we bumped express to express 5 which is a breaking change. One thing we missed was how the catch all routes were defined. Because of this, we were getting 'TypeError: Missing parameter name at 1: https://git.new/pathToRegexpError' errors in express example. Fixed it by updating the catch all routes in express handler. See the related issue for more details: https://github.com/expressjs/express/issues/5936 --- examples/express/api/index.ts | 11 ++++++++--- platforms/express.ts | 8 ++++---- platforms/nextjs.ts | 3 ++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/examples/express/api/index.ts b/examples/express/api/index.ts index 7cad636..777d7ae 100644 --- a/examples/express/api/index.ts +++ b/examples/express/api/index.ts @@ -26,7 +26,7 @@ app.use('/workflow', serve<{ message: string }>(async (context) => { const { body } = await context.call("get-data", { url: `${process.env.UPSTASH_WORKFLOW_URL ?? "http://localhost:3001"}/get-data`, - method: "POST", + method: "GET", body: { message: result1 } }) @@ -38,6 +38,11 @@ app.use('/workflow', serve<{ message: string }>(async (context) => { }) })); +app.get('/get-data', (req, res) => { + const message = req.body.message as string; + res.json({ message: `Received: ${message}` }); +}); + /** * ServeMany */ @@ -122,6 +127,6 @@ app.post("/ci", serve( } )) -app.listen(3000, () => { - console.log('Server running on port 3000'); +app.listen(3001, () => { + console.log('Server running on port 3001'); }); \ No newline at end of file diff --git a/platforms/express.ts b/platforms/express.ts index a67e9c0..0681694 100644 --- a/platforms/express.ts +++ b/platforms/express.ts @@ -65,12 +65,12 @@ function createExpressHandler( const response = await serveHandler(webRequest); - res.status(response.status).json(await response.json()); - // set headers for (const [key, value] of response.headers.entries()) { res.setHeader(key, value); } + + res.status(response.status).json(await response.json()); }; } @@ -82,7 +82,7 @@ export function serve( const handler: RequestHandler = createExpressHandler([routeFunction, options]); - router.all("*", handler); + router.all(/(.*)/, handler); return router; } @@ -113,6 +113,6 @@ export const serveMany = ( options, }); - router.all("*", handler); + router.all(/(.*)/, handler); return router; }; diff --git a/platforms/nextjs.ts b/platforms/nextjs.ts index a74f41e..730dd9d 100644 --- a/platforms/nextjs.ts +++ b/platforms/nextjs.ts @@ -105,12 +105,13 @@ export const servePagesRouter = ( method: "POST", }); const response = await serveHandler(request); - res.status(response.status).json(await response.json()); // set headers for (const [key, value] of response.headers.entries()) { res.setHeader(key, value); } + + res.status(response.status).json(await response.json()); }; return { From 29fdcb524c0b5e91ab0214d299ff5f84a3f38c49 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Mon, 4 Aug 2025 13:45:19 +0300 Subject: [PATCH 09/10] fix: change expected call count for invoke route --- examples/ci/app/test-routes/invoke/workflows/[...]/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ci/app/test-routes/invoke/workflows/[...]/route.ts b/examples/ci/app/test-routes/invoke/workflows/[...]/route.ts index c32619a..24c3ebf 100644 --- a/examples/ci/app/test-routes/invoke/workflows/[...]/route.ts +++ b/examples/ci/app/test-routes/invoke/workflows/[...]/route.ts @@ -210,7 +210,7 @@ export const { POST, GET } = testServe( baseUrl: BASE_URL }), { - expectedCallCount: 28, + expectedCallCount: 29, expectedResult: "done invoke", payload, headers: { From 4c468dfd9762659e27ac9f2706c2570bd06c519a Mon Sep 17 00:00:00 2001 From: CahidArda Date: Mon, 4 Aug 2025 14:33:48 +0300 Subject: [PATCH 10/10] fix: add non workflow trigger to ci --- examples/ci/app/ci/constants.ts | 4 ++ .../trigger-non-workflow/constants.ts | 1 + .../non-workflow/route.ts | 5 ++ .../trigger-non-workflow/workflow/route.ts | 68 +++++++++++++++++++ src/client/types.ts | 2 +- 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 examples/ci/app/test-routes/trigger-non-workflow/constants.ts create mode 100644 examples/ci/app/test-routes/trigger-non-workflow/non-workflow/route.ts create mode 100644 examples/ci/app/test-routes/trigger-non-workflow/workflow/route.ts diff --git a/examples/ci/app/ci/constants.ts b/examples/ci/app/ci/constants.ts index d010cea..69cedf6 100644 --- a/examples/ci/app/ci/constants.ts +++ b/examples/ci/app/ci/constants.ts @@ -99,6 +99,10 @@ export const TEST_ROUTES: Pick[] = [ { route: "invoke/workflows/workflowOne", waitForSeconds: 10 + }, + { + route: "trigger-non-workflow/workflow", + waitForSeconds: 10 } /** diff --git a/examples/ci/app/test-routes/trigger-non-workflow/constants.ts b/examples/ci/app/test-routes/trigger-non-workflow/constants.ts new file mode 100644 index 0000000..f27683a --- /dev/null +++ b/examples/ci/app/test-routes/trigger-non-workflow/constants.ts @@ -0,0 +1 @@ +export const NON_WORKFLOW_ROUTE_RESPONSE = "super-secret-response-foo"; \ No newline at end of file diff --git a/examples/ci/app/test-routes/trigger-non-workflow/non-workflow/route.ts b/examples/ci/app/test-routes/trigger-non-workflow/non-workflow/route.ts new file mode 100644 index 0000000..9ae0d16 --- /dev/null +++ b/examples/ci/app/test-routes/trigger-non-workflow/non-workflow/route.ts @@ -0,0 +1,5 @@ +import { NON_WORKFLOW_ROUTE_RESPONSE } from "../constants"; + +export const POST = async (request: Request) => { + return new Response(NON_WORKFLOW_ROUTE_RESPONSE, { status: 200 }); +} \ No newline at end of file diff --git a/examples/ci/app/test-routes/trigger-non-workflow/workflow/route.ts b/examples/ci/app/test-routes/trigger-non-workflow/workflow/route.ts new file mode 100644 index 0000000..2828f25 --- /dev/null +++ b/examples/ci/app/test-routes/trigger-non-workflow/workflow/route.ts @@ -0,0 +1,68 @@ +import { serve } from "@upstash/workflow/nextjs"; +import { Client, StepError } from "@upstash/workflow"; +import { BASE_URL } from "app/ci/constants"; +import { saveResult } from "app/ci/upstash/redis"; +import { expect, testServe } from "app/ci/utils"; +import { NON_WORKFLOW_ROUTE_RESPONSE } from "../constants"; + +const header = `test-header-foo` +const headerValue = `header-bar` + +const workflowClient = new Client({ baseUrl: process.env.QSTASH_URL, token: process.env.QSTASH_TOKEN! }) + +export const { POST, GET } = testServe( + serve(async (context) => { + + const { workflowRunId } = await context.run("trigger non-workflow", async () => + workflowClient.trigger({ + url: `${BASE_URL}/test-routes/trigger-non-workflow/non-workflow`, + }) + ) + + await context.sleep("wait before checking logs", 5) + + const errorBody = await context.run("check run logs", async () => { + for (let counter = 0; counter < 5; counter++) { + const { runs } = await workflowClient.logs({ workflowRunId }) + if (runs.length === 1) { + const run = runs[0]; + expect(run.workflowState, "RUN_FAILED") + expect(run.steps.length, 2) + + const secondStep = run.steps[1]; + expect(secondStep.type, "next") + expect(secondStep.steps.length, 1) + expect(secondStep.steps[0].state, "STEP_FAILED") + + const errors = (secondStep.steps[0] as { errors: StepError[] }).errors; + + expect(errors.length, 1) + expect(errors[0].error, "detected a non-workflow destination for trigger/invoke. make sure you are sending the request to the correct endpoint") + return errors[0].body + } else { + await new Promise(r => setTimeout(r, 2000)); + } + } + return false + }) + + if (!errorBody) { + throw new Error(`Workflow run with ID ${workflowRunId} did not complete successfully`) + } + + await saveResult( + context, + errorBody + ) + + }, { + baseUrl: BASE_URL, + retries: 0 + }), { + expectedCallCount: 5, + expectedResult: NON_WORKFLOW_ROUTE_RESPONSE, + payload: NON_WORKFLOW_ROUTE_RESPONSE, + headers: { + [header]: headerValue + } +}) \ No newline at end of file diff --git a/src/client/types.ts b/src/client/types.ts index ef4f78c..e600d84 100644 --- a/src/client/types.ts +++ b/src/client/types.ts @@ -145,7 +145,7 @@ export type StepLog = BaseStepLog & AsOptional<{ sleepUntil: number }> & AsOptional; -type StepError = { +export type StepError = { /** * error message associated with the request *