@upstash/workflow 1.3.2 → 1.4.0-rc
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.d.mts +3 -2
- package/astro.d.ts +3 -2
- package/astro.js +595 -140
- package/astro.mjs +3 -1
- package/{chunk-ICDNN4UW.mjs → chunk-UH6O5IJY.mjs} +751 -281
- package/cloudflare.d.mts +3 -2
- package/cloudflare.d.ts +3 -2
- package/cloudflare.js +595 -140
- package/cloudflare.mjs +3 -1
- package/express.d.mts +3 -2
- package/express.d.ts +3 -2
- package/express.js +596 -140
- package/express.mjs +4 -1
- package/h3.d.mts +3 -2
- package/h3.d.ts +3 -2
- package/h3.js +595 -140
- package/h3.mjs +3 -1
- package/hono.d.mts +3 -2
- package/hono.d.ts +3 -2
- package/hono.js +595 -140
- package/hono.mjs +3 -1
- package/index.d.mts +64 -17
- package/index.d.ts +64 -17
- package/index.js +652 -150
- package/index.mjs +49 -11
- package/nextjs.d.mts +3 -2
- package/nextjs.d.ts +3 -2
- package/nextjs.js +596 -140
- package/nextjs.mjs +4 -1
- package/package.json +1 -1
- package/react-router.d.mts +3 -2
- package/react-router.d.ts +3 -2
- package/react-router.js +595 -140
- package/react-router.mjs +3 -1
- package/{serve-many-iJF1IUXk.d.ts → serve-many-BCtZg31b.d.ts} +1 -1
- package/{serve-many-CG3BFvO3.d.mts → serve-many-CbIV7145.d.mts} +1 -1
- package/solidjs.d.mts +2 -1
- package/solidjs.d.ts +2 -1
- package/solidjs.js +595 -140
- package/solidjs.mjs +3 -1
- package/svelte.d.mts +3 -2
- package/svelte.d.ts +3 -2
- package/svelte.js +595 -140
- package/svelte.mjs +3 -1
- package/tanstack.d.mts +3 -2
- package/tanstack.d.ts +3 -2
- package/tanstack.js +595 -140
- package/tanstack.mjs +3 -1
- package/{types-CekOpKvz.d.ts → types-ffLPVG5_.d.mts} +248 -8
- package/{types-CekOpKvz.d.mts → types-ffLPVG5_.d.ts} +248 -8
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
|
|
|
@@ -149,8 +152,20 @@ function isInstanceOf(v, ctor) {
|
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
// src/client/utils.ts
|
|
155
|
+
var assertNonEmptyId = (id, label = "id") => {
|
|
156
|
+
if (!id) {
|
|
157
|
+
throw new import_qstash2.QstashError(`${label} cannot be empty`);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
var toNonEmptyIdArray = (request, label = "id") => {
|
|
161
|
+
const ids = typeof request === "string" ? [request] : request;
|
|
162
|
+
for (const id of ids) assertNonEmptyId(id, label);
|
|
163
|
+
return ids;
|
|
164
|
+
};
|
|
152
165
|
var makeNotifyRequest = async (requester, eventId, eventData, workflowRunId) => {
|
|
153
|
-
|
|
166
|
+
assertNonEmptyId(eventId, "Event id");
|
|
167
|
+
if (workflowRunId !== void 0) assertNonEmptyId(workflowRunId, "Workflow run id");
|
|
168
|
+
const path = workflowRunId === void 0 ? ["v2", "notify", eventId] : ["v2", "notify", workflowRunId, eventId];
|
|
154
169
|
const result = await requester.request({
|
|
155
170
|
path,
|
|
156
171
|
method: "POST",
|
|
@@ -159,6 +174,7 @@ var makeNotifyRequest = async (requester, eventId, eventData, workflowRunId) =>
|
|
|
159
174
|
return result;
|
|
160
175
|
};
|
|
161
176
|
var makeGetWaitersRequest = async (requester, eventId) => {
|
|
177
|
+
assertNonEmptyId(eventId, "Event id");
|
|
162
178
|
const result = await requester.request({
|
|
163
179
|
path: ["v2", "waiters", eventId],
|
|
164
180
|
method: "GET"
|
|
@@ -245,6 +261,13 @@ function buildBulkActionQueryParameters(request, options) {
|
|
|
245
261
|
"No filter provided. Use { filter: { ... } } with at least one filter field, or { all: true }."
|
|
246
262
|
);
|
|
247
263
|
}
|
|
264
|
+
for (const [field, value] of Object.entries(filter)) {
|
|
265
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
266
|
+
throw new import_qstash2.QstashError(
|
|
267
|
+
`Empty array provided for filter field '${field}'. If you intend to target all records, use { all: true } explicitly.`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
248
271
|
if (options?.translateWorkflowUrl) {
|
|
249
272
|
const { workflowUrlStartingWith, workflowUrl, ...rest } = filter;
|
|
250
273
|
if (workflowUrlStartingWith && workflowUrl) {
|
|
@@ -281,6 +304,14 @@ var WORKFLOW_CREATED_AT_HEADER = "Upstash-Workflow-CreatedAt";
|
|
|
281
304
|
var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
|
|
282
305
|
var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
|
|
283
306
|
var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
|
|
307
|
+
var WORKFLOW_FEATURE_SET = "LazyFetch,InitialBody,WF_DetectTrigger,WF_TriggerOnConfig";
|
|
308
|
+
var WORKFLOW_STEP_CONFIG_CALL_TYPE = "stepConfig";
|
|
309
|
+
var WORKFLOW_STEP_CONFIG_HEADER = "Upstash-Workflow-Step-Config";
|
|
310
|
+
var FLOW_CONTROL_KEY_HEADER = "Upstash-Flow-Control-Key";
|
|
311
|
+
var FLOW_CONTROL_VALUE_HEADER = "Upstash-Flow-Control-Value";
|
|
312
|
+
var RETRIES_HEADER = "Upstash-Retries";
|
|
313
|
+
var RETRY_DELAY_HEADER = "Upstash-Retry-Delay";
|
|
314
|
+
var WORKFLOW_STEP_CONFIG_FEATURE = "WF_StepConfig";
|
|
284
315
|
var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
|
|
285
316
|
var WORKFLOW_RETRIED_HEADER = "Upstash-Retried";
|
|
286
317
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
@@ -298,102 +329,6 @@ var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
|
298
329
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
299
330
|
var TELEMETRY_HEADER_RUNTIME = "Upstash-Telemetry-Runtime";
|
|
300
331
|
|
|
301
|
-
// src/context/auto-executor.ts
|
|
302
|
-
var import_qstash5 = require("@upstash/qstash");
|
|
303
|
-
|
|
304
|
-
// src/qstash/headers.ts
|
|
305
|
-
var import_qstash4 = require("@upstash/qstash");
|
|
306
|
-
|
|
307
|
-
// src/utils.ts
|
|
308
|
-
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
309
|
-
var NANOID_LENGTH = 21;
|
|
310
|
-
var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
|
|
311
|
-
function validateLabel(label) {
|
|
312
|
-
if (label === void 0) return;
|
|
313
|
-
const labels = Array.isArray(label) ? label : [label];
|
|
314
|
-
if (labels.length === 0) {
|
|
315
|
-
throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
|
|
316
|
-
}
|
|
317
|
-
for (const value of labels) {
|
|
318
|
-
if (!RESOURCE_NAME_PATTERN.test(value)) {
|
|
319
|
-
throw new WorkflowNonRetryableError(
|
|
320
|
-
`Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
function serializeLabel(label) {
|
|
326
|
-
return Array.isArray(label) ? label.join(",") : label;
|
|
327
|
-
}
|
|
328
|
-
function validateFlowControl(flowControl) {
|
|
329
|
-
if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
|
|
330
|
-
throw new WorkflowNonRetryableError(
|
|
331
|
-
`Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
function getRandomInt() {
|
|
336
|
-
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
337
|
-
}
|
|
338
|
-
function nanoid(length = NANOID_LENGTH) {
|
|
339
|
-
return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
340
|
-
}
|
|
341
|
-
function getWorkflowRunId(id) {
|
|
342
|
-
return `wfr_${id ?? nanoid()}`;
|
|
343
|
-
}
|
|
344
|
-
function decodeBase64(base64) {
|
|
345
|
-
const binString = atob(base64);
|
|
346
|
-
try {
|
|
347
|
-
const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
|
348
|
-
return new TextDecoder().decode(intArray);
|
|
349
|
-
} catch (error) {
|
|
350
|
-
console.warn(
|
|
351
|
-
`Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
|
|
352
|
-
);
|
|
353
|
-
return binString;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
function getUserIdFromToken(qstashClient) {
|
|
357
|
-
try {
|
|
358
|
-
const token = qstashClient.token;
|
|
359
|
-
const decodedToken = decodeBase64(token);
|
|
360
|
-
const tokenPayload = JSON.parse(decodedToken);
|
|
361
|
-
const userId = tokenPayload.UserID;
|
|
362
|
-
if (!userId) {
|
|
363
|
-
throw new WorkflowError("QStash token payload does not contain userId");
|
|
364
|
-
}
|
|
365
|
-
return userId;
|
|
366
|
-
} catch (error) {
|
|
367
|
-
throw new WorkflowError(
|
|
368
|
-
`Failed to decode QStash token while running create webhook step: ${error.message}`
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
function getQStashUrl(qstashClient) {
|
|
373
|
-
try {
|
|
374
|
-
const requester = qstashClient.http;
|
|
375
|
-
const baseUrl = requester.baseUrl;
|
|
376
|
-
if (!baseUrl) {
|
|
377
|
-
throw new WorkflowError("QStash client does not have a baseUrl");
|
|
378
|
-
}
|
|
379
|
-
return baseUrl;
|
|
380
|
-
} catch (error) {
|
|
381
|
-
throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
function getEventId() {
|
|
385
|
-
return `evt_${nanoid(15)}`;
|
|
386
|
-
}
|
|
387
|
-
function stringifyBody(body) {
|
|
388
|
-
if (body === void 0) {
|
|
389
|
-
return void 0;
|
|
390
|
-
}
|
|
391
|
-
if (typeof body === "string") {
|
|
392
|
-
return body;
|
|
393
|
-
}
|
|
394
|
-
return JSON.stringify(body);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
332
|
// node_modules/neverthrow/dist/index.es.js
|
|
398
333
|
var defaultErrorConfig = {
|
|
399
334
|
withStackTrace: false
|
|
@@ -809,6 +744,102 @@ var Err = class {
|
|
|
809
744
|
};
|
|
810
745
|
var fromThrowable = Result.fromThrowable;
|
|
811
746
|
|
|
747
|
+
// src/context/auto-executor.ts
|
|
748
|
+
var import_qstash5 = require("@upstash/qstash");
|
|
749
|
+
|
|
750
|
+
// src/qstash/headers.ts
|
|
751
|
+
var import_qstash4 = require("@upstash/qstash");
|
|
752
|
+
|
|
753
|
+
// src/utils.ts
|
|
754
|
+
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
755
|
+
var NANOID_LENGTH = 21;
|
|
756
|
+
var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
|
|
757
|
+
function validateLabel(label) {
|
|
758
|
+
if (label === void 0) return;
|
|
759
|
+
const labels = Array.isArray(label) ? label : [label];
|
|
760
|
+
if (labels.length === 0) {
|
|
761
|
+
throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
|
|
762
|
+
}
|
|
763
|
+
for (const value of labels) {
|
|
764
|
+
if (!RESOURCE_NAME_PATTERN.test(value)) {
|
|
765
|
+
throw new WorkflowNonRetryableError(
|
|
766
|
+
`Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
function serializeLabel(label) {
|
|
772
|
+
return Array.isArray(label) ? label.join(",") : label;
|
|
773
|
+
}
|
|
774
|
+
function validateFlowControl(flowControl) {
|
|
775
|
+
if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
|
|
776
|
+
throw new WorkflowNonRetryableError(
|
|
777
|
+
`Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
function getRandomInt() {
|
|
782
|
+
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
783
|
+
}
|
|
784
|
+
function nanoid(length = NANOID_LENGTH) {
|
|
785
|
+
return Array.from({ length }).map(() => NANOID_CHARS[getRandomInt()]).join("");
|
|
786
|
+
}
|
|
787
|
+
function getWorkflowRunId(id) {
|
|
788
|
+
return `wfr_${id ?? nanoid()}`;
|
|
789
|
+
}
|
|
790
|
+
function decodeBase64(base64) {
|
|
791
|
+
const binString = atob(base64);
|
|
792
|
+
try {
|
|
793
|
+
const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
|
794
|
+
return new TextDecoder().decode(intArray);
|
|
795
|
+
} catch (error) {
|
|
796
|
+
console.warn(
|
|
797
|
+
`Upstash Qstash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
|
|
798
|
+
);
|
|
799
|
+
return binString;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
function getUserIdFromToken(qstashClient) {
|
|
803
|
+
try {
|
|
804
|
+
const token = qstashClient.token;
|
|
805
|
+
const decodedToken = decodeBase64(token);
|
|
806
|
+
const tokenPayload = JSON.parse(decodedToken);
|
|
807
|
+
const userId = tokenPayload.UserID;
|
|
808
|
+
if (!userId) {
|
|
809
|
+
throw new WorkflowError("QStash token payload does not contain userId");
|
|
810
|
+
}
|
|
811
|
+
return userId;
|
|
812
|
+
} catch (error) {
|
|
813
|
+
throw new WorkflowError(
|
|
814
|
+
`Failed to decode QStash token while running create webhook step: ${error.message}`
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
function getQStashUrl(qstashClient) {
|
|
819
|
+
try {
|
|
820
|
+
const requester = qstashClient.http;
|
|
821
|
+
const baseUrl = requester.baseUrl;
|
|
822
|
+
if (!baseUrl) {
|
|
823
|
+
throw new WorkflowError("QStash client does not have a baseUrl");
|
|
824
|
+
}
|
|
825
|
+
return baseUrl;
|
|
826
|
+
} catch (error) {
|
|
827
|
+
throw new WorkflowError(`Failed to get QStash URL from client: ${error.message}`);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
function getEventId() {
|
|
831
|
+
return `evt_${nanoid(15)}`;
|
|
832
|
+
}
|
|
833
|
+
function stringifyBody(body) {
|
|
834
|
+
if (body === void 0) {
|
|
835
|
+
return void 0;
|
|
836
|
+
}
|
|
837
|
+
if (typeof body === "string") {
|
|
838
|
+
return body;
|
|
839
|
+
}
|
|
840
|
+
return JSON.stringify(body);
|
|
841
|
+
}
|
|
842
|
+
|
|
812
843
|
// src/types.ts
|
|
813
844
|
var StepTypes = [
|
|
814
845
|
"Initial",
|
|
@@ -914,6 +945,10 @@ var triggerFirstInvocation = async (params) => {
|
|
|
914
945
|
return err(error_);
|
|
915
946
|
}
|
|
916
947
|
};
|
|
948
|
+
var flushPendingStep = async (workflowContext) => {
|
|
949
|
+
const { executor } = workflowContext;
|
|
950
|
+
return await executor.submitPendingStep();
|
|
951
|
+
};
|
|
917
952
|
var triggerRouteFunction = async ({
|
|
918
953
|
onCleanup,
|
|
919
954
|
onStep,
|
|
@@ -1121,8 +1156,29 @@ If you want to disable QStash Verification, you should clear env variables QSTAS
|
|
|
1121
1156
|
var BaseLazyStep = class _BaseLazyStep {
|
|
1122
1157
|
stepName;
|
|
1123
1158
|
context;
|
|
1124
|
-
|
|
1159
|
+
/**
|
|
1160
|
+
* step-level settings (flow control, retries etc.) which override the
|
|
1161
|
+
* settings the workflow run was triggered with, for this step only.
|
|
1162
|
+
*/
|
|
1163
|
+
stepSettings;
|
|
1164
|
+
/**
|
|
1165
|
+
* whether this step's result can be submitted after the route function
|
|
1166
|
+
* has moved on, instead of right away.
|
|
1167
|
+
*
|
|
1168
|
+
* Enabled for steps whose `getResultStep` produces the final `out` —
|
|
1169
|
+
* which is not the same as "needs no server round trip":
|
|
1170
|
+
* `LazyCreateWebhookStep` builds its result in `getBody` and returns
|
|
1171
|
+
* `out: undefined` from `getResultStep`, so deferring it would hand
|
|
1172
|
+
* `undefined` to the route function.
|
|
1173
|
+
*
|
|
1174
|
+
* Deferring lets the route function continue and reveal the next step,
|
|
1175
|
+
* so that step's settings can ride on this step's submission instead
|
|
1176
|
+
* of needing a step config request of their own.
|
|
1177
|
+
*/
|
|
1178
|
+
supportsDeferredSubmission = false;
|
|
1179
|
+
constructor(context, stepName, stepSettings) {
|
|
1125
1180
|
this.context = context;
|
|
1181
|
+
this.stepSettings = stepSettings;
|
|
1126
1182
|
if (!stepName) {
|
|
1127
1183
|
throw new WorkflowError(
|
|
1128
1184
|
"A workflow step name cannot be undefined or an empty string. Please provide a name for your workflow step."
|
|
@@ -1186,7 +1242,13 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1186
1242
|
step.out = JSON.stringify(step.out);
|
|
1187
1243
|
return JSON.stringify(step);
|
|
1188
1244
|
}
|
|
1189
|
-
getHeaders({
|
|
1245
|
+
getHeaders({
|
|
1246
|
+
context,
|
|
1247
|
+
telemetry,
|
|
1248
|
+
invokeCount,
|
|
1249
|
+
step,
|
|
1250
|
+
stepSettings
|
|
1251
|
+
}) {
|
|
1190
1252
|
return getHeaders({
|
|
1191
1253
|
initHeaderValue: "false",
|
|
1192
1254
|
workflowConfig: {
|
|
@@ -1199,7 +1261,8 @@ var BaseLazyStep = class _BaseLazyStep {
|
|
|
1199
1261
|
stepInfo: {
|
|
1200
1262
|
step,
|
|
1201
1263
|
lazyStep: this
|
|
1202
|
-
}
|
|
1264
|
+
},
|
|
1265
|
+
stepSettings
|
|
1203
1266
|
});
|
|
1204
1267
|
}
|
|
1205
1268
|
async submitStep({ context, body, headers }) {
|
|
@@ -1217,8 +1280,9 @@ var LazyFunctionStep = class extends BaseLazyStep {
|
|
|
1217
1280
|
stepFunction;
|
|
1218
1281
|
stepType = "Run";
|
|
1219
1282
|
allowUndefinedOut = true;
|
|
1220
|
-
|
|
1221
|
-
|
|
1283
|
+
supportsDeferredSubmission = true;
|
|
1284
|
+
constructor(context, stepName, stepFunction, stepSettings) {
|
|
1285
|
+
super(context, stepName, stepSettings);
|
|
1222
1286
|
this.stepFunction = stepFunction;
|
|
1223
1287
|
}
|
|
1224
1288
|
getPlanStep(concurrent, targetStep) {
|
|
@@ -1248,6 +1312,7 @@ var LazySleepStep = class extends BaseLazyStep {
|
|
|
1248
1312
|
sleep;
|
|
1249
1313
|
stepType = "SleepFor";
|
|
1250
1314
|
allowUndefinedOut = true;
|
|
1315
|
+
supportsDeferredSubmission = true;
|
|
1251
1316
|
constructor(context, stepName, sleep) {
|
|
1252
1317
|
super(context, stepName);
|
|
1253
1318
|
this.sleep = sleep;
|
|
@@ -1287,6 +1352,7 @@ var LazySleepUntilStep = class extends BaseLazyStep {
|
|
|
1287
1352
|
sleepUntil;
|
|
1288
1353
|
stepType = "SleepUntil";
|
|
1289
1354
|
allowUndefinedOut = true;
|
|
1355
|
+
supportsDeferredSubmission = true;
|
|
1290
1356
|
constructor(context, stepName, sleepUntil) {
|
|
1291
1357
|
super(context, stepName);
|
|
1292
1358
|
this.sleepUntil = sleepUntil;
|
|
@@ -1779,6 +1845,7 @@ var WorkflowHeaders = class {
|
|
|
1779
1845
|
invokeCount;
|
|
1780
1846
|
initHeaderValue;
|
|
1781
1847
|
stepInfo;
|
|
1848
|
+
stepSettings;
|
|
1782
1849
|
headers;
|
|
1783
1850
|
/**
|
|
1784
1851
|
* @param params workflow header parameters
|
|
@@ -1788,17 +1855,20 @@ var WorkflowHeaders = class {
|
|
|
1788
1855
|
workflowConfig,
|
|
1789
1856
|
invokeCount,
|
|
1790
1857
|
initHeaderValue,
|
|
1791
|
-
stepInfo
|
|
1858
|
+
stepInfo,
|
|
1859
|
+
stepSettings
|
|
1792
1860
|
}) {
|
|
1793
1861
|
this.userHeaders = userHeaders;
|
|
1794
1862
|
this.workflowConfig = workflowConfig;
|
|
1795
1863
|
this.invokeCount = invokeCount;
|
|
1796
1864
|
this.initHeaderValue = initHeaderValue;
|
|
1797
1865
|
this.stepInfo = stepInfo;
|
|
1866
|
+
this.stepSettings = stepSettings;
|
|
1798
1867
|
this.headers = {
|
|
1799
1868
|
rawHeaders: {},
|
|
1800
1869
|
workflowHeaders: {},
|
|
1801
|
-
failureHeaders: {}
|
|
1870
|
+
failureHeaders: {},
|
|
1871
|
+
stepSettingsHeaders: {}
|
|
1802
1872
|
};
|
|
1803
1873
|
}
|
|
1804
1874
|
getHeaders() {
|
|
@@ -1809,6 +1879,7 @@ var WorkflowHeaders = class {
|
|
|
1809
1879
|
this.addUserHeaders();
|
|
1810
1880
|
this.addInvokeCount();
|
|
1811
1881
|
this.addFailureUrl();
|
|
1882
|
+
this.addStepSettings();
|
|
1812
1883
|
const contentType = this.addContentType();
|
|
1813
1884
|
return this.prefixHeaders(contentType);
|
|
1814
1885
|
}
|
|
@@ -1818,7 +1889,7 @@ var WorkflowHeaders = class {
|
|
|
1818
1889
|
[WORKFLOW_INIT_HEADER]: this.initHeaderValue,
|
|
1819
1890
|
[WORKFLOW_ID_HEADER]: this.workflowConfig.workflowRunId,
|
|
1820
1891
|
[WORKFLOW_URL_HEADER]: this.workflowConfig.workflowUrl,
|
|
1821
|
-
[WORKFLOW_FEATURE_HEADER]:
|
|
1892
|
+
[WORKFLOW_FEATURE_HEADER]: WORKFLOW_FEATURE_SET,
|
|
1822
1893
|
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
1823
1894
|
...this.workflowConfig.telemetry ? getTelemetryHeaders(this.workflowConfig.telemetry) : {}
|
|
1824
1895
|
};
|
|
@@ -1896,7 +1967,7 @@ var WorkflowHeaders = class {
|
|
|
1896
1967
|
this.headers.failureHeaders["Workflow-Init"] = "false";
|
|
1897
1968
|
this.headers.failureHeaders["Workflow-Url"] = this.workflowConfig.workflowUrl;
|
|
1898
1969
|
this.headers.failureHeaders["Workflow-Calltype"] = "failureCall";
|
|
1899
|
-
this.headers.failureHeaders["Feature-Set"] =
|
|
1970
|
+
this.headers.failureHeaders["Feature-Set"] = WORKFLOW_FEATURE_SET;
|
|
1900
1971
|
if (this.workflowConfig.retries !== void 0 && this.workflowConfig.retries !== DEFAULT_RETRIES) {
|
|
1901
1972
|
this.headers.failureHeaders["Retries"] = this.workflowConfig.retries.toString();
|
|
1902
1973
|
}
|
|
@@ -1904,6 +1975,42 @@ var WorkflowHeaders = class {
|
|
|
1904
1975
|
this.headers.failureHeaders["Retry-Delay"] = this.workflowConfig.retryDelay.toString();
|
|
1905
1976
|
}
|
|
1906
1977
|
}
|
|
1978
|
+
/**
|
|
1979
|
+
* Applies the step-level settings of a step to the message.
|
|
1980
|
+
*
|
|
1981
|
+
* These headers configure the message itself, so that QStash uses them
|
|
1982
|
+
* instead of the settings the run was triggered with. The delivery of
|
|
1983
|
+
* the message is what executes the step, which is how a step's
|
|
1984
|
+
* settings reach it.
|
|
1985
|
+
*
|
|
1986
|
+
* When any setting is present the feature set is extended with
|
|
1987
|
+
* `WF_StepConfig`, which is what tells QStash to keep them. QStash
|
|
1988
|
+
* reports that back on the delivery as `Upstash-Workflow-Step-Config`.
|
|
1989
|
+
*/
|
|
1990
|
+
addStepSettings() {
|
|
1991
|
+
if (!this.stepSettings) {
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
const headers = {};
|
|
1995
|
+
if (this.stepSettings.flowControl) {
|
|
1996
|
+
const { flowControlKey, flowControlValue } = prepareFlowControl(
|
|
1997
|
+
this.stepSettings.flowControl
|
|
1998
|
+
);
|
|
1999
|
+
headers[FLOW_CONTROL_KEY_HEADER] = flowControlKey;
|
|
2000
|
+
headers[FLOW_CONTROL_VALUE_HEADER] = flowControlValue;
|
|
2001
|
+
}
|
|
2002
|
+
if (this.stepSettings.retries !== void 0) {
|
|
2003
|
+
headers[RETRIES_HEADER] = this.stepSettings.retries.toString();
|
|
2004
|
+
}
|
|
2005
|
+
if (this.stepSettings.retryDelay) {
|
|
2006
|
+
headers[RETRY_DELAY_HEADER] = this.stepSettings.retryDelay;
|
|
2007
|
+
}
|
|
2008
|
+
if (Object.keys(headers).length === 0) {
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
headers[WORKFLOW_FEATURE_HEADER] = `${WORKFLOW_FEATURE_SET},${WORKFLOW_STEP_CONFIG_FEATURE}`;
|
|
2012
|
+
this.headers.stepSettingsHeaders = headers;
|
|
2013
|
+
}
|
|
1907
2014
|
addContentType() {
|
|
1908
2015
|
if (this.workflowConfig.useJSONContent) {
|
|
1909
2016
|
this.headers.rawHeaders["content-type"] = "application/json";
|
|
@@ -1917,14 +2024,17 @@ var WorkflowHeaders = class {
|
|
|
1917
2024
|
return contentType;
|
|
1918
2025
|
}
|
|
1919
2026
|
prefixHeaders(contentType) {
|
|
1920
|
-
const { rawHeaders, workflowHeaders, failureHeaders } = this.headers;
|
|
2027
|
+
const { rawHeaders, workflowHeaders, failureHeaders, stepSettingsHeaders } = this.headers;
|
|
1921
2028
|
const isCall = this.stepInfo?.lazyStep.stepType === "Call";
|
|
1922
2029
|
return {
|
|
1923
2030
|
headers: {
|
|
1924
2031
|
...rawHeaders,
|
|
1925
2032
|
...addPrefixToHeaders(workflowHeaders, isCall ? "Upstash-Callback-" : "Upstash-"),
|
|
1926
2033
|
...addPrefixToHeaders(failureHeaders, "Upstash-Failure-Callback-"),
|
|
1927
|
-
...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {}
|
|
2034
|
+
...isCall ? addPrefixToHeaders(failureHeaders, "Upstash-Callback-Failure-Callback-") : {},
|
|
2035
|
+
// last, so they override the run's settings above. Never
|
|
2036
|
+
// prefixed: they configure this message, not its callback.
|
|
2037
|
+
...stepSettingsHeaders
|
|
1928
2038
|
},
|
|
1929
2039
|
contentType
|
|
1930
2040
|
};
|
|
@@ -1975,7 +2085,7 @@ var submitParallelSteps = async ({
|
|
|
1975
2085
|
info: `Submitting ${planSteps.length} parallel steps.`
|
|
1976
2086
|
});
|
|
1977
2087
|
const result = await context.qstashClient.batch(
|
|
1978
|
-
planSteps.map((planStep) => {
|
|
2088
|
+
planSteps.map((planStep, index) => {
|
|
1979
2089
|
const { headers } = getHeaders({
|
|
1980
2090
|
initHeaderValue: "false",
|
|
1981
2091
|
workflowConfig: {
|
|
@@ -1983,7 +2093,8 @@ var submitParallelSteps = async ({
|
|
|
1983
2093
|
workflowUrl: context.url,
|
|
1984
2094
|
telemetry
|
|
1985
2095
|
},
|
|
1986
|
-
invokeCount
|
|
2096
|
+
invokeCount,
|
|
2097
|
+
stepSettings: steps[index].stepSettings
|
|
1987
2098
|
});
|
|
1988
2099
|
return {
|
|
1989
2100
|
headers,
|
|
@@ -2002,31 +2113,38 @@ var submitParallelSteps = async ({
|
|
|
2002
2113
|
}
|
|
2003
2114
|
throw new WorkflowAbort(planSteps[0].stepName, planSteps[0]);
|
|
2004
2115
|
};
|
|
2005
|
-
var
|
|
2006
|
-
context,
|
|
2116
|
+
var executeStep = async ({
|
|
2007
2117
|
lazyStep,
|
|
2008
2118
|
stepId,
|
|
2009
|
-
invokeCount,
|
|
2010
2119
|
concurrency,
|
|
2011
|
-
telemetry,
|
|
2012
|
-
dispatchDebug,
|
|
2013
2120
|
dispatchLifecycle
|
|
2014
2121
|
}) => {
|
|
2015
2122
|
await dispatchLifecycle("beforeExecution", {
|
|
2016
2123
|
stepName: lazyStep.stepName
|
|
2017
2124
|
});
|
|
2018
|
-
let resultStep;
|
|
2019
2125
|
try {
|
|
2020
|
-
|
|
2126
|
+
return await lazyStep.getResultStep(concurrency, stepId);
|
|
2021
2127
|
} catch (error) {
|
|
2022
2128
|
attachStepNameToError(error, lazyStep.stepName);
|
|
2023
2129
|
throw error;
|
|
2024
2130
|
}
|
|
2131
|
+
};
|
|
2132
|
+
var submitStepResult = async ({
|
|
2133
|
+
context,
|
|
2134
|
+
lazyStep,
|
|
2135
|
+
resultStep,
|
|
2136
|
+
invokeCount,
|
|
2137
|
+
concurrency,
|
|
2138
|
+
telemetry,
|
|
2139
|
+
dispatchDebug,
|
|
2140
|
+
nextStepSettings
|
|
2141
|
+
}) => {
|
|
2025
2142
|
const { headers } = lazyStep.getHeaders({
|
|
2026
2143
|
context,
|
|
2027
2144
|
step: resultStep,
|
|
2028
2145
|
invokeCount,
|
|
2029
|
-
telemetry
|
|
2146
|
+
telemetry,
|
|
2147
|
+
stepSettings: nextStepSettings
|
|
2030
2148
|
});
|
|
2031
2149
|
const body = lazyStep.getBody({
|
|
2032
2150
|
context,
|
|
@@ -2051,6 +2169,138 @@ var submitSingleStep = async ({
|
|
|
2051
2169
|
}
|
|
2052
2170
|
return resultStep;
|
|
2053
2171
|
};
|
|
2172
|
+
var publishStepConfigRequest = async ({
|
|
2173
|
+
context,
|
|
2174
|
+
lazyStep,
|
|
2175
|
+
targetStep,
|
|
2176
|
+
invokeCount,
|
|
2177
|
+
telemetry,
|
|
2178
|
+
dispatchDebug
|
|
2179
|
+
}) => {
|
|
2180
|
+
const { headers } = getHeaders({
|
|
2181
|
+
initHeaderValue: "false",
|
|
2182
|
+
workflowConfig: {
|
|
2183
|
+
workflowRunId: context.workflowRunId,
|
|
2184
|
+
workflowUrl: context.url,
|
|
2185
|
+
telemetry
|
|
2186
|
+
},
|
|
2187
|
+
invokeCount,
|
|
2188
|
+
stepSettings: lazyStep.stepSettings
|
|
2189
|
+
});
|
|
2190
|
+
await dispatchDebug("onInfo", {
|
|
2191
|
+
info: `Step "${lazyStep.stepName}" (${targetStep}) has step-level settings which the current delivery does not carry. Requesting a delivery which does.`
|
|
2192
|
+
});
|
|
2193
|
+
const result = await context.qstashClient.publishJSON({
|
|
2194
|
+
headers: {
|
|
2195
|
+
...headers,
|
|
2196
|
+
"Upstash-Workflow-CallType": WORKFLOW_STEP_CONFIG_CALL_TYPE
|
|
2197
|
+
},
|
|
2198
|
+
method: "POST",
|
|
2199
|
+
body: { targetStep, invokeCount },
|
|
2200
|
+
url: context.url,
|
|
2201
|
+
contentBasedDeduplication: true
|
|
2202
|
+
});
|
|
2203
|
+
if (result?.deduplicated) {
|
|
2204
|
+
await dispatchDebug("onWarning", {
|
|
2205
|
+
warning: `A step config request for step "${lazyStep.stepName}" (${targetStep}) was already published and this one was deduplicated. Expected when the delivery which published it is being retried; otherwise the run stops here.`
|
|
2206
|
+
});
|
|
2207
|
+
} else if (result?.messageId) {
|
|
2208
|
+
await dispatchDebug("onInfo", {
|
|
2209
|
+
info: `Requested a delivery under the settings of step "${lazyStep.stepName}" with messageId: ${result.messageId}.`
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
};
|
|
2213
|
+
|
|
2214
|
+
// src/qstash/step-config.ts
|
|
2215
|
+
var DEFAULT_PARALLELISM = 0;
|
|
2216
|
+
var DEFAULT_RATE = 0;
|
|
2217
|
+
var DEFAULT_PERIOD_SECONDS = 1;
|
|
2218
|
+
var SECONDS_PER_UNIT = {
|
|
2219
|
+
s: 1,
|
|
2220
|
+
m: 60,
|
|
2221
|
+
h: 60 * 60,
|
|
2222
|
+
d: 24 * 60 * 60
|
|
2223
|
+
};
|
|
2224
|
+
var durationToSeconds = (duration) => {
|
|
2225
|
+
if (typeof duration === "number") {
|
|
2226
|
+
return Number.isFinite(duration) ? duration : void 0;
|
|
2227
|
+
}
|
|
2228
|
+
const match = /^(\d+)([smhd])$/.exec(duration.trim());
|
|
2229
|
+
if (match) {
|
|
2230
|
+
return Number(match[1]) * SECONDS_PER_UNIT[match[2]];
|
|
2231
|
+
}
|
|
2232
|
+
const seconds = Number(duration.trim());
|
|
2233
|
+
return Number.isFinite(seconds) ? seconds : void 0;
|
|
2234
|
+
};
|
|
2235
|
+
var normalizeFlowControl = (flowControl) => {
|
|
2236
|
+
const period = flowControl.period === void 0 ? void 0 : durationToSeconds(flowControl.period);
|
|
2237
|
+
return {
|
|
2238
|
+
key: flowControl.key,
|
|
2239
|
+
parallelism: flowControl.parallelism ?? DEFAULT_PARALLELISM,
|
|
2240
|
+
rate: flowControl.rate ?? flowControl.ratePerSecond ?? DEFAULT_RATE,
|
|
2241
|
+
period: period ?? DEFAULT_PERIOD_SECONDS
|
|
2242
|
+
};
|
|
2243
|
+
};
|
|
2244
|
+
var parseFlowControlHeaders = (key, value) => {
|
|
2245
|
+
if (!key) {
|
|
2246
|
+
return void 0;
|
|
2247
|
+
}
|
|
2248
|
+
const flowControl = {
|
|
2249
|
+
key,
|
|
2250
|
+
parallelism: DEFAULT_PARALLELISM,
|
|
2251
|
+
rate: DEFAULT_RATE,
|
|
2252
|
+
period: DEFAULT_PERIOD_SECONDS
|
|
2253
|
+
};
|
|
2254
|
+
for (const entry of (value ?? "").split(",")) {
|
|
2255
|
+
const [name, rawValue] = entry.split("=").map((part) => part.trim());
|
|
2256
|
+
if (!name || rawValue === void 0) {
|
|
2257
|
+
continue;
|
|
2258
|
+
}
|
|
2259
|
+
switch (name) {
|
|
2260
|
+
case "parallelism": {
|
|
2261
|
+
flowControl.parallelism = Number(rawValue);
|
|
2262
|
+
break;
|
|
2263
|
+
}
|
|
2264
|
+
case "rate": {
|
|
2265
|
+
flowControl.rate = Number(rawValue);
|
|
2266
|
+
break;
|
|
2267
|
+
}
|
|
2268
|
+
case "period": {
|
|
2269
|
+
flowControl.period = durationToSeconds(rawValue) ?? DEFAULT_PERIOD_SECONDS;
|
|
2270
|
+
break;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
return flowControl;
|
|
2275
|
+
};
|
|
2276
|
+
var getEffectiveConfig = (headers) => {
|
|
2277
|
+
const retriesHeader = headers.get(RETRIES_HEADER);
|
|
2278
|
+
return {
|
|
2279
|
+
flowControl: parseFlowControlHeaders(
|
|
2280
|
+
headers.get(FLOW_CONTROL_KEY_HEADER),
|
|
2281
|
+
headers.get(FLOW_CONTROL_VALUE_HEADER)
|
|
2282
|
+
),
|
|
2283
|
+
retries: retriesHeader === null ? void 0 : Number(retriesHeader),
|
|
2284
|
+
retryDelay: headers.get(RETRY_DELAY_HEADER) ?? void 0,
|
|
2285
|
+
hasStepConfig: headers.get(WORKFLOW_STEP_CONFIG_HEADER) === "true"
|
|
2286
|
+
};
|
|
2287
|
+
};
|
|
2288
|
+
var describeStepSettingsMismatch = (stepSettings, effectiveConfig) => {
|
|
2289
|
+
if (stepSettings.flowControl) {
|
|
2290
|
+
const wanted = normalizeFlowControl(stepSettings.flowControl);
|
|
2291
|
+
const applied = effectiveConfig.flowControl;
|
|
2292
|
+
if (!applied || applied.key !== wanted.key || applied.parallelism !== wanted.parallelism || applied.rate !== wanted.rate || applied.period !== wanted.period) {
|
|
2293
|
+
return `flow control: expected ${JSON.stringify(wanted)}, delivery has ${JSON.stringify(applied)}`;
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
if (stepSettings.retries !== void 0 && effectiveConfig.retries !== void 0 && stepSettings.retries !== effectiveConfig.retries) {
|
|
2297
|
+
return `retries: expected ${stepSettings.retries}, delivery has ${effectiveConfig.retries}`;
|
|
2298
|
+
}
|
|
2299
|
+
if (stepSettings.retryDelay !== void 0 && stepSettings.retryDelay !== effectiveConfig.retryDelay) {
|
|
2300
|
+
return `retry delay: expected '${stepSettings.retryDelay}', delivery has '${effectiveConfig.retryDelay ?? ""}'`;
|
|
2301
|
+
}
|
|
2302
|
+
return void 0;
|
|
2303
|
+
};
|
|
2054
2304
|
|
|
2055
2305
|
// src/context/auto-executor.ts
|
|
2056
2306
|
var AutoExecutor = class _AutoExecutor {
|
|
@@ -2067,6 +2317,25 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2067
2317
|
stepCount = 0;
|
|
2068
2318
|
planStepCount = 0;
|
|
2069
2319
|
executingStep = false;
|
|
2320
|
+
/**
|
|
2321
|
+
* an executed step whose result hasn't been submitted to QStash yet.
|
|
2322
|
+
*
|
|
2323
|
+
* Set when a step which can produce its result in-process executes. The
|
|
2324
|
+
* submission waits so that the route function can continue and reveal
|
|
2325
|
+
* what comes next: if that is a single step with step-level settings,
|
|
2326
|
+
* the settings ride on this submission and no step config request is
|
|
2327
|
+
* needed, since the delivery of the submission is what executes it.
|
|
2328
|
+
*
|
|
2329
|
+
* Flushed when the next step is reached (see `addStep`) or when the
|
|
2330
|
+
* route function ends (see `flushPendingStep` in `serve`).
|
|
2331
|
+
*/
|
|
2332
|
+
pendingStep;
|
|
2333
|
+
/**
|
|
2334
|
+
* configuration QStash applied to the delivery being handled. A step
|
|
2335
|
+
* with step-level settings is settled by comparing its settings against
|
|
2336
|
+
* this.
|
|
2337
|
+
*/
|
|
2338
|
+
effectiveConfig;
|
|
2070
2339
|
/**
|
|
2071
2340
|
* @param context workflow context
|
|
2072
2341
|
* @param steps list of steps
|
|
@@ -2074,14 +2343,17 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2074
2343
|
* @param dispatchLifecycle lifecycle event dispatcher
|
|
2075
2344
|
* @param telemetry optional telemetry information
|
|
2076
2345
|
* @param invokeCount optional invoke count
|
|
2346
|
+
* @param effectiveConfig configuration QStash applied to the delivery
|
|
2347
|
+
* being handled
|
|
2077
2348
|
*/
|
|
2078
|
-
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry, invokeCount) {
|
|
2349
|
+
constructor(context, steps, dispatchDebug, dispatchLifecycle, telemetry, invokeCount, effectiveConfig) {
|
|
2079
2350
|
this.context = context;
|
|
2080
2351
|
this.steps = steps;
|
|
2081
2352
|
this.dispatchDebug = dispatchDebug;
|
|
2082
2353
|
this.dispatchLifecycle = dispatchLifecycle;
|
|
2083
2354
|
this.telemetry = telemetry;
|
|
2084
2355
|
this.invokeCount = invokeCount ?? 0;
|
|
2356
|
+
this.effectiveConfig = effectiveConfig ?? { hasStepConfig: false };
|
|
2085
2357
|
this.nonPlanStepCount = this.steps.filter((step) => !step.targetStep).length;
|
|
2086
2358
|
}
|
|
2087
2359
|
/**
|
|
@@ -2116,6 +2388,15 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2116
2388
|
lazyStepList.push(stepInfo);
|
|
2117
2389
|
const index = this.indexInCurrentList++;
|
|
2118
2390
|
const requestComplete = this.deferExecution().then(async () => {
|
|
2391
|
+
const submitted = await this.submitPendingStep(
|
|
2392
|
+
lazyStepList.length === 1 ? lazyStepList[0].stepSettings : void 0
|
|
2393
|
+
);
|
|
2394
|
+
if (submitted.isErr()) {
|
|
2395
|
+
throw submitted.error;
|
|
2396
|
+
}
|
|
2397
|
+
if (submitted.value.result === "submitted-step") {
|
|
2398
|
+
throw submitted.value.abort;
|
|
2399
|
+
}
|
|
2119
2400
|
if (!this.promises.has(lazyStepList)) {
|
|
2120
2401
|
const promise2 = this.getExecutionPromise(lazyStepList);
|
|
2121
2402
|
this.promises.set(lazyStepList, promise2);
|
|
@@ -2169,18 +2450,100 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2169
2450
|
}
|
|
2170
2451
|
return parsedOut;
|
|
2171
2452
|
}
|
|
2172
|
-
const
|
|
2173
|
-
|
|
2453
|
+
const mismatch = lazyStep.stepSettings ? describeStepSettingsMismatch(lazyStep.stepSettings, this.effectiveConfig) : void 0;
|
|
2454
|
+
if (mismatch) {
|
|
2455
|
+
if (!this.effectiveConfig.hasStepConfig) {
|
|
2456
|
+
await publishStepConfigRequest({
|
|
2457
|
+
context: this.context,
|
|
2458
|
+
lazyStep,
|
|
2459
|
+
targetStep: this.stepCount,
|
|
2460
|
+
invokeCount: this.invokeCount,
|
|
2461
|
+
telemetry: this.telemetry,
|
|
2462
|
+
dispatchDebug: this.dispatchDebug
|
|
2463
|
+
});
|
|
2464
|
+
throw new WorkflowAbort(lazyStep.stepName);
|
|
2465
|
+
} else {
|
|
2466
|
+
await this.dispatchDebug("onWarning", {
|
|
2467
|
+
warning: `The step-level settings of step '${lazyStep.stepName}' were published but are not recognized on the delivery which carries them (${mismatch}). Executing the step with the settings of the delivery instead. This is a bug in @upstash/workflow, please report it.`
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
const resultStep = await executeStep({
|
|
2174
2472
|
lazyStep,
|
|
2175
2473
|
stepId: this.stepCount,
|
|
2474
|
+
concurrency: NO_CONCURRENCY,
|
|
2475
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2476
|
+
});
|
|
2477
|
+
if (lazyStep.supportsDeferredSubmission) {
|
|
2478
|
+
this.pendingStep = { status: "held", lazyStep, resultStep };
|
|
2479
|
+
return lazyStep.parseOut({
|
|
2480
|
+
...resultStep,
|
|
2481
|
+
out: resultStep.out === void 0 ? void 0 : JSON.stringify(resultStep.out)
|
|
2482
|
+
});
|
|
2483
|
+
}
|
|
2484
|
+
await submitStepResult({
|
|
2485
|
+
context: this.context,
|
|
2486
|
+
lazyStep,
|
|
2487
|
+
resultStep,
|
|
2176
2488
|
invokeCount: this.invokeCount,
|
|
2177
|
-
concurrency:
|
|
2489
|
+
concurrency: NO_CONCURRENCY,
|
|
2178
2490
|
telemetry: this.telemetry,
|
|
2179
|
-
dispatchDebug: this.dispatchDebug
|
|
2180
|
-
dispatchLifecycle: this.dispatchLifecycle
|
|
2491
|
+
dispatchDebug: this.dispatchDebug
|
|
2181
2492
|
});
|
|
2182
2493
|
throw new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
2183
2494
|
}
|
|
2495
|
+
/**
|
|
2496
|
+
* Submits the result of the step being held, if there is one, and
|
|
2497
|
+
* returns the abort the caller has to throw to end the invocation.
|
|
2498
|
+
*
|
|
2499
|
+
* Returns undefined when no step is being held, which is the caller's
|
|
2500
|
+
* signal that there is nothing to end. Throws instead of returning
|
|
2501
|
+
* when the submission itself fails, so that error reaches the caller
|
|
2502
|
+
* rather than an abort claiming the step was submitted.
|
|
2503
|
+
*
|
|
2504
|
+
* Callers which arrive while a submission is already under way (the
|
|
2505
|
+
* steps of a parallel group, added together) wait for that one and get
|
|
2506
|
+
* the same abort, rather than submitting a second time.
|
|
2507
|
+
*
|
|
2508
|
+
* The next step's settings are attached whenever it has any, without
|
|
2509
|
+
* checking them against the current delivery: the executor only knows
|
|
2510
|
+
* the configuration of the delivery in hand, which inside a
|
|
2511
|
+
* delivery is the *previous* step's, so such a check would be wrong
|
|
2512
|
+
* exactly when two steps with settings follow each other.
|
|
2513
|
+
*
|
|
2514
|
+
* @param nextStepSettings step-level settings of the next step
|
|
2515
|
+
* @returns `submitted-step` with the abort which ends this invocation,
|
|
2516
|
+
* or `no-pending-step` when nothing was held and there is nothing to
|
|
2517
|
+
* end
|
|
2518
|
+
*/
|
|
2519
|
+
async submitPendingStep(nextStepSettings) {
|
|
2520
|
+
if (!this.pendingStep) {
|
|
2521
|
+
return ok({ result: "no-pending-step" });
|
|
2522
|
+
}
|
|
2523
|
+
try {
|
|
2524
|
+
if (this.pendingStep.status === "submitting") {
|
|
2525
|
+
return ok({ result: "submitted-step", abort: await this.pendingStep.submitted });
|
|
2526
|
+
}
|
|
2527
|
+
const { lazyStep, resultStep } = this.pendingStep;
|
|
2528
|
+
const submitted = (async () => {
|
|
2529
|
+
await submitStepResult({
|
|
2530
|
+
context: this.context,
|
|
2531
|
+
lazyStep,
|
|
2532
|
+
resultStep,
|
|
2533
|
+
invokeCount: this.invokeCount,
|
|
2534
|
+
concurrency: NO_CONCURRENCY,
|
|
2535
|
+
telemetry: this.telemetry,
|
|
2536
|
+
dispatchDebug: this.dispatchDebug,
|
|
2537
|
+
nextStepSettings
|
|
2538
|
+
});
|
|
2539
|
+
return new WorkflowAbort(lazyStep.stepName, resultStep);
|
|
2540
|
+
})();
|
|
2541
|
+
this.pendingStep = { status: "submitting", submitted };
|
|
2542
|
+
return ok({ result: "submitted-step", abort: await submitted });
|
|
2543
|
+
} catch (error) {
|
|
2544
|
+
return err(error);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2184
2547
|
/**
|
|
2185
2548
|
* Runs steps in parallel.
|
|
2186
2549
|
*
|
|
@@ -2229,15 +2592,20 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2229
2592
|
validateStep(parallelSteps[stepIndex], planStep);
|
|
2230
2593
|
try {
|
|
2231
2594
|
const parallelStep = parallelSteps[stepIndex];
|
|
2232
|
-
const resultStep = await
|
|
2233
|
-
context: this.context,
|
|
2595
|
+
const resultStep = await executeStep({
|
|
2234
2596
|
lazyStep: parallelStep,
|
|
2235
2597
|
stepId: planStep.targetStep,
|
|
2598
|
+
concurrency: parallelSteps.length,
|
|
2599
|
+
dispatchLifecycle: this.dispatchLifecycle
|
|
2600
|
+
});
|
|
2601
|
+
await submitStepResult({
|
|
2602
|
+
context: this.context,
|
|
2603
|
+
lazyStep: parallelStep,
|
|
2604
|
+
resultStep,
|
|
2236
2605
|
invokeCount: this.invokeCount,
|
|
2237
2606
|
concurrency: parallelSteps.length,
|
|
2238
2607
|
telemetry: this.telemetry,
|
|
2239
|
-
dispatchDebug: this.dispatchDebug
|
|
2240
|
-
dispatchLifecycle: this.dispatchLifecycle
|
|
2608
|
+
dispatchDebug: this.dispatchDebug
|
|
2241
2609
|
});
|
|
2242
2610
|
throw new WorkflowAbort(parallelStep.stepName, resultStep);
|
|
2243
2611
|
} catch (error) {
|
|
@@ -2786,6 +3154,46 @@ var WorkflowContext = class {
|
|
|
2786
3154
|
* `1` on the first retry, `2` on the second, and so on.
|
|
2787
3155
|
*/
|
|
2788
3156
|
retried;
|
|
3157
|
+
/**
|
|
3158
|
+
* Configuration QStash applied to the request being handled.
|
|
3159
|
+
*
|
|
3160
|
+
* This is the configuration of *this delivery*, not of the run: it is
|
|
3161
|
+
* the configuration the run was triggered with, except inside the
|
|
3162
|
+
* delivery which executes a step that has step-level settings, where
|
|
3163
|
+
* it is that step's settings. The fields below expose it; the executor
|
|
3164
|
+
* gets it directly rather than reaching through the context.
|
|
3165
|
+
*/
|
|
3166
|
+
effectiveConfig;
|
|
3167
|
+
/**
|
|
3168
|
+
* Flow control QStash applied to the request being handled, if any.
|
|
3169
|
+
*
|
|
3170
|
+
* This is the flow control of the request in hand, which is the one the
|
|
3171
|
+
* run was triggered with except inside a step carrying its own.
|
|
3172
|
+
*/
|
|
3173
|
+
get flowControl() {
|
|
3174
|
+
return this.effectiveConfig.flowControl;
|
|
3175
|
+
}
|
|
3176
|
+
/**
|
|
3177
|
+
* Retry limit QStash applied to the request being handled, if it
|
|
3178
|
+
* reported one.
|
|
3179
|
+
*
|
|
3180
|
+
* Not to be confused with {@link retried}, which is how many retries
|
|
3181
|
+
* have already happened.
|
|
3182
|
+
*
|
|
3183
|
+
* @see {@link flowControl} for what "the request being handled" means
|
|
3184
|
+
*/
|
|
3185
|
+
get retries() {
|
|
3186
|
+
return this.effectiveConfig.retries;
|
|
3187
|
+
}
|
|
3188
|
+
/**
|
|
3189
|
+
* Retry delay expression QStash applied to the request being handled,
|
|
3190
|
+
* if any.
|
|
3191
|
+
*
|
|
3192
|
+
* @see {@link flowControl} for what "the request being handled" means
|
|
3193
|
+
*/
|
|
3194
|
+
get retryDelay() {
|
|
3195
|
+
return this.effectiveConfig.retryDelay;
|
|
3196
|
+
}
|
|
2789
3197
|
constructor({
|
|
2790
3198
|
qstashClient,
|
|
2791
3199
|
workflowRunId,
|
|
@@ -2799,7 +3207,8 @@ var WorkflowContext = class {
|
|
|
2799
3207
|
invokeCount,
|
|
2800
3208
|
label,
|
|
2801
3209
|
retried,
|
|
2802
|
-
middlewareManager
|
|
3210
|
+
middlewareManager,
|
|
3211
|
+
effectiveConfig
|
|
2803
3212
|
}) {
|
|
2804
3213
|
this.qstashClient = qstashClient;
|
|
2805
3214
|
this.workflowRunId = workflowRunId;
|
|
@@ -2811,6 +3220,7 @@ var WorkflowContext = class {
|
|
|
2811
3220
|
this.env = env ?? {};
|
|
2812
3221
|
this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
|
|
2813
3222
|
this.retried = retried ?? 0;
|
|
3223
|
+
this.effectiveConfig = effectiveConfig ?? { hasStepConfig: false };
|
|
2814
3224
|
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
2815
3225
|
middlewareManagerInstance.assignContext(this);
|
|
2816
3226
|
this.executor = new AutoExecutor(
|
|
@@ -2819,7 +3229,8 @@ var WorkflowContext = class {
|
|
|
2819
3229
|
middlewareManagerInstance.dispatchDebug.bind(middlewareManagerInstance),
|
|
2820
3230
|
middlewareManagerInstance.dispatchLifecycle.bind(middlewareManagerInstance),
|
|
2821
3231
|
telemetry,
|
|
2822
|
-
invokeCount
|
|
3232
|
+
invokeCount,
|
|
3233
|
+
this.effectiveConfig
|
|
2823
3234
|
);
|
|
2824
3235
|
}
|
|
2825
3236
|
/**
|
|
@@ -2845,13 +3256,35 @@ var WorkflowContext = class {
|
|
|
2845
3256
|
* ])
|
|
2846
3257
|
* ```
|
|
2847
3258
|
*
|
|
3259
|
+
* Step-level settings can be passed as a third argument, overriding
|
|
3260
|
+
* the settings the workflow run was triggered with for this step only:
|
|
3261
|
+
*
|
|
3262
|
+
* ```typescript
|
|
3263
|
+
* const result = await context.run(
|
|
3264
|
+
* "step 1",
|
|
3265
|
+
* () => {
|
|
3266
|
+
* return "result"
|
|
3267
|
+
* },
|
|
3268
|
+
* {
|
|
3269
|
+
* flowControl: { key: "custom-key", parallelism: 3 },
|
|
3270
|
+
* retries: 5,
|
|
3271
|
+
* }
|
|
3272
|
+
* )
|
|
3273
|
+
* ```
|
|
3274
|
+
*
|
|
2848
3275
|
* @param stepName name of the step
|
|
2849
3276
|
* @param stepFunction step function to be executed
|
|
3277
|
+
* @param stepSettings step-level settings for this step
|
|
2850
3278
|
* @returns result of the step function
|
|
2851
3279
|
*/
|
|
2852
|
-
async run(stepName, stepFunction) {
|
|
3280
|
+
async run(stepName, stepFunction, stepSettings) {
|
|
3281
|
+
if (stepSettings) {
|
|
3282
|
+
validateFlowControl(stepSettings.flowControl);
|
|
3283
|
+
}
|
|
2853
3284
|
const wrappedStepFunction = (() => this.executor.wrapStep(stepName, stepFunction));
|
|
2854
|
-
return await this.addStep(
|
|
3285
|
+
return await this.addStep(
|
|
3286
|
+
new LazyFunctionStep(this, stepName, wrappedStepFunction, stepSettings)
|
|
3287
|
+
);
|
|
2855
3288
|
}
|
|
2856
3289
|
/**
|
|
2857
3290
|
* Stops the execution for the duration provided.
|
|
@@ -3074,7 +3507,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3074
3507
|
*
|
|
3075
3508
|
* @param routeFunction
|
|
3076
3509
|
*/
|
|
3077
|
-
static async tryAuthentication(routeFunction, context) {
|
|
3510
|
+
static async tryAuthentication(routeFunction, context, effectiveConfig) {
|
|
3078
3511
|
const disabledContext = new _DisabledWorkflowContext({
|
|
3079
3512
|
qstashClient: new import_qstash10.Client({
|
|
3080
3513
|
baseUrl: "disabled-client",
|
|
@@ -3089,7 +3522,10 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3089
3522
|
initialPayload: context.requestPayload,
|
|
3090
3523
|
env: context.env,
|
|
3091
3524
|
label: context.labels,
|
|
3092
|
-
retried: context.retried
|
|
3525
|
+
retried: context.retried,
|
|
3526
|
+
// the route function runs for real here until it reaches a step, so
|
|
3527
|
+
// it observes the same configuration as it will on the real context
|
|
3528
|
+
effectiveConfig
|
|
3093
3529
|
});
|
|
3094
3530
|
try {
|
|
3095
3531
|
await routeFunction(disabledContext);
|
|
@@ -3306,6 +3742,7 @@ var handleFailure = async ({
|
|
|
3306
3742
|
}
|
|
3307
3743
|
const userHeaders = recreateUserHeaders(request.headers);
|
|
3308
3744
|
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
3745
|
+
const effectiveConfig = getEffectiveConfig(request.headers);
|
|
3309
3746
|
const workflowContext = new WorkflowContext({
|
|
3310
3747
|
qstashClient,
|
|
3311
3748
|
workflowRunId,
|
|
@@ -3319,11 +3756,13 @@ var handleFailure = async ({
|
|
|
3319
3756
|
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
|
|
3320
3757
|
retried,
|
|
3321
3758
|
workflowRunCreatedAt: workflowCreatedAt,
|
|
3322
|
-
middlewareManager: void 0
|
|
3759
|
+
middlewareManager: void 0,
|
|
3760
|
+
effectiveConfig
|
|
3323
3761
|
});
|
|
3324
3762
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3325
3763
|
routeFunction,
|
|
3326
|
-
workflowContext
|
|
3764
|
+
workflowContext,
|
|
3765
|
+
effectiveConfig
|
|
3327
3766
|
);
|
|
3328
3767
|
if (authCheck.isErr()) {
|
|
3329
3768
|
await dispatchDebug?.("onError", {
|
|
@@ -3415,7 +3854,13 @@ var createRegionalHandler = (environment, receiverConfig, region, clientOptions)
|
|
|
3415
3854
|
const client = new import_qstash11.Client({
|
|
3416
3855
|
...clientOptions,
|
|
3417
3856
|
baseUrl: clientEnv.QSTASH_URL,
|
|
3418
|
-
token: clientEnv.QSTASH_TOKEN
|
|
3857
|
+
token: clientEnv.QSTASH_TOKEN,
|
|
3858
|
+
// Mirror the receiver's dev-mode decision so a single QSTASH_DEV=true points
|
|
3859
|
+
// the client at the local dev server too. Without this the QStash Client
|
|
3860
|
+
// re-derives dev mode from its own process.env, which is blind to the
|
|
3861
|
+
// Cloudflare worker `env` binding that actually carries QSTASH_DEV there.
|
|
3862
|
+
// An explicit devMode in the user's client config still wins.
|
|
3863
|
+
devMode: clientOptions?.devMode ?? isQStashDevModeEnabled(environment)
|
|
3419
3864
|
});
|
|
3420
3865
|
const receiver = getReceiver(environment, receiverConfig, region);
|
|
3421
3866
|
return { client, receiver };
|
|
@@ -3465,7 +3910,9 @@ var getQStashHandlers = ({
|
|
|
3465
3910
|
client: qstashClientOption && "http" in qstashClientOption ? qstashClientOption : new import_qstash11.Client({
|
|
3466
3911
|
...qstashClientOption,
|
|
3467
3912
|
baseUrl: environment.QSTASH_URL,
|
|
3468
|
-
token: environment.QSTASH_TOKEN
|
|
3913
|
+
token: environment.QSTASH_TOKEN,
|
|
3914
|
+
// Mirror the receiver's dev-mode decision (see createRegionalHandler).
|
|
3915
|
+
devMode: qstashClientOption?.devMode ?? isQStashDevModeEnabled(environment)
|
|
3469
3916
|
}),
|
|
3470
3917
|
receiver: getReceiver(environment, receiverConfig)
|
|
3471
3918
|
}
|
|
@@ -3805,12 +4252,14 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3805
4252
|
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
3806
4253
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
3807
4254
|
const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
4255
|
+
const effectiveConfig = getEffectiveConfig(request.headers);
|
|
3808
4256
|
const workflowContext = new WorkflowContext({
|
|
3809
4257
|
qstashClient: regionalClient,
|
|
3810
4258
|
workflowRunId,
|
|
3811
4259
|
initialPayload: isThirdPartyCallResult(request) ? JSON.parse(rawInitialPayload) : initialPayloadParser(rawInitialPayload),
|
|
3812
4260
|
headers: recreateUserHeaders(request.headers),
|
|
3813
4261
|
steps,
|
|
4262
|
+
effectiveConfig,
|
|
3814
4263
|
url: workflowUrl,
|
|
3815
4264
|
env,
|
|
3816
4265
|
telemetry,
|
|
@@ -3822,7 +4271,8 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3822
4271
|
});
|
|
3823
4272
|
const authCheck = await DisabledWorkflowContext.tryAuthentication(
|
|
3824
4273
|
routeFunction,
|
|
3825
|
-
workflowContext
|
|
4274
|
+
workflowContext,
|
|
4275
|
+
effectiveConfig
|
|
3826
4276
|
);
|
|
3827
4277
|
if (authCheck.isErr()) {
|
|
3828
4278
|
throw authCheck.error;
|
|
@@ -3859,7 +4309,23 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3859
4309
|
if (steps.length === 1) {
|
|
3860
4310
|
await middlewareManager.dispatchLifecycle("runStarted", {});
|
|
3861
4311
|
}
|
|
3862
|
-
|
|
4312
|
+
let outcome;
|
|
4313
|
+
try {
|
|
4314
|
+
outcome = { ran: true, result: await routeFunction(workflowContext) };
|
|
4315
|
+
} catch (error) {
|
|
4316
|
+
outcome = { ran: false, error };
|
|
4317
|
+
}
|
|
4318
|
+
const submitted = await flushPendingStep(workflowContext);
|
|
4319
|
+
if (submitted.isErr()) {
|
|
4320
|
+
throw submitted.error;
|
|
4321
|
+
}
|
|
4322
|
+
if (submitted.value.result === "submitted-step") {
|
|
4323
|
+
throw submitted.value.abort;
|
|
4324
|
+
}
|
|
4325
|
+
if (!outcome.ran) {
|
|
4326
|
+
throw outcome.error;
|
|
4327
|
+
}
|
|
4328
|
+
return outcome.result;
|
|
3863
4329
|
},
|
|
3864
4330
|
onCleanup: async (result2) => {
|
|
3865
4331
|
await middlewareManager.dispatchLifecycle("runCompleted", {
|
|
@@ -3939,6 +4405,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3939
4405
|
return { handler: safeHandler };
|
|
3940
4406
|
};
|
|
3941
4407
|
var serve = (routeFunction, options) => {
|
|
4408
|
+
void (0, import_qstash12.startDevServer)();
|
|
3942
4409
|
return serveBase(
|
|
3943
4410
|
routeFunction,
|
|
3944
4411
|
{
|
|
@@ -3950,9 +4417,10 @@ var serve = (routeFunction, options) => {
|
|
|
3950
4417
|
};
|
|
3951
4418
|
|
|
3952
4419
|
// src/client/index.ts
|
|
3953
|
-
var
|
|
4420
|
+
var import_qstash14 = require("@upstash/qstash");
|
|
3954
4421
|
|
|
3955
4422
|
// src/client/dlq.ts
|
|
4423
|
+
var import_qstash13 = require("@upstash/qstash");
|
|
3956
4424
|
function buildResumeRestartHeaders(options) {
|
|
3957
4425
|
const headers = {};
|
|
3958
4426
|
if (options?.flowControl) {
|
|
@@ -4013,8 +4481,11 @@ var DLQ = class {
|
|
|
4013
4481
|
});
|
|
4014
4482
|
return Array.isArray(dlqId) ? workflowRuns : workflowRuns[0];
|
|
4015
4483
|
}
|
|
4016
|
-
if (typeof request === "string"
|
|
4017
|
-
|
|
4484
|
+
if (typeof request === "string" || Array.isArray(request)) {
|
|
4485
|
+
const ids = toNonEmptyIdArray(request, "DLQ id");
|
|
4486
|
+
if (ids.length === 0) return { workflowRuns: [] };
|
|
4487
|
+
request = ids;
|
|
4488
|
+
}
|
|
4018
4489
|
const filters = Array.isArray(request) ? { dlqIds: request } : request;
|
|
4019
4490
|
return normalizeCursor(
|
|
4020
4491
|
await this.client.http.request({
|
|
@@ -4037,8 +4508,11 @@ var DLQ = class {
|
|
|
4037
4508
|
});
|
|
4038
4509
|
return Array.isArray(dlqId) ? workflowRuns : workflowRuns[0];
|
|
4039
4510
|
}
|
|
4040
|
-
if (typeof request === "string"
|
|
4041
|
-
|
|
4511
|
+
if (typeof request === "string" || Array.isArray(request)) {
|
|
4512
|
+
const ids = toNonEmptyIdArray(request, "DLQ id");
|
|
4513
|
+
if (ids.length === 0) return { workflowRuns: [] };
|
|
4514
|
+
request = ids;
|
|
4515
|
+
}
|
|
4042
4516
|
const filters = Array.isArray(request) ? { dlqIds: request } : request;
|
|
4043
4517
|
return normalizeCursor(
|
|
4044
4518
|
await this.client.http.request({
|
|
@@ -4057,12 +4531,34 @@ var DLQ = class {
|
|
|
4057
4531
|
* @returns response with workflow run information
|
|
4058
4532
|
*/
|
|
4059
4533
|
async retryFailureFunction({ dlqId }) {
|
|
4534
|
+
assertNonEmptyId(dlqId, "DLQ id");
|
|
4060
4535
|
const response = await this.client.http.request({
|
|
4061
4536
|
path: ["v2", "workflows", "dlq", "callback", dlqId],
|
|
4062
4537
|
method: "POST"
|
|
4063
4538
|
});
|
|
4064
4539
|
return response;
|
|
4065
4540
|
}
|
|
4541
|
+
/**
|
|
4542
|
+
* Cancel the in-progress failure function (failureUrl/failureFunction) call
|
|
4543
|
+
* of a workflow run.
|
|
4544
|
+
*
|
|
4545
|
+
* Fails if the run has no failure function call in progress.
|
|
4546
|
+
*
|
|
4547
|
+
* @param dlqId - The ID of the DLQ message whose failure function call will be canceled
|
|
4548
|
+
*/
|
|
4549
|
+
async cancelFailureFunction({ dlqId }) {
|
|
4550
|
+
assertNonEmptyId(dlqId, "DLQ id");
|
|
4551
|
+
try {
|
|
4552
|
+
await this.client.http.request({
|
|
4553
|
+
path: ["v2", "workflows", "dlq", "callback", dlqId],
|
|
4554
|
+
method: "DELETE",
|
|
4555
|
+
parseResponseAsJson: false
|
|
4556
|
+
});
|
|
4557
|
+
} catch (error) {
|
|
4558
|
+
if (isInstanceOf(error, import_qstash13.QstashError) && error.status === 302) return;
|
|
4559
|
+
throw error;
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4066
4562
|
/**
|
|
4067
4563
|
* Delete DLQ messages.
|
|
4068
4564
|
*
|
|
@@ -4084,8 +4580,11 @@ var DLQ = class {
|
|
|
4084
4580
|
* ```
|
|
4085
4581
|
*/
|
|
4086
4582
|
async delete(request) {
|
|
4087
|
-
if (typeof request === "string"
|
|
4088
|
-
|
|
4583
|
+
if (typeof request === "string" || Array.isArray(request)) {
|
|
4584
|
+
const ids = toNonEmptyIdArray(request, "DLQ id");
|
|
4585
|
+
if (ids.length === 0) return { deleted: 0 };
|
|
4586
|
+
request = ids;
|
|
4587
|
+
}
|
|
4089
4588
|
const filters = Array.isArray(request) ? { dlqIds: request } : request;
|
|
4090
4589
|
return normalizeCursor(
|
|
4091
4590
|
await this.client.http.request({
|
|
@@ -4101,7 +4600,7 @@ var DLQ = class {
|
|
|
4101
4600
|
var Client4 = class {
|
|
4102
4601
|
client;
|
|
4103
4602
|
constructor(clientConfig) {
|
|
4104
|
-
this.client = new
|
|
4603
|
+
this.client = new import_qstash14.Client(clientConfig);
|
|
4105
4604
|
}
|
|
4106
4605
|
async cancel(request) {
|
|
4107
4606
|
if (typeof request === "object" && !Array.isArray(request) && ("ids" in request || "urlStartingWith" in request)) {
|
|
@@ -4114,8 +4613,11 @@ var Client4 = class {
|
|
|
4114
4613
|
return this.cancel({ filter: { workflowUrlStartingWith: legacy.urlStartingWith } });
|
|
4115
4614
|
}
|
|
4116
4615
|
}
|
|
4117
|
-
if (typeof request === "string"
|
|
4118
|
-
|
|
4616
|
+
if (typeof request === "string" || Array.isArray(request)) {
|
|
4617
|
+
const ids = toNonEmptyIdArray(request, "Workflow run id");
|
|
4618
|
+
if (ids.length === 0) return { cancelled: 0 };
|
|
4619
|
+
request = ids;
|
|
4620
|
+
}
|
|
4119
4621
|
const filters = Array.isArray(request) ? { workflowRunIds: request } : request;
|
|
4120
4622
|
return await this.client.http.request({
|
|
4121
4623
|
path: ["v2", "workflows", "runs"],
|