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