@upstash/workflow 0.2.13 → 0.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/h3.js CHANGED
@@ -403,7 +403,7 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
403
403
  var DEFAULT_CONTENT_TYPE = "application/json";
404
404
  var NO_CONCURRENCY = 1;
405
405
  var DEFAULT_RETRIES = 3;
406
- var VERSION = "v0.2.13";
406
+ var VERSION = "v0.2.14";
407
407
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
408
408
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
409
409
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
@@ -911,59 +911,72 @@ var StepTypes = [
911
911
 
912
912
  // src/workflow-requests.ts
913
913
  var import_qstash3 = require("@upstash/qstash");
914
- var triggerFirstInvocation = async ({
915
- workflowContext,
916
- useJSONContent,
917
- telemetry: telemetry2,
918
- debug,
919
- invokeCount,
920
- delay
921
- }) => {
922
- const { headers } = getHeaders({
923
- initHeaderValue: "true",
924
- workflowConfig: {
925
- workflowRunId: workflowContext.workflowRunId,
926
- workflowUrl: workflowContext.url,
927
- failureUrl: workflowContext.failureUrl,
928
- retries: workflowContext.retries,
929
- telemetry: telemetry2,
930
- flowControl: workflowContext.flowControl,
931
- useJSONContent: useJSONContent ?? false
932
- },
933
- invokeCount: invokeCount ?? 0,
934
- userHeaders: workflowContext.headers
935
- });
936
- if (workflowContext.headers.get("content-type")) {
937
- headers["content-type"] = workflowContext.headers.get("content-type");
938
- }
939
- if (useJSONContent) {
940
- headers["content-type"] = "application/json";
941
- }
942
- try {
943
- const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
944
- const result = await workflowContext.qstashClient.publish({
945
- headers,
946
- method: "POST",
947
- body,
948
- url: workflowContext.url,
949
- delay
950
- });
951
- if (result.deduplicated) {
952
- await debug?.log("WARN", "SUBMIT_FIRST_INVOCATION", {
953
- message: `Workflow run ${workflowContext.workflowRunId} already exists. A new one isn't created.`,
914
+ var triggerFirstInvocation = async (params) => {
915
+ const firstInvocationParams = Array.isArray(params) ? params : [params];
916
+ const workflowContextClient = firstInvocationParams[0].workflowContext.qstashClient;
917
+ const invocationBatch = firstInvocationParams.map(
918
+ ({ workflowContext, useJSONContent, telemetry: telemetry2, invokeCount, delay }) => {
919
+ const { headers } = getHeaders({
920
+ initHeaderValue: "true",
921
+ workflowConfig: {
922
+ workflowRunId: workflowContext.workflowRunId,
923
+ workflowUrl: workflowContext.url,
924
+ failureUrl: workflowContext.failureUrl,
925
+ retries: workflowContext.retries,
926
+ telemetry: telemetry2,
927
+ flowControl: workflowContext.flowControl,
928
+ useJSONContent: useJSONContent ?? false
929
+ },
930
+ invokeCount: invokeCount ?? 0,
931
+ userHeaders: workflowContext.headers
932
+ });
933
+ if (workflowContext.headers.get("content-type")) {
934
+ headers["content-type"] = workflowContext.headers.get("content-type");
935
+ }
936
+ if (useJSONContent) {
937
+ headers["content-type"] = "application/json";
938
+ }
939
+ const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
940
+ return {
954
941
  headers,
955
- requestPayload: workflowContext.requestPayload,
942
+ method: "POST",
943
+ body,
956
944
  url: workflowContext.url,
957
- messageId: result.messageId
958
- });
945
+ delay
946
+ };
947
+ }
948
+ );
949
+ try {
950
+ const results = await workflowContextClient.batch(invocationBatch);
951
+ const invocationStatuses = [];
952
+ for (let i = 0; i < results.length; i++) {
953
+ const result = results[i];
954
+ const invocationParams = firstInvocationParams[i];
955
+ if (result.deduplicated) {
956
+ await invocationParams.debug?.log("WARN", "SUBMIT_FIRST_INVOCATION", {
957
+ message: `Workflow run ${invocationParams.workflowContext.workflowRunId} already exists. A new one isn't created.`,
958
+ headers: invocationBatch[i].headers,
959
+ requestPayload: invocationParams.workflowContext.requestPayload,
960
+ url: invocationParams.workflowContext.url,
961
+ messageId: result.messageId
962
+ });
963
+ invocationStatuses.push("workflow-run-already-exists");
964
+ } else {
965
+ await invocationParams.debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
966
+ headers: invocationBatch[i].headers,
967
+ requestPayload: invocationParams.workflowContext.requestPayload,
968
+ url: invocationParams.workflowContext.url,
969
+ messageId: result.messageId
970
+ });
971
+ invocationStatuses.push("success");
972
+ }
973
+ }
974
+ const hasAnyDeduplicated = invocationStatuses.some(
975
+ (status) => status === "workflow-run-already-exists"
976
+ );
977
+ if (hasAnyDeduplicated) {
959
978
  return ok("workflow-run-already-exists");
960
979
  } else {
961
- await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
962
- headers,
963
- requestPayload: workflowContext.requestPayload,
964
- url: workflowContext.url,
965
- messageId: result.messageId
966
- });
967
980
  return ok("success");
968
981
  }
969
982
  } catch (error) {
@@ -1453,7 +1466,7 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1453
1466
  return { header, status, body };
1454
1467
  }
1455
1468
  }
1456
- static applicationHeaders = /* @__PURE__ */ new Set([
1469
+ static applicationContentTypes = [
1457
1470
  "application/json",
1458
1471
  "application/xml",
1459
1472
  "application/javascript",
@@ -1462,12 +1475,12 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1462
1475
  "application/ld+json",
1463
1476
  "application/rss+xml",
1464
1477
  "application/atom+xml"
1465
- ]);
1478
+ ];
1466
1479
  static isText = (contentTypeHeader) => {
1467
1480
  if (!contentTypeHeader) {
1468
1481
  return false;
1469
1482
  }
1470
- if (_LazyCallStep.applicationHeaders.has(contentTypeHeader)) {
1483
+ if (_LazyCallStep.applicationContentTypes.some((type) => contentTypeHeader.includes(type))) {
1471
1484
  return true;
1472
1485
  }
1473
1486
  if (contentTypeHeader.startsWith("text/")) {
package/h3.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase,
4
4
  serveManyBase
5
- } from "./chunk-XVNSBBDC.mjs";
5
+ } from "./chunk-RMS2NQ3K.mjs";
6
6
 
7
7
  // node_modules/defu/dist/defu.mjs
8
8
  function isPlainObject(value) {
package/hono.js CHANGED
@@ -91,7 +91,7 @@ var WORKFLOW_PROTOCOL_VERSION_HEADER = "Upstash-Workflow-Sdk-Version";
91
91
  var DEFAULT_CONTENT_TYPE = "application/json";
92
92
  var NO_CONCURRENCY = 1;
93
93
  var DEFAULT_RETRIES = 3;
94
- var VERSION = "v0.2.13";
94
+ var VERSION = "v0.2.14";
95
95
  var SDK_TELEMETRY = `@upstash/workflow@${VERSION}`;
96
96
  var TELEMETRY_HEADER_SDK = "Upstash-Telemetry-Sdk";
97
97
  var TELEMETRY_HEADER_FRAMEWORK = "Upstash-Telemetry-Framework";
@@ -599,59 +599,72 @@ var StepTypes = [
599
599
 
600
600
  // src/workflow-requests.ts
601
601
  var import_qstash3 = require("@upstash/qstash");
602
- var triggerFirstInvocation = async ({
603
- workflowContext,
604
- useJSONContent,
605
- telemetry: telemetry2,
606
- debug,
607
- invokeCount,
608
- delay
609
- }) => {
610
- const { headers } = getHeaders({
611
- initHeaderValue: "true",
612
- workflowConfig: {
613
- workflowRunId: workflowContext.workflowRunId,
614
- workflowUrl: workflowContext.url,
615
- failureUrl: workflowContext.failureUrl,
616
- retries: workflowContext.retries,
617
- telemetry: telemetry2,
618
- flowControl: workflowContext.flowControl,
619
- useJSONContent: useJSONContent ?? false
620
- },
621
- invokeCount: invokeCount ?? 0,
622
- userHeaders: workflowContext.headers
623
- });
624
- if (workflowContext.headers.get("content-type")) {
625
- headers["content-type"] = workflowContext.headers.get("content-type");
626
- }
627
- if (useJSONContent) {
628
- headers["content-type"] = "application/json";
629
- }
630
- try {
631
- const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
632
- const result = await workflowContext.qstashClient.publish({
633
- headers,
634
- method: "POST",
635
- body,
636
- url: workflowContext.url,
637
- delay
638
- });
639
- if (result.deduplicated) {
640
- await debug?.log("WARN", "SUBMIT_FIRST_INVOCATION", {
641
- message: `Workflow run ${workflowContext.workflowRunId} already exists. A new one isn't created.`,
602
+ var triggerFirstInvocation = async (params) => {
603
+ const firstInvocationParams = Array.isArray(params) ? params : [params];
604
+ const workflowContextClient = firstInvocationParams[0].workflowContext.qstashClient;
605
+ const invocationBatch = firstInvocationParams.map(
606
+ ({ workflowContext, useJSONContent, telemetry: telemetry2, invokeCount, delay }) => {
607
+ const { headers } = getHeaders({
608
+ initHeaderValue: "true",
609
+ workflowConfig: {
610
+ workflowRunId: workflowContext.workflowRunId,
611
+ workflowUrl: workflowContext.url,
612
+ failureUrl: workflowContext.failureUrl,
613
+ retries: workflowContext.retries,
614
+ telemetry: telemetry2,
615
+ flowControl: workflowContext.flowControl,
616
+ useJSONContent: useJSONContent ?? false
617
+ },
618
+ invokeCount: invokeCount ?? 0,
619
+ userHeaders: workflowContext.headers
620
+ });
621
+ if (workflowContext.headers.get("content-type")) {
622
+ headers["content-type"] = workflowContext.headers.get("content-type");
623
+ }
624
+ if (useJSONContent) {
625
+ headers["content-type"] = "application/json";
626
+ }
627
+ const body = typeof workflowContext.requestPayload === "string" ? workflowContext.requestPayload : JSON.stringify(workflowContext.requestPayload);
628
+ return {
642
629
  headers,
643
- requestPayload: workflowContext.requestPayload,
630
+ method: "POST",
631
+ body,
644
632
  url: workflowContext.url,
645
- messageId: result.messageId
646
- });
633
+ delay
634
+ };
635
+ }
636
+ );
637
+ try {
638
+ const results = await workflowContextClient.batch(invocationBatch);
639
+ const invocationStatuses = [];
640
+ for (let i = 0; i < results.length; i++) {
641
+ const result = results[i];
642
+ const invocationParams = firstInvocationParams[i];
643
+ if (result.deduplicated) {
644
+ await invocationParams.debug?.log("WARN", "SUBMIT_FIRST_INVOCATION", {
645
+ message: `Workflow run ${invocationParams.workflowContext.workflowRunId} already exists. A new one isn't created.`,
646
+ headers: invocationBatch[i].headers,
647
+ requestPayload: invocationParams.workflowContext.requestPayload,
648
+ url: invocationParams.workflowContext.url,
649
+ messageId: result.messageId
650
+ });
651
+ invocationStatuses.push("workflow-run-already-exists");
652
+ } else {
653
+ await invocationParams.debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
654
+ headers: invocationBatch[i].headers,
655
+ requestPayload: invocationParams.workflowContext.requestPayload,
656
+ url: invocationParams.workflowContext.url,
657
+ messageId: result.messageId
658
+ });
659
+ invocationStatuses.push("success");
660
+ }
661
+ }
662
+ const hasAnyDeduplicated = invocationStatuses.some(
663
+ (status) => status === "workflow-run-already-exists"
664
+ );
665
+ if (hasAnyDeduplicated) {
647
666
  return ok("workflow-run-already-exists");
648
667
  } else {
649
- await debug?.log("SUBMIT", "SUBMIT_FIRST_INVOCATION", {
650
- headers,
651
- requestPayload: workflowContext.requestPayload,
652
- url: workflowContext.url,
653
- messageId: result.messageId
654
- });
655
668
  return ok("success");
656
669
  }
657
670
  } catch (error) {
@@ -1141,7 +1154,7 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1141
1154
  return { header, status, body };
1142
1155
  }
1143
1156
  }
1144
- static applicationHeaders = /* @__PURE__ */ new Set([
1157
+ static applicationContentTypes = [
1145
1158
  "application/json",
1146
1159
  "application/xml",
1147
1160
  "application/javascript",
@@ -1150,12 +1163,12 @@ var LazyCallStep = class _LazyCallStep extends BaseLazyStep {
1150
1163
  "application/ld+json",
1151
1164
  "application/rss+xml",
1152
1165
  "application/atom+xml"
1153
- ]);
1166
+ ];
1154
1167
  static isText = (contentTypeHeader) => {
1155
1168
  if (!contentTypeHeader) {
1156
1169
  return false;
1157
1170
  }
1158
- if (_LazyCallStep.applicationHeaders.has(contentTypeHeader)) {
1171
+ if (_LazyCallStep.applicationContentTypes.some((type) => contentTypeHeader.includes(type))) {
1159
1172
  return true;
1160
1173
  }
1161
1174
  if (contentTypeHeader.startsWith("text/")) {
package/hono.mjs CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  SDK_TELEMETRY,
3
3
  serveBase,
4
4
  serveManyBase
5
- } from "./chunk-XVNSBBDC.mjs";
5
+ } from "./chunk-RMS2NQ3K.mjs";
6
6
 
7
7
  // platforms/hono.ts
8
8
  var telemetry = {
package/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { R as RouteFunction, W as WorkflowServeOptions, E as ExclusiveValidationOptions, T as Telemetry, S as StepType, a as RawStep, N as NotifyResponse, b as Waiter, c as Step } from './types-C1WIgVLA.mjs';
2
2
  export { A as AsyncStepFunction, C as CallResponse, r as CallSettings, D as Duration, l as FailureFunctionPayload, F as FinishCondition, H as HeaderParams, t as InvokableWorkflow, s as InvokeStepResponse, I as InvokeWorkflowRequest, L as LazyInvokeStepParams, u as LogLevel, p as NotifyStepResponse, P as ParallelCallState, k as PublicServeOptions, m as RequiredExceptFields, j as StepFunction, h as StepTypes, i as SyncStepFunction, q as WaitEventOptions, n as WaitRequest, o as WaitStepResponse, f as WorkflowClient, e as WorkflowContext, w as WorkflowLogger, v as WorkflowLoggerOptions, g as WorkflowReceiver, d as WorkflowTool } from './types-C1WIgVLA.mjs';
3
- import { HTTPMethods, State, FlowControl, PublishRequest, Client as Client$1, QstashError } from '@upstash/qstash';
3
+ import { FlowControl, PublishRequest, HTTPMethods, State, Client as Client$1, QstashError } from '@upstash/qstash';
4
4
  import 'zod';
5
5
  import 'ai';
6
6
  import '@ai-sdk/openai';
@@ -78,6 +78,10 @@ type BaseStepLog = {
78
78
  * headers
79
79
  */
80
80
  headers: Record<string, string[]>;
81
+ /**
82
+ * retries
83
+ */
84
+ retries: number;
81
85
  };
82
86
  type CallUrlGroup = {
83
87
  /**
@@ -184,6 +188,19 @@ type StepLogGroup = {
184
188
  steps: {
185
189
  messageId: string;
186
190
  state: "STEP_PROGRESS" | "STEP_RETRY" | "STEP_FAILED" | "STEP_CANCELED";
191
+ /**
192
+ * retries
193
+ */
194
+ retries: number;
195
+ /**
196
+ * errors which occured in the step
197
+ */
198
+ errors?: {
199
+ error: string;
200
+ headers: Record<string, string[]>;
201
+ status: number;
202
+ time: number;
203
+ }[];
187
204
  }[];
188
205
  /**
189
206
  * Log which belongs to the next step
@@ -215,6 +232,9 @@ type FailureFunctionLog = {
215
232
  * Response body of the step which caused the workflow to fail
216
233
  */
217
234
  failResponse: string;
235
+ /**
236
+ * @deprecated use dlqId field of the workflow run itself
237
+ */
218
238
  dlqId: string;
219
239
  };
220
240
  type WorkflowRunLog = {
@@ -281,11 +301,73 @@ type WorkflowRunLog = {
281
301
  */
282
302
  workflowRunCreatedAt: number;
283
303
  };
304
+ /**
305
+ * If the workflow run has failed, id of the run in DLQ
306
+ */
307
+ dlqId?: string;
284
308
  };
285
309
  type WorkflowRunLogs = {
286
310
  cursor: string;
287
311
  runs: WorkflowRunLog[];
288
312
  };
313
+ type TriggerOptions = {
314
+ /**
315
+ * URL of the workflow to trigger
316
+ */
317
+ url: string;
318
+ /**
319
+ * Body to send to the workflow
320
+ */
321
+ body?: unknown;
322
+ /**
323
+ * Headers to send to the workflow
324
+ */
325
+ headers?: Record<string, string>;
326
+ /**
327
+ * Workflow run id to use for the workflow run.
328
+ * If not provided, a random workflow run id will be generated.
329
+ */
330
+ workflowRunId?: string;
331
+ /**
332
+ * Number of retries to perform if the request fails.
333
+ *
334
+ * @default 3
335
+ */
336
+ retries?: number;
337
+ /**
338
+ * Flow control to use for the workflow run.
339
+ * If not provided, no flow control will be used.
340
+ */
341
+ flowControl?: FlowControl;
342
+ /**
343
+ * Delay to apply before triggering the workflow.
344
+ */
345
+ delay?: PublishRequest["delay"];
346
+ } & ({
347
+ /**
348
+ * URL to call if the first request to the workflow endpoint fails
349
+ */
350
+ failureUrl?: never;
351
+ /**
352
+ * Whether the workflow endpoint has a failure function
353
+ * defined to be invoked if the first request fails.
354
+ *
355
+ * If true, the failureUrl will be ignored.
356
+ */
357
+ useFailureFunction?: true;
358
+ } | {
359
+ /**
360
+ * URL to call if the first request to the workflow endpoint fails
361
+ */
362
+ failureUrl?: string;
363
+ /**
364
+ * Whether the workflow endpoint has a failure function
365
+ * defined to be invoked if the first request fails.
366
+ *
367
+ * If true, the failureUrl will be ignored.
368
+ */
369
+ useFailureFunction?: never;
370
+ });
289
371
 
290
372
  type ClientConfig = ConstructorParameters<typeof Client$1>[0];
291
373
  /**
@@ -398,8 +480,9 @@ declare class Client {
398
480
  eventId: string;
399
481
  }): Promise<Required<Waiter>[]>;
400
482
  /**
401
- * Trigger new workflow run and returns the workflow run id
483
+ * Trigger new workflow run and returns the workflow run id or an array of workflow run ids
402
484
  *
485
+ * trigger a single workflow run:
403
486
  * ```ts
404
487
  * const { workflowRunId } = await client.trigger({
405
488
  * url: "https://workflow-endpoint.com",
@@ -412,6 +495,31 @@ declare class Client {
412
495
  * console.log(workflowRunId)
413
496
  * // wfr_my-workflow
414
497
  * ```
498
+ * trigger multiple workflow runs:
499
+ * ```ts
500
+ * const result = await client.trigger([
501
+ * {
502
+ * url: "https://workflow-endpoint.com",
503
+ * body: "hello there!", // Optional body
504
+ * headers: { ... }, // Optional headers
505
+ * workflowRunId: "my-workflow", // Optional workflow run ID
506
+ * retries: 3 // Optional retries for the initial request
507
+ * },
508
+ * {
509
+ * url: "https://workflow-endpoint-2.com",
510
+ * body: "hello world!", // Optional body
511
+ * headers: { ... }, // Optional headers
512
+ * workflowRunId: "my-workflow-2", // Optional workflow run ID
513
+ * retries: 5 // Optional retries for the initial request
514
+ * },
515
+ * ]);
516
+ *
517
+ * console.log(result)
518
+ * // [
519
+ * // { workflowRunId: "wfr_my-workflow" },
520
+ * // { workflowRunId: "wfr_my-workflow-2" },
521
+ * // ]
522
+ * ```
415
523
  *
416
524
  * @param url URL of the workflow
417
525
  * @param body body to start the workflow with
@@ -428,19 +536,14 @@ declare class Client {
428
536
  * @param delay Delay for the workflow run. This is used to delay the
429
537
  * execution of the workflow run. The delay is in seconds or can be passed
430
538
  * as a string with a time unit (e.g. "1h", "30m", "15s").
431
- * @returns workflow run id
432
- */
433
- trigger({ url, body, headers, workflowRunId, retries, flowControl, delay, }: {
434
- url: string;
435
- body?: unknown;
436
- headers?: Record<string, string>;
437
- workflowRunId?: string;
438
- retries?: number;
439
- flowControl?: FlowControl;
440
- delay?: PublishRequest["delay"];
441
- }): Promise<{
539
+ * @returns workflow run id or an array of workflow run ids
540
+ */
541
+ trigger(params: TriggerOptions): Promise<{
442
542
  workflowRunId: string;
443
543
  }>;
544
+ trigger(params: TriggerOptions[]): Promise<{
545
+ workflowRunId: string;
546
+ }[]>;
444
547
  /**
445
548
  * Fetches logs for workflow runs.
446
549
  *
@@ -504,4 +607,4 @@ declare class WorkflowAbort extends Error {
504
607
  constructor(stepName: string, stepInfo?: Step, cancelWorkflow?: boolean);
505
608
  }
506
609
 
507
- export { Client, ExclusiveValidationOptions, NotifyResponse, RawStep, RouteFunction, Step, type StepLog, StepType, Telemetry, Waiter, WorkflowAbort, WorkflowError, type WorkflowRunLog, type WorkflowRunLogs, WorkflowServeOptions, serve };
610
+ export { Client, ExclusiveValidationOptions, NotifyResponse, RawStep, RouteFunction, Step, type StepLog, StepType, Telemetry, type TriggerOptions, Waiter, WorkflowAbort, WorkflowError, type WorkflowRunLog, type WorkflowRunLogs, WorkflowServeOptions, serve };