@upstash/workflow 1.3.0 → 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 (50) hide show
  1. package/astro.d.mts +2 -2
  2. package/astro.d.ts +2 -2
  3. package/astro.js +50 -10
  4. package/astro.mjs +1 -1
  5. package/{chunk-G24J5PCC.mjs → chunk-CWCCIOXR.mjs} +53 -10
  6. package/cloudflare.d.mts +2 -2
  7. package/cloudflare.d.ts +2 -2
  8. package/cloudflare.js +50 -10
  9. package/cloudflare.mjs +1 -1
  10. package/express.d.mts +2 -2
  11. package/express.d.ts +2 -2
  12. package/express.js +50 -10
  13. package/express.mjs +1 -1
  14. package/h3.d.mts +2 -2
  15. package/h3.d.ts +2 -2
  16. package/h3.js +50 -10
  17. package/h3.mjs +1 -1
  18. package/hono.d.mts +2 -2
  19. package/hono.d.ts +2 -2
  20. package/hono.js +50 -10
  21. package/hono.mjs +1 -1
  22. package/index.d.mts +24 -5
  23. package/index.d.ts +24 -5
  24. package/index.js +53 -11
  25. package/index.mjs +8 -3
  26. package/nextjs.d.mts +2 -2
  27. package/nextjs.d.ts +2 -2
  28. package/nextjs.js +50 -10
  29. package/nextjs.mjs +1 -1
  30. package/package.json +1 -1
  31. package/react-router.d.mts +2 -2
  32. package/react-router.d.ts +2 -2
  33. package/react-router.js +50 -10
  34. package/react-router.mjs +1 -1
  35. package/{serve-many-D3D9uE4u.d.mts → serve-many-CG3BFvO3.d.mts} +1 -1
  36. package/{serve-many-kPOasiyb.d.ts → serve-many-iJF1IUXk.d.ts} +1 -1
  37. package/solidjs.d.mts +1 -1
  38. package/solidjs.d.ts +1 -1
  39. package/solidjs.js +50 -10
  40. package/solidjs.mjs +1 -1
  41. package/svelte.d.mts +2 -2
  42. package/svelte.d.ts +2 -2
  43. package/svelte.js +50 -10
  44. package/svelte.mjs +1 -1
  45. package/tanstack.d.mts +2 -2
  46. package/tanstack.d.ts +2 -2
  47. package/tanstack.js +50 -10
  48. package/tanstack.mjs +1 -1
  49. package/{types-B_E1VAK6.d.ts → types-CekOpKvz.d.mts} +14 -6
  50. package/{types-B_E1VAK6.d.mts → types-CekOpKvz.d.ts} +14 -6
package/h3.js CHANGED
@@ -525,6 +525,31 @@ var import_qstash4 = require("@upstash/qstash");
525
525
  // src/utils.ts
526
526
  var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
527
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
+ }
528
553
  function getRandomInt() {
529
554
  return Math.floor(Math.random() * NANOID_CHARS.length);
530
555
  }
@@ -1060,8 +1085,8 @@ var triggerFirstInvocation = async (params) => {
1060
1085
  if (unknownSdk) {
1061
1086
  headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
1062
1087
  }
1063
- if (workflowContext.label) {
1064
- headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
1088
+ if (workflowContext.labels.length > 0) {
1089
+ headers[WORKFLOW_LABEL_HEADER] = serializeLabel(workflowContext.labels);
1065
1090
  }
1066
1091
  const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
1067
1092
  return {
@@ -1863,8 +1888,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1863
1888
  });
1864
1889
  triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1865
1890
  if (label) {
1866
- triggerHeaders[WORKFLOW_LABEL_HEADER] = label;
1867
- 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;
1868
1894
  }
1869
1895
  return { headers: triggerHeaders, contentType };
1870
1896
  }
@@ -3005,9 +3031,10 @@ var WorkflowContext = class {
3005
3031
  */
3006
3032
  env;
3007
3033
  /**
3008
- * Label to apply to the workflow run.
3034
+ * Labels attached to the workflow run.
3009
3035
  *
3010
- * 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[]`.
3011
3038
  *
3012
3039
  * Can be set by passing a `label` parameter when triggering the workflow
3013
3040
  * with `client.trigger`:
@@ -3015,11 +3042,20 @@ var WorkflowContext = class {
3015
3042
  * ```ts
3016
3043
  * await client.trigger({
3017
3044
  * url: "https://workflow-endpoint.com",
3018
- * label: "my-label"
3045
+ * label: ["label-1", "label-2"]
3019
3046
  * });
3020
3047
  * ```
3021
3048
  */
3022
- 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
+ }
3023
3059
  /**
3024
3060
  * Number of times QStash has retried delivering the current request.
3025
3061
  *
@@ -3050,7 +3086,7 @@ var WorkflowContext = class {
3050
3086
  this.headers = headers;
3051
3087
  this.requestPayload = initialPayload;
3052
3088
  this.env = env ?? {};
3053
- this.label = label;
3089
+ this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
3054
3090
  this.retried = retried ?? 0;
3055
3091
  const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
3056
3092
  middlewareManagerInstance.assignContext(this);
@@ -3131,8 +3167,10 @@ var WorkflowContext = class {
3131
3167
  await this.addStep(new LazySleepUntilStep(this, stepName, time));
3132
3168
  }
3133
3169
  async call(stepName, settings) {
3170
+ validateFlowControl(settings.flowControl);
3134
3171
  let callStep;
3135
3172
  if ("workflow" in settings) {
3173
+ validateLabel(settings.label);
3136
3174
  const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
3137
3175
  const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
3138
3176
  callStep = new LazyCallStep({
@@ -3240,6 +3278,8 @@ var WorkflowContext = class {
3240
3278
  );
3241
3279
  }
3242
3280
  async invoke(stepName, settings) {
3281
+ validateLabel(settings.label);
3282
+ validateFlowControl(settings.flowControl);
3243
3283
  return await this.addStep(
3244
3284
  new LazyInvokeStep(this, stepName, settings)
3245
3285
  );
@@ -3325,7 +3365,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3325
3365
  url: context.url,
3326
3366
  initialPayload: context.requestPayload,
3327
3367
  env: context.env,
3328
- label: context.label,
3368
+ label: context.labels,
3329
3369
  retried: context.retried
3330
3370
  });
3331
3371
  try {
package/h3.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase,
4
4
  serveManyBase
5
- } from "./chunk-G24J5PCC.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-B_E1VAK6.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-D3D9uE4u.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-B_E1VAK6.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-kPOasiyb.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
@@ -216,6 +216,31 @@ var import_qstash4 = require("@upstash/qstash");
216
216
  // src/utils.ts
217
217
  var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
218
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
+ }
219
244
  function getRandomInt() {
220
245
  return Math.floor(Math.random() * NANOID_CHARS.length);
221
246
  }
@@ -751,8 +776,8 @@ var triggerFirstInvocation = async (params) => {
751
776
  if (unknownSdk) {
752
777
  headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
753
778
  }
754
- if (workflowContext.label) {
755
- headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
779
+ if (workflowContext.labels.length > 0) {
780
+ headers[WORKFLOW_LABEL_HEADER] = serializeLabel(workflowContext.labels);
756
781
  }
757
782
  const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
758
783
  return {
@@ -1554,8 +1579,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1554
1579
  });
1555
1580
  triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1556
1581
  if (label) {
1557
- triggerHeaders[WORKFLOW_LABEL_HEADER] = label;
1558
- 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;
1559
1585
  }
1560
1586
  return { headers: triggerHeaders, contentType };
1561
1587
  }
@@ -2696,9 +2722,10 @@ var WorkflowContext = class {
2696
2722
  */
2697
2723
  env;
2698
2724
  /**
2699
- * Label to apply to the workflow run.
2725
+ * Labels attached to the workflow run.
2700
2726
  *
2701
- * 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[]`.
2702
2729
  *
2703
2730
  * Can be set by passing a `label` parameter when triggering the workflow
2704
2731
  * with `client.trigger`:
@@ -2706,11 +2733,20 @@ var WorkflowContext = class {
2706
2733
  * ```ts
2707
2734
  * await client.trigger({
2708
2735
  * url: "https://workflow-endpoint.com",
2709
- * label: "my-label"
2736
+ * label: ["label-1", "label-2"]
2710
2737
  * });
2711
2738
  * ```
2712
2739
  */
2713
- 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
+ }
2714
2750
  /**
2715
2751
  * Number of times QStash has retried delivering the current request.
2716
2752
  *
@@ -2741,7 +2777,7 @@ var WorkflowContext = class {
2741
2777
  this.headers = headers;
2742
2778
  this.requestPayload = initialPayload;
2743
2779
  this.env = env ?? {};
2744
- this.label = label;
2780
+ this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
2745
2781
  this.retried = retried ?? 0;
2746
2782
  const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
2747
2783
  middlewareManagerInstance.assignContext(this);
@@ -2822,8 +2858,10 @@ var WorkflowContext = class {
2822
2858
  await this.addStep(new LazySleepUntilStep(this, stepName, time));
2823
2859
  }
2824
2860
  async call(stepName, settings) {
2861
+ validateFlowControl(settings.flowControl);
2825
2862
  let callStep;
2826
2863
  if ("workflow" in settings) {
2864
+ validateLabel(settings.label);
2827
2865
  const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
2828
2866
  const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
2829
2867
  callStep = new LazyCallStep({
@@ -2931,6 +2969,8 @@ var WorkflowContext = class {
2931
2969
  );
2932
2970
  }
2933
2971
  async invoke(stepName, settings) {
2972
+ validateLabel(settings.label);
2973
+ validateFlowControl(settings.flowControl);
2934
2974
  return await this.addStep(
2935
2975
  new LazyInvokeStep(this, stepName, settings)
2936
2976
  );
@@ -3016,7 +3056,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3016
3056
  url: context.url,
3017
3057
  initialPayload: context.requestPayload,
3018
3058
  env: context.env,
3019
- label: context.label,
3059
+ label: context.labels,
3020
3060
  retried: context.retried
3021
3061
  });
3022
3062
  try {
package/hono.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase,
4
4
  serveManyBase
5
- } from "./chunk-G24J5PCC.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-B_E1VAK6.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-B_E1VAK6.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-B_E1VAK6.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-B_E1VAK6.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. */
package/index.js CHANGED
@@ -290,6 +290,31 @@ var import_qstash4 = require("@upstash/qstash");
290
290
  // src/utils.ts
291
291
  var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
292
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
+ }
293
318
  function getRandomInt() {
294
319
  return Math.floor(Math.random() * NANOID_CHARS.length);
295
320
  }
@@ -825,8 +850,8 @@ var triggerFirstInvocation = async (params) => {
825
850
  if (unknownSdk) {
826
851
  headers[WORKFLOW_UNKOWN_SDK_TRIGGER_HEADER] = "true";
827
852
  }
828
- if (workflowContext.label) {
829
- headers[WORKFLOW_LABEL_HEADER] = workflowContext.label;
853
+ if (workflowContext.labels.length > 0) {
854
+ headers[WORKFLOW_LABEL_HEADER] = serializeLabel(workflowContext.labels);
830
855
  }
831
856
  const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
832
857
  return {
@@ -1628,8 +1653,9 @@ var LazyInvokeStep = class extends BaseLazyStep {
1628
1653
  });
1629
1654
  triggerHeaders["Upstash-Workflow-Invoke"] = "true";
1630
1655
  if (label) {
1631
- triggerHeaders[WORKFLOW_LABEL_HEADER] = label;
1632
- 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;
1633
1659
  }
1634
1660
  return { headers: triggerHeaders, contentType };
1635
1661
  }
@@ -2703,9 +2729,10 @@ var WorkflowContext = class {
2703
2729
  */
2704
2730
  env;
2705
2731
  /**
2706
- * Label to apply to the workflow run.
2732
+ * Labels attached to the workflow run.
2707
2733
  *
2708
- * 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[]`.
2709
2736
  *
2710
2737
  * Can be set by passing a `label` parameter when triggering the workflow
2711
2738
  * with `client.trigger`:
@@ -2713,11 +2740,20 @@ var WorkflowContext = class {
2713
2740
  * ```ts
2714
2741
  * await client.trigger({
2715
2742
  * url: "https://workflow-endpoint.com",
2716
- * label: "my-label"
2743
+ * label: ["label-1", "label-2"]
2717
2744
  * });
2718
2745
  * ```
2719
2746
  */
2720
- 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
+ }
2721
2757
  /**
2722
2758
  * Number of times QStash has retried delivering the current request.
2723
2759
  *
@@ -2748,7 +2784,7 @@ var WorkflowContext = class {
2748
2784
  this.headers = headers;
2749
2785
  this.requestPayload = initialPayload;
2750
2786
  this.env = env ?? {};
2751
- this.label = label;
2787
+ this.labels = label === void 0 ? [] : Array.isArray(label) ? label : label ? label.split(",") : [];
2752
2788
  this.retried = retried ?? 0;
2753
2789
  const middlewareManagerInstance = middlewareManager ?? new MiddlewareManager([]);
2754
2790
  middlewareManagerInstance.assignContext(this);
@@ -2829,8 +2865,10 @@ var WorkflowContext = class {
2829
2865
  await this.addStep(new LazySleepUntilStep(this, stepName, time));
2830
2866
  }
2831
2867
  async call(stepName, settings) {
2868
+ validateFlowControl(settings.flowControl);
2832
2869
  let callStep;
2833
2870
  if ("workflow" in settings) {
2871
+ validateLabel(settings.label);
2834
2872
  const url = getNewUrlFromWorkflowId(this.url, settings.workflow.workflowId);
2835
2873
  const stringBody = typeof settings.body === "string" ? settings.body : settings.body === void 0 ? void 0 : JSON.stringify(settings.body);
2836
2874
  callStep = new LazyCallStep({
@@ -2938,6 +2976,8 @@ var WorkflowContext = class {
2938
2976
  );
2939
2977
  }
2940
2978
  async invoke(stepName, settings) {
2979
+ validateLabel(settings.label);
2980
+ validateFlowControl(settings.flowControl);
2941
2981
  return await this.addStep(
2942
2982
  new LazyInvokeStep(this, stepName, settings)
2943
2983
  );
@@ -3023,7 +3063,7 @@ var DisabledWorkflowContext = class _DisabledWorkflowContext extends WorkflowCon
3023
3063
  url: context.url,
3024
3064
  initialPayload: context.requestPayload,
3025
3065
  env: context.env,
3026
- label: context.label,
3066
+ label: context.labels,
3027
3067
  retried: context.retried
3028
3068
  });
3029
3069
  try {
@@ -4111,13 +4151,15 @@ var Client4 = class {
4111
4151
  const isBatchInput = Array.isArray(params);
4112
4152
  const options = isBatchInput ? params : [params];
4113
4153
  const invocations = options.map((option) => {
4154
+ validateLabel(option.label);
4155
+ validateFlowControl(option.flowControl);
4114
4156
  const failureUrl = option.failureUrl ?? option.url;
4115
4157
  const finalWorkflowRunId = getWorkflowRunId(option.workflowRunId);
4116
4158
  const context = new WorkflowContext({
4117
4159
  qstashClient: this.client,
4118
4160
  headers: new Headers({
4119
4161
  ...option.headers ?? {},
4120
- ...option.label ? { [WORKFLOW_LABEL_HEADER]: option.label } : {}
4162
+ ...option.label ? { [WORKFLOW_LABEL_HEADER]: serializeLabel(option.label) } : {}
4121
4163
  }),
4122
4164
  initialPayload: option.body,
4123
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-G24J5PCC.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-B_E1VAK6.mjs';
3
- import { s as serveManyBase } from './serve-many-D3D9uE4u.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-B_E1VAK6.js';
3
- import { s as serveManyBase } from './serve-many-kPOasiyb.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