From 1b1071778dec328e6f0ef7b1454eab4f163ebc45 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sat, 3 May 2025 23:55:09 +0000 Subject: [PATCH] feat: run jobs syncronously when inngest is not setup --- packages/lib/jobs/client/client.ts | 67 +++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/packages/lib/jobs/client/client.ts b/packages/lib/jobs/client/client.ts index 8c9956e34..bcfa01d86 100644 --- a/packages/lib/jobs/client/client.ts +++ b/packages/lib/jobs/client/client.ts @@ -1,13 +1,19 @@ import { match } from 'ts-pattern'; import { env } from '../../utils/env'; -import type { JobDefinition, TriggerJobOptions } from './_internal/job'; +import type { + JobDefinition, + JobRunIO, + SimpleTriggerJobOptions, + TriggerJobOptions, +} from './_internal/job'; import type { BaseJobProvider as JobClientProvider } from './base'; import { InngestJobProvider } from './inngest'; import { LocalJobProvider } from './local'; export class JobClient = []> { private _provider: JobClientProvider; + private _jobDefinitions: Record = {}; public constructor(definitions: T) { this._provider = match(env('NEXT_PRIVATE_JOBS_PROVIDER')) @@ -16,10 +22,67 @@ export class JobClient = []> { definitions.forEach((definition) => { this._provider.defineJob(definition); + this._jobDefinitions[definition.id] = definition; }); } - public async triggerJob(options: TriggerJobOptions) { + /** + * Check if Inngest background job processing is available. + * + * For Inngest to be considered available: + * 1. NEXT_PRIVATE_JOBS_PROVIDER must be set to 'inngest' + * 2. Either INNGEST_EVENT_KEY or NEXT_PRIVATE_INNGEST_EVENT_KEY must be provided + * + * If Inngest is not available, jobs will be executed synchronously without background scheduling. + */ + public isInngestAvailable(): boolean { + return ( + env('NEXT_PRIVATE_JOBS_PROVIDER') === 'inngest' && + Boolean(env('INNGEST_EVENT_KEY') || env('NEXT_PRIVATE_INNGEST_EVENT_KEY')) + ); + } + + public async triggerJob(options: TriggerJobOptions): Promise { + // When Inngest is not available, execute the job directly + if (!this.isInngestAvailable()) { + const eligibleJob = Object.values(this._jobDefinitions).find( + (job) => job.trigger.name === options.name, + ); + + if (eligibleJob && eligibleJob.handler) { + // Execute the job directly without scheduling + const payload = options.payload; + const io: JobRunIO = { + wait: async (_cacheKey: string, ms: number): Promise => { + // Create a Promise that resolves after ms milliseconds + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + }, + logger: console, + runTask: async (cacheKey: string, callback: () => Promise): Promise => { + return await callback(); + }, + triggerJob: async ( + _cacheKey: string, + jobOptions: SimpleTriggerJobOptions, + ): Promise => { + // Type casting is necessary due to generic constraints + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return await this.triggerJob(jobOptions as any); + }, + }; + + try { + return await eligibleJob.handler({ payload, io }); + } catch (error) { + console.error(`Direct job execution failed for ${options.name}:`, error); + throw error; + } + } + } + + // Use background processing with Inngest if available return this._provider.triggerJob(options); }