UNPKG

@inngest/middleware-validation

Version:
175 lines (174 loc) 9.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.validationMiddleware = void 0; const inngest_1 = require("inngest"); const zod_1 = require("zod"); /** * Middleware that validates events using Zod schemas passed using * `EventSchemas.fromZod()`. */ const validationMiddleware = (opts) => { const mw = new inngest_1.InngestMiddleware({ name: "Inngest: Runtime schema validation", init({ client }) { /** * Given an `event`, validate it against its schema. */ const validateEvent = (event_1, ...args_1) => __awaiter(this, [event_1, ...args_1], void 0, function* (event, potentialInvokeEvents = []) { var _a, _b; let schemasToAttempt = new Set([event.name]); let hasSchema = false; /** * Trust internal events; don't allow overwriting their typing. */ if (event.name.startsWith("inngest/")) { if (event.name !== inngest_1.internalEvents.FunctionInvoked) { return event; } /** * If this is an `inngest/function.invoked` event, try validating the * payload against one of the function's schemas. */ schemasToAttempt = new Set(potentialInvokeEvents); hasSchema = [...schemasToAttempt.values()].some((schemaName) => { var _a; return Boolean((_a = client["schemas"]) === null || _a === void 0 ? void 0 : _a["runtimeSchemas"][schemaName]); }); } else { hasSchema = Boolean((_a = client["schemas"]) === null || _a === void 0 ? void 0 : _a["runtimeSchemas"][event.name]); } if (!hasSchema) { if (opts === null || opts === void 0 ? void 0 : opts.disallowSchemalessEvents) { throw new inngest_1.NonRetriableError(`Event "${event.name}" has no schema defined; disallowing`); } return event; } const errors = {}; for (const schemaName of schemasToAttempt) { try { const schema = (_b = client["schemas"]) === null || _b === void 0 ? void 0 : _b["runtimeSchemas"][schemaName]; /** * The schema could be a full Zod object. */ if (helpers.isZodObject(schema)) { const check = yield schema.passthrough().safeParseAsync(event); if (check.success) { return check.data; } throw new inngest_1.NonRetriableError(`${check.error.name}: ${check.error.message}`); } /** * The schema could also be a regular object with Zod objects * inside. */ if (helpers.isObject(schema)) { // It could be a partial schema; validate each field return yield Object.keys(schema).reduce((acc, key) => __awaiter(this, void 0, void 0, function* () { const fieldSchema = schema[key]; const eventField = event[key]; if (!helpers.isZodObject(fieldSchema) || !eventField) { return acc; } const check = yield fieldSchema .passthrough() .safeParseAsync(eventField); if (check.success) { return Object.assign(Object.assign({}, (yield acc)), { [key]: check.data }); } throw new inngest_1.NonRetriableError(`${check.error.name}: ${check.error.message}`); }), Promise.resolve(Object.assign({}, event))); } /** * Didn't find anything? Throw or warn. * * We only allow this for assessing single schemas, as otherwise * we're assessing an invocation would could be multiple. */ if ((opts === null || opts === void 0 ? void 0 : opts.disallowUnknownSchemas) && schemasToAttempt.size === 1) { throw new inngest_1.NonRetriableError(`Event "${event.name}" has an unknown schema; disallowing`); } else { console.warn("Unknown schema found; cannot validate, but allowing"); } } catch (err) { errors[schemaName] = err; } } if (Object.keys(errors).length) { throw new inngest_1.NonRetriableError(`Event "${event.name}" failed validation:\n\n${Object.keys(errors) .map((key) => `Using ${key}: ${errors[key].message}`) .join("\n\n")}`); } return event; }); return Object.assign(Object.assign({}, ((opts === null || opts === void 0 ? void 0 : opts.disableIncomingValidation) ? {} : { onFunctionRun(_a) { return __awaiter(this, arguments, void 0, function* ({ fn }) { const backupEvents = (fn.opts.triggers || []).reduce((acc, trigger) => { if (trigger.event) { return [...acc, trigger.event]; } return acc; }, []); return { transformInput(_a) { return __awaiter(this, arguments, void 0, function* ({ ctx: { events } }) { const validatedEvents = yield Promise.all(events.map((event) => { return validateEvent(event, backupEvents); })); return { ctx: { event: validatedEvents[0], events: validatedEvents, }, }; }); }, }; }); }, })), ((opts === null || opts === void 0 ? void 0 : opts.disableOutgoingValidation) ? {} : { onSendEvent() { return __awaiter(this, void 0, void 0, function* () { return { transformInput(_a) { return __awaiter(this, arguments, void 0, function* ({ payloads }) { return { payloads: yield Promise.all(payloads.map((payload) => { return validateEvent(payload); })), }; }); }, }; }); }, })); }, }); return mw; }; exports.validationMiddleware = validationMiddleware; const helpers = { isZodObject: (value) => { return value instanceof zod_1.ZodType && value._def.typeName === "ZodObject"; }, isObject: (value) => { return typeof value === "object" && value !== null && !Array.isArray(value); }, };