From 9ecda816dc65c96e978d2f814649881f9f711444 Mon Sep 17 00:00:00 2001 From: Boon Kai Date: Mon, 4 Aug 2025 19:09:38 +0800 Subject: [PATCH 001/101] fix(cli): improve contrast for chalkWorker yellow in light mode (#2239) * fix(cli-output): improve color contrast for chalkWorker on light mode * add changeset --------- Co-authored-by: nicktrn <55853254+nicktrn@users.noreply.github.com> --- .changeset/giant-rivers-tease.md | 5 +++++ packages/cli-v3/src/utilities/cliOutput.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/giant-rivers-tease.md diff --git a/.changeset/giant-rivers-tease.md b/.changeset/giant-rivers-tease.md new file mode 100644 index 000000000..43ff6b942 --- /dev/null +++ b/.changeset/giant-rivers-tease.md @@ -0,0 +1,5 @@ +--- +"trigger.dev": patch +--- + +improve contrast for chalkWorker in light mode diff --git a/packages/cli-v3/src/utilities/cliOutput.ts b/packages/cli-v3/src/utilities/cliOutput.ts index 8b499c87c..7d950c40a 100644 --- a/packages/cli-v3/src/utilities/cliOutput.ts +++ b/packages/cli-v3/src/utilities/cliOutput.ts @@ -38,7 +38,7 @@ export function chalkLink(text: string) { } export function chalkWorker(text: string) { - return chalk.hex("#FFFF89")(text); + return chalk.yellowBright(text); } export function chalkTask(text: string) { From b98584364247ebdf7b029922ddf90e2bdc8fa04b Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 4 Aug 2025 13:13:03 +0100 Subject: [PATCH 002/101] Prevents long project menu titles pushing the admin buttons out of bounds (#2332) --- .../app/components/navigation/SideMenu.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/webapp/app/components/navigation/SideMenu.tsx b/apps/webapp/app/components/navigation/SideMenu.tsx index de7c045ad..20d2a821d 100644 --- a/apps/webapp/app/components/navigation/SideMenu.tsx +++ b/apps/webapp/app/components/navigation/SideMenu.tsx @@ -143,16 +143,18 @@ export function SideMenu({ >
- +
+ +
{isAdmin && !user.isImpersonating ? ( From 63857b0445a535361abdd171dea2669e34d3f818 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 4 Aug 2025 13:13:38 +0100 Subject: [PATCH 003/101] =?UTF-8?q?Makes=20it=20clear=20you=20only=20get?= =?UTF-8?q?=20preview=20branches=20if=20you=E2=80=99re=20on=20v4=20(#2343)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/webapp/app/components/BlankStatePanels.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/webapp/app/components/BlankStatePanels.tsx b/apps/webapp/app/components/BlankStatePanels.tsx index 03ebae66e..4dfe4045f 100644 --- a/apps/webapp/app/components/BlankStatePanels.tsx +++ b/apps/webapp/app/components/BlankStatePanels.tsx @@ -477,6 +477,10 @@ export function BranchesNoBranchableEnvironment() { Preview branches in Trigger.dev create isolated environments for testing new features before production. + + You must be on to access preview branches. Read our{" "} + upgrade to v4 guide to learn more. + ); } From df0dc0767806e3c70deffb8785d81043dbccd848 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 4 Aug 2025 13:17:06 +0100 Subject: [PATCH 004/101] New Task page deploy blank state (#2344) * Adds a step by step deploy blank state to Tasks page * Adds links to Ask AI, docs and troubleshooting --- .../app/components/BlankStatePanels.tsx | 87 ++++++++++++++----- apps/webapp/app/components/SetupCommands.tsx | 61 +++++++++++++ .../route.tsx | 3 +- 3 files changed, 126 insertions(+), 25 deletions(-) diff --git a/apps/webapp/app/components/BlankStatePanels.tsx b/apps/webapp/app/components/BlankStatePanels.tsx index 4dfe4045f..380a6d990 100644 --- a/apps/webapp/app/components/BlankStatePanels.tsx +++ b/apps/webapp/app/components/BlankStatePanels.tsx @@ -5,6 +5,7 @@ import { ChatBubbleLeftRightIcon, ClockIcon, PlusIcon, + QuestionMarkCircleIcon, RectangleGroupIcon, RectangleStackIcon, ServerStackIcon, @@ -12,7 +13,6 @@ import { } from "@heroicons/react/20/solid"; import { useLocation } from "react-use"; import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons"; -import { TaskIcon } from "~/assets/icons/TaskIcon"; import { WaitpointTokenIcon } from "~/assets/icons/WaitpointTokenIcon"; import openBulkActionsPanel from "~/assets/images/open-bulk-actions-panel.png"; import selectRunsIndividually from "~/assets/images/select-runs-individually.png"; @@ -32,8 +32,9 @@ import { v3NewProjectAlertPath, v3NewSchedulePath, } from "~/utils/pathBuilder"; +import { AskAI } from "./AskAI"; import { InlineCode } from "./code/InlineCode"; -import { environmentFullTitle } from "./environments/EnvironmentLabel"; +import { environmentFullTitle, EnvironmentIcon } from "./environments/EnvironmentLabel"; import { Feedback } from "./Feedback"; import { EnvironmentSelector } from "./navigation/EnvironmentSelector"; import { Button, LinkButton } from "./primitives/Buttons"; @@ -42,7 +43,13 @@ import { InfoPanel } from "./primitives/InfoPanel"; import { Paragraph } from "./primitives/Paragraph"; import { StepNumber } from "./primitives/StepNumber"; import { TextLink } from "./primitives/TextLink"; -import { InitCommandV3, PackageManagerProvider, TriggerDevStepV3 } from "./SetupCommands"; +import { SimpleTooltip } from "./primitives/Tooltip"; +import { + InitCommandV3, + PackageManagerProvider, + TriggerDeployStep, + TriggerDevStepV3, +} from "./SetupCommands"; import { StepContentContainer } from "./StepContentContainer"; import { V4Badge } from "./V4Badge"; @@ -87,26 +94,60 @@ export function HasNoTasksDev() { export function HasNoTasksDeployed({ environment }: { environment: MinimumEnvironment }) { return ( - - How to deploy tasks - - } - > - - Run the CLI deploy command to - deploy your tasks to the {environmentFullTitle(environment)} environment. - - + +
+
+
+ + Deploy your tasks to {environmentFullTitle(environment)} +
+
+ + } + content="Deploy docs" + /> + + } + content="Troubleshooting docs" + /> + +
+
+ + + + This will deploy your tasks to the {environmentFullTitle(environment)} environment. Read + the full guide. + + + + + + + Read the GitHub Actions guide to + get started. + + + + + This page will automatically refresh when your tasks are deployed. + +
+
); } diff --git a/apps/webapp/app/components/SetupCommands.tsx b/apps/webapp/app/components/SetupCommands.tsx index e68273a0d..accb2f65a 100644 --- a/apps/webapp/app/components/SetupCommands.tsx +++ b/apps/webapp/app/components/SetupCommands.tsx @@ -208,3 +208,64 @@ export function TriggerLoginStepV3({ title }: TabsProps) { ); } + +export function TriggerDeployStep({ title, environment }: TabsProps & { environment: { type: string } }) { + const triggerCliTag = useTriggerCliTag(); + const { activePackageManager, setActivePackageManager } = usePackageManager(); + + // Generate the environment flag based on environment type + const getEnvironmentFlag = () => { + switch (environment.type) { + case "STAGING": + return " --env staging"; + case "PREVIEW": + return " --env preview"; + case "PRODUCTION": + default: + return ""; + } + }; + + const environmentFlag = getEnvironmentFlag(); + + return ( + +
+ {title && {title}} + + npm + pnpm + yarn + +
+ + + + + + + + + +
+ ); +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx index 29efff3dc..5c702acb7 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx @@ -77,7 +77,6 @@ import { type TaskActivity, type TaskListItem, taskListPresenter, - TaskListPresenter, } from "~/presenters/v3/TaskListPresenter.server"; import { getUsefulLinksPreference, @@ -411,7 +410,7 @@ export default function Page() { ) : ( - + )} From f8126871f38677638eb5dfa15e72047e7f2b3aac Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 4 Aug 2025 13:18:07 +0100 Subject: [PATCH 005/101] Improves large output messaging (#2341) * Adds more information to the large payloads/outputs * Changes the Download logs button to secondary --- .../app/components/runs/v3/PacketDisplay.tsx | 29 +++++++++++++++---- .../route.tsx | 6 ++-- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/apps/webapp/app/components/runs/v3/PacketDisplay.tsx b/apps/webapp/app/components/runs/v3/PacketDisplay.tsx index 5d5878a93..8e30b2df7 100644 --- a/apps/webapp/app/components/runs/v3/PacketDisplay.tsx +++ b/apps/webapp/app/components/runs/v3/PacketDisplay.tsx @@ -1,7 +1,11 @@ import { CloudArrowDownIcon } from "@heroicons/react/20/solid"; import { CodeBlock } from "~/components/code/CodeBlock"; +import { InlineCode } from "~/components/code/InlineCode"; import { LinkButton } from "~/components/primitives/Buttons"; +import { Header3 } from "~/components/primitives/Headers"; import { Paragraph } from "~/components/primitives/Paragraph"; +import { TextLink } from "~/components/primitives/TextLink"; +import { docsPath } from "~/utils/pathBuilder"; export function PacketDisplay({ data, @@ -15,13 +19,26 @@ export function PacketDisplay({ switch (dataType) { case "application/store": { return ( -
- - {title} +
+ {title} + + This {title.toLowerCase()} exceeded the size limit and was automatically offloaded to + object storage. You can retrieve it using{" "} + runs.retrieve or download it directly + below. Learn more + . - - Download - +
+ + Download {title.toLowerCase()} + +
); } diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx index 7158c57b0..f68885c58 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx @@ -7,7 +7,6 @@ import { import { type LoaderFunctionArgs } from "@remix-run/server-runtime"; import { formatDurationMilliseconds, - MachinePresetName, type TaskRunError, taskRunErrorEnhancer, } from "@trigger.dev/core/v3"; @@ -804,14 +803,15 @@ function RunBody({ {run.isCached ? "Jump to original run" : "Focus on run"} )} +
-
{run.logsDeletedAt === null ? ( From 0fc234b97077d8bb2398c103d3a31fcba5dda139 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 4 Aug 2025 13:18:32 +0100 Subject: [PATCH 006/101] Fixes incorrect icon colors (#2339) --- .../route.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx index 5c702acb7..10f05e48c 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam._index/route.tsx @@ -371,12 +371,13 @@ export default function Page() { icon={RunsIcon} to={path} title="View runs" - leadingIconClassName="text-teal-500" + leadingIconClassName="text-runs" /> } From 792c97339e8d26290234aef2e79696f62df83bd7 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 4 Aug 2025 13:19:57 +0100 Subject: [PATCH 007/101] Force-wrap long strings in the run error callout (#2340) --- .../route.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx index f68885c58..6931df64c 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx @@ -855,7 +855,7 @@ function RunError({ error }: { error: TaskRunError }) { {name} {enhancedError.message && ( -
+              
                 {enhancedError.message}
               
From edafc71142e3af865037264d8e6f42fdaddf6a1f Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 4 Aug 2025 13:21:03 +0100 Subject: [PATCH 008/101] Explain run status on run inspector panel (#2342) --- .../route.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx index 6931df64c..846f2567b 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx @@ -45,7 +45,10 @@ import { RunTag } from "~/components/runs/v3/RunTag"; import { SpanEvents } from "~/components/runs/v3/SpanEvents"; import { SpanTitle } from "~/components/runs/v3/SpanTitle"; import { TaskRunAttemptStatusCombo } from "~/components/runs/v3/TaskRunAttemptStatus"; -import { TaskRunStatusCombo, TaskRunStatusReason } from "~/components/runs/v3/TaskRunStatus"; +import { + descriptionForTaskRunStatus, + TaskRunStatusCombo, +} from "~/components/runs/v3/TaskRunStatus"; import { WaitpointDetailTable } from "~/components/runs/v3/WaitpointDetails"; import { RuntimeIcon } from "~/components/RuntimeIcon"; import { WarmStartCombo } from "~/components/WarmStarts"; @@ -402,7 +405,10 @@ function RunBody({ Status - + } + content={descriptionForTaskRunStatus(run.status)} + /> @@ -766,9 +772,11 @@ function RunBody({
) : (
-
- - +
+ } + content={descriptionForTaskRunStatus(run.status)} + />
From bb54e978063cafc044290e795acc489133946de0 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 4 Aug 2025 13:21:30 +0100 Subject: [PATCH 009/101] Show text wrapping on code blocks (#2338) * Show text wrapping on deployment code blocks * Show textWrapping on some more CodeBlocks * Adds missing margins to the error block --- apps/webapp/app/components/runs/v3/DeploymentError.tsx | 4 +++- apps/webapp/app/components/runs/v3/PacketDisplay.tsx | 1 + .../route.tsx | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/webapp/app/components/runs/v3/DeploymentError.tsx b/apps/webapp/app/components/runs/v3/DeploymentError.tsx index 517f91789..11ee62dfa 100644 --- a/apps/webapp/app/components/runs/v3/DeploymentError.tsx +++ b/apps/webapp/app/components/runs/v3/DeploymentError.tsx @@ -9,7 +9,7 @@ type DeploymentErrorProps = { export function DeploymentError({ errorData }: DeploymentErrorProps) { return ( -
+
{errorData.message && {errorData.message}} {errorData.stack && ( @@ -18,6 +18,7 @@ export function DeploymentError({ errorData }: DeploymentErrorProps) { showLineNumbers={false} code={errorData.stack} maxLines={20} + showTextWrapping /> )} {errorData.stderr && ( @@ -28,6 +29,7 @@ export function DeploymentError({ errorData }: DeploymentErrorProps) { showLineNumbers={false} code={errorData.stderr} maxLines={20} + showTextWrapping /> )} diff --git a/apps/webapp/app/components/runs/v3/PacketDisplay.tsx b/apps/webapp/app/components/runs/v3/PacketDisplay.tsx index 8e30b2df7..24a9b66b6 100644 --- a/apps/webapp/app/components/runs/v3/PacketDisplay.tsx +++ b/apps/webapp/app/components/runs/v3/PacketDisplay.tsx @@ -50,6 +50,7 @@ export function PacketDisplay({ code={data} maxLines={20} showLineNumbers={false} + showTextWrapping /> ); } diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx index 846f2567b..1559b2c29 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx @@ -758,12 +758,12 @@ function RunBody({
) : tab === "context" ? (
- +
) : tab === "metadata" ? (
{run.metadata ? ( - + ) : ( No metadata set for this run. View our metadata documentation to learn more. From be70f42bec8d1f8a26aea47d4ed3b5daa0827724 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 4 Aug 2025 13:22:18 +0100 Subject: [PATCH 010/101] Adds a link to the reduce spend docs page from the billing alerts page (#2345) * Adds a link to the reduce spend docs page from the billing alerts page * Copy tweak --- .../route.tsx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-alerts/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-alerts/route.tsx index c4d3327be..4dd0352f8 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-alerts/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-alerts/route.tsx @@ -2,6 +2,7 @@ import { conform, list, requestIntent, useFieldList, useForm } from "@conform-to import { parse } from "@conform-to/zod"; import { Form, useActionData, type MetaFunction } from "@remix-run/react"; import { json, type ActionFunction, type LoaderFunctionArgs } from "@remix-run/server-runtime"; +import { tryCatch } from "@trigger.dev/core"; import { Fragment, useEffect, useRef, useState } from "react"; import { redirect, typedjson, useTypedLoaderData } from "remix-typedjson"; import { z } from "zod"; @@ -22,6 +23,7 @@ import { InputGroup } from "~/components/primitives/InputGroup"; import { Label } from "~/components/primitives/Label"; import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader"; import { Paragraph } from "~/components/primitives/Paragraph"; +import { TextLink } from "~/components/primitives/TextLink"; import { prisma } from "~/db.server"; import { featuresForRequest } from "~/features.server"; import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server"; @@ -29,12 +31,12 @@ import { getBillingAlerts, setBillingAlert } from "~/services/platform.v3.server import { requireUserId } from "~/services/session.server"; import { formatCurrency } from "~/utils/numberFormatter"; import { + docsPath, OrganizationParamsSchema, organizationPath, v3BillingAlertsPath, } from "~/utils/pathBuilder"; import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route"; -import { tryCatch } from "@trigger.dev/core"; export const meta: MetaFunction = () => { return [ @@ -199,10 +201,17 @@ export default function Page() {
- Billing alerts - - Receive an email when your compute spend crosses different thresholds. - +
+ Billing alerts + + Receive an email when your compute spend crosses different thresholds. You can also + learn how to{" "} + + reduce your compute spend + + . + +
From 7bb88451be74c04ea787e86f9a5f55cfb992f92b Mon Sep 17 00:00:00 2001 From: Dan <8297864+D-K-P@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:08:44 +0100 Subject: [PATCH 011/101] Sidebar fix (#2349) * Commented out tooltip style * Deleted stylesheet --- docs/style.css | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 docs/style.css diff --git a/docs/style.css b/docs/style.css deleted file mode 100644 index f41ac4821..000000000 --- a/docs/style.css +++ /dev/null @@ -1,17 +0,0 @@ -/* Tooltip style on the code block */ -div[class="absolute top-11 left-1/2 transform -translate-x-1/2 -translate-y-1/2 hidden group-hover:block text-white rounded-lg px-1.5 py-0.5 text-xs bg-primary-dark"] { - color:#1A1B1F; - border-radius: 0.25rem; -} - -/* Link anchor active state - icon container */ -div[class="mr-4 rounded-md p-1"] { - border-radius: 0.25rem; - border: 1px solid #464748; -} - -/* Link anchor default state - icon container */ -div[class="mr-4 rounded-md p-1 zinc-box group-hover:brightness-100 group-hover:ring-0 ring-1 ring-gray-950/5 dark:ring-gray-700/40"] { - border-radius: 0.25rem; - border: 1px solid transparent; -} \ No newline at end of file From 1f4434035e11d336e8cb793d4cc7bff3097bdd01 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 6 Aug 2025 08:10:46 +0100 Subject: [PATCH 012/101] latest @opentelemetry packages and correlate external traces (#2334) --- .changeset/five-nails-whisper.md | 92 +++ .../app/presenters/v3/SpanPresenter.server.ts | 29 +- .../routes/api.v1.tasks.$taskId.trigger.ts | 15 +- apps/webapp/app/routes/api.v2.tasks.batch.ts | 7 +- .../route.tsx | 6 + .../runEngine/services/batchTrigger.server.ts | 2 +- .../runEngine/services/triggerTask.server.ts | 59 +- apps/webapp/app/runEngine/types.ts | 4 +- .../app/v3/environmentVariableRules.server.ts | 2 - .../environmentVariablesRepository.server.ts | 102 ++- apps/webapp/app/v3/eventRepository.server.ts | 62 +- .../app/v3/services/triggerTask.server.ts | 2 +- .../test/environmentVariableRules.test.ts | 44 +- .../run-engine/src/engine/types.ts | 3 +- packages/cli-v3/package.json | 18 +- packages/cli-v3/src/cli/common.ts | 93 ++- .../cli-v3/src/entryPoints/dev-run-worker.ts | 22 +- .../src/entryPoints/managed-run-worker.ts | 23 +- packages/cli-v3/src/telemetry/tracing.ts | 75 --- packages/cli-v3/src/utilities/session.ts | 78 +-- packages/core/package.json | 21 +- packages/core/src/v3/apiClient/core.ts | 6 +- packages/core/src/v3/index.ts | 1 + packages/core/src/v3/isomorphic/index.ts | 1 + .../core/src/v3/isomorphic/traceContext.ts | 29 + packages/core/src/v3/otel/tracingSDK.ts | 299 +++++---- packages/core/src/v3/schemas/schemas.ts | 13 + .../core/src/v3/taskContext/otelProcessors.ts | 13 +- packages/core/src/v3/trace-context-api.ts | 5 + packages/core/src/v3/traceContext/api.ts | 74 +++ packages/core/src/v3/traceContext/manager.ts | 77 +++ packages/core/src/v3/traceContext/types.ts | 15 + packages/core/src/v3/tracer.ts | 4 - packages/core/src/v3/utils/globals.ts | 2 + packages/core/src/v3/workers/index.ts | 1 + packages/core/src/v3/workers/taskExecutor.ts | 29 +- packages/core/test/taskExecutor.test.ts | 2 +- packages/trigger-sdk/package.json | 3 +- packages/trigger-sdk/src/v3/index.ts | 1 + packages/trigger-sdk/src/v3/otel.ts | 7 + pnpm-lock.yaml | 600 +++++++++++------- references/d3-chat/package.json | 9 +- .../src/app/api/demo-batch-trigger/route.ts | 18 + .../app/api/demo-call-from-trigger/route.ts | 5 + .../d3-chat/src/app/api/demo-trigger/route.ts | 14 + references/d3-chat/src/instrumentation.ts | 5 + references/d3-chat/src/trigger/chat.ts | 25 +- 47 files changed, 1294 insertions(+), 723 deletions(-) create mode 100644 .changeset/five-nails-whisper.md delete mode 100644 packages/cli-v3/src/telemetry/tracing.ts create mode 100644 packages/core/src/v3/isomorphic/traceContext.ts create mode 100644 packages/core/src/v3/trace-context-api.ts create mode 100644 packages/core/src/v3/traceContext/api.ts create mode 100644 packages/core/src/v3/traceContext/manager.ts create mode 100644 packages/core/src/v3/traceContext/types.ts create mode 100644 packages/trigger-sdk/src/v3/otel.ts create mode 100644 references/d3-chat/src/app/api/demo-batch-trigger/route.ts create mode 100644 references/d3-chat/src/app/api/demo-call-from-trigger/route.ts create mode 100644 references/d3-chat/src/app/api/demo-trigger/route.ts create mode 100644 references/d3-chat/src/instrumentation.ts diff --git a/.changeset/five-nails-whisper.md b/.changeset/five-nails-whisper.md new file mode 100644 index 000000000..185f4a0cf --- /dev/null +++ b/.changeset/five-nails-whisper.md @@ -0,0 +1,92 @@ +--- +"@trigger.dev/sdk": patch +--- + +External Trace Correlation & OpenTelemetry Package Updates. + +| Package | Previous Version | New Version | Change Type | +|---------|------------------|-------------|-------------| +| `@opentelemetry/api` | 1.9.0 | 1.9.0 | No change (stable API) | +| `@opentelemetry/api-logs` | 0.52.1 | 0.203.0 | Major update | +| `@opentelemetry/core` | - | 2.0.1 | New dependency | +| `@opentelemetry/exporter-logs-otlp-http` | 0.52.1 | 0.203.0 | Major update | +| `@opentelemetry/exporter-trace-otlp-http` | 0.52.1 | 0.203.0 | Major update | +| `@opentelemetry/instrumentation` | 0.52.1 | 0.203.0 | Major update | +| `@opentelemetry/instrumentation-fetch` | 0.52.1 | 0.203.0 | Major update | +| `@opentelemetry/resources` | 1.25.1 | 2.0.1 | Major update | +| `@opentelemetry/sdk-logs` | 0.52.1 | 0.203.0 | Major update | +| `@opentelemetry/sdk-node` | 0.52.1 | - | Removed (functionality consolidated) | +| `@opentelemetry/sdk-trace-base` | 1.25.1 | 2.0.1 | Major update | +| `@opentelemetry/sdk-trace-node` | 1.25.1 | 2.0.1 | Major update | +| `@opentelemetry/semantic-conventions` | 1.25.1 | 1.36.0 | Minor update | + +### External trace correlation and propagation + +We will now correlate your external traces with trigger.dev traces and logs when using our external exporters: + +```ts +import { defineConfig } from "@trigger.dev/sdk"; +import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http"; +import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; + +export default defineConfig({ + project: process.env.TRIGGER_PROJECT_REF, + dirs: ["./src/trigger"], + telemetry: { + logExporters: [ + new OTLPLogExporter({ + url: "https://api.axiom.co/v1/logs", + headers: { + Authorization: `Bearer ${process.env.AXIOM_TOKEN}`, + "X-Axiom-Dataset": "test", + }, + }), + ], + exporters: [ + new OTLPTraceExporter({ + url: "https://api.axiom.co/v1/traces", + headers: { + Authorization: `Bearer ${process.env.AXIOM_TOKEN}`, + "X-Axiom-Dataset": "test", + }, + }), + ], + }, + maxDuration: 3600, +}); +``` + +You can also now propagate your external trace context when calling back into your own backend infra from inside a trigger.dev task: + +```ts +import { otel, task } from "@trigger.dev/sdk"; +import { context, propagation } from "@opentelemetry/api"; + +async function callNextjsApp() { + return await otel.withExternalTrace(async () => { + const headersObject = {}; + + // Now context.active() refers to your external trace context + propagation.inject(context.active(), headersObject); + + const result = await fetch("http://localhost:3000/api/demo-call-from-trigger", { + headers: new Headers(headersObject), + method: "POST", + body: JSON.stringify({ + message: "Hello from Trigger.dev", + }), + }); + + return result.json(); + }); +} + +export const myTask = task({ + id: "my-task", + run: async (payload: any) => { + await callNextjsApp() + } +}) +``` + + diff --git a/apps/webapp/app/presenters/v3/SpanPresenter.server.ts b/apps/webapp/app/presenters/v3/SpanPresenter.server.ts index 0d9065e28..a3a574784 100644 --- a/apps/webapp/app/presenters/v3/SpanPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/SpanPresenter.server.ts @@ -4,9 +4,10 @@ import { SemanticInternalAttributes, TaskRunContext, TaskRunError, + TriggerTraceContext, V3TaskRunContext, } from "@trigger.dev/core/v3"; -import { AttemptId, getMaxDuration } from "@trigger.dev/core/v3/isomorphic"; +import { AttemptId, getMaxDuration, parseTraceparent } from "@trigger.dev/core/v3/isomorphic"; import { RUNNING_STATUSES } from "~/components/runs/v3/TaskRunStatus"; import { logger } from "~/services/logger.server"; import { eventRepository, rehydrateAttribute } from "~/v3/eventRepository.server"; @@ -173,6 +174,8 @@ export class SpanPresenter extends BasePresenter { const context = await this.#getTaskRunContext({ run, machine: machine ?? undefined }); + const externalTraceId = this.#getExternalTraceId(run.traceContext); + return { id: run.id, friendlyId: run.friendlyId, @@ -234,6 +237,7 @@ export class SpanPresenter extends BasePresenter { spanId: run.spanId, isCached: !!span.originalRun, machinePreset: machine?.name, + externalTraceId, }; } @@ -272,6 +276,7 @@ export class SpanPresenter extends BasePresenter { id: true, spanId: true, traceId: true, + traceContext: true, //metadata number: true, taskIdentifier: true, @@ -574,4 +579,26 @@ export class SpanPresenter extends BasePresenter { async #getV4TaskRunContext({ run }: { run: FindRunResult }): Promise { return engine.resolveTaskRunContext(run.id); } + + #getExternalTraceId(traceContext: unknown) { + if (!traceContext) { + return; + } + + const parsedTraceContext = TriggerTraceContext.safeParse(traceContext); + + if (!parsedTraceContext.success) { + return; + } + + const externalTraceparent = parsedTraceContext.data.external?.traceparent; + + if (!externalTraceparent) { + return; + } + + const parsedTraceparent = parseTraceparent(externalTraceparent); + + return parsedTraceparent?.traceId; + } } diff --git a/apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts b/apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts index a5e4e1622..11613427a 100644 --- a/apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts +++ b/apps/webapp/app/routes/api.v1.tasks.$taskId.trigger.ts @@ -93,10 +93,9 @@ const { action, loader } = createActionApiRoute( const service = new TriggerTaskService(); try { - const traceContext = - traceparent && isFromWorker /// If the request is from a worker, we should pass the trace context - ? { traceparent, tracestate } - : undefined; + const traceContext = isFromWorker + ? { traceparent, tracestate } + : { external: { traceparent, tracestate } }; const oneTimeUseToken = await getOneTimeUseToken(authentication); @@ -111,6 +110,14 @@ const { action, loader } = createActionApiRoute( traceContext, }); + logger.debug("[otelContext]", { + taskId: params.taskId, + headers, + options: body.options, + isFromWorker, + traceContext, + }); + const idempotencyKeyExpiresAt = resolveIdempotencyKeyTTL(idempotencyKeyTTL); const result = await service.call( diff --git a/apps/webapp/app/routes/api.v2.tasks.batch.ts b/apps/webapp/app/routes/api.v2.tasks.batch.ts index ba03919d4..93715bbdd 100644 --- a/apps/webapp/app/routes/api.v2.tasks.batch.ts +++ b/apps/webapp/app/routes/api.v2.tasks.batch.ts @@ -103,10 +103,9 @@ const { action, loader } = createActionApiRoute( return cachedResponse; } - const traceContext = - traceparent && isFromWorker // If the request is from a worker, we should pass the trace context - ? { traceparent, tracestate } - : undefined; + const traceContext = isFromWorker + ? { traceparent, tracestate } + : { external: { traceparent, tracestate } }; const service = new RunEngineBatchTriggerService(batchProcessingStrategy ?? undefined); diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx index 1559b2c29..4e232b888 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx @@ -743,6 +743,12 @@ function RunBody({ Run Engine {run.engine} + {run.externalTraceId && ( + + External Trace ID + {run.externalTraceId} + + )} {isAdmin && (
diff --git a/apps/webapp/app/runEngine/services/batchTrigger.server.ts b/apps/webapp/app/runEngine/services/batchTrigger.server.ts index 2a476e5a1..beadcc9cf 100644 --- a/apps/webapp/app/runEngine/services/batchTrigger.server.ts +++ b/apps/webapp/app/runEngine/services/batchTrigger.server.ts @@ -42,7 +42,7 @@ export type BatchProcessingOptions = z.infer; export type BatchTriggerTaskServiceOptions = { triggerVersion?: string; - traceContext?: Record; + traceContext?: Record>; spanParentAsLink?: boolean; oneTimeUseToken?: string; }; diff --git a/apps/webapp/app/runEngine/services/triggerTask.server.ts b/apps/webapp/app/runEngine/services/triggerTask.server.ts index 3847a3036..ea41c12c7 100644 --- a/apps/webapp/app/runEngine/services/triggerTask.server.ts +++ b/apps/webapp/app/runEngine/services/triggerTask.server.ts @@ -10,8 +10,14 @@ import { taskRunErrorEnhancer, taskRunErrorToString, TriggerTaskRequestBody, + TriggerTraceContext, } from "@trigger.dev/core/v3"; -import { RunId, stringifyDuration } from "@trigger.dev/core/v3/isomorphic"; +import { + parseTraceparent, + RunId, + serializeTraceparent, + stringifyDuration, +} from "@trigger.dev/core/v3/isomorphic"; import type { PrismaClientOrTransaction } from "@trigger.dev/database"; import { createTags } from "~/models/taskRunTag.server"; import type { AuthenticatedEnvironment } from "~/services/apiAuth.server"; @@ -253,7 +259,11 @@ export class RunEngineTriggerTaskService { payload: payloadPacket.data ?? "", payloadType: payloadPacket.dataType, context: body.context, - traceContext: event.traceContext, + traceContext: this.#propagateExternalTraceContext( + event.traceContext, + parentRun?.traceContext, + event.traceparent?.spanId + ), traceId: event.traceId, spanId: event.spanId, parentSpanId: @@ -341,4 +351,49 @@ export class RunEngineTriggerTaskService { } }); } + + #propagateExternalTraceContext( + traceContext: Record, + parentRunTraceContext: unknown, + parentSpanId: string | undefined + ): TriggerTraceContext { + if (!parentRunTraceContext) { + return traceContext; + } + + const parsedParentRunTraceContext = TriggerTraceContext.safeParse(parentRunTraceContext); + + if (!parsedParentRunTraceContext.success) { + return traceContext; + } + + const { external } = parsedParentRunTraceContext.data; + + if (!external) { + return traceContext; + } + + if (!external.traceparent) { + return traceContext; + } + + const parsedTraceparent = parseTraceparent(external.traceparent); + + if (!parsedTraceparent) { + return traceContext; + } + + const newExternalTraceparent = serializeTraceparent( + parsedTraceparent.traceId, + parentSpanId ?? parsedTraceparent.spanId + ); + + return { + ...traceContext, + external: { + ...external, + traceparent: newExternalTraceparent, + }, + }; + } } diff --git a/apps/webapp/app/runEngine/types.ts b/apps/webapp/app/runEngine/types.ts index 9523999d5..3564a5d71 100644 --- a/apps/webapp/app/runEngine/types.ts +++ b/apps/webapp/app/runEngine/types.ts @@ -12,7 +12,7 @@ export type TriggerTaskServiceOptions = { idempotencyKey?: string; idempotencyKeyExpiresAt?: Date; triggerVersion?: string; - traceContext?: Record; + traceContext?: Record; spanParentAsLink?: boolean; parentAsLinkType?: "replay" | "trigger"; batchId?: string; @@ -119,7 +119,7 @@ export interface TriggerTaskValidator { export type TracedEventSpan = { traceId: string; spanId: string; - traceContext: Record; + traceContext: Record; traceparent?: { traceId: string; spanId: string; diff --git a/apps/webapp/app/v3/environmentVariableRules.server.ts b/apps/webapp/app/v3/environmentVariableRules.server.ts index ddaee2b24..fce17d036 100644 --- a/apps/webapp/app/v3/environmentVariableRules.server.ts +++ b/apps/webapp/app/v3/environmentVariableRules.server.ts @@ -8,8 +8,6 @@ type VariableRule = const blacklistedVariables: VariableRule[] = [ { type: "exact", key: "TRIGGER_SECRET_KEY" }, { type: "exact", key: "TRIGGER_API_URL" }, - { type: "prefix", prefix: "OTEL_" }, - { type: "whitelist", key: "OTEL_LOG_LEVEL" }, ]; export function removeBlacklistedVariables( diff --git a/apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts b/apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts index ea6243f4a..12d0a1236 100644 --- a/apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts +++ b/apps/webapp/app/v3/environmentVariables/environmentVariablesRepository.server.ts @@ -17,6 +17,7 @@ import { } from "./repository"; import { removeBlacklistedVariables } from "../environmentVariableRules.server"; import { deduplicateVariableArray } from "../deduplicateVariableArray.server"; +import { logger } from "~/services/logger.server"; function secretKeyProjectPrefix(projectId: string) { return `environmentvariable:${projectId}:`; @@ -837,11 +838,23 @@ export async function resolveVariablesForEnvironment( ? await resolveBuiltInDevVariables(runtimeEnvironment) : await resolveBuiltInProdVariables(runtimeEnvironment, parentEnvironment); - return deduplicateVariableArray([ + const overridableOtelVariables = + runtimeEnvironment.type === "DEVELOPMENT" + ? await resolveOverridableOtelDevVariables(runtimeEnvironment) + : []; + + const result = deduplicateVariableArray([ ...overridableTriggerVariables, + ...overridableOtelVariables, ...projectSecrets, ...builtInVariables, ]); + + logger.debug("Resolved variables", { + result, + }); + + return result; } async function resolveOverridableTriggerVariables( @@ -860,7 +873,7 @@ async function resolveOverridableTriggerVariables( async function resolveBuiltInDevVariables(runtimeEnvironment: RuntimeEnvironmentForEnvRepo) { let result: Array = [ { - key: "OTEL_EXPORTER_OTLP_ENDPOINT", + key: "TRIGGER_OTEL_EXPORTER_OTLP_ENDPOINT", value: env.DEV_OTEL_EXPORTER_OTLP_ENDPOINT ?? `${env.APP_ORIGIN.replace(/\/$/, "")}/otel`, }, { @@ -875,6 +888,42 @@ async function resolveBuiltInDevVariables(runtimeEnvironment: RuntimeEnvironment if (env.DEV_OTEL_BATCH_PROCESSING_ENABLED === "1") { result = result.concat([ + { + key: "TRIGGER_OTEL_BATCH_PROCESSING_ENABLED", + value: "1", + }, + { + key: "TRIGGER_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE", + value: env.DEV_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE, + }, + { + key: "TRIGGER_OTEL_SPAN_SCHEDULED_DELAY_MILLIS", + value: env.DEV_OTEL_SPAN_SCHEDULED_DELAY_MILLIS, + }, + { + key: "TRIGGER_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS", + value: env.DEV_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS, + }, + { + key: "TRIGGER_OTEL_SPAN_MAX_QUEUE_SIZE", + value: env.DEV_OTEL_SPAN_MAX_QUEUE_SIZE, + }, + { + key: "TRIGGER_OTEL_LOG_MAX_EXPORT_BATCH_SIZE", + value: env.DEV_OTEL_LOG_MAX_EXPORT_BATCH_SIZE, + }, + { + key: "TRIGGER_OTEL_LOG_SCHEDULED_DELAY_MILLIS", + value: env.DEV_OTEL_LOG_SCHEDULED_DELAY_MILLIS, + }, + { + key: "TRIGGER_OTEL_LOG_EXPORT_TIMEOUT_MILLIS", + value: env.DEV_OTEL_LOG_EXPORT_TIMEOUT_MILLIS, + }, + { + key: "TRIGGER_OTEL_LOG_MAX_QUEUE_SIZE", + value: env.DEV_OTEL_LOG_MAX_QUEUE_SIZE, + }, { key: "OTEL_BATCH_PROCESSING_ENABLED", value: "1", @@ -919,6 +968,19 @@ async function resolveBuiltInDevVariables(runtimeEnvironment: RuntimeEnvironment return [...result, ...commonVariables]; } +async function resolveOverridableOtelDevVariables( + runtimeEnvironment: RuntimeEnvironmentForEnvRepo +) { + let result: Array = [ + { + key: "OTEL_EXPORTER_OTLP_ENDPOINT", + value: env.DEV_OTEL_EXPORTER_OTLP_ENDPOINT ?? `${env.APP_ORIGIN.replace(/\/$/, "")}/otel`, + }, + ]; + + return result; +} + async function resolveBuiltInProdVariables( runtimeEnvironment: RuntimeEnvironmentForEnvRepo, parentEnvironment?: RuntimeEnvironmentForEnvRepo @@ -957,6 +1019,42 @@ async function resolveBuiltInProdVariables( if (env.PROD_OTEL_BATCH_PROCESSING_ENABLED === "1") { result = result.concat([ + { + key: "TRIGGER_OTEL_BATCH_PROCESSING_ENABLED", + value: "1", + }, + { + key: "TRIGGER_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE", + value: env.PROD_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE, + }, + { + key: "TRIGGER_OTEL_SPAN_SCHEDULED_DELAY_MILLIS", + value: env.PROD_OTEL_SPAN_SCHEDULED_DELAY_MILLIS, + }, + { + key: "TRIGGER_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS", + value: env.PROD_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS, + }, + { + key: "TRIGGER_OTEL_SPAN_MAX_QUEUE_SIZE", + value: env.PROD_OTEL_SPAN_MAX_QUEUE_SIZE, + }, + { + key: "TRIGGER_OTEL_LOG_MAX_EXPORT_BATCH_SIZE", + value: env.PROD_OTEL_LOG_MAX_EXPORT_BATCH_SIZE, + }, + { + key: "TRIGGER_OTEL_LOG_SCHEDULED_DELAY_MILLIS", + value: env.PROD_OTEL_LOG_SCHEDULED_DELAY_MILLIS, + }, + { + key: "TRIGGER_OTEL_LOG_EXPORT_TIMEOUT_MILLIS", + value: env.PROD_OTEL_LOG_EXPORT_TIMEOUT_MILLIS, + }, + { + key: "TRIGGER_OTEL_LOG_MAX_QUEUE_SIZE", + value: env.PROD_OTEL_LOG_MAX_QUEUE_SIZE, + }, { key: "OTEL_BATCH_PROCESSING_ENABLED", value: "1", diff --git a/apps/webapp/app/v3/eventRepository.server.ts b/apps/webapp/app/v3/eventRepository.server.ts index dc4fdd43e..d70deb3ef 100644 --- a/apps/webapp/app/v3/eventRepository.server.ts +++ b/apps/webapp/app/v3/eventRepository.server.ts @@ -2,9 +2,14 @@ import { Attributes, AttributeValue, Link, trace, TraceFlags, Tracer } from "@op import { RandomIdGenerator } from "@opentelemetry/sdk-trace-base"; import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; import { + correctErrorStackTrace, + createPacketAttributesAsJson, ExceptionEventProperties, ExceptionSpanEvent, + flattenAttributes, + isExceptionSpanEvent, NULL_SENTINEL, + omit, PRIMARY_VARIANT, SemanticInternalAttributes, SpanEvent, @@ -13,28 +18,24 @@ import { TaskEventEnvironment, TaskEventStyle, TaskRunError, - correctErrorStackTrace, - createPacketAttributesAsJson, - flattenAttributes, - isExceptionSpanEvent, - omit, unflattenAttributes, } from "@trigger.dev/core/v3"; +import { parseTraceparent, serializeTraceparent } from "@trigger.dev/core/v3/isomorphic"; import { Prisma, TaskEvent, TaskEventKind, TaskEventStatus } from "@trigger.dev/database"; +import { nanoid } from "nanoid"; import { createHash } from "node:crypto"; import { EventEmitter } from "node:stream"; import { Gauge } from "prom-client"; -import { $replica, PrismaClient, PrismaReplicaClient, prisma } from "~/db.server"; +import { $replica, prisma, PrismaClient, PrismaReplicaClient } from "~/db.server"; import { env } from "~/env.server"; import { metricsRegister } from "~/metrics.server"; +import { createRedisClient, RedisClient, RedisWithClusterOptions } from "~/redis.server"; import { logger } from "~/services/logger.server"; import { singleton } from "~/utils/singleton"; import { DynamicFlushScheduler } from "./dynamicFlushScheduler.server"; -import { startActiveSpan } from "./tracer.server"; -import { createRedisClient, RedisClient, RedisWithClusterOptions } from "~/redis.server"; -import { startSpan } from "./tracing.server"; -import { nanoid } from "nanoid"; import { TaskEventStore, TaskEventStoreTable } from "./taskEventStore.server"; +import { startActiveSpan } from "./tracer.server"; +import { startSpan } from "./tracing.server"; const MAX_FLUSH_DEPTH = 5; @@ -80,7 +81,7 @@ export type SetAttribute = (key: keyof T, value: T[ke export type TraceEventOptions = { kind?: CreatableEventKind; - context?: Record; + context?: Record; spanParentAsLink?: boolean; parentAsLinkType?: "trigger" | "replay"; spanIdSeed?: string; @@ -932,7 +933,7 @@ export class EventRepository { traceId, spanId, parentId, - tracestate, + tracestate: typeof tracestate === "string" ? tracestate : undefined, message: message, serviceName: "api server", serviceNamespace: "trigger.dev", @@ -989,6 +990,11 @@ export class EventRepository { ): Promise { const propagatedContext = extractContextFromCarrier(options.context ?? {}); + logger.debug("[otelContext]", { + propagatedContext, + options, + }); + const start = process.hrtime.bigint(); const startTime = options.startTime ?? getNowInNanoseconds(); @@ -1002,7 +1008,8 @@ export class EventRepository { : this.generateSpanId(); const traceContext = { - traceparent: `00-${traceId}-${spanId}-01`, + ...options.context, + traceparent: serializeTraceparent(traceId, spanId), }; const links: Link[] = @@ -1087,7 +1094,7 @@ export class EventRepository { traceId, spanId, parentId, - tracestate, + tracestate: typeof tracestate === "string" ? tracestate : undefined, duration: options.incomplete ? 0 : duration, isPartial: failedWithError ? false : options.incomplete, isError: !!failedWithError, @@ -1486,36 +1493,21 @@ function excludePartialEventsWithCorrespondingFullEvent(batch: CreatableEvent[]) ); } -export function extractContextFromCarrier(carrier: Record) { +export function extractContextFromCarrier(carrier: Record) { const traceparent = carrier["traceparent"]; const tracestate = carrier["tracestate"]; + if (typeof traceparent !== "string") { + return undefined; + } + return { + ...carrier, traceparent: parseTraceparent(traceparent), tracestate, }; } -function parseTraceparent(traceparent?: string): { traceId: string; spanId: string } | undefined { - if (!traceparent) { - return undefined; - } - - const parts = traceparent.split("-"); - - if (parts.length !== 4) { - return undefined; - } - - const [version, traceId, spanId, flags] = parts; - - if (version !== "00") { - return undefined; - } - - return { traceId, spanId }; -} - function prepareEvent(event: QueriedEvent): PreparedEvent { return { ...event, diff --git a/apps/webapp/app/v3/services/triggerTask.server.ts b/apps/webapp/app/v3/services/triggerTask.server.ts index c43485cbb..bfb31e249 100644 --- a/apps/webapp/app/v3/services/triggerTask.server.ts +++ b/apps/webapp/app/v3/services/triggerTask.server.ts @@ -19,7 +19,7 @@ export type TriggerTaskServiceOptions = { idempotencyKey?: string; idempotencyKeyExpiresAt?: Date; triggerVersion?: string; - traceContext?: Record; + traceContext?: Record; spanParentAsLink?: boolean; parentAsLinkType?: "replay" | "trigger"; batchId?: string; diff --git a/apps/webapp/test/environmentVariableRules.test.ts b/apps/webapp/test/environmentVariableRules.test.ts index af27dd3c7..39e3d9892 100644 --- a/apps/webapp/test/environmentVariableRules.test.ts +++ b/apps/webapp/test/environmentVariableRules.test.ts @@ -15,33 +15,6 @@ describe("removeBlacklistedVariables", () => { expect(result).toEqual([{ key: "NORMAL_VAR", value: "normal" }]); }); - it("should remove variables with blacklisted prefixes", () => { - const variables: EnvironmentVariable[] = [ - { key: "OTEL_SERVICE_NAME", value: "my-service" }, - { key: "OTEL_TRACE_SAMPLER", value: "always_on" }, - { key: "NORMAL_VAR", value: "normal" }, - ]; - - const result = removeBlacklistedVariables(variables); - - expect(result).toEqual([{ key: "NORMAL_VAR", value: "normal" }]); - }); - - it("should keep whitelisted variables even if they match a blacklisted prefix", () => { - const variables: EnvironmentVariable[] = [ - { key: "OTEL_LOG_LEVEL", value: "debug" }, - { key: "OTEL_SERVICE_NAME", value: "my-service" }, - { key: "NORMAL_VAR", value: "normal" }, - ]; - - const result = removeBlacklistedVariables(variables); - - expect(result).toEqual([ - { key: "OTEL_LOG_LEVEL", value: "debug" }, - { key: "NORMAL_VAR", value: "normal" }, - ]); - }); - it("should handle empty input array", () => { const variables: EnvironmentVariable[] = []; @@ -53,8 +26,6 @@ describe("removeBlacklistedVariables", () => { it("should handle mixed case variables", () => { const variables: EnvironmentVariable[] = [ { key: "trigger_secret_key", value: "secret123" }, // Different case - { key: "OTEL_LOG_LEVEL", value: "debug" }, - { key: "otel_service_name", value: "my-service" }, // Different case { key: "NORMAL_VAR", value: "normal" }, ]; @@ -64,8 +35,6 @@ describe("removeBlacklistedVariables", () => { // Note: The function is case-sensitive, so different case variables should pass through expect(result).toEqual([ { key: "trigger_secret_key", value: "secret123" }, - { key: "OTEL_LOG_LEVEL", value: "debug" }, - { key: "otel_service_name", value: "my-service" }, { key: "NORMAL_VAR", value: "normal" }, ]); }); @@ -73,17 +42,12 @@ describe("removeBlacklistedVariables", () => { it("should handle variables with empty values", () => { const variables: EnvironmentVariable[] = [ { key: "TRIGGER_SECRET_KEY", value: "" }, - { key: "OTEL_SERVICE_NAME", value: "" }, - { key: "OTEL_LOG_LEVEL", value: "" }, { key: "NORMAL_VAR", value: "" }, ]; const result = removeBlacklistedVariables(variables); - expect(result).toEqual([ - { key: "OTEL_LOG_LEVEL", value: "" }, - { key: "NORMAL_VAR", value: "" }, - ]); + expect(result).toEqual([{ key: "NORMAL_VAR", value: "" }]); }); it("should handle all types of rules in a single array", () => { @@ -91,11 +55,6 @@ describe("removeBlacklistedVariables", () => { // Exact matches (should be removed) { key: "TRIGGER_SECRET_KEY", value: "secret123" }, { key: "TRIGGER_API_URL", value: "https://api.example.com" }, - // Prefix matches (should be removed) - { key: "OTEL_SERVICE_NAME", value: "my-service" }, - { key: "OTEL_TRACE_SAMPLER", value: "always_on" }, - // Whitelist exception (should be kept) - { key: "OTEL_LOG_LEVEL", value: "debug" }, // Normal variables (should be kept) { key: "NORMAL_VAR", value: "normal" }, { key: "DATABASE_URL", value: "postgres://..." }, @@ -104,7 +63,6 @@ describe("removeBlacklistedVariables", () => { const result = removeBlacklistedVariables(variables); expect(result).toEqual([ - { key: "OTEL_LOG_LEVEL", value: "debug" }, { key: "NORMAL_VAR", value: "normal" }, { key: "DATABASE_URL", value: "postgres://..." }, ]); diff --git a/internal-packages/run-engine/src/engine/types.ts b/internal-packages/run-engine/src/engine/types.ts index e15f90b1c..7f22b6770 100644 --- a/internal-packages/run-engine/src/engine/types.ts +++ b/internal-packages/run-engine/src/engine/types.ts @@ -6,6 +6,7 @@ import { MachinePresetName, RetryOptions, RunChainState, + TriggerTraceContext, } from "@trigger.dev/core/v3"; import { PrismaClient, PrismaReplicaClient } from "@trigger.dev/database"; import { Worker, type WorkerConcurrencyOptions } from "@trigger.dev/redis-worker"; @@ -89,7 +90,7 @@ export type TriggerParams = { payload: string; payloadType: string; context: any; - traceContext: Record; + traceContext: TriggerTraceContext; traceId: string; spanId: string; parentSpanId?: string; diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index 3d4e4c8a9..24c68670a 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -82,17 +82,13 @@ "@depot/cli": "0.0.1-cli.2.80.0", "@modelcontextprotocol/sdk": "^1.6.1", "@opentelemetry/api": "1.9.0", - "@opentelemetry/api-logs": "0.52.1", - "@opentelemetry/exporter-logs-otlp-http": "0.52.1", - "@opentelemetry/exporter-trace-otlp-http": "0.52.1", - "@opentelemetry/instrumentation": "0.52.1", - "@opentelemetry/instrumentation-fetch": "0.52.1", - "@opentelemetry/resources": "1.25.1", - "@opentelemetry/sdk-logs": "0.52.1", - "@opentelemetry/sdk-node": "0.52.1", - "@opentelemetry/sdk-trace-base": "1.25.1", - "@opentelemetry/sdk-trace-node": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1", + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/exporter-trace-otlp-http": "0.203.0", + "@opentelemetry/instrumentation": "0.203.0", + "@opentelemetry/instrumentation-fetch": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-trace-node": "2.0.1", + "@opentelemetry/semantic-conventions": "1.36.0", "@trigger.dev/build": "workspace:4.0.0-v4-beta.26", "@trigger.dev/core": "workspace:4.0.0-v4-beta.26", "ansi-escapes": "^7.0.0", diff --git a/packages/cli-v3/src/cli/common.ts b/packages/cli-v3/src/cli/common.ts index b607ca07f..3cf9f2aba 100644 --- a/packages/cli-v3/src/cli/common.ts +++ b/packages/cli-v3/src/cli/common.ts @@ -1,15 +1,13 @@ -import { flattenAttributes } from "@trigger.dev/core/v3"; -import { recordSpanException } from "@trigger.dev/core/v3/workers"; +import { outro } from "@clack/prompts"; import { Command } from "commander"; import { z } from "zod"; -import { getTracer, provider } from "../telemetry/tracing.js"; import { fromZodError } from "zod-validation-error"; -import { logger } from "../utilities/logger.js"; -import { outro } from "@clack/prompts"; -import { chalkError } from "../utilities/cliOutput.js"; -import { CLOUD_API_URL } from "../consts.js"; -import { readAuthConfigCurrentProfileName } from "../utilities/configFiles.js"; import { BundleError } from "../build/bundle.js"; +import { CLOUD_API_URL } from "../consts.js"; +import { chalkError } from "../utilities/cliOutput.js"; +import { readAuthConfigCurrentProfileName } from "../utilities/configFiles.js"; +import { logger } from "../utilities/logger.js"; +import { trace } from "@opentelemetry/api"; export const CommonCommandOptions = z.object({ apiUrl: z.string().optional(), @@ -39,69 +37,52 @@ export class OutroCommandError extends SkipCommandError {} export async function handleTelemetry(action: () => Promise) { try { await action(); - - await provider?.forceFlush(); } catch (e) { - await provider?.forceFlush(); - process.exitCode = 1; } } -export const tracer = getTracer(); - export async function wrapCommandAction( name: string, schema: T, options: unknown, action: (opts: z.output) => Promise ): Promise { - return await tracer.startActiveSpan(name, async (span) => { - try { - const parsedOptions = schema.safeParse(options); + try { + const parsedOptions = schema.safeParse(options); - if (!parsedOptions.success) { - throw new Error(fromZodError(parsedOptions.error).toString()); - } - - span.setAttributes({ - ...flattenAttributes(parsedOptions.data, "cli.options"), - }); - - logger.loggerLevel = parsedOptions.data.logLevel; - - logger.debug(`Running "${name}" with the following options`, { - options: options, - spanContext: span?.spanContext(), - }); - - const result = await action(parsedOptions.data); - - span.end(); - - return result; - } catch (e) { - if (e instanceof SkipLoggingError) { - recordSpanException(span, e); - } else if (e instanceof OutroCommandError) { - outro("Operation cancelled"); - } else if (e instanceof SkipCommandError) { - // do nothing - } else if (e instanceof BundleError) { - process.exit(1); - } else { - recordSpanException(span, e); - - logger.log(`${chalkError("X Error:")} ${e instanceof Error ? e.message : String(e)}`); - } - - span.end(); - - throw e; + if (!parsedOptions.success) { + throw new Error(fromZodError(parsedOptions.error).toString()); } - }); + + logger.loggerLevel = parsedOptions.data.logLevel; + + logger.debug(`Running "${name}" with the following options`, { + options: options, + }); + + const result = await action(parsedOptions.data); + + return result; + } catch (e) { + if (e instanceof SkipLoggingError) { + // do nothing + } else if (e instanceof OutroCommandError) { + outro("Operation cancelled"); + } else if (e instanceof SkipCommandError) { + // do nothing + } else if (e instanceof BundleError) { + process.exit(1); + } else { + logger.log(`${chalkError("X Error:")} ${e instanceof Error ? e.message : String(e)}`); + } + + throw e; + } } +export const tracer = trace.getTracer("trigger.dev/cli"); + export function installExitHandler() { process.on("SIGINT", () => { process.exit(0); diff --git a/packages/cli-v3/src/entryPoints/dev-run-worker.ts b/packages/cli-v3/src/entryPoints/dev-run-worker.ts index 8a148eaa7..6a526a57c 100644 --- a/packages/cli-v3/src/entryPoints/dev-run-worker.ts +++ b/packages/cli-v3/src/entryPoints/dev-run-worker.ts @@ -21,6 +21,7 @@ import { runtime, runTimelineMetrics, taskContext, + TaskRunContext, TaskRunErrorCodes, TaskRunExecution, timeout, @@ -29,6 +30,7 @@ import { waitUntil, WorkerManifest, WorkerToExecutorMessageCatalog, + traceContext, } from "@trigger.dev/core/v3"; import { TriggerTracer } from "@trigger.dev/core/v3/tracer"; import { @@ -52,6 +54,7 @@ import { TracingSDK, usage, UsageTimeoutManager, + StandardTraceContextManager, } from "@trigger.dev/core/v3/workers"; import { ZodIpcConnection } from "@trigger.dev/core/v3/zodIpc"; import { readFile } from "node:fs/promises"; @@ -126,6 +129,9 @@ timeout.setGlobalManager(usageTimeoutManager); const standardResourceCatalog = new StandardResourceCatalog(); resourceCatalog.setGlobalResourceCatalog(standardResourceCatalog); +const standardTraceContextManager = new StandardTraceContextManager(); +traceContext.setGlobalManager(standardTraceContextManager); + const durableClock = new DurableClock(); clock.setGlobalClock(durableClock); const runMetadataManager = new StandardMetadataManager( @@ -175,11 +181,11 @@ async function doBootstrap() { const { config, handleError } = await importConfig(workerManifest.configPath); const tracingSDK = new TracingSDK({ - url: env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://0.0.0.0:4318", + url: env.TRIGGER_OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://0.0.0.0:4318", instrumentations: config.telemetry?.instrumentations ?? config.instrumentations ?? [], exporters: config.telemetry?.exporters ?? [], logExporters: config.telemetry?.logExporters ?? [], - diagLogLevel: (env.OTEL_LOG_LEVEL as TracingDiagnosticLogLevel) ?? "none", + diagLogLevel: (env.TRIGGER_OTEL_LOG_LEVEL as TracingDiagnosticLogLevel) ?? "none", forceFlushTimeoutMillis: 30_000, }); @@ -301,6 +307,7 @@ function resetExecutionEnvironment() { _sharedWorkerRuntime?.reset(); durableClock.reset(); taskContext.disable(); + standardTraceContextManager.reset(); log(`[${new Date().toISOString()}] Reset execution environment`); } @@ -337,6 +344,7 @@ const zodIpc = new ZodIpcConnection({ resetExecutionEnvironment(); + standardTraceContextManager.traceContext = traceContext; standardRunTimelineMetricsManager.registerMetricsFromExecution(metrics, isWarmStart); if (_isRunning) { @@ -361,6 +369,14 @@ const zodIpc = new ZodIpcConnection({ return; } + const ctx = TaskRunContext.parse(execution); + + taskContext.setGlobalTaskContext({ + ctx, + worker: metadata, + isWarmStart: isWarmStart ?? false, + }); + try { const { tracer, tracingSDK, consoleInterceptor, config, workerManifest } = await bootstrap(); @@ -516,7 +532,7 @@ const zodIpc = new ZodIpcConnection({ const signal = AbortSignal.any([_cancelController.signal, timeoutController.signal]); - const { result } = await executor.execute(execution, metadata, traceContext, signal); + const { result } = await executor.execute(execution, ctx, signal); if (_isRunning && !_isCancelled) { const usageSample = usage.stop(_executionMeasurement); diff --git a/packages/cli-v3/src/entryPoints/managed-run-worker.ts b/packages/cli-v3/src/entryPoints/managed-run-worker.ts index 21ed6a265..c659af93d 100644 --- a/packages/cli-v3/src/entryPoints/managed-run-worker.ts +++ b/packages/cli-v3/src/entryPoints/managed-run-worker.ts @@ -20,6 +20,7 @@ import { runtime, runTimelineMetrics, taskContext, + TaskRunContext, TaskRunErrorCodes, TaskRunExecution, timeout, @@ -28,6 +29,7 @@ import { waitUntil, WorkerManifest, WorkerToExecutorMessageCatalog, + traceContext, } from "@trigger.dev/core/v3"; import { TriggerTracer } from "@trigger.dev/core/v3/tracer"; import { @@ -52,6 +54,7 @@ import { TracingSDK, usage, UsageTimeoutManager, + StandardTraceContextManager, } from "@trigger.dev/core/v3/workers"; import { ZodIpcConnection } from "@trigger.dev/core/v3/zodIpc"; import { readFile } from "node:fs/promises"; @@ -119,6 +122,9 @@ resourceCatalog.setGlobalResourceCatalog(standardResourceCatalog); const durableClock = new DurableClock(); clock.setGlobalClock(durableClock); +const standardTraceContextManager = new StandardTraceContextManager(); +traceContext.setGlobalManager(standardTraceContextManager); + const runMetadataManager = new StandardMetadataManager( apiClientManager.clientOrThrow(), getEnvVar("TRIGGER_STREAM_URL", getEnvVar("TRIGGER_API_URL")) ?? "https://api.trigger.dev" @@ -166,9 +172,9 @@ async function doBootstrap() { ); const tracingSDK = new TracingSDK({ - url: env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://0.0.0.0:4318", + url: env.TRIGGER_OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://0.0.0.0:4318", instrumentations: config.instrumentations ?? [], - diagLogLevel: (env.OTEL_LOG_LEVEL as TracingDiagnosticLogLevel) ?? "none", + diagLogLevel: (env.TRIGGER_OTEL_LOG_LEVEL as TracingDiagnosticLogLevel) ?? "none", forceFlushTimeoutMillis: 30_000, exporters: config.telemetry?.exporters ?? [], logExporters: config.telemetry?.logExporters ?? [], @@ -287,6 +293,7 @@ function resetExecutionEnvironment() { _sharedWorkerRuntime?.reset(); durableClock.reset(); taskContext.disable(); + standardTraceContextManager.reset(); console.log(`[${new Date().toISOString()}] Reset execution environment`); } @@ -328,6 +335,8 @@ const zodIpc = new ZodIpcConnection({ resetExecutionEnvironment(); + standardTraceContextManager.traceContext = traceContext; + const prodManager = initializeUsageManager({ usageIntervalMs: getEnvVar("USAGE_HEARTBEAT_INTERVAL_MS"), usageEventUrl: getEnvVar("USAGE_EVENT_URL"), @@ -365,6 +374,14 @@ const zodIpc = new ZodIpcConnection({ return; } + const ctx = TaskRunContext.parse(execution); + + taskContext.setGlobalTaskContext({ + ctx, + worker: metadata, + isWarmStart: isWarmStart ?? false, + }); + try { const { tracer, tracingSDK, consoleInterceptor, config, workerManifest } = await bootstrap(); @@ -514,7 +531,7 @@ const zodIpc = new ZodIpcConnection({ const signal = AbortSignal.any([_cancelController.signal, timeoutController.signal]); - const { result } = await executor.execute(execution, metadata, traceContext, signal); + const { result } = await executor.execute(execution, ctx, signal); if (_isRunning && !_isCancelled) { const usageSample = usage.stop(_executionMeasurement); diff --git a/packages/cli-v3/src/telemetry/tracing.ts b/packages/cli-v3/src/telemetry/tracing.ts deleted file mode 100644 index e00e0668b..000000000 --- a/packages/cli-v3/src/telemetry/tracing.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; -import { registerInstrumentations } from "@opentelemetry/instrumentation"; -import { Resource, detectResourcesSync, processDetectorSync } from "@opentelemetry/resources"; -import { NodeTracerProvider, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-node"; -import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch"; -import { DiagConsoleLogger, DiagLogLevel, diag, trace } from "@opentelemetry/api"; -import { - SEMRESATTRS_SERVICE_NAME, - SEMRESATTRS_SERVICE_VERSION, -} from "@opentelemetry/semantic-conventions"; -import { logger } from "../utilities/logger.js"; -import { VERSION } from "../version.js"; -import { env } from "std-env"; - -function initializeTracing(): NodeTracerProvider | undefined { - if ( - process.argv.includes("--skip-telemetry") || - env.TRIGGER_DEV_SKIP_TELEMETRY || // only for backwards compat - env.TRIGGER_TELEMETRY_DISABLED - ) { - logger.debug("📉 Telemetry disabled"); - return; - } - - if (env.OTEL_INTERNAL_DIAG_DEBUG) { - diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG); - } - - const resource = detectResourcesSync({ - detectors: [processDetectorSync], - }).merge( - new Resource({ - [SEMRESATTRS_SERVICE_NAME]: "trigger.dev cli v3", - [SEMRESATTRS_SERVICE_VERSION]: VERSION, - }) - ); - - const traceProvider = new NodeTracerProvider({ - forceFlushTimeoutMillis: 30_000, - resource, - spanLimits: { - attributeCountLimit: 1000, - attributeValueLengthLimit: 2048, - eventCountLimit: 100, - attributePerEventCountLimit: 100, - linkCountLimit: 10, - attributePerLinkCountLimit: 100, - }, - }); - - const spanExporter = new OTLPTraceExporter({ - url: "https://otel.baselime.io/v1", - timeoutMillis: 5000, - headers: { - "x-api-key": "b6e0fbbaf8dc2524773d2152ae2e9eb5c7fbaa52", - }, - }); - - const spanProcessor = new SimpleSpanProcessor(spanExporter); - - traceProvider.addSpanProcessor(spanProcessor); - traceProvider.register(); - - registerInstrumentations({ - instrumentations: [new FetchInstrumentation()], - }); - - return traceProvider; -} - -export const provider = initializeTracing(); - -export function getTracer() { - return trace.getTracer("trigger.dev cli v3", VERSION); -} diff --git a/packages/cli-v3/src/utilities/session.ts b/packages/cli-v3/src/utilities/session.ts index be9d9ebfd..341947ff7 100644 --- a/packages/cli-v3/src/utilities/session.ts +++ b/packages/cli-v3/src/utilities/session.ts @@ -1,12 +1,9 @@ import { recordSpanException } from "@trigger.dev/core/v3/workers"; import { CliApiClient } from "../apiClient.js"; import { readAuthConfigProfile } from "./configFiles.js"; -import { getTracer } from "../telemetry/tracing.js"; import { logger } from "./logger.js"; import { GitMeta } from "@trigger.dev/core/v3"; -const tracer = getTracer(); - export type LoginResultOk = { ok: true; profile: string; @@ -31,63 +28,44 @@ export type LoginResult = }; export async function isLoggedIn(profile: string = "default"): Promise { - return await tracer.startActiveSpan("isLoggedIn", async (span) => { - try { - const config = readAuthConfigProfile(profile); + try { + const config = readAuthConfigProfile(profile); - if (!config?.accessToken || !config?.apiUrl) { - span.recordException(new Error("You must login first")); - span.end(); - return { ok: false as const, error: "You must login first" }; - } + if (!config?.accessToken || !config?.apiUrl) { + return { ok: false as const, error: "You must login first" }; + } - const apiClient = new CliApiClient(config.apiUrl, config.accessToken); - const userData = await apiClient.whoAmI(); - - if (!userData.success) { - recordSpanException(span, userData.error); - span.end(); - - return { - ok: false as const, - error: userData.error, - auth: { - apiUrl: config.apiUrl, - accessToken: config.accessToken, - }, - }; - } - - span.setAttributes({ - "login.userId": userData.data.userId, - "login.email": userData.data.email, - "login.dashboardUrl": userData.data.dashboardUrl, - "login.profile": profile, - }); - - span.end(); + const apiClient = new CliApiClient(config.apiUrl, config.accessToken); + const userData = await apiClient.whoAmI(); + if (!userData.success) { return { - ok: true as const, - profile, - userId: userData.data.userId, - email: userData.data.email, - dashboardUrl: userData.data.dashboardUrl, + ok: false as const, + error: userData.error, auth: { apiUrl: config.apiUrl, accessToken: config.accessToken, }, }; - } catch (e) { - recordSpanException(span, e); - span.end(); - - return { - ok: false as const, - error: e instanceof Error ? e.message : "Unknown error", - }; } - }); + + return { + ok: true as const, + profile, + userId: userData.data.userId, + email: userData.data.email, + dashboardUrl: userData.data.dashboardUrl, + auth: { + apiUrl: config.apiUrl, + accessToken: config.accessToken, + }, + }; + } catch (e) { + return { + ok: false as const, + error: e instanceof Error ? e.message : "Unknown error", + }; + } } export type GetEnvOptions = { diff --git a/packages/core/package.json b/packages/core/package.json index 05e781fda..bf14dfdbd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -171,17 +171,16 @@ "@google-cloud/precise-date": "^4.0.0", "@jsonhero/path": "^1.0.21", "@opentelemetry/api": "1.9.0", - "@opentelemetry/api-logs": "0.52.1", - "@opentelemetry/core": "^1.30.1", - "@opentelemetry/exporter-logs-otlp-http": "0.52.1", - "@opentelemetry/exporter-trace-otlp-http": "0.52.1", - "@opentelemetry/instrumentation": "0.52.1", - "@opentelemetry/resources": "1.25.1", - "@opentelemetry/sdk-logs": "0.52.1", - "@opentelemetry/sdk-node": "0.52.1", - "@opentelemetry/sdk-trace-base": "1.25.1", - "@opentelemetry/sdk-trace-node": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1", + "@opentelemetry/api-logs": "0.203.0", + "@opentelemetry/core": "2.0.1", + "@opentelemetry/exporter-logs-otlp-http": "0.203.0", + "@opentelemetry/exporter-trace-otlp-http": "0.203.0", + "@opentelemetry/instrumentation": "0.203.0", + "@opentelemetry/resources": "2.0.1", + "@opentelemetry/sdk-logs": "0.203.0", + "@opentelemetry/sdk-trace-base": "2.0.1", + "@opentelemetry/sdk-trace-node": "2.0.1", + "@opentelemetry/semantic-conventions": "1.36.0", "dequal": "^2.0.3", "eventsource": "^3.0.5", "eventsource-parser": "^3.0.0", diff --git a/packages/core/src/v3/apiClient/core.ts b/packages/core/src/v3/apiClient/core.ts index ec1fe8421..1f3dca03f 100644 --- a/packages/core/src/v3/apiClient/core.ts +++ b/packages/core/src/v3/apiClient/core.ts @@ -4,7 +4,7 @@ import { RetryOptions } from "../schemas/index.js"; import { calculateNextRetryDelay } from "../utils/retries.js"; import { ApiConnectionError, ApiError, ApiSchemaValidationError } from "./errors.js"; -import { Attributes, context, propagation, Span } from "@opentelemetry/api"; +import { Attributes, context, propagation, Span, trace } from "@opentelemetry/api"; import { suppressTracing } from "@opentelemetry/core"; import { SemanticInternalAttributes } from "../semanticInternalAttributes.js"; import type { TriggerTracer } from "../tracer.js"; @@ -617,10 +617,6 @@ export function hasOwn(obj: Object, key: string): boolean { function injectPropagationHeadersIfInWorker(requestInit?: RequestInit): RequestInit | undefined { const headers = new Headers(requestInit?.headers); - if (headers.get("x-trigger-worker") !== "true") { - return requestInit; - } - const headersObject = Object.fromEntries(headers.entries()); propagation.inject(context.active(), headersObject); diff --git a/packages/core/src/v3/index.ts b/packages/core/src/v3/index.ts index 3bd1fc454..e1785e504 100644 --- a/packages/core/src/v3/index.ts +++ b/packages/core/src/v3/index.ts @@ -9,6 +9,7 @@ export * from "./limits.js"; export * from "./logger-api.js"; export * from "./runtime-api.js"; export * from "./task-context-api.js"; +export * from "./trace-context-api.js"; export * from "./apiClientManager-api.js"; export * from "./usage-api.js"; export * from "./run-metadata-api.js"; diff --git a/packages/core/src/v3/isomorphic/index.ts b/packages/core/src/v3/isomorphic/index.ts index 53836fb09..8e15c36d2 100644 --- a/packages/core/src/v3/isomorphic/index.ts +++ b/packages/core/src/v3/isomorphic/index.ts @@ -3,3 +3,4 @@ export * from "./duration.js"; export * from "./maxDuration.js"; export * from "./queueName.js"; export * from "./consts.js"; +export * from "./traceContext.js"; diff --git a/packages/core/src/v3/isomorphic/traceContext.ts b/packages/core/src/v3/isomorphic/traceContext.ts new file mode 100644 index 000000000..b6754c4c6 --- /dev/null +++ b/packages/core/src/v3/isomorphic/traceContext.ts @@ -0,0 +1,29 @@ +export function parseTraceparent( + traceparent?: string +): { traceId: string; spanId: string } | undefined { + if (!traceparent) { + return undefined; + } + + const parts = traceparent.split("-"); + + if (parts.length !== 4) { + return undefined; + } + + const [version, traceId, spanId] = parts; + + if (version !== "00") { + return undefined; + } + + if (!traceId || !spanId) { + return undefined; + } + + return { traceId, spanId }; +} + +export function serializeTraceparent(traceId: string, spanId: string) { + return `00-${traceId}-${spanId}-01`; +} diff --git a/packages/core/src/v3/otel/tracingSDK.ts b/packages/core/src/v3/otel/tracingSDK.ts index c2d7b3d34..7fded903c 100644 --- a/packages/core/src/v3/otel/tracingSDK.ts +++ b/packages/core/src/v3/otel/tracingSDK.ts @@ -1,25 +1,19 @@ import { DiagConsoleLogger, DiagLogLevel, TracerProvider, diag } from "@opentelemetry/api"; -import { RandomIdGenerator } from "@opentelemetry/sdk-trace-base"; import { logs } from "@opentelemetry/api-logs"; +import { TraceState } from "@opentelemetry/core"; import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http"; import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http"; import { registerInstrumentations, type Instrumentation } from "@opentelemetry/instrumentation"; -import { - DetectorSync, - IResource, - Resource, - ResourceAttributes, - ResourceDetectionConfig, - detectResourcesSync, - processDetectorSync, -} from "@opentelemetry/resources"; +import { detectResources, processDetector, resourceFromAttributes } from "@opentelemetry/resources"; import { BatchLogRecordProcessor, - LoggerProvider, LogRecordExporter, + LogRecordProcessor, + LoggerProvider, ReadableLogRecord, SimpleLogRecordProcessor, } from "@opentelemetry/sdk-logs"; +import { RandomIdGenerator, SpanProcessor } from "@opentelemetry/sdk-trace-base"; import { BatchSpanProcessor, NodeTracerProvider, @@ -40,45 +34,14 @@ import { OTEL_SPAN_EVENT_COUNT_LIMIT, } from "../limits.js"; import { SemanticInternalAttributes } from "../semanticInternalAttributes.js"; +import { taskContext } from "../task-context-api.js"; import { TaskContextLogProcessor, TaskContextSpanProcessor, } from "../taskContext/otelProcessors.js"; +import { traceContext } from "../trace-context-api.js"; import { getEnvVar } from "../utils/getEnv.js"; -class AsyncResourceDetector implements DetectorSync { - private _promise: Promise; - private _resolver?: (value: ResourceAttributes) => void; - private _resolved: boolean = false; - - constructor() { - this._promise = new Promise((resolver) => { - this._resolver = resolver; - }); - } - - get isResolved() { - return this._resolved; - } - - detect(_config?: ResourceDetectionConfig): Resource { - return new Resource({}, this._promise); - } - - resolveWithAttributes(attributes: ResourceAttributes) { - if (!this._resolver) { - throw new Error("Resolver not available"); - } - - if (this._resolved) { - return; - } - - this._resolved = true; - this._resolver(attributes); - } -} - export type TracingDiagnosticLogLevel = | "none" | "error" @@ -91,7 +54,6 @@ export type TracingDiagnosticLogLevel = export type TracingSDKConfig = { url: string; forceFlushTimeoutMillis?: number; - resource?: IResource; instrumentations?: Instrumentation[]; exporters?: SpanExporter[]; logExporters?: LogRecordExporter[]; @@ -101,7 +63,6 @@ export type TracingSDKConfig = { const idGenerator = new RandomIdGenerator(); export class TracingSDK { - public readonly asyncResourceDetector = new AsyncResourceDetector(); private readonly _logProvider: LoggerProvider; private readonly _spanExporter: SpanExporter; private readonly _traceProvider: NodeTracerProvider; @@ -112,27 +73,81 @@ export class TracingSDK { constructor(private readonly config: TracingSDKConfig) { setLogLevel(config.diagLogLevel ?? "none"); - const envResourceAttributesSerialized = getEnvVar("OTEL_RESOURCE_ATTRIBUTES"); + const envResourceAttributesSerialized = getEnvVar("TRIGGER_OTEL_RESOURCE_ATTRIBUTES"); const envResourceAttributes = envResourceAttributesSerialized ? JSON.parse(envResourceAttributesSerialized) : {}; - const commonResources = detectResourcesSync({ - detectors: [this.asyncResourceDetector, processDetectorSync], + const commonResources = detectResources({ + detectors: [processDetector], }) .merge( - new Resource({ + resourceFromAttributes({ [SemanticResourceAttributes.CLOUD_PROVIDER]: "trigger.dev", [SemanticResourceAttributes.SERVICE_NAME]: - getEnvVar("OTEL_SERVICE_NAME") ?? "trigger.dev", + getEnvVar("TRIGGER_OTEL_SERVICE_NAME") ?? "trigger.dev", [SemanticInternalAttributes.TRIGGER]: true, [SemanticInternalAttributes.CLI_VERSION]: VERSION, [SemanticInternalAttributes.SDK_VERSION]: VERSION, [SemanticInternalAttributes.SDK_LANGUAGE]: "typescript", }) ) - .merge(config.resource ?? new Resource({})) - .merge(new Resource(envResourceAttributes)); + .merge(resourceFromAttributes(envResourceAttributes)) + .merge(resourceFromAttributes(taskContext.resourceAttributes)); + + const spanExporter = new OTLPTraceExporter({ + url: `${config.url}/v1/traces`, + timeoutMillis: config.forceFlushTimeoutMillis, + }); + + const spanProcessors: Array = []; + + spanProcessors.push( + new TaskContextSpanProcessor( + VERSION, + getEnvVar("TRIGGER_OTEL_BATCH_PROCESSING_ENABLED") === "1" + ? new BatchSpanProcessor(spanExporter, { + maxExportBatchSize: parseInt( + getEnvVar("TRIGGER_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE") ?? "64" + ), + scheduledDelayMillis: parseInt( + getEnvVar("TRIGGER_OTEL_SPAN_SCHEDULED_DELAY_MILLIS") ?? "200" + ), + exportTimeoutMillis: parseInt( + getEnvVar("TRIGGER_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS") ?? "30000" + ), + maxQueueSize: parseInt(getEnvVar("TRIGGER_OTEL_SPAN_MAX_QUEUE_SIZE") ?? "512"), + }) + : new SimpleSpanProcessor(spanExporter) + ) + ); + + const externalTraceId = idGenerator.generateTraceId(); + const externalTraceContext = traceContext.getExternalTraceContext(); + + for (const exporter of config.exporters ?? []) { + spanProcessors.push( + getEnvVar("TRIGGER_OTEL_BATCH_PROCESSING_ENABLED") === "1" + ? new BatchSpanProcessor( + new ExternalSpanExporterWrapper(exporter, externalTraceId, externalTraceContext), + { + maxExportBatchSize: parseInt( + getEnvVar("TRIGGER_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE") ?? "64" + ), + scheduledDelayMillis: parseInt( + getEnvVar("TRIGGER_OTEL_SPAN_SCHEDULED_DELAY_MILLIS") ?? "200" + ), + exportTimeoutMillis: parseInt( + getEnvVar("TRIGGER_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS") ?? "30000" + ), + maxQueueSize: parseInt(getEnvVar("TRIGGER_OTEL_SPAN_MAX_QUEUE_SIZE") ?? "512"), + } + ) + : new SimpleSpanProcessor( + new ExternalSpanExporterWrapper(exporter, externalTraceId, externalTraceContext) + ) + ); + } const traceProvider = new NodeTracerProvider({ forceFlushTimeoutMillis: config.forceFlushTimeoutMillis, @@ -145,50 +160,9 @@ export class TracingSDK { linkCountLimit: OTEL_LINK_COUNT_LIMIT, attributePerLinkCountLimit: OTEL_ATTRIBUTE_PER_LINK_COUNT_LIMIT, }, + spanProcessors, }); - const spanExporter = new OTLPTraceExporter({ - url: `${config.url}/v1/traces`, - timeoutMillis: config.forceFlushTimeoutMillis, - }); - - traceProvider.addSpanProcessor( - new TaskContextSpanProcessor( - traceProvider.getTracer("trigger-dev-worker", VERSION), - getEnvVar("OTEL_BATCH_PROCESSING_ENABLED") === "1" - ? new BatchSpanProcessor(spanExporter, { - maxExportBatchSize: parseInt(getEnvVar("OTEL_SPAN_MAX_EXPORT_BATCH_SIZE") ?? "64"), - scheduledDelayMillis: parseInt( - getEnvVar("OTEL_SPAN_SCHEDULED_DELAY_MILLIS") ?? "200" - ), - exportTimeoutMillis: parseInt( - getEnvVar("OTEL_SPAN_EXPORT_TIMEOUT_MILLIS") ?? "30000" - ), - maxQueueSize: parseInt(getEnvVar("OTEL_SPAN_MAX_QUEUE_SIZE") ?? "512"), - }) - : new SimpleSpanProcessor(spanExporter) - ) - ); - - const externalTraceId = idGenerator.generateTraceId(); - - for (const exporter of config.exporters ?? []) { - traceProvider.addSpanProcessor( - getEnvVar("OTEL_BATCH_PROCESSING_ENABLED") === "1" - ? new BatchSpanProcessor(new ExternalSpanExporterWrapper(exporter, externalTraceId), { - maxExportBatchSize: parseInt(getEnvVar("OTEL_SPAN_MAX_EXPORT_BATCH_SIZE") ?? "64"), - scheduledDelayMillis: parseInt( - getEnvVar("OTEL_SPAN_SCHEDULED_DELAY_MILLIS") ?? "200" - ), - exportTimeoutMillis: parseInt( - getEnvVar("OTEL_SPAN_EXPORT_TIMEOUT_MILLIS") ?? "30000" - ), - maxQueueSize: parseInt(getEnvVar("OTEL_SPAN_MAX_QUEUE_SIZE") ?? "512"), - }) - : new SimpleSpanProcessor(new ExternalSpanExporterWrapper(exporter, externalTraceId)) - ); - } - traceProvider.register(); registerInstrumentations({ @@ -200,6 +174,57 @@ export class TracingSDK { url: `${config.url}/v1/logs`, }); + const logProcessors: Array = [ + new TaskContextLogProcessor( + getEnvVar("TRIGGER_OTEL_BATCH_PROCESSING_ENABLED") === "1" + ? new BatchLogRecordProcessor(logExporter, { + maxExportBatchSize: parseInt( + getEnvVar("TRIGGER_OTEL_LOG_MAX_EXPORT_BATCH_SIZE") ?? "64" + ), + scheduledDelayMillis: parseInt( + getEnvVar("TRIGGER_OTEL_LOG_SCHEDULED_DELAY_MILLIS") ?? "200" + ), + exportTimeoutMillis: parseInt( + getEnvVar("TRIGGER_OTEL_LOG_EXPORT_TIMEOUT_MILLIS") ?? "30000" + ), + maxQueueSize: parseInt(getEnvVar("TRIGGER_OTEL_LOG_MAX_QUEUE_SIZE") ?? "512"), + }) + : new SimpleLogRecordProcessor(logExporter) + ), + ]; + + for (const externalLogExporter of config.logExporters ?? []) { + logProcessors.push( + getEnvVar("TRIGGER_OTEL_BATCH_PROCESSING_ENABLED") === "1" + ? new BatchLogRecordProcessor( + new ExternalLogRecordExporterWrapper( + externalLogExporter, + externalTraceId, + externalTraceContext + ), + { + maxExportBatchSize: parseInt( + getEnvVar("TRIGGER_OTEL_LOG_MAX_EXPORT_BATCH_SIZE") ?? "64" + ), + scheduledDelayMillis: parseInt( + getEnvVar("TRIGGER_OTEL_LOG_SCHEDULED_DELAY_MILLIS") ?? "200" + ), + exportTimeoutMillis: parseInt( + getEnvVar("TRIGGER_OTEL_LOG_EXPORT_TIMEOUT_MILLIS") ?? "30000" + ), + maxQueueSize: parseInt(getEnvVar("TRIGGER_OTEL_LOG_MAX_QUEUE_SIZE") ?? "512"), + } + ) + : new SimpleLogRecordProcessor( + new ExternalLogRecordExporterWrapper( + externalLogExporter, + externalTraceId, + externalTraceContext + ) + ) + ); + } + // To start a logger, you first need to initialize the Logger provider. const loggerProvider = new LoggerProvider({ resource: commonResources, @@ -207,43 +232,9 @@ export class TracingSDK { attributeCountLimit: OTEL_LOG_ATTRIBUTE_COUNT_LIMIT, attributeValueLengthLimit: OTEL_LOG_ATTRIBUTE_VALUE_LENGTH_LIMIT, }, + processors: logProcessors, }); - loggerProvider.addLogRecordProcessor( - new TaskContextLogProcessor( - getEnvVar("OTEL_BATCH_PROCESSING_ENABLED") === "1" - ? new BatchLogRecordProcessor(logExporter, { - maxExportBatchSize: parseInt(getEnvVar("OTEL_LOG_MAX_EXPORT_BATCH_SIZE") ?? "64"), - scheduledDelayMillis: parseInt(getEnvVar("OTEL_LOG_SCHEDULED_DELAY_MILLIS") ?? "200"), - exportTimeoutMillis: parseInt(getEnvVar("OTEL_LOG_EXPORT_TIMEOUT_MILLIS") ?? "30000"), - maxQueueSize: parseInt(getEnvVar("OTEL_LOG_MAX_QUEUE_SIZE") ?? "512"), - }) - : new SimpleLogRecordProcessor(logExporter) - ) - ); - - for (const externalLogExporter of config.logExporters ?? []) { - loggerProvider.addLogRecordProcessor( - getEnvVar("OTEL_BATCH_PROCESSING_ENABLED") === "1" - ? new BatchLogRecordProcessor( - new ExternalLogRecordExporterWrapper(externalLogExporter, externalTraceId), - { - maxExportBatchSize: parseInt(getEnvVar("OTEL_LOG_MAX_EXPORT_BATCH_SIZE") ?? "64"), - scheduledDelayMillis: parseInt( - getEnvVar("OTEL_LOG_SCHEDULED_DELAY_MILLIS") ?? "200" - ), - exportTimeoutMillis: parseInt( - getEnvVar("OTEL_LOG_EXPORT_TIMEOUT_MILLIS") ?? "30000" - ), - maxQueueSize: parseInt(getEnvVar("OTEL_LOG_MAX_QUEUE_SIZE") ?? "512"), - } - ) - : new SimpleLogRecordProcessor( - new ExternalLogRecordExporterWrapper(externalLogExporter, externalTraceId) - ) - ); - } - this._logProvider = loggerProvider; this._spanExporter = spanExporter; this._traceProvider = traceProvider; @@ -298,7 +289,10 @@ function setLogLevel(level: TracingDiagnosticLogLevel) { class ExternalSpanExporterWrapper { constructor( private underlyingExporter: SpanExporter, - private externalTraceId: string + private externalTraceId: string, + private externalTraceContext: + | { traceId: string; spanId: string; tracestate?: string } + | undefined ) {} private transformSpan(span: ReadableSpan): ReadableSpan | undefined { @@ -307,14 +301,38 @@ class ExternalSpanExporterWrapper { return; } + const externalTraceId = this.externalTraceContext + ? this.externalTraceContext.traceId + : this.externalTraceId; + + const isAttemptSpan = span.attributes[SemanticInternalAttributes.SPAN_ATTEMPT]; + const spanContext = span.spanContext(); + let parentSpanContext = span.parentSpanContext; + + if (parentSpanContext) { + parentSpanContext = { + ...parentSpanContext, + traceId: externalTraceId, + }; + } + + if (isAttemptSpan && this.externalTraceContext) { + parentSpanContext = { + ...parentSpanContext, + traceId: externalTraceId, + spanId: this.externalTraceContext.spanId, + traceState: this.externalTraceContext.tracestate + ? new TraceState(this.externalTraceContext.tracestate) + : undefined, + traceFlags: parentSpanContext?.traceFlags ?? 0, + }; + } return { ...span, - spanContext: () => ({ ...spanContext, traceId: this.externalTraceId }), - parentSpanId: span.attributes[SemanticInternalAttributes.SPAN_ATTEMPT] - ? undefined - : span.parentSpanId, + spanContext: () => ({ ...spanContext, traceId: externalTraceId }), + parentSpanContext, }; } @@ -344,7 +362,10 @@ class ExternalSpanExporterWrapper { class ExternalLogRecordExporterWrapper { constructor( private underlyingExporter: LogRecordExporter, - private externalTraceId: string + private externalTraceId: string, + private externalTraceContext: + | { traceId: string; spanId: string; tracestate?: string } + | undefined ) {} export(logs: any[], resultCallback: (result: any) => void): void { @@ -359,12 +380,14 @@ class ExternalLogRecordExporterWrapper { transformLogRecord(logRecord: ReadableLogRecord): ReadableLogRecord { // If there's no spanContext, or if the externalTraceId is not set, return the original logRecord. - if (!logRecord.spanContext || !this.externalTraceId) { + if (!logRecord.spanContext || !this.externalTraceId || !this.externalTraceContext) { return logRecord; } // Capture externalTraceId for use within the proxy's scope. - const { externalTraceId } = this; + const externalTraceId = this.externalTraceContext + ? this.externalTraceContext.traceId + : this.externalTraceId; return new Proxy(logRecord, { get(target, prop, receiver) { diff --git a/packages/core/src/v3/schemas/schemas.ts b/packages/core/src/v3/schemas/schemas.ts index bd32d848f..ccd0fa188 100644 --- a/packages/core/src/v3/schemas/schemas.ts +++ b/packages/core/src/v3/schemas/schemas.ts @@ -296,3 +296,16 @@ export const RunChainState = z.object({ }); export type RunChainState = z.infer; + +export const TriggerTraceContext = z.object({ + traceparent: z.string().optional(), + tracestate: z.string().optional(), + external: z + .object({ + traceparent: z.string().optional(), + tracestate: z.string().optional(), + }) + .optional(), +}); + +export type TriggerTraceContext = z.infer; diff --git a/packages/core/src/v3/taskContext/otelProcessors.ts b/packages/core/src/v3/taskContext/otelProcessors.ts index db30624ff..16f93d42a 100644 --- a/packages/core/src/v3/taskContext/otelProcessors.ts +++ b/packages/core/src/v3/taskContext/otelProcessors.ts @@ -1,17 +1,16 @@ -import { LogRecord, LogRecordProcessor } from "@opentelemetry/sdk-logs"; +import { Context, trace, Tracer } from "@opentelemetry/api"; +import { LogRecordProcessor, SdkLogRecord } from "@opentelemetry/sdk-logs"; import { Span, SpanProcessor } from "@opentelemetry/sdk-trace-base"; import { SemanticInternalAttributes } from "../semanticInternalAttributes.js"; -import { Context } from "@opentelemetry/api"; -import { flattenAttributes } from "../utils/flattenAttributes.js"; import { taskContext } from "../task-context-api.js"; -import { Tracer } from "@opentelemetry/api"; +import { flattenAttributes } from "../utils/flattenAttributes.js"; export class TaskContextSpanProcessor implements SpanProcessor { private _innerProcessor: SpanProcessor; private _tracer: Tracer; - constructor(tracer: Tracer, innerProcessor: SpanProcessor) { - this._tracer = tracer; + constructor(version: string, innerProcessor: SpanProcessor) { + this._tracer = trace.getTracer("trigger-dev-worker", version); this._innerProcessor = innerProcessor; } @@ -91,7 +90,7 @@ export class TaskContextLogProcessor implements LogRecordProcessor { forceFlush(): Promise { return this._innerProcessor.forceFlush(); } - onEmit(logRecord: LogRecord, context?: Context | undefined): void { + onEmit(logRecord: SdkLogRecord, context?: Context | undefined): void { // Adds in the context attributes to the log record if (taskContext.ctx) { logRecord.setAttributes( diff --git a/packages/core/src/v3/trace-context-api.ts b/packages/core/src/v3/trace-context-api.ts new file mode 100644 index 000000000..1a4333fbe --- /dev/null +++ b/packages/core/src/v3/trace-context-api.ts @@ -0,0 +1,5 @@ +// Split module-level variable definition into separate files to allow +// tree-shaking on each api instance. +import { TraceContextAPI } from "./traceContext/api.js"; +/** Entrypoint for trace context API */ +export const traceContext = TraceContextAPI.getInstance(); diff --git a/packages/core/src/v3/traceContext/api.ts b/packages/core/src/v3/traceContext/api.ts new file mode 100644 index 000000000..3b21e0668 --- /dev/null +++ b/packages/core/src/v3/traceContext/api.ts @@ -0,0 +1,74 @@ +import { context, Context } from "@opentelemetry/api"; +import { getGlobal, registerGlobal, unregisterGlobal } from "../utils/globals.js"; +import { TraceContextManager } from "./types.js"; + +const API_NAME = "trace-context"; + +class NoopTraceContextManager implements TraceContextManager { + getTraceContext() { + return {}; + } + + reset() {} + + getExternalTraceContext() { + return undefined; + } + + extractContext(): Context { + return context.active(); + } + + withExternalTrace(fn: () => T): T { + return fn(); + } +} + +const NOOP_TRACE_CONTEXT_MANAGER = new NoopTraceContextManager(); + +export class TraceContextAPI implements TraceContextManager { + private static _instance?: TraceContextAPI; + + private constructor() {} + + public static getInstance(): TraceContextAPI { + if (!this._instance) { + this._instance = new TraceContextAPI(); + } + + return this._instance; + } + + public setGlobalManager(manager: TraceContextManager): boolean { + return registerGlobal(API_NAME, manager); + } + + public disable() { + unregisterGlobal(API_NAME); + } + + public reset() { + this.#getManager().reset(); + this.disable(); + } + + public getTraceContext() { + return this.#getManager().getTraceContext(); + } + + public getExternalTraceContext() { + return this.#getManager().getExternalTraceContext(); + } + + public extractContext() { + return this.#getManager().extractContext(); + } + + public withExternalTrace(fn: () => T): T { + return this.#getManager().withExternalTrace(fn); + } + + #getManager(): TraceContextManager { + return getGlobal(API_NAME) ?? NOOP_TRACE_CONTEXT_MANAGER; + } +} diff --git a/packages/core/src/v3/traceContext/manager.ts b/packages/core/src/v3/traceContext/manager.ts new file mode 100644 index 000000000..aa7f92b74 --- /dev/null +++ b/packages/core/src/v3/traceContext/manager.ts @@ -0,0 +1,77 @@ +import { Context, context, propagation, trace, TraceFlags } from "@opentelemetry/api"; +import { TraceContextManager } from "./types.js"; + +export class StandardTraceContextManager implements TraceContextManager { + public traceContext: Record = {}; + + getTraceContext() { + return this.traceContext; + } + + reset() { + this.traceContext = {}; + } + + getExternalTraceContext() { + return extractExternalTraceContext(this.traceContext?.external); + } + + extractContext(): Context { + return propagation.extract(context.active(), this.traceContext ?? {}); + } + + withExternalTrace(fn: () => T): T { + const externalTraceContext = this.getExternalTraceContext(); + + if (!externalTraceContext) { + return fn(); + } + + // Get the current active span context to extract the span ID + const currentSpanContext = trace.getActiveSpan()?.spanContext(); + + if (!currentSpanContext) { + throw new Error( + "No active span found. withExternalSpan must be called within an active span context." + ); + } + + const spanContext = { + traceId: externalTraceContext.traceId, + spanId: currentSpanContext.spanId, + traceFlags: TraceFlags.SAMPLED, + isRemote: true, + }; + + const contextWithSpan = trace.setSpanContext(context.active(), spanContext); + + return context.with(contextWithSpan, fn); + } +} + +function extractExternalTraceContext(traceContext: unknown) { + if (typeof traceContext !== "object" || traceContext === null) { + return undefined; + } + + const tracestate = + "tracestate" in traceContext && typeof traceContext.tracestate === "string" + ? traceContext.tracestate + : undefined; + + if ("traceparent" in traceContext && typeof traceContext.traceparent === "string") { + const [version, traceId, spanId] = traceContext.traceparent.split("-"); + + if (!traceId || !spanId) { + return undefined; + } + + return { + traceId, + spanId, + tracestate: tracestate, + }; + } + + return undefined; +} diff --git a/packages/core/src/v3/traceContext/types.ts b/packages/core/src/v3/traceContext/types.ts new file mode 100644 index 000000000..a1130cdf5 --- /dev/null +++ b/packages/core/src/v3/traceContext/types.ts @@ -0,0 +1,15 @@ +import { Context } from "@opentelemetry/api"; + +export interface TraceContextManager { + getTraceContext(): Record; + extractContext(): Context; + reset(): void; + getExternalTraceContext(): + | { + traceId: string; + spanId: string; + tracestate?: string; + } + | undefined; + withExternalTrace(fn: () => T): T; +} diff --git a/packages/core/src/v3/tracer.ts b/packages/core/src/v3/tracer.ts index 4adf8268b..5b2139175 100644 --- a/packages/core/src/v3/tracer.ts +++ b/packages/core/src/v3/tracer.ts @@ -63,10 +63,6 @@ export class TriggerTracer { return this._logger; } - extractContext(traceContext?: Record) { - return propagation.extract(context.active(), traceContext ?? {}); - } - startActiveSpan( name: string, fn: (span: Span) => Promise, diff --git a/packages/core/src/v3/utils/globals.ts b/packages/core/src/v3/utils/globals.ts index e59539b34..570bb3415 100644 --- a/packages/core/src/v3/utils/globals.ts +++ b/packages/core/src/v3/utils/globals.ts @@ -8,6 +8,7 @@ import type { RuntimeManager } from "../runtime/manager.js"; import { RunTimelineMetricsManager } from "../runTimelineMetrics/types.js"; import { TaskContext } from "../taskContext/types.js"; import { TimeoutManager } from "../timeout/types.js"; +import { TraceContextManager } from "../traceContext/types.js"; import { UsageManager } from "../usage/types.js"; import { WaitUntilManager } from "../waitUntil/types.js"; import { _globalThis } from "./platform.js"; @@ -66,4 +67,5 @@ type TriggerDotDevGlobalAPI = { ["run-timeline-metrics"]?: RunTimelineMetricsManager; ["lifecycle-hooks"]?: LifecycleHooksManager; ["locals"]?: LocalsManager; + ["trace-context"]?: TraceContextManager; }; diff --git a/packages/core/src/v3/workers/index.ts b/packages/core/src/v3/workers/index.ts index 5ff626f2e..613fe3302 100644 --- a/packages/core/src/v3/workers/index.ts +++ b/packages/core/src/v3/workers/index.ts @@ -28,3 +28,4 @@ export { WarmStartClient, type WarmStartClientOptions } from "../workers/warmSta export { StandardLifecycleHooksManager } from "../lifecycleHooks/manager.js"; export { StandardLocalsManager } from "../locals/manager.js"; export { populateEnv } from "./populateEnv.js"; +export { StandardTraceContextManager } from "../traceContext/manager.js"; diff --git a/packages/core/src/v3/workers/taskExecutor.ts b/packages/core/src/v3/workers/taskExecutor.ts index eafe00b1f..38e7d7c19 100644 --- a/packages/core/src/v3/workers/taskExecutor.ts +++ b/packages/core/src/v3/workers/taskExecutor.ts @@ -1,5 +1,5 @@ -import { Context, context, SpanKind, trace } from "@opentelemetry/api"; -import { VERSION } from "../../version.js"; +import { Context, context, SpanKind } from "@opentelemetry/api"; +import { promiseWithResolvers } from "../../utils.js"; import { ApiError, RateLimitError } from "../apiClient/errors.js"; import { ConsoleInterceptor } from "../consoleInterceptor.js"; import { @@ -17,6 +17,7 @@ import { lifecycleHooks, OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, runMetadata, + traceContext, waitUntil, } from "../index.js"; import { @@ -31,7 +32,6 @@ import { runTimelineMetrics } from "../run-timeline-metrics-api.js"; import { COLD_VARIANT, RetryOptions, - ServerBackgroundWorker, TaskRunContext, TaskRunErrorCodes, TaskRunExecution, @@ -40,7 +40,6 @@ import { WARM_VARIANT, } from "../schemas/index.js"; import { SemanticInternalAttributes } from "../semanticInternalAttributes.js"; -import { taskContext } from "../task-context-api.js"; import { TriggerTracer } from "../tracer.js"; import { tryCatch } from "../tryCatch.js"; import { HandleErrorModificationOptions, TaskMetadataWithFunctions } from "../types/index.js"; @@ -52,7 +51,6 @@ import { stringifyIO, } from "../utils/ioSerialization.js"; import { calculateNextRetryDelay } from "../utils/retries.js"; -import { promiseWithResolvers } from "../../utils.js"; export type TaskExecutorOptions = { tracingSDK: TracingSDK; @@ -93,12 +91,9 @@ export class TaskExecutor { async execute( execution: TaskRunExecution, - worker: ServerBackgroundWorker, - traceContext: Record, - signal: AbortSignal, - isWarmStart?: boolean + ctx: TaskRunContext, + signal: AbortSignal ): Promise<{ result: TaskRunExecutionResult }> { - const ctx = TaskRunContext.parse(execution); const attemptMessage = `Attempt ${execution.attempt.number}`; const originalPacket = { @@ -106,22 +101,10 @@ export class TaskExecutor { dataType: execution.run.payloadType, }; - taskContext.setGlobalTaskContext({ - ctx, - worker, - isWarmStart: isWarmStart ?? this._isWarmStart, - }); - if (execution.run.metadata) { runMetadata.enterWithMetadata(execution.run.metadata); } - if (!this._tracingSDK.asyncResourceDetector.isResolved) { - this._tracingSDK.asyncResourceDetector.resolveWithAttributes({ - ...taskContext.resourceAttributes, - }); - } - const result = await this._tracer.startActiveSpan( attemptMessage, async (span) => { @@ -369,7 +352,7 @@ export class TaskExecutor { ? runTimelineMetrics.convertMetricsToSpanEvents() : undefined, }, - this._tracer.extractContext(traceContext), + traceContext.extractContext(), signal ); diff --git a/packages/core/test/taskExecutor.test.ts b/packages/core/test/taskExecutor.test.ts index d78cc57cd..229a952ff 100644 --- a/packages/core/test/taskExecutor.test.ts +++ b/packages/core/test/taskExecutor.test.ts @@ -1942,5 +1942,5 @@ function executeTask( const $signal = signal ? signal : new AbortController().signal; - return executor.execute(execution, worker, {}, $signal); + return executor.execute(execution, execution, $signal); } diff --git a/packages/trigger-sdk/package.json b/packages/trigger-sdk/package.json index 8b2ce0db4..d070d2049 100644 --- a/packages/trigger-sdk/package.json +++ b/packages/trigger-sdk/package.json @@ -50,8 +50,7 @@ }, "dependencies": { "@opentelemetry/api": "1.9.0", - "@opentelemetry/api-logs": "0.52.1", - "@opentelemetry/semantic-conventions": "1.25.1", + "@opentelemetry/semantic-conventions": "1.36.0", "@trigger.dev/core": "workspace:4.0.0-v4-beta.26", "chalk": "^5.2.0", "cronstrue": "^2.21.0", diff --git a/packages/trigger-sdk/src/v3/index.ts b/packages/trigger-sdk/src/v3/index.ts index 4527cd6d0..94e3e23cd 100644 --- a/packages/trigger-sdk/src/v3/index.ts +++ b/packages/trigger-sdk/src/v3/index.ts @@ -13,6 +13,7 @@ export * from "./metadata.js"; export * from "./timeout.js"; export * from "./webhooks.js"; export * from "./locals.js"; +export * from "./otel.js"; export type { Context }; import type { Context } from "./shared.js"; diff --git a/packages/trigger-sdk/src/v3/otel.ts b/packages/trigger-sdk/src/v3/otel.ts new file mode 100644 index 000000000..e80c77562 --- /dev/null +++ b/packages/trigger-sdk/src/v3/otel.ts @@ -0,0 +1,7 @@ +import { traceContext } from "@trigger.dev/core/v3"; + +export const otel = { + withExternalTrace: (fn: () => T): T => { + return traceContext.withExternalTrace(fn); + }, +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90b137195..0c91127b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -304,7 +304,7 @@ importers: version: 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + version: 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-express': specifier: ^0.36.1 version: 0.36.1(@opentelemetry/api@1.9.0) @@ -322,7 +322,7 @@ importers: version: 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-node': specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + version: 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': specifier: 1.25.1 version: 1.25.1(@opentelemetry/api@1.9.0) @@ -1276,38 +1276,26 @@ importers: specifier: 1.9.0 version: 1.9.0 '@opentelemetry/api-logs': - specifier: 0.52.1 - version: 0.52.1 - '@opentelemetry/exporter-logs-otlp-http': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0) + specifier: 0.203.0 + version: 0.203.0 '@opentelemetry/exporter-trace-otlp-http': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0) + specifier: 0.203.0 + version: 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + specifier: 0.203.0 + version: 0.203.0(@opentelemetry/api@1.9.0)(supports-color@10.0.0) '@opentelemetry/instrumentation-fetch': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + specifier: 0.203.0 + version: 0.203.0(@opentelemetry/api@1.9.0)(supports-color@10.0.0) '@opentelemetry/resources': - specifier: 1.25.1 - version: 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-logs': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-node': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0) - '@opentelemetry/sdk-trace-base': - specifier: 1.25.1 - version: 1.25.1(@opentelemetry/api@1.9.0) + specifier: 2.0.1 + version: 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-node': - specifier: 1.25.1 - version: 1.25.1(@opentelemetry/api@1.9.0) + specifier: 2.0.1 + version: 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': - specifier: 1.25.1 - version: 1.25.1 + specifier: 1.36.0 + version: 1.36.0 '@trigger.dev/build': specifier: workspace:4.0.0-v4-beta.26 version: link:../build @@ -1526,38 +1514,35 @@ importers: specifier: 1.9.0 version: 1.9.0 '@opentelemetry/api-logs': - specifier: 0.52.1 - version: 0.52.1 + specifier: 0.203.0 + version: 0.203.0 '@opentelemetry/core': - specifier: ^1.30.1 - version: 1.30.1(@opentelemetry/api@1.9.0) + specifier: 2.0.1 + version: 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-logs-otlp-http': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0) + specifier: 0.203.0 + version: 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-trace-otlp-http': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0) + specifier: 0.203.0 + version: 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + specifier: 0.203.0 + version: 0.203.0(@opentelemetry/api@1.9.0)(supports-color@10.0.0) '@opentelemetry/resources': - specifier: 1.25.1 - version: 1.25.1(@opentelemetry/api@1.9.0) + specifier: 2.0.1 + version: 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-logs': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-node': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + specifier: 0.203.0 + version: 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': - specifier: 1.25.1 - version: 1.25.1(@opentelemetry/api@1.9.0) + specifier: 2.0.1 + version: 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-node': - specifier: 1.25.1 - version: 1.25.1(@opentelemetry/api@1.9.0) + specifier: 2.0.1 + version: 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': - specifier: 1.25.1 - version: 1.25.1 + specifier: 1.36.0 + version: 1.36.0 dequal: specifier: ^2.0.3 version: 2.0.3 @@ -1815,12 +1800,9 @@ importers: '@opentelemetry/api': specifier: 1.9.0 version: 1.9.0 - '@opentelemetry/api-logs': - specifier: 0.52.1 - version: 0.52.1 '@opentelemetry/semantic-conventions': - specifier: 1.25.1 - version: 1.25.1 + specifier: 1.36.0 + version: 1.36.0 '@trigger.dev/core': specifier: workspace:4.0.0-v4-beta.26 version: link:../core @@ -1913,12 +1895,24 @@ importers: '@e2b/code-interpreter': specifier: ^1.1.0 version: 1.1.0 + '@opentelemetry/api': + specifier: ^1.9.0 + version: 1.9.0 + '@opentelemetry/api-logs': + specifier: ^0.203.0 + version: 0.203.0 '@opentelemetry/exporter-logs-otlp-http': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0) + specifier: 0.203.0 + version: 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-trace-otlp-http': - specifier: 0.52.1 - version: 0.52.1(@opentelemetry/api@1.9.0) + specifier: 0.203.0 + version: 0.203.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': + specifier: ^0.203.0 + version: 0.203.0(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + '@opentelemetry/sdk-logs': + specifier: ^0.203.0 + version: 0.203.0(@opentelemetry/api@1.9.0) '@radix-ui/react-avatar': specifier: ^1.1.3 version: 1.1.3(@types/react-dom@19.0.4)(@types/react@19.0.12)(react-dom@19.0.0)(react@19.0.0) @@ -1934,6 +1928,9 @@ importers: '@trigger.dev/sdk': specifier: workspace:* version: link:../../packages/trigger-sdk + '@vercel/otel': + specifier: ^1.13.0 + version: 1.13.0(@opentelemetry/api-logs@0.203.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.203.0)(@opentelemetry/resources@1.25.1)(@opentelemetry/sdk-logs@0.203.0)(@opentelemetry/sdk-metrics@1.25.1)(@opentelemetry/sdk-trace-base@1.25.1) '@vercel/postgres': specifier: ^0.10.0 version: 0.10.0 @@ -2469,7 +2466,7 @@ importers: version: 1.25.1(@opentelemetry/api@1.4.1) '@opentelemetry/sdk-logs': specifier: ^0.49.1 - version: 0.49.1(@opentelemetry/api-logs@0.52.1)(@opentelemetry/api@1.4.1) + version: 0.49.1(@opentelemetry/api-logs@0.203.0)(@opentelemetry/api@1.4.1) '@opentelemetry/sdk-node': specifier: ^0.49.1 version: 0.49.1(@opentelemetry/api@1.4.1) @@ -3019,7 +3016,7 @@ packages: dependencies: '@andrewbranch/untar.js': 1.0.3 fflate: 0.8.2 - semver: 7.6.3 + semver: 7.7.2 ts-expose-internals-conditionally: 1.0.0-empty.0 typescript: 5.3.3 validate-npm-package-name: 5.0.0 @@ -4817,7 +4814,7 @@ packages: '@babel/traverse': 7.24.7 '@babel/types': 7.24.0 convert-source-map: 1.9.0 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -4839,7 +4836,7 @@ packages: '@babel/traverse': 7.24.7 '@babel/types': 7.27.0 convert-source-map: 2.0.0 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -5416,7 +5413,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.24.7 '@babel/types': 7.24.0 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -5434,7 +5431,7 @@ packages: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.27.0 '@babel/types': 7.27.0 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -5448,7 +5445,7 @@ packages: '@babel/parser': 7.27.0 '@babel/template': 7.25.0 '@babel/types': 7.27.0 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -5544,7 +5541,7 @@ packages: outdent: 0.5.0 prettier: 2.8.8 resolve-from: 5.0.0 - semver: 7.6.3 + semver: 7.7.2 dev: false /@changesets/assemble-release-plan@5.2.4(patch_hash=3wuhjtl4hjck4itk3w32z4cd5u): @@ -5555,7 +5552,7 @@ packages: '@changesets/get-dependents-graph': 1.3.6 '@changesets/types': 5.2.1 '@manypkg/get-packages': 1.1.3 - semver: 7.6.3 + semver: 7.7.2 dev: false patched: true @@ -5629,7 +5626,7 @@ packages: '@manypkg/get-packages': 1.1.3 chalk: 2.4.2 fs-extra: 7.0.1 - semver: 7.6.3 + semver: 7.7.2 dev: false /@changesets/get-github-info@0.5.2: @@ -7955,7 +7952,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 espree: 9.6.0 globals: 13.19.0 ignore: 5.2.4 @@ -8205,13 +8202,6 @@ packages: '@grpc/proto-loader': 0.7.13 '@js-sdsl/ordered-map': 4.4.2 - /@grpc/grpc-js@1.8.17: - resolution: {integrity: sha512-DGuSbtMFbaRsyffMf+VEkVu8HkSXEUfO3UyGJNtqxW9ABdtTIA+2UXAJpwbJS+xfQxuwqLUeELmL6FuZkOqPxw==} - engines: {node: ^8.13.0 || >=10.10.0} - dependencies: - '@grpc/proto-loader': 0.7.7 - '@types/node': 20.14.14 - /@grpc/proto-loader@0.7.13: resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} engines: {node: '>=6'} @@ -8222,17 +8212,6 @@ packages: protobufjs: 7.3.2 yargs: 17.7.2 - /@grpc/proto-loader@0.7.7: - resolution: {integrity: sha512-1TIeXOi8TuSCQprPItwoMymZXxWT0CPxUhkrkeCUH+D8U7QDwQ6b7SUz2MaLuWM2llT+J/TVFLmQI5KtML3BhQ==} - engines: {node: '>=6'} - hasBin: true - dependencies: - '@types/long': 4.0.2 - lodash.camelcase: 4.3.0 - long: 4.0.0 - protobufjs: 7.3.2 - yargs: 17.7.2 - /@hapi/boom@10.0.1: resolution: {integrity: sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==} dependencies: @@ -8332,7 +8311,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -9679,7 +9658,7 @@ packages: resolution: {integrity: sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: - semver: 7.6.3 + semver: 7.7.2 /@npmcli/git@4.1.0: resolution: {integrity: sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==} @@ -9691,7 +9670,7 @@ packages: proc-log: 3.0.0 promise-inflight: 1.0.1 promise-retry: 2.0.1 - semver: 7.6.3 + semver: 7.7.2 which: 3.0.1 transitivePeerDependencies: - bluebird @@ -9718,7 +9697,7 @@ packages: json-parse-even-better-errors: 3.0.0 normalize-package-data: 5.0.0 proc-log: 3.0.0 - semver: 7.6.3 + semver: 7.7.2 transitivePeerDependencies: - bluebird dev: true @@ -9748,6 +9727,12 @@ packages: resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} dev: false + /@opentelemetry/api-logs@0.203.0: + resolution: {integrity: sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==} + engines: {node: '>=8.0.0'} + dependencies: + '@opentelemetry/api': 1.9.0 + /@opentelemetry/api-logs@0.49.1: resolution: {integrity: sha512-kaNl/T7WzyMUQHQlVq7q0oV4Kev6+0xFwqzofryC66jgGMacd0QH5TwfpbUwSTby+SdAdprAe5UKMvBw4tKS5Q==} engines: {node: '>=14'} @@ -9766,6 +9751,7 @@ packages: engines: {node: '>=14'} dependencies: '@opentelemetry/api': 1.9.0 + dev: false /@opentelemetry/api-logs@0.57.0: resolution: {integrity: sha512-l1aJ30CXeauVYaI+btiynHpw341LthkMTv3omi1VJDX14werY2Wmv9n1yudMsq9HuY0m8PvXEVX4d8zxEb+WRg==} @@ -9830,6 +9816,15 @@ packages: '@opentelemetry/api': 1.9.0 dev: false + /@opentelemetry/context-async-hooks@2.0.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-XuY23lSI3d4PEqKA+7SLtAgwqIfc6E/E9eAQWLN1vlpC53ybO3o6jW4BsXo1xvz9lYyyWItfQDDLzezER01mCw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.9.0 + dev: false + /@opentelemetry/core@1.22.0(@opentelemetry/api@1.4.1): resolution: {integrity: sha512-0VoAlT6x+Xzik1v9goJ3pZ2ppi6+xd3aUfg4brfrLkDBHRIVjMP0eBHrKrhB+NKcDyMAg8fAbGL3Npg/F6AwWA==} engines: {node: '>=14'} @@ -9889,6 +9884,30 @@ packages: '@opentelemetry/semantic-conventions': 1.28.0 dev: false + /@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.36.0 + dev: false + + /@opentelemetry/exporter-logs-otlp-http@0.203.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-s0hys1ljqlMTbXx2XiplmMJg9wG570Z5lH7wMvrZX6lcODI56sG4HL03jklF63tBeyNwK2RV1/ntXGo3HgG4Qw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.203.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.203.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.203.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.203.0(@opentelemetry/api@1.9.0) + dev: false + /@opentelemetry/exporter-logs-otlp-http@0.49.1(@opentelemetry/api@1.4.1): resolution: {integrity: sha512-3QoBnIGCmEkujynUP0mK155QtOM0MSf9FNrEw7u9ieCFsoMiyatg2hPp+alEDONJ8N8wGEK+wP2q3icgXBiggw==} engines: {node: '>=14'} @@ -9952,7 +9971,7 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 dependencies: - '@grpc/grpc-js': 1.8.17 + '@grpc/grpc-js': 1.12.6 '@opentelemetry/api': 1.4.1 '@opentelemetry/core': 1.22.0(@opentelemetry/api@1.4.1) '@opentelemetry/otlp-grpc-exporter-base': 0.49.1(@opentelemetry/api@1.4.1) @@ -9967,7 +9986,7 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 dependencies: - '@grpc/grpc-js': 1.8.17 + '@grpc/grpc-js': 1.12.6 '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/otlp-grpc-exporter-base': 0.52.1(@opentelemetry/api@1.9.0) @@ -9976,6 +9995,20 @@ packages: '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) dev: false + /@opentelemetry/exporter-trace-otlp-http@0.203.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-ZDiaswNYo0yq/cy1bBLJFe691izEJ6IgNmkjm4C6kE9ub/OMQqDXORx2D2j8fzTBTxONyzusbaZlqtfmyqURPw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.203.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.203.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + dev: false + /@opentelemetry/exporter-trace-otlp-http@0.49.1(@opentelemetry/api@1.4.1): resolution: {integrity: sha512-KOLtZfZvIrpGZLVvblKsiVQT7gQUZNKcUUH24Zz6Xbi7LJb9Vt6xtUZFYdR5IIjvt47PIqBKDWUQlU0o1wAsRw==} engines: {node: '>=14'} @@ -10080,7 +10113,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 transitivePeerDependencies: @@ -10094,7 +10127,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 '@types/connect': 3.4.38 @@ -10123,7 +10156,7 @@ packages: '@opentelemetry/api': 1.4.1 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.4.1) '@opentelemetry/instrumentation': 0.49.1(@opentelemetry/api@1.4.1) - '@opentelemetry/semantic-conventions': 1.25.1 + '@opentelemetry/semantic-conventions': 1.36.0 transitivePeerDependencies: - supports-color dev: true @@ -10149,13 +10182,28 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 transitivePeerDependencies: - supports-color dev: false + /@opentelemetry/instrumentation-fetch@0.203.0(@opentelemetry/api@1.9.0)(supports-color@10.0.0): + resolution: {integrity: sha512-Z+mls3rOP2BaVykDZLLZPvchjj9l2oj3dYG1GTnrc27Y8o3biE+5M1b0izblycbbQHXjMPHQCpmjHbLMQuWtBg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + '@opentelemetry/sdk-trace-web': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + dev: false + /@opentelemetry/instrumentation-fetch@0.49.1(@opentelemetry/api@1.4.1): resolution: {integrity: sha512-hizhULZXlq02y8YC0vPQ4WtUWiXcwxPdEqHBy8p75jzF9rAuP/ldrVr0Oxvz5Xr9qQcdEOFLvEl0ZxbVL76WKw==} engines: {node: '>=14'} @@ -10171,21 +10219,6 @@ packages: - supports-color dev: true - /@opentelemetry/instrumentation-fetch@0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0): - resolution: {integrity: sha512-EJDQXdv1ZGyBifox+8BK+hP0tg29abNPdScE+lW77bUVrThD5vn2dOo+blAS3Z8Od+eqTUTDzXVDIFjGgTK01w==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.0.0 - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0) - '@opentelemetry/sdk-trace-web': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.1 - transitivePeerDependencies: - - supports-color - dev: false - /@opentelemetry/instrumentation-fs@0.19.1(@opentelemetry/api@1.9.0): resolution: {integrity: sha512-6g0FhB3B9UobAR60BGTcXg4IHZ6aaYJzp0Ki5FhnxyAPt8Ns+9SSvgcrnsN2eGmk3RWG5vYycUGOEApycQL24A==} engines: {node: '>=14'} @@ -10193,7 +10226,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -10230,7 +10263,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 transitivePeerDependencies: @@ -10260,7 +10293,7 @@ packages: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 semver: 7.6.3 transitivePeerDependencies: @@ -10330,7 +10363,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 transitivePeerDependencies: @@ -10369,7 +10402,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 transitivePeerDependencies: @@ -10456,7 +10489,7 @@ packages: '@opentelemetry/api': ^1.7.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -10475,6 +10508,20 @@ packages: - supports-color dev: true + /@opentelemetry/instrumentation@0.203.0(@opentelemetry/api@1.9.0)(supports-color@10.0.0): + resolution: {integrity: sha512-ke1qyM+3AK2zPuBPb6Hk/GCsc5ewbLvPNkEuELx/JmANeEp6ZjnZ+wypPAJSucTw0wvCGrUaibDSdcrGFoWxKQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.203.0 + import-in-the-middle: 1.11.0 + require-in-the-middle: 7.1.1(supports-color@10.0.0) + transitivePeerDependencies: + - supports-color + dev: false + /@opentelemetry/instrumentation@0.49.1(@opentelemetry/api@1.4.1): resolution: {integrity: sha512-0DLtWtaIppuNNRRllSD4bjU8ZIiLp1cDXvJEbp752/Zf+y3gaLNaoGRGIlX4UHhcsrmtL+P2qxi3Hodi8VuKiQ==} engines: {node: '>=14'} @@ -10534,10 +10581,10 @@ packages: dependencies: '@opentelemetry/api': 1.4.1 '@opentelemetry/api-logs': 0.51.1 - '@types/shimmer': 1.0.2 + '@types/shimmer': 1.2.0 import-in-the-middle: 1.7.4 require-in-the-middle: 7.1.1(supports-color@10.0.0) - semver: 7.6.3 + semver: 7.7.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color @@ -10551,16 +10598,16 @@ packages: dependencies: '@opentelemetry/api': 1.4.1 '@opentelemetry/api-logs': 0.52.1 - '@types/shimmer': 1.0.2 + '@types/shimmer': 1.2.0 import-in-the-middle: 1.11.0 require-in-the-middle: 7.1.1(supports-color@10.0.0) - semver: 7.6.3 + semver: 7.7.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0): + /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0): resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} engines: {node: '>=14'} peerDependencies: @@ -10568,10 +10615,10 @@ packages: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.52.1 - '@types/shimmer': 1.0.2 + '@types/shimmer': 1.2.0 import-in-the-middle: 1.11.0 require-in-the-middle: 7.1.1(supports-color@10.0.0) - semver: 7.6.3 + semver: 7.7.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color @@ -10594,6 +10641,17 @@ packages: - supports-color dev: false + /@opentelemetry/otlp-exporter-base@0.203.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-Wbxf7k+87KyvxFr5D7uOiSq/vHXWommvdnNE7vECO3tAhsA2GfOlpWINCMWUEPdHZ7tCXxw6Epp3vgx3jU7llQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.203.0(@opentelemetry/api@1.9.0) + dev: false + /@opentelemetry/otlp-exporter-base@0.49.1(@opentelemetry/api@1.4.1): resolution: {integrity: sha512-z6sHliPqDgJU45kQatAettY9/eVF58qVPaTuejw9YWfSRqid9pXPYeegDCSdyS47KAUgAtm+nC28K3pfF27HWg==} engines: {node: '>=14'} @@ -10632,7 +10690,7 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 dependencies: - '@grpc/grpc-js': 1.8.17 + '@grpc/grpc-js': 1.12.6 '@opentelemetry/api': 1.4.1 '@opentelemetry/core': 1.22.0(@opentelemetry/api@1.4.1) '@opentelemetry/otlp-exporter-base': 0.49.1(@opentelemetry/api@1.4.1) @@ -10645,7 +10703,7 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 dependencies: - '@grpc/grpc-js': 1.8.17 + '@grpc/grpc-js': 1.12.6 '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/otlp-exporter-base': 0.52.1(@opentelemetry/api@1.9.0) @@ -10664,6 +10722,22 @@ packages: protobufjs: 7.3.2 dev: true + /@opentelemetry/otlp-transformer@0.203.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-Y8I6GgoCna0qDQ2W6GCRtaF24SnvqvA8OfeTi7fqigD23u8Jpb4R5KFv/pRvrlGagcCLICMIyh9wiejp4TXu/A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.203.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.203.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + protobufjs: 7.3.2 + dev: false + /@opentelemetry/otlp-transformer@0.49.1(@opentelemetry/api@1.4.1): resolution: {integrity: sha512-Z+koA4wp9L9e3jkFacyXTGphSWTbOKjwwXMpb0CxNb0kjTHGUxhYRN8GnkLFsFo5NbZPjP07hwAqeEG/uCratQ==} engines: {node: '>=14'} @@ -10842,6 +10916,42 @@ packages: '@opentelemetry/semantic-conventions': 1.28.0 dev: false + /@opentelemetry/resources@2.0.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + dev: false + + /@opentelemetry/sdk-logs@0.203.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-vM2+rPq0Vi3nYA5akQD2f3QwossDnTDLvKbea6u/A2NZ3XDkPxMfo/PNrDoXhDUD/0pPo2CdH5ce/thn9K0kLw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.203.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + dev: false + + /@opentelemetry/sdk-logs@0.49.1(@opentelemetry/api-logs@0.203.0)(@opentelemetry/api@1.4.1): + resolution: {integrity: sha512-gCzYWsJE0h+3cuh3/cK+9UwlVFyHvj3PReIOCDOmdeXOp90ZjKRoDOJBc3mvk1LL6wyl1RWIivR8Rg9OToyesw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.9.0' + '@opentelemetry/api-logs': '>=0.39.1' + dependencies: + '@opentelemetry/api': 1.4.1 + '@opentelemetry/api-logs': 0.203.0 + '@opentelemetry/core': 1.22.0(@opentelemetry/api@1.4.1) + '@opentelemetry/resources': 1.22.0(@opentelemetry/api@1.4.1) + dev: true + /@opentelemetry/sdk-logs@0.49.1(@opentelemetry/api-logs@0.49.1)(@opentelemetry/api@1.4.1): resolution: {integrity: sha512-gCzYWsJE0h+3cuh3/cK+9UwlVFyHvj3PReIOCDOmdeXOp90ZjKRoDOJBc3mvk1LL6wyl1RWIivR8Rg9OToyesw==} engines: {node: '>=14'} @@ -10855,19 +10965,6 @@ packages: '@opentelemetry/resources': 1.22.0(@opentelemetry/api@1.4.1) dev: true - /@opentelemetry/sdk-logs@0.49.1(@opentelemetry/api-logs@0.52.1)(@opentelemetry/api@1.4.1): - resolution: {integrity: sha512-gCzYWsJE0h+3cuh3/cK+9UwlVFyHvj3PReIOCDOmdeXOp90ZjKRoDOJBc3mvk1LL6wyl1RWIivR8Rg9OToyesw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.4.0 <1.9.0' - '@opentelemetry/api-logs': '>=0.39.1' - dependencies: - '@opentelemetry/api': 1.4.1 - '@opentelemetry/api-logs': 0.52.1 - '@opentelemetry/core': 1.22.0(@opentelemetry/api@1.4.1) - '@opentelemetry/resources': 1.22.0(@opentelemetry/api@1.4.1) - dev: true - /@opentelemetry/sdk-logs@0.52.1(@opentelemetry/api@1.9.0): resolution: {integrity: sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==} engines: {node: '>=14'} @@ -10927,6 +11024,17 @@ packages: '@opentelemetry/resources': 1.30.0(@opentelemetry/api@1.9.0) dev: false + /@opentelemetry/sdk-metrics@2.0.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + dev: false + /@opentelemetry/sdk-node@0.49.1(@opentelemetry/api@1.4.1): resolution: {integrity: sha512-feBIT85ndiSHXsQ2gfGpXC/sNeX4GCHLksC4A9s/bfpUbbgbCSl0RvzZlmEpCHarNrkZMwFRi4H0xFfgvJEjrg==} engines: {node: '>=14'} @@ -10951,7 +11059,7 @@ packages: - supports-color dev: true - /@opentelemetry/sdk-node@0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0): + /@opentelemetry/sdk-node@0.52.1(@opentelemetry/api@1.9.0): resolution: {integrity: sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==} engines: {node: '>=14'} peerDependencies: @@ -10964,7 +11072,7 @@ packages: '@opentelemetry/exporter-trace-otlp-http': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-trace-otlp-proto': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-zipkin': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-logs': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 1.25.1(@opentelemetry/api@1.9.0) @@ -11047,6 +11155,18 @@ packages: '@opentelemetry/semantic-conventions': 1.28.0 dev: false + /@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + dev: false + /@opentelemetry/sdk-trace-node@1.22.0(@opentelemetry/api@1.4.1): resolution: {integrity: sha512-gTGquNz7ue8uMeiWPwp3CU321OstQ84r7PCDtOaCicjbJxzvO8RZMlEC4geOipTeiF88kss5n6w+//A0MhP1lQ==} engines: {node: '>=14'} @@ -11059,7 +11179,7 @@ packages: '@opentelemetry/propagator-b3': 1.22.0(@opentelemetry/api@1.4.1) '@opentelemetry/propagator-jaeger': 1.22.0(@opentelemetry/api@1.4.1) '@opentelemetry/sdk-trace-base': 1.22.0(@opentelemetry/api@1.4.1) - semver: 7.6.3 + semver: 7.7.2 dev: true /@opentelemetry/sdk-trace-node@1.25.1(@opentelemetry/api@1.4.1): @@ -11092,6 +11212,18 @@ packages: semver: 7.6.3 dev: false + /@opentelemetry/sdk-trace-node@2.0.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-UhdbPF19pMpBtCWYP5lHbTogLWx9N0EBxtdagvkn5YtsAnCBZzL7SjktG+ZmupRgifsHMjwUaCCaVmqGfSADmA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + dev: false + /@opentelemetry/sdk-trace-web@1.22.0(@opentelemetry/api@1.4.1): resolution: {integrity: sha512-id5bUhWYg475xbm4hjwWA4PnWM4duNK1EyFRkZxa3BZNuCITwiKCLvDkVhlE9RK2kvuDOPmcRxgSbU1apF9/1w==} engines: {node: '>=14'} @@ -11104,16 +11236,15 @@ packages: '@opentelemetry/semantic-conventions': 1.22.0 dev: true - /@opentelemetry/sdk-trace-web@1.25.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-SS6JaSkHngcBCNdWGthzcvaKGRnDw2AeP57HyTEileLToJ7WLMeV+064iRlVyoT4+e77MRp2T2dDSrmaUyxoNg==} - engines: {node: '>=14'} + /@opentelemetry/sdk-trace-web@2.0.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-R4/i0rISvAujG4Zwk3s6ySyrWG+Db3SerZVM4jZ2lEzjrNylF7nRAy1hVvWe8gTbwIxX+6w6ZvZwdtl2C7UQHQ==} + engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.1 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) dev: false /@opentelemetry/semantic-conventions@1.22.0: @@ -11132,7 +11263,6 @@ packages: /@opentelemetry/semantic-conventions@1.36.0: resolution: {integrity: sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ==} engines: {node: '>=14'} - dev: false /@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0): resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} @@ -11141,7 +11271,7 @@ packages: '@opentelemetry/api': ^1.1.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) dev: false /@pkgjs/parseargs@0.11.0: @@ -11330,7 +11460,7 @@ packages: '@opentelemetry/api': ^1.8 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color dev: false @@ -11427,7 +11557,7 @@ packages: engines: {node: '>=18'} hasBin: true dependencies: - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 @@ -11444,11 +11574,11 @@ packages: engines: {node: '>=18'} hasBin: true dependencies: - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.4.0 - semver: 7.6.3 + semver: 7.7.2 tar-fs: 3.0.9 unbzip2-stream: 1.4.3 yargs: 17.7.2 @@ -19552,7 +19682,7 @@ packages: dependencies: '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.4.1) '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.4.1) - '@opentelemetry/semantic-conventions': 1.28.0 + '@opentelemetry/semantic-conventions': 1.36.0 '@traceloop/ai-semantic-conventions': 0.10.0 js-tiktoken: 1.0.14 tslib: 2.6.2 @@ -19912,9 +20042,6 @@ packages: resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==} dev: true - /@types/long@4.0.2: - resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} - /@types/marked@4.0.8: resolution: {integrity: sha512-HVNzMT5QlWCOdeuBsgXP8EZzKUf0+AXzN+sLmjvaB3ZlLqO+e4u0uXrdw9ub69wBKFs+c6/pA4r9sy6cCDvImw==} dev: true @@ -20211,7 +20338,6 @@ packages: /@types/shimmer@1.2.0: resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} - dev: false /@types/simple-oauth2@5.0.4: resolution: {integrity: sha512-4SvTfmAa1fGUa1d07j9vIiC4o92bGh0ihPXmtS05udMMmNwVIaU2nZ706cC4wI8cJxOlHD4P/d5tzqvWYd+KxA==} @@ -20419,7 +20545,7 @@ packages: '@typescript-eslint/scope-manager': 5.59.6 '@typescript-eslint/types': 5.59.6 '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.5.4) - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 eslint: 8.31.0 typescript: 5.5.4 transitivePeerDependencies: @@ -20446,7 +20572,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.5.4) '@typescript-eslint/utils': 5.59.6(eslint@8.31.0)(typescript@5.5.4) - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 eslint: 8.31.0 tsutils: 3.21.0(typescript@5.5.4) typescript: 5.5.4 @@ -20470,10 +20596,10 @@ packages: dependencies: '@typescript-eslint/types': 5.59.6 '@typescript-eslint/visitor-keys': 5.59.6 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.6.3 + semver: 7.7.2 tsutils: 3.21.0(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: @@ -20494,7 +20620,7 @@ packages: '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.5.4) eslint: 8.31.0 eslint-scope: 5.1.1 - semver: 7.6.3 + semver: 7.7.2 transitivePeerDependencies: - supports-color - typescript @@ -20676,6 +20802,27 @@ packages: resolution: {integrity: sha512-17kVyLq3ePTKOkveHxXuIJZtGYs+cSoev7BlP+Lf4916qfDhk/HBjvlYDe8egrea7LNPHKwSZJK/bzZC+Q6AwQ==} dev: true + /@vercel/otel@1.13.0(@opentelemetry/api-logs@0.203.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.203.0)(@opentelemetry/resources@1.25.1)(@opentelemetry/sdk-logs@0.203.0)(@opentelemetry/sdk-metrics@1.25.1)(@opentelemetry/sdk-trace-base@1.25.1): + resolution: {integrity: sha512-esRkt470Y2jRK1B1g7S1vkt4Csu44gp83Zpu8rIyPoqy2BKgk4z7ik1uSMswzi45UogLHFl6yR5TauDurBQi4Q==} + engines: {node: '>=18'} + peerDependencies: + '@opentelemetry/api': '>=1.7.0 <2.0.0' + '@opentelemetry/api-logs': '>=0.46.0 <0.200.0' + '@opentelemetry/instrumentation': '>=0.46.0 <0.200.0' + '@opentelemetry/resources': '>=1.19.0 <2.0.0' + '@opentelemetry/sdk-logs': '>=0.46.0 <0.200.0' + '@opentelemetry/sdk-metrics': '>=1.19.0 <2.0.0' + '@opentelemetry/sdk-trace-base': '>=1.19.0 <2.0.0' + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.203.0 + '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0)(supports-color@10.0.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.203.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) + dev: false + /@vercel/postgres@0.10.0: resolution: {integrity: sha512-fSD23DxGND40IzSkXjcFcxr53t3Tiym59Is0jSYIFpG4/0f0KO9SGtcp1sXiebvPaGe7N/tU05cH4yt2S6/IPg==} engines: {node: '>=18.14'} @@ -20698,7 +20845,7 @@ packages: dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 @@ -21179,20 +21326,12 @@ packages: negotiator: 1.0.0 dev: false - /acorn-import-assertions@1.9.0(acorn@8.12.1): - resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} - peerDependencies: - acorn: ^8 - dependencies: - acorn: 8.12.1 - /acorn-import-assertions@1.9.0(acorn@8.14.1): resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} peerDependencies: acorn: ^8 dependencies: acorn: 8.14.1 - dev: false /acorn-import-attributes@1.9.5(acorn@8.12.1): resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} @@ -21222,7 +21361,6 @@ packages: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: acorn: 8.14.1 - dev: true /acorn-node@1.8.2: resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} @@ -21276,7 +21414,7 @@ packages: engines: {node: '>= 6.0.0'} requiresBuild: true dependencies: - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) transitivePeerDependencies: - supports-color @@ -21284,7 +21422,7 @@ packages: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} dependencies: - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) transitivePeerDependencies: - supports-color @@ -22216,7 +22354,7 @@ packages: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) http-errors: 2.0.0 iconv-lite: 0.6.3 on-finished: 2.4.1 @@ -22577,7 +22715,7 @@ packages: /capnp-ts@0.7.0: resolution: {integrity: sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==} dependencies: - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -23691,7 +23829,7 @@ packages: ms: 2.1.3 supports-color: 10.0.0 - /debug@4.4.0(supports-color@10.0.0): + /debug@4.4.0: resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} peerDependencies: @@ -23701,9 +23839,8 @@ packages: optional: true dependencies: ms: 2.1.3 - supports-color: 10.0.0 - /debug@4.4.1: + /debug@4.4.1(supports-color@10.0.0): resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} peerDependencies: @@ -23713,7 +23850,7 @@ packages: optional: true dependencies: ms: 2.1.3 - dev: false + supports-color: 10.0.0 /decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} @@ -23977,7 +24114,7 @@ packages: resolution: {integrity: sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==} engines: {node: '>= 8.0'} dependencies: - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 readable-stream: 3.6.0 split-ca: 1.0.1 ssh2: 1.16.0 @@ -25030,7 +25167,7 @@ packages: eslint: '*' eslint-plugin-import: '*' dependencies: - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 enhanced-resolve: 5.15.0 eslint: 8.31.0 eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.59.6)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0) @@ -25399,8 +25536,8 @@ packages: resolution: {integrity: sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) eslint-visitor-keys: 3.4.2 /esprima@4.0.1: @@ -25727,7 +25864,7 @@ packages: engines: {node: '>= 10.17.0'} hasBin: true dependencies: - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -25998,7 +26135,7 @@ packages: resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} engines: {node: '>= 0.8'} dependencies: - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -26479,7 +26616,7 @@ packages: dependencies: basic-ftp: 5.0.3 data-uri-to-buffer: 5.0.1 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) fs-extra: 8.1.0 transitivePeerDependencies: - supports-color @@ -26709,9 +26846,9 @@ packages: '@types/node': 20.14.14 '@types/semver': 7.5.1 chalk: 4.1.2 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 interpret: 3.1.1 - semver: 7.6.3 + semver: 7.7.2 tslib: 2.6.2 yargs: 17.7.2 transitivePeerDependencies: @@ -27008,7 +27145,7 @@ packages: dependencies: '@tootallnate/once': 1.1.2 agent-base: 6.0.2 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) transitivePeerDependencies: - supports-color dev: false @@ -27019,7 +27156,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.1 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) transitivePeerDependencies: - supports-color @@ -27028,7 +27165,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) transitivePeerDependencies: - supports-color dev: false @@ -27047,7 +27184,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -27056,7 +27193,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.1 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) transitivePeerDependencies: - supports-color dev: true @@ -27066,7 +27203,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.1 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) transitivePeerDependencies: - supports-color dev: false @@ -27076,7 +27213,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) transitivePeerDependencies: - supports-color dev: false @@ -27180,8 +27317,8 @@ packages: /import-in-the-middle@1.7.1: resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} dependencies: - acorn: 8.12.1 - acorn-import-assertions: 1.9.0(acorn@8.12.1) + acorn: 8.14.1 + acorn-import-assertions: 1.9.0(acorn@8.14.1) cjs-module-lexer: 1.2.3 module-details-from-path: 1.0.3 @@ -27770,7 +27907,7 @@ packages: engines: {node: '>=10'} dependencies: '@jridgewell/trace-mapping': 0.3.25 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -28522,9 +28659,6 @@ packages: chalk: 4.1.2 is-unicode-supported: 0.1.0 - /long@4.0.0: - resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} - /long@5.2.3: resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} @@ -29500,7 +29634,7 @@ packages: resolution: {integrity: sha512-6Mj0yHLdUZjHnOPgr5xfWIMqMWS12zDN6iws9SLuSz76W8jTtAv24MN4/CL7gJrl5vtxGInkkqDv/JIoRsQOvA==} dependencies: '@types/debug': 4.1.12 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) decode-named-character-reference: 1.0.2 micromark-core-commonmark: 1.0.6 micromark-factory-space: 1.0.0 @@ -29524,7 +29658,7 @@ packages: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} dependencies: '@types/debug': 4.1.12 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -29621,7 +29755,7 @@ packages: hasBin: true dependencies: '@cspotcode/source-map-support': 0.8.1 - acorn: 8.12.1 + acorn: 8.14.1 acorn-walk: 8.3.2 capnp-ts: 0.7.0 exit-hook: 2.2.1 @@ -30397,7 +30531,7 @@ packages: nopt: 5.0.0 npmlog: 6.0.2 rimraf: 3.0.2 - semver: 7.6.3 + semver: 7.7.2 tar: 6.2.1 which: 2.0.2 transitivePeerDependencies: @@ -30458,7 +30592,7 @@ packages: dependencies: hosted-git-info: 6.1.1 is-core-module: 2.14.0 - semver: 7.6.3 + semver: 7.7.2 validate-npm-package-license: 3.0.4 dev: true @@ -31134,7 +31268,7 @@ packages: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.1 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) get-uri: 6.0.1 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.5 @@ -31150,7 +31284,7 @@ packages: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) get-uri: 6.0.1 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -32357,7 +32491,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.1 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.5 lru-cache: 7.18.3 @@ -32373,7 +32507,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 lru-cache: 7.18.3 @@ -32435,7 +32569,7 @@ packages: dependencies: '@puppeteer/browsers': 2.4.0 chromium-bidi: 0.6.5(devtools-protocol@0.0.1342118) - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) devtools-protocol: 0.0.1342118 typed-query-selector: 2.12.0 ws: 8.18.0(bufferutil@4.0.9) @@ -32452,7 +32586,7 @@ packages: dependencies: '@puppeteer/browsers': 2.10.6 chromium-bidi: 7.2.0(devtools-protocol@0.0.1464554) - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) devtools-protocol: 0.0.1464554 typed-query-selector: 2.12.0 ws: 8.18.3 @@ -33546,7 +33680,7 @@ packages: remix-auth: ^3.6.0 dependencies: '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 remix-auth: 3.6.0(@remix-run/react@2.1.0)(@remix-run/server-runtime@2.1.0) transitivePeerDependencies: - supports-color @@ -33673,7 +33807,7 @@ packages: resolution: {integrity: sha512-OScOjQjrrjhAdFpQmnkE/qbIBGCRFhQB/YaJhcC3CPOlmhe7llnW46Ac1J5+EjcNXOTnDdpF96Erw/yedsGksQ==} engines: {node: '>=8.6.0'} dependencies: - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: @@ -34131,7 +34265,7 @@ packages: resolution: {integrity: sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==} engines: {node: '>= 18'} dependencies: - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) destroy: 1.2.0 encodeurl: 2.0.0 escape-html: 1.0.3 @@ -34237,7 +34371,7 @@ packages: dependencies: color: 4.2.3 detect-libc: 2.0.3 - semver: 7.6.3 + semver: 7.7.2 optionalDependencies: '@img/sharp-darwin-arm64': 0.33.5 '@img/sharp-darwin-x64': 0.33.5 @@ -34584,7 +34718,7 @@ packages: requiresBuild: true dependencies: agent-base: 6.0.2 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -34596,7 +34730,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.1 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -34607,7 +34741,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.4 - debug: 4.4.1 + debug: 4.4.1(supports-color@10.0.0) socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -35198,7 +35332,7 @@ packages: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 fast-safe-stringify: 2.1.1 form-data: 4.0.4 formidable: 3.5.1 @@ -35748,7 +35882,7 @@ packages: archiver: 7.0.1 async-lock: 1.4.1 byline: 5.0.0 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 docker-compose: 0.24.8 dockerode: 4.0.6 get-port: 7.1.0 @@ -36259,7 +36393,7 @@ packages: cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 esbuild: 0.25.1 joycon: 3.1.1 picocolors: 1.1.1 @@ -36904,7 +37038,7 @@ packages: /unplugin@1.0.1: resolution: {integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==} dependencies: - acorn: 8.12.1 + acorn: 8.14.1 chokidar: 3.6.0 webpack-sources: 3.2.3 webpack-virtual-modules: 0.5.0 @@ -37288,7 +37422,7 @@ packages: hasBin: true dependencies: cac: 6.7.14 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) mlly: 1.7.1 pathe: 1.1.2 picocolors: 1.1.1 @@ -37312,7 +37446,7 @@ packages: hasBin: true dependencies: cac: 6.7.14 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.1(supports-color@10.0.0) es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 5.2.7(@types/node@20.14.14) @@ -37481,7 +37615,7 @@ packages: '@vitest/spy': 3.1.4 '@vitest/utils': 3.1.4 chai: 5.2.0 - debug: 4.4.0(supports-color@10.0.0) + debug: 4.4.0 expect-type: 1.2.1 magic-string: 0.30.17 pathe: 2.0.3 @@ -37588,7 +37722,7 @@ packages: hasBin: true dependencies: '@discoveryjs/json-ext': 0.5.7 - acorn: 8.12.1 + acorn: 8.14.1 acorn-walk: 8.3.2 commander: 7.2.0 debounce: 1.2.1 diff --git a/references/d3-chat/package.json b/references/d3-chat/package.json index 36dc313f2..2d14a1d22 100644 --- a/references/d3-chat/package.json +++ b/references/d3-chat/package.json @@ -22,13 +22,18 @@ "@ai-sdk/anthropic": "^1.2.4", "@ai-sdk/openai": "1.3.3", "@e2b/code-interpreter": "^1.1.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/api-logs": "^0.203.0", + "@opentelemetry/exporter-logs-otlp-http": "0.203.0", + "@opentelemetry/exporter-trace-otlp-http": "0.203.0", + "@opentelemetry/instrumentation": "^0.203.0", + "@opentelemetry/sdk-logs": "^0.203.0", "@radix-ui/react-avatar": "^1.1.3", "@slack/web-api": "7.9.1", "@trigger.dev/python": "workspace:*", "@trigger.dev/react-hooks": "workspace:*", "@trigger.dev/sdk": "workspace:*", - "@opentelemetry/exporter-logs-otlp-http": "0.52.1", - "@opentelemetry/exporter-trace-otlp-http": "0.52.1", + "@vercel/otel": "^1.13.0", "@vercel/postgres": "^0.10.0", "ai": "4.2.5", "class-variance-authority": "^0.7.1", diff --git a/references/d3-chat/src/app/api/demo-batch-trigger/route.ts b/references/d3-chat/src/app/api/demo-batch-trigger/route.ts new file mode 100644 index 000000000..9342830c3 --- /dev/null +++ b/references/d3-chat/src/app/api/demo-batch-trigger/route.ts @@ -0,0 +1,18 @@ +import { NextResponse } from "next/server"; +import { tasks } from "@trigger.dev/sdk"; +import type { todoChat } from "@/trigger/chat"; + +export async function POST(request: Request) { + const body = await request.json(); + + const handle = await tasks.batchTrigger("todo-chat", [ + { + payload: { + input: body.input, + userId: "123", + }, + }, + ]); + + return NextResponse.json({ handle }); +} diff --git a/references/d3-chat/src/app/api/demo-call-from-trigger/route.ts b/references/d3-chat/src/app/api/demo-call-from-trigger/route.ts new file mode 100644 index 000000000..940c844d0 --- /dev/null +++ b/references/d3-chat/src/app/api/demo-call-from-trigger/route.ts @@ -0,0 +1,5 @@ +import { NextResponse } from "next/server"; + +export async function POST(request: Request) { + return NextResponse.json({ body: "Hello, world!" }); +} diff --git a/references/d3-chat/src/app/api/demo-trigger/route.ts b/references/d3-chat/src/app/api/demo-trigger/route.ts new file mode 100644 index 000000000..f97874f26 --- /dev/null +++ b/references/d3-chat/src/app/api/demo-trigger/route.ts @@ -0,0 +1,14 @@ +import { NextResponse } from "next/server"; +import { tasks } from "@trigger.dev/sdk"; +import type { todoChat } from "@/trigger/chat"; + +export async function POST(request: Request) { + const body = await request.json(); + + const handle = await tasks.trigger("todo-chat", { + input: body.input, + userId: "123", + }); + + return NextResponse.json({ handle }); +} diff --git a/references/d3-chat/src/instrumentation.ts b/references/d3-chat/src/instrumentation.ts new file mode 100644 index 000000000..ad3e54744 --- /dev/null +++ b/references/d3-chat/src/instrumentation.ts @@ -0,0 +1,5 @@ +import { registerOTel } from "@vercel/otel"; + +export function register() { + registerOTel({ serviceName: "d3-chat" }); +} diff --git a/references/d3-chat/src/trigger/chat.ts b/references/d3-chat/src/trigger/chat.ts index 7ac4d77f6..9870cbc17 100644 --- a/references/d3-chat/src/trigger/chat.ts +++ b/references/d3-chat/src/trigger/chat.ts @@ -1,7 +1,7 @@ import { anthropic } from "@ai-sdk/anthropic"; import { openai } from "@ai-sdk/openai"; import { ai } from "@trigger.dev/sdk/ai"; -import { logger, metadata, runs, schemaTask, tasks, wait } from "@trigger.dev/sdk/v3"; +import { logger, metadata, runs, schemaTask, tasks, wait, otel } from "@trigger.dev/sdk/v3"; import { sql } from "@vercel/postgres"; import { CoreMessage, @@ -17,6 +17,25 @@ import { sendSQLApprovalMessage } from "../lib/slack"; import { crawler } from "./crawler"; import { chartTool } from "./sandbox"; import { QueryApproval } from "./schemas"; +import { context, propagation } from "@opentelemetry/api"; + +async function callNextjsApp() { + return await otel.withExternalTrace(async () => { + const headersObject = {}; + + propagation.inject(context.active(), headersObject); + + const result = await fetch("http://localhost:3000/api/demo-call-from-trigger", { + headers: new Headers(headersObject), + method: "POST", + body: JSON.stringify({ + message: "Hello from Trigger.dev", + }), + }); + + return result.json(); + }); +} const queryApprovalTask = schemaTask({ id: "query-approval", @@ -29,6 +48,8 @@ const queryApprovalTask = schemaTask({ run: async ({ userId, input, query }) => { logger.info("queryApproval: starting", { projectRef: process.env.TRIGGER_PROJECT_REF }); + await callNextjsApp(); + const token = await wait.createToken({ tags: [`user:${userId}`, "approval"], timeout: "5m", // timeout in 5 minutes @@ -129,6 +150,8 @@ export const todoChat = schemaTask({ run: async ({ input, userId }, { signal }) => { metadata.set("user_id", userId); + logger.info("todoChat: starting", { input, userId }); + const system = ` You are a SQL (postgres) expert who can turn natural language descriptions for a todo app into a SQL query which can then be executed against a SQL database. Here is the schema: From f82a4e9651152089c05abf42bb81ce64dab6f248 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:08:33 +0100 Subject: [PATCH 013/101] fix(playwright): improve chrome installation and fix spinner duplication on narrow terminals (#2347) * headless: false will now install binaries for headless mode too * fix spinner message duplication on narrow terminals * add changeset --- .changeset/silent-lobsters-march.md | 7 +++ packages/build/src/extensions/playwright.ts | 23 +++++++- packages/cli-v3/src/utilities/windows.ts | 63 ++++++++++++++++++++- 3 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 .changeset/silent-lobsters-march.md diff --git a/.changeset/silent-lobsters-march.md b/.changeset/silent-lobsters-march.md new file mode 100644 index 000000000..a6ca8a3b5 --- /dev/null +++ b/.changeset/silent-lobsters-march.md @@ -0,0 +1,7 @@ +--- +"trigger.dev": patch +"@trigger.dev/build": patch +--- + +- Improve playwright non-headless chrome installation +- Prevent spinner message duplication in narrow terminals \ No newline at end of file diff --git a/packages/build/src/extensions/playwright.ts b/packages/build/src/extensions/playwright.ts index 0f675d85e..0931a4855 100644 --- a/packages/build/src/extensions/playwright.ts +++ b/packages/build/src/extensions/playwright.ts @@ -295,10 +295,29 @@ class PlaywrightExtension implements BuildExtension { * We save this output to a file and then parse it to get the download urls for the browsers. */ instructions.push(`RUN npx playwright install --dry-run > /tmp/browser-info.txt`); + + // Determine which browsers to actually install + const browsersToInstall = new Set(); + this.options.browsers.forEach((browser) => { - const browserType = browser === "chromium" ? "chromium-headless-shell" : browser; + if (browser === "chromium") { + if (this.options.headless) { + // For headless mode, only install chromium-headless-shell + browsersToInstall.add("chromium-headless-shell"); + } else { + // For non-headless mode, install both chromium and chromium-headless-shell + // This allows users to easily switch between headless and non-headless mode + browsersToInstall.add("chromium"); + browsersToInstall.add("chromium-headless-shell"); + } + } else { + browsersToInstall.add(browser); + } + }); + + Array.from(browsersToInstall).forEach((browser) => { instructions.push( - `RUN grep -A5 "browser: ${browserType}" /tmp/browser-info.txt > /tmp/${browser}-info.txt`, + `RUN grep -A5 -m1 "browser: ${browser}" /tmp/browser-info.txt > /tmp/${browser}-info.txt`, `RUN INSTALL_DIR=$(grep "Install location:" /tmp/${browser}-info.txt | cut -d':' -f2- | xargs) && \ DIR_NAME=$(basename "$INSTALL_DIR") && \ diff --git a/packages/cli-v3/src/utilities/windows.ts b/packages/cli-v3/src/utilities/windows.ts index 4e8a6ad79..5b1f12125 100644 --- a/packages/cli-v3/src/utilities/windows.ts +++ b/packages/cli-v3/src/utilities/windows.ts @@ -7,6 +7,67 @@ export function escapeImportPath(path: string) { return isWindows ? path.replaceAll("\\", "\\\\") : path; } +// Removes ANSI escape sequences to get actual visible length +function getVisibleLength(str: string): number { + return ( + str + // Remove terminal hyperlinks: \u001b]8;;URL\u0007TEXT\u001b]8;;\u0007 + .replace(/\u001b]8;;[^\u0007]*\u0007/g, "") + // Remove standard ANSI escape sequences (colors, cursor movement, etc.) + .replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").length + ); +} + +function truncateMessage(msg: string, maxLength?: number): string { + const terminalWidth = maxLength ?? process.stdout.columns ?? 80; + const availableWidth = terminalWidth - 5; // Reserve some space for the spinner and padding + const visibleLength = getVisibleLength(msg); + + if (visibleLength <= availableWidth) { + return msg; + } + + // We need to truncate based on visible characters, but preserve ANSI sequences + // Simple approach: truncate character by character until we fit + let truncated = msg; + while (getVisibleLength(truncated) > availableWidth - 3) { + truncated = truncated.slice(0, -1); + } + + return truncated + "..."; +} + +const wrappedClackSpinner = () => { + let currentMessage = ""; + let isActive = false; + + const handleResize = () => { + if (isActive && currentMessage) { + spinner.message(truncateMessage(currentMessage)); + } + }; + + const spinner = clackSpinner(); + + return { + start: (msg?: string): void => { + currentMessage = msg ?? ""; + isActive = true; + process.stdout.on("resize", handleResize); + spinner.start(truncateMessage(currentMessage)); + }, + stop: (msg?: string, code?: number): void => { + isActive = false; + process.stdout.off("resize", handleResize); + spinner.stop(truncateMessage(msg ?? ""), code); + }, + message: (msg?: string): void => { + currentMessage = msg ?? ""; + spinner.message(truncateMessage(currentMessage)); + }, + }; +}; + const ballmerSpinner = () => ({ start: (msg?: string): void => { log.step(msg ?? ""); @@ -21,4 +82,4 @@ const ballmerSpinner = () => ({ // This will become unecessary with the next clack release, the bug was fixed here: // https://github.com/natemoo-re/clack/pull/182 -export const spinner = () => (isWindows ? ballmerSpinner() : clackSpinner()); +export const spinner = () => (isWindows ? ballmerSpinner() : wrappedClackSpinner()); From 1294076484c68c78d9e840d78ea1094fb8df3732 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 6 Aug 2025 13:44:56 +0100 Subject: [PATCH 014/101] feat: index json schemas on tasks and schemaTask (#2351) * Add payload schema handling for task indexing This change introduces support for handling payload schemas during task indexing. By incorporating the `payloadSchema` attribute into various components, we ensure that each task's payload structure is clearly defined and can be validated before processing. - Updated the TaskManifest and task metadata structures to include an optional `payloadSchema` attribute. This addition allows for more robust validation and handling of task payloads. - Enhanced several core modules to export and utilize the new `getSchemaToJsonSchema` function, providing easier conversion of schema types to JSON schemas. - Modified the database schema to store the `payloadSchema` attribute, ensuring that the payload schema information is persisted. - The change helps in maintaining consistency in data handling and improves the integrity of task data across the application. * Refactor: Remove getSchemaToJsonSchema in favor of schemaToJsonSchema The `getSchemaToJsonSchema` function was removed and replaced with `schemaToJsonSchema` across the codebase. This update introduces a new `@trigger.dev/schema-to-json` package to handle conversions of schema validation libraries to JSON Schema format, centralizing the functionality and improving maintainability. - Removed `getSchemaToJsonSchema` exports and references. - Added new schema conversion utility `@trigger.dev/schema-to-json`. - Updated `trigger-sdk` package to utilize `schemaToJsonSchema` for payloads. - Extensive testing coverage included to ensure conversion accuracy across various schema libraries including Zod, Yup, ArkType, Effect, and TypeBox. - The update ensures consistent and reliable schema conversions, facilitating future enhancements and supporting additional schema libraries. * Add support for Zod 4 in schema-to-json This change enhances the schema-to-json package by adding support for Zod version 4, which introduces the native `toJsonSchema` method. This method facilitates a direct conversion of Zod schemas to JSON Schema format, improving performance and reducing reliance on the `zod-to-json-schema` library. - Updated README to reflect Zod 4 support with native method and retained support for Zod 3 via existing library. - Modified package.json to allow installation of both Zod 3 and 4 versions. - Implemented handling for Zod 4 schemas in `src/index.ts` using their native method. - Added a test case to verify the proper conversion of Zod 4 schemas to JSON Schema. - Included a script for updating the package version based on the root package.json. - Introduced a specific TypeScript config for source files. * Revise schema-to-json for bundle safety and tests The package @trigger.dev/schema-to-json has been revised to ensure bundle safety by removing direct dependencies on schema libraries such as Zod, Yup, and Effect. This change minimizes bundle size and enhances tree-shaking by allowing external conversion libraries to be utilized only at runtime if necessary. As a result, the README was updated to reflect this usage pattern. - Introduced `initializeSchemaConverters` function to load necessary conversion libraries at runtime, keeping the base package slim. - Adjusted test suite to initialize converters before tests, ensuring accurate testing of schema conversion capabilities. - Updated `schemaToJsonSchema` function to dynamically check for availability of conversion libraries, improving flexibility without increasing the package size. - Added configuration files for Vitest to support the new testing framework, reflecting the transition from previous test setups. These enhancements ensure that only the schema libraries actively used in an application are bundled, optimizing performance and resource usage. * Refine JSON Schema typing across packages The changes introduce stricter typing for JSON Schema-related definitions, specifically replacing vague types with more precise ones, such as using `z.record(z.unknown())` instead of `z.any()` and `Record` in place of `any`. This is part of an effort to better align with common practices and improve type safety in the packages. - Updated the `payloadSchema` in several files to use `z.record(z.unknown())`, enhancing the type strictness and consistency with JSON Schema Draft 7 recommendations. - Added `@types/json-schema` as a dependency, utilizing its definitions for improved type clarity and adherence to best practices in TypeScript. - Modified various comments to explicitly mention JSON Schema Draft 7, ensuring developers are aware of the JSON Schema version being implemented. - These adjustments are informed by research into how popular libraries and tools handle JSON Schema typing, aiming to integrate best practices for improved maintainability and interoperability. * Add JSON Schema examples using various libraries The change introduces extensive examples of using JSON Schemas in the 'references/hello-world' project within the 'trigger.dev' repository. These examples utilize libraries like Zod, Yup, and TypeBox for JSON Schema conversion and validation. The new examples demonstrate different use cases, including automatic conversion with schemaTask, manual schema provision, and schema conversion at build time. We also updated the dependencies in 'package.json' to include the necessary libraries for schema conversion and validation. - Included examples of processing tasks with JSON Schema using libraries such as Zod, Yup, TypeBox, and ArkType. - Showcased schema conversion techniques and type-safe JSON Schema creation. - Updated 'package.json' to ensure all necessary dependencies for schema operations are available. - Created illustrative scripts that cover task management from user processing to complex schema implementations. * Refactor SDK to encapsulate schema-to-json package The previous implementation required users to directly import and initialize functions from the `@trigger.dev/schema-to-json` package, which was not the intended user experience. This change refactors the SDK so that all necessary functions and types from `@trigger.dev/schema-to-json` are encapsulated within the `@trigger.dev/*` packages. - The examples in `usage.ts` have been updated to clearly mark `@trigger.dev/schema-to-json` as an internal-only package. - Re-export JSON Schema types and conversions in the SDK to improve developer experience (DX). - Removed unnecessary direct dependencies on `@trigger.dev/schema-to-json` from user-facing code, ensuring initialization and conversion logic is handled internally. - Replaced instances where users were required to manually perform schema conversions with automatic handling within the SDK for simplification and better maintainability. * Add JSONSchema type for payloadSchema in tasks The change was necessary to improve type safety by using a proper JSONSchema type definition instead of a generic Record. This enhances the developer experience and ensures that task payloads conform to the JSON Schema Draft 7 specification. The JSONSchema type is now re-exported from the SDK for user convenience, hiding internal complexity and maintaining a seamless developer experience. - Added JSONSchema type based on Draft 7 specification - Updated task metadata and options to use JSONSchema type - Hid internal schema conversion logic from users by re-exporting types from SDK - Improved bundle safety and dependency management * Add JSON schema testing and revert package dependencies This commit introduces a comprehensive set of JSON schema testing within the monorepo, specifically adding a new test project in `references/json-schema-test`. This includes a variety of schema definitions and tasks utilizing multiple validation libraries to ensure robust type-checking and runtime validation. Additionally, the dependency versions for `@effect/schema` have been adjusted from `^0.76.5` to `^0.75.5` to maintain compatibility across the project components. This ensures consistent behavior and compatibility with existing code bases without introducing breaking changes or unexpected behavior due to version discrepancies. Key updates include: - Added new test project with extensive schema validation tests. - Ensured type safety across various task implementations. - Reverted dependency versions to ensure compatibility. - Created multiple schema tasks using libraries like Zod, Yup, and others for thorough testing. * Refactor JSON Schema test files for clarity Whitespace and formatting changes were applied across the `json-schema-test` reference project to enhance code readability and cohesion. This included removing unnecessary trailing spaces and ensuring consistent indentation patterns, which improves maintainability and readability by following the project's code style guidelines. - Renamed JSONSchema type annotations to adhere to TypeScript conventions, ensuring that all schema definitions properly satisfy the JSONSchema interface. - Restructured some object declarations for improved clarity, especially within complex schema definitions. - These adjustments are crucial for better future maintainability, reducing potential developer errors when interacting with these test schemas. * Fixed some stuff * WIP * we now convert schema to jsonSchema on the CLI side via the indexing * Remove the json-schema-test reference project * Improve schema-to-json peer deps and fix effect schema * Explain the casting and match the version numbers * Fixed a bunch more schema stuff * Don't clean files that might be written to * Don't use a custom version of vitest in the new package * fix attw in schema-to-json --- .../services/createBackgroundWorker.server.ts | 4 +- ...eateDeploymentBackgroundWorkerV3.server.ts | 5 +- ...eateDeploymentBackgroundWorkerV4.server.ts | 5 +- .../migration.sql | 2 + .../database/prisma/schema.prisma | 2 + packages/cli-v3/package.json | 1 + .../src/entryPoints/dev-index-worker.ts | 24 +- .../src/entryPoints/managed-index-worker.ts | 24 +- .../core/src/v3/resource-catalog/catalog.ts | 3 +- .../core/src/v3/resource-catalog/index.ts | 6 +- .../resource-catalog/noopResourceCatalog.ts | 6 +- .../standardResourceCatalog.ts | 17 +- packages/core/src/v3/schemas/resources.ts | 2 + packages/core/src/v3/schemas/schemas.ts | 1 + packages/core/src/v3/types/index.ts | 1 + packages/core/src/v3/types/jsonSchema.ts | 76 ++++ packages/core/src/v3/types/tasks.ts | 17 + packages/schema-to-json/.gitignore | 6 + packages/schema-to-json/README.md | 151 +++++++ packages/schema-to-json/package.json | 107 +++++ packages/schema-to-json/src/index.ts | 238 ++++++++++ packages/schema-to-json/tests/index.test.ts | 350 +++++++++++++++ packages/schema-to-json/tsconfig.json | 11 + packages/schema-to-json/tsconfig.src.json | 11 + packages/schema-to-json/tsconfig.test.json | 11 + packages/schema-to-json/vitest.config.ts | 8 + packages/trigger-sdk/package.json | 2 +- packages/trigger-sdk/src/v3/index.ts | 1 + packages/trigger-sdk/src/v3/schemas.ts | 2 + packages/trigger-sdk/src/v3/shared.ts | 26 +- pnpm-lock.yaml | 154 ++++++- references/hello-world/package.json | 5 +- references/hello-world/src/trigger/example.ts | 4 +- .../hello-world/src/trigger/jsonSchema.ts | 413 ++++++++++++++++++ .../hello-world/src/trigger/jsonSchemaApi.ts | 343 +++++++++++++++ .../src/trigger/jsonSchemaSimple.ts | 235 ++++++++++ 36 files changed, 2234 insertions(+), 40 deletions(-) create mode 100644 internal-packages/database/prisma/migrations/20250730084611_add_payload_schema_to_background_worker_task/migration.sql create mode 100644 packages/core/src/v3/types/jsonSchema.ts create mode 100644 packages/schema-to-json/.gitignore create mode 100644 packages/schema-to-json/README.md create mode 100644 packages/schema-to-json/package.json create mode 100644 packages/schema-to-json/src/index.ts create mode 100644 packages/schema-to-json/tests/index.test.ts create mode 100644 packages/schema-to-json/tsconfig.json create mode 100644 packages/schema-to-json/tsconfig.src.json create mode 100644 packages/schema-to-json/tsconfig.test.json create mode 100644 packages/schema-to-json/vitest.config.ts create mode 100644 packages/trigger-sdk/src/v3/schemas.ts create mode 100644 references/hello-world/src/trigger/jsonSchema.ts create mode 100644 references/hello-world/src/trigger/jsonSchemaApi.ts create mode 100644 references/hello-world/src/trigger/jsonSchemaSimple.ts diff --git a/apps/webapp/app/v3/services/createBackgroundWorker.server.ts b/apps/webapp/app/v3/services/createBackgroundWorker.server.ts index 5ca2d5d38..ea43bbe42 100644 --- a/apps/webapp/app/v3/services/createBackgroundWorker.server.ts +++ b/apps/webapp/app/v3/services/createBackgroundWorker.server.ts @@ -77,7 +77,8 @@ export class CreateBackgroundWorkerService extends BaseService { version: nextVersion, runtimeEnvironmentId: environment.id, projectId: project.id, - metadata: body.metadata, + // body.metadata has an index signature that Prisma doesn't like (from the JSONSchema type) so we are safe to just cast it + metadata: body.metadata as Prisma.InputJsonValue, contentHash: body.metadata.contentHash, cliVersion: body.metadata.cliPackageVersion, sdkVersion: body.metadata.packageVersion, @@ -280,6 +281,7 @@ async function createWorkerTask( fileId: tasksToBackgroundFiles?.get(task.id) ?? null, maxDurationInSeconds: task.maxDuration ? clampMaxDuration(task.maxDuration) : null, queueId: queue.id, + payloadSchema: task.payloadSchema as any, }, }); } catch (error) { diff --git a/apps/webapp/app/v3/services/createDeploymentBackgroundWorkerV3.server.ts b/apps/webapp/app/v3/services/createDeploymentBackgroundWorkerV3.server.ts index 76be01652..e093f2c20 100644 --- a/apps/webapp/app/v3/services/createDeploymentBackgroundWorkerV3.server.ts +++ b/apps/webapp/app/v3/services/createDeploymentBackgroundWorkerV3.server.ts @@ -1,5 +1,5 @@ import { CreateBackgroundWorkerRequestBody } from "@trigger.dev/core/v3"; -import type { BackgroundWorker } from "@trigger.dev/database"; +import type { BackgroundWorker, Prisma } from "@trigger.dev/database"; import { AuthenticatedEnvironment } from "~/services/apiAuth.server"; import { logger } from "~/services/logger.server"; import { socketIo } from "../handleSocketIo.server"; @@ -48,7 +48,8 @@ export class CreateDeploymentBackgroundWorkerServiceV3 extends BaseService { version: deployment.version, runtimeEnvironmentId: environment.id, projectId: environment.projectId, - metadata: body.metadata, + // body.metadata has an index signature that Prisma doesn't like (from the JSONSchema type) so we are safe to just cast it + metadata: body.metadata as Prisma.InputJsonValue, contentHash: body.metadata.contentHash, cliVersion: body.metadata.cliPackageVersion, sdkVersion: body.metadata.packageVersion, diff --git a/apps/webapp/app/v3/services/createDeploymentBackgroundWorkerV4.server.ts b/apps/webapp/app/v3/services/createDeploymentBackgroundWorkerV4.server.ts index 2fb32966d..cc73a8569 100644 --- a/apps/webapp/app/v3/services/createDeploymentBackgroundWorkerV4.server.ts +++ b/apps/webapp/app/v3/services/createDeploymentBackgroundWorkerV4.server.ts @@ -1,6 +1,6 @@ import { CreateBackgroundWorkerRequestBody, logger, tryCatch } from "@trigger.dev/core/v3"; import { BackgroundWorkerId } from "@trigger.dev/core/v3/isomorphic"; -import type { BackgroundWorker, WorkerDeployment } from "@trigger.dev/database"; +import type { BackgroundWorker, Prisma, WorkerDeployment } from "@trigger.dev/database"; import { AuthenticatedEnvironment } from "~/services/apiAuth.server"; import { BaseService, ServiceValidationError } from "./baseService.server"; import { @@ -65,7 +65,8 @@ export class CreateDeploymentBackgroundWorkerServiceV4 extends BaseService { version: deployment.version, runtimeEnvironmentId: environment.id, projectId: environment.projectId, - metadata: body.metadata, + // body.metadata has an index signature that Prisma doesn't like (from the JSONSchema type) so we are safe to just cast it + metadata: body.metadata as Prisma.InputJsonValue, contentHash: body.metadata.contentHash, cliVersion: body.metadata.cliPackageVersion, sdkVersion: body.metadata.packageVersion, diff --git a/internal-packages/database/prisma/migrations/20250730084611_add_payload_schema_to_background_worker_task/migration.sql b/internal-packages/database/prisma/migrations/20250730084611_add_payload_schema_to_background_worker_task/migration.sql new file mode 100644 index 000000000..bbbb6694f --- /dev/null +++ b/internal-packages/database/prisma/migrations/20250730084611_add_payload_schema_to_background_worker_task/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "BackgroundWorkerTask" ADD COLUMN "payloadSchema" JSONB; \ No newline at end of file diff --git a/internal-packages/database/prisma/schema.prisma b/internal-packages/database/prisma/schema.prisma index 211ff2b35..ce12dcf7c 100644 --- a/internal-packages/database/prisma/schema.prisma +++ b/internal-packages/database/prisma/schema.prisma @@ -510,6 +510,8 @@ model BackgroundWorkerTask { triggerSource TaskTriggerSource @default(STANDARD) + payloadSchema Json? + @@unique([workerId, slug]) // Quick lookup of task identifiers @@index([projectId, slug]) diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index 24c68670a..74e935590 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -91,6 +91,7 @@ "@opentelemetry/semantic-conventions": "1.36.0", "@trigger.dev/build": "workspace:4.0.0-v4-beta.26", "@trigger.dev/core": "workspace:4.0.0-v4-beta.26", + "@trigger.dev/schema-to-json": "workspace:4.0.0-v4-beta.26", "ansi-escapes": "^7.0.0", "braces": "^3.0.3", "c12": "^1.11.1", diff --git a/packages/cli-v3/src/entryPoints/dev-index-worker.ts b/packages/cli-v3/src/entryPoints/dev-index-worker.ts index d44ac53fe..7b40ecf9d 100644 --- a/packages/cli-v3/src/entryPoints/dev-index-worker.ts +++ b/packages/cli-v3/src/entryPoints/dev-index-worker.ts @@ -18,6 +18,7 @@ import { registerResources } from "../indexing/registerResources.js"; import { env } from "std-env"; import { normalizeImportPath } from "../utilities/normalizeImportPath.js"; import { detectRuntimeVersion } from "@trigger.dev/core/v3/build"; +import { schemaToJsonSchema, initializeSchemaConverters } from "@trigger.dev/schema-to-json"; sourceMapSupport.install({ handleUncaughtExceptions: false, @@ -100,7 +101,7 @@ async function bootstrap() { const { buildManifest, importErrors, config, timings } = await bootstrap(); -let tasks = resourceCatalog.listTaskManifests(); +let tasks = await convertSchemasToJsonSchemas(resourceCatalog.listTaskManifests()); // If the config has retry defaults, we need to apply them to all tasks that don't have any retry settings if (config.retries?.default) { @@ -190,3 +191,24 @@ await new Promise((resolve) => { resolve(); }, 10); }); + +async function convertSchemasToJsonSchemas(tasks: TaskManifest[]): Promise { + await initializeSchemaConverters(); + + const convertedTasks = tasks.map((task) => { + const schema = resourceCatalog.getTaskSchema(task.id); + + if (schema) { + try { + const result = schemaToJsonSchema(schema); + return { ...task, payloadSchema: result?.jsonSchema }; + } catch { + return task; + } + } + + return task; + }); + + return convertedTasks; +} diff --git a/packages/cli-v3/src/entryPoints/managed-index-worker.ts b/packages/cli-v3/src/entryPoints/managed-index-worker.ts index 845ece47a..03c4ff4b1 100644 --- a/packages/cli-v3/src/entryPoints/managed-index-worker.ts +++ b/packages/cli-v3/src/entryPoints/managed-index-worker.ts @@ -18,6 +18,7 @@ import { registerResources } from "../indexing/registerResources.js"; import { env } from "std-env"; import { normalizeImportPath } from "../utilities/normalizeImportPath.js"; import { detectRuntimeVersion } from "@trigger.dev/core/v3/build"; +import { schemaToJsonSchema, initializeSchemaConverters } from "@trigger.dev/schema-to-json"; sourceMapSupport.install({ handleUncaughtExceptions: false, @@ -100,7 +101,7 @@ async function bootstrap() { const { buildManifest, importErrors, config, timings } = await bootstrap(); -let tasks = resourceCatalog.listTaskManifests(); +let tasks = await convertSchemasToJsonSchemas(resourceCatalog.listTaskManifests()); // If the config has retry defaults, we need to apply them to all tasks that don't have any retry settings if (config.retries?.default) { @@ -196,3 +197,24 @@ await new Promise((resolve) => { resolve(); }, 10); }); + +async function convertSchemasToJsonSchemas(tasks: TaskManifest[]): Promise { + await initializeSchemaConverters(); + + const convertedTasks = tasks.map((task) => { + const schema = resourceCatalog.getTaskSchema(task.id); + + if (schema) { + try { + const result = schemaToJsonSchema(schema); + return { ...task, payloadSchema: result?.jsonSchema }; + } catch { + return task; + } + } + + return task; + }); + + return convertedTasks; +} diff --git a/packages/core/src/v3/resource-catalog/catalog.ts b/packages/core/src/v3/resource-catalog/catalog.ts index 725899c0d..9ad14dd84 100644 --- a/packages/core/src/v3/resource-catalog/catalog.ts +++ b/packages/core/src/v3/resource-catalog/catalog.ts @@ -1,5 +1,5 @@ import { QueueManifest, TaskManifest, WorkerManifest } from "../schemas/index.js"; -import { TaskMetadataWithFunctions } from "../types/index.js"; +import { TaskMetadataWithFunctions, TaskSchema } from "../types/index.js"; export interface ResourceCatalog { setCurrentFileContext(filePath: string, entryPoint: string): void; @@ -13,4 +13,5 @@ export interface ResourceCatalog { registerWorkerManifest(workerManifest: WorkerManifest): void; registerQueueMetadata(queue: QueueManifest): void; listQueueManifests(): Array; + getTaskSchema(id: string): TaskSchema | undefined; } diff --git a/packages/core/src/v3/resource-catalog/index.ts b/packages/core/src/v3/resource-catalog/index.ts index 6773f1b62..a564648fc 100644 --- a/packages/core/src/v3/resource-catalog/index.ts +++ b/packages/core/src/v3/resource-catalog/index.ts @@ -1,7 +1,7 @@ const API_NAME = "resource-catalog"; import { QueueManifest, TaskManifest, WorkerManifest } from "../schemas/index.js"; -import { TaskMetadataWithFunctions } from "../types/index.js"; +import { TaskMetadataWithFunctions, TaskSchema } from "../types/index.js"; import { getGlobal, registerGlobal, unregisterGlobal } from "../utils/globals.js"; import { type ResourceCatalog } from "./catalog.js"; import { NoopResourceCatalog } from "./noopResourceCatalog.js"; @@ -65,6 +65,10 @@ export class ResourceCatalogAPI { return this.#getCatalog().getTask(id); } + public getTaskSchema(id: string): TaskSchema | undefined { + return this.#getCatalog().getTaskSchema(id); + } + public taskExists(id: string): boolean { return this.#getCatalog().taskExists(id); } diff --git a/packages/core/src/v3/resource-catalog/noopResourceCatalog.ts b/packages/core/src/v3/resource-catalog/noopResourceCatalog.ts index b0e0f7305..53a953393 100644 --- a/packages/core/src/v3/resource-catalog/noopResourceCatalog.ts +++ b/packages/core/src/v3/resource-catalog/noopResourceCatalog.ts @@ -1,5 +1,5 @@ import { QueueManifest, TaskManifest, WorkerManifest } from "../schemas/index.js"; -import { TaskMetadataWithFunctions } from "../types/index.js"; +import { TaskMetadataWithFunctions, TaskSchema } from "../types/index.js"; import { ResourceCatalog } from "./catalog.js"; export class NoopResourceCatalog implements ResourceCatalog { @@ -31,6 +31,10 @@ export class NoopResourceCatalog implements ResourceCatalog { return undefined; } + getTaskSchema(id: string): TaskSchema | undefined { + return undefined; + } + taskExists(id: string): boolean { return false; } diff --git a/packages/core/src/v3/resource-catalog/standardResourceCatalog.ts b/packages/core/src/v3/resource-catalog/standardResourceCatalog.ts index 7468b63d8..3b8eaa7d6 100644 --- a/packages/core/src/v3/resource-catalog/standardResourceCatalog.ts +++ b/packages/core/src/v3/resource-catalog/standardResourceCatalog.ts @@ -5,10 +5,11 @@ import { WorkerManifest, QueueManifest, } from "../schemas/index.js"; -import { TaskMetadataWithFunctions } from "../types/index.js"; +import { TaskMetadataWithFunctions, TaskSchema } from "../types/index.js"; import { ResourceCatalog } from "./catalog.js"; export class StandardResourceCatalog implements ResourceCatalog { + private _taskSchemas: Map = new Map(); private _taskMetadata: Map = new Map(); private _taskFunctions: Map = new Map(); private _taskFileMetadata: Map = new Map(); @@ -72,6 +73,10 @@ export class StandardResourceCatalog implements ResourceCatalog { this._taskMetadata.set(task.id, metadata); this._taskFunctions.set(task.id, fns); + + if (task.schema) { + this._taskSchemas.set(task.id, task.schema); + } } updateTaskMetadata(id: string, updates: Partial): void { @@ -107,15 +112,21 @@ export class StandardResourceCatalog implements ResourceCatalog { continue; } - result.push({ + const taskManifest = { ...metadata, ...fileMetadata, - }); + }; + + result.push(taskManifest); } return result; } + getTaskSchema(id: string): TaskSchema | undefined { + return this._taskSchemas.get(id); + } + listQueueManifests(): Array { return Array.from(this._queueMetadata.values()); } diff --git a/packages/core/src/v3/schemas/resources.ts b/packages/core/src/v3/schemas/resources.ts index ec2b180bb..08764906e 100644 --- a/packages/core/src/v3/schemas/resources.ts +++ b/packages/core/src/v3/schemas/resources.ts @@ -13,6 +13,8 @@ export const TaskResource = z.object({ triggerSource: z.string().optional(), schedule: ScheduleMetadata.optional(), maxDuration: z.number().optional(), + // JSONSchema type - using z.unknown() for runtime validation to accept JSONSchema7 + payloadSchema: z.unknown().optional(), }); export type TaskResource = z.infer; diff --git a/packages/core/src/v3/schemas/schemas.ts b/packages/core/src/v3/schemas/schemas.ts index ccd0fa188..233068c0b 100644 --- a/packages/core/src/v3/schemas/schemas.ts +++ b/packages/core/src/v3/schemas/schemas.ts @@ -189,6 +189,7 @@ const taskMetadata = { triggerSource: z.string().optional(), schedule: ScheduleMetadata.optional(), maxDuration: z.number().optional(), + payloadSchema: z.unknown().optional(), }; export const TaskMetadata = z.object(taskMetadata); diff --git a/packages/core/src/v3/types/index.ts b/packages/core/src/v3/types/index.ts index 55cc4d3a1..ea2bc8d55 100644 --- a/packages/core/src/v3/types/index.ts +++ b/packages/core/src/v3/types/index.ts @@ -7,6 +7,7 @@ export * from "./tasks.js"; export * from "./idempotencyKeys.js"; export * from "./tools.js"; export * from "./queues.js"; +export * from "./jsonSchema.js"; type ResolveEnvironmentVariablesOptions = { variables: Record | Array<{ name: string; value: string }>; diff --git a/packages/core/src/v3/types/jsonSchema.ts b/packages/core/src/v3/types/jsonSchema.ts new file mode 100644 index 000000000..7abf241a6 --- /dev/null +++ b/packages/core/src/v3/types/jsonSchema.ts @@ -0,0 +1,76 @@ +/** + * JSON Schema type definition - compatible with JSON Schema Draft 7 + * Based on the JSONSchema7 type from @types/json-schema but defined inline to avoid import issues + */ +export interface JSONSchema { + $id?: string; + $ref?: string; + $schema?: string; + $comment?: string; + + type?: JSONSchemaType | JSONSchemaType[]; + enum?: any[]; + const?: any; + + // Number/Integer validations + multipleOf?: number; + maximum?: number; + exclusiveMaximum?: number; + minimum?: number; + exclusiveMinimum?: number; + + // String validations + maxLength?: number; + minLength?: number; + pattern?: string; + format?: string; + + // Array validations + items?: JSONSchema | JSONSchema[]; + additionalItems?: JSONSchema | boolean; + maxItems?: number; + minItems?: number; + uniqueItems?: boolean; + contains?: JSONSchema; + + // Object validations + maxProperties?: number; + minProperties?: number; + required?: string[]; + properties?: Record; + patternProperties?: Record; + additionalProperties?: JSONSchema | boolean; + dependencies?: Record; + propertyNames?: JSONSchema; + + // Conditional schemas + if?: JSONSchema; + then?: JSONSchema; + else?: JSONSchema; + + // Boolean logic + allOf?: JSONSchema[]; + anyOf?: JSONSchema[]; + oneOf?: JSONSchema[]; + not?: JSONSchema; + + // Metadata + title?: string; + description?: string; + default?: any; + readOnly?: boolean; + writeOnly?: boolean; + examples?: any[]; + + // Additional properties for extensibility + [key: string]: any; +} + +export type JSONSchemaType = + | "string" + | "number" + | "integer" + | "boolean" + | "object" + | "array" + | "null"; diff --git a/packages/core/src/v3/types/tasks.ts b/packages/core/src/v3/types/tasks.ts index f9595b51e..66c9d98d5 100644 --- a/packages/core/src/v3/types/tasks.ts +++ b/packages/core/src/v3/types/tasks.ts @@ -28,6 +28,7 @@ import { QueueOptions } from "./queues.js"; import { AnySchemaParseFn, inferSchemaIn, inferSchemaOut, Schema } from "./schemas.js"; import { inferToolParameters, ToolTaskParameters } from "./tools.js"; import { Prettify } from "./utils.js"; +import { JSONSchema } from "./jsonSchema.js"; export type Queue = QueueOptions; export type TaskSchema = Schema; @@ -339,6 +340,12 @@ type CommonTaskOptions< * onFailure is called after a task run has failed (meaning the run function threw an error and won't be retried anymore) */ onFailure?: OnFailureHookFunction; + + /** + * JSON Schema for the task payload. This will be synced to the server during indexing. + * Should be a valid JSON Schema Draft 7 object. + */ + jsonSchema?: JSONSchema; }; export type TaskOptions< @@ -348,6 +355,15 @@ export type TaskOptions< TInitOutput extends InitOutput = any, > = CommonTaskOptions; +// Task options when payloadSchema is provided - payload should be any +export type TaskOptionsWithSchema< + TIdentifier extends string, + TOutput = unknown, + TInitOutput extends InitOutput = any, +> = CommonTaskOptions & { + jsonSchema: JSONSchema; +}; + export type TaskWithSchemaOptions< TIdentifier extends string, TSchema extends TaskSchema | undefined = undefined, @@ -881,6 +897,7 @@ export type TaskMetadataWithFunctions = TaskMetadata & { onStart?: (payload: any, params: StartFnParams) => Promise; parsePayload?: AnySchemaParseFn; }; + schema?: TaskSchema; }; export type RunTypes = { diff --git a/packages/schema-to-json/.gitignore b/packages/schema-to-json/.gitignore new file mode 100644 index 000000000..c88715239 --- /dev/null +++ b/packages/schema-to-json/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +.tshy +.tshy-build +*.log +.DS_Store \ No newline at end of file diff --git a/packages/schema-to-json/README.md b/packages/schema-to-json/README.md new file mode 100644 index 000000000..9576b6e80 --- /dev/null +++ b/packages/schema-to-json/README.md @@ -0,0 +1,151 @@ +# @trigger.dev/schema-to-json + +Convert various schema validation libraries to JSON Schema format. + +## Installation + +```bash +npm install @trigger.dev/schema-to-json +``` + +## Important: Bundle Safety + +This package is designed to be **bundle-safe**. It does NOT bundle any schema libraries (zod, yup, etc.) as dependencies. Instead: + +1. **Built-in conversions** work immediately (ArkType, Zod 4, TypeBox) +2. **External conversions** (Zod 3, Yup, Effect) require the conversion libraries to be available at runtime + +This design ensures that: +- ✅ Your bundle size stays small +- ✅ You only include the schema libraries you actually use +- ✅ Tree-shaking works properly +- ✅ No unnecessary dependencies are installed + +## Supported Schema Libraries + +- ✅ **Zod** - Full support + - Zod 4: Native support via built-in `toJsonSchema` method (no external deps needed) + - Zod 3: Requires `zod-to-json-schema` to be installed +- ✅ **Yup** - Requires `@sodaru/yup-to-json-schema` to be installed +- ✅ **ArkType** - Native support (built-in `toJsonSchema` method) +- ✅ **Effect/Schema** - Requires `effect` or `@effect/schema` to be installed +- ✅ **TypeBox** - Native support (already JSON Schema compliant) +- ⏳ **Valibot** - Coming soon +- ⏳ **Superstruct** - Coming soon +- ⏳ **Runtypes** - Coming soon + +## Usage + +### Basic Usage (Built-in conversions only) + +```typescript +import { schemaToJsonSchema } from '@trigger.dev/schema-to-json'; +import { type } from 'arktype'; + +// Works immediately for schemas with built-in conversion +const arkSchema = type({ + name: 'string', + age: 'number', +}); + +const result = schemaToJsonSchema(arkSchema); +console.log(result); +// { jsonSchema: {...}, schemaType: 'arktype' } +``` + +### Full Usage (With external conversion libraries) + +```typescript +import { schemaToJsonSchema, initializeSchemaConverters } from '@trigger.dev/schema-to-json'; +import { z } from 'zod'; + +// Initialize converters once in your app (loads conversion libraries if available) +await initializeSchemaConverters(); + +// Now you can convert Zod 3, Yup, and Effect schemas +const zodSchema = z.object({ + name: z.string(), + age: z.number(), + email: z.string().email(), +}); + +const result = schemaToJsonSchema(zodSchema); +console.log(result); +// { +// jsonSchema: { +// type: 'object', +// properties: { +// name: { type: 'string' }, +// age: { type: 'number' }, +// email: { type: 'string', format: 'email' } +// }, +// required: ['name', 'age', 'email'] +// }, +// schemaType: 'zod' +// } +``` + +## API + +### `schemaToJsonSchema(schema, options?)` + +Convert a schema to JSON Schema format. + +**Parameters:** +- `schema` - The schema to convert +- `options` (optional) + - `name` - Name to use for the schema (supported by some converters) + - `additionalProperties` - Additional properties to merge into the result + +**Returns:** +- `{ jsonSchema, schemaType }` - The converted JSON Schema and detected type +- `undefined` - If the schema cannot be converted + +### `initializeSchemaConverters()` + +Initialize the external conversion libraries. Call this once in your application if you need to convert schemas that don't have built-in JSON Schema support (Zod 3, Yup, Effect). + +**Returns:** `Promise` + +### `canConvertSchema(schema)` + +Check if a schema can be converted to JSON Schema. + +**Returns:** `boolean` + +### `detectSchemaType(schema)` + +Detect the type of schema. + +**Returns:** `'zod' | 'yup' | 'arktype' | 'effect' | 'valibot' | 'superstruct' | 'runtypes' | 'typebox' | 'unknown'` + +### `areConvertersInitialized()` + +Check which conversion libraries are available. + +**Returns:** `{ zod: boolean, yup: boolean, effect: boolean }` + +## Peer Dependencies + +Each schema library is an optional peer dependency. Install only the ones you need: + +```bash +# For Zod +npm install zod + +# For Yup +npm install yup + +# For ArkType +npm install arktype + +# For Effect +npm install effect @effect/schema + +# For TypeBox +npm install @sinclair/typebox +``` + +## License + +MIT \ No newline at end of file diff --git a/packages/schema-to-json/package.json b/packages/schema-to-json/package.json new file mode 100644 index 000000000..d095e6f22 --- /dev/null +++ b/packages/schema-to-json/package.json @@ -0,0 +1,107 @@ +{ + "name": "@trigger.dev/schema-to-json", + "version": "4.0.0-v4-beta.26", + "description": "Convert various schema validation libraries to JSON Schema", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/triggerdotdev/trigger.dev", + "directory": "packages/schema-to-json" + }, + "type": "module", + "engines": { + "node": ">=18.20.0" + }, + "files": [ + "dist" + ], + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "scripts": { + "clean": "rimraf dist", + "build": "pnpm run clean && pnpm run build:tshy && pnpm run update-version", + "build:tshy": "tshy", + "dev": "tshy --watch", + "typecheck": "tsc -p tsconfig.src.json --noEmit", + "test": "vitest", + "update-version": "tsx ../../scripts/updateVersion.ts", + "check-exports": "attw --pack ." + }, + "dependencies": { + "@trigger.dev/core": "workspace:*", + "zod-to-json-schema": "^3.24.5", + "@sodaru/yup-to-json-schema": "^2.0.1" + }, + "devDependencies": { + "arktype": "^2.0.0", + "effect": "^3.11.11", + "runtypes": "^6.7.0", + "superstruct": "^2.0.2", + "tshy": "^3.0.2", + "@sinclair/typebox": "^0.34.3", + "valibot": "^1.1.0", + "yup": "^1.7.0", + "zod": "^3.24.1 || ^4.0.0", + "rimraf": "6.0.1", + "@arethetypeswrong/cli": "^0.15.4" + }, + "peerDependencies": { + "arktype": ">=2.0.0", + "effect": ">=3.0.0", + "runtypes": ">=5.0.0", + "superstruct": ">=0.14.2", + "@sinclair/typebox": ">=0.34.30", + "valibot": ">=0.41.0", + "yup": ">=1.0.0", + "zod": "^3.24.1 || ^4.0.0" + }, + "peerDependenciesMeta": { + "arktype": { + "optional": true + }, + "effect": { + "optional": true + }, + "runtypes": { + "optional": true + }, + "superstruct": { + "optional": true + }, + "@sinclair/typebox": { + "optional": true + }, + "valibot": { + "optional": true + }, + "yup": { + "optional": true + }, + "zod": { + "optional": true + } + }, + "tshy": { + "selfLink": false, + "exports": { + ".": "./src/index.ts" + }, + "project": "./tsconfig.src.json" + }, + "main": "./dist/commonjs/index.js", + "types": "./dist/commonjs/index.d.ts", + "module": "./dist/esm/index.js" +} diff --git a/packages/schema-to-json/src/index.ts b/packages/schema-to-json/src/index.ts new file mode 100644 index 000000000..604ec8d78 --- /dev/null +++ b/packages/schema-to-json/src/index.ts @@ -0,0 +1,238 @@ +// Import JSONSchema from core to ensure compatibility +import type { JSONSchema } from "@trigger.dev/core/v3"; + +export type Schema = unknown; +export type { JSONSchema }; + +export interface ConversionOptions { + /** + * The name to use for the schema in the JSON Schema + */ + name?: string; + /** + * Additional JSON Schema properties to merge + */ + additionalProperties?: Record; +} + +export interface ConversionResult { + /** + * The JSON Schema representation (JSON Schema Draft 7) + */ + jsonSchema: JSONSchema; + /** + * The detected schema type + */ + schemaType: + | "zod" + | "yup" + | "arktype" + | "effect" + | "valibot" + | "superstruct" + | "runtypes" + | "typebox" + | "unknown"; +} + +/** + * Convert a schema from various validation libraries to JSON Schema + * + * This function attempts to convert schemas without requiring external dependencies to be bundled. + * It will only succeed if: + * 1. The schema has built-in JSON Schema conversion (ArkType, Zod 4, TypeBox) + * 2. The required conversion library is available at runtime (zod-to-json-schema, @sodaru/yup-to-json-schema, etc.) + * + * @param schema The schema to convert + * @param options Optional conversion options + * @returns The conversion result or undefined if conversion is not possible + */ +export function schemaToJsonSchema( + schema: Schema, + options?: ConversionOptions +): ConversionResult | undefined { + const parser = schema as any; + + // Check if schema has a built-in toJsonSchema method (e.g., ArkType, Zod 4) + if (typeof parser.toJsonSchema === "function") { + try { + const jsonSchema = parser.toJsonSchema(); + // Determine if it's Zod or ArkType based on other methods + const schemaType = + typeof parser.parseAsync === "function" || typeof parser.parse === "function" + ? "zod" + : "arktype"; + return { + jsonSchema: options?.additionalProperties + ? { ...jsonSchema, ...options.additionalProperties } + : jsonSchema, + schemaType, + }; + } catch (error) { + // If toJsonSchema fails, continue to other checks + } + } + + // Check if it's a TypeBox schema (has Static and Kind symbols) + if (parser[Symbol.for("TypeBox.Kind")] !== undefined) { + // TypeBox schemas are already JSON Schema compliant + return { + jsonSchema: options?.additionalProperties + ? { ...parser, ...options.additionalProperties } + : parser, + schemaType: "typebox", + }; + } + + // For schemas that need external libraries, we need to check if they're available + // This approach avoids bundling the dependencies while still allowing runtime usage + + // Check if it's a Zod schema (without built-in toJsonSchema) + if (typeof parser.parseAsync === "function" || typeof parser.parse === "function") { + try { + // Try to access zod-to-json-schema if it's available + // @ts-ignore - This is intentionally dynamic + if (typeof globalThis.__zodToJsonSchema !== "undefined") { + // @ts-ignore + const { zodToJsonSchema } = globalThis.__zodToJsonSchema; + const jsonSchema = options?.name + ? zodToJsonSchema(parser, options.name) + : zodToJsonSchema(parser); + + if (jsonSchema && typeof jsonSchema === "object" && "$schema" in jsonSchema) { + const { $schema, ...rest } = jsonSchema as any; + return { + jsonSchema: options?.additionalProperties + ? { ...rest, ...options.additionalProperties } + : rest, + schemaType: "zod", + }; + } + + return { + jsonSchema: options?.additionalProperties + ? { ...jsonSchema, ...options.additionalProperties } + : jsonSchema, + schemaType: "zod", + }; + } + } catch (error) { + // Library not available + } + } + + // Check if it's a Yup schema + if (typeof parser.validateSync === "function" && typeof parser.describe === "function") { + try { + // @ts-ignore + if (typeof globalThis.__yupToJsonSchema !== "undefined") { + // @ts-ignore + const { convertSchema } = globalThis.__yupToJsonSchema; + const jsonSchema = convertSchema(parser); + return { + jsonSchema: options?.additionalProperties + ? { ...jsonSchema, ...options.additionalProperties } + : jsonSchema, + schemaType: "yup", + }; + } + } catch (error) { + // Library not available + } + } + + // Check if it's an Effect schema + if (typeof parser.ast === "object" && typeof parser.ast._tag === "string") { + try { + // @ts-ignore + if (typeof globalThis.__effectJsonSchema !== "undefined") { + // @ts-ignore + const { JSONSchema } = globalThis.__effectJsonSchema; + const jsonSchema = JSONSchema.make(parser); + return { + jsonSchema: options?.additionalProperties + ? { ...jsonSchema, ...options.additionalProperties } + : jsonSchema, + schemaType: "effect", + }; + } + } catch (error) { + // Library not available + } + } + + // Future schema types can be added here... + + // Unknown schema type + return undefined; +} + +/** + * Initialize the schema conversion libraries + * This should be called by the consuming application if they want to enable + * conversion for schemas that don't have built-in JSON Schema support + */ +export async function initializeSchemaConverters(): Promise { + try { + // @ts-ignore + globalThis.__zodToJsonSchema = await import("zod-to-json-schema"); + } catch { + // Zod conversion not available + } + + try { + // @ts-ignore + globalThis.__yupToJsonSchema = await import("@sodaru/yup-to-json-schema"); + } catch { + // Yup conversion not available + } + + try { + // Try Effect first, then @effect/schema + let module; + try { + module = await import("effect"); + } catch {} + + if (module?.JSONSchema) { + // @ts-ignore + globalThis.__effectJsonSchema = { JSONSchema: module.JSONSchema }; + } + } catch { + // Effect conversion not available + } +} + +/** + * Check if a schema can be converted to JSON Schema + */ +export function canConvertSchema(schema: Schema): boolean { + const result = schemaToJsonSchema(schema); + return result !== undefined; +} + +/** + * Get the detected schema type + */ +export function detectSchemaType(schema: Schema): ConversionResult["schemaType"] { + const result = schemaToJsonSchema(schema); + return result?.schemaType ?? "unknown"; +} + +/** + * Check if the conversion libraries are initialized + */ +export function areConvertersInitialized(): { + zod: boolean; + yup: boolean; + effect: boolean; +} { + return { + // @ts-ignore + zod: typeof globalThis.__zodToJsonSchema !== "undefined", + // @ts-ignore + yup: typeof globalThis.__yupToJsonSchema !== "undefined", + // @ts-ignore + effect: typeof globalThis.__effectJsonSchema !== "undefined", + }; +} diff --git a/packages/schema-to-json/tests/index.test.ts b/packages/schema-to-json/tests/index.test.ts new file mode 100644 index 000000000..e64ddc96a --- /dev/null +++ b/packages/schema-to-json/tests/index.test.ts @@ -0,0 +1,350 @@ +import { z } from "zod"; +import * as y from "yup"; +// @ts-ignore +import { type } from "arktype"; +import { Schema } from "effect"; +import { Type } from "@sinclair/typebox"; +import { + schemaToJsonSchema, + canConvertSchema, + detectSchemaType, + initializeSchemaConverters, + areConvertersInitialized, +} from "../src/index.js"; + +// Initialize converters before running tests +beforeAll(async () => { + await initializeSchemaConverters(); +}); + +describe("schemaToJsonSchema", () => { + describe("Initialization", () => { + it("should have converters initialized", () => { + const status = areConvertersInitialized(); + expect(status.zod).toBe(true); + expect(status.yup).toBe(true); + expect(status.effect).toBe(true); + }); + }); + + describe("Zod schemas", () => { + it("should convert a simple Zod object schema", () => { + const schema = z.object({ + name: z.string(), + age: z.number(), + email: z.string().email(), + }); + + const result = schemaToJsonSchema(schema); + + expect(result).toBeDefined(); + expect(result?.schemaType).toBe("zod"); + expect(result?.jsonSchema).toMatchObject({ + type: "object", + properties: { + name: { type: "string" }, + age: { type: "number" }, + email: { type: "string", format: "email" }, + }, + required: ["name", "age", "email"], + }); + }); + + it("should convert a Zod schema with optional fields", () => { + const schema = z.object({ + id: z.string(), + description: z.string().optional(), + tags: z.array(z.string()).optional(), + }); + + const result = schemaToJsonSchema(schema); + + expect(result).toBeDefined(); + expect(result?.jsonSchema).toMatchObject({ + type: "object", + properties: { + id: { type: "string" }, + description: { type: "string" }, + tags: { type: "array", items: { type: "string" } }, + }, + required: ["id"], + }); + }); + + it("should handle Zod schema with name option", () => { + const schema = z.object({ + value: z.number(), + }); + + const result = schemaToJsonSchema(schema, { name: "MySchema" }); + + expect(result).toBeDefined(); + expect(result?.jsonSchema).toBeDefined(); + // The exact structure depends on zod-to-json-schema implementation + }); + + it("should handle Zod 4 schema with built-in toJsonSchema method", () => { + // Mock a Zod 4 schema with toJsonSchema method + const mockZod4Schema = { + parse: (val: unknown) => val, + parseAsync: async (val: unknown) => val, + toJsonSchema: () => ({ + type: "object", + properties: { + id: { type: "string" }, + count: { type: "number" }, + }, + required: ["id", "count"], + }), + }; + + const result = schemaToJsonSchema(mockZod4Schema); + + expect(result).toBeDefined(); + expect(result?.schemaType).toBe("zod"); + expect(result?.jsonSchema).toEqual({ + type: "object", + properties: { + id: { type: "string" }, + count: { type: "number" }, + }, + required: ["id", "count"], + }); + }); + }); + + describe("Yup schemas", () => { + it("should convert a simple Yup object schema", () => { + const schema = y.object({ + name: y.string().required(), + age: y.number().required(), + email: y.string().email().required(), + }); + + const result = schemaToJsonSchema(schema); + + expect(result).toBeDefined(); + expect(result?.schemaType).toBe("yup"); + expect(result?.jsonSchema).toMatchObject({ + type: "object", + properties: { + name: { type: "string" }, + age: { type: "number" }, + email: { type: "string", format: "email" }, + }, + required: ["name", "age", "email"], + }); + }); + + it("should convert a Yup schema with optional fields", () => { + const schema = y.object({ + id: y.string().required(), + description: y.string(), + count: y.number().min(0).max(100), + }); + + const result = schemaToJsonSchema(schema); + + expect(result).toBeDefined(); + expect(result?.jsonSchema).toMatchObject({ + type: "object", + properties: { + id: { type: "string" }, + description: { type: "string" }, + count: { type: "number", minimum: 0, maximum: 100 }, + }, + required: ["id"], + }); + }); + }); + + describe("ArkType schemas", () => { + it("should convert a simple ArkType schema", () => { + const schema = type({ + name: "string", + age: "number", + active: "boolean", + }); + + const result = schemaToJsonSchema(schema); + + expect(result).toBeDefined(); + expect(result?.schemaType).toBe("arktype"); + expect(result?.jsonSchema).toBeDefined(); + expect(result?.jsonSchema.type).toBe("object"); + }); + + it("should convert an ArkType schema with optional fields", () => { + const schema = type({ + id: "string", + "description?": "string", + "tags?": "string[]", + }); + + const result = schemaToJsonSchema(schema); + + expect(result).toBeDefined(); + expect(result?.jsonSchema).toBeDefined(); + expect(result?.jsonSchema.type).toBe("object"); + }); + }); + + describe("Effect schemas", () => { + it("should convert a simple Effect schema", () => { + const schema = Schema.Struct({ + name: Schema.String, + age: Schema.Number, + active: Schema.Boolean, + }); + + const result = schemaToJsonSchema(schema); + + expect(result).toBeDefined(); + expect(result?.schemaType).toBe("effect"); + expect(result?.jsonSchema).toMatchObject({ + type: "object", + properties: { + name: { type: "string" }, + age: { type: "number" }, + active: { type: "boolean" }, + }, + required: ["name", "age", "active"], + }); + }); + + it("should convert an Effect schema with optional fields", () => { + const schema = Schema.Struct({ + id: Schema.String, + description: Schema.optional(Schema.String), + count: Schema.optional(Schema.Number), + }); + + const result = schemaToJsonSchema(schema); + + expect(result).toBeDefined(); + expect(result?.jsonSchema).toBeDefined(); + expect(result?.jsonSchema.type).toBe("object"); + }); + }); + + describe("TypeBox schemas", () => { + it("should convert a simple TypeBox schema", () => { + const schema = Type.Object({ + name: Type.String(), + age: Type.Number(), + active: Type.Boolean(), + }); + + const result = schemaToJsonSchema(schema); + + expect(result).toBeDefined(); + expect(result?.schemaType).toBe("typebox"); + expect(result?.jsonSchema).toMatchObject({ + type: "object", + properties: { + name: { type: "string" }, + age: { type: "number" }, + active: { type: "boolean" }, + }, + required: ["name", "age", "active"], + }); + }); + + it("should convert a TypeBox schema with optional fields", () => { + const schema = Type.Object({ + id: Type.String(), + description: Type.Optional(Type.String()), + tags: Type.Optional(Type.Array(Type.String())), + }); + + const result = schemaToJsonSchema(schema); + + expect(result).toBeDefined(); + expect(result?.jsonSchema).toMatchObject({ + type: "object", + properties: { + id: { type: "string" }, + description: { type: "string" }, + tags: { type: "array", items: { type: "string" } }, + }, + required: ["id"], + }); + }); + }); + + describe("Additional options", () => { + it("should merge additional properties", () => { + const schema = z.object({ + value: z.number(), + }); + + const result = schemaToJsonSchema(schema, { + additionalProperties: { + title: "My Schema", + description: "A test schema", + "x-custom": "custom value", + }, + }); + + expect(result).toBeDefined(); + expect(result?.jsonSchema.title).toBe("My Schema"); + expect(result?.jsonSchema.description).toBe("A test schema"); + expect(result?.jsonSchema["x-custom"]).toBe("custom value"); + }); + }); + + describe("Unsupported schemas", () => { + it("should return undefined for unsupported schema types", () => { + const invalidSchema = { notASchema: true }; + const result = schemaToJsonSchema(invalidSchema); + expect(result).toBeUndefined(); + }); + + it("should return undefined for plain functions", () => { + const fn = (value: unknown) => typeof value === "string"; + const result = schemaToJsonSchema(fn); + expect(result).toBeUndefined(); + }); + }); +}); + +describe("canConvertSchema", () => { + it("should return true for supported schemas", () => { + expect(canConvertSchema(z.string())).toBe(true); + expect(canConvertSchema(y.string())).toBe(true); + expect(canConvertSchema(type("string"))).toBe(true); + expect(canConvertSchema(Schema.String)).toBe(true); + expect(canConvertSchema(Type.String())).toBe(true); + }); + + it("should return false for unsupported schemas", () => { + expect(canConvertSchema({ notASchema: true })).toBe(false); + expect(canConvertSchema(() => true)).toBe(false); + }); +}); + +describe("detectSchemaType", () => { + it("should detect Zod schemas", () => { + expect(detectSchemaType(z.string())).toBe("zod"); + }); + + it("should detect Yup schemas", () => { + expect(detectSchemaType(y.string())).toBe("yup"); + }); + + it("should detect ArkType schemas", () => { + expect(detectSchemaType(type("string"))).toBe("arktype"); + }); + + it("should detect Effect schemas", () => { + expect(detectSchemaType(Schema.String)).toBe("effect"); + }); + + it("should detect TypeBox schemas", () => { + expect(detectSchemaType(Type.String())).toBe("typebox"); + }); + + it("should return unknown for unsupported schemas", () => { + expect(detectSchemaType({ notASchema: true })).toBe("unknown"); + }); +}); diff --git a/packages/schema-to-json/tsconfig.json b/packages/schema-to-json/tsconfig.json new file mode 100644 index 000000000..5bf5eba8d --- /dev/null +++ b/packages/schema-to-json/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../.configs/tsconfig.base.json", + "references": [ + { + "path": "./tsconfig.src.json" + }, + { + "path": "./tsconfig.test.json" + } + ] +} \ No newline at end of file diff --git a/packages/schema-to-json/tsconfig.src.json b/packages/schema-to-json/tsconfig.src.json new file mode 100644 index 000000000..93f59a20f --- /dev/null +++ b/packages/schema-to-json/tsconfig.src.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "include": ["./src/**/*.ts"], + "exclude": ["src/**/*.test.ts"], + "compilerOptions": { + "isolatedDeclarations": false, + "composite": true, + "sourceMap": true, + "customConditions": ["@triggerdotdev/source"] + } +} diff --git a/packages/schema-to-json/tsconfig.test.json b/packages/schema-to-json/tsconfig.test.json new file mode 100644 index 000000000..c68227ee3 --- /dev/null +++ b/packages/schema-to-json/tsconfig.test.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "include": ["./tests/**/*.ts"], + "references": [{ "path": "./tsconfig.src.json" }], + "compilerOptions": { + "isolatedDeclarations": false, + "composite": true, + "sourceMap": true, + "types": ["vitest/globals"] + } +} diff --git a/packages/schema-to-json/vitest.config.ts b/packages/schema-to-json/vitest.config.ts new file mode 100644 index 000000000..c7da6b38e --- /dev/null +++ b/packages/schema-to-json/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + }, +}); \ No newline at end of file diff --git a/packages/trigger-sdk/package.json b/packages/trigger-sdk/package.json index d070d2049..fe4c7d502 100644 --- a/packages/trigger-sdk/package.json +++ b/packages/trigger-sdk/package.json @@ -77,7 +77,7 @@ "zod": "3.23.8" }, "peerDependencies": { - "zod": "^3.0.0", + "zod": "^3.0.0 || ^4.0.0", "ai": "^4.2.0" }, "peerDependenciesMeta": { diff --git a/packages/trigger-sdk/src/v3/index.ts b/packages/trigger-sdk/src/v3/index.ts index 94e3e23cd..a9a833fe5 100644 --- a/packages/trigger-sdk/src/v3/index.ts +++ b/packages/trigger-sdk/src/v3/index.ts @@ -14,6 +14,7 @@ export * from "./timeout.js"; export * from "./webhooks.js"; export * from "./locals.js"; export * from "./otel.js"; +export * from "./schemas.js"; export type { Context }; import type { Context } from "./shared.js"; diff --git a/packages/trigger-sdk/src/v3/schemas.ts b/packages/trigger-sdk/src/v3/schemas.ts new file mode 100644 index 000000000..65be53024 --- /dev/null +++ b/packages/trigger-sdk/src/v3/schemas.ts @@ -0,0 +1,2 @@ +// Re-export JSON Schema types for user convenience +export type { JSONSchema } from "@trigger.dev/core/v3"; \ No newline at end of file diff --git a/packages/trigger-sdk/src/v3/shared.ts b/packages/trigger-sdk/src/v3/shared.ts index 487c16308..05300dc4f 100644 --- a/packages/trigger-sdk/src/v3/shared.ts +++ b/packages/trigger-sdk/src/v3/shared.ts @@ -76,6 +76,7 @@ import type { TaskBatchOutputHandle, TaskIdentifier, TaskOptions, + TaskOptionsWithSchema, TaskOutput, TaskOutputHandle, TaskPayload, @@ -128,6 +129,16 @@ export function queue(options: QueueOptions): Queue { return options; } +// Overload: when payloadSchema is provided, payload type should be any +export function createTask< + TIdentifier extends string, + TOutput = unknown, + TInitOutput extends InitOutput = any, +>( + params: TaskOptionsWithSchema +): Task; + +// Overload: normal case without payloadSchema export function createTask< TIdentifier extends string, TInput = void, @@ -135,7 +146,18 @@ export function createTask< TInitOutput extends InitOutput = any, >( params: TaskOptions -): Task { +): Task; + +export function createTask< + TIdentifier extends string, + TInput = void, + TOutput = unknown, + TInitOutput extends InitOutput = any, +>( + params: + | TaskOptions + | TaskOptionsWithSchema +): Task | Task { const task: Task = { id: params.id, description: params.description, @@ -204,6 +226,7 @@ export function createTask< retry: params.retry ? { ...defaultRetryOptions, ...params.retry } : undefined, machine: typeof params.machine === "string" ? { preset: params.machine } : params.machine, maxDuration: params.maxDuration, + payloadSchema: params.jsonSchema, fns: { run: params.run, }, @@ -338,6 +361,7 @@ export function createSchemaTask< run: params.run, parsePayload, }, + schema: params.schema, }); const queue = params.queue; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0c91127b6..c2d7021ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1302,6 +1302,9 @@ importers: '@trigger.dev/core': specifier: workspace:4.0.0-v4-beta.26 version: link:../core + '@trigger.dev/schema-to-json': + specifier: workspace:4.0.0-v4-beta.26 + version: link:../schema-to-json ansi-escapes: specifier: ^7.0.0 version: 7.0.0 @@ -1795,6 +1798,52 @@ importers: specifier: 4.17.0 version: 4.17.0 + packages/schema-to-json: + dependencies: + '@sodaru/yup-to-json-schema': + specifier: ^2.0.1 + version: 2.0.1 + '@trigger.dev/core': + specifier: workspace:* + version: link:../core + zod-to-json-schema: + specifier: ^3.24.5 + version: 3.24.5(zod@3.25.76) + devDependencies: + '@arethetypeswrong/cli': + specifier: ^0.15.4 + version: 0.15.4 + '@sinclair/typebox': + specifier: ^0.34.3 + version: 0.34.38 + arktype: + specifier: ^2.0.0 + version: 2.1.20 + effect: + specifier: ^3.11.11 + version: 3.17.1 + rimraf: + specifier: 6.0.1 + version: 6.0.1 + runtypes: + specifier: ^6.7.0 + version: 6.7.0 + superstruct: + specifier: ^2.0.2 + version: 2.0.2 + tshy: + specifier: ^3.0.2 + version: 3.0.2 + valibot: + specifier: ^1.1.0 + version: 1.1.0(typescript@5.5.4) + yup: + specifier: ^1.7.0 + version: 1.7.0 + zod: + specifier: ^3.24.1 || ^4.0.0 + version: 3.25.76 + packages/trigger-sdk: dependencies: '@opentelemetry/api': @@ -2111,12 +2160,18 @@ importers: references/hello-world: dependencies: + '@sinclair/typebox': + specifier: ^0.34.3 + version: 0.34.38 '@trigger.dev/build': specifier: workspace:* version: link:../../packages/build '@trigger.dev/sdk': specifier: workspace:* version: link:../../packages/trigger-sdk + arktype: + specifier: ^2.0.0 + version: 2.1.20 openai: specifier: ^4.97.0 version: 4.97.0(ws@8.12.0)(zod@3.23.8) @@ -2126,6 +2181,9 @@ importers: replicate: specifier: ^1.0.1 version: 1.0.1 + yup: + specifier: ^1.6.1 + version: 1.6.1 zod: specifier: 3.23.8 version: 3.23.8 @@ -3056,10 +3114,18 @@ packages: '@ark/util': 0.18.0 dev: false + /@ark/schema@0.46.0: + resolution: {integrity: sha512-c2UQdKgP2eqqDArfBqQIJppxJHvNNXuQPeuSPlDML4rjw+f1cu0qAlzOG4b8ujgm9ctIDWwhpyw6gjG5ledIVQ==} + dependencies: + '@ark/util': 0.46.0 + /@ark/util@0.18.0: resolution: {integrity: sha512-TpHY532LKQwwYHui5NN/eO/6eSiSMvf652YNt1BsV7fya7RzXL27IiU9x4bm7jTFZxLQGYDQTB7nw41TqeuF4g==} dev: false + /@ark/util@0.46.0: + resolution: {integrity: sha512-JPy/NGWn/lvf1WmGCPw2VGpBg5utZraE84I7wli18EDF3p3zc/e9WolT35tINeZO3l7C77SjqRJeAUoT0CvMRg==} + /@arr/every@1.0.1: resolution: {integrity: sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==} engines: {node: '>=4'} @@ -6193,6 +6259,7 @@ packages: /@effect/schema@0.72.2(effect@3.7.2): resolution: {integrity: sha512-/x1BIA2pqcUidNrOMmwYe6Z58KtSgHSc5iJu7bNwIxi2LHMVuUao1BvpI5x6i7T/zkoi4dd1S6qasZzJIYDjdw==} + deprecated: this package has been merged into the main effect package peerDependencies: effect: ^3.7.2 dependencies: @@ -6202,11 +6269,12 @@ packages: /@effect/schema@0.75.5(effect@3.17.1): resolution: {integrity: sha512-TQInulTVCuF+9EIbJpyLP6dvxbQJMphrnRqgexm/Ze39rSjfhJuufF7XvU3SxTgg3HnL7B/kpORTJbHhlE6thw==} + deprecated: this package has been merged into the main effect package peerDependencies: effect: ^3.9.2 dependencies: effect: 3.17.1 - fast-check: 3.22.0 + fast-check: 3.23.2 dev: false /@electric-sql/client@0.4.0: @@ -8982,10 +9050,6 @@ packages: '@jridgewell/trace-mapping': 0.3.25 dev: true - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: false - /@jridgewell/sourcemap-codec@1.5.0: resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} @@ -10156,7 +10220,7 @@ packages: '@opentelemetry/api': 1.4.1 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.4.1) '@opentelemetry/instrumentation': 0.49.1(@opentelemetry/api@1.4.1) - '@opentelemetry/semantic-conventions': 1.36.0 + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color dev: true @@ -11263,6 +11327,7 @@ packages: /@opentelemetry/semantic-conventions@1.36.0: resolution: {integrity: sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ==} engines: {node: '>=14'} + dev: false /@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0): resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} @@ -17847,6 +17912,9 @@ packages: resolution: {integrity: sha512-75232GRx3wp3P7NP+yc4nRK3XUAnaQShxTAzapgmQrgs0QvSq0/mOJGoZXRpH15cFCKyys+4laCPbBselqJ5Ag==} dev: false + /@sinclair/typebox@0.34.38: + resolution: {integrity: sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==} + /@sindresorhus/is@0.14.0: resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==} engines: {node: '>=6'} @@ -19040,6 +19108,10 @@ packages: - supports-color dev: false + /@sodaru/yup-to-json-schema@2.0.1: + resolution: {integrity: sha512-lWb0Wiz8KZ9ip/dY1eUqt7fhTPmL24p6Hmv5Fd9pzlzAdw/YNcWZr+tiCT4oZ4Zyxzi9+1X4zv82o7jYvcFxYA==} + dev: false + /@splinetool/react-spline@2.2.6(@splinetool/runtime@1.9.98)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-y9L2VEbnC6FNZZu8XMmWM9YTTTWal6kJVfP05Amf0QqDNzCSumKsJxZyGUODvuCmiAvy0PfIfEsiVKnSxvhsDw==} peerDependencies: @@ -19067,7 +19139,6 @@ packages: /@standard-schema/spec@1.0.0: resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - dev: false /@stricli/auto-complete@1.2.0: resolution: {integrity: sha512-r9/msiloVmTF95mdhe04Uzqei1B0ZofhYRLeiPqpJ1W1RMCC8p9iW7kqBZEbALl2aRL5ZK9OEW3Q1cIejH7KEQ==} @@ -21874,6 +21945,12 @@ packages: '@ark/util': 0.18.0 dev: false + /arktype@2.1.20: + resolution: {integrity: sha512-IZCEEXaJ8g+Ijd59WtSYwtjnqXiwM8sWQ5EjGamcto7+HVN9eK0C4p0zDlCuAwWhpqr6fIBkxPuYDl4/Mcj/+Q==} + dependencies: + '@ark/schema': 0.46.0 + '@ark/util': 0.46.0 + /array-buffer-byte-length@1.0.1: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} @@ -24311,7 +24388,6 @@ packages: dependencies: '@standard-schema/spec': 1.0.0 fast-check: 3.23.2 - dev: false /effect@3.7.2: resolution: {integrity: sha512-pV7l1+LSZFvVObj4zuy4nYiBaC7qZOfrKV6s/Ef4p3KueiQwZFgamazklwyZ+x7Nyj2etRDFvHE/xkThTfQD1w==} @@ -25890,7 +25966,6 @@ packages: engines: {node: '>=8.0.0'} dependencies: pure-rand: 6.1.0 - dev: false /fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} @@ -28774,7 +28849,7 @@ packages: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 dev: false /magicast@0.3.4: @@ -29765,7 +29840,7 @@ packages: workerd: 1.20240806.0 ws: 8.18.0(bufferutil@4.0.9) youch: 3.3.3 - zod: 3.23.8 + zod: 3.25.76 transitivePeerDependencies: - bufferutil - supports-color @@ -32326,8 +32401,8 @@ packages: '@mrleebo/prisma-ast': 0.7.0 '@prisma/generator-helper': 5.3.1 '@prisma/internals': 5.3.1 - typescript: 5.5.4 - zod: 3.23.8 + typescript: 5.8.3 + zod: 3.25.76 transitivePeerDependencies: - encoding - supports-color @@ -32447,7 +32522,6 @@ packages: /property-expr@2.0.6: resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} - dev: false /property-information@6.2.0: resolution: {integrity: sha512-kma4U7AFCTwpqq5twzC1YVIDXSqg6qQK6JN0smOw8fgRy1OkMi0CYSzFmsy6dnqSenamAtj0CyXMUJ1Mf6oROg==} @@ -32619,7 +32693,6 @@ packages: /pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - dev: false /purgecss@2.3.0: resolution: {integrity: sha512-BE5CROfVGsx2XIhxGuZAT7rTH9lLeQx/6M0P7DTXQH4IUc3BBzs9JUzt4yzGf3JrH9enkeq6YJBe9CTtkm1WmQ==} @@ -34060,7 +34133,6 @@ packages: /runtypes@6.7.0: resolution: {integrity: sha512-3TLdfFX8YHNFOhwHrSJza6uxVBmBrEjnNQlNXvXCdItS0Pdskfg5vVXUTWIN+Y23QR09jWpSl99UHkA83m4uWA==} - dev: false /rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} @@ -35353,7 +35425,6 @@ packages: /superstruct@2.0.2: resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} engines: {node: '>=14.0.0'} - dev: false /supertest@7.0.0: resolution: {integrity: sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==} @@ -35944,7 +36015,6 @@ packages: /tiny-case@1.0.3: resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} - dev: false /tiny-glob@0.2.9: resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} @@ -36111,7 +36181,6 @@ packages: /toposort@2.0.2: resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} - dev: false /totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} @@ -36595,7 +36664,6 @@ packages: /type-fest@2.19.0: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - dev: false /type-fest@4.10.3: resolution: {integrity: sha512-JLXyjizi072smKGGcZiAJDCNweT8J+AuRxmPZ1aG7TERg4ijx9REl8CNhbr36RV4qXqL1gO1FF9HL8OkVmmrsA==} @@ -36781,6 +36849,12 @@ packages: engines: {node: '>=14.17'} hasBin: true + /typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + /ufo@1.5.4: resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} @@ -37320,6 +37394,17 @@ packages: typescript: 5.5.4 dev: false + /valibot@1.1.0(typescript@5.5.4): + resolution: {integrity: sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 5.5.4 + dev: true + /validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -37572,7 +37657,7 @@ packages: dependencies: '@types/node': 20.14.14 esbuild: 0.20.2 - postcss: 8.5.3 + postcss: 8.5.4 rollup: 4.36.0 optionalDependencies: fsevents: 2.3.3 @@ -38258,6 +38343,24 @@ packages: type-fest: 2.19.0 dev: false + /yup@1.6.1: + resolution: {integrity: sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==} + dependencies: + property-expr: 2.0.6 + tiny-case: 1.0.3 + toposort: 2.0.2 + type-fest: 2.19.0 + dev: false + + /yup@1.7.0: + resolution: {integrity: sha512-VJce62dBd+JQvoc+fCVq+KZfPHr+hXaxCcVgotfwWvlR0Ja3ffYKaJBT8rptPOSKOGJDCUnW2C2JWpud7aRP6Q==} + dependencies: + property-expr: 2.0.6 + tiny-case: 1.0.3 + toposort: 2.0.2 + type-fest: 2.19.0 + dev: true + /zimmerframe@1.1.2: resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} @@ -38316,6 +38419,14 @@ packages: dependencies: zod: 3.23.8 + /zod-to-json-schema@3.24.5(zod@3.25.76): + resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + peerDependencies: + zod: ^3.24.1 + dependencies: + zod: 3.25.76 + dev: false + /zod-validation-error@1.5.0(zod@3.23.8): resolution: {integrity: sha512-/7eFkAI4qV0tcxMBB/3+d2c1P6jzzZYdYSlBuAklzMuCrJu5bzJfHS0yVAS87dRHVlhftd6RFJDIvv03JgkSbw==} engines: {node: '>=16.0.0'} @@ -38330,7 +38441,6 @@ packages: /zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - dev: false /zustand@4.5.5(@types/react@18.2.69)(react@18.2.0): resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} diff --git a/references/hello-world/package.json b/references/hello-world/package.json index 89dbeea91..352994052 100644 --- a/references/hello-world/package.json +++ b/references/hello-world/package.json @@ -8,10 +8,13 @@ "dependencies": { "@trigger.dev/build": "workspace:*", "@trigger.dev/sdk": "workspace:*", + "arktype": "^2.0.0", "openai": "^4.97.0", "puppeteer-core": "^24.15.0", "replicate": "^1.0.1", - "zod": "3.23.8" + "yup": "^1.6.1", + "zod": "3.23.8", + "@sinclair/typebox": "^0.34.3" }, "scripts": { "dev": "trigger dev", diff --git a/references/hello-world/src/trigger/example.ts b/references/hello-world/src/trigger/example.ts index 1eb7f1891..14fdd0faa 100644 --- a/references/hello-world/src/trigger/example.ts +++ b/references/hello-world/src/trigger/example.ts @@ -14,12 +14,12 @@ export const helloWorldTask = task({ logger.info("Hello, world from the onStart hook", { payload, init }); }, run: async (payload: any, { ctx }) => { - logger.info("Hello, world from the init", { ctx, payload }); + logger.info("Hello, world froms the init", { ctx, payload }); logger.info("env vars", { env: process.env, }); - logger.debug("debug: Hello, world!", { payload }); + logger.debug("debug: Hello, worlds!", { payload }); logger.info("info: Hello, world!", { payload }); logger.log("log: Hello, world!", { payload }); logger.warn("warn: Hello, world!", { payload }); diff --git a/references/hello-world/src/trigger/jsonSchema.ts b/references/hello-world/src/trigger/jsonSchema.ts new file mode 100644 index 000000000..f347f9d38 --- /dev/null +++ b/references/hello-world/src/trigger/jsonSchema.ts @@ -0,0 +1,413 @@ +import { task, schemaTask, logger, type JSONSchema } from "@trigger.dev/sdk/v3"; +import { z } from "zod"; +import * as y from "yup"; +import { type } from "arktype"; +import { Type, Static } from "@sinclair/typebox"; + +// =========================================== +// Example 1: Using schemaTask with Zod +// =========================================== +const userSchema = z.object({ + id: z.string().uuid(), + name: z.string().min(1), + email: z.string().email(), + age: z.number().int().min(0).max(150), + preferences: z + .object({ + newsletter: z.boolean().default(false), + theme: z.enum(["light", "dark"]).default("light"), + }) + .optional(), +}); + +export const processUserWithZod = schemaTask({ + id: "json-schema-zod-example", + schema: userSchema, + run: async (payload, { ctx }) => { + // payload is fully typed based on the Zod schema + logger.info("Processing user with Zod schema", { + userId: payload.id, + userName: payload.name, + }); + + // The schema is automatically converted to JSON Schema and synced + return { + processed: true, + userId: payload.id, + welcomeMessage: `Welcome ${payload.name}!`, + }; + }, +}); + +// =========================================== +// Example 2: Using plain task with manual JSON Schema +// =========================================== +export const processOrderManualSchema = task({ + id: "json-schema-manual-example", + // Manually provide JSON Schema for the payload + jsonSchema: { + $schema: "http://json-schema.org/draft-07/schema#", + type: "object", + title: "Order Processing Request", + description: "Schema for processing customer orders", + properties: { + orderId: { + type: "string", + pattern: "^ORD-[0-9]+$", + description: "Order ID in format ORD-XXXXX", + }, + customerId: { + type: "string", + format: "uuid", + }, + items: { + type: "array", + minItems: 1, + items: { + type: "object", + properties: { + productId: { type: "string" }, + quantity: { type: "integer", minimum: 1 }, + price: { type: "number", minimum: 0, multipleOf: 0.01 }, + }, + required: ["productId", "quantity", "price"], + additionalProperties: false, + }, + }, + totalAmount: { + type: "number", + minimum: 0, + multipleOf: 0.01, + }, + status: { + type: "string", + enum: ["pending", "processing", "shipped", "delivered"], + default: "pending", + }, + }, + required: ["orderId", "customerId", "items", "totalAmount"], + additionalProperties: false, + } satisfies JSONSchema, + run: async (payload, { ctx }) => { + logger.info("Processing order with manual JSON Schema", { + orderId: payload.orderId, + }); + + // Note: With plain tasks, the payload is typed as 'any' + // The JSON Schema will be used for documentation and validation on the server + return { + processed: true, + orderId: payload.orderId, + status: "processing", + }; + }, +}); + +// =========================================== +// Example 3: Using schemaTask with Yup +// =========================================== +const productSchema = y.object({ + sku: y + .string() + .required() + .matches(/^[A-Z]{3}-[0-9]{5}$/), + name: y.string().required().min(3).max(100), + description: y.string().max(500), + price: y.number().required().positive(), + categories: y.array().of(y.string()).min(1).required(), + inStock: y.boolean().default(true), +}); + +export const processProductWithYup = schemaTask({ + id: "json-schema-yup-example", + schema: productSchema, + run: async (payload, { ctx }) => { + logger.info("Processing product with Yup schema", { + sku: payload.sku, + name: payload.name, + }); + + return { + processed: true, + sku: payload.sku, + message: `Product ${payload.name} has been processed`, + }; + }, +}); + +// =========================================== +// Example 4: Using schemaTask with ArkType +// =========================================== +const invoiceSchema = type({ + invoiceNumber: "string", + date: "Date", + dueDate: "Date", + "discount?": "number", + lineItems: [ + { + description: "string", + quantity: "number", + unitPrice: "number", + }, + ], + customer: { + id: "string", + name: "string", + "taxId?": "string", + }, +}); + +export const processInvoiceWithArkType = schemaTask({ + id: "json-schema-arktype-example", + schema: invoiceSchema, + run: async (payload, { ctx }) => { + logger.info("Processing invoice with ArkType schema", { + invoiceNumber: payload.invoiceNumber, + customerName: payload.customer.name, + }); + + const total = payload.lineItems.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0); + + const discount = payload.discount || 0; + const finalAmount = total * (1 - discount / 100); + + return { + processed: true, + invoiceNumber: payload.invoiceNumber, + totalAmount: finalAmount, + }; + }, +}); + +// =========================================== +// Example 5: Using TypeBox (already JSON Schema) +// =========================================== +const eventSchema = Type.Object({ + eventId: Type.String({ format: "uuid" }), + eventType: Type.Union([ + Type.Literal("user.created"), + Type.Literal("user.updated"), + Type.Literal("user.deleted"), + Type.Literal("order.placed"), + Type.Literal("order.shipped"), + ]), + timestamp: Type.Integer({ minimum: 0 }), + userId: Type.String(), + metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown())), + payload: Type.Unknown(), +}); + +type EventType = Static; + +export const processEventWithTypeBox = task({ + id: "json-schema-typebox-example", + // TypeBox schemas are already JSON Schema compliant + jsonSchema: eventSchema, + run: async (payload: EventType, { ctx }) => { + // Cast to get TypeScript type safety + const event = payload; + + logger.info("Processing event with TypeBox schema", { + eventId: event.eventId, + eventType: event.eventType, + userId: event.userId, + }); + + // Handle different event types + switch (event.eventType) { + case "user.created": + logger.info("New user created", { userId: event.userId }); + break; + case "order.placed": + logger.info("Order placed", { userId: event.userId }); + break; + default: + logger.info("Event processed", { eventType: event.eventType }); + } + + return { + processed: true, + eventId: event.eventId, + eventType: event.eventType, + }; + }, +}); + +// =========================================== +// Example 6: Using plain task with a Zod schema +// =========================================== +// If you need to use a plain task but have a Zod schema, +// you should use schemaTask instead for better DX. +// This example shows what NOT to do: + +const notificationSchema = z.object({ + recipientId: z.string(), + type: z.enum(["email", "sms", "push"]), + subject: z.string().optional(), + message: z.string(), + priority: z.enum(["low", "normal", "high"]).default("normal"), + scheduledFor: z.date().optional(), + metadata: z.record(z.unknown()).optional(), +}); + +// ❌ Don't do this - use schemaTask instead! +export const sendNotificationBadExample = task({ + id: "json-schema-dont-do-this", + run: async (payload, { ctx }) => { + // You'd have to manually validate + const notification = notificationSchema.parse(payload); + + logger.info("This is not ideal - use schemaTask instead!"); + + return { sent: true }; + }, +}); + +// ✅ Do this instead - much better! +export const sendNotificationGoodExample = schemaTask({ + id: "json-schema-do-this-instead", + schema: notificationSchema, + run: async (notification, { ctx }) => { + // notification is already validated and typed! + logger.info("Sending notification", { + recipientId: notification.recipientId, + type: notification.type, + priority: notification.priority, + }); + + // Simulate sending notification + await new Promise((resolve) => setTimeout(resolve, 1000)); + + return { + sent: true, + notificationId: ctx.run.id, + recipientId: notification.recipientId, + type: notification.type, + }; + }, +}); + +// =========================================== +// Example 7: Complex nested schema with references +// =========================================== +const addressSchema = z.object({ + street: z.string(), + city: z.string(), + state: z.string().length(2), + zipCode: z.string().regex(/^\d{5}(-\d{4})?$/), + country: z.string().default("US"), +}); + +const companySchema = z.object({ + companyId: z.string().uuid(), + name: z.string(), + taxId: z.string().optional(), + addresses: z.object({ + billing: addressSchema, + shipping: addressSchema.optional(), + }), + contacts: z + .array( + z.object({ + name: z.string(), + email: z.string().email(), + phone: z.string().optional(), + role: z.enum(["primary", "billing", "technical"]), + }) + ) + .min(1), + settings: z.object({ + invoicePrefix: z.string().default("INV"), + paymentTerms: z.number().int().min(0).max(90).default(30), + currency: z.enum(["USD", "EUR", "GBP"]).default("USD"), + }), +}); + +export const processCompanyWithComplexSchema = schemaTask({ + id: "json-schema-complex-example", + schema: companySchema, + maxDuration: 300, // 5 minutes + retry: { + maxAttempts: 3, + factor: 2, + }, + run: async (payload, { ctx }) => { + logger.info("Processing company with complex schema", { + companyId: payload.companyId, + name: payload.name, + contactCount: payload.contacts.length, + }); + + // Process each contact + for (const contact of payload.contacts) { + logger.info("Processing contact", { + name: contact.name, + role: contact.role, + }); + } + + return { + processed: true, + companyId: payload.companyId, + name: payload.name, + primaryContact: payload.contacts.find((c) => c.role === "primary"), + }; + }, +}); + +// =========================================== +// Example 8: Demonstrating schema benefits +// =========================================== +export const triggerExamples = task({ + id: "json-schema-trigger-examples", + run: async (_, { ctx }) => { + logger.info("Triggering various schema examples"); + + // Trigger Zod example - TypeScript will enforce correct payload + await processUserWithZod.trigger({ + id: "550e8400-e29b-41d4-a716-446655440000", + name: "John Doe", + email: "john@example.com", + age: 30, + preferences: { + newsletter: true, + theme: "dark", + }, + }); + + // Trigger Yup example + await processProductWithYup.trigger({ + sku: "ABC-12345", + name: "Premium Widget", + description: "A high-quality widget for all your needs", + price: 99.99, + categories: ["electronics", "gadgets"], + inStock: true, + }); + + // Trigger manual schema example (no compile-time validation) + await processOrderManualSchema.trigger({ + orderId: "ORD-12345", + customerId: "550e8400-e29b-41d4-a716-446655440001", + items: [ + { + productId: "PROD-001", + quantity: 2, + price: 29.99, + }, + { + productId: "PROD-002", + quantity: 1, + price: 49.99, + }, + ], + totalAmount: 109.97, + status: "pending", + }); + + return { + message: "All examples triggered successfully", + timestamp: new Date().toISOString(), + }; + }, +}); \ No newline at end of file diff --git a/references/hello-world/src/trigger/jsonSchemaApi.ts b/references/hello-world/src/trigger/jsonSchemaApi.ts new file mode 100644 index 000000000..9ac121b36 --- /dev/null +++ b/references/hello-world/src/trigger/jsonSchemaApi.ts @@ -0,0 +1,343 @@ +import { task, schemaTask, logger, type JSONSchema } from "@trigger.dev/sdk/v3"; +import { z } from "zod"; + +// =========================================== +// Example: Webhook Handler with Schema Validation +// =========================================== + +// Define schemas for different webhook event types +const baseWebhookSchema = z.object({ + id: z.string(), + timestamp: z.string().datetime(), + type: z.string(), + version: z.literal("1.0"), +}); + +// Payment webhook events +const paymentEventSchema = baseWebhookSchema.extend({ + type: z.literal("payment"), + data: z.object({ + paymentId: z.string(), + amount: z.number().positive(), + currency: z.string().length(3), + status: z.enum(["pending", "processing", "completed", "failed"]), + customerId: z.string(), + paymentMethod: z.object({ + type: z.enum(["card", "bank_transfer", "paypal"]), + last4: z.string().optional(), + }), + metadata: z.record(z.string()).optional(), + }), +}); + +// Customer webhook events +const customerEventSchema = baseWebhookSchema.extend({ + type: z.literal("customer"), + data: z.object({ + customerId: z.string(), + action: z.enum(["created", "updated", "deleted"]), + email: z.string().email(), + name: z.string(), + subscription: z.object({ + status: z.enum(["active", "cancelled", "past_due"]), + plan: z.string(), + }).optional(), + }), +}); + +// Union of all webhook types +const webhookSchema = z.discriminatedUnion("type", [ + paymentEventSchema, + customerEventSchema, +]); + +export const handleWebhook = schemaTask({ + id: "handle-webhook", + schema: webhookSchema, + run: async (payload, { ctx }) => { + logger.info("Processing webhook", { + id: payload.id, + type: payload.type, + timestamp: payload.timestamp, + }); + + // TypeScript knows the exact shape based on the discriminated union + switch (payload.type) { + case "payment": + logger.info("Payment event received", { + paymentId: payload.data.paymentId, + amount: payload.data.amount, + status: payload.data.status, + }); + + if (payload.data.status === "completed") { + // Trigger order fulfillment + await fulfillOrder.trigger({ + customerId: payload.data.customerId, + paymentId: payload.data.paymentId, + amount: payload.data.amount, + }); + } + break; + + case "customer": + logger.info("Customer event received", { + customerId: payload.data.customerId, + action: payload.data.action, + }); + + if (payload.data.action === "created") { + // Send welcome email + await sendWelcomeEmail.trigger({ + email: payload.data.email, + name: payload.data.name, + }); + } + break; + } + + return { + processed: true, + eventId: payload.id, + eventType: payload.type, + }; + }, +}); + +// =========================================== +// Example: External API Integration +// =========================================== + +// Schema for making API requests to a third-party service +const apiRequestSchema = z.object({ + endpoint: z.enum(["/users", "/products", "/orders"]), + method: z.enum(["GET", "POST", "PUT", "DELETE"]), + params: z.record(z.string()).optional(), + body: z.unknown().optional(), + headers: z.record(z.string()).optional(), + retryOnError: z.boolean().default(true), +}); + +// Response schemas for different endpoints +const userResponseSchema = z.object({ + id: z.string(), + email: z.string().email(), + name: z.string(), + createdAt: z.string().datetime(), +}); + +const productResponseSchema = z.object({ + id: z.string(), + name: z.string(), + price: z.number(), + inStock: z.boolean(), +}); + +export const callExternalApi = schemaTask({ + id: "call-external-api", + schema: apiRequestSchema, + retry: { + maxAttempts: 3, + factor: 2, + minTimeoutInMs: 1000, + maxTimeoutInMs: 10000, + }, + run: async (payload, { ctx }) => { + logger.info("Making API request", { + endpoint: payload.endpoint, + method: payload.method, + }); + + // Simulate API call + const response = await makeApiCall(payload); + + // Validate response based on endpoint + let validatedResponse; + switch (payload.endpoint) { + case "/users": + validatedResponse = userResponseSchema.parse(response); + break; + case "/products": + validatedResponse = productResponseSchema.parse(response); + break; + default: + validatedResponse = response; + } + + return { + success: true, + endpoint: payload.endpoint, + response: validatedResponse, + }; + }, +}); + +// Helper function to simulate API calls +async function makeApiCall(request: z.infer) { + // Simulate network delay + await new Promise(resolve => setTimeout(resolve, 100)); + + // Return mock data based on endpoint + switch (request.endpoint) { + case "/users": + return { + id: "user_123", + email: "user@example.com", + name: "John Doe", + createdAt: new Date().toISOString(), + }; + case "/products": + return { + id: "prod_456", + name: "Premium Widget", + price: 99.99, + inStock: true, + }; + default: + return { message: "Success" }; + } +} + +// =========================================== +// Example: Batch Processing with Validation +// =========================================== + +const batchItemSchema = z.object({ + id: z.string(), + operation: z.enum(["create", "update", "delete"]), + resourceType: z.enum(["user", "product", "order"]), + data: z.record(z.unknown()), +}); + +const batchRequestSchema = z.object({ + batchId: z.string(), + items: z.array(batchItemSchema).min(1).max(100), + options: z.object({ + stopOnError: z.boolean().default(false), + parallel: z.boolean().default(true), + maxConcurrency: z.number().int().min(1).max(10).default(5), + }).default({}), +}); + +export const processBatch = schemaTask({ + id: "process-batch", + schema: batchRequestSchema, + maxDuration: 300, // 5 minutes for large batches + run: async (payload, { ctx }) => { + logger.info("Processing batch", { + batchId: payload.batchId, + itemCount: payload.items.length, + parallel: payload.options.parallel, + }); + + const results = []; + const errors = []; + + if (payload.options.parallel) { + // Process items in parallel with concurrency limit + const chunks = chunkArray(payload.items, payload.options.maxConcurrency); + + for (const chunk of chunks) { + const chunkResults = await Promise.allSettled( + chunk.map(item => processItem(item)) + ); + + chunkResults.forEach((result, index) => { + if (result.status === "fulfilled") { + results.push(result.value); + } else { + errors.push({ + item: chunk[index], + error: result.reason, + }); + + if (payload.options.stopOnError) { + throw new Error(`Batch processing stopped due to error in item ${chunk[index].id}`); + } + } + }); + } + } else { + // Process items sequentially + for (const item of payload.items) { + try { + const result = await processItem(item); + results.push(result); + } catch (error) { + errors.push({ item, error }); + + if (payload.options.stopOnError) { + throw new Error(`Batch processing stopped due to error in item ${item.id}`); + } + } + } + } + + return { + batchId: payload.batchId, + processed: results.length, + failed: errors.length, + results, + errors, + }; + }, +}); + +async function processItem(item: z.infer) { + logger.info("Processing batch item", { + id: item.id, + operation: item.operation, + resourceType: item.resourceType, + }); + + // Simulate processing + await new Promise(resolve => setTimeout(resolve, 50)); + + return { + id: item.id, + success: true, + operation: item.operation, + resourceType: item.resourceType, + }; +} + +function chunkArray(array: T[], size: number): T[][] { + const chunks: T[][] = []; + for (let i = 0; i < array.length; i += size) { + chunks.push(array.slice(i, i + size)); + } + return chunks; +} + +// =========================================== +// Helper Tasks +// =========================================== + +const orderSchema = z.object({ + customerId: z.string(), + paymentId: z.string(), + amount: z.number(), +}); + +export const fulfillOrder = schemaTask({ + id: "fulfill-order", + schema: orderSchema, + run: async (payload, { ctx }) => { + logger.info("Fulfilling order", payload); + return { fulfilled: true }; + }, +}); + +const welcomeEmailSchema = z.object({ + email: z.string().email(), + name: z.string(), +}); + +export const sendWelcomeEmail = schemaTask({ + id: "send-welcome-email", + schema: welcomeEmailSchema, + run: async (payload, { ctx }) => { + logger.info("Sending welcome email", payload); + return { sent: true }; + }, +}); \ No newline at end of file diff --git a/references/hello-world/src/trigger/jsonSchemaSimple.ts b/references/hello-world/src/trigger/jsonSchemaSimple.ts new file mode 100644 index 000000000..a844b1403 --- /dev/null +++ b/references/hello-world/src/trigger/jsonSchemaSimple.ts @@ -0,0 +1,235 @@ +import { task, schemaTask, logger, type JSONSchema } from "@trigger.dev/sdk/v3"; +import { z } from "zod"; + +// =========================================== +// The Two Main Approaches +// =========================================== + +// Approach 1: Using schemaTask (Recommended) +// - Automatic JSON Schema conversion +// - Full TypeScript type safety +// - Runtime validation built-in +const emailSchema = z.object({ + to: z.string().email(), + subject: z.string(), + body: z.string(), + attachments: z + .array( + z.object({ + filename: z.string(), + url: z.string().url(), + }) + ) + .optional(), +}); + +export const sendEmailSchemaTask = schemaTask({ + id: "send-email-schema-task", + schema: emailSchema, + run: async (payload, { ctx }) => { + // payload is fully typed as: + // { + // to: string; + // subject: string; + // body: string; + // attachments?: Array<{ filename: string; url: string; }>; + // } + + logger.info("Sending email", { + to: payload.to, + subject: payload.subject, + hasAttachments: !!payload.attachments?.length, + }); + + // Your email sending logic here... + + return { + sent: true, + messageId: `msg_${ctx.run.id}`, + sentAt: new Date().toISOString(), + }; + }, +}); + +// Approach 2: Using plain task with payloadSchema +// - Manual JSON Schema definition +// - No automatic type inference (payload is 'any') +// - Good for when you already have JSON Schema definitions +export const sendEmailPlainTask = task({ + id: "send-email-plain-task", + jsonSchema: { + type: "object", + properties: { + to: { + type: "string", + format: "email", + description: "Recipient email address", + }, + subject: { + type: "string", + maxLength: 200, + }, + body: { + type: "string", + }, + attachments: { + type: "array", + items: { + type: "object", + properties: { + filename: { type: "string" }, + url: { type: "string", format: "uri" }, + }, + required: ["filename", "url"], + }, + }, + }, + required: ["to", "subject", "body"], + } satisfies JSONSchema, // Use 'satisfies' for type checking + run: async (payload, { ctx }) => { + // payload is typed as 'any' - you need to validate/cast it yourself + logger.info("Sending email", { + to: payload.to, + subject: payload.subject, + }); + + // Your email sending logic here... + + return { + sent: true, + messageId: `msg_${ctx.run.id}`, + sentAt: new Date().toISOString(), + }; + }, +}); + +// =========================================== +// Benefits of JSON Schema +// =========================================== + +// 1. Documentation - The schema is visible in the Trigger.dev dashboard +// 2. Validation - Invalid payloads are rejected before execution +// 3. Type Safety - With schemaTask, you get full TypeScript support +// 4. OpenAPI Generation - Can be used to generate API documentation +// 5. Client SDKs - Can generate typed clients for other languages + +export const demonstrateBenefits = task({ + id: "json-schema-benefits-demo", + run: async (_, { ctx }) => { + logger.info("Demonstrating JSON Schema benefits"); + + // With schemaTask, TypeScript prevents invalid payloads at compile time + try { + await sendEmailSchemaTask.trigger({ + to: "user@example.com", + subject: "Welcome!", + body: "Thanks for signing up!", + // TypeScript error if you try to add invalid fields + // invalidField: "This would cause a TypeScript error", + }); + } catch (error) { + logger.error("Failed to send email", { error }); + } + + // With plain task, validation happens at runtime + try { + await sendEmailPlainTask.trigger({ + to: "not-an-email", // This will fail validation at runtime + subject: "Test", + body: "Test email", + }); + } catch (error) { + logger.error("Failed validation", { error }); + } + + return { demonstrated: true }; + }, +}); + +// =========================================== +// Real-World Example: User Registration Flow +// =========================================== +const userRegistrationSchema = z.object({ + email: z.string().email(), + username: z + .string() + .min(3) + .max(20) + .regex(/^[a-zA-Z0-9_]+$/), + password: z.string().min(8), + profile: z.object({ + firstName: z.string(), + lastName: z.string(), + dateOfBirth: z.string().optional(), // ISO date string + preferences: z + .object({ + newsletter: z.boolean().default(false), + notifications: z.boolean().default(true), + }) + .default({}), + }), + referralCode: z.string().optional(), +}); + +export const registerUser = schemaTask({ + id: "register-user", + schema: userRegistrationSchema, + retry: { + maxAttempts: 3, + minTimeoutInMs: 1000, + maxTimeoutInMs: 10000, + }, + run: async (payload, { ctx }) => { + logger.info("Registering new user", { + email: payload.email, + username: payload.username, + }); + + // Step 1: Validate uniqueness + logger.info("Checking if user exists"); + // ... database check logic ... + + // Step 2: Create user account + logger.info("Creating user account"); + const userId = `user_${Date.now()}`; + // ... user creation logic ... + + // Step 3: Send welcome email + await sendEmailSchemaTask.trigger({ + to: payload.email, + subject: `Welcome to our platform, ${payload.profile.firstName}!`, + body: `Hi ${payload.profile.firstName},\n\nThanks for joining us...`, + }); + + // Step 4: Apply referral code if provided + if (payload.referralCode) { + logger.info("Processing referral code", { code: payload.referralCode }); + // ... referral logic ... + } + + return { + success: true, + userId, + username: payload.username, + welcomeEmailSent: true, + }; + }, +}); + +// =========================================== +// When to Use Each Approach +// =========================================== + +/* +Use schemaTask when: +- You're already using Zod, Yup, ArkType, etc. in your codebase +- You want TypeScript type inference +- You want runtime validation handled automatically +- You're building new tasks from scratch + +Use plain task with payloadSchema when: +- You have existing JSON Schema definitions +- You're migrating from another system that uses JSON Schema +- You need fine-grained control over the schema format +- You're working with generated schemas from OpenAPI/Swagger +*/ \ No newline at end of file From 8eecc7e54851baae38cd4039ee81aa244efe3a82 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 6 Aug 2025 13:56:57 +0100 Subject: [PATCH 015/101] chore: json schema missing changeset (#2353) --- .changeset/fluffy-mirrors-live.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fluffy-mirrors-live.md diff --git a/.changeset/fluffy-mirrors-live.md b/.changeset/fluffy-mirrors-live.md new file mode 100644 index 000000000..97e741eca --- /dev/null +++ b/.changeset/fluffy-mirrors-live.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/sdk": patch +--- + +Add jsonSchema support when indexing tasks From d950a969bd57697dc0a639be11b9bc24cda08b49 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 6 Aug 2025 14:48:43 +0100 Subject: [PATCH 016/101] Update zod package to version 3.25.76 across all modules (#2352) * Update zod package to version 3.25.76 across all modules Update the zod library from version 3.23.8 to 3.25.76 in multiple package files to ensure compatibility and take advantage of new features or bug fixes introduced in recent releases. Keeping all modules synchronized with the latest version of zod helps maintain consistency across the project and reduces potential compatibility issues. - Modified zod version in apps/supervisor, webapp, and various internal packages. - Updated zod references in pnpm-lock.yaml to reflect the new version. - Ensure dependencies that rely on zod are using the updated version to avoid mismatches. * Add changeset --- .changeset/swift-vans-dress.md | 7 + apps/supervisor/package.json | 2 +- apps/webapp/package.json | 2 +- internal-packages/clickhouse/package.json | 2 +- internal-packages/emails/package.json | 2 +- internal-packages/run-engine/package.json | 2 +- .../schedule-engine/package.json | 2 +- internal-packages/zod-worker/package.json | 2 +- packages/cli-v3/package.json | 2 +- packages/core/package.json | 2 +- packages/redis-worker/package.json | 2 +- packages/trigger-sdk/package.json | 2 +- pnpm-lock.yaml | 445 +++++++++--------- references/d3-chat/package.json | 2 +- references/d3-openai-agents/package.json | 2 +- references/hello-world/package.json | 2 +- references/nextjs-realtime/package.json | 2 +- references/python-catalog/package.json | 2 +- references/test-tasks/package.json | 2 +- references/v3-catalog/package.json | 2 +- 20 files changed, 244 insertions(+), 244 deletions(-) create mode 100644 .changeset/swift-vans-dress.md diff --git a/.changeset/swift-vans-dress.md b/.changeset/swift-vans-dress.md new file mode 100644 index 000000000..1ccbf2027 --- /dev/null +++ b/.changeset/swift-vans-dress.md @@ -0,0 +1,7 @@ +--- +"@trigger.dev/sdk": patch +"trigger.dev": patch +"@trigger.dev/core": patch +--- + +Upgrade to zod 3.25.76 diff --git a/apps/supervisor/package.json b/apps/supervisor/package.json index ae3654927..9cce9d5fe 100644 --- a/apps/supervisor/package.json +++ b/apps/supervisor/package.json @@ -19,7 +19,7 @@ "prom-client": "^15.1.0", "socket.io": "4.7.4", "std-env": "^3.8.0", - "zod": "3.23.8" + "zod": "3.25.76" }, "devDependencies": { "@types/dockerode": "^3.3.33" diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 80b7c9614..69473b78b 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -203,7 +203,7 @@ "ulidx": "^2.2.1", "uuid": "^9.0.0", "ws": "^8.11.0", - "zod": "3.23.8", + "zod": "3.25.76", "zod-error": "1.5.0", "zod-validation-error": "^1.5.0" }, diff --git a/internal-packages/clickhouse/package.json b/internal-packages/clickhouse/package.json index d85051b50..efa7cffd1 100644 --- a/internal-packages/clickhouse/package.json +++ b/internal-packages/clickhouse/package.json @@ -9,7 +9,7 @@ "@clickhouse/client": "^1.11.1", "@internal/tracing": "workspace:*", "@trigger.dev/core": "workspace:*", - "zod": "3.23.8", + "zod": "3.25.76", "zod-error": "1.5.0" }, "devDependencies": { diff --git a/internal-packages/emails/package.json b/internal-packages/emails/package.json index 85cb7abe0..e5d35cb2e 100644 --- a/internal-packages/emails/package.json +++ b/internal-packages/emails/package.json @@ -17,7 +17,7 @@ "react-email": "^2.1.1", "resend": "^3.2.0", "tiny-invariant": "^1.2.0", - "zod": "3.23.8" + "zod": "3.25.76" }, "devDependencies": { "@types/nodemailer": "^6.4.17", diff --git a/internal-packages/run-engine/package.json b/internal-packages/run-engine/package.json index 62f083789..a12abc73e 100644 --- a/internal-packages/run-engine/package.json +++ b/internal-packages/run-engine/package.json @@ -30,7 +30,7 @@ "nanoid": "3.3.8", "redlock": "5.0.0-beta.2", "seedrandom": "^3.0.5", - "zod": "3.23.8" + "zod": "3.25.76" }, "devDependencies": { "@internal/testcontainers": "workspace:*", diff --git a/internal-packages/schedule-engine/package.json b/internal-packages/schedule-engine/package.json index ecdf15124..86929a393 100644 --- a/internal-packages/schedule-engine/package.json +++ b/internal-packages/schedule-engine/package.json @@ -22,7 +22,7 @@ "cron-parser": "^4.9.0", "cronstrue": "^2.50.0", "nanoid": "3.3.8", - "zod": "3.23.8" + "zod": "3.25.76" }, "devDependencies": { "@internal/testcontainers": "workspace:*", diff --git a/internal-packages/zod-worker/package.json b/internal-packages/zod-worker/package.json index 1d9cbad93..352fa9452 100644 --- a/internal-packages/zod-worker/package.json +++ b/internal-packages/zod-worker/package.json @@ -10,7 +10,7 @@ "@trigger.dev/database": "workspace:*", "graphile-worker": "0.16.6", "lodash.omit": "^4.5.0", - "zod": "3.23.8" + "zod": "3.25.76" }, "devDependencies": { "@types/lodash.omit": "^4.5.7", diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index 74e935590..b6a20b86d 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -135,7 +135,7 @@ "tinyglobby": "^0.2.10", "ws": "^8.18.0", "xdg-app-paths": "^8.3.0", - "zod": "3.23.8", + "zod": "3.25.76", "zod-validation-error": "^1.5.0" }, "engines": { diff --git a/packages/core/package.json b/packages/core/package.json index bf14dfdbd..a6afde03e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -196,7 +196,7 @@ "superjson": "^2.2.1", "tinyexec": "^0.3.2", "uncrypto": "^0.1.3", - "zod": "3.23.8", + "zod": "3.25.76", "zod-error": "1.5.0", "zod-validation-error": "^1.5.0" }, diff --git a/packages/redis-worker/package.json b/packages/redis-worker/package.json index 3625e0f13..3ba44752e 100644 --- a/packages/redis-worker/package.json +++ b/packages/redis-worker/package.json @@ -27,7 +27,7 @@ "lodash.omit": "^4.5.0", "nanoid": "^5.0.7", "p-limit": "^6.2.0", - "zod": "3.23.8", + "zod": "3.25.76", "cron-parser": "^4.9.0" }, "devDependencies": { diff --git a/packages/trigger-sdk/package.json b/packages/trigger-sdk/package.json index fe4c7d502..6dc5977d5 100644 --- a/packages/trigger-sdk/package.json +++ b/packages/trigger-sdk/package.json @@ -74,7 +74,7 @@ "tshy": "^3.0.2", "tsx": "4.17.0", "typed-emitter": "^2.1.0", - "zod": "3.23.8" + "zod": "3.25.76" }, "peerDependencies": { "zod": "^3.0.0 || ^4.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c2d7021ef..6b3af7126 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -182,8 +182,8 @@ importers: specifier: ^3.8.0 version: 3.8.1 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@types/dockerode': specifier: ^3.3.33 @@ -193,7 +193,7 @@ importers: dependencies: '@ai-sdk/openai': specifier: ^1.3.23 - version: 1.3.23(zod@3.23.8) + version: 1.3.23(zod@3.25.76) '@ariakit/react': specifier: ^0.4.6 version: 0.4.6(react-dom@18.2.0)(react@18.2.0) @@ -244,7 +244,7 @@ importers: version: 0.9.2(react@18.2.0) '@conform-to/zod': specifier: 0.9.2 - version: 0.9.2(@conform-to/dom@0.9.2)(zod@3.23.8) + version: 0.9.2(@conform-to/dom@0.9.2)(zod@3.25.76) '@depot/cli': specifier: 0.0.1-cli.2.80.0 version: 0.0.1-cli.2.80.0 @@ -469,7 +469,7 @@ importers: version: 0.9.14 ai: specifier: ^4.3.19 - version: 4.3.19(react@18.2.0)(zod@3.23.8) + version: 4.3.19(react@18.2.0)(zod@3.25.76) assert-never: specifier: ^1.2.1 version: 1.2.1 @@ -652,7 +652,7 @@ importers: version: 0.3.1(@remix-run/react@2.1.0)(@remix-run/server-runtime@2.1.0)(react@18.2.0) remix-utils: specifier: ^7.7.0 - version: 7.7.0(@remix-run/node@2.1.0)(@remix-run/react@2.1.0)(@remix-run/router@1.15.3)(intl-parse-accept-language@1.0.0)(react@18.2.0)(zod@3.23.8) + version: 7.7.0(@remix-run/node@2.1.0)(@remix-run/react@2.1.0)(@remix-run/router@1.15.3)(intl-parse-accept-language@1.0.0)(react@18.2.0)(zod@3.25.76) seedrandom: specifier: ^3.0.5 version: 3.0.5 @@ -711,14 +711,14 @@ importers: specifier: ^8.11.0 version: 8.12.0 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 zod-error: specifier: 1.5.0 version: 1.5.0 zod-validation-error: specifier: ^1.5.0 - version: 1.5.0(zod@3.23.8) + version: 1.5.0(zod@3.25.76) devDependencies: '@internal/clickhouse': specifier: workspace:* @@ -951,8 +951,8 @@ importers: specifier: workspace:* version: link:../../packages/core zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 zod-error: specifier: 1.5.0 version: 1.5.0 @@ -1004,8 +1004,8 @@ importers: specifier: ^1.2.0 version: 1.3.1 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@types/nodemailer': specifier: ^6.4.17 @@ -1103,8 +1103,8 @@ importers: specifier: ^3.0.5 version: 3.0.5 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@internal/testcontainers': specifier: workspace:* @@ -1143,8 +1143,8 @@ importers: specifier: 3.3.8 version: 3.3.8 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@internal/testcontainers': specifier: workspace:* @@ -1220,8 +1220,8 @@ importers: specifier: ^4.5.0 version: 4.5.0 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@types/lodash.omit': specifier: ^4.5.7 @@ -1435,11 +1435,11 @@ importers: specifier: ^8.3.0 version: 8.3.0 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 zod-validation-error: specifier: ^1.5.0 - version: 1.5.0(zod@3.23.8) + version: 1.5.0(zod@3.25.76) devDependencies: '@epic-web/test-server': specifier: ^0.1.0 @@ -1592,18 +1592,18 @@ importers: specifier: ^0.1.3 version: 0.1.3 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 zod-error: specifier: 1.5.0 version: 1.5.0 zod-validation-error: specifier: ^1.5.0 - version: 1.5.0(zod@3.23.8) + version: 1.5.0(zod@3.25.76) devDependencies: '@ai-sdk/provider-utils': specifier: ^1.0.22 - version: 1.0.22(zod@3.23.8) + version: 1.0.22(zod@3.25.76) '@arethetypeswrong/cli': specifier: ^0.15.4 version: 0.15.4 @@ -1624,7 +1624,7 @@ importers: version: 4.0.14 ai: specifier: ^3.4.33 - version: 3.4.33(react@18.3.1)(svelte@5.33.14)(vue@3.5.16)(zod@3.23.8) + version: 3.4.33(react@18.3.1)(svelte@5.33.14)(vue@3.5.16)(zod@3.25.76) defu: specifier: ^6.1.4 version: 6.1.4 @@ -1733,8 +1733,8 @@ importers: specifier: ^6.2.0 version: 6.2.0 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@internal/redis': specifier: workspace:* @@ -1900,7 +1900,7 @@ importers: version: 8.5.4 ai: specifier: ^4.2.0 - version: 4.2.5(react@18.3.1)(zod@3.23.8) + version: 4.2.5(react@18.3.1)(zod@3.25.76) encoding: specifier: ^0.1.13 version: 0.1.13 @@ -1917,8 +1917,8 @@ importers: specifier: ^2.1.0 version: 2.1.0 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 references/bun-catalog: dependencies: @@ -1937,10 +1937,10 @@ importers: dependencies: '@ai-sdk/anthropic': specifier: ^1.2.4 - version: 1.2.4(zod@3.23.8) + version: 1.2.4(zod@3.25.76) '@ai-sdk/openai': specifier: 1.3.3 - version: 1.3.3(zod@3.23.8) + version: 1.3.3(zod@3.25.76) '@e2b/code-interpreter': specifier: ^1.1.0 version: 1.1.0 @@ -1985,7 +1985,7 @@ importers: version: 0.10.0 ai: specifier: 4.2.5 - version: 4.2.5(react@19.0.0)(zod@3.23.8) + version: 4.2.5(react@19.0.0)(zod@3.25.76) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -2020,8 +2020,8 @@ importers: specifier: ^1.2.4 version: 1.2.4 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@tailwindcss/postcss': specifier: ^4 @@ -2058,7 +2058,7 @@ importers: dependencies: '@ai-sdk/openai': specifier: 1.3.3 - version: 1.3.3(zod@3.23.8) + version: 1.3.3(zod@3.25.76) '@slack/web-api': specifier: 7.9.1 version: 7.9.1 @@ -2076,7 +2076,7 @@ importers: version: 0.10.0 ai: specifier: 4.2.5 - version: 4.2.5(react@19.0.0)(zod@3.23.8) + version: 4.2.5(react@19.0.0)(zod@3.25.76) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -2105,11 +2105,11 @@ importers: specifier: ^1.2.4 version: 1.2.4 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 zod-to-json-schema: specifier: ^3.24.5 - version: 3.24.5(zod@3.23.8) + version: 3.24.5(zod@3.25.76) devDependencies: '@tailwindcss/postcss': specifier: ^4 @@ -2174,7 +2174,7 @@ importers: version: 2.1.20 openai: specifier: ^4.97.0 - version: 4.97.0(ws@8.12.0)(zod@3.23.8) + version: 4.97.0(ws@8.12.0)(zod@3.25.76) puppeteer-core: specifier: ^24.15.0 version: 24.15.0 @@ -2185,8 +2185,8 @@ importers: specifier: ^1.6.1 version: 1.6.1 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: trigger.dev: specifier: workspace:* @@ -2208,7 +2208,7 @@ importers: dependencies: '@ai-sdk/openai': specifier: ^1.0.1 - version: 1.0.1(zod@3.23.8) + version: 1.0.1(zod@3.25.76) '@fal-ai/serverless-client': specifier: ^0.15.0 version: 0.15.0 @@ -2247,7 +2247,7 @@ importers: version: 7.0.3(next@14.2.21)(react@18.3.1)(uploadthing@7.1.0) ai: specifier: ^4.0.0 - version: 4.0.0(react@18.3.1)(zod@3.23.8) + version: 4.0.0(react@18.3.1)(zod@3.25.76) class-variance-authority: specifier: ^0.7.0 version: 0.7.0 @@ -2268,7 +2268,7 @@ importers: version: 14.2.21(@opentelemetry/api@1.9.0)(@playwright/test@1.37.0)(react-dom@18.2.0)(react@18.3.1) openai: specifier: ^4.68.4 - version: 4.68.4(zod@3.23.8) + version: 4.68.4(zod@3.25.76) react: specifier: ^18 version: 18.3.1 @@ -2285,8 +2285,8 @@ importers: specifier: ^7.1.0 version: 7.1.0(next@14.2.21)(tailwindcss@3.4.1) zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@next/bundle-analyzer': specifier: ^15.0.2 @@ -2319,8 +2319,8 @@ importers: specifier: workspace:* version: link:../../packages/trigger-sdk zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@trigger.dev/build': specifier: workspace:* @@ -2338,8 +2338,8 @@ importers: specifier: workspace:* version: link:../../packages/trigger-sdk zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@trigger.dev/build': specifier: workspace:* @@ -2385,10 +2385,10 @@ importers: version: 2.2.1 '@t3-oss/env-core': specifier: ^0.11.0 - version: 0.11.0(typescript@5.5.4)(zod@3.23.8) + version: 0.11.0(typescript@5.5.4)(zod@3.25.76) '@t3-oss/env-nextjs': specifier: ^0.10.1 - version: 0.10.1(typescript@5.5.4)(zod@3.23.8) + version: 0.10.1(typescript@5.5.4)(zod@3.25.76) '@traceloop/instrumentation-openai': specifier: ^0.10.0 version: 0.10.0(@opentelemetry/api@1.4.1) @@ -2400,7 +2400,7 @@ importers: version: 0.14.0(@sinclair/typebox@0.33.17) ai: specifier: ^3.3.24 - version: 3.3.24(openai@4.56.0)(react@19.0.0-rc.0)(svelte@5.33.14)(vue@3.5.16)(zod@3.23.8) + version: 3.3.24(openai@4.56.0)(react@19.0.0-rc.0)(svelte@5.33.14)(vue@3.5.16)(zod@3.25.76) arktype: specifier: 2.0.0-rc.17 version: 2.0.0-rc.17 @@ -2436,7 +2436,7 @@ importers: version: 1.3.6 openai: specifier: ^4.47.0 - version: 4.56.0(zod@3.23.8) + version: 4.56.0(zod@3.25.76) pg: specifier: ^8.11.5 version: 8.11.5 @@ -2492,8 +2492,8 @@ importers: specifier: ^0.0.11 version: 0.0.11 zod: - specifier: 3.23.8 - version: 3.23.8 + specifier: 3.25.76 + version: 3.25.76 devDependencies: '@opentelemetry/core': specifier: ^1.22.0 @@ -2590,51 +2590,51 @@ packages: resolution: {integrity: sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==} dev: false - /@ai-sdk/anthropic@1.2.4(zod@3.23.8): + /@ai-sdk/anthropic@1.2.4(zod@3.25.76): resolution: {integrity: sha512-dAN6MXvLffeFVAr2gz3RGvOTgX1KL/Yn5q1l4/Dt0TUeDjQgCt4AbbYxZZB2qIAYzQvoyAFPhlw0sB3nNizG/g==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 dependencies: '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.3(zod@3.23.8) - zod: 3.23.8 + '@ai-sdk/provider-utils': 2.2.3(zod@3.25.76) + zod: 3.25.76 dev: false - /@ai-sdk/openai@1.0.1(zod@3.23.8): + /@ai-sdk/openai@1.0.1(zod@3.25.76): resolution: {integrity: sha512-snZge8457afWlosVNUn+BG60MrxAPOOm3zmIMxJZih8tneNSiRbTVCbSzAtq/9vsnOHDe5RR83PRl85juOYEnA==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 dependencies: '@ai-sdk/provider': 1.0.0 - '@ai-sdk/provider-utils': 2.0.0(zod@3.23.8) - zod: 3.23.8 + '@ai-sdk/provider-utils': 2.0.0(zod@3.25.76) + zod: 3.25.76 dev: false - /@ai-sdk/openai@1.3.23(zod@3.23.8): + /@ai-sdk/openai@1.3.23(zod@3.25.76): resolution: {integrity: sha512-86U7rFp8yacUAOE/Jz8WbGcwMCqWvjK33wk5DXkfnAOEn3mx2r7tNSJdjukQFZbAK97VMXGPPHxF+aEARDXRXQ==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 dependencies: '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@3.23.8) - zod: 3.23.8 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 dev: false - /@ai-sdk/openai@1.3.3(zod@3.23.8): + /@ai-sdk/openai@1.3.3(zod@3.25.76): resolution: {integrity: sha512-CH57tonLB4DwkwqwnMmTCoIOR7cNW3bP5ciyloI7rBGJS/Bolemsoo+vn5YnwkyT9O1diWJyvYeTh7A4UfiYOw==} engines: {node: '>=18'} peerDependencies: zod: ^3.0.0 dependencies: '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.1(zod@3.23.8) - zod: 3.23.8 + '@ai-sdk/provider-utils': 2.2.1(zod@3.25.76) + zod: 3.25.76 dev: false - /@ai-sdk/provider-utils@1.0.17(zod@3.23.8): + /@ai-sdk/provider-utils@1.0.17(zod@3.25.76): resolution: {integrity: sha512-2VyeTH5DQ6AxqvwdyytKIeiZyYTyJffpufWjE67zM2sXMIHgYl7fivo8m5wVl6Cbf1dFPSGKq//C9s+lz+NHrQ==} engines: {node: '>=18'} peerDependencies: @@ -2647,10 +2647,10 @@ packages: eventsource-parser: 1.1.2 nanoid: 3.3.6 secure-json-parse: 2.7.0 - zod: 3.23.8 + zod: 3.25.76 dev: false - /@ai-sdk/provider-utils@1.0.22(zod@3.23.8): + /@ai-sdk/provider-utils@1.0.22(zod@3.25.76): resolution: {integrity: sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ==} engines: {node: '>=18'} peerDependencies: @@ -2663,10 +2663,10 @@ packages: eventsource-parser: 1.1.2 nanoid: 3.3.8 secure-json-parse: 2.7.0 - zod: 3.23.8 + zod: 3.25.76 dev: true - /@ai-sdk/provider-utils@2.0.0(zod@3.23.8): + /@ai-sdk/provider-utils@2.0.0(zod@3.25.76): resolution: {integrity: sha512-uITgVJByhtzuQU2ZW+2CidWRmQqTUTp6KADevy+4aRnmILZxY2LCt+UZ/ZtjJqq0MffwkuQPPY21ExmFAQ6kKA==} engines: {node: '>=18'} peerDependencies: @@ -2679,10 +2679,10 @@ packages: eventsource-parser: 3.0.0 nanoid: 5.1.5 secure-json-parse: 2.7.0 - zod: 3.23.8 + zod: 3.25.76 dev: false - /@ai-sdk/provider-utils@2.2.1(zod@3.23.8): + /@ai-sdk/provider-utils@2.2.1(zod@3.25.76): resolution: {integrity: sha512-BuExLp+NcpwsAVj1F4bgJuQkSqO/+roV9wM7RdIO+NVrcT8RBUTdXzf5arHt5T58VpK7bZyB2V9qigjaPHE+Dg==} engines: {node: '>=18'} peerDependencies: @@ -2691,9 +2691,9 @@ packages: '@ai-sdk/provider': 1.1.0 nanoid: 3.3.8 secure-json-parse: 2.7.0 - zod: 3.23.8 + zod: 3.25.76 - /@ai-sdk/provider-utils@2.2.3(zod@3.23.8): + /@ai-sdk/provider-utils@2.2.3(zod@3.25.76): resolution: {integrity: sha512-o3fWTzkxzI5Af7U7y794MZkYNEsxbjLam2nxyoUZSScqkacb7vZ3EYHLh21+xCcSSzEC161C7pZAGHtC0hTUMw==} engines: {node: '>=18'} peerDependencies: @@ -2702,10 +2702,10 @@ packages: '@ai-sdk/provider': 1.1.0 nanoid: 3.3.8 secure-json-parse: 2.7.0 - zod: 3.23.8 + zod: 3.25.76 dev: false - /@ai-sdk/provider-utils@2.2.8(zod@3.23.8): + /@ai-sdk/provider-utils@2.2.8(zod@3.25.76): resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} engines: {node: '>=18'} peerDependencies: @@ -2714,7 +2714,7 @@ packages: '@ai-sdk/provider': 1.1.3 nanoid: 3.3.8 secure-json-parse: 2.7.0 - zod: 3.23.8 + zod: 3.25.76 dev: false /@ai-sdk/provider@0.0.22: @@ -2751,7 +2751,7 @@ packages: json-schema: 0.4.0 dev: false - /@ai-sdk/react@0.0.53(react@19.0.0-rc.0)(zod@3.23.8): + /@ai-sdk/react@0.0.53(react@19.0.0-rc.0)(zod@3.25.76): resolution: {integrity: sha512-sIsmTFoR/QHvUUkltmHwP4bPjwy2vko6j/Nj8ayxLhEHs04Ug+dwXQyfA7MwgimEE3BcDQpWL8ikVj0m3ZILWQ==} engines: {node: '>=18'} peerDependencies: @@ -2763,14 +2763,14 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.17(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.40(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.17(zod@3.25.76) + '@ai-sdk/ui-utils': 0.0.40(zod@3.25.76) react: 19.0.0-rc.0 swr: 2.2.5(react@19.0.0-rc.0) - zod: 3.23.8 + zod: 3.25.76 dev: false - /@ai-sdk/react@0.0.70(react@18.3.1)(zod@3.23.8): + /@ai-sdk/react@0.0.70(react@18.3.1)(zod@3.25.76): resolution: {integrity: sha512-GnwbtjW4/4z7MleLiW+TOZC2M29eCg1tOUpuEiYFMmFNZK8mkrqM0PFZMo6UsYeUYMWqEOOcPOU9OQVJMJh7IQ==} engines: {node: '>=18'} peerDependencies: @@ -2782,15 +2782,15 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.50(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.22(zod@3.25.76) + '@ai-sdk/ui-utils': 0.0.50(zod@3.25.76) react: 18.3.1 swr: 2.2.5(react@18.3.1) throttleit: 2.1.0 - zod: 3.23.8 + zod: 3.25.76 dev: true - /@ai-sdk/react@1.0.0(react@18.3.1)(zod@3.23.8): + /@ai-sdk/react@1.0.0(react@18.3.1)(zod@3.25.76): resolution: {integrity: sha512-BDrZqQA07Btg64JCuhFvBgYV+tt2B8cXINzEqWknGoxqcwgdE8wSLG2gkXoLzyC2Rnj7oj0HHpOhLUxDCmoKZg==} engines: {node: '>=18'} peerDependencies: @@ -2802,15 +2802,15 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider-utils': 2.0.0(zod@3.23.8) - '@ai-sdk/ui-utils': 1.0.0(zod@3.23.8) + '@ai-sdk/provider-utils': 2.0.0(zod@3.25.76) + '@ai-sdk/ui-utils': 1.0.0(zod@3.25.76) react: 18.3.1 swr: 2.2.5(react@18.3.1) throttleit: 2.1.0 - zod: 3.23.8 + zod: 3.25.76 dev: false - /@ai-sdk/react@1.2.12(react@18.2.0)(zod@3.23.8): + /@ai-sdk/react@1.2.12(react@18.2.0)(zod@3.25.76): resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} engines: {node: '>=18'} peerDependencies: @@ -2820,15 +2820,15 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider-utils': 2.2.8(zod@3.23.8) - '@ai-sdk/ui-utils': 1.2.11(zod@3.23.8) + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) react: 18.2.0 swr: 2.2.5(react@18.2.0) throttleit: 2.1.0 - zod: 3.23.8 + zod: 3.25.76 dev: false - /@ai-sdk/react@1.2.2(react@18.3.1)(zod@3.23.8): + /@ai-sdk/react@1.2.2(react@18.3.1)(zod@3.25.76): resolution: {integrity: sha512-rxyNTFjUd3IilVOJFuUJV5ytZBYAIyRi50kFS2gNmSEiG4NHMBBm31ddrxI/i86VpY8gzZVp1/igtljnWBihUA==} engines: {node: '>=18'} peerDependencies: @@ -2838,15 +2838,15 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider-utils': 2.2.1(zod@3.23.8) - '@ai-sdk/ui-utils': 1.2.1(zod@3.23.8) + '@ai-sdk/provider-utils': 2.2.1(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.1(zod@3.25.76) react: 18.3.1 swr: 2.2.5(react@18.3.1) throttleit: 2.1.0 - zod: 3.23.8 + zod: 3.25.76 dev: true - /@ai-sdk/react@1.2.2(react@19.0.0)(zod@3.23.8): + /@ai-sdk/react@1.2.2(react@19.0.0)(zod@3.25.76): resolution: {integrity: sha512-rxyNTFjUd3IilVOJFuUJV5ytZBYAIyRi50kFS2gNmSEiG4NHMBBm31ddrxI/i86VpY8gzZVp1/igtljnWBihUA==} engines: {node: '>=18'} peerDependencies: @@ -2856,15 +2856,15 @@ packages: zod: optional: true dependencies: - '@ai-sdk/provider-utils': 2.2.1(zod@3.23.8) - '@ai-sdk/ui-utils': 1.2.1(zod@3.23.8) + '@ai-sdk/provider-utils': 2.2.1(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.1(zod@3.25.76) react: 19.0.0 swr: 2.2.5(react@19.0.0) throttleit: 2.1.0 - zod: 3.23.8 + zod: 3.25.76 dev: false - /@ai-sdk/solid@0.0.43(zod@3.23.8): + /@ai-sdk/solid@0.0.43(zod@3.25.76): resolution: {integrity: sha512-7PlPLaeMAu97oOY2gjywvKZMYHF+GDfUxYNcuJ4AZ3/MRBatzs/U2r4ClT1iH8uMOcMg02RX6UKzP5SgnUBjVw==} engines: {node: '>=18'} peerDependencies: @@ -2873,13 +2873,13 @@ packages: solid-js: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.17(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.40(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.17(zod@3.25.76) + '@ai-sdk/ui-utils': 0.0.40(zod@3.25.76) transitivePeerDependencies: - zod dev: false - /@ai-sdk/solid@0.0.54(zod@3.23.8): + /@ai-sdk/solid@0.0.54(zod@3.25.76): resolution: {integrity: sha512-96KWTVK+opdFeRubqrgaJXoNiDP89gNxFRWUp0PJOotZW816AbhUf4EnDjBjXTLjXL1n0h8tGSE9sZsRkj9wQQ==} engines: {node: '>=18'} peerDependencies: @@ -2888,13 +2888,13 @@ packages: solid-js: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.50(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.22(zod@3.25.76) + '@ai-sdk/ui-utils': 0.0.50(zod@3.25.76) transitivePeerDependencies: - zod dev: true - /@ai-sdk/svelte@0.0.45(svelte@5.33.14)(zod@3.23.8): + /@ai-sdk/svelte@0.0.45(svelte@5.33.14)(zod@3.25.76): resolution: {integrity: sha512-w5Sdl0ArFIM3Fp8BbH4TUvlrS84WP/jN/wC1+fghMOXd7ceVO3Yhs9r71wTqndhgkLC7LAEX9Ll7ZEPfW9WBDA==} engines: {node: '>=18'} peerDependencies: @@ -2903,15 +2903,15 @@ packages: svelte: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.17(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.40(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.17(zod@3.25.76) + '@ai-sdk/ui-utils': 0.0.40(zod@3.25.76) sswr: 2.1.0(svelte@5.33.14) svelte: 5.33.14 transitivePeerDependencies: - zod dev: false - /@ai-sdk/svelte@0.0.57(svelte@5.33.14)(zod@3.23.8): + /@ai-sdk/svelte@0.0.57(svelte@5.33.14)(zod@3.25.76): resolution: {integrity: sha512-SyF9ItIR9ALP9yDNAD+2/5Vl1IT6kchgyDH8xkmhysfJI6WrvJbtO1wdQ0nylvPLcsPoYu+cAlz1krU4lFHcYw==} engines: {node: '>=18'} peerDependencies: @@ -2920,15 +2920,15 @@ packages: svelte: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.50(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.22(zod@3.25.76) + '@ai-sdk/ui-utils': 0.0.50(zod@3.25.76) sswr: 2.1.0(svelte@5.33.14) svelte: 5.33.14 transitivePeerDependencies: - zod dev: true - /@ai-sdk/ui-utils@0.0.40(zod@3.23.8): + /@ai-sdk/ui-utils@0.0.40(zod@3.25.76): resolution: {integrity: sha512-f0eonPUBO13pIO8jA9IGux7IKMeqpvWK22GBr3tOoSRnO5Wg5GEpXZU1V0Po+unpeZHyEPahrWbj5JfXcyWCqw==} engines: {node: '>=18'} peerDependencies: @@ -2938,14 +2938,14 @@ packages: optional: true dependencies: '@ai-sdk/provider': 0.0.22 - '@ai-sdk/provider-utils': 1.0.17(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.17(zod@3.25.76) json-schema: 0.4.0 secure-json-parse: 2.7.0 - zod: 3.23.8 - zod-to-json-schema: 3.23.2(zod@3.23.8) + zod: 3.25.76 + zod-to-json-schema: 3.23.2(zod@3.25.76) dev: false - /@ai-sdk/ui-utils@0.0.50(zod@3.23.8): + /@ai-sdk/ui-utils@0.0.50(zod@3.25.76): resolution: {integrity: sha512-Z5QYJVW+5XpSaJ4jYCCAVG7zIAuKOOdikhgpksneNmKvx61ACFaf98pmOd+xnjahl0pIlc/QIe6O4yVaJ1sEaw==} engines: {node: '>=18'} peerDependencies: @@ -2955,14 +2955,14 @@ packages: optional: true dependencies: '@ai-sdk/provider': 0.0.26 - '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.22(zod@3.25.76) json-schema: 0.4.0 secure-json-parse: 2.7.0 - zod: 3.23.8 - zod-to-json-schema: 3.24.5(zod@3.23.8) + zod: 3.25.76 + zod-to-json-schema: 3.24.5(zod@3.25.76) dev: true - /@ai-sdk/ui-utils@1.0.0(zod@3.23.8): + /@ai-sdk/ui-utils@1.0.0(zod@3.25.76): resolution: {integrity: sha512-oXBDIM/0niWeTWyw77RVl505dNxBUDLLple7bTsqo2d3i1UKwGlzBUX8XqZsh7GbY7I6V05nlG0Y8iGlWxv1Aw==} engines: {node: '>=18'} peerDependencies: @@ -2972,35 +2972,35 @@ packages: optional: true dependencies: '@ai-sdk/provider': 1.0.0 - '@ai-sdk/provider-utils': 2.0.0(zod@3.23.8) - zod: 3.23.8 - zod-to-json-schema: 3.24.5(zod@3.23.8) + '@ai-sdk/provider-utils': 2.0.0(zod@3.25.76) + zod: 3.25.76 + zod-to-json-schema: 3.24.5(zod@3.25.76) dev: false - /@ai-sdk/ui-utils@1.2.1(zod@3.23.8): + /@ai-sdk/ui-utils@1.2.1(zod@3.25.76): resolution: {integrity: sha512-BzvMbYm7LHBlbWuLlcG1jQh4eu14MGpz7L+wrGO1+F4oQ+O0fAjgUSNwPWGlZpKmg4NrcVq/QLmxiVJrx2R4Ew==} engines: {node: '>=18'} peerDependencies: zod: ^3.23.8 dependencies: '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.1(zod@3.23.8) - zod: 3.23.8 - zod-to-json-schema: 3.24.5(zod@3.23.8) + '@ai-sdk/provider-utils': 2.2.1(zod@3.25.76) + zod: 3.25.76 + zod-to-json-schema: 3.24.5(zod@3.25.76) - /@ai-sdk/ui-utils@1.2.11(zod@3.23.8): + /@ai-sdk/ui-utils@1.2.11(zod@3.25.76): resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} engines: {node: '>=18'} peerDependencies: zod: ^3.23.8 dependencies: '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@3.23.8) - zod: 3.23.8 - zod-to-json-schema: 3.24.5(zod@3.23.8) + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + zod-to-json-schema: 3.24.5(zod@3.25.76) dev: false - /@ai-sdk/vue@0.0.45(vue@3.5.16)(zod@3.23.8): + /@ai-sdk/vue@0.0.45(vue@3.5.16)(zod@3.25.76): resolution: {integrity: sha512-bqeoWZqk88TQmfoPgnFUKkrvhOIcOcSH5LMPgzZ8XwDqz5tHHrMHzpPfHCj7XyYn4ROTFK/2kKdC/ta6Ko0fMw==} engines: {node: '>=18'} peerDependencies: @@ -3009,15 +3009,15 @@ packages: vue: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.17(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.40(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.17(zod@3.25.76) + '@ai-sdk/ui-utils': 0.0.40(zod@3.25.76) swrv: 1.0.4(vue@3.5.16) vue: 3.5.16(typescript@5.5.4) transitivePeerDependencies: - zod dev: false - /@ai-sdk/vue@0.0.59(vue@3.5.16)(zod@3.23.8): + /@ai-sdk/vue@0.0.59(vue@3.5.16)(zod@3.25.76): resolution: {integrity: sha512-+ofYlnqdc8c4F6tM0IKF0+7NagZRAiqBJpGDJ+6EYhDW8FHLUP/JFBgu32SjxSxC6IKFZxEnl68ZoP/Z38EMlw==} engines: {node: '>=18'} peerDependencies: @@ -3026,8 +3026,8 @@ packages: vue: optional: true dependencies: - '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.50(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.22(zod@3.25.76) + '@ai-sdk/ui-utils': 0.0.50(zod@3.25.76) swrv: 1.0.4(vue@3.5.16) vue: 3.5.16(typescript@5.5.4) transitivePeerDependencies: @@ -6073,14 +6073,14 @@ packages: react: 18.2.0 dev: false - /@conform-to/zod@0.9.2(@conform-to/dom@0.9.2)(zod@3.23.8): + /@conform-to/zod@0.9.2(@conform-to/dom@0.9.2)(zod@3.25.76): resolution: {integrity: sha512-treG9ZcuNuRERQ1uYvJSWT0zZuqHnYTzRwucg20+/WdjgKNSb60Br+Cy6BAHvVQ8dN6wJsGkHenkX2mSVw3xOA==} peerDependencies: '@conform-to/dom': 0.9.2 zod: ^3.21.0 dependencies: '@conform-to/dom': 0.9.2 - zod: 3.23.8 + zod: 3.25.76 dev: false /@connectrpc/connect-node@1.4.0(@bufbuild/protobuf@1.10.0)(@connectrpc/connect@1.4.0): @@ -9295,8 +9295,8 @@ packages: express-rate-limit: 7.5.0(express@5.0.1) pkce-challenge: 4.1.0 raw-body: 3.0.0 - zod: 3.23.8 - zod-to-json-schema: 3.24.3(zod@3.23.8) + zod: 3.25.76 + zod-to-json-schema: 3.24.3(zod@3.25.76) transitivePeerDependencies: - supports-color dev: false @@ -19410,7 +19410,7 @@ packages: defer-to-connect: 1.1.3 dev: true - /@t3-oss/env-core@0.10.1(typescript@5.5.4)(zod@3.23.8): + /@t3-oss/env-core@0.10.1(typescript@5.5.4)(zod@3.25.76): resolution: {integrity: sha512-GcKZiCfWks5CTxhezn9k5zWX3sMDIYf6Kaxy2Gx9YEQftFcz8hDRN56hcbylyAO3t4jQnQ5ifLawINsNgCDpOg==} peerDependencies: typescript: '>=5.0.0' @@ -19420,10 +19420,10 @@ packages: optional: true dependencies: typescript: 5.5.4 - zod: 3.23.8 + zod: 3.25.76 dev: false - /@t3-oss/env-core@0.11.0(typescript@5.5.4)(zod@3.23.8): + /@t3-oss/env-core@0.11.0(typescript@5.5.4)(zod@3.25.76): resolution: {integrity: sha512-PSalC5bG0a7XbyoLydiQdAnx3gICX6IQNctvh+TyLrdFxsxgocdj9Ui7sd061UlBzi+z4aIGjnem1kZx9QtUgQ==} peerDependencies: typescript: '>=5.0.0' @@ -19433,10 +19433,10 @@ packages: optional: true dependencies: typescript: 5.5.4 - zod: 3.23.8 + zod: 3.25.76 dev: false - /@t3-oss/env-nextjs@0.10.1(typescript@5.5.4)(zod@3.23.8): + /@t3-oss/env-nextjs@0.10.1(typescript@5.5.4)(zod@3.25.76): resolution: {integrity: sha512-iy2qqJLnFh1RjEWno2ZeyTu0ufomkXruUsOZludzDIroUabVvHsrSjtkHqwHp1/pgPUzN3yBRHMILW162X7x2Q==} peerDependencies: typescript: '>=5.0.0' @@ -19445,9 +19445,9 @@ packages: typescript: optional: true dependencies: - '@t3-oss/env-core': 0.10.1(typescript@5.5.4)(zod@3.23.8) + '@t3-oss/env-core': 0.10.1(typescript@5.5.4)(zod@3.25.76) typescript: 5.5.4 - zod: 3.23.8 + zod: 3.25.76 dev: false /@tabler/icons-react@2.47.0(react@18.2.0): @@ -19673,7 +19673,7 @@ packages: dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.6.0) graphql: 16.6.0 - zod: 3.23.8 + zod: 3.25.76 dev: false /@testcontainers/postgresql@10.28.0: @@ -20767,7 +20767,7 @@ packages: /@unkey/error@0.2.0: resolution: {integrity: sha512-DFGb4A7SrusZPP0FYuRIF0CO+Gi4etLUAEJ6EKc+TKYmscL0nEJ2Pr38FyX9MvjI4Wx5l35Wc9KsBjMm9Ybh7w==} dependencies: - zod: 3.23.8 + zod: 3.25.76 dev: false /@uploadthing/mime-types@0.3.0: @@ -21527,7 +21527,7 @@ packages: resolution: {integrity: sha512-hCOfMzbFx5IDutmWLAt6MZwOUjIfSM9G9FyVxytmE4Rs/5YDPWQrD/+IR1w+FweD9H2oOZEnv36TmkjhNURBVA==} dev: true - /ai@3.3.24(openai@4.56.0)(react@19.0.0-rc.0)(svelte@5.33.14)(vue@3.5.16)(zod@3.23.8): + /ai@3.3.24(openai@4.56.0)(react@19.0.0-rc.0)(svelte@5.33.14)(vue@3.5.16)(zod@3.25.76): resolution: {integrity: sha512-hhyczvEdCQeeEMWBWP4Af8k1YIzsheC+dHv6lAsti8NBiOnySFhnjS1sTiIrLyuCgciHXoFYLhlA2+/3AtBLAQ==} engines: {node: '>=18'} peerDependencies: @@ -21549,29 +21549,29 @@ packages: optional: true dependencies: '@ai-sdk/provider': 0.0.22 - '@ai-sdk/provider-utils': 1.0.17(zod@3.23.8) - '@ai-sdk/react': 0.0.53(react@19.0.0-rc.0)(zod@3.23.8) - '@ai-sdk/solid': 0.0.43(zod@3.23.8) - '@ai-sdk/svelte': 0.0.45(svelte@5.33.14)(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.40(zod@3.23.8) - '@ai-sdk/vue': 0.0.45(vue@3.5.16)(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.17(zod@3.25.76) + '@ai-sdk/react': 0.0.53(react@19.0.0-rc.0)(zod@3.25.76) + '@ai-sdk/solid': 0.0.43(zod@3.25.76) + '@ai-sdk/svelte': 0.0.45(svelte@5.33.14)(zod@3.25.76) + '@ai-sdk/ui-utils': 0.0.40(zod@3.25.76) + '@ai-sdk/vue': 0.0.45(vue@3.5.16)(zod@3.25.76) '@opentelemetry/api': 1.9.0 eventsource-parser: 1.1.2 json-schema: 0.4.0 jsondiffpatch: 0.6.0 nanoid: 3.3.6 - openai: 4.56.0(zod@3.23.8) + openai: 4.56.0(zod@3.25.76) react: 19.0.0-rc.0 secure-json-parse: 2.7.0 svelte: 5.33.14 - zod: 3.23.8 - zod-to-json-schema: 3.23.2(zod@3.23.8) + zod: 3.25.76 + zod-to-json-schema: 3.23.2(zod@3.25.76) transitivePeerDependencies: - solid-js - vue dev: false - /ai@3.4.33(react@18.3.1)(svelte@5.33.14)(vue@3.5.16)(zod@3.23.8): + /ai@3.4.33(react@18.3.1)(svelte@5.33.14)(vue@3.5.16)(zod@3.25.76): resolution: {integrity: sha512-plBlrVZKwPoRTmM8+D1sJac9Bq8eaa2jiZlHLZIWekKWI1yMWYZvCCEezY9ASPwRhULYDJB2VhKOBUUeg3S5JQ==} engines: {node: '>=18'} peerDependencies: @@ -21593,12 +21593,12 @@ packages: optional: true dependencies: '@ai-sdk/provider': 0.0.26 - '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) - '@ai-sdk/react': 0.0.70(react@18.3.1)(zod@3.23.8) - '@ai-sdk/solid': 0.0.54(zod@3.23.8) - '@ai-sdk/svelte': 0.0.57(svelte@5.33.14)(zod@3.23.8) - '@ai-sdk/ui-utils': 0.0.50(zod@3.23.8) - '@ai-sdk/vue': 0.0.59(vue@3.5.16)(zod@3.23.8) + '@ai-sdk/provider-utils': 1.0.22(zod@3.25.76) + '@ai-sdk/react': 0.0.70(react@18.3.1)(zod@3.25.76) + '@ai-sdk/solid': 0.0.54(zod@3.25.76) + '@ai-sdk/svelte': 0.0.57(svelte@5.33.14)(zod@3.25.76) + '@ai-sdk/ui-utils': 0.0.50(zod@3.25.76) + '@ai-sdk/vue': 0.0.59(vue@3.5.16)(zod@3.25.76) '@opentelemetry/api': 1.9.0 eventsource-parser: 1.1.2 json-schema: 0.4.0 @@ -21606,14 +21606,14 @@ packages: react: 18.3.1 secure-json-parse: 2.7.0 svelte: 5.33.14 - zod: 3.23.8 - zod-to-json-schema: 3.24.3(zod@3.23.8) + zod: 3.25.76 + zod-to-json-schema: 3.24.3(zod@3.25.76) transitivePeerDependencies: - solid-js - vue dev: true - /ai@4.0.0(react@18.3.1)(zod@3.23.8): + /ai@4.0.0(react@18.3.1)(zod@3.25.76): resolution: {integrity: sha512-cqf2GCaXnOPhUU+Ccq6i+5I0jDjnFkzfq7t6mc0SUSibSa1wDPn5J4p8+Joh2fDGDYZOJ44rpTW9hSs40rXNAw==} engines: {node: '>=18'} peerDependencies: @@ -21626,17 +21626,17 @@ packages: optional: true dependencies: '@ai-sdk/provider': 1.0.0 - '@ai-sdk/provider-utils': 2.0.0(zod@3.23.8) - '@ai-sdk/react': 1.0.0(react@18.3.1)(zod@3.23.8) - '@ai-sdk/ui-utils': 1.0.0(zod@3.23.8) + '@ai-sdk/provider-utils': 2.0.0(zod@3.25.76) + '@ai-sdk/react': 1.0.0(react@18.3.1)(zod@3.25.76) + '@ai-sdk/ui-utils': 1.0.0(zod@3.25.76) '@opentelemetry/api': 1.9.0 jsondiffpatch: 0.6.0 react: 18.3.1 - zod: 3.23.8 - zod-to-json-schema: 3.24.5(zod@3.23.8) + zod: 3.25.76 + zod-to-json-schema: 3.24.5(zod@3.25.76) dev: false - /ai@4.2.5(react@18.3.1)(zod@3.23.8): + /ai@4.2.5(react@18.3.1)(zod@3.25.76): resolution: {integrity: sha512-URJEslI3cgF/atdTJHtz+Sj0W1JTmiGmD3znw9KensL3qV605odktDim+GTazNJFPR4QaIu1lUio5b8RymvOjA==} engines: {node: '>=18'} peerDependencies: @@ -21647,16 +21647,16 @@ packages: optional: true dependencies: '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.1(zod@3.23.8) - '@ai-sdk/react': 1.2.2(react@18.3.1)(zod@3.23.8) - '@ai-sdk/ui-utils': 1.2.1(zod@3.23.8) + '@ai-sdk/provider-utils': 2.2.1(zod@3.25.76) + '@ai-sdk/react': 1.2.2(react@18.3.1)(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.1(zod@3.25.76) '@opentelemetry/api': 1.9.0 jsondiffpatch: 0.6.0 react: 18.3.1 - zod: 3.23.8 + zod: 3.25.76 dev: true - /ai@4.2.5(react@19.0.0)(zod@3.23.8): + /ai@4.2.5(react@19.0.0)(zod@3.25.76): resolution: {integrity: sha512-URJEslI3cgF/atdTJHtz+Sj0W1JTmiGmD3znw9KensL3qV605odktDim+GTazNJFPR4QaIu1lUio5b8RymvOjA==} engines: {node: '>=18'} peerDependencies: @@ -21667,16 +21667,16 @@ packages: optional: true dependencies: '@ai-sdk/provider': 1.1.0 - '@ai-sdk/provider-utils': 2.2.1(zod@3.23.8) - '@ai-sdk/react': 1.2.2(react@19.0.0)(zod@3.23.8) - '@ai-sdk/ui-utils': 1.2.1(zod@3.23.8) + '@ai-sdk/provider-utils': 2.2.1(zod@3.25.76) + '@ai-sdk/react': 1.2.2(react@19.0.0)(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.1(zod@3.25.76) '@opentelemetry/api': 1.9.0 jsondiffpatch: 0.6.0 react: 19.0.0 - zod: 3.23.8 + zod: 3.25.76 dev: false - /ai@4.3.19(react@18.2.0)(zod@3.23.8): + /ai@4.3.19(react@18.2.0)(zod@3.25.76): resolution: {integrity: sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==} engines: {node: '>=18'} peerDependencies: @@ -21687,13 +21687,13 @@ packages: optional: true dependencies: '@ai-sdk/provider': 1.1.3 - '@ai-sdk/provider-utils': 2.2.8(zod@3.23.8) - '@ai-sdk/react': 1.2.12(react@18.2.0)(zod@3.23.8) - '@ai-sdk/ui-utils': 1.2.11(zod@3.23.8) + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/react': 1.2.12(react@18.2.0)(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) '@opentelemetry/api': 1.9.0 jsondiffpatch: 0.6.0 react: 18.2.0 - zod: 3.23.8 + zod: 3.25.76 dev: false /ajv-formats@2.1.1(ajv@8.17.1): @@ -22154,9 +22154,9 @@ packages: js-yaml: 4.1.0 linear-sum-assignment: 1.0.7 mustache: 4.2.0 - openai: 4.97.0(ws@8.12.0)(zod@3.23.8) - zod: 3.23.8 - zod-to-json-schema: 3.24.5(zod@3.23.8) + openai: 4.97.0(ws@8.12.0)(zod@3.25.76) + zod: 3.25.76 + zod-to-json-schema: 3.24.5(zod@3.25.76) transitivePeerDependencies: - encoding - ws @@ -28306,7 +28306,7 @@ packages: dependencies: '@types/uuid': 10.0.0 commander: 10.0.1 - openai: 4.68.4(zod@3.23.8) + openai: 4.68.4(zod@3.25.76) p-queue: 6.6.2 p-retry: 4.6.2 semver: 7.6.3 @@ -31029,7 +31029,7 @@ packages: - encoding dev: false - /openai@4.56.0(zod@3.23.8): + /openai@4.56.0(zod@3.25.76): resolution: {integrity: sha512-zcag97+3bG890MNNa0DQD9dGmmTWL8unJdNkulZzWRXrl+QeD+YkBI4H58rJcwErxqGK6a0jVPZ4ReJjhDGcmw==} hasBin: true peerDependencies: @@ -31045,12 +31045,12 @@ packages: form-data-encoder: 1.7.2 formdata-node: 4.4.1 node-fetch: 2.6.12 - zod: 3.23.8 + zod: 3.25.76 transitivePeerDependencies: - encoding dev: false - /openai@4.68.4(zod@3.23.8): + /openai@4.68.4(zod@3.25.76): resolution: {integrity: sha512-LRinV8iU9VQplkr25oZlyrsYGPGasIwYN8KFMAAFTHHLHjHhejtJ5BALuLFrkGzY4wfbKhOhuT+7lcHZ+F3iEA==} hasBin: true peerDependencies: @@ -31066,12 +31066,12 @@ packages: form-data-encoder: 1.7.2 formdata-node: 4.4.1 node-fetch: 2.6.12 - zod: 3.23.8 + zod: 3.25.76 transitivePeerDependencies: - encoding dev: false - /openai@4.97.0(ws@8.12.0)(zod@3.23.8): + /openai@4.97.0(ws@8.12.0)(zod@3.25.76): resolution: {integrity: sha512-LRoiy0zvEf819ZUEJhgfV8PfsE8G5WpQi4AwA1uCV8SKvvtXQkoWUFkepD6plqyJQRghy2+AEPQ07FrJFKHZ9Q==} hasBin: true peerDependencies: @@ -31091,7 +31091,7 @@ packages: formdata-node: 4.4.1 node-fetch: 2.6.12 ws: 8.12.0 - zod: 3.23.8 + zod: 3.25.76 transitivePeerDependencies: - encoding @@ -33782,7 +33782,7 @@ packages: react: 18.2.0 dev: false - /remix-utils@7.7.0(@remix-run/node@2.1.0)(@remix-run/react@2.1.0)(@remix-run/router@1.15.3)(intl-parse-accept-language@1.0.0)(react@18.2.0)(zod@3.23.8): + /remix-utils@7.7.0(@remix-run/node@2.1.0)(@remix-run/react@2.1.0)(@remix-run/router@1.15.3)(intl-parse-accept-language@1.0.0)(react@18.2.0)(zod@3.25.76): resolution: {integrity: sha512-J8NhP044nrNIam/xOT1L9a4RQ9FSaA2wyrUwmN8ZT+c/+CdAAf70yfaLnvMyKcV5U+8BcURQ/aVbth77sT6jGA==} engines: {node: '>=18.0.0'} peerDependencies: @@ -33821,7 +33821,7 @@ packages: intl-parse-accept-language: 1.0.0 react: 18.2.0 type-fest: 4.33.0 - zod: 3.23.8 + zod: 3.25.76 dev: false /remove-accents@0.5.0: @@ -38394,30 +38394,23 @@ packages: /zod-error@1.5.0: resolution: {integrity: sha512-zzopKZ/skI9iXpqCEPj+iLCKl9b88E43ehcU+sbRoHuwGd9F1IDVGQ70TyO6kmfiRL1g4IXkjsXK+g1gLYl4WQ==} dependencies: - zod: 3.23.8 + zod: 3.25.76 dev: false - /zod-to-json-schema@3.23.2(zod@3.23.8): + /zod-to-json-schema@3.23.2(zod@3.25.76): resolution: {integrity: sha512-uSt90Gzc/tUfyNqxnjlfBs8W6WSGpNBv0rVsNxP/BVSMHMKGdthPYff4xtCHYloJGM0CFxFsb3NbC0eqPhfImw==} peerDependencies: zod: ^3.23.3 dependencies: - zod: 3.23.8 + zod: 3.25.76 dev: false - /zod-to-json-schema@3.24.3(zod@3.23.8): + /zod-to-json-schema@3.24.3(zod@3.25.76): resolution: {integrity: sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A==} peerDependencies: zod: ^3.24.1 dependencies: - zod: 3.23.8 - - /zod-to-json-schema@3.24.5(zod@3.23.8): - resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} - peerDependencies: - zod: ^3.24.1 - dependencies: - zod: 3.23.8 + zod: 3.25.76 /zod-to-json-schema@3.24.5(zod@3.25.76): resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} @@ -38425,19 +38418,19 @@ packages: zod: ^3.24.1 dependencies: zod: 3.25.76 - dev: false - /zod-validation-error@1.5.0(zod@3.23.8): + /zod-validation-error@1.5.0(zod@3.25.76): resolution: {integrity: sha512-/7eFkAI4qV0tcxMBB/3+d2c1P6jzzZYdYSlBuAklzMuCrJu5bzJfHS0yVAS87dRHVlhftd6RFJDIvv03JgkSbw==} engines: {node: '>=16.0.0'} peerDependencies: zod: ^3.18.0 dependencies: - zod: 3.23.8 + zod: 3.25.76 dev: false /zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + dev: false /zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} diff --git a/references/d3-chat/package.json b/references/d3-chat/package.json index 2d14a1d22..22e5a9dae 100644 --- a/references/d3-chat/package.json +++ b/references/d3-chat/package.json @@ -47,7 +47,7 @@ "react-markdown": "^10.1.0", "tailwind-merge": "^3.1.0", "tw-animate-css": "^1.2.4", - "zod": "3.23.8" + "zod": "3.25.76" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/references/d3-openai-agents/package.json b/references/d3-openai-agents/package.json index e94dc26a2..91309ea5a 100644 --- a/references/d3-openai-agents/package.json +++ b/references/d3-openai-agents/package.json @@ -33,7 +33,7 @@ "react-dom": "^19.0.0", "tailwind-merge": "^3.0.2", "tw-animate-css": "^1.2.4", - "zod": "3.23.8", + "zod": "3.25.76", "zod-to-json-schema": "^3.24.5" }, "devDependencies": { diff --git a/references/hello-world/package.json b/references/hello-world/package.json index 352994052..dea86c6b7 100644 --- a/references/hello-world/package.json +++ b/references/hello-world/package.json @@ -13,7 +13,7 @@ "puppeteer-core": "^24.15.0", "replicate": "^1.0.1", "yup": "^1.6.1", - "zod": "3.23.8", + "zod": "3.25.76", "@sinclair/typebox": "^0.34.3" }, "scripts": { diff --git a/references/nextjs-realtime/package.json b/references/nextjs-realtime/package.json index 9e9edd34a..4c2736a02 100644 --- a/references/nextjs-realtime/package.json +++ b/references/nextjs-realtime/package.json @@ -37,7 +37,7 @@ "tailwind-merge": "^2.5.3", "tailwindcss-animate": "^1.0.7", "uploadthing": "^7.1.0", - "zod": "3.23.8" + "zod": "3.25.76" }, "devDependencies": { "@next/bundle-analyzer": "^15.0.2", diff --git a/references/python-catalog/package.json b/references/python-catalog/package.json index 171a1e3ea..1c4f58592 100644 --- a/references/python-catalog/package.json +++ b/references/python-catalog/package.json @@ -10,7 +10,7 @@ "dependencies": { "@trigger.dev/sdk": "workspace:*", "@trigger.dev/python": "workspace:*", - "zod": "3.23.8" + "zod": "3.25.76" }, "devDependencies": { "@trigger.dev/build": "workspace:*", diff --git a/references/test-tasks/package.json b/references/test-tasks/package.json index 6739b44b3..9ed2635da 100644 --- a/references/test-tasks/package.json +++ b/references/test-tasks/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@trigger.dev/sdk": "workspace:*", - "zod": "3.23.8" + "zod": "3.25.76" }, "devDependencies": { "@trigger.dev/build": "workspace:*", diff --git a/references/v3-catalog/package.json b/references/v3-catalog/package.json index 5b2315fe6..b63752019 100644 --- a/references/v3-catalog/package.json +++ b/references/v3-catalog/package.json @@ -62,7 +62,7 @@ "yt-dlp-wrap": "^2.3.12", "yup": "^1.4.0", "zip-node-addon": "^0.0.11", - "zod": "3.23.8" + "zod": "3.25.76" }, "devDependencies": { "@opentelemetry/api": "^1.8.0", From a782813c6c840e9340e8403d18bb4b492150c1f2 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Wed, 6 Aug 2025 15:56:01 +0100 Subject: [PATCH 017/101] =?UTF-8?q?Regions=20=E2=80=93=20dashboard=20page,?= =?UTF-8?q?=20switching=20default,=20allowedMasterQueues=20(#2354)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial Regions page * Fix for bad attribute name * Switching regions is working. Some style improvements * Use a dialog for confirmation, not great yet * Added allowedMasterQueues * Improves flag icons * Improves the region switch modal with more info * Adds “suggest a region” table row * New icons for the buttons * Improved the tooltip information * New “small” badge style * Make the default badge live in its column * Better DO icon size * Remove unused export of regions options * Show upgrade message for free users to get static IPs * Admins can view all regions and switch at will --------- Co-authored-by: James Ritchie --- .../app/assets/icons/CloudProviderIcon.tsx | 76 +++ apps/webapp/app/assets/icons/RegionIcons.tsx | 106 +++++ apps/webapp/app/components/CloudProvider.tsx | 10 + .../app/components/navigation/SideMenu.tsx | 13 +- .../app/components/primitives/Badge.tsx | 2 + .../presenters/v3/RegionsPresenter.server.ts | 159 +++++++ .../route.tsx | 435 ++++++++++++++++++ apps/webapp/app/routes/resources.feedback.ts | 1 + apps/webapp/app/utils/pathBuilder.ts | 8 + .../v3/services/setDefaultRegion.server.ts | 58 +++ .../migration.sql | 5 + .../migration.sql | 2 + .../database/prisma/schema.prisma | 9 + 13 files changed, 882 insertions(+), 2 deletions(-) create mode 100644 apps/webapp/app/assets/icons/CloudProviderIcon.tsx create mode 100644 apps/webapp/app/assets/icons/RegionIcons.tsx create mode 100644 apps/webapp/app/components/CloudProvider.tsx create mode 100644 apps/webapp/app/presenters/v3/RegionsPresenter.server.ts create mode 100644 apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.regions/route.tsx create mode 100644 apps/webapp/app/v3/services/setDefaultRegion.server.ts create mode 100644 internal-packages/database/prisma/migrations/20250805161650_worker_instance_group_added_cloud_provider_location_static_ips_columns/migration.sql create mode 100644 internal-packages/database/prisma/migrations/20250806124301_project_allowed_master_queues_column/migration.sql diff --git a/apps/webapp/app/assets/icons/CloudProviderIcon.tsx b/apps/webapp/app/assets/icons/CloudProviderIcon.tsx new file mode 100644 index 000000000..6c1625282 --- /dev/null +++ b/apps/webapp/app/assets/icons/CloudProviderIcon.tsx @@ -0,0 +1,76 @@ +export function CloudProviderIcon({ + provider, + className, +}: { + provider: "aws" | "digitalocean" | (string & {}); + className?: string; +}) { + switch (provider) { + case "aws": + return ; + case "digitalocean": + return ; + default: + return null; + } +} + +export function AWS({ className }: { className?: string }) { + return ( + + + + + + ); +} + +export function DigitalOcean({ className }: { className?: string }) { + return ( + + + + + + + + + + + + + + ); +} diff --git a/apps/webapp/app/assets/icons/RegionIcons.tsx b/apps/webapp/app/assets/icons/RegionIcons.tsx new file mode 100644 index 000000000..098d5bc98 --- /dev/null +++ b/apps/webapp/app/assets/icons/RegionIcons.tsx @@ -0,0 +1,106 @@ +export function FlagIcon({ + region, + className, +}: { + region: "usa" | "europe" | (string & {}); + className?: string; +}) { + switch (region) { + case "usa": + return ; + case "europe": + return ; + default: + return null; + } +} + +export function FlagUSA({ className }: { className?: string }) { + return ( + + + + + + + + + + + + + + + + + + ); +} + +export function FlagEurope({ className }: { className?: string }) { + return ( + + + + + + + + + + + + ); +} diff --git a/apps/webapp/app/components/CloudProvider.tsx b/apps/webapp/app/components/CloudProvider.tsx new file mode 100644 index 000000000..acf8cff55 --- /dev/null +++ b/apps/webapp/app/components/CloudProvider.tsx @@ -0,0 +1,10 @@ +export function cloudProviderTitle(provider: "aws" | "digitalocean" | (string & {})) { + switch (provider) { + case "aws": + return "Amazon Web Services"; + case "digitalocean": + return "Digital Ocean"; + default: + return provider; + } +} diff --git a/apps/webapp/app/components/navigation/SideMenu.tsx b/apps/webapp/app/components/navigation/SideMenu.tsx index 20d2a821d..4e0adf979 100644 --- a/apps/webapp/app/components/navigation/SideMenu.tsx +++ b/apps/webapp/app/components/navigation/SideMenu.tsx @@ -10,6 +10,7 @@ import { CogIcon, FolderIcon, FolderOpenIcon, + GlobeAmericasIcon, IdentificationIcon, KeyIcon, PlusIcon, @@ -22,6 +23,7 @@ import { useNavigation } from "@remix-run/react"; import { useEffect, useRef, useState, type ReactNode } from "react"; import simplur from "simplur"; import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons"; +import { ListCheckedIcon } from "~/assets/icons/ListCheckedIcon"; import { RunsIconExtraSmall } from "~/assets/icons/RunsIcon"; import { TaskIconSmall } from "~/assets/icons/TaskIcon"; import { WaitpointTokenIcon } from "~/assets/icons/WaitpointTokenIcon"; @@ -46,6 +48,7 @@ import { organizationPath, organizationSettingsPath, organizationTeamPath, + regionsPath, v3ApiKeysPath, v3BatchesPath, v3BillingPath, @@ -87,8 +90,6 @@ import { HelpAndFeedback } from "./HelpAndFeedbackPopover"; import { SideMenuHeader } from "./SideMenuHeader"; import { SideMenuItem } from "./SideMenuItem"; import { SideMenuSection } from "./SideMenuSection"; -import { ListChecks } from "lucide-react"; -import { ListCheckedIcon } from "~/assets/icons/ListCheckedIcon"; type SideMenuUser = Pick & { isImpersonating: boolean }; export type SideMenuProject = Pick< @@ -311,6 +312,14 @@ export function SideMenu({ data-action="preview-branches" badge={} /> + } + />