Compare commits

...

29 Commits

Author SHA1 Message Date
Eric Allam 4acfb8f4bb Release 3.0.0-beta.41 2024-06-24 17:30:01 +01:00
github-actions[bot] 2ef278db67 chore: Update version for release (beta) (#1178)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-06-24 17:29:32 +01:00
Eric Allam c7a55804d9 v3: fix jsonc-parser import in cli
Lock jsonc-parser to 3.2.1

Revert that change
2024-06-24 17:24:23 +01:00
Eric Allam da6a66efff v3: fix task queues with concurrency = 0 getting cleared on task trigger 2024-06-24 16:37:30 +01:00
Eric Allam 7c36a1a4b0 v3: Adding SDK functions for triggering tasks in a typesafe way (#1177)
* v3: Adding SDK functions for triggering tasks in a typesafe way, without importing task file

* Add type usages
2024-06-24 12:37:34 +01:00
Eric Allam 225effb599 Add task endpoints to the endpoint authentication table 2024-06-21 15:11:50 +01:00
Eric Allam 98eb6ed4f9 Add trigger and batch trigger API docs 2024-06-21 15:08:06 +01:00
Eric Allam 3069ebf0d8 Release 3.0.0-beta.40 2024-06-21 14:29:04 +01:00
github-actions[bot] e133e628ca chore: Update version for release (beta) (#1170)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-06-21 14:28:25 +01:00
Eric Allam 098932ea96 v3: edge runtime support (#1172)
* v3: remove node:stream and simplify env var upload API to better work with non-node runtimes

* Remove file/Response envvars upload docs

* Add changeset
2024-06-21 13:33:38 +01:00
Eric Allam 65f960e883 Move the max live reload setting into an env var instead of hardcoding it 2024-06-21 12:55:33 +01:00
Eric Allam ccbeff47e6 Improve trace view performance 2024-06-21 12:46:46 +01:00
Matt Aitken 7c8f2df105 After 500 logs we stop live reloading on the run page 2024-06-21 11:26:02 +01:00
Eric Allam fd44dabfe0 Disable the event loop monitor 2024-06-20 23:56:29 +01:00
Eric Allam 5daed3f69d Turn off rebalancing 2024-06-20 23:42:54 +01:00
Eric Allam 596bf78e55 Add an event loop monitor 2024-06-20 23:26:38 +01:00
Eric Allam 6ca66b76f4 Remove unnecessary transaction from the runtime environment session handling 2024-06-20 22:47:12 +01:00
nicktrn 29ef0395ce Only import types from db in task status 2024-06-20 15:25:18 +01:00
nicktrn 55d1f8c677 v3: fix dependency checkpoint race (#1171)
* consolidate task statuses and utils

* check dependency completion when creating checkpoints

* add changeset
2024-06-20 10:10:39 +01:00
Eric Allam 9835f4ec55 Adding changeset 2024-06-19 15:11:50 +01:00
Eric Allam 7fae10db23 v3: make sure force flush timeout is set so flushing doesn’t hang forever 2024-06-19 14:56:10 +01:00
Eric Allam 8cf1f0a37d v3: don’t set a default forceFlushTimeoutMillis 2024-06-19 14:29:50 +01:00
Eric Allam dba4313c5c v3: more visibility into flushing worker otel data in prod 2024-06-19 14:22:06 +01:00
Eric Allam 506613dc92 v3: prod workers should always log everything from forked node process 2024-06-19 14:05:30 +01:00
Eric Allam 764df23d19 Release 3.0.0-beta.39 2024-06-19 12:22:19 +01:00
github-actions[bot] 4b961a6ae2 chore: Update version for release (beta) (#1169)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-06-19 12:21:30 +01:00
Eric Allam 8757fdceef v3: [prod] force flush timeout should be 1s 2024-06-19 12:16:11 +01:00
Eric Allam 2404e88ac5 Add a IMPORTANT note to the ProdTaskRunExecution 2024-06-19 11:16:40 +01:00
Eric Allam 88b36f5090 Add a default on machine preset 2024-06-19 11:07:57 +01:00
123 changed files with 2433 additions and 1022 deletions
+6
View File
@@ -110,18 +110,22 @@
"rotten-dryers-exercise",
"rude-toys-compare",
"selfish-ducks-sort",
"serious-hats-rest",
"shaggy-spoons-taste",
"sharp-emus-compare",
"sharp-zebras-serve",
"shiny-coats-cry",
"silly-buses-obey",
"silly-suits-switch",
"silver-doors-juggle",
"six-ligers-exist",
"sixty-insects-watch",
"slow-buses-own",
"slow-kiwis-hide",
"slow-sloths-retire",
"smart-needles-move",
"smart-olives-eat",
"sour-pugs-teach",
"spicy-lamps-smoke",
"spicy-terms-bow",
"strange-ghosts-matter",
@@ -131,6 +135,7 @@
"strong-phones-smoke",
"stupid-adults-sniff",
"stupid-bulldogs-applaud",
"sweet-ducks-remember",
"sweet-lizards-press",
"swift-dragons-peel",
"tall-bees-wave",
@@ -149,6 +154,7 @@
"tricky-keys-attack",
"tricky-ladybugs-unite",
"two-pumas-wait",
"violet-clocks-notice",
"warm-olives-provide",
"warm-planes-taste",
"young-snails-sell"
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
v3: [prod] force flush timeout should be 1s
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/core": patch
---
Add callback to checkpoint created message
+6
View File
@@ -0,0 +1,6 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
v3: vercel edge runtime support
+6
View File
@@ -0,0 +1,6 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
v3: fix otel flushing causing CLEANUP ack timeout errors by always setting a forceFlushTimeoutMillis value
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/sdk": patch
---
v3: Adding SDK functions for triggering tasks in a typesafe way, without importing task file
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Fix jsonc-parser import
+36 -21
View File
@@ -1162,13 +1162,7 @@ class TaskCoordinator {
return;
}
if (!checkpoint.docker || !willSimulate) {
socket.emit("REQUEST_EXIT", {
version: "v1",
});
}
this.#platformSocket?.send("CHECKPOINT_CREATED", {
const ack = await this.#platformSocket?.sendWithAck("CHECKPOINT_CREATED", {
version: "v1",
attemptFriendlyId: message.attemptFriendlyId,
docker: checkpoint.docker,
@@ -1179,6 +1173,17 @@ class TaskCoordinator {
now: message.now,
},
});
if (ack?.keepRunAlive) {
logger.log("keeping run alive after duration checkpoint", { runId: socket.data.runId });
return;
}
if (!checkpoint.docker || !willSimulate) {
socket.emit("REQUEST_EXIT", {
version: "v1",
});
}
});
socket.on("WAIT_FOR_TASK", async (message, callback) => {
@@ -1205,13 +1210,7 @@ class TaskCoordinator {
return;
}
if (!checkpoint.docker || !willSimulate) {
socket.emit("REQUEST_EXIT", {
version: "v1",
});
}
this.#platformSocket?.send("CHECKPOINT_CREATED", {
const ack = await this.#platformSocket?.sendWithAck("CHECKPOINT_CREATED", {
version: "v1",
attemptFriendlyId: message.attemptFriendlyId,
docker: checkpoint.docker,
@@ -1221,6 +1220,17 @@ class TaskCoordinator {
friendlyId: message.friendlyId,
},
});
if (ack?.keepRunAlive) {
logger.log("keeping run alive after task checkpoint", { runId: socket.data.runId });
return;
}
if (!checkpoint.docker || !willSimulate) {
socket.emit("REQUEST_EXIT", {
version: "v1",
});
}
});
socket.on("WAIT_FOR_BATCH", async (message, callback) => {
@@ -1247,13 +1257,7 @@ class TaskCoordinator {
return;
}
if (!checkpoint.docker || !willSimulate) {
socket.emit("REQUEST_EXIT", {
version: "v1",
});
}
this.#platformSocket?.send("CHECKPOINT_CREATED", {
const ack = await this.#platformSocket?.sendWithAck("CHECKPOINT_CREATED", {
version: "v1",
attemptFriendlyId: message.attemptFriendlyId,
docker: checkpoint.docker,
@@ -1264,6 +1268,17 @@ class TaskCoordinator {
runFriendlyIds: message.runFriendlyIds,
},
});
if (ack?.keepRunAlive) {
logger.log("keeping run alive after batch checkpoint", { runId: socket.data.runId });
return;
}
if (!checkpoint.docker || !willSimulate) {
socket.emit("REQUEST_EXIT", {
version: "v1",
});
}
});
socket.on("INDEX_TASKS", async (message, callback) => {
@@ -528,7 +528,7 @@ export type Tree<TData> = {
/** A tree but flattened so it can easily be used for DOM elements */
export type FlatTreeItem<TData> = {
id: string;
parentId: string | undefined;
parentId?: string | undefined;
children: string[];
hasChildren: boolean;
/** The indentation level, the root is 0 */
+6
View File
@@ -186,3 +186,9 @@ export { apiRateLimiter } from "./services/apiRateLimit.server";
export { socketIo } from "./v3/handleSocketIo.server";
export { wss } from "./v3/handleWebsockets.server";
export { registryProxy } from "./v3/registryProxy.server";
import { eventLoopMonitor } from "./eventLoopMonitor.server";
import { env } from "./env.server";
if (env.EVENT_LOOP_MONITOR_ENABLED === "1") {
eventLoopMonitor.enable();
}
+2
View File
@@ -204,6 +204,8 @@ const EnvironmentSchema = z.object({
USAGE_OPEN_METER_API_KEY: z.string().optional(),
USAGE_OPEN_METER_BASE_URL: z.string().optional(),
EVENT_LOOP_MONITOR_ENABLED: z.string().default("1"),
MAXIMUM_LIVE_RELOADING_EVENTS: z.coerce.number().int().default(1000),
});
export type Environment = z.infer<typeof EnvironmentSchema>;
@@ -0,0 +1,77 @@
import { createHook } from "node:async_hooks";
import { singleton } from "./utils/singleton";
import { tracer } from "./v3/tracer.server";
const THRESHOLD_NS = 1e8; // 100ms
const cache = new Map<number, { type: string; start?: [number, number] }>();
function init(asyncId: number, type: string, triggerAsyncId: number, resource: any) {
cache.set(asyncId, {
type,
});
}
function destroy(asyncId: number) {
cache.delete(asyncId);
}
function before(asyncId: number) {
const cached = cache.get(asyncId);
if (!cached) {
return;
}
cache.set(asyncId, {
...cached,
start: process.hrtime(),
});
}
function after(asyncId: number) {
const cached = cache.get(asyncId);
if (!cached) {
return;
}
cache.delete(asyncId);
if (!cached.start) {
return;
}
const diff = process.hrtime(cached.start);
const diffNs = diff[0] * 1e9 + diff[1];
if (diffNs > THRESHOLD_NS) {
const time = diffNs / 1e6; // in ms
const newSpan = tracer.startSpan("event-loop-blocked", {
startTime: new Date(new Date().getTime() - time),
attributes: {
asyncType: cached.type,
label: "EventLoopMonitor",
},
});
newSpan.end();
}
}
export const eventLoopMonitor = singleton("eventLoopMonitor", () => {
const hook = createHook({ init, before, after, destroy });
return {
enable: () => {
console.log("🥸 Initializing event loop monitor");
hook.enable();
},
disable: () => {
console.log("🥸 Disabling event loop monitor");
hook.disable();
},
};
});
+1 -1
View File
@@ -26,7 +26,7 @@ export function useEventSource(
const eventSource = new EventSource(url, init);
eventSource.addEventListener(event ?? "message", handler);
// rest data if dependencies change
// reset data if dependencies change
setData(null);
function handler(event: MessageEvent) {
@@ -65,59 +65,55 @@ export async function findEnvironmentById(id: string) {
}
export async function createNewSession(environment: RuntimeEnvironment, ipAddress: string) {
return prisma.$transaction(async (tx) => {
const session = await tx.runtimeEnvironmentSession.create({
data: {
environmentId: environment.id,
ipAddress,
},
});
await tx.runtimeEnvironment.update({
where: {
id: environment.id,
},
data: {
currentSessionId: session.id,
},
});
return session;
const session = await prisma.runtimeEnvironmentSession.create({
data: {
environmentId: environment.id,
ipAddress,
},
});
await prisma.runtimeEnvironment.update({
where: {
id: environment.id,
},
data: {
currentSessionId: session.id,
},
});
return session;
}
export async function disconnectSession(environmentId: string) {
return prisma.$transaction(async (tx) => {
const environment = await tx.runtimeEnvironment.findUnique({
where: {
id: environmentId,
},
});
if (!environment || !environment.currentSessionId) {
return null;
}
const session = await tx.runtimeEnvironmentSession.update({
where: {
id: environment.currentSessionId,
},
data: {
disconnectedAt: new Date(),
},
});
await tx.runtimeEnvironment.update({
where: {
id: environment.id,
},
data: {
currentSessionId: null,
},
});
return session;
const environment = await prisma.runtimeEnvironment.findUnique({
where: {
id: environmentId,
},
});
if (!environment || !environment.currentSessionId) {
return null;
}
const session = await prisma.runtimeEnvironmentSession.update({
where: {
id: environment.currentSessionId,
},
data: {
disconnectedAt: new Date(),
},
});
await prisma.runtimeEnvironment.update({
where: {
id: environment.id,
},
data: {
currentSessionId: null,
},
});
return session;
}
type DisplayableInputEnvironment = Prisma.RuntimeEnvironmentGetPayload<{
@@ -4,8 +4,8 @@ import { Direction } from "~/components/runs/RunStatuses";
import { FINISHED_STATUSES } from "~/components/runs/v3/TaskRunStatus";
import { sqlDatabaseSchema } from "~/db.server";
import { displayableEnvironment } from "~/models/runtimeEnvironment.server";
import { CANCELLABLE_STATUSES } from "~/v3/services/cancelTaskRun.server";
import { BasePresenter } from "./basePresenter.server";
import { isCancellableRunStatus } from "~/v3/taskStatus";
export type RunListOptions = {
userId?: string;
@@ -291,7 +291,7 @@ export class RunListPresenter extends BasePresenter {
taskIdentifier: run.taskIdentifier,
spanId: run.spanId,
isReplayable: true,
isCancellable: CANCELLABLE_STATUSES.includes(run.status),
isCancellable: isCancellableRunStatus(run.status),
environment: displayableEnvironment(environment, userId),
idempotencyKey: run.idempotencyKey ? run.idempotencyKey : undefined,
};
@@ -94,17 +94,24 @@ export class RunStreamPresenter {
eventEmitter.removeAllListeners();
unsubscribe().catch((error) => {
logger.error("RunStreamPresenter.abort.unsubscribe", {
runFriendlyId,
traceId: run.traceId,
error: {
name: error.name,
message: error.message,
stack: error.stack,
},
unsubscribe()
.then(() => {
logger.info("RunStreamPresenter.abort.unsubscribe succeeded", {
runFriendlyId,
traceId: run.traceId,
});
})
.catch((error) => {
logger.error("RunStreamPresenter.abort.unsubscribe failed", {
runFriendlyId,
traceId: run.traceId,
error: {
name: error.name,
message: error.message,
stack: error.stack,
},
});
});
});
};
});
}
@@ -1,12 +1,13 @@
import {
BoltSlashIcon,
ChevronDownIcon,
ChevronRightIcon,
MagnifyingGlassMinusIcon,
MagnifyingGlassPlusIcon,
} from "@heroicons/react/20/solid";
import type { Location } from "@remix-run/react";
import { useParams, useRevalidator } from "@remix-run/react";
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
import { useLoaderData, useParams, useRevalidator } from "@remix-run/react";
import { LoaderFunctionArgs, SerializeFrom } from "@remix-run/server-runtime";
import { Virtualizer } from "@tanstack/react-virtual";
import {
formatDurationMilliseconds,
@@ -17,10 +18,10 @@ import { RuntimeEnvironmentType } from "@trigger.dev/database";
import { motion } from "framer-motion";
import { useCallback, useEffect, useRef, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { ShowParentIcon, ShowParentIconSelected } from "~/assets/icons/ShowParentIcon";
import tileBgPath from "~/assets/images/error-banner-tile@2x.png";
import { BlankstateInstructions } from "~/components/BlankstateInstructions";
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
import { InlineCode } from "~/components/code/InlineCode";
import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel";
import { MainCenteredContainer, PageBody } from "~/components/layout/AppLayout";
@@ -32,6 +33,7 @@ import { Input } from "~/components/primitives/Input";
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
import { Paragraph } from "~/components/primitives/Paragraph";
import { Popover, PopoverArrowTrigger, PopoverContent } from "~/components/primitives/Popover";
import { Property, PropertyTable } from "~/components/primitives/PropertyTable";
import {
ResizableHandle,
ResizablePanel,
@@ -54,7 +56,7 @@ import { useProject } from "~/hooks/useProject";
import { useReplaceLocation } from "~/hooks/useReplaceLocation";
import { Shortcut, useShortcutKeys } from "~/hooks/useShortcutKeys";
import { useUser } from "~/hooks/useUser";
import { RunEvent, RunPresenter } from "~/presenters/v3/RunPresenter.server";
import { RunPresenter } from "~/presenters/v3/RunPresenter.server";
import { getResizableRunSettings, setResizableRunSettings } from "~/services/resizablePanel";
import { requireUserId } from "~/services/session.server";
import { cn } from "~/utils/cn";
@@ -67,8 +69,10 @@ import {
v3RunsPath,
} from "~/utils/pathBuilder";
import { SpanView } from "../resources.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route";
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
import { Property, PropertyTable } from "~/components/primitives/PropertyTable";
import { SimpleTooltip } from "~/components/primitives/Tooltip";
import { env } from "~/env.server";
type TraceEvent = NonNullable<SerializeFrom<typeof loader>["trace"]>["events"][0];
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
const userId = await requireUserId(request);
@@ -85,10 +89,12 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
//resizable settings
const resizeSettings = await getResizableRunSettings(request);
return typedjson({
...result,
return {
run: result.run,
trace: result.trace,
maximumLiveReloadingSetting: env.MAXIMUM_LIVE_RELOADING_EVENTS,
resizeSettings,
});
};
};
function getSpanId(location: Location<any>): string | undefined {
@@ -97,7 +103,8 @@ function getSpanId(location: Location<any>): string | undefined {
}
export default function Page() {
const { run, trace, resizeSettings } = useTypedLoaderData<typeof loader>();
const { run, trace, resizeSettings, maximumLiveReloadingSetting } =
useLoaderData<typeof loader>();
const organization = useOrganization();
const project = useProject();
const user = useUser();
@@ -167,6 +174,7 @@ export default function Page() {
}
const { events, parentRunFriendlyId, duration, rootSpanStatus, rootStartedAt } = trace;
const shouldLiveReload = events.length <= maximumLiveReloadingSetting;
const changeToSpan = useDebounce((selectedSpan: string) => {
replaceSearchParam("span", selectedSpan);
@@ -175,6 +183,7 @@ export default function Page() {
const revalidator = useRevalidator();
const streamedEvents = useEventSource(v3RunStreamingPath(organization, project, run), {
event: "message",
disabled: !shouldLiveReload,
});
useEffect(() => {
if (streamedEvents !== null) {
@@ -252,8 +261,10 @@ export default function Page() {
}}
totalDuration={duration}
rootSpanStatus={rootSpanStatus}
rootStartedAt={rootStartedAt}
rootStartedAt={rootStartedAt ? new Date(rootStartedAt) : undefined}
environmentType={run.environment.type}
shouldLiveReload={shouldLiveReload}
maximumLiveReloadingSetting={maximumLiveReloadingSetting}
/>
</ResizablePanel>
<ResizableHandle withHandle />
@@ -274,7 +285,7 @@ export default function Page() {
}
type TasksTreeViewProps = {
events: RunEvent[];
events: TraceEvent[];
selectedId?: string;
parentRunFriendlyId?: string;
onSelectedIdChanged: (selectedId: string | undefined) => void;
@@ -282,6 +293,8 @@ type TasksTreeViewProps = {
rootSpanStatus: "executing" | "completed" | "failed";
rootStartedAt: Date | undefined;
environmentType: RuntimeEnvironmentType;
shouldLiveReload: boolean;
maximumLiveReloadingSetting: number;
};
function TasksTreeView({
@@ -293,6 +306,8 @@ function TasksTreeView({
rootSpanStatus,
rootStartedAt,
environmentType,
shouldLiveReload,
maximumLiveReloadingSetting,
}: TasksTreeViewProps) {
const [filterText, setFilterText] = useState("");
const [errorsOnly, setErrorsOnly] = useState(false);
@@ -367,7 +382,11 @@ function TasksTreeView({
This is the root task
</Paragraph>
)}
<LiveReloadingStatus rootSpanCompleted={rootSpanStatus !== "executing"} />
<LiveReloadingStatus
rootSpanCompleted={rootSpanStatus !== "executing"}
isLiveReloading={shouldLiveReload}
settingValue={maximumLiveReloadingSetting}
/>
</div>
<TreeView
parentRef={parentRef}
@@ -750,7 +769,7 @@ function TimelineView({
);
}
function NodeText({ node }: { node: RunEvent }) {
function NodeText({ node }: { node: TraceEvent }) {
const className = "truncate";
return (
<Paragraph variant="small" className={cn(className)}>
@@ -759,7 +778,7 @@ function NodeText({ node }: { node: RunEvent }) {
);
}
function NodeStatusIcon({ node }: { node: RunEvent }) {
function NodeStatusIcon({ node }: { node: TraceEvent }) {
if (node.data.level !== "TRACE") return null;
if (node.data.style.variant !== "primary") return null;
@@ -834,16 +853,40 @@ function ShowParentLink({ runFriendlyId }: { runFriendlyId: string }) {
);
}
function LiveReloadingStatus({ rootSpanCompleted }: { rootSpanCompleted: boolean }) {
function LiveReloadingStatus({
rootSpanCompleted,
isLiveReloading,
settingValue,
}: {
rootSpanCompleted: boolean;
isLiveReloading: boolean;
settingValue: number;
}) {
if (rootSpanCompleted) return null;
return (
<div className="flex items-center gap-1">
<PulsingDot />
<Paragraph variant="extra-small" className="whitespace-nowrap text-blue-500">
Live reloading
</Paragraph>
</div>
<>
{isLiveReloading ? (
<div className="flex items-center gap-1">
<PulsingDot />
<Paragraph variant="extra-small" className="whitespace-nowrap text-blue-500">
Live reloading
</Paragraph>
</div>
) : (
<SimpleTooltip
content={`Live reloading is disabled because you've exceeded ${settingValue} logs.`}
button={
<div className="flex items-center gap-1">
<BoltSlashIcon className="size-3.5 text-text-dimmed" />
<Paragraph variant="extra-small" className="whitespace-nowrap text-text-dimmed">
Live reloading disabled
</Paragraph>
</div>
}
></SimpleTooltip>
)}
</>
);
}
@@ -862,7 +905,7 @@ function SpanWithDuration({
showDuration,
node,
...props
}: Timeline.SpanProps & { node: RunEvent; showDuration: boolean }) {
}: Timeline.SpanProps & { node: TraceEvent; showDuration: boolean }) {
return (
<Timeline.Span {...props}>
<motion.div
+115 -87
View File
@@ -4,6 +4,7 @@ import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"
import {
ExceptionEventProperties,
ExceptionSpanEvent,
NULL_SENTINEL,
PRIMARY_VARIANT,
SemanticInternalAttributes,
SpanEvent,
@@ -14,7 +15,6 @@ import {
correctErrorStackTrace,
createPacketAttributesAsJson,
flattenAttributes,
NULL_SENTINEL,
isExceptionSpanEvent,
omit,
unflattenAttributes,
@@ -23,14 +23,15 @@ import { Prisma, TaskEvent, TaskEventStatus, type TaskEventKind } from "@trigger
import Redis, { RedisOptions } from "ioredis";
import { createHash } from "node:crypto";
import { EventEmitter } from "node:stream";
import { Gauge } from "prom-client";
import { $replica, PrismaClient, PrismaReplicaClient, prisma } from "~/db.server";
import { env } from "~/env.server";
import { metricsRegister } from "~/metrics.server";
import { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { logger } from "~/services/logger.server";
import { DynamicFlushScheduler } from "./dynamicFlushScheduler.server";
import { singleton } from "~/utils/singleton";
import { Gauge } from "prom-client";
import { metricsRegister } from "~/metrics.server";
import { DynamicFlushScheduler } from "./dynamicFlushScheduler.server";
import { startActiveSpan } from "./tracer.server";
export type CreatableEvent = Omit<
Prisma.TaskEventCreateInput,
@@ -374,78 +375,100 @@ export class EventRepository {
}
public async getTraceSummary(traceId: string): Promise<TraceSummary | undefined> {
const events = await this.readReplica.taskEvent.findMany({
select: {
id: true,
spanId: true,
parentId: true,
runId: true,
idempotencyKey: true,
message: true,
style: true,
startTime: true,
duration: true,
isError: true,
isPartial: true,
isCancelled: true,
level: true,
events: true,
environmentType: true,
},
where: {
traceId,
},
orderBy: {
startTime: "asc",
},
});
return await startActiveSpan("getTraceSummary", async (span) => {
const events = await this.readReplica.taskEvent.findMany({
select: {
id: true,
spanId: true,
parentId: true,
runId: true,
idempotencyKey: true,
message: true,
style: true,
startTime: true,
duration: true,
isError: true,
isPartial: true,
isCancelled: true,
level: true,
events: true,
environmentType: true,
},
where: {
traceId,
},
orderBy: {
startTime: "asc",
},
});
const preparedEvents = removeDuplicateEvents(events.map(prepareEvent));
let preparedEvents: Array<PreparedEvent> = [];
const eventsBySpanId = new Map<string, PreparedEvent>();
const spans = preparedEvents.map((event) => {
const ancestorCancelled = isAncestorCancelled(preparedEvents, event.spanId);
const duration = calculateDurationIfAncestorIsCancelled(
preparedEvents,
event.spanId,
event.duration
);
for (const event of events) {
preparedEvents.push(prepareEvent(event));
}
for (const event of preparedEvents) {
const existingEvent = eventsBySpanId.get(event.spanId);
if (!existingEvent) {
eventsBySpanId.set(event.spanId, event);
continue;
}
if (event.isCancelled || !event.isPartial) {
eventsBySpanId.set(event.spanId, event);
}
}
preparedEvents = Array.from(eventsBySpanId.values());
const spans = preparedEvents.map((event) => {
const ancestorCancelled = isAncestorCancelled(eventsBySpanId, event.spanId);
const duration = calculateDurationIfAncestorIsCancelled(
eventsBySpanId,
event.spanId,
event.duration
);
return {
recordId: event.id,
id: event.spanId,
parentId: event.parentId ?? undefined,
runId: event.runId,
idempotencyKey: event.idempotencyKey,
data: {
message: event.message,
style: event.style,
duration,
isError: event.isError,
isPartial: ancestorCancelled ? false : event.isPartial,
isCancelled: event.isCancelled === true ? true : event.isPartial && ancestorCancelled,
startTime: getDateFromNanoseconds(event.startTime),
level: event.level,
events: event.events,
environmentType: event.environmentType,
},
};
});
const rootSpanId = events.find((event) => !event.parentId);
if (!rootSpanId) {
return;
}
const rootSpan = spans.find((span) => span.id === rootSpanId.spanId);
if (!rootSpan) {
return;
}
return {
recordId: event.id,
id: event.spanId,
parentId: event.parentId ?? undefined,
runId: event.runId,
idempotencyKey: event.idempotencyKey,
data: {
message: event.message,
style: event.style,
duration,
isError: event.isError,
isPartial: ancestorCancelled ? false : event.isPartial,
isCancelled: event.isCancelled === true ? true : event.isPartial && ancestorCancelled,
startTime: getDateFromNanoseconds(event.startTime),
level: event.level,
events: event.events,
environmentType: event.environmentType,
},
rootSpan,
spans,
};
});
const rootSpanId = events.find((event) => !event.parentId);
if (!rootSpanId) {
return;
}
const rootSpan = spans.find((span) => span.id === rootSpanId.spanId);
if (!rootSpan) {
return;
}
return {
rootSpan,
spans,
};
}
// A Span can be cancelled if it is partial and has a parent that is cancelled
@@ -973,34 +996,38 @@ function prepareEvent(event: QueriedEvent): PreparedEvent {
}
function parseEventsField(events: Prisma.JsonValue): SpanEvents {
const eventsUnflattened = events
const unsafe = events
? (events as any[]).map((e) => ({
...e,
properties: unflattenAttributes(e.properties as Attributes),
}))
: undefined;
const spanEvents = SpanEvents.safeParse(eventsUnflattened);
if (spanEvents.success) {
return spanEvents.data;
}
return [];
return unsafe as SpanEvents;
}
function parseStyleField(style: Prisma.JsonValue): TaskEventStyle {
const parsedStyle = TaskEventStyle.safeParse(unflattenAttributes(style as Attributes));
const unsafe = unflattenAttributes(style as Attributes);
if (parsedStyle.success) {
return parsedStyle.data;
if (!unsafe) {
return {};
}
if (typeof unsafe === "object") {
return Object.assign(
{
icon: undefined,
variant: undefined,
},
unsafe
) as TaskEventStyle;
}
return {};
}
function isAncestorCancelled(events: PreparedEvent[], spanId: string) {
const event = events.find((event) => event.spanId === spanId);
function isAncestorCancelled(events: Map<string, PreparedEvent>, spanId: string) {
const event = events.get(spanId);
if (!event) {
return false;
@@ -1018,11 +1045,11 @@ function isAncestorCancelled(events: PreparedEvent[], spanId: string) {
}
function calculateDurationIfAncestorIsCancelled(
events: PreparedEvent[],
events: Map<string, PreparedEvent>,
spanId: string,
defaultDuration: number
) {
const event = events.find((event) => event.spanId === spanId);
const event = events.get(spanId);
if (!event) {
return defaultDuration;
@@ -1054,8 +1081,9 @@ function calculateDurationIfAncestorIsCancelled(
return defaultDuration;
}
function findFirstCancelledAncestor(events: PreparedEvent[], spanId: string) {
const event = events.find((event) => event.spanId === spanId);
function findFirstCancelledAncestor(events: Map<string, PreparedEvent>, spanId: string) {
const event = events.get(spanId);
if (!event) {
return;
}
+13 -2
View File
@@ -138,8 +138,19 @@ function createCoordinatorNamespace(io: Server) {
await sharedQueueTasks.taskRunHeartbeat(message.runId);
},
CHECKPOINT_CREATED: async (message) => {
const createCheckpoint = new CreateCheckpointService();
await createCheckpoint.call(message);
try {
const createCheckpoint = new CreateCheckpointService();
const result = await createCheckpoint.call(message);
return { keepRunAlive: result?.keepRunAlive ?? false };
} catch (error) {
logger.error("Error while creating checkpoint", {
rawMessage: message,
error: error instanceof Error ? error.message : error,
});
return { keepRunAlive: false };
}
},
CREATE_WORKER: async (message) => {
try {
+1 -1
View File
@@ -1625,7 +1625,7 @@ function getMarQSClient() {
defaultEnvConcurrency: env.DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT,
defaultOrgConcurrency: env.DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT,
visibilityTimeoutInMs: 120 * 1000, // 2 minutes,
enableRebalancing: !env.MARQS_DISABLE_REBALANCING,
enableRebalancing: false,
});
} else {
console.warn(
@@ -17,7 +17,6 @@ import {
BackgroundWorkerTask,
RuntimeEnvironment,
TaskRun,
TaskRunAttemptStatus,
TaskRunStatus,
} from "@trigger.dev/database";
import { z } from "zod";
@@ -43,6 +42,7 @@ import { generateJWTTokenForEnvironment } from "~/services/apiAuth.server";
import { EnvironmentVariable } from "../environmentVariables/repository";
import { machinePresetFromConfig } from "../machinePresets.server";
import { env } from "~/env.server";
import { isFinalAttemptStatus, isFinalRunStatus } from "../taskStatus";
const WithTraceContext = z.object({
traceparent: z.string().optional(),
@@ -962,19 +962,7 @@ class SharedQueueTasks {
}
if (setToExecuting) {
const FINAL_RUN_STATUSES: TaskRunStatus[] = [
"CANCELED",
"COMPLETED_SUCCESSFULLY",
"COMPLETED_WITH_ERRORS",
"INTERRUPTED",
"SYSTEM_FAILURE",
];
const FINAL_ATTEMPT_STATUSES: TaskRunAttemptStatus[] = ["CANCELED", "COMPLETED", "FAILED"];
if (
FINAL_ATTEMPT_STATUSES.includes(attempt.status) ||
FINAL_RUN_STATUSES.includes(attempt.taskRun.status)
) {
if (isFinalAttemptStatus(attempt.status) || isFinalRunStatus(attempt.taskRun.status)) {
logger.error("Status already in final state", {
attempt: {
id: attempt.id,
+1 -1
View File
@@ -82,7 +82,7 @@ function getMarQSClient() {
defaultEnvConcurrency: env.V2_MARQS_DEFAULT_ENV_CONCURRENCY, // this is so we aren't limited by the environment concurrency
defaultOrgConcurrency: env.DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT,
visibilityTimeoutInMs: env.V2_MARQS_VISIBILITY_TIMEOUT_MS, // 15 minutes
enableRebalancing: env.V2_MARQS_CONSUMER_POOL_ENABLED === "1",
enableRebalancing: false,
});
}
@@ -6,7 +6,7 @@ import { logger } from "~/services/logger.server";
import { PrismaClientOrTransaction, prisma } from "~/db.server";
import { ResumeTaskRunDependenciesService } from "./resumeTaskRunDependencies.server";
import { CANCELLABLE_STATUSES } from "./cancelTaskRun.server";
import { isCancellableRunStatus } from "../taskStatus";
export class CancelAttemptService extends BaseService {
public async call(
@@ -55,7 +55,7 @@ export class CancelAttemptService extends BaseService {
taskRun: {
update: {
data: {
status: CANCELLABLE_STATUSES.includes(taskRunAttempt.taskRun.status)
status: isCancellableRunStatus(taskRunAttempt.taskRun.status)
? "INTERRUPTED"
: undefined,
},
@@ -1,4 +1,4 @@
import { Prisma, TaskRun, TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database";
import { Prisma, TaskRun } from "@trigger.dev/database";
import assertNever from "assert-never";
import { logger } from "~/services/logger.server";
import { marqs } from "~/v3/marqs/index.server";
@@ -7,22 +7,7 @@ import { socketIo } from "../handleSocketIo.server";
import { devPubSub } from "../marqs/devPubSub.server";
import { BaseService } from "./baseService.server";
import { CancelAttemptService } from "./cancelAttempt.server";
export const CANCELLABLE_STATUSES: Array<TaskRunStatus> = [
"PENDING",
"WAITING_FOR_DEPLOY",
"EXECUTING",
"PAUSED",
"WAITING_TO_RESUME",
"PAUSED",
"RETRYING_AFTER_FAILURE",
];
const CANCELLABLE_ATTEMPT_STATUSES: Array<TaskRunAttemptStatus> = [
"EXECUTING",
"PAUSED",
"PENDING",
];
import { CANCELLABLE_ATTEMPT_STATUSES, isCancellableRunStatus } from "../taskStatus";
type ExtendedTaskRun = Prisma.TaskRunGetPayload<{
include: {
@@ -53,7 +38,11 @@ export class CancelTaskRunService extends BaseService {
};
// Make sure the task run is in a cancellable state
if (!CANCELLABLE_STATUSES.includes(taskRun.status)) {
if (!isCancellableRunStatus(taskRun.status)) {
logger.error("Task run is not in a cancellable state", {
runId: taskRun.id,
status: taskRun.status,
});
return;
}
@@ -1,27 +1,11 @@
import {
TaskRun,
TaskRunAttempt,
TaskRunAttemptStatus,
TaskRunStatus,
} from "@trigger.dev/database";
import { TaskRun, TaskRunAttempt } from "@trigger.dev/database";
import { eventRepository } from "../eventRepository.server";
import { marqs } from "~/v3/marqs/index.server";
import { BaseService } from "./baseService.server";
import { logger } from "~/services/logger.server";
import { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { ResumeTaskRunDependenciesService } from "./resumeTaskRunDependencies.server";
export const CRASHABLE_RUN_STATUSES: Array<TaskRunStatus> = [
"PENDING",
"WAITING_FOR_DEPLOY",
"EXECUTING",
"PAUSED",
"WAITING_TO_RESUME",
"PAUSED",
"RETRYING_AFTER_FAILURE",
];
const CRASHABLE_ATTEMPT_STATUSES: Array<TaskRunAttemptStatus> = ["EXECUTING", "PAUSED", "PENDING"];
import { CRASHABLE_ATTEMPT_STATUSES, isCrashableRunStatus } from "../taskStatus";
export type CrashTaskRunServiceOptions = {
reason?: string;
@@ -52,7 +36,8 @@ export class CrashTaskRunService extends BaseService {
}
// Make sure the task run is in a crashable state
if (!CRASHABLE_RUN_STATUSES.includes(taskRun.status)) {
if (!isCrashableRunStatus(taskRun.status)) {
logger.error("Task run is not in a crashable state", { runId, status: taskRun.status });
return;
}
@@ -1,19 +1,13 @@
import { CoordinatorToPlatformMessages } from "@trigger.dev/core/v3";
import type { InferSocketMessageSchema } from "@trigger.dev/core/v3/zodSocket";
import type {
CheckpointRestoreEvent,
TaskRunAttemptStatus,
TaskRunStatus,
} from "@trigger.dev/database";
import type { Checkpoint, CheckpointRestoreEvent } from "@trigger.dev/database";
import { logger } from "~/services/logger.server";
import { generateFriendlyId } from "../friendlyIdentifiers";
import { marqs } from "~/v3/marqs/index.server";
import { CreateCheckpointRestoreEventService } from "./createCheckpointRestoreEvent.server";
import { BaseService } from "./baseService.server";
import { CrashTaskRunService } from "./crashTaskRun.server";
const FREEZABLE_RUN_STATUSES: TaskRunStatus[] = ["EXECUTING", "RETRYING_AFTER_FAILURE"];
const FREEZABLE_ATTEMPT_STATUSES: TaskRunAttemptStatus[] = ["EXECUTING", "FAILED"];
import { isFinalRunStatus, isFreezableAttemptStatus, isFreezableRunStatus } from "../taskStatus";
export class CreateCheckpointService extends BaseService {
public async call(
@@ -21,7 +15,14 @@ export class CreateCheckpointService extends BaseService {
InferSocketMessageSchema<typeof CoordinatorToPlatformMessages, "CHECKPOINT_CREATED">,
"version"
>
) {
): Promise<
| {
checkpoint: Checkpoint;
event: CheckpointRestoreEvent;
keepRunAlive: boolean;
}
| undefined
> {
logger.debug(`Creating checkpoint`, params);
const attempt = await this._prisma.taskRunAttempt.findUnique({
@@ -49,8 +50,8 @@ export class CreateCheckpointService extends BaseService {
}
if (
!FREEZABLE_ATTEMPT_STATUSES.includes(attempt.status) ||
!FREEZABLE_RUN_STATUSES.includes(attempt.taskRun.status)
!isFreezableAttemptStatus(attempt.status) ||
!isFreezableRunStatus(attempt.taskRun.status)
) {
logger.error("Unfreezable state", {
attempt: {
@@ -115,7 +116,9 @@ export class CreateCheckpointService extends BaseService {
});
const { reason } = params;
let checkpointEvent: CheckpointRestoreEvent | undefined;
let keepRunAlive = false;
switch (reason.type) {
case "WAIT_FOR_DURATION": {
@@ -131,7 +134,12 @@ export class CreateCheckpointService extends BaseService {
dependencyFriendlyRunId: reason.friendlyId,
});
await marqs?.acknowledgeMessage(attempt.taskRunId);
keepRunAlive = await this.#isRunCompleted(reason.friendlyId);
if (!keepRunAlive) {
await marqs?.acknowledgeMessage(attempt.taskRunId);
}
break;
}
case "WAIT_FOR_BATCH": {
@@ -140,7 +148,12 @@ export class CreateCheckpointService extends BaseService {
batchDependencyFriendlyId: reason.batchFriendlyId,
});
await marqs?.acknowledgeMessage(attempt.taskRunId);
keepRunAlive = await this.#isBatchCompleted(reason.batchFriendlyId);
if (!keepRunAlive) {
await marqs?.acknowledgeMessage(attempt.taskRunId);
}
break;
}
case "RETRYING_AFTER_FAILURE": {
@@ -180,6 +193,37 @@ export class CreateCheckpointService extends BaseService {
return {
checkpoint,
event: checkpointEvent,
keepRunAlive,
};
}
async #isBatchCompleted(friendlyId: string): Promise<boolean> {
const batch = await this._prisma.batchTaskRun.findUnique({
where: {
friendlyId,
},
});
if (!batch) {
logger.error("Batch not found", { friendlyId });
return false;
}
return batch.status === "COMPLETED";
}
async #isRunCompleted(friendlyId: string): Promise<boolean> {
const run = await this._prisma.taskRun.findUnique({
where: {
friendlyId,
},
});
if (!run) {
logger.error("Run not found", { friendlyId });
return false;
}
return isFinalRunStatus(run.status);
}
}
@@ -1,12 +1,10 @@
import { TaskRunAttemptStatus, TaskRunStatus, type Checkpoint } from "@trigger.dev/database";
import { type Checkpoint } from "@trigger.dev/database";
import { logger } from "~/services/logger.server";
import { socketIo } from "../handleSocketIo.server";
import { machinePresetFromConfig } from "../machinePresets.server";
import { BaseService } from "./baseService.server";
import { CreateCheckpointRestoreEventService } from "./createCheckpointRestoreEvent.server";
const RESTORABLE_RUN_STATUSES: TaskRunStatus[] = ["WAITING_TO_RESUME"];
const RESTORABLE_ATTEMPT_STATUSES: TaskRunAttemptStatus[] = ["PAUSED"];
import { isRestorableAttemptStatus, isRestorableRunStatus } from "../taskStatus";
export class RestoreCheckpointService extends BaseService {
public async call(params: {
@@ -51,10 +49,7 @@ export class RestoreCheckpointService extends BaseService {
const checkpoint = checkpointEvent.checkpoint;
const runIsRestorable = RESTORABLE_RUN_STATUSES.includes(checkpoint.run.status);
const attemptIsRestorable = RESTORABLE_ATTEMPT_STATUSES.includes(checkpoint.attempt.status);
if (!runIsRestorable) {
if (!isRestorableRunStatus(checkpoint.run.status)) {
logger.error("Run is unrestorable", {
eventId: params.eventId,
runId: checkpoint.runId,
@@ -64,7 +59,7 @@ export class RestoreCheckpointService extends BaseService {
return;
}
if (!attemptIsRestorable && !params.isRetry) {
if (!isRestorableAttemptStatus(checkpoint.attempt.status) && !params.isRetry) {
logger.error("Attempt is unrestorable", {
eventId: params.eventId,
runId: checkpoint.runId,
@@ -176,10 +176,12 @@ export class TriggerTaskService extends BaseService {
}
if (body.options?.queue) {
const concurrencyLimit = body.options.queue.concurrencyLimit
? Math.max(0, body.options.queue.concurrencyLimit)
: null;
const taskQueue = await prisma.taskQueue.upsert({
const concurrencyLimit =
typeof body.options.queue.concurrencyLimit === "number"
? Math.max(0, body.options.queue.concurrencyLimit)
: undefined;
const taskQueue = await tx.taskQueue.upsert({
where: {
runtimeEnvironmentId_name: {
runtimeEnvironmentId: environment.id,
+69
View File
@@ -0,0 +1,69 @@
import type { TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database";
export const CANCELLABLE_RUN_STATUSES: TaskRunStatus[] = [
"PENDING",
"WAITING_FOR_DEPLOY",
"EXECUTING",
"PAUSED",
"WAITING_TO_RESUME",
"PAUSED",
"RETRYING_AFTER_FAILURE",
];
export const CANCELLABLE_ATTEMPT_STATUSES: TaskRunAttemptStatus[] = [
"EXECUTING",
"PAUSED",
"PENDING",
];
export function isCancellableRunStatus(status: TaskRunStatus): boolean {
return CANCELLABLE_RUN_STATUSES.includes(status);
}
export function isCancellableAttemptStatus(status: TaskRunAttemptStatus): boolean {
return CANCELLABLE_ATTEMPT_STATUSES.includes(status);
}
export const CRASHABLE_RUN_STATUSES: TaskRunStatus[] = CANCELLABLE_RUN_STATUSES;
export const CRASHABLE_ATTEMPT_STATUSES: TaskRunAttemptStatus[] = CANCELLABLE_ATTEMPT_STATUSES;
export function isCrashableRunStatus(status: TaskRunStatus): boolean {
return CRASHABLE_RUN_STATUSES.includes(status);
}
export function isCrashableAttemptStatus(status: TaskRunAttemptStatus): boolean {
return CRASHABLE_ATTEMPT_STATUSES.includes(status);
}
export const FINAL_RUN_STATUSES: TaskRunStatus[] = [
"CANCELED",
"COMPLETED_SUCCESSFULLY",
"COMPLETED_WITH_ERRORS",
"INTERRUPTED",
"SYSTEM_FAILURE",
];
export const FINAL_ATTEMPT_STATUSES: TaskRunAttemptStatus[] = ["CANCELED", "COMPLETED", "FAILED"];
export const FREEZABLE_RUN_STATUSES: TaskRunStatus[] = ["EXECUTING", "RETRYING_AFTER_FAILURE"];
export const FREEZABLE_ATTEMPT_STATUSES: TaskRunAttemptStatus[] = ["EXECUTING", "FAILED"];
export function isFreezableRunStatus(status: TaskRunStatus): boolean {
return FREEZABLE_RUN_STATUSES.includes(status);
}
export function isFreezableAttemptStatus(status: TaskRunAttemptStatus): boolean {
return FREEZABLE_ATTEMPT_STATUSES.includes(status);
}
export function isFinalRunStatus(status: TaskRunStatus): boolean {
return FINAL_RUN_STATUSES.includes(status);
}
export function isFinalAttemptStatus(status: TaskRunAttemptStatus): boolean {
return FINAL_ATTEMPT_STATUSES.includes(status);
}
export const RESTORABLE_RUN_STATUSES: TaskRunStatus[] = ["WAITING_TO_RESUME"];
export const RESTORABLE_ATTEMPT_STATUSES: TaskRunAttemptStatus[] = ["PAUSED"];
export function isRestorableRunStatus(status: TaskRunStatus): boolean {
return RESTORABLE_RUN_STATUSES.includes(status);
}
export function isRestorableAttemptStatus(status: TaskRunAttemptStatus): boolean {
return RESTORABLE_ATTEMPT_STATUSES.includes(status);
}
+30 -1
View File
@@ -4,7 +4,10 @@ import {
DiagConsoleLogger,
DiagLogLevel,
Link,
Span,
SpanKind,
SpanOptions,
SpanStatusCode,
diag,
trace,
} from "@opentelemetry/api";
@@ -76,6 +79,32 @@ class CustomWebappSampler implements Sampler {
export const tracer = singleton("tracer", getTracer);
export async function startActiveSpan<T>(
name: string,
fn: (span: Span) => Promise<T>,
options?: SpanOptions
): Promise<T> {
return tracer.startActiveSpan(name, options ?? {}, async (span) => {
try {
return await fn(span);
} catch (error) {
if (error instanceof Error) {
span.recordException(error);
} else if (typeof error === "string") {
span.recordException(new Error(error));
} else {
span.recordException(new Error(String(error)));
}
span.setStatus({ code: SpanStatusCode.ERROR });
throw error;
} finally {
span.end();
}
});
}
function getTracer() {
if (env.INTERNAL_OTEL_TRACE_DISABLED === "1") {
console.log(`🔦 Tracer disabled, returning a noop tracer`);
@@ -88,7 +117,7 @@ function getTracer() {
const samplingRate = 1.0 / Math.max(parseInt(env.INTERNAL_OTEL_TRACE_SAMPLING_RATE, 10), 1);
const provider = new NodeTracerProvider({
forceFlushTimeoutMillis: 500,
forceFlushTimeoutMillis: 5000,
resource: new Resource({
[SEMRESATTRS_SERVICE_NAME]: env.SERVICE_NAME,
}),
+80 -20
View File
@@ -1,8 +1,14 @@
{
"$schema": "https://mintlify.com/schema.json",
"name": "Trigger.dev",
"openapi": ["/openapi.yml", "/v3-openapi.yaml"],
"versions": ["v3 (Developer Preview)", "v2"],
"openapi": [
"/openapi.yml",
"/v3-openapi.yaml"
],
"versions": [
"v3 (Developer Preview)",
"v2"
],
"api": {
"playground": {
"mode": "simple"
@@ -96,7 +102,9 @@
{
"group": "",
"version": "v3 (Developer Preview)",
"pages": ["v3/introduction"]
"pages": [
"v3/introduction"
]
},
{
"group": "Getting Started",
@@ -119,7 +127,10 @@
"v3/apikeys",
{
"group": "Task types",
"pages": ["v3/tasks-regular", "v3/tasks-scheduled"]
"pages": [
"v3/tasks-regular",
"v3/tasks-scheduled"
]
},
"v3/trigger-config"
]
@@ -127,7 +138,10 @@
{
"group": "Development",
"version": "v3 (Developer Preview)",
"pages": ["v3/cli-dev", "v3/run-tests"]
"pages": [
"v3/cli-dev",
"v3/run-tests"
]
},
{
"group": "Deployment",
@@ -138,7 +152,9 @@
"v3/github-actions",
{
"group": "Deployment integrations",
"pages": ["v3/vercel-integration"]
"pages": [
"v3/vercel-integration"
]
}
]
},
@@ -172,6 +188,13 @@
"version": "v3 (Developer Preview)",
"pages": [
"v3/management/overview",
{
"group": "Tasks API",
"pages": [
"v3/management/tasks/trigger",
"v3/management/tasks/batch-trigger"
]
},
{
"group": "Runs API",
"pages": [
@@ -207,14 +230,20 @@
},
{
"group": "Projects API",
"pages": ["v3/management/projects/runs"]
"pages": [
"v3/management/projects/runs"
]
}
]
},
{
"group": "Open source",
"version": "v3 (Developer Preview)",
"pages": ["v3/github-repo", "v3/open-source-self-hosting", "v3/open-source-contributing"]
"pages": [
"v3/github-repo",
"v3/open-source-self-hosting",
"v3/open-source-contributing"
]
},
{
"group": "Troubleshooting",
@@ -230,7 +259,11 @@
{
"group": "Help",
"version": "v3 (Developer Preview)",
"pages": ["v3/community", "v3/help-slack", "v3/help-email"]
"pages": [
"v3/community",
"v3/help-slack",
"v3/help-email"
]
},
{
"group": "Getting Started",
@@ -422,7 +455,10 @@
"pages": [
{
"group": "Airtable",
"pages": ["integrations/apis/airtable", "integrations/apis/airtable-tasks"]
"pages": [
"integrations/apis/airtable",
"integrations/apis/airtable-tasks"
]
},
{
"group": "GitHub",
@@ -448,16 +484,25 @@
},
{
"group": "Plain",
"pages": ["integrations/apis/plain", "integrations/apis/plain-tasks"]
"pages": [
"integrations/apis/plain",
"integrations/apis/plain-tasks"
]
},
"integrations/apis/replicate",
{
"group": "SendGrid",
"pages": ["integrations/apis/sendgrid", "integrations/apis/sendgrid-tasks"]
"pages": [
"integrations/apis/sendgrid",
"integrations/apis/sendgrid-tasks"
]
},
{
"group": "Resend",
"pages": ["integrations/apis/resend", "integrations/apis/resend-tasks"]
"pages": [
"integrations/apis/resend",
"integrations/apis/resend-tasks"
]
},
{
"group": "Shopify",
@@ -469,7 +514,10 @@
},
{
"group": "Slack",
"pages": ["integrations/apis/slack", "integrations/apis/slack-tasks"]
"pages": [
"integrations/apis/slack",
"integrations/apis/slack-tasks"
]
},
"integrations/apis/stripe",
{
@@ -495,7 +543,9 @@
"sdk/triggerclient/constructor",
{
"group": "Instance properties",
"pages": ["sdk/triggerclient/store"]
"pages": [
"sdk/triggerclient/store"
]
},
{
"group": "Instance methods",
@@ -558,7 +608,10 @@
"sdk/dynamictrigger/constructor",
{
"group": "Instance methods",
"pages": ["sdk/dynamictrigger/register", "sdk/dynamictrigger/unregister"]
"pages": [
"sdk/dynamictrigger/register",
"sdk/dynamictrigger/unregister"
]
}
]
},
@@ -569,7 +622,10 @@
"sdk/dynamicschedule/constructor",
{
"group": "Instance methods",
"pages": ["sdk/dynamicschedule/register", "sdk/dynamicschedule/unregister"]
"pages": [
"sdk/dynamicschedule/register",
"sdk/dynamicschedule/unregister"
]
}
]
},
@@ -582,7 +638,9 @@
{
"group": "HTTP Reference",
"version": "v2",
"pages": ["sdk/api-reference/events/create-an-event"]
"pages": [
"sdk/api-reference/events/create-an-event"
]
},
{
"group": "React SDK",
@@ -598,7 +656,9 @@
{
"group": "Overview",
"version": "v2",
"pages": ["examples/introduction"]
"pages": [
"examples/introduction"
]
}
],
"footerSocials": {
@@ -606,4 +666,4 @@
"github": "https://github.com/triggerdotdev",
"linkedin": "https://www.linkedin.com/company/triggerdotdev"
}
}
}
+282 -58
View File
@@ -811,17 +811,7 @@ paths:
description: Whether to override existing variables or not
default: false
required: ["variables"]
multipart/form-data:
schema:
type: object
properties:
variables:
type: string
format: binary
override:
type: boolean
required:
- variables
responses:
"200":
description: Environment variables imported successfully
@@ -864,57 +854,11 @@ paths:
source: |-
import { envvars } from "@trigger.dev/sdk/v3";
// Import variables from an array
await envvars.upload("proj_yubjwjsfkxnylobaqvqz", "dev", {
variables: [
{
name: "SLACK_API_KEY",
value: "slack_123456"
}
],
variables: { SLACK_API_KEY: "slack_key_1234" },
override: false
});
- lang: typescript
label: Import variables from a read stream
source: |-
import { envvars } from "@trigger.dev/sdk/v3";
import { createReadStream } from "node:fs";
// Import variables in dotenv format from a file
await envvars.upload("proj_yubjwjsfkxnylobaqvqz", "dev", {
variables: createReadStream(".env"),
override: false
});
- lang: typescript
label: Import variables from a response
source: |-
import { envvars } from "@trigger.dev/sdk/v3";
// Import variables in dotenv format from a response
await envvars.upload("proj_yubjwjsfkxnylobaqvqz", "dev", {
variables: await fetch("https://example.com/.env"),
override: false
});
- lang: typescript
label: Import variables from a Buffer
source: |-
import { envvars } from "@trigger.dev/sdk/v3";
// Import variables in dotenv format from a buffer
await envvars.upload("proj_yubjwjsfkxnylobaqvqz", "dev", {
variables: Buffer.from("SLACK_API_KEY=slack_1234"),
override: false
});
- lang: typescript
label: Import variables from a File
source: |-
import { envvars } from "@trigger.dev/sdk/v3";
// Import variables in dotenv format from a file
await envvars.upload("proj_yubjwjsfkxnylobaqvqz", "dev", {
variables: new File(["SLACK_API_KEY=slack_1234"], ".env"),
override: false
});
"/api/v1/projects/{projectRef}/envvars/{env}/{name}":
parameters:
@@ -1095,9 +1039,229 @@ paths:
});
}
})
"/api/v1/tasks/{taskIdentifier}/trigger":
parameters:
- $ref: "#/components/parameters/taskIdentifier"
post:
operationId: trigger_task_v1
summary: Trigger a task
description: Trigger a task by its identifier.
requestBody:
required: true
content:
application/json:
schema:
"$ref": "#/components/schemas/TriggerTaskRequestBody"
responses:
"200":
description: Task triggered successfully
content:
application/json:
schema:
"$ref": "#/components/schemas/TriggerTaskResponse"
"400":
description: Invalid request parameters or body
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
"401":
description: Unauthorized request
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
"404":
description: Resource not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
tags:
- tasks
security:
- secretKey: []
x-codeSamples:
- lang: typescript
source: |-
import { task } from "@trigger.dev/sdk/v3";
export const myTask = await task({
id: "my-task",
run: async (payload: { message: string }) => {
console.log("Hello, world!");
}
});
// Somewhere else in your code
await myTask.trigger({ message: "Hello, world!" }, {
idempotencyKey: "unique-key-123",
concurrencyKey: "user123-task",
queue: {
name: "my-task-queue",
concurrencyLimit: 5
},
});
- lang: curl
source: |-
curl -X POST "https://api.trigger.dev/api/v1/tasks/my-task/trigger" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer tr_dev_1234" \
-d '{
"payload": {
"message": "Hello, world!"
},
"context": {
"user": "user123"
},
"options": {
"queue": {
"name": "default",
"concurrencyLimit": 5
},
"concurrencyKey": "user123-task",
"idempotencyKey": "unique-key-123"
}
}'
- lang: python
source: |-
import requests
url = "https://api.trigger.dev/api/v1/tasks/my-task/trigger"
headers = {
"Content-Type": "application/json",
"Authorization": "Bearer tr_dev_1234"
}
data = {
"payload": {
"message": "Hello, world!"
},
"context": {
"user": "user123"
},
"options": {
"queue": {
"name": "default",
"concurrencyLimit": 5
},
"concurrencyKey": "user123-task",
"idempotencyKey": "unique-key-123"
}
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
"/api/v1/tasks/{taskIdentifier}/batch":
parameters:
- $ref: "#/components/parameters/taskIdentifier"
post:
operationId: batch_trigger_task_v1
summary: Batch trigger a task
description: Batch trigger a task with up to 100 payloads.
requestBody:
required: true
content:
application/json:
schema:
"$ref": "#/components/schemas/BatchTriggerRequestBody"
responses:
"200":
description: Task batch triggered successfully
content:
application/json:
schema:
"$ref": "#/components/schemas/BatchTriggerTaskResponse"
"400":
description: Invalid request parameters or body
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
"401":
description: Unauthorized request
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
"404":
description: Resource not found
content:
application/json:
schema:
"$ref": "#/components/schemas/ErrorResponse"
tags:
- tasks
security:
- secretKey: []
x-codeSamples:
- lang: typescript
source: |-
import { task } from "@trigger.dev/sdk/v3";
export const myTask = await task({
id: "my-task",
run: async (payload: { message: string }) => {
console.log("Hello, world!");
}
});
// Somewhere else in your code
await myTask.batchTrigger({
items: [
{
payload: { message: "Hello, world!" },
options: {
idempotencyKey: "unique-key-123",
concurrencyKey: "user-123-task",
queue: {
name: "my-task-queue",
concurrencyLimit: 5
}
}
}
]
});
- lang: curl
source: |-
curl -X POST "https://api.trigger.dev/api/v1/tasks/my-task/batch" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer tr_dev_1234" \
-d '{
"items": [
{
"payload": {
"message": "Hello, world!"
},
"context": {
"user": "user123"
},
"options": {
"queue": {
"name": "default",
"concurrencyLimit": 5
},
"concurrencyKey": "user123-task",
"idempotencyKey": "unique-key-123"
}
}
]
}'
components:
parameters:
taskIdentifier:
in: path
name: taskIdentifier
required: true
schema:
type: string
description: The id of a task
example: my-task
runsFilterWithEnv:
in: query
name: filter
@@ -1215,6 +1379,65 @@ components:
configure({ secretKey: "tr_pat_1234" });
```
schemas:
TriggerTaskResponse:
type: object
properties:
id:
type: string
description: The ID of the run that was triggered.
example: run_1234
QueueOptions:
type: object
properties:
name:
type: string
description: You can define a shared queue and then pass the name in to your task.
concurrencyLimit:
type: integer
minimum: 0
maximum: 1000
description: An optional property that specifies the maximum number of concurrent run executions. If this property is omitted, the task can potentially use up the full concurrency of an environment.
BatchTriggerRequestBody:
type: object
properties:
items:
type: array
items:
"$ref": "#/components/schemas/TriggerTaskRequestBody"
description: An array of payloads to trigger the task with
required: ["items"]
BatchTriggerTaskResponse:
type: object
required: ["batchId", "runs"]
properties:
batchId:
type: string
description: The ID of the batch that was triggered
example: batch_1234
runs:
type: array
items:
type: string
description: An array of run IDs that were triggered
TriggerTaskRequestBody:
type: object
properties:
payload:
description: The payload can include any valid JSON
context:
description: The context can include any valid JSON
options:
type: object
properties:
queue:
$ref: '#/components/schemas/QueueOptions'
concurrencyKey:
type: string
description: Scope the concurrency limit to a specific key.
idempotencyKey:
type: string
description: An optional property that specifies the idempotency key used to prevent creating duplicate runs. If you provide an existing idempotency key, we will return the existing run ID.
EnvFilter:
type: object
properties:
@@ -1406,6 +1629,7 @@ components:
properties:
error:
type: string
example: Something went wrong
required: ["error"]
ErrorWithDetailsResponse:
type: object
+2
View File
@@ -94,6 +94,8 @@ function personalAccessTokenExample() {
| Endpoint | Secret key | Personal Access Token |
| ---------------------- | ---------- | --------------------- |
| `task.trigger` | ✅ | |
| `task.batchTrigger` | ✅ | |
| `runs.list` | ✅ | ✅ |
| `runs.retrieve` | ✅ | |
| `runs.cancel` | ✅ | |
@@ -0,0 +1,4 @@
---
title: "Batch trigger"
openapi: "v3-openapi POST /api/v1/tasks/{taskIdentifier}/batch"
---
+4
View File
@@ -0,0 +1,4 @@
---
title: "Trigger"
openapi: "v3-openapi POST /api/v1/tasks/{taskIdentifier}/trigger"
---
+234 -47
View File
@@ -3,7 +3,7 @@ title: "Triggering"
description: "Tasks need to be triggered to run."
---
There are currently four ways you can trigger any task from your own code:
There are currently six ways you can trigger tasks:
| Function | Where does this work? | What it does |
| -------------------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
@@ -11,6 +11,8 @@ There are currently four ways you can trigger any task from your own code:
| `yourTask.batchTrigger()` | Anywhere | Triggers a task multiple times and gets a handle you can use to monitor and manage the runs. It does not wait for the results. |
| `yourTask.triggerAndWait()` | Inside a task | Triggers a task and then waits until it's complete. You get the result data to continue with. |
| `yourTask.batchTriggerAndWait()` | Inside a task | Triggers a task multiple times in parallel and then waits until they're all complete. You get the resulting data to continue with. |
| `tasks.trigger()` | Outside of a task | Triggers a task and gets a handle you can use to fetch and manage the run. |
| `tasks.batchTrigger()` | Outside of a task | Triggers a task multiple times and gets a handle you can use to fetch and manage the runs. |
Additionally, [scheduled tasks](/v3/tasks-scheduled) get automatically triggered on their schedule and [webhooks](/v3/tasks-webhooks) when receiving a webhook.
@@ -18,25 +20,20 @@ Additionally, [scheduled tasks](/v3/tasks-scheduled) get automatically triggered
You should attach one or more schedules to your `schedules.task()` to trigger it on a recurring schedule. [Read the scheduled tasks docs](/v3/tasks-scheduled).
## From outside of a task
You can trigger any task from your backend code, using either `trigger()` or `batchTrigger()`.
<Note>
Do not trigger tasks directly from your frontend. If you do, you will leak your private
Trigger.dev API key to the world.
</Note>
You can use Next.js Server Actions but [you need to be careful with bundling](#next-js-server-actions).
### Authentication
## Authentication
When you trigger a task from your backend code, you need to set the `TRIGGER_SECRET_KEY` environment variable. You can find the value on the API keys page in the Trigger.dev dashboard. [More info on API keys](/v3/apikeys).
### trigger()
## Task instance methods
Task instance methods are available on the `Task` object you receive when you define a task. They can be called from your backend code or from inside another task.
### Task.trigger()
Triggers a single run of a task with the payload you pass in, and any options you specify. It does NOT wait for the result, you cannot do that from outside a task.
If called from within a task, you can use the `AndWait` version to pause execution until the triggered run is complete.
<CodeGroup>
```ts Next.js API route
@@ -74,11 +71,24 @@ export async function action({ request, params }: ActionFunctionArgs) {
}
```
```ts /trigger/my-task.ts
import { myOtherTask } from "~/trigger/my-other-task";
export const myTask = task({
id: "my-task",
run: async (payload: string) => {
const handle = await myOtherTask.trigger("some data");
//...do other stuff
},
});
```
</CodeGroup>
### batchTrigger()
### Task.batchTrigger()
Triggers multiples runs of a task with the payloads you pass in, and any options you specify. It does NOT wait for the results, you cannot do that from outside a task.
Triggers multiples runs of a task with the payloads you pass in, and any options you specify.
<CodeGroup>
@@ -121,33 +131,6 @@ export async function action({ request, params }: ActionFunctionArgs) {
}
```
</CodeGroup>
## From inside a task
You can trigger tasks from other tasks using `trigger()` or `batchTrigger()`. You can also trigger and wait for the result of triggered tasks using `triggerAndWait()` and `batchTriggerAndWait()`. This is a powerful way to build complex tasks.
### trigger()
This works the same as from outside a task. You call it and you get a handle back, but it does not wait for the result.
```ts /trigger/my-task.ts
import { myOtherTask } from "~/trigger/my-other-task";
export const myTask = task({
id: "my-task",
run: async (payload: string) => {
const handle = await myOtherTask.trigger("some data");
//...do other stuff
},
});
```
### batchTrigger()
This works the same as from outside a task. You call it and you get a handle back, but it does not wait for the results.
```ts /trigger/my-task.ts
import { myOtherTask } from "~/trigger/my-other-task";
@@ -161,7 +144,9 @@ export const myTask = task({
});
```
### triggerAndWait()
</CodeGroup>
### Task.triggerAndWait()
This is where it gets interesting. You can trigger a task and then wait for the result. This is useful when you need to call a different task and then use the result to continue with your task.
@@ -219,7 +204,7 @@ export const parentTask = task({
});
```
### batchTriggerAndWait()
### Task.batchTriggerAndWait()
You can batch trigger a task and wait for all the results. This is useful for the fan-out pattern, where you need to call a task multiple times and then wait for all the results to continue with your task.
@@ -284,11 +269,213 @@ export const batchParentTask = task({
});
```
## SDK functions
You can trigger any task from your backend code using the `tasks.trigger()` or `tasks.batchTrigger()` SDK functions.
<Note>
Do not trigger tasks directly from your frontend. If you do, you will leak your private
Trigger.dev API key.
</Note>
You can use Next.js Server Actions but [you need to be careful with bundling](#next-js-server-actions).
### tasks.trigger()
Triggers a single run of a task with the payload you pass in, and any options you specify, without needing to import the task.
<Note>
Why would you use this instead of the `Task.trigger()` instance method? Tasks can import
dependencies/modules that you might not want included in your application code or cause problems
with building.
</Note>
<CodeGroup>
```ts Next.js API route
import { tasks } from "@trigger.dev/sdk/v3";
import type { emailSequence } from "~/trigger/emails";
// 👆 **type-only** import
//app/email/route.ts
export async function POST(request: Request) {
//get the JSON from the request
const data = await request.json();
// Pass the task type to `trigger()` as a generic argument, giving you full type checking
const handle = await tasks.trigger<typeof emailSequence>("email-sequence", {
to: data.email,
name: data.name,
});
//return a success response with the handle
return Response.json(handle);
}
```
```ts Remix
import { tasks } from "@trigger.dev/sdk/v3";
export async function action({ request, params }: ActionFunctionArgs) {
if (request.method.toUpperCase() !== "POST") {
return json("Method Not Allowed", { status: 405 });
}
//get the JSON from the request
const data = await request.json();
// The generic argument is optional, but recommended for full type checking
const handle = await tasks.trigger("email-sequence", {
to: data.email,
name: data.name,
});
//return a success response with the handle
return json(handle);
}
```
</CodeGroup>
<Tip>
By importing the task with the type modifier, the import of `"~/trigger/emails"` is a type-only
import. This means that the task code is not included in your application at build time.
</Tip>
### tasks.batchTrigger()
Triggers multiples runs of a task with the payloads you pass in, and any options you specify, without needing to import the task.
<CodeGroup>
```ts Next.js API route
import { tasks } from "@trigger.dev/sdk/v3";
import type { emailSequence } from "~/trigger/emails";
// 👆 **type-only** import
//app/email/route.ts
export async function POST(request: Request) {
//get the JSON from the request
const data = await request.json();
// Pass the task type to `batchTrigger()` as a generic argument, giving you full type checking
const batchHandle = await tasks.batchTrigger<typeof emailSequence>(
"email-sequence",
data.users.map((u) => ({ payload: { to: u.email, name: u.name } }))
);
//return a success response with the handle
return Response.json(batchHandle);
}
```
</CodeGroup>
### tasks.triggerAndPoll()
Triggers a single run of a task with the payload you pass in, and any options you specify, and then polls the run until it's complete.
<CodeGroup>
```ts Next.js API route
import { tasks } from "@trigger.dev/sdk/v3";
import type { emailSequence } from "~/trigger/emails";
//app/email/route.ts
export async function POST(request: Request) {
//get the JSON from the request
const data = await request.json();
// Pass the task type to `triggerAndPoll()` as a generic argument, giving you full type checking
const result = await tasks.triggerAndPoll<typeof emailSequence>(
"email-sequence",
{
to: data.email,
name: data.name,
},
{ pollIntervalMs: 5000 }
);
//return a success response with the result
return Response.json(result);
}
```
</CodeGroup>
<Note>
The above code is just a demonstration of the API and is not recommended to use in an API route
this way as it will block the request until the task is complete.
</Note>
### runs.retrieve()
You can retrieve a run by its handle using the `runs.retrieve()` function.
<CodeGroup>
```ts Next.js API route
import { tasks, runs } from "@trigger.dev/sdk/v3";
import type { emailSequence } from "~/trigger/emails";
// 👆 **type-only** import
//app/email/route.ts
export async function POST(request: Request) {
//get the JSON from the request
const data = await request.json();
// Pass the task type to `trigger()` as a generic argument, giving you full type checking
const handle = await tasks.trigger<typeof emailSequence>("email-sequence", {
to: data.email,
name: data.name,
});
const run = await runs.retrieve(handle);
// run.output will be correctly typed as the return value of the task
return Response.json(run.output);
}
```
</CodeGroup>
### runs.poll()
You can poll a run by its handle using the `runs.poll()` function.
<CodeGroup>
```ts Next.js API route
import { tasks, runs } from "@trigger.dev/sdk/v3";
import type { emailSequence } from "~/trigger/emails";
// 👆 **type-only** import
//app/email/route.ts
export async function POST(request: Request) {
//get the JSON from the request
const data = await request.json();
// Pass the task type to `trigger()` as a generic argument, giving you full type checking
const handle = await tasks.trigger<typeof emailSequence>("email-sequence", {
to: data.email,
name: data.name,
});
// Poll the run until it's complete
const run = await runs.poll(handle, { pollIntervalMs: 5000 });
// run.output will be correctly typed as the return value of the task
return Response.json(run.output);
}
```
</CodeGroup>
## Next.js Server Actions
Server Actions allow you to call your backend code without creating API routes. This is very useful for triggering tasks but you need to be careful you don't accidentally bundle the Trigger.dev SDK into your frontend code.
If you see an error like this then you've bundled `@trigger.dev/sdk` into your frontend code:
If you see an error like this then you've bundled `@trigger.dev/sdk/v3` into your frontend code:
```bash
Module build failed: UnhandledSchemeError: Reading from "node:crypto" is not handled by plugins (Unhandled scheme).
@@ -297,7 +484,7 @@ Webpack supports "data:" and "file:" URIs by default.
You may need an additional plugin to handle "node:" URIs.
```
When you use server actions that use `@trigger.dev/sdk`:
When you use server actions that use `@trigger.dev/sdk/v3`:
- The file can't have any React components in it.
- The file should have `"use server"` on the first line.
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/airtable
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/airtable",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Trigger.dev integration for airtable",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -25,8 +25,8 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"airtable": "^0.12.1",
"zod": "3.22.3"
},
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/github
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/github",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "The official GitHub integration for Trigger.dev",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -30,8 +30,8 @@
"@octokit/request-error": "^5.0.1",
"@octokit/webhooks": "^12.0.10",
"octokit": "^3.1.2",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"zod": "3.22.3"
},
"engines": {
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/linear
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/linear",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Trigger.dev integration for @linear/sdk",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -26,8 +26,8 @@
},
"dependencies": {
"@linear/sdk": "^8.0.0",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"zod": "3.22.3"
},
"engines": {
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/slack
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/openai",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "The official OpenAI integration for Trigger.dev",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -42,8 +42,8 @@
},
"dependencies": {
"openai": "^4.16.1",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41"
},
"engines": {
"node": ">=18.0.0"
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/plain
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/plain",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "The official Plain.com integration for Trigger.dev",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -24,8 +24,8 @@
"build:tsup": "tsup"
},
"dependencies": {
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"@team-plain/typescript-sdk": "^2.7.0"
},
"engines": {
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/replicate
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/replicate",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Trigger.dev integration for replicate",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -25,8 +25,8 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"replicate": "^0.18.1",
"zod": "3.22.3"
},
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/resend
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/resend",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "The official Resend.com integration for Trigger.dev",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -24,8 +24,8 @@
"build:tsup": "tsup"
},
"dependencies": {
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"resend": "^2.1.0"
},
"engines": {
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/sendgrid
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/sendgrid",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Trigger.dev integration for @sendgrid/mail",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -26,8 +26,8 @@
},
"dependencies": {
"@sendgrid/mail": "^7.7.0",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41"
},
"engines": {
"node": ">=16.8.0"
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/shopify
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/shopify",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Trigger.dev integration for @shopify/shopify-api",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -26,8 +26,8 @@
},
"dependencies": {
"@shopify/shopify-api": "^8.0.2",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41",
"zod": "3.22.3"
},
"engines": {
+19
View File
@@ -1,5 +1,24 @@
# @trigger.dev/slack
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/slack",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "The official Slack integration for Trigger.dev",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -25,7 +25,7 @@
},
"dependencies": {
"@slack/web-api": "^6.8.1",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"zod": "3.22.3"
},
"engines": {
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/stripe
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/stripe",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Trigger.dev integration for stripe",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -25,8 +25,8 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"stripe": "^12.14.0",
"zod": "3.22.3"
},
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/supabase
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/supabase",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Trigger.dev integration for @supabase/supabase-js",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -26,8 +26,8 @@
},
"dependencies": {
"@supabase/supabase-js": "^2.26.0",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"supabase-management-js": "^1.0.0",
"zod": "3.22.3"
},
+22
View File
@@ -1,5 +1,27 @@
# @trigger.dev/typeform
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
- @trigger.dev/integration-kit@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.40
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/integration-kit@3.0.0-beta.39
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/typeform",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "The official Typeform integration for Trigger.dev",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -24,8 +24,8 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.41",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"@typeform/api-client": "^1.8.0",
"zod": "3.22.3"
},
+19
View File
@@ -1,5 +1,24 @@
# @trigger.dev/astro
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@trigger.dev/astro",
"description": "An Astro-native integration for Trigger.dev background jobs platform",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
@@ -20,7 +20,7 @@
"build:tsup": "tsup"
},
"peerDependencies": {
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41"
},
"devDependencies": {
"astro": "^3.0.12",
+25
View File
@@ -1,5 +1,30 @@
# trigger.dev
## 3.0.0-beta.41
### Patch Changes
- c7a55804d: Fix jsonc-parser import
- @trigger.dev/core@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- 098932ea9: v3: vercel edge runtime support
- 9835f4ec5: v3: fix otel flushing causing CLEANUP ack timeout errors by always setting a forceFlushTimeoutMillis value
- Updated dependencies [55d1f8c67]
- Updated dependencies [098932ea9]
- Updated dependencies [9835f4ec5]
- @trigger.dev/core@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- 8757fdcee: v3: [prod] force flush timeout should be 1s
- @trigger.dev/core@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "trigger.dev",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "A Command-Line Interface for Trigger.dev (v3) projects",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -87,7 +87,7 @@
"@opentelemetry/sdk-trace-base": "^1.22.0",
"@opentelemetry/sdk-trace-node": "^1.22.0",
"@opentelemetry/semantic-conventions": "^1.22.0",
"@trigger.dev/core": "workspace:3.0.0-beta.38",
"@trigger.dev/core": "workspace:3.0.0-beta.41",
"@types/degit": "^2.8.3",
"chalk": "^5.2.0",
"chokidar": "^3.5.3",
@@ -103,7 +103,7 @@
"gradient-string": "^2.0.2",
"import-meta-resolve": "^4.0.0",
"ink": "^4.4.1",
"jsonc-parser": "^3.2.1",
"jsonc-parser": "3.2.1",
"liquidjs": "^10.9.2",
"mock-fs": "^5.2.0",
"nanoid": "^4.0.2",
@@ -132,4 +132,4 @@
"engines": {
"node": ">=18.0.0"
}
}
}
+1 -1
View File
@@ -178,7 +178,7 @@ export async function readConfig(
write: true,
format: "cjs",
platform: "node",
target: ["es2018", "node18"],
target: ["es2020", "node18"],
outfile: builtConfigFilePath,
logLevel: "silent",
plugins: [
@@ -18,6 +18,7 @@ export const tracingSDK = new TracingSDK({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://0.0.0.0:4318",
instrumentations: setupImportedConfig?.instrumentations ?? [],
diagLogLevel: (process.env.OTEL_LOG_LEVEL as TracingDiagnosticLogLevel) ?? "none",
forceFlushTimeoutMillis: 5_000,
});
export const otelTracer: Tracer = tracingSDK.getTracer("trigger-dev-worker", packageJson.version);
@@ -863,33 +863,11 @@ class TaskRunProcess {
}
#handleLog(data: Buffer) {
if (!this._currentExecution) {
return;
}
console.log(
`[${this.metadata.version}][${this._currentExecution.run.id}.${
this._currentExecution.attempt.number
}] ${data.toString()}`
);
console.log(data.toString());
}
#handleStdErr(data: Buffer) {
if (this._isBeingKilled) {
return;
}
if (!this._currentExecution) {
console.error(`[${this.metadata.version}] ${data.toString()}`);
return;
}
console.error(
`[${this.metadata.version}][${this._currentExecution.run.id}.${
this._currentExecution.attempt.number
}] ${data.toString()}`
);
console.error(data.toString());
}
async kill(signal?: number | NodeJS.Signals, timeoutInMs?: number) {
@@ -202,18 +202,54 @@ const zodIpc = new ZodIpcConnection({
},
CLEANUP: async ({ flush, kill }, sender) => {
if (kill) {
await Promise.all([prodUsageManager.flush(), tracingSDK.flush()]);
await flushAll();
// Now we need to exit the process
await sender.send("READY_TO_DISPOSE", undefined);
} else {
if (flush) {
await Promise.all([prodUsageManager.flush(), tracingSDK.flush()]);
await flushAll();
}
}
},
},
});
async function flushAll(timeoutInMs: number = 10_000) {
const now = performance.now();
console.log(`Flushing at ${now}`);
await Promise.all([flushUsage(), flushTracingSDK()]);
const duration = performance.now() - now;
console.log(`Flushed in ${duration}ms`);
}
async function flushUsage() {
const now = performance.now();
console.log(`Flushing usage at ${now}`);
await prodUsageManager.flush();
const duration = performance.now() - now;
console.log(`Flushed usage in ${duration}ms`);
}
async function flushTracingSDK() {
const now = performance.now();
console.log(`Flushing tracingSDK at ${now}`);
await tracingSDK.flush();
const duration = performance.now() - now;
console.log(`Flushed tracingSDK in ${duration}ms`);
}
// Ignore SIGTERM, handled by entry point
process.on("SIGTERM", async () => {});
@@ -16,6 +16,9 @@ export const tracingSDK = new TracingSDK({
url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://0.0.0.0:4318",
instrumentations: setupImportedConfig?.instrumentations ?? [],
diagLogLevel: (process.env.OTEL_LOG_LEVEL as TracingDiagnosticLogLevel) ?? "none",
forceFlushTimeoutMillis: process.env.OTEL_FORCE_FLUSH_TIMEOUT
? parseInt(process.env.OTEL_FORCE_FLUSH_TIMEOUT, 10)
: 5_000,
});
export const otelTracer: Tracer = tracingSDK.getTracer("trigger-prod-worker", packageJson.version);
+24
View File
@@ -1,5 +1,29 @@
# create-trigger
## 3.0.0-beta.41
### Patch Changes
- @trigger.dev/core@3.0.0-beta.41
- @trigger.dev/yalt@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- Updated dependencies [55d1f8c67]
- Updated dependencies [098932ea9]
- Updated dependencies [9835f4ec5]
- @trigger.dev/core@3.0.0-beta.40
- @trigger.dev/yalt@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/core@3.0.0-beta.39
- @trigger.dev/yalt@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/cli",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "The Trigger.dev CLI",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/core-apps
## 3.0.0-beta.41
## 3.0.0-beta.40
## 3.0.0-beta.39
## 3.0.0-beta.38
## 3.0.0-beta.37
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@trigger.dev/core-apps",
"description": "Backend core code used across apps",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"private": true,
"license": "MIT",
"main": "./dist/index.js",
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/core-backend
## 3.0.0-beta.41
## 3.0.0-beta.40
## 3.0.0-beta.39
## 3.0.0-beta.38
## 3.0.0-beta.37
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/core-backend",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Core code used across `@trigger.dev/sdk` and Trigger.dev server",
"license": "MIT",
"main": "./dist/index.js",
+12
View File
@@ -1,5 +1,17 @@
# internal-platform
## 3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- 55d1f8c67: Add callback to checkpoint created message
- 098932ea9: v3: vercel edge runtime support
- 9835f4ec5: v3: fix otel flushing causing CLEANUP ack timeout errors by always setting a forceFlushTimeoutMillis value
## 3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/core",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Core code used across the Trigger.dev SDK and platform",
"license": "MIT",
"main": "./dist/index.js",
@@ -142,7 +142,6 @@
"@opentelemetry/sdk-trace-base": "^1.22.0",
"@opentelemetry/sdk-trace-node": "^1.22.0",
"@opentelemetry/semantic-conventions": "^1.22.0",
"form-data-encoder": "^4.0.2",
"humanize-duration": "^3.27.3",
"socket.io-client": "4.7.4",
"superjson": "^2.2.1",
@@ -157,6 +156,7 @@
"@types/humanize-duration": "^3.27.1",
"@types/jest": "^29.5.3",
"@types/node": "20.12.7",
"@types/readable-stream": "^4.0.14",
"jest": "^29.6.2",
"rimraf": "^3.0.2",
"socket.io": "4.7.4",
+1 -263
View File
@@ -3,8 +3,7 @@ import { fromZodError } from "zod-validation-error";
import { ApiConnectionError, ApiError } from "./errors";
import { RetryOptions } from "../schemas";
import { calculateNextRetryDelay } from "../utils/retries";
import { FormDataEncoder } from "form-data-encoder";
import { Readable } from "node:stream";
import {
CursorPage,
CursorPageParams,
@@ -114,59 +113,6 @@ export function zodfetchOffsetLimitPage<TItemSchema extends z.ZodTypeAny>(
return new OffsetLimitPagePromise(fetchResult, schema, url, params, requestInit, options);
}
export function zodupload<
TResponseBodySchema extends z.ZodTypeAny,
TBody = Record<string, unknown>,
>(
schema: TResponseBodySchema,
url: string,
body: TBody,
requestInit?: RequestInit,
options?: ZodFetchOptions
): ApiPromise<z.output<TResponseBodySchema>> {
const finalRequestInit = createMultipartFormRequestInit(body, requestInit);
return new ApiPromise(_doZodFetch(schema, url, finalRequestInit, options));
}
async function createMultipartFormRequestInit<TBody = Record<string, unknown>>(
body: TBody,
requestInit?: RequestInit
): Promise<RequestInit> {
const form = await createForm(body);
const encoder = new FormDataEncoder(form);
const finalHeaders: Record<string, string> = {};
for (const [key, value] of Object.entries(requestInit?.headers || {})) {
finalHeaders[key] = value as string;
}
for (const [key, value] of Object.entries(encoder.headers)) {
finalHeaders[key] = value;
}
finalHeaders["Content-Length"] = String(encoder.contentLength);
const finalRequestInit: RequestInit = {
...requestInit,
headers: finalHeaders,
body: Readable.from(encoder) as any,
// @ts-expect-error
duplex: "half",
};
return finalRequestInit;
}
const createForm = async <T = Record<string, unknown>>(body: T | undefined): Promise<FormData> => {
const form = new FormData();
await Promise.all(
Object.entries(body || {}).map(([key, value]) => addFormValue(form, key, value))
);
return form;
};
type ZodFetchResult<T> = {
data: T;
response: Response;
@@ -324,214 +270,6 @@ function requestInitWithCache(requestInit?: RequestInit): RequestInit {
}
}
const addFormValue = async (form: FormData, key: string, value: unknown): Promise<void> => {
if (value === undefined) return;
if (value == null) {
throw new TypeError(
`Received null for "${key}"; to pass null in FormData, you must use the string 'null'`
);
}
// TODO: make nested formats configurable
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
form.append(key, String(value));
} else if (
isUploadable(value) ||
isBlobLike(value) ||
value instanceof Buffer ||
value instanceof ArrayBuffer
) {
const file = await toFile(value);
form.append(key, file as File);
} else if (Array.isArray(value)) {
await Promise.all(value.map((entry) => addFormValue(form, key + "[]", entry)));
} else if (typeof value === "object") {
await Promise.all(
Object.entries(value).map(([name, prop]) => addFormValue(form, `${key}[${name}]`, prop))
);
} else {
throw new TypeError(
`Invalid value given to form, expected a string, number, boolean, object, Array, File or Blob but got ${value} instead`
);
}
};
export type ToFileInput = Uploadable | Exclude<BlobLikePart, string> | AsyncIterable<BlobLikePart>;
/**
* Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats
* @param value the raw content of the file. Can be an {@link Uploadable}, {@link BlobLikePart}, or {@link AsyncIterable} of {@link BlobLikePart}s
* @param {string=} name the name of the file. If omitted, toFile will try to determine a file name from bits if possible
* @param {Object=} options additional properties
* @param {string=} options.type the MIME type of the content
* @param {number=} options.lastModified the last modified timestamp
* @returns a {@link File} with the given properties
*/
export async function toFile(
value: ToFileInput | PromiseLike<ToFileInput>,
name?: string | null | undefined,
options?: FilePropertyBag | undefined
): Promise<FileLike> {
// If it's a promise, resolve it.
value = await value;
// Use the file's options if there isn't one provided
options ??= isFileLike(value) ? { lastModified: value.lastModified, type: value.type } : {};
if (isResponseLike(value)) {
const blob = await value.blob();
name ||= new URL(value.url).pathname.split(/[\\/]/).pop() ?? "unknown_file";
return new File([blob as any], name, options);
}
const bits = await getBytes(value);
name ||= getName(value) ?? "unknown_file";
if (!options.type) {
const type = (bits[0] as any)?.type;
if (typeof type === "string") {
options = { ...options, type };
}
}
return new File(bits, name, options);
}
function getName(value: any): string | undefined {
return (
getStringFromMaybeBuffer(value.name) ||
getStringFromMaybeBuffer(value.filename) ||
// For fs.ReadStream
getStringFromMaybeBuffer(value.path)?.split(/[\\/]/).pop()
);
}
const getStringFromMaybeBuffer = (x: string | Buffer | unknown): string | undefined => {
if (typeof x === "string") return x;
if (typeof Buffer !== "undefined" && x instanceof Buffer) return String(x);
return undefined;
};
async function getBytes(value: ToFileInput): Promise<Array<BlobPart>> {
let parts: Array<BlobPart> = [];
if (
typeof value === "string" ||
ArrayBuffer.isView(value) || // includes Uint8Array, Buffer, etc.
value instanceof ArrayBuffer
) {
parts.push(value);
} else if (isBlobLike(value)) {
parts.push(await value.arrayBuffer());
} else if (
isAsyncIterableIterator(value) // includes Readable, ReadableStream, etc.
) {
for await (const chunk of value) {
parts.push(chunk as BlobPart); // TODO, consider validating?
}
} else {
throw new Error(
`Unexpected data type: ${typeof value}; constructor: ${value?.constructor
?.name}; props: ${propsForError(value)}`
);
}
return parts;
}
function propsForError(value: any): string {
const props = Object.getOwnPropertyNames(value);
return `[${props.map((p) => `"${p}"`).join(", ")}]`;
}
const isAsyncIterableIterator = (value: any): value is AsyncIterableIterator<unknown> =>
value != null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function";
/**
* Intended to match web.Blob, node.Blob, node-fetch.Blob, etc.
*/
export interface BlobLike {
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */
readonly size: number;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */
readonly type: string;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */
text(): Promise<string>;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */
slice(start?: number, end?: number): BlobLike;
// unfortunately @types/node-fetch@^2.6.4 doesn't type the arrayBuffer method
}
/**
* Intended to match web.File, node.File, node-fetch.File, etc.
*/
export interface FileLike extends BlobLike {
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */
readonly lastModified: number;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */
readonly name: string;
}
/**
* Intended to match web.Response, node.Response, node-fetch.Response, etc.
*/
export interface ResponseLike {
url: string;
blob(): Promise<BlobLike>;
}
export type Uploadable = FileLike | ResponseLike | Readable;
export const isResponseLike = (value: any): value is ResponseLike =>
value != null &&
typeof value === "object" &&
typeof value.url === "string" &&
typeof value.blob === "function";
export const isFileLike = (value: any): value is FileLike =>
value != null &&
typeof value === "object" &&
typeof value.name === "string" &&
typeof value.lastModified === "number" &&
isBlobLike(value);
/**
* The BlobLike type omits arrayBuffer() because @types/node-fetch@^2.6.4 lacks it; but this check
* adds the arrayBuffer() method type because it is available and used at runtime
*/
export const isBlobLike = (
value: any
): value is BlobLike & { arrayBuffer(): Promise<ArrayBuffer> } =>
value != null &&
typeof value === "object" &&
typeof value.size === "number" &&
typeof value.type === "string" &&
typeof value.text === "function" &&
typeof value.slice === "function" &&
typeof value.arrayBuffer === "function";
export const isFsReadStream = (value: any): value is Readable => value instanceof Readable;
export const isUploadable = (value: any): value is Uploadable => {
return isFileLike(value) || isResponseLike(value) || isFsReadStream(value);
};
export type BlobLikePart =
| string
| ArrayBuffer
| ArrayBufferView
| BlobLike
| Uint8Array
| DataView;
export const isRecordLike = (value: any): value is Record<string, string> =>
value != null &&
typeof value === "object" &&
!Array.isArray(value) &&
Object.keys(value).length > 0 &&
Object.keys(value).every((key) => typeof key === "string" && typeof value[key] === "string");
/**
* A subclass of `Promise` providing additional helper methods
* for interacting with the SDK.
+9 -24
View File
@@ -14,7 +14,6 @@ import {
EnvironmentVariables,
ListRunResponseItem,
ListScheduleOptions,
ListSchedulesResult,
ReplayRunResponse,
RetrieveRunResponse,
ScheduleObject,
@@ -28,11 +27,9 @@ import { taskContext } from "../task-context-api";
import {
CursorPagePromise,
ZodFetchOptions,
isRecordLike,
zodfetch,
zodfetchCursorPage,
zodfetchOffsetLimitPage,
zodupload,
} from "./core";
import { ApiError } from "./errors";
import {
@@ -331,27 +328,15 @@ export class ApiClient {
}
importEnvVars(projectRef: string, slug: string, body: ImportEnvironmentVariablesParams) {
if (isRecordLike(body.variables)) {
return zodfetch(
EnvironmentVariableResponseBody,
`${this.baseUrl}/api/v1/projects/${projectRef}/envvars/${slug}/import`,
{
method: "POST",
headers: this.#getHeaders(false),
body: JSON.stringify(body),
}
);
} else {
return zodupload(
EnvironmentVariableResponseBody,
`${this.baseUrl}/api/v1/projects/${projectRef}/envvars/${slug}/import`,
body,
{
method: "POST",
headers: this.#getHeaders(false),
}
);
}
return zodfetch(
EnvironmentVariableResponseBody,
`${this.baseUrl}/api/v1/projects/${projectRef}/envvars/${slug}/import`,
{
method: "POST",
headers: this.#getHeaders(false),
body: JSON.stringify(body),
}
);
}
retrieveEnvVar(projectRef: string, slug: string, key: string) {
+2 -7
View File
@@ -1,18 +1,13 @@
import { RunStatus } from "../schemas";
import { BlobLikePart, Uploadable } from "./core";
import { CursorPageParams } from "./pagination";
export interface ImportEnvironmentVariablesParams {
/**
* The variables to be imported. If a variable with the same key already exists, it will be overwritten when `override` is `true`.
*
* There are two ways to specify the variables:
*
* 1. As a record of key-value pairs. e.g. `{ "key1": "value1", "key2": "value2" }`
* 2. As an "uploadable" object in dotenv format. An uploadable can be a Node readable stream, a string, or a Buffer. You can also pass the return value of a `fetch` call.
* To specify the variables, you can pass them in as a record of key-value pairs. e.g. `{ "key1": "value1", "key2": "value2" }`
*/
variables: Uploadable | BlobLikePart | Record<string, string>;
variables: Record<string, string>;
override?: boolean;
}
+2 -4
View File
@@ -197,13 +197,11 @@ export class TracingSDK {
}
public async flush() {
await this._traceProvider.forceFlush();
await this._logProvider.forceFlush();
await Promise.all([this._traceProvider.forceFlush(), this._logProvider.forceFlush()]);
}
public async shutdown() {
await this._traceProvider.shutdown();
await this._logProvider.shutdown();
await Promise.all([this._traceProvider.shutdown(), this._logProvider.shutdown()]);
}
}
+4
View File
@@ -554,6 +554,10 @@ export const CoordinatorToPlatformMessages = {
}),
]),
}),
callback: z.object({
version: z.literal("v1").default("v1"),
keepRunAlive: z.boolean(),
}),
},
INDEXING_FAILED: {
message: z.object({
+5 -1
View File
@@ -16,13 +16,17 @@ export const TaskRunExecutionPayload = z.object({
export type TaskRunExecutionPayload = z.infer<typeof TaskRunExecutionPayload>;
// **IMPORTANT NOTE**: If you change this schema, make sure it is backwards compatible with the previous version as this also used when a worker signals to the coordinator that a TaskRun is complete.
// Strategies for not breaking backwards compatibility:
// 1. Add new fields as optional
// 2. If a field is required, add a default value
export const ProdTaskRunExecution = TaskRunExecution.extend({
worker: z.object({
id: z.string(),
contentHash: z.string(),
version: z.string(),
}),
machine: MachinePreset,
machine: MachinePreset.default({ name: "small-1x", cpu: 1, memory: 1, centsPerMs: 0 }),
});
export type ProdTaskRunExecution = z.infer<typeof ProdTaskRunExecution>;
-1
View File
@@ -17,5 +17,4 @@ export default defineConfig({
"./src/v3/workers/index.ts",
"./src/v3/zodfetch.ts",
],
external: ["node:stream"],
});
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/eslint-plugin
## 3.0.0-beta.41
## 3.0.0-beta.40
## 3.0.0-beta.39
## 3.0.0-beta.38
## 3.0.0-beta.37
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/eslint-plugin",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "ESLint plugin with trigger.dev best practices",
"keywords": [
"eslint",
+19
View File
@@ -1,5 +1,24 @@
# @trigger.dev/express
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/express",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Official Express adapter for Trigger.dev",
"license": "MIT",
"main": "./dist/index.js",
@@ -23,7 +23,7 @@
"./package.json": "./package.json"
},
"devDependencies": {
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"@trigger.dev/tsconfig": "workspace:*",
"@types/debug": "^4.1.7",
"@types/express": "^4.17.13",
@@ -39,7 +39,7 @@
"build:tsup": "tsup"
},
"peerDependencies": {
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41"
},
"dependencies": {
"debug": "^4.3.4",
+19
View File
@@ -1,5 +1,24 @@
# @trigger.dev/hono
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/hono",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "A Trigger.dev adapter for Hono.dev",
"license": "MIT",
"main": "./dist/index.js",
@@ -32,7 +32,7 @@
},
"peerDependencies": {
"hono": "3.x",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41"
},
"devDependencies": {
"@trigger.dev/tsconfig": "workspace:*",
+21
View File
@@ -1,5 +1,26 @@
# @trigger.dev/integration-kit
## 3.0.0-beta.41
### Patch Changes
- @trigger.dev/core@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- Updated dependencies [55d1f8c67]
- Updated dependencies [098932ea9]
- Updated dependencies [9835f4ec5]
- @trigger.dev/core@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/core@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/integration-kit",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Trigger.dev Integration Kit has helpers to make creating integrations easier",
"license": "MIT",
"main": "./dist/index.js",
+19
View File
@@ -1,5 +1,24 @@
# @trigger.dev/nestjs
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/nestjs",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Official NestJS adapter for Trigger.dev",
"license": "MIT",
"main": "./dist/index.js",
@@ -23,7 +23,7 @@
"./package.json": "./package.json"
},
"devDependencies": {
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"@trigger.dev/tsconfig": "workspace:*",
"@types/debug": "^4.1.7",
"@types/express": "^4.17.13",
@@ -41,7 +41,7 @@
},
"peerDependencies": {
"@nestjs/common": ">=10.0.0",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41"
},
"dependencies": {
"@nestjs/common": "^10.2.4",
+19
View File
@@ -1,5 +1,24 @@
# @trigger.dev/nextjs
## 3.0.0-beta.41
### Patch Changes
- Updated dependencies [7c36a1a4b]
- @trigger.dev/sdk@3.0.0-beta.41
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.39
## 3.0.0-beta.38
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/nextjs",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "Trigger.dev Next.js integration",
"license": "MIT",
"main": "./dist/index.js",
@@ -41,7 +41,7 @@
"build:tsup": "tsup"
},
"peerDependencies": {
"@trigger.dev/sdk": "workspace:^3.0.0-beta.38",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.41",
"next": ">=12.0.0"
},
"dependencies": {
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/otlp-importer
## 3.0.0-beta.41
## 3.0.0-beta.40
## 3.0.0-beta.39
## 3.0.0-beta.38
## 3.0.0-beta.37
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/otlp-importer",
"version": "3.0.0-beta.38",
"version": "3.0.0-beta.41",
"description": "OpenTelemetry OTLP Importer for Node.js written in TypeScript",
"license": "MIT",
"main": "./dist/index.js",

Some files were not shown because too many files have changed in this diff Show More