@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/index.js CHANGED
@@ -38,6 +38,7 @@ __export(src_exports, {
38
38
  QStashWorkflowError: () => QStashWorkflowError,
39
39
  QstashChatRatelimitError: () => QstashChatRatelimitError,
40
40
  QstashDailyRatelimitError: () => QstashDailyRatelimitError,
41
+ QstashEmptyArrayError: () => QstashEmptyArrayError,
41
42
  QstashError: () => QstashError,
42
43
  QstashRatelimitError: () => QstashRatelimitError,
43
44
  Receiver: () => Receiver,
@@ -275,6 +276,14 @@ var QstashDailyRatelimitError = class extends QstashError {
275
276
  this.reset = args.reset;
276
277
  }
277
278
  };
279
+ var QstashEmptyArrayError = class extends QstashError {
280
+ constructor(parameterName) {
281
+ super(
282
+ `Empty array provided for query parameter "${parameterName}". This would result in no filter being applied, which could affect all resources.`
283
+ );
284
+ this.name = "QstashEmptyArrayError";
285
+ }
286
+ };
278
287
  var QStashWorkflowError = class extends QstashError {
279
288
  constructor(message) {
280
289
  super(message);
@@ -304,6 +313,7 @@ var formatWorkflowError = (error) => {
304
313
  };
305
314
 
306
315
  // src/client/utils.ts
316
+ var DEFAULT_BULK_COUNT = 100;
307
317
  var isIgnoredHeader = (header) => {
308
318
  const lowerCaseHeader = header.toLowerCase();
309
319
  return lowerCaseHeader.startsWith("content-type") || lowerCaseHeader.startsWith("upstash-");
@@ -390,6 +400,24 @@ function processHeaders(request) {
390
400
  if (request.label !== void 0) {
391
401
  headers.set("Upstash-Label", request.label);
392
402
  }
403
+ if (request.redact !== void 0) {
404
+ const redactParts = [];
405
+ if (request.redact.body) {
406
+ redactParts.push("body");
407
+ }
408
+ if (request.redact.header !== void 0) {
409
+ if (request.redact.header === true) {
410
+ redactParts.push("header");
411
+ } else if (Array.isArray(request.redact.header) && request.redact.header.length > 0) {
412
+ for (const headerName of request.redact.header) {
413
+ redactParts.push(`header[${headerName}]`);
414
+ }
415
+ }
416
+ }
417
+ if (redactParts.length > 0) {
418
+ headers.set("Upstash-Redact-Fields", redactParts.join(","));
419
+ }
420
+ }
393
421
  return headers;
394
422
  }
395
423
  function getRequestPath(request) {
@@ -424,6 +452,44 @@ function decodeBase64(base64) {
424
452
  }
425
453
  }
426
454
  }
455
+ function buildBulkActionFilterPayload(request) {
456
+ const cursor = "cursor" in request ? request.cursor : void 0;
457
+ if ("all" in request) {
458
+ const count2 = "count" in request ? request.count ?? DEFAULT_BULK_COUNT : DEFAULT_BULK_COUNT;
459
+ return { count: count2, cursor };
460
+ }
461
+ if ("dlqIds" in request) {
462
+ const ids = request.dlqIds;
463
+ if (Array.isArray(ids) && ids.length === 0) {
464
+ throw new QstashError(
465
+ "Empty dlqIds array provided. If you intend to target all DLQ messages, use { all: true } explicitly."
466
+ );
467
+ }
468
+ return { dlqIds: ids, cursor };
469
+ }
470
+ if ("messageIds" in request && request.messageIds) {
471
+ if (request.messageIds.length === 0) {
472
+ throw new QstashError(
473
+ "Empty messageIds array provided. If you intend to target all messages, use { all: true } explicitly."
474
+ );
475
+ }
476
+ return { messageIds: request.messageIds, cursor };
477
+ }
478
+ const count = "count" in request ? request.count ?? DEFAULT_BULK_COUNT : DEFAULT_BULK_COUNT;
479
+ return {
480
+ ...renameUrlGroup(request.filter),
481
+ count,
482
+ cursor
483
+ };
484
+ }
485
+ function renameUrlGroup(filter) {
486
+ const { urlGroup, api, ...rest } = filter;
487
+ return { ...rest, ...urlGroup === void 0 ? {} : { topicName: urlGroup } };
488
+ }
489
+ function normalizeCursor(response) {
490
+ const cursor = response.cursor;
491
+ return { ...response, cursor: cursor || void 0 };
492
+ }
427
493
  function getRuntime() {
428
494
  if (typeof process === "object" && typeof process.versions == "object" && process.versions.bun)
429
495
  return `bun@${process.versions.bun}`;
@@ -652,20 +718,21 @@ var DLQ = class {
652
718
  }
653
719
  /**
654
720
  * List messages in the dlq
721
+ *
722
+ * Can be called with:
723
+ * - Filters: `listMessages({ filter: { url: "https://example.com" } })`
724
+ * - DLQ IDs: `listMessages({ dlqIds: ["id1", "id2"] })`
725
+ * - No filter (list all): `listMessages()`
655
726
  */
656
- async listMessages(options) {
657
- const filterPayload = {
658
- ...options?.filter,
659
- topicName: options?.filter?.urlGroup
727
+ async listMessages(options = {}) {
728
+ const query = {
729
+ count: options.count,
730
+ ..."dlqIds" in options ? { dlqIds: options.dlqIds } : { ...renameUrlGroup(options.filter ?? {}), cursor: options.cursor }
660
731
  };
661
732
  const messagesPayload = await this.http.request({
662
733
  method: "GET",
663
734
  path: ["v2", "dlq"],
664
- query: {
665
- cursor: options?.cursor,
666
- count: options?.count,
667
- ...filterPayload
668
- }
735
+ query
669
736
  });
670
737
  return {
671
738
  messages: messagesPayload.messages.map((message) => {
@@ -679,26 +746,86 @@ var DLQ = class {
679
746
  };
680
747
  }
681
748
  /**
682
- * Remove a message from the dlq using it's `dlqId`
749
+ * Remove messages from the dlq.
750
+ *
751
+ * Can be called with:
752
+ * - A single dlqId: `delete("id")`
753
+ * - An array of dlqIds: `delete(["id1", "id2"])`
754
+ * - An object with dlqIds: `delete({ dlqIds: ["id1", "id2"] })`
755
+ * - A filter object: `delete({ filter: { url: "https://example.com", label: "label" } })`
756
+ * - All messages: `delete({ all: true })`
757
+ *
758
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
759
+ * Call in a loop until cursor is undefined:
760
+ *
761
+ * ```ts
762
+ * let cursor: string | undefined;
763
+ * do {
764
+ * const result = await dlq.delete({ all: true, count: 100, cursor });
765
+ * cursor = result.cursor;
766
+ * } while (cursor);
767
+ * ```
683
768
  */
684
- async delete(dlqMessageId) {
769
+ async delete(request) {
770
+ if (typeof request === "string") {
771
+ await this.http.request({
772
+ method: "DELETE",
773
+ path: ["v2", "dlq", request],
774
+ parseResponseAsJson: false
775
+ });
776
+ return { deleted: 1 };
777
+ }
778
+ if (Array.isArray(request) && request.length === 0)
779
+ return { deleted: 0 };
780
+ const filters = Array.isArray(request) ? { dlqIds: request } : request;
685
781
  return await this.http.request({
686
782
  method: "DELETE",
687
- path: ["v2", "dlq", dlqMessageId],
688
- parseResponseAsJson: false
689
- // there is no response
783
+ path: ["v2", "dlq"],
784
+ query: buildBulkActionFilterPayload(filters)
690
785
  });
691
786
  }
692
787
  /**
693
788
  * Remove multiple messages from the dlq using their `dlqId`s
789
+ *
790
+ * @deprecated Use `delete` instead
694
791
  */
695
792
  async deleteMany(request) {
696
- return await this.http.request({
697
- method: "DELETE",
698
- path: ["v2", "dlq"],
699
- headers: { "Content-Type": "application/json" },
700
- body: JSON.stringify({ dlqIds: request.dlqIds })
701
- });
793
+ return await this.delete(request);
794
+ }
795
+ /**
796
+ * Retry messages from the dlq.
797
+ *
798
+ * Can be called with:
799
+ * - A single dlqId: `retry("id")`
800
+ * - An array of dlqIds: `retry(["id1", "id2"])`
801
+ * - An object with dlqIds: `retry({ dlqIds: ["id1", "id2"] })`
802
+ * - A filter object: `retry({ filter: { url: "https://example.com", label: "label" } })`
803
+ * - All messages: `retry({ all: true })`
804
+ *
805
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
806
+ * Call in a loop until cursor is undefined:
807
+ *
808
+ * ```ts
809
+ * let cursor: string | undefined;
810
+ * do {
811
+ * const result = await dlq.retry({ all: true, count: 100, cursor });
812
+ * cursor = result.cursor;
813
+ * } while (cursor);
814
+ * ```
815
+ */
816
+ async retry(request) {
817
+ if (typeof request === "string")
818
+ request = [request];
819
+ if (Array.isArray(request) && request.length === 0)
820
+ return { responses: [] };
821
+ const filters = Array.isArray(request) ? { dlqIds: request } : request;
822
+ return normalizeCursor(
823
+ await this.http.request({
824
+ method: "POST",
825
+ path: ["v2", "dlq", "retry"],
826
+ query: buildBulkActionFilterPayload(filters)
827
+ })
828
+ );
702
829
  }
703
830
  };
704
831
 
@@ -900,7 +1027,18 @@ var HttpClient = class {
900
1027
  const url = new URL([request.baseUrl ?? this.baseUrl, ...request.path].join("/"));
901
1028
  if (request.query) {
902
1029
  for (const [key, value] of Object.entries(request.query)) {
903
- if (value !== void 0) {
1030
+ if (value === void 0)
1031
+ continue;
1032
+ if (Array.isArray(value)) {
1033
+ if (value.length === 0) {
1034
+ throw new QstashEmptyArrayError(key);
1035
+ }
1036
+ for (const item of value) {
1037
+ url.searchParams.append(key, item);
1038
+ }
1039
+ } else if (value instanceof Date) {
1040
+ url.searchParams.set(key, value.getTime().toString());
1041
+ } else {
904
1042
  url.searchParams.set(key, value.toString());
905
1043
  }
906
1044
  }
@@ -1131,29 +1269,68 @@ var Messages = class {
1131
1269
  return message;
1132
1270
  }
1133
1271
  /**
1134
- * Cancel a message
1272
+ * Cancel messages.
1273
+ *
1274
+ * Can be called with:
1275
+ * - A single messageId: `cancel("id")`
1276
+ * - An array of messageIds: `cancel(["id1", "id2"])`
1277
+ * - A filter object: `cancel({ filter: { flowControlKey: "key", label: "label" } })`
1278
+ * - All messages: `cancel({ all: true })`
1279
+ *
1280
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
1281
+ * Call in a loop until `cancelled` is 0:
1282
+ *
1283
+ * ```ts
1284
+ * let cancelled: number;
1285
+ * do {
1286
+ * const result = await messages.cancel({ all: true, count: 100 });
1287
+ * cancelled = result.cancelled;
1288
+ * } while (cancelled > 0);
1289
+ * ```
1135
1290
  */
1136
- async delete(messageId) {
1291
+ async cancel(request) {
1292
+ if (typeof request === "string") {
1293
+ return await this.http.request({
1294
+ method: "DELETE",
1295
+ path: ["v2", "messages", request]
1296
+ });
1297
+ }
1298
+ if (Array.isArray(request) && request.length === 0)
1299
+ return { cancelled: 0 };
1300
+ const filters = Array.isArray(request) ? { messageIds: request } : request;
1137
1301
  return await this.http.request({
1302
+ method: "DELETE",
1303
+ path: ["v2", "messages"],
1304
+ query: buildBulkActionFilterPayload(filters)
1305
+ });
1306
+ }
1307
+ /**
1308
+ * Delete a message.
1309
+ *
1310
+ * @deprecated Use `cancel(messageId: string)` instead
1311
+ */
1312
+ async delete(messageId) {
1313
+ await this.http.request({
1138
1314
  method: "DELETE",
1139
1315
  path: ["v2", "messages", messageId],
1140
1316
  parseResponseAsJson: false
1141
1317
  });
1142
1318
  }
1319
+ /**
1320
+ * Cancel multiple messages by their messageIds.
1321
+ *
1322
+ * @deprecated Use `cancel(messageIds: string[])` instead
1323
+ */
1143
1324
  async deleteMany(messageIds) {
1144
- const result = await this.http.request({
1145
- method: "DELETE",
1146
- path: ["v2", "messages"],
1147
- headers: { "Content-Type": "application/json" },
1148
- body: JSON.stringify({ messageIds })
1149
- });
1325
+ const result = await this.cancel(messageIds);
1150
1326
  return result.cancelled;
1151
1327
  }
1328
+ /**
1329
+ * Cancel all messages
1330
+ * @deprecated Use `cancel({all: true})` to cancel all
1331
+ */
1152
1332
  async deleteAll() {
1153
- const result = await this.http.request({
1154
- method: "DELETE",
1155
- path: ["v2", "messages"]
1156
- });
1333
+ const result = await this.cancel({ all: true });
1157
1334
  return result.cancelled;
1158
1335
  }
1159
1336
  };
@@ -1358,6 +1535,24 @@ var Schedules = class {
1358
1535
  if (request.label !== void 0) {
1359
1536
  headers.set("Upstash-Label", request.label);
1360
1537
  }
1538
+ if (request.redact !== void 0) {
1539
+ const redactParts = [];
1540
+ if (request.redact.body) {
1541
+ redactParts.push("body");
1542
+ }
1543
+ if (request.redact.header !== void 0) {
1544
+ if (request.redact.header === true) {
1545
+ redactParts.push("header");
1546
+ } else if (Array.isArray(request.redact.header) && request.redact.header.length > 0) {
1547
+ for (const headerName of request.redact.header) {
1548
+ redactParts.push(`header[${headerName}]`);
1549
+ }
1550
+ }
1551
+ }
1552
+ if (redactParts.length > 0) {
1553
+ headers.set("Upstash-Redact-Fields", redactParts.join(","));
1554
+ }
1555
+ }
1361
1556
  return await this.http.request({
1362
1557
  method: "POST",
1363
1558
  headers: wrapWithGlobalHeaders(headers, this.http.headers, this.http.telemetryHeaders),
@@ -1518,7 +1713,7 @@ var Workflow = class {
1518
1713
  };
1519
1714
 
1520
1715
  // version.ts
1521
- var VERSION = "v2.9.1";
1716
+ var VERSION = "2.10.0";
1522
1717
 
1523
1718
  // src/client/client.ts
1524
1719
  var Client = class {
@@ -1721,39 +1916,18 @@ var Client = class {
1721
1916
  * }
1722
1917
  * ```
1723
1918
  */
1724
- async logs(request) {
1725
- const query = {};
1726
- if (typeof request?.cursor === "number" && request.cursor > 0) {
1727
- query.cursor = request.cursor.toString();
1728
- } else if (typeof request?.cursor === "string" && request.cursor !== "") {
1729
- query.cursor = request.cursor;
1730
- }
1731
- for (const [key, value] of Object.entries(request?.filter ?? {})) {
1732
- if (typeof value === "number" && value < 0) {
1733
- continue;
1734
- }
1735
- if (key === "urlGroup") {
1736
- query.topicName = value.toString();
1737
- } else if (typeof value !== "undefined") {
1738
- query[key] = value.toString();
1739
- }
1740
- }
1919
+ async logs(request = {}) {
1920
+ const query = {
1921
+ count: request.count,
1922
+ ..."messageIds" in request ? { messageIds: request.messageIds } : { ...renameUrlGroup(request.filter ?? {}), cursor: request.cursor }
1923
+ };
1741
1924
  const responsePayload = await this.http.request({
1742
1925
  path: ["v2", "events"],
1743
1926
  method: "GET",
1744
1927
  query
1745
1928
  });
1746
- const logs = responsePayload.events.map((event) => {
1747
- return {
1748
- ...event,
1749
- urlGroup: event.topicName
1750
- };
1751
- });
1752
- return {
1753
- cursor: responsePayload.cursor,
1754
- logs,
1755
- events: logs
1756
- };
1929
+ const logs = responsePayload.events.map((event) => ({ ...event, urlGroup: event.topicName }));
1930
+ return { cursor: responsePayload.cursor, logs, events: logs };
1757
1931
  }
1758
1932
  /**
1759
1933
  * @deprecated Will be removed in the next major release. Use the `logs` method instead.
@@ -1818,6 +1992,7 @@ var resend = ({
1818
1992
  QStashWorkflowError,
1819
1993
  QstashChatRatelimitError,
1820
1994
  QstashDailyRatelimitError,
1995
+ QstashEmptyArrayError,
1821
1996
  QstashError,
1822
1997
  QstashRatelimitError,
1823
1998
  Receiver,
package/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  resend
3
- } from "./chunk-KDHOB7B5.mjs";
3
+ } from "./chunk-FAMFGAMR.mjs";
4
4
  import {
5
5
  Chat,
6
6
  Client,
@@ -10,6 +10,7 @@ import {
10
10
  QStashWorkflowError,
11
11
  QstashChatRatelimitError,
12
12
  QstashDailyRatelimitError,
13
+ QstashEmptyArrayError,
13
14
  QstashError,
14
15
  QstashRatelimitError,
15
16
  Receiver,
@@ -23,7 +24,7 @@ import {
23
24
  openai,
24
25
  setupAnalytics,
25
26
  upstash
26
- } from "./chunk-DT2X63FB.mjs";
27
+ } from "./chunk-X3MMU3BQ.mjs";
27
28
  export {
28
29
  Chat,
29
30
  Client,
@@ -33,6 +34,7 @@ export {
33
34
  QStashWorkflowError,
34
35
  QstashChatRatelimitError,
35
36
  QstashDailyRatelimitError,
37
+ QstashEmptyArrayError,
36
38
  QstashError,
37
39
  QstashRatelimitError,
38
40
  Receiver,
package/nextjs.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { NextApiHandler } from 'next';
2
2
  import { NextRequest, NextFetchEvent } from 'next/server';
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/nextjs.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { NextApiHandler } from 'next';
2
2
  import { NextRequest, NextFetchEvent } from 'next/server';
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 = {