diff --git a/src/app/api/core/utils/afterIfAvailable.ts b/src/app/api/core/utils/afterIfAvailable.ts new file mode 100644 index 0000000..e434f6d --- /dev/null +++ b/src/app/api/core/utils/afterIfAvailable.ts @@ -0,0 +1,21 @@ +import { after } from 'next/server' + +/** + * Runs the callback via Next.js `after()` if inside a request scope, + * otherwise executes it directly. This allows shared service code to + * work in both Vercel serverless (request context) and Trigger.dev / + * CLI (no request context) environments. + * + * `after()` is a pure registration call — the only errors it can throw + * are scope/context errors. Real callback errors are handled separately + * via `.catch()` in the fallback path. + */ +export function afterIfAvailable(callback: () => Promise): void { + try { + after(callback) + } catch { + void callback().catch((err) => { + console.error('[afterIfAvailable] callback failed:', err) + }) + } +} diff --git a/src/app/api/quickbooks/auth/auth.service.ts b/src/app/api/quickbooks/auth/auth.service.ts index 48eecc0..3a40713 100644 --- a/src/app/api/quickbooks/auth/auth.service.ts +++ b/src/app/api/quickbooks/auth/auth.service.ts @@ -31,6 +31,7 @@ import IntuitAPI, { IntuitAPITokensType } from '@/utils/intuitAPI' import dayjs from 'dayjs' import { and, eq, SQL } from 'drizzle-orm' import httpStatus from 'http-status' +import { afterIfAvailable } from '@/app/api/core/utils/afterIfAvailable' import { after } from 'next/server' export class AuthService extends BaseService { @@ -344,7 +345,11 @@ export class AuthService extends BaseService { await tokenService.turnOffSync(intuitRealmId) // send notification to IU - after(async () => { + afterIfAvailable(async () => { + console.info( + 'AuthService#handleConnectionError | running after() .. | Sending notification to IU', + ) + const notificationService = new NotificationService(this.user) await notificationService.sendNotificationToIU( intiatedBy,