@upstash/qstash 2.1.8 → 2.1.9
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/dist/index.d.mts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +13 -0
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs.d.mts +4 -4
- package/dist/nextjs.d.ts +4 -4
- package/dist/nextjs.js.map +1 -1
- package/dist/nextjs.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -564,5 +564,8 @@ type PublishResponse<R> = R extends {
|
|
|
564
564
|
declare class QstashError extends Error {
|
|
565
565
|
constructor(message: string);
|
|
566
566
|
}
|
|
567
|
+
declare class QstashRatelimitError extends QstashError {
|
|
568
|
+
constructor(args: unknown);
|
|
569
|
+
}
|
|
567
570
|
|
|
568
|
-
export { AddEndpointsRequest, Client, CreateScheduleRequest, Endpoint, Event, EventsRequest, GetEventsResponse, Message, Messages, PublishJsonRequest, PublishRequest, QstashError, Receiver, ReceiverConfig, RemoveEndpointsRequest, Schedule, Schedules, SignatureError, State, Topic, Topics, VerifyRequest, WithCursor };
|
|
571
|
+
export { AddEndpointsRequest, Client, CreateScheduleRequest, Endpoint, Event, EventsRequest, GetEventsResponse, Message, Messages, PublishJsonRequest, PublishRequest, QstashError, QstashRatelimitError, Receiver, ReceiverConfig, RemoveEndpointsRequest, Schedule, Schedules, SignatureError, State, Topic, Topics, VerifyRequest, WithCursor };
|
package/dist/index.d.ts
CHANGED
|
@@ -564,5 +564,8 @@ type PublishResponse<R> = R extends {
|
|
|
564
564
|
declare class QstashError extends Error {
|
|
565
565
|
constructor(message: string);
|
|
566
566
|
}
|
|
567
|
+
declare class QstashRatelimitError extends QstashError {
|
|
568
|
+
constructor(args: unknown);
|
|
569
|
+
}
|
|
567
570
|
|
|
568
|
-
export { AddEndpointsRequest, Client, CreateScheduleRequest, Endpoint, Event, EventsRequest, GetEventsResponse, Message, Messages, PublishJsonRequest, PublishRequest, QstashError, Receiver, ReceiverConfig, RemoveEndpointsRequest, Schedule, Schedules, SignatureError, State, Topic, Topics, VerifyRequest, WithCursor };
|
|
571
|
+
export { AddEndpointsRequest, Client, CreateScheduleRequest, Endpoint, Event, EventsRequest, GetEventsResponse, Message, Messages, PublishJsonRequest, PublishRequest, QstashError, QstashRatelimitError, Receiver, ReceiverConfig, RemoveEndpointsRequest, Schedule, Schedules, SignatureError, State, Topic, Topics, VerifyRequest, WithCursor };
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,11 @@ var QstashError = class extends Error {
|
|
|
13
13
|
this.name = "QstashError";
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
|
+
var QstashRatelimitError = class extends QstashError {
|
|
17
|
+
constructor(args) {
|
|
18
|
+
super(`You have been ratelimited. ${JSON.stringify(args)} `);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
16
21
|
|
|
17
22
|
// src/client/http.ts
|
|
18
23
|
var HttpClient = class {
|
|
@@ -65,6 +70,13 @@ var HttpClient = class {
|
|
|
65
70
|
if (!res) {
|
|
66
71
|
throw error != null ? error : new Error("Exhausted all retries");
|
|
67
72
|
}
|
|
73
|
+
if (res.status === 429) {
|
|
74
|
+
throw new QstashRatelimitError({
|
|
75
|
+
limit: res.headers.get("Burst-RateLimit-Limit"),
|
|
76
|
+
remaining: res.headers.get("Burst-RateLimit-Remaining"),
|
|
77
|
+
reset: res.headers.get("Burst-RateLimit-Reset")
|
|
78
|
+
});
|
|
79
|
+
}
|
|
68
80
|
if (res.status < 200 || res.status >= 300) {
|
|
69
81
|
const body = yield res.text();
|
|
70
82
|
throw new QstashError(body.length > 0 ? body : `Error: status=${res.status}`);
|
|
@@ -410,5 +422,6 @@ var Client = class {
|
|
|
410
422
|
|
|
411
423
|
|
|
412
424
|
|
|
413
|
-
|
|
425
|
+
|
|
426
|
+
exports.Client = Client; exports.Messages = Messages; exports.QstashError = QstashError; exports.QstashRatelimitError = QstashRatelimitError; exports.Receiver = _chunkEQTYEU4Ujs.Receiver; exports.Schedules = Schedules; exports.SignatureError = _chunkEQTYEU4Ujs.SignatureError; exports.Topics = Topics;
|
|
414
427
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/error.ts","../src/client/http.ts","../src/client/topics.ts","../src/client/messages.ts","../src/client/schedules.ts","../src/client/dlq.ts","../src/client/client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Result of 500 Internal Server Error\n */\nexport class QstashError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"QstashError\";\n }\n}\n","import { QstashError } from \"./error\";\n\nexport type UpstashRequest = {\n /**\n * The path to the resource.\n */\n path: string[];\n\n /**\n * A BodyInit object or null to set request's body.\n */\n body?: BodyInit | null;\n\n /**\n * A Headers object, an object literal, or an array of two-item arrays to set\n * request's headers.\n */\n headers?: HeadersInit;\n\n /**\n * A boolean to set request's keepalive.\n */\n keepalive?: boolean;\n\n /**\n * A string to set request's method.\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n\n query?: Record<string, string | number | boolean | undefined>;\n\n /**\n * if enabled, call `res.json()`\n *\n * @default true\n */\n parseResponseAsJson?: boolean;\n};\nexport type UpstashResponse<TResult> = TResult & { error?: string };\n\nexport interface Requester {\n request: <TResult = unknown>(req: UpstashRequest) => Promise<UpstashResponse<TResult>>;\n}\n\nexport type RetryConfig =\n | false\n | {\n /**\n * The number of retries to attempt before giving up.\n *\n * @default 5\n */\n retries?: number;\n /**\n * A backoff function receives the current retry cound and returns a number in milliseconds to wait before retrying.\n *\n * @default\n * ```ts\n * Math.exp(retryCount) * 50\n * ```\n */\n backoff?: (retryCount: number) => number;\n };\n\nexport type HttpClientConfig = {\n baseUrl: string;\n authorization: string;\n retry?: RetryConfig;\n};\n\nexport class HttpClient implements Requester {\n public readonly baseUrl: string;\n\n public readonly authorization: string;\n\n public readonly options?: { backend?: string };\n\n public retry: {\n attempts: number;\n backoff: (retryCount: number) => number;\n };\n\n public constructor(config: HttpClientConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n\n this.authorization = config.authorization;\n\n if (typeof config?.retry === \"boolean\" && config?.retry === false) {\n this.retry = {\n attempts: 1,\n backoff: () => 0,\n };\n } else {\n this.retry = {\n attempts: config.retry?.retries ? config.retry.retries + 1 : 5,\n backoff: config.retry?.backoff ?? ((retryCount) => Math.exp(retryCount) * 50),\n };\n }\n }\n\n public async request<TResult>(req: UpstashRequest): Promise<UpstashResponse<TResult>> {\n const headers = new Headers(req.headers);\n headers.set(\"Authorization\", this.authorization);\n\n const requestOptions: RequestInit & { backend?: string } = {\n method: req.method,\n headers,\n body: req.body,\n keepalive: req.keepalive,\n };\n\n const url = new URL([this.baseUrl, ...(req.path ?? [])].join(\"/\"));\n if (req.query) {\n for (const [key, value] of Object.entries(req.query)) {\n if (typeof value !== \"undefined\") {\n url.searchParams.set(key, value.toString());\n }\n }\n }\n\n let res: Response | null = null;\n let error: Error | null = null;\n for (let i = 0; i < this.retry.attempts; i++) {\n try {\n res = await fetch(url.toString(), requestOptions);\n break;\n } catch (err) {\n error = err as Error;\n await new Promise((r) => setTimeout(r, this.retry.backoff(i)));\n }\n }\n if (!res) {\n throw error ?? new Error(\"Exhausted all retries\");\n }\n\n if (res.status < 200 || res.status >= 300) {\n const body = await res.text();\n throw new QstashError(body.length > 0 ? body : `Error: status=${res.status}`);\n }\n if (req.parseResponseAsJson === false) {\n return undefined as unknown as UpstashResponse<TResult>;\n } else {\n return (await res.json()) as UpstashResponse<TResult>;\n }\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Endpoint = {\n /**\n * The name of the endpoint (optional)\n */\n name?: string;\n\n /**\n * The url of the endpoint\n */\n url: string;\n};\n\nexport type AddEndpointsRequest = {\n /**\n * The name of the topic.\n * Must be unique and only contain alphanumeric, hyphen, underscore and periods.\n */\n name: string;\n\n endpoints: Endpoint[];\n};\n\nexport type RemoveEndpointsRequest = {\n /**\n * The name of the topic.\n * Must be unique and only contain alphanumeric, hyphen, underscore and periods.\n */\n name: string;\n\n endpoints: (\n | {\n name: string;\n url?: string;\n }\n | {\n name?: string;\n url: string;\n }\n )[];\n};\n\nexport type Topic = {\n /**\n * The name of this topic.\n */\n name: string;\n\n /**\n * A list of all subscribed endpoints\n */\n endpoints: Endpoint[];\n};\n\nexport class Topics {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Create a new topic with the given name and endpoints\n */\n public async addEndpoints(req: AddEndpointsRequest): Promise<void> {\n await this.http.request<Topic>({\n method: \"POST\",\n path: [\"v2\", \"topics\", req.name, \"endpoints\"],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n parseResponseAsJson: false,\n });\n }\n\n /**\n * Remove endpoints from a topic.\n */\n public async removeEndpoints(req: RemoveEndpointsRequest): Promise<void> {\n await this.http.request<Topic>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", req.name, \"endpoints\"],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n parseResponseAsJson: false,\n });\n }\n\n /**\n * Get a list of all topics.\n */\n public async list(): Promise<Topic[]> {\n return await this.http.request<Topic[]>({\n method: \"GET\",\n path: [\"v2\", \"topics\"],\n });\n }\n\n /**\n * Get a single topic\n */\n public async get(name: string): Promise<Topic> {\n return await this.http.request<Topic>({\n method: \"GET\",\n path: [\"v2\", \"topics\", name],\n });\n }\n\n /**\n * Delete a topic\n */\n public async delete(name: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", name],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Message = {\n /**\n * A unique identifier for this message.\n */\n messageId: string;\n\n /**\n * The topic name if this message was sent to a topic.\n */\n topicName?: string;\n\n /**\n * The url where this message is sent to.\n */\n url: string;\n\n /**\n * The http method used to deliver the message\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n /**\n * The http headers sent along with the message to your API.\n */\n header?: Record<string, string[]>;\n\n /**\n * The http body sent to your API\n */\n body?: string;\n\n /**\n * Maxmimum number of retries.\n */\n maxRetries?: number;\n\n /**\n * A unix timestamp (milliseconds) after which this message may get delivered.\n */\n notBefore?: number;\n\n /**\n * A unix timestamp (milliseconds) when this messages was crated.\n */\n createdAt: number;\n\n /**\n * The callback url if configured.\n */\n callback?: string;\n};\n\nexport class Messages {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Get a message\n */\n public async get(messageId: string): Promise<Message> {\n return await this.http.request<Message>({\n method: \"GET\",\n path: [\"v2\", \"messages\", messageId],\n });\n }\n\n /**\n * Cancel a message\n */\n public async delete(messageId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"messages\", messageId],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Schedule = {\n scheduleId: string;\n cron: string;\n createdAt: number;\n destination: string;\n method: string;\n header?: Record<string, string[]>;\n body?: string;\n retries: number;\n delay?: number;\n callback?: string;\n};\n\nexport type CreateScheduleRequest = {\n /**\n * Either a URL or topic name\n */\n destination: string;\n\n /**\n * The message to send.\n *\n * This can be anything, but please set the `Content-Type` header accordingly.\n *\n * You can leave this empty if you want to send a message with no body.\n */\n body?: BodyInit;\n\n /**\n * Optionally send along headers with the message.\n * These headers will be sent to your destination.\n *\n * We highly recommend sending a `Content-Type` header along, as this will help your destination\n * server to understand the content of the message.\n */\n headers?: HeadersInit;\n\n /**\n * Optionally delay the delivery of this message.\n *\n * In seconds.\n *\n * @default undefined\n */\n delay?: number;\n\n /**\n * In case your destination server is unavaialble or returns a status code outside of the 200-299\n * range, we will retry the request after a certain amount of time.\n *\n * Configure how many times you would like the delivery to be retried\n *\n * @default The maximum retry quota associated with your account.\n */\n retries?: number;\n\n /**\n * Use a callback url to forward the response of your destination server to your callback url.\n *\n * The callback url must be publicly accessible\n *\n * @default undefined\n */\n callback?: string;\n\n /**\n * The method to use when sending a request to your API\n *\n * @default `POST`\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n /**\n * Specify a cron expression to repeatedly send this message to the destination.\n */\n cron: string;\n};\n\nexport class Schedules {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Create a schedule\n */\n public async create(req: CreateScheduleRequest): Promise<{ scheduleId: string }> {\n const headers = new Headers(req.headers);\n\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n headers.set(\"Upstash-Cron\", req.cron);\n\n if (typeof req.method !== \"undefined\") {\n headers.set(\"Upstash-Method\", req.method);\n }\n\n if (typeof req.delay !== \"undefined\") {\n headers.set(\"Upstash-Delay\", `${req.delay.toFixed()}s`);\n }\n\n if (typeof req.retries !== \"undefined\") {\n headers.set(\"Upstash-Retries\", req.retries.toFixed());\n }\n\n if (typeof req.callback !== \"undefined\") {\n headers.set(\"Upstash-Callback\", req.callback);\n }\n\n return await this.http.request({\n method: \"POST\",\n headers,\n path: [\"v2\", \"schedules\", req.destination],\n body: req.body,\n });\n }\n\n /**\n * Get a schedule\n */\n public async get(scheduleId: string): Promise<Schedule> {\n return await this.http.request<Schedule>({\n method: \"GET\",\n path: [\"v2\", \"schedules\", scheduleId],\n });\n }\n\n /**\n * List your schedules\n */\n public async list(): Promise<Schedule[]> {\n return await this.http.request<Schedule[]>({\n method: \"GET\",\n path: [\"v2\", \"schedules\"],\n });\n }\n\n /**\n * Delete a schedule\n */\n public async delete(scheduleId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"schedules\", scheduleId],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\nimport type { Message } from \"./messages\";\n\ntype DlqMessage = Message & {\n dlqId: string;\n};\n\nexport class DLQ {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * List messages in the dlq\n */\n public async listMessages(opts?: { cursor?: string }): Promise<{\n messages: DlqMessage[];\n cursor?: string;\n }> {\n return await this.http.request({\n method: \"GET\",\n path: [\"v2\", \"dlq\"],\n query: { cursor: opts?.cursor },\n });\n }\n\n /**\n * Remove a message from the dlq using it's `dlqId`\n */\n public async delete(dlqMessageId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"dlq\", dlqMessageId],\n parseResponseAsJson: false, // there is no response\n });\n }\n}\n","import { HttpClient, Requester, RetryConfig } from \"./http\";\nimport { Topics } from \"./topics\";\nimport { Messages } from \"./messages\";\nimport { Schedules } from \"./schedules\";\nimport { Event } from \"./types\";\nimport { DLQ } from \"./dlq\";\ntype ClientConfig = {\n /**\n * Url of the qstash api server.\n *\n * This is only used for testing.\n *\n * @default \"https://qstash.upstash.io\"\n */\n baseUrl?: string;\n\n /**\n * The authorization token from the upstash console.\n */\n token: string;\n\n /**\n * Configure how the client should retry requests.\n */\n retry?: RetryConfig;\n};\n\nexport type PublishRequest<TBody = BodyInit> = {\n /**\n * The message to send.\n *\n * This can be anything, but please set the `Content-Type` header accordingly.\n *\n * You can leave this empty if you want to send a message with no body.\n */\n body?: TBody;\n\n /**\n * Optionally send along headers with the message.\n * These headers will be sent to your destination.\n *\n * We highly recommend sending a `Content-Type` header along, as this will help your destination\n * server to understand the content of the message.\n */\n headers?: HeadersInit;\n\n /**\n * Optionally delay the delivery of this message.\n *\n * In seconds.\n *\n * @default undefined\n */\n delay?: number;\n\n /**\n * Optionally set the absolute delay of this message.\n * This will override the delay option.\n * The message will not delivered until the specified time.\n *\n * Unix timestamp in seconds.\n *\n * @default undefined\n */\n notBefore?: number;\n\n /**\n * Provide a unique id for deduplication. This id will be used to detect duplicate messages.\n * If a duplicate message is detected, the request will be accepted but not enqueued.\n *\n * We store deduplication ids for 90 days. Afterwards it is possible that the message with the\n * same deduplication id is delivered again.\n *\n * When scheduling a message, the deduplication happens before the schedule is created.\n *\n * @default undefined\n */\n deduplicationId?: string;\n\n /**\n * If true, the message content will get hashed and used as deduplication id.\n * If a duplicate message is detected, the request will be accepted but not enqueued.\n *\n * The content based hash includes the following values:\n * - All headers, except Upstash-Authorization, this includes all headers you are sending.\n * - The entire raw request body The destination from the url path\n *\n * We store deduplication ids for 90 days. Afterwards it is possible that the message with the\n * same deduplication id is delivered again.\n *\n * When scheduling a message, the deduplication happens before the schedule is created.\n *\n * @default false\n */\n contentBasedDeduplication?: boolean;\n\n /**\n * In case your destination server is unavaialble or returns a status code outside of the 200-299\n * range, we will retry the request after a certain amount of time.\n *\n * Configure how many times you would like the delivery to be retried\n *\n * @default The maximum retry quota associated with your account.\n */\n retries?: number;\n\n /**\n * Use a callback url to forward the response of your destination server to your callback url.\n *\n * The callback url must be publicly accessible\n *\n * @default undefined\n */\n callback?: string;\n\n /**\n * The method to use when sending a request to your API\n *\n * @default `POST`\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n} & (\n | {\n /**\n * The url where the message should be sent to.\n */\n url: string;\n topic?: never;\n }\n | {\n url?: never;\n /**\n * The url where the message should be sent to.\n */\n topic: string;\n }\n);\n\nexport type PublishJsonRequest = Omit<PublishRequest, \"body\"> & {\n /**\n * The message to send.\n * This can be anything as long as it can be serialized to JSON.\n */\n body: unknown;\n};\n\nexport type EventsRequest = {\n cursor?: number;\n};\n\nexport type GetEventsResponse = {\n cursor?: number;\n events: Event[];\n};\n\nexport class Client {\n public http: Requester;\n\n public constructor(config: ClientConfig) {\n this.http = new HttpClient({\n retry: config.retry,\n baseUrl: config.baseUrl ? config.baseUrl.replace(/\\/$/, \"\") : \"https://qstash.upstash.io\",\n authorization: `Bearer ${config.token}`,\n });\n }\n\n /**\n * Access the topic API.\n *\n * Create, read, update or delete topics.\n */\n public get topics(): Topics {\n return new Topics(this.http);\n }\n\n /**\n * Access the dlq API.\n *\n * List or remove messages from the DLQ.\n */\n public get dlq(): DLQ {\n return new DLQ(this.http);\n }\n\n /**\n * Access the message API.\n *\n * Read or cancel messages.\n */\n public get messages(): Messages {\n return new Messages(this.http);\n }\n\n /**\n * Access the schedule API.\n *\n * Create, read or delete schedules.\n */\n public get schedules(): Schedules {\n return new Schedules(this.http);\n }\n public async publish<TRequest extends PublishRequest>(\n req: TRequest,\n ): Promise<PublishResponse<TRequest>> {\n const headers = new Headers(req.headers);\n\n headers.set(\"Upstash-Method\", req.method ?? \"POST\");\n\n if (typeof req.delay !== \"undefined\") {\n headers.set(\"Upstash-Delay\", `${req.delay.toFixed()}s`);\n }\n\n if (typeof req.notBefore !== \"undefined\") {\n headers.set(\"Upstash-Not-Before\", req.notBefore.toFixed());\n }\n\n if (typeof req.deduplicationId !== \"undefined\") {\n headers.set(\"Upstash-Deduplication-Id\", req.deduplicationId);\n }\n\n if (typeof req.contentBasedDeduplication !== \"undefined\") {\n headers.set(\"Upstash-Content-Based-Deduplication\", \"true\");\n }\n\n if (typeof req.retries !== \"undefined\") {\n headers.set(\"Upstash-Retries\", req.retries.toFixed());\n }\n\n if (typeof req.callback !== \"undefined\") {\n headers.set(\"Upstash-Callback\", req.callback);\n }\n\n const res = await this.http.request<PublishResponse<TRequest>>({\n path: [\"v2\", \"publish\", req.url ?? req.topic],\n body: req.body,\n headers,\n method: \"POST\",\n });\n return res;\n }\n\n /**\n * publishJSON is a utility wrapper around `publish` that automatically serializes the body\n * and sets the `Content-Type` header to `application/json`.\n */\n public async publishJSON<\n TBody = unknown,\n TRequest extends PublishRequest<TBody> = PublishRequest<TBody>,\n >(req: TRequest): Promise<PublishResponse<TRequest>> {\n const headers = new Headers(req.headers);\n headers.set(\"Content-Type\", \"application/json\");\n\n // @ts-ignore it's just internal\n const res = await this.publish<TRequest>({\n ...req,\n headers,\n body: JSON.stringify(req.body),\n } as PublishRequest);\n return res;\n }\n\n /**\n * Retrieve your logs.\n *\n * The logs endpoint is paginated and returns only 100 logs at a time.\n * If you want to receive more logs, you can use the cursor to paginate.\n *\n * The cursor is a unix timestamp with millisecond precision\n *\n * @example\n * ```ts\n * let cursor = Date.now()\n * const logs: Log[] = []\n * while (cursor > 0) {\n * const res = await qstash.logs({ cursor })\n * logs.push(...res.logs)\n * cursor = res.cursor ?? 0\n * }\n * ```\n */\n public async events(req?: EventsRequest): Promise<GetEventsResponse> {\n const query: Record<string, number> = {};\n if (req?.cursor && req.cursor > 0) {\n query.cursor = req.cursor;\n }\n const res = await this.http.request<GetEventsResponse>({\n path: [\"v2\", \"events\"],\n method: \"GET\",\n query,\n });\n return res;\n }\n}\ntype PublishToUrlResponse = {\n messageId: string;\n url: string;\n deduplicated?: boolean;\n};\n\ntype PublishToTopicResponse = PublishToUrlResponse[];\n\ntype PublishResponse<R> = R extends { url: string } ? PublishToUrlResponse : PublishToTopicResponse;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client/error.ts","../src/client/http.ts","../src/client/topics.ts","../src/client/messages.ts","../src/client/schedules.ts","../src/client/dlq.ts","../src/client/client.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Result of 500 Internal Server Error\n */\nexport class QstashError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"QstashError\";\n }\n}\n\nexport class QstashRatelimitError extends QstashError {\n constructor(args: unknown) {\n super(`You have been ratelimited. ${JSON.stringify(args)} `);\n }\n}\n","import { QstashError, QstashRatelimitError } from \"./error\";\n\nexport type UpstashRequest = {\n /**\n * The path to the resource.\n */\n path: string[];\n\n /**\n * A BodyInit object or null to set request's body.\n */\n body?: BodyInit | null;\n\n /**\n * A Headers object, an object literal, or an array of two-item arrays to set\n * request's headers.\n */\n headers?: HeadersInit;\n\n /**\n * A boolean to set request's keepalive.\n */\n keepalive?: boolean;\n\n /**\n * A string to set request's method.\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n\n query?: Record<string, string | number | boolean | undefined>;\n\n /**\n * if enabled, call `res.json()`\n *\n * @default true\n */\n parseResponseAsJson?: boolean;\n};\nexport type UpstashResponse<TResult> = TResult & { error?: string };\n\nexport interface Requester {\n request: <TResult = unknown>(req: UpstashRequest) => Promise<UpstashResponse<TResult>>;\n}\n\nexport type RetryConfig =\n | false\n | {\n /**\n * The number of retries to attempt before giving up.\n *\n * @default 5\n */\n retries?: number;\n /**\n * A backoff function receives the current retry cound and returns a number in milliseconds to wait before retrying.\n *\n * @default\n * ```ts\n * Math.exp(retryCount) * 50\n * ```\n */\n backoff?: (retryCount: number) => number;\n };\n\nexport type HttpClientConfig = {\n baseUrl: string;\n authorization: string;\n retry?: RetryConfig;\n};\n\nexport class HttpClient implements Requester {\n public readonly baseUrl: string;\n\n public readonly authorization: string;\n\n public readonly options?: { backend?: string };\n\n public retry: {\n attempts: number;\n backoff: (retryCount: number) => number;\n };\n\n public constructor(config: HttpClientConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n\n this.authorization = config.authorization;\n\n if (typeof config?.retry === \"boolean\" && config?.retry === false) {\n this.retry = {\n attempts: 1,\n backoff: () => 0,\n };\n } else {\n this.retry = {\n attempts: config.retry?.retries ? config.retry.retries + 1 : 5,\n backoff: config.retry?.backoff ?? ((retryCount) => Math.exp(retryCount) * 50),\n };\n }\n }\n\n public async request<TResult>(req: UpstashRequest): Promise<UpstashResponse<TResult>> {\n const headers = new Headers(req.headers);\n headers.set(\"Authorization\", this.authorization);\n\n const requestOptions: RequestInit & { backend?: string } = {\n method: req.method,\n headers,\n body: req.body,\n keepalive: req.keepalive,\n };\n\n const url = new URL([this.baseUrl, ...(req.path ?? [])].join(\"/\"));\n if (req.query) {\n for (const [key, value] of Object.entries(req.query)) {\n if (typeof value !== \"undefined\") {\n url.searchParams.set(key, value.toString());\n }\n }\n }\n\n let res: Response | null = null;\n let error: Error | null = null;\n for (let i = 0; i < this.retry.attempts; i++) {\n try {\n res = await fetch(url.toString(), requestOptions);\n break;\n } catch (err) {\n error = err as Error;\n await new Promise((r) => setTimeout(r, this.retry.backoff(i)));\n }\n }\n if (!res) {\n throw error ?? new Error(\"Exhausted all retries\");\n }\n if (res.status === 429) {\n throw new QstashRatelimitError({\n limit: res.headers.get(\"Burst-RateLimit-Limit\"),\n remaining: res.headers.get(\"Burst-RateLimit-Remaining\"),\n reset: res.headers.get(\"Burst-RateLimit-Reset\"),\n });\n }\n\n if (res.status < 200 || res.status >= 300) {\n const body = await res.text();\n throw new QstashError(body.length > 0 ? body : `Error: status=${res.status}`);\n }\n if (req.parseResponseAsJson === false) {\n return undefined as unknown as UpstashResponse<TResult>;\n } else {\n return (await res.json()) as UpstashResponse<TResult>;\n }\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Endpoint = {\n /**\n * The name of the endpoint (optional)\n */\n name?: string;\n\n /**\n * The url of the endpoint\n */\n url: string;\n};\n\nexport type AddEndpointsRequest = {\n /**\n * The name of the topic.\n * Must be unique and only contain alphanumeric, hyphen, underscore and periods.\n */\n name: string;\n\n endpoints: Endpoint[];\n};\n\nexport type RemoveEndpointsRequest = {\n /**\n * The name of the topic.\n * Must be unique and only contain alphanumeric, hyphen, underscore and periods.\n */\n name: string;\n\n endpoints: (\n | {\n name: string;\n url?: string;\n }\n | {\n name?: string;\n url: string;\n }\n )[];\n};\n\nexport type Topic = {\n /**\n * The name of this topic.\n */\n name: string;\n\n /**\n * A list of all subscribed endpoints\n */\n endpoints: Endpoint[];\n};\n\nexport class Topics {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Create a new topic with the given name and endpoints\n */\n public async addEndpoints(req: AddEndpointsRequest): Promise<void> {\n await this.http.request<Topic>({\n method: \"POST\",\n path: [\"v2\", \"topics\", req.name, \"endpoints\"],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n parseResponseAsJson: false,\n });\n }\n\n /**\n * Remove endpoints from a topic.\n */\n public async removeEndpoints(req: RemoveEndpointsRequest): Promise<void> {\n await this.http.request<Topic>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", req.name, \"endpoints\"],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n parseResponseAsJson: false,\n });\n }\n\n /**\n * Get a list of all topics.\n */\n public async list(): Promise<Topic[]> {\n return await this.http.request<Topic[]>({\n method: \"GET\",\n path: [\"v2\", \"topics\"],\n });\n }\n\n /**\n * Get a single topic\n */\n public async get(name: string): Promise<Topic> {\n return await this.http.request<Topic>({\n method: \"GET\",\n path: [\"v2\", \"topics\", name],\n });\n }\n\n /**\n * Delete a topic\n */\n public async delete(name: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", name],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Message = {\n /**\n * A unique identifier for this message.\n */\n messageId: string;\n\n /**\n * The topic name if this message was sent to a topic.\n */\n topicName?: string;\n\n /**\n * The url where this message is sent to.\n */\n url: string;\n\n /**\n * The http method used to deliver the message\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n /**\n * The http headers sent along with the message to your API.\n */\n header?: Record<string, string[]>;\n\n /**\n * The http body sent to your API\n */\n body?: string;\n\n /**\n * Maxmimum number of retries.\n */\n maxRetries?: number;\n\n /**\n * A unix timestamp (milliseconds) after which this message may get delivered.\n */\n notBefore?: number;\n\n /**\n * A unix timestamp (milliseconds) when this messages was crated.\n */\n createdAt: number;\n\n /**\n * The callback url if configured.\n */\n callback?: string;\n};\n\nexport class Messages {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Get a message\n */\n public async get(messageId: string): Promise<Message> {\n return await this.http.request<Message>({\n method: \"GET\",\n path: [\"v2\", \"messages\", messageId],\n });\n }\n\n /**\n * Cancel a message\n */\n public async delete(messageId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"messages\", messageId],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Schedule = {\n scheduleId: string;\n cron: string;\n createdAt: number;\n destination: string;\n method: string;\n header?: Record<string, string[]>;\n body?: string;\n retries: number;\n delay?: number;\n callback?: string;\n};\n\nexport type CreateScheduleRequest = {\n /**\n * Either a URL or topic name\n */\n destination: string;\n\n /**\n * The message to send.\n *\n * This can be anything, but please set the `Content-Type` header accordingly.\n *\n * You can leave this empty if you want to send a message with no body.\n */\n body?: BodyInit;\n\n /**\n * Optionally send along headers with the message.\n * These headers will be sent to your destination.\n *\n * We highly recommend sending a `Content-Type` header along, as this will help your destination\n * server to understand the content of the message.\n */\n headers?: HeadersInit;\n\n /**\n * Optionally delay the delivery of this message.\n *\n * In seconds.\n *\n * @default undefined\n */\n delay?: number;\n\n /**\n * In case your destination server is unavaialble or returns a status code outside of the 200-299\n * range, we will retry the request after a certain amount of time.\n *\n * Configure how many times you would like the delivery to be retried\n *\n * @default The maximum retry quota associated with your account.\n */\n retries?: number;\n\n /**\n * Use a callback url to forward the response of your destination server to your callback url.\n *\n * The callback url must be publicly accessible\n *\n * @default undefined\n */\n callback?: string;\n\n /**\n * The method to use when sending a request to your API\n *\n * @default `POST`\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n /**\n * Specify a cron expression to repeatedly send this message to the destination.\n */\n cron: string;\n};\n\nexport class Schedules {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Create a schedule\n */\n public async create(req: CreateScheduleRequest): Promise<{ scheduleId: string }> {\n const headers = new Headers(req.headers);\n\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n headers.set(\"Upstash-Cron\", req.cron);\n\n if (typeof req.method !== \"undefined\") {\n headers.set(\"Upstash-Method\", req.method);\n }\n\n if (typeof req.delay !== \"undefined\") {\n headers.set(\"Upstash-Delay\", `${req.delay.toFixed()}s`);\n }\n\n if (typeof req.retries !== \"undefined\") {\n headers.set(\"Upstash-Retries\", req.retries.toFixed());\n }\n\n if (typeof req.callback !== \"undefined\") {\n headers.set(\"Upstash-Callback\", req.callback);\n }\n\n return await this.http.request({\n method: \"POST\",\n headers,\n path: [\"v2\", \"schedules\", req.destination],\n body: req.body,\n });\n }\n\n /**\n * Get a schedule\n */\n public async get(scheduleId: string): Promise<Schedule> {\n return await this.http.request<Schedule>({\n method: \"GET\",\n path: [\"v2\", \"schedules\", scheduleId],\n });\n }\n\n /**\n * List your schedules\n */\n public async list(): Promise<Schedule[]> {\n return await this.http.request<Schedule[]>({\n method: \"GET\",\n path: [\"v2\", \"schedules\"],\n });\n }\n\n /**\n * Delete a schedule\n */\n public async delete(scheduleId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"schedules\", scheduleId],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\nimport type { Message } from \"./messages\";\n\ntype DlqMessage = Message & {\n dlqId: string;\n};\n\nexport class DLQ {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * List messages in the dlq\n */\n public async listMessages(opts?: { cursor?: string }): Promise<{\n messages: DlqMessage[];\n cursor?: string;\n }> {\n return await this.http.request({\n method: \"GET\",\n path: [\"v2\", \"dlq\"],\n query: { cursor: opts?.cursor },\n });\n }\n\n /**\n * Remove a message from the dlq using it's `dlqId`\n */\n public async delete(dlqMessageId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"dlq\", dlqMessageId],\n parseResponseAsJson: false, // there is no response\n });\n }\n}\n","import { HttpClient, Requester, RetryConfig } from \"./http\";\nimport { Topics } from \"./topics\";\nimport { Messages } from \"./messages\";\nimport { Schedules } from \"./schedules\";\nimport { Event } from \"./types\";\nimport { DLQ } from \"./dlq\";\ntype ClientConfig = {\n /**\n * Url of the qstash api server.\n *\n * This is only used for testing.\n *\n * @default \"https://qstash.upstash.io\"\n */\n baseUrl?: string;\n\n /**\n * The authorization token from the upstash console.\n */\n token: string;\n\n /**\n * Configure how the client should retry requests.\n */\n retry?: RetryConfig;\n};\n\nexport type PublishRequest<TBody = BodyInit> = {\n /**\n * The message to send.\n *\n * This can be anything, but please set the `Content-Type` header accordingly.\n *\n * You can leave this empty if you want to send a message with no body.\n */\n body?: TBody;\n\n /**\n * Optionally send along headers with the message.\n * These headers will be sent to your destination.\n *\n * We highly recommend sending a `Content-Type` header along, as this will help your destination\n * server to understand the content of the message.\n */\n headers?: HeadersInit;\n\n /**\n * Optionally delay the delivery of this message.\n *\n * In seconds.\n *\n * @default undefined\n */\n delay?: number;\n\n /**\n * Optionally set the absolute delay of this message.\n * This will override the delay option.\n * The message will not delivered until the specified time.\n *\n * Unix timestamp in seconds.\n *\n * @default undefined\n */\n notBefore?: number;\n\n /**\n * Provide a unique id for deduplication. This id will be used to detect duplicate messages.\n * If a duplicate message is detected, the request will be accepted but not enqueued.\n *\n * We store deduplication ids for 90 days. Afterwards it is possible that the message with the\n * same deduplication id is delivered again.\n *\n * When scheduling a message, the deduplication happens before the schedule is created.\n *\n * @default undefined\n */\n deduplicationId?: string;\n\n /**\n * If true, the message content will get hashed and used as deduplication id.\n * If a duplicate message is detected, the request will be accepted but not enqueued.\n *\n * The content based hash includes the following values:\n * - All headers, except Upstash-Authorization, this includes all headers you are sending.\n * - The entire raw request body The destination from the url path\n *\n * We store deduplication ids for 90 days. Afterwards it is possible that the message with the\n * same deduplication id is delivered again.\n *\n * When scheduling a message, the deduplication happens before the schedule is created.\n *\n * @default false\n */\n contentBasedDeduplication?: boolean;\n\n /**\n * In case your destination server is unavaialble or returns a status code outside of the 200-299\n * range, we will retry the request after a certain amount of time.\n *\n * Configure how many times you would like the delivery to be retried\n *\n * @default The maximum retry quota associated with your account.\n */\n retries?: number;\n\n /**\n * Use a callback url to forward the response of your destination server to your callback url.\n *\n * The callback url must be publicly accessible\n *\n * @default undefined\n */\n callback?: string;\n\n /**\n * The method to use when sending a request to your API\n *\n * @default `POST`\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n} & (\n | {\n /**\n * The url where the message should be sent to.\n */\n url: string;\n topic?: never;\n }\n | {\n url?: never;\n /**\n * The url where the message should be sent to.\n */\n topic: string;\n }\n);\n\nexport type PublishJsonRequest = Omit<PublishRequest, \"body\"> & {\n /**\n * The message to send.\n * This can be anything as long as it can be serialized to JSON.\n */\n body: unknown;\n};\n\nexport type EventsRequest = {\n cursor?: number;\n};\n\nexport type GetEventsResponse = {\n cursor?: number;\n events: Event[];\n};\n\nexport class Client {\n public http: Requester;\n\n public constructor(config: ClientConfig) {\n this.http = new HttpClient({\n retry: config.retry,\n baseUrl: config.baseUrl ? config.baseUrl.replace(/\\/$/, \"\") : \"https://qstash.upstash.io\",\n authorization: `Bearer ${config.token}`,\n });\n }\n\n /**\n * Access the topic API.\n *\n * Create, read, update or delete topics.\n */\n public get topics(): Topics {\n return new Topics(this.http);\n }\n\n /**\n * Access the dlq API.\n *\n * List or remove messages from the DLQ.\n */\n public get dlq(): DLQ {\n return new DLQ(this.http);\n }\n\n /**\n * Access the message API.\n *\n * Read or cancel messages.\n */\n public get messages(): Messages {\n return new Messages(this.http);\n }\n\n /**\n * Access the schedule API.\n *\n * Create, read or delete schedules.\n */\n public get schedules(): Schedules {\n return new Schedules(this.http);\n }\n public async publish<TRequest extends PublishRequest>(\n req: TRequest,\n ): Promise<PublishResponse<TRequest>> {\n const headers = new Headers(req.headers);\n\n headers.set(\"Upstash-Method\", req.method ?? \"POST\");\n\n if (typeof req.delay !== \"undefined\") {\n headers.set(\"Upstash-Delay\", `${req.delay.toFixed()}s`);\n }\n\n if (typeof req.notBefore !== \"undefined\") {\n headers.set(\"Upstash-Not-Before\", req.notBefore.toFixed());\n }\n\n if (typeof req.deduplicationId !== \"undefined\") {\n headers.set(\"Upstash-Deduplication-Id\", req.deduplicationId);\n }\n\n if (typeof req.contentBasedDeduplication !== \"undefined\") {\n headers.set(\"Upstash-Content-Based-Deduplication\", \"true\");\n }\n\n if (typeof req.retries !== \"undefined\") {\n headers.set(\"Upstash-Retries\", req.retries.toFixed());\n }\n\n if (typeof req.callback !== \"undefined\") {\n headers.set(\"Upstash-Callback\", req.callback);\n }\n\n const res = await this.http.request<PublishResponse<TRequest>>({\n path: [\"v2\", \"publish\", req.url ?? req.topic],\n body: req.body,\n headers,\n method: \"POST\",\n });\n return res;\n }\n\n /**\n * publishJSON is a utility wrapper around `publish` that automatically serializes the body\n * and sets the `Content-Type` header to `application/json`.\n */\n public async publishJSON<\n TBody = unknown,\n TRequest extends PublishRequest<TBody> = PublishRequest<TBody>,\n >(req: TRequest): Promise<PublishResponse<TRequest>> {\n const headers = new Headers(req.headers);\n headers.set(\"Content-Type\", \"application/json\");\n\n // @ts-ignore it's just internal\n const res = await this.publish<TRequest>({\n ...req,\n headers,\n body: JSON.stringify(req.body),\n } as PublishRequest);\n return res;\n }\n\n /**\n * Retrieve your logs.\n *\n * The logs endpoint is paginated and returns only 100 logs at a time.\n * If you want to receive more logs, you can use the cursor to paginate.\n *\n * The cursor is a unix timestamp with millisecond precision\n *\n * @example\n * ```ts\n * let cursor = Date.now()\n * const logs: Log[] = []\n * while (cursor > 0) {\n * const res = await qstash.logs({ cursor })\n * logs.push(...res.logs)\n * cursor = res.cursor ?? 0\n * }\n * ```\n */\n public async events(req?: EventsRequest): Promise<GetEventsResponse> {\n const query: Record<string, number> = {};\n if (req?.cursor && req.cursor > 0) {\n query.cursor = req.cursor;\n }\n const res = await this.http.request<GetEventsResponse>({\n path: [\"v2\", \"events\"],\n method: \"GET\",\n query,\n });\n return res;\n }\n}\ntype PublishToUrlResponse = {\n messageId: string;\n url: string;\n deduplicated?: boolean;\n};\n\ntype PublishToTopicResponse = PublishToUrlResponse[];\n\ntype PublishResponse<R> = R extends { url: string } ? PublishToUrlResponse : PublishToTopicResponse;\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -13,6 +13,11 @@ var QstashError = class extends Error {
|
|
|
13
13
|
this.name = "QstashError";
|
|
14
14
|
}
|
|
15
15
|
};
|
|
16
|
+
var QstashRatelimitError = class extends QstashError {
|
|
17
|
+
constructor(args) {
|
|
18
|
+
super(`You have been ratelimited. ${JSON.stringify(args)} `);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
16
21
|
|
|
17
22
|
// src/client/http.ts
|
|
18
23
|
var HttpClient = class {
|
|
@@ -65,6 +70,13 @@ var HttpClient = class {
|
|
|
65
70
|
if (!res) {
|
|
66
71
|
throw error != null ? error : new Error("Exhausted all retries");
|
|
67
72
|
}
|
|
73
|
+
if (res.status === 429) {
|
|
74
|
+
throw new QstashRatelimitError({
|
|
75
|
+
limit: res.headers.get("Burst-RateLimit-Limit"),
|
|
76
|
+
remaining: res.headers.get("Burst-RateLimit-Remaining"),
|
|
77
|
+
reset: res.headers.get("Burst-RateLimit-Reset")
|
|
78
|
+
});
|
|
79
|
+
}
|
|
68
80
|
if (res.status < 200 || res.status >= 300) {
|
|
69
81
|
const body = yield res.text();
|
|
70
82
|
throw new QstashError(body.length > 0 ? body : `Error: status=${res.status}`);
|
|
@@ -406,6 +418,7 @@ export {
|
|
|
406
418
|
Client,
|
|
407
419
|
Messages,
|
|
408
420
|
QstashError,
|
|
421
|
+
QstashRatelimitError,
|
|
409
422
|
Receiver,
|
|
410
423
|
Schedules,
|
|
411
424
|
SignatureError,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/error.ts","../src/client/http.ts","../src/client/topics.ts","../src/client/messages.ts","../src/client/schedules.ts","../src/client/dlq.ts","../src/client/client.ts"],"sourcesContent":["/**\n * Result of 500 Internal Server Error\n */\nexport class QstashError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"QstashError\";\n }\n}\n","import { QstashError } from \"./error\";\n\nexport type UpstashRequest = {\n /**\n * The path to the resource.\n */\n path: string[];\n\n /**\n * A BodyInit object or null to set request's body.\n */\n body?: BodyInit | null;\n\n /**\n * A Headers object, an object literal, or an array of two-item arrays to set\n * request's headers.\n */\n headers?: HeadersInit;\n\n /**\n * A boolean to set request's keepalive.\n */\n keepalive?: boolean;\n\n /**\n * A string to set request's method.\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n\n query?: Record<string, string | number | boolean | undefined>;\n\n /**\n * if enabled, call `res.json()`\n *\n * @default true\n */\n parseResponseAsJson?: boolean;\n};\nexport type UpstashResponse<TResult> = TResult & { error?: string };\n\nexport interface Requester {\n request: <TResult = unknown>(req: UpstashRequest) => Promise<UpstashResponse<TResult>>;\n}\n\nexport type RetryConfig =\n | false\n | {\n /**\n * The number of retries to attempt before giving up.\n *\n * @default 5\n */\n retries?: number;\n /**\n * A backoff function receives the current retry cound and returns a number in milliseconds to wait before retrying.\n *\n * @default\n * ```ts\n * Math.exp(retryCount) * 50\n * ```\n */\n backoff?: (retryCount: number) => number;\n };\n\nexport type HttpClientConfig = {\n baseUrl: string;\n authorization: string;\n retry?: RetryConfig;\n};\n\nexport class HttpClient implements Requester {\n public readonly baseUrl: string;\n\n public readonly authorization: string;\n\n public readonly options?: { backend?: string };\n\n public retry: {\n attempts: number;\n backoff: (retryCount: number) => number;\n };\n\n public constructor(config: HttpClientConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n\n this.authorization = config.authorization;\n\n if (typeof config?.retry === \"boolean\" && config?.retry === false) {\n this.retry = {\n attempts: 1,\n backoff: () => 0,\n };\n } else {\n this.retry = {\n attempts: config.retry?.retries ? config.retry.retries + 1 : 5,\n backoff: config.retry?.backoff ?? ((retryCount) => Math.exp(retryCount) * 50),\n };\n }\n }\n\n public async request<TResult>(req: UpstashRequest): Promise<UpstashResponse<TResult>> {\n const headers = new Headers(req.headers);\n headers.set(\"Authorization\", this.authorization);\n\n const requestOptions: RequestInit & { backend?: string } = {\n method: req.method,\n headers,\n body: req.body,\n keepalive: req.keepalive,\n };\n\n const url = new URL([this.baseUrl, ...(req.path ?? [])].join(\"/\"));\n if (req.query) {\n for (const [key, value] of Object.entries(req.query)) {\n if (typeof value !== \"undefined\") {\n url.searchParams.set(key, value.toString());\n }\n }\n }\n\n let res: Response | null = null;\n let error: Error | null = null;\n for (let i = 0; i < this.retry.attempts; i++) {\n try {\n res = await fetch(url.toString(), requestOptions);\n break;\n } catch (err) {\n error = err as Error;\n await new Promise((r) => setTimeout(r, this.retry.backoff(i)));\n }\n }\n if (!res) {\n throw error ?? new Error(\"Exhausted all retries\");\n }\n\n if (res.status < 200 || res.status >= 300) {\n const body = await res.text();\n throw new QstashError(body.length > 0 ? body : `Error: status=${res.status}`);\n }\n if (req.parseResponseAsJson === false) {\n return undefined as unknown as UpstashResponse<TResult>;\n } else {\n return (await res.json()) as UpstashResponse<TResult>;\n }\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Endpoint = {\n /**\n * The name of the endpoint (optional)\n */\n name?: string;\n\n /**\n * The url of the endpoint\n */\n url: string;\n};\n\nexport type AddEndpointsRequest = {\n /**\n * The name of the topic.\n * Must be unique and only contain alphanumeric, hyphen, underscore and periods.\n */\n name: string;\n\n endpoints: Endpoint[];\n};\n\nexport type RemoveEndpointsRequest = {\n /**\n * The name of the topic.\n * Must be unique and only contain alphanumeric, hyphen, underscore and periods.\n */\n name: string;\n\n endpoints: (\n | {\n name: string;\n url?: string;\n }\n | {\n name?: string;\n url: string;\n }\n )[];\n};\n\nexport type Topic = {\n /**\n * The name of this topic.\n */\n name: string;\n\n /**\n * A list of all subscribed endpoints\n */\n endpoints: Endpoint[];\n};\n\nexport class Topics {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Create a new topic with the given name and endpoints\n */\n public async addEndpoints(req: AddEndpointsRequest): Promise<void> {\n await this.http.request<Topic>({\n method: \"POST\",\n path: [\"v2\", \"topics\", req.name, \"endpoints\"],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n parseResponseAsJson: false,\n });\n }\n\n /**\n * Remove endpoints from a topic.\n */\n public async removeEndpoints(req: RemoveEndpointsRequest): Promise<void> {\n await this.http.request<Topic>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", req.name, \"endpoints\"],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n parseResponseAsJson: false,\n });\n }\n\n /**\n * Get a list of all topics.\n */\n public async list(): Promise<Topic[]> {\n return await this.http.request<Topic[]>({\n method: \"GET\",\n path: [\"v2\", \"topics\"],\n });\n }\n\n /**\n * Get a single topic\n */\n public async get(name: string): Promise<Topic> {\n return await this.http.request<Topic>({\n method: \"GET\",\n path: [\"v2\", \"topics\", name],\n });\n }\n\n /**\n * Delete a topic\n */\n public async delete(name: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", name],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Message = {\n /**\n * A unique identifier for this message.\n */\n messageId: string;\n\n /**\n * The topic name if this message was sent to a topic.\n */\n topicName?: string;\n\n /**\n * The url where this message is sent to.\n */\n url: string;\n\n /**\n * The http method used to deliver the message\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n /**\n * The http headers sent along with the message to your API.\n */\n header?: Record<string, string[]>;\n\n /**\n * The http body sent to your API\n */\n body?: string;\n\n /**\n * Maxmimum number of retries.\n */\n maxRetries?: number;\n\n /**\n * A unix timestamp (milliseconds) after which this message may get delivered.\n */\n notBefore?: number;\n\n /**\n * A unix timestamp (milliseconds) when this messages was crated.\n */\n createdAt: number;\n\n /**\n * The callback url if configured.\n */\n callback?: string;\n};\n\nexport class Messages {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Get a message\n */\n public async get(messageId: string): Promise<Message> {\n return await this.http.request<Message>({\n method: \"GET\",\n path: [\"v2\", \"messages\", messageId],\n });\n }\n\n /**\n * Cancel a message\n */\n public async delete(messageId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"messages\", messageId],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Schedule = {\n scheduleId: string;\n cron: string;\n createdAt: number;\n destination: string;\n method: string;\n header?: Record<string, string[]>;\n body?: string;\n retries: number;\n delay?: number;\n callback?: string;\n};\n\nexport type CreateScheduleRequest = {\n /**\n * Either a URL or topic name\n */\n destination: string;\n\n /**\n * The message to send.\n *\n * This can be anything, but please set the `Content-Type` header accordingly.\n *\n * You can leave this empty if you want to send a message with no body.\n */\n body?: BodyInit;\n\n /**\n * Optionally send along headers with the message.\n * These headers will be sent to your destination.\n *\n * We highly recommend sending a `Content-Type` header along, as this will help your destination\n * server to understand the content of the message.\n */\n headers?: HeadersInit;\n\n /**\n * Optionally delay the delivery of this message.\n *\n * In seconds.\n *\n * @default undefined\n */\n delay?: number;\n\n /**\n * In case your destination server is unavaialble or returns a status code outside of the 200-299\n * range, we will retry the request after a certain amount of time.\n *\n * Configure how many times you would like the delivery to be retried\n *\n * @default The maximum retry quota associated with your account.\n */\n retries?: number;\n\n /**\n * Use a callback url to forward the response of your destination server to your callback url.\n *\n * The callback url must be publicly accessible\n *\n * @default undefined\n */\n callback?: string;\n\n /**\n * The method to use when sending a request to your API\n *\n * @default `POST`\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n /**\n * Specify a cron expression to repeatedly send this message to the destination.\n */\n cron: string;\n};\n\nexport class Schedules {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Create a schedule\n */\n public async create(req: CreateScheduleRequest): Promise<{ scheduleId: string }> {\n const headers = new Headers(req.headers);\n\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n headers.set(\"Upstash-Cron\", req.cron);\n\n if (typeof req.method !== \"undefined\") {\n headers.set(\"Upstash-Method\", req.method);\n }\n\n if (typeof req.delay !== \"undefined\") {\n headers.set(\"Upstash-Delay\", `${req.delay.toFixed()}s`);\n }\n\n if (typeof req.retries !== \"undefined\") {\n headers.set(\"Upstash-Retries\", req.retries.toFixed());\n }\n\n if (typeof req.callback !== \"undefined\") {\n headers.set(\"Upstash-Callback\", req.callback);\n }\n\n return await this.http.request({\n method: \"POST\",\n headers,\n path: [\"v2\", \"schedules\", req.destination],\n body: req.body,\n });\n }\n\n /**\n * Get a schedule\n */\n public async get(scheduleId: string): Promise<Schedule> {\n return await this.http.request<Schedule>({\n method: \"GET\",\n path: [\"v2\", \"schedules\", scheduleId],\n });\n }\n\n /**\n * List your schedules\n */\n public async list(): Promise<Schedule[]> {\n return await this.http.request<Schedule[]>({\n method: \"GET\",\n path: [\"v2\", \"schedules\"],\n });\n }\n\n /**\n * Delete a schedule\n */\n public async delete(scheduleId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"schedules\", scheduleId],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\nimport type { Message } from \"./messages\";\n\ntype DlqMessage = Message & {\n dlqId: string;\n};\n\nexport class DLQ {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * List messages in the dlq\n */\n public async listMessages(opts?: { cursor?: string }): Promise<{\n messages: DlqMessage[];\n cursor?: string;\n }> {\n return await this.http.request({\n method: \"GET\",\n path: [\"v2\", \"dlq\"],\n query: { cursor: opts?.cursor },\n });\n }\n\n /**\n * Remove a message from the dlq using it's `dlqId`\n */\n public async delete(dlqMessageId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"dlq\", dlqMessageId],\n parseResponseAsJson: false, // there is no response\n });\n }\n}\n","import { HttpClient, Requester, RetryConfig } from \"./http\";\nimport { Topics } from \"./topics\";\nimport { Messages } from \"./messages\";\nimport { Schedules } from \"./schedules\";\nimport { Event } from \"./types\";\nimport { DLQ } from \"./dlq\";\ntype ClientConfig = {\n /**\n * Url of the qstash api server.\n *\n * This is only used for testing.\n *\n * @default \"https://qstash.upstash.io\"\n */\n baseUrl?: string;\n\n /**\n * The authorization token from the upstash console.\n */\n token: string;\n\n /**\n * Configure how the client should retry requests.\n */\n retry?: RetryConfig;\n};\n\nexport type PublishRequest<TBody = BodyInit> = {\n /**\n * The message to send.\n *\n * This can be anything, but please set the `Content-Type` header accordingly.\n *\n * You can leave this empty if you want to send a message with no body.\n */\n body?: TBody;\n\n /**\n * Optionally send along headers with the message.\n * These headers will be sent to your destination.\n *\n * We highly recommend sending a `Content-Type` header along, as this will help your destination\n * server to understand the content of the message.\n */\n headers?: HeadersInit;\n\n /**\n * Optionally delay the delivery of this message.\n *\n * In seconds.\n *\n * @default undefined\n */\n delay?: number;\n\n /**\n * Optionally set the absolute delay of this message.\n * This will override the delay option.\n * The message will not delivered until the specified time.\n *\n * Unix timestamp in seconds.\n *\n * @default undefined\n */\n notBefore?: number;\n\n /**\n * Provide a unique id for deduplication. This id will be used to detect duplicate messages.\n * If a duplicate message is detected, the request will be accepted but not enqueued.\n *\n * We store deduplication ids for 90 days. Afterwards it is possible that the message with the\n * same deduplication id is delivered again.\n *\n * When scheduling a message, the deduplication happens before the schedule is created.\n *\n * @default undefined\n */\n deduplicationId?: string;\n\n /**\n * If true, the message content will get hashed and used as deduplication id.\n * If a duplicate message is detected, the request will be accepted but not enqueued.\n *\n * The content based hash includes the following values:\n * - All headers, except Upstash-Authorization, this includes all headers you are sending.\n * - The entire raw request body The destination from the url path\n *\n * We store deduplication ids for 90 days. Afterwards it is possible that the message with the\n * same deduplication id is delivered again.\n *\n * When scheduling a message, the deduplication happens before the schedule is created.\n *\n * @default false\n */\n contentBasedDeduplication?: boolean;\n\n /**\n * In case your destination server is unavaialble or returns a status code outside of the 200-299\n * range, we will retry the request after a certain amount of time.\n *\n * Configure how many times you would like the delivery to be retried\n *\n * @default The maximum retry quota associated with your account.\n */\n retries?: number;\n\n /**\n * Use a callback url to forward the response of your destination server to your callback url.\n *\n * The callback url must be publicly accessible\n *\n * @default undefined\n */\n callback?: string;\n\n /**\n * The method to use when sending a request to your API\n *\n * @default `POST`\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n} & (\n | {\n /**\n * The url where the message should be sent to.\n */\n url: string;\n topic?: never;\n }\n | {\n url?: never;\n /**\n * The url where the message should be sent to.\n */\n topic: string;\n }\n);\n\nexport type PublishJsonRequest = Omit<PublishRequest, \"body\"> & {\n /**\n * The message to send.\n * This can be anything as long as it can be serialized to JSON.\n */\n body: unknown;\n};\n\nexport type EventsRequest = {\n cursor?: number;\n};\n\nexport type GetEventsResponse = {\n cursor?: number;\n events: Event[];\n};\n\nexport class Client {\n public http: Requester;\n\n public constructor(config: ClientConfig) {\n this.http = new HttpClient({\n retry: config.retry,\n baseUrl: config.baseUrl ? config.baseUrl.replace(/\\/$/, \"\") : \"https://qstash.upstash.io\",\n authorization: `Bearer ${config.token}`,\n });\n }\n\n /**\n * Access the topic API.\n *\n * Create, read, update or delete topics.\n */\n public get topics(): Topics {\n return new Topics(this.http);\n }\n\n /**\n * Access the dlq API.\n *\n * List or remove messages from the DLQ.\n */\n public get dlq(): DLQ {\n return new DLQ(this.http);\n }\n\n /**\n * Access the message API.\n *\n * Read or cancel messages.\n */\n public get messages(): Messages {\n return new Messages(this.http);\n }\n\n /**\n * Access the schedule API.\n *\n * Create, read or delete schedules.\n */\n public get schedules(): Schedules {\n return new Schedules(this.http);\n }\n public async publish<TRequest extends PublishRequest>(\n req: TRequest,\n ): Promise<PublishResponse<TRequest>> {\n const headers = new Headers(req.headers);\n\n headers.set(\"Upstash-Method\", req.method ?? \"POST\");\n\n if (typeof req.delay !== \"undefined\") {\n headers.set(\"Upstash-Delay\", `${req.delay.toFixed()}s`);\n }\n\n if (typeof req.notBefore !== \"undefined\") {\n headers.set(\"Upstash-Not-Before\", req.notBefore.toFixed());\n }\n\n if (typeof req.deduplicationId !== \"undefined\") {\n headers.set(\"Upstash-Deduplication-Id\", req.deduplicationId);\n }\n\n if (typeof req.contentBasedDeduplication !== \"undefined\") {\n headers.set(\"Upstash-Content-Based-Deduplication\", \"true\");\n }\n\n if (typeof req.retries !== \"undefined\") {\n headers.set(\"Upstash-Retries\", req.retries.toFixed());\n }\n\n if (typeof req.callback !== \"undefined\") {\n headers.set(\"Upstash-Callback\", req.callback);\n }\n\n const res = await this.http.request<PublishResponse<TRequest>>({\n path: [\"v2\", \"publish\", req.url ?? req.topic],\n body: req.body,\n headers,\n method: \"POST\",\n });\n return res;\n }\n\n /**\n * publishJSON is a utility wrapper around `publish` that automatically serializes the body\n * and sets the `Content-Type` header to `application/json`.\n */\n public async publishJSON<\n TBody = unknown,\n TRequest extends PublishRequest<TBody> = PublishRequest<TBody>,\n >(req: TRequest): Promise<PublishResponse<TRequest>> {\n const headers = new Headers(req.headers);\n headers.set(\"Content-Type\", \"application/json\");\n\n // @ts-ignore it's just internal\n const res = await this.publish<TRequest>({\n ...req,\n headers,\n body: JSON.stringify(req.body),\n } as PublishRequest);\n return res;\n }\n\n /**\n * Retrieve your logs.\n *\n * The logs endpoint is paginated and returns only 100 logs at a time.\n * If you want to receive more logs, you can use the cursor to paginate.\n *\n * The cursor is a unix timestamp with millisecond precision\n *\n * @example\n * ```ts\n * let cursor = Date.now()\n * const logs: Log[] = []\n * while (cursor > 0) {\n * const res = await qstash.logs({ cursor })\n * logs.push(...res.logs)\n * cursor = res.cursor ?? 0\n * }\n * ```\n */\n public async events(req?: EventsRequest): Promise<GetEventsResponse> {\n const query: Record<string, number> = {};\n if (req?.cursor && req.cursor > 0) {\n query.cursor = req.cursor;\n }\n const res = await this.http.request<GetEventsResponse>({\n path: [\"v2\", \"events\"],\n method: \"GET\",\n query,\n });\n return res;\n }\n}\ntype PublishToUrlResponse = {\n messageId: string;\n url: string;\n deduplicated?: boolean;\n};\n\ntype PublishToTopicResponse = PublishToUrlResponse[];\n\ntype PublishResponse<R> = R extends { url: string } ? PublishToUrlResponse : PublishToTopicResponse;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client/error.ts","../src/client/http.ts","../src/client/topics.ts","../src/client/messages.ts","../src/client/schedules.ts","../src/client/dlq.ts","../src/client/client.ts"],"sourcesContent":["/**\n * Result of 500 Internal Server Error\n */\nexport class QstashError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"QstashError\";\n }\n}\n\nexport class QstashRatelimitError extends QstashError {\n constructor(args: unknown) {\n super(`You have been ratelimited. ${JSON.stringify(args)} `);\n }\n}\n","import { QstashError, QstashRatelimitError } from \"./error\";\n\nexport type UpstashRequest = {\n /**\n * The path to the resource.\n */\n path: string[];\n\n /**\n * A BodyInit object or null to set request's body.\n */\n body?: BodyInit | null;\n\n /**\n * A Headers object, an object literal, or an array of two-item arrays to set\n * request's headers.\n */\n headers?: HeadersInit;\n\n /**\n * A boolean to set request's keepalive.\n */\n keepalive?: boolean;\n\n /**\n * A string to set request's method.\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n\n query?: Record<string, string | number | boolean | undefined>;\n\n /**\n * if enabled, call `res.json()`\n *\n * @default true\n */\n parseResponseAsJson?: boolean;\n};\nexport type UpstashResponse<TResult> = TResult & { error?: string };\n\nexport interface Requester {\n request: <TResult = unknown>(req: UpstashRequest) => Promise<UpstashResponse<TResult>>;\n}\n\nexport type RetryConfig =\n | false\n | {\n /**\n * The number of retries to attempt before giving up.\n *\n * @default 5\n */\n retries?: number;\n /**\n * A backoff function receives the current retry cound and returns a number in milliseconds to wait before retrying.\n *\n * @default\n * ```ts\n * Math.exp(retryCount) * 50\n * ```\n */\n backoff?: (retryCount: number) => number;\n };\n\nexport type HttpClientConfig = {\n baseUrl: string;\n authorization: string;\n retry?: RetryConfig;\n};\n\nexport class HttpClient implements Requester {\n public readonly baseUrl: string;\n\n public readonly authorization: string;\n\n public readonly options?: { backend?: string };\n\n public retry: {\n attempts: number;\n backoff: (retryCount: number) => number;\n };\n\n public constructor(config: HttpClientConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n\n this.authorization = config.authorization;\n\n if (typeof config?.retry === \"boolean\" && config?.retry === false) {\n this.retry = {\n attempts: 1,\n backoff: () => 0,\n };\n } else {\n this.retry = {\n attempts: config.retry?.retries ? config.retry.retries + 1 : 5,\n backoff: config.retry?.backoff ?? ((retryCount) => Math.exp(retryCount) * 50),\n };\n }\n }\n\n public async request<TResult>(req: UpstashRequest): Promise<UpstashResponse<TResult>> {\n const headers = new Headers(req.headers);\n headers.set(\"Authorization\", this.authorization);\n\n const requestOptions: RequestInit & { backend?: string } = {\n method: req.method,\n headers,\n body: req.body,\n keepalive: req.keepalive,\n };\n\n const url = new URL([this.baseUrl, ...(req.path ?? [])].join(\"/\"));\n if (req.query) {\n for (const [key, value] of Object.entries(req.query)) {\n if (typeof value !== \"undefined\") {\n url.searchParams.set(key, value.toString());\n }\n }\n }\n\n let res: Response | null = null;\n let error: Error | null = null;\n for (let i = 0; i < this.retry.attempts; i++) {\n try {\n res = await fetch(url.toString(), requestOptions);\n break;\n } catch (err) {\n error = err as Error;\n await new Promise((r) => setTimeout(r, this.retry.backoff(i)));\n }\n }\n if (!res) {\n throw error ?? new Error(\"Exhausted all retries\");\n }\n if (res.status === 429) {\n throw new QstashRatelimitError({\n limit: res.headers.get(\"Burst-RateLimit-Limit\"),\n remaining: res.headers.get(\"Burst-RateLimit-Remaining\"),\n reset: res.headers.get(\"Burst-RateLimit-Reset\"),\n });\n }\n\n if (res.status < 200 || res.status >= 300) {\n const body = await res.text();\n throw new QstashError(body.length > 0 ? body : `Error: status=${res.status}`);\n }\n if (req.parseResponseAsJson === false) {\n return undefined as unknown as UpstashResponse<TResult>;\n } else {\n return (await res.json()) as UpstashResponse<TResult>;\n }\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Endpoint = {\n /**\n * The name of the endpoint (optional)\n */\n name?: string;\n\n /**\n * The url of the endpoint\n */\n url: string;\n};\n\nexport type AddEndpointsRequest = {\n /**\n * The name of the topic.\n * Must be unique and only contain alphanumeric, hyphen, underscore and periods.\n */\n name: string;\n\n endpoints: Endpoint[];\n};\n\nexport type RemoveEndpointsRequest = {\n /**\n * The name of the topic.\n * Must be unique and only contain alphanumeric, hyphen, underscore and periods.\n */\n name: string;\n\n endpoints: (\n | {\n name: string;\n url?: string;\n }\n | {\n name?: string;\n url: string;\n }\n )[];\n};\n\nexport type Topic = {\n /**\n * The name of this topic.\n */\n name: string;\n\n /**\n * A list of all subscribed endpoints\n */\n endpoints: Endpoint[];\n};\n\nexport class Topics {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Create a new topic with the given name and endpoints\n */\n public async addEndpoints(req: AddEndpointsRequest): Promise<void> {\n await this.http.request<Topic>({\n method: \"POST\",\n path: [\"v2\", \"topics\", req.name, \"endpoints\"],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n parseResponseAsJson: false,\n });\n }\n\n /**\n * Remove endpoints from a topic.\n */\n public async removeEndpoints(req: RemoveEndpointsRequest): Promise<void> {\n await this.http.request<Topic>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", req.name, \"endpoints\"],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n parseResponseAsJson: false,\n });\n }\n\n /**\n * Get a list of all topics.\n */\n public async list(): Promise<Topic[]> {\n return await this.http.request<Topic[]>({\n method: \"GET\",\n path: [\"v2\", \"topics\"],\n });\n }\n\n /**\n * Get a single topic\n */\n public async get(name: string): Promise<Topic> {\n return await this.http.request<Topic>({\n method: \"GET\",\n path: [\"v2\", \"topics\", name],\n });\n }\n\n /**\n * Delete a topic\n */\n public async delete(name: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", name],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Message = {\n /**\n * A unique identifier for this message.\n */\n messageId: string;\n\n /**\n * The topic name if this message was sent to a topic.\n */\n topicName?: string;\n\n /**\n * The url where this message is sent to.\n */\n url: string;\n\n /**\n * The http method used to deliver the message\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n /**\n * The http headers sent along with the message to your API.\n */\n header?: Record<string, string[]>;\n\n /**\n * The http body sent to your API\n */\n body?: string;\n\n /**\n * Maxmimum number of retries.\n */\n maxRetries?: number;\n\n /**\n * A unix timestamp (milliseconds) after which this message may get delivered.\n */\n notBefore?: number;\n\n /**\n * A unix timestamp (milliseconds) when this messages was crated.\n */\n createdAt: number;\n\n /**\n * The callback url if configured.\n */\n callback?: string;\n};\n\nexport class Messages {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Get a message\n */\n public async get(messageId: string): Promise<Message> {\n return await this.http.request<Message>({\n method: \"GET\",\n path: [\"v2\", \"messages\", messageId],\n });\n }\n\n /**\n * Cancel a message\n */\n public async delete(messageId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"messages\", messageId],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\n\nexport type Schedule = {\n scheduleId: string;\n cron: string;\n createdAt: number;\n destination: string;\n method: string;\n header?: Record<string, string[]>;\n body?: string;\n retries: number;\n delay?: number;\n callback?: string;\n};\n\nexport type CreateScheduleRequest = {\n /**\n * Either a URL or topic name\n */\n destination: string;\n\n /**\n * The message to send.\n *\n * This can be anything, but please set the `Content-Type` header accordingly.\n *\n * You can leave this empty if you want to send a message with no body.\n */\n body?: BodyInit;\n\n /**\n * Optionally send along headers with the message.\n * These headers will be sent to your destination.\n *\n * We highly recommend sending a `Content-Type` header along, as this will help your destination\n * server to understand the content of the message.\n */\n headers?: HeadersInit;\n\n /**\n * Optionally delay the delivery of this message.\n *\n * In seconds.\n *\n * @default undefined\n */\n delay?: number;\n\n /**\n * In case your destination server is unavaialble or returns a status code outside of the 200-299\n * range, we will retry the request after a certain amount of time.\n *\n * Configure how many times you would like the delivery to be retried\n *\n * @default The maximum retry quota associated with your account.\n */\n retries?: number;\n\n /**\n * Use a callback url to forward the response of your destination server to your callback url.\n *\n * The callback url must be publicly accessible\n *\n * @default undefined\n */\n callback?: string;\n\n /**\n * The method to use when sending a request to your API\n *\n * @default `POST`\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n /**\n * Specify a cron expression to repeatedly send this message to the destination.\n */\n cron: string;\n};\n\nexport class Schedules {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * Create a schedule\n */\n public async create(req: CreateScheduleRequest): Promise<{ scheduleId: string }> {\n const headers = new Headers(req.headers);\n\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\");\n }\n\n headers.set(\"Upstash-Cron\", req.cron);\n\n if (typeof req.method !== \"undefined\") {\n headers.set(\"Upstash-Method\", req.method);\n }\n\n if (typeof req.delay !== \"undefined\") {\n headers.set(\"Upstash-Delay\", `${req.delay.toFixed()}s`);\n }\n\n if (typeof req.retries !== \"undefined\") {\n headers.set(\"Upstash-Retries\", req.retries.toFixed());\n }\n\n if (typeof req.callback !== \"undefined\") {\n headers.set(\"Upstash-Callback\", req.callback);\n }\n\n return await this.http.request({\n method: \"POST\",\n headers,\n path: [\"v2\", \"schedules\", req.destination],\n body: req.body,\n });\n }\n\n /**\n * Get a schedule\n */\n public async get(scheduleId: string): Promise<Schedule> {\n return await this.http.request<Schedule>({\n method: \"GET\",\n path: [\"v2\", \"schedules\", scheduleId],\n });\n }\n\n /**\n * List your schedules\n */\n public async list(): Promise<Schedule[]> {\n return await this.http.request<Schedule[]>({\n method: \"GET\",\n path: [\"v2\", \"schedules\"],\n });\n }\n\n /**\n * Delete a schedule\n */\n public async delete(scheduleId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"schedules\", scheduleId],\n parseResponseAsJson: false,\n });\n }\n}\n","import { Requester } from \"./http\";\nimport type { Message } from \"./messages\";\n\ntype DlqMessage = Message & {\n dlqId: string;\n};\n\nexport class DLQ {\n private readonly http: Requester;\n\n constructor(http: Requester) {\n this.http = http;\n }\n\n /**\n * List messages in the dlq\n */\n public async listMessages(opts?: { cursor?: string }): Promise<{\n messages: DlqMessage[];\n cursor?: string;\n }> {\n return await this.http.request({\n method: \"GET\",\n path: [\"v2\", \"dlq\"],\n query: { cursor: opts?.cursor },\n });\n }\n\n /**\n * Remove a message from the dlq using it's `dlqId`\n */\n public async delete(dlqMessageId: string): Promise<void> {\n return await this.http.request<void>({\n method: \"DELETE\",\n path: [\"v2\", \"dlq\", dlqMessageId],\n parseResponseAsJson: false, // there is no response\n });\n }\n}\n","import { HttpClient, Requester, RetryConfig } from \"./http\";\nimport { Topics } from \"./topics\";\nimport { Messages } from \"./messages\";\nimport { Schedules } from \"./schedules\";\nimport { Event } from \"./types\";\nimport { DLQ } from \"./dlq\";\ntype ClientConfig = {\n /**\n * Url of the qstash api server.\n *\n * This is only used for testing.\n *\n * @default \"https://qstash.upstash.io\"\n */\n baseUrl?: string;\n\n /**\n * The authorization token from the upstash console.\n */\n token: string;\n\n /**\n * Configure how the client should retry requests.\n */\n retry?: RetryConfig;\n};\n\nexport type PublishRequest<TBody = BodyInit> = {\n /**\n * The message to send.\n *\n * This can be anything, but please set the `Content-Type` header accordingly.\n *\n * You can leave this empty if you want to send a message with no body.\n */\n body?: TBody;\n\n /**\n * Optionally send along headers with the message.\n * These headers will be sent to your destination.\n *\n * We highly recommend sending a `Content-Type` header along, as this will help your destination\n * server to understand the content of the message.\n */\n headers?: HeadersInit;\n\n /**\n * Optionally delay the delivery of this message.\n *\n * In seconds.\n *\n * @default undefined\n */\n delay?: number;\n\n /**\n * Optionally set the absolute delay of this message.\n * This will override the delay option.\n * The message will not delivered until the specified time.\n *\n * Unix timestamp in seconds.\n *\n * @default undefined\n */\n notBefore?: number;\n\n /**\n * Provide a unique id for deduplication. This id will be used to detect duplicate messages.\n * If a duplicate message is detected, the request will be accepted but not enqueued.\n *\n * We store deduplication ids for 90 days. Afterwards it is possible that the message with the\n * same deduplication id is delivered again.\n *\n * When scheduling a message, the deduplication happens before the schedule is created.\n *\n * @default undefined\n */\n deduplicationId?: string;\n\n /**\n * If true, the message content will get hashed and used as deduplication id.\n * If a duplicate message is detected, the request will be accepted but not enqueued.\n *\n * The content based hash includes the following values:\n * - All headers, except Upstash-Authorization, this includes all headers you are sending.\n * - The entire raw request body The destination from the url path\n *\n * We store deduplication ids for 90 days. Afterwards it is possible that the message with the\n * same deduplication id is delivered again.\n *\n * When scheduling a message, the deduplication happens before the schedule is created.\n *\n * @default false\n */\n contentBasedDeduplication?: boolean;\n\n /**\n * In case your destination server is unavaialble or returns a status code outside of the 200-299\n * range, we will retry the request after a certain amount of time.\n *\n * Configure how many times you would like the delivery to be retried\n *\n * @default The maximum retry quota associated with your account.\n */\n retries?: number;\n\n /**\n * Use a callback url to forward the response of your destination server to your callback url.\n *\n * The callback url must be publicly accessible\n *\n * @default undefined\n */\n callback?: string;\n\n /**\n * The method to use when sending a request to your API\n *\n * @default `POST`\n */\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n} & (\n | {\n /**\n * The url where the message should be sent to.\n */\n url: string;\n topic?: never;\n }\n | {\n url?: never;\n /**\n * The url where the message should be sent to.\n */\n topic: string;\n }\n);\n\nexport type PublishJsonRequest = Omit<PublishRequest, \"body\"> & {\n /**\n * The message to send.\n * This can be anything as long as it can be serialized to JSON.\n */\n body: unknown;\n};\n\nexport type EventsRequest = {\n cursor?: number;\n};\n\nexport type GetEventsResponse = {\n cursor?: number;\n events: Event[];\n};\n\nexport class Client {\n public http: Requester;\n\n public constructor(config: ClientConfig) {\n this.http = new HttpClient({\n retry: config.retry,\n baseUrl: config.baseUrl ? config.baseUrl.replace(/\\/$/, \"\") : \"https://qstash.upstash.io\",\n authorization: `Bearer ${config.token}`,\n });\n }\n\n /**\n * Access the topic API.\n *\n * Create, read, update or delete topics.\n */\n public get topics(): Topics {\n return new Topics(this.http);\n }\n\n /**\n * Access the dlq API.\n *\n * List or remove messages from the DLQ.\n */\n public get dlq(): DLQ {\n return new DLQ(this.http);\n }\n\n /**\n * Access the message API.\n *\n * Read or cancel messages.\n */\n public get messages(): Messages {\n return new Messages(this.http);\n }\n\n /**\n * Access the schedule API.\n *\n * Create, read or delete schedules.\n */\n public get schedules(): Schedules {\n return new Schedules(this.http);\n }\n public async publish<TRequest extends PublishRequest>(\n req: TRequest,\n ): Promise<PublishResponse<TRequest>> {\n const headers = new Headers(req.headers);\n\n headers.set(\"Upstash-Method\", req.method ?? \"POST\");\n\n if (typeof req.delay !== \"undefined\") {\n headers.set(\"Upstash-Delay\", `${req.delay.toFixed()}s`);\n }\n\n if (typeof req.notBefore !== \"undefined\") {\n headers.set(\"Upstash-Not-Before\", req.notBefore.toFixed());\n }\n\n if (typeof req.deduplicationId !== \"undefined\") {\n headers.set(\"Upstash-Deduplication-Id\", req.deduplicationId);\n }\n\n if (typeof req.contentBasedDeduplication !== \"undefined\") {\n headers.set(\"Upstash-Content-Based-Deduplication\", \"true\");\n }\n\n if (typeof req.retries !== \"undefined\") {\n headers.set(\"Upstash-Retries\", req.retries.toFixed());\n }\n\n if (typeof req.callback !== \"undefined\") {\n headers.set(\"Upstash-Callback\", req.callback);\n }\n\n const res = await this.http.request<PublishResponse<TRequest>>({\n path: [\"v2\", \"publish\", req.url ?? req.topic],\n body: req.body,\n headers,\n method: \"POST\",\n });\n return res;\n }\n\n /**\n * publishJSON is a utility wrapper around `publish` that automatically serializes the body\n * and sets the `Content-Type` header to `application/json`.\n */\n public async publishJSON<\n TBody = unknown,\n TRequest extends PublishRequest<TBody> = PublishRequest<TBody>,\n >(req: TRequest): Promise<PublishResponse<TRequest>> {\n const headers = new Headers(req.headers);\n headers.set(\"Content-Type\", \"application/json\");\n\n // @ts-ignore it's just internal\n const res = await this.publish<TRequest>({\n ...req,\n headers,\n body: JSON.stringify(req.body),\n } as PublishRequest);\n return res;\n }\n\n /**\n * Retrieve your logs.\n *\n * The logs endpoint is paginated and returns only 100 logs at a time.\n * If you want to receive more logs, you can use the cursor to paginate.\n *\n * The cursor is a unix timestamp with millisecond precision\n *\n * @example\n * ```ts\n * let cursor = Date.now()\n * const logs: Log[] = []\n * while (cursor > 0) {\n * const res = await qstash.logs({ cursor })\n * logs.push(...res.logs)\n * cursor = res.cursor ?? 0\n * }\n * ```\n */\n public async events(req?: EventsRequest): Promise<GetEventsResponse> {\n const query: Record<string, number> = {};\n if (req?.cursor && req.cursor > 0) {\n query.cursor = req.cursor;\n }\n const res = await this.http.request<GetEventsResponse>({\n path: [\"v2\", \"events\"],\n method: \"GET\",\n query,\n });\n return res;\n }\n}\ntype PublishToUrlResponse = {\n messageId: string;\n url: string;\n deduplicated?: boolean;\n};\n\ntype PublishToTopicResponse = PublishToUrlResponse[];\n\ntype PublishResponse<R> = R extends { url: string } ? PublishToUrlResponse : PublishToTopicResponse;\n"],"mappings":"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","names":[]}
|
package/dist/nextjs.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextApiHandler } from 'next';
|
|
2
2
|
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
|
|
3
3
|
|
|
4
|
-
type
|
|
4
|
+
type VerifySignatureConfig = {
|
|
5
5
|
currentSigningKey?: string;
|
|
6
6
|
nextSigningKey?: string;
|
|
7
7
|
/**
|
|
@@ -17,7 +17,7 @@ type VerifySignaturConfig = {
|
|
|
17
17
|
*/
|
|
18
18
|
clockTolerance?: number;
|
|
19
19
|
};
|
|
20
|
-
declare function verifySignature(handler: NextApiHandler, config?:
|
|
21
|
-
declare function verifySignatureEdge(handler: (req: NextRequest, nfe
|
|
20
|
+
declare function verifySignature(handler: NextApiHandler, config?: VerifySignatureConfig): NextApiHandler;
|
|
21
|
+
declare function verifySignatureEdge(handler: (req: NextRequest, nfe?: NextFetchEvent) => NextResponse | Promise<NextResponse>, config?: VerifySignatureConfig): (req: NextRequest, nfe: NextFetchEvent) => Promise<NextResponse<unknown>>;
|
|
22
22
|
|
|
23
|
-
export {
|
|
23
|
+
export { VerifySignatureConfig, verifySignature, verifySignatureEdge };
|
package/dist/nextjs.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextApiHandler } from 'next';
|
|
2
2
|
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
|
|
3
3
|
|
|
4
|
-
type
|
|
4
|
+
type VerifySignatureConfig = {
|
|
5
5
|
currentSigningKey?: string;
|
|
6
6
|
nextSigningKey?: string;
|
|
7
7
|
/**
|
|
@@ -17,7 +17,7 @@ type VerifySignaturConfig = {
|
|
|
17
17
|
*/
|
|
18
18
|
clockTolerance?: number;
|
|
19
19
|
};
|
|
20
|
-
declare function verifySignature(handler: NextApiHandler, config?:
|
|
21
|
-
declare function verifySignatureEdge(handler: (req: NextRequest, nfe
|
|
20
|
+
declare function verifySignature(handler: NextApiHandler, config?: VerifySignatureConfig): NextApiHandler;
|
|
21
|
+
declare function verifySignatureEdge(handler: (req: NextRequest, nfe?: NextFetchEvent) => NextResponse | Promise<NextResponse>, config?: VerifySignatureConfig): (req: NextRequest, nfe: NextFetchEvent) => Promise<NextResponse<unknown>>;
|
|
22
22
|
|
|
23
|
-
export {
|
|
23
|
+
export { VerifySignatureConfig, verifySignature, verifySignatureEdge };
|
package/dist/nextjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nextjs.ts"],"names":[],"mappings":"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","sourcesContent":["import type { NextApiHandler, NextApiRequest, NextApiResponse } from \"next\";\nimport { NextRequest, NextFetchEvent, NextResponse } from \"next/server\";\nimport { Receiver } from \"./receiver\";\n\nexport type
|
|
1
|
+
{"version":3,"sources":["../src/nextjs.ts"],"names":[],"mappings":"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","sourcesContent":["import type { NextApiHandler, NextApiRequest, NextApiResponse } from \"next\";\nimport { NextRequest, NextFetchEvent, NextResponse } from \"next/server\";\nimport { Receiver } from \"./receiver\";\n\nexport type VerifySignatureConfig = {\n currentSigningKey?: string;\n nextSigningKey?: string;\n\n /**\n * The url of this api route, including the protocol.\n *\n * If you omit this, the url will be automatically determined by checking the `VERCEL_URL` env variable and assuming `https`\n */\n url?: string;\n\n /**\n * Number of seconds to tolerate when checking `nbf` and `exp` claims, to deal with small clock differences among different servers\n *\n * @default 0\n */\n clockTolerance?: number;\n};\n\nexport function verifySignature(\n handler: NextApiHandler,\n config?: VerifySignatureConfig,\n): NextApiHandler {\n const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;\n if (!currentSigningKey) {\n throw new Error(\n \"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY\",\n );\n }\n const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;\n if (!nextSigningKey) {\n throw new Error(\n \"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY\",\n );\n }\n const receiver = new Receiver({\n currentSigningKey,\n nextSigningKey,\n });\n\n return async (req: NextApiRequest, res: NextApiResponse) => {\n // @ts-ignore This can throw errors during vercel build\n const signature = req.headers[\"upstash-signature\"];\n if (!signature) {\n res.status(400);\n res.send(\"`Upstash-Signature` header is missing\");\n res.end();\n return;\n }\n if (typeof signature !== \"string\") {\n throw new Error(\"`Upstash-Signature` header is not a string\");\n }\n\n const chunks = [];\n for await (const chunk of req) {\n // @ts-ignore\n chunks.push(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk);\n }\n const body = Buffer.concat(chunks).toString(\"utf-8\");\n\n const isValid = await receiver.verify({\n signature,\n body,\n clockTolerance: config?.clockTolerance,\n });\n if (!isValid) {\n res.status(400);\n res.send(\"Invalid signature\");\n res.end();\n return;\n }\n\n try {\n if (req.headers[\"content-type\"] === \"application/json\") {\n req.body = JSON.parse(body);\n } else {\n req.body = body;\n }\n } catch {\n req.body = body;\n }\n\n return handler(req, res);\n };\n}\n\nexport function verifySignatureEdge(\n handler: (req: NextRequest, nfe?: NextFetchEvent) => NextResponse | Promise<NextResponse>,\n config?: VerifySignatureConfig,\n) {\n const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;\n if (!currentSigningKey) {\n throw new Error(\n \"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY\",\n );\n }\n const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;\n if (!nextSigningKey) {\n throw new Error(\n \"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY\",\n );\n }\n const receiver = new Receiver({\n currentSigningKey,\n nextSigningKey,\n });\n\n return async (req: NextRequest, nfe: NextFetchEvent) => {\n const reqClone = req.clone() as NextRequest;\n // @ts-ignore This can throw errors during vercel build\n const signature = req.headers.get(\"upstash-signature\");\n if (!signature) {\n return new NextResponse(new TextEncoder().encode(\"`Upstash-Signature` header is missing\"), {\n status: 403,\n });\n }\n if (typeof signature !== \"string\") {\n throw new Error(\"`Upstash-Signature` header is not a string\");\n }\n\n const body = await req.text();\n const isValid = await receiver.verify({\n signature,\n body,\n clockTolerance: config?.clockTolerance,\n });\n if (!isValid) {\n return new NextResponse(new TextEncoder().encode(\"invalid signature\"), { status: 403 });\n }\n\n let parsedBody: unknown = undefined;\n\n try {\n if (req.headers.get(\"content-type\") === \"application/json\") {\n parsedBody = JSON.parse(body);\n } else {\n parsedBody = body;\n }\n } catch {\n parsedBody = body;\n }\n\n return handler(reqClone, nfe);\n };\n}\n"]}
|
package/dist/nextjs.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nextjs.ts"],"sourcesContent":["import type { NextApiHandler, NextApiRequest, NextApiResponse } from \"next\";\nimport { NextRequest, NextFetchEvent, NextResponse } from \"next/server\";\nimport { Receiver } from \"./receiver\";\n\nexport type
|
|
1
|
+
{"version":3,"sources":["../src/nextjs.ts"],"sourcesContent":["import type { NextApiHandler, NextApiRequest, NextApiResponse } from \"next\";\nimport { NextRequest, NextFetchEvent, NextResponse } from \"next/server\";\nimport { Receiver } from \"./receiver\";\n\nexport type VerifySignatureConfig = {\n currentSigningKey?: string;\n nextSigningKey?: string;\n\n /**\n * The url of this api route, including the protocol.\n *\n * If you omit this, the url will be automatically determined by checking the `VERCEL_URL` env variable and assuming `https`\n */\n url?: string;\n\n /**\n * Number of seconds to tolerate when checking `nbf` and `exp` claims, to deal with small clock differences among different servers\n *\n * @default 0\n */\n clockTolerance?: number;\n};\n\nexport function verifySignature(\n handler: NextApiHandler,\n config?: VerifySignatureConfig,\n): NextApiHandler {\n const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;\n if (!currentSigningKey) {\n throw new Error(\n \"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY\",\n );\n }\n const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;\n if (!nextSigningKey) {\n throw new Error(\n \"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY\",\n );\n }\n const receiver = new Receiver({\n currentSigningKey,\n nextSigningKey,\n });\n\n return async (req: NextApiRequest, res: NextApiResponse) => {\n // @ts-ignore This can throw errors during vercel build\n const signature = req.headers[\"upstash-signature\"];\n if (!signature) {\n res.status(400);\n res.send(\"`Upstash-Signature` header is missing\");\n res.end();\n return;\n }\n if (typeof signature !== \"string\") {\n throw new Error(\"`Upstash-Signature` header is not a string\");\n }\n\n const chunks = [];\n for await (const chunk of req) {\n // @ts-ignore\n chunks.push(typeof chunk === \"string\" ? Buffer.from(chunk) : chunk);\n }\n const body = Buffer.concat(chunks).toString(\"utf-8\");\n\n const isValid = await receiver.verify({\n signature,\n body,\n clockTolerance: config?.clockTolerance,\n });\n if (!isValid) {\n res.status(400);\n res.send(\"Invalid signature\");\n res.end();\n return;\n }\n\n try {\n if (req.headers[\"content-type\"] === \"application/json\") {\n req.body = JSON.parse(body);\n } else {\n req.body = body;\n }\n } catch {\n req.body = body;\n }\n\n return handler(req, res);\n };\n}\n\nexport function verifySignatureEdge(\n handler: (req: NextRequest, nfe?: NextFetchEvent) => NextResponse | Promise<NextResponse>,\n config?: VerifySignatureConfig,\n) {\n const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;\n if (!currentSigningKey) {\n throw new Error(\n \"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY\",\n );\n }\n const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;\n if (!nextSigningKey) {\n throw new Error(\n \"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY\",\n );\n }\n const receiver = new Receiver({\n currentSigningKey,\n nextSigningKey,\n });\n\n return async (req: NextRequest, nfe: NextFetchEvent) => {\n const reqClone = req.clone() as NextRequest;\n // @ts-ignore This can throw errors during vercel build\n const signature = req.headers.get(\"upstash-signature\");\n if (!signature) {\n return new NextResponse(new TextEncoder().encode(\"`Upstash-Signature` header is missing\"), {\n status: 403,\n });\n }\n if (typeof signature !== \"string\") {\n throw new Error(\"`Upstash-Signature` header is not a string\");\n }\n\n const body = await req.text();\n const isValid = await receiver.verify({\n signature,\n body,\n clockTolerance: config?.clockTolerance,\n });\n if (!isValid) {\n return new NextResponse(new TextEncoder().encode(\"invalid signature\"), { status: 403 });\n }\n\n let parsedBody: unknown = undefined;\n\n try {\n if (req.headers.get(\"content-type\") === \"application/json\") {\n parsedBody = JSON.parse(body);\n } else {\n parsedBody = body;\n }\n } catch {\n parsedBody = body;\n }\n\n return handler(reqClone, nfe);\n };\n}\n"],"mappings":"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","names":[]}
|