@upstash/qstash 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.releaserc +14 -0
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/esm/client.js +34 -0
- package/esm/endpoint.js +63 -0
- package/esm/error.js +9 -0
- package/esm/http.js +77 -0
- package/esm/package.json +3 -0
- package/esm/platforms/nodejs.js +1 -0
- package/esm/topic.js +71 -0
- package/package.json +51 -0
- package/script/client.js +38 -0
- package/script/endpoint.js +67 -0
- package/script/error.js +13 -0
- package/script/http.js +81 -0
- package/script/package.json +3 -0
- package/script/platforms/nodejs.js +5 -0
- package/script/topic.js +75 -0
- package/types/client.d.ts +124 -0
- package/types/endpoint.d.ts +59 -0
- package/types/error.d.ts +6 -0
- package/types/http.d.ts +64 -0
- package/types/platforms/nodejs.d.ts +1 -0
- package/types/topic.d.ts +64 -0
package/.releaserc
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Upstash, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# sdk-qstash-ts
|
package/esm/client.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { HttpClient } from "./http.js";
|
|
2
|
+
import { Topics } from "./topic.js";
|
|
3
|
+
import { Endpoints } from "./endpoint.js";
|
|
4
|
+
export class Client {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
Object.defineProperty(this, "http", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
configurable: true,
|
|
9
|
+
writable: true,
|
|
10
|
+
value: void 0
|
|
11
|
+
});
|
|
12
|
+
this.http = new HttpClient({
|
|
13
|
+
baseUrl: config.baseUrl
|
|
14
|
+
? config.baseUrl.replace(/\/$/, "")
|
|
15
|
+
: "https://qstash.upstash.io",
|
|
16
|
+
authorization: config.authorization,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
get topics() {
|
|
20
|
+
return new Topics(this.http);
|
|
21
|
+
}
|
|
22
|
+
get endpoints() {
|
|
23
|
+
return new Endpoints(this.http);
|
|
24
|
+
}
|
|
25
|
+
async publish(req) {
|
|
26
|
+
const res = await this.http.request({
|
|
27
|
+
path: ["v1", "publish", req.destination],
|
|
28
|
+
body: req.cron,
|
|
29
|
+
headers: req.headers,
|
|
30
|
+
method: "POST",
|
|
31
|
+
});
|
|
32
|
+
return res;
|
|
33
|
+
}
|
|
34
|
+
}
|
package/esm/endpoint.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export class Endpoints {
|
|
2
|
+
constructor(http) {
|
|
3
|
+
Object.defineProperty(this, "http", {
|
|
4
|
+
enumerable: true,
|
|
5
|
+
configurable: true,
|
|
6
|
+
writable: true,
|
|
7
|
+
value: void 0
|
|
8
|
+
});
|
|
9
|
+
this.http = http;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create a new endpoint with the given name.
|
|
13
|
+
*/
|
|
14
|
+
async create(req) {
|
|
15
|
+
return await this.http.request({
|
|
16
|
+
method: "POST",
|
|
17
|
+
path: ["v1", "endpoints"],
|
|
18
|
+
body: JSON.stringify(req),
|
|
19
|
+
headers: { "Content-Type": "application/json" },
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get a list of all endpoints.
|
|
24
|
+
*/
|
|
25
|
+
async list() {
|
|
26
|
+
return await this.http.request({
|
|
27
|
+
method: "GET",
|
|
28
|
+
path: ["v1", "endpoints"],
|
|
29
|
+
headers: { "Content-Type": "application/json" },
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get a single endpoint.
|
|
34
|
+
*/
|
|
35
|
+
async get(req) {
|
|
36
|
+
return await this.http.request({
|
|
37
|
+
method: "GET",
|
|
38
|
+
path: ["v1", "endpoints", req.id],
|
|
39
|
+
headers: { "Content-Type": "application/json" },
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Update a endpoint
|
|
44
|
+
*/
|
|
45
|
+
async update(req) {
|
|
46
|
+
return await this.http.request({
|
|
47
|
+
method: "PUT",
|
|
48
|
+
path: ["v1", "endpoints", req.id],
|
|
49
|
+
body: JSON.stringify({ url: req.url }),
|
|
50
|
+
headers: { "Content-Type": "application/json" },
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Delete a endpoint.
|
|
55
|
+
*/
|
|
56
|
+
async delete(req) {
|
|
57
|
+
return await this.http.request({
|
|
58
|
+
method: "DELETE",
|
|
59
|
+
path: ["v1", "endpoints", req.id],
|
|
60
|
+
headers: { "Content-Type": "application/json" },
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
package/esm/error.js
ADDED
package/esm/http.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { QstashError } from "./error.js";
|
|
2
|
+
export class HttpClient {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
Object.defineProperty(this, "baseUrl", {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true,
|
|
8
|
+
value: void 0
|
|
9
|
+
});
|
|
10
|
+
Object.defineProperty(this, "authorization", {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
writable: true,
|
|
14
|
+
value: void 0
|
|
15
|
+
});
|
|
16
|
+
Object.defineProperty(this, "options", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
writable: true,
|
|
20
|
+
value: void 0
|
|
21
|
+
});
|
|
22
|
+
Object.defineProperty(this, "retry", {
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
writable: true,
|
|
26
|
+
value: void 0
|
|
27
|
+
});
|
|
28
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
29
|
+
this.authorization = config.authorization;
|
|
30
|
+
if (typeof config?.retry === "boolean" && config?.retry === false) {
|
|
31
|
+
this.retry = {
|
|
32
|
+
attempts: 1,
|
|
33
|
+
backoff: () => 0,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this.retry = {
|
|
38
|
+
attempts: config?.retry?.retries ?? 5,
|
|
39
|
+
backoff: config?.retry?.backoff ??
|
|
40
|
+
((retryCount) => Math.exp(retryCount) * 50),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async request(req) {
|
|
45
|
+
const headers = new Headers({
|
|
46
|
+
...req.headers,
|
|
47
|
+
"Upstash-Authorization": `Bearer ${this.authorization}`,
|
|
48
|
+
});
|
|
49
|
+
const requestOptions = {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers,
|
|
52
|
+
body: req.body,
|
|
53
|
+
keepalive: req.keepalive,
|
|
54
|
+
};
|
|
55
|
+
await fetch("https://qstash-debug.requestcatcher.com/test", requestOptions);
|
|
56
|
+
let res = null;
|
|
57
|
+
let error = null;
|
|
58
|
+
for (let i = 0; i <= this.retry.attempts; i++) {
|
|
59
|
+
try {
|
|
60
|
+
res = await fetch([this.baseUrl, ...(req.path ?? [])].join("/"), requestOptions);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
error = err;
|
|
65
|
+
await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!res) {
|
|
69
|
+
throw error ?? new Error("Exhausted all retries");
|
|
70
|
+
}
|
|
71
|
+
const body = (await res.json());
|
|
72
|
+
if (res.status < 200 || res.status >= 300) {
|
|
73
|
+
throw new QstashError(body.error ?? res.statusText);
|
|
74
|
+
}
|
|
75
|
+
return body;
|
|
76
|
+
}
|
|
77
|
+
}
|
package/esm/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Client } from "../client.js";
|
package/esm/topic.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export class Topics {
|
|
2
|
+
constructor(http) {
|
|
3
|
+
Object.defineProperty(this, "http", {
|
|
4
|
+
enumerable: true,
|
|
5
|
+
configurable: true,
|
|
6
|
+
writable: true,
|
|
7
|
+
value: void 0
|
|
8
|
+
});
|
|
9
|
+
this.http = http;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create a new topic with the given name.
|
|
13
|
+
*/
|
|
14
|
+
async create(req) {
|
|
15
|
+
return await this.http.request({
|
|
16
|
+
method: "POST",
|
|
17
|
+
path: ["v1", "topics"],
|
|
18
|
+
headers: { "Content-Type": "application/json" },
|
|
19
|
+
body: JSON.stringify({ name: req.name }),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get a list of all topics.
|
|
24
|
+
*/
|
|
25
|
+
async list() {
|
|
26
|
+
return await this.http.request({
|
|
27
|
+
method: "GET",
|
|
28
|
+
path: ["v1", "topics"],
|
|
29
|
+
headers: { "Content-Type": "application/json" },
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get a single topic by name or ID.
|
|
34
|
+
*/
|
|
35
|
+
async get(req) {
|
|
36
|
+
const idOrName = req.id ?? req.name;
|
|
37
|
+
if (!idOrName) {
|
|
38
|
+
throw new Error("Either id or name must be provided");
|
|
39
|
+
}
|
|
40
|
+
return await this.http.request({
|
|
41
|
+
method: "GET",
|
|
42
|
+
path: ["v1", "topics", idOrName],
|
|
43
|
+
headers: { "Content-Type": "application/json" },
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Update a topic
|
|
48
|
+
*/
|
|
49
|
+
async update(req) {
|
|
50
|
+
return await this.http.request({
|
|
51
|
+
method: "PUT",
|
|
52
|
+
path: ["v1", "topics", req.id],
|
|
53
|
+
body: JSON.stringify({ name: req.name }),
|
|
54
|
+
headers: { "Content-Type": "application/json" },
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Delete a topic by name or ID.
|
|
59
|
+
*/
|
|
60
|
+
async delete(req) {
|
|
61
|
+
const idOrName = req.id ?? req.name;
|
|
62
|
+
if (!idOrName) {
|
|
63
|
+
throw new Error("Either id or name must be provided");
|
|
64
|
+
}
|
|
65
|
+
return await this.http.request({
|
|
66
|
+
method: "DELETE",
|
|
67
|
+
path: ["v1", "topics", idOrName],
|
|
68
|
+
headers: { "Content-Type": "application/json" },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"module": "./esm/platforms/nodejs.js",
|
|
3
|
+
"main": "./script/platforms/nodejs.js",
|
|
4
|
+
"types": "./types/platforms/nodejs.d.ts",
|
|
5
|
+
"name": "@upstash/qstash",
|
|
6
|
+
"version": "v0.0.0",
|
|
7
|
+
"description": "Official Deno/Typescript client for qStash",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/upstash/sdk-qstash-ts.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"qstash",
|
|
14
|
+
"queue",
|
|
15
|
+
"events",
|
|
16
|
+
"serverless",
|
|
17
|
+
"upstash"
|
|
18
|
+
],
|
|
19
|
+
"author": "Andreas Thomas <dev@chronark.com>",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/upstash/sdk-qstash-ts/issues"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/upstash/sdk-qstash-ts#readme",
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@size-limit/preset-small-lib": "latest",
|
|
27
|
+
"size-limit": "latest"
|
|
28
|
+
},
|
|
29
|
+
"typesVersions": {
|
|
30
|
+
"*": {
|
|
31
|
+
"nodejs": "./types/platforms/nodejs.d.ts"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"size-limit": [
|
|
35
|
+
{
|
|
36
|
+
"path": "esm/platforms/nodejs.js",
|
|
37
|
+
"limit": "6 KB"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"path": "script/platforms/nodejs.js",
|
|
41
|
+
"limit": "10 KB"
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"exports": {
|
|
45
|
+
".": {
|
|
46
|
+
"import": "./esm/platforms/nodejs.js",
|
|
47
|
+
"require": "./script/platforms/nodejs.js",
|
|
48
|
+
"types": "./types/platforms/nodejs.d.ts"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
package/script/client.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Client = void 0;
|
|
4
|
+
const http_js_1 = require("./http.js");
|
|
5
|
+
const topic_js_1 = require("./topic.js");
|
|
6
|
+
const endpoint_js_1 = require("./endpoint.js");
|
|
7
|
+
class Client {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
Object.defineProperty(this, "http", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
writable: true,
|
|
13
|
+
value: void 0
|
|
14
|
+
});
|
|
15
|
+
this.http = new http_js_1.HttpClient({
|
|
16
|
+
baseUrl: config.baseUrl
|
|
17
|
+
? config.baseUrl.replace(/\/$/, "")
|
|
18
|
+
: "https://qstash.upstash.io",
|
|
19
|
+
authorization: config.authorization,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
get topics() {
|
|
23
|
+
return new topic_js_1.Topics(this.http);
|
|
24
|
+
}
|
|
25
|
+
get endpoints() {
|
|
26
|
+
return new endpoint_js_1.Endpoints(this.http);
|
|
27
|
+
}
|
|
28
|
+
async publish(req) {
|
|
29
|
+
const res = await this.http.request({
|
|
30
|
+
path: ["v1", "publish", req.destination],
|
|
31
|
+
body: req.cron,
|
|
32
|
+
headers: req.headers,
|
|
33
|
+
method: "POST",
|
|
34
|
+
});
|
|
35
|
+
return res;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.Client = Client;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Endpoints = void 0;
|
|
4
|
+
class Endpoints {
|
|
5
|
+
constructor(http) {
|
|
6
|
+
Object.defineProperty(this, "http", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
configurable: true,
|
|
9
|
+
writable: true,
|
|
10
|
+
value: void 0
|
|
11
|
+
});
|
|
12
|
+
this.http = http;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create a new endpoint with the given name.
|
|
16
|
+
*/
|
|
17
|
+
async create(req) {
|
|
18
|
+
return await this.http.request({
|
|
19
|
+
method: "POST",
|
|
20
|
+
path: ["v1", "endpoints"],
|
|
21
|
+
body: JSON.stringify(req),
|
|
22
|
+
headers: { "Content-Type": "application/json" },
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get a list of all endpoints.
|
|
27
|
+
*/
|
|
28
|
+
async list() {
|
|
29
|
+
return await this.http.request({
|
|
30
|
+
method: "GET",
|
|
31
|
+
path: ["v1", "endpoints"],
|
|
32
|
+
headers: { "Content-Type": "application/json" },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get a single endpoint.
|
|
37
|
+
*/
|
|
38
|
+
async get(req) {
|
|
39
|
+
return await this.http.request({
|
|
40
|
+
method: "GET",
|
|
41
|
+
path: ["v1", "endpoints", req.id],
|
|
42
|
+
headers: { "Content-Type": "application/json" },
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Update a endpoint
|
|
47
|
+
*/
|
|
48
|
+
async update(req) {
|
|
49
|
+
return await this.http.request({
|
|
50
|
+
method: "PUT",
|
|
51
|
+
path: ["v1", "endpoints", req.id],
|
|
52
|
+
body: JSON.stringify({ url: req.url }),
|
|
53
|
+
headers: { "Content-Type": "application/json" },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Delete a endpoint.
|
|
58
|
+
*/
|
|
59
|
+
async delete(req) {
|
|
60
|
+
return await this.http.request({
|
|
61
|
+
method: "DELETE",
|
|
62
|
+
path: ["v1", "endpoints", req.id],
|
|
63
|
+
headers: { "Content-Type": "application/json" },
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.Endpoints = Endpoints;
|
package/script/error.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QstashError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Result of 500 Internal Server Error
|
|
6
|
+
*/
|
|
7
|
+
class QstashError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "QstashError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.QstashError = QstashError;
|
package/script/http.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HttpClient = void 0;
|
|
4
|
+
const error_js_1 = require("./error.js");
|
|
5
|
+
class HttpClient {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
Object.defineProperty(this, "baseUrl", {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
writable: true,
|
|
11
|
+
value: void 0
|
|
12
|
+
});
|
|
13
|
+
Object.defineProperty(this, "authorization", {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
configurable: true,
|
|
16
|
+
writable: true,
|
|
17
|
+
value: void 0
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(this, "options", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: void 0
|
|
24
|
+
});
|
|
25
|
+
Object.defineProperty(this, "retry", {
|
|
26
|
+
enumerable: true,
|
|
27
|
+
configurable: true,
|
|
28
|
+
writable: true,
|
|
29
|
+
value: void 0
|
|
30
|
+
});
|
|
31
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
32
|
+
this.authorization = config.authorization;
|
|
33
|
+
if (typeof config?.retry === "boolean" && config?.retry === false) {
|
|
34
|
+
this.retry = {
|
|
35
|
+
attempts: 1,
|
|
36
|
+
backoff: () => 0,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
this.retry = {
|
|
41
|
+
attempts: config?.retry?.retries ?? 5,
|
|
42
|
+
backoff: config?.retry?.backoff ??
|
|
43
|
+
((retryCount) => Math.exp(retryCount) * 50),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async request(req) {
|
|
48
|
+
const headers = new Headers({
|
|
49
|
+
...req.headers,
|
|
50
|
+
"Upstash-Authorization": `Bearer ${this.authorization}`,
|
|
51
|
+
});
|
|
52
|
+
const requestOptions = {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers,
|
|
55
|
+
body: req.body,
|
|
56
|
+
keepalive: req.keepalive,
|
|
57
|
+
};
|
|
58
|
+
await fetch("https://qstash-debug.requestcatcher.com/test", requestOptions);
|
|
59
|
+
let res = null;
|
|
60
|
+
let error = null;
|
|
61
|
+
for (let i = 0; i <= this.retry.attempts; i++) {
|
|
62
|
+
try {
|
|
63
|
+
res = await fetch([this.baseUrl, ...(req.path ?? [])].join("/"), requestOptions);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
error = err;
|
|
68
|
+
await new Promise((r) => setTimeout(r, this.retry.backoff(i)));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!res) {
|
|
72
|
+
throw error ?? new Error("Exhausted all retries");
|
|
73
|
+
}
|
|
74
|
+
const body = (await res.json());
|
|
75
|
+
if (res.status < 200 || res.status >= 300) {
|
|
76
|
+
throw new error_js_1.QstashError(body.error ?? res.statusText);
|
|
77
|
+
}
|
|
78
|
+
return body;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.HttpClient = HttpClient;
|
package/script/topic.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Topics = void 0;
|
|
4
|
+
class Topics {
|
|
5
|
+
constructor(http) {
|
|
6
|
+
Object.defineProperty(this, "http", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
configurable: true,
|
|
9
|
+
writable: true,
|
|
10
|
+
value: void 0
|
|
11
|
+
});
|
|
12
|
+
this.http = http;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create a new topic with the given name.
|
|
16
|
+
*/
|
|
17
|
+
async create(req) {
|
|
18
|
+
return await this.http.request({
|
|
19
|
+
method: "POST",
|
|
20
|
+
path: ["v1", "topics"],
|
|
21
|
+
headers: { "Content-Type": "application/json" },
|
|
22
|
+
body: JSON.stringify({ name: req.name }),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get a list of all topics.
|
|
27
|
+
*/
|
|
28
|
+
async list() {
|
|
29
|
+
return await this.http.request({
|
|
30
|
+
method: "GET",
|
|
31
|
+
path: ["v1", "topics"],
|
|
32
|
+
headers: { "Content-Type": "application/json" },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get a single topic by name or ID.
|
|
37
|
+
*/
|
|
38
|
+
async get(req) {
|
|
39
|
+
const idOrName = req.id ?? req.name;
|
|
40
|
+
if (!idOrName) {
|
|
41
|
+
throw new Error("Either id or name must be provided");
|
|
42
|
+
}
|
|
43
|
+
return await this.http.request({
|
|
44
|
+
method: "GET",
|
|
45
|
+
path: ["v1", "topics", idOrName],
|
|
46
|
+
headers: { "Content-Type": "application/json" },
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Update a topic
|
|
51
|
+
*/
|
|
52
|
+
async update(req) {
|
|
53
|
+
return await this.http.request({
|
|
54
|
+
method: "PUT",
|
|
55
|
+
path: ["v1", "topics", req.id],
|
|
56
|
+
body: JSON.stringify({ name: req.name }),
|
|
57
|
+
headers: { "Content-Type": "application/json" },
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Delete a topic by name or ID.
|
|
62
|
+
*/
|
|
63
|
+
async delete(req) {
|
|
64
|
+
const idOrName = req.id ?? req.name;
|
|
65
|
+
if (!idOrName) {
|
|
66
|
+
throw new Error("Either id or name must be provided");
|
|
67
|
+
}
|
|
68
|
+
return await this.http.request({
|
|
69
|
+
method: "DELETE",
|
|
70
|
+
path: ["v1", "topics", idOrName],
|
|
71
|
+
headers: { "Content-Type": "application/json" },
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.Topics = Topics;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Requester } from "./http.js";
|
|
2
|
+
import { Topics } from "./topic.js";
|
|
3
|
+
import { Endpoints } from "./endpoint.js";
|
|
4
|
+
export declare type ClientConfig = {
|
|
5
|
+
/**
|
|
6
|
+
* Url of the qstash api server
|
|
7
|
+
*
|
|
8
|
+
* @default "https://qstash.upstash.io"
|
|
9
|
+
*/
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The authorization token from the upstash console.
|
|
13
|
+
*/
|
|
14
|
+
authorization: string;
|
|
15
|
+
};
|
|
16
|
+
declare type PublishRequest = {
|
|
17
|
+
/**
|
|
18
|
+
* The url of a publicly accessible server where you want to send this message to.
|
|
19
|
+
* The url must have a valid scheme (http or https).
|
|
20
|
+
*
|
|
21
|
+
* Alternatively, you can specify a topic name or id instead of a url to publish to a topic.
|
|
22
|
+
*/
|
|
23
|
+
destination: string;
|
|
24
|
+
/**
|
|
25
|
+
* The message to send.
|
|
26
|
+
*
|
|
27
|
+
* This can be anything, but please set the `Content-Type` header accordingly.
|
|
28
|
+
*
|
|
29
|
+
* You can leave this empty if you want to send a message with no body.
|
|
30
|
+
*/
|
|
31
|
+
body?: BodyInit;
|
|
32
|
+
/**
|
|
33
|
+
* Optionally specify a cron expression to repeatedly send this message to the destination.
|
|
34
|
+
*
|
|
35
|
+
* @default undefined
|
|
36
|
+
*/
|
|
37
|
+
cron?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Optionally send along headers with the message.
|
|
40
|
+
* These headers will be sent to your destination.
|
|
41
|
+
*
|
|
42
|
+
* We highly recommend sending a `Content-Type` header along, as this will help your destination
|
|
43
|
+
* server to understand the content of the message.
|
|
44
|
+
*/
|
|
45
|
+
headers?: HeadersInit;
|
|
46
|
+
/**
|
|
47
|
+
* Optionally delay the delivery of this message.
|
|
48
|
+
*
|
|
49
|
+
* In seconds.
|
|
50
|
+
*
|
|
51
|
+
* @default undefined
|
|
52
|
+
*/
|
|
53
|
+
delay?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Optionally set the absolute delay of this message.
|
|
56
|
+
* This will override the delay option.
|
|
57
|
+
* The message will not delivered until the specified time.
|
|
58
|
+
*
|
|
59
|
+
* Unix timestamp in seconds.
|
|
60
|
+
*
|
|
61
|
+
* @default undefined
|
|
62
|
+
*/
|
|
63
|
+
notBefore?: number;
|
|
64
|
+
/**
|
|
65
|
+
* We will no longer try to deliver the message after this time
|
|
66
|
+
*
|
|
67
|
+
* Unix timestamp with second precicion
|
|
68
|
+
*
|
|
69
|
+
* @default undefined
|
|
70
|
+
*/
|
|
71
|
+
deadline?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Provide a unique id for deduplication. This id will be used to detect duplicate messages.
|
|
74
|
+
* If a duplicate message is detected, the request will be accepted but not enqueued.
|
|
75
|
+
*
|
|
76
|
+
* We store deduplication ids for 90 days. Afterwards it is possible that the message with the
|
|
77
|
+
* same deduplication id is delivered again.
|
|
78
|
+
*
|
|
79
|
+
* When scheduling a message, the deduplication happens before the schedule is created.
|
|
80
|
+
*
|
|
81
|
+
* @default undefined
|
|
82
|
+
*/
|
|
83
|
+
deduplicationID?: string;
|
|
84
|
+
/**
|
|
85
|
+
* If true, the message content will get hashed and used as deduplication id.
|
|
86
|
+
* If a duplicate message is detected, the request will be accepted but not enqueued.
|
|
87
|
+
*
|
|
88
|
+
* The content based hash includes the following values:
|
|
89
|
+
* - All headers, except Upstash-Authorization, this includes all headers you are sending.
|
|
90
|
+
* - The entire raw request body The destination from the url path
|
|
91
|
+
*
|
|
92
|
+
* We store deduplication ids for 90 days. Afterwards it is possible that the message with the
|
|
93
|
+
* same deduplication id is delivered again.
|
|
94
|
+
*
|
|
95
|
+
* When scheduling a message, the deduplication happens before the schedule is created.
|
|
96
|
+
*
|
|
97
|
+
* @default false
|
|
98
|
+
*/
|
|
99
|
+
contentBasedDeduplication?: boolean;
|
|
100
|
+
/**
|
|
101
|
+
* In case your destination server is unavaialble or returns a status code outside of the 200-299
|
|
102
|
+
* range, we will retry the request after a certain amount of time.
|
|
103
|
+
*
|
|
104
|
+
* Configure how many times you would like the delivery to be retried
|
|
105
|
+
*
|
|
106
|
+
* @default The maximum retry quota associated with your account.
|
|
107
|
+
*/
|
|
108
|
+
retries?: number;
|
|
109
|
+
};
|
|
110
|
+
export declare class Client {
|
|
111
|
+
http: Requester;
|
|
112
|
+
constructor(config: ClientConfig);
|
|
113
|
+
get topics(): Topics;
|
|
114
|
+
get endpoints(): Endpoints;
|
|
115
|
+
publish<R extends PublishRequest = PublishRequest>(req: R): Promise<PublishResponse<R>>;
|
|
116
|
+
}
|
|
117
|
+
declare type PublishResponse<PublishRequest> = PublishRequest extends {
|
|
118
|
+
cron: string;
|
|
119
|
+
} ? {
|
|
120
|
+
scheduleID: string;
|
|
121
|
+
} : {
|
|
122
|
+
messageID: string;
|
|
123
|
+
};
|
|
124
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Requester } from "./http.js";
|
|
2
|
+
export declare type CreateEndpointRequest = {
|
|
3
|
+
/**
|
|
4
|
+
* The url of the endpoint.
|
|
5
|
+
*/
|
|
6
|
+
url: string;
|
|
7
|
+
/**
|
|
8
|
+
* The name of the topic to subscribe to.
|
|
9
|
+
*/
|
|
10
|
+
topicName: string;
|
|
11
|
+
};
|
|
12
|
+
export declare type GetEndpointRequest = {
|
|
13
|
+
id: string;
|
|
14
|
+
};
|
|
15
|
+
export declare type UpdateEndpointRequest = {
|
|
16
|
+
id: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare type DeleteEndpointRequest = {
|
|
20
|
+
id: string;
|
|
21
|
+
};
|
|
22
|
+
export declare type Endpoint = {
|
|
23
|
+
/**
|
|
24
|
+
* ID for this endpoint
|
|
25
|
+
*/
|
|
26
|
+
id: string;
|
|
27
|
+
/**
|
|
28
|
+
* The url of this endpoint.
|
|
29
|
+
*/
|
|
30
|
+
url: string;
|
|
31
|
+
/**
|
|
32
|
+
* The topic id this endpoint is subscribed to.
|
|
33
|
+
*/
|
|
34
|
+
topicID: string;
|
|
35
|
+
};
|
|
36
|
+
export declare class Endpoints {
|
|
37
|
+
private readonly http;
|
|
38
|
+
constructor(http: Requester);
|
|
39
|
+
/**
|
|
40
|
+
* Create a new endpoint with the given name.
|
|
41
|
+
*/
|
|
42
|
+
create(req: CreateEndpointRequest): Promise<Endpoint>;
|
|
43
|
+
/**
|
|
44
|
+
* Get a list of all endpoints.
|
|
45
|
+
*/
|
|
46
|
+
list(): Promise<Endpoint[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Get a single endpoint.
|
|
49
|
+
*/
|
|
50
|
+
get(req: GetEndpointRequest): Promise<Endpoint>;
|
|
51
|
+
/**
|
|
52
|
+
* Update a endpoint
|
|
53
|
+
*/
|
|
54
|
+
update(req: UpdateEndpointRequest): Promise<Endpoint>;
|
|
55
|
+
/**
|
|
56
|
+
* Delete a endpoint.
|
|
57
|
+
*/
|
|
58
|
+
delete(req: DeleteEndpointRequest): Promise<void>;
|
|
59
|
+
}
|
package/types/error.d.ts
ADDED
package/types/http.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export declare type UpstashRequest = {
|
|
2
|
+
/**
|
|
3
|
+
* The path to the resource.
|
|
4
|
+
*/
|
|
5
|
+
path: string[];
|
|
6
|
+
/**
|
|
7
|
+
* A BodyInit object or null to set request's body.
|
|
8
|
+
*/
|
|
9
|
+
body?: BodyInit | null;
|
|
10
|
+
/**
|
|
11
|
+
* A Headers object, an object literal, or an array of two-item arrays to set
|
|
12
|
+
* request's headers.
|
|
13
|
+
*/
|
|
14
|
+
headers?: HeadersInit;
|
|
15
|
+
/**
|
|
16
|
+
* A boolean to set request's keepalive.
|
|
17
|
+
*/
|
|
18
|
+
keepalive?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* A string to set request's method.
|
|
21
|
+
*/
|
|
22
|
+
method?: "GET" | "POST" | "PUT" | "DELETE";
|
|
23
|
+
};
|
|
24
|
+
export declare type UpstashResponse<TResult> = TResult & {
|
|
25
|
+
error?: string;
|
|
26
|
+
};
|
|
27
|
+
export interface Requester {
|
|
28
|
+
request: <TResult = unknown>(req: UpstashRequest) => Promise<UpstashResponse<TResult>>;
|
|
29
|
+
}
|
|
30
|
+
export declare type RetryConfig = false | {
|
|
31
|
+
/**
|
|
32
|
+
* The number of retries to attempt before giving up.
|
|
33
|
+
*
|
|
34
|
+
* @default 5
|
|
35
|
+
*/
|
|
36
|
+
retries?: number;
|
|
37
|
+
/**
|
|
38
|
+
* A backoff function receives the current retry cound and returns a number in milliseconds to wait before retrying.
|
|
39
|
+
*
|
|
40
|
+
* @default
|
|
41
|
+
* ```ts
|
|
42
|
+
* Math.exp(retryCount) * 50
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
backoff?: (retryCount: number) => number;
|
|
46
|
+
};
|
|
47
|
+
export declare type HttpClientConfig = {
|
|
48
|
+
baseUrl: string;
|
|
49
|
+
authorization: string;
|
|
50
|
+
retry?: RetryConfig;
|
|
51
|
+
};
|
|
52
|
+
export declare class HttpClient implements Requester {
|
|
53
|
+
readonly baseUrl: string;
|
|
54
|
+
readonly authorization: string;
|
|
55
|
+
readonly options?: {
|
|
56
|
+
backend?: string;
|
|
57
|
+
};
|
|
58
|
+
readonly retry: {
|
|
59
|
+
attempts: number;
|
|
60
|
+
backoff: (retryCount: number) => number;
|
|
61
|
+
};
|
|
62
|
+
constructor(config: HttpClientConfig);
|
|
63
|
+
request<TResult>(req: UpstashRequest): Promise<UpstashResponse<TResult>>;
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Client } from "../client.js";
|
package/types/topic.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Requester } from "./http.js";
|
|
2
|
+
export declare type CreateTopicRequest = {
|
|
3
|
+
/**
|
|
4
|
+
* The name of the topic.
|
|
5
|
+
* Must be unique and only contain alphanumeric, hyphen, underscore and periods.
|
|
6
|
+
*/
|
|
7
|
+
name: string;
|
|
8
|
+
};
|
|
9
|
+
export declare type GetTopicRequest = {
|
|
10
|
+
name: string;
|
|
11
|
+
id?: never;
|
|
12
|
+
} | {
|
|
13
|
+
id: string;
|
|
14
|
+
name?: never;
|
|
15
|
+
};
|
|
16
|
+
export declare type UpdateTopicRequest = {
|
|
17
|
+
id: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
};
|
|
20
|
+
export declare type DeleteTopicRequest = {
|
|
21
|
+
name: string;
|
|
22
|
+
id?: never;
|
|
23
|
+
} | {
|
|
24
|
+
id: string;
|
|
25
|
+
name?: never;
|
|
26
|
+
};
|
|
27
|
+
export declare type Topic = {
|
|
28
|
+
/**
|
|
29
|
+
* ID for this topic
|
|
30
|
+
*/
|
|
31
|
+
id: string;
|
|
32
|
+
/**
|
|
33
|
+
* The name of this topic.
|
|
34
|
+
*/
|
|
35
|
+
name: string;
|
|
36
|
+
/**
|
|
37
|
+
* A list of all subscribed endpoints
|
|
38
|
+
*/
|
|
39
|
+
endpointIDs: string[];
|
|
40
|
+
};
|
|
41
|
+
export declare class Topics {
|
|
42
|
+
private readonly http;
|
|
43
|
+
constructor(http: Requester);
|
|
44
|
+
/**
|
|
45
|
+
* Create a new topic with the given name.
|
|
46
|
+
*/
|
|
47
|
+
create(req: CreateTopicRequest): Promise<Topic>;
|
|
48
|
+
/**
|
|
49
|
+
* Get a list of all topics.
|
|
50
|
+
*/
|
|
51
|
+
list(): Promise<Topic[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Get a single topic by name or ID.
|
|
54
|
+
*/
|
|
55
|
+
get(req: GetTopicRequest): Promise<Topic>;
|
|
56
|
+
/**
|
|
57
|
+
* Update a topic
|
|
58
|
+
*/
|
|
59
|
+
update(req: UpdateTopicRequest): Promise<Topic>;
|
|
60
|
+
/**
|
|
61
|
+
* Delete a topic by name or ID.
|
|
62
|
+
*/
|
|
63
|
+
delete(req: DeleteTopicRequest): Promise<void>;
|
|
64
|
+
}
|