@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.
package/cloudflare.js CHANGED
@@ -238,6 +238,14 @@ var QstashDailyRatelimitError = class extends QstashError {
238
238
  this.reset = args.reset;
239
239
  }
240
240
  };
241
+ var QstashEmptyArrayError = class extends QstashError {
242
+ constructor(parameterName) {
243
+ super(
244
+ `Empty array provided for query parameter "${parameterName}". This would result in no filter being applied, which could affect all resources.`
245
+ );
246
+ this.name = "QstashEmptyArrayError";
247
+ }
248
+ };
241
249
  var QStashWorkflowError = class extends QstashError {
242
250
  constructor(message) {
243
251
  super(message);
@@ -267,6 +275,7 @@ var formatWorkflowError = (error) => {
267
275
  };
268
276
 
269
277
  // src/client/utils.ts
278
+ var DEFAULT_BULK_COUNT = 100;
270
279
  var isIgnoredHeader = (header) => {
271
280
  const lowerCaseHeader = header.toLowerCase();
272
281
  return lowerCaseHeader.startsWith("content-type") || lowerCaseHeader.startsWith("upstash-");
@@ -353,6 +362,24 @@ function processHeaders(request) {
353
362
  if (request.label !== void 0) {
354
363
  headers.set("Upstash-Label", request.label);
355
364
  }
365
+ if (request.redact !== void 0) {
366
+ const redactParts = [];
367
+ if (request.redact.body) {
368
+ redactParts.push("body");
369
+ }
370
+ if (request.redact.header !== void 0) {
371
+ if (request.redact.header === true) {
372
+ redactParts.push("header");
373
+ } else if (Array.isArray(request.redact.header) && request.redact.header.length > 0) {
374
+ for (const headerName of request.redact.header) {
375
+ redactParts.push(`header[${headerName}]`);
376
+ }
377
+ }
378
+ }
379
+ if (redactParts.length > 0) {
380
+ headers.set("Upstash-Redact-Fields", redactParts.join(","));
381
+ }
382
+ }
356
383
  return headers;
357
384
  }
358
385
  function getRequestPath(request) {
@@ -392,6 +419,44 @@ function decodeBase64(base64) {
392
419
  }
393
420
  }
394
421
  }
422
+ function buildBulkActionFilterPayload(request) {
423
+ const cursor = "cursor" in request ? request.cursor : void 0;
424
+ if ("all" in request) {
425
+ const count2 = "count" in request ? request.count ?? DEFAULT_BULK_COUNT : DEFAULT_BULK_COUNT;
426
+ return { count: count2, cursor };
427
+ }
428
+ if ("dlqIds" in request) {
429
+ const ids = request.dlqIds;
430
+ if (Array.isArray(ids) && ids.length === 0) {
431
+ throw new QstashError(
432
+ "Empty dlqIds array provided. If you intend to target all DLQ messages, use { all: true } explicitly."
433
+ );
434
+ }
435
+ return { dlqIds: ids, cursor };
436
+ }
437
+ if ("messageIds" in request && request.messageIds) {
438
+ if (request.messageIds.length === 0) {
439
+ throw new QstashError(
440
+ "Empty messageIds array provided. If you intend to target all messages, use { all: true } explicitly."
441
+ );
442
+ }
443
+ return { messageIds: request.messageIds, cursor };
444
+ }
445
+ const count = "count" in request ? request.count ?? DEFAULT_BULK_COUNT : DEFAULT_BULK_COUNT;
446
+ return {
447
+ ...renameUrlGroup(request.filter),
448
+ count,
449
+ cursor
450
+ };
451
+ }
452
+ function renameUrlGroup(filter) {
453
+ const { urlGroup, api, ...rest } = filter;
454
+ return { ...rest, ...urlGroup === void 0 ? {} : { topicName: urlGroup } };
455
+ }
456
+ function normalizeCursor(response) {
457
+ const cursor = response.cursor;
458
+ return { ...response, cursor: cursor || void 0 };
459
+ }
395
460
  function getRuntime() {
396
461
  if (typeof process === "object" && typeof process.versions == "object" && process.versions.bun)
397
462
  return `bun@${process.versions.bun}`;
@@ -620,20 +685,21 @@ var DLQ = class {
620
685
  }
621
686
  /**
622
687
  * List messages in the dlq
688
+ *
689
+ * Can be called with:
690
+ * - Filters: `listMessages({ filter: { url: "https://example.com" } })`
691
+ * - DLQ IDs: `listMessages({ dlqIds: ["id1", "id2"] })`
692
+ * - No filter (list all): `listMessages()`
623
693
  */
624
- async listMessages(options) {
625
- const filterPayload = {
626
- ...options?.filter,
627
- topicName: options?.filter?.urlGroup
694
+ async listMessages(options = {}) {
695
+ const query = {
696
+ count: options.count,
697
+ ..."dlqIds" in options ? { dlqIds: options.dlqIds } : { ...renameUrlGroup(options.filter ?? {}), cursor: options.cursor }
628
698
  };
629
699
  const messagesPayload = await this.http.request({
630
700
  method: "GET",
631
701
  path: ["v2", "dlq"],
632
- query: {
633
- cursor: options?.cursor,
634
- count: options?.count,
635
- ...filterPayload
636
- }
702
+ query
637
703
  });
638
704
  return {
639
705
  messages: messagesPayload.messages.map((message) => {
@@ -647,26 +713,86 @@ var DLQ = class {
647
713
  };
648
714
  }
649
715
  /**
650
- * Remove a message from the dlq using it's `dlqId`
716
+ * Remove messages from the dlq.
717
+ *
718
+ * Can be called with:
719
+ * - A single dlqId: `delete("id")`
720
+ * - An array of dlqIds: `delete(["id1", "id2"])`
721
+ * - An object with dlqIds: `delete({ dlqIds: ["id1", "id2"] })`
722
+ * - A filter object: `delete({ filter: { url: "https://example.com", label: "label" } })`
723
+ * - All messages: `delete({ all: true })`
724
+ *
725
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
726
+ * Call in a loop until cursor is undefined:
727
+ *
728
+ * ```ts
729
+ * let cursor: string | undefined;
730
+ * do {
731
+ * const result = await dlq.delete({ all: true, count: 100, cursor });
732
+ * cursor = result.cursor;
733
+ * } while (cursor);
734
+ * ```
651
735
  */
652
- async delete(dlqMessageId) {
736
+ async delete(request) {
737
+ if (typeof request === "string") {
738
+ await this.http.request({
739
+ method: "DELETE",
740
+ path: ["v2", "dlq", request],
741
+ parseResponseAsJson: false
742
+ });
743
+ return { deleted: 1 };
744
+ }
745
+ if (Array.isArray(request) && request.length === 0)
746
+ return { deleted: 0 };
747
+ const filters = Array.isArray(request) ? { dlqIds: request } : request;
653
748
  return await this.http.request({
654
749
  method: "DELETE",
655
- path: ["v2", "dlq", dlqMessageId],
656
- parseResponseAsJson: false
657
- // there is no response
750
+ path: ["v2", "dlq"],
751
+ query: buildBulkActionFilterPayload(filters)
658
752
  });
659
753
  }
660
754
  /**
661
755
  * Remove multiple messages from the dlq using their `dlqId`s
756
+ *
757
+ * @deprecated Use `delete` instead
662
758
  */
663
759
  async deleteMany(request) {
664
- return await this.http.request({
665
- method: "DELETE",
666
- path: ["v2", "dlq"],
667
- headers: { "Content-Type": "application/json" },
668
- body: JSON.stringify({ dlqIds: request.dlqIds })
669
- });
760
+ return await this.delete(request);
761
+ }
762
+ /**
763
+ * Retry messages from the dlq.
764
+ *
765
+ * Can be called with:
766
+ * - A single dlqId: `retry("id")`
767
+ * - An array of dlqIds: `retry(["id1", "id2"])`
768
+ * - An object with dlqIds: `retry({ dlqIds: ["id1", "id2"] })`
769
+ * - A filter object: `retry({ filter: { url: "https://example.com", label: "label" } })`
770
+ * - All messages: `retry({ all: true })`
771
+ *
772
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
773
+ * Call in a loop until cursor is undefined:
774
+ *
775
+ * ```ts
776
+ * let cursor: string | undefined;
777
+ * do {
778
+ * const result = await dlq.retry({ all: true, count: 100, cursor });
779
+ * cursor = result.cursor;
780
+ * } while (cursor);
781
+ * ```
782
+ */
783
+ async retry(request) {
784
+ if (typeof request === "string")
785
+ request = [request];
786
+ if (Array.isArray(request) && request.length === 0)
787
+ return { responses: [] };
788
+ const filters = Array.isArray(request) ? { dlqIds: request } : request;
789
+ return normalizeCursor(
790
+ await this.http.request({
791
+ method: "POST",
792
+ path: ["v2", "dlq", "retry"],
793
+ query: buildBulkActionFilterPayload(filters)
794
+ })
795
+ );
670
796
  }
671
797
  };
672
798
 
@@ -868,7 +994,18 @@ var HttpClient = class {
868
994
  const url = new URL([request.baseUrl ?? this.baseUrl, ...request.path].join("/"));
869
995
  if (request.query) {
870
996
  for (const [key, value] of Object.entries(request.query)) {
871
- if (value !== void 0) {
997
+ if (value === void 0)
998
+ continue;
999
+ if (Array.isArray(value)) {
1000
+ if (value.length === 0) {
1001
+ throw new QstashEmptyArrayError(key);
1002
+ }
1003
+ for (const item of value) {
1004
+ url.searchParams.append(key, item);
1005
+ }
1006
+ } else if (value instanceof Date) {
1007
+ url.searchParams.set(key, value.getTime().toString());
1008
+ } else {
872
1009
  url.searchParams.set(key, value.toString());
873
1010
  }
874
1011
  }
@@ -1099,29 +1236,68 @@ var Messages = class {
1099
1236
  return message;
1100
1237
  }
1101
1238
  /**
1102
- * Cancel a message
1239
+ * Cancel messages.
1240
+ *
1241
+ * Can be called with:
1242
+ * - A single messageId: `cancel("id")`
1243
+ * - An array of messageIds: `cancel(["id1", "id2"])`
1244
+ * - A filter object: `cancel({ filter: { flowControlKey: "key", label: "label" } })`
1245
+ * - All messages: `cancel({ all: true })`
1246
+ *
1247
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
1248
+ * Call in a loop until `cancelled` is 0:
1249
+ *
1250
+ * ```ts
1251
+ * let cancelled: number;
1252
+ * do {
1253
+ * const result = await messages.cancel({ all: true, count: 100 });
1254
+ * cancelled = result.cancelled;
1255
+ * } while (cancelled > 0);
1256
+ * ```
1103
1257
  */
1104
- async delete(messageId) {
1258
+ async cancel(request) {
1259
+ if (typeof request === "string") {
1260
+ return await this.http.request({
1261
+ method: "DELETE",
1262
+ path: ["v2", "messages", request]
1263
+ });
1264
+ }
1265
+ if (Array.isArray(request) && request.length === 0)
1266
+ return { cancelled: 0 };
1267
+ const filters = Array.isArray(request) ? { messageIds: request } : request;
1105
1268
  return await this.http.request({
1269
+ method: "DELETE",
1270
+ path: ["v2", "messages"],
1271
+ query: buildBulkActionFilterPayload(filters)
1272
+ });
1273
+ }
1274
+ /**
1275
+ * Delete a message.
1276
+ *
1277
+ * @deprecated Use `cancel(messageId: string)` instead
1278
+ */
1279
+ async delete(messageId) {
1280
+ await this.http.request({
1106
1281
  method: "DELETE",
1107
1282
  path: ["v2", "messages", messageId],
1108
1283
  parseResponseAsJson: false
1109
1284
  });
1110
1285
  }
1286
+ /**
1287
+ * Cancel multiple messages by their messageIds.
1288
+ *
1289
+ * @deprecated Use `cancel(messageIds: string[])` instead
1290
+ */
1111
1291
  async deleteMany(messageIds) {
1112
- const result = await this.http.request({
1113
- method: "DELETE",
1114
- path: ["v2", "messages"],
1115
- headers: { "Content-Type": "application/json" },
1116
- body: JSON.stringify({ messageIds })
1117
- });
1292
+ const result = await this.cancel(messageIds);
1118
1293
  return result.cancelled;
1119
1294
  }
1295
+ /**
1296
+ * Cancel all messages
1297
+ * @deprecated Use `cancel({all: true})` to cancel all
1298
+ */
1120
1299
  async deleteAll() {
1121
- const result = await this.http.request({
1122
- method: "DELETE",
1123
- path: ["v2", "messages"]
1124
- });
1300
+ const result = await this.cancel({ all: true });
1125
1301
  return result.cancelled;
1126
1302
  }
1127
1303
  };
@@ -1326,6 +1502,24 @@ var Schedules = class {
1326
1502
  if (request.label !== void 0) {
1327
1503
  headers.set("Upstash-Label", request.label);
1328
1504
  }
1505
+ if (request.redact !== void 0) {
1506
+ const redactParts = [];
1507
+ if (request.redact.body) {
1508
+ redactParts.push("body");
1509
+ }
1510
+ if (request.redact.header !== void 0) {
1511
+ if (request.redact.header === true) {
1512
+ redactParts.push("header");
1513
+ } else if (Array.isArray(request.redact.header) && request.redact.header.length > 0) {
1514
+ for (const headerName of request.redact.header) {
1515
+ redactParts.push(`header[${headerName}]`);
1516
+ }
1517
+ }
1518
+ }
1519
+ if (redactParts.length > 0) {
1520
+ headers.set("Upstash-Redact-Fields", redactParts.join(","));
1521
+ }
1522
+ }
1329
1523
  return await this.http.request({
1330
1524
  method: "POST",
1331
1525
  headers: wrapWithGlobalHeaders(headers, this.http.headers, this.http.telemetryHeaders),
@@ -1455,7 +1649,7 @@ var UrlGroups = class {
1455
1649
  };
1456
1650
 
1457
1651
  // version.ts
1458
- var VERSION = "v2.9.1";
1652
+ var VERSION = "2.10.1";
1459
1653
 
1460
1654
  // src/client/client.ts
1461
1655
  var Client = class {
@@ -1658,39 +1852,18 @@ var Client = class {
1658
1852
  * }
1659
1853
  * ```
1660
1854
  */
1661
- async logs(request) {
1662
- const query = {};
1663
- if (typeof request?.cursor === "number" && request.cursor > 0) {
1664
- query.cursor = request.cursor.toString();
1665
- } else if (typeof request?.cursor === "string" && request.cursor !== "") {
1666
- query.cursor = request.cursor;
1667
- }
1668
- for (const [key, value] of Object.entries(request?.filter ?? {})) {
1669
- if (typeof value === "number" && value < 0) {
1670
- continue;
1671
- }
1672
- if (key === "urlGroup") {
1673
- query.topicName = value.toString();
1674
- } else if (typeof value !== "undefined") {
1675
- query[key] = value.toString();
1676
- }
1677
- }
1855
+ async logs(request = {}) {
1856
+ const query = {
1857
+ count: request.count,
1858
+ ..."messageIds" in request ? { messageIds: request.messageIds } : { ...renameUrlGroup(request.filter ?? {}), cursor: request.cursor }
1859
+ };
1678
1860
  const responsePayload = await this.http.request({
1679
1861
  path: ["v2", "events"],
1680
1862
  method: "GET",
1681
1863
  query
1682
1864
  });
1683
- const logs = responsePayload.events.map((event) => {
1684
- return {
1685
- ...event,
1686
- urlGroup: event.topicName
1687
- };
1688
- });
1689
- return {
1690
- cursor: responsePayload.cursor,
1691
- logs,
1692
- events: logs
1693
- };
1865
+ const logs = responsePayload.events.map((event) => ({ ...event, urlGroup: event.topicName }));
1866
+ return { cursor: responsePayload.cursor, logs, events: logs };
1694
1867
  }
1695
1868
  /**
1696
1869
  * @deprecated Will be removed in the next major release. Use the `logs` method instead.
package/cloudflare.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  serve
3
- } from "./chunk-DT2X63FB.mjs";
3
+ } from "./chunk-35B33QW3.mjs";
4
4
 
5
5
  // platforms/cloudflare.ts
6
6
  var getArgs = (args) => {
package/h3.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as h3 from 'h3';
2
2
  import { H3Event } from 'h3';
3
- import { ae as RouteFunction, af as WorkflowServeOptions } from './client-jh_SomWB.mjs';
3
+ import { ae as RouteFunction, af as WorkflowServeOptions } from './client-CsM1dTnz.mjs';
4
4
  import 'neverthrow';
5
5
 
6
6
  type VerifySignatureConfig = {
package/h3.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as h3 from 'h3';
2
2
  import { H3Event } from 'h3';
3
- import { ae as RouteFunction, af as WorkflowServeOptions } from './client-jh_SomWB.js';
3
+ import { ae as RouteFunction, af as WorkflowServeOptions } from './client-CsM1dTnz.js';
4
4
  import 'neverthrow';
5
5
 
6
6
  type VerifySignatureConfig = {