@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.
Files changed (51) hide show
  1. package/README.md +10 -0
  2. package/astro.d.mts +2 -2
  3. package/astro.d.ts +2 -2
  4. package/astro.js +80 -14
  5. package/astro.mjs +1 -1
  6. package/{chunk-THS5AX2D.mjs → chunk-CWCCIOXR.mjs} +83 -14
  7. package/cloudflare.d.mts +2 -2
  8. package/cloudflare.d.ts +2 -2
  9. package/cloudflare.js +80 -14
  10. package/cloudflare.mjs +1 -1
  11. package/express.d.mts +2 -2
  12. package/express.d.ts +2 -2
  13. package/express.js +80 -14
  14. package/express.mjs +1 -1
  15. package/h3.d.mts +2 -2
  16. package/h3.d.ts +2 -2
  17. package/h3.js +80 -14
  18. package/h3.mjs +1 -1
  19. package/hono.d.mts +2 -2
  20. package/hono.d.ts +2 -2
  21. package/hono.js +80 -14
  22. package/hono.mjs +1 -1
  23. package/index.d.mts +24 -5
  24. package/index.d.ts +24 -5
  25. package/index.js +83 -15
  26. package/index.mjs +8 -3
  27. package/nextjs.d.mts +2 -2
  28. package/nextjs.d.ts +2 -2
  29. package/nextjs.js +80 -14
  30. package/nextjs.mjs +1 -1
  31. package/package.json +1 -1
  32. package/react-router.d.mts +2 -2
  33. package/react-router.d.ts +2 -2
  34. package/react-router.js +80 -14
  35. package/react-router.mjs +1 -1
  36. package/{serve-many-C6sa_DxN.d.mts → serve-many-CG3BFvO3.d.mts} +1 -1
  37. package/{serve-many-B-fe7bh7.d.ts → serve-many-iJF1IUXk.d.ts} +1 -1
  38. package/solidjs.d.mts +1 -1
  39. package/solidjs.d.ts +1 -1
  40. package/solidjs.js +80 -14
  41. package/solidjs.mjs +1 -1
  42. package/svelte.d.mts +2 -2
  43. package/svelte.d.ts +2 -2
  44. package/svelte.js +80 -14
  45. package/svelte.mjs +1 -1
  46. package/tanstack.d.mts +2 -2
  47. package/tanstack.d.ts +2 -2
  48. package/tanstack.js +80 -14
  49. package/tanstack.mjs +1 -1
  50. package/{types-B2S08hRU.d.ts → types-CekOpKvz.d.mts} +23 -7
  51. 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.0.0";
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.label) {
828
- headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
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.headers.get("Upstash-Workflow-Callback")) {
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
- triggerHeaders[WORKFLOW_LABEL_HEADER] = label;
1628
- triggerHeaders[`upstash-forward-${WORKFLOW_LABEL_HEADER}`] = label;
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
- * Label to apply to the workflow run.
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: "my-label"
2743
+ * label: ["label-1", "label-2"]
2713
2744
  * });
2714
2745
  * ```
2715
2746
  */
2716
- label;
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.label = label;
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.label
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
- } from "./chunk-THS5AX2D.mjs";
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-B2S08hRU.mjs';
3
- import { s as serveManyBase } from './serve-many-C6sa_DxN.mjs';
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-B2S08hRU.js';
3
- import { s as serveManyBase } from './serve-many-B-fe7bh7.js';
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.0.0";
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.label) {
757
- headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
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.headers.get("Upstash-Workflow-Callback")) {
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
- triggerHeaders[WORKFLOW_LABEL_HEADER] = label;
1557
- triggerHeaders[`upstash-forward-${WORKFLOW_LABEL_HEADER}`] = label;
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
- * Label to apply to the workflow run.
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: "my-label"
2739
+ * label: ["label-1", "label-2"]
2709
2740
  * });
2710
2741
  * ```
2711
2742
  */
2712
- label;
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.label = label;
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.label
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
@@ -2,7 +2,7 @@ import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase,
4
4
  serveManyBase
5
- } from "./chunk-THS5AX2D.mjs";
5
+ } from "./chunk-CWCCIOXR.mjs";
6
6
 
7
7
  // platforms/nextjs.ts
8
8
  var appTelemetry = {
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@upstash/workflow","version":"1.2.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.10.1"},"directories":{"example":"examples"},"peerDependencies":{"zod":"^3.25.0 || ^4.0.0"}}
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"}}
@@ -1,5 +1,5 @@
1
- import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-B2S08hRU.mjs';
2
- import { s as serveManyBase } from './serve-many-C6sa_DxN.mjs';
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-B2S08hRU.js';
2
- import { s as serveManyBase } from './serve-many-B-fe7bh7.js';
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