@upstash/qstash 2.9.1 → 2.10.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.
@@ -218,6 +218,14 @@ var QstashDailyRatelimitError = class extends QstashError {
218
218
  this.reset = args.reset;
219
219
  }
220
220
  };
221
+ var QstashEmptyArrayError = class extends QstashError {
222
+ constructor(parameterName) {
223
+ super(
224
+ `Empty array provided for query parameter "${parameterName}". This would result in no filter being applied, which could affect all resources.`
225
+ );
226
+ this.name = "QstashEmptyArrayError";
227
+ }
228
+ };
221
229
  var QStashWorkflowError = class extends QstashError {
222
230
  constructor(message) {
223
231
  super(message);
@@ -247,6 +255,7 @@ var formatWorkflowError = (error) => {
247
255
  };
248
256
 
249
257
  // src/client/utils.ts
258
+ var DEFAULT_BULK_COUNT = 100;
250
259
  var isIgnoredHeader = (header) => {
251
260
  const lowerCaseHeader = header.toLowerCase();
252
261
  return lowerCaseHeader.startsWith("content-type") || lowerCaseHeader.startsWith("upstash-");
@@ -333,6 +342,24 @@ function processHeaders(request) {
333
342
  if (request.label !== void 0) {
334
343
  headers.set("Upstash-Label", request.label);
335
344
  }
345
+ if (request.redact !== void 0) {
346
+ const redactParts = [];
347
+ if (request.redact.body) {
348
+ redactParts.push("body");
349
+ }
350
+ if (request.redact.header !== void 0) {
351
+ if (request.redact.header === true) {
352
+ redactParts.push("header");
353
+ } else if (Array.isArray(request.redact.header) && request.redact.header.length > 0) {
354
+ for (const headerName of request.redact.header) {
355
+ redactParts.push(`header[${headerName}]`);
356
+ }
357
+ }
358
+ }
359
+ if (redactParts.length > 0) {
360
+ headers.set("Upstash-Redact-Fields", redactParts.join(","));
361
+ }
362
+ }
336
363
  return headers;
337
364
  }
338
365
  function getRequestPath(request) {
@@ -372,6 +399,44 @@ function decodeBase64(base64) {
372
399
  }
373
400
  }
374
401
  }
402
+ function buildBulkActionFilterPayload(request) {
403
+ const cursor = "cursor" in request ? request.cursor : void 0;
404
+ if ("all" in request) {
405
+ const count2 = "count" in request ? request.count ?? DEFAULT_BULK_COUNT : DEFAULT_BULK_COUNT;
406
+ return { count: count2, cursor };
407
+ }
408
+ if ("dlqIds" in request) {
409
+ const ids = request.dlqIds;
410
+ if (Array.isArray(ids) && ids.length === 0) {
411
+ throw new QstashError(
412
+ "Empty dlqIds array provided. If you intend to target all DLQ messages, use { all: true } explicitly."
413
+ );
414
+ }
415
+ return { dlqIds: ids, cursor };
416
+ }
417
+ if ("messageIds" in request && request.messageIds) {
418
+ if (request.messageIds.length === 0) {
419
+ throw new QstashError(
420
+ "Empty messageIds array provided. If you intend to target all messages, use { all: true } explicitly."
421
+ );
422
+ }
423
+ return { messageIds: request.messageIds, cursor };
424
+ }
425
+ const count = "count" in request ? request.count ?? DEFAULT_BULK_COUNT : DEFAULT_BULK_COUNT;
426
+ return {
427
+ ...renameUrlGroup(request.filter),
428
+ count,
429
+ cursor
430
+ };
431
+ }
432
+ function renameUrlGroup(filter) {
433
+ const { urlGroup, api, ...rest } = filter;
434
+ return { ...rest, ...urlGroup === void 0 ? {} : { topicName: urlGroup } };
435
+ }
436
+ function normalizeCursor(response) {
437
+ const cursor = response.cursor;
438
+ return { ...response, cursor: cursor || void 0 };
439
+ }
375
440
  function getRuntime() {
376
441
  if (typeof process === "object" && typeof process.versions == "object" && process.versions.bun)
377
442
  return `bun@${process.versions.bun}`;
@@ -600,20 +665,21 @@ var DLQ = class {
600
665
  }
601
666
  /**
602
667
  * List messages in the dlq
668
+ *
669
+ * Can be called with:
670
+ * - Filters: `listMessages({ filter: { url: "https://example.com" } })`
671
+ * - DLQ IDs: `listMessages({ dlqIds: ["id1", "id2"] })`
672
+ * - No filter (list all): `listMessages()`
603
673
  */
604
- async listMessages(options) {
605
- const filterPayload = {
606
- ...options?.filter,
607
- topicName: options?.filter?.urlGroup
674
+ async listMessages(options = {}) {
675
+ const query = {
676
+ count: options.count,
677
+ ..."dlqIds" in options ? { dlqIds: options.dlqIds } : { ...renameUrlGroup(options.filter ?? {}), cursor: options.cursor }
608
678
  };
609
679
  const messagesPayload = await this.http.request({
610
680
  method: "GET",
611
681
  path: ["v2", "dlq"],
612
- query: {
613
- cursor: options?.cursor,
614
- count: options?.count,
615
- ...filterPayload
616
- }
682
+ query
617
683
  });
618
684
  return {
619
685
  messages: messagesPayload.messages.map((message) => {
@@ -627,26 +693,86 @@ var DLQ = class {
627
693
  };
628
694
  }
629
695
  /**
630
- * Remove a message from the dlq using it's `dlqId`
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({ filter: { url: "https://example.com", label: "label" } })`
703
+ * - All messages: `delete({ all: true })`
704
+ *
705
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
706
+ * Call in a loop until cursor is undefined:
707
+ *
708
+ * ```ts
709
+ * let cursor: string | undefined;
710
+ * do {
711
+ * const result = await dlq.delete({ all: true, count: 100, cursor });
712
+ * cursor = result.cursor;
713
+ * } while (cursor);
714
+ * ```
631
715
  */
632
- async delete(dlqMessageId) {
716
+ async delete(request) {
717
+ if (typeof request === "string") {
718
+ await this.http.request({
719
+ method: "DELETE",
720
+ path: ["v2", "dlq", request],
721
+ parseResponseAsJson: false
722
+ });
723
+ return { deleted: 1 };
724
+ }
725
+ if (Array.isArray(request) && request.length === 0)
726
+ return { deleted: 0 };
727
+ const filters = Array.isArray(request) ? { dlqIds: request } : request;
633
728
  return await this.http.request({
634
729
  method: "DELETE",
635
- path: ["v2", "dlq", dlqMessageId],
636
- parseResponseAsJson: false
637
- // there is no response
730
+ path: ["v2", "dlq"],
731
+ query: buildBulkActionFilterPayload(filters)
638
732
  });
639
733
  }
640
734
  /**
641
735
  * Remove multiple messages from the dlq using their `dlqId`s
736
+ *
737
+ * @deprecated Use `delete` instead
642
738
  */
643
739
  async deleteMany(request) {
644
- return await this.http.request({
645
- method: "DELETE",
646
- path: ["v2", "dlq"],
647
- headers: { "Content-Type": "application/json" },
648
- body: JSON.stringify({ dlqIds: request.dlqIds })
649
- });
740
+ return await this.delete(request);
741
+ }
742
+ /**
743
+ * Retry messages from the dlq.
744
+ *
745
+ * Can be called with:
746
+ * - A single dlqId: `retry("id")`
747
+ * - An array of dlqIds: `retry(["id1", "id2"])`
748
+ * - An object with dlqIds: `retry({ dlqIds: ["id1", "id2"] })`
749
+ * - A filter object: `retry({ filter: { url: "https://example.com", label: "label" } })`
750
+ * - All messages: `retry({ all: true })`
751
+ *
752
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
753
+ * Call in a loop until cursor is undefined:
754
+ *
755
+ * ```ts
756
+ * let cursor: string | undefined;
757
+ * do {
758
+ * const result = await dlq.retry({ all: true, count: 100, cursor });
759
+ * cursor = result.cursor;
760
+ * } while (cursor);
761
+ * ```
762
+ */
763
+ async retry(request) {
764
+ if (typeof request === "string")
765
+ request = [request];
766
+ if (Array.isArray(request) && request.length === 0)
767
+ return { responses: [] };
768
+ const filters = Array.isArray(request) ? { dlqIds: request } : request;
769
+ return normalizeCursor(
770
+ await this.http.request({
771
+ method: "POST",
772
+ path: ["v2", "dlq", "retry"],
773
+ query: buildBulkActionFilterPayload(filters)
774
+ })
775
+ );
650
776
  }
651
777
  };
652
778
 
@@ -848,7 +974,18 @@ var HttpClient = class {
848
974
  const url = new URL([request.baseUrl ?? this.baseUrl, ...request.path].join("/"));
849
975
  if (request.query) {
850
976
  for (const [key, value] of Object.entries(request.query)) {
851
- if (value !== void 0) {
977
+ if (value === void 0)
978
+ continue;
979
+ if (Array.isArray(value)) {
980
+ if (value.length === 0) {
981
+ throw new QstashEmptyArrayError(key);
982
+ }
983
+ for (const item of value) {
984
+ url.searchParams.append(key, item);
985
+ }
986
+ } else if (value instanceof Date) {
987
+ url.searchParams.set(key, value.getTime().toString());
988
+ } else {
852
989
  url.searchParams.set(key, value.toString());
853
990
  }
854
991
  }
@@ -1079,29 +1216,68 @@ var Messages = class {
1079
1216
  return message;
1080
1217
  }
1081
1218
  /**
1082
- * Cancel a message
1219
+ * Cancel messages.
1220
+ *
1221
+ * Can be called with:
1222
+ * - A single messageId: `cancel("id")`
1223
+ * - An array of messageIds: `cancel(["id1", "id2"])`
1224
+ * - A filter object: `cancel({ filter: { flowControlKey: "key", label: "label" } })`
1225
+ * - All messages: `cancel({ all: true })`
1226
+ *
1227
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
1228
+ * Call in a loop until `cancelled` is 0:
1229
+ *
1230
+ * ```ts
1231
+ * let cancelled: number;
1232
+ * do {
1233
+ * const result = await messages.cancel({ all: true, count: 100 });
1234
+ * cancelled = result.cancelled;
1235
+ * } while (cancelled > 0);
1236
+ * ```
1083
1237
  */
1084
- async delete(messageId) {
1238
+ async cancel(request) {
1239
+ if (typeof request === "string") {
1240
+ return await this.http.request({
1241
+ method: "DELETE",
1242
+ path: ["v2", "messages", request]
1243
+ });
1244
+ }
1245
+ if (Array.isArray(request) && request.length === 0)
1246
+ return { cancelled: 0 };
1247
+ const filters = Array.isArray(request) ? { messageIds: request } : request;
1085
1248
  return await this.http.request({
1249
+ method: "DELETE",
1250
+ path: ["v2", "messages"],
1251
+ query: buildBulkActionFilterPayload(filters)
1252
+ });
1253
+ }
1254
+ /**
1255
+ * Delete a message.
1256
+ *
1257
+ * @deprecated Use `cancel(messageId: string)` instead
1258
+ */
1259
+ async delete(messageId) {
1260
+ await this.http.request({
1086
1261
  method: "DELETE",
1087
1262
  path: ["v2", "messages", messageId],
1088
1263
  parseResponseAsJson: false
1089
1264
  });
1090
1265
  }
1266
+ /**
1267
+ * Cancel multiple messages by their messageIds.
1268
+ *
1269
+ * @deprecated Use `cancel(messageIds: string[])` instead
1270
+ */
1091
1271
  async deleteMany(messageIds) {
1092
- const result = await this.http.request({
1093
- method: "DELETE",
1094
- path: ["v2", "messages"],
1095
- headers: { "Content-Type": "application/json" },
1096
- body: JSON.stringify({ messageIds })
1097
- });
1272
+ const result = await this.cancel(messageIds);
1098
1273
  return result.cancelled;
1099
1274
  }
1275
+ /**
1276
+ * Cancel all messages
1277
+ * @deprecated Use `cancel({all: true})` to cancel all
1278
+ */
1100
1279
  async deleteAll() {
1101
- const result = await this.http.request({
1102
- method: "DELETE",
1103
- path: ["v2", "messages"]
1104
- });
1280
+ const result = await this.cancel({ all: true });
1105
1281
  return result.cancelled;
1106
1282
  }
1107
1283
  };
@@ -1306,6 +1482,24 @@ var Schedules = class {
1306
1482
  if (request.label !== void 0) {
1307
1483
  headers.set("Upstash-Label", request.label);
1308
1484
  }
1485
+ if (request.redact !== void 0) {
1486
+ const redactParts = [];
1487
+ if (request.redact.body) {
1488
+ redactParts.push("body");
1489
+ }
1490
+ if (request.redact.header !== void 0) {
1491
+ if (request.redact.header === true) {
1492
+ redactParts.push("header");
1493
+ } else if (Array.isArray(request.redact.header) && request.redact.header.length > 0) {
1494
+ for (const headerName of request.redact.header) {
1495
+ redactParts.push(`header[${headerName}]`);
1496
+ }
1497
+ }
1498
+ }
1499
+ if (redactParts.length > 0) {
1500
+ headers.set("Upstash-Redact-Fields", redactParts.join(","));
1501
+ }
1502
+ }
1309
1503
  return await this.http.request({
1310
1504
  method: "POST",
1311
1505
  headers: wrapWithGlobalHeaders(headers, this.http.headers, this.http.telemetryHeaders),
@@ -1435,7 +1629,7 @@ var UrlGroups = class {
1435
1629
  };
1436
1630
 
1437
1631
  // version.ts
1438
- var VERSION = "v2.9.1";
1632
+ var VERSION = "2.10.1";
1439
1633
 
1440
1634
  // src/client/client.ts
1441
1635
  var Client = class {
@@ -1638,39 +1832,18 @@ var Client = class {
1638
1832
  * }
1639
1833
  * ```
1640
1834
  */
1641
- async logs(request) {
1642
- const query = {};
1643
- if (typeof request?.cursor === "number" && request.cursor > 0) {
1644
- query.cursor = request.cursor.toString();
1645
- } else if (typeof request?.cursor === "string" && request.cursor !== "") {
1646
- query.cursor = request.cursor;
1647
- }
1648
- for (const [key, value] of Object.entries(request?.filter ?? {})) {
1649
- if (typeof value === "number" && value < 0) {
1650
- continue;
1651
- }
1652
- if (key === "urlGroup") {
1653
- query.topicName = value.toString();
1654
- } else if (typeof value !== "undefined") {
1655
- query[key] = value.toString();
1656
- }
1657
- }
1835
+ async logs(request = {}) {
1836
+ const query = {
1837
+ count: request.count,
1838
+ ..."messageIds" in request ? { messageIds: request.messageIds } : { ...renameUrlGroup(request.filter ?? {}), cursor: request.cursor }
1839
+ };
1658
1840
  const responsePayload = await this.http.request({
1659
1841
  path: ["v2", "events"],
1660
1842
  method: "GET",
1661
1843
  query
1662
1844
  });
1663
- const logs = responsePayload.events.map((event) => {
1664
- return {
1665
- ...event,
1666
- urlGroup: event.topicName
1667
- };
1668
- });
1669
- return {
1670
- cursor: responsePayload.cursor,
1671
- logs,
1672
- events: logs
1673
- };
1845
+ const logs = responsePayload.events.map((event) => ({ ...event, urlGroup: event.topicName }));
1846
+ return { cursor: responsePayload.cursor, logs, events: logs };
1674
1847
  }
1675
1848
  /**
1676
1849
  * @deprecated Will be removed in the next major release. Use the `logs` method instead.
@@ -3094,6 +3267,7 @@ export {
3094
3267
  QstashRatelimitError,
3095
3268
  QstashChatRatelimitError,
3096
3269
  QstashDailyRatelimitError,
3270
+ QstashEmptyArrayError,
3097
3271
  QStashWorkflowError,
3098
3272
  QStashWorkflowAbort,
3099
3273
  formatWorkflowError,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Receiver,
3
3
  serve
4
- } from "./chunk-DT2X63FB.mjs";
4
+ } from "./chunk-35B33QW3.mjs";
5
5
 
6
6
  // node_modules/defu/dist/defu.mjs
7
7
  function isPlainObject(value) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BaseProvider
3
- } from "./chunk-DT2X63FB.mjs";
3
+ } from "./chunk-35B33QW3.mjs";
4
4
 
5
5
  // src/client/api/email.ts
6
6
  var EmailProvider = class extends BaseProvider {