@upstash/qstash 2.9.1 → 2.10.0

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
@@ -562,6 +562,14 @@ var QstashDailyRatelimitError = class extends QstashError {
562
562
  this.reset = args.reset;
563
563
  }
564
564
  };
565
+ var QstashEmptyArrayError = class extends QstashError {
566
+ constructor(parameterName) {
567
+ super(
568
+ `Empty array provided for query parameter "${parameterName}". This would result in no filter being applied, which could affect all resources.`
569
+ );
570
+ this.name = "QstashEmptyArrayError";
571
+ }
572
+ };
565
573
  var QStashWorkflowError = class extends QstashError {
566
574
  constructor(message) {
567
575
  super(message);
@@ -591,6 +599,7 @@ var formatWorkflowError = (error) => {
591
599
  };
592
600
 
593
601
  // src/client/utils.ts
602
+ var DEFAULT_BULK_COUNT = 100;
594
603
  var isIgnoredHeader = (header) => {
595
604
  const lowerCaseHeader = header.toLowerCase();
596
605
  return lowerCaseHeader.startsWith("content-type") || lowerCaseHeader.startsWith("upstash-");
@@ -677,6 +686,24 @@ function processHeaders(request) {
677
686
  if (request.label !== void 0) {
678
687
  headers.set("Upstash-Label", request.label);
679
688
  }
689
+ if (request.redact !== void 0) {
690
+ const redactParts = [];
691
+ if (request.redact.body) {
692
+ redactParts.push("body");
693
+ }
694
+ if (request.redact.header !== void 0) {
695
+ if (request.redact.header === true) {
696
+ redactParts.push("header");
697
+ } else if (Array.isArray(request.redact.header) && request.redact.header.length > 0) {
698
+ for (const headerName of request.redact.header) {
699
+ redactParts.push(`header[${headerName}]`);
700
+ }
701
+ }
702
+ }
703
+ if (redactParts.length > 0) {
704
+ headers.set("Upstash-Redact-Fields", redactParts.join(","));
705
+ }
706
+ }
680
707
  return headers;
681
708
  }
682
709
  function getRequestPath(request) {
@@ -716,6 +743,44 @@ function decodeBase64(base64) {
716
743
  }
717
744
  }
718
745
  }
746
+ function buildBulkActionFilterPayload(request) {
747
+ const cursor = "cursor" in request ? request.cursor : void 0;
748
+ if ("all" in request) {
749
+ const count2 = "count" in request ? request.count ?? DEFAULT_BULK_COUNT : DEFAULT_BULK_COUNT;
750
+ return { count: count2, cursor };
751
+ }
752
+ if ("dlqIds" in request) {
753
+ const ids = request.dlqIds;
754
+ if (Array.isArray(ids) && ids.length === 0) {
755
+ throw new QstashError(
756
+ "Empty dlqIds array provided. If you intend to target all DLQ messages, use { all: true } explicitly."
757
+ );
758
+ }
759
+ return { dlqIds: ids, cursor };
760
+ }
761
+ if ("messageIds" in request && request.messageIds) {
762
+ if (request.messageIds.length === 0) {
763
+ throw new QstashError(
764
+ "Empty messageIds array provided. If you intend to target all messages, use { all: true } explicitly."
765
+ );
766
+ }
767
+ return { messageIds: request.messageIds, cursor };
768
+ }
769
+ const count = "count" in request ? request.count ?? DEFAULT_BULK_COUNT : DEFAULT_BULK_COUNT;
770
+ return {
771
+ ...renameUrlGroup(request.filter),
772
+ count,
773
+ cursor
774
+ };
775
+ }
776
+ function renameUrlGroup(filter) {
777
+ const { urlGroup, api, ...rest } = filter;
778
+ return { ...rest, ...urlGroup === void 0 ? {} : { topicName: urlGroup } };
779
+ }
780
+ function normalizeCursor(response) {
781
+ const cursor = response.cursor;
782
+ return { ...response, cursor: cursor || void 0 };
783
+ }
719
784
  function getRuntime() {
720
785
  if (typeof process === "object" && typeof process.versions == "object" && process.versions.bun)
721
786
  return `bun@${process.versions.bun}`;
@@ -944,20 +1009,21 @@ var DLQ = class {
944
1009
  }
945
1010
  /**
946
1011
  * List messages in the dlq
1012
+ *
1013
+ * Can be called with:
1014
+ * - Filters: `listMessages({ filter: { url: "https://example.com" } })`
1015
+ * - DLQ IDs: `listMessages({ dlqIds: ["id1", "id2"] })`
1016
+ * - No filter (list all): `listMessages()`
947
1017
  */
948
- async listMessages(options) {
949
- const filterPayload = {
950
- ...options?.filter,
951
- topicName: options?.filter?.urlGroup
1018
+ async listMessages(options = {}) {
1019
+ const query = {
1020
+ count: options.count,
1021
+ ..."dlqIds" in options ? { dlqIds: options.dlqIds } : { ...renameUrlGroup(options.filter ?? {}), cursor: options.cursor }
952
1022
  };
953
1023
  const messagesPayload = await this.http.request({
954
1024
  method: "GET",
955
1025
  path: ["v2", "dlq"],
956
- query: {
957
- cursor: options?.cursor,
958
- count: options?.count,
959
- ...filterPayload
960
- }
1026
+ query
961
1027
  });
962
1028
  return {
963
1029
  messages: messagesPayload.messages.map((message) => {
@@ -971,26 +1037,86 @@ var DLQ = class {
971
1037
  };
972
1038
  }
973
1039
  /**
974
- * Remove a message from the dlq using it's `dlqId`
1040
+ * Remove messages from the dlq.
1041
+ *
1042
+ * Can be called with:
1043
+ * - A single dlqId: `delete("id")`
1044
+ * - An array of dlqIds: `delete(["id1", "id2"])`
1045
+ * - An object with dlqIds: `delete({ dlqIds: ["id1", "id2"] })`
1046
+ * - A filter object: `delete({ filter: { url: "https://example.com", label: "label" } })`
1047
+ * - All messages: `delete({ all: true })`
1048
+ *
1049
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
1050
+ * Call in a loop until cursor is undefined:
1051
+ *
1052
+ * ```ts
1053
+ * let cursor: string | undefined;
1054
+ * do {
1055
+ * const result = await dlq.delete({ all: true, count: 100, cursor });
1056
+ * cursor = result.cursor;
1057
+ * } while (cursor);
1058
+ * ```
975
1059
  */
976
- async delete(dlqMessageId) {
1060
+ async delete(request) {
1061
+ if (typeof request === "string") {
1062
+ await this.http.request({
1063
+ method: "DELETE",
1064
+ path: ["v2", "dlq", request],
1065
+ parseResponseAsJson: false
1066
+ });
1067
+ return { deleted: 1 };
1068
+ }
1069
+ if (Array.isArray(request) && request.length === 0)
1070
+ return { deleted: 0 };
1071
+ const filters = Array.isArray(request) ? { dlqIds: request } : request;
977
1072
  return await this.http.request({
978
1073
  method: "DELETE",
979
- path: ["v2", "dlq", dlqMessageId],
980
- parseResponseAsJson: false
981
- // there is no response
1074
+ path: ["v2", "dlq"],
1075
+ query: buildBulkActionFilterPayload(filters)
982
1076
  });
983
1077
  }
984
1078
  /**
985
1079
  * Remove multiple messages from the dlq using their `dlqId`s
1080
+ *
1081
+ * @deprecated Use `delete` instead
986
1082
  */
987
1083
  async deleteMany(request) {
988
- return await this.http.request({
989
- method: "DELETE",
990
- path: ["v2", "dlq"],
991
- headers: { "Content-Type": "application/json" },
992
- body: JSON.stringify({ dlqIds: request.dlqIds })
993
- });
1084
+ return await this.delete(request);
1085
+ }
1086
+ /**
1087
+ * Retry messages from the dlq.
1088
+ *
1089
+ * Can be called with:
1090
+ * - A single dlqId: `retry("id")`
1091
+ * - An array of dlqIds: `retry(["id1", "id2"])`
1092
+ * - An object with dlqIds: `retry({ dlqIds: ["id1", "id2"] })`
1093
+ * - A filter object: `retry({ filter: { url: "https://example.com", label: "label" } })`
1094
+ * - All messages: `retry({ all: true })`
1095
+ *
1096
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
1097
+ * Call in a loop until cursor is undefined:
1098
+ *
1099
+ * ```ts
1100
+ * let cursor: string | undefined;
1101
+ * do {
1102
+ * const result = await dlq.retry({ all: true, count: 100, cursor });
1103
+ * cursor = result.cursor;
1104
+ * } while (cursor);
1105
+ * ```
1106
+ */
1107
+ async retry(request) {
1108
+ if (typeof request === "string")
1109
+ request = [request];
1110
+ if (Array.isArray(request) && request.length === 0)
1111
+ return { responses: [] };
1112
+ const filters = Array.isArray(request) ? { dlqIds: request } : request;
1113
+ return normalizeCursor(
1114
+ await this.http.request({
1115
+ method: "POST",
1116
+ path: ["v2", "dlq", "retry"],
1117
+ query: buildBulkActionFilterPayload(filters)
1118
+ })
1119
+ );
994
1120
  }
995
1121
  };
996
1122
 
@@ -1192,7 +1318,18 @@ var HttpClient = class {
1192
1318
  const url = new URL([request.baseUrl ?? this.baseUrl, ...request.path].join("/"));
1193
1319
  if (request.query) {
1194
1320
  for (const [key, value] of Object.entries(request.query)) {
1195
- if (value !== void 0) {
1321
+ if (value === void 0)
1322
+ continue;
1323
+ if (Array.isArray(value)) {
1324
+ if (value.length === 0) {
1325
+ throw new QstashEmptyArrayError(key);
1326
+ }
1327
+ for (const item of value) {
1328
+ url.searchParams.append(key, item);
1329
+ }
1330
+ } else if (value instanceof Date) {
1331
+ url.searchParams.set(key, value.getTime().toString());
1332
+ } else {
1196
1333
  url.searchParams.set(key, value.toString());
1197
1334
  }
1198
1335
  }
@@ -1423,29 +1560,68 @@ var Messages = class {
1423
1560
  return message;
1424
1561
  }
1425
1562
  /**
1426
- * Cancel a message
1563
+ * Cancel messages.
1564
+ *
1565
+ * Can be called with:
1566
+ * - A single messageId: `cancel("id")`
1567
+ * - An array of messageIds: `cancel(["id1", "id2"])`
1568
+ * - A filter object: `cancel({ filter: { flowControlKey: "key", label: "label" } })`
1569
+ * - All messages: `cancel({ all: true })`
1570
+ *
1571
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
1572
+ * Call in a loop until `cancelled` is 0:
1573
+ *
1574
+ * ```ts
1575
+ * let cancelled: number;
1576
+ * do {
1577
+ * const result = await messages.cancel({ all: true, count: 100 });
1578
+ * cancelled = result.cancelled;
1579
+ * } while (cancelled > 0);
1580
+ * ```
1427
1581
  */
1428
- async delete(messageId) {
1582
+ async cancel(request) {
1583
+ if (typeof request === "string") {
1584
+ return await this.http.request({
1585
+ method: "DELETE",
1586
+ path: ["v2", "messages", request]
1587
+ });
1588
+ }
1589
+ if (Array.isArray(request) && request.length === 0)
1590
+ return { cancelled: 0 };
1591
+ const filters = Array.isArray(request) ? { messageIds: request } : request;
1429
1592
  return await this.http.request({
1593
+ method: "DELETE",
1594
+ path: ["v2", "messages"],
1595
+ query: buildBulkActionFilterPayload(filters)
1596
+ });
1597
+ }
1598
+ /**
1599
+ * Delete a message.
1600
+ *
1601
+ * @deprecated Use `cancel(messageId: string)` instead
1602
+ */
1603
+ async delete(messageId) {
1604
+ await this.http.request({
1430
1605
  method: "DELETE",
1431
1606
  path: ["v2", "messages", messageId],
1432
1607
  parseResponseAsJson: false
1433
1608
  });
1434
1609
  }
1610
+ /**
1611
+ * Cancel multiple messages by their messageIds.
1612
+ *
1613
+ * @deprecated Use `cancel(messageIds: string[])` instead
1614
+ */
1435
1615
  async deleteMany(messageIds) {
1436
- const result = await this.http.request({
1437
- method: "DELETE",
1438
- path: ["v2", "messages"],
1439
- headers: { "Content-Type": "application/json" },
1440
- body: JSON.stringify({ messageIds })
1441
- });
1616
+ const result = await this.cancel(messageIds);
1442
1617
  return result.cancelled;
1443
1618
  }
1619
+ /**
1620
+ * Cancel all messages
1621
+ * @deprecated Use `cancel({all: true})` to cancel all
1622
+ */
1444
1623
  async deleteAll() {
1445
- const result = await this.http.request({
1446
- method: "DELETE",
1447
- path: ["v2", "messages"]
1448
- });
1624
+ const result = await this.cancel({ all: true });
1449
1625
  return result.cancelled;
1450
1626
  }
1451
1627
  };
@@ -1650,6 +1826,24 @@ var Schedules = class {
1650
1826
  if (request.label !== void 0) {
1651
1827
  headers.set("Upstash-Label", request.label);
1652
1828
  }
1829
+ if (request.redact !== void 0) {
1830
+ const redactParts = [];
1831
+ if (request.redact.body) {
1832
+ redactParts.push("body");
1833
+ }
1834
+ if (request.redact.header !== void 0) {
1835
+ if (request.redact.header === true) {
1836
+ redactParts.push("header");
1837
+ } else if (Array.isArray(request.redact.header) && request.redact.header.length > 0) {
1838
+ for (const headerName of request.redact.header) {
1839
+ redactParts.push(`header[${headerName}]`);
1840
+ }
1841
+ }
1842
+ }
1843
+ if (redactParts.length > 0) {
1844
+ headers.set("Upstash-Redact-Fields", redactParts.join(","));
1845
+ }
1846
+ }
1653
1847
  return await this.http.request({
1654
1848
  method: "POST",
1655
1849
  headers: wrapWithGlobalHeaders(headers, this.http.headers, this.http.telemetryHeaders),
@@ -3165,7 +3359,7 @@ var Workflow = class {
3165
3359
  };
3166
3360
 
3167
3361
  // version.ts
3168
- var VERSION = "v2.9.1";
3362
+ var VERSION = "2.10.0";
3169
3363
 
3170
3364
  // src/client/client.ts
3171
3365
  var Client = class {
@@ -3368,39 +3562,18 @@ var Client = class {
3368
3562
  * }
3369
3563
  * ```
3370
3564
  */
3371
- async logs(request) {
3372
- const query = {};
3373
- if (typeof request?.cursor === "number" && request.cursor > 0) {
3374
- query.cursor = request.cursor.toString();
3375
- } else if (typeof request?.cursor === "string" && request.cursor !== "") {
3376
- query.cursor = request.cursor;
3377
- }
3378
- for (const [key, value] of Object.entries(request?.filter ?? {})) {
3379
- if (typeof value === "number" && value < 0) {
3380
- continue;
3381
- }
3382
- if (key === "urlGroup") {
3383
- query.topicName = value.toString();
3384
- } else if (typeof value !== "undefined") {
3385
- query[key] = value.toString();
3386
- }
3387
- }
3565
+ async logs(request = {}) {
3566
+ const query = {
3567
+ count: request.count,
3568
+ ..."messageIds" in request ? { messageIds: request.messageIds } : { ...renameUrlGroup(request.filter ?? {}), cursor: request.cursor }
3569
+ };
3388
3570
  const responsePayload = await this.http.request({
3389
3571
  path: ["v2", "events"],
3390
3572
  method: "GET",
3391
3573
  query
3392
3574
  });
3393
- const logs = responsePayload.events.map((event) => {
3394
- return {
3395
- ...event,
3396
- urlGroup: event.topicName
3397
- };
3398
- });
3399
- return {
3400
- cursor: responsePayload.cursor,
3401
- logs,
3402
- events: logs
3403
- };
3575
+ const logs = responsePayload.events.map((event) => ({ ...event, urlGroup: event.topicName }));
3576
+ return { cursor: responsePayload.cursor, logs, events: logs };
3404
3577
  }
3405
3578
  /**
3406
3579
  * @deprecated Will be removed in the next major release. Use the `logs` method instead.
package/h3.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  serve,
3
3
  verifySignatureH3
4
- } from "./chunk-JO26IBSH.mjs";
5
- import "./chunk-KDHOB7B5.mjs";
6
- import "./chunk-DT2X63FB.mjs";
4
+ } from "./chunk-FE7GQ4RU.mjs";
5
+ import "./chunk-FAMFGAMR.mjs";
6
+ import "./chunk-X3MMU3BQ.mjs";
7
7
  export {
8
8
  serve,
9
9
  verifySignatureH3
package/hono.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Context } from 'hono';
2
- import { ae as RouteFunction, af as WorkflowServeOptions } from './client-jh_SomWB.mjs';
2
+ import { ae as RouteFunction, af as WorkflowServeOptions } from './client-CsM1dTnz.mjs';
3
3
  import 'neverthrow';
4
4
 
5
5
  type WorkflowBindings = {
package/hono.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Context } from 'hono';
2
- import { ae as RouteFunction, af as WorkflowServeOptions } from './client-jh_SomWB.js';
2
+ import { ae as RouteFunction, af as WorkflowServeOptions } from './client-CsM1dTnz.js';
3
3
  import 'neverthrow';
4
4
 
5
5
  type WorkflowBindings = {