diff --git a/examples/ci/app/ci/constants.ts b/examples/ci/app/ci/constants.ts index d283226..d65e288 100644 --- a/examples/ci/app/ci/constants.ts +++ b/examples/ci/app/ci/constants.ts @@ -109,6 +109,9 @@ export const TEST_ROUTES: Pick[] = [ }, { route: "qstash-trigger-fetch/workflows/mainWorkflow", + }, + { + route: "invoke-parent-failure/workflow", } /** diff --git a/examples/ci/app/ci/utils.ts b/examples/ci/app/ci/utils.ts index c94f9a7..54b4cf9 100644 --- a/examples/ci/app/ci/utils.ts +++ b/examples/ci/app/ci/utils.ts @@ -54,7 +54,7 @@ export const getTestConfig = async (route: string) => { ) if (response.status !== 200) { - throw new Error(`Failed to get the error config: ${response.statusText}`) + throw new Error(`Failed to get the test config: ${response.statusText}`) } const testConfig = await response.json() as Parameters[1] diff --git a/examples/ci/app/test-routes/invoke-parent-failure/[...]/route.ts b/examples/ci/app/test-routes/invoke-parent-failure/[...]/route.ts new file mode 100644 index 0000000..07bd542 --- /dev/null +++ b/examples/ci/app/test-routes/invoke-parent-failure/[...]/route.ts @@ -0,0 +1,98 @@ + +import { Client, WorkflowContext, WorkflowNonRetryableError } from "@upstash/workflow"; +import { createWorkflow, serveMany } from "@upstash/workflow/nextjs"; +import { BASE_URL, CI_RANDOM_ID_HEADER, CI_ROUTE_HEADER } from "app/ci/constants"; +import { saveResult } from "app/ci/upstash/redis"; +import { testServe } from "app/ci/utils"; + +const client = new Client({ + baseUrl: process.env.QSTASH_URL!, + token: process.env.QSTASH_TOKEN!, +}) + +const FAILING_STEP_NAME = "failing step" +const INVOKE_CHILD_STEP_NAME = "invoke child" + +const workflow = createWorkflow(async (context: WorkflowContext) => { + const workflowRunId = await context.run("step 1", async () => { + console.log("workflow says hi") + return `workflow-run-id-${(Math.random() * 1000).toFixed(0)}` + }) + + await context.invoke("invoke child", { + workflow: testWorkflow, + body: undefined, + headers: { + [CI_ROUTE_HEADER]: context.headers.get(CI_ROUTE_HEADER) as string, + [CI_RANDOM_ID_HEADER]: context.headers.get(CI_RANDOM_ID_HEADER) as string, + }, + workflowRunId, + }) + + await context.run("verify step", async () => { + + for (let i = 0; i < 30; i++) { + const workflowLogs = await client.logs({ workflowRunId: `wfr_${workflowRunId}` }) + const workflowRun = workflowLogs.runs[0] + + if (workflowRun && workflowRun.steps[1] && workflowRun.steps[1].type === "parallel") { + const invokeChild = workflowRun.steps[1].steps?.find(s => s.stepName === INVOKE_CHILD_STEP_NAME) + const failingStep = workflowRun.steps[1].steps?.find(s => s.stepName === FAILING_STEP_NAME) + + if (invokeChild && invokeChild.state === "STEP_SUCCESS" && failingStep && failingStep.state === "STEP_FAILED") { + return { success: true } + } + } + // sleep for 1 sec + await new Promise(r => setTimeout(r, 1000)); + } + + console.warn("child workflow did not fail within expected time"); + throw new WorkflowNonRetryableError("child workflow did not fail within expected time") + }) + + await saveResult( + context, + "done invoke" + ) +}) + +const testWorkflow = createWorkflow(async (context: WorkflowContext) => { + await Promise.all([ + context.invoke(INVOKE_CHILD_STEP_NAME, { + workflow: childWorkflow, + body: undefined, + headers: { + [CI_ROUTE_HEADER]: context.headers.get(CI_ROUTE_HEADER) as string, + [CI_RANDOM_ID_HEADER]: context.headers.get(CI_RANDOM_ID_HEADER) as string, + }, + }), + context.run(FAILING_STEP_NAME, async () => { + // sleep for 1 sec + await new Promise(r => setTimeout(r, 1000)); + throw new WorkflowNonRetryableError("step failed") + }) + ]) + +}) + +const childWorkflow = createWorkflow(async (context: WorkflowContext) => { + await context.sleep("sleep 3s", 3) + await context.run("child step", async () => { + console.log("child workflow step") + }) +}) + +export const { POST, GET } = testServe(serveMany({ + workflow, + testWorkflow, + childWorkflow, +}, { + baseUrl: BASE_URL +}), + { + expectedCallCount: 11, + expectedResult: "done invoke", + payload: undefined, + } +) diff --git a/src/client/index.test.ts b/src/client/index.test.ts index 0edf6ba..6b6261d 100644 --- a/src/client/index.test.ts +++ b/src/client/index.test.ts @@ -512,6 +512,7 @@ describe("workflow client", () => { workflowRunId, steps: [], url: "https://httpstat.us/200", + workflowRunCreatedAt: 0, }), }); @@ -656,6 +657,7 @@ describe("workflow client", () => { workflowRunId, steps: [], url: "https://httpstat.us/400", + workflowRunCreatedAt: 0, }), failureUrl: "https://400check.requestcatcher.com/", retries: 0, diff --git a/src/client/index.ts b/src/client/index.ts index 55e4c43..d8f106c 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -245,6 +245,7 @@ export class Client { workflowRunId: finalWorkflowRunId, telemetry: option.disableTelemetry ? undefined : { sdk: SDK_TELEMETRY }, label: option.label, + workflowRunCreatedAt: Date.now(), // pass a timestamp (server will override it) }); return { diff --git a/src/constants.ts b/src/constants.ts index a889c43..6c7d84e 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -3,6 +3,7 @@ import { Telemetry } from "./types"; export const WORKFLOW_ID_HEADER = "Upstash-Workflow-RunId"; export const WORKFLOW_INIT_HEADER = "Upstash-Workflow-Init"; export const WORKFLOW_URL_HEADER = "Upstash-Workflow-Url"; +export const WORKFLOW_CREATED_AT_HEADER = "Upstash-Workflow-CreatedAt"; export const WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure"; export const WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback"; export const WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set"; diff --git a/src/context/auto-executor.test.ts b/src/context/auto-executor.test.ts index 81b876e..24c71e4 100644 --- a/src/context/auto-executor.test.ts +++ b/src/context/auto-executor.test.ts @@ -92,6 +92,7 @@ describe("auto-executor", () => { steps, url: WORKFLOW_ENDPOINT, invokeCount: 7, + workflowRunCreatedAt: 0, }); }; diff --git a/src/context/context.test.ts b/src/context/context.test.ts index bdcef29..a78a3ec 100644 --- a/src/context/context.test.ts +++ b/src/context/context.test.ts @@ -24,6 +24,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers({ "upstash-label": label }) as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, label, }); expect(context.label).toBe(label); @@ -43,6 +44,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); const throws = async () => { @@ -67,6 +69,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); const throws = async () => { @@ -89,6 +92,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); const throws = async () => { @@ -111,6 +115,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); const throws = async () => { @@ -134,6 +139,7 @@ describe("context tests", () => { headers: new Headers() as Headers, workflowRunId: "wfr-id", invokeCount: 5, + workflowRunCreatedAt: 0, }); await mockQStashServer({ @@ -183,6 +189,7 @@ describe("context tests", () => { headers: new Headers() as Headers, workflowRunId: "wfr-id", invokeCount: 5, + workflowRunCreatedAt: 0, }); const eventId = "my-event-id"; @@ -235,6 +242,7 @@ describe("context tests", () => { headers: new Headers() as Headers, workflowRunId: "wfr-id", invokeCount: 1, + workflowRunCreatedAt: 0, }); const eventId = "my-event-id"; @@ -306,6 +314,7 @@ describe("context tests", () => { headers: new Headers() as Headers, workflowRunId: "wfr-id", invokeCount: 7, + workflowRunCreatedAt: 0, }); await mockQStashServer({ execute: () => { @@ -384,6 +393,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); await mockQStashServer({ execute: () => { @@ -445,6 +455,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); await mockQStashServer({ execute: () => { @@ -509,6 +520,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); try { await context.cancel(); @@ -530,6 +542,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); await mockQStashServer({ @@ -559,6 +572,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); await mockQStashServer({ @@ -589,6 +603,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); const openAIToken = `hello-there`; @@ -670,6 +685,7 @@ describe("context tests", () => { headers: new Headers() as Headers, workflowRunId: "wfr-id", invokeCount: 5, + workflowRunCreatedAt: 0, }); const openAIToken = `hello-there`; @@ -753,6 +769,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); const resendToken = `hello-there`; @@ -832,6 +849,7 @@ describe("context tests", () => { headers: new Headers() as Headers, workflowRunId: "wfr-id", invokeCount: 3, + workflowRunCreatedAt: 0, }); const anthropicToken = `hello-there`; @@ -925,6 +943,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); await mockQStashServer({ @@ -972,6 +991,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); const webhook = { @@ -1028,6 +1048,7 @@ describe("context tests", () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "wfr-id", + workflowRunCreatedAt: 0, }); // First create webhook diff --git a/src/context/context.ts b/src/context/context.ts index 376fd6e..065356c 100644 --- a/src/context/context.ts +++ b/src/context/context.ts @@ -60,6 +60,10 @@ export class WorkflowContext { * Run id of the workflow */ public readonly workflowRunId: string; + /** + * Creation time of the workflow run + */ + public readonly workflowRunCreatedAt: number; /** * URL of the workflow * @@ -153,6 +157,7 @@ export class WorkflowContext { constructor({ qstashClient, workflowRunId, + workflowRunCreatedAt, headers, steps, url, @@ -165,6 +170,7 @@ export class WorkflowContext { }: { qstashClient: WorkflowClient; workflowRunId: string; + workflowRunCreatedAt: number; headers: Headers; steps: Step[]; url: string; @@ -177,6 +183,7 @@ export class WorkflowContext { }) { this.qstashClient = qstashClient; this.workflowRunId = workflowRunId; + this.workflowRunCreatedAt = workflowRunCreatedAt; this.steps = steps; this.url = url; this.headers = headers; diff --git a/src/context/steps.test.ts b/src/context/steps.test.ts index dd8c4ee..145f131 100644 --- a/src/context/steps.test.ts +++ b/src/context/steps.test.ts @@ -30,6 +30,7 @@ const createMockContext = () => { url: WORKFLOW_ENDPOINT, headers: new Headers() as Headers, workflowRunId: "test-wfr-id", + workflowRunCreatedAt: 0, }); }; diff --git a/src/context/steps.ts b/src/context/steps.ts index c29c229..4de4d68 100644 --- a/src/context/steps.ts +++ b/src/context/steps.ts @@ -714,6 +714,7 @@ export class LazyInvokeStep extends BaseLazy Object.entries(invokerHeaders).map((pairs) => [pairs[0], [pairs[1]]]) ), workflowRunId: context.workflowRunId, + workflowRunCreatedAt: context.workflowRunCreatedAt, workflowUrl: context.url, step, }; diff --git a/src/serve/authorization.test.ts b/src/serve/authorization.test.ts index b36b19e..b8e4a9e 100644 --- a/src/serve/authorization.test.ts +++ b/src/serve/authorization.test.ts @@ -22,6 +22,7 @@ describe("disabled workflow context", () => { steps: [], url: WORKFLOW_ENDPOINT, initialPayload: "my-payload", + workflowRunCreatedAt: 0, }); describe("should throw abort for each step kind", () => { test("run", async () => { @@ -116,6 +117,7 @@ describe("disabled workflow context", () => { steps: [], url: WORKFLOW_ENDPOINT, initialPayload: "my-payload", + workflowRunCreatedAt: 0, }); test("should return step-found on step", async () => { @@ -193,6 +195,7 @@ describe("disabled workflow context", () => { steps: [], url: WORKFLOW_ENDPOINT, initialPayload: "my-payload", + workflowRunCreatedAt: 0, }); let called = false; @@ -247,6 +250,7 @@ describe("disabled workflow context", () => { steps: [], url: WORKFLOW_ENDPOINT, initialPayload: "my-payload", + workflowRunCreatedAt: 0, }); let called = false; @@ -302,6 +306,7 @@ describe("disabled workflow context", () => { url: WORKFLOW_ENDPOINT, initialPayload: "my-payload", invokeCount: 4, + workflowRunCreatedAt: 0, }); let called = false; diff --git a/src/serve/authorization.ts b/src/serve/authorization.ts index 3a42977..c3affb8 100644 --- a/src/serve/authorization.ts +++ b/src/serve/authorization.ts @@ -83,6 +83,7 @@ export class DisabledWorkflowContext< token: "disabled-client", }), workflowRunId: context.workflowRunId, + workflowRunCreatedAt: context.workflowRunCreatedAt, headers: context.headers, steps: [], url: context.url, diff --git a/src/serve/index.ts b/src/serve/index.ts index c8f818d..747049c 100644 --- a/src/serve/index.ts +++ b/src/serve/index.ts @@ -1,6 +1,7 @@ import { makeCancelRequest } from "../client/utils"; import { SDK_TELEMETRY, + WORKFLOW_CREATED_AT_HEADER, WORKFLOW_INVOKE_COUNT_HEADER, WORKFLOW_LABEL_HEADER, WORKFLOW_PROTOCOL_VERSION, @@ -185,6 +186,7 @@ export const serveBase = < const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0"); const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? undefined; + const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER)!; // create context const workflowContext = new WorkflowContext({ @@ -198,6 +200,7 @@ export const serveBase = < telemetry, invokeCount, label, + workflowRunCreatedAt: Number(workflowRunCreatedAt), middlewareManager, }); diff --git a/src/serve/serve-many.test.ts b/src/serve/serve-many.test.ts index 2446c50..65c10ed 100644 --- a/src/serve/serve-many.test.ts +++ b/src/serve/serve-many.test.ts @@ -9,7 +9,11 @@ import { WORKFLOW_ENDPOINT, } from "../test-utils"; import { nanoid } from "../utils"; -import { WORKFLOW_INVOKE_COUNT_HEADER, WORKFLOW_LABEL_HEADER } from "../constants"; +import { + WORKFLOW_CREATED_AT_HEADER, + WORKFLOW_INVOKE_COUNT_HEADER, + WORKFLOW_LABEL_HEADER, +} from "../constants"; import { getNewUrlFromWorkflowId } from "./serve-many"; describe("serveMany", () => { @@ -158,11 +162,15 @@ describe("serveMany", () => { ); test("should invoke workflowOne from workflowTwo with object body", async () => { + const workflowCreatedAt = "1769433002013"; const request = getRequest( `${WORKFLOW_ENDPOINT}/workflowTwo`, "wfr_id", "initial-payload", - [] + [], + { + [WORKFLOW_CREATED_AT_HEADER]: workflowCreatedAt, + } ); await mockQStashServer({ @@ -191,7 +199,8 @@ describe("serveMany", () => { "content-type": ["application/json"], "upstash-forward-x-vercel-protection-bypass": ["testing"], }, - workflowRunId: expect.any(String), + workflowRunId: "wfr_id", + workflowRunCreatedAt: Number(workflowCreatedAt), workflowUrl: "https://requestcatcher.com/api/workflowTwo", step: { stepId: 1, @@ -328,11 +337,15 @@ describe("serveMany", () => { }); test("should invoke workflowTwo from workflowFive with string body", async () => { + const workflowCreatedAt = "1869433002013"; const request = getRequest( `${WORKFLOW_ENDPOINT}/workflowFive`, "wfr_id", "initial-payload", - [] + [], + { + [WORKFLOW_CREATED_AT_HEADER]: workflowCreatedAt, + } ); await mockQStashServer({ @@ -360,7 +373,8 @@ describe("serveMany", () => { "Upstash-Workflow-Url": ["https://requestcatcher.com/api/workflowFive"], "content-type": ["application/json"], }, - workflowRunId: expect.any(String), + workflowRunId: "wfr_id", + workflowRunCreatedAt: Number(workflowCreatedAt), workflowUrl: "https://requestcatcher.com/api/workflowFive", step: { stepId: 1, @@ -374,11 +388,15 @@ describe("serveMany", () => { }); test("should invoke workflowThree from workflowSix with no body", async () => { + const workflowCreatedAt = "1629433002013"; const request = getRequest( `${WORKFLOW_ENDPOINT}/workflowSix`, "wfr_id", "initial-payload", - [] + [], + { + [WORKFLOW_CREATED_AT_HEADER]: workflowCreatedAt, + } ); await mockQStashServer({ @@ -411,8 +429,9 @@ describe("serveMany", () => { stepName: "invoke workflow three", stepType: "Invoke", }, - workflowRunId: expect.any(String), + workflowRunId: "wfr_id", workflowUrl: "https://requestcatcher.com/api/workflowSix", + workflowRunCreatedAt: Number(workflowCreatedAt), }, }, }); @@ -473,11 +492,15 @@ describe("serveMany", () => { }); test("should invoke workflowOne from workflowEight with label", async () => { + const workflowCreatedAt = "4323425434234"; const request = getRequest( `${WORKFLOW_ENDPOINT}/workflowEight`, "wfr_id", "initial-payload", - [] + [], + { + [WORKFLOW_CREATED_AT_HEADER]: workflowCreatedAt, + } ); await mockQStashServer({ @@ -505,7 +528,8 @@ describe("serveMany", () => { "Upstash-Workflow-Url": ["https://requestcatcher.com/api/workflowEight"], "content-type": ["application/json"], }, - workflowRunId: expect.any(String), + workflowRunId: "wfr_id", + workflowRunCreatedAt: Number(workflowCreatedAt), workflowUrl: "https://requestcatcher.com/api/workflowEight", step: { stepId: 1, diff --git a/src/serve/utils.test.ts b/src/serve/utils.test.ts index 41e0543..a606b31 100644 --- a/src/serve/utils.test.ts +++ b/src/serve/utils.test.ts @@ -13,6 +13,7 @@ describe("isDisabledWorkflowContext", () => { steps: [], url: "", workflowRunId: "", + workflowRunCreatedAt: 0, }); expect(isDisabledWorkflowContext(context)).toBeFalse(); @@ -26,6 +27,7 @@ describe("isDisabledWorkflowContext", () => { steps: [], url: "", workflowRunId: "", + workflowRunCreatedAt: 0, }); expect(isDisabledWorkflowContext(context)).toBeTrue(); diff --git a/src/types.ts b/src/types.ts index a176f22..9e11446 100644 --- a/src/types.ts +++ b/src/types.ts @@ -483,6 +483,7 @@ export type HeaderParams = { export type InvokeWorkflowRequest = { workflowUrl: string; workflowRunId: string; + workflowRunCreatedAt: number; headers: Record; step: Step; body?: string; diff --git a/src/workflow-parser.ts b/src/workflow-parser.ts index a7d2f4c..ac9982e 100644 --- a/src/workflow-parser.ts +++ b/src/workflow-parser.ts @@ -380,7 +380,9 @@ export const handleFailure = async ({ } try { - const { status, header, body, url, sourceBody, workflowRunId } = JSON.parse(requestPayload) as { + const { status, header, body, url, sourceBody, workflowRunId, workflowCreatedAt } = JSON.parse( + requestPayload + ) as { status: number; header: Record; body: string; @@ -388,6 +390,7 @@ export const handleFailure = async ({ sourceHeader: Record; sourceBody: string; workflowRunId: string; + workflowCreatedAt: number; sourceMessageId: string; }; @@ -425,6 +428,7 @@ export const handleFailure = async ({ env, telemetry: undefined, // not going to make requests in authentication check label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? undefined, + workflowRunCreatedAt: workflowCreatedAt, middlewareManager: undefined, }); diff --git a/src/workflow-requests.test.ts b/src/workflow-requests.test.ts index 5a127ca..a331899 100644 --- a/src/workflow-requests.test.ts +++ b/src/workflow-requests.test.ts @@ -62,6 +62,7 @@ describe("Workflow Requests", () => { url: WORKFLOW_ENDPOINT, label, middlewareManager: new MiddlewareManager(), + workflowRunCreatedAt: 0, }); expect(context.label).toBe(label); @@ -103,6 +104,7 @@ describe("Workflow Requests", () => { headers: new Headers({}) as Headers, steps: [], url: WORKFLOW_ENDPOINT, + workflowRunCreatedAt: 0, }); await mockQStashServer({ @@ -217,6 +219,7 @@ describe("Workflow Requests", () => { headers: new Headers({}) as Headers, steps: [], url: WORKFLOW_ENDPOINT, + workflowRunCreatedAt: 0, }); const finished = new FinishState(); @@ -287,6 +290,7 @@ describe("Workflow Requests", () => { headers: new Headers({}) as Headers, steps: [], url: WORKFLOW_ENDPOINT, + workflowRunCreatedAt: 0, }); const finished = new FinishState(); @@ -320,6 +324,7 @@ describe("Workflow Requests", () => { headers: new Headers({}) as Headers, steps: [], url: WORKFLOW_ENDPOINT, + workflowRunCreatedAt: 0, }); const spy = spyOn(context.qstashClient.http, "request"); @@ -567,6 +572,7 @@ describe("Workflow Requests", () => { const mockContext = new WorkflowContext({ qstashClient: new Client({ baseUrl: MOCK_SERVER_URL, token: "myToken" }), workflowRunId: "test-run-id", + workflowRunCreatedAt: 0, headers: new Headers() as Headers, steps: [], url: WORKFLOW_ENDPOINT, @@ -616,6 +622,7 @@ describe("Workflow Requests", () => { const mockContext = new WorkflowContext({ qstashClient: new Client({ baseUrl: MOCK_SERVER_URL, token: "myToken" }), workflowRunId, + workflowRunCreatedAt: 0, headers: new Headers() as Headers, steps: [], url: WORKFLOW_ENDPOINT, @@ -723,6 +730,7 @@ describe("Workflow Requests", () => { steps: [], url: WORKFLOW_ENDPOINT, workflowRunId, + workflowRunCreatedAt: 0, }); const lazyStep = new LazyWaitForEventStep( context, @@ -796,6 +804,7 @@ describe("Workflow Requests", () => { headers: new Headers({}) as Headers, steps: [], url: WORKFLOW_ENDPOINT, + workflowRunCreatedAt: 0, }); await triggerFirstInvocation({ workflowContext: context }); @@ -819,6 +828,7 @@ describe("Workflow Requests", () => { headers: new Headers({}) as Headers, steps: [], url: WORKFLOW_ENDPOINT, + workflowRunCreatedAt: 0, }); await triggerFirstInvocation({ @@ -870,6 +880,7 @@ describe("Workflow Requests", () => { headers: new Headers({}) as Headers, steps: [], url: WORKFLOW_ENDPOINT, + workflowRunCreatedAt: 0, }); await triggerFirstInvocation({ @@ -907,6 +918,7 @@ describe("Workflow Requests", () => { const context = new WorkflowContext({ qstashClient, workflowRunId: workflowRunId, + workflowRunCreatedAt: 0, initialPayload: undefined, headers: new Headers({}) as Headers, steps: [ @@ -969,6 +981,7 @@ describe("Workflow Requests", () => { const context = new WorkflowContext({ qstashClient, workflowRunId: workflowRunId, + workflowRunCreatedAt: 0, initialPayload: undefined, headers: new Headers({}) as Headers, steps: [], @@ -988,6 +1001,7 @@ describe("Workflow Requests", () => { const noRetryContext = new WorkflowContext({ qstashClient, workflowRunId: workflowRunId, + workflowRunCreatedAt: 0, initialPayload: undefined, headers: new Headers({}) as Headers, steps: [],