@upstash/qstash
Version:
Official Typescript client for QStash
475 lines (465 loc) • 15.1 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// platforms/nuxt.ts
var nuxt_exports = {};
__export(nuxt_exports, {
verifySignatureNuxt: () => verifySignatureNuxt
});
module.exports = __toCommonJS(nuxt_exports);
// node_modules/defu/dist/defu.mjs
function isPlainObject(value) {
if (value === null || typeof value !== "object") {
return false;
}
const prototype = Object.getPrototypeOf(value);
if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) {
return false;
}
if (Symbol.iterator in value) {
return false;
}
if (Symbol.toStringTag in value) {
return Object.prototype.toString.call(value) === "[object Module]";
}
return true;
}
function _defu(baseObject, defaults, namespace = ".", merger) {
if (!isPlainObject(defaults)) {
return _defu(baseObject, {}, namespace, merger);
}
const object = Object.assign({}, defaults);
for (const key in baseObject) {
if (key === "__proto__" || key === "constructor") {
continue;
}
const value = baseObject[key];
if (value === null || value === void 0) {
continue;
}
if (merger && merger(object, key, value, namespace)) {
continue;
}
if (Array.isArray(value) && Array.isArray(object[key])) {
object[key] = [...value, ...object[key]];
} else if (isPlainObject(value) && isPlainObject(object[key])) {
object[key] = _defu(
value,
object[key],
(namespace ? `${namespace}.` : "") + key.toString(),
merger
);
} else {
object[key] = value;
}
}
return object;
}
function createDefu(merger) {
return (...arguments_) => (
// eslint-disable-next-line unicorn/no-array-reduce
arguments_.reduce((p, c) => _defu(p, c, "", merger), {})
);
}
var defu = createDefu();
var defuFn = createDefu((object, key, currentValue) => {
if (object[key] !== void 0 && typeof currentValue === "function") {
object[key] = currentValue(object[key]);
return true;
}
});
var defuArrayFn = createDefu((object, key, currentValue) => {
if (Array.isArray(object[key]) && typeof currentValue === "function") {
object[key] = currentValue(object[key]);
return true;
}
});
// node_modules/h3/dist/index.mjs
function hasProp(obj, prop) {
try {
return prop in obj;
} catch {
return false;
}
}
var __defProp$2 = Object.defineProperty;
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField$2 = (obj, key, value) => {
__defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
var H3Error = class extends Error {
constructor(message, opts = {}) {
super(message, opts);
__publicField$2(this, "statusCode", 500);
__publicField$2(this, "fatal", false);
__publicField$2(this, "unhandled", false);
__publicField$2(this, "statusMessage");
__publicField$2(this, "data");
__publicField$2(this, "cause");
if (opts.cause && !this.cause) {
this.cause = opts.cause;
}
}
toJSON() {
const obj = {
message: this.message,
statusCode: sanitizeStatusCode(this.statusCode, 500)
};
if (this.statusMessage) {
obj.statusMessage = sanitizeStatusMessage(this.statusMessage);
}
if (this.data !== void 0) {
obj.data = this.data;
}
return obj;
}
};
__publicField$2(H3Error, "__h3_error__", true);
function createError(input) {
if (typeof input === "string") {
return new H3Error(input);
}
if (isError(input)) {
return input;
}
const err4 = new H3Error(input.message ?? input.statusMessage ?? "", {
cause: input.cause || input
});
if (hasProp(input, "stack")) {
try {
Object.defineProperty(err4, "stack", {
get() {
return input.stack;
}
});
} catch {
try {
err4.stack = input.stack;
} catch {
}
}
}
if (input.data) {
err4.data = input.data;
}
if (input.statusCode) {
err4.statusCode = sanitizeStatusCode(input.statusCode, err4.statusCode);
} else if (input.status) {
err4.statusCode = sanitizeStatusCode(input.status, err4.statusCode);
}
if (input.statusMessage) {
err4.statusMessage = input.statusMessage;
} else if (input.statusText) {
err4.statusMessage = input.statusText;
}
if (err4.statusMessage) {
const originalMessage = err4.statusMessage;
const sanitizedMessage = sanitizeStatusMessage(err4.statusMessage);
if (sanitizedMessage !== originalMessage) {
console.warn(
"[h3] Please prefer using `message` for longer error messages instead of `statusMessage`. In the future, `statusMessage` will be sanitized by default."
);
}
}
if (input.fatal !== void 0) {
err4.fatal = input.fatal;
}
if (input.unhandled !== void 0) {
err4.unhandled = input.unhandled;
}
return err4;
}
function isError(input) {
return input?.constructor?.__h3_error__ === true;
}
function isMethod(event, expected, allowHead) {
if (allowHead && event.method === "HEAD") {
return true;
}
if (typeof expected === "string") {
if (event.method === expected) {
return true;
}
} else if (expected.includes(event.method)) {
return true;
}
return false;
}
function assertMethod(event, expected, allowHead) {
if (!isMethod(event, expected, allowHead)) {
throw createError({
statusCode: 405,
statusMessage: "HTTP method is not allowed."
});
}
}
function getRequestHeaders(event) {
const _headers = {};
for (const key in event.node.req.headers) {
const val = event.node.req.headers[key];
_headers[key] = Array.isArray(val) ? val.filter(Boolean).join(", ") : val;
}
return _headers;
}
function getRequestHeader(event, name) {
const headers = getRequestHeaders(event);
const value = headers[name.toLowerCase()];
return value;
}
var getHeader = getRequestHeader;
var RawBodySymbol = Symbol.for("h3RawBody");
var ParsedBodySymbol = Symbol.for("h3ParsedBody");
var PayloadMethods$1 = ["PATCH", "POST", "PUT", "DELETE"];
function readRawBody(event, encoding = "utf8") {
assertMethod(event, PayloadMethods$1);
const _rawBody = event._requestBody || event.web?.request?.body || event.node.req[RawBodySymbol] || event.node.req.rawBody || event.node.req.body;
if (_rawBody) {
const promise2 = Promise.resolve(_rawBody).then((_resolved) => {
if (Buffer.isBuffer(_resolved)) {
return _resolved;
}
if (typeof _resolved.pipeTo === "function") {
return new Promise((resolve, reject) => {
const chunks = [];
_resolved.pipeTo(
new WritableStream({
write(chunk) {
chunks.push(chunk);
},
close() {
resolve(Buffer.concat(chunks));
},
abort(reason) {
reject(reason);
}
})
).catch(reject);
});
} else if (typeof _resolved.pipe === "function") {
return new Promise((resolve, reject) => {
const chunks = [];
_resolved.on("data", (chunk) => {
chunks.push(chunk);
}).on("end", () => {
resolve(Buffer.concat(chunks));
}).on("error", reject);
});
}
if (_resolved.constructor === Object) {
return Buffer.from(JSON.stringify(_resolved));
}
return Buffer.from(_resolved);
});
return encoding ? promise2.then((buff) => buff.toString(encoding)) : promise2;
}
if (!Number.parseInt(event.node.req.headers["content-length"] || "") && !String(event.node.req.headers["transfer-encoding"] ?? "").split(",").map((e) => e.trim()).filter(Boolean).includes("chunked")) {
return Promise.resolve(void 0);
}
const promise = event.node.req[RawBodySymbol] = new Promise(
(resolve, reject) => {
const bodyData = [];
event.node.req.on("error", (err4) => {
reject(err4);
}).on("data", (chunk) => {
bodyData.push(chunk);
}).on("end", () => {
resolve(Buffer.concat(bodyData));
});
}
);
const result = encoding ? promise.then((buff) => buff.toString(encoding)) : promise;
return result;
}
var DISALLOWED_STATUS_CHARS = /[^\u0009\u0020-\u007E]/g;
function sanitizeStatusMessage(statusMessage = "") {
return statusMessage.replace(DISALLOWED_STATUS_CHARS, "");
}
function sanitizeStatusCode(statusCode, defaultStatusCode = 200) {
if (!statusCode) {
return defaultStatusCode;
}
if (typeof statusCode === "string") {
statusCode = Number.parseInt(statusCode, 10);
}
if (statusCode < 100 || statusCode > 999) {
return defaultStatusCode;
}
return statusCode;
}
var getSessionPromise = Symbol("getSession");
function defineEventHandler(handler) {
if (typeof handler === "function") {
handler.__is_handler__ = true;
return handler;
}
const _hooks = {
onRequest: _normalizeArray(handler.onRequest),
onBeforeResponse: _normalizeArray(handler.onBeforeResponse)
};
const _handler = (event) => {
return _callHandler(event, handler.handler, _hooks);
};
_handler.__is_handler__ = true;
_handler.__resolve__ = handler.handler.__resolve__;
_handler.__websocket__ = handler.websocket;
return _handler;
}
function _normalizeArray(input) {
return input ? Array.isArray(input) ? input : [input] : void 0;
}
async function _callHandler(event, handler, hooks) {
if (hooks.onRequest) {
for (const hook of hooks.onRequest) {
await hook(event);
if (event.handled) {
return;
}
}
}
const body = await handler(event);
const response = { body };
if (hooks.onBeforeResponse) {
for (const hook of hooks.onBeforeResponse) {
await hook(event, response);
}
}
return response.body;
}
var H3Headers = globalThis.Headers;
var H3Response = globalThis.Response;
// src/receiver.ts
var jose = __toESM(require("jose"));
var import_crypto_js = __toESM(require("crypto-js"));
var SignatureError = class extends Error {
constructor(message) {
super(message);
this.name = "SignatureError";
}
};
var Receiver = class {
currentSigningKey;
nextSigningKey;
constructor(config) {
this.currentSigningKey = config.currentSigningKey;
this.nextSigningKey = config.nextSigningKey;
}
/**
* Verify the signature of a request.
*
* Tries to verify the signature with the current signing key.
* If that fails, maybe because you have rotated the keys recently, it will
* try to verify the signature with the next signing key.
*
* If that fails, the signature is invalid and a `SignatureError` is thrown.
*/
async verify(request) {
let payload;
try {
payload = await this.verifyWithKey(this.currentSigningKey, request);
} catch {
payload = await this.verifyWithKey(this.nextSigningKey, request);
}
this.verifyBodyAndUrl(payload, request);
return true;
}
/**
* Verify signature with a specific signing key
*/
async verifyWithKey(key, request) {
const jwt = await jose.jwtVerify(request.signature, new TextEncoder().encode(key), {
issuer: "Upstash",
clockTolerance: request.clockTolerance
}).catch((error) => {
throw new SignatureError(error.message);
});
return jwt.payload;
}
verifyBodyAndUrl(payload, request) {
const p = payload;
if (request.url !== void 0 && p.sub !== request.url) {
throw new SignatureError(`invalid subject: ${p.sub}, want: ${request.url}`);
}
const bodyHash = import_crypto_js.default.SHA256(request.body).toString(import_crypto_js.default.enc.Base64url);
const padding = new RegExp(/=+$/);
if (p.body.replace(padding, "") !== bodyHash.replace(padding, "")) {
throw new SignatureError(`body hash does not match, want: ${p.body}, got: ${bodyHash}`);
}
}
};
// src/client/workflow/context.ts
var import_neverthrow2 = require("neverthrow");
// src/client/workflow/workflow-requests.ts
var import_neverthrow = require("neverthrow");
// src/client/workflow/workflow-parser.ts
var import_neverthrow3 = require("neverthrow");
// platforms/h3.ts
var verifySignatureH3 = (handler, config) => {
const currentSigningKey = config?.currentSigningKey ?? process.env.QSTASH_CURRENT_SIGNING_KEY;
if (!currentSigningKey) {
throw new Error(
"currentSigningKey is required, either in the config or as env variable QSTASH_CURRENT_SIGNING_KEY"
);
}
const nextSigningKey = config?.nextSigningKey ?? process.env.QSTASH_NEXT_SIGNING_KEY;
if (!nextSigningKey) {
throw new Error(
"nextSigningKey is required, either in the config or as env variable QSTASH_NEXT_SIGNING_KEY"
);
}
const receiver = new Receiver({
currentSigningKey,
nextSigningKey
});
return defineEventHandler(async (event) => {
const signature = getHeader(event, "upstash-signature");
if (!signature) {
return { status: 403, body: "`Upstash-Signature` header is missing" };
}
if (typeof signature !== "string") {
throw new TypeError("`Upstash-Signature` header is not a string");
}
const body = await readRawBody(event);
const isValid = await receiver.verify({
signature,
body: JSON.stringify(body),
clockTolerance: config?.clockTolerance
});
if (!isValid) {
return { status: 403, body: "invalid signature" };
}
event._requestBody = body;
return handler(event);
});
};
// platforms/nuxt.ts
var verifySignatureNuxt = verifySignatureH3;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
verifySignatureNuxt
});