From 6977381e001768b3920ee55c743488e4065ddbae Mon Sep 17 00:00:00 2001 From: Mythie Date: Fri, 14 Jun 2024 13:53:48 +1000 Subject: [PATCH] feat: inngest provider --- package-lock.json | 179 ++++++++++++++++++++-- package.json | 1 + packages/lib/jobs/client/_internal/job.ts | 5 +- packages/lib/jobs/client/inngest.ts | 107 +++++++++++++ packages/lib/package.json | 5 +- turbo.json | 3 +- 6 files changed, 286 insertions(+), 14 deletions(-) create mode 100644 packages/lib/jobs/client/inngest.ts diff --git a/package-lock.json b/package-lock.json index 70c8a8e17..34f543c95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ ], "dependencies": { "@documenso/pdf-sign": "^0.1.0", + "inngest-cli": "^0.29.1", "next-runtime-env": "^3.2.0", "react": "18.2.0" }, @@ -10057,6 +10058,14 @@ "node": ">=0.4.0" } }, + "node_modules/adm-zip": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.14.tgz", + "integrity": "sha512-DnyqqifT4Jrcvb8USYjp6FHtBpEIz1mnXu6pTRHZ0RL69LbQYiO+0lDFg5+OKA7U29oWSs3a/i8fhn8ZcceIWg==", + "engines": { + "node": ">=12.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -11249,6 +11258,11 @@ } ] }, + "node_modules/canonicalize": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", + "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==" + }, "node_modules/canvas": { "version": "2.11.2", "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", @@ -11502,7 +11516,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "devOptional": true, "engines": { "node": ">=10" } @@ -15463,7 +15476,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "devOptional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -15475,7 +15487,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "devOptional": true, "dependencies": { "yallist": "^4.0.0" }, @@ -16141,6 +16152,15 @@ "resolved": "https://registry.npmjs.org/hash-wasm/-/hash-wasm-4.11.0.tgz", "integrity": "sha512-HVusNXlVqHe0fzIzdQOGolnFN6mX/fqcrSAOcTBXdvzrXVHwTz11vXeKRmkR5gTuwVpvHZEIyKoePDvuAR+XwQ==" }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -16742,6 +16762,130 @@ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" }, + "node_modules/inngest": { + "version": "3.19.13", + "resolved": "https://registry.npmjs.org/inngest/-/inngest-3.19.13.tgz", + "integrity": "sha512-Vc8yy+dq361J4ytqSF0ulc5bSrNCW4/fggpH/5sAvb32zM5ybqcZ9IMHdIpK/Xj7UXJhhtBHweasrpIXJ/mqKA==", + "dependencies": { + "@types/debug": "^4.1.12", + "canonicalize": "^1.0.8", + "chalk": "^4.1.2", + "cross-fetch": "^4.0.0", + "debug": "^4.3.4", + "hash.js": "^1.1.7", + "json-stringify-safe": "^5.0.1", + "ms": "^2.1.3", + "serialize-error-cjs": "^0.1.3", + "strip-ansi": "^5.2.0", + "zod": "~3.22.3" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@sveltejs/kit": ">=1.27.3", + "@vercel/node": ">=2.15.9", + "aws-lambda": ">=1.0.7", + "express": ">=4.19.2", + "fastify": ">=4.21.0", + "h3": ">=1.8.1", + "hono": ">=4.2.7", + "koa": ">=2.14.2", + "next": ">=12.0.0", + "typescript": ">=4.7.2" + }, + "peerDependenciesMeta": { + "@sveltejs/kit": { + "optional": true + }, + "@vercel/node": { + "optional": true + }, + "aws-lambda": { + "optional": true + }, + "express": { + "optional": true + }, + "fastify": { + "optional": true + }, + "h3": { + "optional": true + }, + "hono": { + "optional": true + }, + "koa": { + "optional": true + }, + "next": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/inngest-cli": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/inngest-cli/-/inngest-cli-0.29.1.tgz", + "integrity": "sha512-I4wa5QpJP3hO4YbO3sE+ilPgqlc5ny9HPt8kBf2teGnqJ++qPZF+VepHNOFuAwiHdhldGQttfMyysASGJbv2bA==", + "hasInstallScript": true, + "dependencies": { + "adm-zip": "^0.5.10", + "debug": "^4.3.4", + "node-fetch": "2.6.7", + "tar": "6.2.1" + }, + "bin": { + "inngest": "bin/inngest", + "inngest-cli": "bin/inngest" + } + }, + "node_modules/inngest-cli/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/inngest/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "engines": { + "node": ">=6" + } + }, + "node_modules/inngest/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/inngest/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/input-otp": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.2.4.tgz", @@ -17847,6 +17991,11 @@ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "node_modules/json5": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", @@ -20020,6 +20169,11 @@ "resolved": "https://registry.npmjs.org/minimal-polyfills/-/minimal-polyfills-2.2.3.tgz", "integrity": "sha512-oxdmJ9cL+xV72h0xYxp4tP2d5/fTBpP45H8DIOn9pASuF8a3IYTf+25fMGDYGiWW+MFsuog6KD6nfmhZJQ+uUw==" }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -20209,7 +20363,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "devOptional": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -20222,7 +20375,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "devOptional": true, "dependencies": { "yallist": "^4.0.0" }, @@ -20234,7 +20386,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "devOptional": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -25925,6 +26076,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/serialize-error-cjs": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/serialize-error-cjs/-/serialize-error-cjs-0.1.3.tgz", + "integrity": "sha512-GXwbHkufrNZ87O7DUEvWhR8eBnOqiXtHsOXakkJliG7eLDmjh6gDlbJbMZFFbUx0J5sXKgwq4NFCs41dF5MhiA==", + "funding": { + "url": "https://github.com/sponsors/finwo" + } + }, "node_modules/serialize-error/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -27203,10 +27362,9 @@ } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", - "devOptional": true, + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -31672,6 +31830,7 @@ "@trigger.dev/sdk": "^2.3.18", "@upstash/redis": "^1.20.6", "@vvo/tzdb": "^6.117.0", + "inngest": "^3.19.13", "kysely": "^0.26.3", "luxon": "^3.4.0", "nanoid": "^4.0.2", diff --git a/package.json b/package.json index e66f4dd1b..bd9c6bfc2 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ ], "dependencies": { "@documenso/pdf-sign": "^0.1.0", + "inngest-cli": "^0.29.1", "next-runtime-env": "^3.2.0", "react": "18.2.0" }, diff --git a/packages/lib/jobs/client/_internal/job.ts b/packages/lib/jobs/client/_internal/job.ts index f68cf40b7..d9e1392cc 100644 --- a/packages/lib/jobs/client/_internal/job.ts +++ b/packages/lib/jobs/client/_internal/job.ts @@ -41,7 +41,10 @@ export type JobDefinition = { export interface JobRunIO { // stableRun(cacheKey: string, callback: (io: JobRunIO) => T | Promise): Promise; - runTask(cacheKey: string, callback: () => Promise): Promise; + runTask( + cacheKey: string, + callback: () => Promise, + ): Promise; triggerJob(cacheKey: string, options: SimpleTriggerJobOptions): Promise; wait(cacheKey: string, ms: number): Promise; logger: { diff --git a/packages/lib/jobs/client/inngest.ts b/packages/lib/jobs/client/inngest.ts new file mode 100644 index 000000000..b2994072b --- /dev/null +++ b/packages/lib/jobs/client/inngest.ts @@ -0,0 +1,107 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; + +import type { Context, Handler, InngestFunction } from 'inngest'; +import { Inngest as InngestClient } from 'inngest'; +import type { Logger } from 'inngest/middleware/logger'; +import { serve as createPagesRoute } from 'inngest/next'; + +import type { JobDefinition, JobRunIO, SimpleTriggerJobOptions } from './_internal/job'; +import { BaseJobProvider } from './base'; + +export class InngestJobProvider extends BaseJobProvider { + private static _instance: InngestJobProvider; + + private _client: InngestClient; + private _functions: Array> = + []; + + private constructor(options: { client: InngestClient }) { + super(); + + this._client = options.client; + } + + static getInstance() { + if (!this._instance) { + const client = new InngestClient({ + id: 'documenso-app', + eventKey: process.env.NEXT_PRIVATE_INNGEST_EVENT_KEY, + }); + + this._instance = new InngestJobProvider({ client }); + } + + return this._instance; + } + + public defineJob(job: JobDefinition): void { + const fn = this._client.createFunction( + { + id: job.id, + name: job.name, + }, + { + event: job.trigger.name, + }, + async (ctx) => { + const io = this.convertInngestIoToJobRunIo(ctx); + + // We need to cast to any so we can deal with parsing later. + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any + let payload = ctx.event.data as any; + + if (job.trigger.schema) { + payload = job.trigger.schema.parse(payload); + } + + await job.handler({ payload, io }); + }, + ); + + this._functions.push(fn); + } + + public async triggerJob(options: SimpleTriggerJobOptions): Promise { + await this._client.send({ + id: options.id, + name: options.name, + data: options.payload, + ts: options.timestamp, + }); + } + + public getApiHandler(): (req: NextApiRequest, res: NextApiResponse) => Promise { + // !: Ignoring the error here since this is designed to work with the Next.js pages router + // !: but wants a more strict type. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + return createPagesRoute({ + client: this._client, + functions: this._functions, + }); + } + + private convertInngestIoToJobRunIo(ctx: Context.Any & { logger: Logger }) { + const { step } = ctx; + + return { + wait: step.sleep, + logger: { + ...ctx.logger, + log: ctx.logger.info, + }, + runTask: async (cacheKey, callback) => { + const result = await step.run(cacheKey, callback); + + // !: Not dealing with this right now but it should be correct. + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any + return result as any; + }, + triggerJob: async (cacheKey, payload) => + step.sendEvent(cacheKey, { + ...payload, + timestamp: payload.timestamp, + }), + } satisfies JobRunIO; + } +} diff --git a/packages/lib/package.json b/packages/lib/package.json index e0cf03da6..21c62b56b 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -36,6 +36,7 @@ "@trigger.dev/sdk": "^2.3.18", "@upstash/redis": "^1.20.6", "@vvo/tzdb": "^6.117.0", + "inngest": "^3.19.13", "kysely": "^0.26.3", "luxon": "^3.4.0", "nanoid": "^4.0.2", @@ -52,8 +53,8 @@ "zod": "^3.22.4" }, "devDependencies": { + "@playwright/browser-chromium": "1.43.0", "@types/luxon": "^3.3.1", - "@types/pg": "^8.11.4", - "@playwright/browser-chromium": "1.43.0" + "@types/pg": "^8.11.4" } } diff --git a/turbo.json b/turbo.json index e92b41087..ecac5d8aa 100644 --- a/turbo.json +++ b/turbo.json @@ -111,6 +111,7 @@ "NEXT_PRIVATE_JOBS_PROVIDER", "NEXT_PRIVATE_TRIGGER_API_KEY", "NEXT_PRIVATE_TRIGGER_API_URL", + "NEXT_PRIVATE_INNGEST_EVENT_KEY", "CI", "VERCEL", "VERCEL_ENV", @@ -129,4 +130,4 @@ "E2E_TEST_AUTHENTICATE_USER_EMAIL", "E2E_TEST_AUTHENTICATE_USER_PASSWORD" ] -} \ No newline at end of file +}