@upstash/workflow 1.2.1 → 1.3.1
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/README.md +10 -0
- package/astro.d.mts +2 -2
- package/astro.d.ts +2 -2
- package/astro.js +80 -14
- package/astro.mjs +1 -1
- package/{chunk-THS5AX2D.mjs → chunk-CWCCIOXR.mjs} +83 -14
- package/cloudflare.d.mts +2 -2
- package/cloudflare.d.ts +2 -2
- package/cloudflare.js +80 -14
- package/cloudflare.mjs +1 -1
- package/express.d.mts +2 -2
- package/express.d.ts +2 -2
- package/express.js +80 -14
- package/express.mjs +1 -1
- package/h3.d.mts +2 -2
- package/h3.d.ts +2 -2
- package/h3.js +80 -14
- package/h3.mjs +1 -1
- package/hono.d.mts +2 -2
- package/hono.d.ts +2 -2
- package/hono.js +80 -14
- package/hono.mjs +1 -1
- package/index.d.mts +24 -5
- package/index.d.ts +24 -5
- package/index.js +83 -15
- package/index.mjs +8 -3
- package/nextjs.d.mts +2 -2
- package/nextjs.d.ts +2 -2
- package/nextjs.js +80 -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 +80 -14
- package/react-router.mjs +1 -1
- package/{serve-many-C6sa_DxN.d.mts → serve-many-CG3BFvO3.d.mts} +1 -1
- package/{serve-many-B-fe7bh7.d.ts → serve-many-iJF1IUXk.d.ts} +1 -1
- package/solidjs.d.mts +1 -1
- package/solidjs.d.ts +1 -1
- package/solidjs.js +80 -14
- package/solidjs.mjs +1 -1
- package/svelte.d.mts +2 -2
- package/svelte.d.ts +2 -2
- package/svelte.js +80 -14
- package/svelte.mjs +1 -1
- package/tanstack.d.mts +2 -2
- package/tanstack.d.ts +2 -2
- package/tanstack.js +80 -14
- package/tanstack.mjs +1 -1
- package/{types-B2S08hRU.d.ts → types-CekOpKvz.d.mts} +23 -7
- package/{types-B2S08hRU.d.mts → types-CekOpKvz.d.ts} +23 -7
package/index.js
CHANGED
|
@@ -266,6 +266,7 @@ var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
|
|
|
266
266
|
var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
|
|
267
267
|
var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
|
|
268
268
|
var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
|
|
269
|
+
var WORKFLOW_RETRIED_HEADER = "Upstash-Retried";
|
|
269
270
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
270
271
|
var WORKFLOW_UNKOWN_SDK_VERSION_HEADER = "Upstash-Workflow-Unknown-Sdk";
|
|
271
272
|
var WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER = "upstash-workflow-trigger-by-sdk";
|
|
@@ -274,7 +275,7 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
|
|
|
274
275
|
var DEFAULT_CONTENT_TYPE = "application/json";
|
|
275
276
|
var NO_CONCURRENCY = 1;
|
|
276
277
|
var DEFAULT_RETRIES = 3;
|
|
277
|
-
var VERSION = "v1.
|
|
278
|
+
var VERSION = "v1.2.1";
|
|
278
279
|
var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
|
|
279
280
|
var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
280
281
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
@@ -289,6 +290,31 @@ var import_qstash4 = require("@upstash/qstash");
|
|
|
289
290
|
// src/utils.ts
|
|
290
291
|
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
291
292
|
var NANOID_LENGTH = 21;
|
|
293
|
+
var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
|
|
294
|
+
function validateLabel(label) {
|
|
295
|
+
if (label === void 0) return;
|
|
296
|
+
const labels = Array.isArray(label) ? label : [label];
|
|
297
|
+
if (labels.length === 0) {
|
|
298
|
+
throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
|
|
299
|
+
}
|
|
300
|
+
for (const value of labels) {
|
|
301
|
+
if (!RESOURCE_NAME_PATTERN.test(value)) {
|
|
302
|
+
throw new WorkflowNonRetryableError(
|
|
303
|
+
`Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
function serializeLabel(label) {
|
|
309
|
+
return Array.isArray(label) ? label.join(",") : label;
|
|
310
|
+
}
|
|
311
|
+
function validateFlowControl(flowControl) {
|
|
312
|
+
if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
|
|
313
|
+
throw new WorkflowNonRetryableError(
|
|
314
|
+
`Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
292
318
|
function getRandomInt() {
|
|
293
319
|
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
294
320
|
}
|
|
@@ -824,8 +850,8 @@ var triggerFirstInvocation = async (params) => {
|
|
|
824
850
|
if (unknownSdk) {
|
|
825
851
|
headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
|
|
826
852
|
}
|
|
827
|
-
if (workflowContext.
|
|
828
|
-
headers[WORKFLOW_LABEL_HEADER] = workflowContext.
|
|
853
|
+
if (workflowContext.labels.length > 0) {
|
|
854
|
+
headers[WORKFLOW_LABEL_HEADER] = serializeLabel(workflowContext.labels);
|
|
829
855
|
}
|
|
830
856
|
const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
|
|
831
857
|
return {
|
|
@@ -929,6 +955,9 @@ var recreateUserHeaders = (headers) => {
|
|
|
929
955
|
}
|
|
930
956
|
return filteredHeaders;
|
|
931
957
|
};
|
|
958
|
+
var isThirdPartyCallResult = (request) => {
|
|
959
|
+
return request.headers.get("Upstash-Workflow-Callback") !== null;
|
|
960
|
+
};
|
|
932
961
|
var handleThirdPartyCallResult = async ({
|
|
933
962
|
request,
|
|
934
963
|
requestPayload,
|
|
@@ -938,7 +967,7 @@ var handleThirdPartyCallResult = async ({
|
|
|
938
967
|
middlewareManager
|
|
939
968
|
}) => {
|
|
940
969
|
try {
|
|
941
|
-
if (request
|
|
970
|
+
if (isThirdPartyCallResult(request)) {
|
|
942
971
|
let callbackPayload;
|
|
943
972
|
if (requestPayload) {
|
|
944
973
|
callbackPayload = requestPayload;
|
|
@@ -1624,8 +1653,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1624
1653
|
});
|
|
1625
1654
|
triggerHeaders["Upstash-Workflow-Invoke"] = "true";
|
|
1626
1655
|
if (label) {
|
|
1627
|
-
|
|
1628
|
-
triggerHeaders[
|
|
1656
|
+
const labelHeader = serializeLabel(label);
|
|
1657
|
+
triggerHeaders[WORKFLOW_LABEL_HEADER] = labelHeader;
|
|
1658
|
+
triggerHeaders[`upstash-forward-${WORKFLOW_LABEL_HEADER}`] = labelHeader;
|
|
1629
1659
|
}
|
|
1630
1660
|
return { headers: triggerHeaders, contentType };
|
|
1631
1661
|
}
|
|
@@ -2699,9 +2729,10 @@ var WorkflowContext = class {
|
|
|
2699
2729
|
*/
|
|
2700
2730
|
env;
|
|
2701
2731
|
/**
|
|
2702
|
-
*
|
|
2732
|
+
* Labels attached to the workflow run.
|
|
2703
2733
|
*
|
|
2704
|
-
* Can be used to filter the workflow run logs.
|
|
2734
|
+
* Can be used to filter the workflow run logs. A run can have multiple
|
|
2735
|
+
* labels when triggered with `label: string[]`.
|
|
2705
2736
|
*
|
|
2706
2737
|
* Can be set by passing a `label` parameter when triggering the workflow
|
|
2707
2738
|
* with `client.trigger`:
|
|
@@ -2709,11 +2740,27 @@ var WorkflowContext = class {
|
|
|
2709
2740
|
* ```ts
|
|
2710
2741
|
* await client.trigger({
|
|
2711
2742
|
* url: "https://workflow-endpoint.com",
|
|
2712
|
-
* label: "
|
|
2743
|
+
* label: ["label-1", "label-2"]
|
|
2713
2744
|
* });
|
|
2714
2745
|
* ```
|
|
2715
2746
|
*/
|
|
2716
|
-
|
|
2747
|
+
labels;
|
|
2748
|
+
/**
|
|
2749
|
+
* Label of the workflow run.
|
|
2750
|
+
*
|
|
2751
|
+
* @deprecated Use `labels` instead. When a run has multiple labels, this
|
|
2752
|
+
* only returns the first one.
|
|
2753
|
+
*/
|
|
2754
|
+
get label() {
|
|
2755
|
+
return this.labels[0];
|
|
2756
|
+
}
|
|
2757
|
+
/**
|
|
2758
|
+
* Number of times QStash has retried delivering the current request.
|
|
2759
|
+
*
|
|
2760
|
+
* Sourced from the `Upstash-Retried` header. `0` on the first delivery,
|
|
2761
|
+
* `1` on the first retry, `2` on the second, and so on.
|
|
2762
|
+
*/
|
|
2763
|
+
retried;
|
|
2717
2764
|
constructor({
|
|
2718
2765
|
qstashClient,
|
|
2719
2766
|
workflowRunId,
|
|
@@ -2726,6 +2773,7 @@ var WorkflowContext = class {
|
|
|
2726
2773
|
telemetry,
|
|
2727
2774
|
invokeCount,
|
|
2728
2775
|
label,
|
|
2776
|
+
retried,
|
|
2729
2777
|
middlewareManager
|
|
2730
2778
|
}) {
|
|
2731
2779
|
this.qstashClient = qstashClient;
|
|
@@ -2736,7 +2784,8 @@ var WorkflowContext = class {
|
|
|
2736
2784
|
this.headers = headers;
|
|
2737
2785
|
this.requestPayload = initialPayload;
|
|
2738
2786
|
this.env = env ?? {};
|
|
2739
|
-
this.
|
|
2787
|
+
this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
|
|
2788
|
+
this.retried = retried ?? 0;
|
|
2740
2789
|
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
2741
2790
|
middlewareManagerInstance.assignContext(this);
|
|
2742
2791
|
this.executor = new AutoExecutor(
|
|
@@ -2816,8 +2865,10 @@ var WorkflowContext = class {
|
|
|
2816
2865
|
await this.addStep(new LazySleepUntilStep(this, stepName, time));
|
|
2817
2866
|
}
|
|
2818
2867
|
async call(stepName, settings) {
|
|
2868
|
+
validateFlowControl(settings.flowControl);
|
|
2819
2869
|
let callStep;
|
|
2820
2870
|
if ("workflow" in settings) {
|
|
2871
|
+
validateLabel(settings.label);
|
|
2821
2872
|
const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
|
|
2822
2873
|
const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
|
|
2823
2874
|
callStep = new LazyCallStep({
|
|
@@ -2925,6 +2976,8 @@ var WorkflowContext = class {
|
|
|
2925
2976
|
);
|
|
2926
2977
|
}
|
|
2927
2978
|
async invoke(stepName, settings) {
|
|
2979
|
+
validateLabel(settings.label);
|
|
2980
|
+
validateFlowControl(settings.flowControl);
|
|
2928
2981
|
return await this.addStep(
|
|
2929
2982
|
new LazyInvokeStep(this, stepName, settings)
|
|
2930
2983
|
);
|
|
@@ -3000,7 +3053,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3000
3053
|
const disabledContext = new _DisabledWorkflowContext({
|
|
3001
3054
|
qstashClient: new import_qstash10.Client({
|
|
3002
3055
|
baseUrl: "disabled-client",
|
|
3003
|
-
token: "disabled-client"
|
|
3056
|
+
token: "disabled-client",
|
|
3057
|
+
devMode: false
|
|
3004
3058
|
}),
|
|
3005
3059
|
workflowRunId: context.workflowRunId,
|
|
3006
3060
|
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
@@ -3009,7 +3063,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3009
3063
|
url: context.url,
|
|
3010
3064
|
initialPayload: context.requestPayload,
|
|
3011
3065
|
env: context.env,
|
|
3012
|
-
label: context.
|
|
3066
|
+
label: context.labels,
|
|
3067
|
+
retried: context.retried
|
|
3013
3068
|
});
|
|
3014
3069
|
try {
|
|
3015
3070
|
await routeFunction(disabledContext);
|
|
@@ -3225,6 +3280,7 @@ var handleFailure = async ({
|
|
|
3225
3280
|
errorMessage = `Couldn't parse 'failResponse' in 'failureFunction', received: '${decodedBody}'`;
|
|
3226
3281
|
}
|
|
3227
3282
|
const userHeaders = recreateUserHeaders(request.headers);
|
|
3283
|
+
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
3228
3284
|
const workflowContext = new WorkflowContext({
|
|
3229
3285
|
qstashClient,
|
|
3230
3286
|
workflowRunId,
|
|
@@ -3236,6 +3292,7 @@ var handleFailure = async ({
|
|
|
3236
3292
|
telemetry: void 0,
|
|
3237
3293
|
// not going to make requests in authentication check
|
|
3238
3294
|
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
|
|
3295
|
+
retried,
|
|
3239
3296
|
workflowRunCreatedAt: workflowCreatedAt,
|
|
3240
3297
|
middlewareManager: void 0
|
|
3241
3298
|
});
|
|
@@ -3395,6 +3452,9 @@ var getReceiver = (environment, receiverConfig, region) => {
|
|
|
3395
3452
|
if (receiverConfig === "set-to-undefined") {
|
|
3396
3453
|
return void 0;
|
|
3397
3454
|
}
|
|
3455
|
+
if (isQStashDevModeEnabled(environment)) {
|
|
3456
|
+
return new import_qstash11.Receiver({ devMode: true });
|
|
3457
|
+
}
|
|
3398
3458
|
const receiverEnv = readReceiverEnvironmentVariables(environment, region);
|
|
3399
3459
|
return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new import_qstash11.Receiver({
|
|
3400
3460
|
currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
|
|
@@ -3404,6 +3464,10 @@ var getReceiver = (environment, receiverConfig, region) => {
|
|
|
3404
3464
|
return receiverConfig;
|
|
3405
3465
|
}
|
|
3406
3466
|
};
|
|
3467
|
+
var isQStashDevModeEnabled = (env) => {
|
|
3468
|
+
const value = env.QSTASH_DEV;
|
|
3469
|
+
return value === "true" || value === "1";
|
|
3470
|
+
};
|
|
3407
3471
|
var getQStashHandlerOptions = (...params) => {
|
|
3408
3472
|
const handlers = getQStashHandlers(...params);
|
|
3409
3473
|
return {
|
|
@@ -3713,12 +3777,13 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3713
3777
|
);
|
|
3714
3778
|
}
|
|
3715
3779
|
const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
|
|
3780
|
+
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
3716
3781
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
3717
3782
|
const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
3718
3783
|
const workflowContext = new WorkflowContext({
|
|
3719
3784
|
qstashClient: regionalClient,
|
|
3720
3785
|
workflowRunId,
|
|
3721
|
-
initialPayload: initialPayloadParser(rawInitialPayload),
|
|
3786
|
+
initialPayload: isThirdPartyCallResult(request) ? JSON.parse(rawInitialPayload) : initialPayloadParser(rawInitialPayload),
|
|
3722
3787
|
headers: recreateUserHeaders(request.headers),
|
|
3723
3788
|
steps,
|
|
3724
3789
|
url: workflowUrl,
|
|
@@ -3726,6 +3791,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3726
3791
|
telemetry,
|
|
3727
3792
|
invokeCount,
|
|
3728
3793
|
label,
|
|
3794
|
+
retried,
|
|
3729
3795
|
workflowRunCreatedAt: Number(workflowRunCreatedAt),
|
|
3730
3796
|
middlewareManager
|
|
3731
3797
|
});
|
|
@@ -4085,13 +4151,15 @@ var Client4 = class {
|
|
|
4085
4151
|
const isBatchInput = Array.isArray(params);
|
|
4086
4152
|
const options = isBatchInput ? params : [params];
|
|
4087
4153
|
const invocations = options.map((option) => {
|
|
4154
|
+
validateLabel(option.label);
|
|
4155
|
+
validateFlowControl(option.flowControl);
|
|
4088
4156
|
const failureUrl = option.failureUrl ?? option.url;
|
|
4089
4157
|
const finalWorkflowRunId = getWorkflowRunId(option.workflowRunId);
|
|
4090
4158
|
const context = new WorkflowContext({
|
|
4091
4159
|
qstashClient: this.client,
|
|
4092
4160
|
headers: new Headers({
|
|
4093
4161
|
...option.headers ?? {},
|
|
4094
|
-
...option.label ? { [WORKFLOW_LABEL_HEADER]: option.label } : {}
|
|
4162
|
+
...option.label ? { [WORKFLOW_LABEL_HEADER]: serializeLabel(option.label) } : {}
|
|
4095
4163
|
}),
|
|
4096
4164
|
initialPayload: option.body,
|
|
4097
4165
|
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-CWCCIOXR.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: [],
|
package/nextjs.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
|
|
2
|
-
import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-
|
|
3
|
-
import { s as serveManyBase } from './serve-many-
|
|
2
|
+
import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-CekOpKvz.mjs';
|
|
3
|
+
import { s as serveManyBase } from './serve-many-CG3BFvO3.mjs';
|
|
4
4
|
import '@upstash/qstash';
|
|
5
5
|
import 'zod';
|
|
6
6
|
|
package/nextjs.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
|
|
2
|
-
import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-
|
|
3
|
-
import { s as serveManyBase } from './serve-many-
|
|
2
|
+
import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-CekOpKvz.js';
|
|
3
|
+
import { s as serveManyBase } from './serve-many-iJF1IUXk.js';
|
|
4
4
|
import '@upstash/qstash';
|
|
5
5
|
import 'zod';
|
|
6
6
|
|
package/nextjs.js
CHANGED
|
@@ -195,6 +195,7 @@ var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
|
|
|
195
195
|
var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
|
|
196
196
|
var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
|
|
197
197
|
var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
|
|
198
|
+
var WORKFLOW_RETRIED_HEADER = "Upstash-Retried";
|
|
198
199
|
var WORKFLOW_LABEL_HEADER = "Upstash-Label";
|
|
199
200
|
var WORKFLOW_UNKOWN_SDK_VERSION_HEADER = "Upstash-Workflow-Unknown-Sdk";
|
|
200
201
|
var WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER = "upstash-workflow-trigger-by-sdk";
|
|
@@ -203,7 +204,7 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
|
|
|
203
204
|
var DEFAULT_CONTENT_TYPE = "application/json";
|
|
204
205
|
var NO_CONCURRENCY = 1;
|
|
205
206
|
var DEFAULT_RETRIES = 3;
|
|
206
|
-
var VERSION = "v1.
|
|
207
|
+
var VERSION = "v1.2.1";
|
|
207
208
|
var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
|
|
208
209
|
var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
|
|
209
210
|
var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
|
|
@@ -218,6 +219,31 @@ var import_qstash4 = require("@upstash/qstash");
|
|
|
218
219
|
// src/utils.ts
|
|
219
220
|
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
220
221
|
var NANOID_LENGTH = 21;
|
|
222
|
+
var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
|
|
223
|
+
function validateLabel(label) {
|
|
224
|
+
if (label === void 0) return;
|
|
225
|
+
const labels = Array.isArray(label) ? label : [label];
|
|
226
|
+
if (labels.length === 0) {
|
|
227
|
+
throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
|
|
228
|
+
}
|
|
229
|
+
for (const value of labels) {
|
|
230
|
+
if (!RESOURCE_NAME_PATTERN.test(value)) {
|
|
231
|
+
throw new WorkflowNonRetryableError(
|
|
232
|
+
`Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function serializeLabel(label) {
|
|
238
|
+
return Array.isArray(label) ? label.join(",") : label;
|
|
239
|
+
}
|
|
240
|
+
function validateFlowControl(flowControl) {
|
|
241
|
+
if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
|
|
242
|
+
throw new WorkflowNonRetryableError(
|
|
243
|
+
`Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
221
247
|
function getRandomInt() {
|
|
222
248
|
return Math.floor(Math.random() * NANOID_CHARS.length);
|
|
223
249
|
}
|
|
@@ -753,8 +779,8 @@ var triggerFirstInvocation = async (params) => {
|
|
|
753
779
|
if (unknownSdk) {
|
|
754
780
|
headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
|
|
755
781
|
}
|
|
756
|
-
if (workflowContext.
|
|
757
|
-
headers[WORKFLOW_LABEL_HEADER] = workflowContext.
|
|
782
|
+
if (workflowContext.labels.length > 0) {
|
|
783
|
+
headers[WORKFLOW_LABEL_HEADER] = serializeLabel(workflowContext.labels);
|
|
758
784
|
}
|
|
759
785
|
const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
|
|
760
786
|
return {
|
|
@@ -858,6 +884,9 @@ var recreateUserHeaders = (headers) => {
|
|
|
858
884
|
}
|
|
859
885
|
return filteredHeaders;
|
|
860
886
|
};
|
|
887
|
+
var isThirdPartyCallResult = (request) => {
|
|
888
|
+
return request.headers.get("Upstash-Workflow-Callback") !== null;
|
|
889
|
+
};
|
|
861
890
|
var handleThirdPartyCallResult = async ({
|
|
862
891
|
request,
|
|
863
892
|
requestPayload,
|
|
@@ -867,7 +896,7 @@ var handleThirdPartyCallResult = async ({
|
|
|
867
896
|
middlewareManager
|
|
868
897
|
}) => {
|
|
869
898
|
try {
|
|
870
|
-
if (request
|
|
899
|
+
if (isThirdPartyCallResult(request)) {
|
|
871
900
|
let callbackPayload;
|
|
872
901
|
if (requestPayload) {
|
|
873
902
|
callbackPayload = requestPayload;
|
|
@@ -1553,8 +1582,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
|
|
|
1553
1582
|
});
|
|
1554
1583
|
triggerHeaders["Upstash-Workflow-Invoke"] = "true";
|
|
1555
1584
|
if (label) {
|
|
1556
|
-
|
|
1557
|
-
triggerHeaders[
|
|
1585
|
+
const labelHeader = serializeLabel(label);
|
|
1586
|
+
triggerHeaders[WORKFLOW_LABEL_HEADER] = labelHeader;
|
|
1587
|
+
triggerHeaders[`upstash-forward-${WORKFLOW_LABEL_HEADER}`] = labelHeader;
|
|
1558
1588
|
}
|
|
1559
1589
|
return { headers: triggerHeaders, contentType };
|
|
1560
1590
|
}
|
|
@@ -2695,9 +2725,10 @@ var WorkflowContext = class {
|
|
|
2695
2725
|
*/
|
|
2696
2726
|
env;
|
|
2697
2727
|
/**
|
|
2698
|
-
*
|
|
2728
|
+
* Labels attached to the workflow run.
|
|
2699
2729
|
*
|
|
2700
|
-
* Can be used to filter the workflow run logs.
|
|
2730
|
+
* Can be used to filter the workflow run logs. A run can have multiple
|
|
2731
|
+
* labels when triggered with `label: string[]`.
|
|
2701
2732
|
*
|
|
2702
2733
|
* Can be set by passing a `label` parameter when triggering the workflow
|
|
2703
2734
|
* with `client.trigger`:
|
|
@@ -2705,11 +2736,27 @@ var WorkflowContext = class {
|
|
|
2705
2736
|
* ```ts
|
|
2706
2737
|
* await client.trigger({
|
|
2707
2738
|
* url: "https://workflow-endpoint.com",
|
|
2708
|
-
* label: "
|
|
2739
|
+
* label: ["label-1", "label-2"]
|
|
2709
2740
|
* });
|
|
2710
2741
|
* ```
|
|
2711
2742
|
*/
|
|
2712
|
-
|
|
2743
|
+
labels;
|
|
2744
|
+
/**
|
|
2745
|
+
* Label of the workflow run.
|
|
2746
|
+
*
|
|
2747
|
+
* @deprecated Use `labels` instead. When a run has multiple labels, this
|
|
2748
|
+
* only returns the first one.
|
|
2749
|
+
*/
|
|
2750
|
+
get label() {
|
|
2751
|
+
return this.labels[0];
|
|
2752
|
+
}
|
|
2753
|
+
/**
|
|
2754
|
+
* Number of times QStash has retried delivering the current request.
|
|
2755
|
+
*
|
|
2756
|
+
* Sourced from the `Upstash-Retried` header. `0` on the first delivery,
|
|
2757
|
+
* `1` on the first retry, `2` on the second, and so on.
|
|
2758
|
+
*/
|
|
2759
|
+
retried;
|
|
2713
2760
|
constructor({
|
|
2714
2761
|
qstashClient,
|
|
2715
2762
|
workflowRunId,
|
|
@@ -2722,6 +2769,7 @@ var WorkflowContext = class {
|
|
|
2722
2769
|
telemetry,
|
|
2723
2770
|
invokeCount,
|
|
2724
2771
|
label,
|
|
2772
|
+
retried,
|
|
2725
2773
|
middlewareManager
|
|
2726
2774
|
}) {
|
|
2727
2775
|
this.qstashClient = qstashClient;
|
|
@@ -2732,7 +2780,8 @@ var WorkflowContext = class {
|
|
|
2732
2780
|
this.headers = headers;
|
|
2733
2781
|
this.requestPayload = initialPayload;
|
|
2734
2782
|
this.env = env ?? {};
|
|
2735
|
-
this.
|
|
2783
|
+
this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
|
|
2784
|
+
this.retried = retried ?? 0;
|
|
2736
2785
|
const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
|
|
2737
2786
|
middlewareManagerInstance.assignContext(this);
|
|
2738
2787
|
this.executor = new AutoExecutor(
|
|
@@ -2812,8 +2861,10 @@ var WorkflowContext = class {
|
|
|
2812
2861
|
await this.addStep(new LazySleepUntilStep(this, stepName, time));
|
|
2813
2862
|
}
|
|
2814
2863
|
async call(stepName, settings) {
|
|
2864
|
+
validateFlowControl(settings.flowControl);
|
|
2815
2865
|
let callStep;
|
|
2816
2866
|
if ("workflow" in settings) {
|
|
2867
|
+
validateLabel(settings.label);
|
|
2817
2868
|
const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
|
|
2818
2869
|
const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
|
|
2819
2870
|
callStep = new LazyCallStep({
|
|
@@ -2921,6 +2972,8 @@ var WorkflowContext = class {
|
|
|
2921
2972
|
);
|
|
2922
2973
|
}
|
|
2923
2974
|
async invoke(stepName, settings) {
|
|
2975
|
+
validateLabel(settings.label);
|
|
2976
|
+
validateFlowControl(settings.flowControl);
|
|
2924
2977
|
return await this.addStep(
|
|
2925
2978
|
new LazyInvokeStep(this, stepName, settings)
|
|
2926
2979
|
);
|
|
@@ -2996,7 +3049,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
2996
3049
|
const disabledContext = new _DisabledWorkflowContext({
|
|
2997
3050
|
qstashClient: new import_qstash10.Client({
|
|
2998
3051
|
baseUrl: "disabled-client",
|
|
2999
|
-
token: "disabled-client"
|
|
3052
|
+
token: "disabled-client",
|
|
3053
|
+
devMode: false
|
|
3000
3054
|
}),
|
|
3001
3055
|
workflowRunId: context.workflowRunId,
|
|
3002
3056
|
workflowRunCreatedAt: context.workflowRunCreatedAt,
|
|
@@ -3005,7 +3059,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
|
|
|
3005
3059
|
url: context.url,
|
|
3006
3060
|
initialPayload: context.requestPayload,
|
|
3007
3061
|
env: context.env,
|
|
3008
|
-
label: context.
|
|
3062
|
+
label: context.labels,
|
|
3063
|
+
retried: context.retried
|
|
3009
3064
|
});
|
|
3010
3065
|
try {
|
|
3011
3066
|
await routeFunction(disabledContext);
|
|
@@ -3221,6 +3276,7 @@ var handleFailure = async ({
|
|
|
3221
3276
|
errorMessage = `Couldn't parse 'failResponse' in 'failureFunction', received: '${decodedBody}'`;
|
|
3222
3277
|
}
|
|
3223
3278
|
const userHeaders = recreateUserHeaders(request.headers);
|
|
3279
|
+
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
3224
3280
|
const workflowContext = new WorkflowContext({
|
|
3225
3281
|
qstashClient,
|
|
3226
3282
|
workflowRunId,
|
|
@@ -3232,6 +3288,7 @@ var handleFailure = async ({
|
|
|
3232
3288
|
telemetry: void 0,
|
|
3233
3289
|
// not going to make requests in authentication check
|
|
3234
3290
|
label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
|
|
3291
|
+
retried,
|
|
3235
3292
|
workflowRunCreatedAt: workflowCreatedAt,
|
|
3236
3293
|
middlewareManager: void 0
|
|
3237
3294
|
});
|
|
@@ -3391,6 +3448,9 @@ var getReceiver = (environment, receiverConfig, region) => {
|
|
|
3391
3448
|
if (receiverConfig === "set-to-undefined") {
|
|
3392
3449
|
return void 0;
|
|
3393
3450
|
}
|
|
3451
|
+
if (isQStashDevModeEnabled(environment)) {
|
|
3452
|
+
return new import_qstash11.Receiver({ devMode: true });
|
|
3453
|
+
}
|
|
3394
3454
|
const receiverEnv = readReceiverEnvironmentVariables(environment, region);
|
|
3395
3455
|
return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new import_qstash11.Receiver({
|
|
3396
3456
|
currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
|
|
@@ -3400,6 +3460,10 @@ var getReceiver = (environment, receiverConfig, region) => {
|
|
|
3400
3460
|
return receiverConfig;
|
|
3401
3461
|
}
|
|
3402
3462
|
};
|
|
3463
|
+
var isQStashDevModeEnabled = (env) => {
|
|
3464
|
+
const value = env.QSTASH_DEV;
|
|
3465
|
+
return value === "true" || value === "1";
|
|
3466
|
+
};
|
|
3403
3467
|
var getQStashHandlerOptions = (...params) => {
|
|
3404
3468
|
const handlers = getQStashHandlers(...params);
|
|
3405
3469
|
return {
|
|
@@ -3709,12 +3773,13 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3709
3773
|
);
|
|
3710
3774
|
}
|
|
3711
3775
|
const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
|
|
3776
|
+
const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
|
|
3712
3777
|
const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
|
|
3713
3778
|
const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
|
|
3714
3779
|
const workflowContext = new WorkflowContext({
|
|
3715
3780
|
qstashClient: regionalClient,
|
|
3716
3781
|
workflowRunId,
|
|
3717
|
-
initialPayload: initialPayloadParser(rawInitialPayload),
|
|
3782
|
+
initialPayload: isThirdPartyCallResult(request) ? JSON.parse(rawInitialPayload) : initialPayloadParser(rawInitialPayload),
|
|
3718
3783
|
headers: recreateUserHeaders(request.headers),
|
|
3719
3784
|
steps,
|
|
3720
3785
|
url: workflowUrl,
|
|
@@ -3722,6 +3787,7 @@ var serveBase = (routeFunction, telemetry, options, internalOptions) => {
|
|
|
3722
3787
|
telemetry,
|
|
3723
3788
|
invokeCount,
|
|
3724
3789
|
label,
|
|
3790
|
+
retried,
|
|
3725
3791
|
workflowRunCreatedAt: Number(workflowRunCreatedAt),
|
|
3726
3792
|
middlewareManager
|
|
3727
3793
|
});
|
package/nextjs.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@upstash/workflow","version":"1.
|
|
1
|
+
{"name":"@upstash/workflow","version":"1.3.1","description":"Durable, Reliable and Performant Serverless Functions","main":"./index.js","module":"./index.mjs","types":"./index.d.ts","files":["./*"],"exports":{".":{"import":"./index.mjs","require":"./index.js"},"./dist/nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./h3":{"import":"./h3.mjs","require":"./h3.js"},"./svelte":{"import":"./svelte.mjs","require":"./svelte.js"},"./solidjs":{"import":"./solidjs.mjs","require":"./solidjs.js"},"./workflow":{"import":"./workflow.mjs","require":"./workflow.js"},"./hono":{"import":"./hono.mjs","require":"./hono.js"},"./cloudflare":{"import":"./cloudflare.mjs","require":"./cloudflare.js"},"./astro":{"import":"./astro.mjs","require":"./astro.js"},"./express":{"import":"./express.mjs","require":"./express.js"},"./tanstack":{"import":"./tanstack.mjs","require":"./tanstack.js"},"./react-router":{"import":"./react-router.mjs","require":"./react-router.js"}},"scripts":{"build":"tsup && cp README.md ./dist/ && cp package.json ./dist/ && cp LICENSE ./dist/","test":"bun test src","fmt":"prettier --write .","lint":"tsc && eslint \"{src,platforms}/**/*.{js,ts,tsx}\" --quiet --fix","check-exports":"bun run build && cd dist && attw -P"},"repository":{"type":"git","url":"git@github.com:upstash/workflow-js.git"},"keywords":["upstash","qstash","workflow","serverless"],"author":"Cahid Arda Oz","license":"MIT","bugs":{"url":"https://github.com/upstash/workflow-ts/issues"},"homepage":"https://github.com/upstash/workflow-ts#readme","devDependencies":{"@commitlint/cli":"^19.5.0","@commitlint/config-conventional":"^19.5.0","@eslint/js":"^9.11.1","@solidjs/start":"^1.0.8","@sveltejs/kit":"^2.6.1","@types/bun":"^1.1.10","@types/express":"^5.0.6","astro":"^4.16.7","eslint":"^9.11.1","eslint-plugin-unicorn":"^55.0.0","express":"^5.1.0","globals":"^15.10.0","h3":"^1.12.0","hono":"^4.6.20","husky":"^9.1.6","next":"^14.2.14","prettier":"3.3.3","tsup":"^8.3.0","typescript":"^5.7.2","typescript-eslint":"^8.18.0"},"dependencies":{"@upstash/qstash":"^2.11.0"},"directories":{"example":"examples"},"peerDependencies":{"zod":"^3.25.0 || ^4.0.0"}}
|
package/react-router.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-
|
|
2
|
-
import { s as serveManyBase } from './serve-many-
|
|
1
|
+
import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-CekOpKvz.mjs';
|
|
2
|
+
import { s as serveManyBase } from './serve-many-CG3BFvO3.mjs';
|
|
3
3
|
import '@upstash/qstash';
|
|
4
4
|
import 'zod';
|
|
5
5
|
|
package/react-router.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-
|
|
2
|
-
import { s as serveManyBase } from './serve-many-
|
|
1
|
+
import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-CekOpKvz.js';
|
|
2
|
+
import { s as serveManyBase } from './serve-many-iJF1IUXk.js';
|
|
3
3
|
import '@upstash/qstash';
|
|
4
4
|
import 'zod';
|
|
5
5
|
|