@upstash/qstash
Advanced tools
Comparing version 0.0.0-ci.58463b768ad41df0667237530a94939beae3fce2-20240516131014 to 0.0.0-ci.5daf80975ba2cf15c84cc516c6ac6c5c9b3a1c7b-20241106143055
740
index.d.ts
@@ -0,684 +1,90 @@ | ||
import { R as RateLimit, C as ChatRateLimit, S as Step, F as FailureFunctionPayload, L as LLMOwner, B as BaseProvider, E as EmailOwner, P as ProviderInfo } from './client-BY4y-4To.js'; | ||
export { A as AddEndpointsRequest, y as BodyInit, I as Chat, K as ChatCompletion, N as ChatCompletionChunk, J as ChatCompletionMessage, _ as ChatRequest, h as Client, p as CreateScheduleRequest, r as Endpoint, v as Event, w as EventPayload, g as EventsRequest, x as GetEventsPayload, G as GetEventsResponse, H as HTTPMethods, z as HeadersInit, M as Message, m as MessagePayload, n as Messages, Y as OpenAIChatModel, Z as PromptChatRequest, d as PublishBatchRequest, f as PublishJsonRequest, e as PublishRequest, l as PublishResponse, i as PublishToApiResponse, k as PublishToUrlGroupsResponse, j as PublishToUrlResponse, Q as QueueRequest, c as Receiver, a as ReceiverConfig, s as RemoveEndpointsRequest, D as RequestOptions, o as Schedule, q as Schedules, b as SignatureError, u as State, T as StreamDisabled, O as StreamEnabled, X as StreamParameter, U as UrlGroup, t as UrlGroups, V as VerifyRequest, W as WithCursor, a1 as anthropic, a2 as custom, a0 as openai, $ as upstash } from './client-BY4y-4To.js'; | ||
import 'neverthrow'; | ||
/** | ||
* Necessary to verify the signature of a request. | ||
* Result of 500 Internal Server Error | ||
*/ | ||
type ReceiverConfig = { | ||
/** | ||
* The current signing key. Get it from `https://console.upstash.com/qstash | ||
*/ | ||
currentSigningKey: string; | ||
/** | ||
* The next signing key. Get it from `https://console.upstash.com/qstash | ||
*/ | ||
nextSigningKey: string; | ||
}; | ||
type VerifyRequest = { | ||
/** | ||
* The signature from the `upstash-signature` header. | ||
*/ | ||
signature: string; | ||
/** | ||
* The raw request body. | ||
*/ | ||
body: string; | ||
/** | ||
* URL of the endpoint where the request was sent to. | ||
* | ||
* Omit empty to disable checking the url. | ||
*/ | ||
url?: string; | ||
/** | ||
* Number of seconds to tolerate when checking `nbf` and `exp` claims, to deal with small clock differences among different servers | ||
* | ||
* @default 0 | ||
*/ | ||
clockTolerance?: number; | ||
}; | ||
declare class SignatureError extends Error { | ||
declare class QstashError extends Error { | ||
constructor(message: string); | ||
} | ||
/** | ||
* Receiver offers a simple way to verify the signature of a request. | ||
*/ | ||
declare class Receiver { | ||
private readonly currentSigningKey; | ||
private readonly nextSigningKey; | ||
constructor(config: ReceiverConfig); | ||
/** | ||
* Verify the signature of a request. | ||
* | ||
* Tries to verify the signature with the current signing key. | ||
* If that fails, maybe because you have rotated the keys recently, it will | ||
* try to verify the signature with the next signing key. | ||
* | ||
* If that fails, the signature is invalid and a `SignatureError` is thrown. | ||
*/ | ||
verify(request: VerifyRequest): Promise<boolean>; | ||
/** | ||
* Verify signature with a specific signing key | ||
*/ | ||
private verifyWithKey; | ||
declare class QstashRatelimitError extends QstashError { | ||
limit: string | null; | ||
remaining: string | null; | ||
reset: string | null; | ||
constructor(args: RateLimit); | ||
} | ||
type State = "CREATED" | "ACTIVE" | "DELIVERED" | "ERROR" | "RETRY" | "FAILED"; | ||
type Event = { | ||
time: number; | ||
state: State; | ||
messageId: string; | ||
nextDeliveryTime?: number; | ||
error?: string; | ||
url: string; | ||
topicName?: string; | ||
endpointName?: string; | ||
}; | ||
type WithCursor<T> = T & { | ||
cursor?: number; | ||
}; | ||
type BodyInit = Blob | FormData | URLSearchParams | ReadableStream<Uint8Array> | string; | ||
type HeadersInit = Headers | Record<string, string> | [string, string][] | IterableIterator<[string, string]>; | ||
type UpstashRequest = { | ||
/** | ||
* The path to the resource. | ||
*/ | ||
path: string[]; | ||
/** | ||
* A BodyInit object or null to set request's body. | ||
*/ | ||
body?: BodyInit | null; | ||
/** | ||
* A Headers object, an object literal, or an array of two-item arrays to set | ||
* request's headers. | ||
*/ | ||
headers?: HeadersInit; | ||
/** | ||
* A boolean to set request's keepalive. | ||
*/ | ||
keepalive?: boolean; | ||
/** | ||
* A string to set request's method. | ||
*/ | ||
method?: "GET" | "POST" | "PUT" | "DELETE"; | ||
query?: Record<string, string | number | boolean | undefined>; | ||
/** | ||
* if enabled, call `res.json()` | ||
* | ||
* @default true | ||
*/ | ||
parseResponseAsJson?: boolean; | ||
}; | ||
type UpstashResponse<TResult> = TResult & { | ||
error?: string; | ||
}; | ||
type Requester = { | ||
request: <TResult = unknown>(request: UpstashRequest) => Promise<UpstashResponse<TResult>>; | ||
}; | ||
type RetryConfig = false | { | ||
/** | ||
* The number of retries to attempt before giving up. | ||
* | ||
* @default 5 | ||
*/ | ||
retries?: number; | ||
/** | ||
* A backoff function receives the current retry cound and returns a number in milliseconds to wait before retrying. | ||
* | ||
* @default | ||
* ```ts | ||
* Math.exp(retryCount) * 50 | ||
* ``` | ||
*/ | ||
backoff?: (retryCount: number) => number; | ||
}; | ||
type Message = { | ||
/** | ||
* A unique identifier for this message. | ||
*/ | ||
messageId: string; | ||
/** | ||
* The topic name if this message was sent to a topic. | ||
*/ | ||
topicName?: string; | ||
/** | ||
* The url where this message is sent to. | ||
*/ | ||
url: string; | ||
/** | ||
* The http method used to deliver the message | ||
*/ | ||
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; | ||
/** | ||
* The http headers sent along with the message to your API. | ||
*/ | ||
header?: Record<string, string[]>; | ||
/** | ||
* The http body sent to your API | ||
*/ | ||
body?: string; | ||
/** | ||
* Maxmimum number of retries. | ||
*/ | ||
maxRetries?: number; | ||
/** | ||
* A unix timestamp (milliseconds) after which this message may get delivered. | ||
*/ | ||
notBefore?: number; | ||
/** | ||
* A unix timestamp (milliseconds) when this messages was created. | ||
*/ | ||
createdAt: number; | ||
/** | ||
* The callback url if configured. | ||
*/ | ||
callback?: string; | ||
/** | ||
* The failure callback url if configured. | ||
*/ | ||
failureCallback?: string; | ||
/** | ||
* The queue name if this message was sent to a queue. | ||
*/ | ||
queueName?: string; | ||
}; | ||
declare class Messages { | ||
private readonly http; | ||
constructor(http: Requester); | ||
/** | ||
* Get a message | ||
*/ | ||
get(messageId: string): Promise<Message>; | ||
/** | ||
* Cancel a message | ||
*/ | ||
delete(messageId: string): Promise<void>; | ||
declare class QstashChatRatelimitError extends QstashError { | ||
limitRequests: string | null; | ||
limitTokens: string | null; | ||
remainingRequests: string | null; | ||
remainingTokens: string | null; | ||
resetRequests: string | null; | ||
resetTokens: string | null; | ||
constructor(args: ChatRateLimit); | ||
} | ||
type DlqMessage = Message & { | ||
dlqId: string; | ||
}; | ||
declare class DLQ { | ||
private readonly http; | ||
constructor(http: Requester); | ||
/** | ||
* List messages in the dlq | ||
*/ | ||
listMessages(options?: { | ||
cursor?: string; | ||
}): Promise<{ | ||
messages: DlqMessage[]; | ||
cursor?: string; | ||
}>; | ||
/** | ||
* Remove a message from the dlq using it's `dlqId` | ||
*/ | ||
delete(dlqMessageId: string): Promise<void>; | ||
/** | ||
* Remove multiple messages from the dlq using their `dlqId`s | ||
*/ | ||
deleteMany(request: { | ||
dlqIds: string[]; | ||
}): Promise<{ | ||
deleted: number; | ||
}>; | ||
declare class QstashDailyRatelimitError extends QstashError { | ||
limit: string | null; | ||
remaining: string | null; | ||
reset: string | null; | ||
constructor(args: RateLimit); | ||
} | ||
type QueueResponse = { | ||
createdAt: number; | ||
updatedAt: number; | ||
name: string; | ||
parallelism: number; | ||
lag: number; | ||
}; | ||
type UpsertQueueRequest = { | ||
parallelism: number; | ||
}; | ||
declare class Queue { | ||
private readonly http; | ||
private readonly queueName; | ||
constructor(http: Requester, queueName?: string); | ||
/** | ||
* Create or update the queue | ||
*/ | ||
upsert(request: UpsertQueueRequest): Promise<void>; | ||
/** | ||
* Get the queue details | ||
*/ | ||
get(): Promise<QueueResponse>; | ||
/** | ||
* List queues | ||
*/ | ||
list(): Promise<QueueResponse[]>; | ||
/** | ||
* Delete the queue | ||
*/ | ||
delete(): Promise<void>; | ||
/** | ||
* Enqueue a message to a queue. | ||
*/ | ||
enqueue<TRequest extends PublishRequest>(request: TRequest): Promise<PublishResponse<TRequest>>; | ||
/** | ||
* Enqueue a message to a queue, serializing the body to JSON. | ||
*/ | ||
enqueueJSON<TBody = unknown>(request: PublishRequest<TBody>): Promise<PublishResponse<PublishRequest<TBody>>>; | ||
/** | ||
* Error raised during Workflow execution | ||
*/ | ||
declare class QStashWorkflowError extends QstashError { | ||
constructor(message: string); | ||
} | ||
type Schedule = { | ||
scheduleId: string; | ||
cron: string; | ||
createdAt: number; | ||
destination: string; | ||
method: string; | ||
header?: Record<string, string[]>; | ||
body?: string; | ||
retries: number; | ||
delay?: number; | ||
callback?: string; | ||
failureCallback?: string; | ||
}; | ||
type CreateScheduleRequest = { | ||
/** | ||
* Either a URL or topic name | ||
*/ | ||
destination: string; | ||
/** | ||
* The message to send. | ||
* | ||
* This can be anything, but please set the `Content-Type` header accordingly. | ||
* | ||
* You can leave this empty if you want to send a message with no body. | ||
*/ | ||
body?: BodyInit; | ||
/** | ||
* Optionally send along headers with the message. | ||
* These headers will be sent to your destination. | ||
* | ||
* We highly recommend sending a `Content-Type` header along, as this will help your destination | ||
* server to understand the content of the message. | ||
*/ | ||
headers?: HeadersInit; | ||
/** | ||
* Optionally delay the delivery of this message. | ||
* | ||
* In seconds. | ||
* | ||
* @default undefined | ||
*/ | ||
delay?: number; | ||
/** | ||
* In case your destination server is unavailable or returns a status code outside of the 200-299 | ||
* range, we will retry the request after a certain amount of time. | ||
* | ||
* Configure how many times you would like the delivery to be retried | ||
* | ||
* @default The maximum retry quota associated with your account. | ||
*/ | ||
retries?: number; | ||
/** | ||
* Use a callback url to forward the response of your destination server to your callback url. | ||
* | ||
* The callback url must be publicly accessible | ||
* | ||
* @default undefined | ||
*/ | ||
callback?: string; | ||
/** | ||
* Use a failure callback url to handle messages that could not be delivered. | ||
* | ||
* The failure callback url must be publicly accessible | ||
* | ||
* @default undefined | ||
*/ | ||
failureCallback?: string; | ||
/** | ||
* The method to use when sending a request to your API | ||
* | ||
* @default `POST` | ||
*/ | ||
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; | ||
/** | ||
* Specify a cron expression to repeatedly send this message to the destination. | ||
*/ | ||
cron: string; | ||
}; | ||
declare class Schedules { | ||
private readonly http; | ||
constructor(http: Requester); | ||
/** | ||
* Create a schedule | ||
*/ | ||
create(request: CreateScheduleRequest): Promise<{ | ||
scheduleId: string; | ||
}>; | ||
/** | ||
* Get a schedule | ||
*/ | ||
get(scheduleId: string): Promise<Schedule>; | ||
/** | ||
* List your schedules | ||
*/ | ||
list(): Promise<Schedule[]>; | ||
/** | ||
* Delete a schedule | ||
*/ | ||
delete(scheduleId: string): Promise<void>; | ||
/** | ||
* Raised when the workflow executes a function and aborts | ||
*/ | ||
declare class QStashWorkflowAbort extends Error { | ||
stepInfo?: Step; | ||
stepName: string; | ||
constructor(stepName: string, stepInfo?: Step); | ||
} | ||
/** | ||
* Formats an unknown error to match the FailureFunctionPayload format | ||
* | ||
* @param error | ||
* @returns | ||
*/ | ||
declare const formatWorkflowError: (error: unknown) => FailureFunctionPayload; | ||
type Endpoint = { | ||
/** | ||
* The name of the endpoint (optional) | ||
*/ | ||
name?: string; | ||
/** | ||
* The url of the endpoint | ||
*/ | ||
url: string; | ||
}; | ||
type AddEndpointsRequest = { | ||
/** | ||
* The name of the topic. | ||
* Must be unique and only contain alphanumeric, hyphen, underscore and periods. | ||
*/ | ||
name: string; | ||
endpoints: Endpoint[]; | ||
}; | ||
type RemoveEndpointsRequest = { | ||
/** | ||
* The name of the topic. | ||
* Must be unique and only contain alphanumeric, hyphen, underscore and periods. | ||
*/ | ||
name: string; | ||
endpoints: ({ | ||
name: string; | ||
url?: string; | ||
} | { | ||
name?: string; | ||
url: string; | ||
})[]; | ||
}; | ||
type Topic = { | ||
/** | ||
* A unix timestamp (milliseconds) | ||
*/ | ||
createdAt: number; | ||
/** | ||
* A unix timestamp (milliseconds) | ||
*/ | ||
updatedAt: number; | ||
/** | ||
* The name of this topic. | ||
*/ | ||
name: string; | ||
/** | ||
* A list of all subscribed endpoints | ||
*/ | ||
endpoints: Endpoint[]; | ||
}; | ||
declare class Topics { | ||
private readonly http; | ||
constructor(http: Requester); | ||
/** | ||
* Create a new topic with the given name and endpoints | ||
*/ | ||
addEndpoints(request: AddEndpointsRequest): Promise<void>; | ||
/** | ||
* Remove endpoints from a topic. | ||
*/ | ||
removeEndpoints(request: RemoveEndpointsRequest): Promise<void>; | ||
/** | ||
* Get a list of all topics. | ||
*/ | ||
list(): Promise<Topic[]>; | ||
/** | ||
* Get a single topic | ||
*/ | ||
get(name: string): Promise<Topic>; | ||
/** | ||
* Delete a topic | ||
*/ | ||
delete(name: string): Promise<void>; | ||
} | ||
/** | ||
* When the base64 string has unicode characters, atob doesn't decode | ||
* them correctly since it only outputs ASCII characters. Therefore, | ||
* instead of using atob, we properly decode them. | ||
* | ||
* If the decoding into unicode somehow fails, returns the result of atob | ||
* | ||
* https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem | ||
* | ||
* @param base64 encoded string | ||
*/ | ||
declare function decodeBase64(base64: string): string; | ||
type ClientConfig = { | ||
/** | ||
* Url of the qstash api server. | ||
* | ||
* This is only used for testing. | ||
* | ||
* @default "https://qstash.upstash.io" | ||
*/ | ||
baseUrl?: string; | ||
/** | ||
* The authorization token from the upstash console. | ||
*/ | ||
type AnalyticsConfig = { | ||
name: "helicone"; | ||
token: string; | ||
/** | ||
* Configure how the client should retry requests. | ||
*/ | ||
retry?: RetryConfig; | ||
}; | ||
type PublishRequest<TBody = BodyInit> = { | ||
/** | ||
* The message to send. | ||
* | ||
* This can be anything, but please set the `Content-Type` header accordingly. | ||
* | ||
* You can leave this empty if you want to send a message with no body. | ||
*/ | ||
body?: TBody; | ||
/** | ||
* Optionally send along headers with the message. | ||
* These headers will be sent to your destination. | ||
* | ||
* We highly recommend sending a `Content-Type` header along, as this will help your destination | ||
* server to understand the content of the message. | ||
*/ | ||
headers?: HeadersInit; | ||
/** | ||
* Optionally delay the delivery of this message. | ||
* | ||
* In seconds. | ||
* | ||
* @default undefined | ||
*/ | ||
delay?: number; | ||
/** | ||
* Optionally set the absolute delay of this message. | ||
* This will override the delay option. | ||
* The message will not delivered until the specified time. | ||
* | ||
* Unix timestamp in seconds. | ||
* | ||
* @default undefined | ||
*/ | ||
notBefore?: number; | ||
/** | ||
* Provide a unique id for deduplication. This id will be used to detect duplicate messages. | ||
* If a duplicate message is detected, the request will be accepted but not enqueued. | ||
* | ||
* We store deduplication ids for 90 days. Afterwards it is possible that the message with the | ||
* same deduplication id is delivered again. | ||
* | ||
* When scheduling a message, the deduplication happens before the schedule is created. | ||
* | ||
* @default undefined | ||
*/ | ||
deduplicationId?: string; | ||
/** | ||
* If true, the message content will get hashed and used as deduplication id. | ||
* If a duplicate message is detected, the request will be accepted but not enqueued. | ||
* | ||
* The content based hash includes the following values: | ||
* - All headers, except Upstash-Authorization, this includes all headers you are sending. | ||
* - The entire raw request body The destination from the url path | ||
* | ||
* We store deduplication ids for 90 days. Afterwards it is possible that the message with the | ||
* same deduplication id is delivered again. | ||
* | ||
* When scheduling a message, the deduplication happens before the schedule is created. | ||
* | ||
* @default false | ||
*/ | ||
contentBasedDeduplication?: boolean; | ||
/** | ||
* In case your destination server is unavaialble or returns a status code outside of the 200-299 | ||
* range, we will retry the request after a certain amount of time. | ||
* | ||
* Configure how many times you would like the delivery to be retried up to the maxRetries limit | ||
* defined in your plan. | ||
* | ||
* @default 3 | ||
*/ | ||
retries?: number; | ||
/** | ||
* Use a callback url to forward the response of your destination server to your callback url. | ||
* | ||
* The callback url must be publicly accessible | ||
* | ||
* @default undefined | ||
*/ | ||
callback?: string; | ||
/** | ||
* Use a failure callback url to handle messages that could not be delivered. | ||
* | ||
* The failure callback url must be publicly accessible | ||
* | ||
* @default undefined | ||
*/ | ||
failureCallback?: string; | ||
/** | ||
* The method to use when sending a request to your API | ||
* | ||
* @default `POST` | ||
*/ | ||
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; | ||
} & ({ | ||
/** | ||
* The url where the message should be sent to. | ||
*/ | ||
url: string; | ||
topic?: never; | ||
} | { | ||
url?: never; | ||
/** | ||
* The url where the message should be sent to. | ||
*/ | ||
topic: string; | ||
}); | ||
type PublishJsonRequest = Omit<PublishRequest, "body"> & { | ||
/** | ||
* The message to send. | ||
* This can be anything as long as it can be serialized to JSON. | ||
*/ | ||
body: unknown; | ||
type AnalyticsSetup = { | ||
baseURL?: string; | ||
defaultHeaders?: Record<string, string | undefined>; | ||
}; | ||
type EventsRequest = { | ||
cursor?: number; | ||
filter?: EventsRequestFilter; | ||
}; | ||
type EventsRequestFilter = { | ||
messageId?: string; | ||
state?: State; | ||
url?: string; | ||
topicName?: string; | ||
scheduleId?: string; | ||
queueName?: string; | ||
fromDate?: number; | ||
toDate?: number; | ||
count?: number; | ||
}; | ||
type GetEventsResponse = { | ||
cursor?: number; | ||
events: Event[]; | ||
}; | ||
type QueueRequest = { | ||
queueName?: string; | ||
}; | ||
declare class Client { | ||
http: Requester; | ||
constructor(config: ClientConfig); | ||
/** | ||
* Access the topic API. | ||
* | ||
* Create, read, update or delete topics. | ||
*/ | ||
get topics(): Topics; | ||
/** | ||
* Access the dlq API. | ||
* | ||
* List or remove messages from the DLQ. | ||
*/ | ||
get dlq(): DLQ; | ||
/** | ||
* Access the message API. | ||
* | ||
* Read or cancel messages. | ||
*/ | ||
get messages(): Messages; | ||
/** | ||
* Access the schedule API. | ||
* | ||
* Create, read or delete schedules. | ||
*/ | ||
get schedules(): Schedules; | ||
/** | ||
* Access the queue API. | ||
* | ||
* Create, read, update or delete queues. | ||
*/ | ||
queue(request?: QueueRequest): Queue; | ||
publish<TRequest extends PublishRequest>(request: TRequest): Promise<PublishResponse<TRequest>>; | ||
/** | ||
* publishJSON is a utility wrapper around `publish` that automatically serializes the body | ||
* and sets the `Content-Type` header to `application/json`. | ||
*/ | ||
publishJSON<TBody = unknown, TRequest extends PublishRequest<TBody> = PublishRequest<TBody>>(request: TRequest): Promise<PublishResponse<TRequest>>; | ||
/** | ||
* Batch publish messages to QStash. | ||
*/ | ||
batch(request: PublishRequest[]): Promise<PublishResponse<PublishRequest>[]>; | ||
/** | ||
* Batch publish messages to QStash, serializing each body to JSON. | ||
*/ | ||
batchJSON<TBody = unknown, TRequest extends PublishRequest<TBody> = PublishRequest<TBody>>(request: TRequest[]): Promise<PublishResponse<TRequest>[]>; | ||
/** | ||
* Retrieve your logs. | ||
* | ||
* The logs endpoint is paginated and returns only 100 logs at a time. | ||
* If you want to receive more logs, you can use the cursor to paginate. | ||
* | ||
* The cursor is a unix timestamp with millisecond precision | ||
* | ||
* @example | ||
* ```ts | ||
* let cursor = Date.now() | ||
* const logs: Log[] = [] | ||
* while (cursor > 0) { | ||
* const res = await qstash.logs({ cursor }) | ||
* logs.push(...res.logs) | ||
* cursor = res.cursor ?? 0 | ||
* } | ||
* ``` | ||
*/ | ||
events(request?: EventsRequest): Promise<GetEventsResponse>; | ||
} | ||
type PublishToUrlResponse = { | ||
messageId: string; | ||
url: string; | ||
deduplicated?: boolean; | ||
}; | ||
type PublishToTopicResponse = PublishToUrlResponse[]; | ||
type PublishResponse<R> = R extends { | ||
url: string; | ||
} ? PublishToUrlResponse : PublishToTopicResponse; | ||
declare const setupAnalytics: (analytics: AnalyticsConfig | undefined, providerApiKey: string, providerBaseUrl?: string, provider?: LLMOwner) => AnalyticsSetup; | ||
/** | ||
* Result of 500 Internal Server Error | ||
*/ | ||
declare class QstashError extends Error { | ||
constructor(message: string); | ||
declare class EmailProvider extends BaseProvider<"email", EmailOwner> { | ||
readonly apiKind = "email"; | ||
readonly batch: boolean; | ||
constructor(baseUrl: string, token: string, owner: EmailOwner, batch: boolean); | ||
getRoute(): string[]; | ||
getHeaders(_options: unknown): Record<string, string>; | ||
onFinish(providerInfo: ProviderInfo, _options: unknown): ProviderInfo; | ||
} | ||
declare class QstashRatelimitError extends QstashError { | ||
constructor(args: unknown); | ||
} | ||
declare const resend: ({ token, batch, }: { | ||
token: string; | ||
batch?: boolean; | ||
}) => EmailProvider; | ||
export { type AddEndpointsRequest, type BodyInit, Client, type CreateScheduleRequest, type Endpoint, type Event, type EventsRequest, type GetEventsResponse, type HeadersInit, type Message, Messages, type PublishJsonRequest, type PublishRequest, type PublishResponse, type PublishToTopicResponse, type PublishToUrlResponse, QstashError, QstashRatelimitError, type QueueRequest, Receiver, type ReceiverConfig, type RemoveEndpointsRequest, type Schedule, Schedules, SignatureError, type State, type Topic, Topics, type VerifyRequest, type WithCursor }; | ||
export { ChatRateLimit, QStashWorkflowAbort, QStashWorkflowError, QstashChatRatelimitError, QstashDailyRatelimitError, QstashError, QstashRatelimitError, RateLimit, decodeBase64, formatWorkflowError, resend, setupAnalytics }; |
1000
index.js
@@ -1,9 +0,120 @@ | ||
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } | ||
"use strict"; | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __getOwnPropNames = Object.getOwnPropertyNames; | ||
var __getProtoOf = Object.getPrototypeOf; | ||
var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
var __export = (target, all) => { | ||
for (var name in all) | ||
__defProp(target, name, { get: all[name], enumerable: true }); | ||
}; | ||
var __copyProps = (to, from, except, desc) => { | ||
if (from && typeof from === "object" || typeof from === "function") { | ||
for (let key of __getOwnPropNames(from)) | ||
if (!__hasOwnProp.call(to, key) && key !== except) | ||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
} | ||
return to; | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
// If the importer is in node compatibility mode or this is not an ESM | ||
// file that has been converted to a CommonJS file using a Babel- | ||
// compatible transform (i.e. "__esModule" has not been set), then set | ||
// "default" to the CommonJS "module.exports" for node compatibility. | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
mod | ||
)); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
// src/index.ts | ||
var src_exports = {}; | ||
__export(src_exports, { | ||
Chat: () => Chat, | ||
Client: () => Client, | ||
Messages: () => Messages, | ||
QStashWorkflowAbort: () => QStashWorkflowAbort, | ||
QStashWorkflowError: () => QStashWorkflowError, | ||
QstashChatRatelimitError: () => QstashChatRatelimitError, | ||
QstashDailyRatelimitError: () => QstashDailyRatelimitError, | ||
QstashError: () => QstashError, | ||
QstashRatelimitError: () => QstashRatelimitError, | ||
Receiver: () => Receiver, | ||
Schedules: () => Schedules, | ||
SignatureError: () => SignatureError, | ||
UrlGroups: () => UrlGroups, | ||
anthropic: () => anthropic, | ||
custom: () => custom, | ||
decodeBase64: () => decodeBase64, | ||
formatWorkflowError: () => formatWorkflowError, | ||
openai: () => openai, | ||
resend: () => resend, | ||
setupAnalytics: () => setupAnalytics, | ||
upstash: () => upstash | ||
}); | ||
module.exports = __toCommonJS(src_exports); | ||
var _chunkUUR7N6E6js = require('./chunk-UUR7N6E6.js'); | ||
// src/receiver.ts | ||
var jose = __toESM(require("jose")); | ||
var import_crypto_js = __toESM(require("crypto-js")); | ||
var SignatureError = class extends Error { | ||
constructor(message) { | ||
super(message); | ||
this.name = "SignatureError"; | ||
} | ||
}; | ||
var Receiver = class { | ||
currentSigningKey; | ||
nextSigningKey; | ||
constructor(config) { | ||
this.currentSigningKey = config.currentSigningKey; | ||
this.nextSigningKey = config.nextSigningKey; | ||
} | ||
/** | ||
* Verify the signature of a request. | ||
* | ||
* Tries to verify the signature with the current signing key. | ||
* If that fails, maybe because you have rotated the keys recently, it will | ||
* try to verify the signature with the next signing key. | ||
* | ||
* If that fails, the signature is invalid and a `SignatureError` is thrown. | ||
*/ | ||
async verify(request) { | ||
let payload; | ||
try { | ||
payload = await this.verifyWithKey(this.currentSigningKey, request); | ||
} catch { | ||
payload = await this.verifyWithKey(this.nextSigningKey, request); | ||
} | ||
this.verifyBodyAndUrl(payload, request); | ||
return true; | ||
} | ||
/** | ||
* Verify signature with a specific signing key | ||
*/ | ||
async verifyWithKey(key, request) { | ||
const jwt = await jose.jwtVerify(request.signature, new TextEncoder().encode(key), { | ||
issuer: "Upstash", | ||
clockTolerance: request.clockTolerance | ||
}).catch((error) => { | ||
throw new SignatureError(error.message); | ||
}); | ||
return jwt.payload; | ||
} | ||
verifyBodyAndUrl(payload, request) { | ||
const p = payload; | ||
if (request.url !== void 0 && p.sub !== request.url) { | ||
throw new SignatureError(`invalid subject: ${p.sub}, want: ${request.url}`); | ||
} | ||
const bodyHash = import_crypto_js.default.SHA256(request.body).toString(import_crypto_js.default.enc.Base64url); | ||
const padding = new RegExp(/=+$/); | ||
if (p.body.replace(padding, "") !== bodyHash.replace(padding, "")) { | ||
throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`); | ||
} | ||
} | ||
}; | ||
// src/client/dlq.ts | ||
var DLQ = class { | ||
http; | ||
constructor(http) { | ||
@@ -16,7 +127,24 @@ this.http = http; | ||
async listMessages(options) { | ||
return await this.http.request({ | ||
const filterPayload = { | ||
...options?.filter, | ||
topicName: options?.filter?.urlGroup | ||
}; | ||
const messagesPayload = await this.http.request({ | ||
method: "GET", | ||
path: ["v2", "dlq"], | ||
query: { cursor: _optionalChain([options, 'optionalAccess', _ => _.cursor]) } | ||
query: { | ||
cursor: options?.cursor, | ||
count: options?.count, | ||
...filterPayload | ||
} | ||
}); | ||
return { | ||
messages: messagesPayload.messages.map((message) => { | ||
return { | ||
...message, | ||
urlGroup: message.topicName | ||
}; | ||
}), | ||
cursor: messagesPayload.cursor | ||
}; | ||
} | ||
@@ -55,13 +183,76 @@ /** | ||
var QstashRatelimitError = class extends QstashError { | ||
limit; | ||
remaining; | ||
reset; | ||
constructor(args) { | ||
super(`You have been ratelimited. ${JSON.stringify(args)} `); | ||
super(`Exceeded burst rate limit. ${JSON.stringify(args)} `); | ||
this.name = "QstashRatelimitError"; | ||
this.limit = args.limit; | ||
this.remaining = args.remaining; | ||
this.reset = args.reset; | ||
} | ||
}; | ||
var QstashChatRatelimitError = class extends QstashError { | ||
limitRequests; | ||
limitTokens; | ||
remainingRequests; | ||
remainingTokens; | ||
resetRequests; | ||
resetTokens; | ||
constructor(args) { | ||
super(`Exceeded chat rate limit. ${JSON.stringify(args)} `); | ||
this.limitRequests = args["limit-requests"]; | ||
this.limitTokens = args["limit-tokens"]; | ||
this.remainingRequests = args["remaining-requests"]; | ||
this.remainingTokens = args["remaining-tokens"]; | ||
this.resetRequests = args["reset-requests"]; | ||
this.resetTokens = args["reset-tokens"]; | ||
} | ||
}; | ||
var QstashDailyRatelimitError = class extends QstashError { | ||
limit; | ||
remaining; | ||
reset; | ||
constructor(args) { | ||
super(`Exceeded daily rate limit. ${JSON.stringify(args)} `); | ||
this.limit = args.limit; | ||
this.remaining = args.remaining; | ||
this.reset = args.reset; | ||
this.name = "QstashChatRatelimitError"; | ||
} | ||
}; | ||
var QStashWorkflowError = class extends QstashError { | ||
constructor(message) { | ||
super(message); | ||
this.name = "QStashWorkflowError"; | ||
} | ||
}; | ||
var QStashWorkflowAbort = class extends Error { | ||
stepInfo; | ||
stepName; | ||
constructor(stepName, stepInfo) { | ||
super( | ||
`This is an Upstash Workflow error thrown after a step executes. It is expected to be raised. Make sure that you await for each step. Also, if you are using try/catch blocks, you should not wrap context.run/sleep/sleepUntil/call methods with try/catch. Aborting workflow after executing step '${stepName}'.` | ||
); | ||
this.name = "QStashWorkflowAbort"; | ||
this.stepName = stepName; | ||
this.stepInfo = stepInfo; | ||
} | ||
}; | ||
var formatWorkflowError = (error) => { | ||
return error instanceof Error ? { | ||
error: error.name, | ||
message: error.message | ||
} : { | ||
error: "Error", | ||
message: "An error occured while executing workflow." | ||
}; | ||
}; | ||
// src/client/http.ts | ||
var HttpClient = class { | ||
baseUrl; | ||
authorization; | ||
options; | ||
retry; | ||
constructor(config) { | ||
@@ -75,9 +266,72 @@ this.baseUrl = config.baseUrl.replace(/\/$/, ""); | ||
} : { | ||
attempts: _optionalChain([config, 'access', _2 => _2.retry, 'optionalAccess', _3 => _3.retries]) ? config.retry.retries + 1 : 5, | ||
backoff: _nullishCoalesce(_optionalChain([config, 'access', _4 => _4.retry, 'optionalAccess', _5 => _5.backoff]), () => ( ((retryCount) => Math.exp(retryCount) * 50))) | ||
attempts: config.retry?.retries ?? 5, | ||
backoff: config.retry?.backoff ?? ((retryCount) => Math.exp(retryCount) * 50) | ||
}; | ||
} | ||
async request(request) { | ||
const { response } = await this.requestWithBackoff(request); | ||
if (request.parseResponseAsJson === false) { | ||
return void 0; | ||
} | ||
return await response.json(); | ||
} | ||
async *requestStream(request) { | ||
const { response } = await this.requestWithBackoff(request); | ||
if (!response.body) { | ||
throw new Error("No response body"); | ||
} | ||
const body = response.body; | ||
const reader = body.getReader(); | ||
const decoder = new TextDecoder(); | ||
try { | ||
while (true) { | ||
const { done, value } = await reader.read(); | ||
if (done) { | ||
break; | ||
} | ||
const chunkText = decoder.decode(value, { stream: true }); | ||
const chunks = chunkText.split("\n").filter(Boolean); | ||
for (const chunk of chunks) { | ||
if (chunk.startsWith("data: ")) { | ||
const data = chunk.slice(6); | ||
if (data === "[DONE]") { | ||
break; | ||
} | ||
yield JSON.parse(data); | ||
} | ||
} | ||
} | ||
} finally { | ||
await reader.cancel(); | ||
} | ||
} | ||
requestWithBackoff = async (request) => { | ||
const [url, requestOptions] = this.processRequest(request); | ||
let response = void 0; | ||
let error = void 0; | ||
for (let index = 0; index <= this.retry.attempts; index++) { | ||
try { | ||
response = await fetch(url.toString(), requestOptions); | ||
break; | ||
} catch (error_) { | ||
error = error_; | ||
if (index < this.retry.attempts) { | ||
await new Promise((r) => setTimeout(r, this.retry.backoff(index))); | ||
} | ||
} | ||
} | ||
if (!response) { | ||
throw error ?? new Error("Exhausted all retries"); | ||
} | ||
await this.checkResponse(response); | ||
return { | ||
response, | ||
error | ||
}; | ||
}; | ||
processRequest = (request) => { | ||
const headers = new Headers(request.headers); | ||
headers.set("Authorization", this.authorization); | ||
if (!headers.has("Authorization")) { | ||
headers.set("Authorization", this.authorization); | ||
} | ||
const requestOptions = { | ||
@@ -89,3 +343,3 @@ method: request.method, | ||
}; | ||
const url = new URL([this.baseUrl, ...request.path].join("/")); | ||
const url = new URL([request.baseUrl ?? this.baseUrl, ...request.path].join("/")); | ||
if (request.query) { | ||
@@ -98,17 +352,22 @@ for (const [key, value] of Object.entries(request.query)) { | ||
} | ||
let response = void 0; | ||
let error = void 0; | ||
for (let index = 0; index < this.retry.attempts; index++) { | ||
try { | ||
response = await fetch(url.toString(), requestOptions); | ||
break; | ||
} catch (error_) { | ||
error = error_; | ||
await new Promise((r) => setTimeout(r, this.retry.backoff(index))); | ||
return [url.toString(), requestOptions]; | ||
}; | ||
async checkResponse(response) { | ||
if (response.status === 429) { | ||
if (response.headers.get("x-ratelimit-limit-requests")) { | ||
throw new QstashChatRatelimitError({ | ||
"limit-requests": response.headers.get("x-ratelimit-limit-requests"), | ||
"limit-tokens": response.headers.get("x-ratelimit-limit-tokens"), | ||
"remaining-requests": response.headers.get("x-ratelimit-remaining-requests"), | ||
"remaining-tokens": response.headers.get("x-ratelimit-remaining-tokens"), | ||
"reset-requests": response.headers.get("x-ratelimit-reset-requests"), | ||
"reset-tokens": response.headers.get("x-ratelimit-reset-tokens") | ||
}); | ||
} else if (response.headers.get("RateLimit-Limit")) { | ||
throw new QstashDailyRatelimitError({ | ||
limit: response.headers.get("RateLimit-Limit"), | ||
remaining: response.headers.get("RateLimit-Remaining"), | ||
reset: response.headers.get("RateLimit-Reset") | ||
}); | ||
} | ||
} | ||
if (!response) { | ||
throw _nullishCoalesce(error, () => ( new Error("Exhausted all retries"))); | ||
} | ||
if (response.status === 429) { | ||
throw new QstashRatelimitError({ | ||
@@ -124,12 +383,176 @@ limit: response.headers.get("Burst-RateLimit-Limit"), | ||
} | ||
if (request.parseResponseAsJson === false) { | ||
return void 0; | ||
} | ||
}; | ||
// src/client/llm/providers.ts | ||
var setupAnalytics = (analytics, providerApiKey, providerBaseUrl, provider) => { | ||
if (!analytics) | ||
return {}; | ||
switch (analytics.name) { | ||
case "helicone": { | ||
switch (provider) { | ||
case "upstash": { | ||
return { | ||
baseURL: "https://qstash.helicone.ai/llm/v1/chat/completions", | ||
defaultHeaders: { | ||
"Helicone-Auth": `Bearer ${analytics.token}`, | ||
Authorization: `Bearer ${providerApiKey}` | ||
} | ||
}; | ||
} | ||
default: { | ||
return { | ||
baseURL: "https://gateway.helicone.ai/v1/chat/completions", | ||
defaultHeaders: { | ||
"Helicone-Auth": `Bearer ${analytics.token}`, | ||
"Helicone-Target-Url": providerBaseUrl, | ||
Authorization: `Bearer ${providerApiKey}` | ||
} | ||
}; | ||
} | ||
} | ||
} | ||
return await response.json(); | ||
default: { | ||
throw new Error("Unknown analytics provider"); | ||
} | ||
} | ||
}; | ||
// src/client/llm/chat.ts | ||
var Chat = class _Chat { | ||
http; | ||
token; | ||
constructor(http, token) { | ||
this.http = http; | ||
this.token = token; | ||
} | ||
static toChatRequest(request) { | ||
const messages = []; | ||
messages.push( | ||
{ role: "system", content: request.system }, | ||
{ role: "user", content: request.user } | ||
); | ||
const chatRequest = { ...request, messages }; | ||
return chatRequest; | ||
} | ||
/** | ||
* Calls the Upstash completions api given a ChatRequest. | ||
* | ||
* Returns a ChatCompletion or a stream of ChatCompletionChunks | ||
* if stream is enabled. | ||
* | ||
* @param request ChatRequest with messages | ||
* @returns Chat completion or stream | ||
*/ | ||
create = async (request) => { | ||
if (request.provider.owner != "upstash") | ||
return this.createThirdParty(request); | ||
const body = JSON.stringify(request); | ||
let baseUrl = void 0; | ||
let headers = { | ||
"Content-Type": "application/json", | ||
Authorization: `Bearer ${this.token}`, | ||
..."stream" in request && request.stream ? { | ||
Connection: "keep-alive", | ||
Accept: "text/event-stream", | ||
"Cache-Control": "no-cache" | ||
} : {} | ||
}; | ||
if (request.analytics) { | ||
const { baseURL, defaultHeaders } = setupAnalytics( | ||
{ name: "helicone", token: request.analytics.token }, | ||
this.getAuthorizationToken(), | ||
request.provider.baseUrl, | ||
"upstash" | ||
); | ||
headers = { ...headers, ...defaultHeaders }; | ||
baseUrl = baseURL; | ||
} | ||
const path = request.analytics ? [] : ["llm", "v1", "chat", "completions"]; | ||
return "stream" in request && request.stream ? this.http.requestStream({ | ||
path, | ||
method: "POST", | ||
headers, | ||
baseUrl, | ||
body | ||
}) : this.http.request({ | ||
path, | ||
method: "POST", | ||
headers, | ||
baseUrl, | ||
body | ||
}); | ||
}; | ||
/** | ||
* Calls the Upstash completions api given a ChatRequest. | ||
* | ||
* Returns a ChatCompletion or a stream of ChatCompletionChunks | ||
* if stream is enabled. | ||
* | ||
* @param request ChatRequest with messages | ||
* @returns Chat completion or stream | ||
*/ | ||
createThirdParty = async (request) => { | ||
const { baseUrl, token, owner, organization } = request.provider; | ||
if (owner === "upstash") | ||
throw new Error("Upstash is not 3rd party provider!"); | ||
delete request.provider; | ||
delete request.system; | ||
const analytics = request.analytics; | ||
delete request.analytics; | ||
const body = JSON.stringify(request); | ||
const isAnalyticsEnabled = analytics?.name && analytics.token; | ||
const analyticsConfig = analytics?.name && analytics.token ? setupAnalytics({ name: analytics.name, token: analytics.token }, token, baseUrl, owner) : { defaultHeaders: void 0, baseURL: baseUrl }; | ||
const isStream = "stream" in request && request.stream; | ||
const headers = { | ||
"Content-Type": "application/json", | ||
Authorization: `Bearer ${token}`, | ||
...organization ? { | ||
"OpenAI-Organization": organization | ||
} : {}, | ||
...isStream ? { | ||
Connection: "keep-alive", | ||
Accept: "text/event-stream", | ||
"Cache-Control": "no-cache" | ||
} : {}, | ||
...analyticsConfig.defaultHeaders | ||
}; | ||
const response = await this.http[isStream ? "requestStream" : "request"]({ | ||
path: isAnalyticsEnabled ? [] : ["v1", "chat", "completions"], | ||
method: "POST", | ||
headers, | ||
body, | ||
baseUrl: analyticsConfig.baseURL | ||
}); | ||
return response; | ||
}; | ||
// Helper method to get the authorization token | ||
getAuthorizationToken() { | ||
const authHeader = String(this.http.authorization); | ||
const match = /Bearer (.+)/.exec(authHeader); | ||
if (!match) { | ||
throw new Error("Invalid authorization header format"); | ||
} | ||
return match[1]; | ||
} | ||
/** | ||
* Calls the Upstash completions api given a PromptRequest. | ||
* | ||
* Returns a ChatCompletion or a stream of ChatCompletionChunks | ||
* if stream is enabled. | ||
* | ||
* @param request PromptRequest with system and user messages. | ||
* Note that system parameter shouldn't be passed in the case of | ||
* mistralai/Mistral-7B-Instruct-v0.2 model. | ||
* @returns Chat completion or stream | ||
*/ | ||
prompt = async (request) => { | ||
const chatRequest = _Chat.toChatRequest(request); | ||
return this.create(chatRequest); | ||
}; | ||
}; | ||
// src/client/messages.ts | ||
var Messages = class { | ||
http; | ||
constructor(http) { | ||
@@ -142,6 +565,11 @@ this.http = http; | ||
async get(messageId) { | ||
return await this.http.request({ | ||
const messagePayload = await this.http.request({ | ||
method: "GET", | ||
path: ["v2", "messages", messageId] | ||
}); | ||
const message = { | ||
...messagePayload, | ||
urlGroup: messagePayload.topicName | ||
}; | ||
return message; | ||
} | ||
@@ -158,4 +586,177 @@ /** | ||
} | ||
async deleteMany(messageIds) { | ||
const result = await this.http.request({ | ||
method: "DELETE", | ||
path: ["v2", "messages"], | ||
headers: { "Content-Type": "application/json" }, | ||
body: JSON.stringify({ messageIds }) | ||
}); | ||
return result.cancelled; | ||
} | ||
async deleteAll() { | ||
const result = await this.http.request({ | ||
method: "DELETE", | ||
path: ["v2", "messages"] | ||
}); | ||
return result.cancelled; | ||
} | ||
}; | ||
// src/client/api/base.ts | ||
var BaseProvider = class { | ||
baseUrl; | ||
token; | ||
owner; | ||
constructor(baseUrl, token, owner) { | ||
this.baseUrl = baseUrl; | ||
this.token = token; | ||
this.owner = owner; | ||
} | ||
getUrl() { | ||
return `${this.baseUrl}/${this.getRoute().join("/")}`; | ||
} | ||
}; | ||
// src/client/api/llm.ts | ||
var LLMProvider = class extends BaseProvider { | ||
apiKind = "llm"; | ||
organization; | ||
constructor(baseUrl, token, owner, organization) { | ||
super(baseUrl, token, owner); | ||
this.organization = organization; | ||
} | ||
getRoute() { | ||
return this.owner === "anthropic" ? ["v1", "messages"] : ["v1", "chat", "completions"]; | ||
} | ||
getHeaders(options) { | ||
if (this.owner === "upstash" && !options.analytics) { | ||
return { "content-type": "application/json" }; | ||
} | ||
const header = this.owner === "anthropic" ? "x-api-key" : "authorization"; | ||
const headerValue = this.owner === "anthropic" ? this.token : `Bearer ${this.token}`; | ||
const headers = { | ||
[header]: headerValue, | ||
"content-type": "application/json" | ||
}; | ||
if (this.owner === "openai" && this.organization) { | ||
headers["OpenAI-Organization"] = this.organization; | ||
} | ||
return headers; | ||
} | ||
/** | ||
* Checks if callback exists and adds analytics in place if it's set. | ||
* | ||
* @param request | ||
* @param options | ||
*/ | ||
onFinish(providerInfo, options) { | ||
if (options.analytics) { | ||
return updateWithAnalytics(providerInfo, options.analytics); | ||
} | ||
return providerInfo; | ||
} | ||
}; | ||
var upstash = () => { | ||
return new LLMProvider("https://qstash.upstash.io/llm", "", "upstash"); | ||
}; | ||
var openai = ({ | ||
token, | ||
organization | ||
}) => { | ||
return new LLMProvider("https://api.openai.com", token, "openai", organization); | ||
}; | ||
var anthropic = ({ token }) => { | ||
return new LLMProvider("https://api.anthropic.com", token, "anthropic"); | ||
}; | ||
var custom = ({ | ||
baseUrl, | ||
token | ||
}) => { | ||
const trimmedBaseUrl = baseUrl.replace(/\/(v1\/)?chat\/completions$/, ""); | ||
return new LLMProvider(trimmedBaseUrl, token, "custom"); | ||
}; | ||
// src/client/api/utils.ts | ||
var getProviderInfo = (api, upstashToken) => { | ||
const { name, provider, ...parameters } = api; | ||
const finalProvider = provider ?? upstash(); | ||
if (finalProvider.owner === "upstash" && !finalProvider.token) { | ||
finalProvider.token = upstashToken; | ||
} | ||
if (!finalProvider.baseUrl) | ||
throw new TypeError("baseUrl cannot be empty or undefined!"); | ||
if (!finalProvider.token) | ||
throw new TypeError("token cannot be empty or undefined!"); | ||
if (finalProvider.apiKind !== name) { | ||
throw new TypeError( | ||
`Unexpected api name. Expected '${finalProvider.apiKind}', received ${name}` | ||
); | ||
} | ||
const providerInfo = { | ||
url: finalProvider.getUrl(), | ||
baseUrl: finalProvider.baseUrl, | ||
route: finalProvider.getRoute(), | ||
appendHeaders: finalProvider.getHeaders(parameters), | ||
owner: finalProvider.owner | ||
}; | ||
return finalProvider.onFinish(providerInfo, parameters); | ||
}; | ||
var processApi = (request, upstashToken) => { | ||
if (!request.api) { | ||
return request; | ||
} | ||
const { url, appendHeaders, owner } = getProviderInfo(request.api, upstashToken); | ||
if (request.api.name === "llm") { | ||
const callback = request.callback; | ||
if (!callback) { | ||
throw new TypeError("Callback cannot be undefined when using LLM api."); | ||
} | ||
return { | ||
...request, | ||
// @ts-expect-error undici header conflict | ||
headers: new Headers({ | ||
...request.headers, | ||
...appendHeaders | ||
}), | ||
...owner === "upstash" && !request.api.analytics ? { api: { name: "llm" }, url: void 0, callback } : { url, api: void 0 } | ||
}; | ||
} else { | ||
return { | ||
...request, | ||
// @ts-expect-error undici header conflict | ||
headers: new Headers({ | ||
...request.headers, | ||
...appendHeaders | ||
}), | ||
url, | ||
api: void 0 | ||
}; | ||
} | ||
}; | ||
function updateWithAnalytics(providerInfo, analytics) { | ||
switch (analytics.name) { | ||
case "helicone": { | ||
providerInfo.appendHeaders["Helicone-Auth"] = `Bearer ${analytics.token}`; | ||
if (providerInfo.owner === "upstash") { | ||
updateProviderInfo(providerInfo, "https://qstash.helicone.ai", [ | ||
"llm", | ||
...providerInfo.route | ||
]); | ||
} else { | ||
providerInfo.appendHeaders["Helicone-Target-Url"] = providerInfo.baseUrl; | ||
updateProviderInfo(providerInfo, "https://gateway.helicone.ai", providerInfo.route); | ||
} | ||
return providerInfo; | ||
} | ||
default: { | ||
throw new Error("Unknown analytics provider"); | ||
} | ||
} | ||
} | ||
function updateProviderInfo(providerInfo, baseUrl, route) { | ||
providerInfo.baseUrl = baseUrl; | ||
providerInfo.route = route; | ||
providerInfo.url = `${baseUrl}/${route.join("/")}`; | ||
} | ||
// src/client/utils.ts | ||
@@ -179,5 +780,9 @@ var isIgnoredHeader = (header) => { | ||
const headers = prefixHeaders(new Headers(request.headers)); | ||
headers.set("Upstash-Method", _nullishCoalesce(request.method, () => ( "POST"))); | ||
headers.set("Upstash-Method", request.method ?? "POST"); | ||
if (request.delay !== void 0) { | ||
headers.set("Upstash-Delay", `${request.delay.toFixed(0)}s`); | ||
if (typeof request.delay === "string") { | ||
headers.set("Upstash-Delay", request.delay); | ||
} else { | ||
headers.set("Upstash-Delay", `${request.delay.toFixed(0)}s`); | ||
} | ||
} | ||
@@ -190,3 +795,3 @@ if (request.notBefore !== void 0) { | ||
} | ||
if (request.contentBasedDeduplication !== void 0) { | ||
if (request.contentBasedDeduplication) { | ||
headers.set("Upstash-Content-Based-Deduplication", "true"); | ||
@@ -203,9 +808,48 @@ } | ||
} | ||
if (request.timeout !== void 0) { | ||
if (typeof request.timeout === "string") { | ||
headers.set("Upstash-Timeout", request.timeout); | ||
} else { | ||
headers.set("Upstash-Timeout", `${request.timeout}s`); | ||
} | ||
} | ||
return headers; | ||
} | ||
function getRequestPath(request) { | ||
const nonApiPath = request.url ?? request.urlGroup ?? request.topic; | ||
if (nonApiPath) | ||
return nonApiPath; | ||
if (request.api?.name === "llm") | ||
return `api/llm`; | ||
if (request.api?.name === "email") { | ||
const providerInfo = getProviderInfo(request.api, "not-needed"); | ||
return providerInfo.baseUrl; | ||
} | ||
throw new QstashError(`Failed to infer request path for ${JSON.stringify(request)}`); | ||
} | ||
function decodeBase64(base64) { | ||
try { | ||
const binString = atob(base64); | ||
const intArray = Uint8Array.from(binString, (m) => m.codePointAt(0)); | ||
return new TextDecoder().decode(intArray); | ||
} catch (error) { | ||
try { | ||
const result = atob(base64); | ||
console.warn( | ||
`Upstash QStash: Failed while decoding base64 "${base64}". Decoding with atob and returning it instead. ${error}` | ||
); | ||
return result; | ||
} catch (error2) { | ||
console.warn( | ||
`Upstash QStash: Failed to decode base64 "${base64}" with atob. Returning it as it is. ${error2}` | ||
); | ||
return base64; | ||
} | ||
} | ||
} | ||
// src/client/queue.ts | ||
var Queue = class { | ||
http; | ||
queueName; | ||
constructor(http, queueName) { | ||
@@ -224,3 +868,4 @@ this.http = http; | ||
queueName: this.queueName, | ||
parallelism: request.parallelism | ||
parallelism: request.parallelism ?? 1, | ||
paused: request.paused ?? false | ||
}; | ||
@@ -279,3 +924,3 @@ await this.http.request({ | ||
const headers = processHeaders(request); | ||
const destination = _nullishCoalesce(request.url, () => ( request.topic)); | ||
const destination = getRequestPath(request); | ||
const response = await this.http.request({ | ||
@@ -295,5 +940,8 @@ path: ["v2", "enqueue", this.queueName, destination], | ||
headers.set("Content-Type", "application/json"); | ||
request.headers = headers; | ||
const upstashToken = String(this.http.authorization).split("Bearer ")[1]; | ||
const nonApiRequest = processApi(request, upstashToken); | ||
const response = await this.enqueue({ | ||
...request, | ||
body: JSON.stringify(request.body), | ||
...nonApiRequest, | ||
body: JSON.stringify(nonApiRequest.body), | ||
headers | ||
@@ -303,2 +951,31 @@ }); | ||
} | ||
/** | ||
* Pauses the queue. | ||
* | ||
* A paused queue will not deliver messages until | ||
* it is resumed. | ||
*/ | ||
async pause() { | ||
if (!this.queueName) { | ||
throw new Error("Please provide a queue name to the Queue constructor"); | ||
} | ||
await this.http.request({ | ||
method: "POST", | ||
path: ["v2", "queues", this.queueName, "pause"], | ||
parseResponseAsJson: false | ||
}); | ||
} | ||
/** | ||
* Resumes the queue. | ||
*/ | ||
async resume() { | ||
if (!this.queueName) { | ||
throw new Error("Please provide a queue name to the Queue constructor"); | ||
} | ||
await this.http.request({ | ||
method: "POST", | ||
path: ["v2", "queues", this.queueName, "resume"], | ||
parseResponseAsJson: false | ||
}); | ||
} | ||
}; | ||
@@ -308,3 +985,3 @@ | ||
var Schedules = class { | ||
http; | ||
constructor(http) { | ||
@@ -326,3 +1003,7 @@ this.http = http; | ||
if (request.delay !== void 0) { | ||
headers.set("Upstash-Delay", `${request.delay.toFixed(0)}s`); | ||
if (typeof request.delay === "string") { | ||
headers.set("Upstash-Delay", request.delay); | ||
} else { | ||
headers.set("Upstash-Delay", `${request.delay.toFixed(0)}s`); | ||
} | ||
} | ||
@@ -338,2 +1019,15 @@ if (request.retries !== void 0) { | ||
} | ||
if (request.timeout !== void 0) { | ||
if (typeof request.timeout === "string") { | ||
headers.set("Upstash-Timeout", request.timeout); | ||
} else { | ||
headers.set("Upstash-Timeout", `${request.timeout}s`); | ||
} | ||
} | ||
if (request.scheduleId !== void 0) { | ||
headers.set("Upstash-Schedule-Id", request.scheduleId); | ||
} | ||
if (request.queueName !== void 0) { | ||
headers.set("Upstash-Queue-Name", request.queueName); | ||
} | ||
return await this.http.request({ | ||
@@ -374,7 +1068,30 @@ method: "POST", | ||
} | ||
/** | ||
* Pauses the schedule. | ||
* | ||
* A paused schedule will not deliver messages until | ||
* it is resumed. | ||
*/ | ||
async pause({ schedule }) { | ||
await this.http.request({ | ||
method: "PATCH", | ||
path: ["v2", "schedules", schedule, "pause"], | ||
parseResponseAsJson: false | ||
}); | ||
} | ||
/** | ||
* Resumes the schedule. | ||
*/ | ||
async resume({ schedule }) { | ||
await this.http.request({ | ||
method: "PATCH", | ||
path: ["v2", "schedules", schedule, "resume"], | ||
parseResponseAsJson: false | ||
}); | ||
} | ||
}; | ||
// src/client/topics.ts | ||
var Topics = class { | ||
// src/client/url-groups.ts | ||
var UrlGroups = class { | ||
http; | ||
constructor(http) { | ||
@@ -384,3 +1101,3 @@ this.http = http; | ||
/** | ||
* Create a new topic with the given name and endpoints | ||
* Create a new url group with the given name and endpoints | ||
*/ | ||
@@ -397,3 +1114,3 @@ async addEndpoints(request) { | ||
/** | ||
* Remove endpoints from a topic. | ||
* Remove endpoints from a url group. | ||
*/ | ||
@@ -410,3 +1127,3 @@ async removeEndpoints(request) { | ||
/** | ||
* Get a list of all topics. | ||
* Get a list of all url groups. | ||
*/ | ||
@@ -420,3 +1137,3 @@ async list() { | ||
/** | ||
* Get a single topic | ||
* Get a single url group | ||
*/ | ||
@@ -430,3 +1147,3 @@ async get(name) { | ||
/** | ||
* Delete a topic | ||
* Delete a url group | ||
*/ | ||
@@ -442,5 +1159,37 @@ async delete(name) { | ||
// src/client/workflow/context.ts | ||
var import_neverthrow2 = require("neverthrow"); | ||
// src/client/workflow/workflow-requests.ts | ||
var import_neverthrow = require("neverthrow"); | ||
// src/client/workflow/workflow-parser.ts | ||
var import_neverthrow3 = require("neverthrow"); | ||
// src/client/workflow/index.ts | ||
var Workflow = class { | ||
http; | ||
constructor(http) { | ||
this.http = http; | ||
} | ||
/** | ||
* Cancel an ongoing workflow | ||
* | ||
* @param workflowRunId run id of the workflow to delete | ||
* @returns true if workflow is succesfully deleted. Otherwise throws QStashError | ||
*/ | ||
async cancel(workflowRunId) { | ||
const result = await this.http.request({ | ||
path: ["v2", "workflows", "runs", `${workflowRunId}?cancel=true`], | ||
method: "DELETE", | ||
parseResponseAsJson: false | ||
}); | ||
return result ?? true; | ||
} | ||
}; | ||
// src/client/client.ts | ||
var Client = class { | ||
http; | ||
token; | ||
constructor(config) { | ||
@@ -452,4 +1201,15 @@ this.http = new HttpClient({ | ||
}); | ||
this.token = config.token; | ||
} | ||
/** | ||
* Access the urlGroup API. | ||
* | ||
* Create, read, update or delete urlGroups. | ||
*/ | ||
get urlGroups() { | ||
return new UrlGroups(this.http); | ||
} | ||
/** | ||
* Deprecated. Use urlGroups instead. | ||
* | ||
* Access the topic API. | ||
@@ -460,3 +1220,3 @@ * | ||
get topics() { | ||
return new Topics(this.http); | ||
return this.urlGroups; | ||
} | ||
@@ -488,2 +1248,14 @@ /** | ||
/** | ||
* Access the workflow API. | ||
* | ||
* cancel workflows. | ||
* | ||
* @deprecated as of version 2.7.17. Will be removed in qstash-js 3.0.0. | ||
* Please use @upstash/workflow instead https://github.com/upstash/workflow-js | ||
* Migration Guide: https://upstash.com/docs/workflow/migration | ||
*/ | ||
get workflow() { | ||
return new Workflow(this.http); | ||
} | ||
/** | ||
* Access the queue API. | ||
@@ -494,8 +1266,16 @@ * | ||
queue(request) { | ||
return new Queue(this.http, _optionalChain([request, 'optionalAccess', _6 => _6.queueName])); | ||
return new Queue(this.http, request?.queueName); | ||
} | ||
/** | ||
* Access the Chat API | ||
* | ||
* Call the create or prompt methods | ||
*/ | ||
chat() { | ||
return new Chat(this.http, this.token); | ||
} | ||
async publish(request) { | ||
const headers = processHeaders(request); | ||
const response = await this.http.request({ | ||
path: ["v2", "publish", _nullishCoalesce(request.url, () => ( request.topic))], | ||
path: ["v2", "publish", getRequestPath(request)], | ||
body: request.body, | ||
@@ -514,6 +1294,8 @@ headers, | ||
headers.set("Content-Type", "application/json"); | ||
request.headers = headers; | ||
const upstashToken = String(this.http.authorization).split("Bearer ")[1]; | ||
const nonApiRequest = processApi(request, upstashToken); | ||
const response = await this.publish({ | ||
...request, | ||
headers, | ||
body: JSON.stringify(request.body) | ||
...nonApiRequest, | ||
body: JSON.stringify(nonApiRequest.body) | ||
}); | ||
@@ -531,5 +1313,6 @@ return response; | ||
messages.push({ | ||
destination: _nullishCoalesce(message.url, () => ( message.topic)), | ||
destination: getRequestPath(message), | ||
headers: headerEntries, | ||
body: message.body | ||
body: message.body, | ||
...message.queueName && { queue: message.queueName } | ||
}); | ||
@@ -545,3 +1328,4 @@ } | ||
}); | ||
return response; | ||
const arrayResposne = Array.isArray(response) ? response : [response]; | ||
return arrayResposne; | ||
} | ||
@@ -552,3 +1336,3 @@ /** | ||
async batchJSON(request) { | ||
for (const message of request) { | ||
const batchPayload = request.map((message) => { | ||
if ("body" in message) { | ||
@@ -558,5 +1342,8 @@ message.body = JSON.stringify(message.body); | ||
message.headers = new Headers(message.headers); | ||
message.headers.set("Content-Type", "application/json"); | ||
} | ||
const response = await this.batch(request); | ||
const upstashToken = String(this.http.authorization).split("Bearer ")[1]; | ||
const nonApiMessage = processApi(message, upstashToken); | ||
nonApiMessage.headers.set("Content-Type", "application/json"); | ||
return nonApiMessage; | ||
}); | ||
const response = await this.batch(batchPayload); | ||
return response; | ||
@@ -585,14 +1372,18 @@ } | ||
const query = {}; | ||
if (_optionalChain([request, 'optionalAccess', _7 => _7.cursor]) && request.cursor > 0) { | ||
if (typeof request?.cursor === "number" && request.cursor > 0) { | ||
query.cursor = request.cursor.toString(); | ||
} else if (typeof request?.cursor === "string" && request.cursor !== "") { | ||
query.cursor = request.cursor; | ||
} | ||
for (const [key, value] of Object.entries(_nullishCoalesce(_optionalChain([request, 'optionalAccess', _8 => _8.filter]), () => ( {})))) { | ||
for (const [key, value] of Object.entries(request?.filter ?? {})) { | ||
if (typeof value === "number" && value < 0) { | ||
continue; | ||
} | ||
if (typeof value !== "undefined") { | ||
if (key === "urlGroup") { | ||
query.topicName = value.toString(); | ||
} else if (typeof value !== "undefined") { | ||
query[key] = value.toString(); | ||
} | ||
} | ||
const response = await this.http.request({ | ||
const responsePayload = await this.http.request({ | ||
path: ["v2", "events"], | ||
@@ -602,14 +1393,63 @@ method: "GET", | ||
}); | ||
return response; | ||
return { | ||
cursor: responsePayload.cursor, | ||
events: responsePayload.events.map((event) => { | ||
return { | ||
...event, | ||
urlGroup: event.topicName | ||
}; | ||
}) | ||
}; | ||
} | ||
}; | ||
exports.Client = Client; exports.Messages = Messages; exports.QstashError = QstashError; exports.QstashRatelimitError = QstashRatelimitError; exports.Receiver = _chunkUUR7N6E6js.Receiver; exports.Schedules = Schedules; exports.SignatureError = _chunkUUR7N6E6js.SignatureError; exports.Topics = Topics; | ||
// src/client/api/email.ts | ||
var EmailProvider = class extends BaseProvider { | ||
apiKind = "email"; | ||
batch; | ||
constructor(baseUrl, token, owner, batch) { | ||
super(baseUrl, token, owner); | ||
this.batch = batch; | ||
} | ||
getRoute() { | ||
return this.batch ? ["emails", "batch"] : ["emails"]; | ||
} | ||
getHeaders(_options) { | ||
return { | ||
"upstash-forward-authorization": `Bearer ${this.token}` | ||
}; | ||
} | ||
onFinish(providerInfo, _options) { | ||
return providerInfo; | ||
} | ||
}; | ||
var resend = ({ | ||
token, | ||
batch = false | ||
}) => { | ||
return new EmailProvider("https://api.resend.com", token, "resend", batch); | ||
}; | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
Chat, | ||
Client, | ||
Messages, | ||
QStashWorkflowAbort, | ||
QStashWorkflowError, | ||
QstashChatRatelimitError, | ||
QstashDailyRatelimitError, | ||
QstashError, | ||
QstashRatelimitError, | ||
Receiver, | ||
Schedules, | ||
SignatureError, | ||
UrlGroups, | ||
anthropic, | ||
custom, | ||
decodeBase64, | ||
formatWorkflowError, | ||
openai, | ||
resend, | ||
setupAnalytics, | ||
upstash | ||
}); |
import { NextApiHandler } from 'next'; | ||
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server'; | ||
import { NextRequest, NextFetchEvent } from 'next/server'; | ||
import { a3 as RouteFunction, a4 as WorkflowServeOptions } from './client-BY4y-4To.js'; | ||
import 'neverthrow'; | ||
@@ -21,6 +23,26 @@ type VerifySignatureConfig = { | ||
declare function verifySignature(handler: NextApiHandler, config?: VerifySignatureConfig): NextApiHandler; | ||
declare function verifySignatureEdge(handler: (request: NextRequest, nfe?: NextFetchEvent) => NextResponse | Promise<NextResponse>, config?: VerifySignatureConfig): (request: NextRequest, nfe: NextFetchEvent) => Promise<NextResponse<unknown>>; | ||
type VerifySignatureAppRouterResponse = NextResponse | Promise<NextResponse> | Response | Promise<Response>; | ||
declare function verifySignatureAppRouter(handler: ((request: Request) => VerifySignatureAppRouterResponse) | ((request: NextRequest) => VerifySignatureAppRouterResponse), config?: VerifySignatureConfig): (request: NextRequest | Request) => Promise<Response>; | ||
declare function verifySignatureEdge(handler: (request: NextRequest, nfe?: NextFetchEvent) => Response | Promise<Response>, config?: VerifySignatureConfig): (request: NextRequest, nfe: NextFetchEvent) => Promise<Response>; | ||
type VerifySignatureAppRouterResponse = Response | Promise<Response>; | ||
declare function verifySignatureAppRouter(handler: ((request: Request, params?: unknown) => VerifySignatureAppRouterResponse) | ((request: NextRequest, params?: unknown) => VerifySignatureAppRouterResponse), config?: VerifySignatureConfig): (request: NextRequest | Request, params?: unknown) => Promise<Response>; | ||
/** | ||
* Serve method to serve a Upstash Workflow in a Nextjs project | ||
* | ||
* See for options https://upstash.com/docs/qstash/workflows/basics/serve | ||
* | ||
* @param routeFunction workflow function | ||
* @param options workflow options | ||
* @returns | ||
* | ||
* @deprecated as of version 2.7.17. Will be removed in qstash-js 3.0.0. | ||
* Please use https://github.com/upstash/workflow-js | ||
* Migration Guide: https://upstash.com/docs/workflow/migration | ||
*/ | ||
declare const serve: <TInitialPayload = unknown>(routeFunction: RouteFunction<TInitialPayload>, options?: Omit<WorkflowServeOptions<Response, TInitialPayload>, "onStepFinish">) => ((request: Request) => Promise<Response>); | ||
/** | ||
* @deprecated as of version 2.7.17. Will be removed in qstash-js 3.0.0. | ||
* Please use https://github.com/upstash/workflow-js | ||
* Migration Guide: https://upstash.com/docs/workflow/migration | ||
*/ | ||
declare const servePagesRouter: <TInitialPayload = unknown>(routeFunction: RouteFunction<TInitialPayload>, options?: Omit<WorkflowServeOptions<Response, TInitialPayload>, "onStepFinish">) => NextApiHandler; | ||
export { type VerifySignatureConfig, verifySignature, verifySignatureAppRouter, verifySignatureEdge }; | ||
export { type VerifySignatureConfig, serve, servePagesRouter, verifySignature, verifySignatureAppRouter, verifySignatureEdge }; |
@@ -1,1 +0,1 @@ | ||
{"version":"v0.0.0-ci.58463b768ad41df0667237530a94939beae3fce2-20240516131014","name":"@upstash/qstash","description":"Official Typescript client for QStash","author":"Andreas Thomas <dev@chronark.com>","license":"MIT","homepage":"https://github.com/upstash/sdk-qstash-ts#readme","repository":{"type":"git","url":"git+https://github.com/upstash/sdk-qstash-ts.git"},"bugs":{"url":"https://github.com/upstash/sdk-qstash-ts/issues"},"main":"./index.js","module":"./index.mjs","types":"./index.d.ts","files":["./**"],"exports":{".":{"import":"./index.mjs","require":"./index.js"},"./nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"},"./dist/nextjs":{"import":"./nextjs.mjs","require":"./nextjs.js"}},"typesVersions":{"*":{"nextjs":["./nextjs.d.ts"]}},"keywords":["qstash","queue","events","serverless","upstash"],"scripts":{"build":"tsup && cp README.md ./dist/ && cp package.json ./dist/ && cp LICENSE ./dist/","test":"bun test","fmt":"prettier --write .","lint":"tsc && eslint \"src/**/*.{js,ts,tsx}\" --quiet --fix"},"devDependencies":{"@commitlint/cli":"^19.2.2","@commitlint/config-conventional":"^19.2.2","@types/bun":"^1.1.1","@types/crypto-js":"^4.2.0","@typescript-eslint/eslint-plugin":"^7.0.1","@typescript-eslint/parser":"^7.0.1","bun-types":"^1.1.7","eslint":"^8","eslint-plugin-unicorn":"^51.0.1","husky":"^9.0.10","next":"^14.0.2","prettier":"^3.2.5","tsup":"latest","typescript":"^5.4.5","undici-types":"^6.16.0","vitest":"latest"},"dependencies":{"crypto-js":">=4.2.0","jose":"^ 5.2.3"}} | ||
{ "version": "v0.0.0-ci.5daf80975ba2cf15c84cc516c6ac6c5c9b3a1c7b-20241106143055", "name": "@upstash/qstash", "description": "Official Typescript client for QStash", "author": "Andreas Thomas <dev@chronark.com>", "license": "MIT", "homepage": "https://github.com/upstash/sdk-qstash-ts#readme", "repository": { "type": "git", "url": "git+https://github.com/upstash/sdk-qstash-ts.git" }, "bugs": { "url": "https://github.com/upstash/sdk-qstash-ts/issues" }, "main": "./index.js", "module": "./index.mjs", "types": "./index.d.ts", "files": [ "./*" ], "exports": { ".": { "import": "./index.mjs", "require": "./index.js" }, "./dist/nextjs": { "import": "./nextjs.mjs", "require": "./nextjs.js" }, "./nextjs": { "import": "./nextjs.mjs", "require": "./nextjs.js" }, "./h3": { "import": "./h3.mjs", "require": "./h3.js" }, "./nuxt": { "import": "./nuxt.mjs", "require": "./nuxt.js" }, "./svelte": { "import": "./svelte.mjs", "require": "./svelte.js" }, "./solidjs": { "import": "./solidjs.mjs", "require": "./solidjs.js" }, "./workflow": { "import": "./workflow.mjs", "require": "./workflow.js" }, "./hono": { "import": "./hono.mjs", "require": "./hono.js" }, "./cloudflare": { "import": "./cloudflare.mjs", "require": "./cloudflare.js" } }, "keywords": [ "qstash", "queue", "events", "serverless", "upstash" ], "scripts": { "build": "tsup && cp README.md ./dist/ && cp package.json ./dist/ && cp LICENSE ./dist/", "test": "bun test src", "fmt": "prettier --write .", "lint": "tsc && eslint \"{src,platforms}/**/*.{js,ts,tsx}\" --quiet --fix", "check-exports": "bun run build && cd dist && attw -P", "prepare": "husky" }, "devDependencies": { "@commitlint/cli": "^19.2.2", "@commitlint/config-conventional": "^19.2.2", "@eslint/eslintrc": "^3.1.0", "@eslint/js": "^9.10.0", "@solidjs/start": "^1.0.6", "@sveltejs/kit": "^2.5.18", "@types/bun": "^1.1.1", "@types/crypto-js": "^4.2.0", "@typescript-eslint/eslint-plugin": "^8.4.0", "@typescript-eslint/parser": "^8.4.0", "ai": "^3.1.28", "bun-types": "^1.1.7", "eslint": "^9.10.0", "eslint-plugin-unicorn": "^51.0.1", "h3": "^1.12.0", "hono": "^4.5.8", "husky": "^9.0.10", "next": "^14.0.2", "prettier": "^3.2.5", "tsup": "latest", "typescript": "^5.4.5", "undici-types": "^6.16.0", "vitest": "latest" }, "dependencies": { "neverthrow": "^7.0.1", "crypto-js": ">=4.2.0", "jose": "^5.2.3" } } |
@@ -5,2 +5,7 @@ # Upstash QStash SDK | ||
> [!NOTE] | ||
> **This project is in GA Stage.** | ||
> The Upstash Professional Support fully covers this project. It receives regular updates, and bug fixes. | ||
> The Upstash team is committed to maintaining and improving its functionality. | ||
**QStash** is an HTTP based messaging and scheduling solution for serverless and | ||
@@ -41,3 +46,3 @@ edge runtimes. | ||
Go to [upstash](https://console.upstash.com/qstash) and copy the token. | ||
Go to [Upstash Console](https://console.upstash.com/qstash) and copy the QSTASH_TOKEN. | ||
@@ -56,9 +61,9 @@ ## Basic Usage: | ||
const c = new Client({ | ||
const client = new Client({ | ||
token: "<QSTASH_TOKEN>", | ||
}); | ||
const res = await c.publishJSON({ | ||
const res = await client.publishJSON({ | ||
url: "https://my-api...", | ||
// or topic: "the name or id of a topic" | ||
// or urlGroup: "the name or id of a url group" | ||
body: { | ||
@@ -102,2 +107,28 @@ hello: "world", | ||
### Publishing a message to an LLM provider | ||
No need for complicated setup your LLM request. We'll call LLM and schedule it for your serverless needs. | ||
```ts | ||
import { Client, openai } from "@upstash/qstash"; | ||
const client = new Client({ | ||
token: "<QSTASH_TOKEN>", | ||
}); | ||
const result = await client.publishJSON({ | ||
api: { name: "llm", provider: openai({ token: process.env.OPENAI_API_KEY! }) }, | ||
body: { | ||
model: "gpt-3.5-turbo", | ||
messages: [ | ||
{ | ||
role: "user", | ||
content: "Where is the capital of Turkey?", | ||
}, | ||
], | ||
}, | ||
callback: "https://oz.requestcatcher.com/", | ||
}); | ||
``` | ||
## Docs | ||
@@ -109,2 +140,33 @@ | ||
### [Install Deno](https://deno.land/#installation) | ||
### Setup | ||
This project requires [Bun](https://bun.sh/) to be installed. Please see the [Bun installation documentation](https://bun.sh/docs/installation) for further instructions. | ||
Once you have cloned the project, you will need to install the dependencies and then you can run the project. | ||
```sh | ||
bun install | ||
bun run build | ||
``` | ||
### Testing | ||
To begin testing, environment variables will need to be setup. First, create a `.env` file in the root of the project. [`.env.template`](/.env.template) can be used as a template. Your values can be found in the [Qstash Console](https://console.upstash.com/qstash). | ||
```sh | ||
bun run test | ||
``` | ||
### Committing | ||
This project uses [commitlint](https://commitlint.js.org/). When committing, please ensure your commit message is formatted to include an appropriate prefix with the message. | ||
#### Examples | ||
``` | ||
fix: typescript bug | ||
feat: use new logger | ||
perf: refactor cache | ||
``` | ||
For a full list of options, please see the [`commitlint.config.js`](/commitlint.config.js) file. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
912749
44
26935
168
3
23
30
11
+ Addedneverthrow@^7.0.1
+ Addedneverthrow@7.2.0(transitive)
Updatedjose@^5.2.3