@upstash/workflow 1.3.0 → 1.3.2-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 +2 -2
- package/astro.d.ts +2 -2
- package/astro.js +81 -14
- package/astro.mjs +1 -1
- package/{chunk-G24J5PCC.mjs → chunk-ICDNN4UW.mjs} +84 -14
- package/cloudflare.d.mts +2 -2
- package/cloudflare.d.ts +2 -2
- package/cloudflare.js +81 -14
- package/cloudflare.mjs +1 -1
- package/express.d.mts +2 -2
- package/express.d.ts +2 -2
- package/express.js +81 -14
- package/express.mjs +1 -1
- package/h3.d.mts +2 -2
- package/h3.d.ts +2 -2
- package/h3.js +81 -14
- package/h3.mjs +1 -1
- package/hono.d.mts +2 -2
- package/hono.d.ts +2 -2
- package/hono.js +81 -14
- package/hono.mjs +1 -1
- package/index.d.mts +43 -7
- package/index.d.ts +43 -7
- package/index.js +84 -15
- package/index.mjs +8 -3
- package/nextjs.d.mts +2 -2
- package/nextjs.d.ts +2 -2
- package/nextjs.js +81 -14
- package/nextjs.mjs +1 -1
- package/package.json +1 -1
- package/react-router.d.mts +2 -2
- package/react-router.d.ts +2 -2
- package/react-router.js +81 -14
- package/react-router.mjs +1 -1
- package/{serve-many-D3D9uE4u.d.mts → serve-many-CG3BFvO3.d.mts} +1 -1
- package/{serve-many-kPOasiyb.d.ts → serve-many-iJF1IUXk.d.ts} +1 -1
- package/solidjs.d.mts +1 -1
- package/solidjs.d.ts +1 -1
- package/solidjs.js +81 -14
- package/solidjs.mjs +1 -1
- package/svelte.d.mts +2 -2
- package/svelte.d.ts +2 -2
- package/svelte.js +81 -14
- package/svelte.mjs +1 -1
- package/tanstack.d.mts +2 -2
- package/tanstack.d.ts +2 -2
- package/tanstack.js +81 -14
- package/tanstack.mjs +1 -1
- package/{types-B_E1VAK6.d.ts → types-CekOpKvz.d.mts} +14 -6
- package/{types-B_E1VAK6.d.mts → types-CekOpKvz.d.ts} +14 -6
package/hono.js
CHANGED
|
@@ -103,6 +103,22 @@ var formatWorkflowError = (error) => {
|
|
|
103
103
|
message: `An error occured while executing workflow: '${typeof error === "string" ? error : JSON.stringify(error)}'`
|
|
104
104
|
};
|
|
105
105
|
};
|
|
106
|
+
var attachStepNameToError = (error, stepName) => {
|
|
107
|
+
if (typeof error !== "object" || error === null) return;
|
|
108
|
+
if ("errorStepName" in error) return;
|
|
109
|
+
if (!Object.isExtensible(error)) return;
|
|
110
|
+
try {
|
|
111
|
+
error.errorStepName = stepName;
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
var getStepNameFromError = (error) => {
|
|
116
|
+
if (typeof error === "object" && error !== null) {
|
|
117
|
+
const stepName = error.errorStepName;
|
|
118
|
+
return typeof stepName === "string" ? stepName : void 0;
|
|
119
|
+
}
|
|
120
|
+
return void 0;
|
|
121
|
+
};
|
|
106
122
|
function getConstructorName(obj) {
|
|
107
123
|
if (obj === null || obj === void 0) {
|
|
108
124
|
return null;
|
|
@@ -196,12 +212,13 @@ var WORKFLOW_RETRIED_HEADER = "Upstash-Retried";
|
|
|
196
212
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
197
213
|
var WORKFLOW_UNKOWN_SDK_VERSION_HEADER = "Upstash-Workflow-Unknown-Sdk";
|
|
198
214
|
var WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER = "upstash-workflow-trigger-by-sdk";
|
|
215
|
+
var WORKFLOW_ERROR_STEP_NAME_HEADER = "Upstash-Error-Step-Name";
|
|
199
216
|
var WORKFLOW_PROTOCOL_VERSION = "1";
|
|
200
217
|
var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
|
|
201
218
|
var DEFAULT_CONTENT_TYPE = "application/json";
|
|
202
219
|
var NO_CONCURRENCY = 1;
|
|
203
220
|
var DEFAULT_RETRIES = 3;
|
|
204
|
-
var VERSION = "v1.2
|
|
221
|
+
var VERSION = "v1.3.2";
|
|
205
222
|
var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
|
|
206
223
|
var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
207
224
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
@@ -216,6 +233,31 @@ var import_qstash4 = require("@upstash/qstash");
|
|
|
216
233
|
// src/utils.ts
|
|
217
234
|
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
218
235
|
var NANOID_LENGTH = 21;
|
|
236
|
+
var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
|
|
237
|
+
function validateLabel(label) {
|
|
238
|
+
if (label === void 0) return;
|
|
239
|
+
const labels = Array.isArray(label) ? label : [label];
|
|
240
|
+
if (labels.length === 0) {
|
|
241
|
+
throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
|
|
242
|
+
}
|
|
243
|
+
for (const value of labels) {
|
|
244
|
+
if (!RESOURCE_NAME_PATTERN.test(value)) {
|
|
245
|
+
throw new WorkflowNonRetryableError(
|
|
246
|
+
`Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function serializeLabel(label) {
|
|
252
|
+
return Array.isArray(label) ? label.join(",") : label;
|
|
253
|
+
}
|
|
254
|
+
function validateFlowControl(flowControl) {
|
|
255
|
+
if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
|
|
256
|
+
throw new WorkflowNonRetryableError(
|
|
257
|
+
`Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
219
261
|
function getRandomInt() {
|
|
220
262
|
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
221
263
|
}
|
|
@@ -751,8 +793,8 @@ var triggerFirstInvocation = async (params) => {
|
|
|
751
793
|
if (unknownSdk) {
|
|
752
794
|
headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
|
|
753
795
|
}
|
|
754
|
-
if (workflowContext.
|
|
755
|
-
headers[WORKFLOW_LABEL_HEADER] = workflowContext.
|
|
796
|
+
if (workflowContext.labels.length > 0) {
|
|
797
|
+
headers[WORKFLOW_LABEL_HEADER] = serializeLabel(workflowContext.labels);
|
|
756
798
|
}
|
|
757
799
|
const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
|
|
758
800
|
return {
|
|
@@ -1554,8 +1596,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1554
1596
|
});
|
|
1555
1597
|
triggerHeaders["Upstash-Workflow-Invoke"] = "true";
|
|
1556
1598
|
if (label) {
|
|
1557
|
-
|
|
1558
|
-
triggerHeaders[
|
|
1599
|
+
const labelHeader = serializeLabel(label);
|
|
1600
|
+
triggerHeaders[WORKFLOW_LABEL_HEADER] = labelHeader;
|
|
1601
|
+
triggerHeaders[`upstash-forward-${WORKFLOW_LABEL_HEADER}`] = labelHeader;
|
|
1559
1602
|
}
|
|
1560
1603
|
return { headers: triggerHeaders, contentType };
|
|
1561
1604
|
}
|
|
@@ -1898,7 +1941,13 @@ var submitSingleStep = async ({
|
|
|
1898
1941
|
await dispatchLifecycle("beforeExecution", {
|
|
1899
1942
|
stepName: lazyStep.stepName
|
|
1900
1943
|
});
|
|
1901
|
-
|
|
1944
|
+
let resultStep;
|
|
1945
|
+
try {
|
|
1946
|
+
resultStep = await lazyStep.getResultStep(concurrency, stepId);
|
|
1947
|
+
} catch (error) {
|
|
1948
|
+
attachStepNameToError(error, lazyStep.stepName);
|
|
1949
|
+
throw error;
|
|
1950
|
+
}
|
|
1902
1951
|
const { headers } = lazyStep.getHeaders({
|
|
1903
1952
|
context,
|
|
1904
1953
|
step: resultStep,
|
|
@@ -2121,9 +2170,11 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2121
2170
|
if (isInstanceOf(error, WorkflowAbort) || isInstanceOf(error, import_qstash5.QstashError) && error.status === 400 || isInstanceOf(error, import_qstash5.QstashError) && error.status === 412) {
|
|
2122
2171
|
throw error;
|
|
2123
2172
|
}
|
|
2124
|
-
|
|
2173
|
+
const wrappedError = new WorkflowError(
|
|
2125
2174
|
`Error submitting steps to QStash in partial parallel step execution: ${error}`
|
|
2126
2175
|
);
|
|
2176
|
+
attachStepNameToError(wrappedError, parallelSteps[stepIndex].stepName);
|
|
2177
|
+
throw wrappedError;
|
|
2127
2178
|
}
|
|
2128
2179
|
break;
|
|
2129
2180
|
}
|
|
@@ -2696,9 +2747,10 @@ var WorkflowContext = class {
|
|
|
2696
2747
|
*/
|
|
2697
2748
|
env;
|
|
2698
2749
|
/**
|
|
2699
|
-
*
|
|
2750
|
+
* Labels attached to the workflow run.
|
|
2700
2751
|
*
|
|
2701
|
-
* Can be used to filter the workflow run logs.
|
|
2752
|
+
* Can be used to filter the workflow run logs. A run can have multiple
|
|
2753
|
+
* labels when triggered with `label: string[]`.
|
|
2702
2754
|
*
|
|
2703
2755
|
* Can be set by passing a `label` parameter when triggering the workflow
|
|
2704
2756
|
* with `client.trigger`:
|
|
@@ -2706,11 +2758,20 @@ var WorkflowContext = class {
|
|
|
2706
2758
|
* ```ts
|
|
2707
2759
|
* await client.trigger({
|
|
2708
2760
|
* url: "https://workflow-endpoint.com",
|
|
2709
|
-
* label: "
|
|
2761
|
+
* label: ["label-1", "label-2"]
|
|
2710
2762
|
* });
|
|
2711
2763
|
* ```
|
|
2712
2764
|
*/
|
|
2713
|
-
|
|
2765
|
+
labels;
|
|
2766
|
+
/**
|
|
2767
|
+
* Label of the workflow run.
|
|
2768
|
+
*
|
|
2769
|
+
* @deprecated Use `labels` instead. When a run has multiple labels, this
|
|
2770
|
+
* only returns the first one.
|
|
2771
|
+
*/
|
|
2772
|
+
get label() {
|
|
2773
|
+
return this.labels[0];
|
|
2774
|
+
}
|
|
2714
2775
|
/**
|
|
2715
2776
|
* Number of times QStash has retried delivering the current request.
|
|
2716
2777
|
*
|
|
@@ -2741,7 +2802,7 @@ var WorkflowContext = class {
|
|
|
2741
2802
|
this.headers = headers;
|
|
2742
2803
|
this.requestPayload = initialPayload;
|
|
2743
2804
|
this.env = env ?? {};
|
|
2744
|
-
this.
|
|
2805
|
+
this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
|
|
2745
2806
|
this.retried = retried ?? 0;
|
|
2746
2807
|
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
2747
2808
|
middlewareManagerInstance.assignContext(this);
|
|
@@ -2822,8 +2883,10 @@ var WorkflowContext = class {
|
|
|
2822
2883
|
await this.addStep(new LazySleepUntilStep(this, stepName, time));
|
|
2823
2884
|
}
|
|
2824
2885
|
async call(stepName, settings) {
|
|
2886
|
+
validateFlowControl(settings.flowControl);
|
|
2825
2887
|
let callStep;
|
|
2826
2888
|
if ("workflow" in settings) {
|
|
2889
|
+
validateLabel(settings.label);
|
|
2827
2890
|
const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
|
|
2828
2891
|
const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
|
|
2829
2892
|
callStep = new LazyCallStep({
|
|
@@ -2931,6 +2994,8 @@ var WorkflowContext = class {
|
|
|
2931
2994
|
);
|
|
2932
2995
|
}
|
|
2933
2996
|
async invoke(stepName, settings) {
|
|
2997
|
+
validateLabel(settings.label);
|
|
2998
|
+
validateFlowControl(settings.flowControl);
|
|
2934
2999
|
return await this.addStep(
|
|
2935
3000
|
new LazyInvokeStep(this, stepName, settings)
|
|
2936
3001
|
);
|
|
@@ -3016,7 +3081,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3016
3081
|
url: context.url,
|
|
3017
3082
|
initialPayload: context.requestPayload,
|
|
3018
3083
|
env: context.env,
|
|
3019
|
-
label: context.
|
|
3084
|
+
label: context.labels,
|
|
3020
3085
|
retried: context.retried
|
|
3021
3086
|
});
|
|
3022
3087
|
try {
|
|
@@ -3854,10 +3919,12 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
|
|
|
3854
3919
|
await middlewareManager.dispatchDebug("onError", {
|
|
3855
3920
|
error: isInstanceOf(error, Error) ? error : new Error(formattedError.message)
|
|
3856
3921
|
});
|
|
3922
|
+
const stepName = getStepNameFromError(error)?.replace(/[\u0000-\u001F\u007F]+/g, " ").trim();
|
|
3857
3923
|
return new Response(JSON.stringify(formattedError), {
|
|
3858
3924
|
status: 500,
|
|
3859
3925
|
headers: {
|
|
3860
|
-
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
|
|
3926
|
+
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
3927
|
+
...stepName ? { [WORKFLOW_ERROR_STEP_NAME_HEADER]: stepName } : {}
|
|
3861
3928
|
}
|
|
3862
3929
|
});
|
|
3863
3930
|
}
|
package/hono.mjs
CHANGED
package/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as StepType, R as RawStep, W as WorkflowMiddleware, a as WorkflowClient, b as WorkflowReceiver, c as RouteFunction, d as WorkflowServeOptions, T as Telemetry, N as NotifyResponse, e as Waiter } from './types-
|
|
2
|
-
export { A as AsyncStepFunction, C as CallResponse, f as CallSettings, D as DetailedFinishCondition, g as Duration, E as ExclusiveValidationOptions, F as FailureFunctionPayload, h as FinishCondition, H as HeaderParams, I as InvokableWorkflow, i as InvokeStepResponse, j as InvokeWorkflowRequest, L as LazyInvokeStepParams, k as NotifyStepResponse, P as ParallelCallState, Q as QStashClientExtraConfig, l as RequiredExceptFields, m as Step, n as StepFunction, o as StepTypes, p as SyncStepFunction, q as WaitEventOptions, r as WaitRequest, s as WaitStepResponse, t as WorkflowAbort, u as WorkflowContext, v as WorkflowError, w as WorkflowNonRetryableError, x as WorkflowRetryAfterError } from './types-
|
|
1
|
+
import { S as StepType, R as RawStep, W as WorkflowMiddleware, a as WorkflowClient, b as WorkflowReceiver, c as RouteFunction, d as WorkflowServeOptions, T as Telemetry, N as NotifyResponse, e as Waiter } from './types-CekOpKvz.mjs';
|
|
2
|
+
export { A as AsyncStepFunction, C as CallResponse, f as CallSettings, D as DetailedFinishCondition, g as Duration, E as ExclusiveValidationOptions, F as FailureFunctionPayload, h as FinishCondition, H as HeaderParams, I as InvokableWorkflow, i as InvokeStepResponse, j as InvokeWorkflowRequest, L as LazyInvokeStepParams, k as NotifyStepResponse, P as ParallelCallState, Q as QStashClientExtraConfig, l as RequiredExceptFields, m as Step, n as StepFunction, o as StepTypes, p as SyncStepFunction, q as WaitEventOptions, r as WaitRequest, s as WaitStepResponse, t as WorkflowAbort, u as WorkflowContext, v as WorkflowError, w as WorkflowNonRetryableError, x as WorkflowRetryAfterError } from './types-CekOpKvz.mjs';
|
|
3
3
|
import { FlowControl, PublishRequest, HTTPMethods, Client as Client$1 } from '@upstash/qstash';
|
|
4
4
|
import 'zod';
|
|
5
5
|
|
|
@@ -183,6 +183,13 @@ type StepLogGroup = {
|
|
|
183
183
|
*/
|
|
184
184
|
steps: {
|
|
185
185
|
messageId: string;
|
|
186
|
+
/**
|
|
187
|
+
* name of the step being run.
|
|
188
|
+
*
|
|
189
|
+
* only available when the SDK reported it while the step was failing
|
|
190
|
+
* (via the `Upstash-Error-Step-Name` response header).
|
|
191
|
+
*/
|
|
192
|
+
stepName?: string;
|
|
186
193
|
state: "STEP_PROGRESS" | "STEP_RETRY" | "STEP_FAILED" | "STEP_CANCELED";
|
|
187
194
|
/**
|
|
188
195
|
* retries
|
|
@@ -247,6 +254,7 @@ type FailureFunctionLog = {
|
|
|
247
254
|
* Status code of the response from the failure function
|
|
248
255
|
*/
|
|
249
256
|
responseStatus?: number;
|
|
257
|
+
nextDeliveryTime?: number;
|
|
250
258
|
};
|
|
251
259
|
type WorkflowRunLog = {
|
|
252
260
|
/**
|
|
@@ -317,9 +325,18 @@ type WorkflowRunLog = {
|
|
|
317
325
|
*/
|
|
318
326
|
dlqId?: string;
|
|
319
327
|
/**
|
|
320
|
-
* Label of the workflow run
|
|
328
|
+
* Label of the workflow run.
|
|
329
|
+
*
|
|
330
|
+
* @deprecated Use `labels` instead. When a run has multiple labels, this
|
|
331
|
+
* field only contains the first one.
|
|
321
332
|
*/
|
|
322
333
|
label?: string;
|
|
334
|
+
/**
|
|
335
|
+
* Labels attached to the workflow run.
|
|
336
|
+
*
|
|
337
|
+
* A run can have multiple labels when triggered with `label: string[]`.
|
|
338
|
+
*/
|
|
339
|
+
labels?: string[];
|
|
323
340
|
};
|
|
324
341
|
type WorkflowRunLogs = {
|
|
325
342
|
cursor: string;
|
|
@@ -403,8 +420,11 @@ type TriggerOptions = {
|
|
|
403
420
|
* Label to apply to the workflow run.
|
|
404
421
|
*
|
|
405
422
|
* Can be used to filter the workflow run logs.
|
|
423
|
+
*
|
|
424
|
+
* Pass an array to attach multiple labels to a single workflow run; they are
|
|
425
|
+
* sent as a comma-separated value in the `Upstash-Label` header.
|
|
406
426
|
*/
|
|
407
|
-
label?: string;
|
|
427
|
+
label?: string | string[];
|
|
408
428
|
/**
|
|
409
429
|
* By default, Workflow SDK sends telemetry about SDK version, framework or runtime.
|
|
410
430
|
*
|
|
@@ -526,7 +546,14 @@ type UniversalFilterFields = {
|
|
|
526
546
|
fromDate?: Date | number;
|
|
527
547
|
toDate?: Date | number;
|
|
528
548
|
callerIp?: string;
|
|
529
|
-
|
|
549
|
+
/**
|
|
550
|
+
* Filter by label.
|
|
551
|
+
*
|
|
552
|
+
* Pass an array to match runs that have any of the given labels (OR semantics).
|
|
553
|
+
* For example, with runs labelled `[label_1, label_2]` and `[label_2, label_3]`,
|
|
554
|
+
* filtering by `[label_1, label_2]` returns both.
|
|
555
|
+
*/
|
|
556
|
+
label?: string | string[];
|
|
530
557
|
flowControlKey?: string;
|
|
531
558
|
};
|
|
532
559
|
/** Workflow-specific filter fields for DLQ and bulk endpoints. */
|
|
@@ -664,11 +691,20 @@ type DLQMessage = {
|
|
|
664
691
|
*/
|
|
665
692
|
failureCallbackInfo?: FailureCallbackInfo;
|
|
666
693
|
/**
|
|
667
|
-
*
|
|
694
|
+
* Label passed when triggering the workflow run.
|
|
695
|
+
*
|
|
696
|
+
* @deprecated Use `labels` instead. When a run has multiple labels, this
|
|
697
|
+
* field only contains the first one.
|
|
668
698
|
*/
|
|
669
699
|
label?: string;
|
|
700
|
+
/**
|
|
701
|
+
* Labels attached to the workflow run.
|
|
702
|
+
*
|
|
703
|
+
* A run can have multiple labels when triggered with `label: string[]`.
|
|
704
|
+
*/
|
|
705
|
+
labels?: string[];
|
|
670
706
|
};
|
|
671
|
-
type PublicDLQMessage = Pick<DLQMessage, "header" | "body" | "maxRetries" | "notBefore" | "createdAt" | "callerIP" | "workflowRunId" | "workflowCreatedAt" | "workflowUrl" | "responseStatus" | "responseHeader" | "responseBody" | "dlqId" | "failureCallback" | "failureCallbackInfo" | "label">;
|
|
707
|
+
type PublicDLQMessage = Pick<DLQMessage, "header" | "body" | "maxRetries" | "notBefore" | "createdAt" | "callerIP" | "workflowRunId" | "workflowCreatedAt" | "workflowUrl" | "responseStatus" | "responseHeader" | "responseBody" | "dlqId" | "failureCallback" | "failureCallbackInfo" | "label" | "labels">;
|
|
672
708
|
declare class DLQ {
|
|
673
709
|
private client;
|
|
674
710
|
constructor(client: Client$1);
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as StepType, R as RawStep, W as WorkflowMiddleware, a as WorkflowClient, b as WorkflowReceiver, c as RouteFunction, d as WorkflowServeOptions, T as Telemetry, N as NotifyResponse, e as Waiter } from './types-
|
|
2
|
-
export { A as AsyncStepFunction, C as CallResponse, f as CallSettings, D as DetailedFinishCondition, g as Duration, E as ExclusiveValidationOptions, F as FailureFunctionPayload, h as FinishCondition, H as HeaderParams, I as InvokableWorkflow, i as InvokeStepResponse, j as InvokeWorkflowRequest, L as LazyInvokeStepParams, k as NotifyStepResponse, P as ParallelCallState, Q as QStashClientExtraConfig, l as RequiredExceptFields, m as Step, n as StepFunction, o as StepTypes, p as SyncStepFunction, q as WaitEventOptions, r as WaitRequest, s as WaitStepResponse, t as WorkflowAbort, u as WorkflowContext, v as WorkflowError, w as WorkflowNonRetryableError, x as WorkflowRetryAfterError } from './types-
|
|
1
|
+
import { S as StepType, R as RawStep, W as WorkflowMiddleware, a as WorkflowClient, b as WorkflowReceiver, c as RouteFunction, d as WorkflowServeOptions, T as Telemetry, N as NotifyResponse, e as Waiter } from './types-CekOpKvz.js';
|
|
2
|
+
export { A as AsyncStepFunction, C as CallResponse, f as CallSettings, D as DetailedFinishCondition, g as Duration, E as ExclusiveValidationOptions, F as FailureFunctionPayload, h as FinishCondition, H as HeaderParams, I as InvokableWorkflow, i as InvokeStepResponse, j as InvokeWorkflowRequest, L as LazyInvokeStepParams, k as NotifyStepResponse, P as ParallelCallState, Q as QStashClientExtraConfig, l as RequiredExceptFields, m as Step, n as StepFunction, o as StepTypes, p as SyncStepFunction, q as WaitEventOptions, r as WaitRequest, s as WaitStepResponse, t as WorkflowAbort, u as WorkflowContext, v as WorkflowError, w as WorkflowNonRetryableError, x as WorkflowRetryAfterError } from './types-CekOpKvz.js';
|
|
3
3
|
import { FlowControl, PublishRequest, HTTPMethods, Client as Client$1 } from '@upstash/qstash';
|
|
4
4
|
import 'zod';
|
|
5
5
|
|
|
@@ -183,6 +183,13 @@ type StepLogGroup = {
|
|
|
183
183
|
*/
|
|
184
184
|
steps: {
|
|
185
185
|
messageId: string;
|
|
186
|
+
/**
|
|
187
|
+
* name of the step being run.
|
|
188
|
+
*
|
|
189
|
+
* only available when the SDK reported it while the step was failing
|
|
190
|
+
* (via the `Upstash-Error-Step-Name` response header).
|
|
191
|
+
*/
|
|
192
|
+
stepName?: string;
|
|
186
193
|
state: "STEP_PROGRESS" | "STEP_RETRY" | "STEP_FAILED" | "STEP_CANCELED";
|
|
187
194
|
/**
|
|
188
195
|
* retries
|
|
@@ -247,6 +254,7 @@ type FailureFunctionLog = {
|
|
|
247
254
|
* Status code of the response from the failure function
|
|
248
255
|
*/
|
|
249
256
|
responseStatus?: number;
|
|
257
|
+
nextDeliveryTime?: number;
|
|
250
258
|
};
|
|
251
259
|
type WorkflowRunLog = {
|
|
252
260
|
/**
|
|
@@ -317,9 +325,18 @@ type WorkflowRunLog = {
|
|
|
317
325
|
*/
|
|
318
326
|
dlqId?: string;
|
|
319
327
|
/**
|
|
320
|
-
* Label of the workflow run
|
|
328
|
+
* Label of the workflow run.
|
|
329
|
+
*
|
|
330
|
+
* @deprecated Use `labels` instead. When a run has multiple labels, this
|
|
331
|
+
* field only contains the first one.
|
|
321
332
|
*/
|
|
322
333
|
label?: string;
|
|
334
|
+
/**
|
|
335
|
+
* Labels attached to the workflow run.
|
|
336
|
+
*
|
|
337
|
+
* A run can have multiple labels when triggered with `label: string[]`.
|
|
338
|
+
*/
|
|
339
|
+
labels?: string[];
|
|
323
340
|
};
|
|
324
341
|
type WorkflowRunLogs = {
|
|
325
342
|
cursor: string;
|
|
@@ -403,8 +420,11 @@ type TriggerOptions = {
|
|
|
403
420
|
* Label to apply to the workflow run.
|
|
404
421
|
*
|
|
405
422
|
* Can be used to filter the workflow run logs.
|
|
423
|
+
*
|
|
424
|
+
* Pass an array to attach multiple labels to a single workflow run; they are
|
|
425
|
+
* sent as a comma-separated value in the `Upstash-Label` header.
|
|
406
426
|
*/
|
|
407
|
-
label?: string;
|
|
427
|
+
label?: string | string[];
|
|
408
428
|
/**
|
|
409
429
|
* By default, Workflow SDK sends telemetry about SDK version, framework or runtime.
|
|
410
430
|
*
|
|
@@ -526,7 +546,14 @@ type UniversalFilterFields = {
|
|
|
526
546
|
fromDate?: Date | number;
|
|
527
547
|
toDate?: Date | number;
|
|
528
548
|
callerIp?: string;
|
|
529
|
-
|
|
549
|
+
/**
|
|
550
|
+
* Filter by label.
|
|
551
|
+
*
|
|
552
|
+
* Pass an array to match runs that have any of the given labels (OR semantics).
|
|
553
|
+
* For example, with runs labelled `[label_1, label_2]` and `[label_2, label_3]`,
|
|
554
|
+
* filtering by `[label_1, label_2]` returns both.
|
|
555
|
+
*/
|
|
556
|
+
label?: string | string[];
|
|
530
557
|
flowControlKey?: string;
|
|
531
558
|
};
|
|
532
559
|
/** Workflow-specific filter fields for DLQ and bulk endpoints. */
|
|
@@ -664,11 +691,20 @@ type DLQMessage = {
|
|
|
664
691
|
*/
|
|
665
692
|
failureCallbackInfo?: FailureCallbackInfo;
|
|
666
693
|
/**
|
|
667
|
-
*
|
|
694
|
+
* Label passed when triggering the workflow run.
|
|
695
|
+
*
|
|
696
|
+
* @deprecated Use `labels` instead. When a run has multiple labels, this
|
|
697
|
+
* field only contains the first one.
|
|
668
698
|
*/
|
|
669
699
|
label?: string;
|
|
700
|
+
/**
|
|
701
|
+
* Labels attached to the workflow run.
|
|
702
|
+
*
|
|
703
|
+
* A run can have multiple labels when triggered with `label: string[]`.
|
|
704
|
+
*/
|
|
705
|
+
labels?: string[];
|
|
670
706
|
};
|
|
671
|
-
type PublicDLQMessage = Pick<DLQMessage, "header" | "body" | "maxRetries" | "notBefore" | "createdAt" | "callerIP" | "workflowRunId" | "workflowCreatedAt" | "workflowUrl" | "responseStatus" | "responseHeader" | "responseBody" | "dlqId" | "failureCallback" | "failureCallbackInfo" | "label">;
|
|
707
|
+
type PublicDLQMessage = Pick<DLQMessage, "header" | "body" | "maxRetries" | "notBefore" | "createdAt" | "callerIP" | "workflowRunId" | "workflowCreatedAt" | "workflowUrl" | "responseStatus" | "responseHeader" | "responseBody" | "dlqId" | "failureCallback" | "failureCallbackInfo" | "label" | "labels">;
|
|
672
708
|
declare class DLQ {
|
|
673
709
|
private client;
|
|
674
710
|
constructor(client: Client$1);
|
package/index.js
CHANGED
|
@@ -110,6 +110,22 @@ var formatWorkflowError = (error) => {
|
|
|
110
110
|
message: `An error occured while executing workflow: '${typeof error === "string" ? error : JSON.stringify(error)}'`
|
|
111
111
|
};
|
|
112
112
|
};
|
|
113
|
+
var attachStepNameToError = (error, stepName) => {
|
|
114
|
+
if (typeof error !== "object" || error === null) return;
|
|
115
|
+
if ("errorStepName" in error) return;
|
|
116
|
+
if (!Object.isExtensible(error)) return;
|
|
117
|
+
try {
|
|
118
|
+
error.errorStepName = stepName;
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
var getStepNameFromError = (error) => {
|
|
123
|
+
if (typeof error === "object" && error !== null) {
|
|
124
|
+
const stepName = error.errorStepName;
|
|
125
|
+
return typeof stepName === "string" ? stepName : void 0;
|
|
126
|
+
}
|
|
127
|
+
return void 0;
|
|
128
|
+
};
|
|
113
129
|
function getConstructorName(obj) {
|
|
114
130
|
if (obj === null || obj === void 0) {
|
|
115
131
|
return null;
|
|
@@ -270,12 +286,13 @@ var WORKFLOW_RETRIED_HEADER = "Upstash-Retried";
|
|
|
270
286
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
271
287
|
var WORKFLOW_UNKOWN_SDK_VERSION_HEADER = "Upstash-Workflow-Unknown-Sdk";
|
|
272
288
|
var WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER = "upstash-workflow-trigger-by-sdk";
|
|
289
|
+
var WORKFLOW_ERROR_STEP_NAME_HEADER = "Upstash-Error-Step-Name";
|
|
273
290
|
var WORKFLOW_PROTOCOL_VERSION = "1";
|
|
274
291
|
var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
|
|
275
292
|
var DEFAULT_CONTENT_TYPE = "application/json";
|
|
276
293
|
var NO_CONCURRENCY = 1;
|
|
277
294
|
var DEFAULT_RETRIES = 3;
|
|
278
|
-
var VERSION = "v1.2
|
|
295
|
+
var VERSION = "v1.3.2";
|
|
279
296
|
var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
|
|
280
297
|
var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
281
298
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
@@ -290,6 +307,31 @@ var import_qstash4 = require("@upstash/qstash");
|
|
|
290
307
|
// src/utils.ts
|
|
291
308
|
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
292
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
|
+
}
|
|
293
335
|
function getRandomInt() {
|
|
294
336
|
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
295
337
|
}
|
|
@@ -825,8 +867,8 @@ var triggerFirstInvocation = async (params) => {
|
|
|
825
867
|
if (unknownSdk) {
|
|
826
868
|
headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
|
|
827
869
|
}
|
|
828
|
-
if (workflowContext.
|
|
829
|
-
headers[WORKFLOW_LABEL_HEADER] = workflowContext.
|
|
870
|
+
if (workflowContext.labels.length > 0) {
|
|
871
|
+
headers[WORKFLOW_LABEL_HEADER] = serializeLabel(workflowContext.labels);
|
|
830
872
|
}
|
|
831
873
|
const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
|
|
832
874
|
return {
|
|
@@ -1628,8 +1670,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1628
1670
|
});
|
|
1629
1671
|
triggerHeaders["Upstash-Workflow-Invoke"] = "true";
|
|
1630
1672
|
if (label) {
|
|
1631
|
-
|
|
1632
|
-
triggerHeaders[
|
|
1673
|
+
const labelHeader = serializeLabel(label);
|
|
1674
|
+
triggerHeaders[WORKFLOW_LABEL_HEADER] = labelHeader;
|
|
1675
|
+
triggerHeaders[`upstash-forward-${WORKFLOW_LABEL_HEADER}`] = labelHeader;
|
|
1633
1676
|
}
|
|
1634
1677
|
return { headers: triggerHeaders, contentType };
|
|
1635
1678
|
}
|
|
@@ -1972,7 +2015,13 @@ var submitSingleStep = async ({
|
|
|
1972
2015
|
await dispatchLifecycle("beforeExecution", {
|
|
1973
2016
|
stepName: lazyStep.stepName
|
|
1974
2017
|
});
|
|
1975
|
-
|
|
2018
|
+
let resultStep;
|
|
2019
|
+
try {
|
|
2020
|
+
resultStep = await lazyStep.getResultStep(concurrency, stepId);
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
attachStepNameToError(error, lazyStep.stepName);
|
|
2023
|
+
throw error;
|
|
2024
|
+
}
|
|
1976
2025
|
const { headers } = lazyStep.getHeaders({
|
|
1977
2026
|
context,
|
|
1978
2027
|
step: resultStep,
|
|
@@ -2195,9 +2244,11 @@ var AutoExecutor = class _AutoExecutor {
|
|
|
2195
2244
|
if (isInstanceOf(error, WorkflowAbort) || isInstanceOf(error, import_qstash5.QstashError) && error.status === 400 || isInstanceOf(error, import_qstash5.QstashError) && error.status === 412) {
|
|
2196
2245
|
throw error;
|
|
2197
2246
|
}
|
|
2198
|
-
|
|
2247
|
+
const wrappedError = new WorkflowError(
|
|
2199
2248
|
`Error submitting steps to QStash in partial parallel step execution: ${error}`
|
|
2200
2249
|
);
|
|
2250
|
+
attachStepNameToError(wrappedError, parallelSteps[stepIndex].stepName);
|
|
2251
|
+
throw wrappedError;
|
|
2201
2252
|
}
|
|
2202
2253
|
break;
|
|
2203
2254
|
}
|
|
@@ -2703,9 +2754,10 @@ var WorkflowContext = class {
|
|
|
2703
2754
|
*/
|
|
2704
2755
|
env;
|
|
2705
2756
|
/**
|
|
2706
|
-
*
|
|
2757
|
+
* Labels attached to the workflow run.
|
|
2707
2758
|
*
|
|
2708
|
-
* Can be used to filter the workflow run logs.
|
|
2759
|
+
* Can be used to filter the workflow run logs. A run can have multiple
|
|
2760
|
+
* labels when triggered with `label: string[]`.
|
|
2709
2761
|
*
|
|
2710
2762
|
* Can be set by passing a `label` parameter when triggering the workflow
|
|
2711
2763
|
* with `client.trigger`:
|
|
@@ -2713,11 +2765,20 @@ var WorkflowContext = class {
|
|
|
2713
2765
|
* ```ts
|
|
2714
2766
|
* await client.trigger({
|
|
2715
2767
|
* url: "https://workflow-endpoint.com",
|
|
2716
|
-
* label: "
|
|
2768
|
+
* label: ["label-1", "label-2"]
|
|
2717
2769
|
* });
|
|
2718
2770
|
* ```
|
|
2719
2771
|
*/
|
|
2720
|
-
|
|
2772
|
+
labels;
|
|
2773
|
+
/**
|
|
2774
|
+
* Label of the workflow run.
|
|
2775
|
+
*
|
|
2776
|
+
* @deprecated Use `labels` instead. When a run has multiple labels, this
|
|
2777
|
+
* only returns the first one.
|
|
2778
|
+
*/
|
|
2779
|
+
get label() {
|
|
2780
|
+
return this.labels[0];
|
|
2781
|
+
}
|
|
2721
2782
|
/**
|
|
2722
2783
|
* Number of times QStash has retried delivering the current request.
|
|
2723
2784
|
*
|
|
@@ -2748,7 +2809,7 @@ var WorkflowContext = class {
|
|
|
2748
2809
|
this.headers = headers;
|
|
2749
2810
|
this.requestPayload = initialPayload;
|
|
2750
2811
|
this.env = env ?? {};
|
|
2751
|
-
this.
|
|
2812
|
+
this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
|
|
2752
2813
|
this.retried = retried ?? 0;
|
|
2753
2814
|
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
2754
2815
|
middlewareManagerInstance.assignContext(this);
|
|
@@ -2829,8 +2890,10 @@ var WorkflowContext = class {
|
|
|
2829
2890
|
await this.addStep(new LazySleepUntilStep(this, stepName, time));
|
|
2830
2891
|
}
|
|
2831
2892
|
async call(stepName, settings) {
|
|
2893
|
+
validateFlowControl(settings.flowControl);
|
|
2832
2894
|
let callStep;
|
|
2833
2895
|
if ("workflow" in settings) {
|
|
2896
|
+
validateLabel(settings.label);
|
|
2834
2897
|
const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
|
|
2835
2898
|
const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
|
|
2836
2899
|
callStep = new LazyCallStep({
|
|
@@ -2938,6 +3001,8 @@ var WorkflowContext = class {
|
|
|
2938
3001
|
);
|
|
2939
3002
|
}
|
|
2940
3003
|
async invoke(stepName, settings) {
|
|
3004
|
+
validateLabel(settings.label);
|
|
3005
|
+
validateFlowControl(settings.flowControl);
|
|
2941
3006
|
return await this.addStep(
|
|
2942
3007
|
new LazyInvokeStep(this, stepName, settings)
|
|
2943
3008
|
);
|
|
@@ -3023,7 +3088,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3023
3088
|
url: context.url,
|
|
3024
3089
|
initialPayload: context.requestPayload,
|
|
3025
3090
|
env: context.env,
|
|
3026
|
-
label: context.
|
|
3091
|
+
label: context.labels,
|
|
3027
3092
|
retried: context.retried
|
|
3028
3093
|
});
|
|
3029
3094
|
try {
|
|
@@ -3861,10 +3926,12 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3861
3926
|
await middlewareManager.dispatchDebug("onError", {
|
|
3862
3927
|
error: isInstanceOf(error, Error) ? error : new Error(formattedError.message)
|
|
3863
3928
|
});
|
|
3929
|
+
const stepName = getStepNameFromError(error)?.replace(/[\u0000-\u001F\u007F]+/g, " ").trim();
|
|
3864
3930
|
return new Response(JSON.stringify(formattedError), {
|
|
3865
3931
|
status: 500,
|
|
3866
3932
|
headers: {
|
|
3867
|
-
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION
|
|
3933
|
+
[WORKFLOW_PROTOCOL_VERSION_HEADER]: WORKFLOW_PROTOCOL_VERSION,
|
|
3934
|
+
...stepName ? { [WORKFLOW_ERROR_STEP_NAME_HEADER]: stepName } : {}
|
|
3868
3935
|
}
|
|
3869
3936
|
});
|
|
3870
3937
|
}
|
|
@@ -4111,13 +4178,15 @@ var Client4 = class {
|
|
|
4111
4178
|
const isBatchInput = Array.isArray(params);
|
|
4112
4179
|
const options = isBatchInput ? params : [params];
|
|
4113
4180
|
const invocations = options.map((option) => {
|
|
4181
|
+
validateLabel(option.label);
|
|
4182
|
+
validateFlowControl(option.flowControl);
|
|
4114
4183
|
const failureUrl = option.failureUrl ?? option.url;
|
|
4115
4184
|
const finalWorkflowRunId = getWorkflowRunId(option.workflowRunId);
|
|
4116
4185
|
const context = new WorkflowContext({
|
|
4117
4186
|
qstashClient: this.client,
|
|
4118
4187
|
headers: new Headers({
|
|
4119
4188
|
...option.headers ?? {},
|
|
4120
|
-
...option.label ? { [WORKFLOW_LABEL_HEADER]: option.label } : {}
|
|
4189
|
+
...option.label ? { [WORKFLOW_LABEL_HEADER]: serializeLabel(option.label) } : {}
|
|
4121
4190
|
}),
|
|
4122
4191
|
initialPayload: option.body,
|
|
4123
4192
|
steps: [],
|
package/index.mjs
CHANGED
|
@@ -15,9 +15,12 @@ import {
|
|
|
15
15
|
makeNotifyRequest,
|
|
16
16
|
normalizeCursor,
|
|
17
17
|
prepareFlowControl,
|
|
18
|
+
serializeLabel,
|
|
18
19
|
serve,
|
|
19
|
-
triggerFirstInvocation
|
|
20
|
-
|
|
20
|
+
triggerFirstInvocation,
|
|
21
|
+
validateFlowControl,
|
|
22
|
+
validateLabel
|
|
23
|
+
} from "./chunk-ICDNN4UW.mjs";
|
|
21
24
|
|
|
22
25
|
// src/client/index.ts
|
|
23
26
|
import { Client as QStashClient } from "@upstash/qstash";
|
|
@@ -248,13 +251,15 @@ var Client = class {
|
|
|
248
251
|
const isBatchInput = Array.isArray(params);
|
|
249
252
|
const options = isBatchInput ? params : [params];
|
|
250
253
|
const invocations = options.map((option) => {
|
|
254
|
+
validateLabel(option.label);
|
|
255
|
+
validateFlowControl(option.flowControl);
|
|
251
256
|
const failureUrl = option.failureUrl ?? option.url;
|
|
252
257
|
const finalWorkflowRunId = getWorkflowRunId(option.workflowRunId);
|
|
253
258
|
const context = new WorkflowContext({
|
|
254
259
|
qstashClient: this.client,
|
|
255
260
|
headers: new Headers({
|
|
256
261
|
...option.headers ?? {},
|
|
257
|
-
...option.label ? { [WORKFLOW_LABEL_HEADER]: option.label } : {}
|
|
262
|
+
...option.label ? { [WORKFLOW_LABEL_HEADER]: serializeLabel(option.label) } : {}
|
|
258
263
|
}),
|
|
259
264
|
initialPayload: option.body,
|
|
260
265
|
steps: [],
|