Files
2026-07-13 13:32:57 +08:00

108 lines
3.8 KiB
TypeScript

import type { TriggerTaskRequestBody } from "@trigger.dev/core/v3";
import type { TriggerTaskServiceOptions } from "~/v3/services/triggerTask.server";
// Canonical payload shape written to the mollifier buffer when the gate
// decides to mollify a trigger. At this stage the call site ALSO calls
// engine.trigger directly (dual-write), so this is currently an
// audit/preview record. A later change makes the buffer the primary write
// path: the drainer's handler reads this payload and replays it through
// engine.trigger to create the run in Postgres, and read-fallback
// endpoints synthesise a Run view from it while it is still QUEUED.
//
// CONTRACT: this shape must contain everything the drainer-replay needs to
// reconstruct an equivalent engine.trigger call. Today it is emitted to
// logs; later it is serialised into Redis and rebuilt on the drain side.
// Keep it serialisable — no functions, no class instances.
export type BufferedTriggerPayload = {
runFriendlyId: string;
// Routing identifiers — let the drainer re-fetch full AuthenticatedEnvironment
// at replay time rather than embedding it in the payload.
envId: string;
envType: string;
envSlug: string;
orgId: string;
orgSlug: string;
projectId: string;
projectRef: string;
// Task identifier — looked up against the locked BackgroundWorkerTask
// at replay time to recover task-defaults.
taskId: string;
// Customer-supplied trigger body (payload, options, context).
body: TriggerTaskRequestBody;
// Resolved values from upstream concerns. The drainer should NOT re-resolve
// these — that would create a second idempotency-key check, etc.
idempotencyKey: string | null;
idempotencyKeyExpiresAt: string | null;
tags: string[];
// Parent/root linkage for nested triggers.
parentRunFriendlyId: string | null;
// Trace context — propagates the original triggering span across the
// buffer→drain boundary so the run's lifecycle stays under one trace.
traceContext: Record<string, unknown>;
// Annotations + service options that influence routing/replay.
triggerSource: string;
triggerAction: string;
serviceOptions: TriggerTaskServiceOptions;
// Wall-clock instants relevant to the run.
createdAt: string;
};
// Assemble the canonical payload from the inputs available at the point
// `evaluateGate` returns "mollify" in `RunEngineTriggerTaskService.call`.
// All fields must be derivable from data already in scope at that call site;
// nothing should require an extra DB lookup.
export function buildBufferedTriggerPayload(input: {
runFriendlyId: string;
taskId: string;
envId: string;
envType: string;
envSlug: string;
orgId: string;
orgSlug: string;
projectId: string;
projectRef: string;
body: TriggerTaskRequestBody;
idempotencyKey: string | null;
idempotencyKeyExpiresAt: Date | null;
tags: string[];
parentRunFriendlyId: string | null;
traceContext: Record<string, unknown>;
triggerSource: string;
triggerAction: string;
serviceOptions: TriggerTaskServiceOptions;
createdAt: Date;
}): BufferedTriggerPayload {
return {
runFriendlyId: input.runFriendlyId,
envId: input.envId,
envType: input.envType,
envSlug: input.envSlug,
orgId: input.orgId,
orgSlug: input.orgSlug,
projectId: input.projectId,
projectRef: input.projectRef,
taskId: input.taskId,
body: input.body,
idempotencyKey: input.idempotencyKey,
idempotencyKeyExpiresAt:
input.idempotencyKey && input.idempotencyKeyExpiresAt
? input.idempotencyKeyExpiresAt.toISOString()
: null,
tags: input.tags,
parentRunFriendlyId: input.parentRunFriendlyId,
traceContext: input.traceContext,
triggerSource: input.triggerSource,
triggerAction: input.triggerAction,
serviceOptions: input.serviceOptions,
createdAt: input.createdAt.toISOString(),
};
}