@upstash/workflow 1.3.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/astro.js +42 -5
- package/astro.mjs +3 -1
- package/{chunk-WEQGCISK.mjs → chunk-DRHEHWMS.mjs} +42 -5
- package/cloudflare.js +42 -5
- package/cloudflare.mjs +3 -1
- package/express.js +43 -5
- package/express.mjs +4 -1
- package/h3.js +42 -5
- package/h3.mjs +3 -1
- package/hono.js +42 -5
- package/hono.mjs +3 -1
- package/index.d.mts +22 -4
- package/index.d.ts +22 -4
- package/index.js +65 -7
- package/index.mjs +26 -3
- package/nextjs.js +43 -5
- package/nextjs.mjs +4 -1
- package/package.json +1 -1
- package/react-router.js +42 -5
- package/react-router.mjs +3 -1
- package/solidjs.js +42 -5
- package/solidjs.mjs +3 -1
- package/svelte.js +42 -5
- package/svelte.mjs +3 -1
- package/tanstack.js +42 -5
- package/tanstack.mjs +3 -1
package/index.d.ts
CHANGED
|
@@ -209,6 +209,11 @@ type StepLogGroup = {
|
|
|
209
209
|
*/
|
|
210
210
|
type: "next";
|
|
211
211
|
};
|
|
212
|
+
/**
|
|
213
|
+
* State of the failure function (`failureUrl`/`failureFunction`) call of a
|
|
214
|
+
* failed workflow run.
|
|
215
|
+
*/
|
|
216
|
+
type FailureFunctionState = "CALLBACK_INPROGRESS" | "CALLBACK_SUCCESS" | "CALLBACK_FAIL" | "CALLBACK_CANCELED";
|
|
212
217
|
type FailureFunctionLog = {
|
|
213
218
|
/**
|
|
214
219
|
* messageId of the message published for handling the failure
|
|
@@ -221,7 +226,7 @@ type FailureFunctionLog = {
|
|
|
221
226
|
/**
|
|
222
227
|
* State of the message published for failure
|
|
223
228
|
*/
|
|
224
|
-
state:
|
|
229
|
+
state: FailureFunctionState;
|
|
225
230
|
/**
|
|
226
231
|
* Headers received from the step which caused the workflow to fail
|
|
227
232
|
*/
|
|
@@ -570,7 +575,11 @@ type WorkflowFilterFields = {
|
|
|
570
575
|
workflowUrl?: string | string[];
|
|
571
576
|
workflowRunId?: string;
|
|
572
577
|
workflowCreatedAt?: number;
|
|
573
|
-
|
|
578
|
+
/**
|
|
579
|
+
* Filter by the state of the failure function call. Supports multiple values:
|
|
580
|
+
* pass an array to match runs in any of the given states (OR semantics).
|
|
581
|
+
*/
|
|
582
|
+
failureFunctionState?: FailureFunctionState | FailureFunctionState[];
|
|
574
583
|
};
|
|
575
584
|
type WorkflowLogsFilterFields = {
|
|
576
585
|
state?: WorkflowRunLog["workflowState"];
|
|
@@ -680,7 +689,7 @@ type ResumeRestartOptions = {
|
|
|
680
689
|
retries?: number;
|
|
681
690
|
};
|
|
682
691
|
type FailureCallbackInfo = {
|
|
683
|
-
state?:
|
|
692
|
+
state?: FailureFunctionState;
|
|
684
693
|
responseStatus?: number;
|
|
685
694
|
responseBody?: string;
|
|
686
695
|
responseHeaders?: Record<string, string[]>;
|
|
@@ -834,6 +843,15 @@ declare class DLQ {
|
|
|
834
843
|
retryFailureFunction({ dlqId }: Pick<DLQResumeRestartOptions<string>, "dlqId">): Promise<DLQResumeRestartResponse & {
|
|
835
844
|
error?: string;
|
|
836
845
|
}>;
|
|
846
|
+
/**
|
|
847
|
+
* Cancel the in-progress failure function (failureUrl/failureFunction) call
|
|
848
|
+
* of a workflow run.
|
|
849
|
+
*
|
|
850
|
+
* Fails if the run has no failure function call in progress.
|
|
851
|
+
*
|
|
852
|
+
* @param dlqId - The ID of the DLQ message whose failure function call will be canceled
|
|
853
|
+
*/
|
|
854
|
+
cancelFailureFunction({ dlqId }: Pick<DLQResumeRestartOptions<string>, "dlqId">): Promise<void>;
|
|
837
855
|
/**
|
|
838
856
|
* Delete DLQ messages.
|
|
839
857
|
*
|
|
@@ -1071,4 +1089,4 @@ declare class Client {
|
|
|
1071
1089
|
get dlq(): DLQ;
|
|
1072
1090
|
}
|
|
1073
1091
|
|
|
1074
|
-
export { Client, type DLQResumeRestartOptions, type DLQResumeRestartResponse, NotifyResponse, RawStep, RouteFunction, type StepError, type StepLog, StepType, Telemetry, type TriggerOptions, Waiter, WorkflowClient, WorkflowMiddleware, WorkflowReceiver, type WorkflowRunLog, type WorkflowRunLogs, WorkflowServeOptions, loggingMiddleware, serve };
|
|
1092
|
+
export { Client, type DLQResumeRestartOptions, type DLQResumeRestartResponse, type FailureFunctionState, NotifyResponse, RawStep, RouteFunction, type StepError, type StepLog, StepType, Telemetry, type TriggerOptions, Waiter, WorkflowClient, WorkflowMiddleware, WorkflowReceiver, type WorkflowRunLog, type WorkflowRunLogs, WorkflowServeOptions, loggingMiddleware, serve };
|
package/index.js
CHANGED
|
@@ -33,6 +33,9 @@ __export(src_exports, {
|
|
|
33
33
|
});
|
|
34
34
|
module.exports = __toCommonJS(src_exports);
|
|
35
35
|
|
|
36
|
+
// src/serve/index.ts
|
|
37
|
+
var import_qstash12 = require("@upstash/qstash");
|
|
38
|
+
|
|
36
39
|
// src/client/utils.ts
|
|
37
40
|
var import_qstash2 = require("@upstash/qstash");
|
|
38
41
|
|
|
@@ -868,6 +871,10 @@ var triggerFirstInvocation = async (params) => {
|
|
|
868
871
|
workflowConfig: {
|
|
869
872
|
workflowRunId: workflowContext.workflowRunId,
|
|
870
873
|
workflowUrl: workflowContext.url,
|
|
874
|
+
// the run doesn't exist yet, so the trigger time is sent when the creation time
|
|
875
|
+
// is not known. serve and client.trigger set it on the context already, so a
|
|
876
|
+
// retry of this request carries the same value and is deduplicated.
|
|
877
|
+
workflowRunCreatedAt: workflowContext.workflowRunCreatedAt || Date.now(),
|
|
871
878
|
failureUrl,
|
|
872
879
|
retries,
|
|
873
880
|
retryDelay,
|
|
@@ -971,7 +978,10 @@ var triggerWorkflowDelete = async (workflowContext, result, cancel = false, disp
|
|
|
971
978
|
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
972
979
|
method: "DELETE",
|
|
973
980
|
parseResponseAsJson: false,
|
|
974
|
-
body: JSON.stringify(result)
|
|
981
|
+
body: JSON.stringify(result),
|
|
982
|
+
// with the creation time, QStash deletes the run only if it is the same run.
|
|
983
|
+
// a late delete of a previous run can't delete a new run with the same id.
|
|
984
|
+
headers: workflowContext.workflowRunCreatedAt ? { [WORKFLOW_CREATED_AT_HEADER]: workflowContext.workflowRunCreatedAt.toString() } : void 0
|
|
975
985
|
});
|
|
976
986
|
await dispatchDebug?.("onInfo", {
|
|
977
987
|
info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
|
|
@@ -1048,6 +1058,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
1048
1058
|
const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
|
|
1049
1059
|
const contentType = request.headers.get("Upstash-Workflow-ContentType");
|
|
1050
1060
|
const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
|
|
1061
|
+
const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
1051
1062
|
if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
|
|
1052
1063
|
throw new Error(
|
|
1053
1064
|
`Missing info in callback message source header: ${JSON.stringify({
|
|
@@ -1065,6 +1076,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
1065
1076
|
workflowConfig: {
|
|
1066
1077
|
workflowRunId,
|
|
1067
1078
|
workflowUrl,
|
|
1079
|
+
workflowRunCreatedAt: Number(workflowRunCreatedAt ?? "0"),
|
|
1068
1080
|
telemetry
|
|
1069
1081
|
},
|
|
1070
1082
|
invokeCount: Number(invokeCount)
|
|
@@ -1212,6 +1224,7 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1212
1224
|
workflowConfig: {
|
|
1213
1225
|
workflowRunId: context.workflowRunId,
|
|
1214
1226
|
workflowUrl: context.url,
|
|
1227
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1215
1228
|
useJSONContent: false,
|
|
1216
1229
|
telemetry
|
|
1217
1230
|
},
|
|
@@ -1539,6 +1552,8 @@ var LazyWaitEventStep = class extends BaseLazyStep {
|
|
|
1539
1552
|
) : {},
|
|
1540
1553
|
// note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
|
|
1541
1554
|
"Upstash-Workflow-Runid": [context.workflowRunId],
|
|
1555
|
+
// same for WORKFLOW_CREATED_AT_HEADER, because Createdat -> CreatedAt:
|
|
1556
|
+
...context.workflowRunCreatedAt ? { "Upstash-Workflow-Createdat": [context.workflowRunCreatedAt.toString()] } : {},
|
|
1542
1557
|
[WORKFLOW_INIT_HEADER]: ["false"],
|
|
1543
1558
|
[WORKFLOW_URL_HEADER]: [context.url],
|
|
1544
1559
|
"Upstash-Workflow-CallType": ["step"]
|
|
@@ -1641,6 +1656,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1641
1656
|
workflowConfig: {
|
|
1642
1657
|
workflowRunId: context.workflowRunId,
|
|
1643
1658
|
workflowUrl: context.url,
|
|
1659
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1644
1660
|
telemetry,
|
|
1645
1661
|
useJSONContent: false
|
|
1646
1662
|
},
|
|
@@ -1650,6 +1666,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1650
1666
|
invokerHeaders[key] = value;
|
|
1651
1667
|
});
|
|
1652
1668
|
invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
|
|
1669
|
+
if (context.workflowRunCreatedAt) {
|
|
1670
|
+
invokerHeaders["Upstash-Workflow-Createdat"] = context.workflowRunCreatedAt.toString();
|
|
1671
|
+
}
|
|
1653
1672
|
const request = {
|
|
1654
1673
|
body: stringifyBody(this.params.body),
|
|
1655
1674
|
headers: Object.fromEntries(
|
|
@@ -1678,6 +1697,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1678
1697
|
workflowConfig: {
|
|
1679
1698
|
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1680
1699
|
workflowUrl: newUrl,
|
|
1700
|
+
// the invoked run doesn't exist yet, so the creation time of the invoker run is sent.
|
|
1701
|
+
// it separates the invocations made by different runs of the invoker.
|
|
1702
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1681
1703
|
retries,
|
|
1682
1704
|
retryDelay,
|
|
1683
1705
|
telemetry,
|
|
@@ -1842,6 +1864,9 @@ var WorkflowHeaders = class {
|
|
|
1842
1864
|
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
1843
1865
|
...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
|
|
1844
1866
|
};
|
|
1867
|
+
if (this.workflowConfig.workflowRunCreatedAt) {
|
|
1868
|
+
this.headers.rawHeaders[WORKFLOW_CREATED_AT_HEADER] = this.workflowConfig.workflowRunCreatedAt.toString();
|
|
1869
|
+
}
|
|
1845
1870
|
if (this.stepInfo?.lazyStep.stepType !== "Call") {
|
|
1846
1871
|
this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
|
|
1847
1872
|
}
|
|
@@ -2001,6 +2026,7 @@ var submitParallelSteps = async ({
|
|
|
2001
2026
|
workflowConfig: {
|
|
2002
2027
|
workflowRunId: context.workflowRunId,
|
|
2003
2028
|
workflowUrl: context.url,
|
|
2029
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
2004
2030
|
telemetry
|
|
2005
2031
|
},
|
|
2006
2032
|
invokeCount
|
|
@@ -3435,7 +3461,13 @@ var createRegionalHandler = (environment, receiverConfig, region, clientOptions)
|
|
|
3435
3461
|
const client = new import_qstash11.Client({
|
|
3436
3462
|
...clientOptions,
|
|
3437
3463
|
baseUrl: clientEnv.QSTASH_URL,
|
|
3438
|
-
token: clientEnv.QSTASH_TOKEN
|
|
3464
|
+
token: clientEnv.QSTASH_TOKEN,
|
|
3465
|
+
// Mirror the receiver's dev-mode decision so a single QSTASH_DEV=true points
|
|
3466
|
+
// the client at the local dev server too. Without this the QStash Client
|
|
3467
|
+
// re-derives dev mode from its own process.env, which is blind to the
|
|
3468
|
+
// Cloudflare worker `env` binding that actually carries QSTASH_DEV there.
|
|
3469
|
+
// An explicit devMode in the user's client config still wins.
|
|
3470
|
+
devMode: clientOptions?.devMode ?? isQStashDevModeEnabled(environment)
|
|
3439
3471
|
});
|
|
3440
3472
|
const receiver = getReceiver(environment, receiverConfig, region);
|
|
3441
3473
|
return { client, receiver };
|
|
@@ -3485,7 +3517,9 @@ var getQStashHandlers = ({
|
|
|
3485
3517
|
client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash11.Client({
|
|
3486
3518
|
...qstashClientOption,
|
|
3487
3519
|
baseUrl: environment.QSTASH_URL,
|
|
3488
|
-
token: environment.QSTASH_TOKEN
|
|
3520
|
+
token: environment.QSTASH_TOKEN,
|
|
3521
|
+
// Mirror the receiver's dev-mode decision (see createRegionalHandler).
|
|
3522
|
+
devMode: qstashClientOption?.devMode ?? isQStashDevModeEnabled(environment)
|
|
3489
3523
|
}),
|
|
3490
3524
|
receiver: getReceiver(environment, receiverConfig)
|
|
3491
3525
|
}
|
|
@@ -3824,7 +3858,8 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3824
3858
|
const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
|
|
3825
3859
|
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
3826
3860
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
3827
|
-
const
|
|
3861
|
+
const workflowRunCreatedAtHeader = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
3862
|
+
const workflowRunCreatedAt = workflowRunCreatedAtHeader ? Number(workflowRunCreatedAtHeader) : isFirstInvocation ? Date.now() : 0;
|
|
3828
3863
|
const workflowContext = new WorkflowContext({
|
|
3829
3864
|
qstashClient: regionalClient,
|
|
3830
3865
|
workflowRunId,
|
|
@@ -3837,7 +3872,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3837
3872
|
invokeCount,
|
|
3838
3873
|
label,
|
|
3839
3874
|
retried,
|
|
3840
|
-
workflowRunCreatedAt
|
|
3875
|
+
workflowRunCreatedAt,
|
|
3841
3876
|
middlewareManager
|
|
3842
3877
|
});
|
|
3843
3878
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
@@ -3959,6 +3994,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3959
3994
|
return { handler: safeHandler };
|
|
3960
3995
|
};
|
|
3961
3996
|
var serve = (routeFunction, options) => {
|
|
3997
|
+
void (0, import_qstash12.startDevServer)();
|
|
3962
3998
|
return serveBase(
|
|
3963
3999
|
routeFunction,
|
|
3964
4000
|
{
|
|
@@ -3970,9 +4006,10 @@ var serve = (routeFunction, options) => {
|
|
|
3970
4006
|
};
|
|
3971
4007
|
|
|
3972
4008
|
// src/client/index.ts
|
|
3973
|
-
var
|
|
4009
|
+
var import_qstash14 = require("@upstash/qstash");
|
|
3974
4010
|
|
|
3975
4011
|
// src/client/dlq.ts
|
|
4012
|
+
var import_qstash13 = require("@upstash/qstash");
|
|
3976
4013
|
function buildResumeRestartHeaders(options) {
|
|
3977
4014
|
const headers = {};
|
|
3978
4015
|
if (options?.flowControl) {
|
|
@@ -4090,6 +4127,27 @@ var DLQ = class {
|
|
|
4090
4127
|
});
|
|
4091
4128
|
return response;
|
|
4092
4129
|
}
|
|
4130
|
+
/**
|
|
4131
|
+
* Cancel the in-progress failure function (failureUrl/failureFunction) call
|
|
4132
|
+
* of a workflow run.
|
|
4133
|
+
*
|
|
4134
|
+
* Fails if the run has no failure function call in progress.
|
|
4135
|
+
*
|
|
4136
|
+
* @param dlqId - The ID of the DLQ message whose failure function call will be canceled
|
|
4137
|
+
*/
|
|
4138
|
+
async cancelFailureFunction({ dlqId }) {
|
|
4139
|
+
assertNonEmptyId(dlqId, "DLQ id");
|
|
4140
|
+
try {
|
|
4141
|
+
await this.client.http.request({
|
|
4142
|
+
path: ["v2", "workflows", "dlq", "callback", dlqId],
|
|
4143
|
+
method: "DELETE",
|
|
4144
|
+
parseResponseAsJson: false
|
|
4145
|
+
});
|
|
4146
|
+
} catch (error) {
|
|
4147
|
+
if (isInstanceOf(error, import_qstash13.QstashError) && error.status === 302) return;
|
|
4148
|
+
throw error;
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4093
4151
|
/**
|
|
4094
4152
|
* Delete DLQ messages.
|
|
4095
4153
|
*
|
|
@@ -4131,7 +4189,7 @@ var DLQ = class {
|
|
|
4131
4189
|
var Client4 = class {
|
|
4132
4190
|
client;
|
|
4133
4191
|
constructor(clientConfig) {
|
|
4134
|
-
this.client = new
|
|
4192
|
+
this.client = new import_qstash14.Client(clientConfig);
|
|
4135
4193
|
}
|
|
4136
4194
|
async cancel(request) {
|
|
4137
4195
|
if (typeof request === "object" && !Array.isArray(request) && ("ids" in request || "urlStartingWith" in request)) {
|
package/index.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
assertNonEmptyId,
|
|
12
12
|
buildBulkActionQueryParameters,
|
|
13
13
|
getWorkflowRunId,
|
|
14
|
+
isInstanceOf,
|
|
14
15
|
loggingMiddleware,
|
|
15
16
|
makeGetWaitersRequest,
|
|
16
17
|
makeNotifyRequest,
|
|
@@ -22,12 +23,13 @@ import {
|
|
|
22
23
|
triggerFirstInvocation,
|
|
23
24
|
validateFlowControl,
|
|
24
25
|
validateLabel
|
|
25
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-DRHEHWMS.mjs";
|
|
26
27
|
|
|
27
28
|
// src/client/index.ts
|
|
28
|
-
import { Client as
|
|
29
|
+
import { Client as QStashClient2 } from "@upstash/qstash";
|
|
29
30
|
|
|
30
31
|
// src/client/dlq.ts
|
|
32
|
+
import { QstashError } from "@upstash/qstash";
|
|
31
33
|
function buildResumeRestartHeaders(options) {
|
|
32
34
|
const headers = {};
|
|
33
35
|
if (options?.flowControl) {
|
|
@@ -145,6 +147,27 @@ var DLQ = class {
|
|
|
145
147
|
});
|
|
146
148
|
return response;
|
|
147
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Cancel the in-progress failure function (failureUrl/failureFunction) call
|
|
152
|
+
* of a workflow run.
|
|
153
|
+
*
|
|
154
|
+
* Fails if the run has no failure function call in progress.
|
|
155
|
+
*
|
|
156
|
+
* @param dlqId - The ID of the DLQ message whose failure function call will be canceled
|
|
157
|
+
*/
|
|
158
|
+
async cancelFailureFunction({ dlqId }) {
|
|
159
|
+
assertNonEmptyId(dlqId, "DLQ id");
|
|
160
|
+
try {
|
|
161
|
+
await this.client.http.request({
|
|
162
|
+
path: ["v2", "workflows", "dlq", "callback", dlqId],
|
|
163
|
+
method: "DELETE",
|
|
164
|
+
parseResponseAsJson: false
|
|
165
|
+
});
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (isInstanceOf(error, QstashError) && error.status === 302) return;
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
148
171
|
/**
|
|
149
172
|
* Delete DLQ messages.
|
|
150
173
|
*
|
|
@@ -186,7 +209,7 @@ var DLQ = class {
|
|
|
186
209
|
var Client = class {
|
|
187
210
|
client;
|
|
188
211
|
constructor(clientConfig) {
|
|
189
|
-
this.client = new
|
|
212
|
+
this.client = new QStashClient2(clientConfig);
|
|
190
213
|
}
|
|
191
214
|
async cancel(request) {
|
|
192
215
|
if (typeof request === "object" && !Array.isArray(request) && ("ids" in request || "urlStartingWith" in request)) {
|
package/nextjs.js
CHANGED
|
@@ -29,6 +29,9 @@ __export(nextjs_exports, {
|
|
|
29
29
|
});
|
|
30
30
|
module.exports = __toCommonJS(nextjs_exports);
|
|
31
31
|
|
|
32
|
+
// src/serve/index.ts
|
|
33
|
+
var import_qstash12 = require("@upstash/qstash");
|
|
34
|
+
|
|
32
35
|
// src/client/utils.ts
|
|
33
36
|
var import_qstash2 = require("@upstash/qstash");
|
|
34
37
|
|
|
@@ -784,6 +787,10 @@ var triggerFirstInvocation = async (params) => {
|
|
|
784
787
|
workflowConfig: {
|
|
785
788
|
workflowRunId: workflowContext.workflowRunId,
|
|
786
789
|
workflowUrl: workflowContext.url,
|
|
790
|
+
// the run doesn't exist yet, so the trigger time is sent when the creation time
|
|
791
|
+
// is not known. serve and client.trigger set it on the context already, so a
|
|
792
|
+
// retry of this request carries the same value and is deduplicated.
|
|
793
|
+
workflowRunCreatedAt: workflowContext.workflowRunCreatedAt || Date.now(),
|
|
787
794
|
failureUrl,
|
|
788
795
|
retries,
|
|
789
796
|
retryDelay,
|
|
@@ -887,7 +894,10 @@ var triggerWorkflowDelete = async (workflowContext, result, cancel = false, disp
|
|
|
887
894
|
path: ["v2", "workflows", "runs", `${workflowContext.workflowRunId}?cancel=${cancel}`],
|
|
888
895
|
method: "DELETE",
|
|
889
896
|
parseResponseAsJson: false,
|
|
890
|
-
body: JSON.stringify(result)
|
|
897
|
+
body: JSON.stringify(result),
|
|
898
|
+
// with the creation time, QStash deletes the run only if it is the same run.
|
|
899
|
+
// a late delete of a previous run can't delete a new run with the same id.
|
|
900
|
+
headers: workflowContext.workflowRunCreatedAt ? { [WORKFLOW_CREATED_AT_HEADER]: workflowContext.workflowRunCreatedAt.toString() } : void 0
|
|
891
901
|
});
|
|
892
902
|
await dispatchDebug?.("onInfo", {
|
|
893
903
|
info: `Workflow run ${workflowContext.workflowRunId} deleted from QStash successfully.`
|
|
@@ -964,6 +974,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
964
974
|
const concurrentString = request.headers.get("Upstash-Workflow-Concurrent");
|
|
965
975
|
const contentType = request.headers.get("Upstash-Workflow-ContentType");
|
|
966
976
|
const invokeCount = request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER);
|
|
977
|
+
const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
967
978
|
if (!(workflowRunId && stepIdString && stepName && StepTypes.includes(stepType) && concurrentString && contentType)) {
|
|
968
979
|
throw new Error(
|
|
969
980
|
`Missing info in callback message source header: ${JSON.stringify({
|
|
@@ -981,6 +992,7 @@ ${atob(callbackMessage.body ?? "")}`
|
|
|
981
992
|
workflowConfig: {
|
|
982
993
|
workflowRunId,
|
|
983
994
|
workflowUrl,
|
|
995
|
+
workflowRunCreatedAt: Number(workflowRunCreatedAt ?? "0"),
|
|
984
996
|
telemetry
|
|
985
997
|
},
|
|
986
998
|
invokeCount: Number(invokeCount)
|
|
@@ -1128,6 +1140,7 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1128
1140
|
workflowConfig: {
|
|
1129
1141
|
workflowRunId: context.workflowRunId,
|
|
1130
1142
|
workflowUrl: context.url,
|
|
1143
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1131
1144
|
useJSONContent: false,
|
|
1132
1145
|
telemetry
|
|
1133
1146
|
},
|
|
@@ -1455,6 +1468,8 @@ var LazyWaitEventStep = class extends BaseLazyStep {
|
|
|
1455
1468
|
) : {},
|
|
1456
1469
|
// note: using WORKFLOW_ID_HEADER doesn't work, because Runid -> RunId:
|
|
1457
1470
|
"Upstash-Workflow-Runid": [context.workflowRunId],
|
|
1471
|
+
// same for WORKFLOW_CREATED_AT_HEADER, because Createdat -> CreatedAt:
|
|
1472
|
+
...context.workflowRunCreatedAt ? { "Upstash-Workflow-Createdat": [context.workflowRunCreatedAt.toString()] } : {},
|
|
1458
1473
|
[WORKFLOW_INIT_HEADER]: ["false"],
|
|
1459
1474
|
[WORKFLOW_URL_HEADER]: [context.url],
|
|
1460
1475
|
"Upstash-Workflow-CallType": ["step"]
|
|
@@ -1557,6 +1572,7 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1557
1572
|
workflowConfig: {
|
|
1558
1573
|
workflowRunId: context.workflowRunId,
|
|
1559
1574
|
workflowUrl: context.url,
|
|
1575
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1560
1576
|
telemetry,
|
|
1561
1577
|
useJSONContent: false
|
|
1562
1578
|
},
|
|
@@ -1566,6 +1582,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1566
1582
|
invokerHeaders[key] = value;
|
|
1567
1583
|
});
|
|
1568
1584
|
invokerHeaders["Upstash-Workflow-Runid"] = context.workflowRunId;
|
|
1585
|
+
if (context.workflowRunCreatedAt) {
|
|
1586
|
+
invokerHeaders["Upstash-Workflow-Createdat"] = context.workflowRunCreatedAt.toString();
|
|
1587
|
+
}
|
|
1569
1588
|
const request = {
|
|
1570
1589
|
body: stringifyBody(this.params.body),
|
|
1571
1590
|
headers: Object.fromEntries(
|
|
@@ -1594,6 +1613,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1594
1613
|
workflowConfig: {
|
|
1595
1614
|
workflowRunId: getWorkflowRunId(workflowRunId),
|
|
1596
1615
|
workflowUrl: newUrl,
|
|
1616
|
+
// the invoked run doesn't exist yet, so the creation time of the invoker run is sent.
|
|
1617
|
+
// it separates the invocations made by different runs of the invoker.
|
|
1618
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1597
1619
|
retries,
|
|
1598
1620
|
retryDelay,
|
|
1599
1621
|
telemetry,
|
|
@@ -1758,6 +1780,9 @@ var WorkflowHeaders = class {
|
|
|
1758
1780
|
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
1759
1781
|
...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
|
|
1760
1782
|
};
|
|
1783
|
+
if (this.workflowConfig.workflowRunCreatedAt) {
|
|
1784
|
+
this.headers.rawHeaders[WORKFLOW_CREATED_AT_HEADER] = this.workflowConfig.workflowRunCreatedAt.toString();
|
|
1785
|
+
}
|
|
1761
1786
|
if (this.stepInfo?.lazyStep.stepType !== "Call") {
|
|
1762
1787
|
this.headers.rawHeaders[`Upstash-Forward-${WORKFLOW_PROTOCOL_VERSION_HEADER}`] = WORKFLOW_PROTOCOL_VERSION;
|
|
1763
1788
|
}
|
|
@@ -1917,6 +1942,7 @@ var submitParallelSteps = async ({
|
|
|
1917
1942
|
workflowConfig: {
|
|
1918
1943
|
workflowRunId: context.workflowRunId,
|
|
1919
1944
|
workflowUrl: context.url,
|
|
1945
|
+
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
1920
1946
|
telemetry
|
|
1921
1947
|
},
|
|
1922
1948
|
invokeCount
|
|
@@ -3418,7 +3444,13 @@ var createRegionalHandler = (environment, receiverConfig, region, clientOptions)
|
|
|
3418
3444
|
const client = new import_qstash11.Client({
|
|
3419
3445
|
...clientOptions,
|
|
3420
3446
|
baseUrl: clientEnv.QSTASH_URL,
|
|
3421
|
-
token: clientEnv.QSTASH_TOKEN
|
|
3447
|
+
token: clientEnv.QSTASH_TOKEN,
|
|
3448
|
+
// Mirror the receiver's dev-mode decision so a single QSTASH_DEV=true points
|
|
3449
|
+
// the client at the local dev server too. Without this the QStash Client
|
|
3450
|
+
// re-derives dev mode from its own process.env, which is blind to the
|
|
3451
|
+
// Cloudflare worker `env` binding that actually carries QSTASH_DEV there.
|
|
3452
|
+
// An explicit devMode in the user's client config still wins.
|
|
3453
|
+
devMode: clientOptions?.devMode ?? isQStashDevModeEnabled(environment)
|
|
3422
3454
|
});
|
|
3423
3455
|
const receiver = getReceiver(environment, receiverConfig, region);
|
|
3424
3456
|
return { client, receiver };
|
|
@@ -3468,7 +3500,9 @@ var getQStashHandlers = ({
|
|
|
3468
3500
|
client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash11.Client({
|
|
3469
3501
|
...qstashClientOption,
|
|
3470
3502
|
baseUrl: environment.QSTASH_URL,
|
|
3471
|
-
token: environment.QSTASH_TOKEN
|
|
3503
|
+
token: environment.QSTASH_TOKEN,
|
|
3504
|
+
// Mirror the receiver's dev-mode decision (see createRegionalHandler).
|
|
3505
|
+
devMode: qstashClientOption?.devMode ?? isQStashDevModeEnabled(environment)
|
|
3472
3506
|
}),
|
|
3473
3507
|
receiver: getReceiver(environment, receiverConfig)
|
|
3474
3508
|
}
|
|
@@ -3807,7 +3841,8 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3807
3841
|
const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
|
|
3808
3842
|
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
3809
3843
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
3810
|
-
const
|
|
3844
|
+
const workflowRunCreatedAtHeader = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
3845
|
+
const workflowRunCreatedAt = workflowRunCreatedAtHeader ? Number(workflowRunCreatedAtHeader) : isFirstInvocation ? Date.now() : 0;
|
|
3811
3846
|
const workflowContext = new WorkflowContext({
|
|
3812
3847
|
qstashClient: regionalClient,
|
|
3813
3848
|
workflowRunId,
|
|
@@ -3820,7 +3855,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3820
3855
|
invokeCount,
|
|
3821
3856
|
label,
|
|
3822
3857
|
retried,
|
|
3823
|
-
workflowRunCreatedAt
|
|
3858
|
+
workflowRunCreatedAt,
|
|
3824
3859
|
middlewareManager
|
|
3825
3860
|
});
|
|
3826
3861
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
@@ -3943,6 +3978,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3943
3978
|
};
|
|
3944
3979
|
|
|
3945
3980
|
// platforms/nextjs.ts
|
|
3981
|
+
var import_qstash13 = require("@upstash/qstash");
|
|
3946
3982
|
var appTelemetry = {
|
|
3947
3983
|
sdk: SDK_TELEMETRY,
|
|
3948
3984
|
framework: "nextjs",
|
|
@@ -3954,6 +3990,7 @@ var pagesTelemetry = {
|
|
|
3954
3990
|
runtime: process.versions.bun ? `bun@${process.versions.bun}/node@${process.version}` : `node@${process.version}`
|
|
3955
3991
|
};
|
|
3956
3992
|
var serve = (routeFunction, options) => {
|
|
3993
|
+
void (0, import_qstash13.startDevServer)();
|
|
3957
3994
|
const { handler: serveHandler } = serveBase(
|
|
3958
3995
|
routeFunction,
|
|
3959
3996
|
appTelemetry,
|
|
@@ -3986,6 +4023,7 @@ var serveMany = (workflows, options) => {
|
|
|
3986
4023
|
};
|
|
3987
4024
|
};
|
|
3988
4025
|
var servePagesRouter = (routeFunction, options) => {
|
|
4026
|
+
void (0, import_qstash13.startDevServer)();
|
|
3989
4027
|
const { handler: serveHandler } = serveBase(routeFunction, pagesTelemetry, options, void 0);
|
|
3990
4028
|
const handler = async (request_, res) => {
|
|
3991
4029
|
if (request_.method?.toUpperCase() !== "POST") {
|
package/nextjs.mjs
CHANGED
|
@@ -2,9 +2,10 @@ import {
|
|
|
2
2
|
SDK_TELEMETRY,
|
|
3
3
|
serveBase,
|
|
4
4
|
serveManyBase
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-DRHEHWMS.mjs";
|
|
6
6
|
|
|
7
7
|
// platforms/nextjs.ts
|
|
8
|
+
import { startDevServer } from "@upstash/qstash";
|
|
8
9
|
var appTelemetry = {
|
|
9
10
|
sdk: SDK_TELEMETRY,
|
|
10
11
|
framework: "nextjs",
|
|
@@ -16,6 +17,7 @@ var pagesTelemetry = {
|
|
|
16
17
|
runtime: process.versions.bun ? `bun@${process.versions.bun}/node@${process.version}` : `node@${process.version}`
|
|
17
18
|
};
|
|
18
19
|
var serve = (routeFunction, options) => {
|
|
20
|
+
void startDevServer();
|
|
19
21
|
const { handler: serveHandler } = serveBase(
|
|
20
22
|
routeFunction,
|
|
21
23
|
appTelemetry,
|
|
@@ -48,6 +50,7 @@ var serveMany = (workflows, options) => {
|
|
|
48
50
|
};
|
|
49
51
|
};
|
|
50
52
|
var servePagesRouter = (routeFunction, options) => {
|
|
53
|
+
void startDevServer();
|
|
51
54
|
const { handler: serveHandler } = serveBase(routeFunction, pagesTelemetry, options, void 0);
|
|
52
55
|
const handler = async (request_, res) => {
|
|
53
56
|
if (request_.method?.toUpperCase() !== "POST") {
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@upstash/workflow","version":"1.
|
|
1
|
+
{"name":"@upstash/workflow","version":"1.4.0","description":"Durable, Reliable and Performant Serverless Functions","main":"./index.js","module":"./index.mjs","types":"./index.d.ts","files":["./*"],"exports":{".":{"import":"./index.mjs","require":"./index.js"},"./dist/nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./h3":{"import":"./h3.mjs","require":"./h3.js"},"./svelte":{"import":"./svelte.mjs","require":"./svelte.js"},"./solidjs":{"import":"./solidjs.mjs","require":"./solidjs.js"},"./workflow":{"import":"./workflow.mjs","require":"./workflow.js"},"./hono":{"import":"./hono.mjs","require":"./hono.js"},"./cloudflare":{"import":"./cloudflare.mjs","require":"./cloudflare.js"},"./astro":{"import":"./astro.mjs","require":"./astro.js"},"./express":{"import":"./express.mjs","require":"./express.js"},"./tanstack":{"import":"./tanstack.mjs","require":"./tanstack.js"},"./react-router":{"import":"./react-router.mjs","require":"./react-router.js"}},"scripts":{"build":"tsup && cp README.md ./dist/ && cp package.json ./dist/ && cp LICENSE ./dist/","test":"bun test src","fmt":"prettier --write .","lint":"tsc && eslint \"{src,platforms}/**/*.{js,ts,tsx}\" --quiet --fix","check-exports":"bun run build && cd dist && attw -P"},"repository":{"type":"git","url":"git@github.com:upstash/workflow-js.git"},"keywords":["upstash","qstash","workflow","serverless"],"author":"Cahid Arda Oz","license":"MIT","bugs":{"url":"https://github.com/upstash/workflow-ts/issues"},"homepage":"https://github.com/upstash/workflow-ts#readme","devDependencies":{"@commitlint/cli":"^19.5.0","@commitlint/config-conventional":"^19.5.0","@eslint/js":"^9.11.1","@solidjs/start":"^1.0.8","@sveltejs/kit":"^2.6.1","@types/bun":"^1.1.10","@types/express":"^5.0.6","astro":"^4.16.7","eslint":"^9.11.1","eslint-plugin-unicorn":"^55.0.0","express":"^5.1.0","globals":"^15.10.0","h3":"^1.12.0","hono":"^4.6.20","husky":"^9.1.6","next":"^14.2.14","prettier":"3.3.3","tsup":"^8.3.0","typescript":"^5.7.2","typescript-eslint":"^8.18.0"},"dependencies":{"@upstash/qstash":"^2.11.3"},"directories":{"example":"examples"},"peerDependencies":{"zod":"^3.25.0 || ^4.0.0"}}
|