@upstash/qstash 0.0.3 → 0.0.7
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/README.md +119 -1
- package/esm/_dnt.shims.js +65 -0
- package/esm/consumer.js +82 -0
- package/esm/deps/deno.land/std@0.144.0/encoding/base64.js +115 -0
- package/esm/deps/deno.land/std@0.144.0/encoding/base64url.js +42 -0
- package/esm/entrypoints/nextjs.js +49 -0
- package/package.json +15 -21
- package/script/_dnt.shims.js +70 -0
- package/script/consumer.js +110 -0
- package/script/deps/deno.land/std@0.144.0/encoding/base64.js +120 -0
- package/script/deps/deno.land/std@0.144.0/encoding/base64url.js +71 -0
- package/script/entrypoints/nextjs.js +53 -0
- package/types/_dnt.shims.d.ts +7 -0
- package/types/consumer.d.ts +43 -0
- package/types/deps/deno.land/std@0.144.0/encoding/base64.d.ts +11 -0
- package/types/deps/deno.land/std@0.144.0/encoding/base64url.d.ts +11 -0
- package/types/entrypoints/nextjs.d.ts +6 -0
- package/esm/client.js +0 -133
- package/esm/endpoints.js +0 -63
- package/esm/error.js +0 -9
- package/esm/http.js +0 -81
- package/esm/messages.js +0 -64
- package/esm/platforms/nodejs.js +0 -1
- package/esm/schedules.js +0 -41
- package/esm/topics.js +0 -71
- package/esm/types.js +0 -1
- package/script/client.js +0 -137
- package/script/endpoints.js +0 -67
- package/script/error.js +0 -13
- package/script/http.js +0 -85
- package/script/messages.js +0 -68
- package/script/platforms/nodejs.js +0 -5
- package/script/schedules.js +0 -45
- package/script/topics.js +0 -75
- package/script/types.js +0 -2
- package/types/client.d.ts +0 -198
- package/types/endpoints.d.ts +0 -59
- package/types/error.d.ts +0 -6
- package/types/http.d.ts +0 -65
- package/types/messages.d.ts +0 -66
- package/types/platforms/nodejs.d.ts +0 -1
- package/types/schedules.d.ts +0 -63
- package/types/topics.d.ts +0 -64
- package/types/types.d.ts +0 -20
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.Consumer = exports.SignatureError = void 0;
|
|
27
|
+
const dntShim = __importStar(require("./_dnt.shims.js"));
|
|
28
|
+
const base64url = __importStar(require("./deps/deno.land/std@0.144.0/encoding/base64url.js"));
|
|
29
|
+
const base64 = __importStar(require("./deps/deno.land/std@0.144.0/encoding/base64.js"));
|
|
30
|
+
class SignatureError extends Error {
|
|
31
|
+
constructor(message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "SignatureError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.SignatureError = SignatureError;
|
|
37
|
+
/**
|
|
38
|
+
* Consumer offers a simlpe way to verify the signature of a request.
|
|
39
|
+
*/
|
|
40
|
+
class Consumer {
|
|
41
|
+
constructor(config) {
|
|
42
|
+
Object.defineProperty(this, "currentSigningKey", {
|
|
43
|
+
enumerable: true,
|
|
44
|
+
configurable: true,
|
|
45
|
+
writable: true,
|
|
46
|
+
value: void 0
|
|
47
|
+
});
|
|
48
|
+
Object.defineProperty(this, "nextSigningKey", {
|
|
49
|
+
enumerable: true,
|
|
50
|
+
configurable: true,
|
|
51
|
+
writable: true,
|
|
52
|
+
value: void 0
|
|
53
|
+
});
|
|
54
|
+
this.currentSigningKey = config.currentSigningKey;
|
|
55
|
+
this.nextSigningKey = config.nextSigningKey;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Verify the signature of a request.
|
|
59
|
+
*
|
|
60
|
+
* Tries to verify the signature with the current signing key.
|
|
61
|
+
* If that fails, maybe because you have rotated the keys recently, it will
|
|
62
|
+
* try to verify the signature with the next signing key.
|
|
63
|
+
*
|
|
64
|
+
* If that fails, the signature is invalid and a `SignatureError` is thrown.
|
|
65
|
+
*/
|
|
66
|
+
async verify(req) {
|
|
67
|
+
const isValid = await this.verifyWithKey(this.currentSigningKey, req);
|
|
68
|
+
if (isValid) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
return this.verifyWithKey(this.nextSigningKey, req);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Verify signature with a specific signing key
|
|
75
|
+
*/
|
|
76
|
+
async verifyWithKey(key, req) {
|
|
77
|
+
const parts = req.signature.split(".");
|
|
78
|
+
if (parts.length !== 3) {
|
|
79
|
+
throw new SignatureError("`Upstash-Signature` header is not a valid signature");
|
|
80
|
+
}
|
|
81
|
+
const [header, payload, signature] = parts;
|
|
82
|
+
const k = await dntShim.crypto.subtle.importKey("raw", new TextEncoder().encode(key), { name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"]);
|
|
83
|
+
const isValid = await dntShim.crypto.subtle.verify({ name: "HMAC" }, k, base64url.decode(signature), new TextEncoder().encode(`${header}.${payload}`));
|
|
84
|
+
if (!isValid) {
|
|
85
|
+
throw new SignatureError("signature does not match");
|
|
86
|
+
}
|
|
87
|
+
const p = JSON.parse(new TextDecoder().decode(base64url.decode(payload)));
|
|
88
|
+
console.log(JSON.stringify(p, null, 2));
|
|
89
|
+
if (p.iss !== "Upstash") {
|
|
90
|
+
throw new SignatureError(`invalid issuer: ${p.iss}`);
|
|
91
|
+
}
|
|
92
|
+
if (p.sub !== req.url) {
|
|
93
|
+
throw new SignatureError(`invalid subject: ${p.sub}, want: ${req.url}`);
|
|
94
|
+
}
|
|
95
|
+
const now = Math.floor(Date.now() / 1000);
|
|
96
|
+
if (now > p.exp) {
|
|
97
|
+
console.log({ now, exp: p.exp });
|
|
98
|
+
throw new SignatureError("token has expired");
|
|
99
|
+
}
|
|
100
|
+
if (now < p.nbf) {
|
|
101
|
+
throw new SignatureError("token is not yet valid");
|
|
102
|
+
}
|
|
103
|
+
const bodyHash = await dntShim.crypto.subtle.digest("SHA-256", new TextEncoder().encode(req.body));
|
|
104
|
+
if (p.body != base64.encode(bodyHash)) {
|
|
105
|
+
throw new SignatureError("body hash does not match");
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.Consumer = Consumer;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
|
3
|
+
// This module is browser compatible.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.decode = exports.encode = void 0;
|
|
6
|
+
const base64abc = [
|
|
7
|
+
"A",
|
|
8
|
+
"B",
|
|
9
|
+
"C",
|
|
10
|
+
"D",
|
|
11
|
+
"E",
|
|
12
|
+
"F",
|
|
13
|
+
"G",
|
|
14
|
+
"H",
|
|
15
|
+
"I",
|
|
16
|
+
"J",
|
|
17
|
+
"K",
|
|
18
|
+
"L",
|
|
19
|
+
"M",
|
|
20
|
+
"N",
|
|
21
|
+
"O",
|
|
22
|
+
"P",
|
|
23
|
+
"Q",
|
|
24
|
+
"R",
|
|
25
|
+
"S",
|
|
26
|
+
"T",
|
|
27
|
+
"U",
|
|
28
|
+
"V",
|
|
29
|
+
"W",
|
|
30
|
+
"X",
|
|
31
|
+
"Y",
|
|
32
|
+
"Z",
|
|
33
|
+
"a",
|
|
34
|
+
"b",
|
|
35
|
+
"c",
|
|
36
|
+
"d",
|
|
37
|
+
"e",
|
|
38
|
+
"f",
|
|
39
|
+
"g",
|
|
40
|
+
"h",
|
|
41
|
+
"i",
|
|
42
|
+
"j",
|
|
43
|
+
"k",
|
|
44
|
+
"l",
|
|
45
|
+
"m",
|
|
46
|
+
"n",
|
|
47
|
+
"o",
|
|
48
|
+
"p",
|
|
49
|
+
"q",
|
|
50
|
+
"r",
|
|
51
|
+
"s",
|
|
52
|
+
"t",
|
|
53
|
+
"u",
|
|
54
|
+
"v",
|
|
55
|
+
"w",
|
|
56
|
+
"x",
|
|
57
|
+
"y",
|
|
58
|
+
"z",
|
|
59
|
+
"0",
|
|
60
|
+
"1",
|
|
61
|
+
"2",
|
|
62
|
+
"3",
|
|
63
|
+
"4",
|
|
64
|
+
"5",
|
|
65
|
+
"6",
|
|
66
|
+
"7",
|
|
67
|
+
"8",
|
|
68
|
+
"9",
|
|
69
|
+
"+",
|
|
70
|
+
"/",
|
|
71
|
+
];
|
|
72
|
+
/**
|
|
73
|
+
* CREDIT: https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
|
|
74
|
+
* Encodes a given Uint8Array, ArrayBuffer or string into RFC4648 base64 representation
|
|
75
|
+
* @param data
|
|
76
|
+
*/
|
|
77
|
+
function encode(data) {
|
|
78
|
+
const uint8 = typeof data === "string"
|
|
79
|
+
? new TextEncoder().encode(data)
|
|
80
|
+
: data instanceof Uint8Array
|
|
81
|
+
? data
|
|
82
|
+
: new Uint8Array(data);
|
|
83
|
+
let result = "", i;
|
|
84
|
+
const l = uint8.length;
|
|
85
|
+
for (i = 2; i < l; i += 3) {
|
|
86
|
+
result += base64abc[uint8[i - 2] >> 2];
|
|
87
|
+
result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)];
|
|
88
|
+
result += base64abc[((uint8[i - 1] & 0x0f) << 2) | (uint8[i] >> 6)];
|
|
89
|
+
result += base64abc[uint8[i] & 0x3f];
|
|
90
|
+
}
|
|
91
|
+
if (i === l + 1) {
|
|
92
|
+
// 1 octet yet to write
|
|
93
|
+
result += base64abc[uint8[i - 2] >> 2];
|
|
94
|
+
result += base64abc[(uint8[i - 2] & 0x03) << 4];
|
|
95
|
+
result += "==";
|
|
96
|
+
}
|
|
97
|
+
if (i === l) {
|
|
98
|
+
// 2 octets yet to write
|
|
99
|
+
result += base64abc[uint8[i - 2] >> 2];
|
|
100
|
+
result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)];
|
|
101
|
+
result += base64abc[(uint8[i - 1] & 0x0f) << 2];
|
|
102
|
+
result += "=";
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
exports.encode = encode;
|
|
107
|
+
/**
|
|
108
|
+
* Decodes a given RFC4648 base64 encoded string
|
|
109
|
+
* @param b64
|
|
110
|
+
*/
|
|
111
|
+
function decode(b64) {
|
|
112
|
+
const binString = atob(b64);
|
|
113
|
+
const size = binString.length;
|
|
114
|
+
const bytes = new Uint8Array(size);
|
|
115
|
+
for (let i = 0; i < size; i++) {
|
|
116
|
+
bytes[i] = binString.charCodeAt(i);
|
|
117
|
+
}
|
|
118
|
+
return bytes;
|
|
119
|
+
}
|
|
120
|
+
exports.decode = decode;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
|
3
|
+
// This module is browser compatible.
|
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
+
if (k2 === undefined) k2 = k;
|
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
+
}
|
|
10
|
+
Object.defineProperty(o, k2, desc);
|
|
11
|
+
}) : (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
o[k2] = m[k];
|
|
14
|
+
}));
|
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
+
}) : function(o, v) {
|
|
18
|
+
o["default"] = v;
|
|
19
|
+
});
|
|
20
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
21
|
+
if (mod && mod.__esModule) return mod;
|
|
22
|
+
var result = {};
|
|
23
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
24
|
+
__setModuleDefault(result, mod);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.decode = exports.encode = exports.addPaddingToBase64url = void 0;
|
|
29
|
+
const base64 = __importStar(require("./base64.js"));
|
|
30
|
+
/*
|
|
31
|
+
* Some variants allow or require omitting the padding '=' signs:
|
|
32
|
+
* https://en.wikipedia.org/wiki/Base64#The_URL_applications
|
|
33
|
+
* @param base64url
|
|
34
|
+
*/
|
|
35
|
+
function addPaddingToBase64url(base64url) {
|
|
36
|
+
if (base64url.length % 4 === 2)
|
|
37
|
+
return base64url + "==";
|
|
38
|
+
if (base64url.length % 4 === 3)
|
|
39
|
+
return base64url + "=";
|
|
40
|
+
if (base64url.length % 4 === 1) {
|
|
41
|
+
throw new TypeError("Illegal base64url string!");
|
|
42
|
+
}
|
|
43
|
+
return base64url;
|
|
44
|
+
}
|
|
45
|
+
exports.addPaddingToBase64url = addPaddingToBase64url;
|
|
46
|
+
function convertBase64urlToBase64(b64url) {
|
|
47
|
+
if (!/^[-_A-Z0-9]*?={0,2}$/i.test(b64url)) {
|
|
48
|
+
// Contains characters not part of base64url spec.
|
|
49
|
+
throw new TypeError("Failed to decode base64url: invalid character");
|
|
50
|
+
}
|
|
51
|
+
return addPaddingToBase64url(b64url).replace(/\-/g, "+").replace(/_/g, "/");
|
|
52
|
+
}
|
|
53
|
+
function convertBase64ToBase64url(b64) {
|
|
54
|
+
return b64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Encodes a given ArrayBuffer or string into a base64url representation
|
|
58
|
+
* @param data
|
|
59
|
+
*/
|
|
60
|
+
function encode(data) {
|
|
61
|
+
return convertBase64ToBase64url(base64.encode(data));
|
|
62
|
+
}
|
|
63
|
+
exports.encode = encode;
|
|
64
|
+
/**
|
|
65
|
+
* Converts given base64url encoded data back to original
|
|
66
|
+
* @param b64url
|
|
67
|
+
*/
|
|
68
|
+
function decode(b64url) {
|
|
69
|
+
return base64.decode(convertBase64urlToBase64(b64url));
|
|
70
|
+
}
|
|
71
|
+
exports.decode = decode;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifySignature = void 0;
|
|
4
|
+
const micro_1 = require("micro");
|
|
5
|
+
const consumer_js_1 = require("../consumer.js");
|
|
6
|
+
function verifySignature(handler, config) {
|
|
7
|
+
const currentSigningKey = config?.currentSigningKey ??
|
|
8
|
+
process.env.get["QSTASH_CURRENT_SIGNING_KEY"];
|
|
9
|
+
if (!currentSigningKey) {
|
|
10
|
+
throw new Error("currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY");
|
|
11
|
+
}
|
|
12
|
+
const nextSigningKey = config?.nextSigningKey ??
|
|
13
|
+
process.env.get["QSTASH_NEXT_SIGNING_KEY"];
|
|
14
|
+
if (!nextSigningKey) {
|
|
15
|
+
throw new Error("nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY");
|
|
16
|
+
}
|
|
17
|
+
const consumer = new consumer_js_1.Consumer({
|
|
18
|
+
currentSigningKey,
|
|
19
|
+
nextSigningKey,
|
|
20
|
+
});
|
|
21
|
+
return async (req, res) => {
|
|
22
|
+
const signature = req.headers["upstash-signature"];
|
|
23
|
+
if (!signature) {
|
|
24
|
+
throw new Error("`Upstash-Signature` header is missing");
|
|
25
|
+
}
|
|
26
|
+
if (typeof signature !== "string") {
|
|
27
|
+
throw new Error("`Upstash-Signature` header is not a string");
|
|
28
|
+
}
|
|
29
|
+
const body = (await (0, micro_1.buffer)(req)).toString();
|
|
30
|
+
const url = new URL(req.url, `https://${req.headers.host}`).href;
|
|
31
|
+
console.log({ reqUrl: req.url, url });
|
|
32
|
+
const isValid = await consumer.verify({ signature, body, url });
|
|
33
|
+
if (!isValid) {
|
|
34
|
+
res.status(400);
|
|
35
|
+
res.send("Invalid signature");
|
|
36
|
+
return res.end();
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
if (req.headers["content-type"] === "application/json") {
|
|
40
|
+
req.body = JSON.parse(body);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
req.body = body;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
req.body = body;
|
|
48
|
+
console.log("body is not json");
|
|
49
|
+
}
|
|
50
|
+
return handler(req, res);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
exports.verifySignature = verifySignature;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Deno } from "@deno/shim-deno";
|
|
2
|
+
export { Deno } from "@deno/shim-deno";
|
|
3
|
+
export { crypto, type Crypto, type SubtleCrypto, type AlgorithmIdentifier, type Algorithm, type RsaOaepParams, type BufferSource, type AesCtrParams, type AesCbcParams, type AesGcmParams, type CryptoKey, type KeyAlgorithm, type KeyType, type KeyUsage, type EcdhKeyDeriveParams, type HkdfParams, type HashAlgorithmIdentifier, type Pbkdf2Params, type AesDerivedKeyParams, type HmacImportParams, type JsonWebKey, type RsaOtherPrimesInfo, type KeyFormat, type RsaHashedKeyGenParams, type RsaKeyGenParams, type BigInteger, type EcKeyGenParams, type NamedCurve, type CryptoKeyPair, type AesKeyGenParams, type HmacKeyGenParams, type RsaHashedImportParams, type EcKeyImportParams, type AesKeyAlgorithm, type RsaPssParams, type EcdsaParams } from "@deno/shim-crypto";
|
|
4
|
+
export declare const dntGlobalThis: Omit<typeof globalThis, "crypto" | "Deno"> & {
|
|
5
|
+
Deno: typeof Deno;
|
|
6
|
+
crypto: import("@deno/shim-crypto").Crypto;
|
|
7
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export declare type ConsumerConfig = {
|
|
2
|
+
currentSigningKey: string;
|
|
3
|
+
nextSigningKey: string;
|
|
4
|
+
};
|
|
5
|
+
export declare type VerifyRequest = {
|
|
6
|
+
/**
|
|
7
|
+
* The signature from the `upstash-signature` header.
|
|
8
|
+
*/
|
|
9
|
+
signature: string;
|
|
10
|
+
/**
|
|
11
|
+
* The raw request body.
|
|
12
|
+
*/
|
|
13
|
+
body: string;
|
|
14
|
+
/**
|
|
15
|
+
* URL of the endpoint where the request was sent to.
|
|
16
|
+
*/
|
|
17
|
+
url: string;
|
|
18
|
+
};
|
|
19
|
+
export declare class SignatureError extends Error {
|
|
20
|
+
constructor(message: string);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Consumer offers a simlpe way to verify the signature of a request.
|
|
24
|
+
*/
|
|
25
|
+
export declare class Consumer {
|
|
26
|
+
private readonly currentSigningKey;
|
|
27
|
+
private readonly nextSigningKey;
|
|
28
|
+
constructor(config: ConsumerConfig);
|
|
29
|
+
/**
|
|
30
|
+
* Verify the signature of a request.
|
|
31
|
+
*
|
|
32
|
+
* Tries to verify the signature with the current signing key.
|
|
33
|
+
* If that fails, maybe because you have rotated the keys recently, it will
|
|
34
|
+
* try to verify the signature with the next signing key.
|
|
35
|
+
*
|
|
36
|
+
* If that fails, the signature is invalid and a `SignatureError` is thrown.
|
|
37
|
+
*/
|
|
38
|
+
verify(req: VerifyRequest): Promise<boolean>;
|
|
39
|
+
/**
|
|
40
|
+
* Verify signature with a specific signing key
|
|
41
|
+
*/
|
|
42
|
+
private verifyWithKey;
|
|
43
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CREDIT: https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
|
|
3
|
+
* Encodes a given Uint8Array, ArrayBuffer or string into RFC4648 base64 representation
|
|
4
|
+
* @param data
|
|
5
|
+
*/
|
|
6
|
+
export declare function encode(data: ArrayBuffer | string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Decodes a given RFC4648 base64 encoded string
|
|
9
|
+
* @param b64
|
|
10
|
+
*/
|
|
11
|
+
export declare function decode(b64: string): Uint8Array;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function addPaddingToBase64url(base64url: string): string;
|
|
2
|
+
/**
|
|
3
|
+
* Encodes a given ArrayBuffer or string into a base64url representation
|
|
4
|
+
* @param data
|
|
5
|
+
*/
|
|
6
|
+
export declare function encode(data: ArrayBuffer | string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Converts given base64url encoded data back to original
|
|
9
|
+
* @param b64url
|
|
10
|
+
*/
|
|
11
|
+
export declare function decode(b64url: string): Uint8Array;
|
package/esm/client.js
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { HttpClient } from "./http.js";
|
|
2
|
-
import { Topics } from "./topics.js";
|
|
3
|
-
import { Messages } from "./messages.js";
|
|
4
|
-
import { Schedules } from "./schedules.js";
|
|
5
|
-
import { Endpoints } from "./endpoints.js";
|
|
6
|
-
export class Client {
|
|
7
|
-
constructor(config) {
|
|
8
|
-
Object.defineProperty(this, "http", {
|
|
9
|
-
enumerable: true,
|
|
10
|
-
configurable: true,
|
|
11
|
-
writable: true,
|
|
12
|
-
value: void 0
|
|
13
|
-
});
|
|
14
|
-
this.http = new HttpClient({
|
|
15
|
-
baseUrl: config.baseUrl
|
|
16
|
-
? config.baseUrl.replace(/\/$/, "")
|
|
17
|
-
: "https://qstash.upstash.io",
|
|
18
|
-
authorization: config.authorization,
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Access the topic API.
|
|
23
|
-
*
|
|
24
|
-
* Create, read, update or delete topics.
|
|
25
|
-
*/
|
|
26
|
-
get topics() {
|
|
27
|
-
return new Topics(this.http);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Access the endpoint API.
|
|
31
|
-
*
|
|
32
|
-
* Create, read, update or delete endpoints.
|
|
33
|
-
*/
|
|
34
|
-
get endpoints() {
|
|
35
|
-
return new Endpoints(this.http);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Access the message API.
|
|
39
|
-
*
|
|
40
|
-
* Read or cancel messages.
|
|
41
|
-
*/
|
|
42
|
-
get messages() {
|
|
43
|
-
return new Messages(this.http);
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Access the schedule API.
|
|
47
|
-
*
|
|
48
|
-
* Read or delete schedules.
|
|
49
|
-
*/
|
|
50
|
-
get schedules() {
|
|
51
|
-
return new Schedules(this.http);
|
|
52
|
-
}
|
|
53
|
-
async publish(req) {
|
|
54
|
-
const destination = req.url ?? req.topic;
|
|
55
|
-
if (!destination) {
|
|
56
|
-
throw new Error("Either url or topic must be set");
|
|
57
|
-
}
|
|
58
|
-
const headers = new Headers(req.headers);
|
|
59
|
-
if (req.delay) {
|
|
60
|
-
headers.set("Upstash-Delay", req.delay.toFixed());
|
|
61
|
-
}
|
|
62
|
-
if (req.notBefore) {
|
|
63
|
-
headers.set("Upstash-Not-Before", req.notBefore.toFixed());
|
|
64
|
-
}
|
|
65
|
-
if (req.deadline) {
|
|
66
|
-
headers.set("Upstash-Deadline", req.deadline.toFixed());
|
|
67
|
-
}
|
|
68
|
-
if (req.deduplicationID) {
|
|
69
|
-
headers.set("Upstash-Deduplication-ID", req.deduplicationID);
|
|
70
|
-
}
|
|
71
|
-
if (req.contentBasedDeduplication) {
|
|
72
|
-
headers.set("Upstash-Content-Based-Deduplication", "true");
|
|
73
|
-
}
|
|
74
|
-
if (req.retries) {
|
|
75
|
-
headers.set("Upstash-Retries", req.retries.toFixed());
|
|
76
|
-
}
|
|
77
|
-
if (req.cron) {
|
|
78
|
-
headers.set("Upstash-Cron", req.cron);
|
|
79
|
-
}
|
|
80
|
-
const res = await this.http.request({
|
|
81
|
-
path: ["v1", "publish", destination],
|
|
82
|
-
body: req.body,
|
|
83
|
-
headers,
|
|
84
|
-
method: "POST",
|
|
85
|
-
});
|
|
86
|
-
return res;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* publishJSON is a utility wrapper around `publish` that automatically serializes the body
|
|
90
|
-
* and sets the `Content-Type` header to `application/json`.
|
|
91
|
-
*/
|
|
92
|
-
async publishJSON(req) {
|
|
93
|
-
const headers = new Headers(req.headers);
|
|
94
|
-
headers.set("Content-Type", "application/json");
|
|
95
|
-
const res = await this.publish({
|
|
96
|
-
...req,
|
|
97
|
-
headers,
|
|
98
|
-
body: JSON.stringify(req.body),
|
|
99
|
-
});
|
|
100
|
-
return res;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Retrieve your logs.
|
|
104
|
-
*
|
|
105
|
-
* The logs endpoint is paginated and returns only 100 logs at a time.
|
|
106
|
-
* If you want to receive more logs, you can use the cursor to paginate.
|
|
107
|
-
*
|
|
108
|
-
* The cursor is a unix timestamp with millisecond precision
|
|
109
|
-
*
|
|
110
|
-
* @example
|
|
111
|
-
* ```ts
|
|
112
|
-
* let cursor = Date.now()
|
|
113
|
-
* const logs: Log[] = []
|
|
114
|
-
* while (cursor > 0) {
|
|
115
|
-
* const res = await qstash.logs({ cursor })
|
|
116
|
-
* logs.push(...res.logs)
|
|
117
|
-
* cursor = res.cursor ?? 0
|
|
118
|
-
* }
|
|
119
|
-
* ```
|
|
120
|
-
*/
|
|
121
|
-
async logs(req) {
|
|
122
|
-
const query = {};
|
|
123
|
-
if (req?.cursor && req.cursor > 0) {
|
|
124
|
-
query["cursor"] = req.cursor;
|
|
125
|
-
}
|
|
126
|
-
const res = await this.http.request({
|
|
127
|
-
path: ["v1", "logs"],
|
|
128
|
-
method: "GET",
|
|
129
|
-
query,
|
|
130
|
-
});
|
|
131
|
-
return res;
|
|
132
|
-
}
|
|
133
|
-
}
|
package/esm/endpoints.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
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
|
-
}
|