UNPKG

@upstash/qstash

Version:

Official Typescript client for QStash

475 lines (465 loc) 15.1 kB
"use strict"; 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 });