@windrun-huaiin/backend-core 13.0.0 → 14.1.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.
Files changed (110) hide show
  1. package/dist/_virtual/index.js +7 -3
  2. package/dist/_virtual/index.mjs +5 -3
  3. package/dist/_virtual/index2.js +2 -6
  4. package/dist/_virtual/index2.mjs +2 -6
  5. package/dist/index.js +3 -1
  6. package/dist/index.mjs +2 -2
  7. package/dist/lib/index.js +3 -1
  8. package/dist/lib/index.mjs +2 -2
  9. package/dist/lib/stripe-config.d.ts +1 -1
  10. package/dist/lib/stripe-config.d.ts.map +1 -1
  11. package/dist/lib/stripe-config.js +25 -16
  12. package/dist/lib/stripe-config.mjs +25 -16
  13. package/dist/lib/upstash/qstash.d.ts.map +1 -1
  14. package/dist/lib/upstash/qstash.js +66 -62
  15. package/dist/lib/upstash/qstash.mjs +67 -63
  16. package/dist/lib/upstash/redis-counter.d.ts.map +1 -1
  17. package/dist/lib/upstash/redis-counter.js +9 -24
  18. package/dist/lib/upstash/redis-counter.mjs +10 -25
  19. package/dist/lib/upstash/redis-favorite.d.ts.map +1 -1
  20. package/dist/lib/upstash/redis-favorite.js +22 -36
  21. package/dist/lib/upstash/redis-favorite.mjs +23 -37
  22. package/dist/lib/upstash/redis-like.d.ts.map +1 -1
  23. package/dist/lib/upstash/redis-like.js +22 -36
  24. package/dist/lib/upstash/redis-like.mjs +23 -37
  25. package/dist/lib/upstash/redis-lock.d.ts.map +1 -1
  26. package/dist/lib/upstash/redis-lock.js +22 -38
  27. package/dist/lib/upstash/redis-lock.mjs +23 -39
  28. package/dist/lib/upstash/redis-structures.d.ts.map +1 -1
  29. package/dist/lib/upstash/redis-structures.js +77 -113
  30. package/dist/lib/upstash/redis-structures.mjs +78 -114
  31. package/dist/lib/upstash-config.d.ts +9 -1
  32. package/dist/lib/upstash-config.d.ts.map +1 -1
  33. package/dist/lib/upstash-config.js +221 -27
  34. package/dist/lib/upstash-config.mjs +220 -28
  35. package/dist/node_modules/.pnpm/{@upstash_qstash@2.8.4/node_modules/@upstash/qstash/chunk-RQPZUJXG.js → @upstash_qstash@2.10.1/node_modules/@upstash/qstash/chunk-35B33QW3.js} +897 -468
  36. package/dist/node_modules/.pnpm/{@upstash_qstash@2.8.4/node_modules/@upstash/qstash/chunk-RQPZUJXG.mjs → @upstash_qstash@2.10.1/node_modules/@upstash/qstash/chunk-35B33QW3.mjs} +895 -468
  37. package/dist/node_modules/.pnpm/{@upstash_redis@1.36.1/node_modules/@upstash/redis/chunk-LLI2WIYN.js → @upstash_redis@1.37.0/node_modules/@upstash/redis/chunk-IH7W44G6.js} +657 -40
  38. package/dist/node_modules/.pnpm/{@upstash_redis@1.36.1/node_modules/@upstash/redis/chunk-LLI2WIYN.mjs → @upstash_redis@1.37.0/node_modules/@upstash/redis/chunk-IH7W44G6.mjs} +657 -41
  39. package/dist/node_modules/.pnpm/{@upstash_redis@1.36.1 → @upstash_redis@1.37.0}/node_modules/@upstash/redis/nodejs.js +6 -5
  40. package/dist/node_modules/.pnpm/{@upstash_redis@1.36.1 → @upstash_redis@1.37.0}/node_modules/@upstash/redis/nodejs.mjs +2 -2
  41. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/index.js +1 -1
  42. package/dist/node_modules/.pnpm/crypto-js@4.2.0/node_modules/crypto-js/index.mjs +1 -1
  43. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/jws/flattened/verify.js +6 -6
  44. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/jwt/verify.js +1 -1
  45. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/key/import.js +2 -2
  46. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/epoch.js +3 -1
  47. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/is_disjoint.js +3 -1
  48. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/is_jwk.js +1 -1
  49. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/is_object.js +3 -1
  50. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/jwt_claims_set.js +7 -5
  51. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/secs.js +3 -1
  52. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/validate_algorithms.js +3 -1
  53. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/lib/validate_crit.js +3 -1
  54. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/check_key_length.js +3 -1
  55. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/dsa_digest.js +3 -1
  56. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/get_named_curve.js +4 -2
  57. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/get_sign_verify_key.js +3 -1
  58. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/hmac_digest.js +3 -1
  59. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/is_key_like.js +1 -1
  60. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/is_key_object.js +3 -1
  61. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/jwk_to_key.js +3 -1
  62. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/node_key.js +6 -4
  63. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/sign.js +6 -4
  64. package/dist/node_modules/.pnpm/jose@5.10.0/node_modules/jose/dist/node/esm/runtime/verify.js +7 -5
  65. package/dist/services/stripe/webhook-handler.js +4 -2
  66. package/dist/services/stripe/webhook-handler.mjs +3 -1
  67. package/package.json +5 -5
  68. package/src/lib/stripe-config.ts +27 -15
  69. package/src/lib/upstash/qstash.ts +64 -62
  70. package/src/lib/upstash/redis-counter.ts +10 -26
  71. package/src/lib/upstash/redis-favorite.ts +23 -42
  72. package/src/lib/upstash/redis-like.ts +23 -42
  73. package/src/lib/upstash/redis-lock.ts +23 -49
  74. package/src/lib/upstash/redis-structures.ts +82 -131
  75. package/src/lib/upstash-config.ts +231 -24
  76. package/src/services/stripe/webhook-handler.ts +3 -1
  77. package/dist/_virtual/index3.js +0 -5
  78. package/dist/_virtual/index3.mjs +0 -3
  79. package/dist/node_modules/.pnpm/@upstash_lock@0.2.1_typescript@5.9.3/node_modules/@upstash/lock/dist/index.js +0 -191
  80. package/dist/node_modules/.pnpm/@upstash_lock@0.2.1_typescript@5.9.3/node_modules/@upstash/lock/dist/index.mjs +0 -189
  81. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/errors.js +0 -54
  82. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/errors.mjs +0 -51
  83. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/iso.js +0 -44
  84. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/iso.mjs +0 -35
  85. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/parse.js +0 -31
  86. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/parse.mjs +0 -18
  87. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/schemas.js +0 -587
  88. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/classic/schemas.mjs +0 -527
  89. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/api.js +0 -447
  90. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/api.mjs +0 -399
  91. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/checks.js +0 -245
  92. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/checks.mjs +0 -232
  93. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/core.js +0 -68
  94. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/core.mjs +0 -62
  95. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/doc.js +0 -39
  96. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/doc.mjs +0 -37
  97. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/errors.js +0 -80
  98. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/errors.mjs +0 -75
  99. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/parse.js +0 -101
  100. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/parse.mjs +0 -86
  101. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/regexes.js +0 -102
  102. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/regexes.mjs +0 -76
  103. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/registries.js +0 -56
  104. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/registries.mjs +0 -52
  105. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/schemas.js +0 -1205
  106. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/schemas.mjs +0 -1157
  107. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/util.js +0 -407
  108. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/util.mjs +0 -374
  109. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/versions.js +0 -9
  110. package/dist/node_modules/.pnpm/zod@4.1.12/node_modules/zod/v4/core/versions.mjs +0 -7
@@ -1,7 +1,524 @@
1
- import crypto2 from '../../../../../../_virtual/index2.mjs';
1
+ import crypto2 from '../../../../../../_virtual/index.mjs';
2
2
  import '../../../../neverthrow@7.2.0/node_modules/neverthrow/dist/index.es.mjs';
3
3
  import { jwtVerify } from '../../../../jose@5.10.0/node_modules/jose/dist/node/esm/jwt/verify.mjs';
4
4
 
5
+ // src/receiver.ts
6
+
7
+ // src/client/api/base.ts
8
+ var BaseProvider = class {
9
+ baseUrl;
10
+ token;
11
+ owner;
12
+ constructor(baseUrl, token, owner) {
13
+ this.baseUrl = baseUrl;
14
+ this.token = token;
15
+ this.owner = owner;
16
+ }
17
+ getUrl() {
18
+ return `${this.baseUrl}/${this.getRoute().join("/")}`;
19
+ }
20
+ };
21
+
22
+ // src/client/api/llm.ts
23
+ var LLMProvider = class extends BaseProvider {
24
+ apiKind = "llm";
25
+ organization;
26
+ method = "POST";
27
+ constructor(baseUrl, token, owner, organization) {
28
+ super(baseUrl, token, owner);
29
+ this.organization = organization;
30
+ }
31
+ getRoute() {
32
+ return this.owner === "anthropic" ? ["v1", "messages"] : ["v1", "chat", "completions"];
33
+ }
34
+ getHeaders(options) {
35
+ if (this.owner === "upstash" && !options.analytics) {
36
+ return { "content-type": "application/json" };
37
+ }
38
+ const header = this.owner === "anthropic" ? "x-api-key" : "authorization";
39
+ const headerValue = this.owner === "anthropic" ? this.token : `Bearer ${this.token}`;
40
+ const headers = {
41
+ [header]: headerValue,
42
+ "content-type": "application/json"
43
+ };
44
+ if (this.owner === "openai" && this.organization) {
45
+ headers["OpenAI-Organization"] = this.organization;
46
+ }
47
+ if (this.owner === "anthropic") {
48
+ headers["anthropic-version"] = "2023-06-01";
49
+ }
50
+ return headers;
51
+ }
52
+ /**
53
+ * Checks if callback exists and adds analytics in place if it's set.
54
+ *
55
+ * @param request
56
+ * @param options
57
+ */
58
+ onFinish(providerInfo, options) {
59
+ if (options.analytics) {
60
+ return updateWithAnalytics(providerInfo, options.analytics);
61
+ }
62
+ return providerInfo;
63
+ }
64
+ };
65
+ var upstash = () => {
66
+ return new LLMProvider("https://qstash.upstash.io/llm", "", "upstash");
67
+ };
68
+
69
+ // src/client/api/utils.ts
70
+ var getProviderInfo = (api, upstashToken) => {
71
+ const { name, provider, ...parameters } = api;
72
+ const finalProvider = provider ?? upstash();
73
+ if (finalProvider.owner === "upstash" && !finalProvider.token) {
74
+ finalProvider.token = upstashToken;
75
+ }
76
+ if (!finalProvider.baseUrl)
77
+ throw new TypeError("baseUrl cannot be empty or undefined!");
78
+ if (!finalProvider.token)
79
+ throw new TypeError("token cannot be empty or undefined!");
80
+ if (finalProvider.apiKind !== name) {
81
+ throw new TypeError(
82
+ `Unexpected api name. Expected '${finalProvider.apiKind}', received ${name}`
83
+ );
84
+ }
85
+ const providerInfo = {
86
+ url: finalProvider.getUrl(),
87
+ baseUrl: finalProvider.baseUrl,
88
+ route: finalProvider.getRoute(),
89
+ appendHeaders: finalProvider.getHeaders(parameters),
90
+ owner: finalProvider.owner,
91
+ method: finalProvider.method
92
+ };
93
+ return finalProvider.onFinish(providerInfo, parameters);
94
+ };
95
+ var safeJoinHeaders = (headers, record) => {
96
+ const joinedHeaders = new Headers(record);
97
+ for (const [header, value] of headers.entries()) {
98
+ joinedHeaders.set(header, value);
99
+ }
100
+ return joinedHeaders;
101
+ };
102
+ var processApi = (request, headers, upstashToken) => {
103
+ if (!request.api) {
104
+ request.headers = headers;
105
+ return request;
106
+ }
107
+ const { url, appendHeaders, owner, method } = getProviderInfo(request.api, upstashToken);
108
+ if (request.api.name === "llm") {
109
+ const callback = request.callback;
110
+ if (!callback) {
111
+ throw new TypeError("Callback cannot be undefined when using LLM api.");
112
+ }
113
+ return {
114
+ ...request,
115
+ method: request.method ?? method,
116
+ headers: safeJoinHeaders(headers, appendHeaders),
117
+ ...owner === "upstash" && !request.api.analytics ? { api: { name: "llm" }, url: void 0, callback } : { url, api: void 0 }
118
+ };
119
+ } else {
120
+ return {
121
+ ...request,
122
+ method: request.method ?? method,
123
+ headers: safeJoinHeaders(headers, appendHeaders),
124
+ url,
125
+ api: void 0
126
+ };
127
+ }
128
+ };
129
+ function updateWithAnalytics(providerInfo, analytics) {
130
+ switch (analytics.name) {
131
+ case "helicone": {
132
+ providerInfo.appendHeaders["Helicone-Auth"] = `Bearer ${analytics.token}`;
133
+ if (providerInfo.owner === "upstash") {
134
+ updateProviderInfo(providerInfo, "https://qstash.helicone.ai", [
135
+ "llm",
136
+ ...providerInfo.route
137
+ ]);
138
+ } else {
139
+ providerInfo.appendHeaders["Helicone-Target-Url"] = providerInfo.baseUrl;
140
+ updateProviderInfo(providerInfo, "https://gateway.helicone.ai", providerInfo.route);
141
+ }
142
+ return providerInfo;
143
+ }
144
+ default: {
145
+ throw new Error("Unknown analytics provider");
146
+ }
147
+ }
148
+ }
149
+ function updateProviderInfo(providerInfo, baseUrl, route) {
150
+ providerInfo.baseUrl = baseUrl;
151
+ providerInfo.route = route;
152
+ providerInfo.url = `${baseUrl}/${route.join("/")}`;
153
+ }
154
+
155
+ // src/client/error.ts
156
+ var RATELIMIT_STATUS = 429;
157
+ var QstashError = class extends Error {
158
+ status;
159
+ constructor(message, status) {
160
+ super(message);
161
+ this.name = "QstashError";
162
+ this.status = status;
163
+ }
164
+ };
165
+ var QstashRatelimitError = class extends QstashError {
166
+ limit;
167
+ remaining;
168
+ reset;
169
+ constructor(args) {
170
+ super(`Exceeded burst rate limit. ${JSON.stringify(args)}`, RATELIMIT_STATUS);
171
+ this.name = "QstashRatelimitError";
172
+ this.limit = args.limit;
173
+ this.remaining = args.remaining;
174
+ this.reset = args.reset;
175
+ }
176
+ };
177
+ var QstashChatRatelimitError = class extends QstashError {
178
+ limitRequests;
179
+ limitTokens;
180
+ remainingRequests;
181
+ remainingTokens;
182
+ resetRequests;
183
+ resetTokens;
184
+ constructor(args) {
185
+ super(`Exceeded chat rate limit. ${JSON.stringify(args)}`, RATELIMIT_STATUS);
186
+ this.name = "QstashChatRatelimitError";
187
+ this.limitRequests = args["limit-requests"];
188
+ this.limitTokens = args["limit-tokens"];
189
+ this.remainingRequests = args["remaining-requests"];
190
+ this.remainingTokens = args["remaining-tokens"];
191
+ this.resetRequests = args["reset-requests"];
192
+ this.resetTokens = args["reset-tokens"];
193
+ }
194
+ };
195
+ var QstashDailyRatelimitError = class extends QstashError {
196
+ limit;
197
+ remaining;
198
+ reset;
199
+ constructor(args) {
200
+ super(`Exceeded daily rate limit. ${JSON.stringify(args)}`, RATELIMIT_STATUS);
201
+ this.name = "QstashDailyRatelimitError";
202
+ this.limit = args.limit;
203
+ this.remaining = args.remaining;
204
+ this.reset = args.reset;
205
+ }
206
+ };
207
+ var QstashEmptyArrayError = class extends QstashError {
208
+ constructor(parameterName) {
209
+ super(
210
+ `Empty array provided for query parameter "${parameterName}". This would result in no filter being applied, which could affect all resources.`
211
+ );
212
+ this.name = "QstashEmptyArrayError";
213
+ }
214
+ };
215
+
216
+ // src/client/utils.ts
217
+ var DEFAULT_BULK_COUNT = 100;
218
+ var isIgnoredHeader = (header) => {
219
+ const lowerCaseHeader = header.toLowerCase();
220
+ return lowerCaseHeader.startsWith("content-type") || lowerCaseHeader.startsWith("upstash-");
221
+ };
222
+ function prefixHeaders(headers) {
223
+ const keysToBePrefixed = [...headers.keys()].filter((key) => !isIgnoredHeader(key));
224
+ for (const key of keysToBePrefixed) {
225
+ const value = headers.get(key);
226
+ if (value !== null) {
227
+ headers.set(`Upstash-Forward-${key}`, value);
228
+ }
229
+ headers.delete(key);
230
+ }
231
+ return headers;
232
+ }
233
+ function wrapWithGlobalHeaders(headers, globalHeaders, telemetryHeaders) {
234
+ if (!globalHeaders) {
235
+ return headers;
236
+ }
237
+ const finalHeaders = new Headers(globalHeaders);
238
+ headers.forEach((value, key) => {
239
+ finalHeaders.set(key, value);
240
+ });
241
+ telemetryHeaders?.forEach((value, key) => {
242
+ if (!value)
243
+ return;
244
+ finalHeaders.append(key, value);
245
+ });
246
+ return finalHeaders;
247
+ }
248
+ function processHeaders(request) {
249
+ const headers = prefixHeaders(new Headers(request.headers));
250
+ headers.set("Upstash-Method", request.method ?? "POST");
251
+ if (request.delay !== void 0) {
252
+ if (typeof request.delay === "string") {
253
+ headers.set("Upstash-Delay", request.delay);
254
+ } else {
255
+ headers.set("Upstash-Delay", `${request.delay.toFixed(0)}s`);
256
+ }
257
+ }
258
+ if (request.notBefore !== void 0) {
259
+ headers.set("Upstash-Not-Before", request.notBefore.toFixed(0));
260
+ }
261
+ if (request.deduplicationId !== void 0) {
262
+ headers.set("Upstash-Deduplication-Id", request.deduplicationId);
263
+ }
264
+ if (request.contentBasedDeduplication) {
265
+ headers.set("Upstash-Content-Based-Deduplication", "true");
266
+ }
267
+ if (request.retries !== void 0) {
268
+ headers.set("Upstash-Retries", request.retries.toFixed(0));
269
+ }
270
+ if (request.retryDelay !== void 0) {
271
+ headers.set("Upstash-Retry-Delay", request.retryDelay);
272
+ }
273
+ if (request.callback !== void 0) {
274
+ headers.set("Upstash-Callback", request.callback);
275
+ }
276
+ if (request.failureCallback !== void 0) {
277
+ headers.set("Upstash-Failure-Callback", request.failureCallback);
278
+ }
279
+ if (request.timeout !== void 0) {
280
+ if (typeof request.timeout === "string") {
281
+ headers.set("Upstash-Timeout", request.timeout);
282
+ } else {
283
+ headers.set("Upstash-Timeout", `${request.timeout}s`);
284
+ }
285
+ }
286
+ if (request.flowControl?.key) {
287
+ const parallelism = request.flowControl.parallelism?.toString();
288
+ const rate = (request.flowControl.rate ?? request.flowControl.ratePerSecond)?.toString();
289
+ const period = typeof request.flowControl.period === "number" ? `${request.flowControl.period}s` : request.flowControl.period;
290
+ const controlValue = [
291
+ parallelism ? `parallelism=${parallelism}` : void 0,
292
+ rate ? `rate=${rate}` : void 0,
293
+ period ? `period=${period}` : void 0
294
+ ].filter(Boolean);
295
+ if (controlValue.length === 0) {
296
+ throw new QstashError("Provide at least one of parallelism or ratePerSecond for flowControl");
297
+ }
298
+ headers.set("Upstash-Flow-Control-Key", request.flowControl.key);
299
+ headers.set("Upstash-Flow-Control-Value", controlValue.join(", "));
300
+ }
301
+ if (request.label !== void 0) {
302
+ headers.set("Upstash-Label", request.label);
303
+ }
304
+ if (request.redact !== void 0) {
305
+ const redactParts = [];
306
+ if (request.redact.body) {
307
+ redactParts.push("body");
308
+ }
309
+ if (request.redact.header !== void 0) {
310
+ if (request.redact.header === true) {
311
+ redactParts.push("header");
312
+ } else if (Array.isArray(request.redact.header) && request.redact.header.length > 0) {
313
+ for (const headerName of request.redact.header) {
314
+ redactParts.push(`header[${headerName}]`);
315
+ }
316
+ }
317
+ }
318
+ if (redactParts.length > 0) {
319
+ headers.set("Upstash-Redact-Fields", redactParts.join(","));
320
+ }
321
+ }
322
+ return headers;
323
+ }
324
+ function getRequestPath(request) {
325
+ const nonApiPath = request.url ?? request.urlGroup ?? request.topic;
326
+ if (nonApiPath)
327
+ return nonApiPath;
328
+ if (request.api?.name === "llm")
329
+ return `api/llm`;
330
+ if (request.api?.name === "email") {
331
+ const providerInfo = getProviderInfo(request.api, "not-needed");
332
+ return providerInfo.baseUrl;
333
+ }
334
+ throw new QstashError(`Failed to infer request path for ${JSON.stringify(request)}`);
335
+ }
336
+ function buildBulkActionFilterPayload(request) {
337
+ const cursor = "cursor" in request ? request.cursor : void 0;
338
+ if ("all" in request) {
339
+ const count2 = "count" in request ? request.count ?? DEFAULT_BULK_COUNT : DEFAULT_BULK_COUNT;
340
+ return { count: count2, cursor };
341
+ }
342
+ if ("dlqIds" in request) {
343
+ const ids = request.dlqIds;
344
+ if (Array.isArray(ids) && ids.length === 0) {
345
+ throw new QstashError(
346
+ "Empty dlqIds array provided. If you intend to target all DLQ messages, use { all: true } explicitly."
347
+ );
348
+ }
349
+ return { dlqIds: ids, cursor };
350
+ }
351
+ if ("messageIds" in request && request.messageIds) {
352
+ if (request.messageIds.length === 0) {
353
+ throw new QstashError(
354
+ "Empty messageIds array provided. If you intend to target all messages, use { all: true } explicitly."
355
+ );
356
+ }
357
+ return { messageIds: request.messageIds, cursor };
358
+ }
359
+ const count = "count" in request ? request.count ?? DEFAULT_BULK_COUNT : DEFAULT_BULK_COUNT;
360
+ return {
361
+ ...renameUrlGroup(request.filter),
362
+ count,
363
+ cursor
364
+ };
365
+ }
366
+ function renameUrlGroup(filter) {
367
+ const { urlGroup, api, ...rest } = filter;
368
+ return { ...rest, ...urlGroup === void 0 ? {} : { topicName: urlGroup } };
369
+ }
370
+ function normalizeCursor(response) {
371
+ const cursor = response.cursor;
372
+ return { ...response, cursor: cursor || void 0 };
373
+ }
374
+ function getRuntime() {
375
+ if (typeof process === "object" && typeof process.versions == "object" && process.versions.bun)
376
+ return `bun@${process.versions.bun}`;
377
+ if (typeof EdgeRuntime === "string")
378
+ return "edge-light";
379
+ else if (typeof process === "object" && typeof process.version === "string")
380
+ return `node@${process.version}`;
381
+ return "";
382
+ }
383
+ function getSafeEnvironment() {
384
+ return typeof process === "undefined" ? {} : process.env;
385
+ }
386
+
387
+ // src/client/multi-region/utils.ts
388
+ var VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"];
389
+ var DEFAULT_QSTASH_URL = "https://qstash.upstash.io";
390
+ var getRegionFromEnvironment = (environment) => {
391
+ const region = environment.QSTASH_REGION;
392
+ return normalizeRegionHeader(region);
393
+ };
394
+ function readEnvironmentVariables(environmentVariables, environment, region) {
395
+ const result = {};
396
+ for (const variable of environmentVariables) {
397
+ const key = region ? `${region}_${variable}` : variable;
398
+ result[variable] = environment[key];
399
+ }
400
+ return result;
401
+ }
402
+ function readClientEnvironmentVariables(environment, region) {
403
+ return readEnvironmentVariables(["QSTASH_URL", "QSTASH_TOKEN"], environment, region);
404
+ }
405
+ function readReceiverEnvironmentVariables(environment, region) {
406
+ return readEnvironmentVariables(
407
+ ["QSTASH_CURRENT_SIGNING_KEY", "QSTASH_NEXT_SIGNING_KEY"],
408
+ environment,
409
+ region
410
+ );
411
+ }
412
+ function normalizeRegionHeader(region) {
413
+ if (!region) {
414
+ return void 0;
415
+ }
416
+ region = region.replaceAll("-", "_").toUpperCase();
417
+ if (VALID_REGIONS.includes(region)) {
418
+ return region;
419
+ }
420
+ console.warn(
421
+ `[Upstash QStash] Invalid UPSTASH_REGION header value: "${region}". Expected one of: ${VALID_REGIONS.join(
422
+ ", "
423
+ )}.`
424
+ );
425
+ return void 0;
426
+ }
427
+
428
+ // src/client/multi-region/incoming.ts
429
+ var getReceiverSigningKeys = ({
430
+ environment,
431
+ regionFromHeader,
432
+ config
433
+ }) => {
434
+ if (config?.currentSigningKey && config.nextSigningKey) {
435
+ return {
436
+ currentSigningKey: config.currentSigningKey,
437
+ nextSigningKey: config.nextSigningKey
438
+ };
439
+ }
440
+ const regionEnvironment = getRegionFromEnvironment(environment);
441
+ if (regionEnvironment) {
442
+ const regionHeader = normalizeRegionHeader(regionFromHeader);
443
+ if (regionHeader) {
444
+ const regionCreds = readReceiverEnvironmentVariables(environment, regionHeader);
445
+ if (regionCreds.QSTASH_CURRENT_SIGNING_KEY && regionCreds.QSTASH_NEXT_SIGNING_KEY) {
446
+ return {
447
+ currentSigningKey: regionCreds.QSTASH_CURRENT_SIGNING_KEY,
448
+ nextSigningKey: regionCreds.QSTASH_NEXT_SIGNING_KEY,
449
+ region: regionHeader
450
+ };
451
+ } else {
452
+ console.warn(
453
+ `[Upstash QStash] Signing keys not found for region "${regionHeader}". Falling back to default signing keys.`
454
+ );
455
+ }
456
+ } else {
457
+ console.warn(
458
+ `[Upstash QStash] Invalid UPSTASH_REGION header value: "${regionFromHeader}". Expected one of: EU-CENTRAL-1, US-EAST-1. Falling back to default signing keys.`
459
+ );
460
+ }
461
+ }
462
+ const defaultCreds = readReceiverEnvironmentVariables(environment);
463
+ if (defaultCreds.QSTASH_CURRENT_SIGNING_KEY && defaultCreds.QSTASH_NEXT_SIGNING_KEY) {
464
+ return {
465
+ currentSigningKey: defaultCreds.QSTASH_CURRENT_SIGNING_KEY,
466
+ nextSigningKey: defaultCreds.QSTASH_NEXT_SIGNING_KEY
467
+ };
468
+ }
469
+ };
470
+
471
+ // src/client/multi-region/outgoing.ts
472
+ var getClientCredentials = (clientCredentialConfig) => {
473
+ const credentials = resolveCredentials(clientCredentialConfig);
474
+ return verifyCredentials(credentials);
475
+ };
476
+ var resolveCredentials = ({
477
+ environment,
478
+ config
479
+ }) => {
480
+ if (config?.baseUrl && config.token) {
481
+ return {
482
+ baseUrl: config.baseUrl,
483
+ token: config.token
484
+ };
485
+ }
486
+ const region = getRegionFromEnvironment(environment);
487
+ if (region) {
488
+ const regionCreds = readClientEnvironmentVariables(environment, region);
489
+ if (regionCreds.QSTASH_URL && regionCreds.QSTASH_TOKEN) {
490
+ return {
491
+ baseUrl: regionCreds.QSTASH_URL,
492
+ token: regionCreds.QSTASH_TOKEN,
493
+ region
494
+ };
495
+ } else {
496
+ console.warn(
497
+ `[Upstash QStash] QSTASH_REGION is set to "${region}" but credentials are missing. Expected ${region}_QSTASH_URL and ${region}_QSTASH_TOKEN. Falling back to default credentials.`
498
+ );
499
+ }
500
+ }
501
+ const defaultCreds = readClientEnvironmentVariables(environment);
502
+ return {
503
+ baseUrl: config?.baseUrl ?? defaultCreds.QSTASH_URL ?? DEFAULT_QSTASH_URL,
504
+ token: config?.token ?? defaultCreds.QSTASH_TOKEN ?? ""
505
+ };
506
+ };
507
+ var verifyCredentials = (credentials) => {
508
+ const token = credentials.token;
509
+ let baseUrl = credentials.baseUrl;
510
+ baseUrl = baseUrl.replace(/\/$/, "");
511
+ if (baseUrl === "https://qstash.upstash.io/v2/publish") {
512
+ baseUrl = DEFAULT_QSTASH_URL;
513
+ }
514
+ if (!token) {
515
+ console.warn(
516
+ "[Upstash QStash] client token is not set. Either pass a token or set QSTASH_TOKEN env variable."
517
+ );
518
+ }
519
+ return { baseUrl, token };
520
+ };
521
+
5
522
  // src/receiver.ts
6
523
  var SignatureError = class extends Error {
7
524
  constructor(message) {
@@ -13,8 +530,8 @@ var Receiver = class {
13
530
  currentSigningKey;
14
531
  nextSigningKey;
15
532
  constructor(config) {
16
- this.currentSigningKey = config.currentSigningKey;
17
- this.nextSigningKey = config.nextSigningKey;
533
+ this.currentSigningKey = config?.currentSigningKey;
534
+ this.nextSigningKey = config?.nextSigningKey;
18
535
  }
19
536
  /**
20
537
  * Verify the signature of a request.
@@ -26,11 +543,25 @@ var Receiver = class {
26
543
  * If that fails, the signature is invalid and a `SignatureError` is thrown.
27
544
  */
28
545
  async verify(request) {
546
+ const environment = getSafeEnvironment();
547
+ const signingKeys = getReceiverSigningKeys({
548
+ environment,
549
+ regionFromHeader: request.upstashRegion,
550
+ config: {
551
+ currentSigningKey: this.currentSigningKey,
552
+ nextSigningKey: this.nextSigningKey
553
+ }
554
+ });
555
+ if (!signingKeys) {
556
+ throw new Error(
557
+ "[Upstash QStash] No signing keys available for verification. See the warning above for more details."
558
+ );
559
+ }
29
560
  let payload;
30
561
  try {
31
- payload = await this.verifyWithKey(this.currentSigningKey, request);
562
+ payload = await this.verifyWithKey(signingKeys.currentSigningKey, request);
32
563
  } catch {
33
- payload = await this.verifyWithKey(this.nextSigningKey, request);
564
+ payload = await this.verifyWithKey(signingKeys.nextSigningKey, request);
34
565
  }
35
566
  this.verifyBodyAndUrl(payload, request);
36
567
  return true;
@@ -68,20 +599,21 @@ var DLQ = class {
68
599
  }
69
600
  /**
70
601
  * List messages in the dlq
602
+ *
603
+ * Can be called with:
604
+ * - Filters: `listMessages({ filter: { url: "https://example.com" } })`
605
+ * - DLQ IDs: `listMessages({ dlqIds: ["id1", "id2"] })`
606
+ * - No filter (list all): `listMessages()`
71
607
  */
72
- async listMessages(options) {
73
- const filterPayload = {
74
- ...options?.filter,
75
- topicName: options?.filter?.urlGroup
608
+ async listMessages(options = {}) {
609
+ const query = {
610
+ count: options.count,
611
+ ..."dlqIds" in options ? { dlqIds: options.dlqIds } : { ...renameUrlGroup(options.filter ?? {}), cursor: options.cursor }
76
612
  };
77
613
  const messagesPayload = await this.http.request({
78
614
  method: "GET",
79
615
  path: ["v2", "dlq"],
80
- query: {
81
- cursor: options?.cursor,
82
- count: options?.count,
83
- ...filterPayload
84
- }
616
+ query
85
617
  });
86
618
  return {
87
619
  messages: messagesPayload.messages.map((message) => {
@@ -95,79 +627,187 @@ var DLQ = class {
95
627
  };
96
628
  }
97
629
  /**
98
- * Remove a message from the dlq using it's `dlqId`
630
+ * Remove messages from the dlq.
631
+ *
632
+ * Can be called with:
633
+ * - A single dlqId: `delete("id")`
634
+ * - An array of dlqIds: `delete(["id1", "id2"])`
635
+ * - An object with dlqIds: `delete({ dlqIds: ["id1", "id2"] })`
636
+ * - A filter object: `delete({ filter: { url: "https://example.com", label: "label" } })`
637
+ * - All messages: `delete({ all: true })`
638
+ *
639
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
640
+ * Call in a loop until cursor is undefined:
641
+ *
642
+ * ```ts
643
+ * let cursor: string | undefined;
644
+ * do {
645
+ * const result = await dlq.delete({ all: true, count: 100, cursor });
646
+ * cursor = result.cursor;
647
+ * } while (cursor);
648
+ * ```
99
649
  */
100
- async delete(dlqMessageId) {
650
+ async delete(request) {
651
+ if (typeof request === "string") {
652
+ await this.http.request({
653
+ method: "DELETE",
654
+ path: ["v2", "dlq", request],
655
+ parseResponseAsJson: false
656
+ });
657
+ return { deleted: 1 };
658
+ }
659
+ if (Array.isArray(request) && request.length === 0)
660
+ return { deleted: 0 };
661
+ const filters = Array.isArray(request) ? { dlqIds: request } : request;
101
662
  return await this.http.request({
102
663
  method: "DELETE",
103
- path: ["v2", "dlq", dlqMessageId],
104
- parseResponseAsJson: false
105
- // there is no response
664
+ path: ["v2", "dlq"],
665
+ query: buildBulkActionFilterPayload(filters)
106
666
  });
107
667
  }
108
668
  /**
109
669
  * Remove multiple messages from the dlq using their `dlqId`s
670
+ *
671
+ * @deprecated Use `delete` instead
110
672
  */
111
673
  async deleteMany(request) {
112
- return await this.http.request({
113
- method: "DELETE",
114
- path: ["v2", "dlq"],
115
- headers: { "Content-Type": "application/json" },
116
- body: JSON.stringify({ dlqIds: request.dlqIds })
117
- });
674
+ return await this.delete(request);
675
+ }
676
+ /**
677
+ * Retry messages from the dlq.
678
+ *
679
+ * Can be called with:
680
+ * - A single dlqId: `retry("id")`
681
+ * - An array of dlqIds: `retry(["id1", "id2"])`
682
+ * - An object with dlqIds: `retry({ dlqIds: ["id1", "id2"] })`
683
+ * - A filter object: `retry({ filter: { url: "https://example.com", label: "label" } })`
684
+ * - All messages: `retry({ all: true })`
685
+ *
686
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
687
+ * Call in a loop until cursor is undefined:
688
+ *
689
+ * ```ts
690
+ * let cursor: string | undefined;
691
+ * do {
692
+ * const result = await dlq.retry({ all: true, count: 100, cursor });
693
+ * cursor = result.cursor;
694
+ * } while (cursor);
695
+ * ```
696
+ */
697
+ async retry(request) {
698
+ if (typeof request === "string")
699
+ request = [request];
700
+ if (Array.isArray(request) && request.length === 0)
701
+ return { responses: [] };
702
+ const filters = Array.isArray(request) ? { dlqIds: request } : request;
703
+ return normalizeCursor(
704
+ await this.http.request({
705
+ method: "POST",
706
+ path: ["v2", "dlq", "retry"],
707
+ query: buildBulkActionFilterPayload(filters)
708
+ })
709
+ );
118
710
  }
119
711
  };
120
712
 
121
- // src/client/error.ts
122
- var RATELIMIT_STATUS = 429;
123
- var QstashError = class extends Error {
124
- status;
125
- constructor(message, status) {
126
- super(message);
127
- this.name = "QstashError";
128
- this.status = status;
713
+ // src/client/flow-control.ts
714
+ var FlowControlApi = class {
715
+ http;
716
+ constructor(http) {
717
+ this.http = http;
129
718
  }
130
- };
131
- var QstashRatelimitError = class extends QstashError {
132
- limit;
133
- remaining;
134
- reset;
135
- constructor(args) {
136
- super(`Exceeded burst rate limit. ${JSON.stringify(args)}`, RATELIMIT_STATUS);
137
- this.name = "QstashRatelimitError";
138
- this.limit = args.limit;
139
- this.remaining = args.remaining;
140
- this.reset = args.reset;
719
+ /**
720
+ * Get a single flow control by key.
721
+ */
722
+ async get(flowControlKey) {
723
+ return await this.http.request({
724
+ method: "GET",
725
+ path: ["v2", "flowControl", flowControlKey]
726
+ });
141
727
  }
142
- };
143
- var QstashChatRatelimitError = class extends QstashError {
144
- limitRequests;
145
- limitTokens;
146
- remainingRequests;
147
- remainingTokens;
148
- resetRequests;
149
- resetTokens;
150
- constructor(args) {
151
- super(`Exceeded chat rate limit. ${JSON.stringify(args)}`, RATELIMIT_STATUS);
152
- this.name = "QstashChatRatelimitError";
153
- this.limitRequests = args["limit-requests"];
154
- this.limitTokens = args["limit-tokens"];
155
- this.remainingRequests = args["remaining-requests"];
156
- this.remainingTokens = args["remaining-tokens"];
157
- this.resetRequests = args["reset-requests"];
158
- this.resetTokens = args["reset-tokens"];
728
+ /**
729
+ * Get the global parallelism info.
730
+ */
731
+ async getGlobalParallelism() {
732
+ const response = await this.http.request({
733
+ method: "GET",
734
+ path: ["v2", "globalParallelism"]
735
+ });
736
+ return {
737
+ parallelismMax: response.parallelismMax ?? 0,
738
+ parallelismCount: response.parallelismCount ?? 0
739
+ };
740
+ }
741
+ /**
742
+ * Pause message delivery for a flow-control key.
743
+ *
744
+ * Messages already in the waitlist will remain there.
745
+ * New incoming messages will be added directly to the waitlist.
746
+ */
747
+ async pause(flowControlKey) {
748
+ await this.http.request({
749
+ method: "POST",
750
+ path: ["v2", "flowControl", flowControlKey, "pause"],
751
+ parseResponseAsJson: false
752
+ });
753
+ }
754
+ /**
755
+ * Resume message delivery for a flow-control key.
756
+ */
757
+ async resume(flowControlKey) {
758
+ await this.http.request({
759
+ method: "POST",
760
+ path: ["v2", "flowControl", flowControlKey, "resume"],
761
+ parseResponseAsJson: false
762
+ });
763
+ }
764
+ /**
765
+ * Pin a processing configuration for a flow-control key.
766
+ *
767
+ * While pinned, the system ignores configurations provided by incoming
768
+ * messages and uses the pinned configuration instead.
769
+ */
770
+ async pin(flowControlKey, options) {
771
+ await this.http.request({
772
+ method: "POST",
773
+ path: ["v2", "flowControl", flowControlKey, "pin"],
774
+ query: {
775
+ parallelism: options.parallelism,
776
+ rate: options.rate,
777
+ period: options.period
778
+ },
779
+ parseResponseAsJson: false
780
+ });
159
781
  }
160
- };
161
- var QstashDailyRatelimitError = class extends QstashError {
162
- limit;
163
- remaining;
164
- reset;
165
- constructor(args) {
166
- super(`Exceeded daily rate limit. ${JSON.stringify(args)}`, RATELIMIT_STATUS);
167
- this.name = "QstashDailyRatelimitError";
168
- this.limit = args.limit;
169
- this.remaining = args.remaining;
170
- this.reset = args.reset;
782
+ /**
783
+ * Remove the pinned configuration for a flow-control key.
784
+ *
785
+ * After unpinning, the system resumes updating the configuration
786
+ * based on incoming messages.
787
+ */
788
+ async unpin(flowControlKey, options) {
789
+ await this.http.request({
790
+ method: "POST",
791
+ path: ["v2", "flowControl", flowControlKey, "unpin"],
792
+ query: {
793
+ parallelism: options.parallelism,
794
+ rate: options.rate
795
+ },
796
+ parseResponseAsJson: false
797
+ });
798
+ }
799
+ /**
800
+ * Reset the rate configuration state for a flow-control key.
801
+ *
802
+ * Clears the current rate count and immediately ends the current period.
803
+ * The current timestamp becomes the start of the new rate period.
804
+ */
805
+ async resetRate(flowControlKey) {
806
+ await this.http.request({
807
+ method: "POST",
808
+ path: ["v2", "flowControl", flowControlKey, "resetRate"],
809
+ parseResponseAsJson: false
810
+ });
171
811
  }
172
812
  };
173
813
 
@@ -268,7 +908,18 @@ var HttpClient = class {
268
908
  const url = new URL([request.baseUrl ?? this.baseUrl, ...request.path].join("/"));
269
909
  if (request.query) {
270
910
  for (const [key, value] of Object.entries(request.query)) {
271
- if (value !== void 0) {
911
+ if (value === void 0)
912
+ continue;
913
+ if (Array.isArray(value)) {
914
+ if (value.length === 0) {
915
+ throw new QstashEmptyArrayError(key);
916
+ }
917
+ for (const item of value) {
918
+ url.searchParams.append(key, item);
919
+ }
920
+ } else if (value instanceof Date) {
921
+ url.searchParams.set(key, value.getTime().toString());
922
+ } else {
272
923
  url.searchParams.set(key, value.toString());
273
924
  }
274
925
  }
@@ -419,371 +1070,151 @@ var Chat = class _Chat {
419
1070
  */
420
1071
  createThirdParty = async (request) => {
421
1072
  const { baseUrl, token, owner, organization } = request.provider;
422
- if (owner === "upstash")
423
- throw new Error("Upstash is not 3rd party provider!");
424
- delete request.provider;
425
- delete request.system;
426
- const analytics = request.analytics;
427
- delete request.analytics;
428
- const body = JSON.stringify(request);
429
- const isAnalyticsEnabled = analytics?.name && analytics.token;
430
- const analyticsConfig = analytics?.name && analytics.token ? setupAnalytics({ name: analytics.name, token: analytics.token }, token, baseUrl, owner) : { defaultHeaders: void 0, baseURL: baseUrl };
431
- const isStream = "stream" in request && request.stream;
432
- const headers = {
433
- "Content-Type": "application/json",
434
- Authorization: `Bearer ${token}`,
435
- ...organization ? {
436
- "OpenAI-Organization": organization
437
- } : {},
438
- ...isStream ? {
439
- Connection: "keep-alive",
440
- Accept: "text/event-stream",
441
- "Cache-Control": "no-cache"
442
- } : {},
443
- ...analyticsConfig.defaultHeaders
444
- };
445
- const response = await this.http[isStream ? "requestStream" : "request"]({
446
- path: isAnalyticsEnabled ? [] : ["v1", "chat", "completions"],
447
- method: "POST",
448
- headers,
449
- body,
450
- baseUrl: analyticsConfig.baseURL
451
- });
452
- return response;
453
- };
454
- // Helper method to get the authorization token
455
- getAuthorizationToken() {
456
- const authHeader = String(this.http.authorization);
457
- const match = /Bearer (.+)/.exec(authHeader);
458
- if (!match) {
459
- throw new Error("Invalid authorization header format");
460
- }
461
- return match[1];
462
- }
463
- /**
464
- * Calls the Upstash completions api given a PromptRequest.
465
- *
466
- * Returns a ChatCompletion or a stream of ChatCompletionChunks
467
- * if stream is enabled.
468
- *
469
- * @param request PromptRequest with system and user messages.
470
- * Note that system parameter shouldn't be passed in the case of
471
- * mistralai/Mistral-7B-Instruct-v0.2 model.
472
- * @returns Chat completion or stream
473
- */
474
- prompt = async (request) => {
475
- const chatRequest = _Chat.toChatRequest(request);
476
- return this.create(chatRequest);
477
- };
478
- };
479
-
480
- // src/client/messages.ts
481
- var Messages = class {
482
- http;
483
- constructor(http) {
484
- this.http = http;
485
- }
486
- /**
487
- * Get a message
488
- */
489
- async get(messageId) {
490
- const messagePayload = await this.http.request({
491
- method: "GET",
492
- path: ["v2", "messages", messageId]
493
- });
494
- const message = {
495
- ...messagePayload,
496
- urlGroup: messagePayload.topicName,
497
- ratePerSecond: "rate" in messagePayload ? messagePayload.rate : void 0
498
- };
499
- return message;
500
- }
501
- /**
502
- * Cancel a message
503
- */
504
- async delete(messageId) {
505
- return await this.http.request({
506
- method: "DELETE",
507
- path: ["v2", "messages", messageId],
508
- parseResponseAsJson: false
509
- });
510
- }
511
- async deleteMany(messageIds) {
512
- const result = await this.http.request({
513
- method: "DELETE",
514
- path: ["v2", "messages"],
515
- headers: { "Content-Type": "application/json" },
516
- body: JSON.stringify({ messageIds })
517
- });
518
- return result.cancelled;
519
- }
520
- async deleteAll() {
521
- const result = await this.http.request({
522
- method: "DELETE",
523
- path: ["v2", "messages"]
524
- });
525
- return result.cancelled;
526
- }
527
- };
528
-
529
- // src/client/api/base.ts
530
- var BaseProvider = class {
531
- baseUrl;
532
- token;
533
- owner;
534
- constructor(baseUrl, token, owner) {
535
- this.baseUrl = baseUrl;
536
- this.token = token;
537
- this.owner = owner;
538
- }
539
- getUrl() {
540
- return `${this.baseUrl}/${this.getRoute().join("/")}`;
541
- }
542
- };
543
-
544
- // src/client/api/llm.ts
545
- var LLMProvider = class extends BaseProvider {
546
- apiKind = "llm";
547
- organization;
548
- method = "POST";
549
- constructor(baseUrl, token, owner, organization) {
550
- super(baseUrl, token, owner);
551
- this.organization = organization;
552
- }
553
- getRoute() {
554
- return this.owner === "anthropic" ? ["v1", "messages"] : ["v1", "chat", "completions"];
555
- }
556
- getHeaders(options) {
557
- if (this.owner === "upstash" && !options.analytics) {
558
- return { "content-type": "application/json" };
559
- }
560
- const header = this.owner === "anthropic" ? "x-api-key" : "authorization";
561
- const headerValue = this.owner === "anthropic" ? this.token : `Bearer ${this.token}`;
562
- const headers = {
563
- [header]: headerValue,
564
- "content-type": "application/json"
565
- };
566
- if (this.owner === "openai" && this.organization) {
567
- headers["OpenAI-Organization"] = this.organization;
568
- }
569
- if (this.owner === "anthropic") {
570
- headers["anthropic-version"] = "2023-06-01";
571
- }
572
- return headers;
573
- }
574
- /**
575
- * Checks if callback exists and adds analytics in place if it's set.
576
- *
577
- * @param request
578
- * @param options
579
- */
580
- onFinish(providerInfo, options) {
581
- if (options.analytics) {
582
- return updateWithAnalytics(providerInfo, options.analytics);
583
- }
584
- return providerInfo;
585
- }
586
- };
587
- var upstash = () => {
588
- return new LLMProvider("https://qstash.upstash.io/llm", "", "upstash");
589
- };
590
-
591
- // src/client/api/utils.ts
592
- var getProviderInfo = (api, upstashToken) => {
593
- const { name, provider, ...parameters } = api;
594
- const finalProvider = provider ?? upstash();
595
- if (finalProvider.owner === "upstash" && !finalProvider.token) {
596
- finalProvider.token = upstashToken;
597
- }
598
- if (!finalProvider.baseUrl)
599
- throw new TypeError("baseUrl cannot be empty or undefined!");
600
- if (!finalProvider.token)
601
- throw new TypeError("token cannot be empty or undefined!");
602
- if (finalProvider.apiKind !== name) {
603
- throw new TypeError(
604
- `Unexpected api name. Expected '${finalProvider.apiKind}', received ${name}`
605
- );
606
- }
607
- const providerInfo = {
608
- url: finalProvider.getUrl(),
609
- baseUrl: finalProvider.baseUrl,
610
- route: finalProvider.getRoute(),
611
- appendHeaders: finalProvider.getHeaders(parameters),
612
- owner: finalProvider.owner,
613
- method: finalProvider.method
614
- };
615
- return finalProvider.onFinish(providerInfo, parameters);
616
- };
617
- var safeJoinHeaders = (headers, record) => {
618
- const joinedHeaders = new Headers(record);
619
- for (const [header, value] of headers.entries()) {
620
- joinedHeaders.set(header, value);
621
- }
622
- return joinedHeaders;
623
- };
624
- var processApi = (request, headers, upstashToken) => {
625
- if (!request.api) {
626
- request.headers = headers;
627
- return request;
628
- }
629
- const { url, appendHeaders, owner, method } = getProviderInfo(request.api, upstashToken);
630
- if (request.api.name === "llm") {
631
- const callback = request.callback;
632
- if (!callback) {
633
- throw new TypeError("Callback cannot be undefined when using LLM api.");
634
- }
635
- return {
636
- ...request,
637
- method: request.method ?? method,
638
- headers: safeJoinHeaders(headers, appendHeaders),
639
- ...owner === "upstash" && !request.api.analytics ? { api: { name: "llm" }, url: void 0, callback } : { url, api: void 0 }
640
- };
641
- } else {
642
- return {
643
- ...request,
644
- method: request.method ?? method,
645
- headers: safeJoinHeaders(headers, appendHeaders),
646
- url,
647
- api: void 0
1073
+ if (owner === "upstash")
1074
+ throw new Error("Upstash is not 3rd party provider!");
1075
+ delete request.provider;
1076
+ delete request.system;
1077
+ const analytics = request.analytics;
1078
+ delete request.analytics;
1079
+ const body = JSON.stringify(request);
1080
+ const isAnalyticsEnabled = analytics?.name && analytics.token;
1081
+ const analyticsConfig = analytics?.name && analytics.token ? setupAnalytics({ name: analytics.name, token: analytics.token }, token, baseUrl, owner) : { defaultHeaders: void 0, baseURL: baseUrl };
1082
+ const isStream = "stream" in request && request.stream;
1083
+ const headers = {
1084
+ "Content-Type": "application/json",
1085
+ Authorization: `Bearer ${token}`,
1086
+ ...organization ? {
1087
+ "OpenAI-Organization": organization
1088
+ } : {},
1089
+ ...isStream ? {
1090
+ Connection: "keep-alive",
1091
+ Accept: "text/event-stream",
1092
+ "Cache-Control": "no-cache"
1093
+ } : {},
1094
+ ...analyticsConfig.defaultHeaders
648
1095
  };
649
- }
650
- };
651
- function updateWithAnalytics(providerInfo, analytics) {
652
- switch (analytics.name) {
653
- case "helicone": {
654
- providerInfo.appendHeaders["Helicone-Auth"] = `Bearer ${analytics.token}`;
655
- if (providerInfo.owner === "upstash") {
656
- updateProviderInfo(providerInfo, "https://qstash.helicone.ai", [
657
- "llm",
658
- ...providerInfo.route
659
- ]);
660
- } else {
661
- providerInfo.appendHeaders["Helicone-Target-Url"] = providerInfo.baseUrl;
662
- updateProviderInfo(providerInfo, "https://gateway.helicone.ai", providerInfo.route);
663
- }
664
- return providerInfo;
665
- }
666
- default: {
667
- throw new Error("Unknown analytics provider");
1096
+ const response = await this.http[isStream ? "requestStream" : "request"]({
1097
+ path: isAnalyticsEnabled ? [] : ["v1", "chat", "completions"],
1098
+ method: "POST",
1099
+ headers,
1100
+ body,
1101
+ baseUrl: analyticsConfig.baseURL
1102
+ });
1103
+ return response;
1104
+ };
1105
+ // Helper method to get the authorization token
1106
+ getAuthorizationToken() {
1107
+ const authHeader = String(this.http.authorization);
1108
+ const match = /Bearer (.+)/.exec(authHeader);
1109
+ if (!match) {
1110
+ throw new Error("Invalid authorization header format");
668
1111
  }
1112
+ return match[1];
669
1113
  }
670
- }
671
- function updateProviderInfo(providerInfo, baseUrl, route) {
672
- providerInfo.baseUrl = baseUrl;
673
- providerInfo.route = route;
674
- providerInfo.url = `${baseUrl}/${route.join("/")}`;
675
- }
676
-
677
- // src/client/utils.ts
678
- var isIgnoredHeader = (header) => {
679
- const lowerCaseHeader = header.toLowerCase();
680
- return lowerCaseHeader.startsWith("content-type") || lowerCaseHeader.startsWith("upstash-");
1114
+ /**
1115
+ * Calls the Upstash completions api given a PromptRequest.
1116
+ *
1117
+ * Returns a ChatCompletion or a stream of ChatCompletionChunks
1118
+ * if stream is enabled.
1119
+ *
1120
+ * @param request PromptRequest with system and user messages.
1121
+ * Note that system parameter shouldn't be passed in the case of
1122
+ * mistralai/Mistral-7B-Instruct-v0.2 model.
1123
+ * @returns Chat completion or stream
1124
+ */
1125
+ prompt = async (request) => {
1126
+ const chatRequest = _Chat.toChatRequest(request);
1127
+ return this.create(chatRequest);
1128
+ };
681
1129
  };
682
- function prefixHeaders(headers) {
683
- const keysToBePrefixed = [...headers.keys()].filter((key) => !isIgnoredHeader(key));
684
- for (const key of keysToBePrefixed) {
685
- const value = headers.get(key);
686
- if (value !== null) {
687
- headers.set(`Upstash-Forward-${key}`, value);
688
- }
689
- headers.delete(key);
690
- }
691
- return headers;
692
- }
693
- function wrapWithGlobalHeaders(headers, globalHeaders, telemetryHeaders) {
694
- if (!globalHeaders) {
695
- return headers;
696
- }
697
- const finalHeaders = new Headers(globalHeaders);
698
- headers.forEach((value, key) => {
699
- finalHeaders.set(key, value);
700
- });
701
- telemetryHeaders?.forEach((value, key) => {
702
- if (!value)
703
- return;
704
- finalHeaders.append(key, value);
705
- });
706
- return finalHeaders;
707
- }
708
- function processHeaders(request) {
709
- const headers = prefixHeaders(new Headers(request.headers));
710
- headers.set("Upstash-Method", request.method ?? "POST");
711
- if (request.delay !== void 0) {
712
- if (typeof request.delay === "string") {
713
- headers.set("Upstash-Delay", request.delay);
714
- } else {
715
- headers.set("Upstash-Delay", `${request.delay.toFixed(0)}s`);
716
- }
717
- }
718
- if (request.notBefore !== void 0) {
719
- headers.set("Upstash-Not-Before", request.notBefore.toFixed(0));
720
- }
721
- if (request.deduplicationId !== void 0) {
722
- headers.set("Upstash-Deduplication-Id", request.deduplicationId);
723
- }
724
- if (request.contentBasedDeduplication) {
725
- headers.set("Upstash-Content-Based-Deduplication", "true");
726
- }
727
- if (request.retries !== void 0) {
728
- headers.set("Upstash-Retries", request.retries.toFixed(0));
729
- }
730
- if (request.retryDelay !== void 0) {
731
- headers.set("Upstash-Retry-Delay", request.retryDelay);
732
- }
733
- if (request.callback !== void 0) {
734
- headers.set("Upstash-Callback", request.callback);
1130
+
1131
+ // src/client/messages.ts
1132
+ var Messages = class {
1133
+ http;
1134
+ constructor(http) {
1135
+ this.http = http;
735
1136
  }
736
- if (request.failureCallback !== void 0) {
737
- headers.set("Upstash-Failure-Callback", request.failureCallback);
1137
+ /**
1138
+ * Get a message
1139
+ */
1140
+ async get(messageId) {
1141
+ const messagePayload = await this.http.request({
1142
+ method: "GET",
1143
+ path: ["v2", "messages", messageId]
1144
+ });
1145
+ const message = {
1146
+ ...messagePayload,
1147
+ urlGroup: messagePayload.topicName,
1148
+ ratePerSecond: "rate" in messagePayload ? messagePayload.rate : void 0
1149
+ };
1150
+ return message;
738
1151
  }
739
- if (request.timeout !== void 0) {
740
- if (typeof request.timeout === "string") {
741
- headers.set("Upstash-Timeout", request.timeout);
742
- } else {
743
- headers.set("Upstash-Timeout", `${request.timeout}s`);
1152
+ /**
1153
+ * Cancel messages.
1154
+ *
1155
+ * Can be called with:
1156
+ * - A single messageId: `cancel("id")`
1157
+ * - An array of messageIds: `cancel(["id1", "id2"])`
1158
+ * - A filter object: `cancel({ filter: { flowControlKey: "key", label: "label" } })`
1159
+ * - All messages: `cancel({ all: true })`
1160
+ *
1161
+ * Pass `count` to limit the number of messages processed per call (defaults to 100).
1162
+ * Call in a loop until `cancelled` is 0:
1163
+ *
1164
+ * ```ts
1165
+ * let cancelled: number;
1166
+ * do {
1167
+ * const result = await messages.cancel({ all: true, count: 100 });
1168
+ * cancelled = result.cancelled;
1169
+ * } while (cancelled > 0);
1170
+ * ```
1171
+ */
1172
+ async cancel(request) {
1173
+ if (typeof request === "string") {
1174
+ return await this.http.request({
1175
+ method: "DELETE",
1176
+ path: ["v2", "messages", request]
1177
+ });
744
1178
  }
1179
+ if (Array.isArray(request) && request.length === 0)
1180
+ return { cancelled: 0 };
1181
+ const filters = Array.isArray(request) ? { messageIds: request } : request;
1182
+ return await this.http.request({
1183
+ method: "DELETE",
1184
+ path: ["v2", "messages"],
1185
+ query: buildBulkActionFilterPayload(filters)
1186
+ });
745
1187
  }
746
- if (request.flowControl?.key) {
747
- const parallelism = request.flowControl.parallelism?.toString();
748
- const rate = (request.flowControl.rate ?? request.flowControl.ratePerSecond)?.toString();
749
- const period = typeof request.flowControl.period === "number" ? `${request.flowControl.period}s` : request.flowControl.period;
750
- const controlValue = [
751
- parallelism ? `parallelism=${parallelism}` : void 0,
752
- rate ? `rate=${rate}` : void 0,
753
- period ? `period=${period}` : void 0
754
- ].filter(Boolean);
755
- if (controlValue.length === 0) {
756
- throw new QstashError("Provide at least one of parallelism or ratePerSecond for flowControl");
757
- }
758
- headers.set("Upstash-Flow-Control-Key", request.flowControl.key);
759
- headers.set("Upstash-Flow-Control-Value", controlValue.join(", "));
1188
+ /**
1189
+ * Delete a message.
1190
+ *
1191
+ * @deprecated Use `cancel(messageId: string)` instead
1192
+ */
1193
+ async delete(messageId) {
1194
+ await this.http.request({
1195
+ method: "DELETE",
1196
+ path: ["v2", "messages", messageId],
1197
+ parseResponseAsJson: false
1198
+ });
760
1199
  }
761
- if (request.label !== void 0) {
762
- headers.set("Upstash-Label", request.label);
1200
+ /**
1201
+ * Cancel multiple messages by their messageIds.
1202
+ *
1203
+ * @deprecated Use `cancel(messageIds: string[])` instead
1204
+ */
1205
+ async deleteMany(messageIds) {
1206
+ const result = await this.cancel(messageIds);
1207
+ return result.cancelled;
763
1208
  }
764
- return headers;
765
- }
766
- function getRequestPath(request) {
767
- const nonApiPath = request.url ?? request.urlGroup ?? request.topic;
768
- if (nonApiPath)
769
- return nonApiPath;
770
- if (request.api?.name === "llm")
771
- return `api/llm`;
772
- if (request.api?.name === "email") {
773
- const providerInfo = getProviderInfo(request.api, "not-needed");
774
- return providerInfo.baseUrl;
1209
+ /**
1210
+ * Cancel all messages
1211
+ * @deprecated Use `cancel({all: true})` to cancel all
1212
+ */
1213
+ async deleteAll() {
1214
+ const result = await this.cancel({ all: true });
1215
+ return result.cancelled;
775
1216
  }
776
- throw new QstashError(`Failed to infer request path for ${JSON.stringify(request)}`);
777
- }
778
- function getRuntime() {
779
- if (typeof process === "object" && typeof process.versions == "object" && process.versions.bun)
780
- return `bun@${process.versions.bun}`;
781
- if (typeof EdgeRuntime === "string")
782
- return "edge-light";
783
- else if (typeof process === "object" && typeof process.version === "string")
784
- return `node@${process.version}`;
785
- return "";
786
- }
1217
+ };
787
1218
 
788
1219
  // src/client/queue.ts
789
1220
  var Queue = class {
@@ -985,6 +1416,24 @@ var Schedules = class {
985
1416
  if (request.label !== void 0) {
986
1417
  headers.set("Upstash-Label", request.label);
987
1418
  }
1419
+ if (request.redact !== void 0) {
1420
+ const redactParts = [];
1421
+ if (request.redact.body) {
1422
+ redactParts.push("body");
1423
+ }
1424
+ if (request.redact.header !== void 0) {
1425
+ if (request.redact.header === true) {
1426
+ redactParts.push("header");
1427
+ } else if (Array.isArray(request.redact.header) && request.redact.header.length > 0) {
1428
+ for (const headerName of request.redact.header) {
1429
+ redactParts.push(`header[${headerName}]`);
1430
+ }
1431
+ }
1432
+ }
1433
+ if (redactParts.length > 0) {
1434
+ headers.set("Upstash-Redact-Fields", redactParts.join(","));
1435
+ }
1436
+ }
988
1437
  return await this.http.request({
989
1438
  method: "POST",
990
1439
  headers: wrapWithGlobalHeaders(headers, this.http.headers, this.http.telemetryHeaders),
@@ -1114,19 +1563,15 @@ var UrlGroups = class {
1114
1563
  };
1115
1564
 
1116
1565
  // version.ts
1117
- var VERSION = "v2.8.4";
1566
+ var VERSION = "2.10.1";
1118
1567
 
1119
1568
  // src/client/client.ts
1120
1569
  var Client = class {
1121
1570
  http;
1122
1571
  token;
1123
1572
  constructor(config) {
1124
- const environment = typeof process === "undefined" ? {} : process.env;
1125
- let baseUrl = (config?.baseUrl ?? environment.QSTASH_URL ?? "https://qstash.upstash.io").replace(/\/$/, "");
1126
- if (baseUrl === "https://qstash.upstash.io/v2/publish") {
1127
- baseUrl = "https://qstash.upstash.io";
1128
- }
1129
- const token = config?.token ?? environment.QSTASH_TOKEN;
1573
+ const environment = getSafeEnvironment();
1574
+ const { baseUrl, token } = getClientCredentials({ environment, config });
1130
1575
  const enableTelemetry = environment.UPSTASH_DISABLE_TELEMETRY ? false : config?.enableTelemetry ?? true;
1131
1576
  const isCloudflare = typeof caches !== "undefined" && "default" in caches;
1132
1577
  const telemetryHeaders = new Headers(
@@ -1145,11 +1590,6 @@ var Client = class {
1145
1590
  //@ts-expect-error caused by undici and bunjs type overlap
1146
1591
  telemetryHeaders
1147
1592
  });
1148
- if (!token) {
1149
- console.warn(
1150
- "[Upstash QStash] client token is not set. Either pass a token or set QSTASH_TOKEN env variable."
1151
- );
1152
- }
1153
1593
  this.token = token;
1154
1594
  }
1155
1595
  /**
@@ -1194,6 +1634,14 @@ var Client = class {
1194
1634
  get schedules() {
1195
1635
  return new Schedules(this.http);
1196
1636
  }
1637
+ /**
1638
+ * Access the flow control API.
1639
+ *
1640
+ * List, get, or reset flow controls.
1641
+ */
1642
+ get flowControl() {
1643
+ return new FlowControlApi(this.http);
1644
+ }
1197
1645
  /**
1198
1646
  * Access the workflow API.
1199
1647
  *
@@ -1318,39 +1766,18 @@ var Client = class {
1318
1766
  * }
1319
1767
  * ```
1320
1768
  */
1321
- async logs(request) {
1322
- const query = {};
1323
- if (typeof request?.cursor === "number" && request.cursor > 0) {
1324
- query.cursor = request.cursor.toString();
1325
- } else if (typeof request?.cursor === "string" && request.cursor !== "") {
1326
- query.cursor = request.cursor;
1327
- }
1328
- for (const [key, value] of Object.entries(request?.filter ?? {})) {
1329
- if (typeof value === "number" && value < 0) {
1330
- continue;
1331
- }
1332
- if (key === "urlGroup") {
1333
- query.topicName = value.toString();
1334
- } else if (typeof value !== "undefined") {
1335
- query[key] = value.toString();
1336
- }
1337
- }
1769
+ async logs(request = {}) {
1770
+ const query = {
1771
+ count: request.count,
1772
+ ..."messageIds" in request ? { messageIds: request.messageIds } : { ...renameUrlGroup(request.filter ?? {}), cursor: request.cursor }
1773
+ };
1338
1774
  const responsePayload = await this.http.request({
1339
1775
  path: ["v2", "events"],
1340
1776
  method: "GET",
1341
1777
  query
1342
1778
  });
1343
- const logs = responsePayload.events.map((event) => {
1344
- return {
1345
- ...event,
1346
- urlGroup: event.topicName
1347
- };
1348
- });
1349
- return {
1350
- cursor: responsePayload.cursor,
1351
- logs,
1352
- events: logs
1353
- };
1779
+ const logs = responsePayload.events.map((event) => ({ ...event, urlGroup: event.topicName }));
1780
+ return { cursor: responsePayload.cursor, logs, events: logs };
1354
1781
  }
1355
1782
  /**
1356
1783
  * @deprecated Will be removed in the next major release. Use the `logs` method instead.
@@ -1400,4 +1827,4 @@ var Workflow = class {
1400
1827
  }
1401
1828
  };
1402
1829
 
1403
- export { BaseProvider, Chat, Client, Messages, QstashChatRatelimitError, QstashDailyRatelimitError, QstashError, QstashRatelimitError, Receiver, Schedules, SignatureError, UrlGroups, Workflow, setupAnalytics, upstash };
1830
+ export { BaseProvider, Chat, Client, FlowControlApi, Messages, QstashChatRatelimitError, QstashDailyRatelimitError, QstashEmptyArrayError, QstashError, QstashRatelimitError, Receiver, Schedules, SignatureError, UrlGroups, Workflow, setupAnalytics, upstash };