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