Compare commits

...

17 Commits

Author SHA1 Message Date
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
106 changed files with 988 additions and 822 deletions
+3
View File
@@ -115,14 +115,17 @@
"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",
+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
+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,
+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);
}
+29
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`);
+2 -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:
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/airtable
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/airtable",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"airtable": "^0.12.1",
"zod": "3.22.3"
},
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/github
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/github",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"zod": "3.22.3"
},
"engines": {
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/linear
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/linear",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"zod": "3.22.3"
},
"engines": {
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/slack
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/openai",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.39"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40"
},
"engines": {
"node": ">=18.0.0"
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/plain
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/plain",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"@team-plain/typescript-sdk": "^2.7.0"
},
"engines": {
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/replicate
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/replicate",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"replicate": "^0.18.1",
"zod": "3.22.3"
},
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/resend
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/resend",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"resend": "^2.1.0"
},
"engines": {
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/sendgrid
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/sendgrid",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.39"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40"
},
"engines": {
"node": ">=16.8.0"
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/shopify
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/shopify",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40",
"zod": "3.22.3"
},
"engines": {
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/slack
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/slack",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"zod": "3.22.3"
},
"engines": {
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/stripe
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/stripe",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"stripe": "^12.14.0",
"zod": "3.22.3"
},
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/supabase
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/supabase",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"supabase-management-js": "^1.0.0",
"zod": "3.22.3"
},
+7
View File
@@ -1,5 +1,12 @@
# @trigger.dev/typeform
## 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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/typeform",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.39",
"@trigger.dev/integration-kit": "workspace:^3.0.0-beta.40",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"@typeform/api-client": "^1.8.0",
"zod": "3.22.3"
},
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/astro
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### 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.39",
"version": "3.0.0-beta.40",
"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.39"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40"
},
"devDependencies": {
"astro": "^3.0.12",
+11
View File
@@ -1,5 +1,16 @@
# trigger.dev
## 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
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "trigger.dev",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/core": "workspace:3.0.0-beta.40",
"@types/degit": "^2.8.3",
"chalk": "^5.2.0",
"chokidar": "^3.5.3",
+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,7 +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: 1_000,
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);
+10
View File
@@ -1,5 +1,15 @@
# create-trigger
## 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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/cli",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"description": "The Trigger.dev CLI",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
+2
View File
@@ -1,5 +1,7 @@
# @trigger.dev/core-apps
## 3.0.0-beta.40
## 3.0.0-beta.39
## 3.0.0-beta.38
+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.39",
"version": "3.0.0-beta.40",
"private": true,
"license": "MIT",
"main": "./dist/index.js",
+2
View File
@@ -1,5 +1,7 @@
# @trigger.dev/core-backend
## 3.0.0-beta.40
## 3.0.0-beta.39
## 3.0.0-beta.38
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/core-backend",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"description": "Core code used across `@trigger.dev/sdk` and Trigger.dev server",
"license": "MIT",
"main": "./dist/index.js",
+8
View File
@@ -1,5 +1,13 @@
# internal-platform
## 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
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/core",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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({
-1
View File
@@ -17,5 +17,4 @@ export default defineConfig({
"./src/v3/workers/index.ts",
"./src/v3/zodfetch.ts",
],
external: ["node:stream"],
});
+2
View File
@@ -1,5 +1,7 @@
# @trigger.dev/eslint-plugin
## 3.0.0-beta.40
## 3.0.0-beta.39
## 3.0.0-beta.38
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/eslint-plugin",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"description": "ESLint plugin with trigger.dev best practices",
"keywords": [
"eslint",
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/express
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/express",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"@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.39"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40"
},
"dependencies": {
"debug": "^4.3.4",
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/hono
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/hono",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40"
},
"devDependencies": {
"@trigger.dev/tsconfig": "workspace:*",
+9
View File
@@ -1,5 +1,14 @@
# @trigger.dev/integration-kit
## 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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/integration-kit",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"description": "Trigger.dev Integration Kit has helpers to make creating integrations easier",
"license": "MIT",
"main": "./dist/index.js",
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/nestjs
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/nestjs",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"@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.39"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40"
},
"dependencies": {
"@nestjs/common": "^10.2.4",
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/nextjs
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/nextjs",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"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.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"next": ">=12.0.0"
},
"dependencies": {
+2
View File
@@ -1,5 +1,7 @@
# @trigger.dev/otlp-importer
## 3.0.0-beta.40
## 3.0.0-beta.39
## 3.0.0-beta.38
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/otlp-importer",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"description": "OpenTelemetry OTLP Importer for Node.js written in TypeScript",
"license": "MIT",
"main": "./dist/index.js",
+9
View File
@@ -1,5 +1,14 @@
# @trigger.dev/react
## 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
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/react",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"description": "Trigger.dev React SDK",
"license": "MIT",
"types": "dist/index.d.ts",
@@ -27,7 +27,7 @@
},
"dependencies": {
"@tanstack/react-query": "5.0.0-beta.2",
"@trigger.dev/core": "workspace:^3.0.0-beta.39",
"@trigger.dev/core": "workspace:^3.0.0-beta.40",
"debug": "^4.3.4",
"zod": "3.22.3"
},
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/remix
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/remix",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"description": "Trigger.dev Remix integration",
"license": "MIT",
"main": "./dist/index.js",
@@ -39,7 +39,7 @@
"build:tsup": "tsup"
},
"peerDependencies": {
"@trigger.dev/sdk": "workspace:^3.0.0-beta.39",
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40",
"@remix-run/server-runtime": ">1.19.0"
},
"dependencies": {
+6
View File
@@ -1,5 +1,11 @@
# @trigger.dev/sveltekit
## 3.0.0-beta.40
### Patch Changes
- @trigger.dev/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/sveltekit",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"description": "Trigger.dev svelteKit integration",
"license": "MIT",
"main": "./dist/index.js",
@@ -39,7 +39,7 @@
"build:tsup": "tsup"
},
"peerDependencies": {
"@trigger.dev/sdk": "workspace:^3.0.0-beta.39"
"@trigger.dev/sdk": "workspace:^3.0.0-beta.40"
},
"dependencies": {
"debug": "^4.3.4"
+10
View File
@@ -1,5 +1,15 @@
# @trigger.dev/testing
## 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/sdk@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@trigger.dev/testing",
"description": "A collection of useful tools to write tests for Trigger.dev.",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
+10
View File
@@ -1,5 +1,15 @@
# @trigger.dev/sdk
## 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/core-backend@3.0.0-beta.40
## 3.0.0-beta.39
### Patch Changes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@trigger.dev/sdk",
"version": "3.0.0-beta.39",
"version": "3.0.0-beta.40",
"description": "trigger.dev Node.JS SDK",
"license": "MIT",
"main": "./dist/index.js",
@@ -48,8 +48,8 @@
"@opentelemetry/api": "^1.8.0",
"@opentelemetry/api-logs": "^0.48.0",
"@opentelemetry/semantic-conventions": "^1.22.0",
"@trigger.dev/core": "workspace:3.0.0-beta.39",
"@trigger.dev/core-backend": "workspace:3.0.0-beta.39",
"@trigger.dev/core": "workspace:3.0.0-beta.40",
"@trigger.dev/core-backend": "workspace:3.0.0-beta.40",
"chalk": "^5.2.0",
"cronstrue": "^2.21.0",
"debug": "^4.3.4",

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