@upstash/qstash 2.9.1-rc.1 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/workflow.js CHANGED
@@ -398,43 +398,6 @@ function decodeBase64(base64) {
398
398
  }
399
399
  }
400
400
  }
401
- function buildBulkActionFilterPayload(request, options) {
402
- const hasDlqIds = "dlqIds" in request && request.dlqIds !== void 0;
403
- const hasAll = "all" in request && Boolean(request.all);
404
- const filterKeys = Object.keys(request).filter((k) => k !== "dlqIds" && k !== "all");
405
- const hasFilters = filterKeys.length > 0;
406
- if (hasDlqIds && hasAll) {
407
- throw new QstashError("dlqIds and all: true are mutually exclusive.");
408
- }
409
- if (hasDlqIds && hasFilters) {
410
- throw new QstashError(
411
- `dlqIds cannot be combined with filter fields: ${filterKeys.join(", ")}.`
412
- );
413
- }
414
- if (hasAll && hasFilters) {
415
- throw new QstashError(
416
- `all: true cannot be combined with filter fields: ${filterKeys.join(", ")}.`
417
- );
418
- }
419
- if (hasAll)
420
- return {};
421
- const { urlGroup, callerIp, ...rest } = request;
422
- const payload = {
423
- ...rest,
424
- ...urlGroup === void 0 || typeof urlGroup !== "string" ? {} : { topicName: urlGroup },
425
- ...callerIp === void 0 || typeof callerIp !== "string" ? {} : options?.callerIpCasing ? { callerIP: callerIp } : { callerIp }
426
- };
427
- if (Object.keys(payload).length === 0) {
428
- throw new QstashError(
429
- "No filters provided. Pass { all: true } to explicitly target all messages."
430
- );
431
- }
432
- return payload;
433
- }
434
- function normalizeCursor(response) {
435
- const cursor = response.cursor;
436
- return { ...response, cursor: cursor || void 0 };
437
- }
438
401
  function getRuntime() {
439
402
  if (typeof process === "object" && typeof process.versions == "object" && process.versions.bun)
440
403
  return `bun@${process.versions.bun}`;
@@ -664,20 +627,17 @@ var DLQ = class {
664
627
  /**
665
628
  * List messages in the dlq
666
629
  */
667
- async listMessages(options = {}) {
668
- const { urlGroup, ...restFilter } = options.filter ?? {};
630
+ async listMessages(options) {
669
631
  const filterPayload = {
670
- ...restFilter,
671
- ...urlGroup === void 0 ? {} : { topicName: urlGroup }
632
+ ...options?.filter,
633
+ topicName: options?.filter?.urlGroup
672
634
  };
673
635
  const messagesPayload = await this.http.request({
674
636
  method: "GET",
675
637
  path: ["v2", "dlq"],
676
638
  query: {
677
- cursor: options.cursor,
678
- count: options.count,
679
- order: options.order,
680
- trimBody: options.trimBody,
639
+ cursor: options?.cursor,
640
+ count: options?.count,
681
641
  ...filterPayload
682
642
  }
683
643
  });
@@ -693,70 +653,127 @@ var DLQ = class {
693
653
  };
694
654
  }
695
655
  /**
696
- * Remove messages from the dlq.
697
- *
698
- * Can be called with:
699
- * - A single dlqId: `delete("id")`
700
- * - An array of dlqIds: `delete(["id1", "id2"])`
701
- * - An object with dlqIds: `delete({ dlqIds: ["id1", "id2"] })`
702
- * - A filter object: `delete({ url: "https://example.com", label: "label" })`
703
- * - All messages: `delete({ all: true })`
704
- *
705
- * Note: passing an empty array returns `{ deleted: 0 }` without making a request.
656
+ * Remove a message from the dlq using it's `dlqId`
706
657
  */
707
- async delete(request) {
708
- if (typeof request === "string") {
709
- await this.http.request({
710
- method: "DELETE",
711
- path: ["v2", "dlq", request],
712
- parseResponseAsJson: false
713
- });
714
- return { deleted: 1 };
715
- }
716
- if (Array.isArray(request) && request.length === 0)
717
- return { deleted: 0 };
718
- const filters = Array.isArray(request) ? { dlqIds: request } : request;
719
- return normalizeCursor(
720
- await this.http.request({
721
- method: "DELETE",
722
- path: ["v2", "dlq"],
723
- query: buildBulkActionFilterPayload(filters)
724
- })
725
- );
658
+ async delete(dlqMessageId) {
659
+ return await this.http.request({
660
+ method: "DELETE",
661
+ path: ["v2", "dlq", dlqMessageId],
662
+ parseResponseAsJson: false
663
+ // there is no response
664
+ });
726
665
  }
727
666
  /**
728
667
  * Remove multiple messages from the dlq using their `dlqId`s
729
- *
730
- * @deprecated Use `delete` instead
731
668
  */
732
669
  async deleteMany(request) {
733
- return await this.delete(request);
670
+ return await this.http.request({
671
+ method: "DELETE",
672
+ path: ["v2", "dlq"],
673
+ headers: { "Content-Type": "application/json" },
674
+ body: JSON.stringify({ dlqIds: request.dlqIds })
675
+ });
676
+ }
677
+ };
678
+
679
+ // src/client/flow-control.ts
680
+ var FlowControlApi = class {
681
+ http;
682
+ constructor(http) {
683
+ this.http = http;
734
684
  }
735
685
  /**
736
- * Retry messages from the dlq.
686
+ * Get a single flow control by key.
687
+ */
688
+ async get(flowControlKey) {
689
+ return await this.http.request({
690
+ method: "GET",
691
+ path: ["v2", "flowControl", flowControlKey]
692
+ });
693
+ }
694
+ /**
695
+ * Get the global parallelism info.
696
+ */
697
+ async getGlobalParallelism() {
698
+ const response = await this.http.request({
699
+ method: "GET",
700
+ path: ["v2", "globalParallelism"]
701
+ });
702
+ return {
703
+ parallelismMax: response.parallelismMax ?? 0,
704
+ parallelismCount: response.parallelismCount ?? 0
705
+ };
706
+ }
707
+ /**
708
+ * Pause message delivery for a flow-control key.
737
709
  *
738
- * Can be called with:
739
- * - A single dlqId: `retry("id")`
740
- * - An array of dlqIds: `retry(["id1", "id2"])`
741
- * - An object with dlqIds: `retry({ dlqIds: ["id1", "id2"] })`
742
- * - A filter object: `retry({ url: "https://example.com", label: "label" })`
743
- * - All messages: `retry({ all: true })`
710
+ * Messages already in the waitlist will remain there.
711
+ * New incoming messages will be added directly to the waitlist.
712
+ */
713
+ async pause(flowControlKey) {
714
+ await this.http.request({
715
+ method: "POST",
716
+ path: ["v2", "flowControl", flowControlKey, "pause"],
717
+ parseResponseAsJson: false
718
+ });
719
+ }
720
+ /**
721
+ * Resume message delivery for a flow-control key.
722
+ */
723
+ async resume(flowControlKey) {
724
+ await this.http.request({
725
+ method: "POST",
726
+ path: ["v2", "flowControl", flowControlKey, "resume"],
727
+ parseResponseAsJson: false
728
+ });
729
+ }
730
+ /**
731
+ * Pin a processing configuration for a flow-control key.
744
732
  *
745
- * Note: passing an empty array returns `{ responses: [] }` without making a request.
733
+ * While pinned, the system ignores configurations provided by incoming
734
+ * messages and uses the pinned configuration instead.
746
735
  */
747
- async retry(request) {
748
- if (typeof request === "string")
749
- request = [request];
750
- if (Array.isArray(request) && request.length === 0)
751
- return { responses: [] };
752
- const filters = Array.isArray(request) ? { dlqIds: request } : request;
753
- return normalizeCursor(
754
- await this.http.request({
755
- method: "POST",
756
- path: ["v2", "dlq", "retry"],
757
- query: buildBulkActionFilterPayload(filters)
758
- })
759
- );
736
+ async pin(flowControlKey, options) {
737
+ await this.http.request({
738
+ method: "POST",
739
+ path: ["v2", "flowControl", flowControlKey, "pin"],
740
+ query: {
741
+ parallelism: options.parallelism,
742
+ rate: options.rate,
743
+ period: options.period
744
+ },
745
+ parseResponseAsJson: false
746
+ });
747
+ }
748
+ /**
749
+ * Remove the pinned configuration for a flow-control key.
750
+ *
751
+ * After unpinning, the system resumes updating the configuration
752
+ * based on incoming messages.
753
+ */
754
+ async unpin(flowControlKey, options) {
755
+ await this.http.request({
756
+ method: "POST",
757
+ path: ["v2", "flowControl", flowControlKey, "unpin"],
758
+ query: {
759
+ parallelism: options.parallelism,
760
+ rate: options.rate
761
+ },
762
+ parseResponseAsJson: false
763
+ });
764
+ }
765
+ /**
766
+ * Reset the rate configuration state for a flow-control key.
767
+ *
768
+ * Clears the current rate count and immediately ends the current period.
769
+ * The current timestamp becomes the start of the new rate period.
770
+ */
771
+ async resetRate(flowControlKey) {
772
+ await this.http.request({
773
+ method: "POST",
774
+ path: ["v2", "flowControl", flowControlKey, "resetRate"],
775
+ parseResponseAsJson: false
776
+ });
760
777
  }
761
778
  };
762
779
 
@@ -857,15 +874,7 @@ var HttpClient = class {
857
874
  const url = new URL([request.baseUrl ?? this.baseUrl, ...request.path].join("/"));
858
875
  if (request.query) {
859
876
  for (const [key, value] of Object.entries(request.query)) {
860
- if (value === void 0)
861
- continue;
862
- if (Array.isArray(value)) {
863
- for (const item of value) {
864
- url.searchParams.append(key, item);
865
- }
866
- } else if (value instanceof Date) {
867
- url.searchParams.set(key, value.getTime().toString());
868
- } else {
877
+ if (value !== void 0) {
869
878
  url.searchParams.set(key, value.toString());
870
879
  }
871
880
  }
@@ -1096,57 +1105,29 @@ var Messages = class {
1096
1105
  return message;
1097
1106
  }
1098
1107
  /**
1099
- * Cancel messages.
1100
- *
1101
- * Can be called with:
1102
- * - A single messageId: `cancel("id")`
1103
- * - An array of messageIds: `cancel(["id1", "id2"])`
1104
- * - A filter object: `cancel({ flowControlKey: "key", label: "label" })`
1105
- * - All messages: `cancel({ all: true })`
1106
- */
1107
- async cancel(request) {
1108
- if (typeof request === "string") {
1109
- return await this.http.request({
1110
- method: "DELETE",
1111
- path: ["v2", "messages", request]
1112
- });
1113
- }
1114
- if (Array.isArray(request) && request.length === 0)
1115
- return { cancelled: 0 };
1116
- const filters = Array.isArray(request) ? { messageIds: request } : request;
1117
- return await this.http.request({
1118
- method: "DELETE",
1119
- path: ["v2", "messages"],
1120
- query: buildBulkActionFilterPayload(filters, { callerIpCasing: true })
1121
- });
1122
- }
1123
- /**
1124
- * Delete a message.
1125
- *
1126
- * @deprecated Use `cancel(messageId: string)` instead
1108
+ * Cancel a message
1127
1109
  */
1128
1110
  async delete(messageId) {
1129
- await this.http.request({
1111
+ return await this.http.request({
1130
1112
  method: "DELETE",
1131
1113
  path: ["v2", "messages", messageId],
1132
1114
  parseResponseAsJson: false
1133
1115
  });
1134
1116
  }
1135
- /**
1136
- * Cancel multiple messages by their messageIds.
1137
- *
1138
- * @deprecated Use `cancel(messageIds: string[])` instead
1139
- */
1140
1117
  async deleteMany(messageIds) {
1141
- const result = await this.cancel(messageIds);
1118
+ const result = await this.http.request({
1119
+ method: "DELETE",
1120
+ path: ["v2", "messages"],
1121
+ headers: { "Content-Type": "application/json" },
1122
+ body: JSON.stringify({ messageIds })
1123
+ });
1142
1124
  return result.cancelled;
1143
1125
  }
1144
- /**
1145
- * Cancel all messages
1146
- * @deprecated Use `cancel({all: true})` to cancel all
1147
- */
1148
1126
  async deleteAll() {
1149
- const result = await this.cancel({ all: true });
1127
+ const result = await this.http.request({
1128
+ method: "DELETE",
1129
+ path: ["v2", "messages"]
1130
+ });
1150
1131
  return result.cancelled;
1151
1132
  }
1152
1133
  };
@@ -1480,7 +1461,7 @@ var UrlGroups = class {
1480
1461
  };
1481
1462
 
1482
1463
  // version.ts
1483
- var VERSION = "v2.9.1-rc.1";
1464
+ var VERSION = "v2.9.1";
1484
1465
 
1485
1466
  // src/client/client.ts
1486
1467
  var Client = class {
@@ -1551,6 +1532,14 @@ var Client = class {
1551
1532
  get schedules() {
1552
1533
  return new Schedules(this.http);
1553
1534
  }
1535
+ /**
1536
+ * Access the flow control API.
1537
+ *
1538
+ * List, get, or reset flow controls.
1539
+ */
1540
+ get flowControl() {
1541
+ return new FlowControlApi(this.http);
1542
+ }
1554
1543
  /**
1555
1544
  * Access the workflow API.
1556
1545
  *
@@ -1675,47 +1664,39 @@ var Client = class {
1675
1664
  * }
1676
1665
  * ```
1677
1666
  */
1678
- async logs(request = {}) {
1679
- const {
1680
- urlGroup,
1681
- // eslint-disable-next-line @typescript-eslint/no-deprecated
1682
- topicName,
1683
- fromDate,
1684
- toDate,
1685
- callerIp,
1686
- messageIds,
1687
- // eslint-disable-next-line @typescript-eslint/no-deprecated
1688
- count: filterCount,
1689
- ...restFilter
1690
- } = request.filter ?? {};
1691
- const filterPayload = {
1692
- ...restFilter,
1693
- topicName: urlGroup ?? topicName,
1694
- fromDate,
1695
- toDate,
1696
- callerIp
1697
- };
1698
- let cursorString;
1699
- if (typeof request.cursor === "number" && request.cursor > 0) {
1700
- cursorString = request.cursor.toString();
1701
- } else if (typeof request.cursor === "string" && request.cursor !== "") {
1702
- cursorString = request.cursor;
1703
- }
1704
- const query = {
1705
- cursor: cursorString,
1706
- count: request.count ?? filterCount,
1707
- order: request.order,
1708
- trimBody: request.trimBody,
1709
- messageIds,
1710
- ...filterPayload
1711
- };
1667
+ async logs(request) {
1668
+ const query = {};
1669
+ if (typeof request?.cursor === "number" && request.cursor > 0) {
1670
+ query.cursor = request.cursor.toString();
1671
+ } else if (typeof request?.cursor === "string" && request.cursor !== "") {
1672
+ query.cursor = request.cursor;
1673
+ }
1674
+ for (const [key, value] of Object.entries(request?.filter ?? {})) {
1675
+ if (typeof value === "number" && value < 0) {
1676
+ continue;
1677
+ }
1678
+ if (key === "urlGroup") {
1679
+ query.topicName = value.toString();
1680
+ } else if (typeof value !== "undefined") {
1681
+ query[key] = value.toString();
1682
+ }
1683
+ }
1712
1684
  const responsePayload = await this.http.request({
1713
1685
  path: ["v2", "events"],
1714
1686
  method: "GET",
1715
1687
  query
1716
1688
  });
1717
- const logs = responsePayload.events.map((event) => ({ ...event, urlGroup: event.topicName }));
1718
- return { cursor: responsePayload.cursor, logs, events: logs };
1689
+ const logs = responsePayload.events.map((event) => {
1690
+ return {
1691
+ ...event,
1692
+ urlGroup: event.topicName
1693
+ };
1694
+ });
1695
+ return {
1696
+ cursor: responsePayload.cursor,
1697
+ logs,
1698
+ events: logs
1699
+ };
1719
1700
  }
1720
1701
  /**
1721
1702
  * @deprecated Will be removed in the next major release. Use the `logs` method instead.
package/workflow.mjs CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  WorkflowLogger,
7
7
  processOptions,
8
8
  serve
9
- } from "./chunk-RUCOF5QZ.mjs";
9
+ } from "./chunk-DT2X63FB.mjs";
10
10
  export {
11
11
  DisabledWorkflowContext,
12
12
  StepTypes,