Added ability to timeout triggers so they don’t run too late after the event time
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@trigger.dev/sdk": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Added triggerTTL option that prevents old events from running a workflow
|
||||||
@@ -21,6 +21,8 @@ export function runStatusTitle(status: WorkflowRunStatus): string {
|
|||||||
return "Disconnected";
|
return "Disconnected";
|
||||||
case "ERROR":
|
case "ERROR":
|
||||||
return "Error";
|
return "Error";
|
||||||
|
case "TIMED_OUT":
|
||||||
|
return "Timed out";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +38,8 @@ export function runStatusLabel(status: WorkflowRunStatus): ReactNode {
|
|||||||
return <span className="text-amber-300">{runStatusTitle(status)}</span>;
|
return <span className="text-amber-300">{runStatusTitle(status)}</span>;
|
||||||
case "ERROR":
|
case "ERROR":
|
||||||
return <span className="text-rose-500">{runStatusTitle(status)}</span>;
|
return <span className="text-rose-500">{runStatusTitle(status)}</span>;
|
||||||
|
case "TIMED_OUT":
|
||||||
|
return <span className="text-amber-300">{runStatusTitle(status)}</span>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,5 +95,14 @@ export function runStatusIcon(
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
case "TIMED_OUT":
|
||||||
|
return (
|
||||||
|
<ExclamationTriangleIcon
|
||||||
|
className={classNames(
|
||||||
|
iconSize === "small" ? smallClasses : largeClasses,
|
||||||
|
"relative text-amber-300"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export async function findWorklowRunById(id: string) {
|
|||||||
include: {
|
include: {
|
||||||
event: true,
|
event: true,
|
||||||
environment: true,
|
environment: true,
|
||||||
|
workflow: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import { WaitForConnection } from "./requests/waitForConnection.server";
|
|||||||
import { WorkflowRunDisconnected } from "./runs/runDisconnected.server";
|
import { WorkflowRunDisconnected } from "./runs/runDisconnected.server";
|
||||||
import { DeliverScheduledEvent } from "./scheduler/deliverScheduledEvent.server";
|
import { DeliverScheduledEvent } from "./scheduler/deliverScheduledEvent.server";
|
||||||
import { RegisterSchedulerSource } from "./scheduler/registerSchedulerSource.server";
|
import { RegisterSchedulerSource } from "./scheduler/registerSchedulerSource.server";
|
||||||
|
import { WorkflowRunTriggerTimeout } from "./runs/runTriggerTimeout.server";
|
||||||
|
|
||||||
let pulsarClient: PulsarClient;
|
let pulsarClient: PulsarClient;
|
||||||
let triggerPublisher: ZodPublisher<TriggerCatalog>;
|
let triggerPublisher: ZodPublisher<TriggerCatalog>;
|
||||||
@@ -206,6 +207,13 @@ function createCommandSubscriber() {
|
|||||||
|
|
||||||
return !!success;
|
return !!success;
|
||||||
},
|
},
|
||||||
|
WORKFLOW_RUN_TRIGGER_TIMEOUT: async (id, data, properties) => {
|
||||||
|
const service = new WorkflowRunTriggerTimeout();
|
||||||
|
|
||||||
|
await service.call(data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
SEND_INTEGRATION_REQUEST: async (id, data, properties) => {
|
SEND_INTEGRATION_REQUEST: async (id, data, properties) => {
|
||||||
const service = new CreateIntegrationRequest();
|
const service = new CreateIntegrationRequest();
|
||||||
|
|
||||||
@@ -526,6 +534,10 @@ function createTaskQueue() {
|
|||||||
"x-workflow-id": run.workflowId,
|
"x-workflow-id": run.workflowId,
|
||||||
"x-env": run.environment.slug,
|
"x-env": run.environment.slug,
|
||||||
"x-workflow-run-id": run.id,
|
"x-workflow-run-id": run.id,
|
||||||
|
"x-ttl": run.workflow.triggerTtlInSeconds,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
eventTimestamp: run.event.timestamp.getTime(),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import type { PrismaClient } from "~/db.server";
|
||||||
|
import { prisma } from "~/db.server";
|
||||||
|
|
||||||
|
export class WorkflowRunTriggerTimeout {
|
||||||
|
#prismaClient: PrismaClient;
|
||||||
|
|
||||||
|
constructor(prismaClient: PrismaClient = prisma) {
|
||||||
|
this.#prismaClient = prismaClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
async call(data: { id: string; ttl: number; elapsedSeconds: number }) {
|
||||||
|
const workflowRun = await this.#prismaClient.workflowRun.findUnique({
|
||||||
|
where: { id: data.id },
|
||||||
|
include: {
|
||||||
|
event: true,
|
||||||
|
environment: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!workflowRun) {
|
||||||
|
throw new Error("Workflow run not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (workflowRun.status !== "PENDING") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.#prismaClient.workflowRun.update({
|
||||||
|
where: { id: data.id },
|
||||||
|
data: {
|
||||||
|
status: "TIMED_OUT",
|
||||||
|
timedOutAt: new Date(),
|
||||||
|
timedOutReason: `Trigger timed out after ${data.elapsedSeconds}s because it exceeded the TTL of ${data.ttl}s`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,6 +67,7 @@ export class DeliverScheduledEvent {
|
|||||||
context: {},
|
context: {},
|
||||||
organizationId: schedulerSource.organizationId,
|
organizationId: schedulerSource.organizationId,
|
||||||
environmentId: schedulerSource.environmentId,
|
environmentId: schedulerSource.environmentId,
|
||||||
|
timestamp: payload.scheduledTime,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ export class RegisterWorkflow {
|
|||||||
type: payload.trigger.type,
|
type: payload.trigger.type,
|
||||||
service: payload.trigger.service,
|
service: payload.trigger.service,
|
||||||
eventNames: payload.trigger.name,
|
eventNames: payload.trigger.name,
|
||||||
|
triggerTtlInSeconds: payload.triggerTTL,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
organizationId: organization.id,
|
organizationId: organization.id,
|
||||||
@@ -120,6 +121,7 @@ export class RegisterWorkflow {
|
|||||||
status: payload.trigger.service === "trigger" ? "READY" : "CREATED",
|
status: payload.trigger.service === "trigger" ? "READY" : "CREATED",
|
||||||
service: payload.trigger.service,
|
service: payload.trigger.service,
|
||||||
eventNames: payload.trigger.name,
|
eventNames: payload.trigger.name,
|
||||||
|
triggerTtlInSeconds: payload.triggerTTL,
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
externalSource: true,
|
externalSource: true,
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Workflow" ADD COLUMN "triggerTtlInSeconds" INTEGER NOT NULL DEFAULT 3600;
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterEnum
|
||||||
|
ALTER TYPE "WorkflowRunStatus" ADD VALUE 'TIMED_OUT';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "WorkflowRun" ADD COLUMN "timedOutAt" TIMESTAMP(3),
|
||||||
|
ADD COLUMN "timedOutReason" TEXT;
|
||||||
@@ -142,6 +142,8 @@ model Workflow {
|
|||||||
archivedAt DateTime?
|
archivedAt DateTime?
|
||||||
isArchived Boolean @default(false)
|
isArchived Boolean @default(false)
|
||||||
|
|
||||||
|
triggerTtlInSeconds Int @default(3600)
|
||||||
|
|
||||||
@@unique([organizationId, slug])
|
@@unique([organizationId, slug])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,6 +408,9 @@ model WorkflowRun {
|
|||||||
startedAt DateTime?
|
startedAt DateTime?
|
||||||
finishedAt DateTime?
|
finishedAt DateTime?
|
||||||
|
|
||||||
|
timedOutAt DateTime?
|
||||||
|
timedOutReason String?
|
||||||
|
|
||||||
isTest Boolean @default(false)
|
isTest Boolean @default(false)
|
||||||
requests IntegrationRequest[]
|
requests IntegrationRequest[]
|
||||||
delays DurableDelay[]
|
delays DurableDelay[]
|
||||||
@@ -417,6 +422,7 @@ enum WorkflowRunStatus {
|
|||||||
DISCONNECTED
|
DISCONNECTED
|
||||||
SUCCESS
|
SUCCESS
|
||||||
ERROR
|
ERROR
|
||||||
|
TIMED_OUT
|
||||||
}
|
}
|
||||||
|
|
||||||
model WorkflowRunStep {
|
model WorkflowRunStep {
|
||||||
|
|||||||
+29
-1
@@ -324,6 +324,7 @@ export class TriggerServer {
|
|||||||
name: data.packageName,
|
name: data.packageName,
|
||||||
version: data.packageVersion,
|
version: data.packageVersion,
|
||||||
},
|
},
|
||||||
|
triggerTTL: data.triggerTTL,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.#workflowId = response.id;
|
this.#workflowId = response.id;
|
||||||
@@ -340,7 +341,7 @@ export class TriggerServer {
|
|||||||
subscriptionInitialPosition: "Earliest",
|
subscriptionInitialPosition: "Earliest",
|
||||||
},
|
},
|
||||||
handlers: {
|
handlers: {
|
||||||
TRIGGER_WORKFLOW: async (id, data, properties) => {
|
TRIGGER_WORKFLOW: async (id, data, properties, messageAttributes) => {
|
||||||
this.#logger.debug("Received trigger", id, data, properties);
|
this.#logger.debug("Received trigger", id, data, properties);
|
||||||
// If the API keys don't match, then we should ignore it
|
// If the API keys don't match, then we should ignore it
|
||||||
// This ensures the workflow is triggered for the correct environment
|
// This ensures the workflow is triggered for the correct environment
|
||||||
@@ -366,6 +367,33 @@ export class TriggerServer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (properties["x-ttl"] && messageAttributes.eventTimestamp) {
|
||||||
|
const ttl = properties["x-ttl"];
|
||||||
|
const eventTimestamp = messageAttributes.eventTimestamp;
|
||||||
|
const now = Date.now();
|
||||||
|
const elapsedMilliseconds = now - eventTimestamp.getTime();
|
||||||
|
const elapsedSeconds = elapsedMilliseconds / 1000;
|
||||||
|
|
||||||
|
if (elapsedSeconds > ttl) {
|
||||||
|
this.#logger.debug("Message is expired, ignoring", {
|
||||||
|
messageAttributes,
|
||||||
|
properties,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.#commandPublisher.publish(
|
||||||
|
"WORKFLOW_RUN_TRIGGER_TIMEOUT",
|
||||||
|
{
|
||||||
|
id: data.id,
|
||||||
|
ttl,
|
||||||
|
elapsedSeconds,
|
||||||
|
},
|
||||||
|
{ ...properties, "x-timestamp": String(Date.now()) }
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.#logger.debug("Triggering workflow", data, properties);
|
this.#logger.debug("Triggering workflow", data, properties);
|
||||||
|
|
||||||
const runController = new WorkflowRunController({
|
const runController = new WorkflowRunController({
|
||||||
|
|||||||
@@ -427,6 +427,7 @@ new Trigger({
|
|||||||
apiKey: "trigger_dev_zC25mKNn6c0q",
|
apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||||
endpoint: "ws://localhost:8889/ws",
|
endpoint: "ws://localhost:8889/ws",
|
||||||
logLevel: "debug",
|
logLevel: "debug",
|
||||||
|
triggerTTL: 5,
|
||||||
on: scheduleEvent({ rateOf: { minutes: 4 } }),
|
on: scheduleEvent({ rateOf: { minutes: 4 } }),
|
||||||
run: async (event, ctx) => {
|
run: async (event, ctx) => {
|
||||||
await ctx.logger.info("Received the scheduled event", {
|
await ctx.logger.info("Received the scheduled event", {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const trigger = new Trigger({
|
|||||||
apiKey: "trigger_dev_zC25mKNn6c0q",
|
apiKey: "trigger_dev_zC25mKNn6c0q",
|
||||||
endpoint: "ws://localhost:8889/ws",
|
endpoint: "ws://localhost:8889/ws",
|
||||||
logLevel: "log",
|
logLevel: "log",
|
||||||
|
triggerTTL: 60 * 60 * 24,
|
||||||
on: customEvent({ name: "user.created", schema: userCreatedEvent }),
|
on: customEvent({ name: "user.created", schema: userCreatedEvent }),
|
||||||
run: async (event, ctx) => {
|
run: async (event, ctx) => {
|
||||||
await ctx.logger.info("Inside the smoke test workflow, received event", {
|
await ctx.logger.info("Inside the smoke test workflow, received event", {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export const ServerRPCSchema = {
|
|||||||
trigger: TriggerMetadataSchema,
|
trigger: TriggerMetadataSchema,
|
||||||
packageVersion: z.string(),
|
packageVersion: z.string(),
|
||||||
packageName: z.string(),
|
packageName: z.string(),
|
||||||
|
triggerTTL: z.number().optional(),
|
||||||
}),
|
}),
|
||||||
response: z
|
response: z
|
||||||
.discriminatedUnion("type", [
|
.discriminatedUnion("type", [
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
import { z } from "zod";
|
||||||
import { TriggerWorkflowMessageSchema } from "../schemas/workflows";
|
import { TriggerWorkflowMessageSchema } from "../schemas/workflows";
|
||||||
import { WorkflowRunEventPropertiesSchema } from "../sharedSchemas";
|
import { WorkflowRunEventPropertiesSchema } from "../sharedSchemas";
|
||||||
|
|
||||||
const Catalog = {
|
const Catalog = {
|
||||||
TRIGGER_WORKFLOW: {
|
TRIGGER_WORKFLOW: {
|
||||||
data: TriggerWorkflowMessageSchema,
|
data: TriggerWorkflowMessageSchema,
|
||||||
properties: WorkflowRunEventPropertiesSchema,
|
properties: WorkflowRunEventPropertiesSchema.extend({
|
||||||
|
"x-ttl": z.coerce.number().optional(),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -27,4 +27,12 @@ export const commands = {
|
|||||||
}),
|
}),
|
||||||
properties: WorkflowSendRunEventPropertiesSchema,
|
properties: WorkflowSendRunEventPropertiesSchema,
|
||||||
},
|
},
|
||||||
|
WORKFLOW_RUN_TRIGGER_TIMEOUT: {
|
||||||
|
data: z.object({
|
||||||
|
id: z.string(),
|
||||||
|
ttl: z.number(),
|
||||||
|
elapsedSeconds: z.number(),
|
||||||
|
}),
|
||||||
|
properties: WorkflowSendRunEventPropertiesSchema,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export type PublishOptions = {
|
|||||||
partitionKey?: string;
|
partitionKey?: string;
|
||||||
orderingKey?: string;
|
orderingKey?: string;
|
||||||
id?: string;
|
id?: string;
|
||||||
|
eventTimestamp?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type PendingMessages<PublisherSchema extends MessageCatalogSchema> = Array<{
|
type PendingMessages<PublisherSchema extends MessageCatalogSchema> = Array<{
|
||||||
@@ -216,6 +217,7 @@ export class ZodPublisher<PublisherSchema extends MessageCatalogSchema> {
|
|||||||
deliverAt: options?.deliverAt,
|
deliverAt: options?.deliverAt,
|
||||||
partitionKey: options?.partitionKey,
|
partitionKey: options?.partitionKey,
|
||||||
orderingKey: options?.orderingKey,
|
orderingKey: options?.orderingKey,
|
||||||
|
eventTimestamp: options?.eventTimestamp,
|
||||||
});
|
});
|
||||||
|
|
||||||
return response.toString();
|
return response.toString();
|
||||||
|
|||||||
@@ -14,13 +14,21 @@ import {
|
|||||||
import { z, ZodError } from "zod";
|
import { z, ZodError } from "zod";
|
||||||
import { ZodPubSubStatus } from "./types";
|
import { ZodPubSubStatus } from "./types";
|
||||||
|
|
||||||
|
export type SubscriberMessageAttributes = {
|
||||||
|
messageId: string;
|
||||||
|
eventTimestamp?: Date;
|
||||||
|
publishedTimestamp: Date;
|
||||||
|
redeliveryCount: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type ZodSubscriberHandlers<
|
export type ZodSubscriberHandlers<
|
||||||
TConsumerSchema extends MessageCatalogSchema
|
TConsumerSchema extends MessageCatalogSchema
|
||||||
> = {
|
> = {
|
||||||
[K in keyof TConsumerSchema]: (
|
[K in keyof TConsumerSchema]: (
|
||||||
id: string,
|
id: string,
|
||||||
data: z.infer<TConsumerSchema[K]["data"]>,
|
data: z.infer<TConsumerSchema[K]["data"]>,
|
||||||
properties: z.infer<TConsumerSchema[K]["properties"]>
|
properties: z.infer<TConsumerSchema[K]["properties"]>,
|
||||||
|
attributes: SubscriberMessageAttributes
|
||||||
) => Promise<boolean>;
|
) => Promise<boolean>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -109,8 +117,32 @@ export class ZodSubscriber<SubscriberSchema extends MessageCatalogSchema> {
|
|||||||
|
|
||||||
const properties = this.#getRawProperties(msg);
|
const properties = this.#getRawProperties(msg);
|
||||||
|
|
||||||
|
const messageId = msg.getMessageId();
|
||||||
|
const publishedTimestamp = msg.getPublishTimestamp();
|
||||||
|
const eventTimestamp = msg.getEventTimestamp();
|
||||||
|
const redeliveryCount = msg.getRedeliveryCount();
|
||||||
|
|
||||||
|
this.#logger.debug("#onMessage", {
|
||||||
|
messageId,
|
||||||
|
publishedTimestamp,
|
||||||
|
eventTimestamp,
|
||||||
|
redeliveryCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
const messageAttributes = {
|
||||||
|
eventTimestamp:
|
||||||
|
eventTimestamp === 0 ? undefined : new Date(eventTimestamp),
|
||||||
|
messageId: messageId.toString(),
|
||||||
|
publishedTimestamp: new Date(publishedTimestamp),
|
||||||
|
redeliveryCount,
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const wasHandled = await this.#handleMessage(messageData, properties);
|
const wasHandled = await this.#handleMessage(
|
||||||
|
messageData,
|
||||||
|
properties,
|
||||||
|
messageAttributes
|
||||||
|
);
|
||||||
|
|
||||||
if (wasHandled) {
|
if (wasHandled) {
|
||||||
await consumer.acknowledge(msg);
|
await consumer.acknowledge(msg);
|
||||||
@@ -134,7 +166,8 @@ export class ZodSubscriber<SubscriberSchema extends MessageCatalogSchema> {
|
|||||||
|
|
||||||
async #handleMessage<K extends keyof SubscriberSchema>(
|
async #handleMessage<K extends keyof SubscriberSchema>(
|
||||||
rawMessage: MessageData,
|
rawMessage: MessageData,
|
||||||
rawProperties: Record<string, string> = {}
|
rawProperties: Record<string, string> = {},
|
||||||
|
messageAttributes: SubscriberMessageAttributes
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const subscriberSchema = this.#schema;
|
const subscriberSchema = this.#schema;
|
||||||
type TypeKeys = keyof typeof subscriberSchema;
|
type TypeKeys = keyof typeof subscriberSchema;
|
||||||
@@ -160,7 +193,12 @@ export class ZodSubscriber<SubscriberSchema extends MessageCatalogSchema> {
|
|||||||
|
|
||||||
const handler = this.#handlers[typeName];
|
const handler = this.#handlers[typeName];
|
||||||
|
|
||||||
const returnValue = await handler(rawMessage.id, message, properties);
|
const returnValue = await handler(
|
||||||
|
rawMessage.id,
|
||||||
|
message,
|
||||||
|
properties,
|
||||||
|
messageAttributes
|
||||||
|
);
|
||||||
|
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export const WorkflowMetadataSchema = z.object({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
trigger: TriggerMetadataSchema,
|
trigger: TriggerMetadataSchema,
|
||||||
package: PackageMetadataSchema,
|
package: PackageMetadataSchema,
|
||||||
|
triggerTTL: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type WorkflowMetadata = z.infer<typeof WorkflowMetadataSchema>;
|
export type WorkflowMetadata = z.infer<typeof WorkflowMetadataSchema>;
|
||||||
|
|||||||
@@ -443,6 +443,7 @@ export class TriggerClient<TSchema extends z.ZodTypeAny> {
|
|||||||
trigger: this.#trigger.on.metadata,
|
trigger: this.#trigger.on.metadata,
|
||||||
packageVersion: pkg.version,
|
packageVersion: pkg.version,
|
||||||
packageName: pkg.name,
|
packageName: pkg.name,
|
||||||
|
triggerTTL: this.#options.triggerTTL,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response?.type === "error") {
|
if (response?.type === "error") {
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ export type TriggerOptions<TSchema extends z.ZodTypeAny> = {
|
|||||||
apiKey?: string;
|
apiKey?: string;
|
||||||
endpoint?: string;
|
endpoint?: string;
|
||||||
logLevel?: LogLevel;
|
logLevel?: LogLevel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The TTL for the trigger in seconds. If the trigger is not run within this time, it will be aborted. Defaults to 3600 seconds (1 hour).
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
triggerTTL?: number;
|
||||||
|
|
||||||
run: (event: z.infer<TSchema>, ctx: TriggerContext) => Promise<any>;
|
run: (event: z.infer<TSchema>, ctx: TriggerContext) => Promise<any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user