@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/h3.js CHANGED
@@ -501,6 +501,7 @@ var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
501
501
  var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
502
502
  var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
503
503
  var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
504
+ var WORKFLOW_RETRIED_HEADER = "Upstash-Retried";
504
505
  var WORKFLOW_LABEL_HEADER = "Upstash-Label";
505
506
  var WORKFLOW_UNKOWN_SDK_VERSION_HEADER = "Upstash-Workflow-Unknown-Sdk";
506
507
  var WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER = "upstash-workflow-trigger-by-sdk";
@@ -509,7 +510,7 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
509
510
  var DEFAULT_CONTENT_TYPE = "application/json";
510
511
  var NO_CONCURRENCY = 1;
511
512
  var DEFAULT_RETRIES = 3;
512
- var VERSION = "v1.0.0";
513
+ var VERSION = "v1.2.1";
513
514
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
514
515
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
515
516
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
@@ -524,6 +525,31 @@ var import_qstash4 = require("@upstash/qstash");
524
525
  // src/utils.ts
525
526
  var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
526
527
  var NANOID_LENGTH = 21;
528
+ var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
529
+ function validateLabel(label) {
530
+ if (label === void 0) return;
531
+ const labels = Array.isArray(label) ? label : [label];
532
+ if (labels.length === 0) {
533
+ throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
534
+ }
535
+ for (const value of labels) {
536
+ if (!RESOURCE_NAME_PATTERN.test(value)) {
537
+ throw new WorkflowNonRetryableError(
538
+ `Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
539
+ );
540
+ }
541
+ }
542
+ }
543
+ function serializeLabel(label) {
544
+ return Array.isArray(label) ? label.join(",") : label;
545
+ }
546
+ function validateFlowControl(flowControl) {
547
+ if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
548
+ throw new WorkflowNonRetryableError(
549
+ `Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
550
+ );
551
+ }
552
+ }
527
553
  function getRandomInt() {
528
554
  return Math.floor(Math.random() * NANOID_CHARS.length);
529
555
  }
@@ -1059,8 +1085,8 @@ var triggerFirstInvocation = async (params) => {
1059
1085
  if (unknownSdk) {
1060
1086
  headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
1061
1087
  }
1062
- if (workflowContext.label) {
1063
- headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
1088
+ if (workflowContext.labels.length > 0) {
1089
+ headers[WORKFLOW_LABEL_HEADER] = serializeLabel(workflowContext.labels);
1064
1090
  }
1065
1091
  const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
1066
1092
  return {
@@ -1164,6 +1190,9 @@ var recreateUserHeaders = (headers) => {
1164
1190
  }
1165
1191
  return filteredHeaders;
1166
1192
  };
1193
+ var isThirdPartyCallResult = (request) => {
1194
+ return request.headers.get("Upstash-Workflow-Callback") !== null;
1195
+ };
1167
1196
  var handleThirdPartyCallResult = async ({
1168
1197
  request,
1169
1198
  requestPayload,
@@ -1173,7 +1202,7 @@ var handleThirdPartyCallResult = async ({
1173
1202
  middlewareManager
1174
1203
  }) => {
1175
1204
  try {
1176
- if (request.headers.get("Upstash-Workflow-Callback")) {
1205
+ if (isThirdPartyCallResult(request)) {
1177
1206
  let callbackPayload;
1178
1207
  if (requestPayload) {
1179
1208
  callbackPayload = requestPayload;
@@ -1859,8 +1888,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1859
1888
  });
1860
1889
  triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1861
1890
  if (label) {
1862
- triggerHeaders[WORKFLOW_LABEL_HEADER] = label;
1863
- triggerHeaders[`upstash-forward-${WORKFLOW_LABEL_HEADER}`] = label;
1891
+ const labelHeader = serializeLabel(label);
1892
+ triggerHeaders[WORKFLOW_LABEL_HEADER] = labelHeader;
1893
+ triggerHeaders[`upstash-forward-${WORKFLOW_LABEL_HEADER}`] = labelHeader;
1864
1894
  }
1865
1895
  return { headers: triggerHeaders, contentType };
1866
1896
  }
@@ -3001,9 +3031,10 @@ var WorkflowContext = class {
3001
3031
  */
3002
3032
  env;
3003
3033
  /**
3004
- * Label to apply to the workflow run.
3034
+ * Labels attached to the workflow run.
3005
3035
  *
3006
- * Can be used to filter the workflow run logs.
3036
+ * Can be used to filter the workflow run logs. A run can have multiple
3037
+ * labels when triggered with `label: string[]`.
3007
3038
  *
3008
3039
  * Can be set by passing a `label` parameter when triggering the workflow
3009
3040
  * with `client.trigger`:
@@ -3011,11 +3042,27 @@ var WorkflowContext = class {
3011
3042
  * ```ts
3012
3043
  * await client.trigger({
3013
3044
  * url: "https://workflow-endpoint.com",
3014
- * label: "my-label"
3045
+ * label: ["label-1", "label-2"]
3015
3046
  * });
3016
3047
  * ```
3017
3048
  */
3018
- label;
3049
+ labels;
3050
+ /**
3051
+ * Label of the workflow run.
3052
+ *
3053
+ * @deprecated Use `labels` instead. When a run has multiple labels, this
3054
+ * only returns the first one.
3055
+ */
3056
+ get label() {
3057
+ return this.labels[0];
3058
+ }
3059
+ /**
3060
+ * Number of times QStash has retried delivering the current request.
3061
+ *
3062
+ * Sourced from the `Upstash-Retried` header. `0` on the first delivery,
3063
+ * `1` on the first retry, `2` on the second, and so on.
3064
+ */
3065
+ retried;
3019
3066
  constructor({
3020
3067
  qstashClient,
3021
3068
  workflowRunId,
@@ -3028,6 +3075,7 @@ var WorkflowContext = class {
3028
3075
  telemetry: telemetry2,
3029
3076
  invokeCount,
3030
3077
  label,
3078
+ retried,
3031
3079
  middlewareManager
3032
3080
  }) {
3033
3081
  this.qstashClient = qstashClient;
@@ -3038,7 +3086,8 @@ var WorkflowContext = class {
3038
3086
  this.headers = headers;
3039
3087
  this.requestPayload = initialPayload;
3040
3088
  this.env = env ?? {};
3041
- this.label = label;
3089
+ this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
3090
+ this.retried = retried ?? 0;
3042
3091
  const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
3043
3092
  middlewareManagerInstance.assignContext(this);
3044
3093
  this.executor = new AutoExecutor(
@@ -3118,8 +3167,10 @@ var WorkflowContext = class {
3118
3167
  await this.addStep(new LazySleepUntilStep(this, stepName, time));
3119
3168
  }
3120
3169
  async call(stepName, settings) {
3170
+ validateFlowControl(settings.flowControl);
3121
3171
  let callStep;
3122
3172
  if ("workflow" in settings) {
3173
+ validateLabel(settings.label);
3123
3174
  const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
3124
3175
  const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
3125
3176
  callStep = new LazyCallStep({
@@ -3227,6 +3278,8 @@ var WorkflowContext = class {
3227
3278
  );
3228
3279
  }
3229
3280
  async invoke(stepName, settings) {
3281
+ validateLabel(settings.label);
3282
+ validateFlowControl(settings.flowControl);
3230
3283
  return await this.addStep(
3231
3284
  new LazyInvokeStep(this, stepName, settings)
3232
3285
  );
@@ -3302,7 +3355,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3302
3355
  const disabledContext = new _DisabledWorkflowContext({
3303
3356
  qstashClient: new import_qstash10.Client({
3304
3357
  baseUrl: "disabled-client",
3305
- token: "disabled-client"
3358
+ token: "disabled-client",
3359
+ devMode: false
3306
3360
  }),
3307
3361
  workflowRunId: context.workflowRunId,
3308
3362
  workflowRunCreatedAt: context.workflowRunCreatedAt,
@@ -3311,7 +3365,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3311
3365
  url: context.url,
3312
3366
  initialPayload: context.requestPayload,
3313
3367
  env: context.env,
3314
- label: context.label
3368
+ label: context.labels,
3369
+ retried: context.retried
3315
3370
  });
3316
3371
  try {
3317
3372
  await routeFunction(disabledContext);
@@ -3527,6 +3582,7 @@ var handleFailure = async ({
3527
3582
  errorMessage = `Couldn't parse 'failResponse' in 'failureFunction', received: '${decodedBody}'`;
3528
3583
  }
3529
3584
  const userHeaders = recreateUserHeaders(request.headers);
3585
+ const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
3530
3586
  const workflowContext = new WorkflowContext({
3531
3587
  qstashClient,
3532
3588
  workflowRunId,
@@ -3538,6 +3594,7 @@ var handleFailure = async ({
3538
3594
  telemetry: void 0,
3539
3595
  // not going to make requests in authentication check
3540
3596
  label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
3597
+ retried,
3541
3598
  workflowRunCreatedAt: workflowCreatedAt,
3542
3599
  middlewareManager: void 0
3543
3600
  });
@@ -3697,6 +3754,9 @@ var getReceiver = (environment, receiverConfig, region) => {
3697
3754
  if (receiverConfig === "set-to-undefined") {
3698
3755
  return void 0;
3699
3756
  }
3757
+ if (isQStashDevModeEnabled(environment)) {
3758
+ return new import_qstash11.Receiver({ devMode: true });
3759
+ }
3700
3760
  const receiverEnv = readReceiverEnvironmentVariables(environment, region);
3701
3761
  return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new import_qstash11.Receiver({
3702
3762
  currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
@@ -3706,6 +3766,10 @@ var getReceiver = (environment, receiverConfig, region) => {
3706
3766
  return receiverConfig;
3707
3767
  }
3708
3768
  };
3769
+ var isQStashDevModeEnabled = (env) => {
3770
+ const value = env.QSTASH_DEV;
3771
+ return value === "true" || value === "1";
3772
+ };
3709
3773
  var getQStashHandlerOptions = (...params) => {
3710
3774
  const handlers = getQStashHandlers(...params);
3711
3775
  return {
@@ -4015,12 +4079,13 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
4015
4079
  );
4016
4080
  }
4017
4081
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
4082
+ const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
4018
4083
  const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
4019
4084
  const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
4020
4085
  const workflowContext = new WorkflowContext({
4021
4086
  qstashClient: regionalClient,
4022
4087
  workflowRunId,
4023
- initialPayload: initialPayloadParser(rawInitialPayload),
4088
+ initialPayload: isThirdPartyCallResult(request) ? JSON.parse(rawInitialPayload) : initialPayloadParser(rawInitialPayload),
4024
4089
  headers: recreateUserHeaders(request.headers),
4025
4090
  steps,
4026
4091
  url: workflowUrl,
@@ -4028,6 +4093,7 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
4028
4093
  telemetry: telemetry2,
4029
4094
  invokeCount,
4030
4095
  label,
4096
+ retried,
4031
4097
  workflowRunCreatedAt: Number(workflowRunCreatedAt),
4032
4098
  middlewareManager
4033
4099
  });
package/h3.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
  // node_modules/defu/dist/defu.mjs
8
8
  function isPlainObject(value) {
package/hono.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Context } from 'hono';
2
- import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-B2S08hRU.mjs';
2
+ import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-CekOpKvz.mjs';
3
3
  import { Variables } from 'hono/types';
4
- import { s as serveManyBase } from './serve-many-C6sa_DxN.mjs';
4
+ import { s as serveManyBase } from './serve-many-CG3BFvO3.mjs';
5
5
  import '@upstash/qstash';
6
6
  import 'zod';
7
7
 
package/hono.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Context } from 'hono';
2
- import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-B2S08hRU.js';
2
+ import { c as RouteFunction, d as WorkflowServeOptions, I as InvokableWorkflow } from './types-CekOpKvz.js';
3
3
  import { Variables } from 'hono/types';
4
- import { s as serveManyBase } from './serve-many-B-fe7bh7.js';
4
+ import { s as serveManyBase } from './serve-many-iJF1IUXk.js';
5
5
  import '@upstash/qstash';
6
6
  import 'zod';
7
7
 
package/hono.js CHANGED
@@ -192,6 +192,7 @@ var WORKFLOW_FAILURE_HEADER = "Upstash-Workflow-Is-Failure";
192
192
  var WORKFLOW_FAILURE_CALLBACK_HEADER = "Upstash-Workflow-Failure-Callback";
193
193
  var WORKFLOW_FEATURE_HEADER = "Upstash-Feature-Set";
194
194
  var WORKFLOW_INVOKE_COUNT_HEADER = "Upstash-Workflow-Invoke-Count";
195
+ var WORKFLOW_RETRIED_HEADER = "Upstash-Retried";
195
196
  var WORKFLOW_LABEL_HEADER = "Upstash-Label";
196
197
  var WORKFLOW_UNKOWN_SDK_VERSION_HEADER = "Upstash-Workflow-Unknown-Sdk";
197
198
  var WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER = "upstash-workflow-trigger-by-sdk";
@@ -200,7 +201,7 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
200
201
  var DEFAULT_CONTENT_TYPE = "application/json";
201
202
  var NO_CONCURRENCY = 1;
202
203
  var DEFAULT_RETRIES = 3;
203
- var VERSION = "v1.0.0";
204
+ var VERSION = "v1.2.1";
204
205
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
205
206
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
206
207
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
@@ -215,6 +216,31 @@ var import_qstash4 = require("@upstash/qstash");
215
216
  // src/utils.ts
216
217
  var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
217
218
  var NANOID_LENGTH = 21;
219
+ var RESOURCE_NAME_PATTERN = /^[a-zA-Z0-9\-_.]+$/;
220
+ function validateLabel(label) {
221
+ if (label === void 0) return;
222
+ const labels = Array.isArray(label) ? label : [label];
223
+ if (labels.length === 0) {
224
+ throw new WorkflowNonRetryableError("Invalid label: label array must not be empty.");
225
+ }
226
+ for (const value of labels) {
227
+ if (!RESOURCE_NAME_PATTERN.test(value)) {
228
+ throw new WorkflowNonRetryableError(
229
+ `Invalid label "${value}": must be alphanumeric, hyphen, underscore, or period.`
230
+ );
231
+ }
232
+ }
233
+ }
234
+ function serializeLabel(label) {
235
+ return Array.isArray(label) ? label.join(",") : label;
236
+ }
237
+ function validateFlowControl(flowControl) {
238
+ if (flowControl?.key !== void 0 && !RESOURCE_NAME_PATTERN.test(flowControl.key)) {
239
+ throw new WorkflowNonRetryableError(
240
+ `Invalid flow control key "${flowControl.key}": must be alphanumeric, hyphen, underscore, or period.`
241
+ );
242
+ }
243
+ }
218
244
  function getRandomInt() {
219
245
  return Math.floor(Math.random() * NANOID_CHARS.length);
220
246
  }
@@ -750,8 +776,8 @@ var triggerFirstInvocation = async (params) => {
750
776
  if (unknownSdk) {
751
777
  headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
752
778
  }
753
- if (workflowContext.label) {
754
- headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
779
+ if (workflowContext.labels.length > 0) {
780
+ headers[WORKFLOW_LABEL_HEADER] = serializeLabel(workflowContext.labels);
755
781
  }
756
782
  const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
757
783
  return {
@@ -855,6 +881,9 @@ var recreateUserHeaders = (headers) => {
855
881
  }
856
882
  return filteredHeaders;
857
883
  };
884
+ var isThirdPartyCallResult = (request) => {
885
+ return request.headers.get("Upstash-Workflow-Callback") !== null;
886
+ };
858
887
  var handleThirdPartyCallResult = async ({
859
888
  request,
860
889
  requestPayload,
@@ -864,7 +893,7 @@ var handleThirdPartyCallResult = async ({
864
893
  middlewareManager
865
894
  }) => {
866
895
  try {
867
- if (request.headers.get("Upstash-Workflow-Callback")) {
896
+ if (isThirdPartyCallResult(request)) {
868
897
  let callbackPayload;
869
898
  if (requestPayload) {
870
899
  callbackPayload = requestPayload;
@@ -1550,8 +1579,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1550
1579
  });
1551
1580
  triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1552
1581
  if (label) {
1553
- triggerHeaders[WORKFLOW_LABEL_HEADER] = label;
1554
- triggerHeaders[`upstash-forward-${WORKFLOW_LABEL_HEADER}`] = label;
1582
+ const labelHeader = serializeLabel(label);
1583
+ triggerHeaders[WORKFLOW_LABEL_HEADER] = labelHeader;
1584
+ triggerHeaders[`upstash-forward-${WORKFLOW_LABEL_HEADER}`] = labelHeader;
1555
1585
  }
1556
1586
  return { headers: triggerHeaders, contentType };
1557
1587
  }
@@ -2692,9 +2722,10 @@ var WorkflowContext = class {
2692
2722
  */
2693
2723
  env;
2694
2724
  /**
2695
- * Label to apply to the workflow run.
2725
+ * Labels attached to the workflow run.
2696
2726
  *
2697
- * Can be used to filter the workflow run logs.
2727
+ * Can be used to filter the workflow run logs. A run can have multiple
2728
+ * labels when triggered with `label: string[]`.
2698
2729
  *
2699
2730
  * Can be set by passing a `label` parameter when triggering the workflow
2700
2731
  * with `client.trigger`:
@@ -2702,11 +2733,27 @@ var WorkflowContext = class {
2702
2733
  * ```ts
2703
2734
  * await client.trigger({
2704
2735
  * url: "https://workflow-endpoint.com",
2705
- * label: "my-label"
2736
+ * label: ["label-1", "label-2"]
2706
2737
  * });
2707
2738
  * ```
2708
2739
  */
2709
- label;
2740
+ labels;
2741
+ /**
2742
+ * Label of the workflow run.
2743
+ *
2744
+ * @deprecated Use `labels` instead. When a run has multiple labels, this
2745
+ * only returns the first one.
2746
+ */
2747
+ get label() {
2748
+ return this.labels[0];
2749
+ }
2750
+ /**
2751
+ * Number of times QStash has retried delivering the current request.
2752
+ *
2753
+ * Sourced from the `Upstash-Retried` header. `0` on the first delivery,
2754
+ * `1` on the first retry, `2` on the second, and so on.
2755
+ */
2756
+ retried;
2710
2757
  constructor({
2711
2758
  qstashClient,
2712
2759
  workflowRunId,
@@ -2719,6 +2766,7 @@ var WorkflowContext = class {
2719
2766
  telemetry: telemetry2,
2720
2767
  invokeCount,
2721
2768
  label,
2769
+ retried,
2722
2770
  middlewareManager
2723
2771
  }) {
2724
2772
  this.qstashClient = qstashClient;
@@ -2729,7 +2777,8 @@ var WorkflowContext = class {
2729
2777
  this.headers = headers;
2730
2778
  this.requestPayload = initialPayload;
2731
2779
  this.env = env ?? {};
2732
- this.label = label;
2780
+ this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
2781
+ this.retried = retried ?? 0;
2733
2782
  const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
2734
2783
  middlewareManagerInstance.assignContext(this);
2735
2784
  this.executor = new AutoExecutor(
@@ -2809,8 +2858,10 @@ var WorkflowContext = class {
2809
2858
  await this.addStep(new LazySleepUntilStep(this, stepName, time));
2810
2859
  }
2811
2860
  async call(stepName, settings) {
2861
+ validateFlowControl(settings.flowControl);
2812
2862
  let callStep;
2813
2863
  if ("workflow" in settings) {
2864
+ validateLabel(settings.label);
2814
2865
  const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
2815
2866
  const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
2816
2867
  callStep = new LazyCallStep({
@@ -2918,6 +2969,8 @@ var WorkflowContext = class {
2918
2969
  );
2919
2970
  }
2920
2971
  async invoke(stepName, settings) {
2972
+ validateLabel(settings.label);
2973
+ validateFlowControl(settings.flowControl);
2921
2974
  return await this.addStep(
2922
2975
  new LazyInvokeStep(this, stepName, settings)
2923
2976
  );
@@ -2993,7 +3046,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
2993
3046
  const disabledContext = new _DisabledWorkflowContext({
2994
3047
  qstashClient: new import_qstash10.Client({
2995
3048
  baseUrl: "disabled-client",
2996
- token: "disabled-client"
3049
+ token: "disabled-client",
3050
+ devMode: false
2997
3051
  }),
2998
3052
  workflowRunId: context.workflowRunId,
2999
3053
  workflowRunCreatedAt: context.workflowRunCreatedAt,
@@ -3002,7 +3056,8 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3002
3056
  url: context.url,
3003
3057
  initialPayload: context.requestPayload,
3004
3058
  env: context.env,
3005
- label: context.label
3059
+ label: context.labels,
3060
+ retried: context.retried
3006
3061
  });
3007
3062
  try {
3008
3063
  await routeFunction(disabledContext);
@@ -3218,6 +3273,7 @@ var handleFailure = async ({
3218
3273
  errorMessage = `Couldn't parse 'failResponse' in 'failureFunction', received: '${decodedBody}'`;
3219
3274
  }
3220
3275
  const userHeaders = recreateUserHeaders(request.headers);
3276
+ const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
3221
3277
  const workflowContext = new WorkflowContext({
3222
3278
  qstashClient,
3223
3279
  workflowRunId,
@@ -3229,6 +3285,7 @@ var handleFailure = async ({
3229
3285
  telemetry: void 0,
3230
3286
  // not going to make requests in authentication check
3231
3287
  label: userHeaders.get(WORKFLOW_LABEL_HEADER) ?? void 0,
3288
+ retried,
3232
3289
  workflowRunCreatedAt: workflowCreatedAt,
3233
3290
  middlewareManager: void 0
3234
3291
  });
@@ -3388,6 +3445,9 @@ var getReceiver = (environment, receiverConfig, region) => {
3388
3445
  if (receiverConfig === "set-to-undefined") {
3389
3446
  return void 0;
3390
3447
  }
3448
+ if (isQStashDevModeEnabled(environment)) {
3449
+ return new import_qstash11.Receiver({ devMode: true });
3450
+ }
3391
3451
  const receiverEnv = readReceiverEnvironmentVariables(environment, region);
3392
3452
  return receiverEnv.QSTASH_CURRENT_SIGNING_KEY && receiverEnv.QSTASH_NEXT_SIGNING_KEY ? new import_qstash11.Receiver({
3393
3453
  currentSigningKey: receiverEnv.QSTASH_CURRENT_SIGNING_KEY,
@@ -3397,6 +3457,10 @@ var getReceiver = (environment, receiverConfig, region) => {
3397
3457
  return receiverConfig;
3398
3458
  }
3399
3459
  };
3460
+ var isQStashDevModeEnabled = (env) => {
3461
+ const value = env.QSTASH_DEV;
3462
+ return value === "true" || value === "1";
3463
+ };
3400
3464
  var getQStashHandlerOptions = (...params) => {
3401
3465
  const handlers = getQStashHandlers(...params);
3402
3466
  return {
@@ -3706,12 +3770,13 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3706
3770
  );
3707
3771
  }
3708
3772
  const invokeCount = Number(request.headers.get(WORKFLOW_INVOKE_COUNT_HEADER) ?? "0");
3773
+ const retried = Number(request.headers.get(WORKFLOW_RETRIED_HEADER) ?? "0");
3709
3774
  const label = request.headers.get(WORKFLOW_LABEL_HEADER) ?? void 0;
3710
3775
  const workflowRunCreatedAt = request.headers.get(WORKFLOW_CREATED_AT_HEADER);
3711
3776
  const workflowContext = new WorkflowContext({
3712
3777
  qstashClient: regionalClient,
3713
3778
  workflowRunId,
3714
- initialPayload: initialPayloadParser(rawInitialPayload),
3779
+ initialPayload: isThirdPartyCallResult(request) ? JSON.parse(rawInitialPayload) : initialPayloadParser(rawInitialPayload),
3715
3780
  headers: recreateUserHeaders(request.headers),
3716
3781
  steps,
3717
3782
  url: workflowUrl,
@@ -3719,6 +3784,7 @@ var serveBase = (routeFunction, telemetry2, options, internalOptions) => {
3719
3784
  telemetry: telemetry2,
3720
3785
  invokeCount,
3721
3786
  label,
3787
+ retried,
3722
3788
  workflowRunCreatedAt: Number(workflowRunCreatedAt),
3723
3789
  middlewareManager
3724
3790
  });
package/hono.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/hono.ts
8
8
  var telemetry = {
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-B2S08hRU.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-B2S08hRU.mjs';
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
 
@@ -317,9 +317,18 @@ type WorkflowRunLog = {
317
317
  */
318
318
  dlqId?: string;
319
319
  /**
320
- * Label of the workflow run
320
+ * Label of the workflow run.
321
+ *
322
+ * @deprecated Use `labels` instead. When a run has multiple labels, this
323
+ * field only contains the first one.
321
324
  */
322
325
  label?: string;
326
+ /**
327
+ * Labels attached to the workflow run.
328
+ *
329
+ * A run can have multiple labels when triggered with `label: string[]`.
330
+ */
331
+ labels?: string[];
323
332
  };
324
333
  type WorkflowRunLogs = {
325
334
  cursor: string;
@@ -403,8 +412,11 @@ type TriggerOptions = {
403
412
  * Label to apply to the workflow run.
404
413
  *
405
414
  * Can be used to filter the workflow run logs.
415
+ *
416
+ * Pass an array to attach multiple labels to a single workflow run; they are
417
+ * sent as a comma-separated value in the `Upstash-Label` header.
406
418
  */
407
- label?: string;
419
+ label?: string | string[];
408
420
  /**
409
421
  * By default, Workflow SDK sends telemetry about SDK version, framework or runtime.
410
422
  *
@@ -526,7 +538,14 @@ type UniversalFilterFields = {
526
538
  fromDate?: Date | number;
527
539
  toDate?: Date | number;
528
540
  callerIp?: string;
529
- label?: string;
541
+ /**
542
+ * Filter by label.
543
+ *
544
+ * Pass an array to match runs that have any of the given labels (OR semantics).
545
+ * For example, with runs labelled `[label_1, label_2]` and `[label_2, label_3]`,
546
+ * filtering by `[label_1, label_2]` returns both.
547
+ */
548
+ label?: string | string[];
530
549
  flowControlKey?: string;
531
550
  };
532
551
  /** Workflow-specific filter fields for DLQ and bulk endpoints. */
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-B2S08hRU.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-B2S08hRU.js';
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
 
@@ -317,9 +317,18 @@ type WorkflowRunLog = {
317
317
  */
318
318
  dlqId?: string;
319
319
  /**
320
- * Label of the workflow run
320
+ * Label of the workflow run.
321
+ *
322
+ * @deprecated Use `labels` instead. When a run has multiple labels, this
323
+ * field only contains the first one.
321
324
  */
322
325
  label?: string;
326
+ /**
327
+ * Labels attached to the workflow run.
328
+ *
329
+ * A run can have multiple labels when triggered with `label: string[]`.
330
+ */
331
+ labels?: string[];
323
332
  };
324
333
  type WorkflowRunLogs = {
325
334
  cursor: string;
@@ -403,8 +412,11 @@ type TriggerOptions = {
403
412
  * Label to apply to the workflow run.
404
413
  *
405
414
  * Can be used to filter the workflow run logs.
415
+ *
416
+ * Pass an array to attach multiple labels to a single workflow run; they are
417
+ * sent as a comma-separated value in the `Upstash-Label` header.
406
418
  */
407
- label?: string;
419
+ label?: string | string[];
408
420
  /**
409
421
  * By default, Workflow SDK sends telemetry about SDK version, framework or runtime.
410
422
  *
@@ -526,7 +538,14 @@ type UniversalFilterFields = {
526
538
  fromDate?: Date | number;
527
539
  toDate?: Date | number;
528
540
  callerIp?: string;
529
- label?: string;
541
+ /**
542
+ * Filter by label.
543
+ *
544
+ * Pass an array to match runs that have any of the given labels (OR semantics).
545
+ * For example, with runs labelled `[label_1, label_2]` and `[label_2, label_3]`,
546
+ * filtering by `[label_1, label_2]` returns both.
547
+ */
548
+ label?: string | string[];
530
549
  flowControlKey?: string;
531
550
  };
532
551
  /** Workflow-specific filter fields for DLQ and bulk endpoints. */