courtmitra / apps /api /src /workflow /inngest.module.ts
Hetansh Waghela
Phase 1 complete: guided frontend + adversarial API fixes + i18n
d0de93d
Raw
History Blame
2.41 kB
/**
* InngestModule β€” provides an Inngest client under the INNGEST token and a
* WorkflowService for emitting events. The worker (Track 2b) registers the
* matching functions; this module only EMITS (CLAUDE.md #2 β€” no direct
* side-effects from the API).
*
* Event contract (FIXED β€” Track 2b consumes these exactly):
* "message.received" β†’ { missionId, conversationId, messageId }
* "evidence.uploaded" β†’ { missionId, evidenceItemId }
*/
import { Global, Inject, Injectable, Module } from "@nestjs/common";
import { Inngest } from "inngest";
export const INNGEST = Symbol("INNGEST");
/** WorkflowService is the only surface that sends Inngest events from the API. */
@Injectable()
export class WorkflowService {
constructor(@Inject(INNGEST) private readonly inngest: Inngest) {}
async send(
name:
| "message.received"
| "evidence.uploaded"
| "mission.route_plan_requested"
| "mission.facts_confirmed"
| "action.execute_requested"
| "action.executed"
| "action.failed"
| "mission.followup_due"
| "email.reply_received"
| "mission.reply_processed"
| "mission.resolution_claimed"
| "ledger.outcome_recorded"
| "swarm.entity_resolution_requested"
| "swarm.member_joined"
| "portal.assist_requested"
| "portal.assist_proceeded"
| "case.lookup_requested",
data: Record<string, string | boolean>,
): Promise<void> {
try {
await this.inngest.send({ name, data });
} catch (err) {
// REVIEW_FINDINGS F1: event bus must not crash user-facing API requests.
// Non-blocking: log and continue. Loop-critical events (route_plan,
// execute, facts_confirmed) will be retried by the caller or surfaced
// through the snapshot. A transactional outbox is the Phase-2 fix.
console.error(`[WorkflowService] Failed to send event "${name}":`, err);
}
}
}
function createInngestClient(): Inngest {
// In dev mode (INNGEST_DEV=1) the SDK automatically targets the local Inngest
// dev server. Set INNGEST_BASE_URL to override (e.g. "http://localhost:8288").
return new Inngest({ id: "courtmitra" });
}
@Global()
@Module({
providers: [
{
provide: INNGEST,
useFactory: (): Inngest => createInngestClient(),
},
WorkflowService,
],
exports: [INNGEST, WorkflowService],
})
export class InngestModule {}