@upstash/qstash 2.8.4 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{chunk-EZZS7N6P.mjs → chunk-H5OAU75L.mjs} +6 -9
- package/{chunk-5PQP3YLP.mjs → chunk-M7SEEFAC.mjs} +1 -1
- package/{chunk-RQPZUJXG.mjs → chunk-QYBCXZKB.mjs} +640 -495
- package/{client-DKNfczbM.d.ts → client-BVG9vt90.d.mts} +17 -5
- package/{client-DKNfczbM.d.mts → client-BVG9vt90.d.ts} +17 -5
- package/cloudflare.d.mts +1 -1
- package/cloudflare.d.ts +1 -1
- package/cloudflare.js +812 -667
- package/cloudflare.mjs +1 -1
- package/h3.d.mts +1 -1
- package/h3.d.ts +1 -1
- package/h3.js +825 -683
- package/h3.mjs +3 -3
- package/hono.d.mts +1 -1
- package/hono.d.ts +1 -1
- package/hono.js +812 -667
- package/hono.mjs +1 -1
- package/index.d.mts +2 -2
- package/index.d.ts +2 -2
- package/index.js +637 -492
- package/index.mjs +2 -2
- package/nextjs.d.mts +1 -1
- package/nextjs.d.ts +1 -1
- package/nextjs.js +647 -511
- package/nextjs.mjs +16 -25
- package/nuxt.js +110 -12
- package/nuxt.mjs +3 -3
- package/package.json +1 -1
- package/solidjs.d.mts +1 -1
- package/solidjs.d.ts +1 -1
- package/solidjs.js +828 -682
- package/solidjs.mjs +9 -8
- package/svelte.d.mts +4 -4
- package/svelte.d.ts +4 -4
- package/svelte.js +830 -684
- package/svelte.mjs +11 -10
- package/workflow.d.mts +1 -1
- package/workflow.d.ts +1 -1
- package/workflow.js +812 -667
- package/workflow.mjs +1 -1
package/hono.js
CHANGED
|
@@ -37,120 +37,154 @@ module.exports = __toCommonJS(hono_exports);
|
|
|
37
37
|
// src/receiver.ts
|
|
38
38
|
var jose = __toESM(require("jose"));
|
|
39
39
|
var import_crypto_js = __toESM(require("crypto-js"));
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
|
|
41
|
+
// src/client/api/base.ts
|
|
42
|
+
var BaseProvider = class {
|
|
43
|
+
baseUrl;
|
|
44
|
+
token;
|
|
45
|
+
owner;
|
|
46
|
+
constructor(baseUrl, token, owner) {
|
|
47
|
+
this.baseUrl = baseUrl;
|
|
48
|
+
this.token = token;
|
|
49
|
+
this.owner = owner;
|
|
50
|
+
}
|
|
51
|
+
getUrl() {
|
|
52
|
+
return `${this.baseUrl}/${this.getRoute().join("/")}`;
|
|
44
53
|
}
|
|
45
54
|
};
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
|
|
56
|
+
// src/client/api/llm.ts
|
|
57
|
+
var LLMProvider = class extends BaseProvider {
|
|
58
|
+
apiKind = "llm";
|
|
59
|
+
organization;
|
|
60
|
+
method = "POST";
|
|
61
|
+
constructor(baseUrl, token, owner, organization) {
|
|
62
|
+
super(baseUrl, token, owner);
|
|
63
|
+
this.organization = organization;
|
|
52
64
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
*
|
|
60
|
-
* If that fails, the signature is invalid and a `SignatureError` is thrown.
|
|
61
|
-
*/
|
|
62
|
-
async verify(request) {
|
|
63
|
-
let payload;
|
|
64
|
-
try {
|
|
65
|
-
payload = await this.verifyWithKey(this.currentSigningKey, request);
|
|
66
|
-
} catch {
|
|
67
|
-
payload = await this.verifyWithKey(this.nextSigningKey, request);
|
|
65
|
+
getRoute() {
|
|
66
|
+
return this.owner === "anthropic" ? ["v1", "messages"] : ["v1", "chat", "completions"];
|
|
67
|
+
}
|
|
68
|
+
getHeaders(options) {
|
|
69
|
+
if (this.owner === "upstash" && !options.analytics) {
|
|
70
|
+
return { "content-type": "application/json" };
|
|
68
71
|
}
|
|
69
|
-
this.
|
|
70
|
-
|
|
72
|
+
const header = this.owner === "anthropic" ? "x-api-key" : "authorization";
|
|
73
|
+
const headerValue = this.owner === "anthropic" ? this.token : `Bearer ${this.token}`;
|
|
74
|
+
const headers = {
|
|
75
|
+
[header]: headerValue,
|
|
76
|
+
"content-type": "application/json"
|
|
77
|
+
};
|
|
78
|
+
if (this.owner === "openai" && this.organization) {
|
|
79
|
+
headers["OpenAI-Organization"] = this.organization;
|
|
80
|
+
}
|
|
81
|
+
if (this.owner === "anthropic") {
|
|
82
|
+
headers["anthropic-version"] = "2023-06-01";
|
|
83
|
+
}
|
|
84
|
+
return headers;
|
|
71
85
|
}
|
|
72
86
|
/**
|
|
73
|
-
*
|
|
87
|
+
* Checks if callback exists and adds analytics in place if it's set.
|
|
88
|
+
*
|
|
89
|
+
* @param request
|
|
90
|
+
* @param options
|
|
74
91
|
*/
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
clockTolerance: request.clockTolerance
|
|
79
|
-
}).catch((error) => {
|
|
80
|
-
throw new SignatureError(error.message);
|
|
81
|
-
});
|
|
82
|
-
return jwt.payload;
|
|
83
|
-
}
|
|
84
|
-
verifyBodyAndUrl(payload, request) {
|
|
85
|
-
const p = payload;
|
|
86
|
-
if (request.url !== void 0 && p.sub !== request.url) {
|
|
87
|
-
throw new SignatureError(`invalid subject: ${p.sub}, want: ${request.url}`);
|
|
88
|
-
}
|
|
89
|
-
const bodyHash = import_crypto_js.default.SHA256(request.body).toString(import_crypto_js.default.enc.Base64url);
|
|
90
|
-
const padding = new RegExp(/=+$/);
|
|
91
|
-
if (p.body.replace(padding, "") !== bodyHash.replace(padding, "")) {
|
|
92
|
-
throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`);
|
|
92
|
+
onFinish(providerInfo, options) {
|
|
93
|
+
if (options.analytics) {
|
|
94
|
+
return updateWithAnalytics(providerInfo, options.analytics);
|
|
93
95
|
}
|
|
96
|
+
return providerInfo;
|
|
94
97
|
}
|
|
95
98
|
};
|
|
99
|
+
var upstash = () => {
|
|
100
|
+
return new LLMProvider("https://qstash.upstash.io/llm", "", "upstash");
|
|
101
|
+
};
|
|
96
102
|
|
|
97
|
-
// src/client/
|
|
98
|
-
var
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
103
|
+
// src/client/api/utils.ts
|
|
104
|
+
var getProviderInfo = (api, upstashToken) => {
|
|
105
|
+
const { name, provider, ...parameters } = api;
|
|
106
|
+
const finalProvider = provider ?? upstash();
|
|
107
|
+
if (finalProvider.owner === "upstash" && !finalProvider.token) {
|
|
108
|
+
finalProvider.token = upstashToken;
|
|
102
109
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
if (!finalProvider.baseUrl)
|
|
111
|
+
throw new TypeError("baseUrl cannot be empty or undefined!");
|
|
112
|
+
if (!finalProvider.token)
|
|
113
|
+
throw new TypeError("token cannot be empty or undefined!");
|
|
114
|
+
if (finalProvider.apiKind !== name) {
|
|
115
|
+
throw new TypeError(
|
|
116
|
+
`Unexpected api name. Expected '${finalProvider.apiKind}', received ${name}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const providerInfo = {
|
|
120
|
+
url: finalProvider.getUrl(),
|
|
121
|
+
baseUrl: finalProvider.baseUrl,
|
|
122
|
+
route: finalProvider.getRoute(),
|
|
123
|
+
appendHeaders: finalProvider.getHeaders(parameters),
|
|
124
|
+
owner: finalProvider.owner,
|
|
125
|
+
method: finalProvider.method
|
|
126
|
+
};
|
|
127
|
+
return finalProvider.onFinish(providerInfo, parameters);
|
|
128
|
+
};
|
|
129
|
+
var safeJoinHeaders = (headers, record) => {
|
|
130
|
+
const joinedHeaders = new Headers(record);
|
|
131
|
+
for (const [header, value] of headers.entries()) {
|
|
132
|
+
joinedHeaders.set(header, value);
|
|
133
|
+
}
|
|
134
|
+
return joinedHeaders;
|
|
135
|
+
};
|
|
136
|
+
var processApi = (request, headers, upstashToken) => {
|
|
137
|
+
if (!request.api) {
|
|
138
|
+
request.headers = headers;
|
|
139
|
+
return request;
|
|
140
|
+
}
|
|
141
|
+
const { url, appendHeaders, owner, method } = getProviderInfo(request.api, upstashToken);
|
|
142
|
+
if (request.api.name === "llm") {
|
|
143
|
+
const callback = request.callback;
|
|
144
|
+
if (!callback) {
|
|
145
|
+
throw new TypeError("Callback cannot be undefined when using LLM api.");
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
...request,
|
|
149
|
+
method: request.method ?? method,
|
|
150
|
+
headers: safeJoinHeaders(headers, appendHeaders),
|
|
151
|
+
...owner === "upstash" && !request.api.analytics ? { api: { name: "llm" }, url: void 0, callback } : { url, api: void 0 }
|
|
110
152
|
};
|
|
111
|
-
|
|
112
|
-
method: "GET",
|
|
113
|
-
path: ["v2", "dlq"],
|
|
114
|
-
query: {
|
|
115
|
-
cursor: options?.cursor,
|
|
116
|
-
count: options?.count,
|
|
117
|
-
...filterPayload
|
|
118
|
-
}
|
|
119
|
-
});
|
|
153
|
+
} else {
|
|
120
154
|
return {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
};
|
|
127
|
-
}),
|
|
128
|
-
cursor: messagesPayload.cursor
|
|
155
|
+
...request,
|
|
156
|
+
method: request.method ?? method,
|
|
157
|
+
headers: safeJoinHeaders(headers, appendHeaders),
|
|
158
|
+
url,
|
|
159
|
+
api: void 0
|
|
129
160
|
};
|
|
130
161
|
}
|
|
131
|
-
/**
|
|
132
|
-
* Remove a message from the dlq using it's `dlqId`
|
|
133
|
-
*/
|
|
134
|
-
async delete(dlqMessageId) {
|
|
135
|
-
return await this.http.request({
|
|
136
|
-
method: "DELETE",
|
|
137
|
-
path: ["v2", "dlq", dlqMessageId],
|
|
138
|
-
parseResponseAsJson: false
|
|
139
|
-
// there is no response
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Remove multiple messages from the dlq using their `dlqId`s
|
|
144
|
-
*/
|
|
145
|
-
async deleteMany(request) {
|
|
146
|
-
return await this.http.request({
|
|
147
|
-
method: "DELETE",
|
|
148
|
-
path: ["v2", "dlq"],
|
|
149
|
-
headers: { "Content-Type": "application/json" },
|
|
150
|
-
body: JSON.stringify({ dlqIds: request.dlqIds })
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
162
|
};
|
|
163
|
+
function updateWithAnalytics(providerInfo, analytics) {
|
|
164
|
+
switch (analytics.name) {
|
|
165
|
+
case "helicone": {
|
|
166
|
+
providerInfo.appendHeaders["Helicone-Auth"] = `Bearer ${analytics.token}`;
|
|
167
|
+
if (providerInfo.owner === "upstash") {
|
|
168
|
+
updateProviderInfo(providerInfo, "https://qstash.helicone.ai", [
|
|
169
|
+
"llm",
|
|
170
|
+
...providerInfo.route
|
|
171
|
+
]);
|
|
172
|
+
} else {
|
|
173
|
+
providerInfo.appendHeaders["Helicone-Target-Url"] = providerInfo.baseUrl;
|
|
174
|
+
updateProviderInfo(providerInfo, "https://gateway.helicone.ai", providerInfo.route);
|
|
175
|
+
}
|
|
176
|
+
return providerInfo;
|
|
177
|
+
}
|
|
178
|
+
default: {
|
|
179
|
+
throw new Error("Unknown analytics provider");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function updateProviderInfo(providerInfo, baseUrl, route) {
|
|
184
|
+
providerInfo.baseUrl = baseUrl;
|
|
185
|
+
providerInfo.route = route;
|
|
186
|
+
providerInfo.url = `${baseUrl}/${route.join("/")}`;
|
|
187
|
+
}
|
|
154
188
|
|
|
155
189
|
// src/client/error.ts
|
|
156
190
|
var RATELIMIT_STATUS = 429;
|
|
@@ -232,644 +266,764 @@ var formatWorkflowError = (error) => {
|
|
|
232
266
|
};
|
|
233
267
|
};
|
|
234
268
|
|
|
235
|
-
// src/client/
|
|
236
|
-
var
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
headers;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
this.retry = // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
247
|
-
typeof config.retry === "boolean" && !config.retry ? {
|
|
248
|
-
attempts: 1,
|
|
249
|
-
backoff: () => 0
|
|
250
|
-
} : {
|
|
251
|
-
attempts: config.retry?.retries ?? 5,
|
|
252
|
-
backoff: config.retry?.backoff ?? ((retryCount) => Math.exp(retryCount) * 50)
|
|
253
|
-
};
|
|
254
|
-
this.headers = config.headers;
|
|
255
|
-
this.telemetryHeaders = config.telemetryHeaders;
|
|
256
|
-
}
|
|
257
|
-
async request(request) {
|
|
258
|
-
const { response } = await this.requestWithBackoff(request);
|
|
259
|
-
if (request.parseResponseAsJson === false) {
|
|
260
|
-
return void 0;
|
|
261
|
-
}
|
|
262
|
-
return await response.json();
|
|
263
|
-
}
|
|
264
|
-
async *requestStream(request) {
|
|
265
|
-
const { response } = await this.requestWithBackoff(request);
|
|
266
|
-
if (!response.body) {
|
|
267
|
-
throw new Error("No response body");
|
|
269
|
+
// src/client/utils.ts
|
|
270
|
+
var isIgnoredHeader = (header) => {
|
|
271
|
+
const lowerCaseHeader = header.toLowerCase();
|
|
272
|
+
return lowerCaseHeader.startsWith("content-type") || lowerCaseHeader.startsWith("upstash-");
|
|
273
|
+
};
|
|
274
|
+
function prefixHeaders(headers) {
|
|
275
|
+
const keysToBePrefixed = [...headers.keys()].filter((key) => !isIgnoredHeader(key));
|
|
276
|
+
for (const key of keysToBePrefixed) {
|
|
277
|
+
const value = headers.get(key);
|
|
278
|
+
if (value !== null) {
|
|
279
|
+
headers.set(`Upstash-Forward-${key}`, value);
|
|
268
280
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
281
|
+
headers.delete(key);
|
|
282
|
+
}
|
|
283
|
+
return headers;
|
|
284
|
+
}
|
|
285
|
+
function wrapWithGlobalHeaders(headers, globalHeaders, telemetryHeaders) {
|
|
286
|
+
if (!globalHeaders) {
|
|
287
|
+
return headers;
|
|
288
|
+
}
|
|
289
|
+
const finalHeaders = new Headers(globalHeaders);
|
|
290
|
+
headers.forEach((value, key) => {
|
|
291
|
+
finalHeaders.set(key, value);
|
|
292
|
+
});
|
|
293
|
+
telemetryHeaders?.forEach((value, key) => {
|
|
294
|
+
if (!value)
|
|
295
|
+
return;
|
|
296
|
+
finalHeaders.append(key, value);
|
|
297
|
+
});
|
|
298
|
+
return finalHeaders;
|
|
299
|
+
}
|
|
300
|
+
function processHeaders(request) {
|
|
301
|
+
const headers = prefixHeaders(new Headers(request.headers));
|
|
302
|
+
headers.set("Upstash-Method", request.method ?? "POST");
|
|
303
|
+
if (request.delay !== void 0) {
|
|
304
|
+
if (typeof request.delay === "string") {
|
|
305
|
+
headers.set("Upstash-Delay", request.delay);
|
|
306
|
+
} else {
|
|
307
|
+
headers.set("Upstash-Delay", `${request.delay.toFixed(0)}s`);
|
|
292
308
|
}
|
|
293
309
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
310
|
+
if (request.notBefore !== void 0) {
|
|
311
|
+
headers.set("Upstash-Not-Before", request.notBefore.toFixed(0));
|
|
312
|
+
}
|
|
313
|
+
if (request.deduplicationId !== void 0) {
|
|
314
|
+
headers.set("Upstash-Deduplication-Id", request.deduplicationId);
|
|
315
|
+
}
|
|
316
|
+
if (request.contentBasedDeduplication) {
|
|
317
|
+
headers.set("Upstash-Content-Based-Deduplication", "true");
|
|
318
|
+
}
|
|
319
|
+
if (request.retries !== void 0) {
|
|
320
|
+
headers.set("Upstash-Retries", request.retries.toFixed(0));
|
|
321
|
+
}
|
|
322
|
+
if (request.retryDelay !== void 0) {
|
|
323
|
+
headers.set("Upstash-Retry-Delay", request.retryDelay);
|
|
324
|
+
}
|
|
325
|
+
if (request.callback !== void 0) {
|
|
326
|
+
headers.set("Upstash-Callback", request.callback);
|
|
327
|
+
}
|
|
328
|
+
if (request.failureCallback !== void 0) {
|
|
329
|
+
headers.set("Upstash-Failure-Callback", request.failureCallback);
|
|
330
|
+
}
|
|
331
|
+
if (request.timeout !== void 0) {
|
|
332
|
+
if (typeof request.timeout === "string") {
|
|
333
|
+
headers.set("Upstash-Timeout", request.timeout);
|
|
334
|
+
} else {
|
|
335
|
+
headers.set("Upstash-Timeout", `${request.timeout}s`);
|
|
308
336
|
}
|
|
309
|
-
|
|
310
|
-
|
|
337
|
+
}
|
|
338
|
+
if (request.flowControl?.key) {
|
|
339
|
+
const parallelism = request.flowControl.parallelism?.toString();
|
|
340
|
+
const rate = (request.flowControl.rate ?? request.flowControl.ratePerSecond)?.toString();
|
|
341
|
+
const period = typeof request.flowControl.period === "number" ? `${request.flowControl.period}s` : request.flowControl.period;
|
|
342
|
+
const controlValue = [
|
|
343
|
+
parallelism ? `parallelism=${parallelism}` : void 0,
|
|
344
|
+
rate ? `rate=${rate}` : void 0,
|
|
345
|
+
period ? `period=${period}` : void 0
|
|
346
|
+
].filter(Boolean);
|
|
347
|
+
if (controlValue.length === 0) {
|
|
348
|
+
throw new QstashError("Provide at least one of parallelism or ratePerSecond for flowControl");
|
|
311
349
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
350
|
+
headers.set("Upstash-Flow-Control-Key", request.flowControl.key);
|
|
351
|
+
headers.set("Upstash-Flow-Control-Value", controlValue.join(", "));
|
|
352
|
+
}
|
|
353
|
+
if (request.label !== void 0) {
|
|
354
|
+
headers.set("Upstash-Label", request.label);
|
|
355
|
+
}
|
|
356
|
+
return headers;
|
|
357
|
+
}
|
|
358
|
+
function getRequestPath(request) {
|
|
359
|
+
const nonApiPath = request.url ?? request.urlGroup ?? request.topic;
|
|
360
|
+
if (nonApiPath)
|
|
361
|
+
return nonApiPath;
|
|
362
|
+
if (request.api?.name === "llm")
|
|
363
|
+
return `api/llm`;
|
|
364
|
+
if (request.api?.name === "email") {
|
|
365
|
+
const providerInfo = getProviderInfo(request.api, "not-needed");
|
|
366
|
+
return providerInfo.baseUrl;
|
|
367
|
+
}
|
|
368
|
+
throw new QstashError(`Failed to infer request path for ${JSON.stringify(request)}`);
|
|
369
|
+
}
|
|
370
|
+
var NANOID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
|
371
|
+
var NANOID_LENGTH = 21;
|
|
372
|
+
function nanoid() {
|
|
373
|
+
return [...crypto.getRandomValues(new Uint8Array(NANOID_LENGTH))].map((x) => NANOID_CHARS[x % NANOID_CHARS.length]).join("");
|
|
374
|
+
}
|
|
375
|
+
function decodeBase64(base64) {
|
|
376
|
+
try {
|
|
377
|
+
const binString = atob(base64);
|
|
378
|
+
const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
|
379
|
+
return new TextDecoder().decode(intArray);
|
|
380
|
+
} catch (error) {
|
|
381
|
+
try {
|
|
382
|
+
const result = atob(base64);
|
|
383
|
+
console.warn(
|
|
384
|
+
`Upstash QStash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
|
|
385
|
+
);
|
|
386
|
+
return result;
|
|
387
|
+
} catch (error2) {
|
|
388
|
+
console.warn(
|
|
389
|
+
`Upstash QStash: Failed to decode base64 "${base64}" with atob. Returning it as it is. ${error2}`
|
|
390
|
+
);
|
|
391
|
+
return base64;
|
|
322
392
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function getRuntime() {
|
|
396
|
+
if (typeof process === "object" && typeof process.versions == "object" && process.versions.bun)
|
|
397
|
+
return `bun@${process.versions.bun}`;
|
|
398
|
+
if (typeof EdgeRuntime === "string")
|
|
399
|
+
return "edge-light";
|
|
400
|
+
else if (typeof process === "object" && typeof process.version === "string")
|
|
401
|
+
return `node@${process.version}`;
|
|
402
|
+
return "";
|
|
403
|
+
}
|
|
404
|
+
function getSafeEnvironment() {
|
|
405
|
+
return typeof process === "undefined" ? {} : process.env;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/client/multi-region/utils.ts
|
|
409
|
+
var VALID_REGIONS = ["EU_CENTRAL_1", "US_EAST_1"];
|
|
410
|
+
var DEFAULT_QSTASH_URL = "https://qstash.upstash.io";
|
|
411
|
+
var getRegionFromEnvironment = (environment) => {
|
|
412
|
+
const region = environment.QSTASH_REGION;
|
|
413
|
+
return normalizeRegionHeader(region);
|
|
414
|
+
};
|
|
415
|
+
function readEnvironmentVariables(environmentVariables, environment, region) {
|
|
416
|
+
const result = {};
|
|
417
|
+
for (const variable of environmentVariables) {
|
|
418
|
+
const key = region ? `${region}_${variable}` : variable;
|
|
419
|
+
result[variable] = environment[key];
|
|
420
|
+
}
|
|
421
|
+
return result;
|
|
422
|
+
}
|
|
423
|
+
function readClientEnvironmentVariables(environment, region) {
|
|
424
|
+
return readEnvironmentVariables(["QSTASH_URL", "QSTASH_TOKEN"], environment, region);
|
|
425
|
+
}
|
|
426
|
+
function readReceiverEnvironmentVariables(environment, region) {
|
|
427
|
+
return readEnvironmentVariables(
|
|
428
|
+
["QSTASH_CURRENT_SIGNING_KEY", "QSTASH_NEXT_SIGNING_KEY"],
|
|
429
|
+
environment,
|
|
430
|
+
region
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
function normalizeRegionHeader(region) {
|
|
434
|
+
if (!region) {
|
|
435
|
+
return void 0;
|
|
436
|
+
}
|
|
437
|
+
region = region.replaceAll("-", "_").toUpperCase();
|
|
438
|
+
if (VALID_REGIONS.includes(region)) {
|
|
439
|
+
return region;
|
|
440
|
+
}
|
|
441
|
+
console.warn(
|
|
442
|
+
`[Upstash QStash] Invalid UPSTASH_REGION header value: "${region}". Expected one of: ${VALID_REGIONS.join(
|
|
443
|
+
", "
|
|
444
|
+
)}.`
|
|
445
|
+
);
|
|
446
|
+
return void 0;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// src/client/multi-region/incoming.ts
|
|
450
|
+
var getReceiverSigningKeys = ({
|
|
451
|
+
environment,
|
|
452
|
+
regionFromHeader,
|
|
453
|
+
config
|
|
454
|
+
}) => {
|
|
455
|
+
if (config?.currentSigningKey && config.nextSigningKey) {
|
|
456
|
+
return {
|
|
457
|
+
currentSigningKey: config.currentSigningKey,
|
|
458
|
+
nextSigningKey: config.nextSigningKey
|
|
328
459
|
};
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
"
|
|
344
|
-
|
|
345
|
-
"remaining-requests": response.headers.get("x-ratelimit-remaining-requests"),
|
|
346
|
-
"remaining-tokens": response.headers.get("x-ratelimit-remaining-tokens"),
|
|
347
|
-
"reset-requests": response.headers.get("x-ratelimit-reset-requests"),
|
|
348
|
-
"reset-tokens": response.headers.get("x-ratelimit-reset-tokens")
|
|
349
|
-
});
|
|
350
|
-
} else if (response.headers.get("RateLimit-Limit")) {
|
|
351
|
-
throw new QstashDailyRatelimitError({
|
|
352
|
-
limit: response.headers.get("RateLimit-Limit"),
|
|
353
|
-
remaining: response.headers.get("RateLimit-Remaining"),
|
|
354
|
-
reset: response.headers.get("RateLimit-Reset")
|
|
355
|
-
});
|
|
460
|
+
}
|
|
461
|
+
const regionEnvironment = getRegionFromEnvironment(environment);
|
|
462
|
+
if (regionEnvironment) {
|
|
463
|
+
const regionHeader = normalizeRegionHeader(regionFromHeader);
|
|
464
|
+
if (regionHeader) {
|
|
465
|
+
const regionCreds = readReceiverEnvironmentVariables(environment, regionHeader);
|
|
466
|
+
if (regionCreds.QSTASH_CURRENT_SIGNING_KEY && regionCreds.QSTASH_NEXT_SIGNING_KEY) {
|
|
467
|
+
return {
|
|
468
|
+
currentSigningKey: regionCreds.QSTASH_CURRENT_SIGNING_KEY,
|
|
469
|
+
nextSigningKey: regionCreds.QSTASH_NEXT_SIGNING_KEY,
|
|
470
|
+
region: regionHeader
|
|
471
|
+
};
|
|
472
|
+
} else {
|
|
473
|
+
console.warn(
|
|
474
|
+
`[Upstash QStash] Signing keys not found for region "${regionHeader}". Falling back to default signing keys.`
|
|
475
|
+
);
|
|
356
476
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
reset: response.headers.get("Burst-RateLimit-Reset")
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
if (response.status < 200 || response.status >= 300) {
|
|
364
|
-
const body = await response.text();
|
|
365
|
-
throw new QstashError(
|
|
366
|
-
body.length > 0 ? body : `Error: status=${response.status}`,
|
|
367
|
-
response.status
|
|
477
|
+
} else {
|
|
478
|
+
console.warn(
|
|
479
|
+
`[Upstash QStash] Invalid UPSTASH_REGION header value: "${regionFromHeader}". Expected one of: EU-CENTRAL-1, US-EAST-1. Falling back to default signing keys.`
|
|
368
480
|
);
|
|
369
481
|
}
|
|
370
482
|
}
|
|
483
|
+
const defaultCreds = readReceiverEnvironmentVariables(environment);
|
|
484
|
+
if (defaultCreds.QSTASH_CURRENT_SIGNING_KEY && defaultCreds.QSTASH_NEXT_SIGNING_KEY) {
|
|
485
|
+
return {
|
|
486
|
+
currentSigningKey: defaultCreds.QSTASH_CURRENT_SIGNING_KEY,
|
|
487
|
+
nextSigningKey: defaultCreds.QSTASH_NEXT_SIGNING_KEY
|
|
488
|
+
};
|
|
489
|
+
}
|
|
371
490
|
};
|
|
372
491
|
|
|
373
|
-
// src/client/
|
|
374
|
-
var
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
default: {
|
|
402
|
-
throw new Error("Unknown analytics provider");
|
|
492
|
+
// src/client/multi-region/outgoing.ts
|
|
493
|
+
var getClientCredentials = (clientCredentialConfig) => {
|
|
494
|
+
const credentials = resolveCredentials(clientCredentialConfig);
|
|
495
|
+
return verifyCredentials(credentials);
|
|
496
|
+
};
|
|
497
|
+
var resolveCredentials = ({
|
|
498
|
+
environment,
|
|
499
|
+
config
|
|
500
|
+
}) => {
|
|
501
|
+
if (config?.baseUrl && config.token) {
|
|
502
|
+
return {
|
|
503
|
+
baseUrl: config.baseUrl,
|
|
504
|
+
token: config.token
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
const region = getRegionFromEnvironment(environment);
|
|
508
|
+
if (region) {
|
|
509
|
+
const regionCreds = readClientEnvironmentVariables(environment, region);
|
|
510
|
+
if (regionCreds.QSTASH_URL && regionCreds.QSTASH_TOKEN) {
|
|
511
|
+
return {
|
|
512
|
+
baseUrl: regionCreds.QSTASH_URL,
|
|
513
|
+
token: regionCreds.QSTASH_TOKEN,
|
|
514
|
+
region
|
|
515
|
+
};
|
|
516
|
+
} else {
|
|
517
|
+
console.warn(
|
|
518
|
+
`[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.`
|
|
519
|
+
);
|
|
403
520
|
}
|
|
404
521
|
}
|
|
522
|
+
const defaultCreds = readClientEnvironmentVariables(environment);
|
|
523
|
+
return {
|
|
524
|
+
baseUrl: config?.baseUrl ?? defaultCreds.QSTASH_URL ?? DEFAULT_QSTASH_URL,
|
|
525
|
+
token: config?.token ?? defaultCreds.QSTASH_TOKEN ?? ""
|
|
526
|
+
};
|
|
527
|
+
};
|
|
528
|
+
var verifyCredentials = (credentials) => {
|
|
529
|
+
const token = credentials.token;
|
|
530
|
+
let baseUrl = credentials.baseUrl;
|
|
531
|
+
baseUrl = baseUrl.replace(/\/$/, "");
|
|
532
|
+
if (baseUrl === "https://qstash.upstash.io/v2/publish") {
|
|
533
|
+
baseUrl = DEFAULT_QSTASH_URL;
|
|
534
|
+
}
|
|
535
|
+
if (!token) {
|
|
536
|
+
console.warn(
|
|
537
|
+
"[Upstash QStash] client token is not set. Either pass a token or set QSTASH_TOKEN env variable."
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
return { baseUrl, token };
|
|
405
541
|
};
|
|
406
542
|
|
|
407
|
-
// src/
|
|
408
|
-
var
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
this.http = http;
|
|
413
|
-
this.token = token;
|
|
543
|
+
// src/receiver.ts
|
|
544
|
+
var SignatureError = class extends Error {
|
|
545
|
+
constructor(message) {
|
|
546
|
+
super(message);
|
|
547
|
+
this.name = "SignatureError";
|
|
414
548
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
return chatRequest;
|
|
549
|
+
};
|
|
550
|
+
var Receiver = class {
|
|
551
|
+
currentSigningKey;
|
|
552
|
+
nextSigningKey;
|
|
553
|
+
constructor(config) {
|
|
554
|
+
this.currentSigningKey = config?.currentSigningKey;
|
|
555
|
+
this.nextSigningKey = config?.nextSigningKey;
|
|
423
556
|
}
|
|
424
557
|
/**
|
|
425
|
-
*
|
|
558
|
+
* Verify the signature of a request.
|
|
426
559
|
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
560
|
+
* Tries to verify the signature with the current signing key.
|
|
561
|
+
* If that fails, maybe because you have rotated the keys recently, it will
|
|
562
|
+
* try to verify the signature with the next signing key.
|
|
429
563
|
*
|
|
430
|
-
*
|
|
431
|
-
* @returns Chat completion or stream
|
|
564
|
+
* If that fails, the signature is invalid and a `SignatureError` is thrown.
|
|
432
565
|
*/
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
};
|
|
447
|
-
if (request.analytics) {
|
|
448
|
-
const { baseURL, defaultHeaders } = setupAnalytics(
|
|
449
|
-
{ name: "helicone", token: request.analytics.token },
|
|
450
|
-
this.getAuthorizationToken(),
|
|
451
|
-
request.provider.baseUrl,
|
|
452
|
-
"upstash"
|
|
566
|
+
async verify(request) {
|
|
567
|
+
const environment = getSafeEnvironment();
|
|
568
|
+
const signingKeys = getReceiverSigningKeys({
|
|
569
|
+
environment,
|
|
570
|
+
regionFromHeader: request.upstashRegion,
|
|
571
|
+
config: {
|
|
572
|
+
currentSigningKey: this.currentSigningKey,
|
|
573
|
+
nextSigningKey: this.nextSigningKey
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
if (!signingKeys) {
|
|
577
|
+
throw new Error(
|
|
578
|
+
"[Upstash QStash] No signing keys available for verification. See the warning above for more details."
|
|
453
579
|
);
|
|
454
|
-
headers = { ...headers, ...defaultHeaders };
|
|
455
|
-
baseUrl = baseURL;
|
|
456
580
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
method: "POST",
|
|
467
|
-
headers,
|
|
468
|
-
baseUrl,
|
|
469
|
-
body
|
|
470
|
-
});
|
|
471
|
-
};
|
|
581
|
+
let payload;
|
|
582
|
+
try {
|
|
583
|
+
payload = await this.verifyWithKey(signingKeys.currentSigningKey, request);
|
|
584
|
+
} catch {
|
|
585
|
+
payload = await this.verifyWithKey(signingKeys.nextSigningKey, request);
|
|
586
|
+
}
|
|
587
|
+
this.verifyBodyAndUrl(payload, request);
|
|
588
|
+
return true;
|
|
589
|
+
}
|
|
472
590
|
/**
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
* Returns a ChatCompletion or a stream of ChatCompletionChunks
|
|
476
|
-
* if stream is enabled.
|
|
477
|
-
*
|
|
478
|
-
* @param request ChatRequest with messages
|
|
479
|
-
* @returns Chat completion or stream
|
|
591
|
+
* Verify signature with a specific signing key
|
|
480
592
|
*/
|
|
481
|
-
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const analytics = request.analytics;
|
|
488
|
-
delete request.analytics;
|
|
489
|
-
const body = JSON.stringify(request);
|
|
490
|
-
const isAnalyticsEnabled = analytics?.name && analytics.token;
|
|
491
|
-
const analyticsConfig = analytics?.name && analytics.token ? setupAnalytics({ name: analytics.name, token: analytics.token }, token, baseUrl, owner) : { defaultHeaders: void 0, baseURL: baseUrl };
|
|
492
|
-
const isStream = "stream" in request && request.stream;
|
|
493
|
-
const headers = {
|
|
494
|
-
"Content-Type": "application/json",
|
|
495
|
-
Authorization: `Bearer ${token}`,
|
|
496
|
-
...organization ? {
|
|
497
|
-
"OpenAI-Organization": organization
|
|
498
|
-
} : {},
|
|
499
|
-
...isStream ? {
|
|
500
|
-
Connection: "keep-alive",
|
|
501
|
-
Accept: "text/event-stream",
|
|
502
|
-
"Cache-Control": "no-cache"
|
|
503
|
-
} : {},
|
|
504
|
-
...analyticsConfig.defaultHeaders
|
|
505
|
-
};
|
|
506
|
-
const response = await this.http[isStream ? "requestStream" : "request"]({
|
|
507
|
-
path: isAnalyticsEnabled ? [] : ["v1", "chat", "completions"],
|
|
508
|
-
method: "POST",
|
|
509
|
-
headers,
|
|
510
|
-
body,
|
|
511
|
-
baseUrl: analyticsConfig.baseURL
|
|
593
|
+
async verifyWithKey(key, request) {
|
|
594
|
+
const jwt = await jose.jwtVerify(request.signature, new TextEncoder().encode(key), {
|
|
595
|
+
issuer: "Upstash",
|
|
596
|
+
clockTolerance: request.clockTolerance
|
|
597
|
+
}).catch((error) => {
|
|
598
|
+
throw new SignatureError(error.message);
|
|
512
599
|
});
|
|
513
|
-
return
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
600
|
+
return jwt.payload;
|
|
601
|
+
}
|
|
602
|
+
verifyBodyAndUrl(payload, request) {
|
|
603
|
+
const p = payload;
|
|
604
|
+
if (request.url !== void 0 && p.sub !== request.url) {
|
|
605
|
+
throw new SignatureError(`invalid subject: ${p.sub}, want: ${request.url}`);
|
|
606
|
+
}
|
|
607
|
+
const bodyHash = import_crypto_js.default.SHA256(request.body).toString(import_crypto_js.default.enc.Base64url);
|
|
608
|
+
const padding = new RegExp(/=+$/);
|
|
609
|
+
if (p.body.replace(padding, "") !== bodyHash.replace(padding, "")) {
|
|
610
|
+
throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`);
|
|
521
611
|
}
|
|
522
|
-
return match[1];
|
|
523
612
|
}
|
|
524
|
-
/**
|
|
525
|
-
* Calls the Upstash completions api given a PromptRequest.
|
|
526
|
-
*
|
|
527
|
-
* Returns a ChatCompletion or a stream of ChatCompletionChunks
|
|
528
|
-
* if stream is enabled.
|
|
529
|
-
*
|
|
530
|
-
* @param request PromptRequest with system and user messages.
|
|
531
|
-
* Note that system parameter shouldn't be passed in the case of
|
|
532
|
-
* mistralai/Mistral-7B-Instruct-v0.2 model.
|
|
533
|
-
* @returns Chat completion or stream
|
|
534
|
-
*/
|
|
535
|
-
prompt = async (request) => {
|
|
536
|
-
const chatRequest = _Chat.toChatRequest(request);
|
|
537
|
-
return this.create(chatRequest);
|
|
538
|
-
};
|
|
539
613
|
};
|
|
540
614
|
|
|
541
|
-
// src/client/
|
|
542
|
-
var
|
|
615
|
+
// src/client/dlq.ts
|
|
616
|
+
var DLQ = class {
|
|
543
617
|
http;
|
|
544
618
|
constructor(http) {
|
|
545
619
|
this.http = http;
|
|
546
620
|
}
|
|
547
621
|
/**
|
|
548
|
-
*
|
|
622
|
+
* List messages in the dlq
|
|
549
623
|
*/
|
|
550
|
-
async
|
|
551
|
-
const
|
|
624
|
+
async listMessages(options) {
|
|
625
|
+
const filterPayload = {
|
|
626
|
+
...options?.filter,
|
|
627
|
+
topicName: options?.filter?.urlGroup
|
|
628
|
+
};
|
|
629
|
+
const messagesPayload = await this.http.request({
|
|
552
630
|
method: "GET",
|
|
553
|
-
path: ["v2", "
|
|
631
|
+
path: ["v2", "dlq"],
|
|
632
|
+
query: {
|
|
633
|
+
cursor: options?.cursor,
|
|
634
|
+
count: options?.count,
|
|
635
|
+
...filterPayload
|
|
636
|
+
}
|
|
554
637
|
});
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
638
|
+
return {
|
|
639
|
+
messages: messagesPayload.messages.map((message) => {
|
|
640
|
+
return {
|
|
641
|
+
...message,
|
|
642
|
+
urlGroup: message.topicName,
|
|
643
|
+
ratePerSecond: "rate" in message ? message.rate : void 0
|
|
644
|
+
};
|
|
645
|
+
}),
|
|
646
|
+
cursor: messagesPayload.cursor
|
|
559
647
|
};
|
|
560
|
-
return message;
|
|
561
648
|
}
|
|
562
649
|
/**
|
|
563
|
-
*
|
|
650
|
+
* Remove a message from the dlq using it's `dlqId`
|
|
564
651
|
*/
|
|
565
|
-
async delete(
|
|
652
|
+
async delete(dlqMessageId) {
|
|
566
653
|
return await this.http.request({
|
|
567
654
|
method: "DELETE",
|
|
568
|
-
path: ["v2", "
|
|
655
|
+
path: ["v2", "dlq", dlqMessageId],
|
|
569
656
|
parseResponseAsJson: false
|
|
657
|
+
// there is no response
|
|
570
658
|
});
|
|
571
659
|
}
|
|
572
|
-
|
|
573
|
-
|
|
660
|
+
/**
|
|
661
|
+
* Remove multiple messages from the dlq using their `dlqId`s
|
|
662
|
+
*/
|
|
663
|
+
async deleteMany(request) {
|
|
664
|
+
return await this.http.request({
|
|
574
665
|
method: "DELETE",
|
|
575
|
-
path: ["v2", "
|
|
666
|
+
path: ["v2", "dlq"],
|
|
576
667
|
headers: { "Content-Type": "application/json" },
|
|
577
|
-
body: JSON.stringify({
|
|
668
|
+
body: JSON.stringify({ dlqIds: request.dlqIds })
|
|
578
669
|
});
|
|
579
|
-
return result.cancelled;
|
|
580
|
-
}
|
|
581
|
-
async deleteAll() {
|
|
582
|
-
const result = await this.http.request({
|
|
583
|
-
method: "DELETE",
|
|
584
|
-
path: ["v2", "messages"]
|
|
585
|
-
});
|
|
586
|
-
return result.cancelled;
|
|
587
670
|
}
|
|
588
671
|
};
|
|
589
672
|
|
|
590
|
-
// src/client/
|
|
591
|
-
var
|
|
673
|
+
// src/client/http.ts
|
|
674
|
+
var HttpClient = class {
|
|
592
675
|
baseUrl;
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
super(baseUrl, token, owner);
|
|
612
|
-
this.organization = organization;
|
|
613
|
-
}
|
|
614
|
-
getRoute() {
|
|
615
|
-
return this.owner === "anthropic" ? ["v1", "messages"] : ["v1", "chat", "completions"];
|
|
676
|
+
authorization;
|
|
677
|
+
options;
|
|
678
|
+
retry;
|
|
679
|
+
headers;
|
|
680
|
+
telemetryHeaders;
|
|
681
|
+
constructor(config) {
|
|
682
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
683
|
+
this.authorization = config.authorization;
|
|
684
|
+
this.retry = // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
685
|
+
typeof config.retry === "boolean" && !config.retry ? {
|
|
686
|
+
attempts: 1,
|
|
687
|
+
backoff: () => 0
|
|
688
|
+
} : {
|
|
689
|
+
attempts: config.retry?.retries ?? 5,
|
|
690
|
+
backoff: config.retry?.backoff ?? ((retryCount) => Math.exp(retryCount) * 50)
|
|
691
|
+
};
|
|
692
|
+
this.headers = config.headers;
|
|
693
|
+
this.telemetryHeaders = config.telemetryHeaders;
|
|
616
694
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
695
|
+
async request(request) {
|
|
696
|
+
const { response } = await this.requestWithBackoff(request);
|
|
697
|
+
if (request.parseResponseAsJson === false) {
|
|
698
|
+
return void 0;
|
|
620
699
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
if (this.owner === "openai" && this.organization) {
|
|
628
|
-
headers["OpenAI-Organization"] = this.organization;
|
|
700
|
+
return await response.json();
|
|
701
|
+
}
|
|
702
|
+
async *requestStream(request) {
|
|
703
|
+
const { response } = await this.requestWithBackoff(request);
|
|
704
|
+
if (!response.body) {
|
|
705
|
+
throw new Error("No response body");
|
|
629
706
|
}
|
|
630
|
-
|
|
631
|
-
|
|
707
|
+
const body = response.body;
|
|
708
|
+
const reader = body.getReader();
|
|
709
|
+
const decoder = new TextDecoder();
|
|
710
|
+
try {
|
|
711
|
+
while (true) {
|
|
712
|
+
const { done, value } = await reader.read();
|
|
713
|
+
if (done) {
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
const chunkText = decoder.decode(value, { stream: true });
|
|
717
|
+
const chunks = chunkText.split("\n").filter(Boolean);
|
|
718
|
+
for (const chunk of chunks) {
|
|
719
|
+
if (chunk.startsWith("data: ")) {
|
|
720
|
+
const data = chunk.slice(6);
|
|
721
|
+
if (data === "[DONE]") {
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
yield JSON.parse(data);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
} finally {
|
|
729
|
+
await reader.cancel();
|
|
632
730
|
}
|
|
633
|
-
return headers;
|
|
634
731
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
732
|
+
requestWithBackoff = async (request) => {
|
|
733
|
+
const [url, requestOptions] = this.processRequest(request);
|
|
734
|
+
let response = void 0;
|
|
735
|
+
let error = void 0;
|
|
736
|
+
for (let index = 0; index <= this.retry.attempts; index++) {
|
|
737
|
+
try {
|
|
738
|
+
response = await fetch(url.toString(), requestOptions);
|
|
739
|
+
break;
|
|
740
|
+
} catch (error_) {
|
|
741
|
+
error = error_;
|
|
742
|
+
if (index < this.retry.attempts) {
|
|
743
|
+
await new Promise((r) => setTimeout(r, this.retry.backoff(index)));
|
|
744
|
+
}
|
|
745
|
+
}
|
|
644
746
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
};
|
|
648
|
-
var upstash = () => {
|
|
649
|
-
return new LLMProvider("https://qstash.upstash.io/llm", "", "upstash");
|
|
650
|
-
};
|
|
651
|
-
|
|
652
|
-
// src/client/api/utils.ts
|
|
653
|
-
var getProviderInfo = (api, upstashToken) => {
|
|
654
|
-
const { name, provider, ...parameters } = api;
|
|
655
|
-
const finalProvider = provider ?? upstash();
|
|
656
|
-
if (finalProvider.owner === "upstash" && !finalProvider.token) {
|
|
657
|
-
finalProvider.token = upstashToken;
|
|
658
|
-
}
|
|
659
|
-
if (!finalProvider.baseUrl)
|
|
660
|
-
throw new TypeError("baseUrl cannot be empty or undefined!");
|
|
661
|
-
if (!finalProvider.token)
|
|
662
|
-
throw new TypeError("token cannot be empty or undefined!");
|
|
663
|
-
if (finalProvider.apiKind !== name) {
|
|
664
|
-
throw new TypeError(
|
|
665
|
-
`Unexpected api name. Expected '${finalProvider.apiKind}', received ${name}`
|
|
666
|
-
);
|
|
667
|
-
}
|
|
668
|
-
const providerInfo = {
|
|
669
|
-
url: finalProvider.getUrl(),
|
|
670
|
-
baseUrl: finalProvider.baseUrl,
|
|
671
|
-
route: finalProvider.getRoute(),
|
|
672
|
-
appendHeaders: finalProvider.getHeaders(parameters),
|
|
673
|
-
owner: finalProvider.owner,
|
|
674
|
-
method: finalProvider.method
|
|
675
|
-
};
|
|
676
|
-
return finalProvider.onFinish(providerInfo, parameters);
|
|
677
|
-
};
|
|
678
|
-
var safeJoinHeaders = (headers, record) => {
|
|
679
|
-
const joinedHeaders = new Headers(record);
|
|
680
|
-
for (const [header, value] of headers.entries()) {
|
|
681
|
-
joinedHeaders.set(header, value);
|
|
682
|
-
}
|
|
683
|
-
return joinedHeaders;
|
|
684
|
-
};
|
|
685
|
-
var processApi = (request, headers, upstashToken) => {
|
|
686
|
-
if (!request.api) {
|
|
687
|
-
request.headers = headers;
|
|
688
|
-
return request;
|
|
689
|
-
}
|
|
690
|
-
const { url, appendHeaders, owner, method } = getProviderInfo(request.api, upstashToken);
|
|
691
|
-
if (request.api.name === "llm") {
|
|
692
|
-
const callback = request.callback;
|
|
693
|
-
if (!callback) {
|
|
694
|
-
throw new TypeError("Callback cannot be undefined when using LLM api.");
|
|
747
|
+
if (!response) {
|
|
748
|
+
throw error ?? new Error("Exhausted all retries");
|
|
695
749
|
}
|
|
750
|
+
await this.checkResponse(response);
|
|
696
751
|
return {
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
headers: safeJoinHeaders(headers, appendHeaders),
|
|
700
|
-
...owner === "upstash" && !request.api.analytics ? { api: { name: "llm" }, url: void 0, callback } : { url, api: void 0 }
|
|
752
|
+
response,
|
|
753
|
+
error
|
|
701
754
|
};
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
headers
|
|
707
|
-
|
|
708
|
-
|
|
755
|
+
};
|
|
756
|
+
processRequest = (request) => {
|
|
757
|
+
const headers = new Headers(request.headers);
|
|
758
|
+
if (!headers.has("Authorization")) {
|
|
759
|
+
headers.set("Authorization", this.authorization);
|
|
760
|
+
}
|
|
761
|
+
const requestOptions = {
|
|
762
|
+
method: request.method,
|
|
763
|
+
headers,
|
|
764
|
+
body: request.body,
|
|
765
|
+
keepalive: request.keepalive
|
|
709
766
|
};
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
if (providerInfo.owner === "upstash") {
|
|
717
|
-
updateProviderInfo(providerInfo, "https://qstash.helicone.ai", [
|
|
718
|
-
"llm",
|
|
719
|
-
...providerInfo.route
|
|
720
|
-
]);
|
|
721
|
-
} else {
|
|
722
|
-
providerInfo.appendHeaders["Helicone-Target-Url"] = providerInfo.baseUrl;
|
|
723
|
-
updateProviderInfo(providerInfo, "https://gateway.helicone.ai", providerInfo.route);
|
|
767
|
+
const url = new URL([request.baseUrl ?? this.baseUrl, ...request.path].join("/"));
|
|
768
|
+
if (request.query) {
|
|
769
|
+
for (const [key, value] of Object.entries(request.query)) {
|
|
770
|
+
if (value !== void 0) {
|
|
771
|
+
url.searchParams.set(key, value.toString());
|
|
772
|
+
}
|
|
724
773
|
}
|
|
725
|
-
return providerInfo;
|
|
726
774
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
775
|
+
return [url.toString(), requestOptions];
|
|
776
|
+
};
|
|
777
|
+
async checkResponse(response) {
|
|
778
|
+
if (response.status === 429) {
|
|
779
|
+
if (response.headers.get("x-ratelimit-limit-requests")) {
|
|
780
|
+
throw new QstashChatRatelimitError({
|
|
781
|
+
"limit-requests": response.headers.get("x-ratelimit-limit-requests"),
|
|
782
|
+
"limit-tokens": response.headers.get("x-ratelimit-limit-tokens"),
|
|
783
|
+
"remaining-requests": response.headers.get("x-ratelimit-remaining-requests"),
|
|
784
|
+
"remaining-tokens": response.headers.get("x-ratelimit-remaining-tokens"),
|
|
785
|
+
"reset-requests": response.headers.get("x-ratelimit-reset-requests"),
|
|
786
|
+
"reset-tokens": response.headers.get("x-ratelimit-reset-tokens")
|
|
787
|
+
});
|
|
788
|
+
} else if (response.headers.get("RateLimit-Limit")) {
|
|
789
|
+
throw new QstashDailyRatelimitError({
|
|
790
|
+
limit: response.headers.get("RateLimit-Limit"),
|
|
791
|
+
remaining: response.headers.get("RateLimit-Remaining"),
|
|
792
|
+
reset: response.headers.get("RateLimit-Reset")
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
throw new QstashRatelimitError({
|
|
796
|
+
limit: response.headers.get("Burst-RateLimit-Limit"),
|
|
797
|
+
remaining: response.headers.get("Burst-RateLimit-Remaining"),
|
|
798
|
+
reset: response.headers.get("Burst-RateLimit-Reset")
|
|
799
|
+
});
|
|
749
800
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
return headers;
|
|
757
|
-
}
|
|
758
|
-
const finalHeaders = new Headers(globalHeaders);
|
|
759
|
-
headers.forEach((value, key) => {
|
|
760
|
-
finalHeaders.set(key, value);
|
|
761
|
-
});
|
|
762
|
-
telemetryHeaders?.forEach((value, key) => {
|
|
763
|
-
if (!value)
|
|
764
|
-
return;
|
|
765
|
-
finalHeaders.append(key, value);
|
|
766
|
-
});
|
|
767
|
-
return finalHeaders;
|
|
768
|
-
}
|
|
769
|
-
function processHeaders(request) {
|
|
770
|
-
const headers = prefixHeaders(new Headers(request.headers));
|
|
771
|
-
headers.set("Upstash-Method", request.method ?? "POST");
|
|
772
|
-
if (request.delay !== void 0) {
|
|
773
|
-
if (typeof request.delay === "string") {
|
|
774
|
-
headers.set("Upstash-Delay", request.delay);
|
|
775
|
-
} else {
|
|
776
|
-
headers.set("Upstash-Delay", `${request.delay.toFixed(0)}s`);
|
|
801
|
+
if (response.status < 200 || response.status >= 300) {
|
|
802
|
+
const body = await response.text();
|
|
803
|
+
throw new QstashError(
|
|
804
|
+
body.length > 0 ? body : `Error: status=${response.status}`,
|
|
805
|
+
response.status
|
|
806
|
+
);
|
|
777
807
|
}
|
|
778
808
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
// src/client/llm/providers.ts
|
|
812
|
+
var setupAnalytics = (analytics, providerApiKey, providerBaseUrl, provider) => {
|
|
813
|
+
if (!analytics)
|
|
814
|
+
return {};
|
|
815
|
+
switch (analytics.name) {
|
|
816
|
+
case "helicone": {
|
|
817
|
+
switch (provider) {
|
|
818
|
+
case "upstash": {
|
|
819
|
+
return {
|
|
820
|
+
baseURL: "https://qstash.helicone.ai/llm/v1/chat/completions",
|
|
821
|
+
defaultHeaders: {
|
|
822
|
+
"Helicone-Auth": `Bearer ${analytics.token}`,
|
|
823
|
+
Authorization: `Bearer ${providerApiKey}`
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
default: {
|
|
828
|
+
return {
|
|
829
|
+
baseURL: "https://gateway.helicone.ai/v1/chat/completions",
|
|
830
|
+
defaultHeaders: {
|
|
831
|
+
"Helicone-Auth": `Bearer ${analytics.token}`,
|
|
832
|
+
"Helicone-Target-Url": providerBaseUrl,
|
|
833
|
+
Authorization: `Bearer ${providerApiKey}`
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
default: {
|
|
840
|
+
throw new Error("Unknown analytics provider");
|
|
841
|
+
}
|
|
793
842
|
}
|
|
794
|
-
|
|
795
|
-
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
// src/client/llm/chat.ts
|
|
846
|
+
var Chat = class _Chat {
|
|
847
|
+
http;
|
|
848
|
+
token;
|
|
849
|
+
constructor(http, token) {
|
|
850
|
+
this.http = http;
|
|
851
|
+
this.token = token;
|
|
796
852
|
}
|
|
797
|
-
|
|
798
|
-
|
|
853
|
+
static toChatRequest(request) {
|
|
854
|
+
const messages = [];
|
|
855
|
+
messages.push(
|
|
856
|
+
{ role: "system", content: request.system },
|
|
857
|
+
{ role: "user", content: request.user }
|
|
858
|
+
);
|
|
859
|
+
const chatRequest = { ...request, messages };
|
|
860
|
+
return chatRequest;
|
|
799
861
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
862
|
+
/**
|
|
863
|
+
* Calls the Upstash completions api given a ChatRequest.
|
|
864
|
+
*
|
|
865
|
+
* Returns a ChatCompletion or a stream of ChatCompletionChunks
|
|
866
|
+
* if stream is enabled.
|
|
867
|
+
*
|
|
868
|
+
* @param request ChatRequest with messages
|
|
869
|
+
* @returns Chat completion or stream
|
|
870
|
+
*/
|
|
871
|
+
create = async (request) => {
|
|
872
|
+
if (request.provider.owner != "upstash")
|
|
873
|
+
return this.createThirdParty(request);
|
|
874
|
+
const body = JSON.stringify(request);
|
|
875
|
+
let baseUrl = void 0;
|
|
876
|
+
let headers = {
|
|
877
|
+
"Content-Type": "application/json",
|
|
878
|
+
Authorization: `Bearer ${this.token}`,
|
|
879
|
+
..."stream" in request && request.stream ? {
|
|
880
|
+
Connection: "keep-alive",
|
|
881
|
+
Accept: "text/event-stream",
|
|
882
|
+
"Cache-Control": "no-cache"
|
|
883
|
+
} : {}
|
|
884
|
+
};
|
|
885
|
+
if (request.analytics) {
|
|
886
|
+
const { baseURL, defaultHeaders } = setupAnalytics(
|
|
887
|
+
{ name: "helicone", token: request.analytics.token },
|
|
888
|
+
this.getAuthorizationToken(),
|
|
889
|
+
request.provider.baseUrl,
|
|
890
|
+
"upstash"
|
|
891
|
+
);
|
|
892
|
+
headers = { ...headers, ...defaultHeaders };
|
|
893
|
+
baseUrl = baseURL;
|
|
805
894
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
895
|
+
const path = request.analytics ? [] : ["llm", "v1", "chat", "completions"];
|
|
896
|
+
return "stream" in request && request.stream ? this.http.requestStream({
|
|
897
|
+
path,
|
|
898
|
+
method: "POST",
|
|
899
|
+
headers,
|
|
900
|
+
baseUrl,
|
|
901
|
+
body
|
|
902
|
+
}) : this.http.request({
|
|
903
|
+
path,
|
|
904
|
+
method: "POST",
|
|
905
|
+
headers,
|
|
906
|
+
baseUrl,
|
|
907
|
+
body
|
|
908
|
+
});
|
|
909
|
+
};
|
|
910
|
+
/**
|
|
911
|
+
* Calls the Upstash completions api given a ChatRequest.
|
|
912
|
+
*
|
|
913
|
+
* Returns a ChatCompletion or a stream of ChatCompletionChunks
|
|
914
|
+
* if stream is enabled.
|
|
915
|
+
*
|
|
916
|
+
* @param request ChatRequest with messages
|
|
917
|
+
* @returns Chat completion or stream
|
|
918
|
+
*/
|
|
919
|
+
createThirdParty = async (request) => {
|
|
920
|
+
const { baseUrl, token, owner, organization } = request.provider;
|
|
921
|
+
if (owner === "upstash")
|
|
922
|
+
throw new Error("Upstash is not 3rd party provider!");
|
|
923
|
+
delete request.provider;
|
|
924
|
+
delete request.system;
|
|
925
|
+
const analytics = request.analytics;
|
|
926
|
+
delete request.analytics;
|
|
927
|
+
const body = JSON.stringify(request);
|
|
928
|
+
const isAnalyticsEnabled = analytics?.name && analytics.token;
|
|
929
|
+
const analyticsConfig = analytics?.name && analytics.token ? setupAnalytics({ name: analytics.name, token: analytics.token }, token, baseUrl, owner) : { defaultHeaders: void 0, baseURL: baseUrl };
|
|
930
|
+
const isStream = "stream" in request && request.stream;
|
|
931
|
+
const headers = {
|
|
932
|
+
"Content-Type": "application/json",
|
|
933
|
+
Authorization: `Bearer ${token}`,
|
|
934
|
+
...organization ? {
|
|
935
|
+
"OpenAI-Organization": organization
|
|
936
|
+
} : {},
|
|
937
|
+
...isStream ? {
|
|
938
|
+
Connection: "keep-alive",
|
|
939
|
+
Accept: "text/event-stream",
|
|
940
|
+
"Cache-Control": "no-cache"
|
|
941
|
+
} : {},
|
|
942
|
+
...analyticsConfig.defaultHeaders
|
|
943
|
+
};
|
|
944
|
+
const response = await this.http[isStream ? "requestStream" : "request"]({
|
|
945
|
+
path: isAnalyticsEnabled ? [] : ["v1", "chat", "completions"],
|
|
946
|
+
method: "POST",
|
|
947
|
+
headers,
|
|
948
|
+
body,
|
|
949
|
+
baseUrl: analyticsConfig.baseURL
|
|
950
|
+
});
|
|
951
|
+
return response;
|
|
952
|
+
};
|
|
953
|
+
// Helper method to get the authorization token
|
|
954
|
+
getAuthorizationToken() {
|
|
955
|
+
const authHeader = String(this.http.authorization);
|
|
956
|
+
const match = /Bearer (.+)/.exec(authHeader);
|
|
957
|
+
if (!match) {
|
|
958
|
+
throw new Error("Invalid authorization header format");
|
|
818
959
|
}
|
|
819
|
-
|
|
820
|
-
headers.set("Upstash-Flow-Control-Value", controlValue.join(", "));
|
|
960
|
+
return match[1];
|
|
821
961
|
}
|
|
822
|
-
|
|
823
|
-
|
|
962
|
+
/**
|
|
963
|
+
* Calls the Upstash completions api given a PromptRequest.
|
|
964
|
+
*
|
|
965
|
+
* Returns a ChatCompletion or a stream of ChatCompletionChunks
|
|
966
|
+
* if stream is enabled.
|
|
967
|
+
*
|
|
968
|
+
* @param request PromptRequest with system and user messages.
|
|
969
|
+
* Note that system parameter shouldn't be passed in the case of
|
|
970
|
+
* mistralai/Mistral-7B-Instruct-v0.2 model.
|
|
971
|
+
* @returns Chat completion or stream
|
|
972
|
+
*/
|
|
973
|
+
prompt = async (request) => {
|
|
974
|
+
const chatRequest = _Chat.toChatRequest(request);
|
|
975
|
+
return this.create(chatRequest);
|
|
976
|
+
};
|
|
977
|
+
};
|
|
978
|
+
|
|
979
|
+
// src/client/messages.ts
|
|
980
|
+
var Messages = class {
|
|
981
|
+
http;
|
|
982
|
+
constructor(http) {
|
|
983
|
+
this.http = http;
|
|
824
984
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
985
|
+
/**
|
|
986
|
+
* Get a message
|
|
987
|
+
*/
|
|
988
|
+
async get(messageId) {
|
|
989
|
+
const messagePayload = await this.http.request({
|
|
990
|
+
method: "GET",
|
|
991
|
+
path: ["v2", "messages", messageId]
|
|
992
|
+
});
|
|
993
|
+
const message = {
|
|
994
|
+
...messagePayload,
|
|
995
|
+
urlGroup: messagePayload.topicName,
|
|
996
|
+
ratePerSecond: "rate" in messagePayload ? messagePayload.rate : void 0
|
|
997
|
+
};
|
|
998
|
+
return message;
|
|
836
999
|
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
const binString = atob(base64);
|
|
847
|
-
const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
|
848
|
-
return new TextDecoder().decode(intArray);
|
|
849
|
-
} catch (error) {
|
|
850
|
-
try {
|
|
851
|
-
const result = atob(base64);
|
|
852
|
-
console.warn(
|
|
853
|
-
`Upstash QStash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}`
|
|
854
|
-
);
|
|
855
|
-
return result;
|
|
856
|
-
} catch (error2) {
|
|
857
|
-
console.warn(
|
|
858
|
-
`Upstash QStash: Failed to decode base64 "${base64}" with atob. Returning it as it is. ${error2}`
|
|
859
|
-
);
|
|
860
|
-
return base64;
|
|
861
|
-
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Cancel a message
|
|
1002
|
+
*/
|
|
1003
|
+
async delete(messageId) {
|
|
1004
|
+
return await this.http.request({
|
|
1005
|
+
method: "DELETE",
|
|
1006
|
+
path: ["v2", "messages", messageId],
|
|
1007
|
+
parseResponseAsJson: false
|
|
1008
|
+
});
|
|
862
1009
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
return
|
|
871
|
-
|
|
872
|
-
|
|
1010
|
+
async deleteMany(messageIds) {
|
|
1011
|
+
const result = await this.http.request({
|
|
1012
|
+
method: "DELETE",
|
|
1013
|
+
path: ["v2", "messages"],
|
|
1014
|
+
headers: { "Content-Type": "application/json" },
|
|
1015
|
+
body: JSON.stringify({ messageIds })
|
|
1016
|
+
});
|
|
1017
|
+
return result.cancelled;
|
|
1018
|
+
}
|
|
1019
|
+
async deleteAll() {
|
|
1020
|
+
const result = await this.http.request({
|
|
1021
|
+
method: "DELETE",
|
|
1022
|
+
path: ["v2", "messages"]
|
|
1023
|
+
});
|
|
1024
|
+
return result.cancelled;
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
873
1027
|
|
|
874
1028
|
// src/client/queue.ts
|
|
875
1029
|
var Queue = class {
|
|
@@ -1200,19 +1354,15 @@ var UrlGroups = class {
|
|
|
1200
1354
|
};
|
|
1201
1355
|
|
|
1202
1356
|
// version.ts
|
|
1203
|
-
var VERSION = "v2.
|
|
1357
|
+
var VERSION = "v2.9.0";
|
|
1204
1358
|
|
|
1205
1359
|
// src/client/client.ts
|
|
1206
1360
|
var Client = class {
|
|
1207
1361
|
http;
|
|
1208
1362
|
token;
|
|
1209
1363
|
constructor(config) {
|
|
1210
|
-
const environment =
|
|
1211
|
-
|
|
1212
|
-
if (baseUrl === "https://qstash.upstash.io/v2/publish") {
|
|
1213
|
-
baseUrl = "https://qstash.upstash.io";
|
|
1214
|
-
}
|
|
1215
|
-
const token = config?.token ?? environment.QSTASH_TOKEN;
|
|
1364
|
+
const environment = getSafeEnvironment();
|
|
1365
|
+
const { baseUrl, token } = getClientCredentials({ environment, config });
|
|
1216
1366
|
const enableTelemetry = environment.UPSTASH_DISABLE_TELEMETRY ? false : config?.enableTelemetry ?? true;
|
|
1217
1367
|
const isCloudflare = typeof caches !== "undefined" && "default" in caches;
|
|
1218
1368
|
const telemetryHeaders = new Headers(
|
|
@@ -1231,11 +1381,6 @@ var Client = class {
|
|
|
1231
1381
|
//@ts-expect-error caused by undici and bunjs type overlap
|
|
1232
1382
|
telemetryHeaders
|
|
1233
1383
|
});
|
|
1234
|
-
if (!token) {
|
|
1235
|
-
console.warn(
|
|
1236
|
-
"[Upstash QStash] client token is not set. Either pass a token or set QSTASH_TOKEN env variable."
|
|
1237
|
-
);
|
|
1238
|
-
}
|
|
1239
1384
|
this.token = token;
|
|
1240
1385
|
}
|
|
1241
1386
|
/**
|