@upstash/qstash 2.1.2-canary.0 → 2.1.2
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/chunk-EQTYEU4U.js +112 -0
- package/dist/chunk-EQTYEU4U.js.map +1 -0
- package/dist/chunk-G4FL5XMG.mjs +112 -0
- package/dist/chunk-G4FL5XMG.mjs.map +1 -0
- package/dist/index.js +25 -114
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7 -96
- package/dist/index.mjs.map +1 -1
- package/dist/nextjs.d.mts +23 -0
- package/dist/nextjs.d.ts +23 -0
- package/dist/nextjs.js +133 -0
- package/dist/nextjs.js.map +1 -0
- package/dist/nextjs.mjs +133 -0
- package/dist/nextjs.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __knownSymbol = (name, symbol) => {
|
|
8
|
+
if (symbol = Symbol[name])
|
|
9
|
+
return symbol;
|
|
10
|
+
throw Error("Symbol." + name + " is not defined");
|
|
11
|
+
};
|
|
12
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
|
+
var __spreadValues = (a, b) => {
|
|
14
|
+
for (var prop in b || (b = {}))
|
|
15
|
+
if (__hasOwnProp.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
if (__getOwnPropSymbols)
|
|
18
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
19
|
+
if (__propIsEnum.call(b, prop))
|
|
20
|
+
__defNormalProp(a, prop, b[prop]);
|
|
21
|
+
}
|
|
22
|
+
return a;
|
|
23
|
+
};
|
|
24
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
25
|
+
var __async = (__this, __arguments, generator) => {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
var fulfilled = (value) => {
|
|
28
|
+
try {
|
|
29
|
+
step(generator.next(value));
|
|
30
|
+
} catch (e) {
|
|
31
|
+
reject(e);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var rejected = (value) => {
|
|
35
|
+
try {
|
|
36
|
+
step(generator.throw(value));
|
|
37
|
+
} catch (e) {
|
|
38
|
+
reject(e);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
42
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
|
|
46
|
+
|
|
47
|
+
// src/receiver.ts
|
|
48
|
+
var _jose = require('jose'); var jose = _interopRequireWildcard(_jose);
|
|
49
|
+
var _cryptojs = require('crypto-js'); var crypto = _interopRequireWildcard(_cryptojs);
|
|
50
|
+
var SignatureError = class extends Error {
|
|
51
|
+
constructor(message) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.name = "SignatureError";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var Receiver = class {
|
|
57
|
+
constructor(config) {
|
|
58
|
+
this.currentSigningKey = config.currentSigningKey;
|
|
59
|
+
this.nextSigningKey = config.nextSigningKey;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Verify the signature of a request.
|
|
63
|
+
*
|
|
64
|
+
* Tries to verify the signature with the current signing key.
|
|
65
|
+
* If that fails, maybe because you have rotated the keys recently, it will
|
|
66
|
+
* try to verify the signature with the next signing key.
|
|
67
|
+
*
|
|
68
|
+
* If that fails, the signature is invalid and a `SignatureError` is thrown.
|
|
69
|
+
*/
|
|
70
|
+
verify(req) {
|
|
71
|
+
return __async(this, null, function* () {
|
|
72
|
+
const isValid = yield this.verifyWithKey(this.currentSigningKey, req);
|
|
73
|
+
if (isValid) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return this.verifyWithKey(this.nextSigningKey, req);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Verify signature with a specific signing key
|
|
81
|
+
*/
|
|
82
|
+
verifyWithKey(key, req) {
|
|
83
|
+
return __async(this, null, function* () {
|
|
84
|
+
const jwt = yield jose.jwtVerify(req.signature, new TextEncoder().encode(key), {
|
|
85
|
+
issuer: "Upstash",
|
|
86
|
+
clockTolerance: req.clockTolerance
|
|
87
|
+
}).catch((e) => {
|
|
88
|
+
throw new SignatureError(e.message);
|
|
89
|
+
});
|
|
90
|
+
const p = jwt.payload;
|
|
91
|
+
if (typeof req.url !== "undefined" && p.sub !== req.url) {
|
|
92
|
+
throw new SignatureError(`invalid subject: ${p.sub}, want: ${req.url}`);
|
|
93
|
+
}
|
|
94
|
+
const bodyHash = crypto.SHA256(req.body).toString(crypto.enc.Base64url);
|
|
95
|
+
const padding = new RegExp(/=+$/);
|
|
96
|
+
if (p.body.replace(padding, "") !== bodyHash.replace(padding, "")) {
|
|
97
|
+
throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`);
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
exports.__spreadValues = __spreadValues; exports.__spreadProps = __spreadProps; exports.__async = __async; exports.__forAwait = __forAwait; exports.SignatureError = SignatureError; exports.Receiver = Receiver;
|
|
112
|
+
//# sourceMappingURL=chunk-EQTYEU4U.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/receiver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,YAAY,UAAU;AACtB,YAAY,YAAY;AA0CjB,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAIO,IAAM,WAAN,MAAe;AAAA,EAIpB,YAAY,QAAwB;AAClC,SAAK,oBAAoB,OAAO;AAChC,SAAK,iBAAiB,OAAO;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWa,OAAO,KAAsC;AAAA;AACxD,YAAM,UAAU,MAAM,KAAK,cAAc,KAAK,mBAAmB,GAAG;AACpE,UAAI,SAAS;AACX,eAAO;AAAA,MACT;AACA,aAAO,KAAK,cAAc,KAAK,gBAAgB,GAAG;AAAA,IACpD;AAAA;AAAA;AAAA;AAAA;AAAA,EAKc,cAAc,KAAa,KAAsC;AAAA;AAC7E,YAAM,MAAM,MACT,eAAU,IAAI,WAAW,IAAI,YAAY,EAAE,OAAO,GAAG,GAAG;AAAA,QACvD,QAAQ;AAAA,QACR,gBAAgB,IAAI;AAAA,MACtB,CAAC,EACA,MAAM,CAAC,MAAM;AACZ,cAAM,IAAI,eAAgB,EAAY,OAAO;AAAA,MAC/C,CAAC;AAEH,YAAM,IAAI,IAAI;AAUd,UAAI,OAAO,IAAI,QAAQ,eAAe,EAAE,QAAQ,IAAI,KAAK;AACvD,cAAM,IAAI,eAAe,oBAAoB,EAAE,GAAG,WAAW,IAAI,GAAG,EAAE;AAAA,MACxE;AAEA,YAAM,WAAkB,cAAO,IAAI,IAAc,EAAE,SAAgB,WAAI,SAAS;AAEhF,YAAM,UAAU,IAAI,OAAO,KAAK;AAEhC,UAAI,EAAE,KAAK,QAAQ,SAAS,EAAE,MAAM,SAAS,QAAQ,SAAS,EAAE,GAAG;AACjE,cAAM,IAAI,eAAe,mCAAmC,EAAE,IAAI,UAAU,QAAQ,EAAE;AAAA,MACxF;AAEA,aAAO;AAAA,IACT;AAAA;AACF","sourcesContent":["import * as jose from \"jose\";\nimport * as crypto from \"crypto-js\";\n\n/**\n * Necessary to verify the signature of a request.\n */\nexport type ReceiverConfig = {\n /**\n * The current signing key. Get it from `https://console.upstash.com/qstash\n */\n currentSigningKey: string;\n /**\n * The next signing key. Get it from `https://console.upstash.com/qstash\n */\n nextSigningKey: string;\n};\n\nexport type VerifyRequest = {\n /**\n * The signature from the `upstash-signature` header.\n */\n signature: string;\n\n /**\n * The raw request body.\n */\n body: string;\n\n /**\n * URL of the endpoint where the request was sent to.\n *\n * Omit empty to disable checking the url.\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 class SignatureError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SignatureError\";\n }\n}\n/**\n * Receiver offers a simlpe way to verify the signature of a request.\n */\nexport class Receiver {\n private readonly currentSigningKey: string;\n private readonly nextSigningKey: string;\n\n constructor(config: ReceiverConfig) {\n this.currentSigningKey = config.currentSigningKey;\n this.nextSigningKey = config.nextSigningKey;\n }\n\n /**\n * Verify the signature of a request.\n *\n * Tries to verify the signature with the current signing key.\n * If that fails, maybe because you have rotated the keys recently, it will\n * try to verify the signature with the next signing key.\n *\n * If that fails, the signature is invalid and a `SignatureError` is thrown.\n */\n public async verify(req: VerifyRequest): Promise<boolean> {\n const isValid = await this.verifyWithKey(this.currentSigningKey, req);\n if (isValid) {\n return true;\n }\n return this.verifyWithKey(this.nextSigningKey, req);\n }\n\n /**\n * Verify signature with a specific signing key\n */\n private async verifyWithKey(key: string, req: VerifyRequest): Promise<boolean> {\n const jwt = await jose\n .jwtVerify(req.signature, new TextEncoder().encode(key), {\n issuer: \"Upstash\",\n clockTolerance: req.clockTolerance,\n })\n .catch((e) => {\n throw new SignatureError((e as Error).message);\n });\n\n const p = jwt.payload as {\n iss: string;\n sub: string;\n exp: number;\n nbf: number;\n iat: number;\n jti: string;\n body: string;\n };\n\n if (typeof req.url !== \"undefined\" && p.sub !== req.url) {\n throw new SignatureError(`invalid subject: ${p.sub}, want: ${req.url}`);\n }\n\n const bodyHash = crypto.SHA256(req.body as string).toString(crypto.enc.Base64url);\n\n const padding = new RegExp(/=+$/);\n\n if (p.body.replace(padding, \"\") !== bodyHash.replace(padding, \"\")) {\n throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`);\n }\n\n return true;\n }\n}\n"]}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __knownSymbol = (name, symbol) => {
|
|
8
|
+
if (symbol = Symbol[name])
|
|
9
|
+
return symbol;
|
|
10
|
+
throw Error("Symbol." + name + " is not defined");
|
|
11
|
+
};
|
|
12
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
|
+
var __spreadValues = (a, b) => {
|
|
14
|
+
for (var prop in b || (b = {}))
|
|
15
|
+
if (__hasOwnProp.call(b, prop))
|
|
16
|
+
__defNormalProp(a, prop, b[prop]);
|
|
17
|
+
if (__getOwnPropSymbols)
|
|
18
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
19
|
+
if (__propIsEnum.call(b, prop))
|
|
20
|
+
__defNormalProp(a, prop, b[prop]);
|
|
21
|
+
}
|
|
22
|
+
return a;
|
|
23
|
+
};
|
|
24
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
25
|
+
var __async = (__this, __arguments, generator) => {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
var fulfilled = (value) => {
|
|
28
|
+
try {
|
|
29
|
+
step(generator.next(value));
|
|
30
|
+
} catch (e) {
|
|
31
|
+
reject(e);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var rejected = (value) => {
|
|
35
|
+
try {
|
|
36
|
+
step(generator.throw(value));
|
|
37
|
+
} catch (e) {
|
|
38
|
+
reject(e);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
42
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
|
|
46
|
+
|
|
47
|
+
// src/receiver.ts
|
|
48
|
+
import * as jose from "jose";
|
|
49
|
+
import * as crypto from "crypto-js";
|
|
50
|
+
var SignatureError = class extends Error {
|
|
51
|
+
constructor(message) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.name = "SignatureError";
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var Receiver = class {
|
|
57
|
+
constructor(config) {
|
|
58
|
+
this.currentSigningKey = config.currentSigningKey;
|
|
59
|
+
this.nextSigningKey = config.nextSigningKey;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Verify the signature of a request.
|
|
63
|
+
*
|
|
64
|
+
* Tries to verify the signature with the current signing key.
|
|
65
|
+
* If that fails, maybe because you have rotated the keys recently, it will
|
|
66
|
+
* try to verify the signature with the next signing key.
|
|
67
|
+
*
|
|
68
|
+
* If that fails, the signature is invalid and a `SignatureError` is thrown.
|
|
69
|
+
*/
|
|
70
|
+
verify(req) {
|
|
71
|
+
return __async(this, null, function* () {
|
|
72
|
+
const isValid = yield this.verifyWithKey(this.currentSigningKey, req);
|
|
73
|
+
if (isValid) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
return this.verifyWithKey(this.nextSigningKey, req);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Verify signature with a specific signing key
|
|
81
|
+
*/
|
|
82
|
+
verifyWithKey(key, req) {
|
|
83
|
+
return __async(this, null, function* () {
|
|
84
|
+
const jwt = yield jose.jwtVerify(req.signature, new TextEncoder().encode(key), {
|
|
85
|
+
issuer: "Upstash",
|
|
86
|
+
clockTolerance: req.clockTolerance
|
|
87
|
+
}).catch((e) => {
|
|
88
|
+
throw new SignatureError(e.message);
|
|
89
|
+
});
|
|
90
|
+
const p = jwt.payload;
|
|
91
|
+
if (typeof req.url !== "undefined" && p.sub !== req.url) {
|
|
92
|
+
throw new SignatureError(`invalid subject: ${p.sub}, want: ${req.url}`);
|
|
93
|
+
}
|
|
94
|
+
const bodyHash = crypto.SHA256(req.body).toString(crypto.enc.Base64url);
|
|
95
|
+
const padding = new RegExp(/=+$/);
|
|
96
|
+
if (p.body.replace(padding, "") !== bodyHash.replace(padding, "")) {
|
|
97
|
+
throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`);
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export {
|
|
105
|
+
__spreadValues,
|
|
106
|
+
__spreadProps,
|
|
107
|
+
__async,
|
|
108
|
+
__forAwait,
|
|
109
|
+
SignatureError,
|
|
110
|
+
Receiver
|
|
111
|
+
};
|
|
112
|
+
//# sourceMappingURL=chunk-G4FL5XMG.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/receiver.ts"],"sourcesContent":["import * as jose from \"jose\";\nimport * as crypto from \"crypto-js\";\n\n/**\n * Necessary to verify the signature of a request.\n */\nexport type ReceiverConfig = {\n /**\n * The current signing key. Get it from `https://console.upstash.com/qstash\n */\n currentSigningKey: string;\n /**\n * The next signing key. Get it from `https://console.upstash.com/qstash\n */\n nextSigningKey: string;\n};\n\nexport type VerifyRequest = {\n /**\n * The signature from the `upstash-signature` header.\n */\n signature: string;\n\n /**\n * The raw request body.\n */\n body: string;\n\n /**\n * URL of the endpoint where the request was sent to.\n *\n * Omit empty to disable checking the url.\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 class SignatureError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SignatureError\";\n }\n}\n/**\n * Receiver offers a simlpe way to verify the signature of a request.\n */\nexport class Receiver {\n private readonly currentSigningKey: string;\n private readonly nextSigningKey: string;\n\n constructor(config: ReceiverConfig) {\n this.currentSigningKey = config.currentSigningKey;\n this.nextSigningKey = config.nextSigningKey;\n }\n\n /**\n * Verify the signature of a request.\n *\n * Tries to verify the signature with the current signing key.\n * If that fails, maybe because you have rotated the keys recently, it will\n * try to verify the signature with the next signing key.\n *\n * If that fails, the signature is invalid and a `SignatureError` is thrown.\n */\n public async verify(req: VerifyRequest): Promise<boolean> {\n const isValid = await this.verifyWithKey(this.currentSigningKey, req);\n if (isValid) {\n return true;\n }\n return this.verifyWithKey(this.nextSigningKey, req);\n }\n\n /**\n * Verify signature with a specific signing key\n */\n private async verifyWithKey(key: string, req: VerifyRequest): Promise<boolean> {\n const jwt = await jose\n .jwtVerify(req.signature, new TextEncoder().encode(key), {\n issuer: \"Upstash\",\n clockTolerance: req.clockTolerance,\n })\n .catch((e) => {\n throw new SignatureError((e as Error).message);\n });\n\n const p = jwt.payload as {\n iss: string;\n sub: string;\n exp: number;\n nbf: number;\n iat: number;\n jti: string;\n body: string;\n };\n\n if (typeof req.url !== \"undefined\" && p.sub !== req.url) {\n throw new SignatureError(`invalid subject: ${p.sub}, want: ${req.url}`);\n }\n\n const bodyHash = crypto.SHA256(req.body as string).toString(crypto.enc.Base64url);\n\n const padding = new RegExp(/=+$/);\n\n if (p.body.replace(padding, \"\") !== bodyHash.replace(padding, \"\")) {\n throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`);\n }\n\n return true;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,YAAY,UAAU;AACtB,YAAY,YAAY;AA0CjB,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAIO,IAAM,WAAN,MAAe;AAAA,EAIpB,YAAY,QAAwB;AAClC,SAAK,oBAAoB,OAAO;AAChC,SAAK,iBAAiB,OAAO;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWa,OAAO,KAAsC;AAAA;AACxD,YAAM,UAAU,MAAM,KAAK,cAAc,KAAK,mBAAmB,GAAG;AACpE,UAAI,SAAS;AACX,eAAO;AAAA,MACT;AACA,aAAO,KAAK,cAAc,KAAK,gBAAgB,GAAG;AAAA,IACpD;AAAA;AAAA;AAAA;AAAA;AAAA,EAKc,cAAc,KAAa,KAAsC;AAAA;AAC7E,YAAM,MAAM,MACT,eAAU,IAAI,WAAW,IAAI,YAAY,EAAE,OAAO,GAAG,GAAG;AAAA,QACvD,QAAQ;AAAA,QACR,gBAAgB,IAAI;AAAA,MACtB,CAAC,EACA,MAAM,CAAC,MAAM;AACZ,cAAM,IAAI,eAAgB,EAAY,OAAO;AAAA,MAC/C,CAAC;AAEH,YAAM,IAAI,IAAI;AAUd,UAAI,OAAO,IAAI,QAAQ,eAAe,EAAE,QAAQ,IAAI,KAAK;AACvD,cAAM,IAAI,eAAe,oBAAoB,EAAE,GAAG,WAAW,IAAI,GAAG,EAAE;AAAA,MACxE;AAEA,YAAM,WAAkB,cAAO,IAAI,IAAc,EAAE,SAAgB,WAAI,SAAS;AAEhF,YAAM,UAAU,IAAI,OAAO,KAAK;AAEhC,UAAI,EAAE,KAAK,QAAQ,SAAS,EAAE,MAAM,SAAS,QAAQ,SAAS,EAAE,GAAG;AACjE,cAAM,IAAI,eAAe,mCAAmC,EAAE,IAAI,UAAU,QAAQ,EAAE;AAAA,MACxF;AAEA,aAAO;AAAA,IACT;AAAA;AACF;","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -1,99 +1,10 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
-
var __defProps = Object.defineProperties;
|
|
3
|
-
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
-
var __spreadValues = (a, b) => {
|
|
9
|
-
for (var prop in b || (b = {}))
|
|
10
|
-
if (__hasOwnProp.call(b, prop))
|
|
11
|
-
__defNormalProp(a, prop, b[prop]);
|
|
12
|
-
if (__getOwnPropSymbols)
|
|
13
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
-
if (__propIsEnum.call(b, prop))
|
|
15
|
-
__defNormalProp(a, prop, b[prop]);
|
|
16
|
-
}
|
|
17
|
-
return a;
|
|
18
|
-
};
|
|
19
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
-
var __async = (__this, __arguments, generator) => {
|
|
21
|
-
return new Promise((resolve, reject) => {
|
|
22
|
-
var fulfilled = (value) => {
|
|
23
|
-
try {
|
|
24
|
-
step(generator.next(value));
|
|
25
|
-
} catch (e) {
|
|
26
|
-
reject(e);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
var rejected = (value) => {
|
|
30
|
-
try {
|
|
31
|
-
step(generator.throw(value));
|
|
32
|
-
} catch (e) {
|
|
33
|
-
reject(e);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
37
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
38
|
-
});
|
|
39
|
-
};
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
40
2
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
super(message);
|
|
47
|
-
this.name = "SignatureError";
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
var Receiver = class {
|
|
51
|
-
constructor(config) {
|
|
52
|
-
this.currentSigningKey = config.currentSigningKey;
|
|
53
|
-
this.nextSigningKey = config.nextSigningKey;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Verify the signature of a request.
|
|
57
|
-
*
|
|
58
|
-
* Tries to verify the signature with the current signing key.
|
|
59
|
-
* If that fails, maybe because you have rotated the keys recently, it will
|
|
60
|
-
* try to verify the signature with the next signing key.
|
|
61
|
-
*
|
|
62
|
-
* If that fails, the signature is invalid and a `SignatureError` is thrown.
|
|
63
|
-
*/
|
|
64
|
-
verify(req) {
|
|
65
|
-
return __async(this, null, function* () {
|
|
66
|
-
const isValid = yield this.verifyWithKey(this.currentSigningKey, req);
|
|
67
|
-
if (isValid) {
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
return this.verifyWithKey(this.nextSigningKey, req);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Verify signature with a specific signing key
|
|
75
|
-
*/
|
|
76
|
-
verifyWithKey(key, req) {
|
|
77
|
-
return __async(this, null, function* () {
|
|
78
|
-
const jwt = yield jose.jwtVerify(req.signature, new TextEncoder().encode(key), {
|
|
79
|
-
issuer: "Upstash",
|
|
80
|
-
clockTolerance: req.clockTolerance
|
|
81
|
-
}).catch((e) => {
|
|
82
|
-
throw new SignatureError(e.message);
|
|
83
|
-
});
|
|
84
|
-
const p = jwt.payload;
|
|
85
|
-
if (typeof req.url !== "undefined" && p.sub !== req.url) {
|
|
86
|
-
throw new SignatureError(`invalid subject: ${p.sub}, want: ${req.url}`);
|
|
87
|
-
}
|
|
88
|
-
const bodyHash = crypto.SHA256(req.body).toString(crypto.enc.Base64url);
|
|
89
|
-
const padding = new RegExp(/=+$/);
|
|
90
|
-
if (p.body.replace(padding, "") !== bodyHash.replace(padding, "")) {
|
|
91
|
-
throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`);
|
|
92
|
-
}
|
|
93
|
-
return true;
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
};
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
var _chunkEQTYEU4Ujs = require('./chunk-EQTYEU4U.js');
|
|
97
8
|
|
|
98
9
|
// src/client/error.ts
|
|
99
10
|
var QstashError = class extends Error {
|
|
@@ -122,7 +33,7 @@ var HttpClient = class {
|
|
|
122
33
|
}
|
|
123
34
|
}
|
|
124
35
|
request(req) {
|
|
125
|
-
return __async(this, null, function* () {
|
|
36
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
126
37
|
var _a, _b;
|
|
127
38
|
const headers = new Headers(req.headers);
|
|
128
39
|
headers.set("Authorization", this.authorization);
|
|
@@ -171,7 +82,7 @@ var Topics = class {
|
|
|
171
82
|
* Create a new topic with the given name and endpoints
|
|
172
83
|
*/
|
|
173
84
|
addEndpoints(req) {
|
|
174
|
-
return __async(this, null, function* () {
|
|
85
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
175
86
|
return yield this.http.request({
|
|
176
87
|
method: "POST",
|
|
177
88
|
path: ["v2", "topics", req.name],
|
|
@@ -184,7 +95,7 @@ var Topics = class {
|
|
|
184
95
|
* Remove endpoints from a topic.
|
|
185
96
|
*/
|
|
186
97
|
removeEndpoints(req) {
|
|
187
|
-
return __async(this, null, function* () {
|
|
98
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
188
99
|
return yield this.http.request({
|
|
189
100
|
method: "DELETE",
|
|
190
101
|
path: ["v2", "topics", req.name],
|
|
@@ -197,7 +108,7 @@ var Topics = class {
|
|
|
197
108
|
* Get a list of all topics.
|
|
198
109
|
*/
|
|
199
110
|
list() {
|
|
200
|
-
return __async(this, null, function* () {
|
|
111
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
201
112
|
return yield this.http.request({
|
|
202
113
|
method: "GET",
|
|
203
114
|
path: ["v2", "topics"]
|
|
@@ -208,7 +119,7 @@ var Topics = class {
|
|
|
208
119
|
* Get a single topic
|
|
209
120
|
*/
|
|
210
121
|
get(name) {
|
|
211
|
-
return __async(this, null, function* () {
|
|
122
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
212
123
|
return yield this.http.request({
|
|
213
124
|
method: "GET",
|
|
214
125
|
path: ["v2", "topics", name]
|
|
@@ -219,7 +130,7 @@ var Topics = class {
|
|
|
219
130
|
* Delete a topic
|
|
220
131
|
*/
|
|
221
132
|
delete(name) {
|
|
222
|
-
return __async(this, null, function* () {
|
|
133
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
223
134
|
return yield this.http.request({
|
|
224
135
|
method: "DELETE",
|
|
225
136
|
path: ["v2", "topics", name]
|
|
@@ -237,7 +148,7 @@ var Messages = class {
|
|
|
237
148
|
* Get a message
|
|
238
149
|
*/
|
|
239
150
|
get(messageId) {
|
|
240
|
-
return __async(this, null, function* () {
|
|
151
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
241
152
|
return yield this.http.request({
|
|
242
153
|
method: "GET",
|
|
243
154
|
path: ["v2", "messages", messageId]
|
|
@@ -248,7 +159,7 @@ var Messages = class {
|
|
|
248
159
|
* Cancel a message
|
|
249
160
|
*/
|
|
250
161
|
delete(messageId) {
|
|
251
|
-
return __async(this, null, function* () {
|
|
162
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
252
163
|
return yield this.http.request({
|
|
253
164
|
method: "DELETE",
|
|
254
165
|
path: ["v2", "messages", messageId]
|
|
@@ -266,7 +177,7 @@ var Schedules = class {
|
|
|
266
177
|
* Create a schedule
|
|
267
178
|
*/
|
|
268
179
|
create(req) {
|
|
269
|
-
return __async(this, null, function* () {
|
|
180
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
270
181
|
return yield this.http.request({
|
|
271
182
|
method: "POST",
|
|
272
183
|
headers: { "Content-Type": "application/json" },
|
|
@@ -279,7 +190,7 @@ var Schedules = class {
|
|
|
279
190
|
* Get a schedule
|
|
280
191
|
*/
|
|
281
192
|
get(scheduleId) {
|
|
282
|
-
return __async(this, null, function* () {
|
|
193
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
283
194
|
return yield this.http.request({
|
|
284
195
|
method: "GET",
|
|
285
196
|
path: ["v2", "schedules", scheduleId]
|
|
@@ -290,7 +201,7 @@ var Schedules = class {
|
|
|
290
201
|
* List your schedules
|
|
291
202
|
*/
|
|
292
203
|
list() {
|
|
293
|
-
return __async(this, null, function* () {
|
|
204
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
294
205
|
return yield this.http.request({
|
|
295
206
|
method: "GET",
|
|
296
207
|
path: ["v2", "schedules"]
|
|
@@ -301,7 +212,7 @@ var Schedules = class {
|
|
|
301
212
|
* Delete a schedule
|
|
302
213
|
*/
|
|
303
214
|
delete(scheduleId) {
|
|
304
|
-
return __async(this, null, function* () {
|
|
215
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
305
216
|
return yield this.http.request({
|
|
306
217
|
method: "DELETE",
|
|
307
218
|
path: ["v2", "schedules", scheduleId]
|
|
@@ -319,7 +230,7 @@ var DLQ = class {
|
|
|
319
230
|
* List messages in the dlq
|
|
320
231
|
*/
|
|
321
232
|
listMessages() {
|
|
322
|
-
return __async(this, null, function* () {
|
|
233
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
323
234
|
return yield this.http.request({
|
|
324
235
|
method: "GET",
|
|
325
236
|
path: ["v2", "dlq", "messages"]
|
|
@@ -330,7 +241,7 @@ var DLQ = class {
|
|
|
330
241
|
* Remove a message from the dlq using it's `dlqId`
|
|
331
242
|
*/
|
|
332
243
|
delete(dlqMessageId) {
|
|
333
|
-
return __async(this, null, function* () {
|
|
244
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
334
245
|
return yield this.http.request({
|
|
335
246
|
method: "DELETE",
|
|
336
247
|
path: ["v2", "dlq", "messages", dlqMessageId]
|
|
@@ -381,7 +292,7 @@ var Client = class {
|
|
|
381
292
|
return new Schedules(this.http);
|
|
382
293
|
}
|
|
383
294
|
publish(req) {
|
|
384
|
-
return __async(this, null, function* () {
|
|
295
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
385
296
|
var _a, _b;
|
|
386
297
|
const headers = new Headers(req.headers);
|
|
387
298
|
headers.set("Upstash-Method", (_a = req.method) != null ? _a : "POST");
|
|
@@ -417,10 +328,10 @@ var Client = class {
|
|
|
417
328
|
* and sets the `Content-Type` header to `application/json`.
|
|
418
329
|
*/
|
|
419
330
|
publishJSON(req) {
|
|
420
|
-
return __async(this, null, function* () {
|
|
331
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
421
332
|
const headers = new Headers(req.headers);
|
|
422
333
|
headers.set("Content-Type", "application/json");
|
|
423
|
-
const res = yield this.publish(__spreadProps(__spreadValues({}, req), {
|
|
334
|
+
const res = yield this.publish(_chunkEQTYEU4Ujs.__spreadProps.call(void 0, _chunkEQTYEU4Ujs.__spreadValues.call(void 0, {}, req), {
|
|
424
335
|
headers,
|
|
425
336
|
body: JSON.stringify(req.body)
|
|
426
337
|
}));
|
|
@@ -447,7 +358,7 @@ var Client = class {
|
|
|
447
358
|
* ```
|
|
448
359
|
*/
|
|
449
360
|
events(req) {
|
|
450
|
-
return __async(this, null, function* () {
|
|
361
|
+
return _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
451
362
|
const query = {};
|
|
452
363
|
if ((req == null ? void 0 : req.cursor) && req.cursor > 0) {
|
|
453
364
|
query.cursor = req.cursor;
|
|
@@ -469,5 +380,5 @@ var Client = class {
|
|
|
469
380
|
|
|
470
381
|
|
|
471
382
|
|
|
472
|
-
exports.Client = Client; exports.Messages = Messages; exports.QstashError = QstashError; exports.Receiver = Receiver; exports.Schedules = Schedules; exports.SignatureError = SignatureError; exports.Topics = Topics;
|
|
383
|
+
exports.Client = Client; exports.Messages = Messages; exports.QstashError = QstashError; exports.Receiver = _chunkEQTYEU4Ujs.Receiver; exports.Schedules = Schedules; exports.SignatureError = _chunkEQTYEU4Ujs.SignatureError; exports.Topics = Topics;
|
|
473
384
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/receiver.ts","../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":["import * as jose from \"jose\";\nimport * as crypto from \"crypto-js\";\n\n/**\n * Necessary to verify the signature of a request.\n */\nexport type ReceiverConfig = {\n /**\n * The current signing key. Get it from `https://console.upstash.com/qstash\n */\n currentSigningKey: string;\n /**\n * The next signing key. Get it from `https://console.upstash.com/qstash\n */\n nextSigningKey: string;\n};\n\nexport type VerifyRequest = {\n /**\n * The signature from the `upstash-signature` header.\n */\n signature: string;\n\n /**\n * The raw request body.\n */\n body: string;\n\n /**\n * URL of the endpoint where the request was sent to.\n *\n * Omit empty to disable checking the url.\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 class SignatureError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SignatureError\";\n }\n}\n/**\n * Receiver offers a simlpe way to verify the signature of a request.\n */\nexport class Receiver {\n private readonly currentSigningKey: string;\n private readonly nextSigningKey: string;\n\n constructor(config: ReceiverConfig) {\n this.currentSigningKey = config.currentSigningKey;\n this.nextSigningKey = config.nextSigningKey;\n }\n\n /**\n * Verify the signature of a request.\n *\n * Tries to verify the signature with the current signing key.\n * If that fails, maybe because you have rotated the keys recently, it will\n * try to verify the signature with the next signing key.\n *\n * If that fails, the signature is invalid and a `SignatureError` is thrown.\n */\n public async verify(req: VerifyRequest): Promise<boolean> {\n const isValid = await this.verifyWithKey(this.currentSigningKey, req);\n if (isValid) {\n return true;\n }\n return this.verifyWithKey(this.nextSigningKey, req);\n }\n\n /**\n * Verify signature with a specific signing key\n */\n private async verifyWithKey(key: string, req: VerifyRequest): Promise<boolean> {\n const jwt = await jose\n .jwtVerify(req.signature, new TextEncoder().encode(key), {\n issuer: \"Upstash\",\n clockTolerance: req.clockTolerance,\n })\n .catch((e) => {\n throw new SignatureError((e as Error).message);\n });\n\n const p = jwt.payload as {\n iss: string;\n sub: string;\n exp: number;\n nbf: number;\n iat: number;\n jti: string;\n body: string;\n };\n\n if (typeof req.url !== \"undefined\" && p.sub !== req.url) {\n throw new SignatureError(`invalid subject: ${p.sub}, want: ${req.url}`);\n }\n\n const bodyHash = crypto.SHA256(req.body as string).toString(crypto.enc.Base64url);\n\n const padding = new RegExp(/=+$/);\n\n if (p.body.replace(padding, \"\") !== bodyHash.replace(padding, \"\")) {\n throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`);\n }\n\n return true;\n }\n}\n","/**\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};\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 throw new QstashError((await res.text()) ?? res.statusText);\n }\n return (await res.json()) as UpstashResponse<TResult>;\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<Topic> {\n return await this.http.request<Topic>({\n method: \"POST\",\n path: [\"v2\", \"topics\", req.name],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n });\n }\n\n /**\n * Remove endpoints from a topic.\n */\n public async removeEndpoints(req: RemoveEndpointsRequest): Promise<Topic> {\n return await this.http.request<Topic>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", req.name],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\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 });\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 });\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 * Optionally specify a cron expression to repeatedly send this message to the destination.\n *\n * @default undefined\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 return await this.http.request({\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n path: [\"v2\", \"schedules\"],\n body: JSON.stringify(req),\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 });\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(): Promise<DlqMessage[]> {\n return await this.http.request<DlqMessage[]>({\n method: \"GET\",\n path: [\"v2\", \"dlq\", \"messages\"],\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\", \"messages\", dlqMessageId],\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","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};\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 throw new QstashError((await res.text()) ?? res.statusText);\n }\n return (await res.json()) as UpstashResponse<TResult>;\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<Topic> {\n return await this.http.request<Topic>({\n method: \"POST\",\n path: [\"v2\", \"topics\", req.name],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n });\n }\n\n /**\n * Remove endpoints from a topic.\n */\n public async removeEndpoints(req: RemoveEndpointsRequest): Promise<Topic> {\n return await this.http.request<Topic>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", req.name],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\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 });\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 });\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 * Optionally specify a cron expression to repeatedly send this message to the destination.\n *\n * @default undefined\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 return await this.http.request({\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n path: [\"v2\", \"schedules\"],\n body: JSON.stringify(req),\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 });\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(): Promise<DlqMessage[]> {\n return await this.http.request<DlqMessage[]>({\n method: \"GET\",\n path: [\"v2\", \"dlq\", \"messages\"],\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\", \"messages\", dlqMessageId],\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
|
@@ -1,99 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var __spreadValues = (a, b) => {
|
|
9
|
-
for (var prop in b || (b = {}))
|
|
10
|
-
if (__hasOwnProp.call(b, prop))
|
|
11
|
-
__defNormalProp(a, prop, b[prop]);
|
|
12
|
-
if (__getOwnPropSymbols)
|
|
13
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
-
if (__propIsEnum.call(b, prop))
|
|
15
|
-
__defNormalProp(a, prop, b[prop]);
|
|
16
|
-
}
|
|
17
|
-
return a;
|
|
18
|
-
};
|
|
19
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
-
var __async = (__this, __arguments, generator) => {
|
|
21
|
-
return new Promise((resolve, reject) => {
|
|
22
|
-
var fulfilled = (value) => {
|
|
23
|
-
try {
|
|
24
|
-
step(generator.next(value));
|
|
25
|
-
} catch (e) {
|
|
26
|
-
reject(e);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
var rejected = (value) => {
|
|
30
|
-
try {
|
|
31
|
-
step(generator.throw(value));
|
|
32
|
-
} catch (e) {
|
|
33
|
-
reject(e);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
37
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
38
|
-
});
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// src/receiver.ts
|
|
42
|
-
import * as jose from "jose";
|
|
43
|
-
import * as crypto from "crypto-js";
|
|
44
|
-
var SignatureError = class extends Error {
|
|
45
|
-
constructor(message) {
|
|
46
|
-
super(message);
|
|
47
|
-
this.name = "SignatureError";
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
var Receiver = class {
|
|
51
|
-
constructor(config) {
|
|
52
|
-
this.currentSigningKey = config.currentSigningKey;
|
|
53
|
-
this.nextSigningKey = config.nextSigningKey;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Verify the signature of a request.
|
|
57
|
-
*
|
|
58
|
-
* Tries to verify the signature with the current signing key.
|
|
59
|
-
* If that fails, maybe because you have rotated the keys recently, it will
|
|
60
|
-
* try to verify the signature with the next signing key.
|
|
61
|
-
*
|
|
62
|
-
* If that fails, the signature is invalid and a `SignatureError` is thrown.
|
|
63
|
-
*/
|
|
64
|
-
verify(req) {
|
|
65
|
-
return __async(this, null, function* () {
|
|
66
|
-
const isValid = yield this.verifyWithKey(this.currentSigningKey, req);
|
|
67
|
-
if (isValid) {
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
return this.verifyWithKey(this.nextSigningKey, req);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Verify signature with a specific signing key
|
|
75
|
-
*/
|
|
76
|
-
verifyWithKey(key, req) {
|
|
77
|
-
return __async(this, null, function* () {
|
|
78
|
-
const jwt = yield jose.jwtVerify(req.signature, new TextEncoder().encode(key), {
|
|
79
|
-
issuer: "Upstash",
|
|
80
|
-
clockTolerance: req.clockTolerance
|
|
81
|
-
}).catch((e) => {
|
|
82
|
-
throw new SignatureError(e.message);
|
|
83
|
-
});
|
|
84
|
-
const p = jwt.payload;
|
|
85
|
-
if (typeof req.url !== "undefined" && p.sub !== req.url) {
|
|
86
|
-
throw new SignatureError(`invalid subject: ${p.sub}, want: ${req.url}`);
|
|
87
|
-
}
|
|
88
|
-
const bodyHash = crypto.SHA256(req.body).toString(crypto.enc.Base64url);
|
|
89
|
-
const padding = new RegExp(/=+$/);
|
|
90
|
-
if (p.body.replace(padding, "") !== bodyHash.replace(padding, "")) {
|
|
91
|
-
throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`);
|
|
92
|
-
}
|
|
93
|
-
return true;
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
Receiver,
|
|
3
|
+
SignatureError,
|
|
4
|
+
__async,
|
|
5
|
+
__spreadProps,
|
|
6
|
+
__spreadValues
|
|
7
|
+
} from "./chunk-G4FL5XMG.mjs";
|
|
97
8
|
|
|
98
9
|
// src/client/error.ts
|
|
99
10
|
var QstashError = class extends Error {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/receiver.ts","../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":["import * as jose from \"jose\";\nimport * as crypto from \"crypto-js\";\n\n/**\n * Necessary to verify the signature of a request.\n */\nexport type ReceiverConfig = {\n /**\n * The current signing key. Get it from `https://console.upstash.com/qstash\n */\n currentSigningKey: string;\n /**\n * The next signing key. Get it from `https://console.upstash.com/qstash\n */\n nextSigningKey: string;\n};\n\nexport type VerifyRequest = {\n /**\n * The signature from the `upstash-signature` header.\n */\n signature: string;\n\n /**\n * The raw request body.\n */\n body: string;\n\n /**\n * URL of the endpoint where the request was sent to.\n *\n * Omit empty to disable checking the url.\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 class SignatureError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"SignatureError\";\n }\n}\n/**\n * Receiver offers a simlpe way to verify the signature of a request.\n */\nexport class Receiver {\n private readonly currentSigningKey: string;\n private readonly nextSigningKey: string;\n\n constructor(config: ReceiverConfig) {\n this.currentSigningKey = config.currentSigningKey;\n this.nextSigningKey = config.nextSigningKey;\n }\n\n /**\n * Verify the signature of a request.\n *\n * Tries to verify the signature with the current signing key.\n * If that fails, maybe because you have rotated the keys recently, it will\n * try to verify the signature with the next signing key.\n *\n * If that fails, the signature is invalid and a `SignatureError` is thrown.\n */\n public async verify(req: VerifyRequest): Promise<boolean> {\n const isValid = await this.verifyWithKey(this.currentSigningKey, req);\n if (isValid) {\n return true;\n }\n return this.verifyWithKey(this.nextSigningKey, req);\n }\n\n /**\n * Verify signature with a specific signing key\n */\n private async verifyWithKey(key: string, req: VerifyRequest): Promise<boolean> {\n const jwt = await jose\n .jwtVerify(req.signature, new TextEncoder().encode(key), {\n issuer: \"Upstash\",\n clockTolerance: req.clockTolerance,\n })\n .catch((e) => {\n throw new SignatureError((e as Error).message);\n });\n\n const p = jwt.payload as {\n iss: string;\n sub: string;\n exp: number;\n nbf: number;\n iat: number;\n jti: string;\n body: string;\n };\n\n if (typeof req.url !== \"undefined\" && p.sub !== req.url) {\n throw new SignatureError(`invalid subject: ${p.sub}, want: ${req.url}`);\n }\n\n const bodyHash = crypto.SHA256(req.body as string).toString(crypto.enc.Base64url);\n\n const padding = new RegExp(/=+$/);\n\n if (p.body.replace(padding, \"\") !== bodyHash.replace(padding, \"\")) {\n throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`);\n }\n\n return true;\n }\n}\n","/**\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};\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 throw new QstashError((await res.text()) ?? res.statusText);\n }\n return (await res.json()) as UpstashResponse<TResult>;\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<Topic> {\n return await this.http.request<Topic>({\n method: \"POST\",\n path: [\"v2\", \"topics\", req.name],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n });\n }\n\n /**\n * Remove endpoints from a topic.\n */\n public async removeEndpoints(req: RemoveEndpointsRequest): Promise<Topic> {\n return await this.http.request<Topic>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", req.name],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\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 });\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 });\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 * Optionally specify a cron expression to repeatedly send this message to the destination.\n *\n * @default undefined\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 return await this.http.request({\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n path: [\"v2\", \"schedules\"],\n body: JSON.stringify(req),\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 });\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(): Promise<DlqMessage[]> {\n return await this.http.request<DlqMessage[]>({\n method: \"GET\",\n path: [\"v2\", \"dlq\", \"messages\"],\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\", \"messages\", dlqMessageId],\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","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};\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 throw new QstashError((await res.text()) ?? res.statusText);\n }\n return (await res.json()) as UpstashResponse<TResult>;\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<Topic> {\n return await this.http.request<Topic>({\n method: \"POST\",\n path: [\"v2\", \"topics\", req.name],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\n });\n }\n\n /**\n * Remove endpoints from a topic.\n */\n public async removeEndpoints(req: RemoveEndpointsRequest): Promise<Topic> {\n return await this.http.request<Topic>({\n method: \"DELETE\",\n path: [\"v2\", \"topics\", req.name],\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ endpoints: req.endpoints }),\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 });\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 });\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 * Optionally specify a cron expression to repeatedly send this message to the destination.\n *\n * @default undefined\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 return await this.http.request({\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n path: [\"v2\", \"schedules\"],\n body: JSON.stringify(req),\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 });\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(): Promise<DlqMessage[]> {\n return await this.http.request<DlqMessage[]>({\n method: \"GET\",\n path: [\"v2\", \"dlq\", \"messages\"],\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\", \"messages\", dlqMessageId],\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":[]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NextApiHandler } from 'next';
|
|
2
|
+
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
|
|
3
|
+
|
|
4
|
+
type VerifySignaturConfig = {
|
|
5
|
+
currentSigningKey?: string;
|
|
6
|
+
nextSigningKey?: string;
|
|
7
|
+
/**
|
|
8
|
+
* The url of this api route, including the protocol.
|
|
9
|
+
*
|
|
10
|
+
* If you omit this, the url will be automatically determined by checking the `VERCEL_URL` env variable and assuming `https`
|
|
11
|
+
*/
|
|
12
|
+
url?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Number of seconds to tolerate when checking `nbf` and `exp` claims, to deal with small clock differences among different servers
|
|
15
|
+
*
|
|
16
|
+
* @default 0
|
|
17
|
+
*/
|
|
18
|
+
clockTolerance?: number;
|
|
19
|
+
};
|
|
20
|
+
declare function verifySignature(handler: NextApiHandler, config?: VerifySignaturConfig): NextApiHandler;
|
|
21
|
+
declare function verifySignatureEdge(handler: (req: NextRequest, nfe: NextFetchEvent) => NextResponse | Promise<NextResponse>, config?: VerifySignaturConfig): (req: NextRequest, nfe: NextFetchEvent) => Promise<NextResponse<unknown>>;
|
|
22
|
+
|
|
23
|
+
export { VerifySignaturConfig, verifySignature, verifySignatureEdge };
|
package/dist/nextjs.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NextApiHandler } from 'next';
|
|
2
|
+
import { NextRequest, NextFetchEvent, NextResponse } from 'next/server';
|
|
3
|
+
|
|
4
|
+
type VerifySignaturConfig = {
|
|
5
|
+
currentSigningKey?: string;
|
|
6
|
+
nextSigningKey?: string;
|
|
7
|
+
/**
|
|
8
|
+
* The url of this api route, including the protocol.
|
|
9
|
+
*
|
|
10
|
+
* If you omit this, the url will be automatically determined by checking the `VERCEL_URL` env variable and assuming `https`
|
|
11
|
+
*/
|
|
12
|
+
url?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Number of seconds to tolerate when checking `nbf` and `exp` claims, to deal with small clock differences among different servers
|
|
15
|
+
*
|
|
16
|
+
* @default 0
|
|
17
|
+
*/
|
|
18
|
+
clockTolerance?: number;
|
|
19
|
+
};
|
|
20
|
+
declare function verifySignature(handler: NextApiHandler, config?: VerifySignaturConfig): NextApiHandler;
|
|
21
|
+
declare function verifySignatureEdge(handler: (req: NextRequest, nfe: NextFetchEvent) => NextResponse | Promise<NextResponse>, config?: VerifySignaturConfig): (req: NextRequest, nfe: NextFetchEvent) => Promise<NextResponse<unknown>>;
|
|
22
|
+
|
|
23
|
+
export { VerifySignaturConfig, verifySignature, verifySignatureEdge };
|
package/dist/nextjs.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
var _chunkEQTYEU4Ujs = require('./chunk-EQTYEU4U.js');
|
|
6
|
+
|
|
7
|
+
// src/nextjs.ts
|
|
8
|
+
var _server = require('next/server');
|
|
9
|
+
function verifySignature(handler, config) {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
const currentSigningKey = (_a = config == null ? void 0 : config.currentSigningKey) != null ? _a : process.env.QSTASH_CURRENT_SIGNING_KEY;
|
|
12
|
+
if (!currentSigningKey) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
const nextSigningKey = (_b = config == null ? void 0 : config.nextSigningKey) != null ? _b : process.env.QSTASH_NEXT_SIGNING_KEY;
|
|
18
|
+
if (!nextSigningKey) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
const receiver = new (0, _chunkEQTYEU4Ujs.Receiver)({
|
|
24
|
+
currentSigningKey,
|
|
25
|
+
nextSigningKey
|
|
26
|
+
});
|
|
27
|
+
return (req, res) => _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
28
|
+
const signature = req.headers["upstash-signature"];
|
|
29
|
+
if (!signature) {
|
|
30
|
+
res.status(400);
|
|
31
|
+
res.send("`Upstash-Signature` header is missing");
|
|
32
|
+
res.end();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (typeof signature !== "string") {
|
|
36
|
+
throw new Error("`Upstash-Signature` header is not a string");
|
|
37
|
+
}
|
|
38
|
+
const chunks = [];
|
|
39
|
+
try {
|
|
40
|
+
for (var iter = _chunkEQTYEU4Ujs.__forAwait.call(void 0, req), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
|
|
41
|
+
const chunk = temp.value;
|
|
42
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
43
|
+
}
|
|
44
|
+
} catch (temp) {
|
|
45
|
+
error = [temp];
|
|
46
|
+
} finally {
|
|
47
|
+
try {
|
|
48
|
+
more && (temp = iter.return) && (yield temp.call(iter));
|
|
49
|
+
} finally {
|
|
50
|
+
if (error)
|
|
51
|
+
throw error[0];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const body = Buffer.concat(chunks).toString("utf-8");
|
|
55
|
+
const isValid = yield receiver.verify({
|
|
56
|
+
signature,
|
|
57
|
+
body,
|
|
58
|
+
clockTolerance: config == null ? void 0 : config.clockTolerance
|
|
59
|
+
});
|
|
60
|
+
if (!isValid) {
|
|
61
|
+
res.status(400);
|
|
62
|
+
res.send("Invalid signature");
|
|
63
|
+
res.end();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
if (req.headers["content-type"] === "application/json") {
|
|
68
|
+
req.body = JSON.parse(body);
|
|
69
|
+
} else {
|
|
70
|
+
req.body = body;
|
|
71
|
+
}
|
|
72
|
+
} catch (e) {
|
|
73
|
+
req.body = body;
|
|
74
|
+
}
|
|
75
|
+
return handler(req, res);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function verifySignatureEdge(handler, config) {
|
|
79
|
+
var _a, _b;
|
|
80
|
+
const currentSigningKey = (_a = config == null ? void 0 : config.currentSigningKey) != null ? _a : process.env.QSTASH_CURRENT_SIGNING_KEY;
|
|
81
|
+
if (!currentSigningKey) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
const nextSigningKey = (_b = config == null ? void 0 : config.nextSigningKey) != null ? _b : process.env.QSTASH_NEXT_SIGNING_KEY;
|
|
87
|
+
if (!nextSigningKey) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const receiver = new (0, _chunkEQTYEU4Ujs.Receiver)({
|
|
93
|
+
currentSigningKey,
|
|
94
|
+
nextSigningKey
|
|
95
|
+
});
|
|
96
|
+
return (req, nfe) => _chunkEQTYEU4Ujs.__async.call(void 0, this, null, function* () {
|
|
97
|
+
const reqClone = req.clone();
|
|
98
|
+
const signature = req.headers.get("upstash-signature");
|
|
99
|
+
if (!signature) {
|
|
100
|
+
return new (0, _server.NextResponse)(new TextEncoder().encode("`Upstash-Signature` header is missing"), {
|
|
101
|
+
status: 403
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (typeof signature !== "string") {
|
|
105
|
+
throw new Error("`Upstash-Signature` header is not a string");
|
|
106
|
+
}
|
|
107
|
+
const body = yield req.text();
|
|
108
|
+
const isValid = yield receiver.verify({
|
|
109
|
+
signature,
|
|
110
|
+
body,
|
|
111
|
+
clockTolerance: config == null ? void 0 : config.clockTolerance
|
|
112
|
+
});
|
|
113
|
+
if (!isValid) {
|
|
114
|
+
return new (0, _server.NextResponse)(new TextEncoder().encode("invalid signature"), { status: 403 });
|
|
115
|
+
}
|
|
116
|
+
let parsedBody = void 0;
|
|
117
|
+
try {
|
|
118
|
+
if (req.headers.get("content-type") === "application/json") {
|
|
119
|
+
parsedBody = JSON.parse(body);
|
|
120
|
+
} else {
|
|
121
|
+
parsedBody = body;
|
|
122
|
+
}
|
|
123
|
+
} catch (e) {
|
|
124
|
+
parsedBody = body;
|
|
125
|
+
}
|
|
126
|
+
return handler(reqClone, nfe);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
exports.verifySignature = verifySignature; exports.verifySignatureEdge = verifySignatureEdge;
|
|
133
|
+
//# sourceMappingURL=nextjs.js.map
|
|
@@ -0,0 +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 VerifySignaturConfig = {\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?: VerifySignaturConfig,\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?: VerifySignaturConfig,\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
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Receiver,
|
|
3
|
+
__async,
|
|
4
|
+
__forAwait
|
|
5
|
+
} from "./chunk-G4FL5XMG.mjs";
|
|
6
|
+
|
|
7
|
+
// src/nextjs.ts
|
|
8
|
+
import { NextResponse } from "next/server";
|
|
9
|
+
function verifySignature(handler, config) {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
const currentSigningKey = (_a = config == null ? void 0 : config.currentSigningKey) != null ? _a : process.env.QSTASH_CURRENT_SIGNING_KEY;
|
|
12
|
+
if (!currentSigningKey) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
const nextSigningKey = (_b = config == null ? void 0 : config.nextSigningKey) != null ? _b : process.env.QSTASH_NEXT_SIGNING_KEY;
|
|
18
|
+
if (!nextSigningKey) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
const receiver = new Receiver({
|
|
24
|
+
currentSigningKey,
|
|
25
|
+
nextSigningKey
|
|
26
|
+
});
|
|
27
|
+
return (req, res) => __async(this, null, function* () {
|
|
28
|
+
const signature = req.headers["upstash-signature"];
|
|
29
|
+
if (!signature) {
|
|
30
|
+
res.status(400);
|
|
31
|
+
res.send("`Upstash-Signature` header is missing");
|
|
32
|
+
res.end();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (typeof signature !== "string") {
|
|
36
|
+
throw new Error("`Upstash-Signature` header is not a string");
|
|
37
|
+
}
|
|
38
|
+
const chunks = [];
|
|
39
|
+
try {
|
|
40
|
+
for (var iter = __forAwait(req), more, temp, error; more = !(temp = yield iter.next()).done; more = false) {
|
|
41
|
+
const chunk = temp.value;
|
|
42
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
43
|
+
}
|
|
44
|
+
} catch (temp) {
|
|
45
|
+
error = [temp];
|
|
46
|
+
} finally {
|
|
47
|
+
try {
|
|
48
|
+
more && (temp = iter.return) && (yield temp.call(iter));
|
|
49
|
+
} finally {
|
|
50
|
+
if (error)
|
|
51
|
+
throw error[0];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const body = Buffer.concat(chunks).toString("utf-8");
|
|
55
|
+
const isValid = yield receiver.verify({
|
|
56
|
+
signature,
|
|
57
|
+
body,
|
|
58
|
+
clockTolerance: config == null ? void 0 : config.clockTolerance
|
|
59
|
+
});
|
|
60
|
+
if (!isValid) {
|
|
61
|
+
res.status(400);
|
|
62
|
+
res.send("Invalid signature");
|
|
63
|
+
res.end();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
if (req.headers["content-type"] === "application/json") {
|
|
68
|
+
req.body = JSON.parse(body);
|
|
69
|
+
} else {
|
|
70
|
+
req.body = body;
|
|
71
|
+
}
|
|
72
|
+
} catch (e) {
|
|
73
|
+
req.body = body;
|
|
74
|
+
}
|
|
75
|
+
return handler(req, res);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function verifySignatureEdge(handler, config) {
|
|
79
|
+
var _a, _b;
|
|
80
|
+
const currentSigningKey = (_a = config == null ? void 0 : config.currentSigningKey) != null ? _a : process.env.QSTASH_CURRENT_SIGNING_KEY;
|
|
81
|
+
if (!currentSigningKey) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
const nextSigningKey = (_b = config == null ? void 0 : config.nextSigningKey) != null ? _b : process.env.QSTASH_NEXT_SIGNING_KEY;
|
|
87
|
+
if (!nextSigningKey) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const receiver = new Receiver({
|
|
93
|
+
currentSigningKey,
|
|
94
|
+
nextSigningKey
|
|
95
|
+
});
|
|
96
|
+
return (req, nfe) => __async(this, null, function* () {
|
|
97
|
+
const reqClone = req.clone();
|
|
98
|
+
const signature = req.headers.get("upstash-signature");
|
|
99
|
+
if (!signature) {
|
|
100
|
+
return new NextResponse(new TextEncoder().encode("`Upstash-Signature` header is missing"), {
|
|
101
|
+
status: 403
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (typeof signature !== "string") {
|
|
105
|
+
throw new Error("`Upstash-Signature` header is not a string");
|
|
106
|
+
}
|
|
107
|
+
const body = yield req.text();
|
|
108
|
+
const isValid = yield receiver.verify({
|
|
109
|
+
signature,
|
|
110
|
+
body,
|
|
111
|
+
clockTolerance: config == null ? void 0 : config.clockTolerance
|
|
112
|
+
});
|
|
113
|
+
if (!isValid) {
|
|
114
|
+
return new NextResponse(new TextEncoder().encode("invalid signature"), { status: 403 });
|
|
115
|
+
}
|
|
116
|
+
let parsedBody = void 0;
|
|
117
|
+
try {
|
|
118
|
+
if (req.headers.get("content-type") === "application/json") {
|
|
119
|
+
parsedBody = JSON.parse(body);
|
|
120
|
+
} else {
|
|
121
|
+
parsedBody = body;
|
|
122
|
+
}
|
|
123
|
+
} catch (e) {
|
|
124
|
+
parsedBody = body;
|
|
125
|
+
}
|
|
126
|
+
return handler(reqClone, nfe);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
verifySignature,
|
|
131
|
+
verifySignatureEdge
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=nextjs.mjs.map
|
|
@@ -0,0 +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 VerifySignaturConfig = {\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?: VerifySignaturConfig,\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?: VerifySignaturConfig,\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":[]}
|